diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index efbef0341da..5752e1f58fb 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -1216,6 +1216,40 @@ void arm_v7m_cpu_do_interrupt(CPUState *cpu);
hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr,
MemTxAttrs *attrs);
+
+typedef struct ARMGranuleProtectionConfig {
+ /* GPCCR_EL3 */
+ uint64_t gpccr;
+ /* GPTBR_EL3 */
+ uint64_t gptbr;
+ /* ID_AA64MMFR0_EL1.PARange */
+ uint8_t parange;
+ /* FEAT_SEL2 */
+ bool support_sel2;
+ /* Address space to access Granule Protection Table */
+ AddressSpace *gpt_as;
+} ARMGranuleProtectionConfig;
+
+/**
+ * arm_granule_protection_check
+ * @config: granule protection configuration
+ * @paddress: address accessed
+ * @pspace: physical address space accessed
+ * @ss: security state for access
+ * @fi: fault information in case a fault is detected
+ *
+ * Checks if @paddress can be accessed in physical adress space @pspace
+ * for @ss secure state, following granule protection setup with @config.
+ * If a fault is detected, @fi is set accordingly.
+ * See GranuleProtectionCheck() in A-profile manual.
+ *
+ * Returns: true if access is authorized, else false.
+ */
+bool arm_granule_protection_check(ARMGranuleProtectionConfig config,
+ uint64_t paddress,
+ ARMSecuritySpace pspace,
+ ARMSecuritySpace ss,
+ ARMMMUFaultInfo *fi);
#endif /* !CONFIG_USER_ONLY */
int arm_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg);
diff --git a/target/arm/ptw.c b/target/arm/ptw.c
index 2e6b149b2d1..cdc14b1735b 100644
--- a/target/arm/ptw.c
+++ b/target/arm/ptw.c
@@ -330,26 +330,26 @@ 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,
- ARMSecuritySpace ss,
- ARMMMUFaultInfo *fi)
+bool arm_granule_protection_check(ARMGranuleProtectionConfig config,
+ uint64_t paddress,
+ ARMSecuritySpace pspace,
+ ARMSecuritySpace ss,
+ ARMMMUFaultInfo *fi)
{
MemTxAttrs attrs = {
.secure = true,
.space = ARMSS_Root,
};
- ARMCPU *cpu = env_archcpu(env);
- uint64_t gpccr = env->cp15.gpccr_el3;
+ const uint64_t gpccr = config.gpccr;
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;
- }
+ /*
+ * We assume Granule Protection Check is enabled when
+ * calling this function (GPCCR.GPC == 1).
+ */
/*
* GPC Priority 1 (R_GMGRR):
@@ -362,7 +362,7 @@ static bool granule_protection_check(CPUARMState *env,
uint64_t paddress,
* physical address size is invalid.
*/
pps = FIELD_EX64(gpccr, GPCCR, PPS);
- if (pps > FIELD_EX64_IDREG(&cpu->isar, ID_AA64MMFR0, PARANGE)) {
+ if (pps > config.parange) {
goto fault_walk;
}
pps = pamax_map[pps];
@@ -432,7 +432,7 @@ static bool granule_protection_check(CPUARMState *env,
uint64_t paddress,
}
/* GPC Priority 4: the base address of GPTBR_EL3 exceeds PPS. */
- tableaddr = env->cp15.gptbr_el3 << 12;
+ tableaddr = config.gptbr << 12;
if (tableaddr & ~pps_mask) {
goto fault_size;
}
@@ -446,12 +446,10 @@ static bool granule_protection_check(CPUARMState *env,
uint64_t paddress,
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);
+ entry = address_space_ldq_le(config.gpt_as, tableaddr, attrs, &result);
if (result != MEMTX_OK) {
goto fault_eabt;
}
@@ -479,7 +477,7 @@ static bool granule_protection_check(CPUARMState *env,
uint64_t paddress,
level = 1;
index = extract64(paddress, pgs + 4, l0gptsz - pgs - 4);
tableaddr += index * 8;
- entry = address_space_ldq_le(as, tableaddr, attrs, &result);
+ entry = address_space_ldq_le(config.gpt_as, tableaddr, attrs, &result);
if (result != MEMTX_OK) {
goto fault_eabt;
}
@@ -513,7 +511,7 @@ static bool granule_protection_check(CPUARMState *env,
uint64_t paddress,
case 0b1111: /* all access */
return true;
case 0b1000: /* secure */
- if (!cpu_isar_feature(aa64_sel2, cpu)) {
+ if (!config.support_sel2) {
goto fault_walk;
}
/* fall through */
@@ -3786,11 +3784,30 @@ static bool get_phys_addr_gpc(CPUARMState *env,
S1Translate *ptw,
memop, result, fi)) {
return true;
}
- if (!granule_protection_check(env, result->f.phys_addr,
- result->f.attrs.space, ptw->in_space, fi)) {
- fi->type = ARMFault_GPCFOnOutput;
- return true;
+
+ const uint64_t gpccr = env->cp15.gpccr_el3;
+ const bool gpc_enabled = FIELD_EX64(gpccr, GPCCR, GPC);
+ if (gpc_enabled) {
+ ARMCPU *cpu = env_archcpu(env);
+ MemTxAttrs attrs = {
+ .secure = true,
+ .space = ARMSS_Root,
+ };
+ struct ARMGranuleProtectionConfig config = {
+ .gpccr = env->cp15.gpccr_el3,
+ .gptbr = env->cp15.gptbr_el3,
+ .parange = FIELD_EX64_IDREG(&cpu->isar, ID_AA64MMFR0, PARANGE),
+ .support_sel2 = cpu_isar_feature(aa64_sel2, cpu),
+ .gpt_as = arm_addressspace(env_cpu(env), attrs)
+ };
+ if (!arm_granule_protection_check(config, result->f.phys_addr,
+ result->f.attrs.space, ptw->in_space,
+ fi)) {
+ fi->type = ARMFault_GPCFOnOutput;
+ return true;
+ }
}
+
return false;
}