Skip to contentSkip to conversation

apparent KVM problem with LRET in TianoCore S3 resume trampoline

edk2 Laszlo Ersek 5 participants 16 messages Dec 5, 2013 – Dec 8, 2013 KVM

lore
Share & tools
lore
#0 lore raw

Hi,

I'm working on S3 suspend/resume in OVMF. The problem is that I'm getting an unexpected guest reboot for code (LRET) that works on physical hardware. I tried to trace the problem with ftrace, but I didn't get any mentions of em_ret_far(). (Maybe I was looking in the wrong place.)

Please find the the assembly-language "trampoline" that is invoked (in 64-bit mode) with the 16-bit real mode resume vector placed in "rcx" (EFIAPI calling convention). The excerpt is from the edk2 tree, "MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/S3Asm.S".

I'm annotating the source code to the right -- please excuse my audacity as I know you all eat assembly for breakfast, but maybe it will speed up your processing. (Or perhaps I'll sneakily confuse you with my errors :))

  ASM_GLOBAL ASM_PFX(AsmTransferControl)                     #
  ASM_PFX(AsmTransferControl):                               #
      # rcx S3WakingVector    :DWORD                         # ecx: ........ ....PPPP QQQQQQQQ RRRRSSSS
      # rdx AcpiLowMemoryBase :DWORD                         #
      lea   _AsmTransferControl_al_0000(%rip), %eax          # pushing $0x28 for CS
      movq  $0x2800000000, %r8                               # and address of
      orq   %r8, %rax                                        # _AsmTransferControl_al_0000
      pushq %rax                                             # for RIP
      shrd  $20, %ecx, %ebx                                  # ebx: PPPPQQQQ QQQQRRRR SSSS.... ........
      andl  $0x0f, %ecx                                      # ecx: 00000000 00000000 00000000 0000SSSS
      movw  %cx, %bx                                         # ebx: PPPPQQQQ QQQQRRRR 00000000 0000SSSS
      movl  %ebx, jmp_addr(%rip)                             # stores vector as 16-bit segment:offset pair
  xxxx:                                                      # -- my own loop
      jmp xxxx                                               # -- for debugging
      lret                                                   # (*) TRIGGERS REBOOT
  _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 # preps for turning off Paging and Protection Enable
andb $0xdf, %bl # preps for turning off PAE
movq %rax, %cr0 # Paging and PE off
.byte 0x66 # (**)
movl $0x0c0000080, %ecx #
rdmsr #
andb $0xfe, %ah #
wrmsr # IA-32e Mode Enable off
movq %rbx, %cr4 # PAE off
.byte 0x0ea # jmp far jmp_addr #
jmp_addr: #
.long 0 # PPPPQQQQ QQQQRRRR:SSSS

The small loop at xxxx is my debug loop. The "lret" instruction right after (marked with (*)) triggers a reboot in KVM.

In the loop, this is the register dump (taken with the "info registers" qemu monitor command):

RAX=000000289c75be2b RBX=000000009a1d0000 RCX=0000000000000000 RDX=0000000000000000 RSI=0000000000000000 RDI=0000000000000000 RBP=000000009f7bafd0 RSP=000000009f7bae30 R8 =0000002800000000 R9 =0000000000000000 R10=00000000008454cd R11=0000000000000000 R12=0000000000000000 R13=0000000000000000 R14=00000000008454c6 R15=0000000000000000 RIP=000000009c75be28 RFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0018 0000000000000000 ffffffff 00c09300 DPL=0 DS [-WA]
CS =0018 0000000000000000 ffffffff 00a09b00 DPL=0 CS64 [-RA]

  SS =0018 0000000000000000 ffffffff 00c09300 DPL=0 DS   [-WA]
  DS =0018 0000000000000000 ffffffff 00c09300 DPL=0 DS   [-WA]
  FS =0018 0000000000000000 ffffffff 00c09300 DPL=0 DS   [-WA]
  GS =0018 0000000000000000 ffffffff 00c09300 DPL=0 DS   [-WA]

LDT=0000 0000000000000000 0000ffff 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT= 0000000000844c80 00000047
IDT= 000000009c01fd60 0000021f
CR0=80000033 CR2=0000000000000000 CR3=0000000000080000 CR4=00000660 DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 DR6=00000000ffff0ff0 DR7=0000000000000400
EFER=0000000000000500
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000 FPR2=0000000000000000 0000 FPR3=0000000000000000 0000 FPR4=0000000000000000 0000 FPR5=0000000000000000 0000 FPR6=0000000000000000 0000 FPR7=0000000000000000 0000 XMM00=00000000000000000000000000000000 XMM01=00000000000000000000000000000000 XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000 XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000 XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000 XMM08=00000000000000000000000000000000 XMM09=00000000000000000000000000000000 XMM10=00000000000000000000000000000000 XMM11=00000000000000000000000000000000 XMM12=00000000000000000000000000000000 XMM13=00000000000000000000000000000000 XMM14=00000000000000000000000000000000 XMM15=00000000000000000000000000000000

Right before the function call in the C source, I also read the CS register, the GDTR, and the GDT entries (please excuse the long lines):

S3ResumeBootOs: CS=0x0018
S3ResumeBootOs: Desc.Limit=0x0047

  0x0000: 0x0000000000000000: Base=0x00000000 Limit=0x00000 Type=0x0 (D RO       ) S=0x0 (system   ) DPL=0x0 Present=0 Avail=0 64-bitC=0 D/B=0 LimitGran=0x0 (1B )
  0x0008: 0x0000000000000000: Base=0x00000000 Limit=0x00000 Type=0x0 (D RO       ) S=0x0 (system   ) DPL=0x0 Present=0 Avail=0 64-bitC=0 D/B=0 LimitGran=0x0 (1B )
  0x0010: 0x00CF9B000000FFFF: Base=0x00000000 Limit=0xFFFFF Type=0xB (C ER A     ) S=0x1 (code/data) DPL=0x0 Present=1 Avail=0 64-bitC=0 D/B=1 LimitGran=0x1 (4KB)
  0x0018: 0x00CF93000000FFFF: Base=0x00000000 Limit=0xFFFFF Type=0x3 (D RW A     ) S=0x1 (code/data) DPL=0x0 Present=1 Avail=0 64-bitC=0 D/B=1 LimitGran=0x1 (4KB)
  0x0020: 0x0000000000000000: Base=0x00000000 Limit=0x00000 Type=0x0 (D RO       ) S=0x0 (system   ) DPL=0x0 Present=0 Avail=0 64-bitC=0 D/B=0 LimitGran=0x0 (1B )
  0x0028: 0x008F9B000000FFFF: Base=0x00000000 Limit=0xFFFFF Type=0xB (C ER A     ) S=0x1 (code/data) DPL=0x0 Present=1 Avail=0 64-bitC=0 D/B=0 LimitGran=0x1 (4KB)
  0x0030: 0x008F93000000FFFF: Base=0x00000000 Limit=0xFFFFF Type=0x3 (D RW A     ) S=0x1 (code/data) DPL=0x0 Present=1 Avail=0 64-bitC=0 D/B=0 LimitGran=0x1 (4KB)
  0x0038: 0x00AF9B000000FFFF: Base=0x00000000 Limit=0xFFFFF Type=0xB (C ER A     ) S=0x1 (code/data) DPL=0x0 Present=1 Avail=0 64-bitC=1 D/B=0 LimitGran=0x1 (4KB)
  0x0040: 0x0000000000000000: Base=0x00000000 Limit=0x00000 Type=0x0 (D RO       ) S=0x0 (system   ) DPL=0x0 Present=0 Avail=0 64-bitC=0 D/B=0 LimitGran=0x0 (1B )

The purpose of the LRET would be (by way of selecting CS=0x0028) to select compat mode code execution (64-bitC=0), and to turn off the D/B bit, ie. set default address & operand size to 16 bits. (This is the justification for the 0x66 prefixes I marked with (**) in the assembly.)

Interesting things:
- CS is currently 0x18, which describes a data segment. Strange (but works).
- If I select 0x38 or 0x10 as CS, then the LRET works fine (as in, I reach the target label.)
- The offending segment descriptor (at 0x28) differs from these other working segment descriptors in the following small details:

- shared properties:

    Base=0x00000000
    Limit=0xFFFFF
    Type=0xB (C ER A     )
    S=0x1 (code/data)
    DPL=0x0
    Present=1
    Avail=0
    LimitGran=0x1 (4KB)

- different properties:

    0x0010: 0x00CF9B000000FFFF: 64-bitC=0 D/B=1  works
    0x0028: 0x008F9B000000FFFF: 64-bitC=0 D/B=0  reboots
    0x0038: 0x00AF9B000000FFFF: 64-bitC=1 D/B=0  works

That is:
- if I let 64-bit mode execution enabled (64-bitC=1, desc 0x38), the lret works.
- If I switch to compat mode execution (64-bitC=0, desc 0x10), *and* keep the default addr/op size 32 bits, the lret still works.
- If I switch to compat mode execution (64-bitC=0, desc 0x28), but also change the default addr/op size to 16-bits, then the lret reboots the guest in KVM (but works on physical hardware).

Host:
- Intel(R) Core(TM) i7 CPU M 620 @ 2.67GHz

- KVM parameters (all left at default):
emulate_invalid_guest_state: Y
enable_apicv: N
enable_shadow_vmcs: N
ept: Y
eptad: N
fasteoi: Y
flexpriority: Y
nested: N
ple_gap: 0
ple_window: 4096
unrestricted_guest: Y
vmm_exclusive: Y
vpid: Y

- KVM: 3.11
- qemu: at 7dc65c02 ("Open 2.0 development tree")
- guest RAM size: 2560 MB (0xA0000000 bytes)

I'm also pasting an objdump disassembly of the routine below (compiled without my small debug loop). The disassembly is kind of garbled (eg. the movabs and the 32-bit code), but the hexdump might be helpful.

Please keep me CC'd, I'm not subscribed.

Thank you!
Laszlo

0000000000000000 <AsmTransferControl>:

   0:   8d 05 1f 00 00 00       lea    0x1f(%rip),%eax        # 25 <_AsmTransferControl_al_0000>
   6:   49 b8 00 00 00 00 28    movabs $0x2800000000,%r8
   d:   00 00 00
  10:   4c 09 c0                or     %r8,%rax
  13:   50                      push   %rax
  14:   0f ac cb 14             shrd   $0x14,%ecx,%ebx
  18:   83 e1 0f                and    $0xf,%ecx
  1b:   66 89 cb                mov    %cx,%bx
  1e:   89 1d 31 00 00 00       mov    %ebx,0x31(%rip)        # 55 <jmp_addr>
  24:   cb                      lret

0000000000000025 <_AsmTransferControl_al_0000>:

  25:   b8 30 00 8e d8          mov    $0xd88e0030,%eax
  2a:   8e c0                   mov    %eax,%es
  2c:   8e e0                   mov    %eax,%fs
  2e:   8e e8                   mov    %eax,%gs
  30:   8e d0                   mov    %eax,%ss
  32:   0f 20 c0                mov    %cr0,%rax
  35:   0f 20 e3                mov    %cr4,%rbx
  38:   66 25 fe ff             and    $0xfffe,%ax
  3c:   ff                      (bad)
  3d:   7f 80                   jg     ffffffffffffffbf <L1+0xfffffffffffffeb8>
  3f:   e3 df                   jrcxz  20 <AsmTransferControl+0x20>
  41:   0f 22 c0                mov    %rax,%cr0
  44:   66 b9 80 00             mov    $0x80,%cx
  48:   00 c0                   add    %al,%al
  4a:   0f 32                   rdmsr
  4c:   80 e4 fe                and    $0xfe,%ah
  4f:   0f 30                   wrmsr
  51:   0f 22 e3                mov    %rbx,%cr4
  54:   ea                      (bad)

0000000000000055 <jmp_addr>:
55: 00 00 add %al,(%rax)

        ...

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

#1 lore raw

Small addition -- apologies for the self-followup:

Quoted 2 lines · Laszlo Ersek

I tried to trace the problem with ftrace, but I didn't get any mentions of em_ret_far(). (Maybe I was looking in the wrong place.)

I applied the following small patch (to the original code):

MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/S3Asm.S +2 -2
index e59fd04..daa4f7e 100644
@@ -18,8 +18,8 @@ ASM_GLOBAL ASM_PFX(AsmTransferControl)
 ASM_PFX(AsmTransferControl):
     # rcx S3WakingVector    :DWORD
     # rdx AcpiLowMemoryBase :DWORD
-    lea   _AsmTransferControl_al_0000(%rip), %eax
-    movq  $0x2800000000, %r8
+    lea   AsmTransferControl(%rip), %eax
+    movq  $0x3800000000, %r8
     orq   %r8, %rax
     pushq %rax
     shrd  $20, %ecx, %ebx

This turns the code right under AsmTransferControl into a working, 64-bit mode loop. (Recall that 0x38 selects a descriptor that has the L ("64-bitC") bit set:

Quoted 1 line

0x0038: 0x00AF9B000000FFFF: Base=0x00000000 Limit=0xFFFFF Type=0xB (C ER A ) S=0x1 (code/data) DPL=0x0 Present=1 Avail=0 64-bitC=1 D/B=0 LimitGran=0x1 (4KB)

)

While this was spinning (I checked the RIP several times with the qemu monitor and it was alternating between a few close values -- ie. not stuck), I ran trace-cmd. The report seems to confirm that the lret is not emulated, because the only lines I'm seeing are:

 qemu-system-x86-3901  [001] 38939.599663: kvm_exit:             reason EXTERNAL_INTERRUPT rip 0x9c75be0a info 0 800000ef
 qemu-system-x86-3901  [001] 38939.599684: kvm_entry:            vcpu 0

repeated infinitely. The rip varies between a few close values,

    458 rip 0x9c75be04
    313 rip 0x9c75be0a
      5 rip 0x9c75be17
      4 rip 0x9c75be18
      3 rip 0x9c75be22
      8 rip 0x9c75be28

Thanks again and sorry for the noise.
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

maintainer
#2 lore raw
In reply to Laszlo Ersek

Il 05/12/2013 17:12, Laszlo Ersek ha scritto:

Quoted 6 lines

Hi,

I'm working on S3 suspend/resume in OVMF. The problem is that I'm getting an unexpected guest reboot for code (LRET) that works on physical hardware. I tried to trace the problem with ftrace, but I didn't get any mentions of em_ret_far(). (Maybe I was looking in the wrong place.)

What does ftrace say anyway?

Quoted 4 lines

Please find the the assembly-language "trampoline" that is invoked (in 64-bit mode) with the 16-bit real mode resume vector placed in "rcx" (EFIAPI calling convention). The excerpt is from the edk2 tree, "MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/S3Asm.S".

Can you send me a pointer to a git tree or, even better, an OVMF.fd file + instructions on how to trigger the problem?

Quoted 15 lines

- shared properties:

    Base=0x00000000
    Limit=0xFFFFF
    Type=0xB (C ER A     )
    S=0x1 (code/data)
    DPL=0x0
    Present=1
    Avail=0
    LimitGran=0x1 (4KB)

- different properties:

    0x0010: 0x00CF9B000000FFFF: 64-bitC=0 D/B=1  works
    0x0028: 0x008F9B000000FFFF: 64-bitC=0 D/B=0  reboots
    0x0038: 0x00AF9B000000FFFF: 64-bitC=1 D/B=0  works
Quoted 5 lines

That is:
- if I let 64-bit mode execution enabled (64-bitC=1, desc 0x38), the lret works.
- If I switch to compat mode execution (64-bitC=0, desc 0x10), *and* keep the default addr/op size 32 bits, the lret still works.

Perhaps you could try switching to 32-bit mode first, then disable paging, then jump to 16-bit mode. Like this (untested):

MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/S3Asm.S +21 -13
index e59fd04..d1cac9d 100644
@@ -19,7 +19,7 @@ ASM_PFX(AsmTransferControl):
     # rcx S3WakingVector    :DWORD
     # rdx AcpiLowMemoryBase :DWORD
     lea   _AsmTransferControl_al_0000(%rip), %eax 
-    movq  $0x2800000000, %r8 
+    movq  $0x1000000000, %r8 
     orq   %r8, %rax
     pushq %rax
     shrd  $20, %ecx, %ebx
@@ -28,24 +28,32 @@ ASM_PFX(AsmTransferControl):
     movl  %ebx, jmp_addr(%rip) 
     lret
 _AsmTransferControl_al_0000:
+    # Old SS should still be okay?
+    addl  _AsmTransferControl_al_0001-_AsmTransferControl_al_0000, %eax
+    pushl $0x28
+    pushl %eax
+    movq  %cr0, %rax
+    movq  %cr4, %rbx
+    andl  $0x7fffffff, %eax
+    andb  $0xdf, %bl
+    movq  %rax, %cr0             # sets EFER.LMA=0 too, so says Intel
+    movl  $0x0c0000080, %ecx
+    rdmsr
+    andb  $0xfe, %ah             # set EFER.LME=0
+    wrmsr
+    movq  %rbx, %cr4             # only now set CR4.PAE=0
+    lret
+_AsmTransferControl_al_0001:
     .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
+    movl  %cr0, %rax        # Get control register 0
+    .byte 0x66
+    .byte 0x83,0xe0,0xfe    # and    eax, 0fffffffeh  ; Clear PE bit (bit #0)
+    .byte 0xf,0x22,0xc0     # mov    cr0, eax         ; Activate real mode
 
Quoted 3 lines

- If I switch to compat mode execution (64-bitC=0, desc 0x28), but also change the default addr/op size to 16-bits, then the lret reboots the guest in KVM (but works on physical hardware).

Did you try this on physical hardware, or just assumed that? :)

Paolo

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

#3 lore raw
Quoted 9 lines · Paolo Bonzini

Il 05/12/2013 17:12, Laszlo Ersek ha scritto:

Quoted 6 lines

Hi,

I'm working on S3 suspend/resume in OVMF. The problem is that I'm getting an unexpected guest reboot for code (LRET) that works on physical hardware. I tried to trace the problem with ftrace, but I didn't get any mentions of em_ret_far(). (Maybe I was looking in the wrong place.)

What does ftrace say anyway?

(pls. see in the next msg I sent)

Quoted 8 lines
Quoted 5 lines

Please find the the assembly-language "trampoline" that is invoked (in 64-bit mode) with the 16-bit real mode resume vector placed in "rcx" (EFIAPI calling convention). The excerpt is from the edk2 tree, "MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/S3Asm.S".

Can you send me a pointer to a git tree or, even better, an OVMF.fd file + instructions on how to trigger the problem?

http://people.redhat.com/~lersek/ovmf_s3_lret/

I use a stock F19 guest, with the default systemd target set to multi-user. Then I login as root at the console, and issue "pm-suspend". Once the guest is suspended, I type

virsh qemu-monitor-command ovmf.f19 --hmp system_wakeup

on the host.

At this point the guest starts spinning (visible eg. in the virt-manager CPU usage chart), and the OVMF debug log (written to the qemu debug console) is continuously growing. It always takes the same path: selects the S3 boot path due to the 0xFE byte at 0xF in CMOS, progresses to the trampoline, and resets.

Quoted 16 lines
Quoted 16 lines

- shared properties:

    Base=0x00000000
    Limit=0xFFFFF
    Type=0xB (C ER A     )
    S=0x1 (code/data)
    DPL=0x0
    Present=1
    Avail=0
    LimitGran=0x1 (4KB)

- different properties:

    0x0010: 0x00CF9B000000FFFF: 64-bitC=0 D/B=1  works
    0x0028: 0x008F9B000000FFFF: 64-bitC=0 D/B=0  reboots
    0x0038: 0x00AF9B000000FFFF: 64-bitC=1 D/B=0  works
Quoted 8 lines
Quoted 5 lines

That is:
- if I let 64-bit mode execution enabled (64-bitC=1, desc 0x38), the lret works.
- If I switch to compat mode execution (64-bitC=0, desc 0x10), *and* keep the default addr/op size 32 bits, the lret still works.

Perhaps you could try switching to 32-bit mode first, then disable paging, then jump to 16-bit mode. Like this (untested):

I had something like this in mind (I even mentioned it on edk2-devel
<http://thread.gmane.org/gmane.comp.bios.tianocore.devel/5297/focus=5331>),
but didn't know how to implement it. There are at least 6 factors in play here:
- the L bit in the segment descriptor,
- the D/B bit in the segment descriptor,
- Paging,
- Protection Enable,
- IA-32e Mode Enable,
- PAE.

That's (almost) 6! orderings to test :)

So thanks a lot for suggesting a patch, I'll try to play with it.

Quoted 61 lines
MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/S3Asm.S +21 -13
index e59fd04..d1cac9d 100644
@@ -19,7 +19,7 @@ ASM_PFX(AsmTransferControl):
     # rcx S3WakingVector    :DWORD
     # rdx AcpiLowMemoryBase :DWORD
     lea   _AsmTransferControl_al_0000(%rip), %eax 
-    movq  $0x2800000000, %r8 
+    movq  $0x1000000000, %r8 
     orq   %r8, %rax
     pushq %rax
     shrd  $20, %ecx, %ebx
@@ -28,24 +28,32 @@ ASM_PFX(AsmTransferControl):
     movl  %ebx, jmp_addr(%rip) 
     lret
 _AsmTransferControl_al_0000:
+    # Old SS should still be okay?
+    addl  _AsmTransferControl_al_0001-_AsmTransferControl_al_0000, %eax
+    pushl $0x28
+    pushl %eax
+    movq  %cr0, %rax
+    movq  %cr4, %rbx
+    andl  $0x7fffffff, %eax
+    andb  $0xdf, %bl
+    movq  %rax, %cr0             # sets EFER.LMA=0 too, so says Intel
+    movl  $0x0c0000080, %ecx
+    rdmsr
+    andb  $0xfe, %ah             # set EFER.LME=0
+    wrmsr
+    movq  %rbx, %cr4             # only now set CR4.PAE=0
+    lret
+_AsmTransferControl_al_0001:
     .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
+    movl  %cr0, %rax        # Get control register 0
+    .byte 0x66
+    .byte 0x83,0xe0,0xfe    # and    eax, 0fffffffeh  ; Clear PE bit (bit #0)
+    .byte 0xf,0x22,0xc0     # mov    cr0, eax         ; Activate real mode
 
Quoted 3 lines

- If I switch to compat mode execution (64-bitC=0, desc 0x28), but also change the default addr/op size to 16-bits, then the lret reboots the guest in KVM (but works on physical hardware).

Quoted 1 line

Did you try this on physical hardware, or just assumed that? :)

I was told on edk2-devel O:)

http://thread.gmane.org/gmane.comp.bios.tianocore.devel/5297/focus=5333

Thanks!
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

maintainer
#4 lore raw

Il 05/12/2013 19:29, Laszlo Ersek ha scritto:

Quoted 12 lines · Paolo Bonzini
Quoted 9 lines

Il 05/12/2013 17:12, Laszlo Ersek ha scritto:

Quoted 6 lines

Hi,

I'm working on S3 suspend/resume in OVMF. The problem is that I'm getting an unexpected guest reboot for code (LRET) that works on physical hardware. I tried to trace the problem with ftrace, but I didn't get any mentions of em_ret_far(). (Maybe I was looking in the wrong place.)

What does ftrace say anyway?

(pls. see in the next msg I sent)

Actually I meant the ftrace without any patches.

Thanks to your binary I now reproduced the issue and it looks like the 64-bit->16-bit switch works:

 qemu-system-x86-4081  [001] 62650.335040: kvm_exit:             reason CR_ACCESS rip 0x3cf7ae45 info 0 0
 qemu-system-x86-4081  [001] 62650.335041: kvm_cr:               cr_write 0 = 0x32
 qemu-system-x86-4081  [001] 62650.335046: kvm_entry:            vcpu 0

This is the "mov %rax, %cr0". PE and PG are turned off.

 qemu-system-x86-4081  [001] 62650.335047: kvm_exit:             reason MSR_READ rip 0x3cf7ae4e info 0 0
 qemu-system-x86-4081  [001] 62650.335048: kvm_msr:              msr_read c0000080 = 0x100
 qemu-system-x86-4081  [001] 62650.335048: kvm_entry:            vcpu 0
 qemu-system-x86-4081  [001] 62650.335048: kvm_exit:             reason MSR_WRITE rip 0x3cf7ae53 info 0 0
 qemu-system-x86-4081  [001] 62650.335049: kvm_msr:              msr_write c0000080 = 0x0
 qemu-system-x86-4081  [001] 62650.335050: kvm_entry:            vcpu 0

LME is turned off.

 qemu-system-x86-4081  [001] 62650.335050: kvm_exit:             reason CR_ACCESS rip 0x3cf7ae55 info 304 0
 qemu-system-x86-4081  [001] 62650.335050: kvm_cr:               cr_write 4 = 0x640
 qemu-system-x86-4081  [001] 62650.335053: kvm_entry:            vcpu 0

PAE is turned off.

 qemu-system-x86-4081  [001] 62650.335054: kvm_exit:             reason CR_ACCESS rip 0x11e6 info 0 0
 qemu-system-x86-4081  [001] 62650.335054: kvm_cr:               cr_write 0 = 0x33
 qemu-system-x86-4081  [001] 62650.335054: kvm_entry:            vcpu 0
	Here we're already in real mode.  The weird RIP is explained by
	the first few bytes after the FACS resume vector:

		0x9a1d:0000:  cli    
		0x9a1d:0001:  cld    
		0x9a1d:0002:  ljmp   $9900,$11d7

	9900:11d7 is the same physical address as 9a1d:0007.  Fast forward a bit:
 qemu-system-x86-4081  [001] 62650.335071: kvm_exit:             reason CR_ACCESS rip 0x9aec7 info 0 0
 qemu-system-x86-4081  [001] 62650.335071: kvm_cr:               cr_write 0 = 0x80010001
 qemu-system-x86-4081  [001] 62650.335074: kvm_entry:            vcpu 0
 qemu-system-x86-4081  [001] 62650.335076: kvm_exit:             reason TRIPLE_FAULT rip 0x0 info 0 0

Disassembling mixed 16-/32-/64-bit is a pain, so I ran QEMU with -no-shutdown -no-reboot and dumped the memory with

	(qemu) dump-guest-memory tramp.dmp 0x90000 0x10000

Lets look at the rest of the trace now. After the previous cr0 access we have:

 qemu-system-x86-4081  [001] 62650.335055: kvm_exit:             reason CR_ACCESS rip 0x11fa info 0 0
 qemu-system-x86-4081  [001] 62650.335055: kvm_cr:               cr_write 0 = 0x32
 qemu-system-x86-4081  [001] 62650.335055: kvm_entry:            vcpu 0

It gets out to real mode again. No idea why. It does some setup.

 qemu-system-x86-4081  [001] 62650.335056: kvm_exit:             reason MSR_WRITE rip 0x1258 info 0 0
 qemu-system-x86-4081  [001] 62650.335056: kvm_msr:              msr_write 1a0 = 0x1
 qemu-system-x86-4081  [001] 62650.335057: kvm_entry:            vcpu 0
 qemu-system-x86-4081  [001] 62650.335057: kvm_exit:             reason WBINVD rip 0x1001 info 0 0
 qemu-system-x86-4081  [001] 62650.335057: kvm_entry:            vcpu 0
 qemu-system-x86-4081  [001] 62650.335058: kvm_exit:             reason CPUID rip 0x1073 info 0 0
 qemu-system-x86-4081  [001] 62650.335058: kvm_cpuid:            func 0 rax 4 rbx 756e6547 rcx 6c65746e rdx 49656e69
 qemu-system-x86-4081  [001] 62650.335059: kvm_entry:            vcpu 0
 qemu-system-x86-4081  [001] 62650.335059: kvm_exit:             reason CPUID rip 0x10c0 info 0 0
 qemu-system-x86-4081  [001] 62650.335059: kvm_cpuid:            func 1 rax 663 rbx 800 rcx 80802001 rdx 78bfbfd
 qemu-system-x86-4081  [001] 62650.335059: kvm_entry:            vcpu 0
 qemu-system-x86-4081  [001] 62650.335060: kvm_exit:             reason CPUID rip 0x10ff info 0 0
 qemu-system-x86-4081  [001] 62650.335060: kvm_cpuid:            func 1 rax 663 rbx 800 rcx 80802001 rdx 78bfbfd
 qemu-system-x86-4081  [001] 62650.335060: kvm_entry:            vcpu 0
 qemu-system-x86-4081  [001] 62650.335061: kvm_exit:             reason CPUID rip 0x1117 info 0 0
 qemu-system-x86-4081  [001] 62650.335061: kvm_cpuid:            func 80000000 rax 8000000a rbx 756e6547 rcx 6c65746e rdx 49656e69
 qemu-system-x86-4081  [001] 62650.335061: kvm_entry:            vcpu 0
 qemu-system-x86-4081  [001] 62650.335062: kvm_exit:             reason CPUID rip 0x1127 info 0 0
 qemu-system-x86-4081  [001] 62650.335062: kvm_cpuid:            func 80000001 rax 663 rbx 0 rcx 1 rdx 2191abfd
 qemu-system-x86-4081  [001] 62650.335062: kvm_entry:            vcpu 0
 qemu-system-x86-4081  [001] 62650.335063: kvm_exit:             reason CPUID rip 0x113f info 0 0
 qemu-system-x86-4081  [001] 62650.335063: kvm_cpuid:            func 1 rax 663 rbx 800 rcx 80802001 rdx 78bfbfd
 qemu-system-x86-4081  [001] 62650.335063: kvm_entry:            vcpu 0
 qemu-system-x86-4081  [001] 62650.335064: kvm_exit:             reason CR_ACCESS rip 0x103c info 0 0
 qemu-system-x86-4081  [001] 62650.335064: kvm_cr:               cr_write 0 = 0x1
 qemu-system-x86-4081  [001] 62650.335064: kvm_entry:            vcpu 0

Enabling protected mode:

0009A036 66B801000000 mov eax,0x1
0009A03C 0F22C0 mov cr0,eax
0009A03F 66EA90AE09000800 jmp dword 0x8:0x9ae90

This is a 32-bit selector.

0009AE90 8ED2 mov ss,edx
0009AE92 81C400900900 add esp,0x99000
0009AE98 8EDA mov ds,edx
0009AE9A 8EC2 mov es,edx
0009AE9C 8EE2 mov fs,edx
0009AE9E 8EEA mov gs,edx

 qemu-system-x86-4081  [001] 62650.335065: kvm_exit:             reason CR_ACCESS rip 0x9aea5 info 4 0
 qemu-system-x86-4081  [001] 62650.335065: kvm_cr:               cr_write 4 = 0x6f0
 qemu-system-x86-4081  [001] 62650.335066: kvm_entry:            vcpu 0
	Enabling PAE:

	0009AEA0  A110D00900        mov eax,[0x9d010]
	0009AEA5  0F22E0            mov cr4,eax
 qemu-system-x86-4081  [001] 62650.335067: kvm_exit:             reason CR_ACCESS rip 0x9aead info 3 0
 qemu-system-x86-4081  [001] 62650.335067: kvm_cr:               cr_write 3 = 0x9c000
 qemu-system-x86-4081  [001] 62650.335068: kvm_entry:            vcpu 0
	Setting CR3

	0009AEA8  B800C00900        mov eax,0x9c000
	0009AEAD  0F22D8            mov cr3,eax
 qemu-system-x86-4081  [001] 62650.335068: kvm_exit:             reason MSR_WRITE rip 0x9aec0 info 0 0
 qemu-system-x86-4081  [001] 62650.335070: kvm_msr:              msr_write c0000080 = 0x901
 qemu-system-x86-4081  [001] 62650.335070: kvm_entry:            vcpu 0
	Enabling LME

	0009AEB0  A108D00900        mov eax,[0x9d008]
	0009AEB5  8B150CD00900      mov edx,[dword 0x9d00c]
	0009AEBB  B9800000C0        mov ecx,0xc0000080
	0009AEC0  0F30              wrmsr
 qemu-system-x86-4081  [001] 62650.335071: kvm_exit:             reason CR_ACCESS rip 0x9aec7 info 0 0
 qemu-system-x86-4081  [001] 62650.335071: kvm_cr:               cr_write 0 = 0x80010001
 qemu-system-x86-4081  [001] 62650.335074: kvm_entry:            vcpu 0
	Enabling paging

	0009AEC2  B801000180        mov eax,0x80010001
	0009AEC7  0F22C0            mov cr0,eax

	But before we get here:

	0009AECA  EA30AF09001000    jmp dword 0x10:0x9af30

	... kaboom:

qemu-system-x86-4081 [001] 62650.335076: kvm_exit: reason TRIPLE_FAULT rip 0x0 info 0 0

The page tables are, ahem, crap:

000c000: 6750 fe01 0000 0000 0000 0000 0000 0000  gP..............
000c010: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c040: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c060: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c080: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c090: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................

This is 0x9c000. Strikes any bell?

Paolo

maintainer
#5 lore raw

Il 06/12/2013 13:03, Paolo Bonzini ha scritto:

Quoted 19 lines

The page tables are, ahem, crap:

000c000: 6750 fe01 0000 0000 0000 0000 0000 0000  gP..............
000c010: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c040: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c060: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c080: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c090: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
Quoted 1 line

This is 0x9c000. Strikes any bell?

Uh-oh, actually it's fine and it's my turn to say I didn't look far enough.

That far jump is not where it's failing. It's quite close, but it's at a much more interesting place. Still, indeed it's OVMF's fault.

I tried tracing again, this time without unrestricted_guest. I wanted to see emulation around the time we enter long mode, but it went a little past that place.

We get interesting results anyway because the EPT tables are rebuilt on changes to CR3.PG=0:

 qemu-system-x86-6785  [003] 67184.361164: kvm_exit:             reason EPT_VIOLATION rip 0x9aeca info 81 0
 qemu-system-x86-6785  [003] 67184.361165: kvm_page_fault:       address 9c000 error_code 81

level 4

 qemu-system-x86-6785  [003] 67184.361165: kvm_entry:            vcpu 0
 qemu-system-x86-6785  [003] 67184.361166: kvm_exit:             reason EPT_VIOLATION rip 0x9aeca info 81 0
 qemu-system-x86-6785  [003] 67184.361166: kvm_page_fault:       address 1fe5000 error_code 81
 qemu-system-x86-6785  [003] 67184.361168: kvm_mmu_get_page:     new sp gfn 1e00 0/1 q0 direct --- !pge !nxe root 0 sync

level 3

 qemu-system-x86-6785  [003] 67184.361169: kvm_entry:            vcpu 0
 qemu-system-x86-6785  [003] 67184.361169: kvm_exit:             reason EPT_VIOLATION rip 0x9aeca info 81 0
 qemu-system-x86-6785  [003] 67184.361169: kvm_page_fault:       address 1fe6000 error_code 81

level 2

 qemu-system-x86-6785  [003] 67184.361170: kvm_entry:            vcpu 0
 qemu-system-x86-6785  [003] 67184.361171: kvm_exit:             reason EPT_VIOLATION rip 0x9aeca info 81 0
 qemu-system-x86-6785  [003] 67184.361171: kvm_page_fault:       address 1fe74d0 error_code 81
	level 1 (note 0x4D0 means the 0x4D*2=0x9A-th entry, i.e virtual address 0x9A000)

Another way to get this information would be more simply to attach gdb to the running machine. On one hand setting breakpoints is easy (remember they are virtual addresses, and always use hardware breakpoints with "hb" so that you do not touch memory). But it's complicated to use gdb across mode switches, and we're quite lucky that tracing got us fast what we need!

 qemu-system-x86-6785  [003] 67184.361171: kvm_entry:            vcpu 0
 qemu-system-x86-6785  [003] 67184.361172: kvm_exit:             reason EPT_VIOLATION rip 0xffffffff81000110 info 81 0
 qemu-system-x86-6785  [003] 67184.361172: kvm_page_fault:       address 1c0fff0 error_code 81

level 4

 qemu-system-x86-6785  [003] 67184.361173: kvm_mmu_get_page:     new sp gfn 1c00 0/1 q0 direct --- !pge !nxe root 0 sync
 qemu-system-x86-6785  [003] 67184.361174: kvm_entry:            vcpu 0
 qemu-system-x86-6785  [003] 67184.361174: kvm_exit:             reason EPT_VIOLATION rip 0xffffffff81000110 info 81 0
 qemu-system-x86-6785  [003] 67184.361174: kvm_page_fault:       address 1c10040 error_code 81
	level 3.  We should be here:

	   0xffffffff81000110:	mov    $0x1c0c000,%rax
	   0xffffffff81000117:	mov    $0xa0,%ecx
	   0xffffffff8100011c:	mov    %rcx,%cr4
	   0xffffffff8100011f:	add    0xc12eea(%rip),%rax        # 0xffffffff81c13010
	   0xffffffff81000126:	mov    %rax,%cr3
	   0xffffffff81000129:	mov    $0xffffffff81000132,%rax
	   0xffffffff81000130:	jmpq   *%rax

	(grabbed from "dump-guest-memory -p" and gdb's disass command,
	right after suspending the system)
 qemu-system-x86-6785  [003] 67184.361175: kvm_entry:            vcpu 0
 qemu-system-x86-6785  [003] 67184.361176: kvm_exit:             reason EPT_VIOLATION rip 0xffffffff81000113 info 181 0
 qemu-system-x86-6785  [003] 67184.361176: kvm_page_fault:       address 48 error_code 181
	this rip is bogus!  Let's grab another "dump-guest-memory -p", this
	time after shutdown; remember I'm using -no-shutdown -no-reboot:

	   0xffffffff81000110:	mov    -0x18(%rbp),%eax
	   0xffffffff81000113:	mov    0x48(%rax),%rax
	   0xffffffff81000117:	mov    -0x30(%rbp),%rsi
	   0xffffffff8100011b:	lea    -0x48(%rbp),%rdi
	   0xffffffff8100011f:	mov    -0x18(%rbp),%rcx
	   0xffffffff81000123:	lea    -0x40(%rbp),%rdx
	   0xffffffff81000127:	mov    %rdx,0x28(%rsp)
	   0xffffffff8100012c:	lea    -0x38(%rbp),%rdx

	Uh oh.  Something is corrupting virtual address 0xffffffff81000110,
	which corresponds to physical address 0x1000110.
 qemu-system-x86-6785  [003] 67184.361177: kvm_entry:            vcpu 0
 qemu-system-x86-6785  [003] 67184.361177: kvm_exit:             reason EPT_VIOLATION rip 0xffffffff81000127 info 182 0

This rip is also bogus, no surprise it triple faults soon

 qemu-system-x86-6785  [003] 67184.361177: kvm_page_fault:       address 9e048 error_code 182
 qemu-system-x86-6785  [003] 67184.361178: kvm_entry:            vcpu 0
 qemu-system-x86-6785  [003] 67184.361179: kvm_exit:             reason TRIPLE_FAULT rip 0x0 info 0 0

Still an EDK2 problem. Perhaps you can dump the first few bytes of 0x1000110..0x10011f every time a PEIM is loaded?

Paolo

#6 lore raw

Hi Paolo
I am a little confused here. You said "Still, indeed it's OVMF's fault." and "Still an EDK2 problem." ??????

EDKII BIOS should always create 1:1 mapping virtual-physical address. But I am not clear about OS waking vector. For "EPT_VIOLATION rip 0xffffffff81000110.", is that happen in EDKII BIOS or in OS waking vector?

All in all, I have interesting to know one thing at first:
Is OVMF crash in BIOS before jump to OS waking vector? Or is OVMF crash inside OS waking vector?

Thank you
Yao Jiewen

-----Original Message-----
From: Paolo Bonzini [mailto:[email protected]]
Sent: Friday, December 06, 2013 9:31 PM
Cc: [email protected]; KVM devel mailing list
Subject: Re: [edk2] apparent KVM problem with LRET in TianoCore S3 resume trampoline

Il 06/12/2013 13:03, Paolo Bonzini ha scritto:

Quoted 19 lines

The page tables are, ahem, crap:

000c000: 6750 fe01 0000 0000 0000 0000 0000 0000  gP..............
000c010: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c040: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c060: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c080: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c090: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
Quoted 1 line

This is 0x9c000. Strikes any bell?

Uh-oh, actually it's fine and it's my turn to say I didn't look far enough.

That far jump is not where it's failing. It's quite close, but it's at a much more interesting place. Still, indeed it's OVMF's fault.

I tried tracing again, this time without unrestricted_guest. I wanted to see emulation around the time we enter long mode, but it went a little past that place.

We get interesting results anyway because the EPT tables are rebuilt on changes to CR3.PG=0:

 qemu-system-x86-6785  [003] 67184.361164: kvm_exit:             reason EPT_VIOLATION rip 0x9aeca info 81 0
 qemu-system-x86-6785  [003] 67184.361165: kvm_page_fault:       address 9c000 error_code 81

level 4

 qemu-system-x86-6785  [003] 67184.361165: kvm_entry:            vcpu 0
 qemu-system-x86-6785  [003] 67184.361166: kvm_exit:             reason EPT_VIOLATION rip 0x9aeca info 81 0
 qemu-system-x86-6785  [003] 67184.361166: kvm_page_fault:       address 1fe5000 error_code 81
 qemu-system-x86-6785  [003] 67184.361168: kvm_mmu_get_page:     new sp gfn 1e00 0/1 q0 direct --- !pge !nxe root 0 sync

level 3

 qemu-system-x86-6785  [003] 67184.361169: kvm_entry:            vcpu 0
 qemu-system-x86-6785  [003] 67184.361169: kvm_exit:             reason EPT_VIOLATION rip 0x9aeca info 81 0
 qemu-system-x86-6785  [003] 67184.361169: kvm_page_fault:       address 1fe6000 error_code 81

level 2

 qemu-system-x86-6785  [003] 67184.361170: kvm_entry:            vcpu 0
 qemu-system-x86-6785  [003] 67184.361171: kvm_exit:             reason EPT_VIOLATION rip 0x9aeca info 81 0
 qemu-system-x86-6785  [003] 67184.361171: kvm_page_fault:       address 1fe74d0 error_code 81
	level 1 (note 0x4D0 means the 0x4D*2=0x9A-th entry, i.e virtual address 0x9A000)

Another way to get this information would be more simply to attach gdb to the running machine. On one hand setting breakpoints is easy (remember they are virtual addresses, and always use hardware breakpoints with "hb" so that you do not touch memory). But it's complicated to use gdb across mode switches, and we're quite lucky that tracing got us fast what we need!

 qemu-system-x86-6785  [003] 67184.361171: kvm_entry:            vcpu 0
 qemu-system-x86-6785  [003] 67184.361172: kvm_exit:             reason EPT_VIOLATION rip 0xffffffff81000110 info 81 0
 qemu-system-x86-6785  [003] 67184.361172: kvm_page_fault:       address 1c0fff0 error_code 81

level 4

 qemu-system-x86-6785  [003] 67184.361173: kvm_mmu_get_page:     new sp gfn 1c00 0/1 q0 direct --- !pge !nxe root 0 sync
 qemu-system-x86-6785  [003] 67184.361174: kvm_entry:            vcpu 0
 qemu-system-x86-6785  [003] 67184.361174: kvm_exit:             reason EPT_VIOLATION rip 0xffffffff81000110 info 81 0
 qemu-system-x86-6785  [003] 67184.361174: kvm_page_fault:       address 1c10040 error_code 81
	level 3.  We should be here:

	   0xffffffff81000110:	mov    $0x1c0c000,%rax
	   0xffffffff81000117:	mov    $0xa0,%ecx
	   0xffffffff8100011c:	mov    %rcx,%cr4
	   0xffffffff8100011f:	add    0xc12eea(%rip),%rax        # 0xffffffff81c13010
	   0xffffffff81000126:	mov    %rax,%cr3
	   0xffffffff81000129:	mov    $0xffffffff81000132,%rax
	   0xffffffff81000130:	jmpq   *%rax

	(grabbed from "dump-guest-memory -p" and gdb's disass command,
	right after suspending the system)
 qemu-system-x86-6785  [003] 67184.361175: kvm_entry:            vcpu 0
 qemu-system-x86-6785  [003] 67184.361176: kvm_exit:             reason EPT_VIOLATION rip 0xffffffff81000113 info 181 0
 qemu-system-x86-6785  [003] 67184.361176: kvm_page_fault:       address 48 error_code 181
	this rip is bogus!  Let's grab another "dump-guest-memory -p", this
	time after shutdown; remember I'm using -no-shutdown -no-reboot:

	   0xffffffff81000110:	mov    -0x18(%rbp),%eax
	   0xffffffff81000113:	mov    0x48(%rax),%rax
	   0xffffffff81000117:	mov    -0x30(%rbp),%rsi
	   0xffffffff8100011b:	lea    -0x48(%rbp),%rdi
	   0xffffffff8100011f:	mov    -0x18(%rbp),%rcx
	   0xffffffff81000123:	lea    -0x40(%rbp),%rdx
	   0xffffffff81000127:	mov    %rdx,0x28(%rsp)
	   0xffffffff8100012c:	lea    -0x38(%rbp),%rdx

	Uh oh.  Something is corrupting virtual address 0xffffffff81000110,
	which corresponds to physical address 0x1000110.
 qemu-system-x86-6785  [003] 67184.361177: kvm_entry:            vcpu 0
 qemu-system-x86-6785  [003] 67184.361177: kvm_exit:             reason EPT_VIOLATION rip 0xffffffff81000127 info 182 0

This rip is also bogus, no surprise it triple faults soon

 qemu-system-x86-6785  [003] 67184.361177: kvm_page_fault:       address 9e048 error_code 182
 qemu-system-x86-6785  [003] 67184.361178: kvm_entry:            vcpu 0
 qemu-system-x86-6785  [003] 67184.361179: kvm_exit:             reason TRIPLE_FAULT rip 0x0 info 0 0

Still an EDK2 problem. Perhaps you can dump the first few bytes of 0x1000110..0x10011f every time a PEIM is loaded?

Paolo

------------------------------------------------------------------------------
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
[email protected]
https://lists.sourceforge.net/lists/listinfo/edk2-devel

maintainer
#7 lore raw

Il 06/12/2013 14:46, Yao, Jiewen ha scritto:

Quoted 2 lines

Hi Paolo
I am a little confused here. You said "Still, indeed it's OVMF's fault." and "Still an EDK2 problem." ??????

Sorry for the confusion. I wrote OVMF/EDK2 interchangeably, just to say "not KVM".

Quoted 2 lines

EDKII BIOS should always create 1:1 mapping virtual-physical address. But I am not clear about OS waking vector. For "EPT_VIOLATION rip 0xffffffff81000110.", is that happen in EDKII BIOS or in OS waking vector?

That's after the OS waking vector is invoked. But that memory was corrupted by EDKII/OVMF before the OS waking vector is invoked.

Paolo

Quoted 4 lines

All in all, I have interesting to know one thing at first:
Is OVMF crash in BIOS before jump to OS waking vector? Or is OVMF crash inside OS waking vector?

Thank you

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

#8 lore raw

Good investigation. I really appreciate that.

Now, it seems we need OVMF pkg owner to check when 0x9c000 are corrupted, and why.

Thank you
Yao Jiewen

-----Original Message-----
From: Paolo Bonzini [mailto:[email protected]] On Behalf Of Paolo Bonzini
Sent: Friday, December 06, 2013 10:29 PM
To: Yao, Jiewen
Cc: [email protected]; KVM devel mailing list
Subject: Re: [edk2] apparent KVM problem with LRET in TianoCore S3 resume trampoline

Il 06/12/2013 14:46, Yao, Jiewen ha scritto:

Quoted 2 lines

Hi Paolo
I am a little confused here. You said "Still, indeed it's OVMF's fault." and "Still an EDK2 problem." ??????

Sorry for the confusion. I wrote OVMF/EDK2 interchangeably, just to say "not KVM".

Quoted 2 lines

EDKII BIOS should always create 1:1 mapping virtual-physical address. But I am not clear about OS waking vector. For "EPT_VIOLATION rip 0xffffffff81000110.", is that happen in EDKII BIOS or in OS waking vector?

That's after the OS waking vector is invoked. But that memory was corrupted by EDKII/OVMF before the OS waking vector is invoked.

Paolo

Quoted 4 lines

All in all, I have interesting to know one thing at first:
Is OVMF crash in BIOS before jump to OS waking vector? Or is OVMF crash inside OS waking vector?

Thank you

maintainer
#9 lore raw

Il 06/12/2013 15:47, Yao, Jiewen ha scritto:

Quoted 3 lines

Good investigation. I really appreciate that.

Now, it seems we need OVMF pkg owner to check when 0x9c000 are corrupted, and why.

FWIW it's 0x1000110, not 0x9c000. But everything else is right.

Paolo

#10 lore raw
In reply to Paolo Bonzini

Hi Paolo
Do you mean the OVEM already transfer control to OS Waking vector? System stuck just because the page table in 0x9C000 is corrupt?

Thank you
Yao Jiewen

-----Original Message-----
From: Paolo Bonzini [mailto:[email protected]]
Sent: Friday, December 06, 2013 8:03 PM
To: [email protected]
Cc: KVM devel mailing list
Subject: Re: [edk2] apparent KVM problem with LRET in TianoCore S3 resume trampoline

Il 05/12/2013 19:29, Laszlo Ersek ha scritto:

Quoted 10 lines · Paolo Bonzini
Quoted 9 lines

Il 05/12/2013 17:12, Laszlo Ersek ha scritto:

Quoted 7 lines

Hi,

I'm working on S3 suspend/resume in OVMF. The problem is that I'm getting an unexpected guest reboot for code (LRET) that works on physical hardware. I tried to trace the problem with ftrace, but I didn't get any mentions of em_ret_far(). (Maybe I was looking in the wrong place.)

Quoted 3 lines
Quoted 1 line

What does ftrace say anyway?

(pls. see in the next msg I sent)

Actually I meant the ftrace without any patches.

Thanks to your binary I now reproduced the issue and it looks like the 64-bit->16-bit switch works:

 qemu-system-x86-4081  [001] 62650.335040: kvm_exit:             reason CR_ACCESS rip 0x3cf7ae45 info 0 0
 qemu-system-x86-4081  [001] 62650.335041: kvm_cr:               cr_write 0 = 0x32
 qemu-system-x86-4081  [001] 62650.335046: kvm_entry:            vcpu 0

This is the "mov %rax, %cr0". PE and PG are turned off.

 qemu-system-x86-4081  [001] 62650.335047: kvm_exit:             reason MSR_READ rip 0x3cf7ae4e info 0 0
 qemu-system-x86-4081  [001] 62650.335048: kvm_msr:              msr_read c0000080 = 0x100
 qemu-system-x86-4081  [001] 62650.335048: kvm_entry:            vcpu 0
 qemu-system-x86-4081  [001] 62650.335048: kvm_exit:             reason MSR_WRITE rip 0x3cf7ae53 info 0 0
 qemu-system-x86-4081  [001] 62650.335049: kvm_msr:              msr_write c0000080 = 0x0
 qemu-system-x86-4081  [001] 62650.335050: kvm_entry:            vcpu 0

LME is turned off.

 qemu-system-x86-4081  [001] 62650.335050: kvm_exit:             reason CR_ACCESS rip 0x3cf7ae55 info 304 0
 qemu-system-x86-4081  [001] 62650.335050: kvm_cr:               cr_write 4 = 0x640
 qemu-system-x86-4081  [001] 62650.335053: kvm_entry:            vcpu 0

PAE is turned off.

 qemu-system-x86-4081  [001] 62650.335054: kvm_exit:             reason CR_ACCESS rip 0x11e6 info 0 0
 qemu-system-x86-4081  [001] 62650.335054: kvm_cr:               cr_write 0 = 0x33
 qemu-system-x86-4081  [001] 62650.335054: kvm_entry:            vcpu 0
	Here we're already in real mode.  The weird RIP is explained by
	the first few bytes after the FACS resume vector:

		0x9a1d:0000:  cli    
		0x9a1d:0001:  cld    
		0x9a1d:0002:  ljmp   $9900,$11d7

	9900:11d7 is the same physical address as 9a1d:0007.  Fast forward a bit:
 qemu-system-x86-4081  [001] 62650.335071: kvm_exit:             reason CR_ACCESS rip 0x9aec7 info 0 0
 qemu-system-x86-4081  [001] 62650.335071: kvm_cr:               cr_write 0 = 0x80010001
 qemu-system-x86-4081  [001] 62650.335074: kvm_entry:            vcpu 0
 qemu-system-x86-4081  [001] 62650.335076: kvm_exit:             reason TRIPLE_FAULT rip 0x0 info 0 0

Disassembling mixed 16-/32-/64-bit is a pain, so I ran QEMU with -no-shutdown -no-reboot and dumped the memory with

	(qemu) dump-guest-memory tramp.dmp 0x90000 0x10000

Lets look at the rest of the trace now. After the previous cr0 access we have:

 qemu-system-x86-4081  [001] 62650.335055: kvm_exit:             reason CR_ACCESS rip 0x11fa info 0 0
 qemu-system-x86-4081  [001] 62650.335055: kvm_cr:               cr_write 0 = 0x32
 qemu-system-x86-4081  [001] 62650.335055: kvm_entry:            vcpu 0

It gets out to real mode again. No idea why. It does some setup.

 qemu-system-x86-4081  [001] 62650.335056: kvm_exit:             reason MSR_WRITE rip 0x1258 info 0 0
 qemu-system-x86-4081  [001] 62650.335056: kvm_msr:              msr_write 1a0 = 0x1
 qemu-system-x86-4081  [001] 62650.335057: kvm_entry:            vcpu 0
 qemu-system-x86-4081  [001] 62650.335057: kvm_exit:             reason WBINVD rip 0x1001 info 0 0
 qemu-system-x86-4081  [001] 62650.335057: kvm_entry:            vcpu 0
 qemu-system-x86-4081  [001] 62650.335058: kvm_exit:             reason CPUID rip 0x1073 info 0 0
 qemu-system-x86-4081  [001] 62650.335058: kvm_cpuid:            func 0 rax 4 rbx 756e6547 rcx 6c65746e rdx 49656e69
 qemu-system-x86-4081  [001] 62650.335059: kvm_entry:            vcpu 0
 qemu-system-x86-4081  [001] 62650.335059: kvm_exit:             reason CPUID rip 0x10c0 info 0 0
 qemu-system-x86-4081  [001] 62650.335059: kvm_cpuid:            func 1 rax 663 rbx 800 rcx 80802001 rdx 78bfbfd
 qemu-system-x86-4081  [001] 62650.335059: kvm_entry:            vcpu 0
 qemu-system-x86-4081  [001] 62650.335060: kvm_exit:             reason CPUID rip 0x10ff info 0 0
 qemu-system-x86-4081  [001] 62650.335060: kvm_cpuid:            func 1 rax 663 rbx 800 rcx 80802001 rdx 78bfbfd
 qemu-system-x86-4081  [001] 62650.335060: kvm_entry:            vcpu 0
 qemu-system-x86-4081  [001] 62650.335061: kvm_exit:             reason CPUID rip 0x1117 info 0 0
 qemu-system-x86-4081  [001] 62650.335061: kvm_cpuid:            func 80000000 rax 8000000a rbx 756e6547 rcx 6c65746e rdx 49656e69
 qemu-system-x86-4081  [001] 62650.335061: kvm_entry:            vcpu 0
 qemu-system-x86-4081  [001] 62650.335062: kvm_exit:             reason CPUID rip 0x1127 info 0 0
 qemu-system-x86-4081  [001] 62650.335062: kvm_cpuid:            func 80000001 rax 663 rbx 0 rcx 1 rdx 2191abfd
 qemu-system-x86-4081  [001] 62650.335062: kvm_entry:            vcpu 0
 qemu-system-x86-4081  [001] 62650.335063: kvm_exit:             reason CPUID rip 0x113f info 0 0
 qemu-system-x86-4081  [001] 62650.335063: kvm_cpuid:            func 1 rax 663 rbx 800 rcx 80802001 rdx 78bfbfd
 qemu-system-x86-4081  [001] 62650.335063: kvm_entry:            vcpu 0
 qemu-system-x86-4081  [001] 62650.335064: kvm_exit:             reason CR_ACCESS rip 0x103c info 0 0
 qemu-system-x86-4081  [001] 62650.335064: kvm_cr:               cr_write 0 = 0x1
 qemu-system-x86-4081  [001] 62650.335064: kvm_entry:            vcpu 0

Enabling protected mode:

0009A036 66B801000000 mov eax,0x1
0009A03C 0F22C0 mov cr0,eax
0009A03F 66EA90AE09000800 jmp dword 0x8:0x9ae90

This is a 32-bit selector.

0009AE90 8ED2 mov ss,edx
0009AE92 81C400900900 add esp,0x99000
0009AE98 8EDA mov ds,edx
0009AE9A 8EC2 mov es,edx
0009AE9C 8EE2 mov fs,edx
0009AE9E 8EEA mov gs,edx

 qemu-system-x86-4081  [001] 62650.335065: kvm_exit:             reason CR_ACCESS rip 0x9aea5 info 4 0
 qemu-system-x86-4081  [001] 62650.335065: kvm_cr:               cr_write 4 = 0x6f0
 qemu-system-x86-4081  [001] 62650.335066: kvm_entry:            vcpu 0
	Enabling PAE:

	0009AEA0  A110D00900        mov eax,[0x9d010]
	0009AEA5  0F22E0            mov cr4,eax
 qemu-system-x86-4081  [001] 62650.335067: kvm_exit:             reason CR_ACCESS rip 0x9aead info 3 0
 qemu-system-x86-4081  [001] 62650.335067: kvm_cr:               cr_write 3 = 0x9c000
 qemu-system-x86-4081  [001] 62650.335068: kvm_entry:            vcpu 0
	Setting CR3

	0009AEA8  B800C00900        mov eax,0x9c000
	0009AEAD  0F22D8            mov cr3,eax
 qemu-system-x86-4081  [001] 62650.335068: kvm_exit:             reason MSR_WRITE rip 0x9aec0 info 0 0
 qemu-system-x86-4081  [001] 62650.335070: kvm_msr:              msr_write c0000080 = 0x901
 qemu-system-x86-4081  [001] 62650.335070: kvm_entry:            vcpu 0
	Enabling LME

	0009AEB0  A108D00900        mov eax,[0x9d008]
	0009AEB5  8B150CD00900      mov edx,[dword 0x9d00c]
	0009AEBB  B9800000C0        mov ecx,0xc0000080
	0009AEC0  0F30              wrmsr
 qemu-system-x86-4081  [001] 62650.335071: kvm_exit:             reason CR_ACCESS rip 0x9aec7 info 0 0
 qemu-system-x86-4081  [001] 62650.335071: kvm_cr:               cr_write 0 = 0x80010001
 qemu-system-x86-4081  [001] 62650.335074: kvm_entry:            vcpu 0
	Enabling paging

	0009AEC2  B801000180        mov eax,0x80010001
	0009AEC7  0F22C0            mov cr0,eax

	But before we get here:

	0009AECA  EA30AF09001000    jmp dword 0x10:0x9af30

	... kaboom:

qemu-system-x86-4081 [001] 62650.335076: kvm_exit: reason TRIPLE_FAULT rip 0x0 info 0 0

The page tables are, ahem, crap:

000c000: 6750 fe01 0000 0000 0000 0000 0000 0000  gP..............
000c010: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c040: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c060: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c080: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c090: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................

This is 0x9c000. Strikes any bell?

Paolo

------------------------------------------------------------------------------
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
[email protected]
https://lists.sourceforge.net/lists/listinfo/edk2-devel

#11 lore raw
In reply to Paolo Bonzini
Quoted 10 lines · Paolo Bonzini

Il 05/12/2013 19:29, Laszlo Ersek ha scritto:

Quoted 9 lines · Paolo Bonzini
Quoted 8 lines

Il 05/12/2013 17:12, Laszlo Ersek ha scritto:

Quoted 6 lines

Hi,

I'm working on S3 suspend/resume in OVMF. The problem is that I'm getting an unexpected guest reboot for code (LRET) that works on physical hardware. I tried to trace the problem with ftrace, but I didn't get any mentions of em_ret_far(). (Maybe I was looking in the wrong place.)

Quoted 8 lines
Quoted 3 lines
Quoted 1 line

What does ftrace say anyway?

(pls. see in the next msg I sent)

Actually I meant the ftrace without any patches.

Thanks to your binary I now reproduced the issue and it looks like the 64-bit->16-bit switch works:

Thank you for spending (apparently more than a little) time on this!

Quoted 6 lines
 qemu-system-x86-4081  [001] 62650.335040: kvm_exit:             reason CR_ACCESS rip 0x3cf7ae45 info 0 0
 qemu-system-x86-4081  [001] 62650.335041: kvm_cr:               cr_write 0 = 0x32
 qemu-system-x86-4081  [001] 62650.335046: kvm_entry:            vcpu 0

This is the "mov %rax, %cr0". PE and PG are turned off.

I'm surprised by this result. The instruction you refer to is below "_AsmTransferControl_al_0000" (in the original, unpatched code).

I had earlier added an infinite loop right below that label (a different loop than my xxxx debug loop), and it was *never* reached in my test. That is, from the lret that I reported as problematic, to the instruction you refer to, the CPU would have had to cross (and finish) the infinite loop that I had added earlier. And that never happened in my test.

I had added that loop at "_AsmTransferControl_al_0000" immediately precisely because I wanted to see if the label is reached and the problem is with something below that label, or with the first lret. I sent my email to the KVM list after I had isolated the problem to the first LRET:

http://thread.gmane.org/gmane.comp.bios.tianocore.devel/5297/focus=5325

Quoted 5 lines · Laszlo Ersek

I tested if the (intended) target location of the LRET is reached, and it is not. (It's easy to test by adding a small infinite loop, moving it around, and seeing if the VM is spinning with or without producing a bunch of output on the debug port.) It's *really* that internally-targeted LRET that causes a reboot. [...]

I have absolutely no clue why this code executes for you and doesn't for me :) What guest RAM size did you test with?

Quoted 15 lines
 qemu-system-x86-4081  [001] 62650.335047: kvm_exit:             reason MSR_READ rip 0x3cf7ae4e info 0 0
 qemu-system-x86-4081  [001] 62650.335048: kvm_msr:              msr_read c0000080 = 0x100
 qemu-system-x86-4081  [001] 62650.335048: kvm_entry:            vcpu 0
 qemu-system-x86-4081  [001] 62650.335048: kvm_exit:             reason MSR_WRITE rip 0x3cf7ae53 info 0 0
 qemu-system-x86-4081  [001] 62650.335049: kvm_msr:              msr_write c0000080 = 0x0
 qemu-system-x86-4081  [001] 62650.335050: kvm_entry:            vcpu 0

LME is turned off.

 qemu-system-x86-4081  [001] 62650.335050: kvm_exit:             reason CR_ACCESS rip 0x3cf7ae55 info 304 0
 qemu-system-x86-4081  [001] 62650.335050: kvm_cr:               cr_write 4 = 0x640
 qemu-system-x86-4081  [001] 62650.335053: kvm_entry:            vcpu 0

PAE is turned off.

Quoted 8 lines
 qemu-system-x86-4081  [001] 62650.335054: kvm_exit:             reason CR_ACCESS rip 0x11e6 info 0 0
 qemu-system-x86-4081  [001] 62650.335054: kvm_cr:               cr_write 0 = 0x33
 qemu-system-x86-4081  [001] 62650.335054: kvm_entry:            vcpu 0

Here we're already in real mode. The weird RIP is explained by the first few bytes after the FACS resume vector:

From this point on you were debugging the Linux wakeup code, in

"arch/x86/realmode/rm/wakeup_asm.S". I think.

Quoted 4 lines
		0x9a1d:0000:  cli    
		0x9a1d:0001:  cld    
		0x9a1d:0002:  ljmp   $9900,$11d7

ENTRY(wakeup_start)

        cli
        cld

        LJMPW_RM(3f)

3:
/* Apparently some dimwit BIOS programmers don't know how to
program a PM to RM transition, and we might end up here with junk in the data segment descriptor registers. The only way to repair that is to go into PM and fix it ourselves... */
[...]

Quoted 21 lines

From Linux kernel commit 4b4f7280.

The page tables are, ahem, crap:

000c000: 6750 fe01 0000 0000 0000 0000 0000 0000  gP..............
000c010: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c040: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c060: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c080: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c090: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000c0f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
Quoted 1 line

This is 0x9c000. Strikes any bell?

We're wildly corrupting OS memory during OVMF S3 resume. That's a known problem and the next stage for me to figure out (with Jordan's help hopefully):

http://thread.gmane.org/gmane.comp.bios.tianocore.devel/5297/focus=5321
http://thread.gmane.org/gmane.comp.bios.tianocore.devel/5297/focus=5325

So, your tracing reached / debugged code that I had never ever reached. And my report was precisely about not reaching it. Once we reach it, it's expected to blow up, but first I wanted to get there.

Again, the 64-bit->16-bit switch (in the original, unpatched edk2/OVMF code) never worked for me.

I think I did find the reason for that though, please see

http://thread.gmane.org/gmane.comp.bios.tianocore.devel/5343/focus=5365

especially the last patch attached to it.

The likely reason for the failure I was seeing is that the 16-bit code had been relocated to way above 1MB and could not be addressed with the 16-bit CS:IP notation at all.

Thanks!
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

#12 lore raw
Quoted 30 lines · Laszlo Ersek
Quoted 2 lines · Paolo Bonzini

Thanks to your binary I now reproduced the issue and it looks like the 64-bit->16-bit switch works:

Thank you for spending (apparently more than a little) time on this!

Quoted 6 lines
 qemu-system-x86-4081  [001] 62650.335040: kvm_exit:             reason CR_ACCESS rip 0x3cf7ae45 info 0 0
 qemu-system-x86-4081  [001] 62650.335041: kvm_cr:               cr_write 0 = 0x32
 qemu-system-x86-4081  [001] 62650.335046: kvm_entry:            vcpu 0

This is the "mov %rax, %cr0". PE and PG are turned off.

I'm surprised by this result. The instruction you refer to is below "_AsmTransferControl_al_0000" (in the original, unpatched code).

I had earlier added an infinite loop right below that label (a different loop than my xxxx debug loop), and it was *never* reached in my test. That is, from the lret that I reported as problematic, to the instruction you refer to, the CPU would have had to cross (and finish) the infinite loop that I had added earlier. And that never happened in my test.

I had added that loop at "_AsmTransferControl_al_0000" immediately precisely because I wanted to see if the label is reached and the problem is with something below that label, or with the first lret. I sent my email to the KVM list after I had isolated the problem to the first LRET:

Quoted 8 lines

http://thread.gmane.org/gmane.comp.bios.tianocore.devel/5297/focus=5325

Quoted 5 lines · Laszlo Ersek

I tested if the (intended) target location of the LRET is reached, and it is not. (It's easy to test by adding a small infinite loop, moving it around, and seeing if the VM is spinning with or without producing a bunch of output on the debug port.) It's *really* that internally-targeted LRET that causes a reboot. [...]

First, you're right and I'm wrong. I can see the triple fault in my kvm trace as well.

Second... This is incredible. Guess what the following patch does.

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
 _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
+.code16
+    movw $0x402, %dx
+    movb $0x58, %al
+qqq:
+    outb %al, %dx
+    jmp qqq
+    movb $0x59, %al
+    outb %al, %dx
+.code64
     movq  %cr0, %rax
     movq  %cr4, %rbx

The infinite loop that I used to "pin down" the first instruction that broke around lret -- it was useless.

The above patch should produce an infinite string of "X" characters on the qemu debug port. "After" the inifnite string, it should produce a "Y" character.

In practice, I'm seeing *one* "X" character, and then a triple fault.

The "jmp qqq" instruction *itself* causes a triple fault (a guest reboot), masking the problem that I was looking for.

When I had such an empty infinite loop (at that time, still without the outb) right before the lret, it worked. As soon as I moved it below _AsmTransferControl_al_0000 (ie. under the target of the lret), the jmp *itself* caused a triple fault (guest reboot), causing me to think that the *lret* caused the reboot. Because, the tight jmp loop would "always" work, and if I'm seeing a reboot loop instead of tight spinning right after the lret, that means that the lret caused the reboot, right? Right?

This is the binary:

0000000000000037 <qqq>:

  37:   ee                      out    %al,(%dx)
  38:   eb fd                   jmp    37 <qqq>

Opcode Instruction Op/ 64-Bit Compat/ Description

                    En  Mode   Leg Mode
EB cb   JMP rel8    A   Valid  Valid     Jump short, RIP = RIP + 8-bit

displacement sign extended to 64-bits

My belief in the sanity of x86 has been shattered to its core. I don't feel stupid. I feel cheated.

Thank you for your help.
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

#13 lore raw
In reply to Paolo Bonzini
Quoted 45 lines · Paolo Bonzini
MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/S3Asm.S +17 -7
index e59fd04..d1cac9d 100644
@@ -19,7 +19,7 @@ ASM_PFX(AsmTransferControl):
     # rcx S3WakingVector    :DWORD
     # rdx AcpiLowMemoryBase :DWORD
     lea   _AsmTransferControl_al_0000(%rip), %eax 
-    movq  $0x2800000000, %r8 
+    movq  $0x1000000000, %r8 
     orq   %r8, %rax
     pushq %rax
     shrd  $20, %ecx, %ebx
@@ -28,24 +28,32 @@ ASM_PFX(AsmTransferControl):
     movl  %ebx, jmp_addr(%rip) 
     lret
 _AsmTransferControl_al_0000:
+    # Old SS should still be okay?
+    addl  _AsmTransferControl_al_0001-_AsmTransferControl_al_0000, %eax
+    pushl $0x28
+    pushl %eax
+    movq  %cr0, %rax
+    movq  %cr4, %rbx
+    andl  $0x7fffffff, %eax
+    andb  $0xdf, %bl
+    movq  %rax, %cr0             # sets EFER.LMA=0 too, so says Intel
+    movl  $0x0c0000080, %ecx
+    rdmsr
+    andb  $0xfe, %ah             # set EFER.LME=0
+    wrmsr
+    movq  %rbx, %cr4             # only now set CR4.PAE=0
+    lret
+_AsmTransferControl_al_0001:
     .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

MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/S3Asm.S ASM_PFX(AsmTransferControl): · line 56

Quoted 10 lines
MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/S3Asm.S +4 -6
-    .byte    0x66
-    movl  $0x0c0000080, %ecx 
-    rdmsr
-    andb  $0xfe, %ah 
-    wrmsr
-    movq  %rbx, %cr4
+    movl  %cr0, %rax        # Get control register 0
+    .byte 0x66
+    .byte 0x83,0xe0,0xfe    # and    eax, 0fffffffeh  ; Clear PE bit (bit #0)
+    .byte 0xf,0x22,0xc0     # mov    cr0, eax         ; Activate real mode

I had to add this incremental patch to get it to compile:

MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/S3Asm.S +3 -3
index c28df3f..85d2a36 100644
@@ -30,8 +30,8 @@ ASM_PFX(AsmTransferControl):
 _AsmTransferControl_al_0000:
     # Old SS should still be okay?
     addl  _AsmTransferControl_al_0001-_AsmTransferControl_al_0000, %eax
-    pushl $0x28
-    pushl %eax
+    .byte 0x6a,0x28              # pushl $0x28 ; opnd sz = 32bits in seg 0x10
+    .byte 0x50                   # pushl %eax
     movq  %cr0, %rax
     movq  %cr4, %rbx
     andl  $0x7fffffff, %eax
@@ -50,7 +50,7 @@ _AsmTransferControl_al_0001:
     movl  %eax, %fs
     movl  %eax, %gs
     movl  %eax, %ss
-    movl  %cr0, %rax        # Get control register 0
+    .byte 0x0f,0x20,0xc0    # movl  %cr0, %eax        ; Get control register 0
     .byte 0x66
     .byte 0x83,0xe0,0xfe    # and    eax, 0fffffffeh  ; Clear PE bit (bit #0)
     .byte 0xf,0x22,0xc0     # mov    cr0, eax         ; Activate real mode

The 2nd lret is reached (just before _AsmTransferControl_al_0001), but then the CPU goes off in the woods. For a while it seems to be spinning who knows where, and in 15-20 seconds or so the guest reboots.

Does gas support mode switches in one file? I found examples on the net (for nasm I think) where people were thunking to real mode and back to protected mode in a single assembly file, and they could use native mnemonics for each part. (They just switched the assembler's mode in sync with execution modes.)

Thanks
Laszlo

Thanks,
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

#14 lore raw
Quoted 2 lines · Laszlo Ersek

Does gas support mode switches in one file? I found examples on the net (for nasm I think) where people were thunking to real mode and back to protected mode in a single assembly file, and they could use native mnemonics for each part. (They just switched the assembler's mode in sync with execution modes.)

Unfortunately the llvm assembler does not support 16-bit mode :(, and we try to keep the .S assembly common….

So it is possible, but then we have to add a big #ifdef for llvm/clang to use the .byte hackery to make that work.

Given the thrash on these 2 files, maybe it worth doing the GNU native mnemonics version to get things working and then porting that back to llvm after it is stable? I can help with llvm/clang/Xcode related issues and porting.

Thanks,

Andrew Fish

------------------------------------------------------------------------------
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
[email protected]
https://lists.sourceforge.net/lists/listinfo/edk2-devel

#15 lore raw
In reply to Laszlo Ersek
Quoted 6 lines · Laszlo Ersek

Does gas support mode switches in one file? I found examples on the net (for nasm I think) where people were thunking to real mode and back to protected mode in a single assembly file, and they could use native mnemonics for each part. (They just switched the assembler's mode in sync with execution modes.)

$DEITY yes. See the patch I posted to fix Thunk16.S last week, which does exactly that. Without that, it's basically unmaintainable.

As Andrew points out, LLVM doesn't support .code16. There's a bug filed.

But frankly, I don't think we should care. Let them fix it. There *is* active development on LLVM and it *can* be fixed, relatively easily. It's not like we're talking about requiring fixes to the effectively unmaintained Microsoft toolchain — which we can't even describe as "stuck in the 20th century" since it doesn't even support the last C standard from *that* century either :)

Signature · 1 line
dwmw2