diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S index af1d72020976..c0b090db8be4 100644 --- a/arch/arm64/kernel/vmlinux.lds.S +++ b/arch/arm64/kernel/vmlinux.lds.S @@ -15,7 +15,7 @@ #ifdef CONFIG_NVHE_EL2_TRACING #define HYPERVISOR_EVENT_IDS \ - . = ALIGN(PAGE_SIZE); \ + . = ALIGN(SZ_8); \ __hyp_event_ids_start = .; \ *(HYP_SECTION_NAME(.event_ids)) \ __hyp_event_ids_end = .; diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c index 6ac3321f4c57..9278b5383c04 100644 --- a/arch/arm64/kvm/arch_timer.c +++ b/arch/arm64/kvm/arch_timer.c @@ -528,17 +528,11 @@ static void timer_save_state(struct arch_timer_context *ctx) goto out; switch (index) { - u64 cval; - case TIMER_VTIMER: case TIMER_HVTIMER: timer_set_ctl(ctx, read_sysreg_el0(SYS_CNTV_CTL)); - cval = read_sysreg_el0(SYS_CNTV_CVAL); - - if (has_broken_cntvoff()) - cval -= timer_get_offset(ctx); - - timer_set_cval(ctx, cval); + if (!has_broken_cntvoff() || !timer_get_offset(ctx)) + timer_set_cval(ctx, read_sysreg_el0(SYS_CNTV_CVAL)); /* Disable the timer */ write_sysreg_el0(0, SYS_CNTV_CTL); @@ -564,11 +558,12 @@ static void timer_save_state(struct arch_timer_context *ctx) case TIMER_PTIMER: case TIMER_HPTIMER: timer_set_ctl(ctx, read_sysreg_el0(SYS_CNTP_CTL)); - cval = read_sysreg_el0(SYS_CNTP_CVAL); - - cval -= timer_get_offset(ctx); - - timer_set_cval(ctx, cval); + /* + * With an offset, memory already holds the guest's CVAL (the + * trap handler or __deactivate_traps() wrote it). + */ + if (!timer_get_offset(ctx)) + timer_set_cval(ctx, read_sysreg_el0(SYS_CNTP_CVAL)); /* Disable the timer */ write_sysreg_el0(0, SYS_CNTP_CTL); @@ -647,7 +642,8 @@ static void timer_restore_state(struct arch_timer_context *ctx) offset = timer_get_offset(ctx); if (has_broken_cntvoff()) { set_cntvoff(0); - cval += offset; + if (offset) + cval = timer_apply_offset(cval, offset, kvm_phys_timer_read()); } else { set_cntvoff(offset); } @@ -660,7 +656,8 @@ static void timer_restore_state(struct arch_timer_context *ctx) cval = timer_get_cval(ctx); offset = timer_get_offset(ctx); set_cntpoff(offset); - cval += offset; + if (offset) + cval = timer_apply_offset(cval, offset, kvm_phys_timer_read()); write_sysreg_el0(cval, SYS_CNTP_CVAL); isb(); write_sysreg_el0(timer_get_ctl(ctx), SYS_CNTP_CTL); diff --git a/arch/arm64/kvm/hyp/nvhe/hyp.lds.S b/arch/arm64/kvm/hyp/nvhe/hyp.lds.S index 7a02837203d1..89222425359a 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp.lds.S +++ b/arch/arm64/kvm/hyp/nvhe/hyp.lds.S @@ -17,7 +17,6 @@ SECTIONS { HYP_SECTION(.data..ro_after_init) HYP_SECTION(.rodata) #ifdef CONFIG_NVHE_EL2_TRACING - . = ALIGN(PAGE_SIZE); BEGIN_HYP_SECTION(.event_ids) *(SORT(.hyp.event_ids.*)) END_HYP_SECTION diff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c index 7875911c0506..4c965f9b1e1d 100644 --- a/arch/arm64/kvm/hyp/vhe/switch.c +++ b/arch/arm64/kvm/hyp/vhe/switch.c @@ -175,7 +175,8 @@ static void __deactivate_traps(struct kvm_vcpu *vcpu) offset = read_sysreg_s(SYS_CNTPOFF_EL2); if (map.direct_ptimer && offset) { - write_sysreg_el0(val + offset, SYS_CNTP_CVAL); + val = timer_apply_offset(val, offset, arch_timer_read_cntpct_el0()); + write_sysreg_el0(val, SYS_CNTP_CVAL); isb(); } } @@ -296,10 +297,10 @@ static bool kvm_hyp_handle_timer(struct kvm_vcpu *vcpu, u64 *exit_code) break; case SYS_CNTP_CVAL_EL0: if (vcpu_el2_e2h_is_set(vcpu)) { - val = read_sysreg_el0(SYS_CNTP_CVAL); - - if (!has_cntpoff()) - val -= timer_get_offset(vcpu_hptimer(vcpu)); + if (!has_cntpoff() && timer_get_offset(vcpu_hptimer(vcpu))) + val = __vcpu_sys_reg(vcpu, CNTHP_CVAL_EL2); + else + val = read_sysreg_el0(SYS_CNTP_CVAL); } else { val = __vcpu_sys_reg(vcpu, CNTP_CVAL_EL0); } @@ -321,10 +322,14 @@ static bool kvm_hyp_handle_timer(struct kvm_vcpu *vcpu, u64 *exit_code) val = __vcpu_sys_reg(vcpu, CNTV_CVAL_EL0); break; case SYS_CNTV_CVAL_EL0: - if (vcpu_el2_e2h_is_set(vcpu)) - val = read_sysreg_el0(SYS_CNTV_CVAL); - else + if (vcpu_el2_e2h_is_set(vcpu)) { + if (has_broken_cntvoff() && timer_get_offset(vcpu_hvtimer(vcpu))) + val = __vcpu_sys_reg(vcpu, CNTHV_CVAL_EL2); + else + val = read_sysreg_el0(SYS_CNTV_CVAL); + } else { val = __vcpu_sys_reg(vcpu, CNTV_CVAL_EL0); + } break; case SYS_CNTVCT_EL0: case SYS_CNTVCTSS_EL0: diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c index dfa25bb6f25d..b393ba9af5b6 100644 --- a/arch/arm64/kvm/hypercalls.c +++ b/arch/arm64/kvm/hypercalls.c @@ -294,6 +294,10 @@ int kvm_smccc_call_handler(struct kvm_vcpu *vcpu) case ARM_SMCCC_ARCH_FEATURES_FUNC_ID: feature = smccc_get_arg1(vcpu); switch (feature) { + case ARM_SMCCC_VERSION_FUNC_ID: + case ARM_SMCCC_ARCH_FEATURES_FUNC_ID: + val[0] = SMCCC_RET_SUCCESS; + break; case ARM_SMCCC_ARCH_WORKAROUND_1: switch (arm64_get_spectre_v2_state()) { case SPECTRE_VULNERABLE: diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c index 5b1af7e2176f..e2b28dbc0451 100644 --- a/arch/arm64/kvm/pmu-emul.c +++ b/arch/arm64/kvm/pmu-emul.c @@ -50,8 +50,11 @@ static u32 __kvm_pmu_event_mask(unsigned int pmuver) case ID_AA64DFR0_EL1_PMUVer_V3P5: case ID_AA64DFR0_EL1_PMUVer_V3P7: return GENMASK(15, 0); - default: /* Shouldn't be here, just for sanity */ + default: + /* Shouldn't be here, just for sanity */ WARN_ONCE(1, "Unknown PMU version %d\n", pmuver); + fallthrough; + case ID_AA64DFR0_EL1_PMUVer_NI: return 0; } } diff --git a/arch/arm64/kvm/ptdump.c b/arch/arm64/kvm/ptdump.c index 7829fe376385..9f0aebacea5b 100644 --- a/arch/arm64/kvm/ptdump.c +++ b/arch/arm64/kvm/ptdump.c @@ -321,6 +321,10 @@ void kvm_nested_s2_ptdump_create_debugfs(struct kvm_s2_mmu *mmu, int idx) void kvm_s2_ptdump_create_debugfs(struct kvm *kvm) { + /* pKVM handles the page-tables in the hypervisor */ + if (is_protected_kvm_enabled()) + return; + debugfs_create_file("stage2_page_tables", 0400, kvm->debugfs_dentry, &kvm->arch.mmu, &kvm_ptdump_guest_fops); debugfs_create_file("ipa_range", 0400, kvm->debugfs_dentry, diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index d80008b56171..bb76aad0660b 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -1029,7 +1029,7 @@ static unsigned int hidden_visibility(const struct kvm_vcpu *vcpu, static unsigned int pmu_visibility(const struct kvm_vcpu *vcpu, const struct sys_reg_desc *r) { - if (kvm_vcpu_has_pmu(vcpu)) + if (kvm_has_feat(vcpu->kvm, ID_AA64DFR0_EL1, PMUVer, IMP)) return 0; return REG_HIDDEN; @@ -1059,10 +1059,6 @@ static u64 reset_pmevcntr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r) static u64 reset_pmevtyper(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r) { - /* This thing will UNDEF, who cares about the reset value? */ - if (!kvm_vcpu_has_pmu(vcpu)) - return 0; - reset_unknown(vcpu, r); __vcpu_rmw_sys_reg(vcpu, r->reg, &=, kvm_pmu_evtyper_mask(vcpu->kvm)); diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h index bc6f2fdd7ad3..80f96ea59f1a 100644 --- a/include/kvm/arm_arch_timer.h +++ b/include/kvm/arm_arch_timer.h @@ -195,4 +195,19 @@ static inline void timer_set_offset(struct arch_timer_context *ctxt, u64 offset) WRITE_ONCE(*ctxt->offset.vm_offset, offset); } +/* + * CVAL to program when the hardware won't apply the timer's offset, so that + * its compare against the raw counter matches the guest's at 'now'. An + * expired timer stays expired; one past the wrap never fires. + */ +static inline u64 timer_apply_offset(u64 cval, u64 offset, u64 now) +{ + u64 hw = cval + offset; + + if (now - offset >= cval) + return 0; + + return hw > now ? hw : U64_MAX; +} + #endif diff --git a/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c b/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c index d9c9377a6325..d46799ff91dc 100644 --- a/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c +++ b/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c @@ -787,6 +787,30 @@ static void test_timers_in_the_past(enum arch_timer timer) } } +/* + * A cval within long_wait_ms of the counter's wrap while the counter sits at + * wait_ms: a deadline further away than the counter can reach. Unmasked, so + * an early ISTATUS shows up as an IRQ. + */ +static void test_timers_past_counter_wrap(enum arch_timer timer) +{ + u64 cval = -msec_to_cycles(test_args.long_wait_ms); + int i; + + for (i = 0; i < ARRAY_SIZE(sleep_method); i++) { + sleep_method_t sm = sleep_method[i]; + + local_irq_disable(); + set_counter(timer, msec_to_cycles(test_args.wait_ms)); + set_cval_irq(timer, cval, CTL_ENABLE); + sm(timer, msecs_to_usecs(test_args.wait_ms) + TIMEOUT_NO_IRQ_US); + local_irq_enable(); + isb(); + assert_irqs_handled(0); + timer_set_ctl(timer, CTL_IMASK); + } +} + static void test_long_timer_delays(enum arch_timer timer) { s32 tval = (s32)msec_to_cycles(test_args.long_wait_ms); @@ -806,6 +830,7 @@ static void guest_run_iteration(enum arch_timer timer) test_basic_functionality(timer); test_timers_sanity_checks(timer); + test_timers_past_counter_wrap(timer); test_timers_above_tval_max(timer); test_timers_in_the_past(timer); diff --git a/tools/testing/selftests/kvm/arm64/hypercalls.c b/tools/testing/selftests/kvm/arm64/hypercalls.c index a24becda9e62..391e2ba58b0b 100644 --- a/tools/testing/selftests/kvm/arm64/hypercalls.c +++ b/tools/testing/selftests/kvm/arm64/hypercalls.c @@ -6,7 +6,8 @@ * via the pseudo-firmware bitmap register. This includes the registers' * read/write behavior before and after the VM has started, and if the * hypercalls are properly masked or unmasked to the guest when disabled or - * enabled from the KVM userspace, respectively. + * enabled from the KVM userspace, respectively. It also checks the two + * SMCCC_ARCH_FEATURES queries whose answer SMCCC pins to SUCCESS. */ #include #include @@ -97,6 +98,12 @@ static const struct test_hvc_info false_hvc_info[] = { TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_TRNG_RND64), }; +/* SMCCC pins the answer to SUCCESS, whatever the bitmaps hold. */ +static const struct test_hvc_info always_hvc_info[] = { + TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_VERSION_FUNC_ID), + TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_ARCH_FEATURES_FUNC_ID), +}; + static void guest_test_hvc(const struct test_hvc_info *hc_info) { unsigned int i; @@ -128,6 +135,23 @@ static void guest_test_hvc(const struct test_hvc_info *hc_info) } } +static void guest_test_always_hvc(void) +{ + struct arm_smccc_res res; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(always_hvc_info); i++) { + memset(&res, 0, sizeof(res)); + do_smccc(always_hvc_info[i].func_id, always_hvc_info[i].arg1, + 0, 0, 0, 0, 0, 0, &res); + + __GUEST_ASSERT(res.a0 == SMCCC_RET_SUCCESS, + "a0 = 0x%lx, func_id = 0x%x, arg1 = 0x%lx, stage = %u", + res.a0, always_hvc_info[i].func_id, + always_hvc_info[i].arg1, stage); + } +} + static void guest_code(void) { while (stage != TEST_STAGE_END) { @@ -137,6 +161,7 @@ static void guest_code(void) case TEST_STAGE_HVC_IFACE_FEAT_DISABLED: case TEST_STAGE_HVC_IFACE_FEAT_ENABLED: guest_test_hvc(hvc_info); + guest_test_always_hvc(); break; case TEST_STAGE_HVC_IFACE_FALSE_INFO: guest_test_hvc(false_hvc_info);