Page 1 of 1

[SOLVED] Attempt to labeling paging tables instant reset CPU

Posted: Sun Aug 08, 2021 12:19 pm
by Rukog
The code is from https://wiki.osdev.org/Setting_Up_Long_Mode is working

Then I tried to labeling the tables and it instant reboot the OS.

Original code:

Code: Select all

    mov edi, 0x1000    ; Set the destination index to 0x1000.
    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], 0x2003      ; Set the uint32_t at the destination index to 0x2003.
    add edi, 0x1000              ; Add 0x1000 to the destination index.
    mov DWORD [edi], 0x3003      ; Set the uint32_t at the destination index to 0x3003.
    add edi, 0x1000              ; Add 0x1000 to the destination index.
    mov DWORD [edi], 0x4003      ; Set the uint32_t at the destination index to 0x4003.
    add edi, 0x1000              ; Add 0x1000 to the destination index.

    mov ebx, 0x00000003          ; Set the B-register to 0x00000003.
    mov ecx, 512                 ; Set the C-register to 512.
 
.SetEntry:
    mov DWORD [edi], ebx         ; Set the uint32_t at the destination index to the B-register.
    add ebx, 0x1000              ; Add 0x1000 to the B-register.
    add edi, 8                   ; Add eight to the destination index.
    loop .SetEntry               ; Set the next entry.
My mod:

Code: Select all

; Setup page table
;{
    %define PG_PRESENT    (1 << 0)
    %define PG_WRITE      (1 << 1)
    %define PG_READABLE   (1 << 2)

    mov     DWORD [p4_table], p3_table + (PG_PRESENT + PG_WRITE + PG_READABLE)
    mov     DWORD [p3_table], p2_table + (PG_PRESENT + PG_WRITE + PG_READABLE)
    mov     DWORD [p2_table], p1_table + (PG_PRESENT + PG_WRITE + PG_READABLE)

    mov     ebx, PG_PRESENT + PG_WRITE + PG_READABLE    ; Set the B-register to 0x00000003.
    mov     ecx, 512                                    ; Set the C-register to 512.
 
    mov     edi, p1_table
    .SetEntry:
    ;{
        mov     [edi], ebx      ; Set the uint32_t at the destination index to the B-register.
        add     ebx, 0x1000     ; Add 0x1000 to the B-register.
        add     edi, 8          ; Add eight to the destination index.
    
        loop    .SetEntry              ; Set the next entry.
    ;}
;}

p5_table: times 0x1000 db 0     ; Page Map Level 5
p4_table: times 0x1000 db 0     ; Page Map Level 4
p3_table: times 0x1000 db 0     ; Page Directory Pointer Table
p2_table: times 0x1000 db 0     ; Page Directory
p1_table: times 0x1000 db 0     ; Page Table

Re: Attempt to labeling page tables instant reset CPU

Posted: Sun Aug 08, 2021 1:23 pm
by iansjack
Your tables aren't page aligned. They must be.

Re: Attempt to labeling page tables instant reset CPU

Posted: Mon Aug 09, 2021 3:42 am
by Rukog
iansjack wrote:Your tables aren't page aligned. They must be.

Code: Select all

PagingTable: ; 512 address of 64-bit
;{
    .PML5: times 512 dq 0  ; Page Map Level 5
    .PML4: times 512 dq 0  ; Page Map Level 4
    .PDPT: times 512 dq 0  ; Page Directory Pointer Table
    .PD:   times 512 dq 0  ; Page Directory
    .PT:   times 512 dq 0  ; Page Table
;}
EndPagingTable:
Still doesn't work

Re: Attempt to labeling page tables instant reset CPU

Posted: Mon Aug 09, 2021 4:41 am
by Tyler

Code: Select all

align 4096
PagingTable: ; 512 address of 64-bit
;{
    .PML5: times 512 dq 0  ; Page Map Level 5
    .PML4: times 512 dq 0  ; Page Map Level 4
    .PDPT: times 512 dq 0  ; Page Directory Pointer Table
    .PD:   times 512 dq 0  ; Page Directory
    .PT:   times 512 dq 0  ; Page Table
;}
EndPagingTable:

Re: Attempt to labeling page tables instant reset CPU

Posted: Mon Aug 09, 2021 5:08 am
by Rukog
Tyler wrote:

Code: Select all

align 4096
PagingTable: ; 512 address of 64-bit
;{
    .PML5: times 512 dq 0  ; Page Map Level 5
    .PML4: times 512 dq 0  ; Page Map Level 4
    .PDPT: times 512 dq 0  ; Page Directory Pointer Table
    .PD:   times 512 dq 0  ; Page Directory
    .PT:   times 512 dq 0  ; Page Table
;}
EndPagingTable:
Thanks the code is working now but you are aligning the code above the tables there.

Why aligning the code too.

I assembled a source with only

Code: Select all

align 4096
PagingTable: ; 512 address of 64-bit
;{
    .PML5: times 512 dq 0  ; Page Map Level 5
    .PML4: times 512 dq 0  ; Page Map Level 4
    .PDPT: times 512 dq 0  ; Page Directory Pointer Table
    .PD:   times 512 dq 0  ; Page Directory
    .PT:   times 512 dq 0  ; Page Table
;}
EndPagingTable:
versus

Code: Select all

PagingTable: ; 512 address of 64-bit
;{
    .PML5: times 512 dq 0  ; Page Map Level 5
    .PML4: times 512 dq 0  ; Page Map Level 4
    .PDPT: times 512 dq 0  ; Page Directory Pointer Table
    .PD:   times 512 dq 0  ; Page Directory
    .PT:   times 512 dq 0  ; Page Table
;}
EndPagingTable:
And both do have the same bin size. but it's not when I add my code.

Re: Attempt to labeling paging tables instant reset CPU

Posted: Mon Aug 09, 2021 5:09 am
by iansjack
When you compile with just the table data it will be aligned to start with, so no padding is inserted. When you insert your code - unless it just happens to be an exact multiple of the page size - padding will be necessary to ensure that the data is aligned.

Running your code under a debugger would let you inspect memory and the problem would-be apparent immediately.

Re: Attempt to labeling paging tables instant reset CPU

Posted: Mon Aug 09, 2021 5:21 am
by Rukog
iansjack wrote:When you compile with just the table data it will be aligned to start with, so no padding is inserted. When you insert your code - unless it just happens to be an exact multiple of the page size - padding will be necessary to ensure that the data is aligned.

Running your code under a debugger would let you inspect memory and the problem would-be apparent immediately.
I see, so the above code is already encapsulated inside my ongoing paging table init, that's why I have to encapsulate it inside one single page.

Else the CPU does a reset since it cannot execute code or read data that share multiple pages.

Thank you :oops:

Re: Attempt to labeling paging tables instant reset CPU

Posted: Mon Aug 09, 2021 6:01 am
by Tyler
Rukog wrote:Else the CPU does a reset since it cannot execute code or read data that share multiple pages.
No.

IA-32/x86-64 have weak rules regarding aligment. Any code can cross (linear) page boundaries. Any (application) data can cross page boundaries. At most you will have performance penalties.

It's specifically paging data that was the problem in this example. Any given paging structure must appear entirely on a single physical page of memory. For practical purposes, this needs to be for one reason. CR3 and the paging structures (e.g. page table entries) only have bits available for the Top-20- (32-bit paging) or Top-40- (PAE and Long Mode) -bits of of the paging structures physical address. This means it is impossible for you to reference a page table, page directory etc. with any other alignment.

EDIT: I'd like to harp on about this a little more...

Really think about this code.

Code: Select all

    mov     DWORD [p4_table], p3_table + (PG_PRESENT + PG_WRITE + PG_READABLE)
    mov     DWORD [p3_table], p2_table + (PG_PRESENT + PG_WRITE + PG_READABLE)
    mov     DWORD [p2_table], p1_table + (PG_PRESENT + PG_WRITE + PG_READABLE)
Imagine your tables are misaligned such that p3_table is 0x8003. Then the first line becomes,

Code: Select all

    mov     DWORD [p4_table], 0x8003 + (PG_PRESENT + PG_WRITE + PG_READABLE)
Ignoring the (intentional) flags for a second. This means you are placing the value 0x0000000000008003 in the first P4 entry. Go to page 132 of https://software.intel.com/content/dam/ ... vol-3a.pdf and look at the structures. The "Address of page-directory-pointer table " is being set to 0xXXX0000000008XXX (where X is outside the value "Address of page-directory-pointer table " and I put it there to make it it a 64-bit number).

The 0x3 is being places in the lower part of the structure, setting the present and read/write flags.

Worse yet, because you add, rather than or'ing, your flags, you then do the sum 0x8003 + 1 + 2 + 4, giving a value of 0x800A. In this value, you are clearing the Present, Supervisor bits, setting the writable bit, and accidentally setting the page write through caching flag. This lack of present flag is what probably caused the triple fault, as the processor could have never known you intended to use a mis-aligned page.

Incidentally, I'm not sure about,

Code: Select all

    %define PG_READABLE   (1 << 2)
According the my reading of the page I linked above, this sets the User/Supervisor flag, not a flag setting read permissions.

Re: Attempt to labeling paging tables instant reset CPU

Posted: Mon Aug 09, 2021 8:19 am
by iansjack
Rukog wrote:Else the CPU does a reset since it cannot execute code or read data that share multiple pages.
That would be extremely limiting, and is not the case.

I think you may benefit from reading the Intel or AMD Programmer's Manuals to ensure that you have a thorough understanding of the basic operation of the processor. Pay particular attention to the chapter on memory management.

Re: Attempt to labeling paging tables instant reset CPU

Posted: Mon Aug 09, 2021 8:28 am
by Rukog
iansjack wrote:
Rukog wrote:Else the CPU does a reset since it cannot execute code or read data that share multiple pages.
That would be extremely limiting, and is not the case.
I think you may benefit from reading the Intel or AMD Programmer's Manuals to ensure that you have a thorough understanding of the basic operation of the processor. Pay particular attention to the chapter on memory management.
Yep, long reading awaits me oof
Tyler wrote:
Rukog wrote:Else the CPU does a reset since it cannot execute code or read data that share multiple pages.
No.

IA-32/x86-64 have weak rules regarding aligment. Any code can cross (linear) page boundaries. Any (application) data can cross page boundaries. At most you will have performance penalties.

It's specifically paging data that was the problem in this example. Any given paging structure must appear entirely on a single physical page of memory. For practical purposes, this needs to be for one reason. CR3 and the paging structures (e.g. page table entries) only have bits available for the Top-20- (32-bit paging) or Top-40- (PAE and Long Mode) -bits of of the paging structures physical address. This means it is impossible for you to reference a page table, page directory etc. with any other alignment.

EDIT: I'd like to harp on about this a little more...

Really think about this code.

Code: Select all

    mov     DWORD [p4_table], p3_table + (PG_PRESENT + PG_WRITE + PG_READABLE)
    mov     DWORD [p3_table], p2_table + (PG_PRESENT + PG_WRITE + PG_READABLE)
    mov     DWORD [p2_table], p1_table + (PG_PRESENT + PG_WRITE + PG_READABLE)
Imagine your tables are misaligned such that p3_table is 0x8003. Then the first line becomes,

Code: Select all

    mov     DWORD [p4_table], 0x8003 + (PG_PRESENT + PG_WRITE + PG_READABLE)
Ignoring the (intentional) flags for a second. This means you are placing the value 0x0000000000008003 in the first P4 entry. Go to page 132 of https://software.intel.com/content/dam/ ... vol-3a.pdf and look at the structures. The "Address of page-directory-pointer table " is being set to 0xXXX0000000008XXX (where X is outside the value "Address of page-directory-pointer table " and I put it there to make it it a 64-bit number).

The 0x3 is being places in the lower part of the structure, setting the present and read/write flags.

Worse yet, because you add, rather than or'ing, your flags, you then do the sum 0x8003 + 1 + 2 + 4, giving a value of 0x800A. In this value, you are clearing the Present, Supervisor bits, setting the writable bit, and accidentally setting the page write through caching flag. This lack of present flag is what probably caused the triple fault, as the processor could have never known you intended to use a mis-aligned page.

Incidentally, I'm not sure about,

Code: Select all

    %define PG_READABLE   (1 << 2)
According the my reading of the page I linked above, this sets the User/Supervisor flag, not a flag setting read permissions.
Fixed, thanks

Code: Select all

%define PG_FLAG_P(bit)  (bit << 0)  ; 0: Page not Present   1: Page Present
%define PG_FLAG_RW(bit) (bit << 1)  ; 0: Read-only          1: Read Write
%define PG_FLAG_US(bit) (bit << 2)  ; 0: Supervisor Mode    1: User Mode

mov     eax, PG_FLAG_P(1) + PG_FLAG_RW(1) + PG_FLAG_US(0)

; mov     [PagingTable.PML5], dword PagingTable.PML4
; or      [PagingTable.PML5], eax

mov     [PagingTable.PML4], dword PagingTable.PDPT
or      [PagingTable.PML4], eax

mov     [PagingTable.PDPT], dword PagingTable.PD
or      [PagingTable.PDPT], eax

mov     [PagingTable.PD]  , dword PagingTable.PT
or      [PagingTable.PD]  , eax

Re: [SOLVED] Attempt to labeling paging tables instant reset

Posted: Mon Aug 09, 2021 10:21 am
by Ethin

Code: Select all

mov     eax, PG_FLAG_P(1) + PG_FLAG_RW(1) + PG_FLAG_US(0)
Am I missing something here? I feel like these should be ORed and not ADDed together.

Re: [SOLVED] Attempt to labeling paging tables instant reset

Posted: Mon Aug 09, 2021 11:00 am
by iansjack
Strictly speaking, yes, but it makes no difference in this case.