From: Vincent Donnefort <vdonnefort@google.com>
To: maz@kernel.org, oupton@kernel.org, kvmarm@lists.linux.dev,
linux-arm-kernel@lists.infradead.org
Cc: joey.gouly@arm.com, seiden@linux.ibm.com, suzuki.poulose@arm.com,
yuzenghui@huawei.com, catalin.marinas@arm.com, will@kernel.org,
kernel-team@android.com, fuad.tabba@linux.dev,
qperret@google.com, weilin.chang@arm.com,
Vincent Donnefort <vdonnefort@google.com>
Subject: [PATCH v2 18/22] KVM: arm64: Add __pkvm_host_split_guest HVC
Date: Fri, 11 Sep 2026 14:50:49 +0100 [thread overview]
Message-ID: <20260911135053.146435-19-vdonnefort@google.com> (raw)
In-Reply-To: <20260911135053.146435-1-vdonnefort@google.com>
This HVC allows the host to break an existing block in a protected VM. This
will later allow the guest to share a sub-region of an existing
huge-mapping with the host.
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
---
arch/arm64/include/asm/kvm_asm.h | 1 +
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 1 +
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 22 +++
arch/arm64/kvm/hyp/nvhe/mem_protect.c | 127 ++++++++++++++++--
4 files changed, 138 insertions(+), 13 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
index ec59f0fe9374..a67d171c86d0 100644
--- a/arch/arm64/include/asm/kvm_asm.h
+++ b/arch/arm64/include/asm/kvm_asm.h
@@ -105,6 +105,7 @@ enum __kvm_host_smccc_func {
__KVM_HOST_SMCCC_FUNC___pkvm_host_wrprotect_guest,
__KVM_HOST_SMCCC_FUNC___pkvm_host_test_clear_young_guest,
__KVM_HOST_SMCCC_FUNC___pkvm_host_mkyoung_guest,
+ __KVM_HOST_SMCCC_FUNC___pkvm_host_split_guest,
__KVM_HOST_SMCCC_FUNC___pkvm_reserve_vm,
__KVM_HOST_SMCCC_FUNC___pkvm_unreserve_vm,
__KVM_HOST_SMCCC_FUNC___pkvm_init_vm,
diff --git a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
index 7904a95ab8ac..f9604f9a92dc 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
@@ -50,6 +50,7 @@ int __pkvm_host_relax_perms_guest(u64 gfn, struct pkvm_hyp_vcpu *vcpu, enum kvm_
int __pkvm_host_wrprotect_guest(u64 gfn, u64 nr_pages, struct pkvm_hyp_vm *hyp_vm);
int __pkvm_host_test_clear_young_guest(u64 gfn, u64 nr_pages, bool mkold, struct pkvm_hyp_vm *vm);
int __pkvm_host_mkyoung_guest(u64 gfn, struct pkvm_hyp_vcpu *vcpu);
+int __pkvm_host_split_guest(u64 gfn, u64 nr_pages, struct pkvm_hyp_vcpu *vcpu);
bool addr_is_memory(phys_addr_t phys);
int host_stage2_idmap_locked(phys_addr_t addr, u64 size, enum kvm_pgtable_prot prot);
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index 7ef7da69f48b..cc0e1061d584 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -540,6 +540,27 @@ static void handle___pkvm_host_mkyoung_guest(struct kvm_cpu_context *host_ctxt)
cpu_reg(host_ctxt, 1) = ret;
}
+static void handle___pkvm_host_split_guest(struct kvm_cpu_context *host_ctxt)
+{
+ DECLARE_REG(u64, gfn, host_ctxt, 1);
+ DECLARE_REG(u64, nr_pages, host_ctxt, 2);
+ struct pkvm_hyp_vcpu *hyp_vcpu;
+ int ret = -EINVAL;
+
+ hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
+ if (!hyp_vcpu || !pkvm_hyp_vcpu_is_protected(hyp_vcpu))
+ goto out;
+
+ ret = pkvm_refill_memcache(hyp_vcpu);
+ if (ret)
+ goto out;
+
+ ret = __pkvm_host_split_guest(gfn, nr_pages, hyp_vcpu);
+
+out:
+ cpu_reg(host_ctxt, 1) = ret;
+}
+
static void handle___kvm_adjust_pc(struct kvm_cpu_context *host_ctxt)
{
DECLARE_REG(struct kvm_vcpu *, vcpu, host_ctxt, 1);
@@ -958,6 +979,7 @@ static const hcall_t host_hcall[] = {
HANDLE_FUNC(__pkvm_host_wrprotect_guest),
HANDLE_FUNC(__pkvm_host_test_clear_young_guest),
HANDLE_FUNC(__pkvm_host_mkyoung_guest),
+ HANDLE_FUNC(__pkvm_host_split_guest),
HANDLE_FUNC(__pkvm_reserve_vm),
HANDLE_FUNC(__pkvm_unreserve_vm),
HANDLE_FUNC(__pkvm_init_vm),
diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
index 2e8c241f76c9..f4943c2a237f 100644
--- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c
+++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
@@ -578,6 +578,12 @@ static void __host_update_page_state(phys_addr_t addr, u64 size, enum pkvm_page_
#define KVM_HOST_DONATION_PTE_OWNER_MASK GENMASK(3, 1)
#define KVM_HOST_DONATION_PTE_EXTRA_MASK GENMASK(59, 4)
+static kvm_pte_t host_stage2_encode_annotation(u8 owner_id, u64 meta)
+{
+ return FIELD_PREP(KVM_HOST_DONATION_PTE_OWNER_MASK, owner_id) |
+ FIELD_PREP(KVM_HOST_DONATION_PTE_EXTRA_MASK, meta);
+}
+
static int host_stage2_set_owner_metadata_locked(phys_addr_t addr, u64 size,
u8 owner_id, u64 meta)
{
@@ -596,8 +602,7 @@ static int host_stage2_set_owner_metadata_locked(phys_addr_t addr, u64 size,
if (!FIELD_FIT(KVM_HOST_DONATION_PTE_EXTRA_MASK, meta))
return -EINVAL;
- annotation = FIELD_PREP(KVM_HOST_DONATION_PTE_OWNER_MASK, owner_id) |
- FIELD_PREP(KVM_HOST_DONATION_PTE_EXTRA_MASK, meta);
+ annotation = host_stage2_encode_annotation(owner_id, meta);
ret = host_stage2_try(kvm_pgtable_stage2_annotate, &host_mmu.pgt,
addr, size, &host_s2_pool,
KVM_HOST_INVALID_PTE_TYPE_DONATION, annotation);
@@ -649,10 +654,8 @@ static u64 host_stage2_encode_gfn_meta(struct pkvm_hyp_vm *vm, u64 gfn)
FIELD_PREP(KVM_HOST_PTE_OWNER_GUEST_GFN_MASK, gfn);
}
-static int host_stage2_decode_gfn_meta(kvm_pte_t pte, struct pkvm_hyp_vm **vm,
- u64 *gfn)
+static int host_stage2_decode_gfn_meta(kvm_pte_t pte, pkvm_handle_t *handle, u64 *gfn)
{
- pkvm_handle_t handle;
u64 meta;
if (WARN_ON(kvm_pte_valid(pte)))
@@ -667,17 +670,59 @@ static int host_stage2_decode_gfn_meta(kvm_pte_t pte, struct pkvm_hyp_vm **vm,
return -EPERM;
meta = FIELD_GET(KVM_HOST_DONATION_PTE_EXTRA_MASK, pte);
- handle = FIELD_GET(KVM_HOST_PTE_OWNER_GUEST_HANDLE_MASK, meta);
- *vm = get_vm_by_handle(handle);
- if (!*vm) {
- /* We probably raced with teardown; try again */
- return -EAGAIN;
- }
-
+ *handle = FIELD_GET(KVM_HOST_PTE_OWNER_GUEST_HANDLE_MASK, meta);
*gfn = FIELD_GET(KVM_HOST_PTE_OWNER_GUEST_GFN_MASK, meta);
return 0;
}
+static int host_stage2_split_gfn_meta(phys_addr_t phys, u64 ipa, u64 size, struct pkvm_hyp_vm *vm)
+{
+ struct kvm_pgtable_mm_ops *mm_ops = &host_mmu.mm_ops;
+ struct kvm_pgtable *pgt = &host_mmu.pgt;
+ kvm_pte_t pte, *table, *ptep;
+ pkvm_handle_t handle;
+ s8 level;
+ u64 gfn;
+ int ret;
+
+ ret = kvm_pgtable_get_leaf(pgt, phys, &pte, &level);
+ if (ret)
+ return ret;
+
+ if (kvm_granule_size(level) != size)
+ return -EINVAL;
+
+ ret = host_stage2_decode_gfn_meta(pte, &handle, &gfn);
+ if (ret)
+ return ret;
+
+ if (handle != vm->kvm.arch.pkvm.handle || gfn != (ipa >> PAGE_SHIFT))
+ return -EINVAL;
+
+ table = mm_ops->zalloc_page(&host_s2_pool);
+ if (!table)
+ return -ENOMEM;
+
+ ptep = table;
+ while (ptep < table + PTRS_PER_PTE) {
+ u64 meta = host_stage2_encode_gfn_meta(vm, gfn);
+ kvm_pte_t annotation = host_stage2_encode_annotation(PKVM_ID_GUEST, meta);
+
+ *ptep = FIELD_PREP(KVM_INVALID_PTE_TYPE_MASK, KVM_HOST_INVALID_PTE_TYPE_DONATION) |
+ annotation;
+
+ mm_ops->get_page(ptep);
+ ptep++;
+ gfn++;
+ }
+
+ ret = kvm_pgtable_stage2_table_install(pgt, phys, size, table);
+ if (ret)
+ kvm_pgtable_stage2_free_unlinked(mm_ops, table, level);
+
+ return ret;
+}
+
static bool host_stage2_force_pte_cb(u64 addr, u64 end, enum kvm_pgtable_prot prot)
{
/*
@@ -1328,6 +1373,7 @@ static int host_stage2_get_guest_info(phys_addr_t phys, u64 *size, struct pkvm_h
u64 *gfn)
{
enum pkvm_page_state state;
+ pkvm_handle_t handle;
kvm_pte_t pte;
s8 level;
int ret;
@@ -1356,10 +1402,16 @@ static int host_stage2_get_guest_info(phys_addr_t phys, u64 *size, struct pkvm_h
if (level < KVM_PGTABLE_LAST_LEVEL - 1)
return -E2BIG;
- ret = host_stage2_decode_gfn_meta(pte, vm, gfn);
+ ret = host_stage2_decode_gfn_meta(pte, &handle, gfn);
if (ret)
return ret;
+ *vm = get_vm_by_handle(handle);
+ if (!*vm) {
+ /* We probably raced with teardown; try again */
+ return -EAGAIN;
+ }
+
*size = kvm_granule_size(level);
return 0;
@@ -1755,6 +1807,55 @@ int __pkvm_host_mkyoung_guest(u64 gfn, struct pkvm_hyp_vcpu *vcpu)
return 0;
}
+int __pkvm_host_split_guest(u64 gfn, u64 nr_pages, struct pkvm_hyp_vcpu *vcpu)
+{
+ struct kvm_hyp_memcache *mc = &vcpu->vcpu.arch.pkvm_memcache;
+ struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu);
+ u64 size = nr_pages << PAGE_SHIFT;
+ u64 ipa = hyp_pfn_to_phys(gfn);
+ kvm_pte_t pte, *table;
+ phys_addr_t phys;
+ s8 level;
+ int ret;
+
+ if (!vcpu->vcpu.arch.pkvm_memcache.nr_pages)
+ return -ENOMEM;
+
+ if (size != PMD_SIZE || !IS_ALIGNED(ipa, PMD_SIZE))
+ return -EINVAL;
+
+ level = KVM_PGTABLE_LAST_LEVEL - 1;
+
+ host_lock_component();
+ guest_lock_component(vm);
+
+ ret = get_valid_guest_pte(vm, ipa, size, &pte, &phys);
+ if (ret)
+ goto unlock;
+
+ table = kvm_pgtable_stage2_create_unlinked(&vm->pgt, kvm_pte_to_phys(pte), level,
+ kvm_pgtable_stage2_pte_prot(pte),
+ mc, true);
+ if (IS_ERR(table)) {
+ ret = PTR_ERR(table);
+ goto unlock;
+ }
+
+ ret = host_stage2_split_gfn_meta(phys, ipa, size, vm);
+ if (ret) {
+ kvm_pgtable_stage2_free_unlinked(&vm->mm_ops, table, level);
+ goto unlock;
+ }
+
+ WARN_ON(kvm_pgtable_stage2_table_install(&vm->pgt, ipa, size, table));
+
+unlock:
+ guest_unlock_component(vm);
+ host_unlock_component();
+
+ return ret;
+}
+
#ifdef CONFIG_NVHE_EL2_DEBUG
struct pkvm_expected_state {
enum pkvm_page_state host;
--
2.55.0.1007.g17ff1f9808-goog
next prev parent reply other threads:[~2026-09-11 13:52 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-11 13:50 [PATCH v2 00/22] Huge mapping support for protected VMs Vincent Donnefort
2026-09-11 13:50 ` [PATCH v2 01/22] KVM: arm64: Prefault host stage-2 entries on block split Vincent Donnefort
2026-09-24 17:38 ` Mostafa Saleh
2026-09-25 15:02 ` Vincent Donnefort
2026-09-11 13:50 ` [PATCH v2 02/22] KVM: arm64: Propagate host stage-2 annotated " Vincent Donnefort
2026-09-11 13:50 ` [PATCH v2 03/22] KVM: arm64: Allow block-level stage-2 annotation Vincent Donnefort
2026-09-11 13:50 ` [PATCH v2 04/22] KVM: arm64: Use block-level annotations when setting up the host stage-2 Vincent Donnefort
2026-09-11 13:50 ` [PATCH v2 05/22] KVM: arm64: Make pKVM ownership selftest an HVC Vincent Donnefort
2026-09-11 14:14 ` sashiko-bot
2026-09-11 13:50 ` [PATCH v2 06/22] KVM: arm64: Add a range to __pkvm_host_share/unshare_hyp() Vincent Donnefort
2026-09-11 13:50 ` [PATCH v2 07/22] KVM: arm64: Add a range to __pkvm_host_donate_guest() Vincent Donnefort
2026-09-11 13:50 ` [PATCH v2 08/22] KVM: arm64: Add a range to hyp_poison_page() Vincent Donnefort
2026-09-11 13:50 ` [PATCH v2 09/22] KVM: arm64: Add a range to __pkvm_host_reclaim_guest() Vincent Donnefort
2026-09-11 13:50 ` [PATCH v2 10/22] KVM: arm64: Add a range to __pkvm_guest_share_host() Vincent Donnefort
2026-09-11 13:50 ` [PATCH v2 11/22] KVM: arm64: Add a range to __pkvm_guest_unshare_host() Vincent Donnefort
2026-09-11 13:50 ` [PATCH v2 12/22] KVM: arm64: Handle huge mappings in __pkvm_host_force_reclaim_page_guest() Vincent Donnefort
2026-09-11 13:50 ` [PATCH v2 13/22] KVM: arm64: Handle huge mappings in __pkvm_vcpu_in_poison_fault() Vincent Donnefort
2026-09-11 13:50 ` [PATCH v2 14/22] KVM: arm64: Add a range to pKVM ownership selftest Vincent Donnefort
2026-09-11 13:50 ` [PATCH v2 15/22] KVM: arm64: Warn on pKVM guest stage-2 block collapse Vincent Donnefort
2026-09-11 13:50 ` [PATCH v2 16/22] KVM: arm64: Add pkvm_hyp_req infrastructure Vincent Donnefort
2026-09-11 13:50 ` [PATCH v2 17/22] KVM: arm64: Introduce kvm_pgtable_stage2_table_install() Vincent Donnefort
2026-09-11 13:50 ` Vincent Donnefort [this message]
2026-09-11 13:50 ` [PATCH v2 19/22] KVM: arm64: Extend pKVM page ownership selftests to cover guest block split Vincent Donnefort
2026-09-11 13:50 ` [PATCH v2 20/22] KVM: arm64: Add PKVM_HYP_REQ_SPLIT Vincent Donnefort
2026-09-11 13:50 ` [PATCH v2 21/22] KVM: arm64: Raise PKVM_HYP_REQ_SPLIT on guest to host sharing Vincent Donnefort
2026-09-11 13:50 ` [PATCH v2 22/22] KVM: arm64: Stage-2 huge mappings for protected VMs Vincent Donnefort
2026-09-11 14:20 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260911135053.146435-19-vdonnefort@google.com \
--to=vdonnefort@google.com \
--cc=catalin.marinas@arm.com \
--cc=fuad.tabba@linux.dev \
--cc=joey.gouly@arm.com \
--cc=kernel-team@android.com \
--cc=kvmarm@lists.linux.dev \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=maz@kernel.org \
--cc=oupton@kernel.org \
--cc=qperret@google.com \
--cc=seiden@linux.ibm.com \
--cc=suzuki.poulose@arm.com \
--cc=weilin.chang@arm.com \
--cc=will@kernel.org \
--cc=yuzenghui@huawei.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.