Setting up x86-64 Higher Half Kernel

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
DevNoteHQ
Member
Member
Posts: 50
Joined: Mon May 15, 2017 11:04 am

Setting up x86-64 Higher Half Kernel

Post by DevNoteHQ »

Hi!

I am currently trying to put my kernel into virtual higher half. When i am trying to jump from the lower half into the higher half, i get a tripple fault exactly after the jump.
The base address for the kernel should be 0xFFFF FF00 0000 0000 (=HVMA). The PML4 is located at 0x7E0 0000 (+ HVMA).
Because i don't want to paste all of the connected code pieces, it is online at https://github.com/MathiLpHD/MOS/ in the folder system/kernel/
The main files are makefile, linker.ld and src/init/boot.asm

Also, what does -mcmodel=kernel do? And how can i change the datamodel to allow 64-Bit addresses? Or would it be better to move ctors and so on into the text section?

The main Codepieces in boot.asm are:

Code: Select all

mov edi, 0x7E00000		; Set the destination index to 0x7E00000.
    mov cr3, edi			; Set control register 3 to the destination index.
    xor eax, eax			; Nullify the A-register.
    mov ecx, 4096			; Set the C-register to 4096.
    rep stosd				; Clear the memory.
    mov edi, cr3			; Set the destination index to control register 3.

	mov DWORD [edi], 0x7E01003		; Set the uint32_t at the destination index to 0x7E01003.

	mov ebx, 0x00000083				; Set the B-register to 0x000000083. 0x83 = PS = 1 (=> 1GiB Page) R/W = 1, P = 1
	add edi, 0x1000					; Add 0x1000 to the destination index.
	mov DWORD [edi], ebx			; Set the uint32_t at the destination index to the B-register.

	mov edi, 0x7FFE000				; Set the destination index to 0x1000.
	mov DWORD [edi], 0x7E01003		; Set the uint32_t at the destination index to 0x7E01003. This is for HVMA
    add edi, 0x1000					; Add 0x1000 to the destination index.
	mov DWORD [edi], 0x7E00003		; Set the uint32_t at the destination index to 0x7E00003. This is for recrusive mapping
and:

Code: Select all

lgdt [GDT64.Pointer]			; Load the 64-bit global descriptor table.
	mov ax, 0x10
	mov ss, ax
	mov ax, 0x0
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax

	jmp GDT64.Code:.trampoline      ; Set the code segment and enter 64-bit long mode.

.NoLongMode:
	cli
	hlt

[BITS 64]
.trampoline:
	; enter the higher half
	mov rax, QWORD .Realm64
	jmp rax

[section .inith]
.Realm64:
	cli		; Clear the interrupt flag.

	mov rax, [GDT64.Pointer + 2]
	mov rbx, HVMA
	add rax, rbx
	mov [GDT64.Pointer  + 2], rax
	mov rax, GDT64.Pointer + HVMA
	lgdt [rax]
Image
User avatar
iansjack
Member
Member
Posts: 4706
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Setting up x86-64 Higher Half Kernel

Post by iansjack »

The PML4 is located at 0x7E0 0000 (+ HVMA).
PML4 is located at a physical address. Do you really have that much RAM?
DevNoteHQ
Member
Member
Posts: 50
Joined: Mon May 15, 2017 11:04 am

Re: Setting up x86-64 Higher Half Kernel

Post by DevNoteHQ »

iansjack wrote:
The PML4 is located at 0x7E0 0000 (+ HVMA).
PML4 is located at a physical address. Do you really have that much RAM?
Well that (+ HVMA) is just if you want to access it from HVMA. Then it would be 0xFFFFFF0007E00000.

EDIT: I am not sure if i found the source of my problem:
My current paging structure:
PL4R: Points to 0x7E0 0000
PL4H: Points to PL3 (=0x7E0 1000)
...
PL3 + 3 * 4096: Points to 3 * 1GB
PL3 + 2 * 4096: Points to 2 * 1GB
PL3 + 1 * 4096: Points to 1 * 1GB
PL3 + 0 * 4096: Points to 0 * 1GB = 0x0000
PL4L: Points to PL3 (=0x7E0 1000)

When i try to access 0xFFFFFF0007E00000, which should be PL4L's HVMA, i get a Page Fault. So it seems that i can't make a paging structure like that, or did i make a different mistake?
User avatar
iansjack
Member
Member
Posts: 4706
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Setting up x86-64 Higher Half Kernel

Post by iansjack »

I've looked at your code and I can't see you creating any entries to map pages. Perhaps I am misunderstanding it.

What do your page tables look like after you have created them? Are they reasonable and what you expect?
DevNoteHQ
Member
Member
Posts: 50
Joined: Mon May 15, 2017 11:04 am

Re: Setting up x86-64 Higher Half Kernel

Post by DevNoteHQ »

iansjack wrote:I've looked at your code and I can't see you creating any entries to map pages. Perhaps I am misunderstanding it.

What do your page tables look like after you have created them? Are they reasonable and what you expect?

Code: Select all

mov edi, 0x7E00000      ; Set the destination index to 0x7E00000.
    mov cr3, edi         ; Set control register 3 to the destination index.
    xor eax, eax         ; Nullify the A-register.
    mov ecx, 4096         ; Set the C-register to 4096.
    rep stosd            ; Clear the memory.
    mov edi, cr3         ; Set the destination index to control register 3.

   mov DWORD [edi], 0x7E01003      ; Set the uint32_t at the destination index to 0x7E01003.

   mov ebx, 0x00000083            ; Set the B-register to 0x000000083. 0x83 = PS = 1 (=> 1GiB Page) R/W = 1, P = 1
   add edi, 0x1000               ; Add 0x1000 to the destination index.
   mov DWORD [edi], ebx         ; Set the uint32_t at the destination index to the B-register.

   mov edi, 0x7FFE000            ; Set the destination index to 0x1000.
   mov DWORD [edi], 0x7E01003      ; Set the uint32_t at the destination index to 0x7E01003. This is for HVMA
    add edi, 0x1000               ; Add 0x1000 to the destination index.
   mov DWORD [edi], 0x7E00003      ; Set the uint32_t at the destination index to 0x7E00003. This is for recrusive mapping
That creates the paging structure. CR3 is set to 0x7E0 0000, which is PML4T[0] and it points to 0x7E0 1000 which is PDPT[0], which then points to 0x0 as a 1GB page.
Then there is 0x7FF E000 which is PML4T[510] and also points to 0x7E 1003. The last one 0x7FF F000 is PML4T[511] and points to 0x7E0 0000 (=PML4T[0]) for recrusive mapping.
User avatar
iansjack
Member
Member
Posts: 4706
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Setting up x86-64 Higher Half Kernel

Post by iansjack »

That code seems to be setting 32-bit values only. Are you sure that the upper 32-bits of each entry are correctly set?

As before, I would recommend that you inspect the tables I'm memory to see that they correspond to what you think they should be.
DevNoteHQ
Member
Member
Posts: 50
Joined: Mon May 15, 2017 11:04 am

Re: Setting up x86-64 Higher Half Kernel

Post by DevNoteHQ »

iansjack wrote:That code seems to be setting 32-bit values only. Are you sure that the upper 32-bits of each entry are correctly set?

As before, I would recommend that you inspect the tables I'm memory to see that they correspond to what you think they should be.
Well how do i do that? Is there an argument i have to give to QEMU so it saves a image of it's memory?
User avatar
iansjack
Member
Member
Posts: 4706
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Setting up x86-64 Higher Half Kernel

Post by iansjack »

You can use qemu's built-in monitor (see the documentation) or gdb in association with qemu. You'll need to put a "jmp ." instruction somewhere to stop things before the triple fault.

I do see where you are apparently clearing the memory used by the page tables but, unless I missed it, I don't see you setting the DF flag before the repeat string instructions. It's possible that you are clearing memory downwards rather than upwards.
DevNoteHQ
Member
Member
Posts: 50
Joined: Mon May 15, 2017 11:04 am

Re: Setting up x86-64 Higher Half Kernel

Post by DevNoteHQ »

iansjack wrote:You can use qemu's built-in monitor (see the documentation) or gdb in association with qemu. You'll need to put a "jmp ." instruction somewhere to stop things before the triple fault.

I do see where you are apparently clearing the memory used by the page tables but, unless I missed it, I don't see you setting the DF flag before the repeat string instructions. It's possible that you are clearing memory downwards rather than upwards.
The addresses all seem to be right:

Image

EDIT: Do i have to set the 'A' (=Bit 5) Bit?
EDIT2: QEMU is also unable to access the HVMA. x/1xg 0xFFFFFF0007E00000 returns Cannot access memory
User avatar
iansjack
Member
Member
Posts: 4706
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Setting up x86-64 Higher Half Kernel

Post by iansjack »

Info mem doesn't show any mapping for your higher-half addresses. That would explain the triple-faulting.
DevNoteHQ
Member
Member
Posts: 50
Joined: Mon May 15, 2017 11:04 am

Re: Setting up x86-64 Higher Half Kernel

Post by DevNoteHQ »

iansjack wrote:Info mem doesn't show any mapping for your higher-half addresses. That would explain the triple-faulting.
Do i have to do something special to activate the higher mappings? Do i have to set the other entries between PML4T[0] and PML4T[510]? Because the MMU maybe tries to increment through the PML4T entries?

EDIT: Wait, does each entry in a table (from 0 - 511) has to be 4k aligned? Or does only the first entry have to be 4k aligned?
EDIT2: I don't know why i thought that but only the first entry has to be 4k aligned.
Now i changed the line:

Code: Select all

mov edi, 0x7FFE000
to:

Code: Select all

mov edi, 0x7E00FF0
Here the info mem:
Image

Now it works^^
Thanks for your help!
LtG
Member
Member
Posts: 384
Joined: Thu Aug 13, 2015 4:57 pm

Re: Setting up x86-64 Higher Half Kernel

Post by LtG »

MathiLpHD wrote: EDIT: Wait, does each entry in a table (from 0 - 511) has to be 4k aligned? Or does only the first entry have to be 4k aligned?
Just wanted to clarify, _none_ of the entries have to be 4KiB aligned, however _all_ the tables have to be 4KiB aligned. Of course as a consequence of the table being 4KiB aligned the first entry ends up 4KiB aligned as well, but that's just a consequence, it's the table alignment that's relevant here.

Note also that all the entries will need the physical address they refer to be aligned as well, that alignment has to be what ever the "natural" alignment for the entry is, so for 4KiB pages that's 4KiB, for 2MiB pages it's 2MiB, etc.
Post Reply