diff --git a/hw/arm/virt.c b/hw/arm/virt.c index f7a9ba9a58ce1ff79603ae60757ce9fa54b7f0e4..e046fff5dd8ab0de7778f299f4c0feca88088f52 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -29,6 +29,7 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include "qemu/datadir.h" #include "qemu/units.h" #include "qemu/option.h" @@ -3103,6 +3104,62 @@ static void virt_set_oem_table_id(Object *obj, const char *value, strncpy(vms->oem_table_id, value, 8); } +static TargetImplCpu target_impl_cpus[MAX_TARGET_IMPL_CPUS]; + +static void virt_set_target_impl_cpus(Object *obj, const char *value, + Error **errp) +{ + MachineState *ms = MACHINE(obj); + g_autofree char *target_dup = g_strdup(value); + char *target_impl = strtok(target_dup, "-"); + int cnt = 0; + + while (target_impl) { + char num[16]; + long val; + const char *num_start = target_impl; + const char *num_end = strchr(num_start, ':'); + + if (!num_end) { + error_setg(errp, + "Wrong format, Please use 0xmidr:0xrevid-0xmidr:0xrevid"); + return; + } + + strncpy(num, num_start, num_end - num_start); + num[num_end - num_start] = '\0'; + if (qemu_strtol(num, NULL, 16, &val)) { + error_setg(errp, + "Wrong format, Please use 0xmidr:0xrevid-0xmidr:0xrevid"); + return; + } + target_impl_cpus[cnt].midr = val; + num_start = num_end + 1; + if (!(*num_start)) { + error_setg(errp, + "Wrong format, Please use 0xmidr:0xrevid-0xmidr:0xrevid"); + return; + } + + strncpy(num, num_start, sizeof(num) - 1); + num[sizeof(num) - 1] = '\0'; + if (qemu_strtol(num, NULL, 16, &val)) { + error_setg(errp, + "Wrong format, Please use 0xmidr:0xrevid-0xmidr:0xrevid"); + return; + } + target_impl_cpus[cnt].revidr = val; + cnt++; + if (cnt >= MAX_TARGET_IMPL_CPUS) { + error_setg(errp, + "Wrong format, Max %d targets can be set for now!", MAX_TARGET_IMPL_CPUS); + return; + } + target_impl = strtok(NULL, "-"); + } + ms->target_ipml_cpu_num = cnt; + ms->target_ipml_cpu = target_impl_cpus; +} bool virt_is_acpi_enabled(VirtMachineState *vms) { @@ -4085,7 +4142,13 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) "Override the default value of field OEM Table ID " "in ACPI table header." "The string may be up to 8 bytes in size"); - + + object_class_property_add_str(oc, "x-target-impl-cpus", + NULL, + virt_set_target_impl_cpus); + object_class_property_set_description(oc, "x-target-impl-cpus", + "Describe target cpu impl in the format midr1:revidr1-midr2:revidr2" + "Maximum 4 midr:revidr pair is supported"); } static char *virt_get_kvm_type(Object *obj, Error **errp G_GNUC_UNUSED) diff --git a/include/hw/boards.h b/include/hw/boards.h index 8ac8cad2a2225e97df1475c2f20118ccf2af394e..192dc7dc093574df304a5beffa5bbe3d5557d5d8 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -402,6 +402,9 @@ struct MachineState { CpuTopology smp; struct NVDIMMState *nvdimms_state; struct NumaState *numa_state; + + uint32_t target_ipml_cpu_num; + void *target_ipml_cpu; }; #define DEFINE_MACHINE(namestr, machine_initfn) \ diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 1b5bd85e77092fb5fd748e99689b07ea913cdec3..50f22717ec811901e0e06e7c644c7ce50b58134c 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -37,6 +37,51 @@ #include "qapi/visitor.h" #include "qemu/log.h" +/* + * SMMCC KVM Vendor hypercall definitions. + * ToDo: Include using update-linux-headers.sh + */ +#define ARM_SMCCC_FAST_CALL _AC(1,U) +#define ARM_SMCCC_TYPE_SHIFT 31 + +#define ARM_SMCCC_SMC_32 0 +#define ARM_SMCCC_SMC_64 1 +#define ARM_SMCCC_CALL_CONV_SHIFT 30 + +#define ARM_SMCCC_OWNER_MASK 0x3F +#define ARM_SMCCC_OWNER_SHIFT 24 + +#define ARM_SMCCC_FUNC_MASK 0xFFFF + +#define ARM_SMCCC_OWNER_VENDOR_HYP 6 + + +#define ARM_SMCCC_KVM_FUNC_DISCOVER_IMPL_VER 64 +#define ARM_SMCCC_KVM_FUNC_DISCOVER_IMPL_CPUS 65 + +#define ARM_SMCCC_CALL_VAL(type, calling_convention, owner, func_num) \ + (((type) << ARM_SMCCC_TYPE_SHIFT) | \ + ((calling_convention) << ARM_SMCCC_CALL_CONV_SHIFT) | \ + (((owner) & ARM_SMCCC_OWNER_MASK) << ARM_SMCCC_OWNER_SHIFT) | \ + ((func_num) & ARM_SMCCC_FUNC_MASK)) + +#define ARM_SMCCC_VENDOR_HYP_KVM_DISCOVER_IMPL_VER_FUNC_ID \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_64, \ + ARM_SMCCC_OWNER_VENDOR_HYP, \ + ARM_SMCCC_KVM_FUNC_DISCOVER_IMPL_VER) + +#define ARM_SMCCC_VENDOR_HYP_KVM_DISCOVER_IMPL_CPUS_FUNC_ID \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_64, \ + ARM_SMCCC_OWNER_VENDOR_HYP, \ + ARM_SMCCC_KVM_FUNC_DISCOVER_IMPL_CPUS) + +#define SMCCC_RET_SUCCESS 0 +#define SMCCC_RET_NOT_SUPPORTED -1 +#define SMCCC_RET_NOT_REQUIRED -2 +#define SMCCC_RET_INVALID_PARAMETER -3 + const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_LAST_INFO }; @@ -393,6 +438,16 @@ int kvm_arch_init(MachineState *ms, KVMState *s) if (ret) { error_report("Failed to enable RME: %s", strerror(-ret)); } + + if (kvm_arm_set_smccc_filter(ARM_SMCCC_VENDOR_HYP_KVM_DISCOVER_IMPL_VER_FUNC_ID, + KVM_SMCCC_FILTER_FWD_TO_USER)) { + error_report("ARM_SMCCC_KVM_FUNC_DISCOVER_IMPL_VER fwd filter install failed"); + } + + if (kvm_arm_set_smccc_filter(ARM_SMCCC_VENDOR_HYP_KVM_DISCOVER_IMPL_CPUS_FUNC_ID, + KVM_SMCCC_FILTER_FWD_TO_USER)) { + error_report("ARM_SMCCC_KVM_FUNC_DISCOVER_IMPL_CPUS fwd filter install failed"); + } return ret; } @@ -1131,6 +1186,55 @@ void kvm_arm_vm_state_change(void *opaque, bool running, RunState state) } } +static bool arm_handle_smcc_kvm_vendor_hypercall(ARMCPU *cpu) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + CPUARMState *env = &cpu->env; + uint64_t param[4]; + int idx; + int i; + + for (i = 0; i < 4; i++) { + /* + * All PSCI functions take explicit 32-bit or native int sized + * arguments so we can simply zero-extend all arguments regardless + * of which exact function we are about to call. + */ + param[i] = is_a64(env) ? env->xregs[i] : env->regs[i]; + } + + if (is_a64(env)) { + TargetImplCpu *target = ms->target_ipml_cpu; + + switch (param[0]) { + case ARM_SMCCC_VENDOR_HYP_KVM_DISCOVER_IMPL_VER_FUNC_ID: + if (!ms->target_ipml_cpu_num) { + env->xregs[0] = SMCCC_RET_NOT_SUPPORTED; + break; + } + env->xregs[0] = SMCCC_RET_SUCCESS; + env->xregs[1] = PSCI_VERSION(1, 0); + env->xregs[2] = ms->target_ipml_cpu_num; + break; + case ARM_SMCCC_VENDOR_HYP_KVM_DISCOVER_IMPL_CPUS_FUNC_ID: + idx = param[1]; + if (idx >= ms->target_ipml_cpu_num) { + env->xregs[0] = SMCCC_RET_INVALID_PARAMETER; + break; + } + env->xregs[0] = SMCCC_RET_SUCCESS; + env->xregs[1] = target[idx].midr; + env->xregs[2] = target[idx].revidr; + break; + default: + return false; + } + } else { + return false; + } + return true; +} + /** * kvm_arm_handle_dabt_nisv: * @cs: CPUState @@ -1193,9 +1297,12 @@ static int kvm_arm_handle_hypercall(CPUState *cs, struct kvm_run *run) env->exception.syndrome = syn_aa64_hvc(0); } env->exception.target_el = 1; - qemu_mutex_lock_iothread(); - arm_cpu_do_interrupt(cs); - qemu_mutex_unlock_iothread(); + + if (!arm_handle_smcc_kvm_vendor_hypercall(cpu)) { + qemu_mutex_lock_iothread(); + arm_cpu_do_interrupt(cs); + qemu_mutex_unlock_iothread(); + } /* * For PSCI, exit the kvm_run loop and process the work. Especially @@ -1220,7 +1327,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) run->arm_nisv.fault_ipa); break; case KVM_EXIT_HYPERCALL: - ret = kvm_arm_handle_hypercall(cs, run); + ret = kvm_arm_handle_hypercall(cs, run); break; default: qemu_log_mask(LOG_UNIMP, "%s: un-handled exit reason %d\n", diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c index 7e0ca919b2f145ea9502df0b30bb636740e21feb..7614194998db0fa87cbcd3b51297f521585f010c 100644 --- a/target/arm/kvm64.c +++ b/target/arm/kvm64.c @@ -795,6 +795,14 @@ static int kvm_arm_sve_set_vls(CPUState *cs) return kvm_set_one_reg(cs, KVM_REG_ARM64_SVE_VLS, &vls[0]); } +static int kvm_arm_set_vend_hyp_bmap_2(ARMCPU *cpu) +{ + /* Set DISCOVER_IMPL_* hypercalls */ + uint64_t bmap_2 = MAKE_64BIT_MASK(0, 2); + + return kvm_set_one_reg(CPU(cpu), KVM_REG_ARM_VENDOR_HYP_BMAP_2, &bmap_2); +} + #define ARM_CPU_ID_MPIDR 3, 0, 0, 0, 5 int kvm_arch_init_vcpu(CPUState *cs) @@ -871,6 +879,7 @@ int kvm_arch_init_vcpu(CPUState *cs) } } + kvm_arm_set_vend_hyp_bmap_2(cpu); /* * KVM reports the exact PSCI version it is implementing via a * special sysreg. If it is present, use its contents to determine diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index df8258ef34280f051f573cade36f71cc282b207c..8c69957fbf0801c54517a4cb425d8d1cfa5b3a49 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -21,6 +21,12 @@ #define KVM_REG_ARM_ID_AA64DFR0_EL1 ARM64_SYS_REG(3, 0, 0, 5, 0) #define KVM_REG_ARM_PMCR_EL0 ARM64_SYS_REG(3, 3, 9, 12, 0) +#define MAX_TARGET_IMPL_CPUS 8 +typedef struct TargetImplCpu { + uint32_t midr; + uint32_t revidr; +} TargetImplCpu; + /** * kvm_arm_init_debug() - initialize guest debug capabilities * @s: KVMState