Realmode to 64bit long mode switch problems

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
johnsa
Member
Member
Posts: 296
Joined: Mon Oct 15, 2007 3:04 pm

Realmode to 64bit long mode switch problems

Post by johnsa »

Hey all,

So after some discussions in other threads and looking at the long mode tutorial I've run into a few snags with the switch to long mode...

Code: Select all


; LOAD our 64bit GDT
	mov edi,offset GDTReg 
	lgdt fword ptr ds:[edi]					; Load 64bit Kernel GDT.

;SETUP PML4/PDs
xor bx,bx
mov es,bx
cld
mov di,0a000h

mov ax,0b00fh
stosw

xor ax,ax
mov cx,07ffh
rep stosw

mov ax,0c00fh
stosw

xor ax,ax
mov cx,07ffh
rep stosw

mov ax,018fh
stosw

xor ax,ax
mov cx,07ffh
rep stosw

;SETUP LONG MODE and PAGING OPTIONS
	mov eax,10100000b						; Set PAE and PGE.
	mov cr4,eax

	mov edx,0000a000h						; Point CR3 at PML4.
	mov cr3,edx
	
	mov ecx,0c0000080h						; Specify EFER MSR.
	RDMSR									; Read MSR.
	or eax,00000100h						; Enable Long Mode.
	WRMSR									; Write MSR Back.
	mov ebx,cr0
	or ebx,80000001h						; Enable Long Mode By Enabling Paging and Protection.
	mov cr0,ebx

	; !!!ALL WORKS TILL HERE!!!!

	dw 0ea66h
	dd 0100000h
 	dw 08h
Now I wanted to jump straight to the kernel having paging set to the higher-half model with paging set to map 0xFFFF800000000000 to 0x100000 phys (1mb). But based on what I'm trying to do with the jmp to switch cs to 64bit code selector / linear addr. being limited to 32bit this would not seem possible... this makes the kernel linking an issue now... so how would I perform the switch from 16bit real straight to 64bit kernel at the higher half addr of 0xFFFF800000000000??
I tried to embedded the code (db's for opcodes) for the push/retf trick to do the jump straight to 0xFFFF800000000000 but that also didn't seem to get me anywhere.

I've checked that the kernel code is correct and is indeed loaded at 0x100000.

RDMSR/WRMSR I created macros for as follows... why?... for some reason I originally started the bootloader code in tasm.. no biggie, I've added the opcodes that aren't supported as macros..:

Code: Select all

;=======================================================================================
; Obtain CPU Information.
; Function in EAX.
;=======================================================================================
CPUID MACRO
	db 0fh,0a2h
ENDM
;=======================================================================================
; Read Time Stamp Counter
; edx:eax returns counter.
;=======================================================================================
RDTSC MACRO
	db 0fh,31h
ENDM
;=======================================================================================
; (Branch Not Taken) Branch Hint.
;=======================================================================================
BNT MACRO
	db 2eh	
ENDM
;=======================================================================================
; (Branch Taken) Branch Hint.
;=======================================================================================
BTK MACRO
	db 3eh	
ENDM
;=======================================================================================
; Read Model Specific Register.
;=======================================================================================
RDMSR MACRO
	db 0fh,32h
ENDM
;=======================================================================================
; Write Model Specific Register.
;=======================================================================================
WRMSR MACRO
	db 0fh,30h
ENDM
Upon reaching the jump code bochs/qemu and real h/w just reboot...

any ideas? the mode switch code is from the wiki tut as well as the basic temp paging setup stuff (for now until i get this higher half thing right and can setup the paging properly)
Also, I presume theres no point to setup IDT before being in long mode as you'd reload it immediately in long mode using the new 16byte IDT descriptors?

John
johnsa
Member
Member
Posts: 296
Joined: Mon Oct 15, 2007 3:04 pm

Re: Realmode to 64bit long mode switch problems

Post by johnsa »

When enabling the global page feature, paging must be enabled (by setting the PG flag in control register CR0) before the PGE flag is set. Reversing this sequence may affect program correctness, and processor performance will be impacted.

From the Intel guide 3A page 80... so based on that info I updated the code from the wiki long mode tut as follows:

Code: Select all

	mov edx,0000a000h						; Point CR3 at PML4.
	mov cr3,edx
	
	mov ecx,0c0000080h						; Specify EFER MSR.
	RDMSR									; Read MSR.
	or eax,00000100h						; Enable Long Mode.
	WRMSR									; Write MSR Back.
	mov ebx,cr0
	or ebx,80000000h						; Enable Long Mode By Enabling Paging and Protected Mode.
	mov cr0,ebx

	mov eax,10100000b						; Set PAE and PGE.
	mov cr4,eax
	
	mov ebx,cr0
	or ebx,1
	mov cr0,ebx
	
	dw 0ea66h								; Far jump to reload CS into long mode 64bit and flush
	dd 0100000h								; Instruction Caches.
 	dw 08h
Still no joy.. bochs/qemu just freeze now without reboot.
johnsa
Member
Member
Posts: 296
Joined: Mon Oct 15, 2007 3:04 pm

Re: Realmode to 64bit long mode switch problems

Post by johnsa »

according to page 371 of the same manual you MUST enable PAE before before paging for ia32-e consistency check.. so i presume enabling pae and pge in one shot circumvents the problem in the previous post?
User avatar
IanSeyler
Member
Member
Posts: 326
Joined: Mon Jul 28, 2008 9:46 am
Location: Ontario, Canada
Contact:

Re: Realmode to 64bit long mode switch problems

Post by IanSeyler »

This is the code I use for going directly into 64-bit mode from 16-bit mode...

Code: Select all

; Enable physical-address extensions (PAE)
	mov eax, cr4
	or eax, 00000000000000000000000000100000b
	mov cr4, eax

; Point cr3 at PML4
	mov eax, 0x00002008 ; Bit 3 set for write-thru
	mov cr3, eax
	
; Enable long mode (set EFER.LME=1)
	mov ecx, 0xC0000080			; EFER MSR number
	rdmsr						; Read EFER
	or eax, 00000000000000000000000100000001b	; LME amd SYSCALL/SYSRET
	wrmsr						; Write EFER

; Enable paging AND protected mode to activate long mode (set CR0.PG=1)
	mov eax, cr0				; Read CR0
	or eax, 10000000000000000000000000000001b ; enable paging + pmode
	mov cr0, eax				; Write CR0

; Make the jump directly from 16-bit real mode to 64-bit long mode
	jmp SYS64_CODE_SEL:start64
First thing I do is enable PAE.. Then Long mode.. Last step is to enable Paging and Protected mode in one call.

Double check what you are putting into CR4 and CR0.
BareMetal OS - http://www.returninfinity.com/
Mono-tasking 64-bit OS for x86-64 based computers, written entirely in Assembly
johnsa
Member
Member
Posts: 296
Joined: Mon Oct 15, 2007 3:04 pm

Re: Realmode to 64bit long mode switch problems

Post by johnsa »

Hey,

First question: what is the benefit of enabling write-through caching as opposed to write-back on the PD tables via CR3?
Second question: In the wiki code they additionally enable the PGE (Global page bit) in CR4 as well as PAE. I read the Intel manuals in terms of what this bit does, and the only benefit I can see is that if you change / reload your paging structures I'm presuming that page-dir/pml4 entries marked global will not be flushed?

I've updated my code as follows and double checked it again, still getting a complete reboot under qemu/bochs.

Code: Select all

	mov edi,offset GDTReg 
	lgdt fword ptr ds:[edi]					; Load 64bit Kernel GDT.
		
	; Enable physical-address extensions (PAE)
	mov eax,cr4
	or eax,0a0h								; Enable PAE and PGE bits. 
	mov cr4,eax

	; Point CR3 at PML4
	mov eax,0000a008h 						; Bit 3 set for write-thru caching.
	mov cr3,eax
   
	; Enable long mode (set EFER.LME=1)
	mov ecx,0c0000080h         				; EFER MSR number
	RDMSR
	or eax,00000101h   						; LME amd SYSCALL/SYSRET
	WRMSR

	; Enable paging AND protected mode to activate long mode (set CR0.PG=1)
    mov eax,cr0            					; Read CR0
    or eax,80000001h						; Enable Paging and Protection.
    mov cr0,eax            					; Write CR0

	; Make the jump directly from 16-bit real mode to 64-bit long mode
	dw 0ea66h								; Far jump to reload CS into long mode 64bit and flush
	dd 0100000h								; Instruction Caches.
 	dw 08h

.
.
.

	align 16
	GDTReg dw gdtsize,0,0	
GDT:
	dq 0                 									; null descriptor.
	dq 0020980000000000h 									; code64 descriptor.
	dq 0000900000000000h 									; data64 descriptor.
	gdtsize equ ($-GDT)

johnsa
Member
Member
Posts: 296
Joined: Mon Oct 15, 2007 3:04 pm

Re: Realmode to 64bit long mode switch problems

Post by johnsa »

It seems to still die on the jump.. because I'm using tasm .. I've had to hard-code the jump opcode as I did.

I'm also guessing that for this whole thing to work with a higher half 64bit kernel I'd need a 64bit stub first, as I have now with paging basically transparent.. so the stub is linked to appear at it's true physical address
of 0x100000 and in which we'd then reconfigure paging to map the kernel to higher-half and do a 64bit jump?
johnsa
Member
Member
Posts: 296
Joined: Mon Oct 15, 2007 3:04 pm

Re: Realmode to 64bit long mode switch problems

Post by johnsa »

Ok so I just noticed that it's actually triple-faulting ON the dw 0ea66h (JMP) when running under qemu... im running out of ideas here :)

I've now also tried changing the GDT entries to have limit all 1's as opposed to 0. Doesn't seem to make any difference and should be ignored either way.
johnsa
Member
Member
Posts: 296
Joined: Mon Oct 15, 2007 3:04 pm

Re: Realmode to 64bit long mode switch problems

Post by johnsa »

Ok... here is the problem (I think)

The jump code I have is:

dw 0ea66h
dd 0100000h
dw 08h

jmp opcode 0eah is NOT supported in 64bit mode...
I'm trying to get this to work using another jmp, but the standard form :
jmp 08h:0100000h doesn't assemble...
can someone provide a working jmp opcode as above using one of the far jmps (i guess) like m16:32 ?
johnsa
Member
Member
Posts: 296
Joined: Mon Oct 15, 2007 3:04 pm

Re: Realmode to 64bit long mode switch problems

Post by johnsa »

I assembled the sample from wiki using nasm, and looked at the output.. it too generates an 0eah opcode but it works under bochs/qemu... what the >!?!?
johnsa
Member
Member
Posts: 296
Joined: Mon Oct 15, 2007 3:04 pm

Re: Realmode to 64bit long mode switch problems

Post by johnsa »

I am a complete moron... never mind.. I never loaded the GDT's address into the GDTReg structure before loading it with LGDT.... all working now :)
Post Reply