diff --git a/MAINTAINERS b/MAINTAINERS index c4886c1159..31dbba54bb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -509,6 +509,12 @@ R: George Dunlap S: Supported F: xen/common/sched/ +SCI MEDIATORS +R: Oleksii Moisieiev +S: Supported +F: xen/arch/arm/firmware/sci.c +F: xen/arch/arm/include/asm/firmware/sci.h + SEABIOS UPSTREAM M: Wei Liu S: Supported diff --git a/xen/arch/arm/device.c b/xen/arch/arm/device.c index 11523750ae..74b54cad34 100644 --- a/xen/arch/arm/device.c +++ b/xen/arch/arm/device.c @@ -13,6 +13,7 @@ #include #include +#include #include 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 c8d07213e2..0094cf9e40 100644 --- a/xen/arch/arm/dom0less-build.c +++ b/xen/arch/arm/dom0less-build.c @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -272,6 +273,12 @@ int __init init_vuart(struct domain *d, struct kernel_info *kinfo, return rc; } +int __init arch_handle_passthrough_prop(struct kernel_info *kinfo, + struct dt_device_node *node) +{ + return sci_assign_dt_device(kinfo->bd.d, node); +} + int __init arch_parse_dom0less_node(struct dt_device_node *node, struct boot_domain *bd) { @@ -281,6 +288,7 @@ int __init arch_parse_dom0less_node(struct dt_device_node *node, d_cfg->arch.gic_version = XEN_DOMCTL_CONFIG_GIC_NATIVE; d_cfg->flags |= XEN_DOMCTL_CDF_hvm | XEN_DOMCTL_CDF_hap; + d_cfg->arch.arm_sci_type = XEN_DOMCTL_CONFIG_ARM_SCI_NONE; if ( !dt_property_read_u32(node, "nr_spis", &d_cfg->arch.nr_spis) ) { diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c index 863ae18157..1a8585d02b 100644 --- a/xen/arch/arm/domain.c +++ b/xen/arch/arm/domain.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -699,7 +700,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, @@ -791,6 +792,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: @@ -851,6 +855,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)); @@ -1044,6 +1049,7 @@ enum { PROG_p2m_root, PROG_p2m, PROG_p2m_pool, + PROG_sci, PROG_done, }; @@ -1103,6 +1109,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 a9e4153e3c..039aa71439 100644 --- a/xen/arch/arm/domain_build.c +++ b/xen/arch/arm/domain_build.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -1640,6 +1641,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. @@ -1740,6 +1744,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 817da745fd..fc7918c7fc 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 a5e4542666..71bdefc24a 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 0000000000..aa93cda7f0 --- /dev/null +++ b/xen/arch/arm/firmware/sci.c @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Generic part of the SCI (System Control Interface) subsystem. + * + * Oleksii Moisieiev + * Copyright (c) 2025 EPAM Systems + */ + +#include +#include +#include +#include +#include + +#include + +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 *regs) +{ + if ( unlikely(!cur_mediator) ) + return false; + + return cur_mediator->handle_call(regs); +} + +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; +} + +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_FIRMWARE, 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 a3487ca713..af3e168374 100644 --- a/xen/arch/arm/include/asm/domain.h +++ b/xen/arch/arm/include/asm/domain.h @@ -120,6 +120,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 0000000000..3500216bc2 --- /dev/null +++ b/xen/arch/arm/include/asm/firmware/sci.h @@ -0,0 +1,200 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Generic ARM SCI (System Control Interface) subsystem. + * + * Oleksii Moisieiev + * Copyright (c) 2025 EPAM Systems + */ + +#ifndef __ASM_ARM_SCI_H +#define __ASM_ARM_SCI_H + +#include +#include +#include +#include +#include + +#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); +#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 *regs) +{ + 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 0; +} + +static inline int sci_assign_dt_device(struct domain *d, + struct dt_device_node *dev) +{ + 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 6081f14ed0..4095171533 100644 --- a/xen/arch/arm/vsmc.c +++ b/xen/arch/arm/vsmc.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -232,7 +233,7 @@ static bool handle_sip(struct cpu_user_regs *regs) if ( platform_smc(regs) ) return true; - return scmi_handle_smc(regs); + return (scmi_handle_smc(regs)) ? true : sci_handle_call(regs); } /* diff --git a/xen/common/device-tree/dom0less-build.c b/xen/common/device-tree/dom0less-build.c index badc227031..aaa5e9b22c 100644 --- a/xen/common/device-tree/dom0less-build.c +++ b/xen/common/device-tree/dom0less-build.c @@ -228,6 +228,10 @@ static int __init handle_passthrough_prop(struct kernel_info *kinfo, if ( res < 0 ) return res; + res = arch_handle_passthrough_prop(kinfo, node); + if ( res ) + return res; + /* If xen_force, we allow assignment of devices without IOMMU protection. */ if ( xen_force && !dt_device_is_protected(node) ) return 0; diff --git a/xen/include/asm-generic/device.h b/xen/include/asm-generic/device.h index 3bd97e33c5..cb13f7faea 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_FIRMWARE, /* Use for error */ DEVICE_UNKNOWN, }; diff --git a/xen/include/public/arch-arm.h b/xen/include/public/arch-arm.h index e2412a1747..e7a17ede3e 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__ */ diff --git a/xen/include/xen/dom0less-build.h b/xen/include/xen/dom0less-build.h index 408859e325..faaf660424 100644 --- a/xen/include/xen/dom0less-build.h +++ b/xen/include/xen/dom0less-build.h @@ -62,6 +62,9 @@ void set_domain_type(struct domain *d, struct kernel_info *kinfo); int init_intc_phandle(struct kernel_info *kinfo, const char *name, const int node_next, const void *pfdt); +int arch_handle_passthrough_prop(struct kernel_info *kinfo, + struct dt_device_node *node); + #else /* !CONFIG_DOM0LESS_BOOT */ static inline void create_domUs(void) {}