mirror of
https://github.com/torvalds/linux.git
synced 2026-05-22 22:22:08 +02:00
Merge branch kvm-arm64/selftests-6.18 into kvmarm-master/next
* kvm-arm64/selftests-6.18: : . : KVM/arm64 selftest updates for 6.18: : : - Large update to run EL1 selftests at EL2 when possible : (20250917212044.294760-1-oliver.upton@linux.dev) : : - Work around lack of ID_AA64MMFR4_EL1 trapping on CPUs : without FEAT_FGT : (20250923173006.467455-1-oliver.upton@linux.dev) : : - Additional fixes and cleanups : (20250920-kvm-arm64-id-aa64isar3-el1-v1-0-1764c1c1c96d@kernel.org) : . KVM: arm64: selftests: Cover ID_AA64ISAR3_EL1 in set_id_regs KVM: arm64: selftests: Remove a duplicate register listing in set_id_regs KVM: arm64: selftests: Cope with arch silliness in EL2 selftest KVM: arm64: selftests: Add basic test for running in VHE EL2 KVM: arm64: selftests: Enable EL2 by default KVM: arm64: selftests: Initialize HCR_EL2 KVM: arm64: selftests: Use the vCPU attr for setting nr of PMU counters KVM: arm64: selftests: Use hyp timer IRQs when test runs at EL2 KVM: arm64: selftests: Select SMCCC conduit based on current EL KVM: arm64: selftests: Provide helper for getting default vCPU target KVM: arm64: selftests: Alias EL1 registers to EL2 counterparts KVM: arm64: selftests: Create a VGICv3 for 'default' VMs KVM: arm64: selftests: Add unsanitised helpers for VGICv3 creation KVM: arm64: selftests: Add helper to check for VGICv3 support KVM: arm64: selftests: Initialize VGICv3 only once KVM: arm64: selftests: Provide kvm_arch_vm_post_create() in library code Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
commit
10fd028530
|
|
@ -156,6 +156,7 @@ TEST_GEN_PROGS_arm64 = $(TEST_GEN_PROGS_COMMON)
|
|||
TEST_GEN_PROGS_arm64 += arm64/aarch32_id_regs
|
||||
TEST_GEN_PROGS_arm64 += arm64/arch_timer_edge_cases
|
||||
TEST_GEN_PROGS_arm64 += arm64/debug-exceptions
|
||||
TEST_GEN_PROGS_arm64 += arm64/hello_el2
|
||||
TEST_GEN_PROGS_arm64 += arm64/host_sve
|
||||
TEST_GEN_PROGS_arm64 += arm64/hypercalls
|
||||
TEST_GEN_PROGS_arm64 += arm64/external_aborts
|
||||
|
|
|
|||
|
|
@ -165,10 +165,8 @@ static void guest_code(void)
|
|||
static void test_init_timer_irq(struct kvm_vm *vm)
|
||||
{
|
||||
/* Timer initid should be same for all the vCPUs, so query only vCPU-0 */
|
||||
vcpu_device_attr_get(vcpus[0], KVM_ARM_VCPU_TIMER_CTRL,
|
||||
KVM_ARM_VCPU_TIMER_IRQ_PTIMER, &ptimer_irq);
|
||||
vcpu_device_attr_get(vcpus[0], KVM_ARM_VCPU_TIMER_CTRL,
|
||||
KVM_ARM_VCPU_TIMER_IRQ_VTIMER, &vtimer_irq);
|
||||
ptimer_irq = vcpu_get_ptimer_irq(vcpus[0]);
|
||||
vtimer_irq = vcpu_get_vtimer_irq(vcpus[0]);
|
||||
|
||||
sync_global_to_guest(vm, ptimer_irq);
|
||||
sync_global_to_guest(vm, vtimer_irq);
|
||||
|
|
@ -176,14 +174,14 @@ static void test_init_timer_irq(struct kvm_vm *vm)
|
|||
pr_debug("ptimer_irq: %d; vtimer_irq: %d\n", ptimer_irq, vtimer_irq);
|
||||
}
|
||||
|
||||
static int gic_fd;
|
||||
|
||||
struct kvm_vm *test_vm_create(void)
|
||||
{
|
||||
struct kvm_vm *vm;
|
||||
unsigned int i;
|
||||
int nr_vcpus = test_args.nr_vcpus;
|
||||
|
||||
TEST_REQUIRE(kvm_supports_vgic_v3());
|
||||
|
||||
vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus);
|
||||
|
||||
vm_init_descriptor_tables(vm);
|
||||
|
|
@ -204,8 +202,6 @@ struct kvm_vm *test_vm_create(void)
|
|||
vcpu_init_descriptor_tables(vcpus[i]);
|
||||
|
||||
test_init_timer_irq(vm);
|
||||
gic_fd = vgic_v3_setup(vm, nr_vcpus, 64);
|
||||
__TEST_REQUIRE(gic_fd >= 0, "Failed to create vgic-v3");
|
||||
|
||||
/* Make all the test's cmdline args visible to the guest */
|
||||
sync_global_to_guest(vm, test_args);
|
||||
|
|
@ -215,6 +211,5 @@ struct kvm_vm *test_vm_create(void)
|
|||
|
||||
void test_vm_cleanup(struct kvm_vm *vm)
|
||||
{
|
||||
close(gic_fd);
|
||||
kvm_vm_free(vm);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -924,10 +924,8 @@ static void test_run(struct kvm_vm *vm, struct kvm_vcpu *vcpu)
|
|||
|
||||
static void test_init_timer_irq(struct kvm_vm *vm, struct kvm_vcpu *vcpu)
|
||||
{
|
||||
vcpu_device_attr_get(vcpu, KVM_ARM_VCPU_TIMER_CTRL,
|
||||
KVM_ARM_VCPU_TIMER_IRQ_PTIMER, &ptimer_irq);
|
||||
vcpu_device_attr_get(vcpu, KVM_ARM_VCPU_TIMER_CTRL,
|
||||
KVM_ARM_VCPU_TIMER_IRQ_VTIMER, &vtimer_irq);
|
||||
ptimer_irq = vcpu_get_ptimer_irq(vcpu);
|
||||
vtimer_irq = vcpu_get_vtimer_irq(vcpu);
|
||||
|
||||
sync_global_to_guest(vm, ptimer_irq);
|
||||
sync_global_to_guest(vm, vtimer_irq);
|
||||
|
|
@ -935,8 +933,6 @@ static void test_init_timer_irq(struct kvm_vm *vm, struct kvm_vcpu *vcpu)
|
|||
pr_debug("ptimer_irq: %d; vtimer_irq: %d\n", ptimer_irq, vtimer_irq);
|
||||
}
|
||||
|
||||
static int gic_fd;
|
||||
|
||||
static void test_vm_create(struct kvm_vm **vm, struct kvm_vcpu **vcpu,
|
||||
enum arch_timer timer)
|
||||
{
|
||||
|
|
@ -951,8 +947,6 @@ static void test_vm_create(struct kvm_vm **vm, struct kvm_vcpu **vcpu,
|
|||
vcpu_args_set(*vcpu, 1, timer);
|
||||
|
||||
test_init_timer_irq(*vm, *vcpu);
|
||||
gic_fd = vgic_v3_setup(*vm, 1, 64);
|
||||
__TEST_REQUIRE(gic_fd >= 0, "Failed to create vgic-v3");
|
||||
|
||||
sync_global_to_guest(*vm, test_args);
|
||||
sync_global_to_guest(*vm, CVAL_MAX);
|
||||
|
|
@ -961,7 +955,6 @@ static void test_vm_create(struct kvm_vm **vm, struct kvm_vcpu **vcpu,
|
|||
|
||||
static void test_vm_cleanup(struct kvm_vm *vm)
|
||||
{
|
||||
close(gic_fd);
|
||||
kvm_vm_free(vm);
|
||||
}
|
||||
|
||||
|
|
@ -1042,6 +1035,8 @@ int main(int argc, char *argv[])
|
|||
/* Tell stdout not to buffer its content */
|
||||
setbuf(stdout, NULL);
|
||||
|
||||
TEST_REQUIRE(kvm_supports_vgic_v3());
|
||||
|
||||
if (!parse_args(argc, argv))
|
||||
exit(KSFT_SKIP);
|
||||
|
||||
|
|
|
|||
71
tools/testing/selftests/kvm/arm64/hello_el2.c
Normal file
71
tools/testing/selftests/kvm/arm64/hello_el2.c
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* hello_el2 - Basic KVM selftest for VM running at EL2 with E2H=RES1
|
||||
*
|
||||
* Copyright 2025 Google LLC
|
||||
*/
|
||||
#include "kvm_util.h"
|
||||
#include "processor.h"
|
||||
#include "test_util.h"
|
||||
#include "ucall.h"
|
||||
|
||||
#include <asm/sysreg.h>
|
||||
|
||||
static void guest_code(void)
|
||||
{
|
||||
u64 mmfr0 = read_sysreg_s(SYS_ID_AA64MMFR0_EL1);
|
||||
u64 mmfr1 = read_sysreg_s(SYS_ID_AA64MMFR1_EL1);
|
||||
u64 mmfr4 = read_sysreg_s(SYS_ID_AA64MMFR4_EL1);
|
||||
u8 e2h0 = SYS_FIELD_GET(ID_AA64MMFR4_EL1, E2H0, mmfr4);
|
||||
|
||||
GUEST_ASSERT_EQ(get_current_el(), 2);
|
||||
GUEST_ASSERT(read_sysreg(hcr_el2) & HCR_EL2_E2H);
|
||||
GUEST_ASSERT_EQ(SYS_FIELD_GET(ID_AA64MMFR1_EL1, VH, mmfr1),
|
||||
ID_AA64MMFR1_EL1_VH_IMP);
|
||||
|
||||
/*
|
||||
* Traps of the complete ID register space are IMPDEF without FEAT_FGT,
|
||||
* which is really annoying to deal with in KVM describing E2H as RES1.
|
||||
*
|
||||
* If the implementation doesn't honor the trap then expect the register
|
||||
* to return all zeros.
|
||||
*/
|
||||
if (e2h0 == ID_AA64MMFR4_EL1_E2H0_IMP)
|
||||
GUEST_ASSERT_EQ(SYS_FIELD_GET(ID_AA64MMFR0_EL1, FGT, mmfr0),
|
||||
ID_AA64MMFR0_EL1_FGT_NI);
|
||||
else
|
||||
GUEST_ASSERT_EQ(e2h0, ID_AA64MMFR4_EL1_E2H0_NI_NV1);
|
||||
|
||||
GUEST_DONE();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct kvm_vcpu_init init;
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vm *vm;
|
||||
struct ucall uc;
|
||||
|
||||
TEST_REQUIRE(kvm_check_cap(KVM_CAP_ARM_EL2));
|
||||
|
||||
vm = vm_create(1);
|
||||
|
||||
kvm_get_default_vcpu_target(vm, &init);
|
||||
init.features[0] |= BIT(KVM_ARM_VCPU_HAS_EL2);
|
||||
vcpu = aarch64_vcpu_add(vm, 0, &init, guest_code);
|
||||
kvm_arch_vm_finalize_vcpus(vm);
|
||||
|
||||
vcpu_run(vcpu);
|
||||
switch (get_ucall(vcpu, &uc)) {
|
||||
case UCALL_DONE:
|
||||
break;
|
||||
case UCALL_ABORT:
|
||||
REPORT_GUEST_ASSERT(uc);
|
||||
break;
|
||||
default:
|
||||
TEST_FAIL("Unhandled ucall: %ld\n", uc.cmd);
|
||||
}
|
||||
|
||||
kvm_vm_free(vm);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -108,7 +108,7 @@ static void guest_test_hvc(const struct test_hvc_info *hc_info)
|
|||
|
||||
for (i = 0; i < hvc_info_arr_sz; i++, hc_info++) {
|
||||
memset(&res, 0, sizeof(res));
|
||||
smccc_hvc(hc_info->func_id, hc_info->arg1, 0, 0, 0, 0, 0, 0, &res);
|
||||
do_smccc(hc_info->func_id, hc_info->arg1, 0, 0, 0, 0, 0, 0, &res);
|
||||
|
||||
switch (stage) {
|
||||
case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ static void guest_code(void)
|
|||
{
|
||||
struct arm_smccc_res res = {};
|
||||
|
||||
smccc_hvc(ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, 0, 0, 0, 0, 0, 0, 0, &res);
|
||||
do_smccc(ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, 0, 0, 0, 0, 0, 0, 0, &res);
|
||||
|
||||
__GUEST_ASSERT(res.a0 == ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_0 &&
|
||||
res.a1 == ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_1 &&
|
||||
|
|
|
|||
|
|
@ -163,6 +163,8 @@ int main(int argc, char *argv[])
|
|||
struct kvm_vm *vm;
|
||||
uint64_t pfr0;
|
||||
|
||||
test_disable_default_vgic();
|
||||
|
||||
vm = vm_create_with_one_vcpu(&vcpu, NULL);
|
||||
pfr0 = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR0_EL1));
|
||||
__TEST_REQUIRE(FIELD_GET(ID_AA64PFR0_EL1_GIC, pfr0),
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ static uint64_t psci_cpu_on(uint64_t target_cpu, uint64_t entry_addr,
|
|||
{
|
||||
struct arm_smccc_res res;
|
||||
|
||||
smccc_hvc(PSCI_0_2_FN64_CPU_ON, target_cpu, entry_addr, context_id,
|
||||
do_smccc(PSCI_0_2_FN64_CPU_ON, target_cpu, entry_addr, context_id,
|
||||
0, 0, 0, 0, &res);
|
||||
|
||||
return res.a0;
|
||||
|
|
@ -38,7 +38,7 @@ static uint64_t psci_affinity_info(uint64_t target_affinity,
|
|||
{
|
||||
struct arm_smccc_res res;
|
||||
|
||||
smccc_hvc(PSCI_0_2_FN64_AFFINITY_INFO, target_affinity, lowest_affinity_level,
|
||||
do_smccc(PSCI_0_2_FN64_AFFINITY_INFO, target_affinity, lowest_affinity_level,
|
||||
0, 0, 0, 0, 0, &res);
|
||||
|
||||
return res.a0;
|
||||
|
|
@ -48,7 +48,7 @@ static uint64_t psci_system_suspend(uint64_t entry_addr, uint64_t context_id)
|
|||
{
|
||||
struct arm_smccc_res res;
|
||||
|
||||
smccc_hvc(PSCI_1_0_FN64_SYSTEM_SUSPEND, entry_addr, context_id,
|
||||
do_smccc(PSCI_1_0_FN64_SYSTEM_SUSPEND, entry_addr, context_id,
|
||||
0, 0, 0, 0, 0, &res);
|
||||
|
||||
return res.a0;
|
||||
|
|
@ -58,7 +58,7 @@ static uint64_t psci_system_off2(uint64_t type, uint64_t cookie)
|
|||
{
|
||||
struct arm_smccc_res res;
|
||||
|
||||
smccc_hvc(PSCI_1_3_FN64_SYSTEM_OFF2, type, cookie, 0, 0, 0, 0, 0, &res);
|
||||
do_smccc(PSCI_1_3_FN64_SYSTEM_OFF2, type, cookie, 0, 0, 0, 0, 0, &res);
|
||||
|
||||
return res.a0;
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ static uint64_t psci_features(uint32_t func_id)
|
|||
{
|
||||
struct arm_smccc_res res;
|
||||
|
||||
smccc_hvc(PSCI_1_0_FN_PSCI_FEATURES, func_id, 0, 0, 0, 0, 0, 0, &res);
|
||||
do_smccc(PSCI_1_0_FN_PSCI_FEATURES, func_id, 0, 0, 0, 0, 0, 0, &res);
|
||||
|
||||
return res.a0;
|
||||
}
|
||||
|
|
@ -89,12 +89,13 @@ static struct kvm_vm *setup_vm(void *guest_code, struct kvm_vcpu **source,
|
|||
|
||||
vm = vm_create(2);
|
||||
|
||||
vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init);
|
||||
kvm_get_default_vcpu_target(vm, &init);
|
||||
init.features[0] |= (1 << KVM_ARM_VCPU_PSCI_0_2);
|
||||
|
||||
*source = aarch64_vcpu_add(vm, 0, &init, guest_code);
|
||||
*target = aarch64_vcpu_add(vm, 1, &init, guest_code);
|
||||
|
||||
kvm_arch_vm_finalize_vcpus(vm);
|
||||
return vm;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@
|
|||
#include "test_util.h"
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
bool have_cap_arm_mte;
|
||||
|
||||
enum ftr_type {
|
||||
FTR_EXACT, /* Use a predefined safe value */
|
||||
FTR_LOWER_SAFE, /* Smaller value is safe */
|
||||
|
|
@ -125,6 +123,13 @@ static const struct reg_ftr_bits ftr_id_aa64isar2_el1[] = {
|
|||
REG_FTR_END,
|
||||
};
|
||||
|
||||
static const struct reg_ftr_bits ftr_id_aa64isar3_el1[] = {
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR3_EL1, FPRCVT, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR3_EL1, LSFE, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR3_EL1, FAMINMAX, 0),
|
||||
REG_FTR_END,
|
||||
};
|
||||
|
||||
static const struct reg_ftr_bits ftr_id_aa64pfr0_el1[] = {
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, CSV3, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, CSV2, 0),
|
||||
|
|
@ -223,6 +228,7 @@ static struct test_feature_reg test_regs[] = {
|
|||
TEST_REG(SYS_ID_AA64ISAR0_EL1, ftr_id_aa64isar0_el1),
|
||||
TEST_REG(SYS_ID_AA64ISAR1_EL1, ftr_id_aa64isar1_el1),
|
||||
TEST_REG(SYS_ID_AA64ISAR2_EL1, ftr_id_aa64isar2_el1),
|
||||
TEST_REG(SYS_ID_AA64ISAR3_EL1, ftr_id_aa64isar3_el1),
|
||||
TEST_REG(SYS_ID_AA64PFR0_EL1, ftr_id_aa64pfr0_el1),
|
||||
TEST_REG(SYS_ID_AA64PFR1_EL1, ftr_id_aa64pfr1_el1),
|
||||
TEST_REG(SYS_ID_AA64MMFR0_EL1, ftr_id_aa64mmfr0_el1),
|
||||
|
|
@ -241,6 +247,7 @@ static void guest_code(void)
|
|||
GUEST_REG_SYNC(SYS_ID_AA64ISAR0_EL1);
|
||||
GUEST_REG_SYNC(SYS_ID_AA64ISAR1_EL1);
|
||||
GUEST_REG_SYNC(SYS_ID_AA64ISAR2_EL1);
|
||||
GUEST_REG_SYNC(SYS_ID_AA64ISAR3_EL1);
|
||||
GUEST_REG_SYNC(SYS_ID_AA64PFR0_EL1);
|
||||
GUEST_REG_SYNC(SYS_ID_AA64MMFR0_EL1);
|
||||
GUEST_REG_SYNC(SYS_ID_AA64MMFR1_EL1);
|
||||
|
|
@ -570,7 +577,9 @@ static void test_user_set_mte_reg(struct kvm_vcpu *vcpu)
|
|||
uint64_t mte_frac;
|
||||
int idx, err;
|
||||
|
||||
if (!have_cap_arm_mte) {
|
||||
val = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR1_EL1));
|
||||
mte = FIELD_GET(ID_AA64PFR1_EL1_MTE, val);
|
||||
if (!mte) {
|
||||
ksft_test_result_skip("MTE capability not supported, nothing to test\n");
|
||||
return;
|
||||
}
|
||||
|
|
@ -595,9 +604,6 @@ static void test_user_set_mte_reg(struct kvm_vcpu *vcpu)
|
|||
* from unsupported (0xF) to supported (0).
|
||||
*
|
||||
*/
|
||||
val = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR1_EL1));
|
||||
|
||||
mte = FIELD_GET(ID_AA64PFR1_EL1_MTE, val);
|
||||
mte_frac = FIELD_GET(ID_AA64PFR1_EL1_MTE_frac, val);
|
||||
if (mte != ID_AA64PFR1_EL1_MTE_MTE2 ||
|
||||
mte_frac != ID_AA64PFR1_EL1_MTE_frac_NI) {
|
||||
|
|
@ -752,28 +758,23 @@ static void test_reset_preserves_id_regs(struct kvm_vcpu *vcpu)
|
|||
ksft_test_result_pass("%s\n", __func__);
|
||||
}
|
||||
|
||||
void kvm_arch_vm_post_create(struct kvm_vm *vm)
|
||||
{
|
||||
if (vm_check_cap(vm, KVM_CAP_ARM_MTE)) {
|
||||
vm_enable_cap(vm, KVM_CAP_ARM_MTE, 0);
|
||||
have_cap_arm_mte = true;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vm *vm;
|
||||
bool aarch64_only;
|
||||
uint64_t val, el0;
|
||||
int test_cnt;
|
||||
int test_cnt, i, j;
|
||||
|
||||
TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_SUPPORTED_REG_MASK_RANGES));
|
||||
TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_WRITABLE_IMP_ID_REGS));
|
||||
|
||||
test_wants_mte();
|
||||
|
||||
vm = vm_create(1);
|
||||
vm_enable_cap(vm, KVM_CAP_ARM_WRITABLE_IMP_ID_REGS, 0);
|
||||
vcpu = vm_vcpu_add(vm, 0, guest_code);
|
||||
kvm_arch_vm_finalize_vcpus(vm);
|
||||
|
||||
/* Check for AARCH64 only system */
|
||||
val = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR0_EL1));
|
||||
|
|
@ -782,13 +783,10 @@ int main(void)
|
|||
|
||||
ksft_print_header();
|
||||
|
||||
test_cnt = ARRAY_SIZE(ftr_id_aa64dfr0_el1) + ARRAY_SIZE(ftr_id_dfr0_el1) +
|
||||
ARRAY_SIZE(ftr_id_aa64isar0_el1) + ARRAY_SIZE(ftr_id_aa64isar1_el1) +
|
||||
ARRAY_SIZE(ftr_id_aa64isar2_el1) + ARRAY_SIZE(ftr_id_aa64pfr0_el1) +
|
||||
ARRAY_SIZE(ftr_id_aa64pfr1_el1) + ARRAY_SIZE(ftr_id_aa64mmfr0_el1) +
|
||||
ARRAY_SIZE(ftr_id_aa64mmfr1_el1) + ARRAY_SIZE(ftr_id_aa64mmfr2_el1) +
|
||||
ARRAY_SIZE(ftr_id_aa64mmfr3_el1) + ARRAY_SIZE(ftr_id_aa64zfr0_el1) -
|
||||
ARRAY_SIZE(test_regs) + 3 + MPAM_IDREG_TEST + MTE_IDREG_TEST;
|
||||
test_cnt = 3 + MPAM_IDREG_TEST + MTE_IDREG_TEST;
|
||||
for (i = 0; i < ARRAY_SIZE(test_regs); i++)
|
||||
for (j = 0; test_regs[i].ftr_bits[j].type != FTR_END; j++)
|
||||
test_cnt++;
|
||||
|
||||
ksft_set_plan(test_cnt);
|
||||
|
||||
|
|
|
|||
|
|
@ -22,8 +22,20 @@ enum smccc_conduit {
|
|||
SMC_INSN,
|
||||
};
|
||||
|
||||
static bool test_runs_at_el2(void)
|
||||
{
|
||||
struct kvm_vm *vm = vm_create(1);
|
||||
struct kvm_vcpu_init init;
|
||||
|
||||
kvm_get_default_vcpu_target(vm, &init);
|
||||
kvm_vm_free(vm);
|
||||
|
||||
return init.features[0] & BIT(KVM_ARM_VCPU_HAS_EL2);
|
||||
}
|
||||
|
||||
#define for_each_conduit(conduit) \
|
||||
for (conduit = HVC_INSN; conduit <= SMC_INSN; conduit++)
|
||||
for (conduit = test_runs_at_el2() ? SMC_INSN : HVC_INSN; \
|
||||
conduit <= SMC_INSN; conduit++)
|
||||
|
||||
static void guest_main(uint32_t func_id, enum smccc_conduit conduit)
|
||||
{
|
||||
|
|
@ -64,7 +76,7 @@ static struct kvm_vm *setup_vm(struct kvm_vcpu **vcpu)
|
|||
struct kvm_vm *vm;
|
||||
|
||||
vm = vm_create(1);
|
||||
vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init);
|
||||
kvm_get_default_vcpu_target(vm, &init);
|
||||
|
||||
/*
|
||||
* Enable in-kernel emulation of PSCI to ensure that calls are denied
|
||||
|
|
@ -73,6 +85,7 @@ static struct kvm_vm *setup_vm(struct kvm_vcpu **vcpu)
|
|||
init.features[0] |= (1 << KVM_ARM_VCPU_PSCI_0_2);
|
||||
|
||||
*vcpu = aarch64_vcpu_add(vm, 0, &init, guest_main);
|
||||
kvm_arch_vm_finalize_vcpus(vm);
|
||||
return vm;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -994,6 +994,8 @@ int main(int ac, char **av)
|
|||
int pa_bits;
|
||||
int cnt_impl = 0;
|
||||
|
||||
test_disable_default_vgic();
|
||||
|
||||
pa_bits = vm_guest_mode_params[VM_MODE_DEFAULT].pa_bits;
|
||||
max_phys_size = 1ULL << pa_bits;
|
||||
|
||||
|
|
|
|||
|
|
@ -752,7 +752,6 @@ static void test_vgic(uint32_t nr_irqs, bool level_sensitive, bool eoi_split)
|
|||
vcpu_args_set(vcpu, 1, args_gva);
|
||||
|
||||
gic_fd = vgic_v3_setup(vm, 1, nr_irqs);
|
||||
__TEST_REQUIRE(gic_fd >= 0, "Failed to create vgic-v3, skipping");
|
||||
|
||||
vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT,
|
||||
guest_irq_handlers[args.eoi_split][args.level_sensitive]);
|
||||
|
|
@ -802,6 +801,9 @@ int main(int argc, char **argv)
|
|||
int opt;
|
||||
bool eoi_split = false;
|
||||
|
||||
TEST_REQUIRE(kvm_supports_vgic_v3());
|
||||
test_disable_default_vgic();
|
||||
|
||||
while ((opt = getopt(argc, argv, "hn:e:l:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'n':
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ static vm_paddr_t gpa_base;
|
|||
|
||||
static struct kvm_vm *vm;
|
||||
static struct kvm_vcpu **vcpus;
|
||||
static int gic_fd, its_fd;
|
||||
static int its_fd;
|
||||
|
||||
static struct test_data {
|
||||
bool request_vcpus_stop;
|
||||
|
|
@ -214,9 +214,6 @@ static void setup_test_data(void)
|
|||
|
||||
static void setup_gic(void)
|
||||
{
|
||||
gic_fd = vgic_v3_setup(vm, test_data.nr_cpus, 64);
|
||||
__TEST_REQUIRE(gic_fd >= 0, "Failed to create GICv3");
|
||||
|
||||
its_fd = vgic_its_setup(vm);
|
||||
}
|
||||
|
||||
|
|
@ -355,7 +352,6 @@ static void setup_vm(void)
|
|||
static void destroy_vm(void)
|
||||
{
|
||||
close(its_fd);
|
||||
close(gic_fd);
|
||||
kvm_vm_free(vm);
|
||||
free(vcpus);
|
||||
}
|
||||
|
|
@ -374,6 +370,8 @@ int main(int argc, char **argv)
|
|||
u32 nr_threads;
|
||||
int c;
|
||||
|
||||
TEST_REQUIRE(kvm_supports_vgic_v3());
|
||||
|
||||
while ((c = getopt(argc, argv, "hv:d:e:i:")) != -1) {
|
||||
switch (c) {
|
||||
case 'v':
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@
|
|||
struct vpmu_vm {
|
||||
struct kvm_vm *vm;
|
||||
struct kvm_vcpu *vcpu;
|
||||
int gic_fd;
|
||||
};
|
||||
|
||||
static struct vpmu_vm vpmu_vm;
|
||||
|
|
@ -45,11 +44,6 @@ static uint64_t get_pmcr_n(uint64_t pmcr)
|
|||
return FIELD_GET(ARMV8_PMU_PMCR_N, pmcr);
|
||||
}
|
||||
|
||||
static void set_pmcr_n(uint64_t *pmcr, uint64_t pmcr_n)
|
||||
{
|
||||
u64p_replace_bits((__u64 *) pmcr, pmcr_n, ARMV8_PMU_PMCR_N);
|
||||
}
|
||||
|
||||
static uint64_t get_counters_mask(uint64_t n)
|
||||
{
|
||||
uint64_t mask = BIT(ARMV8_PMU_CYCLE_IDX);
|
||||
|
|
@ -415,10 +409,6 @@ static void create_vpmu_vm(void *guest_code)
|
|||
.attr = KVM_ARM_VCPU_PMU_V3_IRQ,
|
||||
.addr = (uint64_t)&irq,
|
||||
};
|
||||
struct kvm_device_attr init_attr = {
|
||||
.group = KVM_ARM_VCPU_PMU_V3_CTRL,
|
||||
.attr = KVM_ARM_VCPU_PMU_V3_INIT,
|
||||
};
|
||||
|
||||
/* The test creates the vpmu_vm multiple times. Ensure a clean state */
|
||||
memset(&vpmu_vm, 0, sizeof(vpmu_vm));
|
||||
|
|
@ -431,13 +421,12 @@ static void create_vpmu_vm(void *guest_code)
|
|||
}
|
||||
|
||||
/* Create vCPU with PMUv3 */
|
||||
vm_ioctl(vpmu_vm.vm, KVM_ARM_PREFERRED_TARGET, &init);
|
||||
kvm_get_default_vcpu_target(vpmu_vm.vm, &init);
|
||||
init.features[0] |= (1 << KVM_ARM_VCPU_PMU_V3);
|
||||
vpmu_vm.vcpu = aarch64_vcpu_add(vpmu_vm.vm, 0, &init, guest_code);
|
||||
vcpu_init_descriptor_tables(vpmu_vm.vcpu);
|
||||
vpmu_vm.gic_fd = vgic_v3_setup(vpmu_vm.vm, 1, 64);
|
||||
__TEST_REQUIRE(vpmu_vm.gic_fd >= 0,
|
||||
"Failed to create vgic-v3, skipping");
|
||||
|
||||
kvm_arch_vm_finalize_vcpus(vpmu_vm.vm);
|
||||
|
||||
/* Make sure that PMUv3 support is indicated in the ID register */
|
||||
dfr0 = vcpu_get_reg(vpmu_vm.vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64DFR0_EL1));
|
||||
|
|
@ -446,14 +435,11 @@ static void create_vpmu_vm(void *guest_code)
|
|||
pmuver >= ID_AA64DFR0_EL1_PMUVer_IMP,
|
||||
"Unexpected PMUVER (0x%x) on the vCPU with PMUv3", pmuver);
|
||||
|
||||
/* Initialize vPMU */
|
||||
vcpu_ioctl(vpmu_vm.vcpu, KVM_SET_DEVICE_ATTR, &irq_attr);
|
||||
vcpu_ioctl(vpmu_vm.vcpu, KVM_SET_DEVICE_ATTR, &init_attr);
|
||||
}
|
||||
|
||||
static void destroy_vpmu_vm(void)
|
||||
{
|
||||
close(vpmu_vm.gic_fd);
|
||||
kvm_vm_free(vpmu_vm.vm);
|
||||
}
|
||||
|
||||
|
|
@ -475,33 +461,28 @@ static void run_vcpu(struct kvm_vcpu *vcpu, uint64_t pmcr_n)
|
|||
}
|
||||
}
|
||||
|
||||
static void test_create_vpmu_vm_with_pmcr_n(uint64_t pmcr_n, bool expect_fail)
|
||||
static void test_create_vpmu_vm_with_nr_counters(unsigned int nr_counters, bool expect_fail)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
uint64_t pmcr, pmcr_orig;
|
||||
unsigned int prev;
|
||||
int ret;
|
||||
|
||||
create_vpmu_vm(guest_code);
|
||||
vcpu = vpmu_vm.vcpu;
|
||||
|
||||
pmcr_orig = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_PMCR_EL0));
|
||||
pmcr = pmcr_orig;
|
||||
prev = get_pmcr_n(vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_PMCR_EL0)));
|
||||
|
||||
/*
|
||||
* Setting a larger value of PMCR.N should not modify the field, and
|
||||
* return a success.
|
||||
*/
|
||||
set_pmcr_n(&pmcr, pmcr_n);
|
||||
vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_PMCR_EL0), pmcr);
|
||||
pmcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_PMCR_EL0));
|
||||
ret = __vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
|
||||
KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS, &nr_counters);
|
||||
|
||||
if (expect_fail)
|
||||
TEST_ASSERT(pmcr_orig == pmcr,
|
||||
"PMCR.N modified by KVM to a larger value (PMCR: 0x%lx) for pmcr_n: 0x%lx",
|
||||
pmcr, pmcr_n);
|
||||
TEST_ASSERT(ret && errno == EINVAL,
|
||||
"Setting more PMU counters (%u) than available (%u) unexpectedly succeeded",
|
||||
nr_counters, prev);
|
||||
else
|
||||
TEST_ASSERT(pmcr_n == get_pmcr_n(pmcr),
|
||||
"Failed to update PMCR.N to %lu (received: %lu)",
|
||||
pmcr_n, get_pmcr_n(pmcr));
|
||||
TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_SET_DEVICE_ATTR, ret));
|
||||
|
||||
vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, KVM_ARM_VCPU_PMU_V3_INIT, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -516,11 +497,11 @@ static void run_access_test(uint64_t pmcr_n)
|
|||
|
||||
pr_debug("Test with pmcr_n %lu\n", pmcr_n);
|
||||
|
||||
test_create_vpmu_vm_with_pmcr_n(pmcr_n, false);
|
||||
test_create_vpmu_vm_with_nr_counters(pmcr_n, false);
|
||||
vcpu = vpmu_vm.vcpu;
|
||||
|
||||
/* Save the initial sp to restore them later to run the guest again */
|
||||
sp = vcpu_get_reg(vcpu, ARM64_CORE_REG(sp_el1));
|
||||
sp = vcpu_get_reg(vcpu, ctxt_reg_alias(vcpu, SYS_SP_EL1));
|
||||
|
||||
run_vcpu(vcpu, pmcr_n);
|
||||
|
||||
|
|
@ -528,11 +509,11 @@ static void run_access_test(uint64_t pmcr_n)
|
|||
* Reset and re-initialize the vCPU, and run the guest code again to
|
||||
* check if PMCR_EL0.N is preserved.
|
||||
*/
|
||||
vm_ioctl(vpmu_vm.vm, KVM_ARM_PREFERRED_TARGET, &init);
|
||||
kvm_get_default_vcpu_target(vpmu_vm.vm, &init);
|
||||
init.features[0] |= (1 << KVM_ARM_VCPU_PMU_V3);
|
||||
aarch64_vcpu_setup(vcpu, &init);
|
||||
vcpu_init_descriptor_tables(vcpu);
|
||||
vcpu_set_reg(vcpu, ARM64_CORE_REG(sp_el1), sp);
|
||||
vcpu_set_reg(vcpu, ctxt_reg_alias(vcpu, SYS_SP_EL1), sp);
|
||||
vcpu_set_reg(vcpu, ARM64_CORE_REG(regs.pc), (uint64_t)guest_code);
|
||||
|
||||
run_vcpu(vcpu, pmcr_n);
|
||||
|
|
@ -557,7 +538,7 @@ static void run_pmregs_validity_test(uint64_t pmcr_n)
|
|||
uint64_t set_reg_id, clr_reg_id, reg_val;
|
||||
uint64_t valid_counters_mask, max_counters_mask;
|
||||
|
||||
test_create_vpmu_vm_with_pmcr_n(pmcr_n, false);
|
||||
test_create_vpmu_vm_with_nr_counters(pmcr_n, false);
|
||||
vcpu = vpmu_vm.vcpu;
|
||||
|
||||
valid_counters_mask = get_counters_mask(pmcr_n);
|
||||
|
|
@ -611,7 +592,7 @@ static void run_error_test(uint64_t pmcr_n)
|
|||
{
|
||||
pr_debug("Error test with pmcr_n %lu (larger than the host)\n", pmcr_n);
|
||||
|
||||
test_create_vpmu_vm_with_pmcr_n(pmcr_n, true);
|
||||
test_create_vpmu_vm_with_nr_counters(pmcr_n, true);
|
||||
destroy_vpmu_vm();
|
||||
}
|
||||
|
||||
|
|
@ -629,11 +610,25 @@ static uint64_t get_pmcr_n_limit(void)
|
|||
return get_pmcr_n(pmcr);
|
||||
}
|
||||
|
||||
static bool kvm_supports_nr_counters_attr(void)
|
||||
{
|
||||
bool supported;
|
||||
|
||||
create_vpmu_vm(NULL);
|
||||
supported = !__vcpu_has_device_attr(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
|
||||
KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS);
|
||||
destroy_vpmu_vm();
|
||||
|
||||
return supported;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uint64_t i, pmcr_n;
|
||||
|
||||
TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_PMU_V3));
|
||||
TEST_REQUIRE(kvm_supports_vgic_v3());
|
||||
TEST_REQUIRE(kvm_supports_nr_counters_attr());
|
||||
|
||||
pmcr_n = get_pmcr_n_limit();
|
||||
for (i = 0; i <= pmcr_n; i++) {
|
||||
|
|
|
|||
|
|
@ -20,38 +20,6 @@
|
|||
#include "guest_modes.h"
|
||||
#include "ucall_common.h"
|
||||
|
||||
#ifdef __aarch64__
|
||||
#include "arm64/vgic.h"
|
||||
|
||||
static int gic_fd;
|
||||
|
||||
static void arch_setup_vm(struct kvm_vm *vm, unsigned int nr_vcpus)
|
||||
{
|
||||
/*
|
||||
* The test can still run even if hardware does not support GICv3, as it
|
||||
* is only an optimization to reduce guest exits.
|
||||
*/
|
||||
gic_fd = vgic_v3_setup(vm, nr_vcpus, 64);
|
||||
}
|
||||
|
||||
static void arch_cleanup_vm(struct kvm_vm *vm)
|
||||
{
|
||||
if (gic_fd > 0)
|
||||
close(gic_fd);
|
||||
}
|
||||
|
||||
#else /* __aarch64__ */
|
||||
|
||||
static void arch_setup_vm(struct kvm_vm *vm, unsigned int nr_vcpus)
|
||||
{
|
||||
}
|
||||
|
||||
static void arch_cleanup_vm(struct kvm_vm *vm)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* How many host loops to run by default (one KVM_GET_DIRTY_LOG for each loop)*/
|
||||
#define TEST_HOST_LOOP_N 2UL
|
||||
|
||||
|
|
@ -166,8 +134,6 @@ static void run_test(enum vm_guest_mode mode, void *arg)
|
|||
vm_enable_cap(vm, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2,
|
||||
dirty_log_manual_caps);
|
||||
|
||||
arch_setup_vm(vm, nr_vcpus);
|
||||
|
||||
/* Start the iterations */
|
||||
iteration = 0;
|
||||
host_quit = false;
|
||||
|
|
@ -285,7 +251,6 @@ static void run_test(enum vm_guest_mode mode, void *arg)
|
|||
}
|
||||
|
||||
memstress_free_bitmaps(bitmaps, p->slots);
|
||||
arch_cleanup_vm(vm);
|
||||
memstress_destroy_vm(vm);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -585,6 +585,7 @@ static struct kvm_vm *create_vm(enum vm_guest_mode mode, struct kvm_vcpu **vcpu,
|
|||
|
||||
log_mode_create_vm_done(vm);
|
||||
*vcpu = vm_vcpu_add(vm, 0, guest_code);
|
||||
kvm_arch_vm_finalize_vcpus(vm);
|
||||
return vm;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -116,10 +116,13 @@ void __weak finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_reg_list *c)
|
|||
}
|
||||
|
||||
#ifdef __aarch64__
|
||||
static void prepare_vcpu_init(struct vcpu_reg_list *c, struct kvm_vcpu_init *init)
|
||||
static void prepare_vcpu_init(struct kvm_vm *vm, struct vcpu_reg_list *c,
|
||||
struct kvm_vcpu_init *init)
|
||||
{
|
||||
struct vcpu_reg_sublist *s;
|
||||
|
||||
vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, init);
|
||||
|
||||
for_each_sublist(c, s)
|
||||
if (s->capability)
|
||||
init->features[s->feature / 32] |= 1 << (s->feature % 32);
|
||||
|
|
@ -127,10 +130,10 @@ static void prepare_vcpu_init(struct vcpu_reg_list *c, struct kvm_vcpu_init *ini
|
|||
|
||||
static struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm)
|
||||
{
|
||||
struct kvm_vcpu_init init = { .target = -1, };
|
||||
struct kvm_vcpu_init init;
|
||||
struct kvm_vcpu *vcpu;
|
||||
|
||||
prepare_vcpu_init(c, &init);
|
||||
prepare_vcpu_init(vm, c, &init);
|
||||
vcpu = __vm_vcpu_add(vm, 0);
|
||||
aarch64_vcpu_setup(vcpu, &init);
|
||||
|
||||
|
|
|
|||
|
|
@ -155,4 +155,28 @@ static inline void timer_set_next_tval_ms(enum arch_timer timer, uint32_t msec)
|
|||
timer_set_tval(timer, msec_to_cycles(msec));
|
||||
}
|
||||
|
||||
static inline u32 vcpu_get_vtimer_irq(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u32 intid;
|
||||
u64 attr;
|
||||
|
||||
attr = vcpu_has_el2(vcpu) ? KVM_ARM_VCPU_TIMER_IRQ_HVTIMER :
|
||||
KVM_ARM_VCPU_TIMER_IRQ_VTIMER;
|
||||
vcpu_device_attr_get(vcpu, KVM_ARM_VCPU_TIMER_CTRL, attr, &intid);
|
||||
|
||||
return intid;
|
||||
}
|
||||
|
||||
static inline u32 vcpu_get_ptimer_irq(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u32 intid;
|
||||
u64 attr;
|
||||
|
||||
attr = vcpu_has_el2(vcpu) ? KVM_ARM_VCPU_TIMER_IRQ_HPTIMER :
|
||||
KVM_ARM_VCPU_TIMER_IRQ_PTIMER;
|
||||
vcpu_device_attr_get(vcpu, KVM_ARM_VCPU_TIMER_CTRL, attr, &intid);
|
||||
|
||||
return intid;
|
||||
}
|
||||
|
||||
#endif /* SELFTEST_KVM_ARCH_TIMER_H */
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
#ifndef SELFTEST_KVM_UTIL_ARCH_H
|
||||
#define SELFTEST_KVM_UTIL_ARCH_H
|
||||
|
||||
struct kvm_vm_arch {};
|
||||
struct kvm_vm_arch {
|
||||
bool has_gic;
|
||||
int gic_fd;
|
||||
};
|
||||
|
||||
#endif // SELFTEST_KVM_UTIL_ARCH_H
|
||||
|
|
|
|||
|
|
@ -301,4 +301,77 @@ void smccc_smc(uint32_t function_id, uint64_t arg0, uint64_t arg1,
|
|||
/* Execute a Wait For Interrupt instruction. */
|
||||
void wfi(void);
|
||||
|
||||
void test_wants_mte(void);
|
||||
void test_disable_default_vgic(void);
|
||||
|
||||
bool vm_supports_el2(struct kvm_vm *vm);
|
||||
static bool vcpu_has_el2(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu->init.features[0] & BIT(KVM_ARM_VCPU_HAS_EL2);
|
||||
}
|
||||
|
||||
#define MAPPED_EL2_SYSREG(el2, el1) \
|
||||
case SYS_##el1: \
|
||||
if (vcpu_has_el2(vcpu)) \
|
||||
alias = SYS_##el2; \
|
||||
break
|
||||
|
||||
|
||||
static __always_inline u64 ctxt_reg_alias(struct kvm_vcpu *vcpu, u32 encoding)
|
||||
{
|
||||
u32 alias = encoding;
|
||||
|
||||
BUILD_BUG_ON(!__builtin_constant_p(encoding));
|
||||
|
||||
switch (encoding) {
|
||||
MAPPED_EL2_SYSREG(SCTLR_EL2, SCTLR_EL1);
|
||||
MAPPED_EL2_SYSREG(CPTR_EL2, CPACR_EL1);
|
||||
MAPPED_EL2_SYSREG(TTBR0_EL2, TTBR0_EL1);
|
||||
MAPPED_EL2_SYSREG(TTBR1_EL2, TTBR1_EL1);
|
||||
MAPPED_EL2_SYSREG(TCR_EL2, TCR_EL1);
|
||||
MAPPED_EL2_SYSREG(VBAR_EL2, VBAR_EL1);
|
||||
MAPPED_EL2_SYSREG(AFSR0_EL2, AFSR0_EL1);
|
||||
MAPPED_EL2_SYSREG(AFSR1_EL2, AFSR1_EL1);
|
||||
MAPPED_EL2_SYSREG(ESR_EL2, ESR_EL1);
|
||||
MAPPED_EL2_SYSREG(FAR_EL2, FAR_EL1);
|
||||
MAPPED_EL2_SYSREG(MAIR_EL2, MAIR_EL1);
|
||||
MAPPED_EL2_SYSREG(TCR2_EL2, TCR2_EL1);
|
||||
MAPPED_EL2_SYSREG(PIR_EL2, PIR_EL1);
|
||||
MAPPED_EL2_SYSREG(PIRE0_EL2, PIRE0_EL1);
|
||||
MAPPED_EL2_SYSREG(POR_EL2, POR_EL1);
|
||||
MAPPED_EL2_SYSREG(AMAIR_EL2, AMAIR_EL1);
|
||||
MAPPED_EL2_SYSREG(ELR_EL2, ELR_EL1);
|
||||
MAPPED_EL2_SYSREG(SPSR_EL2, SPSR_EL1);
|
||||
MAPPED_EL2_SYSREG(ZCR_EL2, ZCR_EL1);
|
||||
MAPPED_EL2_SYSREG(CONTEXTIDR_EL2, CONTEXTIDR_EL1);
|
||||
MAPPED_EL2_SYSREG(SCTLR2_EL2, SCTLR2_EL1);
|
||||
MAPPED_EL2_SYSREG(CNTHCTL_EL2, CNTKCTL_EL1);
|
||||
case SYS_SP_EL1:
|
||||
if (!vcpu_has_el2(vcpu))
|
||||
return ARM64_CORE_REG(sp_el1);
|
||||
|
||||
alias = SYS_SP_EL2;
|
||||
break;
|
||||
default:
|
||||
BUILD_BUG();
|
||||
}
|
||||
|
||||
return KVM_ARM64_SYS_REG(alias);
|
||||
}
|
||||
|
||||
void kvm_get_default_vcpu_target(struct kvm_vm *vm, struct kvm_vcpu_init *init);
|
||||
|
||||
static inline unsigned int get_current_el(void)
|
||||
{
|
||||
return (read_sysreg(CurrentEL) >> 2) & 0x3;
|
||||
}
|
||||
|
||||
#define do_smccc(...) \
|
||||
do { \
|
||||
if (get_current_el() == 2) \
|
||||
smccc_smc(__VA_ARGS__); \
|
||||
else \
|
||||
smccc_hvc(__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#endif /* SELFTEST_KVM_PROCESSOR_H */
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@
|
|||
((uint64_t)(flags) << 12) | \
|
||||
index)
|
||||
|
||||
bool kvm_supports_vgic_v3(void);
|
||||
int __vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs);
|
||||
void __vgic_v3_init(int fd);
|
||||
int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs);
|
||||
|
||||
#define VGIC_MAX_RESERVED 1023
|
||||
|
|
|
|||
|
|
@ -63,6 +63,9 @@ struct kvm_vcpu {
|
|||
struct kvm_run *run;
|
||||
#ifdef __x86_64__
|
||||
struct kvm_cpuid2 *cpuid;
|
||||
#endif
|
||||
#ifdef __aarch64__
|
||||
struct kvm_vcpu_init init;
|
||||
#endif
|
||||
struct kvm_binary_stats stats;
|
||||
struct kvm_dirty_gfn *dirty_gfns;
|
||||
|
|
@ -1257,7 +1260,9 @@ static inline int __vm_disable_nx_huge_pages(struct kvm_vm *vm)
|
|||
*/
|
||||
void kvm_selftest_arch_init(void);
|
||||
|
||||
void kvm_arch_vm_post_create(struct kvm_vm *vm);
|
||||
void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus);
|
||||
void kvm_arch_vm_finalize_vcpus(struct kvm_vm *vm);
|
||||
void kvm_arch_vm_release(struct kvm_vm *vm);
|
||||
|
||||
bool vm_is_gpa_protected(struct kvm_vm *vm, vm_paddr_t paddr);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include "kvm_util.h"
|
||||
#include "processor.h"
|
||||
#include "ucall_common.h"
|
||||
#include "vgic.h"
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/sizes.h>
|
||||
|
|
@ -277,31 +278,49 @@ void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent)
|
|||
}
|
||||
}
|
||||
|
||||
bool vm_supports_el2(struct kvm_vm *vm)
|
||||
{
|
||||
const char *value = getenv("NV");
|
||||
|
||||
if (value && *value == '0')
|
||||
return false;
|
||||
|
||||
return vm_check_cap(vm, KVM_CAP_ARM_EL2) && vm->arch.has_gic;
|
||||
}
|
||||
|
||||
void kvm_get_default_vcpu_target(struct kvm_vm *vm, struct kvm_vcpu_init *init)
|
||||
{
|
||||
struct kvm_vcpu_init preferred = {};
|
||||
|
||||
vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &preferred);
|
||||
if (vm_supports_el2(vm))
|
||||
preferred.features[0] |= BIT(KVM_ARM_VCPU_HAS_EL2);
|
||||
|
||||
*init = preferred;
|
||||
}
|
||||
|
||||
void aarch64_vcpu_setup(struct kvm_vcpu *vcpu, struct kvm_vcpu_init *init)
|
||||
{
|
||||
struct kvm_vcpu_init default_init = { .target = -1, };
|
||||
struct kvm_vm *vm = vcpu->vm;
|
||||
uint64_t sctlr_el1, tcr_el1, ttbr0_el1;
|
||||
|
||||
if (!init)
|
||||
if (!init) {
|
||||
kvm_get_default_vcpu_target(vm, &default_init);
|
||||
init = &default_init;
|
||||
|
||||
if (init->target == -1) {
|
||||
struct kvm_vcpu_init preferred;
|
||||
vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &preferred);
|
||||
init->target = preferred.target;
|
||||
}
|
||||
|
||||
vcpu_ioctl(vcpu, KVM_ARM_VCPU_INIT, init);
|
||||
vcpu->init = *init;
|
||||
|
||||
/*
|
||||
* Enable FP/ASIMD to avoid trapping when accessing Q0-Q15
|
||||
* registers, which the variable argument list macros do.
|
||||
*/
|
||||
vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_CPACR_EL1), 3 << 20);
|
||||
vcpu_set_reg(vcpu, ctxt_reg_alias(vcpu, SYS_CPACR_EL1), 3 << 20);
|
||||
|
||||
sctlr_el1 = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_SCTLR_EL1));
|
||||
tcr_el1 = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_TCR_EL1));
|
||||
sctlr_el1 = vcpu_get_reg(vcpu, ctxt_reg_alias(vcpu, SYS_SCTLR_EL1));
|
||||
tcr_el1 = vcpu_get_reg(vcpu, ctxt_reg_alias(vcpu, SYS_TCR_EL1));
|
||||
|
||||
/* Configure base granule size */
|
||||
switch (vm->mode) {
|
||||
|
|
@ -368,11 +387,17 @@ void aarch64_vcpu_setup(struct kvm_vcpu *vcpu, struct kvm_vcpu_init *init)
|
|||
if (use_lpa2_pte_format(vm))
|
||||
tcr_el1 |= TCR_DS;
|
||||
|
||||
vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_SCTLR_EL1), sctlr_el1);
|
||||
vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_TCR_EL1), tcr_el1);
|
||||
vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MAIR_EL1), DEFAULT_MAIR_EL1);
|
||||
vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_TTBR0_EL1), ttbr0_el1);
|
||||
vcpu_set_reg(vcpu, ctxt_reg_alias(vcpu, SYS_SCTLR_EL1), sctlr_el1);
|
||||
vcpu_set_reg(vcpu, ctxt_reg_alias(vcpu, SYS_TCR_EL1), tcr_el1);
|
||||
vcpu_set_reg(vcpu, ctxt_reg_alias(vcpu, SYS_MAIR_EL1), DEFAULT_MAIR_EL1);
|
||||
vcpu_set_reg(vcpu, ctxt_reg_alias(vcpu, SYS_TTBR0_EL1), ttbr0_el1);
|
||||
vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_TPIDR_EL1), vcpu->id);
|
||||
|
||||
if (!vcpu_has_el2(vcpu))
|
||||
return;
|
||||
|
||||
vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_HCR_EL2),
|
||||
HCR_EL2_RW | HCR_EL2_TGE | HCR_EL2_E2H);
|
||||
}
|
||||
|
||||
void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, uint8_t indent)
|
||||
|
|
@ -406,7 +431,7 @@ static struct kvm_vcpu *__aarch64_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id,
|
|||
|
||||
aarch64_vcpu_setup(vcpu, init);
|
||||
|
||||
vcpu_set_reg(vcpu, ARM64_CORE_REG(sp_el1), stack_vaddr + stack_size);
|
||||
vcpu_set_reg(vcpu, ctxt_reg_alias(vcpu, SYS_SP_EL1), stack_vaddr + stack_size);
|
||||
return vcpu;
|
||||
}
|
||||
|
||||
|
|
@ -476,7 +501,7 @@ void vcpu_init_descriptor_tables(struct kvm_vcpu *vcpu)
|
|||
{
|
||||
extern char vectors;
|
||||
|
||||
vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_VBAR_EL1), (uint64_t)&vectors);
|
||||
vcpu_set_reg(vcpu, ctxt_reg_alias(vcpu, SYS_VBAR_EL1), (uint64_t)&vectors);
|
||||
}
|
||||
|
||||
void route_exception(struct ex_regs *regs, int vector)
|
||||
|
|
@ -664,3 +689,39 @@ void wfi(void)
|
|||
{
|
||||
asm volatile("wfi");
|
||||
}
|
||||
|
||||
static bool request_mte;
|
||||
static bool request_vgic = true;
|
||||
|
||||
void test_wants_mte(void)
|
||||
{
|
||||
request_mte = true;
|
||||
}
|
||||
|
||||
void test_disable_default_vgic(void)
|
||||
{
|
||||
request_vgic = false;
|
||||
}
|
||||
|
||||
void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus)
|
||||
{
|
||||
if (request_mte && vm_check_cap(vm, KVM_CAP_ARM_MTE))
|
||||
vm_enable_cap(vm, KVM_CAP_ARM_MTE, 0);
|
||||
|
||||
if (request_vgic && kvm_supports_vgic_v3()) {
|
||||
vm->arch.gic_fd = __vgic_v3_setup(vm, nr_vcpus, 64);
|
||||
vm->arch.has_gic = true;
|
||||
}
|
||||
}
|
||||
|
||||
void kvm_arch_vm_finalize_vcpus(struct kvm_vm *vm)
|
||||
{
|
||||
if (vm->arch.has_gic)
|
||||
__vgic_v3_init(vm->arch.gic_fd);
|
||||
}
|
||||
|
||||
void kvm_arch_vm_release(struct kvm_vm *vm)
|
||||
{
|
||||
if (vm->arch.has_gic)
|
||||
close(vm->arch.gic_fd);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,17 @@
|
|||
#include "gic.h"
|
||||
#include "gic_v3.h"
|
||||
|
||||
bool kvm_supports_vgic_v3(void)
|
||||
{
|
||||
struct kvm_vm *vm = vm_create_barebones();
|
||||
int r;
|
||||
|
||||
r = __kvm_test_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3);
|
||||
kvm_vm_free(vm);
|
||||
|
||||
return !r;
|
||||
}
|
||||
|
||||
/*
|
||||
* vGIC-v3 default host setup
|
||||
*
|
||||
|
|
@ -30,24 +41,11 @@
|
|||
* redistributor regions of the guest. Since it depends on the number of
|
||||
* vCPUs for the VM, it must be called after all the vCPUs have been created.
|
||||
*/
|
||||
int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs)
|
||||
int __vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs)
|
||||
{
|
||||
int gic_fd;
|
||||
uint64_t attr;
|
||||
struct list_head *iter;
|
||||
unsigned int nr_gic_pages, nr_vcpus_created = 0;
|
||||
|
||||
TEST_ASSERT(nr_vcpus, "Number of vCPUs cannot be empty");
|
||||
|
||||
/*
|
||||
* Make sure that the caller is infact calling this
|
||||
* function after all the vCPUs are added.
|
||||
*/
|
||||
list_for_each(iter, &vm->vcpus)
|
||||
nr_vcpus_created++;
|
||||
TEST_ASSERT(nr_vcpus == nr_vcpus_created,
|
||||
"Number of vCPUs requested (%u) doesn't match with the ones created for the VM (%u)",
|
||||
nr_vcpus, nr_vcpus_created);
|
||||
unsigned int nr_gic_pages;
|
||||
|
||||
/* Distributor setup */
|
||||
gic_fd = __kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3);
|
||||
|
|
@ -56,9 +54,6 @@ int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs)
|
|||
|
||||
kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, &nr_irqs);
|
||||
|
||||
kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
|
||||
|
||||
attr = GICD_BASE_GPA;
|
||||
kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
KVM_VGIC_V3_ADDR_TYPE_DIST, &attr);
|
||||
|
|
@ -73,12 +68,41 @@ int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs)
|
|||
KVM_VGIC_V3_REDIST_SIZE * nr_vcpus);
|
||||
virt_map(vm, GICR_BASE_GPA, GICR_BASE_GPA, nr_gic_pages);
|
||||
|
||||
kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
|
||||
|
||||
return gic_fd;
|
||||
}
|
||||
|
||||
void __vgic_v3_init(int fd)
|
||||
{
|
||||
kvm_device_attr_set(fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
|
||||
}
|
||||
|
||||
int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs)
|
||||
{
|
||||
unsigned int nr_vcpus_created = 0;
|
||||
struct list_head *iter;
|
||||
int fd;
|
||||
|
||||
TEST_ASSERT(nr_vcpus, "Number of vCPUs cannot be empty");
|
||||
|
||||
/*
|
||||
* Make sure that the caller is infact calling this
|
||||
* function after all the vCPUs are added.
|
||||
*/
|
||||
list_for_each(iter, &vm->vcpus)
|
||||
nr_vcpus_created++;
|
||||
TEST_ASSERT(nr_vcpus == nr_vcpus_created,
|
||||
"Number of vCPUs requested (%u) doesn't match with the ones created for the VM (%u)",
|
||||
nr_vcpus, nr_vcpus_created);
|
||||
|
||||
fd = __vgic_v3_setup(vm, nr_vcpus, nr_irqs);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
__vgic_v3_init(fd);
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* should only work for level sensitive interrupts */
|
||||
int _kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -517,7 +517,7 @@ struct kvm_vm *__vm_create(struct vm_shape shape, uint32_t nr_runnable_vcpus,
|
|||
guest_rng = new_guest_random_state(guest_random_seed);
|
||||
sync_global_to_guest(vm, guest_rng);
|
||||
|
||||
kvm_arch_vm_post_create(vm);
|
||||
kvm_arch_vm_post_create(vm, nr_runnable_vcpus);
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
|
@ -555,6 +555,7 @@ struct kvm_vm *__vm_create_with_vcpus(struct vm_shape shape, uint32_t nr_vcpus,
|
|||
for (i = 0; i < nr_vcpus; ++i)
|
||||
vcpus[i] = vm_vcpu_add(vm, i, guest_code);
|
||||
|
||||
kvm_arch_vm_finalize_vcpus(vm);
|
||||
return vm;
|
||||
}
|
||||
|
||||
|
|
@ -805,6 +806,8 @@ void kvm_vm_release(struct kvm_vm *vmp)
|
|||
|
||||
/* Free cached stats metadata and close FD */
|
||||
kvm_stats_release(&vmp->stats);
|
||||
|
||||
kvm_arch_vm_release(vmp);
|
||||
}
|
||||
|
||||
static void __vm_mem_region_delete(struct kvm_vm *vm,
|
||||
|
|
@ -2330,7 +2333,15 @@ void kvm_get_stat(struct kvm_binary_stats *stats, const char *name,
|
|||
TEST_FAIL("Unable to find stat '%s'", name);
|
||||
}
|
||||
|
||||
__weak void kvm_arch_vm_post_create(struct kvm_vm *vm)
|
||||
__weak void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus)
|
||||
{
|
||||
}
|
||||
|
||||
__weak void kvm_arch_vm_finalize_vcpus(struct kvm_vm *vm)
|
||||
{
|
||||
}
|
||||
|
||||
__weak void kvm_arch_vm_release(struct kvm_vm *vm)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -625,7 +625,7 @@ void assert_on_unhandled_exception(struct kvm_vcpu *vcpu)
|
|||
REPORT_GUEST_ASSERT(uc);
|
||||
}
|
||||
|
||||
void kvm_arch_vm_post_create(struct kvm_vm *vm)
|
||||
void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus)
|
||||
{
|
||||
int r;
|
||||
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ static void finish_vm_setup(struct kvm_vm *vm)
|
|||
slot0 = memslot2region(vm, 0);
|
||||
ucall_init(vm, slot0->region.guest_phys_addr + slot0->region.memory_size);
|
||||
|
||||
kvm_arch_vm_post_create(vm);
|
||||
kvm_arch_vm_post_create(vm, 0);
|
||||
}
|
||||
|
||||
static struct kvm_vm *create_vm_two_memslots(void)
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ static int64_t smccc(uint32_t func, uint64_t arg)
|
|||
{
|
||||
struct arm_smccc_res res;
|
||||
|
||||
smccc_hvc(func, arg, 0, 0, 0, 0, 0, 0, &res);
|
||||
do_smccc(func, arg, 0, 0, 0, 0, 0, 0, &res);
|
||||
return res.a0;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user