Page 1 of 2
Loading GDT causes restart loop
Posted: Sun Apr 14, 2024 8:07 am
by 0xY
Hi, I'm trying to make a kernel using UEFI and I'm trying to load a GDT. However, when I call my loading function, my kernel will start, and then restart after a few seconds. All of the following happens after I call ExitBootServices in my bootloader. I do not have paging or anything like that enabled, I have only acquired the memory map so far. Can anyone help out? Im using QEMU to run the kernel.
Code: Select all
load_gdt:
lgdt [rdi]
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
pop rdi
mov rax, 0x08
push rax
push rdi
retfq
typedef struct _gdt_desc {
uint16_t limit;
uint64_t offset;
} __attribute__((packed)) GDT_DESCRIPTOR;
typedef struct _gdt_entry {
uint16_t limit0;
uint16_t base0;
uint8_t base1;
uint8_t access_byte;
uint8_t limit1_flags;
uint8_t base2;
} __attribute__((packed)) GDT_ENTRY;
typedef struct _gdt {
GDT_ENTRY null;
GDT_ENTRY kernel_code;
GDT_ENTRY kernel_data;
GDT_ENTRY user_null;
GDT_ENTRY user_code;
GDT_ENTRY user_data;
} __attribute__((packed)) __attribute__((aligned(0x1000))) GDT;
__attribute__((aligned(0x1000)))
GDT default_gdt = {
{0, 0, 0, 0x00, 0x00, 0},
{0, 0, 0, 0x9a, 0xa0, 0},
{0, 0, 0, 0x92, 0xa0, 0},
{0, 0, 0, 0x00, 0x00, 0},
{0, 0, 0, 0x9a, 0xa0, 0},
{0, 0, 0, 0x92, 0xa0, 0},
};
extern void load_gdt(GDT_DESCRIPTOR* gdt_descriptor);
And in my kernel main function:
Code: Select all
GDT_DESCRIPTOR gdt_desc;
gdt_desc.limit = sizeof(GDT) - 1;
gdt_desc.offset = (uint64_t)&default_gdt;
load_gdt(&gdt_desc);
I have also tried to disable interrupts before calling load_gdt with
but that didn't help. I paused QEMU once my kernel loaded and took a screenshot of QEMU's 'info registers' output, here is what it looks like
Can anyone help me figure this out? Thanks.
Re: Loading GDT causes restart loop
Posted: Sun Apr 14, 2024 10:09 am
by nullplan
0xY wrote:I do not have paging or anything like that enabled,
The code you have written is 64-bit code. 64-bit mode
always has paging enabled. UEFI by default will give you an identity mapping, but apparently it doesn't go all the way up.
With that out of the way, while I think your GDT data descriptor is invalid, apparently it is good enough for QEMU. I also shouldn't think that cli will make a difference, since the interrupt flag is supposed to be disabled when your code is running. And otherwise the register dump doesn't give us a whole lot of info. Except one thing: You are overaligning your GDT to a page boundary, and the GDTR in the register dump is not aligned that much. In fact, it looks like it is located in the boot ROM. So that register dump is from after the reboot.
Re: Loading GDT causes restart loop
Posted: Sun Apr 14, 2024 10:37 am
by MichaelPetch
I'd recommend running QEMU with `-d int -no-reboot -no-restart` . This would tell us a significant amount of information about the exceptions. You could post the output here - you can trim off the SMM interrupts at the start. The last few exceptions (and all their info) should suffice (they start with v=xx) where xx are the exception number in hex.
Like nullplan I find it curious that the GDT is at memory address 0xffffff60 . If memory is identity mapped that is either the boot ROM or could be somewhere else.
Re: Loading GDT causes restart loop
Posted: Sun Apr 14, 2024 10:55 am
by 0xY
Thanks for your replies
MichaelPetch wrote:I'd recommend running QEMU with `-d int -no-reboot -no-restart`.
If I run QEMU with the above options (except -no-restart because QEMU says that is an invalid option), nothing boots and I get the following output in the console, it seems to be the same thing repeating:
Code: Select all
Servicing hardware INT=0x68
40: v=68 e=0000 i=0 cpl=0 IP=0038:000000000f58752a pc=000000000f58752a SP=0030:000000000ff07c40 env->regs[R_EAX]=0000000000000000
RAX=0000000000000000 RBX=00000000000186a0 RCX=0000000000000000 RDX=00000000000004d1
RSI=0000000000000000 RDI=000000000000fffe RBP=0000000000000000 RSP=000000000ff07c40
R8 =0000000000000000 R9 =000000000f588b20 R10=0000000000000002 R11=0000000000000010
R12=000000000ff07cb8 R13=0000000000000000 R14=000000000ff2a720 R15=000000000ff07dc0
RIP=000000000f58752a RFL=00000246 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS [-WA]
CS =0038 0000000000000000 ffffffff 00af9a00 DPL=0 CS64 [-R-]
SS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS [-WA]
DS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS [-WA]
FS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS [-WA]
GS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS [-WA]
LDT=0000 0000000000000000 0000ffff 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT= 000000000f9ee698 00000047
IDT= 000000000f585018 00000fff
CR0=80010033 CR2=0000000000000000 CR3=000000000fc01000 CR4=00000668
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=0000000000000044 CCD=0000000000000000 CCO=EFLAGS
EFER=0000000000000d00
MichaelPetch wrote:Like nullplan I find it curious that the GDT is at memory address 0xffffff60 . If memory is identity mapped that is either the boot ROM or could be somewhere else. Since the GDTR is on the stack, I assume that RSP is in fact 0xffffffxx as well. How do you set your stack pointer (RSP) and with what start value? The stack should be set somewhere valid.
I wasn't aware that RSP needs to be set, I haven't seen this happen in any of the resources I used so far. Their GDT loading functions in assembly seem to be exactly the same as mine.
Re: Loading GDT causes restart loop
Posted: Sun Apr 14, 2024 11:01 am
by MichaelPetch
About RSP I deleted that part of the comment because I realized I had read your register dump wrong. Disregard that. As well I meant -no-shutdown and not -no-restart . Can you try that again with the right options `-d int -no-reboot -no-shutdown`? Sorry. If your kernel in fact crashed eventually that output should stop. If it continues then there is something else going on. The output you show almost seems to be while UEFI firmware was running.
Re: Loading GDT causes restart loop
Posted: Sun Apr 14, 2024 11:19 am
by 0xY
With the above options, QEMU still doesn't launch anything, see the screenshot, and the output is the same as before. Could this be an issue with this particular version of QEMU?
Code: Select all
Servicing hardware INT=0x68
19: v=68 e=0000 i=0 cpl=0 IP=0038:000000000f58752a pc=000000000f58752a SP=0030:000000000ff07c40 env->regs[R_EAX]=0000000000000000
RAX=0000000000000000 RBX=00000000000186a0 RCX=0000000000000000 RDX=00000000000004d1
RSI=0000000000000000 RDI=000000000000fffe RBP=0000000000000000 RSP=000000000ff07c40
R8 =0000000000000000 R9 =000000000f588b20 R10=0000000000000002 R11=0000000000000010
R12=000000000ff07cb8 R13=0000000000000000 R14=000000000ff2a720 R15=000000000ff07dc0
RIP=000000000f58752a RFL=00000246 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS [-WA]
CS =0038 0000000000000000 ffffffff 00af9a00 DPL=0 CS64 [-R-]
SS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS [-WA]
DS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS [-WA]
FS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS [-WA]
GS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS [-WA]
LDT=0000 0000000000000000 0000ffff 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT= 000000000f9ee698 00000047
IDT= 000000000f585018 00000fff
CR0=80010033 CR2=0000000000000000 CR3=000000000fc01000 CR4=00000668
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=0000000000000044 CCD=0000000000000000 CCO=EFLAGS
EFER=0000000000000d00
Re: Loading GDT causes restart loop
Posted: Sun Apr 14, 2024 11:27 am
by MichaelPetch
What is weird is that the output for v=68 suggests it is servicing hardware interrupts and the selectors are set to thing that seem totally unrelated to your code. It almost seems like it is stuck somewhere before it even reached your kernel. I doubt this is a QEMU bug (it is possible but doubtful).
The other possibility is that the output is slowing down the emulator and you just have to wait quite a bit longer for it to reach your kernel code. Redirecting QEMU's output to a file might speed things up.
Re: Loading GDT causes restart loop
Posted: Sun Apr 14, 2024 11:28 am
by MichaelPetch
It might be more useful to put your entire project into Github or similar service so we can look at all the code and if need be rebuild it and test it.
Re: Loading GDT causes restart loop
Posted: Sun Apr 14, 2024 12:18 pm
by 0xY
Here is the repo on github, you can see all the relevant code:
https://github.com/crystalw1ngs/kernel
Re: Loading GDT causes restart loop
Posted: Sun Apr 14, 2024 12:54 pm
by 0xY
MichaelPetch wrote:Redirecting QEMU's output to a file might speed things up.
This was a good idea, it does boot in the end, it looks like it crashes upon calling the kernel start function since my bootloader seems to output everything correctly until its time to call the kernel. Here is the output from QEMU:
Code: Select all
check_exception old: 0xffffffff new 0xd
947: v=0d e=73b0 i=0 cpl=0 IP=0038:0000000000000c56 pc=0000000000000c56 SP=0010:000000000ff072d0 env->regs[R_EAX]=0000000000000008
RAX=0000000000000008 RBX=000000000ea16f98 RCX=0000000000000030 RDX=0000000000000000
RSI=000000000ff07384 RDI=000000000f8eef98 RBP=000000000ff29d40 RSP=000000000ff072d0
R8 =00000000b29952e3 R9 =000000000f167fc7 R10=0000000000000034 R11=0000000000400000
R12=000000000ff073a8 R13=000000000ea16c40 R14=000000000ff073b0 R15=000000000ff073a0
RIP=0000000000000c56 RFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 0000000000000000 00000fff 00a09300 DPL=0 DS [-WA]
CS =0038 0000000000000000 ffffffff 00af9a00 DPL=0 CS64 [-R-]
SS =0010 0000000000000000 00000fff 00a09300 DPL=0 DS [-WA]
DS =0010 0000000000000000 00000fff 00a09300 DPL=0 DS [-WA]
FS =0010 0000000000000000 00000fff 00a09300 DPL=0 DS [-WA]
GS =0010 0000000000000000 00000fff 00a09300 DPL=0 DS [-WA]
LDT=0000 0000000000000000 0000ffff 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT= 0000000000001000 00000fff
IDT= 000000000f585018 00000fff
CR0=80010033 CR2=0000000000000000 CR3=000000000fc01000 CR4=00000668
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=0000000000000030 CCD=000000000ff072c8 CCO=ADDQ
EFER=0000000000000d00
check_exception old: 0xd new 0xd
948: v=08 e=0000 i=0 cpl=0 IP=0038:0000000000000c56 pc=0000000000000c56 SP=0010:000000000ff072d0 env->regs[R_EAX]=0000000000000008
RAX=0000000000000008 RBX=000000000ea16f98 RCX=0000000000000030 RDX=0000000000000000
RSI=000000000ff07384 RDI=000000000f8eef98 RBP=000000000ff29d40 RSP=000000000ff072d0
R8 =00000000b29952e3 R9 =000000000f167fc7 R10=0000000000000034 R11=0000000000400000
R12=000000000ff073a8 R13=000000000ea16c40 R14=000000000ff073b0 R15=000000000ff073a0
RIP=0000000000000c56 RFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 0000000000000000 00000fff 00a09300 DPL=0 DS [-WA]
CS =0038 0000000000000000 ffffffff 00af9a00 DPL=0 CS64 [-R-]
SS =0010 0000000000000000 00000fff 00a09300 DPL=0 DS [-WA]
DS =0010 0000000000000000 00000fff 00a09300 DPL=0 DS [-WA]
FS =0010 0000000000000000 00000fff 00a09300 DPL=0 DS [-WA]
GS =0010 0000000000000000 00000fff 00a09300 DPL=0 DS [-WA]
LDT=0000 0000000000000000 0000ffff 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT= 0000000000001000 00000fff
IDT= 000000000f585018 00000fff
CR0=80010033 CR2=0000000000000000 CR3=000000000fc01000 CR4=00000668
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=0000000000000030 CCD=000000000ff072c8 CCO=ADDQ
EFER=0000000000000d00
check_exception old: 0x8 new 0xd
Re: Loading GDT causes restart loop
Posted: Sun Apr 14, 2024 1:17 pm
by nullplan
You are getting a general protection fault. You can try to look at what address 0xc56 is (i.e. what instruction it points at).
Re: Loading GDT causes restart loop
Posted: Sun Apr 14, 2024 4:02 pm
by MichaelPetch
nullplan is right to look at what instruction is at 0xc56. But there are some interesting tidbits and things I can't explain. Is the code you have in Github the same as what you are using to produce these logs?
There are hints that your GDT is being used and that load_gdt started executing but never finished correctly. For example:
Code: Select all
ES =0010 0000000000000000 00000fff 00a09300 DPL=0 DS [-WA]
CS =0038 0000000000000000 ffffffff 00af9a00 DPL=0 CS64 [-R-]
SS =0010 0000000000000000 00000fff 00a09300 DPL=0 DS [-WA]
DS =0010 0000000000000000 00000fff 00a09300 DPL=0 DS [-WA]
FS =0010 0000000000000000 00000fff 00a09300 DPL=0 DS [-WA]
GS =0010 0000000000000000 00000fff 00a09300 DPL=0 DS [-WA]
All the segment registers EXCEPT CS are set to 0x10. The limit is 0xfff which makes sense given the way you created your GDT entries. The fact you get a #GP and CS is not set suggests it must have failed on the 2 stack pushes or the retfq in load_gdt.
The other puzzling thing for me is 947:
Code: Select all
v=0d e=73b0 i=0 cpl=0 IP=0038:0000000000000c56 pc=0000000000000c56 SP=0010:000000000ff072d0 env->regs[R_EAX]=0000000000000008
e=73b0 is the error code for the #GP being raised. The bottom 3 bits all being 0 suggests it was GDT related and accessing GDT index 0x73b0>>3=0xe76. I have no idea where that came from. If it hit the retfq - the segment pushed was 0x08 (Code segment). You can still see 0x08 in the RAX register.
Re: Loading GDT causes restart loop
Posted: Sun Apr 14, 2024 4:07 pm
by MichaelPetch
I don't believe all the files necessary to build your code are in Github. If you provided everything it would help. Seems some gnuefi stuff is missing and Make.rules etc.
Re: Loading GDT causes restart loop
Posted: Sun Apr 14, 2024 4:13 pm
by MichaelPetch
Something nullplan mentioned earlier you may wish to fix. the GDT only needs 8 byte alignment and you are using 4096 (0x1000). You might want to just use this:
Code: Select all
typedef struct _gdt {
GDT_ENTRY null;
GDT_ENTRY kernel_code;
GDT_ENTRY kernel_data;
GDT_ENTRY user_null;
GDT_ENTRY user_code;
GDT_ENTRY user_data;
} GDT;
__attribute__((aligned(0x08)))
GDT default_gdt = {
{0, 0, 0, 0x00, 0x00, 0},
{0, 0, 0, 0x9a, 0xa0, 0},
{0, 0, 0, 0x92, 0xa0, 0},
{0, 0, 0, 0x00, 0x00, 0},
{0, 0, 0, 0x9a, 0xa0, 0},
{0, 0, 0, 0x92, 0xa0, 0},
};
Re: Loading GDT causes restart loop
Posted: Sun Apr 14, 2024 5:15 pm
by 0xY
Thanks for your suggestions guys, I will try to attach a debugger and inspect the instruction named in the logs. I will also add the remaining files to github shortly. I took nullplan's suggestion to change the GDT aiignment to 0x08, here are the resulting logs:
Code: Select all
check_exception old: 0xffffffff new 0x6
874: v=06 e=0000 i=0 cpl=0 IP=0008:0000000000000bf0 pc=0000000000000bf0 SP=0010:000000000ff072e0 env->regs[R_EAX]=0000000000000008
EAX=00000008 EBX=0ea16f98 ECX=00000030 EDX=00000000
ESI=0ff07384 EDI=0000ffff EBP=0ff07320 ESP=0ff072e0
EIP=00000bf0 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 00000000 00009300 DPL=0 DS [-WA]
CS =0008 00000000 00000000 00009a00 DPL=0 CS16 [-R-]
SS =0010 00000000 00000000 00009300 DPL=0 DS [-WA]
DS =0010 00000000 00000000 00009300 DPL=0 DS [-WA]
FS =0010 00000000 00000000 00009300 DPL=0 DS [-WA]
GS =0010 00000000 00000000 00009300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT= 0000000000001000 0000002f
IDT= 000000000f585018 00000fff
CR0=80010033 CR2=0000000000000000 CR3=000000000fc01000 CR4=00000668
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000040 CCD=0ff072e0 CCO=SUBQ
EFER=0000000000000d00
check_exception old: 0xffffffff new 0xd
875: v=0d e=0038 i=0 cpl=0 IP=0008:0000000000000bf0 pc=0000000000000bf0 SP=0010:000000000ff072e0 env->regs[R_EAX]=0000000000000008
EAX=00000008 EBX=0ea16f98 ECX=00000030 EDX=00000000
ESI=0ff07384 EDI=0000ffff EBP=0ff07320 ESP=0ff072e0
EIP=00000bf0 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 00000000 00009300 DPL=0 DS [-WA]
CS =0008 00000000 00000000 00009a00 DPL=0 CS16 [-R-]
SS =0010 00000000 00000000 00009300 DPL=0 DS [-WA]
DS =0010 00000000 00000000 00009300 DPL=0 DS [-WA]
FS =0010 00000000 00000000 00009300 DPL=0 DS [-WA]
GS =0010 00000000 00000000 00009300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT= 0000000000001000 0000002f
IDT= 000000000f585018 00000fff
CR0=80010033 CR2=0000000000000000 CR3=000000000fc01000 CR4=00000668
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000040 CCD=0ff072e0 CCO=SUBQ
EFER=0000000000000d00
check_exception old: 0xd new 0xd
876: v=08 e=0000 i=0 cpl=0 IP=0008:0000000000000bf0 pc=0000000000000bf0 SP=0010:000000000ff072e0 env->regs[R_EAX]=0000000000000008
EAX=00000008 EBX=0ea16f98 ECX=00000030 EDX=00000000
ESI=0ff07384 EDI=0000ffff EBP=0ff07320 ESP=0ff072e0
EIP=00000bf0 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 00000000 00009300 DPL=0 DS [-WA]
CS =0008 00000000 00000000 00009a00 DPL=0 CS16 [-R-]
SS =0010 00000000 00000000 00009300 DPL=0 DS [-WA]
DS =0010 00000000 00000000 00009300 DPL=0 DS [-WA]
FS =0010 00000000 00000000 00009300 DPL=0 DS [-WA]
GS =0010 00000000 00000000 00009300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT= 0000000000001000 0000002f
IDT= 000000000f585018 00000fff
CR0=80010033 CR2=0000000000000000 CR3=000000000fc01000 CR4=00000668
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000040 CCD=0ff072e0 CCO=SUBQ
EFER=0000000000000d00
check_exception old: 0x8 new 0xd
You set limit1_flags to 0xa0 for all the segments when it is usually should be 0x00 for 64-bit mode
I was not aware of this, all the resources I've seen so far (for example
https://blog.llandsmeer.com/tech/2019/0 ... rland.html) seem to use 0xa0