summaryrefslogtreecommitdiffstats
path: root/kexec/fs2dt.c
diff options
context:
space:
mode:
Diffstat (limited to 'kexec/fs2dt.c')
-rw-r--r--kexec/fs2dt.c852
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);
+}