Merge branch kvm-arm64/psci-suspend into kvmarm-master/next
* kvm-arm64/psci-suspend: : . : Add support for PSCI SYSTEM_SUSPEND and allow userspace to : filter the wake-up events. : : Patches courtesy of Oliver. : . Documentation: KVM: Fix title level for PSCI_SUSPEND selftests: KVM: Test SYSTEM_SUSPEND PSCI call selftests: KVM: Refactor psci_test to make it amenable to new tests selftests: KVM: Use KVM_SET_MP_STATE to power off vCPU in psci_test selftests: KVM: Create helper for making SMCCC calls selftests: KVM: Rename psci_cpu_on_test to psci_test KVM: arm64: Implement PSCI SYSTEM_SUSPEND KVM: arm64: Add support for userspace to suspend a vCPU KVM: arm64: Return a value from check_vcpu_requests() KVM: arm64: Rename the KVM_REQ_SLEEP handler KVM: arm64: Track vCPU power state using MP state values KVM: arm64: Dedupe vCPU power off helpers KVM: arm64: Don't depend on fallthrough to hide SYSTEM_RESET2 Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
commit
3b8e21e3c3
|
@ -1476,14 +1476,43 @@ Possible values are:
|
||||||
[s390]
|
[s390]
|
||||||
KVM_MP_STATE_LOAD the vcpu is in a special load/startup state
|
KVM_MP_STATE_LOAD the vcpu is in a special load/startup state
|
||||||
[s390]
|
[s390]
|
||||||
|
KVM_MP_STATE_SUSPENDED the vcpu is in a suspend state and is waiting
|
||||||
|
for a wakeup event [arm64]
|
||||||
========================== ===============================================
|
========================== ===============================================
|
||||||
|
|
||||||
On x86, this ioctl is only useful after KVM_CREATE_IRQCHIP. Without an
|
On x86, this ioctl is only useful after KVM_CREATE_IRQCHIP. Without an
|
||||||
in-kernel irqchip, the multiprocessing state must be maintained by userspace on
|
in-kernel irqchip, the multiprocessing state must be maintained by userspace on
|
||||||
these architectures.
|
these architectures.
|
||||||
|
|
||||||
For arm64/riscv:
|
For arm64:
|
||||||
^^^^^^^^^^^^^^^^
|
^^^^^^^^^^
|
||||||
|
|
||||||
|
If a vCPU is in the KVM_MP_STATE_SUSPENDED state, KVM will emulate the
|
||||||
|
architectural execution of a WFI instruction.
|
||||||
|
|
||||||
|
If a wakeup event is recognized, KVM will exit to userspace with a
|
||||||
|
KVM_SYSTEM_EVENT exit, where the event type is KVM_SYSTEM_EVENT_WAKEUP. If
|
||||||
|
userspace wants to honor the wakeup, it must set the vCPU's MP state to
|
||||||
|
KVM_MP_STATE_RUNNABLE. If it does not, KVM will continue to await a wakeup
|
||||||
|
event in subsequent calls to KVM_RUN.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
If userspace intends to keep the vCPU in a SUSPENDED state, it is
|
||||||
|
strongly recommended that userspace take action to suppress the
|
||||||
|
wakeup event (such as masking an interrupt). Otherwise, subsequent
|
||||||
|
calls to KVM_RUN will immediately exit with a KVM_SYSTEM_EVENT_WAKEUP
|
||||||
|
event and inadvertently waste CPU cycles.
|
||||||
|
|
||||||
|
Additionally, if userspace takes action to suppress a wakeup event,
|
||||||
|
it is strongly recommended that it also restores the vCPU to its
|
||||||
|
original state when the vCPU is made RUNNABLE again. For example,
|
||||||
|
if userspace masked a pending interrupt to suppress the wakeup,
|
||||||
|
the interrupt should be unmasked before returning control to the
|
||||||
|
guest.
|
||||||
|
|
||||||
|
For riscv:
|
||||||
|
^^^^^^^^^^
|
||||||
|
|
||||||
The only states that are valid are KVM_MP_STATE_STOPPED and
|
The only states that are valid are KVM_MP_STATE_STOPPED and
|
||||||
KVM_MP_STATE_RUNNABLE which reflect if the vcpu is paused or not.
|
KVM_MP_STATE_RUNNABLE which reflect if the vcpu is paused or not.
|
||||||
|
@ -6003,6 +6032,8 @@ should put the acknowledged interrupt vector into the 'epr' field.
|
||||||
#define KVM_SYSTEM_EVENT_SHUTDOWN 1
|
#define KVM_SYSTEM_EVENT_SHUTDOWN 1
|
||||||
#define KVM_SYSTEM_EVENT_RESET 2
|
#define KVM_SYSTEM_EVENT_RESET 2
|
||||||
#define KVM_SYSTEM_EVENT_CRASH 3
|
#define KVM_SYSTEM_EVENT_CRASH 3
|
||||||
|
#define KVM_SYSTEM_EVENT_WAKEUP 4
|
||||||
|
#define KVM_SYSTEM_EVENT_SUSPEND 5
|
||||||
__u32 type;
|
__u32 type;
|
||||||
__u32 ndata;
|
__u32 ndata;
|
||||||
__u64 data[16];
|
__u64 data[16];
|
||||||
|
@ -6027,6 +6058,37 @@ Valid values for 'type' are:
|
||||||
has requested a crash condition maintenance. Userspace can choose
|
has requested a crash condition maintenance. Userspace can choose
|
||||||
to ignore the request, or to gather VM memory core dump and/or
|
to ignore the request, or to gather VM memory core dump and/or
|
||||||
reset/shutdown of the VM.
|
reset/shutdown of the VM.
|
||||||
|
- KVM_SYSTEM_EVENT_WAKEUP -- the exiting vCPU is in a suspended state and
|
||||||
|
KVM has recognized a wakeup event. Userspace may honor this event by
|
||||||
|
marking the exiting vCPU as runnable, or deny it and call KVM_RUN again.
|
||||||
|
- KVM_SYSTEM_EVENT_SUSPEND -- the guest has requested a suspension of
|
||||||
|
the VM.
|
||||||
|
|
||||||
|
For arm/arm64:
|
||||||
|
--------------
|
||||||
|
|
||||||
|
KVM_SYSTEM_EVENT_SUSPEND exits are enabled with the
|
||||||
|
KVM_CAP_ARM_SYSTEM_SUSPEND VM capability. If a guest invokes the PSCI
|
||||||
|
SYSTEM_SUSPEND function, KVM will exit to userspace with this event
|
||||||
|
type.
|
||||||
|
|
||||||
|
It is the sole responsibility of userspace to implement the PSCI
|
||||||
|
SYSTEM_SUSPEND call according to ARM DEN0022D.b 5.19 "SYSTEM_SUSPEND".
|
||||||
|
KVM does not change the vCPU's state before exiting to userspace, so
|
||||||
|
the call parameters are left in-place in the vCPU registers.
|
||||||
|
|
||||||
|
Userspace is _required_ to take action for such an exit. It must
|
||||||
|
either:
|
||||||
|
|
||||||
|
- Honor the guest request to suspend the VM. Userspace can request
|
||||||
|
in-kernel emulation of suspension by setting the calling vCPU's
|
||||||
|
state to KVM_MP_STATE_SUSPENDED. Userspace must configure the vCPU's
|
||||||
|
state according to the parameters passed to the PSCI function when
|
||||||
|
the calling vCPU is resumed. See ARM DEN0022D.b 5.19.1 "Intended use"
|
||||||
|
for details on the function parameters.
|
||||||
|
|
||||||
|
- Deny the guest request to suspend the VM. See ARM DEN0022D.b 5.19.2
|
||||||
|
"Caller responsibilities" for possible return values.
|
||||||
|
|
||||||
If KVM_CAP_SYSTEM_EVENT_DATA is present, the 'data' field can contain
|
If KVM_CAP_SYSTEM_EVENT_DATA is present, the 'data' field can contain
|
||||||
architecture specific information for the system-level event. Only
|
architecture specific information for the system-level event. Only
|
||||||
|
@ -7752,6 +7814,16 @@ At this time, KVM_PMU_CAP_DISABLE is the only capability. Setting
|
||||||
this capability will disable PMU virtualization for that VM. Usermode
|
this capability will disable PMU virtualization for that VM. Usermode
|
||||||
should adjust CPUID leaf 0xA to reflect that the PMU is disabled.
|
should adjust CPUID leaf 0xA to reflect that the PMU is disabled.
|
||||||
|
|
||||||
|
8.36 KVM_CAP_ARM_SYSTEM_SUSPEND
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
:Capability: KVM_CAP_ARM_SYSTEM_SUSPEND
|
||||||
|
:Architectures: arm64
|
||||||
|
:Type: vm
|
||||||
|
|
||||||
|
When enabled, KVM will exit to userspace with KVM_EXIT_SYSTEM_EVENT of
|
||||||
|
type KVM_SYSTEM_EVENT_SUSPEND to process the guest suspend request.
|
||||||
|
|
||||||
9. Known KVM API problems
|
9. Known KVM API problems
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
#define KVM_REQ_RECORD_STEAL KVM_ARCH_REQ(3)
|
#define KVM_REQ_RECORD_STEAL KVM_ARCH_REQ(3)
|
||||||
#define KVM_REQ_RELOAD_GICv4 KVM_ARCH_REQ(4)
|
#define KVM_REQ_RELOAD_GICv4 KVM_ARCH_REQ(4)
|
||||||
#define KVM_REQ_RELOAD_PMU KVM_ARCH_REQ(5)
|
#define KVM_REQ_RELOAD_PMU KVM_ARCH_REQ(5)
|
||||||
|
#define KVM_REQ_SUSPEND KVM_ARCH_REQ(6)
|
||||||
|
|
||||||
#define KVM_DIRTY_LOG_MANUAL_CAPS (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | \
|
#define KVM_DIRTY_LOG_MANUAL_CAPS (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | \
|
||||||
KVM_DIRTY_LOG_INITIALLY_SET)
|
KVM_DIRTY_LOG_INITIALLY_SET)
|
||||||
|
@ -149,6 +150,8 @@ struct kvm_arch {
|
||||||
*/
|
*/
|
||||||
#define KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED 3
|
#define KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED 3
|
||||||
#define KVM_ARCH_FLAG_EL1_32BIT 4
|
#define KVM_ARCH_FLAG_EL1_32BIT 4
|
||||||
|
/* PSCI SYSTEM_SUSPEND enabled for the guest */
|
||||||
|
#define KVM_ARCH_FLAG_SYSTEM_SUSPEND_ENABLED 5
|
||||||
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
|
@ -384,8 +387,8 @@ struct kvm_vcpu_arch {
|
||||||
u32 mdscr_el1;
|
u32 mdscr_el1;
|
||||||
} guest_debug_preserved;
|
} guest_debug_preserved;
|
||||||
|
|
||||||
/* vcpu power-off state */
|
/* vcpu power state */
|
||||||
bool power_off;
|
struct kvm_mp_state mp_state;
|
||||||
|
|
||||||
/* Don't run the guest (internal implementation need) */
|
/* Don't run the guest (internal implementation need) */
|
||||||
bool pause;
|
bool pause;
|
||||||
|
@ -863,4 +866,7 @@ void __init kvm_hyp_reserve(void);
|
||||||
static inline void kvm_hyp_reserve(void) { }
|
static inline void kvm_hyp_reserve(void) { }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void kvm_arm_vcpu_power_off(struct kvm_vcpu *vcpu);
|
||||||
|
bool kvm_arm_vcpu_stopped(struct kvm_vcpu *vcpu);
|
||||||
|
|
||||||
#endif /* __ARM64_KVM_HOST_H__ */
|
#endif /* __ARM64_KVM_HOST_H__ */
|
||||||
|
|
|
@ -97,6 +97,10 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
|
||||||
}
|
}
|
||||||
mutex_unlock(&kvm->lock);
|
mutex_unlock(&kvm->lock);
|
||||||
break;
|
break;
|
||||||
|
case KVM_CAP_ARM_SYSTEM_SUSPEND:
|
||||||
|
r = 0;
|
||||||
|
set_bit(KVM_ARCH_FLAG_SYSTEM_SUSPEND_ENABLED, &kvm->arch.flags);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
r = -EINVAL;
|
r = -EINVAL;
|
||||||
break;
|
break;
|
||||||
|
@ -211,6 +215,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
|
||||||
case KVM_CAP_SET_GUEST_DEBUG:
|
case KVM_CAP_SET_GUEST_DEBUG:
|
||||||
case KVM_CAP_VCPU_ATTRIBUTES:
|
case KVM_CAP_VCPU_ATTRIBUTES:
|
||||||
case KVM_CAP_PTP_KVM:
|
case KVM_CAP_PTP_KVM:
|
||||||
|
case KVM_CAP_ARM_SYSTEM_SUSPEND:
|
||||||
r = 1;
|
r = 1;
|
||||||
break;
|
break;
|
||||||
case KVM_CAP_SET_GUEST_DEBUG2:
|
case KVM_CAP_SET_GUEST_DEBUG2:
|
||||||
|
@ -428,20 +433,34 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
|
||||||
vcpu->cpu = -1;
|
vcpu->cpu = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vcpu_power_off(struct kvm_vcpu *vcpu)
|
void kvm_arm_vcpu_power_off(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
vcpu->arch.power_off = true;
|
vcpu->arch.mp_state.mp_state = KVM_MP_STATE_STOPPED;
|
||||||
kvm_make_request(KVM_REQ_SLEEP, vcpu);
|
kvm_make_request(KVM_REQ_SLEEP, vcpu);
|
||||||
kvm_vcpu_kick(vcpu);
|
kvm_vcpu_kick(vcpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool kvm_arm_vcpu_stopped(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
return vcpu->arch.mp_state.mp_state == KVM_MP_STATE_STOPPED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_arm_vcpu_suspend(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
vcpu->arch.mp_state.mp_state = KVM_MP_STATE_SUSPENDED;
|
||||||
|
kvm_make_request(KVM_REQ_SUSPEND, vcpu);
|
||||||
|
kvm_vcpu_kick(vcpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool kvm_arm_vcpu_suspended(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
return vcpu->arch.mp_state.mp_state == KVM_MP_STATE_SUSPENDED;
|
||||||
|
}
|
||||||
|
|
||||||
int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
|
int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
|
||||||
struct kvm_mp_state *mp_state)
|
struct kvm_mp_state *mp_state)
|
||||||
{
|
{
|
||||||
if (vcpu->arch.power_off)
|
*mp_state = vcpu->arch.mp_state;
|
||||||
mp_state->mp_state = KVM_MP_STATE_STOPPED;
|
|
||||||
else
|
|
||||||
mp_state->mp_state = KVM_MP_STATE_RUNNABLE;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -453,10 +472,13 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
|
||||||
|
|
||||||
switch (mp_state->mp_state) {
|
switch (mp_state->mp_state) {
|
||||||
case KVM_MP_STATE_RUNNABLE:
|
case KVM_MP_STATE_RUNNABLE:
|
||||||
vcpu->arch.power_off = false;
|
vcpu->arch.mp_state = *mp_state;
|
||||||
break;
|
break;
|
||||||
case KVM_MP_STATE_STOPPED:
|
case KVM_MP_STATE_STOPPED:
|
||||||
vcpu_power_off(vcpu);
|
kvm_arm_vcpu_power_off(vcpu);
|
||||||
|
break;
|
||||||
|
case KVM_MP_STATE_SUSPENDED:
|
||||||
|
kvm_arm_vcpu_suspend(vcpu);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
@ -476,7 +498,7 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *v)
|
||||||
{
|
{
|
||||||
bool irq_lines = *vcpu_hcr(v) & (HCR_VI | HCR_VF);
|
bool irq_lines = *vcpu_hcr(v) & (HCR_VI | HCR_VF);
|
||||||
return ((irq_lines || kvm_vgic_vcpu_pending_irq(v))
|
return ((irq_lines || kvm_vgic_vcpu_pending_irq(v))
|
||||||
&& !v->arch.power_off && !v->arch.pause);
|
&& !kvm_arm_vcpu_stopped(v) && !v->arch.pause);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool kvm_arch_vcpu_in_kernel(struct kvm_vcpu *vcpu)
|
bool kvm_arch_vcpu_in_kernel(struct kvm_vcpu *vcpu)
|
||||||
|
@ -588,15 +610,15 @@ void kvm_arm_resume_guest(struct kvm *kvm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vcpu_req_sleep(struct kvm_vcpu *vcpu)
|
static void kvm_vcpu_sleep(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
struct rcuwait *wait = kvm_arch_vcpu_get_wait(vcpu);
|
struct rcuwait *wait = kvm_arch_vcpu_get_wait(vcpu);
|
||||||
|
|
||||||
rcuwait_wait_event(wait,
|
rcuwait_wait_event(wait,
|
||||||
(!vcpu->arch.power_off) &&(!vcpu->arch.pause),
|
(!kvm_arm_vcpu_stopped(vcpu)) && (!vcpu->arch.pause),
|
||||||
TASK_INTERRUPTIBLE);
|
TASK_INTERRUPTIBLE);
|
||||||
|
|
||||||
if (vcpu->arch.power_off || vcpu->arch.pause) {
|
if (kvm_arm_vcpu_stopped(vcpu) || vcpu->arch.pause) {
|
||||||
/* Awaken to handle a signal, request we sleep again later. */
|
/* Awaken to handle a signal, request we sleep again later. */
|
||||||
kvm_make_request(KVM_REQ_SLEEP, vcpu);
|
kvm_make_request(KVM_REQ_SLEEP, vcpu);
|
||||||
}
|
}
|
||||||
|
@ -643,11 +665,53 @@ void kvm_vcpu_wfi(struct kvm_vcpu *vcpu)
|
||||||
preempt_enable();
|
preempt_enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void check_vcpu_requests(struct kvm_vcpu *vcpu)
|
static int kvm_vcpu_suspend(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
if (!kvm_arm_vcpu_suspended(vcpu))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
kvm_vcpu_wfi(vcpu);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The suspend state is sticky; we do not leave it until userspace
|
||||||
|
* explicitly marks the vCPU as runnable. Request that we suspend again
|
||||||
|
* later.
|
||||||
|
*/
|
||||||
|
kvm_make_request(KVM_REQ_SUSPEND, vcpu);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check to make sure the vCPU is actually runnable. If so, exit to
|
||||||
|
* userspace informing it of the wakeup condition.
|
||||||
|
*/
|
||||||
|
if (kvm_arch_vcpu_runnable(vcpu)) {
|
||||||
|
memset(&vcpu->run->system_event, 0, sizeof(vcpu->run->system_event));
|
||||||
|
vcpu->run->system_event.type = KVM_SYSTEM_EVENT_WAKEUP;
|
||||||
|
vcpu->run->exit_reason = KVM_EXIT_SYSTEM_EVENT;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Otherwise, we were unblocked to process a different event, such as a
|
||||||
|
* pending signal. Return 1 and allow kvm_arch_vcpu_ioctl_run() to
|
||||||
|
* process the event.
|
||||||
|
*/
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check_vcpu_requests - check and handle pending vCPU requests
|
||||||
|
* @vcpu: the VCPU pointer
|
||||||
|
*
|
||||||
|
* Return: 1 if we should enter the guest
|
||||||
|
* 0 if we should exit to userspace
|
||||||
|
* < 0 if we should exit to userspace, where the return value indicates
|
||||||
|
* an error
|
||||||
|
*/
|
||||||
|
static int check_vcpu_requests(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
if (kvm_request_pending(vcpu)) {
|
if (kvm_request_pending(vcpu)) {
|
||||||
if (kvm_check_request(KVM_REQ_SLEEP, vcpu))
|
if (kvm_check_request(KVM_REQ_SLEEP, vcpu))
|
||||||
vcpu_req_sleep(vcpu);
|
kvm_vcpu_sleep(vcpu);
|
||||||
|
|
||||||
if (kvm_check_request(KVM_REQ_VCPU_RESET, vcpu))
|
if (kvm_check_request(KVM_REQ_VCPU_RESET, vcpu))
|
||||||
kvm_reset_vcpu(vcpu);
|
kvm_reset_vcpu(vcpu);
|
||||||
|
@ -672,7 +736,12 @@ static void check_vcpu_requests(struct kvm_vcpu *vcpu)
|
||||||
if (kvm_check_request(KVM_REQ_RELOAD_PMU, vcpu))
|
if (kvm_check_request(KVM_REQ_RELOAD_PMU, vcpu))
|
||||||
kvm_pmu_handle_pmcr(vcpu,
|
kvm_pmu_handle_pmcr(vcpu,
|
||||||
__vcpu_sys_reg(vcpu, PMCR_EL0));
|
__vcpu_sys_reg(vcpu, PMCR_EL0));
|
||||||
|
|
||||||
|
if (kvm_check_request(KVM_REQ_SUSPEND, vcpu))
|
||||||
|
return kvm_vcpu_suspend(vcpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool vcpu_mode_is_bad_32bit(struct kvm_vcpu *vcpu)
|
static bool vcpu_mode_is_bad_32bit(struct kvm_vcpu *vcpu)
|
||||||
|
@ -788,7 +857,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
||||||
if (!ret)
|
if (!ret)
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
|
||||||
check_vcpu_requests(vcpu);
|
if (ret > 0)
|
||||||
|
ret = check_vcpu_requests(vcpu);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Preparing the interrupts to be injected also
|
* Preparing the interrupts to be injected also
|
||||||
|
@ -1121,9 +1191,9 @@ static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu,
|
||||||
* Handle the "start in power-off" case.
|
* Handle the "start in power-off" case.
|
||||||
*/
|
*/
|
||||||
if (test_bit(KVM_ARM_VCPU_POWER_OFF, vcpu->arch.features))
|
if (test_bit(KVM_ARM_VCPU_POWER_OFF, vcpu->arch.features))
|
||||||
vcpu_power_off(vcpu);
|
kvm_arm_vcpu_power_off(vcpu);
|
||||||
else
|
else
|
||||||
vcpu->arch.power_off = false;
|
vcpu->arch.mp_state.mp_state = KVM_MP_STATE_RUNNABLE;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,13 +51,6 @@ static unsigned long kvm_psci_vcpu_suspend(struct kvm_vcpu *vcpu)
|
||||||
return PSCI_RET_SUCCESS;
|
return PSCI_RET_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kvm_psci_vcpu_off(struct kvm_vcpu *vcpu)
|
|
||||||
{
|
|
||||||
vcpu->arch.power_off = true;
|
|
||||||
kvm_make_request(KVM_REQ_SLEEP, vcpu);
|
|
||||||
kvm_vcpu_kick(vcpu);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool kvm_psci_valid_affinity(struct kvm_vcpu *vcpu,
|
static inline bool kvm_psci_valid_affinity(struct kvm_vcpu *vcpu,
|
||||||
unsigned long affinity)
|
unsigned long affinity)
|
||||||
{
|
{
|
||||||
|
@ -83,7 +76,7 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu)
|
||||||
*/
|
*/
|
||||||
if (!vcpu)
|
if (!vcpu)
|
||||||
return PSCI_RET_INVALID_PARAMS;
|
return PSCI_RET_INVALID_PARAMS;
|
||||||
if (!vcpu->arch.power_off) {
|
if (!kvm_arm_vcpu_stopped(vcpu)) {
|
||||||
if (kvm_psci_version(source_vcpu) != KVM_ARM_PSCI_0_1)
|
if (kvm_psci_version(source_vcpu) != KVM_ARM_PSCI_0_1)
|
||||||
return PSCI_RET_ALREADY_ON;
|
return PSCI_RET_ALREADY_ON;
|
||||||
else
|
else
|
||||||
|
@ -107,12 +100,12 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu)
|
||||||
kvm_make_request(KVM_REQ_VCPU_RESET, vcpu);
|
kvm_make_request(KVM_REQ_VCPU_RESET, vcpu);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure the reset request is observed if the change to
|
* Make sure the reset request is observed if the RUNNABLE mp_state is
|
||||||
* power_off is observed.
|
* observed.
|
||||||
*/
|
*/
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
|
|
||||||
vcpu->arch.power_off = false;
|
vcpu->arch.mp_state.mp_state = KVM_MP_STATE_RUNNABLE;
|
||||||
kvm_vcpu_wake_up(vcpu);
|
kvm_vcpu_wake_up(vcpu);
|
||||||
|
|
||||||
return PSCI_RET_SUCCESS;
|
return PSCI_RET_SUCCESS;
|
||||||
|
@ -150,7 +143,7 @@ static unsigned long kvm_psci_vcpu_affinity_info(struct kvm_vcpu *vcpu)
|
||||||
mpidr = kvm_vcpu_get_mpidr_aff(tmp);
|
mpidr = kvm_vcpu_get_mpidr_aff(tmp);
|
||||||
if ((mpidr & target_affinity_mask) == target_affinity) {
|
if ((mpidr & target_affinity_mask) == target_affinity) {
|
||||||
matching_cpus++;
|
matching_cpus++;
|
||||||
if (!tmp->arch.power_off)
|
if (!kvm_arm_vcpu_stopped(tmp))
|
||||||
return PSCI_0_2_AFFINITY_LEVEL_ON;
|
return PSCI_0_2_AFFINITY_LEVEL_ON;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,7 +169,7 @@ static void kvm_prepare_system_event(struct kvm_vcpu *vcpu, u32 type, u64 flags)
|
||||||
* re-initialized.
|
* re-initialized.
|
||||||
*/
|
*/
|
||||||
kvm_for_each_vcpu(i, tmp, vcpu->kvm)
|
kvm_for_each_vcpu(i, tmp, vcpu->kvm)
|
||||||
tmp->arch.power_off = true;
|
tmp->arch.mp_state.mp_state = KVM_MP_STATE_STOPPED;
|
||||||
kvm_make_all_cpus_request(vcpu->kvm, KVM_REQ_SLEEP);
|
kvm_make_all_cpus_request(vcpu->kvm, KVM_REQ_SLEEP);
|
||||||
|
|
||||||
memset(&vcpu->run->system_event, 0, sizeof(vcpu->run->system_event));
|
memset(&vcpu->run->system_event, 0, sizeof(vcpu->run->system_event));
|
||||||
|
@ -202,6 +195,15 @@ static void kvm_psci_system_reset2(struct kvm_vcpu *vcpu)
|
||||||
KVM_SYSTEM_EVENT_RESET_FLAG_PSCI_RESET2);
|
KVM_SYSTEM_EVENT_RESET_FLAG_PSCI_RESET2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void kvm_psci_system_suspend(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
struct kvm_run *run = vcpu->run;
|
||||||
|
|
||||||
|
memset(&run->system_event, 0, sizeof(vcpu->run->system_event));
|
||||||
|
run->system_event.type = KVM_SYSTEM_EVENT_SUSPEND;
|
||||||
|
run->exit_reason = KVM_EXIT_SYSTEM_EVENT;
|
||||||
|
}
|
||||||
|
|
||||||
static void kvm_psci_narrow_to_32bit(struct kvm_vcpu *vcpu)
|
static void kvm_psci_narrow_to_32bit(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -245,7 +247,7 @@ static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu)
|
||||||
val = kvm_psci_vcpu_suspend(vcpu);
|
val = kvm_psci_vcpu_suspend(vcpu);
|
||||||
break;
|
break;
|
||||||
case PSCI_0_2_FN_CPU_OFF:
|
case PSCI_0_2_FN_CPU_OFF:
|
||||||
kvm_psci_vcpu_off(vcpu);
|
kvm_arm_vcpu_power_off(vcpu);
|
||||||
val = PSCI_RET_SUCCESS;
|
val = PSCI_RET_SUCCESS;
|
||||||
break;
|
break;
|
||||||
case PSCI_0_2_FN_CPU_ON:
|
case PSCI_0_2_FN_CPU_ON:
|
||||||
|
@ -305,9 +307,10 @@ static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu)
|
||||||
|
|
||||||
static int kvm_psci_1_x_call(struct kvm_vcpu *vcpu, u32 minor)
|
static int kvm_psci_1_x_call(struct kvm_vcpu *vcpu, u32 minor)
|
||||||
{
|
{
|
||||||
|
unsigned long val = PSCI_RET_NOT_SUPPORTED;
|
||||||
u32 psci_fn = smccc_get_function(vcpu);
|
u32 psci_fn = smccc_get_function(vcpu);
|
||||||
|
struct kvm *kvm = vcpu->kvm;
|
||||||
u32 arg;
|
u32 arg;
|
||||||
unsigned long val;
|
|
||||||
int ret = 1;
|
int ret = 1;
|
||||||
|
|
||||||
switch(psci_fn) {
|
switch(psci_fn) {
|
||||||
|
@ -320,6 +323,8 @@ static int kvm_psci_1_x_call(struct kvm_vcpu *vcpu, u32 minor)
|
||||||
if (val)
|
if (val)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
val = PSCI_RET_NOT_SUPPORTED;
|
||||||
|
|
||||||
switch(arg) {
|
switch(arg) {
|
||||||
case PSCI_0_2_FN_PSCI_VERSION:
|
case PSCI_0_2_FN_PSCI_VERSION:
|
||||||
case PSCI_0_2_FN_CPU_SUSPEND:
|
case PSCI_0_2_FN_CPU_SUSPEND:
|
||||||
|
@ -336,18 +341,32 @@ static int kvm_psci_1_x_call(struct kvm_vcpu *vcpu, u32 minor)
|
||||||
case ARM_SMCCC_VERSION_FUNC_ID:
|
case ARM_SMCCC_VERSION_FUNC_ID:
|
||||||
val = 0;
|
val = 0;
|
||||||
break;
|
break;
|
||||||
|
case PSCI_1_0_FN_SYSTEM_SUSPEND:
|
||||||
|
case PSCI_1_0_FN64_SYSTEM_SUSPEND:
|
||||||
|
if (test_bit(KVM_ARCH_FLAG_SYSTEM_SUSPEND_ENABLED, &kvm->arch.flags))
|
||||||
|
val = 0;
|
||||||
|
break;
|
||||||
case PSCI_1_1_FN_SYSTEM_RESET2:
|
case PSCI_1_1_FN_SYSTEM_RESET2:
|
||||||
case PSCI_1_1_FN64_SYSTEM_RESET2:
|
case PSCI_1_1_FN64_SYSTEM_RESET2:
|
||||||
if (minor >= 1) {
|
if (minor >= 1)
|
||||||
val = 0;
|
val = 0;
|
||||||
break;
|
|
||||||
}
|
|
||||||
fallthrough;
|
|
||||||
default:
|
|
||||||
val = PSCI_RET_NOT_SUPPORTED;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case PSCI_1_0_FN_SYSTEM_SUSPEND:
|
||||||
|
kvm_psci_narrow_to_32bit(vcpu);
|
||||||
|
fallthrough;
|
||||||
|
case PSCI_1_0_FN64_SYSTEM_SUSPEND:
|
||||||
|
/*
|
||||||
|
* Return directly to userspace without changing the vCPU's
|
||||||
|
* registers. Userspace depends on reading the SMCCC parameters
|
||||||
|
* to implement SYSTEM_SUSPEND.
|
||||||
|
*/
|
||||||
|
if (test_bit(KVM_ARCH_FLAG_SYSTEM_SUSPEND_ENABLED, &kvm->arch.flags)) {
|
||||||
|
kvm_psci_system_suspend(vcpu);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case PSCI_1_1_FN_SYSTEM_RESET2:
|
case PSCI_1_1_FN_SYSTEM_RESET2:
|
||||||
kvm_psci_narrow_to_32bit(vcpu);
|
kvm_psci_narrow_to_32bit(vcpu);
|
||||||
fallthrough;
|
fallthrough;
|
||||||
|
@ -365,7 +384,7 @@ static int kvm_psci_1_x_call(struct kvm_vcpu *vcpu, u32 minor)
|
||||||
val = PSCI_RET_INVALID_PARAMS;
|
val = PSCI_RET_INVALID_PARAMS;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
fallthrough;
|
break;
|
||||||
default:
|
default:
|
||||||
return kvm_psci_0_2_call(vcpu);
|
return kvm_psci_0_2_call(vcpu);
|
||||||
}
|
}
|
||||||
|
@ -382,7 +401,7 @@ static int kvm_psci_0_1_call(struct kvm_vcpu *vcpu)
|
||||||
|
|
||||||
switch (psci_fn) {
|
switch (psci_fn) {
|
||||||
case KVM_PSCI_FN_CPU_OFF:
|
case KVM_PSCI_FN_CPU_OFF:
|
||||||
kvm_psci_vcpu_off(vcpu);
|
kvm_arm_vcpu_power_off(vcpu);
|
||||||
val = PSCI_RET_SUCCESS;
|
val = PSCI_RET_SUCCESS;
|
||||||
break;
|
break;
|
||||||
case KVM_PSCI_FN_CPU_ON:
|
case KVM_PSCI_FN_CPU_ON:
|
||||||
|
|
|
@ -444,6 +444,8 @@ struct kvm_run {
|
||||||
#define KVM_SYSTEM_EVENT_SHUTDOWN 1
|
#define KVM_SYSTEM_EVENT_SHUTDOWN 1
|
||||||
#define KVM_SYSTEM_EVENT_RESET 2
|
#define KVM_SYSTEM_EVENT_RESET 2
|
||||||
#define KVM_SYSTEM_EVENT_CRASH 3
|
#define KVM_SYSTEM_EVENT_CRASH 3
|
||||||
|
#define KVM_SYSTEM_EVENT_WAKEUP 4
|
||||||
|
#define KVM_SYSTEM_EVENT_SUSPEND 5
|
||||||
__u32 type;
|
__u32 type;
|
||||||
__u32 ndata;
|
__u32 ndata;
|
||||||
union {
|
union {
|
||||||
|
@ -646,6 +648,7 @@ struct kvm_vapic_addr {
|
||||||
#define KVM_MP_STATE_OPERATING 7
|
#define KVM_MP_STATE_OPERATING 7
|
||||||
#define KVM_MP_STATE_LOAD 8
|
#define KVM_MP_STATE_LOAD 8
|
||||||
#define KVM_MP_STATE_AP_RESET_HOLD 9
|
#define KVM_MP_STATE_AP_RESET_HOLD 9
|
||||||
|
#define KVM_MP_STATE_SUSPENDED 10
|
||||||
|
|
||||||
struct kvm_mp_state {
|
struct kvm_mp_state {
|
||||||
__u32 mp_state;
|
__u32 mp_state;
|
||||||
|
@ -1152,6 +1155,7 @@ struct kvm_ppc_resize_hpt {
|
||||||
#define KVM_CAP_DISABLE_QUIRKS2 213
|
#define KVM_CAP_DISABLE_QUIRKS2 213
|
||||||
/* #define KVM_CAP_VM_TSC_CONTROL 214 */
|
/* #define KVM_CAP_VM_TSC_CONTROL 214 */
|
||||||
#define KVM_CAP_SYSTEM_EVENT_DATA 215
|
#define KVM_CAP_SYSTEM_EVENT_DATA 215
|
||||||
|
#define KVM_CAP_ARM_SYSTEM_SUSPEND 216
|
||||||
|
|
||||||
#ifdef KVM_CAP_IRQ_ROUTING
|
#ifdef KVM_CAP_IRQ_ROUTING
|
||||||
|
|
||||||
|
|
|
@ -45,11 +45,83 @@ static uint64_t psci_affinity_info(uint64_t target_affinity,
|
||||||
return res.a0;
|
return res.a0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void guest_main(uint64_t target_cpu)
|
static uint64_t psci_system_suspend(uint64_t entry_addr, uint64_t context_id)
|
||||||
|
{
|
||||||
|
struct arm_smccc_res res;
|
||||||
|
|
||||||
|
smccc_hvc(PSCI_1_0_FN64_SYSTEM_SUSPEND, entry_addr, context_id,
|
||||||
|
0, 0, 0, 0, 0, &res);
|
||||||
|
|
||||||
|
return res.a0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t psci_features(uint32_t func_id)
|
||||||
|
{
|
||||||
|
struct arm_smccc_res res;
|
||||||
|
|
||||||
|
smccc_hvc(PSCI_1_0_FN_PSCI_FEATURES, func_id, 0, 0, 0, 0, 0, 0, &res);
|
||||||
|
|
||||||
|
return res.a0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vcpu_power_off(struct kvm_vm *vm, uint32_t vcpuid)
|
||||||
|
{
|
||||||
|
struct kvm_mp_state mp_state = {
|
||||||
|
.mp_state = KVM_MP_STATE_STOPPED,
|
||||||
|
};
|
||||||
|
|
||||||
|
vcpu_set_mp_state(vm, vcpuid, &mp_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct kvm_vm *setup_vm(void *guest_code)
|
||||||
|
{
|
||||||
|
struct kvm_vcpu_init init;
|
||||||
|
struct kvm_vm *vm;
|
||||||
|
|
||||||
|
vm = vm_create(VM_MODE_DEFAULT, DEFAULT_GUEST_PHY_PAGES, O_RDWR);
|
||||||
|
kvm_vm_elf_load(vm, program_invocation_name);
|
||||||
|
ucall_init(vm, NULL);
|
||||||
|
|
||||||
|
vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init);
|
||||||
|
init.features[0] |= (1 << KVM_ARM_VCPU_PSCI_0_2);
|
||||||
|
|
||||||
|
aarch64_vcpu_add_default(vm, VCPU_ID_SOURCE, &init, guest_code);
|
||||||
|
aarch64_vcpu_add_default(vm, VCPU_ID_TARGET, &init, guest_code);
|
||||||
|
|
||||||
|
return vm;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void enter_guest(struct kvm_vm *vm, uint32_t vcpuid)
|
||||||
|
{
|
||||||
|
struct ucall uc;
|
||||||
|
|
||||||
|
vcpu_run(vm, vcpuid);
|
||||||
|
if (get_ucall(vm, vcpuid, &uc) == UCALL_ABORT)
|
||||||
|
TEST_FAIL("%s at %s:%ld", (const char *)uc.args[0], __FILE__,
|
||||||
|
uc.args[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void assert_vcpu_reset(struct kvm_vm *vm, uint32_t vcpuid)
|
||||||
|
{
|
||||||
|
uint64_t obs_pc, obs_x0;
|
||||||
|
|
||||||
|
get_reg(vm, vcpuid, ARM64_CORE_REG(regs.pc), &obs_pc);
|
||||||
|
get_reg(vm, vcpuid, ARM64_CORE_REG(regs.regs[0]), &obs_x0);
|
||||||
|
|
||||||
|
TEST_ASSERT(obs_pc == CPU_ON_ENTRY_ADDR,
|
||||||
|
"unexpected target cpu pc: %lx (expected: %lx)",
|
||||||
|
obs_pc, CPU_ON_ENTRY_ADDR);
|
||||||
|
TEST_ASSERT(obs_x0 == CPU_ON_CONTEXT_ID,
|
||||||
|
"unexpected target context id: %lx (expected: %lx)",
|
||||||
|
obs_x0, CPU_ON_CONTEXT_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void guest_test_cpu_on(uint64_t target_cpu)
|
||||||
{
|
{
|
||||||
GUEST_ASSERT(!psci_cpu_on(target_cpu, CPU_ON_ENTRY_ADDR, CPU_ON_CONTEXT_ID));
|
|
||||||
uint64_t target_state;
|
uint64_t target_state;
|
||||||
|
|
||||||
|
GUEST_ASSERT(!psci_cpu_on(target_cpu, CPU_ON_ENTRY_ADDR, CPU_ON_CONTEXT_ID));
|
||||||
|
|
||||||
do {
|
do {
|
||||||
target_state = psci_affinity_info(target_cpu, 0);
|
target_state = psci_affinity_info(target_cpu, 0);
|
||||||
|
|
||||||
|
@ -60,53 +132,82 @@ static void guest_main(uint64_t target_cpu)
|
||||||
GUEST_DONE();
|
GUEST_DONE();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void)
|
static void host_test_cpu_on(void)
|
||||||
{
|
{
|
||||||
uint64_t target_mpidr, obs_pc, obs_x0;
|
uint64_t target_mpidr;
|
||||||
struct kvm_vcpu_init init;
|
|
||||||
struct kvm_vm *vm;
|
struct kvm_vm *vm;
|
||||||
struct ucall uc;
|
struct ucall uc;
|
||||||
|
|
||||||
vm = vm_create(VM_MODE_DEFAULT, DEFAULT_GUEST_PHY_PAGES, O_RDWR);
|
vm = setup_vm(guest_test_cpu_on);
|
||||||
kvm_vm_elf_load(vm, program_invocation_name);
|
|
||||||
ucall_init(vm, NULL);
|
|
||||||
|
|
||||||
vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init);
|
|
||||||
init.features[0] |= (1 << KVM_ARM_VCPU_PSCI_0_2);
|
|
||||||
|
|
||||||
aarch64_vcpu_add_default(vm, VCPU_ID_SOURCE, &init, guest_main);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* make sure the target is already off when executing the test.
|
* make sure the target is already off when executing the test.
|
||||||
*/
|
*/
|
||||||
init.features[0] |= (1 << KVM_ARM_VCPU_POWER_OFF);
|
vcpu_power_off(vm, VCPU_ID_TARGET);
|
||||||
aarch64_vcpu_add_default(vm, VCPU_ID_TARGET, &init, guest_main);
|
|
||||||
|
|
||||||
get_reg(vm, VCPU_ID_TARGET, KVM_ARM64_SYS_REG(SYS_MPIDR_EL1), &target_mpidr);
|
get_reg(vm, VCPU_ID_TARGET, KVM_ARM64_SYS_REG(SYS_MPIDR_EL1), &target_mpidr);
|
||||||
vcpu_args_set(vm, VCPU_ID_SOURCE, 1, target_mpidr & MPIDR_HWID_BITMASK);
|
vcpu_args_set(vm, VCPU_ID_SOURCE, 1, target_mpidr & MPIDR_HWID_BITMASK);
|
||||||
vcpu_run(vm, VCPU_ID_SOURCE);
|
enter_guest(vm, VCPU_ID_SOURCE);
|
||||||
|
|
||||||
switch (get_ucall(vm, VCPU_ID_SOURCE, &uc)) {
|
if (get_ucall(vm, VCPU_ID_SOURCE, &uc) != UCALL_DONE)
|
||||||
case UCALL_DONE:
|
|
||||||
break;
|
|
||||||
case UCALL_ABORT:
|
|
||||||
TEST_FAIL("%s at %s:%ld", (const char *)uc.args[0], __FILE__,
|
|
||||||
uc.args[1]);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
TEST_FAIL("Unhandled ucall: %lu", uc.cmd);
|
TEST_FAIL("Unhandled ucall: %lu", uc.cmd);
|
||||||
}
|
|
||||||
|
|
||||||
get_reg(vm, VCPU_ID_TARGET, ARM64_CORE_REG(regs.pc), &obs_pc);
|
assert_vcpu_reset(vm, VCPU_ID_TARGET);
|
||||||
get_reg(vm, VCPU_ID_TARGET, ARM64_CORE_REG(regs.regs[0]), &obs_x0);
|
kvm_vm_free(vm);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_ASSERT(obs_pc == CPU_ON_ENTRY_ADDR,
|
static void enable_system_suspend(struct kvm_vm *vm)
|
||||||
"unexpected target cpu pc: %lx (expected: %lx)",
|
{
|
||||||
obs_pc, CPU_ON_ENTRY_ADDR);
|
struct kvm_enable_cap cap = {
|
||||||
TEST_ASSERT(obs_x0 == CPU_ON_CONTEXT_ID,
|
.cap = KVM_CAP_ARM_SYSTEM_SUSPEND,
|
||||||
"unexpected target context id: %lx (expected: %lx)",
|
};
|
||||||
obs_x0, CPU_ON_CONTEXT_ID);
|
|
||||||
|
vm_enable_cap(vm, &cap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void guest_test_system_suspend(void)
|
||||||
|
{
|
||||||
|
uint64_t ret;
|
||||||
|
|
||||||
|
/* assert that SYSTEM_SUSPEND is discoverable */
|
||||||
|
GUEST_ASSERT(!psci_features(PSCI_1_0_FN_SYSTEM_SUSPEND));
|
||||||
|
GUEST_ASSERT(!psci_features(PSCI_1_0_FN64_SYSTEM_SUSPEND));
|
||||||
|
|
||||||
|
ret = psci_system_suspend(CPU_ON_ENTRY_ADDR, CPU_ON_CONTEXT_ID);
|
||||||
|
GUEST_SYNC(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void host_test_system_suspend(void)
|
||||||
|
{
|
||||||
|
struct kvm_run *run;
|
||||||
|
struct kvm_vm *vm;
|
||||||
|
|
||||||
|
vm = setup_vm(guest_test_system_suspend);
|
||||||
|
enable_system_suspend(vm);
|
||||||
|
|
||||||
|
vcpu_power_off(vm, VCPU_ID_TARGET);
|
||||||
|
run = vcpu_state(vm, VCPU_ID_SOURCE);
|
||||||
|
|
||||||
|
enter_guest(vm, VCPU_ID_SOURCE);
|
||||||
|
|
||||||
|
TEST_ASSERT(run->exit_reason == KVM_EXIT_SYSTEM_EVENT,
|
||||||
|
"Unhandled exit reason: %u (%s)",
|
||||||
|
run->exit_reason, exit_reason_str(run->exit_reason));
|
||||||
|
TEST_ASSERT(run->system_event.type == KVM_SYSTEM_EVENT_SUSPEND,
|
||||||
|
"Unhandled system event: %u (expected: %u)",
|
||||||
|
run->system_event.type, KVM_SYSTEM_EVENT_SUSPEND);
|
||||||
|
|
||||||
kvm_vm_free(vm);
|
kvm_vm_free(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
if (!kvm_check_cap(KVM_CAP_ARM_SYSTEM_SUSPEND)) {
|
||||||
|
print_skip("KVM_CAP_ARM_SYSTEM_SUSPEND not supported");
|
||||||
|
exit(KSFT_SKIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
host_test_cpu_on();
|
||||||
|
host_test_system_suspend();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue