/* * 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; either version 2 of the License, or * (at your option) any later version. * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Copyright IBM Corporation 2007 * * Authors: Ryan Harper */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for PERROR() */ #include #include "mk_flatdevtree.h" static uint32_t current_phandle = 0; static uint32_t get_phandle(void) { return current_phandle++; } static int readfile(const char *fullpath, void *data, int len) { struct stat st; int saved_errno; int rc = -1; int fd; if ((fd = open(fullpath, O_RDONLY)) == -1) { PERROR("%s: failed to open file %s", __func__, fullpath); return -1; } if ((rc = fstat(fd, &st)) == -1) { PERROR("%s: failed to stat fd %d", __func__, fd); goto error; } if (S_ISREG(st.st_mode)) rc = read(fd, data, len); close(fd); return rc; error: saved_errno = errno; close(fd); errno = saved_errno; return -1; } /* * @property - string to check against the filter list * @filter - NULL terminated list of strings * * compare @property string to each string in @filter * * return 1 if @property matches any filter, otherwise 0 * */ static int match(const char *property, const char **filter) { int i; for (i=0; filter[i] != NULL; i++) { /* compare the filter to property */ if (strncmp(property, filter[i], strlen(filter[i])) == 0) return 1; } return 0; } /* * copy the node at @dirpath filtering out any properties that match in @propfilter */ static int copynode(struct ft_cxt *cxt, const char *dirpath, const char **propfilter) { struct dirent *tree; struct stat st; DIR *dir; char fullpath[MAX_PATH]; char *bname = NULL; char *basec = NULL; int saved_errno; if ((dir = opendir(dirpath)) == NULL) { PERROR("%s: failed to open dir %s", __func__, dirpath); return -1; } while (1) { if ((tree = readdir(dir)) == NULL) break; /* reached end of directory entries */ /* ignore . and .. */ if (strcmp(tree->d_name,"." ) == 0 || strcmp(tree->d_name,"..") == 0) continue; /* build full path name of the file, for stat() */ if (snprintf(fullpath, sizeof(fullpath), "%s/%s", dirpath, tree->d_name) >= sizeof(fullpath)) { PERROR("%s: failed to build full path", __func__); goto error; } /* stat the entry */ if (stat(fullpath, &st) < 0) { PERROR("%s: failed to stat file %s", __func__, fullpath); goto error; } if (S_ISDIR(st.st_mode)) { /* start a new node for a dir */ ft_begin_node(cxt, tree->d_name); /* copy everything in this dir */ if (copynode(cxt, fullpath, propfilter) < 0) { PERROR("%s: failed to copy node @ %s", __func__, fullpath); goto error; } /* end the node */ ft_end_node(cxt); } /* add files in dir as properties */ else if (S_ISREG(st.st_mode)) { if ((basec = strdup(fullpath)) == NULL) { PERROR("%s: failed to dupe string", __func__); goto error; } if ((bname = basename(basec)) == NULL) { PERROR("%s: basename() failed", __func__); goto error; } /* only add files that don't match the property filter string */ if (!match(bname, propfilter)) { char data[BUFSIZE]; int len; /* snarf the data and push into the property */ if ((len = readfile(fullpath, data, sizeof(data))) < 0) { PERROR("%s: failed to read data from file %s", __func__, fullpath); goto error; } ft_prop(cxt, tree->d_name, data, len); } /* strdup mallocs memory */ if (basec != NULL ) { free(basec); basec = NULL; } } } closedir(dir); return 0; error: saved_errno = errno; /* strdup mallocs memory */ if (basec != NULL ) { free(basec); basec = NULL; } closedir(dir); errno = saved_errno; return -1; } static int find_cpu0(char *cpupath, int len) { const char path[] = "/proc/device-tree/cpus"; const char device[] = "device_type"; const char dev_cpu[] = "cpu"; const char reg[] = "reg"; char data[sizeof(dev_cpu)]; char prop[MAX_PATH]; char node[MAX_PATH]; struct dirent *tree; struct stat st; DIR* dir; int saved_errno; int found = 0; if ((dir = opendir(path)) == NULL) { PERROR("%s: failed to open directory %s", __func__, path); return -1; } while (!found) { if ((tree = readdir(dir)) == NULL) break; /* reached end of directory entries */ /* ignore ., .. */ if (strcmp(tree->d_name,"." ) == 0 || strcmp(tree->d_name,"..") == 0) continue; /* build full path name of the file, for stat() */ if (snprintf(node, sizeof(node), "%s/%s", path, tree->d_name) >= sizeof(node)) { PERROR("%s: failed to concat strings", __func__); goto error; } /* stat the entry */ if (stat(node, &st) < 0) { PERROR("%s: failed to stat file %s", __func__, node); /* something funny happen in /proc/device-tree, but march onward */ continue; } /* for each dir, check the device_type property until we find 'cpu'*/ if (S_ISDIR(st.st_mode)) { /* construct path to device_type */ if (snprintf(prop, sizeof(prop), "%s/%s", node, device) >= sizeof(prop)) { PERROR("%s: failed to concat strings", __func__); goto error; } /* read device_type into buffer */ if ((readfile(prop, data, sizeof(data))) < 0) { PERROR("%s: failed to read data from file %s", __func__, prop); goto error; } /* if the device_type is 'cpu', and reg is 0 * return the path where we found it */ if (strcmp(data, "cpu") == 0) { /* construct path to reg */ if (snprintf(prop, sizeof(prop), "%s/%s", node, reg) >= sizeof(prop)) { PERROR("%s: failed to concat strings", __func__); goto error; } /* using data buffer since reg and device_type values have same size */ if ((readfile(prop, data, sizeof(data))) < 0) { PERROR("%s: failed to read data from file %s", __func__, prop); goto error; } /* now check property "reg" for value 0 */ if ((u32)*data == 0) { if (snprintf(cpupath, len, "%s", node) >= len) { PERROR("%s: failed to copy cpupath", __func__); goto error; } found = 1; } } } } closedir(dir); return found; error: saved_errno = errno; closedir(dir); errno = saved_errno; return -1; } void free_devtree(struct ft_cxt *root) { if ((root != NULL) && root->bph != NULL) { free(root->bph); root->bph = NULL; } } int make_devtree(struct ft_cxt *root, struct xc_dom_image *dom, unsigned long shadow_mb) { struct boot_param_header *bph = NULL; uint64_t val[2]; uint32_t val32[2]; uint64_t shared_info_paddr = dom->shared_info_pfn << PAGE_SHIFT; uint64_t xenstore_paddr = dom->xenstore_pfn << PAGE_SHIFT; uint64_t console_paddr = dom->console_pfn << PAGE_SHIFT; long remaining; unsigned long ramdisk_start; unsigned long ramdisk_size; unsigned long rma_bytes = 1 << dom->realmodearea_log; int64_t shadow_mb_log; uint64_t pft_size; char cpupath[MAX_PATH]; const char *propfilter[] = { "ibm", "linux,", NULL }; char *cpupath_copy = NULL; char *cpuname = NULL; int saved_errno; int dtb_fd = -1; uint32_t cpu0_phandle = get_phandle(); uint32_t xen_phandle = get_phandle(); uint32_t rma_phandle = get_phandle(); /* initialize bph to prevent double free on error path */ root->bph = NULL; /* carve out space for bph */ if ((bph = (struct boot_param_header *)malloc(BPH_SIZE)) == NULL) { PERROR("%s: Failed to malloc bph buffer size", __func__); goto error; } /* NB: struct ft_cxt root defined at top of file */ /* root = Tree() */ ft_begin(root, bph, BPH_SIZE); /* you MUST set reservations BEFORE _starting_the_tree_ */ /* reserve shared_info_t page */ if (shared_info_paddr) { val[0] = cpu_to_be64((u64) shared_info_paddr); val[1] = cpu_to_be64((u64) PAGE_SIZE); ft_add_rsvmap(root, val[0], val[1]); } /* reserve console page for domU */ if (console_paddr) { val[0] = cpu_to_be64((u64) console_paddr); val[1] = cpu_to_be64((u64) PAGE_SIZE); ft_add_rsvmap(root, val[0], val[1]); } /* reserve xen store page for domU */ if (xenstore_paddr) { val[0] = cpu_to_be64((u64) xenstore_paddr); val[1] = cpu_to_be64((u64) PAGE_SIZE); ft_add_rsvmap(root, val[0], val[1]); } /* reserve space for initrd if needed */ ramdisk_start = dom->ramdisk_seg.pfn << PAGE_SHIFT; ramdisk_size = dom->ramdisk_seg.vend - dom->ramdisk_seg.vstart; if (ramdisk_size > 0) { val[0] = cpu_to_be64((u64) ramdisk_start); val[1] = cpu_to_be64((u64) ramdisk_size); ft_add_rsvmap(root, val[0], val[1]); } /* NB: ft_add_rsvmap() already terminates with a NULL reservation for us */ /* done with reservations, _starting_the_tree_ */ ft_begin_tree(root); /* make root node */ ft_begin_node(root, ""); /* root.addprop('device_type', 'chrp-but-not-really\0') */ ft_prop_str(root, "device_type", "chrp-but-not-really"); /* root.addprop('#size-cells', 2) */ ft_prop_int(root, "#size-cells", 2); /* root.addprop('#address-cells', 2) */ ft_prop_int(root, "#address-cells", 2); /* root.addprop('model', 'Momentum,Maple-D\0') */ ft_prop_str(root, "model", "Momentum,Maple-D"); /* root.addprop('compatible', 'Momentum,Maple\0') */ ft_prop_str(root, "compatible", "Momentum,Maple"); /* start chosen node */ ft_begin_node(root, "chosen"); /* chosen.addprop('cpu', cpu0.get_phandle()) */ ft_prop_int(root, "cpu", cpu0_phandle); /* chosen.addprop('rma', rma.get_phandle()) */ ft_prop_int(root, "memory", rma_phandle); /* chosen.addprop('linux,stdout-path', '/xen/console\0') */ ft_prop_str(root, "linux,stdout-path", "/xen/console"); /* chosen.addprop('interrupt-controller, xen.get_phandle()) */ ft_prop_int(root, "interrupt-controller", xen_phandle); /* chosen.addprop('bootargs', imghandler.cmdline + '\0') */ if (dom->cmdline != NULL) ft_prop_str(root, "bootargs", dom->cmdline); /* mark where the initrd is, if present */ if (ramdisk_size > 0) { val[0] = cpu_to_be64((u64) ramdisk_start); val[1] = cpu_to_be64((u64) ramdisk_start + ramdisk_size); ft_prop(root, "linux,initrd-start", &(val[0]), sizeof(val[0])); ft_prop(root, "linux,initrd-end", &(val[1]), sizeof(val[1])); } /* end chosen node */ ft_end_node(root); /* xen = root.addnode('xen') */ ft_begin_node(root, "xen"); /* xen.addprop('version', 'Xen-3.0-unstable\0') */ ft_prop_str(root, "compatible", "Xen-3.0-unstable"); /* xen.addprop('reg', long(imghandler.vm.domid), long(0)) */ val[0] = cpu_to_be64((u64) dom->guest_domid); val[1] = cpu_to_be64((u64) 0); ft_prop(root, "reg", val, sizeof(val)); /* point to shared_info_t page base addr */ val[0] = cpu_to_be64((u64) shared_info_paddr); val[1] = cpu_to_be64((u64) PAGE_SIZE); ft_prop(root, "shared-info", val, sizeof(val)); /* xen.addprop('domain-name', imghandler.vm.getName() + '\0') */ /* libxc doesn't know the domain name, that is purely a xend thing */ /* ft_prop_str(root, "domain-name", domain_name); */ /* add xen/linux,phandle for chosen/interrupt-controller */ ft_prop_int(root, "linux,phandle", xen_phandle); if (console_paddr != 0) { /* xencons = xen.addnode('console') */ ft_begin_node(root, "console"); /* console_paddr */ val[0] = cpu_to_be64((u64) console_paddr); val[1] = cpu_to_be64((u64) PAGE_SIZE); ft_prop(root, "reg", val, sizeof(val)); /* xencons.addprop('interrupts', console_evtchn, 0) */ val32[0] = cpu_to_be32((u32) dom->console_evtchn); val32[1] = cpu_to_be32((u32) 0); ft_prop(root, "interrupts", val32, sizeof(val32)); /* end of console */ ft_end_node(root); } if (xenstore_paddr != 0) { /* start store node */ ft_begin_node(root, "store"); /* store paddr */ val[0] = cpu_to_be64((u64) xenstore_paddr); val[1] = cpu_to_be64((u64) PAGE_SIZE); ft_prop(root, "reg", val, sizeof(val)); /* store event channel */ val32[0] = cpu_to_be32((u32) dom->xenstore_evtchn); val32[1] = cpu_to_be32((u32) 0); ft_prop(root, "interrupts", val32, sizeof(val32)); /* end of store */ ft_end_node(root); } /* end of xen node */ ft_end_node(root); /* rma = root.addnode('memory@0') */ ft_begin_node(root, "memory@0"); /* rma.addprop('reg', long(0), long(rma_bytes)) */ val[0] = cpu_to_be64((u64) 0); val[1] = cpu_to_be64((u64) rma_bytes); ft_prop(root, "reg", val, sizeof(val)); /* rma.addprop('device_type', 'memory\0') */ ft_prop_str(root, "device_type", "memory"); /* add linux,phandle for chosen/rma node */ ft_prop_int(root, "linux,phandle", rma_phandle); /* end of memory@0 */ ft_end_node(root); /* calculate remaining bytes from total - rma size */ remaining = (dom->total_pages << PAGE_SHIFT) - rma_bytes; /* memory@ is all remaining memory after RMA */ if (remaining > 0) { char mem[MAX_PATH]; if (snprintf(mem, sizeof(mem), "memory@%lx", rma_bytes) >= sizeof(mem)) { PERROR("%s: failed to build memory string", __func__); goto error; } /* memory@ is all remaining memory after RMA */ ft_begin_node(root, mem); /* mem.addprop('reg', long(rma_bytes), long(remaining)) */ val[0] = cpu_to_be64((u64) rma_bytes); val[1] = cpu_to_be64((u64) remaining); ft_prop(root, "reg", val, sizeof(val)); /* mem.addprop('device_type', 'memory\0') */ ft_prop_str(root, "device_type", "memory"); /* end memory@ node */ ft_end_node(root); } /* add CPU nodes */ /* cpus = root.addnode('cpus') */ ft_begin_node(root, "cpus"); /* cpus.addprop('smp-enabled') */ ft_prop(root, "smp-enabled", NULL, 0); /* cpus.addprop('#size-cells', 0) */ ft_prop_int(root, "#size-cells", 0); /* cpus.addprop('#address-cells', 1) */ ft_prop_int(root, "#address-cells", 1); /* * Copy all properties the system firmware gave us from a * CPU node in the device tree. */ if (find_cpu0(cpupath, sizeof(cpupath)) <= 0) { PERROR("%s: failed find cpu0 node in host devtree", __func__); goto error; } /* get the basename from path to cpu device */ if ((cpupath_copy = strdup(cpupath)) == NULL) { PERROR("%s: failed to dupe string", __func__); goto error; } if ((cpuname = basename(cpupath_copy)) == NULL) { PERROR("%s: basename() failed", __func__); goto error; } /* start node for the cpu */ ft_begin_node(root, cpuname); /* strdup() mallocs memory */ if ( cpupath_copy != NULL ) { free(cpupath_copy); cpupath_copy = NULL; } /* copy over most properties from host tree for cpu */ if (copynode(root, cpupath, propfilter) < 0) { PERROR("%s: failed to copy node", __func__); goto error; } /* calculate the pft-size */ shadow_mb_log = (int)log2((double)shadow_mb); pft_size = shadow_mb_log + 20; val32[0] = cpu_to_be32((u32) 0); val32[1] = cpu_to_be32((u32) pft_size); ft_prop(root, "ibm,pft-size", val32, sizeof(val32)); /* make phandle for cpu0 */ ft_prop_int(root, "linux,phandle", cpu0_phandle); /* end node */ ft_end_node(root); /* end cpus node */ ft_end_node(root); /* end root node */ ft_end_node(root); /* end of the tree */ if (ft_end_tree(root) != 0) { PERROR("%s: failed to end tree", __func__); goto error; } /* write a copy of the tree to a file */ if ((dtb_fd = creat(DTB_FILE, S_IRUSR | S_IWUSR)) == -1) { PERROR("%s: failed to open file %s", __func__, DTB_FILE); goto error; } if (write(dtb_fd, (const void *)bph, bph->totalsize) != bph->totalsize) { PERROR("%s: failed to write blob to file", __func__); goto error; } return 0; error: saved_errno = errno; /* strdup() mallocs memory */ if ( cpupath_copy != NULL ) { free(cpupath_copy); cpupath_copy = NULL; } /* free bph buffer */ free_devtree(root); if (dtb_fd) close(dtb_fd); errno = saved_errno; return -1; }