OSDev.org

The Place to Start for Operating System Developers
It is currently Sat May 25, 2024 9:19 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 9 posts ] 
Author Message
 Post subject: PAE paging problem
PostPosted: Sun Apr 21, 2024 12:33 pm 
Offline
Member
Member

Joined: Sat Sep 19, 2020 7:18 am
Posts: 41
Hi all,

I've written a program that enters long mode, clears the screen, and exits long mode. As part of returning from long mode to 32-bit "regular" protected mode, I'm trying to re-enable PAE-style paging. So, after the exit from long mode, I change CR3 from pointing to the PML4 table to point to the Page Directory Pointer Table instead. Then Bochs generates this error message:
Code:
202206508436e[CPU0  ] SetCR0(): PDPTR check failed !


What might be wrong? The PDPT works fine in long mode.

Source code is available on request.

Regards,
Albert.


Top
 Profile  
 
 Post subject: Re: PAE paging problem
PostPosted: Sun Apr 21, 2024 1:15 pm 
Online
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5181
awik wrote:
What might be wrong?

Bochs displays that message when reserved bits are set in the PDPT.

awik wrote:
The PDPT works fine in long mode.

Long mode and protected mode have different definitions for the PDPT. A PDPT that's valid in long mode may not be valid in protected mode.


Top
 Profile  
 
 Post subject: Re: PAE paging problem
PostPosted: Mon Apr 22, 2024 11:56 pm 
Offline
Member
Member

Joined: Sat Sep 19, 2020 7:18 am
Posts: 41
You're were right. Apparently, only bit 0 may be set in a PDPT entry in regular protected mode. It is necessary to re-initialise the entries, because in long mode, several other bits get set automatically.

-Albert.


Top
 Profile  
 
 Post subject: Re: PAE paging problem
PostPosted: Tue Apr 23, 2024 11:06 am 
Offline
Member
Member

Joined: Wed Oct 01, 2008 1:55 pm
Posts: 3196
You must create a GDT, switch to compatibility mode, disable paging, change CR3, set PAE bit and then renable protected mode paging.

My code looks like this: (it's mostly hex-coded since my assembler cannot handle mixed 32 & 64-bit code)

Code:
IMAGE_BASE = 110000h

param_struc     STRUC

lfb_base        DD ?,?
lfb_width       DD ?
lfb_height      DD ?
lfb_line_size   DD ?
lfb_flags       DD ?
mem_entries     DD ?
acpi_table      DD ?,?

param_struc     ENDS

_TEXT segment byte public use16 'CODE'

    .386p

    db 0Ebh            ; jmp init64
    db 38h + SIZE param_struc

param   param_struc <>

rom_gdt:
gdt0:
    dw 0
    dd 0
    dw 0
gdt8:
    dw 0
    dd 0
    dw 0
gdt10:
    dw 28h-1
    dd 92000000h + OFFSET rom_gdt + IMAGE_BASE
    dw 0
gdt18:
    dw 0FFFFh
    dd 9A000000h
    dw 0CFh
gdt20:
    dw 0FFFFh
    dd 92000000h
    dw 08Fh

gdt_ptr:
    dw 28h-1
    dd OFFSET rom_gdt + IMAGE_BASE
    dd 0

prot_ptr:
    dd OFFSET prot_init + IMAGE_BASE
    dw 18h

init64:
    db 0FAh    ; cli
    db 0Fh     ; lgdt gdt_ptr
    db 01h
    db 15h
    dd 0FFFFFFE8h
;
    db 0FFh
    db 1Dh
    dd 0FFFFFFECh

prot_init:
    db 0Fh     ; mov eax,cr0
    db 20h
    db 0C0h
;
    db 25h     ; and eax,7FFFFFFFh
    dd 07FFFFFFFh
;
    db 0Fh     ; mov cr0,eax
    db 22h
    db 0C0h
;
    db 0B9h    ; mov ecx,IA32_EFER
    dd 0C0000080h
;
    db 0Fh     ; rdmsr
    db 32h
;
    db 25h     ; and eax,0FFFFFEFFh
    dd 0FFFFFEFFh
;
    db 0Fh     ; wrmsr
    db 30h
;
    db 0Fh     ; mov rax,cr4
    db 20h
    db 0E0h
;
    db 83h     ; and eax,NOT 20h
    db 0E0h
    db 0DFh
;
    db 0Fh     ; mov cr4,rax
    db 22h
    db 0E0h
;
    db 0B8h    ; mov eax,20h
    dd 20h
;
    db 8Eh     ; mov ds,eax
    db 0D8h
;
    db 0BBh    ; mov ebx,OFFSET gdt18
    dd OFFSET gdt18 + IMAGE_BASE
;
    db 0BAh    ; mov edx,IMAGE_BASE
    dd IMAGE_BASE
;
    db 89h     ; mov [ebx+2],edx
    db 53h
    db 02h
;
    db 0B0h    ; mov al,9Ah
    db 9Ah
;
    db 86h     ; xchg al,[ebx+5]
    db 43h
    db 5
;
    db 32h     ; xor cl,cl
    db 0C9h
;
    db 8Ah     ; mov ch,al
    db 0E8h
;
    db 66h     ; mov [ebx+6],cx
    db 89h
    db 4Bh
    db 6
;
    db 0EAh    ; jmp 18:init
    dd OFFSET init
    dw 18h

init:
    mov ax,20h
    mov ds,ax
    mov es,ax
    mov fs,ax
    mov gs,ax



Top
 Profile  
 
 Post subject: Re: PAE paging problem
PostPosted: Wed Apr 24, 2024 7:41 am 
Offline
Member
Member

Joined: Sat Sep 19, 2020 7:18 am
Posts: 41
rdos wrote:
You must create a GDT, switch to compatibility mode, disable paging, change CR3, set PAE bit and then renable protected mode paging.


I already have a GDT, and the PAE bit is already set.

Quote:
My code looks like this: (it's mostly hex-coded since my assembler cannot handle mixed 32 & 64-bit code)


My (now working) code is as follows:
Code:
   ;
   ; Going from 64-bit mode straight to legacy (non-long) mode is
   ; not legal.  Also, we cannot go directly to a 32-bit code segment
   ; with a (non-zero) base, because the base does not get loaded as
   ; long (pun not intended) as we are in Long Mode.  The solution is
   ; to go to Compatibility Mode first.
   ;
;   mov   ax, SEG_BASED_CS_32
   mov   ax, SEG_FLAT_CS_32  ; segment suitable for
   ;             ;   Compatibility Mode
   push   rax
   lea   eax,[esi+.exit_from_64]
   push   rax
   retfq
BITS 32
ALIGN 4
.exit_from_64:
   ;
   ; We are now in Compatibility Mode.
   ;
   ; We need to turn off paging before we can exit Long Mode.
   ;
   mov   eax,cr0
   btr   eax,CR0_PG_BIT
   mov   cr0,eax
   ;
   ; Disable Long Mode
   ;
   mov   ecx,MSR_EFER
   rdmsr
   btr   eax,EFER_LM_BIT
   wrmsr
   ;
   push   SEG_BASED_CS_32    ; "based" 32-bit code segment
   push   .exit_from_lm32
   retfd
ALIGN 4
BITS 32
.exit_from_lm32:
   ;
   ; We are now running from our "based" 32-bit code segment.
   ;
   mov   ax,SEG_BASED_DS_16  ; "based" data/stack segment
   mov   ss, ax
   sub   esp,esi      ; convert linear to segment-relative
   sub   ebp,esi
   mov   ds, ax
   mov   es, ax
   ;
   ; Set up paging for "normal" Protected Mode
   ;
   mov   eax,[ebp+BSS_PDPT_PHYSADDR]
   mov   ebx,eax
   sub   ebx,esi      ; convert linear to segment-relative
   and   byte [ebx], 1   ; clear reserved bits
   mov   cr3,eax
   mov   eax,cr0
   bts   eax,CR0_PG_BIT
   mov   cr0,eax
   ;


Top
 Profile  
 
 Post subject: Re: PAE paging problem
PostPosted: Wed Apr 24, 2024 11:43 pm 
Offline
Member
Member

Joined: Wed Oct 01, 2008 1:55 pm
Posts: 3196
Not sure about the non-zero base. You can load selectors with non-zero base in long mode, but the base will not be applied until you are in compatibility mode.
It's perfectly possible to do a far jump to legacy code with a non-zero base. Segment register loads operates exactly the same in long mode as in protected mode.

I posted my EFI 64->32 bit code previously, but I also have simpler code to switch between protected mode and long mode and the reverse. Switching to long mode is pretty easy:

Code:

; EAX = cr3

switch_to_long_mode   Proc far
    push eax
    push ebx
    push ecx
    push edx
    pushf
;
    mov ebx,eax
    cli
;
    mov eax,cr0
    and eax,7FFFFFFFh
    mov cr0,eax
;
    mov ecx,IA32_EFER
    rdmsr
    or eax,101h
    wrmsr
;
    mov cr3,ebx
;
    mov eax,cr0
    or eax,80000000h
    mov cr0,eax
;
    lidt fword ptr cs:long_idt_size
;
    popf
    pop edx
    pop ecx
    pop ebx
    pop eax
    ret
switch_to_long_mode  Endp


Switching back is easy too:

Code:

; EAX = cr3

switch_to_protected_mode   Proc far
    push eax
    push ebx
    push ecx
    push edx
    pushf
;
    mov ebx,eax
    cli
;
    mov eax,cr0
    and eax,7FFFFFFFh
    mov cr0,eax
;
    mov ecx,IA32_EFER
    rdmsr
    and eax,0FFFFFEFFh   
    wrmsr
;
    mov cr3,ebx
;
    mov eax,cr0
    or eax,80000000h
    mov cr0,eax
;
    lidt fword ptr cs:prot_idt_size
;
    popf
    pop edx
    pop ecx
    pop ebx
    pop eax   
    ret
switch_to_protected_mode  Endp


However, the switching back code is running in compatibility mode since the scheduler is running in compatibility mode and not in long mode. The switching from long mode to compatibility mode happens as part of a syscall or an IRQ, which contain code to do a far call to the common handler code in compatibility mode.

Also note that the GDT can be shared between protected mode and long mode, but not the IDT. There is a need for a specific IDT for long mode.


Top
 Profile  
 
 Post subject: Re: PAE paging problem
PostPosted: Thu Apr 25, 2024 3:36 am 
Offline
Member
Member

Joined: Sat Sep 19, 2020 7:18 am
Posts: 41
rdos wrote:
Not sure about the non-zero base.


I was pretty sure about it, because I had tested it, but now that I tested it again, the base *does* get set. I think I must have pushed the wrong (ie. flat) address last time.

Quote:
Switching to long mode is pretty easy:


It *is* easy once you have PAE paging set up. Otherwise, you have to set up 4 levels of paging structures first.

Quote:
... a syscall or an IRQ, which contain handlers with a compatibility mode descriptor.


Cool, I didn't know you could do that.

Quote:
Also note that the GDT can be shared between protected mode and long mode, but not the IDT. There is a need for a specific IDT for long mode.


I know. I haven't tried creating a 64-bit IDT myself yet.

-Albert.


Top
 Profile  
 
 Post subject: Re: PAE paging problem
PostPosted: Fri Apr 26, 2024 5:47 am 
Offline
Member
Member

Joined: Sat Sep 19, 2020 7:18 am
Posts: 41
awik wrote:
rdos wrote:
... a syscall or an IRQ, which contain handlers with a compatibility mode descriptor.
Cool, I didn't know you could do that.


The "AMD64 Architecture Programmer's Manual", volume 2, "System Programming), page 102, reads: "The target code segment referenced by a long-mode gate descriptor must be a 64-bit code segment (CS.L=1, CS.D=0). If the target is not a 64-bit code segment, a general-protection exception, #GP(error), occurs.".

Is this incorrect?


Top
 Profile  
 
 Post subject: Re: PAE paging problem
PostPosted: Fri Apr 26, 2024 6:56 am 
Offline
Member
Member

Joined: Wed Oct 01, 2008 1:55 pm
Posts: 3196
awik wrote:
awik wrote:
rdos wrote:
... a syscall or an IRQ, which contain handlers with a compatibility mode descriptor.
Cool, I didn't know you could do that.


The "AMD64 Architecture Programmer's Manual", volume 2, "System Programming), page 102, reads: "The target code segment referenced by a long-mode gate descriptor must be a 64-bit code segment (CS.L=1, CS.D=0). If the target is not a 64-bit code segment, a general-protection exception, #GP(error), occurs.".

Is this incorrect?


The manual is correct. I changed my post so that the IRQ handler does a far call to the compatibility mode server procedure. That's part of the reason you need different IDTs, and also different high-level handler procedures. The long mode interrupt handlers must run in long mode, but can defer their function to compatibility mode for common cases like IRQs. Exceptions, including page fault, need to be handled completely in long mode, perhaps except for aborting a faulty process.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 9 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Google [Bot] and 44 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group