BACKPORT: FROMLIST: KVM: arm64: Support power domains
Unfortunately, as power management is not widely standardized we have to work around that. One implementation we can support is HOST_HVC, where the host is in control of the power management and it notifies the hypervisor about the updates. This adds extra constraints to the IOMMUs, as they must reset to blocking DMA traffic to be able to use this PD interface. Unfortunately again, for SMMUv3 which is the only IOMMU currently supported, there is no architectural way to discover this, so we rely on enabling this driver when it fits the constraints, also the driver sets GBPA and assumes that the SMMU retains across power cycling. In the next patch SCMI support is added. Link: https://lore.kernel.org/all/20241212180423.1578358-21-smostafa@google.com/ Bug: 357781595 Bug: 384432312 Change-Id: I99e71fc44b67ca8e69a043f3d19e2f5c3c69206e Signed-off-by: Mostafa Saleh <smostafa@google.com> Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
This commit is contained in:
@@ -114,6 +114,7 @@ enum __kvm_host_smccc_func {
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_host_iommu_map_pages,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_host_iommu_unmap_pages,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_host_iommu_iova_to_phys,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_host_hvc_pd,
|
||||
|
||||
/*
|
||||
* Start of the dynamically registered hypercalls. Start a bit
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#define __ARM64_KVM_NVHE_PKVM_H__
|
||||
|
||||
#include <asm/kvm_pkvm.h>
|
||||
#include <kvm/power_domain.h>
|
||||
|
||||
#include <nvhe/gfp.h>
|
||||
#include <nvhe/spinlock.h>
|
||||
@@ -146,4 +147,33 @@ void pkvm_poison_pvmfw_pages(void);
|
||||
int pkvm_timer_init(void);
|
||||
void pkvm_udelay(unsigned long usecs);
|
||||
|
||||
#define MAX_POWER_DOMAINS 32
|
||||
|
||||
struct kvm_power_domain_ops {
|
||||
int (*power_on)(struct kvm_power_domain *pd);
|
||||
int (*power_off)(struct kvm_power_domain *pd);
|
||||
};
|
||||
|
||||
int pkvm_init_hvc_pd(struct kvm_power_domain *pd,
|
||||
const struct kvm_power_domain_ops *ops);
|
||||
|
||||
int pkvm_host_hvc_pd(u64 device_id, u64 on);
|
||||
|
||||
/*
|
||||
* Register a power domain. When the hypervisor catches power requests from the
|
||||
* host for this power domain, it calls the power ops with @pd as argument.
|
||||
*/
|
||||
static inline int pkvm_init_power_domain(struct kvm_power_domain *pd,
|
||||
const struct kvm_power_domain_ops *ops)
|
||||
{
|
||||
switch (pd->type) {
|
||||
case KVM_POWER_DOMAIN_NONE:
|
||||
return 0;
|
||||
case KVM_POWER_DOMAIN_HOST_HVC:
|
||||
return pkvm_init_hvc_pd(pd, ops);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __ARM64_KVM_NVHE_PKVM_H__ */
|
||||
|
||||
@@ -8,7 +8,7 @@ CFLAGS_switch.nvhe.o += -Wno-override-init
|
||||
hyp-obj-y := timer-sr.o sysreg-sr.o debug-sr.o switch.o tlb.o hyp-init.o host.o \
|
||||
hyp-main.o hyp-smp.o psci-relay.o alloc.o early_alloc.o page_alloc.o \
|
||||
cache.o setup.o mm.o mem_protect.o sys_regs.o pkvm.o stacktrace.o ffa.o \
|
||||
serial.o alloc_mgt.o iommu/iommu.o
|
||||
serial.o alloc_mgt.o iommu/iommu.o power/hvc.o
|
||||
hyp-obj-y += ../vgic-v3-sr.o ../aarch32.o ../vgic-v2-cpuif-proxy.o ../entry.o \
|
||||
../fpsimd.o ../hyp-entry.o ../exception.o ../pgtable.o
|
||||
hyp-obj-$(CONFIG_LIST_HARDENED) += list_debug.o
|
||||
|
||||
@@ -1682,6 +1682,14 @@ static void handle___pkvm_host_iommu_iova_to_phys(struct kvm_cpu_context *host_c
|
||||
cpu_reg(host_ctxt, 1) = kvm_iommu_iova_to_phys(domain, iova);
|
||||
}
|
||||
|
||||
static void handle___pkvm_host_hvc_pd(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
DECLARE_REG(u64, device_id, host_ctxt, 1);
|
||||
DECLARE_REG(u64, on, host_ctxt, 2);
|
||||
|
||||
cpu_reg(host_ctxt, 1) = pkvm_host_hvc_pd(device_id, on);
|
||||
}
|
||||
|
||||
typedef void (*hcall_t)(struct kvm_cpu_context *);
|
||||
|
||||
#define HANDLE_FUNC(x) [__KVM_HOST_SMCCC_FUNC_##x] = (hcall_t)handle_##x
|
||||
@@ -1746,6 +1754,7 @@ static const hcall_t host_hcall[] = {
|
||||
HANDLE_FUNC(__pkvm_host_iommu_map_pages),
|
||||
HANDLE_FUNC(__pkvm_host_iommu_unmap_pages),
|
||||
HANDLE_FUNC(__pkvm_host_iommu_iova_to_phys),
|
||||
HANDLE_FUNC(__pkvm_host_hvc_pd),
|
||||
};
|
||||
|
||||
static void handle_host_hcall(struct kvm_cpu_context *host_ctxt)
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2023 Google LLC
|
||||
* Author: Mostafa Saleh <smostafa@google.com>
|
||||
*/
|
||||
|
||||
#include <nvhe/pkvm.h>
|
||||
|
||||
struct hvc_power_domain {
|
||||
struct kvm_power_domain *pd;
|
||||
const struct kvm_power_domain_ops *ops;
|
||||
};
|
||||
|
||||
struct hvc_power_domain handlers[MAX_POWER_DOMAINS];
|
||||
|
||||
int pkvm_init_hvc_pd(struct kvm_power_domain *pd,
|
||||
const struct kvm_power_domain_ops *ops)
|
||||
{
|
||||
if (pd->device_id >= MAX_POWER_DOMAINS)
|
||||
return -E2BIG;
|
||||
|
||||
handlers[pd->device_id].ops = ops;
|
||||
handlers[pd->device_id].pd = pd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pkvm_host_hvc_pd(u64 device_id, u64 on)
|
||||
{
|
||||
struct hvc_power_domain *pd;
|
||||
|
||||
if (device_id >= MAX_POWER_DOMAINS)
|
||||
return -E2BIG;
|
||||
|
||||
device_id = array_index_nospec(device_id, MAX_POWER_DOMAINS);
|
||||
pd = &handlers[device_id];
|
||||
|
||||
if (!pd->ops)
|
||||
return -ENOENT;
|
||||
|
||||
if (on)
|
||||
pd->ops->power_on(pd->pd);
|
||||
else
|
||||
pd->ops->power_off(pd->pd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __KVM_POWER_DOMAIN_H
|
||||
#define __KVM_POWER_DOMAIN_H
|
||||
|
||||
enum kvm_power_domain_type {
|
||||
KVM_POWER_DOMAIN_NONE,
|
||||
KVM_POWER_DOMAIN_HOST_HVC,
|
||||
};
|
||||
|
||||
struct kvm_power_domain {
|
||||
enum kvm_power_domain_type type;
|
||||
union {
|
||||
u64 device_id; /* HOST_HVC device ID*/
|
||||
};
|
||||
};
|
||||
|
||||
#endif /* __KVM_POWER_DOMAIN_H */
|
||||
Reference in New Issue
Block a user