Merge remote-tracking branch 'remotes/kvm/uq/master' into staging
* remotes/kvm/uq/master:
kvm: Fix eax for cpuid leaf 0x40000000
kvmclock: Ensure proper env->tsc value for kvmclock_current_nsec calculation
kvm: Enable -cpu option to hide KVM
kvm: Ensure negative return value on kvm_init() error handling path
target-i386: set CC_OP to CC_OP_EFLAGS in cpu_load_eflags
target-i386: get CPL from SS.DPL
target-i386: rework CPL checks during task switch, preparing for next patch
target-i386: fix segment flags for SMM and VM86 mode
target-i386: Fix vm86 mode regression introduced in fd460606fd
.
kvm_stat: allow choosing between tracepoints and old stats
kvmclock: Ensure time in migration never goes backward
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
9f0355b590
|
@ -1004,7 +1004,7 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
#if defined(TARGET_I386)
|
#if defined(TARGET_I386)
|
||||||
env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK;
|
env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK;
|
||||||
env->hflags |= HF_PE_MASK;
|
env->hflags |= HF_PE_MASK | HF_CPL_MASK;
|
||||||
if (env->features[FEAT_1_EDX] & CPUID_SSE) {
|
if (env->features[FEAT_1_EDX] & CPUID_SSE) {
|
||||||
env->cr[4] |= CR4_OSFXSR_MASK;
|
env->cr[4] |= CR4_OSFXSR_MASK;
|
||||||
env->hflags |= HF_OSFXSR_MASK;
|
env->hflags |= HF_OSFXSR_MASK;
|
||||||
|
|
|
@ -14,8 +14,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
|
#include "qemu/host-utils.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "sysemu/kvm.h"
|
#include "sysemu/kvm.h"
|
||||||
|
#include "sysemu/cpus.h"
|
||||||
#include "hw/sysbus.h"
|
#include "hw/sysbus.h"
|
||||||
#include "hw/kvm/clock.h"
|
#include "hw/kvm/clock.h"
|
||||||
|
|
||||||
|
@ -34,6 +36,48 @@ typedef struct KVMClockState {
|
||||||
bool clock_valid;
|
bool clock_valid;
|
||||||
} KVMClockState;
|
} KVMClockState;
|
||||||
|
|
||||||
|
struct pvclock_vcpu_time_info {
|
||||||
|
uint32_t version;
|
||||||
|
uint32_t pad0;
|
||||||
|
uint64_t tsc_timestamp;
|
||||||
|
uint64_t system_time;
|
||||||
|
uint32_t tsc_to_system_mul;
|
||||||
|
int8_t tsc_shift;
|
||||||
|
uint8_t flags;
|
||||||
|
uint8_t pad[2];
|
||||||
|
} __attribute__((__packed__)); /* 32 bytes */
|
||||||
|
|
||||||
|
static uint64_t kvmclock_current_nsec(KVMClockState *s)
|
||||||
|
{
|
||||||
|
CPUState *cpu = first_cpu;
|
||||||
|
CPUX86State *env = cpu->env_ptr;
|
||||||
|
hwaddr kvmclock_struct_pa = env->system_time_msr & ~1ULL;
|
||||||
|
uint64_t migration_tsc = env->tsc;
|
||||||
|
struct pvclock_vcpu_time_info time;
|
||||||
|
uint64_t delta;
|
||||||
|
uint64_t nsec_lo;
|
||||||
|
uint64_t nsec_hi;
|
||||||
|
uint64_t nsec;
|
||||||
|
|
||||||
|
if (!(env->system_time_msr & 1ULL)) {
|
||||||
|
/* KVM clock not active */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_physical_memory_read(kvmclock_struct_pa, &time, sizeof(time));
|
||||||
|
|
||||||
|
assert(time.tsc_timestamp <= migration_tsc);
|
||||||
|
delta = migration_tsc - time.tsc_timestamp;
|
||||||
|
if (time.tsc_shift < 0) {
|
||||||
|
delta >>= -time.tsc_shift;
|
||||||
|
} else {
|
||||||
|
delta <<= time.tsc_shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
mulu64(&nsec_lo, &nsec_hi, delta, time.tsc_to_system_mul);
|
||||||
|
nsec = (nsec_lo >> 32) | (nsec_hi << 32);
|
||||||
|
return nsec + time.system_time;
|
||||||
|
}
|
||||||
|
|
||||||
static void kvmclock_vm_state_change(void *opaque, int running,
|
static void kvmclock_vm_state_change(void *opaque, int running,
|
||||||
RunState state)
|
RunState state)
|
||||||
|
@ -45,9 +89,15 @@ static void kvmclock_vm_state_change(void *opaque, int running,
|
||||||
|
|
||||||
if (running) {
|
if (running) {
|
||||||
struct kvm_clock_data data;
|
struct kvm_clock_data data;
|
||||||
|
uint64_t time_at_migration = kvmclock_current_nsec(s);
|
||||||
|
|
||||||
s->clock_valid = false;
|
s->clock_valid = false;
|
||||||
|
|
||||||
|
/* We can't rely on the migrated clock value, just discard it */
|
||||||
|
if (time_at_migration) {
|
||||||
|
s->clock = time_at_migration;
|
||||||
|
}
|
||||||
|
|
||||||
data.clock = s->clock;
|
data.clock = s->clock;
|
||||||
data.flags = 0;
|
data.flags = 0;
|
||||||
ret = kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, &data);
|
ret = kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, &data);
|
||||||
|
@ -75,6 +125,8 @@ static void kvmclock_vm_state_change(void *opaque, int running,
|
||||||
if (s->clock_valid) {
|
if (s->clock_valid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cpu_synchronize_all_states();
|
||||||
ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
|
ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
|
fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
|
||||||
|
|
|
@ -1410,7 +1410,7 @@ int kvm_init(MachineClass *mc)
|
||||||
|
|
||||||
ret = kvm_ioctl(s, KVM_GET_API_VERSION, 0);
|
ret = kvm_ioctl(s, KVM_GET_API_VERSION, 0);
|
||||||
if (ret < KVM_API_VERSION) {
|
if (ret < KVM_API_VERSION) {
|
||||||
if (ret > 0) {
|
if (ret >= 0) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
}
|
}
|
||||||
fprintf(stderr, "kvm version too old\n");
|
fprintf(stderr, "kvm version too old\n");
|
||||||
|
@ -1461,6 +1461,7 @@ int kvm_init(MachineClass *mc)
|
||||||
if (mc->kvm_type) {
|
if (mc->kvm_type) {
|
||||||
type = mc->kvm_type(kvm_type);
|
type = mc->kvm_type(kvm_type);
|
||||||
} else if (kvm_type) {
|
} else if (kvm_type) {
|
||||||
|
ret = -EINVAL;
|
||||||
fprintf(stderr, "Invalid argument kvm-type=%s\n", kvm_type);
|
fprintf(stderr, "Invalid argument kvm-type=%s\n", kvm_type);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
@ -1561,6 +1562,7 @@ int kvm_init(MachineClass *mc)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
assert(ret < 0);
|
||||||
if (s->vmfd >= 0) {
|
if (s->vmfd >= 0) {
|
||||||
close(s->vmfd);
|
close(s->vmfd);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4052,7 +4052,7 @@ int main(int argc, char **argv, char **envp)
|
||||||
|
|
||||||
#if defined(TARGET_I386)
|
#if defined(TARGET_I386)
|
||||||
env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK;
|
env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK;
|
||||||
env->hflags |= HF_PE_MASK;
|
env->hflags |= HF_PE_MASK | HF_CPL_MASK;
|
||||||
if (env->features[FEAT_1_EDX] & CPUID_SSE) {
|
if (env->features[FEAT_1_EDX] & CPUID_SSE) {
|
||||||
env->cr[4] |= CR4_OSFXSR_MASK;
|
env->cr[4] |= CR4_OSFXSR_MASK;
|
||||||
env->hflags |= HF_OSFXSR_MASK;
|
env->hflags |= HF_OSFXSR_MASK;
|
||||||
|
|
|
@ -352,8 +352,8 @@ class TracepointProvider(object):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
class Stats:
|
class Stats:
|
||||||
def __init__(self, provider, fields = None):
|
def __init__(self, providers, fields = None):
|
||||||
self.provider = provider
|
self.providers = providers
|
||||||
self.fields_filter = fields
|
self.fields_filter = fields
|
||||||
self._update()
|
self._update()
|
||||||
def _update(self):
|
def _update(self):
|
||||||
|
@ -362,22 +362,25 @@ class Stats:
|
||||||
if not self.fields_filter:
|
if not self.fields_filter:
|
||||||
return True
|
return True
|
||||||
return re.match(self.fields_filter, key) is not None
|
return re.match(self.fields_filter, key) is not None
|
||||||
self.values = dict([(key, None)
|
self.values = dict()
|
||||||
for key in provider.fields()
|
for d in providers:
|
||||||
if wanted(key)])
|
provider_fields = [key for key in d.fields() if wanted(key)]
|
||||||
self.provider.select(self.values.keys())
|
for key in provider_fields:
|
||||||
|
self.values[key] = None
|
||||||
|
d.select(provider_fields)
|
||||||
def set_fields_filter(self, fields_filter):
|
def set_fields_filter(self, fields_filter):
|
||||||
self.fields_filter = fields_filter
|
self.fields_filter = fields_filter
|
||||||
self._update()
|
self._update()
|
||||||
def get(self):
|
def get(self):
|
||||||
new = self.provider.read()
|
for d in providers:
|
||||||
for key in self.provider.fields():
|
new = d.read()
|
||||||
oldval = self.values.get(key, (0, 0))
|
for key in d.fields():
|
||||||
newval = new[key]
|
oldval = self.values.get(key, (0, 0))
|
||||||
newdelta = None
|
newval = new[key]
|
||||||
if oldval is not None:
|
newdelta = None
|
||||||
newdelta = newval - oldval[0]
|
if oldval is not None:
|
||||||
self.values[key] = (newval, newdelta)
|
newdelta = newval - oldval[0]
|
||||||
|
self.values[key] = (newval, newdelta)
|
||||||
return self.values
|
return self.values
|
||||||
|
|
||||||
if not os.access('/sys/kernel/debug', os.F_OK):
|
if not os.access('/sys/kernel/debug', os.F_OK):
|
||||||
|
@ -487,6 +490,18 @@ options.add_option('-l', '--log',
|
||||||
dest = 'log',
|
dest = 'log',
|
||||||
help = 'run in logging mode (like vmstat)',
|
help = 'run in logging mode (like vmstat)',
|
||||||
)
|
)
|
||||||
|
options.add_option('-t', '--tracepoints',
|
||||||
|
action = 'store_true',
|
||||||
|
default = False,
|
||||||
|
dest = 'tracepoints',
|
||||||
|
help = 'retrieve statistics from tracepoints',
|
||||||
|
)
|
||||||
|
options.add_option('-d', '--debugfs',
|
||||||
|
action = 'store_true',
|
||||||
|
default = False,
|
||||||
|
dest = 'debugfs',
|
||||||
|
help = 'retrieve statistics from debugfs',
|
||||||
|
)
|
||||||
options.add_option('-f', '--fields',
|
options.add_option('-f', '--fields',
|
||||||
action = 'store',
|
action = 'store',
|
||||||
default = None,
|
default = None,
|
||||||
|
@ -495,12 +510,19 @@ options.add_option('-f', '--fields',
|
||||||
)
|
)
|
||||||
(options, args) = options.parse_args(sys.argv)
|
(options, args) = options.parse_args(sys.argv)
|
||||||
|
|
||||||
try:
|
providers = []
|
||||||
provider = TracepointProvider()
|
if options.tracepoints:
|
||||||
except:
|
providers.append(TracepointProvider())
|
||||||
provider = DebugfsProvider()
|
if options.debugfs:
|
||||||
|
providers.append(DebugfsProvider())
|
||||||
|
|
||||||
stats = Stats(provider, fields = options.fields)
|
if len(providers) == 0:
|
||||||
|
try:
|
||||||
|
providers = [TracepointProvider()]
|
||||||
|
except:
|
||||||
|
providers = [DebugfsProvider()]
|
||||||
|
|
||||||
|
stats = Stats(providers, fields = options.fields)
|
||||||
|
|
||||||
if options.log:
|
if options.log:
|
||||||
log(stats)
|
log(stats)
|
||||||
|
|
|
@ -87,6 +87,7 @@ typedef struct X86CPU {
|
||||||
bool hyperv_time;
|
bool hyperv_time;
|
||||||
bool check_cpuid;
|
bool check_cpuid;
|
||||||
bool enforce_cpuid;
|
bool enforce_cpuid;
|
||||||
|
bool expose_kvm;
|
||||||
|
|
||||||
/* if true the CPUID code directly forward host cache leaves to the guest */
|
/* if true the CPUID code directly forward host cache leaves to the guest */
|
||||||
bool cache_info_passthrough;
|
bool cache_info_passthrough;
|
||||||
|
|
|
@ -2792,6 +2792,7 @@ static Property x86_cpu_properties[] = {
|
||||||
DEFINE_PROP_BOOL("hv-time", X86CPU, hyperv_time, false),
|
DEFINE_PROP_BOOL("hv-time", X86CPU, hyperv_time, false),
|
||||||
DEFINE_PROP_BOOL("check", X86CPU, check_cpuid, false),
|
DEFINE_PROP_BOOL("check", X86CPU, check_cpuid, false),
|
||||||
DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false),
|
DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false),
|
||||||
|
DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true),
|
||||||
DEFINE_PROP_END_OF_LIST()
|
DEFINE_PROP_END_OF_LIST()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -986,7 +986,6 @@ static inline void cpu_x86_load_seg_cache(CPUX86State *env,
|
||||||
/* update the hidden flags */
|
/* update the hidden flags */
|
||||||
{
|
{
|
||||||
if (seg_reg == R_CS) {
|
if (seg_reg == R_CS) {
|
||||||
int cpl = selector & 3;
|
|
||||||
#ifdef TARGET_X86_64
|
#ifdef TARGET_X86_64
|
||||||
if ((env->hflags & HF_LMA_MASK) && (flags & DESC_L_MASK)) {
|
if ((env->hflags & HF_LMA_MASK) && (flags & DESC_L_MASK)) {
|
||||||
/* long mode */
|
/* long mode */
|
||||||
|
@ -996,15 +995,14 @@ static inline void cpu_x86_load_seg_cache(CPUX86State *env,
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
/* legacy / compatibility case */
|
/* 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)
|
new_hflags = (env->segs[R_CS].flags & DESC_B_MASK)
|
||||||
>> (DESC_B_SHIFT - HF_CS32_SHIFT);
|
>> (DESC_B_SHIFT - HF_CS32_SHIFT);
|
||||||
env->hflags = (env->hflags & ~(HF_CS32_MASK | HF_CS64_MASK)) |
|
env->hflags = (env->hflags & ~(HF_CS32_MASK | HF_CS64_MASK)) |
|
||||||
new_hflags;
|
new_hflags;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (seg_reg == R_SS) {
|
||||||
|
int cpl = (flags >> DESC_DPL_SHIFT) & 3;
|
||||||
#if HF_CPL_MASK != 3
|
#if HF_CPL_MASK != 3
|
||||||
#error HF_CPL_MASK is hardcoded
|
#error HF_CPL_MASK is hardcoded
|
||||||
#endif
|
#endif
|
||||||
|
@ -1234,11 +1232,14 @@ static inline uint32_t cpu_compute_eflags(CPUX86State *env)
|
||||||
return env->eflags | cpu_cc_compute_all(env, CC_OP) | (env->df & DF_MASK);
|
return env->eflags | cpu_cc_compute_all(env, CC_OP) | (env->df & DF_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* NOTE: CC_OP must be modified manually to CC_OP_EFLAGS */
|
/* NOTE: the translator must set DisasContext.cc_op to CC_OP_EFLAGS
|
||||||
|
* after generating a call to a helper that uses this.
|
||||||
|
*/
|
||||||
static inline void cpu_load_eflags(CPUX86State *env, int eflags,
|
static inline void cpu_load_eflags(CPUX86State *env, int eflags,
|
||||||
int update_mask)
|
int update_mask)
|
||||||
{
|
{
|
||||||
CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
|
CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
|
||||||
|
CC_OP = CC_OP_EFLAGS;
|
||||||
env->df = 1 - (2 * ((eflags >> 10) & 1));
|
env->df = 1 - (2 * ((eflags >> 10) & 1));
|
||||||
env->eflags = (env->eflags & ~update_mask) |
|
env->eflags = (env->eflags & ~update_mask) |
|
||||||
(eflags & update_mask) | 0x2;
|
(eflags & update_mask) | 0x2;
|
||||||
|
|
|
@ -127,9 +127,11 @@ static int x86_cpu_gdb_load_seg(X86CPU *cpu, int sreg, uint8_t *mem_buf)
|
||||||
target_ulong base;
|
target_ulong base;
|
||||||
|
|
||||||
if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) {
|
if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) {
|
||||||
|
int dpl = (env->eflags & VM_MASK) ? 3 : 0;
|
||||||
base = selector << 4;
|
base = selector << 4;
|
||||||
limit = 0xffff;
|
limit = 0xffff;
|
||||||
flags = 0;
|
flags = DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
|
||||||
|
DESC_A_MASK | (dpl << DESC_DPL_SHIFT);
|
||||||
} else {
|
} else {
|
||||||
if (!cpu_x86_get_descr_debug(env, selector, &base, &limit,
|
if (!cpu_x86_get_descr_debug(env, selector, &base, &limit,
|
||||||
&flags)) {
|
&flags)) {
|
||||||
|
|
|
@ -528,23 +528,25 @@ int kvm_arch_init_vcpu(CPUState *cs)
|
||||||
has_msr_hv_hypercall = true;
|
has_msr_hv_hypercall = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(signature, "KVMKVMKVM\0\0\0", 12);
|
if (cpu->expose_kvm) {
|
||||||
c = &cpuid_data.entries[cpuid_i++];
|
memcpy(signature, "KVMKVMKVM\0\0\0", 12);
|
||||||
c->function = KVM_CPUID_SIGNATURE | kvm_base;
|
c = &cpuid_data.entries[cpuid_i++];
|
||||||
c->eax = 0;
|
c->function = KVM_CPUID_SIGNATURE | kvm_base;
|
||||||
c->ebx = signature[0];
|
c->eax = KVM_CPUID_FEATURES | kvm_base;
|
||||||
c->ecx = signature[1];
|
c->ebx = signature[0];
|
||||||
c->edx = signature[2];
|
c->ecx = signature[1];
|
||||||
|
c->edx = signature[2];
|
||||||
|
|
||||||
c = &cpuid_data.entries[cpuid_i++];
|
c = &cpuid_data.entries[cpuid_i++];
|
||||||
c->function = KVM_CPUID_FEATURES | kvm_base;
|
c->function = KVM_CPUID_FEATURES | kvm_base;
|
||||||
c->eax = env->features[FEAT_KVM];
|
c->eax = env->features[FEAT_KVM];
|
||||||
|
|
||||||
has_msr_async_pf_en = c->eax & (1 << KVM_FEATURE_ASYNC_PF);
|
has_msr_async_pf_en = c->eax & (1 << KVM_FEATURE_ASYNC_PF);
|
||||||
|
|
||||||
has_msr_pv_eoi_en = c->eax & (1 << KVM_FEATURE_PV_EOI);
|
has_msr_pv_eoi_en = c->eax & (1 << KVM_FEATURE_PV_EOI);
|
||||||
|
|
||||||
has_msr_kvm_steal_time = c->eax & (1 << KVM_FEATURE_STEAL_TIME);
|
has_msr_kvm_steal_time = c->eax & (1 << KVM_FEATURE_STEAL_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused);
|
cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused);
|
||||||
|
|
||||||
|
@ -1430,7 +1432,7 @@ static int kvm_get_sregs(X86CPU *cpu)
|
||||||
HF_OSFXSR_MASK | HF_LMA_MASK | HF_CS32_MASK | \
|
HF_OSFXSR_MASK | HF_LMA_MASK | HF_CS32_MASK | \
|
||||||
HF_SS32_MASK | HF_CS64_MASK | HF_ADDSEG_MASK)
|
HF_SS32_MASK | HF_CS64_MASK | HF_ADDSEG_MASK)
|
||||||
|
|
||||||
hflags = (env->segs[R_CS].flags >> DESC_DPL_SHIFT) & HF_CPL_MASK;
|
hflags = (env->segs[R_SS].flags >> DESC_DPL_SHIFT) & HF_CPL_MASK;
|
||||||
hflags |= (env->cr[0] & CR0_PE_MASK) << (HF_PE_SHIFT - CR0_PE_SHIFT);
|
hflags |= (env->cr[0] & CR0_PE_MASK) << (HF_PE_SHIFT - CR0_PE_SHIFT);
|
||||||
hflags |= (env->cr[0] << (HF_MP_SHIFT - CR0_MP_SHIFT)) &
|
hflags |= (env->cr[0] << (HF_MP_SHIFT - CR0_MP_SHIFT)) &
|
||||||
(HF_MP_MASK | HF_EM_MASK | HF_TS_MASK);
|
(HF_MP_MASK | HF_EM_MASK | HF_TS_MASK);
|
||||||
|
|
|
@ -312,6 +312,14 @@ static int cpu_post_load(void *opaque, int version_id)
|
||||||
env->segs[R_SS].flags &= ~(env->segs[R_SS].flags & DESC_DPL_MASK);
|
env->segs[R_SS].flags &= ~(env->segs[R_SS].flags & DESC_DPL_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Older versions of QEMU incorrectly used CS.DPL as the CPL when
|
||||||
|
* running under KVM. This is wrong for conforming code segments.
|
||||||
|
* Luckily, in our implementation the CPL field of hflags is redundant
|
||||||
|
* and we can get the right value from the SS descriptor privilege level.
|
||||||
|
*/
|
||||||
|
env->hflags &= ~HF_CPL_MASK;
|
||||||
|
env->hflags |= (env->segs[R_SS].flags >> DESC_DPL_SHIFT) & HF_CPL_MASK;
|
||||||
|
|
||||||
/* XXX: restore FPU round state */
|
/* XXX: restore FPU round state */
|
||||||
env->fpstt = (env->fpus_vmstate >> 11) & 7;
|
env->fpstt = (env->fpus_vmstate >> 11) & 7;
|
||||||
env->fpus = env->fpus_vmstate & ~0x3800;
|
env->fpus = env->fpus_vmstate & ~0x3800;
|
||||||
|
|
|
@ -88,8 +88,10 @@ static inline void load_seg_cache_raw_dt(SegmentCache *sc, uint32_t e1,
|
||||||
static inline void load_seg_vm(CPUX86State *env, int seg, int selector)
|
static inline void load_seg_vm(CPUX86State *env, int seg, int selector)
|
||||||
{
|
{
|
||||||
selector &= 0xffff;
|
selector &= 0xffff;
|
||||||
cpu_x86_load_seg_cache(env, seg, selector,
|
|
||||||
(selector << 4), 0xffff, 0);
|
cpu_x86_load_seg_cache(env, seg, selector, (selector << 4), 0xffff,
|
||||||
|
DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
|
||||||
|
DESC_A_MASK | (3 << DESC_DPL_SHIFT));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void get_ss_esp_from_tss(CPUX86State *env, uint32_t *ss_ptr,
|
static inline void get_ss_esp_from_tss(CPUX86State *env, uint32_t *ss_ptr,
|
||||||
|
@ -133,11 +135,10 @@ static inline void get_ss_esp_from_tss(CPUX86State *env, uint32_t *ss_ptr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX: merge with load_seg() */
|
static void tss_load_seg(CPUX86State *env, int seg_reg, int selector, int cpl)
|
||||||
static void tss_load_seg(CPUX86State *env, int seg_reg, int selector)
|
|
||||||
{
|
{
|
||||||
uint32_t e1, e2;
|
uint32_t e1, e2;
|
||||||
int rpl, dpl, cpl;
|
int rpl, dpl;
|
||||||
|
|
||||||
if ((selector & 0xfffc) != 0) {
|
if ((selector & 0xfffc) != 0) {
|
||||||
if (load_segment(env, &e1, &e2, selector) != 0) {
|
if (load_segment(env, &e1, &e2, selector) != 0) {
|
||||||
|
@ -148,18 +149,13 @@ static void tss_load_seg(CPUX86State *env, int seg_reg, int selector)
|
||||||
}
|
}
|
||||||
rpl = selector & 3;
|
rpl = selector & 3;
|
||||||
dpl = (e2 >> DESC_DPL_SHIFT) & 3;
|
dpl = (e2 >> DESC_DPL_SHIFT) & 3;
|
||||||
cpl = env->hflags & HF_CPL_MASK;
|
|
||||||
if (seg_reg == R_CS) {
|
if (seg_reg == R_CS) {
|
||||||
if (!(e2 & DESC_CS_MASK)) {
|
if (!(e2 & DESC_CS_MASK)) {
|
||||||
raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc);
|
raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc);
|
||||||
}
|
}
|
||||||
/* XXX: is it correct? */
|
|
||||||
if (dpl != rpl) {
|
if (dpl != rpl) {
|
||||||
raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc);
|
raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc);
|
||||||
}
|
}
|
||||||
if ((e2 & DESC_C_MASK) && dpl > rpl) {
|
|
||||||
raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc);
|
|
||||||
}
|
|
||||||
} else if (seg_reg == R_SS) {
|
} else if (seg_reg == R_SS) {
|
||||||
/* SS must be writable data */
|
/* SS must be writable data */
|
||||||
if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) {
|
if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) {
|
||||||
|
@ -446,12 +442,13 @@ static void switch_tss(CPUX86State *env, int tss_selector,
|
||||||
|
|
||||||
/* load the segments */
|
/* load the segments */
|
||||||
if (!(new_eflags & VM_MASK)) {
|
if (!(new_eflags & VM_MASK)) {
|
||||||
tss_load_seg(env, R_CS, new_segs[R_CS]);
|
int cpl = new_segs[R_CS] & 3;
|
||||||
tss_load_seg(env, R_SS, new_segs[R_SS]);
|
tss_load_seg(env, R_CS, new_segs[R_CS], cpl);
|
||||||
tss_load_seg(env, R_ES, new_segs[R_ES]);
|
tss_load_seg(env, R_SS, new_segs[R_SS], cpl);
|
||||||
tss_load_seg(env, R_DS, new_segs[R_DS]);
|
tss_load_seg(env, R_ES, new_segs[R_ES], cpl);
|
||||||
tss_load_seg(env, R_FS, new_segs[R_FS]);
|
tss_load_seg(env, R_DS, new_segs[R_DS], cpl);
|
||||||
tss_load_seg(env, R_GS, new_segs[R_GS]);
|
tss_load_seg(env, R_FS, new_segs[R_FS], cpl);
|
||||||
|
tss_load_seg(env, R_GS, new_segs[R_GS], cpl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check that env->eip is in the CS segment limits */
|
/* check that env->eip is in the CS segment limits */
|
||||||
|
@ -558,6 +555,7 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int,
|
||||||
int has_error_code, new_stack, shift;
|
int has_error_code, new_stack, shift;
|
||||||
uint32_t e1, e2, offset, ss = 0, esp, ss_e1 = 0, ss_e2 = 0;
|
uint32_t e1, e2, offset, ss = 0, esp, ss_e1 = 0, ss_e2 = 0;
|
||||||
uint32_t old_eip, sp_mask;
|
uint32_t old_eip, sp_mask;
|
||||||
|
int vm86 = env->eflags & VM_MASK;
|
||||||
|
|
||||||
has_error_code = 0;
|
has_error_code = 0;
|
||||||
if (!is_int && !is_hw) {
|
if (!is_int && !is_hw) {
|
||||||
|
@ -673,7 +671,7 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int,
|
||||||
ssp = get_seg_base(ss_e1, ss_e2);
|
ssp = get_seg_base(ss_e1, ss_e2);
|
||||||
} else if ((e2 & DESC_C_MASK) || dpl == cpl) {
|
} else if ((e2 & DESC_C_MASK) || dpl == cpl) {
|
||||||
/* to same privilege */
|
/* to same privilege */
|
||||||
if (env->eflags & VM_MASK) {
|
if (vm86) {
|
||||||
raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
|
raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
|
||||||
}
|
}
|
||||||
new_stack = 0;
|
new_stack = 0;
|
||||||
|
@ -694,14 +692,14 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int,
|
||||||
#if 0
|
#if 0
|
||||||
/* XXX: check that enough room is available */
|
/* XXX: check that enough room is available */
|
||||||
push_size = 6 + (new_stack << 2) + (has_error_code << 1);
|
push_size = 6 + (new_stack << 2) + (has_error_code << 1);
|
||||||
if (env->eflags & VM_MASK) {
|
if (vm86) {
|
||||||
push_size += 8;
|
push_size += 8;
|
||||||
}
|
}
|
||||||
push_size <<= shift;
|
push_size <<= shift;
|
||||||
#endif
|
#endif
|
||||||
if (shift == 1) {
|
if (shift == 1) {
|
||||||
if (new_stack) {
|
if (new_stack) {
|
||||||
if (env->eflags & VM_MASK) {
|
if (vm86) {
|
||||||
PUSHL(ssp, esp, sp_mask, env->segs[R_GS].selector);
|
PUSHL(ssp, esp, sp_mask, env->segs[R_GS].selector);
|
||||||
PUSHL(ssp, esp, sp_mask, env->segs[R_FS].selector);
|
PUSHL(ssp, esp, sp_mask, env->segs[R_FS].selector);
|
||||||
PUSHL(ssp, esp, sp_mask, env->segs[R_DS].selector);
|
PUSHL(ssp, esp, sp_mask, env->segs[R_DS].selector);
|
||||||
|
@ -718,7 +716,7 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (new_stack) {
|
if (new_stack) {
|
||||||
if (env->eflags & VM_MASK) {
|
if (vm86) {
|
||||||
PUSHW(ssp, esp, sp_mask, env->segs[R_GS].selector);
|
PUSHW(ssp, esp, sp_mask, env->segs[R_GS].selector);
|
||||||
PUSHW(ssp, esp, sp_mask, env->segs[R_FS].selector);
|
PUSHW(ssp, esp, sp_mask, env->segs[R_FS].selector);
|
||||||
PUSHW(ssp, esp, sp_mask, env->segs[R_DS].selector);
|
PUSHW(ssp, esp, sp_mask, env->segs[R_DS].selector);
|
||||||
|
@ -742,7 +740,7 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int,
|
||||||
env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
|
env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
|
||||||
|
|
||||||
if (new_stack) {
|
if (new_stack) {
|
||||||
if (env->eflags & VM_MASK) {
|
if (vm86) {
|
||||||
cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0, 0);
|
cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0, 0);
|
||||||
cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0, 0);
|
cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0, 0);
|
||||||
cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0, 0);
|
cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0, 0);
|
||||||
|
@ -1600,7 +1598,6 @@ void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip,
|
||||||
}
|
}
|
||||||
next_eip = env->eip + next_eip_addend;
|
next_eip = env->eip + next_eip_addend;
|
||||||
switch_tss(env, new_cs, e1, e2, SWITCH_TSS_JMP, next_eip);
|
switch_tss(env, new_cs, e1, e2, SWITCH_TSS_JMP, next_eip);
|
||||||
CC_OP = CC_OP_EFLAGS;
|
|
||||||
break;
|
break;
|
||||||
case 4: /* 286 call gate */
|
case 4: /* 286 call gate */
|
||||||
case 12: /* 386 call gate */
|
case 12: /* 386 call gate */
|
||||||
|
@ -1769,7 +1766,6 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip,
|
||||||
raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
|
raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
|
||||||
}
|
}
|
||||||
switch_tss(env, new_cs, e1, e2, SWITCH_TSS_CALL, next_eip);
|
switch_tss(env, new_cs, e1, e2, SWITCH_TSS_CALL, next_eip);
|
||||||
CC_OP = CC_OP_EFLAGS;
|
|
||||||
return;
|
return;
|
||||||
case 4: /* 286 call gate */
|
case 4: /* 286 call gate */
|
||||||
case 12: /* 386 call gate */
|
case 12: /* 386 call gate */
|
||||||
|
@ -2464,9 +2460,12 @@ void helper_verw(CPUX86State *env, target_ulong selector1)
|
||||||
void cpu_x86_load_seg(CPUX86State *env, int seg_reg, int selector)
|
void cpu_x86_load_seg(CPUX86State *env, int seg_reg, int selector)
|
||||||
{
|
{
|
||||||
if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) {
|
if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) {
|
||||||
|
int dpl = (env->eflags & VM_MASK) ? 3 : 0;
|
||||||
selector &= 0xffff;
|
selector &= 0xffff;
|
||||||
cpu_x86_load_seg_cache(env, seg_reg, selector,
|
cpu_x86_load_seg_cache(env, seg_reg, selector,
|
||||||
(selector << 4), 0xffff, 0);
|
(selector << 4), 0xffff,
|
||||||
|
DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
|
||||||
|
DESC_A_MASK | (dpl << DESC_DPL_SHIFT));
|
||||||
} else {
|
} else {
|
||||||
helper_load_seg(env, seg_reg, selector);
|
helper_load_seg(env, seg_reg, selector);
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,15 +168,26 @@ void do_smm_enter(X86CPU *cpu)
|
||||||
CR0_PG_MASK));
|
CR0_PG_MASK));
|
||||||
cpu_x86_update_cr4(env, 0);
|
cpu_x86_update_cr4(env, 0);
|
||||||
env->dr[7] = 0x00000400;
|
env->dr[7] = 0x00000400;
|
||||||
CC_OP = CC_OP_EFLAGS;
|
|
||||||
|
|
||||||
cpu_x86_load_seg_cache(env, R_CS, (env->smbase >> 4) & 0xffff, env->smbase,
|
cpu_x86_load_seg_cache(env, R_CS, (env->smbase >> 4) & 0xffff, env->smbase,
|
||||||
0xffffffff, 0);
|
0xffffffff,
|
||||||
cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0xffffffff, 0);
|
DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
|
||||||
cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0xffffffff, 0);
|
DESC_A_MASK);
|
||||||
cpu_x86_load_seg_cache(env, R_SS, 0, 0, 0xffffffff, 0);
|
cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0xffffffff,
|
||||||
cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0xffffffff, 0);
|
DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
|
||||||
cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0xffffffff, 0);
|
DESC_A_MASK);
|
||||||
|
cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0xffffffff,
|
||||||
|
DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
|
||||||
|
DESC_A_MASK);
|
||||||
|
cpu_x86_load_seg_cache(env, R_SS, 0, 0, 0xffffffff,
|
||||||
|
DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
|
||||||
|
DESC_A_MASK);
|
||||||
|
cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0xffffffff,
|
||||||
|
DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
|
||||||
|
DESC_A_MASK);
|
||||||
|
cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0xffffffff,
|
||||||
|
DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
|
||||||
|
DESC_A_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void helper_rsm(CPUX86State *env)
|
void helper_rsm(CPUX86State *env)
|
||||||
|
@ -296,7 +307,6 @@ void helper_rsm(CPUX86State *env)
|
||||||
env->smbase = ldl_phys(cs->as, sm_state + 0x7ef8) & ~0x7fff;
|
env->smbase = ldl_phys(cs->as, sm_state + 0x7ef8) & ~0x7fff;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
CC_OP = CC_OP_EFLAGS;
|
|
||||||
env->hflags &= ~HF_SMM_MASK;
|
env->hflags &= ~HF_SMM_MASK;
|
||||||
cpu_smm_update(env);
|
cpu_smm_update(env);
|
||||||
|
|
||||||
|
|
|
@ -260,7 +260,6 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend)
|
||||||
env->vm_vmcb + offsetof(struct vmcb,
|
env->vm_vmcb + offsetof(struct vmcb,
|
||||||
save.rflags)),
|
save.rflags)),
|
||||||
~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
|
~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
|
||||||
CC_OP = CC_OP_EFLAGS;
|
|
||||||
|
|
||||||
svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.es),
|
svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.es),
|
||||||
R_ES);
|
R_ES);
|
||||||
|
@ -702,7 +701,6 @@ void helper_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1)
|
||||||
save.rflags)),
|
save.rflags)),
|
||||||
~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK |
|
~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK |
|
||||||
VM_MASK));
|
VM_MASK));
|
||||||
CC_OP = CC_OP_EFLAGS;
|
|
||||||
|
|
||||||
svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.es),
|
svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.es),
|
||||||
R_ES);
|
R_ES);
|
||||||
|
|
Loading…
Reference in a new issue