diff --git a/stand/kboot/arch/powerpc64/load_addr.c b/stand/kboot/arch/powerpc64/load_addr.c index 81b5d0d50581..ac3374ff20f2 100644 --- a/stand/kboot/arch/powerpc64/load_addr.c +++ b/stand/kboot/arch/powerpc64/load_addr.c @@ -1,210 +1,222 @@ /*- * Copyright (C) 2010-2014 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "stand.h" #include "host_syscall.h" #include "kboot.h" struct region_desc { uint64_t start; uint64_t end; }; /* * Find a good place to load the kernel, subject to the PowerPC's constraints * * This excludes ranges that are marked as reserved. * And 0..end of kernel * * It then tries to find the memory exposed from the DTB, which it assumes is one * contiguous range. It adds everything not in that list to the excluded list. * * Sort, dedup, and it finds the first region and uses that as the load_segment * and returns that. All addresses are offset by this amount. */ uint64_t kboot_get_phys_load_segment(void) { int fd; uint64_t entry[2]; static uint64_t load_segment = ~(0UL); uint64_t val_64; uint32_t val_32; struct region_desc rsvd_reg[32]; int rsvd_reg_cnt = 0; int ret, a, b; uint64_t start, end; if (load_segment == ~(0UL)) { /* Default load address is 0x00000000 */ load_segment = 0UL; /* Read reserved regions */ fd = host_open("/proc/device-tree/reserved-ranges", O_RDONLY, 0); if (fd >= 0) { while (host_read(fd, &entry[0], sizeof(entry)) == sizeof(entry)) { rsvd_reg[rsvd_reg_cnt].start = be64toh(entry[0]); rsvd_reg[rsvd_reg_cnt].end = be64toh(entry[1]) + rsvd_reg[rsvd_reg_cnt].start - 1; rsvd_reg_cnt++; } host_close(fd); } /* Read where the kernel ends */ fd = host_open("/proc/device-tree/chosen/linux,kernel-end", O_RDONLY, 0); if (fd >= 0) { ret = host_read(fd, &val_64, sizeof(val_64)); if (ret == sizeof(uint64_t)) { rsvd_reg[rsvd_reg_cnt].start = 0; rsvd_reg[rsvd_reg_cnt].end = be64toh(val_64) - 1; } else { memcpy(&val_32, &val_64, sizeof(val_32)); rsvd_reg[rsvd_reg_cnt].start = 0; rsvd_reg[rsvd_reg_cnt].end = be32toh(val_32) - 1; } rsvd_reg_cnt++; host_close(fd); } /* Read memory size (SOCKET0 only) */ fd = host_open("/proc/device-tree/memory@0/reg", O_RDONLY, 0); if (fd < 0) fd = host_open("/proc/device-tree/memory/reg", O_RDONLY, 0); if (fd >= 0) { ret = host_read(fd, &entry, sizeof(entry)); /* Memory range in start:length format */ entry[0] = be64toh(entry[0]); entry[1] = be64toh(entry[1]); /* Reserve everything what is before start */ if (entry[0] != 0) { rsvd_reg[rsvd_reg_cnt].start = 0; rsvd_reg[rsvd_reg_cnt].end = entry[0] - 1; rsvd_reg_cnt++; } /* Reserve everything what is after end */ if (entry[1] != 0xffffffffffffffffUL) { rsvd_reg[rsvd_reg_cnt].start = entry[0] + entry[1]; rsvd_reg[rsvd_reg_cnt].end = 0xffffffffffffffffUL; rsvd_reg_cnt++; } host_close(fd); } /* Sort entries in ascending order (bubble) */ for (a = rsvd_reg_cnt - 1; a > 0; a--) { for (b = 0; b < a; b++) { if (rsvd_reg[b].start > rsvd_reg[b + 1].start) { struct region_desc tmp; tmp = rsvd_reg[b]; rsvd_reg[b] = rsvd_reg[b + 1]; rsvd_reg[b + 1] = tmp; } } } /* Join overlapping/adjacent regions */ for (a = 0; a < rsvd_reg_cnt - 1; ) { if ((rsvd_reg[a + 1].start >= rsvd_reg[a].start) && ((rsvd_reg[a + 1].start - 1) <= rsvd_reg[a].end)) { /* We have overlapping/adjacent regions! */ rsvd_reg[a].end = MAX(rsvd_reg[a].end, rsvd_reg[a + a].end); for (b = a + 1; b < rsvd_reg_cnt - 1; b++) rsvd_reg[b] = rsvd_reg[b + 1]; rsvd_reg_cnt--; } else a++; } /* Find the first free region */ if (rsvd_reg_cnt > 0) { start = 0; end = rsvd_reg[0].start; for (a = 0; a < rsvd_reg_cnt - 1; a++) { if ((start >= rsvd_reg[a].start) && (start <= rsvd_reg[a].end)) { start = rsvd_reg[a].end + 1; end = rsvd_reg[a + 1].start; } else break; } if (start != end) { uint64_t align = 64UL*1024UL*1024UL; /* Align both to 64MB boundary */ start = (start + align - 1UL) & ~(align - 1UL); end = ((end + 1UL) & ~(align - 1UL)) - 1UL; if (start < end) load_segment = start; } } } return (load_segment); } #if 0 /* * XXX this appears to be unused, but may have been for selecting the allowed * kernels ABIs. It's been unused since the first commit, which suggests an * error in bringing this into the tree. */ uint8_t kboot_get_kernel_machine_bits(void) { static uint8_t bits = 0; struct old_utsname utsname; int ret; if (bits == 0) { /* Default is 32-bit kernel */ bits = 32; /* Try to get system type */ memset(&utsname, 0, sizeof(utsname)); ret = host_uname(&utsname); if (ret == 0) { if (strcmp(utsname.machine, "ppc64") == 0) bits = 64; else if (strcmp(utsname.machine, "ppc64le") == 0) bits = 64; } } return (bits); } #endif + +/* Need to transition from current hacky FDT way to this code */ +bool enumerate_memory_arch(void) +{ + /* + * For now, we dig it out of the FDT, plus we need to pass all data into + * the kernel via the (adjusted) FDT we find. + */ + setenv("usefdt", "1", 1); + + return true; +} diff --git a/stand/kboot/main.c b/stand/kboot/main.c index b8523cb202a2..bc2dad96dbfe 100644 --- a/stand/kboot/main.c +++ b/stand/kboot/main.c @@ -1,464 +1,463 @@ /*- * Copyright (C) 2010-2014 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include "host_syscall.h" #include "kboot.h" struct arch_switch archsw; extern void *_end; int kboot_getdev(void **vdev, const char *devspec, const char **path); ssize_t kboot_copyin(const void *src, vm_offset_t dest, const size_t len); ssize_t kboot_copyout(vm_offset_t src, void *dest, const size_t len); ssize_t kboot_readin(readin_handle_t fd, vm_offset_t dest, const size_t len); int kboot_autoload(void); static void kboot_kseg_get(int *nseg, void **ptr); static void kboot_zfs_probe(void); extern int command_fdt_internal(int argc, char *argv[]); static uint64_t commit_limit; static uint64_t committed_as; static uint64_t mem_avail; static void memory_limits(void) { int fd; char buf[128]; /* * To properly size the slabs, we need to find how much memory we can * commit to using. commit_limit is the max, while commited_as is the * current total. We can use these later to allocate the largetst amount * of memory possible so we can support larger ram disks than we could * by using fixed segment sizes. We also grab the memory available so * we don't use more than 49% of that. */ fd = open("host:/proc/meminfo", O_RDONLY); if (fd != -1) { while (fgetstr(buf, sizeof(buf), fd) > 0) { if (strncmp(buf, "MemAvailable:", 13) == 0) { mem_avail = strtoll(buf + 13, NULL, 0); mem_avail <<= 10; /* Units are kB */ } else if (strncmp(buf, "CommitLimit:", 12) == 0) { commit_limit = strtoll(buf + 13, NULL, 0); commit_limit <<= 10; /* Units are kB */ } else if (strncmp(buf, "Committed_AS:", 13) == 0) { committed_as = strtoll(buf + 14, NULL, 0); committed_as <<= 10; /* Units are kB */ } } } printf("Commit limit: %lld Committed bytes %lld Available %lld\n", (long long)commit_limit, (long long)committed_as, (long long)mem_avail); close(fd); } /* * NB: getdev should likely be identical to this most places, except maybe * we should move to storing the length of the platform devdesc. */ int kboot_getdev(void **vdev, const char *devspec, const char **path) { int rv; struct devdesc **dev = (struct devdesc **)vdev; /* * If it looks like this is just a path and no device, go with the * current device. */ if (devspec == NULL || strchr(devspec, ':') == NULL) { if (((rv = devparse(dev, getenv("currdev"), NULL)) == 0) && (path != NULL)) *path = devspec; return (rv); } /* * Try to parse the device name off the beginning of the devspec */ return (devparse(dev, devspec, path)); } static int parse_args(int argc, const char **argv) { int howto = 0; /* * When run as init, sometimes argv[0] is a EFI-ESP path, other times * it's the name of the init program, and sometimes it's a placeholder * string, so we exclude it here. For the other args, look for DOS-like * and Unix-like absolte paths and exclude parsing it if we find that, * otherwise parse it as a command arg (so looking for '-X', 'foo' or * 'foo=bar'). This is a little different than EFI where it argv[0] * often times is the first argument passed in. There are cases when * linux-booting via EFI that we have the EFI path we used to run * bootXXX.efi as the arguments to init, so we need to exclude the paths * there as well. */ for (int i = 1; i < argc; i++) { if (argv[i][0] != '\\' && argv[i][0] != '/') { howto |= boot_parse_arg(argv[i]); } } return (howto); } static vm_offset_t rsdp; static vm_offset_t kboot_rsdp_from_efi(void) { char buffer[512 + 1]; char *walker, *ep; if (!file2str("/sys/firmware/efi/systab", buffer, sizeof(buffer))) return (0); /* Not an EFI system */ ep = buffer + strlen(buffer); walker = buffer; while (walker < ep) { if (strncmp("ACPI20=", walker, 7) == 0) return((vm_offset_t)strtoull(walker + 7, NULL, 0)); if (strncmp("ACPI=", walker, 5) == 0) return((vm_offset_t)strtoull(walker + 5, NULL, 0)); walker += strcspn(walker, "\n"); } return (0); } static void find_acpi(void) { rsdp = kboot_rsdp_from_efi(); #if 0 /* maybe for amd64 */ if (rsdp == 0) rsdp = find_rsdp_arch(); #endif } vm_offset_t acpi_rsdp(void) { return (rsdp); } bool has_acpi(void) { return rsdp != 0; } int main(int argc, const char **argv) { void *heapbase; const size_t heapsize = 64*1024*1024; const char *bootdev; archsw.arch_getdev = kboot_getdev; archsw.arch_copyin = kboot_copyin; archsw.arch_copyout = kboot_copyout; archsw.arch_readin = kboot_readin; archsw.arch_autoload = kboot_autoload; archsw.arch_kexec_kseg_get = kboot_kseg_get; archsw.arch_zfs_probe = kboot_zfs_probe; /* Give us a sane world if we're running as init */ do_init(); /* * Setup the heap, 64MB is minimum for ZFS booting */ heapbase = host_getmem(heapsize); setheap(heapbase, heapbase + heapsize); /* Parse the command line args -- ignoring for now the console selection */ parse_args(argc, argv); /* * Set up console. */ cons_probe(); /* Initialize all the devices */ devinit(); bootdev = getenv("bootdev"); if (bootdev == NULL) bootdev = hostdisk_gen_probe(); if (bootdev == NULL) bootdev="zfs:"; hostfs_root = getenv("hostfs_root"); if (hostfs_root == NULL) hostfs_root = "/"; #if defined(LOADER_ZFS_SUPPORT) if (strcmp(bootdev, "zfs:") == 0) { /* * Pseudo device that says go find the right ZFS pool. This will be * the first pool that we find that passes the sanity checks (eg looks * like it might be vbootable) and sets currdev to the right thing based * on active BEs, etc */ hostdisk_zfs_find_default(); } else #endif { /* * Otherwise, honor what's on the command line. If we've been * given a specific ZFS partition, then we'll honor it w/o BE * processing that would otherwise pick a different snapshot to * boot than the default one in the pool. */ set_currdev(bootdev); } printf("Boot device: %s with hostfs_root %s\n", bootdev, hostfs_root); printf("\n%s", bootprog_info); setenv("LINES", "24", 1); - setenv("usefdt", "1", 1); memory_limits(); /* * Find acpi, if it exists */ find_acpi(); interact(); /* doesn't return */ return (0); } void exit(int code) { host_exit(code); __unreachable(); } void delay(int usecs) { struct host_timeval tvi, tv; uint64_t ti, t; host_gettimeofday(&tvi, NULL); ti = tvi.tv_sec*1000000 + tvi.tv_usec; do { host_gettimeofday(&tv, NULL); t = tv.tv_sec*1000000 + tv.tv_usec; } while (t < ti + usecs); } time_t getsecs(void) { struct host_timeval tv; host_gettimeofday(&tv, NULL); return (tv.tv_sec); } time_t time(time_t *tloc) { time_t rv; rv = getsecs(); if (tloc != NULL) *tloc = rv; return (rv); } struct host_kexec_segment loaded_segments[HOST_KEXEC_SEGMENT_MAX]; int nkexec_segments = 0; static ssize_t get_phys_buffer(vm_offset_t dest, const size_t len, void **buf) { int i = 0; const size_t segsize = 8*1024*1024; if (nkexec_segments == HOST_KEXEC_SEGMENT_MAX) panic("Tried to load too many kexec segments"); for (i = 0; i < nkexec_segments; i++) { if (dest >= (vm_offset_t)loaded_segments[i].mem && dest < (vm_offset_t)loaded_segments[i].mem + loaded_segments[i].memsz) goto out; } loaded_segments[nkexec_segments].buf = host_getmem(segsize); loaded_segments[nkexec_segments].bufsz = segsize; loaded_segments[nkexec_segments].mem = (void *)rounddown2(dest,segsize); loaded_segments[nkexec_segments].memsz = segsize; i = nkexec_segments; nkexec_segments++; out: *buf = loaded_segments[i].buf + (dest - (vm_offset_t)loaded_segments[i].mem); return (min(len,loaded_segments[i].bufsz - (dest - (vm_offset_t)loaded_segments[i].mem))); } ssize_t kboot_copyin(const void *src, vm_offset_t dest, const size_t len) { ssize_t segsize, remainder; void *destbuf; remainder = len; do { segsize = get_phys_buffer(dest, remainder, &destbuf); bcopy(src, destbuf, segsize); remainder -= segsize; src += segsize; dest += segsize; } while (remainder > 0); return (len); } ssize_t kboot_copyout(vm_offset_t src, void *dest, const size_t len) { ssize_t segsize, remainder; void *srcbuf; remainder = len; do { segsize = get_phys_buffer(src, remainder, &srcbuf); bcopy(srcbuf, dest, segsize); remainder -= segsize; src += segsize; dest += segsize; } while (remainder > 0); return (len); } ssize_t kboot_readin(readin_handle_t fd, vm_offset_t dest, const size_t len) { void *buf; size_t resid, chunk, get; ssize_t got; vm_offset_t p; p = dest; chunk = min(PAGE_SIZE, len); buf = malloc(chunk); if (buf == NULL) { printf("kboot_readin: buf malloc failed\n"); return (0); } for (resid = len; resid > 0; resid -= got, p += got) { get = min(chunk, resid); got = VECTX_READ(fd, buf, get); if (got <= 0) { if (got < 0) printf("kboot_readin: read failed\n"); break; } kboot_copyin(buf, p, got); } free (buf); return (len - resid); } int kboot_autoload(void) { return (0); } static void kboot_kseg_get(int *nseg, void **ptr) { #if 0 int a; for (a = 0; a < nkexec_segments; a++) { printf("kseg_get: %jx %jx %jx %jx\n", (uintmax_t)loaded_segments[a].buf, (uintmax_t)loaded_segments[a].bufsz, (uintmax_t)loaded_segments[a].mem, (uintmax_t)loaded_segments[a].memsz); } #endif *nseg = nkexec_segments; *ptr = &loaded_segments[0]; } static void kboot_zfs_probe(void) { #if defined(LOADER_ZFS_SUPPORT) /* * Open all the disks and partitions we can find to see if there are ZFS * pools on them. */ hostdisk_zfs_probe(); #endif } /* * Since proper fdt command handling function is defined in fdt_loader_cmd.c, * and declaring it as extern is in contradiction with COMMAND_SET() macro * (which uses static pointer), we're defining wrapper function, which * calls the proper fdt handling routine. */ static int command_fdt(int argc, char *argv[]) { return (command_fdt_internal(argc, argv)); } COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);