target/arm: Implement the granule protection check
Place the check at the end of get_phys_addr_with_struct, so that we check all physical results. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20230620124418.805717-20-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
11b76fda0a
commit
46f38c975f
249
target/arm/ptw.c
249
target/arm/ptw.c
@ -39,11 +39,17 @@ typedef struct S1Translate {
|
||||
void *out_host;
|
||||
} S1Translate;
|
||||
|
||||
static bool get_phys_addr_with_struct(CPUARMState *env, S1Translate *ptw,
|
||||
target_ulong address,
|
||||
MMUAccessType access_type,
|
||||
GetPhysAddrResult *result,
|
||||
ARMMMUFaultInfo *fi);
|
||||
static bool get_phys_addr_nogpc(CPUARMState *env, S1Translate *ptw,
|
||||
target_ulong address,
|
||||
MMUAccessType access_type,
|
||||
GetPhysAddrResult *result,
|
||||
ARMMMUFaultInfo *fi);
|
||||
|
||||
static bool get_phys_addr_gpc(CPUARMState *env, S1Translate *ptw,
|
||||
target_ulong address,
|
||||
MMUAccessType access_type,
|
||||
GetPhysAddrResult *result,
|
||||
ARMMMUFaultInfo *fi);
|
||||
|
||||
/* This mapping is common between ID_AA64MMFR0.PARANGE and TCR_ELx.{I}PS. */
|
||||
static const uint8_t pamax_map[] = {
|
||||
@ -230,6 +236,197 @@ static bool regime_translation_disabled(CPUARMState *env, ARMMMUIdx mmu_idx,
|
||||
return (regime_sctlr(env, mmu_idx) & SCTLR_M) == 0;
|
||||
}
|
||||
|
||||
static bool granule_protection_check(CPUARMState *env, uint64_t paddress,
|
||||
ARMSecuritySpace pspace,
|
||||
ARMMMUFaultInfo *fi)
|
||||
{
|
||||
MemTxAttrs attrs = {
|
||||
.secure = true,
|
||||
.space = ARMSS_Root,
|
||||
};
|
||||
ARMCPU *cpu = env_archcpu(env);
|
||||
uint64_t gpccr = env->cp15.gpccr_el3;
|
||||
unsigned pps, pgs, l0gptsz, level = 0;
|
||||
uint64_t tableaddr, pps_mask, align, entry, index;
|
||||
AddressSpace *as;
|
||||
MemTxResult result;
|
||||
int gpi;
|
||||
|
||||
if (!FIELD_EX64(gpccr, GPCCR, GPC)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* GPC Priority 1 (R_GMGRR):
|
||||
* R_JWCSM: If the configuration of GPCCR_EL3 is invalid,
|
||||
* the access fails as GPT walk fault at level 0.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Configuration of PPS to a value exceeding the implemented
|
||||
* physical address size is invalid.
|
||||
*/
|
||||
pps = FIELD_EX64(gpccr, GPCCR, PPS);
|
||||
if (pps > FIELD_EX64(cpu->isar.id_aa64mmfr0, ID_AA64MMFR0, PARANGE)) {
|
||||
goto fault_walk;
|
||||
}
|
||||
pps = pamax_map[pps];
|
||||
pps_mask = MAKE_64BIT_MASK(0, pps);
|
||||
|
||||
switch (FIELD_EX64(gpccr, GPCCR, SH)) {
|
||||
case 0b10: /* outer shareable */
|
||||
break;
|
||||
case 0b00: /* non-shareable */
|
||||
case 0b11: /* inner shareable */
|
||||
/* Inner and Outer non-cacheable requires Outer shareable. */
|
||||
if (FIELD_EX64(gpccr, GPCCR, ORGN) == 0 &&
|
||||
FIELD_EX64(gpccr, GPCCR, IRGN) == 0) {
|
||||
goto fault_walk;
|
||||
}
|
||||
break;
|
||||
default: /* reserved */
|
||||
goto fault_walk;
|
||||
}
|
||||
|
||||
switch (FIELD_EX64(gpccr, GPCCR, PGS)) {
|
||||
case 0b00: /* 4KB */
|
||||
pgs = 12;
|
||||
break;
|
||||
case 0b01: /* 64KB */
|
||||
pgs = 16;
|
||||
break;
|
||||
case 0b10: /* 16KB */
|
||||
pgs = 14;
|
||||
break;
|
||||
default: /* reserved */
|
||||
goto fault_walk;
|
||||
}
|
||||
|
||||
/* Note this field is read-only and fixed at reset. */
|
||||
l0gptsz = 30 + FIELD_EX64(gpccr, GPCCR, L0GPTSZ);
|
||||
|
||||
/*
|
||||
* GPC Priority 2: Secure, Realm or Root address exceeds PPS.
|
||||
* R_CPDSB: A NonSecure physical address input exceeding PPS
|
||||
* does not experience any fault.
|
||||
*/
|
||||
if (paddress & ~pps_mask) {
|
||||
if (pspace == ARMSS_NonSecure) {
|
||||
return true;
|
||||
}
|
||||
goto fault_size;
|
||||
}
|
||||
|
||||
/* GPC Priority 3: the base address of GPTBR_EL3 exceeds PPS. */
|
||||
tableaddr = env->cp15.gptbr_el3 << 12;
|
||||
if (tableaddr & ~pps_mask) {
|
||||
goto fault_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* BADDR is aligned per a function of PPS and L0GPTSZ.
|
||||
* These bits of GPTBR_EL3 are RES0, but are not a configuration error,
|
||||
* unlike the RES0 bits of the GPT entries (R_XNKFZ).
|
||||
*/
|
||||
align = MAX(pps - l0gptsz + 3, 12);
|
||||
align = MAKE_64BIT_MASK(0, align);
|
||||
tableaddr &= ~align;
|
||||
|
||||
as = arm_addressspace(env_cpu(env), attrs);
|
||||
|
||||
/* Level 0 lookup. */
|
||||
index = extract64(paddress, l0gptsz, pps - l0gptsz);
|
||||
tableaddr += index * 8;
|
||||
entry = address_space_ldq_le(as, tableaddr, attrs, &result);
|
||||
if (result != MEMTX_OK) {
|
||||
goto fault_eabt;
|
||||
}
|
||||
|
||||
switch (extract32(entry, 0, 4)) {
|
||||
case 1: /* block descriptor */
|
||||
if (entry >> 8) {
|
||||
goto fault_walk; /* RES0 bits not 0 */
|
||||
}
|
||||
gpi = extract32(entry, 4, 4);
|
||||
goto found;
|
||||
case 3: /* table descriptor */
|
||||
tableaddr = entry & ~0xf;
|
||||
align = MAX(l0gptsz - pgs - 1, 12);
|
||||
align = MAKE_64BIT_MASK(0, align);
|
||||
if (tableaddr & (~pps_mask | align)) {
|
||||
goto fault_walk; /* RES0 bits not 0 */
|
||||
}
|
||||
break;
|
||||
default: /* invalid */
|
||||
goto fault_walk;
|
||||
}
|
||||
|
||||
/* Level 1 lookup */
|
||||
level = 1;
|
||||
index = extract64(paddress, pgs + 4, l0gptsz - pgs - 4);
|
||||
tableaddr += index * 8;
|
||||
entry = address_space_ldq_le(as, tableaddr, attrs, &result);
|
||||
if (result != MEMTX_OK) {
|
||||
goto fault_eabt;
|
||||
}
|
||||
|
||||
switch (extract32(entry, 0, 4)) {
|
||||
case 1: /* contiguous descriptor */
|
||||
if (entry >> 10) {
|
||||
goto fault_walk; /* RES0 bits not 0 */
|
||||
}
|
||||
/*
|
||||
* Because the softmmu tlb only works on units of TARGET_PAGE_SIZE,
|
||||
* and because we cannot invalidate by pa, and thus will always
|
||||
* flush entire tlbs, we don't actually care about the range here
|
||||
* and can simply extract the GPI as the result.
|
||||
*/
|
||||
if (extract32(entry, 8, 2) == 0) {
|
||||
goto fault_walk; /* reserved contig */
|
||||
}
|
||||
gpi = extract32(entry, 4, 4);
|
||||
break;
|
||||
default:
|
||||
index = extract64(paddress, pgs, 4);
|
||||
gpi = extract64(entry, index * 4, 4);
|
||||
break;
|
||||
}
|
||||
|
||||
found:
|
||||
switch (gpi) {
|
||||
case 0b0000: /* no access */
|
||||
break;
|
||||
case 0b1111: /* all access */
|
||||
return true;
|
||||
case 0b1000:
|
||||
case 0b1001:
|
||||
case 0b1010:
|
||||
case 0b1011:
|
||||
if (pspace == (gpi & 3)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
goto fault_walk; /* reserved */
|
||||
}
|
||||
|
||||
fi->gpcf = GPCF_Fail;
|
||||
goto fault_common;
|
||||
fault_eabt:
|
||||
fi->gpcf = GPCF_EABT;
|
||||
goto fault_common;
|
||||
fault_size:
|
||||
fi->gpcf = GPCF_AddressSize;
|
||||
goto fault_common;
|
||||
fault_walk:
|
||||
fi->gpcf = GPCF_Walk;
|
||||
fault_common:
|
||||
fi->level = level;
|
||||
fi->paddr = paddress;
|
||||
fi->paddr_space = pspace;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool S2_attrs_are_device(uint64_t hcr, uint8_t attrs)
|
||||
{
|
||||
/*
|
||||
@ -276,10 +473,10 @@ static bool S1_ptw_translate(CPUARMState *env, S1Translate *ptw,
|
||||
};
|
||||
GetPhysAddrResult s2 = { };
|
||||
|
||||
if (get_phys_addr_with_struct(env, &s2ptw, addr,
|
||||
MMU_DATA_LOAD, &s2, fi)) {
|
||||
if (get_phys_addr_gpc(env, &s2ptw, addr, MMU_DATA_LOAD, &s2, fi)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ptw->out_phys = s2.f.phys_addr;
|
||||
pte_attrs = s2.cacheattrs.attrs;
|
||||
ptw->out_host = NULL;
|
||||
@ -332,6 +529,9 @@ static bool S1_ptw_translate(CPUARMState *env, S1Translate *ptw,
|
||||
|
||||
fail:
|
||||
assert(fi->type != ARMFault_None);
|
||||
if (fi->type == ARMFault_GPCFOnOutput) {
|
||||
fi->type = ARMFault_GPCFOnWalk;
|
||||
}
|
||||
fi->s2addr = addr;
|
||||
fi->stage2 = true;
|
||||
fi->s1ptw = true;
|
||||
@ -2769,7 +2969,7 @@ static bool get_phys_addr_disabled(CPUARMState *env, target_ulong address,
|
||||
ARMMMUFaultInfo *fi)
|
||||
{
|
||||
uint8_t memattr = 0x00; /* Device nGnRnE */
|
||||
uint8_t shareability = 0; /* non-sharable */
|
||||
uint8_t shareability = 0; /* non-shareable */
|
||||
int r_el;
|
||||
|
||||
switch (mmu_idx) {
|
||||
@ -2828,7 +3028,7 @@ static bool get_phys_addr_disabled(CPUARMState *env, target_ulong address,
|
||||
} else {
|
||||
memattr = 0x44; /* Normal, NC, No */
|
||||
}
|
||||
shareability = 2; /* outer sharable */
|
||||
shareability = 2; /* outer shareable */
|
||||
}
|
||||
result->cacheattrs.is_s2_format = false;
|
||||
break;
|
||||
@ -2856,7 +3056,7 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw,
|
||||
ARMSecuritySpace ipa_space;
|
||||
uint64_t hcr;
|
||||
|
||||
ret = get_phys_addr_with_struct(env, ptw, address, access_type, result, fi);
|
||||
ret = get_phys_addr_nogpc(env, ptw, address, access_type, result, fi);
|
||||
|
||||
/* If S1 fails, return early. */
|
||||
if (ret) {
|
||||
@ -2882,7 +3082,7 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw,
|
||||
cacheattrs1 = result->cacheattrs;
|
||||
memset(result, 0, sizeof(*result));
|
||||
|
||||
ret = get_phys_addr_with_struct(env, ptw, ipa, access_type, result, fi);
|
||||
ret = get_phys_addr_nogpc(env, ptw, ipa, access_type, result, fi);
|
||||
fi->s2addr = ipa;
|
||||
|
||||
/* Combine the S1 and S2 perms. */
|
||||
@ -2942,7 +3142,7 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw,
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool get_phys_addr_with_struct(CPUARMState *env, S1Translate *ptw,
|
||||
static bool get_phys_addr_nogpc(CPUARMState *env, S1Translate *ptw,
|
||||
target_ulong address,
|
||||
MMUAccessType access_type,
|
||||
GetPhysAddrResult *result,
|
||||
@ -3076,6 +3276,23 @@ static bool get_phys_addr_with_struct(CPUARMState *env, S1Translate *ptw,
|
||||
}
|
||||
}
|
||||
|
||||
static bool get_phys_addr_gpc(CPUARMState *env, S1Translate *ptw,
|
||||
target_ulong address,
|
||||
MMUAccessType access_type,
|
||||
GetPhysAddrResult *result,
|
||||
ARMMMUFaultInfo *fi)
|
||||
{
|
||||
if (get_phys_addr_nogpc(env, ptw, address, access_type, result, fi)) {
|
||||
return true;
|
||||
}
|
||||
if (!granule_protection_check(env, result->f.phys_addr,
|
||||
result->f.attrs.space, fi)) {
|
||||
fi->type = ARMFault_GPCFOnOutput;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool get_phys_addr_with_secure(CPUARMState *env, target_ulong address,
|
||||
MMUAccessType access_type, ARMMMUIdx mmu_idx,
|
||||
bool is_secure, GetPhysAddrResult *result,
|
||||
@ -3086,8 +3303,7 @@ bool get_phys_addr_with_secure(CPUARMState *env, target_ulong address,
|
||||
.in_secure = is_secure,
|
||||
.in_space = arm_secure_to_space(is_secure),
|
||||
};
|
||||
return get_phys_addr_with_struct(env, &ptw, address, access_type,
|
||||
result, fi);
|
||||
return get_phys_addr_gpc(env, &ptw, address, access_type, result, fi);
|
||||
}
|
||||
|
||||
bool get_phys_addr(CPUARMState *env, target_ulong address,
|
||||
@ -3157,8 +3373,7 @@ bool get_phys_addr(CPUARMState *env, target_ulong address,
|
||||
|
||||
ptw.in_space = ss;
|
||||
ptw.in_secure = arm_space_is_secure(ss);
|
||||
return get_phys_addr_with_struct(env, &ptw, address, access_type,
|
||||
result, fi);
|
||||
return get_phys_addr_gpc(env, &ptw, address, access_type, result, fi);
|
||||
}
|
||||
|
||||
hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr,
|
||||
@ -3178,7 +3393,7 @@ hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr,
|
||||
ARMMMUFaultInfo fi = {};
|
||||
bool ret;
|
||||
|
||||
ret = get_phys_addr_with_struct(env, &ptw, addr, MMU_DATA_LOAD, &res, &fi);
|
||||
ret = get_phys_addr_gpc(env, &ptw, addr, MMU_DATA_LOAD, &res, &fi);
|
||||
*attrs = res.f.attrs;
|
||||
|
||||
if (ret) {
|
||||
|
Loading…
Reference in New Issue
Block a user