[PATCH 8/15] bootwrapper: convert flatdevtree to version 16
David Gibson
david at gibson.dropbear.id.au
Mon Sep 24 13:36:12 EST 2007
On Fri, Sep 21, 2007 at 06:05:06PM -0500, Milton Miller wrote:
> kexec-tools still produces a version 2 device tree, while the
> libraries in the wrapper only support version 16 and later.
>
> Add a routine to convert a v2 flat device tree to a v16 one inplace
> by inserting OF_DT_NOP and chomping full path. Make space for new
> headers by moving and then chomping the OF_DT_NOPs.
>
> Signed-off-by: Milton Miller <miltonm at bga.com>
> ---
> vs 12175
> Rediffed Makefile, ops, kexec.c
>
> Index: kernel/arch/powerpc/boot/flatdevtree_conv.c
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ kernel/arch/powerpc/boot/flatdevtree_conv.c 2007-09-20 17:49:04.000000000 -0500
> @@ -0,0 +1,280 @@
> +/*
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
> + *
> + * Copyright IBM Corporation 2007
> + *
> + * Authors: Milton Miller <miltonm at bga.com>
> + */
> +#include "flatdevtree.h"
> +#include "stdio.h"
> +#include "ops.h"
> +
> +#define MIN_VERSION 2
> +#define OUT_VERSION 16
Should output version 17. In any case, don't try to be so general -
just convert v123 (all basically the same) to latest (i.e. v17)
without all the #if nonsense.
> +#define OUT_COMPAT 16
> +
> +#ifdef NO_CHECK
> +static int check_v123_tree(u32 *start, u32 *limit)
> +{
> + return 0;
> +}
> +#else
> +/**
> + * check_v123_tree - check integrety of a version 1, 2, or 3 tree
> + * @start: the start of the device tree struct
> + * @limit: the end of the region for the struct
> + * structural checks on device_tree
> + */
> +static int check_v123_tree(u32 *start, u32 *limit)
What is the point of this check? If the device tree is corrupt, we're
stuffed anyway, so why bother?
> +{
> + u32 len;
> + int depth = 0;
> + u32 *dtp = start;
> +
> + while (dtp < limit)
> + switch (*dtp) {
> + case OF_DT_END:
> + if (depth)
> + return -1;
> + return ++dtp - start;
> + case OF_DT_NOP:
> + dtp++;
> + break;
> + case OF_DT_END_NODE:
> + dtp++;
> + depth--;
> + break;
> + case OF_DT_BEGIN_NODE:
> + len = strlen((char *)(++dtp));
> + /* check path is suffix to previous? */
> + dtp += 1 + (len / 4);
> + depth++;
> + break;
> + case OF_DT_PROP:
> + len = dtp[1];
> + dtp += 3;
> + if ((len >= 8) && ((long)dtp & 4))
> + dtp++;
> + dtp += (len + 3) / 4;
> + break;
> + default:
> + return -1;
> + }
> + return -1; /* no OF_DT_END */
> +}
> +#endif
> +
> +/**
> + * nop_to_v16 - add %OF_DT_NOP to hide alignment differences
> + * @dtp: pointer to the beginning of the struct area to modify
> + * insert %OF_DT_NOP into the dt_struct @dtp to make it v16 from v1, 2, or 3.
> + */
> +static int nop_to_v16(u32 *dtp)
> +{
> + int nops = 0;
> + char *p, *s;
> + int len;
> + u32 *next;
> +
> + while (*dtp != OF_DT_END)
> + switch (*dtp) {
> + case OF_DT_BEGIN_NODE:
> + /* v2 & v3 names are full path, v16+ is relative */
> + p = (char *)(++dtp);
> + len = strlen(p);
> + next = dtp + 1 + len / 4;
> +
> + for (s = p + len; *s != '/'; s--)
> + if (s == p)
> + fatal("name %s has no '/'", p);
> +
> + len -= s++ - p; /* not the slash but the nul */
> + memmove(p, s, len);
> + while (len % 4)
> + p[len++] = '\0';
> + dtp += len / 4;
> + while (dtp != next) {
> + *dtp++ = OF_DT_NOP;
> + nops++;
> + }
> + break;
> + case OF_DT_PROP:
> + /* convert from align_8 to align_4 via prefixing nop */
> + len = dtp[1];
> + if ((len >= 8) && !((long)dtp & 4)) {
> + memmove(dtp+1, dtp, 12);
> + *dtp++ = OF_DT_NOP;
> + nops++;
> + }
> + dtp += 3 + (len + 3)/4;
> + break;
> + default:
> + fatal("%s: unrecognised tag %d at %p\n", __FUNCTION__,
> + *dtp, dtp);
> + case OF_DT_NOP:
> + nops ++;
> + /* fall through */
> + case OF_DT_END_NODE:
> + dtp ++;
> + break;
> + }
> + return nops;
> +}
> +
> +#if MIN_VERSION < 3 || OUT_VERSION > 16
> +/**
> + * move_nops_fwd - move nops in a v16 dt_struct to the beginning
> + * @start - device tree starting address
> + * @count - number of %OF_DT_NOP cells to move
> + */
> +static void move_nops_fwd(u32 *start, int count)
What on earth is the point of this. The NOPs are perfectly valid
scattered within the tree, why go to all this trouble to shuffle them
about.
> +{
> + u32 *dtp = start;
> + int len;
> + while (count)
> + switch (*dtp) {
> + case OF_DT_NOP:
> + memmove(start+1,start,(dtp-start) * 4);
> + *start++ = OF_DT_NOP;
> + dtp++;
> + count--;
> + break;
> + case OF_DT_END_NODE:
> + dtp++;
> + break;
> + case OF_DT_BEGIN_NODE:
> + len = strlen((char *)(++dtp));
> + dtp += 1 + len / 4;
> + break;
> + case OF_DT_PROP:
> + len = dtp[1];
> + dtp += 3 + (len + 3) / 4;
> + break;
> + case OF_DT_END:
> + fatal("Not enough nops -- need %d more\n", count);
> + return;
> + default:
> + fatal("%s: unknown tag %d at %p", __FUNCTION__, *dtp, dtp)
> + }
> +}
> +#endif
> +
> +/**
> + * conv_flattree_inplace upgrade the version of a boot_param_header
> + * @tree: pointer to the device tree header to convert
> + *
> + * Converts a v1, 2, 3 device tree (of at least MIN_VERSION)
> + * in place to OUT_VERSION (16) format, usable by flatdevtree.c
> + */
> +void conv_flattree_inplace(struct boot_param_header *tree)
> +{
> + u32 *dtp;
> + u32 need = 0, nops;
> + int slen;
> +
> + if (tree->magic != OF_DT_HEADER)
> + fatal("%s: no magic", __FUNCTION__);
More pointless tests...
> + if (tree->last_comp_version > 3)
> + return; /* don't know what to do */
Rather, don't need to do anything.
> +
> + if (tree->version < MIN_VERSION) {
> + printf("%s: Warning: can't handle version %d tree\n",
> + __FUNCTION__, tree->version);
> + return;
> + }
> +
> + if (tree->version < 2)
> + need++; /* boot_cpu_id */
> +
> + if (tree->version < 3)
> + need++; /* dt_string_size */
> +
> + if (OUT_VERSION > 16)
> + need++; /* dt_struct_size */
> +
> + dtp = (void *)tree + tree->off_dt_struct;
> +
> + slen = check_v123_tree(dtp, (void *)tree + tree->totalsize);
> + if (slen < 0)
> + fatal("device tree check failed\n");
> +
> + nops = nop_to_v16(dtp);
> +
> + if (need & 1) /* keep 8 byte alignment of mem reserve */
> + need++;
> +
> + if (need > nops)
> + fatal("Didn't find enough space to add new header fields\n\r"
> + "(needed %d found %d tree %p)", need, nops, tree);
> +
> + /* ok now compress the dtb struct */
> + move_nops_fwd(dtp, need);
> + dtp += need;
> +
> + /*
> + * move mem_rsvmap and dt_strings if they are before dt_struct
> + * onto our nops . Adjust start addresses for the 3 sections.
> + */
Hrm. Do we really need to worry about this case. You may be
producing v2 trees in kexec-tools, but do they actually have the
blocks out of order? dtc certainly never produced them that way.
> + if ((tree->off_mem_rsvmap < tree->off_dt_struct) ||
> + (tree->off_dt_strings < tree->off_dt_struct)) {
> + int start, end;
> + void *ptr;
> +
> + if (tree->off_mem_rsvmap < tree->off_dt_strings)
> + start = tree->off_mem_rsvmap;
> + else
> + start = tree->off_dt_strings;
> +
> + end = tree->off_dt_struct;
> + ptr = (void *)tree + start;
> +
> + memmove(ptr + 4 * need, ptr, end - start);
> +
> + if (tree->off_mem_rsvmap < tree->off_dt_struct)
> + tree->off_mem_rsvmap += 4 * need;
> + if (tree->off_dt_strings < tree->off_dt_struct)
> + tree->off_dt_strings += 4 * need;
> + }
> + tree->off_dt_struct += 4 * need;
> +
> + /* ok now we have space to extend the header. */
> + if (tree->version < 2 && MIN_VERSION < 2) {
> + tree->boot_cpuid_phys = 0; /* default, caller can fix */
> + }
> +
> + /* calculate size of dt_strings_size */
> + if (tree->version < 3 && MIN_VERSION < 3) {
> + int end = tree->totalsize;
> +
> + if (tree->off_dt_strings < tree->off_mem_rsvmap)
> + end = tree->off_mem_rsvmap;
> +
> + if ((tree->off_dt_strings < tree->off_dt_struct) &&
> + (end > tree->off_dt_struct))
> + end = tree->off_dt_struct;
> +
> + tree->dt_strings_size = end - tree->off_dt_strings;
> + }
> +
> +#if OUT_VERSION > 16
> + tree->dt_struct_size = 4 * slen;
> +#endif
> +
> + tree->version = OUT_VERSION;
> + tree->last_comp_version = OUT_COMPAT;
> +
> + return;
> +}
> Index: kernel/arch/powerpc/boot/Makefile
> ===================================================================
> --- kernel.orig/arch/powerpc/boot/Makefile 2007-09-20 17:42:24.000000000 -0500
> +++ kernel/arch/powerpc/boot/Makefile 2007-09-20 17:49:04.000000000 -0500
> @@ -42,7 +42,7 @@ $(addprefix $(obj)/,$(zlib) gunzip_util.
> $(addprefix $(obj)/,$(zliblinuxheader)) $(addprefix $(obj)/,$(zlibheader))
>
> src-wlib := string.S crt0.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \
> - marshal.c memranges.c kexec.c \
> + flatdevtree_conv.c marshal.c memranges.c kexec.c \
> ns16550.c serial.c simple_alloc.c div64.S util.S \
> gunzip_util.c elf_util.c $(zlib) devtree.c oflib.c ofconsole.c \
> 4xx.c ebony.c mv64x60.c mpsc.c mv64x60_i2c.c cuboot.c bamboo.c \
> Index: kernel/arch/powerpc/boot/ops.h
> ===================================================================
> --- kernel.orig/arch/powerpc/boot/ops.h 2007-09-20 17:42:24.000000000 -0500
> +++ kernel/arch/powerpc/boot/ops.h 2007-09-20 17:49:04.000000000 -0500
> @@ -81,7 +81,10 @@ struct loader_info {
> };
> extern struct loader_info loader_info;
>
> +struct boot_param_header;
> +
> void start(void);
> +void conv_flattree_inplace(struct boot_param_header *tree);
> int ft_init(void *dt_blob, unsigned int max_size, unsigned int max_find_device);
> int serial_console_init(void);
> int ns16550_console_init(void *devp, struct serial_console_data *scdp);
> Index: kernel/arch/powerpc/boot/kexec.c
> ===================================================================
> --- kernel.orig/arch/powerpc/boot/kexec.c 2007-09-20 17:42:24.000000000 -0500
> +++ kernel/arch/powerpc/boot/kexec.c 2007-09-20 17:49:04.000000000 -0500
> @@ -99,6 +99,7 @@ void kexec_platform_init(struct boot_par
> move_slaves_up();
>
> setup_initial_heap();
> + conv_flattree_inplace(dt_blob);
> init_flat_tree(dt_blob);
> /*
> * drivers can malloc and read the tree, but not realloc later
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev at ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
>
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
More information about the Linuxppc-dev
mailing list