diff --git a/stand/kboot/hostdisk.c b/stand/kboot/hostdisk.c index a96c38c21182..fab4ee04bb6b 100644 --- a/stand/kboot/hostdisk.c +++ b/stand/kboot/hostdisk.c @@ -1,540 +1,601 @@ /*- * Copyright (C) 2014 Nathan Whitehorn * All rights reserved. * Copyright 2022 Netflix, Inc * * 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 "host_syscall.h" #include "kboot.h" #include "bootstrap.h" #ifdef LOADER_ZFS_SUPPORT #include "libzfs.h" #include #endif static int hostdisk_init(void); static int hostdisk_strategy(void *devdata, int flag, daddr_t dblk, size_t size, char *buf, size_t *rsize); static int hostdisk_open(struct open_file *f, ...); static int hostdisk_close(struct open_file *f); static int hostdisk_ioctl(struct open_file *f, u_long cmd, void *data); static int hostdisk_print(int verbose); static char *hostdisk_fmtdev(struct devdesc *vdev); +static bool hostdisk_match(struct devsw *devsw, const char *devspec); static int hostdisk_parsedev(struct devdesc **idev, const char *devspec, const char **path); struct devsw hostdisk = { .dv_name = "/dev", .dv_type = DEVT_HOSTDISK, .dv_init = hostdisk_init, .dv_strategy = hostdisk_strategy, .dv_open = hostdisk_open, .dv_close = hostdisk_close, .dv_ioctl = hostdisk_ioctl, .dv_print = hostdisk_print, .dv_cleanup = nullsys, .dv_fmtdev = hostdisk_fmtdev, + .dv_match = hostdisk_match, .dv_parsedev = hostdisk_parsedev, }; /* * We need to walk through the /sys/block directories looking for * block devices that we can use. */ #define SYSBLK "/sys/block" #define HOSTDISK_MIN_SIZE (16ul << 20) /* 16MB */ typedef STAILQ_HEAD(, hdinfo) hdinfo_list_t; typedef struct hdinfo { STAILQ_ENTRY(hdinfo) hd_link; /* link in device list */ hdinfo_list_t hd_children; struct hdinfo *hd_parent; const char *hd_dev; uint64_t hd_size; /* In bytes */ uint64_t hd_sectors; uint64_t hd_sectorsize; int hd_flags; #define HDF_HAS_ZPOOL 1 /* We found a zpool here and uuid valid */ uint64_t hd_zfs_uuid; } hdinfo_t; #define dev2hd(d) ((hdinfo_t *)d->d_opendata) #define hd_name(hd) ((hd->hd_dev + 5)) static hdinfo_list_t hdinfo = STAILQ_HEAD_INITIALIZER(hdinfo); typedef bool fef_cb_t(struct host_dirent64 *, void *); #define FEF_RECURSIVE 1 static bool foreach_file(const char *dir, fef_cb_t cb, void *argp, u_int flags) { char dents[2048]; int fd, dentsize; struct host_dirent64 *dent; fd = host_open(dir, O_RDONLY, 0); if (fd < 0) { printf("Can't open %s\n", dir);/* XXX */ return (false); } while (1) { dentsize = host_getdents64(fd, dents, sizeof(dents)); if (dentsize <= 0) break; for (dent = (struct host_dirent64 *)dents; (char *)dent < dents + dentsize; dent = (struct host_dirent64 *)((void *)dent + dent->d_reclen)) { if (!cb(dent, argp)) break; } } host_close(fd); return (true); } static void hostdisk_add_part(hdinfo_t *hd, const char *drv, uint64_t secs) { hdinfo_t *md; char *dev; printf("hd %s adding %s %ju\n", hd->hd_dev, drv, (uintmax_t)secs); if ((md = calloc(1, sizeof(*md))) == NULL) return; if (asprintf(&dev, "/dev/%s", drv) == -1) { printf("hostdisk: no memory\n"); free(md); return; } md->hd_dev = dev; md->hd_sectors = secs; md->hd_sectorsize = hd->hd_sectorsize; md->hd_size = md->hd_sectors * md->hd_sectorsize; md->hd_parent = hd; STAILQ_INSERT_TAIL(&hd->hd_children, md, hd_link); } static bool hostdisk_one_part(struct host_dirent64 *dent, void *argp) { hdinfo_t *hd = argp; char szfn[1024]; uint64_t sz; /* Need to skip /dev/ at start of hd_name */ if (strncmp(dent->d_name, hd_name(hd), strlen(hd_name(hd))) != 0) return (true); /* Find out how big this is -- no size not a disk */ snprintf(szfn, sizeof(szfn), "%s/%s/%s/size", SYSBLK, hd_name(hd), dent->d_name); if (!file2u64(szfn, &sz)) return true; hostdisk_add_part(hd, dent->d_name, sz); return true; } static void hostdisk_add_parts(hdinfo_t *hd) { char fn[1024]; snprintf(fn, sizeof(fn), "%s/%s", SYSBLK, hd_name(hd)); foreach_file(fn, hostdisk_one_part, hd, 0); } static void hostdisk_add_drive(const char *drv, uint64_t secs) { hdinfo_t *hd = NULL; char *dev = NULL; char fn[1024]; if ((hd = calloc(1, sizeof(*hd))) == NULL) return; if (asprintf(&dev, "/dev/%s", drv) == -1) { printf("hostdisk: no memory\n"); free(hd); return; } hd->hd_dev = dev; hd->hd_sectors = secs; snprintf(fn, sizeof(fn), "%s/%s/queue/hw_sector_size", SYSBLK, drv); if (!file2u64(fn, &hd->hd_sectorsize)) goto err; hd->hd_size = hd->hd_sectors * hd->hd_sectorsize; if (hd->hd_size < HOSTDISK_MIN_SIZE) goto err; hd->hd_flags = 0; STAILQ_INIT(&hd->hd_children); printf("/dev/%s: %ju %ju %ju\n", drv, hd->hd_size, hd->hd_sectors, hd->hd_sectorsize); STAILQ_INSERT_TAIL(&hdinfo, hd, hd_link); hostdisk_add_parts(hd); return; err: free(dev); free(hd); return; } /* Find a disk / partition by its filename */ static hdinfo_t * hostdisk_find(const char *fn) { hdinfo_t *hd, *md; STAILQ_FOREACH(hd, &hdinfo, hd_link) { if (strcmp(hd->hd_dev, fn) == 0) return (hd); STAILQ_FOREACH(md, &hd->hd_children, hd_link) { if (strcmp(md->hd_dev, fn) == 0) return (md); } } return (NULL); } static bool hostdisk_one_disk(struct host_dirent64 *dent, void *argp __unused) { char szfn[1024]; uint64_t sz; /* * Skip . and .. */ if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) return (true); /* Find out how big this is -- no size not a disk */ snprintf(szfn, sizeof(szfn), "%s/%s/size", SYSBLK, dent->d_name); if (!file2u64(szfn, &sz)) return (true); hostdisk_add_drive(dent->d_name, sz); return (true); } -static void -hostdisk_find_block_devices(void) +static bool +hostdisk_fake_one_disk(struct host_dirent64 *dent, void *argp) { + char *override_dir = argp; + char *fn = NULL; + hdinfo_t *hd = NULL; + struct host_kstat sb; + /* - * Start here XXX open SYSBLK, walk through all directories, keep the - * ones that return a size and a 'block' device when we 'stat' it. Try - * to avoid partitions and only do raw devices. + * We only do regular files. Each one is treated as a disk image + * accessible via /dev/${dent->d_name}. */ - foreach_file(SYSBLK, hostdisk_one_disk, NULL, 0); + if (dent->d_type != HOST_DT_REG && dent->d_type != HOST_DT_LNK) + return (true); + if (asprintf(&fn, "%s/%s", override_dir, dent->d_name) == -1) + return (true); + if (host_stat(fn, &sb) != 0) + goto err; + if (!HOST_S_ISREG(sb.st_mode)) + return (true); + if (sb.st_size == 0) + goto err; + if ((hd = calloc(1, sizeof(*hd))) == NULL) + goto err; + hd->hd_dev = fn; + hd->hd_size = sb.st_size; + hd->hd_sectorsize = 512; /* XXX configurable? */ + hd->hd_sectors = hd->hd_size / hd->hd_sectorsize; + if (hd->hd_size < HOSTDISK_MIN_SIZE) + goto err; + hd->hd_flags = 0; + STAILQ_INIT(&hd->hd_children); + printf("%s: %ju %ju %ju\n", + hd->hd_dev, hd->hd_size, hd->hd_sectors, hd->hd_sectorsize); + STAILQ_INSERT_TAIL(&hdinfo, hd, hd_link); + /* XXX no partiions? -- is that OK? */ + return (true); +err: + free(hd); + free(fn); + return (true); +} + +static void +hostdisk_find_block_devices(void) +{ + char *override; + + override=getenv("hostdisk_override"); + if (override != NULL) + foreach_file(override, hostdisk_fake_one_disk, override, 0); + else + foreach_file(SYSBLK, hostdisk_one_disk, NULL, 0); } static int hostdisk_init(void) { hostdisk_find_block_devices(); return (0); } static int hostdisk_strategy(void *devdata, int flag, daddr_t dblk, size_t size, char *buf, size_t *rsize) { struct devdesc *desc = devdata; daddr_t pos; int n; int64_t off; uint64_t res; uint32_t posl, posh; pos = dblk * 512; posl = pos & 0xffffffffu; posh = (pos >> 32) & 0xffffffffu; if ((off = host_llseek(desc->d_unit, posh, posl, &res, 0)) < 0) { printf("Seek error on fd %d to %ju (dblk %ju) returns %jd\n", desc->d_unit, (uintmax_t)pos, (uintmax_t)dblk, (intmax_t)off); return (EIO); } n = host_read(desc->d_unit, buf, size); if (n < 0) return (EIO); *rsize = n; return (0); } static int hostdisk_open(struct open_file *f, ...) { struct devdesc *desc; const char *fn; va_list vl; va_start(vl, f); desc = va_arg(vl, struct devdesc *); va_end(vl); fn = dev2hd(desc)->hd_dev; desc->d_unit = host_open(fn, O_RDONLY, 0); if (desc->d_unit <= 0) { printf("hostdisk_open: couldn't open %s: %d\n", fn, errno); return (ENOENT); } return (0); } static int hostdisk_close(struct open_file *f) { struct devdesc *desc = f->f_devdata; host_close(desc->d_unit); return (0); } static int hostdisk_ioctl(struct open_file *f, u_long cmd, void *data) { struct devdesc *desc = f->f_devdata; hdinfo_t *hd = dev2hd(desc); switch (cmd) { case DIOCGSECTORSIZE: *(u_int *)data = hd->hd_sectorsize; break; case DIOCGMEDIASIZE: *(uint64_t *)data = hd->hd_size; break; default: return (ENOTTY); } return (0); } static int hostdisk_print(int verbose) { char line[80]; hdinfo_t *hd, *md; int ret = 0; printf("%s devices:", hostdisk.dv_name); if (pager_output("\n") != 0) return (1); STAILQ_FOREACH(hd, &hdinfo, hd_link) { snprintf(line, sizeof(line), " %s: %ju X %ju: %ju bytes\n", hd->hd_dev, (uintmax_t)hd->hd_sectors, (uintmax_t)hd->hd_sectorsize, (uintmax_t)hd->hd_size); if ((ret = pager_output(line)) != 0) break; STAILQ_FOREACH(md, &hd->hd_children, hd_link) { snprintf(line, sizeof(line), " %s: %ju X %ju: %ju bytes\n", md->hd_dev, (uintmax_t)md->hd_sectors, (uintmax_t)md->hd_sectorsize, (uintmax_t)md->hd_size); if ((ret = pager_output(line)) != 0) goto done; } } done: return (ret); } static char * hostdisk_fmtdev(struct devdesc *vdev) { return ((char *)hd_name(dev2hd(vdev))); } +static bool +hostdisk_match(struct devsw *devsw, const char *devspec) +{ + hdinfo_t *hd; + const char *colon; + char *cp; + + colon = strchr(devspec, ':'); + if (colon == NULL) + return false; + cp = strdup(devspec); + cp[colon - devspec] = '\0'; + hd = hostdisk_find(cp); + free(cp); + return (hd != NULL); +} + static int hostdisk_parsedev(struct devdesc **idev, const char *devspec, const char **path) { const char *cp; struct devdesc *dev; hdinfo_t *hd; int len; char *fn; - /* Must start with /dev */ - if (strncmp(devspec, "/dev", 4) != 0) - return (EINVAL); /* Must have a : in it */ cp = strchr(devspec, ':'); if (cp == NULL) return (EINVAL); /* XXX Stat the /dev or defer error handling to open(2) call? */ if (path != NULL) *path = cp + 1; len = cp - devspec; fn = strdup(devspec); fn[len] = '\0'; hd = hostdisk_find(fn); if (hd == NULL) { printf("Can't find hdinfo for %s\n", fn); + free(fn); return (EINVAL); } free(fn); dev = malloc(sizeof(*dev)); if (dev == NULL) return (ENOMEM); dev->d_unit = 0; dev->d_dev = &hostdisk; dev->d_opendata = hd; *idev = dev; return (0); } #ifdef LOADER_ZFS_SUPPORT static bool hostdisk_zfs_check_one(hdinfo_t *hd) { char *fn; bool found = false; uint64_t pool_uuid; if (asprintf(&fn, "%s:", hd->hd_dev) == -1) return (false); pool_uuid = 0; zfs_probe_dev(fn, &pool_uuid, false); if (pool_uuid != 0) { found = true; hd->hd_flags |= HDF_HAS_ZPOOL; hd->hd_zfs_uuid = pool_uuid; } free(fn); return (found); } void hostdisk_zfs_probe(void) { hdinfo_t *hd, *md; STAILQ_FOREACH(hd, &hdinfo, hd_link) { if (hostdisk_zfs_check_one(hd)) continue; STAILQ_FOREACH(md, &hd->hd_children, hd_link) { hostdisk_zfs_check_one(md); } } } /* XXX refactor */ static bool sanity_check_currdev(void) { struct stat st; return (stat(PATH_DEFAULTS_LOADER_CONF, &st) == 0 || #ifdef PATH_BOOTABLE_TOKEN stat(PATH_BOOTABLE_TOKEN, &st) == 0 || /* non-standard layout */ #endif stat(PATH_KERNEL, &st) == 0); } /* This likely shoud move to libsa/zfs/zfs.c and be used by at least EFI booting */ static bool probe_zfs_currdev(uint64_t pool_guid, uint64_t root_guid, bool setcurrdev) { char *devname; struct zfs_devdesc currdev; char *buf = NULL; bool bootable; currdev.dd.d_dev = &zfs_dev; currdev.dd.d_unit = 0; currdev.pool_guid = pool_guid; currdev.root_guid = root_guid; devname = devformat(&currdev.dd); if (setcurrdev) set_currdev(devname); bootable = sanity_check_currdev(); if (bootable) { buf = malloc(VDEV_PAD_SIZE); if (buf != NULL) { if (zfs_get_bootonce(&currdev, OS_BOOTONCE, buf, VDEV_PAD_SIZE) == 0) { printf("zfs bootonce: %s\n", buf); if (setcurrdev) set_currdev(buf); setenv("zfs-bootonce", buf, 1); } free(buf); (void)zfs_attach_nvstore(&currdev); } init_zfs_boot_options(devname); } return (bootable); } static bool hostdisk_zfs_try_default(hdinfo_t *hd) { return (probe_zfs_currdev(hd->hd_zfs_uuid, 0, true)); } bool hostdisk_zfs_find_default(void) { hdinfo_t *hd, *md; STAILQ_FOREACH(hd, &hdinfo, hd_link) { if (hd->hd_flags & HDF_HAS_ZPOOL) { if (hostdisk_zfs_try_default(hd)) return (true); continue; } STAILQ_FOREACH(md, &hd->hd_children, hd_link) { if (md->hd_flags & HDF_HAS_ZPOOL) { if (hostdisk_zfs_try_default(md)) return (true); } } } return (false); } #endif diff --git a/stand/kboot/main.c b/stand/kboot/main.c index 9a0f8b8baf69..91bc4c06e452 100644 --- a/stand/kboot/main.c +++ b/stand/kboot/main.c @@ -1,425 +1,428 @@ /*- * 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); uint64_t kboot_loadaddr(u_int type, void *data, uint64_t addr); static void kboot_kseg_get(int *nseg, void **ptr); static void kboot_zfs_probe(void); extern int command_fdt_internal(int argc, char *argv[]); /* * 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 = 128*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_loadaddr = kboot_loadaddr; 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 15MB should be plenty */ 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="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. + * 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 */ - printf("WARNING: bare 'zfs:' for boot device not yet implemented\n"); + hostdisk_zfs_find_default(); } #endif printf("Boot device: %s with hostfs_root %s\n", bootdev, hostfs_root); printf("\n%s", bootprog_info); setenv("currdev", bootdev, 1); setenv("loaddev", bootdev, 1); setenv("LINES", "24", 1); setenv("usefdt", "1", 1); /* * 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); } uint64_t kboot_loadaddr(u_int type, void *data, uint64_t addr) { if (type == LOAD_ELF) addr = roundup(addr, PAGE_SIZE); else addr += kboot_get_phys_load_segment(); return (addr); } 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);