Index: kexec-tools-2.0.0/kexec/arch/ppc64/fs2dt.c =================================================================== --- kexec-tools-2.0.0.orig/kexec/arch/ppc64/fs2dt.c 2011-05-31 01:29:40.000000000 -0500 +++ kexec-tools-2.0.0/kexec/arch/ppc64/fs2dt.c 2011-05-31 01:32:55.447240900 -0500 @@ -35,13 +35,14 @@ #define MAXPATH 1024 /* max path name length */ #define NAMESPACE 16384 /* max bytes for property names */ -#define TREEWORDS 131070 /* max 32 bit words for property values */ +#define INIT_TREE_WORDS 65536 /* Initial num words for prop values */ #define MEMRESERVE 256 /* max number of reserved memory blocks */ -#define MAX_MEMORY_RANGES 1024 +#define MEM_RANGE_CHUNK_SZ 2048 /* Initial num dwords for mem ranges */ static char pathname[MAXPATH], *pathstart; static char propnames[NAMESPACE] = { 0 }; -static unsigned dtstruct[TREEWORDS] __attribute__ ((aligned (8))), *dt; +static unsigned *dt_base, *dt; +static unsigned int dt_cur_size; static unsigned long long mem_rsrv[2*MEMRESERVE] = { 0, 0 }; static int crash_param = 0; @@ -51,6 +52,28 @@ extern mem_rgns_t usablemem_rgns; static struct bootblock bb[1]; +/* Before we add something to the dt, reserve N words using this. + * If there isn't enough room, it's realloced -- and you don't overflow and + * splat bits of your heap. + */ +void dt_reserve(unsigned **dt_ptr, unsigned words) +{ + if (((*dt_ptr - dt_base) + words) >= dt_cur_size) { + int offset; + unsigned int new_size = dt_cur_size + INIT_TREE_WORDS; + unsigned *new_dt = realloc(dt_base, new_size*4); + + if (!new_dt) + die("unrecoverable error: Can't realloc %d bytes for " + "device tree\n", new_size*4); + offset = *dt_ptr - dt_base; + dt_base = new_dt; + dt_cur_size = new_size; + *dt_ptr = dt_base + offset; + memset(*dt_ptr, 0, (new_size - offset)*4); + } +} + void reserve(unsigned long long where, unsigned long long length) { size_t offset; @@ -126,7 +149,8 @@ { char fname[MAXPATH], *bname; uint64_t buf[32]; - uint64_t ranges[2*MAX_MEMORY_RANGES]; + uint64_t *ranges; + int ranges_size = MEM_RANGE_CHUNK_SZ; uint64_t base, end, loc_base, loc_end; int range, rlen = 0, i; int rngs_cnt, tmp_indx; @@ -142,6 +166,11 @@ die("unrecoverable error: error seeking in \"%s\": %s\n", pathname, strerror(errno)); + ranges = malloc(ranges_size*8); + if (!ranges) + die("unrecoverable error: can't alloc %d bytes for ranges.\n", + ranges_size*8); + rlen = 0; for (i = 0; i < num_of_lmbs; i++) { if (read(fd, buf, 24) < 0) @@ -157,36 +186,71 @@ rngs_cnt = 0; for (range = 0; range < usablemem_rgns.size; range++) { + int add = 0; loc_base = usablemem_rgns.ranges[range].start; loc_end = usablemem_rgns.ranges[range].end; if (loc_base >= base && loc_end <= end) { - ranges[rlen++] = loc_base; - ranges[rlen++] = loc_end - loc_base; - rngs_cnt++; + add = 1; } else if (base < loc_end && end > loc_base) { if (loc_base < base) loc_base = base; if (loc_end > end) loc_end = end; + add = 1; + } + + if (add) { + if (rlen >= (ranges_size-2)) { + ranges_size += MEM_RANGE_CHUNK_SZ; + ranges = realloc(ranges, ranges_size*8); + if (!ranges) + die("unrecoverable error: can't" + " realloc %d bytes for" + " ranges.\n", + ranges_size*8); + } ranges[rlen++] = loc_base; ranges[rlen++] = loc_end - loc_base; rngs_cnt++; } } - /* Store the count of (base, size) duple */ - ranges[tmp_indx] = rngs_cnt; + if (rngs_cnt == 0) { + /* We still need to add a counter for every LMB because + * the kernel parsing code is dumb. We just have + * a zero in this case, with no following base/len. + */ + ranges[tmp_indx] = 0; + /* rlen is already just tmp_indx+1 as we didn't write + * anything. Check array size here, as we'll probably + * go on for a while writing zeros now. + */ + if (rlen >= (ranges_size-1)) { + ranges_size += MEM_RANGE_CHUNK_SZ; + ranges = realloc(ranges, ranges_size*8); + if (!ranges) + die("unrecoverable error: can't" + " realloc %d bytes for" + " ranges.\n", + ranges_size*8); + } + } else { + /* Store the count of (base, size) duple */ + ranges[tmp_indx] = rngs_cnt; + } } rlen = rlen * sizeof(uint64_t); /* * Add linux,drconf-usable-memory property. */ + dt_reserve(&dt, 4+((rlen + 3)/4)); *dt++ = 3; *dt++ = rlen; *dt++ = propnum("linux,drconf-usable-memory"); if ((rlen >= 8) && ((unsigned long)dt & 0x4)) dt++; - memcpy(dt, &ranges, rlen); + memcpy(dt, ranges, rlen); + free(ranges); dt += (rlen + 3)/4; } @@ -194,7 +258,8 @@ { char fname[MAXPATH], *bname; uint64_t buf[2]; - uint64_t ranges[2*MAX_MEMORY_RANGES]; + uint64_t *ranges; + int ranges_size = MEM_RANGE_CHUNK_SZ; uint64_t base, end, loc_base, loc_end; int range, rlen = 0; @@ -221,17 +286,33 @@ base = buf[0]; end = base + buf[1]; + ranges = malloc(ranges_size*8); + if (!ranges) + die("unrecoverable error: can't alloc %d bytes for ranges.\n", + ranges_size*8); + for (range = 0; range < usablemem_rgns.size; range++) { + int add = 0; loc_base = usablemem_rgns.ranges[range].start; loc_end = usablemem_rgns.ranges[range].end; if (loc_base >= base && loc_end <= end) { - ranges[rlen++] = loc_base; - ranges[rlen++] = loc_end - loc_base; + add = 1; } else if (base < loc_end && end > loc_base) { if (loc_base < base) loc_base = base; if (loc_end > end) loc_end = end; + add = 1; + } + if (add) { + if (rlen >= (ranges_size-2)) { + ranges_size += MEM_RANGE_CHUNK_SZ; + ranges = realloc(ranges, ranges_size*8); + if (!ranges) + die("unrecoverable error: can't realloc" + "%d bytes for ranges.\n", + ranges_size*8); + } ranges[rlen++] = loc_base; ranges[rlen++] = loc_end - loc_base; } @@ -251,12 +332,14 @@ /* * No add linux,usable-memory property. */ + dt_reserve(&dt, 4+((rlen + 3)/4)); *dt++ = 3; *dt++ = rlen; *dt++ = propnum("linux,usable-memory"); if ((rlen >= 8) && ((unsigned long)dt & 0x4)) dt++; - memcpy(dt,&ranges,rlen); + memcpy(dt, ranges, rlen); + free(ranges); dt += (rlen + 3)/4; } @@ -313,6 +396,7 @@ len = statbuf.st_size; + dt_reserve(&dt, 4+((len + 3)/4)); *dt++ = 3; dt_len = dt; *dt++ = len; @@ -380,13 +464,17 @@ struct dirent **namelist; int numlist, i; struct stat statbuf; + int plen; + plen = *pathstart ? strlen(pathstart) : 1; + /* Reserve space for string packed to words; e.g. string length 10 + * occupies 3 words, length 12 occupies 4 (for terminating \0s). + * So round up & include the \0: + */ + dt_reserve(&dt, 1+((plen + 4)/4)); *dt++ = 1; strcpy((void *)dt, *pathstart ? pathstart : "/"); - while(*dt) - dt++; - if (dt[-1] & 0xff) - dt++; + dt += ((plen + 4)/4); numlist = scandir(pathname, &namelist, 0, comparefunc); if (numlist < 0) @@ -407,6 +495,8 @@ if (initrd_base && !strcmp(basename,"/chosen/")) { int len = 8; unsigned long long initrd_end; + + dt_reserve(&dt, 12); /* both props, of 6 words ea. */ *dt++ = 3; *dt++ = len; *dt++ = propnum("linux,initrd-start"); @@ -477,6 +567,7 @@ cmd_len = cmd_len + 1; /* add new bootargs */ + dt_reserve(&dt, 4+((cmd_len+3)/4)); *dt++ = 3; *dt++ = cmd_len; *dt++ = propnum("bootargs"); @@ -504,6 +595,7 @@ putnode(); } + dt_reserve(&dt, 1); *dt++ = 2; dn[-1] = '\0'; free(namelist); @@ -522,12 +614,21 @@ strcpy(pathname, "/proc/device-tree/"); pathstart = pathname + strlen(pathname); - dt = dtstruct; + + dt_cur_size = INIT_TREE_WORDS; + dt_base = malloc(dt_cur_size*4); + if (!dt_base) { + die("Can't malloc %d bytes for dt struct!\n", dt_cur_size*4); + } + memset(dt_base, 0, dt_cur_size*4); + + dt = dt_base; if (cmdline) strcpy(local_cmdline, cmdline); putnode(); + dt_reserve(&dt, 1); *dt++ = 9; len = sizeof(bb[0]); @@ -542,7 +643,7 @@ bb->off_dt_struct = bb->off_mem_rsvmap + len; - len = dt - dtstruct; + len = dt - dt_base; len *= sizeof(unsigned); bb->off_dt_strings = bb->off_dt_struct + len; @@ -562,10 +663,11 @@ tlen = bb->off_mem_rsvmap; memcpy(buf+tlen, mem_rsrv, bb->off_dt_struct - bb->off_mem_rsvmap); tlen = tlen + (bb->off_dt_struct - bb->off_mem_rsvmap); - memcpy(buf+tlen, dtstruct, bb->off_dt_strings - bb->off_dt_struct); + memcpy(buf+tlen, dt_base, bb->off_dt_strings - bb->off_dt_struct); tlen = tlen + (bb->off_dt_strings - bb->off_dt_struct); memcpy(buf+tlen, propnames, bb->totalsize - bb->off_dt_strings); tlen = tlen + bb->totalsize - bb->off_dt_strings; *sizep = tlen; + free(dt_base); return 0; }