Page 1 of 1

JamesM GDT & IDT tutorial causes triple fault in long mode.

Posted: Thu Jul 28, 2022 10:52 pm
by Techflash
I have used James Molloy's GDT & IDT tutorial as a base for getting interrupts set up.
I get a triple fault when running the `lgdt` instruction. Bochs produced some logs that might be useful to diagnose the issue:

Code: Select all

07797480380e[CPU0  ] interrupt(long mode): IDT entry extended attributes DWORD4 TYPE != 0
07797480380e[CPU0  ] interrupt(long mode): IDT entry extended attributes DWORD4 TYPE != 0
(0).[7797480380] [0x00000010fe3a] 0008:ffffffffffe02e3a (unk. ctxt): lgdt %ds:(%rax)           ; 0f0110
07797480380p[CPU0  ] >>PANIC<< exception(): 3rd (13) exception with no resolution
After a little bit of poking around. It seems that the following things are happening leading up the the lgdt call:
1. The function is called
2. We dereference the stack pointer + 4 bytes and move that into rax [The result I got in rax just before the lgdt call was 00000000_ffffffff in the bochs debugger, this seems wrong]
3. We dereference rax and lgdt that [00000000_ffffffff]

I have no idea what is going on that is making this break so horribly. I have pushed the current code to my GitHub repo if you want to see what's going on. The related code is in kernel/hardware/CPU/interrupts.
The github repo is https://github.com/techflashYT/Techflash-OS

Re: JamesM GDT & IDT tutorial causes triple fault in long mo

Posted: Thu Jul 28, 2022 11:19 pm
by Octocontrabass
Techflash wrote:2. We dereference the stack pointer + 4 bytes and move that into rax [The result I got in rax just before the lgdt call was 00000000_ffffffff in the bochs debugger, this seems wrong]
It seems wrong because it is wrong. James Molloy's tutorial uses the System V i386 psABI, but your compiler is using the System V x86-64 psABI. In the x86-64 psABI, the parameter is passed in the EDI register, not on the stack.

You might want to read the overview of calling conventions on the wiki or this list of x86 calling conventions on Wikipedia. If you're interested in reading the ABI documentation itself, here is the i386 psABI and you can click the "download" icon on this page to get the x86-64 psABI. There's also plenty of example code out there; just ask your favorite search engine for parameter passing in x86-64 assembly.

Speaking of James Molloy's tutorial, it has quite a few bugs.

Re: JamesM GDT & IDT tutorial causes triple fault in long mo

Posted: Thu Jul 28, 2022 11:23 pm
by Techflash
Octocontrabass wrote:
Techflash wrote:2. We dereference the stack pointer + 4 bytes and move that into rax [The result I got in rax just before the lgdt call was 00000000_ffffffff in the bochs debugger, this seems wrong]
It seems wrong because it is wrong. James Molloy's tutorial uses the System V i386 psABI, but your compiler is using the System V x86-64 psABI. In the x86-64 psABI, the parameter is passed in the EDI register, not on the stack.

You might want to read the overview of calling conventions on the wiki or this list of x86 calling conventions on Wikipedia. If you're interested in reading the ABI documentation itself, here is the i386 psABI and you can click the "download" icon on this page to get the x86-64 psABI. There's also plenty of example code out there; just ask your favorite search engine for parameter passing in x86-64 assembly.

Speaking of James Molloy's tutorial, it has quite a few bugs.
Ahh. I didn't know there was a difference between x86 and x86_64 calling conventions. And I knew it was using the System V ABI.

Also yup, I read that page already. I didn't use that tutorial as the entire base. I just used it for interrupts.

Re: JamesM GDT & IDT tutorial causes triple fault in long mo

Posted: Thu Jul 28, 2022 11:32 pm
by Techflash
UPDATE:
Well now it seems to be reading the GDT correctly. But it has a new issue:
It gets past the lgdt line alright.
But when it tries to move ax [0x10] into ds:

Code: Select all

07097422515e[CPU0  ] load_seg_reg(DS, 0x0010): segment not present
07097422515e[CPU0  ] interrupt(long mode): IDT entry extended attributes DWORD4 TYPE != 0
07097422515e[CPU0  ] interrupt(long mode): IDT entry extended attributes DWORD4 TYPE != 0
(0).[7097422515] [0x00000010fe3d] 0008:ffffffffffe02e3d (unk. ctxt): mov ds, ax                ; 8ed8
07097422515p[CPU0  ] >>PANIC<< exception(): 3rd (13) exception with no resolution
It can't seem to find the new data segment? It then complains about the IDT (which hasn't yet been set up, since the code is still working on the GDT).
I'm starting to think this is going to be a much bigger rabbit hole than I originally thought.

Re: JamesM GDT & IDT tutorial causes triple fault in long mo

Posted: Thu Jul 28, 2022 11:38 pm
by Octocontrabass
Techflash wrote:It can't seem to find the new data segment?
Yeah, that sounds about right when you're throwing away half of the GDT base address. (In long mode, the base is 64 bits instead of 32.)

Re: JamesM GDT & IDT tutorial causes triple fault in long mo

Posted: Thu Jul 28, 2022 11:48 pm
by Techflash
Octocontrabass wrote:
Techflash wrote:It can't seem to find the new data segment?
Yeah, that sounds about right when you're throwing away half of the GDT base address. (In long mode, the base is 64 bits instead of 32.)
Ahh. Fixed that. But it still gives the same error. Could these lines be at fault? That's the only other related part I could think of that could be an issue still.

Code: Select all

	gdtEntries[num].baseLow = (base & 0xFFFF);
	gdtEntries[num].baseMiddle = (base >> 16) & 0xFF;
	gdtEntries[num].baseHigh = (base >> 24) & 0xFF;

Re: JamesM GDT & IDT tutorial causes triple fault in long mo

Posted: Thu Jul 28, 2022 11:56 pm
by Octocontrabass
In 64-bit mode, the base is ignored for the CS, DS, ES, and SS registers, so it doesn't matter what you put there - the CPU will ignore it.

But you still need the correct number of bits for that unused space. The comment is correct, but the structure doesn't match the comment.

Re: JamesM GDT & IDT tutorial causes triple fault in long mo

Posted: Thu Jul 28, 2022 11:58 pm
by Techflash
Octocontrabass wrote:In 64-bit mode, the base is ignored for the CS, DS, ES, and SS registers, so it doesn't matter what you put there - the CPU will ignore it.

But you still need the correct number of bits for that unused space. The comment is correct, but the structure doesn't match the comment.
Huh. Nice catch. Must have made a typo when I was fixing the types from the non standard "u*int" that the JamesM tutorials use. I'll update this post with the results once it finishes compiling.

UPDATE: Same error again. Honestly kinda surprised. I would have thought that it would give a different error at some point.
UPDATE 2: Just double checked the original code. It matches now. But a little math shows that the combination of "baseLow", "baseMiddle", and "baseHigh", only gives 32-bits.... Would doubling each of the types to: uint32_t, uint16_t, uint16_t respectively work correctly then?
UPDATE 3: Nope, that didn't work.

Re: JamesM GDT & IDT tutorial causes triple fault in long mo

Posted: Fri Jul 29, 2022 12:40 am
by Octocontrabass
Techflash wrote:UPDATE: Same error again. Honestly kinda surprised. I would have thought that it would give a different error at some point.
Huh. I didn't see any other obvious mistakes that would lead to a non-present segment. Sounds like you'll have to dig a little bit deeper with your debugger. (I hear the Bochs debugger can dump the GDT...)
Techflash wrote:But a little math shows that the combination of "baseLow", "baseMiddle", and "baseHigh", only gives 32-bits.... Would doubling each of the types to: uint32_t, uint16_t, uint16_t respectively work correctly then?
No. Since there's no segmentation in 64-bit mode, AMD decided to leave those fields the same size.

Re: JamesM GDT & IDT tutorial causes triple fault in long mo

Posted: Fri Jul 29, 2022 12:54 am
by Techflash
Octocontrabass wrote:
Techflash wrote:UPDATE: Same error again. Honestly kinda surprised. I would have thought that it would give a different error at some point.
Huh. I didn't see any other obvious mistakes that would lead to a non-present segment. Sounds like you'll have to dig a little bit deeper with your debugger. (I hear the Bochs debugger can dump the GDT...)
Techflash wrote:But a little math shows that the combination of "baseLow", "baseMiddle", and "baseHigh", only gives 32-bits.... Would doubling each of the types to: uint32_t, uint16_t, uint16_t respectively work correctly then?
No. Since there's no segmentation in 64-bit mode, AMD decided to leave those fields the same size.
Alright. I switched the types back, and I found a command that looks like it dumps something related to the GDT:

Code: Select all

es:0x0010, dh=0x00809300, dl=0x0000ffff, valid=1
        Data segment, base=0x00000000, limit=0x0fffffff, Read/Write, Accessed
cs:0x0008, dh=0x00209900, dl=0x0000ffff, valid=1
        Code segment, base=0x00000000, limit=0x0000ffff, Execute-Only, Non-Conforming, Accessed, 64-bit
ss:0x0010, dh=0x00809300, dl=0x0000ffff, valid=1
        Data segment, base=0x00000000, limit=0x0fffffff, Read/Write, Accessed
ds:0x0010, dh=0x00809300, dl=0x0000ffff, valid=1
        Data segment, base=0x00000000, limit=0x0fffffff, Read/Write, Accessed
fs:0x0010, dh=0x00809300, dl=0x0000ffff, valid=1
        Data segment, base=0x00000000, limit=0x0fffffff, Read/Write, Accessed
gs:0x0010, dh=0x00809300, dl=0x0000ffff, valid=1
        Data segment, base=0x00000000, limit=0x0fffffff, Read/Write, Accessed
ldtr:0x0000, dh=0x00008200, dl=0x0000ffff, valid=1
tr:0x0000, dh=0x00008b00, dl=0x0000ffff, valid=1
gdtr:base=0xffffffffffffffff, limit=0xffff
idtr:base=0x0000000000000000, limit=0x3ff
This was taken just before moving ax into ds.

Re: JamesM GDT & IDT tutorial causes triple fault in long mo

Posted: Fri Jul 29, 2022 1:04 am
by Octocontrabass
Techflash wrote:

Code: Select all

gdtr:base=0xffffffffffffffff, limit=0xffff
Well, that's obviously wrong.

And I think I see the problem. Addresses are 64 bits, that should be RDI instead of EDI.

Re: JamesM GDT & IDT tutorial causes triple fault in long mo

Posted: Fri Jul 29, 2022 1:09 am
by Techflash
Octocontrabass wrote:
Techflash wrote:

Code: Select all

gdtr:base=0xffffffffffffffff, limit=0xffff
Well, that's obviously wrong.

And I think I see the problem. Addresses are 64 bits, that should be RDI instead of EDI.
Ahh. That makes a lot more sense. One sec while I try it.
UPDATE: YAY! It now finally triple faults yet again but in the IDT [likely very similar issues]
UPDATE 2: After applying similar patches the the IDT code (switching the bases to uint64s, and swapping some code around in the assembly loader), it now works correctly! Thanks for all the help. I probably would have never figured this out otherwise.