1*67730e6cSSean Christopherson // SPDX-License-Identifier: GPL-2.0-only 2*67730e6cSSean Christopherson /* 3*67730e6cSSean Christopherson * psci_test - Tests relating to KVM's PSCI implementation. 4*67730e6cSSean Christopherson * 5*67730e6cSSean Christopherson * Copyright (c) 2021 Google LLC. 6*67730e6cSSean Christopherson * 7*67730e6cSSean Christopherson * This test includes: 8*67730e6cSSean Christopherson * - A regression test for a race between KVM servicing the PSCI CPU_ON call 9*67730e6cSSean Christopherson * and userspace reading the targeted vCPU's registers. 10*67730e6cSSean Christopherson * - A test for KVM's handling of PSCI SYSTEM_SUSPEND and the associated 11*67730e6cSSean Christopherson * KVM_SYSTEM_EVENT_SUSPEND UAPI. 12*67730e6cSSean Christopherson */ 13*67730e6cSSean Christopherson 14*67730e6cSSean Christopherson #include <linux/kernel.h> 15*67730e6cSSean Christopherson #include <linux/psci.h> 16*67730e6cSSean Christopherson #include <asm/cputype.h> 17*67730e6cSSean Christopherson 18*67730e6cSSean Christopherson #include "kvm_util.h" 19*67730e6cSSean Christopherson #include "processor.h" 20*67730e6cSSean Christopherson #include "test_util.h" 21*67730e6cSSean Christopherson 22*67730e6cSSean Christopherson #define CPU_ON_ENTRY_ADDR 0xfeedf00dul 23*67730e6cSSean Christopherson #define CPU_ON_CONTEXT_ID 0xdeadc0deul 24*67730e6cSSean Christopherson 25*67730e6cSSean Christopherson static uint64_t psci_cpu_on(uint64_t target_cpu, uint64_t entry_addr, 26*67730e6cSSean Christopherson uint64_t context_id) 27*67730e6cSSean Christopherson { 28*67730e6cSSean Christopherson struct arm_smccc_res res; 29*67730e6cSSean Christopherson 30*67730e6cSSean Christopherson smccc_hvc(PSCI_0_2_FN64_CPU_ON, target_cpu, entry_addr, context_id, 31*67730e6cSSean Christopherson 0, 0, 0, 0, &res); 32*67730e6cSSean Christopherson 33*67730e6cSSean Christopherson return res.a0; 34*67730e6cSSean Christopherson } 35*67730e6cSSean Christopherson 36*67730e6cSSean Christopherson static uint64_t psci_affinity_info(uint64_t target_affinity, 37*67730e6cSSean Christopherson uint64_t lowest_affinity_level) 38*67730e6cSSean Christopherson { 39*67730e6cSSean Christopherson struct arm_smccc_res res; 40*67730e6cSSean Christopherson 41*67730e6cSSean Christopherson smccc_hvc(PSCI_0_2_FN64_AFFINITY_INFO, target_affinity, lowest_affinity_level, 42*67730e6cSSean Christopherson 0, 0, 0, 0, 0, &res); 43*67730e6cSSean Christopherson 44*67730e6cSSean Christopherson return res.a0; 45*67730e6cSSean Christopherson } 46*67730e6cSSean Christopherson 47*67730e6cSSean Christopherson static uint64_t psci_system_suspend(uint64_t entry_addr, uint64_t context_id) 48*67730e6cSSean Christopherson { 49*67730e6cSSean Christopherson struct arm_smccc_res res; 50*67730e6cSSean Christopherson 51*67730e6cSSean Christopherson smccc_hvc(PSCI_1_0_FN64_SYSTEM_SUSPEND, entry_addr, context_id, 52*67730e6cSSean Christopherson 0, 0, 0, 0, 0, &res); 53*67730e6cSSean Christopherson 54*67730e6cSSean Christopherson return res.a0; 55*67730e6cSSean Christopherson } 56*67730e6cSSean Christopherson 57*67730e6cSSean Christopherson static uint64_t psci_system_off2(uint64_t type, uint64_t cookie) 58*67730e6cSSean Christopherson { 59*67730e6cSSean Christopherson struct arm_smccc_res res; 60*67730e6cSSean Christopherson 61*67730e6cSSean Christopherson smccc_hvc(PSCI_1_3_FN64_SYSTEM_OFF2, type, cookie, 0, 0, 0, 0, 0, &res); 62*67730e6cSSean Christopherson 63*67730e6cSSean Christopherson return res.a0; 64*67730e6cSSean Christopherson } 65*67730e6cSSean Christopherson 66*67730e6cSSean Christopherson static uint64_t psci_features(uint32_t func_id) 67*67730e6cSSean Christopherson { 68*67730e6cSSean Christopherson struct arm_smccc_res res; 69*67730e6cSSean Christopherson 70*67730e6cSSean Christopherson smccc_hvc(PSCI_1_0_FN_PSCI_FEATURES, func_id, 0, 0, 0, 0, 0, 0, &res); 71*67730e6cSSean Christopherson 72*67730e6cSSean Christopherson return res.a0; 73*67730e6cSSean Christopherson } 74*67730e6cSSean Christopherson 75*67730e6cSSean Christopherson static void vcpu_power_off(struct kvm_vcpu *vcpu) 76*67730e6cSSean Christopherson { 77*67730e6cSSean Christopherson struct kvm_mp_state mp_state = { 78*67730e6cSSean Christopherson .mp_state = KVM_MP_STATE_STOPPED, 79*67730e6cSSean Christopherson }; 80*67730e6cSSean Christopherson 81*67730e6cSSean Christopherson vcpu_mp_state_set(vcpu, &mp_state); 82*67730e6cSSean Christopherson } 83*67730e6cSSean Christopherson 84*67730e6cSSean Christopherson static struct kvm_vm *setup_vm(void *guest_code, struct kvm_vcpu **source, 85*67730e6cSSean Christopherson struct kvm_vcpu **target) 86*67730e6cSSean Christopherson { 87*67730e6cSSean Christopherson struct kvm_vcpu_init init; 88*67730e6cSSean Christopherson struct kvm_vm *vm; 89*67730e6cSSean Christopherson 90*67730e6cSSean Christopherson vm = vm_create(2); 91*67730e6cSSean Christopherson 92*67730e6cSSean Christopherson vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init); 93*67730e6cSSean Christopherson init.features[0] |= (1 << KVM_ARM_VCPU_PSCI_0_2); 94*67730e6cSSean Christopherson 95*67730e6cSSean Christopherson *source = aarch64_vcpu_add(vm, 0, &init, guest_code); 96*67730e6cSSean Christopherson *target = aarch64_vcpu_add(vm, 1, &init, guest_code); 97*67730e6cSSean Christopherson 98*67730e6cSSean Christopherson return vm; 99*67730e6cSSean Christopherson } 100*67730e6cSSean Christopherson 101*67730e6cSSean Christopherson static void enter_guest(struct kvm_vcpu *vcpu) 102*67730e6cSSean Christopherson { 103*67730e6cSSean Christopherson struct ucall uc; 104*67730e6cSSean Christopherson 105*67730e6cSSean Christopherson vcpu_run(vcpu); 106*67730e6cSSean Christopherson if (get_ucall(vcpu, &uc) == UCALL_ABORT) 107*67730e6cSSean Christopherson REPORT_GUEST_ASSERT(uc); 108*67730e6cSSean Christopherson } 109*67730e6cSSean Christopherson 110*67730e6cSSean Christopherson static void assert_vcpu_reset(struct kvm_vcpu *vcpu) 111*67730e6cSSean Christopherson { 112*67730e6cSSean Christopherson uint64_t obs_pc, obs_x0; 113*67730e6cSSean Christopherson 114*67730e6cSSean Christopherson obs_pc = vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.pc)); 115*67730e6cSSean Christopherson obs_x0 = vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.regs[0])); 116*67730e6cSSean Christopherson 117*67730e6cSSean Christopherson TEST_ASSERT(obs_pc == CPU_ON_ENTRY_ADDR, 118*67730e6cSSean Christopherson "unexpected target cpu pc: %lx (expected: %lx)", 119*67730e6cSSean Christopherson obs_pc, CPU_ON_ENTRY_ADDR); 120*67730e6cSSean Christopherson TEST_ASSERT(obs_x0 == CPU_ON_CONTEXT_ID, 121*67730e6cSSean Christopherson "unexpected target context id: %lx (expected: %lx)", 122*67730e6cSSean Christopherson obs_x0, CPU_ON_CONTEXT_ID); 123*67730e6cSSean Christopherson } 124*67730e6cSSean Christopherson 125*67730e6cSSean Christopherson static void guest_test_cpu_on(uint64_t target_cpu) 126*67730e6cSSean Christopherson { 127*67730e6cSSean Christopherson uint64_t target_state; 128*67730e6cSSean Christopherson 129*67730e6cSSean Christopherson GUEST_ASSERT(!psci_cpu_on(target_cpu, CPU_ON_ENTRY_ADDR, CPU_ON_CONTEXT_ID)); 130*67730e6cSSean Christopherson 131*67730e6cSSean Christopherson do { 132*67730e6cSSean Christopherson target_state = psci_affinity_info(target_cpu, 0); 133*67730e6cSSean Christopherson 134*67730e6cSSean Christopherson GUEST_ASSERT((target_state == PSCI_0_2_AFFINITY_LEVEL_ON) || 135*67730e6cSSean Christopherson (target_state == PSCI_0_2_AFFINITY_LEVEL_OFF)); 136*67730e6cSSean Christopherson } while (target_state != PSCI_0_2_AFFINITY_LEVEL_ON); 137*67730e6cSSean Christopherson 138*67730e6cSSean Christopherson GUEST_DONE(); 139*67730e6cSSean Christopherson } 140*67730e6cSSean Christopherson 141*67730e6cSSean Christopherson static void host_test_cpu_on(void) 142*67730e6cSSean Christopherson { 143*67730e6cSSean Christopherson struct kvm_vcpu *source, *target; 144*67730e6cSSean Christopherson uint64_t target_mpidr; 145*67730e6cSSean Christopherson struct kvm_vm *vm; 146*67730e6cSSean Christopherson struct ucall uc; 147*67730e6cSSean Christopherson 148*67730e6cSSean Christopherson vm = setup_vm(guest_test_cpu_on, &source, &target); 149*67730e6cSSean Christopherson 150*67730e6cSSean Christopherson /* 151*67730e6cSSean Christopherson * make sure the target is already off when executing the test. 152*67730e6cSSean Christopherson */ 153*67730e6cSSean Christopherson vcpu_power_off(target); 154*67730e6cSSean Christopherson 155*67730e6cSSean Christopherson target_mpidr = vcpu_get_reg(target, KVM_ARM64_SYS_REG(SYS_MPIDR_EL1)); 156*67730e6cSSean Christopherson vcpu_args_set(source, 1, target_mpidr & MPIDR_HWID_BITMASK); 157*67730e6cSSean Christopherson enter_guest(source); 158*67730e6cSSean Christopherson 159*67730e6cSSean Christopherson if (get_ucall(source, &uc) != UCALL_DONE) 160*67730e6cSSean Christopherson TEST_FAIL("Unhandled ucall: %lu", uc.cmd); 161*67730e6cSSean Christopherson 162*67730e6cSSean Christopherson assert_vcpu_reset(target); 163*67730e6cSSean Christopherson kvm_vm_free(vm); 164*67730e6cSSean Christopherson } 165*67730e6cSSean Christopherson 166*67730e6cSSean Christopherson static void guest_test_system_suspend(void) 167*67730e6cSSean Christopherson { 168*67730e6cSSean Christopherson uint64_t ret; 169*67730e6cSSean Christopherson 170*67730e6cSSean Christopherson /* assert that SYSTEM_SUSPEND is discoverable */ 171*67730e6cSSean Christopherson GUEST_ASSERT(!psci_features(PSCI_1_0_FN_SYSTEM_SUSPEND)); 172*67730e6cSSean Christopherson GUEST_ASSERT(!psci_features(PSCI_1_0_FN64_SYSTEM_SUSPEND)); 173*67730e6cSSean Christopherson 174*67730e6cSSean Christopherson ret = psci_system_suspend(CPU_ON_ENTRY_ADDR, CPU_ON_CONTEXT_ID); 175*67730e6cSSean Christopherson GUEST_SYNC(ret); 176*67730e6cSSean Christopherson } 177*67730e6cSSean Christopherson 178*67730e6cSSean Christopherson static void host_test_system_suspend(void) 179*67730e6cSSean Christopherson { 180*67730e6cSSean Christopherson struct kvm_vcpu *source, *target; 181*67730e6cSSean Christopherson struct kvm_run *run; 182*67730e6cSSean Christopherson struct kvm_vm *vm; 183*67730e6cSSean Christopherson 184*67730e6cSSean Christopherson vm = setup_vm(guest_test_system_suspend, &source, &target); 185*67730e6cSSean Christopherson vm_enable_cap(vm, KVM_CAP_ARM_SYSTEM_SUSPEND, 0); 186*67730e6cSSean Christopherson 187*67730e6cSSean Christopherson vcpu_power_off(target); 188*67730e6cSSean Christopherson run = source->run; 189*67730e6cSSean Christopherson 190*67730e6cSSean Christopherson enter_guest(source); 191*67730e6cSSean Christopherson 192*67730e6cSSean Christopherson TEST_ASSERT_KVM_EXIT_REASON(source, KVM_EXIT_SYSTEM_EVENT); 193*67730e6cSSean Christopherson TEST_ASSERT(run->system_event.type == KVM_SYSTEM_EVENT_SUSPEND, 194*67730e6cSSean Christopherson "Unhandled system event: %u (expected: %u)", 195*67730e6cSSean Christopherson run->system_event.type, KVM_SYSTEM_EVENT_SUSPEND); 196*67730e6cSSean Christopherson 197*67730e6cSSean Christopherson kvm_vm_free(vm); 198*67730e6cSSean Christopherson } 199*67730e6cSSean Christopherson 200*67730e6cSSean Christopherson static void guest_test_system_off2(void) 201*67730e6cSSean Christopherson { 202*67730e6cSSean Christopherson uint64_t ret; 203*67730e6cSSean Christopherson 204*67730e6cSSean Christopherson /* assert that SYSTEM_OFF2 is discoverable */ 205*67730e6cSSean Christopherson GUEST_ASSERT(psci_features(PSCI_1_3_FN_SYSTEM_OFF2) & 206*67730e6cSSean Christopherson PSCI_1_3_OFF_TYPE_HIBERNATE_OFF); 207*67730e6cSSean Christopherson GUEST_ASSERT(psci_features(PSCI_1_3_FN64_SYSTEM_OFF2) & 208*67730e6cSSean Christopherson PSCI_1_3_OFF_TYPE_HIBERNATE_OFF); 209*67730e6cSSean Christopherson 210*67730e6cSSean Christopherson /* With non-zero 'cookie' field, it should fail */ 211*67730e6cSSean Christopherson ret = psci_system_off2(PSCI_1_3_OFF_TYPE_HIBERNATE_OFF, 1); 212*67730e6cSSean Christopherson GUEST_ASSERT(ret == PSCI_RET_INVALID_PARAMS); 213*67730e6cSSean Christopherson 214*67730e6cSSean Christopherson /* 215*67730e6cSSean Christopherson * This would normally never return, so KVM sets the return value 216*67730e6cSSean Christopherson * to PSCI_RET_INTERNAL_FAILURE. The test case *does* return, so 217*67730e6cSSean Christopherson * that it can test both values for HIBERNATE_OFF. 218*67730e6cSSean Christopherson */ 219*67730e6cSSean Christopherson ret = psci_system_off2(PSCI_1_3_OFF_TYPE_HIBERNATE_OFF, 0); 220*67730e6cSSean Christopherson GUEST_ASSERT(ret == PSCI_RET_INTERNAL_FAILURE); 221*67730e6cSSean Christopherson 222*67730e6cSSean Christopherson /* 223*67730e6cSSean Christopherson * Revision F.b of the PSCI v1.3 specification documents zero as an 224*67730e6cSSean Christopherson * alias for HIBERNATE_OFF, since that's the value used in earlier 225*67730e6cSSean Christopherson * revisions of the spec and some implementations in the field. 226*67730e6cSSean Christopherson */ 227*67730e6cSSean Christopherson ret = psci_system_off2(0, 1); 228*67730e6cSSean Christopherson GUEST_ASSERT(ret == PSCI_RET_INVALID_PARAMS); 229*67730e6cSSean Christopherson 230*67730e6cSSean Christopherson ret = psci_system_off2(0, 0); 231*67730e6cSSean Christopherson GUEST_ASSERT(ret == PSCI_RET_INTERNAL_FAILURE); 232*67730e6cSSean Christopherson 233*67730e6cSSean Christopherson GUEST_DONE(); 234*67730e6cSSean Christopherson } 235*67730e6cSSean Christopherson 236*67730e6cSSean Christopherson static void host_test_system_off2(void) 237*67730e6cSSean Christopherson { 238*67730e6cSSean Christopherson struct kvm_vcpu *source, *target; 239*67730e6cSSean Christopherson struct kvm_mp_state mps; 240*67730e6cSSean Christopherson uint64_t psci_version = 0; 241*67730e6cSSean Christopherson int nr_shutdowns = 0; 242*67730e6cSSean Christopherson struct kvm_run *run; 243*67730e6cSSean Christopherson struct ucall uc; 244*67730e6cSSean Christopherson 245*67730e6cSSean Christopherson setup_vm(guest_test_system_off2, &source, &target); 246*67730e6cSSean Christopherson 247*67730e6cSSean Christopherson psci_version = vcpu_get_reg(target, KVM_REG_ARM_PSCI_VERSION); 248*67730e6cSSean Christopherson 249*67730e6cSSean Christopherson TEST_ASSERT(psci_version >= PSCI_VERSION(1, 3), 250*67730e6cSSean Christopherson "Unexpected PSCI version %lu.%lu", 251*67730e6cSSean Christopherson PSCI_VERSION_MAJOR(psci_version), 252*67730e6cSSean Christopherson PSCI_VERSION_MINOR(psci_version)); 253*67730e6cSSean Christopherson 254*67730e6cSSean Christopherson vcpu_power_off(target); 255*67730e6cSSean Christopherson run = source->run; 256*67730e6cSSean Christopherson 257*67730e6cSSean Christopherson enter_guest(source); 258*67730e6cSSean Christopherson while (run->exit_reason == KVM_EXIT_SYSTEM_EVENT) { 259*67730e6cSSean Christopherson TEST_ASSERT(run->system_event.type == KVM_SYSTEM_EVENT_SHUTDOWN, 260*67730e6cSSean Christopherson "Unhandled system event: %u (expected: %u)", 261*67730e6cSSean Christopherson run->system_event.type, KVM_SYSTEM_EVENT_SHUTDOWN); 262*67730e6cSSean Christopherson TEST_ASSERT(run->system_event.ndata >= 1, 263*67730e6cSSean Christopherson "Unexpected amount of system event data: %u (expected, >= 1)", 264*67730e6cSSean Christopherson run->system_event.ndata); 265*67730e6cSSean Christopherson TEST_ASSERT(run->system_event.data[0] & KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2, 266*67730e6cSSean Christopherson "PSCI_OFF2 flag not set. Flags %llu (expected %llu)", 267*67730e6cSSean Christopherson run->system_event.data[0], KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2); 268*67730e6cSSean Christopherson 269*67730e6cSSean Christopherson nr_shutdowns++; 270*67730e6cSSean Christopherson 271*67730e6cSSean Christopherson /* Restart the vCPU */ 272*67730e6cSSean Christopherson mps.mp_state = KVM_MP_STATE_RUNNABLE; 273*67730e6cSSean Christopherson vcpu_mp_state_set(source, &mps); 274*67730e6cSSean Christopherson 275*67730e6cSSean Christopherson enter_guest(source); 276*67730e6cSSean Christopherson } 277*67730e6cSSean Christopherson 278*67730e6cSSean Christopherson TEST_ASSERT(get_ucall(source, &uc) == UCALL_DONE, "Guest did not exit cleanly"); 279*67730e6cSSean Christopherson TEST_ASSERT(nr_shutdowns == 2, "Two shutdown events were expected, but saw %d", nr_shutdowns); 280*67730e6cSSean Christopherson } 281*67730e6cSSean Christopherson 282*67730e6cSSean Christopherson int main(void) 283*67730e6cSSean Christopherson { 284*67730e6cSSean Christopherson TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_SYSTEM_SUSPEND)); 285*67730e6cSSean Christopherson 286*67730e6cSSean Christopherson host_test_cpu_on(); 287*67730e6cSSean Christopherson host_test_system_suspend(); 288*67730e6cSSean Christopherson host_test_system_off2(); 289*67730e6cSSean Christopherson return 0; 290*67730e6cSSean Christopherson } 291