On 2/27/26 4:20 PM, Tao Tang wrote:
> Hi Pierrick,
>
> On 2026/2/26 04:52, Pierrick Bouvier wrote:
>> On 2/21/26 2:02 AM, Tao Tang wrote:
>>> As a preliminary step towards a multi-security-state configuration
>>> cache, introduce MemTxAttrs and AddressSpace * members to the
>>> SMMUTransCfg struct. The goal is to cache these attributes so that
>>> internal functions can use them directly.
>>>
>>> To facilitate this, hw/arm/arm-security.h is now included in
>>> smmu-common.h. This is a notable change, as it marks the first time
>>> these Arm CPU-specific security space definitions are used outside of
>>> cpu.h, making them more generally available for device models.
>>>
>>> The decode helpers (smmu_get_ste, smmu_get_cd, smmu_find_ste,
>>> smmuv3_get_config) are updated to use these new attributes for memory
>>> accesses. This ensures that reads of SMMU structures from memory, such
>>> as the Stream Table, use the correct security context.
>>>
>>> For the special case of smmuv3-accel.c, we only support the NS-only
>>> path
>>> for now. Therefore, we initialize a minimal cfg with sec_sid, txattrs,
>>> and as for the NS-only accel path.
>>>
>>> For now, the configuration cache lookup key remains unchanged and is
>>> still based solely on the SMMUDevice pointer. The new attributes are
>>> populated during a cache miss in smmuv3_get_config. And some paths
>>> still
>>> rely on the NS-only address_space_memory, for example
>>> smmuv3_notify_iova
>>> and get_pte(). These will be progressively converted in follow-up
>>> commits
>>> to use an AddressSpace selected according to SEC_SID.
>>>
>>> Signed-off-by: Tao Tang <[email protected]>
>>> ---
>>> hw/arm/smmu-common.c | 19 ++++++++++++++++++
>>> hw/arm/smmuv3-accel.c | 12 +++++++++++-
>>> hw/arm/smmuv3-internal.h | 3 ++-
>>> hw/arm/smmuv3.c | 38
>>> ++++++++++++++++++++++--------------
>>> include/hw/arm/smmu-common.h | 12 ++++++++++++
>>> 5 files changed, 67 insertions(+), 17 deletions(-)
>>>
>>> diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c
>>> index 3baba2a4c8e..b320aec8c60 100644
>>> --- a/hw/arm/smmu-common.c
>>> +++ b/hw/arm/smmu-common.c
>>> @@ -30,6 +30,25 @@
>>> #include "hw/arm/smmu-common.h"
>>> #include "smmu-internal.h"
>>> +ARMSecuritySpace smmu_get_security_space(SMMUSecSID sec_sid)
>>> +{
>>> + switch (sec_sid) {
>>> + case SMMU_SEC_SID_S:
>>> + return ARMSS_Secure;
>>> + case SMMU_SEC_SID_NS:
>>> + default:
>>> + return ARMSS_NonSecure;
>>> + }
>>> +}
>>> +
>>> +MemTxAttrs smmu_get_txattrs(SMMUSecSID sec_sid)
>>> +{
>>> + return (MemTxAttrs) {
>>> + .secure = sec_sid > SMMU_SEC_SID_NS ? 1 : 0,
>>> + .space = smmu_get_security_space(sec_sid),
>>> + };
>>> +}
>>> +
>>> AddressSpace *smmu_get_address_space(SMMUState *s, SMMUSecSID
>>> sec_sid)
>>> {
>>> switch (sec_sid) {
>>> diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c
>>> index fdcb15005ea..9a41391826b 100644
>>> --- a/hw/arm/smmuv3-accel.c
>>> +++ b/hw/arm/smmuv3-accel.c
>>> @@ -244,6 +244,16 @@ bool smmuv3_accel_install_ste(SMMUv3State *s,
>>> SMMUDevice *sdev, int sid,
>>> const char *type;
>>> STE ste;
>>> SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
>>> + /*
>>> + * smmu_find_ste() requires a SMMUTransCfg to provide address
>>> space and
>>> + * transaction attributes for DMA reads. Only NS state is
>>> supported here.
>>> + */
>>> + SMMUState *bc = &s->smmu_state;
>>> + SMMUTransCfg cfg = {
>>> + .sec_sid = sec_sid,
>>> + .txattrs = smmu_get_txattrs(sec_sid),
>>> + .as = smmu_get_address_space(bc, sec_sid),
>>> + };
>>> if (!accel || !accel->viommu) {
>>> return true;
>>> @@ -259,7 +269,7 @@ bool smmuv3_accel_install_ste(SMMUv3State *s,
>>> SMMUDevice *sdev, int sid,
>>> return false;
>>> }
>>> - if (smmu_find_ste(sdev->smmu, sid, &ste, &event)) {
>>> + if (smmu_find_ste(sdev->smmu, sid, &ste, &event, &cfg)) {
>>> /* No STE found, nothing to install */
>>> return true;
>>> }
>>> diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h
>>> index a1071f7b689..6d29b9027f0 100644
>>> --- a/hw/arm/smmuv3-internal.h
>>> +++ b/hw/arm/smmuv3-internal.h
>>> @@ -363,7 +363,8 @@ typedef struct SMMUEventInfo {
>>> } while (0)
>>> void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *event);
>>> -int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste,
>>> SMMUEventInfo *event);
>>> +int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste,
>>> SMMUEventInfo *event,
>>> + SMMUTransCfg *cfg);
>>> static inline int oas2bits(int oas_field)
>>> {
>>> diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
>>> index 3438adcecd2..2192bec2368 100644
>>> --- a/hw/arm/smmuv3.c
>>> +++ b/hw/arm/smmuv3.c
>>> @@ -347,14 +347,13 @@ static void smmuv3_reset(SMMUv3State *s)
>>> }
>>> static int smmu_get_ste(SMMUv3State *s, dma_addr_t addr, STE *buf,
>>> - SMMUEventInfo *event)
>>> + SMMUEventInfo *event, SMMUTransCfg *cfg)
>>> {
>>> int ret, i;
>>> trace_smmuv3_get_ste(addr);
>>> /* TODO: guarantee 64-bit single-copy atomicity */
>>> - ret = dma_memory_read(&address_space_memory, addr, buf,
>>> sizeof(*buf),
>>> - MEMTXATTRS_UNSPECIFIED);
>>> + ret = dma_memory_read(cfg->as, addr, buf, sizeof(*buf),
>>> cfg->txattrs);
>>> if (ret != MEMTX_OK) {
>>> qemu_log_mask(LOG_GUEST_ERROR,
>>> "Cannot fetch pte at address=0x%"PRIx64"\n",
>>> addr);
>>> @@ -399,8 +398,7 @@ static int smmu_get_cd(SMMUv3State *s, STE *ste,
>>> SMMUTransCfg *cfg,
>>> }
>>> /* TODO: guarantee 64-bit single-copy atomicity */
>>> - ret = dma_memory_read(&address_space_memory, addr, buf,
>>> sizeof(*buf),
>>> - MEMTXATTRS_UNSPECIFIED);
>>> + ret = dma_memory_read(cfg->as, addr, buf, sizeof(*buf),
>>> cfg->txattrs);
>>> if (ret != MEMTX_OK) {
>>> qemu_log_mask(LOG_GUEST_ERROR,
>>> "Cannot fetch pte at address=0x%"PRIx64"\n",
>>> addr);
>>> @@ -655,17 +653,19 @@ bad_ste:
>>> * @sid: stream ID
>>> * @ste: returned stream table entry
>>> * @event: handle to an event info
>>> + * @cfg: translation configuration cache
>>> *
>>> * Supports linear and 2-level stream table
>>> * Return 0 on success, -EINVAL otherwise
>>> */
>>> -int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste,
>>> SMMUEventInfo *event)
>>> +int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste,
>>> SMMUEventInfo *event,
>>> + SMMUTransCfg *cfg)
>>> {
>>> dma_addr_t addr, strtab_base;
>>> uint32_t log2size;
>>> int strtab_size_shift;
>>> int ret;
>>> - SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
>>> + SMMUSecSID sec_sid = cfg->sec_sid;
>>> SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
>>> trace_smmuv3_find_ste(sid, bank->features, bank->sid_split);
>>> @@ -693,8 +693,8 @@ int smmu_find_ste(SMMUv3State *s, uint32_t sid,
>>> STE *ste, SMMUEventInfo *event)
>>> l2_ste_offset = sid & ((1 << bank->sid_split) - 1);
>>> l1ptr = (dma_addr_t)(strtab_base + l1_ste_offset *
>>> sizeof(l1std));
>>> /* TODO: guarantee 64-bit single-copy atomicity */
>>> - ret = dma_memory_read(&address_space_memory, l1ptr, &l1std,
>>> - sizeof(l1std), MEMTXATTRS_UNSPECIFIED);
>>> + ret = dma_memory_read(cfg->as, l1ptr, &l1std, sizeof(l1std),
>>> + cfg->txattrs);
>>> if (ret != MEMTX_OK) {
>>> qemu_log_mask(LOG_GUEST_ERROR,
>>> "Could not read L1PTR at 0X%"PRIx64"\n",
>>> l1ptr);
>>> @@ -736,7 +736,7 @@ int smmu_find_ste(SMMUv3State *s, uint32_t sid,
>>> STE *ste, SMMUEventInfo *event)
>>> addr = strtab_base + sid * sizeof(*ste);
>>> }
>>> - if (smmu_get_ste(s, addr, ste, event)) {
>>> + if (smmu_get_ste(s, addr, ste, event, cfg)) {
>>> return -EINVAL;
>>> }
>>> @@ -865,7 +865,7 @@ static int
>>> smmuv3_decode_config(IOMMUMemoryRegion *mr, SMMUTransCfg *cfg,
>>> /* ASID defaults to -1 (if s1 is not supported). */
>>> cfg->asid = -1;
>>> - ret = smmu_find_ste(s, sid, &ste, event);
>>> + ret = smmu_find_ste(s, sid, &ste, event, cfg);
>>> if (ret) {
>>> return ret;
>>> }
>>> @@ -899,7 +899,8 @@ static int
>>> smmuv3_decode_config(IOMMUMemoryRegion *mr, SMMUTransCfg *cfg,
>>> * decoding under the form of an SMMUTransCfg struct. The hash
>>> table is indexed
>>> * by the SMMUDevice handle.
>>> */
>>> -static SMMUTransCfg *smmuv3_get_config(SMMUDevice *sdev,
>>> SMMUEventInfo *event)
>>> +static SMMUTransCfg *smmuv3_get_config(SMMUDevice *sdev,
>>> SMMUEventInfo *event,
>>> + SMMUSecSID sec_sid)
>>> {
>>> SMMUv3State *s = sdev->smmu;
>>> SMMUState *bc = &s->smmu_state;
>>> @@ -919,7 +920,14 @@ static SMMUTransCfg
>>> *smmuv3_get_config(SMMUDevice *sdev, SMMUEventInfo *event)
>>> 100 * sdev->cfg_cache_hits /
>>> (sdev->cfg_cache_hits +
>>> sdev->cfg_cache_misses));
>>> cfg = g_new0(SMMUTransCfg, 1);
>>> - cfg->sec_sid = SMMU_SEC_SID_NS;
>>> + cfg->sec_sid = sec_sid;
>>> + cfg->txattrs = smmu_get_txattrs(sec_sid);
>>> + cfg->as = smmu_get_address_space(bc, sec_sid);
>>> + cfg->ns_as = (sec_sid > SMMU_SEC_SID_NS)
>>> + ? smmu_get_address_space(bc, SMMU_SEC_SID_NS)
>>> + : cfg->as;
>>> + /* AddressSpace must be available, assert if not. */
>>> + g_assert(cfg->as && cfg->ns_as);
>>> if (!smmuv3_decode_config(&sdev->iommu, cfg, event)) {
>>> g_hash_table_insert(bc->configs, sdev, cfg);
>>> @@ -1101,7 +1109,7 @@ static IOMMUTLBEntry
>>> smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
>>> goto epilogue;
>>> }
>>> - cfg = smmuv3_get_config(sdev, &event);
>>> + cfg = smmuv3_get_config(sdev, &event, sec_sid);
>>> if (!cfg) {
>>> status = SMMU_TRANS_ERROR;
>>> goto epilogue;
>>> @@ -1183,7 +1191,7 @@ static void
>>> smmuv3_notify_iova(IOMMUMemoryRegion *mr,
>>> SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
>>> SMMUEventInfo eventinfo = {.sec_sid = sec_sid,
>>> .inval_ste_allowed = true};
>>> - SMMUTransCfg *cfg = smmuv3_get_config(sdev, &eventinfo);
>>> + SMMUTransCfg *cfg = smmuv3_get_config(sdev, &eventinfo, sec_sid);
>>> IOMMUTLBEvent event;
>>> uint8_t granule;
>>> diff --git a/include/hw/arm/smmu-common.h
>>> b/include/hw/arm/smmu-common.h
>>> index b3ca55effc5..7944e8d1b64 100644
>>> --- a/include/hw/arm/smmu-common.h
>>> +++ b/include/hw/arm/smmu-common.h
>>> @@ -22,6 +22,7 @@
>>> #include "hw/core/sysbus.h"
>>> #include "hw/pci/pci.h"
>>> #include "qom/object.h"
>>> +#include "hw/arm/arm-security.h"
>>> #define SMMU_PCI_BUS_MAX 256
>>> #define SMMU_PCI_DEVFN_MAX 256
>>> @@ -47,6 +48,9 @@ typedef enum SMMUSecSID {
>>> SMMU_SEC_SID_NUM,
>>> } SMMUSecSID;
>>> +MemTxAttrs smmu_get_txattrs(SMMUSecSID sec_sid);
>>> +ARMSecuritySpace smmu_get_security_space(SMMUSecSID sec_sid);
>>> +
>>> /*
>>> * Page table walk error types
>>> */
>>> @@ -124,6 +128,14 @@ typedef struct SMMUTransCfg {
>>> SMMUTransTableInfo tt[2];
>>> /* Used by stage-2 only. */
>>> struct SMMUS2Cfg s2cfg;
>>> + MemTxAttrs txattrs; /* cached transaction attributes */
>>> + /*
>>> + * Cached AS related to the SEC_SID, which will be statically
>>> marked, and
>>> + * in future RME support it will be implemented as a dynamic
>>> switch.
>>> + */
>>> + AddressSpace *as;
>>> + /* Cached NS AS. It will be used if previous SEC_SID !=
>>> SMMU_SEC_SID_NS. */
>>> + AddressSpace *ns_as;
>>> } SMMUTransCfg;
>>>
>>
>> As an alternative, you can simply pass SMMUState where it's missing
>> (like smmu_ptw_64_s2) and call smmu_get_address_space from there.
>> It will avoid having to keep this in cfg.
>>
>> Maybe there is another reason I missed?
>
>
> My original motivation for having an extra ns_as (and caching
> as/txattrs) was tied to the architectural semantics: a transaction
> being "secure" does not automatically mean the walk can access Secure
> PAS. Whether the walk can touch Secure PAS (or is effectively
> constrained to Non-secure PAS) is determined by the STE/CD fields and
> the page-table attributes at each level; in an RME setup we would also
> add GPC checks. So, if the combined state ends up forcing Non-secure
> PAS, the intent was to reuse a preselected NS address space rather
> than re-deriving it repeatedly during the walk.
>
> That said, I agree that keeping as/txattrs (and ns_as) inside
> SMMUTransCfg is debatable, and it also blurs “decoded translation
> config” versus “per-walk execution context”. Eric previously suggested
> caching as and MemTxAttrs in v2 to streamline the hot path:
I was rather suggesting splitting the patch to ease the review ;-)
>
> https://lore.kernel.org/qemu-devel/[email protected]/
>
>
>
> But given your feedback, I’m leaning toward your alternative. In
> particular, with your recent commit (53b54a8 "add memory regions as
> property for an SMMU instance"), selecting the appropriate
> AddressSpace is very cheap. So I think it’s reasonable to refactor
> now: pass SMMUState where it’s missing (e.g. in smmu_ptw_64_s2) and
> compute AddressSpace/MemTxAttrs on-demand based on the current walk
> state, instead of caching them in SMMUTransCfg.
>
SMMUTransCfg is rather supposed to cache info that were grabbed from
STE/CD with smmuv3_decode_config(). sec_sid rather is a property of the
DMA initiator [28/31] and thus does not relate to the SMMUTransConfig I
think. So I agree with Pierrick.
Thanks
Eric
>
> I’ll rework the patch accordingly, and I’d also very much welcome any
> further comments or suggestions from others.
>
>
>>
>> Regards,
>> Pierrick
>
>
> Thanks,
>
> Tao
>