1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * psci_cpu_on_test - Test that the observable state of a vCPU targeted by the
4 * CPU_ON PSCI call matches what the caller requested.
5 *
6 * Copyright (c) 2021 Google LLC.
7 *
8 * This is a regression test for a race between KVM servicing the PSCI call and
9 * userspace reading the vCPUs registers.
10 */
11
12 #define _GNU_SOURCE
13
14 #include <linux/psci.h>
15
16 #include "kvm_util.h"
17 #include "processor.h"
18 #include "test_util.h"
19
20 #define VCPU_ID_SOURCE 0
21 #define VCPU_ID_TARGET 1
22
23 #define CPU_ON_ENTRY_ADDR 0xfeedf00dul
24 #define CPU_ON_CONTEXT_ID 0xdeadc0deul
25
psci_cpu_on(uint64_t target_cpu,uint64_t entry_addr,uint64_t context_id)26 static uint64_t psci_cpu_on(uint64_t target_cpu, uint64_t entry_addr,
27 uint64_t context_id)
28 {
29 struct arm_smccc_res res;
30
31 smccc_hvc(PSCI_0_2_FN64_CPU_ON, target_cpu, entry_addr, context_id,
32 0, 0, 0, 0, &res);
33
34 return res.a0;
35 }
36
psci_affinity_info(uint64_t target_affinity,uint64_t lowest_affinity_level)37 static uint64_t psci_affinity_info(uint64_t target_affinity,
38 uint64_t lowest_affinity_level)
39 {
40 struct arm_smccc_res res;
41
42 smccc_hvc(PSCI_0_2_FN64_AFFINITY_INFO, target_affinity, lowest_affinity_level,
43 0, 0, 0, 0, 0, &res);
44
45 return res.a0;
46 }
47
psci_system_suspend(uint64_t entry_addr,uint64_t context_id)48 static uint64_t psci_system_suspend(uint64_t entry_addr, uint64_t context_id)
49 {
50 struct arm_smccc_res res;
51
52 smccc_hvc(PSCI_1_0_FN64_SYSTEM_SUSPEND, entry_addr, context_id,
53 0, 0, 0, 0, 0, &res);
54
55 return res.a0;
56 }
57
psci_features(uint32_t func_id)58 static uint64_t psci_features(uint32_t func_id)
59 {
60 struct arm_smccc_res res;
61
62 smccc_hvc(PSCI_1_0_FN_PSCI_FEATURES, func_id, 0, 0, 0, 0, 0, 0, &res);
63
64 return res.a0;
65 }
66
vcpu_power_off(struct kvm_vm * vm,uint32_t vcpuid)67 static void vcpu_power_off(struct kvm_vm *vm, uint32_t vcpuid)
68 {
69 struct kvm_mp_state mp_state = {
70 .mp_state = KVM_MP_STATE_STOPPED,
71 };
72
73 vcpu_set_mp_state(vm, vcpuid, &mp_state);
74 }
75
setup_vm(void * guest_code)76 static struct kvm_vm *setup_vm(void *guest_code)
77 {
78 struct kvm_vcpu_init init;
79 struct kvm_vm *vm;
80
81 vm = vm_create(VM_MODE_DEFAULT, DEFAULT_GUEST_PHY_PAGES, O_RDWR);
82 kvm_vm_elf_load(vm, program_invocation_name);
83 ucall_init(vm, NULL);
84
85 vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init);
86 init.features[0] |= (1 << KVM_ARM_VCPU_PSCI_0_2);
87
88 aarch64_vcpu_add_default(vm, VCPU_ID_SOURCE, &init, guest_code);
89 aarch64_vcpu_add_default(vm, VCPU_ID_TARGET, &init, guest_code);
90
91 return vm;
92 }
93
enter_guest(struct kvm_vm * vm,uint32_t vcpuid)94 static void enter_guest(struct kvm_vm *vm, uint32_t vcpuid)
95 {
96 struct ucall uc;
97
98 vcpu_run(vm, vcpuid);
99 if (get_ucall(vm, vcpuid, &uc) == UCALL_ABORT)
100 TEST_FAIL("%s at %s:%ld", (const char *)uc.args[0], __FILE__,
101 uc.args[1]);
102 }
103
assert_vcpu_reset(struct kvm_vm * vm,uint32_t vcpuid)104 static void assert_vcpu_reset(struct kvm_vm *vm, uint32_t vcpuid)
105 {
106 uint64_t obs_pc, obs_x0;
107
108 get_reg(vm, vcpuid, ARM64_CORE_REG(regs.pc), &obs_pc);
109 get_reg(vm, vcpuid, ARM64_CORE_REG(regs.regs[0]), &obs_x0);
110
111 TEST_ASSERT(obs_pc == CPU_ON_ENTRY_ADDR,
112 "unexpected target cpu pc: %lx (expected: %lx)",
113 obs_pc, CPU_ON_ENTRY_ADDR);
114 TEST_ASSERT(obs_x0 == CPU_ON_CONTEXT_ID,
115 "unexpected target context id: %lx (expected: %lx)",
116 obs_x0, CPU_ON_CONTEXT_ID);
117 }
118
guest_test_cpu_on(uint64_t target_cpu)119 static void guest_test_cpu_on(uint64_t target_cpu)
120 {
121 uint64_t target_state;
122
123 GUEST_ASSERT(!psci_cpu_on(target_cpu, CPU_ON_ENTRY_ADDR, CPU_ON_CONTEXT_ID));
124
125 do {
126 target_state = psci_affinity_info(target_cpu, 0);
127
128 GUEST_ASSERT((target_state == PSCI_0_2_AFFINITY_LEVEL_ON) ||
129 (target_state == PSCI_0_2_AFFINITY_LEVEL_OFF));
130 } while (target_state != PSCI_0_2_AFFINITY_LEVEL_ON);
131
132 GUEST_DONE();
133 }
134
host_test_cpu_on(void)135 static void host_test_cpu_on(void)
136 {
137 uint64_t target_mpidr;
138 struct kvm_vm *vm;
139 struct ucall uc;
140
141 vm = setup_vm(guest_test_cpu_on);
142
143 /*
144 * make sure the target is already off when executing the test.
145 */
146 vcpu_power_off(vm, VCPU_ID_TARGET);
147
148 get_reg(vm, VCPU_ID_TARGET, KVM_ARM64_SYS_REG(SYS_MPIDR_EL1), &target_mpidr);
149 vcpu_args_set(vm, VCPU_ID_SOURCE, 1, target_mpidr & MPIDR_HWID_BITMASK);
150 enter_guest(vm, VCPU_ID_SOURCE);
151
152 if (get_ucall(vm, VCPU_ID_SOURCE, &uc) != UCALL_DONE)
153 TEST_FAIL("Unhandled ucall: %lu", uc.cmd);
154
155 assert_vcpu_reset(vm, VCPU_ID_TARGET);
156 kvm_vm_free(vm);
157 }
158
enable_system_suspend(struct kvm_vm * vm)159 static void enable_system_suspend(struct kvm_vm *vm)
160 {
161 struct kvm_enable_cap cap = {
162 .cap = KVM_CAP_ARM_SYSTEM_SUSPEND,
163 };
164
165 vm_enable_cap(vm, &cap);
166 }
167
guest_test_system_suspend(void)168 static void guest_test_system_suspend(void)
169 {
170 uint64_t ret;
171
172 /* assert that SYSTEM_SUSPEND is discoverable */
173 GUEST_ASSERT(!psci_features(PSCI_1_0_FN_SYSTEM_SUSPEND));
174 GUEST_ASSERT(!psci_features(PSCI_1_0_FN64_SYSTEM_SUSPEND));
175
176 ret = psci_system_suspend(CPU_ON_ENTRY_ADDR, CPU_ON_CONTEXT_ID);
177 GUEST_SYNC(ret);
178 }
179
host_test_system_suspend(void)180 static void host_test_system_suspend(void)
181 {
182 struct kvm_run *run;
183 struct kvm_vm *vm;
184
185 vm = setup_vm(guest_test_system_suspend);
186 enable_system_suspend(vm);
187
188 vcpu_power_off(vm, VCPU_ID_TARGET);
189 run = vcpu_state(vm, VCPU_ID_SOURCE);
190
191 enter_guest(vm, VCPU_ID_SOURCE);
192
193 TEST_ASSERT(run->exit_reason == KVM_EXIT_SYSTEM_EVENT,
194 "Unhandled exit reason: %u (%s)",
195 run->exit_reason, exit_reason_str(run->exit_reason));
196 TEST_ASSERT(run->system_event.type == KVM_SYSTEM_EVENT_SUSPEND,
197 "Unhandled system event: %u (expected: %u)",
198 run->system_event.type, KVM_SYSTEM_EVENT_SUSPEND);
199
200 kvm_vm_free(vm);
201 }
202
main(void)203 int main(void)
204 {
205 if (!kvm_check_cap(KVM_CAP_ARM_SYSTEM_SUSPEND)) {
206 print_skip("KVM_CAP_ARM_SYSTEM_SUSPEND not supported");
207 exit(KSFT_SKIP);
208 }
209
210 host_test_cpu_on();
211 host_test_system_suspend();
212 return 0;
213 }
214