target/arm: Rewrite check_s2_mmu_setup
Integrate neighboring code from get_phys_addr_lpae which computed starting level, as it is easier to validate when doing both at the same time. Mirror the checks at the start of AArch{64,32}.S2Walk, especially S2InvalidSL and S2InconsistentSL. This reverts49ba115bb7
, which was incorrect -- there is nothing in the ARM pseudocode that depends on TxSZ, i.e. outputsize; the pseudocode is consistent in referencing PAMax. Fixes:49ba115bb7
("target/arm: Pass outputsize down to check_s2_mmu_setup") Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20230227225832.816605-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
fcc7404eff
commit
0ffe5b7ba8
173
target/arm/ptw.c
173
target/arm/ptw.c
|
@ -1081,70 +1081,119 @@ static ARMVAParameters aa32_va_parameters(CPUARMState *env, uint32_t va,
|
||||||
* check_s2_mmu_setup
|
* check_s2_mmu_setup
|
||||||
* @cpu: ARMCPU
|
* @cpu: ARMCPU
|
||||||
* @is_aa64: True if the translation regime is in AArch64 state
|
* @is_aa64: True if the translation regime is in AArch64 state
|
||||||
* @startlevel: Suggested starting level
|
* @tcr: VTCR_EL2 or VSTCR_EL2
|
||||||
* @inputsize: Bitsize of IPAs
|
* @ds: Effective value of TCR.DS.
|
||||||
|
* @iasize: Bitsize of IPAs
|
||||||
* @stride: Page-table stride (See the ARM ARM)
|
* @stride: Page-table stride (See the ARM ARM)
|
||||||
*
|
*
|
||||||
* Returns true if the suggested S2 translation parameters are OK and
|
* Decode the starting level of the S2 lookup, returning INT_MIN if
|
||||||
* false otherwise.
|
* the configuration is invalid.
|
||||||
*/
|
*/
|
||||||
static bool check_s2_mmu_setup(ARMCPU *cpu, bool is_aa64, int level,
|
static int check_s2_mmu_setup(ARMCPU *cpu, bool is_aa64, uint64_t tcr,
|
||||||
int inputsize, int stride, int outputsize)
|
bool ds, int iasize, int stride)
|
||||||
{
|
{
|
||||||
const int grainsize = stride + 3;
|
int sl0, sl2, startlevel, granulebits, levels;
|
||||||
int startsizecheck;
|
int s1_min_iasize, s1_max_iasize;
|
||||||
|
|
||||||
/*
|
|
||||||
* Negative levels are usually not allowed...
|
|
||||||
* Except for FEAT_LPA2, 4k page table, 52-bit address space, which
|
|
||||||
* begins with level -1. Note that previous feature tests will have
|
|
||||||
* eliminated this combination if it is not enabled.
|
|
||||||
*/
|
|
||||||
if (level < (inputsize == 52 && stride == 9 ? -1 : 0)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
startsizecheck = inputsize - ((3 - level) * stride + grainsize);
|
|
||||||
if (startsizecheck < 1 || startsizecheck > stride + 4) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
sl0 = extract32(tcr, 6, 2);
|
||||||
if (is_aa64) {
|
if (is_aa64) {
|
||||||
|
/*
|
||||||
|
* AArch64.S2InvalidTxSZ: While we checked tsz_oob near the top of
|
||||||
|
* get_phys_addr_lpae, that used aa64_va_parameters which apply
|
||||||
|
* to aarch64. If Stage1 is aarch32, the min_txsz is larger.
|
||||||
|
* See AArch64.S2MinTxSZ, where min_tsz is 24, translated to
|
||||||
|
* inputsize is 64 - 24 = 40.
|
||||||
|
*/
|
||||||
|
if (iasize < 40 && !arm_el_is_aa64(&cpu->env, 1)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AArch64.S2InvalidSL: Interpretation of SL depends on the page size,
|
||||||
|
* so interleave AArch64.S2StartLevel.
|
||||||
|
*/
|
||||||
switch (stride) {
|
switch (stride) {
|
||||||
case 13: /* 64KB Pages. */
|
case 9: /* 4KB */
|
||||||
if (level == 0 || (level == 1 && outputsize <= 42)) {
|
/* SL2 is RES0 unless DS=1 & 4KB granule. */
|
||||||
return false;
|
sl2 = extract64(tcr, 33, 1);
|
||||||
|
if (ds && sl2) {
|
||||||
|
if (sl0 != 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
startlevel = -1;
|
||||||
|
} else {
|
||||||
|
startlevel = 2 - sl0;
|
||||||
|
switch (sl0) {
|
||||||
|
case 2:
|
||||||
|
if (arm_pamax(cpu) < 44) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if (!cpu_isar_feature(aa64_st, cpu)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
startlevel = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 11: /* 16KB Pages. */
|
case 11: /* 16KB */
|
||||||
if (level == 0 || (level == 1 && outputsize <= 40)) {
|
switch (sl0) {
|
||||||
return false;
|
case 2:
|
||||||
|
if (arm_pamax(cpu) < 42) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if (!ds) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
startlevel = 3 - sl0;
|
||||||
break;
|
break;
|
||||||
case 9: /* 4KB Pages. */
|
case 13: /* 64KB */
|
||||||
if (level == 0 && outputsize <= 42) {
|
switch (sl0) {
|
||||||
return false;
|
case 2:
|
||||||
|
if (arm_pamax(cpu) < 44) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
|
startlevel = 3 - sl0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Inputsize checks. */
|
|
||||||
if (inputsize > outputsize &&
|
|
||||||
(arm_el_is_aa64(&cpu->env, 1) || inputsize > 40)) {
|
|
||||||
/* This is CONSTRAINED UNPREDICTABLE and we choose to fault. */
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
/* AArch32 only supports 4KB pages. Assert on that. */
|
/*
|
||||||
|
* Things are simpler for AArch32 EL2, with only 4k pages.
|
||||||
|
* There is no separate S2InvalidSL function, but AArch32.S2Walk
|
||||||
|
* begins with walkparms.sl0 in {'1x'}.
|
||||||
|
*/
|
||||||
assert(stride == 9);
|
assert(stride == 9);
|
||||||
|
if (sl0 >= 2) {
|
||||||
if (level == 0) {
|
goto fail;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
startlevel = 2 - sl0;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
|
/* AArch{64,32}.S2InconsistentSL are functionally equivalent. */
|
||||||
|
levels = 3 - startlevel;
|
||||||
|
granulebits = stride + 3;
|
||||||
|
|
||||||
|
s1_min_iasize = levels * stride + granulebits + 1;
|
||||||
|
s1_max_iasize = s1_min_iasize + (stride - 1) + 4;
|
||||||
|
|
||||||
|
if (iasize >= s1_min_iasize && iasize <= s1_max_iasize) {
|
||||||
|
return startlevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return INT_MIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1300,38 +1349,10 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw,
|
||||||
*/
|
*/
|
||||||
level = 4 - (inputsize - 4) / stride;
|
level = 4 - (inputsize - 4) / stride;
|
||||||
} else {
|
} else {
|
||||||
/*
|
int startlevel = check_s2_mmu_setup(cpu, aarch64, tcr, param.ds,
|
||||||
* For stage 2 translations the starting level is specified by the
|
inputsize, stride);
|
||||||
* VTCR_EL2.SL0 field (whose interpretation depends on the page size)
|
if (startlevel == INT_MIN) {
|
||||||
*/
|
level = 0;
|
||||||
uint32_t sl0 = extract32(tcr, 6, 2);
|
|
||||||
uint32_t sl2 = extract64(tcr, 33, 1);
|
|
||||||
int32_t startlevel;
|
|
||||||
bool ok;
|
|
||||||
|
|
||||||
/* SL2 is RES0 unless DS=1 & 4kb granule. */
|
|
||||||
if (param.ds && stride == 9 && sl2) {
|
|
||||||
if (sl0 != 0) {
|
|
||||||
level = 0;
|
|
||||||
goto do_translation_fault;
|
|
||||||
}
|
|
||||||
startlevel = -1;
|
|
||||||
} else if (!aarch64 || stride == 9) {
|
|
||||||
/* AArch32 or 4KB pages */
|
|
||||||
startlevel = 2 - sl0;
|
|
||||||
|
|
||||||
if (cpu_isar_feature(aa64_st, cpu)) {
|
|
||||||
startlevel &= 3;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* 16KB or 64KB pages */
|
|
||||||
startlevel = 3 - sl0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check that the starting level is valid. */
|
|
||||||
ok = check_s2_mmu_setup(cpu, aarch64, startlevel,
|
|
||||||
inputsize, stride, outputsize);
|
|
||||||
if (!ok) {
|
|
||||||
goto do_translation_fault;
|
goto do_translation_fault;
|
||||||
}
|
}
|
||||||
level = startlevel;
|
level = startlevel;
|
||||||
|
|
Loading…
Reference in New Issue