@@ -143,7 +143,7 @@ static int __init make_gicv3_domU_node(struct kernel_info *kinfo)
}
#endif
-static int __init make_gic_domU_node(struct kernel_info *kinfo)
+int __init make_intc_domU_node(struct kernel_info *kinfo)
{
switch ( kinfo->d->arch.vgic.version )
{
@@ -209,577 +209,62 @@ static int __init make_vpl011_uart_node(struct kernel_info *kinfo)
}
#endif
-/*
- * Scan device tree properties for passthrough specific information.
- * Returns < 0 on error
- * 0 on success
- */
-static int __init handle_passthrough_prop(struct kernel_info *kinfo,
- const struct fdt_property *xen_reg,
- const struct fdt_property *xen_path,
- bool xen_force,
- uint32_t address_cells,
- uint32_t size_cells)
-{
- const __be32 *cell;
- unsigned int i, len;
- struct dt_device_node *node;
- int res;
- paddr_t mstart, size, gstart;
-
- /* xen,reg specifies where to map the MMIO region */
- cell = (const __be32 *)xen_reg->data;
- len = fdt32_to_cpu(xen_reg->len) / ((address_cells * 2 + size_cells) *
- sizeof(uint32_t));
-
- for ( i = 0; i < len; i++ )
- {
- device_tree_get_reg(&cell, address_cells, size_cells,
- &mstart, &size);
- gstart = dt_next_cell(address_cells, &cell);
-
- if ( gstart & ~PAGE_MASK || mstart & ~PAGE_MASK || size & ~PAGE_MASK )
- {
- printk(XENLOG_ERR
- "DomU passthrough config has not page aligned addresses/sizes\n");
- return -EINVAL;
- }
-
- res = iomem_permit_access(kinfo->d, paddr_to_pfn(mstart),
- paddr_to_pfn(PAGE_ALIGN(mstart + size - 1)));
- if ( res )
- {
- printk(XENLOG_ERR "Unable to permit to dom%d access to"
- " 0x%"PRIpaddr" - 0x%"PRIpaddr"\n",
- kinfo->d->domain_id,
- mstart & PAGE_MASK, PAGE_ALIGN(mstart + size) - 1);
- return res;
- }
-
- res = map_regions_p2mt(kinfo->d,
- gaddr_to_gfn(gstart),
- PFN_DOWN(size),
- maddr_to_mfn(mstart),
- p2m_mmio_direct_dev);
- if ( res < 0 )
- {
- printk(XENLOG_ERR
- "Failed to map %"PRIpaddr" to the guest at%"PRIpaddr"\n",
- mstart, gstart);
- return -EFAULT;
- }
- }
-
- /*
- * If xen_force, we let the user assign a MMIO region with no
- * associated path.
- */
- if ( xen_path == NULL )
- return xen_force ? 0 : -EINVAL;
-
- /*
- * xen,path specifies the corresponding node in the host DT.
- * Both interrupt mappings and IOMMU settings are based on it,
- * as they are done based on the corresponding host DT node.
- */
- node = dt_find_node_by_path(xen_path->data);
- if ( node == NULL )
- {
- printk(XENLOG_ERR "Couldn't find node %s in host_dt!\n",
- xen_path->data);
- return -EINVAL;
- }
-
- res = map_device_irqs_to_domain(kinfo->d, node, true, NULL);
- if ( res < 0 )
- return res;
-
- res = iommu_add_dt_device(node);
- if ( res < 0 )
- return res;
-
- /* If xen_force, we allow assignment of devices without IOMMU protection. */
- if ( xen_force && !dt_device_is_protected(node) )
- return 0;
-
- return iommu_assign_dt_device(kinfo->d, node);
-}
-
-static int __init handle_prop_pfdt(struct kernel_info *kinfo,
- const void *pfdt, int nodeoff,
- uint32_t address_cells, uint32_t size_cells,
- bool scan_passthrough_prop)
-{
- void *fdt = kinfo->fdt;
- int propoff, nameoff, res;
- const struct fdt_property *prop, *xen_reg = NULL, *xen_path = NULL;
- const char *name;
- bool found, xen_force = false;
-
- for ( propoff = fdt_first_property_offset(pfdt, nodeoff);
- propoff >= 0;
- propoff = fdt_next_property_offset(pfdt, propoff) )
- {
- if ( !(prop = fdt_get_property_by_offset(pfdt, propoff, NULL)) )
- return -FDT_ERR_INTERNAL;
-
- found = false;
- nameoff = fdt32_to_cpu(prop->nameoff);
- name = fdt_string(pfdt, nameoff);
-
- if ( scan_passthrough_prop )
- {
- if ( dt_prop_cmp("xen,reg", name) == 0 )
- {
- xen_reg = prop;
- found = true;
- }
- else if ( dt_prop_cmp("xen,path", name) == 0 )
- {
- xen_path = prop;
- found = true;
- }
- else if ( dt_prop_cmp("xen,force-assign-without-iommu",
- name) == 0 )
- {
- xen_force = true;
- found = true;
- }
- }
-
- /*
- * Copy properties other than the ones above: xen,reg, xen,path,
- * and xen,force-assign-without-iommu.
- */
- if ( !found )
- {
- res = fdt_property(fdt, name, prop->data, fdt32_to_cpu(prop->len));
- if ( res )
- return res;
- }
- }
-
- /*
- * Only handle passthrough properties if both xen,reg and xen,path
- * are present, or if xen,force-assign-without-iommu is specified.
- */
- if ( xen_reg != NULL && (xen_path != NULL || xen_force) )
- {
- res = handle_passthrough_prop(kinfo, xen_reg, xen_path, xen_force,
- address_cells, size_cells);
- if ( res < 0 )
- {
- printk(XENLOG_ERR "Failed to assign device to %pd\n", kinfo->d);
- return res;
- }
- }
- else if ( (xen_path && !xen_reg) || (xen_reg && !xen_path && !xen_force) )
- {
- printk(XENLOG_ERR "xen,reg or xen,path missing for %pd\n",
- kinfo->d);
- return -EINVAL;
- }
-
- /* FDT_ERR_NOTFOUND => There is no more properties for this node */
- return ( propoff != -FDT_ERR_NOTFOUND ) ? propoff : 0;
-}
-
-static int __init scan_pfdt_node(struct kernel_info *kinfo, const void *pfdt,
- int nodeoff,
- uint32_t address_cells, uint32_t size_cells,
- bool scan_passthrough_prop)
-{
- int rc = 0;
- void *fdt = kinfo->fdt;
- int node_next;
-
- rc = fdt_begin_node(fdt, fdt_get_name(pfdt, nodeoff, NULL));
- if ( rc )
- return rc;
-
- rc = handle_prop_pfdt(kinfo, pfdt, nodeoff, address_cells, size_cells,
- scan_passthrough_prop);
- if ( rc )
- return rc;
-
- address_cells = device_tree_get_u32(pfdt, nodeoff, "#address-cells",
- DT_ROOT_NODE_ADDR_CELLS_DEFAULT);
- size_cells = device_tree_get_u32(pfdt, nodeoff, "#size-cells",
- DT_ROOT_NODE_SIZE_CELLS_DEFAULT);
-
- node_next = fdt_first_subnode(pfdt, nodeoff);
- while ( node_next > 0 )
- {
- rc = scan_pfdt_node(kinfo, pfdt, node_next, address_cells, size_cells,
- scan_passthrough_prop);
- if ( rc )
- return rc;
-
- node_next = fdt_next_subnode(pfdt, node_next);
- }
-
- return fdt_end_node(fdt);
-}
-
-static int __init check_partial_fdt(void *pfdt, size_t size)
-{
- int res;
-
- if ( fdt_magic(pfdt) != FDT_MAGIC )
- {
- dprintk(XENLOG_ERR, "Partial FDT is not a valid Flat Device Tree");
- return -EINVAL;
- }
-
- res = fdt_check_header(pfdt);
- if ( res )
- {
- dprintk(XENLOG_ERR, "Failed to check the partial FDT (%d)", res);
- return -EINVAL;
- }
-
- if ( fdt_totalsize(pfdt) > size )
- {
- dprintk(XENLOG_ERR, "Partial FDT totalsize is too big");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int __init domain_handle_dtb_bootmodule(struct domain *d,
- struct kernel_info *kinfo)
+int __init make_arch_nodes(struct kernel_info *kinfo)
{
- void *pfdt;
- int res, node_next;
-
- pfdt = ioremap_cache(kinfo->dtb_bootmodule->start,
- kinfo->dtb_bootmodule->size);
- if ( pfdt == NULL )
- return -EFAULT;
-
- res = check_partial_fdt(pfdt, kinfo->dtb_bootmodule->size);
- if ( res < 0 )
- goto out;
-
- for ( node_next = fdt_first_subnode(pfdt, 0);
- node_next > 0;
- node_next = fdt_next_subnode(pfdt, node_next) )
- {
- const char *name = fdt_get_name(pfdt, node_next, NULL);
-
- if ( name == NULL )
- continue;
-
- /*
- * Only scan /gic /aliases /passthrough, ignore the rest.
- * They don't have to be parsed in order.
- *
- * Take the GIC phandle value from the special /gic node in the
- * DTB fragment.
- */
- if ( dt_node_cmp(name, "gic") == 0 )
- {
- uint32_t phandle_intc = fdt_get_phandle(pfdt, node_next);
-
- if ( phandle_intc != 0 )
- kinfo->phandle_intc = phandle_intc;
- continue;
- }
-
- if ( dt_node_cmp(name, "aliases") == 0 )
- {
- res = scan_pfdt_node(kinfo, pfdt, node_next,
- DT_ROOT_NODE_ADDR_CELLS_DEFAULT,
- DT_ROOT_NODE_SIZE_CELLS_DEFAULT,
- false);
- if ( res )
- goto out;
- continue;
- }
- if ( dt_node_cmp(name, "passthrough") == 0 )
- {
- res = scan_pfdt_node(kinfo, pfdt, node_next,
- DT_ROOT_NODE_ADDR_CELLS_DEFAULT,
- DT_ROOT_NODE_SIZE_CELLS_DEFAULT,
- true);
- if ( res )
- goto out;
- continue;
- }
- }
-
- out:
- iounmap(pfdt);
-
- return res;
-}
-
-/*
- * The max size for DT is 2MB. However, the generated DT is small (not including
- * domU passthrough DT nodes whose size we account separately), 4KB are enough
- * for now, but we might have to increase it in the future.
- */
-#define DOMU_DTB_SIZE 4096
-static int __init prepare_dtb_domU(struct domain *d, struct kernel_info *kinfo)
-{
- int addrcells, sizecells;
- int ret, fdt_size = DOMU_DTB_SIZE;
-
- kinfo->phandle_intc = GUEST_PHANDLE_GIC;
- kinfo->gnttab_start = GUEST_GNTTAB_BASE;
- kinfo->gnttab_size = GUEST_GNTTAB_SIZE;
-
- addrcells = GUEST_ROOT_ADDRESS_CELLS;
- sizecells = GUEST_ROOT_SIZE_CELLS;
-
- /* Account for domU passthrough DT size */
- if ( kinfo->dtb_bootmodule )
- fdt_size += kinfo->dtb_bootmodule->size;
-
- /* Cap to max DT size if needed */
- fdt_size = min(fdt_size, SZ_2M);
-
- kinfo->fdt = xmalloc_bytes(fdt_size);
- if ( kinfo->fdt == NULL )
- return -ENOMEM;
-
- ret = fdt_create(kinfo->fdt, fdt_size);
- if ( ret < 0 )
- goto err;
-
- ret = fdt_finish_reservemap(kinfo->fdt);
- if ( ret < 0 )
- goto err;
-
- ret = fdt_begin_node(kinfo->fdt, "");
- if ( ret < 0 )
- goto err;
-
- ret = fdt_property_cell(kinfo->fdt, "#address-cells", addrcells);
- if ( ret )
- goto err;
-
- ret = fdt_property_cell(kinfo->fdt, "#size-cells", sizecells);
- if ( ret )
- goto err;
-
- ret = make_chosen_node(kinfo);
- if ( ret )
- goto err;
+ int ret;
ret = make_psci_node(kinfo->fdt);
if ( ret )
- goto err;
-
- ret = make_cpus_node(d, kinfo->fdt);
- if ( ret )
- goto err;
-
- ret = make_memory_node(kinfo, addrcells, sizecells,
- kernel_info_get_mem(kinfo));
- if ( ret )
- goto err;
-
- ret = make_resv_memory_node(kinfo, addrcells, sizecells);
- if ( ret )
- goto err;
-
- /*
- * domain_handle_dtb_bootmodule has to be called before the rest of
- * the device tree is generated because it depends on the value of
- * the field phandle_intc.
- */
- if ( kinfo->dtb_bootmodule )
- {
- ret = domain_handle_dtb_bootmodule(d, kinfo);
- if ( ret )
- goto err;
- }
-
- ret = make_gic_domU_node(kinfo);
- if ( ret )
- goto err;
-
- ret = make_timer_node(kinfo);
- if ( ret )
- goto err;
+ return -EINVAL;
if ( kinfo->vuart )
{
- ret = -EINVAL;
#ifdef CONFIG_SBSA_VUART_CONSOLE
ret = make_vpl011_uart_node(kinfo);
#endif
if ( ret )
- goto err;
- }
-
- if ( kinfo->dom0less_feature & DOM0LESS_ENHANCED_NO_XS )
- {
- ret = make_hypervisor_node(d, kinfo, addrcells, sizecells);
- if ( ret )
- goto err;
+ return -EINVAL;
}
- ret = fdt_end_node(kinfo->fdt);
- if ( ret < 0 )
- goto err;
-
- ret = fdt_finish(kinfo->fdt);
- if ( ret < 0 )
- goto err;
-
return 0;
-
- err:
- printk("Device tree generation failed (%d).\n", ret);
- xfree(kinfo->fdt);
-
- return -EINVAL;
}
-static unsigned long __init domain_p2m_pages(unsigned long maxmem_kb,
- unsigned int smp_cpus)
+/* TODO: make arch.type generic ? */
+#ifdef CONFIG_ARM_64
+void __init set_domain_type(struct domain *d, struct kernel_info *kinfo)
{
- /*
- * Keep in sync with libxl__get_required_paging_memory().
- * 256 pages (1MB) per vcpu, plus 1 page per MiB of RAM for the P2M map,
- * plus 128 pages to cover extended regions.
- */
- unsigned long memkb = 4 * (256 * smp_cpus + (maxmem_kb / 1024) + 128);
-
- BUILD_BUG_ON(PAGE_SIZE != SZ_4K);
-
- return DIV_ROUND_UP(memkb, 1024) << (20 - PAGE_SHIFT);
+ /* type must be set before allocate memory */
+ d->arch.type = kinfo->arch.type;
}
-
-static int __init alloc_xenstore_evtchn(struct domain *d)
+#else
+void __init set_domain_type(struct domain *d, struct kernel_info *kinfo)
{
- evtchn_alloc_unbound_t alloc;
- int rc;
-
- alloc.dom = d->domain_id;
- alloc.remote_dom = hardware_domain->domain_id;
- rc = evtchn_alloc_unbound(&alloc, 0);
- if ( rc )
- {
- printk("Failed allocating event channel for domain\n");
- return rc;
- }
-
- d->arch.hvm.params[HVM_PARAM_STORE_EVTCHN] = alloc.port;
-
- return 0;
+ /* Nothing to do */
}
+#endif
-int __init construct_domU(struct domain *d,
- const struct dt_device_node *node)
+int __init init_vuart(struct domain *d, struct kernel_info *kinfo,
+ const struct dt_device_node *node)
{
- struct kernel_info kinfo = KERNEL_INFO_INIT;
- const char *dom0less_enhanced;
- int rc;
- u64 mem;
- u32 p2m_mem_mb;
- unsigned long p2m_pages;
-
- rc = dt_property_read_u64(node, "memory", &mem);
- if ( !rc )
- {
- printk("Error building DomU: cannot read \"memory\" property\n");
- return -EINVAL;
- }
- kinfo.unassigned_mem = (paddr_t)mem * SZ_1K;
-
- rc = dt_property_read_u32(node, "xen,domain-p2m-mem-mb", &p2m_mem_mb);
- /* If xen,domain-p2m-mem-mb is not specified, use the default value. */
- p2m_pages = rc ?
- p2m_mem_mb << (20 - PAGE_SHIFT) :
- domain_p2m_pages(mem, d->max_vcpus);
-
- spin_lock(&d->arch.paging.lock);
- rc = p2m_set_allocation(d, p2m_pages, NULL);
- spin_unlock(&d->arch.paging.lock);
- if ( rc != 0 )
- return rc;
-
- printk("*** LOADING DOMU cpus=%u memory=%#"PRIx64"KB ***\n",
- d->max_vcpus, mem);
-
- kinfo.vuart = dt_property_read_bool(node, "vpl011");
-
- rc = dt_property_read_string(node, "xen,enhanced", &dom0less_enhanced);
- if ( rc == -EILSEQ ||
- rc == -ENODATA ||
- (rc == 0 && !strcmp(dom0less_enhanced, "enabled")) )
- {
- if ( hardware_domain )
- kinfo.dom0less_feature = DOM0LESS_ENHANCED;
- else
- panic("At the moment, Xenstore support requires dom0 to be present\n");
- }
- else if ( rc == 0 && !strcmp(dom0less_enhanced, "no-xenstore") )
- kinfo.dom0less_feature = DOM0LESS_ENHANCED_NO_XS;
-
- if ( vcpu_create(d, 0) == NULL )
- return -ENOMEM;
-
- d->max_pages = ((paddr_t)mem * SZ_1K) >> PAGE_SHIFT;
-
- kinfo.d = d;
-
- rc = kernel_probe(&kinfo, node);
- if ( rc < 0 )
- return rc;
-
-#ifdef CONFIG_ARM_64
- /* type must be set before allocate memory */
- d->arch.type = kinfo.arch.type;
-#endif
- if ( !dt_find_property(node, "xen,static-mem", NULL) )
- allocate_memory(d, &kinfo);
- else if ( !is_domain_direct_mapped(d) )
- allocate_static_memory(d, &kinfo, node);
- else
- assign_static_memory_11(d, &kinfo, node);
+ int rc = 0;
- rc = process_shm(d, &kinfo, node);
- if ( rc < 0 )
- return rc;
+ kinfo->vuart = dt_property_read_bool(node, "vpl011");
/*
* Base address and irq number are needed when creating vpl011 device
* tree node in prepare_dtb_domU, so initialization on related variables
* shall be done first.
*/
- if ( kinfo.vuart )
+ if ( kinfo->vuart )
{
rc = domain_vpl011_init(d, NULL);
if ( rc < 0 )
return rc;
}
- rc = prepare_dtb_domU(d, &kinfo);
- if ( rc < 0 )
- return rc;
-
- rc = construct_domain(d, &kinfo);
- if ( rc < 0 )
- return rc;
-
- if ( kinfo.dom0less_feature & DOM0LESS_XENSTORE )
- {
- ASSERT(hardware_domain);
- rc = alloc_xenstore_evtchn(d);
- if ( rc < 0 )
- return rc;
- d->arch.hvm.params[HVM_PARAM_STORE_PFN] = ~0ULL;
- }
-
return rc;
}
-
struct xen_domctl_createdomain __init arch_xen_domctl_createdomain(void)
{
struct xen_domctl_createdomain d_cfg = {
@@ -868,6 +353,22 @@ void __init arch_create_domus(struct dt_device_node *node,
}
}
+int __init init_intc_phandle(struct kernel_info *kinfo, const char *name,
+ const int node_next, const void *pfdt)
+{
+ if ( dt_node_cmp(name, "gic") == 0 )
+ {
+ uint32_t phandle_intc = fdt_get_phandle(pfdt, node_next);
+
+ if ( phandle_intc != 0 )
+ kinfo->phandle_intc = phandle_intc;
+
+ return 0;
+ }
+
+ return 1;
+}
+
/*
* Local variables:
* mode: C
@@ -3,17 +3,26 @@
#include <xen/device_tree.h>
#include <xen/domain.h>
#include <xen/err.h>
+#include <xen/event.h>
#include <xen/init.h>
+#include <xen/iocap.h>
#include <xen/iommu.h>
+#include <xen/libfdt/libfdt.h>
#include <xen/llc-coloring.h>
+#include <xen/sizes.h>
#include <xen/sched.h>
#include <xen/stdbool.h>
#include <xen/types.h>
+#include <xen/vmap.h>
#include <public/domctl.h>
+#include <asm/domain-build.h>
#include <asm/dom0less-build.h>
+#include <asm/kernel.h>
#include <asm/setup.h>
+#include <asm/static-memory.h>
+#include <asm/static-shmem.h>
bool __init is_dom0less_mode(void)
{
@@ -159,3 +168,553 @@ void __init create_domUs(void)
dt_node_name(node), rc);
}
}
+
+/*
+ * Scan device tree properties for passthrough specific information.
+ * Returns < 0 on error
+ * 0 on success
+ */
+static int __init handle_passthrough_prop(struct kernel_info *kinfo,
+ const struct fdt_property *xen_reg,
+ const struct fdt_property *xen_path,
+ bool xen_force,
+ uint32_t address_cells,
+ uint32_t size_cells)
+{
+ const __be32 *cell;
+ unsigned int i, len;
+ struct dt_device_node *node;
+ int res;
+ paddr_t mstart, size, gstart;
+
+ /* xen,reg specifies where to map the MMIO region */
+ cell = (const __be32 *)xen_reg->data;
+ len = fdt32_to_cpu(xen_reg->len) / ((address_cells * 2 + size_cells) *
+ sizeof(uint32_t));
+
+ for ( i = 0; i < len; i++ )
+ {
+ device_tree_get_reg(&cell, address_cells, size_cells,
+ &mstart, &size);
+ gstart = dt_next_cell(address_cells, &cell);
+
+ if ( gstart & ~PAGE_MASK || mstart & ~PAGE_MASK || size & ~PAGE_MASK )
+ {
+ printk(XENLOG_ERR
+ "DomU passthrough config has not page aligned addresses/sizes\n");
+ return -EINVAL;
+ }
+
+ res = iomem_permit_access(kinfo->d, paddr_to_pfn(mstart),
+ paddr_to_pfn(PAGE_ALIGN(mstart + size - 1)));
+ if ( res )
+ {
+ printk(XENLOG_ERR "Unable to permit to dom%d access to"
+ " 0x%"PRIpaddr" - 0x%"PRIpaddr"\n",
+ kinfo->d->domain_id,
+ mstart & PAGE_MASK, PAGE_ALIGN(mstart + size) - 1);
+ return res;
+ }
+
+ res = map_regions_p2mt(kinfo->d,
+ gaddr_to_gfn(gstart),
+ PFN_DOWN(size),
+ maddr_to_mfn(mstart),
+ p2m_mmio_direct_dev);
+ if ( res < 0 )
+ {
+ printk(XENLOG_ERR
+ "Failed to map %"PRIpaddr" to the guest at%"PRIpaddr"\n",
+ mstart, gstart);
+ return -EFAULT;
+ }
+ }
+
+ /*
+ * If xen_force, we let the user assign a MMIO region with no
+ * associated path.
+ */
+ if ( xen_path == NULL )
+ return xen_force ? 0 : -EINVAL;
+
+ /*
+ * xen,path specifies the corresponding node in the host DT.
+ * Both interrupt mappings and IOMMU settings are based on it,
+ * as they are done based on the corresponding host DT node.
+ */
+ node = dt_find_node_by_path(xen_path->data);
+ if ( node == NULL )
+ {
+ printk(XENLOG_ERR "Couldn't find node %s in host_dt!\n",
+ xen_path->data);
+ return -EINVAL;
+ }
+
+ res = map_device_irqs_to_domain(kinfo->d, node, true, NULL);
+ if ( res < 0 )
+ return res;
+
+ res = iommu_add_dt_device(node);
+ if ( res < 0 )
+ return res;
+
+ /* If xen_force, we allow assignment of devices without IOMMU protection. */
+ if ( xen_force && !dt_device_is_protected(node) )
+ return 0;
+
+ return iommu_assign_dt_device(kinfo->d, node);
+}
+
+static int __init handle_prop_pfdt(struct kernel_info *kinfo,
+ const void *pfdt, int nodeoff,
+ uint32_t address_cells, uint32_t size_cells,
+ bool scan_passthrough_prop)
+{
+ void *fdt = kinfo->fdt;
+ int propoff, nameoff, res;
+ const struct fdt_property *prop, *xen_reg = NULL, *xen_path = NULL;
+ const char *name;
+ bool found, xen_force = false;
+
+ for ( propoff = fdt_first_property_offset(pfdt, nodeoff);
+ propoff >= 0;
+ propoff = fdt_next_property_offset(pfdt, propoff) )
+ {
+ if ( !(prop = fdt_get_property_by_offset(pfdt, propoff, NULL)) )
+ return -FDT_ERR_INTERNAL;
+
+ found = false;
+ nameoff = fdt32_to_cpu(prop->nameoff);
+ name = fdt_string(pfdt, nameoff);
+
+ if ( scan_passthrough_prop )
+ {
+ if ( dt_prop_cmp("xen,reg", name) == 0 )
+ {
+ xen_reg = prop;
+ found = true;
+ }
+ else if ( dt_prop_cmp("xen,path", name) == 0 )
+ {
+ xen_path = prop;
+ found = true;
+ }
+ else if ( dt_prop_cmp("xen,force-assign-without-iommu",
+ name) == 0 )
+ {
+ xen_force = true;
+ found = true;
+ }
+ }
+
+ /*
+ * Copy properties other than the ones above: xen,reg, xen,path,
+ * and xen,force-assign-without-iommu.
+ */
+ if ( !found )
+ {
+ res = fdt_property(fdt, name, prop->data, fdt32_to_cpu(prop->len));
+ if ( res )
+ return res;
+ }
+ }
+
+ /*
+ * Only handle passthrough properties if both xen,reg and xen,path
+ * are present, or if xen,force-assign-without-iommu is specified.
+ */
+ if ( xen_reg != NULL && (xen_path != NULL || xen_force) )
+ {
+ res = handle_passthrough_prop(kinfo, xen_reg, xen_path, xen_force,
+ address_cells, size_cells);
+ if ( res < 0 )
+ {
+ printk(XENLOG_ERR "Failed to assign device to %pd\n", kinfo->d);
+ return res;
+ }
+ }
+ else if ( (xen_path && !xen_reg) || (xen_reg && !xen_path && !xen_force) )
+ {
+ printk(XENLOG_ERR "xen,reg or xen,path missing for %pd\n",
+ kinfo->d);
+ return -EINVAL;
+ }
+
+ /* FDT_ERR_NOTFOUND => There is no more properties for this node */
+ return ( propoff != -FDT_ERR_NOTFOUND ) ? propoff : 0;
+}
+
+static int __init scan_pfdt_node(struct kernel_info *kinfo, const void *pfdt,
+ int nodeoff,
+ uint32_t address_cells, uint32_t size_cells,
+ bool scan_passthrough_prop)
+{
+ int rc = 0;
+ void *fdt = kinfo->fdt;
+ int node_next;
+
+ rc = fdt_begin_node(fdt, fdt_get_name(pfdt, nodeoff, NULL));
+ if ( rc )
+ return rc;
+
+ rc = handle_prop_pfdt(kinfo, pfdt, nodeoff, address_cells, size_cells,
+ scan_passthrough_prop);
+ if ( rc )
+ return rc;
+
+ address_cells = device_tree_get_u32(pfdt, nodeoff, "#address-cells",
+ DT_ROOT_NODE_ADDR_CELLS_DEFAULT);
+ size_cells = device_tree_get_u32(pfdt, nodeoff, "#size-cells",
+ DT_ROOT_NODE_SIZE_CELLS_DEFAULT);
+
+ node_next = fdt_first_subnode(pfdt, nodeoff);
+ while ( node_next > 0 )
+ {
+ rc = scan_pfdt_node(kinfo, pfdt, node_next, address_cells, size_cells,
+ scan_passthrough_prop);
+ if ( rc )
+ return rc;
+
+ node_next = fdt_next_subnode(pfdt, node_next);
+ }
+
+ return fdt_end_node(fdt);
+}
+
+static int __init check_partial_fdt(void *pfdt, size_t size)
+{
+ int res;
+
+ if ( fdt_magic(pfdt) != FDT_MAGIC )
+ {
+ dprintk(XENLOG_ERR, "Partial FDT is not a valid Flat Device Tree");
+ return -EINVAL;
+ }
+
+ res = fdt_check_header(pfdt);
+ if ( res )
+ {
+ dprintk(XENLOG_ERR, "Failed to check the partial FDT (%d)", res);
+ return -EINVAL;
+ }
+
+ if ( fdt_totalsize(pfdt) > size )
+ {
+ dprintk(XENLOG_ERR, "Partial FDT totalsize is too big");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __init domain_handle_dtb_bootmodule(struct domain *d,
+ struct kernel_info *kinfo)
+{
+ void *pfdt;
+ int res, node_next;
+
+ pfdt = ioremap_cache(kinfo->dtb_bootmodule->start,
+ kinfo->dtb_bootmodule->size);
+ if ( pfdt == NULL )
+ return -EFAULT;
+
+ res = check_partial_fdt(pfdt, kinfo->dtb_bootmodule->size);
+ if ( res < 0 )
+ goto out;
+
+ for ( node_next = fdt_first_subnode(pfdt, 0);
+ node_next > 0;
+ node_next = fdt_next_subnode(pfdt, node_next) )
+ {
+ const char *name = fdt_get_name(pfdt, node_next, NULL);
+
+ if ( name == NULL )
+ continue;
+
+ /*
+ * Only scan /$(interrupt_controller) /aliases /passthrough,
+ * ignore the rest.
+ * They don't have to be parsed in order.
+ *
+ * Take the interrupt controller phandle value from the special
+ * interrupt controller node in the DTB fragment.
+ */
+ if ( init_intc_phandle(kinfo, name, node_next, pfdt) == 0 )
+ continue;
+
+ if ( dt_node_cmp(name, "aliases") == 0 )
+ {
+ res = scan_pfdt_node(kinfo, pfdt, node_next,
+ DT_ROOT_NODE_ADDR_CELLS_DEFAULT,
+ DT_ROOT_NODE_SIZE_CELLS_DEFAULT,
+ false);
+ if ( res )
+ goto out;
+ continue;
+ }
+ if ( dt_node_cmp(name, "passthrough") == 0 )
+ {
+ res = scan_pfdt_node(kinfo, pfdt, node_next,
+ DT_ROOT_NODE_ADDR_CELLS_DEFAULT,
+ DT_ROOT_NODE_SIZE_CELLS_DEFAULT,
+ true);
+ if ( res )
+ goto out;
+ continue;
+ }
+ }
+
+ out:
+ iounmap(pfdt);
+
+ return res;
+}
+
+static unsigned long __init domain_p2m_pages(unsigned long maxmem_kb,
+ unsigned int smp_cpus)
+{
+ /*
+ * Keep in sync with libxl__get_required_paging_memory().
+ * 256 pages (1MB) per vcpu, plus 1 page per MiB of RAM for the P2M map,
+ * plus 128 pages to cover extended regions.
+ */
+ unsigned long memkb = 4 * (256 * smp_cpus + (maxmem_kb / 1024) + 128);
+
+ BUILD_BUG_ON(PAGE_SIZE != SZ_4K);
+
+ return DIV_ROUND_UP(memkb, 1024) << (20 - PAGE_SHIFT);
+}
+
+/*
+ * The max size for DT is 2MB. However, the generated DT is small (not including
+ * domU passthrough DT nodes whose size we account separately), 4KB are enough
+ * for now, but we might have to increase it in the future.
+ */
+#define DOMU_DTB_SIZE 4096
+static int __init prepare_dtb_domU(struct domain *d, struct kernel_info *kinfo)
+{
+ int addrcells, sizecells;
+ int ret, fdt_size = DOMU_DTB_SIZE;
+
+ kinfo->phandle_intc = GUEST_PHANDLE_GIC;
+
+#ifdef CONFIG_GRANT_TABLE
+ kinfo->gnttab_start = GUEST_GNTTAB_BASE;
+ kinfo->gnttab_size = GUEST_GNTTAB_SIZE;
+#endif
+
+ addrcells = GUEST_ROOT_ADDRESS_CELLS;
+ sizecells = GUEST_ROOT_SIZE_CELLS;
+
+ /* Account for domU passthrough DT size */
+ if ( kinfo->dtb_bootmodule )
+ fdt_size += kinfo->dtb_bootmodule->size;
+
+ /* Cap to max DT size if needed */
+ fdt_size = min(fdt_size, SZ_2M);
+
+ kinfo->fdt = xmalloc_bytes(fdt_size);
+ if ( kinfo->fdt == NULL )
+ return -ENOMEM;
+
+ ret = fdt_create(kinfo->fdt, fdt_size);
+ if ( ret < 0 )
+ goto err;
+
+ ret = fdt_finish_reservemap(kinfo->fdt);
+ if ( ret < 0 )
+ goto err;
+
+ ret = fdt_begin_node(kinfo->fdt, "");
+ if ( ret < 0 )
+ goto err;
+
+ ret = fdt_property_cell(kinfo->fdt, "#address-cells", addrcells);
+ if ( ret )
+ goto err;
+
+ ret = fdt_property_cell(kinfo->fdt, "#size-cells", sizecells);
+ if ( ret )
+ goto err;
+
+ ret = make_chosen_node(kinfo);
+ if ( ret )
+ goto err;
+
+ ret = make_cpus_node(d, kinfo->fdt);
+ if ( ret )
+ goto err;
+
+ ret = make_memory_node(kinfo, addrcells, sizecells,
+ kernel_info_get_mem(kinfo));
+ if ( ret )
+ goto err;
+
+ ret = make_resv_memory_node(kinfo, addrcells, sizecells);
+ if ( ret )
+ goto err;
+
+ /*
+ * domain_handle_dtb_bootmodule has to be called before the rest of
+ * the device tree is generated because it depends on the value of
+ * the field phandle_intc.
+ */
+ if ( kinfo->dtb_bootmodule )
+ {
+ ret = domain_handle_dtb_bootmodule(d, kinfo);
+ if ( ret )
+ goto err;
+ }
+
+ ret = make_intc_domU_node(kinfo);
+ if ( ret )
+ goto err;
+
+ ret = make_timer_node(kinfo);
+ if ( ret )
+ goto err;
+
+ if ( kinfo->dom0less_feature & DOM0LESS_ENHANCED_NO_XS )
+ {
+ ret = make_hypervisor_node(d, kinfo, addrcells, sizecells);
+ if ( ret )
+ goto err;
+ }
+
+ ret = make_arch_nodes(kinfo);
+ if ( ret )
+ goto err;
+
+ ret = fdt_end_node(kinfo->fdt);
+ if ( ret < 0 )
+ goto err;
+
+ ret = fdt_finish(kinfo->fdt);
+ if ( ret < 0 )
+ goto err;
+
+ return 0;
+
+ err:
+ printk("Device tree generation failed (%d).\n", ret);
+ xfree(kinfo->fdt);
+
+ return -EINVAL;
+}
+
+static int __init alloc_xenstore_evtchn(struct domain *d)
+{
+ evtchn_alloc_unbound_t alloc;
+ int rc;
+
+ alloc.dom = d->domain_id;
+ alloc.remote_dom = hardware_domain->domain_id;
+ rc = evtchn_alloc_unbound(&alloc, 0);
+ if ( rc )
+ {
+ printk("Failed allocating event channel for domain\n");
+ return rc;
+ }
+
+#ifdef CONFIG_HVM
+ d->arch.hvm.params[HVM_PARAM_STORE_EVTCHN] = alloc.port;
+#endif
+
+ return 0;
+}
+
+int __init construct_domU(struct domain *d,
+ const struct dt_device_node *node)
+{
+ struct kernel_info kinfo = KERNEL_INFO_INIT;
+ const char *dom0less_enhanced;
+ int rc;
+ u64 mem;
+ u32 p2m_mem_mb;
+ unsigned long p2m_pages;
+
+ rc = dt_property_read_u64(node, "memory", &mem);
+ if ( !rc )
+ {
+ printk("Error building DomU: cannot read \"memory\" property\n");
+ return -EINVAL;
+ }
+ kinfo.unassigned_mem = (paddr_t)mem * SZ_1K;
+
+ rc = dt_property_read_u32(node, "xen,domain-p2m-mem-mb", &p2m_mem_mb);
+ /* If xen,domain-p2m-mem-mb is not specified, use the default value. */
+ p2m_pages = rc ?
+ p2m_mem_mb << (20 - PAGE_SHIFT) :
+ domain_p2m_pages(mem, d->max_vcpus);
+
+ spin_lock(&d->arch.paging.lock);
+ rc = p2m_set_allocation(d, p2m_pages, NULL);
+ spin_unlock(&d->arch.paging.lock);
+ if ( rc != 0 )
+ return rc;
+
+ printk("*** LOADING DOMU cpus=%u memory=%#"PRIx64"KB ***\n",
+ d->max_vcpus, mem);
+
+ rc = dt_property_read_string(node, "xen,enhanced", &dom0less_enhanced);
+ if ( rc == -EILSEQ ||
+ rc == -ENODATA ||
+ (rc == 0 && !strcmp(dom0less_enhanced, "enabled")) )
+ {
+ if ( hardware_domain )
+ kinfo.dom0less_feature = DOM0LESS_ENHANCED;
+ else
+ panic("At the moment, Xenstore support requires dom0 to be present\n");
+ }
+ else if ( rc == 0 && !strcmp(dom0less_enhanced, "no-xenstore") )
+ kinfo.dom0less_feature = DOM0LESS_ENHANCED_NO_XS;
+
+ if ( vcpu_create(d, 0) == NULL )
+ return -ENOMEM;
+
+ d->max_pages = ((paddr_t)mem * SZ_1K) >> PAGE_SHIFT;
+
+ kinfo.d = d;
+
+ rc = kernel_probe(&kinfo, node);
+ if ( rc < 0 )
+ return rc;
+
+ set_domain_type(d, &kinfo);
+
+ if ( !dt_find_property(node, "xen,static-mem", NULL) )
+ allocate_memory(d, &kinfo);
+ else if ( !is_domain_direct_mapped(d) )
+ allocate_static_memory(d, &kinfo, node);
+ else
+ assign_static_memory_11(d, &kinfo, node);
+
+ rc = process_shm(d, &kinfo, node);
+ if ( rc < 0 )
+ return rc;
+
+ rc = init_vuart(d, &kinfo, node);
+ if ( rc < 0 )
+ return rc;
+
+ rc = prepare_dtb_domU(d, &kinfo);
+ if ( rc < 0 )
+ return rc;
+
+ rc = construct_domain(d, &kinfo);
+ if ( rc < 0 )
+ return rc;
+
+ if ( kinfo.dom0less_feature & DOM0LESS_XENSTORE )
+ {
+ ASSERT(hardware_domain);
+ rc = alloc_xenstore_evtchn(d);
+ if ( rc < 0 )
+ return rc;
+#ifdef CONFIG_HVM
+ d->arch.hvm.params[HVM_PARAM_STORE_PFN] = ~0ULL;
+#endif
+ }
+
+ return rc;
+}
@@ -8,6 +8,9 @@
#include <public/domctl.h>
+struct kernel_info;
+struct dt_device_node;
+
void create_domUs(void);
bool is_dom0less_mode(void);
@@ -18,6 +21,17 @@ void arch_create_domus(struct dt_device_node *node,
struct xen_domctl_createdomain *d_cfg,
unsigned int flags);
+int init_vuart(struct domain *d, struct kernel_info *kinfo,
+ const struct dt_device_node *node);
+
+int make_intc_domU_node(struct kernel_info *kinfo);
+int make_arch_nodes(struct kernel_info *kinfo);
+
+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);
+
#else /* !CONFIG_DOM0LESS_BOOT */
static inline void create_domUs(void) {}
Part of Arm's dom0less-build.c could be common between architectures which are using device tree files to create guest domains. Thereby move some parts of Arm's dom0less-build.c to common code with minor changes. As a part of theses changes the following changes are introduced: - Introduce make_arch_nodes() to cover arch-specific nodes. For example, in case of Arm, it is PSCI and vpl011 nodes. - Introduce set_domain_type() to abstract a way how setting of domain type happens. For example, RISC-V won't have this member of arch_domain structure as vCPUs will always have the same bitness as hypervisor. In case of Arm, it is possible that Arm64 could create 32-bit and 64-bit domains. - Introduce init_vuart() to cover details of virtual uart initialization. - Introduce init_intc_phandle() to cover some details of interrupt controller phandle initialization. As an example, RISC-V could have different name for interrupt controller node ( APLIC, PLIC, IMSIC, etc ) but the code in domain_handle_dtb_bootmodule() could handle only one interrupt controller node name. - s/make_gic_domU_node/make_intc_domU_node as GIC is Arm specific naming and add prototype of make_intc_domU_node() to dom0less-build.h The following functions are moved to xen/common/device-tree: - Functions which are moved as is: - domain_p2m_pages(). - handle_passthrough_prop(). - handle_prop_pfdt(). - scan_pfdt_node(). - check_partial_fdt(). - Functions which are moved with some minor changes: - alloc_xenstore_evtchn(): - ifdef-ing by CONFIG_HVM accesses to hvm.params. - prepare_dtb_domU(): - ifdef-ing access to gnttab_{start,size} by CONFIG_GRANT_TABLE. - s/make_gic_domU_node/make_intc_domU_node. - Add call of make_arch_nodes(). - domain_handle_dtb_bootmodule(): - hide details of interrupt controller phandle initialization by calling init_intc_phandle(). - Update the comment above init_intc_phandle(): s/gic/interrupt controller. - construct_domU(): - ifdef-ing by CONFIG_HVM accesses to hvm.params. - Call init_vuart() to hide Arm's vpl011_init() details there. - Add call of set_domain_type() instead of setting kinfo->arch.type explicitly. Signed-off-by: Oleksii Kurochko <oleksii.kurochko@gmail.com> --- xen/arch/arm/dom0less-build.c | 569 ++--------------------- xen/common/device-tree/dom0less-build.c | 559 ++++++++++++++++++++++ xen/include/asm-generic/dom0less-build.h | 14 + 3 files changed, 608 insertions(+), 534 deletions(-)