1ca987d46SWarner Losh /*- 2ca987d46SWarner Losh * Copyright (c) 1998 Robert Nordier 3ca987d46SWarner Losh * All rights reserved. 4ca987d46SWarner Losh * 5ca987d46SWarner Losh * Redistribution and use in source and binary forms are freely 6ca987d46SWarner Losh * permitted provided that the above copyright notice and this 7ca987d46SWarner Losh * paragraph and the following disclaimer are duplicated in all 8ca987d46SWarner Losh * such forms. 9ca987d46SWarner Losh * 10ca987d46SWarner Losh * This software is provided "AS IS" and without any express or 11ca987d46SWarner Losh * implied warranties, including, without limitation, the implied 12ca987d46SWarner Losh * warranties of merchantability and fitness for a particular 13ca987d46SWarner Losh * purpose. 14ca987d46SWarner Losh */ 15ca987d46SWarner Losh 16ca987d46SWarner Losh #include <sys/cdefs.h> 17ca987d46SWarner Losh __FBSDID("$FreeBSD$"); 18ca987d46SWarner Losh 193830659eSToomas Soome #include <stand.h> 2065628439SWarner Losh 21ca987d46SWarner Losh #include <sys/param.h> 22ca987d46SWarner Losh #include <sys/errno.h> 23ca987d46SWarner Losh #include <sys/diskmbr.h> 24ca987d46SWarner Losh #ifdef GPT 25ca987d46SWarner Losh #include <sys/gpt.h> 26ca987d46SWarner Losh #endif 27ca987d46SWarner Losh #include <sys/reboot.h> 28ca987d46SWarner Losh #include <sys/queue.h> 291dc762d4SToomas Soome #ifdef LOADER_ZFS_SUPPORT 30e307eb94SToomas Soome #include <sys/zfs_bootenv.h> 311dc762d4SToomas Soome #endif 32ca987d46SWarner Losh 33ca987d46SWarner Losh #include <machine/bootinfo.h> 34ca987d46SWarner Losh #include <machine/elf.h> 35ca987d46SWarner Losh #include <machine/pc/bios.h> 36ca987d46SWarner Losh 37ca987d46SWarner Losh #include <stdarg.h> 38ca987d46SWarner Losh #include <stddef.h> 39ca987d46SWarner Losh 40ca987d46SWarner Losh #include <a.out.h> 413830659eSToomas Soome #include "bootstrap.h" 423830659eSToomas Soome #include "libi386.h" 43ca987d46SWarner Losh #include <btxv86.h> 44ca987d46SWarner Losh 45ca987d46SWarner Losh #include "lib.h" 46ca987d46SWarner Losh #include "rbx.h" 47ca987d46SWarner Losh #include "cons.h" 48ca987d46SWarner Losh #include "bootargs.h" 493830659eSToomas Soome #include "disk.h" 503830659eSToomas Soome #include "part.h" 51ca987d46SWarner Losh #include "paths.h" 52ca987d46SWarner Losh 53ca987d46SWarner Losh #include "libzfs.h" 54ca987d46SWarner Losh 55ca987d46SWarner Losh #define ARGS 0x900 56ca987d46SWarner Losh #define NOPT 14 57ca987d46SWarner Losh #define NDEV 3 58ca987d46SWarner Losh 59ca987d46SWarner Losh #define BIOS_NUMDRIVES 0x475 60ca987d46SWarner Losh #define DRV_HARD 0x80 61ca987d46SWarner Losh #define DRV_MASK 0x7f 62ca987d46SWarner Losh 63ca987d46SWarner Losh #define TYPE_AD 0 64ca987d46SWarner Losh #define TYPE_DA 1 65ca987d46SWarner Losh #define TYPE_MAXHARD TYPE_DA 66ca987d46SWarner Losh #define TYPE_FD 2 67ca987d46SWarner Losh 68ca987d46SWarner Losh extern uint32_t _end; 69ca987d46SWarner Losh 70ca987d46SWarner Losh static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */ 71ca987d46SWarner Losh static const unsigned char flags[NOPT] = { 72ca987d46SWarner Losh RBX_DUAL, 73ca987d46SWarner Losh RBX_SERIAL, 74ca987d46SWarner Losh RBX_ASKNAME, 75ca987d46SWarner Losh RBX_CDROM, 76ca987d46SWarner Losh RBX_CONFIG, 77ca987d46SWarner Losh RBX_KDB, 78ca987d46SWarner Losh RBX_GDB, 79ca987d46SWarner Losh RBX_MUTE, 80ca987d46SWarner Losh RBX_NOINTR, 81ca987d46SWarner Losh RBX_PAUSE, 82ca987d46SWarner Losh RBX_QUIET, 83ca987d46SWarner Losh RBX_DFLTROOT, 84ca987d46SWarner Losh RBX_SINGLE, 85ca987d46SWarner Losh RBX_VERBOSE 86ca987d46SWarner Losh }; 87ca987d46SWarner Losh uint32_t opts; 88ca987d46SWarner Losh 8963acab6aSWarner Losh /* 9063acab6aSWarner Losh * Paths to try loading before falling back to the boot2 prompt. 9163acab6aSWarner Losh * 9263acab6aSWarner Losh * /boot/zfsloader must be tried before /boot/loader in order to remain 9363acab6aSWarner Losh * backward compatible with ZFS boot environments where /boot/loader exists 9463acab6aSWarner Losh * but does not have ZFS support, which was the case before FreeBSD 12. 9563acab6aSWarner Losh * 9663acab6aSWarner Losh * If no loader is found, try to load a kernel directly instead. 9763acab6aSWarner Losh */ 9863acab6aSWarner Losh static const struct string { 9963acab6aSWarner Losh const char *p; 10063acab6aSWarner Losh size_t len; 10163acab6aSWarner Losh } loadpath[] = { 10263acab6aSWarner Losh { PATH_LOADER_ZFS, sizeof(PATH_LOADER_ZFS) }, 10363acab6aSWarner Losh { PATH_LOADER, sizeof(PATH_LOADER) }, 10463acab6aSWarner Losh { PATH_KERNEL, sizeof(PATH_KERNEL) }, 10563acab6aSWarner Losh }; 10663acab6aSWarner Losh 107ca987d46SWarner Losh static const unsigned char dev_maj[NDEV] = {30, 4, 2}; 108ca987d46SWarner Losh 1093830659eSToomas Soome static struct i386_devdesc *bdev; 110ca987d46SWarner Losh static char cmd[512]; 111ca987d46SWarner Losh static char cmddup[512]; 112ca987d46SWarner Losh static char kname[1024]; 113ca987d46SWarner Losh static int comspeed = SIOSPD; 114ca987d46SWarner Losh static struct bootinfo bootinfo; 115ca987d46SWarner Losh static uint32_t bootdev; 116ca987d46SWarner Losh static struct zfs_boot_args zfsargs; 1173830659eSToomas Soome #ifdef LOADER_GELI_SUPPORT 1183830659eSToomas Soome static struct geli_boot_args geliargs; 1193830659eSToomas Soome #endif 120ca987d46SWarner Losh 1213830659eSToomas Soome extern vm_offset_t high_heap_base; 1223830659eSToomas Soome extern uint32_t bios_basemem, bios_extmem, high_heap_size; 123ca987d46SWarner Losh 1243830659eSToomas Soome static char *heap_top; 1253830659eSToomas Soome static char *heap_bottom; 126ca987d46SWarner Losh 127ca987d46SWarner Losh void exit(int); 1283830659eSToomas Soome static void i386_zfs_probe(void); 129ca987d46SWarner Losh static void load(void); 130ca987d46SWarner Losh static int parse_cmd(void); 131ca987d46SWarner Losh 132ca987d46SWarner Losh #ifdef LOADER_GELI_SUPPORT 133c1418270SIan Lepore #include "geliboot.h" 134ca987d46SWarner Losh static char gelipw[GELI_PW_MAXLEN]; 135ca987d46SWarner Losh #endif 136ca987d46SWarner Losh 1373830659eSToomas Soome struct arch_switch archsw; /* MI/MD interface boundary */ 1383830659eSToomas Soome static char boot_devname[2 * ZFS_MAXNAMELEN + 8]; /* disk or pool:dataset */ 1393830659eSToomas Soome 1403830659eSToomas Soome struct devsw *devsw[] = { 1413830659eSToomas Soome &bioshd, 1423830659eSToomas Soome #if defined(LOADER_ZFS_SUPPORT) 1433830659eSToomas Soome &zfs_dev, 144c1418270SIan Lepore #endif 1453830659eSToomas Soome NULL 146c1418270SIan Lepore }; 147c1418270SIan Lepore 1483830659eSToomas Soome struct fs_ops *file_system[] = { 1493830659eSToomas Soome #if defined(LOADER_ZFS_SUPPORT) 1503830659eSToomas Soome &zfs_fsops, 151ca987d46SWarner Losh #endif 1523830659eSToomas Soome #if defined(LOADER_UFS_SUPPORT) 1533830659eSToomas Soome &ufs_fsops, 1543a1f80e2SToomas Soome #endif 1553830659eSToomas Soome NULL 1563830659eSToomas Soome }; 1573a1f80e2SToomas Soome 1583830659eSToomas Soome caddr_t 1593830659eSToomas Soome ptov(uintptr_t x) 160ca987d46SWarner Losh { 1613830659eSToomas Soome return (PTOV(x)); 162ca987d46SWarner Losh } 163ca987d46SWarner Losh 164ca987d46SWarner Losh int 165ca987d46SWarner Losh main(void) 166ca987d46SWarner Losh { 1673830659eSToomas Soome unsigned i; 1683830659eSToomas Soome int auto_boot, fd, nextboot = 0; 1693830659eSToomas Soome struct disk_devdesc devdesc; 170ca987d46SWarner Losh 171ca987d46SWarner Losh bios_getmem(); 172ca987d46SWarner Losh 173ca987d46SWarner Losh if (high_heap_size > 0) { 1743830659eSToomas Soome heap_top = PTOV(high_heap_base + high_heap_size); 1753830659eSToomas Soome heap_bottom = PTOV(high_heap_base); 176ca987d46SWarner Losh } else { 1773830659eSToomas Soome heap_bottom = (char *) 1783830659eSToomas Soome (roundup2(__base + (int32_t)&_end, 0x10000) - __base); 1793830659eSToomas Soome heap_top = (char *)PTOV(bios_basemem); 180ca987d46SWarner Losh } 1813830659eSToomas Soome setheap(heap_bottom, heap_top); 182ca987d46SWarner Losh 1833830659eSToomas Soome /* 1843830659eSToomas Soome * Initialise the block cache. Set the upper limit. 1853830659eSToomas Soome */ 1863830659eSToomas Soome bcache_init(32768, 512); 1873830659eSToomas Soome 1883830659eSToomas Soome archsw.arch_autoload = NULL; 1893830659eSToomas Soome archsw.arch_getdev = i386_getdev; 1903830659eSToomas Soome archsw.arch_copyin = NULL; 1913830659eSToomas Soome archsw.arch_copyout = NULL; 1923830659eSToomas Soome archsw.arch_readin = NULL; 1933830659eSToomas Soome archsw.arch_isainb = NULL; 1943830659eSToomas Soome archsw.arch_isaoutb = NULL; 1953830659eSToomas Soome archsw.arch_zfs_probe = i386_zfs_probe; 196ca987d46SWarner Losh 197ca987d46SWarner Losh bootinfo.bi_version = BOOTINFO_VERSION; 198ca987d46SWarner Losh bootinfo.bi_size = sizeof(bootinfo); 199ca987d46SWarner Losh bootinfo.bi_basemem = bios_basemem / 1024; 200ca987d46SWarner Losh bootinfo.bi_extmem = bios_extmem / 1024; 201ca987d46SWarner Losh bootinfo.bi_memsizes_valid++; 2023830659eSToomas Soome bootinfo.bi_bios_dev = *(uint8_t *)PTOV(ARGS); 203ca987d46SWarner Losh 2043830659eSToomas Soome /* Set up fall back device name. */ 2053830659eSToomas Soome snprintf(boot_devname, sizeof (boot_devname), "disk%d:", 2063830659eSToomas Soome bd_bios2unit(bootinfo.bi_bios_dev)); 207ca987d46SWarner Losh 2083830659eSToomas Soome for (i = 0; devsw[i] != NULL; i++) 2093830659eSToomas Soome if (devsw[i]->dv_init != NULL) 2103830659eSToomas Soome (devsw[i]->dv_init)(); 211ca987d46SWarner Losh 2123830659eSToomas Soome disk_parsedev(&devdesc, boot_devname + 4, NULL); 213ca987d46SWarner Losh 2143830659eSToomas Soome bootdev = MAKEBOOTDEV(dev_maj[DEVT_DISK], devdesc.d_slice + 1, 2153830659eSToomas Soome devdesc.dd.d_unit, 2163830659eSToomas Soome devdesc.d_partition >= 0 ? devdesc.d_partition : 0xff); 217ca987d46SWarner Losh 218ca987d46SWarner Losh /* 2193830659eSToomas Soome * zfs_fmtdev() can be called only after dv_init 220ca987d46SWarner Losh */ 2213830659eSToomas Soome if (bdev != NULL && bdev->dd.d_dev->dv_type == DEVT_ZFS) { 2223830659eSToomas Soome /* set up proper device name string for ZFS */ 2233830659eSToomas Soome strncpy(boot_devname, zfs_fmtdev(bdev), sizeof (boot_devname)); 224e307eb94SToomas Soome if (zfs_get_bootonce(bdev, OS_BOOTONCE, cmd, 225e307eb94SToomas Soome sizeof(cmd)) == 0) { 226e307eb94SToomas Soome nvlist_t *benv; 227e307eb94SToomas Soome 228ca987d46SWarner Losh nextboot = 1; 229ca987d46SWarner Losh memcpy(cmddup, cmd, sizeof(cmd)); 230ca987d46SWarner Losh if (parse_cmd()) { 2313830659eSToomas Soome if (!OPT_CHECK(RBX_QUIET)) 232e307eb94SToomas Soome printf("failed to parse bootonce " 233e307eb94SToomas Soome "command\n"); 2343830659eSToomas Soome exit(0); 235ca987d46SWarner Losh } 236ca987d46SWarner Losh if (!OPT_CHECK(RBX_QUIET)) 237e307eb94SToomas Soome printf("zfs bootonce: %s\n", cmddup); 238e307eb94SToomas Soome 239e307eb94SToomas Soome if (zfs_get_bootenv(bdev, &benv) == 0) { 240e307eb94SToomas Soome nvlist_add_string(benv, OS_BOOTONCE_USED, 241e307eb94SToomas Soome cmddup); 242e307eb94SToomas Soome zfs_set_bootenv(bdev, benv); 243e307eb94SToomas Soome } 244ca987d46SWarner Losh /* Do not process this command twice */ 245ca987d46SWarner Losh *cmd = 0; 246ca987d46SWarner Losh } 2473830659eSToomas Soome } 248ca987d46SWarner Losh 2493830659eSToomas Soome /* now make sure we have bdev on all cases */ 2503830659eSToomas Soome free(bdev); 2513830659eSToomas Soome i386_getdev((void **)&bdev, boot_devname, NULL); 2523830659eSToomas Soome 2533830659eSToomas Soome env_setenv("currdev", EV_VOLATILE, boot_devname, i386_setcurrdev, 2543830659eSToomas Soome env_nounset); 2553830659eSToomas Soome 2563830659eSToomas Soome /* Process configuration file */ 2573830659eSToomas Soome auto_boot = 1; 2583830659eSToomas Soome 2593830659eSToomas Soome fd = open(PATH_CONFIG, O_RDONLY); 2603830659eSToomas Soome if (fd == -1) 2613830659eSToomas Soome fd = open(PATH_DOTCONFIG, O_RDONLY); 2623830659eSToomas Soome 2633830659eSToomas Soome if (fd != -1) { 264c7dd069cSGleb Smirnoff ssize_t cmdlen; 265c7dd069cSGleb Smirnoff 266c7dd069cSGleb Smirnoff if ((cmdlen = read(fd, cmd, sizeof(cmd))) > 0) 267c7dd069cSGleb Smirnoff cmd[cmdlen] = '\0'; 268c7dd069cSGleb Smirnoff else 269c7dd069cSGleb Smirnoff *cmd = '\0'; 2703830659eSToomas Soome close(fd); 271ca987d46SWarner Losh } 272ca987d46SWarner Losh 273ca987d46SWarner Losh if (*cmd) { 274ca987d46SWarner Losh /* 275dfdeb454SToomas Soome * Note that parse_cmd() is destructive to cmd[] and we also 276dfdeb454SToomas Soome * want to honor RBX_QUIET option that could be present in 277dfdeb454SToomas Soome * cmd[]. 278ca987d46SWarner Losh */ 279ca987d46SWarner Losh memcpy(cmddup, cmd, sizeof(cmd)); 280ca987d46SWarner Losh if (parse_cmd()) 2813830659eSToomas Soome auto_boot = 0; 282ca987d46SWarner Losh if (!OPT_CHECK(RBX_QUIET)) 283ca987d46SWarner Losh printf("%s: %s\n", PATH_CONFIG, cmddup); 284ca987d46SWarner Losh /* Do not process this command twice */ 285ca987d46SWarner Losh *cmd = 0; 286ca987d46SWarner Losh } 287ca987d46SWarner Losh 288ca987d46SWarner Losh /* Do not risk waiting at the prompt forever. */ 2893830659eSToomas Soome if (nextboot && !auto_boot) 2903830659eSToomas Soome exit(0); 291ca987d46SWarner Losh 2923830659eSToomas Soome if (auto_boot && !*kname) { 29363acab6aSWarner Losh /* 294dfdeb454SToomas Soome * Iterate through the list of loader and kernel paths, 295dfdeb454SToomas Soome * trying to load. If interrupted by a keypress, or in case of 296dfdeb454SToomas Soome * failure, drop the user to the boot2 prompt. 29763acab6aSWarner Losh */ 29863acab6aSWarner Losh for (i = 0; i < nitems(loadpath); i++) { 29963acab6aSWarner Losh memcpy(kname, loadpath[i].p, loadpath[i].len); 30063acab6aSWarner Losh if (keyhit(3)) 30163acab6aSWarner Losh break; 302ca987d46SWarner Losh load(); 303ca987d46SWarner Losh } 304ca987d46SWarner Losh } 305ca987d46SWarner Losh 306ca987d46SWarner Losh /* Present the user with the boot2 prompt. */ 307ca987d46SWarner Losh 308ca987d46SWarner Losh for (;;) { 3093830659eSToomas Soome if (!auto_boot || !OPT_CHECK(RBX_QUIET)) { 310ca987d46SWarner Losh printf("\nFreeBSD/x86 boot\n"); 3113830659eSToomas Soome printf("Default: %s%s\nboot: ", boot_devname, kname); 312ca987d46SWarner Losh } 313ca987d46SWarner Losh if (ioctrl & IO_SERIAL) 314ca987d46SWarner Losh sio_flush(); 3153830659eSToomas Soome if (!auto_boot || keyhit(5)) 316ca987d46SWarner Losh getstr(cmd, sizeof(cmd)); 3173830659eSToomas Soome else if (!auto_boot || !OPT_CHECK(RBX_QUIET)) 318ca987d46SWarner Losh putchar('\n'); 3193830659eSToomas Soome auto_boot = 0; 320ca987d46SWarner Losh if (parse_cmd()) 321ca987d46SWarner Losh putchar('\a'); 322ca987d46SWarner Losh else 323ca987d46SWarner Losh load(); 324ca987d46SWarner Losh } 325ca987d46SWarner Losh } 326ca987d46SWarner Losh 327ca987d46SWarner Losh /* XXX - Needed for btxld to link the boot2 binary; do not remove. */ 328ca987d46SWarner Losh void 329ca987d46SWarner Losh exit(int x) 330ca987d46SWarner Losh { 331ca987d46SWarner Losh __exit(x); 332ca987d46SWarner Losh } 333ca987d46SWarner Losh 334ca987d46SWarner Losh static void 335ca987d46SWarner Losh load(void) 336ca987d46SWarner Losh { 337ca987d46SWarner Losh union { 338ca987d46SWarner Losh struct exec ex; 339ca987d46SWarner Losh Elf32_Ehdr eh; 340ca987d46SWarner Losh } hdr; 341ca987d46SWarner Losh static Elf32_Phdr ep[2]; 342ca987d46SWarner Losh static Elf32_Shdr es[2]; 343ca987d46SWarner Losh caddr_t p; 344ca987d46SWarner Losh uint32_t addr, x; 3453830659eSToomas Soome int fd, fmt, i, j; 3463830659eSToomas Soome ssize_t size; 347ca987d46SWarner Losh 3483830659eSToomas Soome if ((fd = open(kname, O_RDONLY)) == -1) { 349ca987d46SWarner Losh printf("\nCan't find %s\n", kname); 350ca987d46SWarner Losh return; 351ca987d46SWarner Losh } 3523830659eSToomas Soome 3533830659eSToomas Soome size = sizeof(hdr); 3543830659eSToomas Soome if (read(fd, &hdr, sizeof (hdr)) != size) { 3553830659eSToomas Soome close(fd); 356ca987d46SWarner Losh return; 3573830659eSToomas Soome } 3583830659eSToomas Soome if (N_GETMAGIC(hdr.ex) == ZMAGIC) { 359ca987d46SWarner Losh fmt = 0; 3603830659eSToomas Soome } else if (IS_ELF(hdr.eh)) { 361ca987d46SWarner Losh fmt = 1; 3623830659eSToomas Soome } else { 363ca987d46SWarner Losh printf("Invalid %s\n", "format"); 3643830659eSToomas Soome close(fd); 365ca987d46SWarner Losh return; 366ca987d46SWarner Losh } 367ca987d46SWarner Losh if (fmt == 0) { 368ca987d46SWarner Losh addr = hdr.ex.a_entry & 0xffffff; 369ca987d46SWarner Losh p = PTOV(addr); 3703830659eSToomas Soome lseek(fd, PAGE_SIZE, SEEK_SET); 3713830659eSToomas Soome size = hdr.ex.a_text; 3723830659eSToomas Soome if (read(fd, p, hdr.ex.a_text) != size) { 3733830659eSToomas Soome close(fd); 374ca987d46SWarner Losh return; 3753830659eSToomas Soome } 376ca987d46SWarner Losh p += roundup2(hdr.ex.a_text, PAGE_SIZE); 3773830659eSToomas Soome size = hdr.ex.a_data; 3783830659eSToomas Soome if (read(fd, p, hdr.ex.a_data) != size) { 3793830659eSToomas Soome close(fd); 380ca987d46SWarner Losh return; 3813830659eSToomas Soome } 382ca987d46SWarner Losh p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE); 383ca987d46SWarner Losh bootinfo.bi_symtab = VTOP(p); 384ca987d46SWarner Losh memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); 385ca987d46SWarner Losh p += sizeof(hdr.ex.a_syms); 386ca987d46SWarner Losh if (hdr.ex.a_syms) { 3873830659eSToomas Soome size = hdr.ex.a_syms; 3883830659eSToomas Soome if (read(fd, p, hdr.ex.a_syms) != size) { 3893830659eSToomas Soome close(fd); 390ca987d46SWarner Losh return; 3913830659eSToomas Soome } 392ca987d46SWarner Losh p += hdr.ex.a_syms; 3933830659eSToomas Soome size = sizeof (int); 3943830659eSToomas Soome if (read(fd, p, sizeof (int)) != size) { 3953830659eSToomas Soome close(fd); 396ca987d46SWarner Losh return; 3973830659eSToomas Soome } 398ca987d46SWarner Losh x = *(uint32_t *)p; 399ca987d46SWarner Losh p += sizeof(int); 400ca987d46SWarner Losh x -= sizeof(int); 4013830659eSToomas Soome size = x; 4023830659eSToomas Soome if (read(fd, p, x) != size) { 4033830659eSToomas Soome close(fd); 404ca987d46SWarner Losh return; 4053830659eSToomas Soome } 406ca987d46SWarner Losh p += x; 407ca987d46SWarner Losh } 408ca987d46SWarner Losh } else { 4093830659eSToomas Soome lseek(fd, hdr.eh.e_phoff, SEEK_SET); 410ca987d46SWarner Losh for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { 4113830659eSToomas Soome size = sizeof (ep[0]); 4123830659eSToomas Soome if (read(fd, ep + j, sizeof (ep[0])) != size) { 4133830659eSToomas Soome close(fd); 414ca987d46SWarner Losh return; 4153830659eSToomas Soome } 416ca987d46SWarner Losh if (ep[j].p_type == PT_LOAD) 417ca987d46SWarner Losh j++; 418ca987d46SWarner Losh } 419ca987d46SWarner Losh for (i = 0; i < 2; i++) { 420ca987d46SWarner Losh p = PTOV(ep[i].p_paddr & 0xffffff); 4213830659eSToomas Soome lseek(fd, ep[i].p_offset, SEEK_SET); 4223830659eSToomas Soome size = ep[i].p_filesz; 4233830659eSToomas Soome if (read(fd, p, ep[i].p_filesz) != size) { 4243830659eSToomas Soome close(fd); 425ca987d46SWarner Losh return; 426ca987d46SWarner Losh } 4273830659eSToomas Soome } 428ca987d46SWarner Losh p += roundup2(ep[1].p_memsz, PAGE_SIZE); 429ca987d46SWarner Losh bootinfo.bi_symtab = VTOP(p); 430ca987d46SWarner Losh if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { 4313830659eSToomas Soome lseek(fd, hdr.eh.e_shoff + 4323830659eSToomas Soome sizeof (es[0]) * (hdr.eh.e_shstrndx + 1), 4333830659eSToomas Soome SEEK_SET); 4343830659eSToomas Soome size = sizeof(es); 4353830659eSToomas Soome if (read(fd, &es, sizeof (es)) != size) { 4363830659eSToomas Soome close(fd); 437ca987d46SWarner Losh return; 4383830659eSToomas Soome } 439ca987d46SWarner Losh for (i = 0; i < 2; i++) { 440dfdeb454SToomas Soome memcpy(p, &es[i].sh_size, 441dfdeb454SToomas Soome sizeof(es[i].sh_size)); 442ca987d46SWarner Losh p += sizeof(es[i].sh_size); 4433830659eSToomas Soome lseek(fd, es[i].sh_offset, SEEK_SET); 4443830659eSToomas Soome size = es[i].sh_size; 4453830659eSToomas Soome if (read(fd, p, es[i].sh_size) != size) { 4463830659eSToomas Soome close(fd); 447ca987d46SWarner Losh return; 4483830659eSToomas Soome } 449ca987d46SWarner Losh p += es[i].sh_size; 450ca987d46SWarner Losh } 451ca987d46SWarner Losh } 452ca987d46SWarner Losh addr = hdr.eh.e_entry & 0xffffff; 453ca987d46SWarner Losh } 4543830659eSToomas Soome close(fd); 4553830659eSToomas Soome 456ca987d46SWarner Losh bootinfo.bi_esymtab = VTOP(p); 457ca987d46SWarner Losh bootinfo.bi_kernelname = VTOP(kname); 458ca987d46SWarner Losh #ifdef LOADER_GELI_SUPPORT 459ca987d46SWarner Losh explicit_bzero(gelipw, sizeof(gelipw)); 4603830659eSToomas Soome #endif 4613830659eSToomas Soome 4623830659eSToomas Soome if (bdev->dd.d_dev->dv_type == DEVT_ZFS) { 4633830659eSToomas Soome zfsargs.size = sizeof(zfsargs); 4643830659eSToomas Soome zfsargs.pool = bdev->d_kind.zfs.pool_guid; 4653830659eSToomas Soome zfsargs.root = bdev->d_kind.zfs.root_guid; 4663830659eSToomas Soome #ifdef LOADER_GELI_SUPPORT 467df108aafSIan Lepore export_geli_boot_data(&zfsargs.gelidata); 468ca987d46SWarner Losh #endif 469b92c2c90SIan Lepore /* 4703830659eSToomas Soome * Note that the zfsargs struct is passed by value, not by 4713830659eSToomas Soome * pointer. Code in btxldr.S copies the values from the entry 4723830659eSToomas Soome * stack to a fixed location within loader(8) at startup due 4733830659eSToomas Soome * to the presence of KARGS_FLAGS_EXTARG. 474b92c2c90SIan Lepore */ 475ca987d46SWarner Losh __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), 476ca987d46SWarner Losh bootdev, 477ca987d46SWarner Losh KARGS_FLAGS_ZFS | KARGS_FLAGS_EXTARG, 4783830659eSToomas Soome (uint32_t)bdev->d_kind.zfs.pool_guid, 4793830659eSToomas Soome (uint32_t)(bdev->d_kind.zfs.pool_guid >> 32), 480ca987d46SWarner Losh VTOP(&bootinfo), 481ca987d46SWarner Losh zfsargs); 4823830659eSToomas Soome } else { 4833830659eSToomas Soome #ifdef LOADER_GELI_SUPPORT 4843830659eSToomas Soome geliargs.size = sizeof(geliargs); 4853830659eSToomas Soome export_geli_boot_data(&geliargs.gelidata); 4863830659eSToomas Soome #endif 4873830659eSToomas Soome 4883830659eSToomas Soome /* 4893830659eSToomas Soome * Note that the geliargs struct is passed by value, not by 4903830659eSToomas Soome * pointer. Code in btxldr.S copies the values from the entry 4913830659eSToomas Soome * stack to a fixed location within loader(8) at startup due 4923830659eSToomas Soome * to the presence of the KARGS_FLAGS_EXTARG flag. 4933830659eSToomas Soome */ 4943830659eSToomas Soome __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), 4953830659eSToomas Soome bootdev, 4963830659eSToomas Soome #ifdef LOADER_GELI_SUPPORT 4973830659eSToomas Soome KARGS_FLAGS_GELI | KARGS_FLAGS_EXTARG, 0, 0, 4983830659eSToomas Soome VTOP(&bootinfo), geliargs 4993830659eSToomas Soome #else 5003830659eSToomas Soome 0, 0, 0, VTOP(&bootinfo) 5013830659eSToomas Soome #endif 5023830659eSToomas Soome ); 5033830659eSToomas Soome } 504ca987d46SWarner Losh } 505ca987d46SWarner Losh 506ca987d46SWarner Losh static int 5073830659eSToomas Soome mount_root(char *arg) 508ca987d46SWarner Losh { 5093830659eSToomas Soome char *root; 5103830659eSToomas Soome struct i386_devdesc *ddesc; 5113830659eSToomas Soome uint8_t part; 512ca987d46SWarner Losh 5133830659eSToomas Soome if (asprintf(&root, "%s:", arg) < 0) 5143830659eSToomas Soome return (1); 5153830659eSToomas Soome 5163830659eSToomas Soome if (i386_getdev((void **)&ddesc, root, NULL)) { 5173830659eSToomas Soome free(root); 5183830659eSToomas Soome return (1); 519ca987d46SWarner Losh } 520ca987d46SWarner Losh 5213830659eSToomas Soome /* we should have new device descriptor, free old and replace it. */ 5223830659eSToomas Soome free(bdev); 5233830659eSToomas Soome bdev = ddesc; 5243830659eSToomas Soome if (bdev->dd.d_dev->dv_type == DEVT_DISK) { 5253830659eSToomas Soome if (bdev->d_kind.biosdisk.partition == -1) 5263830659eSToomas Soome part = 0xff; 5273830659eSToomas Soome else 5283830659eSToomas Soome part = bdev->d_kind.biosdisk.partition; 5293830659eSToomas Soome bootdev = MAKEBOOTDEV(dev_maj[bdev->dd.d_dev->dv_type], 5303830659eSToomas Soome bdev->d_kind.biosdisk.slice + 1, 5313830659eSToomas Soome bdev->dd.d_unit, part); 5323830659eSToomas Soome bootinfo.bi_bios_dev = bd_unit2bios(bdev); 533ca987d46SWarner Losh } 5343830659eSToomas Soome strncpy(boot_devname, root, sizeof (boot_devname)); 5353830659eSToomas Soome setenv("currdev", root, 1); 5363830659eSToomas Soome free(root); 537ca987d46SWarner Losh return (0); 538ca987d46SWarner Losh } 539ca987d46SWarner Losh 5403830659eSToomas Soome static void 5413830659eSToomas Soome fs_list(char *arg) 5423830659eSToomas Soome { 5433830659eSToomas Soome int fd; 5443830659eSToomas Soome struct dirent *d; 5453830659eSToomas Soome char line[80]; 5463830659eSToomas Soome 5473830659eSToomas Soome fd = open(arg, O_RDONLY); 5483830659eSToomas Soome if (fd < 0) 5493830659eSToomas Soome return; 5503830659eSToomas Soome pager_open(); 5513830659eSToomas Soome while ((d = readdirfd(fd)) != NULL) { 5523830659eSToomas Soome sprintf(line, "%s\n", d->d_name); 5533830659eSToomas Soome if (pager_output(line)) 5543830659eSToomas Soome break; 5553830659eSToomas Soome } 5563830659eSToomas Soome pager_close(); 5573830659eSToomas Soome close(fd); 5583830659eSToomas Soome } 5593830659eSToomas Soome 560ca987d46SWarner Losh static int 561ca987d46SWarner Losh parse_cmd(void) 562ca987d46SWarner Losh { 563ca987d46SWarner Losh char *arg = cmd; 564ca987d46SWarner Losh char *ep, *p, *q; 565ca987d46SWarner Losh const char *cp; 5663830659eSToomas Soome char line[80]; 567ca987d46SWarner Losh int c, i, j; 568ca987d46SWarner Losh 569ca987d46SWarner Losh while ((c = *arg++)) { 570ca987d46SWarner Losh if (c == ' ' || c == '\t' || c == '\n') 571ca987d46SWarner Losh continue; 572dfdeb454SToomas Soome for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++) 573dfdeb454SToomas Soome ; 574ca987d46SWarner Losh ep = p; 575ca987d46SWarner Losh if (*p) 576ca987d46SWarner Losh *p++ = 0; 577ca987d46SWarner Losh if (c == '-') { 578ca987d46SWarner Losh while ((c = *arg++)) { 579ca987d46SWarner Losh if (c == 'P') { 580ca987d46SWarner Losh if (*(uint8_t *)PTOV(0x496) & 0x10) { 581ca987d46SWarner Losh cp = "yes"; 582ca987d46SWarner Losh } else { 583dfdeb454SToomas Soome opts |= OPT_SET(RBX_DUAL); 584dfdeb454SToomas Soome opts |= OPT_SET(RBX_SERIAL); 585ca987d46SWarner Losh cp = "no"; 586ca987d46SWarner Losh } 587ca987d46SWarner Losh printf("Keyboard: %s\n", cp); 588ca987d46SWarner Losh continue; 589ca987d46SWarner Losh } else if (c == 'S') { 590ca987d46SWarner Losh j = 0; 591dfdeb454SToomas Soome while ((unsigned int) 592dfdeb454SToomas Soome (i = *arg++ - '0') <= 9) 593ca987d46SWarner Losh j = j * 10 + i; 594ca987d46SWarner Losh if (j > 0 && i == -'0') { 595ca987d46SWarner Losh comspeed = j; 596ca987d46SWarner Losh break; 597ca987d46SWarner Losh } 598dfdeb454SToomas Soome /* 599dfdeb454SToomas Soome * Fall through to error below 600dfdeb454SToomas Soome * ('S' not in optstr[]). 601dfdeb454SToomas Soome */ 602ca987d46SWarner Losh } 603ca987d46SWarner Losh for (i = 0; c != optstr[i]; i++) 604ca987d46SWarner Losh if (i == NOPT - 1) 605dfdeb454SToomas Soome return (-1); 606ca987d46SWarner Losh opts ^= OPT_SET(flags[i]); 607ca987d46SWarner Losh } 608ca987d46SWarner Losh ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : 609ca987d46SWarner Losh OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD; 610ca987d46SWarner Losh if (ioctrl & IO_SERIAL) { 611ca987d46SWarner Losh if (sio_init(115200 / comspeed) != 0) 612ca987d46SWarner Losh ioctrl &= ~IO_SERIAL; 613ca987d46SWarner Losh } 614ca987d46SWarner Losh } if (c == '?') { 6153830659eSToomas Soome printf("\n"); 6163830659eSToomas Soome if (*arg == '\0') 6173830659eSToomas Soome arg = (char *)"/"; 6183830659eSToomas Soome fs_list(arg); 6193830659eSToomas Soome zfs_list(arg); 620dfdeb454SToomas Soome return (-1); 621ca987d46SWarner Losh } else { 6223830659eSToomas Soome char *ptr; 6233830659eSToomas Soome printf("\n"); 624ca987d46SWarner Losh arg--; 625ca987d46SWarner Losh 626ca987d46SWarner Losh /* 627ca987d46SWarner Losh * Report pool status if the comment is 'status'. Lets 628ca987d46SWarner Losh * hope no-one wants to load /status as a kernel. 629ca987d46SWarner Losh */ 630dfdeb454SToomas Soome if (strcmp(arg, "status") == 0) { 6313830659eSToomas Soome pager_open(); 6323830659eSToomas Soome for (i = 0; devsw[i] != NULL; i++) { 6333830659eSToomas Soome if (devsw[i]->dv_print != NULL) { 6343830659eSToomas Soome if (devsw[i]->dv_print(1)) 6353830659eSToomas Soome break; 6363830659eSToomas Soome } else { 6373830659eSToomas Soome snprintf(line, sizeof(line), 6383830659eSToomas Soome "%s: (unknown)\n", 6393830659eSToomas Soome devsw[i]->dv_name); 6403830659eSToomas Soome if (pager_output(line)) 6413830659eSToomas Soome break; 6423830659eSToomas Soome } 6433830659eSToomas Soome } 6443830659eSToomas Soome pager_close(); 645dfdeb454SToomas Soome return (-1); 646ca987d46SWarner Losh } 647ca987d46SWarner Losh 648ca987d46SWarner Losh /* 649ca987d46SWarner Losh * If there is "zfs:" prefix simply ignore it. 650ca987d46SWarner Losh */ 6513830659eSToomas Soome ptr = arg; 6523830659eSToomas Soome if (strncmp(ptr, "zfs:", 4) == 0) 6533830659eSToomas Soome ptr += 4; 654ca987d46SWarner Losh 655ca987d46SWarner Losh /* 656ca987d46SWarner Losh * If there is a colon, switch pools. 657ca987d46SWarner Losh */ 6583830659eSToomas Soome q = strchr(ptr, ':'); 659ca987d46SWarner Losh if (q) { 660ca987d46SWarner Losh *q++ = '\0'; 6613830659eSToomas Soome if (mount_root(arg) != 0) { 662dfdeb454SToomas Soome return (-1); 6633830659eSToomas Soome } 664ca987d46SWarner Losh arg = q; 665ca987d46SWarner Losh } 666ca987d46SWarner Losh if ((i = ep - arg)) { 667ca987d46SWarner Losh if ((size_t)i >= sizeof(kname)) 668dfdeb454SToomas Soome return (-1); 669ca987d46SWarner Losh memcpy(kname, arg, i + 1); 670ca987d46SWarner Losh } 671ca987d46SWarner Losh } 672ca987d46SWarner Losh arg = p; 673ca987d46SWarner Losh } 674dfdeb454SToomas Soome return (0); 675ca987d46SWarner Losh } 6763830659eSToomas Soome 6773830659eSToomas Soome /* 6783830659eSToomas Soome * Probe all disks to discover ZFS pools. The idea is to walk all possible 6793830659eSToomas Soome * disk devices, however, we also need to identify possible boot pool. 6803830659eSToomas Soome * For boot pool detection we have boot disk passed us from BIOS, recorded 6813830659eSToomas Soome * in bootinfo.bi_bios_dev. 6823830659eSToomas Soome */ 6833830659eSToomas Soome static void 6843830659eSToomas Soome i386_zfs_probe(void) 6853830659eSToomas Soome { 6863830659eSToomas Soome char devname[32]; 6873830659eSToomas Soome int boot_unit; 6883830659eSToomas Soome struct i386_devdesc dev; 6893830659eSToomas Soome uint64_t pool_guid = 0; 6903830659eSToomas Soome 6913830659eSToomas Soome dev.dd.d_dev = &bioshd; 6923830659eSToomas Soome /* Translate bios dev to our unit number. */ 6933830659eSToomas Soome boot_unit = bd_bios2unit(bootinfo.bi_bios_dev); 6943830659eSToomas Soome 6953830659eSToomas Soome /* 6963830659eSToomas Soome * Open all the disks we can find and see if we can reconstruct 6973830659eSToomas Soome * ZFS pools from them. 6983830659eSToomas Soome */ 6993830659eSToomas Soome for (dev.dd.d_unit = 0; bd_unit2bios(&dev) >= 0; dev.dd.d_unit++) { 7003830659eSToomas Soome snprintf(devname, sizeof (devname), "%s%d:", bioshd.dv_name, 7013830659eSToomas Soome dev.dd.d_unit); 7023830659eSToomas Soome /* If this is not boot disk, use generic probe. */ 7033830659eSToomas Soome if (dev.dd.d_unit != boot_unit) 7043830659eSToomas Soome zfs_probe_dev(devname, NULL); 7053830659eSToomas Soome else 7063830659eSToomas Soome zfs_probe_dev(devname, &pool_guid); 7073830659eSToomas Soome 7083830659eSToomas Soome if (pool_guid != 0 && bdev == NULL) { 7093830659eSToomas Soome bdev = malloc(sizeof (struct i386_devdesc)); 7103830659eSToomas Soome bzero(bdev, sizeof (struct i386_devdesc)); 7113830659eSToomas Soome bdev->dd.d_dev = &zfs_dev; 7123830659eSToomas Soome bdev->d_kind.zfs.pool_guid = pool_guid; 7133830659eSToomas Soome } 7143830659eSToomas Soome } 7153830659eSToomas Soome } 716