xref: /freebsd/stand/i386/zfsboot/zfsboot.c (revision ca987d46)
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 
19ca987d46SWarner Losh #include <sys/param.h>
20ca987d46SWarner Losh #include <sys/errno.h>
21ca987d46SWarner Losh #include <sys/diskmbr.h>
22ca987d46SWarner Losh #ifdef GPT
23ca987d46SWarner Losh #include <sys/gpt.h>
24ca987d46SWarner Losh #endif
25ca987d46SWarner Losh #include <sys/reboot.h>
26ca987d46SWarner Losh #include <sys/queue.h>
27ca987d46SWarner Losh 
28ca987d46SWarner Losh #include <machine/bootinfo.h>
29ca987d46SWarner Losh #include <machine/elf.h>
30ca987d46SWarner Losh #include <machine/pc/bios.h>
31ca987d46SWarner Losh 
32ca987d46SWarner Losh #include <stdarg.h>
33ca987d46SWarner Losh #include <stddef.h>
34ca987d46SWarner Losh 
35ca987d46SWarner Losh #include <a.out.h>
36ca987d46SWarner Losh 
37ca987d46SWarner Losh #include <btxv86.h>
38ca987d46SWarner Losh 
39ca987d46SWarner Losh #include "lib.h"
40ca987d46SWarner Losh #include "rbx.h"
41ca987d46SWarner Losh #include "drv.h"
42ca987d46SWarner Losh #include "edd.h"
43ca987d46SWarner Losh #include "util.h"
44ca987d46SWarner Losh #include "cons.h"
45ca987d46SWarner Losh #include "bootargs.h"
46ca987d46SWarner Losh #include "paths.h"
47ca987d46SWarner Losh 
48ca987d46SWarner Losh #include "libzfs.h"
49ca987d46SWarner Losh 
50ca987d46SWarner Losh #define ARGS			0x900
51ca987d46SWarner Losh #define NOPT			14
52ca987d46SWarner Losh #define NDEV			3
53ca987d46SWarner Losh 
54ca987d46SWarner Losh #define BIOS_NUMDRIVES		0x475
55ca987d46SWarner Losh #define DRV_HARD		0x80
56ca987d46SWarner Losh #define DRV_MASK		0x7f
57ca987d46SWarner Losh 
58ca987d46SWarner Losh #define TYPE_AD			0
59ca987d46SWarner Losh #define TYPE_DA			1
60ca987d46SWarner Losh #define TYPE_MAXHARD		TYPE_DA
61ca987d46SWarner Losh #define TYPE_FD			2
62ca987d46SWarner Losh 
63ca987d46SWarner Losh #define DEV_GELIBOOT_BSIZE	4096
64ca987d46SWarner Losh 
65ca987d46SWarner Losh extern uint32_t _end;
66ca987d46SWarner Losh 
67ca987d46SWarner Losh #ifdef GPT
68ca987d46SWarner Losh static const uuid_t freebsd_zfs_uuid = GPT_ENT_TYPE_FREEBSD_ZFS;
69ca987d46SWarner Losh #endif
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 
89ca987d46SWarner Losh static const unsigned char dev_maj[NDEV] = {30, 4, 2};
90ca987d46SWarner Losh 
91ca987d46SWarner Losh static char cmd[512];
92ca987d46SWarner Losh static char cmddup[512];
93ca987d46SWarner Losh static char kname[1024];
94ca987d46SWarner Losh static char rootname[256];
95ca987d46SWarner Losh static int comspeed = SIOSPD;
96ca987d46SWarner Losh static struct bootinfo bootinfo;
97ca987d46SWarner Losh static uint32_t bootdev;
98ca987d46SWarner Losh static struct zfs_boot_args zfsargs;
99ca987d46SWarner Losh static struct zfsmount zfsmount;
100ca987d46SWarner Losh 
101ca987d46SWarner Losh vm_offset_t	high_heap_base;
102ca987d46SWarner Losh uint32_t	bios_basemem, bios_extmem, high_heap_size;
103ca987d46SWarner Losh 
104ca987d46SWarner Losh static struct bios_smap smap;
105ca987d46SWarner Losh 
106ca987d46SWarner Losh /*
107ca987d46SWarner Losh  * The minimum amount of memory to reserve in bios_extmem for the heap.
108ca987d46SWarner Losh  */
109ca987d46SWarner Losh #define	HEAP_MIN		(64 * 1024 * 1024)
110ca987d46SWarner Losh 
111ca987d46SWarner Losh static char *heap_next;
112ca987d46SWarner Losh static char *heap_end;
113ca987d46SWarner Losh 
114ca987d46SWarner Losh /* Buffers that must not span a 64k boundary. */
115ca987d46SWarner Losh #define READ_BUF_SIZE		8192
116ca987d46SWarner Losh struct dmadat {
117ca987d46SWarner Losh 	char rdbuf[READ_BUF_SIZE];	/* for reading large things */
118ca987d46SWarner Losh 	char secbuf[READ_BUF_SIZE];	/* for MBR/disklabel */
119ca987d46SWarner Losh };
120ca987d46SWarner Losh static struct dmadat *dmadat;
121ca987d46SWarner Losh 
122ca987d46SWarner Losh void exit(int);
123ca987d46SWarner Losh void reboot(void);
124ca987d46SWarner Losh static void load(void);
125ca987d46SWarner Losh static int parse_cmd(void);
126ca987d46SWarner Losh static void bios_getmem(void);
127ca987d46SWarner Losh void *malloc(size_t n);
128ca987d46SWarner Losh void free(void *ptr);
129ca987d46SWarner Losh int main(void);
130ca987d46SWarner Losh 
131ca987d46SWarner Losh void *
132ca987d46SWarner Losh malloc(size_t n)
133ca987d46SWarner Losh {
134ca987d46SWarner Losh 	char *p = heap_next;
135ca987d46SWarner Losh 	if (p + n > heap_end) {
136ca987d46SWarner Losh 		printf("malloc failure\n");
137ca987d46SWarner Losh 		for (;;)
138ca987d46SWarner Losh 		    ;
139ca987d46SWarner Losh 		/* NOTREACHED */
140ca987d46SWarner Losh 		return (0);
141ca987d46SWarner Losh 	}
142ca987d46SWarner Losh 	heap_next += n;
143ca987d46SWarner Losh 	return (p);
144ca987d46SWarner Losh }
145ca987d46SWarner Losh 
146ca987d46SWarner Losh void
147ca987d46SWarner Losh free(void *ptr)
148ca987d46SWarner Losh {
149ca987d46SWarner Losh 
150ca987d46SWarner Losh 	return;
151ca987d46SWarner Losh }
152ca987d46SWarner Losh 
153ca987d46SWarner Losh static char *
154ca987d46SWarner Losh strdup(const char *s)
155ca987d46SWarner Losh {
156ca987d46SWarner Losh 	char *p = malloc(strlen(s) + 1);
157ca987d46SWarner Losh 	strcpy(p, s);
158ca987d46SWarner Losh 	return (p);
159ca987d46SWarner Losh }
160ca987d46SWarner Losh 
161ca987d46SWarner Losh #ifdef LOADER_GELI_SUPPORT
162ca987d46SWarner Losh #include "geliboot.c"
163ca987d46SWarner Losh static char gelipw[GELI_PW_MAXLEN];
164ca987d46SWarner Losh static struct keybuf *gelibuf;
165ca987d46SWarner Losh #endif
166ca987d46SWarner Losh 
167ca987d46SWarner Losh #include "zfsimpl.c"
168ca987d46SWarner Losh 
169ca987d46SWarner Losh /*
170ca987d46SWarner Losh  * Read from a dnode (which must be from a ZPL filesystem).
171ca987d46SWarner Losh  */
172ca987d46SWarner Losh static int
173ca987d46SWarner Losh zfs_read(spa_t *spa, const dnode_phys_t *dnode, off_t *offp, void *start, size_t size)
174ca987d46SWarner Losh {
175ca987d46SWarner Losh 	const znode_phys_t *zp = (const znode_phys_t *) dnode->dn_bonus;
176ca987d46SWarner Losh 	size_t n;
177ca987d46SWarner Losh 	int rc;
178ca987d46SWarner Losh 
179ca987d46SWarner Losh 	n = size;
180ca987d46SWarner Losh 	if (*offp + n > zp->zp_size)
181ca987d46SWarner Losh 		n = zp->zp_size - *offp;
182ca987d46SWarner Losh 
183ca987d46SWarner Losh 	rc = dnode_read(spa, dnode, *offp, start, n);
184ca987d46SWarner Losh 	if (rc)
185ca987d46SWarner Losh 		return (-1);
186ca987d46SWarner Losh 	*offp += n;
187ca987d46SWarner Losh 
188ca987d46SWarner Losh 	return (n);
189ca987d46SWarner Losh }
190ca987d46SWarner Losh 
191ca987d46SWarner Losh /*
192ca987d46SWarner Losh  * Current ZFS pool
193ca987d46SWarner Losh  */
194ca987d46SWarner Losh static spa_t *spa;
195ca987d46SWarner Losh static spa_t *primary_spa;
196ca987d46SWarner Losh static vdev_t *primary_vdev;
197ca987d46SWarner Losh 
198ca987d46SWarner Losh /*
199ca987d46SWarner Losh  * A wrapper for dskread that doesn't have to worry about whether the
200ca987d46SWarner Losh  * buffer pointer crosses a 64k boundary.
201ca987d46SWarner Losh  */
202ca987d46SWarner Losh static int
203ca987d46SWarner Losh vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes)
204ca987d46SWarner Losh {
205ca987d46SWarner Losh 	char *p;
206ca987d46SWarner Losh 	daddr_t lba, alignlba;
207ca987d46SWarner Losh 	off_t diff;
208ca987d46SWarner Losh 	unsigned int nb, alignnb;
209ca987d46SWarner Losh 	struct dsk *dsk = (struct dsk *) priv;
210ca987d46SWarner Losh 
211ca987d46SWarner Losh 	if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1)))
212ca987d46SWarner Losh 		return -1;
213ca987d46SWarner Losh 
214ca987d46SWarner Losh 	p = buf;
215ca987d46SWarner Losh 	lba = off / DEV_BSIZE;
216ca987d46SWarner Losh 	lba += dsk->start;
217ca987d46SWarner Losh 	/*
218ca987d46SWarner Losh 	 * Align reads to 4k else 4k sector GELIs will not decrypt.
219ca987d46SWarner Losh 	 * Round LBA down to nearest multiple of DEV_GELIBOOT_BSIZE bytes.
220ca987d46SWarner Losh 	 */
221ca987d46SWarner Losh 	alignlba = rounddown2(off, DEV_GELIBOOT_BSIZE) / DEV_BSIZE;
222ca987d46SWarner Losh 	/*
223ca987d46SWarner Losh 	 * The read must be aligned to DEV_GELIBOOT_BSIZE bytes relative to the
224ca987d46SWarner Losh 	 * start of the GELI partition, not the start of the actual disk.
225ca987d46SWarner Losh 	 */
226ca987d46SWarner Losh 	alignlba += dsk->start;
227ca987d46SWarner Losh 	diff = (lba - alignlba) * DEV_BSIZE;
228ca987d46SWarner Losh 
229ca987d46SWarner Losh 	while (bytes > 0) {
230ca987d46SWarner Losh 		nb = bytes / DEV_BSIZE;
231ca987d46SWarner Losh 		/*
232ca987d46SWarner Losh 		 * Ensure that the read size plus the leading offset does not
233ca987d46SWarner Losh 		 * exceed the size of the read buffer.
234ca987d46SWarner Losh 		 */
235ca987d46SWarner Losh 		if (nb > (READ_BUF_SIZE - diff) / DEV_BSIZE)
236ca987d46SWarner Losh 			nb = (READ_BUF_SIZE - diff) / DEV_BSIZE;
237ca987d46SWarner Losh 		/*
238ca987d46SWarner Losh 		 * Round the number of blocks to read up to the nearest multiple
239ca987d46SWarner Losh 		 * of DEV_GELIBOOT_BSIZE.
240ca987d46SWarner Losh 		 */
241ca987d46SWarner Losh 		alignnb = roundup2(nb * DEV_BSIZE + diff, DEV_GELIBOOT_BSIZE)
242ca987d46SWarner Losh 		    / DEV_BSIZE;
243ca987d46SWarner Losh 
244ca987d46SWarner Losh 		if (drvread(dsk, dmadat->rdbuf, alignlba, alignnb))
245ca987d46SWarner Losh 			return -1;
246ca987d46SWarner Losh #ifdef LOADER_GELI_SUPPORT
247ca987d46SWarner Losh 		/* decrypt */
248ca987d46SWarner Losh 		if (is_geli(dsk) == 0) {
249ca987d46SWarner Losh 			if (geli_read(dsk, ((alignlba - dsk->start) *
250ca987d46SWarner Losh 			    DEV_BSIZE), dmadat->rdbuf, alignnb * DEV_BSIZE))
251ca987d46SWarner Losh 				return (-1);
252ca987d46SWarner Losh 		}
253ca987d46SWarner Losh #endif
254ca987d46SWarner Losh 		memcpy(p, dmadat->rdbuf + diff, nb * DEV_BSIZE);
255ca987d46SWarner Losh 		p += nb * DEV_BSIZE;
256ca987d46SWarner Losh 		lba += nb;
257ca987d46SWarner Losh 		alignlba += alignnb;
258ca987d46SWarner Losh 		bytes -= nb * DEV_BSIZE;
259ca987d46SWarner Losh 		/* Don't need the leading offset after the first block. */
260ca987d46SWarner Losh 		diff = 0;
261ca987d46SWarner Losh 	}
262ca987d46SWarner Losh 
263ca987d46SWarner Losh 	return 0;
264ca987d46SWarner Losh }
265ca987d46SWarner Losh 
266ca987d46SWarner Losh static int
267ca987d46SWarner Losh vdev_write(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes)
268ca987d46SWarner Losh {
269ca987d46SWarner Losh 	char *p;
270ca987d46SWarner Losh 	daddr_t lba;
271ca987d46SWarner Losh 	unsigned int nb;
272ca987d46SWarner Losh 	struct dsk *dsk = (struct dsk *) priv;
273ca987d46SWarner Losh 
274ca987d46SWarner Losh 	if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1)))
275ca987d46SWarner Losh 		return -1;
276ca987d46SWarner Losh 
277ca987d46SWarner Losh 	p = buf;
278ca987d46SWarner Losh 	lba = off / DEV_BSIZE;
279ca987d46SWarner Losh 	lba += dsk->start;
280ca987d46SWarner Losh 	while (bytes > 0) {
281ca987d46SWarner Losh 		nb = bytes / DEV_BSIZE;
282ca987d46SWarner Losh 		if (nb > READ_BUF_SIZE / DEV_BSIZE)
283ca987d46SWarner Losh 			nb = READ_BUF_SIZE / DEV_BSIZE;
284ca987d46SWarner Losh 		memcpy(dmadat->rdbuf, p, nb * DEV_BSIZE);
285ca987d46SWarner Losh 		if (drvwrite(dsk, dmadat->rdbuf, lba, nb))
286ca987d46SWarner Losh 			return -1;
287ca987d46SWarner Losh 		p += nb * DEV_BSIZE;
288ca987d46SWarner Losh 		lba += nb;
289ca987d46SWarner Losh 		bytes -= nb * DEV_BSIZE;
290ca987d46SWarner Losh 	}
291ca987d46SWarner Losh 
292ca987d46SWarner Losh 	return 0;
293ca987d46SWarner Losh }
294ca987d46SWarner Losh 
295ca987d46SWarner Losh static int
296ca987d46SWarner Losh xfsread(const dnode_phys_t *dnode, off_t *offp, void *buf, size_t nbyte)
297ca987d46SWarner Losh {
298ca987d46SWarner Losh     if ((size_t)zfs_read(spa, dnode, offp, buf, nbyte) != nbyte) {
299ca987d46SWarner Losh 	printf("Invalid format\n");
300ca987d46SWarner Losh 	return -1;
301ca987d46SWarner Losh     }
302ca987d46SWarner Losh     return 0;
303ca987d46SWarner Losh }
304ca987d46SWarner Losh 
305ca987d46SWarner Losh /*
306ca987d46SWarner Losh  * Read Pad2 (formerly "Boot Block Header") area of the first
307ca987d46SWarner Losh  * vdev label of the given vdev.
308ca987d46SWarner Losh  */
309ca987d46SWarner Losh static int
310ca987d46SWarner Losh vdev_read_pad2(vdev_t *vdev, char *buf, size_t size)
311ca987d46SWarner Losh {
312ca987d46SWarner Losh 	blkptr_t bp;
313ca987d46SWarner Losh 	char *tmp = zap_scratch;
314ca987d46SWarner Losh 	off_t off = offsetof(vdev_label_t, vl_pad2);
315ca987d46SWarner Losh 
316ca987d46SWarner Losh 	if (size > VDEV_PAD_SIZE)
317ca987d46SWarner Losh 		size = VDEV_PAD_SIZE;
318ca987d46SWarner Losh 
319ca987d46SWarner Losh 	BP_ZERO(&bp);
320ca987d46SWarner Losh 	BP_SET_LSIZE(&bp, VDEV_PAD_SIZE);
321ca987d46SWarner Losh 	BP_SET_PSIZE(&bp, VDEV_PAD_SIZE);
322ca987d46SWarner Losh 	BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL);
323ca987d46SWarner Losh 	BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF);
324ca987d46SWarner Losh 	DVA_SET_OFFSET(BP_IDENTITY(&bp), off);
325ca987d46SWarner Losh 	if (vdev_read_phys(vdev, &bp, tmp, off, 0))
326ca987d46SWarner Losh 		return (EIO);
327ca987d46SWarner Losh 	memcpy(buf, tmp, size);
328ca987d46SWarner Losh 	return (0);
329ca987d46SWarner Losh }
330ca987d46SWarner Losh 
331ca987d46SWarner Losh static int
332ca987d46SWarner Losh vdev_clear_pad2(vdev_t *vdev)
333ca987d46SWarner Losh {
334ca987d46SWarner Losh 	char *zeroes = zap_scratch;
335ca987d46SWarner Losh 	uint64_t *end;
336ca987d46SWarner Losh 	off_t off = offsetof(vdev_label_t, vl_pad2);
337ca987d46SWarner Losh 
338ca987d46SWarner Losh 	memset(zeroes, 0, VDEV_PAD_SIZE);
339ca987d46SWarner Losh 	end = (uint64_t *)(zeroes + VDEV_PAD_SIZE);
340ca987d46SWarner Losh 	/* ZIO_CHECKSUM_LABEL magic and pre-calcualted checksum for all zeros */
341ca987d46SWarner Losh 	end[-5] = 0x0210da7ab10c7a11;
342ca987d46SWarner Losh 	end[-4] = 0x97f48f807f6e2a3f;
343ca987d46SWarner Losh 	end[-3] = 0xaf909f1658aacefc;
344ca987d46SWarner Losh 	end[-2] = 0xcbd1ea57ff6db48b;
345ca987d46SWarner Losh 	end[-1] = 0x6ec692db0d465fab;
346ca987d46SWarner Losh 	if (vdev_write(vdev, vdev->v_read_priv, off, zeroes, VDEV_PAD_SIZE))
347ca987d46SWarner Losh 		return (EIO);
348ca987d46SWarner Losh 	return (0);
349ca987d46SWarner Losh }
350ca987d46SWarner Losh 
351ca987d46SWarner Losh static void
352ca987d46SWarner Losh bios_getmem(void)
353ca987d46SWarner Losh {
354ca987d46SWarner Losh     uint64_t size;
355ca987d46SWarner Losh 
356ca987d46SWarner Losh     /* Parse system memory map */
357ca987d46SWarner Losh     v86.ebx = 0;
358ca987d46SWarner Losh     do {
359ca987d46SWarner Losh 	v86.ctl = V86_FLAGS;
360ca987d46SWarner Losh 	v86.addr = 0x15;		/* int 0x15 function 0xe820*/
361ca987d46SWarner Losh 	v86.eax = 0xe820;
362ca987d46SWarner Losh 	v86.ecx = sizeof(struct bios_smap);
363ca987d46SWarner Losh 	v86.edx = SMAP_SIG;
364ca987d46SWarner Losh 	v86.es = VTOPSEG(&smap);
365ca987d46SWarner Losh 	v86.edi = VTOPOFF(&smap);
366ca987d46SWarner Losh 	v86int();
367ca987d46SWarner Losh 	if (V86_CY(v86.efl) || (v86.eax != SMAP_SIG))
368ca987d46SWarner Losh 	    break;
369ca987d46SWarner Losh 	/* look for a low-memory segment that's large enough */
370ca987d46SWarner Losh 	if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) &&
371ca987d46SWarner Losh 	    (smap.length >= (512 * 1024)))
372ca987d46SWarner Losh 	    bios_basemem = smap.length;
373ca987d46SWarner Losh 	/* look for the first segment in 'extended' memory */
374ca987d46SWarner Losh 	if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0x100000)) {
375ca987d46SWarner Losh 	    bios_extmem = smap.length;
376ca987d46SWarner Losh 	}
377ca987d46SWarner Losh 
378ca987d46SWarner Losh 	/*
379ca987d46SWarner Losh 	 * Look for the largest segment in 'extended' memory beyond
380ca987d46SWarner Losh 	 * 1MB but below 4GB.
381ca987d46SWarner Losh 	 */
382ca987d46SWarner Losh 	if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base > 0x100000) &&
383ca987d46SWarner Losh 	    (smap.base < 0x100000000ull)) {
384ca987d46SWarner Losh 	    size = smap.length;
385ca987d46SWarner Losh 
386ca987d46SWarner Losh 	    /*
387ca987d46SWarner Losh 	     * If this segment crosses the 4GB boundary, truncate it.
388ca987d46SWarner Losh 	     */
389ca987d46SWarner Losh 	    if (smap.base + size > 0x100000000ull)
390ca987d46SWarner Losh 		size = 0x100000000ull - smap.base;
391ca987d46SWarner Losh 
392ca987d46SWarner Losh 	    if (size > high_heap_size) {
393ca987d46SWarner Losh 		high_heap_size = size;
394ca987d46SWarner Losh 		high_heap_base = smap.base;
395ca987d46SWarner Losh 	    }
396ca987d46SWarner Losh 	}
397ca987d46SWarner Losh     } while (v86.ebx != 0);
398ca987d46SWarner Losh 
399ca987d46SWarner Losh     /* Fall back to the old compatibility function for base memory */
400ca987d46SWarner Losh     if (bios_basemem == 0) {
401ca987d46SWarner Losh 	v86.ctl = 0;
402ca987d46SWarner Losh 	v86.addr = 0x12;		/* int 0x12 */
403ca987d46SWarner Losh 	v86int();
404ca987d46SWarner Losh 
405ca987d46SWarner Losh 	bios_basemem = (v86.eax & 0xffff) * 1024;
406ca987d46SWarner Losh     }
407ca987d46SWarner Losh 
408ca987d46SWarner Losh     /* Fall back through several compatibility functions for extended memory */
409ca987d46SWarner Losh     if (bios_extmem == 0) {
410ca987d46SWarner Losh 	v86.ctl = V86_FLAGS;
411ca987d46SWarner Losh 	v86.addr = 0x15;		/* int 0x15 function 0xe801*/
412ca987d46SWarner Losh 	v86.eax = 0xe801;
413ca987d46SWarner Losh 	v86int();
414ca987d46SWarner Losh 	if (!V86_CY(v86.efl)) {
415ca987d46SWarner Losh 	    bios_extmem = ((v86.ecx & 0xffff) + ((v86.edx & 0xffff) * 64)) * 1024;
416ca987d46SWarner Losh 	}
417ca987d46SWarner Losh     }
418ca987d46SWarner Losh     if (bios_extmem == 0) {
419ca987d46SWarner Losh 	v86.ctl = 0;
420ca987d46SWarner Losh 	v86.addr = 0x15;		/* int 0x15 function 0x88*/
421ca987d46SWarner Losh 	v86.eax = 0x8800;
422ca987d46SWarner Losh 	v86int();
423ca987d46SWarner Losh 	bios_extmem = (v86.eax & 0xffff) * 1024;
424ca987d46SWarner Losh     }
425ca987d46SWarner Losh 
426ca987d46SWarner Losh     /*
427ca987d46SWarner Losh      * If we have extended memory and did not find a suitable heap
428ca987d46SWarner Losh      * region in the SMAP, use the last 3MB of 'extended' memory as a
429ca987d46SWarner Losh      * high heap candidate.
430ca987d46SWarner Losh      */
431ca987d46SWarner Losh     if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) {
432ca987d46SWarner Losh 	high_heap_size = HEAP_MIN;
433ca987d46SWarner Losh 	high_heap_base = bios_extmem + 0x100000 - HEAP_MIN;
434ca987d46SWarner Losh     }
435ca987d46SWarner Losh }
436ca987d46SWarner Losh 
437ca987d46SWarner Losh /*
438ca987d46SWarner Losh  * Try to detect a device supported by the legacy int13 BIOS
439ca987d46SWarner Losh  */
440ca987d46SWarner Losh static int
441ca987d46SWarner Losh int13probe(int drive)
442ca987d46SWarner Losh {
443ca987d46SWarner Losh     v86.ctl = V86_FLAGS;
444ca987d46SWarner Losh     v86.addr = 0x13;
445ca987d46SWarner Losh     v86.eax = 0x800;
446ca987d46SWarner Losh     v86.edx = drive;
447ca987d46SWarner Losh     v86int();
448ca987d46SWarner Losh 
449ca987d46SWarner Losh     if (!V86_CY(v86.efl) &&				/* carry clear */
450ca987d46SWarner Losh 	((v86.edx & 0xff) != (drive & DRV_MASK))) {	/* unit # OK */
451ca987d46SWarner Losh 	if ((v86.ecx & 0x3f) == 0) {			/* absurd sector size */
452ca987d46SWarner Losh 		return(0);				/* skip device */
453ca987d46SWarner Losh 	}
454ca987d46SWarner Losh 	return (1);
455ca987d46SWarner Losh     }
456ca987d46SWarner Losh     return(0);
457ca987d46SWarner Losh }
458ca987d46SWarner Losh 
459ca987d46SWarner Losh /*
460ca987d46SWarner Losh  * We call this when we find a ZFS vdev - ZFS consumes the dsk
461ca987d46SWarner Losh  * structure so we must make a new one.
462ca987d46SWarner Losh  */
463ca987d46SWarner Losh static struct dsk *
464ca987d46SWarner Losh copy_dsk(struct dsk *dsk)
465ca987d46SWarner Losh {
466ca987d46SWarner Losh     struct dsk *newdsk;
467ca987d46SWarner Losh 
468ca987d46SWarner Losh     newdsk = malloc(sizeof(struct dsk));
469ca987d46SWarner Losh     *newdsk = *dsk;
470ca987d46SWarner Losh     return (newdsk);
471ca987d46SWarner Losh }
472ca987d46SWarner Losh 
473ca987d46SWarner Losh /*
474ca987d46SWarner Losh  * Get disk size from eax=0x800 and 0x4800. We need to probe both
475ca987d46SWarner Losh  * because 0x4800 may not be available and we would like to get more
476ca987d46SWarner Losh  * or less correct disk size - if it is possible at all.
477ca987d46SWarner Losh  * Note we do not really want to touch drv.c because that code is shared
478ca987d46SWarner Losh  * with boot2 and we can not afford to grow that code.
479ca987d46SWarner Losh  */
480ca987d46SWarner Losh static uint64_t
481ca987d46SWarner Losh drvsize_ext(struct dsk *dskp)
482ca987d46SWarner Losh {
483ca987d46SWarner Losh 	uint64_t size, tmp;
484ca987d46SWarner Losh 	int cyl, hds, sec;
485ca987d46SWarner Losh 
486ca987d46SWarner Losh 	v86.ctl = V86_FLAGS;
487ca987d46SWarner Losh 	v86.addr = 0x13;
488ca987d46SWarner Losh 	v86.eax = 0x800;
489ca987d46SWarner Losh 	v86.edx = dskp->drive;
490ca987d46SWarner Losh 	v86int();
491ca987d46SWarner Losh 
492ca987d46SWarner Losh 	/* Don't error out if we get bad sector number, try EDD as well */
493ca987d46SWarner Losh 	if (V86_CY(v86.efl) ||	/* carry set */
494ca987d46SWarner Losh 	    (v86.edx & 0xff) <= (unsigned)(dskp->drive & 0x7f)) /* unit # bad */
495ca987d46SWarner Losh 		return (0);
496ca987d46SWarner Losh 
497ca987d46SWarner Losh 	cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1;
498ca987d46SWarner Losh 	/* Convert max head # -> # of heads */
499ca987d46SWarner Losh 	hds = ((v86.edx & 0xff00) >> 8) + 1;
500ca987d46SWarner Losh 	sec = v86.ecx & 0x3f;
501ca987d46SWarner Losh 
502ca987d46SWarner Losh 	size = (uint64_t)cyl * hds * sec;
503ca987d46SWarner Losh 
504ca987d46SWarner Losh 	/* Determine if we can use EDD with this device. */
505ca987d46SWarner Losh 	v86.ctl = V86_FLAGS;
506ca987d46SWarner Losh 	v86.addr = 0x13;
507ca987d46SWarner Losh 	v86.eax = 0x4100;
508ca987d46SWarner Losh 	v86.edx = dskp->drive;
509ca987d46SWarner Losh 	v86.ebx = 0x55aa;
510ca987d46SWarner Losh 	v86int();
511ca987d46SWarner Losh 	if (V86_CY(v86.efl) ||  /* carry set */
512ca987d46SWarner Losh 	    (v86.ebx & 0xffff) != 0xaa55 || /* signature */
513ca987d46SWarner Losh 	    (v86.ecx & EDD_INTERFACE_FIXED_DISK) == 0)
514ca987d46SWarner Losh 		return (size);
515ca987d46SWarner Losh 
516ca987d46SWarner Losh 	tmp = drvsize(dskp);
517ca987d46SWarner Losh 	if (tmp > size)
518ca987d46SWarner Losh 		size = tmp;
519ca987d46SWarner Losh 
520ca987d46SWarner Losh 	return (size);
521ca987d46SWarner Losh }
522ca987d46SWarner Losh 
523ca987d46SWarner Losh /*
524ca987d46SWarner Losh  * The "layered" ioctl to read disk/partition size. Unfortunately
525ca987d46SWarner Losh  * the zfsboot case is hardest, because we do not have full software
526ca987d46SWarner Losh  * stack available, so we need to do some manual work here.
527ca987d46SWarner Losh  */
528ca987d46SWarner Losh uint64_t
529ca987d46SWarner Losh ldi_get_size(void *priv)
530ca987d46SWarner Losh {
531ca987d46SWarner Losh 	struct dsk *dskp = priv;
532ca987d46SWarner Losh 	uint64_t size = dskp->size;
533ca987d46SWarner Losh 
534ca987d46SWarner Losh 	if (dskp->start == 0)
535ca987d46SWarner Losh 		size = drvsize_ext(dskp);
536ca987d46SWarner Losh 
537ca987d46SWarner Losh 	return (size * DEV_BSIZE);
538ca987d46SWarner Losh }
539ca987d46SWarner Losh 
540ca987d46SWarner Losh static void
541ca987d46SWarner Losh probe_drive(struct dsk *dsk)
542ca987d46SWarner Losh {
543ca987d46SWarner Losh #ifdef GPT
544ca987d46SWarner Losh     struct gpt_hdr hdr;
545ca987d46SWarner Losh     struct gpt_ent *ent;
546ca987d46SWarner Losh     unsigned part, entries_per_sec;
547ca987d46SWarner Losh     daddr_t slba;
548ca987d46SWarner Losh #endif
549ca987d46SWarner Losh #if defined(GPT) || defined(LOADER_GELI_SUPPORT)
550ca987d46SWarner Losh     daddr_t elba;
551ca987d46SWarner Losh #endif
552ca987d46SWarner Losh 
553ca987d46SWarner Losh     struct dos_partition *dp;
554ca987d46SWarner Losh     char *sec;
555ca987d46SWarner Losh     unsigned i;
556ca987d46SWarner Losh 
557ca987d46SWarner Losh     /*
558ca987d46SWarner Losh      * If we find a vdev on the whole disk, stop here.
559ca987d46SWarner Losh      */
560ca987d46SWarner Losh     if (vdev_probe(vdev_read, dsk, NULL) == 0)
561ca987d46SWarner Losh 	return;
562ca987d46SWarner Losh 
563ca987d46SWarner Losh #ifdef LOADER_GELI_SUPPORT
564ca987d46SWarner Losh     /*
565ca987d46SWarner Losh      * Taste the disk, if it is GELI encrypted, decrypt it and check to see if
566ca987d46SWarner Losh      * it is a usable vdev then. Otherwise dig
567ca987d46SWarner Losh      * out the partition table and probe each slice/partition
568ca987d46SWarner Losh      * in turn for a vdev or GELI encrypted vdev.
569ca987d46SWarner Losh      */
570ca987d46SWarner Losh     elba = drvsize_ext(dsk);
571ca987d46SWarner Losh     if (elba > 0) {
572ca987d46SWarner Losh 	elba--;
573ca987d46SWarner Losh     }
574ca987d46SWarner Losh     if (geli_taste(vdev_read, dsk, elba) == 0) {
575ca987d46SWarner Losh 	if (geli_havekey(dsk) == 0 || geli_passphrase(&gelipw, dsk->unit,
576ca987d46SWarner Losh 	  ':', 0, dsk) == 0) {
577ca987d46SWarner Losh 	    if (vdev_probe(vdev_read, dsk, NULL) == 0) {
578ca987d46SWarner Losh 		return;
579ca987d46SWarner Losh 	    }
580ca987d46SWarner Losh 	}
581ca987d46SWarner Losh     }
582ca987d46SWarner Losh #endif /* LOADER_GELI_SUPPORT */
583ca987d46SWarner Losh 
584ca987d46SWarner Losh     sec = dmadat->secbuf;
585ca987d46SWarner Losh     dsk->start = 0;
586ca987d46SWarner Losh 
587ca987d46SWarner Losh #ifdef GPT
588ca987d46SWarner Losh     /*
589ca987d46SWarner Losh      * First check for GPT.
590ca987d46SWarner Losh      */
591ca987d46SWarner Losh     if (drvread(dsk, sec, 1, 1)) {
592ca987d46SWarner Losh 	return;
593ca987d46SWarner Losh     }
594ca987d46SWarner Losh     memcpy(&hdr, sec, sizeof(hdr));
595ca987d46SWarner Losh     if (memcmp(hdr.hdr_sig, GPT_HDR_SIG, sizeof(hdr.hdr_sig)) != 0 ||
596ca987d46SWarner Losh 	hdr.hdr_lba_self != 1 || hdr.hdr_revision < 0x00010000 ||
597ca987d46SWarner Losh 	hdr.hdr_entsz < sizeof(*ent) || DEV_BSIZE % hdr.hdr_entsz != 0) {
598ca987d46SWarner Losh 	goto trymbr;
599ca987d46SWarner Losh     }
600ca987d46SWarner Losh 
601ca987d46SWarner Losh     /*
602ca987d46SWarner Losh      * Probe all GPT partitions for the presence of ZFS pools. We
603ca987d46SWarner Losh      * return the spa_t for the first we find (if requested). This
604ca987d46SWarner Losh      * will have the effect of booting from the first pool on the
605ca987d46SWarner Losh      * disk.
606ca987d46SWarner Losh      *
607ca987d46SWarner Losh      * If no vdev is found, GELI decrypting the device and try again
608ca987d46SWarner Losh      */
609ca987d46SWarner Losh     entries_per_sec = DEV_BSIZE / hdr.hdr_entsz;
610ca987d46SWarner Losh     slba = hdr.hdr_lba_table;
611ca987d46SWarner Losh     elba = slba + hdr.hdr_entries / entries_per_sec;
612ca987d46SWarner Losh     while (slba < elba) {
613ca987d46SWarner Losh 	dsk->start = 0;
614ca987d46SWarner Losh 	if (drvread(dsk, sec, slba, 1))
615ca987d46SWarner Losh 	    return;
616ca987d46SWarner Losh 	for (part = 0; part < entries_per_sec; part++) {
617ca987d46SWarner Losh 	    ent = (struct gpt_ent *)(sec + part * hdr.hdr_entsz);
618ca987d46SWarner Losh 	    if (memcmp(&ent->ent_type, &freebsd_zfs_uuid,
619ca987d46SWarner Losh 		     sizeof(uuid_t)) == 0) {
620ca987d46SWarner Losh 		dsk->start = ent->ent_lba_start;
621ca987d46SWarner Losh 		dsk->size = ent->ent_lba_end - ent->ent_lba_start + 1;
622ca987d46SWarner Losh 		dsk->slice = part + 1;
623ca987d46SWarner Losh 		dsk->part = 255;
624ca987d46SWarner Losh 		if (vdev_probe(vdev_read, dsk, NULL) == 0) {
625ca987d46SWarner Losh 		    /*
626ca987d46SWarner Losh 		     * This slice had a vdev. We need a new dsk
627ca987d46SWarner Losh 		     * structure now since the vdev now owns this one.
628ca987d46SWarner Losh 		     */
629ca987d46SWarner Losh 		    dsk = copy_dsk(dsk);
630ca987d46SWarner Losh 		}
631ca987d46SWarner Losh #ifdef LOADER_GELI_SUPPORT
632ca987d46SWarner Losh 		else if (geli_taste(vdev_read, dsk, ent->ent_lba_end -
633ca987d46SWarner Losh 			 ent->ent_lba_start) == 0) {
634ca987d46SWarner Losh 		    if (geli_havekey(dsk) == 0 || geli_passphrase(&gelipw,
635ca987d46SWarner Losh 		      dsk->unit, 'p', dsk->slice, dsk) == 0) {
636ca987d46SWarner Losh 			/*
637ca987d46SWarner Losh 			 * This slice has GELI, check it for ZFS.
638ca987d46SWarner Losh 			 */
639ca987d46SWarner Losh 			if (vdev_probe(vdev_read, dsk, NULL) == 0) {
640ca987d46SWarner Losh 			    /*
641ca987d46SWarner Losh 			     * This slice had a vdev. We need a new dsk
642ca987d46SWarner Losh 			     * structure now since the vdev now owns this one.
643ca987d46SWarner Losh 			     */
644ca987d46SWarner Losh 			    dsk = copy_dsk(dsk);
645ca987d46SWarner Losh 			}
646ca987d46SWarner Losh 			break;
647ca987d46SWarner Losh 		    }
648ca987d46SWarner Losh 		}
649ca987d46SWarner Losh #endif /* LOADER_GELI_SUPPORT */
650ca987d46SWarner Losh 	    }
651ca987d46SWarner Losh 	}
652ca987d46SWarner Losh 	slba++;
653ca987d46SWarner Losh     }
654ca987d46SWarner Losh     return;
655ca987d46SWarner Losh trymbr:
656ca987d46SWarner Losh #endif /* GPT */
657ca987d46SWarner Losh 
658ca987d46SWarner Losh     if (drvread(dsk, sec, DOSBBSECTOR, 1))
659ca987d46SWarner Losh 	return;
660ca987d46SWarner Losh     dp = (void *)(sec + DOSPARTOFF);
661ca987d46SWarner Losh 
662ca987d46SWarner Losh     for (i = 0; i < NDOSPART; i++) {
663ca987d46SWarner Losh 	if (!dp[i].dp_typ)
664ca987d46SWarner Losh 	    continue;
665ca987d46SWarner Losh 	dsk->start = dp[i].dp_start;
666ca987d46SWarner Losh 	dsk->size = dp[i].dp_size;
667ca987d46SWarner Losh 	dsk->slice = i + 1;
668ca987d46SWarner Losh 	if (vdev_probe(vdev_read, dsk, NULL) == 0) {
669ca987d46SWarner Losh 	    dsk = copy_dsk(dsk);
670ca987d46SWarner Losh 	}
671ca987d46SWarner Losh #ifdef LOADER_GELI_SUPPORT
672ca987d46SWarner Losh 	else if (geli_taste(vdev_read, dsk, dp[i].dp_size -
673ca987d46SWarner Losh 		 dp[i].dp_start) == 0) {
674ca987d46SWarner Losh 	    if (geli_havekey(dsk) == 0 || geli_passphrase(&gelipw, dsk->unit,
675ca987d46SWarner Losh 	      's', i, dsk) == 0) {
676ca987d46SWarner Losh 		/*
677ca987d46SWarner Losh 		 * This slice has GELI, check it for ZFS.
678ca987d46SWarner Losh 		 */
679ca987d46SWarner Losh 		if (vdev_probe(vdev_read, dsk, NULL) == 0) {
680ca987d46SWarner Losh 		    /*
681ca987d46SWarner Losh 		     * This slice had a vdev. We need a new dsk
682ca987d46SWarner Losh 		     * structure now since the vdev now owns this one.
683ca987d46SWarner Losh 		     */
684ca987d46SWarner Losh 		    dsk = copy_dsk(dsk);
685ca987d46SWarner Losh 		}
686ca987d46SWarner Losh 		break;
687ca987d46SWarner Losh 	    }
688ca987d46SWarner Losh 	}
689ca987d46SWarner Losh #endif /* LOADER_GELI_SUPPORT */
690ca987d46SWarner Losh     }
691ca987d46SWarner Losh }
692ca987d46SWarner Losh 
693ca987d46SWarner Losh int
694ca987d46SWarner Losh main(void)
695ca987d46SWarner Losh {
696ca987d46SWarner Losh     dnode_phys_t dn;
697ca987d46SWarner Losh     off_t off;
698ca987d46SWarner Losh     struct dsk *dsk;
699ca987d46SWarner Losh     int autoboot, i;
700ca987d46SWarner Losh     int nextboot;
701ca987d46SWarner Losh     int rc;
702ca987d46SWarner Losh 
703ca987d46SWarner Losh     dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
704ca987d46SWarner Losh 
705ca987d46SWarner Losh     bios_getmem();
706ca987d46SWarner Losh 
707ca987d46SWarner Losh     if (high_heap_size > 0) {
708ca987d46SWarner Losh 	heap_end = PTOV(high_heap_base + high_heap_size);
709ca987d46SWarner Losh 	heap_next = PTOV(high_heap_base);
710ca987d46SWarner Losh     } else {
711ca987d46SWarner Losh 	heap_next = (char *)dmadat + sizeof(*dmadat);
712ca987d46SWarner Losh 	heap_end = (char *)PTOV(bios_basemem);
713ca987d46SWarner Losh     }
714ca987d46SWarner Losh 
715ca987d46SWarner Losh     dsk = malloc(sizeof(struct dsk));
716ca987d46SWarner Losh     dsk->drive = *(uint8_t *)PTOV(ARGS);
717ca987d46SWarner Losh     dsk->type = dsk->drive & DRV_HARD ? TYPE_AD : TYPE_FD;
718ca987d46SWarner Losh     dsk->unit = dsk->drive & DRV_MASK;
719ca987d46SWarner Losh     dsk->slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
720ca987d46SWarner Losh     dsk->part = 0;
721ca987d46SWarner Losh     dsk->start = 0;
722ca987d46SWarner Losh     dsk->size = 0;
723ca987d46SWarner Losh 
724ca987d46SWarner Losh     bootinfo.bi_version = BOOTINFO_VERSION;
725ca987d46SWarner Losh     bootinfo.bi_size = sizeof(bootinfo);
726ca987d46SWarner Losh     bootinfo.bi_basemem = bios_basemem / 1024;
727ca987d46SWarner Losh     bootinfo.bi_extmem = bios_extmem / 1024;
728ca987d46SWarner Losh     bootinfo.bi_memsizes_valid++;
729ca987d46SWarner Losh     bootinfo.bi_bios_dev = dsk->drive;
730ca987d46SWarner Losh 
731ca987d46SWarner Losh     bootdev = MAKEBOOTDEV(dev_maj[dsk->type],
732ca987d46SWarner Losh 			  dsk->slice, dsk->unit, dsk->part);
733ca987d46SWarner Losh 
734ca987d46SWarner Losh     /* Process configuration file */
735ca987d46SWarner Losh 
736ca987d46SWarner Losh     autoboot = 1;
737ca987d46SWarner Losh 
738ca987d46SWarner Losh #ifdef LOADER_GELI_SUPPORT
739ca987d46SWarner Losh     geli_init();
740ca987d46SWarner Losh #endif
741ca987d46SWarner Losh     zfs_init();
742ca987d46SWarner Losh 
743ca987d46SWarner Losh     /*
744ca987d46SWarner Losh      * Probe the boot drive first - we will try to boot from whatever
745ca987d46SWarner Losh      * pool we find on that drive.
746ca987d46SWarner Losh      */
747ca987d46SWarner Losh     probe_drive(dsk);
748ca987d46SWarner Losh 
749ca987d46SWarner Losh     /*
750ca987d46SWarner Losh      * Probe the rest of the drives that the bios knows about. This
751ca987d46SWarner Losh      * will find any other available pools and it may fill in missing
752ca987d46SWarner Losh      * vdevs for the boot pool.
753ca987d46SWarner Losh      */
754ca987d46SWarner Losh #ifndef VIRTUALBOX
755ca987d46SWarner Losh     for (i = 0; i < *(unsigned char *)PTOV(BIOS_NUMDRIVES); i++)
756ca987d46SWarner Losh #else
757ca987d46SWarner Losh     for (i = 0; i < MAXBDDEV; i++)
758ca987d46SWarner Losh #endif
759ca987d46SWarner Losh     {
760ca987d46SWarner Losh 	if ((i | DRV_HARD) == *(uint8_t *)PTOV(ARGS))
761ca987d46SWarner Losh 	    continue;
762ca987d46SWarner Losh 
763ca987d46SWarner Losh 	if (!int13probe(i | DRV_HARD))
764ca987d46SWarner Losh 	    break;
765ca987d46SWarner Losh 
766ca987d46SWarner Losh 	dsk = malloc(sizeof(struct dsk));
767ca987d46SWarner Losh 	dsk->drive = i | DRV_HARD;
768ca987d46SWarner Losh 	dsk->type = dsk->drive & TYPE_AD;
769ca987d46SWarner Losh 	dsk->unit = i;
770ca987d46SWarner Losh 	dsk->slice = 0;
771ca987d46SWarner Losh 	dsk->part = 0;
772ca987d46SWarner Losh 	dsk->start = 0;
773ca987d46SWarner Losh 	dsk->size = 0;
774ca987d46SWarner Losh 	probe_drive(dsk);
775ca987d46SWarner Losh     }
776ca987d46SWarner Losh 
777ca987d46SWarner Losh     /*
778ca987d46SWarner Losh      * The first discovered pool, if any, is the pool.
779ca987d46SWarner Losh      */
780ca987d46SWarner Losh     spa = spa_get_primary();
781ca987d46SWarner Losh     if (!spa) {
782ca987d46SWarner Losh 	printf("%s: No ZFS pools located, can't boot\n", BOOTPROG);
783ca987d46SWarner Losh 	for (;;)
784ca987d46SWarner Losh 	    ;
785ca987d46SWarner Losh     }
786ca987d46SWarner Losh 
787ca987d46SWarner Losh     primary_spa = spa;
788ca987d46SWarner Losh     primary_vdev = spa_get_primary_vdev(spa);
789ca987d46SWarner Losh 
790ca987d46SWarner Losh     nextboot = 0;
791ca987d46SWarner Losh     rc  = vdev_read_pad2(primary_vdev, cmd, sizeof(cmd));
792ca987d46SWarner Losh     if (vdev_clear_pad2(primary_vdev))
793ca987d46SWarner Losh 	printf("failed to clear pad2 area of primary vdev\n");
794ca987d46SWarner Losh     if (rc == 0) {
795ca987d46SWarner Losh 	if (*cmd) {
796ca987d46SWarner Losh 	    /*
797ca987d46SWarner Losh 	     * We could find an old-style ZFS Boot Block header here.
798ca987d46SWarner Losh 	     * Simply ignore it.
799ca987d46SWarner Losh 	     */
800ca987d46SWarner Losh 	    if (*(uint64_t *)cmd != 0x2f5b007b10c) {
801ca987d46SWarner Losh 		/*
802ca987d46SWarner Losh 		 * Note that parse() is destructive to cmd[] and we also want
803ca987d46SWarner Losh 		 * to honor RBX_QUIET option that could be present in cmd[].
804ca987d46SWarner Losh 		 */
805ca987d46SWarner Losh 		nextboot = 1;
806ca987d46SWarner Losh 		memcpy(cmddup, cmd, sizeof(cmd));
807ca987d46SWarner Losh 		if (parse_cmd()) {
808ca987d46SWarner Losh 		    printf("failed to parse pad2 area of primary vdev\n");
809ca987d46SWarner Losh 		    reboot();
810ca987d46SWarner Losh 		}
811ca987d46SWarner Losh 		if (!OPT_CHECK(RBX_QUIET))
812ca987d46SWarner Losh 		    printf("zfs nextboot: %s\n", cmddup);
813ca987d46SWarner Losh 	    }
814ca987d46SWarner Losh 	    /* Do not process this command twice */
815ca987d46SWarner Losh 	    *cmd = 0;
816ca987d46SWarner Losh 	}
817ca987d46SWarner Losh     } else
818ca987d46SWarner Losh 	printf("failed to read pad2 area of primary vdev\n");
819ca987d46SWarner Losh 
820ca987d46SWarner Losh     /* Mount ZFS only if it's not already mounted via nextboot parsing. */
821ca987d46SWarner Losh     if (zfsmount.spa == NULL &&
822ca987d46SWarner Losh 	(zfs_spa_init(spa) != 0 || zfs_mount(spa, 0, &zfsmount) != 0)) {
823ca987d46SWarner Losh 	printf("%s: failed to mount default pool %s\n",
824ca987d46SWarner Losh 	    BOOTPROG, spa->spa_name);
825ca987d46SWarner Losh 	autoboot = 0;
826ca987d46SWarner Losh     } else if (zfs_lookup(&zfsmount, PATH_CONFIG, &dn) == 0 ||
827ca987d46SWarner Losh         zfs_lookup(&zfsmount, PATH_DOTCONFIG, &dn) == 0) {
828ca987d46SWarner Losh 	off = 0;
829ca987d46SWarner Losh 	zfs_read(spa, &dn, &off, cmd, sizeof(cmd));
830ca987d46SWarner Losh     }
831ca987d46SWarner Losh 
832ca987d46SWarner Losh     if (*cmd) {
833ca987d46SWarner Losh 	/*
834ca987d46SWarner Losh 	 * Note that parse_cmd() is destructive to cmd[] and we also want
835ca987d46SWarner Losh 	 * to honor RBX_QUIET option that could be present in cmd[].
836ca987d46SWarner Losh 	 */
837ca987d46SWarner Losh 	memcpy(cmddup, cmd, sizeof(cmd));
838ca987d46SWarner Losh 	if (parse_cmd())
839ca987d46SWarner Losh 	    autoboot = 0;
840ca987d46SWarner Losh 	if (!OPT_CHECK(RBX_QUIET))
841ca987d46SWarner Losh 	    printf("%s: %s\n", PATH_CONFIG, cmddup);
842ca987d46SWarner Losh 	/* Do not process this command twice */
843ca987d46SWarner Losh 	*cmd = 0;
844ca987d46SWarner Losh     }
845ca987d46SWarner Losh 
846ca987d46SWarner Losh     /* Do not risk waiting at the prompt forever. */
847ca987d46SWarner Losh     if (nextboot && !autoboot)
848ca987d46SWarner Losh 	reboot();
849ca987d46SWarner Losh 
850ca987d46SWarner Losh     /*
851ca987d46SWarner Losh      * Try to exec /boot/loader. If interrupted by a keypress,
852ca987d46SWarner Losh      * or in case of failure, try to load a kernel directly instead.
853ca987d46SWarner Losh      */
854ca987d46SWarner Losh 
855ca987d46SWarner Losh     if (autoboot && !*kname) {
856ca987d46SWarner Losh 	memcpy(kname, PATH_LOADER_ZFS, sizeof(PATH_LOADER_ZFS));
857ca987d46SWarner Losh 	if (!keyhit(3)) {
858ca987d46SWarner Losh 	    load();
859ca987d46SWarner Losh 	    memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
860ca987d46SWarner Losh 	}
861ca987d46SWarner Losh     }
862ca987d46SWarner Losh 
863ca987d46SWarner Losh     /* Present the user with the boot2 prompt. */
864ca987d46SWarner Losh 
865ca987d46SWarner Losh     for (;;) {
866ca987d46SWarner Losh 	if (!autoboot || !OPT_CHECK(RBX_QUIET)) {
867ca987d46SWarner Losh 	    printf("\nFreeBSD/x86 boot\n");
868ca987d46SWarner Losh 	    if (zfs_rlookup(spa, zfsmount.rootobj, rootname) != 0)
869ca987d46SWarner Losh 		printf("Default: %s/<0x%llx>:%s\n"
870ca987d46SWarner Losh 		       "boot: ",
871ca987d46SWarner Losh 		       spa->spa_name, zfsmount.rootobj, kname);
872ca987d46SWarner Losh 	    else if (rootname[0] != '\0')
873ca987d46SWarner Losh 		printf("Default: %s/%s:%s\n"
874ca987d46SWarner Losh 		       "boot: ",
875ca987d46SWarner Losh 		       spa->spa_name, rootname, kname);
876ca987d46SWarner Losh 	    else
877ca987d46SWarner Losh 		printf("Default: %s:%s\n"
878ca987d46SWarner Losh 		       "boot: ",
879ca987d46SWarner Losh 		       spa->spa_name, kname);
880ca987d46SWarner Losh 	}
881ca987d46SWarner Losh 	if (ioctrl & IO_SERIAL)
882ca987d46SWarner Losh 	    sio_flush();
883ca987d46SWarner Losh 	if (!autoboot || keyhit(5))
884ca987d46SWarner Losh 	    getstr(cmd, sizeof(cmd));
885ca987d46SWarner Losh 	else if (!autoboot || !OPT_CHECK(RBX_QUIET))
886ca987d46SWarner Losh 	    putchar('\n');
887ca987d46SWarner Losh 	autoboot = 0;
888ca987d46SWarner Losh 	if (parse_cmd())
889ca987d46SWarner Losh 	    putchar('\a');
890ca987d46SWarner Losh 	else
891ca987d46SWarner Losh 	    load();
892ca987d46SWarner Losh     }
893ca987d46SWarner Losh }
894ca987d46SWarner Losh 
895ca987d46SWarner Losh /* XXX - Needed for btxld to link the boot2 binary; do not remove. */
896ca987d46SWarner Losh void
897ca987d46SWarner Losh exit(int x)
898ca987d46SWarner Losh {
899ca987d46SWarner Losh     __exit(x);
900ca987d46SWarner Losh }
901ca987d46SWarner Losh 
902ca987d46SWarner Losh void
903ca987d46SWarner Losh reboot(void)
904ca987d46SWarner Losh {
905ca987d46SWarner Losh     __exit(0);
906ca987d46SWarner Losh }
907ca987d46SWarner Losh 
908ca987d46SWarner Losh static void
909ca987d46SWarner Losh load(void)
910ca987d46SWarner Losh {
911ca987d46SWarner Losh     union {
912ca987d46SWarner Losh 	struct exec ex;
913ca987d46SWarner Losh 	Elf32_Ehdr eh;
914ca987d46SWarner Losh     } hdr;
915ca987d46SWarner Losh     static Elf32_Phdr ep[2];
916ca987d46SWarner Losh     static Elf32_Shdr es[2];
917ca987d46SWarner Losh     caddr_t p;
918ca987d46SWarner Losh     dnode_phys_t dn;
919ca987d46SWarner Losh     off_t off;
920ca987d46SWarner Losh     uint32_t addr, x;
921ca987d46SWarner Losh     int fmt, i, j;
922ca987d46SWarner Losh 
923ca987d46SWarner Losh     if (zfs_lookup(&zfsmount, kname, &dn)) {
924ca987d46SWarner Losh 	printf("\nCan't find %s\n", kname);
925ca987d46SWarner Losh 	return;
926ca987d46SWarner Losh     }
927ca987d46SWarner Losh     off = 0;
928ca987d46SWarner Losh     if (xfsread(&dn, &off, &hdr, sizeof(hdr)))
929ca987d46SWarner Losh 	return;
930ca987d46SWarner Losh     if (N_GETMAGIC(hdr.ex) == ZMAGIC)
931ca987d46SWarner Losh 	fmt = 0;
932ca987d46SWarner Losh     else if (IS_ELF(hdr.eh))
933ca987d46SWarner Losh 	fmt = 1;
934ca987d46SWarner Losh     else {
935ca987d46SWarner Losh 	printf("Invalid %s\n", "format");
936ca987d46SWarner Losh 	return;
937ca987d46SWarner Losh     }
938ca987d46SWarner Losh     if (fmt == 0) {
939ca987d46SWarner Losh 	addr = hdr.ex.a_entry & 0xffffff;
940ca987d46SWarner Losh 	p = PTOV(addr);
941ca987d46SWarner Losh 	off = PAGE_SIZE;
942ca987d46SWarner Losh 	if (xfsread(&dn, &off, p, hdr.ex.a_text))
943ca987d46SWarner Losh 	    return;
944ca987d46SWarner Losh 	p += roundup2(hdr.ex.a_text, PAGE_SIZE);
945ca987d46SWarner Losh 	if (xfsread(&dn, &off, p, hdr.ex.a_data))
946ca987d46SWarner Losh 	    return;
947ca987d46SWarner Losh 	p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
948ca987d46SWarner Losh 	bootinfo.bi_symtab = VTOP(p);
949ca987d46SWarner Losh 	memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
950ca987d46SWarner Losh 	p += sizeof(hdr.ex.a_syms);
951ca987d46SWarner Losh 	if (hdr.ex.a_syms) {
952ca987d46SWarner Losh 	    if (xfsread(&dn, &off, p, hdr.ex.a_syms))
953ca987d46SWarner Losh 		return;
954ca987d46SWarner Losh 	    p += hdr.ex.a_syms;
955ca987d46SWarner Losh 	    if (xfsread(&dn, &off, p, sizeof(int)))
956ca987d46SWarner Losh 		return;
957ca987d46SWarner Losh 	    x = *(uint32_t *)p;
958ca987d46SWarner Losh 	    p += sizeof(int);
959ca987d46SWarner Losh 	    x -= sizeof(int);
960ca987d46SWarner Losh 	    if (xfsread(&dn, &off, p, x))
961ca987d46SWarner Losh 		return;
962ca987d46SWarner Losh 	    p += x;
963ca987d46SWarner Losh 	}
964ca987d46SWarner Losh     } else {
965ca987d46SWarner Losh 	off = hdr.eh.e_phoff;
966ca987d46SWarner Losh 	for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
967ca987d46SWarner Losh 	    if (xfsread(&dn, &off, ep + j, sizeof(ep[0])))
968ca987d46SWarner Losh 		return;
969ca987d46SWarner Losh 	    if (ep[j].p_type == PT_LOAD)
970ca987d46SWarner Losh 		j++;
971ca987d46SWarner Losh 	}
972ca987d46SWarner Losh 	for (i = 0; i < 2; i++) {
973ca987d46SWarner Losh 	    p = PTOV(ep[i].p_paddr & 0xffffff);
974ca987d46SWarner Losh 	    off = ep[i].p_offset;
975ca987d46SWarner Losh 	    if (xfsread(&dn, &off, p, ep[i].p_filesz))
976ca987d46SWarner Losh 		return;
977ca987d46SWarner Losh 	}
978ca987d46SWarner Losh 	p += roundup2(ep[1].p_memsz, PAGE_SIZE);
979ca987d46SWarner Losh 	bootinfo.bi_symtab = VTOP(p);
980ca987d46SWarner Losh 	if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
981ca987d46SWarner Losh 	    off = hdr.eh.e_shoff + sizeof(es[0]) *
982ca987d46SWarner Losh 		(hdr.eh.e_shstrndx + 1);
983ca987d46SWarner Losh 	    if (xfsread(&dn, &off, &es, sizeof(es)))
984ca987d46SWarner Losh 		return;
985ca987d46SWarner Losh 	    for (i = 0; i < 2; i++) {
986ca987d46SWarner Losh 		memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size));
987ca987d46SWarner Losh 		p += sizeof(es[i].sh_size);
988ca987d46SWarner Losh 		off = es[i].sh_offset;
989ca987d46SWarner Losh 		if (xfsread(&dn, &off, p, es[i].sh_size))
990ca987d46SWarner Losh 		    return;
991ca987d46SWarner Losh 		p += es[i].sh_size;
992ca987d46SWarner Losh 	    }
993ca987d46SWarner Losh 	}
994ca987d46SWarner Losh 	addr = hdr.eh.e_entry & 0xffffff;
995ca987d46SWarner Losh     }
996ca987d46SWarner Losh     bootinfo.bi_esymtab = VTOP(p);
997ca987d46SWarner Losh     bootinfo.bi_kernelname = VTOP(kname);
998ca987d46SWarner Losh     zfsargs.size = sizeof(zfsargs);
999ca987d46SWarner Losh     zfsargs.pool = zfsmount.spa->spa_guid;
1000ca987d46SWarner Losh     zfsargs.root = zfsmount.rootobj;
1001ca987d46SWarner Losh     zfsargs.primary_pool = primary_spa->spa_guid;
1002ca987d46SWarner Losh #ifdef LOADER_GELI_SUPPORT
1003ca987d46SWarner Losh     explicit_bzero(gelipw, sizeof(gelipw));
1004ca987d46SWarner Losh     gelibuf = malloc(sizeof(struct keybuf) + (GELI_MAX_KEYS * sizeof(struct keybuf_ent)));
1005ca987d46SWarner Losh     geli_fill_keybuf(gelibuf);
1006ca987d46SWarner Losh     zfsargs.notapw = '\0';
1007ca987d46SWarner Losh     zfsargs.keybuf_sentinel = KEYBUF_SENTINEL;
1008ca987d46SWarner Losh     zfsargs.keybuf = gelibuf;
1009ca987d46SWarner Losh #else
1010ca987d46SWarner Losh     zfsargs.gelipw[0] = '\0';
1011ca987d46SWarner Losh #endif
1012ca987d46SWarner Losh     if (primary_vdev != NULL)
1013ca987d46SWarner Losh 	zfsargs.primary_vdev = primary_vdev->v_guid;
1014ca987d46SWarner Losh     else
1015ca987d46SWarner Losh 	printf("failed to detect primary vdev\n");
1016ca987d46SWarner Losh     __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
1017ca987d46SWarner Losh 	   bootdev,
1018ca987d46SWarner Losh 	   KARGS_FLAGS_ZFS | KARGS_FLAGS_EXTARG,
1019ca987d46SWarner Losh 	   (uint32_t) spa->spa_guid,
1020ca987d46SWarner Losh 	   (uint32_t) (spa->spa_guid >> 32),
1021ca987d46SWarner Losh 	   VTOP(&bootinfo),
1022ca987d46SWarner Losh 	   zfsargs);
1023ca987d46SWarner Losh }
1024ca987d46SWarner Losh 
1025ca987d46SWarner Losh static int
1026ca987d46SWarner Losh zfs_mount_ds(char *dsname)
1027ca987d46SWarner Losh {
1028ca987d46SWarner Losh     uint64_t newroot;
1029ca987d46SWarner Losh     spa_t *newspa;
1030ca987d46SWarner Losh     char *q;
1031ca987d46SWarner Losh 
1032ca987d46SWarner Losh     q = strchr(dsname, '/');
1033ca987d46SWarner Losh     if (q)
1034ca987d46SWarner Losh 	*q++ = '\0';
1035ca987d46SWarner Losh     newspa = spa_find_by_name(dsname);
1036ca987d46SWarner Losh     if (newspa == NULL) {
1037ca987d46SWarner Losh 	printf("\nCan't find ZFS pool %s\n", dsname);
1038ca987d46SWarner Losh 	return -1;
1039ca987d46SWarner Losh     }
1040ca987d46SWarner Losh 
1041ca987d46SWarner Losh     if (zfs_spa_init(newspa))
1042ca987d46SWarner Losh 	return -1;
1043ca987d46SWarner Losh 
1044ca987d46SWarner Losh     newroot = 0;
1045ca987d46SWarner Losh     if (q) {
1046ca987d46SWarner Losh 	if (zfs_lookup_dataset(newspa, q, &newroot)) {
1047ca987d46SWarner Losh 	    printf("\nCan't find dataset %s in ZFS pool %s\n",
1048ca987d46SWarner Losh 		    q, newspa->spa_name);
1049ca987d46SWarner Losh 	    return -1;
1050ca987d46SWarner Losh 	}
1051ca987d46SWarner Losh     }
1052ca987d46SWarner Losh     if (zfs_mount(newspa, newroot, &zfsmount)) {
1053ca987d46SWarner Losh 	printf("\nCan't mount ZFS dataset\n");
1054ca987d46SWarner Losh 	return -1;
1055ca987d46SWarner Losh     }
1056ca987d46SWarner Losh     spa = newspa;
1057ca987d46SWarner Losh     return (0);
1058ca987d46SWarner Losh }
1059ca987d46SWarner Losh 
1060ca987d46SWarner Losh static int
1061ca987d46SWarner Losh parse_cmd(void)
1062ca987d46SWarner Losh {
1063ca987d46SWarner Losh     char *arg = cmd;
1064ca987d46SWarner Losh     char *ep, *p, *q;
1065ca987d46SWarner Losh     const char *cp;
1066ca987d46SWarner Losh     int c, i, j;
1067ca987d46SWarner Losh 
1068ca987d46SWarner Losh     while ((c = *arg++)) {
1069ca987d46SWarner Losh 	if (c == ' ' || c == '\t' || c == '\n')
1070ca987d46SWarner Losh 	    continue;
1071ca987d46SWarner Losh 	for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
1072ca987d46SWarner Losh 	ep = p;
1073ca987d46SWarner Losh 	if (*p)
1074ca987d46SWarner Losh 	    *p++ = 0;
1075ca987d46SWarner Losh 	if (c == '-') {
1076ca987d46SWarner Losh 	    while ((c = *arg++)) {
1077ca987d46SWarner Losh 		if (c == 'P') {
1078ca987d46SWarner Losh 		    if (*(uint8_t *)PTOV(0x496) & 0x10) {
1079ca987d46SWarner Losh 			cp = "yes";
1080ca987d46SWarner Losh 		    } else {
1081ca987d46SWarner Losh 			opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL);
1082ca987d46SWarner Losh 			cp = "no";
1083ca987d46SWarner Losh 		    }
1084ca987d46SWarner Losh 		    printf("Keyboard: %s\n", cp);
1085ca987d46SWarner Losh 		    continue;
1086ca987d46SWarner Losh 		} else if (c == 'S') {
1087ca987d46SWarner Losh 		    j = 0;
1088ca987d46SWarner Losh 		    while ((unsigned int)(i = *arg++ - '0') <= 9)
1089ca987d46SWarner Losh 			j = j * 10 + i;
1090ca987d46SWarner Losh 		    if (j > 0 && i == -'0') {
1091ca987d46SWarner Losh 			comspeed = j;
1092ca987d46SWarner Losh 			break;
1093ca987d46SWarner Losh 		    }
1094ca987d46SWarner Losh 		    /* Fall through to error below ('S' not in optstr[]). */
1095ca987d46SWarner Losh 		}
1096ca987d46SWarner Losh 		for (i = 0; c != optstr[i]; i++)
1097ca987d46SWarner Losh 		    if (i == NOPT - 1)
1098ca987d46SWarner Losh 			return -1;
1099ca987d46SWarner Losh 		opts ^= OPT_SET(flags[i]);
1100ca987d46SWarner Losh 	    }
1101ca987d46SWarner Losh 	    ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
1102ca987d46SWarner Losh 		     OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
1103ca987d46SWarner Losh 	    if (ioctrl & IO_SERIAL) {
1104ca987d46SWarner Losh 	        if (sio_init(115200 / comspeed) != 0)
1105ca987d46SWarner Losh 		    ioctrl &= ~IO_SERIAL;
1106ca987d46SWarner Losh 	    }
1107ca987d46SWarner Losh 	} if (c == '?') {
1108ca987d46SWarner Losh 	    dnode_phys_t dn;
1109ca987d46SWarner Losh 
1110ca987d46SWarner Losh 	    if (zfs_lookup(&zfsmount, arg, &dn) == 0) {
1111ca987d46SWarner Losh 		zap_list(spa, &dn);
1112ca987d46SWarner Losh 	    }
1113ca987d46SWarner Losh 	    return -1;
1114ca987d46SWarner Losh 	} else {
1115ca987d46SWarner Losh 	    arg--;
1116ca987d46SWarner Losh 
1117ca987d46SWarner Losh 	    /*
1118ca987d46SWarner Losh 	     * Report pool status if the comment is 'status'. Lets
1119ca987d46SWarner Losh 	     * hope no-one wants to load /status as a kernel.
1120ca987d46SWarner Losh 	     */
1121ca987d46SWarner Losh 	    if (!strcmp(arg, "status")) {
1122ca987d46SWarner Losh 		spa_all_status();
1123ca987d46SWarner Losh 		return -1;
1124ca987d46SWarner Losh 	    }
1125ca987d46SWarner Losh 
1126ca987d46SWarner Losh 	    /*
1127ca987d46SWarner Losh 	     * If there is "zfs:" prefix simply ignore it.
1128ca987d46SWarner Losh 	     */
1129ca987d46SWarner Losh 	    if (strncmp(arg, "zfs:", 4) == 0)
1130ca987d46SWarner Losh 		arg += 4;
1131ca987d46SWarner Losh 
1132ca987d46SWarner Losh 	    /*
1133ca987d46SWarner Losh 	     * If there is a colon, switch pools.
1134ca987d46SWarner Losh 	     */
1135ca987d46SWarner Losh 	    q = strchr(arg, ':');
1136ca987d46SWarner Losh 	    if (q) {
1137ca987d46SWarner Losh 		*q++ = '\0';
1138ca987d46SWarner Losh 		if (zfs_mount_ds(arg) != 0)
1139ca987d46SWarner Losh 		    return -1;
1140ca987d46SWarner Losh 		arg = q;
1141ca987d46SWarner Losh 	    }
1142ca987d46SWarner Losh 	    if ((i = ep - arg)) {
1143ca987d46SWarner Losh 		if ((size_t)i >= sizeof(kname))
1144ca987d46SWarner Losh 		    return -1;
1145ca987d46SWarner Losh 		memcpy(kname, arg, i + 1);
1146ca987d46SWarner Losh 	    }
1147ca987d46SWarner Losh 	}
1148ca987d46SWarner Losh 	arg = p;
1149ca987d46SWarner Losh     }
1150ca987d46SWarner Losh     return 0;
1151ca987d46SWarner Losh }
1152