From: Fuad Tabba <fuad.tabba@linux.dev>
To: Peter Maydell <peter.maydell@linaro.org>
Cc: qemu-arm@nongnu.org, qemu-devel@nongnu.org,
Richard Henderson <richard.henderson@linaro.org>,
Will Deacon <will@kernel.org>,
Itaru Kitayama <itaru.kitayama@linux.dev>,
Fuad Tabba <tabba@google.com>
Subject: [PATCH 2/2] target/arm: Use the 52-bit TTBR base address layout when TCR.DS is set
Date: Fri, 2 Oct 2026 08:09:20 +0100 [thread overview]
Message-ID: <20261002070925.2627344-2-fuad.tabba@linux.dev> (raw)
In-Reply-To: <20261001175003.ttbr-baddr-52bit-0-fuad.tabba@linux.dev>
With TCR.DS set, TTBR[5:2] hold bits [51:48] of the translation table
base address, whatever the OA size. If the effective OA size is below
52 bits, setting any of them is a level 0 Address size fault
(AArch64_S1TTBaseAddress(), AArch64_S1Walk()).
get_phys_addr_lpae() uses the 52-bit layout only when the OA size is
over 48 bits. With TCR.DS set and an IPS of 48 bits, it takes TTBR[5:2]
as base address bits [5:2] instead. A guest that sets them gets no
fault: the walk either succeeds with TTBR[5:2] ignored, or reads its
first descriptor from the wrong address. VTTBR walks with VTCR.DS take
the same path.
Use the 52-bit layout whenever TCR.DS is set, and check the whole base
address against the OA size. Add TCR.DS and VTCR.DS cases with a 48-bit
OA to the test.
Fixes: ef56c2425e5f ("target/arm: Implement FEAT_LPA2")
Cc: qemu-stable@nongnu.org
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
target/arm/ptw.c | 16 ++++----
tests/tcg/aarch64/system/ttbr-baddr.c | 53 +++++++++++++++++++++++++++
2 files changed, 61 insertions(+), 8 deletions(-)
diff --git a/target/arm/ptw.c b/target/arm/ptw.c
index 6cff52d592..16f5fdfe0b 100644
--- a/target/arm/ptw.c
+++ b/target/arm/ptw.c
@@ -2107,18 +2107,18 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw,
descaddr = extract64(ttbr, 0, 48);
/*
- * With a 52-bit OA (FEAT_LPA or FEAT_LPA2), bits [51:48] of descaddr are
- * in [5:2] of TTBR, and bits [5:0] of the base address are zero: a table
- * under 64 bytes is still 64-byte aligned (R_KBLCR).
+ * With a 52-bit OA, or with TCR.DS, bits [51:48] of the base address are
+ * in [5:2] of TTBR, and bits [5:0] of the base address are zero: the
+ * table is at least 64-byte aligned.
*
- * Otherwise, if the base address is out of range, raise AddressSizeFault.
- * In the pseudocode, this is !IsZero(baseregister<47:outputsize>),
- * but we've just cleared the bits above 47, so simplify the test.
+ * If the base address is out of range, raise AddressSizeFault, as
+ * AArch64_OAOutOfRange() does in the pseudocode.
*/
- if (outputsize > 48) {
+ if (outputsize > 48 || param.ds) {
descaddr &= ~MAKE_64BIT_MASK(0, 6);
descaddr |= extract64(ttbr, 2, 4) << 48;
- } else if (descaddr >> outputsize) {
+ }
+ if (descaddr >> outputsize) {
level = 0;
fi->type = ARMFault_AddressSize;
goto do_fault;
diff --git a/tests/tcg/aarch64/system/ttbr-baddr.c b/tests/tcg/aarch64/system/ttbr-baddr.c
index 7360684268..6a15c55fae 100644
--- a/tests/tcg/aarch64/system/ttbr-baddr.c
+++ b/tests/tcg/aarch64/system/ttbr-baddr.c
@@ -34,10 +34,13 @@
* Stage 1: 16KB granule, 48-bit VA, so level 0 has two entries.
* Stage 2: 64KB granule, 52-bit IPA, mapping RAM at its own address and
* again at HIGH_IPA.
+ * Stage 2 with VTCR.DS: 16KB granule, 47-bit IPA, mapping RAM flat.
*/
static uint64_t s1_l0[2048] __attribute__((aligned(16384)));
static uint64_t s1_l1[2048] __attribute__((aligned(16384)));
static uint64_t s1_l2[2048] __attribute__((aligned(16384)));
+static uint64_t s2ds_l1[2048] __attribute__((aligned(16384)));
+static uint64_t s2ds_l2[2048] __attribute__((aligned(16384)));
static uint64_t s2_l1[1024] __attribute__((aligned(65536)));
static uint64_t s2_l2[8192] __attribute__((aligned(65536)));
static uint64_t s2_l2_high[8192] __attribute__((aligned(65536)));
@@ -46,20 +49,27 @@ static uint64_t s2_l2_high[8192] __attribute__((aligned(65536)));
#define TCR_T1SZ(x) ((uint64_t)(x) << 16)
#define TCR_TG0_16K (2UL << 14)
#define TCR_TG1_16K (1UL << 30)
+#define TCR_IPS_48 (5UL << 32)
#define TCR_IPS_52 (6UL << 32)
#define TCR_DS (1UL << 59)
#define VTCR_T0SZ(x) ((uint64_t)(x) << 0)
#define VTCR_SL0_L1 (2UL << 6)
+#define VTCR_TG0_16K (2UL << 14)
#define VTCR_TG0_64K (1UL << 14)
+#define VTCR_PS_48 (5UL << 16)
#define VTCR_PS_52 (6UL << 16)
#define VTCR_RES1 (1UL << 31)
+#define VTCR_DS (1UL << 32)
#define HCR_VM (1UL << 0)
#define HCR_RW (1UL << 31)
#define PAR_F (1UL << 0)
+#define PAR_FST(par) (((par) >> 1) & 0x3f)
#define PAR_PA(par) ((par) & 0xfffffffff000UL)
+#define FST_ADDR_SIZE_L0 0x00
+
#define S2_BLOCK ((1 << 10) | (3 << 6) | (0xf << 2) | 1)
static void tlb_flush(void)
@@ -78,6 +88,17 @@ static uint64_t at(uint64_t ttbr1)
return par;
}
+static uint64_t at_s1(uint64_t ttbr1)
+{
+ uint64_t par;
+
+ write_sysreg(ttbr1_el1, ttbr1);
+ tlb_flush();
+ asm volatile("at s1e1r, %1; isb; mrs %0, par_el1"
+ : "=r" (par) : "r" (TEST_VA));
+ return par;
+}
+
static void setup_tables(void)
{
/* Stage 1: TEST_VA -> RAM_BASE, 32MB block, AF */
@@ -85,6 +106,10 @@ static void setup_tables(void)
s1_l1[0] = (uint64_t)s1_l2 | 3;
s1_l2[(RAM_BASE >> 25) & 0x7ff] = RAM_BASE | (1 << 10) | 1;
+ /* Stage 2 with VTCR.DS: RAM_BASE -> RAM_BASE, 32MB block, AF, RW */
+ s2ds_l1[0] = (uint64_t)s2ds_l2 | 3;
+ s2ds_l2[(RAM_BASE >> 25) & 0x7ff] = RAM_BASE | S2_BLOCK;
+
/* Stage 2: 512MB blocks at RAM_BASE and HIGH_IPA | RAM_BASE, AF, RW */
s2_l1[0] = (uint64_t)s2_l2 | 3;
s2_l1[HIGH_IPA >> 42] = (uint64_t)s2_l2_high | 3;
@@ -100,6 +125,15 @@ static int check(const char *name, uint64_t par)
return !ok;
}
+/* A level 0 Address size fault */
+static int check_fault(const char *name, uint64_t par)
+{
+ int ok = (par & PAR_F) && PAR_FST(par) == FST_ADDR_SIZE_L0;
+
+ ml_printf("%s: PAR_EL1=%lx %s\n", name, par, ok ? "ok" : "FAIL");
+ return !ok;
+}
+
int main(void)
{
uint64_t mmfr0 = read_sysreg(id_aa64mmfr0_el1);
@@ -124,6 +158,25 @@ int main(void)
write_sysreg(mair_el1, 0xff);
write_sysreg(sctlr_el1, read_sysreg(sctlr_el1) | 1);
+ /*
+ * TCR.DS with a 48-bit OA: TTBR[5:2] are base address bits [51:48],
+ * so setting any of them is a level 0 Address size fault.
+ */
+ write_sysreg(hcr_el2, HCR_RW);
+ write_sysreg(tcr_el1, tcr | TCR_IPS_48);
+ ret |= check("ips48", at_s1(base));
+ ret |= check_fault("ips48 ttbr[2]", at_s1(base | (1 << 2)));
+ ret |= check_fault("ips48 ttbr[4]", at_s1(base | (1 << 4)));
+
+ /* The same for VTTBR, with VTCR.DS and a 48-bit PS */
+ write_sysreg(vtcr_el2, VTCR_T0SZ(17) | VTCR_SL0_L1 | VTCR_TG0_16K |
+ VTCR_PS_48 | VTCR_RES1 | VTCR_DS);
+ write_sysreg(hcr_el2, HCR_RW | HCR_VM);
+ write_sysreg(vttbr_el2, (uint64_t)s2ds_l1);
+ ret |= check("ps48", at(base));
+ write_sysreg(vttbr_el2, (uint64_t)s2ds_l1 | (1 << 2));
+ ret |= check_fault("ps48 vttbr[2]", at(base));
+
/*
* 52-bit OA: TTBR[4] is base address bit 50. Through stage 2, the
* table at HIGH_IPA | s1_l0 is s1_l0, so the walk succeeds only if
--
2.39.5
next prev parent reply other threads:[~2026-10-02 7:11 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-02 7:09 [PATCH 0/2] target/arm: Fix the TTBR table base address in its 52-bit layout Fuad Tabba
2026-10-02 7:09 ` [PATCH 1/2] target/arm: Clear TTBR[5:0] from a 52-bit table base address Fuad Tabba
2026-10-05 20:06 ` Gustavo Romero
2026-10-05 21:44 ` Fuad Tabba
2026-10-06 9:03 ` Peter Maydell
2026-10-06 9:27 ` Fuad Tabba
2026-10-02 7:09 ` Fuad Tabba [this message]
2026-10-06 11:24 ` [PATCH 2/2] target/arm: Use the 52-bit TTBR base address layout when TCR.DS is set Peter Maydell
2026-10-06 13:20 ` Fuad Tabba
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=20261002070925.2627344-2-fuad.tabba@linux.dev \
--to=fuad.tabba@linux.dev \
--cc=itaru.kitayama@linux.dev \
--cc=peter.maydell@linaro.org \
--cc=qemu-arm@nongnu.org \
--cc=qemu-devel@nongnu.org \
--cc=richard.henderson@linaro.org \
--cc=tabba@google.com \
--cc=will@kernel.org \
/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.