@@ -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
@@ -1674,6 +1674,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
@@ -1738,6 +1746,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)
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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 */