OSDev.org

The Place to Start for Operating System Developers
It is currently Tue Apr 23, 2024 7:15 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 11 posts ] 
Author Message
 Post subject: IDT invalid entry in long mode
PostPosted: Mon Sep 03, 2018 6:48 am 
Offline
Member
Member

Joined: Wed May 02, 2018 1:26 pm
Posts: 55
I am currently trying to get the interrupt handlers working in the kernel after I successfully entered long mode. However there are some errors when trying to trigger an interrupt via
Code:
   __asm__ volatile("int $0x0");
.
The current setup looks like this:
Code:
// interrupt.h:

struct idt_entry {
   uint16_t offset_low;
   uint16_t selector;
   uint8_t __r0;
   uint8_t type;
   uint16_t offset_middle;
   uint32_t offset_high;
   uint32_t __r1;
} __attribute((packed));

// isr.c:
void isr_init(void)
{
   idt_set_gate(0,  (uint64_t)__isr0,  __KERNEL_CS, 0x8e);
   idt_set_gate(1,  (uint64_t)__isr1,  __KERNEL_CS, 0x8e);
   idt_set_gate(2,  (uint64_t)__isr2,  __KERNEL_CS, 0x8e);
   idt_set_gate(3,  (uint64_t)__isr3,  __KERNEL_CS, 0x8e);
   idt_set_gate(4,  (uint64_t)__isr4,  __KERNEL_CS, 0x8e);
   idt_set_gate(5,  (uint64_t)__isr5,  __KERNEL_CS, 0x8e);
   idt_set_gate(6,  (uint64_t)__isr6,  __KERNEL_CS, 0x8e);
   idt_set_gate(7,  (uint64_t)__isr7,  __KERNEL_CS, 0x8e);
   idt_set_gate(8,  (uint64_t)__isr8,  __KERNEL_CS, 0x8e);
   idt_set_gate(9,  (uint64_t)__isr9,  __KERNEL_CS, 0x8e);
   idt_set_gate(10, (uint64_t)__isr10, __KERNEL_CS, 0x8e);
   idt_set_gate(11, (uint64_t)__isr11, __KERNEL_CS, 0x8e);
   idt_set_gate(12, (uint64_t)__isr12, __KERNEL_CS, 0x8e);
   idt_set_gate(13, (uint64_t)__isr13, __KERNEL_CS, 0x8e);
   idt_set_gate(14, (uint64_t)__isr14, __KERNEL_CS, 0x8e);
   idt_set_gate(15, (uint64_t)__isr15, __KERNEL_CS, 0x8e);
   idt_set_gate(16, (uint64_t)__isr16, __KERNEL_CS, 0x8e);
   idt_set_gate(17, (uint64_t)__isr17, __KERNEL_CS, 0x8e);
   idt_set_gate(18, (uint64_t)__isr18, __KERNEL_CS, 0x8e);
   idt_set_gate(19, (uint64_t)__isr19, __KERNEL_CS, 0x8e);
   idt_set_gate(20, (uint64_t)__isr20, __KERNEL_CS, 0x8e);
}

with __KERNEL_CS being 0x8 (GDT_SEGMENT_CS = 1, __KERNEL_CS = GDT_SEGMENT_CS * 8 ). When setting a breakpoint right before the int 0x0 instruction, the bochs GUI debugger shows the addresses of all __isrXX functions accordingly. But the problem is when the interrupt is triggered:
    - When int 0x0 is triggered, the instruction pointer moves to the function located at the first entry (index 0) of the IDT. No problems so far.
    - When stepping to the next instruction, the instruction pointer however moves to the next interrupt service routine, which is the page fault handler. In there, the instruction triggers itself multiple consecutive page fault, as the instruction pointer seems to only complete the cli instruction at the beginning of the isr stub. This goes on until I seemed to enter real mode again.
I inspected the bochs source code at the location which prints the output:
Code:
Dump gate:
08894662103e[CPU0  ] Type : f
08894662103e[CPU0  ] Valid: 1
08894662103e[CPU0  ] Segm : 1
08894662103e[CPU0  ] interrupt(long mode): gate descriptor is not valid sys seg
08894662105e[CPU0  ] Dump gate:
08894662105e[CPU0  ] Type : f
08894662105e[CPU0  ] Valid: 1
08894662105e[CPU0  ] Segm : 1
08894662105e[CPU0  ] interrupt(long mode): gate descriptor is not valid sys seg
08894662107e[CPU0  ] Dump gate:
08894662107e[CPU0  ] Type : f
08894662107e[CPU0  ] Valid: 1
08894662107e[CPU0  ] Segm : 1
08894662107e[CPU0  ] interrupt(long mode): gate descriptor is not valid sys seg
08894662107e[CPU0  ] Dump gate:
08894662107e[CPU0  ] Type : f
08894662107e[CPU0  ] Valid: 1
08894662107e[CPU0  ] Segm : 1
08894662107e[CPU0  ] interrupt(long mode): gate descriptor is not valid sys seg

I added the Type, Valid and Segment output to the bochs code, which is simply printing the stats of the descriptor being checked:
Code:
// bochs/cpu/exception.cc:64

  if ((gate_descriptor.valid==0) || gate_descriptor.segment)
  {
    BX_ERROR(("Dump gate:"));
    BX_ERROR(("Type : %x", gate_descriptor.type));
    BX_ERROR(("Valid: %x", gate_descriptor.valid ? 1 : 0));
    BX_ERROR(("Segm : %x", gate_descriptor.segment ? 1 : 0));

    BX_ERROR(("interrupt(long mode): gate descriptor is not valid sys seg"));
    exception(BX_GP_EXCEPTION, vector*8 + 2);
  }

Apparently the gate_descriptor.segment value is 1, which shouldn't be:
Code:
// bochs/cpu/descriptor.h:typedef struct bx_descriptor_t
// ...
  bx_bool segment;       /* 0 = system/gate, 1 = data/code segment */
// ...

So apparently my code uses a data/code segment in the storage segment bit of the type_attr byte:
Code:
// From https://wiki.osdev.org/Interrupt_Descriptor_Table#Structure_IA-32

  7                           0
+---+---+---+---+---+---+---+---+
| P |  DPL  | S |    GateType   |
+---+---+---+---+---+---+---+---+
  1   0   0   0   1   1   1   0    --> 0x8E

Even though the storage segment bit (bit 4 of type_attr, S) is set to 0, bochs seems to find a set bit at this place. Also, because my additional output says that the Type member of the bochs descriptor is 0xf, this also means that it is not using an interrupt gate but rather a trap gate. This would evaluate to a type_attr of 0x9f, but I'm setting the type_attr with 0x8e.
I am at loss of ideas how to move on here, because I'm very sure I checked the values in the type_attr member of my struct idt_entry above (0x8E).


Top
 Profile  
 
 Post subject: Re: IDT invalid entry in long mode
PostPosted: Mon Sep 03, 2018 7:12 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 4594
Location: Chichester, UK
You might want to list your ids_set_gate function as that's the most likely place for the error to occur.


Top
 Profile  
 
 Post subject: Re: IDT invalid entry in long mode
PostPosted: Mon Sep 03, 2018 8:05 am 
Offline
Member
Member

Joined: Wed May 02, 2018 1:26 pm
Posts: 55
iansjack wrote:
You might want to list your ids_set_gate function as that's the most likely place for the error to occur.

You're right, I missed that:

Code:
// idt.c

void idt_set_gate(uint8_t idx, uint64_t offset, uint16_t selector, uint8_t type)
{
   idt[idx].offset_low    = (offset & 0xffff);
   idt[idx].offset_middle = (offset >> 16) & 0xffff;
   idt[idx].offset_high   = (offset >> 32) & 0xffffffff;

   idt[idx].selector = selector;
   idt[idx].type = type;

   idt[idx].__r0 = 0;
   idt[idx].__r1 = 0;
}


Top
 Profile  
 
 Post subject: Re: IDT invalid entry in long mode
PostPosted: Mon Sep 03, 2018 8:32 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 4594
Location: Chichester, UK
I can't comment on the Boch's debugging as I don't use Bochs.

But it seems to me that the most likely explanation is that you are getting a page fault when executing the first instruction in your interrupt routine. Is it a stack access instruction? If so, are you sure that your interrupt stack is valid?


Top
 Profile  
 
 Post subject: Re: IDT invalid entry in long mode
PostPosted: Mon Sep 03, 2018 8:42 am 
Offline
Member
Member

Joined: Wed May 02, 2018 1:26 pm
Posts: 55
The first instruction ist the cli instruction, which does not seem to have any effects, whether I am using it or not. The next instruction (the one which leads to the page fault), is a push instruction:

Code:
.macro EXCEPTION code

      .global __isr\code\()
      .type __isr\code\(), @function

   __isr\code\():
      cli

      pushw 0
      pushw \code

      jmp _isr_common_stub

.endm

.macro EXCEPTION_ERRNO code

      .global __isr\code\()
      .type __isr\code\(), @function

   __isr\code\():
      cli

      pushw \code

      jmp _isr_common_stub

.endm

Since the page fault leads to a consecutive invocation of the page fault stub, the stack is growing until I seem to enter real mode again.


Top
 Profile  
 
 Post subject: Re: IDT invalid entry in long mode
PostPosted: Mon Sep 03, 2018 9:05 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 4594
Location: Chichester, UK
Whatever you are using as your kernel stack is invalid. Hence the page fault(s).


Top
 Profile  
 
 Post subject: Re: IDT invalid entry in long mode
PostPosted: Mon Sep 03, 2018 9:16 am 
Offline
Member
Member

Joined: Wed May 02, 2018 1:26 pm
Posts: 55
iansjack wrote:
Whatever you are using as your kernel stack is invalid. Hence the page fault(s).

The only thing I am doing with my kernel stack is the following:
Code:
# entry.s

.code64

.section .text
.align 8

   .global _entry
   .type _entry, @function

_entry:
   cli

   # Setup stack pointer
   mov $kernel_stack_top, %rsp

   # Load multiboot struct
   movq %rbx, %rdi

   # Start bootstrap loader
   call kmain

.loop:
   cli
   hlt
   jmp .loop

.section .kernel_heap
kernel_heap_bottom:
   .fill 0x8000, 1, 0
kernel_heap_top:

.section .kernel_stack
kernel_stack_bottom:
   .fill 0x4000, 1, 0
kernel_stack_top:

The kernel heap and stack both have their own sections in the linker file:
Code:
ENTRY(_entry)

KERNEL_VMA = 0xffffffff80200000;

SECTIONS
{
   . = KERNEL_VMA;
   _kernel = .;

   .text ALIGN(4K) : AT(ADDR(.text) - KERNEL_VMA)
   {
      _text = .;
      *(.text)
      _etext = .;
   }

   .rodata ALIGN(4K) : AT(ADDR(.rodata) - KERNEL_VMA)
   {
      _rodata = .;
      *(.rodata)
      _erodata = .;
   }

   .data ALIGN(4K) : AT(ADDR(.data) - KERNEL_VMA)
   {
      _data = .;
      *(.data)
      _edata = .;
   }

   .bss ALIGN(0x10) : AT(ADDR(.bss) - KERNEL_VMA)
   {
      _bss = .;
      *(COMMON)
      *(.bss)
      *(.kernel_heap)
      *(.kernel_stack)
      _ebss = .;
   }

   /DISCARD/ :
   {
      *(.comment)
   }

   _ekernel = .;
}


Top
 Profile  
 
 Post subject: Re: IDT invalid entry in long mode
PostPosted: Mon Sep 03, 2018 9:31 am 
Offline
Member
Member

Joined: Fri Aug 26, 2016 1:41 pm
Posts: 693
These kinds of questions are often best answered if all the code is available. Do you have a github/gitlab or other online site that you can make all the code for your project available?


Top
 Profile  
 
 Post subject: Re: IDT invalid entry in long mode
PostPosted: Mon Sep 03, 2018 9:37 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 4594
Location: Chichester, UK
I'd suggest that you inspect CR2 to see where the page fault is, and the exception code to determine the cause of the fault. But a page fault on a push instruction looks awfully like a stack problem.


Top
 Profile  
 
 Post subject: Re: IDT invalid entry in long mode
PostPosted: Mon Sep 03, 2018 9:46 am 
Offline
Member
Member

Joined: Wed May 02, 2018 1:26 pm
Posts: 55
iansjack wrote:
I'd suggest that you inspect CR2 to see where the page fault is, and the exception code to determine the cause of the fault. But a page fault on a push instruction looks awfully like a stack problem.

The value in cr2 is simply 0xE at the time of the page fault :/


Top
 Profile  
 
 Post subject: Re: IDT invalid entry in long mode
PostPosted: Mon Sep 03, 2018 11:20 am 
Offline
Member
Member

Joined: Wed May 02, 2018 1:26 pm
Posts: 55
The problem is solved. I shouldn't have used intel syntax in my project as I was using AT&T syntax all the time. I was pushing my error codes via
Code:
pushq 0
, whereas I should have been using
Code:
pushq $0
.
Sorry for the inconveniences ;)


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

All times are UTC - 6 hours


Who is online

Users browsing this forum: 0xY, DotBot [Bot], SemrushBot [Bot] and 110 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