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

Re: [Xen-devel] [PATCH 08/11] device tree, arm: supply a flat device tree to dom0



On Mon, 2012-03-19 at 17:52 +0000, David Vrabel wrote:
> From: David Vrabel <david.vrabel@xxxxxxxxxx>
> 
> 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.

With the series applied up to and including this patch my dom0 kernel
fails to boot with:
[    0.000000] Linux version 3.2.0-rc5-arm-native+ (ianc@drall) (gcc version 
4.6.0 (GCC) ) #77 Thu Mar 22 13:58:33 GMT 2012
[    0.000000] CPU: ARMv7 Processor [410fc0f0] revision 0 (ARMv7), cr=10c53c7d
[    0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing 
instruction cache
[    0.000000] Machine: ARM Versatile Express, model: V2P-AEMv7A
[    0.000000] bootconsole [earlycon0] enabled
[    0.000000] Memory policy: ECC disabled, Data cache writeback
[    0.000000] Kernel panic - not syncing: ERROR: Failed to allocate 0x1000 
bytes below 0x0.
[    0.000000] 
[    0.000000] [<c000d64c>] (unwind_backtrace+0x0/0xe0) from [<c0273558>] 
(panic+0x50/0x17c)
[    0.000000] [<c0273558>] (panic+0x50/0x17c) from [<c032ff04>] 
(memblock_alloc_base+0x2c/0x34)
[    0.000000] [<c032ff04>] (memblock_alloc_base+0x2c/0x34) from [<c0329f30>] 
(early_alloc.constprop.3+0x10/0x28)
[    0.000000] [<c0329f30>] (early_alloc.constprop.3+0x10/0x28) from 
[<c032a8c0>] (paging_init+0x48c/0x628)
[    0.000000] [<c032a8c0>] (paging_init+0x48c/0x628) from [<c0328678>] 
(setup_arch+0x508/0x7ac)
[    0.000000] [<c0328678>] (setup_arch+0x508/0x7ac) from [<c03254dc>] 
(start_kernel+0x6c/0x2c0)
[    0.000000] [<c03254dc>] (start_kernel+0x6c/0x2c0) from [<80008048>] 
(0x80008048)

I'm guessing that the memory isn't being passed in correctly?

> 
> Signed-off-by: David Vrabel <david.vrabel@xxxxxxxxxx>
> ---
>  xen/arch/arm/Makefile         |    2 +
>  xen/arch/arm/domain_build.c   |  253 
> ++++++++++++++++++++++++++++++++++-------
>  xen/arch/arm/kernel.c         |    2 +-
>  xen/arch/arm/kernel.h         |    8 +-
>  xen/common/device_tree.c      |   47 +++++---
>  xen/include/xen/device_tree.h |    8 ++
>  6 files changed, 260 insertions(+), 60 deletions(-)
> 
> diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
> index d5afb6b..8d6edf5 100644
> --- a/xen/arch/arm/Makefile
> +++ b/xen/arch/arm/Makefile
> @@ -27,6 +27,8 @@ obj-y += vtimer.o
> 
>  #obj-bin-y += ....o
> 
> +CFLAGS += -I../../common/libfdt
> +
>  ifdef CONFIG_DTB_FILE
>  obj-y += dtb.o
>  AFLAGS += -DCONFIG_DTB_FILE=\"$(CONFIG_DTB_FILE)\"
> diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
> index 15632f7..963f1cf 100644
> --- a/xen/arch/arm/domain_build.c
> +++ b/xen/arch/arm/domain_build.c
> @@ -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,12 @@
>  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) so no additional space is needed.
> + */
> +#define DOM0_FDT_EXTRA_SIZE 0
> +
>  struct vcpu *__init alloc_dom0_vcpu0(void)
>  {
>      if ( opt_dom0_max_vcpus == 0 )
> @@ -28,43 +38,207 @@ struct vcpu *__init alloc_dom0_vcpu0(void)
>      return alloc_vcpu(dom0, 0, 0);
>  }
> 
> -extern void guest_mode_entry(void);
> +static void set_memory_reg(struct domain *d, struct kernel_info *kinfo,
> +                           const void *fdt,
> +                           const u32 *cell, int address_cells, int 
> size_cells,
> +                           u32 *new_cell, int *len)
> +{
> +    int reg_size = (address_cells + size_cells) * sizeof(*cell);
> +    int l;
> +    u64 start;
> +    u64 size;
> +
> +    l = *len;
> +
> +    while ( kinfo->unassigned_mem > 0 && l >= reg_size
> +            && 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;
> +    }
> +
> +    *len -= 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)
> +{
> +    int prop;
> +
> +    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;
> +
> +        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);
> +
> +        /*
> +         * 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 -ENOMEM;
> +
> +                set_memory_reg(d, kinfo, fdt,
> +                               (u32 *)prop_data, address_cells, size_cells,
> +                               (u32 *)new_data, &prop_len);
> +                prop_data = new_data;
> +            }
> +        }
> +
> +        /*
> +         * 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.
> +         */
> +
> +        fdt_property(kinfo->fdt, prop_name, prop_data, prop_len);
> +
> +        xfree(new_data);
> +    }
> +
> +    if ( prop == -FDT_ERR_NOTFOUND )
> +        return 0;
> +    return prop;
> +}
> 
> -static void setup_linux_atag(paddr_t tags, paddr_t ram_s, paddr_t ram_e)
> +static int write_nodes(struct domain *d, struct kernel_info *kinfo,
> +                       const void *fdt)
>  {
> -    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 node;
> +    int depth = 0, last_depth = -1;
> +    u32 address_cells[DEVICE_TREE_MAX_DEPTH];
> +    u32 size_cells[DEVICE_TREE_MAX_DEPTH];
> +    int ret;
> 
> -    /* not enough room on this page for all the tags */
> -    BUG_ON(PAGE_SIZE - (tags & (PAGE_SIZE - 1)) < 8 * sizeof(uint32_t));
> +    for ( node = 0, depth = 0;
> +          node >= 0 && depth >= 0;
> +          node = fdt_next_node(fdt, node, &depth) )
> +    {
> +        const char *name;
> 
> -#define TAG(type, val) *(type*)p = val; p+= sizeof(type)
> +        name = fdt_get_name(fdt, node, NULL);
> 
> -    /* ATAG_CORE */
> -    TAG(uint32_t, 2);
> -    TAG(uint32_t, 0x54410001);
> +        if ( depth >= DEVICE_TREE_MAX_DEPTH )
> +        {
> +            printk("warning: node `%s' is nested too deep\n", name);
> +            continue;
> +        }
> 
> -    /* ATAG_MEM */
> -    TAG(uint32_t, 4);
> -    TAG(uint32_t, 0x54410002);
> -    TAG(uint32_t, (ram_e - ram_s) & 0xFFFFFFFF);
> -    TAG(uint32_t, ram_s & 0xFFFFFFFF);
> +        while ( last_depth-- >= depth )
> +            fdt_end_node(kinfo->fdt);
> 
> -    /* 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;
> +        address_cells[depth] = device_tree_get_u32(fdt, node, 
> "#address-cells");
> +        size_cells[depth] = device_tree_get_u32(fdt, node, "#size-cells");
> 
> -    /* ATAG_NONE */
> -    TAG(uint32_t, 0);
> -    TAG(uint32_t, 0);
> +        fdt_begin_node(kinfo->fdt, name);
> 
> -#undef TAG
> +        ret = write_properties(d, kinfo, fdt, node, name, depth,
> +                               address_cells[depth-1], size_cells[depth-1]);
> +        if ( ret < 0 )
> +            return ret;
> 
> -    unmap_domain_page(map);
> +        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;
> +
> +    fdt_finish(kinfo->fdt);
> +
> +    device_tree_dump(kinfo->fdt);
> +
> +    /*
> +     * 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 = -EINVAL;
> +        goto err;
> +    }
> +
> +    return 0;
> +
> +  err:
> +    xfree(kinfo->fdt);
> +    return ret;
> +}
> +
> +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 +256,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;
> +    d->max_pages = ~0U;
> 
> -    rc = kernel_prepare(&kinfo);
> -    if (rc < 0)
> +    if ( (rc = p2m_alloc_table(d)) != 0 )
>          return rc;
> 
> -    d->max_pages = ~0U;
> +    kinfo.unassigned_mem = 0x08000000; /* XXX */
> 
> -    if ( (rc = p2m_alloc_table(d)) != 0 )
> +    rc = prepare_dtb(d, &kinfo);
> +    if ( rc < 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);
> +    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 +297,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 +324,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 --git a/xen/arch/arm/kernel.c b/xen/arch/arm/kernel.c
> index dd757e5..130d488 100644
> --- a/xen/arch/arm/kernel.c
> +++ b/xen/arch/arm/kernel.c
> @@ -121,7 +121,7 @@ static int kernel_try_zimage_prepare(struct kernel_info 
> *info)
>       * 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 --git a/xen/arch/arm/kernel.h b/xen/arch/arm/kernel.h
> index 5caebe5..4533568 100644
> --- a/xen/arch/arm/kernel.h
> +++ b/xen/arch/arm/kernel.h
> @@ -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 --git a/xen/common/device_tree.c b/xen/common/device_tree.c
> index d4b1556..715fbf6 100644
> --- a/xen/common/device_tree.c
> +++ b/xen/common/device_tree.c
> @@ -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 **cell, u32 cells, 
> u64 *val)
>      }
>  }
> 
> -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 void *fdt, int 
> node,
>      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 *fdt,
>  {
>      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 *fdt,
>  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(const void *fdt, 
> int node,
> 
>      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 void *fdt,
>                                    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 --git a/xen/include/xen/device_tree.h b/xen/include/xen/device_tree.h
> index b91b39f..510b5b4 100644
> --- a/xen/include/xen/device_tree.h
> +++ b/xen/include/xen/device_tree.h
> @@ -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);
> --
> 1.7.2.5
> 



_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

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