@@ -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.
@@ -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
@@ -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;
@@ -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;
@@ -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);
@@ -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);
}
@@ -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;
@@ -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;
@@ -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)
@@ -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
@@ -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;
};