OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 2:33 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 4 posts ] 
Author Message
 Post subject: [solved] GPF on x86 baremetal when returninig from interrupt
PostPosted: Mon Jun 26, 2017 8:28 am 
Offline

Joined: Sun Feb 26, 2017 6:16 am
Posts: 2
Hello!

I'm developing small OS. It is correctly working in emulators (qemu, bochs and virtualbox). I have tried to run it on two baremetal old machines (old because it doesn't have uefi). On friend's desktop everything works fine. But on my netbook OS gives GPF exception.
Netbook is Samsung NC110P.

I tried to localize the problem. Minimal steps to repeat the exception:
1. init memory, enable paging.
2. init gdt
3. init idt (besides standard exception, also create handler for 32 exception vector. All other entries in IDT are zero)
4. init pic, remap irqs (shift is 32)
5. init pit, unmask only IRQ0
6. enable interrupt (sti)
7. fall into forever loop.

After interrupts are enabled PIT generate an exception, which is passed to handler, handler prints values, which look ok. I handler I don't do EOI, so PIT interrupt will not repeat.
If I put forever loop before iret (in exception handler), then system correctlry freeze. If not, then after iret, I get GPF exception with error code 0x13b.
Error code 0x13b is segment error in IDT, segment selector is 39. But 39th entry in IDT is zero.
I don't understand why it happens, and why only on on machine. Do you have any idea?


Code that may be usefull
"main" code
Code:
    ...
    asm("sti");
    while(1) {
    }


Code:
idt_entry_t   pok_idt[IDT_SIZE];

void ja_idt_init (void)
{
   sysdesc_t sysdesc;

   /* Clear table */
   memset(pok_idt, 0, sizeof (idt_entry_t) * IDT_SIZE);

   /* Load IDT */
   sysdesc.limit = sizeof (pok_idt);
   sysdesc.base = (uint32_t)pok_idt;

   asm ("lidt %0"
        :
        : "m" (sysdesc));
}

void pok_idt_set_gate (uint8_t     index,
                       void (*entry)(void),
                       e_idte_type  t)
{
   uint32_t offset = (uint32_t)entry;

   pok_idt[index].offset_low   = (offset) & 0xFFFF;
   pok_idt[index].offset_high  = (offset >> 16) & 0xFFFF;
   pok_idt[index].segsel       = GDT_CORE_CODE_SEGMENT << 3;
   pok_idt[index].dpl          = 3;
   pok_idt[index].type         = t;
   pok_idt[index].d            = 1;
   pok_idt[index].res0         = 0; /* reserved */
   pok_idt[index].res1         = 0; /* reserved */
   pok_idt[index].present   = 1;
}

const struct exception_descriptor exception_list[] =
{
    {EXCEPTION_DIVIDE_ERROR, exception_DIVIDE_ERROR},
    {EXCEPTION_DEBUG, exception_DEBUG},
    {EXCEPTION_NMI, exception_NMI},
    {EXCEPTION_BREAKPOINT, exception_BREAKPOINT},
    {EXCEPTION_OVERFLOW, exception_OVERFLOW},
    {EXCEPTION_INVALIDOPCODE, exception_INVALIDOPCODE},
    {EXCEPTION_NOMATH_COPROC, exception_NOMATH_COPROC},
    {EXCEPTION_DOUBLEFAULT, exception_DOUBLEFAULT},
    {EXCEPTION_COPSEG_OVERRUN, exception_COPSEG_OVERRUN},
    {EXCEPTION_INVALID_TSS, exception_INVALID_TSS},
    {EXCEPTION_SEGMENT_NOT_PRESENT, exception_SEGMENT_NOT_PRESENT},
    {EXCEPTION_STACKSEG_FAULT, exception_STACKSEG_FAULT},
    {EXCEPTION_GENERAL_PROTECTION, exception_GENERAL_PROTECTION},
    {EXCEPTION_PAGEFAULT, exception_PAGEFAULT},
    {EXCEPTION_FPU_FAULT, exception_FPU_FAULT},
    {EXCEPTION_ALIGNEMENT_CHECK, exception_ALIGNEMENT_CHECK},
    {EXCEPTION_MACHINE_CHECK, exception_MACHINE_CHECK},
    {EXCEPTION_SIMD_FAULT, exception_SIMD_FAULT},
    {EXCEPTION_TIMER, exception_TIMER}, //32
    {0, NULL}
};

void ja_exception_init(void)
{
  int i;

  for (i = 0; exception_list[i].handler != NULL; ++i)
  {
    pok_idt_set_gate (exception_list[i].vector,
                      exception_list[i].handler,
                      IDTE_INTERRUPT);
  }
}



Code:

.macro INTERRUPT_PROLOGUE_ERROR
    pusha
    push %ds //push 16 bit value in 32 bit cell
    push %es //push 16 bit value in 32 bit cell

    mov $0x10, %ax
    mov %ax, %ds
    mov %ax, %es
    mov $0, %ebp // Mark current frame as first
    push %esp // Interrupt frame is the only parameter to the followed functions.
.endm

.macro INTERRUPT_PROLOGUE
    pushl $0 // error_code
    INTERRUPT_PROLOGUE_ERROR // Fallback to the common code
.endm


INTERRUPT_EPILOGUE:
    //call update_tss
    addl $4, %esp
    pop %es
    pop %ds
    popa
    addl $4, %esp
    iret


    .global exception_TIMER
    .type exception_TIMER ,@function
exception_TIMER:
    INTERRUPT_PROLOGUE
    call exception_TIMER_handler
    jmp INTERRUPT_EPILOGUE



Code:
typedef struct
{
  uint16_t es;
  uint16_t padding1;
  uint16_t ds;
  uint16_t padding2;
  // These registers are ordered for pusha/popa
  uint32_t edi;
  uint32_t esi;
  uint32_t ebp;
  uint32_t __esp;
  uint32_t ebx;
  uint32_t edx;
  uint32_t ecx;
  uint32_t eax;

  /* These are pushed by interrupt */
  uint32_t error;   /* Error code or padding */
  uint32_t eip;
  uint16_t cs;
  uint16_t padding3;
  uint32_t eflags;

  /* Only pushed with privilege switch */
  /* (Check cs content to have original CPL) */
  uint32_t esp;
  uint16_t ss;
  uint16_t padding4;
} __attribute__((packed)) interrupt_frame;

void exception_TIMER_handler(interrupt_frame* frame)
{
  printf ("ES: %x, DS: %x\n",  frame->es, frame->ds);
  printf ("CS: %x, SS: %x\n",  frame->cs, frame->ss);
  printf ("EDI: %lx, ESI: %lx\n", frame->edi, frame->esi);
  printf ("EBP: %lx, ESP: %lx\n", frame->ebp, frame->esp);
  printf ("EAX: %lx, ECX: %lx\n", frame->eax, frame->ecx);
  printf ("EDX: %lx, EBX: %lx\n", frame->edx, frame->ebx);
  printf ("EIP: %lx, ErrorCode: %lx\n", frame->eip, frame->error);
  printf ("EFLAGS: %lx\n\n", frame->eflags);

}


Last edited by mkurmag on Tue Jun 27, 2017 12:28 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: GPF on x86 baremetal when returninig from interrupt
PostPosted: Mon Jun 26, 2017 1:19 pm 
Offline
Member
Member

Joined: Thu Aug 13, 2015 4:57 pm
Posts: 384
Have you tried to put in an ISR at that interrupt and simply do IRET, what happens then?

Could it be spurious interrupt?
http://wiki.osdev.org/8259_PIC#Spurious_IRQs

Note, I think it would be useful to have a ISR for every possible IDT entry (depending on how many you've set in the IDTR) and all of them should by default kpanic with a relevant message, then replace them with proper handlers as you have time and as they become annoying. This will save you time in the long run.


Top
 Profile  
 
 Post subject: Re: GPF on x86 baremetal when returninig from interrupt
PostPosted: Tue Jun 27, 2017 4:50 am 
Offline
Member
Member
User avatar

Joined: Sat Dec 27, 2014 9:11 am
Posts: 901
Location: Maadi, Cairo, Egypt
LtG wrote:
Note, I think it would be useful to have a ISR for every possible IDT entry (depending on how many you've set in the IDTR) and all of them should by default kpanic with a relevant message, then replace them with proper handlers as you have time and as they become annoying. This will save you time in the long run.

Exactly.

You also have to handle PIC spurious IRQs. These are unlikely to happen on virtual machines, although I have seen them occur on Bochs sometimes. On real hardware however, the spurious IRQs really do occur.

Aside from that, you should consider masking the PICs. Each PIC has a mask register, at port 0x21 for the master and 0xA1 for the slave, where each bit corresponds to whether or not that specific IRQ line is enabled or not. Zero means enabled, one means disabled. The master PIC as you probably know handles IRQs 0-7, while the slave handles IRQs 8-15. Basically, you want to write 0xFF to port 0xA1 to disable all slave IRQs, and write 0xFB to the port 0x21 to disable all master IRQs, except the cascade IRQ (IRQ 2), which would allow you to handle IRQs from the slave PIC, eventually when you configure a device that uses it. After this initialization of the PIC, you install handlers in the IDT, where each handler makes a kernel panic (or prints relevant messages to the kernel log) or something similar. You also want to install special handlers for IRQ 7 and IRQ 15. These handlers should check if the IRQ is spurious, and if so, IRQ 7 should just return, while IRQ 15 should send an EOI to the master PIC only. The Wiki has relevant information on how to check whether it is a "real" or spurious IRQ. You can now safely execute STI, knowing that unhandled interrupts won't occur because everything is masked. When the PICs are all masked before you configure devices that use IRQs, you can be somewhat sure that no unwanted IRQs will come in. Of course, spurious IRQs and NMIs and such are special cases, but those are irrelevant.

After initializing the PICs, you can now initialize devices that use IRQs. Let's take the PIT as an example. Your PIT initialization code will set the timer's divider frequency, install a proper IRQ handler to replace the previous default handlers that simply panic, and then it would unmask the IRQ line on the PIC. Specifically for the PIT, this is IRQ 0, which corresponds to IRQ 0 on the master PIC. The OS can now handle IRQs coming in from the PIT.

In short, your problem is most likely an IRQ that you don't have a handler for, and thus you should use the mask to your benefit, and not allow unhandled IRQs to occur at all in the first place.

_________________
You know your OS is advanced when you stop using the Intel programming guide as a reference.


Top
 Profile  
 
 Post subject: Re: GPF on x86 baremetal when returninig from interrupt
PostPosted: Tue Jun 27, 2017 12:32 pm 
Offline

Joined: Sun Feb 26, 2017 6:16 am
Posts: 2
Thank you, guys! This really was spurious interrupt.

LtG wrote:
Note, I think it would be useful to have a ISR for every possible IDT entry (depending on how many you've set in the IDTR) and all of them should by default kpanic with a relevant message, then replace them with proper handlers as you have time and as they become annoying. This will save you time in the long run.

Good point. Thank you.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 4 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 75 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group