diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in index acff45d308..3b18bcc095 100644 --- a/docs/man/xl.cfg.5.pod.in +++ b/docs/man/xl.cfg.5.pod.in @@ -3123,6 +3123,40 @@ writes will be ignored. This option is only implemented for Arm where the default is enabled. +=item B + +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 is a comma separated list of C settings, +from the following list: + +=over 4 + +=item B + +Specifies an ARM SCI type for the guest. + +=over 4 + +=item B + +Don't allow guest to use ARM SCI if present on the platform. This is the +default value. + +=item B + +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 Xen command line +option. + +=back + +=back + =back =head3 x86 diff --git a/docs/misc/arm/device-tree/booting.txt b/docs/misc/arm/device-tree/booting.txt index 07acc7ba64..977b428608 100644 --- a/docs/misc/arm/device-tree/booting.txt +++ b/docs/misc/arm/device-tree/booting.txt @@ -307,6 +307,21 @@ with the following properties: passed through. This option is the default if this property is missing and the user does not provide the device partial device tree for the domain. +- xen,sci_type + + A string property specifying an ARM SCI type for the guest. + + - "none" + Don't allow guest to use ARM SCI if present on the platform. This is the + default value. + + - "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 scmi-smc-passthrough Xen command line + option. + Under the "xen,domain" compatible node, one or more sub-nodes are present for the DomU kernel and ramdisk. diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc index 4adcd7e762..518e42d965 100644 --- a/docs/misc/xen-command-line.pandoc +++ b/docs/misc/xen-command-line.pandoc @@ -2382,6 +2382,15 @@ sockets, &c. This will reduce performance somewhat, particularly on systems with hyperthreading enabled, but should reduce power by enabling more sockets and cores to go into deeper sleep states. +### scmi-smc-passthrough (ARM) +> `= ` + +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). + ### scrub-domheap > `= ` diff --git a/tools/golang/xenlight/helpers.gen.go b/tools/golang/xenlight/helpers.gen.go index 667030cbd7..8909fe8a1b 100644 --- a/tools/golang/xenlight/helpers.gen.go +++ b/tools/golang/xenlight/helpers.gen.go @@ -938,6 +938,35 @@ return fmt.Errorf("converting field Vcpus: %v", err) return nil } +// NewArmSci returns an instance of ArmSci initialized with defaults. +func NewArmSci() (*ArmSci, error) { +var ( +x ArmSci +xc C.libxl_arm_sci) + +C.libxl_arm_sci_init(&xc) +defer C.libxl_arm_sci_dispose(&xc) + +if err := x.fromC(&xc); err != nil { +return nil, err } + +return &x, nil} + +func (x *ArmSci) fromC(xc *C.libxl_arm_sci) error { + x.Type = ArmSciType(xc._type) + + return nil} + +func (x *ArmSci) toC(xc *C.libxl_arm_sci) (err error){defer func(){ +if err != nil{ +C.libxl_arm_sci_dispose(xc)} +}() + +xc._type = C.libxl_arm_sci_type(x.Type) + + return nil + } + // NewRdmReserve returns an instance of RdmReserve initialized with defaults. func NewRdmReserve() (*RdmReserve, error) { var ( @@ -1163,6 +1192,9 @@ x.ArchArm.GicVersion = GicVersion(xc.arch_arm.gic_version) x.ArchArm.Vuart = VuartType(xc.arch_arm.vuart) x.ArchArm.SveVl = SveType(xc.arch_arm.sve_vl) x.ArchArm.NrSpis = uint32(xc.arch_arm.nr_spis) +if err := x.ArchArm.ArmSci.fromC(&xc.arch_arm.arm_sci);err != nil { +return fmt.Errorf("converting field ArchArm.ArmSci: %v", err) +} if err := x.ArchX86.MsrRelaxed.fromC(&xc.arch_x86.msr_relaxed);err != nil { return fmt.Errorf("converting field ArchX86.MsrRelaxed: %v", err) } @@ -1699,6 +1731,9 @@ xc.arch_arm.gic_version = C.libxl_gic_version(x.ArchArm.GicVersion) xc.arch_arm.vuart = C.libxl_vuart_type(x.ArchArm.Vuart) xc.arch_arm.sve_vl = C.libxl_sve_type(x.ArchArm.SveVl) xc.arch_arm.nr_spis = C.uint32_t(x.ArchArm.NrSpis) +if err := x.ArchArm.ArmSci.toC(&xc.arch_arm.arm_sci); err != nil { +return fmt.Errorf("converting field ArchArm.ArmSci: %v", err) +} if err := x.ArchX86.MsrRelaxed.toC(&xc.arch_x86.msr_relaxed); err != nil { return fmt.Errorf("converting field ArchX86.MsrRelaxed: %v", err) } diff --git a/tools/golang/xenlight/types.gen.go b/tools/golang/xenlight/types.gen.go index e26b3cdfc7..ab9d4ca7b4 100644 --- a/tools/golang/xenlight/types.gen.go +++ b/tools/golang/xenlight/types.gen.go @@ -520,6 +520,16 @@ SveType1920 SveType = 1920 SveType2048 SveType = 2048 ) +type ArmSciType int +const( +ArmSciTypeNone ArmSciType = 0 +ArmSciTypeScmiSmc ArmSciType = 1 +) + +type ArmSci struct { +Type ArmSciType +} + type RdmReserve struct { Strategy RdmReserveStrategy Policy RdmReservePolicy @@ -599,6 +609,7 @@ GicVersion GicVersion Vuart VuartType SveVl SveType NrSpis uint32 +ArmSci ArmSci } ArchX86 struct { MsrRelaxed Defbool diff --git a/tools/include/libxl.h b/tools/include/libxl.h index 185f74d8a8..bc35e412da 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 4a19a8d22b..7e9f8a1bc3 100644 --- a/tools/libs/light/libxl_arm.c +++ b/tools/libs/light/libxl_arm.c @@ -233,6 +233,20 @@ 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.arch_arm.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.arch_arm.arm_sci.type); + return ERROR_FAIL; + } + LOG(DEBUG, " - SCI type=%u", config->arch.arm_sci_type); + return 0; } diff --git a/tools/libs/light/libxl_types.idl b/tools/libs/light/libxl_types.idl index a6030a2dbd..b53e013a44 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), @@ -725,6 +734,7 @@ libxl_domain_build_info = Struct("domain_build_info",[ ("vuart", libxl_vuart_type), ("sve_vl", libxl_sve_type), ("nr_spis", uint32, {'init_val': 'LIBXL_NR_SPIS_DEFAULT'}), + ("arm_sci", libxl_arm_sci), ])), ("arch_x86", Struct(None, [("msr_relaxed", libxl_defbool), ])), diff --git a/tools/xl/xl_parse.c b/tools/xl/xl_parse.c index 90c9386f5b..af86d3186d 100644 --- a/tools/xl/xl_parse.c +++ b/tools/xl/xl_parse.c @@ -1284,6 +1284,36 @@ out: if (rc) exit(EXIT_FAILURE); } +static int parse_arm_sci_config(XLU_Config *cfg, libxl_arm_sci *arm_sci, + const char *str) +{ + int ret = 0; + char *buf2, *ptr; + char *oparg; + + if (NULL == (buf2 = ptr = strdup(str))) + return ERROR_NOMEM; + + ptr = strtok(buf2, ","); + while (ptr != NULL) + { + if (MATCH_OPTION("type", ptr, oparg)) { + ret = libxl_arm_sci_type_from_string(oparg, &arm_sci->type); + if (ret) { + fprintf(stderr, "Unknown ARM_SCI type: %s\n", oparg); + ret = ERROR_INVAL; + goto out; + } + } + + ptr = strtok(NULL, ","); + } + +out: + free(buf2); + return ret; +} + void parse_config_data(const char *config_source, const char *config_data, int config_len, @@ -2989,6 +3019,12 @@ skip_usbdev: xlu_cfg_get_defbool(config, "trap_unmapped_accesses", &b_info->trap_unmapped_accesses, 0); + if (!xlu_cfg_get_string(config, "arm_sci", &buf, 1)) { + if (parse_arm_sci_config(config, &b_info->arch_arm.arm_sci, buf)) { + exit(EXIT_FAILURE); + } + } + parse_vkb_list(config, d_config); d_config->virtios = NULL; diff --git a/xen/arch/arm/dom0less-build.c b/xen/arch/arm/dom0less-build.c index 0094cf9e40..f00912a1ca 100644 --- a/xen/arch/arm/dom0less-build.c +++ b/xen/arch/arm/dom0less-build.c @@ -279,6 +279,36 @@ int __init arch_handle_passthrough_prop(struct kernel_info *kinfo, return sci_assign_dt_device(kinfo->bd.d, node); } +static int __init domu_dt_sci_parse(struct dt_device_node *node, + struct xen_domctl_createdomain *d_cfg) +{ + const char *sci_type; + int ret; + + d_cfg->arch.arm_sci_type = XEN_DOMCTL_CONFIG_ARM_SCI_NONE; + + if ( !IS_ENABLED(CONFIG_ARM_SCI) || + !dt_property_read_bool(node, "xen,sci_type") ) + return 0; + + ret = dt_property_read_string(node, "xen,sci_type", &sci_type); + if ( ret ) + return ret; + + if ( !strcmp(sci_type, "none") ) + d_cfg->arch.arm_sci_type = XEN_DOMCTL_CONFIG_ARM_SCI_NONE; + else if ( !strcmp(sci_type, "scmi_smc") ) + d_cfg->arch.arm_sci_type = XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC; + else + { + printk(XENLOG_ERR "xen,sci_type in not valid (%s) for domain %s\n", + sci_type, dt_node_name(node)); + return -EINVAL; + } + + return 0; +} + int __init arch_parse_dom0less_node(struct dt_device_node *node, struct boot_domain *bd) { @@ -288,7 +318,9 @@ 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 ( domu_dt_sci_parse(node, d_cfg) ) + panic("Error getting SCI configuration\n"); if ( !dt_property_read_u32(node, "nr_spis", &d_cfg->arch.nr_spis) ) { diff --git a/xen/arch/arm/firmware/Kconfig b/xen/arch/arm/firmware/Kconfig index bbf88fbb9a..5c5f0880c4 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 4c5df714c2..0835ddeeec 100644 --- a/xen/arch/arm/firmware/scmi-smc.c +++ b/xen/arch/arm/firmware/scmi-smc.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include @@ -22,7 +24,11 @@ #define SCMI_SMC_ID_PROP "arm,smc-id" +static bool __ro_after_init opt_scmi_smc_passthrough; +boolean_param("scmi-smc-passthrough", opt_scmi_smc_passthrough); + static uint32_t __ro_after_init scmi_smc_id; +static struct domain __read_mostly *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; @@ -75,12 +81,45 @@ static bool scmi_handle_smc(struct cpu_user_regs *regs) return true; } +static int +scmi_smc_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 ) + return -EINVAL; + + return 0; +} + static int scmi_smc_domain_init(struct domain *d, struct xen_domctl_createdomain *config) { - if ( !is_hardware_domain(d) ) + /* + * scmi_passthrough is not enabled: + * - proceed only for hw_domain + * - fail if guest domain has SCMI enabled. + */ + if ( !opt_scmi_smc_passthrough && !is_hardware_domain(d) ) + { + if ( config->arch.arm_sci_type == XEN_DOMCTL_CONFIG_ARM_SCI_SCMI_SMC ) + return -EINVAL; + else + return 0; + } + /* + * scmi_passthrough is enabled: + * - ignore hw_domain + * - proceed only for domain with SCMI enabled. + */ + if ( opt_scmi_smc_passthrough && + (config->arch.arm_sci_type == XEN_DOMCTL_CONFIG_ARM_SCI_NONE || + 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 +127,80 @@ 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 + * + * if scmi_smc_passthrough=false: + * - Copy SCMI nodes into Dom0 device tree. + * if scmi_smc_passthrough=true: + * - skip SCMI nodes from Dom0 DT + * - give dom0 control access to SCMI shmem MMIO, so SCMI can be passed + * through to guest. + */ +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 */ }, + }; + + /* skip scmi shmem node for dom0 if scmi not enabled */ + if ( dt_match_node(shmem_matches, node) && !sci_domain_is_enabled(d) ) + { + dt_dprintk("Skip scmi shmem node\n"); + return true; + } + + /* + * skip scmi node for dom0 if scmi not enabled, but give dom0 control + * access to SCMI shmem + */ + if ( dt_match_node(scmi_matches, node) && !sci_domain_is_enabled(d) ) + { + struct dt_device_node *shmem_node; + const __be32 *prop; + uint64_t paddr, size; + int ret; + + /* give dom0 control access to SCMI shmem */ + prop = dt_get_property(node, "shmem", NULL); + if ( !prop ) + return true; + + shmem_node = dt_find_node_by_phandle(be32_to_cpu(*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)); + if ( ret ) + printk(XENLOG_WARNING + "SCMI: Failed to give access to SCMI shmem with code: %d\n", ret); + + dt_dprintk("Skip scmi node\n"); + return true; + } + + return false; +} + static int __init scmi_check_smccc_ver(void) { if ( smccc_ver < ARM_SMCCC_VERSION_1_1 ) @@ -108,8 +215,10 @@ static int __init scmi_check_smccc_ver(void) static const struct sci_mediator_ops scmi_smc_ops = { .handle_call = scmi_handle_smc, + .domain_sanitise_config = scmi_smc_domain_sanitise_config, .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 */