diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig index c5e7b74733..a26d3e1182 100644 --- a/xen/arch/arm/Kconfig +++ b/xen/arch/arm/Kconfig @@ -272,6 +272,8 @@ config PARTIAL_EMULATION not been emulated to their complete functionality. Enabling this might result in unwanted/non-spec compliant behavior. +source "arch/arm/firmware/Kconfig" + endmenu menu "ARM errata workaround via the alternative framework" diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile index ccbfc61f88..43ab5e8f25 100644 --- a/xen/arch/arm/Makefile +++ b/xen/arch/arm/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_HAS_PCI) += pci/ ifneq ($(CONFIG_NO_PLAT),y) obj-y += platforms/ endif +obj-y += firmware/ obj-$(CONFIG_TEE) += tee/ obj-$(CONFIG_HAS_VPCI) += vpci.o diff --git a/xen/arch/arm/firmware/Kconfig b/xen/arch/arm/firmware/Kconfig new file mode 100644 index 0000000000..817da745fd --- /dev/null +++ b/xen/arch/arm/firmware/Kconfig @@ -0,0 +1,13 @@ +menu "Firmware Drivers" + +config SCMI_SMC + bool "Forward SCMI over SMC calls from hwdom to EL3 firmware" + default y + help + This option enables basic awareness for SCMI calls using SMC as + 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. + +endmenu diff --git a/xen/arch/arm/firmware/Makefile b/xen/arch/arm/firmware/Makefile new file mode 100644 index 0000000000..a5e4542666 --- /dev/null +++ b/xen/arch/arm/firmware/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SCMI_SMC) += scmi-smc.o diff --git a/xen/arch/arm/firmware/scmi-smc.c b/xen/arch/arm/firmware/scmi-smc.c new file mode 100644 index 0000000000..33473c04b1 --- /dev/null +++ b/xen/arch/arm/firmware/scmi-smc.c @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * xen/arch/arm/firmware/scmi-smc.c + * + * ARM System Control and Management Interface (SCMI) over SMC + * Generic handling layer + * + * Andrei Cherechesu + * Copyright 2024 NXP + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define SCMI_SMC_ID_PROP "arm,smc-id" + +static bool __ro_after_init scmi_enabled; +static uint32_t __ro_after_init scmi_smc_id; + +/* + * Check if provided SMC Function Identifier matches the one known by the SCMI + * layer, as read from DT prop 'arm,smc-id' during initialiation. + */ +static bool scmi_is_valid_smc_id(uint32_t fid) +{ + return (fid == scmi_smc_id); +} + +/* + * Generic handler for SCMI-SMC requests, currently only forwarding the + * request to FW running at EL3 if it came from the hardware domain. + * Called from the vSMC layer for SiP SMCs, since SCMI calls are usually + * provided this way. + * + * Returns true if SMC was handled (regardless of response), false otherwise. + */ +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; + + /* Only the hardware domain should use SCMI calls */ + if ( !is_hardware_domain(current->domain) ) + { + gdprintk(XENLOG_WARNING, "SCMI: Unprivileged access attempt\n"); + return false; + } + + /* For the moment, forward the SCMI Request to FW running at EL3 */ + 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), + &res); + + set_user_reg(regs, 0, res.a0); + set_user_reg(regs, 1, res.a1); + set_user_reg(regs, 2, res.a2); + set_user_reg(regs, 3, res.a3); + + return true; +} + +static int __init scmi_check_smccc_ver(void) +{ + if ( smccc_ver < ARM_SMCCC_VERSION_1_1 ) + { + printk(XENLOG_WARNING + "SCMI: No SMCCC 1.1 support, SCMI calls forwarding disabled\n"); + return -ENOSYS; + } + + return 0; +} + +static int __init scmi_dt_init_smccc(void) +{ + 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 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; + + ret = dt_property_read_u32(scmi_node, 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, scmi_node->full_name); + return -ENOENT; + } + + scmi_enabled = true; + + return 0; +} + +/* Initialize the SCMI layer based on SMCs and Device-tree */ +static int __init scmi_init(void) +{ + int ret; + + if ( !acpi_disabled ) + { + printk(XENLOG_WARNING "SCMI is not supported when using ACPI\n"); + return -EINVAL; + } + + ret = scmi_check_smccc_ver(); + if ( ret ) + return ret; + + ret = scmi_dt_init_smccc(); + if ( ret == -EOPNOTSUPP ) + return ret; + if ( ret ) + goto err; + + 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); + +/* + * 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/include/asm/firmware/scmi-smc.h b/xen/arch/arm/include/asm/firmware/scmi-smc.h new file mode 100644 index 0000000000..6b1a164a40 --- /dev/null +++ b/xen/arch/arm/include/asm/firmware/scmi-smc.h @@ -0,0 +1,41 @@ +/* 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 + * Copyright 2024 NXP + */ + +#ifndef __ASM_SCMI_SMC_H__ +#define __ASM_SCMI_SMC_H__ + +#include + +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: + */