
From: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com> This is a hacked interface to test the kernel. Usage Eg: -machine virt,.., x-target-impl-cpus=0xMIDR1:0xREVIDR1-0xMIDR2:REVIDR2 The HVC/SMC hypercall exit handling part is loosly based on this patch, https://lore.kernel.org/qemu-devel/20240614001510.202991-1-salil.mehta@huawe... Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com> --- hw/arm/virt.c | 65 ++++++++++++++++++++++++++++++++++++++- include/hw/boards.h | 3 ++ target/arm/kvm.c | 72 +++++++++++++++++++++++++++++++++++++++++--- target/arm/kvm_arm.h | 6 ++++ 4 files changed, 140 insertions(+), 6 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index cf4156ed49..d8890e319f 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" @@ -3112,6 +3113,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) { @@ -4094,7 +4151,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 8ac8cad2a2..192dc7dc09 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 610a913062..165e0034bb 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -59,7 +59,7 @@ #define ARM_SMCCC_KVM_FUNC_DISCOVER_IMPL_VER 64 #define ARM_SMCCC_KVM_FUNC_DISCOVER_IMPL_CPUS 65 -#define ARM_SMCCC_KVM_DISCOVER_IMPL_VER_1_0 0x10000 +#define ARM_SMCCC_KVM_DISCOVER_IMPL_VER_1_0 0x100000000 #define ARM_SMCCC_CALL_VAL(type, calling_convention, owner, func_num) \ (((type) << ARM_SMCCC_TYPE_SHIFT) | \ @@ -436,6 +436,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; } @@ -1174,6 +1184,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] = ARM_SMCCC_KVM_DISCOVER_IMPL_VER_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 @@ -1236,9 +1295,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 @@ -1263,7 +1325,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/kvm_arm.h b/target/arm/kvm_arm.h index df8258ef34..8c69957fbf 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 -- 2.33.0