All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3
@ 2026-08-13 16:15 Tao Tang
  2026-08-13 16:21 ` [RFC v5 23/28] hw/pci: Add sec-sid property to PCIDevice Tao Tang
                   ` (27 more replies)
  0 siblings, 28 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:15 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell, Michael S . Tsirkin
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Gustavo Romero, Jim MacArthur, Tao Tang

Hi all,

This is v5 of the Secure SMMUv3 series.

Many thanks to everyone who reviewed the earlier versions. This version contains
28 commits in total. Only one patch touches hw/pci (patch #23); the remaining
patches are confined to the Arm SMMUv3 model. Also some patches in v4 have been
merged into the mainline QEMU tree, so the v5 series is now smaller than v4.

Secure Stage 2 is not implemented. Secure STEs which enable Stage 2 are
rejected with C_BAD_STE. Realm and Root programming interfaces are outside the
scope of this series.

Commits layout:
---------------
The commits are roughly grouped as follows:
1. (#23, hw/pci only) Add a PCIDevice "sec-sid" property so boards can mark a
  device's SEC_SID (system integration knob).
2. (#1-2, #24-27) Core data-structure and plumbing refactors.
3. (#3-8) Introduce SEC_SID through caches, notifiers, and helpers.
4. (#9-12) Page table walk related updates (e.g. NSCFG/PTE helpers).
5. (#13-15) EVTQ/CMDQ handling and invalidation paths.
6. (#16-22) MMIO/register access checks and architectural corner cases.
7. (#28) A temporary [NOT-MERGE] commit used with Hafnium to exercise
  secure-bank register access.


Main updates:
-------------
- (#23, hw/pci only) Add a "sec-sid" property to PCIDevice and consume it from
  the SMMU in #24 to select the security context and validate capabilities.

  @Michael: could you please review the hw/pci change
  ("hw/pci: Add sec-sid property to PCIDevice") and confirm this kind of
  system-integration property is acceptable for the PCI subtree?

  For context, I previously posted this as a standalone patch [1]:
  [1] https://lore.kernel.org/qemu-devel/20251211102729.227376-1-tangtao1634@phytium.com.cn/

  The commit in this v5 series is an updated version based on Pierrick's
  feedback (notably switching sec-sid to a string property). We also discussed
  the need for this knob as a prerequisite for accurately modelling SMMU secure
  state here [2]:

  [2] https://lore.kernel.org/qemu-devel/4870b7df-4cb3-457e-9a18-87f3558adf09@linaro.org/

  We do need a stable way for boards to statically mark devices that may issue
  Secure transactions.

other updates:
- Drop the CR0 and CFGI_CD fixes which are already upstream.
- Select MemTxAttrs and AddressSpace on demand from SEC_SID and keep the
  programming-interface namespace in the configuration and IOTLB keys.
- Route CMDQ, EventQ, IRQ and GERROR handling through the originating
  programming-interface bank.
- Tighten MMIO RAZ/WI, writability and reserved-bit handling.
- Reject unsupported non-NS IOMMU notifier registration.
- Make secure-impl an on/off/auto property, resolve auto from the Secure
  AddressSpace, and require Stage 1 support when enabled.


Testing notes:
--------------
- Testing MMIO accesses
  The branch currently contains one commit explicitly marked [NOT-MERGE]. It is
  only used to cooperate with Hafnium to exercise and validate secure-bank
  register read/write paths in a small, reproducible setup as described in [3]:
  [3] https://hnusdr.github.io/2025/08/09/Test-Secure-SMMU-with-Hafnium-ENG/

  This commit is not intended for upstream and will be dropped before posting
  the final mergeable series.

  For reference, the Hafnium test logs show the SMMU being initialized
  successfully:

  INFO: Loading VM id 0x8001: op-tee.
  INFO: Loaded with 4 vCPUs, entry at 0xe300000.
  INFO: Hafnium initialisation completed

  With all smmu* traces enabled in QEMU, we can also observe secure-bank MMIO
  accesses and secure CMDQ consumption (sec_sid=1), including CFGI invalidations
  with ssec=1.

- Testing DMA translation
  DMA translation/invalidation is tested with qtest and iommu-testdev.
  The required secure/space extensions are still under RFC at [4].
  Branch [5] adds the integration needed to test this series.

  [4] https://gitlab.com/TaoTang/qemu/tree/qtest-secure-v5
  [5] https://gitlab.com/TaoTang/qemu/tree/integration/secure-smmu-v5-qtest

  This covers Secure S1 translation and the unsupported SEL2 negative case:

    QTEST_QEMU_BINARY=./build/qemu-system-aarch64 \
      ./build/tests/qtest/iommu-smmuv3-test --tap -k \
      -p /aarch64/iommu-testdev/translation/secure-s1-only

    QTEST_QEMU_BINARY=./build/qemu-system-aarch64 \
      ./build/tests/qtest/iommu-smmuv3-test --tap -k \
      -p /aarch64/iommu-testdev/translation/secure-s2-only-bad-ste


The patches are available in:
https://gitlab.com/TaoTang/qemu/tree/series/secure-smmu-v5

CI is all green:
https://gitlab.com/TaoTang/qemu/-/pipelines/2757878178

Best regards,
Tao


Tao Tang (28):
  hw/arm/smmuv3: Introduce secure registers
  hw/arm/smmuv3: Introduce banked registers for SMMUv3 state
  hw/arm/smmuv3: Thread SEC_SID through helper APIs
  hw/arm/smmuv3: Track SEC_SID in configs and events
  hw/arm/smmu-common: Add security-aware address space selector
  hw/arm/smmuv3: Plumb transaction attributes into config helpers
  hw/arm/smmuv3: Reject secure STEs with stage-2 enabled
  hw/arm/smmu-common: Key configuration cache on SMMUDevice and SEC_SID
  hw/arm/smmu: Add PTE NS/NSTable helpers
  hw/arm/smmuv3: Store CD NSCFG in TT info
  hw/arm/smmu-common: Implement secure state handling in ptw
  hw/arm/smmuv3: Tag IOTLB cache keys with SEC_SID
  hw/arm/smmuv3: Pass sec_sid into cmdq consume path
  hw/arm/smmuv3: Make evtq producer use SEC_SID
  hw/arm/smmu: Make CMDQ invalidation security-state aware
  hw/arm/smmuv3: Add access checks for GERROR_IRQ_CFG registers
  hw/arm/smmuv3: Add access checks for STRTAB_BASE and CR2 registers
  hw/arm/smmuv3: Add access checks for CMDQ and EVENTQ registers
  hw/arm/smmuv3: Determine register bank from MMIO offset
  hw/arm/smmuv3: Route IRQ and GERROR handling by SEC_SID
  hw/arm/smmuv3: Implement SMMU_S_INIT register
  hw/arm/smmuv3: Harden security checks in MMIO handlers
  hw/pci: Add sec-sid property to PCIDevice
  hw/arm/smmuv3: Select sec-sid from PCI property and validate
    SECURE_IMPL
  hw/arm/smmuv3: Reject IOMMU notifiers for non-NS devices
  hw/arm/smmuv3: Initialize the secure register bank
  hw/arm/smmuv3: Add secure bank migration and secure-impl property
  [NOT-MERGE] hw/arm/smmuv3: temporarily enable SEL2 bit and some other
    features

 hw/arm/smmu-common.c           |  406 +++++++--
 hw/arm/smmu-internal.h         |   13 +
 hw/arm/smmuv3-accel-stubs.c    |    6 +-
 hw/arm/smmuv3-accel.c          |  104 ++-
 hw/arm/smmuv3-accel.h          |    6 +-
 hw/arm/smmuv3-internal.h       |   39 +-
 hw/arm/smmuv3.c                | 1426 +++++++++++++++++++++++++-------
 hw/arm/tegra241-cmdqv.c        |    6 +-
 hw/arm/trace-events            |   37 +-
 hw/pci/pci.c                   |    7 +
 include/hw/arm/smmu-common.h   |   74 +-
 include/hw/arm/smmuv3-common.h |   77 +-
 include/hw/arm/smmuv3.h        |   33 +-
 include/hw/pci/pci_device.h    |    3 +
 14 files changed, 1780 insertions(+), 457 deletions(-)

--
2.34.1



^ permalink raw reply	[flat|nested] 30+ messages in thread

* [RFC v5 23/28] hw/pci: Add sec-sid property to PCIDevice
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
@ 2026-08-13 16:21 ` Tao Tang
  2026-08-13 16:24 ` [RFC v5 01/28] hw/arm/smmuv3: Introduce secure registers Tao Tang
                   ` (26 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:21 UTC (permalink / raw)
  To: Michael S . Tsirkin, Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Arm SMMUv3 uses a SEC_SID (StreamID Security state) to determine the
security state of the programming interface that controls a transaction.
The architecture explicitly states that the association between a device
and its SEC_SID is a system-defined property, not something derived from
the physical address space.

We need a way to represent this system-defined SEC_SID for PCI devices
if we want to implement SMMU's Secure state. So that SMMUv3 can select
the correct register bank and configuration when handling their streams.

This patch adds a new char *sec_sid field to PCIDevice, together with
a "sec-sid" QOM property. The property is intended to carry the
platform-defined SEC_SID for the device; for now only Non-secure and
Secure security states are supported.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
---
 hw/pci/pci.c                | 7 +++++++
 include/hw/pci/pci_device.h | 3 +++
 2 files changed, 10 insertions(+)

diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index d3191609e28..b86135af96d 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -104,6 +104,13 @@ static const Property pci_props[] = {
     DEFINE_PROP_STRING("sriov-pf", PCIDevice, sriov_pf),
     DEFINE_PROP_BIT("x-pcie-ext-tag", PCIDevice, cap_present,
                     QEMU_PCIE_EXT_TAG_BITNR, true),
+
+    /*
+     * System-defined, statically configured SEC_SID for this PCI device, used
+     * by Arm SMMU. Only support "non-secure" and "secure" security states.
+     */
+    DEFINE_PROP_STRING("sec-sid", PCIDevice, sec_sid),
+
     { .name = "busnr", .info = &prop_pci_busnr },
 };
 
diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h
index 5cac6e16886..6e6c49fdd19 100644
--- a/include/hw/pci/pci_device.h
+++ b/include/hw/pci/pci_device.h
@@ -187,6 +187,9 @@ struct PCIDevice {
     uint32_t max_bounce_buffer_size;
 
     char *sriov_pf;
+
+    /* Arm SMMU SEC_SID */
+    char *sec_sid;
 };
 
 static inline int pci_intx(PCIDevice *pci_dev)
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 01/28] hw/arm/smmuv3: Introduce secure registers
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
  2026-08-13 16:21 ` [RFC v5 23/28] hw/pci: Add sec-sid property to PCIDevice Tao Tang
@ 2026-08-13 16:24 ` Tao Tang
  2026-08-18 14:21   ` Jim MacArthur
  2026-08-13 16:24 ` [RFC v5 02/28] hw/arm/smmuv3: Introduce banked registers for SMMUv3 state Tao Tang
                   ` (25 subsequent siblings)
  27 siblings, 1 reply; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:24 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

The Arm SMMUv3 architecture defines a set of registers for managing
Secure transactions and contexts.

This patch introduces the definitions for these secure registers within
the SMMUv3 device model internal header.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
Reviewed-by: Mostafa Saleh <smostafa@google.com>
Link: https://lore.kernel.org/qemu-devel/759ec110-365f-488b-802d-c7bb1efe30bc@redhat.com/
---
 include/hw/arm/smmuv3-common.h | 71 +++++++++++++++++++++++++++++++++-
 1 file changed, 70 insertions(+), 1 deletion(-)

diff --git a/include/hw/arm/smmuv3-common.h b/include/hw/arm/smmuv3-common.h
index 4609975edf9..ec442a85586 100644
--- a/include/hw/arm/smmuv3-common.h
+++ b/include/hw/arm/smmuv3-common.h
@@ -269,7 +269,7 @@ REG32(CD_5, 20)
     ((sel) ? ((cd)->word[4] = FIELD_DP32((cd)->word[4], CD_4, NSCFG1, (v))) : \
              ((cd)->word[2] = FIELD_DP32((cd)->word[2], CD_2, NSCFG0, (v))))
 
-/* MMIO Registers */
+/* MMIO Registers. Shared by Non-secure/Realm/Root states. */
 
 REG32(IDR0,                0x0)
     FIELD(IDR0, S2P,         0 , 1)
@@ -353,6 +353,7 @@ REG32(CR0,                 0x20)
     FIELD(CR0, CMDQEN,        3, 1)
 
 #define SMMU_CR0_RESERVED 0xFFFFFA20
+#define SMMU_S_CR0_RESERVED 0xFFFFFC12
 
 REG32(CR0ACK,              0x24)
 REG32(CR1,                 0x28)
@@ -411,6 +412,74 @@ REG32(EVENTQ_IRQ_CFG2,     0xbc)
 
 #define A_IDREGS           0xfd0
 
+#define SMMU_SECURE_REG_START 0x8000 /* Start of secure-only registers */
+
+REG32(S_IDR0,               0x8000)
+    FIELD(S_IDR0, MSI,         13, 1)
+    FIELD(S_IDR0, STALL_MODEL, 24, 2)
+REG32(S_IDR1,               0x8004)
+    FIELD(S_IDR1, S_SIDSIZE,          0 , 6)
+    FIELD(S_IDR1, SEL2,               29, 1)
+    FIELD(S_IDR1, SECURE_IMPL,        31, 1)
+
+REG32(S_IDR2,               0x8008)
+REG32(S_IDR3,               0x800c)
+REG32(S_IDR4,               0x8010)
+
+REG32(S_CR0,                0x8020)
+    FIELD(S_CR0, SMMUEN,      0, 1)
+    FIELD(S_CR0, EVENTQEN,    2, 1)
+    FIELD(S_CR0, CMDQEN,      3, 1)
+
+REG32(S_CR0ACK,             0x8024)
+REG32(S_CR1,                0x8028)
+REG32(S_CR2,                0x802c)
+
+REG32(S_INIT,               0x803c)
+    FIELD(S_INIT, INV_ALL,    0, 1)
+
+REG32(S_GBPA,               0x8044)
+    FIELD(S_GBPA, ABORT,     20, 1)
+    FIELD(S_GBPA, UPDATE,    31, 1)
+
+REG32(S_IRQ_CTRL,           0x8050)
+    FIELD(S_IRQ_CTRL, GERROR_IRQEN,    0, 1)
+    FIELD(S_IRQ_CTRL, EVENTQ_IRQEN,    2, 1)
+
+REG32(S_IRQ_CTRLACK,        0x8054)
+
+REG32(S_GERROR,             0x8060)
+    FIELD(S_GERROR, CMDQ_ERR,          0, 1)
+
+#define SMMU_GERROR_IRQ_CFG0_RESERVED   0x00FFFFFFFFFFFFFC
+#define SMMU_GERROR_IRQ_CFG2_RESERVED   0x000000000000003F
+
+#define SMMU_STRTAB_BASE_RESERVED       0x40FFFFFFFFFFFFC0
+#define SMMU_QUEUE_BASE_RESERVED        0x40FFFFFFFFFFFFFF
+#define SMMU_EVENTQ_IRQ_CFG0_RESERVED   0x00FFFFFFFFFFFFFC
+
+REG32(S_GERRORN,            0x8064)
+REG64(S_GERROR_IRQ_CFG0,    0x8068)
+REG32(S_GERROR_IRQ_CFG1,    0x8070)
+REG32(S_GERROR_IRQ_CFG2,    0x8074)
+REG64(S_STRTAB_BASE,        0x8080)
+REG32(S_STRTAB_BASE_CFG,    0x8088)
+    FIELD(S_STRTAB_BASE_CFG, LOG2SIZE, 0, 6)
+    FIELD(S_STRTAB_BASE_CFG, SPLIT,    6, 5)
+    FIELD(S_STRTAB_BASE_CFG, FMT,     16, 2)
+
+REG64(S_CMDQ_BASE,          0x8090)
+REG32(S_CMDQ_PROD,          0x8098)
+REG32(S_CMDQ_CONS,          0x809c)
+    FIELD(S_CMDQ_CONS, ERR,           24, 7)
+
+REG64(S_EVENTQ_BASE,        0x80a0)
+REG32(S_EVENTQ_PROD,        0x80a8)
+REG32(S_EVENTQ_CONS,        0x80ac)
+REG64(S_EVENTQ_IRQ_CFG0,    0x80b0)
+REG32(S_EVENTQ_IRQ_CFG1,    0x80b8)
+REG32(S_EVENTQ_IRQ_CFG2,    0x80bc)
+
 /* Commands */
 
 typedef enum SMMUCommandType {
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 02/28] hw/arm/smmuv3: Introduce banked registers for SMMUv3 state
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
  2026-08-13 16:21 ` [RFC v5 23/28] hw/pci: Add sec-sid property to PCIDevice Tao Tang
  2026-08-13 16:24 ` [RFC v5 01/28] hw/arm/smmuv3: Introduce secure registers Tao Tang
@ 2026-08-13 16:24 ` Tao Tang
  2026-08-13 16:24 ` [RFC v5 03/28] hw/arm/smmuv3: Thread SEC_SID through helper APIs Tao Tang
                   ` (24 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:24 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Rework the SMMUv3 state management by introducing a banked register
structure. This is a purely mechanical refactoring with no functional
changes.

To support multiple security states, a new enum, SMMUSecSID, is
introduced to identify each state, sticking to the spec terminology.

A new structure, SMMUv3RegBank, is then defined to hold the state
for a single security context. The main SMMUv3State now contains an
array of these banks, indexed by SMMUSecSID. This avoids the need for
separate fields for non-secure and future secure registers.

All existing code, which handles only the Non-secure state, is updated
to access its state via s->bank[SMMU_SEC_SID_NS]. A local bank helper
pointer is used where it improves readability.

Function signatures and logic remain untouched in this commit to
isolate the structural changes and simplify review. This is the
foundational step for building multi-security-state support.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
Reviewed-by: Mostafa Saleh <smostafa@google.com>
---
 hw/arm/smmuv3-accel.c        |  67 ++++---
 hw/arm/smmuv3-internal.h     |  24 ++-
 hw/arm/smmuv3.c              | 374 +++++++++++++++++++----------------
 hw/arm/tegra241-cmdqv.c      |   6 +-
 include/hw/arm/smmu-common.h |   6 +
 include/hw/arm/smmuv3.h      |  30 ++-
 6 files changed, 295 insertions(+), 212 deletions(-)

diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c
index 91aca1aa14e..3ff3da66acd 100644
--- a/hw/arm/smmuv3-accel.c
+++ b/hw/arm/smmuv3-accel.c
@@ -41,6 +41,7 @@ static void smmuv3_accel_auto_finalise(SMMUv3State *s,
                                        struct iommu_hw_info_arm_smmuv3 *info)
 {
     SMMUv3AccelState *accel = s->s_accel;
+    SMMUv3RegBank *bank = smmuv3_bank(s, SMMU_SEC_SID_NS);
 
     /*
      * Return if 'auto' was not set for any accel SMMUv3 property, or
@@ -55,23 +56,23 @@ static void smmuv3_accel_auto_finalise(SMMUv3State *s,
     }
 
     if (s->ats == ON_OFF_AUTO_AUTO) {
-        s->idr[0] = FIELD_DP32(s->idr[0], IDR0, ATS,
-                               FIELD_EX32(info->idr[0], IDR0, ATS));
+        bank->idr[0] = FIELD_DP32(bank->idr[0], IDR0, ATS,
+                                  FIELD_EX32(info->idr[0], IDR0, ATS));
     }
 
     if (s->ril == ON_OFF_AUTO_AUTO) {
-        s->idr[3] = FIELD_DP32(s->idr[3], IDR3, RIL,
-                               FIELD_EX32(info->idr[3], IDR3, RIL));
+        bank->idr[3] = FIELD_DP32(bank->idr[3], IDR3, RIL,
+                                  FIELD_EX32(info->idr[3], IDR3, RIL));
     }
 
     if (s->ssidsize == SSID_SIZE_MODE_AUTO) {
-        s->idr[1] = FIELD_DP32(s->idr[1], IDR1, SSIDSIZE,
-                               FIELD_EX32(info->idr[1], IDR1, SSIDSIZE));
+        bank->idr[1] = FIELD_DP32(bank->idr[1], IDR1, SSIDSIZE,
+                                  FIELD_EX32(info->idr[1], IDR1, SSIDSIZE));
     }
 
     if (s->oas == OAS_MODE_AUTO) {
-        s->idr[5] = FIELD_DP32(s->idr[5], IDR5, OAS,
-                               FIELD_EX32(info->idr[5], IDR5, OAS));
+        bank->idr[5] = FIELD_DP32(bank->idr[5], IDR5, OAS,
+                                  FIELD_EX32(info->idr[5], IDR5, OAS));
     }
 
     accel->auto_finalised = true;
@@ -82,21 +83,22 @@ smmuv3_accel_check_hw_compatible(SMMUv3State *s,
                                  struct iommu_hw_info_arm_smmuv3 *info,
                                  Error **errp)
 {
+    SMMUv3RegBank *bank = smmuv3_bank(s, SMMU_SEC_SID_NS);
     smmuv3_accel_auto_finalise(s, info);
 
     /* QEMU SMMUv3 supports both linear and 2-level stream tables */
     if (FIELD_EX32(info->idr[0], IDR0, STLEVEL) !=
-                FIELD_EX32(s->idr[0], IDR0, STLEVEL)) {
+                FIELD_EX32(bank->idr[0], IDR0, STLEVEL)) {
         error_setg(errp, "Host SMMUv3 Stream Table format mismatch "
                    "(host STLEVEL=%u, QEMU STLEVEL=%u)",
                    FIELD_EX32(info->idr[0], IDR0, STLEVEL),
-                   FIELD_EX32(s->idr[0], IDR0, STLEVEL));
+                   FIELD_EX32(bank->idr[0], IDR0, STLEVEL));
         return false;
     }
 
     /* QEMU SMMUv3 supports only little-endian translation table walks */
     if (FIELD_EX32(info->idr[0], IDR0, TTENDIAN) >
-                FIELD_EX32(s->idr[0], IDR0, TTENDIAN)) {
+                FIELD_EX32(bank->idr[0], IDR0, TTENDIAN)) {
         error_setg(errp, "Host SMMUv3 doesn't support Little-endian "
                    "translation table");
         return false;
@@ -104,7 +106,7 @@ smmuv3_accel_check_hw_compatible(SMMUv3State *s,
 
     /* QEMU SMMUv3 supports only AArch64 translation table format */
     if (FIELD_EX32(info->idr[0], IDR0, TTF) <
-                FIELD_EX32(s->idr[0], IDR0, TTF)) {
+                FIELD_EX32(bank->idr[0], IDR0, TTF)) {
         error_setg(errp, "Host SMMUv3 doesn't support AArch64 translation "
                    "table format");
         return false;
@@ -112,59 +114,59 @@ smmuv3_accel_check_hw_compatible(SMMUv3State *s,
 
     /* QEMU SMMUv3 supports SIDSIZE 16 */
     if (FIELD_EX32(info->idr[1], IDR1, SIDSIZE) <
-                FIELD_EX32(s->idr[1], IDR1, SIDSIZE)) {
+                FIELD_EX32(bank->idr[1], IDR1, SIDSIZE)) {
         error_setg(errp, "Host SMMUv3 SIDSIZE not compatible "
                    "(host=%u, QEMU=%u)",
                    FIELD_EX32(info->idr[1], IDR1, SIDSIZE),
-                   FIELD_EX32(s->idr[1], IDR1, SIDSIZE));
+                   FIELD_EX32(bank->idr[1], IDR1, SIDSIZE));
         return false;
     }
 
     /* Check SSIDSIZE value opted-in is compatible with Host SMMUv3 SSIDSIZE */
     if (FIELD_EX32(info->idr[1], IDR1, SSIDSIZE) <
-                FIELD_EX32(s->idr[1], IDR1, SSIDSIZE)) {
+                FIELD_EX32(bank->idr[1], IDR1, SSIDSIZE)) {
         error_setg(errp, "Host SMMUv3 SSIDSIZE not compatible "
                    "(host=%u, QEMU=%u)",
                    FIELD_EX32(info->idr[1], IDR1, SSIDSIZE),
-                   FIELD_EX32(s->idr[1], IDR1, SSIDSIZE));
+                   FIELD_EX32(bank->idr[1], IDR1, SSIDSIZE));
         return false;
     }
 
     /* User can disable QEMU SMMUv3 Range Invalidation support */
     if (FIELD_EX32(info->idr[3], IDR3, RIL) <
-                FIELD_EX32(s->idr[3], IDR3, RIL)) {
+                FIELD_EX32(bank->idr[3], IDR3, RIL)) {
         error_setg(errp, "Host SMMUv3 doesn't support Range Invalidation");
         return false;
     }
     /* Check OAS value opted is compatible with Host SMMUv3 IPA */
     if (FIELD_EX32(info->idr[5], IDR5, OAS) <
-                FIELD_EX32(s->idr[5], IDR5, OAS)) {
+                FIELD_EX32(bank->idr[5], IDR5, OAS)) {
         error_setg(errp, "Host SMMUv3 supports only %d-bit IPA, but the vSMMU "
                    "OAS implies %d-bit IPA",
                    smmuv3_oas_bits(FIELD_EX32(info->idr[5], IDR5, OAS)),
-                   smmuv3_oas_bits(FIELD_EX32(s->idr[5], IDR5, OAS)));
+                   smmuv3_oas_bits(FIELD_EX32(bank->idr[5], IDR5, OAS)));
         return false;
     }
     /* Check ATS value opted is compatible with Host SMMUv3 */
     if (FIELD_EX32(info->idr[0], IDR0, ATS) <
-                FIELD_EX32(s->idr[0], IDR0, ATS)) {
+                FIELD_EX32(bank->idr[0], IDR0, ATS)) {
         error_setg(errp, "Host SMMUv3 doesn't support Address Translation Services");
         return false;
     }
 
     /* QEMU SMMUv3 supports GRAN4K/GRAN16K/GRAN64K translation granules */
     if (FIELD_EX32(info->idr[5], IDR5, GRAN4K) !=
-                FIELD_EX32(s->idr[5], IDR5, GRAN4K)) {
+                FIELD_EX32(bank->idr[5], IDR5, GRAN4K)) {
         error_setg(errp, "Host SMMUv3 doesn't support 4K translation granule");
         return false;
     }
     if (FIELD_EX32(info->idr[5], IDR5, GRAN16K) !=
-                FIELD_EX32(s->idr[5], IDR5, GRAN16K)) {
+                FIELD_EX32(bank->idr[5], IDR5, GRAN16K)) {
         error_setg(errp, "Host SMMUv3 doesn't support 16K translation granule");
         return false;
     }
     if (FIELD_EX32(info->idr[5], IDR5, GRAN64K) !=
-                FIELD_EX32(s->idr[5], IDR5, GRAN64K)) {
+                FIELD_EX32(bank->idr[5], IDR5, GRAN64K)) {
         error_setg(errp, "Host SMMUv3 doesn't support 64K translation granule");
         return false;
     }
@@ -218,7 +220,8 @@ static SMMUv3AccelDevice *smmuv3_accel_get_dev(SMMUState *bs, SMMUPciBus *sbus,
 
 static uint32_t smmuv3_accel_gbpa_hwpt(SMMUv3State *s, SMMUv3AccelState *accel)
 {
-    return FIELD_EX32(s->gbpa, GBPA, ABORT) ?
+    SMMUv3RegBank *bank = smmuv3_bank(s, SMMU_SEC_SID_NS);
+    return FIELD_EX32(bank->gbpa, GBPA, ABORT) ?
            accel->abort_hwpt_id : accel->bypass_hwpt_id;
 }
 
@@ -544,6 +547,8 @@ bool smmuv3_accel_alloc_veventq(SMMUv3State *s, Error **errp)
     uint32_t veventq_id;
     uint32_t veventq_fd;
     int flags;
+    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
+    SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
 
     if (!accel || !accel->viommu) {
         return true;
@@ -560,7 +565,7 @@ bool smmuv3_accel_alloc_veventq(SMMUv3State *s, Error **errp)
     if (!iommufd_backend_alloc_veventq(accel->viommu->iommufd,
                                        accel->viommu->viommu_id,
                                        IOMMU_VEVENTQ_TYPE_ARM_SMMUV3,
-                                       1 << s->eventq.log2size, &veventq_id,
+                                       1 << bank->eventq.log2size, &veventq_id,
                                        &veventq_fd, errp)) {
         return false;
     }
@@ -943,9 +948,10 @@ static AddressSpace *smmuv3_accel_find_add_as(PCIBus *bus, void *opaque,
 
 static inline bool smmuv3_pasid_supported(SMMUv3State *s)
 {
+    SMMUv3RegBank *bank = smmuv3_bank(s, SMMU_SEC_SID_NS);
     return s->ssidsize > SSID_SIZE_MODE_0 ||
            (s->ssidsize == SSID_SIZE_MODE_AUTO &&
-            FIELD_EX32(s->idr[1], IDR1, SSIDSIZE));
+            FIELD_EX32(bank->idr[1], IDR1, SSIDSIZE));
 }
 
 static uint64_t smmuv3_accel_get_viommu_flags(void *opaque)
@@ -997,19 +1003,20 @@ void smmuv3_accel_idr_override(SMMUv3State *s)
         return;
     }
 
+    SMMUv3RegBank *bank = smmuv3_bank(s, SMMU_SEC_SID_NS);
     /* Only override RIL if user explicitly set OFF */
     if (s->ril == ON_OFF_AUTO_OFF) {
-        s->idr[3] = FIELD_DP32(s->idr[3], IDR3, RIL, 0);
+        bank->idr[3] = FIELD_DP32(bank->idr[3], IDR3, RIL, 0);
     }
 
     /* QEMU SMMUv3 has no ATS. Advertise ATS if opt-in by property */
     if (s->ats == ON_OFF_AUTO_ON) {
-        s->idr[0] = FIELD_DP32(s->idr[0], IDR0, ATS, 1);
+        bank->idr[0] = FIELD_DP32(bank->idr[0], IDR0, ATS, 1);
     }
 
     /* Advertise 48-bit OAS in IDR5 when requested (default is 44 bits). */
     if (s->oas == OAS_MODE_48) {
-        s->idr[5] = FIELD_DP32(s->idr[5], IDR5, OAS, SMMU_IDR5_OAS_48);
+        bank->idr[5] = FIELD_DP32(bank->idr[5], IDR5, OAS, SMMU_IDR5_OAS_48);
     }
 
     /*
@@ -1017,7 +1024,7 @@ void smmuv3_accel_idr_override(SMMUv3State *s)
      * has enabled it.
      */
     if (s->ssidsize > SSID_SIZE_MODE_0) {
-        s->idr[1] = FIELD_DP32(s->idr[1], IDR1, SSIDSIZE,
+        bank->idr[1] = FIELD_DP32(bank->idr[1], IDR1, SSIDSIZE,
                                ssidsize_mode_to_value(s->ssidsize));
     }
 }
diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h
index 0819a4b2e8d..10154ca161d 100644
--- a/hw/arm/smmuv3-internal.h
+++ b/hw/arm/smmuv3-internal.h
@@ -41,7 +41,9 @@ typedef enum SMMUTranslationClass {
 
 static inline int smmu_enabled(SMMUv3State *s)
 {
-    return FIELD_EX32(s->cr[0], CR0, SMMUEN);
+    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
+    SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
+    return FIELD_EX32(bank->cr[0], CR0, SMMUEN);
 }
 
 /* Command Queue Entry */
@@ -69,12 +71,16 @@ static inline uint32_t smmuv3_idreg(int regoffset)
 
 static inline bool smmuv3_eventq_irq_enabled(SMMUv3State *s)
 {
-    return FIELD_EX32(s->irq_ctrl, IRQ_CTRL, EVENTQ_IRQEN);
+    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
+    SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
+    return FIELD_EX32(bank->irq_ctrl, IRQ_CTRL, EVENTQ_IRQEN);
 }
 
 static inline bool smmuv3_gerror_irq_enabled(SMMUv3State *s)
 {
-    return FIELD_EX32(s->irq_ctrl, IRQ_CTRL, GERROR_IRQEN);
+    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
+    SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
+    return FIELD_EX32(bank->irq_ctrl, IRQ_CTRL, GERROR_IRQEN);
 }
 
 /* Queue Handling */
@@ -119,17 +125,23 @@ static inline void queue_cons_incr(SMMUQueue *q)
 
 static inline bool smmuv3_cmdq_enabled(SMMUv3State *s)
 {
-    return FIELD_EX32(s->cr[0], CR0, CMDQEN);
+    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
+    SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
+    return FIELD_EX32(bank->cr[0], CR0, CMDQEN);
 }
 
 static inline bool smmuv3_eventq_enabled(SMMUv3State *s)
 {
-    return FIELD_EX32(s->cr[0], CR0, EVENTQEN);
+    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
+    SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
+    return FIELD_EX32(bank->cr[0], CR0, EVENTQEN);
 }
 
 static inline void smmu_write_cmdq_err(SMMUv3State *s, uint32_t err_type)
 {
-    s->cmdq.cons = FIELD_DP32(s->cmdq.cons, CMDQ_CONS, ERR, err_type);
+    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
+    SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
+    bank->cmdq.cons = FIELD_DP32(bank->cmdq.cons, CMDQ_CONS, ERR, err_type);
 }
 
 static const char *cmd_stringify[] = {
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index ed19536a4dc..d3baae8a32a 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -53,6 +53,8 @@
 static void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq,
                                uint32_t gerror_mask)
 {
+    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
+    SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
 
     bool pulse = false;
 
@@ -68,15 +70,15 @@ static void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq,
         break;
     case SMMU_IRQ_GERROR:
     {
-        uint32_t pending = s->gerror ^ s->gerrorn;
+        uint32_t pending = bank->gerror ^ bank->gerrorn;
         uint32_t new_gerrors = ~pending & gerror_mask;
 
         if (!new_gerrors) {
             /* only toggle non pending errors */
             return;
         }
-        s->gerror ^= new_gerrors;
-        trace_smmuv3_write_gerror(new_gerrors, s->gerror);
+        bank->gerror ^= new_gerrors;
+        trace_smmuv3_write_gerror(new_gerrors, bank->gerror);
 
         pulse = smmuv3_gerror_irq_enabled(s);
         break;
@@ -90,8 +92,10 @@ static void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq,
 
 static void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t new_gerrorn)
 {
-    uint32_t pending = s->gerror ^ s->gerrorn;
-    uint32_t toggled = s->gerrorn ^ new_gerrorn;
+    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
+    SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
+    uint32_t pending = bank->gerror ^ bank->gerrorn;
+    uint32_t toggled = bank->gerrorn ^ new_gerrorn;
 
     if (toggled & ~pending) {
         qemu_log_mask(LOG_GUEST_ERROR,
@@ -103,9 +107,9 @@ static void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t new_gerrorn)
      * We do not raise any error in case guest toggles bits corresponding
      * to not active IRQs (CONSTRAINED UNPREDICTABLE)
      */
-    s->gerrorn = new_gerrorn;
+    bank->gerrorn = new_gerrorn;
 
-    trace_smmuv3_write_gerrorn(toggled & pending, s->gerrorn);
+    trace_smmuv3_write_gerrorn(toggled & pending, bank->gerrorn);
 }
 
 static inline MemTxResult queue_read(SMMUQueue *q, Cmd *cmd)
@@ -147,7 +151,9 @@ static MemTxResult queue_write(SMMUQueue *q, Evt *evt_in)
 
 static MemTxResult smmuv3_write_eventq(SMMUv3State *s, Evt *evt)
 {
-    SMMUQueue *q = &s->eventq;
+    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
+    SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
+    SMMUQueue *q = &bank->eventq;
     MemTxResult r;
 
     if (!smmuv3_eventq_enabled(s)) {
@@ -275,74 +281,87 @@ void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *info)
  */
 static void smmuv3_init_id_regs(SMMUv3State *s)
 {
+    SMMUv3RegBank *bank = smmuv3_bank(s, SMMU_SEC_SID_NS);
+
     /* Based on sys property, the stages supported in smmu will be advertised.*/
     if (s->stage && !strcmp("2", s->stage)) {
-        s->idr[0] = FIELD_DP32(s->idr[0], IDR0, S2P, 1);
+        bank->idr[0] = FIELD_DP32(bank->idr[0], IDR0, S2P, 1);
     } else if (s->stage && !strcmp("nested", s->stage)) {
-        s->idr[0] = FIELD_DP32(s->idr[0], IDR0, S1P, 1);
-        s->idr[0] = FIELD_DP32(s->idr[0], IDR0, S2P, 1);
+        bank->idr[0] = FIELD_DP32(bank->idr[0], IDR0, S1P, 1);
+        bank->idr[0] = FIELD_DP32(bank->idr[0], IDR0, S2P, 1);
     } else {
-        s->idr[0] = FIELD_DP32(s->idr[0], IDR0, S1P, 1);
-    }
-
-    s->idr[0] = FIELD_DP32(s->idr[0], IDR0, TTF, 2); /* AArch64 PTW only */
-    s->idr[0] = FIELD_DP32(s->idr[0], IDR0, COHACC, 1); /* IO coherent */
-    s->idr[0] = FIELD_DP32(s->idr[0], IDR0, ASID16, 1); /* 16-bit ASID */
-    s->idr[0] = FIELD_DP32(s->idr[0], IDR0, VMID16, 1); /* 16-bit VMID */
-    s->idr[0] = FIELD_DP32(s->idr[0], IDR0, TTENDIAN, 2); /* little endian */
-    s->idr[0] = FIELD_DP32(s->idr[0], IDR0, STALL_MODEL, 1); /* No stall */
+        bank->idr[0] = FIELD_DP32(bank->idr[0], IDR0, S1P, 1);
+    }
+
+    /* AArch64 PTW only */
+    bank->idr[0] = FIELD_DP32(bank->idr[0], IDR0, TTF, 2);
+    /* IO coherent */
+    bank->idr[0] = FIELD_DP32(bank->idr[0], IDR0, COHACC, 1);
+    /* 16-bit ASID */
+    bank->idr[0] = FIELD_DP32(bank->idr[0], IDR0, ASID16, 1);
+    /* 16-bit VMID */
+    bank->idr[0] = FIELD_DP32(bank->idr[0], IDR0, VMID16, 1);
+    /* little endian */
+    bank->idr[0] = FIELD_DP32(bank->idr[0], IDR0, TTENDIAN, 2);
+    /* No stall */
+    bank->idr[0] = FIELD_DP32(bank->idr[0], IDR0, STALL_MODEL, 1);
     /* terminated transaction will always be aborted/error returned */
-    s->idr[0] = FIELD_DP32(s->idr[0], IDR0, TERM_MODEL, 1);
+    bank->idr[0] = FIELD_DP32(bank->idr[0], IDR0, TERM_MODEL, 1);
     /* 2-level stream table supported */
-    s->idr[0] = FIELD_DP32(s->idr[0], IDR0, STLEVEL, 1);
+    bank->idr[0] = FIELD_DP32(bank->idr[0], IDR0, STLEVEL, 1);
 
-    s->idr[1] = FIELD_DP32(s->idr[1], IDR1, SIDSIZE, SMMU_IDR1_SIDSIZE);
-    s->idr[1] = FIELD_DP32(s->idr[1], IDR1, EVENTQS, SMMU_EVENTQS);
-    s->idr[1] = FIELD_DP32(s->idr[1], IDR1, CMDQS,   SMMU_CMDQS);
+    bank->idr[1] = FIELD_DP32(bank->idr[1], IDR1, SIDSIZE, SMMU_IDR1_SIDSIZE);
+    bank->idr[1] = FIELD_DP32(bank->idr[1], IDR1, EVENTQS, SMMU_EVENTQS);
+    bank->idr[1] = FIELD_DP32(bank->idr[1], IDR1, CMDQS,   SMMU_CMDQS);
 
-    s->idr[3] = FIELD_DP32(s->idr[3], IDR3, HAD, 1);
-    if (FIELD_EX32(s->idr[0], IDR0, S2P)) {
+    bank->idr[3] = FIELD_DP32(bank->idr[3], IDR3, HAD, 1);
+    if (FIELD_EX32(bank->idr[0], IDR0, S2P)) {
         /* XNX is a stage-2-specific feature */
-        s->idr[3] = FIELD_DP32(s->idr[3], IDR3, XNX, 1);
+        bank->idr[3] = FIELD_DP32(bank->idr[3], IDR3, XNX, 1);
     }
-    s->idr[3] = FIELD_DP32(s->idr[3], IDR3, RIL, 1);
-    s->idr[3] = FIELD_DP32(s->idr[3], IDR3, BBML, 2);
+    bank->idr[3] = FIELD_DP32(bank->idr[3], IDR3, RIL, 1);
+    bank->idr[3] = FIELD_DP32(bank->idr[3], IDR3, BBML, 2);
 
     /* OAS: 44 bits */
-    s->idr[5] = FIELD_DP32(s->idr[5], IDR5, OAS, SMMU_IDR5_OAS_44);
+    bank->idr[5] = FIELD_DP32(bank->idr[5], IDR5, OAS, SMMU_IDR5_OAS_44);
     /* 4K, 16K and 64K granule support */
-    s->idr[5] = FIELD_DP32(s->idr[5], IDR5, GRAN4K, 1);
-    s->idr[5] = FIELD_DP32(s->idr[5], IDR5, GRAN16K, 1);
-    s->idr[5] = FIELD_DP32(s->idr[5], IDR5, GRAN64K, 1);
+    bank->idr[5] = FIELD_DP32(bank->idr[5], IDR5, GRAN4K, 1);
+    bank->idr[5] = FIELD_DP32(bank->idr[5], IDR5, GRAN16K, 1);
+    bank->idr[5] = FIELD_DP32(bank->idr[5], IDR5, GRAN64K, 1);
     s->aidr = 0x1;
     smmuv3_accel_idr_override(s);
 }
 
 bool smmuv3_ats_enabled(SMMUv3State *s)
 {
-    return FIELD_EX32(s->idr[0], IDR0, ATS);
+    SMMUv3RegBank *bank = smmuv3_bank(s, SMMU_SEC_SID_NS);
+    return FIELD_EX32(bank->idr[0], IDR0, ATS);
 }
 
 static void smmuv3_reset(SMMUv3State *s)
 {
-    s->cmdq.base = deposit64(s->cmdq.base, 0, 5, SMMU_CMDQS);
-    s->cmdq.prod = 0;
-    s->cmdq.cons = 0;
-    s->cmdq.entry_size = sizeof(struct Cmd);
-    s->eventq.base = deposit64(s->eventq.base, 0, 5, SMMU_EVENTQS);
-    s->eventq.prod = 0;
-    s->eventq.cons = 0;
-    s->eventq.entry_size = sizeof(struct Evt);
-
-    s->features = 0;
-    s->sid_split = 0;
-    s->cr[0] = 0;
-    s->cr0ack = 0;
-    s->irq_ctrl = 0;
-    s->gerror = 0;
-    s->gerrorn = 0;
+    SMMUv3RegBank *bank = smmuv3_bank(s, SMMU_SEC_SID_NS);
+
+    bank->cmdq.base = deposit64(bank->cmdq.base, 0, 5, SMMU_CMDQS);
+    bank->cmdq.prod = 0;
+    bank->cmdq.cons = 0;
+    bank->cmdq.entry_size = sizeof(struct Cmd);
+    bank->eventq.base = deposit64(bank->eventq.base, 0, 5, SMMU_EVENTQS);
+    bank->eventq.prod = 0;
+    bank->eventq.cons = 0;
+    bank->eventq.entry_size = sizeof(struct Evt);
+
+    bank->features = 0;
+    bank->sid_split = 0;
+    bank->cr[0] = 0;
+    bank->cr0ack = 0;
+    bank->irq_ctrl = 0;
+    bank->gerror = 0;
+    bank->gerrorn = 0;
+    bank->gbpa = SMMU_GBPA_RESET_VAL;
+
+    s->aidr = 0x1;
     s->statusr = 0;
-    s->gbpa = SMMU_GBPA_RESET_VAL;
 }
 
 static int smmu_get_ste(SMMUv3State *s, dma_addr_t addr, STE *buf,
@@ -456,7 +475,8 @@ static bool s2_pgtable_config_valid(uint8_t sl0, uint8_t t0sz, uint8_t gran)
 static int decode_ste_s2_cfg(SMMUv3State *s, SMMUTransCfg *cfg,
                              STE *ste)
 {
-    uint8_t oas = FIELD_EX32(s->idr[5], IDR5, OAS);
+    uint8_t oas = FIELD_EX32(smmuv3_bank(s, SMMU_SEC_SID_NS)->idr[5],
+                             IDR5, OAS);
 
     if (STE_S2AA64(ste) == 0x0) {
         qemu_log_mask(LOG_UNIMP,
@@ -574,7 +594,9 @@ static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg,
                       STE *ste, SMMUEventInfo *event)
 {
     uint32_t config;
-    uint8_t oas = FIELD_EX32(s->idr[5], IDR5, OAS);
+    /* OAS field only presents on NS-IDR5 so we use hardcoded SMMU_SEC_SID_NS */
+    uint8_t oas = FIELD_EX32(smmuv3_bank(s, SMMU_SEC_SID_NS)->idr[5],
+                             IDR5, OAS);
     int ret;
 
     if (!STE_VALID(ste)) {
@@ -628,7 +650,8 @@ static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg,
     /* Multiple context descriptors require SubstreamID support */
     if ((s->ssidsize == SSID_SIZE_MODE_0 ||
          (s->ssidsize == SSID_SIZE_MODE_AUTO &&
-          !FIELD_EX32(s->idr[1], IDR1, SSIDSIZE))) &&
+          !FIELD_EX32(smmuv3_bank(s, SMMU_SEC_SID_NS)->idr[1],
+                      IDR1, SSIDSIZE))) &&
         STE_S1CDMAX(ste) != 0) {
         qemu_log_mask(LOG_UNIMP,
                 "SMMUv3: multiple S1 context descriptors require SubstreamID support. "
@@ -666,9 +689,11 @@ int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste, SMMUEventInfo *event)
     uint32_t log2size;
     int strtab_size;
     int ret;
+    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
+    SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
 
-    trace_smmuv3_find_ste(sid, s->features, s->sid_split);
-    log2size = FIELD_EX32(s->strtab_base_cfg, STRTAB_BASE_CFG, LOG2SIZE);
+    trace_smmuv3_find_ste(sid, bank->features, bank->sid_split);
+    log2size = FIELD_EX32(bank->strtab_base_cfg, STRTAB_BASE_CFG, LOG2SIZE);
     /*
      * Check SID range against both guest-configured and implementation limits
      */
@@ -676,7 +701,7 @@ int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste, SMMUEventInfo *event)
         event->type = SMMU_EVT_C_BAD_STREAMID;
         return -EINVAL;
     }
-    if (s->features & SMMU_FEATURE_2LVL_STE) {
+    if (bank->features & SMMU_FEATURE_2LVL_STE) {
         int l1_ste_offset, l2_ste_offset, max_l2_ste, span, i;
         dma_addr_t l1ptr, l2ptr;
         STEDesc l1std;
@@ -685,11 +710,11 @@ int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste, SMMUEventInfo *event)
          * Align strtab base address to table size. For this purpose, assume it
          * is not bounded by SMMU_IDR1_SIDSIZE.
          */
-        strtab_size = MAX(6, (int)log2size - s->sid_split + L1STD_SIZE);
-        strtab_base = s->strtab_base & SMMU_BASE_ADDR_MASK &
+        strtab_size = MAX(6, (int)log2size - bank->sid_split + L1STD_SIZE);
+        strtab_base = bank->strtab_base & SMMU_BASE_ADDR_MASK &
                       ~MAKE_64BIT_MASK(0, strtab_size);
-        l1_ste_offset = sid >> s->sid_split;
-        l2_ste_offset = sid & ((1 << s->sid_split) - 1);
+        l1_ste_offset = sid >> bank->sid_split;
+        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,
@@ -717,7 +742,7 @@ int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste, SMMUEventInfo *event)
             return -EINVAL;
         }
 
-        if (span > s->sid_split + 1) {
+        if (span > bank->sid_split + 1) {
             if (!event->inval_ste_allowed) {
                 qemu_log_mask(LOG_GUEST_ERROR,
                               "invalid span (0x%x)\n", span);
@@ -728,9 +753,8 @@ int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste, SMMUEventInfo *event)
 
         max_l2_ste = (1 << span) - 1;
         l2ptr = l1std_l2ptr(&l1std);
-
         l2ptr &= ~MAKE_64BIT_MASK(0, 6 + (span - 1));
-        trace_smmuv3_find_ste_2lvl(s->strtab_base, l1ptr, l1_ste_offset,
+        trace_smmuv3_find_ste_2lvl(bank->strtab_base, l1ptr, l1_ste_offset,
                                    l2ptr, l2_ste_offset, max_l2_ste);
         if (l2_ste_offset > max_l2_ste) {
             qemu_log_mask(LOG_GUEST_ERROR,
@@ -743,7 +767,7 @@ int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste, SMMUEventInfo *event)
     } else {
         strtab_size = log2size + STE_SIZE;
         strtab_size = MIN(64, strtab_size);
-        strtab_base = s->strtab_base & SMMU_BASE_ADDR_MASK &
+        strtab_base = bank->strtab_base & SMMU_BASE_ADDR_MASK &
                       ~MAKE_64BIT_MASK(0, strtab_size);
         addr = strtab_base + sid * sizeof(*ste);
     }
@@ -762,7 +786,8 @@ static int decode_cd(SMMUv3State *s, SMMUTransCfg *cfg,
     int i;
     SMMUTranslationStatus status;
     SMMUTLBEntry *entry;
-    uint8_t oas = FIELD_EX32(s->idr[5], IDR5, OAS);
+    uint8_t oas = FIELD_EX32(smmuv3_bank(s, SMMU_SEC_SID_NS)->idr[5],
+                             IDR5, OAS);
 
     if (!CD_VALID(cd) || !CD_AARCH64(cd)) {
         goto bad_cd;
@@ -1084,6 +1109,8 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
     SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu);
     SMMUv3State *s = sdev->smmu;
     uint32_t sid = smmu_get_sid(sdev);
+    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
+    SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
     SMMUEventInfo event = {.type = SMMU_EVT_NONE,
                            .sid = sid,
                            .inval_ste_allowed = false};
@@ -1101,7 +1128,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
     qemu_mutex_lock(&s->mutex);
 
     if (!smmu_enabled(s)) {
-        if (FIELD_EX32(s->gbpa, GBPA, ABORT)) {
+        if (FIELD_EX32(bank->gbpa, GBPA, ABORT)) {
             status = SMMU_TRANS_ABORT;
         } else {
             status = SMMU_TRANS_DISABLE;
@@ -1325,7 +1352,9 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp)
 {
     SMMUState *bs = ARM_SMMU(s);
     SMMUCmdError cmd_error = SMMU_CERROR_NONE;
-    SMMUQueue *q = &s->cmdq;
+    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
+    SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
+    SMMUQueue *q = &bank->cmdq;
     SMMUCommandType type = 0;
 
     if (!smmuv3_cmdq_enabled(s)) {
@@ -1339,7 +1368,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp)
      */
 
     while (!smmuv3_q_empty(q)) {
-        uint32_t pending = s->gerror ^ s->gerrorn;
+        uint32_t pending = bank->gerror ^ bank->gerrorn;
         Cmd cmd;
 
         trace_smmuv3_cmdq_consume(Q_PROD(q), Q_CONS(q),
@@ -1601,29 +1630,32 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp)
 static MemTxResult smmu_writell(SMMUv3State *s, hwaddr offset,
                                uint64_t data, MemTxAttrs attrs)
 {
+    SMMUSecSID reg_sec_sid = SMMU_SEC_SID_NS;
+    SMMUv3RegBank *bank = smmuv3_bank(s, reg_sec_sid);
+
     switch (offset) {
     case A_GERROR_IRQ_CFG0:
-        s->gerror_irq_cfg0 = data;
+        bank->gerror_irq_cfg0 = data;
         return MEMTX_OK;
     case A_STRTAB_BASE:
-        s->strtab_base = data;
+        bank->strtab_base = data;
         return MEMTX_OK;
     case A_CMDQ_BASE:
-        s->cmdq.base = data;
-        s->cmdq.log2size = extract64(s->cmdq.base, 0, 5);
-        if (s->cmdq.log2size > SMMU_CMDQS) {
-            s->cmdq.log2size = SMMU_CMDQS;
+        bank->cmdq.base = data;
+        bank->cmdq.log2size = extract64(bank->cmdq.base, 0, 5);
+        if (bank->cmdq.log2size > SMMU_CMDQS) {
+            bank->cmdq.log2size = SMMU_CMDQS;
         }
         return MEMTX_OK;
     case A_EVENTQ_BASE:
-        s->eventq.base = data;
-        s->eventq.log2size = extract64(s->eventq.base, 0, 5);
-        if (s->eventq.log2size > SMMU_EVENTQS) {
-            s->eventq.log2size = SMMU_EVENTQS;
+        bank->eventq.base = data;
+        bank->eventq.log2size = extract64(bank->eventq.base, 0, 5);
+        if (bank->eventq.log2size > SMMU_EVENTQS) {
+            bank->eventq.log2size = SMMU_EVENTQS;
         }
         return MEMTX_OK;
     case A_EVENTQ_IRQ_CFG0:
-        s->eventq_irq_cfg0 = data;
+        bank->eventq_irq_cfg0 = data;
         return MEMTX_OK;
     default:
         qemu_log_mask(LOG_UNIMP,
@@ -1637,11 +1669,13 @@ static MemTxResult smmu_writel(SMMUv3State *s, hwaddr offset,
                                uint64_t data, MemTxAttrs attrs)
 {
     Error *local_err = NULL;
+    SMMUSecSID reg_sec_sid = SMMU_SEC_SID_NS;
+    SMMUv3RegBank *bank = smmuv3_bank(s, reg_sec_sid);
 
     switch (offset) {
     case A_CR0:
-        s->cr[0] = data;
-        s->cr0ack = data & ~SMMU_CR0_RESERVED;
+        bank->cr[0] = data;
+        bank->cr0ack = data & ~SMMU_CR0_RESERVED;
         /* in case the command queue has been enabled */
         smmuv3_cmdq_consume(s, &local_err);
         if (local_err) {
@@ -1652,13 +1686,13 @@ static MemTxResult smmu_writel(SMMUv3State *s, hwaddr offset,
         smmuv3_accel_alloc_veventq(s, &local_err);
         break;
     case A_CR1:
-        s->cr[1] = data;
+        bank->cr[1] = data;
         break;
     case A_CR2:
-        s->cr[2] = data;
+        bank->cr[2] = data;
         break;
     case A_IRQ_CTRL:
-        s->irq_ctrl = data;
+        bank->irq_ctrl = data;
         break;
     case A_GERRORN:
         smmuv3_write_gerrorn(s, data);
@@ -1669,16 +1703,16 @@ static MemTxResult smmu_writel(SMMUv3State *s, hwaddr offset,
         smmuv3_cmdq_consume(s, &local_err);
         break;
     case A_GERROR_IRQ_CFG0: /* 64b */
-        s->gerror_irq_cfg0 = deposit64(s->gerror_irq_cfg0, 0, 32, data);
+        bank->gerror_irq_cfg0 = deposit64(bank->gerror_irq_cfg0, 0, 32, data);
         break;
     case A_GERROR_IRQ_CFG0 + 4:
-        s->gerror_irq_cfg0 = deposit64(s->gerror_irq_cfg0, 32, 32, data);
+        bank->gerror_irq_cfg0 = deposit64(bank->gerror_irq_cfg0, 32, 32, data);
         break;
     case A_GERROR_IRQ_CFG1:
-        s->gerror_irq_cfg1 = data;
+        bank->gerror_irq_cfg1 = data;
         break;
     case A_GERROR_IRQ_CFG2:
-        s->gerror_irq_cfg2 = data;
+        bank->gerror_irq_cfg2 = data;
         break;
     case A_GBPA:
         /*
@@ -1687,74 +1721,75 @@ static MemTxResult smmu_writel(SMMUv3State *s, hwaddr offset,
          */
         if (data & R_GBPA_UPDATE_MASK) {
             /* Ignore update bit as write is synchronous. */
-            s->gbpa = data & ~R_GBPA_UPDATE_MASK;
+            bank->gbpa = data & ~R_GBPA_UPDATE_MASK;
             smmuv3_accel_attach_gbpa_hwpt(s, &local_err);
         }
         break;
     case A_STRTAB_BASE: /* 64b */
-        s->strtab_base = deposit64(s->strtab_base, 0, 32, data);
+        bank->strtab_base = deposit64(bank->strtab_base, 0, 32, data);
         break;
     case A_STRTAB_BASE + 4:
-        s->strtab_base = deposit64(s->strtab_base, 32, 32, data);
+        bank->strtab_base = deposit64(bank->strtab_base, 32, 32, data);
         break;
     case A_STRTAB_BASE_CFG:
-        s->strtab_base_cfg = data;
+        bank->strtab_base_cfg = data;
         if (FIELD_EX32(data, STRTAB_BASE_CFG, FMT) == 1) {
-            s->sid_split = FIELD_EX32(data, STRTAB_BASE_CFG, SPLIT);
-            if (s->sid_split != 6 && s->sid_split != 8 && s->sid_split != 10) {
+            bank->sid_split = FIELD_EX32(data, STRTAB_BASE_CFG, SPLIT);
+            if (bank->sid_split != 6 && bank->sid_split != 8 &&
+                bank->sid_split != 10) {
                 /* Other values are reserved, behave as 6 */
                 qemu_log_mask(LOG_GUEST_ERROR,
                               "Invalid STRTAB_BASE_CFG.SPLIT=%u, use 6 instead\n",
-                              s->sid_split);
-                s->sid_split = 6;
+                              bank->sid_split);
+                bank->sid_split = 6;
             }
-            s->features |= SMMU_FEATURE_2LVL_STE;
+            bank->features |= SMMU_FEATURE_2LVL_STE;
         }
         break;
     case A_CMDQ_BASE: /* 64b */
-        s->cmdq.base = deposit64(s->cmdq.base, 0, 32, data);
-        s->cmdq.log2size = extract64(s->cmdq.base, 0, 5);
-        if (s->cmdq.log2size > SMMU_CMDQS) {
-            s->cmdq.log2size = SMMU_CMDQS;
+        bank->cmdq.base = deposit64(bank->cmdq.base, 0, 32, data);
+        bank->cmdq.log2size = extract64(bank->cmdq.base, 0, 5);
+        if (bank->cmdq.log2size > SMMU_CMDQS) {
+            bank->cmdq.log2size = SMMU_CMDQS;
         }
         break;
     case A_CMDQ_BASE + 4: /* 64b */
-        s->cmdq.base = deposit64(s->cmdq.base, 32, 32, data);
+        bank->cmdq.base = deposit64(bank->cmdq.base, 32, 32, data);
         break;
     case A_CMDQ_PROD:
-        s->cmdq.prod = data;
+        bank->cmdq.prod = data;
         smmuv3_cmdq_consume(s, &local_err);
         break;
     case A_CMDQ_CONS:
-        s->cmdq.cons = data;
+        bank->cmdq.cons = data;
         break;
     case A_EVENTQ_BASE: /* 64b */
-        s->eventq.base = deposit64(s->eventq.base, 0, 32, data);
-        s->eventq.log2size = extract64(s->eventq.base, 0, 5);
-        if (s->eventq.log2size > SMMU_EVENTQS) {
-            s->eventq.log2size = SMMU_EVENTQS;
+        bank->eventq.base = deposit64(bank->eventq.base, 0, 32, data);
+        bank->eventq.log2size = extract64(bank->eventq.base, 0, 5);
+        if (bank->eventq.log2size > SMMU_EVENTQS) {
+            bank->eventq.log2size = SMMU_EVENTQS;
         }
         break;
     case A_EVENTQ_BASE + 4:
-        s->eventq.base = deposit64(s->eventq.base, 32, 32, data);
+        bank->eventq.base = deposit64(bank->eventq.base, 32, 32, data);
         break;
     case A_EVENTQ_PROD:
-        s->eventq.prod = data;
+        bank->eventq.prod = data;
         break;
     case A_EVENTQ_CONS:
-        s->eventq.cons = data;
+        bank->eventq.cons = data;
         break;
     case A_EVENTQ_IRQ_CFG0: /* 64b */
-        s->eventq_irq_cfg0 = deposit64(s->eventq_irq_cfg0, 0, 32, data);
+        bank->eventq_irq_cfg0 = deposit64(bank->eventq_irq_cfg0, 0, 32, data);
         break;
     case A_EVENTQ_IRQ_CFG0 + 4:
-        s->eventq_irq_cfg0 = deposit64(s->eventq_irq_cfg0, 32, 32, data);
+        bank->eventq_irq_cfg0 = deposit64(bank->eventq_irq_cfg0, 32, 32, data);
         break;
     case A_EVENTQ_IRQ_CFG1:
-        s->eventq_irq_cfg1 = data;
+        bank->eventq_irq_cfg1 = data;
         break;
     case A_EVENTQ_IRQ_CFG2:
-        s->eventq_irq_cfg2 = data;
+        bank->eventq_irq_cfg2 = data;
         break;
     default:
         qemu_log_mask(LOG_UNIMP,
@@ -1798,18 +1833,21 @@ static MemTxResult smmu_write_mmio(void *opaque, hwaddr offset, uint64_t data,
 static MemTxResult smmu_readll(SMMUv3State *s, hwaddr offset,
                                uint64_t *data, MemTxAttrs attrs)
 {
+    SMMUSecSID reg_sec_sid = SMMU_SEC_SID_NS;
+    SMMUv3RegBank *bank = smmuv3_bank(s, reg_sec_sid);
+
     switch (offset) {
     case A_GERROR_IRQ_CFG0:
-        *data = s->gerror_irq_cfg0;
+        *data = bank->gerror_irq_cfg0;
         return MEMTX_OK;
     case A_STRTAB_BASE:
-        *data = s->strtab_base;
+        *data = bank->strtab_base;
         return MEMTX_OK;
     case A_CMDQ_BASE:
-        *data = s->cmdq.base;
+        *data = bank->cmdq.base;
         return MEMTX_OK;
     case A_EVENTQ_BASE:
-        *data = s->eventq.base;
+        *data = bank->eventq.base;
         return MEMTX_OK;
     default:
         *data = 0;
@@ -1823,12 +1861,15 @@ static MemTxResult smmu_readll(SMMUv3State *s, hwaddr offset,
 static MemTxResult smmu_readl(SMMUv3State *s, hwaddr offset,
                               uint64_t *data, MemTxAttrs attrs)
 {
+    SMMUSecSID reg_sec_sid = SMMU_SEC_SID_NS;
+    SMMUv3RegBank *bank = smmuv3_bank(s, reg_sec_sid);
+
     switch (offset) {
     case A_IDREGS ... A_IDREGS + 0x2f:
         *data = smmuv3_idreg(offset - A_IDREGS);
         return MEMTX_OK;
     case A_IDR0 ... A_IDR5:
-        *data = s->idr[(offset - A_IDR0) / 4];
+        *data = bank->idr[(offset - A_IDR0) / 4];
         return MEMTX_OK;
     case A_IIDR:
         *data = s->iidr;
@@ -1837,77 +1878,77 @@ static MemTxResult smmu_readl(SMMUv3State *s, hwaddr offset,
         *data = s->aidr;
         return MEMTX_OK;
     case A_CR0:
-        *data = s->cr[0];
+        *data = bank->cr[0];
         return MEMTX_OK;
     case A_CR0ACK:
-        *data = s->cr0ack;
+        *data = bank->cr0ack;
         return MEMTX_OK;
     case A_CR1:
-        *data = s->cr[1];
+        *data = bank->cr[1];
         return MEMTX_OK;
     case A_CR2:
-        *data = s->cr[2];
+        *data = bank->cr[2];
         return MEMTX_OK;
     case A_STATUSR:
         *data = s->statusr;
         return MEMTX_OK;
     case A_GBPA:
-        *data = s->gbpa;
+        *data = bank->gbpa;
         return MEMTX_OK;
     case A_IRQ_CTRL:
     case A_IRQ_CTRL_ACK:
-        *data = s->irq_ctrl;
+        *data = bank->irq_ctrl;
         return MEMTX_OK;
     case A_GERROR:
-        *data = s->gerror;
+        *data = bank->gerror;
         return MEMTX_OK;
     case A_GERRORN:
-        *data = s->gerrorn;
+        *data = bank->gerrorn;
         return MEMTX_OK;
     case A_GERROR_IRQ_CFG0: /* 64b */
-        *data = extract64(s->gerror_irq_cfg0, 0, 32);
+        *data = extract64(bank->gerror_irq_cfg0, 0, 32);
         return MEMTX_OK;
     case A_GERROR_IRQ_CFG0 + 4:
-        *data = extract64(s->gerror_irq_cfg0, 32, 32);
+        *data = extract64(bank->gerror_irq_cfg0, 32, 32);
         return MEMTX_OK;
     case A_GERROR_IRQ_CFG1:
-        *data = s->gerror_irq_cfg1;
+        *data = bank->gerror_irq_cfg1;
         return MEMTX_OK;
     case A_GERROR_IRQ_CFG2:
-        *data = s->gerror_irq_cfg2;
+        *data = bank->gerror_irq_cfg2;
         return MEMTX_OK;
     case A_STRTAB_BASE: /* 64b */
-        *data = extract64(s->strtab_base, 0, 32);
+        *data = extract64(bank->strtab_base, 0, 32);
         return MEMTX_OK;
     case A_STRTAB_BASE + 4: /* 64b */
-        *data = extract64(s->strtab_base, 32, 32);
+        *data = extract64(bank->strtab_base, 32, 32);
         return MEMTX_OK;
     case A_STRTAB_BASE_CFG:
-        *data = s->strtab_base_cfg;
+        *data = bank->strtab_base_cfg;
         return MEMTX_OK;
     case A_CMDQ_BASE: /* 64b */
-        *data = extract64(s->cmdq.base, 0, 32);
+        *data = extract64(bank->cmdq.base, 0, 32);
         return MEMTX_OK;
     case A_CMDQ_BASE + 4:
-        *data = extract64(s->cmdq.base, 32, 32);
+        *data = extract64(bank->cmdq.base, 32, 32);
         return MEMTX_OK;
     case A_CMDQ_PROD:
-        *data = s->cmdq.prod;
+        *data = bank->cmdq.prod;
         return MEMTX_OK;
     case A_CMDQ_CONS:
-        *data = s->cmdq.cons;
+        *data = bank->cmdq.cons;
         return MEMTX_OK;
     case A_EVENTQ_BASE: /* 64b */
-        *data = extract64(s->eventq.base, 0, 32);
+        *data = extract64(bank->eventq.base, 0, 32);
         return MEMTX_OK;
     case A_EVENTQ_BASE + 4: /* 64b */
-        *data = extract64(s->eventq.base, 32, 32);
+        *data = extract64(bank->eventq.base, 32, 32);
         return MEMTX_OK;
     case A_EVENTQ_PROD:
-        *data = s->eventq.prod;
+        *data = bank->eventq.prod;
         return MEMTX_OK;
     case A_EVENTQ_CONS:
-        *data = s->eventq.cons;
+        *data = bank->eventq.cons;
         return MEMTX_OK;
     default:
         *data = 0;
@@ -2089,9 +2130,10 @@ static const VMStateDescription vmstate_smmuv3_queue = {
 static bool smmuv3_gbpa_needed(void *opaque)
 {
     SMMUv3State *s = opaque;
+    SMMUv3RegBank *bank = smmuv3_bank(s, SMMU_SEC_SID_NS);
 
     /* Only migrate GBPA if it has different reset value. */
-    return s->gbpa != SMMU_GBPA_RESET_VAL;
+    return bank->gbpa != SMMU_GBPA_RESET_VAL;
 }
 
 static const VMStateDescription vmstate_gbpa = {
@@ -2100,7 +2142,7 @@ static const VMStateDescription vmstate_gbpa = {
     .minimum_version_id = 1,
     .needed = smmuv3_gbpa_needed,
     .fields = (const VMStateField[]) {
-        VMSTATE_UINT32(gbpa, SMMUv3State),
+        VMSTATE_UINT32(bank[SMMU_SEC_SID_NS].gbpa, SMMUv3State),
         VMSTATE_END_OF_LIST()
     }
 };
@@ -2111,27 +2153,29 @@ static const VMStateDescription vmstate_smmuv3 = {
     .minimum_version_id = 1,
     .priority = MIG_PRI_IOMMU,
     .fields = (const VMStateField[]) {
-        VMSTATE_UINT32(features, SMMUv3State),
+        VMSTATE_UINT32(bank[SMMU_SEC_SID_NS].features, SMMUv3State),
         VMSTATE_UINT8(sid_size, SMMUv3State),
-        VMSTATE_UINT8(sid_split, SMMUv3State),
+        VMSTATE_UINT8(bank[SMMU_SEC_SID_NS].sid_split, SMMUv3State),
 
-        VMSTATE_UINT32_ARRAY(cr, SMMUv3State, 3),
-        VMSTATE_UINT32(cr0ack, SMMUv3State),
+        VMSTATE_UINT32_ARRAY(bank[SMMU_SEC_SID_NS].cr, SMMUv3State, 3),
+        VMSTATE_UINT32(bank[SMMU_SEC_SID_NS].cr0ack, SMMUv3State),
         VMSTATE_UINT32(statusr, SMMUv3State),
-        VMSTATE_UINT32(irq_ctrl, SMMUv3State),
-        VMSTATE_UINT32(gerror, SMMUv3State),
-        VMSTATE_UINT32(gerrorn, SMMUv3State),
-        VMSTATE_UINT64(gerror_irq_cfg0, SMMUv3State),
-        VMSTATE_UINT32(gerror_irq_cfg1, SMMUv3State),
-        VMSTATE_UINT32(gerror_irq_cfg2, SMMUv3State),
-        VMSTATE_UINT64(strtab_base, SMMUv3State),
-        VMSTATE_UINT32(strtab_base_cfg, SMMUv3State),
-        VMSTATE_UINT64(eventq_irq_cfg0, SMMUv3State),
-        VMSTATE_UINT32(eventq_irq_cfg1, SMMUv3State),
-        VMSTATE_UINT32(eventq_irq_cfg2, SMMUv3State),
-
-        VMSTATE_STRUCT(cmdq, SMMUv3State, 0, vmstate_smmuv3_queue, SMMUQueue),
-        VMSTATE_STRUCT(eventq, SMMUv3State, 0, vmstate_smmuv3_queue, SMMUQueue),
+        VMSTATE_UINT32(bank[SMMU_SEC_SID_NS].irq_ctrl, SMMUv3State),
+        VMSTATE_UINT32(bank[SMMU_SEC_SID_NS].gerror, SMMUv3State),
+        VMSTATE_UINT32(bank[SMMU_SEC_SID_NS].gerrorn, SMMUv3State),
+        VMSTATE_UINT64(bank[SMMU_SEC_SID_NS].gerror_irq_cfg0, SMMUv3State),
+        VMSTATE_UINT32(bank[SMMU_SEC_SID_NS].gerror_irq_cfg1, SMMUv3State),
+        VMSTATE_UINT32(bank[SMMU_SEC_SID_NS].gerror_irq_cfg2, SMMUv3State),
+        VMSTATE_UINT64(bank[SMMU_SEC_SID_NS].strtab_base, SMMUv3State),
+        VMSTATE_UINT32(bank[SMMU_SEC_SID_NS].strtab_base_cfg, SMMUv3State),
+        VMSTATE_UINT64(bank[SMMU_SEC_SID_NS].eventq_irq_cfg0, SMMUv3State),
+        VMSTATE_UINT32(bank[SMMU_SEC_SID_NS].eventq_irq_cfg1, SMMUv3State),
+        VMSTATE_UINT32(bank[SMMU_SEC_SID_NS].eventq_irq_cfg2, SMMUv3State),
+
+        VMSTATE_STRUCT(bank[SMMU_SEC_SID_NS].cmdq, SMMUv3State, 0,
+                       vmstate_smmuv3_queue, SMMUQueue),
+        VMSTATE_STRUCT(bank[SMMU_SEC_SID_NS].eventq, SMMUv3State, 0,
+                       vmstate_smmuv3_queue, SMMUQueue),
 
         VMSTATE_END_OF_LIST(),
     },
diff --git a/hw/arm/tegra241-cmdqv.c b/hw/arm/tegra241-cmdqv.c
index 273633e6293..c7c63f27bb1 100644
--- a/hw/arm/tegra241-cmdqv.c
+++ b/hw/arm/tegra241-cmdqv.c
@@ -974,6 +974,7 @@ static void tegra241_cmdqv_init_regs(SMMUv3State *s, Tegra241CMDQV *cmdqv)
     int i;
     long pgsize;
     uint32_t val;
+    SMMUv3RegBank *bank = smmuv3_bank(s, SMMU_SEC_SID_NS);
 
     cmdqv->config = V_CONFIG_RESET;
     cmdqv->param = FIELD_DP32(0, PARAM, CMDQV_VER, CMDQV_VER);
@@ -1019,8 +1020,9 @@ static void tegra241_cmdqv_init_regs(SMMUv3State *s, Tegra241CMDQV *cmdqv)
     if (pgsize == LONG_MAX) {
         pgsize = qemu_real_host_page_size();
     }
-    val = FIELD_EX32(s->idr[1], IDR1, CMDQS);
-    s->idr[1] = FIELD_DP32(s->idr[1], IDR1, CMDQS, MIN(ctz64(pgsize) - 4, val));
+    val = FIELD_EX32(bank->idr[1], IDR1, CMDQS);
+    bank->idr[1] = FIELD_DP32(bank->idr[1], IDR1, CMDQS,
+                              MIN(ctz64(pgsize) - 4, val));
 }
 
 static void tegra241_cmdqv_reset(SMMUv3State *s)
diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h
index 7b975abc25a..6ea40f6b074 100644
--- a/include/hw/arm/smmu-common.h
+++ b/include/hw/arm/smmu-common.h
@@ -40,6 +40,12 @@
 #define CACHED_ENTRY_TO_ADDR(ent, addr)      ((ent)->entry.translated_addr + \
                                              ((addr) & (ent)->entry.addr_mask))
 
+/* StreamID Security state */
+typedef enum SMMUSecSID {
+    SMMU_SEC_SID_NS = 0,
+    SMMU_SEC_SID_NUM,
+} SMMUSecSID;
+
 /*
  * Page table walk error types
  */
diff --git a/include/hw/arm/smmuv3.h b/include/hw/arm/smmuv3.h
index d39fe8850bc..45033d26a1b 100644
--- a/include/hw/arm/smmuv3.h
+++ b/include/hw/arm/smmuv3.h
@@ -34,19 +34,13 @@ typedef struct SMMUQueue {
      uint8_t log2size;
 } SMMUQueue;
 
-struct SMMUv3State {
-    SMMUState     smmu_state;
-
+typedef struct SMMUv3RegBank {
     uint32_t features;
-    uint8_t sid_size;
     uint8_t sid_split;
 
     uint32_t idr[6];
-    uint32_t iidr;
-    uint32_t aidr;
     uint32_t cr[3];
     uint32_t cr0ack;
-    uint32_t statusr;
     uint32_t gbpa;
     uint32_t irq_ctrl;
     uint32_t gerror;
@@ -61,6 +55,17 @@ struct SMMUv3State {
     uint32_t eventq_irq_cfg2;
 
     SMMUQueue eventq, cmdq;
+} SMMUv3RegBank;
+
+struct SMMUv3State {
+    SMMUState     smmu_state;
+
+    uint8_t sid_size;
+    uint32_t iidr;
+    uint32_t aidr;
+    uint32_t statusr;
+
+    SMMUv3RegBank bank[SMMU_SEC_SID_NUM];
 
     qemu_irq     irq[4];
     QemuMutex mutex;
@@ -103,7 +108,14 @@ bool smmuv3_ats_enabled(struct SMMUv3State *s);
 #define TYPE_ARM_SMMUV3   "arm-smmuv3"
 OBJECT_DECLARE_TYPE(SMMUv3State, SMMUv3Class, ARM_SMMUV3)
 
-#define STAGE1_SUPPORTED(s)      FIELD_EX32(s->idr[0], IDR0, S1P)
-#define STAGE2_SUPPORTED(s)      FIELD_EX32(s->idr[0], IDR0, S2P)
+#define STAGE1_SUPPORTED(s) \
+    FIELD_EX32((s)->bank[SMMU_SEC_SID_NS].idr[0], IDR0, S1P)
+#define STAGE2_SUPPORTED(s) \
+    FIELD_EX32((s)->bank[SMMU_SEC_SID_NS].idr[0], IDR0, S2P)
+
+static inline SMMUv3RegBank *smmuv3_bank(SMMUv3State *s, SMMUSecSID sec_sid)
+{
+    return &s->bank[sec_sid];
+}
 
 #endif
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 03/28] hw/arm/smmuv3: Thread SEC_SID through helper APIs
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (2 preceding siblings ...)
  2026-08-13 16:24 ` [RFC v5 02/28] hw/arm/smmuv3: Introduce banked registers for SMMUv3 state Tao Tang
@ 2026-08-13 16:24 ` Tao Tang
  2026-08-13 16:24 ` [RFC v5 04/28] hw/arm/smmuv3: Track SEC_SID in configs and events Tao Tang
                   ` (23 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:24 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Extend the register and queue helper routines to accept an explicit
SEC_SID argument instead of hard-coding the non-secure bank.

All existing callers are updated to pass SMMU_SEC_SID_NS, so the
behavior remains identical. This prepares the code for handling
additional security state banks in the future. So Non-secure state
is the only state bank supported for now.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
Link: https://lore.kernel.org/qemu-devel/3097d58e-3793-4434-8beb-2e4f4c52f772@redhat.com/
---
 hw/arm/smmuv3-accel.c    |  5 +++--
 hw/arm/smmuv3-internal.h | 21 +++++++++------------
 hw/arm/smmuv3.c          | 15 ++++++++-------
 3 files changed, 20 insertions(+), 21 deletions(-)

diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c
index 3ff3da66acd..fa079c8acea 100644
--- a/hw/arm/smmuv3-accel.c
+++ b/hw/arm/smmuv3-accel.c
@@ -294,6 +294,7 @@ bool smmuv3_accel_install_ste(SMMUv3State *s, SMMUDevice *sdev, int sid,
     SMMUS1Hwpt *s1_hwpt = NULL;
     const char *type;
     STE ste;
+    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
 
     if (!accel || !accel->viommu) {
         return true;
@@ -323,7 +324,7 @@ bool smmuv3_accel_install_ste(SMMUv3State *s, SMMUDevice *sdev, int sid,
      * attach/alloc fails, since the Guest–Host SID mapping stays
      * valid as long as the device is behind the accelerated SMMUv3.
      */
-    if (!smmu_enabled(s)) {
+    if (!smmu_enabled(s, sec_sid)) {
         hwpt_id = smmuv3_accel_gbpa_hwpt(s, accel);
     } else {
         config = STE_CONFIG(&ste);
@@ -558,7 +559,7 @@ bool smmuv3_accel_alloc_veventq(SMMUv3State *s, Error **errp)
         return true;
     }
 
-    if (!smmuv3_eventq_enabled(s)) {
+    if (!smmuv3_eventq_enabled(s, sec_sid)) {
         return true;
     }
 
diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h
index 10154ca161d..a88743ba9cd 100644
--- a/hw/arm/smmuv3-internal.h
+++ b/hw/arm/smmuv3-internal.h
@@ -39,9 +39,8 @@ typedef enum SMMUTranslationClass {
     SMMU_CLASS_IN,
 } SMMUTranslationClass;
 
-static inline int smmu_enabled(SMMUv3State *s)
+static inline int smmu_enabled(SMMUv3State *s, SMMUSecSID sec_sid)
 {
-    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
     SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
     return FIELD_EX32(bank->cr[0], CR0, SMMUEN);
 }
@@ -69,16 +68,16 @@ static inline uint32_t smmuv3_idreg(int regoffset)
     return smmuv3_ids[regoffset / 4];
 }
 
-static inline bool smmuv3_eventq_irq_enabled(SMMUv3State *s)
+static inline bool smmuv3_eventq_irq_enabled(SMMUv3State *s,
+                                             SMMUSecSID sec_sid)
 {
-    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
     SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
     return FIELD_EX32(bank->irq_ctrl, IRQ_CTRL, EVENTQ_IRQEN);
 }
 
-static inline bool smmuv3_gerror_irq_enabled(SMMUv3State *s)
+static inline bool smmuv3_gerror_irq_enabled(SMMUv3State *s,
+                                             SMMUSecSID sec_sid)
 {
-    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
     SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
     return FIELD_EX32(bank->irq_ctrl, IRQ_CTRL, GERROR_IRQEN);
 }
@@ -123,23 +122,21 @@ static inline void queue_cons_incr(SMMUQueue *q)
     q->cons = deposit32(q->cons, 0, q->log2size + 1, q->cons + 1);
 }
 
-static inline bool smmuv3_cmdq_enabled(SMMUv3State *s)
+static inline bool smmuv3_cmdq_enabled(SMMUv3State *s, SMMUSecSID sec_sid)
 {
-    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
     SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
     return FIELD_EX32(bank->cr[0], CR0, CMDQEN);
 }
 
-static inline bool smmuv3_eventq_enabled(SMMUv3State *s)
+static inline bool smmuv3_eventq_enabled(SMMUv3State *s, SMMUSecSID sec_sid)
 {
-    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
     SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
     return FIELD_EX32(bank->cr[0], CR0, EVENTQEN);
 }
 
-static inline void smmu_write_cmdq_err(SMMUv3State *s, uint32_t err_type)
+static inline void smmu_write_cmdq_err(SMMUv3State *s, uint32_t err_type,
+                                       SMMUSecSID sec_sid)
 {
-    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
     SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
     bank->cmdq.cons = FIELD_DP32(bank->cmdq.cons, CMDQ_CONS, ERR, err_type);
 }
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index d3baae8a32a..51e970d710a 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -60,7 +60,7 @@ static void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq,
 
     switch (irq) {
     case SMMU_IRQ_EVTQ:
-        pulse = smmuv3_eventq_irq_enabled(s);
+        pulse = smmuv3_eventq_irq_enabled(s, sec_sid);
         break;
     case SMMU_IRQ_PRIQ:
         qemu_log_mask(LOG_UNIMP, "PRI not yet supported\n");
@@ -80,7 +80,7 @@ static void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq,
         bank->gerror ^= new_gerrors;
         trace_smmuv3_write_gerror(new_gerrors, bank->gerror);
 
-        pulse = smmuv3_gerror_irq_enabled(s);
+        pulse = smmuv3_gerror_irq_enabled(s, sec_sid);
         break;
     }
     }
@@ -156,7 +156,7 @@ static MemTxResult smmuv3_write_eventq(SMMUv3State *s, Evt *evt)
     SMMUQueue *q = &bank->eventq;
     MemTxResult r;
 
-    if (!smmuv3_eventq_enabled(s)) {
+    if (!smmuv3_eventq_enabled(s, sec_sid)) {
         return MEMTX_ERROR;
     }
 
@@ -191,8 +191,9 @@ void smmuv3_propagate_event(SMMUv3State *s, Evt *evt)
 void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *info)
 {
     Evt evt = {};
+    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
 
-    if (!smmuv3_eventq_enabled(s)) {
+    if (!smmuv3_eventq_enabled(s, sec_sid)) {
         return;
     }
 
@@ -1127,7 +1128,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
 
     qemu_mutex_lock(&s->mutex);
 
-    if (!smmu_enabled(s)) {
+    if (!smmu_enabled(s, sec_sid)) {
         if (FIELD_EX32(bank->gbpa, GBPA, ABORT)) {
             status = SMMU_TRANS_ABORT;
         } else {
@@ -1357,7 +1358,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp)
     SMMUQueue *q = &bank->cmdq;
     SMMUCommandType type = 0;
 
-    if (!smmuv3_cmdq_enabled(s)) {
+    if (!smmuv3_cmdq_enabled(s, sec_sid)) {
         return 0;
     }
     /*
@@ -1617,7 +1618,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp)
 
     if (cmd_error) {
         trace_smmuv3_cmdq_consume_error(smmu_cmd_string(type), cmd_error);
-        smmu_write_cmdq_err(s, cmd_error);
+        smmu_write_cmdq_err(s, cmd_error, sec_sid);
         smmuv3_trigger_irq(s, SMMU_IRQ_GERROR, R_GERROR_CMDQ_ERR_MASK);
     }
 
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 04/28] hw/arm/smmuv3: Track SEC_SID in configs and events
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (3 preceding siblings ...)
  2026-08-13 16:24 ` [RFC v5 03/28] hw/arm/smmuv3: Thread SEC_SID through helper APIs Tao Tang
@ 2026-08-13 16:24 ` Tao Tang
  2026-08-13 16:24 ` [RFC v5 05/28] hw/arm/smmu-common: Add security-aware address space selector Tao Tang
                   ` (22 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:24 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Pass the SEC_SID explicitly to smmuv3_decode_config() so configuration
decoding can be tied to the correct register bank.

Plumb the SEC_SID through tracepoints and queue helpers so diagnostics
and event logs always show which security interface emitted the record.
To support this, the SEC_SID is placed in SMMUEventInfo so the bank is
identified as soon as an event record is built.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
---
 hw/arm/smmuv3-accel.c    |  2 +-
 hw/arm/smmuv3-internal.h |  3 ++-
 hw/arm/smmuv3.c          | 26 +++++++++++++++-----------
 hw/arm/trace-events      |  2 +-
 include/hw/arm/smmuv3.h  |  1 +
 5 files changed, 20 insertions(+), 14 deletions(-)

diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c
index fa079c8acea..2ce94786829 100644
--- a/hw/arm/smmuv3-accel.c
+++ b/hw/arm/smmuv3-accel.c
@@ -509,7 +509,7 @@ static void smmuv3_accel_event_read(void *opaque)
     if (ret > 0) {
         return; /* EAGAIN/EINTR */
     }
-    smmuv3_propagate_event(s, (Evt *)&buf.vevent);
+    smmuv3_propagate_event(s, (Evt *)&buf.vevent, SMMU_SEC_SID_NS);
 }
 
 static void smmuv3_accel_free_veventq(SMMUv3AccelState *accel)
diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h
index a88743ba9cd..202cd533636 100644
--- a/hw/arm/smmuv3-internal.h
+++ b/hw/arm/smmuv3-internal.h
@@ -274,6 +274,7 @@ static inline const char *smmu_event_string(SMMUEventType type)
 
 /*  Encode an event record */
 typedef struct SMMUEventInfo {
+    SMMUSecSID sec_sid;
     SMMUEventType type;
     uint32_t sid;
     bool recorded;
@@ -365,7 +366,7 @@ typedef struct SMMUEventInfo {
 #define EVT_GET_SID(x)   ((x)->word[1])
 
 void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *event);
-void smmuv3_propagate_event(SMMUv3State *s, Evt *evt);
+void smmuv3_propagate_event(SMMUv3State *s, Evt *evt, SMMUSecSID sec_sid);
 int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste, SMMUEventInfo *event);
 
 #define STE_SIZE 6
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index 51e970d710a..649050b9347 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -149,9 +149,9 @@ static MemTxResult queue_write(SMMUQueue *q, Evt *evt_in)
     return MEMTX_OK;
 }
 
-static MemTxResult smmuv3_write_eventq(SMMUv3State *s, Evt *evt)
+static MemTxResult smmuv3_write_eventq(SMMUv3State *s, SMMUSecSID sec_sid,
+                                       Evt *evt)
 {
-    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
     SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
     SMMUQueue *q = &bank->eventq;
     MemTxResult r;
@@ -175,14 +175,14 @@ static MemTxResult smmuv3_write_eventq(SMMUv3State *s, Evt *evt)
     return MEMTX_OK;
 }
 
-void smmuv3_propagate_event(SMMUv3State *s, Evt *evt)
+void smmuv3_propagate_event(SMMUv3State *s, Evt *evt, SMMUSecSID sec_sid)
 {
     MemTxResult r;
 
-    trace_smmuv3_propagate_event(smmu_event_string(EVT_GET_TYPE(evt)),
+    trace_smmuv3_propagate_event(sec_sid, smmu_event_string(EVT_GET_TYPE(evt)),
                                  EVT_GET_SID(evt));
     QEMU_LOCK_GUARD(&s->mutex);
-    r = smmuv3_write_eventq(s, evt);
+    r = smmuv3_write_eventq(s, sec_sid, evt);
     if (r != MEMTX_OK) {
         smmuv3_trigger_irq(s, SMMU_IRQ_GERROR, R_GERROR_EVENTQ_ABT_ERR_MASK);
     }
@@ -191,7 +191,7 @@ void smmuv3_propagate_event(SMMUv3State *s, Evt *evt)
 void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *info)
 {
     Evt evt = {};
-    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
+    SMMUSecSID sec_sid = info->sec_sid;
 
     if (!smmuv3_eventq_enabled(s, sec_sid)) {
         return;
@@ -271,7 +271,7 @@ void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *info)
         g_assert_not_reached();
     }
 
-    smmuv3_propagate_event(s, &evt);
+    smmuv3_propagate_event(s, &evt, sec_sid);
     info->recorded = true;
 }
 
@@ -886,12 +886,13 @@ bad_cd:
  * @cfg: output translation configuration which is populated through
  *       the different configuration decoding steps
  * @event: must be zero'ed by the caller
+ * @sec_sid: StreamID Security state
  *
  * return < 0 in case of config decoding error (@event is filled
  * accordingly). Return 0 otherwise.
  */
 static int smmuv3_decode_config(IOMMUMemoryRegion *mr, SMMUTransCfg *cfg,
-                                SMMUEventInfo *event)
+                                SMMUEventInfo *event, SMMUSecSID sec_sid)
 {
     SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu);
     uint32_t sid = smmu_get_sid(sdev);
@@ -958,7 +959,7 @@ static SMMUTransCfg *smmuv3_get_config(SMMUDevice *sdev, SMMUEventInfo *event)
                             (sdev->cfg_cache_hits + sdev->cfg_cache_misses));
         cfg = g_new0(SMMUTransCfg, 1);
 
-        if (!smmuv3_decode_config(&sdev->iommu, cfg, event)) {
+        if (!smmuv3_decode_config(&sdev->iommu, cfg, event, SMMU_SEC_SID_NS)) {
             g_hash_table_insert(bc->configs, sdev, cfg);
         } else {
             g_free(cfg);
@@ -1114,7 +1115,8 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
     SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
     SMMUEventInfo event = {.type = SMMU_EVT_NONE,
                            .sid = sid,
-                           .inval_ste_allowed = false};
+                           .inval_ste_allowed = false,
+                           .sec_sid = sec_sid};
     SMMUTranslationStatus status;
     SMMUTransCfg *cfg = NULL;
     IOMMUTLBEntry entry = {
@@ -1216,7 +1218,9 @@ static void smmuv3_notify_iova(IOMMUMemoryRegion *mr,
                                uint64_t num_pages, int stage)
 {
     SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu);
-    SMMUEventInfo eventinfo = {.inval_ste_allowed = true};
+    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
+    SMMUEventInfo eventinfo = {.sec_sid = sec_sid,
+                               .inval_ste_allowed = true};
     SMMUTransCfg *cfg = smmuv3_get_config(sdev, &eventinfo);
     IOMMUTLBEvent event;
     uint8_t granule;
diff --git a/hw/arm/trace-events b/hw/arm/trace-events
index 1b16f710fed..a166b79c8ef 100644
--- a/hw/arm/trace-events
+++ b/hw/arm/trace-events
@@ -41,7 +41,7 @@ smmuv3_cmdq_opcode(const char *opcode) "<--- %s"
 smmuv3_cmdq_consume_out(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "prod:%d, cons:%d, prod_wrap:%d, cons_wrap:%d "
 smmuv3_cmdq_consume_error(const char *cmd_name, uint8_t cmd_error) "Error on %s command execution: %d"
 smmuv3_write_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x(%d)"
-smmuv3_propagate_event(const char *type, uint32_t sid) "%s sid=0x%x"
+smmuv3_propagate_event(int sec_sid, const char *type, uint32_t sid) "sec_sid=%d %s sid=0x%x"
 smmuv3_find_ste(uint16_t sid, uint32_t features, uint16_t sid_split) "sid=0x%x features:0x%x, sid_split:0x%x"
 smmuv3_find_ste_2lvl(uint64_t strtab_base, uint64_t l1ptr, int l1_ste_offset, uint64_t l2ptr, int l2_ste_offset, int max_l2_ste) "strtab_base:0x%"PRIx64" l1ptr:0x%"PRIx64" l1_off:0x%x, l2ptr:0x%"PRIx64" l2_off:0x%x max_l2_ste:%d"
 smmuv3_get_ste(uint64_t addr) "STE addr: 0x%"PRIx64
diff --git a/include/hw/arm/smmuv3.h b/include/hw/arm/smmuv3.h
index 45033d26a1b..8cd2edc3412 100644
--- a/include/hw/arm/smmuv3.h
+++ b/include/hw/arm/smmuv3.h
@@ -115,6 +115,7 @@ OBJECT_DECLARE_TYPE(SMMUv3State, SMMUv3Class, ARM_SMMUV3)
 
 static inline SMMUv3RegBank *smmuv3_bank(SMMUv3State *s, SMMUSecSID sec_sid)
 {
+    g_assert(sec_sid < SMMU_SEC_SID_NUM);
     return &s->bank[sec_sid];
 }
 
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 05/28] hw/arm/smmu-common: Add security-aware address space selector
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (4 preceding siblings ...)
  2026-08-13 16:24 ` [RFC v5 04/28] hw/arm/smmuv3: Track SEC_SID in configs and events Tao Tang
@ 2026-08-13 16:24 ` Tao Tang
  2026-08-13 16:24 ` [RFC v5 06/28] hw/arm/smmuv3: Plumb transaction attributes into config helpers Tao Tang
                   ` (21 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:24 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Introduce SMMU_SEC_SID_S to represent SEC_SID == 1, meaning Secure. And
then provide smmu_get_address_space, a SMMU instance-based address space
selector. The helper returns the per-device memory or secure-memory
AddressSpace selected by SEC_SID.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
---
 hw/arm/smmu-common.c         | 15 +++++++++++++++
 include/hw/arm/smmu-common.h |  3 +++
 2 files changed, 18 insertions(+)

diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c
index 8e40ba603d3..54a6a9f4d62 100644
--- a/hw/arm/smmu-common.c
+++ b/hw/arm/smmu-common.c
@@ -30,6 +30,21 @@
 #include "hw/arm/smmu-common.h"
 #include "smmu-internal.h"
 
+AddressSpace *smmu_get_address_space(SMMUState *s, SMMUSecSID sec_sid)
+{
+    switch (sec_sid) {
+    case SMMU_SEC_SID_NS:
+        return &s->memory_as;
+    case SMMU_SEC_SID_S:
+        g_assert(s->secure_memory);
+        g_assert(s->secure_memory_as.root);
+        return &s->secure_memory_as;
+    case SMMU_SEC_SID_NUM:
+        g_assert_not_reached();
+    }
+    g_assert_not_reached();
+}
+
 /* IOTLB Management */
 
 static guint smmu_iotlb_key_hash(gconstpointer v)
diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h
index 6ea40f6b074..9d6e2d3038f 100644
--- a/include/hw/arm/smmu-common.h
+++ b/include/hw/arm/smmu-common.h
@@ -43,6 +43,7 @@
 /* StreamID Security state */
 typedef enum SMMUSecSID {
     SMMU_SEC_SID_NS = 0,
+    SMMU_SEC_SID_S,
     SMMU_SEC_SID_NUM,
 } SMMUSecSID;
 
@@ -188,6 +189,8 @@ struct SMMUBaseClass {
 #define TYPE_ARM_SMMU "arm-smmu"
 OBJECT_DECLARE_TYPE(SMMUState, SMMUBaseClass, ARM_SMMU)
 
+AddressSpace *smmu_get_address_space(SMMUState *s, SMMUSecSID sec_sid);
+
 /* Return the SMMUPciBus handle associated to a PCI bus number */
 SMMUPciBus *smmu_find_smmu_pcibus(SMMUState *s, uint8_t bus_num);
 
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 06/28] hw/arm/smmuv3: Plumb transaction attributes into config helpers
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (5 preceding siblings ...)
  2026-08-13 16:24 ` [RFC v5 05/28] hw/arm/smmu-common: Add security-aware address space selector Tao Tang
@ 2026-08-13 16:24 ` Tao Tang
  2026-08-13 16:24 ` [RFC v5 07/28] hw/arm/smmuv3: Reject secure STEs with stage-2 enabled Tao Tang
                   ` (20 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:24 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Introduce helpers that derive MemTxAttrs and select an AddressSpace from
SEC_SID, then pass SEC_SID through the Stream Table and Context Descriptor
fetch paths. This makes reads of SMMU configuration structures use the
corresponding Non-secure or Secure memory context.

To support these helpers, include hw/arm/arm-security.h from smmu-common.h,
making the Arm security-space definitions available to the common SMMU code.

The accelerated path remains Non-secure-only and therefore passes
SMMU_SEC_SID_NS explicitly.

For now, the configuration cache lookup key remains based solely on the
SMMUDevice pointer. It is extended with SEC_SID in a later commit. Other
SEC_SID-sensitive memory accesses that still use the Non-secure address
space are converted in follow-up commits.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
---
 hw/arm/smmu-common.c         | 30 +++++++++++++++++++++----
 hw/arm/smmuv3-accel.c        |  2 +-
 hw/arm/smmuv3-internal.h     |  3 ++-
 hw/arm/smmuv3.c              | 43 ++++++++++++++++++++++--------------
 include/hw/arm/smmu-common.h | 10 +++++++++
 5 files changed, 65 insertions(+), 23 deletions(-)

diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c
index 54a6a9f4d62..89e1e2826d4 100644
--- a/hw/arm/smmu-common.c
+++ b/hw/arm/smmu-common.c
@@ -30,6 +30,27 @@
 #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:
+        return ARMSS_NonSecure;
+    case SMMU_SEC_SID_NUM:
+        g_assert_not_reached();
+    }
+    g_assert_not_reached();
+}
+
+MemTxAttrs smmu_get_txattrs(SMMUSecSID sec_sid)
+{
+    return (MemTxAttrs) {
+        .secure = smmu_sec_sid_is_secure(sec_sid) ? 1 : 0,
+        .space = smmu_get_security_space(sec_sid),
+    };
+}
+
 AddressSpace *smmu_get_address_space(SMMUState *s, SMMUSecSID sec_sid)
 {
     switch (sec_sid) {
@@ -588,6 +609,7 @@ error:
 /**
  * smmu_ptw_64_s2 - VMSAv8-64 Walk of the page tables for a given ipa
  * for stage-2.
+ * @bs: smmu state which includes TLB instance
  * @cfg: translation config
  * @ipa: ipa to translate
  * @perm: access type
@@ -599,7 +621,7 @@ error:
  * Upon success, @tlbe is filled with translated_addr and entry
  * permission rights.
  */
-static int smmu_ptw_64_s2(SMMUTransCfg *cfg,
+static int smmu_ptw_64_s2(SMMUState *bs, SMMUTransCfg *cfg,
                           dma_addr_t ipa, IOMMUAccessFlags perm,
                           SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info)
 {
@@ -636,7 +658,6 @@ static int smmu_ptw_64_s2(SMMUTransCfg *cfg,
         uint64_t pte, gpa;
         dma_addr_t pte_addr = baseaddr + offset * sizeof(pte);
         uint8_t s2ap;
-
         if (get_pte(baseaddr, offset, &pte, info)) {
                 goto error;
         }
@@ -690,6 +711,7 @@ static int smmu_ptw_64_s2(SMMUTransCfg *cfg,
             goto error_ipa;
         }
 
+        tlbe->entry.target_as = &bs->memory_as;
         tlbe->entry.translated_addr = gpa;
         tlbe->entry.iova = ipa & ~mask;
         tlbe->entry.addr_mask = mask;
@@ -765,7 +787,7 @@ int smmu_ptw(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t iova,
             return -EINVAL;
         }
 
-        return smmu_ptw_64_s2(cfg, iova, perm, tlbe, info);
+        return smmu_ptw_64_s2(bs, cfg, iova, perm, tlbe, info);
     }
 
     /* SMMU_NESTED. */
@@ -775,7 +797,7 @@ int smmu_ptw(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t iova,
     }
 
     ipa = CACHED_ENTRY_TO_ADDR(tlbe, iova);
-    ret = smmu_ptw_64_s2(cfg, ipa, perm, &tlbe_s2, info);
+    ret = smmu_ptw_64_s2(bs, cfg, ipa, perm, &tlbe_s2, info);
     if (ret) {
         return ret;
     }
diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c
index 2ce94786829..9d207acc8e3 100644
--- a/hw/arm/smmuv3-accel.c
+++ b/hw/arm/smmuv3-accel.c
@@ -310,7 +310,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, sec_sid)) {
         /* No STE found, nothing to install */
         return true;
     }
diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h
index 202cd533636..1f2f426da01 100644
--- a/hw/arm/smmuv3-internal.h
+++ b/hw/arm/smmuv3-internal.h
@@ -367,7 +367,8 @@ typedef struct SMMUEventInfo {
 
 void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *event);
 void smmuv3_propagate_event(SMMUv3State *s, Evt *evt, SMMUSecSID sec_sid);
-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,
+                  SMMUSecSID sec_sid);
 
 #define STE_SIZE 6
 #define L1STD_SIZE 3
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index 649050b9347..b5dc67dcb01 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -366,14 +366,15 @@ static void smmuv3_reset(SMMUv3State *s)
 }
 
 static int smmu_get_ste(SMMUv3State *s, dma_addr_t addr, STE *buf,
-                        SMMUEventInfo *event)
+                        SMMUEventInfo *event, SMMUSecSID sec_sid)
 {
+    AddressSpace *as = smmu_get_address_space(ARM_SMMU(s), sec_sid);
+    MemTxAttrs txattrs = smmu_get_txattrs(sec_sid);
     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(as, addr, buf, sizeof(*buf), txattrs);
     if (ret != MEMTX_OK) {
         qemu_log_mask(LOG_GUEST_ERROR,
                       "Cannot fetch pte at address=0x%"PRIx64"\n", addr);
@@ -396,12 +397,15 @@ static SMMUTranslationStatus smmuv3_do_translate(SMMUv3State *s, hwaddr addr,
                                                  SMMUTranslationClass class);
 /* @ssid > 0 not supported yet */
 static int smmu_get_cd(SMMUv3State *s, STE *ste, SMMUTransCfg *cfg,
-                       uint32_t ssid, CD *buf, SMMUEventInfo *event)
+                       uint32_t ssid, CD *buf, SMMUEventInfo *event,
+                       SMMUSecSID sec_sid)
 {
     dma_addr_t addr = STE_CTXPTR(ste);
     int ret, i;
     SMMUTranslationStatus status;
     SMMUTLBEntry *entry;
+    AddressSpace *as;
+    MemTxAttrs txattrs;
 
     trace_smmuv3_get_cd(addr);
 
@@ -417,9 +421,10 @@ static int smmu_get_cd(SMMUv3State *s, STE *ste, SMMUTransCfg *cfg,
         addr = CACHED_ENTRY_TO_ADDR(entry, addr);
     }
 
+    as = smmu_get_address_space(ARM_SMMU(s), sec_sid);
+    txattrs = smmu_get_txattrs(sec_sid);
     /* TODO: guarantee 64-bit single-copy atomicity */
-    ret = dma_memory_read(&address_space_memory, addr, buf, sizeof(*buf),
-                          MEMTXATTRS_UNSPECIFIED);
+    ret = dma_memory_read(as, addr, buf, sizeof(*buf), txattrs);
     if (ret != MEMTX_OK) {
         qemu_log_mask(LOG_GUEST_ERROR,
                       "Cannot fetch pte at address=0x%"PRIx64"\n", addr);
@@ -680,18 +685,21 @@ bad_ste:
  * @sid: stream ID
  * @ste: returned stream table entry
  * @event: handle to an event info
+ * @sec_sid: StreamID Security state
  *
  * 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,
+                  SMMUSecSID sec_sid)
 {
     dma_addr_t addr, strtab_base;
     uint32_t log2size;
     int strtab_size;
     int ret;
-    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
     SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
+    AddressSpace *as = smmu_get_address_space(ARM_SMMU(s), sec_sid);
+    MemTxAttrs txattrs = smmu_get_txattrs(sec_sid);
 
     trace_smmuv3_find_ste(sid, bank->features, bank->sid_split);
     log2size = FIELD_EX32(bank->strtab_base_cfg, STRTAB_BASE_CFG, LOG2SIZE);
@@ -718,8 +726,7 @@ 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(as, l1ptr, &l1std, sizeof(l1std), txattrs);
         if (ret != MEMTX_OK) {
             qemu_log_mask(LOG_GUEST_ERROR,
                           "Could not read L1PTR at 0X%"PRIx64"\n", l1ptr);
@@ -773,7 +780,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, sec_sid)) {
         return -EINVAL;
     }
 
@@ -904,7 +911,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, sec_sid);
     if (ret) {
         return ret;
     }
@@ -918,7 +925,7 @@ static int smmuv3_decode_config(IOMMUMemoryRegion *mr, SMMUTransCfg *cfg,
         return 0;
     }
 
-    ret = smmu_get_cd(s, &ste, cfg, 0 /* ssid */, &cd, event);
+    ret = smmu_get_cd(s, &ste, cfg, 0 /* ssid */, &cd, event, sec_sid);
     if (ret) {
         return ret;
     }
@@ -933,12 +940,14 @@ static int smmuv3_decode_config(IOMMUMemoryRegion *mr, SMMUTransCfg *cfg,
  *
  * @sdev: SMMUDevice handle
  * @event: output event info
+ * @sec_sid: StreamID Security state
  *
  * The configuration cache contains data resulting from both STE and CD
  * 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;
@@ -959,7 +968,7 @@ static SMMUTransCfg *smmuv3_get_config(SMMUDevice *sdev, SMMUEventInfo *event)
                             (sdev->cfg_cache_hits + sdev->cfg_cache_misses));
         cfg = g_new0(SMMUTransCfg, 1);
 
-        if (!smmuv3_decode_config(&sdev->iommu, cfg, event, SMMU_SEC_SID_NS)) {
+        if (!smmuv3_decode_config(&sdev->iommu, cfg, event, sec_sid)) {
             g_hash_table_insert(bc->configs, sdev, cfg);
         } else {
             g_free(cfg);
@@ -1139,7 +1148,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;
@@ -1221,7 +1230,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 9d6e2d3038f..a31ffcf6e71 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
  */
@@ -200,6 +204,12 @@ SMMUPciBus *smmu_get_sbus(SMMUState *s, PCIBus *bus);
 /* Initialize SMMUDevice handle associated to a SMMUPciBus */
 void smmu_init_sdev(SMMUState *s, SMMUDevice *sdev, PCIBus *bus, int devfn);
 
+
+static inline bool smmu_sec_sid_is_secure(SMMUSecSID sec_sid)
+{
+    return sec_sid == SMMU_SEC_SID_S;
+}
+
 /* Return the stream ID of an SMMU device */
 static inline uint16_t smmu_get_sid(SMMUDevice *sdev)
 {
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 07/28] hw/arm/smmuv3: Reject secure STEs with stage-2 enabled
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (6 preceding siblings ...)
  2026-08-13 16:24 ` [RFC v5 06/28] hw/arm/smmuv3: Plumb transaction attributes into config helpers Tao Tang
@ 2026-08-13 16:24 ` Tao Tang
  2026-08-13 16:24 ` [RFC v5 08/28] hw/arm/smmu-common: Key configuration cache on SMMUDevice and SEC_SID Tao Tang
                   ` (19 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:24 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Pass sec_sid into decode_ste() and treat a secure STE that enables stage-2
translation as an illegal STE.

The current model does not implement Secure stage-2 translation, so reject
Secure STE configurations that enable stage 2 and report BAD_STE. This
restriction can be relaxed when SEL2 and Secure stage-2 support are
implemented.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
---
 hw/arm/smmuv3.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index b5dc67dcb01..3ff2f498a3b 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -597,7 +597,7 @@ static void decode_ste_config(SMMUTransCfg *cfg, uint32_t config)
 
 /* Returns < 0 in case of invalid STE, 0 otherwise */
 static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg,
-                      STE *ste, SMMUEventInfo *event)
+                      STE *ste, SMMUEventInfo *event, SMMUSecSID sec_sid)
 {
     uint32_t config;
     /* OAS field only presents on NS-IDR5 so we use hardcoded SMMU_SEC_SID_NS */
@@ -651,6 +651,14 @@ static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg,
         if (ret) {
             goto bad_ste;
         }
+
+        /*
+         * It is ILLEGAL to set STE.Config == 0b11x according to (IHI 0070G.b)
+         * 5.2 STE, Stream Table Entry, Page 218.
+         */
+        if (sec_sid == SMMU_SEC_SID_S) {
+            goto bad_ste;
+        }
     }
 
     /* Multiple context descriptors require SubstreamID support */
@@ -916,7 +924,7 @@ static int smmuv3_decode_config(IOMMUMemoryRegion *mr, SMMUTransCfg *cfg,
         return ret;
     }
 
-    ret = decode_ste(s, cfg, &ste, event);
+    ret = decode_ste(s, cfg, &ste, event, sec_sid);
     if (ret) {
         return ret;
     }
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 08/28] hw/arm/smmu-common: Key configuration cache on SMMUDevice and SEC_SID
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (7 preceding siblings ...)
  2026-08-13 16:24 ` [RFC v5 07/28] hw/arm/smmuv3: Reject secure STEs with stage-2 enabled Tao Tang
@ 2026-08-13 16:24 ` Tao Tang
  2026-08-13 16:24 ` [RFC v5 09/28] hw/arm/smmu: Add PTE NS/NSTable helpers Tao Tang
                   ` (18 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:24 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Adapt the configuration cache to distinguish entries decoded through
different SEC_SID-selected programming interfaces by introducing a
composite SMMUConfigKey. The key combines SMMUDevice with SEC_SID,
preventing Secure and Non-secure configuration entries from aliasing in
the cache and leaving the key extensible for a future Realm SEC_SID
namespace.

The cache lookup, insertion, and invalidation mechanisms are updated to
use this new keying infrastructure.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
Link: https://lore.kernel.org/qemu-devel/a9a840a6-c65f-4352-9a49-ddd1b5483f43@redhat.com/
---
 hw/arm/smmu-common.c         | 45 ++++++++++++++++++++++++++++++++++--
 hw/arm/smmuv3.c              | 12 ++++++----
 include/hw/arm/smmu-common.h |  7 ++++++
 3 files changed, 57 insertions(+), 7 deletions(-)

diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c
index 89e1e2826d4..63c36329a98 100644
--- a/hw/arm/smmu-common.c
+++ b/hw/arm/smmu-common.c
@@ -30,6 +30,26 @@
 #include "hw/arm/smmu-common.h"
 #include "smmu-internal.h"
 
+/* Configuration Cache Management */
+static guint smmu_config_key_hash(gconstpointer key)
+{
+    const SMMUConfigKey *k = key;
+    return g_direct_hash(k->sdev) ^ (guint)k->sec_sid;
+}
+
+static gboolean smmu_config_key_equal(gconstpointer a, gconstpointer b)
+{
+    const SMMUConfigKey *ka = a;
+    const SMMUConfigKey *kb = b;
+    return ka->sdev == kb->sdev && ka->sec_sid == kb->sec_sid;
+}
+
+SMMUConfigKey smmu_get_config_key(SMMUDevice *sdev, SMMUSecSID sec_sid)
+{
+    SMMUConfigKey key = {.sdev = sdev, .sec_sid = sec_sid};
+    return key;
+}
+
 ARMSecuritySpace smmu_get_security_space(SMMUSecSID sec_sid)
 {
     switch (sec_sid) {
@@ -265,7 +285,8 @@ static gboolean smmu_hash_remove_by_vmid_ipa(gpointer key, gpointer value,
 static gboolean
 smmu_hash_remove_by_sid_range(gpointer key, gpointer value, gpointer user_data)
 {
-    SMMUDevice *sdev = (SMMUDevice *)key;
+    SMMUConfigKey *config_key = (SMMUConfigKey *)key;
+    SMMUDevice *sdev = config_key->sdev;
     uint32_t sid = smmu_get_sid(sdev);
     SMMUSIDRange *sid_range = (SMMUSIDRange *)user_data;
 
@@ -283,6 +304,24 @@ void smmu_configs_inv_sid_range(SMMUState *s, SMMUSIDRange sid_range)
                                 &sid_range);
 }
 
+static gboolean smmu_hash_remove_by_sdev(gpointer key, gpointer value,
+                                         gpointer user_data)
+{
+    SMMUConfigKey *config_key = (SMMUConfigKey *)key;
+    SMMUDevice *target = (SMMUDevice *)user_data;
+
+    if (config_key->sdev != target) {
+        return false;
+    }
+    trace_smmu_config_cache_inv(smmu_get_sid(target));
+    return true;
+}
+
+void smmu_configs_inv_sdev(SMMUState *s, SMMUDevice *sdev)
+{
+    g_hash_table_foreach_remove(s->configs, smmu_hash_remove_by_sdev, sdev);
+}
+
 void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova,
                          uint8_t tg, uint64_t num_pages, uint8_t ttl)
 {
@@ -979,7 +1018,9 @@ static void smmu_base_realize(DeviceState *dev, Error **errp)
         error_propagate(errp, local_err);
         return;
     }
-    s->configs = g_hash_table_new_full(NULL, NULL, NULL, g_free);
+    s->configs = g_hash_table_new_full(smmu_config_key_hash,
+                                       smmu_config_key_equal,
+                                       g_free, g_free);
     s->iotlb = g_hash_table_new_full(smmu_iotlb_key_hash, smmu_iotlb_key_equal,
                                      g_free, g_free);
     s->smmu_pcibus_by_busptr = g_hash_table_new(NULL, NULL);
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index 3ff2f498a3b..a01175cc672 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -952,7 +952,7 @@ static int smmuv3_decode_config(IOMMUMemoryRegion *mr, SMMUTransCfg *cfg,
  *
  * The configuration cache contains data resulting from both STE and CD
  * decoding under the form of an SMMUTransCfg struct. The hash table is indexed
- * by the SMMUDevice handle.
+ * by a composite key of the SMMUDevice and the sec_sid.
  */
 static SMMUTransCfg *smmuv3_get_config(SMMUDevice *sdev, SMMUEventInfo *event,
                                        SMMUSecSID sec_sid)
@@ -960,8 +960,9 @@ static SMMUTransCfg *smmuv3_get_config(SMMUDevice *sdev, SMMUEventInfo *event,
     SMMUv3State *s = sdev->smmu;
     SMMUState *bc = &s->smmu_state;
     SMMUTransCfg *cfg;
+    SMMUConfigKey lookup_key = smmu_get_config_key(sdev, sec_sid);
 
-    cfg = g_hash_table_lookup(bc->configs, sdev);
+    cfg = g_hash_table_lookup(bc->configs, &lookup_key);
     if (cfg) {
         sdev->cfg_cache_hits++;
         trace_smmuv3_config_cache_hit(smmu_get_sid(sdev),
@@ -977,7 +978,9 @@ static SMMUTransCfg *smmuv3_get_config(SMMUDevice *sdev, SMMUEventInfo *event,
         cfg = g_new0(SMMUTransCfg, 1);
 
         if (!smmuv3_decode_config(&sdev->iommu, cfg, event, sec_sid)) {
-            g_hash_table_insert(bc->configs, sdev, cfg);
+            SMMUConfigKey *persistent_key = g_new(SMMUConfigKey, 1);
+            *persistent_key = lookup_key;
+            g_hash_table_insert(bc->configs, persistent_key, cfg);
         } else {
             g_free(cfg);
             cfg = NULL;
@@ -991,8 +994,7 @@ static void smmuv3_flush_config(SMMUDevice *sdev)
     SMMUv3State *s = sdev->smmu;
     SMMUState *bc = &s->smmu_state;
 
-    trace_smmu_config_cache_inv(smmu_get_sid(sdev));
-    g_hash_table_remove(bc->configs, sdev);
+    smmu_configs_inv_sdev(bc, sdev);
 }
 
 /* Do translation with TLB lookup. */
diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h
index a31ffcf6e71..bd83648b14c 100644
--- a/include/hw/arm/smmu-common.h
+++ b/include/hw/arm/smmu-common.h
@@ -153,6 +153,11 @@ typedef struct SMMUIOTLBKey {
     uint8_t level;
 } SMMUIOTLBKey;
 
+typedef struct SMMUConfigKey {
+    SMMUDevice *sdev;
+    SMMUSecSID sec_sid;
+} SMMUConfigKey;
+
 typedef struct SMMUSIDRange {
     uint32_t start;
     uint32_t end;
@@ -247,6 +252,7 @@ SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg,
 void smmu_iotlb_insert(SMMUState *bs, SMMUTransCfg *cfg, SMMUTLBEntry *entry);
 SMMUIOTLBKey smmu_get_iotlb_key(int asid, int vmid, uint64_t iova,
                                 uint8_t tg, uint8_t level);
+SMMUConfigKey smmu_get_config_key(SMMUDevice *sdev, SMMUSecSID sec_sid);
 void smmu_iotlb_inv_all(SMMUState *s);
 void smmu_iotlb_inv_asid_vmid(SMMUState *s, int asid, int vmid);
 void smmu_iotlb_inv_vmid(SMMUState *s, int vmid);
@@ -256,6 +262,7 @@ void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova,
 void smmu_iotlb_inv_ipa(SMMUState *s, int vmid, dma_addr_t ipa, uint8_t tg,
                         uint64_t num_pages, uint8_t ttl);
 void smmu_configs_inv_sid_range(SMMUState *s, SMMUSIDRange sid_range);
+void smmu_configs_inv_sdev(SMMUState *s, SMMUDevice *sdev);
 /* Unmap the range of all the notifiers registered to any IOMMU mr */
 void smmu_inv_notifiers_all(SMMUState *s);
 
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 09/28] hw/arm/smmu: Add PTE NS/NSTable helpers
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (8 preceding siblings ...)
  2026-08-13 16:24 ` [RFC v5 08/28] hw/arm/smmu-common: Key configuration cache on SMMUDevice and SEC_SID Tao Tang
@ 2026-08-13 16:24 ` Tao Tang
  2026-08-13 16:24 ` [RFC v5 10/28] hw/arm/smmuv3: Store CD NSCFG in TT info Tao Tang
                   ` (17 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:24 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Add helper macros for NS and NSTable bits and group PTE attribute
accessors for clarity. No functional change beyond the new helpers.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
---
 hw/arm/smmu-internal.h | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/hw/arm/smmu-internal.h b/hw/arm/smmu-internal.h
index d143d296f34..004abd58bca 100644
--- a/hw/arm/smmu-internal.h
+++ b/hw/arm/smmu-internal.h
@@ -58,16 +58,27 @@
     ((level == 3) &&                                                    \
      ((pte & ARM_LPAE_PTE_TYPE_MASK) == ARM_LPAE_L3_PTE_TYPE_PAGE))
 
+/* Stage 1 attribute fields in VMSAv8-64 Block and Page descriptors */
+/* Non-secure bit */
+#define PTE_NS(pte) \
+    (extract64(pte, 5, 1))
+
 /* access permissions */
 
 #define PTE_AP(pte) \
     (extract64(pte, 6, 2))
 
+/* Stage 1 VMSAv8-64 Table descriptor fields */
 #define PTE_APTABLE(pte) \
     (extract64(pte, 61, 2))
 
+#define PTE_NSTABLE(pte) \
+    (extract64(pte, 63, 1))
+
+/* Access flag: defined for both Block/Page and Table descriptors. */
 #define PTE_AF(pte) \
     (extract64(pte, 10, 1))
+
 /*
  * TODO: At the moment all transactions are considered as privileged (EL1)
  * as IOMMU translation callback does not pass user/priv attributes.
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 10/28] hw/arm/smmuv3: Store CD NSCFG in TT info
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (9 preceding siblings ...)
  2026-08-13 16:24 ` [RFC v5 09/28] hw/arm/smmu: Add PTE NS/NSTable helpers Tao Tang
@ 2026-08-13 16:24 ` Tao Tang
  2026-08-13 16:25 ` [RFC v5 11/28] hw/arm/smmu-common: Implement secure state handling in ptw Tao Tang
                   ` (16 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:24 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

With NSCFG definitions in place, record the per-table NSCFG bits
in SMMUTransTableInfo during CD decode for later use.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
---
 hw/arm/smmuv3.c              | 1 +
 include/hw/arm/smmu-common.h | 1 +
 2 files changed, 2 insertions(+)

diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index a01175cc672..6b34f76f11b 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -881,6 +881,7 @@ static int decode_cd(SMMUv3State *s, SMMUTransCfg *cfg,
             tt->ttb = CACHED_ENTRY_TO_ADDR(entry, tt->ttb);
         }
 
+        tt->nscfg = CD_NSCFG(cd, i);
         tt->had = CD_HAD(cd, i);
         trace_smmuv3_decode_cd_tt(i, tt->tsz, tt->ttb, tt->granule_sz, tt->had);
     }
diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h
index bd83648b14c..1aa4e3208da 100644
--- a/include/hw/arm/smmu-common.h
+++ b/include/hw/arm/smmu-common.h
@@ -83,6 +83,7 @@ typedef struct SMMUTransTableInfo {
     uint8_t tsz;               /* input range, ie. 2^(64 -tsz)*/
     uint8_t granule_sz;        /* granule page shift */
     bool had;                  /* hierarchical attribute disable */
+    int nscfg;                 /* Non-secure attribute of Starting-level TT */
 } SMMUTransTableInfo;
 
 typedef struct SMMUTLBEntry {
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 11/28] hw/arm/smmu-common: Implement secure state handling in ptw
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (10 preceding siblings ...)
  2026-08-13 16:24 ` [RFC v5 10/28] hw/arm/smmuv3: Store CD NSCFG in TT info Tao Tang
@ 2026-08-13 16:25 ` Tao Tang
  2026-08-13 16:25 ` [RFC v5 12/28] hw/arm/smmuv3: Tag IOTLB cache keys with SEC_SID Tao Tang
                   ` (15 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:25 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Enhance the page table walker to correctly handle secure and non-secure
memory accesses. This change introduces logic to select the appropriate
address space and enforce architectural security policies during walks.

The page table walker now correctly processes Secure Stage 1
translations. Key changes include:

- The get_pte() function now uses the effective security state to fetch
page-table entries from either the Secure or Non-secure address space,
with explicit transaction attributes matching that address space.

- The stage 1 walker tracks the security state, respecting the NSCFG
and NSTable attributes. It correctly handles the hierarchical security
model: if a table descriptor in a secure walk has NSTable=1, all
subsequent lookups for that walk are forced into the Non-secure space.
This is a one-way transition, as specified by the architecture.

- The final TLB entry is tagged with the correct output address space,
ensuring proper memory isolation.

Note: We do not yet support secure stage 2 translations. This patch
only implements Secure stage 1 page-table walks. Baseline propagation
of the incoming NS attribute for stage 1 bypass is handled separately
in this series with ATTR_PERMS_OVR == 0. Full ATTR_PERMS_OVR support
is left for a separate series.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
---
 hw/arm/smmu-common.c         | 73 +++++++++++++++++++++++++++++-------
 hw/arm/smmuv3.c              | 19 ++++++----
 include/hw/arm/smmu-common.h |  7 ++--
 3 files changed, 74 insertions(+), 25 deletions(-)

diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c
index 63c36329a98..317cfafded2 100644
--- a/hw/arm/smmu-common.c
+++ b/hw/arm/smmu-common.c
@@ -408,13 +408,13 @@ void smmu_iotlb_inv_vmid_s1(SMMUState *s, int vmid)
  * @base_addr[@index]
  */
 static int get_pte(dma_addr_t baseaddr, uint32_t index, uint64_t *pte,
-                   SMMUPTWEventInfo *info)
+                   SMMUPTWEventInfo *info, AddressSpace *as, MemTxAttrs attrs)
 {
     int ret;
     dma_addr_t addr = baseaddr + index * sizeof(*pte);
 
     /* TODO: guarantee 64-bit single-copy atomicity */
-    ret = ldq_le_dma(&address_space_memory, addr, pte, MEMTXATTRS_UNSPECIFIED);
+    ret = ldq_le_dma(as, addr, pte, attrs);
 
     if (ret != MEMTX_OK) {
         info->type = SMMU_PTW_ERR_WALK_EABT;
@@ -488,7 +488,8 @@ SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova)
 static inline int translate_table_addr_ipa(SMMUState *bs,
                                            dma_addr_t *table_addr,
                                            SMMUTransCfg *cfg,
-                                           SMMUPTWEventInfo *info)
+                                           SMMUPTWEventInfo *info,
+                                           SMMUSecSID sec_sid)
 {
     dma_addr_t addr = *table_addr;
     SMMUTLBEntry *cached_entry;
@@ -501,7 +502,7 @@ static inline int translate_table_addr_ipa(SMMUState *bs,
     asid = cfg->asid;
     cfg->stage = SMMU_STAGE_2;
     cfg->asid = -1;
-    cached_entry = smmu_translate(bs, cfg, addr, IOMMU_RO, info);
+    cached_entry = smmu_translate(bs, cfg, addr, IOMMU_RO, info, sec_sid);
     cfg->asid = asid;
     cfg->stage = SMMU_NESTED;
 
@@ -524,6 +525,7 @@ static inline int translate_table_addr_ipa(SMMUState *bs,
  * @perm: access type
  * @tlbe: SMMUTLBEntry (out)
  * @info: handle to an error info
+ * @sec_sid: StreamID Security state
  *
  * Return 0 on success, < 0 on error. In case of error, @info is filled
  * and tlbe->perm is set to IOMMU_NONE.
@@ -532,12 +534,16 @@ static inline int translate_table_addr_ipa(SMMUState *bs,
  */
 static int smmu_ptw_64_s1(SMMUState *bs, SMMUTransCfg *cfg,
                           dma_addr_t iova, IOMMUAccessFlags perm,
-                          SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info)
+                          SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info,
+                          SMMUSecSID sec_sid)
 {
     dma_addr_t baseaddr, indexmask;
     SMMUStage stage = cfg->stage;
     SMMUTransTableInfo *tt = select_tt(cfg, iova);
     uint8_t level, granule_sz, inputsize, stride;
+    int nscfg, current_ns, new_nstable;
+    bool sid_is_ns = sec_sid == SMMU_SEC_SID_NS;
+    SMMUSecSID table_sec_sid;
 
     if (!tt || tt->disabled) {
         info->type = SMMU_PTW_ERR_TRANSLATION;
@@ -552,6 +558,7 @@ static int smmu_ptw_64_s1(SMMUState *bs, SMMUTransCfg *cfg,
 
     baseaddr = extract64(tt->ttb, 0, cfg->oas);
     baseaddr &= ~indexmask;
+    nscfg = tt->nscfg;
 
     while (level < VMSA_LEVELS) {
         uint64_t subpage_size = 1ULL << level_shift(level, granule_sz);
@@ -560,8 +567,19 @@ static int smmu_ptw_64_s1(SMMUState *bs, SMMUTransCfg *cfg,
         uint64_t pte, gpa;
         dma_addr_t pte_addr = baseaddr + offset * sizeof(pte);
         uint8_t ap;
+        AddressSpace *pte_as;
+        MemTxAttrs pte_attrs;
+        SMMUSecSID cur_sec_sid;
 
-        if (get_pte(baseaddr, offset, &pte, info)) {
+        /*
+         * Start in NS for Non-secure streams or CD.NSCFGx == 1.
+         * Once walk is in NS, NSTable is ignored on subsequent levels.
+         */
+        current_ns = sid_is_ns || nscfg;
+        table_sec_sid = current_ns ? SMMU_SEC_SID_NS : sec_sid;
+        pte_as = smmu_get_address_space(bs, table_sec_sid);
+        pte_attrs = smmu_get_txattrs(table_sec_sid);
+        if (get_pte(baseaddr, offset, &pte, info, pte_as, pte_attrs)) {
                 goto error;
         }
         trace_smmu_ptw_level(stage, level, iova, subpage_size,
@@ -582,10 +600,25 @@ static int smmu_ptw_64_s1(SMMUState *bs, SMMUTransCfg *cfg,
             }
             baseaddr = get_table_pte_address(pte, granule_sz);
             if (cfg->stage == SMMU_NESTED) {
-                if (translate_table_addr_ipa(bs, &baseaddr, cfg, info)) {
+                if (translate_table_addr_ipa(bs, &baseaddr, cfg,
+                                             info, table_sec_sid)) {
                     goto error;
                 }
             }
+
+            /*
+             * NSTable can switch the walk to NS only while the current walk
+             * level is Secure. Once switched to NS, NSTable is ignored according
+             * to hierarchical control of Secure/Non-secure accesses:
+             * (IHI 0070G.b)13.4.1 Stage 1 page permissions and
+             * (DDI 0487H.a)D8.4.2 Control of Secure or Non-secure memory access
+             */
+            if (!current_ns) {
+                new_nstable = PTE_NSTABLE(pte);
+                nscfg = new_nstable ? 1 : 0;
+            } else {
+                nscfg = 1;
+            }
             level++;
             continue;
         } else if (is_page_pte(pte, level)) {
@@ -628,6 +661,12 @@ static int smmu_ptw_64_s1(SMMUState *bs, SMMUTransCfg *cfg,
             goto error;
         }
 
+        if (current_ns) {
+            cur_sec_sid = SMMU_SEC_SID_NS;
+        } else {
+            cur_sec_sid = PTE_NS(pte) ? SMMU_SEC_SID_NS : SMMU_SEC_SID_S;
+        }
+        tlbe->entry.target_as = smmu_get_address_space(bs, cur_sec_sid);
         tlbe->entry.translated_addr = gpa;
         tlbe->entry.iova = iova & ~mask;
         tlbe->entry.addr_mask = mask;
@@ -697,7 +736,10 @@ static int smmu_ptw_64_s2(SMMUState *bs, SMMUTransCfg *cfg,
         uint64_t pte, gpa;
         dma_addr_t pte_addr = baseaddr + offset * sizeof(pte);
         uint8_t s2ap;
-        if (get_pte(baseaddr, offset, &pte, info)) {
+        AddressSpace *pte_as = &bs->memory_as;
+        MemTxAttrs pte_attrs = MEMTXATTRS_UNSPECIFIED;
+
+        if (get_pte(baseaddr, offset, &pte, info, pte_as, pte_attrs)) {
                 goto error;
         }
         trace_smmu_ptw_level(stage, level, ipa, subpage_size,
@@ -792,7 +834,7 @@ static void combine_tlb(SMMUTLBEntry *tlbe, SMMUTLBEntry *tlbe_s2,
 }
 
 /**
- * smmu_ptw - Walk the page tables for an IOVA, according to @cfg
+ * smmu_ptw - Walk the page tables for an IOVA, according to @cfg and @sec_sid
  *
  * @bs: smmu state which includes TLB instance
  * @cfg: translation configuration
@@ -800,18 +842,20 @@ static void combine_tlb(SMMUTLBEntry *tlbe, SMMUTLBEntry *tlbe_s2,
  * @perm: tentative access type
  * @tlbe: returned entry
  * @info: ptw event handle
+ * @sec_sid: StreamID Security state
  *
  * return 0 on success
  */
 int smmu_ptw(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t iova,
-             IOMMUAccessFlags perm, SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info)
+             IOMMUAccessFlags perm, SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info,
+             SMMUSecSID sec_sid)
 {
     int ret;
     SMMUTLBEntry tlbe_s2;
     dma_addr_t ipa;
 
     if (cfg->stage == SMMU_STAGE_1) {
-        return smmu_ptw_64_s1(bs, cfg, iova, perm, tlbe, info);
+        return smmu_ptw_64_s1(bs, cfg, iova, perm, tlbe, info, sec_sid);
     } else if (cfg->stage == SMMU_STAGE_2) {
         /*
          * If bypassing stage 1(or unimplemented), the input address is passed
@@ -830,7 +874,7 @@ int smmu_ptw(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t iova,
     }
 
     /* SMMU_NESTED. */
-    ret = smmu_ptw_64_s1(bs, cfg, iova, perm, tlbe, info);
+    ret = smmu_ptw_64_s1(bs, cfg, iova, perm, tlbe, info, sec_sid);
     if (ret) {
         return ret;
     }
@@ -846,7 +890,8 @@ int smmu_ptw(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t iova,
 }
 
 SMMUTLBEntry *smmu_translate(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t addr,
-                             IOMMUAccessFlags flag, SMMUPTWEventInfo *info)
+                             IOMMUAccessFlags flag, SMMUPTWEventInfo *info,
+                             SMMUSecSID sec_sid)
 {
     SMMUTLBEntry *cached_entry = NULL;
     SMMUTransTableInfo *tt;
@@ -888,7 +933,7 @@ SMMUTLBEntry *smmu_translate(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t addr,
     }
 
     cached_entry = g_new0(SMMUTLBEntry, 1);
-    status = smmu_ptw(bs, cfg, addr, flag, cached_entry, info);
+    status = smmu_ptw(bs, cfg, addr, flag, cached_entry, info, sec_sid);
     if (status) {
             g_free(cached_entry);
             return NULL;
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index 6b34f76f11b..cc5d3ab696c 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -394,7 +394,8 @@ static SMMUTranslationStatus smmuv3_do_translate(SMMUv3State *s, hwaddr addr,
                                                  SMMUEventInfo *event,
                                                  IOMMUAccessFlags flag,
                                                  SMMUTLBEntry **out_entry,
-                                                 SMMUTranslationClass class);
+                                                 SMMUTranslationClass class,
+                                                 SMMUSecSID sec_sid);
 /* @ssid > 0 not supported yet */
 static int smmu_get_cd(SMMUv3State *s, STE *ste, SMMUTransCfg *cfg,
                        uint32_t ssid, CD *buf, SMMUEventInfo *event,
@@ -411,7 +412,7 @@ static int smmu_get_cd(SMMUv3State *s, STE *ste, SMMUTransCfg *cfg,
 
     if (cfg->stage == SMMU_NESTED) {
         status = smmuv3_do_translate(s, addr, cfg, event,
-                                     IOMMU_RO, &entry, SMMU_CLASS_CD);
+                                     IOMMU_RO, &entry, SMMU_CLASS_CD, sec_sid);
 
         /* Same PTW faults are reported but with CLASS = CD. */
         if (status != SMMU_TRANS_SUCCESS) {
@@ -796,7 +797,7 @@ int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste, SMMUEventInfo *event,
 }
 
 static int decode_cd(SMMUv3State *s, SMMUTransCfg *cfg,
-                     CD *cd, SMMUEventInfo *event)
+                     CD *cd, SMMUEventInfo *event, SMMUSecSID sec_sid)
 {
     int ret = -EINVAL;
     int i;
@@ -869,7 +870,7 @@ static int decode_cd(SMMUv3State *s, SMMUTransCfg *cfg,
         /* Translate the TTBx, from IPA to PA if nesting is enabled. */
         if (cfg->stage == SMMU_NESTED) {
             status = smmuv3_do_translate(s, tt->ttb, cfg, event, IOMMU_RO,
-                                         &entry, SMMU_CLASS_TT);
+                                         &entry, SMMU_CLASS_TT, sec_sid);
             /*
              * Same PTW faults are reported but with CLASS = TT.
              * If TTBx is larger than the effective stage 1 output addres
@@ -939,7 +940,7 @@ static int smmuv3_decode_config(IOMMUMemoryRegion *mr, SMMUTransCfg *cfg,
         return ret;
     }
 
-    return decode_cd(s, cfg, &cd, event);
+    return decode_cd(s, cfg, &cd, event, sec_sid);
 }
 
 /**
@@ -1004,7 +1005,8 @@ static SMMUTranslationStatus smmuv3_do_translate(SMMUv3State *s, hwaddr addr,
                                                  SMMUEventInfo *event,
                                                  IOMMUAccessFlags flag,
                                                  SMMUTLBEntry **out_entry,
-                                                 SMMUTranslationClass class)
+                                                 SMMUTranslationClass class,
+                                                 SMMUSecSID sec_sid)
 {
     SMMUPTWEventInfo ptw_info = {};
     SMMUState *bs = ARM_SMMU(s);
@@ -1030,7 +1032,7 @@ static SMMUTranslationStatus smmuv3_do_translate(SMMUv3State *s, hwaddr addr,
         cfg->stage = SMMU_STAGE_2;
     }
 
-    cached_entry = smmu_translate(bs, cfg, addr, flag, &ptw_info);
+    cached_entry = smmu_translate(bs, cfg, addr, flag, &ptw_info, sec_sid);
 
     if (desc_s2_translation) {
         cfg->asid = asid;
@@ -1176,13 +1178,14 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
     }
 
     status = smmuv3_do_translate(s, addr, cfg, &event, flag,
-                                 &cached_entry, SMMU_CLASS_IN);
+                                 &cached_entry, SMMU_CLASS_IN, sec_sid);
 
 epilogue:
     qemu_mutex_unlock(&s->mutex);
     switch (status) {
     case SMMU_TRANS_SUCCESS:
         entry.perm = cached_entry->entry.perm;
+        entry.target_as = cached_entry->entry.target_as;
         entry.translated_addr = CACHED_ENTRY_TO_ADDR(cached_entry, addr);
         entry.addr_mask = cached_entry->entry.addr_mask;
         trace_smmuv3_translate_success(mr->parent_obj.name, sid, addr,
diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h
index 1aa4e3208da..0c5718ea684 100644
--- a/include/hw/arm/smmu-common.h
+++ b/include/hw/arm/smmu-common.h
@@ -224,18 +224,19 @@ static inline uint16_t smmu_get_sid(SMMUDevice *sdev)
 
 /**
  * smmu_ptw - Perform the page table walk for a given iova / access flags
- * pair, according to @cfg translation config
+ * pair, according to @cfg translation config and @sec_sid
  */
 int smmu_ptw(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t iova,
              IOMMUAccessFlags perm, SMMUTLBEntry *tlbe,
-             SMMUPTWEventInfo *info);
+             SMMUPTWEventInfo *info, SMMUSecSID sec_sid);
 
 /*
  * smmu_translate - Look for a translation in TLB, if not, do a PTW.
  * Returns NULL on PTW error or incase of TLB permission errors.
  */
 SMMUTLBEntry *smmu_translate(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t addr,
-                             IOMMUAccessFlags flag, SMMUPTWEventInfo *info);
+                             IOMMUAccessFlags flag, SMMUPTWEventInfo *info,
+                             SMMUSecSID sec_sid);
 
 /**
  * select_tt - compute which translation table shall be used according to
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 12/28] hw/arm/smmuv3: Tag IOTLB cache keys with SEC_SID
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (11 preceding siblings ...)
  2026-08-13 16:25 ` [RFC v5 11/28] hw/arm/smmu-common: Implement secure state handling in ptw Tao Tang
@ 2026-08-13 16:25 ` Tao Tang
  2026-08-13 16:25 ` [RFC v5 13/28] hw/arm/smmuv3: Pass sec_sid into cmdq consume path Tao Tang
                   ` (14 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:25 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

To prevent aliasing between translations controlled through the Secure and
Non-secure programming interfaces, the IOTLB lookup key must incorporate
SEC_SID.

This commit:
- expands SMMUIOTLBKey with SEC_SID field for cache key differentiation
- extends SMMUIOTLBPageInvInfo with SEC_SID for invalidation filtering
- updates all IOTLB invalidation helpers (smmu_iotlb_inv_iova,
  smmu_iotlb_inv_ipa, smmu_iotlb_inv_asid_vmid, smmu_iotlb_inv_vmid,
  smmu_iotlb_inv_vmid_s1) to accept and filter by SEC_SID
- plumbs SEC_SID through smmuv3_range_inval for TLB invalidation
- enhances trace events to include SEC_SID for better debugging

This ensures that IOTLB entries decoded through the Secure and Non-secure
programming interfaces are distinct, preventing cache aliasing across
SEC_SID namespaces.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
---
 hw/arm/smmu-common.c         | 110 +++++++++++++++++++++++------------
 hw/arm/smmu-internal.h       |   2 +
 hw/arm/smmuv3.c              |  47 ++++++++++-----
 hw/arm/trace-events          |  20 +++----
 include/hw/arm/smmu-common.h |  32 +++++++---
 5 files changed, 140 insertions(+), 71 deletions(-)

diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c
index 317cfafded2..3d4b6b3a287 100644
--- a/hw/arm/smmu-common.c
+++ b/hw/arm/smmu-common.c
@@ -95,7 +95,7 @@ static guint smmu_iotlb_key_hash(gconstpointer v)
 
     /* Jenkins hash */
     a = b = c = JHASH_INITVAL + sizeof(*key);
-    a += key->asid + key->vmid + key->level + key->tg;
+    a += key->asid + key->vmid + key->level + key->tg + key->sec_sid;
     b += extract64(key->iova, 0, 32);
     c += extract64(key->iova, 32, 32);
 
@@ -111,14 +111,15 @@ static gboolean smmu_iotlb_key_equal(gconstpointer v1, gconstpointer v2)
 
     return (k1->asid == k2->asid) && (k1->iova == k2->iova) &&
            (k1->level == k2->level) && (k1->tg == k2->tg) &&
-           (k1->vmid == k2->vmid);
+           (k1->vmid == k2->vmid) && (k1->sec_sid == k2->sec_sid);
 }
 
 SMMUIOTLBKey smmu_get_iotlb_key(int asid, int vmid, uint64_t iova,
-                                uint8_t tg, uint8_t level)
+                                uint8_t tg, uint8_t level,
+                                SMMUSecSID sec_sid)
 {
     SMMUIOTLBKey key = {.asid = asid, .vmid = vmid, .iova = iova,
-                        .tg = tg, .level = level};
+                        .tg = tg, .level = level, .sec_sid = sec_sid};
 
     return key;
 }
@@ -126,7 +127,8 @@ SMMUIOTLBKey smmu_get_iotlb_key(int asid, int vmid, uint64_t iova,
 static SMMUTLBEntry *smmu_iotlb_lookup_all_levels(SMMUState *bs,
                                                   SMMUTransCfg *cfg,
                                                   SMMUTransTableInfo *tt,
-                                                  hwaddr iova)
+                                                  hwaddr iova,
+                                                  SMMUSecSID sec_sid)
 {
     uint8_t tg = (tt->granule_sz - 10) / 2;
     uint8_t inputsize = 64 - tt->tsz;
@@ -140,7 +142,7 @@ static SMMUTLBEntry *smmu_iotlb_lookup_all_levels(SMMUState *bs,
         SMMUIOTLBKey key;
 
         key = smmu_get_iotlb_key(cfg->asid, cfg->s2cfg.vmid,
-                                 iova & ~mask, tg, level);
+                                 iova & ~mask, tg, level, sec_sid);
         entry = g_hash_table_lookup(bs->iotlb, &key);
         if (entry) {
             break;
@@ -156,6 +158,7 @@ static SMMUTLBEntry *smmu_iotlb_lookup_all_levels(SMMUState *bs,
  * @cfg: Configuration of the translation
  * @tt: Translation table info (granule and tsz)
  * @iova: IOVA address to lookup
+ * @sec_sid: StreamID Security state
  *
  * returns a valid entry on success, otherwise NULL.
  * In case of nested translation, tt can be updated to include
@@ -163,11 +166,12 @@ static SMMUTLBEntry *smmu_iotlb_lookup_all_levels(SMMUState *bs,
  * the IOVA granule.
  */
 SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg,
-                                SMMUTransTableInfo *tt, hwaddr iova)
+                                SMMUTransTableInfo *tt, hwaddr iova,
+                                SMMUSecSID sec_sid)
 {
     SMMUTLBEntry *entry = NULL;
 
-    entry = smmu_iotlb_lookup_all_levels(bs, cfg, tt, iova);
+    entry = smmu_iotlb_lookup_all_levels(bs, cfg, tt, iova, sec_sid);
     /*
      * For nested translation also try the s2 granule, as the TLB will insert
      * it if the size of s2 tlb entry was smaller.
@@ -175,18 +179,20 @@ SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg,
     if (!entry && (cfg->stage == SMMU_NESTED) &&
         (cfg->s2cfg.granule_sz != tt->granule_sz)) {
         tt->granule_sz = cfg->s2cfg.granule_sz;
-        entry = smmu_iotlb_lookup_all_levels(bs, cfg, tt, iova);
+        entry = smmu_iotlb_lookup_all_levels(bs, cfg, tt, iova, sec_sid);
     }
 
     if (entry) {
         cfg->iotlb_hits++;
-        trace_smmu_iotlb_lookup_hit(cfg->asid, cfg->s2cfg.vmid, iova,
+        trace_smmu_iotlb_lookup_hit(sec_sid, cfg->asid,
+                                    cfg->s2cfg.vmid, iova,
                                     cfg->iotlb_hits, cfg->iotlb_misses,
                                     100 * cfg->iotlb_hits /
                                     (cfg->iotlb_hits + cfg->iotlb_misses));
     } else {
         cfg->iotlb_misses++;
-        trace_smmu_iotlb_lookup_miss(cfg->asid, cfg->s2cfg.vmid, iova,
+        trace_smmu_iotlb_lookup_miss(sec_sid, cfg->asid,
+                                     cfg->s2cfg.vmid, iova,
                                      cfg->iotlb_hits, cfg->iotlb_misses,
                                      100 * cfg->iotlb_hits /
                                      (cfg->iotlb_hits + cfg->iotlb_misses));
@@ -194,7 +200,8 @@ SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg,
     return entry;
 }
 
-void smmu_iotlb_insert(SMMUState *bs, SMMUTransCfg *cfg, SMMUTLBEntry *new)
+void smmu_iotlb_insert(SMMUState *bs, SMMUTransCfg *cfg, SMMUTLBEntry *new,
+                       SMMUSecSID sec_sid)
 {
     SMMUIOTLBKey *key = g_new0(SMMUIOTLBKey, 1);
     uint8_t tg = (new->granule - 10) / 2;
@@ -204,9 +211,9 @@ void smmu_iotlb_insert(SMMUState *bs, SMMUTransCfg *cfg, SMMUTLBEntry *new)
     }
 
     *key = smmu_get_iotlb_key(cfg->asid, cfg->s2cfg.vmid, new->entry.iova,
-                              tg, new->level);
-    trace_smmu_iotlb_insert(cfg->asid, cfg->s2cfg.vmid, new->entry.iova,
-                            tg, new->level);
+                              tg, new->level, sec_sid);
+    trace_smmu_iotlb_insert(sec_sid, cfg->asid, cfg->s2cfg.vmid,
+                            new->entry.iova, tg, new->level);
     g_hash_table_insert(bs->iotlb, key, new);
 }
 
@@ -223,26 +230,29 @@ static gboolean smmu_hash_remove_by_asid_vmid(gpointer key, gpointer value,
     SMMUIOTLBKey *iotlb_key = (SMMUIOTLBKey *)key;
 
     return (SMMU_IOTLB_ASID(*iotlb_key) == info->asid) &&
-           (SMMU_IOTLB_VMID(*iotlb_key) == info->vmid);
+           (SMMU_IOTLB_VMID(*iotlb_key) == info->vmid) &&
+           (SMMU_IOTLB_SEC_SID(*iotlb_key) == info->sec_sid);
 }
 
 static gboolean smmu_hash_remove_by_vmid(gpointer key, gpointer value,
                                          gpointer user_data)
 {
-    int vmid = *(int *)user_data;
+    SMMUIOTLBPageInvInfo *info = (SMMUIOTLBPageInvInfo *)user_data;
     SMMUIOTLBKey *iotlb_key = (SMMUIOTLBKey *)key;
 
-    return SMMU_IOTLB_VMID(*iotlb_key) == vmid;
+    return (SMMU_IOTLB_VMID(*iotlb_key) == info->vmid) &&
+           (SMMU_IOTLB_SEC_SID(*iotlb_key) == info->sec_sid);
 }
 
 static gboolean smmu_hash_remove_by_vmid_s1(gpointer key, gpointer value,
                                             gpointer user_data)
 {
-    int vmid = *(int *)user_data;
+    SMMUIOTLBPageInvInfo *info = (SMMUIOTLBPageInvInfo *)user_data;
     SMMUIOTLBKey *iotlb_key = (SMMUIOTLBKey *)key;
 
-    return (SMMU_IOTLB_VMID(*iotlb_key) == vmid) &&
-           (SMMU_IOTLB_ASID(*iotlb_key) >= 0);
+    return (SMMU_IOTLB_VMID(*iotlb_key) == info->vmid) &&
+           (SMMU_IOTLB_ASID(*iotlb_key) >= 0) &&
+           (SMMU_IOTLB_SEC_SID(*iotlb_key) == info->sec_sid);
 }
 
 static gboolean smmu_hash_remove_by_asid_vmid_iova(gpointer key, gpointer value,
@@ -259,6 +269,9 @@ static gboolean smmu_hash_remove_by_asid_vmid_iova(gpointer key, gpointer value,
     if (info->vmid >= 0 && info->vmid != SMMU_IOTLB_VMID(iotlb_key)) {
         return false;
     }
+    if (info->sec_sid != SMMU_IOTLB_SEC_SID(iotlb_key)) {
+        return false;
+    }
     return ((info->iova & ~entry->addr_mask) == entry->iova) ||
            ((entry->iova & ~info->mask) == info->iova);
 }
@@ -278,6 +291,9 @@ static gboolean smmu_hash_remove_by_vmid_ipa(gpointer key, gpointer value,
     if (info->vmid != SMMU_IOTLB_VMID(iotlb_key)) {
         return false;
     }
+    if (info->sec_sid != SMMU_IOTLB_SEC_SID(iotlb_key)) {
+        return false;
+    }
     return ((info->iova & ~entry->addr_mask) == entry->iova) ||
            ((entry->iova & ~info->mask) == info->iova);
 }
@@ -323,13 +339,17 @@ void smmu_configs_inv_sdev(SMMUState *s, SMMUDevice *sdev)
 }
 
 void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova,
-                         uint8_t tg, uint64_t num_pages, uint8_t ttl)
+                         uint8_t tg, uint64_t num_pages, uint8_t ttl,
+                         SMMUSecSID sec_sid)
 {
     /* if tg is not set we use 4KB range invalidation */
     uint8_t granule = tg ? tg * 2 + 10 : 12;
 
+    trace_smmu_iotlb_inv_iova(sec_sid, asid, iova);
+
     if (ttl && (num_pages == 1) && (asid >= 0)) {
-        SMMUIOTLBKey key = smmu_get_iotlb_key(asid, vmid, iova, tg, ttl);
+        SMMUIOTLBKey key = smmu_get_iotlb_key(asid, vmid, iova,
+                                              tg, ttl, sec_sid);
 
         if (g_hash_table_remove(s->iotlb, &key)) {
             return;
@@ -343,7 +363,8 @@ void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova,
     SMMUIOTLBPageInvInfo info = {
         .asid = asid, .iova = iova,
         .vmid = vmid,
-        .mask = (num_pages * 1 << granule) - 1};
+        .mask = (num_pages * 1 << granule) - 1,
+        .sec_sid = sec_sid};
 
     g_hash_table_foreach_remove(s->iotlb,
                                 smmu_hash_remove_by_asid_vmid_iova,
@@ -355,13 +376,15 @@ void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova,
  * in Stage-1 invalidation ASID = -1, means don't care.
  */
 void smmu_iotlb_inv_ipa(SMMUState *s, int vmid, dma_addr_t ipa, uint8_t tg,
-                        uint64_t num_pages, uint8_t ttl)
+                        uint64_t num_pages, uint8_t ttl,
+                        SMMUSecSID sec_sid)
 {
     uint8_t granule = tg ? tg * 2 + 10 : 12;
     int asid = -1;
 
    if (ttl && (num_pages == 1)) {
-        SMMUIOTLBKey key = smmu_get_iotlb_key(asid, vmid, ipa, tg, ttl);
+        SMMUIOTLBKey key = smmu_get_iotlb_key(asid, vmid, ipa,
+                                              tg, ttl, sec_sid);
 
         if (g_hash_table_remove(s->iotlb, &key)) {
             return;
@@ -371,34 +394,47 @@ void smmu_iotlb_inv_ipa(SMMUState *s, int vmid, dma_addr_t ipa, uint8_t tg,
     SMMUIOTLBPageInvInfo info = {
         .iova = ipa,
         .vmid = vmid,
-        .mask = (num_pages << granule) - 1};
+        .mask = (num_pages << granule) - 1,
+        .sec_sid = sec_sid};
 
     g_hash_table_foreach_remove(s->iotlb,
                                 smmu_hash_remove_by_vmid_ipa,
                                 &info);
 }
 
-void smmu_iotlb_inv_asid_vmid(SMMUState *s, int asid, int vmid)
+void smmu_iotlb_inv_asid_vmid(SMMUState *s, int asid, int vmid,
+                              SMMUSecSID sec_sid)
 {
     SMMUIOTLBPageInvInfo info = {
         .asid = asid,
         .vmid = vmid,
+        .sec_sid = sec_sid,
     };
 
-    trace_smmu_iotlb_inv_asid_vmid(asid, vmid);
+    trace_smmu_iotlb_inv_asid_vmid(sec_sid, asid, vmid);
     g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_asid_vmid, &info);
 }
 
-void smmu_iotlb_inv_vmid(SMMUState *s, int vmid)
+void smmu_iotlb_inv_vmid(SMMUState *s, int vmid, SMMUSecSID sec_sid)
 {
-    trace_smmu_iotlb_inv_vmid(vmid);
-    g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_vmid, &vmid);
+    SMMUIOTLBPageInvInfo info = {
+        .vmid = vmid,
+        .sec_sid = sec_sid,
+    };
+
+    trace_smmu_iotlb_inv_vmid(sec_sid, vmid);
+    g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_vmid, &info);
 }
 
-void smmu_iotlb_inv_vmid_s1(SMMUState *s, int vmid)
+void smmu_iotlb_inv_vmid_s1(SMMUState *s, int vmid, SMMUSecSID sec_sid)
 {
-    trace_smmu_iotlb_inv_vmid_s1(vmid);
-    g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_vmid_s1, &vmid);
+    SMMUIOTLBPageInvInfo info = {
+        .vmid = vmid,
+        .sec_sid = sec_sid,
+    };
+
+    trace_smmu_iotlb_inv_vmid_s1(sec_sid, vmid);
+    g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_vmid_s1, &info);
 }
 
 /* VMSAv8-64 Translation */
@@ -919,7 +955,7 @@ SMMUTLBEntry *smmu_translate(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t addr,
         tt_combined.tsz = tt->tsz;
     }
 
-    cached_entry = smmu_iotlb_lookup(bs, cfg, &tt_combined, addr);
+    cached_entry = smmu_iotlb_lookup(bs, cfg, &tt_combined, addr, sec_sid);
     if (cached_entry) {
         if ((flag & IOMMU_WO) && !(cached_entry->entry.perm &
             cached_entry->parent_perm & IOMMU_WO)) {
@@ -938,7 +974,7 @@ SMMUTLBEntry *smmu_translate(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t addr,
             g_free(cached_entry);
             return NULL;
     }
-    smmu_iotlb_insert(bs, cfg, cached_entry);
+    smmu_iotlb_insert(bs, cfg, cached_entry, sec_sid);
     return cached_entry;
 }
 
diff --git a/hw/arm/smmu-internal.h b/hw/arm/smmu-internal.h
index 004abd58bca..ce68d4b5813 100644
--- a/hw/arm/smmu-internal.h
+++ b/hw/arm/smmu-internal.h
@@ -144,12 +144,14 @@ static inline int pgd_concat_idx(int start_level, int granule_sz,
 
 #define SMMU_IOTLB_ASID(key) ((key).asid)
 #define SMMU_IOTLB_VMID(key) ((key).vmid)
+#define SMMU_IOTLB_SEC_SID(key) ((key).sec_sid)
 
 typedef struct SMMUIOTLBPageInvInfo {
     int asid;
     int vmid;
     uint64_t iova;
     uint64_t mask;
+    SMMUSecSID sec_sid;
 } SMMUIOTLBPageInvInfo;
 
 #endif
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index cc5d3ab696c..087112ba4b6 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -634,6 +634,17 @@ static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg,
         goto bad_ste;
     }
 
+    /*
+     * Keep the SEC_SID-to-StreamWorld approximation used by the IOTLB key
+     * one-to-one until the other Secure translation regimes are modeled.
+     */
+    if (sec_sid == SMMU_SEC_SID_S && STE_CFG_S1_TRANSLATE(config) &&
+        STE_STRW(ste) != 0) {
+        qemu_log_mask(LOG_UNIMP,
+                      "SMMUv3 Secure StreamWorld is not implemented\n");
+        goto bad_ste;
+    }
+
     if (STAGE2_SUPPORTED(s)) {
         /* VMID is considered even if s2 is disabled. */
         cfg->s2cfg.vmid = STE_S2VMID(ste);
@@ -1317,7 +1328,8 @@ static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, int vmid,
     }
 }
 
-static void smmuv3_range_inval(SMMUState *s, Cmd *cmd, SMMUStage stage)
+static void smmuv3_range_inval(SMMUState *s, Cmd *cmd, SMMUStage stage,
+                               SMMUSecSID sec_sid)
 {
     dma_addr_t end, addr = CMD_ADDR(cmd);
     uint8_t type = CMD_TYPE(cmd);
@@ -1342,12 +1354,13 @@ static void smmuv3_range_inval(SMMUState *s, Cmd *cmd, SMMUStage stage)
     }
 
     if (!tg) {
-        trace_smmuv3_range_inval(vmid, asid, addr, tg, 1, ttl, leaf, stage);
+        trace_smmuv3_range_inval(sec_sid, vmid, asid, addr,
+                                 tg, 1, ttl, leaf, stage);
         smmuv3_inv_notifiers_iova(s, asid, vmid, addr, tg, 1, stage);
         if (stage == SMMU_STAGE_1) {
-            smmu_iotlb_inv_iova(s, asid, vmid, addr, tg, 1, ttl);
+            smmu_iotlb_inv_iova(s, asid, vmid, addr, tg, 1, ttl, sec_sid);
         } else {
-            smmu_iotlb_inv_ipa(s, vmid, addr, tg, 1, ttl);
+            smmu_iotlb_inv_ipa(s, vmid, addr, tg, 1, ttl, sec_sid);
         }
         return;
     }
@@ -1364,13 +1377,15 @@ static void smmuv3_range_inval(SMMUState *s, Cmd *cmd, SMMUStage stage)
         uint64_t mask = dma_aligned_pow2_mask(addr, end, 64);
 
         num_pages = (mask + 1) >> granule;
-        trace_smmuv3_range_inval(vmid, asid, addr, tg, num_pages,
-                                 ttl, leaf, stage);
-        smmuv3_inv_notifiers_iova(s, asid, vmid, addr, tg, num_pages, stage);
+        trace_smmuv3_range_inval(sec_sid, vmid, asid, addr, tg,
+                                 num_pages, ttl, leaf, stage);
+        smmuv3_inv_notifiers_iova(s, asid, vmid, addr, tg,
+                                  num_pages, stage);
         if (stage == SMMU_STAGE_1) {
-            smmu_iotlb_inv_iova(s, asid, vmid, addr, tg, num_pages, ttl);
+            smmu_iotlb_inv_iova(s, asid, vmid, addr, tg,
+                                num_pages, ttl, sec_sid);
         } else {
-            smmu_iotlb_inv_ipa(s, vmid, addr, tg, num_pages, ttl);
+            smmu_iotlb_inv_ipa(s, vmid, addr, tg, num_pages, ttl, sec_sid);
         }
         addr += mask + 1;
     }
@@ -1521,9 +1536,9 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp)
                 vmid = CMD_VMID(&cmd);
             }
 
-            trace_smmuv3_cmdq_tlbi_nh_asid(asid);
+            trace_smmuv3_cmdq_tlbi_nh_asid(sec_sid, asid);
             smmu_inv_notifiers_all(&s->smmu_state);
-            smmu_iotlb_inv_asid_vmid(bs, asid, vmid);
+            smmu_iotlb_inv_asid_vmid(bs, asid, vmid, sec_sid);
             if (!smmuv3_accel_issue_inv_cmd(s, &cmd, NULL, errp)) {
                 cmd_error = SMMU_CERROR_ILL;
                 break;
@@ -1545,8 +1560,8 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp)
              */
             if (STAGE2_SUPPORTED(s)) {
                 vmid = CMD_VMID(&cmd);
-                trace_smmuv3_cmdq_tlbi_nh(vmid);
-                smmu_iotlb_inv_vmid_s1(bs, vmid);
+                trace_smmuv3_cmdq_tlbi_nh(sec_sid, vmid);
+                smmu_iotlb_inv_vmid_s1(bs, vmid, sec_sid);
                 break;
             }
             QEMU_FALLTHROUGH;
@@ -1566,7 +1581,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp)
                 cmd_error = SMMU_CERROR_ILL;
                 break;
             }
-            smmuv3_range_inval(bs, &cmd, SMMU_STAGE_1);
+            smmuv3_range_inval(bs, &cmd, SMMU_STAGE_1, SMMU_SEC_SID_NS);
             if (!smmuv3_accel_issue_inv_cmd(s, &cmd, NULL, errp)) {
                 cmd_error = SMMU_CERROR_ILL;
                 break;
@@ -1583,7 +1598,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp)
 
             trace_smmuv3_cmdq_tlbi_s12_vmid(vmid);
             smmu_inv_notifiers_all(&s->smmu_state);
-            smmu_iotlb_inv_vmid(bs, vmid);
+            smmu_iotlb_inv_vmid(bs, vmid, SMMU_SEC_SID_NS);
             break;
         }
         case SMMU_CMD_TLBI_S2_IPA:
@@ -1595,7 +1610,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp)
              * As currently only either s1 or s2 are supported
              * we can reuse same function for s2.
              */
-            smmuv3_range_inval(bs, &cmd, SMMU_STAGE_2);
+            smmuv3_range_inval(bs, &cmd, SMMU_STAGE_2, SMMU_SEC_SID_NS);
             break;
         case SMMU_CMD_ATC_INV:
         {
diff --git a/hw/arm/trace-events b/hw/arm/trace-events
index a166b79c8ef..6a8716e8041 100644
--- a/hw/arm/trace-events
+++ b/hw/arm/trace-events
@@ -19,16 +19,16 @@ smmu_ptw_page_pte(int stage, int level,  uint64_t iova, uint64_t baseaddr, uint6
 smmu_ptw_block_pte(int stage, int level, uint64_t baseaddr, uint64_t pteaddr, uint64_t pte, uint64_t iova, uint64_t gpa, int bsize_mb) "stage=%d level=%d base@=0x%"PRIx64" pte@=0x%"PRIx64" pte=0x%"PRIx64" iova=0x%"PRIx64" block address = 0x%"PRIx64" block size = %d MiB"
 smmu_get_pte(uint64_t baseaddr, int index, uint64_t pteaddr, uint64_t pte) "baseaddr=0x%"PRIx64" index=0x%x, pteaddr=0x%"PRIx64", pte=0x%"PRIx64
 smmu_iotlb_inv_all(void) "IOTLB invalidate all"
-smmu_iotlb_inv_asid_vmid(int asid, int vmid) "IOTLB invalidate asid=%d vmid=%d"
-smmu_iotlb_inv_vmid(int vmid) "IOTLB invalidate vmid=%d"
-smmu_iotlb_inv_vmid_s1(int vmid) "IOTLB invalidate vmid=%d"
-smmu_iotlb_inv_iova(int asid, uint64_t addr) "IOTLB invalidate asid=%d addr=0x%"PRIx64
+smmu_iotlb_inv_asid_vmid(int sec_sid, int asid, int vmid) "IOTLB invalidate sec_sid=%d asid=%d vmid=%d"
+smmu_iotlb_inv_vmid(int sec_sid, int vmid) "IOTLB invalidate sec_sid=%d vmid=%d"
+smmu_iotlb_inv_vmid_s1(int sec_sid, int vmid) "IOTLB invalidate S1 sec_sid=%d vmid=%d"
+smmu_iotlb_inv_iova(int sec_sid, int asid, uint64_t addr) "IOTLB invalidate sec_sid=%d asid=%d addr=0x%"PRIx64
 smmu_configs_inv_sid_range(uint32_t start, uint32_t end) "Config cache INV SID range from 0x%x to 0x%x"
 smmu_config_cache_inv(uint32_t sid) "Config cache INV for sid=0x%x"
 smmu_inv_notifiers_mr(const char *name) "iommu mr=%s"
-smmu_iotlb_lookup_hit(int asid, int vmid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache HIT asid=%d vmid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d"
-smmu_iotlb_lookup_miss(int asid, int vmid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache MISS asid=%d vmid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d"
-smmu_iotlb_insert(int asid, int vmid, uint64_t addr, uint8_t tg, uint8_t level) "IOTLB ++ asid=%d vmid=%d addr=0x%"PRIx64" tg=%d level=%d"
+smmu_iotlb_lookup_hit(int sec_sid, int asid, int vmid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache HIT sec_sid=%d asid=%d vmid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d"
+smmu_iotlb_lookup_miss(int sec_sid, int asid, int vmid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache MISS sec_sid=%d asid=%d vmid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d"
+smmu_iotlb_insert(int sec_sid, int asid, int vmid, uint64_t addr, uint8_t tg, uint8_t level) "IOTLB ++ sec_sid=%d asid=%d vmid=%d addr=0x%"PRIx64" tg=%d level=%d"
 
 # smmuv3.c
 smmuv3_read_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x(%d)"
@@ -57,10 +57,10 @@ smmuv3_cmdq_cfgi_ste_range(int start, int end) "start=0x%x - end=0x%x"
 smmuv3_cmdq_cfgi_cd(uint32_t sid) "sid=0x%x"
 smmuv3_config_cache_hit(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache HIT for sid=0x%x (hits=%d, misses=%d, hit rate=%d)"
 smmuv3_config_cache_miss(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache MISS for sid=0x%x (hits=%d, misses=%d, hit rate=%d)"
-smmuv3_range_inval(int vmid, int asid, uint64_t addr, uint8_t tg, uint64_t num_pages, uint8_t ttl, bool leaf, int stage) "vmid=%d asid=%d addr=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" ttl=%d leaf=%d stage=%d"
-smmuv3_cmdq_tlbi_nh(int vmid) "vmid=%d"
+smmuv3_range_inval(int sec_sid, int vmid, int asid, uint64_t addr, uint8_t tg, uint64_t num_pages, uint8_t ttl, bool leaf, int stage) "sec_sid=%d vmid=%d asid=%d addr=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" ttl=%d leaf=%d stage=%d"
+smmuv3_cmdq_tlbi_nh(int sec_sid, int vmid) "sec_sid=%d vmid=%d"
 smmuv3_cmdq_tlbi_nsnh(void) ""
-smmuv3_cmdq_tlbi_nh_asid(int asid) "asid=%d"
+smmuv3_cmdq_tlbi_nh_asid(int sec_sid, int asid) "sec_sid=%d asid=%d"
 smmuv3_cmdq_tlbi_s12_vmid(int vmid) "vmid=%d"
 smmuv3_notify_flag_add(const char *iommu) "ADD SMMUNotifier node for iommu mr=%s"
 smmuv3_notify_flag_del(const char *iommu) "DEL SMMUNotifier node for iommu mr=%s"
diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h
index 0c5718ea684..8e971c28093 100644
--- a/include/hw/arm/smmu-common.h
+++ b/include/hw/arm/smmu-common.h
@@ -152,6 +152,17 @@ typedef struct SMMUIOTLBKey {
     int vmid;
     uint8_t tg;
     uint8_t level;
+    /*
+     * We currently model one StreamWorld per SEC_SID, giving the approximate
+     * mapping:
+     *
+     *   SMMU_SEC_SID_NS -> NS-EL1
+     *   SMMU_SEC_SID_S  -> Secure
+     *
+     * SEC_SID is not architecturally equivalent to StreamWorld. Extend this
+     * key when additional translation regimes are implemented.
+     */
+    SMMUSecSID sec_sid;
 } SMMUIOTLBKey;
 
 typedef struct SMMUConfigKey {
@@ -250,19 +261,24 @@ SMMUDevice *smmu_find_sdev(SMMUState *s, uint32_t sid);
 #define SMMU_IOTLB_MAX_SIZE 256
 
 SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg,
-                                SMMUTransTableInfo *tt, hwaddr iova);
-void smmu_iotlb_insert(SMMUState *bs, SMMUTransCfg *cfg, SMMUTLBEntry *entry);
+                                SMMUTransTableInfo *tt, hwaddr iova,
+                                SMMUSecSID sec_sid);
+void smmu_iotlb_insert(SMMUState *bs, SMMUTransCfg *cfg, SMMUTLBEntry *entry,
+                       SMMUSecSID sec_sid);
 SMMUIOTLBKey smmu_get_iotlb_key(int asid, int vmid, uint64_t iova,
-                                uint8_t tg, uint8_t level);
+                                uint8_t tg, uint8_t level, SMMUSecSID sec_sid);
 SMMUConfigKey smmu_get_config_key(SMMUDevice *sdev, SMMUSecSID sec_sid);
 void smmu_iotlb_inv_all(SMMUState *s);
-void smmu_iotlb_inv_asid_vmid(SMMUState *s, int asid, int vmid);
-void smmu_iotlb_inv_vmid(SMMUState *s, int vmid);
-void smmu_iotlb_inv_vmid_s1(SMMUState *s, int vmid);
+void smmu_iotlb_inv_asid_vmid(SMMUState *s, int asid, int vmid,
+                              SMMUSecSID sec_sid);
+void smmu_iotlb_inv_vmid(SMMUState *s, int vmid, SMMUSecSID sec_sid);
+void smmu_iotlb_inv_vmid_s1(SMMUState *s, int vmid, SMMUSecSID sec_sid);
 void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova,
-                         uint8_t tg, uint64_t num_pages, uint8_t ttl);
+                         uint8_t tg, uint64_t num_pages, uint8_t ttl,
+                         SMMUSecSID sec_sid);
 void smmu_iotlb_inv_ipa(SMMUState *s, int vmid, dma_addr_t ipa, uint8_t tg,
-                        uint64_t num_pages, uint8_t ttl);
+                        uint64_t num_pages, uint8_t ttl,
+                        SMMUSecSID sec_sid);
 void smmu_configs_inv_sid_range(SMMUState *s, SMMUSIDRange sid_range);
 void smmu_configs_inv_sdev(SMMUState *s, SMMUDevice *sdev);
 /* Unmap the range of all the notifiers registered to any IOMMU mr */
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 13/28] hw/arm/smmuv3: Pass sec_sid into cmdq consume path
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (12 preceding siblings ...)
  2026-08-13 16:25 ` [RFC v5 12/28] hw/arm/smmuv3: Tag IOTLB cache keys with SEC_SID Tao Tang
@ 2026-08-13 16:25 ` Tao Tang
  2026-08-13 16:25 ` [RFC v5 14/28] hw/arm/smmuv3: Make evtq producer use SEC_SID Tao Tang
                   ` (13 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:25 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Make smmuv3_cmdq_consume() security-state aware by passing sec_sid
from smmu_writel() call sites (CR0/GERRORN/CMDQ_PROD paths), instead
of hardcoding non-secure state. The related AddressSpace and MemTxAttrs
are also obtained based on sec_sid.

Also move CMD_SSEC legality checking to a single early check before
command dispatch: secure commands are rejected on non-secure queues
(including future Realm queue) with CERROR_ILL. This removes duplicated
per-command checks in CFGI handlers.

Finally, extend cmdq trace output with sec_sid so command processing
can be correlated with the correct security bank.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
---
 hw/arm/smmuv3.c     | 47 +++++++++++++++++++++------------------------
 hw/arm/trace-events |  2 +-
 2 files changed, 23 insertions(+), 26 deletions(-)

diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index 087112ba4b6..fa13191fc3b 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -112,14 +112,14 @@ static void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t new_gerrorn)
     trace_smmuv3_write_gerrorn(toggled & pending, bank->gerrorn);
 }
 
-static inline MemTxResult queue_read(SMMUQueue *q, Cmd *cmd)
+static inline MemTxResult queue_read(SMMUQueue *q, Cmd *cmd,
+                                     AddressSpace *as, MemTxAttrs attrs)
 {
     dma_addr_t addr = Q_CONS_ENTRY(q);
     MemTxResult ret;
     int i;
 
-    ret = dma_memory_read(&address_space_memory, addr, cmd, sizeof(Cmd),
-                          MEMTXATTRS_UNSPECIFIED);
+    ret = dma_memory_read(as, addr, cmd, sizeof(Cmd), attrs);
     if (ret != MEMTX_OK) {
         return ret;
     }
@@ -1391,14 +1391,15 @@ static void smmuv3_range_inval(SMMUState *s, Cmd *cmd, SMMUStage stage,
     }
 }
 
-static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp)
+static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp, SMMUSecSID sec_sid)
 {
     SMMUState *bs = ARM_SMMU(s);
     SMMUCmdError cmd_error = SMMU_CERROR_NONE;
-    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
     SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
     SMMUQueue *q = &bank->cmdq;
     SMMUCommandType type = 0;
+    MemTxAttrs attrs = smmu_get_txattrs(sec_sid);
+    AddressSpace *as = smmu_get_address_space(bs, sec_sid);
 
     if (!smmuv3_cmdq_enabled(s, sec_sid)) {
         return 0;
@@ -1413,19 +1414,30 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp)
     while (!smmuv3_q_empty(q)) {
         uint32_t pending = bank->gerror ^ bank->gerrorn;
         Cmd cmd;
+        SMMUSecSID ssec = SMMU_SEC_SID_NS;
 
-        trace_smmuv3_cmdq_consume(Q_PROD(q), Q_CONS(q),
+        trace_smmuv3_cmdq_consume(sec_sid, Q_PROD(q), Q_CONS(q),
                                   Q_PROD_WRAP(q), Q_CONS_WRAP(q));
 
         if (FIELD_EX32(pending, GERROR, CMDQ_ERR)) {
             break;
         }
 
-        if (queue_read(q, &cmd) != MEMTX_OK) {
+        if (queue_read(q, &cmd, as, attrs) != MEMTX_OK) {
             cmd_error = SMMU_CERROR_ABT;
             break;
         }
 
+        ssec = CMD_SSEC(&cmd) ? SMMU_SEC_SID_S : SMMU_SEC_SID_NS;
+        /*
+         * A command with SSec == 1 is illegal on Non-secure and Realm Command
+         * queues and raises CERROR_ILL, as specified by (IHI 0070G.b) 4.1.6.
+         */
+        if (ssec && sec_sid != SMMU_SEC_SID_S) {
+            cmd_error = SMMU_CERROR_ILL;
+            break;
+        }
+
         type = CMD_TYPE(&cmd);
 
         trace_smmuv3_cmdq_opcode(smmu_cmd_string(type));
@@ -1445,11 +1457,6 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp)
             uint32_t sid = CMD_SID(&cmd);
             SMMUDevice *sdev = smmu_find_sdev(bs, sid);
 
-            if (CMD_SSEC(&cmd)) {
-                cmd_error = SMMU_CERROR_ILL;
-                break;
-            }
-
             if (!sdev) {
                 break;
             }
@@ -1469,11 +1476,6 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp)
             uint8_t range = CMD_STE_RANGE(&cmd);
             SMMUSIDRange sid_range;
 
-            if (CMD_SSEC(&cmd)) {
-                cmd_error = SMMU_CERROR_ILL;
-                break;
-            }
-
             mask = (1ULL << (range + 1)) - 1;
             sid_range.start = sid & ~mask;
             sid_range.end = sid_range.start + mask;
@@ -1492,11 +1494,6 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp)
             uint32_t sid = CMD_SID(&cmd);
             SMMUDevice *sdev = smmu_find_sdev(bs, sid);
 
-            if (CMD_SSEC(&cmd)) {
-                cmd_error = SMMU_CERROR_ILL;
-                break;
-            }
-
             if (!sdev) {
                 break;
             }
@@ -1720,7 +1717,7 @@ static MemTxResult smmu_writel(SMMUv3State *s, hwaddr offset,
         bank->cr[0] = data;
         bank->cr0ack = data & ~SMMU_CR0_RESERVED;
         /* in case the command queue has been enabled */
-        smmuv3_cmdq_consume(s, &local_err);
+        smmuv3_cmdq_consume(s, &local_err, reg_sec_sid);
         if (local_err) {
             error_report_err(local_err);
             local_err = NULL;
@@ -1743,7 +1740,7 @@ static MemTxResult smmu_writel(SMMUv3State *s, hwaddr offset,
          * By acknowledging the CMDQ_ERR, SW may notify cmds can
          * be processed again
          */
-        smmuv3_cmdq_consume(s, &local_err);
+        smmuv3_cmdq_consume(s, &local_err, reg_sec_sid);
         break;
     case A_GERROR_IRQ_CFG0: /* 64b */
         bank->gerror_irq_cfg0 = deposit64(bank->gerror_irq_cfg0, 0, 32, data);
@@ -1801,7 +1798,7 @@ static MemTxResult smmu_writel(SMMUv3State *s, hwaddr offset,
         break;
     case A_CMDQ_PROD:
         bank->cmdq.prod = data;
-        smmuv3_cmdq_consume(s, &local_err);
+        smmuv3_cmdq_consume(s, &local_err, reg_sec_sid);
         break;
     case A_CMDQ_CONS:
         bank->cmdq.cons = data;
diff --git a/hw/arm/trace-events b/hw/arm/trace-events
index 6a8716e8041..ccc0ab50164 100644
--- a/hw/arm/trace-events
+++ b/hw/arm/trace-events
@@ -36,7 +36,7 @@ smmuv3_trigger_irq(int irq) "irq=%d"
 smmuv3_write_gerror(uint32_t toggled, uint32_t gerror) "toggled=0x%x, new GERROR=0x%x"
 smmuv3_write_gerrorn(uint32_t acked, uint32_t gerrorn) "acked=0x%x, new GERRORN=0x%x"
 smmuv3_unhandled_cmd(uint32_t type) "Unhandled command type=%d"
-smmuv3_cmdq_consume(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "prod=%d cons=%d prod.wrap=%d cons.wrap=%d"
+smmuv3_cmdq_consume(int sec_sid, uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "sec_sid=%d prod=%d cons=%d prod.wrap=%d cons.wrap=%d"
 smmuv3_cmdq_opcode(const char *opcode) "<--- %s"
 smmuv3_cmdq_consume_out(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "prod:%d, cons:%d, prod_wrap:%d, cons_wrap:%d "
 smmuv3_cmdq_consume_error(const char *cmd_name, uint8_t cmd_error) "Error on %s command execution: %d"
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 14/28] hw/arm/smmuv3: Make evtq producer use SEC_SID
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (13 preceding siblings ...)
  2026-08-13 16:25 ` [RFC v5 13/28] hw/arm/smmuv3: Pass sec_sid into cmdq consume path Tao Tang
@ 2026-08-13 16:25 ` Tao Tang
  2026-08-13 16:25 ` [RFC v5 15/28] hw/arm/smmu: Make CMDQ invalidation security-state aware Tao Tang
                   ` (12 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:25 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

The event queue producer path wrote entries through address_space_memory
with MEMTXATTRS_UNSPECIFIED, so produced entries did not use the
sec_sid-selected DMA context. Pass AddressSpace and MemTxAttrs to
queue_write() based on sec_sid.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
---
 hw/arm/smmuv3.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index fa13191fc3b..7e08b88689f 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -129,7 +129,8 @@ static inline MemTxResult queue_read(SMMUQueue *q, Cmd *cmd,
     return ret;
 }
 
-static MemTxResult queue_write(SMMUQueue *q, Evt *evt_in)
+static MemTxResult queue_write(SMMUQueue *q, Evt *evt_in,
+                               AddressSpace *as, MemTxAttrs attrs)
 {
     dma_addr_t addr = Q_PROD_ENTRY(q);
     MemTxResult ret;
@@ -139,8 +140,7 @@ static MemTxResult queue_write(SMMUQueue *q, Evt *evt_in)
     for (i = 0; i < ARRAY_SIZE(evt.word); i++) {
         cpu_to_le32s(&evt.word[i]);
     }
-    ret = dma_memory_write(&address_space_memory, addr, &evt, sizeof(Evt),
-                           MEMTXATTRS_UNSPECIFIED);
+    ret = dma_memory_write(as, addr, &evt, sizeof(Evt), attrs);
     if (ret != MEMTX_OK) {
         return ret;
     }
@@ -155,6 +155,9 @@ static MemTxResult smmuv3_write_eventq(SMMUv3State *s, SMMUSecSID sec_sid,
     SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
     SMMUQueue *q = &bank->eventq;
     MemTxResult r;
+    SMMUState *bs = ARM_SMMU(s);
+    MemTxAttrs txattrs = smmu_get_txattrs(sec_sid);
+    AddressSpace *as = smmu_get_address_space(bs, sec_sid);
 
     if (!smmuv3_eventq_enabled(s, sec_sid)) {
         return MEMTX_ERROR;
@@ -164,7 +167,7 @@ static MemTxResult smmuv3_write_eventq(SMMUv3State *s, SMMUSecSID sec_sid,
         return MEMTX_ERROR;
     }
 
-    r = queue_write(q, evt);
+    r = queue_write(q, evt, as, txattrs);
     if (r != MEMTX_OK) {
         return r;
     }
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 15/28] hw/arm/smmu: Make CMDQ invalidation security-state aware
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (14 preceding siblings ...)
  2026-08-13 16:25 ` [RFC v5 14/28] hw/arm/smmuv3: Make evtq producer use SEC_SID Tao Tang
@ 2026-08-13 16:25 ` Tao Tang
  2026-08-13 16:25 ` [RFC v5 16/28] hw/arm/smmuv3: Add access checks for GERROR_IRQ_CFG registers Tao Tang
                   ` (11 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:25 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Refactor CMDQ invalidation paths to carry security state and apply cache
invalidation per sec_sid instead of globally. Add separate helpers for
invalidating all entries and for invalidating entries belonging to one
valid sec_sid.

In smmuv3, propagate the command queue sec_sid and command SSec through
CFGI and TLBI handling, and gate VMID use on the stage-2 capability of
the selected command queue, including SMMU_S_IDR1.SEL2 for a Secure
Command queue.

Keep acceleration and IOMMU notifier propagation Non-secure-only.
Commands targeting a programming interface other than Non-secure do not
reach the accelerated backend or Non-secure notifiers, while Non-secure
stage-1 CMD_TLBI_NH_ALL remains forwarded to the host.

Include the command queue SEC_SID and target SEC_SID in the relevant
invalidation tracepoints.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
---
 hw/arm/smmu-common.c         | 100 ++++++++++++++++++++++++++++-
 hw/arm/smmuv3-accel-stubs.c  |   6 +-
 hw/arm/smmuv3-accel.c        |  30 +++++++--
 hw/arm/smmuv3-accel.h        |   6 +-
 hw/arm/smmuv3.c              | 121 ++++++++++++++++++++++++++---------
 hw/arm/trace-events          |  12 ++--
 include/hw/arm/smmu-common.h |   6 ++
 7 files changed, 231 insertions(+), 50 deletions(-)

diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c
index 3d4b6b3a287..e8a1ed65c19 100644
--- a/hw/arm/smmu-common.c
+++ b/hw/arm/smmu-common.c
@@ -217,12 +217,30 @@ void smmu_iotlb_insert(SMMUState *bs, SMMUTransCfg *cfg, SMMUTLBEntry *new,
     g_hash_table_insert(bs->iotlb, key, new);
 }
 
+static gboolean smmu_hash_remove_by_sec_sid(gpointer key, gpointer value,
+                                            gpointer user_data)
+{
+    SMMUIOTLBKey *iotlb_key = (SMMUIOTLBKey *)key;
+    SMMUSecSID *sec_sid = (SMMUSecSID *)user_data;
+
+    return SMMU_IOTLB_SEC_SID(*iotlb_key) == *sec_sid;
+}
+
 void smmu_iotlb_inv_all(SMMUState *s)
 {
     trace_smmu_iotlb_inv_all();
     g_hash_table_remove_all(s->iotlb);
 }
 
+void smmu_iotlb_inv_by_sec_sid(SMMUState *s, SMMUSecSID sec_sid)
+{
+    g_assert(sec_sid < SMMU_SEC_SID_NUM);
+
+    trace_smmu_iotlb_inv_by_sec_sid(sec_sid);
+    g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_sec_sid,
+                                &sec_sid);
+}
+
 static gboolean smmu_hash_remove_by_asid_vmid(gpointer key, gpointer value,
                                               gpointer user_data)
 {
@@ -298,6 +316,16 @@ static gboolean smmu_hash_remove_by_vmid_ipa(gpointer key, gpointer value,
            ((entry->iova & ~info->mask) == info->iova);
 }
 
+typedef struct SMMUConfigInvRangeInfo {
+    SMMUSIDRange sid_range;
+    SMMUSecSID sec_sid;
+} SMMUConfigInvRangeInfo;
+
+typedef struct SMMUConfigInvSdevInfo {
+    SMMUDevice *sdev;
+    SMMUSecSID sec_sid;
+} SMMUConfigInvSdevInfo;
+
 static gboolean
 smmu_hash_remove_by_sid_range(gpointer key, gpointer value, gpointer user_data)
 {
@@ -309,7 +337,26 @@ smmu_hash_remove_by_sid_range(gpointer key, gpointer value, gpointer user_data)
     if (sid < sid_range->start || sid > sid_range->end) {
         return false;
     }
-    trace_smmu_config_cache_inv(sid);
+    trace_smmu_config_cache_inv(config_key->sec_sid, sid);
+    return true;
+}
+
+static gboolean
+smmu_hash_remove_by_sid_range_sec(gpointer key, gpointer value,
+                                  gpointer user_data)
+{
+    SMMUConfigKey *config_key = (SMMUConfigKey *)key;
+    SMMUConfigInvRangeInfo *info = (SMMUConfigInvRangeInfo *)user_data;
+    SMMUDevice *sdev = config_key->sdev;
+    uint32_t sid = smmu_get_sid(sdev);
+
+    if (config_key->sec_sid != info->sec_sid) {
+        return false;
+    }
+    if (sid < info->sid_range.start || sid > info->sid_range.end) {
+        return false;
+    }
+    trace_smmu_config_cache_inv(config_key->sec_sid, sid);
     return true;
 }
 
@@ -320,6 +367,23 @@ void smmu_configs_inv_sid_range(SMMUState *s, SMMUSIDRange sid_range)
                                 &sid_range);
 }
 
+void smmu_configs_inv_sid_range_by_sec_sid(SMMUState *s,
+                                           SMMUSIDRange sid_range,
+                                           SMMUSecSID sec_sid)
+{
+    SMMUConfigInvRangeInfo info = {
+        .sid_range = sid_range,
+        .sec_sid = sec_sid,
+    };
+
+    g_assert(sec_sid < SMMU_SEC_SID_NUM);
+
+    trace_smmu_configs_inv_sid_range_by_sec_sid(sec_sid, sid_range.start,
+                                                sid_range.end);
+    g_hash_table_foreach_remove(s->configs, smmu_hash_remove_by_sid_range_sec,
+                                &info);
+}
+
 static gboolean smmu_hash_remove_by_sdev(gpointer key, gpointer value,
                                          gpointer user_data)
 {
@@ -329,7 +393,25 @@ static gboolean smmu_hash_remove_by_sdev(gpointer key, gpointer value,
     if (config_key->sdev != target) {
         return false;
     }
-    trace_smmu_config_cache_inv(smmu_get_sid(target));
+    trace_smmu_config_cache_inv(config_key->sec_sid,
+                                smmu_get_sid(target));
+    return true;
+}
+
+static gboolean smmu_hash_remove_by_sdev_sec(gpointer key, gpointer value,
+                                             gpointer user_data)
+{
+    SMMUConfigKey *config_key = (SMMUConfigKey *)key;
+    SMMUConfigInvSdevInfo *info = (SMMUConfigInvSdevInfo *)user_data;
+
+    if (config_key->sdev != info->sdev) {
+        return false;
+    }
+    if (config_key->sec_sid != info->sec_sid) {
+        return false;
+    }
+    trace_smmu_config_cache_inv(config_key->sec_sid,
+                                smmu_get_sid(info->sdev));
     return true;
 }
 
@@ -338,6 +420,20 @@ void smmu_configs_inv_sdev(SMMUState *s, SMMUDevice *sdev)
     g_hash_table_foreach_remove(s->configs, smmu_hash_remove_by_sdev, sdev);
 }
 
+void smmu_configs_inv_sdev_by_sec_sid(SMMUState *s, SMMUDevice *sdev,
+                                      SMMUSecSID sec_sid)
+{
+    SMMUConfigInvSdevInfo info = {
+        .sdev = sdev,
+        .sec_sid = sec_sid,
+    };
+
+    g_assert(sec_sid < SMMU_SEC_SID_NUM);
+
+    g_hash_table_foreach_remove(s->configs, smmu_hash_remove_by_sdev_sec,
+                                &info);
+}
+
 void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova,
                          uint8_t tg, uint64_t num_pages, uint8_t ttl,
                          SMMUSecSID sec_sid)
diff --git a/hw/arm/smmuv3-accel-stubs.c b/hw/arm/smmuv3-accel-stubs.c
index b8dd7e7b897..ecc6890a9cc 100644
--- a/hw/arm/smmuv3-accel-stubs.c
+++ b/hw/arm/smmuv3-accel-stubs.c
@@ -16,13 +16,13 @@ bool smmuv3_accel_init(SMMUv3State *s, Error **errp)
 }
 
 bool smmuv3_accel_install_ste(SMMUv3State *s, SMMUDevice *sdev, int sid,
-                              Error **errp)
+                              SMMUSecSID sec_sid, Error **errp)
 {
     return true;
 }
 
 bool smmuv3_accel_install_ste_range(SMMUv3State *s, SMMUSIDRange *range,
-                                    Error **errp)
+                                    SMMUSecSID sec_sid, Error **errp)
 {
     return true;
 }
@@ -33,7 +33,7 @@ bool smmuv3_accel_attach_gbpa_hwpt(SMMUv3State *s, Error **errp)
 }
 
 bool smmuv3_accel_issue_inv_cmd(SMMUv3State *s, void *cmd, SMMUDevice *sdev,
-                                Error **errp)
+                                SMMUSecSID sec_sid, Error **errp)
 {
     return true;
 }
diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c
index 9d207acc8e3..a738c4fad67 100644
--- a/hw/arm/smmuv3-accel.c
+++ b/hw/arm/smmuv3-accel.c
@@ -283,7 +283,7 @@ smmuv3_accel_dev_alloc_translate(SMMUv3AccelDevice *accel_dev, STE *ste,
 }
 
 bool smmuv3_accel_install_ste(SMMUv3State *s, SMMUDevice *sdev, int sid,
-                              Error **errp)
+                              SMMUSecSID sec_sid, Error **errp)
 {
     SMMUEventInfo event = {.type = SMMU_EVT_NONE, .sid = sid,
                            .inval_ste_allowed = true};
@@ -294,7 +294,13 @@ bool smmuv3_accel_install_ste(SMMUv3State *s, SMMUDevice *sdev, int sid,
     SMMUS1Hwpt *s1_hwpt = NULL;
     const char *type;
     STE ste;
-    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
+
+    g_assert(sec_sid < SMMU_SEC_SID_NUM);
+
+    /* Acceleration supports only the Non-secure programming interface. */
+    if (sec_sid != SMMU_SEC_SID_NS) {
+        return true;
+    }
 
     if (!accel || !accel->viommu) {
         return true;
@@ -377,13 +383,20 @@ bool smmuv3_accel_install_ste(SMMUv3State *s, SMMUDevice *sdev, int sid,
 }
 
 bool smmuv3_accel_install_ste_range(SMMUv3State *s, SMMUSIDRange *range,
-                                    Error **errp)
+                                    SMMUSecSID sec_sid, Error **errp)
 {
     SMMUv3AccelState *accel = s->s_accel;
     SMMUv3AccelDevice *accel_dev;
     Error *local_err = NULL;
     bool all_ok = true;
 
+    g_assert(sec_sid < SMMU_SEC_SID_NUM);
+
+    /* Acceleration supports only the Non-secure programming interface. */
+    if (sec_sid != SMMU_SEC_SID_NS) {
+        return true;
+    }
+
     if (!accel || !accel->viommu) {
         return true;
     }
@@ -393,7 +406,7 @@ bool smmuv3_accel_install_ste_range(SMMUv3State *s, SMMUSIDRange *range,
 
         if (sid >= range->start && sid <= range->end) {
             if (!smmuv3_accel_install_ste(s, &accel_dev->sdev,
-                                          sid, &local_err)) {
+                                          sid, sec_sid, &local_err)) {
                 error_append_hint(&local_err, "Device 0x%x: Failed to install "
                                   "STE\n", sid);
                 error_report_err(local_err);
@@ -416,12 +429,19 @@ bool smmuv3_accel_install_ste_range(SMMUv3State *s, SMMUSIDRange *range,
  * non SID invalidations such as SMMU_CMD_TLBI_NH_ASID and SMMU_CMD_TLBI_NH_VA.
  */
 bool smmuv3_accel_issue_inv_cmd(SMMUv3State *bs, void *cmd, SMMUDevice *sdev,
-                                Error **errp)
+                                SMMUSecSID sec_sid, Error **errp)
 {
     SMMUv3State *s = ARM_SMMUV3(bs);
     SMMUv3AccelState *accel = s->s_accel;
     uint32_t entry_num = 1;
 
+    g_assert(sec_sid < SMMU_SEC_SID_NUM);
+
+    /* Acceleration supports only the Non-secure programming interface. */
+    if (sec_sid != SMMU_SEC_SID_NS) {
+        return true;
+    }
+
     /*
      * No accel or viommu means no VFIO/IOMMUFD devices, nothing to
      * invalidate.
diff --git a/hw/arm/smmuv3-accel.h b/hw/arm/smmuv3-accel.h
index ea11d513cc9..761943f137a 100644
--- a/hw/arm/smmuv3-accel.h
+++ b/hw/arm/smmuv3-accel.h
@@ -89,12 +89,12 @@ typedef struct SMMUv3AccelDevice {
 
 bool smmuv3_accel_init(SMMUv3State *s, Error **errp);
 bool smmuv3_accel_install_ste(SMMUv3State *s, SMMUDevice *sdev, int sid,
-                              Error **errp);
+                              SMMUSecSID sec_sid, Error **errp);
 bool smmuv3_accel_install_ste_range(SMMUv3State *s, SMMUSIDRange *range,
-                                    Error **errp);
+                                    SMMUSecSID sec_sid, Error **errp);
 bool smmuv3_accel_attach_gbpa_hwpt(SMMUv3State *s, Error **errp);
 bool smmuv3_accel_issue_inv_cmd(SMMUv3State *s, void *cmd, SMMUDevice *sdev,
-                                Error **errp);
+                                SMMUSecSID sec_sid, Error **errp);
 void smmuv3_accel_idr_override(SMMUv3State *s);
 bool smmuv3_accel_alloc_veventq(SMMUv3State *s, Error **errp);
 int smmuv3_accel_event_read_validate(IOMMUFDVeventq *veventq, uint32_t type,
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index 7e08b88689f..47f0d575817 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -1005,12 +1005,13 @@ static SMMUTransCfg *smmuv3_get_config(SMMUDevice *sdev, SMMUEventInfo *event,
     return cfg;
 }
 
-static void smmuv3_flush_config(SMMUDevice *sdev)
+static void smmuv3_flush_config_by_sec_sid(SMMUDevice *sdev,
+                                           SMMUSecSID sec_sid)
 {
     SMMUv3State *s = sdev->smmu;
     SMMUState *bc = &s->smmu_state;
 
-    smmu_configs_inv_sdev(bc, sdev);
+    smmu_configs_inv_sdev_by_sec_sid(bc, sdev, sec_sid);
 }
 
 /* Do translation with TLB lookup. */
@@ -1314,10 +1315,16 @@ static void smmuv3_notify_iova(IOMMUMemoryRegion *mr,
 /* invalidate an asid/vmid/iova range tuple in all mr's */
 static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, int vmid,
                                       dma_addr_t iova, uint8_t tg,
-                                      uint64_t num_pages, int stage)
+                                      uint64_t num_pages, int stage,
+                                      SMMUSecSID sec_sid)
 {
     SMMUDevice *sdev;
 
+    /* IOMMU notifiers are supported only for Non-secure devices. */
+    if (sec_sid != SMMU_SEC_SID_NS) {
+        return;
+    }
+
     QLIST_FOREACH(sdev, &s->devices_with_notifiers, next) {
         IOMMUMemoryRegion *mr = &sdev->iommu;
         IOMMUNotifier *n;
@@ -1331,8 +1338,16 @@ static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, int vmid,
     }
 }
 
+static void smmuv3_inv_notifiers_all(SMMUState *s, SMMUSecSID sec_sid)
+{
+    /* IOMMU notifiers are supported only for Non-secure devices. */
+    if (sec_sid == SMMU_SEC_SID_NS) {
+        smmu_inv_notifiers_all(s);
+    }
+}
+
 static void smmuv3_range_inval(SMMUState *s, Cmd *cmd, SMMUStage stage,
-                               SMMUSecSID sec_sid)
+                               SMMUSecSID sec_sid, bool use_vmid)
 {
     dma_addr_t end, addr = CMD_ADDR(cmd);
     uint8_t type = CMD_TYPE(cmd);
@@ -1345,10 +1360,8 @@ static void smmuv3_range_inval(SMMUState *s, Cmd *cmd, SMMUStage stage,
     uint64_t num_pages;
     uint8_t granule;
     int asid = -1;
-    SMMUv3State *smmuv3 = ARM_SMMUV3(s);
 
-    /* Only consider VMID if stage-2 is supported. */
-    if (STAGE2_SUPPORTED(smmuv3)) {
+    if (use_vmid) {
         vmid = CMD_VMID(cmd);
     }
 
@@ -1359,7 +1372,8 @@ static void smmuv3_range_inval(SMMUState *s, Cmd *cmd, SMMUStage stage,
     if (!tg) {
         trace_smmuv3_range_inval(sec_sid, vmid, asid, addr,
                                  tg, 1, ttl, leaf, stage);
-        smmuv3_inv_notifiers_iova(s, asid, vmid, addr, tg, 1, stage);
+        smmuv3_inv_notifiers_iova(s, asid, vmid, addr, tg, 1, stage,
+                                  sec_sid);
         if (stage == SMMU_STAGE_1) {
             smmu_iotlb_inv_iova(s, asid, vmid, addr, tg, 1, ttl, sec_sid);
         } else {
@@ -1383,7 +1397,7 @@ static void smmuv3_range_inval(SMMUState *s, Cmd *cmd, SMMUStage stage,
         trace_smmuv3_range_inval(sec_sid, vmid, asid, addr, tg,
                                  num_pages, ttl, leaf, stage);
         smmuv3_inv_notifiers_iova(s, asid, vmid, addr, tg,
-                                  num_pages, stage);
+                                  num_pages, stage, sec_sid);
         if (stage == SMMU_STAGE_1) {
             smmu_iotlb_inv_iova(s, asid, vmid, addr, tg,
                                 num_pages, ttl, sec_sid);
@@ -1394,6 +1408,26 @@ static void smmuv3_range_inval(SMMUState *s, Cmd *cmd, SMMUStage stage,
     }
 }
 
+static inline bool
+smmu_cmdq_stage2_supported(SMMUv3State *s, SMMUSecSID sec_sid)
+{
+    /* IDR0.S2P: Stage 2 translation supported */
+    bool s2p = STAGE2_SUPPORTED(s);
+    if (!s2p) {
+        return false;
+    }
+
+    /*
+     * For Secure Command queue, Secure stage 2 is additionally gated by SEL2
+     * (SEL2 is 0 if S2P is 0).
+     */
+    if (sec_sid == SMMU_SEC_SID_S) {
+        return FIELD_EX32(s->bank[SMMU_SEC_SID_S].idr[1], S_IDR1, SEL2);
+    }
+
+    return true;
+}
+
 static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp, SMMUSecSID sec_sid)
 {
     SMMUState *bs = ARM_SMMU(s);
@@ -1403,6 +1437,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp, SMMUSecSID sec_sid)
     SMMUCommandType type = 0;
     MemTxAttrs attrs = smmu_get_txattrs(sec_sid);
     AddressSpace *as = smmu_get_address_space(bs, sec_sid);
+    bool queue_stage2_supported = smmu_cmdq_stage2_supported(s, sec_sid);
 
     if (!smmuv3_cmdq_enabled(s, sec_sid)) {
         return 0;
@@ -1464,12 +1499,12 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp, SMMUSecSID sec_sid)
                 break;
             }
 
-            trace_smmuv3_cmdq_cfgi_ste(sid);
-            if (!smmuv3_accel_install_ste(s, sdev, sid, errp)) {
+            trace_smmuv3_cmdq_cfgi_ste(sec_sid, ssec, sid);
+            if (!smmuv3_accel_install_ste(s, sdev, sid, ssec, errp)) {
                 cmd_error = SMMU_CERROR_ILL;
                 break;
             }
-            smmuv3_flush_config(sdev);
+            smmuv3_flush_config_by_sec_sid(sdev, ssec);
 
             break;
         }
@@ -1483,12 +1518,13 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp, SMMUSecSID sec_sid)
             sid_range.start = sid & ~mask;
             sid_range.end = sid_range.start + mask;
 
-            trace_smmuv3_cmdq_cfgi_ste_range(sid_range.start, sid_range.end);
-            if (!smmuv3_accel_install_ste_range(s, &sid_range, errp)) {
+            trace_smmuv3_cmdq_cfgi_ste_range(sec_sid, ssec,
+                                             sid_range.start, sid_range.end);
+            if (!smmuv3_accel_install_ste_range(s, &sid_range, ssec, errp)) {
                 cmd_error = SMMU_CERROR_ILL;
                 break;
             }
-            smmu_configs_inv_sid_range(bs, sid_range);
+            smmu_configs_inv_sid_range_by_sec_sid(bs, sid_range, ssec);
             break;
         }
         case SMMU_CMD_CFGI_CD:
@@ -1510,9 +1546,9 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp, SMMUSecSID sec_sid)
                 break;
             }
 
-            trace_smmuv3_cmdq_cfgi_cd(sid);
-            smmuv3_flush_config(sdev);
-            if (!smmuv3_accel_issue_inv_cmd(s, &cmd, sdev, errp)) {
+            trace_smmuv3_cmdq_cfgi_cd(sec_sid, ssec, sid);
+            smmuv3_flush_config_by_sec_sid(sdev, ssec);
+            if (!smmuv3_accel_issue_inv_cmd(s, &cmd, sdev, ssec, errp)) {
                 cmd_error = SMMU_CERROR_ILL;
                 break;
             }
@@ -1532,14 +1568,14 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp, SMMUSecSID sec_sid)
              * VMID is only matched when stage 2 is supported, otherwise set it
              * to -1 as the value used for stage-1 only VMIDs.
              */
-            if (STAGE2_SUPPORTED(s)) {
+            if (queue_stage2_supported) {
                 vmid = CMD_VMID(&cmd);
             }
 
             trace_smmuv3_cmdq_tlbi_nh_asid(sec_sid, asid);
-            smmu_inv_notifiers_all(&s->smmu_state);
+            smmuv3_inv_notifiers_all(bs, sec_sid);
             smmu_iotlb_inv_asid_vmid(bs, asid, vmid, sec_sid);
-            if (!smmuv3_accel_issue_inv_cmd(s, &cmd, NULL, errp)) {
+            if (!smmuv3_accel_issue_inv_cmd(s, &cmd, NULL, sec_sid, errp)) {
                 cmd_error = SMMU_CERROR_ILL;
                 break;
             }
@@ -1558,31 +1594,51 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp, SMMUSecSID sec_sid)
              * If stage-2 is supported, invalidate for this VMID only, otherwise
              * invalidate the whole thing.
              */
-            if (STAGE2_SUPPORTED(s)) {
+            if (queue_stage2_supported) {
                 vmid = CMD_VMID(&cmd);
                 trace_smmuv3_cmdq_tlbi_nh(sec_sid, vmid);
                 smmu_iotlb_inv_vmid_s1(bs, vmid, sec_sid);
                 break;
             }
-            QEMU_FALLTHROUGH;
+            trace_smmuv3_cmdq_tlbi_nh(sec_sid, vmid);
+            smmuv3_inv_notifiers_all(bs, sec_sid);
+            smmu_iotlb_inv_by_sec_sid(bs, sec_sid);
+            if (!smmuv3_accel_issue_inv_cmd(s, &cmd, NULL, sec_sid, errp)) {
+                cmd_error = SMMU_CERROR_ILL;
+                break;
+            }
+            break;
         }
         case SMMU_CMD_TLBI_NSNH_ALL:
-            trace_smmuv3_cmdq_tlbi_nsnh();
-            smmu_inv_notifiers_all(&s->smmu_state);
-            smmu_iotlb_inv_all(bs);
-            if (!smmuv3_accel_issue_inv_cmd(s, &cmd, NULL, errp)) {
+        {
+            /*
+             * CMD_TLBI_NSNH_ALL targets Non-secure entries when issued from the
+             * Non-secure or Secure Command queue, but Realm entries when issued
+             * from the Realm Command queue.
+             * (IHI 0070G.b) 4.4.4.1 CMD_TLBI_NSNH_ALL, Page 194
+             */
+            SMMUSecSID target_sec_sid = sec_sid > SMMU_SEC_SID_S ?
+                                        sec_sid : SMMU_SEC_SID_NS;
+
+            trace_smmuv3_cmdq_tlbi_nsnh(sec_sid, target_sec_sid);
+            smmuv3_inv_notifiers_all(bs, target_sec_sid);
+            smmu_iotlb_inv_by_sec_sid(bs, target_sec_sid);
+            if (!smmuv3_accel_issue_inv_cmd(s, &cmd, NULL, target_sec_sid,
+                                            errp)) {
                 cmd_error = SMMU_CERROR_ILL;
                 break;
             }
             break;
+        }
         case SMMU_CMD_TLBI_NH_VAA:
         case SMMU_CMD_TLBI_NH_VA:
             if (!STAGE1_SUPPORTED(s)) {
                 cmd_error = SMMU_CERROR_ILL;
                 break;
             }
-            smmuv3_range_inval(bs, &cmd, SMMU_STAGE_1, SMMU_SEC_SID_NS);
-            if (!smmuv3_accel_issue_inv_cmd(s, &cmd, NULL, errp)) {
+            smmuv3_range_inval(bs, &cmd, SMMU_STAGE_1, sec_sid,
+                               queue_stage2_supported);
+            if (!smmuv3_accel_issue_inv_cmd(s, &cmd, NULL, sec_sid, errp)) {
                 cmd_error = SMMU_CERROR_ILL;
                 break;
             }
@@ -1597,7 +1653,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp, SMMUSecSID sec_sid)
             }
 
             trace_smmuv3_cmdq_tlbi_s12_vmid(vmid);
-            smmu_inv_notifiers_all(&s->smmu_state);
+            smmuv3_inv_notifiers_all(bs, SMMU_SEC_SID_NS);
             smmu_iotlb_inv_vmid(bs, vmid, SMMU_SEC_SID_NS);
             break;
         }
@@ -1610,7 +1666,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp, SMMUSecSID sec_sid)
              * As currently only either s1 or s2 are supported
              * we can reuse same function for s2.
              */
-            smmuv3_range_inval(bs, &cmd, SMMU_STAGE_2, SMMU_SEC_SID_NS);
+            smmuv3_range_inval(bs, &cmd, SMMU_STAGE_2, SMMU_SEC_SID_NS, true);
             break;
         case SMMU_CMD_ATC_INV:
         {
@@ -1621,7 +1677,8 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp, SMMUSecSID sec_sid)
                 break;
             }
 
-            if (!smmuv3_accel_issue_inv_cmd(s, &cmd, sdev, errp)) {
+            if (!smmuv3_accel_issue_inv_cmd(s, &cmd, sdev,
+                                            SMMU_SEC_SID_NS, errp)) {
                 cmd_error = SMMU_CERROR_ILL;
                 break;
             }
diff --git a/hw/arm/trace-events b/hw/arm/trace-events
index ccc0ab50164..7cd4eb38578 100644
--- a/hw/arm/trace-events
+++ b/hw/arm/trace-events
@@ -19,12 +19,14 @@ smmu_ptw_page_pte(int stage, int level,  uint64_t iova, uint64_t baseaddr, uint6
 smmu_ptw_block_pte(int stage, int level, uint64_t baseaddr, uint64_t pteaddr, uint64_t pte, uint64_t iova, uint64_t gpa, int bsize_mb) "stage=%d level=%d base@=0x%"PRIx64" pte@=0x%"PRIx64" pte=0x%"PRIx64" iova=0x%"PRIx64" block address = 0x%"PRIx64" block size = %d MiB"
 smmu_get_pte(uint64_t baseaddr, int index, uint64_t pteaddr, uint64_t pte) "baseaddr=0x%"PRIx64" index=0x%x, pteaddr=0x%"PRIx64", pte=0x%"PRIx64
 smmu_iotlb_inv_all(void) "IOTLB invalidate all"
+smmu_iotlb_inv_by_sec_sid(int sec_sid) "IOTLB invalidate sec_sid=%d"
 smmu_iotlb_inv_asid_vmid(int sec_sid, int asid, int vmid) "IOTLB invalidate sec_sid=%d asid=%d vmid=%d"
 smmu_iotlb_inv_vmid(int sec_sid, int vmid) "IOTLB invalidate sec_sid=%d vmid=%d"
 smmu_iotlb_inv_vmid_s1(int sec_sid, int vmid) "IOTLB invalidate S1 sec_sid=%d vmid=%d"
 smmu_iotlb_inv_iova(int sec_sid, int asid, uint64_t addr) "IOTLB invalidate sec_sid=%d asid=%d addr=0x%"PRIx64
 smmu_configs_inv_sid_range(uint32_t start, uint32_t end) "Config cache INV SID range from 0x%x to 0x%x"
-smmu_config_cache_inv(uint32_t sid) "Config cache INV for sid=0x%x"
+smmu_configs_inv_sid_range_by_sec_sid(int sec_sid, uint32_t start, uint32_t end) "Config cache INV sec_sid=%d SID range from 0x%x to 0x%x"
+smmu_config_cache_inv(int sec_sid, uint32_t sid) "Config cache INV sec_sid=%d sid=0x%x"
 smmu_inv_notifiers_mr(const char *name) "iommu mr=%s"
 smmu_iotlb_lookup_hit(int sec_sid, int asid, int vmid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache HIT sec_sid=%d asid=%d vmid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d"
 smmu_iotlb_lookup_miss(int sec_sid, int asid, int vmid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache MISS sec_sid=%d asid=%d vmid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d"
@@ -52,14 +54,14 @@ smmuv3_translate_success(const char *n, uint16_t sid, uint64_t iova, uint64_t tr
 smmuv3_get_cd(uint64_t addr) "CD addr: 0x%"PRIx64
 smmuv3_decode_cd(uint32_t oas) "oas=%d"
 smmuv3_decode_cd_tt(int i, uint32_t tsz, uint64_t ttb, uint32_t granule_sz, bool had) "TT[%d]:tsz:%d ttb:0x%"PRIx64" granule_sz:%d had:%d"
-smmuv3_cmdq_cfgi_ste(int streamid) "streamid= 0x%x"
-smmuv3_cmdq_cfgi_ste_range(int start, int end) "start=0x%x - end=0x%x"
-smmuv3_cmdq_cfgi_cd(uint32_t sid) "sid=0x%x"
+smmuv3_cmdq_cfgi_ste(int sec_sid, int ssec, int streamid) "cmdq_sec_sid=%d ssec=%d streamid=0x%x"
+smmuv3_cmdq_cfgi_ste_range(int sec_sid, int ssec, int start, int end) "cmdq_sec_sid=%d ssec=%d start=0x%x - end=0x%x"
+smmuv3_cmdq_cfgi_cd(int sec_sid, int ssec, uint32_t sid) "cmdq_sec_sid=%d ssec=%d sid=0x%x"
 smmuv3_config_cache_hit(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache HIT for sid=0x%x (hits=%d, misses=%d, hit rate=%d)"
 smmuv3_config_cache_miss(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache MISS for sid=0x%x (hits=%d, misses=%d, hit rate=%d)"
 smmuv3_range_inval(int sec_sid, int vmid, int asid, uint64_t addr, uint8_t tg, uint64_t num_pages, uint8_t ttl, bool leaf, int stage) "sec_sid=%d vmid=%d asid=%d addr=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" ttl=%d leaf=%d stage=%d"
 smmuv3_cmdq_tlbi_nh(int sec_sid, int vmid) "sec_sid=%d vmid=%d"
-smmuv3_cmdq_tlbi_nsnh(void) ""
+smmuv3_cmdq_tlbi_nsnh(int sec_sid, int target_sec_sid) "cmdq_sec_sid=%d target_sec_sid=%d"
 smmuv3_cmdq_tlbi_nh_asid(int sec_sid, int asid) "sec_sid=%d asid=%d"
 smmuv3_cmdq_tlbi_s12_vmid(int vmid) "vmid=%d"
 smmuv3_notify_flag_add(const char *iommu) "ADD SMMUNotifier node for iommu mr=%s"
diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h
index 8e971c28093..a21c6061808 100644
--- a/include/hw/arm/smmu-common.h
+++ b/include/hw/arm/smmu-common.h
@@ -269,6 +269,7 @@ SMMUIOTLBKey smmu_get_iotlb_key(int asid, int vmid, uint64_t iova,
                                 uint8_t tg, uint8_t level, SMMUSecSID sec_sid);
 SMMUConfigKey smmu_get_config_key(SMMUDevice *sdev, SMMUSecSID sec_sid);
 void smmu_iotlb_inv_all(SMMUState *s);
+void smmu_iotlb_inv_by_sec_sid(SMMUState *s, SMMUSecSID sec_sid);
 void smmu_iotlb_inv_asid_vmid(SMMUState *s, int asid, int vmid,
                               SMMUSecSID sec_sid);
 void smmu_iotlb_inv_vmid(SMMUState *s, int vmid, SMMUSecSID sec_sid);
@@ -280,7 +281,12 @@ void smmu_iotlb_inv_ipa(SMMUState *s, int vmid, dma_addr_t ipa, uint8_t tg,
                         uint64_t num_pages, uint8_t ttl,
                         SMMUSecSID sec_sid);
 void smmu_configs_inv_sid_range(SMMUState *s, SMMUSIDRange sid_range);
+void smmu_configs_inv_sid_range_by_sec_sid(SMMUState *s,
+                                           SMMUSIDRange sid_range,
+                                           SMMUSecSID sec_sid);
 void smmu_configs_inv_sdev(SMMUState *s, SMMUDevice *sdev);
+void smmu_configs_inv_sdev_by_sec_sid(SMMUState *s, SMMUDevice *sdev,
+                                      SMMUSecSID sec_sid);
 /* Unmap the range of all the notifiers registered to any IOMMU mr */
 void smmu_inv_notifiers_all(SMMUState *s);
 
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 16/28] hw/arm/smmuv3: Add access checks for GERROR_IRQ_CFG registers
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (15 preceding siblings ...)
  2026-08-13 16:25 ` [RFC v5 15/28] hw/arm/smmu: Make CMDQ invalidation security-state aware Tao Tang
@ 2026-08-13 16:25 ` Tao Tang
  2026-08-13 16:25 ` [RFC v5 17/28] hw/arm/smmuv3: Add access checks for STRTAB_BASE and CR2 registers Tao Tang
                   ` (10 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:25 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Add helpers that separate IRQ_CFG register presence from the runtime
IRQ enable guard. The Non-secure and Secure GERROR_IRQ_CFG register sets
both use SMMU_IDR0.MSI for presence, while writability additionally
requires GERROR_IRQEN to be clear. IRQ_CTRL and IRQ_CTRLACK share one
synchronous backing field in the current model.

Reads return RES0 when the register set is absent. Writes are ignored
when it is absent or GERROR_IRQEN is set. Apply the same checks to all
three GERROR_IRQ_CFG registers and mask reserved bits in CFG0 and CFG2.

Fixes: fae4be38b35d ("hw/arm/smmuv3: Implement MMIO write operations")
Fixes: 10a83cb9887e ("hw/arm/smmuv3: Skeleton")
Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
---
 hw/arm/smmuv3.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 116 insertions(+), 2 deletions(-)

diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index 47f0d575817..628911d3f21 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -1428,6 +1428,62 @@ smmu_cmdq_stage2_supported(SMMUv3State *s, SMMUSecSID sec_sid)
     return true;
 }
 
+/* Check whether the selected IRQ_CFG register set is present. */
+static bool smmu_irq_cfg_present(SMMUv3State *s, SMMUSecSID sec_sid,
+                                 SMMUIrq irq)
+{
+    SMMUv3RegBank *bank = smmuv3_bank(s, SMMU_SEC_SID_NS);
+
+    switch (irq) {
+    case SMMU_IRQ_GERROR:
+        switch (sec_sid) {
+        case SMMU_SEC_SID_NS:
+        case SMMU_SEC_SID_S:
+            return FIELD_EX32(bank->idr[0], IDR0, MSI);
+        case SMMU_SEC_SID_NUM:
+            g_assert_not_reached();
+        }
+        break;
+    case SMMU_IRQ_EVTQ:
+    case SMMU_IRQ_PRIQ:
+    case SMMU_IRQ_CMD_SYNC:
+        g_assert_not_reached();
+    }
+
+    g_assert_not_reached();
+}
+
+/* Check whether the selected IRQ_CFG register set is writable. */
+static bool smmu_irq_cfg_writable(SMMUv3State *s, SMMUSecSID sec_sid,
+                                  SMMUIrq irq)
+{
+    SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
+    uint32_t irqen;
+
+    if (!smmu_irq_cfg_present(s, sec_sid, irq)) {
+        return false;
+    }
+
+    switch (irq) {
+    case SMMU_IRQ_GERROR:
+        irqen = FIELD_EX32(bank->irq_ctrl, IRQ_CTRL, GERROR_IRQEN);
+        break;
+    case SMMU_IRQ_EVTQ:
+    case SMMU_IRQ_PRIQ:
+    case SMMU_IRQ_CMD_SYNC:
+        g_assert_not_reached();
+    }
+
+    /* IRQ_CTRL and IRQ_CTRLACK share one synchronous backing field. */
+    return irqen == 0;
+}
+
+static bool
+smmu_gerror_irq_cfg_writable(SMMUv3State *s, SMMUSecSID sec_sid)
+{
+    return smmu_irq_cfg_writable(s, sec_sid, SMMU_IRQ_GERROR);
+}
+
 static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp, SMMUSecSID sec_sid)
 {
     SMMUState *bs = ARM_SMMU(s);
@@ -1735,7 +1791,14 @@ static MemTxResult smmu_writell(SMMUv3State *s, hwaddr offset,
 
     switch (offset) {
     case A_GERROR_IRQ_CFG0:
-        bank->gerror_irq_cfg0 = data;
+        if (!smmu_gerror_irq_cfg_writable(s, reg_sec_sid)) {
+            /* SMMU_(*_)_IRQ_CTRL.GERROR_IRQEN == 1: IGNORED this write */
+            qemu_log_mask(LOG_GUEST_ERROR, "GERROR_IRQ_CFG0 write ignored: "
+                         "register is RO when IRQ enabled\n");
+            return MEMTX_OK;
+        }
+
+        bank->gerror_irq_cfg0 = data & SMMU_GERROR_IRQ_CFG0_RESERVED;
         return MEMTX_OK;
     case A_STRTAB_BASE:
         bank->strtab_base = data;
@@ -1803,16 +1866,42 @@ static MemTxResult smmu_writel(SMMUv3State *s, hwaddr offset,
         smmuv3_cmdq_consume(s, &local_err, reg_sec_sid);
         break;
     case A_GERROR_IRQ_CFG0: /* 64b */
+        if (!smmu_gerror_irq_cfg_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR, "GERROR_IRQ_CFG0 write ignored: "
+                          "register is RO when IRQ enabled\n");
+            return MEMTX_OK;
+        }
+
+        data &= SMMU_GERROR_IRQ_CFG0_RESERVED;
         bank->gerror_irq_cfg0 = deposit64(bank->gerror_irq_cfg0, 0, 32, data);
         break;
     case A_GERROR_IRQ_CFG0 + 4:
+        if (!smmu_gerror_irq_cfg_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR, "GERROR_IRQ_CFG0 + 4 write ignored: "
+                          "register is RO when IRQ enabled\n");
+            return MEMTX_OK;
+        }
+
+        data &= SMMU_GERROR_IRQ_CFG0_RESERVED >> 32;
         bank->gerror_irq_cfg0 = deposit64(bank->gerror_irq_cfg0, 32, 32, data);
         break;
     case A_GERROR_IRQ_CFG1:
+        if (!smmu_gerror_irq_cfg_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR, "GERROR_IRQ_CFG1 write ignored: "
+                          "register is RO when IRQ enabled\n");
+            return MEMTX_OK;
+        }
+
         bank->gerror_irq_cfg1 = data;
         break;
     case A_GERROR_IRQ_CFG2:
-        bank->gerror_irq_cfg2 = data;
+        if (!smmu_gerror_irq_cfg_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR, "GERROR_IRQ_CFG2 write ignored: "
+                          "register is RO when IRQ enabled\n");
+            return MEMTX_OK;
+        }
+
+        bank->gerror_irq_cfg2 = data & SMMU_GERROR_IRQ_CFG2_RESERVED;
         break;
     case A_GBPA:
         /*
@@ -1938,6 +2027,11 @@ static MemTxResult smmu_readll(SMMUv3State *s, hwaddr offset,
 
     switch (offset) {
     case A_GERROR_IRQ_CFG0:
+        if (!smmu_irq_cfg_present(s, reg_sec_sid, SMMU_IRQ_GERROR)) {
+            *data = 0; /* RES0 */
+            return MEMTX_OK;
+        }
+
         *data = bank->gerror_irq_cfg0;
         return MEMTX_OK;
     case A_STRTAB_BASE:
@@ -2006,15 +2100,35 @@ static MemTxResult smmu_readl(SMMUv3State *s, hwaddr offset,
         *data = bank->gerrorn;
         return MEMTX_OK;
     case A_GERROR_IRQ_CFG0: /* 64b */
+        if (!smmu_irq_cfg_present(s, reg_sec_sid, SMMU_IRQ_GERROR)) {
+            *data = 0; /* RES0 */
+            return MEMTX_OK;
+        }
+
         *data = extract64(bank->gerror_irq_cfg0, 0, 32);
         return MEMTX_OK;
     case A_GERROR_IRQ_CFG0 + 4:
+        if (!smmu_irq_cfg_present(s, reg_sec_sid, SMMU_IRQ_GERROR)) {
+            *data = 0; /* RES0 */
+            return MEMTX_OK;
+        }
+
         *data = extract64(bank->gerror_irq_cfg0, 32, 32);
         return MEMTX_OK;
     case A_GERROR_IRQ_CFG1:
+        if (!smmu_irq_cfg_present(s, reg_sec_sid, SMMU_IRQ_GERROR)) {
+            *data = 0; /* RES0 */
+            return MEMTX_OK;
+        }
+
         *data = bank->gerror_irq_cfg1;
         return MEMTX_OK;
     case A_GERROR_IRQ_CFG2:
+        if (!smmu_irq_cfg_present(s, reg_sec_sid, SMMU_IRQ_GERROR)) {
+            *data = 0; /* RES0 */
+            return MEMTX_OK;
+        }
+
         *data = bank->gerror_irq_cfg2;
         return MEMTX_OK;
     case A_STRTAB_BASE: /* 64b */
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 17/28] hw/arm/smmuv3: Add access checks for STRTAB_BASE and CR2 registers
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (16 preceding siblings ...)
  2026-08-13 16:25 ` [RFC v5 16/28] hw/arm/smmuv3: Add access checks for GERROR_IRQ_CFG registers Tao Tang
@ 2026-08-13 16:25 ` Tao Tang
  2026-08-13 16:25 ` [RFC v5 18/28] hw/arm/smmuv3: Add access checks for CMDQ and EVENTQ registers Tao Tang
                   ` (9 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:25 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Add access control for SMMU_STRTAB_BASE and SMMU_CR2 registers to
ensure they can only be modified when the SMMU is disabled.

This implements:
- smmuv3_smmu_disabled_stable(): Check whether the SMMU is in a stable
   disabled state (CR0.SMMUEN == 0 and CR0ACK.SMMUEN == 0);
- smmu_strtab_base_writable(): returns true only when IDR1.TABLES_PRESET==0
   and SMMU is completely disabled.

Additionally, mask reserved bits on writes to SMMU_STRTAB_BASE using
SMMU_STRTAB_BASE_RESERVED.

Fixes: fae4be38b35d ("hw/arm/smmuv3: Implement MMIO write operations")
Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
---
 hw/arm/smmuv3.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 67 insertions(+), 2 deletions(-)

diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index 628911d3f21..de8ca7cda48 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -1484,6 +1484,36 @@ smmu_gerror_irq_cfg_writable(SMMUv3State *s, SMMUSecSID sec_sid)
     return smmu_irq_cfg_writable(s, sec_sid, SMMU_IRQ_GERROR);
 }
 
+static inline int smmuv3_get_cr0ack_smmuen(SMMUv3State *s, SMMUSecSID sec_sid)
+{
+    /*
+     * CR0, CR0ACK, S_CR0 and S_CR0ACK are bit-layout compatible, so we reuse
+     * the CR0 field definitions and only switch banks via sec_sid to reduce
+     * code duplication. Also the other bits in CR0/CR0ACK are relevant here.
+     */
+    return FIELD_EX32(s->bank[sec_sid].cr0ack, CR0, SMMUEN);
+}
+
+/* Check if SMMU is disabled in stable status */
+static inline bool smmuv3_smmu_disabled_stable(SMMUv3State *s, SMMUSecSID sec_sid)
+{
+    int cr0_smmuen = smmu_enabled(s, sec_sid);
+    int cr0ack_smmuen = smmuv3_get_cr0ack_smmuen(s, sec_sid);
+    return (cr0_smmuen == 0 && cr0ack_smmuen == 0);
+}
+
+/* Check if STRTAB_BASE register is writable */
+static bool smmu_strtab_base_writable(SMMUv3State *s, SMMUSecSID sec_sid)
+{
+    /* SMMU_IDR1.TABLES_PRESET applies to both modeled interfaces. */
+    if (FIELD_EX32(s->bank[SMMU_SEC_SID_NS].idr[1], IDR1, TABLES_PRESET)) {
+        return false;
+    }
+
+    /* Check SMMUEN conditions for the specific security domain */
+    return smmuv3_smmu_disabled_stable(s, sec_sid);
+}
+
 static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp, SMMUSecSID sec_sid)
 {
     SMMUState *bs = ARM_SMMU(s);
@@ -1801,7 +1831,14 @@ static MemTxResult smmu_writell(SMMUv3State *s, hwaddr offset,
         bank->gerror_irq_cfg0 = data & SMMU_GERROR_IRQ_CFG0_RESERVED;
         return MEMTX_OK;
     case A_STRTAB_BASE:
-        bank->strtab_base = data;
+        if (!smmu_strtab_base_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "STRTAB_BASE write ignored: register is RO\n");
+            return MEMTX_OK;
+        }
+
+        /* Clear reserved bits according to spec */
+        bank->strtab_base = data & SMMU_STRTAB_BASE_RESERVED;
         return MEMTX_OK;
     case A_CMDQ_BASE:
         bank->cmdq.base = data;
@@ -1852,7 +1889,15 @@ static MemTxResult smmu_writel(SMMUv3State *s, hwaddr offset,
         bank->cr[1] = data;
         break;
     case A_CR2:
-        bank->cr[2] = data;
+        if (smmuv3_smmu_disabled_stable(s, reg_sec_sid)) {
+            /* Allow write: SMMUEN is 0 in both CR0 and CR0ACK */
+            bank->cr[2] = data;
+        } else {
+            /* CONSTRAINED UNPREDICTABLE behavior: Ignore this write */
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "CR2 write ignored: register is read-only when "
+                          "CR0.SMMUEN or CR0ACK.SMMUEN is set\n");
+        }
         break;
     case A_IRQ_CTRL:
         bank->irq_ctrl = data;
@@ -1915,12 +1960,32 @@ static MemTxResult smmu_writel(SMMUv3State *s, hwaddr offset,
         }
         break;
     case A_STRTAB_BASE: /* 64b */
+        if (!smmu_strtab_base_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "STRTAB_BASE write ignored: register is RO\n");
+            return MEMTX_OK;
+        }
+
+        data &= SMMU_STRTAB_BASE_RESERVED;
         bank->strtab_base = deposit64(bank->strtab_base, 0, 32, data);
         break;
     case A_STRTAB_BASE + 4:
+        if (!smmu_strtab_base_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "STRTAB_BASE + 4 write ignored: register is RO\n");
+            return MEMTX_OK;
+        }
+
+        data &= SMMU_STRTAB_BASE_RESERVED;
         bank->strtab_base = deposit64(bank->strtab_base, 32, 32, data);
         break;
     case A_STRTAB_BASE_CFG:
+        if (!smmu_strtab_base_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "STRTAB_BASE_CFG write ignored: register is RO\n");
+            return MEMTX_OK;
+        }
+
         bank->strtab_base_cfg = data;
         if (FIELD_EX32(data, STRTAB_BASE_CFG, FMT) == 1) {
             bank->sid_split = FIELD_EX32(data, STRTAB_BASE_CFG, SPLIT);
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 18/28] hw/arm/smmuv3: Add access checks for CMDQ and EVENTQ registers
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (17 preceding siblings ...)
  2026-08-13 16:25 ` [RFC v5 17/28] hw/arm/smmuv3: Add access checks for STRTAB_BASE and CR2 registers Tao Tang
@ 2026-08-13 16:25 ` Tao Tang
  2026-08-13 16:25 ` [RFC v5 19/28] hw/arm/smmuv3: Determine register bank from MMIO offset Tao Tang
                   ` (8 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:25 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Add access control for command queue and event queue related registers
to ensure they can only be modified under proper conditions.

For command queue (CMDQ):
- smmu_cmdq_disabled_stable(): checks CMDQ bit in CR0/CR0ACK
- smmu_cmdq_base_writable(): checks IDR1.QUEUES_PRESET==0 and CMDQ disabled

For event queue (EVTQ):
- smmu_eventq_disabled_stable(): checks EVTQ bit in CR0/CR0ACK
- smmu_eventq_base_writable(): checks IDR1.QUEUES_PRESET==0 and EVTQ disabled
- smmu_irq_cfg_present(): checks SMMU_IDR0.MSI for the Non-secure
  EVENTQ_IRQ_CFG register set and SMMU_S_IDR0.MSI for the Secure set
- smmu_eventq_irq_cfg_writable(): additionally checks EVENTQ_IRQEN

Absent EVENTQ_IRQ_CFG registers read as RES0. Present registers retain
their programmed values for readback. The helper remains keyed by
SEC_SID so future Realm support can select SMMU_R_IDR0.MSI.

Additionally, mask reserved bits on writes using SMMU_QUEUE_BASE_RESERVED
for queue base registers and SMMU_EVENTQ_IRQ_CFG0_RESERVED for
EVENTQ_IRQ_CFG0.

Fixes: fae4be38b35d ("hw/arm/smmuv3: Implement MMIO write operations")
Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
---
 hw/arm/smmuv3.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 204 insertions(+), 6 deletions(-)

diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index de8ca7cda48..efa78869251 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -1432,19 +1432,29 @@ smmu_cmdq_stage2_supported(SMMUv3State *s, SMMUSecSID sec_sid)
 static bool smmu_irq_cfg_present(SMMUv3State *s, SMMUSecSID sec_sid,
                                  SMMUIrq irq)
 {
-    SMMUv3RegBank *bank = smmuv3_bank(s, SMMU_SEC_SID_NS);
+    SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
+    SMMUv3RegBank *ns_bank = smmuv3_bank(s, SMMU_SEC_SID_NS);
 
     switch (irq) {
     case SMMU_IRQ_GERROR:
         switch (sec_sid) {
         case SMMU_SEC_SID_NS:
         case SMMU_SEC_SID_S:
-            return FIELD_EX32(bank->idr[0], IDR0, MSI);
+            return FIELD_EX32(ns_bank->idr[0], IDR0, MSI);
         case SMMU_SEC_SID_NUM:
             g_assert_not_reached();
         }
         break;
     case SMMU_IRQ_EVTQ:
+        switch (sec_sid) {
+        case SMMU_SEC_SID_NS:
+            return FIELD_EX32(bank->idr[0], IDR0, MSI);
+        case SMMU_SEC_SID_S:
+            return FIELD_EX32(bank->idr[0], S_IDR0, MSI);
+        case SMMU_SEC_SID_NUM:
+            g_assert_not_reached();
+        }
+        break;
     case SMMU_IRQ_PRIQ:
     case SMMU_IRQ_CMD_SYNC:
         g_assert_not_reached();
@@ -1469,6 +1479,8 @@ static bool smmu_irq_cfg_writable(SMMUv3State *s, SMMUSecSID sec_sid,
         irqen = FIELD_EX32(bank->irq_ctrl, IRQ_CTRL, GERROR_IRQEN);
         break;
     case SMMU_IRQ_EVTQ:
+        irqen = FIELD_EX32(bank->irq_ctrl, IRQ_CTRL, EVENTQ_IRQEN);
+        break;
     case SMMU_IRQ_PRIQ:
     case SMMU_IRQ_CMD_SYNC:
         g_assert_not_reached();
@@ -1514,6 +1526,69 @@ static bool smmu_strtab_base_writable(SMMUv3State *s, SMMUSecSID sec_sid)
     return smmuv3_smmu_disabled_stable(s, sec_sid);
 }
 
+static inline int smmuv3_get_cr0_cmdqen(SMMUv3State *s, SMMUSecSID sec_sid)
+{
+    return FIELD_EX32(s->bank[sec_sid].cr[0], CR0, CMDQEN);
+}
+
+static inline int smmuv3_get_cr0ack_cmdqen(SMMUv3State *s, SMMUSecSID sec_sid)
+{
+    return FIELD_EX32(s->bank[sec_sid].cr0ack, CR0, CMDQEN);
+}
+
+static inline int smmuv3_get_cr0_eventqen(SMMUv3State *s, SMMUSecSID sec_sid)
+{
+    return FIELD_EX32(s->bank[sec_sid].cr[0], CR0, EVENTQEN);
+}
+
+static inline int smmuv3_get_cr0ack_eventqen(SMMUv3State *s, SMMUSecSID sec_sid)
+{
+    return FIELD_EX32(s->bank[sec_sid].cr0ack, CR0, EVENTQEN);
+}
+
+/* Check if CMDQ is disabled in stable status */
+static bool smmu_cmdq_disabled_stable(SMMUv3State *s, SMMUSecSID sec_sid)
+{
+    int cr0_cmdqen = smmuv3_get_cr0_cmdqen(s, sec_sid);
+    int cr0ack_cmdqen = smmuv3_get_cr0ack_cmdqen(s, sec_sid);
+    return (cr0_cmdqen == 0 && cr0ack_cmdqen == 0);
+}
+
+/* Check if CMDQ_BASE register is writable */
+static bool smmu_cmdq_base_writable(SMMUv3State *s, SMMUSecSID sec_sid)
+{
+    /* SMMU_IDR1.QUEUES_PRESET applies to both modeled interfaces. */
+    if (FIELD_EX32(s->bank[SMMU_SEC_SID_NS].idr[1], IDR1, QUEUES_PRESET)) {
+        return false;
+    }
+
+    return smmu_cmdq_disabled_stable(s, sec_sid);
+}
+
+/* Check if EVENTQ is disabled in stable status */
+static bool smmu_eventq_disabled_stable(SMMUv3State *s, SMMUSecSID sec_sid)
+{
+    int cr0_eventqen = smmuv3_get_cr0_eventqen(s, sec_sid);
+    int cr0ack_eventqen = smmuv3_get_cr0ack_eventqen(s, sec_sid);
+    return (cr0_eventqen == 0 && cr0ack_eventqen == 0);
+}
+
+/* Check if EVENTQ_BASE register is writable */
+static bool smmu_eventq_base_writable(SMMUv3State *s, SMMUSecSID sec_sid)
+{
+    if (FIELD_EX32(s->bank[SMMU_SEC_SID_NS].idr[1], IDR1, QUEUES_PRESET)) {
+        return false;
+    }
+
+    return smmu_eventq_disabled_stable(s, sec_sid);
+}
+
+/* Check if EVENTQ_IRQ_CFGx is writable */
+static bool smmu_eventq_irq_cfg_writable(SMMUv3State *s, SMMUSecSID sec_sid)
+{
+    return smmu_irq_cfg_writable(s, sec_sid, SMMU_IRQ_EVTQ);
+}
+
 static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp, SMMUSecSID sec_sid)
 {
     SMMUState *bs = ARM_SMMU(s);
@@ -1841,21 +1916,39 @@ static MemTxResult smmu_writell(SMMUv3State *s, hwaddr offset,
         bank->strtab_base = data & SMMU_STRTAB_BASE_RESERVED;
         return MEMTX_OK;
     case A_CMDQ_BASE:
-        bank->cmdq.base = data;
+        if (!smmu_cmdq_base_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "CMDQ_BASE write ignored: register is RO\n");
+            return MEMTX_OK;
+        }
+
+        bank->cmdq.base = data & SMMU_QUEUE_BASE_RESERVED;
         bank->cmdq.log2size = extract64(bank->cmdq.base, 0, 5);
         if (bank->cmdq.log2size > SMMU_CMDQS) {
             bank->cmdq.log2size = SMMU_CMDQS;
         }
         return MEMTX_OK;
     case A_EVENTQ_BASE:
-        bank->eventq.base = data;
+        if (!smmu_eventq_base_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "EVENTQ_BASE write ignored: register is RO\n");
+            return MEMTX_OK;
+        }
+
+        bank->eventq.base = data & SMMU_QUEUE_BASE_RESERVED;
         bank->eventq.log2size = extract64(bank->eventq.base, 0, 5);
         if (bank->eventq.log2size > SMMU_EVENTQS) {
             bank->eventq.log2size = SMMU_EVENTQS;
         }
         return MEMTX_OK;
     case A_EVENTQ_IRQ_CFG0:
-        bank->eventq_irq_cfg0 = data;
+        if (!smmu_eventq_irq_cfg_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "EVENTQ_IRQ_CFG0 write ignored: register is RO\n");
+            return MEMTX_OK;
+        }
+
+        bank->eventq_irq_cfg0 = data & SMMU_EVENTQ_IRQ_CFG0_RESERVED;
         return MEMTX_OK;
     default:
         qemu_log_mask(LOG_UNIMP,
@@ -2001,6 +2094,13 @@ static MemTxResult smmu_writel(SMMUv3State *s, hwaddr offset,
         }
         break;
     case A_CMDQ_BASE: /* 64b */
+        if (!smmu_cmdq_base_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "CMDQ_BASE write ignored: register is RO\n");
+            return MEMTX_OK;
+        }
+
+        data &= SMMU_QUEUE_BASE_RESERVED;
         bank->cmdq.base = deposit64(bank->cmdq.base, 0, 32, data);
         bank->cmdq.log2size = extract64(bank->cmdq.base, 0, 5);
         if (bank->cmdq.log2size > SMMU_CMDQS) {
@@ -2008,6 +2108,13 @@ static MemTxResult smmu_writel(SMMUv3State *s, hwaddr offset,
         }
         break;
     case A_CMDQ_BASE + 4: /* 64b */
+        if (!smmu_cmdq_base_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "CMDQ_BASE + 4 write ignored: register is RO\n");
+            return MEMTX_OK;
+        }
+
+        data &= SMMU_QUEUE_BASE_RESERVED;
         bank->cmdq.base = deposit64(bank->cmdq.base, 32, 32, data);
         break;
     case A_CMDQ_PROD:
@@ -2015,9 +2122,22 @@ static MemTxResult smmu_writel(SMMUv3State *s, hwaddr offset,
         smmuv3_cmdq_consume(s, &local_err, reg_sec_sid);
         break;
     case A_CMDQ_CONS:
+        if (!smmu_cmdq_disabled_stable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "CMDQ_CONS write ignored: register is RO\n");
+            return MEMTX_OK;
+        }
+
         bank->cmdq.cons = data;
         break;
     case A_EVENTQ_BASE: /* 64b */
+        if (!smmu_eventq_base_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "EVENTQ_BASE write ignored: register is RO\n");
+            return MEMTX_OK;
+        }
+
+        data &= SMMU_QUEUE_BASE_RESERVED;
         bank->eventq.base = deposit64(bank->eventq.base, 0, 32, data);
         bank->eventq.log2size = extract64(bank->eventq.base, 0, 5);
         if (bank->eventq.log2size > SMMU_EVENTQS) {
@@ -2025,24 +2145,63 @@ static MemTxResult smmu_writel(SMMUv3State *s, hwaddr offset,
         }
         break;
     case A_EVENTQ_BASE + 4:
+        if (!smmu_eventq_base_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "EVENTQ_BASE + 4 write ignored: register is RO\n");
+            return MEMTX_OK;
+        }
+
+        data &= SMMU_QUEUE_BASE_RESERVED;
         bank->eventq.base = deposit64(bank->eventq.base, 32, 32, data);
         break;
     case A_EVENTQ_PROD:
+        if (!smmu_eventq_disabled_stable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "EVENTQ_PROD write ignored: register is RO\n");
+            return MEMTX_OK;
+        }
+
         bank->eventq.prod = data;
         break;
     case A_EVENTQ_CONS:
         bank->eventq.cons = data;
         break;
     case A_EVENTQ_IRQ_CFG0: /* 64b */
+        if (!smmu_eventq_irq_cfg_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "EVENTQ_IRQ_CFG0 write ignored: register is RO\n");
+            return MEMTX_OK;
+        }
+
+        data &= SMMU_EVENTQ_IRQ_CFG0_RESERVED;
         bank->eventq_irq_cfg0 = deposit64(bank->eventq_irq_cfg0, 0, 32, data);
         break;
     case A_EVENTQ_IRQ_CFG0 + 4:
+        if (!smmu_eventq_irq_cfg_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "EVENTQ_IRQ_CFG0+4 write ignored: register is RO\n");
+            return MEMTX_OK;
+        }
+
+        data &= SMMU_EVENTQ_IRQ_CFG0_RESERVED >> 32;
         bank->eventq_irq_cfg0 = deposit64(bank->eventq_irq_cfg0, 32, 32, data);
         break;
     case A_EVENTQ_IRQ_CFG1:
+        if (!smmu_eventq_irq_cfg_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "EVENTQ_IRQ_CFG1 write ignored: register is RO\n");
+            return MEMTX_OK;
+        }
+
         bank->eventq_irq_cfg1 = data;
         break;
     case A_EVENTQ_IRQ_CFG2:
+        if (!smmu_eventq_irq_cfg_writable(s, reg_sec_sid)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "EVENTQ_IRQ_CFG2 write ignored: register is RO\n");
+            return MEMTX_OK;
+        }
+
         bank->eventq_irq_cfg2 = data;
         break;
     default:
@@ -2108,6 +2267,14 @@ static MemTxResult smmu_readll(SMMUv3State *s, hwaddr offset,
     case A_EVENTQ_BASE:
         *data = bank->eventq.base;
         return MEMTX_OK;
+    case A_EVENTQ_IRQ_CFG0:
+        if (!smmu_irq_cfg_present(s, reg_sec_sid, SMMU_IRQ_EVTQ)) {
+            *data = 0; /* RES0 */
+            return MEMTX_OK;
+        }
+
+        *data = bank->eventq_irq_cfg0;
+        return MEMTX_OK;
     default:
         *data = 0;
         qemu_log_mask(LOG_UNIMP,
@@ -2229,6 +2396,38 @@ static MemTxResult smmu_readl(SMMUv3State *s, hwaddr offset,
     case A_EVENTQ_CONS:
         *data = bank->eventq.cons;
         return MEMTX_OK;
+    case A_EVENTQ_IRQ_CFG0: /* 64b */
+        if (!smmu_irq_cfg_present(s, reg_sec_sid, SMMU_IRQ_EVTQ)) {
+            *data = 0; /* RES0 */
+            return MEMTX_OK;
+        }
+
+        *data = extract64(bank->eventq_irq_cfg0, 0, 32);
+        return MEMTX_OK;
+    case A_EVENTQ_IRQ_CFG0 + 4:
+        if (!smmu_irq_cfg_present(s, reg_sec_sid, SMMU_IRQ_EVTQ)) {
+            *data = 0; /* RES0 */
+            return MEMTX_OK;
+        }
+
+        *data = extract64(bank->eventq_irq_cfg0, 32, 32);
+        return MEMTX_OK;
+    case A_EVENTQ_IRQ_CFG1:
+        if (!smmu_irq_cfg_present(s, reg_sec_sid, SMMU_IRQ_EVTQ)) {
+            *data = 0; /* RES0 */
+            return MEMTX_OK;
+        }
+
+        *data = bank->eventq_irq_cfg1;
+        return MEMTX_OK;
+    case A_EVENTQ_IRQ_CFG2:
+        if (!smmu_irq_cfg_present(s, reg_sec_sid, SMMU_IRQ_EVTQ)) {
+            *data = 0; /* RES0 */
+            return MEMTX_OK;
+        }
+
+        *data = bank->eventq_irq_cfg2;
+        return MEMTX_OK;
     default:
         *data = 0;
         qemu_log_mask(LOG_UNIMP,
@@ -2610,4 +2809,3 @@ static void smmuv3_register_types(void)
 }
 
 type_init(smmuv3_register_types)
-
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 19/28] hw/arm/smmuv3: Determine register bank from MMIO offset
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (18 preceding siblings ...)
  2026-08-13 16:25 ` [RFC v5 18/28] hw/arm/smmuv3: Add access checks for CMDQ and EVENTQ registers Tao Tang
@ 2026-08-13 16:25 ` Tao Tang
  2026-08-13 16:25 ` [RFC v5 20/28] hw/arm/smmuv3: Route IRQ and GERROR handling by SEC_SID Tao Tang
                   ` (7 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:25 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Modify the main MMIO handlers to select the target register bank from the
memory-mapped offset.

Offsets in the Secure register window select SMMU_SEC_SID_S and are
normalized by subtracting SMMU_SECURE_REG_START. Other offsets select the
Non-secure bank. Pass the selected SEC_SID to the register access helpers,
where the normalized bank-local offset identifies the register.

Keep bank-specific differences explicit, such as S_IDR5 being unimplemented.
This patch implements Non-secure and Secure bank dispatch and prepares for
later access checks; Realm and Root programming interfaces are not modeled.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
---
 hw/arm/smmuv3.c | 44 ++++++++++++++++++++++++++++++++------------
 1 file changed, 32 insertions(+), 12 deletions(-)

diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index efa78869251..b36bc4a54e0 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -1889,9 +1889,9 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp, SMMUSecSID sec_sid)
 }
 
 static MemTxResult smmu_writell(SMMUv3State *s, hwaddr offset,
-                               uint64_t data, MemTxAttrs attrs)
+                                uint64_t data, MemTxAttrs attrs,
+                                SMMUSecSID reg_sec_sid)
 {
-    SMMUSecSID reg_sec_sid = SMMU_SEC_SID_NS;
     SMMUv3RegBank *bank = smmuv3_bank(s, reg_sec_sid);
 
     switch (offset) {
@@ -1959,10 +1959,10 @@ static MemTxResult smmu_writell(SMMUv3State *s, hwaddr offset,
 }
 
 static MemTxResult smmu_writel(SMMUv3State *s, hwaddr offset,
-                               uint64_t data, MemTxAttrs attrs)
+                               uint64_t data, MemTxAttrs attrs,
+                               SMMUSecSID reg_sec_sid)
 {
     Error *local_err = NULL;
-    SMMUSecSID reg_sec_sid = SMMU_SEC_SID_NS;
     SMMUv3RegBank *bank = smmuv3_bank(s, reg_sec_sid);
 
     switch (offset) {
@@ -2223,16 +2223,26 @@ static MemTxResult smmu_write_mmio(void *opaque, hwaddr offset, uint64_t data,
     SMMUState *sys = opaque;
     SMMUv3State *s = ARM_SMMUV3(sys);
     MemTxResult r;
+    SMMUSecSID reg_sec_sid = SMMU_SEC_SID_NS;
 
     /* CONSTRAINED UNPREDICTABLE choice to have page0/1 be exact aliases */
     offset &= ~0x10000;
 
+    /*
+     * Dispatch the Non-secure window directly. After access validation,
+     * translate the Secure window to its bank-local register offsets.
+     */
+    if (offset >= SMMU_SECURE_REG_START) {
+        reg_sec_sid = SMMU_SEC_SID_S;
+        offset -= SMMU_SECURE_REG_START;
+    }
+
     switch (size) {
     case 8:
-        r = smmu_writell(s, offset, data, attrs);
+        r = smmu_writell(s, offset, data, attrs, reg_sec_sid);
         break;
     case 4:
-        r = smmu_writel(s, offset, data, attrs);
+        r = smmu_writel(s, offset, data, attrs, reg_sec_sid);
         break;
     default:
         r = MEMTX_ERROR;
@@ -2244,9 +2254,9 @@ static MemTxResult smmu_write_mmio(void *opaque, hwaddr offset, uint64_t data,
 }
 
 static MemTxResult smmu_readll(SMMUv3State *s, hwaddr offset,
-                               uint64_t *data, MemTxAttrs attrs)
+                               uint64_t *data, MemTxAttrs attrs,
+                               SMMUSecSID reg_sec_sid)
 {
-    SMMUSecSID reg_sec_sid = SMMU_SEC_SID_NS;
     SMMUv3RegBank *bank = smmuv3_bank(s, reg_sec_sid);
 
     switch (offset) {
@@ -2285,9 +2295,9 @@ static MemTxResult smmu_readll(SMMUv3State *s, hwaddr offset,
 }
 
 static MemTxResult smmu_readl(SMMUv3State *s, hwaddr offset,
-                              uint64_t *data, MemTxAttrs attrs)
+                              uint64_t *data, MemTxAttrs attrs,
+                              SMMUSecSID reg_sec_sid)
 {
-    SMMUSecSID reg_sec_sid = SMMU_SEC_SID_NS;
     SMMUv3RegBank *bank = smmuv3_bank(s, reg_sec_sid);
 
     switch (offset) {
@@ -2295,6 +2305,10 @@ static MemTxResult smmu_readl(SMMUv3State *s, hwaddr offset,
         *data = smmuv3_idreg(offset - A_IDREGS);
         return MEMTX_OK;
     case A_IDR0 ... A_IDR5:
+        /* IDR5 does not support Secure or Realm state. */
+        if ((reg_sec_sid != SMMU_SEC_SID_NS) && (offset == A_IDR5)) {
+            goto unhandled;
+        }
         *data = bank->idr[(offset - A_IDR0) / 4];
         return MEMTX_OK;
     case A_IIDR:
@@ -2429,6 +2443,7 @@ static MemTxResult smmu_readl(SMMUv3State *s, hwaddr offset,
         *data = bank->eventq_irq_cfg2;
         return MEMTX_OK;
     default:
+    unhandled:
         *data = 0;
         qemu_log_mask(LOG_UNIMP,
                       "%s unhandled 32-bit access at 0x%"PRIx64" (RAZ)\n",
@@ -2443,16 +2458,21 @@ static MemTxResult smmu_read_mmio(void *opaque, hwaddr offset, uint64_t *data,
     SMMUState *sys = opaque;
     SMMUv3State *s = ARM_SMMUV3(sys);
     MemTxResult r;
+    SMMUSecSID reg_sec_sid = SMMU_SEC_SID_NS;
 
     /* CONSTRAINED UNPREDICTABLE choice to have page0/1 be exact aliases */
     offset &= ~0x10000;
+    if (offset >= SMMU_SECURE_REG_START) {
+        reg_sec_sid = SMMU_SEC_SID_S;
+        offset -= SMMU_SECURE_REG_START;
+    }
 
     switch (size) {
     case 8:
-        r = smmu_readll(s, offset, data, attrs);
+        r = smmu_readll(s, offset, data, attrs, reg_sec_sid);
         break;
     case 4:
-        r = smmu_readl(s, offset, data, attrs);
+        r = smmu_readl(s, offset, data, attrs, reg_sec_sid);
         break;
     default:
         r = MEMTX_ERROR;
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 20/28] hw/arm/smmuv3: Route IRQ and GERROR handling by SEC_SID
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (19 preceding siblings ...)
  2026-08-13 16:25 ` [RFC v5 19/28] hw/arm/smmuv3: Determine register bank from MMIO offset Tao Tang
@ 2026-08-13 16:25 ` Tao Tang
  2026-08-13 16:26 ` [RFC v5 21/28] hw/arm/smmuv3: Implement SMMU_S_INIT register Tao Tang
                   ` (6 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:25 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Interrupt and global error handling currently always selects the
Non-secure register bank, even when the event or command originates
from the Secure programming interface. This splits CMDQ_CONS.ERR from
GERROR.CMDQ_ERR and prevents Secure software from acknowledging the
error through S_GERRORN.

Pass SEC_SID through the IRQ and GERROR helpers and all queue call
sites so interrupt enable checks, global error reporting, and
acknowledgement use the originating programming-interface bank.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
---
 hw/arm/smmuv3.c | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index b36bc4a54e0..d69fc0898af 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -49,11 +49,11 @@
  *
  * @irq: irq type
  * @gerror_mask: mask of gerrors to toggle (relevant if @irq is GERROR)
+ * @sec_sid: security state of the programming interface
  */
 static void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq,
-                               uint32_t gerror_mask)
+                               uint32_t gerror_mask, SMMUSecSID sec_sid)
 {
-    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
     SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
 
     bool pulse = false;
@@ -90,9 +90,9 @@ static void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq,
     }
 }
 
-static void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t new_gerrorn)
+static void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t new_gerrorn,
+                                 SMMUSecSID sec_sid)
 {
-    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
     SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
     uint32_t pending = bank->gerror ^ bank->gerrorn;
     uint32_t toggled = bank->gerrorn ^ new_gerrorn;
@@ -173,7 +173,7 @@ static MemTxResult smmuv3_write_eventq(SMMUv3State *s, SMMUSecSID sec_sid,
     }
 
     if (!smmuv3_q_empty(q)) {
-        smmuv3_trigger_irq(s, SMMU_IRQ_EVTQ, 0);
+        smmuv3_trigger_irq(s, SMMU_IRQ_EVTQ, 0, sec_sid);
     }
     return MEMTX_OK;
 }
@@ -187,7 +187,8 @@ void smmuv3_propagate_event(SMMUv3State *s, Evt *evt, SMMUSecSID sec_sid)
     QEMU_LOCK_GUARD(&s->mutex);
     r = smmuv3_write_eventq(s, sec_sid, evt);
     if (r != MEMTX_OK) {
-        smmuv3_trigger_irq(s, SMMU_IRQ_GERROR, R_GERROR_EVENTQ_ABT_ERR_MASK);
+        smmuv3_trigger_irq(s, SMMU_IRQ_GERROR,
+                           R_GERROR_EVENTQ_ABT_ERR_MASK, sec_sid);
     }
 }
 
@@ -1645,7 +1646,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp, SMMUSecSID sec_sid)
         switch (type) {
         case SMMU_CMD_SYNC:
             if (CMD_SYNC_CS(&cmd) & CMD_SYNC_SIG_IRQ) {
-                smmuv3_trigger_irq(s, SMMU_IRQ_CMD_SYNC, 0);
+                smmuv3_trigger_irq(s, SMMU_IRQ_CMD_SYNC, 0, sec_sid);
             }
             break;
         case SMMU_CMD_PREFETCH_CONFIG:
@@ -1879,7 +1880,8 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp, SMMUSecSID sec_sid)
     if (cmd_error) {
         trace_smmuv3_cmdq_consume_error(smmu_cmd_string(type), cmd_error);
         smmu_write_cmdq_err(s, cmd_error, sec_sid);
-        smmuv3_trigger_irq(s, SMMU_IRQ_GERROR, R_GERROR_CMDQ_ERR_MASK);
+        smmuv3_trigger_irq(s, SMMU_IRQ_GERROR,
+                           R_GERROR_CMDQ_ERR_MASK, sec_sid);
     }
 
     trace_smmuv3_cmdq_consume_out(Q_PROD(q), Q_CONS(q),
@@ -1996,7 +1998,7 @@ static MemTxResult smmu_writel(SMMUv3State *s, hwaddr offset,
         bank->irq_ctrl = data;
         break;
     case A_GERRORN:
-        smmuv3_write_gerrorn(s, data);
+        smmuv3_write_gerrorn(s, data, reg_sec_sid);
         /*
          * By acknowledging the CMDQ_ERR, SW may notify cmds can
          * be processed again
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 21/28] hw/arm/smmuv3: Implement SMMU_S_INIT register
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (20 preceding siblings ...)
  2026-08-13 16:25 ` [RFC v5 20/28] hw/arm/smmuv3: Route IRQ and GERROR handling by SEC_SID Tao Tang
@ 2026-08-13 16:26 ` Tao Tang
  2026-08-13 16:26 ` [RFC v5 22/28] hw/arm/smmuv3: Harden security checks in MMIO handlers Tao Tang
                   ` (5 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:26 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Implement read/write handlers for the SMMU_S_INIT secure-only register.

Writing INV_ALL provides a mechanism for software to perform a global
invalidation of ALL caches within the SMMU, including IOTLBs and
configuration caches across all security states.

The MMIO dispatcher decodes the target register bank from the offset and
normalizes Secure-window offsets by subtracting SMMU_SECURE_REG_START
before switching on the bank-local offset. S_INIT is a Secure-only
register and its A_S_INIT constant is an absolute Secure-window offset,
so the handler matches it using A_S_INIT - SMMU_SECURE_REG_START and
rejects accesses through Non-secure banks.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
---
 hw/arm/smmuv3.c     | 43 +++++++++++++++++++++++++++++++++++++++++++
 hw/arm/trace-events |  1 +
 2 files changed, 44 insertions(+)

diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index d69fc0898af..dc3fa618883 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -393,6 +393,21 @@ static int smmu_get_ste(SMMUv3State *s, dma_addr_t addr, STE *buf,
 
 }
 
+static void smmuv3_invalidate_all_caches(SMMUv3State *s)
+{
+    SMMUState *bs = &s->smmu_state;
+    trace_smmuv3_invalidate_all_caches();
+
+    /* Clear all cached configs including STE and CD */
+    if (bs->configs) {
+        g_hash_table_remove_all(bs->configs);
+    }
+
+    /* Invalidate all SMMU IOTLB entries */
+    smmu_inv_notifiers_all(&s->smmu_state);
+    smmu_iotlb_inv_all(bs);
+}
+
 static SMMUTranslationStatus smmuv3_do_translate(SMMUv3State *s, hwaddr addr,
                                                  SMMUTransCfg *cfg,
                                                  SMMUEventInfo *event,
@@ -2206,7 +2221,29 @@ static MemTxResult smmu_writel(SMMUv3State *s, hwaddr offset,
 
         bank->eventq_irq_cfg2 = data;
         break;
+    case A_S_INIT - SMMU_SECURE_REG_START:
+        if (reg_sec_sid != SMMU_SEC_SID_S) {
+            goto unhandled;
+        }
+        if (data & R_S_INIT_INV_ALL_MASK) {
+            /*
+             * If SMMU_ROOT_CR0.GPCEN == 0, a write of 1 to INV_ALL when any
+             * SMMU_(*_)CR0.SMMUEN == 1, .... , is CONSTRAINED UNPREDICTABLE
+             * according to (IHI 0070G.b) 6.3.62 SMMU_S_INIT, Page 465.
+             */
+            if (!smmuv3_smmu_disabled_stable(s, SMMU_SEC_SID_NS) ||
+                !smmuv3_smmu_disabled_stable(s, SMMU_SEC_SID_S)) {
+                /* CONSTRAINED UNPREDICTABLE behavior: Ignore this write */
+                qemu_log_mask(LOG_GUEST_ERROR, "S_INIT write ignored: "
+                              "(S_)CR0.SMMUEN or (S_)CR0ACK.SMMUEN is set\n");
+                return MEMTX_OK;
+            }
+            smmuv3_invalidate_all_caches(s);
+        }
+        /* Synchronous emulation: invalidation completed instantly. */
+        break;
     default:
+    unhandled:
         qemu_log_mask(LOG_UNIMP,
                       "%s Unexpected 32-bit access to 0x%"PRIx64" (WI)\n",
                       __func__, offset);
@@ -2444,6 +2481,12 @@ static MemTxResult smmu_readl(SMMUv3State *s, hwaddr offset,
 
         *data = bank->eventq_irq_cfg2;
         return MEMTX_OK;
+    case A_S_INIT - SMMU_SECURE_REG_START:
+        if (reg_sec_sid != SMMU_SEC_SID_S) {
+            goto unhandled;
+        }
+        *data = 0;
+        return MEMTX_OK;
     default:
     unhandled:
         *data = 0;
diff --git a/hw/arm/trace-events b/hw/arm/trace-events
index 7cd4eb38578..ac9f737a5e6 100644
--- a/hw/arm/trace-events
+++ b/hw/arm/trace-events
@@ -67,6 +67,7 @@ smmuv3_cmdq_tlbi_s12_vmid(int vmid) "vmid=%d"
 smmuv3_notify_flag_add(const char *iommu) "ADD SMMUNotifier node for iommu mr=%s"
 smmuv3_notify_flag_del(const char *iommu) "DEL SMMUNotifier node for iommu mr=%s"
 smmuv3_inv_notifiers_iova(const char *name, int asid, int vmid, uint64_t iova, uint8_t tg, uint64_t num_pages, int stage) "iommu mr=%s asid=%d vmid=%d iova=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" stage=%d"
+smmuv3_invalidate_all_caches(void) "Invalidate all SMMU caches and TLBs"
 smmu_reset_exit(void) ""
 
 #smmuv3-accel.c
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 22/28] hw/arm/smmuv3: Harden security checks in MMIO handlers
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (21 preceding siblings ...)
  2026-08-13 16:26 ` [RFC v5 21/28] hw/arm/smmuv3: Implement SMMU_S_INIT register Tao Tang
@ 2026-08-13 16:26 ` Tao Tang
  2026-08-13 16:26 ` [RFC v5 24/28] hw/arm/smmuv3: Select sec-sid from PCI property and validate SECURE_IMPL Tao Tang
                   ` (4 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:26 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

This patch hardens the security validation within the main MMIO
dispatcher functions (smmu_read_mmio and smmu_write_mmio).

First, accesses to the Secure register space are gated by whether
SECURE_IMPL is enabled. When it is disabled, all SMMU_S_* registers are
read-as-zero and write-ignored.

Second, the check for the input stream's security is made more robust.
It now validates not only the legacy MemTxAttrs.secure bit, but also
the .space field. This improves compatibility with Arm security space
handling.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
---
 hw/arm/smmuv3.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)

diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index dc3fa618883..e5f0bc18415 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -1605,6 +1605,12 @@ static bool smmu_eventq_irq_cfg_writable(SMMUv3State *s, SMMUSecSID sec_sid)
     return smmu_irq_cfg_writable(s, sec_sid, SMMU_IRQ_EVTQ);
 }
 
+/* Check if the SMMU hardware itself implements secure state features */
+static inline bool smmu_hw_secure_implemented(SMMUv3State *s)
+{
+    return FIELD_EX32(s->bank[SMMU_SEC_SID_S].idr[1], S_IDR1, SECURE_IMPL);
+}
+
 static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp, SMMUSecSID sec_sid)
 {
     SMMUState *bs = ARM_SMMU(s);
@@ -1905,6 +1911,38 @@ static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp, SMMUSecSID sec_sid)
     return 0;
 }
 
+/*
+ * Helper function for Secure register access validation.
+ *
+ * Follow S_IDR1.SECURE_IMPL accessibility rules for SMMU_S_*:
+ *  - SECURE_IMPL == 0: Secure state is not implemented; SMMU_S_* are RAZ/WI to
+ *    all accesses.
+ *  - SECURE_IMPL == 1: Non-secure accesses to SMMU_S_* are RAZ/WI.
+ */
+static bool smmu_check_secure_access(SMMUv3State *s, MemTxAttrs attrs,
+                                     hwaddr offset, bool is_read)
+{
+    /* Check if the access is secure */
+    if (!(attrs.space == ARMSS_Secure ||
+          attrs.secure == 1)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "%s: Non-secure %s attempt at offset 0x%" PRIx64 " (%s)\n",
+            __func__, is_read ? "read" : "write", offset,
+            is_read ? "RAZ" : "WI");
+        return false;
+    }
+
+    /* Check if the secure state is implemented. */
+    if (!smmu_hw_secure_implemented(s)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "%s: Secure %s attempt at offset 0x%" PRIx64 ". But Secure state "
+            "is not implemented (RES0)\n",
+            __func__, is_read ? "read" : "write", offset);
+        return false;
+    }
+    return true;
+}
+
 static MemTxResult smmu_writell(SMMUv3State *s, hwaddr offset,
                                 uint64_t data, MemTxAttrs attrs,
                                 SMMUSecSID reg_sec_sid)
@@ -2272,6 +2310,18 @@ static MemTxResult smmu_write_mmio(void *opaque, hwaddr offset, uint64_t data,
      * translate the Secure window to its bank-local register offsets.
      */
     if (offset >= SMMU_SECURE_REG_START) {
+        if (!smmu_check_secure_access(s, attrs, offset, false)) {
+            trace_smmuv3_write_mmio(offset, data, size, MEMTX_OK);
+            /*
+             * RAZ/WI/RES0 are deterministic register-level behaviors and do not
+             * imply a bus protocol error or abort. Therefore we acknowledge the
+             * MMIO transaction with MEMTX_OK and implement
+             * "Read-As-Zero / Write-Ignored" in the register model, instead of
+             * returning MEMTX_*_ERROR which is reserved for real decode/access
+             * failures.
+             */
+            return MEMTX_OK;
+        }
         reg_sec_sid = SMMU_SEC_SID_S;
         offset -= SMMU_SECURE_REG_START;
     }
@@ -2508,6 +2558,11 @@ static MemTxResult smmu_read_mmio(void *opaque, hwaddr offset, uint64_t *data,
     /* CONSTRAINED UNPREDICTABLE choice to have page0/1 be exact aliases */
     offset &= ~0x10000;
     if (offset >= SMMU_SECURE_REG_START) {
+        if (!smmu_check_secure_access(s, attrs, offset, true)) {
+            *data = 0;
+            trace_smmuv3_read_mmio(offset, *data, size, MEMTX_OK);
+            return MEMTX_OK;
+        }
         reg_sec_sid = SMMU_SEC_SID_S;
         offset -= SMMU_SECURE_REG_START;
     }
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 24/28] hw/arm/smmuv3: Select sec-sid from PCI property and validate SECURE_IMPL
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (22 preceding siblings ...)
  2026-08-13 16:26 ` [RFC v5 22/28] hw/arm/smmuv3: Harden security checks in MMIO handlers Tao Tang
@ 2026-08-13 16:26 ` Tao Tang
  2026-08-13 16:26 ` [RFC v5 25/28] hw/arm/smmuv3: Reject IOMMU notifiers for non-NS devices Tao Tang
                   ` (3 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:26 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Parse each PCI device's sec-sid property during SMMU device initialization
and cache it in SMMUDevice::sec_sid. Support "non-secure" and "secure",
default to non-secure when unspecified, and reject invalid values with an
explicit error. Use sdev->sec_sid in smmuv3_translate() to select the
register bank instead of hardcoding the non-secure context.

Keep sec-sid parsing in smmu-common, and add a SMMUv3-specific validation
hook to enforce architectural constraints: fail fast when sec-sid=secure
while SMMU_S_IDR1.SECURE_IMPL is 0 or secure AS is not available.

Typically, SEC_SID is a system-defined attribute (e.g. sideband or tied-off)
rather than something a PCIe endpoint can freely toggle in pre-RME scenario.
So this PCI sec-sid property is used as a static platform/testing knob to
drive the SMMU bank selection.

For future RME-DA and TDISP support, this static property will need to be
replaced by runtime platform plumbing that derives the effective SEC_SID
from the device security assignment.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
---
 hw/arm/smmu-common.c         | 37 ++++++++++++++++++++++
 hw/arm/smmuv3.c              | 61 +++++++++++++++++++++++++++++++++++-
 include/hw/arm/smmu-common.h |  2 ++
 3 files changed, 99 insertions(+), 1 deletion(-)

diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c
index e8a1ed65c19..4a94799fb0d 100644
--- a/hw/arm/smmu-common.c
+++ b/hw/arm/smmu-common.c
@@ -21,6 +21,7 @@
 #include "exec/target_page.h"
 #include "hw/core/cpu.h"
 #include "hw/pci/pci_bridge.h"
+#include "hw/pci/pci_device.h"
 #include "hw/core/qdev-properties.h"
 #include "qapi/error.h"
 #include "qemu/jhash.h"
@@ -1100,14 +1101,50 @@ SMMUPciBus *smmu_find_smmu_pcibus(SMMUState *s, uint8_t bus_num)
     return NULL;
 }
 
+static SMMUSecSID smmu_parse_pci_sec_sid(PCIDevice *pdev, int bus_num,
+                                         int devfn)
+{
+    const char *sec_sid;
+
+    if (!pdev || !pdev->sec_sid) {
+        return SMMU_SEC_SID_NS;
+    }
+
+    sec_sid = pdev->sec_sid;
+    if (!strcmp(sec_sid, "non-secure")) {
+        return SMMU_SEC_SID_NS;
+    }
+    if (!strcmp(sec_sid, "secure")) {
+        return SMMU_SEC_SID_S;
+    }
+
+    error_report("Invalid sec-sid value '%s' for PCI device %02x:%02x.%x; "
+                 "allowed values: non-secure or secure (case-sensitive)",
+                 sec_sid, bus_num, PCI_SLOT(devfn), PCI_FUNC(devfn));
+    exit(EXIT_FAILURE);
+}
+
 void smmu_init_sdev(SMMUState *s, SMMUDevice *sdev, PCIBus *bus, int devfn)
 {
     static unsigned int index;
     g_autofree char *name = g_strdup_printf("%s-%d-%d", s->mrtypename, devfn,
                                             index++);
+    SMMUBaseClass *sbc = ARM_SMMU_GET_CLASS(s);
+    PCIDevice *pdev;
+    int bus_num;
+
     sdev->smmu = s;
     sdev->bus = bus;
     sdev->devfn = devfn;
+    sdev->sec_sid = SMMU_SEC_SID_NS;
+
+    bus_num = pci_bus_num(bus);
+    pdev = pci_find_device(bus, bus_num, devfn);
+    sdev->sec_sid = smmu_parse_pci_sec_sid(pdev, bus_num, devfn);
+    if (sbc->validate_sec_sid &&
+        !sbc->validate_sec_sid(s, sdev, bus_num)) {
+        exit(EXIT_FAILURE);
+    }
 
     memory_region_init_iommu(&sdev->iommu, sizeof(sdev->iommu),
                              s->mrtypename, OBJECT(s), name, UINT64_MAX);
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index e5f0bc18415..a755f1ebd69 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -43,6 +43,12 @@
                                         ((ptw_info).stage == SMMU_STAGE_2 && \
                                         (cfg)->s2cfg.record_faults))
 
+enum {
+    SMMU_IOMMU_IDX_NS,
+    SMMU_IOMMU_IDX_S,
+    SMMU_IOMMU_IDX_NUM,
+};
+
 /**
  * smmuv3_trigger_irq - pulse @irq if enabled and update
  * GERROR register in case of GERROR interrupt
@@ -1157,6 +1163,33 @@ static void smmuv3_fixup_event(SMMUEventInfo *event, hwaddr iova)
     }
 }
 
+static int smmuv3_attrs_to_index(IOMMUMemoryRegion *iommu, MemTxAttrs attrs)
+{
+    if (attrs.unspecified) {
+        return SMMU_IOMMU_IDX_NS;
+    }
+    return attrs.secure ? SMMU_IOMMU_IDX_S : SMMU_IOMMU_IDX_NS;
+}
+
+static int smmuv3_num_indexes(IOMMUMemoryRegion *iommu)
+{
+    return SMMU_IOMMU_IDX_NUM;
+}
+
+static AddressSpace *smmuv3_bypass_target_as(SMMUv3State *s,
+                                             SMMUSecSID sec_sid,
+                                             int iommu_idx)
+{
+    g_assert(iommu_idx >= SMMU_IOMMU_IDX_NS &&
+             iommu_idx < SMMU_IOMMU_IDX_NUM);
+
+    if (smmu_sec_sid_is_secure(sec_sid) &&
+        iommu_idx == SMMU_IOMMU_IDX_S) {
+        return smmu_get_address_space(&s->smmu_state, SMMU_SEC_SID_S);
+    }
+    return smmu_get_address_space(&s->smmu_state, SMMU_SEC_SID_NS);
+}
+
 /* Entry point to SMMU, does everything. */
 static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
                                       IOMMUAccessFlags flag, int iommu_idx)
@@ -1164,7 +1197,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
     SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu);
     SMMUv3State *s = sdev->smmu;
     uint32_t sid = smmu_get_sid(sdev);
-    SMMUSecSID sec_sid = SMMU_SEC_SID_NS;
+    SMMUSecSID sec_sid = sdev->sec_sid;
     SMMUv3RegBank *bank = smmuv3_bank(s, sec_sid);
     SMMUEventInfo event = {.type = SMMU_EVT_NONE,
                            .sid = sid,
@@ -1181,6 +1214,8 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
     };
     SMMUTLBEntry *cached_entry = NULL;
 
+    entry.target_as = smmuv3_bypass_target_as(s, sec_sid, iommu_idx);
+
     qemu_mutex_lock(&s->mutex);
 
     if (!smmu_enabled(s, sec_sid)) {
@@ -1611,6 +1646,26 @@ static inline bool smmu_hw_secure_implemented(SMMUv3State *s)
     return FIELD_EX32(s->bank[SMMU_SEC_SID_S].idr[1], S_IDR1, SECURE_IMPL);
 }
 
+static bool smmuv3_validate_sec_sid(SMMUState *bs, SMMUDevice *sdev,
+                                    int bus_num)
+{
+    SMMUv3State *s = ARM_SMMUV3(bs);
+
+    if (sdev->sec_sid != SMMU_SEC_SID_S) {
+        return true;
+    }
+
+    if (!smmu_hw_secure_implemented(s)) {
+        error_report("Invalid sec-sid value 'secure' for PCI device "
+                     "%02x:%02x.%x: S_IDR1.SECURE_IMPL is 0, so only "
+                     "non-secure is allowed",
+                     bus_num, PCI_SLOT(sdev->devfn), PCI_FUNC(sdev->devfn));
+        return false;
+    }
+
+    return true;
+}
+
 static int smmuv3_cmdq_consume(SMMUv3State *s, Error **errp, SMMUSecSID sec_sid)
 {
     SMMUState *bs = ARM_SMMU(s);
@@ -2823,6 +2878,7 @@ static void smmuv3_class_init(ObjectClass *klass, const void *data)
     DeviceClass *dc = DEVICE_CLASS(klass);
     ResettableClass *rc = RESETTABLE_CLASS(klass);
     SMMUv3Class *c = ARM_SMMUV3_CLASS(klass);
+    SMMUBaseClass *sbc = ARM_SMMU_CLASS(klass);
 
     dc->vmsd = &vmstate_smmuv3;
     resettable_class_set_parent_phases(rc, NULL, NULL, smmu_reset_exit,
@@ -2832,6 +2888,7 @@ static void smmuv3_class_init(ObjectClass *klass, const void *data)
     device_class_set_props(dc, smmuv3_properties);
     dc->hotpluggable = false;
     dc->user_creatable = true;
+    sbc->validate_sec_sid = smmuv3_validate_sec_sid;
 
     object_class_property_set_description(klass, "accel",
         "Enable SMMUv3 accelerator support. Allows host SMMUv3 to be "
@@ -2905,6 +2962,8 @@ static void smmuv3_iommu_memory_region_class_init(ObjectClass *klass,
 
     imrc->translate = smmuv3_translate;
     imrc->notify_flag_changed = smmuv3_notify_flag_changed;
+    imrc->attrs_to_index = smmuv3_attrs_to_index;
+    imrc->num_indexes = smmuv3_num_indexes;
 }
 
 static const TypeInfo smmuv3_type_info = {
diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h
index a21c6061808..9da54221f20 100644
--- a/include/hw/arm/smmu-common.h
+++ b/include/hw/arm/smmu-common.h
@@ -134,6 +134,7 @@ typedef struct SMMUDevice {
     void               *smmu;
     PCIBus             *bus;
     int                devfn;
+    SMMUSecSID         sec_sid;
     IOMMUMemoryRegion  iommu;
     AddressSpace       as;
     uint32_t           cfg_cache_hits;
@@ -204,6 +205,7 @@ struct SMMUBaseClass {
     /*< public >*/
 
     DeviceRealize parent_realize;
+    bool (*validate_sec_sid)(struct SMMUState *s, SMMUDevice *sdev, int bus_num);
 
 };
 
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 25/28] hw/arm/smmuv3: Reject IOMMU notifiers for non-NS devices
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (23 preceding siblings ...)
  2026-08-13 16:26 ` [RFC v5 24/28] hw/arm/smmuv3: Select sec-sid from PCI property and validate SECURE_IMPL Tao Tang
@ 2026-08-13 16:26 ` Tao Tang
  2026-08-13 16:26 ` [RFC v5 26/28] hw/arm/smmuv3: Initialize the secure register bank Tao Tang
                   ` (2 subsequent siblings)
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:26 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

VFIO and vhost IOMMU notifiers currently support only the Non-secure
system-memory path. Reject notifier registration for non-NS devices
instead of allowing an unsupported configuration.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
---
 hw/arm/smmuv3.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index a755f1ebd69..2375b7a17a0 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -2932,6 +2932,13 @@ static int smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu,
     SMMUv3State *s3 = sdev->smmu;
     SMMUState *s = &(s3->smmu_state);
 
+    if (new != IOMMU_NOTIFIER_NONE && sdev->sec_sid != SMMU_SEC_SID_NS) {
+        error_setg(errp,
+                   "SMMUv3 does not support IOMMU notifiers for "
+                   "secure devices");
+        return -EINVAL;
+    }
+
     if (new & IOMMU_NOTIFIER_DEVIOTLB_UNMAP) {
         error_setg(errp, "SMMUv3 does not support dev-iotlb yet");
         return -EINVAL;
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 26/28] hw/arm/smmuv3: Initialize the secure register bank
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (24 preceding siblings ...)
  2026-08-13 16:26 ` [RFC v5 25/28] hw/arm/smmuv3: Reject IOMMU notifiers for non-NS devices Tao Tang
@ 2026-08-13 16:26 ` Tao Tang
  2026-08-13 16:26 ` [RFC v5 27/28] hw/arm/smmuv3: Add secure bank migration and secure-impl property Tao Tang
  2026-08-13 16:26 ` [RFC v5 28/28] [NOT-MERGE] hw/arm/smmuv3: temporarily enable SEL2 bit and some other features Tao Tang
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:26 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Initialize the secure register bank (SMMU_SEC_SID_S) with sane default
values during the SMMU's reset sequence.

This change ensures that key fields, such as the secure ID registers,
GBPA reset value, and queue entry sizes, are set to a known-good state.
The SECURE_IMPL attribute of the S_IDR1 register will be introduced
later via device properties.

This is a necessary step to prevent undefined behavior when secure SMMU
features are subsequently enabled and used by software.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
---
 hw/arm/smmuv3.c | 29 ++++++++++++++++++++++++++++-
 1 file changed, 28 insertions(+), 1 deletion(-)

diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index 2375b7a17a0..7e7376c65e2 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -293,6 +293,7 @@ void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *info)
 static void smmuv3_init_id_regs(SMMUv3State *s)
 {
     SMMUv3RegBank *bank = smmuv3_bank(s, SMMU_SEC_SID_NS);
+    SMMUv3RegBank *sbank = smmuv3_bank(s, SMMU_SEC_SID_S);
 
     /* Based on sys property, the stages supported in smmu will be advertised.*/
     if (s->stage && !strcmp("2", s->stage)) {
@@ -339,7 +340,14 @@ static void smmuv3_init_id_regs(SMMUv3State *s)
     bank->idr[5] = FIELD_DP32(bank->idr[5], IDR5, GRAN4K, 1);
     bank->idr[5] = FIELD_DP32(bank->idr[5], IDR5, GRAN16K, 1);
     bank->idr[5] = FIELD_DP32(bank->idr[5], IDR5, GRAN64K, 1);
-    s->aidr = 0x1;
+
+    /* Initialize Secure bank */
+    memset(sbank->idr, 0, sizeof(sbank->idr));
+    /* No stall */
+    sbank->idr[0] = FIELD_DP32(sbank->idr[0], S_IDR0, STALL_MODEL, 1);
+
+    sbank->idr[1] = FIELD_DP32(sbank->idr[1], S_IDR1, S_SIDSIZE,
+                               SMMU_IDR1_SIDSIZE);
     smmuv3_accel_idr_override(s);
 }
 
@@ -352,6 +360,7 @@ bool smmuv3_ats_enabled(SMMUv3State *s)
 static void smmuv3_reset(SMMUv3State *s)
 {
     SMMUv3RegBank *bank = smmuv3_bank(s, SMMU_SEC_SID_NS);
+    SMMUv3RegBank *sbank = smmuv3_bank(s, SMMU_SEC_SID_S);
 
     bank->cmdq.base = deposit64(bank->cmdq.base, 0, 5, SMMU_CMDQS);
     bank->cmdq.prod = 0;
@@ -371,6 +380,24 @@ static void smmuv3_reset(SMMUv3State *s)
     bank->gerrorn = 0;
     bank->gbpa = SMMU_GBPA_RESET_VAL;
 
+    sbank->cmdq.base = deposit64(sbank->cmdq.base, 0, 5, SMMU_CMDQS);
+    sbank->cmdq.prod = 0;
+    sbank->cmdq.cons = 0;
+    sbank->cmdq.entry_size = sizeof(struct Cmd);
+    sbank->eventq.base = deposit64(sbank->eventq.base, 0, 5, SMMU_EVENTQS);
+    sbank->eventq.prod = 0;
+    sbank->eventq.cons = 0;
+    sbank->eventq.entry_size = sizeof(struct Evt);
+
+    sbank->features = 0;
+    sbank->sid_split = 0;
+    sbank->cr[0] = 0;
+    sbank->cr0ack = 0;
+    sbank->irq_ctrl = 0;
+    sbank->gerror = 0;
+    sbank->gerrorn = 0;
+    sbank->gbpa = SMMU_GBPA_RESET_VAL;
+
     s->aidr = 0x1;
     s->statusr = 0;
 }
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 27/28] hw/arm/smmuv3: Add secure bank migration and secure-impl property
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (25 preceding siblings ...)
  2026-08-13 16:26 ` [RFC v5 26/28] hw/arm/smmuv3: Initialize the secure register bank Tao Tang
@ 2026-08-13 16:26 ` Tao Tang
  2026-08-13 16:26 ` [RFC v5 28/28] [NOT-MERGE] hw/arm/smmuv3: temporarily enable SEL2 bit and some other features Tao Tang
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:26 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Add a secure-impl device property and advertise it through
S_IDR1.SECURE_IMPL. Reject secure-impl=on unless Secure memory is available
and stage-1 translation is supported.

Usage:
    -M virt,secure=on,iommu=smmuv3 \
    -global arm-smmuv3.secure-impl=on

Add the smmuv3/bank_s migration subsection for the secure register bank.
Serialize secure bank state including GBPA, IRQ config, stream table and
queue state.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
---
 hw/arm/smmuv3.c         | 93 +++++++++++++++++++++++++++++++++++++++++
 include/hw/arm/smmuv3.h |  2 +
 2 files changed, 95 insertions(+)

diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index 7e7376c65e2..8e1de94fd17 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -348,6 +348,8 @@ static void smmuv3_init_id_regs(SMMUv3State *s)
 
     sbank->idr[1] = FIELD_DP32(sbank->idr[1], S_IDR1, S_SIDSIZE,
                                SMMU_IDR1_SIDSIZE);
+    sbank->idr[1] = FIELD_DP32(sbank->idr[1], S_IDR1, SECURE_IMPL,
+                               s->secure_impl == ON_OFF_AUTO_ON);
     smmuv3_accel_idr_override(s);
 }
 
@@ -2752,6 +2754,37 @@ static bool smmu_validate_property(SMMUv3State *s, Error **errp)
     return true;
 }
 
+static bool smmuv3_resolve_secure_impl(SMMUv3State *s, Error **errp)
+{
+    SMMUState *bs = ARM_SMMU(s);
+    bool secure_as_available = bs->secure_memory &&
+                               bs->secure_memory_as.root != NULL;
+
+    if (s->secure_impl == ON_OFF_AUTO_AUTO) {
+        s->secure_impl = secure_as_available ? ON_OFF_AUTO_ON
+                                             : ON_OFF_AUTO_OFF;
+    }
+
+    if (s->secure_impl == ON_OFF_AUTO_ON && !secure_as_available) {
+        error_setg(errp,
+                   "secure-impl=on requires a secure-memory address space");
+        return false;
+    }
+
+    /*
+     * When SECURE_IMPL == 1, stage 1 must be supported according to
+     * (IHI 0070G.b) 6.3.53 SMMU_S_IDR1, Page 442.
+     */
+    if (s->secure_impl == ON_OFF_AUTO_ON &&
+        s->stage && !strcmp(s->stage, "2")) {
+        error_setg(errp,
+                    "secure-impl=on requires stage=1 or stage=nested");
+        return false;
+    }
+
+    return true;
+}
+
 static void smmu_realize(DeviceState *d, Error **errp)
 {
     SMMUState *sys = ARM_SMMU(d);
@@ -2781,6 +2814,10 @@ static void smmu_realize(DeviceState *d, Error **errp)
         return;
     }
 
+    if (!smmuv3_resolve_secure_impl(s, errp)) {
+        return;
+    }
+
     qemu_mutex_init(&s->mutex);
 
     memory_region_init_io(&sys->iomem, OBJECT(s),
@@ -2807,6 +2844,54 @@ static const VMStateDescription vmstate_smmuv3_queue = {
     },
 };
 
+static const VMStateDescription vmstate_smmuv3_secure_bank = {
+    .name = "smmuv3_secure_bank",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (const VMStateField[]) {
+        VMSTATE_UINT32(features, SMMUv3RegBank),
+        VMSTATE_UINT8(sid_split, SMMUv3RegBank),
+        VMSTATE_UINT32_ARRAY(cr, SMMUv3RegBank, 3),
+        VMSTATE_UINT32(cr0ack, SMMUv3RegBank),
+        VMSTATE_UINT32(gbpa, SMMUv3RegBank),
+        VMSTATE_UINT32(irq_ctrl, SMMUv3RegBank),
+        VMSTATE_UINT32(gerror, SMMUv3RegBank),
+        VMSTATE_UINT32(gerrorn, SMMUv3RegBank),
+        VMSTATE_UINT64(gerror_irq_cfg0, SMMUv3RegBank),
+        VMSTATE_UINT32(gerror_irq_cfg1, SMMUv3RegBank),
+        VMSTATE_UINT32(gerror_irq_cfg2, SMMUv3RegBank),
+        VMSTATE_UINT64(strtab_base, SMMUv3RegBank),
+        VMSTATE_UINT32(strtab_base_cfg, SMMUv3RegBank),
+        VMSTATE_UINT64(eventq_irq_cfg0, SMMUv3RegBank),
+        VMSTATE_UINT32(eventq_irq_cfg1, SMMUv3RegBank),
+        VMSTATE_UINT32(eventq_irq_cfg2, SMMUv3RegBank),
+        VMSTATE_STRUCT(cmdq, SMMUv3RegBank, 0,
+                       vmstate_smmuv3_queue, SMMUQueue),
+        VMSTATE_STRUCT(eventq, SMMUv3RegBank, 0,
+                       vmstate_smmuv3_queue, SMMUQueue),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
+static bool smmuv3_secure_bank_needed(void *opaque)
+{
+    SMMUv3State *s = opaque;
+
+    return s->secure_impl == ON_OFF_AUTO_ON;
+}
+
+static const VMStateDescription vmstate_smmuv3_bank_s = {
+    .name = "smmuv3/bank_s",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = smmuv3_secure_bank_needed,
+    .fields = (const VMStateField[]) {
+        VMSTATE_STRUCT(bank[SMMU_SEC_SID_S], SMMUv3State, 0,
+                       vmstate_smmuv3_secure_bank, SMMUv3RegBank),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
 static bool smmuv3_gbpa_needed(void *opaque)
 {
     SMMUv3State *s = opaque;
@@ -2861,6 +2946,7 @@ static const VMStateDescription vmstate_smmuv3 = {
     },
     .subsections = (const VMStateDescription * const []) {
         &vmstate_gbpa,
+        &vmstate_smmuv3_bank_s,
         NULL
     }
 };
@@ -2893,6 +2979,13 @@ static const Property smmuv3_properties[] = {
     DEFINE_PROP_SSIDSIZE_MODE("ssidsize", SMMUv3State, ssidsize,
                               SSID_SIZE_MODE_AUTO),
     DEFINE_PROP_ON_OFF_AUTO("cmdqv", SMMUv3State, cmdqv, ON_OFF_AUTO_AUTO),
+    /*
+     * SECURE_IMPL field in S_IDR1 register.
+     * Indicates whether secure state is implemented.
+     * Defaults to auto.
+     */
+    DEFINE_PROP_ON_OFF_AUTO("secure-impl", SMMUv3State, secure_impl,
+                            ON_OFF_AUTO_AUTO),
 };
 
 static void smmuv3_instance_init(Object *obj)
diff --git a/include/hw/arm/smmuv3.h b/include/hw/arm/smmuv3.h
index 8cd2edc3412..9b533b9a99b 100644
--- a/include/hw/arm/smmuv3.h
+++ b/include/hw/arm/smmuv3.h
@@ -81,6 +81,8 @@ struct SMMUv3State {
     OnOffAuto ats;
     OasMode oas;
     SsidSizeMode ssidsize;
+    OnOffAuto secure_impl;
+
     /* SMMU CMDQV extension */
     OnOffAuto cmdqv;
 
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFC v5 28/28] [NOT-MERGE] hw/arm/smmuv3: temporarily enable SEL2 bit and some other features
  2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
                   ` (26 preceding siblings ...)
  2026-08-13 16:26 ` [RFC v5 27/28] hw/arm/smmuv3: Add secure bank migration and secure-impl property Tao Tang
@ 2026-08-13 16:26 ` Tao Tang
  27 siblings, 0 replies; 30+ messages in thread
From: Tao Tang @ 2026-08-13 16:26 UTC (permalink / raw)
  To: Eric Auger, Peter Maydell
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Mostafa Saleh, Chao Liu,
	Jim MacArthur, Tao Tang

Temporarily:
- enable S_IDR1.SEL2,
- change AIDR to 0x2 to indicate SMMUv3.2,
- use smaller SID size and Cmd/Evt queue size.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
---
 hw/arm/smmuv3.c                | 3 ++-
 include/hw/arm/smmuv3-common.h | 6 +++---
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index 8e1de94fd17..19e080b6d25 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -350,6 +350,7 @@ static void smmuv3_init_id_regs(SMMUv3State *s)
                                SMMU_IDR1_SIDSIZE);
     sbank->idr[1] = FIELD_DP32(sbank->idr[1], S_IDR1, SECURE_IMPL,
                                s->secure_impl == ON_OFF_AUTO_ON);
+    sbank->idr[1] = FIELD_DP32(sbank->idr[1], S_IDR1, SEL2, 1);
     smmuv3_accel_idr_override(s);
 }
 
@@ -400,7 +401,7 @@ static void smmuv3_reset(SMMUv3State *s)
     sbank->gerrorn = 0;
     sbank->gbpa = SMMU_GBPA_RESET_VAL;
 
-    s->aidr = 0x1;
+    s->aidr = 0x2; /* SMMUv3.2 */
     s->statusr = 0;
 }
 
diff --git a/include/hw/arm/smmuv3-common.h b/include/hw/arm/smmuv3-common.h
index ec442a85586..1afaece3df7 100644
--- a/include/hw/arm/smmuv3-common.h
+++ b/include/hw/arm/smmuv3-common.h
@@ -311,9 +311,9 @@ REG32(IDR1,                0x4)
     FIELD(IDR1, TABLES_PRESET, 30, 1)
     FIELD(IDR1, ECMDQ,        31, 1)
 
-#define SMMU_IDR1_SIDSIZE 16
-#define SMMU_CMDQS   19
-#define SMMU_EVENTQS 19
+#define SMMU_IDR1_SIDSIZE 6
+#define SMMU_CMDQS   10
+#define SMMU_EVENTQS 10
 
 REG32(IDR2,                0x8)
      FIELD(IDR2, BA_VATOS, 0, 10)
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* Re: [RFC v5 01/28] hw/arm/smmuv3: Introduce secure registers
  2026-08-13 16:24 ` [RFC v5 01/28] hw/arm/smmuv3: Introduce secure registers Tao Tang
@ 2026-08-18 14:21   ` Jim MacArthur
  0 siblings, 0 replies; 30+ messages in thread
From: Jim MacArthur @ 2026-08-18 14:21 UTC (permalink / raw)
  To: Tao Tang
  Cc: Eric Auger, Peter Maydell, qemu-devel, qemu-arm, Chen Baozi,
	Pierrick Bouvier, Philippe Mathieu-Daudé, Mostafa Saleh,
	Chao Liu

On Fri, Aug 14, 2026 at 12:24:06AM +0800, Tao Tang wrote:
> The Arm SMMUv3 architecture defines a set of registers for managing
> Secure transactions and contexts.
> 
> This patch introduces the definitions for these secure registers within
> the SMMUv3 device model internal header.
> 
> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
> Reviewed-by: Eric Auger <eric.auger@redhat.com>
> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
> Reviewed-by: Mostafa Saleh <smostafa@google.com>
> Link: https://lore.kernel.org/qemu-devel/759ec110-365f-488b-802d-c7bb1efe30bc@redhat.com/
> ---
>  include/hw/arm/smmuv3-common.h | 71 +++++++++++++++++++++++++++++++++-
>  1 file changed, 70 insertions(+), 1 deletion(-)
> 
> diff --git a/include/hw/arm/smmuv3-common.h b/include/hw/arm/smmuv3-common.h
> index 4609975edf9..ec442a85586 100644
> --- a/include/hw/arm/smmuv3-common.h
> +++ b/include/hw/arm/smmuv3-common.h
> @@ -269,7 +269,7 @@ REG32(CD_5, 20)
>      ((sel) ? ((cd)->word[4] = FIELD_DP32((cd)->word[4], CD_4, NSCFG1, (v))) : \
>               ((cd)->word[2] = FIELD_DP32((cd)->word[2], CD_2, NSCFG0, (v))))
>  
> -/* MMIO Registers */
> +/* MMIO Registers. Shared by Non-secure/Realm/Root states. */
>  
>  REG32(IDR0,                0x0)
>      FIELD(IDR0, S2P,         0 , 1)
> @@ -353,6 +353,7 @@ REG32(CR0,                 0x20)
>      FIELD(CR0, CMDQEN,        3, 1)
>  
>  #define SMMU_CR0_RESERVED 0xFFFFFA20
> +#define SMMU_S_CR0_RESERVED 0xFFFFFC12
>  
>  REG32(CR0ACK,              0x24)
>  REG32(CR1,                 0x28)
> @@ -411,6 +412,74 @@ REG32(EVENTQ_IRQ_CFG2,     0xbc)
>  
>  #define A_IDREGS           0xfd0
>  
> +#define SMMU_SECURE_REG_START 0x8000 /* Start of secure-only registers */
> +
> +REG32(S_IDR0,               0x8000)
> +    FIELD(S_IDR0, MSI,         13, 1)
> +    FIELD(S_IDR0, STALL_MODEL, 24, 2)
> +REG32(S_IDR1,               0x8004)
> +    FIELD(S_IDR1, S_SIDSIZE,          0 , 6)
> +    FIELD(S_IDR1, SEL2,               29, 1)
> +    FIELD(S_IDR1, SECURE_IMPL,        31, 1)
> +
> +REG32(S_IDR2,               0x8008)
> +REG32(S_IDR3,               0x800c)
> +REG32(S_IDR4,               0x8010)
> +
> +REG32(S_CR0,                0x8020)
> +    FIELD(S_CR0, SMMUEN,      0, 1)
> +    FIELD(S_CR0, EVENTQEN,    2, 1)
> +    FIELD(S_CR0, CMDQEN,      3, 1)
> +
> +REG32(S_CR0ACK,             0x8024)
> +REG32(S_CR1,                0x8028)
> +REG32(S_CR2,                0x802c)
> +
> +REG32(S_INIT,               0x803c)
> +    FIELD(S_INIT, INV_ALL,    0, 1)
> +
> +REG32(S_GBPA,               0x8044)
> +    FIELD(S_GBPA, ABORT,     20, 1)
> +    FIELD(S_GBPA, UPDATE,    31, 1)
> +
> +REG32(S_IRQ_CTRL,           0x8050)
> +    FIELD(S_IRQ_CTRL, GERROR_IRQEN,    0, 1)
> +    FIELD(S_IRQ_CTRL, EVENTQ_IRQEN,    2, 1)
> +
> +REG32(S_IRQ_CTRLACK,        0x8054)
> +
> +REG32(S_GERROR,             0x8060)
> +    FIELD(S_GERROR, CMDQ_ERR,          0, 1)
> +
> +#define SMMU_GERROR_IRQ_CFG0_RESERVED   0x00FFFFFFFFFFFFFC
> +#define SMMU_GERROR_IRQ_CFG2_RESERVED   0x000000000000003F
> +
> +#define SMMU_STRTAB_BASE_RESERVED       0x40FFFFFFFFFFFFC0
> +#define SMMU_QUEUE_BASE_RESERVED        0x40FFFFFFFFFFFFFF
> +#define SMMU_EVENTQ_IRQ_CFG0_RESERVED   0x00FFFFFFFFFFFFFC
> +
> +REG32(S_GERRORN,            0x8064)
> +REG64(S_GERROR_IRQ_CFG0,    0x8068)
> +REG32(S_GERROR_IRQ_CFG1,    0x8070)
> +REG32(S_GERROR_IRQ_CFG2,    0x8074)
> +REG64(S_STRTAB_BASE,        0x8080)
> +REG32(S_STRTAB_BASE_CFG,    0x8088)
> +    FIELD(S_STRTAB_BASE_CFG, LOG2SIZE, 0, 6)
> +    FIELD(S_STRTAB_BASE_CFG, SPLIT,    6, 5)
> +    FIELD(S_STRTAB_BASE_CFG, FMT,     16, 2)
> +
> +REG64(S_CMDQ_BASE,          0x8090)
> +REG32(S_CMDQ_PROD,          0x8098)
> +REG32(S_CMDQ_CONS,          0x809c)
> +    FIELD(S_CMDQ_CONS, ERR,           24, 7)
> +
> +REG64(S_EVENTQ_BASE,        0x80a0)
> +REG32(S_EVENTQ_PROD,        0x80a8)
> +REG32(S_EVENTQ_CONS,        0x80ac)
> +REG64(S_EVENTQ_IRQ_CFG0,    0x80b0)
> +REG32(S_EVENTQ_IRQ_CFG1,    0x80b8)
> +REG32(S_EVENTQ_IRQ_CFG2,    0x80bc)
> +
>  /* Commands */
>  
>  typedef enum SMMUCommandType {
> -- 
> 2.34.1

Reviewed-by: Jim MacArthur <jim.macarthur@linaro.org>



^ permalink raw reply	[flat|nested] 30+ messages in thread

end of thread, other threads:[~2026-08-18 14:21 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-13 16:15 [RFC v5 00/28] hw/arm/smmuv3: Support Secure state for SMMUv3 Tao Tang
2026-08-13 16:21 ` [RFC v5 23/28] hw/pci: Add sec-sid property to PCIDevice Tao Tang
2026-08-13 16:24 ` [RFC v5 01/28] hw/arm/smmuv3: Introduce secure registers Tao Tang
2026-08-18 14:21   ` Jim MacArthur
2026-08-13 16:24 ` [RFC v5 02/28] hw/arm/smmuv3: Introduce banked registers for SMMUv3 state Tao Tang
2026-08-13 16:24 ` [RFC v5 03/28] hw/arm/smmuv3: Thread SEC_SID through helper APIs Tao Tang
2026-08-13 16:24 ` [RFC v5 04/28] hw/arm/smmuv3: Track SEC_SID in configs and events Tao Tang
2026-08-13 16:24 ` [RFC v5 05/28] hw/arm/smmu-common: Add security-aware address space selector Tao Tang
2026-08-13 16:24 ` [RFC v5 06/28] hw/arm/smmuv3: Plumb transaction attributes into config helpers Tao Tang
2026-08-13 16:24 ` [RFC v5 07/28] hw/arm/smmuv3: Reject secure STEs with stage-2 enabled Tao Tang
2026-08-13 16:24 ` [RFC v5 08/28] hw/arm/smmu-common: Key configuration cache on SMMUDevice and SEC_SID Tao Tang
2026-08-13 16:24 ` [RFC v5 09/28] hw/arm/smmu: Add PTE NS/NSTable helpers Tao Tang
2026-08-13 16:24 ` [RFC v5 10/28] hw/arm/smmuv3: Store CD NSCFG in TT info Tao Tang
2026-08-13 16:25 ` [RFC v5 11/28] hw/arm/smmu-common: Implement secure state handling in ptw Tao Tang
2026-08-13 16:25 ` [RFC v5 12/28] hw/arm/smmuv3: Tag IOTLB cache keys with SEC_SID Tao Tang
2026-08-13 16:25 ` [RFC v5 13/28] hw/arm/smmuv3: Pass sec_sid into cmdq consume path Tao Tang
2026-08-13 16:25 ` [RFC v5 14/28] hw/arm/smmuv3: Make evtq producer use SEC_SID Tao Tang
2026-08-13 16:25 ` [RFC v5 15/28] hw/arm/smmu: Make CMDQ invalidation security-state aware Tao Tang
2026-08-13 16:25 ` [RFC v5 16/28] hw/arm/smmuv3: Add access checks for GERROR_IRQ_CFG registers Tao Tang
2026-08-13 16:25 ` [RFC v5 17/28] hw/arm/smmuv3: Add access checks for STRTAB_BASE and CR2 registers Tao Tang
2026-08-13 16:25 ` [RFC v5 18/28] hw/arm/smmuv3: Add access checks for CMDQ and EVENTQ registers Tao Tang
2026-08-13 16:25 ` [RFC v5 19/28] hw/arm/smmuv3: Determine register bank from MMIO offset Tao Tang
2026-08-13 16:25 ` [RFC v5 20/28] hw/arm/smmuv3: Route IRQ and GERROR handling by SEC_SID Tao Tang
2026-08-13 16:26 ` [RFC v5 21/28] hw/arm/smmuv3: Implement SMMU_S_INIT register Tao Tang
2026-08-13 16:26 ` [RFC v5 22/28] hw/arm/smmuv3: Harden security checks in MMIO handlers Tao Tang
2026-08-13 16:26 ` [RFC v5 24/28] hw/arm/smmuv3: Select sec-sid from PCI property and validate SECURE_IMPL Tao Tang
2026-08-13 16:26 ` [RFC v5 25/28] hw/arm/smmuv3: Reject IOMMU notifiers for non-NS devices Tao Tang
2026-08-13 16:26 ` [RFC v5 26/28] hw/arm/smmuv3: Initialize the secure register bank Tao Tang
2026-08-13 16:26 ` [RFC v5 27/28] hw/arm/smmuv3: Add secure bank migration and secure-impl property Tao Tang
2026-08-13 16:26 ` [RFC v5 28/28] [NOT-MERGE] hw/arm/smmuv3: temporarily enable SEL2 bit and some other features Tao Tang

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.