All of lore.kernel.org
 help / color / mirror / Atom feed
From: Itaru Kitayama <itaru.kitayama@fujitsu.com>
To: Marc Zyngier <maz@kernel.org>, Oliver Upton <oupton@kernel.org>,
	 Joey Gouly <joey.gouly@arm.com>,
	Steffen Eiden <seiden@linux.ibm.com>,
	 Suzuki K Poulose <suzuki.poulose@arm.com>,
	 Zenghui Yu <yuzenghui@huawei.com>,
	Paolo Bonzini <pbonzini@redhat.com>,
	 Shuah Khan <shuah@kernel.org>
Cc: linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev,
	 kvm@vger.kernel.org, linux-kselftest@vger.kernel.org,
	 linux-kernel@vger.kernel.org,
	Itaru Kitayama <itaru.kitayama@fujitsu.com>
Subject: [PATCH] Enable stage 2 translation in L2
Date: Fri, 15 May 2026 10:48:05 +0900	[thread overview]
Message-ID: <20260515-enable-s2-hello_nested-v1-1-41f3faca1a08@fujitsu.com> (raw)

IPA size and start level are configurable at build time.

Signed-off-by: Itaru Kitayama <itaru.kitayama@fujitsu.com>
---
Enable stage 2 translation in L2, but keep stage 1 remain off
as Wei Lin prefers. Types are changed accordingly due to the
recent selftest-wide changes.
---
 tools/testing/selftests/kvm/arm64/hello_nested.c   |  11 +-
 tools/testing/selftests/kvm/include/arm64/nested.h |  38 ++-
 tools/testing/selftests/kvm/lib/arm64/hyp-entry.S  |   5 +
 tools/testing/selftests/kvm/lib/arm64/nested.c     | 279 ++++++++++++++++++++-
 4 files changed, 322 insertions(+), 11 deletions(-)

diff --git a/tools/testing/selftests/kvm/arm64/hello_nested.c b/tools/testing/selftests/kvm/arm64/hello_nested.c
index 69f4d8e750e2..1ac045894b89 100644
--- a/tools/testing/selftests/kvm/arm64/hello_nested.c
+++ b/tools/testing/selftests/kvm/arm64/hello_nested.c
@@ -18,9 +18,9 @@
 /*
  * TPIDR_EL2 is used to store vcpu id, so save and restore it.
  */
-static vm_paddr_t ucall_translate_to_gpa(void *gva)
+static gpa_t ucall_translate_to_gpa(void *gva)
 {
-	vm_paddr_t gpa;
+	gpa_t gpa;
 	u64 vcpu_id = read_sysreg(tpidr_el2);
 
 	GUEST_SYNC2(XLATE2GPA, gva);
@@ -50,7 +50,7 @@ static void guest_code(void)
 	struct vcpu vcpu;
 	struct hyp_data hyp_data;
 	int ret;
-	vm_paddr_t l2_pc, l2_stack_top;
+	gpa_t l2_pc, l2_stack_top;
 	/* force 16-byte alignment for the stack pointer */
 	u8 l2_stack[L2STACKSZ] __attribute__((aligned(16)));
 	u64 arg1, arg2;
@@ -92,7 +92,7 @@ int main(void)
 	struct kvm_vcpu *vcpu;
 	struct kvm_vm *vm;
 	struct ucall uc;
-	vm_paddr_t gpa;
+	gpa_t gpa;
 
 	TEST_REQUIRE(kvm_check_cap(KVM_CAP_ARM_EL2));
 	vm = vm_create(1);
@@ -102,13 +102,14 @@ int main(void)
 	vcpu = aarch64_vcpu_add(vm, 0, &init, guest_code);
 	kvm_arch_vm_finalize_vcpus(vm);
 
+	prepare_hyp_state(vm, vcpu);
 	while (true) {
 		vcpu_run(vcpu);
 
 		switch (get_ucall(vcpu, &uc)) {
 		case UCALL_SYNC:
 			if (uc.args[0] == XLATE2GPA) {
-				gpa = addr_gva2gpa(vm, (vm_vaddr_t)uc.args[1]);
+				gpa = addr_gva2gpa(vm, (gva_t)uc.args[1]);
 				vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_TPIDR_EL2), gpa);
 			}
 			break;
diff --git a/tools/testing/selftests/kvm/include/arm64/nested.h b/tools/testing/selftests/kvm/include/arm64/nested.h
index b16a72488858..b4ccca3593db 100644
--- a/tools/testing/selftests/kvm/include/arm64/nested.h
+++ b/tools/testing/selftests/kvm/include/arm64/nested.h
@@ -18,14 +18,44 @@
 
 #include <asm/ptrace.h>
 #include "kvm_util.h"
+#include "processor.h"
 
 extern char hyp_vectors[];
 
+#ifdef CONFIG_ARM64_64K_PAGES
+
+#define VTCR_EL2_TGRAN                  64K
+#define VTCR_EL2_TGRAN_SL0_BASE         3UL
+
+#elif defined(CONFIG_ARM64_16K_PAGES)
+
+#define VTCR_EL2_TGRAN                  16K
+#define VTCR_EL2_TGRAN_SL0_BASE         3UL
+
+#else   /* 4K */
+
+#define VTCR_EL2_TGRAN                  4K
+#define VTCR_EL2_TGRAN_SL0_BASE         2UL
+
+#endif
+
+struct s2_config {
+	u64 granule;
+	u8 ia_bits;
+	u8 oa_bits;
+	u8 start_level;
+};
+
+u64 get_l1_vtcr(const struct s2_config *cfg);
+
+void nested_map(struct kvm_vm *vm, const struct s2_config *cfg, gpa_t guest_pgd, uint64_t nested_paddr, uint64_t paddr, uint64_t size);
+void nested_map_memslot(struct kvm_vm *vm, const struct s2_config *cfg, gpa_t guest_pgd, u32 memslot);
+
 enum vcpu_sysreg {
 	__INVALID_SYSREG__,   /* 0 is reserved as an invalid value */
 
 	SP_EL1,
-
+	ESR_EL2,
 	NR_SYS_REGS
 };
 
@@ -47,12 +77,14 @@ struct hyp_data {
 };
 
 void prepare_hyp(void);
-void init_vcpu(struct vcpu *vcpu, vm_paddr_t l2_pc, vm_paddr_t l2_stack_top);
+void init_vcpu(struct vcpu *vcpu, gpa_t l2_pc, gpa_t l2_stack_top);
 int run_l2(struct vcpu *vcpu, struct hyp_data *hyp_data);
 
 u64 do_hvc(u64 action, u64 arg1, u64 arg2);
+u64 vcpu_get_esr_el2(struct vcpu *vcpu);
+
 u64 __guest_enter(struct vcpu *vcpu, struct cpu_context *hyp_context);
-void __hyp_exception(u64 type);
+void __hyp_exception(u64 type, u64 esr, u64 elr, u64 far, u64 hpfar, u64 spsr);
 
 void __sysreg_save_el1_state(struct cpu_context *ctxt);
 void __sysreg_restore_el1_state(struct cpu_context *ctxt);
diff --git a/tools/testing/selftests/kvm/lib/arm64/hyp-entry.S b/tools/testing/selftests/kvm/lib/arm64/hyp-entry.S
index 6341f6e05c90..fcf7bb303b77 100644
--- a/tools/testing/selftests/kvm/lib/arm64/hyp-entry.S
+++ b/tools/testing/selftests/kvm/lib/arm64/hyp-entry.S
@@ -30,6 +30,11 @@ el1_error:
 	b	__guest_exit
 
 el2_sync:
+	mrs	x1, esr_el2
+	mrs	x2, elr_el2
+	mrs	x3, far_el2
+	mrs	x4, hpfar_el2
+	mrs	x5, spsr_el2
 	mov	x0, #ARM_EXCEPTION_EL2_TRAP
 	b	__hyp_exception
 
diff --git a/tools/testing/selftests/kvm/lib/arm64/nested.c b/tools/testing/selftests/kvm/lib/arm64/nested.c
index b30d20b101c4..104c98d29eb9 100644
--- a/tools/testing/selftests/kvm/lib/arm64/nested.c
+++ b/tools/testing/selftests/kvm/lib/arm64/nested.c
@@ -7,15 +7,269 @@
 #include "processor.h"
 #include "test_util.h"
 #include <asm/sysreg.h>
+#include <linux/sizes.h>
+
+static const struct s2_config default_s2_cfg = {
+        .granule        = SZ_4K,
+        .ia_bits        = 40,
+        .oa_bits        = 40,
+        .start_level    = 0,
+};
+
+static u64 s2_alloc_page_table(struct kvm_vm *vm, const struct s2_config *cfg)
+{
+        u64 nr_pages = cfg->granule >> vm->page_shift;
+
+        TEST_ASSERT(!(cfg->granule & (vm->page_size - 1)),
+                    "S2 granule 0x%lx smaller/not aligned to VM page size 0x%x",
+                    cfg->granule, vm->page_size);
+
+        return vm_phy_pages_alloc(vm, nr_pages,
+                                  KVM_GUEST_PAGE_TABLE_MIN_PADDR,
+                                  vm->memslots[MEM_REGION_PT]);
+}
 
 void prepare_hyp(void)
 {
-	write_sysreg(HCR_EL2_E2H | HCR_EL2_RW, hcr_el2);
+	write_sysreg(HCR_EL2_E2H | HCR_EL2_RW | HCR_EL2_VM, hcr_el2);
 	write_sysreg(hyp_vectors, vbar_el2);
 	isb();
 }
 
-void init_vcpu(struct vcpu *vcpu, vm_paddr_t l2_pc, vm_paddr_t l2_stack_top)
+static unsigned int s2_granule_shift(const struct s2_config *cfg)
+{
+	switch (cfg->granule) {
+	case SZ_4K:
+		return 12;
+	case SZ_16K:
+		return 14;
+	case SZ_64K:
+		return 16;
+	default:
+		TEST_FAIL("Unsupported stage-2 granule %u", cfg->granule);
+	}
+}
+
+static unsigned int s2_level_stride(const struct s2_config *cfg)
+{
+	return s2_granule_shift(cfg) - 3;
+}
+
+static unsigned int s2_ptrs_per_table(const struct s2_config *cfg)
+{
+	return 1U << s2_level_stride(cfg);
+}
+
+static u64 s2_index_mask(const struct s2_config *cfg)
+{
+	return s2_ptrs_per_table(cfg) - 1;
+}
+
+static unsigned int s2_last_level(const struct s2_config *cfg)
+{
+	return 3;
+}
+
+static unsigned int s2_level_shift(const struct s2_config *cfg,
+				   unsigned int level)
+{
+	return s2_granule_shift(cfg) +
+	       (s2_last_level(cfg) - level) * s2_level_stride(cfg);
+}
+
+static u64 s2_table_mask(const struct s2_config *cfg)
+{
+	return GENMASK_ULL(cfg->ia_bits - 1, s2_granule_shift(cfg));
+}
+
+static u64 s2_output_mask(const struct s2_config *cfg)
+{
+	return GENMASK_ULL(cfg->oa_bits - 1, s2_granule_shift(cfg));
+}
+
+static u64 s2_desc_table(u64 paddr, const struct s2_config *cfg)
+{
+	return (paddr & s2_table_mask(cfg)) | 0x3;
+}
+
+#define S2_MEMATTR_NORMAL_WB		0xfUL
+#define S2_MEMATTR_SHIFT		2
+
+#define S2_S2AP_R			BIT(6)
+#define S2_S2AP_W			BIT(7)
+
+#define S2_SH_INNER			(3UL << 8)
+
+
+static u64 s2_desc_page(u64 paddr, u64 flags, const struct s2_config *cfg)
+{
+	u64 desc;
+
+	desc = paddr & s2_output_mask(cfg);
+
+	/* Stage-2 lower attrs */
+	desc |= S2_MEMATTR_NORMAL_WB << S2_MEMATTR_SHIFT;
+	desc |= S2_S2AP_R | S2_S2AP_W;
+	desc |= S2_SH_INNER;
+	desc |= PTE_AF;
+
+	/* L3 page descriptor: bits[1:0] = 0b11 */
+	desc |= PTE_TYPE_PAGE;
+	desc |= PTE_VALID;
+
+	return desc;
+}
+
+static inline int ipa_bits_to_ps(unsigned int ipa_bits)
+{
+	switch (ipa_bits) {
+	case 32:
+		return 0b000;
+	case 36:
+		return 0b001;
+	case 40:
+		return 0b010;
+	case 42:
+		return 0b011;
+	case 44:
+		return 0b100;
+	case 48:
+		return 0b101;
+	case 52:
+		return 0b110;
+	default:
+		return -EINVAL;
+	}
+}
+
+u64 get_l1_vtcr(const struct s2_config *cfg)
+{
+	if (!cfg)
+		cfg = &default_s2_cfg;
+
+	return  FIELD_PREP(VTCR_EL2_PS, ipa_bits_to_ps(cfg->ia_bits)) |
+		FIELD_PREP(VTCR_EL2_TG0, VTCR_EL2_TG0_4K) |
+		FIELD_PREP(VTCR_EL2_ORGN0_MASK, VTCR_EL2_ORGN0_WBWA) |
+		FIELD_PREP(VTCR_EL2_IRGN0_MASK, VTCR_EL2_IRGN0_WBWA) |
+		FIELD_PREP(VTCR_EL2_SH0_MASK, VTCR_EL2_SH0_INNER) |
+		FIELD_PREP(VTCR_EL2_SL0, VTCR_EL2_TGRAN_SL0_BASE - cfg->start_level) |
+		FIELD_PREP(VTCR_EL2_T0SZ_MASK, 64 - cfg->ia_bits);
+}
+
+void prepare_hyp_state(struct kvm_vm *vm, struct kvm_vcpu *vcpu)
+{
+        const struct s2_config *cfg = &default_s2_cfg;
+        u64 guest_pgd;
+
+        vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_VTCR_EL2), get_l1_vtcr(cfg));
+
+        guest_pgd = s2_alloc_page_table(vm, cfg);
+        nested_map_memslot(vm, cfg, guest_pgd, 0);
+
+	pr_debug("cfg=%p ia_bits=%u oa_bits=%u granule=%u\n",
+	cfg, cfg->ia_bits, cfg->oa_bits, cfg->granule);
+
+        vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_VTTBR_EL2), guest_pgd);
+}
+
+static void __nested_pg_map(struct kvm_vm *vm,
+			    const struct s2_config *cfg,
+			    u64 guest_pgd,
+			    u64 nested_paddr,
+			    u64 paddr,
+			    u64 flags)
+{
+	u64 granule = 1ULL << s2_granule_shift(cfg);
+	u64 *ptep;
+	unsigned int level;
+
+	TEST_ASSERT(!(nested_paddr & (granule - 1)),
+		    "L2 IPA not granule aligned: 0x%lx granule 0x%lx",
+		    nested_paddr, granule);
+
+	TEST_ASSERT(!(paddr & (granule - 1)),
+		    "PA not granule aligned: 0x%lx granule 0x%lx",
+		    paddr, granule);
+
+	ptep = addr_gpa2hva(vm, guest_pgd);
+
+	for (level = cfg->start_level; level < s2_last_level(cfg); level++) {
+		u64 idx;
+		u64 desc;
+
+		idx = (nested_paddr >> s2_level_shift(cfg, level)) &
+		      s2_index_mask(cfg);
+
+		ptep += idx;
+		desc = *ptep;
+
+		if (!desc) {
+			u64 table = s2_alloc_page_table(vm, cfg);
+
+			desc = s2_desc_table(table, cfg);
+			*ptep = desc;
+		}
+
+		ptep = addr_gpa2hva(vm, desc & s2_table_mask(cfg));
+	}
+
+	ptep += (nested_paddr >> s2_granule_shift(cfg)) & s2_index_mask(cfg);
+	*ptep = s2_desc_page(paddr, flags, cfg);
+}
+
+void nested_map(struct kvm_vm *vm,
+		const struct s2_config *cfg,
+		gpa_t guest_pgd,
+		u64 nested_paddr,
+		u64 paddr,
+		u64 size)
+{
+	u64 granule;
+	size_t npages;
+
+	if (!cfg)
+		cfg = &default_s2_cfg;
+
+	granule = 1ULL << s2_granule_shift(cfg);
+
+	TEST_ASSERT(!(size & (granule - 1)),
+		    "Mapping size 0x%lx not aligned to granule 0x%lx",
+		    size, granule);
+
+	TEST_ASSERT(nested_paddr + size > nested_paddr, "IPA overflow");
+	TEST_ASSERT(paddr + size > paddr, "PA overflow");
+
+	npages = size / granule;
+
+	while (npages--) {
+		__nested_pg_map(vm, cfg, guest_pgd, nested_paddr, paddr,
+				MT_NORMAL);
+
+		nested_paddr += granule;
+		paddr += granule;
+	}
+}
+
+void nested_map_memslot(struct kvm_vm *vm,
+			const struct s2_config *cfg,
+			gpa_t guest_pgd,
+			u32 memslot)
+{
+	struct userspace_mem_region *region;
+	u64 gpa, end;
+
+	region = memslot2region(vm, memslot);
+
+	gpa = region->region.guest_phys_addr;
+	end = gpa + region->region.memory_size;
+
+	pr_debug("nested S2 map slot %u: GPA %#lx-%#lx\n", memslot, gpa, end);
+
+	for (; gpa < end; gpa += cfg->granule)
+		nested_map(vm, cfg, guest_pgd, gpa, gpa, cfg->granule);
+}
+
+void init_vcpu(struct vcpu *vcpu, gpa_t l2_pc, gpa_t l2_stack_top)
 {
 	memset(vcpu, 0, sizeof(*vcpu));
 	vcpu->context.regs.pc = l2_pc;
@@ -46,13 +300,32 @@ int run_l2(struct vcpu *vcpu, struct hyp_data *hyp_data)
 
 	vcpu->context.regs.pc = read_sysreg(elr_el2);
 	vcpu->context.regs.pstate = read_sysreg(spsr_el2);
+	vcpu->context.sys_regs[ESR_EL2] = read_sysreg(esr_el2);
 
 	__sysreg_save_el1_state(&vcpu->context);
 
 	return ret;
 }
 
-void __hyp_exception(u64 type)
+u64 vcpu_get_esr_el2(struct vcpu *vcpu)
 {
+	return vcpu->context.sys_regs[ESR_EL2];
+
+}
+
+void __hyp_exception(u64 type, u64 esr, u64 elr, u64 far, u64 hpfar, u64 spsr)
+{
+	u64 ec = esr >> 26;
+	u64 iss = esr & GENMASK_ULL(24, 0);
+	u64 ipa = ((hpfar & GENMASK_ULL(39, 4)) << 8) |
+		  (far & GENMASK_ULL(11, 0));
+
+	GUEST_FAIL("Unexpected hyp exception: type=%lu "
+		   "ESR_EL2=%#lx EC=%#lx ISS=%#lx "
+		   "ELR_EL2=%#lx FAR_EL2=%#lx HPFAR_EL2=%#lx IPA=%#lx "
+		   "SPSR_EL2=%#lx",
+		   type, esr, ec, iss, elr, far, hpfar, ipa, spsr);
 	GUEST_FAIL("Unexpected hyp exception! type: %lx\n", type);
+
+
 }

---
base-commit: eb656a0272c639d43be7a9bdd1c5f31eff3afe86
change-id: 20260515-enable-s2-hello_nested-b360a2e9bb87

Best regards,
-- 
Itaru Kitayama <itaru.kitayama@fujitsu.com>



                 reply	other threads:[~2026-05-15  1:48 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=20260515-enable-s2-hello_nested-v1-1-41f3faca1a08@fujitsu.com \
    --to=itaru.kitayama@fujitsu.com \
    --cc=joey.gouly@arm.com \
    --cc=kvm@vger.kernel.org \
    --cc=kvmarm@lists.linux.dev \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=maz@kernel.org \
    --cc=oupton@kernel.org \
    --cc=pbonzini@redhat.com \
    --cc=seiden@linux.ibm.com \
    --cc=shuah@kernel.org \
    --cc=suzuki.poulose@arm.com \
    --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.