Hi Laszlo
Thanks for the investigation.

Maybe I am wrong, but I would like to point out this piece of code works fine 
on IA real platform.
We also have other piece of code to jump from 64bit to 16bit directly, which 
works fine also.

I just checked IA32 manual, for far return, detail below: (error handling are 
removed)

Would you please help to check if the emulator follow IA32 manual to do the 
mode switch?

===================================
(* IA-32e Mode *)
IF (PE = 1 and VM = 0 and IA32_EFER.LMA = 1) and instruction = far return
  GOTO IA-32E-MODE-RETURN-SAME-PRIVILEGE-LEVEL

IA-32E-MODE-RETURN-SAME-PRIVILEGE-LEVEL:
IF OperandSize = 32
  THEN
    EIP <- Pop();
    CS <- Pop(); (* 32-bit pop, high-order 16 bits discarded *)
  ELSE
    IF OperandSize = 16
      THEN
        EIP <- Pop();
        EIP <- EIP AND 0000FFFFH;
        CS <- Pop(); (* 16-bit pop *)
      ELSE (* OperandSize = 64 *)
        RIP <- Pop();
        CS <- Pop(); (* 64-bit pop, high-order 48 bits discarded *)
    FI;
FI;
IF instruction has immediate operand
  THEN (* Release parameters from stack *)
    IF StackAddressSize = 32
      THEN
        ESP <- ESP + SRC;
      ELSE
        IF StackAddressSize = 16
          THEN
            SP <- SP + SRC;
          ELSE (* StackAddressSize = 64 *)
            RSP <- RSP + SRC;
        FI;
    FI;
FI;
===================================

-----Original Message-----
From: Laszlo Ersek [mailto:ler...@redhat.com] 
Sent: Thursday, December 05, 2013 5:47 AM
To: edk2-devel@lists.sourceforge.net
Cc: Yao, Jiewen
Subject: Re: [edk2] please clarify "PcdDxeIplSwitchToLongMode"

On 12/04/13 06:46, Laszlo Ersek wrote:

>>> OVMF S3 resume [...] The place where it unexpectedly reboots now is 
>>> the following sequence (from
>>> MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/S3Asm.S):
>>>
>>> ASM_GLOBAL ASM_PFX(AsmTransferControl)
>>> ASM_PFX(AsmTransferControl):
>>>     # rcx S3WakingVector    :DWORD
>>>     # rdx AcpiLowMemoryBase :DWORD
>>>     lea   _AsmTransferControl_al_0000(%rip), %eax
>>>     movq  $0x2800000000, %r8
>>>     orq   %r8, %rax
>>>     pushq %rax
>>>     shrd  $20, %ecx, %ebx
>>>     andl  $0x0f, %ecx
>>>     movw  %cx, %bx
>>>     movl  %ebx, jmp_addr(%rip)
>>>     lret                         <-------------- here
>>> _AsmTransferControl_al_0000:
>>>     .byte    0x0b8, 0x30, 0      # mov ax, 30h as selector
>>>     movl  %eax, %ds
>>>     movl  %eax, %es
>>>     movl  %eax, %fs
>>>     movl  %eax, %gs
>>>     movl  %eax, %ss
>>>     movq  %cr0, %rax
>>>     movq  %cr4, %rbx
>>>     .byte    0x66
>>>     andl  $0x7ffffffe, %eax
>>>     andb  $0xdf, %bl
>>>     movq  %rax, %cr0
>>>     .byte    0x66
>>>     movl  $0x0c0000080, %ecx
>>>     rdmsr
>>>     andb  $0xfe, %ah
>>>     wrmsr
>>>     movq  %rbx, %cr4
>>>     .byte    0x0ea              # jmp far jmp_addr
>>> jmp_addr:
>>>     .long    0
>>>
>>> The idea is to simply follow through AsmTransferControl_al_0000, but 
>>> flip CS to 0x28 at that point.

[...]

>>> The GDT entry at/for 0x28 is
>>>
>>>   UINT32  LimitLow    : 16  == 0xFFFF
>>>   UINT32  BaseLow     : 16  == 0
>>>   UINT32  BaseMid     : 8   == 0
>>>   UINT32  Type        : 4   == 0xB /* = Code, Execute/Read, accessed */
>>>   UINT32  System      : 1   == 1   /* = code or data */
>>>   UINT32  Dpl         : 2   == 0
>>>   UINT32  Present     : 1   == 1
>>>   UINT32  LimitHigh   : 4   == 0xF
>>>   UINT32  Software    : 1   == 0   /* this bit is freely usable */
>>>   UINT32  Reserved    : 1   == 0   /* = exec'd in compat (32bit) mode */
>>>   UINT32  DefaultSize : 1   == 0   /* = 16-bit def. addr & op size */
>>>   UINT32  Granularity : 1   == 1   /* = limit in 4KB units */
>>>   UINT32  BaseHigh    : 8   == 0
> 
> (note: the GDT entries are in
> "UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume.c")

The problem is the DefaultSize flag (== 0 in the above descriptor). The 
descriptor at 0x10 only differs in the DefaultSize bit (== 1 in there), and the 
lret instruction works with that as expected.

When the lret is executed, the processor is still in IA-32e mode and apparently 
doesn't accept a code segment where the default address and operand size is 
16-bit. We probably need to clear only the L flag at first (64-bit code 
segment, called "Reserved" above), by selecting the
0x10 descriptor. Then, in 32-bit code, bring the CPU out of IA-32e (the 
assembly in the rest of AsmTransferControl seems to aim at that), and then we 
can probably select the 0x28 descriptor, changing the default operand/address 
size to 16 bits. I guess.

Laszlo

------------------------------------------------------------------------------
Sponsored by Intel(R) XDK 
Develop, test and display web and hybrid apps with a single code base.
Download it for free now!
http://pubads.g.doubleclick.net/gampad/clk?id=111408631&iu=/4140/ostg.clktrk
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/edk2-devel

Reply via email to