Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * psci_cpu_on_test - Test that the observable state of a vCPU targeted by the
0004  * CPU_ON PSCI call matches what the caller requested.
0005  *
0006  * Copyright (c) 2021 Google LLC.
0007  *
0008  * This is a regression test for a race between KVM servicing the PSCI call and
0009  * userspace reading the vCPUs registers.
0010  */
0011 
0012 #define _GNU_SOURCE
0013 
0014 #include <linux/psci.h>
0015 
0016 #include "kvm_util.h"
0017 #include "processor.h"
0018 #include "test_util.h"
0019 
0020 #define CPU_ON_ENTRY_ADDR 0xfeedf00dul
0021 #define CPU_ON_CONTEXT_ID 0xdeadc0deul
0022 
0023 static uint64_t psci_cpu_on(uint64_t target_cpu, uint64_t entry_addr,
0024                 uint64_t context_id)
0025 {
0026     struct arm_smccc_res res;
0027 
0028     smccc_hvc(PSCI_0_2_FN64_CPU_ON, target_cpu, entry_addr, context_id,
0029           0, 0, 0, 0, &res);
0030 
0031     return res.a0;
0032 }
0033 
0034 static uint64_t psci_affinity_info(uint64_t target_affinity,
0035                    uint64_t lowest_affinity_level)
0036 {
0037     struct arm_smccc_res res;
0038 
0039     smccc_hvc(PSCI_0_2_FN64_AFFINITY_INFO, target_affinity, lowest_affinity_level,
0040           0, 0, 0, 0, 0, &res);
0041 
0042     return res.a0;
0043 }
0044 
0045 static uint64_t psci_system_suspend(uint64_t entry_addr, uint64_t context_id)
0046 {
0047     struct arm_smccc_res res;
0048 
0049     smccc_hvc(PSCI_1_0_FN64_SYSTEM_SUSPEND, entry_addr, context_id,
0050           0, 0, 0, 0, 0, &res);
0051 
0052     return res.a0;
0053 }
0054 
0055 static uint64_t psci_features(uint32_t func_id)
0056 {
0057     struct arm_smccc_res res;
0058 
0059     smccc_hvc(PSCI_1_0_FN_PSCI_FEATURES, func_id, 0, 0, 0, 0, 0, 0, &res);
0060 
0061     return res.a0;
0062 }
0063 
0064 static void vcpu_power_off(struct kvm_vcpu *vcpu)
0065 {
0066     struct kvm_mp_state mp_state = {
0067         .mp_state = KVM_MP_STATE_STOPPED,
0068     };
0069 
0070     vcpu_mp_state_set(vcpu, &mp_state);
0071 }
0072 
0073 static struct kvm_vm *setup_vm(void *guest_code, struct kvm_vcpu **source,
0074                    struct kvm_vcpu **target)
0075 {
0076     struct kvm_vcpu_init init;
0077     struct kvm_vm *vm;
0078 
0079     vm = vm_create(2);
0080     ucall_init(vm, NULL);
0081 
0082     vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init);
0083     init.features[0] |= (1 << KVM_ARM_VCPU_PSCI_0_2);
0084 
0085     *source = aarch64_vcpu_add(vm, 0, &init, guest_code);
0086     *target = aarch64_vcpu_add(vm, 1, &init, guest_code);
0087 
0088     return vm;
0089 }
0090 
0091 static void enter_guest(struct kvm_vcpu *vcpu)
0092 {
0093     struct ucall uc;
0094 
0095     vcpu_run(vcpu);
0096     if (get_ucall(vcpu, &uc) == UCALL_ABORT)
0097         REPORT_GUEST_ASSERT(uc);
0098 }
0099 
0100 static void assert_vcpu_reset(struct kvm_vcpu *vcpu)
0101 {
0102     uint64_t obs_pc, obs_x0;
0103 
0104     vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.pc), &obs_pc);
0105     vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.regs[0]), &obs_x0);
0106 
0107     TEST_ASSERT(obs_pc == CPU_ON_ENTRY_ADDR,
0108             "unexpected target cpu pc: %lx (expected: %lx)",
0109             obs_pc, CPU_ON_ENTRY_ADDR);
0110     TEST_ASSERT(obs_x0 == CPU_ON_CONTEXT_ID,
0111             "unexpected target context id: %lx (expected: %lx)",
0112             obs_x0, CPU_ON_CONTEXT_ID);
0113 }
0114 
0115 static void guest_test_cpu_on(uint64_t target_cpu)
0116 {
0117     uint64_t target_state;
0118 
0119     GUEST_ASSERT(!psci_cpu_on(target_cpu, CPU_ON_ENTRY_ADDR, CPU_ON_CONTEXT_ID));
0120 
0121     do {
0122         target_state = psci_affinity_info(target_cpu, 0);
0123 
0124         GUEST_ASSERT((target_state == PSCI_0_2_AFFINITY_LEVEL_ON) ||
0125                  (target_state == PSCI_0_2_AFFINITY_LEVEL_OFF));
0126     } while (target_state != PSCI_0_2_AFFINITY_LEVEL_ON);
0127 
0128     GUEST_DONE();
0129 }
0130 
0131 static void host_test_cpu_on(void)
0132 {
0133     struct kvm_vcpu *source, *target;
0134     uint64_t target_mpidr;
0135     struct kvm_vm *vm;
0136     struct ucall uc;
0137 
0138     vm = setup_vm(guest_test_cpu_on, &source, &target);
0139 
0140     /*
0141      * make sure the target is already off when executing the test.
0142      */
0143     vcpu_power_off(target);
0144 
0145     vcpu_get_reg(target, KVM_ARM64_SYS_REG(SYS_MPIDR_EL1), &target_mpidr);
0146     vcpu_args_set(source, 1, target_mpidr & MPIDR_HWID_BITMASK);
0147     enter_guest(source);
0148 
0149     if (get_ucall(source, &uc) != UCALL_DONE)
0150         TEST_FAIL("Unhandled ucall: %lu", uc.cmd);
0151 
0152     assert_vcpu_reset(target);
0153     kvm_vm_free(vm);
0154 }
0155 
0156 static void guest_test_system_suspend(void)
0157 {
0158     uint64_t ret;
0159 
0160     /* assert that SYSTEM_SUSPEND is discoverable */
0161     GUEST_ASSERT(!psci_features(PSCI_1_0_FN_SYSTEM_SUSPEND));
0162     GUEST_ASSERT(!psci_features(PSCI_1_0_FN64_SYSTEM_SUSPEND));
0163 
0164     ret = psci_system_suspend(CPU_ON_ENTRY_ADDR, CPU_ON_CONTEXT_ID);
0165     GUEST_SYNC(ret);
0166 }
0167 
0168 static void host_test_system_suspend(void)
0169 {
0170     struct kvm_vcpu *source, *target;
0171     struct kvm_run *run;
0172     struct kvm_vm *vm;
0173 
0174     vm = setup_vm(guest_test_system_suspend, &source, &target);
0175     vm_enable_cap(vm, KVM_CAP_ARM_SYSTEM_SUSPEND, 0);
0176 
0177     vcpu_power_off(target);
0178     run = source->run;
0179 
0180     enter_guest(source);
0181 
0182     TEST_ASSERT(run->exit_reason == KVM_EXIT_SYSTEM_EVENT,
0183             "Unhandled exit reason: %u (%s)",
0184             run->exit_reason, exit_reason_str(run->exit_reason));
0185     TEST_ASSERT(run->system_event.type == KVM_SYSTEM_EVENT_SUSPEND,
0186             "Unhandled system event: %u (expected: %u)",
0187             run->system_event.type, KVM_SYSTEM_EVENT_SUSPEND);
0188 
0189     kvm_vm_free(vm);
0190 }
0191 
0192 int main(void)
0193 {
0194     TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_SYSTEM_SUSPEND));
0195 
0196     host_test_cpu_on();
0197     host_test_system_suspend();
0198     return 0;
0199 }