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:
Mostafa Saleh
2024-12-12 18:03:44 +00:00
parent 561f9e3285
commit 2e3d19be3e
6 changed files with 105 additions and 1 deletions
+1
View File
@@ -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
+30
View File
@@ -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__ */
+1 -1
View File
@@ -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
+9
View File
@@ -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)
+47
View File
@@ -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;
}
+17
View File
@@ -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 */