#include <sys/utsname.h>
#include <linux/kvm.h>
+#include <linux/kvm_para.h>
#include "qemu-common.h"
#include "sysemu.h"
#include "hw/pc.h"
#include "hw/apic.h"
#include "ioport.h"
-#include "kvm_x86.h"
-#ifdef CONFIG_KVM_PARA
-#include <linux/kvm_para.h>
-#endif
-//
//#define DEBUG_KVM
#ifdef DEBUG_KVM
#define BUS_MCEERR_AO 5
#endif
+const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
+ KVM_CAP_INFO(SET_TSS_ADDR),
+ KVM_CAP_INFO(EXT_CPUID),
+ KVM_CAP_INFO(MP_STATE),
+ KVM_CAP_LAST_INFO
+};
+
static bool has_msr_star;
static bool has_msr_hsave_pa;
+static bool has_msr_async_pf_en;
static int lm_capable_kernel;
-#ifdef KVM_CAP_EXT_CPUID
-
static struct kvm_cpuid2 *try_get_cpuid(KVMState *s, int max)
{
struct kvm_cpuid2 *cpuid;
return cpuid;
}
+struct kvm_para_features {
+ int cap;
+ int feature;
+} para_features[] = {
+ { KVM_CAP_CLOCKSOURCE, KVM_FEATURE_CLOCKSOURCE },
+ { KVM_CAP_NOP_IO_DELAY, KVM_FEATURE_NOP_IO_DELAY },
+ { KVM_CAP_PV_MMU, KVM_FEATURE_MMU_OP },
+#ifdef KVM_CAP_ASYNC_PF
+ { KVM_CAP_ASYNC_PF, KVM_FEATURE_ASYNC_PF },
+#endif
+ { -1, -1 }
+};
+
+static int get_para_features(CPUState *env)
+{
+ int i, features = 0;
+
+ for (i = 0; i < ARRAY_SIZE(para_features) - 1; i++) {
+ if (kvm_check_extension(env->kvm_state, para_features[i].cap)) {
+ features |= (1 << para_features[i].feature);
+ }
+ }
+
+ return features;
+}
+
+
uint32_t kvm_arch_get_supported_cpuid(CPUState *env, uint32_t function,
uint32_t index, int reg)
{
int i, max;
uint32_t ret = 0;
uint32_t cpuid_1_edx;
-
- if (!kvm_check_extension(env->kvm_state, KVM_CAP_EXT_CPUID)) {
- return -1U;
- }
+ int has_kvm_features = 0;
max = 1;
while ((cpuid = try_get_cpuid(env->kvm_state, max)) == NULL) {
for (i = 0; i < cpuid->nent; ++i) {
if (cpuid->entries[i].function == function &&
cpuid->entries[i].index == index) {
+ if (cpuid->entries[i].function == KVM_CPUID_FEATURES) {
+ has_kvm_features = 1;
+ }
switch (reg) {
case R_EAX:
ret = cpuid->entries[i].eax;
qemu_free(cpuid);
+ /* fallback for older kernels */
+ if (!has_kvm_features && (function == KVM_CPUID_FEATURES)) {
+ ret = get_para_features(env);
+ }
+
return ret;
}
-#else
+typedef struct HWPoisonPage {
+ ram_addr_t ram_addr;
+ QLIST_ENTRY(HWPoisonPage) list;
+} HWPoisonPage;
-uint32_t kvm_arch_get_supported_cpuid(CPUState *env, uint32_t function,
- uint32_t index, int reg)
-{
- return -1U;
-}
+static QLIST_HEAD(, HWPoisonPage) hwpoison_page_list =
+ QLIST_HEAD_INITIALIZER(hwpoison_page_list);
-#endif
+static void kvm_unpoison_all(void *param)
+{
+ HWPoisonPage *page, *next_page;
-#ifdef CONFIG_KVM_PARA
-struct kvm_para_features {
- int cap;
- int feature;
-} para_features[] = {
-#ifdef KVM_CAP_CLOCKSOURCE
- { KVM_CAP_CLOCKSOURCE, KVM_FEATURE_CLOCKSOURCE },
-#endif
-#ifdef KVM_CAP_NOP_IO_DELAY
- { KVM_CAP_NOP_IO_DELAY, KVM_FEATURE_NOP_IO_DELAY },
-#endif
-#ifdef KVM_CAP_PV_MMU
- { KVM_CAP_PV_MMU, KVM_FEATURE_MMU_OP },
-#endif
-#ifdef KVM_CAP_ASYNC_PF
- { KVM_CAP_ASYNC_PF, KVM_FEATURE_ASYNC_PF },
-#endif
- { -1, -1 }
-};
+ QLIST_FOREACH_SAFE(page, &hwpoison_page_list, list, next_page) {
+ QLIST_REMOVE(page, list);
+ qemu_ram_remap(page->ram_addr, TARGET_PAGE_SIZE);
+ qemu_free(page);
+ }
+}
-static int get_para_features(CPUState *env)
+#ifdef KVM_CAP_MCE
+static void kvm_hwpoison_page_add(ram_addr_t ram_addr)
{
- int i, features = 0;
+ HWPoisonPage *page;
- for (i = 0; i < ARRAY_SIZE(para_features) - 1; i++) {
- if (kvm_check_extension(env->kvm_state, para_features[i].cap)) {
- features |= (1 << para_features[i].feature);
+ QLIST_FOREACH(page, &hwpoison_page_list, list) {
+ if (page->ram_addr == ram_addr) {
+ return;
}
}
- return features;
+ page = qemu_malloc(sizeof(HWPoisonPage));
+ page->ram_addr = ram_addr;
+ QLIST_INSERT_HEAD(&hwpoison_page_list, page, list);
}
-#endif
-#ifdef KVM_CAP_MCE
static int kvm_get_mce_cap_supported(KVMState *s, uint64_t *mce_cap,
int *max_banks)
{
return -ENOSYS;
}
-static int kvm_setup_mce(CPUState *env, uint64_t *mcg_cap)
+static void kvm_mce_inject(CPUState *env, target_phys_addr_t paddr, int code)
{
- return kvm_vcpu_ioctl(env, KVM_X86_SETUP_MCE, mcg_cap);
-}
+ uint64_t status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN |
+ MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S;
+ uint64_t mcg_status = MCG_STATUS_MCIP;
-static int kvm_set_mce(CPUState *env, struct kvm_x86_mce *m)
-{
- return kvm_vcpu_ioctl(env, KVM_X86_SET_MCE, m);
+ if (code == BUS_MCEERR_AR) {
+ status |= MCI_STATUS_AR | 0x134;
+ mcg_status |= MCG_STATUS_EIPV;
+ } else {
+ status |= 0xc0;
+ mcg_status |= MCG_STATUS_RIPV;
+ }
+ cpu_x86_inject_mce(NULL, env, 9, status, mcg_status, paddr,
+ (MCM_ADDR_PHYS << 6) | 0xc,
+ cpu_x86_support_mca_broadcast(env) ?
+ MCE_INJECT_BROADCAST : 0);
}
+#endif /* KVM_CAP_MCE */
-static int kvm_get_msr(CPUState *env, struct kvm_msr_entry *msrs, int n)
+static void hardware_memory_error(void)
{
- struct kvm_msrs *kmsrs = qemu_malloc(sizeof *kmsrs + n * sizeof *msrs);
- int r;
-
- kmsrs->nmsrs = n;
- memcpy(kmsrs->entries, msrs, n * sizeof *msrs);
- r = kvm_vcpu_ioctl(env, KVM_GET_MSRS, kmsrs);
- memcpy(msrs, kmsrs->entries, n * sizeof *msrs);
- free(kmsrs);
- return r;
+ fprintf(stderr, "Hardware memory error!\n");
+ exit(1);
}
-/* FIXME: kill this and kvm_get_msr, use env->mcg_status instead */
-static int kvm_mce_in_progress(CPUState *env)
+int kvm_arch_on_sigbus_vcpu(CPUState *env, int code, void *addr)
{
- struct kvm_msr_entry msr_mcg_status = {
- .index = MSR_MCG_STATUS,
- };
- int r;
+#ifdef KVM_CAP_MCE
+ ram_addr_t ram_addr;
+ target_phys_addr_t paddr;
- r = kvm_get_msr(env, &msr_mcg_status, 1);
- if (r == -1 || r == 0) {
- fprintf(stderr, "Failed to get MCE status\n");
- return 0;
+ if ((env->mcg_cap & MCG_SER_P) && addr
+ && (code == BUS_MCEERR_AR || code == BUS_MCEERR_AO)) {
+ if (qemu_ram_addr_from_host(addr, &ram_addr) ||
+ !kvm_physical_memory_addr_from_ram(env->kvm_state, ram_addr,
+ &paddr)) {
+ fprintf(stderr, "Hardware memory error for memory used by "
+ "QEMU itself instead of guest system!\n");
+ /* Hope we are lucky for AO MCE */
+ if (code == BUS_MCEERR_AO) {
+ return 0;
+ } else {
+ hardware_memory_error();
+ }
+ }
+ kvm_hwpoison_page_add(ram_addr);
+ kvm_mce_inject(env, paddr, code);
+ } else
+#endif /* KVM_CAP_MCE */
+ {
+ if (code == BUS_MCEERR_AO) {
+ return 0;
+ } else if (code == BUS_MCEERR_AR) {
+ hardware_memory_error();
+ } else {
+ return 1;
+ }
}
- return !!(msr_mcg_status.data & MCG_STATUS_MCIP);
+ return 0;
}
-struct kvm_x86_mce_data
+int kvm_arch_on_sigbus(int code, void *addr)
{
- CPUState *env;
- struct kvm_x86_mce *mce;
- int abort_on_error;
-};
-
-static void kvm_do_inject_x86_mce(void *_data)
-{
- struct kvm_x86_mce_data *data = _data;
- int r;
+#ifdef KVM_CAP_MCE
+ if ((first_cpu->mcg_cap & MCG_SER_P) && addr && code == BUS_MCEERR_AO) {
+ ram_addr_t ram_addr;
+ target_phys_addr_t paddr;
- /* If there is an MCE exception being processed, ignore this SRAO MCE */
- if ((data->env->mcg_cap & MCG_SER_P) &&
- !(data->mce->status & MCI_STATUS_AR)) {
- if (kvm_mce_in_progress(data->env)) {
- return;
+ /* Hope we are lucky for AO MCE */
+ if (qemu_ram_addr_from_host(addr, &ram_addr) ||
+ !kvm_physical_memory_addr_from_ram(first_cpu->kvm_state, ram_addr,
+ &paddr)) {
+ fprintf(stderr, "Hardware memory error for memory used by "
+ "QEMU itself instead of guest system!: %p\n", addr);
+ return 0;
}
- }
-
- r = kvm_set_mce(data->env, data->mce);
- if (r < 0) {
- perror("kvm_set_mce FAILED");
- if (data->abort_on_error) {
- abort();
+ kvm_hwpoison_page_add(ram_addr);
+ kvm_mce_inject(first_cpu, paddr, code);
+ } else
+#endif /* KVM_CAP_MCE */
+ {
+ if (code == BUS_MCEERR_AO) {
+ return 0;
+ } else if (code == BUS_MCEERR_AR) {
+ hardware_memory_error();
+ } else {
+ return 1;
}
}
+ return 0;
}
-static void kvm_inject_x86_mce_on(CPUState *env, struct kvm_x86_mce *mce,
- int flag)
+static int kvm_inject_mce_oldstyle(CPUState *env)
{
- struct kvm_x86_mce_data data = {
- .env = env,
- .mce = mce,
- .abort_on_error = (flag & ABORT_ON_ERROR),
- };
-
- if (!env->mcg_cap) {
- fprintf(stderr, "MCE support is not enabled!\n");
- return;
- }
+#ifdef KVM_CAP_MCE
+ if (!kvm_has_vcpu_events() && env->exception_injected == EXCP12_MCHK) {
+ unsigned int bank, bank_num = env->mcg_cap & 0xff;
+ struct kvm_x86_mce mce;
- run_on_cpu(env, kvm_do_inject_x86_mce, &data);
-}
+ env->exception_injected = -1;
-static void kvm_mce_broadcast_rest(CPUState *env);
-#endif
+ /*
+ * There must be at least one bank in use if an MCE is pending.
+ * Find it and use its values for the event injection.
+ */
+ for (bank = 0; bank < bank_num; bank++) {
+ if (env->mce_banks[bank * 4 + 1] & MCI_STATUS_VAL) {
+ break;
+ }
+ }
+ assert(bank < bank_num);
-void kvm_inject_x86_mce(CPUState *cenv, int bank, uint64_t status,
- uint64_t mcg_status, uint64_t addr, uint64_t misc,
- int flag)
-{
-#ifdef KVM_CAP_MCE
- struct kvm_x86_mce mce = {
- .bank = bank,
- .status = status,
- .mcg_status = mcg_status,
- .addr = addr,
- .misc = misc,
- };
+ mce.bank = bank;
+ mce.status = env->mce_banks[bank * 4 + 1];
+ mce.mcg_status = env->mcg_status;
+ mce.addr = env->mce_banks[bank * 4 + 2];
+ mce.misc = env->mce_banks[bank * 4 + 3];
- if (flag & MCE_BROADCAST) {
- kvm_mce_broadcast_rest(cenv);
+ return kvm_vcpu_ioctl(env, KVM_X86_SET_MCE, &mce);
}
+#endif /* KVM_CAP_MCE */
+ return 0;
+}
- kvm_inject_x86_mce_on(cenv, &mce, flag);
-#else
- if (flag & ABORT_ON_ERROR) {
- abort();
+static void cpu_update_state(void *opaque, int running, int reason)
+{
+ CPUState *env = opaque;
+
+ if (running) {
+ env->tsc_valid = false;
}
-#endif
}
int kvm_arch_init_vcpu(CPUState *env)
uint32_t limit, i, j, cpuid_i;
uint32_t unused;
struct kvm_cpuid_entry2 *c;
-#ifdef KVM_CPUID_SIGNATURE
uint32_t signature[3];
-#endif
env->cpuid_features &= kvm_arch_get_supported_cpuid(env, 1, 0, R_EDX);
cpuid_i = 0;
-#ifdef CONFIG_KVM_PARA
/* Paravirtualization CPUIDs */
memcpy(signature, "KVMKVMKVM\0\0\0", 12);
c = &cpuid_data.entries[cpuid_i++];
c = &cpuid_data.entries[cpuid_i++];
memset(c, 0, sizeof(*c));
c->function = KVM_CPUID_FEATURES;
- c->eax = env->cpuid_kvm_features & get_para_features(env);
+ c->eax = env->cpuid_kvm_features & kvm_arch_get_supported_cpuid(env,
+ KVM_CPUID_FEATURES, 0, R_EAX);
+
+#ifdef KVM_CAP_ASYNC_PF
+ has_msr_async_pf_en = c->eax & (1 << KVM_FEATURE_ASYNC_PF);
#endif
cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused);
cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx);
}
+ /* Call Centaur's CPUID instructions they are supported. */
+ if (env->cpuid_xlevel2 > 0) {
+ env->cpuid_ext4_features &=
+ kvm_arch_get_supported_cpuid(env, 0xC0000001, 0, R_EDX);
+ cpu_x86_cpuid(env, 0xC0000000, 0, &limit, &unused, &unused, &unused);
+
+ for (i = 0xC0000000; i <= limit; i++) {
+ c = &cpuid_data.entries[cpuid_i++];
+
+ c->function = i;
+ c->flags = 0;
+ cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx);
+ }
+ }
+
cpuid_data.cpuid.nent = cpuid_i;
#ifdef KVM_CAP_MCE
&& kvm_check_extension(env->kvm_state, KVM_CAP_MCE) > 0) {
uint64_t mcg_cap;
int banks;
+ int ret;
- if (kvm_get_mce_cap_supported(env->kvm_state, &mcg_cap, &banks)) {
- perror("kvm_get_mce_cap_supported FAILED");
- } else {
- if (banks > MCE_BANKS_DEF)
- banks = MCE_BANKS_DEF;
- mcg_cap &= MCE_CAP_DEF;
- mcg_cap |= banks;
- if (kvm_setup_mce(env, &mcg_cap)) {
- perror("kvm_setup_mce FAILED");
- } else {
- env->mcg_cap = mcg_cap;
- }
+ ret = kvm_get_mce_cap_supported(env->kvm_state, &mcg_cap, &banks);
+ if (ret < 0) {
+ fprintf(stderr, "kvm_get_mce_cap_supported: %s", strerror(-ret));
+ return ret;
+ }
+
+ if (banks > MCE_BANKS_DEF) {
+ banks = MCE_BANKS_DEF;
}
+ mcg_cap &= MCE_CAP_DEF;
+ mcg_cap |= banks;
+ ret = kvm_vcpu_ioctl(env, KVM_X86_SETUP_MCE, &mcg_cap);
+ if (ret < 0) {
+ fprintf(stderr, "KVM_X86_SETUP_MCE: %s", strerror(-ret));
+ return ret;
+ }
+
+ env->mcg_cap = mcg_cap;
}
#endif
+ qemu_add_vm_change_state_handler(cpu_update_state, env);
+
return kvm_vcpu_ioctl(env, KVM_SET_CPUID2, &cpuid_data);
}
{
env->exception_injected = -1;
env->interrupt_injected = -1;
- env->nmi_injected = 0;
- env->nmi_pending = 0;
env->xcr0 = 1;
if (kvm_irqchip_in_kernel()) {
env->mp_state = cpu_is_bsp(env) ? KVM_MP_STATE_RUNNABLE :
}
}
- free(kvm_msr_list);
+ qemu_free(kvm_msr_list);
}
return ret;
}
-static int kvm_init_identity_map_page(KVMState *s)
-{
-#ifdef KVM_CAP_SET_IDENTITY_MAP_ADDR
- int ret;
- uint64_t addr = 0xfffbc000;
-
- if (!kvm_check_extension(s, KVM_CAP_SET_IDENTITY_MAP_ADDR)) {
- return 0;
- }
-
- ret = kvm_vm_ioctl(s, KVM_SET_IDENTITY_MAP_ADDR, &addr);
- if (ret < 0) {
- fprintf(stderr, "kvm_set_identity_map_addr: %s\n", strerror(ret));
- return ret;
- }
-#endif
- return 0;
-}
-
-int kvm_arch_init(KVMState *s, int smp_cpus)
+int kvm_arch_init(KVMState *s)
{
+ uint64_t identity_base = 0xfffbc000;
int ret;
struct utsname utsname;
uname(&utsname);
lm_capable_kernel = strcmp(utsname.machine, "x86_64") == 0;
- /* create vm86 tss. KVM uses vm86 mode to emulate 16-bit code
- * directly. In order to use vm86 mode, a TSS is needed. Since this
- * must be part of guest physical memory, we need to allocate it. Older
- * versions of KVM just assumed that it would be at the end of physical
- * memory but that doesn't work with more than 4GB of memory. We simply
- * refuse to work with those older versions of KVM. */
- ret = kvm_check_extension(s, KVM_CAP_SET_TSS_ADDR);
- if (ret <= 0) {
- fprintf(stderr, "kvm does not support KVM_CAP_SET_TSS_ADDR\n");
- return ret;
- }
-
- /* this address is 3 pages before the bios, and the bios should present
- * as unavaible memory. FIXME, need to ensure the e820 map deals with
- * this?
- */
/*
- * Tell fw_cfg to notify the BIOS to reserve the range.
+ * On older Intel CPUs, KVM uses vm86 mode to emulate 16-bit code directly.
+ * In order to use vm86 mode, an EPT identity map and a TSS are needed.
+ * Since these must be part of guest physical memory, we need to allocate
+ * them, both by setting their start addresses in the kernel and by
+ * creating a corresponding e820 entry. We need 4 pages before the BIOS.
+ *
+ * Older KVM versions may not support setting the identity map base. In
+ * that case we need to stick with the default, i.e. a 256K maximum BIOS
+ * size.
*/
- if (e820_add_entry(0xfffbc000, 0x4000, E820_RESERVED) < 0) {
- perror("e820_add_entry() table is full");
- exit(1);
+#ifdef KVM_CAP_SET_IDENTITY_MAP_ADDR
+ if (kvm_check_extension(s, KVM_CAP_SET_IDENTITY_MAP_ADDR)) {
+ /* Allows up to 16M BIOSes. */
+ identity_base = 0xfeffc000;
+
+ ret = kvm_vm_ioctl(s, KVM_SET_IDENTITY_MAP_ADDR, &identity_base);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+#endif
+ /* Set TSS base one page after EPT identity map. */
+ ret = kvm_vm_ioctl(s, KVM_SET_TSS_ADDR, identity_base + 0x1000);
+ if (ret < 0) {
+ return ret;
}
- ret = kvm_vm_ioctl(s, KVM_SET_TSS_ADDR, 0xfffbd000);
+
+ /* Tell fw_cfg to notify the BIOS to reserve the range. */
+ ret = e820_add_entry(identity_base, 0x4000, E820_RESERVED);
if (ret < 0) {
+ fprintf(stderr, "e820_add_entry() table is full\n");
return ret;
}
+ qemu_register_reset(kvm_unpoison_all, NULL);
- return kvm_init_identity_map_page(s);
+ return 0;
}
static void set_v8086_seg(struct kvm_segment *lhs, const SegmentCache *rhs)
fpu.fsw = env->fpus & ~(7 << 11);
fpu.fsw |= (env->fpstt & 7) << 11;
fpu.fcw = env->fpuc;
+ fpu.last_opcode = env->fpop;
+ fpu.last_ip = env->fpip;
+ fpu.last_dp = env->fpdp;
for (i = 0; i < 8; ++i) {
fpu.ftwx |= (!env->fptags[i]) << i;
}
#ifdef KVM_CAP_XSAVE
int i, r;
struct kvm_xsave* xsave;
- uint16_t cwd, swd, twd, fop;
+ uint16_t cwd, swd, twd;
if (!kvm_has_xsave()) {
return kvm_put_fpu(env);
xsave = qemu_memalign(4096, sizeof(struct kvm_xsave));
memset(xsave, 0, sizeof(struct kvm_xsave));
- cwd = swd = twd = fop = 0;
+ cwd = swd = twd = 0;
swd = env->fpus & ~(7 << 11);
swd |= (env->fpstt & 7) << 11;
cwd = env->fpuc;
twd |= (!env->fptags[i]) << i;
}
xsave->region[0] = (uint32_t)(swd << 16) + cwd;
- xsave->region[1] = (uint32_t)(fop << 16) + twd;
+ xsave->region[1] = (uint32_t)(env->fpop << 16) + twd;
+ memcpy(&xsave->region[XSAVE_CWD_RIP], &env->fpip, sizeof(env->fpip));
+ memcpy(&xsave->region[XSAVE_CWD_RDP], &env->fpdp, sizeof(env->fpdp));
memcpy(&xsave->region[XSAVE_ST_SPACE], env->fpregs,
sizeof env->fpregs);
memcpy(&xsave->region[XSAVE_XMM_SPACE], env->xmm_regs,
kvm_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_CS, env->sysenter_cs);
kvm_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_ESP, env->sysenter_esp);
kvm_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_EIP, env->sysenter_eip);
+ kvm_msr_entry_set(&msrs[n++], MSR_PAT, env->pat);
if (has_msr_star) {
kvm_msr_entry_set(&msrs[n++], MSR_STAR, env->star);
}
if (smp_cpus == 1 || env->tsc != 0) {
kvm_msr_entry_set(&msrs[n++], MSR_IA32_TSC, env->tsc);
}
+ }
+ /*
+ * The following paravirtual MSRs have side effects on the guest or are
+ * too heavy for normal writeback. Limit them to reset or full state
+ * updates.
+ */
+ if (level >= KVM_PUT_RESET_STATE) {
kvm_msr_entry_set(&msrs[n++], MSR_KVM_SYSTEM_TIME,
env->system_time_msr);
kvm_msr_entry_set(&msrs[n++], MSR_KVM_WALL_CLOCK, env->wall_clock_msr);
-#ifdef KVM_CAP_ASYNC_PF
- kvm_msr_entry_set(&msrs[n++], MSR_KVM_ASYNC_PF_EN, env->async_pf_en_msr);
-#endif
+ if (has_msr_async_pf_en) {
+ kvm_msr_entry_set(&msrs[n++], MSR_KVM_ASYNC_PF_EN,
+ env->async_pf_en_msr);
+ }
}
#ifdef KVM_CAP_MCE
if (env->mcg_cap) {
int i;
- if (level == KVM_PUT_RESET_STATE) {
- kvm_msr_entry_set(&msrs[n++], MSR_MCG_STATUS, env->mcg_status);
- } else if (level == KVM_PUT_FULL_STATE) {
- kvm_msr_entry_set(&msrs[n++], MSR_MCG_STATUS, env->mcg_status);
- kvm_msr_entry_set(&msrs[n++], MSR_MCG_CTL, env->mcg_ctl);
- for (i = 0; i < (env->mcg_cap & 0xff) * 4; i++) {
- kvm_msr_entry_set(&msrs[n++], MSR_MC0_CTL + i, env->mce_banks[i]);
- }
+ kvm_msr_entry_set(&msrs[n++], MSR_MCG_STATUS, env->mcg_status);
+ kvm_msr_entry_set(&msrs[n++], MSR_MCG_CTL, env->mcg_ctl);
+ for (i = 0; i < (env->mcg_cap & 0xff) * 4; i++) {
+ kvm_msr_entry_set(&msrs[n++], MSR_MC0_CTL + i, env->mce_banks[i]);
}
}
#endif
env->fpstt = (fpu.fsw >> 11) & 7;
env->fpus = fpu.fsw;
env->fpuc = fpu.fcw;
+ env->fpop = fpu.last_opcode;
+ env->fpip = fpu.last_ip;
+ env->fpdp = fpu.last_dp;
for (i = 0; i < 8; ++i) {
env->fptags[i] = !((fpu.ftwx >> i) & 1);
}
#ifdef KVM_CAP_XSAVE
struct kvm_xsave* xsave;
int ret, i;
- uint16_t cwd, swd, twd, fop;
+ uint16_t cwd, swd, twd;
if (!kvm_has_xsave()) {
return kvm_get_fpu(env);
cwd = (uint16_t)xsave->region[0];
swd = (uint16_t)(xsave->region[0] >> 16);
twd = (uint16_t)xsave->region[1];
- fop = (uint16_t)(xsave->region[1] >> 16);
+ env->fpop = (uint16_t)(xsave->region[1] >> 16);
env->fpstt = (swd >> 11) & 7;
env->fpus = swd;
env->fpuc = cwd;
for (i = 0; i < 8; ++i) {
env->fptags[i] = !((twd >> i) & 1);
}
+ memcpy(&env->fpip, &xsave->region[XSAVE_CWD_RIP], sizeof(env->fpip));
+ memcpy(&env->fpdp, &xsave->region[XSAVE_CWD_RDP], sizeof(env->fpdp));
env->mxcsr = xsave->region[XSAVE_MXCSR];
memcpy(env->fpregs, &xsave->region[XSAVE_ST_SPACE],
sizeof env->fpregs);
msrs[n++].index = MSR_IA32_SYSENTER_CS;
msrs[n++].index = MSR_IA32_SYSENTER_ESP;
msrs[n++].index = MSR_IA32_SYSENTER_EIP;
+ msrs[n++].index = MSR_PAT;
if (has_msr_star) {
msrs[n++].index = MSR_STAR;
}
if (has_msr_hsave_pa) {
msrs[n++].index = MSR_VM_HSAVE_PA;
}
- msrs[n++].index = MSR_IA32_TSC;
+
+ if (!env->tsc_valid) {
+ msrs[n++].index = MSR_IA32_TSC;
+ env->tsc_valid = !vm_running;
+ }
+
#ifdef TARGET_X86_64
if (lm_capable_kernel) {
msrs[n++].index = MSR_CSTAR;
#endif
msrs[n++].index = MSR_KVM_SYSTEM_TIME;
msrs[n++].index = MSR_KVM_WALL_CLOCK;
-#ifdef KVM_CAP_ASYNC_PF
- msrs[n++].index = MSR_KVM_ASYNC_PF_EN;
-#endif
+ if (has_msr_async_pf_en) {
+ msrs[n++].index = MSR_KVM_ASYNC_PF_EN;
+ }
#ifdef KVM_CAP_MCE
if (env->mcg_cap) {
case MSR_IA32_SYSENTER_EIP:
env->sysenter_eip = msrs[i].data;
break;
+ case MSR_PAT:
+ env->pat = msrs[i].data;
+ break;
case MSR_STAR:
env->star = msrs[i].data;
break;
}
#endif
break;
-#ifdef KVM_CAP_ASYNC_PF
case MSR_KVM_ASYNC_PF_EN:
env->async_pf_en_msr = msrs[i].data;
break;
-#endif
}
}
{
int ret;
- assert(cpu_is_stopped(env) || qemu_cpu_self(env));
+ assert(cpu_is_stopped(env) || qemu_cpu_is_self(env));
ret = kvm_getput_regs(env, 1);
if (ret < 0) {
if (ret < 0) {
return ret;
}
+ /* must be before kvm_put_msrs */
+ ret = kvm_inject_mce_oldstyle(env);
+ if (ret < 0) {
+ return ret;
+ }
ret = kvm_put_msrs(env, level);
if (ret < 0) {
return ret;
{
int ret;
- assert(cpu_is_stopped(env) || qemu_cpu_self(env));
+ assert(cpu_is_stopped(env) || qemu_cpu_is_self(env));
ret = kvm_getput_regs(env, 0);
if (ret < 0) {
return 0;
}
-int kvm_arch_pre_run(CPUState *env, struct kvm_run *run)
+void kvm_arch_pre_run(CPUState *env, struct kvm_run *run)
{
+ int ret;
+
/* Inject NMI */
if (env->interrupt_request & CPU_INTERRUPT_NMI) {
env->interrupt_request &= ~CPU_INTERRUPT_NMI;
DPRINTF("injected NMI\n");
- kvm_vcpu_ioctl(env, KVM_NMI);
- }
-
- /* Try to inject an interrupt if the guest can accept it */
- if (run->ready_for_interrupt_injection &&
- (env->interrupt_request & CPU_INTERRUPT_HARD) &&
- (env->eflags & IF_MASK)) {
- int irq;
-
- env->interrupt_request &= ~CPU_INTERRUPT_HARD;
- irq = cpu_get_pic_interrupt(env);
- if (irq >= 0) {
- struct kvm_interrupt intr;
- intr.irq = irq;
- /* FIXME: errors */
- DPRINTF("injected interrupt %d\n", irq);
- kvm_vcpu_ioctl(env, KVM_INTERRUPT, &intr);
+ ret = kvm_vcpu_ioctl(env, KVM_NMI);
+ if (ret < 0) {
+ fprintf(stderr, "KVM: injection failed, NMI lost (%s)\n",
+ strerror(-ret));
}
}
- /* If we have an interrupt but the guest is not ready to receive an
- * interrupt, request an interrupt window exit. This will
- * cause a return to userspace as soon as the guest is ready to
- * receive interrupts. */
- if ((env->interrupt_request & CPU_INTERRUPT_HARD)) {
- run->request_interrupt_window = 1;
- } else {
- run->request_interrupt_window = 0;
- }
+ if (!kvm_irqchip_in_kernel()) {
+ /* Force the VCPU out of its inner loop to process the INIT request */
+ if (env->interrupt_request & CPU_INTERRUPT_INIT) {
+ env->exit_request = 1;
+ }
- DPRINTF("setting tpr\n");
- run->cr8 = cpu_get_apic_tpr(env->apic_state);
+ /* Try to inject an interrupt if the guest can accept it */
+ if (run->ready_for_interrupt_injection &&
+ (env->interrupt_request & CPU_INTERRUPT_HARD) &&
+ (env->eflags & IF_MASK)) {
+ int irq;
+
+ env->interrupt_request &= ~CPU_INTERRUPT_HARD;
+ irq = cpu_get_pic_interrupt(env);
+ if (irq >= 0) {
+ struct kvm_interrupt intr;
+
+ intr.irq = irq;
+ DPRINTF("injected interrupt %d\n", irq);
+ ret = kvm_vcpu_ioctl(env, KVM_INTERRUPT, &intr);
+ if (ret < 0) {
+ fprintf(stderr,
+ "KVM: injection failed, interrupt lost (%s)\n",
+ strerror(-ret));
+ }
+ }
+ }
- return 0;
+ /* If we have an interrupt but the guest is not ready to receive an
+ * interrupt, request an interrupt window exit. This will
+ * cause a return to userspace as soon as the guest is ready to
+ * receive interrupts. */
+ if ((env->interrupt_request & CPU_INTERRUPT_HARD)) {
+ run->request_interrupt_window = 1;
+ } else {
+ run->request_interrupt_window = 0;
+ }
+
+ DPRINTF("setting tpr\n");
+ run->cr8 = cpu_get_apic_tpr(env->apic_state);
+ }
}
-int kvm_arch_post_run(CPUState *env, struct kvm_run *run)
+void kvm_arch_post_run(CPUState *env, struct kvm_run *run)
{
if (run->if_flag) {
env->eflags |= IF_MASK;
}
cpu_set_apic_tpr(env->apic_state, run->cr8);
cpu_set_apic_base(env->apic_state, run->apic_base);
-
- return 0;
}
-int kvm_arch_process_irqchip_events(CPUState *env)
+int kvm_arch_process_async_events(CPUState *env)
{
+ if (env->interrupt_request & CPU_INTERRUPT_MCE) {
+ /* We must not raise CPU_INTERRUPT_MCE if it's not supported. */
+ assert(env->mcg_cap);
+
+ env->interrupt_request &= ~CPU_INTERRUPT_MCE;
+
+ kvm_cpu_synchronize_state(env);
+
+ if (env->exception_injected == EXCP08_DBLE) {
+ /* this means triple fault */
+ qemu_system_reset_request();
+ env->exit_request = 1;
+ return 0;
+ }
+ env->exception_injected = EXCP12_MCHK;
+ env->has_error_code = 0;
+
+ env->halted = 0;
+ if (kvm_irqchip_in_kernel() && env->mp_state == KVM_MP_STATE_HALTED) {
+ env->mp_state = KVM_MP_STATE_RUNNABLE;
+ }
+ }
+
+ if (kvm_irqchip_in_kernel()) {
+ return 0;
+ }
+
+ if (((env->interrupt_request & CPU_INTERRUPT_HARD) &&
+ (env->eflags & IF_MASK)) ||
+ (env->interrupt_request & CPU_INTERRUPT_NMI)) {
+ env->halted = 0;
+ }
if (env->interrupt_request & CPU_INTERRUPT_INIT) {
kvm_cpu_synchronize_state(env);
do_cpu_init(env);
- env->exception_index = EXCP_HALTED;
}
-
if (env->interrupt_request & CPU_INTERRUPT_SIPI) {
kvm_cpu_synchronize_state(env);
do_cpu_sipi(env);
(env->eflags & IF_MASK)) &&
!(env->interrupt_request & CPU_INTERRUPT_NMI)) {
env->halted = 1;
- env->exception_index = EXCP_HLT;
- return 0;
+ return EXCP_HLT;
}
- return 1;
-}
-
-static bool host_supports_vmx(void)
-{
- uint32_t ecx, unused;
-
- host_cpuid(1, 0, &unused, &unused, &ecx, &unused);
- return ecx & CPUID_EXT_VMX;
-}
-
-#define VMX_INVALID_GUEST_STATE 0x80000021
-
-int kvm_arch_handle_exit(CPUState *env, struct kvm_run *run)
-{
- uint64_t code;
- int ret = 0;
-
- switch (run->exit_reason) {
- case KVM_EXIT_HLT:
- DPRINTF("handle_hlt\n");
- ret = kvm_handle_halt(env);
- break;
- case KVM_EXIT_SET_TPR:
- ret = 1;
- break;
- case KVM_EXIT_FAIL_ENTRY:
- code = run->fail_entry.hardware_entry_failure_reason;
- fprintf(stderr, "KVM: entry failed, hardware error 0x%" PRIx64 "\n",
- code);
- if (host_supports_vmx() && code == VMX_INVALID_GUEST_STATE) {
- fprintf(stderr,
- "\nIf you're runnning a guest on an Intel machine without "
- "unrestricted mode\n"
- "support, the failure can be most likely due to the guest "
- "entering an invalid\n"
- "state for Intel VT. For example, the guest maybe running "
- "in big real mode\n"
- "which is not supported on less recent Intel processors."
- "\n\n");
- }
- ret = -1;
- break;
- case KVM_EXIT_EXCEPTION:
- fprintf(stderr, "KVM: exception %d exit (error code 0x%x)\n",
- run->ex.exception, run->ex.error_code);
- ret = -1;
- break;
- default:
- fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason);
- ret = -1;
- break;
- }
-
- return ret;
+ return 0;
}
#ifdef KVM_CAP_SET_GUEST_DEBUG
static CPUWatchpoint hw_watchpoint;
-int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info)
+static int kvm_handle_debug(struct kvm_debug_exit_arch *arch_info)
{
- int handle = 0;
+ int ret = 0;
int n;
if (arch_info->exception == 1) {
if (arch_info->dr6 & (1 << 14)) {
if (cpu_single_env->singlestep_enabled) {
- handle = 1;
+ ret = EXCP_DEBUG;
}
} else {
for (n = 0; n < 4; n++) {
if (arch_info->dr6 & (1 << n)) {
switch ((arch_info->dr7 >> (16 + n*4)) & 0x3) {
case 0x0:
- handle = 1;
+ ret = EXCP_DEBUG;
break;
case 0x1:
- handle = 1;
+ ret = EXCP_DEBUG;
cpu_single_env->watchpoint_hit = &hw_watchpoint;
hw_watchpoint.vaddr = hw_breakpoint[n].addr;
hw_watchpoint.flags = BP_MEM_WRITE;
break;
case 0x3:
- handle = 1;
+ ret = EXCP_DEBUG;
cpu_single_env->watchpoint_hit = &hw_watchpoint;
hw_watchpoint.vaddr = hw_breakpoint[n].addr;
hw_watchpoint.flags = BP_MEM_ACCESS;
}
}
} else if (kvm_find_sw_breakpoint(cpu_single_env, arch_info->pc)) {
- handle = 1;
+ ret = EXCP_DEBUG;
}
- if (!handle) {
+ if (ret == 0) {
cpu_synchronize_state(cpu_single_env);
assert(cpu_single_env->exception_injected == -1);
+ /* pass to guest */
cpu_single_env->exception_injected = arch_info->exception;
cpu_single_env->has_error_code = 0;
}
- return handle;
+ return ret;
}
void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg)
}
#endif /* KVM_CAP_SET_GUEST_DEBUG */
-bool kvm_arch_stop_on_emulation_error(CPUState *env)
-{
- return !(env->cr[0] & CR0_PE_MASK) ||
- ((env->segs[R_CS].selector & 3) != 3);
-}
-
-static void hardware_memory_error(void)
-{
- fprintf(stderr, "Hardware memory error!\n");
- exit(1);
-}
-
-#ifdef KVM_CAP_MCE
-static void kvm_mce_broadcast_rest(CPUState *env)
-{
- struct kvm_x86_mce mce = {
- .bank = 1,
- .status = MCI_STATUS_VAL | MCI_STATUS_UC,
- .mcg_status = MCG_STATUS_MCIP | MCG_STATUS_RIPV,
- .addr = 0,
- .misc = 0,
- };
- CPUState *cenv;
-
- /* Broadcast MCA signal for processor version 06H_EH and above */
- if (cpu_x86_support_mca_broadcast(env)) {
- for (cenv = first_cpu; cenv != NULL; cenv = cenv->next_cpu) {
- if (cenv == env) {
- continue;
- }
- kvm_inject_x86_mce_on(cenv, &mce, ABORT_ON_ERROR);
- }
- }
-}
-
-static void kvm_mce_inj_srar_dataload(CPUState *env, target_phys_addr_t paddr)
-{
- struct kvm_x86_mce mce = {
- .bank = 9,
- .status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN
- | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S
- | MCI_STATUS_AR | 0x134,
- .mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV,
- .addr = paddr,
- .misc = (MCM_ADDR_PHYS << 6) | 0xc,
- };
- int r;
-
- r = kvm_set_mce(env, &mce);
- if (r < 0) {
- fprintf(stderr, "kvm_set_mce: %s\n", strerror(errno));
- abort();
- }
- kvm_mce_broadcast_rest(env);
-}
-
-static void kvm_mce_inj_srao_memscrub(CPUState *env, target_phys_addr_t paddr)
-{
- struct kvm_x86_mce mce = {
- .bank = 9,
- .status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN
- | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S
- | 0xc0,
- .mcg_status = MCG_STATUS_MCIP | MCG_STATUS_RIPV,
- .addr = paddr,
- .misc = (MCM_ADDR_PHYS << 6) | 0xc,
- };
- int r;
-
- r = kvm_set_mce(env, &mce);
- if (r < 0) {
- fprintf(stderr, "kvm_set_mce: %s\n", strerror(errno));
- abort();
- }
- kvm_mce_broadcast_rest(env);
-}
-
-static void kvm_mce_inj_srao_memscrub2(CPUState *env, target_phys_addr_t paddr)
+static bool host_supports_vmx(void)
{
- struct kvm_x86_mce mce = {
- .bank = 9,
- .status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN
- | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S
- | 0xc0,
- .mcg_status = MCG_STATUS_MCIP | MCG_STATUS_RIPV,
- .addr = paddr,
- .misc = (MCM_ADDR_PHYS << 6) | 0xc,
- };
+ uint32_t ecx, unused;
- kvm_inject_x86_mce_on(env, &mce, ABORT_ON_ERROR);
- kvm_mce_broadcast_rest(env);
+ host_cpuid(1, 0, &unused, &unused, &ecx, &unused);
+ return ecx & CPUID_EXT_VMX;
}
-#endif
+#define VMX_INVALID_GUEST_STATE 0x80000021
-int kvm_on_sigbus_vcpu(CPUState *env, int code, void *addr)
+int kvm_arch_handle_exit(CPUState *env, struct kvm_run *run)
{
-#if defined(KVM_CAP_MCE)
- void *vaddr;
- ram_addr_t ram_addr;
- target_phys_addr_t paddr;
-
- if ((env->mcg_cap & MCG_SER_P) && addr
- && (code == BUS_MCEERR_AR
- || code == BUS_MCEERR_AO)) {
- vaddr = (void *)addr;
- if (qemu_ram_addr_from_host(vaddr, &ram_addr) ||
- !kvm_physical_memory_addr_from_ram(env->kvm_state, ram_addr, &paddr)) {
- fprintf(stderr, "Hardware memory error for memory used by "
- "QEMU itself instead of guest system!\n");
- /* Hope we are lucky for AO MCE */
- if (code == BUS_MCEERR_AO) {
- return 0;
- } else {
- hardware_memory_error();
- }
- }
+ uint64_t code;
+ int ret;
- if (code == BUS_MCEERR_AR) {
- /* Fake an Intel architectural Data Load SRAR UCR */
- kvm_mce_inj_srar_dataload(env, paddr);
- } else {
- /*
- * If there is an MCE excpetion being processed, ignore
- * this SRAO MCE
- */
- if (!kvm_mce_in_progress(env)) {
- /* Fake an Intel architectural Memory scrubbing UCR */
- kvm_mce_inj_srao_memscrub(env, paddr);
- }
- }
- } else
-#endif
- {
- if (code == BUS_MCEERR_AO) {
- return 0;
- } else if (code == BUS_MCEERR_AR) {
- hardware_memory_error();
- } else {
- return 1;
+ switch (run->exit_reason) {
+ case KVM_EXIT_HLT:
+ DPRINTF("handle_hlt\n");
+ ret = kvm_handle_halt(env);
+ break;
+ case KVM_EXIT_SET_TPR:
+ ret = 0;
+ break;
+ case KVM_EXIT_FAIL_ENTRY:
+ code = run->fail_entry.hardware_entry_failure_reason;
+ fprintf(stderr, "KVM: entry failed, hardware error 0x%" PRIx64 "\n",
+ code);
+ if (host_supports_vmx() && code == VMX_INVALID_GUEST_STATE) {
+ fprintf(stderr,
+ "\nIf you're runnning a guest on an Intel machine without "
+ "unrestricted mode\n"
+ "support, the failure can be most likely due to the guest "
+ "entering an invalid\n"
+ "state for Intel VT. For example, the guest maybe running "
+ "in big real mode\n"
+ "which is not supported on less recent Intel processors."
+ "\n\n");
}
+ ret = -1;
+ break;
+ case KVM_EXIT_EXCEPTION:
+ fprintf(stderr, "KVM: exception %d exit (error code 0x%x)\n",
+ run->ex.exception, run->ex.error_code);
+ ret = -1;
+ break;
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ case KVM_EXIT_DEBUG:
+ DPRINTF("kvm_exit_debug\n");
+ ret = kvm_handle_debug(&run->debug.arch);
+ break;
+#endif /* KVM_CAP_SET_GUEST_DEBUG */
+ default:
+ fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason);
+ ret = -1;
+ break;
}
- return 0;
+
+ return ret;
}
-int kvm_on_sigbus(int code, void *addr)
+bool kvm_arch_stop_on_emulation_error(CPUState *env)
{
-#if defined(KVM_CAP_MCE)
- if ((first_cpu->mcg_cap & MCG_SER_P) && addr && code == BUS_MCEERR_AO) {
- void *vaddr;
- ram_addr_t ram_addr;
- target_phys_addr_t paddr;
-
- /* Hope we are lucky for AO MCE */
- vaddr = addr;
- if (qemu_ram_addr_from_host(vaddr, &ram_addr) ||
- !kvm_physical_memory_addr_from_ram(first_cpu->kvm_state, ram_addr, &paddr)) {
- fprintf(stderr, "Hardware memory error for memory used by "
- "QEMU itself instead of guest system!: %p\n", addr);
- return 0;
- }
- kvm_mce_inj_srao_memscrub2(first_cpu, paddr);
- } else
-#endif
- {
- if (code == BUS_MCEERR_AO) {
- return 0;
- } else if (code == BUS_MCEERR_AR) {
- hardware_memory_error();
- } else {
- return 1;
- }
- }
- return 0;
+ return !(env->cr[0] & CR0_PE_MASK) ||
+ ((env->segs[R_CS].selector & 3) != 3);
}