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, loc_base, loc_end; size_t range; 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)); 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) { 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 * sizeof(*ranges)); if (!ranges) die("unrecoverable error: can't realloc" "%zu bytes for ranges.\n", ranges_size*sizeof(*ranges)); } ranges[rlen++] = cpu_to_be64(loc_base); ranges[rlen++] = cpu_to_be64(loc_end - loc_base + 1); } } 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(3); *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 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; 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(1); strcpy((void *)dt, *basename ? basename : ""); dt += ((plen + 4)/4); 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 = 4; off_t initrd_start, initrd_end; initrd_start = cpu_to_be32(initrd_base); initrd_end = cpu_to_be32(initrd_base + initrd_size); dt_reserve(&dt, 12); /* both props, of 6 words ea. */ *dt++ = cpu_to_be32(3); *dt++ = cpu_to_be32(len); *dt++ = cpu_to_be32(propnum("linux,initrd-start")); pad_structure_block(len); memcpy(dt,&initrd_start,len); dt += (len + 3)/4; len = 4; *dt++ = cpu_to_be32(3); *dt++ = cpu_to_be32(len); *dt++ = cpu_to_be32(propnum("linux,initrd-end")); pad_structure_block(len); memcpy(dt,&initrd_end,len); dt += (len + 3)/4; reserve(initrd_base, initrd_size); } /* Add cmdline to the second kernel. Check to see if the new * cmdline has a root=. If not, use the old root= cmdline. */ 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 not, grab root= from the old command line */ if (!param) { FILE *fp; char *last_cmdline = NULL; char *old_param; strcpy(filename, pathname); strcat(filename, "bootargs"); fp = fopen(filename, "r"); if (fp) { if (getline(&last_cmdline, &cmd_len, fp) == -1) die("unable to read %s\n", filename); param = strstr(last_cmdline, "root="); if (param) { old_param = strtok(param, " "); if (cmd_len != 0) strcat(local_cmdline, " "); strcat(local_cmdline, old_param); } } if (last_cmdline) free(last_cmdline); } 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(3); *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. */ strcpy(filename, pathname); strncat(filename, "linux,stdout-path", MAXPATH); fd = open(filename, O_RDONLY); if (fd == -1) { printf("Unable to find %s, printing from purgatory is diabled\n", filename); goto no_debug; } if (fstat(fd, &statbuf)) { printf("Unable to stat %s, printing from purgatory is diabled\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 diabled\n", filename); goto no_debug; } strncpy(filename, "/proc/device-tree/", MAXPATH); strncat(filename, buff, MAXPATH); strncat(filename, "/compatible", MAXPATH); fd = open(filename, O_RDONLY); if (fd == -1) { printf("Unable to find %s printing from purgatory is diabled\n", filename); goto no_debug; } if (fstat(fd, &statbuf)) { printf("Unable to stat %s printing from purgatory is diabled\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(2); dn[-1] = '\0'; free(namelist); }
static void add_dyn_reconf_usable_mem_property__(int fd) { char fname[MAXPATH], *bname; uint64_t buf[32]; uint64_t *ranges; int ranges_size = MEM_RANGE_CHUNK_SZ; uint64_t base, end, loc_base, loc_end; size_t i, rngs_cnt, range; 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_lmbs; i++) { if (read(fd, buf, 24) < 0) die("unrecoverable error: error reading \"%s\": %s\n", pathname, strerror(errno)); base = be64_to_cpu((uint64_t) buf[0]); end = base + lmb_size; if (~0ULL - base < end) die("unrecoverable error: mem property overflow\n"); tmp_indx = rlen++; 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) { 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++] = cpu_to_be64(loc_base); ranges[rlen++] = cpu_to_be64(loc_end - loc_base + 1); 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] = cpu_to_be64((uint64_t) rngs_cnt); } } rlen = rlen * sizeof(uint64_t); /* * Add linux,drconf-usable-memory property. */ dt_reserve(&dt, 4+((rlen + 3)/4)); *dt++ = cpu_to_be32(3); *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; }
/* 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; size_t len; ssize_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(3); *dt++ = cpu_to_be32(len); *dt++ = cpu_to_be32(propnum(fn)); pad_structure_block(len); fd = open(pathname, O_RDONLY); if (fd == -1) die("unrecoverable error: could not open \"%s\": %s\n", pathname, strerror(errno)); slen = read(fd, dt, len); if (slen < 0) die("unrecoverable error: could not read \"%s\": %s\n", pathname, strerror(errno)); if ((size_t)slen != len) die("unrecoverable error: short read from\"%s\"\n", pathname); checkprop(fn, dt, len); dt += (len + 3)/4; 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); }