Page 1 of 1

CR3 changes to 0 on jump

Posted: Fri Aug 31, 2018 11:22 am
by Ender
I've been trying to switch to a higher half kernel but can't seem to figure out paging. I've been reading the wiki page on it as well as the Intel manual.
Can someone help me figure out how to do it correctly?

I've came up with the following algorithm for setting up the structures:

Code: Select all

;[pd + VIRT_BASE >> 22] = pt | flags
;[pd] = pt_low | flags

;while i < ro_end
;	[pt_low + i >> 12 & 0x03FF] = i | flags
;	i += 0x1000
;end
;while i < rw_end
;	[pt_low + i >> 12 & 0x03FF] = i | flags
;	i += 0x1000
;end
;i = 0
;while i < ro_end
;	[pt_high + (VIRT_BASE + i) >> 12 & 0x03FF] = i | flags
;	i += 0x1000
;end
;while i < rw_end
;	[pt_high + (VIRT_BASE + i) >> 12 & 0x03FF] = i | flags
;	i += 0x1000
;end
Although it doesn't seem to have been working (or I implemented it incorrectly)

My code is here: https://github.com/the-sushi/os
And the relevant parts are here: https://github.com/the-sushi/os/blob/ma ... 6/boot.asm

I'm not exactly aiming for the most efficient code here yet, I'll try cleaning it up and making it into routines after I can get it working at all.

Can someone please help get me onto the right path? Also, what is the PAT bit of a page directory table entry supposed to be? Intel's manual was a bit confusing in this regard.

Thanks,
~Ender

Re: Need help with higher half / paging

Posted: Fri Aug 31, 2018 5:21 pm
by Ender
Tried again, with an edited algorithm.
This time the relevant code is small enough that I don't feel too bad putting it into a single post:

Code: Select all

		lea eax, [page_table - VIRT_BASE]
	mov ebx, PT_PRESENT

.fill_ro:
	mov [eax], ebx ;insert entry
	add eax, 4 ;next entry
	add ebx, PAGE_SIZE ;next mem address
	cmp ebx, (ro_end - VIRT_BASE)
	jl .fill_ro

	or ebx, PT_READWRITE
.fill_rw:
	mov [eax], ebx
	add eax, 4
	add ebx, PAGE_SIZE
	cmp ebx, (rw_end - VIRT_BASE)
	jl .fill_rw

;done with page table
	lea ebx, [page_table - VIRT_BASE]
	or ebx, PD_PRESENT | PD_READWRITE

	lea eax, [page_dir - VIRT_BASE] ;load page dir
	mov dword [eax], ebx
	add eax, PD_INDEX
	mov dword [eax], ebx

	xchg bx, bx ;bochs breakpoint
	lea eax, [page_dir - VIRT_BASE]
	mov cr3, eax

	mov eax, cr4
	or eax, 0x00000010 ;enable PSE (4MiB pages)
	mov cr4, eax

	mov eax, cr0
	or eax, 0x80010001 ;enable paging, WP, PE
	mov cr0, eax

	lea eax, [higher_half_start]
	jmp eax
I still can't figure out what exactly I'm doing wrong though

Relevant bochs stuff:

Code: Select all

CPU 0: Exception 0x0e - (#PF) page fault occured (error_code=0x0000)
CPU 0: Interrupt 0x0e occured (error_code=0x0000)
CPU 0: Exception 0x0d - (#GP) general protection fault occured (error_code=0x0073)
CPU 0: Exception 0x08 - (#DF) double fault occured (error_code=0x0000)
CPU 0: Interrupt 0x08 occured (error_code=0x0000)
CPU 0: Exception 0x0d - (#GP) general protection fault occured (error_code=0x0043)
This occurs after the "mov cr0, eax" (obviously).

Re: Need help with higher half / paging

Posted: Sat Sep 01, 2018 1:13 am
by bzt9999
Paging has several levels, you have to fill up the whole table. Only the last level points to physical memory for data, upper levels point to other page tables, and CR3 points to the root of the tree.

In bochs debug console use "page X" to travese the tree for virtual address X. It will show you where the error is. If everything is right, you should see 4 levels and a message "Address X translates to physical page Y".

This is how I do it:

Code: Select all

            ; -------- paging ---------
            ;map core at higher half of memory
            ;to address 0xffffffffffe00000
            xor         eax, eax
            mov         edi, 0A000h
            mov         ecx, (15000h-0A000h)/4
            repnz       stosd

            ;PML4
            mov         edi, 0A000h
            ;pointer to 2M PDPE (first 4G RAM identity mapped)
            mov         dword [edi], 0E001h
            ;pointer to 4k PDPE (core mapped at -2M)
            mov         dword [edi+4096-8], 0B001h

            ;4K PDPE
            mov         edi, 0B000h
            mov         dword [edi+4096-8], 0C001h
            ;4K PDE
            mov         edi, 0C000h+3840
            mov         eax, dword[fb_ptr] ;map framebuffer
            mov         al,81h
            mov         ecx, 31
@@:         stosd
            add         edi, 4
            add         eax, 2*1024*1024
            dec         ecx
            jnz         @b
            mov         dword [0C000h+4096-8], 0D001h

            ;4K PT
            mov         edi, 0D000h
            mov         eax, dword[core_ptr]    ;map core text segment
            inc         eax
            mov         ecx, dword[core_len]
            shr         ecx, 12
            inc         ecx
@@:         stosd
            add         edi, 4
            add         eax, 4096
            dec         ecx
            jnz         @b
            mov         dword[0DFF8h], 014001h  ;map core stack
I place the paging tables at 0xA000, starting with the top level PML4 table, followed by two PDPE tables (one for lower mapping, one for higher mapping), followed by several PDE and PTE tables. I also map the framebuffer in higher half, so that the kernel can use it regardless to lfb's physical address. And finally I map the kernel stack in higher half too, because the C kernel going to need it.

Re: Need help with higher half / paging

Posted: Sat Sep 01, 2018 6:24 am
by Klakap
You can try tutorial http://www.osdever.net/tutorials/view/i ... sic-paging. It seems working.

Re: Need help with higher half / paging

Posted: Sat Sep 01, 2018 12:11 pm
by Ender
bzt9999 wrote: I place the paging tables at 0xA000, starting with the top level PML4 table, followed by two PDPE tables (one for lower mapping, one for higher mapping), followed by several PDE and PTE tables. I also map the framebuffer in higher half, so that the kernel can use it regardless to lfb's physical address. And finally I map the kernel stack in higher half too, because the C kernel going to need it.
Must I use 4 level paging? 32 bit paging seems to have worked fine for others.
Intel's manual says "If CR0.PG = 1 and CR4.PAE = 0, 32-bit paging is used", however "If CR0.PG = 1, CR4.PAE = 1, and IA32_EFER.LME = 1, 4-level paging is used".
And seemingly, 32 bit paging only uses the page directory and page tables, without PML4 and PDPE tables preceding them.
I'll try 4 level later (perhaps later today?), but I don't believe that this is the issue.
Klakap wrote:You can try tutorial http://www.osdever.net/tutorials/view/i ... sic-paging. It seems working.
That tutorial is different from my setup. He simply maps the first 4MB, wheras I'm attempting to map .text and .rodata separately from .data and .bss, as I want to keep .rodata and .text non-writable.

Re: Need help with higher half / paging

Posted: Sun Sep 02, 2018 5:54 am
by bzt9999
Ender wrote:Must I use 4 level paging? 32 bit paging seems to have worked fine for others.
Nope, if you limit yourself to 4G RAM, then you won't need all 4 levels, only 2. I forgot to mention I set up paging tables for long mode (56 bits).

Regardless, I suggest to use "page X" in bochs right after you have loaded cr3, to figure out what's wrong. Or even better, start your page fault handler with "xchg bx,bx", so that you can use "page cr2" in bochs.

Re: Need help with higher half / paging

Posted: Sun Sep 02, 2018 10:46 pm
by Ender
bzt9999 wrote:
Ender wrote:Must I use 4 level paging? 32 bit paging seems to have worked fine for others.
Nope, if you limit yourself to 4G RAM, then you won't need all 4 levels, only 2. I forgot to mention I set up paging tables for long mode (56 bits).

Regardless, I suggest to use "page X" in bochs right after you have loaded cr3, to figure out what's wrong. Or even better, start your page fault handler with "xchg bx,bx", so that you can use "page cr2" in bochs.
Ah.
I quickly tried a couple 'page X's to see if it gave me any obvious errors or anything, but no-go

Code: Select all

<bochs:13> page 0
linear page 0x0000000000000000 maps to physical page 0x000000000000
<bochs:14> page 55555555555555555555555555
linear page 0xfffffffffffff000 maps to physical page 0x0000fffff000
I'll play around with it some more later and see if I can figure something else.

Re: Need help with higher half / paging

Posted: Mon Sep 03, 2018 11:19 pm
by Ender
I've been trying, still can't get it. I might just try and use PAE or 4-level paging, but I highly doubt that one of those will work if this doesn't.

My current code is as such:

Code: Select all

	lea eax, [page_table - VIRT_BASE]
	mov ebx, PT_PRESENT

	mov ecx, (ro_end - VIRT_BASE)
	shr ecx, 12

.fill_ro:
	mov dword [eax], ebx ;insert entry
	add eax, 4 ;next entry
	add ebx, PAGE_SIZE ;next mem address
	loop .fill_ro

	or ebx, PT_READWRITE
	mov ecx, (rw_end - VIRT_BASE)
	sub ecx, (ro_end - VIRT_BASE)
	shr ecx, 12
.fill_rw:
	mov dword [eax], ebx
	add eax, 4
	add ebx, PAGE_SIZE
	loop .fill_rw

;done with page table
	lea ebx, [page_table - VIRT_BASE] ;load page table
	or ebx, PD_PRESENT | PD_READWRITE ;set flags

	lea eax, [page_dir - VIRT_BASE] ;load page dir
	mov dword [eax], ebx ;move page table to 0 in page dir
	add eax, PD_INDEX ;move to higher half entry
	mov dword [eax], ebx ;move page table to higher half entry

	lea eax, [page_dir - VIRT_BASE]
	mov cr3, eax

	mov eax, cr4
	or eax, 0x00000010 ;enable PSE (4MiB pages)
	mov cr4, eax

	mov eax, cr0
	or eax, 0x80010001 ;enable paging, WP, PE
	mov cr0, eax

	lea eax, [higher_half_start]
	jmp eax
Not much different from before, just small changes.

The only thing specific that I can think of is if I need to set PTE.PWT, PTE.PCD, or PTE.PAT, but I'm not sure, and I also doubt it. I've been looking through others' code, but can't seem to find any that are higher half, 32-bit, and separate RO and RW data. Could someone at least point me to that if they happen to know of a kernel that fits those requirements?

Re: Need help with higher half / paging

Posted: Wed Sep 05, 2018 8:27 pm
by Ender
Bochs:

Code: Select all

CR0=0x60000010: pg CD NW ac wp ne ET ts em mp pe
CR2=page fault laddr=0x0000000000000000
CR3=0x000000000000
    PCD=page-level cache disable=0
    PWT=page-level write-through=0
CR4=0x00000000: pke smap smep osxsave pcid fsgsbase smx vmx osxmmexcpt umip osfxsr pce pge mce pae pse de tsd pvi vme
CR8: 0x0
EFER=0x00000000: ffxsr nxe lma lme sce
I doubt that CR3 should be 0, so there's my problem. Now I need to figure out where I went wrong though... #-o
Not sure why I didn't think to check this earlier...

Changed title appropriately, I'm stumped

Turns out that CR3 changes to 0 after the JMP to higher_half_start (according to bochs at least), but I'm not sure of the reason for this


EDIT: Gave up, I'm wasting too much time on this. I'll just map the bottom 4MiB as Ring-0 RW.

Re: Need help: CR3 changes to 0 on jump

Posted: Sat Sep 08, 2018 1:30 am
by Octocontrabass
Ender wrote:I doubt that CR3 should be 0, so there's my problem. Now I need to figure out where I went wrong though... #-o
A triple fault will (usually) cause the CPU to reset. Many registers are cleared by a CPU reset, including CR3.
Ender wrote:

Code: Select all

	mov eax, cr0
	or eax, 0x80010001 ;enable paging, WP, PE
	mov cr0, eax

	lea eax, [higher_half_start]
	jmp eax
The instruction immediately following a MOV to CR0 that enables paging must be JMP. If you're linking your code properly, you should have no trouble doing this:

Code: Select all

	mov cr0, eax
	jmp higher_half_start
Additionally, the page containing this code must be identity mapped, but it sounds like you've got that working already.
Ender wrote:Turns out that CR3 changes to 0 after the JMP to higher_half_start (according to bochs at least), but I'm not sure of the reason for this
What address are you jumping to? Is that address mapped properly? Does it contain your code like it's supposed to? What is the first fault that occurs (before the third fault and CPU reset)?


PAT is an extension upon the older PCD and PWT bits. On CPUs that support it, those three bits are actually used as an index into an 8-entry Page Attribute Table (thus the name). By default, the table is filled with entries to make it appear like the PCD and PWT bits behave the same way they did on really old CPUs, but you can update the table to contain any of the cache types available on x86, in any order. It's almost like having page-granular MTRRs, although you must still pay attention to MTRRs when you're messing with page cache controls.

Re: Need help: CR3 changes to 0 on jump

Posted: Sat Sep 08, 2018 6:14 pm
by Ender
Octocontrabass wrote:The instruction immediately following a MOV to CR0 that enables paging must be JMP. If you're linking your code properly, you should have no trouble doing this
I had it load it into a register to insure it is an absolute jmp, and I've seen other projects do the same, and it works fine when mapping the entire bottom 4MiB instead of in 4KiB increments.
Octocontrabass wrote: What address are you jumping to? Is that address mapped properly? Does it contain your code like it's supposed to? What is the first fault that occurs (before the third fault and CPU reset)?
Page fault, if I remember correctly. higher_half_start was right after the jmp physically, although I was jumping to the virtual address.
Octocontrabass wrote: PAT is an extension upon the older PCD and PWT bits. On CPUs that support it, those three bits are actually used as an index into an 8-entry Page Attribute Table (thus the name). By default, the table is filled with entries to make it appear like the PCD and PWT bits behave the same way they did on really old CPUs, but you can update the table to contain any of the cache types available on x86, in any order. It's almost like having page-granular MTRRs, although you must still pay attention to MTRRs when you're messing with page cache controls.
I see. What should I set the PAT bit to in PTEs?

Re: Need help: CR3 changes to 0 on jump

Posted: Tue Sep 11, 2018 2:15 am
by Octocontrabass
Ender wrote:it works fine when mapping the entire bottom 4MiB instead of in 4KiB increments.
Sounds to me like your code to build page tables in 4KiB increments was not working correctly.
Ender wrote:Page fault, if I remember correctly. higher_half_start was right after the jmp physically, although I was jumping to the virtual address.
After the three faults and reboot, Bochs will log a bunch of information about the CPU state. The address that caused the page fault will be in CR2, and the rest of the CPU state may be helpful as well.
Ender wrote:What should I set the PAT bit to in PTEs?
The PAT, PCD, and PWT bits should all be zero unless you know for sure that you need to change the cache setting for a particular page.

Re: Need help: CR3 changes to 0 on jump

Posted: Wed Sep 12, 2018 8:09 pm
by Ender
Octocontrabass wrote:
Ender wrote:it works fine when mapping the entire bottom 4MiB instead of in 4KiB increments.
Sounds to me like your code to build page tables in 4KiB increments was not working correctly.
That's what I thought as well, but I couldn't figure out where I went wrong.
Octocontrabass wrote:
Ender wrote:Page fault, if I remember correctly. higher_half_start was right after the jmp physically, although I was jumping to the virtual address.
After the three faults and reboot, Bochs will log a bunch of information about the CPU state. The address that caused the page fault will be in CR2, and the rest of the CPU state may be helpful as well.
As posted earlier, bochs said:

Code: Select all

CR2=page fault laddr=0x0000000000000000
Octocontrabass wrote:
Ender wrote:What should I set the PAT bit to in PTEs?
The PAT, PCD, and PWT bits should all be zero unless you know for sure that you need to change the cache setting for a particular page.
Ah, thank you.