Clearing interrupts causes a crash

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
gsuberland
Posts: 2
Joined: Fri Apr 09, 2021 11:53 am
Libera.chat IRC: gsuberland

Clearing interrupts causes a crash

Post by gsuberland »

I'm following the GDT Tutorial page from the wiki and appear to have gotten my descriptor table up and working, but the one oddity I'm running into is that clearing interrupts with cli causes an instant crash.

My kernel is basically the same as what's in the bare bones page. I'm running it under Hyper-V. It starts from GRUB. It sets up a stack in the .bss section, then transfers control to C code. The C code initialises VGA and prints some debug output so I can see the state of the registers and segment addresses. I then build 6 GDT entries: a null entry, code and data segments for DLP0 and DLP3, and a TSS entry (which I'm not using for anything just yet).

I wanted to specify separate code and data segments for ring 0, so I could get separation of executable pages and read/write pages, but I haven't quite figured out the linker scripts to ensure that everything goes into the right places for that. So for now my DLP0 code and data entries point at 0x00000000, with the code entry being 0x0FFFFFFF long and the data entry being 0x1FFFFFFF long. The .data and .bss sections get loaded at 0x10000000, so they're outside of the executable segment.

I'm applying the GDT as follows:

Code: Select all

apply_gdt:
    mov eax, dword ptr [esp + 4]
    mov dword ptr [gdtr + 2], eax
    mov ax, word ptr [esp + 8]
    mov word ptr [gdtr], ax
    lgdt [gdtr]
    ret

reload_gdt:
    jmp 0x08:reload_CS
reload_CS:
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    ret
This is called as follows:

Code: Select all

void gdt_apply(void)
{
    apply_gdt(KernelGDT, sizeof(KernelGDT) - 1);
    reload_gdt();
}
(side note: in the wiki, the example says that the code assumes it's being called with sizeof(GDT), but as I understand it the size should be one less than that due to the way that the size field works)

This appears to work fine. Dumping the segment registers shows that they've changed over. Code continues to execute just fine. I suspect that it's not good practice to return back to C between applying the GDT and reloading the segment registers, but I don't think it causes any issues for now based on the disassembly I've looked at.

However, the wiki page also says that interrupts should be cleared before applying the GDT. I can see why this would be important - you don't want interrupts landing mid-way through your segment reload and messing everything up. So I used the cli/sti mnemonics to clear interrupts during this process, and it crashes immediately - I presume with a triple fault.

To rule things out, I added a cli followed by sti in the _start assembly, way before I even get close to setting up the GDT. This also causes an immediate crash.

I'm a bit confused as to why this occurs. Reading the instruction reference for cli, specifically the cases in which a GPF can be raised, I suspect I might be missing a key detail about protected-mode virtual interrupts, which I'm not particularly familiar with. What's going on here?
thewrongchristian
Member
Member
Posts: 426
Joined: Tue Apr 03, 2018 2:44 am

Re: Clearing interrupts causes a crash

Post by thewrongchristian »

gsuberland wrote:So I used the cli/sti mnemonics to clear interrupts during this process, and it crashes immediately - I presume with a triple fault.

To rule things out, I added a cli followed by sti in the _start assembly, way before I even get close to setting up the GDT. This also causes an immediate crash.

I'm a bit confused as to why this occurs. Reading the instruction reference for cli, specifically the cases in which a GPF can be raised, I suspect I might be missing a key detail about protected-mode virtual interrupts, which I'm not particularly familiar with. What's going on here?
Are you using cli and sti? In what order, and do you know which one triggers the fault?

I suspect it's actually sti that is triggering the fault, as that enables interrupts, and it's probably an incoming interrupt that is causing the crash (because it doesn't sound like you've set up the IDT yet.) cli just clears the interrupt flag, disabling interrupts.

If it is cli that is causing the crash, then it's probably a GPF, which will occur if you're not running in a privileged ring. When you set up your protected mode environment, did you jump to the RING 0 code segment or the RING 3 code segment?
User avatar
iansjack
Member
Member
Posts: 4703
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Enabling interrupts causes a crash

Post by iansjack »

You need to set up an IDT, and interrupt handlers, for any potential interrupt before enabling interrupts. It's also sensible to set up basic exception handlers.

My guess would be a timer interrupt.
gsuberland
Posts: 2
Joined: Fri Apr 09, 2021 11:53 am
Libera.chat IRC: gsuberland

Re: Clearing interrupts causes a crash

Post by gsuberland »

Thanks all. I figured this out last night while the post was awaiting approval. It was indeed that I was hitting sti before I had an IDT set up - I hadn't considered that GRUB would start the kernel with interrupts disabled, but of course that's exactly what was happening.

The critical detail that allowed me to figure it out was switching to qemu and using the debugging logs. I could see that cli and sti were being executed, but the instruction immediately afterwards, at the faulting IP, was not the next one from my function. I quickly realised it must be jumping to a garbage ISR, and the qemu debug logs showed that there was indeed an interrupt being fired.

This is solved, and I now have a working IDT and ISRs set up! :)
Post Reply