Leaving long mode

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
ru2aqare
Member
Member
Posts: 342
Joined: Fri Jul 11, 2008 5:15 am
Location: Hungary

Leaving long mode

Post 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?
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Leaving long mode

Post 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
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
bewing
Member
Member
Posts: 1401
Joined: Wed Feb 07, 2007 1:45 pm
Location: Eugene, OR, US

Re: Leaving long mode

Post 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.
ru2aqare
Member
Member
Posts: 342
Joined: Fri Jul 11, 2008 5:15 am
Location: Hungary

Re: Leaving long mode

Post 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...
User avatar
bellezzasolo
Member
Member
Posts: 110
Joined: Sun Feb 20, 2011 2:01 pm

Re: Leaving long mode

Post 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.
Whoever said you can't do OS development on Windows?
https://github.com/ChaiSoft/ChaiOS
Post Reply