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

Re: [RFC PATCH v3 1/7] xen/arm: add generic SCI subsystem


  • To: Stefano Stabellini <sstabellini@xxxxxxxxxx>, Grygorii Strashko <gragst.linux@xxxxxxxxx>
  • From: Grygorii Strashko <grygorii_strashko@xxxxxxxx>
  • Date: Mon, 24 Mar 2025 16:26:05 +0200
  • 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=ev5Yrp6IKkRDOHqOBu41JDi2dZduy87umjASo109ChU=; b=ZWhwI3ibwbhD4y5Jo371rYTJEz909LkuXGQ78tkHsh3ATUSk4uLzyh3qGorjPhoWO3mA02l3GHul/rwG+Kx4c61RSCLexY8LRPnK0X44XPY7g85b03AoP+LMeEW+ZtS81JSW/RhEXwTmNUFT7Z379kVWu95FSMFnYcRGaJSQNVb3ANAQBl928GgCAnLhk6D6ab15wQFEaX2dvuHKd5N3x6laUzkwHif5KpGxa0q/ouX9n4zg6rlb6CgaJAMYw+P+hzLW/7SE7a0TnNZsrXZ2ZUg0Kn10a5FNnWjbWKjTb3/geHPY/9C9Yg/bBV+B4kE5WgmEPg9cv9tD5qYrrt5zZw==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=qsgvGmyGnvnW6Y+C3OLioLo/+MH8YTstEZi+wJlA+mby0GVNbvXDv+jEV2sKijLgdQYRTwJMaHXbzL0XOcdC7tgyWvNw6Ipt+YeLqQ1mGfsc9Xu+pGjF3CsmmDxGnVBDNRkk7ftPfhYfeg11T2tzbDooi4CEUg8JjiJkdzaVek/RT+dM3yz+0Mfi+B7QmMMFL+ZwNo1FPu7r+4uDsO0FJChtM1PQC1MMDyH91yc2UE/38SF0nRITaMhWNK1hno2rja1T26KbmRWMq/BsJFDCoSnhYsIFQlbX0jmJP2BZlKcwACIxdWkdnBGqbMnG9wWPhzpUsnHLTlSAJTMgin1CSA==
  • Authentication-results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=epam.com;
  • Cc: xen-devel@xxxxxxxxxxxxxxxxxxxx, Julien Grall <julien@xxxxxxx>, Andrew Cooper <andrew.cooper3@xxxxxxxxxx>, Michal Orzel <michal.orzel@xxxxxxx>, Roger Pau Monne <roger.pau@xxxxxxxxxx>, Jan Beulich <jbeulich@xxxxxxxx>, Anthony PERARD <anthony.perard@xxxxxxxxxx>, Volodymyr Babchuk <Volodymyr_Babchuk@xxxxxxxx>, Oleksii Moisieiev <oleksii_moisieiev@xxxxxxxx>, Bertrand Marquis <bertrand.marquis@xxxxxxx>, Juergen Gross <jgross@xxxxxxxx>
  • Delivery-date: Mon, 24 Mar 2025 14:26:12 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

Hi Stefano,

Thanks for your comments.

On 14.03.25 01:48, Stefano Stabellini wrote:
On Tue, 11 Mar 2025, Grygorii Strashko wrote:
From: Oleksii Moisieiev <oleksii_moisieiev@xxxxxxxx>

This patch adds the basic framework for ARM SCI mediator. SCI is System
Control Interface, which is designed to redirect requests from the Domains
to ARM specific Firmware (for example SCMI). This will allow the devices,
passed-through to the different Domains, to access to the System resources
(such as clocks/resets etc) by sending requests to the firmware.

ARM SCI subsystem allows to implement different SCI drivers to handle
specific ARM firmware interfaces (like ARM SCMI) and mediate requests
between the Domains and the Firmware. Also it allows SCI drivers to perform
proper action during Domain creation/destruction which is vital for
handling use cases like Domain reboot.

This patch introduces new DEVICE_ARM_SCI device subclass for probing SCI
drivers basing on device tree, SCI drivers register itself with
DT_DEVICE_START/END macro. On init - the SCI drivers should register its
SCI ops with sci_register(). Only one SCI driver can be supported.

At run-time, the following SCI API calls are introduced:

- sci_domain_sanitise_config() called from arch_sanitise_domain_config()
- sci_domain_init() called from arch_domain_create()
- sci_relinquish_resources() called from domain_relinquish_resources()
- sci_domain_destroy() called from arch_domain_destroy()
- sci_handle_call() called from vsmccc_handle_call()
- sci_dt_handle_node()
   sci_dt_finalize() called from handle_node() (Dom0 DT)

Signed-off-by: Oleksii Moisieiev <oleksii_moisieiev@xxxxxxxx>
Signed-off-by: Grygorii Strashko <grygorii_strashko@xxxxxxxx>
---
  MAINTAINERS                             |   6 +
  xen/arch/arm/device.c                   |   5 +
  xen/arch/arm/dom0less-build.c           |  13 ++
  xen/arch/arm/domain.c                   |  12 +-
  xen/arch/arm/domain_build.c             |   8 +
  xen/arch/arm/firmware/Kconfig           |   8 +
  xen/arch/arm/firmware/Makefile          |   1 +
  xen/arch/arm/firmware/sci.c             | 187 +++++++++++++++++++++
  xen/arch/arm/include/asm/domain.h       |   5 +
  xen/arch/arm/include/asm/firmware/sci.h | 214 ++++++++++++++++++++++++
  xen/arch/arm/vsmc.c                     |   3 +
  xen/common/domctl.c                     |  13 ++
  xen/drivers/passthrough/device_tree.c   |   7 +
  xen/include/asm-generic/device.h        |   1 +
  xen/include/public/arch-arm.h           |   4 +
  15 files changed, 486 insertions(+), 1 deletion(-)
  create mode 100644 xen/arch/arm/firmware/sci.c
  create mode 100644 xen/arch/arm/include/asm/firmware/sci.h


[...]

+/*
+ * Generic part of the SCI (System Control Interface) subsystem.
+ *
+ * Oleksii Moisieiev <oleksii_moisieiev@xxxxxxxx>
+ * Copyright (c) 2025 EPAM Systems
+ */
+
+#include <xen/acpi.h>
+#include <xen/errno.h>
+#include <xen/init.h>
+#include <xen/sched.h>
+#include <xen/types.h>
+
+#include <asm/firmware/sci.h>
+
+static const struct sci_mediator_ops __read_mostly *cur_mediator;
+
+int sci_register(const struct sci_mediator_ops *ops)
+{
+    if ( cur_mediator )
+        return -EEXIST;
+
+    if ( !ops->domain_init || !ops->domain_destroy || !ops->handle_call )
+        return -EINVAL;
+
+    cur_mediator = ops;
+
+    return 0;
+};
+
+bool sci_handle_call(struct cpu_user_regs *args)
+{
+    if ( unlikely(!cur_mediator) )
+        return false;
+
+    return cur_mediator->handle_call(args);
+}
+
+int sci_domain_init(struct domain *d, struct xen_domctl_createdomain *config)
+{
+    if ( !cur_mediator )
+        return 0;
+
+    return cur_mediator->domain_init(d, config);
+}
+
+int sci_domain_sanitise_config(struct xen_domctl_createdomain *config)
+{
+    if ( !cur_mediator )
+        return 0;
+
+    if ( !cur_mediator->domain_sanitise_config )
+        return 0;
+
+    return cur_mediator->domain_sanitise_config(config);
+}
+
+void sci_domain_destroy(struct domain *d)
+{
+    if ( !cur_mediator )
+        return;
+
+    cur_mediator->domain_destroy(d);
+}
+
+int sci_relinquish_resources(struct domain *d)
+{
+    if ( !cur_mediator )
+        return 0;
+
+    if ( !cur_mediator->relinquish_resources )
+        return 0;
+
+    return cur_mediator->relinquish_resources(d);
+}
+
+bool sci_dt_handle_node(struct domain *d, struct dt_device_node *node)
+{
+    if ( !cur_mediator )
+        return 0;
+
+    if ( !cur_mediator->dom0_dt_handle_node )
+        return 0;
+
+    return cur_mediator->dom0_dt_handle_node(d, node);
+}
+
+int sci_dt_finalize(struct domain *d, void *fdt)
+{
+    if ( !cur_mediator )
+        return 0;
+
+    if ( !cur_mediator->dom0_dt_finalize )
+        return 0;
+
+    return cur_mediator->dom0_dt_finalize(d, fdt);
+}
+
+int sci_assign_dt_device(struct domain *d, struct dt_device_node *dev)
+{
+    struct dt_phandle_args ac_spec;
+    int index = 0;
+    int ret;
+
+    if ( !cur_mediator )
+        return 0;
+
+    if ( !cur_mediator->assign_dt_device )
+        return 0;
+
+    while ( !dt_parse_phandle_with_args(dev, "access-controllers",
+                                        "#access-controller-cells", index,
+                                        &ac_spec) )
+    {
+        printk(XENLOG_DEBUG "sci: assign device %s to %pd\n",
+               dt_node_full_name(dev), d);
+
+        ret = cur_mediator->assign_dt_device(d, &ac_spec);
+        if ( ret )
+            return ret;

I am confused by this: we are passing a reference to the controller
rather than to the device to be assigned?

At this moment the Host DT is parsed as specified by access controllers bindings

        access-controllers = <&sci_fw [parameters]>;

and "ac_spec" contains
- np : access controller node, which should correspond SCI FW device node if it 
serves as
  access controller.
- args_count/args : DT device parameters to be passed to access controllers

In case of, SCI SCMI that's enough to perform device assignment - args[0] == 
device_id




+        index++;
+    }
+
+    return 0;
+}
+
+int sci_do_domctl(struct xen_domctl *domctl, struct domain *d,
+                  XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl)
+{
+    struct dt_device_node *dev;
+    int ret = 0;
+
+    switch ( domctl->cmd )
+    {
+    case XEN_DOMCTL_assign_device:
+        if ( domctl->u.assign_device.dev != XEN_DOMCTL_DEV_DT )
+        {
+            ret = -EINVAL;
+            break;
+        }
+
+        ret = dt_find_node_by_gpath(domctl->u.assign_device.u.dt.path,
+                                    domctl->u.assign_device.u.dt.size, &dev);
+        if ( ret )
+            return ret;
+
+        ret = sci_assign_dt_device(d, dev);
+        if ( ret )
+            break;
+
+        break;
+    default:
+        /* do not fail here as call is chained with iommu handling */
+        break;
+    }
+
+    return ret;
+}
+
+static int __init sci_init(void)
+{
+    struct dt_device_node *np;
+    unsigned int num_sci = 0;
+    int rc;
+
+    dt_for_each_device_node(dt_host, np)
+    {
+        rc = device_init(np, DEVICE_ARM_SCI, NULL);
+        if ( !rc && num_sci )
+        {
+            printk(XENLOG_ERR
+                   "SCMI: Only one SCI controller is supported. found second 
%s\n",
+                   np->name);
+            return -EOPNOTSUPP;
+        }
+        else if ( !rc )
+            num_sci++;
+        else if ( rc != -EBADF && rc != -ENODEV )
+            return rc;
+    }
+
+    return 0;
+}
+
+__initcall(sci_init);
diff --git a/xen/arch/arm/include/asm/domain.h 
b/xen/arch/arm/include/asm/domain.h
index f1d72c6e48df..fa0898b7cf80 100644
--- a/xen/arch/arm/include/asm/domain.h
+++ b/xen/arch/arm/include/asm/domain.h
@@ -118,6 +118,11 @@ struct arch_domain
  #ifdef CONFIG_TEE
      void *tee;
  #endif
+#ifdef CONFIG_ARM_SCI
+    bool sci_enabled;
+    /* ARM SCI driver's specific data */
+    void *sci_data;
+#endif
} __cacheline_aligned;

[...]

diff --git a/xen/arch/arm/vsmc.c b/xen/arch/arm/vsmc.c
index 62d8117a120c..51b3c0297314 100644
--- a/xen/arch/arm/vsmc.c
+++ b/xen/arch/arm/vsmc.c
@@ -12,6 +12,7 @@
  #include <public/arch-arm/smccc.h>
  #include <asm/cpuerrata.h>
  #include <asm/cpufeature.h>
+#include <asm/firmware/sci.h>
  #include <asm/monitor.h>
  #include <asm/regs.h>
  #include <asm/smccc.h>
@@ -300,6 +301,8 @@ static bool vsmccc_handle_call(struct cpu_user_regs *regs)
              break;
          case ARM_SMCCC_OWNER_SIP:
              handled = handle_sip(regs);
+            if ( !handled )
+                handled = sci_handle_call(regs);

Isn't there a proper funcid range for SCI calls?

Unfortunately no. The ARM DEN 0028B "SMC CALLING CONVENTION" spec only defines 
the
range

Table 6-2
0x82000000-0x8200FFFF SMC32: SiP Service Calls
0xC2000000-0xC200FFFF SMC64: SiP Service Calls

And in Linux Kernel mainline I can see:

./arm64/boot/dts/freescale/s32g2.dtsi:          arm,smc-id = <0xc20000fe>;
./arm64/boot/dts/freescale/s32g3.dtsi:          arm,smc-id = <0xc20000fe>;
./arm64/boot/dts/freescale/imx8ulp.dtsi:        arm,smc-id = <0xc20000fe>;
./arm64/boot/dts/amlogic/amlogic-c3.dtsi:       arm,smc-id = <0x820000C1>;
./arm64/boot/dts/st/stm32mp251.dtsi:            arm,smc-id = <0xb200005a>;
./arm64/boot/dts/blaize/blaize-blzp1600.dtsi:   arm,smc-id = <0x82002000>;
./arm64/boot/dts/rockchip/rk356x-base.dtsi:     arm,smc-id = <0x82000010>;
./arm64/boot/dts/rockchip/rk3588-base.dtsi:     arm,smc-id = <0x82000010>;
./arm64/boot/dts/rockchip/rk3576.dtsi:          arm,smc-id = <0x82000010>;




              break;
          case ARM_SMCCC_OWNER_TRUSTED_APP ... ARM_SMCCC_OWNER_TRUSTED_APP_END:
          case ARM_SMCCC_OWNER_TRUSTED_OS ... ARM_SMCCC_OWNER_TRUSTED_OS_END:
diff --git a/xen/common/domctl.c b/xen/common/domctl.c
index 05abb581a03d..b48ad20a6e2b 100644
--- a/xen/common/domctl.c
+++ b/xen/common/domctl.c
@@ -27,6 +27,7 @@
  #include <xen/vm_event.h>
  #include <xen/monitor.h>
  #include <asm/current.h>
+#include <asm/firmware/sci.h>
  #include <asm/irq.h>
  #include <asm/page.h>
  #include <asm/p2m.h>
@@ -851,6 +852,18 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) 
u_domctl)
      case XEN_DOMCTL_deassign_device:
      case XEN_DOMCTL_get_device_group:
          ret = iommu_do_domctl(op, d, u_domctl);
+
+        if ( ret >= 0 || (ret == -EOPNOTSUPP) || (ret == -ENODEV) )
+        {
+            /*
+             * TODO: RFC
+             * This change will allow to pass DT nodes/devices to
+             * XEN_DOMCTL_assign_device OP using xl.cfg:"dtdev" property even
+             * if those DT nodes/devices even are not behind IOMMU (or IOMMU
+             * is disabled) without failure.
+             */
+            ret = sci_do_domctl(op, d, u_domctl);
+        }
          break;
case XEN_DOMCTL_get_paging_mempool_size:
diff --git a/xen/drivers/passthrough/device_tree.c 
b/xen/drivers/passthrough/device_tree.c
index 075fb25a3706..f2ee0a72f541 100644
--- a/xen/drivers/passthrough/device_tree.c
+++ b/xen/drivers/passthrough/device_tree.c
@@ -318,6 +318,13 @@ int iommu_do_dt_domctl(struct xen_domctl *domctl, struct 
domain *d,
              break;
          }
+ /* TODO: RFC allow assignment of devices without IOMMU protection. */
+        if ( !dt_device_is_protected(dev) )
+        {
+            ret = 0;
+            break;
+        }

This should not be needed, there is a similar check at the beginning of
iommu_assign_dt_device

Unfortunately no, as iommu_assign_dt_device() checks it as

    if ( !dt_device_is_protected(dev) )
        return -EINVAL;

and returns -EINVAL for DT devices which are not IOMMU-protected,
while iommu_add_dt_device() returns 1 in such cases (few lines above).
Therefore this change which in combination with do_domctl() change allows to
pass DT devices in xl.cfg:"dtdev" for processing.

In general, there are three places where DT IOMMU is configured for ARM

1) arch\arm\device.c handle_device() - used for Dom0/hwdom init and dt-overlays
2) arch\arm\dom0less-build.c handle_passthrough_prop() - used for dom0less DT 
devices pass through
3) drivers/passthrough/device_tree.c iommu_do_dt_domctl() - above

In cases (1) and (2), the code will not fail if DT device is not 
IOMMU-protected and
there is the following calling pattern:

        res = iommu_add_dt_device(dev);
        if ( res < 0 )
            return res;

// if DT device is not IOMMU-protected res == 1
// and dt_device_is_protected(dev) == false

        if ( !dt_device_is_protected(dev) )
                return 0;

        res = iommu_assign_dt_device(d, dev);


The case (3), reviewed here, has different calling pattern:
iommu_do_dt_domctl():
        ret = iommu_add_dt_device(dev);
        if ( ret < 0 )
           return ret;

// if DT device is not IOMMU-protected ret == 1
// and dt_device_is_protected(dev) == false

        ret = iommu_assign_dt_device(d, dev);

and will always fail if DT device is not IOMMU-protected.




          ret = iommu_assign_dt_device(d, dev);
if ( ret )

[...]

--
Best regards,
-grygorii



 


Rackspace

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