[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] [xen-unstable] device tree, arm: supply a flat device tree to dom0



# HG changeset patch
# User David Vrabel <david.vrabel@xxxxxxxxxx>
# Date 1333381842 -3600
# Node ID ce659898b2dfc04dc7f54b52d1c5582fa45ea520
# Parent  d90c658de78ac5c50f1b9c175167c9f94e683829
device tree,arm: supply a flat device tree to dom0

Build a flat device tree for dom0 based on the one supplied to Xen.
The following changes are made:

  * In the /chosen node, the xen,dom0-bootargs parameter is renamed to
    bootargs.

  * In all memory nodes, the reg parameters are adjusted to reflect
    the amount of memory dom0 can use.  The p2m is updated using this
    info.

Support for passing ATAGS to dom0 is removed.

Signed-off-by: David Vrabel <david.vrabel@xxxxxxxxxx>
[ removed device_tree_dump call -- ijc ]
Signed-off-by: Ian Campbell <ian.campbell@xxxxxxxxxx>
Committed-by: Ian Campbell <ian.campbell@xxxxxxxxxx>
---


diff -r d90c658de78a -r ce659898b2df xen/arch/arm/domain_build.c
--- a/xen/arch/arm/domain_build.c       Mon Apr 02 10:54:05 2012 +0100
+++ b/xen/arch/arm/domain_build.c       Mon Apr 02 16:50:42 2012 +0100
@@ -6,6 +6,10 @@
 #include <xen/sched.h>
 #include <asm/irq.h>
 #include <asm/regs.h>
+#include <xen/errno.h>
+#include <xen/device_tree.h>
+#include <xen/libfdt/libfdt.h>
+#include <xen/guest_access.h>
 
 #include "gic.h"
 #include "kernel.h"
@@ -13,6 +17,13 @@
 static unsigned int __initdata opt_dom0_max_vcpus;
 integer_param("dom0_max_vcpus", opt_dom0_max_vcpus);
 
+/*
+ * Amount of extra space required to dom0's device tree.  No new nodes
+ * are added (yet) but one terminating reserve map entry (16 bytes) is
+ * added.
+ */
+#define DOM0_FDT_EXTRA_SIZE (sizeof(struct fdt_reserve_entry))
+
 struct vcpu *__init alloc_dom0_vcpu0(void)
 {
     if ( opt_dom0_max_vcpus == 0 )
@@ -28,43 +39,205 @@ struct vcpu *__init alloc_dom0_vcpu0(voi
     return alloc_vcpu(dom0, 0, 0);
 }
 
-extern void guest_mode_entry(void);
+static int set_memory_reg(struct domain *d, struct kernel_info *kinfo,
+                          const void *fdt, const u32 *cell, int len,
+                          int address_cells, int size_cells, u32 *new_cell)
+{
+    int reg_size = (address_cells + size_cells) * sizeof(*cell);
+    int l = 0;
+    u64 start;
+    u64 size;
 
-static void setup_linux_atag(paddr_t tags, paddr_t ram_s, paddr_t ram_e)
+    while ( kinfo->unassigned_mem > 0 && l + reg_size <= len
+            && kinfo->mem.nr_banks < NR_MEM_BANKS )
+    {
+        device_tree_get_reg(&cell, address_cells, size_cells, &start, &size);
+        if ( size > kinfo->unassigned_mem )
+            size = kinfo->unassigned_mem;
+        device_tree_set_reg(&new_cell, address_cells, size_cells, start, size);
+
+        printk("Populate P2M %#llx->%#llx\n", start, start + size);
+        p2m_populate_ram(d, start, start + size);
+        kinfo->mem.bank[kinfo->mem.nr_banks].start = start;
+        kinfo->mem.bank[kinfo->mem.nr_banks].size = size;
+        kinfo->mem.nr_banks++;
+        kinfo->unassigned_mem -= size;
+
+        l += reg_size;
+    }
+
+    return l;
+}
+
+static int write_properties(struct domain *d, struct kernel_info *kinfo,
+                            const void *fdt,
+                            int node, const char *name, int depth,
+                            u32 address_cells, u32 size_cells)
 {
-    paddr_t ma = gvirt_to_maddr(tags);
-    void *map = map_domain_page(ma>>PAGE_SHIFT);
-    void *p = map + (tags & (PAGE_SIZE - 1));
-    char cmdline[] = "earlyprintk=xenboot console=ttyAMA1 root=/dev/mmcblk0 
debug rw";
+    int prop;
 
-    /* not enough room on this page for all the tags */
-    BUG_ON(PAGE_SIZE - (tags & (PAGE_SIZE - 1)) < 8 * sizeof(uint32_t));
+    for ( prop = fdt_first_property_offset(fdt, node);
+          prop >= 0;
+          prop = fdt_next_property_offset(fdt, prop) )
+    {
+        const struct fdt_property *p;
+        const char *prop_name;
+        const char *prop_data;
+        int prop_len;
+        char *new_data = NULL;
 
-#define TAG(type, val) *(type*)p = val; p+= sizeof(type)
+        p = fdt_get_property_by_offset(fdt, prop, NULL);
+        prop_name = fdt_string(fdt, fdt32_to_cpu(p->nameoff));
+        prop_data = p->data;
+        prop_len  = fdt32_to_cpu(p->len);
 
-    /* ATAG_CORE */
-    TAG(uint32_t, 2);
-    TAG(uint32_t, 0x54410001);
+        /*
+         * In chosen node: replace bootargs with value from
+         * xen,dom0-bootargs.
+         */
+        if ( device_tree_node_matches(fdt, node, "chosen") )
+        {
+            if ( strcmp(prop_name, "bootargs") == 0 )
+                continue;
+            if ( strcmp(prop_name, "xen,dom0-bootargs") == 0 )
+                prop_name = "bootargs";
+        }
+        /*
+         * In a memory node: adjust reg property.
+         */
+        else if ( device_tree_node_matches(fdt, node, "memory") )
+        {
+            if ( strcmp(prop_name, "reg") == 0 )
+            {
+                new_data = xzalloc_bytes(prop_len);
+                if ( new_data  == NULL )
+                    return -FDT_ERR_XEN(ENOMEM);
 
-    /* ATAG_MEM */
-    TAG(uint32_t, 4);
-    TAG(uint32_t, 0x54410002);
-    TAG(uint32_t, (ram_e - ram_s) & 0xFFFFFFFF);
-    TAG(uint32_t, ram_s & 0xFFFFFFFF);
+                prop_len = set_memory_reg(d, kinfo, fdt,
+                                          (u32 *)prop_data, prop_len,
+                                          address_cells, size_cells,
+                                          (u32 *)new_data);
+                prop_data = new_data;
+            }
+        }
 
-    /* ATAG_CMDLINE */
-    TAG(uint32_t, 2 + ((strlen(cmdline) + 4) >> 2));
-    TAG(uint32_t, 0x54410009);
-    memcpy(p, cmdline, strlen(cmdline) + 1);
-    p += ((strlen(cmdline) + 4) >> 2) << 2;
+        /*
+         * TODO: Should call map_mmio_regions() for all devices in the
+         * tree that have a "reg" parameter (except cpus).  This
+         * requires looking into the parent node's "ranges" property
+         * to translate the bus address in the "reg" value into
+         * physical addresses.  Regions also need to be rounded up to
+         * whole pages.
+         */
 
-    /* ATAG_NONE */
-    TAG(uint32_t, 0);
-    TAG(uint32_t, 0);
+        fdt_property(kinfo->fdt, prop_name, prop_data, prop_len);
 
-#undef TAG
+        xfree(new_data);
+    }
 
-    unmap_domain_page(map);
+    if ( prop == -FDT_ERR_NOTFOUND )
+        return 0;
+    return prop;
+}
+
+static int write_nodes(struct domain *d, struct kernel_info *kinfo,
+                       const void *fdt)
+{
+    int node;
+    int depth = 0, last_depth = -1;
+    u32 address_cells[DEVICE_TREE_MAX_DEPTH];
+    u32 size_cells[DEVICE_TREE_MAX_DEPTH];
+    int ret;
+
+    for ( node = 0, depth = 0;
+          node >= 0 && depth >= 0;
+          node = fdt_next_node(fdt, node, &depth) )
+    {
+        const char *name;
+
+        name = fdt_get_name(fdt, node, NULL);
+
+        if ( depth >= DEVICE_TREE_MAX_DEPTH )
+        {
+            printk("warning: node `%s' is nested too deep\n", name);
+            continue;
+        }
+
+        while ( last_depth-- >= depth )
+            fdt_end_node(kinfo->fdt);
+
+        address_cells[depth] = device_tree_get_u32(fdt, node, 
"#address-cells");
+        size_cells[depth] = device_tree_get_u32(fdt, node, "#size-cells");
+
+        fdt_begin_node(kinfo->fdt, name);
+
+        ret = write_properties(d, kinfo, fdt, node, name, depth,
+                               address_cells[depth-1], size_cells[depth-1]);
+        if ( ret < 0 )
+            return ret;
+
+        last_depth = depth;
+    }
+
+    while ( last_depth-- >= 0 )
+        fdt_end_node(kinfo->fdt);
+
+    return 0;
+}
+
+static int prepare_dtb(struct domain *d, struct kernel_info *kinfo)
+{
+    void *fdt;
+    int new_size;
+    int ret;
+
+    fdt = device_tree_flattened;
+
+    new_size = fdt_totalsize(fdt) + DOM0_FDT_EXTRA_SIZE;
+    kinfo->fdt = xmalloc_bytes(new_size);
+    if ( kinfo->fdt == NULL )
+        return -ENOMEM;
+
+    ret = fdt_create(kinfo->fdt, new_size);
+    if ( ret < 0 )
+        goto err;
+
+    fdt_finish_reservemap(kinfo->fdt);
+
+    ret = write_nodes(d, kinfo, fdt);
+    if ( ret < 0 )
+        goto err;
+
+    ret = fdt_finish(kinfo->fdt);
+    if ( ret < 0 )
+        goto err;
+
+    /*
+     * Put the device tree at the beginning of the first bank.  It
+     * must be below 4 GiB.
+     */
+    kinfo->dtb_paddr = kinfo->mem.bank[0].start + 0x100;
+    if ( kinfo->dtb_paddr + fdt_totalsize(kinfo->fdt) > (1ull << 32) )
+    {
+        printk("Not enough memory below 4 GiB for the device tree.");
+        ret = -FDT_ERR_XEN(EINVAL);
+        goto err;
+    }
+
+    return 0;
+
+  err:
+    printk("Device tree generation failed (%d).\n", ret);
+    xfree(kinfo->fdt);
+    return -EINVAL;
+}
+
+static void dtb_load(struct kernel_info *kinfo)
+{
+    void * __user dtb_virt = (void *)(u32)kinfo->dtb_paddr;
+
+    raw_copy_to_guest(dtb_virt, kinfo->fdt, fdt_totalsize(kinfo->fdt));
+    xfree(kinfo->fdt);
 }
 
 int construct_dom0(struct domain *d)
@@ -82,22 +255,20 @@ int construct_dom0(struct domain *d)
 
     printk("*** LOADING DOMAIN 0 ***\n");
 
-    /* 128M at 2G physical */
-    /* TODO size and location from DT. */
-    kinfo.ram_start = 0x80000000;
-    kinfo.ram_end   = 0x88000000;
-
-    rc = kernel_prepare(&kinfo);
-    if (rc < 0)
-        return rc;
-
     d->max_pages = ~0U;
 
     if ( (rc = p2m_alloc_table(d)) != 0 )
         return rc;
 
-    printk("Populate P2M %#llx->%#llx\n", kinfo.ram_start, kinfo.ram_end);
-    p2m_populate_ram(d, kinfo.ram_start, kinfo.ram_end);
+    kinfo.unassigned_mem = 0x08000000; /* XXX */
+
+    rc = prepare_dtb(d, &kinfo);
+    if ( rc < 0 )
+        return rc;
+
+    rc = kernel_prepare(&kinfo);
+    if ( rc < 0 )
+        return rc;
 
     printk("Map CS2 MMIO regions 1:1 in the P2M %#llx->%#llx\n", 
0x18000000ULL, 0x1BFFFFFFULL);
     map_mmio_regions(d, 0x18000000, 0x1BFFFFFF, 0x18000000);
@@ -125,13 +296,12 @@ int construct_dom0(struct domain *d)
     /* Enable second stage translation */
     WRITE_CP32(READ_CP32(HCR) | HCR_VM, HCR); isb();
 
-    /* The following load uses domain's p2m */
+    /* The following loads use the domain's p2m */
     p2m_load_VTTBR(d);
 
+    dtb_load(&kinfo);
     kernel_load(&kinfo);
 
-    setup_linux_atag(kinfo.ram_start + 0x100, kinfo.ram_start, kinfo.ram_end);
-
     clear_bit(_VPF_down, &v->pause_flags);
 
     memset(regs, 0, sizeof(*regs));
@@ -153,7 +323,7 @@ int construct_dom0(struct domain *d)
 
     regs->r0 = 0; /* SBZ */
     regs->r1 = 2272; /* Machine NR: Versatile Express */
-    regs->r2 = kinfo.ram_start + 0x100; /* ATAGS */
+    regs->r2 = kinfo.dtb_paddr;
 
     WRITE_CP32(SCTLR_BASE, SCTLR);
 
diff -r d90c658de78a -r ce659898b2df xen/arch/arm/kernel.c
--- a/xen/arch/arm/kernel.c     Mon Apr 02 10:54:05 2012 +0100
+++ b/xen/arch/arm/kernel.c     Mon Apr 02 16:50:42 2012 +0100
@@ -121,7 +121,7 @@ static int kernel_try_zimage_prepare(str
      * at 32k from start of RAM.
      */
     if (start == 0)
-        info->zimage.load_addr = info->ram_start + 0x8000;
+        info->zimage.load_addr = info->mem.bank[0].start + 0x8000;
     else
         info->zimage.load_addr = start;
     info->zimage.len = end - start;
diff -r d90c658de78a -r ce659898b2df xen/arch/arm/kernel.h
--- a/xen/arch/arm/kernel.h     Mon Apr 02 10:54:05 2012 +0100
+++ b/xen/arch/arm/kernel.h     Mon Apr 02 16:50:42 2012 +0100
@@ -7,11 +7,15 @@
 #define __ARCH_ARM_KERNEL_H__
 
 #include <xen/libelf.h>
+#include <xen/device_tree.h>
 
 struct kernel_info {
+    void *fdt; /* flat device tree */
+    paddr_t unassigned_mem; /* RAM not (yet) assigned to a bank */
+    struct dt_mem_info mem;
+
+    paddr_t dtb_paddr;
     paddr_t entry;
-    paddr_t ram_start;
-    paddr_t ram_end;
 
     void *kernel_img;
     unsigned kernel_order;
diff -r d90c658de78a -r ce659898b2df xen/common/device_tree.c
--- a/xen/common/device_tree.c  Mon Apr 02 10:54:05 2012 +0100
+++ b/xen/common/device_tree.c  Mon Apr 02 16:50:42 2012 +0100
@@ -23,7 +23,7 @@
 struct dt_early_info __initdata early_info;
 void *device_tree_flattened;
 
-static bool_t __init node_matches(const void *fdt, int node, const char *match)
+bool_t device_tree_node_matches(const void *fdt, int node, const char *match)
 {
     const char *name;
     size_t match_len;
@@ -48,16 +48,33 @@ static void __init get_val(const u32 **c
     }
 }
 
-static void __init get_register(const u32 **cell,
-                                u32 address_cells, u32 size_cells,
-                                u64 *start, u64 *size)
+void device_tree_get_reg(const u32 **cell, u32 address_cells, u32 size_cells,
+                         u64 *start, u64 *size)
 {
     get_val(cell, address_cells, start);
     get_val(cell, size_cells, size);
 }
 
-static u32 __init prop_by_name_u32(const void *fdt, int node,
-                                   const char *prop_name)
+static void set_val(u32 **cell, u32 cells, u64 val)
+{
+    u32 c = cells;
+
+    while ( c-- )
+    {
+        (*cell)[c] = cpu_to_fdt32(val);
+        val >>= 32;
+    }
+    (*cell) += cells;
+}
+
+void device_tree_set_reg(u32 **cell, u32 address_cells, u32 size_cells,
+                         u64 start, u64 size)
+{
+    set_val(cell, address_cells, start);
+    set_val(cell, size_cells, size);
+}
+
+u32 device_tree_get_u32(const void *fdt, int node, const char *prop_name)
 {
     const struct fdt_property *prop;
 
@@ -68,8 +85,6 @@ static u32 __init prop_by_name_u32(const
     return fdt32_to_cpu(*(uint32_t*)prop->data);
 }
 
-#define MAX_DEPTH 16
-
 /**
  * device_tree_for_each_node - iterate over all device tree nodes
  * @fdt: flat device tree.
@@ -81,19 +96,19 @@ int device_tree_for_each_node(const void
 {
     int node;
     int depth;
-    u32 address_cells[MAX_DEPTH];
-    u32 size_cells[MAX_DEPTH];
+    u32 address_cells[DEVICE_TREE_MAX_DEPTH];
+    u32 size_cells[DEVICE_TREE_MAX_DEPTH];
     int ret;
 
     for ( node = 0, depth = 0;
           node >=0 && depth >= 0;
           node = fdt_next_node(fdt, node, &depth) )
     {
-        if ( depth >= MAX_DEPTH )
+        if ( depth >= DEVICE_TREE_MAX_DEPTH )
             continue;
 
-        address_cells[depth] = prop_by_name_u32(fdt, node, "#address-cells");
-        size_cells[depth] = prop_by_name_u32(fdt, node, "#size-cells");
+        address_cells[depth] = device_tree_get_u32(fdt, node, 
"#address-cells");
+        size_cells[depth] = device_tree_get_u32(fdt, node, "#size-cells");
 
         ret = func(fdt, node, fdt_get_name(fdt, node, NULL), depth,
                    address_cells[depth-1], size_cells[depth-1], data);
@@ -106,7 +121,7 @@ int device_tree_for_each_node(const void
 static int dump_node(const void *fdt, int node, const char *name, int depth,
                      u32 address_cells, u32 size_cells, void *data)
 {
-    char prefix[2*MAX_DEPTH + 1] = "";
+    char prefix[2*DEVICE_TREE_MAX_DEPTH + 1] = "";
     int i;
     int prop;
 
@@ -172,7 +187,7 @@ static void __init process_memory_node(c
 
     for ( i = 0; i < banks && early_info.mem.nr_banks < NR_MEM_BANKS; i++ )
     {
-        get_register(&cell, address_cells, size_cells, &start, &size);
+        device_tree_get_reg(&cell, address_cells, size_cells, &start, &size);
         early_info.mem.bank[early_info.mem.nr_banks].start = start;
         early_info.mem.bank[early_info.mem.nr_banks].size = size;
         early_info.mem.nr_banks++;
@@ -184,7 +199,7 @@ static int __init early_scan_node(const 
                                   u32 address_cells, u32 size_cells,
                                   void *data)
 {
-    if ( node_matches(fdt, node, "memory") )
+    if ( device_tree_node_matches(fdt, node, "memory") )
         process_memory_node(fdt, node, name, address_cells, size_cells);
 
     return 0;
diff -r d90c658de78a -r ce659898b2df xen/include/xen/device_tree.h
--- a/xen/include/xen/device_tree.h     Mon Apr 02 10:54:05 2012 +0100
+++ b/xen/include/xen/device_tree.h     Mon Apr 02 16:50:42 2012 +0100
@@ -12,6 +12,8 @@
 
 #include <xen/types.h>
 
+#define DEVICE_TREE_MAX_DEPTH 16
+
 #define NR_MEM_BANKS 8
 
 struct membank {
@@ -39,6 +41,12 @@ extern void *device_tree_flattened;
 size_t device_tree_early_init(const void *fdt);
 paddr_t device_tree_get_xen_paddr(void);
 
+void device_tree_get_reg(const u32 **cell, u32 address_cells, u32 size_cells,
+                         u64 *start, u64 *size);
+void device_tree_set_reg(u32 **cell, u32 address_cells, u32 size_cells,
+                         u64 start, u64 size);
+u32 device_tree_get_u32(const void *fdt, int node, const char *prop_name);
+bool_t device_tree_node_matches(const void *fdt, int node, const char *match);
 int device_tree_for_each_node(const void *fdt,
                               device_tree_node_func func, void *data);
 void device_tree_dump(const void *fdt);
diff -r d90c658de78a -r ce659898b2df xen/include/xen/libfdt/libfdt_env.h
--- a/xen/include/xen/libfdt/libfdt_env.h       Mon Apr 02 10:54:05 2012 +0100
+++ b/xen/include/xen/libfdt/libfdt_env.h       Mon Apr 02 16:50:42 2012 +0100
@@ -13,4 +13,7 @@
 #define fdt64_to_cpu(x) be64_to_cpu(x)
 #define cpu_to_fdt64(x) cpu_to_be64(x)
 
+/* Xen-specific libfdt error code. */
+#define FDT_ERR_XEN(err) (FDT_ERR_MAX + (err))
+
 #endif /* _LIBFDT_ENV_H */

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.