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
On 12/15/25 4:01 PM, Pierrick Bouvier wrote:
> 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
>
Another gentle ping.
This series has been reviewed and should be ready to be pulled.
Regards,
Pierrick
On Mon, 12 Jan 2026 at 16:29, Pierrick Bouvier
<pierrick.bouvier@linaro.org> wrote:
>
> On 12/15/25 4:01 PM, Pierrick Bouvier wrote:
> > 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.
> Another gentle ping.
> This series has been reviewed and should be ready to be pulled.
Applied to target-arm.next, thanks. I folded in this minor change
which avoids the "local variables not declared at beginning of block"
style issue:
--- a/target/arm/ptw.c
+++ b/target/arm/ptw.c
@@ -3785,9 +3785,7 @@ static bool get_phys_addr_gpc(CPUARMState *env,
S1Translate *ptw,
return true;
}
- const uint64_t gpccr = env->cp15.gpccr_el3;
- const bool gpc_enabled = FIELD_EX64(gpccr, GPCCR, GPC);
- if (gpc_enabled) {
+ if (FIELD_EX64(env->cp15.gpccr_el3, GPCCR, GPC)) {
ARMCPU *cpu = env_archcpu(env);
MemTxAttrs attrs = {
.secure = true,
-- PMM
On 1/12/26 9:01 AM, Peter Maydell wrote:
> On Mon, 12 Jan 2026 at 16:29, Pierrick Bouvier
> <pierrick.bouvier@linaro.org> wrote:
>>
>> On 12/15/25 4:01 PM, Pierrick Bouvier wrote:
>>> 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.
>
>
>> Another gentle ping.
>> This series has been reviewed and should be ready to be pulled.
>
> Applied to target-arm.next, thanks. I folded in this minor change
> which avoids the "local variables not declared at beginning of block"
> style issue:
>
> --- a/target/arm/ptw.c
> +++ b/target/arm/ptw.c
> @@ -3785,9 +3785,7 @@ static bool get_phys_addr_gpc(CPUARMState *env,
> S1Translate *ptw,
> return true;
> }
>
> - const uint64_t gpccr = env->cp15.gpccr_el3;
> - const bool gpc_enabled = FIELD_EX64(gpccr, GPCCR, GPC);
> - if (gpc_enabled) {
> + if (FIELD_EX64(env->cp15.gpccr_el3, GPCCR, GPC)) {
> ARMCPU *cpu = env_archcpu(env);
> MemTxAttrs attrs = {
> .secure = true,
>
> -- PMM
Ok, thanks.
On 12/15/25 4:01 PM, Pierrick Bouvier wrote:
> 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
>
Gentle ping on this series. It has been reviewed and can be merged.
Regards,
Pierrick
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
On 16/12/25 01:01, Pierrick Bouvier wrote: > 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 Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
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
On 12/16/25 11:01, Pierrick Bouvier wrote:
> 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(-)
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
r~
>
> 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;
> }
>
Hi Pierrick,
On 2025/12/16 08:01, Pierrick Bouvier wrote:
> 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;
Would it make sense to add brief comments explaining how the fields in
ARMGranuleProtectionConfig map to SMMU registers? This could help
clarify that arm_granule_protection_check() is intended as a shared
abstraction for both CPU MMU and SMMU.
That said, I’m also happy to defer this until we actually add the SMMU
RME support.
Regards,
Tao
On 12/16/25 5:39 AM, Tao Tang wrote:
> Hi Pierrick,
>
> On 2025/12/16 08:01, Pierrick Bouvier wrote:
>> 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;
>
> Would it make sense to add brief comments explaining how the fields in
> ARMGranuleProtectionConfig map to SMMU registers? This could help
> clarify that arm_granule_protection_check() is intended as a shared
> abstraction for both CPU MMU and SMMU.
>
I'm not sure it's needed.
If we search for GPCCR/GPTBR/FEAT_SEL2 in SMMU spec, it will
unambigously give the answer of which register holds it or what it is.
I would consider the comment is here to map local name to spec name, but
not further than that.
Concerning finding which are the callers of this function, code
completion tools or grep does a great job at it.
That said, if we want to add details about SMMU using this, it's
definitely worth waiting for the patch using it on SMMU side.
>
> That said, I’m also happy to defer this until we actually add the SMMU
> RME support.
>
>
> Regards,
>
> Tao
>
Thanks,
Pierrick
© 2016 - 2026 Red Hat, Inc.