From: Oleksii Moisieiev <oleksii_moisie...@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_moisie...@epam.com> Signed-off-by: Grygorii Strashko <grygorii_stras...@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