File Index
Files and directories
Sign in
FILE

psci_test.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/kvm/arm64/psci_test.c C
  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
 25static 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
 36static 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
 47static 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
 57static 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
 66static 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
 75static 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
 84static 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
102static 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
111static 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
126static 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
142static 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
167static 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
179static 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
201static 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
237static 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
283int 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}
~/ Read-only file preview.