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