diff options
Diffstat (limited to '')
-rw-r--r-- | kexec/fs2dt.c | 852 |
1 files changed, 852 insertions, 0 deletions
diff --git a/kexec/fs2dt.c b/kexec/fs2dt.c new file mode 100644 index 0000000..993b73b --- /dev/null +++ b/kexec/fs2dt.c @@ -0,0 +1,852 @@ +/* + * fs2dt: creates a flattened device-tree + * + * Copyright (C) 2004,2005 Milton D Miller II, IBM Corporation + * Copyright (C) 2005 R Sharada (sharada@in.ibm.com), IBM Corporation + * + * 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 (version 2 of the License). + * + * 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _GNU_SOURCE + +#include <sys/types.h> +#include <sys/stat.h> + +#include <fcntl.h> +#include <dirent.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include "kexec.h" +#include "fs2dt.h" +#include "libfdt/fdt.h" + +#define MAXPATH 1024 /* max path name length */ +#define NAMESPACE 16384 /* max bytes for property names */ +#define INIT_TREE_WORDS 65536 /* Initial num words for prop values */ +#define MEMRESERVE 256 /* max number of reserved memory blocks */ +#define MEM_RANGE_CHUNK_SZ 2048 /* Initial num dwords for mem ranges */ + +static char pathname[MAXPATH]; +static char propnames[NAMESPACE] = { 0 }; +static unsigned *dt_base, *dt; +static unsigned int dt_cur_size; +static unsigned long long mem_rsrv[2*MEMRESERVE] = { 0ULL, 0ULL }; + +static int crash_param = 0; +static char local_cmdline[COMMAND_LINE_SIZE] = { "" }; + +extern unsigned char reuse_initrd; + +/* Used for enabling printing message from purgatory code + * Only has implemented for PPC64 */ +int my_debug; +int dt_no_old_root; + +/* This provides the behaviour of hte existing ppc64 implementation */ +static void pad_structure_block(size_t len) { +#ifdef NEED_STRUCTURE_BLOCK_EXTRA_PAD + if ((len >= 8) && ((unsigned long)dt & 0x4)) + dt++; +#endif +} + +/* 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. + */ +static void dt_reserve(unsigned **dt_ptr, unsigned words) +{ + unsigned int sz = INIT_TREE_WORDS; + + if (sz < words) + sz = words; + + if (((*dt_ptr - dt_base) + words) >= dt_cur_size) { + int offset; + unsigned int new_size = dt_cur_size + sz; + 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; + + for (offset = 0; be64_to_cpu(mem_rsrv[offset + 1]); offset += 2) + ; + + if (offset + 4 >= 2 * MEMRESERVE) + die("unrecoverable error: exhasuted reservation meta data\n"); + + mem_rsrv[offset] = cpu_to_be64(where); + mem_rsrv[offset + 1] = cpu_to_be64(length); + mem_rsrv[offset + 2] = mem_rsrv[offset + 3] = cpu_to_be64(0); +} + +/* look for properties we need to reserve memory space for */ +static void checkprop(char *name, unsigned *data, int len) +{ + static unsigned long long base, size, end; + + if ((data == NULL) && (base || size || end)) + die("unrecoverable error: no property data"); + else if (!strcmp(name, "linux,rtas-base")) + base = be32_to_cpu(*data); + else if (!strcmp(name, "opal-base-address")) + base = be64_to_cpu(*(unsigned long long *)data); + else if (!strcmp(name, "opal-runtime-size")) + size = be64_to_cpu(*(unsigned long long *)data); + else if (!strcmp(name, "linux,tce-base")) + base = be64_to_cpu(*(unsigned long long *) data); + else if (!strcmp(name, "rtas-size") || + !strcmp(name, "linux,tce-size")) + size = be32_to_cpu(*data); + else if (reuse_initrd && !strcmp(name, "linux,initrd-start")) { + if (len == 8) + base = be64_to_cpu(*(unsigned long long *) data); + else + base = be32_to_cpu(*data); + } else if (reuse_initrd && !strcmp(name, "linux,initrd-end")) { + if (len == 8) + end = be64_to_cpu(*(unsigned long long *) data); + else + end = be32_to_cpu(*data); + } + + if (size && end) + die("unrecoverable error: size and end set at same time\n"); + if (base && size) { + reserve(base, size); + base = size = 0; + } + if (base && end) { + reserve(base, end-base); + base = end = 0; + } +} + +/* + * return the property index for a property name, creating a new one + * if needed. + */ +static unsigned propnum(const char *name) +{ + unsigned offset = 0; + + while(propnames[offset]) + if (strcmp(name, propnames+offset)) + offset += strlen(propnames+offset)+1; + else + return offset; + + if (NAMESPACE - offset < strlen(name) + 1) + die("unrecoverable error: propnames overrun\n"); + + strcpy(propnames+offset, name); + + return offset; +} + +/* + * Add ranges by comparing 'base' and 'end' addresses with usable + * memory ranges. Returns the number of ranges added. Each range added + * increments 'idx' by 2. + */ +static uint64_t add_ranges(uint64_t **ranges, int *ranges_size, int idx, + uint64_t base, uint64_t end) +{ + uint64_t loc_base, loc_end, rngs_cnt = 0; + size_t range; + int add = 0; + + for (range = 0; range < usablemem_rgns.size; range++) { + loc_base = usablemem_rgns.ranges[range].start; + loc_end = usablemem_rgns.ranges[range].end; + if (loc_base >= base && loc_end <= end) { + 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 (idx >= ((*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)[idx++] = cpu_to_be64(loc_base); + (*ranges)[idx++] = cpu_to_be64(loc_end - loc_base); + + rngs_cnt++; + } + } + + return rngs_cnt; +} + +#ifdef HAVE_DYNAMIC_MEMORY +static void add_dyn_reconf_usable_mem_property__(int fd) +{ + char fname[MAXPATH], *bname; + char buf[32]; + uint32_t lmbs_in_set = 1; + uint64_t *ranges; + int ranges_size = MEM_RANGE_CHUNK_SZ; + uint64_t base, end, rngs_cnt; + size_t i, j; + int rlen = 0; + int tmp_indx; + + strcpy(fname, pathname); + bname = strrchr(fname, '/'); + bname[0] = '\0'; + bname = strrchr(fname, '/'); + if (strncmp(bname, "/ibm,dynamic-reconfiguration-memory", 36)) + return; + + if (lseek(fd, 4, SEEK_SET) < 0) + 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_lmb_sets; i++) { + if (read(fd, buf, LMB_ENTRY_SIZE) < 0) + die("unrecoverable error: error reading \"%s\": %s\n", + pathname, strerror(errno)); + + /* + * If the property is ibm,dynamic-memory-v2, the first 4 bytes + * tell the number of sequential LMBs in this entry. Else, if + * the property is ibm,dynamic-memory, each entry represents + * one LMB. Make sure to add an entry for each LMB as kernel + * looks for a counter for every LMB. + */ + if (is_dyn_mem_v2) + lmbs_in_set = be32_to_cpu(((unsigned int *)buf)[0]); + + base = be64_to_cpu(*((uint64_t *)&buf[DRCONF_ADDR])); + for (j = 0; j < lmbs_in_set; j++) { + end = base + lmb_size; + if (~0ULL - base < end) { + die("unrecoverable error: mem property" + " overflow\n"); + } + + tmp_indx = rlen++; + + rngs_cnt = add_ranges(&ranges, &ranges_size, rlen, + base, end); + 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 + * will probably go on writing zeros for a while + */ + 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] = cpu_to_be64(rngs_cnt); + rlen += rngs_cnt * 2; + } + + base = end; + } + } + + rlen = rlen * sizeof(uint64_t); + /* + * Add linux,drconf-usable-memory property. + */ + dt_reserve(&dt, 4+((rlen + 3)/4)); + *dt++ = cpu_to_be32(FDT_PROP); + *dt++ = cpu_to_be32(rlen); + *dt++ = cpu_to_be32(propnum("linux,drconf-usable-memory")); + pad_structure_block(rlen); + memcpy(dt, ranges, rlen); + free(ranges); + dt += (rlen + 3)/4; +} + +static void add_dyn_reconf_usable_mem_property(struct dirent *dp, int fd) +{ + if ((!strcmp(dp->d_name, "ibm,dynamic-memory-v2") || + !strcmp(dp->d_name, "ibm,dynamic-memory")) && usablemem_rgns.size) + add_dyn_reconf_usable_mem_property__(fd); +} +#else +static void add_dyn_reconf_usable_mem_property(struct dirent *dp, int fd) {} +#endif + +static void add_usable_mem_property(int fd, size_t len) +{ + char fname[MAXPATH], *bname; + uint64_t buf[2]; + uint64_t *ranges; + int ranges_size = MEM_RANGE_CHUNK_SZ; + uint64_t base, end, rngs_cnt; + int rlen = 0; + + strcpy(fname, pathname); + bname = strrchr(fname,'/'); + bname[0] = '\0'; + bname = strrchr(fname,'/'); + if (strncmp(bname, "/memory@", 8) && strcmp(bname, "/memory")) + return; + + if (len < sizeof(buf)) + die("unrecoverable error: not enough data for mem property\n"); + + if (lseek(fd, 0, SEEK_SET) < 0) + die("unrecoverable error: error seeking in \"%s\": %s\n", + pathname, strerror(errno)); + if (read(fd, buf, sizeof(buf)) != sizeof(buf)) + die("unrecoverable error: error reading \"%s\": %s\n", + pathname, strerror(errno)); + + base = be64_to_cpu(buf[0]); + end = be64_to_cpu(buf[1]); + if (~0ULL - base < end) + die("unrecoverable error: mem property overflow\n"); + end += base; + + ranges = malloc(ranges_size * sizeof(*ranges)); + if (!ranges) + die("unrecoverable error: can't alloc %zu bytes for ranges.\n", + ranges_size * sizeof(*ranges)); + + rngs_cnt = add_ranges(&ranges, &ranges_size, rlen, base, end); + rlen += rngs_cnt * 2; + + if (!rlen) { + /* + * User did not pass any ranges for thsi region. Hence, write + * (0,0) duple in linux,usable-memory property such that + * this region will be ignored. + */ + ranges[rlen++] = 0; + ranges[rlen++] = 0; + } + + rlen = rlen * sizeof(*ranges); + /* + * No add linux,usable-memory property. + */ + dt_reserve(&dt, 4+((rlen + 3)/4)); + *dt++ = cpu_to_be32(FDT_PROP); + *dt++ = cpu_to_be32(rlen); + *dt++ = cpu_to_be32(propnum("linux,usable-memory")); + pad_structure_block(rlen); + memcpy(dt, ranges, rlen); + free(ranges); + dt += (rlen + 3)/4; +} + +/* put all properties (files) in the property structure */ +static void putprops(char *fn, struct dirent **nlist, int numlist) +{ + struct dirent *dp; + int i = 0, fd; + off_t len; + off_t slen; + struct stat statbuf; + + for (i = 0; i < numlist; i++) { + dp = nlist[i]; + strcpy(fn, dp->d_name); + + if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) + continue; + + /* Empirically, this seems to need to be ecluded. + * Observed on ARM with 3.6-rc2 kernel + */ + if (!strcmp(dp->d_name, "name")) + continue; + + if (lstat(pathname, &statbuf)) + die("unrecoverable error: could not stat \"%s\": %s\n", + pathname, strerror(errno)); + + if (!crash_param && !strcmp(fn,"linux,crashkernel-base")) + continue; + + if (!crash_param && !strcmp(fn,"linux,crashkernel-size")) + continue; + + /* + * This property will be created for each node during kexec + * boot. So, ignore it. + */ + if (!strcmp(dp->d_name, "linux,pci-domain") || + !strcmp(dp->d_name, "linux,htab-base") || + !strcmp(dp->d_name, "linux,htab-size") || + !strcmp(dp->d_name, "linux,kernel-end")) + continue; + + /* This property will be created/modified later in putnode() + * So ignore it, unless we are reusing the initrd. + */ + if ((!strcmp(dp->d_name, "linux,initrd-start") || + !strcmp(dp->d_name, "linux,initrd-end")) && + !reuse_initrd) + continue; + + /* This property will be created later in putnode() So + * ignore it now. + */ + if (!strcmp(dp->d_name, "bootargs")) + continue; + + if (! S_ISREG(statbuf.st_mode)) + continue; + + len = statbuf.st_size; + + dt_reserve(&dt, 4+((len + 3)/4)); + *dt++ = cpu_to_be32(FDT_PROP); + *dt++ = cpu_to_be32(len); + *dt++ = cpu_to_be32(propnum(fn)); + pad_structure_block(len); + + if (len) { + char *buf; + + buf = slurp_file_len(pathname, len, &slen); + if (slen != len) + die("unrecoverable error: short read from\"%s\"\n", + pathname); + + memcpy(dt, buf, slen); + free(buf); + } + + checkprop(fn, dt, len); + + dt += (len + 3)/4; + + fd = open(pathname, O_RDONLY); + if (fd == -1) + die("unrecoverable error: could not open \"%s\": %s\n", + pathname, strerror(errno)); + + if (!strcmp(dp->d_name, "reg") && usablemem_rgns.size) + add_usable_mem_property(fd, len); + add_dyn_reconf_usable_mem_property(dp, fd); + close(fd); + } + + fn[0] = '\0'; + checkprop(pathname, NULL, 0); +} + +/* + * Compare function used to sort the device-tree directories + * This function will be passed to scandir. + */ +static int comparefunc(const struct dirent **dentry1, + const struct dirent **dentry2) +{ + char *str1 = (*(struct dirent **)dentry1)->d_name; + char *str2 = (*(struct dirent **)dentry2)->d_name; + char *sep1 = strchr(str1, '@'); + char *sep2 = strchr(str2, '@'); + + /* + * strcmp scans from left to right and fails to idetify for some + * strings such as memory@10000000 and memory@f000000. + * Therefore, we get the wrong sorted order like memory@10000000 and + * memory@f000000. + */ + if (sep1 && sep2) { + int baselen1 = sep1 - str1; + int baselen2 = sep2 - str2; + int len1 = strlen(str1); + int len2 = strlen(str2); + + /* + * Check the base name matches, and the properties are + * different lengths. + */ + if ((baselen1 == baselen2) && (len1 != len2) && + !strncmp(str1, str2, baselen2)) + return (len1 > len2) - (len1 < len2); + } + + return strcmp(str1, str2); +} + +/* grab root= from the old command line */ +static void dt_copy_old_root_param(void) +{ + FILE *fp; + char filename[MAXPATH]; + char *last_cmdline = NULL; + char *p, *old_param; + size_t len = 0; + + strcpy(filename, pathname); + strcat(filename, "bootargs"); + fp = fopen(filename, "r"); + if (!fp) + return; + + if (getline(&last_cmdline, &len, fp) == -1) + die("unable to read %s\n", filename); + + p = strstr(last_cmdline, "root="); + if (p) { + old_param = strtok(p, " "); + len = strlen(local_cmdline); + if (len != 0) + strcat(local_cmdline, " "); + strcat(local_cmdline, old_param); + } + + if (last_cmdline) + free(last_cmdline); + + fclose(fp); +} + +/* + * put a node (directory) in the property structure. first properties + * then children. + */ +static void putnode(void) +{ + char *dn; + struct dirent *dp; + char *basename; + struct dirent **namelist; + int numlist, i; + struct stat statbuf; + int plen, ret; + + numlist = scandir(pathname, &namelist, 0, comparefunc); + if (numlist < 0) + die("unrecoverable error: could not scan \"%s\": %s\n", + pathname, strerror(errno)); + if (numlist == 0) + die("unrecoverable error: no directory entries in \"%s\"", + pathname); + + basename = strrchr(pathname,'/') + 1; + + plen = *basename ? strlen(basename) : 0; + /* 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++ = cpu_to_be32(FDT_BEGIN_NODE); + strcpy((void *)dt, *basename ? basename : ""); + dt += ((plen + 4)/4); + + if (*basename) + strcat(pathname, "/"); + dn = pathname + strlen(pathname); + + putprops(dn, namelist, numlist); + + /* Add initrd entries to the second kernel */ + if (initrd_base && initrd_size && !strcmp(basename,"chosen/")) { + int len = 8; + uint64_t bevalue; + + dt_reserve(&dt, 12); /* both props, of 6 words ea. */ + *dt++ = cpu_to_be32(FDT_PROP); + *dt++ = cpu_to_be32(len); + *dt++ = cpu_to_be32(propnum("linux,initrd-start")); + pad_structure_block(len); + + bevalue = cpu_to_be64(initrd_base); + memcpy(dt, &bevalue, len); + dt += (len + 3)/4; + + len = 8; + *dt++ = cpu_to_be32(FDT_PROP); + *dt++ = cpu_to_be32(len); + *dt++ = cpu_to_be32(propnum("linux,initrd-end")); + + bevalue = cpu_to_be64(initrd_base + initrd_size); + pad_structure_block(len); + + memcpy(dt, &bevalue, len); + dt += (len + 3)/4; + + reserve(initrd_base, initrd_size); + } + + /* + * Add cmdline to the second kernel. Use the old root= cmdline if there + * is no root= in the new command line and there's no --dt-no-old-root + * option being used. + */ + if (!strcmp(basename,"chosen/")) { + size_t result; + size_t cmd_len = 0; + char *param = NULL; + char filename[MAXPATH]; + char *buff; + int fd; + + cmd_len = strlen(local_cmdline); + if (cmd_len != 0) { + param = strstr(local_cmdline, "crashkernel="); + if (param) + crash_param = 1; + /* does the new cmdline have a root= ? ... */ + param = strstr(local_cmdline, "root="); + } + + if (!param && !dt_no_old_root) + dt_copy_old_root_param(); + + strcat(local_cmdline, " "); + cmd_len = strlen(local_cmdline); + cmd_len = cmd_len + 1; + + /* add new bootargs */ + dt_reserve(&dt, 4+((cmd_len+3)/4)); + *dt++ = cpu_to_be32(FDT_PROP); + *dt++ = cpu_to_be32(cmd_len); + *dt++ = cpu_to_be32(propnum("bootargs")); + pad_structure_block(cmd_len); + memcpy(dt, local_cmdline,cmd_len); + dt += (cmd_len + 3)/4; + + fprintf(stderr, "Modified cmdline:%s\n", local_cmdline); + + /* + * Determine the platform type/stdout type, so that purgatory + * code can print 'I'm in purgatory' message. Currently only + * pseries/hvcterminal is supported. + */ + ret = snprintf(filename, MAXPATH, "%sstdout-path", pathname); + if (ret < 0 || ret >= MAXPATH) { + fprintf(stderr, "snprintf failed: %s\n", strerror(errno)); + goto no_debug; + } + + fd = open(filename, O_RDONLY); + if (fd == -1) { + ret = snprintf(filename, MAXPATH, "%slinux,stdout-path", pathname); + if (ret < 0 || ret >= MAXPATH) { + fprintf(stderr, "snprintf failed: %s\n", strerror(errno)); + goto no_debug; + } + + fd = open(filename, O_RDONLY); + if (fd == -1) { + printf("Unable to find %s[linux,]stdout-path, printing from purgatory is disabled\n", + pathname); + goto no_debug; + } + } + if (fstat(fd, &statbuf)) { + printf("Unable to stat %s, printing from purgatory is disabled\n", + filename); + close(fd); + goto no_debug; + + } + + buff = malloc(statbuf.st_size); + if (!buff) { + printf("Can not allocate memory for buff\n"); + close(fd); + goto no_debug; + } + result = read(fd, buff, statbuf.st_size); + close(fd); + if (result <= 0) { + printf("Unable to read %s, printing from purgatory is disabled\n", + filename); + goto no_debug; + } + snprintf(filename, MAXPATH, "/proc/device-tree/%s/compatible", buff); + fd = open(filename, O_RDONLY); + if (fd == -1) { + printf("Unable to find %s printing from purgatory is disabled\n", + filename); + goto no_debug; + } + if (fstat(fd, &statbuf)) { + printf("Unable to stat %s printing from purgatory is disabled\n", + filename); + close(fd); + goto no_debug; + } + buff = realloc(buff, statbuf.st_size); + if (!buff) { + printf("Can not allocate memory for buff\n"); + close(fd); + goto no_debug; + } + result = read(fd, buff, statbuf.st_size); + if (result && (!strcmp(buff, "hvterm1") + || !strcmp(buff, "hvterm-protocol"))) + my_debug = 1; + close(fd); + free(buff); + } + +no_debug: + for (i=0; i < numlist; i++) { + dp = namelist[i]; + strcpy(dn, dp->d_name); + free(namelist[i]); + + if (!strcmp(dn, ".") || !strcmp(dn, "..")) + continue; + + if (lstat(pathname, &statbuf)) + die("unrecoverable error: could not stat \"%s\": %s\n", + pathname, strerror(errno)); + + if (S_ISDIR(statbuf.st_mode)) + putnode(); + } + + dt_reserve(&dt, 1); + *dt++ = cpu_to_be32(FDT_END_NODE); + dn[-1] = '\0'; + free(namelist); +} + +struct bootblock bb[1]; + +static void add_boot_block(char **bufp, off_t *sizep) +{ + unsigned long len; + unsigned long tlen, toff; + char *buf; + + len = _ALIGN(sizeof(bb[0]), 8); + + bb->off_mem_rsvmap = cpu_to_be32(len); + + for (len = 1; be64_to_cpu(mem_rsrv[len]); len += 2) + ; + len++; +#ifdef NEED_RESERVE_DTB + len+= 3; /* Leave space for totalsize reservation */ +#endif + len *= sizeof(mem_rsrv[0]); + + bb->off_dt_struct = cpu_to_be32(be32_to_cpu(bb->off_mem_rsvmap) + len); + + len = dt - dt_base; + len *= sizeof(unsigned); +#if (BOOT_BLOCK_VERSION >= 17) + bb->dt_struct_size = cpu_to_be32(len); +#endif + bb->off_dt_strings = cpu_to_be32(be32_to_cpu(bb->off_dt_struct) + len); + + len = propnum(""); + bb->dt_strings_size = cpu_to_be32(len); + len = _ALIGN(len, 4); + bb->totalsize = cpu_to_be32(be32_to_cpu(bb->off_dt_strings) + len); + + bb->magic = cpu_to_be32(FDT_MAGIC); + bb->version = cpu_to_be32(BOOT_BLOCK_VERSION); + bb->last_comp_version = cpu_to_be32(BOOT_BLOCK_LAST_COMP_VERSION); + +#ifdef NEED_RESERVE_DTB + reserve(0, be32_to_cpu(bb->totalsize)); /* patched later in kexec_load */ +#endif + + buf = malloc(be32_to_cpu(bb->totalsize)); + *bufp = buf; + + tlen = be32_to_cpu(bb->off_mem_rsvmap); + memcpy(buf, bb, tlen); + + toff = be32_to_cpu(bb->off_mem_rsvmap); + tlen = be32_to_cpu(bb->off_dt_struct) - be32_to_cpu(bb->off_mem_rsvmap); + memcpy(buf + toff, mem_rsrv, tlen); + + toff += be32_to_cpu(bb->off_dt_struct) - be32_to_cpu(bb->off_mem_rsvmap); + tlen = be32_to_cpu(bb->off_dt_strings) - be32_to_cpu(bb->off_dt_struct); + memcpy(buf + toff, dt_base, tlen); + + toff += be32_to_cpu(bb->off_dt_strings) - be32_to_cpu(bb->off_dt_struct); + tlen = be32_to_cpu(bb->totalsize) - be32_to_cpu(bb->off_dt_strings); + memcpy(buf + toff, propnames, tlen); + + *sizep = toff + be32_to_cpu(bb->totalsize) - + be32_to_cpu(bb->off_dt_strings); +} + +void create_flatten_tree(char **bufp, off_t *sizep, const char *cmdline) +{ + strcpy(pathname, "/proc/device-tree/"); + + 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++ = cpu_to_be32(FDT_END); + + add_boot_block(bufp, sizep); + free(dt_base); +} |