diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c index 15632f79e7..2c99567b35 100644 --- a/xen/arch/arm/domain_build.c +++ b/xen/arch/arm/domain_build.c @@ -6,6 +6,10 @@ #include #include #include +#include +#include +#include +#include #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(void) return alloc_vcpu(dom0, 0, 0); } -extern void guest_mode_entry(void); - -static void setup_linux_atag(paddr_t tags, paddr_t ram_s, paddr_t ram_e) +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) { - 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 reg_size = (address_cells + size_cells) * sizeof(*cell); + int l = 0; + u64 start; + u64 size; - /* not enough room on this page for all the tags */ - BUG_ON(PAGE_SIZE - (tags & (PAGE_SIZE - 1)) < 8 * sizeof(uint32_t)); + 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); -#define TAG(type, val) *(type*)p = val; p+= sizeof(type) + 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; - /* ATAG_CORE */ - TAG(uint32_t, 2); - TAG(uint32_t, 0x54410001); + l += reg_size; + } - /* ATAG_MEM */ - TAG(uint32_t, 4); - TAG(uint32_t, 0x54410002); - TAG(uint32_t, (ram_e - ram_s) & 0xFFFFFFFF); - TAG(uint32_t, ram_s & 0xFFFFFFFF); + return l; +} - /* 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; +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; - /* ATAG_NONE */ - TAG(uint32_t, 0); - TAG(uint32_t, 0); + 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; -#undef TAG + 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); - unmap_domain_page(map); + /* + * 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); + + prop_len = set_memory_reg(d, kinfo, fdt, + (u32 *)prop_data, prop_len, + address_cells, size_cells, + (u32 *)new_data); + 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 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 --git a/xen/arch/arm/kernel.c b/xen/arch/arm/kernel.c index dd757e5b23..130d488de2 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 5caebe55c0..453356826c 100644 --- a/xen/arch/arm/kernel.h +++ b/xen/arch/arm/kernel.h @@ -7,11 +7,15 @@ #define __ARCH_ARM_KERNEL_H__ #include +#include 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 d4b15569ff..715fbf6657 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 b91b39f352..510b5b48ba 100644 --- a/xen/include/xen/device_tree.h +++ b/xen/include/xen/device_tree.h @@ -12,6 +12,8 @@ #include +#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 --git a/xen/include/xen/libfdt/libfdt_env.h b/xen/include/xen/libfdt/libfdt_env.h index 8c0c03034b..2f1b03c8e6 100644 --- a/xen/include/xen/libfdt/libfdt_env.h +++ b/xen/include/xen/libfdt/libfdt_env.h @@ -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 */