Page 1 of 1

Leaving long mode

Posted: Mon Sep 15, 2008 3:53 pm
by ru2aqare
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).

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                                   ;
(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

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
If I first try to disable LME, then disable CR0.PG, I get

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
And if I first try to disable PAE, I get

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
Any suggestions?

Re: Leaving long mode

Posted: Mon Sep 15, 2008 6:23 pm
by Brendan
Hi,

From the top of my head, you'd probably need to:
  • - load 32-bit "compatibility mode" segments (including CS)
    - disable paging (which should also turn off long mode)
    - load 16-bit protected mode segments
    - disable protected mode
    - load 16-bit real mode segments
I don't think you need to touch the EFER because you can't be in long mode with paging disabled; and you definitely won't need to disable PAE because the PAE enable/disable flag is ignored when paging is disabled.


Cheers,

Brendan

Re: Leaving long mode

Posted: Mon Sep 15, 2008 7:39 pm
by bewing
Except that he isn't trying to get all the way back to Real mode -- just to Pmode. So he only needs to do the first 2 steps -- especially the part about loading the hidden parts of the segment registers with 32bit pmode selectors.

Re: Leaving long mode

Posted: Tue Sep 16, 2008 12:59 am
by ru2aqare
Brendan wrote:Hi,

From the top of my head, you'd probably need to:
  • - load 32-bit "compatibility mode" segments (including CS)
    - disable paging (which should also turn off long mode)
    - load 16-bit protected mode segments
    - disable protected mode
    - load 16-bit real mode segments
I don't think you need to touch the EFER because you can't be in long mode with paging disabled; and you definitely won't need to disable PAE because the PAE enable/disable flag is ignored when paging is disabled.


Cheers,

Brendan
Thank you, I got it working. It is interesting, however, that the AMD manual does not mention loading CS with a 32-bit descriptor before disabling paging...

Re: Leaving long mode

Posted: Fri Sep 27, 2013 1:58 am
by bellezzasolo
Long mode is enabled, and an attempt is made to enable paging while CS.L=1.

It does say this... it is obsucre though.
CS.L is 64 bit. Enable includes disable.
It is saying you need to be in compatibility mode.