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.
Fixes: ef56c2425e5f ("target/arm: Implement FEAT_LPA2")
Cc: [email protected]
Reviewed-by: Gustavo Romero <[email protected]>
Signed-off-by: Fuad Tabba <[email protected]>
---
target/arm/ptw.c | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/target/arm/ptw.c b/target/arm/ptw.c
index 6cff52d592..2e67d02493 100644
--- a/target/arm/ptw.c
+++ b/target/arm/ptw.c
@@ -2107,18 +2107,21 @@ 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 (FEAT_LPA or FEAT_LPA2), or with TCR.DS set at any OA
+ * size, 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).
*
- * 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.
+ * In the pseudocode, this is !IsZero(address<NUM_PABITS-1:outputsize>)
+ * in AArch64_OAOutOfRange(), but descaddr has no bits set above 51,
+ * so simplify the test.
*/
- 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;
--
2.39.5