Commit 34153fa3 authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by Paul Mackerras
Browse files

[PATCH] flattened device tree changes



This patch updates the format of the flattened device-tree passed
between the boot trampoline and the kernel to support a more compact
representation, for use by embedded systems mostly.

Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
parent e28f7faf
Loading
Loading
Loading
Loading
+145 −30
Original line number Diff line number Diff line
@@ -625,8 +625,8 @@ void __init finish_device_tree(void)

static inline char *find_flat_dt_string(u32 offset)
{
	return ((char *)initial_boot_params) + initial_boot_params->off_dt_strings
		+ offset;
	return ((char *)initial_boot_params) +
		initial_boot_params->off_dt_strings + offset;
}

/**
@@ -635,25 +635,32 @@ static inline char *find_flat_dt_string(u32 offset)
 * unflatten the tree
 */
static int __init scan_flat_dt(int (*it)(unsigned long node,
					 const char *full_path, void *data),
					 const char *uname, int depth,
					 void *data),
			       void *data)
{
	unsigned long p = ((unsigned long)initial_boot_params) +
		initial_boot_params->off_dt_struct;
	int rc = 0;
	int depth = -1;

	do {
		u32 tag = *((u32 *)p);
		char *pathp;
		
		p += 4;
		if (tag == OF_DT_END_NODE)
		if (tag == OF_DT_END_NODE) {
			depth --;
			continue;
		}
		if (tag == OF_DT_NOP)
			continue;
		if (tag == OF_DT_END)
			break;
		if (tag == OF_DT_PROP) {
			u32 sz = *((u32 *)p);
			p += 8;
			if (initial_boot_params->version < 0x10)
				p = _ALIGN(p, sz >= 8 ? 8 : 4);
			p += sz;
			p = _ALIGN(p, 4);
@@ -664,9 +671,18 @@ static int __init scan_flat_dt(int (*it)(unsigned long node,
			       " device tree !\n", tag);
			return -EINVAL;
		}
		depth++;
		pathp = (char *)p;
		p = _ALIGN(p + strlen(pathp) + 1, 4);
		rc = it(p, pathp, data);
		if ((*pathp) == '/') {
			char *lp, *np;
			for (lp = NULL, np = pathp; *np; np++)
				if ((*np) == '/')
					lp = np+1;
			if (lp != NULL)
				pathp = lp;
		}
		rc = it(p, pathp, depth, data);
		if (rc != 0)
			break;		
	} while(1);
@@ -689,17 +705,21 @@ static void* __init get_flat_dt_prop(unsigned long node, const char *name,
		const char *nstr;

		p += 4;
		if (tag == OF_DT_NOP)
			continue;
		if (tag != OF_DT_PROP)
			return NULL;

		sz = *((u32 *)p);
		noff = *((u32 *)(p + 4));
		p += 8;
		if (initial_boot_params->version < 0x10)
			p = _ALIGN(p, sz >= 8 ? 8 : 4);

		nstr = find_flat_dt_string(noff);
		if (nstr == NULL) {
			printk(KERN_WARNING "Can't find property index name !\n");
			printk(KERN_WARNING "Can't find property index"
			       " name !\n");
			return NULL;
		}
		if (strcmp(name, nstr) == 0) {
@@ -727,13 +747,16 @@ static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size,
static unsigned long __init unflatten_dt_node(unsigned long mem,
					      unsigned long *p,
					      struct device_node *dad,
					      struct device_node ***allnextpp)
					      struct device_node ***allnextpp,
					      unsigned long fpsize)
{
	struct device_node *np;
	struct property *pp, **prev_pp = NULL;
	char *pathp;
	u32 tag;
	unsigned int l;
	unsigned int l, allocl;
	int has_name = 0;
	int new_format = 0;

	tag = *((u32 *)(*p));
	if (tag != OF_DT_BEGIN_NODE) {
@@ -742,21 +765,62 @@ static unsigned long __init unflatten_dt_node(unsigned long mem,
	}
	*p += 4;
	pathp = (char *)*p;
	l = strlen(pathp) + 1;
	l = allocl = strlen(pathp) + 1;
	*p = _ALIGN(*p + l, 4);

	np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + l,
	/* version 0x10 has a more compact unit name here instead of the full
	 * path. we accumulate the full path size using "fpsize", we'll rebuild
	 * it later. We detect this because the first character of the name is
	 * not '/'.
	 */
	if ((*pathp) != '/') {
		new_format = 1;
		if (fpsize == 0) {
			/* root node: special case. fpsize accounts for path
			 * plus terminating zero. root node only has '/', so
			 * fpsize should be 2, but we want to avoid the first
			 * level nodes to have two '/' so we use fpsize 1 here
			 */
			fpsize = 1;
			allocl = 2;
		} else {
			/* account for '/' and path size minus terminal 0
			 * already in 'l'
			 */
			fpsize += l;
			allocl = fpsize;
		}
	}


	np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
				__alignof__(struct device_node));
	if (allnextpp) {
		memset(np, 0, sizeof(*np));
		np->full_name = ((char*)np) + sizeof(struct device_node);
		if (new_format) {
			char *p = np->full_name;
			/* rebuild full path for new format */
			if (dad && dad->parent) {
				strcpy(p, dad->full_name);
#ifdef DEBUG
				if ((strlen(p) + l + 1) != allocl) {
					DBG("%s: p: %d, l: %d, a: %d\n",
					    pathp, strlen(p), l, allocl);
				}
#endif
				p += strlen(p);
			}
			*(p++) = '/';
			memcpy(p, pathp, l);
		} else
			memcpy(np->full_name, pathp, l);
		prev_pp = &np->properties;
		**allnextpp = np;
		*allnextpp = &np->allnext;
		if (dad != NULL) {
			np->parent = dad;
			/* we temporarily use the `next' field as `last_child'. */
			/* we temporarily use the next field as `last_child'*/
			if (dad->next == 0)
				dad->child = np;
			else
@@ -770,18 +834,26 @@ static unsigned long __init unflatten_dt_node(unsigned long mem,
		char *pname;

		tag = *((u32 *)(*p));
		if (tag == OF_DT_NOP) {
			*p += 4;
			continue;
		}
		if (tag != OF_DT_PROP)
			break;
		*p += 4;
		sz = *((u32 *)(*p));
		noff = *((u32 *)((*p) + 4));
		*p = _ALIGN((*p) + 8, sz >= 8 ? 8 : 4);
		*p += 8;
		if (initial_boot_params->version < 0x10)
			*p = _ALIGN(*p, sz >= 8 ? 8 : 4);

		pname = find_flat_dt_string(noff);
		if (pname == NULL) {
			printk("Can't find property name in list !\n");
			break;
		}
		if (strcmp(pname, "name") == 0)
			has_name = 1;
		l = strlen(pname) + 1;
		pp = unflatten_dt_alloc(&mem, sizeof(struct property),
					__alignof__(struct property));
@@ -801,6 +873,36 @@ static unsigned long __init unflatten_dt_node(unsigned long mem,
		}
		*p = _ALIGN((*p) + sz, 4);
	}
	/* with version 0x10 we may not have the name property, recreate
	 * it here from the unit name if absent
	 */
	if (!has_name) {
		char *p = pathp, *ps = pathp, *pa = NULL;
		int sz;

		while (*p) {
			if ((*p) == '@')
				pa = p;
			if ((*p) == '/')
				ps = p + 1;
			p++;
		}
		if (pa < ps)
			pa = p;
		sz = (pa - ps) + 1;
		pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
					__alignof__(struct property));
		if (allnextpp) {
			pp->name = "name";
			pp->length = sz;
			pp->value = (unsigned char *)(pp + 1);
			*prev_pp = pp;
			prev_pp = &pp->next;
			memcpy(pp->value, ps, sz - 1);
			((char *)pp->value)[sz - 1] = 0;
			DBG("fixed up name for %s -> %s\n", pathp, pp->value);
		}
	}
	if (allnextpp) {
		*prev_pp = NULL;
		np->name = get_property(np, "name", NULL);
@@ -812,7 +914,7 @@ static unsigned long __init unflatten_dt_node(unsigned long mem,
			np->type = "<NULL>";
	}
	while (tag == OF_DT_BEGIN_NODE) {
		mem = unflatten_dt_node(mem, p, np, allnextpp);
		mem = unflatten_dt_node(mem, p, np, allnextpp, fpsize);
		tag = *((u32 *)(*p));
	}
	if (tag != OF_DT_END_NODE) {
@@ -842,21 +944,27 @@ void __init unflatten_device_tree(void)
	/* First pass, scan for size */
	start = ((unsigned long)initial_boot_params) +
		initial_boot_params->off_dt_struct;
	size = unflatten_dt_node(0, &start, NULL, NULL);
	size = unflatten_dt_node(0, &start, NULL, NULL, 0);
	size = (size | 3) + 1;

	DBG("  size is %lx, allocating...\n", size);

	/* Allocate memory for the expanded device tree */
	mem = (unsigned long)abs_to_virt(lmb_alloc(size,
	mem = (unsigned long)abs_to_virt(lmb_alloc(size + 4,
						   __alignof__(struct device_node)));
	((u32 *)mem)[size / 4] = 0xdeadbeef;

	DBG("  unflattening...\n", mem);

	/* Second pass, do actual unflattening */
	start = ((unsigned long)initial_boot_params) +
		initial_boot_params->off_dt_struct;
	unflatten_dt_node(mem, &start, NULL, &allnextp);
	unflatten_dt_node(mem, &start, NULL, &allnextp, 0);
	if (*((u32 *)start) != OF_DT_END)
		printk(KERN_WARNING "Weird tag at end of tree: %x\n", *((u32 *)start));
		printk(KERN_WARNING "Weird tag at end of tree: %08x\n", *((u32 *)start));
	if (((u32 *)mem)[size / 4] != 0xdeadbeef)
		printk(KERN_WARNING "End of tree marker overwritten: %08x\n",
		       ((u32 *)mem)[size / 4] );
	*allnextp = NULL;

	/* Get pointer to OF "/chosen" node for use everywhere */
@@ -880,7 +988,7 @@ void __init unflatten_device_tree(void)


static int __init early_init_dt_scan_cpus(unsigned long node,
					  const char *full_path, void *data)
					  const char *uname, int depth, void *data)
{
	char *type = get_flat_dt_prop(node, "device_type", NULL);
	u32 *prop;
@@ -947,13 +1055,15 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
}

static int __init early_init_dt_scan_chosen(unsigned long node,
					    const char *full_path, void *data)
					    const char *uname, int depth, void *data)
{
	u32 *prop;
	u64 *prop64;
	extern unsigned long memory_limit, tce_alloc_start, tce_alloc_end;

	if (strcmp(full_path, "/chosen") != 0)
	DBG("search \"chosen\", depth: %d, uname: %s\n", depth, uname);

	if (depth != 1 || strcmp(uname, "chosen") != 0)
		return 0;

	/* get platform type */
@@ -1003,18 +1113,20 @@ static int __init early_init_dt_scan_chosen(unsigned long node,
}

static int __init early_init_dt_scan_root(unsigned long node,
					  const char *full_path, void *data)
					  const char *uname, int depth, void *data)
{
	u32 *prop;

	if (strcmp(full_path, "/") != 0)
	if (depth != 0)
		return 0;

	prop = (u32 *)get_flat_dt_prop(node, "#size-cells", NULL);
	dt_root_size_cells = (prop == NULL) ? 1 : *prop;
	DBG("dt_root_size_cells = %x\n", dt_root_size_cells);

	prop = (u32 *)get_flat_dt_prop(node, "#address-cells", NULL);
	dt_root_addr_cells = (prop == NULL) ? 2 : *prop;
	DBG("dt_root_addr_cells = %x\n", dt_root_addr_cells);
	
	/* break now */
	return 1;
@@ -1042,7 +1154,7 @@ static unsigned long __init dt_mem_next_cell(int s, cell_t **cellp)


static int __init early_init_dt_scan_memory(unsigned long node,
					    const char *full_path, void *data)
					    const char *uname, int depth, void *data)
{
	char *type = get_flat_dt_prop(node, "device_type", NULL);
	cell_t *reg, *endp;
@@ -1058,7 +1170,9 @@ static int __init early_init_dt_scan_memory(unsigned long node,

	endp = reg + (l / sizeof(cell_t));

	DBG("memory scan node %s ...\n", full_path);
	DBG("memory scan node %s ..., reg size %ld, data: %x %x %x %x, ...\n",
	    uname, l, reg[0], reg[1], reg[2], reg[3]);

	while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
		unsigned long base, size;

@@ -1469,10 +1583,11 @@ struct device_node *of_find_node_by_path(const char *path)
	struct device_node *np = allnodes;

	read_lock(&devtree_lock);
	for (; np != 0; np = np->allnext)
	for (; np != 0; np = np->allnext) {
		if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0
		    && of_node_get(np))
			break;
	}
	read_unlock(&devtree_lock);
	return np;
}
+55 −33
Original line number Diff line number Diff line
@@ -1534,7 +1534,8 @@ static unsigned long __init dt_find_string(char *str)
 */
#define MAX_PROPERTY_NAME 64

static void __init scan_dt_build_strings(phandle node, unsigned long *mem_start,
static void __init scan_dt_build_strings(phandle node,
					 unsigned long *mem_start,
					 unsigned long *mem_end)
{
	unsigned long offset = reloc_offset();
@@ -1547,16 +1548,21 @@ static void __init scan_dt_build_strings(phandle node, unsigned long *mem_start,
	/* get and store all property names */
	prev_name = RELOC("");
	for (;;) {
		int rc;

		/* 64 is max len of name including nul. */
		namep = make_room(mem_start, mem_end, MAX_PROPERTY_NAME, 1);
		rc = call_prom("nextprop", 3, 1, node, prev_name, namep);
		if (rc != 1) {
		if (call_prom("nextprop", 3, 1, node, prev_name, namep) != 1) {
			/* No more nodes: unwind alloc */
			*mem_start = (unsigned long)namep;
			break;
		}

 		/* skip "name" */
 		if (strcmp(namep, RELOC("name")) == 0) {
 			*mem_start = (unsigned long)namep;
 			prev_name = RELOC("name");
 			continue;
 		}
		/* get/create string entry */
		soff = dt_find_string(namep);
		if (soff != 0) {
			*mem_start = (unsigned long)namep;
@@ -1571,7 +1577,7 @@ static void __init scan_dt_build_strings(phandle node, unsigned long *mem_start,

	/* do all our children */
	child = call_prom("child", 1, 1, node);
	while (child != (phandle)0) {
	while (child != 0) {
		scan_dt_build_strings(child, mem_start, mem_end);
		child = call_prom("peer", 1, 1, child);
	}
@@ -1580,16 +1586,13 @@ static void __init scan_dt_build_strings(phandle node, unsigned long *mem_start,
static void __init scan_dt_build_struct(phandle node, unsigned long *mem_start,
					unsigned long *mem_end)
{
	int l, align;
	phandle child;
	char *namep, *prev_name, *sstart, *p, *ep;
	char *namep, *prev_name, *sstart, *p, *ep, *lp, *path;
	unsigned long soff;
	unsigned char *valp;
	unsigned long offset = reloc_offset();
	char pname[MAX_PROPERTY_NAME];
	char *path;

	path = RELOC(prom_scratch);
	static char pname[MAX_PROPERTY_NAME];
	int l;

	dt_push_token(OF_DT_BEGIN_NODE, mem_start, mem_end);

@@ -1599,23 +1602,33 @@ static void __init scan_dt_build_struct(phandle node, unsigned long *mem_start,
		      namep, *mem_end - *mem_start);
	if (l >= 0) {
		/* Didn't fit?  Get more room. */
		if (l+1 > *mem_end - *mem_start) {
		if ((l+1) > (*mem_end - *mem_start)) {
			namep = make_room(mem_start, mem_end, l+1, 1);
			call_prom("package-to-path", 3, 1, node, namep, l);
		}
		namep[l] = '\0';

		/* Fixup an Apple bug where they have bogus \0 chars in the
		 * middle of the path in some properties
		 */
		for (p = namep, ep = namep + l; p < ep; p++)
			if (*p == '\0') {
				memmove(p, p+1, ep - p);
				ep--; l--;
				ep--; l--; p--;
			}
		*mem_start = _ALIGN(((unsigned long) namep) + strlen(namep) + 1, 4);

		/* now try to extract the unit name in that mess */
		for (p = namep, lp = NULL; *p; p++)
			if (*p == '/')
				lp = p + 1;
		if (lp != NULL)
			memmove(namep, lp, strlen(lp) + 1);
		*mem_start = _ALIGN(((unsigned long) namep) +
				    strlen(namep) + 1, 4);
	}

	/* get it again for debugging */
	path = RELOC(prom_scratch);
	memset(path, 0, PROM_SCRATCH_SIZE);
	call_prom("package-to-path", 3, 1, node, path, PROM_SCRATCH_SIZE-1);

@@ -1623,23 +1636,27 @@ static void __init scan_dt_build_struct(phandle node, unsigned long *mem_start,
	prev_name = RELOC("");
	sstart = (char *)RELOC(dt_string_start);
	for (;;) {
		int rc;

		rc = call_prom("nextprop", 3, 1, node, prev_name, pname);
		if (rc != 1)
		if (call_prom("nextprop", 3, 1, node, prev_name,
			      RELOC(pname)) != 1)
			break;

 		/* skip "name" */
 		if (strcmp(RELOC(pname), RELOC("name")) == 0) {
 			prev_name = RELOC("name");
 			continue;
 		}

		/* find string offset */
		soff = dt_find_string(pname);
		soff = dt_find_string(RELOC(pname));
		if (soff == 0) {
			prom_printf("WARNING: Can't find string index for <%s>, node %s\n",
				    pname, path);
			prom_printf("WARNING: Can't find string index for"
				    " <%s>, node %s\n", RELOC(pname), path);
			break;
		}
		prev_name = sstart + soff;

		/* get length */
		l = call_prom("getproplen", 2, 1, node, pname);
		l = call_prom("getproplen", 2, 1, node, RELOC(pname));

		/* sanity checks */
		if (l == PROM_ERROR)
@@ -1648,7 +1665,7 @@ static void __init scan_dt_build_struct(phandle node, unsigned long *mem_start,
			prom_printf("WARNING: ignoring large property ");
			/* It seems OF doesn't null-terminate the path :-( */
			prom_printf("[%s] ", path);
			prom_printf("%s length 0x%x\n", pname, l);
			prom_printf("%s length 0x%x\n", RELOC(pname), l);
			continue;
		}

@@ -1658,17 +1675,16 @@ static void __init scan_dt_build_struct(phandle node, unsigned long *mem_start,
		dt_push_token(soff, mem_start, mem_end);

		/* push property content */
		align = (l >= 8) ? 8 : 4;
		valp = make_room(mem_start, mem_end, l, align);
		call_prom("getprop", 4, 1, node, pname, valp, l);
		valp = make_room(mem_start, mem_end, l, 4);
		call_prom("getprop", 4, 1, node, RELOC(pname), valp, l);
		*mem_start = _ALIGN(*mem_start, 4);
	}

	/* Add a "linux,phandle" property. */
	soff = dt_find_string(RELOC("linux,phandle"));
	if (soff == 0)
		prom_printf("WARNING: Can't find string index for <linux-phandle>"
			    " node %s\n", path);
		prom_printf("WARNING: Can't find string index for"
			    " <linux-phandle> node %s\n", path);
	else {
		dt_push_token(OF_DT_PROP, mem_start, mem_end);
		dt_push_token(4, mem_start, mem_end);
@@ -1679,7 +1695,7 @@ static void __init scan_dt_build_struct(phandle node, unsigned long *mem_start,

	/* do all our children */
	child = call_prom("child", 1, 1, node);
	while (child != (phandle)0) {
	while (child != 0) {
		scan_dt_build_struct(child, mem_start, mem_end);
		child = call_prom("peer", 1, 1, child);
	}
@@ -1718,7 +1734,8 @@ static void __init flatten_device_tree(void)

	/* Build header and make room for mem rsv map */ 
	mem_start = _ALIGN(mem_start, 4);
	hdr = make_room(&mem_start, &mem_end, sizeof(struct boot_param_header), 4);
	hdr = make_room(&mem_start, &mem_end,
			sizeof(struct boot_param_header), 4);
	RELOC(dt_header_start) = (unsigned long)hdr;
	rsvmap = make_room(&mem_start, &mem_end, sizeof(mem_reserve_map), 8);

@@ -1731,11 +1748,11 @@ static void __init flatten_device_tree(void)
	namep = make_room(&mem_start, &mem_end, 16, 1);
	strcpy(namep, RELOC("linux,phandle"));
	mem_start = (unsigned long)namep + strlen(namep) + 1;
	RELOC(dt_string_end) = mem_start;

	/* Build string array */
	prom_printf("Building dt strings...\n"); 
	scan_dt_build_strings(root, &mem_start, &mem_end);
	RELOC(dt_string_end) = mem_start;

	/* Build structure */
	mem_start = PAGE_ALIGN(mem_start);
@@ -1750,9 +1767,11 @@ static void __init flatten_device_tree(void)
	hdr->totalsize = RELOC(dt_struct_end) - RELOC(dt_header_start);
	hdr->off_dt_struct = RELOC(dt_struct_start) - RELOC(dt_header_start);
	hdr->off_dt_strings = RELOC(dt_string_start) - RELOC(dt_header_start);
	hdr->dt_strings_size = RELOC(dt_string_end) - RELOC(dt_string_start);
	hdr->off_mem_rsvmap = ((unsigned long)rsvmap) - RELOC(dt_header_start);
	hdr->version = OF_DT_VERSION;
	hdr->last_comp_version = 1;
	/* Version 16 is not backward compatible */
	hdr->last_comp_version = 0x10;

	/* Reserve the whole thing and copy the reserve map in, we
	 * also bump mem_reserve_cnt to cause further reservations to
@@ -1808,6 +1827,9 @@ static void __init fixup_device_tree(void)
	/* does it need fixup ? */
	if (prom_getproplen(i2c, "interrupts") > 0)
		return;

	prom_printf("fixing up bogus interrupts for u3 i2c...\n");

	/* interrupt on this revision of u3 is number 0 and level */
	interrupts[0] = 0;
	interrupts[1] = 1;
+9 −5
Original line number Diff line number Diff line
@@ -22,13 +22,15 @@
#define RELOC(x)        (*PTRRELOC(&(x)))

/* Definitions used by the flattened device tree */
#define OF_DT_HEADER		0xd00dfeed	/* 4: version, 4: total size */
#define OF_DT_BEGIN_NODE	0x1		/* Start node: full name */
#define OF_DT_HEADER		0xd00dfeed	/* marker */
#define OF_DT_BEGIN_NODE	0x1		/* Start of node, full name */
#define OF_DT_END_NODE		0x2		/* End node */
#define OF_DT_PROP		0x3		/* Property: name off, size, content */
#define OF_DT_PROP		0x3		/* Property: name off, size,
						 * content */
#define OF_DT_NOP		0x4		/* nop */
#define OF_DT_END		0x9

#define OF_DT_VERSION		1
#define OF_DT_VERSION		0x10

/*
 * This is what gets passed to the kernel by prom_init or kexec
@@ -54,7 +56,9 @@ struct boot_param_header
	u32	version;		/* format version */
	u32	last_comp_version;	/* last compatible version */
	/* version 2 fields below */
	u32	boot_cpuid_phys;	/* Which physical CPU id we're booting on */
	u32	boot_cpuid_phys;	/* Physical CPU id we're booting on */
	/* version 3 fields below */
	u32	dt_strings_size;	/* size of the DT strings block */
};