1223695Sdfr/*-
2223695Sdfr * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3223695Sdfr * Copyright (c) 1998,2000 Doug Rabson <dfr@freebsd.org>
4223695Sdfr * All rights reserved.
5223695Sdfr *
6223695Sdfr * Redistribution and use in source and binary forms, with or without
7223695Sdfr * modification, are permitted provided that the following conditions
8223695Sdfr * are met:
9223695Sdfr * 1. Redistributions of source code must retain the above copyright
10223695Sdfr *    notice, this list of conditions and the following disclaimer.
11223695Sdfr * 2. Redistributions in binary form must reproduce the above copyright
12223695Sdfr *    notice, this list of conditions and the following disclaimer in the
13223695Sdfr *    documentation and/or other materials provided with the distribution.
14223695Sdfr *
15223695Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16223695Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17223695Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18223695Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19223695Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20223695Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21223695Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22223695Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23223695Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24223695Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25223695Sdfr * SUCH DAMAGE.
26223695Sdfr */
27223695Sdfr
28223695Sdfr#include <sys/cdefs.h>
29223695Sdfr__FBSDID("$FreeBSD$");
30223695Sdfr
31223695Sdfr#include <stand.h>
32223695Sdfr#include <string.h>
33223695Sdfr#include <setjmp.h>
34223695Sdfr
35223695Sdfr#include "bootstrap.h"
36223695Sdfr#include "disk.h"
37223695Sdfr#include "libuserboot.h"
38223695Sdfr
39268932Sjhb#if defined(USERBOOT_ZFS_SUPPORT)
40268932Sjhb#include "../zfs/libzfs.h"
41268932Sjhb
42268932Sjhbstatic void userboot_zfs_probe(void);
43268932Sjhbstatic int userboot_zfs_found;
44268932Sjhb#endif
45268932Sjhb
46242935Sneel#define	USERBOOT_VERSION	USERBOOT_VERSION_3
47239073Sae
48268932Sjhb#define	MALLOCSZ		(10*1024*1024)
49268932Sjhb
50241164Saestruct loader_callbacks *callbacks;
51223695Sdfrvoid *callbacks_arg;
52223695Sdfr
53223695Sdfrextern char bootprog_name[];
54223695Sdfrextern char bootprog_rev[];
55223695Sdfrextern char bootprog_date[];
56223695Sdfrextern char bootprog_maker[];
57223695Sdfrstatic jmp_buf jb;
58223695Sdfr
59223695Sdfrstruct arch_switch archsw;	/* MI/MD interface boundary */
60223695Sdfr
61223695Sdfrstatic void	extract_currdev(void);
62223695Sdfr
63223695Sdfrvoid
64223695Sdfrdelay(int usec)
65223695Sdfr{
66223695Sdfr
67223695Sdfr        CALLBACK(delay, usec);
68223695Sdfr}
69223695Sdfr
70223695Sdfrvoid
71223695Sdfrexit(int v)
72223695Sdfr{
73223695Sdfr
74223695Sdfr	CALLBACK(exit, v);
75223695Sdfr	longjmp(jb, 1);
76223695Sdfr}
77223695Sdfr
78223695Sdfrvoid
79241164Saeloader_main(struct loader_callbacks *cb, void *arg, int version, int ndisks)
80223695Sdfr{
81268932Sjhb	static char mallocbuf[MALLOCSZ];
82242935Sneel	const char *var;
83223695Sdfr	int i;
84223695Sdfr
85239073Sae        if (version != USERBOOT_VERSION)
86223695Sdfr                abort();
87223695Sdfr
88223695Sdfr	callbacks = cb;
89223695Sdfr        callbacks_arg = arg;
90223695Sdfr	userboot_disk_maxunit = ndisks;
91223695Sdfr
92223695Sdfr	/*
93223695Sdfr	 * initialise the heap as early as possible.  Once this is done,
94268932Sjhb	 * alloc() is usable.
95223695Sdfr	 */
96268932Sjhb	setheap((void *)mallocbuf, (void *)(mallocbuf + sizeof(mallocbuf)));
97223695Sdfr
98223695Sdfr        /*
99223695Sdfr         * Hook up the console
100223695Sdfr         */
101223695Sdfr	cons_probe();
102223695Sdfr
103223695Sdfr	printf("\n");
104223695Sdfr	printf("%s, Revision %s\n", bootprog_name, bootprog_rev);
105223695Sdfr	printf("(%s, %s)\n", bootprog_maker, bootprog_date);
106223695Sdfr#if 0
107223695Sdfr	printf("Memory: %ld k\n", memsize() / 1024);
108223695Sdfr#endif
109223695Sdfr
110223695Sdfr	setenv("LINES", "24", 1);	/* optional */
111223695Sdfr
112242935Sneel	/*
113242935Sneel	 * Set custom environment variables
114242935Sneel	 */
115242935Sneel	i = 0;
116242935Sneel	while (1) {
117242935Sneel		var = CALLBACK(getenv, i++);
118242935Sneel		if (var == NULL)
119242935Sneel			break;
120242935Sneel		putenv(var);
121242935Sneel	}
122242935Sneel
123223695Sdfr	archsw.arch_autoload = userboot_autoload;
124223695Sdfr	archsw.arch_getdev = userboot_getdev;
125223695Sdfr	archsw.arch_copyin = userboot_copyin;
126223695Sdfr	archsw.arch_copyout = userboot_copyout;
127223695Sdfr	archsw.arch_readin = userboot_readin;
128268932Sjhb#if defined(USERBOOT_ZFS_SUPPORT)
129268932Sjhb	archsw.arch_zfs_probe = userboot_zfs_probe;
130268932Sjhb#endif
131223695Sdfr
132268932Sjhb	/*
133268932Sjhb	 * March through the device switch probing for things.
134268932Sjhb	 */
135268932Sjhb	for (i = 0; devsw[i] != NULL; i++)
136268932Sjhb		if (devsw[i]->dv_init != NULL)
137268932Sjhb			(devsw[i]->dv_init)();
138268932Sjhb
139223695Sdfr	extract_currdev();
140223695Sdfr
141223695Sdfr	if (setjmp(jb))
142223695Sdfr		return;
143223695Sdfr
144223695Sdfr	interact();			/* doesn't return */
145223695Sdfr
146223695Sdfr	exit(0);
147223695Sdfr}
148223695Sdfr
149223695Sdfr/*
150223695Sdfr * Set the 'current device' by (if possible) recovering the boot device as
151223695Sdfr * supplied by the initial bootstrap.
152223695Sdfr */
153223695Sdfrstatic void
154223695Sdfrextract_currdev(void)
155223695Sdfr{
156223695Sdfr	struct disk_devdesc dev;
157223695Sdfr
158223695Sdfr	//bzero(&dev, sizeof(dev));
159223695Sdfr
160268932Sjhb#if defined(USERBOOT_ZFS_SUPPORT)
161268932Sjhb	if (userboot_zfs_found) {
162268932Sjhb		struct zfs_devdesc zdev;
163268932Sjhb
164268932Sjhb		/* Leave the pool/root guid's unassigned */
165268932Sjhb		bzero(&zdev, sizeof(zdev));
166268932Sjhb		zdev.d_dev = &zfs_dev;
167268932Sjhb		zdev.d_type = zdev.d_dev->dv_type;
168268932Sjhb
169268932Sjhb		dev = *(struct disk_devdesc *)&zdev;
170293802Sallanjude		init_zfs_bootenv(zfs_fmtdev(&dev));
171268932Sjhb	} else
172268932Sjhb#endif
173268932Sjhb
174223695Sdfr	if (userboot_disk_maxunit > 0) {
175223695Sdfr		dev.d_dev = &userboot_disk;
176223695Sdfr		dev.d_type = dev.d_dev->dv_type;
177223695Sdfr		dev.d_unit = 0;
178223695Sdfr		dev.d_slice = 0;
179223695Sdfr		dev.d_partition = 0;
180223695Sdfr		/*
181243700Sneel		 * If we cannot auto-detect the partition type then
182243700Sneel		 * access the disk as a raw device.
183223695Sdfr		 */
184243700Sneel		if (dev.d_dev->dv_open(NULL, &dev)) {
185243700Sneel			dev.d_slice = -1;
186243700Sneel			dev.d_partition = -1;
187243700Sneel		}
188223695Sdfr	} else {
189223695Sdfr		dev.d_dev = &host_dev;
190223695Sdfr		dev.d_type = dev.d_dev->dv_type;
191223695Sdfr		dev.d_unit = 0;
192223695Sdfr	}
193223695Sdfr
194223695Sdfr	env_setenv("currdev", EV_VOLATILE, userboot_fmtdev(&dev),
195223695Sdfr            userboot_setcurrdev, env_nounset);
196223695Sdfr	env_setenv("loaddev", EV_VOLATILE, userboot_fmtdev(&dev),
197223695Sdfr            env_noset, env_nounset);
198223695Sdfr}
199223695Sdfr
200268932Sjhb#if defined(USERBOOT_ZFS_SUPPORT)
201268932Sjhbstatic void
202268932Sjhbuserboot_zfs_probe(void)
203268932Sjhb{
204268932Sjhb	char devname[32];
205268932Sjhb	uint64_t pool_guid;
206268932Sjhb	int unit;
207268932Sjhb
208268932Sjhb	/*
209268932Sjhb	 * Open all the disks we can find and see if we can reconstruct
210268932Sjhb	 * ZFS pools from them. Record if any were found.
211268932Sjhb	 */
212268932Sjhb	for (unit = 0; unit < userboot_disk_maxunit; unit++) {
213268932Sjhb		sprintf(devname, "disk%d:", unit);
214268932Sjhb		pool_guid = 0;
215268932Sjhb		zfs_probe_dev(devname, &pool_guid);
216268932Sjhb		if (pool_guid != 0)
217268932Sjhb			userboot_zfs_found = 1;
218268932Sjhb	}
219268932Sjhb}
220268932Sjhb
221268932SjhbCOMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset",
222268932Sjhb	    command_lszfs);
223268932Sjhb
224268932Sjhbstatic int
225268932Sjhbcommand_lszfs(int argc, char *argv[])
226268932Sjhb{
227268932Sjhb	int err;
228268932Sjhb
229268932Sjhb	if (argc != 2) {
230268932Sjhb		command_errmsg = "a single dataset must be supplied";
231268932Sjhb		return (CMD_ERROR);
232268932Sjhb	}
233268932Sjhb
234268932Sjhb	err = zfs_list(argv[1]);
235268932Sjhb	if (err != 0) {
236268932Sjhb		command_errmsg = strerror(err);
237268932Sjhb		return (CMD_ERROR);
238268932Sjhb	}
239268932Sjhb	return (CMD_OK);
240268932Sjhb}
241293802Sallanjude
242293802SallanjudeCOMMAND_SET(reloadbe, "reloadbe", "refresh the list of ZFS Boot Environments",
243293802Sallanjude	    command_reloadbe);
244293802Sallanjude
245293802Sallanjudestatic int
246293802Sallanjudecommand_reloadbe(int argc, char *argv[])
247293802Sallanjude{
248293802Sallanjude	int err;
249293802Sallanjude	char *root;
250293802Sallanjude
251293802Sallanjude	if (argc > 2) {
252293802Sallanjude		command_errmsg = "wrong number of arguments";
253293802Sallanjude		return (CMD_ERROR);
254293802Sallanjude	}
255293802Sallanjude
256293802Sallanjude	if (argc == 2) {
257293802Sallanjude		err = zfs_bootenv(argv[1]);
258293802Sallanjude	} else {
259293802Sallanjude		root = getenv("zfs_be_root");
260293802Sallanjude		if (root == NULL) {
261293802Sallanjude			return (CMD_OK);
262293802Sallanjude		}
263293802Sallanjude		err = zfs_bootenv(root);
264293802Sallanjude	}
265293802Sallanjude
266293802Sallanjude	if (err != 0) {
267293802Sallanjude		command_errmsg = strerror(err);
268293802Sallanjude		return (CMD_ERROR);
269293802Sallanjude	}
270293802Sallanjude
271293802Sallanjude	return (CMD_OK);
272293802Sallanjude}
273268932Sjhb#endif /* USERBOOT_ZFS_SUPPORT */
274268932Sjhb
275223695SdfrCOMMAND_SET(quit, "quit", "exit the loader", command_quit);
276223695Sdfr
277223695Sdfrstatic int
278223695Sdfrcommand_quit(int argc, char *argv[])
279223695Sdfr{
280223695Sdfr
281223695Sdfr	exit(USERBOOT_EXIT_QUIT);
282223695Sdfr	return (CMD_OK);
283223695Sdfr}
284223695Sdfr
285223695SdfrCOMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
286223695Sdfr
287223695Sdfrstatic int
288223695Sdfrcommand_reboot(int argc, char *argv[])
289223695Sdfr{
290223695Sdfr
291223695Sdfr	exit(USERBOOT_EXIT_REBOOT);
292223695Sdfr	return (CMD_OK);
293223695Sdfr}
294