IDT invalid entry in long mode

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
CRoemheld
Member
Member
Posts: 55
Joined: Wed May 02, 2018 1:26 pm
Libera.chat IRC: CRoemheld

IDT invalid entry in long mode

Post by CRoemheld »

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: Select all

	__asm__ volatile("int $0x0");
.
The current setup looks like this:

Code: Select all

// 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: Select all

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: Select all

// 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: Select all

// 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: Select all

// 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).
User avatar
iansjack
Member
Member
Posts: 4706
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: IDT invalid entry in long mode

Post by iansjack »

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

Re: IDT invalid entry in long mode

Post by CRoemheld »

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: Select all

// 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;
}
User avatar
iansjack
Member
Member
Posts: 4706
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: IDT invalid entry in long mode

Post by iansjack »

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?
CRoemheld
Member
Member
Posts: 55
Joined: Wed May 02, 2018 1:26 pm
Libera.chat IRC: CRoemheld

Re: IDT invalid entry in long mode

Post by CRoemheld »

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: Select all

.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.
User avatar
iansjack
Member
Member
Posts: 4706
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: IDT invalid entry in long mode

Post by iansjack »

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

Re: IDT invalid entry in long mode

Post by CRoemheld »

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: Select all

# 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: Select all

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 = .;
}
MichaelPetch
Member
Member
Posts: 799
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: IDT invalid entry in long mode

Post by MichaelPetch »

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?
User avatar
iansjack
Member
Member
Posts: 4706
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: IDT invalid entry in long mode

Post by iansjack »

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.
CRoemheld
Member
Member
Posts: 55
Joined: Wed May 02, 2018 1:26 pm
Libera.chat IRC: CRoemheld

Re: IDT invalid entry in long mode

Post by CRoemheld »

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 :/
CRoemheld
Member
Member
Posts: 55
Joined: Wed May 02, 2018 1:26 pm
Libera.chat IRC: CRoemheld

Re: IDT invalid entry in long mode

Post by CRoemheld »

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: Select all

pushq 0
, whereas I should have been using

Code: Select all

pushq $0
.
Sorry for the inconveniences ;)
Post Reply