Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/stand/i386/gptzfsboot/zfsboot.c
34865 views
1
/*-
2
* Copyright (c) 1998 Robert Nordier
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms are freely
6
* permitted provided that the above copyright notice and this
7
* paragraph and the following disclaimer are duplicated in all
8
* such forms.
9
*
10
* This software is provided "AS IS" and without any express or
11
* implied warranties, including, without limitation, the implied
12
* warranties of merchantability and fitness for a particular
13
* purpose.
14
*/
15
16
#include <stand.h>
17
18
#include <sys/param.h>
19
#include <sys/errno.h>
20
#include <sys/diskmbr.h>
21
#ifdef GPT
22
#include <sys/gpt.h>
23
#endif
24
#include <sys/reboot.h>
25
#include <sys/queue.h>
26
#ifdef LOADER_ZFS_SUPPORT
27
#include <sys/zfs_bootenv.h>
28
#endif
29
30
#include <machine/bootinfo.h>
31
#include <machine/elf.h>
32
#include <machine/pc/bios.h>
33
34
#include <stdarg.h>
35
#include <stddef.h>
36
37
#include <a.out.h>
38
#include "bootstrap.h"
39
#include "libi386.h"
40
#include <btxv86.h>
41
42
#include "lib.h"
43
#include "rbx.h"
44
#include "cons.h"
45
#include "bootargs.h"
46
#include "disk.h"
47
#include "part.h"
48
#include "paths.h"
49
50
#include "libzfs.h"
51
52
#define ARGS 0x900
53
#define NOPT 14
54
#define NDEV 3
55
56
#define BIOS_NUMDRIVES 0x475
57
#define DRV_HARD 0x80
58
#define DRV_MASK 0x7f
59
60
#define TYPE_AD 0
61
#define TYPE_DA 1
62
#define TYPE_MAXHARD TYPE_DA
63
#define TYPE_FD 2
64
65
extern uint32_t _end;
66
67
static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
68
static const unsigned char flags[NOPT] = {
69
RBX_DUAL,
70
RBX_SERIAL,
71
RBX_ASKNAME,
72
RBX_CDROM,
73
RBX_CONFIG,
74
RBX_KDB,
75
RBX_GDB,
76
RBX_MUTE,
77
RBX_NOINTR,
78
RBX_PAUSE,
79
RBX_QUIET,
80
RBX_DFLTROOT,
81
RBX_SINGLE,
82
RBX_VERBOSE
83
};
84
uint32_t opts;
85
86
/*
87
* Paths to try loading before falling back to the boot2 prompt.
88
*
89
* /boot/zfsloader must be tried before /boot/loader in order to remain
90
* backward compatible with ZFS boot environments where /boot/loader exists
91
* but does not have ZFS support, which was the case before FreeBSD 12.
92
*
93
* If no loader is found, try to load a kernel directly instead.
94
*/
95
static const struct string {
96
const char *p;
97
size_t len;
98
} loadpath[] = {
99
{ PATH_LOADER_ZFS, sizeof(PATH_LOADER_ZFS) },
100
{ PATH_LOADER, sizeof(PATH_LOADER) },
101
{ PATH_KERNEL, sizeof(PATH_KERNEL) },
102
};
103
104
static const unsigned char dev_maj[NDEV] = {30, 4, 2};
105
106
static struct i386_devdesc *bdev;
107
static char cmd[512];
108
static char cmddup[512];
109
static char kname[1024];
110
static int comspeed = SIOSPD;
111
static struct bootinfo bootinfo;
112
static uint32_t bootdev;
113
static struct zfs_boot_args zfsargs;
114
#ifdef LOADER_GELI_SUPPORT
115
static struct geli_boot_args geliargs;
116
#endif
117
118
extern vm_offset_t high_heap_base;
119
extern uint32_t bios_basemem, bios_extmem, high_heap_size;
120
121
static char *heap_top;
122
static char *heap_bottom;
123
124
void exit(int);
125
static void i386_zfs_probe(void);
126
static void load(void);
127
static int parse_cmd(void);
128
129
#ifdef LOADER_GELI_SUPPORT
130
#include "geliboot.h"
131
static char gelipw[GELI_PW_MAXLEN];
132
#endif
133
134
/*
135
* Only because the zfs code requires access through archsw, otherwise the
136
* 'boot' programs don't need archsw. This is less than ideal, but this
137
* workaround is easier than many of the alternatives.
138
*/
139
struct arch_switch archsw = { /* MI/MD interface boundary */
140
.arch_getdev = i386_getdev,
141
.arch_zfs_probe = i386_zfs_probe,
142
};
143
144
static char boot_devname[2 * ZFS_MAXNAMELEN + 8]; /* disk or pool:dataset */
145
146
struct devsw *devsw[] = {
147
&bioshd,
148
#if defined(LOADER_ZFS_SUPPORT)
149
&zfs_dev,
150
#endif
151
NULL
152
};
153
154
struct fs_ops *file_system[] = {
155
#if defined(LOADER_ZFS_SUPPORT)
156
&zfs_fsops,
157
#endif
158
#if defined(LOADER_UFS_SUPPORT)
159
&ufs_fsops,
160
#endif
161
NULL
162
};
163
164
caddr_t
165
ptov(uintptr_t x)
166
{
167
return (PTOV(x));
168
}
169
170
int main(void);
171
172
int
173
main(void)
174
{
175
unsigned i;
176
int auto_boot, fd, nextboot = 0;
177
struct disk_devdesc *devdesc;
178
179
bios_getmem();
180
181
if (high_heap_size > 0) {
182
heap_top = PTOV(high_heap_base + high_heap_size);
183
heap_bottom = PTOV(high_heap_base);
184
} else {
185
heap_bottom = (char *)
186
(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
187
heap_top = (char *)PTOV(bios_basemem);
188
}
189
setheap(heap_bottom, heap_top);
190
191
/*
192
* Initialise the block cache. Set the upper limit.
193
*/
194
bcache_init(32768, 512);
195
196
bootinfo.bi_version = BOOTINFO_VERSION;
197
bootinfo.bi_size = sizeof(bootinfo);
198
bootinfo.bi_basemem = bios_basemem / 1024;
199
bootinfo.bi_extmem = bios_extmem / 1024;
200
bootinfo.bi_memsizes_valid++;
201
bootinfo.bi_bios_dev = *(uint8_t *)PTOV(ARGS);
202
203
/* Set up fall back device name. */
204
snprintf(boot_devname, sizeof (boot_devname), "disk%d:",
205
bd_bios2unit(bootinfo.bi_bios_dev));
206
207
/* Set up currdev variable to have hooks in place. */
208
env_setenv("currdev", EV_VOLATILE, "", gen_setcurrdev,
209
env_nounset);
210
211
devinit();
212
213
/* XXX assumes this will be a disk, but it looks likely give above */
214
disk_parsedev((struct devdesc **)&devdesc, boot_devname, NULL);
215
216
bootdev = MAKEBOOTDEV(dev_maj[DEVT_DISK], devdesc->d_slice + 1,
217
devdesc->dd.d_unit,
218
devdesc->d_partition >= 0 ? devdesc->d_partition : 0xff);
219
free(devdesc);
220
221
/*
222
* devformat() can be called only after dv_init
223
*/
224
if (bdev != NULL && bdev->dd.d_dev->dv_type == DEVT_ZFS) {
225
/* set up proper device name string for ZFS */
226
strncpy(boot_devname, devformat(&bdev->dd), sizeof (boot_devname));
227
if (zfs_get_bootonce(bdev, OS_BOOTONCE, cmd,
228
sizeof(cmd)) == 0) {
229
nvlist_t *benv;
230
231
nextboot = 1;
232
memcpy(cmddup, cmd, sizeof(cmd));
233
if (parse_cmd()) {
234
if (!OPT_CHECK(RBX_QUIET))
235
printf("failed to parse bootonce "
236
"command\n");
237
exit(0);
238
}
239
if (!OPT_CHECK(RBX_QUIET))
240
printf("zfs bootonce: %s\n", cmddup);
241
242
if (zfs_get_bootenv(bdev, &benv) == 0) {
243
nvlist_add_string(benv, OS_BOOTONCE_USED,
244
cmddup);
245
zfs_set_bootenv(bdev, benv);
246
}
247
/* Do not process this command twice */
248
*cmd = 0;
249
}
250
}
251
252
/* now make sure we have bdev on all cases */
253
free(bdev);
254
i386_getdev((void **)&bdev, boot_devname, NULL);
255
256
env_setenv("currdev", EV_VOLATILE, boot_devname, gen_setcurrdev,
257
env_nounset);
258
259
/* Process configuration file */
260
auto_boot = 1;
261
262
fd = open(PATH_CONFIG, O_RDONLY);
263
if (fd == -1)
264
fd = open(PATH_DOTCONFIG, O_RDONLY);
265
266
if (fd != -1) {
267
ssize_t cmdlen;
268
269
if ((cmdlen = read(fd, cmd, sizeof(cmd))) > 0)
270
cmd[cmdlen] = '\0';
271
else
272
*cmd = '\0';
273
close(fd);
274
}
275
276
if (*cmd) {
277
/*
278
* Note that parse_cmd() is destructive to cmd[] and we also
279
* want to honor RBX_QUIET option that could be present in
280
* cmd[].
281
*/
282
memcpy(cmddup, cmd, sizeof(cmd));
283
if (parse_cmd())
284
auto_boot = 0;
285
if (!OPT_CHECK(RBX_QUIET))
286
printf("%s: %s\n", PATH_CONFIG, cmddup);
287
/* Do not process this command twice */
288
*cmd = 0;
289
}
290
291
/* Do not risk waiting at the prompt forever. */
292
if (nextboot && !auto_boot)
293
exit(0);
294
295
if (auto_boot && !*kname) {
296
/*
297
* Iterate through the list of loader and kernel paths,
298
* trying to load. If interrupted by a keypress, or in case of
299
* failure, drop the user to the boot2 prompt.
300
*/
301
for (i = 0; i < nitems(loadpath); i++) {
302
memcpy(kname, loadpath[i].p, loadpath[i].len);
303
if (keyhit(3))
304
break;
305
load();
306
}
307
}
308
309
/* Present the user with the boot2 prompt. */
310
311
for (;;) {
312
if (!auto_boot || !OPT_CHECK(RBX_QUIET)) {
313
printf("\nFreeBSD/x86 boot\n");
314
printf("Default: %s%s\nboot: ", boot_devname, kname);
315
}
316
if (ioctrl & IO_SERIAL)
317
sio_flush();
318
if (!auto_boot || keyhit(5))
319
getstr(cmd, sizeof(cmd));
320
else if (!auto_boot || !OPT_CHECK(RBX_QUIET))
321
putchar('\n');
322
auto_boot = 0;
323
if (parse_cmd())
324
putchar('\a');
325
else
326
load();
327
}
328
}
329
330
/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
331
void
332
exit(int x)
333
{
334
__exit(x);
335
}
336
337
static void
338
load(void)
339
{
340
union {
341
struct exec ex;
342
Elf32_Ehdr eh;
343
} hdr;
344
static Elf32_Phdr ep[2];
345
static Elf32_Shdr es[2];
346
caddr_t p;
347
uint32_t addr, x;
348
int fd, fmt, i, j;
349
ssize_t size;
350
351
if ((fd = open(kname, O_RDONLY)) == -1) {
352
printf("\nCan't find %s\n", kname);
353
return;
354
}
355
356
size = sizeof(hdr);
357
if (read(fd, &hdr, sizeof (hdr)) != size) {
358
close(fd);
359
return;
360
}
361
if (N_GETMAGIC(hdr.ex) == ZMAGIC) {
362
fmt = 0;
363
} else if (IS_ELF(hdr.eh)) {
364
fmt = 1;
365
} else {
366
printf("Invalid %s\n", "format");
367
close(fd);
368
return;
369
}
370
if (fmt == 0) {
371
addr = hdr.ex.a_entry & 0xffffff;
372
p = PTOV(addr);
373
lseek(fd, PAGE_SIZE, SEEK_SET);
374
size = hdr.ex.a_text;
375
if (read(fd, p, hdr.ex.a_text) != size) {
376
close(fd);
377
return;
378
}
379
p += roundup2(hdr.ex.a_text, PAGE_SIZE);
380
size = hdr.ex.a_data;
381
if (read(fd, p, hdr.ex.a_data) != size) {
382
close(fd);
383
return;
384
}
385
p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
386
bootinfo.bi_symtab = VTOP(p);
387
memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
388
p += sizeof(hdr.ex.a_syms);
389
if (hdr.ex.a_syms) {
390
size = hdr.ex.a_syms;
391
if (read(fd, p, hdr.ex.a_syms) != size) {
392
close(fd);
393
return;
394
}
395
p += hdr.ex.a_syms;
396
size = sizeof (int);
397
if (read(fd, p, sizeof (int)) != size) {
398
close(fd);
399
return;
400
}
401
x = *(uint32_t *)p;
402
p += sizeof(int);
403
x -= sizeof(int);
404
size = x;
405
if (read(fd, p, x) != size) {
406
close(fd);
407
return;
408
}
409
p += x;
410
}
411
} else {
412
lseek(fd, hdr.eh.e_phoff, SEEK_SET);
413
for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
414
size = sizeof (ep[0]);
415
if (read(fd, ep + j, sizeof (ep[0])) != size) {
416
close(fd);
417
return;
418
}
419
if (ep[j].p_type == PT_LOAD)
420
j++;
421
}
422
for (i = 0; i < 2; i++) {
423
p = PTOV(ep[i].p_paddr & 0xffffff);
424
lseek(fd, ep[i].p_offset, SEEK_SET);
425
size = ep[i].p_filesz;
426
if (read(fd, p, ep[i].p_filesz) != size) {
427
close(fd);
428
return;
429
}
430
}
431
p += roundup2(ep[1].p_memsz, PAGE_SIZE);
432
bootinfo.bi_symtab = VTOP(p);
433
if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
434
lseek(fd, hdr.eh.e_shoff +
435
sizeof (es[0]) * (hdr.eh.e_shstrndx + 1),
436
SEEK_SET);
437
size = sizeof(es);
438
if (read(fd, &es, sizeof (es)) != size) {
439
close(fd);
440
return;
441
}
442
for (i = 0; i < 2; i++) {
443
memcpy(p, &es[i].sh_size,
444
sizeof(es[i].sh_size));
445
p += sizeof(es[i].sh_size);
446
lseek(fd, es[i].sh_offset, SEEK_SET);
447
size = es[i].sh_size;
448
if (read(fd, p, es[i].sh_size) != size) {
449
close(fd);
450
return;
451
}
452
p += es[i].sh_size;
453
}
454
}
455
addr = hdr.eh.e_entry & 0xffffff;
456
}
457
close(fd);
458
459
bootinfo.bi_esymtab = VTOP(p);
460
bootinfo.bi_kernelname = VTOP(kname);
461
#ifdef LOADER_GELI_SUPPORT
462
explicit_bzero(gelipw, sizeof(gelipw));
463
#endif
464
465
if (bdev->dd.d_dev->dv_type == DEVT_ZFS) {
466
zfsargs.size = sizeof(zfsargs);
467
zfsargs.pool = bdev->zfs.pool_guid;
468
zfsargs.root = bdev->zfs.root_guid;
469
#ifdef LOADER_GELI_SUPPORT
470
export_geli_boot_data(&zfsargs.gelidata);
471
#endif
472
/*
473
* Note that the zfsargs struct is passed by value, not by
474
* pointer. Code in btxldr.S copies the values from the entry
475
* stack to a fixed location within loader(8) at startup due
476
* to the presence of KARGS_FLAGS_EXTARG.
477
*/
478
__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
479
bootdev,
480
KARGS_FLAGS_ZFS | KARGS_FLAGS_EXTARG,
481
(uint32_t)bdev->zfs.pool_guid,
482
(uint32_t)(bdev->zfs.pool_guid >> 32),
483
VTOP(&bootinfo),
484
zfsargs);
485
} else {
486
#ifdef LOADER_GELI_SUPPORT
487
geliargs.size = sizeof(geliargs);
488
export_geli_boot_data(&geliargs.gelidata);
489
#endif
490
491
/*
492
* Note that the geliargs struct is passed by value, not by
493
* pointer. Code in btxldr.S copies the values from the entry
494
* stack to a fixed location within loader(8) at startup due
495
* to the presence of the KARGS_FLAGS_EXTARG flag.
496
*/
497
__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
498
bootdev,
499
#ifdef LOADER_GELI_SUPPORT
500
KARGS_FLAGS_GELI | KARGS_FLAGS_EXTARG, 0, 0,
501
VTOP(&bootinfo), geliargs
502
#else
503
0, 0, 0, VTOP(&bootinfo)
504
#endif
505
);
506
}
507
}
508
509
static int
510
mount_root(char *arg)
511
{
512
char *root;
513
struct i386_devdesc *ddesc;
514
uint8_t part;
515
516
if (asprintf(&root, "%s:", arg) < 0)
517
return (1);
518
519
if (i386_getdev((void **)&ddesc, root, NULL)) {
520
free(root);
521
return (1);
522
}
523
524
/* we should have new device descriptor, free old and replace it. */
525
free(bdev);
526
bdev = ddesc;
527
if (bdev->dd.d_dev->dv_type == DEVT_DISK) {
528
if (bdev->disk.d_partition == -1)
529
part = 0xff;
530
else
531
part = bdev->disk.d_partition;
532
bootdev = MAKEBOOTDEV(dev_maj[bdev->dd.d_dev->dv_type],
533
bdev->disk.d_slice + 1, bdev->dd.d_unit, part);
534
bootinfo.bi_bios_dev = bd_unit2bios(bdev);
535
}
536
strncpy(boot_devname, root, sizeof (boot_devname));
537
setenv("currdev", root, 1);
538
free(root);
539
return (0);
540
}
541
542
static void
543
fs_list(char *arg)
544
{
545
int fd;
546
struct dirent *d;
547
char line[80];
548
549
fd = open(arg, O_RDONLY);
550
if (fd < 0)
551
return;
552
pager_open();
553
while ((d = readdirfd(fd)) != NULL) {
554
sprintf(line, "%s\n", d->d_name);
555
if (pager_output(line))
556
break;
557
}
558
pager_close();
559
close(fd);
560
}
561
562
static int
563
parse_cmd(void)
564
{
565
char *arg = cmd;
566
char *ep, *p, *q;
567
const char *cp;
568
char line[80];
569
int c, i, j;
570
571
while ((c = *arg++)) {
572
if (c == ' ' || c == '\t' || c == '\n')
573
continue;
574
for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++)
575
;
576
ep = p;
577
if (*p)
578
*p++ = 0;
579
if (c == '-') {
580
while ((c = *arg++)) {
581
if (c == 'P') {
582
if (*(uint8_t *)PTOV(0x496) & 0x10) {
583
cp = "yes";
584
} else {
585
opts |= OPT_SET(RBX_DUAL);
586
opts |= OPT_SET(RBX_SERIAL);
587
cp = "no";
588
}
589
printf("Keyboard: %s\n", cp);
590
continue;
591
} else if (c == 'S') {
592
j = 0;
593
while ((unsigned int)
594
(i = *arg++ - '0') <= 9)
595
j = j * 10 + i;
596
if (j > 0 && i == -'0') {
597
comspeed = j;
598
break;
599
}
600
/*
601
* Fall through to error below
602
* ('S' not in optstr[]).
603
*/
604
}
605
for (i = 0; c != optstr[i]; i++)
606
if (i == NOPT - 1)
607
return (-1);
608
opts ^= OPT_SET(flags[i]);
609
}
610
ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
611
OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
612
if (ioctrl & IO_SERIAL) {
613
if (sio_init(115200 / comspeed) != 0)
614
ioctrl &= ~IO_SERIAL;
615
}
616
} if (c == '?') {
617
printf("\n");
618
if (*arg == '\0')
619
arg = (char *)"/";
620
fs_list(arg);
621
zfs_list(arg);
622
return (-1);
623
} else {
624
char *ptr;
625
printf("\n");
626
arg--;
627
628
/*
629
* Report pool status if the comment is 'status'. Lets
630
* hope no-one wants to load /status as a kernel.
631
*/
632
if (strcmp(arg, "status") == 0) {
633
pager_open();
634
for (i = 0; devsw[i] != NULL; i++) {
635
if (devsw[i]->dv_print != NULL) {
636
if (devsw[i]->dv_print(1))
637
break;
638
} else {
639
snprintf(line, sizeof(line),
640
"%s: (unknown)\n",
641
devsw[i]->dv_name);
642
if (pager_output(line))
643
break;
644
}
645
}
646
pager_close();
647
return (-1);
648
}
649
650
/*
651
* If there is "zfs:" prefix simply ignore it.
652
*/
653
ptr = arg;
654
if (strncmp(ptr, "zfs:", 4) == 0)
655
ptr += 4;
656
657
/*
658
* If there is a colon, switch pools.
659
*/
660
q = strchr(ptr, ':');
661
if (q) {
662
*q++ = '\0';
663
if (mount_root(arg) != 0) {
664
return (-1);
665
}
666
arg = q;
667
}
668
if ((i = ep - arg)) {
669
if ((size_t)i >= sizeof(kname))
670
return (-1);
671
memcpy(kname, arg, i + 1);
672
}
673
}
674
arg = p;
675
}
676
return (0);
677
}
678
679
/*
680
* Probe all disks to discover ZFS pools. The idea is to walk all possible
681
* disk devices, however, we also need to identify possible boot pool.
682
* For boot pool detection we have boot disk passed us from BIOS, recorded
683
* in bootinfo.bi_bios_dev.
684
*/
685
static void
686
i386_zfs_probe(void)
687
{
688
char devname[32];
689
int boot_unit;
690
struct i386_devdesc dev;
691
uint64_t pool_guid = 0;
692
693
dev.dd.d_dev = &bioshd;
694
/* Translate bios dev to our unit number. */
695
boot_unit = bd_bios2unit(bootinfo.bi_bios_dev);
696
697
/*
698
* Open all the disks we can find and see if we can reconstruct
699
* ZFS pools from them.
700
*/
701
for (dev.dd.d_unit = 0; bd_unit2bios(&dev) >= 0; dev.dd.d_unit++) {
702
snprintf(devname, sizeof (devname), "%s%d:", bioshd.dv_name,
703
dev.dd.d_unit);
704
/* If this is not boot disk, use generic probe. */
705
if (dev.dd.d_unit != boot_unit)
706
zfs_probe_dev(devname, NULL, true);
707
else
708
zfs_probe_dev(devname, &pool_guid, true);
709
710
if (pool_guid != 0 && bdev == NULL) {
711
bdev = malloc(sizeof (struct i386_devdesc));
712
bzero(bdev, sizeof (struct i386_devdesc));
713
bdev->zfs.dd.d_dev = &zfs_dev;
714
bdev->zfs.pool_guid = pool_guid;
715
}
716
}
717
}
718
719