Page 1 of 1
Higher Half x86 Bare Bones issue calling kernel_main
Posted: Wed May 27, 2026 1:32 pm
by tedavids
I am trying to implement the tutorial at this page:
https://wiki.osdev.org/Higher_Half_x86_Bare_Bones
When I call kernel_main I get a reboot(?) the instruction pointer changes to 0xE05b. I have the full code here:
https://github.com/tedavids/DragonOS
here is a snippet of where the error is:
Code: Select all
_start2:
# At this point, paging is fully set up and enabled.
# Unmap the identity mapping as it is now unnecessary.
movl $0, page_directory + 0
# Reload crc3 to force a TLB flush so the changes to take effect.
movl %cr3, %ecx
movl %ecx, %cr3
# Set up the stack.
mov $stack_top, %esp
# Unmap the identity mapping as it is now unnecessary.
movl $0, page_directory + 0
# Reload crc3 to force a TLB flush so the changes to take effect.
movl %cr3, %ecx
movl %ecx, %cr3
# Enter the high-level kernel.
call kernel_main
using gdb, I can see my instruction pointer (%EIP) is using virtual addresses, its in the 0xC0000000 range. and I can single step through from _start2 to right before the call, and they addresses are higher half.
Things I have checked: CR3 has the proper address of my page directory
CR0 is set to paging on
EIP points at the proper address '0xC...' right before the call (this is how I know I am in virtual space).
The disassembly shows the call using the proper 0xC.. address
The page directory has line 768 (0xC000) pointing at my page table
The page table entry for the address of kernel_main is mapped
If I replace the call to kernel_main with "loop: jmp loop", it happily loops forever.
Any ideas would be helpful. Thanks in advance
Re: Higher Half x86 Bare Bones issue calling kernel_main
Posted: Wed May 27, 2026 7:30 pm
by Octocontrabass
tedavids wrote: ↑Wed May 27, 2026 1:32 pmWhen I call kernel_main I get a reboot(?) the instruction pointer changes to 0xE05b.
It's probably a triple fault, but that's not really enough information to find the problem. Use QEMU's interrupt log ("-d int") to see more information about the exceptions leading up to the triple fault. (You may also want to tell QEMU not to reboot so the log will end after the third exception.)
Re: Higher Half x86 Bare Bones issue calling kernel_main
Posted: Thu May 28, 2026 2:05 am
by sebihepp
Do you have an infinite loop at the end of your kernel_main()? If not and kernel_main() returns, it will run the code after call kernel_main - and that might be the problem, since the code you showed us just ends there, so it will execute whatever is in memory.
Re: Higher Half x86 Bare Bones issue calling kernel_main
Posted: Thu May 28, 2026 10:56 am
by tedavids
Octocontrabase: Thanks for the hint on QEMU. I can see I get a GP fault, followed by a page fault.
Here is the relevant (I think), part of the QEMU dump:
--start--
check_exception old: 0xd new 0xe
1: v=0e e=0000 i=0 cpl=0 IP=0008:c0101170 pc=c0101170 SP=0010:c0108000 CR2=00000068
EAX=00000010 EBX=00009500 ECX=0010a000 EDX=003fe003
--end--
I understand the cpl (ring 0), IP(instruction pointer), SP (Stack pointer), I'm not sure what 'v' is, but I'm guessing the fault number. 'e' I have no idea. 'pc', I'm not sure. From what I found on the internet, CR2 should give me the actual address of the fault, but 68 seems way wierd. I think my fault is in executing the jump (well call) to kernel_main, but my page_directory entry 768 (xC00)does point to my page_entry_C00, and my PTE does point to actual memory page 0x101000. So I'm at a loss.
sebihepp: Yes, My issue is I am not getting to kernel_main. That is where the issue is. I seem to be getting a GP fault, followed by a page fault. I set a break point in gdb to let me know I got there (never fired)
Tom
Re: Higher Half x86 Bare Bones issue calling kernel_main
Posted: Thu May 28, 2026 11:35 am
by nullplan
tedavids wrote: ↑Thu May 28, 2026 10:56 am
Here is the relevant (I think), part of the QEMU dump:
--start--
check_exception old: 0xd new 0xe
Well almost, that is the second exception. Numbers 13 and 14 are the exception vectors, which are GPF and PF respectively. So somehow you caused a general protection fault, and then handling that fault immediately (as in, before managing to invoke the handler) caused a page fault.
tedavids wrote: ↑Thu May 28, 2026 10:56 am
I understand the cpl (ring 0), IP(instruction pointer), SP (Stack pointer), I'm not sure what 'v' is, but I'm guessing the fault number. 'e' I have no idea. 'pc', I'm not sure. From what I found on the internet, CR2 should give me the actual address of the fault, but 68 seems way wierd. I think my fault is in executing the jump (well call) to kernel_main, but my page_directory entry 768 (xC00)does point to my page_entry_C00, and my PTE does point to actual memory page 0x101000. So I'm at a loss.
I think pc is the linear address the IP address leads to. As in, if you actually used segmented memory, then IP would be the segmented address and pc the linear address. Since you use a flat address space, both are equal.
v is the
vector of the exception, and e is the
error code. Meaning that the page fault handler is invoked with an error code of 0. That code means
- Caused by non-present page
- Caused by a read
- Caused in supervisor mode
- Not caused by using reserved bits
- Not caused by instruction fetch (or NX feature disabled)
- Not caused by protection keys
- Not caused by shadow stacks
So we see that it is caused by a data fetch for address 0x68 while trying to handle a general protection fault. There's not a whole lot this can be. Is it possible the GDT is not setup correctly? Or is it not mapped correctly after enabling paging? The IDT seems to be working, else you would get a double fault.
Re: Higher Half x86 Bare Bones issue calling kernel_main
Posted: Thu May 28, 2026 3:23 pm
by tedavids
nullplan; I changed the load of the page_entry_C00 so it loads the first 1023 entries, and now it works. So apparently The higher half tutorial is not extending to the actual end of the kernel, I'll try to see why that is.
I do have an easy question for you guys. I presume I need to change my GDT from a base of 0x0, to a base of 0xC0100000?
Thank you for all the help!
TD
Re: Higher Half x86 Bare Bones issue calling kernel_main
Posted: Thu May 28, 2026 8:51 pm
by nullplan
tedavids wrote: ↑Thu May 28, 2026 3:23 pm
I do have an easy question for you guys. I presume I need to change my GDT from a base of 0x0, to a base of 0xC0100000?
Yes. Once paging is active, the IDT base and GDT base are interpreted as virtual addresses, so if you loaded them before enabling paging, you must load them again (with virtual addresses this time) after enabling paging, else the CPU will look for them in the wrong place and you will get page faults like the one above. Because of this, I personally don't even bother with GDT and IDT until paging is working. But then I have a 64-bit OS and need paging set up to get to the correct environment, anyway.
Actually, I also have to retract my earlier statement. The address that was faulting was 0x68 which happens to be 0xd<<3. Which might be the IDT entry for the GPF being looked up, if the IDT base is also 0.
Re: Higher Half x86 Bare Bones issue calling kernel_main
Posted: Fri May 29, 2026 10:12 am
by Octocontrabass
tedavids wrote: ↑Thu May 28, 2026 3:23 pmI changed the load of the page_entry_C00 so it loads the first 1023 entries, and now it works. So apparently The higher half tutorial is not extending to the actual end of the kernel, I'll try to see why that is.
I would expect incomplete mapping to cause #PF, not #GP. There may be another bug you haven't found yet.
tedavids wrote: ↑Thu May 28, 2026 3:23 pmI presume I need to change my GDT from a base of 0x0, to a base of 0xC0100000?
Do you mean the GDT itself, or the segment descriptors inside the GDT? For the GDT itself, yes, paging applies to the address in GDTR, so you'll need to load a new address. For the segment descriptors, no, those need a base of 0 to disable segment-based address translation.
Re: Higher Half x86 Bare Bones issue calling kernel_main
Posted: Fri May 29, 2026 5:35 pm
by tedavids
Do you mean the GDT itself, or the segment descriptors inside the GDT? For the GDT itself, yes, paging applies to the address in GDTR, so you'll need to load a new address. For the segment descriptors, no, those need a base of 0 to disable segment-based address translation.
octocontrabass: I meant the entries. But never thought of the address in the GDTR. Thank you!