include/hw/arm/arm-security.h | 37 ++++++++++++++++++++++ target/arm/cpu.h | 59 +++++++++++++++++++++-------------- target/arm/ptw.c | 59 ++++++++++++++++++++++------------- 3 files changed, 110 insertions(+), 45 deletions(-) create mode 100644 include/hw/arm/arm-security.h
This series prepare granule_protection_check to be usable from SMMU, for
implementing RME feature.
It's based on Tao's commit [1] extracting ARMSecuritySpace from cpu.h header for
convenience.
[1] https://lore.kernel.org/qemu-devel/20251012150701.4127034-5-tangtao1634@phytium.com.cn/
To demonstrate the purpose, this is the (wip) change to use that from SMMU:
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index 80f48df3dda..1acff3bbd66 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -1055,12 +1056,36 @@ static SMMUTranslationStatus smmuv3_do_translate(SMMUv3State *s, hwaddr addr,
}
cached_entry = smmu_translate(bs, cfg, addr, flag, &ptw_info);
if (desc_s2_translation) {
cfg->asid = asid;
cfg->stage = stage;
}
+ if (cached_entry) {
+ /* The fields in SMMU_ROOT_GPT_BASE_CFG are the same as for GPCCR_EL3,
+ * except there is no copy of GPCCR_EL3.GPC. See SMMU_ROOT_CR0.GPCEN. */
+ const bool gpc_enabled = FIELD_EX32(s->root.cr0, ROOT_CR0, GPCEN);
+ if (gpc_enabled) {
+ hwaddr paddress = CACHED_ENTRY_TO_ADDR(cached_entry, addr);
+ ARMSecuritySpace pspace = sec_sid_to_security_space(cfg->sec_sid);
+ ARMSecuritySpace ss = ARMSS_Root;
+ ARMMMUFaultInfo fi;
+
+ ARMGranuleProtectionConfig config = {
+ .gpccr = s->root.gpt_base_cfg,
+ .gptbr = s->root.gpt_base >> 12,
+ .parange = 6, /* 52 bits */
+ .support_sel2 = false,
+ .gpt_as = &s->smmu_state.as_secure_memory
+ };
+ if (!arm_granule_protection_check(config, paddress,
+ pspace, ss, &fi)) {
+ printf("ERROR: fi.type=%d fi.gpcf=%d\n", fi.type, fi.gpcf);
+ g_assert_not_reached();
+ }
+ }
+ }
+
if (!cached_entry) {
/* All faults from PTW has S2 field. */
event->u.f_walk_eabt.s2 = (ptw_info.stage == SMMU_STAGE_2);
v2
--
- remove extra boilerplate/include/line from arm-security.h
- use local var gpccr instead of config.gpccr
- extract GPC enable check out of arm_granule_protection_check
- rename as_secure in gpt_as
- use arm_addresspace to retrieve gpt address space
Pierrick Bouvier (1):
target/arm/ptw: make granule_protection_check usable without a cpu
Tao Tang (1):
target/arm: Move ARMSecuritySpace to a common header
include/hw/arm/arm-security.h | 37 ++++++++++++++++++++++
target/arm/cpu.h | 59 +++++++++++++++++++++--------------
target/arm/ptw.c | 59 ++++++++++++++++++++++-------------
3 files changed, 110 insertions(+), 45 deletions(-)
create mode 100644 include/hw/arm/arm-security.h
--
2.47.3
From: Tao Tang <tangtao1634@phytium.com.cn>
The ARMSecuritySpace enum and its related helpers were defined in the
target-specific header target/arm/cpu.h. This prevented common,
target-agnostic code like the SMMU model from using these definitions
without triggering "cpu.h included from common code" errors.
To resolve this, this commit introduces a new, lightweight header,
include/hw/arm/arm-security.h, which is safe for inclusion by common
code.
The following change was made:
- The ARMSecuritySpace enum and the arm_space_is_secure() and
arm_secure_to_space() helpers have been moved from target/arm/cpu.h
to the new hw/arm/arm-security.h header.
This refactoring decouples the security state definitions from the core
CPU implementation, allowing common hardware models to correctly handle
security states without pulling in heavyweight, target-specific headers.
Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Link: https://lists.nongnu.org/archive/html/qemu-arm/2025-09/msg01288.html
Signed-off-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
---
include/hw/arm/arm-security.h | 37 +++++++++++++++++++++++++++++++++++
target/arm/cpu.h | 25 +----------------------
2 files changed, 38 insertions(+), 24 deletions(-)
create mode 100644 include/hw/arm/arm-security.h
diff --git a/include/hw/arm/arm-security.h b/include/hw/arm/arm-security.h
new file mode 100644
index 00000000000..196cddd14c9
--- /dev/null
+++ b/include/hw/arm/arm-security.h
@@ -0,0 +1,37 @@
+/*
+ * ARM security space helpers
+ *
+ * Provide ARMSecuritySpace and helpers for code that is not tied to CPU.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_ARM_ARM_SECURITY_H
+#define HW_ARM_ARM_SECURITY_H
+
+/*
+ * ARM v9 security states.
+ * The ordering of the enumeration corresponds to the low 2 bits
+ * of the GPI value, and (except for Root) the concat of NSE:NS.
+ */
+
+ typedef enum ARMSecuritySpace {
+ ARMSS_Secure = 0,
+ ARMSS_NonSecure = 1,
+ ARMSS_Root = 2,
+ ARMSS_Realm = 3,
+} ARMSecuritySpace;
+
+/* Return true if @space is secure, in the pre-v9 sense. */
+static inline bool arm_space_is_secure(ARMSecuritySpace space)
+{
+ return space == ARMSS_Secure || space == ARMSS_Root;
+}
+
+/* Return the ARMSecuritySpace for @secure, assuming !RME or EL[0-2]. */
+static inline ARMSecuritySpace arm_secure_to_space(bool secure)
+{
+ return secure ? ARMSS_Secure : ARMSS_NonSecure;
+}
+
+#endif /* HW_ARM_ARM_SECURITY_H */
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 39f2b2e54de..efbef0341da 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -31,6 +31,7 @@
#include "exec/page-protection.h"
#include "qapi/qapi-types-common.h"
#include "target/arm/multiprocessing.h"
+#include "hw/arm/arm-security.h"
#include "target/arm/gtimer.h"
#include "target/arm/cpu-sysregs.h"
#include "target/arm/mmuidx.h"
@@ -2102,30 +2103,6 @@ static inline int arm_feature(CPUARMState *env, int feature)
void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp);
-/*
- * ARM v9 security states.
- * The ordering of the enumeration corresponds to the low 2 bits
- * of the GPI value, and (except for Root) the concat of NSE:NS.
- */
-
-typedef enum ARMSecuritySpace {
- ARMSS_Secure = 0,
- ARMSS_NonSecure = 1,
- ARMSS_Root = 2,
- ARMSS_Realm = 3,
-} ARMSecuritySpace;
-
-/* Return true if @space is secure, in the pre-v9 sense. */
-static inline bool arm_space_is_secure(ARMSecuritySpace space)
-{
- return space == ARMSS_Secure || space == ARMSS_Root;
-}
-
-/* Return the ARMSecuritySpace for @secure, assuming !RME or EL[0-2]. */
-static inline ARMSecuritySpace arm_secure_to_space(bool secure)
-{
- return secure ? ARMSS_Secure : ARMSS_NonSecure;
-}
#if !defined(CONFIG_USER_ONLY)
/**
--
2.47.3
By removing cpu details and use a config struct, we can use the
same granule_protection_check with other devices, like SMMU.
Signed-off-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
---
target/arm/cpu.h | 34 ++++++++++++++++++++++++++++
target/arm/ptw.c | 59 +++++++++++++++++++++++++++++++-----------------
2 files changed, 72 insertions(+), 21 deletions(-)
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;
}
--
2.47.3
© 2016 - 2025 Red Hat, Inc.