mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
Merge branch kvm-arm64/misc-7.4 into kvmarm-master/next
* kvm-arm64/misc-7.4: : \ : Misc KVM/arm64 fixes for 7.4: : : - Correctly report SMCCC_VERSION and SMCCC_ARCH_FEATURES as implemented : : - Don't register a page table dumper for pKVM VMs, as S2 is not : accessible when in protected mode : : - Drop PAGE_SIZE alignment requirement for hyp trace event sections : : - Tighten PMUv3 being disabled for the guest even if vcpus were created : with one. : : - Fix timer emulation when the HW doesn't support offsetting of the : counter. : / KVM: arm64: selftests: Test a timer set past the counter's wrap KVM: arm64: nv: Read a guest hypervisor's CNTV_CVAL_EL0 from memory on x1e KVM: arm64: timers: Compute an offset-applied CVAL from the current count KVM: arm64: Check ID_AA64DFR0_EL1.PMUVer in pmu_visibility() KVM: arm64: Handle ID_AA64DFR0_EL1.PMUVer == NI in __kvm_pmu_event_mask() KVM: arm64: Remove PAGE_SIZE alignment for hyp event ELF sections KVM: arm64: Disable stage-2 ptdump of pKVM KVM: arm64: selftests: Check the mandatory SMCCC_ARCH_FEATURES queries KVM: arm64: Report SMCCC_VERSION and SMCCC_ARCH_FEATURES as implemented Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
commit
fddb672002
|
|
@ -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 = .;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <errno.h>
|
||||
#include <linux/arm-smccc.h>
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user