xref: /linux/tools/testing/selftests/kvm/arm64/psci_test.c (revision 9e676a024fa1fa2bd8150c2d2ba85478280353bc)
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