0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #define _GNU_SOURCE
0013
0014 #include <linux/psci.h>
0015
0016 #include "kvm_util.h"
0017 #include "processor.h"
0018 #include "test_util.h"
0019
0020 #define CPU_ON_ENTRY_ADDR 0xfeedf00dul
0021 #define CPU_ON_CONTEXT_ID 0xdeadc0deul
0022
0023 static uint64_t psci_cpu_on(uint64_t target_cpu, uint64_t entry_addr,
0024 uint64_t context_id)
0025 {
0026 struct arm_smccc_res res;
0027
0028 smccc_hvc(PSCI_0_2_FN64_CPU_ON, target_cpu, entry_addr, context_id,
0029 0, 0, 0, 0, &res);
0030
0031 return res.a0;
0032 }
0033
0034 static uint64_t psci_affinity_info(uint64_t target_affinity,
0035 uint64_t lowest_affinity_level)
0036 {
0037 struct arm_smccc_res res;
0038
0039 smccc_hvc(PSCI_0_2_FN64_AFFINITY_INFO, target_affinity, lowest_affinity_level,
0040 0, 0, 0, 0, 0, &res);
0041
0042 return res.a0;
0043 }
0044
0045 static uint64_t psci_system_suspend(uint64_t entry_addr, uint64_t context_id)
0046 {
0047 struct arm_smccc_res res;
0048
0049 smccc_hvc(PSCI_1_0_FN64_SYSTEM_SUSPEND, entry_addr, context_id,
0050 0, 0, 0, 0, 0, &res);
0051
0052 return res.a0;
0053 }
0054
0055 static uint64_t psci_features(uint32_t func_id)
0056 {
0057 struct arm_smccc_res res;
0058
0059 smccc_hvc(PSCI_1_0_FN_PSCI_FEATURES, func_id, 0, 0, 0, 0, 0, 0, &res);
0060
0061 return res.a0;
0062 }
0063
0064 static void vcpu_power_off(struct kvm_vcpu *vcpu)
0065 {
0066 struct kvm_mp_state mp_state = {
0067 .mp_state = KVM_MP_STATE_STOPPED,
0068 };
0069
0070 vcpu_mp_state_set(vcpu, &mp_state);
0071 }
0072
0073 static struct kvm_vm *setup_vm(void *guest_code, struct kvm_vcpu **source,
0074 struct kvm_vcpu **target)
0075 {
0076 struct kvm_vcpu_init init;
0077 struct kvm_vm *vm;
0078
0079 vm = vm_create(2);
0080 ucall_init(vm, NULL);
0081
0082 vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init);
0083 init.features[0] |= (1 << KVM_ARM_VCPU_PSCI_0_2);
0084
0085 *source = aarch64_vcpu_add(vm, 0, &init, guest_code);
0086 *target = aarch64_vcpu_add(vm, 1, &init, guest_code);
0087
0088 return vm;
0089 }
0090
0091 static void enter_guest(struct kvm_vcpu *vcpu)
0092 {
0093 struct ucall uc;
0094
0095 vcpu_run(vcpu);
0096 if (get_ucall(vcpu, &uc) == UCALL_ABORT)
0097 REPORT_GUEST_ASSERT(uc);
0098 }
0099
0100 static void assert_vcpu_reset(struct kvm_vcpu *vcpu)
0101 {
0102 uint64_t obs_pc, obs_x0;
0103
0104 vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.pc), &obs_pc);
0105 vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.regs[0]), &obs_x0);
0106
0107 TEST_ASSERT(obs_pc == CPU_ON_ENTRY_ADDR,
0108 "unexpected target cpu pc: %lx (expected: %lx)",
0109 obs_pc, CPU_ON_ENTRY_ADDR);
0110 TEST_ASSERT(obs_x0 == CPU_ON_CONTEXT_ID,
0111 "unexpected target context id: %lx (expected: %lx)",
0112 obs_x0, CPU_ON_CONTEXT_ID);
0113 }
0114
0115 static void guest_test_cpu_on(uint64_t target_cpu)
0116 {
0117 uint64_t target_state;
0118
0119 GUEST_ASSERT(!psci_cpu_on(target_cpu, CPU_ON_ENTRY_ADDR, CPU_ON_CONTEXT_ID));
0120
0121 do {
0122 target_state = psci_affinity_info(target_cpu, 0);
0123
0124 GUEST_ASSERT((target_state == PSCI_0_2_AFFINITY_LEVEL_ON) ||
0125 (target_state == PSCI_0_2_AFFINITY_LEVEL_OFF));
0126 } while (target_state != PSCI_0_2_AFFINITY_LEVEL_ON);
0127
0128 GUEST_DONE();
0129 }
0130
0131 static void host_test_cpu_on(void)
0132 {
0133 struct kvm_vcpu *source, *target;
0134 uint64_t target_mpidr;
0135 struct kvm_vm *vm;
0136 struct ucall uc;
0137
0138 vm = setup_vm(guest_test_cpu_on, &source, &target);
0139
0140
0141
0142
0143 vcpu_power_off(target);
0144
0145 vcpu_get_reg(target, KVM_ARM64_SYS_REG(SYS_MPIDR_EL1), &target_mpidr);
0146 vcpu_args_set(source, 1, target_mpidr & MPIDR_HWID_BITMASK);
0147 enter_guest(source);
0148
0149 if (get_ucall(source, &uc) != UCALL_DONE)
0150 TEST_FAIL("Unhandled ucall: %lu", uc.cmd);
0151
0152 assert_vcpu_reset(target);
0153 kvm_vm_free(vm);
0154 }
0155
0156 static void guest_test_system_suspend(void)
0157 {
0158 uint64_t ret;
0159
0160
0161 GUEST_ASSERT(!psci_features(PSCI_1_0_FN_SYSTEM_SUSPEND));
0162 GUEST_ASSERT(!psci_features(PSCI_1_0_FN64_SYSTEM_SUSPEND));
0163
0164 ret = psci_system_suspend(CPU_ON_ENTRY_ADDR, CPU_ON_CONTEXT_ID);
0165 GUEST_SYNC(ret);
0166 }
0167
0168 static void host_test_system_suspend(void)
0169 {
0170 struct kvm_vcpu *source, *target;
0171 struct kvm_run *run;
0172 struct kvm_vm *vm;
0173
0174 vm = setup_vm(guest_test_system_suspend, &source, &target);
0175 vm_enable_cap(vm, KVM_CAP_ARM_SYSTEM_SUSPEND, 0);
0176
0177 vcpu_power_off(target);
0178 run = source->run;
0179
0180 enter_guest(source);
0181
0182 TEST_ASSERT(run->exit_reason == KVM_EXIT_SYSTEM_EVENT,
0183 "Unhandled exit reason: %u (%s)",
0184 run->exit_reason, exit_reason_str(run->exit_reason));
0185 TEST_ASSERT(run->system_event.type == KVM_SYSTEM_EVENT_SUSPEND,
0186 "Unhandled system event: %u (expected: %u)",
0187 run->system_event.type, KVM_SYSTEM_EVENT_SUSPEND);
0188
0189 kvm_vm_free(vm);
0190 }
0191
0192 int main(void)
0193 {
0194 TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_SYSTEM_SUSPEND));
0195
0196 host_test_cpu_on();
0197 host_test_system_suspend();
0198 return 0;
0199 }