[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [RFC PATCH v5 09/10] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver
On Tue, 22 Jul 2025, Oleksii Moisieiev wrote: > This patch introduces SCI driver to support for ARM EL3 Trusted Firmware-A > (TF-A) which provides SCMI interface with multi-agnet support, as shown multi-agent > 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 expected to provide SCMI SMC/HVC shared > memory transport for every Agent in the system. > > The SCMI Agent transport channel defined by pair: > - smc-id: SMC/HVC id used for Doorbell > - shmem: shared memory for messages transfer, Xen page > aligned. Shared memort is mapped with the following flags: > MT_DEVICE_nGnRE. > > The follwoing SCMI Agents expected to be defined by SCMI FW to enable SCMI > multi-agent functionality under Xen: > - Xen manegement agent: trusted agents that accesses to the Base Protocol management > 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 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 based on the ``xen,config`` node under ``chosen`` > (only one SCMI interface is supported), which describes the Xen management > agent SCMI interface. > > 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 manegement agent smc-id > #address-cells = < 1>; > #size-cells = < 0>; > #access-controller-cells = < 1>; > shmem = <&scmi_shm_1>; <--- Xen manegement 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 { > ranges; > xen,config { The node name could be xen-config, but it doesn't matter because we should check for the compatible string instead (no check on node name). We need to add a compatible string here, I would use "xen,scmi": compatible = "xen,scmi"; > ranges; > scmi-secondary-agents = < > 0x82000003 &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 > > 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 = <0x82000002>; <--- Xen manegement agent smc-id > #address-cells = < 1>; > #size-cells = < 0>; > #access-controller-cells = < 1>; > shmem = <&scmi_shm_1>; <--- Xen manegement 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 > ``/chosen`` 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,config { > 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. > > - 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/HVC 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/HVC 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: > - pass dom0_scmi_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 > shell be used: shall > > dtdev = [ > "/soc/i2c@e6508000", > ] > > xl.cfg:dtdev will contain all nodes which are under SCMI management (not only > those which are behind IOMMU). > > [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: Oleksii Moisieiev <oleksii_moisieiev@xxxxxxxx> > Signed-off-by: Grygorii Strashko <grygorii_strashko@xxxxxxxx> > --- > > 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 | 4 + > SUPPORT.md | 11 + > docs/man/xl.cfg.5.pod.in | 13 + > docs/misc/arm/device-tree/booting.txt | 88 +++ > docs/misc/xen-command-line.pandoc | 9 + > tools/libs/light/libxl_arm.c | 4 + > tools/libs/light/libxl_types.idl | 4 +- > tools/xl/xl_parse.c | 12 + > xen/arch/arm/dom0less-build.c | 11 + > xen/arch/arm/domain_build.c | 3 +- > 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 | 112 +++ > xen/arch/arm/firmware/scmi-shmem.h | 45 ++ > xen/arch/arm/firmware/scmi-smc-multiagent.c | 803 ++++++++++++++++++++ > xen/include/public/arch-arm.h | 3 + > 17 files changed, 1297 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 > > diff --git a/MAINTAINERS b/MAINTAINERS > index 31dbba54bb..1b6b58cbb7 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -514,6 +514,10 @@ R: Oleksii Moisieiev <oleksii_moisieiev@xxxxxxxx> > S: Supported > F: xen/arch/arm/firmware/sci.c > F: xen/arch/arm/include/asm/firmware/sci.h > +F: xen/arch/arm/firmware/scmi-smc-multiagent.c > +F: xen/arch/arm/firmware/scmi-shmem.c > +F: xen/arch/arm/firmware/scmi-shmem.h > +F: xen/arch/arm/firmware/scmi-proto.h > > SEABIOS UPSTREAM > M: Wei Liu <wl@xxxxxxx> > diff --git a/SUPPORT.md b/SUPPORT.md > index 6a82a92189..9d7857d953 100644 > --- a/SUPPORT.md > +++ b/SUPPORT.md > @@ -956,6 +956,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/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in > index 8f1a203e21..f6cf2d4567 100644 > --- a/docs/man/xl.cfg.5.pod.in > +++ b/docs/man/xl.cfg.5.pod.in > @@ -3103,8 +3103,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 a non-zero ARM SCI agent id for the guest. This option is mandatory > +if the SCMI SMC support is enabled for the guest. The agent ids of domains > +existing on a single host must be unique and in the range [1..255]. Why is the option mandatory if the commit description explains that the agent_id can be probed by Xen? In the commit message, the mandatory field is smc-id instead? I guess it is because we expect Xen to have already the agent_id->smc-id mapping and we expect the agent_id to be easier to remember and to write in the config file? > =back > > =back > diff --git a/docs/misc/arm/device-tree/booting.txt > b/docs/misc/arm/device-tree/booting.txt > index 8ea11c1551..10a99cfd15 100644 > --- a/docs/misc/arm/device-tree/booting.txt > +++ b/docs/misc/arm/device-tree/booting.txt > @@ -322,6 +322,20 @@ 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 a non-zero ARM SCI 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 > [1..255]. same question > Under the "xen,domain" compatible node, one or more sub-nodes are present > for the DomU kernel and ramdisk. > > @@ -824,3 +838,77 @@ 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 shell be provided in the Host DT shall > +according to the SCMI compliant EL3 Firmware specification with > +ARM SMC/HVC transport using property "scmi-secondary-agents" placed in > "xen,config" > +node under "chosen" node: > + > +- 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. > + > +As an example: > + > +/{ > +chosen { > + xen,config { same comment about node name and compatible string > + 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_shm_3: sram@47ff4000 { > + compatible = "arm,scmi-shmem"; > + reg = <0x0 0x47ff4000 0x0 0x1000>; > + }; > + scmi-secondary-agents = < > + 0x82000003 &scmi_shm_1 1 > + 0x82000004 &scmi_shm_2 2 > + 0x82000005 &scmi_shm_3 3 > + 0x82000006 &scmi_shm_4 4>; > + #scmi-secondary-agents-cells = <3>; > + }; > + }; > +}; > + > +- #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,config { > + scmi-secondary-agents = < > + 0x82000003 &scmi_shm_1 > + 0x82000004 &scmi_shm_2 > + 0x82000005 &scmi_shm_3 > + 0x82000006 &scmi_shm_4>; > + #scmi-secondary-agents-cells = <2>; In the example we should also add Xen own nodes like you did in the commit message. > + }; > + }; > +}; > diff --git a/docs/misc/xen-command-line.pandoc > b/docs/misc/xen-command-line.pandoc > index 7a1f723e63..5a1c1e072c 100644 > --- a/docs/misc/xen-command-line.pandoc > +++ b/docs/misc/xen-command-line.pandoc > @@ -1105,6 +1105,15 @@ which serves as Driver domain. The SCMI will be > disabled for Dom0/hwdom and > SCMI nodes removed from Dom0/hwdom device tree. > (for example, thin Dom0 with Driver domain use-case). > > +### dom0_scmi_agent_id (ARM) > +> `= <integer>` > + > +The option is available when `CONFIG_SCMI_SMC_MA` is compiled in, and allows > to > +enable SCMI functionality for Dom0 by specifying a non-zero ARM SCMI agent > id. > +The SCMI will be disabled for Dom0 if this option is not specified > +(for example, thin Dom0 or dom0less use-cases). > +The agent ids of domains existing on a single host must be unique. Same question about agent id vs. smc-id > ### dtuart (ARM) > > `= path [:options]` > > diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c > index e4407d6e3f..be0e6263ae 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); > diff --git a/tools/libs/light/libxl_types.idl > b/tools/libs/light/libxl_types.idl > index c8bc0f8521..da7f87cd0c 100644 > --- a/tools/libs/light/libxl_types.idl > +++ b/tools/libs/light/libxl_types.idl > @@ -553,11 +553,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 68d2ebeb9f..4c05b20c06 100644 > --- a/tools/xl/xl_parse.c > +++ b/tools/xl/xl_parse.c > @@ -1306,6 +1306,18 @@ 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 || val > 255) { > + fprintf(stderr, "An invalid ARM_SCI agent_id specified > (%lu). Valid range [1..255]\n", > + val); > + ret = ERROR_INVAL; > + goto parse_error; > + } > + arm_sci->agent_id = val; > + } > + > ptr = strtok(NULL, ","); > } > > diff --git a/xen/arch/arm/dom0less-build.c b/xen/arch/arm/dom0less-build.c > index d28143f98b..6ca17b0ce6 100644 > --- a/xen/arch/arm/dom0less-build.c > +++ b/xen/arch/arm/dom0less-build.c > @@ -299,6 +299,17 @@ int __init domu_dt_sci_parse(struct dt_device_node *node, > d_cfg->arch.arm_sci_type = XEN_DOMCTL_CONFIG_ARM_SCI_NONE; > else if ( !strcmp(sci_type, "scmi_smc") ) > 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 ( !dt_property_read_u32(node, "xen,sci_agent_id", &agent_id) || > + !agent_id ) > + 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 92039cf213..ea13e374a0 100644 > --- a/xen/arch/arm/domain_build.c > +++ b/xen/arch/arm/domain_build.c > @@ -508,7 +508,8 @@ static int __init write_properties(struct domain *d, > struct kernel_info *kinfo, > dt_property_name_is_equal(prop, "linux,uefi-mmap-start") || > dt_property_name_is_equal(prop, "linux,uefi-mmap-size") || > dt_property_name_is_equal(prop, > "linux,uefi-mmap-desc-size") || > - dt_property_name_is_equal(prop, "linux,uefi-mmap-desc-ver")) > + dt_property_name_is_equal(prop, "linux,uefi-mmap-desc-ver") > || > + dt_property_name_is_equal(prop, "xen,config") ) > continue; please match by compatible string rather than by node name > if ( dt_property_name_is_equal(prop, "xen,dom0-bootargs") ) > 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..e290d6630d > --- /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) 2024 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))) ffs64 is declared in xen/bitops.h > +#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..8fc8ca356b > --- /dev/null > +++ b/xen/arch/arm/firmware/scmi-shmem.c > @@ -0,0 +1,112 @@ > +/* 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 > + */ > +/* SPDX-License-Identifier: GPL-2.0-only */ > + > +#include <asm/io.h> > +#include <xen/err.h> > +#include <xen/lib/arm/io.h> > + > +#include "scmi-proto.h" > +#include "scmi-shmem.h" > + > +static inline int > +shmem_channel_is_free(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, int len) > +{ > + int ret; > + > + if ( (len + sizeof(shmem->msg_header)) > SCMI_SHMEM_MAPPED_SIZE ) shouldn't this take into account offsetof(struct scmi_shared_mem, msg_header) ? > + { > + printk(XENLOG_ERR "scmi: Wrong size of smc message. Data is > invalid\n"); > + return -EINVAL; > + } > + > + ret = shmem_channel_is_free(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, int len) > +{ > + int recv_len; > + int ret; > + int pad = sizeof(hdr->status); > + > + if ( len >= SCMI_SHMEM_MAPPED_SIZE - sizeof(shmem) ) should this be sizeof(*shmem) ? > + { > + printk(XENLOG_ERR > + "scmi: Wrong size of input smc message. Data may be > invalid\n"); > + return -EINVAL; > + } > + > + ret = shmem_channel_is_free(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..7313cb6b26 > --- /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, int len); > + > +int shmem_get_response(const volatile struct scmi_shared_mem __iomem *shmem, > + scmi_msg_header_t *hdr, void *data, 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..9f839147d4 > --- /dev/null > +++ b/xen/arch/arm/firmware/scmi-smc-multiagent.c > @@ -0,0 +1,803 @@ > +/* 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/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_AGENT_ID_INVALID 0xFF > + > +static uint8_t __initdata opt_dom0_scmi_agent_id = SCMI_AGENT_ID_INVALID; > +integer_param("dom0_scmi_agent_id", opt_dom0_scmi_agent_id); > + > +#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; > + uint64_t len; doesn't seem to be used > + 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 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; > + > + arm_smccc_1_1_smc(chan_info->func_id, 0, 0, 0, 0, 0, 0, 0, &resp); > + > + if ( resp.a0 == 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 && channel->shmem); > + 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; > + > + channel = get_channel_by_id(agent_id); > + if ( channel ) > + return ERR_PTR(EEXIST); > + > + channel = xmalloc(struct scmi_channel); > + if ( !channel ) > + 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); > + return channel; > +} > + > +static void free_channel_list(void) > +{ > + struct scmi_channel *curr, *_curr; > + > + list_for_each_entry_safe(curr, _curr, &scmi_data.channel_list, list) > + { > + list_del(&curr->list); > + xfree(curr); > + } > +} > + > +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 ( ac_spec->np != scmi_data.dt_dev ) > + 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_node_by_path("/chosen/xen,config"); Please use compatible strings, not paths > + if ( !config_node ) > + { > + printk(XENLOG_WARNING "scmi: /chosen/xen,config 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 ) > + { > + /* This is not an error, as there may be no secondary agents. */ > + printk(XENLOG_WARNING "scmi: No %s property found, no agents to > collect.\n", > + SCMI_SECONDARY_AGENTS); > + return -EINVAL; While the comment says this is not a fatal error, returning -EINVAL leads to scmi_probe taking the error path > + } > + > + /* 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; why no "goto error" ? > + } > + > + if ( !IS_ALIGNED(size, SCMI_SHMEM_MAPPED_SIZE) ) should we also check that size % SCMI_SHMEM_MAPPED_SIZE == 0 ? > + { > + 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; > + > + /* > + * Special case for Dom0 - the SCMI support is enabled basing on > + * "dom0_sci_agent_id" Xen command line parameter > + */ > + if ( is_hardware_domain(d) ) > + { > + if ( opt_dom0_scmi_agent_id != SCMI_AGENT_ID_INVALID ) > + { > + config->arch.arm_sci_type = > XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC_MA; > + config->arch.arm_sci_agent_id = opt_dom0_scmi_agent_id; > + } > + else > + config->arch.arm_sci_type = XEN_DOMCTL_CONFIG_ARM_SCI_NONE; > + } > + > + 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 + PAGE_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; > + } > + else if ( config->arch.arm_sci_type == > + XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC_MA && > + config->arch.arm_sci_agent_id == 0 ) > + { > + dprintk(XENLOG_INFO, > + "scmi: A zero ARM SCMI agent_id is not supported\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 = 0; > + > + 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 = true; Should this be 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; > + struct arm_smccc_res resp; > + bool res = false; > + > + if ( !sci_domain_is_enabled(d) ) > + 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_1_1_smc(fid, > + get_user_reg(regs, 1), > + get_user_reg(regs, 2), > + get_user_reg(regs, 3), > + get_user_reg(regs, 4), > + get_user_reg(regs, 5), > + get_user_reg(regs, 6), > + get_user_reg(regs, 7), > + &resp); > + > + set_user_reg(regs, 0, resp.a0); > + set_user_reg(regs, 1, resp.a1); > + set_user_reg(regs, 2, resp.a2); > + set_user_reg(regs, 3, resp.a3); > + 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 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; > + int n_agents; > + scmi_msg_header_t hdr; > + struct scmi_msg_base_attributes_p2a rx; > + > + ASSERT(scmi_node != NULL); > + > + INIT_LIST_HEAD(&scmi_data.channel_list); > + spin_lock_init(&scmi_data.channel_list_lock); > + > + 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; > + > + scmi_data.dt_dev = scmi_node; > + > + channel = smc_create_channel(SCMI_BASE_AGENT_ID_OWN, scmi_data.func_id, > addr); Should we call this with channel_list_lock held? > + if ( IS_ERR(channel) ) > + goto out; > + > + /* Request agent id for Xen management channel */ At this poing channel->domain_id == DOMID_INVALID but collect_agent_id checks for agent_channel->domain_id != DOMID_XEN > + ret = collect_agent_id(channel); > + if ( ret ) > + return ret; > + > + /* Save the agent id for Xen management channel */ > + scmi_data.hyp_channel_agent_id = channel->agent_id; > + > + ret = map_channel_memory(channel); > + if ( ret ) > + goto out; collect_agent_id already called map_channel_memory it looks like the code was supposed to not unmap it the memory if domain_id == DOMID_XEN, although the check doesn't work as expected. > + channel->domain_id = DOMID_XEN; > + > + 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); > + return ret; > + } > + > + 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_PATH("/chosen/xen,config/scmi"), We should match by compatible string, not by path > + { /* 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 095b1a23e3..30e46de6d7 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 > > struct xen_arch_domainconfig { > /* IN/OUT */ > @@ -355,6 +356,8 @@ struct xen_arch_domainconfig { > uint32_t clock_frequency; > /* IN */ > uint8_t arm_sci_type; > + /* IN */ > + uint8_t arm_sci_agent_id; > }; > #endif /* __XEN__ || __XEN_TOOLS__ */ > > -- > 2.34.1 >
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |