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
163830659eSToomas Soome #include <stand.h>
1765628439SWarner Losh
18ca987d46SWarner Losh #include <sys/param.h>
19ca987d46SWarner Losh #include <sys/errno.h>
20ca987d46SWarner Losh #include <sys/diskmbr.h>
21ca987d46SWarner Losh #ifdef GPT
22ca987d46SWarner Losh #include <sys/gpt.h>
23ca987d46SWarner Losh #endif
24ca987d46SWarner Losh #include <sys/reboot.h>
25ca987d46SWarner Losh #include <sys/queue.h>
261dc762d4SToomas Soome #ifdef LOADER_ZFS_SUPPORT
27e307eb94SToomas Soome #include <sys/zfs_bootenv.h>
281dc762d4SToomas Soome #endif
29ca987d46SWarner Losh
30ca987d46SWarner Losh #include <machine/bootinfo.h>
31ca987d46SWarner Losh #include <machine/elf.h>
32ca987d46SWarner Losh #include <machine/pc/bios.h>
33ca987d46SWarner Losh
34ca987d46SWarner Losh #include <stdarg.h>
35ca987d46SWarner Losh #include <stddef.h>
36ca987d46SWarner Losh
37ca987d46SWarner Losh #include <a.out.h>
383830659eSToomas Soome #include "bootstrap.h"
393830659eSToomas Soome #include "libi386.h"
40ca987d46SWarner Losh #include <btxv86.h>
41ca987d46SWarner Losh
42ca987d46SWarner Losh #include "lib.h"
43ca987d46SWarner Losh #include "rbx.h"
44ca987d46SWarner Losh #include "cons.h"
45ca987d46SWarner Losh #include "bootargs.h"
463830659eSToomas Soome #include "disk.h"
473830659eSToomas Soome #include "part.h"
48ca987d46SWarner Losh #include "paths.h"
49ca987d46SWarner Losh
50ca987d46SWarner Losh #include "libzfs.h"
51ca987d46SWarner Losh
52ca987d46SWarner Losh #define ARGS 0x900
53ca987d46SWarner Losh #define NOPT 14
54ca987d46SWarner Losh #define NDEV 3
55ca987d46SWarner Losh
56ca987d46SWarner Losh #define BIOS_NUMDRIVES 0x475
57ca987d46SWarner Losh #define DRV_HARD 0x80
58ca987d46SWarner Losh #define DRV_MASK 0x7f
59ca987d46SWarner Losh
60ca987d46SWarner Losh #define TYPE_AD 0
61ca987d46SWarner Losh #define TYPE_DA 1
62ca987d46SWarner Losh #define TYPE_MAXHARD TYPE_DA
63ca987d46SWarner Losh #define TYPE_FD 2
64ca987d46SWarner Losh
65ca987d46SWarner Losh extern uint32_t _end;
66ca987d46SWarner Losh
67ca987d46SWarner Losh static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
68ca987d46SWarner Losh static const unsigned char flags[NOPT] = {
69ca987d46SWarner Losh RBX_DUAL,
70ca987d46SWarner Losh RBX_SERIAL,
71ca987d46SWarner Losh RBX_ASKNAME,
72ca987d46SWarner Losh RBX_CDROM,
73ca987d46SWarner Losh RBX_CONFIG,
74ca987d46SWarner Losh RBX_KDB,
75ca987d46SWarner Losh RBX_GDB,
76ca987d46SWarner Losh RBX_MUTE,
77ca987d46SWarner Losh RBX_NOINTR,
78ca987d46SWarner Losh RBX_PAUSE,
79ca987d46SWarner Losh RBX_QUIET,
80ca987d46SWarner Losh RBX_DFLTROOT,
81ca987d46SWarner Losh RBX_SINGLE,
82ca987d46SWarner Losh RBX_VERBOSE
83ca987d46SWarner Losh };
84ca987d46SWarner Losh uint32_t opts;
85ca987d46SWarner Losh
8663acab6aSWarner Losh /*
8763acab6aSWarner Losh * Paths to try loading before falling back to the boot2 prompt.
8863acab6aSWarner Losh *
8963acab6aSWarner Losh * /boot/zfsloader must be tried before /boot/loader in order to remain
9063acab6aSWarner Losh * backward compatible with ZFS boot environments where /boot/loader exists
9163acab6aSWarner Losh * but does not have ZFS support, which was the case before FreeBSD 12.
9263acab6aSWarner Losh *
9363acab6aSWarner Losh * If no loader is found, try to load a kernel directly instead.
9463acab6aSWarner Losh */
9563acab6aSWarner Losh static const struct string {
9663acab6aSWarner Losh const char *p;
9763acab6aSWarner Losh size_t len;
9863acab6aSWarner Losh } loadpath[] = {
9963acab6aSWarner Losh { PATH_LOADER_ZFS, sizeof(PATH_LOADER_ZFS) },
10063acab6aSWarner Losh { PATH_LOADER, sizeof(PATH_LOADER) },
10163acab6aSWarner Losh { PATH_KERNEL, sizeof(PATH_KERNEL) },
10263acab6aSWarner Losh };
10363acab6aSWarner Losh
104ca987d46SWarner Losh static const unsigned char dev_maj[NDEV] = {30, 4, 2};
105ca987d46SWarner Losh
1063830659eSToomas Soome static struct i386_devdesc *bdev;
107ca987d46SWarner Losh static char cmd[512];
108ca987d46SWarner Losh static char cmddup[512];
109ca987d46SWarner Losh static char kname[1024];
110ca987d46SWarner Losh static int comspeed = SIOSPD;
111ca987d46SWarner Losh static struct bootinfo bootinfo;
112ca987d46SWarner Losh static uint32_t bootdev;
113ca987d46SWarner Losh static struct zfs_boot_args zfsargs;
1143830659eSToomas Soome #ifdef LOADER_GELI_SUPPORT
1153830659eSToomas Soome static struct geli_boot_args geliargs;
1163830659eSToomas Soome #endif
117ca987d46SWarner Losh
1183830659eSToomas Soome extern vm_offset_t high_heap_base;
1193830659eSToomas Soome extern uint32_t bios_basemem, bios_extmem, high_heap_size;
120ca987d46SWarner Losh
1213830659eSToomas Soome static char *heap_top;
1223830659eSToomas Soome static char *heap_bottom;
123ca987d46SWarner Losh
124ca987d46SWarner Losh void exit(int);
1253830659eSToomas Soome static void i386_zfs_probe(void);
126ca987d46SWarner Losh static void load(void);
127ca987d46SWarner Losh static int parse_cmd(void);
128ca987d46SWarner Losh
129ca987d46SWarner Losh #ifdef LOADER_GELI_SUPPORT
130c1418270SIan Lepore #include "geliboot.h"
131ca987d46SWarner Losh static char gelipw[GELI_PW_MAXLEN];
132ca987d46SWarner Losh #endif
133ca987d46SWarner Losh
1343830659eSToomas Soome struct arch_switch archsw; /* MI/MD interface boundary */
1353830659eSToomas Soome static char boot_devname[2 * ZFS_MAXNAMELEN + 8]; /* disk or pool:dataset */
1363830659eSToomas Soome
1373830659eSToomas Soome struct devsw *devsw[] = {
1383830659eSToomas Soome &bioshd,
1393830659eSToomas Soome #if defined(LOADER_ZFS_SUPPORT)
1403830659eSToomas Soome &zfs_dev,
141c1418270SIan Lepore #endif
1423830659eSToomas Soome NULL
143c1418270SIan Lepore };
144c1418270SIan Lepore
1453830659eSToomas Soome struct fs_ops *file_system[] = {
1463830659eSToomas Soome #if defined(LOADER_ZFS_SUPPORT)
1473830659eSToomas Soome &zfs_fsops,
148ca987d46SWarner Losh #endif
1493830659eSToomas Soome #if defined(LOADER_UFS_SUPPORT)
1503830659eSToomas Soome &ufs_fsops,
1513a1f80e2SToomas Soome #endif
1523830659eSToomas Soome NULL
1533830659eSToomas Soome };
1543a1f80e2SToomas Soome
1553830659eSToomas Soome caddr_t
ptov(uintptr_t x)1563830659eSToomas Soome ptov(uintptr_t x)
157ca987d46SWarner Losh {
1583830659eSToomas Soome return (PTOV(x));
159ca987d46SWarner Losh }
160ca987d46SWarner Losh
161fc7cf724SToomas Soome int main(void);
162fc7cf724SToomas Soome
163ca987d46SWarner Losh int
main(void)164ca987d46SWarner Losh main(void)
165ca987d46SWarner Losh {
1663830659eSToomas Soome unsigned i;
1673830659eSToomas Soome int auto_boot, fd, nextboot = 0;
16817276525SWarner Losh struct disk_devdesc *devdesc;
169ca987d46SWarner Losh
170ca987d46SWarner Losh bios_getmem();
171ca987d46SWarner Losh
172ca987d46SWarner Losh if (high_heap_size > 0) {
1733830659eSToomas Soome heap_top = PTOV(high_heap_base + high_heap_size);
1743830659eSToomas Soome heap_bottom = PTOV(high_heap_base);
175ca987d46SWarner Losh } else {
1763830659eSToomas Soome heap_bottom = (char *)
1773830659eSToomas Soome (roundup2(__base + (int32_t)&_end, 0x10000) - __base);
1783830659eSToomas Soome heap_top = (char *)PTOV(bios_basemem);
179ca987d46SWarner Losh }
1803830659eSToomas Soome setheap(heap_bottom, heap_top);
181ca987d46SWarner Losh
1823830659eSToomas Soome /*
1833830659eSToomas Soome * Initialise the block cache. Set the upper limit.
1843830659eSToomas Soome */
1853830659eSToomas Soome bcache_init(32768, 512);
1863830659eSToomas Soome
1873830659eSToomas Soome archsw.arch_autoload = NULL;
1883830659eSToomas Soome archsw.arch_getdev = i386_getdev;
1893830659eSToomas Soome archsw.arch_copyin = NULL;
1903830659eSToomas Soome archsw.arch_copyout = NULL;
1913830659eSToomas Soome archsw.arch_readin = NULL;
1923830659eSToomas Soome archsw.arch_isainb = NULL;
1933830659eSToomas Soome archsw.arch_isaoutb = NULL;
1943830659eSToomas Soome archsw.arch_zfs_probe = i386_zfs_probe;
195ca987d46SWarner Losh
196ca987d46SWarner Losh bootinfo.bi_version = BOOTINFO_VERSION;
197ca987d46SWarner Losh bootinfo.bi_size = sizeof(bootinfo);
198ca987d46SWarner Losh bootinfo.bi_basemem = bios_basemem / 1024;
199ca987d46SWarner Losh bootinfo.bi_extmem = bios_extmem / 1024;
200ca987d46SWarner Losh bootinfo.bi_memsizes_valid++;
2013830659eSToomas Soome bootinfo.bi_bios_dev = *(uint8_t *)PTOV(ARGS);
202ca987d46SWarner Losh
2033830659eSToomas Soome /* Set up fall back device name. */
2043830659eSToomas Soome snprintf(boot_devname, sizeof (boot_devname), "disk%d:",
2053830659eSToomas Soome bd_bios2unit(bootinfo.bi_bios_dev));
206ca987d46SWarner Losh
207b4cb3fe0SToomas Soome /* Set up currdev variable to have hooks in place. */
208*1c1783d6SWarner Losh env_setenv("currdev", EV_VOLATILE, "", gen_setcurrdev,
209b4cb3fe0SToomas Soome env_nounset);
210b4cb3fe0SToomas Soome
21166012c8fSWarner Losh devinit();
212ca987d46SWarner Losh
21317276525SWarner Losh /* XXX assumes this will be a disk, but it looks likely give above */
21433bbe5ddSWarner Losh disk_parsedev((struct devdesc **)&devdesc, boot_devname, NULL);
215ca987d46SWarner Losh
21617276525SWarner Losh bootdev = MAKEBOOTDEV(dev_maj[DEVT_DISK], devdesc->d_slice + 1,
21717276525SWarner Losh devdesc->dd.d_unit,
21817276525SWarner Losh devdesc->d_partition >= 0 ? devdesc->d_partition : 0xff);
21917276525SWarner Losh free(devdesc);
220ca987d46SWarner Losh
221ca987d46SWarner Losh /*
222edb26097SWarner Losh * devformat() can be called only after dv_init
223ca987d46SWarner Losh */
2243830659eSToomas Soome if (bdev != NULL && bdev->dd.d_dev->dv_type == DEVT_ZFS) {
2253830659eSToomas Soome /* set up proper device name string for ZFS */
226edb26097SWarner Losh strncpy(boot_devname, devformat(&bdev->dd), sizeof (boot_devname));
227e307eb94SToomas Soome if (zfs_get_bootonce(bdev, OS_BOOTONCE, cmd,
228e307eb94SToomas Soome sizeof(cmd)) == 0) {
229e307eb94SToomas Soome nvlist_t *benv;
230e307eb94SToomas Soome
231ca987d46SWarner Losh nextboot = 1;
232ca987d46SWarner Losh memcpy(cmddup, cmd, sizeof(cmd));
233ca987d46SWarner Losh if (parse_cmd()) {
2343830659eSToomas Soome if (!OPT_CHECK(RBX_QUIET))
235e307eb94SToomas Soome printf("failed to parse bootonce "
236e307eb94SToomas Soome "command\n");
2373830659eSToomas Soome exit(0);
238ca987d46SWarner Losh }
239ca987d46SWarner Losh if (!OPT_CHECK(RBX_QUIET))
240e307eb94SToomas Soome printf("zfs bootonce: %s\n", cmddup);
241e307eb94SToomas Soome
242e307eb94SToomas Soome if (zfs_get_bootenv(bdev, &benv) == 0) {
243e307eb94SToomas Soome nvlist_add_string(benv, OS_BOOTONCE_USED,
244e307eb94SToomas Soome cmddup);
245e307eb94SToomas Soome zfs_set_bootenv(bdev, benv);
246e307eb94SToomas Soome }
247ca987d46SWarner Losh /* Do not process this command twice */
248ca987d46SWarner Losh *cmd = 0;
249ca987d46SWarner Losh }
2503830659eSToomas Soome }
251ca987d46SWarner Losh
2523830659eSToomas Soome /* now make sure we have bdev on all cases */
2533830659eSToomas Soome free(bdev);
2543830659eSToomas Soome i386_getdev((void **)&bdev, boot_devname, NULL);
2553830659eSToomas Soome
256*1c1783d6SWarner Losh env_setenv("currdev", EV_VOLATILE, boot_devname, gen_setcurrdev,
2573830659eSToomas Soome env_nounset);
2583830659eSToomas Soome
2593830659eSToomas Soome /* Process configuration file */
2603830659eSToomas Soome auto_boot = 1;
2613830659eSToomas Soome
2623830659eSToomas Soome fd = open(PATH_CONFIG, O_RDONLY);
2633830659eSToomas Soome if (fd == -1)
2643830659eSToomas Soome fd = open(PATH_DOTCONFIG, O_RDONLY);
2653830659eSToomas Soome
2663830659eSToomas Soome if (fd != -1) {
267c7dd069cSGleb Smirnoff ssize_t cmdlen;
268c7dd069cSGleb Smirnoff
269c7dd069cSGleb Smirnoff if ((cmdlen = read(fd, cmd, sizeof(cmd))) > 0)
270c7dd069cSGleb Smirnoff cmd[cmdlen] = '\0';
271c7dd069cSGleb Smirnoff else
272c7dd069cSGleb Smirnoff *cmd = '\0';
2733830659eSToomas Soome close(fd);
274ca987d46SWarner Losh }
275ca987d46SWarner Losh
276ca987d46SWarner Losh if (*cmd) {
277ca987d46SWarner Losh /*
278dfdeb454SToomas Soome * Note that parse_cmd() is destructive to cmd[] and we also
279dfdeb454SToomas Soome * want to honor RBX_QUIET option that could be present in
280dfdeb454SToomas Soome * cmd[].
281ca987d46SWarner Losh */
282ca987d46SWarner Losh memcpy(cmddup, cmd, sizeof(cmd));
283ca987d46SWarner Losh if (parse_cmd())
2843830659eSToomas Soome auto_boot = 0;
285ca987d46SWarner Losh if (!OPT_CHECK(RBX_QUIET))
286ca987d46SWarner Losh printf("%s: %s\n", PATH_CONFIG, cmddup);
287ca987d46SWarner Losh /* Do not process this command twice */
288ca987d46SWarner Losh *cmd = 0;
289ca987d46SWarner Losh }
290ca987d46SWarner Losh
291ca987d46SWarner Losh /* Do not risk waiting at the prompt forever. */
2923830659eSToomas Soome if (nextboot && !auto_boot)
2933830659eSToomas Soome exit(0);
294ca987d46SWarner Losh
2953830659eSToomas Soome if (auto_boot && !*kname) {
29663acab6aSWarner Losh /*
297dfdeb454SToomas Soome * Iterate through the list of loader and kernel paths,
298dfdeb454SToomas Soome * trying to load. If interrupted by a keypress, or in case of
299dfdeb454SToomas Soome * failure, drop the user to the boot2 prompt.
30063acab6aSWarner Losh */
30163acab6aSWarner Losh for (i = 0; i < nitems(loadpath); i++) {
30263acab6aSWarner Losh memcpy(kname, loadpath[i].p, loadpath[i].len);
30363acab6aSWarner Losh if (keyhit(3))
30463acab6aSWarner Losh break;
305ca987d46SWarner Losh load();
306ca987d46SWarner Losh }
307ca987d46SWarner Losh }
308ca987d46SWarner Losh
309ca987d46SWarner Losh /* Present the user with the boot2 prompt. */
310ca987d46SWarner Losh
311ca987d46SWarner Losh for (;;) {
3123830659eSToomas Soome if (!auto_boot || !OPT_CHECK(RBX_QUIET)) {
313ca987d46SWarner Losh printf("\nFreeBSD/x86 boot\n");
3143830659eSToomas Soome printf("Default: %s%s\nboot: ", boot_devname, kname);
315ca987d46SWarner Losh }
316ca987d46SWarner Losh if (ioctrl & IO_SERIAL)
317ca987d46SWarner Losh sio_flush();
3183830659eSToomas Soome if (!auto_boot || keyhit(5))
319ca987d46SWarner Losh getstr(cmd, sizeof(cmd));
3203830659eSToomas Soome else if (!auto_boot || !OPT_CHECK(RBX_QUIET))
321ca987d46SWarner Losh putchar('\n');
3223830659eSToomas Soome auto_boot = 0;
323ca987d46SWarner Losh if (parse_cmd())
324ca987d46SWarner Losh putchar('\a');
325ca987d46SWarner Losh else
326ca987d46SWarner Losh load();
327ca987d46SWarner Losh }
328ca987d46SWarner Losh }
329ca987d46SWarner Losh
330ca987d46SWarner Losh /* XXX - Needed for btxld to link the boot2 binary; do not remove. */
331ca987d46SWarner Losh void
exit(int x)332ca987d46SWarner Losh exit(int x)
333ca987d46SWarner Losh {
334ca987d46SWarner Losh __exit(x);
335ca987d46SWarner Losh }
336ca987d46SWarner Losh
337ca987d46SWarner Losh static void
load(void)338ca987d46SWarner Losh load(void)
339ca987d46SWarner Losh {
340ca987d46SWarner Losh union {
341ca987d46SWarner Losh struct exec ex;
342ca987d46SWarner Losh Elf32_Ehdr eh;
343ca987d46SWarner Losh } hdr;
344ca987d46SWarner Losh static Elf32_Phdr ep[2];
345ca987d46SWarner Losh static Elf32_Shdr es[2];
346ca987d46SWarner Losh caddr_t p;
347ca987d46SWarner Losh uint32_t addr, x;
3483830659eSToomas Soome int fd, fmt, i, j;
3493830659eSToomas Soome ssize_t size;
350ca987d46SWarner Losh
3513830659eSToomas Soome if ((fd = open(kname, O_RDONLY)) == -1) {
352ca987d46SWarner Losh printf("\nCan't find %s\n", kname);
353ca987d46SWarner Losh return;
354ca987d46SWarner Losh }
3553830659eSToomas Soome
3563830659eSToomas Soome size = sizeof(hdr);
3573830659eSToomas Soome if (read(fd, &hdr, sizeof (hdr)) != size) {
3583830659eSToomas Soome close(fd);
359ca987d46SWarner Losh return;
3603830659eSToomas Soome }
3613830659eSToomas Soome if (N_GETMAGIC(hdr.ex) == ZMAGIC) {
362ca987d46SWarner Losh fmt = 0;
3633830659eSToomas Soome } else if (IS_ELF(hdr.eh)) {
364ca987d46SWarner Losh fmt = 1;
3653830659eSToomas Soome } else {
366ca987d46SWarner Losh printf("Invalid %s\n", "format");
3673830659eSToomas Soome close(fd);
368ca987d46SWarner Losh return;
369ca987d46SWarner Losh }
370ca987d46SWarner Losh if (fmt == 0) {
371ca987d46SWarner Losh addr = hdr.ex.a_entry & 0xffffff;
372ca987d46SWarner Losh p = PTOV(addr);
3733830659eSToomas Soome lseek(fd, PAGE_SIZE, SEEK_SET);
3743830659eSToomas Soome size = hdr.ex.a_text;
3753830659eSToomas Soome if (read(fd, p, hdr.ex.a_text) != size) {
3763830659eSToomas Soome close(fd);
377ca987d46SWarner Losh return;
3783830659eSToomas Soome }
379ca987d46SWarner Losh p += roundup2(hdr.ex.a_text, PAGE_SIZE);
3803830659eSToomas Soome size = hdr.ex.a_data;
3813830659eSToomas Soome if (read(fd, p, hdr.ex.a_data) != size) {
3823830659eSToomas Soome close(fd);
383ca987d46SWarner Losh return;
3843830659eSToomas Soome }
385ca987d46SWarner Losh p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
386ca987d46SWarner Losh bootinfo.bi_symtab = VTOP(p);
387ca987d46SWarner Losh memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
388ca987d46SWarner Losh p += sizeof(hdr.ex.a_syms);
389ca987d46SWarner Losh if (hdr.ex.a_syms) {
3903830659eSToomas Soome size = hdr.ex.a_syms;
3913830659eSToomas Soome if (read(fd, p, hdr.ex.a_syms) != size) {
3923830659eSToomas Soome close(fd);
393ca987d46SWarner Losh return;
3943830659eSToomas Soome }
395ca987d46SWarner Losh p += hdr.ex.a_syms;
3963830659eSToomas Soome size = sizeof (int);
3973830659eSToomas Soome if (read(fd, p, sizeof (int)) != size) {
3983830659eSToomas Soome close(fd);
399ca987d46SWarner Losh return;
4003830659eSToomas Soome }
401ca987d46SWarner Losh x = *(uint32_t *)p;
402ca987d46SWarner Losh p += sizeof(int);
403ca987d46SWarner Losh x -= sizeof(int);
4043830659eSToomas Soome size = x;
4053830659eSToomas Soome if (read(fd, p, x) != size) {
4063830659eSToomas Soome close(fd);
407ca987d46SWarner Losh return;
4083830659eSToomas Soome }
409ca987d46SWarner Losh p += x;
410ca987d46SWarner Losh }
411ca987d46SWarner Losh } else {
4123830659eSToomas Soome lseek(fd, hdr.eh.e_phoff, SEEK_SET);
413ca987d46SWarner Losh for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
4143830659eSToomas Soome size = sizeof (ep[0]);
4153830659eSToomas Soome if (read(fd, ep + j, sizeof (ep[0])) != size) {
4163830659eSToomas Soome close(fd);
417ca987d46SWarner Losh return;
4183830659eSToomas Soome }
419ca987d46SWarner Losh if (ep[j].p_type == PT_LOAD)
420ca987d46SWarner Losh j++;
421ca987d46SWarner Losh }
422ca987d46SWarner Losh for (i = 0; i < 2; i++) {
423ca987d46SWarner Losh p = PTOV(ep[i].p_paddr & 0xffffff);
4243830659eSToomas Soome lseek(fd, ep[i].p_offset, SEEK_SET);
4253830659eSToomas Soome size = ep[i].p_filesz;
4263830659eSToomas Soome if (read(fd, p, ep[i].p_filesz) != size) {
4273830659eSToomas Soome close(fd);
428ca987d46SWarner Losh return;
429ca987d46SWarner Losh }
4303830659eSToomas Soome }
431ca987d46SWarner Losh p += roundup2(ep[1].p_memsz, PAGE_SIZE);
432ca987d46SWarner Losh bootinfo.bi_symtab = VTOP(p);
433ca987d46SWarner Losh if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
4343830659eSToomas Soome lseek(fd, hdr.eh.e_shoff +
4353830659eSToomas Soome sizeof (es[0]) * (hdr.eh.e_shstrndx + 1),
4363830659eSToomas Soome SEEK_SET);
4373830659eSToomas Soome size = sizeof(es);
4383830659eSToomas Soome if (read(fd, &es, sizeof (es)) != size) {
4393830659eSToomas Soome close(fd);
440ca987d46SWarner Losh return;
4413830659eSToomas Soome }
442ca987d46SWarner Losh for (i = 0; i < 2; i++) {
443dfdeb454SToomas Soome memcpy(p, &es[i].sh_size,
444dfdeb454SToomas Soome sizeof(es[i].sh_size));
445ca987d46SWarner Losh p += sizeof(es[i].sh_size);
4463830659eSToomas Soome lseek(fd, es[i].sh_offset, SEEK_SET);
4473830659eSToomas Soome size = es[i].sh_size;
4483830659eSToomas Soome if (read(fd, p, es[i].sh_size) != size) {
4493830659eSToomas Soome close(fd);
450ca987d46SWarner Losh return;
4513830659eSToomas Soome }
452ca987d46SWarner Losh p += es[i].sh_size;
453ca987d46SWarner Losh }
454ca987d46SWarner Losh }
455ca987d46SWarner Losh addr = hdr.eh.e_entry & 0xffffff;
456ca987d46SWarner Losh }
4573830659eSToomas Soome close(fd);
4583830659eSToomas Soome
459ca987d46SWarner Losh bootinfo.bi_esymtab = VTOP(p);
460ca987d46SWarner Losh bootinfo.bi_kernelname = VTOP(kname);
461ca987d46SWarner Losh #ifdef LOADER_GELI_SUPPORT
462ca987d46SWarner Losh explicit_bzero(gelipw, sizeof(gelipw));
4633830659eSToomas Soome #endif
4643830659eSToomas Soome
4653830659eSToomas Soome if (bdev->dd.d_dev->dv_type == DEVT_ZFS) {
4663830659eSToomas Soome zfsargs.size = sizeof(zfsargs);
467f197c0bfSWarner Losh zfsargs.pool = bdev->zfs.pool_guid;
468f197c0bfSWarner Losh zfsargs.root = bdev->zfs.root_guid;
4693830659eSToomas Soome #ifdef LOADER_GELI_SUPPORT
470df108aafSIan Lepore export_geli_boot_data(&zfsargs.gelidata);
471ca987d46SWarner Losh #endif
472b92c2c90SIan Lepore /*
4733830659eSToomas Soome * Note that the zfsargs struct is passed by value, not by
4743830659eSToomas Soome * pointer. Code in btxldr.S copies the values from the entry
4753830659eSToomas Soome * stack to a fixed location within loader(8) at startup due
4763830659eSToomas Soome * to the presence of KARGS_FLAGS_EXTARG.
477b92c2c90SIan Lepore */
478ca987d46SWarner Losh __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
479ca987d46SWarner Losh bootdev,
480ca987d46SWarner Losh KARGS_FLAGS_ZFS | KARGS_FLAGS_EXTARG,
481f197c0bfSWarner Losh (uint32_t)bdev->zfs.pool_guid,
482f197c0bfSWarner Losh (uint32_t)(bdev->zfs.pool_guid >> 32),
483ca987d46SWarner Losh VTOP(&bootinfo),
484ca987d46SWarner Losh zfsargs);
4853830659eSToomas Soome } else {
4863830659eSToomas Soome #ifdef LOADER_GELI_SUPPORT
4873830659eSToomas Soome geliargs.size = sizeof(geliargs);
4883830659eSToomas Soome export_geli_boot_data(&geliargs.gelidata);
4893830659eSToomas Soome #endif
4903830659eSToomas Soome
4913830659eSToomas Soome /*
4923830659eSToomas Soome * Note that the geliargs struct is passed by value, not by
4933830659eSToomas Soome * pointer. Code in btxldr.S copies the values from the entry
4943830659eSToomas Soome * stack to a fixed location within loader(8) at startup due
4953830659eSToomas Soome * to the presence of the KARGS_FLAGS_EXTARG flag.
4963830659eSToomas Soome */
4973830659eSToomas Soome __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
4983830659eSToomas Soome bootdev,
4993830659eSToomas Soome #ifdef LOADER_GELI_SUPPORT
5003830659eSToomas Soome KARGS_FLAGS_GELI | KARGS_FLAGS_EXTARG, 0, 0,
5013830659eSToomas Soome VTOP(&bootinfo), geliargs
5023830659eSToomas Soome #else
5033830659eSToomas Soome 0, 0, 0, VTOP(&bootinfo)
5043830659eSToomas Soome #endif
5053830659eSToomas Soome );
5063830659eSToomas Soome }
507ca987d46SWarner Losh }
508ca987d46SWarner Losh
509ca987d46SWarner Losh static int
mount_root(char * arg)5103830659eSToomas Soome mount_root(char *arg)
511ca987d46SWarner Losh {
5123830659eSToomas Soome char *root;
5133830659eSToomas Soome struct i386_devdesc *ddesc;
5143830659eSToomas Soome uint8_t part;
515ca987d46SWarner Losh
5163830659eSToomas Soome if (asprintf(&root, "%s:", arg) < 0)
5173830659eSToomas Soome return (1);
5183830659eSToomas Soome
5193830659eSToomas Soome if (i386_getdev((void **)&ddesc, root, NULL)) {
5203830659eSToomas Soome free(root);
5213830659eSToomas Soome return (1);
522ca987d46SWarner Losh }
523ca987d46SWarner Losh
5243830659eSToomas Soome /* we should have new device descriptor, free old and replace it. */
5253830659eSToomas Soome free(bdev);
5263830659eSToomas Soome bdev = ddesc;
5273830659eSToomas Soome if (bdev->dd.d_dev->dv_type == DEVT_DISK) {
528f197c0bfSWarner Losh if (bdev->disk.d_partition == -1)
5293830659eSToomas Soome part = 0xff;
5303830659eSToomas Soome else
531f197c0bfSWarner Losh part = bdev->disk.d_partition;
5323830659eSToomas Soome bootdev = MAKEBOOTDEV(dev_maj[bdev->dd.d_dev->dv_type],
533f197c0bfSWarner Losh bdev->disk.d_slice + 1, bdev->dd.d_unit, part);
5343830659eSToomas Soome bootinfo.bi_bios_dev = bd_unit2bios(bdev);
535ca987d46SWarner Losh }
5363830659eSToomas Soome strncpy(boot_devname, root, sizeof (boot_devname));
5373830659eSToomas Soome setenv("currdev", root, 1);
5383830659eSToomas Soome free(root);
539ca987d46SWarner Losh return (0);
540ca987d46SWarner Losh }
541ca987d46SWarner Losh
5423830659eSToomas Soome static void
fs_list(char * arg)5433830659eSToomas Soome fs_list(char *arg)
5443830659eSToomas Soome {
5453830659eSToomas Soome int fd;
5463830659eSToomas Soome struct dirent *d;
5473830659eSToomas Soome char line[80];
5483830659eSToomas Soome
5493830659eSToomas Soome fd = open(arg, O_RDONLY);
5503830659eSToomas Soome if (fd < 0)
5513830659eSToomas Soome return;
5523830659eSToomas Soome pager_open();
5533830659eSToomas Soome while ((d = readdirfd(fd)) != NULL) {
5543830659eSToomas Soome sprintf(line, "%s\n", d->d_name);
5553830659eSToomas Soome if (pager_output(line))
5563830659eSToomas Soome break;
5573830659eSToomas Soome }
5583830659eSToomas Soome pager_close();
5593830659eSToomas Soome close(fd);
5603830659eSToomas Soome }
5613830659eSToomas Soome
562ca987d46SWarner Losh static int
parse_cmd(void)563ca987d46SWarner Losh parse_cmd(void)
564ca987d46SWarner Losh {
565ca987d46SWarner Losh char *arg = cmd;
566ca987d46SWarner Losh char *ep, *p, *q;
567ca987d46SWarner Losh const char *cp;
5683830659eSToomas Soome char line[80];
569ca987d46SWarner Losh int c, i, j;
570ca987d46SWarner Losh
571ca987d46SWarner Losh while ((c = *arg++)) {
572ca987d46SWarner Losh if (c == ' ' || c == '\t' || c == '\n')
573ca987d46SWarner Losh continue;
574dfdeb454SToomas Soome for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++)
575dfdeb454SToomas Soome ;
576ca987d46SWarner Losh ep = p;
577ca987d46SWarner Losh if (*p)
578ca987d46SWarner Losh *p++ = 0;
579ca987d46SWarner Losh if (c == '-') {
580ca987d46SWarner Losh while ((c = *arg++)) {
581ca987d46SWarner Losh if (c == 'P') {
582ca987d46SWarner Losh if (*(uint8_t *)PTOV(0x496) & 0x10) {
583ca987d46SWarner Losh cp = "yes";
584ca987d46SWarner Losh } else {
585dfdeb454SToomas Soome opts |= OPT_SET(RBX_DUAL);
586dfdeb454SToomas Soome opts |= OPT_SET(RBX_SERIAL);
587ca987d46SWarner Losh cp = "no";
588ca987d46SWarner Losh }
589ca987d46SWarner Losh printf("Keyboard: %s\n", cp);
590ca987d46SWarner Losh continue;
591ca987d46SWarner Losh } else if (c == 'S') {
592ca987d46SWarner Losh j = 0;
593dfdeb454SToomas Soome while ((unsigned int)
594dfdeb454SToomas Soome (i = *arg++ - '0') <= 9)
595ca987d46SWarner Losh j = j * 10 + i;
596ca987d46SWarner Losh if (j > 0 && i == -'0') {
597ca987d46SWarner Losh comspeed = j;
598ca987d46SWarner Losh break;
599ca987d46SWarner Losh }
600dfdeb454SToomas Soome /*
601dfdeb454SToomas Soome * Fall through to error below
602dfdeb454SToomas Soome * ('S' not in optstr[]).
603dfdeb454SToomas Soome */
604ca987d46SWarner Losh }
605ca987d46SWarner Losh for (i = 0; c != optstr[i]; i++)
606ca987d46SWarner Losh if (i == NOPT - 1)
607dfdeb454SToomas Soome return (-1);
608ca987d46SWarner Losh opts ^= OPT_SET(flags[i]);
609ca987d46SWarner Losh }
610ca987d46SWarner Losh ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
611ca987d46SWarner Losh OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
612ca987d46SWarner Losh if (ioctrl & IO_SERIAL) {
613ca987d46SWarner Losh if (sio_init(115200 / comspeed) != 0)
614ca987d46SWarner Losh ioctrl &= ~IO_SERIAL;
615ca987d46SWarner Losh }
616ca987d46SWarner Losh } if (c == '?') {
6173830659eSToomas Soome printf("\n");
6183830659eSToomas Soome if (*arg == '\0')
6193830659eSToomas Soome arg = (char *)"/";
6203830659eSToomas Soome fs_list(arg);
6213830659eSToomas Soome zfs_list(arg);
622dfdeb454SToomas Soome return (-1);
623ca987d46SWarner Losh } else {
6243830659eSToomas Soome char *ptr;
6253830659eSToomas Soome printf("\n");
626ca987d46SWarner Losh arg--;
627ca987d46SWarner Losh
628ca987d46SWarner Losh /*
629ca987d46SWarner Losh * Report pool status if the comment is 'status'. Lets
630ca987d46SWarner Losh * hope no-one wants to load /status as a kernel.
631ca987d46SWarner Losh */
632dfdeb454SToomas Soome if (strcmp(arg, "status") == 0) {
6333830659eSToomas Soome pager_open();
6343830659eSToomas Soome for (i = 0; devsw[i] != NULL; i++) {
6353830659eSToomas Soome if (devsw[i]->dv_print != NULL) {
6363830659eSToomas Soome if (devsw[i]->dv_print(1))
6373830659eSToomas Soome break;
6383830659eSToomas Soome } else {
6393830659eSToomas Soome snprintf(line, sizeof(line),
6403830659eSToomas Soome "%s: (unknown)\n",
6413830659eSToomas Soome devsw[i]->dv_name);
6423830659eSToomas Soome if (pager_output(line))
6433830659eSToomas Soome break;
6443830659eSToomas Soome }
6453830659eSToomas Soome }
6463830659eSToomas Soome pager_close();
647dfdeb454SToomas Soome return (-1);
648ca987d46SWarner Losh }
649ca987d46SWarner Losh
650ca987d46SWarner Losh /*
651ca987d46SWarner Losh * If there is "zfs:" prefix simply ignore it.
652ca987d46SWarner Losh */
6533830659eSToomas Soome ptr = arg;
6543830659eSToomas Soome if (strncmp(ptr, "zfs:", 4) == 0)
6553830659eSToomas Soome ptr += 4;
656ca987d46SWarner Losh
657ca987d46SWarner Losh /*
658ca987d46SWarner Losh * If there is a colon, switch pools.
659ca987d46SWarner Losh */
6603830659eSToomas Soome q = strchr(ptr, ':');
661ca987d46SWarner Losh if (q) {
662ca987d46SWarner Losh *q++ = '\0';
6633830659eSToomas Soome if (mount_root(arg) != 0) {
664dfdeb454SToomas Soome return (-1);
6653830659eSToomas Soome }
666ca987d46SWarner Losh arg = q;
667ca987d46SWarner Losh }
668ca987d46SWarner Losh if ((i = ep - arg)) {
669ca987d46SWarner Losh if ((size_t)i >= sizeof(kname))
670dfdeb454SToomas Soome return (-1);
671ca987d46SWarner Losh memcpy(kname, arg, i + 1);
672ca987d46SWarner Losh }
673ca987d46SWarner Losh }
674ca987d46SWarner Losh arg = p;
675ca987d46SWarner Losh }
676dfdeb454SToomas Soome return (0);
677ca987d46SWarner Losh }
6783830659eSToomas Soome
6793830659eSToomas Soome /*
6803830659eSToomas Soome * Probe all disks to discover ZFS pools. The idea is to walk all possible
6813830659eSToomas Soome * disk devices, however, we also need to identify possible boot pool.
6823830659eSToomas Soome * For boot pool detection we have boot disk passed us from BIOS, recorded
6833830659eSToomas Soome * in bootinfo.bi_bios_dev.
6843830659eSToomas Soome */
6853830659eSToomas Soome static void
i386_zfs_probe(void)6863830659eSToomas Soome i386_zfs_probe(void)
6873830659eSToomas Soome {
6883830659eSToomas Soome char devname[32];
6893830659eSToomas Soome int boot_unit;
6903830659eSToomas Soome struct i386_devdesc dev;
6913830659eSToomas Soome uint64_t pool_guid = 0;
6923830659eSToomas Soome
6933830659eSToomas Soome dev.dd.d_dev = &bioshd;
6943830659eSToomas Soome /* Translate bios dev to our unit number. */
6953830659eSToomas Soome boot_unit = bd_bios2unit(bootinfo.bi_bios_dev);
6963830659eSToomas Soome
6973830659eSToomas Soome /*
6983830659eSToomas Soome * Open all the disks we can find and see if we can reconstruct
6993830659eSToomas Soome * ZFS pools from them.
7003830659eSToomas Soome */
7013830659eSToomas Soome for (dev.dd.d_unit = 0; bd_unit2bios(&dev) >= 0; dev.dd.d_unit++) {
7023830659eSToomas Soome snprintf(devname, sizeof (devname), "%s%d:", bioshd.dv_name,
7033830659eSToomas Soome dev.dd.d_unit);
7043830659eSToomas Soome /* If this is not boot disk, use generic probe. */
7053830659eSToomas Soome if (dev.dd.d_unit != boot_unit)
70671bbe6fbSWarner Losh zfs_probe_dev(devname, NULL, true);
7073830659eSToomas Soome else
70871bbe6fbSWarner Losh zfs_probe_dev(devname, &pool_guid, true);
7093830659eSToomas Soome
7103830659eSToomas Soome if (pool_guid != 0 && bdev == NULL) {
7113830659eSToomas Soome bdev = malloc(sizeof (struct i386_devdesc));
7123830659eSToomas Soome bzero(bdev, sizeof (struct i386_devdesc));
713f197c0bfSWarner Losh bdev->zfs.dd.d_dev = &zfs_dev;
714f197c0bfSWarner Losh bdev->zfs.pool_guid = pool_guid;
7153830659eSToomas Soome }
7163830659eSToomas Soome }
7173830659eSToomas Soome }
718