Commit efa8a179 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] ppc64: make_room macro for ppc64 prom.c

From: Rusty Russell <rusty@rustcorp.com.au>

copy_device_tree calls inspect_node() which recurses to copy the Open Firmware
device tree.  It just copied into memory above the kernel for about 8MB, which
can overwrite the initrd, and/or get truncated on big machines.

The real solution is to allocate memory properly for all the prom_init memory
users.  However, that's quite a change.  This patch introduces a "make_room"
macro which inspect_node uses to get memory.  This does the same as before,
except skips over the initrd if neccessary.
parent d6d33678
...@@ -162,6 +162,8 @@ extern void enter_prom(struct prom_args *args); ...@@ -162,6 +162,8 @@ extern void enter_prom(struct prom_args *args);
extern void copy_and_flush(unsigned long dest, unsigned long src, extern void copy_and_flush(unsigned long dest, unsigned long src,
unsigned long size, unsigned long offset); unsigned long size, unsigned long offset);
static unsigned long __initdata initrd_start /* = 0 */, initrd_len;
unsigned long dev_tree_size; unsigned long dev_tree_size;
unsigned long _get_PIR(void); unsigned long _get_PIR(void);
...@@ -1566,9 +1568,34 @@ check_display(unsigned long mem) ...@@ -1566,9 +1568,34 @@ check_display(unsigned long mem)
return DOUBLEWORD_ALIGN(mem); return DOUBLEWORD_ALIGN(mem);
} }
static unsigned long __init /* Return (relocated) pointer to this much memory: skips initrd if any. */
static void *__make_room(unsigned long *mem_start, unsigned long *mem_end,
unsigned long needed, unsigned long align)
{
void *ret;
unsigned long offset = reloc_offset();
*mem_start = ALIGN(*mem_start, align);
if (*mem_start + needed > *mem_end) {
if (*mem_end != RELOC(initrd_start))
prom_panic(RELOC("No memory for copy_device_tree"));
*mem_start = RELOC(initrd_start) + RELOC(initrd_len);
/* We can't pass huge values to OF, so use 1G. */
*mem_end = *mem_start + 1024*1024*1024;
}
ret = (void *)*mem_start;
*mem_start += needed;
return ret;
}
#define make_room(startp, endp, type) \
__make_room(startp, endp, sizeof(type), __alignof__(type))
static void __init
inspect_node(phandle node, struct device_node *dad, inspect_node(phandle node, struct device_node *dad,
unsigned long mem_start, unsigned long mem_end, unsigned long *mem_start, unsigned long *mem_end,
struct device_node ***allnextpp) struct device_node ***allnextpp)
{ {
int l; int l;
...@@ -1579,9 +1606,9 @@ inspect_node(phandle node, struct device_node *dad, ...@@ -1579,9 +1606,9 @@ inspect_node(phandle node, struct device_node *dad,
unsigned char *valp; unsigned char *valp;
unsigned long offset = reloc_offset(); unsigned long offset = reloc_offset();
np = (struct device_node *) mem_start; np = make_room(mem_start, mem_end, struct device_node);
mem_start += sizeof(struct device_node);
memset(np, 0, sizeof(*np)); memset(np, 0, sizeof(*np));
np->node = node; np->node = node;
**allnextpp = PTRUNRELOC(np); **allnextpp = PTRUNRELOC(np);
*allnextpp = &np->allnext; *allnextpp = &np->allnext;
...@@ -1599,19 +1626,22 @@ inspect_node(phandle node, struct device_node *dad, ...@@ -1599,19 +1626,22 @@ inspect_node(phandle node, struct device_node *dad,
prev_propp = &np->properties; prev_propp = &np->properties;
prev_name = RELOC(""); prev_name = RELOC("");
for (;;) { for (;;) {
pp = (struct property *) mem_start; /* 32 is max len of name including nul. */
namep = (char *) (pp + 1); namep = make_room(mem_start, mem_end, char[32]);
pp->name = PTRUNRELOC(namep);
if ((long) call_prom(RELOC("nextprop"), 3, 1, node, prev_name, if ((long) call_prom(RELOC("nextprop"), 3, 1, node, prev_name,
namep) <= 0) namep) <= 0) {
/* No more nodes: unwind alloc */
*mem_start = (unsigned long)namep;
break; break;
mem_start = DOUBLEWORD_ALIGN((unsigned long)namep + strlen(namep) + 1); }
/* Trim off some if we can */
*mem_start = DOUBLEWORD_ALIGN((unsigned long)namep
+ strlen(namep) + 1);
pp = make_room(mem_start, mem_end, struct property);
pp->name = PTRUNRELOC(namep);
prev_name = namep; prev_name = namep;
valp = (unsigned char *) mem_start;
pp->value = PTRUNRELOC(valp); pp->length = call_prom(RELOC("getproplen"), 2, 1, node, namep);
pp->length = (int)(long)
call_prom(RELOC("getprop"), 4, 1, node, namep,
valp, mem_end - mem_start);
if (pp->length < 0) if (pp->length < 0)
continue; continue;
if (pp->length > MAX_PROPERTY_LENGTH) { if (pp->length > MAX_PROPERTY_LENGTH) {
...@@ -1630,7 +1660,9 @@ inspect_node(phandle node, struct device_node *dad, ...@@ -1630,7 +1660,9 @@ inspect_node(phandle node, struct device_node *dad,
continue; continue;
} }
mem_start = DOUBLEWORD_ALIGN(mem_start + pp->length); valp = __make_room(mem_start, mem_end, pp->length, 1);
pp->value = PTRUNRELOC(valp);
call_prom(RELOC("getprop"), 4, 1, node, namep,valp,pp->length);
*prev_propp = PTRUNRELOC(pp); *prev_propp = PTRUNRELOC(pp);
prev_propp = &pp->next; prev_propp = &pp->next;
} }
...@@ -1654,23 +1686,27 @@ inspect_node(phandle node, struct device_node *dad, ...@@ -1654,23 +1686,27 @@ inspect_node(phandle node, struct device_node *dad,
*prev_propp = 0; *prev_propp = 0;
/* get the node's full name */ /* get the node's full name */
namep = (char *)*mem_start;
l = (long) call_prom(RELOC("package-to-path"), 3, 1, node, l = (long) call_prom(RELOC("package-to-path"), 3, 1, node,
(char *) mem_start, mem_end - mem_start); namep, *mem_end - *mem_start);
if (l >= 0) { if (l >= 0) {
np->full_name = PTRUNRELOC((char *) mem_start); /* Didn't fit? Get more room. */
*(char *)(mem_start + l) = 0; if (l+1 > *mem_end - *mem_start) {
mem_start = DOUBLEWORD_ALIGN(mem_start + l + 1); namep = __make_room(mem_start, mem_end, l+1, 1);
call_prom(RELOC("package-to-path"),3,1,node,namep,l);
}
np->full_name = PTRUNRELOC(namep);
namep[l] = '\0';
*mem_start = DOUBLEWORD_ALIGN(*mem_start + l + 1);
} }
/* do all our children */ /* do all our children */
child = call_prom(RELOC("child"), 1, 1, node); child = call_prom(RELOC("child"), 1, 1, node);
while (child != (phandle)0) { while (child != (phandle)0) {
mem_start = inspect_node(child, np, mem_start, mem_end, inspect_node(child, np, mem_start, mem_end,
allnextpp); allnextpp);
child = call_prom(RELOC("peer"), 1, 1, child); child = call_prom(RELOC("peer"), 1, 1, child);
} }
return mem_start;
} }
/* /*
...@@ -1680,20 +1716,22 @@ static unsigned long __init ...@@ -1680,20 +1716,22 @@ static unsigned long __init
copy_device_tree(unsigned long mem_start) copy_device_tree(unsigned long mem_start)
{ {
phandle root; phandle root;
unsigned long new_start;
struct device_node **allnextp; struct device_node **allnextp;
unsigned long offset = reloc_offset(); unsigned long offset = reloc_offset();
unsigned long mem_end = mem_start + (8<<20); unsigned long mem_end = RELOC(initrd_start);
/* We pass mem_end-mem_start to OF: keep it well under 32-bit */
if (!mem_end)
mem_end = mem_start + 1024*1024*1024;
root = call_prom(RELOC("peer"), 1, 1, (phandle)0); root = call_prom(RELOC("peer"), 1, 1, (phandle)0);
if (root == (phandle)0) { if (root == (phandle)0) {
prom_panic(RELOC("couldn't get device tree root\n")); prom_panic(RELOC("couldn't get device tree root\n"));
} }
allnextp = &RELOC(allnodes); allnextp = &RELOC(allnodes);
mem_start = DOUBLEWORD_ALIGN(mem_start); inspect_node(root, 0, &mem_start, &mem_end, &allnextp);
new_start = inspect_node(root, 0, mem_start, mem_end, &allnextp);
*allnextp = 0; *allnextp = 0;
return new_start; return mem_start;
} }
/* Verify bi_recs are good */ /* Verify bi_recs are good */
...@@ -1731,7 +1769,8 @@ prom_bi_rec_reserve(unsigned long mem) ...@@ -1731,7 +1769,8 @@ prom_bi_rec_reserve(unsigned long mem)
switch (rec->tag) { switch (rec->tag) {
#ifdef CONFIG_BLK_DEV_INITRD #ifdef CONFIG_BLK_DEV_INITRD
case BI_INITRD: case BI_INITRD:
lmb_reserve(rec->data[0], rec->data[1]); RELOC(initrd_start) = rec->data[0];
RELOC(initrd_len) = rec->data[1];
break; break;
#endif /* CONFIG_BLK_DEV_INITRD */ #endif /* CONFIG_BLK_DEV_INITRD */
} }
...@@ -1885,6 +1924,11 @@ prom_init(unsigned long r3, unsigned long r4, unsigned long pp, ...@@ -1885,6 +1924,11 @@ prom_init(unsigned long r3, unsigned long r4, unsigned long pp,
RELOC(klimit) = mem + offset; RELOC(klimit) = mem + offset;
lmb_reserve(0, __pa(RELOC(klimit))); lmb_reserve(0, __pa(RELOC(klimit)));
#ifdef CONFIG_BLK_DEV_INITRD
/* If this didn't cover the initrd, do so now */
if (mem < RELOC(initrd_start))
lmb_reserve(RELOC(initrd_start), RELOC(initrd_len));
#endif /* CONFIG_BLK_DEV_INITRD */
if (_systemcfg->platform == PLATFORM_PSERIES) if (_systemcfg->platform == PLATFORM_PSERIES)
prom_initialize_tce_table(); prom_initialize_tce_table();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment