target/arm: Honor TCR_ELx.{I}PS

This field controls the output (intermediate) physical address size
of the translation process.  V8 requires to raise an AddressSize
fault if the page tables are programmed incorrectly, such that any
intermediate descriptor address, or the final translated address,
is out of range.

Add a PS field to ARMVAParameters, and properly compute outputsize
in get_phys_addr_lpae.  Test the descaddr as extracted from TTBR
and from page table entries.

Restrict descaddrmask so that we won't raise the fault for v7.

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20220301215958.157011-8-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Richard Henderson 2022-03-01 11:59:47 -10:00 committed by Peter Maydell
parent d06449f2eb
commit f4ecc01537
2 changed files with 57 additions and 16 deletions

View File

@ -11149,10 +11149,8 @@ static uint8_t convert_stage2_attrs(CPUARMState *env, uint8_t s2attrs)
} }
#endif /* !CONFIG_USER_ONLY */ #endif /* !CONFIG_USER_ONLY */
/* The cpu-specific constant value of PAMax; also used by hw/arm/virt. */ /* This mapping is common between ID_AA64MMFR0.PARANGE and TCR_ELx.{I}PS. */
unsigned int arm_pamax(ARMCPU *cpu) static const uint8_t pamax_map[] = {
{
static const unsigned int pamax_map[] = {
[0] = 32, [0] = 32,
[1] = 36, [1] = 36,
[2] = 40, [2] = 40,
@ -11160,6 +11158,10 @@ unsigned int arm_pamax(ARMCPU *cpu)
[4] = 44, [4] = 44,
[5] = 48, [5] = 48,
}; };
/* The cpu-specific constant value of PAMax; also used by hw/arm/virt. */
unsigned int arm_pamax(ARMCPU *cpu)
{
unsigned int parange = unsigned int parange =
FIELD_EX64(cpu->isar.id_aa64mmfr0, ID_AA64MMFR0, PARANGE); FIELD_EX64(cpu->isar.id_aa64mmfr0, ID_AA64MMFR0, PARANGE);
@ -11210,7 +11212,7 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va,
{ {
uint64_t tcr = regime_tcr(env, mmu_idx)->raw_tcr; uint64_t tcr = regime_tcr(env, mmu_idx)->raw_tcr;
bool epd, hpd, using16k, using64k, tsz_oob; bool epd, hpd, using16k, using64k, tsz_oob;
int select, tsz, tbi, max_tsz, min_tsz; int select, tsz, tbi, max_tsz, min_tsz, ps;
if (!regime_has_2_ranges(mmu_idx)) { if (!regime_has_2_ranges(mmu_idx)) {
select = 0; select = 0;
@ -11224,6 +11226,7 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va,
hpd = extract32(tcr, 24, 1); hpd = extract32(tcr, 24, 1);
} }
epd = false; epd = false;
ps = extract32(tcr, 16, 3);
} else { } else {
/* /*
* Bit 55 is always between the two regions, and is canonical for * Bit 55 is always between the two regions, and is canonical for
@ -11244,6 +11247,7 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va,
epd = extract32(tcr, 23, 1); epd = extract32(tcr, 23, 1);
hpd = extract64(tcr, 42, 1); hpd = extract64(tcr, 42, 1);
} }
ps = extract64(tcr, 32, 3);
} }
if (cpu_isar_feature(aa64_st, env_archcpu(env))) { if (cpu_isar_feature(aa64_st, env_archcpu(env))) {
@ -11272,6 +11276,7 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va,
return (ARMVAParameters) { return (ARMVAParameters) {
.tsz = tsz, .tsz = tsz,
.ps = ps,
.select = select, .select = select,
.tbi = tbi, .tbi = tbi,
.epd = epd, .epd = epd,
@ -11399,6 +11404,8 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address,
/* TODO: This code does not support shareability levels. */ /* TODO: This code does not support shareability levels. */
if (aarch64) { if (aarch64) {
int ps;
param = aa64_va_parameters(env, address, mmu_idx, param = aa64_va_parameters(env, address, mmu_idx,
access_type != MMU_INST_FETCH); access_type != MMU_INST_FETCH);
level = 0; level = 0;
@ -11419,7 +11426,16 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address,
addrsize = 64 - 8 * param.tbi; addrsize = 64 - 8 * param.tbi;
inputsize = 64 - param.tsz; inputsize = 64 - param.tsz;
outputsize = arm_pamax(cpu);
/*
* Bound PS by PARANGE to find the effective output address size.
* ID_AA64MMFR0 is a read-only register so values outside of the
* supported mappings can be considered an implementation error.
*/
ps = FIELD_EX64(cpu->isar.id_aa64mmfr0, ID_AA64MMFR0, PARANGE);
ps = MIN(ps, param.ps);
assert(ps < ARRAY_SIZE(pamax_map));
outputsize = pamax_map[ps];
} else { } else {
param = aa32_va_parameters(env, address, mmu_idx); param = aa32_va_parameters(env, address, mmu_idx);
level = 1; level = 1;
@ -11523,19 +11539,38 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address,
/* Now we can extract the actual base address from the TTBR */ /* Now we can extract the actual base address from the TTBR */
descaddr = extract64(ttbr, 0, 48); descaddr = extract64(ttbr, 0, 48);
/*
* 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 (descaddr >> outputsize) {
level = 0;
fault_type = ARMFault_AddressSize;
goto do_fault;
}
/* /*
* We rely on this masking to clear the RES0 bits at the bottom of the TTBR * We rely on this masking to clear the RES0 bits at the bottom of the TTBR
* and also to mask out CnP (bit 0) which could validly be non-zero. * and also to mask out CnP (bit 0) which could validly be non-zero.
*/ */
descaddr &= ~indexmask; descaddr &= ~indexmask;
/* The address field in the descriptor goes up to bit 39 for ARMv7 /*
* but up to bit 47 for ARMv8, but we use the descaddrmask * For AArch32, the address field in the descriptor goes up to bit 39
* up to bit 39 for AArch32, because we don't need other bits in that case * for both v7 and v8. However, for v8 the SBZ bits [47:40] must be 0
* to construct next descriptor address (anyway they should be all zeroes). * or an AddressSize fault is raised. So for v8 we extract those SBZ
* bits as part of the address, which will be checked via outputsize.
* For AArch64, the address field always goes up to bit 47 (with extra
* bits for FEAT_LPA placed elsewhere). AArch64 implies v8.
*/ */
descaddrmask = ((1ull << (aarch64 ? 48 : 40)) - 1) & if (arm_feature(env, ARM_FEATURE_V8)) {
~indexmask_grainsize; descaddrmask = MAKE_64BIT_MASK(0, 48);
} else {
descaddrmask = MAKE_64BIT_MASK(0, 40);
}
descaddrmask &= ~indexmask_grainsize;
/* Secure accesses start with the page table in secure memory and /* Secure accesses start with the page table in secure memory and
* can be downgraded to non-secure at any step. Non-secure accesses * can be downgraded to non-secure at any step. Non-secure accesses
@ -11560,7 +11595,12 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address,
/* Invalid, or the Reserved level 3 encoding */ /* Invalid, or the Reserved level 3 encoding */
goto do_fault; goto do_fault;
} }
descaddr = descriptor & descaddrmask; descaddr = descriptor & descaddrmask;
if (descaddr >> outputsize) {
fault_type = ARMFault_AddressSize;
goto do_fault;
}
if ((descriptor & 2) && (level < 3)) { if ((descriptor & 2) && (level < 3)) {
/* Table entry. The top five bits are attributes which may /* Table entry. The top five bits are attributes which may

View File

@ -1032,6 +1032,7 @@ static inline uint32_t aarch64_pstate_valid_mask(const ARMISARegisters *id)
*/ */
typedef struct ARMVAParameters { typedef struct ARMVAParameters {
unsigned tsz : 8; unsigned tsz : 8;
unsigned ps : 3;
unsigned select : 1; unsigned select : 1;
bool tbi : 1; bool tbi : 1;
bool epd : 1; bool epd : 1;