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: [email protected]
Signed-off-by: Fuad Tabba <[email protected]>
---
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