[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v4 13/27] xen/arm: Build DOM0 FDT by browsing the device tree structure
Remove the usage of the FDT in benefit of the device tree structure. The latter is easier to use and can embedded meta-data for Xen (ie: is the device is used by Xen...). Signed-off-by: Julien Grall <julien.grall@xxxxxxxxxx> --- Changes in v4: - rename set_interrupts to set_interrupt_ppi - rename interrupt_t to gic_interrupt_t - add fdt_property_interrupts Changes in v3: - use dt_for_each_node_property instea of for_each_node_of_property Changes in v2: - use dt_set_range - add set_interrupts - add more debug print - add comment to explain why some device are not mapped - mix kbasename directly in handle_node - fix malloc check when memory cells are allocated --- xen/arch/arm/domain_build.c | 327 +++++++++++++++++++++---------------------- 1 file changed, 162 insertions(+), 165 deletions(-) diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c index 8ebe1bc..fb4f172 100644 --- a/xen/arch/arm/domain_build.c +++ b/xen/arch/arm/domain_build.c @@ -63,10 +63,10 @@ struct vcpu *__init alloc_dom0_vcpu0(void) } static int set_memory_reg_11(struct domain *d, struct kernel_info *kinfo, - const void *fdt, const u32 *cell, int len, - int address_cells, int size_cells, u32 *new_cell) + const struct dt_property *pp, + const struct dt_device_node *np, __be32 *new_cell) { - int reg_size = (address_cells + size_cells) * sizeof(*cell); + int reg_size = dt_cells_to_size(dt_n_addr_cells(np) + dt_n_size_cells(np)); paddr_t start; paddr_t size; struct page_info *pg; @@ -90,7 +90,7 @@ static int set_memory_reg_11(struct domain *d, struct kernel_info *kinfo, if ( res ) panic("Unable to add pages in DOM0: %d\n", res); - device_tree_set_reg(&new_cell, address_cells, size_cells, start, size); + dt_set_range(&new_cell, np, start, size); kinfo->mem.bank[0].start = start; kinfo->mem.bank[0].size = size; @@ -100,25 +100,30 @@ static int set_memory_reg_11(struct domain *d, struct kernel_info *kinfo, } 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) + const struct dt_property *pp, + const struct dt_device_node *np, __be32 *new_cell) { - int reg_size = (address_cells + size_cells) * sizeof(*cell); + int reg_size = dt_cells_to_size(dt_n_addr_cells(np) + dt_n_size_cells(np)); int l = 0; + unsigned int bank = 0; u64 start; u64 size; + int ret; if ( platform_has_quirk(PLATFORM_QUIRK_DOM0_MAPPING_11) ) - return set_memory_reg_11(d, kinfo, fdt, cell, len, address_cells, - size_cells, new_cell); + return set_memory_reg_11(d, kinfo, pp, np, new_cell); - while ( kinfo->unassigned_mem > 0 && l + reg_size <= len + while ( kinfo->unassigned_mem > 0 && l + reg_size <= pp->length && kinfo->mem.nr_banks < NR_MEM_BANKS ) { - device_tree_get_reg(&cell, address_cells, size_cells, &start, &size); + ret = dt_device_get_address(np, bank, &start, &size); + if ( ret ) + panic("Unable to retrieve the bank %u for %s\n", + bank, dt_node_full_name(np)); + if ( size > kinfo->unassigned_mem ) size = kinfo->unassigned_mem; - device_tree_set_reg(&new_cell, address_cells, size_cells, start, size); + dt_set_range(&new_cell, np, start, size); printk("Populate P2M %#"PRIx64"->%#"PRIx64"\n", start, start + size); if ( p2m_populate_ram(d, start, start + size) < 0 ) @@ -135,31 +140,21 @@ static int set_memory_reg(struct domain *d, struct kernel_info *kinfo, } 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) + const struct dt_device_node *np) { const char *bootargs = NULL; - int prop; + const struct dt_property *pp; + int res = 0; if ( early_info.modules.nr_mods >= MOD_KERNEL && early_info.modules.module[MOD_KERNEL].cmdline[0] ) bootargs = &early_info.modules.module[MOD_KERNEL].cmdline[0]; - for ( prop = fdt_first_property_offset(fdt, node); - prop >= 0; - prop = fdt_next_property_offset(fdt, prop) ) + dt_for_each_property_node (np, pp) { - 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); + const void *prop_data = pp->value; + void *new_data = NULL; + u32 prop_len = pp->length; /* * In chosen node: @@ -168,105 +163,114 @@ static int write_properties(struct domain *d, struct kernel_info *kinfo, * bootargs (from module #1, above). * * remove bootargs and xen,dom0-bootargs. */ - if ( device_tree_node_matches(fdt, node, "chosen") ) + if ( dt_node_path_is_equal(np, "/chosen") ) { - if ( strcmp(prop_name, "bootargs") == 0 ) + if ( dt_property_name_is_equal(pp, "bootargs") ) continue; - else if ( strcmp(prop_name, "xen,dom0-bootargs") == 0 ) + else if ( dt_property_name_is_equal(pp, "xen,dom0-bootargs") ) { if ( !bootargs ) - bootargs = prop_data; + bootargs = pp->value; continue; } } /* * In a memory node: adjust reg property. + * TODO: handle properly memory node (ie: device_type = "memory") */ - else if ( device_tree_node_matches(fdt, node, "memory") ) + else if ( dt_node_name_is_equal(np, "memory") ) { - if ( strcmp(prop_name, "reg") == 0 ) + if ( dt_property_name_is_equal(pp, "reg") ) { - new_data = xzalloc_bytes(prop_len); + new_data = xzalloc_bytes(pp->length); 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_len = set_memory_reg(d, kinfo, pp, np, + (__be32 *)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); + res = fdt_property(kinfo->fdt, pp->name, prop_data, prop_len); xfree(new_data); + + if ( res ) + return res; } - if ( device_tree_node_matches(fdt, node, "chosen") && bootargs ) - fdt_property(kinfo->fdt, "bootargs", bootargs, strlen(bootargs) + 1); + if ( dt_node_path_is_equal(np, "/chosen") && bootargs ) + { + res = fdt_property(kinfo->fdt, "bootargs", bootargs, + strlen(bootargs) + 1); + if ( res ) + return res; + } /* * XXX should populate /chosen/linux,initrd-{start,end} here if we * have module[2] */ - if ( prop == -FDT_ERR_NOTFOUND ) - return 0; - return prop; + return 0; } -/* Returns the next node in fdt (starting from offset) which should be - * passed through to dom0. +/* + * Helper to write an interrupts with the GIC format + * This code is assuming the irq is an PPI. */ -static int fdt_next_dom0_node(const void *fdt, int node, - int *depth_out) + +typedef __be32 gic_interrupt_t[3]; + +static void set_interrupt_ppi(gic_interrupt_t interrupt, unsigned int irq, + unsigned int cpumask, unsigned int level) { - int depth = *depth_out; + __be32 *cells = interrupt; - while ( (node = fdt_next_node(fdt, node, &depth)) && - node >= 0 && depth >= 0 ) - { - if ( depth >= DEVICE_TREE_MAX_DEPTH ) - break; + BUG_ON(irq < 16 && irq >= 32); - /* Skip /hypervisor/ node. We will inject our own. */ - if ( fdt_node_check_compatible(fdt, node, "xen,xen" ) == 0 ) - { - printk("Device-tree contains \"xen,xen\" node. Ignoring.\n"); - continue; - } + /* See linux Documentation/devictree/bindings/arm/gic.txt */ + dt_set_cell(&cells, 1, 1); /* is a PPI */ + dt_set_cell(&cells, 1, irq - 16); /* PPIs start at 16 */ + dt_set_cell(&cells, 1, (cpumask << 8) | level); +} - /* Skip multiboot subnodes */ - if ( fdt_node_check_compatible(fdt, node, - "xen,multiboot-module" ) == 0 ) - continue; +/* + * Helper to set interrupts for a node in the flat device tree. + * It needs 2 property: + * "interrupts": contains the list of interrupts + * "interrupt-parent": link to the GIC + */ +static int fdt_property_interrupts(void *fdt, gic_interrupt_t *intr, + unsigned num_irq) +{ + int res; - /* We've arrived at a node which dom0 is interested in. */ - break; - } + res = fdt_property(fdt, "interrupts", intr, sizeof (intr[0]) * num_irq); + if ( res ) + return res; + + res = fdt_property_cell(fdt, "interrupt-parent", + dt_interrupt_controller->phandle); - *depth_out = depth; - return node; + return res; } -static void make_hypervisor_node(void *fdt, int addrcells, int sizecells) + +static int make_hypervisor_node(void *fdt, const struct dt_device_node *parent) { const char compat[] = "xen,xen-"__stringify(XEN_VERSION)"."__stringify(XEN_SUBVERSION)"\0" "xen,xen"; - u32 reg[4]; - u32 intr[3]; - u32 *cell; + __be32 reg[4]; + gic_interrupt_t intr; + __be32 *cells; + int res; + int addrcells = dt_n_addr_cells(parent); + int sizecells = dt_n_size_cells(parent); + + DPRINT("Create hypervisor node\n"); /* * Sanity-check address sizes, since addresses and sizes which do @@ -277,82 +281,42 @@ static void make_hypervisor_node(void *fdt, int addrcells, int sizecells) panic("Cannot cope with this size"); /* See linux Documentation/devicetree/bindings/arm/xen.txt */ - fdt_begin_node(fdt, "hypervisor"); + res = fdt_begin_node(fdt, "hypervisor"); + if ( res ) + return res; /* Cannot use fdt_property_string due to embedded nulls */ - fdt_property(fdt, "compatible", compat, sizeof(compat)); + res = fdt_property(fdt, "compatible", compat, sizeof(compat)); + if ( res ) + return res; + DPRINT(" Grant table range: 0xb0000000-0x20000\n"); /* reg 0 is grant table space */ - cell = ®[0]; - device_tree_set_reg(&cell, addrcells, sizecells, 0xb0000000, 0x20000); - fdt_property(fdt, "reg", reg, - sizeof(reg[0]) * (addrcells + sizecells)); + cells = ®[0]; + dt_set_range(&cells, parent, 0xb0000000, 0x20000); + res = fdt_property(fdt, "reg", reg, + dt_cells_to_size(addrcells + sizecells)); + if ( res ) + return res; /* - * interrupts is evtchn upcall <1 15 0xf08> - * See linux Documentation/devicetree/bindings/arm/gic.txt + * interrupts is evtchn upcall: + * - Active-low level-sensitive + * - All cpus + * + * TODO: Handle correctly the cpumask */ - intr[0] = cpu_to_fdt32(1); /* is a PPI */ - intr[1] = cpu_to_fdt32(VGIC_IRQ_EVTCHN_CALLBACK - 16); /* PPIs start at 16 */ - intr[2] = cpu_to_fdt32(0xf08); /* Active-low level-sensitive */ - - fdt_property(fdt, "interrupts", intr, sizeof(intr[0]) * 3); + DPRINT(" Event channel interrupt to %u\n", VGIC_IRQ_EVTCHN_CALLBACK); + set_interrupt_ppi(intr, VGIC_IRQ_EVTCHN_CALLBACK, 0xf, + DT_IRQ_TYPE_LEVEL_LOW); - fdt_end_node(fdt); -} - -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_dom0_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 (%d)\n", - name, depth); - continue; - } - - /* We cannot handle descending more than one level at a time */ - ASSERT( depth <= last_depth + 1 ); - - while ( last_depth-- >= depth ) - fdt_end_node(kinfo->fdt); - - address_cells[depth] = device_tree_get_u32(fdt, node, "#address-cells", - depth > 0 ? address_cells[depth-1] : 0); - size_cells[depth] = device_tree_get_u32(fdt, node, "#size-cells", - depth > 0 ? size_cells[depth-1] : 0); - - 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-- >= 1 ) - fdt_end_node(kinfo->fdt); + res = fdt_property_interrupts(fdt, &intr, 1); + if ( res ) + return res; - make_hypervisor_node(kinfo->fdt, address_cells[0], size_cells[0]); + res = fdt_end_node(fdt); - fdt_end_node(kinfo->fdt); - return 0; + return res; } /* Map the device in the domain */ @@ -435,25 +399,39 @@ static int map_device(struct domain *d, const struct dt_device_node *dev) return 0; } -static int handle_node(struct domain *d, const struct dt_device_node *np) +static int handle_node(struct domain *d, struct kernel_info *kinfo, + const struct dt_device_node *np) { static const struct dt_device_match skip_matches[] __initconst = { DT_MATCH_COMPATIBLE("xen,xen"), - DT_MATCH_TYPE("memory"), - DT_MATCH_PATH("/chosen"), + DT_MATCH_COMPATIBLE("xen,multiboot-module"), { /* sentinel */ }, }; const struct dt_device_node *child; int res; + const char *name; + const char *path; + + path = dt_node_full_name(np); - DPRINT("handle %s\n", dt_node_full_name(np)); + DPRINT("handle %s\n", path); /* Skip theses nodes and the sub-nodes */ if ( dt_match_node(skip_matches, np ) ) + { + DPRINT(" Skip it!\n"); return 0; + } - if ( dt_device_used_by(np) != DOMID_XEN ) + /* + * Some device doesn't need to be mapped in Xen: + * - Device used by Xen: Obviously dom0 can't use them + * - Memory: the guest will see a different view of memory. It will + * be allocated later. + */ + if ( dt_device_used_by(np) != DOMID_XEN && + !dt_device_type_is_equal(np, "memory") ) { res = map_device(d, np); @@ -461,21 +439,39 @@ static int handle_node(struct domain *d, const struct dt_device_node *np) return res; } + /* + * The property "name" is used to have a different name on older FDT + * version. We want to keep the name retrieved during the tree + * structure creation, that is store in the node path. + */ + name = strrchr(path, '/'); + name = name ? name + 1 : path; + + res = fdt_begin_node(kinfo->fdt, name); + if ( res ) + return res; + + res = write_properties(d, kinfo, np); + if ( res ) + return res; + for ( child = np->child; child != NULL; child = child->sibling ) { - res = handle_node(d, child); + res = handle_node(d, kinfo, child); if ( res ) return res; } - return 0; -} + if ( np == dt_host ) + { + res = make_hypervisor_node(kinfo->fdt, np); + if ( res ) + return res; + } -static int map_devices_from_device_tree(struct domain *d) -{ - ASSERT(dt_host && (dt_host->sibling == NULL)); + res = fdt_end_node(kinfo->fdt); - return handle_node(d, dt_host); + return res; } static int prepare_dtb(struct domain *d, struct kernel_info *kinfo) @@ -485,6 +481,8 @@ static int prepare_dtb(struct domain *d, struct kernel_info *kinfo) int ret; paddr_t end; + ASSERT(dt_host && (dt_host->sibling == NULL)); + kinfo->unassigned_mem = dom0_mem; fdt = device_tree_flattened; @@ -500,8 +498,8 @@ static int prepare_dtb(struct domain *d, struct kernel_info *kinfo) fdt_finish_reservemap(kinfo->fdt); - ret = write_nodes(d, kinfo, fdt); - if ( ret < 0 ) + ret = handle_node(d, kinfo, dt_host); + if ( ret ) goto err; ret = fdt_finish(kinfo->fdt); @@ -576,7 +574,6 @@ int construct_dom0(struct domain *d) if ( rc < 0 ) return rc; - map_devices_from_device_tree(d); rc = platform_specific_mapping(d); if ( rc < 0 ) return rc; -- 1.7.10.4 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |