* [RFC PATCH v3 1/7] xen/arm: add generic SCI subsystem
2025-03-11 11:16 [RFC PATCH v3 0/7] xen/arm: scmi: introduce SCI SCMI SMC multi-agent support Grygorii Strashko
@ 2025-03-11 11:16 ` Grygorii Strashko
2025-03-11 11:43 ` Jan Beulich
2025-03-13 23:48 ` Stefano Stabellini
2025-03-11 11:16 ` [RFC PATCH v3 2/7] xen/arm: scmi-smc: update to be used under sci subsystem Grygorii Strashko
` (6 subsequent siblings)
7 siblings, 2 replies; 30+ messages in thread
From: Grygorii Strashko @ 2025-03-11 11:16 UTC (permalink / raw)
To: xen-devel
Cc: Stefano Stabellini, Julien Grall, Andrew Cooper, Michal Orzel,
Roger Pau Monne, Jan Beulich, Anthony PERARD, Volodymyr Babchuk,
Oleksii Moisieiev, Bertrand Marquis, Juergen Gross,
Grygorii Strashko
From: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
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@epam.com>
Signed-off-by: Grygorii Strashko <grygorii_strashko@epam.com>
---
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
diff --git a/MAINTAINERS b/MAINTAINERS
index c11b82eca98f..c0e8143dca63 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -526,6 +526,12 @@ S: Supported
F: xen/arch/arm/include/asm/tee/
F: xen/arch/arm/tee/
+SCI MEDIATORS
+M: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
+S: Supported
+F: xen/arch/arm/sci
+F: xen/include/asm-arm/sci
+
TOOLSTACK
M: Anthony PERARD <anthony.perard@vates.tech>
S: Supported
diff --git a/xen/arch/arm/device.c b/xen/arch/arm/device.c
index 5610cddcba8e..bdab96a408c4 100644
--- a/xen/arch/arm/device.c
+++ b/xen/arch/arm/device.c
@@ -13,6 +13,7 @@
#include <xen/iocap.h>
#include <xen/lib.h>
+#include <asm/firmware/sci.h>
#include <asm/setup.h>
int map_irq_to_domain(struct domain *d, unsigned int irq,
@@ -303,6 +304,10 @@ int handle_device(struct domain *d, struct dt_device_node *dev, p2m_type_t p2mt,
return res;
}
}
+
+ res = sci_assign_dt_device(d, dev);
+ if ( res )
+ return res;
}
res = map_device_irqs_to_domain(d, dev, own_device, irq_ranges);
diff --git a/xen/arch/arm/dom0less-build.c b/xen/arch/arm/dom0less-build.c
index 49d1f14d659b..c463ab3eaca5 100644
--- a/xen/arch/arm/dom0less-build.c
+++ b/xen/arch/arm/dom0less-build.c
@@ -14,6 +14,7 @@
#include <asm/arm64/sve.h>
#include <asm/dom0less-build.h>
#include <asm/domain_build.h>
+#include <asm/firmware/sci.h>
#include <asm/static-memory.h>
#include <asm/static-shmem.h>
@@ -321,6 +322,10 @@ static int __init handle_passthrough_prop(struct kernel_info *kinfo,
return -EINVAL;
}
+ res = sci_assign_dt_device(kinfo->d, node);
+ if ( res )
+ return res;
+
res = map_device_irqs_to_domain(kinfo->d, node, true, NULL);
if ( res < 0 )
return res;
@@ -970,6 +975,14 @@ void __init create_domUs(void)
if ( !llc_coloring_enabled && llc_colors_str )
panic("'llc-colors' found, but LLC coloring is disabled\n");
+ /*
+ * TODO: enable ARM SCI for dom0less case
+ * The configuration need to be retrieved from DT
+ * - arch.arm_sci_type, like "xen,sci_type"
+ * - arch.arm_sci_agent_id, like "xen,sci_agent_id"
+ */
+ d_cfg.arch.arm_sci_type = XEN_DOMCTL_CONFIG_ARM_SCI_NONE;
+
/*
* The variable max_init_domid is initialized with zero, so here it's
* very important to use the pre-increment operator to call
diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c
index 3ba959f86633..652aeb7a55de 100644
--- a/xen/arch/arm/domain.c
+++ b/xen/arch/arm/domain.c
@@ -25,6 +25,7 @@
#include <asm/platform.h>
#include <asm/procinfo.h>
#include <asm/regs.h>
+#include <asm/firmware/sci.h>
#include <asm/tee/tee.h>
#include <asm/vfp.h>
#include <asm/vgic.h>
@@ -694,7 +695,7 @@ int arch_sanitise_domain_config(struct xen_domctl_createdomain *config)
return -EINVAL;
}
- return 0;
+ return sci_domain_sanitise_config(config);
}
int arch_domain_create(struct domain *d,
@@ -786,6 +787,9 @@ int arch_domain_create(struct domain *d,
d->arch.sve_vl = config->arch.sve_vl;
#endif
+ if ( (rc = sci_domain_init(d, config)) != 0 )
+ goto fail;
+
return 0;
fail:
@@ -846,6 +850,7 @@ void arch_domain_destroy(struct domain *d)
domain_vgic_free(d);
domain_vuart_free(d);
free_xenheap_page(d->shared_info);
+ sci_domain_destroy(d);
#ifdef CONFIG_ACPI
free_xenheap_pages(d->arch.efi_acpi_table,
get_order_from_bytes(d->arch.efi_acpi_len));
@@ -1039,6 +1044,7 @@ enum {
PROG_p2m_root,
PROG_p2m,
PROG_p2m_pool,
+ PROG_sci,
PROG_done,
};
@@ -1098,6 +1104,10 @@ int domain_relinquish_resources(struct domain *d)
ret = relinquish_p2m_mapping(d);
if ( ret )
return ret;
+ PROGRESS(sci):
+ ret = sci_relinquish_resources(d);
+ if ( ret )
+ return ret;
PROGRESS(p2m_root):
/*
diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
index 7b47abade196..36d28b52a418 100644
--- a/xen/arch/arm/domain_build.c
+++ b/xen/arch/arm/domain_build.c
@@ -24,6 +24,7 @@
#include <asm/setup.h>
#include <asm/tee/tee.h>
#include <asm/pci.h>
+#include <asm/firmware/sci.h>
#include <asm/platform.h>
#include <asm/psci.h>
#include <asm/setup.h>
@@ -1888,6 +1889,9 @@ static int __init handle_node(struct domain *d, struct kernel_info *kinfo,
return 0;
}
+ if ( sci_dt_handle_node(d, node) )
+ return 0;
+
/*
* The vGIC does not support routing hardware PPIs to guest. So
* we need to skip any node using PPIs.
@@ -1988,6 +1992,10 @@ static int __init handle_node(struct domain *d, struct kernel_info *kinfo,
if ( res )
return res;
+ res = sci_dt_finalize(d, kinfo->fdt);
+ if ( res )
+ return res;
+
/*
* Create a second memory node to store the ranges covering
* reserved-memory regions.
diff --git a/xen/arch/arm/firmware/Kconfig b/xen/arch/arm/firmware/Kconfig
index 817da745fd43..fc7918c7fc56 100644
--- a/xen/arch/arm/firmware/Kconfig
+++ b/xen/arch/arm/firmware/Kconfig
@@ -1,3 +1,11 @@
+config ARM_SCI
+ bool
+ depends on ARM
+ help
+ This option enables generic Arm SCI (System Control Interface) mediators
+ support. It allows domains to control system resources via one of
+ Arm SCI mediators drivers implemented in XEN, like SCMI.
+
menu "Firmware Drivers"
config SCMI_SMC
diff --git a/xen/arch/arm/firmware/Makefile b/xen/arch/arm/firmware/Makefile
index a5e454266625..71bdefc24afb 100644
--- a/xen/arch/arm/firmware/Makefile
+++ b/xen/arch/arm/firmware/Makefile
@@ -1 +1,2 @@
+obj-$(CONFIG_ARM_SCI) += sci.o
obj-$(CONFIG_SCMI_SMC) += scmi-smc.o
diff --git a/xen/arch/arm/firmware/sci.c b/xen/arch/arm/firmware/sci.c
new file mode 100644
index 000000000000..1fb6b55e030a
--- /dev/null
+++ b/xen/arch/arm/firmware/sci.c
@@ -0,0 +1,187 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Generic part of the SCI (System Control Interface) subsystem.
+ *
+ * Oleksii Moisieiev <oleksii_moisieiev@epam.com>
+ * 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;
+
+ 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/include/asm/firmware/sci.h b/xen/arch/arm/include/asm/firmware/sci.h
new file mode 100644
index 000000000000..4c6f624723c5
--- /dev/null
+++ b/xen/arch/arm/include/asm/firmware/sci.h
@@ -0,0 +1,214 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Generic ARM SCI (System Control Interface) subsystem.
+ *
+ * Oleksii Moisieiev <oleksii_moisieiev@epam.com>
+ * Copyright (c) 2025 EPAM Systems
+ */
+
+#ifndef __ASM_ARM_SCI_H
+#define __ASM_ARM_SCI_H
+
+#include <xen/lib.h>
+#include <xen/types.h>
+#include <xen/device_tree.h>
+#include <xen/errno.h>
+#include <xen/sched.h>
+
+#ifdef CONFIG_ARM_SCI
+
+struct sci_mediator_ops {
+ /*
+ * Called during domain construction. If it is requested to enable
+ * SCI support, so SCI driver can create own structures for the new domain
+ * and inform firmware about new domain (if required).
+ * Mandatory.
+ */
+ int (*domain_init)(struct domain *d,
+ struct xen_domctl_createdomain *config);
+
+ /*
+ * Called during domain construction. The SCI driver uses
+ * it to sanitize domain SCI configuration parameters.
+ * Optional.
+ */
+ int (*domain_sanitise_config)(struct xen_domctl_createdomain *config);
+
+ /*
+ * Called during domain destruction, releases all resources, that
+ * were allocated for domain.
+ * Mandatory.
+ */
+ void (*domain_destroy)(struct domain *d);
+
+ /*
+ * Called during domain destruction to relinquish resources used
+ * by SCI driver itself and request resources releasing from firmware.
+ * Optional.
+ */
+ int (*relinquish_resources)(struct domain *d);
+
+ /* SMC/HVC Handle callback */
+ bool (*handle_call)(struct cpu_user_regs *regs);
+
+ /*
+ * Dom0 DT nodes handling callback so SCI driver can detect DT nodes it
+ * need to handle and decide if those nodes need to be provided to Dom0.
+ * Optional.
+ */
+ bool (*dom0_dt_handle_node)(struct domain *d, struct dt_device_node *node);
+
+ /*
+ * SCI driver callback called at the end of Dom0 DT generation, so
+ * it can perform steps to modify DT to enable/disable SCI
+ * functionality for Dom0.
+ */
+ int (*dom0_dt_finalize)(struct domain *d, void *fdt);
+
+ /*
+ * SCI driver callback called when DT device is passed through to guest,
+ * so SCI driver can enable device access to the domain if SCI FW provides
+ * Device specific access control functionality.
+ * Optional.
+ */
+ int (*assign_dt_device)(struct domain *d, struct dt_phandle_args *ac_spec);
+};
+
+
+static inline bool sci_domain_is_enabled(struct domain *d)
+{
+ return d->arch.sci_enabled;
+}
+
+/*
+ * Register SCI subsystem ops.
+ *
+ * Register SCI drivers operation and so enable SCI functionality.
+ * Only one SCI driver is supported.
+ */
+int sci_register(const struct sci_mediator_ops *ops);
+
+/*
+ * Initialize SCI functionality for domain if configured.
+ *
+ * Initialization routine to enable SCI functionality for the domain.
+ * The SCI configuration data and decision about enabling SCI functionality
+ * for the domain is SCI driver specific.
+ */
+int sci_domain_init(struct domain *d, struct xen_domctl_createdomain *config);
+
+/*
+ * Sanitise domain configuration parameters.
+ *
+ */
+int sci_domain_sanitise_config(struct xen_domctl_createdomain *config);
+
+/*
+ * Destroy SCI domain instance.
+ */
+void sci_domain_destroy(struct domain *d);
+
+/*
+ * Free resources assigned to the certain domain.
+ */
+int sci_relinquish_resources(struct domain *d);
+
+/*
+ * SMC/HVC Handle callback.
+ *
+ * SCI driver acts as SMC/HVC server for the registered domains and
+ * does redirection of the domain calls to the SCI firmware,
+ * such as ARM TF-A or similar.
+ */
+bool sci_handle_call(struct cpu_user_regs *regs);
+
+/*
+ * Dom0 DT nodes handling function.
+ *
+ * Allows SCI driver to detect DT nodes it need to handle and decide if
+ * those nodes need to be provided to Dom0.
+ */
+bool sci_dt_handle_node(struct domain *d, struct dt_device_node *node);
+
+/*
+ * Dom0 DT generation finalize.
+ *
+ * Called at the end of Dom0 DT generation, so SCI driver can perform steps
+ * to modify DT to enable/disable SCI functionality for Dom0.
+ */
+int sci_dt_finalize(struct domain *d, void *fdt);
+
+/*
+ * Assign DT device to domain.
+ *
+ * Called when DT device is passed through to guest, so SCI driver can enable
+ * device access to the domain if SCI FW provides "Device specific access
+ * control" functionality.
+ */
+int sci_assign_dt_device(struct domain *d, struct dt_device_node *dev);
+
+/*
+ * SCI domctl handler
+ *
+ * Only XEN_DOMCTL_assign_device is handled for now.
+ */
+int sci_do_domctl(struct xen_domctl *domctl, struct domain *d,
+ XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl);
+#else
+
+static inline bool sci_domain_is_enabled(struct domain *d)
+{
+ return false;
+}
+
+static inline int sci_domain_init(struct domain *d,
+ struct xen_domctl_createdomain *config)
+{
+ return 0;
+}
+
+static inline int
+sci_domain_sanitise_config(struct xen_domctl_createdomain *config)
+{
+ return 0;
+}
+
+static inline void sci_domain_destroy(struct domain *d)
+{}
+
+static inline int sci_relinquish_resources(struct domain *d)
+{
+ return 0;
+}
+
+static inline bool sci_handle_call(struct cpu_user_regs *args)
+{
+ return false;
+}
+
+static inline bool sci_dt_handle_node(struct domain *d,
+ struct dt_device_node *node)
+{
+ return false;
+}
+
+static inline int sci_dt_finalize(struct domain *d, void *fdt)
+{
+ return false;
+}
+
+static inline int sci_assign_dt_device(struct domain *d,
+ struct dt_device_node *dev)
+{
+ return 0;
+}
+
+static inline int sci_do_domctl(struct xen_domctl *domctl, struct domain *d,
+ XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl)
+{
+ return 0;
+}
+
+#endif /* CONFIG_ARM_SCI */
+
+#endif /* __ASM_ARM_SCI_H */
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);
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;
+ }
+
ret = iommu_assign_dt_device(d, dev);
if ( ret )
diff --git a/xen/include/asm-generic/device.h b/xen/include/asm-generic/device.h
index 1acd1ba1d8b4..9fcdabfda9bc 100644
--- a/xen/include/asm-generic/device.h
+++ b/xen/include/asm-generic/device.h
@@ -18,6 +18,7 @@ enum device_class
DEVICE_IOMMU,
DEVICE_INTERRUPT_CONTROLLER,
DEVICE_PCI_HOSTBRIDGE,
+ DEVICE_ARM_SCI,
/* Use for error */
DEVICE_UNKNOWN,
};
diff --git a/xen/include/public/arch-arm.h b/xen/include/public/arch-arm.h
index 24840eeaa666..55eed9992c9d 100644
--- a/xen/include/public/arch-arm.h
+++ b/xen/include/public/arch-arm.h
@@ -327,6 +327,8 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t);
#define XEN_DOMCTL_CONFIG_TEE_OPTEE 1
#define XEN_DOMCTL_CONFIG_TEE_FFA 2
+#define XEN_DOMCTL_CONFIG_ARM_SCI_NONE 0
+
struct xen_arch_domainconfig {
/* IN/OUT */
uint8_t gic_version;
@@ -350,6 +352,8 @@ struct xen_arch_domainconfig {
*
*/
uint32_t clock_frequency;
+ /* IN */
+ uint8_t arm_sci_type;
};
#endif /* __XEN__ || __XEN_TOOLS__ */
--
2.34.1
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [RFC PATCH v3 1/7] xen/arm: add generic SCI subsystem
2025-03-11 11:16 ` [RFC PATCH v3 1/7] xen/arm: add generic SCI subsystem Grygorii Strashko
@ 2025-03-11 11:43 ` Jan Beulich
2025-03-24 9:00 ` Oleksii Moisieiev
2025-03-24 14:25 ` Grygorii Strashko
2025-03-13 23:48 ` Stefano Stabellini
1 sibling, 2 replies; 30+ messages in thread
From: Jan Beulich @ 2025-03-11 11:43 UTC (permalink / raw)
To: Grygorii Strashko
Cc: Stefano Stabellini, Julien Grall, Andrew Cooper, Michal Orzel,
Roger Pau Monne, Anthony PERARD, Volodymyr Babchuk,
Oleksii Moisieiev, Bertrand Marquis, Juergen Gross,
Grygorii Strashko, xen-devel
On 11.03.2025 12:16, Grygorii Strashko wrote:
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -526,6 +526,12 @@ S: Supported
> F: xen/arch/arm/include/asm/tee/
> F: xen/arch/arm/tee/
>
> +SCI MEDIATORS
> +M: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
> +S: Supported
> +F: xen/arch/arm/sci
> +F: xen/include/asm-arm/sci
> +
> TOOLSTACK
> M: Anthony PERARD <anthony.perard@vates.tech>
> S: Supported
Please can you respect alphabetical sorting in this file?
> @@ -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;
Despite the comment I fear I don't understand what you're trying to do here.
In any event you may not clobber the original "ret", if all you do is some
"add-on".
> --- a/xen/include/asm-generic/device.h
> +++ b/xen/include/asm-generic/device.h
> @@ -18,6 +18,7 @@ enum device_class
> DEVICE_IOMMU,
> DEVICE_INTERRUPT_CONTROLLER,
> DEVICE_PCI_HOSTBRIDGE,
> + DEVICE_ARM_SCI,
> /* Use for error */
> DEVICE_UNKNOWN,
This is a generic header - I don't see how anything arch-specific could
validly end up here.
> --- a/xen/include/public/arch-arm.h
> +++ b/xen/include/public/arch-arm.h
> @@ -327,6 +327,8 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t);
> #define XEN_DOMCTL_CONFIG_TEE_OPTEE 1
> #define XEN_DOMCTL_CONFIG_TEE_FFA 2
>
> +#define XEN_DOMCTL_CONFIG_ARM_SCI_NONE 0
> +
> struct xen_arch_domainconfig {
> /* IN/OUT */
> uint8_t gic_version;
> @@ -350,6 +352,8 @@ struct xen_arch_domainconfig {
> *
> */
> uint32_t clock_frequency;
> + /* IN */
> + uint8_t arm_sci_type;
> };
You're not re-using a pre-existing padding field, so I don't see how you
can get away without bumping XEN_DOMCTL_INTERFACE_VERSION.
Jan
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [RFC PATCH v3 1/7] xen/arm: add generic SCI subsystem
2025-03-11 11:43 ` Jan Beulich
@ 2025-03-24 9:00 ` Oleksii Moisieiev
2025-03-24 10:05 ` Jan Beulich
2025-03-24 14:25 ` Grygorii Strashko
1 sibling, 1 reply; 30+ messages in thread
From: Oleksii Moisieiev @ 2025-03-24 9:00 UTC (permalink / raw)
To: Jan Beulich, Grygorii Strashko
Cc: Stefano Stabellini, Julien Grall, Andrew Cooper, Michal Orzel,
Roger Pau Monne, Anthony PERARD, Volodymyr Babchuk,
Bertrand Marquis, Juergen Gross, Grygorii Strashko,
xen-devel@lists.xenproject.org
Hi Jan,
Let me answer one of your comment. Please see below:
On 11/03/2025 13:43, Jan Beulich wrote:
> On 11.03.2025 12:16, Grygorii Strashko wrote:
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -526,6 +526,12 @@ S: Supported
>> F: xen/arch/arm/include/asm/tee/
[snip]
>> --- a/xen/include/public/arch-arm.h
>> +++ b/xen/include/public/arch-arm.h
>> @@ -327,6 +327,8 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t);
>> #define XEN_DOMCTL_CONFIG_TEE_OPTEE 1
>> #define XEN_DOMCTL_CONFIG_TEE_FFA 2
>>
>> +#define XEN_DOMCTL_CONFIG_ARM_SCI_NONE 0
>> +
>> struct xen_arch_domainconfig {
>> /* IN/OUT */
>> uint8_t gic_version;
>> @@ -350,6 +352,8 @@ struct xen_arch_domainconfig {
>> *
>> */
>> uint32_t clock_frequency;
>> + /* IN */
>> + uint8_t arm_sci_type;
>> };
> You're not re-using a pre-existing padding field, so I don't see how you
> can get away without bumping XEN_DOMCTL_INTERFACE_VERSION.
>
> Jan
We are reusing an existing padding field in xen_domctl, which is defined
as pad[128].
The xen_arch_domainconfig structure is a part of the following domctl
structures:
- xen_domctl_createdomain
- xen_domctl_getdomaininfo
These structures are included in the union within xen_domctl, which
defines pad[128] for padding.
In the following example, I used the pahole tool to inspect the current
structure sizes for the
xen_domctl structure:
> pahole xen-syms -C xen_domctl
The result is as follows:
struct xen_domctl {
uint32_t cmd; /* 0 4 */
uint32_t interface_version; /* 4
4 */
domid_t domain; /* 8 2 */
uint16_t _pad[3]; /* 10
6 */
union {
struct xen_domctl_createdomain createdomain; /*
16 76 */
struct xen_domctl_getdomaininfo getdomaininfo
__attribute__((__aligned__(8))); /* 16 120 */
...
uint8_t pad[128]; /* 16 128 */
};
Both structures, xen_domctl_createdomain and xen_domctl_getdomaininfo,
do not exceed the size of the 128-byte padding.
WBR,
Oleksii.
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [RFC PATCH v3 1/7] xen/arm: add generic SCI subsystem
2025-03-24 9:00 ` Oleksii Moisieiev
@ 2025-03-24 10:05 ` Jan Beulich
2025-03-24 13:11 ` Oleksii Moisieiev
0 siblings, 1 reply; 30+ messages in thread
From: Jan Beulich @ 2025-03-24 10:05 UTC (permalink / raw)
To: Oleksii Moisieiev
Cc: Stefano Stabellini, Julien Grall, Andrew Cooper, Michal Orzel,
Roger Pau Monne, Anthony PERARD, Volodymyr Babchuk,
Bertrand Marquis, Juergen Gross, Grygorii Strashko,
xen-devel@lists.xenproject.org, Grygorii Strashko
On 24.03.2025 10:00, Oleksii Moisieiev wrote:
> Hi Jan,
>
> Let me answer one of your comment. Please see below:
>
> On 11/03/2025 13:43, Jan Beulich wrote:
>> On 11.03.2025 12:16, Grygorii Strashko wrote:
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -526,6 +526,12 @@ S: Supported
>>> F: xen/arch/arm/include/asm/tee/
> [snip]
>>> --- a/xen/include/public/arch-arm.h
>>> +++ b/xen/include/public/arch-arm.h
>>> @@ -327,6 +327,8 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t);
>>> #define XEN_DOMCTL_CONFIG_TEE_OPTEE 1
>>> #define XEN_DOMCTL_CONFIG_TEE_FFA 2
>>>
>>> +#define XEN_DOMCTL_CONFIG_ARM_SCI_NONE 0
>>> +
>>> struct xen_arch_domainconfig {
>>> /* IN/OUT */
>>> uint8_t gic_version;
>>> @@ -350,6 +352,8 @@ struct xen_arch_domainconfig {
>>> *
>>> */
>>> uint32_t clock_frequency;
>>> + /* IN */
>>> + uint8_t arm_sci_type;
>>> };
>> You're not re-using a pre-existing padding field, so I don't see how you
>> can get away without bumping XEN_DOMCTL_INTERFACE_VERSION.
>
> We are reusing an existing padding field in xen_domctl, which is defined
> as pad[128].
>
> The xen_arch_domainconfig structure is a part of the following domctl
> structures:
>
> - xen_domctl_createdomain
>
> - xen_domctl_getdomaininfo
>
> These structures are included in the union within xen_domctl, which
> defines pad[128] for padding.
Except that "an existing padding field" means a field which isn't just
there in space, but is also checked to be zero right now. That is, new
code setting the field to non-zero needs to properly get an error
indicator back from an older hypervisor.
Jan
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [RFC PATCH v3 1/7] xen/arm: add generic SCI subsystem
2025-03-24 10:05 ` Jan Beulich
@ 2025-03-24 13:11 ` Oleksii Moisieiev
2025-03-24 13:43 ` Jan Beulich
0 siblings, 1 reply; 30+ messages in thread
From: Oleksii Moisieiev @ 2025-03-24 13:11 UTC (permalink / raw)
To: Jan Beulich
Cc: Stefano Stabellini, Julien Grall, Andrew Cooper, Michal Orzel,
Roger Pau Monne, Anthony PERARD, Volodymyr Babchuk,
Bertrand Marquis, Juergen Gross, Grygorii Strashko,
xen-devel@lists.xenproject.org, Grygorii Strashko
Hi Jan,
On 24/03/2025 12:05, Jan Beulich wrote:
> On 24.03.2025 10:00, Oleksii Moisieiev wrote:
>> Hi Jan,
>>
>> Let me answer one of your comment. Please see below:
>>
>> On 11/03/2025 13:43, Jan Beulich wrote:
>>> On 11.03.2025 12:16, Grygorii Strashko wrote:
>>>> --- a/MAINTAINERS
>>>> +++ b/MAINTAINERS
>>>> @@ -526,6 +526,12 @@ S: Supported
>>>> F: xen/arch/arm/include/asm/tee/
>> [snip]
>>>> --- a/xen/include/public/arch-arm.h
>>>> +++ b/xen/include/public/arch-arm.h
>>>> @@ -327,6 +327,8 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t);
>>>> #define XEN_DOMCTL_CONFIG_TEE_OPTEE 1
>>>> #define XEN_DOMCTL_CONFIG_TEE_FFA 2
>>>>
>>>> +#define XEN_DOMCTL_CONFIG_ARM_SCI_NONE 0
>>>> +
>>>> struct xen_arch_domainconfig {
>>>> /* IN/OUT */
>>>> uint8_t gic_version;
>>>> @@ -350,6 +352,8 @@ struct xen_arch_domainconfig {
>>>> *
>>>> */
>>>> uint32_t clock_frequency;
>>>> + /* IN */
>>>> + uint8_t arm_sci_type;
>>>> };
>>> You're not re-using a pre-existing padding field, so I don't see how you
>>> can get away without bumping XEN_DOMCTL_INTERFACE_VERSION.
>> We are reusing an existing padding field in xen_domctl, which is defined
>> as pad[128].
>>
>> The xen_arch_domainconfig structure is a part of the following domctl
>> structures:
>>
>> - xen_domctl_createdomain
>>
>> - xen_domctl_getdomaininfo
>>
>> These structures are included in the union within xen_domctl, which
>> defines pad[128] for padding.
> Except that "an existing padding field" means a field which isn't just
> there in space, but is also checked to be zero right now. That is, new
> code setting the field to non-zero needs to properly get an error
> indicator back from an older hypervisor.
I completely agree with you that XEN_DOMCTL_INTERFACE_VERSION should be
incremented
before the changes are merged. I just wanted to point out that we do not
exceed the size of the xen_domctl padding. If you are okay with the
fields we have added, then XEN_DOMCTL_INTERFACE_VERSION will be updated
in the next patch series.
> but is also checked to be zero right now.
Just out of curiosity, I have one more question: I couldn't find the
check you've mentioned. Could you point me to where Xen or
the toolstack checks the domctl structure for 0? I would greatly
appreciate it if you could show me.
WBR,
Oleksii.
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [RFC PATCH v3 1/7] xen/arm: add generic SCI subsystem
2025-03-24 13:11 ` Oleksii Moisieiev
@ 2025-03-24 13:43 ` Jan Beulich
0 siblings, 0 replies; 30+ messages in thread
From: Jan Beulich @ 2025-03-24 13:43 UTC (permalink / raw)
To: Oleksii Moisieiev
Cc: Stefano Stabellini, Julien Grall, Andrew Cooper, Michal Orzel,
Roger Pau Monne, Anthony PERARD, Volodymyr Babchuk,
Bertrand Marquis, Juergen Gross, Grygorii Strashko,
xen-devel@lists.xenproject.org, Grygorii Strashko
On 24.03.2025 14:11, Oleksii Moisieiev wrote:
> Hi Jan,
>
> On 24/03/2025 12:05, Jan Beulich wrote:
>> On 24.03.2025 10:00, Oleksii Moisieiev wrote:
>>> Hi Jan,
>>>
>>> Let me answer one of your comment. Please see below:
>>>
>>> On 11/03/2025 13:43, Jan Beulich wrote:
>>>> On 11.03.2025 12:16, Grygorii Strashko wrote:
>>>>> --- a/MAINTAINERS
>>>>> +++ b/MAINTAINERS
>>>>> @@ -526,6 +526,12 @@ S: Supported
>>>>> F: xen/arch/arm/include/asm/tee/
>>> [snip]
>>>>> --- a/xen/include/public/arch-arm.h
>>>>> +++ b/xen/include/public/arch-arm.h
>>>>> @@ -327,6 +327,8 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t);
>>>>> #define XEN_DOMCTL_CONFIG_TEE_OPTEE 1
>>>>> #define XEN_DOMCTL_CONFIG_TEE_FFA 2
>>>>>
>>>>> +#define XEN_DOMCTL_CONFIG_ARM_SCI_NONE 0
>>>>> +
>>>>> struct xen_arch_domainconfig {
>>>>> /* IN/OUT */
>>>>> uint8_t gic_version;
>>>>> @@ -350,6 +352,8 @@ struct xen_arch_domainconfig {
>>>>> *
>>>>> */
>>>>> uint32_t clock_frequency;
>>>>> + /* IN */
>>>>> + uint8_t arm_sci_type;
>>>>> };
>>>> You're not re-using a pre-existing padding field, so I don't see how you
>>>> can get away without bumping XEN_DOMCTL_INTERFACE_VERSION.
>>> We are reusing an existing padding field in xen_domctl, which is defined
>>> as pad[128].
>>>
>>> The xen_arch_domainconfig structure is a part of the following domctl
>>> structures:
>>>
>>> - xen_domctl_createdomain
>>>
>>> - xen_domctl_getdomaininfo
>>>
>>> These structures are included in the union within xen_domctl, which
>>> defines pad[128] for padding.
>> Except that "an existing padding field" means a field which isn't just
>> there in space, but is also checked to be zero right now. That is, new
>> code setting the field to non-zero needs to properly get an error
>> indicator back from an older hypervisor.
>
> I completely agree with you that XEN_DOMCTL_INTERFACE_VERSION should be
> incremented
>
> before the changes are merged. I just wanted to point out that we do not
> exceed the size of the xen_domctl padding. If you are okay with the
> fields we have added, then XEN_DOMCTL_INTERFACE_VERSION will be updated
> in the next patch series.
>
>> but is also checked to be zero right now.
>
> Just out of curiosity, I have one more question: I couldn't find the
> check you've mentioned. Could you point me to where Xen or
> the toolstack checks the domctl structure for 0? I would greatly
> appreciate it if you could show me.
There's no such check here (as far as I'm aware), hence why the interface
version bump is necessary. There are _other_ padding fields which do have
appropriate checks, and hence could be assigned purpose without bumping
the interface version.
Jan
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFC PATCH v3 1/7] xen/arm: add generic SCI subsystem
2025-03-11 11:43 ` Jan Beulich
2025-03-24 9:00 ` Oleksii Moisieiev
@ 2025-03-24 14:25 ` Grygorii Strashko
2025-03-24 14:36 ` Jan Beulich
1 sibling, 1 reply; 30+ messages in thread
From: Grygorii Strashko @ 2025-03-24 14:25 UTC (permalink / raw)
To: Jan Beulich, Grygorii Strashko
Cc: Stefano Stabellini, Julien Grall, Andrew Cooper, Michal Orzel,
Roger Pau Monne, Anthony PERARD, Volodymyr Babchuk,
Oleksii Moisieiev, Bertrand Marquis, Juergen Gross, xen-devel
Hi Jan,
Thank you for your comments.
On 11.03.25 13:43, Jan Beulich wrote:> On 11.03.2025 12:16, Grygorii Strashko wrote:
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -526,6 +526,12 @@ S: Supported
>> F: xen/arch/arm/include/asm/tee/
>> F: xen/arch/arm/tee/
>>
>> +SCI MEDIATORS
>> +M: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
>> +S: Supported
>> +F: xen/arch/arm/sci
>> +F: xen/include/asm-arm/sci
>> +
>> TOOLSTACK
>> M: Anthony PERARD <anthony.perard@vates.tech>
>> S: Supported
>
> Please can you respect alphabetical sorting in this file?
>
>> @@ -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;
>
> Despite the comment I fear I don't understand what you're trying to do here.
It enables in toolstack "Device specific access control" function, which is implemented in SCI FW.
SCI FW has privileged management interface assigned to Xen,
and non-privileged interfaces assigned to guest (VM) and identified by agent_id.
SCI FW manages access to HW resources clocks, resets, etc, which considered shared and
which can't be accessed from multiple domains due to HW limitations.
SCI FW can also manage safety specific resources like HW firewalls for example.
Each device identified by device_id and can have HW resources assigned to it
device_id_res = {clk_1, clk_2, reset_1, pd_1 } - FW implementation specific.
Device can be assigned:
1) to any VM, but only to one - dynamic configuration;
2) only one, specific VM identified by agent_id - static configuration.
The policy is FW implementation specific.
Here and below the case (1) is considered, while in the case (2) - nothing need to be done.
To enable VMx access to device_id (and its resources) the special request need
to be sent to the FW management interface to get device_id accessible from VMx.
In case of SCMI, ARM System Control and Management Interface (SCMI)
specification (DEN0056E) - functionality defined in sections
4.2.1.1 Device specific access control
4.2.2.11 BASE_SET_DEVICE_PERMISSIONS
The HW configuration described in device tree, like in the below example
(abstract, not related to any specific FW, but principle is generic)
Host DT:
/sci_fw {
#access-controller-cells = <1>;
#reset-cells = <1>;
#clock-cells = <1>;
#power-domain-cells = <1>;
}
/soc/deviceA {
clocks = <&sci_fw 1>, <&sci_fw 2>;
power-domains = <&sci_fw 1>;
resets = <&sci_fw 1>;
access-controllers = <&sci_fw 0>;
}
To trigger SCI FW it required to pass Host DT device path "/soc/deviceA" down
to the corresponding SCI FW driver during domain creation by toolstack.
And it has to be done as for devices behind IOMMU, as for devices
not protected by IOMMU.
To achieve above xl.cfg:"dtdev" property was selected to be used due to:
- xen doc says
"
Specifies the host device tree nodes to pass through to this guest.
Each DTDEV_PATH is an absolute path in the device tree.
"
- toolstack triggers XEN_DOMCTL_assign_device(XEN_DOMCTL_DEV_DT) hypercall
nothing from above says it's strictly limited to IOMMU-protected devices only.
But now ARM XEN_DOMCTL_assign_device actually limited to IOMMU-protected devices
and will return to toolstack:
-EOPNOTSUPP if iommu is not enabled
-EINVAL if DT device is not IOMMU-protected
in both cases toolstack will fail.
Idea behind this change (and change in iommu_do_dt_domctl()) is to enable
XEN_DOMCTL_assign_device(XEN_DOMCTL_DEV_DT) and so xl.cfg:"dtdev"
for DT devices which
- IOMMU-protected only (has "iommus" property)
- require device access control (has "access-controllers" property)
- both
> In any event you may not clobber the original "ret", if all you do is some
> "add-on".
I'll check and rework return code handling.
>> --- a/xen/include/asm-generic/device.h
>> +++ b/xen/include/asm-generic/device.h
>> @@ -18,6 +18,7 @@ enum device_class
>> DEVICE_IOMMU,
>> DEVICE_INTERRUPT_CONTROLLER,
>> DEVICE_PCI_HOSTBRIDGE,
>> + DEVICE_ARM_SCI,
>> /* Use for error */
>> DEVICE_UNKNOWN,
>
> This is a generic header - I don't see how anything arch-specific could
> validly end up here.
Huh. Not a long time ago it was ARM specific only.
I could rename it to DEVICE_SCI, or may be to even more generic one DEVICE_FIRMWARE.
[...]
--
Best regards,
-grygorii
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [RFC PATCH v3 1/7] xen/arm: add generic SCI subsystem
2025-03-24 14:25 ` Grygorii Strashko
@ 2025-03-24 14:36 ` Jan Beulich
2025-03-24 15:29 ` Grygorii Strashko
0 siblings, 1 reply; 30+ messages in thread
From: Jan Beulich @ 2025-03-24 14:36 UTC (permalink / raw)
To: Grygorii Strashko, Grygorii Strashko
Cc: Stefano Stabellini, Julien Grall, Andrew Cooper, Michal Orzel,
Roger Pau Monne, Anthony PERARD, Volodymyr Babchuk,
Oleksii Moisieiev, Bertrand Marquis, Juergen Gross, xen-devel
On 24.03.2025 15:25, Grygorii Strashko wrote:
> On 11.03.25 13:43, Jan Beulich wrote:> On 11.03.2025 12:16, Grygorii Strashko wrote:
>>> @@ -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;
>>
>> Despite the comment I fear I don't understand what you're trying to do here.
>
> It enables in toolstack "Device specific access control" function, which is implemented in SCI FW.
> SCI FW has privileged management interface assigned to Xen,
> and non-privileged interfaces assigned to guest (VM) and identified by agent_id.
>
> SCI FW manages access to HW resources clocks, resets, etc, which considered shared and
> which can't be accessed from multiple domains due to HW limitations.
> SCI FW can also manage safety specific resources like HW firewalls for example.
>
> Each device identified by device_id and can have HW resources assigned to it
> device_id_res = {clk_1, clk_2, reset_1, pd_1 } - FW implementation specific.
>
> Device can be assigned:
> 1) to any VM, but only to one - dynamic configuration;
> 2) only one, specific VM identified by agent_id - static configuration.
> The policy is FW implementation specific.
>
> Here and below the case (1) is considered, while in the case (2) - nothing need to be done.
> To enable VMx access to device_id (and its resources) the special request need
> to be sent to the FW management interface to get device_id accessible from VMx.
>
> In case of SCMI, ARM System Control and Management Interface (SCMI)
> specification (DEN0056E) - functionality defined in sections
> 4.2.1.1 Device specific access control
> 4.2.2.11 BASE_SET_DEVICE_PERMISSIONS
>
> The HW configuration described in device tree, like in the below example
> (abstract, not related to any specific FW, but principle is generic)
>
> Host DT:
> /sci_fw {
> #access-controller-cells = <1>;
> #reset-cells = <1>;
> #clock-cells = <1>;
> #power-domain-cells = <1>;
> }
>
> /soc/deviceA {
> clocks = <&sci_fw 1>, <&sci_fw 2>;
> power-domains = <&sci_fw 1>;
> resets = <&sci_fw 1>;
> access-controllers = <&sci_fw 0>;
> }
>
> To trigger SCI FW it required to pass Host DT device path "/soc/deviceA" down
> to the corresponding SCI FW driver during domain creation by toolstack.
> And it has to be done as for devices behind IOMMU, as for devices
> not protected by IOMMU.
>
> To achieve above xl.cfg:"dtdev" property was selected to be used due to:
> - xen doc says
> "
> Specifies the host device tree nodes to pass through to this guest.
> Each DTDEV_PATH is an absolute path in the device tree.
> "
> - toolstack triggers XEN_DOMCTL_assign_device(XEN_DOMCTL_DEV_DT) hypercall
> nothing from above says it's strictly limited to IOMMU-protected devices only.
>
> But now ARM XEN_DOMCTL_assign_device actually limited to IOMMU-protected devices
> and will return to toolstack:
> -EOPNOTSUPP if iommu is not enabled
> -EINVAL if DT device is not IOMMU-protected
>
> in both cases toolstack will fail.
>
> Idea behind this change (and change in iommu_do_dt_domctl()) is to enable
> XEN_DOMCTL_assign_device(XEN_DOMCTL_DEV_DT) and so xl.cfg:"dtdev"
> for DT devices which
> - IOMMU-protected only (has "iommus" property)
> - require device access control (has "access-controllers" property)
> - both
Thanks for all the details, but I feel overwhelmed. I'd like to see this clarified
in more basic terms. For example the comment says "This change will allow ...".
What's "this change" here? Together with "TODO: RFC" it feels a little as if the
code comment was really meant to live elsewhere (patch description? post-commit-
message area of the submission?), and thus offering little value to an observer
like me. Yet as this is common code, I'd like to have at least a rough, high
level understanding of what's going on here. This doesn't need to go into any Arm
details that I may not fully understand / appreciate anyway.
Jan
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [RFC PATCH v3 1/7] xen/arm: add generic SCI subsystem
2025-03-24 14:36 ` Jan Beulich
@ 2025-03-24 15:29 ` Grygorii Strashko
0 siblings, 0 replies; 30+ messages in thread
From: Grygorii Strashko @ 2025-03-24 15:29 UTC (permalink / raw)
To: Jan Beulich, Grygorii Strashko
Cc: Stefano Stabellini, Julien Grall, Andrew Cooper, Michal Orzel,
Roger Pau Monne, Anthony PERARD, Volodymyr Babchuk,
Oleksii Moisieiev, Bertrand Marquis, Juergen Gross, xen-devel
On 24.03.25 16:36, Jan Beulich wrote:
> On 24.03.2025 15:25, Grygorii Strashko wrote:
>> On 11.03.25 13:43, Jan Beulich wrote:> On 11.03.2025 12:16, Grygorii Strashko wrote:
>>>> @@ -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;
>>>
>>> Despite the comment I fear I don't understand what you're trying to do here.
>>
>> It enables in toolstack "Device specific access control" function, which is implemented in SCI FW.
>> SCI FW has privileged management interface assigned to Xen,
>> and non-privileged interfaces assigned to guest (VM) and identified by agent_id.
>>
>> SCI FW manages access to HW resources clocks, resets, etc, which considered shared and
>> which can't be accessed from multiple domains due to HW limitations.
>> SCI FW can also manage safety specific resources like HW firewalls for example.
>>
>> Each device identified by device_id and can have HW resources assigned to it
>> device_id_res = {clk_1, clk_2, reset_1, pd_1 } - FW implementation specific.
>>
>> Device can be assigned:
>> 1) to any VM, but only to one - dynamic configuration;
>> 2) only one, specific VM identified by agent_id - static configuration.
>> The policy is FW implementation specific.
>>
>> Here and below the case (1) is considered, while in the case (2) - nothing need to be done.
>> To enable VMx access to device_id (and its resources) the special request need
>> to be sent to the FW management interface to get device_id accessible from VMx.
>>
>> In case of SCMI, ARM System Control and Management Interface (SCMI)
>> specification (DEN0056E) - functionality defined in sections
>> 4.2.1.1 Device specific access control
>> 4.2.2.11 BASE_SET_DEVICE_PERMISSIONS
>>
>> The HW configuration described in device tree, like in the below example
>> (abstract, not related to any specific FW, but principle is generic)
>>
>> Host DT:
>> /sci_fw {
>> #access-controller-cells = <1>;
>> #reset-cells = <1>;
>> #clock-cells = <1>;
>> #power-domain-cells = <1>;
>> }
>>
>> /soc/deviceA {
>> clocks = <&sci_fw 1>, <&sci_fw 2>;
>> power-domains = <&sci_fw 1>;
>> resets = <&sci_fw 1>;
>> access-controllers = <&sci_fw 0>;
>> }
>>
>> To trigger SCI FW it required to pass Host DT device path "/soc/deviceA" down
>> to the corresponding SCI FW driver during domain creation by toolstack.
>> And it has to be done as for devices behind IOMMU, as for devices
>> not protected by IOMMU.
>>
>> To achieve above xl.cfg:"dtdev" property was selected to be used due to:
>> - xen doc says
>> "
>> Specifies the host device tree nodes to pass through to this guest.
>> Each DTDEV_PATH is an absolute path in the device tree.
>> "
>> - toolstack triggers XEN_DOMCTL_assign_device(XEN_DOMCTL_DEV_DT) hypercall
>> nothing from above says it's strictly limited to IOMMU-protected devices only.
>>
>> But now ARM XEN_DOMCTL_assign_device actually limited to IOMMU-protected devices
>> and will return to toolstack:
>> -EOPNOTSUPP if iommu is not enabled
>> -EINVAL if DT device is not IOMMU-protected
>>
>> in both cases toolstack will fail.
>>
>> Idea behind this change (and change in iommu_do_dt_domctl()) is to enable
>> XEN_DOMCTL_assign_device(XEN_DOMCTL_DEV_DT) and so xl.cfg:"dtdev"
>> for DT devices which
>> - IOMMU-protected only (has "iommus" property)
>> - require device access control (has "access-controllers" property)
>> - both
>
> Thanks for all the details, but I feel overwhelmed. I'd like to see this clarified
> in more basic terms. For example the comment says "This change will allow ...".
> What's "this change" here? Together with "TODO: RFC" it feels a little as if the
> code comment was really meant to live elsewhere (patch description? post-commit-
> message area of the submission?), and thus offering little value to an observer
> like me. Yet as this is common code, I'd like to have at least a rough, high
> level understanding of what's going on here. This doesn't need to go into any Arm
> details that I may not fully understand / appreciate anyway.
I'm very sorry for the confusion, I'll move changes in question in separate patch.
--
Best regards,
-grygorii
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFC PATCH v3 1/7] xen/arm: add generic SCI subsystem
2025-03-11 11:16 ` [RFC PATCH v3 1/7] xen/arm: add generic SCI subsystem Grygorii Strashko
2025-03-11 11:43 ` Jan Beulich
@ 2025-03-13 23:48 ` Stefano Stabellini
2025-03-24 14:26 ` Grygorii Strashko
1 sibling, 1 reply; 30+ messages in thread
From: Stefano Stabellini @ 2025-03-13 23:48 UTC (permalink / raw)
To: Grygorii Strashko
Cc: xen-devel, Stefano Stabellini, Julien Grall, Andrew Cooper,
Michal Orzel, Roger Pau Monne, Jan Beulich, Anthony PERARD,
Volodymyr Babchuk, Oleksii Moisieiev, Bertrand Marquis,
Juergen Gross, Grygorii Strashko
On Tue, 11 Mar 2025, Grygorii Strashko wrote:
> From: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
>
> 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@epam.com>
> Signed-off-by: Grygorii Strashko <grygorii_strashko@epam.com>
> ---
> 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
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c11b82eca98f..c0e8143dca63 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -526,6 +526,12 @@ S: Supported
> F: xen/arch/arm/include/asm/tee/
> F: xen/arch/arm/tee/
>
> +SCI MEDIATORS
> +M: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
> +S: Supported
> +F: xen/arch/arm/sci
> +F: xen/include/asm-arm/sci
> +
> TOOLSTACK
> M: Anthony PERARD <anthony.perard@vates.tech>
> S: Supported
> diff --git a/xen/arch/arm/device.c b/xen/arch/arm/device.c
> index 5610cddcba8e..bdab96a408c4 100644
> --- a/xen/arch/arm/device.c
> +++ b/xen/arch/arm/device.c
> @@ -13,6 +13,7 @@
> #include <xen/iocap.h>
> #include <xen/lib.h>
>
> +#include <asm/firmware/sci.h>
> #include <asm/setup.h>
>
> int map_irq_to_domain(struct domain *d, unsigned int irq,
> @@ -303,6 +304,10 @@ int handle_device(struct domain *d, struct dt_device_node *dev, p2m_type_t p2mt,
> return res;
> }
> }
> +
> + res = sci_assign_dt_device(d, dev);
> + if ( res )
> + return res;
> }
>
> res = map_device_irqs_to_domain(d, dev, own_device, irq_ranges);
> diff --git a/xen/arch/arm/dom0less-build.c b/xen/arch/arm/dom0less-build.c
> index 49d1f14d659b..c463ab3eaca5 100644
> --- a/xen/arch/arm/dom0less-build.c
> +++ b/xen/arch/arm/dom0less-build.c
> @@ -14,6 +14,7 @@
> #include <asm/arm64/sve.h>
> #include <asm/dom0less-build.h>
> #include <asm/domain_build.h>
> +#include <asm/firmware/sci.h>
> #include <asm/static-memory.h>
> #include <asm/static-shmem.h>
>
> @@ -321,6 +322,10 @@ static int __init handle_passthrough_prop(struct kernel_info *kinfo,
> return -EINVAL;
> }
>
> + res = sci_assign_dt_device(kinfo->d, node);
> + if ( res )
> + return res;
> +
> res = map_device_irqs_to_domain(kinfo->d, node, true, NULL);
> if ( res < 0 )
> return res;
> @@ -970,6 +975,14 @@ void __init create_domUs(void)
> if ( !llc_coloring_enabled && llc_colors_str )
> panic("'llc-colors' found, but LLC coloring is disabled\n");
>
> + /*
> + * TODO: enable ARM SCI for dom0less case
> + * The configuration need to be retrieved from DT
> + * - arch.arm_sci_type, like "xen,sci_type"
> + * - arch.arm_sci_agent_id, like "xen,sci_agent_id"
> + */
> + d_cfg.arch.arm_sci_type = XEN_DOMCTL_CONFIG_ARM_SCI_NONE;
> +
> /*
> * The variable max_init_domid is initialized with zero, so here it's
> * very important to use the pre-increment operator to call
> diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c
> index 3ba959f86633..652aeb7a55de 100644
> --- a/xen/arch/arm/domain.c
> +++ b/xen/arch/arm/domain.c
> @@ -25,6 +25,7 @@
> #include <asm/platform.h>
> #include <asm/procinfo.h>
> #include <asm/regs.h>
> +#include <asm/firmware/sci.h>
> #include <asm/tee/tee.h>
> #include <asm/vfp.h>
> #include <asm/vgic.h>
> @@ -694,7 +695,7 @@ int arch_sanitise_domain_config(struct xen_domctl_createdomain *config)
> return -EINVAL;
> }
>
> - return 0;
> + return sci_domain_sanitise_config(config);
> }
>
> int arch_domain_create(struct domain *d,
> @@ -786,6 +787,9 @@ int arch_domain_create(struct domain *d,
> d->arch.sve_vl = config->arch.sve_vl;
> #endif
>
> + if ( (rc = sci_domain_init(d, config)) != 0 )
> + goto fail;
> +
> return 0;
>
> fail:
> @@ -846,6 +850,7 @@ void arch_domain_destroy(struct domain *d)
> domain_vgic_free(d);
> domain_vuart_free(d);
> free_xenheap_page(d->shared_info);
> + sci_domain_destroy(d);
> #ifdef CONFIG_ACPI
> free_xenheap_pages(d->arch.efi_acpi_table,
> get_order_from_bytes(d->arch.efi_acpi_len));
> @@ -1039,6 +1044,7 @@ enum {
> PROG_p2m_root,
> PROG_p2m,
> PROG_p2m_pool,
> + PROG_sci,
> PROG_done,
> };
>
> @@ -1098,6 +1104,10 @@ int domain_relinquish_resources(struct domain *d)
> ret = relinquish_p2m_mapping(d);
> if ( ret )
> return ret;
> + PROGRESS(sci):
> + ret = sci_relinquish_resources(d);
> + if ( ret )
> + return ret;
>
> PROGRESS(p2m_root):
> /*
> diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
> index 7b47abade196..36d28b52a418 100644
> --- a/xen/arch/arm/domain_build.c
> +++ b/xen/arch/arm/domain_build.c
> @@ -24,6 +24,7 @@
> #include <asm/setup.h>
> #include <asm/tee/tee.h>
> #include <asm/pci.h>
> +#include <asm/firmware/sci.h>
> #include <asm/platform.h>
> #include <asm/psci.h>
> #include <asm/setup.h>
> @@ -1888,6 +1889,9 @@ static int __init handle_node(struct domain *d, struct kernel_info *kinfo,
> return 0;
> }
>
> + if ( sci_dt_handle_node(d, node) )
> + return 0;
> +
> /*
> * The vGIC does not support routing hardware PPIs to guest. So
> * we need to skip any node using PPIs.
> @@ -1988,6 +1992,10 @@ static int __init handle_node(struct domain *d, struct kernel_info *kinfo,
> if ( res )
> return res;
>
> + res = sci_dt_finalize(d, kinfo->fdt);
> + if ( res )
> + return res;
> +
> /*
> * Create a second memory node to store the ranges covering
> * reserved-memory regions.
> diff --git a/xen/arch/arm/firmware/Kconfig b/xen/arch/arm/firmware/Kconfig
> index 817da745fd43..fc7918c7fc56 100644
> --- a/xen/arch/arm/firmware/Kconfig
> +++ b/xen/arch/arm/firmware/Kconfig
> @@ -1,3 +1,11 @@
> +config ARM_SCI
> + bool
> + depends on ARM
> + help
> + This option enables generic Arm SCI (System Control Interface) mediators
> + support. It allows domains to control system resources via one of
> + Arm SCI mediators drivers implemented in XEN, like SCMI.
> +
> menu "Firmware Drivers"
>
> config SCMI_SMC
> diff --git a/xen/arch/arm/firmware/Makefile b/xen/arch/arm/firmware/Makefile
> index a5e454266625..71bdefc24afb 100644
> --- a/xen/arch/arm/firmware/Makefile
> +++ b/xen/arch/arm/firmware/Makefile
> @@ -1 +1,2 @@
> +obj-$(CONFIG_ARM_SCI) += sci.o
> obj-$(CONFIG_SCMI_SMC) += scmi-smc.o
> diff --git a/xen/arch/arm/firmware/sci.c b/xen/arch/arm/firmware/sci.c
> new file mode 100644
> index 000000000000..1fb6b55e030a
> --- /dev/null
> +++ b/xen/arch/arm/firmware/sci.c
> @@ -0,0 +1,187 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
FYI, while GPL-2.0-or-later is a compatible license accepted in Xen
Project, the default license used by Xen Project is GPL-2.0-only.
> +/*
> + * Generic part of the SCI (System Control Interface) subsystem.
> + *
> + * Oleksii Moisieiev <oleksii_moisieiev@epam.com>
> + * 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?
> + 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/include/asm/firmware/sci.h b/xen/arch/arm/include/asm/firmware/sci.h
> new file mode 100644
> index 000000000000..4c6f624723c5
> --- /dev/null
> +++ b/xen/arch/arm/include/asm/firmware/sci.h
> @@ -0,0 +1,214 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Generic ARM SCI (System Control Interface) subsystem.
> + *
> + * Oleksii Moisieiev <oleksii_moisieiev@epam.com>
> + * Copyright (c) 2025 EPAM Systems
> + */
> +
> +#ifndef __ASM_ARM_SCI_H
> +#define __ASM_ARM_SCI_H
> +
> +#include <xen/lib.h>
> +#include <xen/types.h>
> +#include <xen/device_tree.h>
> +#include <xen/errno.h>
> +#include <xen/sched.h>
> +
> +#ifdef CONFIG_ARM_SCI
> +
> +struct sci_mediator_ops {
> + /*
> + * Called during domain construction. If it is requested to enable
> + * SCI support, so SCI driver can create own structures for the new domain
> + * and inform firmware about new domain (if required).
> + * Mandatory.
> + */
> + int (*domain_init)(struct domain *d,
> + struct xen_domctl_createdomain *config);
> +
> + /*
> + * Called during domain construction. The SCI driver uses
> + * it to sanitize domain SCI configuration parameters.
> + * Optional.
> + */
> + int (*domain_sanitise_config)(struct xen_domctl_createdomain *config);
> +
> + /*
> + * Called during domain destruction, releases all resources, that
> + * were allocated for domain.
> + * Mandatory.
> + */
> + void (*domain_destroy)(struct domain *d);
> +
> + /*
> + * Called during domain destruction to relinquish resources used
> + * by SCI driver itself and request resources releasing from firmware.
> + * Optional.
> + */
> + int (*relinquish_resources)(struct domain *d);
> +
> + /* SMC/HVC Handle callback */
> + bool (*handle_call)(struct cpu_user_regs *regs);
> +
> + /*
> + * Dom0 DT nodes handling callback so SCI driver can detect DT nodes it
> + * need to handle and decide if those nodes need to be provided to Dom0.
> + * Optional.
> + */
> + bool (*dom0_dt_handle_node)(struct domain *d, struct dt_device_node *node);
> +
> + /*
> + * SCI driver callback called at the end of Dom0 DT generation, so
> + * it can perform steps to modify DT to enable/disable SCI
> + * functionality for Dom0.
> + */
> + int (*dom0_dt_finalize)(struct domain *d, void *fdt);
> +
> + /*
> + * SCI driver callback called when DT device is passed through to guest,
> + * so SCI driver can enable device access to the domain if SCI FW provides
> + * Device specific access control functionality.
> + * Optional.
> + */
> + int (*assign_dt_device)(struct domain *d, struct dt_phandle_args *ac_spec);
> +};
> +
> +
> +static inline bool sci_domain_is_enabled(struct domain *d)
> +{
> + return d->arch.sci_enabled;
> +}
> +
> +/*
> + * Register SCI subsystem ops.
> + *
> + * Register SCI drivers operation and so enable SCI functionality.
> + * Only one SCI driver is supported.
> + */
> +int sci_register(const struct sci_mediator_ops *ops);
> +
> +/*
> + * Initialize SCI functionality for domain if configured.
> + *
> + * Initialization routine to enable SCI functionality for the domain.
> + * The SCI configuration data and decision about enabling SCI functionality
> + * for the domain is SCI driver specific.
> + */
> +int sci_domain_init(struct domain *d, struct xen_domctl_createdomain *config);
> +
> +/*
> + * Sanitise domain configuration parameters.
> + *
> + */
> +int sci_domain_sanitise_config(struct xen_domctl_createdomain *config);
> +
> +/*
> + * Destroy SCI domain instance.
> + */
> +void sci_domain_destroy(struct domain *d);
> +
> +/*
> + * Free resources assigned to the certain domain.
> + */
> +int sci_relinquish_resources(struct domain *d);
> +
> +/*
> + * SMC/HVC Handle callback.
> + *
> + * SCI driver acts as SMC/HVC server for the registered domains and
> + * does redirection of the domain calls to the SCI firmware,
> + * such as ARM TF-A or similar.
> + */
> +bool sci_handle_call(struct cpu_user_regs *regs);
> +
> +/*
> + * Dom0 DT nodes handling function.
> + *
> + * Allows SCI driver to detect DT nodes it need to handle and decide if
> + * those nodes need to be provided to Dom0.
> + */
> +bool sci_dt_handle_node(struct domain *d, struct dt_device_node *node);
> +
> +/*
> + * Dom0 DT generation finalize.
> + *
> + * Called at the end of Dom0 DT generation, so SCI driver can perform steps
> + * to modify DT to enable/disable SCI functionality for Dom0.
> + */
> +int sci_dt_finalize(struct domain *d, void *fdt);
> +
> +/*
> + * Assign DT device to domain.
> + *
> + * Called when DT device is passed through to guest, so SCI driver can enable
> + * device access to the domain if SCI FW provides "Device specific access
> + * control" functionality.
> + */
> +int sci_assign_dt_device(struct domain *d, struct dt_device_node *dev);
> +
> +/*
> + * SCI domctl handler
> + *
> + * Only XEN_DOMCTL_assign_device is handled for now.
> + */
> +int sci_do_domctl(struct xen_domctl *domctl, struct domain *d,
> + XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl);
> +#else
> +
> +static inline bool sci_domain_is_enabled(struct domain *d)
> +{
> + return false;
> +}
> +
> +static inline int sci_domain_init(struct domain *d,
> + struct xen_domctl_createdomain *config)
> +{
> + return 0;
> +}
> +
> +static inline int
> +sci_domain_sanitise_config(struct xen_domctl_createdomain *config)
> +{
> + return 0;
> +}
> +
> +static inline void sci_domain_destroy(struct domain *d)
> +{}
> +
> +static inline int sci_relinquish_resources(struct domain *d)
> +{
> + return 0;
> +}
> +
> +static inline bool sci_handle_call(struct cpu_user_regs *args)
> +{
> + return false;
> +}
> +
> +static inline bool sci_dt_handle_node(struct domain *d,
> + struct dt_device_node *node)
> +{
> + return false;
> +}
> +
> +static inline int sci_dt_finalize(struct domain *d, void *fdt)
> +{
> + return false;
> +}
> +
> +static inline int sci_assign_dt_device(struct domain *d,
> + struct dt_device_node *dev)
> +{
> + return 0;
> +}
> +
> +static inline int sci_do_domctl(struct xen_domctl *domctl, struct domain *d,
> + XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl)
> +{
> + return 0;
> +}
> +
> +#endif /* CONFIG_ARM_SCI */
> +
> +#endif /* __ASM_ARM_SCI_H */
> 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?
> 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
> ret = iommu_assign_dt_device(d, dev);
>
> if ( ret )
> diff --git a/xen/include/asm-generic/device.h b/xen/include/asm-generic/device.h
> index 1acd1ba1d8b4..9fcdabfda9bc 100644
> --- a/xen/include/asm-generic/device.h
> +++ b/xen/include/asm-generic/device.h
> @@ -18,6 +18,7 @@ enum device_class
> DEVICE_IOMMU,
> DEVICE_INTERRUPT_CONTROLLER,
> DEVICE_PCI_HOSTBRIDGE,
> + DEVICE_ARM_SCI,
> /* Use for error */
> DEVICE_UNKNOWN,
> };
> diff --git a/xen/include/public/arch-arm.h b/xen/include/public/arch-arm.h
> index 24840eeaa666..55eed9992c9d 100644
> --- a/xen/include/public/arch-arm.h
> +++ b/xen/include/public/arch-arm.h
> @@ -327,6 +327,8 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t);
> #define XEN_DOMCTL_CONFIG_TEE_OPTEE 1
> #define XEN_DOMCTL_CONFIG_TEE_FFA 2
>
> +#define XEN_DOMCTL_CONFIG_ARM_SCI_NONE 0
> +
> struct xen_arch_domainconfig {
> /* IN/OUT */
> uint8_t gic_version;
> @@ -350,6 +352,8 @@ struct xen_arch_domainconfig {
> *
> */
> uint32_t clock_frequency;
> + /* IN */
> + uint8_t arm_sci_type;
> };
> #endif /* __XEN__ || __XEN_TOOLS__ */
>
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [RFC PATCH v3 1/7] xen/arm: add generic SCI subsystem
2025-03-13 23:48 ` Stefano Stabellini
@ 2025-03-24 14:26 ` Grygorii Strashko
0 siblings, 0 replies; 30+ messages in thread
From: Grygorii Strashko @ 2025-03-24 14:26 UTC (permalink / raw)
To: Stefano Stabellini, Grygorii Strashko
Cc: xen-devel, Julien Grall, Andrew Cooper, Michal Orzel,
Roger Pau Monne, Jan Beulich, Anthony PERARD, Volodymyr Babchuk,
Oleksii Moisieiev, Bertrand Marquis, Juergen Gross
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@epam.com>
>>
>> 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@epam.com>
>> Signed-off-by: Grygorii Strashko <grygorii_strashko@epam.com>
>> ---
>> 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@epam.com>
>> + * 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
^ permalink raw reply [flat|nested] 30+ messages in thread
* [RFC PATCH v3 2/7] xen/arm: scmi-smc: update to be used under sci subsystem
2025-03-11 11:16 [RFC PATCH v3 0/7] xen/arm: scmi: introduce SCI SCMI SMC multi-agent support Grygorii Strashko
2025-03-11 11:16 ` [RFC PATCH v3 1/7] xen/arm: add generic SCI subsystem Grygorii Strashko
@ 2025-03-11 11:16 ` Grygorii Strashko
2025-03-13 23:48 ` Stefano Stabellini
2025-03-11 11:16 ` [RFC PATCH v3 3/7] xen/arm: scmi-smc: passthrough SCMI SMC to guest domain Grygorii Strashko
` (5 subsequent siblings)
7 siblings, 1 reply; 30+ messages in thread
From: Grygorii Strashko @ 2025-03-11 11:16 UTC (permalink / raw)
To: xen-devel
Cc: Stefano Stabellini, Julien Grall, Andrew Cooper, Michal Orzel,
Roger Pau Monne, Jan Beulich, Anthony PERARD, Volodymyr Babchuk,
Oleksii Moisieiev, Bertrand Marquis, Juergen Gross,
Grygorii Strashko
The introduced SCI (System Control Interface) subsystem provides unified
interface to integrate in Xen SCI drivers which adds support for ARM
firmware (EL3, SCP) based software interfaces (like SCMI) that are used in
system management. The SCI subsystem allows to add drivers for different FW
interfaces or have different drivers for the same FW interface (for example,
SCMI with different transports).
This patch updates SCMI over SMC calls handling layer, introduced by
commit 3e322bef8bc0 ("xen/arm: firmware: Add SCMI over SMC calls handling
layer"), to be SCI driver:
- convert to DT device;
- convert to SCI Xen interface.
Signed-off-by: Grygorii Strashko <grygorii_strashko@epam.com>
---
xen/arch/arm/firmware/Kconfig | 13 ++-
xen/arch/arm/firmware/scmi-smc.c | 93 +++++++++++---------
xen/arch/arm/include/asm/firmware/scmi-smc.h | 41 ---------
xen/arch/arm/vsmc.c | 5 +-
xen/include/public/arch-arm.h | 1 +
5 files changed, 64 insertions(+), 89 deletions(-)
delete mode 100644 xen/arch/arm/include/asm/firmware/scmi-smc.h
diff --git a/xen/arch/arm/firmware/Kconfig b/xen/arch/arm/firmware/Kconfig
index fc7918c7fc56..02d7b600317f 100644
--- a/xen/arch/arm/firmware/Kconfig
+++ b/xen/arch/arm/firmware/Kconfig
@@ -8,9 +8,18 @@ config ARM_SCI
menu "Firmware Drivers"
+choice
+ prompt "ARM SCI driver type"
+ default ARM_SCI_NONE
+ help
+ Choose which ARM SCI driver to enable.
+
+config ARM_SCI_NONE
+ bool "none"
+
config SCMI_SMC
bool "Forward SCMI over SMC calls from hwdom to EL3 firmware"
- default y
+ select ARM_SCI
help
This option enables basic awareness for SCMI calls using SMC as
doorbell mechanism and Shared Memory for transport ("arm,scmi-smc"
@@ -18,4 +27,6 @@ config SCMI_SMC
firmware node is used to trap and forward corresponding SCMI SMCs
to firmware running at EL3, for calls coming from the hardware domain.
+endchoice
+
endmenu
diff --git a/xen/arch/arm/firmware/scmi-smc.c b/xen/arch/arm/firmware/scmi-smc.c
index 33473c04b181..188bd659513b 100644
--- a/xen/arch/arm/firmware/scmi-smc.c
+++ b/xen/arch/arm/firmware/scmi-smc.c
@@ -9,6 +9,7 @@
* Copyright 2024 NXP
*/
+#include <asm/device.h>
#include <xen/acpi.h>
#include <xen/device_tree.h>
#include <xen/errno.h>
@@ -16,12 +17,11 @@
#include <xen/sched.h>
#include <xen/types.h>
+#include <asm/firmware/sci.h>
#include <asm/smccc.h>
-#include <asm/firmware/scmi-smc.h>
#define SCMI_SMC_ID_PROP "arm,smc-id"
-static bool __ro_after_init scmi_enabled;
static uint32_t __ro_after_init scmi_smc_id;
/*
@@ -41,14 +41,11 @@ static bool scmi_is_valid_smc_id(uint32_t fid)
*
* Returns true if SMC was handled (regardless of response), false otherwise.
*/
-bool scmi_handle_smc(struct cpu_user_regs *regs)
+static bool scmi_handle_smc(struct cpu_user_regs *regs)
{
uint32_t fid = (uint32_t)get_user_reg(regs, 0);
struct arm_smccc_res res;
- if ( !scmi_enabled )
- return false;
-
if ( !scmi_is_valid_smc_id(fid) )
return false;
@@ -78,49 +75,45 @@ bool scmi_handle_smc(struct cpu_user_regs *regs)
return true;
}
-static int __init scmi_check_smccc_ver(void)
+static int scmi_smc_domain_init(struct domain *d,
+ struct xen_domctl_createdomain *config)
{
- if ( smccc_ver < ARM_SMCCC_VERSION_1_1 )
- {
- printk(XENLOG_WARNING
- "SCMI: No SMCCC 1.1 support, SCMI calls forwarding disabled\n");
- return -ENOSYS;
- }
+ if ( !is_hardware_domain(d) )
+ return 0;
+ d->arch.sci_enabled = true;
+ printk(XENLOG_DEBUG "SCMI: %pd init\n", d);
return 0;
}
-static int __init scmi_dt_init_smccc(void)
+static void scmi_smc_domain_destroy(struct domain *d)
{
- static const struct dt_device_match scmi_ids[] __initconst =
- {
- /* We only support "arm,scmi-smc" binding for now */
- DT_MATCH_COMPATIBLE("arm,scmi-smc"),
- { /* sentinel */ },
- };
- const struct dt_device_node *scmi_node;
- int ret;
+ if ( !is_hardware_domain(d) )
+ return;
- /* If no SCMI firmware node found, fail silently as it's not mandatory */
- scmi_node = dt_find_matching_node(NULL, scmi_ids);
- if ( !scmi_node )
- return -EOPNOTSUPP;
+ printk(XENLOG_DEBUG "SCMI: %pd destroy\n", d);
+}
- ret = dt_property_read_u32(scmi_node, SCMI_SMC_ID_PROP, &scmi_smc_id);
- if ( !ret )
+static int __init scmi_check_smccc_ver(void)
+{
+ if ( smccc_ver < ARM_SMCCC_VERSION_1_1 )
{
- printk(XENLOG_ERR "SCMI: No valid \"%s\" property in \"%s\" DT node\n",
- SCMI_SMC_ID_PROP, scmi_node->full_name);
- return -ENOENT;
+ printk(XENLOG_WARNING
+ "SCMI: No SMCCC 1.1 support, SCMI calls forwarding disabled\n");
+ return -ENOSYS;
}
- scmi_enabled = true;
-
return 0;
}
+static const struct sci_mediator_ops scmi_smc_ops = {
+ .handle_call = scmi_handle_smc,
+ .domain_init = scmi_smc_domain_init,
+ .domain_destroy = scmi_smc_domain_destroy,
+};
+
/* Initialize the SCMI layer based on SMCs and Device-tree */
-static int __init scmi_init(void)
+static int __init scmi_dom0_init(struct dt_device_node *dev, const void *data)
{
int ret;
@@ -134,22 +127,36 @@ static int __init scmi_init(void)
if ( ret )
return ret;
- ret = scmi_dt_init_smccc();
- if ( ret == -EOPNOTSUPP )
- return ret;
+ ret = dt_property_read_u32(dev, SCMI_SMC_ID_PROP, &scmi_smc_id);
+ if ( !ret )
+ {
+ printk(XENLOG_ERR "SCMI: No valid \"%s\" property in \"%s\" DT node\n",
+ SCMI_SMC_ID_PROP, dt_node_full_name(dev));
+ return -ENOENT;
+ }
+
+ ret = sci_register(&scmi_smc_ops);
if ( ret )
- goto err;
+ {
+ printk(XENLOG_ERR "SCMI: mediator already registered (ret = %d)\n",
+ ret);
+ return ret;
+ }
printk(XENLOG_INFO "Using SCMI with SMC ID: 0x%x\n", scmi_smc_id);
return 0;
-
- err:
- printk(XENLOG_ERR "SCMI: Initialization failed (ret = %d)\n", ret);
- return ret;
}
-__initcall(scmi_init);
+static const struct dt_device_match scmi_smc_match[] __initconst = {
+ DT_MATCH_COMPATIBLE("arm,scmi-smc"),
+ { /* sentinel */ },
+};
+
+DT_DEVICE_START(gicv3, "SCMI SMC DOM0", DEVICE_ARM_SCI)
+ .dt_match = scmi_smc_match,
+ .init = scmi_dom0_init,
+DT_DEVICE_END
/*
* Local variables:
diff --git a/xen/arch/arm/include/asm/firmware/scmi-smc.h b/xen/arch/arm/include/asm/firmware/scmi-smc.h
deleted file mode 100644
index 6b1a164a400e..000000000000
--- a/xen/arch/arm/include/asm/firmware/scmi-smc.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * xen/arch/arm/include/asm/firmware/scmi-smc.h
- *
- * ARM System Control and Management Interface (SCMI) over SMC
- * Generic handling layer
- *
- * Andrei Cherechesu <andrei.cherechesu@nxp.com>
- * Copyright 2024 NXP
- */
-
-#ifndef __ASM_SCMI_SMC_H__
-#define __ASM_SCMI_SMC_H__
-
-#include <xen/types.h>
-
-struct cpu_user_regs;
-
-#ifdef CONFIG_SCMI_SMC
-
-bool scmi_handle_smc(struct cpu_user_regs *regs);
-
-#else
-
-static inline bool scmi_handle_smc(struct cpu_user_regs *regs)
-{
- return false;
-}
-
-#endif /* CONFIG_SCMI_SMC */
-
-#endif /* __ASM_SCMI_H__ */
-
-/*
- * Local variables:
- * mode: C
- * c-file-style: "BSD"
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/xen/arch/arm/vsmc.c b/xen/arch/arm/vsmc.c
index 51b3c0297314..b33c69a1c22a 100644
--- a/xen/arch/arm/vsmc.c
+++ b/xen/arch/arm/vsmc.c
@@ -21,7 +21,6 @@
#include <asm/traps.h>
#include <asm/vpsci.h>
#include <asm/platform.h>
-#include <asm/firmware/scmi-smc.h>
/* Number of functions currently supported by Hypervisor Service. */
#define XEN_SMCCC_FUNCTION_COUNT 3
@@ -233,7 +232,7 @@ static bool handle_sip(struct cpu_user_regs *regs)
if ( platform_smc(regs) )
return true;
- return scmi_handle_smc(regs);
+ return sci_handle_call(regs);
}
/*
@@ -301,8 +300,6 @@ 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);
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/include/public/arch-arm.h b/xen/include/public/arch-arm.h
index 55eed9992c9d..095b1a23e30c 100644
--- a/xen/include/public/arch-arm.h
+++ b/xen/include/public/arch-arm.h
@@ -328,6 +328,7 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t);
#define XEN_DOMCTL_CONFIG_TEE_FFA 2
#define XEN_DOMCTL_CONFIG_ARM_SCI_NONE 0
+#define XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC 1
struct xen_arch_domainconfig {
/* IN/OUT */
--
2.34.1
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [RFC PATCH v3 2/7] xen/arm: scmi-smc: update to be used under sci subsystem
2025-03-11 11:16 ` [RFC PATCH v3 2/7] xen/arm: scmi-smc: update to be used under sci subsystem Grygorii Strashko
@ 2025-03-13 23:48 ` Stefano Stabellini
2025-03-24 15:08 ` Grygorii Strashko
0 siblings, 1 reply; 30+ messages in thread
From: Stefano Stabellini @ 2025-03-13 23:48 UTC (permalink / raw)
To: Grygorii Strashko
Cc: xen-devel, Stefano Stabellini, Julien Grall, Andrew Cooper,
Michal Orzel, Roger Pau Monne, Jan Beulich, Anthony PERARD,
Volodymyr Babchuk, Oleksii Moisieiev, Bertrand Marquis,
Juergen Gross, Grygorii Strashko
On Tue, 11 Mar 2025, Grygorii Strashko wrote:
> The introduced SCI (System Control Interface) subsystem provides unified
> interface to integrate in Xen SCI drivers which adds support for ARM
> firmware (EL3, SCP) based software interfaces (like SCMI) that are used in
> system management. The SCI subsystem allows to add drivers for different FW
> interfaces or have different drivers for the same FW interface (for example,
> SCMI with different transports).
>
> This patch updates SCMI over SMC calls handling layer, introduced by
> commit 3e322bef8bc0 ("xen/arm: firmware: Add SCMI over SMC calls handling
> layer"), to be SCI driver:
> - convert to DT device;
> - convert to SCI Xen interface.
>
> Signed-off-by: Grygorii Strashko <grygorii_strashko@epam.com>
> ---
> xen/arch/arm/firmware/Kconfig | 13 ++-
> xen/arch/arm/firmware/scmi-smc.c | 93 +++++++++++---------
> xen/arch/arm/include/asm/firmware/scmi-smc.h | 41 ---------
> xen/arch/arm/vsmc.c | 5 +-
> xen/include/public/arch-arm.h | 1 +
> 5 files changed, 64 insertions(+), 89 deletions(-)
> delete mode 100644 xen/arch/arm/include/asm/firmware/scmi-smc.h
>
> diff --git a/xen/arch/arm/firmware/Kconfig b/xen/arch/arm/firmware/Kconfig
> index fc7918c7fc56..02d7b600317f 100644
> --- a/xen/arch/arm/firmware/Kconfig
> +++ b/xen/arch/arm/firmware/Kconfig
> @@ -8,9 +8,18 @@ config ARM_SCI
>
> menu "Firmware Drivers"
>
> +choice
> + prompt "ARM SCI driver type"
> + default ARM_SCI_NONE
> + help
> + Choose which ARM SCI driver to enable.
> +
> +config ARM_SCI_NONE
> + bool "none"
> +
> config SCMI_SMC
> bool "Forward SCMI over SMC calls from hwdom to EL3 firmware"
> - default y
> + select ARM_SCI
> help
> This option enables basic awareness for SCMI calls using SMC as
> doorbell mechanism and Shared Memory for transport ("arm,scmi-smc"
> @@ -18,4 +27,6 @@ config SCMI_SMC
> firmware node is used to trap and forward corresponding SCMI SMCs
> to firmware running at EL3, for calls coming from the hardware domain.
>
> +endchoice
> +
> endmenu
> diff --git a/xen/arch/arm/firmware/scmi-smc.c b/xen/arch/arm/firmware/scmi-smc.c
> index 33473c04b181..188bd659513b 100644
> --- a/xen/arch/arm/firmware/scmi-smc.c
> +++ b/xen/arch/arm/firmware/scmi-smc.c
> @@ -9,6 +9,7 @@
> * Copyright 2024 NXP
> */
>
> +#include <asm/device.h>
> #include <xen/acpi.h>
> #include <xen/device_tree.h>
> #include <xen/errno.h>
> @@ -16,12 +17,11 @@
> #include <xen/sched.h>
> #include <xen/types.h>
>
> +#include <asm/firmware/sci.h>
> #include <asm/smccc.h>
> -#include <asm/firmware/scmi-smc.h>
>
> #define SCMI_SMC_ID_PROP "arm,smc-id"
>
> -static bool __ro_after_init scmi_enabled;
> static uint32_t __ro_after_init scmi_smc_id;
>
> /*
> @@ -41,14 +41,11 @@ static bool scmi_is_valid_smc_id(uint32_t fid)
> *
> * Returns true if SMC was handled (regardless of response), false otherwise.
> */
> -bool scmi_handle_smc(struct cpu_user_regs *regs)
> +static bool scmi_handle_smc(struct cpu_user_regs *regs)
> {
> uint32_t fid = (uint32_t)get_user_reg(regs, 0);
> struct arm_smccc_res res;
>
> - if ( !scmi_enabled )
> - return false;
> -
> if ( !scmi_is_valid_smc_id(fid) )
> return false;
>
> @@ -78,49 +75,45 @@ bool scmi_handle_smc(struct cpu_user_regs *regs)
> return true;
> }
>
> -static int __init scmi_check_smccc_ver(void)
> +static int scmi_smc_domain_init(struct domain *d,
> + struct xen_domctl_createdomain *config)
> {
> - if ( smccc_ver < ARM_SMCCC_VERSION_1_1 )
> - {
> - printk(XENLOG_WARNING
> - "SCMI: No SMCCC 1.1 support, SCMI calls forwarding disabled\n");
> - return -ENOSYS;
> - }
> + if ( !is_hardware_domain(d) )
> + return 0;
>
> + d->arch.sci_enabled = true;
> + printk(XENLOG_DEBUG "SCMI: %pd init\n", d);
> return 0;
> }
>
> -static int __init scmi_dt_init_smccc(void)
> +static void scmi_smc_domain_destroy(struct domain *d)
> {
> - static const struct dt_device_match scmi_ids[] __initconst =
> - {
> - /* We only support "arm,scmi-smc" binding for now */
> - DT_MATCH_COMPATIBLE("arm,scmi-smc"),
> - { /* sentinel */ },
> - };
> - const struct dt_device_node *scmi_node;
> - int ret;
> + if ( !is_hardware_domain(d) )
> + return;
>
> - /* If no SCMI firmware node found, fail silently as it's not mandatory */
> - scmi_node = dt_find_matching_node(NULL, scmi_ids);
> - if ( !scmi_node )
> - return -EOPNOTSUPP;
> + printk(XENLOG_DEBUG "SCMI: %pd destroy\n", d);
> +}
>
> - ret = dt_property_read_u32(scmi_node, SCMI_SMC_ID_PROP, &scmi_smc_id);
> - if ( !ret )
> +static int __init scmi_check_smccc_ver(void)
> +{
> + if ( smccc_ver < ARM_SMCCC_VERSION_1_1 )
> {
> - printk(XENLOG_ERR "SCMI: No valid \"%s\" property in \"%s\" DT node\n",
> - SCMI_SMC_ID_PROP, scmi_node->full_name);
> - return -ENOENT;
> + printk(XENLOG_WARNING
> + "SCMI: No SMCCC 1.1 support, SCMI calls forwarding disabled\n");
> + return -ENOSYS;
> }
>
> - scmi_enabled = true;
> -
> return 0;
> }
>
> +static const struct sci_mediator_ops scmi_smc_ops = {
> + .handle_call = scmi_handle_smc,
> + .domain_init = scmi_smc_domain_init,
> + .domain_destroy = scmi_smc_domain_destroy,
> +};
> +
> /* Initialize the SCMI layer based on SMCs and Device-tree */
> -static int __init scmi_init(void)
> +static int __init scmi_dom0_init(struct dt_device_node *dev, const void *data)
> {
> int ret;
>
> @@ -134,22 +127,36 @@ static int __init scmi_init(void)
> if ( ret )
> return ret;
>
> - ret = scmi_dt_init_smccc();
> - if ( ret == -EOPNOTSUPP )
> - return ret;
> + ret = dt_property_read_u32(dev, SCMI_SMC_ID_PROP, &scmi_smc_id);
> + if ( !ret )
> + {
> + printk(XENLOG_ERR "SCMI: No valid \"%s\" property in \"%s\" DT node\n",
> + SCMI_SMC_ID_PROP, dt_node_full_name(dev));
> + return -ENOENT;
> + }
> +
> + ret = sci_register(&scmi_smc_ops);
> if ( ret )
> - goto err;
> + {
> + printk(XENLOG_ERR "SCMI: mediator already registered (ret = %d)\n",
> + ret);
> + return ret;
> + }
>
> printk(XENLOG_INFO "Using SCMI with SMC ID: 0x%x\n", scmi_smc_id);
>
> return 0;
> -
> - err:
> - printk(XENLOG_ERR "SCMI: Initialization failed (ret = %d)\n", ret);
> - return ret;
> }
>
> -__initcall(scmi_init);
> +static const struct dt_device_match scmi_smc_match[] __initconst = {
> + DT_MATCH_COMPATIBLE("arm,scmi-smc"),
> + { /* sentinel */ },
> +};
> +
> +DT_DEVICE_START(gicv3, "SCMI SMC DOM0", DEVICE_ARM_SCI)
The name should be scmi_smc instead of gicv3?
> + .dt_match = scmi_smc_match,
> + .init = scmi_dom0_init,
> +DT_DEVICE_END
>
> /*
> * Local variables:
> diff --git a/xen/arch/arm/include/asm/firmware/scmi-smc.h b/xen/arch/arm/include/asm/firmware/scmi-smc.h
> deleted file mode 100644
> index 6b1a164a400e..000000000000
> --- a/xen/arch/arm/include/asm/firmware/scmi-smc.h
> +++ /dev/null
> @@ -1,41 +0,0 @@
> -/* SPDX-License-Identifier: GPL-2.0-only */
> -/*
> - * xen/arch/arm/include/asm/firmware/scmi-smc.h
> - *
> - * ARM System Control and Management Interface (SCMI) over SMC
> - * Generic handling layer
> - *
> - * Andrei Cherechesu <andrei.cherechesu@nxp.com>
> - * Copyright 2024 NXP
> - */
> -
> -#ifndef __ASM_SCMI_SMC_H__
> -#define __ASM_SCMI_SMC_H__
> -
> -#include <xen/types.h>
> -
> -struct cpu_user_regs;
> -
> -#ifdef CONFIG_SCMI_SMC
> -
> -bool scmi_handle_smc(struct cpu_user_regs *regs);
> -
> -#else
> -
> -static inline bool scmi_handle_smc(struct cpu_user_regs *regs)
> -{
> - return false;
> -}
> -
> -#endif /* CONFIG_SCMI_SMC */
> -
> -#endif /* __ASM_SCMI_H__ */
> -
> -/*
> - * Local variables:
> - * mode: C
> - * c-file-style: "BSD"
> - * c-basic-offset: 4
> - * indent-tabs-mode: nil
> - * End:
> - */
> diff --git a/xen/arch/arm/vsmc.c b/xen/arch/arm/vsmc.c
> index 51b3c0297314..b33c69a1c22a 100644
> --- a/xen/arch/arm/vsmc.c
> +++ b/xen/arch/arm/vsmc.c
> @@ -21,7 +21,6 @@
> #include <asm/traps.h>
> #include <asm/vpsci.h>
> #include <asm/platform.h>
> -#include <asm/firmware/scmi-smc.h>
>
> /* Number of functions currently supported by Hypervisor Service. */
> #define XEN_SMCCC_FUNCTION_COUNT 3
> @@ -233,7 +232,7 @@ static bool handle_sip(struct cpu_user_regs *regs)
> if ( platform_smc(regs) )
> return true;
>
> - return scmi_handle_smc(regs);
> + return sci_handle_call(regs);
Is this change...
> }
>
> /*
> @@ -301,8 +300,6 @@ 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);
the one allowing this to be removed? Are we sure all possible SCIs are
SIPs?
> 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/include/public/arch-arm.h b/xen/include/public/arch-arm.h
> index 55eed9992c9d..095b1a23e30c 100644
> --- a/xen/include/public/arch-arm.h
> +++ b/xen/include/public/arch-arm.h
> @@ -328,6 +328,7 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t);
> #define XEN_DOMCTL_CONFIG_TEE_FFA 2
>
> #define XEN_DOMCTL_CONFIG_ARM_SCI_NONE 0
> +#define XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC 1
>
> struct xen_arch_domainconfig {
> /* IN/OUT */
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [RFC PATCH v3 2/7] xen/arm: scmi-smc: update to be used under sci subsystem
2025-03-13 23:48 ` Stefano Stabellini
@ 2025-03-24 15:08 ` Grygorii Strashko
0 siblings, 0 replies; 30+ messages in thread
From: Grygorii Strashko @ 2025-03-24 15:08 UTC (permalink / raw)
To: Stefano Stabellini, Grygorii Strashko
Cc: xen-devel, Julien Grall, Andrew Cooper, Michal Orzel,
Roger Pau Monne, Jan Beulich, Anthony PERARD, Volodymyr Babchuk,
Oleksii Moisieiev, Bertrand Marquis, Juergen Gross
Hi Stefano,
On 14.03.25 01:48, Stefano Stabellini wrote:
> On Tue, 11 Mar 2025, Grygorii Strashko wrote:
>> The introduced SCI (System Control Interface) subsystem provides unified
>> interface to integrate in Xen SCI drivers which adds support for ARM
>> firmware (EL3, SCP) based software interfaces (like SCMI) that are used in
>> system management. The SCI subsystem allows to add drivers for different FW
>> interfaces or have different drivers for the same FW interface (for example,
>> SCMI with different transports).
>>
>> This patch updates SCMI over SMC calls handling layer, introduced by
>> commit 3e322bef8bc0 ("xen/arm: firmware: Add SCMI over SMC calls handling
>> layer"), to be SCI driver:
>> - convert to DT device;
>> - convert to SCI Xen interface.
>>
>> Signed-off-by: Grygorii Strashko <grygorii_strashko@epam.com>
>> ---
>> xen/arch/arm/firmware/Kconfig | 13 ++-
>> xen/arch/arm/firmware/scmi-smc.c | 93 +++++++++++---------
>> xen/arch/arm/include/asm/firmware/scmi-smc.h | 41 ---------
>> xen/arch/arm/vsmc.c | 5 +-
>> xen/include/public/arch-arm.h | 1 +
>> 5 files changed, 64 insertions(+), 89 deletions(-)
>> delete mode 100644 xen/arch/arm/include/asm/firmware/scmi-smc.h
>>
>> diff --git a/xen/arch/arm/firmware/Kconfig b/xen/arch/arm/firmware/Kconfig
>> index fc7918c7fc56..02d7b600317f 100644
[...]
>>
>> /*
>> * Local variables:
>> diff --git a/xen/arch/arm/include/asm/firmware/scmi-smc.h b/xen/arch/arm/include/asm/firmware/scmi-smc.h
>> deleted file mode 100644
>> index 6b1a164a400e..000000000000
>> --- a/xen/arch/arm/include/asm/firmware/scmi-smc.h
>> +++ /dev/null
>> @@ -1,41 +0,0 @@
>> -/* SPDX-License-Identifier: GPL-2.0-only */
>> -/*
>> - * xen/arch/arm/include/asm/firmware/scmi-smc.h
>> - *
>> - * ARM System Control and Management Interface (SCMI) over SMC
>> - * Generic handling layer
>> - *
>> - * Andrei Cherechesu <andrei.cherechesu@nxp.com>
>> - * Copyright 2024 NXP
>> - */
>> -
>> -#ifndef __ASM_SCMI_SMC_H__
>> -#define __ASM_SCMI_SMC_H__
>> -
>> -#include <xen/types.h>
>> -
>> -struct cpu_user_regs;
>> -
>> -#ifdef CONFIG_SCMI_SMC
>> -
>> -bool scmi_handle_smc(struct cpu_user_regs *regs);
>> -
>> -#else
>> -
>> -static inline bool scmi_handle_smc(struct cpu_user_regs *regs)
>> -{
>> - return false;
>> -}
>> -
>> -#endif /* CONFIG_SCMI_SMC */
>> -
>> -#endif /* __ASM_SCMI_H__ */
>> -
>> -/*
>> - * Local variables:
>> - * mode: C
>> - * c-file-style: "BSD"
>> - * c-basic-offset: 4
>> - * indent-tabs-mode: nil
>> - * End:
>> - */
>> diff --git a/xen/arch/arm/vsmc.c b/xen/arch/arm/vsmc.c
>> index 51b3c0297314..b33c69a1c22a 100644
>> --- a/xen/arch/arm/vsmc.c
>> +++ b/xen/arch/arm/vsmc.c
>> @@ -21,7 +21,6 @@
>> #include <asm/traps.h>
>> #include <asm/vpsci.h>
>> #include <asm/platform.h>
>> -#include <asm/firmware/scmi-smc.h>
>>
>> /* Number of functions currently supported by Hypervisor Service. */
>> #define XEN_SMCCC_FUNCTION_COUNT 3
>> @@ -233,7 +232,7 @@ static bool handle_sip(struct cpu_user_regs *regs)
>> if ( platform_smc(regs) )
>> return true;
>>
>> - return scmi_handle_smc(regs);
>> + return sci_handle_call(regs);
>
> Is this change...
>
>
>> }
>>
>> /*
>> @@ -301,8 +300,6 @@ 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);
>
> the one allowing this to be removed?
Yes, it's just switch from API introduced by commit 3e322bef8bc0 ("xen/arm:
firmware: Add SCMI over SMC calls handling layer") to more generic SCI API.
>Are we sure all possible SCIs are SIPs?
Yes, as I see it, for sure for SCMI.
SCI is platform specific and so SiP.
>
>
>> 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/include/public/arch-arm.h b/xen/include/public/arch-arm.h
>> index 55eed9992c9d..095b1a23e30c 100644
>> --- a/xen/include/public/arch-arm.h
>> +++ b/xen/include/public/arch-arm.h
>> @@ -328,6 +328,7 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t);
>> #define XEN_DOMCTL_CONFIG_TEE_FFA 2
>>
>> #define XEN_DOMCTL_CONFIG_ARM_SCI_NONE 0
>> +#define XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC 1
>>
>> struct xen_arch_domainconfig {
>> /* IN/OUT */
>> --
>> 2.34.1
>>
--
Best regards,
-grygorii
^ permalink raw reply [flat|nested] 30+ messages in thread
* [RFC PATCH v3 3/7] xen/arm: scmi-smc: passthrough SCMI SMC to guest domain
2025-03-11 11:16 [RFC PATCH v3 0/7] xen/arm: scmi: introduce SCI SCMI SMC multi-agent support Grygorii Strashko
2025-03-11 11:16 ` [RFC PATCH v3 1/7] xen/arm: add generic SCI subsystem Grygorii Strashko
2025-03-11 11:16 ` [RFC PATCH v3 2/7] xen/arm: scmi-smc: update to be used under sci subsystem Grygorii Strashko
@ 2025-03-11 11:16 ` Grygorii Strashko
2025-03-13 23:48 ` Stefano Stabellini
2025-03-14 16:23 ` Anthony PERARD
2025-03-11 11:16 ` [RFC PATCH v3 4/7] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver Grygorii Strashko
` (4 subsequent siblings)
7 siblings, 2 replies; 30+ messages in thread
From: Grygorii Strashko @ 2025-03-11 11:16 UTC (permalink / raw)
To: xen-devel
Cc: Stefano Stabellini, Julien Grall, Andrew Cooper, Michal Orzel,
Roger Pau Monne, Jan Beulich, Anthony PERARD, Volodymyr Babchuk,
Oleksii Moisieiev, Bertrand Marquis, Juergen Gross,
Grygorii Strashko
The commit 3e322bef8bc0 ("xen/arm: firmware: Add SCMI over SMC calls
handling layer") introduces simple driver which forwards SCMI over SMC
calls from hwdom/dom0 to EL3 firmware (TF-A) with a single SCMI OSPM agent
support. While it working gracefully for hwdom/dom0 use case it doesn't
cover "thin Dom0 with guest domain, which serves as Driver domain"
use-case. In this case HW need to be enable in Driver domain and dom0 is
performing only control functions.
The EL3 SCMI firmware (TF-A) with a single SCMI OSPM agent support is
pretty generic case for the default vendors SDK and new platforms.
This patch enables passthrough of SCMI SMC single agent interface to the
Driver domain which can be enabled in the following way:
- dom0: add dom0_scmi_smc_passthrough to the Xen Command Line
- domD: xl.cfg add "arm_sci" option as below
arm_sci = "type=scmi_smc"
- domD: xl.cfg enable access to the "arm,scmi-shmem"
iomem = [
"47ff0,1@22001",
]
- domD: add scmi nodes to the Driver domain partial device tree as in the
below example:
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";
shmem = <&scmi_shm_0>;
...
}
}
}
The SCMI SMC single agent interface can be enabled for one and only one
domain. In general, the configuration is similar to any other HW
passthrough, except explicitly enabling SCMI with "arm_sci" xl.cfg option.
Note that SCMI and "arm,scmi-shmem" nodes will be removed from
dom0 DT.
Signed-off-by: Grygorii Strashko <grygorii_strashko@epam.com>
---
docs/man/xl.cfg.5.pod.in | 36 ++++++++++++++
docs/misc/xen-command-line.pandoc | 9 ++++
tools/include/libxl.h | 5 ++
tools/libs/light/libxl_arm.c | 13 ++++++
tools/libs/light/libxl_types.idl | 10 ++++
tools/xl/xl_parse.c | 66 ++++++++++++++++++++++++++
xen/arch/arm/firmware/Kconfig | 4 +-
xen/arch/arm/firmware/scmi-smc.c | 78 +++++++++++++++++++++++++++++--
8 files changed, 217 insertions(+), 4 deletions(-)
diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in
index 8e1422104e50..7edf272386e3 100644
--- a/docs/man/xl.cfg.5.pod.in
+++ b/docs/man/xl.cfg.5.pod.in
@@ -3094,6 +3094,42 @@ assigned to the domain.
=back
+=over 4
+
+=item B<arm_sci="ARM_SCI_STRING">
+
+Set ARM_SCI specific options for the guest. ARM SCI is System
+Control Protocol allows domain to manage various functions that are provided
+by HW platform firmware.
+
+B<ARM_SCI_STRING> is a comma separated list of C<KEY=VALUE> settings,
+from the following list:
+
+=over 4
+
+=item B<type=STRING>
+
+Specifies an ARM SCI type for the guest.
+
+=over 4
+
+=item B<none>
+
+Don't allow guest to use ARM SCI if present on the platform. This is the
+default value.
+
+=item B<scmi_smc>
+
+Enables ARM SCMI SMC support for the guest by enabling SCMI over SMC calls
+forwarding from domain to the EL3 firmware (like Trusted Firmware-A) with a
+single SCMI OSPM agent support.
+Should be used together with B<dom0_scmi_smc_passthrough> Xen command line
+option.
+
+=back
+
+=back
+
=head3 x86
=over 4
diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc
index 9bbd00baef91..8e50f6b7c7ac 100644
--- a/docs/misc/xen-command-line.pandoc
+++ b/docs/misc/xen-command-line.pandoc
@@ -1082,6 +1082,15 @@ affinities to prefer but be not limited to the specified node(s).
Pin dom0 vcpus to their respective pcpus
+### dom0_scmi_smc_passthrough (ARM)
+> `= <boolean>`
+
+The option is available when `CONFIG_SCMI_SMC` is compiled in, and allows to
+enable SCMI SMC single agent interface for any, but only one guest domain,
+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).
+
### dtuart (ARM)
> `= path [:options]`
diff --git a/tools/include/libxl.h b/tools/include/libxl.h
index f8fe4afd7dca..5fa43637ab76 100644
--- a/tools/include/libxl.h
+++ b/tools/include/libxl.h
@@ -313,6 +313,11 @@
*/
#define LIBXL_HAVE_BUILDINFO_ARCH_NR_SPIS 1
+/*
+ * libxl_domain_build_info has the arch_arm.sci* fields.
+ */
+#define LIBXL_HAVE_BUILDINFO_ARCH_ARM_SCI 1
+
/*
* LIBXL_HAVE_SOFT_RESET indicates that libxl supports performing
* 'soft reset' for domains and there is 'soft_reset' shutdown reason
diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c
index 28cea1f643c9..d41adea1cefd 100644
--- a/tools/libs/light/libxl_arm.c
+++ b/tools/libs/light/libxl_arm.c
@@ -222,6 +222,19 @@ int libxl__arch_domain_prepare_config(libxl__gc *gc,
config->arch.sve_vl = d_config->b_info.arch_arm.sve_vl / 128U;
}
+ switch (d_config->b_info.arm_sci.type) {
+ case LIBXL_ARM_SCI_TYPE_NONE:
+ config->arch.arm_sci_type = XEN_DOMCTL_CONFIG_ARM_SCI_NONE;
+ break;
+ case LIBXL_ARM_SCI_TYPE_SCMI_SMC:
+ config->arch.arm_sci_type = XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC;
+ break;
+ default:
+ LOG(ERROR, "Unknown ARM_SCI type %d",
+ d_config->b_info.arm_sci.type);
+ return ERROR_FAIL;
+ }
+
return 0;
}
diff --git a/tools/libs/light/libxl_types.idl b/tools/libs/light/libxl_types.idl
index 33c9cfc1a267..ea0d30654cdd 100644
--- a/tools/libs/light/libxl_types.idl
+++ b/tools/libs/light/libxl_types.idl
@@ -551,6 +551,15 @@ libxl_sve_type = Enumeration("sve_type", [
(2048, "2048")
], init_val = "LIBXL_SVE_TYPE_DISABLED")
+libxl_arm_sci_type = Enumeration("arm_sci_type", [
+ (0, "none"),
+ (1, "scmi_smc")
+ ], init_val = "LIBXL_ARM_SCI_TYPE_NONE")
+
+libxl_arm_sci = Struct("arm_sci", [
+ ("type", libxl_arm_sci_type),
+ ])
+
libxl_rdm_reserve = Struct("rdm_reserve", [
("strategy", libxl_rdm_reserve_strategy),
("policy", libxl_rdm_reserve_policy),
@@ -639,6 +648,7 @@ libxl_domain_build_info = Struct("domain_build_info",[
("apic", libxl_defbool),
("dm_restrict", libxl_defbool),
("tee", libxl_tee_type),
+ ("arm_sci", libxl_arm_sci),
("u", KeyedUnion(None, libxl_domain_type, "type",
[("hvm", Struct(None, [("firmware", string),
("bios", libxl_bios_type),
diff --git a/tools/xl/xl_parse.c b/tools/xl/xl_parse.c
index 9a3679c02325..ac9bf0b25c5a 100644
--- a/tools/xl/xl_parse.c
+++ b/tools/xl/xl_parse.c
@@ -1284,6 +1284,63 @@ out:
if (rc) exit(EXIT_FAILURE);
}
+static int parse_arm_sci_config(XLU_Config *cfg, libxl_arm_sci *arm_sci,
+ const char *str)
+{
+ enum {
+ STATE_OPTION,
+ STATE_TYPE,
+ STATE_TERMINAL,
+ };
+ int ret, state = STATE_OPTION;
+ char *buf2, *tok, *ptr, *end;
+
+ if (NULL == (buf2 = ptr = strdup(str)))
+ return ERROR_NOMEM;
+
+ for (tok = ptr, end = ptr + strlen(ptr) + 1; ptr < end; ptr++) {
+ switch(state) {
+ case STATE_OPTION:
+ if (*ptr == '=') {
+ *ptr = '\0';
+ if (!strcmp(tok, "type")) {
+ state = STATE_TYPE;
+ } else {
+ fprintf(stderr, "Unknown ARM_SCI option: %s\n", tok);
+ goto parse_error;
+ }
+ tok = ptr + 1;
+ }
+ break;
+ case STATE_TYPE:
+ if (*ptr == '\0' || *ptr == ',') {
+ state = *ptr == ',' ? STATE_OPTION : STATE_TERMINAL;
+ *ptr = '\0';
+ ret = libxl_arm_sci_type_from_string(tok, &arm_sci->type);
+ if (ret) {
+ fprintf(stderr, "Unknown ARM_SCI type: %s\n", tok);
+ goto parse_error;
+ }
+ tok = ptr + 1;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (tok != ptr || state != STATE_TERMINAL)
+ goto parse_error;
+
+ free(buf2);
+
+ return 0;
+
+parse_error:
+ free(buf2);
+ return ERROR_INVAL;
+}
+
void parse_config_data(const char *config_source,
const char *config_data,
int config_len,
@@ -2981,6 +3038,15 @@ skip_usbdev:
if (!xlu_cfg_get_long (config, "nr_spis", &l, 0))
b_info->arch_arm.nr_spis = l;
+ if (!xlu_cfg_get_string(config, "arm_sci", &buf, 1)) {
+ libxl_arm_sci arm_sci = { 0 };
+ if (!parse_arm_sci_config(config, &arm_sci, buf)) {
+ b_info->arm_sci.type = arm_sci.type;
+ } else {
+ exit(EXIT_FAILURE);
+ }
+ }
+
parse_vkb_list(config, d_config);
d_config->virtios = NULL;
diff --git a/xen/arch/arm/firmware/Kconfig b/xen/arch/arm/firmware/Kconfig
index 02d7b600317f..2686ba313898 100644
--- a/xen/arch/arm/firmware/Kconfig
+++ b/xen/arch/arm/firmware/Kconfig
@@ -25,7 +25,9 @@ config SCMI_SMC
doorbell mechanism and Shared Memory for transport ("arm,scmi-smc"
compatible only). The value of "arm,smc-id" DT property from SCMI
firmware node is used to trap and forward corresponding SCMI SMCs
- to firmware running at EL3, for calls coming from the hardware domain.
+ to firmware running at EL3, for calls coming from the hardware domain or
+ driver domain.
+ Use with EL3 firmware which supports only single SCMI OSPM agent.
endchoice
diff --git a/xen/arch/arm/firmware/scmi-smc.c b/xen/arch/arm/firmware/scmi-smc.c
index 188bd659513b..7a0130ea0ea9 100644
--- a/xen/arch/arm/firmware/scmi-smc.c
+++ b/xen/arch/arm/firmware/scmi-smc.c
@@ -14,6 +14,8 @@
#include <xen/device_tree.h>
#include <xen/errno.h>
#include <xen/init.h>
+#include <xen/iocap.h>
+#include <xen/param.h>
#include <xen/sched.h>
#include <xen/types.h>
@@ -22,7 +24,11 @@
#define SCMI_SMC_ID_PROP "arm,smc-id"
+static bool __ro_after_init opt_dom0_scmi_smc_passthrough = false;
+boolean_param("dom0_scmi_smc_passthrough", opt_dom0_scmi_smc_passthrough);
+
static uint32_t __ro_after_init scmi_smc_id;
+static struct domain *scmi_dom;
/*
* Check if provided SMC Function Identifier matches the one known by the SCMI
@@ -50,7 +56,7 @@ static bool scmi_handle_smc(struct cpu_user_regs *regs)
return false;
/* Only the hardware domain should use SCMI calls */
- if ( !is_hardware_domain(current->domain) )
+ if ( scmi_dom != current->domain )
{
gdprintk(XENLOG_WARNING, "SCMI: Unprivileged access attempt\n");
return false;
@@ -78,9 +84,18 @@ static bool scmi_handle_smc(struct cpu_user_regs *regs)
static int scmi_smc_domain_init(struct domain *d,
struct xen_domctl_createdomain *config)
{
- if ( !is_hardware_domain(d) )
+ if ( !opt_dom0_scmi_smc_passthrough && !is_hardware_domain(d) )
+ return 0;
+
+ if ( opt_dom0_scmi_smc_passthrough &&
+ (config->arch.arm_sci_type != XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC ||
+ is_hardware_domain(d)) )
return 0;
+ if ( scmi_dom )
+ return -EEXIST;
+
+ scmi_dom = d;
d->arch.sci_enabled = true;
printk(XENLOG_DEBUG "SCMI: %pd init\n", d);
return 0;
@@ -88,12 +103,68 @@ static int scmi_smc_domain_init(struct domain *d,
static void scmi_smc_domain_destroy(struct domain *d)
{
- if ( !is_hardware_domain(d) )
+ if ( scmi_dom && scmi_dom != d )
return;
+ scmi_dom = NULL;
+ d->arch.sci_enabled = false;
printk(XENLOG_DEBUG "SCMI: %pd destroy\n", d);
}
+/*
+ * Handle Dom0 SCMI SMC specific DT nodes
+ *
+ * Copy SCMI nodes into Dom0 device tree if dom0_scmi_smc_passthrough=false.
+ *
+ */
+static bool scmi_smc_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 ( dt_match_node(shmem_matches, node) && !sci_domain_is_enabled(d) )
+ {
+ dt_dprintk(" Skip scmi shmem node\n");
+ return true;
+ }
+
+ if ( dt_match_node(scmi_matches, node) && !sci_domain_is_enabled(d) )
+ {
+ struct dt_device_node *shmem_node;
+ const __be32 *prop;
+ u64 paddr, size;
+ int ret;
+
+ dt_dprintk(" Skip scmi node\n");
+
+ prop = dt_get_property(node, "shmem", NULL);
+ if ( !prop )
+ return true;
+
+ shmem_node = dt_find_node_by_phandle(be32_to_cpup(prop));
+ if ( !shmem_node )
+ return true;
+
+ ret = dt_device_get_address(shmem_node, 0, &paddr, &size);
+ if ( ret )
+ return true;
+
+ ret = iomem_permit_access(d, paddr_to_pfn(paddr),
+ paddr_to_pfn(paddr + size - 1));
+
+ return true;
+ }
+
+ return false;
+}
+
static int __init scmi_check_smccc_ver(void)
{
if ( smccc_ver < ARM_SMCCC_VERSION_1_1 )
@@ -110,6 +181,7 @@ static const struct sci_mediator_ops scmi_smc_ops = {
.handle_call = scmi_handle_smc,
.domain_init = scmi_smc_domain_init,
.domain_destroy = scmi_smc_domain_destroy,
+ .dom0_dt_handle_node = scmi_smc_dt_handle_node,
};
/* Initialize the SCMI layer based on SMCs and Device-tree */
--
2.34.1
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [RFC PATCH v3 3/7] xen/arm: scmi-smc: passthrough SCMI SMC to guest domain
2025-03-11 11:16 ` [RFC PATCH v3 3/7] xen/arm: scmi-smc: passthrough SCMI SMC to guest domain Grygorii Strashko
@ 2025-03-13 23:48 ` Stefano Stabellini
2025-03-24 16:05 ` Grygorii Strashko
2025-03-14 16:23 ` Anthony PERARD
1 sibling, 1 reply; 30+ messages in thread
From: Stefano Stabellini @ 2025-03-13 23:48 UTC (permalink / raw)
To: Grygorii Strashko
Cc: xen-devel, Stefano Stabellini, Julien Grall, Andrew Cooper,
Michal Orzel, Roger Pau Monne, Jan Beulich, Anthony PERARD,
Volodymyr Babchuk, Oleksii Moisieiev, Bertrand Marquis,
Juergen Gross, Grygorii Strashko
On Tue, 11 Mar 2025, Grygorii Strashko wrote:
> The commit 3e322bef8bc0 ("xen/arm: firmware: Add SCMI over SMC calls
> handling layer") introduces simple driver which forwards SCMI over SMC
> calls from hwdom/dom0 to EL3 firmware (TF-A) with a single SCMI OSPM agent
> support. While it working gracefully for hwdom/dom0 use case it doesn't
> cover "thin Dom0 with guest domain, which serves as Driver domain"
> use-case. In this case HW need to be enable in Driver domain and dom0 is
> performing only control functions.
>
> The EL3 SCMI firmware (TF-A) with a single SCMI OSPM agent support is
> pretty generic case for the default vendors SDK and new platforms.
>
> This patch enables passthrough of SCMI SMC single agent interface to the
> Driver domain which can be enabled in the following way:
>
> - dom0: add dom0_scmi_smc_passthrough to the Xen Command Line
> - domD: xl.cfg add "arm_sci" option as below
>
> arm_sci = "type=scmi_smc"
>
> - domD: xl.cfg enable access to the "arm,scmi-shmem"
>
> iomem = [
> "47ff0,1@22001",
> ]
>
> - domD: add scmi nodes to the Driver domain partial device tree as in the
> below example:
>
> 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";
> shmem = <&scmi_shm_0>;
> ...
> }
> }
> }
>
> The SCMI SMC single agent interface can be enabled for one and only one
> domain. In general, the configuration is similar to any other HW
> passthrough, except explicitly enabling SCMI with "arm_sci" xl.cfg option.
>
> Note that SCMI and "arm,scmi-shmem" nodes will be removed from
> dom0 DT.
>
> Signed-off-by: Grygorii Strashko <grygorii_strashko@epam.com>
> ---
> docs/man/xl.cfg.5.pod.in | 36 ++++++++++++++
> docs/misc/xen-command-line.pandoc | 9 ++++
> tools/include/libxl.h | 5 ++
> tools/libs/light/libxl_arm.c | 13 ++++++
> tools/libs/light/libxl_types.idl | 10 ++++
> tools/xl/xl_parse.c | 66 ++++++++++++++++++++++++++
> xen/arch/arm/firmware/Kconfig | 4 +-
> xen/arch/arm/firmware/scmi-smc.c | 78 +++++++++++++++++++++++++++++--
> 8 files changed, 217 insertions(+), 4 deletions(-)
>
> diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in
> index 8e1422104e50..7edf272386e3 100644
> --- a/docs/man/xl.cfg.5.pod.in
> +++ b/docs/man/xl.cfg.5.pod.in
> @@ -3094,6 +3094,42 @@ assigned to the domain.
>
> =back
>
> +=over 4
> +
> +=item B<arm_sci="ARM_SCI_STRING">
> +
> +Set ARM_SCI specific options for the guest. ARM SCI is System
> +Control Protocol allows domain to manage various functions that are provided
> +by HW platform firmware.
> +
> +B<ARM_SCI_STRING> is a comma separated list of C<KEY=VALUE> settings,
> +from the following list:
> +
> +=over 4
> +
> +=item B<type=STRING>
> +
> +Specifies an ARM SCI type for the guest.
> +
> +=over 4
> +
> +=item B<none>
> +
> +Don't allow guest to use ARM SCI if present on the platform. This is the
> +default value.
> +
> +=item B<scmi_smc>
> +
> +Enables ARM SCMI SMC support for the guest by enabling SCMI over SMC calls
> +forwarding from domain to the EL3 firmware (like Trusted Firmware-A) with a
> +single SCMI OSPM agent support.
> +Should be used together with B<dom0_scmi_smc_passthrough> Xen command line
> +option.
> +
> +=back
> +
> +=back
> +
> =head3 x86
>
> =over 4
> diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc
> index 9bbd00baef91..8e50f6b7c7ac 100644
> --- a/docs/misc/xen-command-line.pandoc
> +++ b/docs/misc/xen-command-line.pandoc
> @@ -1082,6 +1082,15 @@ affinities to prefer but be not limited to the specified node(s).
>
> Pin dom0 vcpus to their respective pcpus
>
> +### dom0_scmi_smc_passthrough (ARM)
> +> `= <boolean>`
> +
> +The option is available when `CONFIG_SCMI_SMC` is compiled in, and allows to
> +enable SCMI SMC single agent interface for any, but only one guest domain,
> +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).
> +
> ### dtuart (ARM)
> > `= path [:options]`
>
> diff --git a/tools/include/libxl.h b/tools/include/libxl.h
> index f8fe4afd7dca..5fa43637ab76 100644
> --- a/tools/include/libxl.h
> +++ b/tools/include/libxl.h
> @@ -313,6 +313,11 @@
> */
> #define LIBXL_HAVE_BUILDINFO_ARCH_NR_SPIS 1
>
> +/*
> + * libxl_domain_build_info has the arch_arm.sci* fields.
> + */
> +#define LIBXL_HAVE_BUILDINFO_ARCH_ARM_SCI 1
> +
> /*
> * LIBXL_HAVE_SOFT_RESET indicates that libxl supports performing
> * 'soft reset' for domains and there is 'soft_reset' shutdown reason
> diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c
> index 28cea1f643c9..d41adea1cefd 100644
> --- a/tools/libs/light/libxl_arm.c
> +++ b/tools/libs/light/libxl_arm.c
> @@ -222,6 +222,19 @@ int libxl__arch_domain_prepare_config(libxl__gc *gc,
> config->arch.sve_vl = d_config->b_info.arch_arm.sve_vl / 128U;
> }
>
> + switch (d_config->b_info.arm_sci.type) {
> + case LIBXL_ARM_SCI_TYPE_NONE:
> + config->arch.arm_sci_type = XEN_DOMCTL_CONFIG_ARM_SCI_NONE;
> + break;
> + case LIBXL_ARM_SCI_TYPE_SCMI_SMC:
> + config->arch.arm_sci_type = XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC;
> + break;
> + default:
> + LOG(ERROR, "Unknown ARM_SCI type %d",
> + d_config->b_info.arm_sci.type);
> + return ERROR_FAIL;
> + }
> +
> return 0;
> }
>
> diff --git a/tools/libs/light/libxl_types.idl b/tools/libs/light/libxl_types.idl
> index 33c9cfc1a267..ea0d30654cdd 100644
> --- a/tools/libs/light/libxl_types.idl
> +++ b/tools/libs/light/libxl_types.idl
> @@ -551,6 +551,15 @@ libxl_sve_type = Enumeration("sve_type", [
> (2048, "2048")
> ], init_val = "LIBXL_SVE_TYPE_DISABLED")
>
> +libxl_arm_sci_type = Enumeration("arm_sci_type", [
> + (0, "none"),
> + (1, "scmi_smc")
> + ], init_val = "LIBXL_ARM_SCI_TYPE_NONE")
> +
> +libxl_arm_sci = Struct("arm_sci", [
> + ("type", libxl_arm_sci_type),
> + ])
> +
> libxl_rdm_reserve = Struct("rdm_reserve", [
> ("strategy", libxl_rdm_reserve_strategy),
> ("policy", libxl_rdm_reserve_policy),
> @@ -639,6 +648,7 @@ libxl_domain_build_info = Struct("domain_build_info",[
> ("apic", libxl_defbool),
> ("dm_restrict", libxl_defbool),
> ("tee", libxl_tee_type),
> + ("arm_sci", libxl_arm_sci),
> ("u", KeyedUnion(None, libxl_domain_type, "type",
> [("hvm", Struct(None, [("firmware", string),
> ("bios", libxl_bios_type),
> diff --git a/tools/xl/xl_parse.c b/tools/xl/xl_parse.c
> index 9a3679c02325..ac9bf0b25c5a 100644
> --- a/tools/xl/xl_parse.c
> +++ b/tools/xl/xl_parse.c
> @@ -1284,6 +1284,63 @@ out:
> if (rc) exit(EXIT_FAILURE);
> }
>
> +static int parse_arm_sci_config(XLU_Config *cfg, libxl_arm_sci *arm_sci,
> + const char *str)
> +{
> + enum {
> + STATE_OPTION,
> + STATE_TYPE,
> + STATE_TERMINAL,
> + };
> + int ret, state = STATE_OPTION;
> + char *buf2, *tok, *ptr, *end;
> +
> + if (NULL == (buf2 = ptr = strdup(str)))
> + return ERROR_NOMEM;
> +
> + for (tok = ptr, end = ptr + strlen(ptr) + 1; ptr < end; ptr++) {
> + switch(state) {
> + case STATE_OPTION:
> + if (*ptr == '=') {
> + *ptr = '\0';
> + if (!strcmp(tok, "type")) {
> + state = STATE_TYPE;
> + } else {
> + fprintf(stderr, "Unknown ARM_SCI option: %s\n", tok);
> + goto parse_error;
> + }
> + tok = ptr + 1;
> + }
> + break;
> + case STATE_TYPE:
> + if (*ptr == '\0' || *ptr == ',') {
> + state = *ptr == ',' ? STATE_OPTION : STATE_TERMINAL;
> + *ptr = '\0';
> + ret = libxl_arm_sci_type_from_string(tok, &arm_sci->type);
> + if (ret) {
> + fprintf(stderr, "Unknown ARM_SCI type: %s\n", tok);
> + goto parse_error;
> + }
> + tok = ptr + 1;
> + }
> + break;
> + default:
> + break;
> + }
> + }
> +
> + if (tok != ptr || state != STATE_TERMINAL)
> + goto parse_error;
> +
> + free(buf2);
> +
> + return 0;
> +
> +parse_error:
> + free(buf2);
> + return ERROR_INVAL;
> +}
> +
> void parse_config_data(const char *config_source,
> const char *config_data,
> int config_len,
> @@ -2981,6 +3038,15 @@ skip_usbdev:
> if (!xlu_cfg_get_long (config, "nr_spis", &l, 0))
> b_info->arch_arm.nr_spis = l;
>
> + if (!xlu_cfg_get_string(config, "arm_sci", &buf, 1)) {
> + libxl_arm_sci arm_sci = { 0 };
> + if (!parse_arm_sci_config(config, &arm_sci, buf)) {
> + b_info->arm_sci.type = arm_sci.type;
> + } else {
> + exit(EXIT_FAILURE);
> + }
> + }
> +
> parse_vkb_list(config, d_config);
>
> d_config->virtios = NULL;
> diff --git a/xen/arch/arm/firmware/Kconfig b/xen/arch/arm/firmware/Kconfig
> index 02d7b600317f..2686ba313898 100644
> --- a/xen/arch/arm/firmware/Kconfig
> +++ b/xen/arch/arm/firmware/Kconfig
> @@ -25,7 +25,9 @@ config SCMI_SMC
> doorbell mechanism and Shared Memory for transport ("arm,scmi-smc"
> compatible only). The value of "arm,smc-id" DT property from SCMI
> firmware node is used to trap and forward corresponding SCMI SMCs
> - to firmware running at EL3, for calls coming from the hardware domain.
> + to firmware running at EL3, for calls coming from the hardware domain or
> + driver domain.
> + Use with EL3 firmware which supports only single SCMI OSPM agent.
>
> endchoice
>
> diff --git a/xen/arch/arm/firmware/scmi-smc.c b/xen/arch/arm/firmware/scmi-smc.c
> index 188bd659513b..7a0130ea0ea9 100644
> --- a/xen/arch/arm/firmware/scmi-smc.c
> +++ b/xen/arch/arm/firmware/scmi-smc.c
> @@ -14,6 +14,8 @@
> #include <xen/device_tree.h>
> #include <xen/errno.h>
> #include <xen/init.h>
> +#include <xen/iocap.h>
> +#include <xen/param.h>
> #include <xen/sched.h>
> #include <xen/types.h>
>
> @@ -22,7 +24,11 @@
>
> #define SCMI_SMC_ID_PROP "arm,smc-id"
>
> +static bool __ro_after_init opt_dom0_scmi_smc_passthrough = false;
> +boolean_param("dom0_scmi_smc_passthrough", opt_dom0_scmi_smc_passthrough);
> +
> static uint32_t __ro_after_init scmi_smc_id;
> +static struct domain *scmi_dom;
This could be read_mostly
> /*
> * Check if provided SMC Function Identifier matches the one known by the SCMI
> @@ -50,7 +56,7 @@ static bool scmi_handle_smc(struct cpu_user_regs *regs)
> return false;
>
> /* Only the hardware domain should use SCMI calls */
> - if ( !is_hardware_domain(current->domain) )
> + if ( scmi_dom != current->domain )
> {
> gdprintk(XENLOG_WARNING, "SCMI: Unprivileged access attempt\n");
> return false;
> @@ -78,9 +84,18 @@ static bool scmi_handle_smc(struct cpu_user_regs *regs)
> static int scmi_smc_domain_init(struct domain *d,
> struct xen_domctl_createdomain *config)
> {
> - if ( !is_hardware_domain(d) )
> + if ( !opt_dom0_scmi_smc_passthrough && !is_hardware_domain(d) )
> + return 0;
> +
> + if ( opt_dom0_scmi_smc_passthrough &&
> + (config->arch.arm_sci_type != XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC ||
I am confused by the check "config->arch.arm_sci_type !=
XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC". If the check was true, this
function should not be called?
> + is_hardware_domain(d)) )
> return 0;
>
> + if ( scmi_dom )
> + return -EEXIST;
> +
> + scmi_dom = d;
> d->arch.sci_enabled = true;
> printk(XENLOG_DEBUG "SCMI: %pd init\n", d);
> return 0;
> @@ -88,12 +103,68 @@ static int scmi_smc_domain_init(struct domain *d,
>
> static void scmi_smc_domain_destroy(struct domain *d)
> {
> - if ( !is_hardware_domain(d) )
> + if ( scmi_dom && scmi_dom != d )
> return;
>
> + scmi_dom = NULL;
> + d->arch.sci_enabled = false;
> printk(XENLOG_DEBUG "SCMI: %pd destroy\n", d);
> }
>
> +/*
> + * Handle Dom0 SCMI SMC specific DT nodes
> + *
> + * Copy SCMI nodes into Dom0 device tree if dom0_scmi_smc_passthrough=false.
I am confused by this: shouldn't scmi_smc_dt_handle_node be part of the
previous patch? Otherwise, how can it work for dom0?
> + */
> +static bool scmi_smc_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 ( dt_match_node(shmem_matches, node) && !sci_domain_is_enabled(d) )
> + {
> + dt_dprintk(" Skip scmi shmem node\n");
> + return true;
> + }
> +
> + if ( dt_match_node(scmi_matches, node) && !sci_domain_is_enabled(d) )
This seems wrong: we are allowing access to the shmem region if
!sci_domain_is_enabled(d). Shouldn't it be:
if ( dt_match_node(scmi_matches, node) && sci_domain_is_enabled(d) )
> + {
> + struct dt_device_node *shmem_node;
> + const __be32 *prop;
> + u64 paddr, size;
> + int ret;
> +
> + dt_dprintk(" Skip scmi node\n");
> +
> + prop = dt_get_property(node, "shmem", NULL);
> + if ( !prop )
> + return true;
> +
> + shmem_node = dt_find_node_by_phandle(be32_to_cpup(prop));
> + if ( !shmem_node )
> + return true;
> +
> + ret = dt_device_get_address(shmem_node, 0, &paddr, &size);
> + if ( ret )
> + return true;
> +
> + ret = iomem_permit_access(d, paddr_to_pfn(paddr),
> + paddr_to_pfn(paddr + size - 1));
> +
> + return true;
> + }
> +
> + return false;
> +}
> +
> static int __init scmi_check_smccc_ver(void)
> {
> if ( smccc_ver < ARM_SMCCC_VERSION_1_1 )
> @@ -110,6 +181,7 @@ static const struct sci_mediator_ops scmi_smc_ops = {
> .handle_call = scmi_handle_smc,
> .domain_init = scmi_smc_domain_init,
> .domain_destroy = scmi_smc_domain_destroy,
> + .dom0_dt_handle_node = scmi_smc_dt_handle_node,
> };
>
> /* Initialize the SCMI layer based on SMCs and Device-tree */
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [RFC PATCH v3 3/7] xen/arm: scmi-smc: passthrough SCMI SMC to guest domain
2025-03-13 23:48 ` Stefano Stabellini
@ 2025-03-24 16:05 ` Grygorii Strashko
0 siblings, 0 replies; 30+ messages in thread
From: Grygorii Strashko @ 2025-03-24 16:05 UTC (permalink / raw)
To: Stefano Stabellini, Grygorii Strashko
Cc: xen-devel, Julien Grall, Andrew Cooper, Michal Orzel,
Roger Pau Monne, Jan Beulich, Anthony PERARD, Volodymyr Babchuk,
Oleksii Moisieiev, Bertrand Marquis, Juergen Gross
On 14.03.25 01:48, Stefano Stabellini wrote:
> On Tue, 11 Mar 2025, Grygorii Strashko wrote:
>> The commit 3e322bef8bc0 ("xen/arm: firmware: Add SCMI over SMC calls
>> handling layer") introduces simple driver which forwards SCMI over SMC
>> calls from hwdom/dom0 to EL3 firmware (TF-A) with a single SCMI OSPM agent
>> support. While it working gracefully for hwdom/dom0 use case it doesn't
>> cover "thin Dom0 with guest domain, which serves as Driver domain"
>> use-case. In this case HW need to be enable in Driver domain and dom0 is
>> performing only control functions.
>>
>> The EL3 SCMI firmware (TF-A) with a single SCMI OSPM agent support is
>> pretty generic case for the default vendors SDK and new platforms.
>>
>> This patch enables passthrough of SCMI SMC single agent interface to the
>> Driver domain which can be enabled in the following way:
>>
>> - dom0: add dom0_scmi_smc_passthrough to the Xen Command Line
>> - domD: xl.cfg add "arm_sci" option as below
>>
>> arm_sci = "type=scmi_smc"
>>
>> - domD: xl.cfg enable access to the "arm,scmi-shmem"
>>
>> iomem = [
>> "47ff0,1@22001",
>> ]
>>
>> - domD: add scmi nodes to the Driver domain partial device tree as in the
>> below example:
>>
>> 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";
>> shmem = <&scmi_shm_0>;
>> ...
>> }
>> }
>> }
>>
>> The SCMI SMC single agent interface can be enabled for one and only one
>> domain. In general, the configuration is similar to any other HW
>> passthrough, except explicitly enabling SCMI with "arm_sci" xl.cfg option.
>>
>> Note that SCMI and "arm,scmi-shmem" nodes will be removed from
>> dom0 DT.
>>
>> Signed-off-by: Grygorii Strashko <grygorii_strashko@epam.com>
>> ---
>> docs/man/xl.cfg.5.pod.in | 36 ++++++++++++++
>> docs/misc/xen-command-line.pandoc | 9 ++++
>> tools/include/libxl.h | 5 ++
>> tools/libs/light/libxl_arm.c | 13 ++++++
>> tools/libs/light/libxl_types.idl | 10 ++++
>> tools/xl/xl_parse.c | 66 ++++++++++++++++++++++++++
>> xen/arch/arm/firmware/Kconfig | 4 +-
>> xen/arch/arm/firmware/scmi-smc.c | 78 +++++++++++++++++++++++++++++--
>> 8 files changed, 217 insertions(+), 4 deletions(-)
>>
[...]
>
>> /*
>> * Check if provided SMC Function Identifier matches the one known by the SCMI
>> @@ -50,7 +56,7 @@ static bool scmi_handle_smc(struct cpu_user_regs *regs)
>> return false;
>>
>> /* Only the hardware domain should use SCMI calls */
>> - if ( !is_hardware_domain(current->domain) )
>> + if ( scmi_dom != current->domain )
>> {
>> gdprintk(XENLOG_WARNING, "SCMI: Unprivileged access attempt\n");
>> return false;
>> @@ -78,9 +84,18 @@ static bool scmi_handle_smc(struct cpu_user_regs *regs)
>> static int scmi_smc_domain_init(struct domain *d,
>> struct xen_domctl_createdomain *config)
>> {
>> - if ( !is_hardware_domain(d) )
>> + if ( !opt_dom0_scmi_smc_passthrough && !is_hardware_domain(d) )
>> + return 0;
>> +
>> + if ( opt_dom0_scmi_smc_passthrough &&
>> + (config->arch.arm_sci_type != XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC ||
>
> I am confused by the check "config->arch.arm_sci_type !=
> XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC". If the check was true, this
> function should not be called?
I'll tried to hide all SCI specifics inside SCI code, so outside SCi only API calls
are present (Patch 1).
>
>
>> + is_hardware_domain(d)) )
>> return 0;
>>
>> + if ( scmi_dom )
>> + return -EEXIST;
>> +
>> + scmi_dom = d;
>> d->arch.sci_enabled = true;
>> printk(XENLOG_DEBUG "SCMI: %pd init\n", d);
>> return 0;
>> @@ -88,12 +103,68 @@ static int scmi_smc_domain_init(struct domain *d,
>>
>> static void scmi_smc_domain_destroy(struct domain *d)
>> {
>> - if ( !is_hardware_domain(d) )
>> + if ( scmi_dom && scmi_dom != d )
>> return;
>>
>> + scmi_dom = NULL;
>> + d->arch.sci_enabled = false;
>> printk(XENLOG_DEBUG "SCMI: %pd destroy\n", d);
>> }
>>
>> +/*
>> + * Handle Dom0 SCMI SMC specific DT nodes
>> + *
>> + * Copy SCMI nodes into Dom0 device tree if dom0_scmi_smc_passthrough=false.
>
> I am confused by this: shouldn't scmi_smc_dt_handle_node be part of the
> previous patch? Otherwise, how can it work for dom0?
Previous patch doesn't change functionality introduced by original
commit 3e322bef8bc0 ("xen/arm: firmware: Add SCMI over SMC calls handling
layer"). Previous patch just makes it part of SCI subsystem without changing
major functionality.
The original functionality:
- if there is SCMI node in the Host DT then enable it for dom0/hwdom
- SCMI DT node will be copied to Dom0/hwdom in generic way as any other DT devices.
This patch enables coping of SCMI nodes in dom0/hwdom DT only if dom0_scmi_smc_passthrough=false,
otherwise scmi and scmi_shmem modes are removed.
>
>> + */
>> +static bool scmi_smc_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 ( dt_match_node(shmem_matches, node) && !sci_domain_is_enabled(d) )
>> + {
>> + dt_dprintk(" Skip scmi shmem node\n");
>> + return true;
>> + }
>> +
>> + if ( dt_match_node(scmi_matches, node) && !sci_domain_is_enabled(d) )
>
It is correct...
> This seems wrong: we are allowing access to the shmem region if
> !sci_domain_is_enabled(d). Shouldn't it be:
>
> if ( dt_match_node(scmi_matches, node) && sci_domain_is_enabled(d) )
>
>
>> + {
>> + struct dt_device_node *shmem_node;
>> + const __be32 *prop;
>> + u64 paddr, size;
>> + int ret;
>> +
>> + dt_dprintk(" Skip scmi node\n");
(this is confusing left over, sorry)
>> +
>> + prop = dt_get_property(node, "shmem", NULL);
>> + if ( !prop )
>> + return true;
>> +
>> + shmem_node = dt_find_node_by_phandle(be32_to_cpup(prop));
>> + if ( !shmem_node )
>> + return true;
>> +
>> + ret = dt_device_get_address(shmem_node, 0, &paddr, &size);
>> + if ( ret )
>> + return true;
>> +
>> + ret = iomem_permit_access(d, paddr_to_pfn(paddr),
>> + paddr_to_pfn(paddr + size - 1));
>> +
... even if SCMI node removed from dom0/hwdom DT - it's required to add
scmi_shmem IO range to dom0/hwdom to allow scmi_shmem configuration from
xl.cfg for other domain.
>> + return true;
... and here node removed
>> + }
>> +
>> + return false;
>> +}
>> +
>> static int __init scmi_check_smccc_ver(void)
>> {
>> if ( smccc_ver < ARM_SMCCC_VERSION_1_1 )
>> @@ -110,6 +181,7 @@ static const struct sci_mediator_ops scmi_smc_ops = {
>> .handle_call = scmi_handle_smc,
>> .domain_init = scmi_smc_domain_init,
>> .domain_destroy = scmi_smc_domain_destroy,
>> + .dom0_dt_handle_node = scmi_smc_dt_handle_node,
>> };
>>
>> /* Initialize the SCMI layer based on SMCs and Device-tree */
>> --
>> 2.34.1
>>
--
Best regards,
-grygorii
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFC PATCH v3 3/7] xen/arm: scmi-smc: passthrough SCMI SMC to guest domain
2025-03-11 11:16 ` [RFC PATCH v3 3/7] xen/arm: scmi-smc: passthrough SCMI SMC to guest domain Grygorii Strashko
2025-03-13 23:48 ` Stefano Stabellini
@ 2025-03-14 16:23 ` Anthony PERARD
2025-03-24 16:11 ` Grygorii Strashko
1 sibling, 1 reply; 30+ messages in thread
From: Anthony PERARD @ 2025-03-14 16:23 UTC (permalink / raw)
To: Grygorii Strashko
Cc: xen-devel, Stefano Stabellini, Julien Grall, Andrew Cooper,
Michal Orzel, Roger Pau Monne, Jan Beulich, Volodymyr Babchuk,
Oleksii Moisieiev, Bertrand Marquis, Juergen Gross,
Grygorii Strashko
On Tue, Mar 11, 2025 at 01:16:14PM +0200, Grygorii Strashko wrote:
> diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in
> index 8e1422104e50..7edf272386e3 100644
> --- a/docs/man/xl.cfg.5.pod.in
> +++ b/docs/man/xl.cfg.5.pod.in
> @@ -3094,6 +3094,42 @@ assigned to the domain.
>
> =back
>
> +=over 4
This =over 4 is unnecessary, but you need to move that existing =back to
the end of the Arm section, just before "=head3 x86" that we can see in
the context of this patch. (And doing that will fix a compiliation
failure ;-))
> +=item B<arm_sci="ARM_SCI_STRING">
> +
> +Set ARM_SCI specific options for the guest. ARM SCI is System
> +Control Protocol allows domain to manage various functions that are provided
> +by HW platform firmware.
> +
> +B<ARM_SCI_STRING> is a comma separated list of C<KEY=VALUE> settings,
> +from the following list:
> +
> +=over 4
> +
> +=item B<type=STRING>
> +
> +Specifies an ARM SCI type for the guest.
> +
> +=over 4
> +
> +=item B<none>
> +
> +Don't allow guest to use ARM SCI if present on the platform. This is the
> +default value.
> +
> +=item B<scmi_smc>
> +
> +Enables ARM SCMI SMC support for the guest by enabling SCMI over SMC calls
> +forwarding from domain to the EL3 firmware (like Trusted Firmware-A) with a
> +single SCMI OSPM agent support.
> +Should be used together with B<dom0_scmi_smc_passthrough> Xen command line
> +option.
> +
> +=back
> +
> +=back
> +
> =head3 x86
>
> =over 4
> diff --git a/tools/include/libxl.h b/tools/include/libxl.h
> index f8fe4afd7dca..5fa43637ab76 100644
> --- a/tools/include/libxl.h
> +++ b/tools/include/libxl.h
> @@ -313,6 +313,11 @@
> */
> #define LIBXL_HAVE_BUILDINFO_ARCH_NR_SPIS 1
>
> +/*
> + * libxl_domain_build_info has the arch_arm.sci* fields.
The new field seems to be called `arm_sci`. Did you intend to add `sci`
in `arch_arm` instead? Also, there's only `type` been added to
`arm_sci`, with the possibility to add more field in the future, so it
would be better to say that only "type" exist.
> + */
> +#define LIBXL_HAVE_BUILDINFO_ARCH_ARM_SCI 1
> +
> /*
> * LIBXL_HAVE_SOFT_RESET indicates that libxl supports performing
> * 'soft reset' for domains and there is 'soft_reset' shutdown reason
> diff --git a/tools/libs/light/libxl_types.idl b/tools/libs/light/libxl_types.idl
> index 33c9cfc1a267..ea0d30654cdd 100644
> --- a/tools/libs/light/libxl_types.idl
> +++ b/tools/libs/light/libxl_types.idl
> @@ -551,6 +551,15 @@ libxl_sve_type = Enumeration("sve_type", [
> (2048, "2048")
> ], init_val = "LIBXL_SVE_TYPE_DISABLED")
>
> +libxl_arm_sci_type = Enumeration("arm_sci_type", [
> + (0, "none"),
> + (1, "scmi_smc")
> + ], init_val = "LIBXL_ARM_SCI_TYPE_NONE")
> +
> +libxl_arm_sci = Struct("arm_sci", [
> + ("type", libxl_arm_sci_type),
> + ])
> +
> libxl_rdm_reserve = Struct("rdm_reserve", [
> ("strategy", libxl_rdm_reserve_strategy),
> ("policy", libxl_rdm_reserve_policy),
> @@ -639,6 +648,7 @@ libxl_domain_build_info = Struct("domain_build_info",[
> ("apic", libxl_defbool),
> ("dm_restrict", libxl_defbool),
> ("tee", libxl_tee_type),
> + ("arm_sci", libxl_arm_sci),
> ("u", KeyedUnion(None, libxl_domain_type, "type",
> [("hvm", Struct(None, [("firmware", string),
> ("bios", libxl_bios_type),
> diff --git a/tools/xl/xl_parse.c b/tools/xl/xl_parse.c
> index 9a3679c02325..ac9bf0b25c5a 100644
> --- a/tools/xl/xl_parse.c
> +++ b/tools/xl/xl_parse.c
> @@ -1284,6 +1284,63 @@ out:
> if (rc) exit(EXIT_FAILURE);
> }
>
> +static int parse_arm_sci_config(XLU_Config *cfg, libxl_arm_sci *arm_sci,
> + const char *str)
> +{
> + enum {
> + STATE_OPTION,
> + STATE_TYPE,
> + STATE_TERMINAL,
> + };
> + int ret, state = STATE_OPTION;
> + char *buf2, *tok, *ptr, *end;
> +
> + if (NULL == (buf2 = ptr = strdup(str)))
> + return ERROR_NOMEM;
> +
> + for (tok = ptr, end = ptr + strlen(ptr) + 1; ptr < end; ptr++) {
> + switch(state) {
> + case STATE_OPTION:
> + if (*ptr == '=') {
> + *ptr = '\0';
> + if (!strcmp(tok, "type")) {
> + state = STATE_TYPE;
> + } else {
> + fprintf(stderr, "Unknown ARM_SCI option: %s\n", tok);
> + goto parse_error;
> + }
> + tok = ptr + 1;
> + }
> + break;
> + case STATE_TYPE:
> + if (*ptr == '\0' || *ptr == ',') {
> + state = *ptr == ',' ? STATE_OPTION : STATE_TERMINAL;
> + *ptr = '\0';
> + ret = libxl_arm_sci_type_from_string(tok, &arm_sci->type);
> + if (ret) {
> + fprintf(stderr, "Unknown ARM_SCI type: %s\n", tok);
> + goto parse_error;
> + }
> + tok = ptr + 1;
> + }
> + break;
> + default:
> + break;
Instead of rolling your own parsing algo, could you do something similar
to the code that parse VIRTIO_DEVICE_STRING just above? It's basically a
loop with strtok() and a bunch of MATCH_OPTION() call (see
parse_virtio_config(), not the MATCH_OPTION for "type") which seems it
would be enough for parsing the SCI string. It would make
parse_arm_sci_config() much smaller and avoid a lot of repetition in the
code.
> + }
> + }
> +
> + if (tok != ptr || state != STATE_TERMINAL)
> + goto parse_error;
> +
> + free(buf2);
> +
> + return 0;
> +
> +parse_error:
> + free(buf2);
> + return ERROR_INVAL;
> +}
> +
> void parse_config_data(const char *config_source,
> const char *config_data,
> int config_len,
> @@ -2981,6 +3038,15 @@ skip_usbdev:
> if (!xlu_cfg_get_long (config, "nr_spis", &l, 0))
> b_info->arch_arm.nr_spis = l;
>
> + if (!xlu_cfg_get_string(config, "arm_sci", &buf, 1)) {
> + libxl_arm_sci arm_sci = { 0 };
Please use libxl_arm_sci_init() to initialise `arm_sci` instead. And add
a call to libxl_arm_sci_dispose() at the end of this context.
> + if (!parse_arm_sci_config(config, &arm_sci, buf)) {
> + b_info->arm_sci.type = arm_sci.type;
> + } else {
> + exit(EXIT_FAILURE);
> + }
> + }
> +
> parse_vkb_list(config, d_config);
>
> d_config->virtios = NULL;
Thanks,
--
Anthony Perard | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [RFC PATCH v3 3/7] xen/arm: scmi-smc: passthrough SCMI SMC to guest domain
2025-03-14 16:23 ` Anthony PERARD
@ 2025-03-24 16:11 ` Grygorii Strashko
0 siblings, 0 replies; 30+ messages in thread
From: Grygorii Strashko @ 2025-03-24 16:11 UTC (permalink / raw)
To: Anthony PERARD, Grygorii Strashko
Cc: xen-devel, Stefano Stabellini, Julien Grall, Andrew Cooper,
Michal Orzel, Roger Pau Monne, Jan Beulich, Volodymyr Babchuk,
Oleksii Moisieiev, Bertrand Marquis, Juergen Gross
Hi Anthony,
Thanks a lot for your comments.
On 14.03.25 18:23, Anthony PERARD wrote:
> On Tue, Mar 11, 2025 at 01:16:14PM +0200, Grygorii Strashko wrote:
>> diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in
>> index 8e1422104e50..7edf272386e3 100644
>> --- a/docs/man/xl.cfg.5.pod.in
>> +++ b/docs/man/xl.cfg.5.pod.in
>> @@ -3094,6 +3094,42 @@ assigned to the domain.
>>
>> =back
>>
>> +=over 4
>
> This =over 4 is unnecessary, but you need to move that existing =back to
> the end of the Arm section, just before "=head3 x86" that we can see in
> the context of this patch. (And doing that will fix a compiliation
> failure ;-))
>
>> +=item B<arm_sci="ARM_SCI_STRING">
>> +
>> +Set ARM_SCI specific options for the guest. ARM SCI is System
>> +Control Protocol allows domain to manage various functions that are provided
>> +by HW platform firmware.
>> +
>> +B<ARM_SCI_STRING> is a comma separated list of C<KEY=VALUE> settings,
>> +from the following list:
>> +
>> +=over 4
>> +
>> +=item B<type=STRING>
>> +
>> +Specifies an ARM SCI type for the guest.
>> +
>> +=over 4
>> +
>> +=item B<none>
>> +
>> +Don't allow guest to use ARM SCI if present on the platform. This is the
>> +default value.
>> +
>> +=item B<scmi_smc>
>> +
>> +Enables ARM SCMI SMC support for the guest by enabling SCMI over SMC calls
>> +forwarding from domain to the EL3 firmware (like Trusted Firmware-A) with a
>> +single SCMI OSPM agent support.
>> +Should be used together with B<dom0_scmi_smc_passthrough> Xen command line
>> +option.
>> +
>> +=back
>> +
>> +=back
>> +
>> =head3 x86
>>
>> =over 4
>> diff --git a/tools/include/libxl.h b/tools/include/libxl.h
>> index f8fe4afd7dca..5fa43637ab76 100644
>> --- a/tools/include/libxl.h
>> +++ b/tools/include/libxl.h
>> @@ -313,6 +313,11 @@
>> */
>> #define LIBXL_HAVE_BUILDINFO_ARCH_NR_SPIS 1
>>
>> +/*
>> + * libxl_domain_build_info has the arch_arm.sci* fields.
>
> The new field seems to be called `arm_sci`. Did you intend to add `sci`
> in `arch_arm` instead? Also, there's only `type` been added to
> `arm_sci`, with the possibility to add more field in the future, so it
> would be better to say that only "type" exist.
>
>> + */
>> +#define LIBXL_HAVE_BUILDINFO_ARCH_ARM_SCI 1
>> +
>> /*
>> * LIBXL_HAVE_SOFT_RESET indicates that libxl supports performing
>> * 'soft reset' for domains and there is 'soft_reset' shutdown reason
>> diff --git a/tools/libs/light/libxl_types.idl b/tools/libs/light/libxl_types.idl
>> index 33c9cfc1a267..ea0d30654cdd 100644
>> --- a/tools/libs/light/libxl_types.idl
>> +++ b/tools/libs/light/libxl_types.idl
>> @@ -551,6 +551,15 @@ libxl_sve_type = Enumeration("sve_type", [
>> (2048, "2048")
>> ], init_val = "LIBXL_SVE_TYPE_DISABLED")
>>
>> +libxl_arm_sci_type = Enumeration("arm_sci_type", [
>> + (0, "none"),
>> + (1, "scmi_smc")
>> + ], init_val = "LIBXL_ARM_SCI_TYPE_NONE")
>> +
>> +libxl_arm_sci = Struct("arm_sci", [
>> + ("type", libxl_arm_sci_type),
>> + ])
>> +
>> libxl_rdm_reserve = Struct("rdm_reserve", [
>> ("strategy", libxl_rdm_reserve_strategy),
>> ("policy", libxl_rdm_reserve_policy),
>> @@ -639,6 +648,7 @@ libxl_domain_build_info = Struct("domain_build_info",[
>> ("apic", libxl_defbool),
>> ("dm_restrict", libxl_defbool),
>> ("tee", libxl_tee_type),
>> + ("arm_sci", libxl_arm_sci),
>> ("u", KeyedUnion(None, libxl_domain_type, "type",
>> [("hvm", Struct(None, [("firmware", string),
>> ("bios", libxl_bios_type),
>> diff --git a/tools/xl/xl_parse.c b/tools/xl/xl_parse.c
>> index 9a3679c02325..ac9bf0b25c5a 100644
>> --- a/tools/xl/xl_parse.c
>> +++ b/tools/xl/xl_parse.c
>> @@ -1284,6 +1284,63 @@ out:
>> if (rc) exit(EXIT_FAILURE);
>> }
>>
>> +static int parse_arm_sci_config(XLU_Config *cfg, libxl_arm_sci *arm_sci,
>> + const char *str)
>> +{
>> + enum {
>> + STATE_OPTION,
>> + STATE_TYPE,
>> + STATE_TERMINAL,
>> + };
>> + int ret, state = STATE_OPTION;
>> + char *buf2, *tok, *ptr, *end;
>> +
>> + if (NULL == (buf2 = ptr = strdup(str)))
>> + return ERROR_NOMEM;
>> +
>> + for (tok = ptr, end = ptr + strlen(ptr) + 1; ptr < end; ptr++) {
>> + switch(state) {
>> + case STATE_OPTION:
>> + if (*ptr == '=') {
>> + *ptr = '\0';
>> + if (!strcmp(tok, "type")) {
>> + state = STATE_TYPE;
>> + } else {
>> + fprintf(stderr, "Unknown ARM_SCI option: %s\n", tok);
>> + goto parse_error;
>> + }
>> + tok = ptr + 1;
>> + }
>> + break;
>> + case STATE_TYPE:
>> + if (*ptr == '\0' || *ptr == ',') {
>> + state = *ptr == ',' ? STATE_OPTION : STATE_TERMINAL;
>> + *ptr = '\0';
>> + ret = libxl_arm_sci_type_from_string(tok, &arm_sci->type);
>> + if (ret) {
>> + fprintf(stderr, "Unknown ARM_SCI type: %s\n", tok);
>> + goto parse_error;
>> + }
>> + tok = ptr + 1;
>> + }
>> + break;
>> + default:
>> + break;
>
> Instead of rolling your own parsing algo, could you do something similar
> to the code that parse VIRTIO_DEVICE_STRING just above? It's basically a
> loop with strtok() and a bunch of MATCH_OPTION() call (see
> parse_virtio_config(), not the MATCH_OPTION for "type") which seems it
> would be enough for parsing the SCI string. It would make
> parse_arm_sci_config() much smaller and avoid a lot of repetition in the
> code.
>
>> + }
>> + }
>> +
>> + if (tok != ptr || state != STATE_TERMINAL)
>> + goto parse_error;
>> +
>> + free(buf2);
>> +
>> + return 0;
>> +
>> +parse_error:
>> + free(buf2);
>> + return ERROR_INVAL;
>> +}
>> +
>> void parse_config_data(const char *config_source,
>> const char *config_data,
>> int config_len,
>> @@ -2981,6 +3038,15 @@ skip_usbdev:
>> if (!xlu_cfg_get_long (config, "nr_spis", &l, 0))
>> b_info->arch_arm.nr_spis = l;
>>
>> + if (!xlu_cfg_get_string(config, "arm_sci", &buf, 1)) {
>> + libxl_arm_sci arm_sci = { 0 };
>
> Please use libxl_arm_sci_init() to initialise `arm_sci` instead. And add
> a call to libxl_arm_sci_dispose() at the end of this context.
>
>> + if (!parse_arm_sci_config(config, &arm_sci, buf)) {
>> + b_info->arm_sci.type = arm_sci.type;
>> + } else {
>> + exit(EXIT_FAILURE);
>> + }
>> + }
>> +
>> parse_vkb_list(config, d_config);
>>
>> d_config->virtios = NULL;
I think I understood you comments and will update accordingly.
--
Best regards,
-grygorii
^ permalink raw reply [flat|nested] 30+ messages in thread
* [RFC PATCH v3 4/7] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver
2025-03-11 11:16 [RFC PATCH v3 0/7] xen/arm: scmi: introduce SCI SCMI SMC multi-agent support Grygorii Strashko
` (2 preceding siblings ...)
2025-03-11 11:16 ` [RFC PATCH v3 3/7] xen/arm: scmi-smc: passthrough SCMI SMC to guest domain Grygorii Strashko
@ 2025-03-11 11:16 ` Grygorii Strashko
2025-03-13 23:48 ` Stefano Stabellini
2025-03-14 17:21 ` Anthony PERARD
2025-03-11 11:16 ` [RFC PATCH v3 5/7] libs: libxenhypfs - handle blob properties Grygorii Strashko
` (3 subsequent siblings)
7 siblings, 2 replies; 30+ messages in thread
From: Grygorii Strashko @ 2025-03-11 11:16 UTC (permalink / raw)
To: xen-devel
Cc: Stefano Stabellini, Julien Grall, Andrew Cooper, Michal Orzel,
Roger Pau Monne, Jan Beulich, Anthony PERARD, Volodymyr Babchuk,
Oleksii Moisieiev, Bertrand Marquis, Juergen Gross,
Grygorii Strashko
From: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
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
below.
+-----------------------------------------+
| |
| EL3 TF-A SCMI |
+-------+--+-------+--+-------+--+-------++
|shmem0 | |shmem1 | |shmem2 | |shmemX |
+-----+-+ +---+---+ +--+----+ +---+---+
smc-id0 | | | |
agent0 | | | |
+-----v--------+---------+-----------+----+
| | | | |
| | | | |
+--------------+---------+-----------+----+
smc-id1 | smc-id2| smc-idX|
agent1 | 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,
p2m_mmio_direct_nc.
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
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
- BASE_RESET_AGENT_CONFIGURATION (optional)
- BASE_SET_DEVICE_PERMISSIONS (optional)
The SCI SCMI SMC multi-agent driver implements following functionality:
- It's initialized based on the Host DT SCMI node (only one SCMI interface
is supported) which describes Xen management agent SCMI interface.
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>; // Xen manegement agent smc-id
\#address-cells = < 1>;
\#size-cells = < 0>;
\#access-controller - cells = < 1>;
shmem = <&scmi_shm_0>; // Xen manegement agent shmem
protocol@X{
};
};
};
- It obtains Xen specific SCMI Agent's configuration from the Host DT,
probes Agents and build SCMI Agents list. The Agents configuration
is taken from:
chosen {
xen,scmi-secondary-agents = <
1 0x82000003 &scmi_shm_1
2 0x82000004 &scmi_shm_2
3 0x82000005 &scmi_shm_3
4 0x82000006 &scmi_shm_4>;
}
/{
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>;
};
}
where first item is "agent_id", second - "arm,smc-id", and third - "arm,scmi-shmem" for
this agent_id.
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 option. 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 (TBD) 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:
dtdev = [
"/soc/i2c@e6508000",
]
xl.cfg:dtdev will contain all nodes which are under SCMI management (not only those which are behind IOMMU).
TODO:
- dom0less is not fully supported yet
- move memcpy_fro/tomio in separate patch
[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@epam.com>
Signed-off-by: Grygorii Strashko <grygorii_strashko@epam.com>
---
docs/man/xl.cfg.5.pod.in | 15 +
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 | 17 +
xen/arch/arm/domain_build.c | 3 +-
xen/arch/arm/firmware/Kconfig | 11 +
xen/arch/arm/firmware/Makefile | 1 +
xen/arch/arm/firmware/scmi-proto.h | 164 ++++
xen/arch/arm/firmware/scmi-shmem.c | 172 ++++
xen/arch/arm/firmware/scmi-shmem.h | 45 +
xen/arch/arm/firmware/scmi-smc-multiagent.c | 856 ++++++++++++++++++++
xen/include/public/arch-arm.h | 3 +
13 files changed, 1302 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/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in
index 7edf272386e3..fc6041724a13 100644
--- a/docs/man/xl.cfg.5.pod.in
+++ b/docs/man/xl.cfg.5.pod.in
@@ -3126,6 +3126,21 @@ single SCMI OSPM agent support.
Should be used together with B<dom0_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.
+
=back
=back
diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc
index 8e50f6b7c7ac..bc3c64d6ec90 100644
--- a/docs/misc/xen-command-line.pandoc
+++ b/docs/misc/xen-command-line.pandoc
@@ -1091,6 +1091,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.
+
### dtuart (ARM)
> `= path [:options]`
diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c
index d41adea1cefd..cdf5edb299af 100644
--- a/tools/libs/light/libxl_arm.c
+++ b/tools/libs/light/libxl_arm.c
@@ -229,6 +229,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.arm_sci.agent_id;
+ break;
default:
LOG(ERROR, "Unknown ARM_SCI type %d",
d_config->b_info.arm_sci.type);
diff --git a/tools/libs/light/libxl_types.idl b/tools/libs/light/libxl_types.idl
index ea0d30654cdd..e6707c7ca9e7 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 ac9bf0b25c5a..011222ec55b9 100644
--- a/tools/xl/xl_parse.c
+++ b/tools/xl/xl_parse.c
@@ -1290,6 +1290,7 @@ static int parse_arm_sci_config(XLU_Config *cfg, libxl_arm_sci *arm_sci,
enum {
STATE_OPTION,
STATE_TYPE,
+ STATE_AGENT_ID,
STATE_TERMINAL,
};
int ret, state = STATE_OPTION;
@@ -1305,6 +1306,8 @@ static int parse_arm_sci_config(XLU_Config *cfg, libxl_arm_sci *arm_sci,
*ptr = '\0';
if (!strcmp(tok, "type")) {
state = STATE_TYPE;
+ } else if (!strcmp(tok, "agent_id")) {
+ state = STATE_AGENT_ID;
} else {
fprintf(stderr, "Unknown ARM_SCI option: %s\n", tok);
goto parse_error;
@@ -1324,11 +1327,24 @@ static int parse_arm_sci_config(XLU_Config *cfg, libxl_arm_sci *arm_sci,
tok = ptr + 1;
}
break;
+ case STATE_AGENT_ID:
+ if (*ptr == ',' || *ptr == '\0') {
+ state = *ptr == ',' ? STATE_OPTION : STATE_TERMINAL;
+ *ptr = '\0';
+ arm_sci->agent_id = strtoul(tok, NULL, 0);
+ tok = ptr + 1;
+ }
default:
break;
}
}
+ if (arm_sci->type == LIBXL_ARM_SCI_TYPE_SCMI_SMC_MULTIAGENT &&
+ arm_sci->agent_id == 0) {
+ fprintf(stderr, "A non-zero ARM_SCI agent_id must be specified\n");
+ goto parse_error;
+ }
+
if (tok != ptr || state != STATE_TERMINAL)
goto parse_error;
@@ -3042,6 +3058,7 @@ skip_usbdev:
libxl_arm_sci arm_sci = { 0 };
if (!parse_arm_sci_config(config, &arm_sci, buf)) {
b_info->arm_sci.type = arm_sci.type;
+ b_info->arm_sci.agent_id = arm_sci.agent_id;
} else {
exit(EXIT_FAILURE);
}
diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
index 36d28b52a418..9238771a4aac 100644
--- a/xen/arch/arm/domain_build.c
+++ b/xen/arch/arm/domain_build.c
@@ -616,7 +616,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,scmi-secondary-agents"))
continue;
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 2686ba313898..63fc5eac8d52 100644
--- a/xen/arch/arm/firmware/Kconfig
+++ b/xen/arch/arm/firmware/Kconfig
@@ -29,6 +29,17 @@ 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"
+ 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 71bdefc24afb..37927e690e98 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 000000000000..3f4b9c5d6b7c
--- /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 XEN_ARCH_ARM_SCI_SCMI_PROTO_H_
+#define XEN_ARCH_ARM_SCI_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 /* XEN_ARCH_ARM_SCI_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 000000000000..3815c15bed80
--- /dev/null
+++ b/xen/arch/arm/firmware/scmi-shmem.c
@@ -0,0 +1,172 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * SCI SCMI multi-agent driver, using SMC/HVC shmem as transport.
+ *
+ * Oleksii Moisieiev <oleksii_moisieiev@epam.com>
+ * Copyright (c) 2025 EPAM Systems
+ */
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <asm/io.h>
+#include <xen/err.h>
+
+#include "scmi-proto.h"
+#include "scmi-shmem.h"
+
+/*
+ * Copy data from IO memory space to "real" memory space.
+ */
+static void __memcpy_fromio(void *to, const volatile void __iomem *from,
+ size_t count)
+{
+ while ( count && !IS_ALIGNED((unsigned long)from, 4) )
+ {
+ *(u8 *)to = readb_relaxed(from);
+ from++;
+ to++;
+ count--;
+ }
+
+ while ( count >= 4 )
+ {
+ *(u32 *)to = readl_relaxed(from);
+ from += 4;
+ to += 4;
+ count -= 4;
+ }
+
+ while ( count )
+ {
+ *(u8 *)to = readb_relaxed(from);
+ from++;
+ to++;
+ count--;
+ }
+}
+
+/*
+ * Copy data from "real" memory space to IO memory space.
+ */
+static void __memcpy_toio(volatile void __iomem *to, const void *from,
+ size_t count)
+{
+ while ( count && !IS_ALIGNED((unsigned long)to, 4) )
+ {
+ writeb_relaxed(*(u8 *)from, to);
+ from++;
+ to++;
+ count--;
+ }
+
+ while ( count >= 4 )
+ {
+ writel_relaxed(*(u32 *)from, to);
+ from += 4;
+ to += 4;
+ count -= 4;
+ }
+
+ while ( count )
+ {
+ writeb_relaxed(*(u8 *)from, to);
+ from++;
+ to++;
+ count--;
+ }
+}
+
+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 )
+ {
+ 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) )
+ {
+ 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 000000000000..f19e90786824
--- /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 XEN_ARCH_ARM_SCI_SCMI_SHMEM_H_
+#define XEN_ARCH_ARM_SCI_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 /* XEN_ARCH_ARM_SCI_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 000000000000..293fb30fa6c5
--- /dev/null
+++ b/xen/arch/arm/firmware/scmi-smc-multiagent.c
@@ -0,0 +1,856 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * SCI SCMI multi-agent driver, using SMC/HVC shmem as transport.
+ *
+ * Oleksii Moisieiev <oleksii_moisieiev@epam.com>
+ * Copyright (c) 2025 EPAM Systems
+ */
+
+#include <xen/acpi.h>
+
+#include <xen/device_tree.h>
+#include <xen/init.h>
+#include <xen/iocap.h>
+#include <xen/err.h>
+#include <xen/libfdt/libfdt.h>
+#include <xen/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 "xen,scmi-secondary-agents"
+
+#define HYP_CHANNEL 0x0
+
+
+struct scmi_channel {
+ uint32_t agent_id;
+ uint32_t func_id;
+ domid_t domain_id;
+ uint64_t paddr;
+ uint64_t len;
+ 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;
+ 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 )
+ 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 *aquire_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 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_cpup(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 skip_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;
+
+ /* always drop shmem */
+ if ( dt_match_node(skip_matches, node) )
+ {
+ dt_dprintk(" Skip scmi shmem\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;
+}
+
+/*
+ * Finalize Dom0 SCMI specific DT nodes
+ *
+ * if SCMI is enabled for Dom0:
+ * - generate shmem node
+ * - map SCMI shmem MMIO into Dom0
+ */
+static int scmi_dt_finalize(struct domain *d, void *fdt)
+{
+ __be32 reg[GUEST_ROOT_ADDRESS_CELLS + GUEST_ROOT_SIZE_CELLS];
+ struct scmi_channel *channel;
+ int nodeoffset;
+ __be32 *cells;
+ __be32 val;
+ char buf[64];
+ int res, rc;
+
+ if ( !sci_domain_is_enabled(d) )
+ return 0;
+
+ channel = d->arch.sci_data;
+
+ /*
+ * Replace "arm,smc-id" with proper value assigned for Dom0 SCMI channel
+ */
+ nodeoffset = fdt_node_offset_by_compatible(fdt, -1, "arm,scmi-smc");
+ if ( nodeoffset < 0 )
+ return -ENODEV;
+
+ cells = (__be32 *)&val;
+ dt_set_cell(&cells, 1, channel->func_id);
+ res = fdt_setprop_inplace(fdt, nodeoffset, "arm,smc-id", &val, sizeof(val));
+ if ( res )
+ return -EINVAL;
+
+ /*
+ * All SCMI shmem nodes should be removed from Dom0 DT at this point, so
+ * the shmem node for Dom0 need to be generated from SCMI channel assigned
+ * to Dom0.
+ * The original SCMI shmem node from platform DT is used by Xen SCMI driver
+ * itself as privileged channel (agent_id=0) to manage other SCMI
+ * agents (domains).
+ */
+ snprintf(buf, sizeof(buf), "scmi-shmem@%lx", channel->paddr);
+
+ res = fdt_begin_node(fdt, buf);
+ if ( res )
+ return res;
+
+ res = fdt_property_string(fdt, "compatible", "arm,scmi-shmem");
+ if ( res )
+ return res;
+
+ cells = ®[0];
+
+ dt_child_set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
+ channel->paddr, SCMI_SHMEM_MAPPED_SIZE);
+
+ res = fdt_property(fdt, "reg", reg, sizeof(reg));
+ if ( res )
+ return res;
+
+ res = fdt_property_cell(fdt, "phandle", scmi_data.shmem_phandle);
+ if ( res )
+ return res;
+
+ res = fdt_end_node(fdt);
+ if ( res )
+ return res;
+
+ /*
+ * Map SCMI shmem into Dom0 here as shmem nodes are excluded from
+ * generic Dom0 DT processing
+ */
+ res = iomem_permit_access(d, paddr_to_pfn(channel->paddr),
+ paddr_to_pfn(channel->paddr +
+ SCMI_SHMEM_MAPPED_SIZE - 1));
+ if ( res )
+ return res;
+
+ res = map_regions_p2mt(d, gaddr_to_gfn(channel->paddr),
+ PFN_UP(SCMI_SHMEM_MAPPED_SIZE),
+ maddr_to_mfn(channel->paddr), p2m_mmio_direct_nc);
+ if ( res )
+ {
+ rc = iomem_deny_access(d, paddr_to_pfn(channel->paddr),
+ paddr_to_pfn(channel->paddr +
+ SCMI_SHMEM_MAPPED_SIZE - 1));
+ if ( rc )
+ printk(XENLOG_ERR "scmi: Unable to deny iomem access , err = %d\n",
+ rc);
+ }
+
+ return res;
+}
+
+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;
+ int ret;
+
+ channel = get_channel_by_id(HYP_CHANNEL);
+ 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;
+
+ ret = do_smc_xfer(channel, &hdr, &tx, sizeof(tx), NULL, 0);
+ if ( ret == -EOPNOTSUPP )
+ return 0;
+
+ return ret;
+}
+
+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 __init int collect_agents(struct dt_device_node *scmi_node)
+{
+ const struct dt_device_node *chosen_node;
+ const __be32 *prop;
+ u32 len, i;
+
+ chosen_node = dt_find_node_by_path("/chosen");
+ if ( !chosen_node )
+ {
+ printk(XENLOG_ERR "scmi: chosen node not found\n");
+ return -ENOENT;
+ }
+
+ prop = dt_get_property(chosen_node, SCMI_SECONDARY_AGENTS, &len);
+ if ( !prop )
+ {
+ printk(XENLOG_WARNING "scmi: No %s property found\n",
+ SCMI_SECONDARY_AGENTS);
+ return -ENODEV;
+ }
+
+ if ( len % (3 * sizeof(u32)) )
+ {
+ printk(XENLOG_ERR "scmi: Invalid length of %s property: %d\n",
+ SCMI_SECONDARY_AGENTS, len);
+ return -EINVAL;
+ }
+
+ for ( i = 0 ; i < len / (3 * sizeof(u32)) ; i++ )
+ {
+ u32 agent_id = be32_to_cpu(*prop++);
+ u32 smc_id = be32_to_cpu(*prop++);
+ u32 shmem_phandle = be32_to_cpu(*prop++);
+ struct dt_device_node *node = dt_find_node_by_phandle(shmem_phandle);
+ u64 addr, size;
+ int ret;
+
+ if ( !node )
+ {
+ printk(XENLOG_ERR"scmi: Could not find shmem node for agent %d\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 %d: %d",
+ agent_id, ret);
+ return ret;
+ }
+
+ if ( !IS_ALIGNED(size, SCMI_SHMEM_MAPPED_SIZE) )
+ {
+ printk(XENLOG_ERR "scmi: shmem memory is not aligned\n");
+ return -EINVAL;
+ }
+
+ ret = PTR_RET(smc_create_channel(agent_id, smc_id, addr));
+ if ( ret )
+ {
+ printk(XENLOG_ERR "scmi: Could not create channel for agent %d: %d",
+ agent_id, ret);
+ return ret;
+ }
+
+ printk(XENLOG_DEBUG "scmi: Agent %d SMC %X addr %lx\n", agent_id,
+ smc_id, 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 = aquire_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(HYP_CHANNEL);
+ 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;
+
+ 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,
+ .dom0_dt_finalize = scmi_dt_finalize,
+ .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 __init int scmi_probe(struct dt_device_node *scmi_node, const void *data)
+{
+ u64 addr, size;
+ int ret, i;
+ struct scmi_channel *channel, *agent_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;
+
+ 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;
+ }
+
+ /* save shmem phandle and re-use it fro Dom0 DT shmem node */
+ if ( !dt_property_read_u32(scmi_node, "shmem", &scmi_data.shmem_phandle) )
+ {
+ printk(XENLOG_ERR "scmi: unable to read shmem phandle 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;
+ }
+
+ scmi_data.dt_dev = scmi_node;
+
+ channel = smc_create_channel(HYP_CHANNEL, scmi_data.func_id, addr);
+ if ( IS_ERR(channel) )
+ goto out;
+
+ ret = map_channel_memory(channel);
+ if ( ret )
+ goto out;
+
+ 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;
+
+ i = 1;
+
+ list_for_each_entry(agent_channel, &scmi_data.channel_list, list)
+ {
+ 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 )
+ goto error;
+
+ 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 )
+ goto error;
+
+ printk(XENLOG_DEBUG "id=0x%x name=%s\n",
+ da_rx.agent_id, da_rx.name);
+
+ agent_channel->agent_id = da_rx.agent_id;
+
+ if ( i > n_agents )
+ break;
+
+ i++;
+ }
+
+ 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_COMPATIBLE("arm,scmi-smc"),
+ { /* sentinel */ },
+};
+
+DT_DEVICE_START(gicv3, "SCMI SMC MEDIATOR", DEVICE_ARM_SCI)
+ .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 095b1a23e30c..30e46de6d7a0 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
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [RFC PATCH v3 4/7] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver
2025-03-11 11:16 ` [RFC PATCH v3 4/7] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver Grygorii Strashko
@ 2025-03-13 23:48 ` Stefano Stabellini
2025-03-14 17:21 ` Anthony PERARD
1 sibling, 0 replies; 30+ messages in thread
From: Stefano Stabellini @ 2025-03-13 23:48 UTC (permalink / raw)
To: Grygorii Strashko
Cc: xen-devel, Stefano Stabellini, Julien Grall, Andrew Cooper,
Michal Orzel, Roger Pau Monne, Jan Beulich, Anthony PERARD,
Volodymyr Babchuk, Oleksii Moisieiev, Bertrand Marquis,
Juergen Gross, Grygorii Strashko
On Tue, 11 Mar 2025, Grygorii Strashko wrote:
> From: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
>
> 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
> below.
>
> +-----------------------------------------+
> | |
> | EL3 TF-A SCMI |
> +-------+--+-------+--+-------+--+-------++
> |shmem0 | |shmem1 | |shmem2 | |shmemX |
> +-----+-+ +---+---+ +--+----+ +---+---+
> smc-id0 | | | |
> agent0 | | | |
> +-----v--------+---------+-----------+----+
> | | | | |
> | | | | |
> +--------------+---------+-----------+----+
> smc-id1 | smc-id2| smc-idX|
> agent1 | 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,
> p2m_mmio_direct_nc.
>
> 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
> 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
> - BASE_RESET_AGENT_CONFIGURATION (optional)
> - BASE_SET_DEVICE_PERMISSIONS (optional)
>
> The SCI SCMI SMC multi-agent driver implements following functionality:
> - It's initialized based on the Host DT SCMI node (only one SCMI interface
> is supported) which describes Xen management agent SCMI interface.
>
> 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>; // Xen manegement agent smc-id
> \#address-cells = < 1>;
> \#size-cells = < 0>;
> \#access-controller - cells = < 1>;
> shmem = <&scmi_shm_0>; // Xen manegement agent shmem
>
> protocol@X{
> };
> };
> };
>
> - It obtains Xen specific SCMI Agent's configuration from the Host DT,
> probes Agents and build SCMI Agents list. The Agents configuration
> is taken from:
>
> chosen {
> xen,scmi-secondary-agents = <
> 1 0x82000003 &scmi_shm_1
> 2 0x82000004 &scmi_shm_2
> 3 0x82000005 &scmi_shm_3
> 4 0x82000006 &scmi_shm_4>;
> }
>
> /{
> 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>;
All the shared memory regions need two addresses: the host address and
the guest address. They might be different to avoid address collisions.
The guest address could be optional, and if absent we could assume 1:1.
Also, these nodes under chosen should be documented in
docs/misc/arm/device-tree/booting.txt
> };
> }
> where first item is "agent_id", second - "arm,smc-id", and third - "arm,scmi-shmem" for
> this agent_id.
>
> 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 option. 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.
I know dom0less is not enabled at this point, but if we had dom0less
support, we would need a way in device tree to specify the agend_id for
every dom0less domain, and ideally we would use the same device tree
binding for dom0 too (or something very similar).
> 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 (TBD) 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:
>
> dtdev = [
> "/soc/i2c@e6508000",
> ]
>
> xl.cfg:dtdev will contain all nodes which are under SCMI management (not only those which are behind IOMMU).
This commit message is great documentation. Please add it as a text
document under docs/misc/arm
>
> TODO:
> - dom0less is not fully supported yet
> - move memcpy_fro/tomio in separate patch
>
> [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@epam.com>
> Signed-off-by: Grygorii Strashko <grygorii_strashko@epam.com>
> ---
> docs/man/xl.cfg.5.pod.in | 15 +
> 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 | 17 +
> xen/arch/arm/domain_build.c | 3 +-
> xen/arch/arm/firmware/Kconfig | 11 +
> xen/arch/arm/firmware/Makefile | 1 +
> xen/arch/arm/firmware/scmi-proto.h | 164 ++++
> xen/arch/arm/firmware/scmi-shmem.c | 172 ++++
> xen/arch/arm/firmware/scmi-shmem.h | 45 +
> xen/arch/arm/firmware/scmi-smc-multiagent.c | 856 ++++++++++++++++++++
> xen/include/public/arch-arm.h | 3 +
> 13 files changed, 1302 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/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in
> index 7edf272386e3..fc6041724a13 100644
> --- a/docs/man/xl.cfg.5.pod.in
> +++ b/docs/man/xl.cfg.5.pod.in
> @@ -3126,6 +3126,21 @@ single SCMI OSPM agent support.
> Should be used together with B<dom0_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.
> +
> =back
>
> =back
> diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc
> index 8e50f6b7c7ac..bc3c64d6ec90 100644
> --- a/docs/misc/xen-command-line.pandoc
> +++ b/docs/misc/xen-command-line.pandoc
> @@ -1091,6 +1091,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.
> +
> ### dtuart (ARM)
> > `= path [:options]`
>
> diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c
> index d41adea1cefd..cdf5edb299af 100644
> --- a/tools/libs/light/libxl_arm.c
> +++ b/tools/libs/light/libxl_arm.c
> @@ -229,6 +229,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.arm_sci.agent_id;
> + break;
> default:
> LOG(ERROR, "Unknown ARM_SCI type %d",
> d_config->b_info.arm_sci.type);
> diff --git a/tools/libs/light/libxl_types.idl b/tools/libs/light/libxl_types.idl
> index ea0d30654cdd..e6707c7ca9e7 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 ac9bf0b25c5a..011222ec55b9 100644
> --- a/tools/xl/xl_parse.c
> +++ b/tools/xl/xl_parse.c
> @@ -1290,6 +1290,7 @@ static int parse_arm_sci_config(XLU_Config *cfg, libxl_arm_sci *arm_sci,
> enum {
> STATE_OPTION,
> STATE_TYPE,
> + STATE_AGENT_ID,
> STATE_TERMINAL,
> };
> int ret, state = STATE_OPTION;
> @@ -1305,6 +1306,8 @@ static int parse_arm_sci_config(XLU_Config *cfg, libxl_arm_sci *arm_sci,
> *ptr = '\0';
> if (!strcmp(tok, "type")) {
> state = STATE_TYPE;
> + } else if (!strcmp(tok, "agent_id")) {
> + state = STATE_AGENT_ID;
> } else {
> fprintf(stderr, "Unknown ARM_SCI option: %s\n", tok);
> goto parse_error;
> @@ -1324,11 +1327,24 @@ static int parse_arm_sci_config(XLU_Config *cfg, libxl_arm_sci *arm_sci,
> tok = ptr + 1;
> }
> break;
> + case STATE_AGENT_ID:
> + if (*ptr == ',' || *ptr == '\0') {
> + state = *ptr == ',' ? STATE_OPTION : STATE_TERMINAL;
> + *ptr = '\0';
> + arm_sci->agent_id = strtoul(tok, NULL, 0);
> + tok = ptr + 1;
> + }
> default:
> break;
> }
> }
>
> + if (arm_sci->type == LIBXL_ARM_SCI_TYPE_SCMI_SMC_MULTIAGENT &&
> + arm_sci->agent_id == 0) {
> + fprintf(stderr, "A non-zero ARM_SCI agent_id must be specified\n");
> + goto parse_error;
> + }
> +
> if (tok != ptr || state != STATE_TERMINAL)
> goto parse_error;
>
> @@ -3042,6 +3058,7 @@ skip_usbdev:
> libxl_arm_sci arm_sci = { 0 };
> if (!parse_arm_sci_config(config, &arm_sci, buf)) {
> b_info->arm_sci.type = arm_sci.type;
> + b_info->arm_sci.agent_id = arm_sci.agent_id;
> } else {
> exit(EXIT_FAILURE);
> }
> diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
> index 36d28b52a418..9238771a4aac 100644
> --- a/xen/arch/arm/domain_build.c
> +++ b/xen/arch/arm/domain_build.c
> @@ -616,7 +616,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,scmi-secondary-agents"))
> continue;
>
> 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 2686ba313898..63fc5eac8d52 100644
> --- a/xen/arch/arm/firmware/Kconfig
> +++ b/xen/arch/arm/firmware/Kconfig
> @@ -29,6 +29,17 @@ 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"
> + 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 71bdefc24afb..37927e690e98 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 000000000000..3f4b9c5d6b7c
> --- /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 XEN_ARCH_ARM_SCI_SCMI_PROTO_H_
> +#define XEN_ARCH_ARM_SCI_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 /* XEN_ARCH_ARM_SCI_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 000000000000..3815c15bed80
> --- /dev/null
> +++ b/xen/arch/arm/firmware/scmi-shmem.c
> @@ -0,0 +1,172 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * SCI SCMI multi-agent driver, using SMC/HVC shmem as transport.
> + *
> + * Oleksii Moisieiev <oleksii_moisieiev@epam.com>
> + * Copyright (c) 2025 EPAM Systems
> + */
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +
> +#include <asm/io.h>
> +#include <xen/err.h>
> +
> +#include "scmi-proto.h"
> +#include "scmi-shmem.h"
> +
> +/*
> + * Copy data from IO memory space to "real" memory space.
> + */
> +static void __memcpy_fromio(void *to, const volatile void __iomem *from,
> + size_t count)
> +{
> + while ( count && !IS_ALIGNED((unsigned long)from, 4) )
> + {
> + *(u8 *)to = readb_relaxed(from);
> + from++;
> + to++;
> + count--;
> + }
> +
> + while ( count >= 4 )
> + {
> + *(u32 *)to = readl_relaxed(from);
> + from += 4;
> + to += 4;
> + count -= 4;
> + }
> +
> + while ( count )
> + {
> + *(u8 *)to = readb_relaxed(from);
> + from++;
> + to++;
> + count--;
> + }
> +}
> +
> +/*
> + * Copy data from "real" memory space to IO memory space.
> + */
> +static void __memcpy_toio(volatile void __iomem *to, const void *from,
> + size_t count)
> +{
> + while ( count && !IS_ALIGNED((unsigned long)to, 4) )
> + {
> + writeb_relaxed(*(u8 *)from, to);
> + from++;
> + to++;
> + count--;
> + }
> +
> + while ( count >= 4 )
> + {
> + writel_relaxed(*(u32 *)from, to);
> + from += 4;
> + to += 4;
> + count -= 4;
> + }
> +
> + while ( count )
> + {
> + writeb_relaxed(*(u8 *)from, to);
> + from++;
> + to++;
> + count--;
> + }
> +}
> +
> +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 )
> + {
> + 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) )
> + {
> + 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 000000000000..f19e90786824
> --- /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 XEN_ARCH_ARM_SCI_SCMI_SHMEM_H_
> +#define XEN_ARCH_ARM_SCI_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 /* XEN_ARCH_ARM_SCI_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 000000000000..293fb30fa6c5
> --- /dev/null
> +++ b/xen/arch/arm/firmware/scmi-smc-multiagent.c
> @@ -0,0 +1,856 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * SCI SCMI multi-agent driver, using SMC/HVC shmem as transport.
> + *
> + * Oleksii Moisieiev <oleksii_moisieiev@epam.com>
> + * Copyright (c) 2025 EPAM Systems
> + */
> +
> +#include <xen/acpi.h>
> +
> +#include <xen/device_tree.h>
> +#include <xen/init.h>
> +#include <xen/iocap.h>
> +#include <xen/err.h>
> +#include <xen/libfdt/libfdt.h>
> +#include <xen/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 "xen,scmi-secondary-agents"
> +
> +#define HYP_CHANNEL 0x0
> +
> +
> +struct scmi_channel {
> + uint32_t agent_id;
> + uint32_t func_id;
> + domid_t domain_id;
> + uint64_t paddr;
> + uint64_t len;
> + 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;
> + 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 )
> + 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);
This lock is not per-CPU and it is held across a potentially
long-running SMC call. Could that be an issue? Do we want to move the
SMC call out of the lock?
> +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 *aquire_scmi_channel(struct domain *d,
> + uint32_t agent_id)
The spelling is acquire
> +{
> + 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)
Could be __init
> +{
> + 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 scmi_dt_read_hyp_channel_addr(struct dt_device_node *scmi_node,
> + u64 *addr, u64 *size)
Could be __init
> +{
> + 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_cpup(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 skip_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;
> +
> + /* always drop shmem */
> + if ( dt_match_node(skip_matches, node) )
> + {
> + dt_dprintk(" Skip scmi shmem\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;
> +}
> +
> +/*
> + * Finalize Dom0 SCMI specific DT nodes
> + *
> + * if SCMI is enabled for Dom0:
> + * - generate shmem node
> + * - map SCMI shmem MMIO into Dom0
> + */
> +static int scmi_dt_finalize(struct domain *d, void *fdt)
> +{
> + __be32 reg[GUEST_ROOT_ADDRESS_CELLS + GUEST_ROOT_SIZE_CELLS];
> + struct scmi_channel *channel;
> + int nodeoffset;
> + __be32 *cells;
> + __be32 val;
> + char buf[64];
> + int res, rc;
> +
> + if ( !sci_domain_is_enabled(d) )
> + return 0;
> +
> + channel = d->arch.sci_data;
> +
> + /*
> + * Replace "arm,smc-id" with proper value assigned for Dom0 SCMI channel
> + */
> + nodeoffset = fdt_node_offset_by_compatible(fdt, -1, "arm,scmi-smc");
> + if ( nodeoffset < 0 )
> + return -ENODEV;
> +
> + cells = (__be32 *)&val;
> + dt_set_cell(&cells, 1, channel->func_id);
> + res = fdt_setprop_inplace(fdt, nodeoffset, "arm,smc-id", &val, sizeof(val));
> + if ( res )
> + return -EINVAL;
> +
> + /*
> + * All SCMI shmem nodes should be removed from Dom0 DT at this point, so
> + * the shmem node for Dom0 need to be generated from SCMI channel assigned
> + * to Dom0.
> + * The original SCMI shmem node from platform DT is used by Xen SCMI driver
> + * itself as privileged channel (agent_id=0) to manage other SCMI
> + * agents (domains).
Wouldn't it be better not to add those nodes to dom0 device tree at all
in the first place? Why adding them, then removing them?
I think it is best to skip them in handle_node and only add the ones
that are correct for dom0 here. Similarly, I don't think we should
replace the value of arm,smc-id in place: we should skip it the first
time around and generate one now.
> + */
> + snprintf(buf, sizeof(buf), "scmi-shmem@%lx", channel->paddr);
> +
> + res = fdt_begin_node(fdt, buf);
> + if ( res )
> + return res;
> +
> + res = fdt_property_string(fdt, "compatible", "arm,scmi-shmem");
> + if ( res )
> + return res;
> +
> + cells = ®[0];
> +
> + dt_child_set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
> + channel->paddr, SCMI_SHMEM_MAPPED_SIZE);
> +
> + res = fdt_property(fdt, "reg", reg, sizeof(reg));
> + if ( res )
> + return res;
> +
> + res = fdt_property_cell(fdt, "phandle", scmi_data.shmem_phandle);
> + if ( res )
> + return res;
> +
> + res = fdt_end_node(fdt);
> + if ( res )
> + return res;
> +
> + /*
> + * Map SCMI shmem into Dom0 here as shmem nodes are excluded from
> + * generic Dom0 DT processing
> + */
> + res = iomem_permit_access(d, paddr_to_pfn(channel->paddr),
> + paddr_to_pfn(channel->paddr +
> + SCMI_SHMEM_MAPPED_SIZE - 1));
> + if ( res )
> + return res;
> +
> + res = map_regions_p2mt(d, gaddr_to_gfn(channel->paddr),
> + PFN_UP(SCMI_SHMEM_MAPPED_SIZE),
> + maddr_to_mfn(channel->paddr), p2m_mmio_direct_nc);
> + if ( res )
> + {
> + rc = iomem_deny_access(d, paddr_to_pfn(channel->paddr),
> + paddr_to_pfn(channel->paddr +
> + SCMI_SHMEM_MAPPED_SIZE - 1));
> + if ( rc )
> + printk(XENLOG_ERR "scmi: Unable to deny iomem access , err = %d\n",
> + rc);
> + }
> +
> + return res;
> +}
> +
> +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;
> + int ret;
> +
> + channel = get_channel_by_id(HYP_CHANNEL);
> + 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;
> +
> + ret = do_smc_xfer(channel, &hdr, &tx, sizeof(tx), NULL, 0);
> + if ( ret == -EOPNOTSUPP )
> + return 0;
> +
> + return ret;
> +}
> +
> +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 __init int collect_agents(struct dt_device_node *scmi_node)
> +{
> + const struct dt_device_node *chosen_node;
> + const __be32 *prop;
> + u32 len, i;
We don't use u32 in Xen anymore. Please use uint32_t instead. However,
in this case, I think i should be unsigned int by our coding style.
> + chosen_node = dt_find_node_by_path("/chosen");
> + if ( !chosen_node )
> + {
> + printk(XENLOG_ERR "scmi: chosen node not found\n");
> + return -ENOENT;
> + }
> +
> + prop = dt_get_property(chosen_node, SCMI_SECONDARY_AGENTS, &len);
> + if ( !prop )
> + {
> + printk(XENLOG_WARNING "scmi: No %s property found\n",
> + SCMI_SECONDARY_AGENTS);
> + return -ENODEV;
> + }
> +
> + if ( len % (3 * sizeof(u32)) )
> + {
> + printk(XENLOG_ERR "scmi: Invalid length of %s property: %d\n",
> + SCMI_SECONDARY_AGENTS, len);
> + return -EINVAL;
> + }
> +
> + for ( i = 0 ; i < len / (3 * sizeof(u32)) ; i++ )
> + {
> + u32 agent_id = be32_to_cpu(*prop++);
> + u32 smc_id = be32_to_cpu(*prop++);
> + u32 shmem_phandle = be32_to_cpu(*prop++);
> + struct dt_device_node *node = dt_find_node_by_phandle(shmem_phandle);
> + u64 addr, size;
> + int ret;
> +
> + if ( !node )
> + {
> + printk(XENLOG_ERR"scmi: Could not find shmem node for agent %d\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 %d: %d",
> + agent_id, ret);
> + return ret;
> + }
> +
> + if ( !IS_ALIGNED(size, SCMI_SHMEM_MAPPED_SIZE) )
> + {
> + printk(XENLOG_ERR "scmi: shmem memory is not aligned\n");
> + return -EINVAL;
> + }
> +
> + ret = PTR_RET(smc_create_channel(agent_id, smc_id, addr));
> + if ( ret )
> + {
> + printk(XENLOG_ERR "scmi: Could not create channel for agent %d: %d",
> + agent_id, ret);
> + return ret;
> + }
> +
> + printk(XENLOG_DEBUG "scmi: Agent %d SMC %X addr %lx\n", agent_id,
> + smc_id, 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) ) {
coding style
> + 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 = aquire_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.
Yeah... that check needs to change
> + */
> + 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(HYP_CHANNEL);
> + 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;
> +
> + 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,
> + .dom0_dt_finalize = scmi_dt_finalize,
> + .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 __init int scmi_probe(struct dt_device_node *scmi_node, const void *data)
> +{
> + u64 addr, size;
> + int ret, i;
> + struct scmi_channel *channel, *agent_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;
> +
> + 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;
> + }
> +
> + /* save shmem phandle and re-use it fro Dom0 DT shmem node */
> + if ( !dt_property_read_u32(scmi_node, "shmem", &scmi_data.shmem_phandle) )
> + {
> + printk(XENLOG_ERR "scmi: unable to read shmem phandle 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;
> + }
> +
> + scmi_data.dt_dev = scmi_node;
> +
> + channel = smc_create_channel(HYP_CHANNEL, scmi_data.func_id, addr);
> + if ( IS_ERR(channel) )
> + goto out;
> +
> + ret = map_channel_memory(channel);
> + if ( ret )
> + goto out;
> +
> + 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;
> +
> + i = 1;
> +
> + list_for_each_entry(agent_channel, &scmi_data.channel_list, list)
> + {
> + 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 )
> + goto error;
> +
> + 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 )
> + goto error;
> +
> + printk(XENLOG_DEBUG "id=0x%x name=%s\n",
> + da_rx.agent_id, da_rx.name);
> +
> + agent_channel->agent_id = da_rx.agent_id;
> +
> + if ( i > n_agents )
> + break;
> +
> + i++;
> + }
> +
> + 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_COMPATIBLE("arm,scmi-smc"),
> + { /* sentinel */ },
> +};
> +
> +DT_DEVICE_START(gicv3, "SCMI SMC MEDIATOR", DEVICE_ARM_SCI)
scmi_smc_ma instead of gicv3?
> + .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 095b1a23e30c..30e46de6d7a0 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
>
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [RFC PATCH v3 4/7] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver
2025-03-11 11:16 ` [RFC PATCH v3 4/7] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver Grygorii Strashko
2025-03-13 23:48 ` Stefano Stabellini
@ 2025-03-14 17:21 ` Anthony PERARD
1 sibling, 0 replies; 30+ messages in thread
From: Anthony PERARD @ 2025-03-14 17:21 UTC (permalink / raw)
To: Grygorii Strashko
Cc: xen-devel, Stefano Stabellini, Julien Grall, Andrew Cooper,
Michal Orzel, Roger Pau Monne, Jan Beulich, Volodymyr Babchuk,
Oleksii Moisieiev, Bertrand Marquis, Juergen Gross,
Grygorii Strashko
On Tue, Mar 11, 2025 at 01:16:15PM +0200, Grygorii Strashko wrote:
> diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in
> index 7edf272386e3..fc6041724a13 100644
> --- a/docs/man/xl.cfg.5.pod.in
> +++ b/docs/man/xl.cfg.5.pod.in
> @@ -3126,6 +3126,21 @@ single SCMI OSPM agent support.
> Should be used together with B<dom0_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
This new =back shouldn't exist in this patch, because there's no =over
been added. This just fix a bug present in the previous patch.
> +
> +=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.
Are they other restriction on what agent_id value can be? I mean from
the description -4242 is a valid value.
> =back
>
> =back
> diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc
> index 8e50f6b7c7ac..bc3c64d6ec90 100644
> --- a/docs/misc/xen-command-line.pandoc
> +++ b/docs/misc/xen-command-line.pandoc
> @@ -1091,6 +1091,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.
> +
> ### dtuart (ARM)
> > `= path [:options]`
>
> diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c
> index d41adea1cefd..cdf5edb299af 100644
> --- a/tools/libs/light/libxl_arm.c
> +++ b/tools/libs/light/libxl_arm.c
> @@ -229,6 +229,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.arm_sci.agent_id;
> + break;
> default:
> LOG(ERROR, "Unknown ARM_SCI type %d",
> d_config->b_info.arm_sci.type);
> diff --git a/tools/libs/light/libxl_types.idl b/tools/libs/light/libxl_types.idl
> index ea0d30654cdd..e6707c7ca9e7 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)
Is it necessary to limit this value to a 8-bit value here? Skimming
through the Xen code, it seems that that value can be 32-bits at times,
but just restricted to 8-bits in the hypercall.
> ])
>
> libxl_rdm_reserve = Struct("rdm_reserve", [
> diff --git a/tools/xl/xl_parse.c b/tools/xl/xl_parse.c
> index ac9bf0b25c5a..011222ec55b9 100644
> --- a/tools/xl/xl_parse.c
> +++ b/tools/xl/xl_parse.c
> @@ -1324,11 +1327,24 @@ static int parse_arm_sci_config(XLU_Config *cfg, libxl_arm_sci *arm_sci,
> tok = ptr + 1;
> }
> break;
> + case STATE_AGENT_ID:
> + if (*ptr == ',' || *ptr == '\0') {
> + state = *ptr == ',' ? STATE_OPTION : STATE_TERMINAL;
> + *ptr = '\0';
> + arm_sci->agent_id = strtoul(tok, NULL, 0);
You should check that the value returned by strtoul() is actually valid
and does fit in `agent_id`.
> + tok = ptr + 1;
> + }
> default:
> break;
> }
> }
>
> + if (arm_sci->type == LIBXL_ARM_SCI_TYPE_SCMI_SMC_MULTIAGENT &&
> + arm_sci->agent_id == 0) {
> + fprintf(stderr, "A non-zero ARM_SCI agent_id must be specified\n");
> + goto parse_error;
> + }
> +
> if (tok != ptr || state != STATE_TERMINAL)
> goto parse_error;
>
> @@ -3042,6 +3058,7 @@ skip_usbdev:
> libxl_arm_sci arm_sci = { 0 };
> if (!parse_arm_sci_config(config, &arm_sci, buf)) {
> b_info->arm_sci.type = arm_sci.type;
> + b_info->arm_sci.agent_id = arm_sci.agent_id;
I just realise that it's probably enough to call
parse_arm_sci_config(.., &b_info->arm_sci) instead of declaring
another local `arm_sci` variable. Or is it necessary to have a different
variable somehow?
> } else {
> exit(EXIT_FAILURE);
> }
Thanks,
--
Anthony Perard | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
^ permalink raw reply [flat|nested] 30+ messages in thread
* [RFC PATCH v3 5/7] libs: libxenhypfs - handle blob properties
2025-03-11 11:16 [RFC PATCH v3 0/7] xen/arm: scmi: introduce SCI SCMI SMC multi-agent support Grygorii Strashko
` (3 preceding siblings ...)
2025-03-11 11:16 ` [RFC PATCH v3 4/7] xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver Grygorii Strashko
@ 2025-03-11 11:16 ` Grygorii Strashko
2025-03-11 11:26 ` Juergen Gross
2025-03-11 11:16 ` [RFC PATCH v3 6/7] xen/arm: Export host device-tree to hypfs Grygorii Strashko
` (2 subsequent siblings)
7 siblings, 1 reply; 30+ messages in thread
From: Grygorii Strashko @ 2025-03-11 11:16 UTC (permalink / raw)
To: xen-devel
Cc: Stefano Stabellini, Julien Grall, Andrew Cooper, Michal Orzel,
Roger Pau Monne, Jan Beulich, Anthony PERARD, Volodymyr Babchuk,
Oleksii Moisieiev, Bertrand Marquis, Juergen Gross,
Volodymyr Babchuk
From: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
libxenhypfs will return blob properties as is. This output can be used
to retrieve information from the hypfs. Caller is responsible for
parsing property value.
Signed-off-by: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
Reviewed-by: Volodymyr Babchuk <volodymyr_babchuk@epam.com>
---
tools/libs/hypfs/core.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/tools/libs/hypfs/core.c b/tools/libs/hypfs/core.c
index 52b30db8d777..d09bba7d8c86 100644
--- a/tools/libs/hypfs/core.c
+++ b/tools/libs/hypfs/core.c
@@ -307,8 +307,6 @@ char *xenhypfs_read(xenhypfs_handle *fshdl, const char *path)
errno = EISDIR;
break;
case xenhypfs_type_blob:
- errno = EDOM;
- break;
case xenhypfs_type_string:
ret_buf = buf;
buf = NULL;
--
2.34.1
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [RFC PATCH v3 5/7] libs: libxenhypfs - handle blob properties
2025-03-11 11:16 ` [RFC PATCH v3 5/7] libs: libxenhypfs - handle blob properties Grygorii Strashko
@ 2025-03-11 11:26 ` Juergen Gross
0 siblings, 0 replies; 30+ messages in thread
From: Juergen Gross @ 2025-03-11 11:26 UTC (permalink / raw)
To: Grygorii Strashko, xen-devel
Cc: Stefano Stabellini, Julien Grall, Andrew Cooper, Michal Orzel,
Roger Pau Monne, Jan Beulich, Anthony PERARD, Volodymyr Babchuk,
Oleksii Moisieiev, Bertrand Marquis
[-- Attachment #1.1.1: Type: text/plain, Size: 1212 bytes --]
On 11.03.25 12:16, Grygorii Strashko wrote:
> From: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
>
> libxenhypfs will return blob properties as is. This output can be used
> to retrieve information from the hypfs. Caller is responsible for
> parsing property value.
>
> Signed-off-by: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
> Reviewed-by: Volodymyr Babchuk <volodymyr_babchuk@epam.com>
> ---
> tools/libs/hypfs/core.c | 2 --
> 1 file changed, 2 deletions(-)
>
> diff --git a/tools/libs/hypfs/core.c b/tools/libs/hypfs/core.c
> index 52b30db8d777..d09bba7d8c86 100644
> --- a/tools/libs/hypfs/core.c
> +++ b/tools/libs/hypfs/core.c
> @@ -307,8 +307,6 @@ char *xenhypfs_read(xenhypfs_handle *fshdl, const char *path)
> errno = EISDIR;
> break;
> case xenhypfs_type_blob:
> - errno = EDOM;
> - break;
> case xenhypfs_type_string:
> ret_buf = buf;
> buf = NULL;
You are losing the size information. So you can only parse the blob
if the size is known or the blob is containing the size.
Any reason you don't want to use xenhypfs_read_raw(), which is
available exactly for that purpose?
Juergen
[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 3743 bytes --]
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 495 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* [RFC PATCH v3 6/7] xen/arm: Export host device-tree to hypfs
2025-03-11 11:16 [RFC PATCH v3 0/7] xen/arm: scmi: introduce SCI SCMI SMC multi-agent support Grygorii Strashko
` (4 preceding siblings ...)
2025-03-11 11:16 ` [RFC PATCH v3 5/7] libs: libxenhypfs - handle blob properties Grygorii Strashko
@ 2025-03-11 11:16 ` Grygorii Strashko
2025-03-11 11:16 ` [RFC PATCH v3 7/7] xen/arm: scmi: generate scmi dt node for DomUs Grygorii Strashko
2025-03-21 11:25 ` [RFC PATCH v3 0/7] xen/arm: scmi: introduce SCI SCMI SMC multi-agent support Grygorii Strashko
7 siblings, 0 replies; 30+ messages in thread
From: Grygorii Strashko @ 2025-03-11 11:16 UTC (permalink / raw)
To: xen-devel
Cc: Stefano Stabellini, Julien Grall, Andrew Cooper, Michal Orzel,
Roger Pau Monne, Jan Beulich, Anthony PERARD, Volodymyr Babchuk,
Oleksii Moisieiev, Bertrand Marquis, Juergen Gross
From: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
If enabled, host device-tree will be exported to hypfs and can be
accessed through /devicetree path.
Exported device-tree has the same format, as the device-tree
exported to the sysfs by the Linux kernel.
This is useful when XEN toolstack needs an access to the host device-tree.
Signed-off-by: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
---
xen/arch/arm/Kconfig | 16 ++++++++++++++++
xen/arch/arm/Makefile | 1 +
xen/arch/arm/host_dtb_export.c | 28 ++++++++++++++++++++++++++++
3 files changed, 45 insertions(+)
create mode 100644 xen/arch/arm/host_dtb_export.c
diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
index a26d3e11827c..fa51244e2706 100644
--- a/xen/arch/arm/Kconfig
+++ b/xen/arch/arm/Kconfig
@@ -125,6 +125,22 @@ config DOM0LESS_BOOT
Xen boot without the need of a control domain (Dom0), which could be
present anyway.
+config HOST_DTB_EXPORT
+ bool "Export host device tree to hypfs if enabled"
+ depends on ARM && HYPFS && !ACPI
+ help
+
+ Export host device-tree to hypfs so toolstack can have an access for the
+ host device tree from Dom0. If you unsure say N.
+
+config HOST_DTB_MAX_SIZE
+ int "Max host dtb export size"
+ depends on HOST_DTB_EXPORT
+ default 8192
+ help
+
+ Maximum size of the host device-tree exported to hypfs.
+
config GICV3
bool "GICv3 driver"
depends on !NEW_VGIC
diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
index 43ab5e8f2550..1518592deb64 100644
--- a/xen/arch/arm/Makefile
+++ b/xen/arch/arm/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_DOM0LESS_BOOT) += dom0less-build.init.o
obj-y += domain.o
obj-y += domain_build.init.o
obj-y += domctl.o
+obj-$(CONFIG_HOST_DTB_EXPORT) += host_dtb_export.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
obj-y += efi/
obj-y += gic.o
diff --git a/xen/arch/arm/host_dtb_export.c b/xen/arch/arm/host_dtb_export.c
new file mode 100644
index 000000000000..c9beb2803883
--- /dev/null
+++ b/xen/arch/arm/host_dtb_export.c
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Export host FDT to the hypfs
+ *
+ * Copyright (C) 2024 EPAM Systems
+ */
+
+#include <xen/device_tree.h>
+#include <xen/hypfs.h>
+#include <xen/init.h>
+#include <xen/libfdt/libfdt.h>
+
+static HYPFS_VARSIZE_INIT(dt_prop, XEN_HYPFS_TYPE_BLOB,
+ "devicetree", CONFIG_HOST_DTB_MAX_SIZE,
+ &hypfs_leaf_ro_funcs);
+
+static int __init host_dtb_export_init(void)
+{
+ ASSERT(dt_host && (dt_host->sibling == NULL));
+
+ dt_prop.u.content = device_tree_flattened;
+ dt_prop.e.size = fdt_totalsize(device_tree_flattened);
+ hypfs_add_leaf(&hypfs_root, &dt_prop, true);
+
+ return 0;
+}
+
+__initcall(host_dtb_export_init);
--
2.34.1
^ permalink raw reply related [flat|nested] 30+ messages in thread* [RFC PATCH v3 7/7] xen/arm: scmi: generate scmi dt node for DomUs
2025-03-11 11:16 [RFC PATCH v3 0/7] xen/arm: scmi: introduce SCI SCMI SMC multi-agent support Grygorii Strashko
` (5 preceding siblings ...)
2025-03-11 11:16 ` [RFC PATCH v3 6/7] xen/arm: Export host device-tree to hypfs Grygorii Strashko
@ 2025-03-11 11:16 ` Grygorii Strashko
2025-03-11 11:46 ` Jan Beulich
2025-03-14 18:28 ` Anthony PERARD
2025-03-21 11:25 ` [RFC PATCH v3 0/7] xen/arm: scmi: introduce SCI SCMI SMC multi-agent support Grygorii Strashko
7 siblings, 2 replies; 30+ messages in thread
From: Grygorii Strashko @ 2025-03-11 11:16 UTC (permalink / raw)
To: xen-devel
Cc: Stefano Stabellini, Julien Grall, Andrew Cooper, Michal Orzel,
Roger Pau Monne, Jan Beulich, Anthony PERARD, Volodymyr Babchuk,
Oleksii Moisieiev, Bertrand Marquis, Juergen Gross,
Grygorii Strashko
From: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
This feature introduces SCMI support for DomU domains with partial SCMI DT
node generation.
During domain creation the following prerequisites are expected:
- SCMI node template in partial device-tree, which should contain all
subnodes used by DomU:
/ {
firmware {
scmi {
scmi_reset: protocol@19 {
\#reset-cells = <1>;
};
scmi_clk: protocol@14 {
\#clock-cells = <1>;
};
scmi_pinctrl: protocol@19 {
sdio_mux: mux {
};
mux1: mux1 {
};
};
};
};
passthrough {
sdio {
pinctrl-0 = <&sdio_mux>;
resets = <&scmi_reset 0x1>;
clocks = <&scmi_clk 0x1>;
};
dev1 {
resets = <&scmi_reset 2>;
pinctrl-0 = <&mux1>;
};
};
};
- properly defined "arm_sci" property in domain xl.cfg:
arm_sci = "type=scmi_smc_multiagent,agent_id=2"
- Platform/Xen DT exposed to Dom0 through Xen hypfs.
The Xen toolstack:
- obtains from Xen information about phys address of the SCMI shmem and
SMC/HVC id used by specified SCMI agent_id (domctl
XEN_DOMCTL_get_sci_info)
- creates the SCMI shmem node in domain DT using predefined guest MMIO
mappings and DT phandle
GUEST_SCI_SHMEM_BASE xen_mk_ullong(0x22001000)
GUEST_SCI_SHMEM_SIZE xen_mk_ullong(0x01000)
GUEST_PHANDLE_SCMI (GUEST_PHANDLE_IOMMU + 1)
- creates SCMI node in domain DT with:
- "shmem" phandle sets to GUEST_PHANDLE_SCMI
- "arm,smc-id" sets to SMC/HVC id obtained from Xen
- parses partial device tree and creates corresponding SCMI subnodes in
domain DT. All SCMI subnodes properties are copied from Xen DT except
phandles, which are taken from partial DT.
- maps the SCMI shmem into DomU GUEST_SCI_SHMEM_BASE address.
Signed-off-by: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
Signed-off-by: Grygorii Strashko <grygorii_strashko@epam.com>
---
tools/include/xenctrl.h | 3 +
tools/libs/ctrl/xc_domain.c | 18 ++
tools/libs/light/libxl_arm.c | 294 +++++++++++++++++++-
tools/libs/light/libxl_create.c | 12 +
tools/libs/light/libxl_internal.h | 3 +
xen/arch/arm/domctl.c | 22 ++
xen/arch/arm/firmware/scmi-smc-multiagent.c | 2 +
xen/arch/arm/include/asm/domain.h | 6 +
xen/include/public/arch-arm.h | 4 +
xen/include/public/device_tree_defs.h | 1 +
xen/include/public/domctl.h | 11 +
11 files changed, 365 insertions(+), 11 deletions(-)
diff --git a/tools/include/xenctrl.h b/tools/include/xenctrl.h
index 495598123133..54a93431641a 100644
--- a/tools/include/xenctrl.h
+++ b/tools/include/xenctrl.h
@@ -1205,6 +1205,9 @@ int xc_domain_getvnuma(xc_interface *xch,
int xc_domain_soft_reset(xc_interface *xch,
uint32_t domid);
+int xc_domain_get_sci_info(xc_interface *xch, uint32_t domid,
+ uint64_t *paddr, uint32_t *func_id);
+
#if defined(__i386__) || defined(__x86_64__)
/*
* PC BIOS standard E820 types and structure.
diff --git a/tools/libs/ctrl/xc_domain.c b/tools/libs/ctrl/xc_domain.c
index 2ddc3f4f426d..f4ffab2021cd 100644
--- a/tools/libs/ctrl/xc_domain.c
+++ b/tools/libs/ctrl/xc_domain.c
@@ -2229,6 +2229,24 @@ out:
return ret;
}
+
+int xc_domain_get_sci_info(xc_interface *xch, uint32_t domid,
+ uint64_t *paddr, uint32_t *func_id)
+{
+ struct xen_domctl domctl = {};
+
+ memset(&domctl, 0, sizeof(domctl));
+ domctl.cmd = XEN_DOMCTL_get_sci_info;
+ domctl.domain = domid;
+
+ if ( do_domctl(xch, &domctl) != 0 )
+ return 1;
+
+ *paddr = domctl.u.sci_info.paddr;
+ *func_id = domctl.u.sci_info.func_id;
+ return 0;
+}
+
/*
* Local variables:
* mode: C
diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c
index cdf5edb299af..cc54abc1ea79 100644
--- a/tools/libs/light/libxl_arm.c
+++ b/tools/libs/light/libxl_arm.c
@@ -9,6 +9,7 @@
#include <libfdt.h>
#include <assert.h>
#include <xen/device_tree_defs.h>
+#include <xenhypfs.h>
/*
* There is no clear requirements for the total size of Virtio MMIO region.
@@ -640,9 +641,6 @@ static int make_optee_node(libxl__gc *gc, void *fdt)
int res;
LOG(DEBUG, "Creating OP-TEE node in dtb");
- res = fdt_begin_node(fdt, "firmware");
- if (res) return res;
-
res = fdt_begin_node(fdt, "optee");
if (res) return res;
@@ -655,9 +653,6 @@ static int make_optee_node(libxl__gc *gc, void *fdt)
res = fdt_end_node(fdt);
if (res) return res;
- res = fdt_end_node(fdt);
- if (res) return res;
-
return 0;
}
@@ -1191,10 +1186,9 @@ static int copy_node(libxl__gc *gc, void *fdt, void *pfdt,
return 0;
}
-static int copy_node_by_path(libxl__gc *gc, const char *path,
- void *fdt, void *pfdt)
+static int get_path_nodeoff(const char *path, void *pfdt)
{
- int nodeoff, r;
+ int nodeoff;
const char *name = strrchr(path, '/');
if (!name)
@@ -1214,12 +1208,277 @@ static int copy_node_by_path(libxl__gc *gc, const char *path,
if (strcmp(fdt_get_name(pfdt, nodeoff, NULL), name))
return -FDT_ERR_NOTFOUND;
+ return nodeoff;
+}
+
+static int copy_node_by_path(libxl__gc *gc, const char *path,
+ void *fdt, void *pfdt)
+{
+ int nodeoff, r;
+
+ nodeoff = get_path_nodeoff(path, pfdt);
+ if (nodeoff < 0)
+ return nodeoff;
+
r = copy_node(gc, fdt, pfdt, nodeoff, 0);
if (r) return r;
return 0;
}
+static int map_sci_page(libxl__gc *gc, uint32_t domid, uint64_t paddr,
+ uint64_t guest_addr)
+{
+ int ret;
+ uint64_t _paddr_pfn = paddr >> XC_PAGE_SHIFT;
+ uint64_t _guest_pfn = guest_addr >> XC_PAGE_SHIFT;
+
+ assert(paddr && guest_addr);
+ LOG(DEBUG, "[%d] mapping sci shmem page %"PRIx64, domid, _paddr_pfn);
+
+ ret = xc_domain_iomem_permission(CTX->xch, domid, _paddr_pfn, 1, 1);
+ if (ret < 0) {
+ LOG(ERROR,
+ "failed give domain access to iomem page %"PRIx64,
+ _paddr_pfn);
+ return ret;
+ }
+
+ ret = xc_domain_memory_mapping(CTX->xch, domid,
+ _guest_pfn, _paddr_pfn,
+ 1, 1);
+ if (ret < 0) {
+ LOG(ERROR,
+ "failed to map to domain iomem page %"PRIx64
+ " to guest address %"PRIx64,
+ _paddr_pfn, _guest_pfn);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int scmi_dt_make_shmem_node(libxl__gc *gc, void *fdt)
+{
+ int res;
+ char buf[64];
+
+ snprintf(buf, sizeof(buf), "scmi-shmem@%llx", GUEST_SCI_SHMEM_BASE);
+
+ res = fdt_begin_node(fdt, buf);
+ if (res) return res;
+
+ res = fdt_property_compat(gc, fdt, 1, "arm,scmi-shmem");
+ if (res) return res;
+
+ res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS,
+ GUEST_ROOT_SIZE_CELLS, 1,
+ GUEST_SCI_SHMEM_BASE, GUEST_SCI_SHMEM_SIZE);
+ if (res) return res;
+
+ res = fdt_property_cell(fdt, "phandle", GUEST_PHANDLE_SCMI);
+ if (res) return res;
+
+ res = fdt_end_node(fdt);
+ if (res) return res;
+
+ return 0;
+}
+
+static const char *name_from_path(const char *path)
+{
+ return strrchr(path, '/') + 1;
+}
+
+static int dt_copy_properties(libxl__gc *gc, void* fdt, void *xen_fdt,
+ const char *full_name)
+{
+ int propoff, nameoff, r, nodeoff;
+ const struct fdt_property *prop;
+
+ LOG(DEBUG, "Copy properties for node: %s", full_name);
+ nodeoff = get_path_nodeoff(full_name, xen_fdt);
+ if (nodeoff < 0)
+ return -FDT_ERR_NOTFOUND;
+
+ for (propoff = fdt_first_property_offset(xen_fdt, nodeoff);
+ propoff >= 0;
+ propoff = fdt_next_property_offset(xen_fdt, propoff)) {
+
+ if (!(prop = fdt_get_property_by_offset(xen_fdt, propoff, NULL)))
+ return -FDT_ERR_INTERNAL;
+
+ nameoff = fdt32_to_cpu(prop->nameoff);
+
+ /* Skipping phandle nodes in xen device-tree */
+ if (strcmp(fdt_string(xen_fdt,nameoff), "phandle") == 0 ||
+ strcmp(fdt_string(xen_fdt, nameoff), "linux,phandle") == 0)
+ continue;
+
+ r = fdt_property(fdt, fdt_string(xen_fdt, nameoff),
+ prop->data, fdt32_to_cpu(prop->len));
+ if (r) return r;
+ }
+
+ return (propoff != -FDT_ERR_NOTFOUND)? propoff : 0;
+}
+
+static int scmi_dt_scan_node(libxl__gc *gc, void *fdt, void *pfdt,
+ void *xen_fdt, int nodeoff)
+{
+ int rc;
+ int node_next;
+ char full_name[128];
+ uint32_t phandle;
+
+ node_next = fdt_first_subnode(pfdt, nodeoff);
+ while (node_next > 0)
+ {
+ LOG(ERROR,"Processing node %s",
+ fdt_get_name(pfdt, node_next, NULL));
+
+ phandle = fdt_get_phandle(pfdt, node_next);
+
+ rc = fdt_get_path(pfdt, node_next, full_name, sizeof(full_name));
+ if (rc) return rc;
+
+ rc = fdt_begin_node(fdt, name_from_path(full_name));
+ if (rc) return rc;
+
+ rc = dt_copy_properties(gc, fdt, xen_fdt, full_name);
+ if (rc) return rc;
+
+ if (phandle) {
+ rc = fdt_property_cell(fdt, "phandle", phandle);
+ if (rc) return rc;
+ }
+
+ rc = scmi_dt_scan_node(gc, fdt, pfdt, xen_fdt, node_next);
+ if (rc) return rc;
+
+ rc = fdt_end_node(fdt);
+ if (rc) return rc;
+
+ node_next = fdt_next_subnode(pfdt, node_next);
+ }
+
+ return 0;
+}
+
+static int scmi_hypfs_fdt_check(libxl__gc *gc, void *fdt)
+{
+ int r;
+
+ if (fdt_magic(fdt) != FDT_MAGIC) {
+ LOG(ERROR, "FDT is not a valid Flat Device Tree");
+ return ERROR_FAIL;
+ }
+
+ r = fdt_check_header(fdt);
+ if (r) {
+ LOG(ERROR, "Failed to check the FDT (%d)", r);
+ return ERROR_FAIL;
+ }
+
+ return r;
+}
+
+static int scmi_dt_copy_subnodes(libxl__gc *gc, void *fdt, void *pfdt)
+{
+ struct xenhypfs_handle *hdl;
+ struct xenhypfs_dirent *ent;
+ void *xen_fdt;
+ int rc, nodeoff;
+
+ hdl = xenhypfs_open(NULL, 0);
+ if (!hdl)
+ return -EINVAL;
+
+ xen_fdt = xenhypfs_read_raw(hdl, "/devicetree", &ent);
+ if (!xen_fdt) {
+ rc = errno;
+ LOG(ERROR, "Unable to read hypfs entry: %d", rc);
+ goto out;
+ }
+
+ rc = scmi_hypfs_fdt_check(gc, xen_fdt);
+ if (rc) {
+ LOG(ERROR, "Hypfs device tree is invalid");
+ goto out;
+ }
+
+ nodeoff = get_path_nodeoff("/firmware/scmi", pfdt);
+ if (nodeoff <= 0) {
+ rc = -ENODEV;
+ goto out;
+ }
+
+ rc = scmi_dt_scan_node(gc, fdt, pfdt, xen_fdt, nodeoff);
+
+out:
+ xenhypfs_close(hdl);
+
+ return rc;
+}
+
+static int scmi_dt_create_node(libxl__gc *gc, void *fdt, void *pfdt,
+ uint32_t func_id)
+{
+ int rc = 0;
+
+ rc = fdt_begin_node(fdt, "scmi");
+ if (rc) return rc;
+
+ rc = fdt_property_compat(gc, fdt, 1, "arm,scmi-smc");
+ if (rc) return rc;
+
+ rc = fdt_property_cell(fdt, "shmem", GUEST_PHANDLE_SCMI);
+ if (rc) return rc;
+
+ rc = fdt_property_cell(fdt, "#addrets-cells", 1);
+ if (rc) return rc;
+
+ rc = fdt_property_cell(fdt, "#size-cells", 0);
+ if (rc) return rc;
+
+ rc = fdt_property_cell(fdt, "arm,smc-id", func_id);
+ if (rc) return rc;
+
+ rc = scmi_dt_copy_subnodes(gc, fdt, pfdt);
+ if (rc) return rc;
+
+ rc = fdt_end_node(fdt);
+ if (rc) return rc;
+
+ return rc;
+}
+
+static int make_firmware_node(libxl__gc *gc, void *fdt, void *pfdt, int tee,
+ int sci, uint32_t func_id)
+{
+ int res;
+
+ if ((tee == LIBXL_TEE_TYPE_NONE) && (sci == LIBXL_ARM_SCI_TYPE_NONE))
+ return 0;
+
+ res = fdt_begin_node(fdt, "firmware");
+ if (res) return res;
+
+ if (tee == LIBXL_TEE_TYPE_OPTEE) {
+ res = make_optee_node(gc, fdt);
+ if (res) return res;
+ }
+
+ if (sci == LIBXL_ARM_SCI_TYPE_SCMI_SMC_MULTIAGENT) {
+ res = scmi_dt_create_node(gc, fdt, pfdt, func_id);
+ if (res) return res;
+ }
+
+ res = fdt_end_node(fdt);
+ if (res) return res;
+ return 0;
+}
+
/*
* The partial device tree is not copied entirely. Only the relevant bits are
* copied to the guest device tree:
@@ -1391,8 +1650,11 @@ next_resize:
if (info->arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART)
FDT( make_vpl011_uart_node(gc, fdt, ainfo, dom) );
- if (info->tee == LIBXL_TEE_TYPE_OPTEE)
- FDT( make_optee_node(gc, fdt) );
+ if (info->arm_sci.type == LIBXL_ARM_SCI_TYPE_SCMI_SMC_MULTIAGENT)
+ FDT( scmi_dt_make_shmem_node(gc, fdt) );
+
+ FDT( make_firmware_node(gc, fdt, pfdt, info->tee, info->arm_sci.type,
+ state->arm_sci_agent_funcid) );
if (d_config->num_pcidevs)
FDT( make_vpci_node(gc, fdt, ainfo, dom) );
@@ -1671,6 +1933,16 @@ int libxl__arch_build_dom_finish(libxl__gc *gc,
{
int rc = 0, ret;
+ if (info->arm_sci.type == LIBXL_ARM_SCI_TYPE_SCMI_SMC_MULTIAGENT) {
+ ret = map_sci_page(gc, dom->guest_domid, state->arm_sci_agent_paddr,
+ GUEST_SCI_SHMEM_BASE);
+ if (ret < 0) {
+ LOG(ERROR, "map_sci_page failed\n");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+
if (info->arch_arm.vuart != LIBXL_VUART_TYPE_SBSA_UART) {
rc = 0;
goto out;
diff --git a/tools/libs/light/libxl_create.c b/tools/libs/light/libxl_create.c
index e03599ea99d1..ba26b9784838 100644
--- a/tools/libs/light/libxl_create.c
+++ b/tools/libs/light/libxl_create.c
@@ -813,6 +813,18 @@ int libxl__domain_make(libxl__gc *gc, libxl_domain_config *d_config,
*/
assert(libxl_domid_valid_guest(*domid));
+ if (d_config->b_info.arm_sci.type == LIBXL_ARM_SCI_TYPE_SCMI_SMC_MULTIAGENT) {
+ ret = xc_domain_get_sci_info(ctx->xch, *domid, &state->arm_sci_agent_paddr,
+ &state->arm_sci_agent_funcid);
+ LOGD(DEBUG, *domid,"sci_agent_paddr = %lx", state->arm_sci_agent_paddr);
+ if (ret) {
+ LOGED(ERROR, *domid, "failed to get sci paddr");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ }
+
dom_path = libxl__xs_get_dompath(gc, *domid);
if (!dom_path) {
rc = ERROR_FAIL;
diff --git a/tools/libs/light/libxl_internal.h b/tools/libs/light/libxl_internal.h
index cfac8e18b6d3..349c89a938ca 100644
--- a/tools/libs/light/libxl_internal.h
+++ b/tools/libs/light/libxl_internal.h
@@ -1405,6 +1405,9 @@ typedef struct {
* applicable to the primary domain, not support domains (e.g. stub QEMU). */
bool restore;
bool soft_reset;
+
+ uint64_t arm_sci_agent_paddr;
+ uint32_t arm_sci_agent_funcid;
} libxl__domain_build_state;
_hidden void libxl__domain_build_state_init(libxl__domain_build_state *s);
diff --git a/xen/arch/arm/domctl.c b/xen/arch/arm/domctl.c
index 9d047065ba13..3ac77ea4d497 100644
--- a/xen/arch/arm/domctl.c
+++ b/xen/arch/arm/domctl.c
@@ -49,6 +49,17 @@ static int handle_vuart_init(struct domain *d,
return rc;
}
+static int get_sci_info(struct domain *d, struct xen_domctl_sci_info *sci_info)
+{
+#ifdef CONFIG_ARM_SCI
+ sci_info->paddr = d->arch.sci_channel.paddr;
+ sci_info->func_id = d->arch.sci_channel.guest_func_id;
+ return 0;
+#else
+ return -ENODEV;
+#endif
+}
+
long arch_do_domctl(struct xen_domctl *domctl, struct domain *d,
XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl)
{
@@ -179,6 +190,17 @@ long arch_do_domctl(struct xen_domctl *domctl, struct domain *d,
}
case XEN_DOMCTL_dt_overlay:
return dt_overlay_domctl(d, &domctl->u.dt_overlay);
+
+ case XEN_DOMCTL_get_sci_info:
+ {
+ int rc = get_sci_info(d, &domctl->u.sci_info);
+
+ if ( !rc )
+ rc = copy_to_guest(u_domctl, domctl, 1);
+
+ return rc;
+ }
+
default:
return subarch_do_domctl(domctl, d, u_domctl);
}
diff --git a/xen/arch/arm/firmware/scmi-smc-multiagent.c b/xen/arch/arm/firmware/scmi-smc-multiagent.c
index 293fb30fa6c5..c2f43d97d804 100644
--- a/xen/arch/arm/firmware/scmi-smc-multiagent.c
+++ b/xen/arch/arm/firmware/scmi-smc-multiagent.c
@@ -560,6 +560,8 @@ static int scmi_domain_init(struct domain *d,
d->arch.sci_data = channel;
d->arch.sci_enabled = true;
+ d->arch.sci_channel.paddr = channel->paddr;
+ d->arch.sci_channel.guest_func_id = channel->func_id;
return 0;
diff --git a/xen/arch/arm/include/asm/domain.h b/xen/arch/arm/include/asm/domain.h
index fa0898b7cf80..511f4aa8ed8d 100644
--- a/xen/arch/arm/include/asm/domain.h
+++ b/xen/arch/arm/include/asm/domain.h
@@ -59,6 +59,11 @@ struct paging_domain {
unsigned long p2m_total_pages;
};
+struct sci_channel {
+ uint32_t guest_func_id;
+ uint64_t paddr;
+};
+
struct arch_domain
{
#ifdef CONFIG_ARM_64
@@ -122,6 +127,7 @@ struct arch_domain
bool sci_enabled;
/* ARM SCI driver's specific data */
void *sci_data;
+ struct sci_channel sci_channel;
#endif
} __cacheline_aligned;
diff --git a/xen/include/public/arch-arm.h b/xen/include/public/arch-arm.h
index 30e46de6d7a0..a5b22225bf31 100644
--- a/xen/include/public/arch-arm.h
+++ b/xen/include/public/arch-arm.h
@@ -469,6 +469,10 @@ typedef uint64_t xen_callback_t;
#define GUEST_PL011_BASE xen_mk_ullong(0x22000000)
#define GUEST_PL011_SIZE xen_mk_ullong(0x00001000)
+/* SCI mediator */
+#define GUEST_SCI_SHMEM_BASE xen_mk_ullong(0x22001000)
+#define GUEST_SCI_SHMEM_SIZE xen_mk_ullong(0x01000)
+
/* Guest PCI-PCIe memory space where config space and BAR will be available.*/
#define GUEST_VPCI_ADDR_TYPE_MEM xen_mk_ullong(0x02000000)
#define GUEST_VPCI_MEM_ADDR xen_mk_ullong(0x23000000)
diff --git a/xen/include/public/device_tree_defs.h b/xen/include/public/device_tree_defs.h
index 9e80d0499dc3..b8bdfcdcf0b9 100644
--- a/xen/include/public/device_tree_defs.h
+++ b/xen/include/public/device_tree_defs.h
@@ -14,6 +14,7 @@
*/
#define GUEST_PHANDLE_GIC (65000)
#define GUEST_PHANDLE_IOMMU (GUEST_PHANDLE_GIC + 1)
+#define GUEST_PHANDLE_SCMI (GUEST_PHANDLE_IOMMU + 1)
#define GUEST_ROOT_ADDRESS_CELLS 2
#define GUEST_ROOT_SIZE_CELLS 2
diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h
index e2d392d1e5e5..6ef78c241f8c 100644
--- a/xen/include/public/domctl.h
+++ b/xen/include/public/domctl.h
@@ -1223,6 +1223,13 @@ struct xen_domctl_vmtrace_op {
#define XEN_DOMCTL_vmtrace_get_option 5
#define XEN_DOMCTL_vmtrace_set_option 6
};
+
+/* XEN_DOMCTL_get_sci_info */
+struct xen_domctl_sci_info {
+ uint64_t paddr;
+ uint32_t func_id;
+};
+
typedef struct xen_domctl_vmtrace_op xen_domctl_vmtrace_op_t;
DEFINE_XEN_GUEST_HANDLE(xen_domctl_vmtrace_op_t);
@@ -1333,6 +1340,9 @@ struct xen_domctl {
#define XEN_DOMCTL_dt_overlay 87
#define XEN_DOMCTL_gsi_permission 88
#define XEN_DOMCTL_set_llc_colors 89
+
+#define XEN_DOMCTL_get_sci_info 90
+
#define XEN_DOMCTL_gdbsx_guestmemio 1000
#define XEN_DOMCTL_gdbsx_pausevcpu 1001
#define XEN_DOMCTL_gdbsx_unpausevcpu 1002
@@ -1400,6 +1410,7 @@ struct xen_domctl {
struct xen_domctl_dt_overlay dt_overlay;
#endif
struct xen_domctl_set_llc_colors set_llc_colors;
+ struct xen_domctl_sci_info sci_info;
uint8_t pad[128];
} u;
};
--
2.34.1
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [RFC PATCH v3 7/7] xen/arm: scmi: generate scmi dt node for DomUs
2025-03-11 11:16 ` [RFC PATCH v3 7/7] xen/arm: scmi: generate scmi dt node for DomUs Grygorii Strashko
@ 2025-03-11 11:46 ` Jan Beulich
2025-03-14 18:28 ` Anthony PERARD
1 sibling, 0 replies; 30+ messages in thread
From: Jan Beulich @ 2025-03-11 11:46 UTC (permalink / raw)
To: Grygorii Strashko
Cc: Stefano Stabellini, Julien Grall, Andrew Cooper, Michal Orzel,
Roger Pau Monne, Anthony PERARD, Volodymyr Babchuk,
Oleksii Moisieiev, Bertrand Marquis, Juergen Gross,
Grygorii Strashko, xen-devel
On 11.03.2025 12:16, Grygorii Strashko wrote:
> --- a/xen/include/public/domctl.h
> +++ b/xen/include/public/domctl.h
> @@ -1223,6 +1223,13 @@ struct xen_domctl_vmtrace_op {
> #define XEN_DOMCTL_vmtrace_get_option 5
> #define XEN_DOMCTL_vmtrace_set_option 6
> };
> +
> +/* XEN_DOMCTL_get_sci_info */
> +struct xen_domctl_sci_info {
> + uint64_t paddr;
> + uint32_t func_id;
> +};
Please take a look at the rest of this header: Outside of x86-specific
sub-ops there's no use of uint64_t; uint64_aligned_t wants using instead.
> @@ -1333,6 +1340,9 @@ struct xen_domctl {
> #define XEN_DOMCTL_dt_overlay 87
> #define XEN_DOMCTL_gsi_permission 88
> #define XEN_DOMCTL_set_llc_colors 89
> +
> +#define XEN_DOMCTL_get_sci_info 90
> +
> #define XEN_DOMCTL_gdbsx_guestmemio 1000
> #define XEN_DOMCTL_gdbsx_pausevcpu 1001
> #define XEN_DOMCTL_gdbsx_unpausevcpu 1002
The latter of the blank lines may make sense to add. There former shouldn't
be there imo.
Jan
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [RFC PATCH v3 7/7] xen/arm: scmi: generate scmi dt node for DomUs
2025-03-11 11:16 ` [RFC PATCH v3 7/7] xen/arm: scmi: generate scmi dt node for DomUs Grygorii Strashko
2025-03-11 11:46 ` Jan Beulich
@ 2025-03-14 18:28 ` Anthony PERARD
1 sibling, 0 replies; 30+ messages in thread
From: Anthony PERARD @ 2025-03-14 18:28 UTC (permalink / raw)
To: Grygorii Strashko
Cc: xen-devel, Stefano Stabellini, Julien Grall, Andrew Cooper,
Michal Orzel, Roger Pau Monne, Jan Beulich, Volodymyr Babchuk,
Oleksii Moisieiev, Bertrand Marquis, Juergen Gross,
Grygorii Strashko
On Tue, Mar 11, 2025 at 01:16:18PM +0200, Grygorii Strashko wrote:
> diff --git a/tools/libs/ctrl/xc_domain.c b/tools/libs/ctrl/xc_domain.c
> index 2ddc3f4f426d..f4ffab2021cd 100644
> --- a/tools/libs/ctrl/xc_domain.c
> +++ b/tools/libs/ctrl/xc_domain.c
> @@ -2229,6 +2229,24 @@ out:
>
> return ret;
> }
> +
> +int xc_domain_get_sci_info(xc_interface *xch, uint32_t domid,
> + uint64_t *paddr, uint32_t *func_id)
> +{
> + struct xen_domctl domctl = {};
> +
> + memset(&domctl, 0, sizeof(domctl));
This memset() looks redundant with variable initialisation.
> + domctl.cmd = XEN_DOMCTL_get_sci_info;
> + domctl.domain = domid;
There's one way to write these first few lines more nicely:
struct xen_domctl domctl = {
.cmd = XEN_DOMCTL_get_sci_info,
.domain = domid,
};
> + if ( do_domctl(xch, &domctl) != 0 )
> + return 1;
The error return value should be -1. (Returning the value returned by
do_domctl() is also an option.)
> +
> + *paddr = domctl.u.sci_info.paddr;
> + *func_id = domctl.u.sci_info.func_id;
> + return 0;
> +}
> +
> /*
> * Local variables:
> * mode: C
> diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c
> index cdf5edb299af..cc54abc1ea79 100644
> --- a/tools/libs/light/libxl_arm.c
> +++ b/tools/libs/light/libxl_arm.c
> @@ -9,6 +9,7 @@
> #include <libfdt.h>
> #include <assert.h>
> #include <xen/device_tree_defs.h>
> +#include <xenhypfs.h>
>
> /*
> * There is no clear requirements for the total size of Virtio MMIO region.
> @@ -640,9 +641,6 @@ static int make_optee_node(libxl__gc *gc, void *fdt)
> int res;
> LOG(DEBUG, "Creating OP-TEE node in dtb");
>
> - res = fdt_begin_node(fdt, "firmware");
> - if (res) return res;
> -
> res = fdt_begin_node(fdt, "optee");
> if (res) return res;
>
> @@ -655,9 +653,6 @@ static int make_optee_node(libxl__gc *gc, void *fdt)
> res = fdt_end_node(fdt);
> if (res) return res;
>
> - res = fdt_end_node(fdt);
> - if (res) return res;
> -
> return 0;
> }
>
> @@ -1191,10 +1186,9 @@ static int copy_node(libxl__gc *gc, void *fdt, void *pfdt,
> return 0;
> }
>
> -static int copy_node_by_path(libxl__gc *gc, const char *path,
> - void *fdt, void *pfdt)
> +static int get_path_nodeoff(const char *path, void *pfdt)
> {
> - int nodeoff, r;
> + int nodeoff;
> const char *name = strrchr(path, '/');
>
> if (!name)
> @@ -1214,12 +1208,277 @@ static int copy_node_by_path(libxl__gc *gc, const char *path,
> if (strcmp(fdt_get_name(pfdt, nodeoff, NULL), name))
> return -FDT_ERR_NOTFOUND;
>
> + return nodeoff;
> +}
> +
> +static int copy_node_by_path(libxl__gc *gc, const char *path,
> + void *fdt, void *pfdt)
> +{
> + int nodeoff, r;
> +
> + nodeoff = get_path_nodeoff(path, pfdt);
> + if (nodeoff < 0)
> + return nodeoff;
> +
> r = copy_node(gc, fdt, pfdt, nodeoff, 0);
> if (r) return r;
>
> return 0;
> }
>
> +static int map_sci_page(libxl__gc *gc, uint32_t domid, uint64_t paddr,
> + uint64_t guest_addr)
> +{
> + int ret;
> + uint64_t _paddr_pfn = paddr >> XC_PAGE_SHIFT;
> + uint64_t _guest_pfn = guest_addr >> XC_PAGE_SHIFT;
> +
> + assert(paddr && guest_addr);
> + LOG(DEBUG, "[%d] mapping sci shmem page %"PRIx64, domid, _paddr_pfn);
Use LOGD() instead, to print domid:
LOGD(DEBUG, domid, "mapping sci shmem page %"PRIx64, _paddr_pfn);
And use LOGD() instead of LOG() throughout the patch whenever `domid` is
available.
> +
> + ret = xc_domain_iomem_permission(CTX->xch, domid, _paddr_pfn, 1, 1);
> + if (ret < 0) {
> + LOG(ERROR,
> + "failed give domain access to iomem page %"PRIx64,
> + _paddr_pfn);
You can use LOGED() variant, which would also print errno in the log as
well. You can use it everything you log an error from a system function
(or xc_* one which should set errno on error).
> + return ret;
> + }
> +
> + ret = xc_domain_memory_mapping(CTX->xch, domid,
> + _guest_pfn, _paddr_pfn,
> + 1, 1);
> + if (ret < 0) {
> + LOG(ERROR,
> + "failed to map to domain iomem page %"PRIx64
> + " to guest address %"PRIx64,
> + _paddr_pfn, _guest_pfn);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int scmi_dt_make_shmem_node(libxl__gc *gc, void *fdt)
> +{
> + int res;
> + char buf[64];
> +
> + snprintf(buf, sizeof(buf), "scmi-shmem@%llx", GUEST_SCI_SHMEM_BASE);
> +
> + res = fdt_begin_node(fdt, buf);
> + if (res) return res;
> +
> + res = fdt_property_compat(gc, fdt, 1, "arm,scmi-shmem");
> + if (res) return res;
> +
> + res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS,
> + GUEST_ROOT_SIZE_CELLS, 1,
> + GUEST_SCI_SHMEM_BASE, GUEST_SCI_SHMEM_SIZE);
> + if (res) return res;
> +
> + res = fdt_property_cell(fdt, "phandle", GUEST_PHANDLE_SCMI);
> + if (res) return res;
> +
> + res = fdt_end_node(fdt);
> + if (res) return res;
> +
> + return 0;
> +}
> +
> +static const char *name_from_path(const char *path)
> +{
> + return strrchr(path, '/') + 1;
This function should probably return NULL in case the chr '/' isn't
found (strrchr returns NULL) instead of 0x1.
Also, it might be worth moving this function to just after "#ifdef
ENABLE_PARTIAL_DEVICE_TREE" in the source file, seen that
get_path_nodeoff() could potentially use it as well.
> +}
> +
> +static int dt_copy_properties(libxl__gc *gc, void* fdt, void *xen_fdt,
^ void *fdt.
> + const char *full_name)
> +{
> + int propoff, nameoff, r, nodeoff;
> + const struct fdt_property *prop;
> +
> + LOG(DEBUG, "Copy properties for node: %s", full_name);
> + nodeoff = get_path_nodeoff(full_name, xen_fdt);
> + if (nodeoff < 0)
> + return -FDT_ERR_NOTFOUND;
> +
> + for (propoff = fdt_first_property_offset(xen_fdt, nodeoff);
> + propoff >= 0;
> + propoff = fdt_next_property_offset(xen_fdt, propoff)) {
> +
> + if (!(prop = fdt_get_property_by_offset(xen_fdt, propoff, NULL)))
> + return -FDT_ERR_INTERNAL;
> +
> + nameoff = fdt32_to_cpu(prop->nameoff);
> +
> + /* Skipping phandle nodes in xen device-tree */
> + if (strcmp(fdt_string(xen_fdt,nameoff), "phandle") == 0 ||
> + strcmp(fdt_string(xen_fdt, nameoff), "linux,phandle") == 0)
> + continue;
> +
> + r = fdt_property(fdt, fdt_string(xen_fdt, nameoff),
> + prop->data, fdt32_to_cpu(prop->len));
> + if (r) return r;
> + }
> +
> + return (propoff != -FDT_ERR_NOTFOUND)? propoff : 0;
> +}
> +
> +static int scmi_dt_scan_node(libxl__gc *gc, void *fdt, void *pfdt,
> + void *xen_fdt, int nodeoff)
> +{
> + int rc;
Could you call this variable `r` instead? `rc` is reserved for libxl's
returns codes. See "CONVENTIONAL VARIABLE NAMES" in
libs/light/CODING_STYLE.
> + int node_next;
> + char full_name[128];
> + uint32_t phandle;
> +
> + node_next = fdt_first_subnode(pfdt, nodeoff);
> + while (node_next > 0)
> + {
> + LOG(ERROR,"Processing node %s",
> + fdt_get_name(pfdt, node_next, NULL));
> +
> + phandle = fdt_get_phandle(pfdt, node_next);
> +
> + rc = fdt_get_path(pfdt, node_next, full_name, sizeof(full_name));
> + if (rc) return rc;
> +
> + rc = fdt_begin_node(fdt, name_from_path(full_name));
> + if (rc) return rc;
> +
> + rc = dt_copy_properties(gc, fdt, xen_fdt, full_name);
> + if (rc) return rc;
> +
> + if (phandle) {
> + rc = fdt_property_cell(fdt, "phandle", phandle);
> + if (rc) return rc;
> + }
> +
> + rc = scmi_dt_scan_node(gc, fdt, pfdt, xen_fdt, node_next);
> + if (rc) return rc;
> +
> + rc = fdt_end_node(fdt);
> + if (rc) return rc;
> +
> + node_next = fdt_next_subnode(pfdt, node_next);
> + }
> +
> + return 0;
> +}
> +
> +static int scmi_hypfs_fdt_check(libxl__gc *gc, void *fdt)
> +{
> + int r;
> +
> + if (fdt_magic(fdt) != FDT_MAGIC) {
> + LOG(ERROR, "FDT is not a valid Flat Device Tree");
> + return ERROR_FAIL;
> + }
> +
> + r = fdt_check_header(fdt);
> + if (r) {
> + LOG(ERROR, "Failed to check the FDT (%d)", r);
> + return ERROR_FAIL;
> + }
> +
> + return r;
> +}
> +
> +static int scmi_dt_copy_subnodes(libxl__gc *gc, void *fdt, void *pfdt)
> +{
> + struct xenhypfs_handle *hdl;
> + struct xenhypfs_dirent *ent;
> + void *xen_fdt;
> + int rc, nodeoff;
> +
> + hdl = xenhypfs_open(NULL, 0);
> + if (!hdl)
> + return -EINVAL;
That value been return doesn't make sense. Also !0 is probably enough.
> +
> + xen_fdt = xenhypfs_read_raw(hdl, "/devicetree", &ent);
> + if (!xen_fdt) {
> + rc = errno;
Is doesn't seems taht anything in the functions constructind the device
tree look at return values as if the were "errno". There's maybe just
one libxl__prepare_dtb() which looks at those value as an FDT_* error
code, via the FDT() macro. Most of the other just check for !0 values.
If errno value is uesful here, you can at least log it by replacing
LOG() by LOGE() in this next line:
> + LOG(ERROR, "Unable to read hypfs entry: %d", rc);
`rc` which contain `errno` here isn't useful to log as is, instead use
LOGE() which will write strerror(errno) automatically. Also, maybe
writing which entry can't be read in the log might be useful.
[...]
Please use `r` to store return code of many of those new function
instead of a mixture of `res`, `ret`, `r`, `rc`. `rc` is reserved for
libxl error code, and are expected to by returned by libxl_*()
functions. But it seems that most functions dealing with the device tree
actually are expected to return FDT_* error code.
Thanks,
--
Anthony Perard | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFC PATCH v3 0/7] xen/arm: scmi: introduce SCI SCMI SMC multi-agent support
2025-03-11 11:16 [RFC PATCH v3 0/7] xen/arm: scmi: introduce SCI SCMI SMC multi-agent support Grygorii Strashko
` (6 preceding siblings ...)
2025-03-11 11:16 ` [RFC PATCH v3 7/7] xen/arm: scmi: generate scmi dt node for DomUs Grygorii Strashko
@ 2025-03-21 11:25 ` Grygorii Strashko
7 siblings, 0 replies; 30+ messages in thread
From: Grygorii Strashko @ 2025-03-21 11:25 UTC (permalink / raw)
To: Grygorii Strashko, xen-devel
Cc: Stefano Stabellini, Julien Grall, Andrew Cooper, Michal Orzel,
Roger Pau Monne, Jan Beulich, Anthony PERARD, Volodymyr Babchuk,
Oleksii Moisieiev, Bertrand Marquis, Juergen Gross
Hi All, Jan, Anthony, Stefano,
On 11.03.25 13:16, Grygorii Strashko wrote:
> Hi,
>
> This is respin of RFCv2 series from Oleksii Moisieiev [1] which was send pretty long time ago (2022),
> with the main intention is to resume this discussion in public and gather more opinions.
>
> Hence the code was previously sent long time ago there are pretty high number of changes,
> including rebase on newer Xen version 4.20.0-rc2, which already contains some basic SCMI SMC driver
> introduced by Andrei Cherechesu, commit 3e322bef8bc0 ("xen/arm: firmware: Add SCMI over SMC calls
> handling layer").
>
> Patch 1 "xen/arm: add generic SCI subsystem"
> - rebased and refactored
> - introduced DEVICE_ARM_SCI DT device class and used for SCI drivers probing instead of custom,
> linker sections based implementation.
> - added SCI API for Dom0 DT handling, instead of manipulating with ARM arch dom0 code directly.
> - TODO: RFC changes in XEN_DOMCTL_assign_device OP processing
>
> Patch 2 "xen/arm: scmi-smc: update to be used under sci subsystem"
> - update driver introduced by commit 3e322bef8bc0 ("xen/arm: firmware: Add SCMI over SMC calls
> handling layer") be used under sci subsystem.
> - no functional changes in general
>
> Patch 3 "xen/arm: scmi-smc: passthrough SCMI SMC to guest domain
> This is new change which allows passthrough SCMI SMC, single agent interface to guest domain
> cover use case "thin Dom0 with guest domain, which serves as Driver domain".
> See patch commit message for full description.
>
> Patch 4 - xen/arm: scmi: introduce SCI SCMI SMC multi-agent driver
> - added "xen,scmi-secondary-agents" property in "chosen" to inform SCI SCMI multi-agent driver
> about available agents and their configuration. It defines <agent_id> to <smc-id,scmi_shm> map.
> This option is Xen specific as Xen is the only one entry in the system which need to know
> about SCMI multi-agent support and configuration.
> - each guest using SCMI should be configured with SCMI agent_id, so SCMI
> FW can implement Agent-specific permission policy.
> -- dom0: dom0_scmi_agent_id=<agent_id> in Xen command line option
> -- toolstack: arm_sci = "type=scmi_smc_multiagent,agent_id=<agent_id>"
> -- dom0less: todo: "xen,sci_type", "xen,sci_agent_id" properties in "xen,domain" nodes.
> - factored out SCMI generic definitions (re-usable)
> - factored out SCMI shmem code (re-usable)
> - the SCMI passthrough configuration for guest domains is similar to any other HW passthrough cfg.
>
> Patches 5-7
> - no major changes, except to follow rebase and changes in previous patches
>
> Regarding patches 5-7 I'd like to rise a question and I, personally, feel very skeptical doing any
> kind of SCMI DT nodes generation as from toolstack as from Xen.
> 1) SCMI is no differ as any other HW MFD device, and HW passthrough configuration works for it in
> the same way.
> 2) if toolstack generates DT then dom0less case might need it also, but this means more code in Xen,
> so, with certification in mind, it means more overhead requirements, docs and testing.
> In my opinion if something can be done outside "kernel" - it should.
> So better invest in tools (imagebuilder, lopper, etc.) instead.
> 3) Hence SCMI DT bindings are pretty complex the corresponding guest DT nodes can't be generated
> from scratch - the user still need to add scmi node, protocols and protocols subnodes in the
> partial device tree, at least empty. But stop, not exactly empty - the properties like
> "#clock-cells" need to be added to avoid DTC warnings. Such behavior is rather confusing than
> helpful.
> 4) Exposing the Host Device tree in Dom0 is another questionable thing for toolstack SCMI DT
> generation. It consumes 128K of memory on Renesas r8a779g0-whitehawk.
> 5) No needs for additional public API (XEN_DOMCTL_get_sci_info, GUEST_SCI_SHMEM_BASE..) if dropped
>
Thanks a lot for your time, review and valuable comments.
Most of comments are clear enough and will be addressed.
Those, required clarification, I'll reply separately.
--
Best regards,
-grygorii
^ permalink raw reply [flat|nested] 30+ messages in thread