diff mbox

SMI handler should set the CPL to zero and save and restore it on rsm.

Message ID 537323DB.5070005@redhat.com
State New
Headers show

Commit Message

Paolo Bonzini May 14, 2014, 8:05 a.m. UTC
Il 14/05/2014 00:07, Kevin O'Connor ha scritto:
> On Tue, May 13, 2014 at 02:39:20PM -0400, Kevin O'Connor wrote:
>> On Tue, May 13, 2014 at 08:24:47PM +0200, Paolo Bonzini wrote:
>>> Il 27/04/2014 19:25, Kevin O'Connor ha scritto:
>>>> I was wondering about that as well.  The Intel docs state that the CPL
>>>> is bits 0-1 of the CS.selector register, and that protected mode
>>>> starts immediately after setting the PE bit.  The CS.selector field
>>>> should be the value of %cs in real mode, which is the value added to
>>>> eip (after shifting right by 4).
>>>>
>>>> I guess that means that the real mode code that enables the PE bit
>>>> must run with a code segment aligned to a value of 4.  (Which
>>>> effectively means code alignment of 64 bytes because of the segment
>>>> shift.)
>>>
>>> It turns out that this is not a requirement; which means that the 
>>> protected mode transition is exactly the only place where CPL is not 
>>> redundant.  The CPL remains zero until you reload CS with a long jump.
>>
>> That doesn't sound right.
> 
> FYI, I ran a couple of tests on a real machine where I set protected
> mode while %cs=0xf003 and I can confirm that it doesn't cause faults.
> So, you are correct - the CPL appears to be stored separately from
> %cs[1:0] and it appears CPL isn't altered until CS is reloaded.

CPL isn't even altered when CS is reloaded, because you cannot jump out
of ring-0 except with an inter-privilege IRET, and that reloads SS too.

An IRET or task switch is also the only way to set EFLAGS.VM, and it will
hardcode SS.DPL=3, again matching CPL=3.

Finally, to get out of real mode you need to have CPL=0, and whatever got
you at CPL has also loaded SS with a ring-0 stack.  This means that SS.DPL=0
right after clearing CR0.PE.

Using SS.DPL as the CPL really sounds like the right approach.  I 
tried it on my KVM testcase, and it works well.  For QEMU, even the
special case of SYSRET will be handled fine because QEMU does set 
SS.DPL = 3:

        cpu_x86_load_seg_cache(env, R_SS, selector + 8,
                               0, 0xffffffff,
                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
                               DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
                               DESC_W_MASK | DESC_A_MASK);

SS.DPL=CPL=3, SS.RPL=selector & 3 is a mix of Intel behavior (which is 
SS.DPL=SS.RPL=CPL=3) and AMD behavior (because they set CPL=3 but 
SS.DPL=SS.RPL=selector & 3).  We may want to match Intel behavior,
but that's a different change.

Can you check if this patch works for you, and if so reply with
Tested-by/Reviewed-by?

------------- 8< -------------------
From: Paolo Bonzini <pbonzini@redhat.com>
Subject: [PATCH] target-i386: get CPL from SS.DPL

CS.RPL is not equal to the CPL in the few instructions between
setting CR0.PE and reloading CS.  We get this right in the common
case, because writes to CR0 do not modify the CPL, but it would
not be enough if an SMI comes exactly during that brief period.
Were this to happen, the RSM instruction would erroneously set
CPL to the low two bits of the real-mode selector; and if they are
not 00, the next instruction fetch cannot access the code segment
and causes a triple fault.

However, SS.DPL *is* always equal to the CPL.  In real processors
(AMD only) there is a weird case of SYSRET setting SS.DPL=SS.RPL
from the STAR register while forcing CPL=3, but we do not emulate
that.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>




Paolo

Comments

Kevin O'Connor May 15, 2014, 12:20 a.m. UTC | #1
On Wed, May 14, 2014 at 10:05:47AM +0200, Paolo Bonzini wrote:
> CPL isn't even altered when CS is reloaded, because you cannot jump out
> of ring-0 except with an inter-privilege IRET, and that reloads SS too.
> 
> An IRET or task switch is also the only way to set EFLAGS.VM, and it will
> hardcode SS.DPL=3, again matching CPL=3.
> 
> Finally, to get out of real mode you need to have CPL=0, and whatever got
> you at CPL has also loaded SS with a ring-0 stack.  This means that SS.DPL=0
> right after clearing CR0.PE.
> 
> Using SS.DPL as the CPL really sounds like the right approach.  I 
> tried it on my KVM testcase, and it works well.  For QEMU, even the
> special case of SYSRET will be handled fine because QEMU does set 
> SS.DPL = 3:
> 
>         cpu_x86_load_seg_cache(env, R_SS, selector + 8,
>                                0, 0xffffffff,
>                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
>                                DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
>                                DESC_W_MASK | DESC_A_MASK);
> 
> SS.DPL=CPL=3, SS.RPL=selector & 3 is a mix of Intel behavior (which is 
> SS.DPL=SS.RPL=CPL=3) and AMD behavior (because they set CPL=3 but 
> SS.DPL=SS.RPL=selector & 3).  We may want to match Intel behavior,
> but that's a different change.
> 
> Can you check if this patch works for you, and if so reply with
> Tested-by/Reviewed-by?

Your patch causes Freedos to crash when emm386 is loaded, so I think
it broke VM86 mode.  Below are some logs I took from qemu at the point
of the crash.

-Kevin


==================== Freedos with unmodified QEMU 2.0.0 (and SeaBIOS master)


IN: 
0x000000000012276e:  mov    %cr0,%eax
0x0000000000122771:  or     $0x80000000,%eax
0x0000000000122777:  mov    %eax,%cr0

EAX=00125000 EBX=00000000 ECX=00000000 EDX=00000000
ESI=00000000 EDI=00000000 EBP=00000000 ESP=000001dc
EIP=000006fe EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0014 00003a60 00000b8c 00009300 DPL=0 DS16 [-WA]
CS =000c 00122070 00002713 00009a00 DPL=0 CS16 [-R-]
SS =0038 00003820 00000200 00009200 DPL=0 DS16 [-W-]
DS =0014 00003a60 00000b8c 00009300 DPL=0 DS16 [-WA]
FS =0014 00003a60 00000b8c 00009300 DPL=0 DS16 [-WA]
GS =0014 00003a60 00000b8c 00009300 DPL=0 DS16 [-WA]
LDT=0008 00003d64 00000020 00008200 DPL=0 LDT
TR =0010 00110000 00002069 00008900 DPL=0 TSS32-avl
GDT=     00003ce4 0000007f
IDT=     00124784 000007ff
CR0=00000011 CR2=00000000 CR3=00125000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff4ff0 DR7=0000000000000400
CCS=00000000 CCD=00126360 CCO=EFLAGS  
EFER=0000000000000000
----------------
IN: 
0x000000000012277a:  iretl  

EAX=80000011 EBX=00000000 ECX=00000000 EDX=00000000
ESI=00000000 EDI=00000000 EBP=00000000 ESP=000001dc
EIP=0000070a EFL=00000086 [--S--P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0014 00003a60 00000b8c 00009300 DPL=0 DS16 [-WA]
CS =000c 00122070 00002713 00009a00 DPL=0 CS16 [-R-]
SS =0038 00003820 00000200 00009200 DPL=0 DS16 [-W-]
DS =0014 00003a60 00000b8c 00009300 DPL=0 DS16 [-WA]
FS =0014 00003a60 00000b8c 00009300 DPL=0 DS16 [-WA]
GS =0014 00003a60 00000b8c 00009300 DPL=0 DS16 [-WA]
LDT=0008 00003d64 00000020 00008200 DPL=0 LDT
TR =0010 00110000 00002069 00008900 DPL=0 TSS32-avl
GDT=     00003ce4 0000007f
IDT=     00124784 000007ff
CR0=80000011 CR2=00000000 CR3=00125000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff4ff0 DR7=0000000000000400
CCS=00000000 CCD=80000011 CCO=LOGICL  
EFER=0000000000000000
Servicing hardware INT=0x08
     0: v=08 e=0000 i=0 cpl=3 IP=0930:00000000000001c9 pc=00000000000094c9 SP=0920:0000000000000100 env->regs[R_EAX]=0000000080000011
EAX=80000011 EBX=00000000 ECX=00000000 EDX=00000000
ESI=00000000 EDI=00000000 EBP=00000000 ESP=00000100
EIP=000001c9 EFL=00023202 [-------] CPL=3 II=0 A20=1 SMM=0 HLT=0
ES =0000 00000000 0000ffff 00000000
CS =0930 00009300 0000ffff 00000000
SS =0920 00009200 0000ffff 00000000
DS =03a6 00003a60 0000ffff 00000000
FS =0000 00000000 0000ffff 00000000
GS =0000 00000000 0000ffff 00000000
LDT=0008 00003d64 00000020 00008200 DPL=0 LDT
TR =0010 00110000 00002069 00008900 DPL=0 TSS32-avl
GDT=     00003ce4 0000007f
IDT=     00124784 000007ff
CR0=80000011 CR2=00000000 CR3=00125000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff4ff0 DR7=0000000000000400
CCS=00000000 CCD=80000011 CCO=EFLAGS  
EFER=0000000000000000
----------------
IN: 
0x000000000012279c:  call   0x122070


==================== Freedos with patched QEMU (and SeaBIOS master)


IN: 
0x000000000012276e:  mov    %cr0,%eax
0x0000000000122771:  or     $0x80000000,%eax
0x0000000000122777:  mov    %eax,%cr0

EAX=00125000 EBX=00000000 ECX=00000000 EDX=00000000
ESI=00000000 EDI=00000000 EBP=00000000 ESP=000001dc
EIP=000006fe EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0014 00003a60 00000b8c 00009300 DPL=0 DS16 [-WA]
CS =000c 00122070 00002713 00009a00 DPL=0 CS16 [-R-]
SS =0038 00003820 00000200 00009200 DPL=0 DS16 [-W-]
DS =0014 00003a60 00000b8c 00009300 DPL=0 DS16 [-WA]
FS =0014 00003a60 00000b8c 00009300 DPL=0 DS16 [-WA]
GS =0014 00003a60 00000b8c 00009300 DPL=0 DS16 [-WA]
LDT=0008 00003d64 00000020 00008200 DPL=0 LDT
TR =0010 00110000 00002069 00008900 DPL=0 TSS32-avl
GDT=     00003ce4 0000007f
IDT=     00124784 000007ff
CR0=00000011 CR2=00000000 CR3=00125000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff4ff0 DR7=0000000000000400
CCS=00000000 CCD=00126360 CCO=EFLAGS  
EFER=0000000000000000
----------------
IN: 
0x000000000012277a:  iretl  

EAX=80000011 EBX=00000000 ECX=00000000 EDX=00000000
ESI=00000000 EDI=00000000 EBP=00000000 ESP=000001dc
EIP=0000070a EFL=00000086 [--S--P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0014 00003a60 00000b8c 00009300 DPL=0 DS16 [-WA]
CS =000c 00122070 00002713 00009a00 DPL=0 CS16 [-R-]
SS =0038 00003820 00000200 00009200 DPL=0 DS16 [-W-]
DS =0014 00003a60 00000b8c 00009300 DPL=0 DS16 [-WA]
FS =0014 00003a60 00000b8c 00009300 DPL=0 DS16 [-WA]
GS =0014 00003a60 00000b8c 00009300 DPL=0 DS16 [-WA]
LDT=0008 00003d64 00000020 00008200 DPL=0 LDT
TR =0010 00110000 00002069 00008900 DPL=0 TSS32-avl
GDT=     00003ce4 0000007f
IDT=     00124784 000007ff
CR0=80000011 CR2=00000000 CR3=00125000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff4ff0 DR7=0000000000000400
CCS=00000000 CCD=80000011 CCO=LOGICL  
EFER=0000000000000000
Servicing hardware INT=0x08
     0: v=08 e=0000 i=0 cpl=0 IP=0930:00000000000001c9 pc=00000000000094c9 SP=0920:0000000000000100 env->regs[R_EAX]=0000000080000011
EAX=80000011 EBX=00000000 ECX=00000000 EDX=00000000
ESI=00000000 EDI=00000000 EBP=00000000 ESP=00000100
EIP=000001c9 EFL=00023202 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0000 00000000 0000ffff 00000000
CS =0930 00009300 0000ffff 00000000
SS =0920 00009200 0000ffff 00000000
DS =03a6 00003a60 0000ffff 00000000
FS =0000 00000000 0000ffff 00000000
GS =0000 00000000 0000ffff 00000000
LDT=0008 00003d64 00000020 00008200 DPL=0 LDT
TR =0010 00110000 00002069 00008900 DPL=0 TSS32-avl
GDT=     00003ce4 0000007f
IDT=     00124784 000007ff
CR0=80000011 CR2=00000000 CR3=00125000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff4ff0 DR7=0000000000000400
CCS=00000000 CCD=80000011 CCO=EFLAGS  
EFER=0000000000000000
check_exception old: 0xffffffff new 0xd
     1: v=0d e=000c i=0 cpl=0 IP=0930:00000000000001c9 pc=00000000000094c9 SP=0920:0000000000000100 env->regs[R_EAX]=0000000080000011
diff mbox

Patch

diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index e9cbdab..65a44d9 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -986,7 +986,6 @@  static inline void cpu_x86_load_seg_cache(CPUX86State *env,
     /* update the hidden flags */
     {
         if (seg_reg == R_CS) {
-            int cpl = selector & 3;
 #ifdef TARGET_X86_64
             if ((env->hflags & HF_LMA_MASK) && (flags & DESC_L_MASK)) {
                 /* long mode */
@@ -996,15 +995,14 @@  static inline void cpu_x86_load_seg_cache(CPUX86State *env,
 #endif
             {
                 /* legacy / compatibility case */
-                if (!(env->cr[0] & CR0_PE_MASK))
-                    cpl = 0;
-                else if (env->eflags & VM_MASK)
-                    cpl = 3;
                 new_hflags = (env->segs[R_CS].flags & DESC_B_MASK)
                     >> (DESC_B_SHIFT - HF_CS32_SHIFT);
                 env->hflags = (env->hflags & ~(HF_CS32_MASK | HF_CS64_MASK)) |
                     new_hflags;
             }
+        }
+        if (seg_reg == R_SS) {
+            int cpl = (flags >> DESC_DPL_SHIFT) & 3;
 #if HF_CPL_MASK != 3
 #error HF_CPL_MASK is hardcoded
 #endif