Page 4 of 4

Re: Higher Half in C?

Posted: Mon Jul 16, 2018 1:01 am
by simeonz
Octacone wrote:
simeonz wrote:
Octacone wrote:Success!!!
I managed to enable PSE 4 MB paging in assembly and identity map 1 MB to 1 MB and 3 GB to 1 MB. Everything seems to be working fine. My kernel is officially in higher half.
Now I'm having a problem because it crashes when I try to re-enable paging, more specially turning off PSE and turning on PAE and resetting CR3. How to solve that?
When you turn off PSE, the current code page (being large 4MB page) will be immediately discarded from the TLB, then resolved again, but since PSE is off, the PS bit will be ignored, and the PDE will be interpreted as a pointer to a page table. This is sufficient for a crash. If you turn on the PAE, the paging structures immediately switch to 3-level with PDPTEs at the top. I don't think that there is a way to do that safely without disabling paging first, in which case you are back to square one. I would advise you to keep PSE. Or don't map the kernel with large pages. If you want PAE, you better start with PAE on. There will be one more level of indirection, and you will have to set the first directory entry of the first and fourth page directories.
I definitely don't want to keep PSE. This never gets easy, doesn't it?
This is going to get really messy and complicated. Making PAE work in assembly, great, just great. Shouldn't take more than like 5 weeks, but OK.
To think of it, there might be a contrived way of enabling PAE on the fly. You could rewrite the first 8 PDEs into PDPTEs, make them point to page directories, etc, and then enable PAE. Assuming that the IDT, GDT and TSS are pointed at higher half memory at this point, you shouldn't fault yet. Turning on PAE should serialize the execution, flush the pipeline and discard the TLB entries, and hopefully just reload the paging structures. Of course, starting with PAE will be more conventional, but it is your decision.

Re: Higher Half in C?

Posted: Mon Jul 16, 2018 2:50 pm
by Octacone
simeonz wrote:To think of it, there might be a contrived way of enabling PAE on the fly. You could rewrite the first 8 PDEs into PDPTEs, make them point to page directories, etc, and then enable PAE. Assuming that the IDT, GDT and TSS are pointed at higher half memory at this point, you shouldn't fault yet. Turning on PAE should serialize the execution, flush the pipeline and discard the TLB entries, and hopefully just reload the paging structures. Of course, starting with PAE will be more conventional, but it is your decision.
I'll go for PAE from the beginning.
Uhmm, how exactly am I supposed to access 64-bit bitfields in assembly while in 32-bit mode? Never had to do something like this.
Like how to move data from one register to some_data + 40 bits? That is why is love higher languages so much and that is why programming abstractions in assembly is hard.

Re: Higher Half in C?

Posted: Mon Jul 16, 2018 5:35 pm
by kzinti
Initializing PAE is not much more complicated than non-PAE. Here is what I do:

Code: Select all

    ; Initialize the PDPT
    mov edi, _BootPDPT - KERNEL_VIRTUAL_BASE
    mov eax, (_BootPageDirectory - KERNEL_VIRTUAL_BASE) + PAGE_PRESENT
    mov [edi], eax                                      ; Identity map the first 4 MB
    mov [edi + 3 * 8], eax                              ; Map the same 4 MB to high memory

    ; Initialize the page directory (2 MB per entry)
    mov edi, _BootPageDirectory - KERNEL_VIRTUAL_BASE
    mov eax, (_BootPageTables - KERNEL_VIRTUAL_BASE) + PAGE_WRITE + PAGE_PRESENT
    mov [edi], eax                                      ; First page table (First 2 MB)
    add eax, 0x1000
    mov [edi + 8], eax                                  ; Second page table (Next 2 MB)

    ; Initialize the page tables
    mov edi, _BootPageTables - KERNEL_VIRTUAL_BASE
    mov eax, 0x00000000 | PAGE_WRITE | PAGE_PRESENT     ; Start at physical address 0
    mov ecx, 1024                                       ; 1024 entries
.fill_page_table:
    mov [edi], eax
    add eax, 0x1000
    add edi, 8
    loop .fill_page_table
You can see the whole implementation here:
https://github.com/kiznit/kiznix/blob/f ... ot_pae.asm

Hope this helps...

Re: Higher Half in C?

Posted: Wed Jul 18, 2018 1:46 pm
by Octacone
kzinti wrote:Initializing PAE is not much more complicated than non-PAE. Here is what I do:

Code: Select all

---snip---
You can see the whole implementation here:
https://github.com/kiznit/kiznix/blob/f ... ot_pae.asm

Hope this helps...
Easy for you to say, my pages are 4 KB not 2 MB. :) Thanks anyways!



Btw, I found a solution to my problem, that doesn't work, yet.
What I did is basically inspected the paging structures in memory (C++ generated) and then I figured out where to put stuff at and what bytes are flags and what bytes are address bytes. That made my job a lot easier.
After all the pain I created 6 page tables filled them accordingly did all the math, interconnected everything and yet Bochs is not happy:

Code: Select all

SetCR0(): PDPTR check failed !
Emulation stops at mov cr0, eax and I can't inspect my page tables (via Bochs GUI debugger) and no "physical address not available" errors were thrown. Any ideas?
Anyways here are chunks of my code:

Code: Select all

//Do so for all 4, make sure that page directory pointer table has 4 page directories
mov eax, (first_page_directory - 0xC0000000)
shr eax, 12
mov dword [page_directory_pointer_table.first_page_directory_pointer_table_entry - 0xC0000000 + 1], eax
mov byte [page_directory_pointer_table.first_page_directory_pointer_table_entry - 0xC0000000], 1

//Inserting page table address and flags into page directory entry, do so for all six of them
mov eax, (first_page_table - 0xC0000000)
shr eax, 12
mov dword [first_page_directory.first_page_directory_entry - 0xC0000000 + 1], eax
mov dword [third_page_directory.first_page_directory_entry - 0xC0000000 + 1], eax
mov byte [first_page_directory.first_page_directory_entry - 0xC0000000], 3
mov byte [third_page_directory.first_page_directory_entry - 0xC0000000], 3

//The way I fill my page tables, only showing the first 3
  mov ecx, 512
  xor eax, eax
  xor edx, edx
.Fill_First_Page_Table:
    cmp ecx, 0
    je .Done_Filling_First_Page_Table
    shr eax, 12
    mov dword [first_page_table - 0xC0000000 + 1 + edx], eax
    shl eax, 12
    mov byte [first_page_table - 0xC0000000 + edx], 3
    add eax, 0x1000
    dec ecx
    add edx, 8
    jmp .Fill_First_Page_Table
.Done_Filling_First_Page_Table:
    mov ecx, 512
    mov eax, 0x200000
    xor edx, edx
.Fill_Second_Page_Table:
    cmp ecx, 0
    je .Done_Filling_Second_Page_Table
    shr eax, 12
    mov dword [second_page_table - 0xC0000000 + 1 + edx], eax
    shl eax, 12
    mov byte [second_page_table - 0xC0000000 + edx], 3
    add eax, 0x1000
    dec ecx
    add edx, 8
    jmp .Fill_Second_Page_Table
.Done_Filling_Second_Page_Table:
    mov ecx, 512
    mov eax, 0x400000
    xor edx, edx
.Fill_Third_Page_Table:
    cmp ecx, 0
    je .Done_Filling_Third_Page_Table
    shr eax, 12
    mov dword [third_page_table - 0xC0000000 + 1 + edx], eax
    shl eax, 12
    mov byte [third_page_table - 0xC0000000 + edx], 3
    add eax, 0x1000
    dec ecx
    add edx, 8
    jmp .Fill_Third_Page_Table
.....etc


//Last chunk
mov eax, (page_directory_pointer_table - 0xC0000000)
    mov cr3, eax
    mov eax, cr4
    or eax, 00000000000000000000000000100000b
    mov cr4, eax
    mov eax, cr0
    or eax, 10000000000000000000000000000000b
    mov cr0, eax
I know this thread is getting long, sorry for that.

Re: Higher Half in C?

Posted: Wed Jul 18, 2018 1:58 pm
by kzinti
Octacone wrote: Easy for you to say, my pages are 4 KB not 2 MB. :) Thanks anyways!
My pages are 4 KB.

There are 512 entries in one page table. 512 x 4 KB = 2 MB per page directory entry.

I use 2 page tables for a total of 4 MB.

That 4 MB is identity-mapped and mapped to 3 GB using the first an last entries of the PDPT.

Re: Higher Half in C?

Posted: Wed Jul 18, 2018 2:08 pm
by Octacone
kzinti wrote:
Octacone wrote: Easy for you to say, my pages are 4 KB not 2 MB. :) Thanks anyways!
My pages are 4 KB.

There are 512 entries in one page table. 512 x 4 KB = 2 MB per page directory entry.

I use 2 page tables for a total of 4 MB.

That 4 MB is identity-mapped and also mapped to 3 GB.
Why did I assume they were 2 MB? :oops:
I'll definitely have a look to see if anything stands out.
I need like 12 MB but I'll figure it out.
Edit: So you are using like 1 page directory for the whole thing, just pointing to it twice inside the page directory pointer table. That is smart, it could save a lot of space when I think of it.
Edit: Wait, I don't see you shifting any addresses to the right by 12. Isn't that like mandatory, or it only applies to pages?
Reply to edit above: since it's page aligned it doesn't matter..., only matters for pages looks like.

Okay, I did some bug fixing and now I am getting physical address not available, which is better than not getting it. But why can't I see my page tables in Bochs? How am I supposed to fix them without seeing them?

Fixed: Higher Half in C?

Posted: Wed Jul 18, 2018 3:55 pm
by Octacone
VICTORY!
It is done, it is over, finally! :-"
Boooooy that took a while, but it was worth it.
Success.png
So how did I fix it?
While looking through @kzinti's code I noticed that he was adding flags directly to the address value, while I was doing it separately and accidentally offsetting it by 1 byte.
Many many thanks to @kzinti he appeared exactly when I needed him.
Now I just need to figure out how to load a new page directory pointer table with different values while removing 0xC000000 offset from all the values so they are physical. I will probably have to switch from static to dynamic allocation, but that is a problem for later for me to figure out.
Again, thanks to everybody who showed interest in helping.