From nobody Fri Sep 25 21:40:22 2026 Received: from mta0.migadu.com (out-12.mta0.migadu.com [91.218.175.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B74AD526A9A for ; Tue, 8 Sep 2026 11:07:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.12 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788865650; cv=none; b=sxo5QlhcJh6c1nE7QjhUgYG7LSLbZ2VnnA9GE0HhHPFFeVgwyQNC5py8jl84OosgCnevEVUxfhMztm2nTd85LxKUYqEYCZwKcXUtWLspCum6H8ux90CZM0JeJV5KppU+qSm7oakIzt0Z+YCalzi8zkbPj3nC92UUVhbOtV5ntms= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788865650; c=relaxed/simple; bh=DqhRUW75apnnRm/+NgVuUwPZB14lDLNVV5b0Gd4lk2M=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=AGuXY9G/qtRIVPVMiwDioizlya98uaMbfaFTg/X6V2POoLoWSHXbGb6jIesbL6wxO+4LGqyyr70wMNaHRNw5AGM+Fe/R0Xyx/I5TGU0b8vPfRB/2O5mWUJCGpRnkJnVQ0hrL2JPZWZXEpEmc1HdUBdtFWgKBwmMfTdHlTJarPHY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=FY3PH6e4; arc=none smtp.client-ip=91.218.175.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="FY3PH6e4" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=DqhRUW75apnnRm/+NgVuUwPZB14lDLNVV5b0Gd4lk2M=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1788865643; v=1; x=1789470443; b=FY3PH6e4/aYAD4gMScM9UVHyVreFsVQy0ehzn3Ul7lA4Ok8aMo5YJtREe2gK8WO3ZNWHPdVQ VqpQsEyfnPgMIofhh3IxjpYsazAxmONZvQrcw52uxHV0s8dHDHtgfY20aHO1kB1fO8MPyMimsBu v8tTj891DabCsbMH2koAMVSk= X-Envelope-To: linux-kernel@vger.kernel.org Received: by smtp.migadu.com with ESMTPS id b5834c5c726827b5; Tue, 08 Sep 2026 11:07:22 +0000 X-Mizu-Trace-ID: b5834c5c726827b5 X-Migadu-Flow: FLOW_OUT From: Fuad Tabba To: Marc Zyngier , Oliver Upton Cc: Will Deacon , Catalin Marinas , Joey Gouly , Suzuki K Poulose , Zenghui Yu , Steffen Eiden , Mark Rutland , Vincent Donnefort , Keir Fraser , Kalesh Singh , Quentin Perret , Hiroyuki Katsura , Fuad Tabba , stable@vger.kernel.org, kvmarm@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [PATCH 1/4] KVM: arm64: Transfer the hyp stack pages out of the host stage-2 Date: Tue, 8 Sep 2026 12:07:10 +0100 Message-Id: <20260908110713.1540304-2-fuad.tabba@linux.dev> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260908110713.1540304-1-fuad.tabba@linux.dev> References: <20260908110713.1540304-1-fuad.tabba@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" fix_host_ownership() walks only the linear-map alias of each memblock region, and the per-CPU hyp stack, mapped in the private VA range for its guard page, has none. Walk each stack's VA range with the same walker. Fixes: 1a919b17ef012 ("KVM: arm64: Add guard pages for pKVM (protected nVHE= ) hypervisor stack") Reported-by: Hiroyuki Katsura Cc: stable@vger.kernel.org Signed-off-by: Fuad Tabba Reviewed-by: Marc Zyngier Reviewed-by: Vincent Donnefort Tested-by: Vincent Donnefort --- arch/arm64/kvm/hyp/nvhe/setup.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm64/kvm/hyp/nvhe/setup.c b/arch/arm64/kvm/hyp/nvhe/setu= p.c index 75b00c3233102..362f2891cb32e 100644 --- a/arch/arm64/kvm/hyp/nvhe/setup.c +++ b/arch/arm64/kvm/hyp/nvhe/setup.c @@ -269,6 +269,16 @@ static int fix_host_ownership(void) return ret; } =20 + /* The stacks sit in the private VA range, not the linear map. */ + for (i =3D 0; i < hyp_nr_cpus; i++) { + struct kvm_nvhe_init_params *params =3D per_cpu_ptr(&kvm_init_params, i); + u64 start =3D params->stack_hyp_va - NVHE_STACK_SIZE; + + ret =3D kvm_pgtable_walk(&pkvm_pgtable, start, NVHE_STACK_SIZE, &walker); + if (ret) + return ret; + } + return 0; } =20 --=20 2.39.5 From nobody Fri Sep 25 21:40:22 2026 Received: from mta0.migadu.com (out-19.mta0.migadu.com [91.218.175.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BC41E5293EE for ; Tue, 8 Sep 2026 11:07:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.19 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788865653; cv=none; b=Zmm6YTDc/9TTCnQSu+Cn2xAEkkhnknHZNSXdghEUvPy74UfnC1Y2tOk1CPDM47qFUyVlh4aaMmHZcHJFJULMV/iTmHSTFZLYsIDkSWWvEy6PYusX/Qq+xRLWlrNKCapV9LNH8daUtNfqkoD9KgMdPECyHTALb1p0wi+gHjfLNdE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788865653; c=relaxed/simple; bh=7bNXYL8yUcqTH70nhL/AuRr3pORuUPvFqJ6xx9nB3rY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=R+wC+raS1fPeKMdSHR5uKiOIJZ+IkoMGZmB5yasRvzw0AW8SfjzHPtI1ExTvOtMpefunBUYghyJwL0kscJyki0NzEg3r34sTtMqAfppx1P2nCV7ivZZBO3NFSMfhQ+XKvv+Bq/6/FqOlvchXiYBElm2v+9jC8YesJeAi1VFwQg4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=LZjw2Nbu; arc=none smtp.client-ip=91.218.175.19 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="LZjw2Nbu" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=7bNXYL8yUcqTH70nhL/AuRr3pORuUPvFqJ6xx9nB3rY=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1788865648; v=1; x=1789470448; b=LZjw2NbugqnYGVCacADZeG65gwnpjhnxo6xTX4MLAiDbDtkzcw6shcHmuIQT2//PDYfxh6fP Yup2NQHhBKJzo6168eWCWjcYXng/uXtilm8kz3F7QaBc5Pv9e9iUZFNzBZwpkil9CADhrgCxwLP L+DHz1bzpTm6yLen09gZdN3U= X-Envelope-To: linux-kernel@vger.kernel.org Received: by smtp.migadu.com with ESMTPS id 50cb0b689ffa052e; Tue, 08 Sep 2026 11:07:27 +0000 X-Mizu-Trace-ID: 50cb0b689ffa052e X-Migadu-Flow: FLOW_OUT From: Fuad Tabba To: Marc Zyngier , Oliver Upton Cc: Will Deacon , Catalin Marinas , Joey Gouly , Suzuki K Poulose , Zenghui Yu , Steffen Eiden , Mark Rutland , Vincent Donnefort , Keir Fraser , Kalesh Singh , Quentin Perret , Hiroyuki Katsura , Fuad Tabba , stable@vger.kernel.org, kvmarm@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [PATCH 2/4] KVM: arm64: Match hyp text by physical address in fix_host_ownership() Date: Tue, 8 Sep 2026 12:07:11 +0100 Message-Id: <20260908110713.1540304-3-fuad.tabba@linux.dev> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260908110713.1540304-1-fuad.tabba@linux.dev> References: <20260908110713.1540304-1-fuad.tabba@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" On a non-hVHE host, fix_host_ownership_walker()'s test for PAGE_HYP_EXEC never matches: KVM_PGTABLE_PROT_UX is cleared at map time and only PX is reported on read-back. Hyp text is therefore donated rather than left read-only in the host stage-2, and the instruction dump in nvhe_hyp_panic_handler() reads a page the host has no access to. Match the text by physical address instead, in a helper a later patch reuses. A test on the permissions would leave any other executable mapping host-readable too. Fixes: 80cbfd7174f31 ("KVM: arm64: Honor UX/PX attributes for EL2 S1 mappin= gs") Signed-off-by: Fuad Tabba Reviewed-by: Marc Zyngier Reviewed-by: Vincent Donnefort Tested-by: Vincent Donnefort --- arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 1 + arch/arm64/kvm/hyp/nvhe/mem_protect.c | 8 ++++++++ arch/arm64/kvm/hyp/nvhe/setup.c | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h b/arch/arm64/kvm= /hyp/include/nvhe/mem_protect.h index 29935c7da1dec..cab27f7bd423a 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +++ b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h @@ -52,6 +52,7 @@ int __pkvm_host_test_clear_young_guest(u64 gfn, u64 nr_pa= ges, bool mkold, struct int __pkvm_host_mkyoung_guest(u64 gfn, struct pkvm_hyp_vcpu *vcpu); =20 bool addr_is_memory(phys_addr_t phys); +bool addr_is_hyp_text(phys_addr_t phys); int host_stage2_idmap_locked(phys_addr_t addr, u64 size, enum kvm_pgtable_= prot prot); int host_stage2_set_owner_locked(phys_addr_t addr, u64 size, u8 owner_id); int kvm_host_prepare_stage2(void *pgt_pool_base); diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvh= e/mem_protect.c index 39aa8911f62c1..d026f446bd8ef 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -450,6 +450,14 @@ bool addr_is_memory(phys_addr_t phys) return !!find_mem_range(phys, &range); } =20 +bool addr_is_hyp_text(phys_addr_t phys) +{ + phys_addr_t start =3D ALIGN_DOWN(__hyp_pa(__hyp_text_start), PAGE_SIZE); + phys_addr_t end =3D PAGE_ALIGN(__hyp_pa(__hyp_text_end)); + + return phys >=3D start && phys < end; +} + static bool is_in_mem_range(u64 addr, struct kvm_mem_range *range) { return range->start <=3D addr && addr < range->end; diff --git a/arch/arm64/kvm/hyp/nvhe/setup.c b/arch/arm64/kvm/hyp/nvhe/setu= p.c index 362f2891cb32e..bb667cd7080b4 100644 --- a/arch/arm64/kvm/hyp/nvhe/setup.c +++ b/arch/arm64/kvm/hyp/nvhe/setup.c @@ -217,7 +217,7 @@ static int fix_host_ownership_walker(const struct kvm_p= gtable_visit_ctx *ctx, case PKVM_PAGE_OWNED: set_hyp_state(page, PKVM_PAGE_OWNED); /* hyp text is RO in the host stage-2 to be inspected on panic. */ - if (prot =3D=3D PAGE_HYP_EXEC) { + if (addr_is_hyp_text(phys)) { set_host_state(page, PKVM_NOPAGE); return host_stage2_idmap_locked(phys, PAGE_SIZE, KVM_PGTABLE_PROT_R); } else { --=20 2.39.5 From nobody Fri Sep 25 21:40:22 2026 Received: from mta1.migadu.com (out-122.mta1.migadu.com [95.215.58.122]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8AD685293E9 for ; Tue, 8 Sep 2026 11:07:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.122 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788865657; cv=none; b=PYuX0TovLJ9mT1GoIW4390EoV2oUOZrX/AI0YZxVhw1ba0HJOF0LiEjgE64gh1j8gN8nieaxPq56oiiFeG9LxyLHCCoL6d+dhc8z/xoQnkwxiM7TtZegg0IS0UydOpoF9rlnhCYm4mKZB5qohgdn5nOevu7FhQmHK0LU4H8NzfE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788865657; c=relaxed/simple; bh=7jgjQnlj8gbwTTpqNz7Ycv0TSVqQ6IULxTwgTLGtkyI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=g6MDyBpdrambqzIF+dJ6f+N02Ta+kbfEeyuyJ/fP1GrMPRXR/qJCX2KnZ9PqyFNLzzLMmbOvIal9U8XZRgfhF5sHvcHaPu7vtRfzg7CIdIGKCRKZ/iL1VDLDAArcjsxDIUb7D4PQB3Lv3OU6qkmjFJmiJXLYdXUDxHgBwHqi2Kk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=wtVnzCoi; arc=none smtp.client-ip=95.215.58.122 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="wtVnzCoi" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=7jgjQnlj8gbwTTpqNz7Ycv0TSVqQ6IULxTwgTLGtkyI=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1788865653; v=1; x=1789470453; b=wtVnzCoiVkA0PBUwzT44/sHHt9z9WkdVgO2X863a6VQniPD93Mg6PzVPpG9EKU5WPcsgTJrW 058s4i4jkaIZz/akTDcMZ0pA1tFqPk80nSIktHhKEm29aFRND+wYBAlcNYQvwIhrtmcy4sYYmvI 0j5TOsTw/lLv15j6Z3+TUVtA= X-Envelope-To: linux-kernel@vger.kernel.org Received: by smtp.migadu.com with ESMTPS id 2a5986739231b1ca; Tue, 08 Sep 2026 11:07:32 +0000 X-Mizu-Trace-ID: 2a5986739231b1ca X-Migadu-Flow: FLOW_OUT From: Fuad Tabba To: Marc Zyngier , Oliver Upton Cc: Will Deacon , Catalin Marinas , Joey Gouly , Suzuki K Poulose , Zenghui Yu , Steffen Eiden , Mark Rutland , Vincent Donnefort , Keir Fraser , Kalesh Singh , Quentin Perret , Hiroyuki Katsura , Fuad Tabba , stable@vger.kernel.org, kvmarm@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [PATCH 3/4] KVM: arm64: Move the private VA allocation cursor to __io_map_next Date: Tue, 8 Sep 2026 12:07:12 +0100 Message-Id: <20260908110713.1540304-4-fuad.tabba@linux.dev> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260908110713.1540304-1-fuad.tabba@linux.dev> References: <20260908110713.1540304-1-fuad.tabba@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" __io_map_base is the start of the private VA range only until the first allocation from it, after which it is the allocation cursor. Keep it as the start and move the cursor to __io_map_next, for the walk of the range the next patch adds. No functional change intended. Signed-off-by: Fuad Tabba Reviewed-by: Marc Zyngier Reviewed-by: Vincent Donnefort Tested-by: Vincent Donnefort --- arch/arm64/kvm/hyp/nvhe/mm.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/arch/arm64/kvm/hyp/nvhe/mm.c b/arch/arm64/kvm/hyp/nvhe/mm.c index 3b0bee496bffb..422ee57be9560 100644 --- a/arch/arm64/kvm/hyp/nvhe/mm.c +++ b/arch/arm64/kvm/hyp/nvhe/mm.c @@ -25,6 +25,7 @@ struct memblock_region hyp_memory[HYP_MEMBLOCK_REGIONS]; unsigned int hyp_memblock_nr; =20 static u64 __io_map_base; +static u64 __io_map_next; =20 struct hyp_fixmap_slot { u64 addr; @@ -50,7 +51,7 @@ static int __pkvm_alloc_private_va_range(unsigned long st= art, size_t size) =20 hyp_assert_lock_held(&pkvm_pgd_lock); =20 - if (!start || start < __io_map_base) + if (!start || start < __io_map_next) return -EINVAL; =20 /* The allocated size is always a multiple of PAGE_SIZE */ @@ -60,7 +61,7 @@ static int __pkvm_alloc_private_va_range(unsigned long st= art, size_t size) if (cur > __hyp_vmemmap) return -ENOMEM; =20 - __io_map_base =3D cur; + __io_map_next =3D cur; =20 return 0; } @@ -70,7 +71,7 @@ static int __pkvm_alloc_private_va_range(unsigned long st= art, size_t size) * @size: The size of the VA range to reserve. * @haddr: The hypervisor virtual start address of the allocation. * - * The private virtual address (VA) range is allocated above __io_map_base + * The private virtual address (VA) range is allocated above __io_map_next * and aligned based on the order of @size. * * Return: 0 on success or negative error code on failure. @@ -81,7 +82,7 @@ int pkvm_alloc_private_va_range(size_t size, unsigned lon= g *haddr) int ret; =20 hyp_spin_lock(&pkvm_pgd_lock); - addr =3D __io_map_base; + addr =3D __io_map_next; ret =3D __pkvm_alloc_private_va_range(addr, size); hyp_spin_unlock(&pkvm_pgd_lock); =20 @@ -341,7 +342,7 @@ static int create_fixblock(void) return -EINVAL; =20 hyp_spin_lock(&pkvm_pgd_lock); - addr =3D ALIGN(__io_map_base, PMD_SIZE); + addr =3D ALIGN(__io_map_next, PMD_SIZE); ret =3D __pkvm_alloc_private_va_range(addr, PMD_SIZE); if (ret) goto unlock; @@ -426,6 +427,7 @@ int hyp_create_idmap(u32 hyp_va_bits) */ __io_map_base =3D start & BIT(hyp_va_bits - 2); __io_map_base ^=3D BIT(hyp_va_bits - 2); + __io_map_next =3D __io_map_base; __hyp_vmemmap =3D __io_map_base | BIT(hyp_va_bits - 3); =20 return __pkvm_create_mappings(start, end - start, start, PAGE_HYP_EXEC); @@ -433,19 +435,19 @@ int hyp_create_idmap(u32 hyp_va_bits) =20 int pkvm_create_stack(phys_addr_t phys, unsigned long *haddr) { - unsigned long addr, prev_base; + unsigned long addr, prev_next; size_t size; int ret; =20 hyp_spin_lock(&pkvm_pgd_lock); =20 - prev_base =3D __io_map_base; + prev_next =3D __io_map_next; /* * Efficient stack verification using the NVHE_STACK_SHIFT bit implies * an alignment of our allocation on the order of the size. */ size =3D NVHE_STACK_SIZE * 2; - addr =3D ALIGN(__io_map_base, size); + addr =3D ALIGN(__io_map_next, size); =20 ret =3D __pkvm_alloc_private_va_range(addr, size); if (!ret) { @@ -461,7 +463,7 @@ int pkvm_create_stack(phys_addr_t phys, unsigned long *= haddr) ret =3D kvm_pgtable_hyp_map(&pkvm_pgtable, addr + NVHE_STACK_SIZE, NVHE_STACK_SIZE, phys, PAGE_HYP); if (ret) - __io_map_base =3D prev_base; + __io_map_next =3D prev_next; } hyp_spin_unlock(&pkvm_pgd_lock); =20 --=20 2.39.5 From nobody Fri Sep 25 21:40:22 2026 Received: from mta1.migadu.com (out-128.mta1.migadu.com [95.215.58.128]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 627F351C35C for ; Tue, 8 Sep 2026 11:07:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.128 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788865660; cv=none; b=SKfCHqo7JPpPoPQeu/Mh/fetsuyCYYqJj8+iDCa1hAHKhaEiE70r+bKu0t/wx1bvVHC7M7DYBFrP4ssRwuLOHS0FSKrvYtmmPQVW3pGQVMMa+TqdZCpbAfTXqtIJ3xTV9+qI3794IgfCddaWacOGFaoAWwD4XZz7ZGPj522C/Qs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788865660; c=relaxed/simple; bh=n+vTJUIvsDxI7yNE//7ETqP96fI8JsmTAhqvehLBjCw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=CVV0pQ6YZNUrietHWzXJoeBjW1I8mc1PXJ2cb51m39vNe6hYCX2Xi2eb9ldohgf/W130BNKVZnRSrSiVb8MY9PH9J5VCQbrROCUlqrLj19+DG45+NMJHCv85x1tYHFb92jDFVPeifk+YZ/80KNcHf0w0XKVHCJpUCqgVcNHbkiI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=Fw9nXrdr; arc=none smtp.client-ip=95.215.58.128 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="Fw9nXrdr" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=n+vTJUIvsDxI7yNE//7ETqP96fI8JsmTAhqvehLBjCw=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1788865654; v=1; x=1789470454; b=Fw9nXrdrrkkKWAhzydgvKtfzedI2UdLMt6vCEmBAHhH9Z4/NCbrhcVPjLyNIkqJj2r5nGM+0 491VgUcPWtwtCkSNTxhBJ2ofBbWhVaTiRj95lOsSH9VXguZpBtkdTo/qCnL7PrI/qLsQDRTgljq LwI6xy4ImfumwNednqaCJXSg= X-Envelope-To: linux-kernel@vger.kernel.org Received: by smtp.migadu.com with ESMTPS id 5f1e715ccda4b9bd; Tue, 08 Sep 2026 11:07:34 +0000 X-Mizu-Trace-ID: 5f1e715ccda4b9bd X-Migadu-Flow: FLOW_OUT From: Fuad Tabba To: Marc Zyngier , Oliver Upton Cc: Will Deacon , Catalin Marinas , Joey Gouly , Suzuki K Poulose , Zenghui Yu , Steffen Eiden , Mark Rutland , Vincent Donnefort , Keir Fraser , Kalesh Singh , Quentin Perret , Hiroyuki Katsura , Fuad Tabba , stable@vger.kernel.org, kvmarm@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [PATCH 4/4] KVM: arm64: Check every private mapping is hyp-owned at pKVM init Date: Tue, 8 Sep 2026 12:07:13 +0100 Message-Id: <20260908110713.1540304-5-fuad.tabba@linux.dev> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260908110713.1540304-1-fuad.tabba@linux.dev> References: <20260908110713.1540304-1-fuad.tabba@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" fix_host_ownership() transfers only what it walks, so a hyp mapping outside the linear map is not manipulated by the walk. Walk the quarter of the VA space holding the private range and the vmemmap once the transfer is done, and fail init unless every valid leaf is hyp-owned: in the vmemmap when the page is memory, and in the host stage-2, where hyp text may instead be mapped without write access. A leaf that is not memory has no vmemmap entry and is checked against the host stage-2 alone. Hyp text is matched by physical address, since the only executable mapping in the range is the Spectre-v3a vectors, whose VA is a private allocation, and an executable mapping of anything else must not be host-readable. The vmemmap can be block-mapped, so the walker checks each page of a leaf. Suggested-by: Will Deacon Signed-off-by: Fuad Tabba Reviewed-by: Marc Zyngier Reviewed-by: Vincent Donnefort Tested-by: Vincent Donnefort --- arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 1 + arch/arm64/kvm/hyp/include/nvhe/mm.h | 1 + arch/arm64/kvm/hyp/nvhe/mem_protect.c | 12 ++++ arch/arm64/kvm/hyp/nvhe/mm.c | 59 +++++++++++++++++++ arch/arm64/kvm/hyp/nvhe/setup.c | 4 ++ 5 files changed, 77 insertions(+) diff --git a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h b/arch/arm64/kvm= /hyp/include/nvhe/mem_protect.h index cab27f7bd423a..ec85a95471207 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +++ b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h @@ -55,6 +55,7 @@ bool addr_is_memory(phys_addr_t phys); bool addr_is_hyp_text(phys_addr_t phys); int host_stage2_idmap_locked(phys_addr_t addr, u64 size, enum kvm_pgtable_= prot prot); int host_stage2_set_owner_locked(phys_addr_t addr, u64 size, u8 owner_id); +bool host_stage2_pte_is_hyp_owned(kvm_pte_t pte); int kvm_host_prepare_stage2(void *pgt_pool_base); int kvm_guest_prepare_stage2(struct pkvm_hyp_vm *vm, void *pgd); void kvm_guest_destroy_stage2(struct pkvm_hyp_vm *vm); diff --git a/arch/arm64/kvm/hyp/include/nvhe/mm.h b/arch/arm64/kvm/hyp/incl= ude/nvhe/mm.h index 6e83ce35c2f2e..31cae95ddb716 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/mm.h +++ b/arch/arm64/kvm/hyp/include/nvhe/mm.h @@ -29,6 +29,7 @@ int __pkvm_create_private_mapping(phys_addr_t phys, size_= t size, enum kvm_pgtable_prot prot, unsigned long *haddr); int pkvm_create_stack(phys_addr_t phys, unsigned long *haddr); +int pkvm_check_host_ownership(void); int pkvm_alloc_private_va_range(size_t size, unsigned long *haddr); =20 #endif /* __KVM_HYP_MM_H */ diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvh= e/mem_protect.c index d026f446bd8ef..a6a47c1e058b3 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -641,6 +641,18 @@ int host_stage2_set_owner_locked(phys_addr_t addr, u64= size, u8 owner_id) return ret; } =20 +bool host_stage2_pte_is_hyp_owned(kvm_pte_t pte) +{ + if (kvm_pte_valid(pte)) + return false; + + if (FIELD_GET(KVM_INVALID_PTE_TYPE_MASK, pte) !=3D + KVM_HOST_INVALID_PTE_TYPE_DONATION) + return false; + + return FIELD_GET(KVM_HOST_DONATION_PTE_OWNER_MASK, pte) =3D=3D PKVM_ID_HY= P; +} + #define KVM_HOST_PTE_OWNER_GUEST_HANDLE_MASK GENMASK(15, 0) /* We need 40 bits for the GFN to cover a 52-bit IPA with 4k pages and LPA= 2 */ #define KVM_HOST_PTE_OWNER_GUEST_GFN_MASK GENMASK(55, 16) diff --git a/arch/arm64/kvm/hyp/nvhe/mm.c b/arch/arm64/kvm/hyp/nvhe/mm.c index 422ee57be9560..29ab5ee9d57fc 100644 --- a/arch/arm64/kvm/hyp/nvhe/mm.c +++ b/arch/arm64/kvm/hyp/nvhe/mm.c @@ -472,6 +472,65 @@ int pkvm_create_stack(phys_addr_t phys, unsigned long = *haddr) return ret; } =20 +static int check_page_ownership(phys_addr_t phys) +{ + kvm_pte_t pte; + bool host_ok; + int ret; + + if (addr_is_memory(phys)) { + struct hyp_page *page =3D hyp_phys_to_page(phys); + + if (get_hyp_state(page) !=3D PKVM_PAGE_OWNED || + get_host_state(page) !=3D PKVM_NOPAGE) + return -EPERM; + } + + ret =3D kvm_pgtable_get_leaf(&host_mmu.pgt, phys, &pte, NULL); + if (ret) + return ret; + + /* Hyp text may stay host-readable, see fix_host_ownership_walker(). */ + if (kvm_pte_valid(pte) && addr_is_hyp_text(phys)) + host_ok =3D !(kvm_pgtable_stage2_pte_prot(pte) & KVM_PGTABLE_PROT_W); + else + host_ok =3D host_stage2_pte_is_hyp_owned(pte); + + return host_ok ? 0 : -EPERM; +} + +static int check_host_ownership_walker(const struct kvm_pgtable_visit_ctx = *ctx, + enum kvm_pgtable_walk_flags visit) +{ + phys_addr_t phys, end; + int ret; + + if (!kvm_pte_valid(ctx->old)) + return 0; + + phys =3D kvm_pte_to_phys(ctx->old); + end =3D phys + kvm_granule_size(ctx->level); + for (; phys < end; phys +=3D PAGE_SIZE) { + ret =3D check_page_ownership(phys); + if (ret) + return ret; + } + + return 0; +} + +int pkvm_check_host_ownership(void) +{ + struct kvm_pgtable_walker walker =3D { + .cb =3D check_host_ownership_walker, + .flags =3D KVM_PGTABLE_WALK_LEAF, + }; + + /* The private range and the vmemmap share one quarter of the VA space. */ + return kvm_pgtable_walk(&pkvm_pgtable, __io_map_base, + BIT(pkvm_pgtable.ia_bits - 2), &walker); +} + static void *admit_host_page(void *arg) { struct kvm_hyp_memcache *host_mc =3D arg; diff --git a/arch/arm64/kvm/hyp/nvhe/setup.c b/arch/arm64/kvm/hyp/nvhe/setu= p.c index bb667cd7080b4..45ac5f2ba4f7a 100644 --- a/arch/arm64/kvm/hyp/nvhe/setup.c +++ b/arch/arm64/kvm/hyp/nvhe/setup.c @@ -334,6 +334,10 @@ void __noreturn __pkvm_init_finalise(void) if (ret) goto out; =20 + ret =3D pkvm_check_host_ownership(); + if (ret) + goto out; + ret =3D hyp_ffa_init(ffa_proxy_pages); if (ret) goto out; --=20 2.39.5