diff options
Diffstat (limited to 'target')
-rw-r--r-- | target/arm/kvm.c | 5 | ||||
-rw-r--r-- | target/i386/cpu.c | 1 | ||||
-rw-r--r-- | target/i386/cpu.h | 1 | ||||
-rw-r--r-- | target/i386/hvf/hvf-i386.h | 16 | ||||
-rw-r--r-- | target/i386/hvf/hvf.c | 100 | ||||
-rw-r--r-- | target/i386/hvf/x86_cpuid.c | 34 | ||||
-rw-r--r-- | target/i386/hvf/x86_emu.c | 5 | ||||
-rw-r--r-- | target/i386/kvm/kvm.c | 10 | ||||
-rw-r--r-- | target/i386/monitor.c | 23 | ||||
-rw-r--r-- | target/i386/sev-stub.c | 15 | ||||
-rw-r--r-- | target/i386/sev.c | 178 | ||||
-rw-r--r-- | target/i386/sev_i386.h | 2 | ||||
-rw-r--r-- | target/mips/kvm.c | 5 | ||||
-rw-r--r-- | target/ppc/kvm.c | 5 | ||||
-rw-r--r-- | target/s390x/kvm.c | 5 |
15 files changed, 371 insertions, 34 deletions
diff --git a/target/arm/kvm.c b/target/arm/kvm.c index ffe186de8d..00e124c812 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -1045,3 +1045,8 @@ int kvm_arch_msi_data_to_gsi(uint32_t data) { return (data - 32) & 0xffff; } + +bool kvm_arch_cpu_check_are_resettable(void) +{ + return true; +} diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 9c3d2d60b7..20c3a5af3f 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5984,6 +5984,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 0x8000001F: *eax = sev_enabled() ? 0x2 : 0; + *eax |= sev_es_enabled() ? 0x8 : 0; *ebx = sev_get_cbit_position(); *ebx |= sev_get_reduced_phys_bits() << 6; *ecx = 0; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 8d599bb5b8..82c1ac00ef 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -368,6 +368,7 @@ typedef enum X86Seg { #define MSR_IA32_SMBASE 0x9e #define MSR_SMI_COUNT 0x34 +#define MSR_CORE_THREAD_COUNT 0x35 #define MSR_MTRRcap 0xfe #define MSR_MTRRcap_VCNT 8 #define MSR_MTRRcap_FIXRANGE_SUPPORT (1 << 8) diff --git a/target/i386/hvf/hvf-i386.h b/target/i386/hvf/hvf-i386.h index 50b914fd67..59cfca8875 100644 --- a/target/i386/hvf/hvf-i386.h +++ b/target/i386/hvf/hvf-i386.h @@ -21,21 +21,6 @@ #include "cpu.h" #include "x86.h" -#define HVF_MAX_VCPU 0x10 - -extern struct hvf_state hvf_global; - -struct hvf_vm { - int id; - struct hvf_vcpu_state *vcpus[HVF_MAX_VCPU]; -}; - -struct hvf_state { - uint32_t version; - struct hvf_vm *vm; - uint64_t mem_quota; -}; - /* hvf_slot flags */ #define HVF_SLOT_LOG (1 << 0) @@ -75,7 +60,6 @@ hvf_slot *hvf_find_overlap_slot(uint64_t, uint64_t); /* Host specific functions */ int hvf_inject_interrupt(CPUArchState *env, int vector); -int hvf_vcpu_run(struct hvf_vcpu_state *vcpu); #endif #endif diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 5b90dcdf88..15f14ac69e 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -65,6 +65,7 @@ #include <Hypervisor/hv.h> #include <Hypervisor/hv_vmx.h> +#include <sys/sysctl.h> #include "exec/address-spaces.h" #include "hw/i386/apic_internal.h" @@ -456,6 +457,48 @@ static void dummy_signal(int sig) { } +static void init_tsc_freq(CPUX86State *env) +{ + size_t length; + uint64_t tsc_freq; + + if (env->tsc_khz != 0) { + return; + } + + length = sizeof(uint64_t); + if (sysctlbyname("machdep.tsc.frequency", &tsc_freq, &length, NULL, 0)) { + return; + } + env->tsc_khz = tsc_freq / 1000; /* Hz to KHz */ +} + +static void init_apic_bus_freq(CPUX86State *env) +{ + size_t length; + uint64_t bus_freq; + + if (env->apic_bus_freq != 0) { + return; + } + + length = sizeof(uint64_t); + if (sysctlbyname("hw.busfrequency", &bus_freq, &length, NULL, 0)) { + return; + } + env->apic_bus_freq = bus_freq; +} + +static inline bool tsc_is_known(CPUX86State *env) +{ + return env->tsc_khz != 0; +} + +static inline bool apic_bus_freq_is_known(CPUX86State *env) +{ + return env->apic_bus_freq != 0; +} + int hvf_init_vcpu(CPUState *cpu) { @@ -480,6 +523,15 @@ int hvf_init_vcpu(CPUState *cpu) hvf_state->hvf_caps = g_new0(struct hvf_vcpu_caps, 1); env->hvf_mmio_buf = g_new(char, 4096); + if (x86cpu->vmware_cpuid_freq) { + init_tsc_freq(env); + init_apic_bus_freq(env); + + if (!tsc_is_known(env) || !apic_bus_freq_is_known(env)) { + error_report("vmware-cpuid-freq: feature couldn't be enabled"); + } + } + r = hv_vcpu_create((hv_vcpuid_t *)&cpu->hvf_fd, HV_VCPU_DEFAULT); cpu->vcpu_dirty = 1; assert_hvf_ok(r); @@ -597,6 +649,48 @@ static void hvf_store_events(CPUState *cpu, uint32_t ins_len, uint64_t idtvec_in } } +static void hvf_cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + /* + * A wrapper extends cpu_x86_cpuid with 0x40000000 and 0x40000010 leafs, + * leafs 0x40000001-0x4000000F are filled with zeros + * Provides vmware-cpuid-freq support to hvf + * + * Note: leaf 0x40000000 not exposes HVF, + * leaving hypervisor signature empty + */ + + if (index < 0x40000000 || index > 0x40000010 || + !tsc_is_known(env) || !apic_bus_freq_is_known(env)) { + + cpu_x86_cpuid(env, index, count, eax, ebx, ecx, edx); + return; + } + + switch (index) { + case 0x40000000: + *eax = 0x40000010; /* Max available cpuid leaf */ + *ebx = 0; /* Leave signature empty */ + *ecx = 0; + *edx = 0; + break; + case 0x40000010: + *eax = env->tsc_khz; + *ebx = env->apic_bus_freq / 1000; /* Hz to KHz */ + *ecx = 0; + *edx = 0; + break; + default: + *eax = 0; + *ebx = 0; + *ecx = 0; + *edx = 0; + break; + } +} + int hvf_vcpu_exec(CPUState *cpu) { X86CPU *x86_cpu = X86_CPU(cpu); @@ -734,7 +828,11 @@ int hvf_vcpu_exec(CPUState *cpu) uint32_t rcx = (uint32_t)rreg(cpu->hvf_fd, HV_X86_RCX); uint32_t rdx = (uint32_t)rreg(cpu->hvf_fd, HV_X86_RDX); - cpu_x86_cpuid(env, rax, rcx, &rax, &rbx, &rcx, &rdx); + if (rax == 1) { + /* CPUID1.ecx.OSXSAVE needs to know CR4 */ + env->cr[4] = rvmcs(cpu->hvf_fd, VMCS_GUEST_CR4); + } + hvf_cpu_x86_cpuid(env, rax, rcx, &rax, &rbx, &rcx, &rdx); wreg(cpu->hvf_fd, HV_X86_RAX, rax); wreg(cpu->hvf_fd, HV_X86_RBX, rbx); diff --git a/target/i386/hvf/x86_cpuid.c b/target/i386/hvf/x86_cpuid.c index a6842912f5..32b0d131df 100644 --- a/target/i386/hvf/x86_cpuid.c +++ b/target/i386/hvf/x86_cpuid.c @@ -27,15 +27,22 @@ #include "vmx.h" #include "sysemu/hvf.h" -static uint64_t xgetbv(uint32_t xcr) +static bool xgetbv(uint32_t cpuid_ecx, uint32_t idx, uint64_t *xcr) { - uint32_t eax, edx; + uint32_t xcrl, xcrh; - __asm__ volatile ("xgetbv" - : "=a" (eax), "=d" (edx) - : "c" (xcr)); + if (cpuid_ecx & CPUID_EXT_OSXSAVE) { + /* + * The xgetbv instruction is not available to older versions of + * the assembler, so we encode the instruction manually. + */ + asm(".byte 0x0f, 0x01, 0xd0" : "=a" (xcrl), "=d" (xcrh) : "c" (idx)); - return (((uint64_t)edx) << 32) | eax; + *xcr = (((uint64_t)xcrh) << 32) | xcrl; + return true; + } + + return false; } uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, @@ -100,12 +107,15 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, break; case 0xD: if (idx == 0) { - uint64_t host_xcr0 = xgetbv(0); - uint64_t supp_xcr0 = host_xcr0 & (XSTATE_FP_MASK | XSTATE_SSE_MASK | - XSTATE_YMM_MASK | XSTATE_BNDREGS_MASK | - XSTATE_BNDCSR_MASK | XSTATE_OPMASK_MASK | - XSTATE_ZMM_Hi256_MASK | XSTATE_Hi16_ZMM_MASK); - eax &= supp_xcr0; + uint64_t host_xcr0; + if (xgetbv(ecx, 0, &host_xcr0)) { + uint64_t supp_xcr0 = host_xcr0 & (XSTATE_FP_MASK | + XSTATE_SSE_MASK | XSTATE_YMM_MASK | + XSTATE_BNDREGS_MASK | XSTATE_BNDCSR_MASK | + XSTATE_OPMASK_MASK | XSTATE_ZMM_Hi256_MASK | + XSTATE_Hi16_ZMM_MASK); + eax &= supp_xcr0; + } } else if (idx == 1) { hv_vmx_read_capability(HV_VMX_CAP_PROCBASED2, &cap); eax &= CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XGETBV1; diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index da570e352b..e52c39ddb1 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -668,6 +668,7 @@ void simulate_rdmsr(struct CPUState *cpu) { X86CPU *x86_cpu = X86_CPU(cpu); CPUX86State *env = &x86_cpu->env; + CPUState *cs = env_cpu(env); uint32_t msr = ECX(env); uint64_t val = 0; @@ -745,6 +746,10 @@ void simulate_rdmsr(struct CPUState *cpu) case MSR_MTRRdefType: val = env->mtrr_deftype; break; + case MSR_CORE_THREAD_COUNT: + val = cs->nr_threads * cs->nr_cores; /* thread count, bits 15..0 */ + val |= ((uint32_t)cs->nr_cores << 16); /* core count, bits 31..16 */ + break; default: /* fprintf(stderr, "%s: unknown msr 0x%x\n", __func__, msr); */ val = 0; diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index e97f841757..0b5755e42b 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -27,6 +27,7 @@ #include "sysemu/kvm_int.h" #include "sysemu/runstate.h" #include "kvm_i386.h" +#include "sev_i386.h" #include "hyperv.h" #include "hyperv-proto.h" @@ -136,7 +137,7 @@ int kvm_has_pit_state2(void) bool kvm_has_smm(void) { - return kvm_check_extension(kvm_state, KVM_CAP_X86_SMM); + return kvm_vm_check_extension(kvm_state, KVM_CAP_X86_SMM); } bool kvm_has_adjust_clock_stable(void) @@ -1922,6 +1923,8 @@ void kvm_arch_reset_vcpu(X86CPU *cpu) } /* enabled by default */ env->poll_control_msr = 1; + + sev_es_set_reset_vector(CPU(cpu)); } void kvm_arch_do_init_vcpu(X86CPU *cpu) @@ -4819,3 +4822,8 @@ bool kvm_has_waitpkg(void) { return has_msr_umwait; } + +bool kvm_arch_cpu_check_are_resettable(void) +{ + return !sev_es_enabled(); +} diff --git a/target/i386/monitor.c b/target/i386/monitor.c index 1bc91442b1..5994408bee 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -34,6 +34,7 @@ #include "sev_i386.h" #include "qapi/qapi-commands-misc-target.h" #include "qapi/qapi-commands-misc.h" +#include "hw/i386/pc.h" /* Perform linear address sign extension */ static hwaddr addr_canonical(CPUArchState *env, hwaddr addr) @@ -730,9 +731,29 @@ SevCapability *qmp_query_sev_capabilities(Error **errp) return sev_get_capabilities(errp); } +#define SEV_SECRET_GUID "4c2eb361-7d9b-4cc3-8081-127c90d3d294" +struct sev_secret_area { + uint32_t base; + uint32_t size; +}; + void qmp_sev_inject_launch_secret(const char *packet_hdr, - const char *secret, uint64_t gpa, + const char *secret, + bool has_gpa, uint64_t gpa, Error **errp) { + if (!has_gpa) { + uint8_t *data; + struct sev_secret_area *area; + + if (!pc_system_ovmf_table_find(SEV_SECRET_GUID, &data, NULL)) { + error_setg(errp, "SEV: no secret area found in OVMF," + " gpa must be specified."); + return; + } + area = (struct sev_secret_area *)data; + gpa = area->base; + } + sev_inject_launch_secret(packet_hdr, secret, gpa, errp); } diff --git a/target/i386/sev-stub.c b/target/i386/sev-stub.c index 1ac1fd5b94..0207f1c5aa 100644 --- a/target/i386/sev-stub.c +++ b/target/i386/sev-stub.c @@ -49,6 +49,7 @@ SevCapability *sev_get_capabilities(Error **errp) error_setg(errp, "SEV is not available in this QEMU"); return NULL; } + int sev_inject_launch_secret(const char *hdr, const char *secret, uint64_t gpa, Error **errp) { @@ -59,3 +60,17 @@ int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp) { return 0; } + +bool sev_es_enabled(void) +{ + return false; +} + +void sev_es_set_reset_vector(CPUState *cpu) +{ +} + +int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) +{ + abort(); +} diff --git a/target/i386/sev.c b/target/i386/sev.c index 11c9a3cc21..0f414df02f 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -22,6 +22,7 @@ #include "qom/object_interfaces.h" #include "qemu/base64.h" #include "qemu/module.h" +#include "qemu/uuid.h" #include "sysemu/kvm.h" #include "sev_i386.h" #include "sysemu/sysemu.h" @@ -32,6 +33,7 @@ #include "exec/address-spaces.h" #include "monitor/monitor.h" #include "exec/confidential-guest-support.h" +#include "hw/i386/pc.h" #define TYPE_SEV_GUEST "sev-guest" OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST) @@ -67,11 +69,21 @@ struct SevGuestState { int sev_fd; SevState state; gchar *measurement; + + uint32_t reset_cs; + uint32_t reset_ip; + bool reset_data_valid; }; #define DEFAULT_GUEST_POLICY 0x1 /* disable debug */ #define DEFAULT_SEV_DEVICE "/dev/sev" +#define SEV_INFO_BLOCK_GUID "00f771de-1a7e-4fcb-890e-68c77e2fb44e" +typedef struct __attribute__((__packed__)) SevInfoBlock { + /* SEV-ES Reset Vector Address */ + uint32_t reset_addr; +} SevInfoBlock; + static SevGuestState *sev_guest; static Error *sev_mig_blocker; @@ -341,6 +353,12 @@ sev_enabled(void) return !!sev_guest; } +bool +sev_es_enabled(void) +{ + return sev_enabled() && (sev_guest->policy & SEV_POLICY_ES); +} + uint64_t sev_get_me_mask(void) { @@ -561,6 +579,20 @@ sev_launch_update_data(SevGuestState *sev, uint8_t *addr, uint64_t len) return ret; } +static int +sev_launch_update_vmsa(SevGuestState *sev) +{ + int ret, fw_error; + + ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_UPDATE_VMSA, NULL, &fw_error); + if (ret) { + error_report("%s: LAUNCH_UPDATE_VMSA ret=%d fw_error=%d '%s'", + __func__, ret, fw_error, fw_error_to_str(fw_error)); + } + + return ret; +} + static void sev_launch_get_measure(Notifier *notifier, void *unused) { @@ -573,6 +605,14 @@ sev_launch_get_measure(Notifier *notifier, void *unused) return; } + if (sev_es_enabled()) { + /* measure all the VM save areas before getting launch_measure */ + ret = sev_launch_update_vmsa(sev); + if (ret) { + exit(1); + } + } + measurement = g_new0(struct kvm_sev_launch_measure, 1); /* query the measurement blob length */ @@ -667,7 +707,7 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) SevGuestState *sev = (SevGuestState *)object_dynamic_cast(OBJECT(cgs), TYPE_SEV_GUEST); char *devname; - int ret, fw_error; + int ret, fw_error, cmd; uint32_t ebx; uint32_t host_cbitpos; struct sev_user_data_status status = {}; @@ -724,8 +764,26 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) sev->api_major = status.api_major; sev->api_minor = status.api_minor; + if (sev_es_enabled()) { + if (!kvm_kernel_irqchip_allowed()) { + error_report("%s: SEV-ES guests require in-kernel irqchip support", + __func__); + goto err; + } + + if (!(status.flags & SEV_STATUS_FLAGS_CONFIG_ES)) { + error_report("%s: guest policy requires SEV-ES, but " + "host SEV-ES support unavailable", + __func__); + goto err; + } + cmd = KVM_SEV_ES_INIT; + } else { + cmd = KVM_SEV_INIT; + } + trace_kvm_sev_init(); - ret = sev_ioctl(sev->sev_fd, KVM_SEV_INIT, NULL, &fw_error); + ret = sev_ioctl(sev->sev_fd, cmd, NULL, &fw_error); if (ret) { error_setg(errp, "%s: failed to initialize ret=%d fw_error=%d '%s'", __func__, ret, fw_error, fw_error_to_str(fw_error)); @@ -833,6 +891,122 @@ int sev_inject_launch_secret(const char *packet_hdr, const char *secret, return 0; } +static int +sev_es_parse_reset_block(SevInfoBlock *info, uint32_t *addr) +{ + if (!info->reset_addr) { + error_report("SEV-ES reset address is zero"); + return 1; + } + + *addr = info->reset_addr; + + return 0; +} + +static int +sev_es_find_reset_vector(void *flash_ptr, uint64_t flash_size, + uint32_t *addr) +{ + QemuUUID info_guid, *guid; + SevInfoBlock *info; + uint8_t *data; + uint16_t *len; + + /* + * Initialize the address to zero. An address of zero with a successful + * return code indicates that SEV-ES is not active. + */ + *addr = 0; + + /* + * Extract the AP reset vector for SEV-ES guests by locating the SEV GUID. + * The SEV GUID is located on its own (original implementation) or within + * the Firmware GUID Table (new implementation), either of which are + * located 32 bytes from the end of the flash. + * + * Check the Firmware GUID Table first. + */ + if (pc_system_ovmf_table_find(SEV_INFO_BLOCK_GUID, &data, NULL)) { + return sev_es_parse_reset_block((SevInfoBlock *)data, addr); + } + + /* + * SEV info block not found in the Firmware GUID Table (or there isn't + * a Firmware GUID Table), fall back to the original implementation. + */ + data = flash_ptr + flash_size - 0x20; + + qemu_uuid_parse(SEV_INFO_BLOCK_GUID, &info_guid); + info_guid = qemu_uuid_bswap(info_guid); /* GUIDs are LE */ + + guid = (QemuUUID *)(data - sizeof(info_guid)); + if (!qemu_uuid_is_equal(guid, &info_guid)) { + error_report("SEV information block/Firmware GUID Table block not found in pflash rom"); + return 1; + } + + len = (uint16_t *)((uint8_t *)guid - sizeof(*len)); + info = (SevInfoBlock *)(data - le16_to_cpu(*len)); + + return sev_es_parse_reset_block(info, addr); +} + +void sev_es_set_reset_vector(CPUState *cpu) +{ + X86CPU *x86; + CPUX86State *env; + + /* Only update if we have valid reset information */ + if (!sev_guest || !sev_guest->reset_data_valid) { + return; + } + + /* Do not update the BSP reset state */ + if (cpu->cpu_index == 0) { + return; + } + + x86 = X86_CPU(cpu); + env = &x86->env; + + cpu_x86_load_seg_cache(env, R_CS, 0xf000, sev_guest->reset_cs, 0xffff, + DESC_P_MASK | DESC_S_MASK | DESC_CS_MASK | + DESC_R_MASK | DESC_A_MASK); + + env->eip = sev_guest->reset_ip; +} + +int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) +{ + CPUState *cpu; + uint32_t addr; + int ret; + + if (!sev_es_enabled()) { + return 0; + } + + addr = 0; + ret = sev_es_find_reset_vector(flash_ptr, flash_size, + &addr); + if (ret) { + return ret; + } + + if (addr) { + sev_guest->reset_cs = addr & 0xffff0000; + sev_guest->reset_ip = addr & 0x0000ffff; + sev_guest->reset_data_valid = true; + + CPU_FOREACH(cpu) { + sev_es_set_reset_vector(cpu); + } + } + + return 0; +} + static void sev_register_types(void) { diff --git a/target/i386/sev_i386.h b/target/i386/sev_i386.h index 4db6960f60..ae221d4c72 100644 --- a/target/i386/sev_i386.h +++ b/target/i386/sev_i386.h @@ -28,7 +28,7 @@ #define SEV_POLICY_DOMAIN 0x10 #define SEV_POLICY_SEV 0x20 -extern bool sev_enabled(void); +extern bool sev_es_enabled(void); extern uint64_t sev_get_me_mask(void); extern SevInfo *sev_get_info(void); extern uint32_t sev_get_cbit_position(void); diff --git a/target/mips/kvm.c b/target/mips/kvm.c index 84fb10ea35..123ec1be7e 100644 --- a/target/mips/kvm.c +++ b/target/mips/kvm.c @@ -1290,3 +1290,8 @@ int mips_kvm_type(MachineState *machine, const char *vm_type) return -1; } + +bool kvm_arch_cpu_check_are_resettable(void) +{ + return true; +} diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 0c5056dd5b..298c1f882c 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -2929,3 +2929,8 @@ void kvmppc_set_reg_tb_offset(PowerPCCPU *cpu, int64_t tb_offset) kvm_set_one_reg(cs, KVM_REG_PPC_TB_OFFSET, &tb_offset); } } + +bool kvm_arch_cpu_check_are_resettable(void) +{ + return true; +} diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c index dc27fa36c9..7a892d663d 100644 --- a/target/s390x/kvm.c +++ b/target/s390x/kvm.c @@ -2599,3 +2599,8 @@ void kvm_s390_stop_interrupt(S390CPU *cpu) kvm_s390_vcpu_interrupt(cpu, &irq); } + +bool kvm_arch_cpu_check_are_resettable(void) +{ + return true; +} |