Subject: Re: [PATCH 8/15] bootwrapper: convert flatdevtree to version 16

From: David Gibson <[email-masked]>
Date: Mon, 24 Sep 2007 13:36:12 +1000

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 <[email-masked]>
> ---
> 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 <[email-masked]>
> + */
> +#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
> [email-masked]
> 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