Path: blob/master/tools/testing/selftests/kvm/arm64/psci_test.c
38237 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* psci_test - Tests relating to KVM's PSCI implementation.3*4* Copyright (c) 2021 Google LLC.5*6* This test includes:7* - A regression test for a race between KVM servicing the PSCI CPU_ON call8* and userspace reading the targeted vCPU's registers.9* - A test for KVM's handling of PSCI SYSTEM_SUSPEND and the associated10* KVM_SYSTEM_EVENT_SUSPEND UAPI.11*/1213#include <linux/kernel.h>14#include <linux/psci.h>15#include <asm/cputype.h>1617#include "kvm_util.h"18#include "processor.h"19#include "test_util.h"2021#define CPU_ON_ENTRY_ADDR 0xfeedf00dul22#define CPU_ON_CONTEXT_ID 0xdeadc0deul2324static uint64_t psci_cpu_on(uint64_t target_cpu, uint64_t entry_addr,25uint64_t context_id)26{27struct arm_smccc_res res;2829do_smccc(PSCI_0_2_FN64_CPU_ON, target_cpu, entry_addr, context_id,300, 0, 0, 0, &res);3132return res.a0;33}3435static uint64_t psci_affinity_info(uint64_t target_affinity,36uint64_t lowest_affinity_level)37{38struct arm_smccc_res res;3940do_smccc(PSCI_0_2_FN64_AFFINITY_INFO, target_affinity, lowest_affinity_level,410, 0, 0, 0, 0, &res);4243return res.a0;44}4546static uint64_t psci_system_suspend(uint64_t entry_addr, uint64_t context_id)47{48struct arm_smccc_res res;4950do_smccc(PSCI_1_0_FN64_SYSTEM_SUSPEND, entry_addr, context_id,510, 0, 0, 0, 0, &res);5253return res.a0;54}5556static uint64_t psci_system_off2(uint64_t type, uint64_t cookie)57{58struct arm_smccc_res res;5960do_smccc(PSCI_1_3_FN64_SYSTEM_OFF2, type, cookie, 0, 0, 0, 0, 0, &res);6162return res.a0;63}6465static uint64_t psci_features(uint32_t func_id)66{67struct arm_smccc_res res;6869do_smccc(PSCI_1_0_FN_PSCI_FEATURES, func_id, 0, 0, 0, 0, 0, 0, &res);7071return res.a0;72}7374static void vcpu_power_off(struct kvm_vcpu *vcpu)75{76struct kvm_mp_state mp_state = {77.mp_state = KVM_MP_STATE_STOPPED,78};7980vcpu_mp_state_set(vcpu, &mp_state);81}8283static struct kvm_vm *setup_vm(void *guest_code, struct kvm_vcpu **source,84struct kvm_vcpu **target)85{86struct kvm_vcpu_init init;87struct kvm_vm *vm;8889vm = vm_create(2);9091kvm_get_default_vcpu_target(vm, &init);92init.features[0] |= (1 << KVM_ARM_VCPU_PSCI_0_2);9394*source = aarch64_vcpu_add(vm, 0, &init, guest_code);95*target = aarch64_vcpu_add(vm, 1, &init, guest_code);9697kvm_arch_vm_finalize_vcpus(vm);98return vm;99}100101static void enter_guest(struct kvm_vcpu *vcpu)102{103struct ucall uc;104105vcpu_run(vcpu);106if (get_ucall(vcpu, &uc) == UCALL_ABORT)107REPORT_GUEST_ASSERT(uc);108}109110static void assert_vcpu_reset(struct kvm_vcpu *vcpu)111{112uint64_t obs_pc, obs_x0;113114obs_pc = vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.pc));115obs_x0 = vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.regs[0]));116117TEST_ASSERT(obs_pc == CPU_ON_ENTRY_ADDR,118"unexpected target cpu pc: %lx (expected: %lx)",119obs_pc, CPU_ON_ENTRY_ADDR);120TEST_ASSERT(obs_x0 == CPU_ON_CONTEXT_ID,121"unexpected target context id: %lx (expected: %lx)",122obs_x0, CPU_ON_CONTEXT_ID);123}124125static void guest_test_cpu_on(uint64_t target_cpu)126{127uint64_t target_state;128129GUEST_ASSERT(!psci_cpu_on(target_cpu, CPU_ON_ENTRY_ADDR, CPU_ON_CONTEXT_ID));130131do {132target_state = psci_affinity_info(target_cpu, 0);133134GUEST_ASSERT((target_state == PSCI_0_2_AFFINITY_LEVEL_ON) ||135(target_state == PSCI_0_2_AFFINITY_LEVEL_OFF));136} while (target_state != PSCI_0_2_AFFINITY_LEVEL_ON);137138GUEST_DONE();139}140141static void host_test_cpu_on(void)142{143struct kvm_vcpu *source, *target;144uint64_t target_mpidr;145struct kvm_vm *vm;146struct ucall uc;147148vm = setup_vm(guest_test_cpu_on, &source, &target);149150/*151* make sure the target is already off when executing the test.152*/153vcpu_power_off(target);154155target_mpidr = vcpu_get_reg(target, KVM_ARM64_SYS_REG(SYS_MPIDR_EL1));156vcpu_args_set(source, 1, target_mpidr & MPIDR_HWID_BITMASK);157enter_guest(source);158159if (get_ucall(source, &uc) != UCALL_DONE)160TEST_FAIL("Unhandled ucall: %lu", uc.cmd);161162assert_vcpu_reset(target);163kvm_vm_free(vm);164}165166static void guest_test_system_suspend(void)167{168uint64_t ret;169170/* assert that SYSTEM_SUSPEND is discoverable */171GUEST_ASSERT(!psci_features(PSCI_1_0_FN_SYSTEM_SUSPEND));172GUEST_ASSERT(!psci_features(PSCI_1_0_FN64_SYSTEM_SUSPEND));173174ret = psci_system_suspend(CPU_ON_ENTRY_ADDR, CPU_ON_CONTEXT_ID);175GUEST_SYNC(ret);176}177178static void host_test_system_suspend(void)179{180struct kvm_vcpu *source, *target;181struct kvm_run *run;182struct kvm_vm *vm;183184vm = setup_vm(guest_test_system_suspend, &source, &target);185vm_enable_cap(vm, KVM_CAP_ARM_SYSTEM_SUSPEND, 0);186187vcpu_power_off(target);188run = source->run;189190enter_guest(source);191192TEST_ASSERT_KVM_EXIT_REASON(source, KVM_EXIT_SYSTEM_EVENT);193TEST_ASSERT(run->system_event.type == KVM_SYSTEM_EVENT_SUSPEND,194"Unhandled system event: %u (expected: %u)",195run->system_event.type, KVM_SYSTEM_EVENT_SUSPEND);196197kvm_vm_free(vm);198}199200static void guest_test_system_off2(void)201{202uint64_t ret;203204/* assert that SYSTEM_OFF2 is discoverable */205GUEST_ASSERT(psci_features(PSCI_1_3_FN_SYSTEM_OFF2) &206PSCI_1_3_OFF_TYPE_HIBERNATE_OFF);207GUEST_ASSERT(psci_features(PSCI_1_3_FN64_SYSTEM_OFF2) &208PSCI_1_3_OFF_TYPE_HIBERNATE_OFF);209210/* With non-zero 'cookie' field, it should fail */211ret = psci_system_off2(PSCI_1_3_OFF_TYPE_HIBERNATE_OFF, 1);212GUEST_ASSERT(ret == PSCI_RET_INVALID_PARAMS);213214/*215* This would normally never return, so KVM sets the return value216* to PSCI_RET_INTERNAL_FAILURE. The test case *does* return, so217* that it can test both values for HIBERNATE_OFF.218*/219ret = psci_system_off2(PSCI_1_3_OFF_TYPE_HIBERNATE_OFF, 0);220GUEST_ASSERT(ret == PSCI_RET_INTERNAL_FAILURE);221222/*223* Revision F.b of the PSCI v1.3 specification documents zero as an224* alias for HIBERNATE_OFF, since that's the value used in earlier225* revisions of the spec and some implementations in the field.226*/227ret = psci_system_off2(0, 1);228GUEST_ASSERT(ret == PSCI_RET_INVALID_PARAMS);229230ret = psci_system_off2(0, 0);231GUEST_ASSERT(ret == PSCI_RET_INTERNAL_FAILURE);232233GUEST_DONE();234}235236static void host_test_system_off2(void)237{238struct kvm_vcpu *source, *target;239struct kvm_mp_state mps;240uint64_t psci_version = 0;241int nr_shutdowns = 0;242struct kvm_run *run;243struct ucall uc;244245setup_vm(guest_test_system_off2, &source, &target);246247psci_version = vcpu_get_reg(target, KVM_REG_ARM_PSCI_VERSION);248249TEST_ASSERT(psci_version >= PSCI_VERSION(1, 3),250"Unexpected PSCI version %lu.%lu",251PSCI_VERSION_MAJOR(psci_version),252PSCI_VERSION_MINOR(psci_version));253254vcpu_power_off(target);255run = source->run;256257enter_guest(source);258while (run->exit_reason == KVM_EXIT_SYSTEM_EVENT) {259TEST_ASSERT(run->system_event.type == KVM_SYSTEM_EVENT_SHUTDOWN,260"Unhandled system event: %u (expected: %u)",261run->system_event.type, KVM_SYSTEM_EVENT_SHUTDOWN);262TEST_ASSERT(run->system_event.ndata >= 1,263"Unexpected amount of system event data: %u (expected, >= 1)",264run->system_event.ndata);265TEST_ASSERT(run->system_event.data[0] & KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2,266"PSCI_OFF2 flag not set. Flags %llu (expected %llu)",267run->system_event.data[0], KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2);268269nr_shutdowns++;270271/* Restart the vCPU */272mps.mp_state = KVM_MP_STATE_RUNNABLE;273vcpu_mp_state_set(source, &mps);274275enter_guest(source);276}277278TEST_ASSERT(get_ucall(source, &uc) == UCALL_DONE, "Guest did not exit cleanly");279TEST_ASSERT(nr_shutdowns == 2, "Two shutdown events were expected, but saw %d", nr_shutdowns);280}281282int main(void)283{284TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_SYSTEM_SUSPEND));285286host_test_cpu_on();287host_test_system_suspend();288host_test_system_off2();289return 0;290}291292293