1*6689fb8fSOliver Upton // SPDX-License-Identifier: GPL-2.0-only
2*6689fb8fSOliver Upton /*
3*6689fb8fSOliver Upton  * psci_cpu_on_test - Test that the observable state of a vCPU targeted by the
4*6689fb8fSOliver Upton  * CPU_ON PSCI call matches what the caller requested.
5*6689fb8fSOliver Upton  *
6*6689fb8fSOliver Upton  * Copyright (c) 2021 Google LLC.
7*6689fb8fSOliver Upton  *
8*6689fb8fSOliver Upton  * This is a regression test for a race between KVM servicing the PSCI call and
9*6689fb8fSOliver Upton  * userspace reading the vCPUs registers.
10*6689fb8fSOliver Upton  */
11*6689fb8fSOliver Upton 
12*6689fb8fSOliver Upton #define _GNU_SOURCE
13*6689fb8fSOliver Upton 
14*6689fb8fSOliver Upton #include <linux/psci.h>
15*6689fb8fSOliver Upton 
16*6689fb8fSOliver Upton #include "kvm_util.h"
17*6689fb8fSOliver Upton #include "processor.h"
18*6689fb8fSOliver Upton #include "test_util.h"
19*6689fb8fSOliver Upton 
20*6689fb8fSOliver Upton #define VCPU_ID_SOURCE 0
21*6689fb8fSOliver Upton #define VCPU_ID_TARGET 1
22*6689fb8fSOliver Upton 
23*6689fb8fSOliver Upton #define CPU_ON_ENTRY_ADDR 0xfeedf00dul
24*6689fb8fSOliver Upton #define CPU_ON_CONTEXT_ID 0xdeadc0deul
25*6689fb8fSOliver Upton 
26*6689fb8fSOliver Upton static uint64_t psci_cpu_on(uint64_t target_cpu, uint64_t entry_addr,
27*6689fb8fSOliver Upton 			    uint64_t context_id)
28*6689fb8fSOliver Upton {
29*6689fb8fSOliver Upton 	register uint64_t x0 asm("x0") = PSCI_0_2_FN64_CPU_ON;
30*6689fb8fSOliver Upton 	register uint64_t x1 asm("x1") = target_cpu;
31*6689fb8fSOliver Upton 	register uint64_t x2 asm("x2") = entry_addr;
32*6689fb8fSOliver Upton 	register uint64_t x3 asm("x3") = context_id;
33*6689fb8fSOliver Upton 
34*6689fb8fSOliver Upton 	asm("hvc #0"
35*6689fb8fSOliver Upton 	    : "=r"(x0)
36*6689fb8fSOliver Upton 	    : "r"(x0), "r"(x1), "r"(x2), "r"(x3)
37*6689fb8fSOliver Upton 	    : "memory");
38*6689fb8fSOliver Upton 
39*6689fb8fSOliver Upton 	return x0;
40*6689fb8fSOliver Upton }
41*6689fb8fSOliver Upton 
42*6689fb8fSOliver Upton static uint64_t psci_affinity_info(uint64_t target_affinity,
43*6689fb8fSOliver Upton 				   uint64_t lowest_affinity_level)
44*6689fb8fSOliver Upton {
45*6689fb8fSOliver Upton 	register uint64_t x0 asm("x0") = PSCI_0_2_FN64_AFFINITY_INFO;
46*6689fb8fSOliver Upton 	register uint64_t x1 asm("x1") = target_affinity;
47*6689fb8fSOliver Upton 	register uint64_t x2 asm("x2") = lowest_affinity_level;
48*6689fb8fSOliver Upton 
49*6689fb8fSOliver Upton 	asm("hvc #0"
50*6689fb8fSOliver Upton 	    : "=r"(x0)
51*6689fb8fSOliver Upton 	    : "r"(x0), "r"(x1), "r"(x2)
52*6689fb8fSOliver Upton 	    : "memory");
53*6689fb8fSOliver Upton 
54*6689fb8fSOliver Upton 	return x0;
55*6689fb8fSOliver Upton }
56*6689fb8fSOliver Upton 
57*6689fb8fSOliver Upton static void guest_main(uint64_t target_cpu)
58*6689fb8fSOliver Upton {
59*6689fb8fSOliver Upton 	GUEST_ASSERT(!psci_cpu_on(target_cpu, CPU_ON_ENTRY_ADDR, CPU_ON_CONTEXT_ID));
60*6689fb8fSOliver Upton 	uint64_t target_state;
61*6689fb8fSOliver Upton 
62*6689fb8fSOliver Upton 	do {
63*6689fb8fSOliver Upton 		target_state = psci_affinity_info(target_cpu, 0);
64*6689fb8fSOliver Upton 
65*6689fb8fSOliver Upton 		GUEST_ASSERT((target_state == PSCI_0_2_AFFINITY_LEVEL_ON) ||
66*6689fb8fSOliver Upton 			     (target_state == PSCI_0_2_AFFINITY_LEVEL_OFF));
67*6689fb8fSOliver Upton 	} while (target_state != PSCI_0_2_AFFINITY_LEVEL_ON);
68*6689fb8fSOliver Upton 
69*6689fb8fSOliver Upton 	GUEST_DONE();
70*6689fb8fSOliver Upton }
71*6689fb8fSOliver Upton 
72*6689fb8fSOliver Upton int main(void)
73*6689fb8fSOliver Upton {
74*6689fb8fSOliver Upton 	uint64_t target_mpidr, obs_pc, obs_x0;
75*6689fb8fSOliver Upton 	struct kvm_vcpu_init init;
76*6689fb8fSOliver Upton 	struct kvm_vm *vm;
77*6689fb8fSOliver Upton 	struct ucall uc;
78*6689fb8fSOliver Upton 
79*6689fb8fSOliver Upton 	vm = vm_create(VM_MODE_DEFAULT, DEFAULT_GUEST_PHY_PAGES, O_RDWR);
80*6689fb8fSOliver Upton 	kvm_vm_elf_load(vm, program_invocation_name);
81*6689fb8fSOliver Upton 	ucall_init(vm, NULL);
82*6689fb8fSOliver Upton 
83*6689fb8fSOliver Upton 	vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init);
84*6689fb8fSOliver Upton 	init.features[0] |= (1 << KVM_ARM_VCPU_PSCI_0_2);
85*6689fb8fSOliver Upton 
86*6689fb8fSOliver Upton 	aarch64_vcpu_add_default(vm, VCPU_ID_SOURCE, &init, guest_main);
87*6689fb8fSOliver Upton 
88*6689fb8fSOliver Upton 	/*
89*6689fb8fSOliver Upton 	 * make sure the target is already off when executing the test.
90*6689fb8fSOliver Upton 	 */
91*6689fb8fSOliver Upton 	init.features[0] |= (1 << KVM_ARM_VCPU_POWER_OFF);
92*6689fb8fSOliver Upton 	aarch64_vcpu_add_default(vm, VCPU_ID_TARGET, &init, guest_main);
93*6689fb8fSOliver Upton 
94*6689fb8fSOliver Upton 	get_reg(vm, VCPU_ID_TARGET, KVM_ARM64_SYS_REG(SYS_MPIDR_EL1), &target_mpidr);
95*6689fb8fSOliver Upton 	vcpu_args_set(vm, VCPU_ID_SOURCE, 1, target_mpidr & MPIDR_HWID_BITMASK);
96*6689fb8fSOliver Upton 	vcpu_run(vm, VCPU_ID_SOURCE);
97*6689fb8fSOliver Upton 
98*6689fb8fSOliver Upton 	switch (get_ucall(vm, VCPU_ID_SOURCE, &uc)) {
99*6689fb8fSOliver Upton 	case UCALL_DONE:
100*6689fb8fSOliver Upton 		break;
101*6689fb8fSOliver Upton 	case UCALL_ABORT:
102*6689fb8fSOliver Upton 		TEST_FAIL("%s at %s:%ld", (const char *)uc.args[0], __FILE__,
103*6689fb8fSOliver Upton 			  uc.args[1]);
104*6689fb8fSOliver Upton 		break;
105*6689fb8fSOliver Upton 	default:
106*6689fb8fSOliver Upton 		TEST_FAIL("Unhandled ucall: %lu", uc.cmd);
107*6689fb8fSOliver Upton 	}
108*6689fb8fSOliver Upton 
109*6689fb8fSOliver Upton 	get_reg(vm, VCPU_ID_TARGET, ARM64_CORE_REG(regs.pc), &obs_pc);
110*6689fb8fSOliver Upton 	get_reg(vm, VCPU_ID_TARGET, ARM64_CORE_REG(regs.regs[0]), &obs_x0);
111*6689fb8fSOliver Upton 
112*6689fb8fSOliver Upton 	TEST_ASSERT(obs_pc == CPU_ON_ENTRY_ADDR,
113*6689fb8fSOliver Upton 		    "unexpected target cpu pc: %lx (expected: %lx)",
114*6689fb8fSOliver Upton 		    obs_pc, CPU_ON_ENTRY_ADDR);
115*6689fb8fSOliver Upton 	TEST_ASSERT(obs_x0 == CPU_ON_CONTEXT_ID,
116*6689fb8fSOliver Upton 		    "unexpected target context id: %lx (expected: %lx)",
117*6689fb8fSOliver Upton 		    obs_x0, CPU_ON_CONTEXT_ID);
118*6689fb8fSOliver Upton 
119*6689fb8fSOliver Upton 	kvm_vm_free(vm);
120*6689fb8fSOliver Upton 	return 0;
121*6689fb8fSOliver Upton }
122