[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v14 4/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver


  • To: Oleksii Moisieiev <Oleksii_Moisieiev@xxxxxxxx>
  • From: Volodymyr Babchuk <Volodymyr_Babchuk@xxxxxxxx>
  • Date: Fri, 23 Oct 2026 01:56:28 +0000
  • Accept-language: en-US
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=epam.com; dmarc=pass action=none header.from=epam.com; dkim=pass header.d=epam.com; arc=none
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=NzgvpxZwGof/EUUXE22kj+bYNxRo0NQ6DPaedQuJZYs=; b=bXScgLgqW2NTszhQnEbdlEFyOOmNyaramKJgEob/Ah4PKoN+Jgl5CbeEFJiRRVUcqpC2Z/8uNGt9++zZ6rGmeh/mddD1etrrG2Y/mSn1pkd9ILD3122uLMVb5YQ8hAj4TVweFAPYhzQKfVbA0IlOB+ltok9DgSHh0RcUhJVNt5nljl8Oihg8fQN2t6l1foKdPREU65OnwTRlXNHGuP4REqoZVakt4z4wy36BopDBtQci9EaoGRlgFq8guL2ylALKDxEge7UAktWCMB+nt0aXf8AEu/XrYq2tSsupblZeMtGre3yIoEawDKQTJhdXWx83TngCitOJGLtIM0SKMGT/YA==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=GBt/SnjZ1NLSllOyb4MD36w7ZZLJAVstzU6JQllkjkcAnjqruW8WBzKTL5va3ItYznNV6+OSZkjcpCFjxQD5Sj9P105fum8YOLC7UOGcxO4dREyRDgOapJAc3jMVoLzcGwaHQCFA6agxyda8l4KvvzyOmIW63N6mAdPAIs5/BLOlEXkEd9Jp0M17Xk2quviTTyQlMe9jGCVFCk9jvTFIBm/9dyZRrDEMWRfk1Fo8RtPx3FbdKwh0AAL2hGeiOrTyllIhl7HtHP1QL3y4lEf2JIRIb5L3869KGEgjtDRO+9ulQfgE3Pmt+l5oZIFqwl66muW/sNoEdf8PODQBLd6kSA==
  • Authentication-results: eu.smtp.expurgate.cloud; dkim=pass header.s=selector1 header.d=epam.com header.i="@epam.com" header.h="From:Date:Subject:Message-ID:Content-Type:MIME-Version:x-ms-exchange-senderadcheck"
  • Authentication-results: mx.microsoft.com 1; dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=epam.com;
  • Cc: "xen-devel@xxxxxxxxxxxxxxxxxxxx" <xen-devel@xxxxxxxxxxxxxxxxxxxx>, Andrew Cooper <andrew.cooper3@xxxxxxxxxx>, Anthony PERARD <anthony.perard@xxxxxxxxxx>, Bertrand Marquis <bertrand.marquis@xxxxxxx>, Jan Beulich <jbeulich@xxxxxxxx>, Juergen Gross <jgross@xxxxxxxx>, Julien Grall <julien@xxxxxxx>, Michal Orzel <michal.orzel@xxxxxxx>, Roger Pau Monné <roger@xxxxxxxxxxxxxx>, Stefano Stabellini <sstabellini@xxxxxxxxxx>, Grygorii Strashko <grygorii_strashko@xxxxxxxx>
  • Delivery-date: Fri, 23 Oct 2026 01:56:50 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>
  • Thread-index: AQHdYG8UqIf/0AA9cU2/foNeomddPw==
  • Thread-topic: [PATCH v14 4/6] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver

Hi Oleksii,

Oleksii Moisieiev <Oleksii_Moisieiev@xxxxxxxx> writes:

> 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 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 (required for Xen and secondary agents without reg)
> - BASE_RESET_AGENT_CONFIGURATION (optional)
> - BASE_SET_DEVICE_PERMISSIONS (optional)
>
> The driver binds to the arm,scmi-smc management node inside a Xen-specific
> container compatible with xen,sci under /chosen/xen. Other SCMI nodes,
> including the host /firmware/scmi, are ignored by the mediator.
>
> Describe secondary agents as xen,scmi-agent children of an agents node
> inside the same container. The agents node uses #address-cells = <1> and
> `#size-cells = <0>`. Each child provides arm,smc-id and a single shmem 
> phandle.
> The optional single-cell reg property supplies the SCMI agent ID (0..254).
> When reg is absent, Xen discovers the ID using BASE_DISCOVER_AGENT on that
> channel. Use agent@<id> for an explicit ID, and a node name without a unit
> address for discovery. Disabled agent nodes are ignored; at least one
> enabled secondary agent is required.
>
> Resolve discovered IDs before adding channels to the list, so duplicate
> IDs are rejected consistently, including clashes with Xen's management
> agent. Explicit IDs avoid mapping and probing secondary channels during
> initialization. Xen always discovers its own management agent ID.
>
> Keep shared-memory nodes under xen_scmi_config and leave the platform's
> existing /firmware/scmi and host OSPM shared-memory node unchanged. Channel
> nodes describe locations, rather than reserve memory: if firmware buffers
> occupy system RAM, the platform DT must reserve the whole firmware-owned
> range in /reserved-memory before Xen boots. A single reservation may cover
> multiple channels, including unused ones. SRAM outside system RAM does
> not require a RAM reservation. Channel nodes need not be moved into
> /reserved-memory.
>
> For example:
>
> / {
>     #address-cells = <2>;
>     #size-cells = <2>;
>
>     scmi_shm_0: sram@47ff0000 {
>         compatible = "arm,scmi-shmem";
>         reg = <0x0 0x47ff0000 0x0 0x1000>;
>     };
>
>     firmware {
>         scmi: scmi {
>             compatible = "arm,scmi-smc";
>             arm,smc-id = <0x82000002>;
>             #address-cells = <1>;
>             #size-cells = <0>;
>             shmem = <&scmi_shm_0>;
>         };
>     };
>
>     chosen {
>         #address-cells = <2>;
>         #size-cells = <2>;
>         ranges;
>
>         xen {
>             #address-cells = <2>;
>             #size-cells = <2>;
>             ranges;
>
>             xen_scmi_config {
>                 compatible = "xen,sci";
>                 #address-cells = <2>;
>                 #size-cells = <2>;
>                 ranges;
>                 xen,dom0-sci-agent-id = <0>;
>
>                 /* Xen 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>;
>                     #address-cells = <1>;
>                     #size-cells = <0>;
>                     #access-controller-cells = <1>;
>                     shmem = <&scmi_shm_1>;
>                 };
>
>                 agents {
>                     #address-cells = <1>;
>                     #size-cells = <0>;
>
>                     agent@0 {
>                         compatible = "xen,scmi-agent";
>                         reg = <0>;
>                         arm,smc-id = <0x82000002>;
>                         shmem = <&scmi_shm_0>;
>                     };
>
>                     agent@2 {
>                         compatible = "xen,scmi-agent";
>                         reg = <2>;
>                         arm,smc-id = <0x82000004>;
>                         shmem = <&scmi_shm_2>;
>                     };
>
>                     agent@3 {
>                         compatible = "xen,scmi-agent";
>                         reg = <3>;
>                         arm,smc-id = <0x82000005>;
>                         shmem = <&scmi_shm_3>;
>                     };
>                 };
>             };
>         };
>     };
> };
>
> To discover a secondary agent ID instead, omit reg from its node:
>
>     agent-domu {
>         compatible = "xen,scmi-agent";
>         arm,smc-id = <0x82000004>;
>         shmem = <&scmi_shm_2>;
>     };
>
> The agents child nodes replace the former scmi-secondary-agents list and
> `#scmi-secondary-agents-cells` property. The xen,sci subtree is omitted from
> Dom0's DT; the host OSPM channel is passed through when SCMI is enabled
> for Dom0. Full configuration and domain examples are included in the
> binding documentation and the hypervisor guide in patch 6.
>
> 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@xxxxxxxx>
> Signed-off-by: Oleksii Moisieiev <oleksii_moisieiev@xxxxxxxx>
> ---
>
> Changes in v14:
> - replace the flat secondary-agent list and its cell-count property with
>   xen,scmi-agent child nodes under agents, using reg for an optional ID
> - scope secondary-agent parsing to the management channel's xen,sci container
> - resolve discovered IDs before insertion and reject duplicate or invalid IDs
> - validate shared-memory phandles, addresses and nonzero page-aligned sizes
> - retain shared-memory nodes under xen_scmi_config and document platform
>   reservations separately, following the review discussion
> - update binding examples and commit description for the child-node format
> - match the const-qualified SCI domain_sanitise_config callback interface
>
> 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       | 195 +++++
>  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 | 792 ++++++++++++++++++++
>  xen/include/public/arch-arm.h               |   5 +-
>  12 files changed, 1403 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@xxxxxxxx>
>  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 3787d2e2b6..178f2493d8 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,183 @@ 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.
> +
> +- agents
> +
> +    Required child of the xen,sci container. Describes the secondary SCMI
> +    agents available for assignment to domains. Set #address-cells = <1>
> +    and #size-cells = <0> in this node. At least one enabled child with
> +    compatible = "xen,scmi-agent" is required. Each such child has:
> +
> +    - reg: Optional single-cell SCMI agent ID in the range 0..254. When
> +      present, name the node agent@<id>, with the unit address in 
> hexadecimal.
> +      When absent, use a name without a unit address; Xen discovers the ID
> +      using BASE_DISCOVER_AGENT on that channel. A malformed reg is rejected.
> +    - arm,smc-id: Required SMC function ID for this agent's doorbell.
> +    - shmem: Required single phandle to an arm,scmi-shmem node. Its address
> +      and nonzero size must be Xen page aligned.
> +
> +    Agent IDs must be unique, including IDs returned by discovery, and must
> +    not identify Xen's management agent. Children marked disabled are 
> ignored.
> +    The management arm,scmi-smc node remains a sibling of agents, directly
> +    under xen,sci.
> +
> +- 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 selected
> +    agent's ``func_id`` and shared-memory region must match the host
> +    ``/firmware/scmi`` channel passed to Dom0.
> +
> +The shared-memory nodes may remain inside xen_scmi_config, while the
> +platform's existing /firmware/scmi and its shared-memory node stay unchanged.
> +Shared-memory nodes describe channel locations; they do not reserve RAM.
> +If the channel buffers occupy system RAM, the platform DT must reserve the
> +whole firmware-owned range in /reserved-memory before Xen boots. One
> +reservation may cover several channels, including channels not listed in
> +this configuration. SRAM outside system RAM does not require a RAM
> +reservation. Moving the channel nodes into /reserved-memory is not required.
> +
> +As an example:
> +
> +/ {
> +    #address-cells = <2>;
> +    #size-cells = <2>;
> +
> +    scmi_shm_0: sram@47ff0000 {
> +        compatible = "arm,scmi-shmem";
> +        reg = <0x0 0x47ff0000 0x0 0x1000>;
> +    };
> +
> +    firmware {
> +        scmi: scmi {
> +            compatible = "arm,scmi-smc";
> +            arm,smc-id = <0x82000002>;
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +            shmem = <&scmi_shm_0>;
> +        };
> +    };
> +
> +    chosen {
> +        #address-cells = <2>;
> +        #size-cells = <2>;
> +        ranges;
> +
> +        xen {
> +            #address-cells = <2>;
> +            #size-cells = <2>;
> +            ranges;
> +
> +            xen_scmi_config {
> +                compatible = "xen,sci";
> +                #address-cells = <2>;
> +                #size-cells = <2>;
> +                ranges;
> +                xen,dom0-sci-agent-id = <0>; /* dom0 agent id */
> +
> +                /* Xen 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"; /* Xen management agent 
> func_id */
> +                    arm,smc-id = <0x82000003>;
> +                    #address-cells = <1>;
> +                    #size-cells = <0>;
> +                    #access-controller-cells = <1>;
> +                    shmem = <&scmi_shm_1>; /* Xen management agent shmem */
> +                };
> +
> +                agents {
> +                    #address-cells = <1>;
> +                    #size-cells = <0>;
> +
> +                    agent@0 {
> +                        compatible = "xen,scmi-agent";
> +                        reg = <0>;
> +                        arm,smc-id = <0x82000002>;
> +                        shmem = <&scmi_shm_0>;
> +                    };
> +
> +                    agent@2 {
> +                        compatible = "xen,scmi-agent";
> +                        reg = <2>;
> +                        arm,smc-id = <0x82000004>;
> +                        shmem = <&scmi_shm_2>;
> +                    };
> +
> +                    agent@3 {
> +                        compatible = "xen,scmi-agent";
> +                        reg = <3>;
> +                        arm,smc-id = <0x82000005>;
> +                        shmem = <&scmi_shm_3>;
> +                    };
> +                };
> +            };
> +        };
> +    };
> +};
> +
> +This keeps the host OSPM channel (SMC 0x82000002, shmem 0x47ff0000) separate
> +from Xen's privileged management channel (SMC 0x82000003, shmem 0x47ff1000).
> +The xen,sci subtree is omitted from Dom0's device tree.
> +
> +To discover a secondary agent ID, omit reg from its node. For example,
> +replace only agent@2 above with:
> +
> +agent-domu {
> +    compatible = "xen,scmi-agent";
> +    arm,smc-id = <0x82000004>;
> +    shmem = <&scmi_shm_2>;
> +};
> +
> +BASE_DISCOVER_AGENT is required on secondary channels only when reg is
> +omitted. Xen always uses it to discover its management agent ID. Providing
> +reg avoids mapping and probing the secondary channel during initialization.
> +The scmi-secondary-agents and #scmi-secondary-agents-cells properties used
> +by earlier versions are replaced by the agents child nodes.
> +
> +Dom0less example (multi-agent)
> +-------------------------------
> +
> +Use the same Xen SCMI configuration as above. Remove xen,dom0-sci-agent-id
> +if Dom0 does not use SCMI. Add xen,sci_type and xen,sci-agent-id to the
> +predefined guest's node under /chosen:
> +
> +chosen {
> +    xen,domain@1 {
> +        compatible = "xen,domain";
> +        xen,sci_type = "scmi_smc_multiagent";
> +        xen,sci-agent-id = <2>;
> +        /* Additional domain properties (memory, cpus, kernels, etc.) */
> +    };
> +};
> +
> +The guest's partial DT and shared-memory mapping must use the SMC function
> +ID and shared-memory channel associated with agent 2 in the agents node.
> diff --git a/xen/arch/arm/dom0less-build.c b/xen/arch/arm/dom0less-build.c
> index 7bbb2eafb6..01c2a5ce52 100644
> --- a/xen/arch/arm/dom0less-build.c
> +++ b/xen/arch/arm/dom0less-build.c
> @@ -293,6 +293,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 72f6f7539a..5d42cdf6af 100644
> --- a/xen/arch/arm/domain_build.c
> +++ b/xen/arch/arm/domain_build.c
> @@ -88,6 +88,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))
> @@ -1480,6 +1513,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 =
> @@ -1975,6 +2009,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@xxxxxxxx>
> + * 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");

"of the SMC message". Also, looks like it is over 80 characters.

> +        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);

Why do you mix writel() and writel_relaxed() here?

> +
> +    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");

"of the input SMC message"

> +        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");
"of the SMC message"

Also, I am wondering if you really need to print this error messages in
the first place.


> +        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[];
> +};

Should it be __packed?

> +
> +#define SCMI_SHMEM_MAPPED_SIZE PAGE_SIZE
> +
> +int shmem_put_message(volatile struct scmi_shared_mem __iomem *shmem,

I am not sure if volatile makes sense here, taking into account that
this is __iomem anyway.

Also, shmem_put_message sounds to generic... Maybe scmi_put_message?

> +                      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);

Basically the same comments as for previous one.

> +#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..6c46f6cd36
> --- /dev/null
> +++ b/xen/arch/arm/firmware/scmi-smc-multiagent.c
> @@ -0,0 +1,792 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * SCI SCMI multi-agent driver, using SMC/HVC shmem as transport.
> + *
> + * Oleksii Moisieiev <oleksii_moisieiev@xxxxxxxx>
> + * 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"
> +
> +struct scmi_channel {
> +    uint32_t agent_id;

If all agent_ids large than UINT8_MAX are invalid, why do you need
uint32_t here?

> +    uint32_t func_id;
> +    domid_t domain_id;
> +    uint64_t paddr;

paddr_t

> +    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);

You will print this even if send_smc_message() fails. I think it should
go before "clean:" label.

Also, maybe "cleanup:" ?

> +
> +    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,

You will need proper format, not "%lx" for 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,

Then you can put this function into __init as well

> +     * 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_channel_addr(struct dt_device_node *scmi_node, u64 *addr,
> +                          u64 *size)
> +{
> +    struct dt_device_node *shmem_node;
> +    uint32_t phandle;
> +    int ret;
> +
> +    ret = dt_property_read_variable_u32_array(scmi_node, "shmem", &phandle,
> +                                              1, 1);
> +    if ( ret < 0 )
> +        return ret;
> +
> +    shmem_node = dt_find_node_by_phandle(phandle);
> +    if ( !shmem_node ||
> +         !dt_device_is_compatible(shmem_node, "arm,scmi-shmem") )
> +        return -EINVAL;
> +
> +    ret = dt_device_get_address(shmem_node, 0, addr, size);
> +    if ( ret )
> +        return ret;
> +
> +    if ( !*size || !IS_ALIGNED(*size, SCMI_SHMEM_MAPPED_SIZE) ||
> +         !IS_ALIGNED(*addr, SCMI_SHMEM_MAPPED_SIZE) )
> +    {
> +        printk(XENLOG_ERR "scmi: invalid shmem range for %s\n",
> +               scmi_node->full_name);
> +        return -EINVAL;
> +    }
> +
> +    return 0;
> +}
> +
> +/*
> + * 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)

Also __init?

> +{
> +    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 )

On error, Xen's channel will be mapped still. Is this intended behaviour?

> +        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 int __init collect_agents(struct dt_device_node *scmi_node)
> +{
> +    struct dt_device_node *agents, *node;
> +    unsigned int count = 0;
> +
> +    /* Use the configuration containing this management channel. */
> +    for ( agents = scmi_node->parent; agents; agents = agents->parent )
> +        if ( dt_device_is_compatible(agents, "xen,sci") )
> +            break;
> +
> +    ASSERT(agents);
> +
> +    dt_for_each_child_node(agents, node)
> +        if ( dt_node_name_is_equal(node, "agents") )
> +            break;
> +
> +    if ( !node )
> +    {
> +        printk(XENLOG_ERR "scmi: missing agents node\n");
> +        return -EINVAL;
> +    }
> +    agents = node;
> +
> +    if ( dt_child_n_addr_cells(agents) != 1 ||
> +         dt_child_n_size_cells(agents) != 0 )
> +        return -EINVAL;
> +
> +    dt_for_each_child_node(agents, node)
> +    {
> +        struct scmi_channel agent = {
> +            .agent_id = SCMI_BASE_AGENT_ID_OWN,
> +            .domain_id = DOMID_INVALID,
> +        };
> +        struct scmi_channel *channel;
> +        bool discover;
> +        uint32_t len;
> +        u64 size;

uint64_t

> +        int ret;
> +
> +        if ( !dt_device_is_compatible(node, "xen,scmi-agent") ||
> +             !dt_device_is_available(node) )
> +            continue;
> +
> +        discover = !dt_find_property(node, "reg", &len);
> +        if ( !discover )
> +        {
> +            if ( len != sizeof(uint32_t) ||
> +                 !dt_property_read_u32(node, "reg", &agent.agent_id) )
> +                return -EINVAL;
> +        }
> +
> +        if ( !dt_property_read_u32(node, "arm,smc-id", &agent.func_id) )
> +            return -EINVAL;
> +
> +        ret = scmi_dt_read_channel_addr(node, &agent.paddr, &size);
> +        if ( ret )
> +            return ret;
> +
> +        if ( discover )
> +        {
> +            /* Resolve the ID before inserting it into the channel list. */
> +            spin_lock_init(&agent.lock);
> +            ret = collect_agent_id(&agent);
> +            if ( ret )
> +                return ret;
> +        }
> +
> +        /* Guest configurations encode agent IDs in an eight-bit field. */
> +        if ( agent.agent_id >= UINT8_MAX )
> +            return -EINVAL;
> +
> +        channel = smc_create_channel(agent.agent_id, agent.func_id,
> +                                     agent.paddr);
> +        if ( IS_ERR(channel) )
> +        {
> +            printk(XENLOG_ERR "scmi: Could not create channel for %s: %ld\n",
> +                   node->full_name, PTR_ERR(channel));
> +            return PTR_ERR(channel);
> +        }
> +
> +        count++;
> +        printk(XENLOG_DEBUG "scmi: Agent %u SMC %X addr %lx\n",
> +               agent.agent_id, agent.func_id, (unsigned long)agent.paddr);

Why cast to unsigned long?

> +    }
> +
> +    return count ? 0 : -EINVAL;
> +}
> +
> +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(const 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);

You can use "%pd"

> +
> +    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)
> +{
> +    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;
> +    }
> +
> +    return scmi_dt_read_channel_addr(scmi_node, addr, &size);
> +}
> +
> +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 14d51e0746..722c2d2e3a 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
> @@ -347,7 +348,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



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.