PAE paging problem

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
awik
Member
Member
Posts: 41
Joined: Sat Sep 19, 2020 7:18 am

PAE paging problem

Post by awik »

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: Select all

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.
Octocontrabass
Member
Member
Posts: 5218
Joined: Mon Mar 25, 2013 7:01 pm

Re: PAE paging problem

Post by Octocontrabass »

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.
awik
Member
Member
Posts: 41
Joined: Sat Sep 19, 2020 7:18 am

Re: PAE paging problem

Post by awik »

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.
rdos
Member
Member
Posts: 3198
Joined: Wed Oct 01, 2008 1:55 pm

Re: PAE paging problem

Post by rdos »

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: Select all

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

awik
Member
Member
Posts: 41
Joined: Sat Sep 19, 2020 7:18 am

Re: PAE paging problem

Post by awik »

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.
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: Select all

	;
	; 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
	;
rdos
Member
Member
Posts: 3198
Joined: Wed Oct 01, 2008 1:55 pm

Re: PAE paging problem

Post by rdos »

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: Select all


; 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: Select all


; 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.
awik
Member
Member
Posts: 41
Joined: Sat Sep 19, 2020 7:18 am

Re: PAE paging problem

Post by awik »

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.
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.
... a syscall or an IRQ, which contain handlers with a compatibility mode descriptor.
Cool, I didn't know you could do that.
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.
awik
Member
Member
Posts: 41
Joined: Sat Sep 19, 2020 7:18 am

Re: PAE paging problem

Post by awik »

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?
rdos
Member
Member
Posts: 3198
Joined: Wed Oct 01, 2008 1:55 pm

Re: PAE paging problem

Post by rdos »

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.
Post Reply