Higher Half x86 Bare Bones issue calling kernel_main

Programming, for all ages and all languages.
Post Reply
tedavids
Posts: 11
Joined: Sun Apr 26, 2026 6:59 am

Higher Half x86 Bare Bones issue calling kernel_main

Post 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
Octocontrabass
Member
Member
Posts: 6245
Joined: Mon Mar 25, 2013 7:01 pm

Re: Higher Half x86 Bare Bones issue calling kernel_main

Post 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.)
sebihepp
Member
Member
Posts: 256
Joined: Tue Aug 26, 2008 11:24 am
GitHub: https://github.com/sebihepp

Re: Higher Half x86 Bare Bones issue calling kernel_main

Post 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.
tedavids
Posts: 11
Joined: Sun Apr 26, 2026 6:59 am

Re: Higher Half x86 Bare Bones issue calling kernel_main

Post 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
nullplan
Member
Member
Posts: 2033
Joined: Wed Aug 30, 2017 8:24 am

Re: Higher Half x86 Bare Bones issue calling kernel_main

Post 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.
Carpe diem!
tedavids
Posts: 11
Joined: Sun Apr 26, 2026 6:59 am

Re: Higher Half x86 Bare Bones issue calling kernel_main

Post 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
nullplan
Member
Member
Posts: 2033
Joined: Wed Aug 30, 2017 8:24 am

Re: Higher Half x86 Bare Bones issue calling kernel_main

Post 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.
Carpe diem!
Octocontrabass
Member
Member
Posts: 6245
Joined: Mon Mar 25, 2013 7:01 pm

Re: Higher Half x86 Bare Bones issue calling kernel_main

Post 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.
tedavids
Posts: 11
Joined: Sun Apr 26, 2026 6:59 am

Re: Higher Half x86 Bare Bones issue calling kernel_main

Post 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!
Post Reply