mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
xen/arm: Implement PSCI SYSTEM_SUSPEND call for guests
Add support for the PSCI SYSTEM_SUSPEND function in the vPSCI interface,
allowing guests to request suspend via the PSCI v1.0+ SYSTEM_SUSPEND call
(both 32-bit and 64-bit variants).
Implementation details:
- Add SYSTEM_SUSPEND function IDs to PSCI definitions
- Trap and handle SYSTEM_SUSPEND in vPSCI
- Reject SYSTEM_SUSPEND for the hardware domain to avoid host shutdown
- Require all secondary VCPUs to be offline before suspend
- Split arch_set_info_guest() into arch_vcpu_validate_guest_context() and
arch_vcpu_apply_guest_context() for reuse by vPSCI
- Add vpsci_build_guest_context() helper and store prevalidated context in
resume_ctx; resume applies it and frees it
- Add arch_domain_resume() function is an architecture-specific hook that
is invoked during domain resume to perform any necessary setup or
restoration steps required by the platform.
Usage:
For Linux-based guests, suspend can be initiated with:
echo mem > /sys/power/state
or via:
systemctl suspend
Resume from control domain:
xl resume <domain>
Signed-off-by: Mykola Kvach <mykola_kvach@epam.com>
Reviewed-by: Michal Orzel <michal.orzel@amd.com>
This commit is contained in:
1 parent
1792bb9a99
commit
1e3dc2f866
9 files changed
+255
-42
No files matched your search
+72
-14
@@ -12,6 +12,8 @@
|
|||||||
#include <xen/softirq.h>
|
#include <xen/softirq.h>
|
||||||
#include <xen/wait.h>
|
#include <xen/wait.h>
|
||||||
|
|
||||||
|
#include <public/sched.h>
|
||||||
|
|
||||||
#include <asm/arm64/sve.h>
|
#include <asm/arm64/sve.h>
|
||||||
#include <asm/cpuerrata.h>
|
#include <asm/cpuerrata.h>
|
||||||
#include <asm/cpufeature.h>
|
#include <asm/cpufeature.h>
|
||||||
@@ -24,10 +26,12 @@
|
|||||||
#include <asm/platform.h>
|
#include <asm/platform.h>
|
||||||
#include <asm/procinfo.h>
|
#include <asm/procinfo.h>
|
||||||
#include <asm/regs.h>
|
#include <asm/regs.h>
|
||||||
|
#include <asm/suspend.h>
|
||||||
#include <asm/firmware/sci.h>
|
#include <asm/firmware/sci.h>
|
||||||
#include <asm/tee/tee.h>
|
#include <asm/tee/tee.h>
|
||||||
#include <asm/vfp.h>
|
#include <asm/vfp.h>
|
||||||
#include <asm/vgic.h>
|
#include <asm/vgic.h>
|
||||||
|
#include <asm/vpsci.h>
|
||||||
#include <asm/vtimer.h>
|
#include <asm/vtimer.h>
|
||||||
|
|
||||||
#include "vpci.h"
|
#include "vpci.h"
|
||||||
@@ -770,8 +774,18 @@ int arch_domain_teardown(struct domain *d)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void resume_ctx_reset(struct resume_info *ctx)
|
||||||
|
{
|
||||||
|
if ( ctx->ctxt )
|
||||||
|
free_vcpu_guest_context(ctx->ctxt);
|
||||||
|
|
||||||
|
memset(ctx, 0, sizeof(*ctx));
|
||||||
|
}
|
||||||
|
|
||||||
void arch_domain_destroy(struct domain *d)
|
void arch_domain_destroy(struct domain *d)
|
||||||
{
|
{
|
||||||
|
resume_ctx_reset(&d->arch.resume_ctx);
|
||||||
|
|
||||||
tee_free_domain_ctx(d);
|
tee_free_domain_ctx(d);
|
||||||
/* IOMMU page table is shared with P2M, always call
|
/* IOMMU page table is shared with P2M, always call
|
||||||
* iommu_domain_destroy() before p2m_final_teardown().
|
* iommu_domain_destroy() before p2m_final_teardown().
|
||||||
@@ -806,6 +820,28 @@ void arch_domain_creation_finished(struct domain *d)
|
|||||||
p2m_domain_creation_finished(d);
|
p2m_domain_creation_finished(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void arch_domain_resume(struct domain *d)
|
||||||
|
{
|
||||||
|
struct resume_info *ctx = &d->arch.resume_ctx;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* It is still possible to call domain_shutdown() with a suspend reason
|
||||||
|
* via some hypercalls, such as SCHEDOP_shutdown or SCHEDOP_remote_shutdown.
|
||||||
|
* In these cases, the resume context will be empty, so there is no
|
||||||
|
* suspend-specific state to restore.
|
||||||
|
*/
|
||||||
|
if ( !ctx->wake_cpu )
|
||||||
|
return;
|
||||||
|
|
||||||
|
ASSERT(d->shutdown_code == SHUTDOWN_suspend);
|
||||||
|
|
||||||
|
domain_lock(d);
|
||||||
|
arch_vcpu_apply_guest_context(ctx->wake_cpu, ctx->ctxt);
|
||||||
|
domain_unlock(d);
|
||||||
|
|
||||||
|
resume_ctx_reset(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
static int is_guest_pv32_psr(uint32_t psr)
|
static int is_guest_pv32_psr(uint32_t psr)
|
||||||
{
|
{
|
||||||
switch (psr & PSR_MODE_MASK)
|
switch (psr & PSR_MODE_MASK)
|
||||||
@@ -848,15 +884,32 @@ static int is_guest_pv64_psr(uint64_t psr)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void arch_vcpu_apply_guest_context(struct vcpu *v,
|
||||||
|
const struct vcpu_guest_context *ctxt)
|
||||||
|
{
|
||||||
|
vcpu_regs_user_to_hyp(v, &ctxt->user_regs);
|
||||||
|
|
||||||
|
v->arch.sctlr = ctxt->sctlr;
|
||||||
|
v->arch.ttbr0 = ctxt->ttbr0;
|
||||||
|
v->arch.ttbr1 = ctxt->ttbr1;
|
||||||
|
v->arch.ttbcr = ctxt->ttbcr;
|
||||||
|
|
||||||
|
v->is_initialised = 1;
|
||||||
|
|
||||||
|
if ( ctxt->flags & VGCF_online )
|
||||||
|
clear_bit(_VPF_down, &v->pause_flags);
|
||||||
|
else
|
||||||
|
set_bit(_VPF_down, &v->pause_flags);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialise vCPU state. The context may be supplied by an external entity, so
|
* Initialise vCPU state. The context may be supplied by an external entity, so
|
||||||
* we need to validate it.
|
* we need to validate it.
|
||||||
*/
|
*/
|
||||||
int arch_set_info_guest(
|
int arch_vcpu_validate_guest_context(const struct vcpu *v,
|
||||||
struct vcpu *v, vcpu_guest_context_u c)
|
const struct vcpu_guest_context *ctxt)
|
||||||
{
|
{
|
||||||
struct vcpu_guest_context *ctxt = c.nat;
|
const struct vcpu_guest_core_regs *regs = &ctxt->user_regs;
|
||||||
struct vcpu_guest_core_regs *regs = &c.nat->user_regs;
|
|
||||||
|
|
||||||
if ( is_32bit_domain(v->domain) )
|
if ( is_32bit_domain(v->domain) )
|
||||||
{
|
{
|
||||||
@@ -885,19 +938,24 @@ int arch_set_info_guest(
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
vcpu_regs_user_to_hyp(v, regs);
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
v->arch.sctlr = ctxt->sctlr;
|
/*
|
||||||
v->arch.ttbr0 = ctxt->ttbr0;
|
* Initialise vCPU state. The context may be supplied by an external entity, so
|
||||||
v->arch.ttbr1 = ctxt->ttbr1;
|
* we need to validate it.
|
||||||
v->arch.ttbcr = ctxt->ttbcr;
|
*/
|
||||||
|
int arch_set_info_guest(
|
||||||
|
struct vcpu *v, vcpu_guest_context_u c)
|
||||||
|
{
|
||||||
|
struct vcpu_guest_context *ctxt = c.nat;
|
||||||
|
int rc;
|
||||||
|
|
||||||
v->is_initialised = 1;
|
rc = arch_vcpu_validate_guest_context(v, ctxt);
|
||||||
|
if ( rc )
|
||||||
|
return rc;
|
||||||
|
|
||||||
if ( ctxt->flags & VGCF_online )
|
arch_vcpu_apply_guest_context(v, ctxt);
|
||||||
clear_bit(_VPF_down, &v->pause_flags);
|
|
||||||
else
|
|
||||||
set_bit(_VPF_down, &v->pause_flags);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <xen/timer.h>
|
#include <xen/timer.h>
|
||||||
#include <asm/page.h>
|
#include <asm/page.h>
|
||||||
#include <asm/p2m.h>
|
#include <asm/p2m.h>
|
||||||
|
#include <asm/suspend.h>
|
||||||
#include <asm/vfp.h>
|
#include <asm/vfp.h>
|
||||||
#include <asm/mmio.h>
|
#include <asm/mmio.h>
|
||||||
#include <asm/gic.h>
|
#include <asm/gic.h>
|
||||||
@@ -126,6 +127,7 @@ struct arch_domain
|
|||||||
void *sci_data;
|
void *sci_data;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct resume_info resume_ctx;
|
||||||
} __cacheline_aligned;
|
} __cacheline_aligned;
|
||||||
|
|
||||||
struct arch_vcpu
|
struct arch_vcpu
|
||||||
@@ -290,6 +292,11 @@ static inline register_t vcpuid_to_vaffinity(unsigned int vcpuid)
|
|||||||
return vaff;
|
return vaff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int arch_vcpu_validate_guest_context(const struct vcpu *v,
|
||||||
|
const struct vcpu_guest_context *ctxt);
|
||||||
|
void arch_vcpu_apply_guest_context(struct vcpu *v,
|
||||||
|
const struct vcpu_guest_context *ctxt);
|
||||||
|
|
||||||
static inline struct vcpu_guest_context *alloc_vcpu_guest_context(void)
|
static inline struct vcpu_guest_context *alloc_vcpu_guest_context(void)
|
||||||
{
|
{
|
||||||
return xmalloc(struct vcpu_guest_context);
|
return xmalloc(struct vcpu_guest_context);
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ PERFCOUNTER(vpsci_system_reset, "vpsci: system_reset")
|
|||||||
PERFCOUNTER(vpsci_cpu_suspend, "vpsci: cpu_suspend")
|
PERFCOUNTER(vpsci_cpu_suspend, "vpsci: cpu_suspend")
|
||||||
PERFCOUNTER(vpsci_cpu_affinity_info, "vpsci: cpu_affinity_info")
|
PERFCOUNTER(vpsci_cpu_affinity_info, "vpsci: cpu_affinity_info")
|
||||||
PERFCOUNTER(vpsci_features, "vpsci: features")
|
PERFCOUNTER(vpsci_features, "vpsci: features")
|
||||||
|
PERFCOUNTER(vpsci_system_suspend, "vpsci: system_suspend")
|
||||||
|
|
||||||
PERFCOUNTER(vcpu_kick, "vcpu: notify other vcpu")
|
PERFCOUNTER(vcpu_kick, "vcpu: notify other vcpu")
|
||||||
|
|
||||||
|
|||||||
@@ -47,10 +47,12 @@ void call_psci_system_reset(void);
|
|||||||
#define PSCI_0_2_FN32_SYSTEM_OFF PSCI_0_2_FN32(8)
|
#define PSCI_0_2_FN32_SYSTEM_OFF PSCI_0_2_FN32(8)
|
||||||
#define PSCI_0_2_FN32_SYSTEM_RESET PSCI_0_2_FN32(9)
|
#define PSCI_0_2_FN32_SYSTEM_RESET PSCI_0_2_FN32(9)
|
||||||
#define PSCI_1_0_FN32_PSCI_FEATURES PSCI_0_2_FN32(10)
|
#define PSCI_1_0_FN32_PSCI_FEATURES PSCI_0_2_FN32(10)
|
||||||
|
#define PSCI_1_0_FN32_SYSTEM_SUSPEND PSCI_0_2_FN32(14)
|
||||||
|
|
||||||
#define PSCI_0_2_FN64_CPU_SUSPEND PSCI_0_2_FN64(1)
|
#define PSCI_0_2_FN64_CPU_SUSPEND PSCI_0_2_FN64(1)
|
||||||
#define PSCI_0_2_FN64_CPU_ON PSCI_0_2_FN64(3)
|
#define PSCI_0_2_FN64_CPU_ON PSCI_0_2_FN64(3)
|
||||||
#define PSCI_0_2_FN64_AFFINITY_INFO PSCI_0_2_FN64(4)
|
#define PSCI_0_2_FN64_AFFINITY_INFO PSCI_0_2_FN64(4)
|
||||||
|
#define PSCI_1_0_FN64_SYSTEM_SUSPEND PSCI_0_2_FN64(14)
|
||||||
|
|
||||||
/* PSCI v0.2 affinity level state returned by AFFINITY_INFO */
|
/* PSCI v0.2 affinity level state returned by AFFINITY_INFO */
|
||||||
#define PSCI_0_2_AFFINITY_LEVEL_ON 0
|
#define PSCI_0_2_AFFINITY_LEVEL_ON 0
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
|
||||||
|
#ifndef ARM_SUSPEND_H
|
||||||
|
#define ARM_SUSPEND_H
|
||||||
|
|
||||||
|
struct domain;
|
||||||
|
struct vcpu;
|
||||||
|
struct vcpu_guest_context;
|
||||||
|
|
||||||
|
struct resume_info {
|
||||||
|
struct vcpu_guest_context *ctxt;
|
||||||
|
struct vcpu *wake_cpu;
|
||||||
|
};
|
||||||
|
|
||||||
|
void arch_domain_resume(struct domain *d);
|
||||||
|
|
||||||
|
#endif /* ARM_SUSPEND_H */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* mode: C
|
||||||
|
* c-file-style: "BSD"
|
||||||
|
* c-basic-offset: 4
|
||||||
|
* tab-width: 4
|
||||||
|
* indent-tabs-mode: nil
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
#include <asm/psci.h>
|
#include <asm/psci.h>
|
||||||
|
|
||||||
/* Number of function implemented by virtual PSCI (only 0.2 or later) */
|
/* Number of function implemented by virtual PSCI (only 0.2 or later) */
|
||||||
#define VPSCI_NR_FUNCS 12
|
#define VPSCI_NR_FUNCS 14
|
||||||
|
|
||||||
/* Functions handle PSCI calls from the guests */
|
/* Functions handle PSCI calls from the guests */
|
||||||
bool do_vpsci_0_1_call(struct cpu_user_regs *regs, uint32_t fid);
|
bool do_vpsci_0_1_call(struct cpu_user_regs *regs, uint32_t fid);
|
||||||
|
|||||||
+119
-27
@@ -4,38 +4,24 @@
|
|||||||
#include <xen/types.h>
|
#include <xen/types.h>
|
||||||
|
|
||||||
#include <asm/current.h>
|
#include <asm/current.h>
|
||||||
|
#include <asm/domain.h>
|
||||||
#include <asm/vgic.h>
|
#include <asm/vgic.h>
|
||||||
#include <asm/vpsci.h>
|
#include <asm/vpsci.h>
|
||||||
#include <asm/event.h>
|
#include <asm/event.h>
|
||||||
|
|
||||||
#include <public/sched.h>
|
#include <public/sched.h>
|
||||||
|
|
||||||
static int do_common_cpu_on(register_t target_cpu, register_t entry_point,
|
static int vpsci_build_guest_context(struct vcpu *v, register_t entry_point,
|
||||||
register_t context_id)
|
register_t context_id,
|
||||||
|
struct vcpu_guest_context **out)
|
||||||
{
|
{
|
||||||
struct vcpu *v;
|
bool is_thumb = entry_point & 1;
|
||||||
struct domain *d = current->domain;
|
|
||||||
struct vcpu_guest_context *ctxt;
|
struct vcpu_guest_context *ctxt;
|
||||||
int rc;
|
int rc;
|
||||||
bool is_thumb = entry_point & 1;
|
|
||||||
register_t vcpuid;
|
|
||||||
|
|
||||||
vcpuid = vaffinity_to_vcpuid(target_cpu);
|
ctxt = alloc_vcpu_guest_context();
|
||||||
|
if ( ctxt == NULL )
|
||||||
if ( (v = domain_vcpu(d, vcpuid)) == NULL )
|
return -ENOMEM;
|
||||||
return PSCI_INVALID_PARAMETERS;
|
|
||||||
|
|
||||||
/* THUMB set is not allowed with 64-bit domain */
|
|
||||||
if ( is_64bit_domain(d) && is_thumb )
|
|
||||||
return PSCI_INVALID_ADDRESS;
|
|
||||||
|
|
||||||
if ( !test_bit(_VPF_down, &v->pause_flags) )
|
|
||||||
return PSCI_ALREADY_ON;
|
|
||||||
|
|
||||||
if ( (ctxt = alloc_vcpu_guest_context()) == NULL )
|
|
||||||
return PSCI_DENIED;
|
|
||||||
|
|
||||||
vgic_clear_pending_irqs(v);
|
|
||||||
|
|
||||||
memset(ctxt, 0, sizeof(*ctxt));
|
memset(ctxt, 0, sizeof(*ctxt));
|
||||||
ctxt->user_regs.pc64 = (u64) entry_point;
|
ctxt->user_regs.pc64 = (u64) entry_point;
|
||||||
@@ -48,7 +34,7 @@ static int do_common_cpu_on(register_t target_cpu, register_t entry_point,
|
|||||||
* x0/r0_usr are always updated because for PSCI 0.1 the general
|
* x0/r0_usr are always updated because for PSCI 0.1 the general
|
||||||
* purpose registers are undefined upon CPU_on.
|
* purpose registers are undefined upon CPU_on.
|
||||||
*/
|
*/
|
||||||
if ( is_32bit_domain(d) )
|
if ( is_32bit_domain(v->domain) )
|
||||||
{
|
{
|
||||||
ctxt->user_regs.cpsr = PSR_GUEST32_INIT;
|
ctxt->user_regs.cpsr = PSR_GUEST32_INIT;
|
||||||
/* Start the VCPU with THUMB set if it's requested by the kernel */
|
/* Start the VCPU with THUMB set if it's requested by the kernel */
|
||||||
@@ -69,15 +55,51 @@ static int do_common_cpu_on(register_t target_cpu, register_t entry_point,
|
|||||||
#endif
|
#endif
|
||||||
ctxt->flags = VGCF_online;
|
ctxt->flags = VGCF_online;
|
||||||
|
|
||||||
|
rc = arch_vcpu_validate_guest_context(v, ctxt);
|
||||||
|
if ( rc )
|
||||||
|
{
|
||||||
|
free_vcpu_guest_context(ctxt);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = ctxt;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_common_cpu_on(register_t target_cpu, register_t entry_point,
|
||||||
|
register_t context_id)
|
||||||
|
{
|
||||||
|
struct vcpu *v;
|
||||||
|
struct domain *d = current->domain;
|
||||||
|
struct vcpu_guest_context *ctxt = NULL;
|
||||||
|
int rc;
|
||||||
|
bool is_thumb = entry_point & 1;
|
||||||
|
register_t vcpuid;
|
||||||
|
|
||||||
|
vcpuid = vaffinity_to_vcpuid(target_cpu);
|
||||||
|
|
||||||
|
if ( (v = domain_vcpu(d, vcpuid)) == NULL )
|
||||||
|
return PSCI_INVALID_PARAMETERS;
|
||||||
|
|
||||||
|
/* THUMB set is not allowed with 64-bit domain */
|
||||||
|
if ( is_64bit_domain(d) && is_thumb )
|
||||||
|
return PSCI_INVALID_ADDRESS;
|
||||||
|
|
||||||
|
if ( !test_bit(_VPF_down, &v->pause_flags) )
|
||||||
|
return PSCI_ALREADY_ON;
|
||||||
|
|
||||||
|
rc = vpsci_build_guest_context(v, entry_point, context_id, &ctxt);
|
||||||
|
if ( rc < 0 )
|
||||||
|
return PSCI_DENIED;
|
||||||
|
|
||||||
|
vgic_clear_pending_irqs(v);
|
||||||
|
|
||||||
domain_lock(d);
|
domain_lock(d);
|
||||||
rc = arch_set_info_guest(v, ctxt);
|
arch_vcpu_apply_guest_context(v, ctxt);
|
||||||
domain_unlock(d);
|
domain_unlock(d);
|
||||||
|
|
||||||
free_vcpu_guest_context(ctxt);
|
free_vcpu_guest_context(ctxt);
|
||||||
|
|
||||||
if ( rc < 0 )
|
|
||||||
return PSCI_DENIED;
|
|
||||||
|
|
||||||
vcpu_wake(v);
|
vcpu_wake(v);
|
||||||
|
|
||||||
return PSCI_SUCCESS;
|
return PSCI_SUCCESS;
|
||||||
@@ -197,6 +219,56 @@ static void do_psci_0_2_system_reset(void)
|
|||||||
domain_shutdown(d,SHUTDOWN_reboot);
|
domain_shutdown(d,SHUTDOWN_reboot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int32_t do_psci_1_0_system_suspend(register_t epoint, register_t cid)
|
||||||
|
{
|
||||||
|
int32_t rc;
|
||||||
|
struct vcpu_guest_context *ctxt;
|
||||||
|
struct vcpu *v;
|
||||||
|
struct domain *d = current->domain;
|
||||||
|
bool is_thumb = epoint & 1;
|
||||||
|
struct resume_info *rctx = &d->arch.resume_ctx;
|
||||||
|
|
||||||
|
/* THUMB set is not allowed with 64-bit domain */
|
||||||
|
if ( is_64bit_domain(d) && is_thumb )
|
||||||
|
return PSCI_INVALID_ADDRESS;
|
||||||
|
|
||||||
|
/* SYSTEM_SUSPEND is not supported for the hardware domain yet */
|
||||||
|
if ( is_hardware_domain(d) )
|
||||||
|
return PSCI_NOT_SUPPORTED;
|
||||||
|
|
||||||
|
/* Ensure that all CPUs other than the calling one are offline */
|
||||||
|
domain_lock(d);
|
||||||
|
for_each_vcpu ( d, v )
|
||||||
|
{
|
||||||
|
if ( v != current && is_vcpu_online(v) )
|
||||||
|
{
|
||||||
|
domain_unlock(d);
|
||||||
|
return PSCI_DENIED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
domain_unlock(d);
|
||||||
|
|
||||||
|
rc = vpsci_build_guest_context(current, epoint, cid, &ctxt);
|
||||||
|
if ( rc )
|
||||||
|
return PSCI_DENIED;
|
||||||
|
|
||||||
|
rc = domain_shutdown(d, SHUTDOWN_suspend);
|
||||||
|
if ( rc )
|
||||||
|
{
|
||||||
|
free_vcpu_guest_context(ctxt);
|
||||||
|
return PSCI_DENIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
rctx->ctxt = ctxt;
|
||||||
|
rctx->wake_cpu = current;
|
||||||
|
|
||||||
|
gprintk(XENLOG_DEBUG,
|
||||||
|
"SYSTEM_SUSPEND requested, epoint=%#"PRIregister", cid=%#"PRIregister"\n",
|
||||||
|
epoint, cid);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
static int32_t do_psci_1_0_features(uint32_t psci_func_id)
|
static int32_t do_psci_1_0_features(uint32_t psci_func_id)
|
||||||
{
|
{
|
||||||
/* /!\ Ordered by function ID and not name */
|
/* /!\ Ordered by function ID and not name */
|
||||||
@@ -216,6 +288,9 @@ static int32_t do_psci_1_0_features(uint32_t psci_func_id)
|
|||||||
case PSCI_1_0_FN32_PSCI_FEATURES:
|
case PSCI_1_0_FN32_PSCI_FEATURES:
|
||||||
case ARM_SMCCC_VERSION_FID:
|
case ARM_SMCCC_VERSION_FID:
|
||||||
return 0;
|
return 0;
|
||||||
|
case PSCI_1_0_FN32_SYSTEM_SUSPEND:
|
||||||
|
case PSCI_1_0_FN64_SYSTEM_SUSPEND:
|
||||||
|
return is_hardware_domain(current->domain) ? PSCI_NOT_SUPPORTED : 0;
|
||||||
default:
|
default:
|
||||||
return PSCI_NOT_SUPPORTED;
|
return PSCI_NOT_SUPPORTED;
|
||||||
}
|
}
|
||||||
@@ -344,6 +419,23 @@ bool do_vpsci_0_2_call(struct cpu_user_regs *regs, uint32_t fid)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case PSCI_1_0_FN32_SYSTEM_SUSPEND:
|
||||||
|
case PSCI_1_0_FN64_SYSTEM_SUSPEND:
|
||||||
|
{
|
||||||
|
register_t epoint = PSCI_ARG(regs, 1);
|
||||||
|
register_t cid = PSCI_ARG(regs, 2);
|
||||||
|
|
||||||
|
if ( fid == PSCI_1_0_FN32_SYSTEM_SUSPEND )
|
||||||
|
{
|
||||||
|
epoint &= GENMASK(31, 0);
|
||||||
|
cid &= GENMASK(31, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
perfc_incr(vpsci_system_suspend);
|
||||||
|
PSCI_SET_RESULT(regs, do_psci_1_0_system_suspend(epoint, cid));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#include <xen/hypercall.h>
|
#include <xen/hypercall.h>
|
||||||
#include <xen/delay.h>
|
#include <xen/delay.h>
|
||||||
#include <xen/shutdown.h>
|
#include <xen/shutdown.h>
|
||||||
|
#include <xen/suspend.h>
|
||||||
#include <xen/percpu.h>
|
#include <xen/percpu.h>
|
||||||
#include <xen/multicall.h>
|
#include <xen/multicall.h>
|
||||||
#include <xen/rcupdate.h>
|
#include <xen/rcupdate.h>
|
||||||
@@ -1435,6 +1436,9 @@ void domain_resume(struct domain *d)
|
|||||||
spin_lock(&d->shutdown_lock);
|
spin_lock(&d->shutdown_lock);
|
||||||
|
|
||||||
d->is_shutting_down = d->is_shut_down = 0;
|
d->is_shutting_down = d->is_shut_down = 0;
|
||||||
|
|
||||||
|
arch_domain_resume(d);
|
||||||
|
|
||||||
d->shutdown_code = SHUTDOWN_CODE_INVALID;
|
d->shutdown_code = SHUTDOWN_CODE_INVALID;
|
||||||
|
|
||||||
for_each_vcpu ( d, v )
|
for_each_vcpu ( d, v )
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
|
||||||
|
#ifndef XEN_SUSPEND_H
|
||||||
|
#define XEN_SUSPEND_H
|
||||||
|
|
||||||
|
#if __has_include(<asm/suspend.h>)
|
||||||
|
#include <asm/suspend.h>
|
||||||
|
#else
|
||||||
|
static inline void arch_domain_resume(struct domain *d) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* XEN_SUSPEND_H */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* mode: C
|
||||||
|
* c-file-style: "BSD"
|
||||||
|
* c-basic-offset: 4
|
||||||
|
* tab-width: 4
|
||||||
|
* indent-tabs-mode: nil
|
||||||
|
* End:
|
||||||
|
*/
|
||||||