All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v13 1/6] xen/domctl: chain SCI handling before IOMMU in assign_device domctl
  2026-09-22 14:40 [PATCH v13 0/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent support Oleksii Moisieiev
  2026-09-22 14:40 ` [PATCH v13 2/6] xen: arm: smccc: add INVALID_PARAMETER error code Oleksii Moisieiev
@ 2026-09-22 14:40 ` Oleksii Moisieiev
  2026-09-22 14:40 ` [PATCH v13 4/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver Oleksii Moisieiev
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 14+ messages in thread
From: Oleksii Moisieiev @ 2026-09-22 14:40 UTC (permalink / raw)
  To: xen-devel@lists.xenproject.org
  Cc: Andrew Cooper, Anthony PERARD, Bertrand Marquis, Jan Beulich,
	Juergen Gross, Julien Grall, Michal Orzel, Oleksii Moisieiev,
	Roger Pau Monné, Stefano Stabellini, Volodymyr Babchuk,
	Grygorii Strashko

From: Grygorii Strashko <grygorii_strashko@epam.com>

Add chained handling of assigned DT devices to support access-controller
functionality through SCI framework, so a DT device assign request can be
passed to firmware for processing and enabling VM access to the requested
device (for example, device power management through SCMI).

The SCI access-controller DT device processing is called before the IOMMU
path. It runs for any DT-described device (protected or not, and even when
the IOMMU is disabled). The IOMMU path remains unchanged for PCI devices;
only the DT path is relaxed to permit non-IOMMU devices.

This lets xl.cfg:"dtdev" list both IOMMU-protected and non-protected DT
devices:

dtdev = [
    "/soc/video@e6ef0000", <- IOMMU protected device
    "/soc/i2c@e6508000", <- not IOMMU protected device
]

The change is done in two parts:
1) call sci_do_domctl() in do_domctl() before IOMMU processing. If
sci_do_domctl() reports an error other than -ENXIO, treat it as
authoritative and skip the IOMMU path. A return of -ENXIO indicates
that SCI did not handle the request and is ignored, allowing the
existing IOMMU handling to run unchanged;
2) update iommu_do_dt_domctl() to check for dt_device_is_protected() and
not fail if DT device is not protected by IOMMU. iommu_do_pci_domctl
doesn't need to be updated because iommu_do_domctl first tries
iommu_do_pci_domctl (when CONFIG_HAS_PCI) and falls back to
iommu_do_dt_domctl only if PCI returns -ENODEV.

The new dt_device_is_protected() bypass in iommu_do_dt_domctl only
applies to DT-described devices; SCI parameters are carried via DT
nodes. PCI devices handled by iommu_do_pci_domctl do not carry DT/SCI
metadata in this path, so there is no notion of “SCI parameters on a
non-IOMMU-protected PCI device” for it to interpret or to skip. The PCI
path should continue to report errors if assignment cannot be performed
by the IOMMU layer. So we should leave iommu_do_pci_domctl unchanged; the
SCI/DT-specific relaxations belong only in the DT path. Also SCI handling
only exists when DT is present.

Signed-off-by: Grygorii Strashko <grygorii_strashko@epam.com>
Signed-off-by: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
Acked-by: Jan Beulich <jbeulich@suse.com>
---

(no changes since v12)

Changes in v12:
- rebase to the latest staging
- add R-Bs from Stefano and Ack from Jan

Changes in v10:
- remove unused sci_do_domctl stub from sci.h

Changes in v9:
- treat SCI as a gate for XEN_DOMCTL_*assign_device: abort before
IOMMU if sci_do_domctl() returns an error other than -ENXIO, instead
of trying to propagate SCI errors after a successful IOMMU
operation. This avoids partial success and the need for IOMMU rollback.
- remove early return from do_domctl() in the assign_device
path to keep RCU handling intact.
- change IS_ENABLED(*) to #ifdef in sci_do_domctl quard

Changes in v8:
- check for CONFIG_ARM_SCI to be ebabled instead of COMFIG_ARM before
calling sci_do_domctl
- rework sci_do_domctl call to avoid extra checks, improved error
handling.
- do not propagate ret1 if sci_do_domctl returned positive ret
- updated comment in domctl.c code

Changes in v7:
- update domctl to build on both Arm and x86 platforms
- move ret1 declaration to the top of the function as required by code
style

Changes in v6:
- change iommu_do_domctl and sci_do_domctl command order and
call sci_do_domctl first which will produce cleaner code path.
Also dropped changing return code when iommu was disabled in
iommu_do_domctl.

Changes in v5:
- return -EINVAL if mediator without assign_dt_device was provided
- invert return code check for iommu_do_domctl in
XEN_DOMCTL_assign_device domctl processing to make cleaner code
- change -ENOTSUPP error code to -ENXIO in sci_do_domctl
- handle -ENXIO return comde of iommu_do_domctl
- leave !dt_device_is_protected check in iommu_do_dt_domctl to make
code work the same way it's done in "handle_device" call while
creating hwdom(dom0) and "handle_passthrough_prop" call for dom0less
creation
- drop return check from sci_assign_dt_device call as not needed
- do not return EINVAL when addign_dt_device is not set. That is
because this callback is optional and not implemented in single-agent driver

 xen/arch/arm/firmware/sci.c             | 36 +++++++++++++++++++++++++
 xen/arch/arm/include/asm/firmware/sci.h |  8 ++++++
 xen/common/domctl.c                     | 15 +++++++++++
 xen/drivers/passthrough/device_tree.c   |  6 +++++
 4 files changed, 65 insertions(+)

diff --git a/xen/arch/arm/firmware/sci.c b/xen/arch/arm/firmware/sci.c
index aa93cda7f0..a6c647a09d 100644
--- a/xen/arch/arm/firmware/sci.c
+++ b/xen/arch/arm/firmware/sci.c
@@ -126,6 +126,42 @@ int sci_assign_dt_device(struct domain *d, struct dt_device_node *dev)
     return 0;
 }
 
+int sci_do_domctl(struct xen_domctl *domctl, struct domain *d,
+                  XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl)
+{
+    struct dt_device_node *dev;
+    int ret = 0;
+
+    switch ( domctl->cmd )
+    {
+    case XEN_DOMCTL_assign_device:
+        ret = -ENXIO;
+        if ( domctl->u.assign_device.dev != XEN_DOMCTL_DEV_DT )
+            break;
+
+        if ( !cur_mediator )
+            break;
+
+        if ( !cur_mediator->assign_dt_device )
+            break;
+
+        ret = dt_find_node_by_gpath(domctl->u.assign_device.u.dt.path,
+                                    domctl->u.assign_device.u.dt.size, &dev);
+        if ( ret )
+            return ret;
+
+        ret = sci_assign_dt_device(d, dev);
+
+        break;
+
+    default:
+        /* do not fail here as call is chained with iommu handling */
+        break;
+    }
+
+    return ret;
+}
+
 static int __init sci_init(void)
 {
     struct dt_device_node *np;
diff --git a/xen/arch/arm/include/asm/firmware/sci.h b/xen/arch/arm/include/asm/firmware/sci.h
index 485ce211c9..3b4b3df770 100644
--- a/xen/arch/arm/include/asm/firmware/sci.h
+++ b/xen/arch/arm/include/asm/firmware/sci.h
@@ -146,6 +146,14 @@ int sci_dt_finalize(struct domain *d, void *fdt);
  * control" functionality.
  */
 int sci_assign_dt_device(struct domain *d, struct dt_device_node *dev);
+
+/*
+ * SCI domctl handler
+ *
+ * Only XEN_DOMCTL_assign_device is handled for now.
+ */
+int sci_do_domctl(struct xen_domctl *domctl, struct domain *d,
+                  XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl);
 #else
 
 #include <public/arch-arm.h>
diff --git a/xen/common/domctl.c b/xen/common/domctl.c
index a6210db4fb..4e40d43a72 100644
--- a/xen/common/domctl.c
+++ b/xen/common/domctl.c
@@ -29,6 +29,9 @@
 #include <xen/xvmalloc.h>
 
 #include <asm/current.h>
+#ifdef CONFIG_ARM
+#include <asm/firmware/sci.h>
+#endif
 #include <asm/irq.h>
 #include <asm/page.h>
 #include <asm/p2m.h>
@@ -921,6 +924,18 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl)
     case XEN_DOMCTL_assign_device:
     case XEN_DOMCTL_test_assign_device:
     case XEN_DOMCTL_deassign_device:
+        /*
+         * Chain SCI DT handling ahead of the IOMMU path so an SCI mediator
+         * can authorise access-controlled DT devices. Unhandled cases report
+         * -ENXIO, which is ignored. Any other SCI error aborts before the
+         * IOMMU path runs.
+         */
+#ifdef CONFIG_ARM_SCI
+        ret = sci_do_domctl(op, d, u_domctl);
+        if ( ret < 0 && ret != -ENXIO )
+            break;
+#endif
+
         ret = iommu_do_domctl(op, d, u_domctl);
         break;
 
diff --git a/xen/drivers/passthrough/device_tree.c b/xen/drivers/passthrough/device_tree.c
index ab1e07ac99..7c4cf27d32 100644
--- a/xen/drivers/passthrough/device_tree.c
+++ b/xen/drivers/passthrough/device_tree.c
@@ -379,6 +379,12 @@ int iommu_do_dt_domctl(struct xen_domctl *domctl, struct domain *d,
             break;
         }
 
+        if ( !dt_device_is_protected(dev) )
+        {
+            ret = 0;
+            break;
+        }
+
         ret = iommu_assign_dt_device(d, dev);
 
         if ( ret )
-- 
2.43.0

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

* [PATCH v13 2/6] xen: arm: smccc: add INVALID_PARAMETER error code
  2026-09-22 14:40 [PATCH v13 0/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent support Oleksii Moisieiev
@ 2026-09-22 14:40 ` Oleksii Moisieiev
  2026-09-22 14:40 ` [PATCH v13 1/6] xen/domctl: chain SCI handling before IOMMU in assign_device domctl Oleksii Moisieiev
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 14+ messages in thread
From: Oleksii Moisieiev @ 2026-09-22 14:40 UTC (permalink / raw)
  To: xen-devel@lists.xenproject.org
  Cc: Andrew Cooper, Anthony PERARD, Bertrand Marquis, Jan Beulich,
	Juergen Gross, Julien Grall, Michal Orzel, Oleksii Moisieiev,
	Roger Pau Monné, Stefano Stabellini, Volodymyr Babchuk,
	Grygorii Strashko

According to the "7.1 Return Codes" section of DEN0028 [1]
INVALID_PARAMETER code (-3) is returned when one of the call
parameters has a non-supported value.
Adding this error code to the common smccc header file.

[1]: https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6

Signed-off-by: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
Acked-by: Stefano Stabellini <sstabellini@kernel.org>
---

(no changes since v12)

Changes in v12:
- add missed Ack.

 xen/arch/arm/include/asm/smccc.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/xen/arch/arm/include/asm/smccc.h b/xen/arch/arm/include/asm/smccc.h
index c7763acd7f..91c9bc1186 100644
--- a/xen/arch/arm/include/asm/smccc.h
+++ b/xen/arch/arm/include/asm/smccc.h
@@ -381,6 +381,7 @@ void arm_smccc_1_2_smc(const struct arm_smccc_1_2_regs *args,
                        0x3FFF)
 
 /* SMCCC error codes */
+#define ARM_SMCCC_INVALID_PARAMETER     (-3)
 #define ARM_SMCCC_NOT_REQUIRED          (-2)
 #define ARM_SMCCC_ERR_UNKNOWN_FUNCTION  (-1)
 #define ARM_SMCCC_NOT_SUPPORTED         (-1)
-- 
2.43.0


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

* [PATCH v13 0/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent support
@ 2026-09-22 14:40 Oleksii Moisieiev
  2026-09-22 14:40 ` [PATCH v13 2/6] xen: arm: smccc: add INVALID_PARAMETER error code Oleksii Moisieiev
                   ` (5 more replies)
  0 siblings, 6 replies; 14+ messages in thread
From: Oleksii Moisieiev @ 2026-09-22 14:40 UTC (permalink / raw)
  To: xen-devel@lists.xenproject.org
  Cc: Andrew Cooper, Anthony PERARD, Bertrand Marquis, Jan Beulich,
	Juergen Gross, Julien Grall, Michal Orzel, Oleksii Moisieiev,
	Roger Pau Monné, Stefano Stabellini, Volodymyr Babchuk,
	Grygorii Strashko

Inroducing patch series which includes implementation of the SCI SCMI
SMC multi-agent support.
This patch series follows RFC v5 [3] series which was introducing both
SCMI single-agent and multi-agent support. After the discussion it was
decided to split features and upstream singe-agent support first. This
feature is merged for now to v4.21-rc2.
I'm starting this patch series from v6 to save the discussion history
and don't break changes log.

Patch - xen/domctl: extend XEN_DOMCTL_assign_device to handle not
only iommu
- add chainged handling of assigned DT devices to support
access-controller functionality through SCI framework.
Change was done in two parts:
 - call to sci_do_domctl() to do_domctl()
 - update iommu_do_dt_domctl() to check for dt_device_is_protected()
 and not fail if DT device is not protected by IOMMU

Patch - xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver
- add Xen-specific SCMI container compatible `xen,sci`
  under `/chosen/xen`; Xen binds only to the `arm,scmi-smc` inside it and
  ignores other SCMI nodes (e.g. under `/firmware`).
- add `scmi-secondary-agents` and `#scmi-secondary-agents-cells` to describe
  func_id/shmem/(optional agent_id) tuples for secondary agents.
- each guest using SCMI supplies its agent_id (dom0 via
  `xem,dom0-sci-agent-id=` parameter in xen,sci compatible node,
  toolstack via `arm_sci = "type=scmi_smc_multiagent,agent_id=..."`, dom0less
  via `xen,sci_type` + `xen,sci-agent-id` in `xen,domain`).
- factor out SCMI generic definitions and shmem code.
- passthrough configuration for SCMI guests mirrors other HW passthrough.

Patch - docs: arm: add SCI SCMI SMC multi-agent driver docs
- document the Xen SCMI container under `/chosen/xen/xen_scmi_config` and the
  mediator’s binding rules; update examples accordingly.

All Xen-specific SCMI configuration now lives under `/chosen/`
to keep host DT changes isolated while leaving the host `/firmware/scmi`
untouched for Dom0 consumption.

Code can be found at:
https://github.com/oleksiimoisieiev/xen/tree/scmi_ma_upstrv6

[1] RFC v2:
http://patchwork.kernel.org/project/xen-devel/cover/cover.1644341635.git.oleksii_moisieiev@epam.com/
[2] RFC v3:
https://patchwork.kernel.org/project/xen-devel/patch/20250311111618.1850927-1-grygorii_strashko@epam.com
[3] RFC v5:
https://lore.kernel.org/xen-devel/cover.1753184487.git.oleksii_moisieiev@epam.com/
[4] SCMI single-agent:
https://lore.kernel.org/xen-devel/cover.1756995595.git.oleksii_moisieiev@epam.com/
SCMI spec:
https://developer.arm.com/documentation/den0056/e/?lang=en

SCMI bindings:
https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/access-controllers/access-controllers.yaml

Reference EL3 FW:
RPI5: https://github.com/xen-troops/arm-trusted-firmware/commits/rpi5_dev/
Renesas v4h:
https://github.com/GrygiriiS/arm-trusted-firmware/commits/rcar_gen4_v2.7_v4x-scmi_upd/

base-commit: dbe60f244c (Update Xen to 4.21, 2025-02-21)

Changes in v13:
- memcpy_{from,to}io(): align only the I/O pointer and access the
  regular memory side through get_unaligned_le32()/put_unaligned_le32().
- document the contract of the helpers in xen/io.h
- reword the Arm implementation notes to state the concrete access
  pattern
- drop the incorrect claim about avoiding endianness conversion from the
  commit message; note instead that the _le32 helpers pair with
  readl()/writel() so the byte sequence is preserved on either endianness
- fix typo in commit description

Changes in v12:
- rebase to the latest staging
- add R-Bs from Stefano and Ack from Jan
- add missed Ack.
- Add missed R-B
- correct the Emacs footer of both files, which asked for 8 column
tab indentation while the code is in 4 column Xen style
- rebase to the latest staging
- return EINVAL if xen,sci_type=scmi_smc_multiagent was set but
CONFIG_SCMI_SMC_MA not set
- put arm_sci_agent_id after v8r_el1_msa and before pad to preserve
order. Reduced pad from uint16_t to uint8_t to fill the gap (was 2
bytes before adding arm_sci_agent_id and left only 1, so used uint8_t
to preserve structure size)
- fix scmi_handle_call to use arm_smccc_guest_smc helper function
- update send_smc_message function to use new format of
arm_smccc_1_1_smc
- guard the Dom0 SCMI agent id setup in create_dom0() and MA related
funcitons with CONFIG_SCMI_SMC_MA
- fix the xen_scmi_config example in booting.txt, which placed properties
after subnodes and was rejected by dtc with "Properties must precede
subnodes"

Changes in v11:
- Fix agent_id documentation: clarify it applies to SCMI SMC
multi-agent support only, not plain SCMI SMC (reviewer feedback)
- Remove "non-zero" from agent_id description to match accepted
range [0..254]
- Remove "UINT8_MAX (255) is treated as invalid" from user
documentation as unnecessary implementation detail
- Add LIBXL_HAVE_SCMI_SMC_MULTIAGENT feature macro in libxl.h to
advertise the new scmi_smc_multiagent type and agent_id field
- Add agent_id validation in
libxl__arch_domain_build_info_setdefault() to reject invalid values at
the libxl level, not only in xl

Changes in v10:
- remove unused sci_do_domctl stub from sci.h
- removed extra include in memcpy-{to/from}io.c files
- Fix tabs in MAINTAINERS file
- remove duplicate SPDX tag from scmi-shmem.c
- add cast to ARM_SMCCC_INVALID_PARAMETER to settle the sign since
ARM_SMCCC_INVALID_PARAMETER is -3 which is part of the spec and resp
is the default smccc call structure.
- update free_channel_list. Add spinlock to avoid race condition and
a comment with a description of the function work
- preserve error of smc_create_channel in scmi_probe
- check scmi shmem address alignment as wel as it is done for size
- check for d->arch.sci_data != NULL in scmi_handle_call
- use SCMI_SHMEM_MAPPED size for iomem_permit_access
- change len type to unsigned in shmem_{get|put}_message
- rename shmem_channel_is_free to shmem_channel_status
- add comment about skipping message status when getting message response
- Set correct agent_id ranges for dom0less and a toolstack. Agent_id 0
is not binded for dom0 so can be reused. Also mentioned that
UINT8_MAX (255) is treated as invalid agent_id.
- Split hypervisor and toolstack changes into separate commits
- move init list and spin init after initial checks in probe call
- fix typo in comments
- clean resources when sci_register returns an error.
- Split hypervisor and toolstack changes into separate commits
- rephrase section about /firmware/scmi. Mentioned that this node is
taken from Host DT and copied unmodified.
- fix xen,reg address for secondary domains for Dom0less configuration

Changes in v9:
- treat SCI as a gate for XEN_DOMCTL_*assign_device: abort before
IOMMU if sci_do_domctl() returns an error other than -ENXIO, instead
of trying to propagate SCI errors after a successful IOMMU
operation. This avoids partial success and the need for IOMMU rollback.
- remove early return from do_domctl() in the assign_device
path to keep RCU handling intact.
- change IS_ENABLED(*) to #ifdef in sci_do_domctl quard
- reword commit description to refer to memcpy_fromio and memcpy_toio
- ordering obj-y in Makefile
- rename ALL_LIBS to ARCH_LIBS
- drop io.h and move definitions to the common header, fix comments to
be arch neutral
- update comments for memcpy_{from/to}io implementation
- sort and refactor MAINTAINERS enties
- remove Spurious changes
- add extra check to avoid ASSERT when calling unmap_channel_memory
from assign device method
- set correct tx flag to SCMI_BASE_AGENT_PERMISSIONS_RESET when
freeing resources. Flag should be set to 1 according to the
section 4.2.2.12 [0].
- fix dt node copmaring
- moved channel->shmem check from ASSERT in unmap_memory_channel to
"if" statement. This will prevent firing ASSERT if
unmap_channel_memory was called twice on the same channel.

Changes in v8:
- check for CONFIG_ARM_SCI to be ebabled instead of COMFIG_ARM before
calling sci_do_domctl
- rework sci_do_domctl call to avoid extra checks, improved error
handling.
- do not propagate ret1 if sci_do_domctl returned positive ret
- updated comment in domctl.c code
- switched to ordered accessors to address the ordering and barrier
concerns.
- updated the documentation to match the implementation and explicitly
state the supported access sizes and granularity.
- rename memcpy_* implementation files to memcpu-* to follow naming
convension
- fix indentation to match Xen style
- fix intendation to match Xen style
- move memcpy-{from/to}io to more convenient library place
- update xen_scmi func_id in commit description
- updated documentation with the new DT format
- updated opt_dom0_scmi_agent_id setting to avoid it to be equal
SCMI_AGENT_ID_INVALID.
- changed SCMI_AGENT_ID_INVALID from 0xff to UINT8_MAX which makes
code more clear showing that UINT8_MAX is theated like invalid
agent_id and couldn't be used. Also excluded SCMI_AGENT_ID_INVALID
from acceptable value range
- remove outdated xen,config property ignore, added xen,sci compatible
to skip_matches in handle_node
- add documentation for pre-existing scmi-smc-passthrough command line
option in alphabetically correct location (in 's' section)
- add note to commit description about documentation for previously
undocumented scmi-smc-passthrough
- Fix SMC IDs in DT examples (Xen management uses 0x82000003, Dom0 uses 0x82000002)
- Add explicit note explaining why Dom0 and Xen channels do not conflict
- Document dom0less multi-agent configuration example (xen,sci_type / xen,sci-agent-id)
- Add scmi_xen node to agent-discovery example with #scmi-secondary-agents-cells = 2
- Drop dom0=sci-agent-id command line handling; Dom0 SCMI is now enabled via
  xen,dom0-sci-agent-id in the xen,sci DT container
- Refresh docs and examples to mention the DT property instead of the cmdline option
- update documentation to match the last DT format
- fixed RST: "... code-block:: dts" -> ".. code-block:: dts"
- update documentation with dom0less configuration example
- update documentation with new param xen,dom0-sci-agent-id
instead of the command line parameter

Changes in v7:
- update domctl to build on both Arm and x86 platforms
- move ret1 declaration to the top of the function as required by code
style
- x86 guidance: removed the speculative note; header now just says
  each arch supplies its own implementation or macro.
- name spacing: dropped the double-underscore; the helpers are now
  memcpy_fromio / memcpy_toio. The header also explicitly allows an
  arch to define these as macros before including it.
- updated io.c to keep 32-bit transfers safe on arm32
- moved to __raw_read*/__raw_write* accessors to avoid endianness conversion.
- split the helpers into separate compilation units
- rework scmi nodes for xen to match on compatible string instead of
the direct path
- update documentation in section of the xen_scmi configuration which
is matched by "xen,sci" compatible instead of the direct path.

Changes in v6:
- change iommu_do_domctl and sci_do_domctl command order and
call sci_do_domctl first which will produce cleaner code path.
Also dropped changing return code when iommu was disabled in
iommu_do_domctl.
- sorted objs in Makefile alhabetically
- added newline at the end of Makefile
- used uint{N}_t intead of u{N}
- add comment about why 32 bit IO operations were used
- updated cast opertaions to avoid dropping constness which is wrong
- move function definitions to generic place so the could be reused by
other arch
- add SPDX tag to io.c
- updated scmi-shmem to use io.h from generic location
- update scmi_agent_id parameter to be provided inside dom0= parameter
list and have the following format "dom0=sci-agent-id=0"
This change was done as a response for Stefano comment and
requires a lot of code changes, but produces much cleaner solution
that's why I've added it to the code.
- fix file comments and return codes
- fix lenght checks in shmem_{get,put}_message to use offsetof
- remove len member from scmi_channel structure as it is not used
- set scmi-secondary-agents property to be mandatory since if no
secondary agents were provided then there is no sence to enable scmi
when no secondary agents are populated to the Domains
- update documentation in booting.txt, added xen_scmi node to the
example
- adjust d->arch.sci_enabled value in scmi_domain_destroy
- fix lock management in smc_create_channel call
- avoid extra map_channel_memory command for Xen management channel
because collect_agent_id call unmaps memory if DOMID_XEN is not
set. So for Xen management channel we can init domain_id ad DOMID_XEN
before calling collect_agent_id so memory shouldn't be unmapped.
- remove all HVC mentions from the multi-agent doc
- update sci-agent-id parameter description in the documentation
- add missing Sign-of
- minor fixes across the document

Changes in v5:
- return -EINVAL if mediator without assign_dt_device was provided
- invert return code check for iommu_do_domctl in
XEN_DOMCTL_assign_device domctl processing to make cleaner code
- change -ENOTSUPP error code to -ENXIO in sci_do_domctl
- handle -ENXIO return comde of iommu_do_domctl
- leave !dt_device_is_protected check in iommu_do_dt_domctl to make
code work the same way it's done in "handle_device" call while
creating hwdom(dom0) and "handle_passthrough_prop" call for dom0less
creation
- drop return check from sci_assign_dt_device call as not needed
- do not return EINVAL when addign_dt_device is not set. That is
because this callback is optional and not implemented in single-agent driver
- move memcpy_toio/fromio to the generic place
- fix device-tree example format in booting.txt, added ";" after "}".
- update define in scmi-proto.h
- update define in scmi-shmem.h file
- scmi_assign_device - do not ignore -EOPNOTSUPP return
code of the do_smc_xfer
- remove overwriting agent_channel->agent_id after
SCMI_BASE_DISCOVER_AGENT call
- add multi-agent files to the MAINTAINERS
- add SCMI multi-agent description to the SUPPORT.md
- handle ARM_SMCCC_INVALID_PARAMETER return code and return -EINVAL
for smc call
- updated collect_agents function. Set agent_id parameter as optional
in scmi-secondary-agents device-tree property
- introduce "#scmi-secondary-agents-cells" parameter to set if
agent_id was provided
- reanme xen,scmi-secondary-agents property to scmi-secondary-agents
- move memcpu_toio/fromio for the generic place
- update Xen to get management channel from /chosen/xen,config node
- get hypervisor channnel from node instead of using hardcoded
- update handling scmi and shmem nodes for the domain
- Set multi-agent driver to support only Arm64
- rework multi-agent driver to leave Host Device-tree unmodified

Changes in v4:
- toolstack comments from Anthony PERARD
- added dom0less support
- added doc for "xen,scmi-secondary-agents"

Grygorii Strashko (2):
  xen/domctl: chain SCI handling before IOMMU in assign_device domctl
  docs: arm: add SCI SCMI SMC multi-agent driver docs

Oleksii Moisieiev (4):
  xen: arm: smccc: add INVALID_PARAMETER error code
  lib/arm: Add I/O memory copy helpers
  xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver
  tools/xl/libxl: wire up SCMI SMC multi-agent configuration

 MAINTAINERS                                   |   1 +
 SUPPORT.md                                    |  11 +
 .../arm/firmware/arm-scmi.rst                 | 422 +++++++++
 docs/man/xl.cfg.5.pod.in                      |  13 +
 docs/misc/arm/device-tree/booting.txt         | 197 +++++
 tools/include/libxl.h                         |   8 +
 tools/libs/light/libxl_arm.c                  |  13 +
 tools/libs/light/libxl_types.idl              |   4 +-
 tools/xl/xl_parse.c                           |  27 +
 xen/arch/arm/Makefile                         |   1 +
 xen/arch/arm/arch.mk                          |   1 +
 xen/arch/arm/dom0less-build.c                 |  17 +
 xen/arch/arm/domain_build.c                   |  43 +
 xen/arch/arm/firmware/Kconfig                 |  12 +
 xen/arch/arm/firmware/Makefile                |   1 +
 xen/arch/arm/firmware/sci.c                   |  36 +
 xen/arch/arm/firmware/scmi-proto.h            | 164 ++++
 xen/arch/arm/firmware/scmi-shmem.c            | 118 +++
 xen/arch/arm/firmware/scmi-shmem.h            |  45 +
 xen/arch/arm/firmware/scmi-smc-multiagent.c   | 816 ++++++++++++++++++
 xen/arch/arm/include/asm/firmware/sci.h       |   8 +
 xen/arch/arm/include/asm/smccc.h              |   1 +
 xen/arch/arm/lib/Makefile                     |   2 +
 xen/arch/arm/lib/memcpy-fromio.c              |  56 ++
 xen/arch/arm/lib/memcpy-toio.c                |  56 ++
 xen/common/domctl.c                           |  15 +
 xen/drivers/passthrough/device_tree.c         |   6 +
 xen/include/public/arch-arm.h                 |   5 +-
 xen/include/xen/io.h                          |  25 +
 29 files changed, 2122 insertions(+), 2 deletions(-)
 create mode 100644 xen/arch/arm/firmware/scmi-proto.h
 create mode 100644 xen/arch/arm/firmware/scmi-shmem.c
 create mode 100644 xen/arch/arm/firmware/scmi-shmem.h
 create mode 100644 xen/arch/arm/firmware/scmi-smc-multiagent.c
 create mode 100644 xen/arch/arm/lib/Makefile
 create mode 100644 xen/arch/arm/lib/memcpy-fromio.c
 create mode 100644 xen/arch/arm/lib/memcpy-toio.c

-- 
2.43.0

base-commit: 822b0be6606d49c144374844530d516889791fa8
branch: scmi_ma_upstrv13

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

* [PATCH v13 5/6] tools/xl/libxl: wire up SCMI SMC multi-agent configuration
  2026-09-22 14:40 [PATCH v13 0/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent support Oleksii Moisieiev
                   ` (2 preceding siblings ...)
  2026-09-22 14:40 ` [PATCH v13 4/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver Oleksii Moisieiev
@ 2026-09-22 14:40 ` Oleksii Moisieiev
  2026-09-22 14:40 ` [PATCH v13 3/6] lib/arm: Add I/O memory copy helpers Oleksii Moisieiev
  2026-09-22 14:40 ` [PATCH v13 6/6] docs: arm: add SCI SCMI SMC multi-agent driver docs Oleksii Moisieiev
  5 siblings, 0 replies; 14+ messages in thread
From: Oleksii Moisieiev @ 2026-09-22 14:40 UTC (permalink / raw)
  To: xen-devel@lists.xenproject.org
  Cc: Andrew Cooper, Anthony PERARD, Bertrand Marquis, Jan Beulich,
	Juergen Gross, Julien Grall, Michal Orzel, Oleksii Moisieiev,
	Roger Pau Monné, Stefano Stabellini, Volodymyr Babchuk,
	Grygorii Strashko

Plumb the SCMI SMC multi-agent type through the toolstack:

- Extend libxl_arm_sci_type enumeration with scmi_smc_multiagent (value 2)
- Add agent_id field to libxl_arm_sci structure for per-domain agent assignment
- Update libxl_arm.c to translate libxl config to XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC_MA
  and pass agent_id to the hypervisor via xen_domctl_createdomain
- Add xl.cfg parsing for arm_sci="type=scmi_smc_multiagent,agent_id=N"
- Document the new xl.cfg options in xl.cfg.5.pod.in

This completes the userspace side of multi-agent SCMI, allowing xl create
and dom0less configurations to assign unique agent_id values to domains.

Series-changes 12:
- reject agent_id in xl.cfg when the ARM_SCI type is not
  scmi_smc_multiagent, instead of parsing it and discarding it
- perform the check after the option loop, as type and agent_id may be
  given in either order

Signed-off-by: Grygorii Strashko <grygorii_strashko@epam.com>
Signed-off-by: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
---

(no changes since v11)

Changes in v11:
- Fix agent_id documentation: clarify it applies to SCMI SMC
multi-agent support only, not plain SCMI SMC (reviewer feedback)
- Remove "non-zero" from agent_id description to match accepted
range [0..254]
- Remove "UINT8_MAX (255) is treated as invalid" from user
documentation as unnecessary implementation detail
- Add LIBXL_HAVE_SCMI_SMC_MULTIAGENT feature macro in libxl.h to
advertise the new scmi_smc_multiagent type and agent_id field
- Add agent_id validation in
libxl__arch_domain_build_info_setdefault() to reject invalid values at
the libxl level, not only in xl

Changes in v10:
- Split hypervisor and toolstack changes into separate commits

 docs/man/xl.cfg.5.pod.in         | 13 +++++++++++++
 tools/include/libxl.h            |  8 ++++++++
 tools/libs/light/libxl_arm.c     | 13 +++++++++++++
 tools/libs/light/libxl_types.idl |  4 +++-
 tools/xl/xl_parse.c              | 27 +++++++++++++++++++++++++++
 5 files changed, 64 insertions(+), 1 deletion(-)

diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in
index d34951edb9..1dec511f20 100644
--- a/docs/man/xl.cfg.5.pod.in
+++ b/docs/man/xl.cfg.5.pod.in
@@ -3179,8 +3179,21 @@ single SCMI OSPM agent support.
 Should be used together with B<scmi-smc-passthrough> Xen command line
 option.
 
+=item B<scmi_smc_multiagent>
+
+Enables ARM SCMI SMC multi-agent support for the guest by enabling SCMI over
+SMC calls forwarding from domain to the EL3 firmware (like Trusted Firmware-A)
+with a multi SCMI OSPM agent support. The SCMI B<agent_id> should be
+specified for the guest.
+
 =back
 
+=item B<agent_id=NUMBER>
+
+Specifies an ARM SCI agent id for the guest. This option is mandatory
+if the SCMI SMC multi-agent support is enabled for the guest. The agent ids of
+domains existing on a single host must be unique and in the range [0..254].
+
 =back
 
 =back
diff --git a/tools/include/libxl.h b/tools/include/libxl.h
index 7c098edab6..9f46566739 100644
--- a/tools/include/libxl.h
+++ b/tools/include/libxl.h
@@ -318,6 +318,14 @@
  */
 #define LIBXL_HAVE_BUILDINFO_ARCH_ARM_SCI 1
 
+/*
+ * LIBXL_HAVE_SCMI_SMC_MULTIAGENT indicates that the
+ * LIBXL_ARM_SCI_TYPE_SCMI_SMC_MULTIAGENT value is available in the
+ * libxl_arm_sci_type enumeration and the agent_id field is available
+ * in the libxl_arm_sci structure.
+ */
+#define LIBXL_HAVE_SCMI_SMC_MULTIAGENT 1
+
 /*
  * LIBXL_HAVE_SOFT_RESET indicates that libxl supports performing
  * 'soft reset' for domains and there is 'soft_reset' shutdown reason
diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c
index e4407d6e3f..3adb90c286 100644
--- a/tools/libs/light/libxl_arm.c
+++ b/tools/libs/light/libxl_arm.c
@@ -240,6 +240,10 @@ int libxl__arch_domain_prepare_config(libxl__gc *gc,
     case LIBXL_ARM_SCI_TYPE_SCMI_SMC:
         config->arch.arm_sci_type = XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC;
         break;
+    case LIBXL_ARM_SCI_TYPE_SCMI_SMC_MULTIAGENT:
+        config->arch.arm_sci_type = XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC_MA;
+        config->arch.arm_sci_agent_id = d_config->b_info.arch_arm.arm_sci.agent_id;
+        break;
     default:
         LOG(ERROR, "Unknown ARM_SCI type %d",
             d_config->b_info.arch_arm.arm_sci.type);
@@ -1837,6 +1841,15 @@ int libxl__arch_domain_build_info_setdefault(libxl__gc *gc,
         }
     }
 
+    /* Sanitise ARM SCI agent_id parameter */
+    if (b_info->arch_arm.arm_sci.type == LIBXL_ARM_SCI_TYPE_SCMI_SMC_MULTIAGENT &&
+        b_info->arch_arm.arm_sci.agent_id >= UINT8_MAX) {
+        LOG(ERROR,
+            "Invalid ARM SCI agent_id: %u. Valid range is [0..254]",
+            b_info->arch_arm.arm_sci.agent_id);
+        return ERROR_FAIL;
+    }
+
     if (b_info->type != LIBXL_DOMAIN_TYPE_PV)
         return 0;
 
diff --git a/tools/libs/light/libxl_types.idl b/tools/libs/light/libxl_types.idl
index 3c35f873df..9830671ca4 100644
--- a/tools/libs/light/libxl_types.idl
+++ b/tools/libs/light/libxl_types.idl
@@ -554,11 +554,13 @@ libxl_sve_type = Enumeration("sve_type", [
 
 libxl_arm_sci_type = Enumeration("arm_sci_type", [
     (0, "none"),
-    (1, "scmi_smc")
+    (1, "scmi_smc"),
+    (2, "scmi_smc_multiagent")
     ], init_val = "LIBXL_ARM_SCI_TYPE_NONE")
 
 libxl_arm_sci = Struct("arm_sci", [
     ("type", libxl_arm_sci_type),
+    ("agent_id", uint8)
     ])
 
 libxl_rdm_reserve = Struct("rdm_reserve", [
diff --git a/tools/xl/xl_parse.c b/tools/xl/xl_parse.c
index 01783731b5..6bb3325c98 100644
--- a/tools/xl/xl_parse.c
+++ b/tools/xl/xl_parse.c
@@ -1290,6 +1290,7 @@ static int parse_arm_sci_config(XLU_Config *cfg, libxl_arm_sci *arm_sci,
                                 const char *str)
 {
     int ret = 0;
+    int agent_id_set = 0;
     char *buf2, *ptr;
     char *oparg;
 
@@ -1308,9 +1309,35 @@ static int parse_arm_sci_config(XLU_Config *cfg, libxl_arm_sci *arm_sci,
             }
         }
 
+        if (MATCH_OPTION("agent_id", ptr, oparg)) {
+            unsigned long val = parse_ulong(oparg);
+
+            if ( val >= UINT8_MAX ) {
+                fprintf(stderr, "An invalid ARM_SCI agent_id specified (%lu). Valid range [0..254]\n",
+                        val);
+                ret = ERROR_INVAL;
+                goto out;
+            }
+            arm_sci->agent_id = val;
+            agent_id_set = 1;
+        }
+
         ptr = strtok(NULL, ",");
     }
 
+    /*
+     * agent_id only means something to the multi-agent driver, and
+     * libxl__arch_domain_prepare_config() only reads it for that type.
+     * Reject it elsewhere instead of silently dropping it. The check is done
+     * after the loop because the options may be given in any order.
+     */
+    if (agent_id_set &&
+        arm_sci->type != LIBXL_ARM_SCI_TYPE_SCMI_SMC_MULTIAGENT) {
+        fprintf(stderr,
+                "ARM_SCI agent_id is only supported with type=scmi_smc_multiagent\n");
+        ret = ERROR_INVAL;
+    }
+
 out:
     free(buf2);
     return ret;
-- 
2.43.0


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

* [PATCH v13 3/6] lib/arm: Add I/O memory copy helpers
  2026-09-22 14:40 [PATCH v13 0/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent support Oleksii Moisieiev
                   ` (3 preceding siblings ...)
  2026-09-22 14:40 ` [PATCH v13 5/6] tools/xl/libxl: wire up SCMI SMC multi-agent configuration Oleksii Moisieiev
@ 2026-09-22 14:40 ` Oleksii Moisieiev
  2026-09-22 14:40 ` [PATCH v13 6/6] docs: arm: add SCI SCMI SMC multi-agent driver docs Oleksii Moisieiev
  5 siblings, 0 replies; 14+ messages in thread
From: Oleksii Moisieiev @ 2026-09-22 14:40 UTC (permalink / raw)
  To: xen-devel@lists.xenproject.org
  Cc: Andrew Cooper, Anthony PERARD, Bertrand Marquis, Jan Beulich,
	Juergen Gross, Julien Grall, Michal Orzel, Oleksii Moisieiev,
	Roger Pau Monné, Stefano Stabellini, Volodymyr Babchuk,
	Grygorii Strashko

Introduce memcpy_fromio() and memcpy_toio() helpers to copy between
regular memory and a memory-like I/O region on Arm. The generic
prototypes live in io.h so other architectures can provide their own
implementations.

The helpers copy a byte sequence: byte accesses are used until the I/O
pointer is 32-bit aligned and for the trailing count % 4 bytes, the
aligned bulk uses 32-bit accesses. Only the I/O pointer is aligned, the
regular memory side is accessed through get_unaligned_le32() /
put_unaligned_le32(), so the access widths issued on the I/O side do not
depend on the alignment of the memory buffer. These _le32 helpers pair
with readl()/writel(), which are little endian accessors, hence the byte
sequence is preserved on either CPU endianness.

The constraints on the I/O region are documented next to the
declarations in xen/io.h, the arch-specific access pattern next to the
implementation.

Signed-off-by: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
---

Changes in v13:
- memcpy_{from,to}io(): align only the I/O pointer and access the
  regular memory side through get_unaligned_le32()/put_unaligned_le32().
- document the contract of the helpers in xen/io.h
- reword the Arm implementation notes to state the concrete access
  pattern
- drop the incorrect claim about avoiding endianness conversion from the
  commit message; note instead that the _le32 helpers pair with
  readl()/writel() so the byte sequence is preserved on either endianness

Changes in v12:
- Add missed R-B
- correct the Emacs footer of both files, which asked for 8 column
tab indentation while the code is in 4 column Xen style

Changes in v10:
- removed extra include in memcpy-{to/from}io.c files

Changes in v9:
- reword commit description to refer to memcpy_fromio and memcpy_toio
- ordering obj-y in Makefile
- rename ALL_LIBS to ARCH_LIBS
- drop io.h and move definitions to the common header, fix comments to
be arch neutral
- update comments for memcpy_{from/to}io implementation

Changes in v8:
- switched to ordered accessors to address the ordering and barrier
concerns.
- updated the documentation to match the implementation and explicitly
state the supported access sizes and granularity.
- rename memcpy_* implementation files to memcpu-* to follow naming
convension
- fix indentation to match Xen style
- fix intendation to match Xen style
- move memcpy-{from/to}io to more convenient library place

Changes in v7:
- x86 guidance: removed the speculative note; header now just says
  each arch supplies its own implementation or macro.
- name spacing: dropped the double-underscore; the helpers are now
  memcpy_fromio / memcpy_toio. The header also explicitly allows an
  arch to define these as macros before including it.
- updated io.c to keep 32-bit transfers safe on arm32
- moved to __raw_read*/__raw_write* accessors to avoid endianness conversion.
- split the helpers into separate compilation units

Changes in v6:
- sorted objs in Makefile alhabetically
- added newline at the end of Makefile
- used uint{N}_t intead of u{N}
- add comment about why 32 bit IO operations were used
- updated cast opertaions to avoid dropping constness which is wrong
- move function definitions to generic place so the could be reused by
other arch
- add SPDX tag to io.c

Changes in v5:
- move memcpy_toio/fromio to the generic place

 xen/arch/arm/Makefile            |  1 +
 xen/arch/arm/arch.mk             |  1 +
 xen/arch/arm/lib/Makefile        |  2 ++
 xen/arch/arm/lib/memcpy-fromio.c | 56 ++++++++++++++++++++++++++++++++
 xen/arch/arm/lib/memcpy-toio.c   | 56 ++++++++++++++++++++++++++++++++
 xen/include/xen/io.h             | 25 ++++++++++++++
 6 files changed, 141 insertions(+)
 create mode 100644 xen/arch/arm/lib/Makefile
 create mode 100644 xen/arch/arm/lib/memcpy-fromio.c
 create mode 100644 xen/arch/arm/lib/memcpy-toio.c

diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
index b7afd3e58c..e448e69014 100644
--- a/xen/arch/arm/Makefile
+++ b/xen/arch/arm/Makefile
@@ -8,6 +8,7 @@ ifneq ($(CONFIG_NO_PLAT),y)
 obj-y += platforms/
 endif
 obj-y += firmware/
+obj-y += lib/
 obj-$(CONFIG_TEE) += tee/
 obj-$(CONFIG_HAS_VPCI) += vpci.o
 
diff --git a/xen/arch/arm/arch.mk b/xen/arch/arm/arch.mk
index dea8dbd18a..009bb22c45 100644
--- a/xen/arch/arm/arch.mk
+++ b/xen/arch/arm/arch.mk
@@ -2,6 +2,7 @@
 # arm-specific definitions
 
 ARCH_LIBS-y += arch/arm/$(ARCH)/lib/lib.a
+ARCH_LIBS-y += arch/arm/lib/lib.a
 
 $(call cc-options-add,CFLAGS,CC,$(EMBEDDED_EXTRA_CFLAGS))
 $(call cc-option-add,CFLAGS,CC,-Wnested-externs)
diff --git a/xen/arch/arm/lib/Makefile b/xen/arch/arm/lib/Makefile
new file mode 100644
index 0000000000..07a0d9186c
--- /dev/null
+++ b/xen/arch/arm/lib/Makefile
@@ -0,0 +1,2 @@
+lib-y += memcpy-fromio.o
+lib-y += memcpy-toio.o
diff --git a/xen/arch/arm/lib/memcpy-fromio.c b/xen/arch/arm/lib/memcpy-fromio.c
new file mode 100644
index 0000000000..98339a5cfa
--- /dev/null
+++ b/xen/arch/arm/lib/memcpy-fromio.c
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <xen/io.h>
+#include <xen/unaligned.h>
+
+/*
+ * Arm implementation notes:
+ * - Byte accesses are issued until the I/O pointer is 32-bit aligned and
+ *   for the trailing count % 4 bytes; the aligned bulk uses 32-bit
+ *   accesses. No wider accesses are issued.
+ * - The access widths on the I/O side depend only on the alignment of the
+ *   I/O pointer and on count. The regular memory side is accessed through
+ *   put_unaligned_le32(), so its alignment has no influence on them.
+ * - put_unaligned_le32() pairs with readl(), which is a little endian
+ *   accessor, so the byte sequence is preserved on either CPU endianness.
+ * - The I/O region must be mapped with device attributes; ordering is
+ *   provided by the accessors themselves, no extra barriers are added.
+ */
+
+void memcpy_fromio(void *to, const volatile void __iomem *from,
+                   size_t count)
+{
+    while ( count && !IS_ALIGNED((unsigned long)from, 4) )
+    {
+        *(uint8_t *)to = readb(from);
+        from++;
+        to++;
+        count--;
+    }
+
+    while ( count >= 4 )
+    {
+        put_unaligned_le32(readl(from), to);
+        from += 4;
+        to += 4;
+        count -= 4;
+    }
+
+    while ( count )
+    {
+        *(uint8_t *)to = readb(from);
+        from++;
+        to++;
+        count--;
+    }
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/arch/arm/lib/memcpy-toio.c b/xen/arch/arm/lib/memcpy-toio.c
new file mode 100644
index 0000000000..c0be79586d
--- /dev/null
+++ b/xen/arch/arm/lib/memcpy-toio.c
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <xen/io.h>
+#include <xen/unaligned.h>
+
+/*
+ * Arm implementation notes:
+ * - Byte accesses are issued until the I/O pointer is 32-bit aligned and
+ *   for the trailing count % 4 bytes; the aligned bulk uses 32-bit
+ *   accesses. No wider accesses are issued.
+ * - The access widths on the I/O side depend only on the alignment of the
+ *   I/O pointer and on count. The regular memory side is accessed through
+ *   get_unaligned_le32(), so its alignment has no influence on them.
+ * - get_unaligned_le32() pairs with writel(), which is a little endian
+ *   accessor, so the byte sequence is preserved on either CPU endianness.
+ * - The I/O region must be mapped with device attributes; ordering is
+ *   provided by the accessors themselves, no extra barriers are added.
+ */
+
+void memcpy_toio(volatile void __iomem *to, const void *from,
+                 size_t count)
+{
+    while ( count && !IS_ALIGNED((unsigned long)to, 4) )
+    {
+        writeb(*(const uint8_t *)from, to);
+        from++;
+        to++;
+        count--;
+    }
+
+    while ( count >= 4 )
+    {
+        writel(get_unaligned_le32(from), to);
+        from += 4;
+        to += 4;
+        count -= 4;
+    }
+
+    while ( count )
+    {
+        writeb(*(const uint8_t *)from, to);
+        from++;
+        to++;
+        count--;
+    }
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/include/xen/io.h b/xen/include/xen/io.h
index 164a20c5d7..0b60be27c1 100644
--- a/xen/include/xen/io.h
+++ b/xen/include/xen/io.h
@@ -67,4 +67,29 @@ static inline bool write_mmio(volatile void __iomem *mem, unsigned long data,
     return true;
 }
 
+/*
+ * Copy a sequence of bytes between regular memory and a memory-like I/O
+ * region, e.g. shared memory placed in RAM or SRAM mapped with device
+ * attributes.
+ *
+ * The I/O region must behave like byte-addressable storage: it has to
+ * accept 8-bit accesses at any byte address and naturally aligned 32-bit
+ * accesses, with plain byte-storage semantics in both cases (no side
+ * effects, no dependency on the access width). An implementation may use
+ * any of these widths, but never wider ones. The helpers are therefore
+ * not suitable for device registers with access-width requirements.
+ *
+ * Neither pointer needs to be aligned. The access widths issued on the
+ * I/O side depend only on the alignment of the I/O pointer and on count,
+ * never on the alignment of the regular memory pointer.
+ *
+ * Implementations are architecture-specific and may impose further
+ * constraints; see the respective arch/<arch>/lib/memcpy-{from,to}io.c
+ * for the exact access pattern.
+ */
+void memcpy_fromio(void *to, const volatile void __iomem *from,
+                   size_t count);
+void memcpy_toio(volatile void __iomem *to, const void *from,
+                 size_t count);
+
 #endif /* XEN_IO_H */
-- 
2.43.0


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

* [PATCH v13 4/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver
  2026-09-22 14:40 [PATCH v13 0/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent support Oleksii Moisieiev
  2026-09-22 14:40 ` [PATCH v13 2/6] xen: arm: smccc: add INVALID_PARAMETER error code Oleksii Moisieiev
  2026-09-22 14:40 ` [PATCH v13 1/6] xen/domctl: chain SCI handling before IOMMU in assign_device domctl Oleksii Moisieiev
@ 2026-09-22 14:40 ` Oleksii Moisieiev
  2026-09-23  1:02   ` Volodymyr Babchuk
  2026-09-22 14:40 ` [PATCH v13 5/6] tools/xl/libxl: wire up SCMI SMC multi-agent configuration Oleksii Moisieiev
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 14+ messages in thread
From: Oleksii Moisieiev @ 2026-09-22 14:40 UTC (permalink / raw)
  To: xen-devel@lists.xenproject.org
  Cc: Andrew Cooper, Anthony PERARD, Bertrand Marquis, Jan Beulich,
	Juergen Gross, Julien Grall, Michal Orzel, Oleksii Moisieiev,
	Roger Pau Monné, Stefano Stabellini, Volodymyr Babchuk,
	Grygorii Strashko

This patch introduces SCI driver to support for ARM EL3 Trusted Firmware-A
(TF-A) which provides SCMI interface with multi-agent support, as shown
below.

  +-----------------------------------------+
  |                                         |
  | EL3 TF-A SCMI                           |
  +-------+--+-------+--+-------+--+-------++
  |shmem1 |  |shmem0 |  |shmem2 |  |shmemX |
  +-----+-+  +---+---+  +--+----+  +---+---+
smc-id1 |        |         |           |
agent1  |        |         |           |
  +-----v--------+---------+-----------+----+
  |              |         |           |    |
  |              |         |           |    |
  +--------------+---------+-----------+----+
         smc-id0 |  smc-id2|    smc-idX|
         agent0  |  agent2 |    agentX |
                 |         |           |
            +----v---+  +--v-----+  +--v-----+
            |        |  |        |  |        |
            | Dom0   |  | Dom1   |  | DomX   |
            |        |  |        |  |        |
            |        |  |        |  |        |
            +--------+  +--------+  +--------+

The EL3 SCMI multi-agent firmware is expected to provide SCMI SMC shared
memory transport for every Agent in the system.

The SCMI Agent transport channel defined by pair:
 - smc-id: SMC id used for Doorbell
 - shmem: shared memory for messages transfer, Xen page
 aligned. Shared memory is mapped with the following flags:
 MT_DEVICE_nGnRE.

The follwoing SCMI Agents are expected to be defined by SCMI FW to enable SCMI
multi-agent functionality under Xen:
- Xen management agent: trusted agents that accesses to the Base Protocol
commands to configure agent specific permissions
- OSPM VM agents: non-trusted agent, one for each Guest domain which is
  allowed direct HW access. At least one OSPM VM agent has to be provided
  by FW if HW is handled only by Dom0 or Driver Domain.

The EL3 SCMI FW is expected to implement following Base protocol messages:
- BASE_DISCOVER_AGENT (optional if agent_id was provided)
- BASE_RESET_AGENT_CONFIGURATION (optional)
- BASE_SET_DEVICE_PERMISSIONS (optional)

The SCI SCMI SMC multi-agent driver implements following
functionality:
- The driver is initialized from the Xen SCMI container ``xen_scmi_config``
  (compatible ``xen,sci``) placed under ``/chosen/xen``. Only the
  ``arm,scmi-smc`` node that is a child of this container will bind to Xen;
  other SCMI nodes (for example under ``/firmware``) are ignored to avoid
  stealing the host OSPM instance.

scmi_shm_1: sram@47ff1000 {
          compatible = "arm,scmi-shmem";
          reg = <0x0 0x47ff1000 0x0 0x1000>;
};
scmi_xen: scmi {
        compatible = "arm,scmi-smc";
        arm,smc-id = <0x82000003>; <--- Xen management agent smc-id
        #address-cells = < 1>;
        #size-cells = < 0>;
        #access-controller-cells = < 1>;
        shmem = <&scmi_shm_1>; <--- Xen management agent shmem
};

- The driver obtains Xen specific SCMI Agent's configuration from the
  Host DT, probes Agents and builds SCMI Agents list. The Agents
  configuration is taken from "scmi-secondary-agents" property where
  first item is "arm,smc-id", second - "arm,scmi-shmem" phandle and
  third is optional "agent_id":

/ {
  chosen {
    xen {
      ranges;
      xen_scmi_config {
        compatible = "xen,sci";
        #address-cells = <2>;
        #size-cells = <2>;
        ranges;

	scmi-secondary-agents = <
          0x82000002 &scmi_shm_0 0
          0x82000004 &scmi_shm_2 2
          0x82000005 &scmi_shm_3 3>; <--- func_id, shmem, agent_id
        #scmi-secondary-agents-cells = <3>;
        xen,dom0-sci-agent-id = <0>;

        scmi_shm_0: sram@47ff0000 {
          compatible = "arm,scmi-shmem";
          reg = <0x0 0x47ff0000 0x0 0x1000>;
        };

        /* Xen SCMI management channel */
        scmi_shm_1: sram@47ff1000 {
          compatible = "arm,scmi-shmem";
          reg = <0x0 0x47ff1000 0x0 0x1000>;
        };

        scmi_shm_2: sram@47ff2000 {
          compatible = "arm,scmi-shmem";
          reg = <0x0 0x47ff2000 0x0 0x1000>;
        };

        scmi_shm_3: sram@47ff3000 {
          compatible = "arm,scmi-shmem";
          reg = <0x0 0x47ff3000 0x0 0x1000>;
        };

        scmi_xen: scmi {
          compatible = "arm,scmi-smc";
          arm,smc-id = <0x82000003>; <--- Xen management agent func_id
          #address-cells = <1>;
          #size-cells = <0>;
          #access-controller-cells = <1>;
          shmem = <&scmi_shm_1>; <--- Xen management agent shmem
        };
      };
    };
  };
};

/ {
    /*
     * Host SCMI OSPM channel - provided to the Dom0 as is if SCMI
     * enabled for it, ignored by Xen multi-agent mediator
     */
    scmi_shm: sram@47ff0000 {
            compatible = "arm,scmi-shmem";
            reg = <0x0 0x47ff0000 0x0 0x1000>;
    };

    firmware {
      scmi: scmi {
        compatible = "arm,scmi-smc";
        arm,smc-id = <0x82000002>; <--- Host OSPM agent smc-id
        #address-cells = < 1>;
        #size-cells = < 0>;
        shmem = <&scmi_shm>; <--- Host OSPM agent shmem

        protocol@X{
        };
      };
   };
};

This approach allows defining multiple SCMI Agents by adding
Xen-specific properties under the ``/chosen`` node to the Host Device
Tree, leaving the main part unchanged. The Host DT SCMI channel will
be passed to Dom0.

The Xen management agent is described as a ``scmi_xen`` node under the
``xen,sci`` comaptible node, which is used by Xen to control other
SCMI Agents in the system.

All secondary agents' configurations are provided in the
``scmi-secondary-agents`` property with an optional ``agent_id`` field.

The ``agent_id`` from the ``scmi-secondary-agents`` property is used
to identify the agent in the system and can be omitted by setting
``#scmi-secondary-agents-cells = <2>``, so the Secondary Agents
configuration will look like this:

/ {
  chosen {
    xen {
      xen_scmi_config {
        compatible = "xen,sci";
        #address-cells = <2>;
        #size-cells = <2>;
        ranges;

        /* Shared memory nodes as defined earlier */

        scmi-secondary-agents = <
          0x82000003 &scmi_shm_0
          0x82000004 &scmi_shm_2
          0x82000005 &scmi_shm_3
          0x82000006 &scmi_shm_4>;
        #scmi-secondary-agents-cells = <2>;
      };
    };
  };
}

In this case, Xen will use the ``SCMI_BASE_DISCOVER_AGENT`` call to
discover the ``agent_id`` for each secondary agent. Providing the
``agent_id`` in the ``scmi-secondary-agents`` property allows skipping
the discovery call, which is useful when the secondary agent's shared
memory is not accessible by Xen or when boot time is important because
it allows skipping the agent discovery procedure.

  Note that Xen is the only one entry in the system which need to know
  about SCMI multi-agent support.

SMC ID Configuration and SCMI Connection Compatibility:

The configuration allows the same device tree to work for both baremetal
Linux and Linux Dom0. This is achieved because:

- Baremetal Linux uses: func_id 0x82000002, scmi-shmem 0x47ff0000
- Dom0 Linux uses: func_id 0x82000002, scmi-shmem 0x47ff0000
- Xen management uses: func_id 0x82000003, scmi-shmem 0x47ff1000

This works because the privileged SCMI connection in EL3 firmware is not
tied exclusively to func_id 0x82000002. The EL3 firmware supports multiple
SCMI agents with different SMC IDs and shared memory regions. Each agent
(Dom0 via 0x82000002, Xen via 0x82000003, other domains via additional
func_ids) has an independent communication channel to the firmware.

The key distinction is that Xen's management channel (0x82000003) is used
for privileged operations like agent configuration and device permissions
(BASE_SET_DEVICE_PERMISSIONS, BASE_RESET_AGENT_CONFIGURATION), while Dom0's
channel (0x82000002) is used for standard SCMI protocol operations (power,
clock, sensor management, etc.). The firmware enforces different permission
levels for each agent based on their agent_id, not the SMC ID.

Therefore, there is no conflict: Linux Dom0 retains its standard SCMI
connection for hardware management, while Xen uses its separate privileged
channel for mediating access between multiple domains.

- It implements the SCI subsystem interface required for configuring and
enabling SCMI functionality for Dom0/hwdom and Guest domains. To enable
SCMI functionality for domain it has to be configured with unique supported
SCMI Agent_id and use corresponding SCMI SMC shared memory transport
[smc-id, shmem] defined for this SCMI Agent_id.
- Once Xen domain is configured it can communicate with EL3 SCMI FW:
  -- zero-copy, the guest domain puts SCMI message in shmem;
  -- the guest triggers SMC exception with smc-id (doorbell);
  -- the Xen driver catches exception, do checks and synchronously forwards
  it to EL3 FW.
- the Xen driver sends BASE_RESET_AGENT_CONFIGURATION message to Xen
  management agent channel on domain destroy event. This allows to reset
  resources used by domain and so implement use-case like domain reboot.

Dom0 Enable SCMI SMC:
 - set xen,dom0-sci-agent-id=<agent_id> under the xen,sci container in
   the Host DT. If the property is absent, SCMI is disabled for Dom0
   and all SCMI nodes are removed from the Dom0 DT. The driver updates
   the Dom0 DT SCMI node "arm,smc-id" value and fixes up the shmem
   node according to the assigned agent_id.

 - pass dom0=sci-agent-id=<agent_id> in Xen command line. if not provided
   SCMI will be disabled for Dom0 and all SCMI nodes removed from Dom0 DT.
   The driver updates Dom0 DT SCMI node "arm,smc-id" value and fix up shmem
   node according to assigned agent_id.

Guest domains enable SCMI SMC:
 - xl.cfg: add configuration option as below

   arm_sci = "type=scmi_smc_multiagent,agent_id=2"

 - xl.cfg: enable access to the "arm,scmi-shmem" which should
 correspond assigned agent_id for the domain, for example:

iomem = [
    "47ff2,1@22001",
]

 - DT: add SCMI nodes to the Driver domain partial device tree as in the
 below example. The "arm,smc-id" should correspond assigned agent_id
 for the domain:

passthrough {
   scmi_shm_0: sram@22001000 {
       compatible = "arm,scmi-shmem";
       reg = <0x0 0x22001000 0x0 0x1000>;
   };

   firmware {
        compatible = "simple-bus";
            scmi: scmi {
                compatible = "arm,scmi-smc";
                arm,smc-id = <0x82000004>;
                shmem = <&scmi_shm_0>;
                ...
            }
    }
}

SCMI "4.2.1.1 Device specific access control"

The XEN SCI SCMI SMC multi-agent driver performs "access-controller"
provider function in case EL3 SCMI FW implements SCMI "4.2.1.1 Device
specific access control" and provides the BASE_SET_DEVICE_PERMISSIONS
command to configure the devices that an agents have access to.
The DT SCMI node should "#access-controller-cells=<1>" property and DT
devices should be bound to the Xen SCMI.

&i2c1 {
        access-controllers = <&scmi 0>;
};

The Dom0 and dom0less domains DT devices will be processed
automatically through sci_assign_dt_device() call, but to assign SCMI
devices from toolstack the xl.cfg:"dtdev" property
shall be used:

dtdev = [
    "/soc/i2c@e6508000",
]

xl.cfg:dtdev will contain all nodes which are under SCMI
management (not only those which are behind IOMMU).

Additionally, this patch adds documentation for the pre-existing
scmi-smc-passthrough command line option, which was previously
undocumented.

[0] https://developer.arm.com/documentation/den0056
[1] https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
[2] https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/access-controllers/access-controllers.yaml

Signed-off-by: Grygorii Strashko <grygorii_strashko@epam.com>
Signed-off-by: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
---

Changes in v13:
- fix typo in commit description

Changes in v12:
- rebase to the latest staging
- return EINVAL if xen,sci_type=scmi_smc_multiagent was set but
CONFIG_SCMI_SMC_MA not set
- put arm_sci_agent_id after v8r_el1_msa and before pad to preserve
order. Reduced pad from uint16_t to uint8_t to fill the gap (was 2
bytes before adding arm_sci_agent_id and left only 1, so used uint8_t
to preserve structure size)
- fix scmi_handle_call to use arm_smccc_guest_smc helper function
- update send_smc_message function to use new format of
arm_smccc_1_1_smc
- guard the Dom0 SCMI agent id setup in create_dom0() and MA related
funcitons with CONFIG_SCMI_SMC_MA
- fix the xen_scmi_config example in booting.txt, which placed properties
after subnodes and was rejected by dtc with "Properties must precede
subnodes"

Changes in v10:
- Fix tabs in MAINTAINERS file
- remove duplicate SPDX tag from scmi-shmem.c
- add cast to ARM_SMCCC_INVALID_PARAMETER to settle the sign since
ARM_SMCCC_INVALID_PARAMETER is -3 which is part of the spec and resp
is the default smccc call structure.
- update free_channel_list. Add spinlock to avoid race condition and
a comment with a description of the function work
- preserve error of smc_create_channel in scmi_probe
- check scmi shmem address alignment as wel as it is done for size
- check for d->arch.sci_data != NULL in scmi_handle_call
- use SCMI_SHMEM_MAPPED size for iomem_permit_access
- change len type to unsigned in shmem_{get|put}_message
- rename shmem_channel_is_free to shmem_channel_status
- add comment about skipping message status when getting message response
- Set correct agent_id ranges for dom0less and a toolstack. Agent_id 0
is not binded for dom0 so can be reused. Also mentioned that
UINT8_MAX (255) is treated as invalid agent_id.
- Split hypervisor and toolstack changes into separate commits
- move init list and spin init after initial checks in probe call
- fix typo in comments
- clean resources when sci_register returns an error.

Changes in v9:
- sort and refactor MAINTAINERS enties
- remove Spurious changes
- add extra check to avoid ASSERT when calling unmap_channel_memory
from assign device method
- set correct tx flag to SCMI_BASE_AGENT_PERMISSIONS_RESET when
freeing resources. Flag should be set to 1 according to the
section 4.2.2.12 [0].
- fix dt node copmaring
- moved channel->shmem check from ASSERT in unmap_memory_channel to
"if" statement. This will prevent firing ASSERT if
unmap_channel_memory was called twice on the same channel.

Changes in v8:
- update xen_scmi func_id in commit description
- updated documentation with the new DT format
- updated opt_dom0_scmi_agent_id setting to avoid it to be equal
SCMI_AGENT_ID_INVALID.
- changed SCMI_AGENT_ID_INVALID from 0xff to UINT8_MAX which makes
code more clear showing that UINT8_MAX is theated like invalid
agent_id and couldn't be used. Also excluded SCMI_AGENT_ID_INVALID
from acceptable value range
- remove outdated xen,config property ignore, added xen,sci compatible
to skip_matches in handle_node
- add documentation for pre-existing scmi-smc-passthrough command line
option in alphabetically correct location (in 's' section)
- add note to commit description about documentation for previously
undocumented scmi-smc-passthrough
- Fix SMC IDs in DT examples (Xen management uses 0x82000003, Dom0 uses 0x82000002)
- Add explicit note explaining why Dom0 and Xen channels do not conflict
- Document dom0less multi-agent configuration example (xen,sci_type / xen,sci-agent-id)
- Add scmi_xen node to agent-discovery example with #scmi-secondary-agents-cells = 2
- Drop dom0=sci-agent-id command line handling; Dom0 SCMI is now enabled via
  xen,dom0-sci-agent-id in the xen,sci DT container
- Refresh docs and examples to mention the DT property instead of the cmdline option

Changes in v7:
- rework scmi nodes for xen to match on compatible string instead of
the direct path

Changes in v6:
- updated scmi-shmem to use io.h from generic location
- update scmi_agent_id parameter to be provided inside dom0= parameter
list and have the following format "dom0=sci-agent-id=0"
This change was done as a response for Stefano comment and
requires a lot of code changes, but produces much cleaner solution
that's why I've added it to the code.
- fix file comments and return codes
- fix lenght checks in shmem_{get,put}_message to use offsetof
- remove len member from scmi_channel structure as it is not used
- set scmi-secondary-agents property to be mandatory since if no
secondary agents were provided then there is no sence to enable scmi
when no secondary agents are populated to the Domains
- update documentation in booting.txt, added xen_scmi node to the
example
- adjust d->arch.sci_enabled value in scmi_domain_destroy
- fix lock management in smc_create_channel call
- avoid extra map_channel_memory command for Xen management channel
because collect_agent_id call unmaps memory if DOMID_XEN is not
set. So for Xen management channel we can init domain_id ad DOMID_XEN
before calling collect_agent_id so memory shouldn't be unmapped.

Changes in v5:
- fix device-tree example format in booting.txt, added ";" after "}".
- update define in scmi-proto.h
- update define in scmi-shmem.h file
- scmi_assign_device - do not ignore -EOPNOTSUPP return
code of the do_smc_xfer
- remove overwriting agent_channel->agent_id after
SCMI_BASE_DISCOVER_AGENT call
- add multi-agent files to the MAINTAINERS
- add SCMI multi-agent description to the SUPPORT.md
- handle ARM_SMCCC_INVALID_PARAMETER return code and return -EINVAL
for smc call
- updated collect_agents function. Set agent_id parameter as optional
in scmi-secondary-agents device-tree property
- introduce "#scmi-secondary-agents-cells" parameter to set if
agent_id was provided
- reanme xen,scmi-secondary-agents property to scmi-secondary-agents
- move memcpu_toio/fromio for the generic place
- update Xen to get management channel from /chosen/xen,config node
- get hypervisor channnel from node instead of using hardcoded
- update handling scmi and shmem nodes for the domain
- Set multi-agent driver to support only Arm64

Changes in v4:
- toolstack comments from Anthony PERARD
- added dom0less support
- added doc for "xen,scmi-secondary-agents"

 MAINTAINERS                                 |   1 +
 SUPPORT.md                                  |  11 +
 docs/misc/arm/device-tree/booting.txt       | 197 +++++
 xen/arch/arm/dom0less-build.c               |  17 +
 xen/arch/arm/domain_build.c                 |  43 ++
 xen/arch/arm/firmware/Kconfig               |  12 +
 xen/arch/arm/firmware/Makefile              |   1 +
 xen/arch/arm/firmware/scmi-proto.h          | 164 ++++
 xen/arch/arm/firmware/scmi-shmem.c          | 118 +++
 xen/arch/arm/firmware/scmi-shmem.h          |  45 ++
 xen/arch/arm/firmware/scmi-smc-multiagent.c | 816 ++++++++++++++++++++
 xen/include/public/arch-arm.h               |   5 +-
 12 files changed, 1429 insertions(+), 1 deletion(-)
 create mode 100644 xen/arch/arm/firmware/scmi-proto.h
 create mode 100644 xen/arch/arm/firmware/scmi-shmem.c
 create mode 100644 xen/arch/arm/firmware/scmi-shmem.h
 create mode 100644 xen/arch/arm/firmware/scmi-smc-multiagent.c

diff --git a/MAINTAINERS b/MAINTAINERS
index c60afc93a5..cd6d1243bd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -534,6 +534,7 @@ SCI MEDIATORS
 R:	Oleksii Moisieiev <oleksii_moisieiev@epam.com>
 S:	Supported
 F:	xen/arch/arm/firmware/sci.c
+F:	xen/arch/arm/firmware/scmi-*.[ch]
 F:	xen/arch/arm/include/asm/firmware/sci.h
 
 SEABIOS UPSTREAM
diff --git a/SUPPORT.md b/SUPPORT.md
index eb07332462..dc41a6b2f3 100644
--- a/SUPPORT.md
+++ b/SUPPORT.md
@@ -972,6 +972,17 @@ by hwdom. Some platforms use SCMI for access to system-level resources.
 
     Status: Supported
 
+### Arm: SCMI SMC multi-agent support
+
+Enable support for the multi-agent configuration of the EL3 Firmware, which
+allows Xen to provide an SCMI interface to the Domains.
+Xen manages access permissions to the HW resources and provides an SCMI interface
+to the Domains. Each Domain is represented as a separate Agent, which can
+communicate with EL3 Firmware using a dedicated shared memory region, and
+notifications are passed through by Xen.
+
+    Status, ARM64: Tech Preview
+
 ### ARM: Guest PSCI support
 
 Emulated PSCI interface exposed to guests. We support all mandatory
diff --git a/docs/misc/arm/device-tree/booting.txt b/docs/misc/arm/device-tree/booting.txt
index bcb06bc796..1d0382a865 100644
--- a/docs/misc/arm/device-tree/booting.txt
+++ b/docs/misc/arm/device-tree/booting.txt
@@ -331,6 +331,21 @@ with the following properties:
     Should be used together with scmi-smc-passthrough Xen command line
     option.
 
+    - "scmi_smc_multiagent"
+
+    Enables ARM SCMI SMC multi-agent support for the guest by enabling SCMI over
+    SMC calls forwarding from domain to the EL3 firmware (like ARM
+    Trusted Firmware-A) with a multi SCMI OSPM agent support.
+    The SCMI agent_id should be specified for the guest with "xen,sci-agent-id"
+    property.
+
+- "xen,sci-agent-id"
+
+    Specifies ARM SCMI agent id for the guest. This option is mandatory if the
+    SCMI SMC "scmi_smc_multiagent" support is enabled for the guest. The agent ids
+    of guest must be unique and in the range [0..254]. UINT8_MAX (255) is
+    treated as invalid.
+
 - v8r_el1_msa
 
     A string property specifying whether, on Armv8-R systems at EL1, a domain
@@ -847,3 +862,185 @@ The automatically allocated static shared memory will get mapped at
 0x80000000 in DomU1 guest physical address space, and at 0x90000000 in DomU2
 guest physical address space. DomU1 is explicitly defined as the owner domain,
 and DomU2 is the borrower domain.
+
+SCMI SMC multi-agent support
+============================
+
+For enabling the ARM SCMI SMC multi-agent support (enabled by CONFIG_SCMI_SMC_MA)
+the Xen specific SCMI Agent's configuration shall be provided in the Host DT
+according to the SCMI compliant EL3 Firmware specification with ARM SMC/HVC
+transport. The SCMI configuration must live under the Xen SCMI container
+"xen,sci" beneath "/chosen" (for example "/chosen/xen/xen_scmi_config/scmi"). The
+Xen SCMI mediator will bind only to the "arm,scmi-smc" node that is a child of
+this "xen,sci" container; any other "arm,scmi-smc" nodes (for example under
+"/firmware") are ignored to avoid stealing the host's SCMI OSPM instance.
+
+- scmi-secondary-agents
+
+    Defines a set of SCMI agents configuration supported by SCMI EL3 FW and
+    available for Xen. Each Agent defined as triple consisting of:
+    SMC/HVC function_id assigned for the agent transport ("arm,smc-id"),
+    phandle to SCMI SHM assigned for the agent transport ("arm,scmi-shmem"),
+    SCMI agent_id (optional) if not set - Xen will determine Agent ID for
+    each provided channel using BASE_DISCOVER_AGENT message.
+
+- xen,dom0-sci-agent-id
+
+    Optional. Specifies the Dom0/hwdom SCMI agent_id inside the ``xen,sci``
+    container. When provided, Dom0 will be configured for SCMI multi-agent
+    support; when omitted, SCMI remains disabled for Dom0. The value must
+    match the ``func_id`` and shmem pairing that EL3 firmware exposes for
+    Dom0 (for example via ``/firmware/scmi``).
+
+As an example:
+
+/ {
+    chosen {
+        xen {
+            ranges;
+            xen_scmi_config {
+                compatible = "xen,sci";
+                #address-cells = <2>;
+                #size-cells = <2>;
+                ranges;
+
+                xen,dom0-sci-agent-id = <0>; <--- dom0 agent id
+                scmi-secondary-agents = <
+                    0x82000002 &scmi_shm_0 0
+                    0x82000004 &scmi_shm_2 2
+                    0x82000005 &scmi_shm_3 3>; <--- func_id, shmem, agent_id
+                #scmi-secondary-agents-cells = <3>;
+
+                scmi_shm_0: sram@47ff0000 {
+                    compatible = "arm,scmi-shmem";
+                    reg = <0x0 0x47ff0000 0x0 0x1000>;
+                };
+
+                /* Xen SCMI management channel */
+                scmi_shm_1: sram@47ff1000 {
+                    compatible = "arm,scmi-shmem";
+                    reg = <0x0 0x47ff1000 0x0 0x1000>;
+                };
+
+                scmi_shm_2: sram@47ff2000 {
+                    compatible = "arm,scmi-shmem";
+                    reg = <0x0 0x47ff2000 0x0 0x1000>;
+                };
+
+                scmi_shm_3: sram@47ff3000 {
+                    compatible = "arm,scmi-shmem";
+                    reg = <0x0 0x47ff3000 0x0 0x1000>;
+                };
+
+                scmi_xen: scmi {
+                    compatible = "arm,scmi-smc";
+                    arm,smc-id = <0x82000003>; <--- Xen management agent func_id
+                    #address-cells = <1>;
+                    #size-cells = <0>;
+                    #access-controller-cells = <1>;
+                    shmem = <&scmi_shm_1>; <--- Xen management agent shmem
+                };
+            };
+        };
+    };
+};
+
+Note: This example keeps the Host DT unchanged for Dom0 and baremetal Linux
+by using func_id 0x82000002 / shmem 0x47ff0000 for Dom0, while Xen uses a
+separate privileged channel func_id 0x82000003 / shmem 0x47ff1000. EL3
+firmware enforces permissions per agent_id, so there is no conflict between
+Dom0 and Xen channels.
+
+- #scmi-secondary-agents-cells
+
+    Defines whether Agent_id is set in the "scmi-secondary-agents" property.
+    Possible values are: 2, 3.
+    When set to 3 (the default), expect agent_id to be present in the secondary
+    agents list.
+    When set to 2, agent_id will be discovered for each channel using
+    BASE_DISCOVER_AGENT message.
+
+
+Example:
+
+/ {
+    chosen {
+        xen {
+            ranges;
+            xen_scmi_config {
+                compatible = "xen,sci";
+                #address-cells = <2>;
+                #size-cells = <2>;
+                ranges;
+
+                /* Shared memory nodes as in the previous example */
+
+                scmi-secondary-agents = <
+                    0x82000002 &scmi_shm_0
+                    0x82000004 &scmi_shm_2
+                    0x82000005 &scmi_shm_3
+                    0x82000006 &scmi_shm_4>;
+                #scmi-secondary-agents-cells = <2>;
+
+                scmi_xen: scmi {
+                    compatible = "arm,scmi-smc";
+                    arm,smc-id = <0x82000003>; <--- Xen management agent func_id
+                    #address-cells = <1>;
+                    #size-cells = <0>;
+                    #access-controller-cells = <1>;
+                    shmem = <&scmi_shm_1>; <--- Xen management agent shmem
+                };
+            };
+        };
+    };
+};
+
+Dom0less example (multi-agent)
+-------------------------------
+
+Below is a minimal dom0less configuration showing how to enable SCMI SMC
+multi-agent for a pre-defined guest domain using xen,sci_type and
+xen,sci-agent-id, together with the Xen SCMI container:
+
+chosen {
+    xen {
+        ranges;
+        xen_scmi_config {
+            compatible = "xen,sci";
+            #address-cells = <2>;
+            #size-cells = <2>;
+            ranges;
+
+            /* Xen management channel shared memory */
+            scmi_shm_1: sram@47ff1000 {
+                compatible = "arm,scmi-shmem";
+                reg = <0x0 0x47ff1000 0x0 0x1000>;
+            };
+
+            scmi_shm_domu: sram@47ff2000 {
+                compatible = "arm,scmi-shmem";
+                reg = <0x0 0x47ff2000 0x0 0x1000>;
+            };
+
+            scmi-secondary-agents = <
+                0x82000004 &scmi_shm_domu 2>;
+            #scmi-secondary-agents-cells = <3>;
+
+            scmi_xen: scmi {
+                compatible = "arm,scmi-smc";
+                arm,smc-id = <0x82000003>;
+                #address-cells = <1>;
+                #size-cells = <0>;
+                #access-controller-cells = <1>;
+                shmem = <&scmi_shm_1>;
+            };
+        };
+    };
+
+    xen,domain@1 {
+        compatible = "xen,domain";
+        xen,sci_type = "scmi_smc_multiagent";
+        xen,sci-agent-id = <2>;
+        /* Additional domain properties (memory, cpus, kernels, etc.) */
+    };
+};
diff --git a/xen/arch/arm/dom0less-build.c b/xen/arch/arm/dom0less-build.c
index 3f48f74226..50e2516b9e 100644
--- a/xen/arch/arm/dom0less-build.c
+++ b/xen/arch/arm/dom0less-build.c
@@ -292,6 +292,23 @@ static int __init domu_dt_sci_parse(struct dt_device_node *node,
 
         d_cfg->arch.arm_sci_type = XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC;
     }
+    else if ( !strcmp(sci_type, "scmi_smc_multiagent") )
+    {
+        uint32_t agent_id = 0;
+
+        if ( !IS_ENABLED(CONFIG_SCMI_SMC_MA) )
+        {
+            printk(XENLOG_ERR "xen,sci_type=scmi_smc_multiagent requested, but CONFIG_SCMI_SMC_MA not set\n");
+            return -EINVAL;
+        }
+
+        if ( !dt_property_read_u32(node, "xen,sci-agent-id", &agent_id) ||
+             agent_id >= UINT8_MAX )
+            return -EINVAL;
+
+        d_cfg->arch.arm_sci_type = XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC_MA;
+        d_cfg->arch.arm_sci_agent_id = agent_id;
+    }
     else
     {
         printk(XENLOG_ERR "xen,sci_type in not valid (%s) for domain %s\n",
diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
index 72d5316180..297c46d5a2 100644
--- a/xen/arch/arm/domain_build.c
+++ b/xen/arch/arm/domain_build.c
@@ -87,6 +87,39 @@ int __init parse_arch_dom0_param(const char *s, const char *e)
     return -EINVAL;
 }
 
+#ifdef CONFIG_SCMI_SMC_MA
+/* SCMI agent ID for dom0 obtained from xen,sci container */
+#define SCMI_AGENT_ID_INVALID UINT8_MAX
+
+static uint8_t __init get_dom0_scmi_agent_id(void)
+{
+    const struct dt_device_node *config_node;
+    u32 val;
+    const struct dt_property *prop;
+
+    config_node = dt_find_compatible_node(NULL, NULL, "xen,sci");
+    if ( !config_node )
+        return SCMI_AGENT_ID_INVALID;
+
+    prop = dt_find_property(config_node, "xen,dom0-sci-agent-id", NULL);
+    if ( !prop )
+        return SCMI_AGENT_ID_INVALID;
+
+    if ( !dt_property_read_u32(config_node, "xen,dom0-sci-agent-id", &val) )
+        return SCMI_AGENT_ID_INVALID;
+
+    if ( val >= SCMI_AGENT_ID_INVALID )
+    {
+         printk(XENLOG_WARNING
+             "Invalid xen,dom0-sci-agent-id=%u, SCMI disabled for Dom0\n",
+             val);
+        return SCMI_AGENT_ID_INVALID;
+    }
+
+    return val;
+}
+#endif /* CONFIG_SCMI_SMC_MA */
+
 /* Override macros from asm/page.h to make them work with mfn_t */
 #undef virt_to_mfn
 #define virt_to_mfn(va) _mfn(__virt_to_mfn(va))
@@ -1479,6 +1512,7 @@ static int __init handle_node(struct domain *d, struct kernel_info *kinfo,
         DT_MATCH_TYPE("memory"),
         /* The memory mapped timer is not supported by Xen. */
         DT_MATCH_COMPATIBLE("arm,armv7-timer-mem"),
+        DT_MATCH_COMPATIBLE("xen,sci"),
         { /* sentinel */ },
     };
     static const struct dt_device_match timer_matches[] __initconst =
@@ -1965,6 +1999,15 @@ void __init create_dom0(void)
     dom0_cfg.arch.tee_type = tee_get_type();
     dom0_cfg.max_vcpus = dom0_max_vcpus();
 
+#ifdef CONFIG_SCMI_SMC_MA
+    /* Set up SCMI agent ID if provided in the xen,sci container */
+    dom0_cfg.arch.arm_sci_agent_id = get_dom0_scmi_agent_id();
+    dom0_cfg.arch.arm_sci_type = (dom0_cfg.arch.arm_sci_agent_id !=
+                                  SCMI_AGENT_ID_INVALID) ?
+                                 XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC_MA :
+                                 XEN_DOMCTL_CONFIG_ARM_SCI_NONE;
+#endif
+
     if ( iommu_enabled )
         dom0_cfg.flags |= XEN_DOMCTL_CDF_iommu;
 
diff --git a/xen/arch/arm/firmware/Kconfig b/xen/arch/arm/firmware/Kconfig
index 5c5f0880c4..972cd9b173 100644
--- a/xen/arch/arm/firmware/Kconfig
+++ b/xen/arch/arm/firmware/Kconfig
@@ -29,6 +29,18 @@ config SCMI_SMC
 	  driver domain.
 	  Use with EL3 firmware which supports only single SCMI OSPM agent.
 
+config SCMI_SMC_MA
+	bool "Enable ARM SCMI SMC multi-agent driver"
+	depends on ARM_64
+	select ARM_SCI
+	help
+	  Enables SCMI SMC/HVC multi-agent in XEN to pass SCMI requests from Domains
+	  to EL3 firmware (TF-A) which supports multi-agent feature.
+	  This feature allows to enable SCMI per Domain using unique SCMI agent_id,
+	  so Domain is identified by EL3 firmware as an SCMI Agent and can access
+	  allowed platform resources through dedicated SMC/HVC Shared memory based
+	  transport.
+
 endchoice
 
 endmenu
diff --git a/xen/arch/arm/firmware/Makefile b/xen/arch/arm/firmware/Makefile
index 71bdefc24a..37927e690e 100644
--- a/xen/arch/arm/firmware/Makefile
+++ b/xen/arch/arm/firmware/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_ARM_SCI) += sci.o
 obj-$(CONFIG_SCMI_SMC) += scmi-smc.o
+obj-$(CONFIG_SCMI_SMC_MA) += scmi-shmem.o scmi-smc-multiagent.o
diff --git a/xen/arch/arm/firmware/scmi-proto.h b/xen/arch/arm/firmware/scmi-proto.h
new file mode 100644
index 0000000000..49f63cfc0a
--- /dev/null
+++ b/xen/arch/arm/firmware/scmi-proto.h
@@ -0,0 +1,164 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Arm System Control and Management Interface definitions
+ * Version 3.0 (DEN0056C)
+ *
+ * Copyright (c) 2025 EPAM Systems
+ */
+
+#ifndef ARM_FIRMWARE_SCMI_PROTO_H_
+#define ARM_FIRMWARE_SCMI_PROTO_H_
+
+#include <xen/stdint.h>
+
+#define SCMI_SHORT_NAME_MAX_SIZE 16
+
+/* SCMI status codes. See section 4.1.4 */
+#define SCMI_SUCCESS              0
+#define SCMI_NOT_SUPPORTED      (-1)
+#define SCMI_INVALID_PARAMETERS (-2)
+#define SCMI_DENIED             (-3)
+#define SCMI_NOT_FOUND          (-4)
+#define SCMI_OUT_OF_RANGE       (-5)
+#define SCMI_BUSY               (-6)
+#define SCMI_COMMS_ERROR        (-7)
+#define SCMI_GENERIC_ERROR      (-8)
+#define SCMI_HARDWARE_ERROR     (-9)
+#define SCMI_PROTOCOL_ERROR     (-10)
+
+/* Protocol IDs */
+#define SCMI_BASE_PROTOCOL 0x10
+
+/* Base protocol message IDs */
+#define SCMI_BASE_PROTOCOL_VERSION            0x0
+#define SCMI_BASE_PROTOCOL_ATTIBUTES          0x1
+#define SCMI_BASE_PROTOCOL_MESSAGE_ATTRIBUTES 0x2
+#define SCMI_BASE_DISCOVER_AGENT              0x7
+#define SCMI_BASE_SET_DEVICE_PERMISSIONS      0x9
+#define SCMI_BASE_RESET_AGENT_CONFIGURATION   0xB
+
+typedef struct scmi_msg_header {
+    uint8_t id;
+    uint8_t type;
+    uint8_t protocol;
+    uint32_t status;
+} scmi_msg_header_t;
+
+/* Table 2 Message header format */
+#define SCMI_HDR_ID    GENMASK(7, 0)
+#define SCMI_HDR_TYPE  GENMASK(9, 8)
+#define SCMI_HDR_PROTO GENMASK(17, 10)
+
+#define SCMI_FIELD_GET(_mask, _reg)                                            \
+    ((typeof(_mask))(((_reg) & (_mask)) >> (ffs64(_mask) - 1)))
+#define SCMI_FIELD_PREP(_mask, _val)                                           \
+    (((typeof(_mask))(_val) << (ffs64(_mask) - 1)) & (_mask))
+
+static inline uint32_t pack_scmi_header(scmi_msg_header_t *hdr)
+{
+    return SCMI_FIELD_PREP(SCMI_HDR_ID, hdr->id) |
+           SCMI_FIELD_PREP(SCMI_HDR_TYPE, hdr->type) |
+           SCMI_FIELD_PREP(SCMI_HDR_PROTO, hdr->protocol);
+}
+
+static inline void unpack_scmi_header(uint32_t msg_hdr, scmi_msg_header_t *hdr)
+{
+    hdr->id = SCMI_FIELD_GET(SCMI_HDR_ID, msg_hdr);
+    hdr->type = SCMI_FIELD_GET(SCMI_HDR_TYPE, msg_hdr);
+    hdr->protocol = SCMI_FIELD_GET(SCMI_HDR_PROTO, msg_hdr);
+}
+
+static inline int scmi_to_xen_errno(int scmi_status)
+{
+    if ( scmi_status == SCMI_SUCCESS )
+        return 0;
+
+    switch ( scmi_status )
+    {
+    case SCMI_NOT_SUPPORTED:
+        return -EOPNOTSUPP;
+    case SCMI_INVALID_PARAMETERS:
+        return -EINVAL;
+    case SCMI_DENIED:
+        return -EACCES;
+    case SCMI_NOT_FOUND:
+        return -ENOENT;
+    case SCMI_OUT_OF_RANGE:
+        return -ERANGE;
+    case SCMI_BUSY:
+        return -EBUSY;
+    case SCMI_COMMS_ERROR:
+        return -ENOTCONN;
+    case SCMI_GENERIC_ERROR:
+        return -EIO;
+    case SCMI_HARDWARE_ERROR:
+        return -ENXIO;
+    case SCMI_PROTOCOL_ERROR:
+        return -EBADMSG;
+    default:
+        return -EINVAL;
+    }
+}
+
+/* PROTOCOL_VERSION */
+#define SCMI_VERSION_MINOR GENMASK(15, 0)
+#define SCMI_VERSION_MAJOR GENMASK(31, 16)
+
+struct scmi_msg_prot_version_p2a {
+    uint32_t version;
+} __packed;
+
+/* BASE PROTOCOL_ATTRIBUTES */
+#define SCMI_BASE_ATTR_NUM_PROTO GENMASK(7, 0)
+#define SCMI_BASE_ATTR_NUM_AGENT GENMASK(15, 8)
+
+struct scmi_msg_base_attributes_p2a {
+    uint32_t attributes;
+} __packed;
+
+/*
+ * BASE_DISCOVER_AGENT
+ */
+#define SCMI_BASE_AGENT_ID_OWN 0xFFFFFFFF
+
+struct scmi_msg_base_discover_agent_a2p {
+    uint32_t agent_id;
+} __packed;
+
+struct scmi_msg_base_discover_agent_p2a {
+    uint32_t agent_id;
+    char name[SCMI_SHORT_NAME_MAX_SIZE];
+} __packed;
+
+/*
+ * BASE_SET_DEVICE_PERMISSIONS
+ */
+#define SCMI_BASE_DEVICE_ACCESS_ALLOW           BIT(0, UL)
+
+struct scmi_msg_base_set_device_permissions_a2p {
+    uint32_t agent_id;
+    uint32_t device_id;
+    uint32_t flags;
+} __packed;
+
+/*
+ * BASE_RESET_AGENT_CONFIGURATION
+ */
+#define SCMI_BASE_AGENT_PERMISSIONS_RESET       BIT(0, UL)
+
+struct scmi_msg_base_reset_agent_cfg_a2p {
+    uint32_t agent_id;
+    uint32_t flags;
+} __packed;
+
+#endif /* ARM_FIRMWARE_SCMI_PROTO_H_ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/arch/arm/firmware/scmi-shmem.c b/xen/arch/arm/firmware/scmi-shmem.c
new file mode 100644
index 0000000000..e36745a85e
--- /dev/null
+++ b/xen/arch/arm/firmware/scmi-shmem.c
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * SMC/HVC shmem transport implementation used by
+ * SCI SCMI multi-agent driver.
+ *
+ * Oleksii Moisieiev <oleksii_moisieiev@epam.com>
+ * Copyright (c) 2025 EPAM Systems
+ */
+
+#include <xen/err.h>
+#include <xen/io.h>
+#include <asm/io.h>
+
+#include "scmi-proto.h"
+#include "scmi-shmem.h"
+
+static inline int
+shmem_channel_status(const volatile struct scmi_shared_mem __iomem *shmem)
+{
+    return (readl(&shmem->channel_status) &
+            SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE) ? 0 : -EBUSY;
+}
+
+int shmem_put_message(volatile struct scmi_shared_mem __iomem *shmem,
+                      scmi_msg_header_t *hdr, void *data, unsigned int len)
+{
+    int ret;
+
+    if ( (len + offsetof(struct scmi_shared_mem, msg_payload)) >
+         SCMI_SHMEM_MAPPED_SIZE )
+    {
+        printk(XENLOG_ERR "scmi: Wrong size of smc message. Data is invalid\n");
+        return -EINVAL;
+    }
+
+    ret = shmem_channel_status(shmem);
+    if ( ret )
+        return ret;
+
+    writel_relaxed(0x0, &shmem->channel_status);
+    /* Writing 0x0 right now, but "shmem"_FLAG_INTR_ENABLED can be set */
+    writel_relaxed(0x0, &shmem->flags);
+    writel_relaxed(sizeof(shmem->msg_header) + len, &shmem->length);
+    writel(pack_scmi_header(hdr), &shmem->msg_header);
+
+    if ( len > 0 && data )
+        memcpy_toio(shmem->msg_payload, data, len);
+
+    return 0;
+}
+
+int shmem_get_response(const volatile struct scmi_shared_mem __iomem *shmem,
+                       scmi_msg_header_t *hdr, void *data, unsigned int len)
+{
+    int recv_len;
+    int ret;
+    /*
+     * First word of msg_payload carries the returned status; exclude it from
+     * recv_len so only the protocol payload is copied back to the caller.
+     */
+    int pad = sizeof(hdr->status);
+
+    if ( len >= SCMI_SHMEM_MAPPED_SIZE -
+         offsetof(struct scmi_shared_mem, msg_payload) )
+    {
+        printk(XENLOG_ERR
+               "scmi: Wrong size of input smc message. Data may be invalid\n");
+        return -EINVAL;
+    }
+
+    ret = shmem_channel_status(shmem);
+    if ( ret )
+        return ret;
+
+    recv_len = readl(&shmem->length) - sizeof(shmem->msg_header);
+
+    if ( recv_len < 0 )
+    {
+        printk(XENLOG_ERR
+               "scmi: Wrong size of smc message. Data may be invalid\n");
+        return -EINVAL;
+    }
+
+    unpack_scmi_header(readl(&shmem->msg_header), hdr);
+
+    hdr->status = readl(&shmem->msg_payload);
+    recv_len = recv_len > pad ? recv_len - pad : 0;
+
+    ret = scmi_to_xen_errno(hdr->status);
+    if ( ret )
+    {
+        printk(XENLOG_DEBUG "scmi: Error received: %d\n", ret);
+        return ret;
+    }
+
+    if ( recv_len > len )
+    {
+        printk(XENLOG_ERR
+               "scmi: Not enough buffer for message %d, expecting %d\n",
+               recv_len, len);
+        return -EINVAL;
+    }
+
+    if ( recv_len > 0 )
+        memcpy_fromio(data, shmem->msg_payload + pad, recv_len);
+
+    return 0;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/arch/arm/firmware/scmi-shmem.h b/xen/arch/arm/firmware/scmi-shmem.h
new file mode 100644
index 0000000000..722263aa77
--- /dev/null
+++ b/xen/arch/arm/firmware/scmi-shmem.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Arm System Control and Management Interface definitions
+ * Version 3.0 (DEN0056C)
+ * Shared Memory based Transport
+ *
+ * Copyright (c) 2024 EPAM Systems
+ */
+
+#ifndef ARM_FIRMWARE_SCMI_SHMEM_H_
+#define ARM_FIRMWARE_SCMI_SHMEM_H_
+
+#include <xen/stdint.h>
+
+#define SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE  BIT(0, UL)
+#define SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR BIT(1, UL)
+
+struct scmi_shared_mem {
+    uint32_t reserved;
+    uint32_t channel_status;
+    uint32_t reserved1[2];
+    uint32_t flags;
+    uint32_t length;
+    uint32_t msg_header;
+    uint8_t msg_payload[];
+};
+
+#define SCMI_SHMEM_MAPPED_SIZE PAGE_SIZE
+
+int shmem_put_message(volatile struct scmi_shared_mem __iomem *shmem,
+                      scmi_msg_header_t *hdr, void *data, unsigned int len);
+
+int shmem_get_response(const volatile struct scmi_shared_mem __iomem *shmem,
+                       scmi_msg_header_t *hdr, void *data, unsigned int len);
+#endif /* ARM_FIRMWARE_SCMI_SHMEM_H_ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/arch/arm/firmware/scmi-smc-multiagent.c b/xen/arch/arm/firmware/scmi-smc-multiagent.c
new file mode 100644
index 0000000000..0c5653fbc0
--- /dev/null
+++ b/xen/arch/arm/firmware/scmi-smc-multiagent.c
@@ -0,0 +1,816 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * SCI SCMI multi-agent driver, using SMC/HVC shmem as transport.
+ *
+ * Oleksii Moisieiev <oleksii_moisieiev@epam.com>
+ * Copyright (c) 2025 EPAM Systems
+ */
+
+#include <xen/acpi.h>
+
+#include <xen/device_tree.h>
+#include <xen/init.h>
+#include <xen/iocap.h>
+#include <xen/err.h>
+#include <xen/libfdt/libfdt.h>
+#include <xen/string.h>
+#include <xen/param.h>
+#include <xen/sched.h>
+#include <xen/vmap.h>
+
+#include <asm/firmware/sci.h>
+#include <asm/smccc.h>
+
+#include "scmi-proto.h"
+#include "scmi-shmem.h"
+
+#define SCMI_SECONDARY_AGENTS "scmi-secondary-agents"
+
+struct scmi_channel {
+    uint32_t agent_id;
+    uint32_t func_id;
+    domid_t domain_id;
+    uint64_t paddr;
+    struct scmi_shared_mem __iomem *shmem;
+    spinlock_t lock;
+    struct list_head list;
+};
+
+struct scmi_data {
+    struct list_head channel_list;
+    spinlock_t channel_list_lock;
+    uint32_t func_id;
+    bool initialized;
+    uint32_t shmem_phandle;
+    uint32_t hyp_channel_agent_id;
+    struct dt_device_node *dt_dev;
+};
+
+static struct scmi_data scmi_data;
+
+static bool scmi_is_under_xen_sci(const struct dt_device_node *node)
+{
+    const struct dt_device_node *p;
+
+    for ( p = node->parent; p; p = p->parent )
+        if ( dt_device_is_compatible(p, "xen,sci") )
+            return true;
+
+    return false;
+}
+
+static int send_smc_message(struct scmi_channel *chan_info,
+                            scmi_msg_header_t *hdr, void *data, int len)
+{
+    struct arm_smccc_res resp;
+    int ret;
+
+    ret = shmem_put_message(chan_info->shmem, hdr, data, len);
+    if ( ret )
+        return ret;
+
+    resp = arm_smccc_1_1_smc(chan_info->func_id, 0, 0, 0, 0, 0, 0, 0);
+
+    if ( resp.a0 == (unsigned long)ARM_SMCCC_INVALID_PARAMETER )
+        return -EINVAL;
+
+    if ( resp.a0 )
+        return -EOPNOTSUPP;
+
+    return 0;
+}
+
+static int do_smc_xfer(struct scmi_channel *chan_info, scmi_msg_header_t *hdr,
+                       void *tx_data, int tx_size, void *rx_data, int rx_size)
+{
+    int ret = 0;
+
+    ASSERT(chan_info && chan_info->shmem);
+
+    if ( !hdr )
+        return -EINVAL;
+
+    spin_lock(&chan_info->lock);
+
+    printk(XENLOG_DEBUG
+           "scmi: agent_id = %d msg_id = %x type = %d, proto = %x\n",
+           chan_info->agent_id, hdr->id, hdr->type, hdr->protocol);
+
+    ret = send_smc_message(chan_info, hdr, tx_data, tx_size);
+    if ( ret )
+        goto clean;
+
+    ret = shmem_get_response(chan_info->shmem, hdr, rx_data, rx_size);
+
+clean:
+    printk(XENLOG_DEBUG
+           "scmi: get smc response agent_id = %d msg_id = %x proto = %x res=%d\n",
+           chan_info->agent_id, hdr->id, hdr->protocol, ret);
+
+    spin_unlock(&chan_info->lock);
+
+    return ret;
+}
+
+static struct scmi_channel *get_channel_by_id(uint32_t agent_id)
+{
+    struct scmi_channel *curr;
+    bool found = false;
+
+    spin_lock(&scmi_data.channel_list_lock);
+    list_for_each_entry(curr, &scmi_data.channel_list, list)
+    {
+        if ( curr->agent_id == agent_id )
+        {
+            found = true;
+            break;
+        }
+    }
+
+    spin_unlock(&scmi_data.channel_list_lock);
+    if ( found )
+        return curr;
+
+    return NULL;
+}
+
+static struct scmi_channel *acquire_scmi_channel(struct domain *d,
+                                                 uint32_t agent_id)
+{
+    struct scmi_channel *curr;
+    struct scmi_channel *ret = ERR_PTR(-ENOENT);
+
+    spin_lock(&scmi_data.channel_list_lock);
+    list_for_each_entry(curr, &scmi_data.channel_list, list)
+    {
+        if ( curr->agent_id == agent_id )
+        {
+            if ( curr->domain_id != DOMID_INVALID )
+            {
+                ret = ERR_PTR(-EEXIST);
+                break;
+            }
+
+            curr->domain_id = d->domain_id;
+            ret = curr;
+            break;
+        }
+    }
+
+    spin_unlock(&scmi_data.channel_list_lock);
+
+    return ret;
+}
+
+static void relinquish_scmi_channel(struct scmi_channel *channel)
+{
+    ASSERT(channel != NULL);
+
+    spin_lock(&scmi_data.channel_list_lock);
+    channel->domain_id = DOMID_INVALID;
+    spin_unlock(&scmi_data.channel_list_lock);
+}
+
+static int map_channel_memory(struct scmi_channel *channel)
+{
+    ASSERT(channel && channel->paddr);
+    channel->shmem = ioremap_nocache(channel->paddr, SCMI_SHMEM_MAPPED_SIZE);
+    if ( !channel->shmem )
+        return -ENOMEM;
+
+    channel->shmem->channel_status = SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE;
+    printk(XENLOG_DEBUG "scmi: Got shmem %lx after vmap %p\n", channel->paddr,
+           channel->shmem);
+
+    return 0;
+}
+
+static void unmap_channel_memory(struct scmi_channel *channel)
+{
+    ASSERT(channel);
+
+    if ( !channel->shmem )
+        return;
+
+    iounmap(channel->shmem);
+    channel->shmem = NULL;
+}
+
+static struct scmi_channel *smc_create_channel(uint32_t agent_id,
+                                               uint32_t func_id, uint64_t addr)
+{
+    struct scmi_channel *channel, *curr;
+
+    spin_lock(&scmi_data.channel_list_lock);
+
+    /* Check if channel already exists while holding the lock */
+    list_for_each_entry(curr, &scmi_data.channel_list, list)
+    {
+        if ( curr->agent_id == agent_id )
+        {
+            spin_unlock(&scmi_data.channel_list_lock);
+            return ERR_PTR(-EEXIST);
+        }
+    }
+
+    channel = xmalloc(struct scmi_channel);
+    if ( !channel )
+    {
+        spin_unlock(&scmi_data.channel_list_lock);
+        return ERR_PTR(-ENOMEM);
+    }
+
+    spin_lock_init(&channel->lock);
+    channel->agent_id = agent_id;
+    channel->func_id = func_id;
+    channel->domain_id = DOMID_INVALID;
+    channel->shmem = NULL;
+    channel->paddr = addr;
+    list_add_tail(&channel->list, &scmi_data.channel_list);
+
+    spin_unlock(&scmi_data.channel_list_lock);
+    return channel;
+}
+
+static void free_channel_list(void)
+{
+    struct scmi_channel *curr, *_curr;
+    /*
+     * Called only on the __init error path, before any runtime users exist,
+     * so no other thread is iterating channel_list. Keep the lock for the
+     * whole drain for clarity and to avoid misleading readers about possible
+     * concurrent access.
+     */
+    spin_lock(&scmi_data.channel_list_lock);
+    list_for_each_entry_safe(curr, _curr, &scmi_data.channel_list, list)
+    {
+        list_del(&curr->list);
+        xfree(curr);
+    }
+    spin_unlock(&scmi_data.channel_list_lock);
+}
+
+static int __init
+scmi_dt_read_hyp_channel_addr(struct dt_device_node *scmi_node, u64 *addr,
+                              u64 *size)
+{
+    struct dt_device_node *shmem_node;
+    const __be32 *prop;
+
+    prop = dt_get_property(scmi_node, "shmem", NULL);
+    if ( !prop )
+        return -EINVAL;
+
+    shmem_node = dt_find_node_by_phandle(be32_to_cpu(*prop));
+    if ( IS_ERR_OR_NULL(shmem_node) )
+    {
+        printk(XENLOG_ERR
+               "scmi: Device tree error, can't parse reserved memory %ld\n",
+               PTR_ERR(shmem_node));
+        return PTR_ERR(shmem_node);
+    }
+
+    return dt_device_get_address(shmem_node, 0, addr, size);
+}
+
+/*
+ * Handle Dom0 SCMI specific DT nodes
+ *
+ * Make a decision on copying SCMI specific nodes into Dom0 device tree.
+ * For SCMI multi-agent case:
+ * - shmem nodes will not be copied and generated instead if SCMI
+ *   is enabled for Dom0
+ * - scmi node will be copied if SCMI is enabled for Dom0
+ */
+static bool scmi_dt_handle_node(struct domain *d, struct dt_device_node *node)
+{
+    static const struct dt_device_match shmem_matches[] __initconst = {
+        DT_MATCH_COMPATIBLE("arm,scmi-shmem"),
+        { /* sentinel */ },
+    };
+    static const struct dt_device_match scmi_matches[] __initconst = {
+        DT_MATCH_PATH("/firmware/scmi"),
+        { /* sentinel */ },
+    };
+
+    if ( !scmi_data.initialized )
+        return false;
+
+    /* skip scmi shmem node for dom0 if scmi not enabled */
+    if ( dt_match_node(shmem_matches, node) && !sci_domain_is_enabled(d) )
+    {
+        dt_dprintk("  Skip scmi shmem node\n");
+        return true;
+    }
+
+    /* drop scmi if not enabled */
+    if ( dt_match_node(scmi_matches, node) && !sci_domain_is_enabled(d) )
+    {
+        dt_dprintk("  Skip scmi node\n");
+        return true;
+    }
+
+    return false;
+}
+
+static int scmi_assign_device(uint32_t agent_id, uint32_t device_id,
+                              uint32_t flags)
+{
+    struct scmi_msg_base_set_device_permissions_a2p tx;
+    struct scmi_channel *channel;
+    scmi_msg_header_t hdr;
+
+    channel = get_channel_by_id(scmi_data.hyp_channel_agent_id);
+    if ( !channel )
+        return -EINVAL;
+
+    hdr.id = SCMI_BASE_SET_DEVICE_PERMISSIONS;
+    hdr.type = 0;
+    hdr.protocol = SCMI_BASE_PROTOCOL;
+
+    tx.agent_id = agent_id;
+    tx.device_id = device_id;
+    tx.flags = flags;
+
+    return do_smc_xfer(channel, &hdr, &tx, sizeof(tx), NULL, 0);
+}
+
+static int scmi_dt_assign_device(struct domain *d,
+                                 struct dt_phandle_args *ac_spec)
+{
+    struct scmi_channel *agent_channel;
+    uint32_t scmi_device_id = ac_spec->args[0];
+    int ret;
+
+    if ( !d->arch.sci_data )
+        return 0;
+
+    /* The access-controllers is specified for DT dev, but it's not a SCMI */
+    if ( !scmi_data.dt_dev ||
+         !dt_node_path_is_equal(ac_spec->np, scmi_data.dt_dev->full_name) )
+        return 0;
+
+    agent_channel = d->arch.sci_data;
+
+    spin_lock(&agent_channel->lock);
+
+    ret = scmi_assign_device(agent_channel->agent_id, scmi_device_id,
+                             SCMI_BASE_DEVICE_ACCESS_ALLOW);
+    if ( ret )
+    {
+        printk(XENLOG_ERR
+               "scmi: could not assign dev for %pd agent:%d dev_id:%u (%d)",
+               d, agent_channel->agent_id, scmi_device_id, ret);
+    }
+
+    spin_unlock(&agent_channel->lock);
+    return ret;
+}
+
+static int collect_agent_id(struct scmi_channel *agent_channel)
+{
+    int ret;
+    scmi_msg_header_t hdr;
+    struct scmi_msg_base_discover_agent_p2a da_rx;
+    struct scmi_msg_base_discover_agent_a2p da_tx;
+
+    ret = map_channel_memory(agent_channel);
+    if ( ret )
+        return ret;
+
+    hdr.id = SCMI_BASE_DISCOVER_AGENT;
+    hdr.type = 0;
+    hdr.protocol = SCMI_BASE_PROTOCOL;
+
+    da_tx.agent_id = agent_channel->agent_id;
+
+    ret = do_smc_xfer(agent_channel, &hdr, &da_tx, sizeof(da_tx), &da_rx,
+                        sizeof(da_rx));
+    if ( agent_channel->domain_id != DOMID_XEN )
+        unmap_channel_memory(agent_channel);
+    if ( ret )
+        return ret;
+
+    printk(XENLOG_DEBUG "id=0x%x name=%s\n", da_rx.agent_id, da_rx.name);
+    agent_channel->agent_id = da_rx.agent_id;
+    return 0;
+}
+
+static __init int collect_agents(struct dt_device_node *scmi_node)
+{
+    const struct dt_device_node *config_node;
+    const __be32 *prop;
+    uint32_t len;
+    const __be32 *end;
+    uint32_t cells_per_entry = 3; /* Default to 3 cells if property is absent. */
+
+    config_node = dt_find_compatible_node(NULL, NULL, "xen,sci");
+    if ( !config_node )
+    {
+        printk(XENLOG_WARNING "scmi: xen,sci node not found, no agents to collect.\n");
+        return -ENOENT;
+    }
+
+    /* Check for the optional '#scmi-secondary-agents-cells' property. */
+    if ( dt_property_read_u32(config_node, "#scmi-secondary-agents-cells",
+                              &cells_per_entry) )
+    {
+        if ( cells_per_entry != 2 && cells_per_entry != 3 )
+        {
+            printk(XENLOG_ERR "scmi: Invalid #scmi-secondary-agents-cells value: %u\n",
+                   cells_per_entry);
+            return -EINVAL;
+        }
+    }
+
+    prop = dt_get_property(config_node, SCMI_SECONDARY_AGENTS, &len);
+    if ( !prop )
+    {
+        printk(XENLOG_ERR "scmi: No %s property found, no agents to collect.\n",
+               SCMI_SECONDARY_AGENTS);
+        return -EINVAL;
+    }
+
+    /* Validate that the property length is a multiple of the cell size. */
+    if ( len == 0 || len % (cells_per_entry * sizeof(uint32_t)) != 0 )
+    {
+        printk(XENLOG_ERR "scmi: Invalid length of %s property: %u for %u cells per entry\n",
+               SCMI_SECONDARY_AGENTS, len, cells_per_entry);
+        return -EINVAL;
+    }
+
+    end = (const __be32 *)((const u8 *)prop + len);
+
+    for ( ; prop < end; )
+    {
+        uint32_t agent_id;
+        uint32_t smc_id;
+        uint32_t shmem_phandle;
+        struct dt_device_node *node;
+        u64 addr, size;
+        int ret;
+        struct scmi_channel *agent_channel;
+
+        smc_id = be32_to_cpu(*prop++);
+        shmem_phandle = be32_to_cpu(*prop++);
+
+        if ( cells_per_entry == 3 )
+            agent_id = be32_to_cpu(*prop++);
+        else
+            agent_id = SCMI_BASE_AGENT_ID_OWN;
+
+        node = dt_find_node_by_phandle(shmem_phandle);
+        if ( !node )
+        {
+            printk(XENLOG_ERR "scmi: Could not find shmem node for agent %u\n",
+                   agent_id);
+            return -EINVAL;
+        }
+
+        ret = dt_device_get_address(node, 0, &addr, &size);
+        if ( ret )
+        {
+            printk(XENLOG_ERR
+                   "scmi: Could not read shmem address for agent %u: %d\n",
+                   agent_id, ret);
+            return ret;
+        }
+
+        if ( !IS_ALIGNED(size, SCMI_SHMEM_MAPPED_SIZE) ||
+             !IS_ALIGNED(addr, SCMI_SHMEM_MAPPED_SIZE) )
+        {
+            printk(XENLOG_ERR "scmi: shmem memory is not aligned\n");
+            return -EINVAL;
+        }
+
+        agent_channel = smc_create_channel(agent_id, smc_id, addr);
+        if ( IS_ERR(agent_channel) )
+        {
+            printk(XENLOG_ERR "scmi: Could not create channel for agent %u: %ld\n",
+                   agent_id, PTR_ERR(agent_channel));
+            return PTR_ERR(agent_channel);
+        }
+
+        if ( cells_per_entry == 2 )
+        {
+            ret = collect_agent_id(agent_channel);
+            if ( ret )
+                return ret;
+        }
+
+        printk(XENLOG_DEBUG "scmi: Agent %u SMC %X addr %lx\n", agent_channel->agent_id,
+               smc_id, (unsigned long)addr);
+    }
+
+    return 0;
+}
+
+static int scmi_domain_init(struct domain *d,
+                            struct xen_domctl_createdomain *config)
+{
+    struct scmi_channel *channel;
+    int ret;
+
+    if ( !scmi_data.initialized )
+        return 0;
+
+    /*
+     * SCMI support is configured via:
+     * - For dom0: xen,dom0-sci-agent-id property under the xen,sci container
+     * - For dom0less: xen,sci-agent-id in the domain node
+     * The config->arch.arm_sci_type and config->arch.arm_sci_agent_id
+     * are already set by domain_build.c or dom0less-build.c
+     */
+
+    if ( config->arch.arm_sci_type == XEN_DOMCTL_CONFIG_ARM_SCI_NONE )
+        return 0;
+
+    channel = acquire_scmi_channel(d, config->arch.arm_sci_agent_id);
+    if ( IS_ERR(channel) )
+    {
+        printk(XENLOG_ERR
+               "scmi: Failed to acquire SCMI channel for agent_id %u: %ld\n",
+               config->arch.arm_sci_agent_id, PTR_ERR(channel));
+        return PTR_ERR(channel);
+    }
+
+    printk(XENLOG_INFO
+           "scmi: Acquire channel id = 0x%x, domain_id = %d paddr = 0x%lx\n",
+           channel->agent_id, channel->domain_id, channel->paddr);
+
+    /*
+     * Dom0 (if present) needs to have an access to the guest memory range
+     * to satisfy iomem_access_permitted() check in XEN_DOMCTL_iomem_permission
+     * domctl.
+     */
+    if ( hardware_domain && !is_hardware_domain(d) )
+    {
+        ret = iomem_permit_access(hardware_domain, paddr_to_pfn(channel->paddr),
+                                  paddr_to_pfn(channel->paddr +
+                                  SCMI_SHMEM_MAPPED_SIZE - 1));
+        if ( ret )
+            goto error;
+    }
+
+    d->arch.sci_data = channel;
+    d->arch.sci_enabled = true;
+
+    return 0;
+
+error:
+    relinquish_scmi_channel(channel);
+    return ret;
+}
+
+int scmi_domain_sanitise_config(struct xen_domctl_createdomain *config)
+{
+    if ( config->arch.arm_sci_type != XEN_DOMCTL_CONFIG_ARM_SCI_NONE &&
+         config->arch.arm_sci_type != XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC_MA )
+    {
+        dprintk(XENLOG_INFO, "scmi: Unsupported ARM_SCI type\n");
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+static int scmi_relinquish_resources(struct domain *d)
+{
+    int ret;
+    struct scmi_channel *channel, *agent_channel;
+    scmi_msg_header_t hdr;
+    struct scmi_msg_base_reset_agent_cfg_a2p tx;
+
+    if ( !d->arch.sci_data )
+        return 0;
+
+    agent_channel = d->arch.sci_data;
+
+    spin_lock(&agent_channel->lock);
+    tx.agent_id = agent_channel->agent_id;
+    spin_unlock(&agent_channel->lock);
+
+    channel = get_channel_by_id(scmi_data.hyp_channel_agent_id);
+    if ( !channel )
+    {
+        printk(XENLOG_ERR
+               "scmi: Unable to get Hypervisor scmi channel for domain %d\n",
+               d->domain_id);
+        return -EINVAL;
+    }
+
+    hdr.id = SCMI_BASE_RESET_AGENT_CONFIGURATION;
+    hdr.type = 0;
+    hdr.protocol = SCMI_BASE_PROTOCOL;
+
+    tx.flags = SCMI_BASE_AGENT_PERMISSIONS_RESET;
+
+    ret = do_smc_xfer(channel, &hdr, &tx, sizeof(tx), NULL, 0);
+    if ( ret == -EOPNOTSUPP )
+        return 0;
+
+    return ret;
+}
+
+static void scmi_domain_destroy(struct domain *d)
+{
+    struct scmi_channel *channel;
+
+    if ( !d->arch.sci_data )
+        return;
+
+    channel = d->arch.sci_data;
+    spin_lock(&channel->lock);
+
+    relinquish_scmi_channel(channel);
+    printk(XENLOG_DEBUG "scmi: Free domain %d\n", d->domain_id);
+
+    d->arch.sci_data = NULL;
+    d->arch.sci_enabled = false;
+
+    spin_unlock(&channel->lock);
+}
+
+static bool scmi_handle_call(struct cpu_user_regs *regs)
+{
+    uint32_t fid = (uint32_t)get_user_reg(regs, 0);
+    struct scmi_channel *agent_channel;
+    struct domain *d = current->domain;
+    bool res = false;
+
+    if ( (!sci_domain_is_enabled(d)) || (!d->arch.sci_data) )
+        return false;
+
+    agent_channel = d->arch.sci_data;
+    spin_lock(&agent_channel->lock);
+
+    if ( agent_channel->func_id != fid )
+    {
+        res = false;
+        goto unlock;
+    }
+
+    arm_smccc_guest_smc(regs);
+    res = true;
+unlock:
+    spin_unlock(&agent_channel->lock);
+
+    return res;
+}
+
+static const struct sci_mediator_ops scmi_ops = {
+    .domain_init = scmi_domain_init,
+    .domain_destroy = scmi_domain_destroy,
+    .relinquish_resources = scmi_relinquish_resources,
+    .handle_call = scmi_handle_call,
+    .dom0_dt_handle_node = scmi_dt_handle_node,
+    .domain_sanitise_config = scmi_domain_sanitise_config,
+    .assign_dt_device = scmi_dt_assign_device,
+};
+
+static int __init scmi_check_smccc_ver(void)
+{
+    if ( smccc_ver < ARM_SMCCC_VERSION_1_1 )
+    {
+        printk(XENLOG_WARNING
+               "scmi: No SMCCC 1.1 support, SCMI calls forwarding disabled\n");
+        return -ENOSYS;
+    }
+
+    return 0;
+}
+
+static int __init scmi_dt_hyp_channel_read(struct dt_device_node *scmi_node,
+                                           struct scmi_data *scmi_data,
+                                           u64 *addr)
+{
+    int ret;
+    u64 size;
+
+    if ( !dt_property_read_u32(scmi_node, "arm,smc-id", &scmi_data->func_id) )
+    {
+        printk(XENLOG_ERR "scmi: unable to read smc-id from DT\n");
+        return -ENOENT;
+    }
+
+    ret = scmi_dt_read_hyp_channel_addr(scmi_node, addr, &size);
+    if ( IS_ERR_VALUE(ret) )
+        return -ENOENT;
+
+    if ( !IS_ALIGNED(size, SCMI_SHMEM_MAPPED_SIZE) )
+    {
+        printk(XENLOG_ERR "scmi: shmem memory is not aligned\n");
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+static __init int scmi_probe(struct dt_device_node *scmi_node, const void *data)
+{
+    u64 addr;
+    int ret;
+    struct scmi_channel *channel;
+    unsigned int n_agents;
+    scmi_msg_header_t hdr;
+    struct scmi_msg_base_attributes_p2a rx;
+
+    ASSERT(scmi_node != NULL);
+
+    /*
+     * Only bind to the SCMI node provided by Xen under the xen,sci container
+     * (e.g. /chosen/xen/xen_scmi_config/scmi). This avoids binding to firmware
+     * SCMI nodes that belong to the host OSPM and keeps the mediator scoped to
+     * Xen-provided configuration only.
+     */
+    if ( !scmi_is_under_xen_sci(scmi_node) )
+        return -ENODEV;
+
+    if ( !acpi_disabled )
+    {
+        printk(XENLOG_WARNING "scmi: is not supported when using ACPI\n");
+        return -EINVAL;
+    }
+
+    ret = scmi_check_smccc_ver();
+    if ( ret )
+        return ret;
+
+    ret = scmi_dt_hyp_channel_read(scmi_node, &scmi_data, &addr);
+    if ( ret )
+        return ret;
+
+    INIT_LIST_HEAD(&scmi_data.channel_list);
+    spin_lock_init(&scmi_data.channel_list_lock);
+
+    scmi_data.dt_dev = scmi_node;
+
+    channel = smc_create_channel(SCMI_BASE_AGENT_ID_OWN, scmi_data.func_id, addr);
+    if ( IS_ERR(channel) )
+    {
+        ret = PTR_ERR(channel);
+        goto out;
+    }
+
+    /* Mark as Xen management channel before collecting agent ID */
+    channel->domain_id = DOMID_XEN;
+
+    /* Request agent id for Xen management channel */
+    ret = collect_agent_id(channel);
+    if ( ret )
+        goto error;
+
+    /* Save the agent id for Xen management channel */
+    scmi_data.hyp_channel_agent_id = channel->agent_id;
+
+    hdr.id = SCMI_BASE_PROTOCOL_ATTIBUTES;
+    hdr.type = 0;
+    hdr.protocol = SCMI_BASE_PROTOCOL;
+
+    ret = do_smc_xfer(channel, &hdr, NULL, 0, &rx, sizeof(rx));
+    if ( ret )
+        goto error;
+
+    n_agents = SCMI_FIELD_GET(SCMI_BASE_ATTR_NUM_AGENT, rx.attributes);
+    printk(XENLOG_DEBUG "scmi: Got agent count %d\n", n_agents);
+    ret = collect_agents(scmi_node);
+    if ( ret )
+        goto error;
+
+    ret = sci_register(&scmi_ops);
+    if ( ret )
+    {
+        printk(XENLOG_ERR "SCMI: mediator already registered (ret = %d)\n",
+               ret);
+        goto error;
+    }
+
+    scmi_data.initialized = true;
+    goto out;
+
+error:
+    unmap_channel_memory(channel);
+    free_channel_list();
+out:
+    return ret;
+}
+
+static const struct dt_device_match scmi_smc_match[] __initconst = {
+    DT_MATCH_COMPATIBLE("arm,scmi-smc"),
+    { /* sentinel */ },
+};
+
+DT_DEVICE_START(scmi_smc_ma, "SCMI SMC MEDIATOR", DEVICE_FIRMWARE)
+        .dt_match = scmi_smc_match,
+        .init = scmi_probe,
+DT_DEVICE_END
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/include/public/arch-arm.h b/xen/include/public/arch-arm.h
index 1fc676fd96..a9a10a3b8d 100644
--- a/xen/include/public/arch-arm.h
+++ b/xen/include/public/arch-arm.h
@@ -329,6 +329,7 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t);
 
 #define XEN_DOMCTL_CONFIG_ARM_SCI_NONE      0
 #define XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC  1
+#define XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC_MA  2
 
 #define XEN_DOMCTL_CONFIG_ARM_V8R_EL1_MSA_NONE    0
 #define XEN_DOMCTL_CONFIG_ARM_V8R_EL1_MSA_PMSA    1
@@ -361,7 +362,9 @@ struct xen_arch_domainconfig {
     uint8_t arm_sci_type;
     /* IN */
     uint8_t v8r_el1_msa;
-    uint16_t pad;
+    /* IN */
+    uint8_t arm_sci_agent_id;
+    uint8_t pad;
 };
 #endif /* __XEN__ || __XEN_TOOLS__ */
 
-- 
2.43.0


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

* [PATCH v13 6/6] docs: arm: add SCI SCMI SMC multi-agent driver docs
  2026-09-22 14:40 [PATCH v13 0/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent support Oleksii Moisieiev
                   ` (4 preceding siblings ...)
  2026-09-22 14:40 ` [PATCH v13 3/6] lib/arm: Add I/O memory copy helpers Oleksii Moisieiev
@ 2026-09-22 14:40 ` Oleksii Moisieiev
  5 siblings, 0 replies; 14+ messages in thread
From: Oleksii Moisieiev @ 2026-09-22 14:40 UTC (permalink / raw)
  To: xen-devel@lists.xenproject.org
  Cc: Andrew Cooper, Anthony PERARD, Bertrand Marquis, Jan Beulich,
	Juergen Gross, Julien Grall, Michal Orzel, Oleksii Moisieiev,
	Roger Pau Monné, Stefano Stabellini, Volodymyr Babchuk,
	Grygorii Strashko

From: Grygorii Strashko <grygorii_strashko@epam.com>

Add SCI SCMI SMC multi-agent driver documentation.
It includes a detailed description of the SCMI multi-agent driver.
This document explains the driver's functionality, configuration,
and the compilation process. The Xen SCMI multi-agent driver is
designed to provide SCMI access to system resources from different
domains.

Signed-off-by: Grygorii Strashko <grygorii_strashko@epam.com>
Signed-off-by: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
---

(no changes since v10)

Changes in v10:
- rephrase section about /firmware/scmi. Mentioned that this node is
taken from Host DT and copied unmodified.
- fix xen,reg address for secondary domains for Dom0less configuration

Changes in v8:
- update documentation to match the last DT format
- fixed RST: "... code-block:: dts" -> ".. code-block:: dts"
- update documentation with dom0less configuration example
- update documentation with new param xen,dom0-sci-agent-id
instead of the command line parameter

Changes in v7:
- update documentation in section of the xen_scmi configuration which
is matched by "xen,sci" compatible instead of the direct path.

Changes in v6:
- remove all HVC mentions from the multi-agent doc
- update sci-agent-id parameter description in the documentation
- add missing Sign-of
- minor fixes across the document

Changes in v5:
- rework multi-agent driver to leave Host Device-tree unmodified

 .../arm/firmware/arm-scmi.rst                 | 422 ++++++++++++++++++
 1 file changed, 422 insertions(+)

diff --git a/docs/hypervisor-guide/arm/firmware/arm-scmi.rst b/docs/hypervisor-guide/arm/firmware/arm-scmi.rst
index d9698f4e4b..8791bc665e 100644
--- a/docs/hypervisor-guide/arm/firmware/arm-scmi.rst
+++ b/docs/hypervisor-guide/arm/firmware/arm-scmi.rst
@@ -36,6 +36,8 @@ The below sections describe SCMI support options available for Xen.
 
 | [1] `Arm SCMI <https://developer.arm.com/documentation/den0056/latest/>`_
 | [2] `System Control and Management Interface (SCMI) bindings <https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/firmware/arm,scmi.yaml>`_
+| [3] `Generic Domain Access Controllers bindings <https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/access-controllers/access-controllers.yaml>`_
+
 
 Simple SCMI over SMC calls forwarding driver (EL3)
 ------------------------------------------------------
@@ -189,3 +191,423 @@ except explicitly enabling SCMI with "arm_sci" xl.cfg option.
     ->        xen,reg = <0x0 0x47ff0000 0x0 0x1000 0x0 0x22001000>;
     ->        xen,force-assign-without-iommu;
       };
+
+SCMI SMC multi-agent driver (EL3)
+-------------------------------------
+
+The SCMI SMC multi-agent driver enables support for ARM EL3 Trusted Firmware-A (TF-A) which
+provides SCMI interface with multi-agent support, as shown below.
+
+::
+
+      +-----------------------------------------+
+      |                                         |
+      | EL3 TF-A SCMI                           |
+      +-------+--+-------+--+-------+--+-------++
+      |shmem1 |  |shmem0 |  |shmem2 |  |shmemX |
+      +-----+-+  +---+---+  +--+----+  +---+---+
+    smc-id1 |        |         |           |
+    agent1  |        |         |           |
+      +-----v--------+---------+-----------+----+
+      |              |         |           |    |
+      |              |         |           |    |
+      +--------------+---------+-----------+----+
+             smc-id0 |  smc-id2|    smc-idX|
+             agent0  |  agent2 |    agentX |
+                     |         |           |
+                +----v---+  +--v-----+  +--v-----+
+                |        |  |        |  |        |
+                | Dom0   |  | Dom1   |  | DomX   |
+                |        |  |        |  |        |
+                |        |  |        |  |        |
+                +--------+  +--------+  +--------+
+
+The EL3 SCMI multi-agent firmware is expected to provide SCMI SMC shared-memory transport
+for every Agent in the system. The SCMI Agent transport channel defined by pair:
+
+- smc-id: SMC function id used for Doorbell
+- shmem: shared memory for messages transfer, **Xen page aligned**.
+  Shared memory is mapped with the following flags: MT_DEVICE_nGnRE and _PAGE_DEVICE, indicating that this
+  memory is mapped as device memory.
+
+The following SCMI Agents are expected to be defined by SCMI FW to enable SCMI multi-agent functionality
+under Xen:
+
+- Xen management agent: trusted agents that accesses to the Base Protocol commands to configure
+  agent specific permissions
+- OSPM VM agents: non-trusted agent, one for each Guest domain which is  allowed direct HW access.
+  At least one OSPM VM agent has to be provided by FW if HW is handled only by Dom0 or Driver Domain.
+
+The EL3 SCMI FW is expected to implement following Base protocol messages:
+
+- BASE_DISCOVER_AGENT (optional if agent_id was provided)
+- BASE_RESET_AGENT_CONFIGURATION (optional)
+- BASE_SET_DEVICE_PERMISSIONS (optional)
+
+The number of supported SCMI agents and their transport specifications are SCMI FW implementation
+specific.
+
+Compiling with multi-agent support
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To build with the SCMI SMC multi-agent driver support, enable Kconfig option:
+
+::
+
+    CONFIG_SCMI_SMC_MA
+
+
+Driver functionality
+^^^^^^^^^^^^^^^^^^^^
+
+The SCI SCMI SMC multi-agent driver implements following functionality:
+
+- The driver is initialized from the Xen SCMI container ``xen_scmi_config``
+  under ``/chosen/xen`` (for example ``/chosen/xen/xen_scmi_config/scmi``).
+  Only one SCMI interface is supported. The SCMI configuration must live under
+  the Xen SCMI container ``xen,sci`` beneath ``/chosen``.
+  The Xen SCMI mediator will bind only to the "arm,scmi-smc" node that is a child of
+  this "xen,sci" container; any other "arm,scmi-smc" nodes (for example under
+  "/firmware") are ignored to avoid stealing the host's SCMI OSPM instance.
+
+.. code-block:: dts
+
+        scmi_shm_1: sram@47ff1000 {
+            compatible = "arm,scmi-shmem";
+            reg = <0x0 0x47ff1000 0x0 0x1000>;
+        };
+        scmi_xen: scmi {
+          compatible = "arm,scmi-smc";
+          arm,smc-id = <0x82000003>; <--- Xen management agent smc-id
+          #address-cells = < 1>;
+          #size-cells = < 0>;
+          #access-controller-cells = < 1>;
+          shmem = <&scmi_shm_1>; <--- Xen management agent shmem
+        };
+
+.. note::
+   This layout keeps the Host DT unchanged for Dom0 and baremetal Linux by
+   using func_id 0x82000002 / shmem 0x47ff0000 for Dom0, while Xen uses a
+   separate privileged channel func_id 0x82000003 / shmem 0x47ff1000. EL3
+   firmware enforces permissions per agent_id, so there is no conflict between
+   Dom0 and Xen channels.
+
+- The driver obtains Xen specific SCMI Agent's configuration from the Host DT, probes Agents and
+  builds SCMI Agents list. The Agents configuration is taken from "scmi-secondary-agents"
+  property where first item is "arm,smc-id", second - "arm,scmi-shmem" phandle and third is
+  optional "agent_id":
+
+.. code-block:: dts
+
+    chosen {
+      ranges; <--- set default ranges so address can be translated when parsing scmi_shm node
+      xen {
+        ranges;
+        xen_scmi_config {
+          compatible = "xen,sci";
+          #address-cells = <2>;
+          #size-cells = <2>;
+          ranges; <--- set default ranges so address can be translated when parsing scmi_shm node
+          scmi-secondary-agents = <
+                        0x82000002 &scmi_shm_0 0
+                        0x82000004 &scmi_shm_2 2
+                        0x82000005 &scmi_shm_3 3
+                        0x82000006 &scmi_shm_4 4>;
+          #scmi-secondary-agents-cells = <3>; <--- optional, default 3
+          xen,dom0-sci-agent-id = <0>;  /* Dom0 agent ID */
+
+          scmi_shm_0 : sram@47ff0000 {
+              compatible = "arm,scmi-shmem";
+              reg = <0x0 0x47ff0000 0x0 0x1000>;
+          };
+
+          scmi_shm_2: sram@47ff2000 {
+              compatible = "arm,scmi-shmem";
+              reg = <0x0 0x47ff2000 0x0 0x1000>;
+          };
+          scmi_shm_3: sram@47ff3000 {
+              compatible = "arm,scmi-shmem";
+              reg = <0x0 0x47ff3000 0x0 0x1000>;
+          };
+          scmi_shm_4: sram@47ff4000 {
+              compatible = "arm,scmi-shmem";
+              reg = <0x0 0x47ff4000 0x0 0x1000>;
+          };
+
+          // Xen SCMI management channel
+          scmi_shm_1: sram@47ff1000 {
+              compatible = "arm,scmi-shmem";
+              reg = <0x0 0x47ff1000 0x0 0x1000>;
+          };
+
+          scmi_xen: scmi {
+              compatible = "arm,scmi-smc";
+              arm,smc-id = <0x82000003>; <--- Xen management agent smc-id
+              #address-cells = < 1>;
+              #size-cells = < 0>;
+              #access-controller-cells = < 1>;
+              shmem = <&scmi_shm_1>; <--- Xen management agent shmem
+          };
+        };
+      };
+    };
+
+    /{
+        // Host SCMI OSPM channel - provided to the Dom0 as is if SCMI enabled for it
+        scmi_shm: sram@47ff0000 {
+                compatible = "arm,scmi-shmem";
+                reg = <0x0 0x47ff0000 0x0 0x1000>;
+        };
+
+        firmware {
+            scmi: scmi {
+                compatible = "arm,scmi-smc";
+                arm,smc-id = <0x82000002>; <--- Host OSPM agent smc-id
+                #address-cells = < 1>;
+                #size-cells = < 0>;
+                shmem = <&scmi_shm>; <--- Host OSPM agent shmem
+
+                protocol@X{
+                };
+            };
+        };
+    };
+
+  This approach allows defining multiple SCMI Agents by adding Xen-specific properties under
+  the ``/chosen`` node to the Host Device Tree, leaving the main part unchanged. The Host DT
+  SCMI channel will be passed to Dom0.
+
+  The Xen management agent is described as a ``scmi_xen`` node under the ``xen,sci`` compatible node,
+  which is used by Xen to control other SCMI Agents in the system.
+
+  All secondary agents' configurations are provided in the ``scmi-secondary-agents`` property with
+  an optional ``agent_id`` field.
+
+  The ``agent_id`` from the ``scmi-secondary-agents`` property is used to identify the agent in the
+  system and can be omitted by setting ``#scmi-secondary-agents-cells = <2>``, so the Secondary
+  Agents configuration will look like this:
+
+.. code-block:: dts
+
+    chosen {
+      xen {
+        xen_scmi_config {
+          compatible = "xen,sci";
+          scmi-secondary-agents = <
+                        0x82000002 &scmi_shm_0
+                        0x82000004 &scmi_shm_2
+                        0x82000005 &scmi_shm_3
+                        0x82000006 &scmi_shm_4>;
+          #scmi-secondary-agents-cells = <2>;
+        };
+      };
+    }
+
+  In this case, Xen will use the ``SCMI_BASE_DISCOVER_AGENT`` call to discover the ``agent_id``
+  for each secondary agent. Providing the ``agent_id`` in the ``scmi-secondary-agents`` property
+  allows skipping the discovery call, which is useful when the secondary agent's shared memory is
+  not accessible by Xen or when boot time is important because it allows skipping the agent
+  discovery procedure.
+
+.. note::
+
+    Note that Xen is the only one entry in the system which need to know about SCMI multi-agent support.
+
+- The driver implements the SCI subsystem interface required for configuring and enabling SCMI
+  functionality for Dom0/hwdom and Guest domains. To enable SCMI functionality for guest domain
+  it has to be configured with unique supported SCMI Agent_id and use corresponding SCMI SMC
+  shared-memory transport ``[smc-id, shmem]`` defined for this SCMI Agent_id.
+
+- Once Xen domain is configured it can communicate with EL3 SCMI FW:
+
+  - zero-copy, the guest domain puts/gets SCMI message in/from shmem;
+  - the guest triggers SMC exception with agent "smc-id" (doorbell);
+  - the Xen driver catches exception, do checks and synchronously forwards it to EL3 FW.
+
+- the Xen driver sends BASE_RESET_AGENT_CONFIGURATION message to Xen management agent channel on
+  domain destroy event. This allows to reset resources used by domain and so implement use-case
+  like domain reboot.
+
+
+Configure SCMI for Dom0
+^^^^^^^^^^^^^^^^^^^^^^^
+Set the Dom0 SCMI agent ID in the device tree using the Xen SCMI container under ``/chosen``.
+Add ``xen,dom0-sci-agent-id`` to the ``xen,sci`` node. If the property is absent, SCMI stays
+disabled for Dom0 and the SCMI nodes are removed from Dom0 DT.
+
+.. code-block:: dts
+
+  chosen {
+    xen {
+      ranges;
+      xen_scmi_config {
+        compatible = "xen,sci";
+        xen,dom0-sci-agent-id = <0>;  /* Dom0 agent ID */
+        /* scmi-secondary-agents and scmi_xen as shown above */
+      };
+    };
+  };
+
+The Host DT ``/firmware/scmi`` node is copied to the Dom0 DT unmodified. However, for Dom0 SCMI
+configuration, Xen actually relies on ``scmi-secondary-agents`` and ``xen,dom0-sci-agent-id``
+properties from the ``xen,sci`` container under ``/chosen``. If the ``/firmware/scmi`` node is
+missing or disabled, or if ``xen,dom0-sci-agent-id`` is not provided, the Dom0 SCMI agent will not
+be configured.
+
+.. note::
+
+  The ``xen,dom0-sci-agent-id`` value must match the ``func_id`` and ``shmem`` pairing provided by
+  the EL3 firmware for Dom0 (for example in the ``/firmware/scmi`` node).
+
+Configure SCMI for for guest domain with toolstack
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* In domain's xl.cfg file add **"arm_sci"** option as below
+
+::
+
+    arm_sci = "type=scmi_smc_multiagent,agent_id=2"
+
+* In domain's xl.cfg file enable access to the "arm,scmi-shmem" which should correspond
+  assigned "agent_id" for the domain, for example:
+
+::
+
+    iomem = [
+        "47ff2,1@22001",
+    ]
+
+.. note:: It's up to the user to select guest IPA for mapping SCMI shared-memory.
+
+* Add SCMI nodes to the Driver domain partial device tree as in the below example.
+  The "arm,smc-id" should correspond assigned agent_id for the domain:
+
+.. code::
+
+    passthrough {
+       scmi_shm_0: sram@22001000 {
+           compatible = "arm,scmi-shmem";
+           reg = <0x0 0x22001000 0x0 0x1000>;
+       };
+
+       firmware {
+            compatible = "simple-bus";
+                scmi: scmi {
+                    compatible = "arm,scmi-smc";
+                    arm,smc-id = <0x82000004>;  <--- smc-id for agent_id=2
+                    shmem = <&scmi_shm_0>;
+                    ...
+                }
+        }
+    }
+
+**Device specific access control**
+
+The XEN SCMI SMC multi-agent driver performs "access-controller" provider function in case
+EL3 SCMI FW implements SCMI "4.2.1.1 Device specific access control" and provides the
+BASE_SET_DEVICE_PERMISSIONS command to configure the devices that an agents have access to.
+The Host DT SCMI node should have "#access-controller-cells=<1>" property and DT devices should
+be bound to the SCMI node using Access Controllers bindings [3].
+
+For example:
+
+.. code-block:: dts
+
+    &i2c1 {
+            access-controllers = <&scmi 0>;
+    };
+
+Use domain's xl.cfg file **"dtdev"** property to assign SCMI devices from toolstack to the guest:
+
+::
+
+    dtdev = [
+        "/soc/i2c@e6508000",
+    ]
+
+.. note::
+
+    xl.cfg:"dtdev" need contain all nodes which are under SCMI management (not only those which are
+    behind IOMMU) and passed-through to the guest domain.
+
+Configure SCMI for predefined domains (dom0less)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* add "xen,sci_type" and "xen,sci-agent-id" properties for required DomU ("xen,domain") node
+
+::
+
+    xen,sci_type="scmi_smc_multiagent"
+    xen,sci-agent-id=2
+
+* add scmi nodes to the Driver domain partial device tree the same way as above (toolstack case) and
+  enable access to the "arm,scmi-shmem" according to the dom0less documentation. For example:
+
+.. code-block:: dts
+
+      scmi_shm_0: sram@22001000 {
+            compatible = "arm,scmi-shmem";
+            reg = <0x00 0x22001000 0x00 0x1000>;
+    ->        xen,reg = <0x0 0x47ff2000 0x0 0x1000 0x0 0x22001000>;
+    ->        xen,force-assign-without-iommu;
+      };
+
+* For SCMI device access control configure pass-through devices in the guest partial DT according to
+  the dom0less documentation and ensure that devices SCMI management has "xen,path" property set:
+
+Example (dom0less, multi-agent):
+
+.. code-block:: dts
+
+  chosen {
+    xen {
+      ranges;
+      xen_scmi_config {
+        compatible = "xen,sci";
+        #address-cells = <2>;
+        #size-cells = <2>;
+        ranges;
+
+        /* Xen management channel shared memory */
+        scmi_shm_1: sram@47ff1000 {
+          compatible = "arm,scmi-shmem";
+          reg = <0x0 0x47ff1000 0x0 0x1000>;
+        };
+
+        scmi_shm_domu: sram@47ff2000 {
+          compatible = "arm,scmi-shmem";
+          reg = <0x0 0x47ff2000 0x0 0x1000>;
+        };
+
+        scmi-secondary-agents = <
+          0x82000004 &scmi_shm_domu 2>;
+        #scmi-secondary-agents-cells = <3>;
+
+        scmi_xen: scmi {
+          compatible = "arm,scmi-smc";
+          arm,smc-id = <0x82000003>;
+          #address-cells = <1>;
+          #size-cells = <0>;
+          #access-controller-cells = <1>;
+          shmem = <&scmi_shm_1>;
+        };
+      };
+    };
+
+    xen,domain@1 {
+      compatible = "xen,domain";
+      xen,sci_type = "scmi_smc_multiagent";
+      xen,sci-agent-id = <2>;
+      /* other domain properties here */
+    };
+  };
+
+.. code-block:: dts
+
+		i2c@e6508000 {
+            ...
+			reg = <0x00 0xe6508000 0x00 0x1000>;
+    ->        xen,path = "/soc/i2c@e6508000"
+    ->        xen,reg = <0x0 0xe6508000 0x0 0x1000 0x0 0xe6508000>;
+    ->        xen,force-assign-without-iommu;
+        };
-- 
2.43.0


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

* Re: [PATCH v13 4/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver
  2026-09-22 14:40 ` [PATCH v13 4/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver Oleksii Moisieiev
@ 2026-09-23  1:02   ` Volodymyr Babchuk
  2026-09-23  8:25     ` Oleksii Moisieiev
  0 siblings, 1 reply; 14+ messages in thread
From: Volodymyr Babchuk @ 2026-09-23  1:02 UTC (permalink / raw)
  To: Oleksii Moisieiev
  Cc: xen-devel@lists.xenproject.org, Andrew Cooper, Anthony PERARD,
	Bertrand Marquis, Jan Beulich, Juergen Gross, Julien Grall,
	Michal Orzel, Roger Pau Monné, Stefano Stabellini,
	Grygorii Strashko

Hi Oleksii,

Oleksii Moisieiev <Oleksii_Moisieiev@epam.com> writes:

This is a great piece of documentation. It is larger than the text you
added to the 'doc' directory. So I believe it is better to put into a
design document, so it is will not be lost in the git commit messages.

> This patch introduces SCI driver to support for ARM EL3 Trusted Firmware-A
> (TF-A) which provides SCMI interface with multi-agent support, as shown
> below.
>
>   +-----------------------------------------+
>   |                                         |
>   | EL3 TF-A SCMI                           |
>   +-------+--+-------+--+-------+--+-------++
>   |shmem1 |  |shmem0 |  |shmem2 |  |shmemX |
>   +-----+-+  +---+---+  +--+----+  +---+---+
> smc-id1 |        |         |           |
> agent1  |        |         |           |
>   +-----v--------+---------+-----------+----+
>   |              |         |           |    |
>   |              |         |           |    |
>   +--------------+---------+-----------+----+
>          smc-id0 |  smc-id2|    smc-idX|
>          agent0  |  agent2 |    agentX |
>                  |         |           |
>             +----v---+  +--v-----+  +--v-----+
>             |        |  |        |  |        |
>             | Dom0   |  | Dom1   |  | DomX   |
>             |        |  |        |  |        |
>             |        |  |        |  |        |
>             +--------+  +--------+  +--------+
>
> The EL3 SCMI multi-agent firmware is expected to provide SCMI SMC shared
> memory transport for every Agent in the system.
>
> The SCMI Agent transport channel defined by pair:
>  - smc-id: SMC id used for Doorbell
>  - shmem: shared memory for messages transfer, Xen page
>  aligned. Shared memory is mapped with the following flags:
>  MT_DEVICE_nGnRE.
>
> The follwoing SCMI Agents are expected to be defined by SCMI FW to enable SCMI
> multi-agent functionality under Xen:
> - Xen management agent: trusted agents that accesses to the Base Protocol
> commands to configure agent specific permissions
> - OSPM VM agents: non-trusted agent, one for each Guest domain which is
>   allowed direct HW access. At least one OSPM VM agent has to be provided
>   by FW if HW is handled only by Dom0 or Driver Domain.
>
> The EL3 SCMI FW is expected to implement following Base protocol messages:
> - BASE_DISCOVER_AGENT (optional if agent_id was provided)
> - BASE_RESET_AGENT_CONFIGURATION (optional)
> - BASE_SET_DEVICE_PERMISSIONS (optional)
>
> The SCI SCMI SMC multi-agent driver implements following
> functionality:
> - The driver is initialized from the Xen SCMI container ``xen_scmi_config``
>   (compatible ``xen,sci``) placed under ``/chosen/xen``. Only the
>   ``arm,scmi-smc`` node that is a child of this container will bind to Xen;
>   other SCMI nodes (for example under ``/firmware``) are ignored to avoid
>   stealing the host OSPM instance.
>
> scmi_shm_1: sram@47ff1000 {
>           compatible = "arm,scmi-shmem";
>           reg = <0x0 0x47ff1000 0x0 0x1000>;
> };
> scmi_xen: scmi {
>         compatible = "arm,scmi-smc";
>         arm,smc-id = <0x82000003>; <--- Xen management agent smc-id
>         #address-cells = < 1>;
>         #size-cells = < 0>;
>         #access-controller-cells = < 1>;
>         shmem = <&scmi_shm_1>; <--- Xen management agent shmem
> };
>
> - The driver obtains Xen specific SCMI Agent's configuration from the
>   Host DT, probes Agents and builds SCMI Agents list. The Agents
>   configuration is taken from "scmi-secondary-agents" property where
>   first item is "arm,smc-id", second - "arm,scmi-shmem" phandle and
>   third is optional "agent_id":
>
> / {
>   chosen {
>     xen {
>       ranges;
>       xen_scmi_config {
>         compatible = "xen,sci";
>         #address-cells = <2>;
>         #size-cells = <2>;
>         ranges;
>
>       scmi-secondary-agents = <
>           0x82000002 &scmi_shm_0 0
>           0x82000004 &scmi_shm_2 2
>           0x82000005 &scmi_shm_3 3>; <--- func_id, shmem, agent_id
>         #scmi-secondary-agents-cells = <3>;

I don't think that this is the correct way of using -cells property.

These properties are used either to provide information for **child** nodes
(like #address-cells or #size-cells) or to provide a number of cells to
encode a specifier for a domain (like #interrupt-cells).

Here you are doing neither of these. I think the proper way is to define
secondary agents as children:

agents {
        #address-cells = 1;
        #size-cells = 0;
         scmi_agent_2: scmi_agent@2 {
           compatible = "arm,scmi-agent";  // Actually, I am not sure if
                                           // we allowed to use 'arm' namespace

           reg = <2>;                      // Agent id goes here
           shmmem = &scmi_shm_2;
           arm,smc-id = 0x82000004;
         };
}

With this approach you don't need #scmi-secondary-agents-cells at all
and the whole structure becomes more device-tree-ish.

>         xen,dom0-sci-agent-id = <0>;
>
>         scmi_shm_0: sram@47ff0000 {
>           compatible = "arm,scmi-shmem";
>           reg = <0x0 0x47ff0000 0x0 0x1000>;
>         };
>
>         /* Xen SCMI management channel */
>         scmi_shm_1: sram@47ff1000 {
>           compatible = "arm,scmi-shmem";
>           reg = <0x0 0x47ff1000 0x0 0x1000>;
>         };
>
>         scmi_shm_2: sram@47ff2000 {
>           compatible = "arm,scmi-shmem";
>           reg = <0x0 0x47ff2000 0x0 0x1000>;
>         };
>
>         scmi_shm_3: sram@47ff3000 {
>           compatible = "arm,scmi-shmem";
>           reg = <0x0 0x47ff3000 0x0 0x1000>;
>         };
>
>         scmi_xen: scmi {
>           compatible = "arm,scmi-smc";
>           arm,smc-id = <0x82000003>; <--- Xen management agent func_id
>           #address-cells = <1>;
>           #size-cells = <0>;
>           #access-controller-cells = <1>;
>           shmem = <&scmi_shm_1>; <--- Xen management agent shmem
>         };
>       };
>     };
>   };
> };
>
> / {
>     /*
>      * Host SCMI OSPM channel - provided to the Dom0 as is if SCMI
>      * enabled for it, ignored by Xen multi-agent mediator
>      */
>     scmi_shm: sram@47ff0000 {
>             compatible = "arm,scmi-shmem";
>             reg = <0x0 0x47ff0000 0x0 0x1000>;
>     };
>
>     firmware {
>       scmi: scmi {
>         compatible = "arm,scmi-smc";
>         arm,smc-id = <0x82000002>; <--- Host OSPM agent smc-id
>         #address-cells = < 1>;
>         #size-cells = < 0>;
>         shmem = <&scmi_shm>; <--- Host OSPM agent shmem
>
>         protocol@X{
>         };
>       };
>    };
> };
>
> This approach allows defining multiple SCMI Agents by adding
> Xen-specific properties under the ``/chosen`` node to the Host Device
> Tree, leaving the main part unchanged. The Host DT SCMI channel will
> be passed to Dom0.
>
> The Xen management agent is described as a ``scmi_xen`` node under the
> ``xen,sci`` comaptible node, which is used by Xen to control other
> SCMI Agents in the system.
>
> All secondary agents' configurations are provided in the
> ``scmi-secondary-agents`` property with an optional ``agent_id`` field.
>
> The ``agent_id`` from the ``scmi-secondary-agents`` property is used
> to identify the agent in the system and can be omitted by setting
> ``#scmi-secondary-agents-cells = <2>``, so the Secondary Agents
> configuration will look like this:
>
> / {
>   chosen {
>     xen {
>       xen_scmi_config {
>         compatible = "xen,sci";
>         #address-cells = <2>;
>         #size-cells = <2>;
>         ranges;
>
>         /* Shared memory nodes as defined earlier */
>
>         scmi-secondary-agents = <
>           0x82000003 &scmi_shm_0
>           0x82000004 &scmi_shm_2
>           0x82000005 &scmi_shm_3
>           0x82000006 &scmi_shm_4>;
>         #scmi-secondary-agents-cells = <2>;
>       };
>     };
>   };
> }
>
> In this case, Xen will use the ``SCMI_BASE_DISCOVER_AGENT`` call to
> discover the ``agent_id`` for each secondary agent. Providing the
> ``agent_id`` in the ``scmi-secondary-agents`` property allows skipping
> the discovery call, which is useful when the secondary agent's shared
> memory is not accessible by Xen or when boot time is important because
> it allows skipping the agent discovery procedure.
>
>   Note that Xen is the only one entry in the system which need to know
>   about SCMI multi-agent support.
>
> SMC ID Configuration and SCMI Connection Compatibility:
>
> The configuration allows the same device tree to work for both baremetal
> Linux and Linux Dom0. This is achieved because:
>
> - Baremetal Linux uses: func_id 0x82000002, scmi-shmem 0x47ff0000
> - Dom0 Linux uses: func_id 0x82000002, scmi-shmem 0x47ff0000
> - Xen management uses: func_id 0x82000003, scmi-shmem 0x47ff1000
>
> This works because the privileged SCMI connection in EL3 firmware is not
> tied exclusively to func_id 0x82000002. The EL3 firmware supports multiple
> SCMI agents with different SMC IDs and shared memory regions. Each agent
> (Dom0 via 0x82000002, Xen via 0x82000003, other domains via additional
> func_ids) has an independent communication channel to the firmware.
>
> The key distinction is that Xen's management channel (0x82000003) is used
> for privileged operations like agent configuration and device permissions
> (BASE_SET_DEVICE_PERMISSIONS, BASE_RESET_AGENT_CONFIGURATION), while Dom0's
> channel (0x82000002) is used for standard SCMI protocol operations (power,
> clock, sensor management, etc.). The firmware enforces different permission
> levels for each agent based on their agent_id, not the SMC ID.
>
> Therefore, there is no conflict: Linux Dom0 retains its standard SCMI
> connection for hardware management, while Xen uses its separate privileged
> channel for mediating access between multiple domains.
>
> - It implements the SCI subsystem interface required for configuring and
> enabling SCMI functionality for Dom0/hwdom and Guest domains. To enable
> SCMI functionality for domain it has to be configured with unique supported
> SCMI Agent_id and use corresponding SCMI SMC shared memory transport
> [smc-id, shmem] defined for this SCMI Agent_id.
> - Once Xen domain is configured it can communicate with EL3 SCMI FW:
>   -- zero-copy, the guest domain puts SCMI message in shmem;
>   -- the guest triggers SMC exception with smc-id (doorbell);
>   -- the Xen driver catches exception, do checks and synchronously forwards
>   it to EL3 FW.
> - the Xen driver sends BASE_RESET_AGENT_CONFIGURATION message to Xen
>   management agent channel on domain destroy event. This allows to reset
>   resources used by domain and so implement use-case like domain reboot.
>
> Dom0 Enable SCMI SMC:
>  - set xen,dom0-sci-agent-id=<agent_id> under the xen,sci container in
>    the Host DT. If the property is absent, SCMI is disabled for Dom0
>    and all SCMI nodes are removed from the Dom0 DT. The driver updates
>    the Dom0 DT SCMI node "arm,smc-id" value and fixes up the shmem
>    node according to the assigned agent_id.
>
>  - pass dom0=sci-agent-id=<agent_id> in Xen command line. if not provided
>    SCMI will be disabled for Dom0 and all SCMI nodes removed from Dom0 DT.
>    The driver updates Dom0 DT SCMI node "arm,smc-id" value and fix up shmem
>    node according to assigned agent_id.
>
> Guest domains enable SCMI SMC:
>  - xl.cfg: add configuration option as below
>
>    arm_sci = "type=scmi_smc_multiagent,agent_id=2"
>
>  - xl.cfg: enable access to the "arm,scmi-shmem" which should
>  correspond assigned agent_id for the domain, for example:
>
> iomem = [
>     "47ff2,1@22001",
> ]
>
>  - DT: add SCMI nodes to the Driver domain partial device tree as in the
>  below example. The "arm,smc-id" should correspond assigned agent_id
>  for the domain:
>
> passthrough {
>    scmi_shm_0: sram@22001000 {
>        compatible = "arm,scmi-shmem";
>        reg = <0x0 0x22001000 0x0 0x1000>;
>    };
>
>    firmware {
>         compatible = "simple-bus";
>             scmi: scmi {
>                 compatible = "arm,scmi-smc";
>                 arm,smc-id = <0x82000004>;
>                 shmem = <&scmi_shm_0>;
>                 ...
>             }
>     }
> }
>
> SCMI "4.2.1.1 Device specific access control"
>
> The XEN SCI SCMI SMC multi-agent driver performs "access-controller"
> provider function in case EL3 SCMI FW implements SCMI "4.2.1.1 Device
> specific access control" and provides the BASE_SET_DEVICE_PERMISSIONS
> command to configure the devices that an agents have access to.
> The DT SCMI node should "#access-controller-cells=<1>" property and DT
> devices should be bound to the Xen SCMI.
>
> &i2c1 {
>         access-controllers = <&scmi 0>;
> };
>
> The Dom0 and dom0less domains DT devices will be processed
> automatically through sci_assign_dt_device() call, but to assign SCMI
> devices from toolstack the xl.cfg:"dtdev" property
> shall be used:
>
> dtdev = [
>     "/soc/i2c@e6508000",
> ]
>
> xl.cfg:dtdev will contain all nodes which are under SCMI
> management (not only those which are behind IOMMU).
>
> Additionally, this patch adds documentation for the pre-existing
> scmi-smc-passthrough command line option, which was previously
> undocumented.
>
> [0] https://developer.arm.com/documentation/den0056
> [1] https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
> [2] https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/access-controllers/access-controllers.yaml
>
> Signed-off-by: Grygorii Strashko <grygorii_strashko@epam.com>
> Signed-off-by: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
> ---
>
> Changes in v13:
> - fix typo in commit description
>
> Changes in v12:
> - rebase to the latest staging
> - return EINVAL if xen,sci_type=scmi_smc_multiagent was set but
> CONFIG_SCMI_SMC_MA not set
> - put arm_sci_agent_id after v8r_el1_msa and before pad to preserve
> order. Reduced pad from uint16_t to uint8_t to fill the gap (was 2
> bytes before adding arm_sci_agent_id and left only 1, so used uint8_t
> to preserve structure size)
> - fix scmi_handle_call to use arm_smccc_guest_smc helper function
> - update send_smc_message function to use new format of
> arm_smccc_1_1_smc
> - guard the Dom0 SCMI agent id setup in create_dom0() and MA related
> funcitons with CONFIG_SCMI_SMC_MA
> - fix the xen_scmi_config example in booting.txt, which placed properties
> after subnodes and was rejected by dtc with "Properties must precede
> subnodes"
>
> Changes in v10:
> - Fix tabs in MAINTAINERS file
> - remove duplicate SPDX tag from scmi-shmem.c
> - add cast to ARM_SMCCC_INVALID_PARAMETER to settle the sign since
> ARM_SMCCC_INVALID_PARAMETER is -3 which is part of the spec and resp
> is the default smccc call structure.
> - update free_channel_list. Add spinlock to avoid race condition and
> a comment with a description of the function work
> - preserve error of smc_create_channel in scmi_probe
> - check scmi shmem address alignment as wel as it is done for size
> - check for d->arch.sci_data != NULL in scmi_handle_call
> - use SCMI_SHMEM_MAPPED size for iomem_permit_access
> - change len type to unsigned in shmem_{get|put}_message
> - rename shmem_channel_is_free to shmem_channel_status
> - add comment about skipping message status when getting message response
> - Set correct agent_id ranges for dom0less and a toolstack. Agent_id 0
> is not binded for dom0 so can be reused. Also mentioned that
> UINT8_MAX (255) is treated as invalid agent_id.
> - Split hypervisor and toolstack changes into separate commits
> - move init list and spin init after initial checks in probe call
> - fix typo in comments
> - clean resources when sci_register returns an error.
>
> Changes in v9:
> - sort and refactor MAINTAINERS enties
> - remove Spurious changes
> - add extra check to avoid ASSERT when calling unmap_channel_memory
> from assign device method
> - set correct tx flag to SCMI_BASE_AGENT_PERMISSIONS_RESET when
> freeing resources. Flag should be set to 1 according to the
> section 4.2.2.12 [0].
> - fix dt node copmaring
> - moved channel->shmem check from ASSERT in unmap_memory_channel to
> "if" statement. This will prevent firing ASSERT if
> unmap_channel_memory was called twice on the same channel.
>
> Changes in v8:
> - update xen_scmi func_id in commit description
> - updated documentation with the new DT format
> - updated opt_dom0_scmi_agent_id setting to avoid it to be equal
> SCMI_AGENT_ID_INVALID.
> - changed SCMI_AGENT_ID_INVALID from 0xff to UINT8_MAX which makes
> code more clear showing that UINT8_MAX is theated like invalid
> agent_id and couldn't be used. Also excluded SCMI_AGENT_ID_INVALID
> from acceptable value range
> - remove outdated xen,config property ignore, added xen,sci compatible
> to skip_matches in handle_node
> - add documentation for pre-existing scmi-smc-passthrough command line
> option in alphabetically correct location (in 's' section)
> - add note to commit description about documentation for previously
> undocumented scmi-smc-passthrough
> - Fix SMC IDs in DT examples (Xen management uses 0x82000003, Dom0 uses 0x82000002)
> - Add explicit note explaining why Dom0 and Xen channels do not conflict
> - Document dom0less multi-agent configuration example (xen,sci_type / xen,sci-agent-id)
> - Add scmi_xen node to agent-discovery example with #scmi-secondary-agents-cells = 2
> - Drop dom0=sci-agent-id command line handling; Dom0 SCMI is now enabled via
>   xen,dom0-sci-agent-id in the xen,sci DT container
> - Refresh docs and examples to mention the DT property instead of the cmdline option
>
> Changes in v7:
> - rework scmi nodes for xen to match on compatible string instead of
> the direct path
>
> Changes in v6:
> - updated scmi-shmem to use io.h from generic location
> - update scmi_agent_id parameter to be provided inside dom0= parameter
> list and have the following format "dom0=sci-agent-id=0"
> This change was done as a response for Stefano comment and
> requires a lot of code changes, but produces much cleaner solution
> that's why I've added it to the code.
> - fix file comments and return codes
> - fix lenght checks in shmem_{get,put}_message to use offsetof
> - remove len member from scmi_channel structure as it is not used
> - set scmi-secondary-agents property to be mandatory since if no
> secondary agents were provided then there is no sence to enable scmi
> when no secondary agents are populated to the Domains
> - update documentation in booting.txt, added xen_scmi node to the
> example
> - adjust d->arch.sci_enabled value in scmi_domain_destroy
> - fix lock management in smc_create_channel call
> - avoid extra map_channel_memory command for Xen management channel
> because collect_agent_id call unmaps memory if DOMID_XEN is not
> set. So for Xen management channel we can init domain_id ad DOMID_XEN
> before calling collect_agent_id so memory shouldn't be unmapped.
>
> Changes in v5:
> - fix device-tree example format in booting.txt, added ";" after "}".
> - update define in scmi-proto.h
> - update define in scmi-shmem.h file
> - scmi_assign_device - do not ignore -EOPNOTSUPP return
> code of the do_smc_xfer
> - remove overwriting agent_channel->agent_id after
> SCMI_BASE_DISCOVER_AGENT call
> - add multi-agent files to the MAINTAINERS
> - add SCMI multi-agent description to the SUPPORT.md
> - handle ARM_SMCCC_INVALID_PARAMETER return code and return -EINVAL
> for smc call
> - updated collect_agents function. Set agent_id parameter as optional
> in scmi-secondary-agents device-tree property
> - introduce "#scmi-secondary-agents-cells" parameter to set if
> agent_id was provided
> - reanme xen,scmi-secondary-agents property to scmi-secondary-agents
> - move memcpu_toio/fromio for the generic place
> - update Xen to get management channel from /chosen/xen,config node
> - get hypervisor channnel from node instead of using hardcoded
> - update handling scmi and shmem nodes for the domain
> - Set multi-agent driver to support only Arm64
>
> Changes in v4:
> - toolstack comments from Anthony PERARD
> - added dom0less support
> - added doc for "xen,scmi-secondary-agents"
>
>  MAINTAINERS                                 |   1 +
>  SUPPORT.md                                  |  11 +
>  docs/misc/arm/device-tree/booting.txt       | 197 +++++
>  xen/arch/arm/dom0less-build.c               |  17 +
>  xen/arch/arm/domain_build.c                 |  43 ++
>  xen/arch/arm/firmware/Kconfig               |  12 +
>  xen/arch/arm/firmware/Makefile              |   1 +
>  xen/arch/arm/firmware/scmi-proto.h          | 164 ++++
>  xen/arch/arm/firmware/scmi-shmem.c          | 118 +++
>  xen/arch/arm/firmware/scmi-shmem.h          |  45 ++
>  xen/arch/arm/firmware/scmi-smc-multiagent.c | 816 ++++++++++++++++++++
>  xen/include/public/arch-arm.h               |   5 +-
>  12 files changed, 1429 insertions(+), 1 deletion(-)
>  create mode 100644 xen/arch/arm/firmware/scmi-proto.h
>  create mode 100644 xen/arch/arm/firmware/scmi-shmem.c
>  create mode 100644 xen/arch/arm/firmware/scmi-shmem.h
>  create mode 100644 xen/arch/arm/firmware/scmi-smc-multiagent.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c60afc93a5..cd6d1243bd 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -534,6 +534,7 @@ SCI MEDIATORS
>  R:   Oleksii Moisieiev <oleksii_moisieiev@epam.com>
>  S:   Supported
>  F:   xen/arch/arm/firmware/sci.c
> +F:   xen/arch/arm/firmware/scmi-*.[ch]
>  F:   xen/arch/arm/include/asm/firmware/sci.h
>
>  SEABIOS UPSTREAM
> diff --git a/SUPPORT.md b/SUPPORT.md
> index eb07332462..dc41a6b2f3 100644
> --- a/SUPPORT.md
> +++ b/SUPPORT.md
> @@ -972,6 +972,17 @@ by hwdom. Some platforms use SCMI for access to system-level resources.
>
>      Status: Supported
>
> +### Arm: SCMI SMC multi-agent support
> +
> +Enable support for the multi-agent configuration of the EL3 Firmware, which
> +allows Xen to provide an SCMI interface to the Domains.
> +Xen manages access permissions to the HW resources and provides an SCMI interface
> +to the Domains. Each Domain is represented as a separate Agent, which can
> +communicate with EL3 Firmware using a dedicated shared memory region, and
> +notifications are passed through by Xen.
> +
> +    Status, ARM64: Tech Preview
> +
>  ### ARM: Guest PSCI support
>
>  Emulated PSCI interface exposed to guests. We support all mandatory
> diff --git a/docs/misc/arm/device-tree/booting.txt b/docs/misc/arm/device-tree/booting.txt
> index bcb06bc796..1d0382a865 100644
> --- a/docs/misc/arm/device-tree/booting.txt
> +++ b/docs/misc/arm/device-tree/booting.txt
> @@ -331,6 +331,21 @@ with the following properties:
>      Should be used together with scmi-smc-passthrough Xen command line
>      option.
>
> +    - "scmi_smc_multiagent"
> +
> +    Enables ARM SCMI SMC multi-agent support for the guest by enabling SCMI over
> +    SMC calls forwarding from domain to the EL3 firmware (like ARM
> +    Trusted Firmware-A) with a multi SCMI OSPM agent support.
> +    The SCMI agent_id should be specified for the guest with "xen,sci-agent-id"
> +    property.
> +
> +- "xen,sci-agent-id"
> +
> +    Specifies ARM SCMI agent id for the guest. This option is mandatory if the
> +    SCMI SMC "scmi_smc_multiagent" support is enabled for the guest. The agent ids
> +    of guest must be unique and in the range [0..254]. UINT8_MAX (255) is
> +    treated as invalid.
> +
>  - v8r_el1_msa
>
>      A string property specifying whether, on Armv8-R systems at EL1, a domain
> @@ -847,3 +862,185 @@ The automatically allocated static shared memory will get mapped at
>  0x80000000 in DomU1 guest physical address space, and at 0x90000000 in DomU2
>  guest physical address space. DomU1 is explicitly defined as the owner domain,
>  and DomU2 is the borrower domain.
> +
> +SCMI SMC multi-agent support
> +============================
> +
> +For enabling the ARM SCMI SMC multi-agent support (enabled by CONFIG_SCMI_SMC_MA)
> +the Xen specific SCMI Agent's configuration shall be provided in the Host DT
> +according to the SCMI compliant EL3 Firmware specification with ARM SMC/HVC
> +transport. The SCMI configuration must live under the Xen SCMI container
> +"xen,sci" beneath "/chosen" (for example "/chosen/xen/xen_scmi_config/scmi"). The
> +Xen SCMI mediator will bind only to the "arm,scmi-smc" node that is a child of
> +this "xen,sci" container; any other "arm,scmi-smc" nodes (for example under
> +"/firmware") are ignored to avoid stealing the host's SCMI OSPM instance.
> +
> +- scmi-secondary-agents
> +
> +    Defines a set of SCMI agents configuration supported by SCMI EL3 FW and
> +    available for Xen. Each Agent defined as triple consisting of:
> +    SMC/HVC function_id assigned for the agent transport ("arm,smc-id"),
> +    phandle to SCMI SHM assigned for the agent transport ("arm,scmi-shmem"),
> +    SCMI agent_id (optional) if not set - Xen will determine Agent ID for
> +    each provided channel using BASE_DISCOVER_AGENT message.
> +
> +- xen,dom0-sci-agent-id
> +
> +    Optional. Specifies the Dom0/hwdom SCMI agent_id inside the ``xen,sci``
> +    container. When provided, Dom0 will be configured for SCMI multi-agent
> +    support; when omitted, SCMI remains disabled for Dom0. The value must
> +    match the ``func_id`` and shmem pairing that EL3 firmware exposes for
> +    Dom0 (for example via ``/firmware/scmi``).
> +
> +As an example:
> +
> +/ {
> +    chosen {
> +        xen {
> +            ranges;
> +            xen_scmi_config {
> +                compatible = "xen,sci";
> +                #address-cells = <2>;
> +                #size-cells = <2>;
> +                ranges;
> +
> +                xen,dom0-sci-agent-id = <0>; <--- dom0 agent id
> +                scmi-secondary-agents = <
> +                    0x82000002 &scmi_shm_0 0
> +                    0x82000004 &scmi_shm_2 2
> +                    0x82000005 &scmi_shm_3 3>; <--- func_id, shmem, agent_id
> +                #scmi-secondary-agents-cells = <3>;
> +
> +                scmi_shm_0: sram@47ff0000 {
> +                    compatible = "arm,scmi-shmem";
> +                    reg = <0x0 0x47ff0000 0x0 0x1000>;
> +                };
> +
> +                /* Xen SCMI management channel */
> +                scmi_shm_1: sram@47ff1000 {
> +                    compatible = "arm,scmi-shmem";
> +                    reg = <0x0 0x47ff1000 0x0 0x1000>;
> +                };
> +
> +                scmi_shm_2: sram@47ff2000 {
> +                    compatible = "arm,scmi-shmem";
> +                    reg = <0x0 0x47ff2000 0x0 0x1000>;
> +                };
> +
> +                scmi_shm_3: sram@47ff3000 {
> +                    compatible = "arm,scmi-shmem";
> +                    reg = <0x0 0x47ff3000 0x0 0x1000>;
> +                };
> +
> +                scmi_xen: scmi {
> +                    compatible = "arm,scmi-smc";
> +                    arm,smc-id = <0x82000003>; <--- Xen management agent func_id
> +                    #address-cells = <1>;
> +                    #size-cells = <0>;
> +                    #access-controller-cells = <1>;
> +                    shmem = <&scmi_shm_1>; <--- Xen management agent shmem
> +                };
> +            };
> +        };
> +    };
> +};
> +
> +Note: This example keeps the Host DT unchanged for Dom0 and baremetal Linux
> +by using func_id 0x82000002 / shmem 0x47ff0000 for Dom0, while Xen uses a
> +separate privileged channel func_id 0x82000003 / shmem 0x47ff1000. EL3
> +firmware enforces permissions per agent_id, so there is no conflict between
> +Dom0 and Xen channels.
> +
> +- #scmi-secondary-agents-cells
> +
> +    Defines whether Agent_id is set in the "scmi-secondary-agents" property.
> +    Possible values are: 2, 3.
> +    When set to 3 (the default), expect agent_id to be present in the secondary
> +    agents list.
> +    When set to 2, agent_id will be discovered for each channel using
> +    BASE_DISCOVER_AGENT message.
> +
> +
> +Example:
> +
> +/ {
> +    chosen {
> +        xen {
> +            ranges;
> +            xen_scmi_config {
> +                compatible = "xen,sci";
> +                #address-cells = <2>;
> +                #size-cells = <2>;
> +                ranges;
> +
> +                /* Shared memory nodes as in the previous example */
> +
> +                scmi-secondary-agents = <
> +                    0x82000002 &scmi_shm_0
> +                    0x82000004 &scmi_shm_2
> +                    0x82000005 &scmi_shm_3
> +                    0x82000006 &scmi_shm_4>;
> +                #scmi-secondary-agents-cells = <2>;
> +
> +                scmi_xen: scmi {
> +                    compatible = "arm,scmi-smc";
> +                    arm,smc-id = <0x82000003>; <--- Xen management agent func_id
> +                    #address-cells = <1>;
> +                    #size-cells = <0>;
> +                    #access-controller-cells = <1>;
> +                    shmem = <&scmi_shm_1>; <--- Xen management agent shmem
> +                };
> +            };
> +        };
> +    };
> +};
> +
> +Dom0less example (multi-agent)
> +-------------------------------
> +
> +Below is a minimal dom0less configuration showing how to enable SCMI SMC
> +multi-agent for a pre-defined guest domain using xen,sci_type and
> +xen,sci-agent-id, together with the Xen SCMI container:
> +
> +chosen {
> +    xen {
> +        ranges;
> +        xen_scmi_config {
> +            compatible = "xen,sci";
> +            #address-cells = <2>;
> +            #size-cells = <2>;
> +            ranges;
> +
> +            /* Xen management channel shared memory */
> +            scmi_shm_1: sram@47ff1000 {
> +                compatible = "arm,scmi-shmem";
> +                reg = <0x0 0x47ff1000 0x0 0x1000>;
> +            };
> +
> +            scmi_shm_domu: sram@47ff2000 {
> +                compatible = "arm,scmi-shmem";
> +                reg = <0x0 0x47ff2000 0x0 0x1000>;
> +            };
> +
> +            scmi-secondary-agents = <
> +                0x82000004 &scmi_shm_domu 2>;
> +            #scmi-secondary-agents-cells = <3>;
> +
> +            scmi_xen: scmi {
> +                compatible = "arm,scmi-smc";
> +                arm,smc-id = <0x82000003>;
> +                #address-cells = <1>;
> +                #size-cells = <0>;
> +                #access-controller-cells = <1>;
> +                shmem = <&scmi_shm_1>;
> +            };
> +        };
> +    };
> +
> +    xen,domain@1 {
> +        compatible = "xen,domain";
> +        xen,sci_type = "scmi_smc_multiagent";
> +        xen,sci-agent-id = <2>;
> +        /* Additional domain properties (memory, cpus, kernels, etc.) */
> +    };
> +};
> diff --git a/xen/arch/arm/dom0less-build.c b/xen/arch/arm/dom0less-build.c
> index 3f48f74226..50e2516b9e 100644
> --- a/xen/arch/arm/dom0less-build.c
> +++ b/xen/arch/arm/dom0less-build.c
> @@ -292,6 +292,23 @@ static int __init domu_dt_sci_parse(struct dt_device_node *node,
>
>          d_cfg->arch.arm_sci_type = XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC;
>      }
> +    else if ( !strcmp(sci_type, "scmi_smc_multiagent") )
> +    {
> +        uint32_t agent_id = 0;
> +
> +        if ( !IS_ENABLED(CONFIG_SCMI_SMC_MA) )
> +        {
> +            printk(XENLOG_ERR "xen,sci_type=scmi_smc_multiagent requested, but CONFIG_SCMI_SMC_MA not set\n");
> +            return -EINVAL;
> +        }
> +
> +        if ( !dt_property_read_u32(node, "xen,sci-agent-id", &agent_id) ||
> +             agent_id >= UINT8_MAX )
> +            return -EINVAL;
> +
> +        d_cfg->arch.arm_sci_type = XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC_MA;
> +        d_cfg->arch.arm_sci_agent_id = agent_id;
> +    }
>      else
>      {
>          printk(XENLOG_ERR "xen,sci_type in not valid (%s) for domain %s\n",
> diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
> index 72d5316180..297c46d5a2 100644
> --- a/xen/arch/arm/domain_build.c
> +++ b/xen/arch/arm/domain_build.c
> @@ -87,6 +87,39 @@ int __init parse_arch_dom0_param(const char *s, const char *e)
>      return -EINVAL;
>  }
>
> +#ifdef CONFIG_SCMI_SMC_MA
> +/* SCMI agent ID for dom0 obtained from xen,sci container */
> +#define SCMI_AGENT_ID_INVALID UINT8_MAX
> +
> +static uint8_t __init get_dom0_scmi_agent_id(void)
> +{
> +    const struct dt_device_node *config_node;
> +    u32 val;
> +    const struct dt_property *prop;
> +
> +    config_node = dt_find_compatible_node(NULL, NULL, "xen,sci");
> +    if ( !config_node )
> +        return SCMI_AGENT_ID_INVALID;
> +
> +    prop = dt_find_property(config_node, "xen,dom0-sci-agent-id", NULL);
> +    if ( !prop )
> +        return SCMI_AGENT_ID_INVALID;
> +
> +    if ( !dt_property_read_u32(config_node, "xen,dom0-sci-agent-id", &val) )
> +        return SCMI_AGENT_ID_INVALID;
> +
> +    if ( val >= SCMI_AGENT_ID_INVALID )
> +    {
> +         printk(XENLOG_WARNING
> +             "Invalid xen,dom0-sci-agent-id=%u, SCMI disabled for Dom0\n",
> +             val);
> +        return SCMI_AGENT_ID_INVALID;
> +    }
> +
> +    return val;
> +}
> +#endif /* CONFIG_SCMI_SMC_MA */
> +
>  /* Override macros from asm/page.h to make them work with mfn_t */
>  #undef virt_to_mfn
>  #define virt_to_mfn(va) _mfn(__virt_to_mfn(va))
> @@ -1479,6 +1512,7 @@ static int __init handle_node(struct domain *d, struct kernel_info *kinfo,
>          DT_MATCH_TYPE("memory"),
>          /* The memory mapped timer is not supported by Xen. */
>          DT_MATCH_COMPATIBLE("arm,armv7-timer-mem"),
> +        DT_MATCH_COMPATIBLE("xen,sci"),
>          { /* sentinel */ },
>      };
>      static const struct dt_device_match timer_matches[] __initconst =
> @@ -1965,6 +1999,15 @@ void __init create_dom0(void)
>      dom0_cfg.arch.tee_type = tee_get_type();
>      dom0_cfg.max_vcpus = dom0_max_vcpus();
>
> +#ifdef CONFIG_SCMI_SMC_MA
> +    /* Set up SCMI agent ID if provided in the xen,sci container */
> +    dom0_cfg.arch.arm_sci_agent_id = get_dom0_scmi_agent_id();
> +    dom0_cfg.arch.arm_sci_type = (dom0_cfg.arch.arm_sci_agent_id !=
> +                                  SCMI_AGENT_ID_INVALID) ?
> +                                 XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC_MA :
> +                                 XEN_DOMCTL_CONFIG_ARM_SCI_NONE;
> +#endif
> +
>      if ( iommu_enabled )
>          dom0_cfg.flags |= XEN_DOMCTL_CDF_iommu;
>
> diff --git a/xen/arch/arm/firmware/Kconfig b/xen/arch/arm/firmware/Kconfig
> index 5c5f0880c4..972cd9b173 100644
> --- a/xen/arch/arm/firmware/Kconfig
> +++ b/xen/arch/arm/firmware/Kconfig
> @@ -29,6 +29,18 @@ config SCMI_SMC
>         driver domain.
>         Use with EL3 firmware which supports only single SCMI OSPM agent.
>
> +config SCMI_SMC_MA
> +     bool "Enable ARM SCMI SMC multi-agent driver"
> +     depends on ARM_64
> +     select ARM_SCI
> +     help
> +       Enables SCMI SMC/HVC multi-agent in XEN to pass SCMI requests from Domains
> +       to EL3 firmware (TF-A) which supports multi-agent feature.
> +       This feature allows to enable SCMI per Domain using unique SCMI agent_id,
> +       so Domain is identified by EL3 firmware as an SCMI Agent and can access
> +       allowed platform resources through dedicated SMC/HVC Shared memory based
> +       transport.
> +
>  endchoice
>
>  endmenu
> diff --git a/xen/arch/arm/firmware/Makefile b/xen/arch/arm/firmware/Makefile
> index 71bdefc24a..37927e690e 100644
> --- a/xen/arch/arm/firmware/Makefile
> +++ b/xen/arch/arm/firmware/Makefile
> @@ -1,2 +1,3 @@
>  obj-$(CONFIG_ARM_SCI) += sci.o
>  obj-$(CONFIG_SCMI_SMC) += scmi-smc.o
> +obj-$(CONFIG_SCMI_SMC_MA) += scmi-shmem.o scmi-smc-multiagent.o
> diff --git a/xen/arch/arm/firmware/scmi-proto.h b/xen/arch/arm/firmware/scmi-proto.h
> new file mode 100644
> index 0000000000..49f63cfc0a
> --- /dev/null
> +++ b/xen/arch/arm/firmware/scmi-proto.h
> @@ -0,0 +1,164 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Arm System Control and Management Interface definitions
> + * Version 3.0 (DEN0056C)
> + *
> + * Copyright (c) 2025 EPAM Systems
> + */
> +
> +#ifndef ARM_FIRMWARE_SCMI_PROTO_H_
> +#define ARM_FIRMWARE_SCMI_PROTO_H_
> +
> +#include <xen/stdint.h>
> +
> +#define SCMI_SHORT_NAME_MAX_SIZE 16
> +
> +/* SCMI status codes. See section 4.1.4 */
> +#define SCMI_SUCCESS              0
> +#define SCMI_NOT_SUPPORTED      (-1)
> +#define SCMI_INVALID_PARAMETERS (-2)
> +#define SCMI_DENIED             (-3)
> +#define SCMI_NOT_FOUND          (-4)
> +#define SCMI_OUT_OF_RANGE       (-5)
> +#define SCMI_BUSY               (-6)
> +#define SCMI_COMMS_ERROR        (-7)
> +#define SCMI_GENERIC_ERROR      (-8)
> +#define SCMI_HARDWARE_ERROR     (-9)
> +#define SCMI_PROTOCOL_ERROR     (-10)
> +
> +/* Protocol IDs */
> +#define SCMI_BASE_PROTOCOL 0x10
> +
> +/* Base protocol message IDs */
> +#define SCMI_BASE_PROTOCOL_VERSION            0x0
> +#define SCMI_BASE_PROTOCOL_ATTIBUTES          0x1
> +#define SCMI_BASE_PROTOCOL_MESSAGE_ATTRIBUTES 0x2
> +#define SCMI_BASE_DISCOVER_AGENT              0x7
> +#define SCMI_BASE_SET_DEVICE_PERMISSIONS      0x9
> +#define SCMI_BASE_RESET_AGENT_CONFIGURATION   0xB
> +
> +typedef struct scmi_msg_header {
> +    uint8_t id;
> +    uint8_t type;
> +    uint8_t protocol;
> +    uint32_t status;
> +} scmi_msg_header_t;
> +
> +/* Table 2 Message header format */
> +#define SCMI_HDR_ID    GENMASK(7, 0)
> +#define SCMI_HDR_TYPE  GENMASK(9, 8)
> +#define SCMI_HDR_PROTO GENMASK(17, 10)
> +
> +#define SCMI_FIELD_GET(_mask, _reg)                                            \
> +    ((typeof(_mask))(((_reg) & (_mask)) >> (ffs64(_mask) - 1)))
> +#define SCMI_FIELD_PREP(_mask, _val)                                           \
> +    (((typeof(_mask))(_val) << (ffs64(_mask) - 1)) & (_mask))
> +
> +static inline uint32_t pack_scmi_header(scmi_msg_header_t *hdr)
> +{
> +    return SCMI_FIELD_PREP(SCMI_HDR_ID, hdr->id) |
> +           SCMI_FIELD_PREP(SCMI_HDR_TYPE, hdr->type) |
> +           SCMI_FIELD_PREP(SCMI_HDR_PROTO, hdr->protocol);
> +}
> +
> +static inline void unpack_scmi_header(uint32_t msg_hdr, scmi_msg_header_t *hdr)
> +{
> +    hdr->id = SCMI_FIELD_GET(SCMI_HDR_ID, msg_hdr);
> +    hdr->type = SCMI_FIELD_GET(SCMI_HDR_TYPE, msg_hdr);
> +    hdr->protocol = SCMI_FIELD_GET(SCMI_HDR_PROTO, msg_hdr);
> +}
> +
> +static inline int scmi_to_xen_errno(int scmi_status)
> +{
> +    if ( scmi_status == SCMI_SUCCESS )
> +        return 0;
> +
> +    switch ( scmi_status )
> +    {
> +    case SCMI_NOT_SUPPORTED:
> +        return -EOPNOTSUPP;
> +    case SCMI_INVALID_PARAMETERS:
> +        return -EINVAL;
> +    case SCMI_DENIED:
> +        return -EACCES;
> +    case SCMI_NOT_FOUND:
> +        return -ENOENT;
> +    case SCMI_OUT_OF_RANGE:
> +        return -ERANGE;
> +    case SCMI_BUSY:
> +        return -EBUSY;
> +    case SCMI_COMMS_ERROR:
> +        return -ENOTCONN;
> +    case SCMI_GENERIC_ERROR:
> +        return -EIO;
> +    case SCMI_HARDWARE_ERROR:
> +        return -ENXIO;
> +    case SCMI_PROTOCOL_ERROR:
> +        return -EBADMSG;
> +    default:
> +        return -EINVAL;
> +    }
> +}
> +
> +/* PROTOCOL_VERSION */
> +#define SCMI_VERSION_MINOR GENMASK(15, 0)
> +#define SCMI_VERSION_MAJOR GENMASK(31, 16)
> +
> +struct scmi_msg_prot_version_p2a {
> +    uint32_t version;
> +} __packed;
> +
> +/* BASE PROTOCOL_ATTRIBUTES */
> +#define SCMI_BASE_ATTR_NUM_PROTO GENMASK(7, 0)
> +#define SCMI_BASE_ATTR_NUM_AGENT GENMASK(15, 8)
> +
> +struct scmi_msg_base_attributes_p2a {
> +    uint32_t attributes;
> +} __packed;
> +
> +/*
> + * BASE_DISCOVER_AGENT
> + */
> +#define SCMI_BASE_AGENT_ID_OWN 0xFFFFFFFF
> +
> +struct scmi_msg_base_discover_agent_a2p {
> +    uint32_t agent_id;
> +} __packed;
> +
> +struct scmi_msg_base_discover_agent_p2a {
> +    uint32_t agent_id;
> +    char name[SCMI_SHORT_NAME_MAX_SIZE];
> +} __packed;
> +
> +/*
> + * BASE_SET_DEVICE_PERMISSIONS
> + */
> +#define SCMI_BASE_DEVICE_ACCESS_ALLOW           BIT(0, UL)
> +
> +struct scmi_msg_base_set_device_permissions_a2p {
> +    uint32_t agent_id;
> +    uint32_t device_id;
> +    uint32_t flags;
> +} __packed;
> +
> +/*
> + * BASE_RESET_AGENT_CONFIGURATION
> + */
> +#define SCMI_BASE_AGENT_PERMISSIONS_RESET       BIT(0, UL)
> +
> +struct scmi_msg_base_reset_agent_cfg_a2p {
> +    uint32_t agent_id;
> +    uint32_t flags;
> +} __packed;
> +
> +#endif /* ARM_FIRMWARE_SCMI_PROTO_H_ */
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * tab-width: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/xen/arch/arm/firmware/scmi-shmem.c b/xen/arch/arm/firmware/scmi-shmem.c
> new file mode 100644
> index 0000000000..e36745a85e
> --- /dev/null
> +++ b/xen/arch/arm/firmware/scmi-shmem.c
> @@ -0,0 +1,118 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * SMC/HVC shmem transport implementation used by
> + * SCI SCMI multi-agent driver.
> + *
> + * Oleksii Moisieiev <oleksii_moisieiev@epam.com>
> + * Copyright (c) 2025 EPAM Systems
> + */
> +
> +#include <xen/err.h>
> +#include <xen/io.h>
> +#include <asm/io.h>
> +
> +#include "scmi-proto.h"
> +#include "scmi-shmem.h"
> +
> +static inline int
> +shmem_channel_status(const volatile struct scmi_shared_mem __iomem *shmem)
> +{
> +    return (readl(&shmem->channel_status) &
> +            SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE) ? 0 : -EBUSY;
> +}
> +
> +int shmem_put_message(volatile struct scmi_shared_mem __iomem *shmem,
> +                      scmi_msg_header_t *hdr, void *data, unsigned int len)
> +{
> +    int ret;
> +
> +    if ( (len + offsetof(struct scmi_shared_mem, msg_payload)) >
> +         SCMI_SHMEM_MAPPED_SIZE )
> +    {
> +        printk(XENLOG_ERR "scmi: Wrong size of smc message. Data is invalid\n");
> +        return -EINVAL;
> +    }
> +
> +    ret = shmem_channel_status(shmem);
> +    if ( ret )
> +        return ret;
> +
> +    writel_relaxed(0x0, &shmem->channel_status);
> +    /* Writing 0x0 right now, but "shmem"_FLAG_INTR_ENABLED can be set */
> +    writel_relaxed(0x0, &shmem->flags);
> +    writel_relaxed(sizeof(shmem->msg_header) + len, &shmem->length);
> +    writel(pack_scmi_header(hdr), &shmem->msg_header);
> +
> +    if ( len > 0 && data )
> +        memcpy_toio(shmem->msg_payload, data, len);
> +
> +    return 0;
> +}
> +
> +int shmem_get_response(const volatile struct scmi_shared_mem __iomem *shmem,
> +                       scmi_msg_header_t *hdr, void *data, unsigned int len)
> +{
> +    int recv_len;
> +    int ret;
> +    /*
> +     * First word of msg_payload carries the returned status; exclude it from
> +     * recv_len so only the protocol payload is copied back to the caller.
> +     */
> +    int pad = sizeof(hdr->status);
> +
> +    if ( len >= SCMI_SHMEM_MAPPED_SIZE -
> +         offsetof(struct scmi_shared_mem, msg_payload) )
> +    {
> +        printk(XENLOG_ERR
> +               "scmi: Wrong size of input smc message. Data may be invalid\n");
> +        return -EINVAL;
> +    }
> +
> +    ret = shmem_channel_status(shmem);
> +    if ( ret )
> +        return ret;
> +
> +    recv_len = readl(&shmem->length) - sizeof(shmem->msg_header);
> +
> +    if ( recv_len < 0 )
> +    {
> +        printk(XENLOG_ERR
> +               "scmi: Wrong size of smc message. Data may be invalid\n");
> +        return -EINVAL;
> +    }
> +
> +    unpack_scmi_header(readl(&shmem->msg_header), hdr);
> +
> +    hdr->status = readl(&shmem->msg_payload);
> +    recv_len = recv_len > pad ? recv_len - pad : 0;
> +
> +    ret = scmi_to_xen_errno(hdr->status);
> +    if ( ret )
> +    {
> +        printk(XENLOG_DEBUG "scmi: Error received: %d\n", ret);
> +        return ret;
> +    }
> +
> +    if ( recv_len > len )
> +    {
> +        printk(XENLOG_ERR
> +               "scmi: Not enough buffer for message %d, expecting %d\n",
> +               recv_len, len);
> +        return -EINVAL;
> +    }
> +
> +    if ( recv_len > 0 )
> +        memcpy_fromio(data, shmem->msg_payload + pad, recv_len);
> +
> +    return 0;
> +}
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * tab-width: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/xen/arch/arm/firmware/scmi-shmem.h b/xen/arch/arm/firmware/scmi-shmem.h
> new file mode 100644
> index 0000000000..722263aa77
> --- /dev/null
> +++ b/xen/arch/arm/firmware/scmi-shmem.h
> @@ -0,0 +1,45 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Arm System Control and Management Interface definitions
> + * Version 3.0 (DEN0056C)
> + * Shared Memory based Transport
> + *
> + * Copyright (c) 2024 EPAM Systems
> + */
> +
> +#ifndef ARM_FIRMWARE_SCMI_SHMEM_H_
> +#define ARM_FIRMWARE_SCMI_SHMEM_H_
> +
> +#include <xen/stdint.h>
> +
> +#define SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE  BIT(0, UL)
> +#define SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR BIT(1, UL)
> +
> +struct scmi_shared_mem {
> +    uint32_t reserved;
> +    uint32_t channel_status;
> +    uint32_t reserved1[2];
> +    uint32_t flags;
> +    uint32_t length;
> +    uint32_t msg_header;
> +    uint8_t msg_payload[];
> +};
> +
> +#define SCMI_SHMEM_MAPPED_SIZE PAGE_SIZE
> +
> +int shmem_put_message(volatile struct scmi_shared_mem __iomem *shmem,
> +                      scmi_msg_header_t *hdr, void *data, unsigned int len);
> +
> +int shmem_get_response(const volatile struct scmi_shared_mem __iomem *shmem,
> +                       scmi_msg_header_t *hdr, void *data, unsigned int len);
> +#endif /* ARM_FIRMWARE_SCMI_SHMEM_H_ */
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * tab-width: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/xen/arch/arm/firmware/scmi-smc-multiagent.c b/xen/arch/arm/firmware/scmi-smc-multiagent.c
> new file mode 100644
> index 0000000000..0c5653fbc0
> --- /dev/null
> +++ b/xen/arch/arm/firmware/scmi-smc-multiagent.c
> @@ -0,0 +1,816 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * SCI SCMI multi-agent driver, using SMC/HVC shmem as transport.
> + *
> + * Oleksii Moisieiev <oleksii_moisieiev@epam.com>
> + * Copyright (c) 2025 EPAM Systems
> + */
> +
> +#include <xen/acpi.h>
> +
> +#include <xen/device_tree.h>
> +#include <xen/init.h>
> +#include <xen/iocap.h>
> +#include <xen/err.h>
> +#include <xen/libfdt/libfdt.h>
> +#include <xen/string.h>
> +#include <xen/param.h>
> +#include <xen/sched.h>
> +#include <xen/vmap.h>
> +
> +#include <asm/firmware/sci.h>
> +#include <asm/smccc.h>
> +
> +#include "scmi-proto.h"
> +#include "scmi-shmem.h"
> +
> +#define SCMI_SECONDARY_AGENTS "scmi-secondary-agents"
> +
> +struct scmi_channel {
> +    uint32_t agent_id;
> +    uint32_t func_id;
> +    domid_t domain_id;
> +    uint64_t paddr;
> +    struct scmi_shared_mem __iomem *shmem;
> +    spinlock_t lock;
> +    struct list_head list;
> +};
> +
> +struct scmi_data {
> +    struct list_head channel_list;
> +    spinlock_t channel_list_lock;
> +    uint32_t func_id;
> +    bool initialized;
> +    uint32_t shmem_phandle;
> +    uint32_t hyp_channel_agent_id;
> +    struct dt_device_node *dt_dev;
> +};
> +
> +static struct scmi_data scmi_data;
> +
> +static bool scmi_is_under_xen_sci(const struct dt_device_node *node)
> +{
> +    const struct dt_device_node *p;
> +
> +    for ( p = node->parent; p; p = p->parent )
> +        if ( dt_device_is_compatible(p, "xen,sci") )
> +            return true;
> +
> +    return false;
> +}
> +
> +static int send_smc_message(struct scmi_channel *chan_info,
> +                            scmi_msg_header_t *hdr, void *data, int len)
> +{
> +    struct arm_smccc_res resp;
> +    int ret;
> +
> +    ret = shmem_put_message(chan_info->shmem, hdr, data, len);
> +    if ( ret )
> +        return ret;
> +
> +    resp = arm_smccc_1_1_smc(chan_info->func_id, 0, 0, 0, 0, 0, 0, 0);
> +
> +    if ( resp.a0 == (unsigned long)ARM_SMCCC_INVALID_PARAMETER )
> +        return -EINVAL;
> +
> +    if ( resp.a0 )
> +        return -EOPNOTSUPP;
> +
> +    return 0;
> +}
> +
> +static int do_smc_xfer(struct scmi_channel *chan_info, scmi_msg_header_t *hdr,
> +                       void *tx_data, int tx_size, void *rx_data, int rx_size)
> +{
> +    int ret = 0;
> +
> +    ASSERT(chan_info && chan_info->shmem);
> +
> +    if ( !hdr )
> +        return -EINVAL;
> +
> +    spin_lock(&chan_info->lock);
> +
> +    printk(XENLOG_DEBUG
> +           "scmi: agent_id = %d msg_id = %x type = %d, proto = %x\n",
> +           chan_info->agent_id, hdr->id, hdr->type, hdr->protocol);
> +
> +    ret = send_smc_message(chan_info, hdr, tx_data, tx_size);
> +    if ( ret )
> +        goto clean;
> +
> +    ret = shmem_get_response(chan_info->shmem, hdr, rx_data, rx_size);
> +
> +clean:
> +    printk(XENLOG_DEBUG
> +           "scmi: get smc response agent_id = %d msg_id = %x proto = %x res=%d\n",
> +           chan_info->agent_id, hdr->id, hdr->protocol, ret);
> +
> +    spin_unlock(&chan_info->lock);
> +
> +    return ret;
> +}
> +
> +static struct scmi_channel *get_channel_by_id(uint32_t agent_id)
> +{
> +    struct scmi_channel *curr;
> +    bool found = false;
> +
> +    spin_lock(&scmi_data.channel_list_lock);
> +    list_for_each_entry(curr, &scmi_data.channel_list, list)
> +    {
> +        if ( curr->agent_id == agent_id )
> +        {
> +            found = true;
> +            break;
> +        }
> +    }
> +
> +    spin_unlock(&scmi_data.channel_list_lock);
> +    if ( found )
> +        return curr;
> +
> +    return NULL;
> +}
> +
> +static struct scmi_channel *acquire_scmi_channel(struct domain *d,
> +                                                 uint32_t agent_id)
> +{
> +    struct scmi_channel *curr;
> +    struct scmi_channel *ret = ERR_PTR(-ENOENT);
> +
> +    spin_lock(&scmi_data.channel_list_lock);
> +    list_for_each_entry(curr, &scmi_data.channel_list, list)
> +    {
> +        if ( curr->agent_id == agent_id )
> +        {
> +            if ( curr->domain_id != DOMID_INVALID )
> +            {
> +                ret = ERR_PTR(-EEXIST);
> +                break;
> +            }
> +
> +            curr->domain_id = d->domain_id;
> +            ret = curr;
> +            break;
> +        }
> +    }
> +
> +    spin_unlock(&scmi_data.channel_list_lock);
> +
> +    return ret;
> +}
> +
> +static void relinquish_scmi_channel(struct scmi_channel *channel)
> +{
> +    ASSERT(channel != NULL);
> +
> +    spin_lock(&scmi_data.channel_list_lock);
> +    channel->domain_id = DOMID_INVALID;
> +    spin_unlock(&scmi_data.channel_list_lock);
> +}
> +
> +static int map_channel_memory(struct scmi_channel *channel)
> +{
> +    ASSERT(channel && channel->paddr);
> +    channel->shmem = ioremap_nocache(channel->paddr, SCMI_SHMEM_MAPPED_SIZE);
> +    if ( !channel->shmem )
> +        return -ENOMEM;
> +
> +    channel->shmem->channel_status = SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE;
> +    printk(XENLOG_DEBUG "scmi: Got shmem %lx after vmap %p\n", channel->paddr,
> +           channel->shmem);
> +
> +    return 0;
> +}
> +
> +static void unmap_channel_memory(struct scmi_channel *channel)
> +{
> +    ASSERT(channel);
> +
> +    if ( !channel->shmem )
> +        return;
> +
> +    iounmap(channel->shmem);
> +    channel->shmem = NULL;
> +}
> +
> +static struct scmi_channel *smc_create_channel(uint32_t agent_id,
> +                                               uint32_t func_id, uint64_t addr)
> +{
> +    struct scmi_channel *channel, *curr;
> +
> +    spin_lock(&scmi_data.channel_list_lock);
> +
> +    /* Check if channel already exists while holding the lock */
> +    list_for_each_entry(curr, &scmi_data.channel_list, list)
> +    {
> +        if ( curr->agent_id == agent_id )
> +        {
> +            spin_unlock(&scmi_data.channel_list_lock);
> +            return ERR_PTR(-EEXIST);
> +        }
> +    }
> +
> +    channel = xmalloc(struct scmi_channel);
> +    if ( !channel )
> +    {
> +        spin_unlock(&scmi_data.channel_list_lock);
> +        return ERR_PTR(-ENOMEM);
> +    }
> +
> +    spin_lock_init(&channel->lock);
> +    channel->agent_id = agent_id;
> +    channel->func_id = func_id;
> +    channel->domain_id = DOMID_INVALID;
> +    channel->shmem = NULL;
> +    channel->paddr = addr;
> +    list_add_tail(&channel->list, &scmi_data.channel_list);
> +
> +    spin_unlock(&scmi_data.channel_list_lock);
> +    return channel;
> +}
> +
> +static void free_channel_list(void)
> +{
> +    struct scmi_channel *curr, *_curr;
> +    /*
> +     * Called only on the __init error path, before any runtime users exist,
> +     * so no other thread is iterating channel_list. Keep the lock for the
> +     * whole drain for clarity and to avoid misleading readers about possible
> +     * concurrent access.
> +     */
> +    spin_lock(&scmi_data.channel_list_lock);
> +    list_for_each_entry_safe(curr, _curr, &scmi_data.channel_list, list)
> +    {
> +        list_del(&curr->list);
> +        xfree(curr);
> +    }
> +    spin_unlock(&scmi_data.channel_list_lock);
> +}
> +
> +static int __init
> +scmi_dt_read_hyp_channel_addr(struct dt_device_node *scmi_node, u64 *addr,
> +                              u64 *size)
> +{
> +    struct dt_device_node *shmem_node;
> +    const __be32 *prop;
> +
> +    prop = dt_get_property(scmi_node, "shmem", NULL);
> +    if ( !prop )
> +        return -EINVAL;
> +
> +    shmem_node = dt_find_node_by_phandle(be32_to_cpu(*prop));
> +    if ( IS_ERR_OR_NULL(shmem_node) )
> +    {
> +        printk(XENLOG_ERR
> +               "scmi: Device tree error, can't parse reserved memory %ld\n",
> +               PTR_ERR(shmem_node));
> +        return PTR_ERR(shmem_node);
> +    }
> +
> +    return dt_device_get_address(shmem_node, 0, addr, size);
> +}
> +
> +/*
> + * Handle Dom0 SCMI specific DT nodes
> + *
> + * Make a decision on copying SCMI specific nodes into Dom0 device tree.
> + * For SCMI multi-agent case:
> + * - shmem nodes will not be copied and generated instead if SCMI
> + *   is enabled for Dom0
> + * - scmi node will be copied if SCMI is enabled for Dom0
> + */
> +static bool scmi_dt_handle_node(struct domain *d, struct dt_device_node *node)
> +{
> +    static const struct dt_device_match shmem_matches[] __initconst = {
> +        DT_MATCH_COMPATIBLE("arm,scmi-shmem"),
> +        { /* sentinel */ },
> +    };
> +    static const struct dt_device_match scmi_matches[] __initconst = {
> +        DT_MATCH_PATH("/firmware/scmi"),
> +        { /* sentinel */ },
> +    };
> +
> +    if ( !scmi_data.initialized )
> +        return false;
> +
> +    /* skip scmi shmem node for dom0 if scmi not enabled */
> +    if ( dt_match_node(shmem_matches, node) && !sci_domain_is_enabled(d) )
> +    {
> +        dt_dprintk("  Skip scmi shmem node\n");
> +        return true;
> +    }
> +
> +    /* drop scmi if not enabled */
> +    if ( dt_match_node(scmi_matches, node) && !sci_domain_is_enabled(d) )
> +    {
> +        dt_dprintk("  Skip scmi node\n");
> +        return true;
> +    }
> +
> +    return false;
> +}
> +
> +static int scmi_assign_device(uint32_t agent_id, uint32_t device_id,
> +                              uint32_t flags)
> +{
> +    struct scmi_msg_base_set_device_permissions_a2p tx;
> +    struct scmi_channel *channel;
> +    scmi_msg_header_t hdr;
> +
> +    channel = get_channel_by_id(scmi_data.hyp_channel_agent_id);
> +    if ( !channel )
> +        return -EINVAL;
> +
> +    hdr.id = SCMI_BASE_SET_DEVICE_PERMISSIONS;
> +    hdr.type = 0;
> +    hdr.protocol = SCMI_BASE_PROTOCOL;
> +
> +    tx.agent_id = agent_id;
> +    tx.device_id = device_id;
> +    tx.flags = flags;
> +
> +    return do_smc_xfer(channel, &hdr, &tx, sizeof(tx), NULL, 0);
> +}
> +
> +static int scmi_dt_assign_device(struct domain *d,
> +                                 struct dt_phandle_args *ac_spec)
> +{
> +    struct scmi_channel *agent_channel;
> +    uint32_t scmi_device_id = ac_spec->args[0];
> +    int ret;
> +
> +    if ( !d->arch.sci_data )
> +        return 0;
> +
> +    /* The access-controllers is specified for DT dev, but it's not a SCMI */
> +    if ( !scmi_data.dt_dev ||
> +         !dt_node_path_is_equal(ac_spec->np, scmi_data.dt_dev->full_name) )
> +        return 0;
> +
> +    agent_channel = d->arch.sci_data;
> +
> +    spin_lock(&agent_channel->lock);
> +
> +    ret = scmi_assign_device(agent_channel->agent_id, scmi_device_id,
> +                             SCMI_BASE_DEVICE_ACCESS_ALLOW);
> +    if ( ret )
> +    {
> +        printk(XENLOG_ERR
> +               "scmi: could not assign dev for %pd agent:%d dev_id:%u (%d)",
> +               d, agent_channel->agent_id, scmi_device_id, ret);
> +    }
> +
> +    spin_unlock(&agent_channel->lock);
> +    return ret;
> +}
> +
> +static int collect_agent_id(struct scmi_channel *agent_channel)
> +{
> +    int ret;
> +    scmi_msg_header_t hdr;
> +    struct scmi_msg_base_discover_agent_p2a da_rx;
> +    struct scmi_msg_base_discover_agent_a2p da_tx;
> +
> +    ret = map_channel_memory(agent_channel);
> +    if ( ret )
> +        return ret;
> +
> +    hdr.id = SCMI_BASE_DISCOVER_AGENT;
> +    hdr.type = 0;
> +    hdr.protocol = SCMI_BASE_PROTOCOL;
> +
> +    da_tx.agent_id = agent_channel->agent_id;
> +
> +    ret = do_smc_xfer(agent_channel, &hdr, &da_tx, sizeof(da_tx), &da_rx,
> +                        sizeof(da_rx));
> +    if ( agent_channel->domain_id != DOMID_XEN )
> +        unmap_channel_memory(agent_channel);
> +    if ( ret )
> +        return ret;
> +
> +    printk(XENLOG_DEBUG "id=0x%x name=%s\n", da_rx.agent_id, da_rx.name);
> +    agent_channel->agent_id = da_rx.agent_id;
> +    return 0;
> +}
> +
> +static __init int collect_agents(struct dt_device_node *scmi_node)
> +{
> +    const struct dt_device_node *config_node;
> +    const __be32 *prop;
> +    uint32_t len;
> +    const __be32 *end;
> +    uint32_t cells_per_entry = 3; /* Default to 3 cells if property is absent. */
> +
> +    config_node = dt_find_compatible_node(NULL, NULL, "xen,sci");
> +    if ( !config_node )
> +    {
> +        printk(XENLOG_WARNING "scmi: xen,sci node not found, no agents to collect.\n");
> +        return -ENOENT;
> +    }
> +
> +    /* Check for the optional '#scmi-secondary-agents-cells' property. */
> +    if ( dt_property_read_u32(config_node, "#scmi-secondary-agents-cells",
> +                              &cells_per_entry) )
> +    {
> +        if ( cells_per_entry != 2 && cells_per_entry != 3 )
> +        {
> +            printk(XENLOG_ERR "scmi: Invalid #scmi-secondary-agents-cells value: %u\n",
> +                   cells_per_entry);
> +            return -EINVAL;
> +        }
> +    }
> +
> +    prop = dt_get_property(config_node, SCMI_SECONDARY_AGENTS, &len);
> +    if ( !prop )
> +    {
> +        printk(XENLOG_ERR "scmi: No %s property found, no agents to collect.\n",
> +               SCMI_SECONDARY_AGENTS);
> +        return -EINVAL;
> +    }
> +
> +    /* Validate that the property length is a multiple of the cell size. */
> +    if ( len == 0 || len % (cells_per_entry * sizeof(uint32_t)) != 0 )
> +    {
> +        printk(XENLOG_ERR "scmi: Invalid length of %s property: %u for %u cells per entry\n",
> +               SCMI_SECONDARY_AGENTS, len, cells_per_entry);
> +        return -EINVAL;
> +    }
> +
> +    end = (const __be32 *)((const u8 *)prop + len);
> +
> +    for ( ; prop < end; )
> +    {
> +        uint32_t agent_id;
> +        uint32_t smc_id;
> +        uint32_t shmem_phandle;
> +        struct dt_device_node *node;
> +        u64 addr, size;
> +        int ret;
> +        struct scmi_channel *agent_channel;
> +
> +        smc_id = be32_to_cpu(*prop++);
> +        shmem_phandle = be32_to_cpu(*prop++);
> +
> +        if ( cells_per_entry == 3 )
> +            agent_id = be32_to_cpu(*prop++);
> +        else
> +            agent_id = SCMI_BASE_AGENT_ID_OWN;
> +
> +        node = dt_find_node_by_phandle(shmem_phandle);
> +        if ( !node )
> +        {
> +            printk(XENLOG_ERR "scmi: Could not find shmem node for agent %u\n",
> +                   agent_id);
> +            return -EINVAL;
> +        }
> +
> +        ret = dt_device_get_address(node, 0, &addr, &size);
> +        if ( ret )
> +        {
> +            printk(XENLOG_ERR
> +                   "scmi: Could not read shmem address for agent %u: %d\n",
> +                   agent_id, ret);
> +            return ret;
> +        }
> +
> +        if ( !IS_ALIGNED(size, SCMI_SHMEM_MAPPED_SIZE) ||
> +             !IS_ALIGNED(addr, SCMI_SHMEM_MAPPED_SIZE) )
> +        {
> +            printk(XENLOG_ERR "scmi: shmem memory is not aligned\n");
> +            return -EINVAL;
> +        }
> +
> +        agent_channel = smc_create_channel(agent_id, smc_id, addr);
> +        if ( IS_ERR(agent_channel) )
> +        {
> +            printk(XENLOG_ERR "scmi: Could not create channel for agent %u: %ld\n",
> +                   agent_id, PTR_ERR(agent_channel));
> +            return PTR_ERR(agent_channel);
> +        }
> +
> +        if ( cells_per_entry == 2 )
> +        {
> +            ret = collect_agent_id(agent_channel);
> +            if ( ret )
> +                return ret;
> +        }
> +
> +        printk(XENLOG_DEBUG "scmi: Agent %u SMC %X addr %lx\n", agent_channel->agent_id,
> +               smc_id, (unsigned long)addr);
> +    }
> +
> +    return 0;
> +}
> +
> +static int scmi_domain_init(struct domain *d,
> +                            struct xen_domctl_createdomain *config)
> +{
> +    struct scmi_channel *channel;
> +    int ret;
> +
> +    if ( !scmi_data.initialized )
> +        return 0;
> +
> +    /*
> +     * SCMI support is configured via:
> +     * - For dom0: xen,dom0-sci-agent-id property under the xen,sci container
> +     * - For dom0less: xen,sci-agent-id in the domain node
> +     * The config->arch.arm_sci_type and config->arch.arm_sci_agent_id
> +     * are already set by domain_build.c or dom0less-build.c
> +     */
> +
> +    if ( config->arch.arm_sci_type == XEN_DOMCTL_CONFIG_ARM_SCI_NONE )
> +        return 0;
> +
> +    channel = acquire_scmi_channel(d, config->arch.arm_sci_agent_id);
> +    if ( IS_ERR(channel) )
> +    {
> +        printk(XENLOG_ERR
> +               "scmi: Failed to acquire SCMI channel for agent_id %u: %ld\n",
> +               config->arch.arm_sci_agent_id, PTR_ERR(channel));
> +        return PTR_ERR(channel);
> +    }
> +
> +    printk(XENLOG_INFO
> +           "scmi: Acquire channel id = 0x%x, domain_id = %d paddr = 0x%lx\n",
> +           channel->agent_id, channel->domain_id, channel->paddr);
> +
> +    /*
> +     * Dom0 (if present) needs to have an access to the guest memory range
> +     * to satisfy iomem_access_permitted() check in XEN_DOMCTL_iomem_permission
> +     * domctl.
> +     */
> +    if ( hardware_domain && !is_hardware_domain(d) )
> +    {
> +        ret = iomem_permit_access(hardware_domain, paddr_to_pfn(channel->paddr),
> +                                  paddr_to_pfn(channel->paddr +
> +                                  SCMI_SHMEM_MAPPED_SIZE - 1));
> +        if ( ret )
> +            goto error;
> +    }
> +
> +    d->arch.sci_data = channel;
> +    d->arch.sci_enabled = true;
> +
> +    return 0;
> +
> +error:
> +    relinquish_scmi_channel(channel);
> +    return ret;
> +}
> +
> +int scmi_domain_sanitise_config(struct xen_domctl_createdomain *config)
> +{
> +    if ( config->arch.arm_sci_type != XEN_DOMCTL_CONFIG_ARM_SCI_NONE &&
> +         config->arch.arm_sci_type != XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC_MA )
> +    {
> +        dprintk(XENLOG_INFO, "scmi: Unsupported ARM_SCI type\n");
> +        return -EINVAL;
> +    }
> +
> +    return 0;
> +}
> +
> +static int scmi_relinquish_resources(struct domain *d)
> +{
> +    int ret;
> +    struct scmi_channel *channel, *agent_channel;
> +    scmi_msg_header_t hdr;
> +    struct scmi_msg_base_reset_agent_cfg_a2p tx;
> +
> +    if ( !d->arch.sci_data )
> +        return 0;
> +
> +    agent_channel = d->arch.sci_data;
> +
> +    spin_lock(&agent_channel->lock);
> +    tx.agent_id = agent_channel->agent_id;
> +    spin_unlock(&agent_channel->lock);
> +
> +    channel = get_channel_by_id(scmi_data.hyp_channel_agent_id);
> +    if ( !channel )
> +    {
> +        printk(XENLOG_ERR
> +               "scmi: Unable to get Hypervisor scmi channel for domain %d\n",
> +               d->domain_id);
> +        return -EINVAL;
> +    }
> +
> +    hdr.id = SCMI_BASE_RESET_AGENT_CONFIGURATION;
> +    hdr.type = 0;
> +    hdr.protocol = SCMI_BASE_PROTOCOL;
> +
> +    tx.flags = SCMI_BASE_AGENT_PERMISSIONS_RESET;
> +
> +    ret = do_smc_xfer(channel, &hdr, &tx, sizeof(tx), NULL, 0);
> +    if ( ret == -EOPNOTSUPP )
> +        return 0;
> +
> +    return ret;
> +}
> +
> +static void scmi_domain_destroy(struct domain *d)
> +{
> +    struct scmi_channel *channel;
> +
> +    if ( !d->arch.sci_data )
> +        return;
> +
> +    channel = d->arch.sci_data;
> +    spin_lock(&channel->lock);
> +
> +    relinquish_scmi_channel(channel);
> +    printk(XENLOG_DEBUG "scmi: Free domain %d\n", d->domain_id);
> +
> +    d->arch.sci_data = NULL;
> +    d->arch.sci_enabled = false;
> +
> +    spin_unlock(&channel->lock);
> +}
> +
> +static bool scmi_handle_call(struct cpu_user_regs *regs)
> +{
> +    uint32_t fid = (uint32_t)get_user_reg(regs, 0);
> +    struct scmi_channel *agent_channel;
> +    struct domain *d = current->domain;
> +    bool res = false;
> +
> +    if ( (!sci_domain_is_enabled(d)) || (!d->arch.sci_data) )
> +        return false;
> +
> +    agent_channel = d->arch.sci_data;
> +    spin_lock(&agent_channel->lock);
> +
> +    if ( agent_channel->func_id != fid )
> +    {
> +        res = false;
> +        goto unlock;
> +    }
> +
> +    arm_smccc_guest_smc(regs);
> +    res = true;
> +unlock:
> +    spin_unlock(&agent_channel->lock);
> +
> +    return res;
> +}
> +
> +static const struct sci_mediator_ops scmi_ops = {
> +    .domain_init = scmi_domain_init,
> +    .domain_destroy = scmi_domain_destroy,
> +    .relinquish_resources = scmi_relinquish_resources,
> +    .handle_call = scmi_handle_call,
> +    .dom0_dt_handle_node = scmi_dt_handle_node,
> +    .domain_sanitise_config = scmi_domain_sanitise_config,
> +    .assign_dt_device = scmi_dt_assign_device,
> +};
> +
> +static int __init scmi_check_smccc_ver(void)
> +{
> +    if ( smccc_ver < ARM_SMCCC_VERSION_1_1 )
> +    {
> +        printk(XENLOG_WARNING
> +               "scmi: No SMCCC 1.1 support, SCMI calls forwarding disabled\n");
> +        return -ENOSYS;
> +    }
> +
> +    return 0;
> +}
> +
> +static int __init scmi_dt_hyp_channel_read(struct dt_device_node *scmi_node,
> +                                           struct scmi_data *scmi_data,
> +                                           u64 *addr)
> +{
> +    int ret;
> +    u64 size;
> +
> +    if ( !dt_property_read_u32(scmi_node, "arm,smc-id", &scmi_data->func_id) )
> +    {
> +        printk(XENLOG_ERR "scmi: unable to read smc-id from DT\n");
> +        return -ENOENT;
> +    }
> +
> +    ret = scmi_dt_read_hyp_channel_addr(scmi_node, addr, &size);
> +    if ( IS_ERR_VALUE(ret) )
> +        return -ENOENT;
> +
> +    if ( !IS_ALIGNED(size, SCMI_SHMEM_MAPPED_SIZE) )
> +    {
> +        printk(XENLOG_ERR "scmi: shmem memory is not aligned\n");
> +        return -EINVAL;
> +    }
> +
> +    return 0;
> +}
> +
> +static __init int scmi_probe(struct dt_device_node *scmi_node, const void *data)
> +{
> +    u64 addr;
> +    int ret;
> +    struct scmi_channel *channel;
> +    unsigned int n_agents;
> +    scmi_msg_header_t hdr;
> +    struct scmi_msg_base_attributes_p2a rx;
> +
> +    ASSERT(scmi_node != NULL);
> +
> +    /*
> +     * Only bind to the SCMI node provided by Xen under the xen,sci container
> +     * (e.g. /chosen/xen/xen_scmi_config/scmi). This avoids binding to firmware
> +     * SCMI nodes that belong to the host OSPM and keeps the mediator scoped to
> +     * Xen-provided configuration only.
> +     */
> +    if ( !scmi_is_under_xen_sci(scmi_node) )
> +        return -ENODEV;
> +
> +    if ( !acpi_disabled )
> +    {
> +        printk(XENLOG_WARNING "scmi: is not supported when using ACPI\n");
> +        return -EINVAL;
> +    }
> +
> +    ret = scmi_check_smccc_ver();
> +    if ( ret )
> +        return ret;
> +
> +    ret = scmi_dt_hyp_channel_read(scmi_node, &scmi_data, &addr);
> +    if ( ret )
> +        return ret;
> +
> +    INIT_LIST_HEAD(&scmi_data.channel_list);
> +    spin_lock_init(&scmi_data.channel_list_lock);
> +
> +    scmi_data.dt_dev = scmi_node;
> +
> +    channel = smc_create_channel(SCMI_BASE_AGENT_ID_OWN, scmi_data.func_id, addr);
> +    if ( IS_ERR(channel) )
> +    {
> +        ret = PTR_ERR(channel);
> +        goto out;
> +    }
> +
> +    /* Mark as Xen management channel before collecting agent ID */
> +    channel->domain_id = DOMID_XEN;
> +
> +    /* Request agent id for Xen management channel */
> +    ret = collect_agent_id(channel);
> +    if ( ret )
> +        goto error;
> +
> +    /* Save the agent id for Xen management channel */
> +    scmi_data.hyp_channel_agent_id = channel->agent_id;
> +
> +    hdr.id = SCMI_BASE_PROTOCOL_ATTIBUTES;
> +    hdr.type = 0;
> +    hdr.protocol = SCMI_BASE_PROTOCOL;
> +
> +    ret = do_smc_xfer(channel, &hdr, NULL, 0, &rx, sizeof(rx));
> +    if ( ret )
> +        goto error;
> +
> +    n_agents = SCMI_FIELD_GET(SCMI_BASE_ATTR_NUM_AGENT, rx.attributes);
> +    printk(XENLOG_DEBUG "scmi: Got agent count %d\n", n_agents);
> +    ret = collect_agents(scmi_node);
> +    if ( ret )
> +        goto error;
> +
> +    ret = sci_register(&scmi_ops);
> +    if ( ret )
> +    {
> +        printk(XENLOG_ERR "SCMI: mediator already registered (ret = %d)\n",
> +               ret);
> +        goto error;
> +    }
> +
> +    scmi_data.initialized = true;
> +    goto out;
> +
> +error:
> +    unmap_channel_memory(channel);
> +    free_channel_list();
> +out:
> +    return ret;
> +}
> +
> +static const struct dt_device_match scmi_smc_match[] __initconst = {
> +    DT_MATCH_COMPATIBLE("arm,scmi-smc"),
> +    { /* sentinel */ },
> +};
> +
> +DT_DEVICE_START(scmi_smc_ma, "SCMI SMC MEDIATOR", DEVICE_FIRMWARE)
> +        .dt_match = scmi_smc_match,
> +        .init = scmi_probe,
> +DT_DEVICE_END
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * tab-width: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/xen/include/public/arch-arm.h b/xen/include/public/arch-arm.h
> index 1fc676fd96..a9a10a3b8d 100644
> --- a/xen/include/public/arch-arm.h
> +++ b/xen/include/public/arch-arm.h
> @@ -329,6 +329,7 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t);
>
>  #define XEN_DOMCTL_CONFIG_ARM_SCI_NONE      0
>  #define XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC  1
> +#define XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC_MA  2
>
>  #define XEN_DOMCTL_CONFIG_ARM_V8R_EL1_MSA_NONE    0
>  #define XEN_DOMCTL_CONFIG_ARM_V8R_EL1_MSA_PMSA    1
> @@ -361,7 +362,9 @@ struct xen_arch_domainconfig {
>      uint8_t arm_sci_type;
>      /* IN */
>      uint8_t v8r_el1_msa;
> -    uint16_t pad;
> +    /* IN */
> +    uint8_t arm_sci_agent_id;
> +    uint8_t pad;
>  };
>  #endif /* __XEN__ || __XEN_TOOLS__ */

--
WBR, Volodymyr


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

* Re: [PATCH v13 4/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver
  2026-09-23  1:02   ` Volodymyr Babchuk
@ 2026-09-23  8:25     ` Oleksii Moisieiev
  2026-09-24  1:15       ` Volodymyr Babchuk
  0 siblings, 1 reply; 14+ messages in thread
From: Oleksii Moisieiev @ 2026-09-23  8:25 UTC (permalink / raw)
  To: Volodymyr Babchuk
  Cc: xen-devel@lists.xenproject.org, Andrew Cooper, Anthony PERARD,
	Bertrand Marquis, Jan Beulich, Juergen Gross, Julien Grall,
	Michal Orzel, Roger Pau Monné, Stefano Stabellini,
	Grygorii Strashko


On 23/09/2026 04:02, Volodymyr Babchuk wrote:
> Hi Oleksii,
>
> Oleksii Moisieiev <Oleksii_Moisieiev@epam.com> writes:
>
> This is a great piece of documentation. It is larger than the text you
> added to the 'doc' directory. So I believe it is better to put into a
> design document, so it is will not be lost in the git commit messages.

Hi Volodymyr,

Thank you for a quick response.

I've just recheked document in patch 6:
docs/hypervisor-guide/arm/firmware/arm-scmi.rst and

compared it with the information provided in the commit description.

As I can see all information provided in the description (add schemes
and dts examples at least)

are present in the documentation. Also doc itself is more detailed then
the commit description.

Maybe I missed something?

>> This patch introduces SCI driver to support for ARM EL3 Trusted Firmware-A
>> (TF-A) which provides SCMI interface with multi-agent support, as shown
>> below.
>>
>>    +-----------------------------------------+
>>    |                                         |
>>    | EL3 TF-A SCMI                           |
>>    +-------+--+-------+--+-------+--+-------++
>>    |shmem1 |  |shmem0 |  |shmem2 |  |shmemX |
>>    +-----+-+  +---+---+  +--+----+  +---+---+
>> smc-id1 |        |         |           |
>> agent1  |        |         |           |
>>    +-----v--------+---------+-----------+----+
>>    |              |         |           |    |
>>    |              |         |           |    |
>>    +--------------+---------+-----------+----+
>>           smc-id0 |  smc-id2|    smc-idX|
>>           agent0  |  agent2 |    agentX |
>>                   |         |           |
>>              +----v---+  +--v-----+  +--v-----+
>>              |        |  |        |  |        |
>>              | Dom0   |  | Dom1   |  | DomX   |
>>              |        |  |        |  |        |
>>              |        |  |        |  |        |
>>              +--------+  +--------+  +--------+
>>
>> The EL3 SCMI multi-agent firmware is expected to provide SCMI SMC shared
>> memory transport for every Agent in the system.
>>
>> The SCMI Agent transport channel defined by pair:
>>   - smc-id: SMC id used for Doorbell
>>   - shmem: shared memory for messages transfer, Xen page
>>   aligned. Shared memory is mapped with the following flags:
>>   MT_DEVICE_nGnRE.
>>
>> The follwoing SCMI Agents are expected to be defined by SCMI FW to enable SCMI
>> multi-agent functionality under Xen:
>> - Xen management agent: trusted agents that accesses to the Base Protocol
>> commands to configure agent specific permissions
>> - OSPM VM agents: non-trusted agent, one for each Guest domain which is
>>    allowed direct HW access. At least one OSPM VM agent has to be provided
>>    by FW if HW is handled only by Dom0 or Driver Domain.
>>
>> The EL3 SCMI FW is expected to implement following Base protocol messages:
>> - BASE_DISCOVER_AGENT (optional if agent_id was provided)
>> - BASE_RESET_AGENT_CONFIGURATION (optional)
>> - BASE_SET_DEVICE_PERMISSIONS (optional)
>>
>> The SCI SCMI SMC multi-agent driver implements following
>> functionality:
>> - The driver is initialized from the Xen SCMI container ``xen_scmi_config``
>>    (compatible ``xen,sci``) placed under ``/chosen/xen``. Only the
>>    ``arm,scmi-smc`` node that is a child of this container will bind to Xen;
>>    other SCMI nodes (for example under ``/firmware``) are ignored to avoid
>>    stealing the host OSPM instance.
>>
>> scmi_shm_1: sram@47ff1000 {
>>            compatible = "arm,scmi-shmem";
>>            reg = <0x0 0x47ff1000 0x0 0x1000>;
>> };
>> scmi_xen: scmi {
>>          compatible = "arm,scmi-smc";
>>          arm,smc-id = <0x82000003>; <--- Xen management agent smc-id
>>          #address-cells = < 1>;
>>          #size-cells = < 0>;
>>          #access-controller-cells = < 1>;
>>          shmem = <&scmi_shm_1>; <--- Xen management agent shmem
>> };
>>
>> - The driver obtains Xen specific SCMI Agent's configuration from the
>>    Host DT, probes Agents and builds SCMI Agents list. The Agents
>>    configuration is taken from "scmi-secondary-agents" property where
>>    first item is "arm,smc-id", second - "arm,scmi-shmem" phandle and
>>    third is optional "agent_id":
>>
>> / {
>>    chosen {
>>      xen {
>>        ranges;
>>        xen_scmi_config {
>>          compatible = "xen,sci";
>>          #address-cells = <2>;
>>          #size-cells = <2>;
>>          ranges;
>>
>>      scmi-secondary-agents = <
>>            0x82000002 &scmi_shm_0 0
>>            0x82000004 &scmi_shm_2 2
>>            0x82000005 &scmi_shm_3 3>; <--- func_id, shmem, agent_id
>>          #scmi-secondary-agents-cells = <3>;
> I don't think that this is the correct way of using -cells property.
>
> These properties are used either to provide information for **child** nodes
> (like #address-cells or #size-cells) or to provide a number of cells to
> encode a specifier for a domain (like #interrupt-cells).
>
> Here you are doing neither of these. I think the proper way is to define
> secondary agents as children:
>
> agents {
>          #address-cells = 1;
>          #size-cells = 0;
>           scmi_agent_2: scmi_agent@2 {
>             compatible = "arm,scmi-agent";  // Actually, I am not sure if
>                                             // we allowed to use 'arm' namespace
>
>             reg = <2>;                      // Agent id goes here
>             shmmem = &scmi_shm_2;
>             arm,smc-id = 0x82000004;
>           };
> }
>
> With this approach you don't need #scmi-secondary-agents-cells at all
> and the whole structure becomes more device-tree-ish.
Thank you for looking at this. I agree that the two cases you list are
the most common ones, but they are not the only sanctioned ones: there is a
third, long-standing pattern where a "#<name>-cells" property in a node
declares the cell stride of a *non-phandle list property in that very same
node*. That is exactly what "#scmi-secondary-agents-cells" does for
"scmi-secondary-agents". Some evidence from the Devicetree Specification and
from Linux:

1) "ranges" and "interrupt-map" are parsed with the cell counts of the node
    that carries them, not of a child or of a phandle target.

    dtc enforces this itself:

    - scripts/dtc/checks.c:785 check_ranges_format() computes the entry
length
      as (parent #address-cells + this node's #address-cells + this node's
      #size-cells) and validates the length of "ranges" in this node
against it.

    - scripts/dtc/checks.c:1601 check_interrupt_map():

          cellsize = node_addr_cells(node);
          cellsize += propval_cell(get_property(node, "#interrupt-cells"));

      i.e. the stride of the "interrupt-map" entries in a node is taken from
      "#address-cells" and "#interrupt-cells" *of that same node*. Linux
does
      the same in drivers/of/irq.c.

    So "a #*-cells property in node X describing the layout of a property in
    node X" is not an abuse of the convention, it is how two of the core
    Devicetree Specification properties are defined.

2) There is a precedent whose shape is identical to ours - a vendor property
    holding a list, plus cells properties in the same node that describe
how to
    decode it:

    Documentation/devicetree/bindings/tpm/ibm,vtpm.yaml:36

        ibm,#dma-address-cells:
          description:
            number of cells that are used to encode the physical address
field
            of dma-window properties
        ibm,#dma-size-cells:
          description:
            number of cells that are used to encode the size field of
            dma-window properties
        ibm,my-dma-window:
          description: DMA window associated with this virtual I/O Adapter

    and the example (same file, lines 96-98):

        ibm,#dma-address-cells = <0x2>;
        ibm,#dma-size-cells = <0x2>;
        ibm,my-dma-window = <0x10000003 0x0 0x0 0x0 0x10000000>;

    Both cells properties are "required". The parser is
    arch/powerpc/kernel/prom_parse.c:11 of_parse_dma_window(), which reads
    "ibm,#dma-address-cells" and "ibm,#dma-size-cells" from the same
node "dn"
    to walk "ibm,my-dma-window". There is no phandle and no child node
    involved; this is exactly the "self-describing list property" pattern.

3) A "#*-cells" property does not have to describe a phandle specifier
at all:

    - "#pinctrl-cells" (Documentation/devicetree/bindings/pinctrl/
      pinctrl-single.yaml:55) gives the number of cells per entry of the
plain
      "pinctrl-single,pins" property; drivers/pinctrl/devicetree.c:297
      pinctrl_find_cells_size() looks it up in the parent/grandparent node.
      No phandle specifier is involved.

    - "#index-cells"
(Documentation/devicetree/bindings/usb/fsl,usbmisc.yaml:56)
      is not a specifier for any domain either.

So the convention in practice is "a #<foo>-cells property states how
many cells
one <foo> entry occupies", and the entries may live in a child node's
property
(#address-cells), in a consumer's phandle specifier (#interrupt-cells,
#clock-cells), or in a property of the declaring node itself (ranges,
interrupt-map, ibm,my-dma-window). Our usage falls into the third group.

This bring us to the following conclusion that
#scmi-secondary-agents-cells usage
doesn't technically break the device-tree convention with 2 exceptions:

a) Vendor prefix. Documentation/devicetree/bindings/writing-bindings.rst:68
    says "DO use a vendor prefix on device-specific property names", and all
    the same-node precedents above are vendor-prefixed
("ibm,#dma-address-cells").
    Both of our properties are Xen-specific and live under /chosen/xen,
so if we
    keep the flat form they should become "xen,scmi-secondary-agents" and
    "xen,#scmi-secondary-agents-cells".

b) Entry layout. In our list the phandle is the second cell
    (<smc-id &shmem agent-id>), which prevents the use of the standard
    phandle-array helpers - they all expect the phandle first. If we
keep the
    flat form, reordering to <&shmem smc-id agent-id> would let the
property be
    parsed as an ordinary phandle-array.

As for your proposal: the child-node form you suggest is certainly
more idiomatic, it makes the agent id an addressable "reg" and it
removes the
optional 2-vs-3-cells variant altogether. My only reservations are that
it grows
the Xen DT parser (node iteration instead of a single property walk) and
that the
compatible string would need a namespace we own ("xen,scmi-agent" rather
than
"arm,scmi-agent", as you correctly suspected).

So I do not think the current form is invalid, but I have no objection to
moving to child nodes if you prefer it. Please let me know which way you
would
like me to go for v14 and I will respin accordingly.
>>          xen,dom0-sci-agent-id = <0>;
>>
>>          scmi_shm_0: sram@47ff0000 {
>>            compatible = "arm,scmi-shmem";
>>            reg = <0x0 0x47ff0000 0x0 0x1000>;
>>          };
>>
>>          /* Xen SCMI management channel */
>>          scmi_shm_1: sram@47ff1000 {
>>            compatible = "arm,scmi-shmem";
>>            reg = <0x0 0x47ff1000 0x0 0x1000>;
>>          };
>>
>>          scmi_shm_2: sram@47ff2000 {
>>            compatible = "arm,scmi-shmem";
>>            reg = <0x0 0x47ff2000 0x0 0x1000>;
>>          };
>>
>>          scmi_shm_3: sram@47ff3000 {
>>            compatible = "arm,scmi-shmem";
>>            reg = <0x0 0x47ff3000 0x0 0x1000>;
>>          };
>>
>>          scmi_xen: scmi {
>>            compatible = "arm,scmi-smc";
>>            arm,smc-id = <0x82000003>; <--- Xen management agent func_id
>>            #address-cells = <1>;
>>            #size-cells = <0>;
>>            #access-controller-cells = <1>;
>>            shmem = <&scmi_shm_1>; <--- Xen management agent shmem
>>          };
>>        };
>>      };
>>    };
>> };
>>
>> / {
>>      /*
>>       * Host SCMI OSPM channel - provided to the Dom0 as is if SCMI
>>       * enabled for it, ignored by Xen multi-agent mediator
>>       */
>>      scmi_shm: sram@47ff0000 {
>>              compatible = "arm,scmi-shmem";
>>              reg = <0x0 0x47ff0000 0x0 0x1000>;
>>      };
>>
>>      firmware {
>>        scmi: scmi {
>>          compatible = "arm,scmi-smc";
>>          arm,smc-id = <0x82000002>; <--- Host OSPM agent smc-id
>>          #address-cells = < 1>;
>>          #size-cells = < 0>;
>>          shmem = <&scmi_shm>; <--- Host OSPM agent shmem
>>
>>          protocol@X{
>>          };
>>        };
>>     };
>> };
>>
>> This approach allows defining multiple SCMI Agents by adding
>> Xen-specific properties under the ``/chosen`` node to the Host Device
>> Tree, leaving the main part unchanged. The Host DT SCMI channel will
>> be passed to Dom0.
>>
>> The Xen management agent is described as a ``scmi_xen`` node under the
>> ``xen,sci`` comaptible node, which is used by Xen to control other
>> SCMI Agents in the system.
>>
>> All secondary agents' configurations are provided in the
>> ``scmi-secondary-agents`` property with an optional ``agent_id`` field.
>>
>> The ``agent_id`` from the ``scmi-secondary-agents`` property is used
>> to identify the agent in the system and can be omitted by setting
>> ``#scmi-secondary-agents-cells = <2>``, so the Secondary Agents
>> configuration will look like this:
>>
>> / {
>>    chosen {
>>      xen {
>>        xen_scmi_config {
>>          compatible = "xen,sci";
>>          #address-cells = <2>;
>>          #size-cells = <2>;
>>          ranges;
>>
>>          /* Shared memory nodes as defined earlier */
>>
>>          scmi-secondary-agents = <
>>            0x82000003 &scmi_shm_0
>>            0x82000004 &scmi_shm_2
>>            0x82000005 &scmi_shm_3
>>            0x82000006 &scmi_shm_4>;
>>          #scmi-secondary-agents-cells = <2>;
>>        };
>>      };
>>    };
>> }
>>
>> In this case, Xen will use the ``SCMI_BASE_DISCOVER_AGENT`` call to
>> discover the ``agent_id`` for each secondary agent. Providing the
>> ``agent_id`` in the ``scmi-secondary-agents`` property allows skipping
>> the discovery call, which is useful when the secondary agent's shared
>> memory is not accessible by Xen or when boot time is important because
>> it allows skipping the agent discovery procedure.
>>
>>    Note that Xen is the only one entry in the system which need to know
>>    about SCMI multi-agent support.
>>
>> SMC ID Configuration and SCMI Connection Compatibility:
>>
>> The configuration allows the same device tree to work for both baremetal
>> Linux and Linux Dom0. This is achieved because:
>>
>> - Baremetal Linux uses: func_id 0x82000002, scmi-shmem 0x47ff0000
>> - Dom0 Linux uses: func_id 0x82000002, scmi-shmem 0x47ff0000
>> - Xen management uses: func_id 0x82000003, scmi-shmem 0x47ff1000
>>
>> This works because the privileged SCMI connection in EL3 firmware is not
>> tied exclusively to func_id 0x82000002. The EL3 firmware supports multiple
>> SCMI agents with different SMC IDs and shared memory regions. Each agent
>> (Dom0 via 0x82000002, Xen via 0x82000003, other domains via additional
>> func_ids) has an independent communication channel to the firmware.
>>
>> The key distinction is that Xen's management channel (0x82000003) is used
>> for privileged operations like agent configuration and device permissions
>> (BASE_SET_DEVICE_PERMISSIONS, BASE_RESET_AGENT_CONFIGURATION), while Dom0's
>> channel (0x82000002) is used for standard SCMI protocol operations (power,
>> clock, sensor management, etc.). The firmware enforces different permission
>> levels for each agent based on their agent_id, not the SMC ID.
>>
>> Therefore, there is no conflict: Linux Dom0 retains its standard SCMI
>> connection for hardware management, while Xen uses its separate privileged
>> channel for mediating access between multiple domains.
>>
>> - It implements the SCI subsystem interface required for configuring and
>> enabling SCMI functionality for Dom0/hwdom and Guest domains. To enable
>> SCMI functionality for domain it has to be configured with unique supported
>> SCMI Agent_id and use corresponding SCMI SMC shared memory transport
>> [smc-id, shmem] defined for this SCMI Agent_id.
>> - Once Xen domain is configured it can communicate with EL3 SCMI FW:
>>    -- zero-copy, the guest domain puts SCMI message in shmem;
>>    -- the guest triggers SMC exception with smc-id (doorbell);
>>    -- the Xen driver catches exception, do checks and synchronously forwards
>>    it to EL3 FW.
>> - the Xen driver sends BASE_RESET_AGENT_CONFIGURATION message to Xen
>>    management agent channel on domain destroy event. This allows to reset
>>    resources used by domain and so implement use-case like domain reboot.
>>
>> Dom0 Enable SCMI SMC:
>>   - set xen,dom0-sci-agent-id=<agent_id> under the xen,sci container in
>>     the Host DT. If the property is absent, SCMI is disabled for Dom0
>>     and all SCMI nodes are removed from the Dom0 DT. The driver updates
>>     the Dom0 DT SCMI node "arm,smc-id" value and fixes up the shmem
>>     node according to the assigned agent_id.
>>
>>   - pass dom0=sci-agent-id=<agent_id> in Xen command line. if not provided
>>     SCMI will be disabled for Dom0 and all SCMI nodes removed from Dom0 DT.
>>     The driver updates Dom0 DT SCMI node "arm,smc-id" value and fix up shmem
>>     node according to assigned agent_id.
>>
>> Guest domains enable SCMI SMC:
>>   - xl.cfg: add configuration option as below
>>
>>     arm_sci = "type=scmi_smc_multiagent,agent_id=2"
>>
>>   - xl.cfg: enable access to the "arm,scmi-shmem" which should
>>   correspond assigned agent_id for the domain, for example:
>>
>> iomem = [
>>      "47ff2,1@22001",
>> ]
>>
>>   - DT: add SCMI nodes to the Driver domain partial device tree as in the
>>   below example. The "arm,smc-id" should correspond assigned agent_id
>>   for the domain:
>>
>> passthrough {
>>     scmi_shm_0: sram@22001000 {
>>         compatible = "arm,scmi-shmem";
>>         reg = <0x0 0x22001000 0x0 0x1000>;
>>     };
>>
>>     firmware {
>>          compatible = "simple-bus";
>>              scmi: scmi {
>>                  compatible = "arm,scmi-smc";
>>                  arm,smc-id = <0x82000004>;
>>                  shmem = <&scmi_shm_0>;
>>                  ...
>>              }
>>      }
>> }
>>
>> SCMI "4.2.1.1 Device specific access control"
>>
>> The XEN SCI SCMI SMC multi-agent driver performs "access-controller"
>> provider function in case EL3 SCMI FW implements SCMI "4.2.1.1 Device
>> specific access control" and provides the BASE_SET_DEVICE_PERMISSIONS
>> command to configure the devices that an agents have access to.
>> The DT SCMI node should "#access-controller-cells=<1>" property and DT
>> devices should be bound to the Xen SCMI.
>>
>> &i2c1 {
>>          access-controllers = <&scmi 0>;
>> };
>>
>> The Dom0 and dom0less domains DT devices will be processed
>> automatically through sci_assign_dt_device() call, but to assign SCMI
>> devices from toolstack the xl.cfg:"dtdev" property
>> shall be used:
>>
>> dtdev = [
>>      "/soc/i2c@e6508000",
>> ]
>>
>> xl.cfg:dtdev will contain all nodes which are under SCMI
>> management (not only those which are behind IOMMU).
>>
>> Additionally, this patch adds documentation for the pre-existing
>> scmi-smc-passthrough command line option, which was previously
>> undocumented.
>>
>> [0] https://developer.arm.com/documentation/den0056
>> [1] https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
>> [2] https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/access-controllers/access-controllers.yaml
>>
>> Signed-off-by: Grygorii Strashko <grygorii_strashko@epam.com>
>> Signed-off-by: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
>> ---
>>
>> Changes in v13:
>> - fix typo in commit description
>>
>> Changes in v12:
>> - rebase to the latest staging
>> - return EINVAL if xen,sci_type=scmi_smc_multiagent was set but
>> CONFIG_SCMI_SMC_MA not set
>> - put arm_sci_agent_id after v8r_el1_msa and before pad to preserve
>> order. Reduced pad from uint16_t to uint8_t to fill the gap (was 2
>> bytes before adding arm_sci_agent_id and left only 1, so used uint8_t
>> to preserve structure size)
>> - fix scmi_handle_call to use arm_smccc_guest_smc helper function
>> - update send_smc_message function to use new format of
>> arm_smccc_1_1_smc
>> - guard the Dom0 SCMI agent id setup in create_dom0() and MA related
>> funcitons with CONFIG_SCMI_SMC_MA
>> - fix the xen_scmi_config example in booting.txt, which placed properties
>> after subnodes and was rejected by dtc with "Properties must precede
>> subnodes"
>>
>> Changes in v10:
>> - Fix tabs in MAINTAINERS file
>> - remove duplicate SPDX tag from scmi-shmem.c
>> - add cast to ARM_SMCCC_INVALID_PARAMETER to settle the sign since
>> ARM_SMCCC_INVALID_PARAMETER is -3 which is part of the spec and resp
>> is the default smccc call structure.
>> - update free_channel_list. Add spinlock to avoid race condition and
>> a comment with a description of the function work
>> - preserve error of smc_create_channel in scmi_probe
>> - check scmi shmem address alignment as wel as it is done for size
>> - check for d->arch.sci_data != NULL in scmi_handle_call
>> - use SCMI_SHMEM_MAPPED size for iomem_permit_access
>> - change len type to unsigned in shmem_{get|put}_message
>> - rename shmem_channel_is_free to shmem_channel_status
>> - add comment about skipping message status when getting message response
>> - Set correct agent_id ranges for dom0less and a toolstack. Agent_id 0
>> is not binded for dom0 so can be reused. Also mentioned that
>> UINT8_MAX (255) is treated as invalid agent_id.
>> - Split hypervisor and toolstack changes into separate commits
>> - move init list and spin init after initial checks in probe call
>> - fix typo in comments
>> - clean resources when sci_register returns an error.
>>
>> Changes in v9:
>> - sort and refactor MAINTAINERS enties
>> - remove Spurious changes
>> - add extra check to avoid ASSERT when calling unmap_channel_memory
>> from assign device method
>> - set correct tx flag to SCMI_BASE_AGENT_PERMISSIONS_RESET when
>> freeing resources. Flag should be set to 1 according to the
>> section 4.2.2.12 [0].
>> - fix dt node copmaring
>> - moved channel->shmem check from ASSERT in unmap_memory_channel to
>> "if" statement. This will prevent firing ASSERT if
>> unmap_channel_memory was called twice on the same channel.
>>
>> Changes in v8:
>> - update xen_scmi func_id in commit description
>> - updated documentation with the new DT format
>> - updated opt_dom0_scmi_agent_id setting to avoid it to be equal
>> SCMI_AGENT_ID_INVALID.
>> - changed SCMI_AGENT_ID_INVALID from 0xff to UINT8_MAX which makes
>> code more clear showing that UINT8_MAX is theated like invalid
>> agent_id and couldn't be used. Also excluded SCMI_AGENT_ID_INVALID
>> from acceptable value range
>> - remove outdated xen,config property ignore, added xen,sci compatible
>> to skip_matches in handle_node
>> - add documentation for pre-existing scmi-smc-passthrough command line
>> option in alphabetically correct location (in 's' section)
>> - add note to commit description about documentation for previously
>> undocumented scmi-smc-passthrough
>> - Fix SMC IDs in DT examples (Xen management uses 0x82000003, Dom0 uses 0x82000002)
>> - Add explicit note explaining why Dom0 and Xen channels do not conflict
>> - Document dom0less multi-agent configuration example (xen,sci_type / xen,sci-agent-id)
>> - Add scmi_xen node to agent-discovery example with #scmi-secondary-agents-cells = 2
>> - Drop dom0=sci-agent-id command line handling; Dom0 SCMI is now enabled via
>>    xen,dom0-sci-agent-id in the xen,sci DT container
>> - Refresh docs and examples to mention the DT property instead of the cmdline option
>>
>> Changes in v7:
>> - rework scmi nodes for xen to match on compatible string instead of
>> the direct path
>>
>> Changes in v6:
>> - updated scmi-shmem to use io.h from generic location
>> - update scmi_agent_id parameter to be provided inside dom0= parameter
>> list and have the following format "dom0=sci-agent-id=0"
>> This change was done as a response for Stefano comment and
>> requires a lot of code changes, but produces much cleaner solution
>> that's why I've added it to the code.
>> - fix file comments and return codes
>> - fix lenght checks in shmem_{get,put}_message to use offsetof
>> - remove len member from scmi_channel structure as it is not used
>> - set scmi-secondary-agents property to be mandatory since if no
>> secondary agents were provided then there is no sence to enable scmi
>> when no secondary agents are populated to the Domains
>> - update documentation in booting.txt, added xen_scmi node to the
>> example
>> - adjust d->arch.sci_enabled value in scmi_domain_destroy
>> - fix lock management in smc_create_channel call
>> - avoid extra map_channel_memory command for Xen management channel
>> because collect_agent_id call unmaps memory if DOMID_XEN is not
>> set. So for Xen management channel we can init domain_id ad DOMID_XEN
>> before calling collect_agent_id so memory shouldn't be unmapped.
>>
>> Changes in v5:
>> - fix device-tree example format in booting.txt, added ";" after "}".
>> - update define in scmi-proto.h
>> - update define in scmi-shmem.h file
>> - scmi_assign_device - do not ignore -EOPNOTSUPP return
>> code of the do_smc_xfer
>> - remove overwriting agent_channel->agent_id after
>> SCMI_BASE_DISCOVER_AGENT call
>> - add multi-agent files to the MAINTAINERS
>> - add SCMI multi-agent description to the SUPPORT.md
>> - handle ARM_SMCCC_INVALID_PARAMETER return code and return -EINVAL
>> for smc call
>> - updated collect_agents function. Set agent_id parameter as optional
>> in scmi-secondary-agents device-tree property
>> - introduce "#scmi-secondary-agents-cells" parameter to set if
>> agent_id was provided
>> - reanme xen,scmi-secondary-agents property to scmi-secondary-agents
>> - move memcpu_toio/fromio for the generic place
>> - update Xen to get management channel from /chosen/xen,config node
>> - get hypervisor channnel from node instead of using hardcoded
>> - update handling scmi and shmem nodes for the domain
>> - Set multi-agent driver to support only Arm64
>>
>> Changes in v4:
>> - toolstack comments from Anthony PERARD
>> - added dom0less support
>> - added doc for "xen,scmi-secondary-agents"
>>
>>   MAINTAINERS                                 |   1 +
>>   SUPPORT.md                                  |  11 +
>>   docs/misc/arm/device-tree/booting.txt       | 197 +++++
>>   xen/arch/arm/dom0less-build.c               |  17 +
>>   xen/arch/arm/domain_build.c                 |  43 ++
>>   xen/arch/arm/firmware/Kconfig               |  12 +
>>   xen/arch/arm/firmware/Makefile              |   1 +
>>   xen/arch/arm/firmware/scmi-proto.h          | 164 ++++
>>   xen/arch/arm/firmware/scmi-shmem.c          | 118 +++
>>   xen/arch/arm/firmware/scmi-shmem.h          |  45 ++
>>   xen/arch/arm/firmware/scmi-smc-multiagent.c | 816 ++++++++++++++++++++
>>   xen/include/public/arch-arm.h               |   5 +-
>>   12 files changed, 1429 insertions(+), 1 deletion(-)
>>   create mode 100644 xen/arch/arm/firmware/scmi-proto.h
>>   create mode 100644 xen/arch/arm/firmware/scmi-shmem.c
>>   create mode 100644 xen/arch/arm/firmware/scmi-shmem.h
>>   create mode 100644 xen/arch/arm/firmware/scmi-smc-multiagent.c
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index c60afc93a5..cd6d1243bd 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -534,6 +534,7 @@ SCI MEDIATORS
>>   R: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
>>   S: Supported
>>   F: xen/arch/arm/firmware/sci.c
>> +F:  xen/arch/arm/firmware/scmi-*.[ch]
>>   F: xen/arch/arm/include/asm/firmware/sci.h
>>
>>   SEABIOS UPSTREAM
>> diff --git a/SUPPORT.md b/SUPPORT.md
>> index eb07332462..dc41a6b2f3 100644
>> --- a/SUPPORT.md
>> +++ b/SUPPORT.md
>> @@ -972,6 +972,17 @@ by hwdom. Some platforms use SCMI for access to system-level resources.
>>
>>       Status: Supported
>>
>> +### Arm: SCMI SMC multi-agent support
>> +
>> +Enable support for the multi-agent configuration of the EL3 Firmware, which
>> +allows Xen to provide an SCMI interface to the Domains.
>> +Xen manages access permissions to the HW resources and provides an SCMI interface
>> +to the Domains. Each Domain is represented as a separate Agent, which can
>> +communicate with EL3 Firmware using a dedicated shared memory region, and
>> +notifications are passed through by Xen.
>> +
>> +    Status, ARM64: Tech Preview
>> +
>>   ### ARM: Guest PSCI support
>>
>>   Emulated PSCI interface exposed to guests. We support all mandatory
>> diff --git a/docs/misc/arm/device-tree/booting.txt b/docs/misc/arm/device-tree/booting.txt
>> index bcb06bc796..1d0382a865 100644
>> --- a/docs/misc/arm/device-tree/booting.txt
>> +++ b/docs/misc/arm/device-tree/booting.txt
>> @@ -331,6 +331,21 @@ with the following properties:
>>       Should be used together with scmi-smc-passthrough Xen command line
>>       option.
>>
>> +    - "scmi_smc_multiagent"
>> +
>> +    Enables ARM SCMI SMC multi-agent support for the guest by enabling SCMI over
>> +    SMC calls forwarding from domain to the EL3 firmware (like ARM
>> +    Trusted Firmware-A) with a multi SCMI OSPM agent support.
>> +    The SCMI agent_id should be specified for the guest with "xen,sci-agent-id"
>> +    property.
>> +
>> +- "xen,sci-agent-id"
>> +
>> +    Specifies ARM SCMI agent id for the guest. This option is mandatory if the
>> +    SCMI SMC "scmi_smc_multiagent" support is enabled for the guest. The agent ids
>> +    of guest must be unique and in the range [0..254]. UINT8_MAX (255) is
>> +    treated as invalid.
>> +
>>   - v8r_el1_msa
>>
>>       A string property specifying whether, on Armv8-R systems at EL1, a domain
>> @@ -847,3 +862,185 @@ The automatically allocated static shared memory will get mapped at
>>   0x80000000 in DomU1 guest physical address space, and at 0x90000000 in DomU2
>>   guest physical address space. DomU1 is explicitly defined as the owner domain,
>>   and DomU2 is the borrower domain.
>> +
>> +SCMI SMC multi-agent support
>> +============================
>> +
>> +For enabling the ARM SCMI SMC multi-agent support (enabled by CONFIG_SCMI_SMC_MA)
>> +the Xen specific SCMI Agent's configuration shall be provided in the Host DT
>> +according to the SCMI compliant EL3 Firmware specification with ARM SMC/HVC
>> +transport. The SCMI configuration must live under the Xen SCMI container
>> +"xen,sci" beneath "/chosen" (for example "/chosen/xen/xen_scmi_config/scmi"). The
>> +Xen SCMI mediator will bind only to the "arm,scmi-smc" node that is a child of
>> +this "xen,sci" container; any other "arm,scmi-smc" nodes (for example under
>> +"/firmware") are ignored to avoid stealing the host's SCMI OSPM instance.
>> +
>> +- scmi-secondary-agents
>> +
>> +    Defines a set of SCMI agents configuration supported by SCMI EL3 FW and
>> +    available for Xen. Each Agent defined as triple consisting of:
>> +    SMC/HVC function_id assigned for the agent transport ("arm,smc-id"),
>> +    phandle to SCMI SHM assigned for the agent transport ("arm,scmi-shmem"),
>> +    SCMI agent_id (optional) if not set - Xen will determine Agent ID for
>> +    each provided channel using BASE_DISCOVER_AGENT message.
>> +
>> +- xen,dom0-sci-agent-id
>> +
>> +    Optional. Specifies the Dom0/hwdom SCMI agent_id inside the ``xen,sci``
>> +    container. When provided, Dom0 will be configured for SCMI multi-agent
>> +    support; when omitted, SCMI remains disabled for Dom0. The value must
>> +    match the ``func_id`` and shmem pairing that EL3 firmware exposes for
>> +    Dom0 (for example via ``/firmware/scmi``).
>> +
>> +As an example:
>> +
>> +/ {
>> +    chosen {
>> +        xen {
>> +            ranges;
>> +            xen_scmi_config {
>> +                compatible = "xen,sci";
>> +                #address-cells = <2>;
>> +                #size-cells = <2>;
>> +                ranges;
>> +
>> +                xen,dom0-sci-agent-id = <0>; <--- dom0 agent id
>> +                scmi-secondary-agents = <
>> +                    0x82000002 &scmi_shm_0 0
>> +                    0x82000004 &scmi_shm_2 2
>> +                    0x82000005 &scmi_shm_3 3>; <--- func_id, shmem, agent_id
>> +                #scmi-secondary-agents-cells = <3>;
>> +
>> +                scmi_shm_0: sram@47ff0000 {
>> +                    compatible = "arm,scmi-shmem";
>> +                    reg = <0x0 0x47ff0000 0x0 0x1000>;
>> +                };
>> +
>> +                /* Xen SCMI management channel */
>> +                scmi_shm_1: sram@47ff1000 {
>> +                    compatible = "arm,scmi-shmem";
>> +                    reg = <0x0 0x47ff1000 0x0 0x1000>;
>> +                };
>> +
>> +                scmi_shm_2: sram@47ff2000 {
>> +                    compatible = "arm,scmi-shmem";
>> +                    reg = <0x0 0x47ff2000 0x0 0x1000>;
>> +                };
>> +
>> +                scmi_shm_3: sram@47ff3000 {
>> +                    compatible = "arm,scmi-shmem";
>> +                    reg = <0x0 0x47ff3000 0x0 0x1000>;
>> +                };
>> +
>> +                scmi_xen: scmi {
>> +                    compatible = "arm,scmi-smc";
>> +                    arm,smc-id = <0x82000003>; <--- Xen management agent func_id
>> +                    #address-cells = <1>;
>> +                    #size-cells = <0>;
>> +                    #access-controller-cells = <1>;
>> +                    shmem = <&scmi_shm_1>; <--- Xen management agent shmem
>> +                };
>> +            };
>> +        };
>> +    };
>> +};
>> +
>> +Note: This example keeps the Host DT unchanged for Dom0 and baremetal Linux
>> +by using func_id 0x82000002 / shmem 0x47ff0000 for Dom0, while Xen uses a
>> +separate privileged channel func_id 0x82000003 / shmem 0x47ff1000. EL3
>> +firmware enforces permissions per agent_id, so there is no conflict between
>> +Dom0 and Xen channels.
>> +
>> +- #scmi-secondary-agents-cells
>> +
>> +    Defines whether Agent_id is set in the "scmi-secondary-agents" property.
>> +    Possible values are: 2, 3.
>> +    When set to 3 (the default), expect agent_id to be present in the secondary
>> +    agents list.
>> +    When set to 2, agent_id will be discovered for each channel using
>> +    BASE_DISCOVER_AGENT message.
>> +
>> +
>> +Example:
>> +
>> +/ {
>> +    chosen {
>> +        xen {
>> +            ranges;
>> +            xen_scmi_config {
>> +                compatible = "xen,sci";
>> +                #address-cells = <2>;
>> +                #size-cells = <2>;
>> +                ranges;
>> +
>> +                /* Shared memory nodes as in the previous example */
>> +
>> +                scmi-secondary-agents = <
>> +                    0x82000002 &scmi_shm_0
>> +                    0x82000004 &scmi_shm_2
>> +                    0x82000005 &scmi_shm_3
>> +                    0x82000006 &scmi_shm_4>;
>> +                #scmi-secondary-agents-cells = <2>;
>> +
>> +                scmi_xen: scmi {
>> +                    compatible = "arm,scmi-smc";
>> +                    arm,smc-id = <0x82000003>; <--- Xen management agent func_id
>> +                    #address-cells = <1>;
>> +                    #size-cells = <0>;
>> +                    #access-controller-cells = <1>;
>> +                    shmem = <&scmi_shm_1>; <--- Xen management agent shmem
>> +                };
>> +            };
>> +        };
>> +    };
>> +};
>> +
>> +Dom0less example (multi-agent)
>> +-------------------------------
>> +
>> +Below is a minimal dom0less configuration showing how to enable SCMI SMC
>> +multi-agent for a pre-defined guest domain using xen,sci_type and
>> +xen,sci-agent-id, together with the Xen SCMI container:
>> +
>> +chosen {
>> +    xen {
>> +        ranges;
>> +        xen_scmi_config {
>> +            compatible = "xen,sci";
>> +            #address-cells = <2>;
>> +            #size-cells = <2>;
>> +            ranges;
>> +
>> +            /* Xen management channel shared memory */
>> +            scmi_shm_1: sram@47ff1000 {
>> +                compatible = "arm,scmi-shmem";
>> +                reg = <0x0 0x47ff1000 0x0 0x1000>;
>> +            };
>> +
>> +            scmi_shm_domu: sram@47ff2000 {
>> +                compatible = "arm,scmi-shmem";
>> +                reg = <0x0 0x47ff2000 0x0 0x1000>;
>> +            };
>> +
>> +            scmi-secondary-agents = <
>> +                0x82000004 &scmi_shm_domu 2>;
>> +            #scmi-secondary-agents-cells = <3>;
>> +
>> +            scmi_xen: scmi {
>> +                compatible = "arm,scmi-smc";
>> +                arm,smc-id = <0x82000003>;
>> +                #address-cells = <1>;
>> +                #size-cells = <0>;
>> +                #access-controller-cells = <1>;
>> +                shmem = <&scmi_shm_1>;
>> +            };
>> +        };
>> +    };
>> +
>> +    xen,domain@1 {
>> +        compatible = "xen,domain";
>> +        xen,sci_type = "scmi_smc_multiagent";
>> +        xen,sci-agent-id = <2>;
>> +        /* Additional domain properties (memory, cpus, kernels, etc.) */
>> +    };
>> +};
>> diff --git a/xen/arch/arm/dom0less-build.c b/xen/arch/arm/dom0less-build.c
>> index 3f48f74226..50e2516b9e 100644
>> --- a/xen/arch/arm/dom0less-build.c
>> +++ b/xen/arch/arm/dom0less-build.c
>> @@ -292,6 +292,23 @@ static int __init domu_dt_sci_parse(struct dt_device_node *node,
>>
>>           d_cfg->arch.arm_sci_type = XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC;
>>       }
>> +    else if ( !strcmp(sci_type, "scmi_smc_multiagent") )
>> +    {
>> +        uint32_t agent_id = 0;
>> +
>> +        if ( !IS_ENABLED(CONFIG_SCMI_SMC_MA) )
>> +        {
>> +            printk(XENLOG_ERR "xen,sci_type=scmi_smc_multiagent requested, but CONFIG_SCMI_SMC_MA not set\n");
>> +            return -EINVAL;
>> +        }
>> +
>> +        if ( !dt_property_read_u32(node, "xen,sci-agent-id", &agent_id) ||
>> +             agent_id >= UINT8_MAX )
>> +            return -EINVAL;
>> +
>> +        d_cfg->arch.arm_sci_type = XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC_MA;
>> +        d_cfg->arch.arm_sci_agent_id = agent_id;
>> +    }
>>       else
>>       {
>>           printk(XENLOG_ERR "xen,sci_type in not valid (%s) for domain %s\n",
>> diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
>> index 72d5316180..297c46d5a2 100644
>> --- a/xen/arch/arm/domain_build.c
>> +++ b/xen/arch/arm/domain_build.c
>> @@ -87,6 +87,39 @@ int __init parse_arch_dom0_param(const char *s, const char *e)
>>       return -EINVAL;
>>   }
>>
>> +#ifdef CONFIG_SCMI_SMC_MA
>> +/* SCMI agent ID for dom0 obtained from xen,sci container */
>> +#define SCMI_AGENT_ID_INVALID UINT8_MAX
>> +
>> +static uint8_t __init get_dom0_scmi_agent_id(void)
>> +{
>> +    const struct dt_device_node *config_node;
>> +    u32 val;
>> +    const struct dt_property *prop;
>> +
>> +    config_node = dt_find_compatible_node(NULL, NULL, "xen,sci");
>> +    if ( !config_node )
>> +        return SCMI_AGENT_ID_INVALID;
>> +
>> +    prop = dt_find_property(config_node, "xen,dom0-sci-agent-id", NULL);
>> +    if ( !prop )
>> +        return SCMI_AGENT_ID_INVALID;
>> +
>> +    if ( !dt_property_read_u32(config_node, "xen,dom0-sci-agent-id", &val) )
>> +        return SCMI_AGENT_ID_INVALID;
>> +
>> +    if ( val >= SCMI_AGENT_ID_INVALID )
>> +    {
>> +         printk(XENLOG_WARNING
>> +             "Invalid xen,dom0-sci-agent-id=%u, SCMI disabled for Dom0\n",
>> +             val);
>> +        return SCMI_AGENT_ID_INVALID;
>> +    }
>> +
>> +    return val;
>> +}
>> +#endif /* CONFIG_SCMI_SMC_MA */
>> +
>>   /* Override macros from asm/page.h to make them work with mfn_t */
>>   #undef virt_to_mfn
>>   #define virt_to_mfn(va) _mfn(__virt_to_mfn(va))
>> @@ -1479,6 +1512,7 @@ static int __init handle_node(struct domain *d, struct kernel_info *kinfo,
>>           DT_MATCH_TYPE("memory"),
>>           /* The memory mapped timer is not supported by Xen. */
>>           DT_MATCH_COMPATIBLE("arm,armv7-timer-mem"),
>> +        DT_MATCH_COMPATIBLE("xen,sci"),
>>           { /* sentinel */ },
>>       };
>>       static const struct dt_device_match timer_matches[] __initconst =
>> @@ -1965,6 +1999,15 @@ void __init create_dom0(void)
>>       dom0_cfg.arch.tee_type = tee_get_type();
>>       dom0_cfg.max_vcpus = dom0_max_vcpus();
>>
>> +#ifdef CONFIG_SCMI_SMC_MA
>> +    /* Set up SCMI agent ID if provided in the xen,sci container */
>> +    dom0_cfg.arch.arm_sci_agent_id = get_dom0_scmi_agent_id();
>> +    dom0_cfg.arch.arm_sci_type = (dom0_cfg.arch.arm_sci_agent_id !=
>> +                                  SCMI_AGENT_ID_INVALID) ?
>> +                                 XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC_MA :
>> +                                 XEN_DOMCTL_CONFIG_ARM_SCI_NONE;
>> +#endif
>> +
>>       if ( iommu_enabled )
>>           dom0_cfg.flags |= XEN_DOMCTL_CDF_iommu;
>>
>> diff --git a/xen/arch/arm/firmware/Kconfig b/xen/arch/arm/firmware/Kconfig
>> index 5c5f0880c4..972cd9b173 100644
>> --- a/xen/arch/arm/firmware/Kconfig
>> +++ b/xen/arch/arm/firmware/Kconfig
>> @@ -29,6 +29,18 @@ config SCMI_SMC
>>        driver domain.
>>        Use with EL3 firmware which supports only single SCMI OSPM agent.
>>
>> +config SCMI_SMC_MA
>> +    bool "Enable ARM SCMI SMC multi-agent driver"
>> +    depends on ARM_64
>> +    select ARM_SCI
>> +    help
>> +      Enables SCMI SMC/HVC multi-agent in XEN to pass SCMI requests from Domains
>> +      to EL3 firmware (TF-A) which supports multi-agent feature.
>> +      This feature allows to enable SCMI per Domain using unique SCMI agent_id,
>> +      so Domain is identified by EL3 firmware as an SCMI Agent and can access
>> +      allowed platform resources through dedicated SMC/HVC Shared memory based
>> +      transport.
>> +
>>   endchoice
>>
>>   endmenu
>> diff --git a/xen/arch/arm/firmware/Makefile b/xen/arch/arm/firmware/Makefile
>> index 71bdefc24a..37927e690e 100644
>> --- a/xen/arch/arm/firmware/Makefile
>> +++ b/xen/arch/arm/firmware/Makefile
>> @@ -1,2 +1,3 @@
>>   obj-$(CONFIG_ARM_SCI) += sci.o
>>   obj-$(CONFIG_SCMI_SMC) += scmi-smc.o
>> +obj-$(CONFIG_SCMI_SMC_MA) += scmi-shmem.o scmi-smc-multiagent.o
>> diff --git a/xen/arch/arm/firmware/scmi-proto.h b/xen/arch/arm/firmware/scmi-proto.h
>> new file mode 100644
>> index 0000000000..49f63cfc0a
>> --- /dev/null
>> +++ b/xen/arch/arm/firmware/scmi-proto.h
>> @@ -0,0 +1,164 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * Arm System Control and Management Interface definitions
>> + * Version 3.0 (DEN0056C)
>> + *
>> + * Copyright (c) 2025 EPAM Systems
>> + */
>> +
>> +#ifndef ARM_FIRMWARE_SCMI_PROTO_H_
>> +#define ARM_FIRMWARE_SCMI_PROTO_H_
>> +
>> +#include <xen/stdint.h>
>> +
>> +#define SCMI_SHORT_NAME_MAX_SIZE 16
>> +
>> +/* SCMI status codes. See section 4.1.4 */
>> +#define SCMI_SUCCESS              0
>> +#define SCMI_NOT_SUPPORTED      (-1)
>> +#define SCMI_INVALID_PARAMETERS (-2)
>> +#define SCMI_DENIED             (-3)
>> +#define SCMI_NOT_FOUND          (-4)
>> +#define SCMI_OUT_OF_RANGE       (-5)
>> +#define SCMI_BUSY               (-6)
>> +#define SCMI_COMMS_ERROR        (-7)
>> +#define SCMI_GENERIC_ERROR      (-8)
>> +#define SCMI_HARDWARE_ERROR     (-9)
>> +#define SCMI_PROTOCOL_ERROR     (-10)
>> +
>> +/* Protocol IDs */
>> +#define SCMI_BASE_PROTOCOL 0x10
>> +
>> +/* Base protocol message IDs */
>> +#define SCMI_BASE_PROTOCOL_VERSION            0x0
>> +#define SCMI_BASE_PROTOCOL_ATTIBUTES          0x1
>> +#define SCMI_BASE_PROTOCOL_MESSAGE_ATTRIBUTES 0x2
>> +#define SCMI_BASE_DISCOVER_AGENT              0x7
>> +#define SCMI_BASE_SET_DEVICE_PERMISSIONS      0x9
>> +#define SCMI_BASE_RESET_AGENT_CONFIGURATION   0xB
>> +
>> +typedef struct scmi_msg_header {
>> +    uint8_t id;
>> +    uint8_t type;
>> +    uint8_t protocol;
>> +    uint32_t status;
>> +} scmi_msg_header_t;
>> +
>> +/* Table 2 Message header format */
>> +#define SCMI_HDR_ID    GENMASK(7, 0)
>> +#define SCMI_HDR_TYPE  GENMASK(9, 8)
>> +#define SCMI_HDR_PROTO GENMASK(17, 10)
>> +
>> +#define SCMI_FIELD_GET(_mask, _reg)                                            \
>> +    ((typeof(_mask))(((_reg) & (_mask)) >> (ffs64(_mask) - 1)))
>> +#define SCMI_FIELD_PREP(_mask, _val)                                           \
>> +    (((typeof(_mask))(_val) << (ffs64(_mask) - 1)) & (_mask))
>> +
>> +static inline uint32_t pack_scmi_header(scmi_msg_header_t *hdr)
>> +{
>> +    return SCMI_FIELD_PREP(SCMI_HDR_ID, hdr->id) |
>> +           SCMI_FIELD_PREP(SCMI_HDR_TYPE, hdr->type) |
>> +           SCMI_FIELD_PREP(SCMI_HDR_PROTO, hdr->protocol);
>> +}
>> +
>> +static inline void unpack_scmi_header(uint32_t msg_hdr, scmi_msg_header_t *hdr)
>> +{
>> +    hdr->id = SCMI_FIELD_GET(SCMI_HDR_ID, msg_hdr);
>> +    hdr->type = SCMI_FIELD_GET(SCMI_HDR_TYPE, msg_hdr);
>> +    hdr->protocol = SCMI_FIELD_GET(SCMI_HDR_PROTO, msg_hdr);
>> +}
>> +
>> +static inline int scmi_to_xen_errno(int scmi_status)
>> +{
>> +    if ( scmi_status == SCMI_SUCCESS )
>> +        return 0;
>> +
>> +    switch ( scmi_status )
>> +    {
>> +    case SCMI_NOT_SUPPORTED:
>> +        return -EOPNOTSUPP;
>> +    case SCMI_INVALID_PARAMETERS:
>> +        return -EINVAL;
>> +    case SCMI_DENIED:
>> +        return -EACCES;
>> +    case SCMI_NOT_FOUND:
>> +        return -ENOENT;
>> +    case SCMI_OUT_OF_RANGE:
>> +        return -ERANGE;
>> +    case SCMI_BUSY:
>> +        return -EBUSY;
>> +    case SCMI_COMMS_ERROR:
>> +        return -ENOTCONN;
>> +    case SCMI_GENERIC_ERROR:
>> +        return -EIO;
>> +    case SCMI_HARDWARE_ERROR:
>> +        return -ENXIO;
>> +    case SCMI_PROTOCOL_ERROR:
>> +        return -EBADMSG;
>> +    default:
>> +        return -EINVAL;
>> +    }
>> +}
>> +
>> +/* PROTOCOL_VERSION */
>> +#define SCMI_VERSION_MINOR GENMASK(15, 0)
>> +#define SCMI_VERSION_MAJOR GENMASK(31, 16)
>> +
>> +struct scmi_msg_prot_version_p2a {
>> +    uint32_t version;
>> +} __packed;
>> +
>> +/* BASE PROTOCOL_ATTRIBUTES */
>> +#define SCMI_BASE_ATTR_NUM_PROTO GENMASK(7, 0)
>> +#define SCMI_BASE_ATTR_NUM_AGENT GENMASK(15, 8)
>> +
>> +struct scmi_msg_base_attributes_p2a {
>> +    uint32_t attributes;
>> +} __packed;
>> +
>> +/*
>> + * BASE_DISCOVER_AGENT
>> + */
>> +#define SCMI_BASE_AGENT_ID_OWN 0xFFFFFFFF
>> +
>> +struct scmi_msg_base_discover_agent_a2p {
>> +    uint32_t agent_id;
>> +} __packed;
>> +
>> +struct scmi_msg_base_discover_agent_p2a {
>> +    uint32_t agent_id;
>> +    char name[SCMI_SHORT_NAME_MAX_SIZE];
>> +} __packed;
>> +
>> +/*
>> + * BASE_SET_DEVICE_PERMISSIONS
>> + */
>> +#define SCMI_BASE_DEVICE_ACCESS_ALLOW           BIT(0, UL)
>> +
>> +struct scmi_msg_base_set_device_permissions_a2p {
>> +    uint32_t agent_id;
>> +    uint32_t device_id;
>> +    uint32_t flags;
>> +} __packed;
>> +
>> +/*
>> + * BASE_RESET_AGENT_CONFIGURATION
>> + */
>> +#define SCMI_BASE_AGENT_PERMISSIONS_RESET       BIT(0, UL)
>> +
>> +struct scmi_msg_base_reset_agent_cfg_a2p {
>> +    uint32_t agent_id;
>> +    uint32_t flags;
>> +} __packed;
>> +
>> +#endif /* ARM_FIRMWARE_SCMI_PROTO_H_ */
>> +
>> +/*
>> + * Local variables:
>> + * mode: C
>> + * c-file-style: "BSD"
>> + * c-basic-offset: 4
>> + * tab-width: 4
>> + * indent-tabs-mode: nil
>> + * End:
>> + */
>> diff --git a/xen/arch/arm/firmware/scmi-shmem.c b/xen/arch/arm/firmware/scmi-shmem.c
>> new file mode 100644
>> index 0000000000..e36745a85e
>> --- /dev/null
>> +++ b/xen/arch/arm/firmware/scmi-shmem.c
>> @@ -0,0 +1,118 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * SMC/HVC shmem transport implementation used by
>> + * SCI SCMI multi-agent driver.
>> + *
>> + * Oleksii Moisieiev <oleksii_moisieiev@epam.com>
>> + * Copyright (c) 2025 EPAM Systems
>> + */
>> +
>> +#include <xen/err.h>
>> +#include <xen/io.h>
>> +#include <asm/io.h>
>> +
>> +#include "scmi-proto.h"
>> +#include "scmi-shmem.h"
>> +
>> +static inline int
>> +shmem_channel_status(const volatile struct scmi_shared_mem __iomem *shmem)
>> +{
>> +    return (readl(&shmem->channel_status) &
>> +            SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE) ? 0 : -EBUSY;
>> +}
>> +
>> +int shmem_put_message(volatile struct scmi_shared_mem __iomem *shmem,
>> +                      scmi_msg_header_t *hdr, void *data, unsigned int len)
>> +{
>> +    int ret;
>> +
>> +    if ( (len + offsetof(struct scmi_shared_mem, msg_payload)) >
>> +         SCMI_SHMEM_MAPPED_SIZE )
>> +    {
>> +        printk(XENLOG_ERR "scmi: Wrong size of smc message. Data is invalid\n");
>> +        return -EINVAL;
>> +    }
>> +
>> +    ret = shmem_channel_status(shmem);
>> +    if ( ret )
>> +        return ret;
>> +
>> +    writel_relaxed(0x0, &shmem->channel_status);
>> +    /* Writing 0x0 right now, but "shmem"_FLAG_INTR_ENABLED can be set */
>> +    writel_relaxed(0x0, &shmem->flags);
>> +    writel_relaxed(sizeof(shmem->msg_header) + len, &shmem->length);
>> +    writel(pack_scmi_header(hdr), &shmem->msg_header);
>> +
>> +    if ( len > 0 && data )
>> +        memcpy_toio(shmem->msg_payload, data, len);
>> +
>> +    return 0;
>> +}
>> +
>> +int shmem_get_response(const volatile struct scmi_shared_mem __iomem *shmem,
>> +                       scmi_msg_header_t *hdr, void *data, unsigned int len)
>> +{
>> +    int recv_len;
>> +    int ret;
>> +    /*
>> +     * First word of msg_payload carries the returned status; exclude it from
>> +     * recv_len so only the protocol payload is copied back to the caller.
>> +     */
>> +    int pad = sizeof(hdr->status);
>> +
>> +    if ( len >= SCMI_SHMEM_MAPPED_SIZE -
>> +         offsetof(struct scmi_shared_mem, msg_payload) )
>> +    {
>> +        printk(XENLOG_ERR
>> +               "scmi: Wrong size of input smc message. Data may be invalid\n");
>> +        return -EINVAL;
>> +    }
>> +
>> +    ret = shmem_channel_status(shmem);
>> +    if ( ret )
>> +        return ret;
>> +
>> +    recv_len = readl(&shmem->length) - sizeof(shmem->msg_header);
>> +
>> +    if ( recv_len < 0 )
>> +    {
>> +        printk(XENLOG_ERR
>> +               "scmi: Wrong size of smc message. Data may be invalid\n");
>> +        return -EINVAL;
>> +    }
>> +
>> +    unpack_scmi_header(readl(&shmem->msg_header), hdr);
>> +
>> +    hdr->status = readl(&shmem->msg_payload);
>> +    recv_len = recv_len > pad ? recv_len - pad : 0;
>> +
>> +    ret = scmi_to_xen_errno(hdr->status);
>> +    if ( ret )
>> +    {
>> +        printk(XENLOG_DEBUG "scmi: Error received: %d\n", ret);
>> +        return ret;
>> +    }
>> +
>> +    if ( recv_len > len )
>> +    {
>> +        printk(XENLOG_ERR
>> +               "scmi: Not enough buffer for message %d, expecting %d\n",
>> +               recv_len, len);
>> +        return -EINVAL;
>> +    }
>> +
>> +    if ( recv_len > 0 )
>> +        memcpy_fromio(data, shmem->msg_payload + pad, recv_len);
>> +
>> +    return 0;
>> +}
>> +
>> +/*
>> + * Local variables:
>> + * mode: C
>> + * c-file-style: "BSD"
>> + * c-basic-offset: 4
>> + * tab-width: 4
>> + * indent-tabs-mode: nil
>> + * End:
>> + */
>> diff --git a/xen/arch/arm/firmware/scmi-shmem.h b/xen/arch/arm/firmware/scmi-shmem.h
>> new file mode 100644
>> index 0000000000..722263aa77
>> --- /dev/null
>> +++ b/xen/arch/arm/firmware/scmi-shmem.h
>> @@ -0,0 +1,45 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * Arm System Control and Management Interface definitions
>> + * Version 3.0 (DEN0056C)
>> + * Shared Memory based Transport
>> + *
>> + * Copyright (c) 2024 EPAM Systems
>> + */
>> +
>> +#ifndef ARM_FIRMWARE_SCMI_SHMEM_H_
>> +#define ARM_FIRMWARE_SCMI_SHMEM_H_
>> +
>> +#include <xen/stdint.h>
>> +
>> +#define SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE  BIT(0, UL)
>> +#define SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR BIT(1, UL)
>> +
>> +struct scmi_shared_mem {
>> +    uint32_t reserved;
>> +    uint32_t channel_status;
>> +    uint32_t reserved1[2];
>> +    uint32_t flags;
>> +    uint32_t length;
>> +    uint32_t msg_header;
>> +    uint8_t msg_payload[];
>> +};
>> +
>> +#define SCMI_SHMEM_MAPPED_SIZE PAGE_SIZE
>> +
>> +int shmem_put_message(volatile struct scmi_shared_mem __iomem *shmem,
>> +                      scmi_msg_header_t *hdr, void *data, unsigned int len);
>> +
>> +int shmem_get_response(const volatile struct scmi_shared_mem __iomem *shmem,
>> +                       scmi_msg_header_t *hdr, void *data, unsigned int len);
>> +#endif /* ARM_FIRMWARE_SCMI_SHMEM_H_ */
>> +
>> +/*
>> + * Local variables:
>> + * mode: C
>> + * c-file-style: "BSD"
>> + * c-basic-offset: 4
>> + * tab-width: 4
>> + * indent-tabs-mode: nil
>> + * End:
>> + */
>> diff --git a/xen/arch/arm/firmware/scmi-smc-multiagent.c b/xen/arch/arm/firmware/scmi-smc-multiagent.c
>> new file mode 100644
>> index 0000000000..0c5653fbc0
>> --- /dev/null
>> +++ b/xen/arch/arm/firmware/scmi-smc-multiagent.c
>> @@ -0,0 +1,816 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * SCI SCMI multi-agent driver, using SMC/HVC shmem as transport.
>> + *
>> + * Oleksii Moisieiev <oleksii_moisieiev@epam.com>
>> + * Copyright (c) 2025 EPAM Systems
>> + */
>> +
>> +#include <xen/acpi.h>
>> +
>> +#include <xen/device_tree.h>
>> +#include <xen/init.h>
>> +#include <xen/iocap.h>
>> +#include <xen/err.h>
>> +#include <xen/libfdt/libfdt.h>
>> +#include <xen/string.h>
>> +#include <xen/param.h>
>> +#include <xen/sched.h>
>> +#include <xen/vmap.h>
>> +
>> +#include <asm/firmware/sci.h>
>> +#include <asm/smccc.h>
>> +
>> +#include "scmi-proto.h"
>> +#include "scmi-shmem.h"
>> +
>> +#define SCMI_SECONDARY_AGENTS "scmi-secondary-agents"
>> +
>> +struct scmi_channel {
>> +    uint32_t agent_id;
>> +    uint32_t func_id;
>> +    domid_t domain_id;
>> +    uint64_t paddr;
>> +    struct scmi_shared_mem __iomem *shmem;
>> +    spinlock_t lock;
>> +    struct list_head list;
>> +};
>> +
>> +struct scmi_data {
>> +    struct list_head channel_list;
>> +    spinlock_t channel_list_lock;
>> +    uint32_t func_id;
>> +    bool initialized;
>> +    uint32_t shmem_phandle;
>> +    uint32_t hyp_channel_agent_id;
>> +    struct dt_device_node *dt_dev;
>> +};
>> +
>> +static struct scmi_data scmi_data;
>> +
>> +static bool scmi_is_under_xen_sci(const struct dt_device_node *node)
>> +{
>> +    const struct dt_device_node *p;
>> +
>> +    for ( p = node->parent; p; p = p->parent )
>> +        if ( dt_device_is_compatible(p, "xen,sci") )
>> +            return true;
>> +
>> +    return false;
>> +}
>> +
>> +static int send_smc_message(struct scmi_channel *chan_info,
>> +                            scmi_msg_header_t *hdr, void *data, int len)
>> +{
>> +    struct arm_smccc_res resp;
>> +    int ret;
>> +
>> +    ret = shmem_put_message(chan_info->shmem, hdr, data, len);
>> +    if ( ret )
>> +        return ret;
>> +
>> +    resp = arm_smccc_1_1_smc(chan_info->func_id, 0, 0, 0, 0, 0, 0, 0);
>> +
>> +    if ( resp.a0 == (unsigned long)ARM_SMCCC_INVALID_PARAMETER )
>> +        return -EINVAL;
>> +
>> +    if ( resp.a0 )
>> +        return -EOPNOTSUPP;
>> +
>> +    return 0;
>> +}
>> +
>> +static int do_smc_xfer(struct scmi_channel *chan_info, scmi_msg_header_t *hdr,
>> +                       void *tx_data, int tx_size, void *rx_data, int rx_size)
>> +{
>> +    int ret = 0;
>> +
>> +    ASSERT(chan_info && chan_info->shmem);
>> +
>> +    if ( !hdr )
>> +        return -EINVAL;
>> +
>> +    spin_lock(&chan_info->lock);
>> +
>> +    printk(XENLOG_DEBUG
>> +           "scmi: agent_id = %d msg_id = %x type = %d, proto = %x\n",
>> +           chan_info->agent_id, hdr->id, hdr->type, hdr->protocol);
>> +
>> +    ret = send_smc_message(chan_info, hdr, tx_data, tx_size);
>> +    if ( ret )
>> +        goto clean;
>> +
>> +    ret = shmem_get_response(chan_info->shmem, hdr, rx_data, rx_size);
>> +
>> +clean:
>> +    printk(XENLOG_DEBUG
>> +           "scmi: get smc response agent_id = %d msg_id = %x proto = %x res=%d\n",
>> +           chan_info->agent_id, hdr->id, hdr->protocol, ret);
>> +
>> +    spin_unlock(&chan_info->lock);
>> +
>> +    return ret;
>> +}
>> +
>> +static struct scmi_channel *get_channel_by_id(uint32_t agent_id)
>> +{
>> +    struct scmi_channel *curr;
>> +    bool found = false;
>> +
>> +    spin_lock(&scmi_data.channel_list_lock);
>> +    list_for_each_entry(curr, &scmi_data.channel_list, list)
>> +    {
>> +        if ( curr->agent_id == agent_id )
>> +        {
>> +            found = true;
>> +            break;
>> +        }
>> +    }
>> +
>> +    spin_unlock(&scmi_data.channel_list_lock);
>> +    if ( found )
>> +        return curr;
>> +
>> +    return NULL;
>> +}
>> +
>> +static struct scmi_channel *acquire_scmi_channel(struct domain *d,
>> +                                                 uint32_t agent_id)
>> +{
>> +    struct scmi_channel *curr;
>> +    struct scmi_channel *ret = ERR_PTR(-ENOENT);
>> +
>> +    spin_lock(&scmi_data.channel_list_lock);
>> +    list_for_each_entry(curr, &scmi_data.channel_list, list)
>> +    {
>> +        if ( curr->agent_id == agent_id )
>> +        {
>> +            if ( curr->domain_id != DOMID_INVALID )
>> +            {
>> +                ret = ERR_PTR(-EEXIST);
>> +                break;
>> +            }
>> +
>> +            curr->domain_id = d->domain_id;
>> +            ret = curr;
>> +            break;
>> +        }
>> +    }
>> +
>> +    spin_unlock(&scmi_data.channel_list_lock);
>> +
>> +    return ret;
>> +}
>> +
>> +static void relinquish_scmi_channel(struct scmi_channel *channel)
>> +{
>> +    ASSERT(channel != NULL);
>> +
>> +    spin_lock(&scmi_data.channel_list_lock);
>> +    channel->domain_id = DOMID_INVALID;
>> +    spin_unlock(&scmi_data.channel_list_lock);
>> +}
>> +
>> +static int map_channel_memory(struct scmi_channel *channel)
>> +{
>> +    ASSERT(channel && channel->paddr);
>> +    channel->shmem = ioremap_nocache(channel->paddr, SCMI_SHMEM_MAPPED_SIZE);
>> +    if ( !channel->shmem )
>> +        return -ENOMEM;
>> +
>> +    channel->shmem->channel_status = SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE;
>> +    printk(XENLOG_DEBUG "scmi: Got shmem %lx after vmap %p\n", channel->paddr,
>> +           channel->shmem);
>> +
>> +    return 0;
>> +}
>> +
>> +static void unmap_channel_memory(struct scmi_channel *channel)
>> +{
>> +    ASSERT(channel);
>> +
>> +    if ( !channel->shmem )
>> +        return;
>> +
>> +    iounmap(channel->shmem);
>> +    channel->shmem = NULL;
>> +}
>> +
>> +static struct scmi_channel *smc_create_channel(uint32_t agent_id,
>> +                                               uint32_t func_id, uint64_t addr)
>> +{
>> +    struct scmi_channel *channel, *curr;
>> +
>> +    spin_lock(&scmi_data.channel_list_lock);
>> +
>> +    /* Check if channel already exists while holding the lock */
>> +    list_for_each_entry(curr, &scmi_data.channel_list, list)
>> +    {
>> +        if ( curr->agent_id == agent_id )
>> +        {
>> +            spin_unlock(&scmi_data.channel_list_lock);
>> +            return ERR_PTR(-EEXIST);
>> +        }
>> +    }
>> +
>> +    channel = xmalloc(struct scmi_channel);
>> +    if ( !channel )
>> +    {
>> +        spin_unlock(&scmi_data.channel_list_lock);
>> +        return ERR_PTR(-ENOMEM);
>> +    }
>> +
>> +    spin_lock_init(&channel->lock);
>> +    channel->agent_id = agent_id;
>> +    channel->func_id = func_id;
>> +    channel->domain_id = DOMID_INVALID;
>> +    channel->shmem = NULL;
>> +    channel->paddr = addr;
>> +    list_add_tail(&channel->list, &scmi_data.channel_list);
>> +
>> +    spin_unlock(&scmi_data.channel_list_lock);
>> +    return channel;
>> +}
>> +
>> +static void free_channel_list(void)
>> +{
>> +    struct scmi_channel *curr, *_curr;
>> +    /*
>> +     * Called only on the __init error path, before any runtime users exist,
>> +     * so no other thread is iterating channel_list. Keep the lock for the
>> +     * whole drain for clarity and to avoid misleading readers about possible
>> +     * concurrent access.
>> +     */
>> +    spin_lock(&scmi_data.channel_list_lock);
>> +    list_for_each_entry_safe(curr, _curr, &scmi_data.channel_list, list)
>> +    {
>> +        list_del(&curr->list);
>> +        xfree(curr);
>> +    }
>> +    spin_unlock(&scmi_data.channel_list_lock);
>> +}
>> +
>> +static int __init
>> +scmi_dt_read_hyp_channel_addr(struct dt_device_node *scmi_node, u64 *addr,
>> +                              u64 *size)
>> +{
>> +    struct dt_device_node *shmem_node;
>> +    const __be32 *prop;
>> +
>> +    prop = dt_get_property(scmi_node, "shmem", NULL);
>> +    if ( !prop )
>> +        return -EINVAL;
>> +
>> +    shmem_node = dt_find_node_by_phandle(be32_to_cpu(*prop));
>> +    if ( IS_ERR_OR_NULL(shmem_node) )
>> +    {
>> +        printk(XENLOG_ERR
>> +               "scmi: Device tree error, can't parse reserved memory %ld\n",
>> +               PTR_ERR(shmem_node));
>> +        return PTR_ERR(shmem_node);
>> +    }
>> +
>> +    return dt_device_get_address(shmem_node, 0, addr, size);
>> +}
>> +
>> +/*
>> + * Handle Dom0 SCMI specific DT nodes
>> + *
>> + * Make a decision on copying SCMI specific nodes into Dom0 device tree.
>> + * For SCMI multi-agent case:
>> + * - shmem nodes will not be copied and generated instead if SCMI
>> + *   is enabled for Dom0
>> + * - scmi node will be copied if SCMI is enabled for Dom0
>> + */
>> +static bool scmi_dt_handle_node(struct domain *d, struct dt_device_node *node)
>> +{
>> +    static const struct dt_device_match shmem_matches[] __initconst = {
>> +        DT_MATCH_COMPATIBLE("arm,scmi-shmem"),
>> +        { /* sentinel */ },
>> +    };
>> +    static const struct dt_device_match scmi_matches[] __initconst = {
>> +        DT_MATCH_PATH("/firmware/scmi"),
>> +        { /* sentinel */ },
>> +    };
>> +
>> +    if ( !scmi_data.initialized )
>> +        return false;
>> +
>> +    /* skip scmi shmem node for dom0 if scmi not enabled */
>> +    if ( dt_match_node(shmem_matches, node) && !sci_domain_is_enabled(d) )
>> +    {
>> +        dt_dprintk("  Skip scmi shmem node\n");
>> +        return true;
>> +    }
>> +
>> +    /* drop scmi if not enabled */
>> +    if ( dt_match_node(scmi_matches, node) && !sci_domain_is_enabled(d) )
>> +    {
>> +        dt_dprintk("  Skip scmi node\n");
>> +        return true;
>> +    }
>> +
>> +    return false;
>> +}
>> +
>> +static int scmi_assign_device(uint32_t agent_id, uint32_t device_id,
>> +                              uint32_t flags)
>> +{
>> +    struct scmi_msg_base_set_device_permissions_a2p tx;
>> +    struct scmi_channel *channel;
>> +    scmi_msg_header_t hdr;
>> +
>> +    channel = get_channel_by_id(scmi_data.hyp_channel_agent_id);
>> +    if ( !channel )
>> +        return -EINVAL;
>> +
>> +    hdr.id = SCMI_BASE_SET_DEVICE_PERMISSIONS;
>> +    hdr.type = 0;
>> +    hdr.protocol = SCMI_BASE_PROTOCOL;
>> +
>> +    tx.agent_id = agent_id;
>> +    tx.device_id = device_id;
>> +    tx.flags = flags;
>> +
>> +    return do_smc_xfer(channel, &hdr, &tx, sizeof(tx), NULL, 0);
>> +}
>> +
>> +static int scmi_dt_assign_device(struct domain *d,
>> +                                 struct dt_phandle_args *ac_spec)
>> +{
>> +    struct scmi_channel *agent_channel;
>> +    uint32_t scmi_device_id = ac_spec->args[0];
>> +    int ret;
>> +
>> +    if ( !d->arch.sci_data )
>> +        return 0;
>> +
>> +    /* The access-controllers is specified for DT dev, but it's not a SCMI */
>> +    if ( !scmi_data.dt_dev ||
>> +         !dt_node_path_is_equal(ac_spec->np, scmi_data.dt_dev->full_name) )
>> +        return 0;
>> +
>> +    agent_channel = d->arch.sci_data;
>> +
>> +    spin_lock(&agent_channel->lock);
>> +
>> +    ret = scmi_assign_device(agent_channel->agent_id, scmi_device_id,
>> +                             SCMI_BASE_DEVICE_ACCESS_ALLOW);
>> +    if ( ret )
>> +    {
>> +        printk(XENLOG_ERR
>> +               "scmi: could not assign dev for %pd agent:%d dev_id:%u (%d)",
>> +               d, agent_channel->agent_id, scmi_device_id, ret);
>> +    }
>> +
>> +    spin_unlock(&agent_channel->lock);
>> +    return ret;
>> +}
>> +
>> +static int collect_agent_id(struct scmi_channel *agent_channel)
>> +{
>> +    int ret;
>> +    scmi_msg_header_t hdr;
>> +    struct scmi_msg_base_discover_agent_p2a da_rx;
>> +    struct scmi_msg_base_discover_agent_a2p da_tx;
>> +
>> +    ret = map_channel_memory(agent_channel);
>> +    if ( ret )
>> +        return ret;
>> +
>> +    hdr.id = SCMI_BASE_DISCOVER_AGENT;
>> +    hdr.type = 0;
>> +    hdr.protocol = SCMI_BASE_PROTOCOL;
>> +
>> +    da_tx.agent_id = agent_channel->agent_id;
>> +
>> +    ret = do_smc_xfer(agent_channel, &hdr, &da_tx, sizeof(da_tx), &da_rx,
>> +                        sizeof(da_rx));
>> +    if ( agent_channel->domain_id != DOMID_XEN )
>> +        unmap_channel_memory(agent_channel);
>> +    if ( ret )
>> +        return ret;
>> +
>> +    printk(XENLOG_DEBUG "id=0x%x name=%s\n", da_rx.agent_id, da_rx.name);
>> +    agent_channel->agent_id = da_rx.agent_id;
>> +    return 0;
>> +}
>> +
>> +static __init int collect_agents(struct dt_device_node *scmi_node)
>> +{
>> +    const struct dt_device_node *config_node;
>> +    const __be32 *prop;
>> +    uint32_t len;
>> +    const __be32 *end;
>> +    uint32_t cells_per_entry = 3; /* Default to 3 cells if property is absent. */
>> +
>> +    config_node = dt_find_compatible_node(NULL, NULL, "xen,sci");
>> +    if ( !config_node )
>> +    {
>> +        printk(XENLOG_WARNING "scmi: xen,sci node not found, no agents to collect.\n");
>> +        return -ENOENT;
>> +    }
>> +
>> +    /* Check for the optional '#scmi-secondary-agents-cells' property. */
>> +    if ( dt_property_read_u32(config_node, "#scmi-secondary-agents-cells",
>> +                              &cells_per_entry) )
>> +    {
>> +        if ( cells_per_entry != 2 && cells_per_entry != 3 )
>> +        {
>> +            printk(XENLOG_ERR "scmi: Invalid #scmi-secondary-agents-cells value: %u\n",
>> +                   cells_per_entry);
>> +            return -EINVAL;
>> +        }
>> +    }
>> +
>> +    prop = dt_get_property(config_node, SCMI_SECONDARY_AGENTS, &len);
>> +    if ( !prop )
>> +    {
>> +        printk(XENLOG_ERR "scmi: No %s property found, no agents to collect.\n",
>> +               SCMI_SECONDARY_AGENTS);
>> +        return -EINVAL;
>> +    }
>> +
>> +    /* Validate that the property length is a multiple of the cell size. */
>> +    if ( len == 0 || len % (cells_per_entry * sizeof(uint32_t)) != 0 )
>> +    {
>> +        printk(XENLOG_ERR "scmi: Invalid length of %s property: %u for %u cells per entry\n",
>> +               SCMI_SECONDARY_AGENTS, len, cells_per_entry);
>> +        return -EINVAL;
>> +    }
>> +
>> +    end = (const __be32 *)((const u8 *)prop + len);
>> +
>> +    for ( ; prop < end; )
>> +    {
>> +        uint32_t agent_id;
>> +        uint32_t smc_id;
>> +        uint32_t shmem_phandle;
>> +        struct dt_device_node *node;
>> +        u64 addr, size;
>> +        int ret;
>> +        struct scmi_channel *agent_channel;
>> +
>> +        smc_id = be32_to_cpu(*prop++);
>> +        shmem_phandle = be32_to_cpu(*prop++);
>> +
>> +        if ( cells_per_entry == 3 )
>> +            agent_id = be32_to_cpu(*prop++);
>> +        else
>> +            agent_id = SCMI_BASE_AGENT_ID_OWN;
>> +
>> +        node = dt_find_node_by_phandle(shmem_phandle);
>> +        if ( !node )
>> +        {
>> +            printk(XENLOG_ERR "scmi: Could not find shmem node for agent %u\n",
>> +                   agent_id);
>> +            return -EINVAL;
>> +        }
>> +
>> +        ret = dt_device_get_address(node, 0, &addr, &size);
>> +        if ( ret )
>> +        {
>> +            printk(XENLOG_ERR
>> +                   "scmi: Could not read shmem address for agent %u: %d\n",
>> +                   agent_id, ret);
>> +            return ret;
>> +        }
>> +
>> +        if ( !IS_ALIGNED(size, SCMI_SHMEM_MAPPED_SIZE) ||
>> +             !IS_ALIGNED(addr, SCMI_SHMEM_MAPPED_SIZE) )
>> +        {
>> +            printk(XENLOG_ERR "scmi: shmem memory is not aligned\n");
>> +            return -EINVAL;
>> +        }
>> +
>> +        agent_channel = smc_create_channel(agent_id, smc_id, addr);
>> +        if ( IS_ERR(agent_channel) )
>> +        {
>> +            printk(XENLOG_ERR "scmi: Could not create channel for agent %u: %ld\n",
>> +                   agent_id, PTR_ERR(agent_channel));
>> +            return PTR_ERR(agent_channel);
>> +        }
>> +
>> +        if ( cells_per_entry == 2 )
>> +        {
>> +            ret = collect_agent_id(agent_channel);
>> +            if ( ret )
>> +                return ret;
>> +        }
>> +
>> +        printk(XENLOG_DEBUG "scmi: Agent %u SMC %X addr %lx\n", agent_channel->agent_id,
>> +               smc_id, (unsigned long)addr);
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int scmi_domain_init(struct domain *d,
>> +                            struct xen_domctl_createdomain *config)
>> +{
>> +    struct scmi_channel *channel;
>> +    int ret;
>> +
>> +    if ( !scmi_data.initialized )
>> +        return 0;
>> +
>> +    /*
>> +     * SCMI support is configured via:
>> +     * - For dom0: xen,dom0-sci-agent-id property under the xen,sci container
>> +     * - For dom0less: xen,sci-agent-id in the domain node
>> +     * The config->arch.arm_sci_type and config->arch.arm_sci_agent_id
>> +     * are already set by domain_build.c or dom0less-build.c
>> +     */
>> +
>> +    if ( config->arch.arm_sci_type == XEN_DOMCTL_CONFIG_ARM_SCI_NONE )
>> +        return 0;
>> +
>> +    channel = acquire_scmi_channel(d, config->arch.arm_sci_agent_id);
>> +    if ( IS_ERR(channel) )
>> +    {
>> +        printk(XENLOG_ERR
>> +               "scmi: Failed to acquire SCMI channel for agent_id %u: %ld\n",
>> +               config->arch.arm_sci_agent_id, PTR_ERR(channel));
>> +        return PTR_ERR(channel);
>> +    }
>> +
>> +    printk(XENLOG_INFO
>> +           "scmi: Acquire channel id = 0x%x, domain_id = %d paddr = 0x%lx\n",
>> +           channel->agent_id, channel->domain_id, channel->paddr);
>> +
>> +    /*
>> +     * Dom0 (if present) needs to have an access to the guest memory range
>> +     * to satisfy iomem_access_permitted() check in XEN_DOMCTL_iomem_permission
>> +     * domctl.
>> +     */
>> +    if ( hardware_domain && !is_hardware_domain(d) )
>> +    {
>> +        ret = iomem_permit_access(hardware_domain, paddr_to_pfn(channel->paddr),
>> +                                  paddr_to_pfn(channel->paddr +
>> +                                  SCMI_SHMEM_MAPPED_SIZE - 1));
>> +        if ( ret )
>> +            goto error;
>> +    }
>> +
>> +    d->arch.sci_data = channel;
>> +    d->arch.sci_enabled = true;
>> +
>> +    return 0;
>> +
>> +error:
>> +    relinquish_scmi_channel(channel);
>> +    return ret;
>> +}
>> +
>> +int scmi_domain_sanitise_config(struct xen_domctl_createdomain *config)
>> +{
>> +    if ( config->arch.arm_sci_type != XEN_DOMCTL_CONFIG_ARM_SCI_NONE &&
>> +         config->arch.arm_sci_type != XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC_MA )
>> +    {
>> +        dprintk(XENLOG_INFO, "scmi: Unsupported ARM_SCI type\n");
>> +        return -EINVAL;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int scmi_relinquish_resources(struct domain *d)
>> +{
>> +    int ret;
>> +    struct scmi_channel *channel, *agent_channel;
>> +    scmi_msg_header_t hdr;
>> +    struct scmi_msg_base_reset_agent_cfg_a2p tx;
>> +
>> +    if ( !d->arch.sci_data )
>> +        return 0;
>> +
>> +    agent_channel = d->arch.sci_data;
>> +
>> +    spin_lock(&agent_channel->lock);
>> +    tx.agent_id = agent_channel->agent_id;
>> +    spin_unlock(&agent_channel->lock);
>> +
>> +    channel = get_channel_by_id(scmi_data.hyp_channel_agent_id);
>> +    if ( !channel )
>> +    {
>> +        printk(XENLOG_ERR
>> +               "scmi: Unable to get Hypervisor scmi channel for domain %d\n",
>> +               d->domain_id);
>> +        return -EINVAL;
>> +    }
>> +
>> +    hdr.id = SCMI_BASE_RESET_AGENT_CONFIGURATION;
>> +    hdr.type = 0;
>> +    hdr.protocol = SCMI_BASE_PROTOCOL;
>> +
>> +    tx.flags = SCMI_BASE_AGENT_PERMISSIONS_RESET;
>> +
>> +    ret = do_smc_xfer(channel, &hdr, &tx, sizeof(tx), NULL, 0);
>> +    if ( ret == -EOPNOTSUPP )
>> +        return 0;
>> +
>> +    return ret;
>> +}
>> +
>> +static void scmi_domain_destroy(struct domain *d)
>> +{
>> +    struct scmi_channel *channel;
>> +
>> +    if ( !d->arch.sci_data )
>> +        return;
>> +
>> +    channel = d->arch.sci_data;
>> +    spin_lock(&channel->lock);
>> +
>> +    relinquish_scmi_channel(channel);
>> +    printk(XENLOG_DEBUG "scmi: Free domain %d\n", d->domain_id);
>> +
>> +    d->arch.sci_data = NULL;
>> +    d->arch.sci_enabled = false;
>> +
>> +    spin_unlock(&channel->lock);
>> +}
>> +
>> +static bool scmi_handle_call(struct cpu_user_regs *regs)
>> +{
>> +    uint32_t fid = (uint32_t)get_user_reg(regs, 0);
>> +    struct scmi_channel *agent_channel;
>> +    struct domain *d = current->domain;
>> +    bool res = false;
>> +
>> +    if ( (!sci_domain_is_enabled(d)) || (!d->arch.sci_data) )
>> +        return false;
>> +
>> +    agent_channel = d->arch.sci_data;
>> +    spin_lock(&agent_channel->lock);
>> +
>> +    if ( agent_channel->func_id != fid )
>> +    {
>> +        res = false;
>> +        goto unlock;
>> +    }
>> +
>> +    arm_smccc_guest_smc(regs);
>> +    res = true;
>> +unlock:
>> +    spin_unlock(&agent_channel->lock);
>> +
>> +    return res;
>> +}
>> +
>> +static const struct sci_mediator_ops scmi_ops = {
>> +    .domain_init = scmi_domain_init,
>> +    .domain_destroy = scmi_domain_destroy,
>> +    .relinquish_resources = scmi_relinquish_resources,
>> +    .handle_call = scmi_handle_call,
>> +    .dom0_dt_handle_node = scmi_dt_handle_node,
>> +    .domain_sanitise_config = scmi_domain_sanitise_config,
>> +    .assign_dt_device = scmi_dt_assign_device,
>> +};
>> +
>> +static int __init scmi_check_smccc_ver(void)
>> +{
>> +    if ( smccc_ver < ARM_SMCCC_VERSION_1_1 )
>> +    {
>> +        printk(XENLOG_WARNING
>> +               "scmi: No SMCCC 1.1 support, SCMI calls forwarding disabled\n");
>> +        return -ENOSYS;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int __init scmi_dt_hyp_channel_read(struct dt_device_node *scmi_node,
>> +                                           struct scmi_data *scmi_data,
>> +                                           u64 *addr)
>> +{
>> +    int ret;
>> +    u64 size;
>> +
>> +    if ( !dt_property_read_u32(scmi_node, "arm,smc-id", &scmi_data->func_id) )
>> +    {
>> +        printk(XENLOG_ERR "scmi: unable to read smc-id from DT\n");
>> +        return -ENOENT;
>> +    }
>> +
>> +    ret = scmi_dt_read_hyp_channel_addr(scmi_node, addr, &size);
>> +    if ( IS_ERR_VALUE(ret) )
>> +        return -ENOENT;
>> +
>> +    if ( !IS_ALIGNED(size, SCMI_SHMEM_MAPPED_SIZE) )
>> +    {
>> +        printk(XENLOG_ERR "scmi: shmem memory is not aligned\n");
>> +        return -EINVAL;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static __init int scmi_probe(struct dt_device_node *scmi_node, const void *data)
>> +{
>> +    u64 addr;
>> +    int ret;
>> +    struct scmi_channel *channel;
>> +    unsigned int n_agents;
>> +    scmi_msg_header_t hdr;
>> +    struct scmi_msg_base_attributes_p2a rx;
>> +
>> +    ASSERT(scmi_node != NULL);
>> +
>> +    /*
>> +     * Only bind to the SCMI node provided by Xen under the xen,sci container
>> +     * (e.g. /chosen/xen/xen_scmi_config/scmi). This avoids binding to firmware
>> +     * SCMI nodes that belong to the host OSPM and keeps the mediator scoped to
>> +     * Xen-provided configuration only.
>> +     */
>> +    if ( !scmi_is_under_xen_sci(scmi_node) )
>> +        return -ENODEV;
>> +
>> +    if ( !acpi_disabled )
>> +    {
>> +        printk(XENLOG_WARNING "scmi: is not supported when using ACPI\n");
>> +        return -EINVAL;
>> +    }
>> +
>> +    ret = scmi_check_smccc_ver();
>> +    if ( ret )
>> +        return ret;
>> +
>> +    ret = scmi_dt_hyp_channel_read(scmi_node, &scmi_data, &addr);
>> +    if ( ret )
>> +        return ret;
>> +
>> +    INIT_LIST_HEAD(&scmi_data.channel_list);
>> +    spin_lock_init(&scmi_data.channel_list_lock);
>> +
>> +    scmi_data.dt_dev = scmi_node;
>> +
>> +    channel = smc_create_channel(SCMI_BASE_AGENT_ID_OWN, scmi_data.func_id, addr);
>> +    if ( IS_ERR(channel) )
>> +    {
>> +        ret = PTR_ERR(channel);
>> +        goto out;
>> +    }
>> +
>> +    /* Mark as Xen management channel before collecting agent ID */
>> +    channel->domain_id = DOMID_XEN;
>> +
>> +    /* Request agent id for Xen management channel */
>> +    ret = collect_agent_id(channel);
>> +    if ( ret )
>> +        goto error;
>> +
>> +    /* Save the agent id for Xen management channel */
>> +    scmi_data.hyp_channel_agent_id = channel->agent_id;
>> +
>> +    hdr.id = SCMI_BASE_PROTOCOL_ATTIBUTES;
>> +    hdr.type = 0;
>> +    hdr.protocol = SCMI_BASE_PROTOCOL;
>> +
>> +    ret = do_smc_xfer(channel, &hdr, NULL, 0, &rx, sizeof(rx));
>> +    if ( ret )
>> +        goto error;
>> +
>> +    n_agents = SCMI_FIELD_GET(SCMI_BASE_ATTR_NUM_AGENT, rx.attributes);
>> +    printk(XENLOG_DEBUG "scmi: Got agent count %d\n", n_agents);
>> +    ret = collect_agents(scmi_node);
>> +    if ( ret )
>> +        goto error;
>> +
>> +    ret = sci_register(&scmi_ops);
>> +    if ( ret )
>> +    {
>> +        printk(XENLOG_ERR "SCMI: mediator already registered (ret = %d)\n",
>> +               ret);
>> +        goto error;
>> +    }
>> +
>> +    scmi_data.initialized = true;
>> +    goto out;
>> +
>> +error:
>> +    unmap_channel_memory(channel);
>> +    free_channel_list();
>> +out:
>> +    return ret;
>> +}
>> +
>> +static const struct dt_device_match scmi_smc_match[] __initconst = {
>> +    DT_MATCH_COMPATIBLE("arm,scmi-smc"),
>> +    { /* sentinel */ },
>> +};
>> +
>> +DT_DEVICE_START(scmi_smc_ma, "SCMI SMC MEDIATOR", DEVICE_FIRMWARE)
>> +        .dt_match = scmi_smc_match,
>> +        .init = scmi_probe,
>> +DT_DEVICE_END
>> +
>> +/*
>> + * Local variables:
>> + * mode: C
>> + * c-file-style: "BSD"
>> + * c-basic-offset: 4
>> + * tab-width: 4
>> + * indent-tabs-mode: nil
>> + * End:
>> + */
>> diff --git a/xen/include/public/arch-arm.h b/xen/include/public/arch-arm.h
>> index 1fc676fd96..a9a10a3b8d 100644
>> --- a/xen/include/public/arch-arm.h
>> +++ b/xen/include/public/arch-arm.h
>> @@ -329,6 +329,7 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t);
>>
>>   #define XEN_DOMCTL_CONFIG_ARM_SCI_NONE      0
>>   #define XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC  1
>> +#define XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC_MA  2
>>
>>   #define XEN_DOMCTL_CONFIG_ARM_V8R_EL1_MSA_NONE    0
>>   #define XEN_DOMCTL_CONFIG_ARM_V8R_EL1_MSA_PMSA    1
>> @@ -361,7 +362,9 @@ struct xen_arch_domainconfig {
>>       uint8_t arm_sci_type;
>>       /* IN */
>>       uint8_t v8r_el1_msa;
>> -    uint16_t pad;
>> +    /* IN */
>> +    uint8_t arm_sci_agent_id;
>> +    uint8_t pad;
>>   };
>>   #endif /* __XEN__ || __XEN_TOOLS__ */

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

* Re: [PATCH v13 4/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver
  2026-09-23  8:25     ` Oleksii Moisieiev
@ 2026-09-24  1:15       ` Volodymyr Babchuk
  2026-09-24  7:54         ` Oleksii Moisieiev
  0 siblings, 1 reply; 14+ messages in thread
From: Volodymyr Babchuk @ 2026-09-24  1:15 UTC (permalink / raw)
  To: Oleksii Moisieiev
  Cc: xen-devel@lists.xenproject.org, Andrew Cooper, Anthony PERARD,
	Bertrand Marquis, Jan Beulich, Juergen Gross, Julien Grall,
	Michal Orzel, Roger Pau Monné, Stefano Stabellini,
	Grygorii Strashko

Hi Oleksii,

Oleksii Moisieiev <Oleksii_Moisieiev@epam.com> writes:

> On 23/09/2026 04:02, Volodymyr Babchuk wrote:
>> Hi Oleksii,
>>
>> Oleksii Moisieiev <Oleksii_Moisieiev@epam.com> writes:
>>
>> This is a great piece of documentation. It is larger than the text you
>> added to the 'doc' directory. So I believe it is better to put into a
>> design document, so it is will not be lost in the git commit messages.
>
> Hi Volodymyr,
>
> Thank you for a quick response.
>
> I've just recheked document in patch 6: 
> docs/hypervisor-guide/arm/firmware/arm-scmi.rst and
>
> compared it with the information provided in the commit description.
>
> As I can see all information provided in the description (add schemes 
> and dts examples at least)
>
> are present in the documentation. Also doc itself is more detailed then 
> the commit description.
>
> Maybe I missed something?

Ah, so you put this information in the last patch in the series. Okay,
so I missed it.

>>> This patch introduces SCI driver to support for ARM EL3 Trusted Firmware-A
>>> (TF-A) which provides SCMI interface with multi-agent support, as shown
>>> below.
>>>
>>>    +-----------------------------------------+
>>>    |                                         |
>>>    | EL3 TF-A SCMI                           |
>>>    +-------+--+-------+--+-------+--+-------++
>>>    |shmem1 |  |shmem0 |  |shmem2 |  |shmemX |
>>>    +-----+-+  +---+---+  +--+----+  +---+---+
>>> smc-id1 |        |         |           |
>>> agent1  |        |         |           |
>>>    +-----v--------+---------+-----------+----+
>>>    |              |         |           |    |
>>>    |              |         |           |    |
>>>    +--------------+---------+-----------+----+
>>>           smc-id0 |  smc-id2|    smc-idX|
>>>           agent0  |  agent2 |    agentX |
>>>                   |         |           |
>>>              +----v---+  +--v-----+  +--v-----+
>>>              |        |  |        |  |        |
>>>              | Dom0   |  | Dom1   |  | DomX   |
>>>              |        |  |        |  |        |
>>>              |        |  |        |  |        |
>>>              +--------+  +--------+  +--------+
>>>
>>> The EL3 SCMI multi-agent firmware is expected to provide SCMI SMC shared
>>> memory transport for every Agent in the system.
>>>
>>> The SCMI Agent transport channel defined by pair:
>>>   - smc-id: SMC id used for Doorbell
>>>   - shmem: shared memory for messages transfer, Xen page
>>>   aligned. Shared memory is mapped with the following flags:
>>>   MT_DEVICE_nGnRE.
>>>
>>> The follwoing SCMI Agents are expected to be defined by SCMI FW to enable SCMI
>>> multi-agent functionality under Xen:
>>> - Xen management agent: trusted agents that accesses to the Base Protocol
>>> commands to configure agent specific permissions
>>> - OSPM VM agents: non-trusted agent, one for each Guest domain which is
>>>    allowed direct HW access. At least one OSPM VM agent has to be provided
>>>    by FW if HW is handled only by Dom0 or Driver Domain.
>>>
>>> The EL3 SCMI FW is expected to implement following Base protocol messages:
>>> - BASE_DISCOVER_AGENT (optional if agent_id was provided)
>>> - BASE_RESET_AGENT_CONFIGURATION (optional)
>>> - BASE_SET_DEVICE_PERMISSIONS (optional)
>>>
>>> The SCI SCMI SMC multi-agent driver implements following
>>> functionality:
>>> - The driver is initialized from the Xen SCMI container ``xen_scmi_config``
>>>    (compatible ``xen,sci``) placed under ``/chosen/xen``. Only the
>>>    ``arm,scmi-smc`` node that is a child of this container will bind to Xen;
>>>    other SCMI nodes (for example under ``/firmware``) are ignored to avoid
>>>    stealing the host OSPM instance.
>>>
>>> scmi_shm_1: sram@47ff1000 {
>>>            compatible = "arm,scmi-shmem";
>>>            reg = <0x0 0x47ff1000 0x0 0x1000>;
>>> };
>>> scmi_xen: scmi {
>>>          compatible = "arm,scmi-smc";
>>>          arm,smc-id = <0x82000003>; <--- Xen management agent smc-id
>>>          #address-cells = < 1>;
>>>          #size-cells = < 0>;
>>>          #access-controller-cells = < 1>;
>>>          shmem = <&scmi_shm_1>; <--- Xen management agent shmem
>>> };
>>>
>>> - The driver obtains Xen specific SCMI Agent's configuration from the
>>>    Host DT, probes Agents and builds SCMI Agents list. The Agents
>>>    configuration is taken from "scmi-secondary-agents" property where
>>>    first item is "arm,smc-id", second - "arm,scmi-shmem" phandle and
>>>    third is optional "agent_id":
>>>
>>> / {
>>>    chosen {
>>>      xen {
>>>        ranges;
>>>        xen_scmi_config {
>>>          compatible = "xen,sci";
>>>          #address-cells = <2>;
>>>          #size-cells = <2>;
>>>          ranges;
>>>
>>> 	scmi-secondary-agents = <
>>>            0x82000002 &scmi_shm_0 0
>>>            0x82000004 &scmi_shm_2 2
>>>            0x82000005 &scmi_shm_3 3>; <--- func_id, shmem, agent_id
>>>          #scmi-secondary-agents-cells = <3>;
>> I don't think that this is the correct way of using -cells property.
>>
>> These properties are used either to provide information for **child** nodes
>> (like #address-cells or #size-cells) or to provide a number of cells to
>> encode a specifier for a domain (like #interrupt-cells).
>>
>> Here you are doing neither of these. I think the proper way is to define
>> secondary agents as children:
>>
>> agents {
>>          #address-cells = 1;
>>          #size-cells = 0;
>>           scmi_agent_2: scmi_agent@2 {
>>             compatible = "arm,scmi-agent";  // Actually, I am not sure if
>>                                             // we allowed to use 'arm' namespace
>>
>>             reg = <2>;                      // Agent id goes here
>>             shmmem = &scmi_shm_2;
>>             arm,smc-id = 0x82000004;
>>           };
>> }
>>
>> With this approach you don't need #scmi-secondary-agents-cells at all
>> and the whole structure becomes more device-tree-ish.
> Thank you for looking at this. I agree that the two cases you list are
> the most common ones, but they are not the only sanctioned ones: there is a
> third, long-standing pattern where a "#<name>-cells" property in a node
> declares the cell stride of a *non-phandle list property in that very same
> node*. That is exactly what "#scmi-secondary-agents-cells" does for
> "scmi-secondary-agents". Some evidence from the Devicetree Specification and
> from Linux:
>
> 1) "ranges" and "interrupt-map" are parsed with the cell counts of the node
>     that carries them, not of a child or of a phandle target.
>
>     dtc enforces this itself:
>
>     - scripts/dtc/checks.c:785 check_ranges_format() computes the entry 
> length
>       as (parent #address-cells + this node's #address-cells + this node's
>       #size-cells) and validates the length of "ranges" in this node 
> against it.
>
>     - scripts/dtc/checks.c:1601 check_interrupt_map():
>
>           cellsize = node_addr_cells(node);
>           cellsize += propval_cell(get_property(node, "#interrupt-cells"));
>
>       i.e. the stride of the "interrupt-map" entries in a node is taken from
>       "#address-cells" and "#interrupt-cells" *of that same node*. Linux 
> does
>       the same in drivers/of/irq.c.
>
>     So "a #*-cells property in node X describing the layout of a property in
>     node X" is not an abuse of the convention, it is how two of the core
>     Devicetree Specification properties are defined.
>
> 2) There is a precedent whose shape is identical to ours - a vendor property
>     holding a list, plus cells properties in the same node that describe 
> how to
>     decode it:
>
>     Documentation/devicetree/bindings/tpm/ibm,vtpm.yaml:36
>
>         ibm,#dma-address-cells:
>           description:
>             number of cells that are used to encode the physical address 
> field
>             of dma-window properties
>         ibm,#dma-size-cells:
>           description:
>             number of cells that are used to encode the size field of
>             dma-window properties
>         ibm,my-dma-window:
>           description: DMA window associated with this virtual I/O Adapter
>
>     and the example (same file, lines 96-98):
>
>         ibm,#dma-address-cells = <0x2>;
>         ibm,#dma-size-cells = <0x2>;
>         ibm,my-dma-window = <0x10000003 0x0 0x0 0x0 0x10000000>;
>
>     Both cells properties are "required". The parser is
>     arch/powerpc/kernel/prom_parse.c:11 of_parse_dma_window(), which reads
>     "ibm,#dma-address-cells" and "ibm,#dma-size-cells" from the same 
> node "dn"
>     to walk "ibm,my-dma-window". There is no phandle and no child node
>     involved; this is exactly the "self-describing list property" pattern.
>
> 3) A "#*-cells" property does not have to describe a phandle specifier 
> at all:
>
>     - "#pinctrl-cells" (Documentation/devicetree/bindings/pinctrl/
>       pinctrl-single.yaml:55) gives the number of cells per entry of the 
> plain
>       "pinctrl-single,pins" property; drivers/pinctrl/devicetree.c:297
>       pinctrl_find_cells_size() looks it up in the parent/grandparent node.
>       No phandle specifier is involved.
>
>     - "#index-cells" 
> (Documentation/devicetree/bindings/usb/fsl,usbmisc.yaml:56)
>       is not a specifier for any domain either.
>
> So the convention in practice is "a #<foo>-cells property states how 
> many cells
> one <foo> entry occupies", and the entries may live in a child node's 
> property
> (#address-cells), in a consumer's phandle specifier (#interrupt-cells,
> #clock-cells), or in a property of the declaring node itself (ranges,
> interrupt-map, ibm,my-dma-window). Our usage falls into the third group.
>
> This bring us to the following conclusion that 
> #scmi-secondary-agents-cells usage
> doesn't technically break the device-tree convention with 2 exceptions:
>
> a) Vendor prefix. Documentation/devicetree/bindings/writing-bindings.rst:68
>     says "DO use a vendor prefix on device-specific property names", and all
>     the same-node precedents above are vendor-prefixed 
> ("ibm,#dma-address-cells").
>     Both of our properties are Xen-specific and live under /chosen/xen, 
> so if we
>     keep the flat form they should become "xen,scmi-secondary-agents" and
>     "xen,#scmi-secondary-agents-cells".
>
> b) Entry layout. In our list the phandle is the second cell
>     (<smc-id &shmem agent-id>), which prevents the use of the standard
>     phandle-array helpers - they all expect the phandle first. If we 
> keep the
>     flat form, reordering to <&shmem smc-id agent-id> would let the 
> property be
>     parsed as an ordinary phandle-array.
>
> As for your proposal: the child-node form you suggest is certainly
> more idiomatic, it makes the agent id an addressable "reg" and it 
> removes the
> optional 2-vs-3-cells variant altogether. My only reservations are that 
> it grows
> the Xen DT parser (node iteration instead of a single property walk) and 
> that the
> compatible string would need a namespace we own ("xen,scmi-agent" rather 
> than
> "arm,scmi-agent", as you correctly suspected).
>
> So I do not think the current form is invalid, but I have no objection to
> moving to child nodes if you prefer it. Please let me know which way you 
> would
> like me to go for v14 and I will respin accordingly.

Yes, I think more idiomatic format is better. Also, you need to write a
parser once, but users will write device tree more often.

The second thing that bothers me is the shared memory nodes inside the
xen_scmi_config node. I believe they should belong to reserved_memory, no?


>>>          xen,dom0-sci-agent-id = <0>;
>>>
>>>          scmi_shm_0: sram@47ff0000 {
>>>            compatible = "arm,scmi-shmem";
>>>            reg = <0x0 0x47ff0000 0x0 0x1000>;
>>>          };
>>>

[...]

-- 
WBR, Volodymyr

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

* Re: [PATCH v13 4/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver
  2026-09-24  1:15       ` Volodymyr Babchuk
@ 2026-09-24  7:54         ` Oleksii Moisieiev
  2026-09-24 18:42           ` Volodymyr Babchuk
  0 siblings, 1 reply; 14+ messages in thread
From: Oleksii Moisieiev @ 2026-09-24  7:54 UTC (permalink / raw)
  To: Volodymyr Babchuk
  Cc: xen-devel@lists.xenproject.org, Andrew Cooper, Anthony PERARD,
	Bertrand Marquis, Jan Beulich, Juergen Gross, Julien Grall,
	Michal Orzel, Roger Pau Monné, Stefano Stabellini,
	Grygorii Strashko


On 24/09/2026 04:15, Volodymyr Babchuk wrote:
> Hi Oleksii,
>
> Oleksii Moisieiev <Oleksii_Moisieiev@epam.com> writes:
>
>> On 23/09/2026 04:02, Volodymyr Babchuk wrote:
>>> Hi Oleksii,
>>>
>>> Oleksii Moisieiev <Oleksii_Moisieiev@epam.com> writes:
>>>
>>> This is a great piece of documentation. It is larger than the text you
>>> added to the 'doc' directory. So I believe it is better to put into a
>>> design document, so it is will not be lost in the git commit messages.
>> Hi Volodymyr,
>>
>> Thank you for a quick response.
>>
>> I've just recheked document in patch 6:
>> docs/hypervisor-guide/arm/firmware/arm-scmi.rst and
>>
>> compared it with the information provided in the commit description.
>>
>> As I can see all information provided in the description (add schemes
>> and dts examples at least)
>>
>> are present in the documentation. Also doc itself is more detailed then
>> the commit description.
>>
>> Maybe I missed something?
> Ah, so you put this information in the last patch in the series. Okay,
> so I missed it.
>
>>>> This patch introduces SCI driver to support for ARM EL3 Trusted Firmware-A
>>>> (TF-A) which provides SCMI interface with multi-agent support, as shown
>>>> below.
>>>>
>>>>     +-----------------------------------------+
>>>>     |                                         |
>>>>     | EL3 TF-A SCMI                           |
>>>>     +-------+--+-------+--+-------+--+-------++
>>>>     |shmem1 |  |shmem0 |  |shmem2 |  |shmemX |
>>>>     +-----+-+  +---+---+  +--+----+  +---+---+
>>>> smc-id1 |        |         |           |
>>>> agent1  |        |         |           |
>>>>     +-----v--------+---------+-----------+----+
>>>>     |              |         |           |    |
>>>>     |              |         |           |    |
>>>>     +--------------+---------+-----------+----+
>>>>            smc-id0 |  smc-id2|    smc-idX|
>>>>            agent0  |  agent2 |    agentX |
>>>>                    |         |           |
>>>>               +----v---+  +--v-----+  +--v-----+
>>>>               |        |  |        |  |        |
>>>>               | Dom0   |  | Dom1   |  | DomX   |
>>>>               |        |  |        |  |        |
>>>>               |        |  |        |  |        |
>>>>               +--------+  +--------+  +--------+
>>>>
>>>> The EL3 SCMI multi-agent firmware is expected to provide SCMI SMC shared
>>>> memory transport for every Agent in the system.
>>>>
>>>> The SCMI Agent transport channel defined by pair:
>>>>    - smc-id: SMC id used for Doorbell
>>>>    - shmem: shared memory for messages transfer, Xen page
>>>>    aligned. Shared memory is mapped with the following flags:
>>>>    MT_DEVICE_nGnRE.
>>>>
>>>> The follwoing SCMI Agents are expected to be defined by SCMI FW to enable SCMI
>>>> multi-agent functionality under Xen:
>>>> - Xen management agent: trusted agents that accesses to the Base Protocol
>>>> commands to configure agent specific permissions
>>>> - OSPM VM agents: non-trusted agent, one for each Guest domain which is
>>>>     allowed direct HW access. At least one OSPM VM agent has to be provided
>>>>     by FW if HW is handled only by Dom0 or Driver Domain.
>>>>
>>>> The EL3 SCMI FW is expected to implement following Base protocol messages:
>>>> - BASE_DISCOVER_AGENT (optional if agent_id was provided)
>>>> - BASE_RESET_AGENT_CONFIGURATION (optional)
>>>> - BASE_SET_DEVICE_PERMISSIONS (optional)
>>>>
>>>> The SCI SCMI SMC multi-agent driver implements following
>>>> functionality:
>>>> - The driver is initialized from the Xen SCMI container ``xen_scmi_config``
>>>>     (compatible ``xen,sci``) placed under ``/chosen/xen``. Only the
>>>>     ``arm,scmi-smc`` node that is a child of this container will bind to Xen;
>>>>     other SCMI nodes (for example under ``/firmware``) are ignored to avoid
>>>>     stealing the host OSPM instance.
>>>>
>>>> scmi_shm_1: sram@47ff1000 {
>>>>             compatible = "arm,scmi-shmem";
>>>>             reg = <0x0 0x47ff1000 0x0 0x1000>;
>>>> };
>>>> scmi_xen: scmi {
>>>>           compatible = "arm,scmi-smc";
>>>>           arm,smc-id = <0x82000003>; <--- Xen management agent smc-id
>>>>           #address-cells = < 1>;
>>>>           #size-cells = < 0>;
>>>>           #access-controller-cells = < 1>;
>>>>           shmem = <&scmi_shm_1>; <--- Xen management agent shmem
>>>> };
>>>>
>>>> - The driver obtains Xen specific SCMI Agent's configuration from the
>>>>     Host DT, probes Agents and builds SCMI Agents list. The Agents
>>>>     configuration is taken from "scmi-secondary-agents" property where
>>>>     first item is "arm,smc-id", second - "arm,scmi-shmem" phandle and
>>>>     third is optional "agent_id":
>>>>
>>>> / {
>>>>     chosen {
>>>>       xen {
>>>>         ranges;
>>>>         xen_scmi_config {
>>>>           compatible = "xen,sci";
>>>>           #address-cells = <2>;
>>>>           #size-cells = <2>;
>>>>           ranges;
>>>>
>>>> 	scmi-secondary-agents = <
>>>>             0x82000002 &scmi_shm_0 0
>>>>             0x82000004 &scmi_shm_2 2
>>>>             0x82000005 &scmi_shm_3 3>; <--- func_id, shmem, agent_id
>>>>           #scmi-secondary-agents-cells = <3>;
>>> I don't think that this is the correct way of using -cells property.
>>>
>>> These properties are used either to provide information for **child** nodes
>>> (like #address-cells or #size-cells) or to provide a number of cells to
>>> encode a specifier for a domain (like #interrupt-cells).
>>>
>>> Here you are doing neither of these. I think the proper way is to define
>>> secondary agents as children:
>>>
>>> agents {
>>>           #address-cells = 1;
>>>           #size-cells = 0;
>>>            scmi_agent_2: scmi_agent@2 {
>>>              compatible = "arm,scmi-agent";  // Actually, I am not sure if
>>>                                              // we allowed to use 'arm' namespace
>>>
>>>              reg = <2>;                      // Agent id goes here
>>>              shmmem = &scmi_shm_2;
>>>              arm,smc-id = 0x82000004;
>>>            };
>>> }
>>>
>>> With this approach you don't need #scmi-secondary-agents-cells at all
>>> and the whole structure becomes more device-tree-ish.
>> Thank you for looking at this. I agree that the two cases you list are
>> the most common ones, but they are not the only sanctioned ones: there is a
>> third, long-standing pattern where a "#<name>-cells" property in a node
>> declares the cell stride of a *non-phandle list property in that very same
>> node*. That is exactly what "#scmi-secondary-agents-cells" does for
>> "scmi-secondary-agents". Some evidence from the Devicetree Specification and
>> from Linux:
>>
>> 1) "ranges" and "interrupt-map" are parsed with the cell counts of the node
>>      that carries them, not of a child or of a phandle target.
>>
>>      dtc enforces this itself:
>>
>>      - scripts/dtc/checks.c:785 check_ranges_format() computes the entry
>> length
>>        as (parent #address-cells + this node's #address-cells + this node's
>>        #size-cells) and validates the length of "ranges" in this node
>> against it.
>>
>>      - scripts/dtc/checks.c:1601 check_interrupt_map():
>>
>>            cellsize = node_addr_cells(node);
>>            cellsize += propval_cell(get_property(node, "#interrupt-cells"));
>>
>>        i.e. the stride of the "interrupt-map" entries in a node is taken from
>>        "#address-cells" and "#interrupt-cells" *of that same node*. Linux
>> does
>>        the same in drivers/of/irq.c.
>>
>>      So "a #*-cells property in node X describing the layout of a property in
>>      node X" is not an abuse of the convention, it is how two of the core
>>      Devicetree Specification properties are defined.
>>
>> 2) There is a precedent whose shape is identical to ours - a vendor property
>>      holding a list, plus cells properties in the same node that describe
>> how to
>>      decode it:
>>
>>      Documentation/devicetree/bindings/tpm/ibm,vtpm.yaml:36
>>
>>          ibm,#dma-address-cells:
>>            description:
>>              number of cells that are used to encode the physical address
>> field
>>              of dma-window properties
>>          ibm,#dma-size-cells:
>>            description:
>>              number of cells that are used to encode the size field of
>>              dma-window properties
>>          ibm,my-dma-window:
>>            description: DMA window associated with this virtual I/O Adapter
>>
>>      and the example (same file, lines 96-98):
>>
>>          ibm,#dma-address-cells = <0x2>;
>>          ibm,#dma-size-cells = <0x2>;
>>          ibm,my-dma-window = <0x10000003 0x0 0x0 0x0 0x10000000>;
>>
>>      Both cells properties are "required". The parser is
>>      arch/powerpc/kernel/prom_parse.c:11 of_parse_dma_window(), which reads
>>      "ibm,#dma-address-cells" and "ibm,#dma-size-cells" from the same
>> node "dn"
>>      to walk "ibm,my-dma-window". There is no phandle and no child node
>>      involved; this is exactly the "self-describing list property" pattern.
>>
>> 3) A "#*-cells" property does not have to describe a phandle specifier
>> at all:
>>
>>      - "#pinctrl-cells" (Documentation/devicetree/bindings/pinctrl/
>>        pinctrl-single.yaml:55) gives the number of cells per entry of the
>> plain
>>        "pinctrl-single,pins" property; drivers/pinctrl/devicetree.c:297
>>        pinctrl_find_cells_size() looks it up in the parent/grandparent node.
>>        No phandle specifier is involved.
>>
>>      - "#index-cells"
>> (Documentation/devicetree/bindings/usb/fsl,usbmisc.yaml:56)
>>        is not a specifier for any domain either.
>>
>> So the convention in practice is "a #<foo>-cells property states how
>> many cells
>> one <foo> entry occupies", and the entries may live in a child node's
>> property
>> (#address-cells), in a consumer's phandle specifier (#interrupt-cells,
>> #clock-cells), or in a property of the declaring node itself (ranges,
>> interrupt-map, ibm,my-dma-window). Our usage falls into the third group.
>>
>> This bring us to the following conclusion that
>> #scmi-secondary-agents-cells usage
>> doesn't technically break the device-tree convention with 2 exceptions:
>>
>> a) Vendor prefix. Documentation/devicetree/bindings/writing-bindings.rst:68
>>      says "DO use a vendor prefix on device-specific property names", and all
>>      the same-node precedents above are vendor-prefixed
>> ("ibm,#dma-address-cells").
>>      Both of our properties are Xen-specific and live under /chosen/xen,
>> so if we
>>      keep the flat form they should become "xen,scmi-secondary-agents" and
>>      "xen,#scmi-secondary-agents-cells".
>>
>> b) Entry layout. In our list the phandle is the second cell
>>      (<smc-id &shmem agent-id>), which prevents the use of the standard
>>      phandle-array helpers - they all expect the phandle first. If we
>> keep the
>>      flat form, reordering to <&shmem smc-id agent-id> would let the
>> property be
>>      parsed as an ordinary phandle-array.
>>
>> As for your proposal: the child-node form you suggest is certainly
>> more idiomatic, it makes the agent id an addressable "reg" and it
>> removes the
>> optional 2-vs-3-cells variant altogether. My only reservations are that
>> it grows
>> the Xen DT parser (node iteration instead of a single property walk) and
>> that the
>> compatible string would need a namespace we own ("xen,scmi-agent" rather
>> than
>> "arm,scmi-agent", as you correctly suspected).
>>
>> So I do not think the current form is invalid, but I have no objection to
>> moving to child nodes if you prefer it. Please let me know which way you
>> would
>> like me to go for v14 and I will respin accordingly.
> Yes, I think more idiomatic format is better. Also, you need to write a
> parser once, but users will write device tree more often.
>
> The second thing that bothers me is the shared memory nodes inside the
> xen_scmi_config node. I believe they should belong to reserved_memory, no?
During discussions on the previous versions, it was decided that the 
main goal is to leave the original device-tree untouched and put all 
Xen-related content into the xen node. That's why we preserve the 
original DTS structure, such as the scmi_shm_0 and /firmware/scmi nodes.

Also, if you take a look at the arm,scmi.yaml file in the Linux kernel, 
there is no explicit requirement regarding the exact placement of the 
scmi-shmem node. As can be seen, arm,scmi-shmem is present in the 
patternProperties of sram.yaml, but some DTS files place it into 
reserved-memory instead.

My main point is that we don't touch the original device-tree — we 
simply add the xen node and put all the required properties inside it. 
If we were to place shmem into sram or reserved-memory, it would give us 
the following structure:

/ {

      xen {

          <sram|reserved-memory>: {

              scmi_shm0 ...

             ....

             scmi_shmX ...

          }

      }

}

This wouldn't provide any benefit but would overcomplicate the 
device-tree structure. What do you think?
>
>>>>           xen,dom0-sci-agent-id = <0>;
>>>>
>>>>           scmi_shm_0: sram@47ff0000 {
>>>>             compatible = "arm,scmi-shmem";
>>>>             reg = <0x0 0x47ff0000 0x0 0x1000>;
>>>>           };
>>>>
> [...]
>

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

* Re: [PATCH v13 4/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver
  2026-09-24  7:54         ` Oleksii Moisieiev
@ 2026-09-24 18:42           ` Volodymyr Babchuk
  2026-09-25 10:46             ` Oleksii Moisieiev
  0 siblings, 1 reply; 14+ messages in thread
From: Volodymyr Babchuk @ 2026-09-24 18:42 UTC (permalink / raw)
  To: Oleksii Moisieiev
  Cc: xen-devel@lists.xenproject.org, Andrew Cooper, Anthony PERARD,
	Bertrand Marquis, Jan Beulich, Juergen Gross, Julien Grall,
	Michal Orzel, Roger Pau Monné, Stefano Stabellini,
	Grygorii Strashko

Hi Oleksii,

Oleksii Moisieiev <Oleksii_Moisieiev@epam.com> writes:

> On 24/09/2026 04:15, Volodymyr Babchuk wrote:
>> Hi Oleksii,
>>
>> Oleksii Moisieiev <Oleksii_Moisieiev@epam.com> writes:
>>
>>> On 23/09/2026 04:02, Volodymyr Babchuk wrote:
>>>> Hi Oleksii,
>>>>
>>>> Oleksii Moisieiev <Oleksii_Moisieiev@epam.com> writes:
>>>>
>>>> This is a great piece of documentation. It is larger than the text you
>>>> added to the 'doc' directory. So I believe it is better to put into a
>>>> design document, so it is will not be lost in the git commit messages.
>>> Hi Volodymyr,
>>>
>>> Thank you for a quick response.
>>>
>>> I've just recheked document in patch 6:
>>> docs/hypervisor-guide/arm/firmware/arm-scmi.rst and
>>>
>>> compared it with the information provided in the commit description.
>>>
>>> As I can see all information provided in the description (add schemes
>>> and dts examples at least)
>>>
>>> are present in the documentation. Also doc itself is more detailed then
>>> the commit description.
>>>
>>> Maybe I missed something?
>> Ah, so you put this information in the last patch in the series. Okay,
>> so I missed it.
>>
>>>>> This patch introduces SCI driver to support for ARM EL3 Trusted Firmware-A
>>>>> (TF-A) which provides SCMI interface with multi-agent support, as shown
>>>>> below.
>>>>>
>>>>>     +-----------------------------------------+
>>>>>     |                                         |
>>>>>     | EL3 TF-A SCMI                           |
>>>>>     +-------+--+-------+--+-------+--+-------++
>>>>>     |shmem1 |  |shmem0 |  |shmem2 |  |shmemX |
>>>>>     +-----+-+  +---+---+  +--+----+  +---+---+
>>>>> smc-id1 |        |         |           |
>>>>> agent1  |        |         |           |
>>>>>     +-----v--------+---------+-----------+----+
>>>>>     |              |         |           |    |
>>>>>     |              |         |           |    |
>>>>>     +--------------+---------+-----------+----+
>>>>>            smc-id0 |  smc-id2|    smc-idX|
>>>>>            agent0  |  agent2 |    agentX |
>>>>>                    |         |           |
>>>>>               +----v---+  +--v-----+  +--v-----+
>>>>>               |        |  |        |  |        |
>>>>>               | Dom0   |  | Dom1   |  | DomX   |
>>>>>               |        |  |        |  |        |
>>>>>               |        |  |        |  |        |
>>>>>               +--------+  +--------+  +--------+
>>>>>
>>>>> The EL3 SCMI multi-agent firmware is expected to provide SCMI SMC shared
>>>>> memory transport for every Agent in the system.
>>>>>
>>>>> The SCMI Agent transport channel defined by pair:
>>>>>    - smc-id: SMC id used for Doorbell
>>>>>    - shmem: shared memory for messages transfer, Xen page
>>>>>    aligned. Shared memory is mapped with the following flags:
>>>>>    MT_DEVICE_nGnRE.
>>>>>
>>>>> The follwoing SCMI Agents are expected to be defined by SCMI FW to enable SCMI
>>>>> multi-agent functionality under Xen:
>>>>> - Xen management agent: trusted agents that accesses to the Base Protocol
>>>>> commands to configure agent specific permissions
>>>>> - OSPM VM agents: non-trusted agent, one for each Guest domain which is
>>>>>     allowed direct HW access. At least one OSPM VM agent has to be provided
>>>>>     by FW if HW is handled only by Dom0 or Driver Domain.
>>>>>
>>>>> The EL3 SCMI FW is expected to implement following Base protocol messages:
>>>>> - BASE_DISCOVER_AGENT (optional if agent_id was provided)
>>>>> - BASE_RESET_AGENT_CONFIGURATION (optional)
>>>>> - BASE_SET_DEVICE_PERMISSIONS (optional)
>>>>>
>>>>> The SCI SCMI SMC multi-agent driver implements following
>>>>> functionality:
>>>>> - The driver is initialized from the Xen SCMI container ``xen_scmi_config``
>>>>>     (compatible ``xen,sci``) placed under ``/chosen/xen``. Only the
>>>>>     ``arm,scmi-smc`` node that is a child of this container will bind to Xen;
>>>>>     other SCMI nodes (for example under ``/firmware``) are ignored to avoid
>>>>>     stealing the host OSPM instance.
>>>>>
>>>>> scmi_shm_1: sram@47ff1000 {
>>>>>             compatible = "arm,scmi-shmem";
>>>>>             reg = <0x0 0x47ff1000 0x0 0x1000>;
>>>>> };
>>>>> scmi_xen: scmi {
>>>>>           compatible = "arm,scmi-smc";
>>>>>           arm,smc-id = <0x82000003>; <--- Xen management agent smc-id
>>>>>           #address-cells = < 1>;
>>>>>           #size-cells = < 0>;
>>>>>           #access-controller-cells = < 1>;
>>>>>           shmem = <&scmi_shm_1>; <--- Xen management agent shmem
>>>>> };
>>>>>
>>>>> - The driver obtains Xen specific SCMI Agent's configuration from the
>>>>>     Host DT, probes Agents and builds SCMI Agents list. The Agents
>>>>>     configuration is taken from "scmi-secondary-agents" property where
>>>>>     first item is "arm,smc-id", second - "arm,scmi-shmem" phandle and
>>>>>     third is optional "agent_id":
>>>>>
>>>>> / {
>>>>>     chosen {
>>>>>       xen {
>>>>>         ranges;
>>>>>         xen_scmi_config {
>>>>>           compatible = "xen,sci";
>>>>>           #address-cells = <2>;
>>>>>           #size-cells = <2>;
>>>>>           ranges;
>>>>>
>>>>> 	scmi-secondary-agents = <
>>>>>             0x82000002 &scmi_shm_0 0
>>>>>             0x82000004 &scmi_shm_2 2
>>>>>             0x82000005 &scmi_shm_3 3>; <--- func_id, shmem, agent_id
>>>>>           #scmi-secondary-agents-cells = <3>;
>>>> I don't think that this is the correct way of using -cells property.
>>>>
>>>> These properties are used either to provide information for **child** nodes
>>>> (like #address-cells or #size-cells) or to provide a number of cells to
>>>> encode a specifier for a domain (like #interrupt-cells).
>>>>
>>>> Here you are doing neither of these. I think the proper way is to define
>>>> secondary agents as children:
>>>>
>>>> agents {
>>>>           #address-cells = 1;
>>>>           #size-cells = 0;
>>>>            scmi_agent_2: scmi_agent@2 {
>>>>              compatible = "arm,scmi-agent";  // Actually, I am not sure if
>>>>                                              // we allowed to use 'arm' namespace
>>>>
>>>>              reg = <2>;                      // Agent id goes here
>>>>              shmmem = &scmi_shm_2;
>>>>              arm,smc-id = 0x82000004;
>>>>            };
>>>> }
>>>>
>>>> With this approach you don't need #scmi-secondary-agents-cells at all
>>>> and the whole structure becomes more device-tree-ish.
>>> Thank you for looking at this. I agree that the two cases you list are
>>> the most common ones, but they are not the only sanctioned ones: there is a
>>> third, long-standing pattern where a "#<name>-cells" property in a node
>>> declares the cell stride of a *non-phandle list property in that very same
>>> node*. That is exactly what "#scmi-secondary-agents-cells" does for
>>> "scmi-secondary-agents". Some evidence from the Devicetree Specification and
>>> from Linux:
>>>
>>> 1) "ranges" and "interrupt-map" are parsed with the cell counts of the node
>>>      that carries them, not of a child or of a phandle target.
>>>
>>>      dtc enforces this itself:
>>>
>>>      - scripts/dtc/checks.c:785 check_ranges_format() computes the entry
>>> length
>>>        as (parent #address-cells + this node's #address-cells + this node's
>>>        #size-cells) and validates the length of "ranges" in this node
>>> against it.
>>>
>>>      - scripts/dtc/checks.c:1601 check_interrupt_map():
>>>
>>>            cellsize = node_addr_cells(node);
>>>            cellsize += propval_cell(get_property(node, "#interrupt-cells"));
>>>
>>>        i.e. the stride of the "interrupt-map" entries in a node is taken from
>>>        "#address-cells" and "#interrupt-cells" *of that same node*. Linux
>>> does
>>>        the same in drivers/of/irq.c.
>>>
>>>      So "a #*-cells property in node X describing the layout of a property in
>>>      node X" is not an abuse of the convention, it is how two of the core
>>>      Devicetree Specification properties are defined.
>>>
>>> 2) There is a precedent whose shape is identical to ours - a vendor property
>>>      holding a list, plus cells properties in the same node that describe
>>> how to
>>>      decode it:
>>>
>>>      Documentation/devicetree/bindings/tpm/ibm,vtpm.yaml:36
>>>
>>>          ibm,#dma-address-cells:
>>>            description:
>>>              number of cells that are used to encode the physical address
>>> field
>>>              of dma-window properties
>>>          ibm,#dma-size-cells:
>>>            description:
>>>              number of cells that are used to encode the size field of
>>>              dma-window properties
>>>          ibm,my-dma-window:
>>>            description: DMA window associated with this virtual I/O Adapter
>>>
>>>      and the example (same file, lines 96-98):
>>>
>>>          ibm,#dma-address-cells = <0x2>;
>>>          ibm,#dma-size-cells = <0x2>;
>>>          ibm,my-dma-window = <0x10000003 0x0 0x0 0x0 0x10000000>;
>>>
>>>      Both cells properties are "required". The parser is
>>>      arch/powerpc/kernel/prom_parse.c:11 of_parse_dma_window(), which reads
>>>      "ibm,#dma-address-cells" and "ibm,#dma-size-cells" from the same
>>> node "dn"
>>>      to walk "ibm,my-dma-window". There is no phandle and no child node
>>>      involved; this is exactly the "self-describing list property" pattern.
>>>
>>> 3) A "#*-cells" property does not have to describe a phandle specifier
>>> at all:
>>>
>>>      - "#pinctrl-cells" (Documentation/devicetree/bindings/pinctrl/
>>>        pinctrl-single.yaml:55) gives the number of cells per entry of the
>>> plain
>>>        "pinctrl-single,pins" property; drivers/pinctrl/devicetree.c:297
>>>        pinctrl_find_cells_size() looks it up in the parent/grandparent node.
>>>        No phandle specifier is involved.
>>>
>>>      - "#index-cells"
>>> (Documentation/devicetree/bindings/usb/fsl,usbmisc.yaml:56)
>>>        is not a specifier for any domain either.
>>>
>>> So the convention in practice is "a #<foo>-cells property states how
>>> many cells
>>> one <foo> entry occupies", and the entries may live in a child node's
>>> property
>>> (#address-cells), in a consumer's phandle specifier (#interrupt-cells,
>>> #clock-cells), or in a property of the declaring node itself (ranges,
>>> interrupt-map, ibm,my-dma-window). Our usage falls into the third group.
>>>
>>> This bring us to the following conclusion that
>>> #scmi-secondary-agents-cells usage
>>> doesn't technically break the device-tree convention with 2 exceptions:
>>>
>>> a) Vendor prefix. Documentation/devicetree/bindings/writing-bindings.rst:68
>>>      says "DO use a vendor prefix on device-specific property names", and all
>>>      the same-node precedents above are vendor-prefixed
>>> ("ibm,#dma-address-cells").
>>>      Both of our properties are Xen-specific and live under /chosen/xen,
>>> so if we
>>>      keep the flat form they should become "xen,scmi-secondary-agents" and
>>>      "xen,#scmi-secondary-agents-cells".
>>>
>>> b) Entry layout. In our list the phandle is the second cell
>>>      (<smc-id &shmem agent-id>), which prevents the use of the standard
>>>      phandle-array helpers - they all expect the phandle first. If we
>>> keep the
>>>      flat form, reordering to <&shmem smc-id agent-id> would let the
>>> property be
>>>      parsed as an ordinary phandle-array.
>>>
>>> As for your proposal: the child-node form you suggest is certainly
>>> more idiomatic, it makes the agent id an addressable "reg" and it
>>> removes the
>>> optional 2-vs-3-cells variant altogether. My only reservations are that
>>> it grows
>>> the Xen DT parser (node iteration instead of a single property walk) and
>>> that the
>>> compatible string would need a namespace we own ("xen,scmi-agent" rather
>>> than
>>> "arm,scmi-agent", as you correctly suspected).
>>>
>>> So I do not think the current form is invalid, but I have no objection to
>>> moving to child nodes if you prefer it. Please let me know which way you
>>> would
>>> like me to go for v14 and I will respin accordingly.
>> Yes, I think more idiomatic format is better. Also, you need to write a
>> parser once, but users will write device tree more often.
>>
>> The second thing that bothers me is the shared memory nodes inside the
>> xen_scmi_config node. I believe they should belong to reserved_memory, no?
> During discussions on the previous versions, it was decided that the 
> main goal is to leave the original device-tree untouched and put all 
> Xen-related content into the xen node. That's why we preserve the 
> original DTS structure, such as the scmi_shm_0 and /firmware/scmi nodes.
>
> Also, if you take a look at the arm,scmi.yaml file in the Linux kernel, 
> there is no explicit requirement regarding the exact placement of the 
> scmi-shmem node. As can be seen, arm,scmi-shmem is present in the 
> patternProperties of sram.yaml, but some DTS files place it into 
> reserved-memory instead.
>
> My main point is that we don't touch the original device-tree — we 
> simply add the xen node and put all the required properties inside it.

Okay, but Xen should know about reserved memory, right? So, it is only
fair to have shared memory nodes in /reserved-memory/ as device tree
spec suggests.

> If we were to place shmem into sram or reserved-memory, it would give us 
> the following structure:
>
> / {
>
>       xen {
>
>           <sram|reserved-memory>: {
>
>               scmi_shm0 ...
>
>              ....
>
>              scmi_shmX ...
>
>           }
>
>       }
>
> }
>
> This wouldn't provide any benefit but would overcomplicate the 
> device-tree structure. What do you think?

Well, there is another argument why I want to move shared memory nodes
out of xen_scmi_config. In that case we can have more flat
structure. So, instead of

xen_scmi_config {
        #address-cells = 2;
        #size-cells = 2;

        shm1 {};
        shm2 {};
        shmN {};

        agents {
                #address-cells = 1;
                #size-cells = 0;
                agent1 {};
                agent2 {};
        }
}


we could have

xen_scmi_config {
        #address-cells = 1;
        #size-cells = 0;

        agent1 {};
        agent2 {};
}

which is more idiomatic, IMO.

-- 
WBR, Volodymyr

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

* Re: [PATCH v13 4/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver
  2026-09-24 18:42           ` Volodymyr Babchuk
@ 2026-09-25 10:46             ` Oleksii Moisieiev
  2026-09-25 17:57               ` Volodymyr Babchuk
  0 siblings, 1 reply; 14+ messages in thread
From: Oleksii Moisieiev @ 2026-09-25 10:46 UTC (permalink / raw)
  To: Volodymyr Babchuk
  Cc: xen-devel@lists.xenproject.org, Andrew Cooper, Anthony PERARD,
	Bertrand Marquis, Jan Beulich, Juergen Gross, Julien Grall,
	Michal Orzel, Roger Pau Monné, Stefano Stabellini,
	Grygorii Strashko


On 24/09/2026 21:42, Volodymyr Babchuk wrote:
> Hi Oleksii,
>
> Oleksii Moisieiev <Oleksii_Moisieiev@epam.com> writes:
>
>> On 24/09/2026 04:15, Volodymyr Babchuk wrote:
>>> Hi Oleksii,
>>>
>>> Oleksii Moisieiev <Oleksii_Moisieiev@epam.com> writes:
>>>
>>>> On 23/09/2026 04:02, Volodymyr Babchuk wrote:
>>>>> Hi Oleksii,
>>>>>
>>>>> Oleksii Moisieiev <Oleksii_Moisieiev@epam.com> writes:
>>>>>
>>>>> This is a great piece of documentation. It is larger than the text you
>>>>> added to the 'doc' directory. So I believe it is better to put into a
>>>>> design document, so it is will not be lost in the git commit messages.
>>>> Hi Volodymyr,
>>>>
>>>> Thank you for a quick response.
>>>>
>>>> I've just recheked document in patch 6:
>>>> docs/hypervisor-guide/arm/firmware/arm-scmi.rst and
>>>>
>>>> compared it with the information provided in the commit description.
>>>>
>>>> As I can see all information provided in the description (add schemes
>>>> and dts examples at least)
>>>>
>>>> are present in the documentation. Also doc itself is more detailed then
>>>> the commit description.
>>>>
>>>> Maybe I missed something?
>>> Ah, so you put this information in the last patch in the series. Okay,
>>> so I missed it.
>>>
>>>>>> This patch introduces SCI driver to support for ARM EL3 Trusted Firmware-A
>>>>>> (TF-A) which provides SCMI interface with multi-agent support, as shown
>>>>>> below.
>>>>>>
>>>>>>      +-----------------------------------------+
>>>>>>      |                                         |
>>>>>>      | EL3 TF-A SCMI                           |
>>>>>>      +-------+--+-------+--+-------+--+-------++
>>>>>>      |shmem1 |  |shmem0 |  |shmem2 |  |shmemX |
>>>>>>      +-----+-+  +---+---+  +--+----+  +---+---+
>>>>>> smc-id1 |        |         |           |
>>>>>> agent1  |        |         |           |
>>>>>>      +-----v--------+---------+-----------+----+
>>>>>>      |              |         |           |    |
>>>>>>      |              |         |           |    |
>>>>>>      +--------------+---------+-----------+----+
>>>>>>             smc-id0 |  smc-id2|    smc-idX|
>>>>>>             agent0  |  agent2 |    agentX |
>>>>>>                     |         |           |
>>>>>>                +----v---+  +--v-----+  +--v-----+
>>>>>>                |        |  |        |  |        |
>>>>>>                | Dom0   |  | Dom1   |  | DomX   |
>>>>>>                |        |  |        |  |        |
>>>>>>                |        |  |        |  |        |
>>>>>>                +--------+  +--------+  +--------+
>>>>>>
>>>>>> The EL3 SCMI multi-agent firmware is expected to provide SCMI SMC shared
>>>>>> memory transport for every Agent in the system.
>>>>>>
>>>>>> The SCMI Agent transport channel defined by pair:
>>>>>>     - smc-id: SMC id used for Doorbell
>>>>>>     - shmem: shared memory for messages transfer, Xen page
>>>>>>     aligned. Shared memory is mapped with the following flags:
>>>>>>     MT_DEVICE_nGnRE.
>>>>>>
>>>>>> The follwoing SCMI Agents are expected to be defined by SCMI FW to enable SCMI
>>>>>> multi-agent functionality under Xen:
>>>>>> - Xen management agent: trusted agents that accesses to the Base Protocol
>>>>>> commands to configure agent specific permissions
>>>>>> - OSPM VM agents: non-trusted agent, one for each Guest domain which is
>>>>>>      allowed direct HW access. At least one OSPM VM agent has to be provided
>>>>>>      by FW if HW is handled only by Dom0 or Driver Domain.
>>>>>>
>>>>>> The EL3 SCMI FW is expected to implement following Base protocol messages:
>>>>>> - BASE_DISCOVER_AGENT (optional if agent_id was provided)
>>>>>> - BASE_RESET_AGENT_CONFIGURATION (optional)
>>>>>> - BASE_SET_DEVICE_PERMISSIONS (optional)
>>>>>>
>>>>>> The SCI SCMI SMC multi-agent driver implements following
>>>>>> functionality:
>>>>>> - The driver is initialized from the Xen SCMI container ``xen_scmi_config``
>>>>>>      (compatible ``xen,sci``) placed under ``/chosen/xen``. Only the
>>>>>>      ``arm,scmi-smc`` node that is a child of this container will bind to Xen;
>>>>>>      other SCMI nodes (for example under ``/firmware``) are ignored to avoid
>>>>>>      stealing the host OSPM instance.
>>>>>>
>>>>>> scmi_shm_1: sram@47ff1000 {
>>>>>>              compatible = "arm,scmi-shmem";
>>>>>>              reg = <0x0 0x47ff1000 0x0 0x1000>;
>>>>>> };
>>>>>> scmi_xen: scmi {
>>>>>>            compatible = "arm,scmi-smc";
>>>>>>            arm,smc-id = <0x82000003>; <--- Xen management agent smc-id
>>>>>>            #address-cells = < 1>;
>>>>>>            #size-cells = < 0>;
>>>>>>            #access-controller-cells = < 1>;
>>>>>>            shmem = <&scmi_shm_1>; <--- Xen management agent shmem
>>>>>> };
>>>>>>
>>>>>> - The driver obtains Xen specific SCMI Agent's configuration from the
>>>>>>      Host DT, probes Agents and builds SCMI Agents list. The Agents
>>>>>>      configuration is taken from "scmi-secondary-agents" property where
>>>>>>      first item is "arm,smc-id", second - "arm,scmi-shmem" phandle and
>>>>>>      third is optional "agent_id":
>>>>>>
>>>>>> / {
>>>>>>      chosen {
>>>>>>        xen {
>>>>>>          ranges;
>>>>>>          xen_scmi_config {
>>>>>>            compatible = "xen,sci";
>>>>>>            #address-cells = <2>;
>>>>>>            #size-cells = <2>;
>>>>>>            ranges;
>>>>>>
>>>>>> 	scmi-secondary-agents = <
>>>>>>              0x82000002 &scmi_shm_0 0
>>>>>>              0x82000004 &scmi_shm_2 2
>>>>>>              0x82000005 &scmi_shm_3 3>; <--- func_id, shmem, agent_id
>>>>>>            #scmi-secondary-agents-cells = <3>;
>>>>> I don't think that this is the correct way of using -cells property.
>>>>>
>>>>> These properties are used either to provide information for **child** nodes
>>>>> (like #address-cells or #size-cells) or to provide a number of cells to
>>>>> encode a specifier for a domain (like #interrupt-cells).
>>>>>
>>>>> Here you are doing neither of these. I think the proper way is to define
>>>>> secondary agents as children:
>>>>>
>>>>> agents {
>>>>>            #address-cells = 1;
>>>>>            #size-cells = 0;
>>>>>             scmi_agent_2: scmi_agent@2 {
>>>>>               compatible = "arm,scmi-agent";  // Actually, I am not sure if
>>>>>                                               // we allowed to use 'arm' namespace
>>>>>
>>>>>               reg = <2>;                      // Agent id goes here
>>>>>               shmmem = &scmi_shm_2;
>>>>>               arm,smc-id = 0x82000004;
>>>>>             };
>>>>> }
>>>>>
>>>>> With this approach you don't need #scmi-secondary-agents-cells at all
>>>>> and the whole structure becomes more device-tree-ish.
>>>> Thank you for looking at this. I agree that the two cases you list are
>>>> the most common ones, but they are not the only sanctioned ones: there is a
>>>> third, long-standing pattern where a "#<name>-cells" property in a node
>>>> declares the cell stride of a *non-phandle list property in that very same
>>>> node*. That is exactly what "#scmi-secondary-agents-cells" does for
>>>> "scmi-secondary-agents". Some evidence from the Devicetree Specification and
>>>> from Linux:
>>>>
>>>> 1) "ranges" and "interrupt-map" are parsed with the cell counts of the node
>>>>       that carries them, not of a child or of a phandle target.
>>>>
>>>>       dtc enforces this itself:
>>>>
>>>>       - scripts/dtc/checks.c:785 check_ranges_format() computes the entry
>>>> length
>>>>         as (parent #address-cells + this node's #address-cells + this node's
>>>>         #size-cells) and validates the length of "ranges" in this node
>>>> against it.
>>>>
>>>>       - scripts/dtc/checks.c:1601 check_interrupt_map():
>>>>
>>>>             cellsize = node_addr_cells(node);
>>>>             cellsize += propval_cell(get_property(node, "#interrupt-cells"));
>>>>
>>>>         i.e. the stride of the "interrupt-map" entries in a node is taken from
>>>>         "#address-cells" and "#interrupt-cells" *of that same node*. Linux
>>>> does
>>>>         the same in drivers/of/irq.c.
>>>>
>>>>       So "a #*-cells property in node X describing the layout of a property in
>>>>       node X" is not an abuse of the convention, it is how two of the core
>>>>       Devicetree Specification properties are defined.
>>>>
>>>> 2) There is a precedent whose shape is identical to ours - a vendor property
>>>>       holding a list, plus cells properties in the same node that describe
>>>> how to
>>>>       decode it:
>>>>
>>>>       Documentation/devicetree/bindings/tpm/ibm,vtpm.yaml:36
>>>>
>>>>           ibm,#dma-address-cells:
>>>>             description:
>>>>               number of cells that are used to encode the physical address
>>>> field
>>>>               of dma-window properties
>>>>           ibm,#dma-size-cells:
>>>>             description:
>>>>               number of cells that are used to encode the size field of
>>>>               dma-window properties
>>>>           ibm,my-dma-window:
>>>>             description: DMA window associated with this virtual I/O Adapter
>>>>
>>>>       and the example (same file, lines 96-98):
>>>>
>>>>           ibm,#dma-address-cells = <0x2>;
>>>>           ibm,#dma-size-cells = <0x2>;
>>>>           ibm,my-dma-window = <0x10000003 0x0 0x0 0x0 0x10000000>;
>>>>
>>>>       Both cells properties are "required". The parser is
>>>>       arch/powerpc/kernel/prom_parse.c:11 of_parse_dma_window(), which reads
>>>>       "ibm,#dma-address-cells" and "ibm,#dma-size-cells" from the same
>>>> node "dn"
>>>>       to walk "ibm,my-dma-window". There is no phandle and no child node
>>>>       involved; this is exactly the "self-describing list property" pattern.
>>>>
>>>> 3) A "#*-cells" property does not have to describe a phandle specifier
>>>> at all:
>>>>
>>>>       - "#pinctrl-cells" (Documentation/devicetree/bindings/pinctrl/
>>>>         pinctrl-single.yaml:55) gives the number of cells per entry of the
>>>> plain
>>>>         "pinctrl-single,pins" property; drivers/pinctrl/devicetree.c:297
>>>>         pinctrl_find_cells_size() looks it up in the parent/grandparent node.
>>>>         No phandle specifier is involved.
>>>>
>>>>       - "#index-cells"
>>>> (Documentation/devicetree/bindings/usb/fsl,usbmisc.yaml:56)
>>>>         is not a specifier for any domain either.
>>>>
>>>> So the convention in practice is "a #<foo>-cells property states how
>>>> many cells
>>>> one <foo> entry occupies", and the entries may live in a child node's
>>>> property
>>>> (#address-cells), in a consumer's phandle specifier (#interrupt-cells,
>>>> #clock-cells), or in a property of the declaring node itself (ranges,
>>>> interrupt-map, ibm,my-dma-window). Our usage falls into the third group.
>>>>
>>>> This bring us to the following conclusion that
>>>> #scmi-secondary-agents-cells usage
>>>> doesn't technically break the device-tree convention with 2 exceptions:
>>>>
>>>> a) Vendor prefix. Documentation/devicetree/bindings/writing-bindings.rst:68
>>>>       says "DO use a vendor prefix on device-specific property names", and all
>>>>       the same-node precedents above are vendor-prefixed
>>>> ("ibm,#dma-address-cells").
>>>>       Both of our properties are Xen-specific and live under /chosen/xen,
>>>> so if we
>>>>       keep the flat form they should become "xen,scmi-secondary-agents" and
>>>>       "xen,#scmi-secondary-agents-cells".
>>>>
>>>> b) Entry layout. In our list the phandle is the second cell
>>>>       (<smc-id &shmem agent-id>), which prevents the use of the standard
>>>>       phandle-array helpers - they all expect the phandle first. If we
>>>> keep the
>>>>       flat form, reordering to <&shmem smc-id agent-id> would let the
>>>> property be
>>>>       parsed as an ordinary phandle-array.
>>>>
>>>> As for your proposal: the child-node form you suggest is certainly
>>>> more idiomatic, it makes the agent id an addressable "reg" and it
>>>> removes the
>>>> optional 2-vs-3-cells variant altogether. My only reservations are that
>>>> it grows
>>>> the Xen DT parser (node iteration instead of a single property walk) and
>>>> that the
>>>> compatible string would need a namespace we own ("xen,scmi-agent" rather
>>>> than
>>>> "arm,scmi-agent", as you correctly suspected).
>>>>
>>>> So I do not think the current form is invalid, but I have no objection to
>>>> moving to child nodes if you prefer it. Please let me know which way you
>>>> would
>>>> like me to go for v14 and I will respin accordingly.
>>> Yes, I think more idiomatic format is better. Also, you need to write a
>>> parser once, but users will write device tree more often.
>>>
>>> The second thing that bothers me is the shared memory nodes inside the
>>> xen_scmi_config node. I believe they should belong to reserved_memory, no?
>> During discussions on the previous versions, it was decided that the
>> main goal is to leave the original device-tree untouched and put all
>> Xen-related content into the xen node. That's why we preserve the
>> original DTS structure, such as the scmi_shm_0 and /firmware/scmi nodes.
>>
>> Also, if you take a look at the arm,scmi.yaml file in the Linux kernel,
>> there is no explicit requirement regarding the exact placement of the
>> scmi-shmem node. As can be seen, arm,scmi-shmem is present in the
>> patternProperties of sram.yaml, but some DTS files place it into
>> reserved-memory instead.
>>
>> My main point is that we don't touch the original device-tree — we
>> simply add the xen node and put all the required properties inside it.
> Okay, but Xen should know about reserved memory, right? So, it is only
> fair to have shared memory nodes in /reserved-memory/ as device tree
> spec suggests.
>> If we were to place shmem into sram or reserved-memory, it would give us
>> the following structure:
>>
>> / {
>>
>>        xen {
>>
>>            <sram|reserved-memory>: {
>>
>>                scmi_shm0 ...
>>
>>               ....
>>
>>               scmi_shmX ...
>>
>>            }
>>
>>        }
>>
>> }
>>
>> This wouldn't provide any benefit but would overcomplicate the
>> device-tree structure. What do you think?
> Well, there is another argument why I want to move shared memory nodes
> out of xen_scmi_config. In that case we can have more flat
> structure. So, instead of
>
> xen_scmi_config {
>          #address-cells = 2;
>          #size-cells = 2;
>
>          shm1 {};
>          shm2 {};
>          shmN {};
>
>          agents {
>                  #address-cells = 1;
>                  #size-cells = 0;
>                  agent1 {};
>                  agent2 {};
>          }
> }
>
>
> we could have
>
> xen_scmi_config {
>          #address-cells = 1;
>          #size-cells = 0;
>
>          agent1 {};
>          agent2 {};
> }
>
> which is more idiomatic, IMO.


Ah, now I understand your point. Xen definitely parses the 
/reserved-memory region, but that doesn't mean scmi_shm should be part 
of it(as it is not required by the DT bindings), and this is not common 
practice for vendor DTs.

Let's say we have a board that delivers firmware supporting 8 agents, 
starting from 0x440000 up until 0x448000, along with the device-tree for it.

The device-tree it provides doesn't require all agents to be defined — 
only the first one — but the range 0x440000 -> 0x449000 should be 
reserved. That's why the DT should have the following format:


/ {

     reserved-memory {

          memory@440000 {

             reg = < 0x440000 0x9000 >;

         }

    }

    shm0: scmi_shm@0x440000 {

    }

    /firmware/scmi {

       shm=  <&shm0>;

   }

}

As can be seen, the memory is already reserved.

Now let's add Xen to this DT:

/ {

     reserved-memory {

          memory@440000 {

             reg = < 0x440000 0x9000 >;

         }

    }

    shm0: scmi_shm@0x440000 {

    }

    /firmware/scmi {

       shm=  <&shm0>;

   }

   /xen/xen_scmi_config {
         #address-cells = 2;
         #size-cells = 2;

         shm1 {};
         shm2 {};
         shmN {};

         agents {
                 #address-cells = 1;
                 #size-cells = 0;
                 agent1 {};
                 agent2 {};
         }

}


So, as can be seen:

- We don't make any modifications or break the vendor DT format, except 
for adding the scmi node.
- The reserved-memory node is parsed by Xen, so Xen is aware of the 
reservations.

- All shm nodes are placed in xen_scmi_config.


Best regards,
Oleksii

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

* Re: [PATCH v13 4/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver
  2026-09-25 10:46             ` Oleksii Moisieiev
@ 2026-09-25 17:57               ` Volodymyr Babchuk
  0 siblings, 0 replies; 14+ messages in thread
From: Volodymyr Babchuk @ 2026-09-25 17:57 UTC (permalink / raw)
  To: Oleksii Moisieiev
  Cc: xen-devel@lists.xenproject.org, Andrew Cooper, Anthony PERARD,
	Bertrand Marquis, Jan Beulich, Juergen Gross, Julien Grall,
	Michal Orzel, Roger Pau Monné, Stefano Stabellini,
	Grygorii Strashko

Hi,

Oleksii Moisieiev <Oleksii_Moisieiev@epam.com> writes:

> On 24/09/2026 21:42, Volodymyr Babchuk wrote:
>> Hi Oleksii,
>>
>> Oleksii Moisieiev <Oleksii_Moisieiev@epam.com> writes:
>>
>>> On 24/09/2026 04:15, Volodymyr Babchuk wrote:
>>>> Hi Oleksii,
>>>>
>>>> Oleksii Moisieiev <Oleksii_Moisieiev@epam.com> writes:
>>>>
>>>>> On 23/09/2026 04:02, Volodymyr Babchuk wrote:
>>>>>> Hi Oleksii,
>>>>>>
>>>>>> Oleksii Moisieiev <Oleksii_Moisieiev@epam.com> writes:
>>>>>>
>>>>>> This is a great piece of documentation. It is larger than the text you
>>>>>> added to the 'doc' directory. So I believe it is better to put into a
>>>>>> design document, so it is will not be lost in the git commit messages.
>>>>> Hi Volodymyr,
>>>>>
>>>>> Thank you for a quick response.
>>>>>
>>>>> I've just recheked document in patch 6:
>>>>> docs/hypervisor-guide/arm/firmware/arm-scmi.rst and
>>>>>
>>>>> compared it with the information provided in the commit description.
>>>>>
>>>>> As I can see all information provided in the description (add schemes
>>>>> and dts examples at least)
>>>>>
>>>>> are present in the documentation. Also doc itself is more detailed then
>>>>> the commit description.
>>>>>
>>>>> Maybe I missed something?
>>>> Ah, so you put this information in the last patch in the series. Okay,
>>>> so I missed it.
>>>>
>>>>>>> This patch introduces SCI driver to support for ARM EL3 Trusted Firmware-A
>>>>>>> (TF-A) which provides SCMI interface with multi-agent support, as shown
>>>>>>> below.
>>>>>>>
>>>>>>>      +-----------------------------------------+
>>>>>>>      |                                         |
>>>>>>>      | EL3 TF-A SCMI                           |
>>>>>>>      +-------+--+-------+--+-------+--+-------++
>>>>>>>      |shmem1 |  |shmem0 |  |shmem2 |  |shmemX |
>>>>>>>      +-----+-+  +---+---+  +--+----+  +---+---+
>>>>>>> smc-id1 |        |         |           |
>>>>>>> agent1  |        |         |           |
>>>>>>>      +-----v--------+---------+-----------+----+
>>>>>>>      |              |         |           |    |
>>>>>>>      |              |         |           |    |
>>>>>>>      +--------------+---------+-----------+----+
>>>>>>>             smc-id0 |  smc-id2|    smc-idX|
>>>>>>>             agent0  |  agent2 |    agentX |
>>>>>>>                     |         |           |
>>>>>>>                +----v---+  +--v-----+  +--v-----+
>>>>>>>                |        |  |        |  |        |
>>>>>>>                | Dom0   |  | Dom1   |  | DomX   |
>>>>>>>                |        |  |        |  |        |
>>>>>>>                |        |  |        |  |        |
>>>>>>>                +--------+  +--------+  +--------+
>>>>>>>
>>>>>>> The EL3 SCMI multi-agent firmware is expected to provide SCMI SMC shared
>>>>>>> memory transport for every Agent in the system.
>>>>>>>
>>>>>>> The SCMI Agent transport channel defined by pair:
>>>>>>>     - smc-id: SMC id used for Doorbell
>>>>>>>     - shmem: shared memory for messages transfer, Xen page
>>>>>>>     aligned. Shared memory is mapped with the following flags:
>>>>>>>     MT_DEVICE_nGnRE.
>>>>>>>
>>>>>>> The follwoing SCMI Agents are expected to be defined by SCMI FW to enable SCMI
>>>>>>> multi-agent functionality under Xen:
>>>>>>> - Xen management agent: trusted agents that accesses to the Base Protocol
>>>>>>> commands to configure agent specific permissions
>>>>>>> - OSPM VM agents: non-trusted agent, one for each Guest domain which is
>>>>>>>      allowed direct HW access. At least one OSPM VM agent has to be provided
>>>>>>>      by FW if HW is handled only by Dom0 or Driver Domain.
>>>>>>>
>>>>>>> The EL3 SCMI FW is expected to implement following Base protocol messages:
>>>>>>> - BASE_DISCOVER_AGENT (optional if agent_id was provided)
>>>>>>> - BASE_RESET_AGENT_CONFIGURATION (optional)
>>>>>>> - BASE_SET_DEVICE_PERMISSIONS (optional)
>>>>>>>
>>>>>>> The SCI SCMI SMC multi-agent driver implements following
>>>>>>> functionality:
>>>>>>> - The driver is initialized from the Xen SCMI container ``xen_scmi_config``
>>>>>>>      (compatible ``xen,sci``) placed under ``/chosen/xen``. Only the
>>>>>>>      ``arm,scmi-smc`` node that is a child of this container will bind to Xen;
>>>>>>>      other SCMI nodes (for example under ``/firmware``) are ignored to avoid
>>>>>>>      stealing the host OSPM instance.
>>>>>>>
>>>>>>> scmi_shm_1: sram@47ff1000 {
>>>>>>>              compatible = "arm,scmi-shmem";
>>>>>>>              reg = <0x0 0x47ff1000 0x0 0x1000>;
>>>>>>> };
>>>>>>> scmi_xen: scmi {
>>>>>>>            compatible = "arm,scmi-smc";
>>>>>>>            arm,smc-id = <0x82000003>; <--- Xen management agent smc-id
>>>>>>>            #address-cells = < 1>;
>>>>>>>            #size-cells = < 0>;
>>>>>>>            #access-controller-cells = < 1>;
>>>>>>>            shmem = <&scmi_shm_1>; <--- Xen management agent shmem
>>>>>>> };
>>>>>>>
>>>>>>> - The driver obtains Xen specific SCMI Agent's configuration from the
>>>>>>>      Host DT, probes Agents and builds SCMI Agents list. The Agents
>>>>>>>      configuration is taken from "scmi-secondary-agents" property where
>>>>>>>      first item is "arm,smc-id", second - "arm,scmi-shmem" phandle and
>>>>>>>      third is optional "agent_id":
>>>>>>>
>>>>>>> / {
>>>>>>>      chosen {
>>>>>>>        xen {
>>>>>>>          ranges;
>>>>>>>          xen_scmi_config {
>>>>>>>            compatible = "xen,sci";
>>>>>>>            #address-cells = <2>;
>>>>>>>            #size-cells = <2>;
>>>>>>>            ranges;
>>>>>>>
>>>>>>> 	scmi-secondary-agents = <
>>>>>>>              0x82000002 &scmi_shm_0 0
>>>>>>>              0x82000004 &scmi_shm_2 2
>>>>>>>              0x82000005 &scmi_shm_3 3>; <--- func_id, shmem, agent_id
>>>>>>>            #scmi-secondary-agents-cells = <3>;
>>>>>> I don't think that this is the correct way of using -cells property.
>>>>>>
>>>>>> These properties are used either to provide information for **child** nodes
>>>>>> (like #address-cells or #size-cells) or to provide a number of cells to
>>>>>> encode a specifier for a domain (like #interrupt-cells).
>>>>>>
>>>>>> Here you are doing neither of these. I think the proper way is to define
>>>>>> secondary agents as children:
>>>>>>
>>>>>> agents {
>>>>>>            #address-cells = 1;
>>>>>>            #size-cells = 0;
>>>>>>             scmi_agent_2: scmi_agent@2 {
>>>>>>               compatible = "arm,scmi-agent";  // Actually, I am not sure if
>>>>>>                                               // we allowed to use 'arm' namespace
>>>>>>
>>>>>>               reg = <2>;                      // Agent id goes here
>>>>>>               shmmem = &scmi_shm_2;
>>>>>>               arm,smc-id = 0x82000004;
>>>>>>             };
>>>>>> }
>>>>>>
>>>>>> With this approach you don't need #scmi-secondary-agents-cells at all
>>>>>> and the whole structure becomes more device-tree-ish.
>>>>> Thank you for looking at this. I agree that the two cases you list are
>>>>> the most common ones, but they are not the only sanctioned ones: there is a
>>>>> third, long-standing pattern where a "#<name>-cells" property in a node
>>>>> declares the cell stride of a *non-phandle list property in that very same
>>>>> node*. That is exactly what "#scmi-secondary-agents-cells" does for
>>>>> "scmi-secondary-agents". Some evidence from the Devicetree Specification and
>>>>> from Linux:
>>>>>
>>>>> 1) "ranges" and "interrupt-map" are parsed with the cell counts of the node
>>>>>       that carries them, not of a child or of a phandle target.
>>>>>
>>>>>       dtc enforces this itself:
>>>>>
>>>>>       - scripts/dtc/checks.c:785 check_ranges_format() computes the entry
>>>>> length
>>>>>         as (parent #address-cells + this node's #address-cells + this node's
>>>>>         #size-cells) and validates the length of "ranges" in this node
>>>>> against it.
>>>>>
>>>>>       - scripts/dtc/checks.c:1601 check_interrupt_map():
>>>>>
>>>>>             cellsize = node_addr_cells(node);
>>>>>             cellsize += propval_cell(get_property(node, "#interrupt-cells"));
>>>>>
>>>>>         i.e. the stride of the "interrupt-map" entries in a node is taken from
>>>>>         "#address-cells" and "#interrupt-cells" *of that same node*. Linux
>>>>> does
>>>>>         the same in drivers/of/irq.c.
>>>>>
>>>>>       So "a #*-cells property in node X describing the layout of a property in
>>>>>       node X" is not an abuse of the convention, it is how two of the core
>>>>>       Devicetree Specification properties are defined.
>>>>>
>>>>> 2) There is a precedent whose shape is identical to ours - a vendor property
>>>>>       holding a list, plus cells properties in the same node that describe
>>>>> how to
>>>>>       decode it:
>>>>>
>>>>>       Documentation/devicetree/bindings/tpm/ibm,vtpm.yaml:36
>>>>>
>>>>>           ibm,#dma-address-cells:
>>>>>             description:
>>>>>               number of cells that are used to encode the physical address
>>>>> field
>>>>>               of dma-window properties
>>>>>           ibm,#dma-size-cells:
>>>>>             description:
>>>>>               number of cells that are used to encode the size field of
>>>>>               dma-window properties
>>>>>           ibm,my-dma-window:
>>>>>             description: DMA window associated with this virtual I/O Adapter
>>>>>
>>>>>       and the example (same file, lines 96-98):
>>>>>
>>>>>           ibm,#dma-address-cells = <0x2>;
>>>>>           ibm,#dma-size-cells = <0x2>;
>>>>>           ibm,my-dma-window = <0x10000003 0x0 0x0 0x0 0x10000000>;
>>>>>
>>>>>       Both cells properties are "required". The parser is
>>>>>       arch/powerpc/kernel/prom_parse.c:11 of_parse_dma_window(), which reads
>>>>>       "ibm,#dma-address-cells" and "ibm,#dma-size-cells" from the same
>>>>> node "dn"
>>>>>       to walk "ibm,my-dma-window". There is no phandle and no child node
>>>>>       involved; this is exactly the "self-describing list property" pattern.
>>>>>
>>>>> 3) A "#*-cells" property does not have to describe a phandle specifier
>>>>> at all:
>>>>>
>>>>>       - "#pinctrl-cells" (Documentation/devicetree/bindings/pinctrl/
>>>>>         pinctrl-single.yaml:55) gives the number of cells per entry of the
>>>>> plain
>>>>>         "pinctrl-single,pins" property; drivers/pinctrl/devicetree.c:297
>>>>>         pinctrl_find_cells_size() looks it up in the parent/grandparent node.
>>>>>         No phandle specifier is involved.
>>>>>
>>>>>       - "#index-cells"
>>>>> (Documentation/devicetree/bindings/usb/fsl,usbmisc.yaml:56)
>>>>>         is not a specifier for any domain either.
>>>>>
>>>>> So the convention in practice is "a #<foo>-cells property states how
>>>>> many cells
>>>>> one <foo> entry occupies", and the entries may live in a child node's
>>>>> property
>>>>> (#address-cells), in a consumer's phandle specifier (#interrupt-cells,
>>>>> #clock-cells), or in a property of the declaring node itself (ranges,
>>>>> interrupt-map, ibm,my-dma-window). Our usage falls into the third group.
>>>>>
>>>>> This bring us to the following conclusion that
>>>>> #scmi-secondary-agents-cells usage
>>>>> doesn't technically break the device-tree convention with 2 exceptions:
>>>>>
>>>>> a) Vendor prefix. Documentation/devicetree/bindings/writing-bindings.rst:68
>>>>>       says "DO use a vendor prefix on device-specific property names", and all
>>>>>       the same-node precedents above are vendor-prefixed
>>>>> ("ibm,#dma-address-cells").
>>>>>       Both of our properties are Xen-specific and live under /chosen/xen,
>>>>> so if we
>>>>>       keep the flat form they should become "xen,scmi-secondary-agents" and
>>>>>       "xen,#scmi-secondary-agents-cells".
>>>>>
>>>>> b) Entry layout. In our list the phandle is the second cell
>>>>>       (<smc-id &shmem agent-id>), which prevents the use of the standard
>>>>>       phandle-array helpers - they all expect the phandle first. If we
>>>>> keep the
>>>>>       flat form, reordering to <&shmem smc-id agent-id> would let the
>>>>> property be
>>>>>       parsed as an ordinary phandle-array.
>>>>>
>>>>> As for your proposal: the child-node form you suggest is certainly
>>>>> more idiomatic, it makes the agent id an addressable "reg" and it
>>>>> removes the
>>>>> optional 2-vs-3-cells variant altogether. My only reservations are that
>>>>> it grows
>>>>> the Xen DT parser (node iteration instead of a single property walk) and
>>>>> that the
>>>>> compatible string would need a namespace we own ("xen,scmi-agent" rather
>>>>> than
>>>>> "arm,scmi-agent", as you correctly suspected).
>>>>>
>>>>> So I do not think the current form is invalid, but I have no objection to
>>>>> moving to child nodes if you prefer it. Please let me know which way you
>>>>> would
>>>>> like me to go for v14 and I will respin accordingly.
>>>> Yes, I think more idiomatic format is better. Also, you need to write a
>>>> parser once, but users will write device tree more often.
>>>>
>>>> The second thing that bothers me is the shared memory nodes inside the
>>>> xen_scmi_config node. I believe they should belong to reserved_memory, no?
>>> During discussions on the previous versions, it was decided that the
>>> main goal is to leave the original device-tree untouched and put all
>>> Xen-related content into the xen node. That's why we preserve the
>>> original DTS structure, such as the scmi_shm_0 and /firmware/scmi nodes.
>>>
>>> Also, if you take a look at the arm,scmi.yaml file in the Linux kernel,
>>> there is no explicit requirement regarding the exact placement of the
>>> scmi-shmem node. As can be seen, arm,scmi-shmem is present in the
>>> patternProperties of sram.yaml, but some DTS files place it into
>>> reserved-memory instead.
>>>
>>> My main point is that we don't touch the original device-tree — we
>>> simply add the xen node and put all the required properties inside it.
>> Okay, but Xen should know about reserved memory, right? So, it is only
>> fair to have shared memory nodes in /reserved-memory/ as device tree
>> spec suggests.
>>> If we were to place shmem into sram or reserved-memory, it would give us
>>> the following structure:
>>>
>>> / {
>>>
>>>        xen {
>>>
>>>            <sram|reserved-memory>: {
>>>
>>>                scmi_shm0 ...
>>>
>>>               ....
>>>
>>>               scmi_shmX ...
>>>
>>>            }
>>>
>>>        }
>>>
>>> }
>>>
>>> This wouldn't provide any benefit but would overcomplicate the
>>> device-tree structure. What do you think?
>> Well, there is another argument why I want to move shared memory nodes
>> out of xen_scmi_config. In that case we can have more flat
>> structure. So, instead of
>>
>> xen_scmi_config {
>>          #address-cells = 2;
>>          #size-cells = 2;
>>
>>          shm1 {};
>>          shm2 {};
>>          shmN {};
>>
>>          agents {
>>                  #address-cells = 1;
>>                  #size-cells = 0;
>>                  agent1 {};
>>                  agent2 {};
>>          }
>> }
>>
>>
>> we could have
>>
>> xen_scmi_config {
>>          #address-cells = 1;
>>          #size-cells = 0;
>>
>>          agent1 {};
>>          agent2 {};
>> }
>>
>> which is more idiomatic, IMO.
>
>
> Ah, now I understand your point. Xen definitely parses the 
> /reserved-memory region, but that doesn't mean scmi_shm should be part 
> of it(as it is not required by the DT bindings), and this is not common 
> practice for vendor DTs.
>
> Let's say we have a board that delivers firmware supporting 8 agents, 
> starting from 0x440000 up until 0x448000, along with the device-tree for it.
>
> The device-tree it provides doesn't require all agents to be defined — 
> only the first one — but the range 0x440000 -> 0x449000 should be 
> reserved. That's why the DT should have the following format:
>
>
> / {
>
>      reserved-memory {
>
>           memory@440000 {
>
>              reg = < 0x440000 0x9000 >;
>
>          }
>
>     }
>
>     shm0: scmi_shm@0x440000 {
>
>     }
>
>     /firmware/scmi {
>
>        shm=  <&shm0>;
>
>    }
>
> }
>
> As can be seen, the memory is already reserved.
>
> Now let's add Xen to this DT:
>
> / {
>
>      reserved-memory {
>
>           memory@440000 {
>
>              reg = < 0x440000 0x9000 >;
>
>          }
>
>     }
>
>     shm0: scmi_shm@0x440000 {
>
>     }
>
>     /firmware/scmi {
>
>        shm=  <&shm0>;
>
>    }
>
>    /xen/xen_scmi_config {
>          #address-cells = 2;
>          #size-cells = 2;
>
>          shm1 {};
>          shm2 {};
>          shmN {};
>
>          agents {
>                  #address-cells = 1;
>                  #size-cells = 0;
>                  agent1 {};
>                  agent2 {};
>          }
>
> }
>
>
> So, as can be seen:
>
> - We don't make any modifications or break the vendor DT format, except 
> for adding the scmi node.
> - The reserved-memory node is parsed by Xen, so Xen is aware of the 
> reservations.
>
> - All shm nodes are placed in xen_scmi_config.

Okay, this makes sense, I guess... Anyway, I can't suggest a better alternative.

-- 
WBR, Volodymyr

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

end of thread, other threads:[~2026-09-25 17:58 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-22 14:40 [PATCH v13 0/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent support Oleksii Moisieiev
2026-09-22 14:40 ` [PATCH v13 2/6] xen: arm: smccc: add INVALID_PARAMETER error code Oleksii Moisieiev
2026-09-22 14:40 ` [PATCH v13 1/6] xen/domctl: chain SCI handling before IOMMU in assign_device domctl Oleksii Moisieiev
2026-09-22 14:40 ` [PATCH v13 4/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver Oleksii Moisieiev
2026-09-23  1:02   ` Volodymyr Babchuk
2026-09-23  8:25     ` Oleksii Moisieiev
2026-09-24  1:15       ` Volodymyr Babchuk
2026-09-24  7:54         ` Oleksii Moisieiev
2026-09-24 18:42           ` Volodymyr Babchuk
2026-09-25 10:46             ` Oleksii Moisieiev
2026-09-25 17:57               ` Volodymyr Babchuk
2026-09-22 14:40 ` [PATCH v13 5/6] tools/xl/libxl: wire up SCMI SMC multi-agent configuration Oleksii Moisieiev
2026-09-22 14:40 ` [PATCH v13 3/6] lib/arm: Add I/O memory copy helpers Oleksii Moisieiev
2026-09-22 14:40 ` [PATCH v13 6/6] docs: arm: add SCI SCMI SMC multi-agent driver docs Oleksii Moisieiev

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.