diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2014-05-14 10:38:18 +0200 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2014-05-21 18:02:08 +0200 |
commit | 7125c937c97d9ec4a41b3cb6d1b3e805ec53e255 (patch) | |
tree | 9708833c2a12eecd0a52c9c85a0853d0a54784a5 /target-i386/cpu.h | |
parent | d3b5491897456739c6dc21c604ef8bc28e294bfc (diff) | |
download | qemu-7125c937c97d9ec4a41b3cb6d1b3e805ec53e255.zip |
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.
Tested-by: Kevin O'Connor <kevin@koconnor.net>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'target-i386/cpu.h')
-rw-r--r-- | target-i386/cpu.h | 8 |
1 files changed, 3 insertions, 5 deletions
diff --git a/target-i386/cpu.h b/target-i386/cpu.h index e9cbdabc03..65a44d9fe3 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 |