main.c revision 293802
1/*-
2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3 * Copyright (c) 1998,2000 Doug Rabson <dfr@freebsd.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: stable/10/sys/boot/userboot/userboot/main.c 293802 2016-01-13 01:50:02Z allanjude $");
30
31#include <stand.h>
32#include <string.h>
33#include <setjmp.h>
34
35#include "bootstrap.h"
36#include "disk.h"
37#include "libuserboot.h"
38
39#if defined(USERBOOT_ZFS_SUPPORT)
40#include "../zfs/libzfs.h"
41
42static void userboot_zfs_probe(void);
43static int userboot_zfs_found;
44static void init_zfs_bootenv(char *currdev);
45#endif
46
47#define	USERBOOT_VERSION	USERBOOT_VERSION_3
48
49#define	MALLOCSZ		(10*1024*1024)
50
51struct loader_callbacks *callbacks;
52void *callbacks_arg;
53
54extern char bootprog_name[];
55extern char bootprog_rev[];
56extern char bootprog_date[];
57extern char bootprog_maker[];
58static jmp_buf jb;
59
60struct arch_switch archsw;	/* MI/MD interface boundary */
61
62static void	extract_currdev(void);
63
64void
65delay(int usec)
66{
67
68        CALLBACK(delay, usec);
69}
70
71void
72exit(int v)
73{
74
75	CALLBACK(exit, v);
76	longjmp(jb, 1);
77}
78
79void
80loader_main(struct loader_callbacks *cb, void *arg, int version, int ndisks)
81{
82	static char mallocbuf[MALLOCSZ];
83	const char *var;
84	int i;
85
86        if (version != USERBOOT_VERSION)
87                abort();
88
89	callbacks = cb;
90        callbacks_arg = arg;
91	userboot_disk_maxunit = ndisks;
92
93	/*
94	 * initialise the heap as early as possible.  Once this is done,
95	 * alloc() is usable.
96	 */
97	setheap((void *)mallocbuf, (void *)(mallocbuf + sizeof(mallocbuf)));
98
99        /*
100         * Hook up the console
101         */
102	cons_probe();
103
104	printf("\n");
105	printf("%s, Revision %s\n", bootprog_name, bootprog_rev);
106	printf("(%s, %s)\n", bootprog_maker, bootprog_date);
107#if 0
108	printf("Memory: %ld k\n", memsize() / 1024);
109#endif
110
111	setenv("LINES", "24", 1);	/* optional */
112
113	/*
114	 * Set custom environment variables
115	 */
116	i = 0;
117	while (1) {
118		var = CALLBACK(getenv, i++);
119		if (var == NULL)
120			break;
121		putenv(var);
122	}
123
124	archsw.arch_autoload = userboot_autoload;
125	archsw.arch_getdev = userboot_getdev;
126	archsw.arch_copyin = userboot_copyin;
127	archsw.arch_copyout = userboot_copyout;
128	archsw.arch_readin = userboot_readin;
129#if defined(USERBOOT_ZFS_SUPPORT)
130	archsw.arch_zfs_probe = userboot_zfs_probe;
131#endif
132
133	/*
134	 * March through the device switch probing for things.
135	 */
136	for (i = 0; devsw[i] != NULL; i++)
137		if (devsw[i]->dv_init != NULL)
138			(devsw[i]->dv_init)();
139
140	extract_currdev();
141
142	if (setjmp(jb))
143		return;
144
145	interact();			/* doesn't return */
146
147	exit(0);
148}
149
150/*
151 * Set the 'current device' by (if possible) recovering the boot device as
152 * supplied by the initial bootstrap.
153 */
154static void
155extract_currdev(void)
156{
157	struct disk_devdesc dev;
158
159	//bzero(&dev, sizeof(dev));
160
161#if defined(USERBOOT_ZFS_SUPPORT)
162	if (userboot_zfs_found) {
163		struct zfs_devdesc zdev;
164
165		/* Leave the pool/root guid's unassigned */
166		bzero(&zdev, sizeof(zdev));
167		zdev.d_dev = &zfs_dev;
168		zdev.d_type = zdev.d_dev->dv_type;
169
170		dev = *(struct disk_devdesc *)&zdev;
171		init_zfs_bootenv(zfs_fmtdev(&dev));
172	} else
173#endif
174
175	if (userboot_disk_maxunit > 0) {
176		dev.d_dev = &userboot_disk;
177		dev.d_type = dev.d_dev->dv_type;
178		dev.d_unit = 0;
179		dev.d_slice = 0;
180		dev.d_partition = 0;
181		/*
182		 * If we cannot auto-detect the partition type then
183		 * access the disk as a raw device.
184		 */
185		if (dev.d_dev->dv_open(NULL, &dev)) {
186			dev.d_slice = -1;
187			dev.d_partition = -1;
188		}
189	} else {
190		dev.d_dev = &host_dev;
191		dev.d_type = dev.d_dev->dv_type;
192		dev.d_unit = 0;
193	}
194
195	env_setenv("currdev", EV_VOLATILE, userboot_fmtdev(&dev),
196            userboot_setcurrdev, env_nounset);
197	env_setenv("loaddev", EV_VOLATILE, userboot_fmtdev(&dev),
198            env_noset, env_nounset);
199}
200
201#if defined(USERBOOT_ZFS_SUPPORT)
202static void
203init_zfs_bootenv(char *currdev)
204{
205	char *beroot;
206
207	if (strlen(currdev) == 0)
208		return;
209	if(strncmp(currdev, "zfs:", 4) != 0)
210		return;
211	/* Remove the trailing : */
212	currdev[strlen(currdev) - 1] = '\0';
213	setenv("zfs_be_active", currdev, 1);
214	setenv("zfs_be_currpage", "1", 1);
215	/* Do not overwrite if already set */
216	setenv("vfs.root.mountfrom", currdev, 0);
217	/* Forward past zfs: */
218	currdev = strchr(currdev, ':');
219	currdev++;
220	/* Remove the last element (current bootenv) */
221	beroot = strrchr(currdev, '/');
222	if (beroot != NULL)
223		beroot[0] = '\0';
224	beroot = currdev;
225	setenv("zfs_be_root", beroot, 1);
226}
227
228static void
229userboot_zfs_probe(void)
230{
231	char devname[32];
232	uint64_t pool_guid;
233	int unit;
234
235	/*
236	 * Open all the disks we can find and see if we can reconstruct
237	 * ZFS pools from them. Record if any were found.
238	 */
239	for (unit = 0; unit < userboot_disk_maxunit; unit++) {
240		sprintf(devname, "disk%d:", unit);
241		pool_guid = 0;
242		zfs_probe_dev(devname, &pool_guid);
243		if (pool_guid != 0)
244			userboot_zfs_found = 1;
245	}
246}
247
248COMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset",
249	    command_lszfs);
250
251static int
252command_lszfs(int argc, char *argv[])
253{
254	int err;
255
256	if (argc != 2) {
257		command_errmsg = "a single dataset must be supplied";
258		return (CMD_ERROR);
259	}
260
261	err = zfs_list(argv[1]);
262	if (err != 0) {
263		command_errmsg = strerror(err);
264		return (CMD_ERROR);
265	}
266	return (CMD_OK);
267}
268
269COMMAND_SET(reloadbe, "reloadbe", "refresh the list of ZFS Boot Environments",
270	    command_reloadbe);
271
272static int
273command_reloadbe(int argc, char *argv[])
274{
275	int err;
276	char *root;
277
278	if (argc > 2) {
279		command_errmsg = "wrong number of arguments";
280		return (CMD_ERROR);
281	}
282
283	if (argc == 2) {
284		err = zfs_bootenv(argv[1]);
285	} else {
286		root = getenv("zfs_be_root");
287		if (root == NULL) {
288			return (CMD_OK);
289		}
290		err = zfs_bootenv(root);
291	}
292
293	if (err != 0) {
294		command_errmsg = strerror(err);
295		return (CMD_ERROR);
296	}
297
298	return (CMD_OK);
299}
300#endif /* USERBOOT_ZFS_SUPPORT */
301
302COMMAND_SET(quit, "quit", "exit the loader", command_quit);
303
304static int
305command_quit(int argc, char *argv[])
306{
307
308	exit(USERBOOT_EXIT_QUIT);
309	return (CMD_OK);
310}
311
312COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
313
314static int
315command_reboot(int argc, char *argv[])
316{
317
318	exit(USERBOOT_EXIT_REBOOT);
319	return (CMD_OK);
320}
321