IDT troubles

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.
User avatar
restingwitchface
Posts: 17
Joined: Sat Nov 02, 2024 2:58 pm
GitHub: https://github.com/NyxNeptune

IDT troubles

Post by restingwitchface »

Good day! I'm an avid OSdever (was very interested in the theory and recently acquired the sufficient hardware to experiment and make my own). In the current OS I'm building, I'm having difficulty with setting up my IDT. I'm booting using Multiboot - not Multiboot2 - using the skeleton provided in the first "Bare Bones". It's still 32-bit. Most other issues were easy to resolve, but this guy is bugging me; sorry if it's trivial.

My kernel, after setting up the stack, sets up the GDT via (AT&T syntax)

Code: Select all

pushl %eax
pushl %ebx
call load_default_gdt	
popl %ebx
popl %eax
where the C function to actually do the GDT-loading is

Code: Select all

#include <stddef.h>
#include <stdint.h>

typedef struct {
  uint16_t lim1;
  uint16_t base1;
  uint8_t base2;
  uint8_t access;
  uint8_t lim2_flags;
  uint8_t base3;
} gdt_entry_t;

typedef struct {
  uint16_t size;
  uint32_t addr;
} __attribute__ ((packed)) gdtr_t; /* Also used for the IDTR */

gdt_entry_t fill_gdt_entry (uint32_t limit, uint32_t base, uint8_t access) {
  gdt_entry_t entry;
  uint8_t granularity = 0;

  if (limit > 0x100000) {
    granularity = 1;
    limit >>= 12;
  }

  entry.lim1 = limit & 0xFFFF;
  entry.lim2_flags = (limit >> 16) | (granularity << 7) | 0x40;

  entry.base1 = base & 0xFFFF;
  entry.base2 = (base >> 16) & 0xFF;
  entry.base3 = base >> 24;

  entry.access = access;
  return entry;
}

void lgdt (gdt_entry_t *entries, size_t count) {
  gdtr_t gdtr;

  if (count*sizeof(gdt_entry_t) > 0x10000) {
    return;
  }
  
  gdtr.size = count*sizeof(gdt_entry_t)-1;
  gdtr.addr = (uint32_t) entries;

  __asm__ __volatile__ ( "lgdt %0" : : "m" (gdtr) : );
}

void load_default_gdt () {
  gdt_entry_t entries[3];

  entries[0] = fill_gdt_entry(0, 0, 0);
  entries[1] = fill_gdt_entry(0xFFFFFFFF, 0, 0x99);
  entries[2] = fill_gdt_entry(0xFFFFFFFF, 0, 0x93);

  lgdt((gdt_entry_t*) entries, 3);
}
This produces a 4G kernel code segment After we do some other (presumably irrelevant) stuff, we setup the IDT with

Code: Select all

extern uint32_t _isr222;

typedef struct {
  uint16_t offset1;
  uint16_t segment;
  uint8_t resv;
  uint8_t flags;
  uint16_t offset2;
} idt_entry_t;

idt_entry_t fill_idt_entry (uint32_t offset, uint8_t flags, uint16_t segment) {
  idt_entry_t entry;

  entry.offset1 = offset & 0xFFFF;
  entry.segment = 8*segment;
  entry.resv = 0;
  entry.flags = flags;
  entry.offset2 = offset >> 16;
  
  return entry;
}

void load_default_idt (uint32_t isr1) {
  idt_entry_t entries[256];
  size_t idx;

  for (idx = 0; idx < 256; idx++) {
    entries[idx] = fill_idt_entry(0,0,0);
  }

  entries[0xDE] = fill_idt_entry((uint32_t) isr1, 0xCE, 1);

  lidt((idt_entry_t*) entries);
}

load_default_idt(_isr222);
where the code for our inline assembly function LIDT is exactly the same as for LGDT, minus "count" (always 256) and, well, using the LIDT instruction instead of LGDT. In an also-linked-in assembly file I have an assembly wrapper (which is referenced in the above file as an external integer):

Code: Select all

.section .text
.global _isr222
.type _isr222, @function

_isr222:
	pushal
	cld
	call isr_222
	popal
	iret
When I set the IF and interrupt at vector 0xDE, QEMU crashes, but in a weird way: it goes to "SeaBIOS... booting from PXE", then writes like half a line, then the logs from the kernel show again, then it crashes again, etc. It's very weird. When I don't interrupt but still set up the interrupt vector, it doesn't crash, but I obviously don't have a GPF handler.
she/her, please.

Don't flirt with me.
Oderint dum metuant.
GPG fingerprints:

Code: Select all

b16598566a5124946d87279fbc2d2dd299af811e (encryption)
b504ba54c5c164257156f53a32409b59258453e0 (signing)
davmac314
Member
Member
Posts: 121
Joined: Mon Jul 05, 2021 6:57 pm

Re: IDT troubles

Post by davmac314 »

When I set the IF and interrupt at vector 0xDE
"set the IF" is ringing alarm bells. You don't need to do that in order to issue a software interrupt, which is what I assume you are doing.

And, if you do enable interrupts by setting IF, it's possible that there's a pending hardware interrupt which immediately causes a fault (leading to a double and then triple fault, which causes a reset). Leave them disabled. If that doesn't work, use a debugger to step through and find out exactly where it's going wrong.
QEMU crashes, but in a weird way: it goes to "SeaBIOS... booting from PXE", then writes like half a line, then the logs from the kernel show again, then it crashes again, etc. It's very weird.
That's not really weird at all, it's a classic "triple-fault reset" boot loop. Beginners get those a lot.

(I suggest setting up handlers for the exceptions before you try to get general interrupts (or even anything else) working. It will be very useful for debugging.)
nullplan
Member
Member
Posts: 1779
Joined: Wed Aug 30, 2017 8:24 am

Re: IDT troubles

Post by nullplan »

Yes, I also think there is an issue with your exception handlers. For now, at the start, you should make each exception handler just say its name, and the CS:EIP it was called from (part of the interrupt frame) before halting the system. That will tell you exactly which instruction caused which exception.

Later, when you have userspace processes, you may want to notify them of the crash in some way, but that is then.
Carpe diem!
User avatar
restingwitchface
Posts: 17
Joined: Sat Nov 02, 2024 2:58 pm
GitHub: https://github.com/NyxNeptune

Re: IDT troubles

Post by restingwitchface »

Yes, immediately after posting this I guessed it might be a triple fault, but wasn't sure how it would be triggered. Since paging is disabled and my segments all started at 0, I didn't take the possibility of not being able to find the interrupt handler into account.

This error also occurred without an explicit sti instruction before the int 0xDE. The only thing that the interrupt handler was doing is printing "I was interrupted at vector 0xDE!" and halting, as you suggested.

Anyways, by changing the access byte on segment 1 to 0x9A (rather than 0x99, which has read permissions, stupidly, disabled), it doesn't triple fault, but the message isn't printed either. I'll set up some exception handlers to see if it's a GPF or something.
she/her, please.

Don't flirt with me.
Oderint dum metuant.
GPG fingerprints:

Code: Select all

b16598566a5124946d87279fbc2d2dd299af811e (encryption)
b504ba54c5c164257156f53a32409b59258453e0 (signing)
User avatar
restingwitchface
Posts: 17
Joined: Sat Nov 02, 2024 2:58 pm
GitHub: https://github.com/NyxNeptune

Re: IDT troubles

Post by restingwitchface »

I set up the following exception handlers:

Code: Select all

void isr_0 () {
  panic("Division by zero!");
}

void isr_6 () {
  panic("Undefined opcode!");
}

void isr_8 () {
  panic("Double fault!");
}

void isr_11 () {
  panic("Segment not present!");
}

void isr_13 () {
  panic("General protection fault!");
}

void load_default_idt () {
  idt_entry_t entries[256];
  size_t idx;

  for (idx = 0; idx < 256; idx++) {
    entries[idx] = fill_idt_entry(0,0,0);
  }

  entries[0] = fill_idt_entry(_isr0, 0x8E, 1);
  entries[6] = fill_idt_entry(_isr6, 0x8E, 1);
  entries[8] = fill_idt_entry(_isr8, 0x8E, 1);
  entries[11] = fill_idt_entry(_isr11, 0x8E, 1);
  entries[13] = fill_idt_entry(_isr13, 0x8E, 1);

  lidt((idt_entry_t*) entries);
}
but a division by zero or explicit int 0 now does nothing...
she/her, please.

Don't flirt with me.
Oderint dum metuant.
GPG fingerprints:

Code: Select all

b16598566a5124946d87279fbc2d2dd299af811e (encryption)
b504ba54c5c164257156f53a32409b59258453e0 (signing)
MichaelPetch
Member
Member
Posts: 791
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: IDT troubles

Post by MichaelPetch »

restingwitchface wrote: Sun Nov 03, 2024 6:48 am but a division by zero or explicit int 0 now does nothing...
This could be any number of things without seeing all your code and how you build things. One thing that comes to mind is if you are creating a custom bootloader (are you?) - are you reading enough sectors to read the entire kernel? Adding string literals in C can bloat the kernel size (depending on how you build things) and often it results in strings not being displayed. If you are using GRUB/multiboot/Limine as a bootloader then this likely isn't the problem.

I see you have a Github account. If you were to commit your code to a repository we could better assist you.
User avatar
restingwitchface
Posts: 17
Joined: Sat Nov 02, 2024 2:58 pm
GitHub: https://github.com/NyxNeptune

Re: IDT troubles

Post by restingwitchface »

No, I'm just booting the Multiboot image directly from QEMU, via

Code: Select all

qemu-system-x86_64 -kernel [...] -initrd [...]
I haven't tested it on real hardware yet, but QEMU should be pretty accurate. I don't generally like to put unfinished stuff on Git. If I still can't figure it out later - since I haven't devoted much time after my post earlier - I'll put it on there.
she/her, please.

Don't flirt with me.
Oderint dum metuant.
GPG fingerprints:

Code: Select all

b16598566a5124946d87279fbc2d2dd299af811e (encryption)
b504ba54c5c164257156f53a32409b59258453e0 (signing)
MichaelPetch
Member
Member
Posts: 791
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: IDT troubles

Post by MichaelPetch »

If you are using something supporting Mulitboot (the -kernel option does) then what I suggested as one possible problem (the kernel not being read in memory completely) is not likely the issue at all.

Since you are using mulitboot do you create and load your own GDT and then reload CS (with a FAR JMP or equivalent and then reload the segment registers? Run QEMU with the options `-d int -no-shutdown -no-reboot` to see what exceptions were thrown if any. You could do that and then post the last few exceptions (the last 50 lines should suffice) here in a comment.
User avatar
restingwitchface
Posts: 17
Joined: Sat Nov 02, 2024 2:58 pm
GitHub: https://github.com/NyxNeptune

Re: IDT troubles

Post by restingwitchface »

Thank you! Most of the logs seem to be SMIs, but here are the last 57 lines.

Code: Select all

SMM: enter
EAX=000000b5 EBX=00008a80 ECX=00005678 EDX=00000005
ESI=00000000 EDI=06fff5ce EBP=0000696e ESP=0000696e
EIP=000f8a7d EFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS   [-WA]
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS   [-WA]
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS   [-WA]
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS   [-WA]
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS   [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT=     000f7460 00000037
IDT=     000f749e 00000000
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000008 CCD=00006954 CCO=ADDL
EFER=0000000000000000
SMM: after RSM
EAX=000000b5 EBX=00008a80 ECX=00005678 EDX=00000003
ESI=06f33300 EDI=06fff5ce EBP=00006968 ESP=00006968
EIP=00008a80 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =dd00 000dd000 ffffffff 00809300
CS =f000 000f0000 ffffffff 00809b00
SS =0000 00000000 ffffffff 00809300
DS =0000 00000000 ffffffff 00809300
FS =0000 00000000 ffffffff 00809300
GS =c900 000c9000 ffffffff 00809300
LDT=0000 00000000 0000ffff 00008200
TR =0000 00000000 0000ffff 00008b00
GDT=     00000000 00000000
IDT=     00000000 000003ff
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000000 CCD=00000001 CCO=EFLAGS
EFER=0000000000000000
     0: v=00 e=0000 i=1 cpl=0 IP=0008:0000000000200acd pc=0000000000200acd SP=0010:0000000000206fd0 env->regs[R_EAX]=00000000000007ff
EAX=000007ff EBX=00209060 ECX=002067c8 EDX=00000000
ESI=00000000 EDI=00002000 EBP=00206ff8 ESP=00206fd0
EIP=00200acd EFL=00000007 [-----PC] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT=     00206fd8 00000017
IDT=     002067c8 000007ff
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=000065d1 CCD=000032e8 CCO=SARL
EFER=0000000000000000
she/her, please.

Don't flirt with me.
Oderint dum metuant.
GPG fingerprints:

Code: Select all

b16598566a5124946d87279fbc2d2dd299af811e (encryption)
b504ba54c5c164257156f53a32409b59258453e0 (signing)
MichaelPetch
Member
Member
Posts: 791
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: IDT troubles

Post by MichaelPetch »

If those are the last lines and there was nothing after then it appears that software interrupt 0 or a div by zero was raised. v=00 is the exception 0 interrupt/exception. Since there was nothing faulting afterwards and the CS and data segment registers look okay I can only say that it appears an exception was successfully raised and that the processor did something until it sat and did nothing else. So something in the exception handling prevented you from giving a message about the fault raised. Maybe something with your _isr0 stub handler for interrupt 0 is wrong or if there is a problem with the `panic` code? What does `panic` do? Write to the display? serial port? If a serial port did you tell QEMU to send it to a file or stdio?
User avatar
restingwitchface
Posts: 17
Joined: Sat Nov 02, 2024 2:58 pm
GitHub: https://github.com/NyxNeptune

Re: IDT troubles

Post by restingwitchface »

Alright. My panic function prints the desired message to stdout (VGA), as well as EAX, EBX, ECX, EDX, EIP (before the call), ESP, EBP, ESI, EDI & EFLAGS, and I already verified that it works normally.
she/her, please.

Don't flirt with me.
Oderint dum metuant.
GPG fingerprints:

Code: Select all

b16598566a5124946d87279fbc2d2dd299af811e (encryption)
b504ba54c5c164257156f53a32409b59258453e0 (signing)
MichaelPetch
Member
Member
Posts: 791
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: IDT troubles

Post by MichaelPetch »

Okay so your panic code does what is expected? So how does it not work afterwards. What were you expecting to happen vs what you are observing? For instance if you used `int $0x0` to raise interrupt 0 then it should continue with the code after the software interrupt and run whatever that was. If you caused a div by 0 then when IRET returns it will attempt to execute the same instruction that cause the div by zero and repeatedly do that forever (it will appear to hang). You'd also see a flood of v=00 in the log while in that loop which we don't see in the log. The exception being if your interrupt handler modified the return address (or registers on the stack) before the IRET but that likely doesn't seem like something you would have done? Or did you?

If panic goes into an infinite loop or does a `hlt` of course it will never return.
User avatar
restingwitchface
Posts: 17
Joined: Sat Nov 02, 2024 2:58 pm
GitHub: https://github.com/NyxNeptune

Re: IDT troubles

Post by restingwitchface »

> What were you expecting to happen vs what you are observing?
I expected that int0 calls _isr0 calls isr_0 calls panic, which prints "Division by zero!", the registers in hex format (this subroutine I have tested), and hangs. I am observing that it *just* hangs, without executing the panic. The QEMU logs still show nothing after the seemingly successful int 0. It is frustrating that it does not describe where it thinks the interrupt is, just EIP when interrupting, since else I could check against objdump for where it's going.
she/her, please.

Don't flirt with me.
Oderint dum metuant.
GPG fingerprints:

Code: Select all

b16598566a5124946d87279fbc2d2dd299af811e (encryption)
b504ba54c5c164257156f53a32409b59258453e0 (signing)
sebihepp
Member
Member
Posts: 184
Joined: Tue Aug 26, 2008 11:24 am
GitHub: https://github.com/sebihepp

Re: IDT troubles

Post by sebihepp »

As int 0 is raised without triple fault and only printing Text doesnt Work, please Check if you set your access to video memory as volatile.
User avatar
restingwitchface
Posts: 17
Joined: Sat Nov 02, 2024 2:58 pm
GitHub: https://github.com/NyxNeptune

Re: IDT troubles

Post by restingwitchface »

No change.
she/her, please.

Don't flirt with me.
Oderint dum metuant.
GPG fingerprints:

Code: Select all

b16598566a5124946d87279fbc2d2dd299af811e (encryption)
b504ba54c5c164257156f53a32409b59258453e0 (signing)
Post Reply