Leaving long mode
Posted: Mon Sep 15, 2008 3:53 pm
I am currently writing my boot loader. It successfully enters 32-bit protected mode, enables paging, and also provides a V86 monitor to the PE image attached to it as a payload. The image may specify either i386 or amd64 processor, and the loader will prepare the environment requested. If the image is a 64-bit image, the loader successfully enters long mode (I have used the AMD manual and also have taken a peek at BareMetal OS). My problem is, I can't exit long mode... I believe I am following the steps outlined in the manual (page 44 in the 134 page PDF), but get a triple fault in Bochs.
Here is the code I use for returning to legacy protected mode. The first few lines save the environment, so that the loader can re-enter long mode (for example after calling a BIOS interrupt or executing some v86 code).
(I know that hard-coding offsets is bad practice, however I can't yet get the 64-bit MASM to accept a structure field name as an offset.)
What am I doing wrong? Bochs says
If I first try to disable LME, then disable CR0.PG, I get
And if I first try to disable PAE, I get
Any suggestions?
Here is the code I use for returning to legacy protected mode. The first few lines save the environment, so that the loader can re-enter long mode (for example after calling a BIOS interrupt or executing some v86 code).
Code: Select all
LdrTransition64To32:
xchg rax, rcx ; swap regs (call destination -> rcx)
mov esi, offset LoaderData ; load address of data structure
pushfq ;
pop [rsi + 1Ch] ; save flags
mov [rsi + 0Ch], esp ; save rsp
sidt fword ptr [rsi + 3Ch] ; save IDTR
cli ; disable interrupts
; this is based on the AMD manual
mov rax, cr0 ;
and eax, 7FFFFFFFh ; disable paging
mov cr0, rax ;
mov eax, mrTaskLoaderx86PageDirStart; load cr3 with 32-bit page table
mov cr3, rax ;
mov ecx, 0C0000080h ; EFER MSR number
rdmsr ; read EFER
and eax, not 100h ;
wrmsr ; write EFER
mov rax, cr4 ; disable PAE
and al, not 20h ;
mov cr4, rax ;
; below this line is speculation, this may be outright wrong.
mov rax, offset LdrTransition64To32$0 ; address to return to
pushfq ; fake an iretq
push Code32R0Sel ;
push rax ;
iretq ;
What am I doing wrong? Bochs says
Code: Select all
00055028596e[CPU0 ] SetCR0: attempt to leave 64 bit mode directly to legacy mode !
00055028596d[CPU0 ] exception(0x0d): error_code=0000
00055028596d[CPU0 ] interrupt(): vector = 13, INT = 0, EXT = 1
00055028596d[CPU0 ] interrupt(long mode): INTERRUPT TO SAME PRIVILEGE
Code: Select all
00055028597e[CPU0 ] WRMSR: attempt to change LME when CR0.PG=1
00055028597d[CPU0 ] exception(0x0d): error_code=0000
00055028597d[CPU0 ] interrupt(): vector = 13, INT = 0, EXT = 1
00055028597d[CPU0 ] interrupt(long mode): INTERRUPT TO SAME PRIVILEGE
Code: Select all
00055028594d[CPU0 ] MOV_RqCq: read of CR4
00055028596d[CPU0 ] MOV_CqRq: write to CR4 of 00000000:00000000
00055028596e[CPU0 ] SetCR4: attempt to change PAE when EFER.LMA=1
00055028596d[CPU0 ] exception(0x0d): error_code=0000
00055028596d[CPU0 ] interrupt(): vector = 13, INT = 0, EXT = 1
00055028596d[CPU0 ] interrupt(long mode): INTERRUPT TO SAME PRIVILEGE