diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c index f1fe4ba4fd..24f977c30a 100644 --- a/xen/arch/arm/mm.c +++ b/xen/arch/arm/mm.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -159,7 +160,7 @@ void __init setup_pagetables(unsigned long boot_phys_offset) write_pte(xen_second + second_linear_offset(dest_va), pte); } - xen_paddr = XEN_PADDR; + xen_paddr = device_tree_get_xen_paddr(); /* Map the destination in the boot misc area. */ dest_va = BOOT_MISC_VIRT_START; diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c index 51afb31592..01d26683e0 100644 --- a/xen/arch/arm/setup.c +++ b/xen/arch/arm/setup.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -68,8 +69,13 @@ void __init start_xen(unsigned long boot_phys_offset, unsigned long atag_paddr) { + void *fdt; int i; + fdt = (void *)BOOT_MISC_VIRT_START + + (atag_paddr & ((1 << SECOND_SHIFT) - 1)); + device_tree_early_init(fdt); + setup_pagetables(boot_phys_offset); #ifdef EARLY_UART_ADDRESS diff --git a/xen/common/Makefile b/xen/common/Makefile index 4b1e6f220c..372b259a11 100644 --- a/xen/common/Makefile +++ b/xen/common/Makefile @@ -1,6 +1,7 @@ obj-y += bitmap.o obj-y += cpu.o obj-y += cpupool.o +obj-$(HAS_DEVICE_TREE) += device_tree.o obj-y += domctl.o obj-y += domain.o obj-y += event_channel.o @@ -60,3 +61,5 @@ subdir-$(ia64) += hvm subdir-y += libelf subdir-$(HAS_DEVICE_TREE) += libfdt + +CFLAGS += -Ilibfdt diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c new file mode 100644 index 0000000000..7d7514dc01 --- /dev/null +++ b/xen/common/device_tree.c @@ -0,0 +1,179 @@ +/* + * Device Tree + * + * Copyright (C) 2012 Citrix Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct dt_early_info __initdata early_info; + +static void __init get_val(const u32 **cell, u32 cells, u64 *val) +{ + *val = 0; + + while (cells--) { + *val <<= 32; + *val |= fdt32_to_cpu(*(*cell)++); + } +} + +static void __init get_register(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) +{ + const struct fdt_property *prop; + + prop = fdt_get_property(fdt, node, prop_name, NULL); + if (!prop || prop->len < sizeof(u32)) + return 0; /* default to 0 */ + + return fdt32_to_cpu(*(uint32_t*)prop->data); +} + +static void __init process_memory_node(const void *fdt, int node, + u32 address_cells, u32 size_cells) +{ + const struct fdt_property *prop; + size_t reg_cells; + int i; + int banks; + const u32 *cell; + paddr_t start, size; + + if (address_cells < 1 || size_cells < 1) { + early_printk("fdt: node `%s': invalid #address-cells or #size-cells", + fdt_get_name(fdt, node, NULL)); + return; + } + + prop = fdt_get_property(fdt, node, "reg", NULL); + if (!prop) { + early_printk("fdt: node `%s': missing `reg' property\n", + fdt_get_name(fdt, node, NULL)); + return; + } + + cell = (const u32 *)prop->data; + reg_cells = address_cells + size_cells; + banks = fdt32_to_cpu(prop->len) / (reg_cells * sizeof(u32)); + + for (i = 0; i < banks && early_info.mem.nr_banks < NR_MEM_BANKS; i++) { + get_register(&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++; + } +} + +#define MAX_DEPTH 16 + +static void __init early_scan(const void *fdt) +{ + int node; + int depth; + const char *name; + u32 address_cells[MAX_DEPTH]; + u32 size_cells[MAX_DEPTH]; + + for (node = 0; depth >= 0; node = fdt_next_node(fdt, node, &depth)) { + name = fdt_get_name(fdt, node, NULL); + + if (depth >= MAX_DEPTH) { + early_printk("fdt: node '%s': nested too deep\n", + fdt_get_name(fdt, node, NULL)); + continue; + } + + address_cells[depth] = prop_by_name_u32(fdt, node, "#address-cells"); + size_cells[depth] = prop_by_name_u32(fdt, node, "#size-cells"); + + if (strncmp(name, "memory", 6) == 0) + process_memory_node(fdt, node, address_cells[depth-1], size_cells[depth-1]); + } +} + +static void __init early_print_info(void) +{ + struct dt_mem_info *mi = &early_info.mem; + int i; + + for (i = 0; i < mi->nr_banks; i++) + early_printk("RAM: %016llx - %016llx\n", + mi->bank[i].start, mi->bank[i].start + mi->bank[i].size - 1); +} + +/** + * device_tree_early_init - initialize early info from a DTB + * @fdt: flattened device tree binary + */ +void __init device_tree_early_init(const void *fdt) +{ + int ret; + + ret = fdt_check_header(fdt); + if (ret < 0) + early_panic("No valid device tree\n"); + + early_scan(fdt); + early_print_info(); +} + +/** + * device_tree_get_xen_paddr - get physical address to relocate Xen to + * + * Xen is relocated to the top of RAM and aligned to a XEN_PADDR_ALIGN + * boundary. + */ +paddr_t __init device_tree_get_xen_paddr(void) +{ + struct dt_mem_info *mi = &early_info.mem; + paddr_t min_size; + paddr_t paddr = 0, t; + int i; + + min_size = (_end - _start + (XEN_PADDR_ALIGN-1)) & ~(XEN_PADDR_ALIGN-1); + + /* Find the highest bank with enough space. */ + for (i = 0; i < mi->nr_banks; i++) { + if (mi->bank[i].size >= min_size) { + t = mi->bank[i].start + mi->bank[i].size - min_size; + if (t > paddr) + paddr = t; + } + } + + if (!paddr) + early_panic("Not enough memory to relocate Xen\n"); + + return paddr; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/asm-arm/mm.h b/xen/include/asm-arm/mm.h index f721c54361..8efa63bc24 100644 --- a/xen/include/asm-arm/mm.h +++ b/xen/include/asm-arm/mm.h @@ -6,9 +6,8 @@ #include #include -/* Find a suitable place at the top of memory for Xen to live */ -/* XXX For now, use the top of the VE's 4GB RAM, at a 40-bit alias */ -#define XEN_PADDR 0x80ffe00000ull +/* Align Xen to a 2 MiB boundary. */ +#define XEN_PADDR_ALIGN (1 << 21) /* * Per-page-frame information. diff --git a/xen/include/xen/device_tree.h b/xen/include/xen/device_tree.h new file mode 100644 index 0000000000..ae3e344495 --- /dev/null +++ b/xen/include/xen/device_tree.h @@ -0,0 +1,36 @@ +/* + * Device Tree + * + * Copyright (C) 2012 Citrix Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __XEN_DEVICE_TREE_H__ +#define __XEN_DEVICE_TREE_H__ + +#include + +#define NR_MEM_BANKS 8 + +struct membank { + paddr_t start; + paddr_t size; +}; + +struct dt_mem_info { + int nr_banks; + struct membank bank[NR_MEM_BANKS]; +}; + +struct dt_early_info { + struct dt_mem_info mem; +}; + +extern struct dt_early_info early_info; + +void device_tree_early_init(const void *fdt); +paddr_t device_tree_get_xen_paddr(void); + +#endif