[PATCH v2 13/13] KVM: arm64: Implement HVC interface for ITS emulation setup
Sebastian Ene
sebastianene at google.com
Fri Aug 7 09:43:23 PDT 2026
Introduce a new HVC to allow the host to trigger the ITS emulation
setup. Use the introduced API in the GIC ITS driver to call the driver
to lock the ITS before pKVM finalize and to prepare for emulation setup.
On the return path from the pKVM finalize, call into the driver to
release the ITS locks which performs a switch in the driver to use a
different command queue and a different set of level-1 indirect tables.
Allocate memory that will be used by the emulation to track the internal
state and send the snapshot state from the driver.
Replace the initial "trap-and-forward" MMIO handler with a full-featured
emulation handler.
Signed-off-by: Sebastian Ene <sebastianene at google.com>
---
arch/arm64/include/asm/kvm_asm.h | 1 +
arch/arm64/include/asm/kvm_pkvm.h | 4 ++--
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 16 ++++++++++++++++
arch/arm64/kvm/hyp/nvhe/its_emulate.c | 4 ++--
arch/arm64/kvm/pkvm.c | 26 ++++++++++++++++++++++++--
5 files changed, 45 insertions(+), 6 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
index 043495f7fc78..fcb2871b8a86 100644
--- a/arch/arm64/include/asm/kvm_asm.h
+++ b/arch/arm64/include/asm/kvm_asm.h
@@ -114,6 +114,7 @@ enum __kvm_host_smccc_func {
__KVM_HOST_SMCCC_FUNC___pkvm_vcpu_load,
__KVM_HOST_SMCCC_FUNC___pkvm_vcpu_put,
__KVM_HOST_SMCCC_FUNC___pkvm_tlb_flush_vmid,
+ __KVM_HOST_SMCCC_FUNC___pkvm_its_emulate_setup,
MARKER(__KVM_HOST_SMCCC_FUNC_MAX)
};
diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h
index 78597210a53c..cc89e2bde468 100644
--- a/arch/arm64/include/asm/kvm_pkvm.h
+++ b/arch/arm64/include/asm/kvm_pkvm.h
@@ -32,8 +32,8 @@ struct pkvm_protected_reg {
extern struct pkvm_protected_reg kvm_nvhe_sym(pkvm_protected_regs)[];
extern unsigned int kvm_nvhe_sym(num_protected_reg);
-extern void kvm_nvhe_sym(its_emulate_forward_req)(struct pkvm_protected_reg *region, u64 offset,
- bool write, u64 *reg, u8 reg_size);
+extern void kvm_nvhe_sym(pkvm_its_emulate_handler)(struct pkvm_protected_reg *region, u64 offset,
+ bool write, u64 *reg, u8 reg_size);
int pkvm_init_host_vm(struct kvm *kvm, unsigned long type);
int pkvm_create_hyp_vm(struct kvm *kvm);
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index d3df96ed8ba4..ad57b2076eee 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -16,6 +16,7 @@
#include <asm/kvm_mmu.h>
#include <nvhe/ffa.h>
+#include <nvhe/its_emulate.h>
#include <nvhe/mem_protect.h>
#include <nvhe/mm.h>
#include <nvhe/pkvm.h>
@@ -705,6 +706,20 @@ static void handle___vgic_v5_restore_vmcr_apr(struct kvm_cpu_context *host_ctxt)
__vgic_v5_restore_vmcr_apr(kern_hyp_va(cpu_if));
}
+static void handle___pkvm_its_emulate_setup(struct kvm_cpu_context *host_ctxt)
+{
+ DECLARE_REG(phys_addr_t, dev_addr, host_ctxt, 1);
+ DECLARE_REG(struct its_host_state *, host_state, host_ctxt, 2);
+ DECLARE_REG(void *, priv_state, host_ctxt, 3);
+ DECLARE_REG(size_t, priv_state_num_pages, host_ctxt, 4);
+
+ if (!is_protected_kvm_enabled())
+ return;
+
+ cpu_reg(host_ctxt, 1) = pkvm_its_emulate_setup(dev_addr, host_state, priv_state,
+ priv_state_num_pages);
+}
+
typedef void (*hcall_t)(struct kvm_cpu_context *);
#define HANDLE_FUNC(x) [__KVM_HOST_SMCCC_FUNC_##x] = (hcall_t)handle_##x
@@ -762,6 +777,7 @@ static const hcall_t host_hcall[] = {
HANDLE_FUNC(__pkvm_vcpu_load),
HANDLE_FUNC(__pkvm_vcpu_put),
HANDLE_FUNC(__pkvm_tlb_flush_vmid),
+ HANDLE_FUNC(__pkvm_its_emulate_setup),
};
static void handle_host_hcall(struct kvm_cpu_context *host_ctxt)
diff --git a/arch/arm64/kvm/hyp/nvhe/its_emulate.c b/arch/arm64/kvm/hyp/nvhe/its_emulate.c
index 82dc60dcde68..8c8acaee4d2b 100644
--- a/arch/arm64/kvm/hyp/nvhe/its_emulate.c
+++ b/arch/arm64/kvm/hyp/nvhe/its_emulate.c
@@ -6,8 +6,8 @@
#include <linux/irqchip/arm-gic-v3.h>
-void its_emulate_forward_req(struct pkvm_protected_reg *region, u64 offset, bool write, u64 *reg,
- u8 reg_size)
+static void its_emulate_forward_req(struct pkvm_protected_reg *region, u64 offset, bool write,
+ u64 *reg, u8 reg_size)
{
void __iomem *addr = __hyp_va(PFN_PHYS(region->pfn) + offset);
diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
index 4bfffbedac4c..a9ceb9ffe6a4 100644
--- a/arch/arm64/kvm/pkvm.c
+++ b/arch/arm64/kvm/pkvm.c
@@ -71,7 +71,7 @@ static int __init register_its_emulated_region(void)
*/
kvm_nvhe_sym(pkvm_protected_regs)[i].pfn = PHYS_PFN(res.start);
kvm_nvhe_sym(pkvm_protected_regs)[i].cb =
- lm_alias(&kvm_nvhe_sym(its_emulate_forward_req));
+ lm_alias(&kvm_nvhe_sym(pkvm_its_emulate_handler));
kvm_nvhe_sym(pkvm_protected_regs)[i].nr_pages =
PFN_DOWN(min_t(u64, resource_size(&res), PAGE_ALIGN_DOWN(GITS_TRANSLATER)));
@@ -312,8 +312,28 @@ static void __init _kvm_host_prot_finalize(void *arg)
WRITE_ONCE(*err, -EINVAL);
}
+#define ITS_PAGES (2UL)
+
+static int pkvm_init_its_emulation(phys_addr_t dev_addr, struct its_host_state *host)
+{
+ size_t priv_state_sz = ITS_PAGES << PAGE_SHIFT;
+ void *priv_state;
+ int ret;
+
+ priv_state = alloc_pages_exact(priv_state_sz, GFP_ATOMIC);
+ if (!priv_state)
+ return -ENOMEM;
+
+ ret = kvm_call_hyp_nvhe(__pkvm_its_emulate_setup, dev_addr, host, priv_state, ITS_PAGES);
+ if (ret)
+ free_pages_exact(priv_state, priv_state_sz);
+
+ return ret;
+}
+
static int __init pkvm_drop_host_privileges(void)
{
+ unsigned long its_flags;
int ret = 0;
/*
@@ -321,8 +341,10 @@ static int __init pkvm_drop_host_privileges(void)
* once the host stage 2 is installed.
*/
static_branch_enable(&kvm_protected_mode_initialized);
+
+ its_emulate_acquire_locks(&its_flags);
on_each_cpu(_kvm_host_prot_finalize, &ret, 1);
- return ret;
+ return its_emulate_release_locks(ret, &its_flags, pkvm_init_its_emulation);
}
static int __init finalize_pkvm(void)
--
2.55.0.654.g21b8a5bc05-goog
More information about the linux-arm-kernel
mailing list