xref: /freebsd/stand/userboot/userboot/main.c (revision 190cef3d)
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$");
30 
31 #include <stand.h>
32 #include <string.h>
33 #include <setjmp.h>
34 #include <sys/disk.h>
35 
36 #include "bootstrap.h"
37 #include "disk.h"
38 #include "libuserboot.h"
39 
40 #if defined(USERBOOT_ZFS_SUPPORT)
41 #include "libzfs.h"
42 
43 static void userboot_zfs_probe(void);
44 static int userboot_zfs_found;
45 #endif
46 
47 /* Minimum version required */
48 #define	USERBOOT_VERSION	USERBOOT_VERSION_3
49 
50 #define	LOADER_PATH		"/boot/loader"
51 #define	INTERP_MARKER		"$Interpreter:"
52 
53 #define	MALLOCSZ		(64*1024*1024)
54 
55 struct loader_callbacks *callbacks;
56 void *callbacks_arg;
57 
58 static jmp_buf jb;
59 
60 struct arch_switch archsw;	/* MI/MD interface boundary */
61 
62 static void	extract_currdev(void);
63 static void	check_interpreter(void);
64 
65 void
66 delay(int usec)
67 {
68 
69 	CALLBACK(delay, usec);
70 }
71 
72 void
73 exit(int v)
74 {
75 
76 	CALLBACK(exit, v);
77 	longjmp(jb, 1);
78 }
79 
80 static void
81 check_interpreter(void)
82 {
83 	struct stat st;
84 	size_t marklen, rdsize;
85 	const char *guest_interp, *my_interp;
86 	char *buf;
87 	int fd;
88 
89 	/*
90 	 * If we can't stat(2) or open(2) LOADER_PATH, then we'll fail by
91 	 * simply letting us roll on with whatever interpreter we were compiled
92 	 * with.  This is likely not going to be an issue in reality.
93 	 */
94 	buf =  NULL;
95 	if (stat(LOADER_PATH, &st) != 0)
96 		return;
97 	if ((fd = open(LOADER_PATH, O_RDONLY)) < 0)
98 		return;
99 
100 	rdsize = st.st_size;
101 	buf = malloc(rdsize);
102 	if (buf == NULL)
103 		goto out;
104 	if (read(fd, buf, rdsize) < rdsize)
105 		goto out;
106 
107 	marklen = strlen(INTERP_MARKER);
108 	my_interp = bootprog_interp + marklen;
109 
110 	/*
111 	 * Here we make the assumption that a loader binary without the
112 	 * interpreter marker is a 4th one.  All loader binaries going forward
113 	 * should have this properly specified, so our assumption should always
114 	 * be a good one.
115 	 */
116 	if ((guest_interp = memmem(buf, rdsize, INTERP_MARKER,
117 	    marklen)) != NULL)
118 		guest_interp += marklen;
119 	else
120 		guest_interp = "4th";
121 
122 	/*
123 	 * The guest interpreter may not have a version of loader that
124 	 * specifies the interpreter installed.  If that's the case, we'll
125 	 * assume it's legacy (4th) and request a swap to that if we're
126 	 * a Lua-userboot.
127 	 */
128 	if (strcmp(my_interp, guest_interp) != 0)
129 		CALLBACK(swap_interpreter, guest_interp);
130 out:
131 	free(buf);
132 	close(fd);
133 	return;
134 }
135 
136 void
137 loader_main(struct loader_callbacks *cb, void *arg, int version, int ndisks)
138 {
139 	static char mallocbuf[MALLOCSZ];
140 	char *var;
141 	int i;
142 
143 	if (version < USERBOOT_VERSION)
144 		abort();
145 
146 	callbacks = cb;
147 	callbacks_arg = arg;
148 	userboot_disk_maxunit = ndisks;
149 
150 	/*
151 	 * initialise the heap as early as possible.  Once this is done,
152 	 * alloc() is usable.
153 	 */
154 	setheap((void *)mallocbuf, (void *)(mallocbuf + sizeof(mallocbuf)));
155 
156 	/*
157 	 * Hook up the console
158 	 */
159 	cons_probe();
160 
161 	printf("\n%s", bootprog_info);
162 #if 0
163 	printf("Memory: %ld k\n", memsize() / 1024);
164 #endif
165 
166 	setenv("LINES", "24", 1);	/* optional */
167 
168 	/*
169 	 * Set custom environment variables
170 	 */
171 	i = 0;
172 	while (1) {
173 		var = CALLBACK(getenv, i++);
174 		if (var == NULL)
175 			break;
176 		putenv(var);
177 	}
178 
179 	archsw.arch_autoload = userboot_autoload;
180 	archsw.arch_getdev = userboot_getdev;
181 	archsw.arch_copyin = userboot_copyin;
182 	archsw.arch_copyout = userboot_copyout;
183 	archsw.arch_readin = userboot_readin;
184 #if defined(USERBOOT_ZFS_SUPPORT)
185 	archsw.arch_zfs_probe = userboot_zfs_probe;
186 #endif
187 
188 	/*
189 	 * Initialise the block cache. Set the upper limit.
190 	 */
191 	bcache_init(32768, 512);
192 	/*
193 	 * March through the device switch probing for things.
194 	 */
195 	for (i = 0; devsw[i] != NULL; i++)
196 		if (devsw[i]->dv_init != NULL)
197 			(devsw[i]->dv_init)();
198 
199 	extract_currdev();
200 
201 	/*
202 	 * Checking the interpreter isn't worth the overhead unless we
203 	 * actually have the swap_interpreter callback, so we actually version
204 	 * check here rather than later on.
205 	 */
206 	if (version >= USERBOOT_VERSION_5)
207 		check_interpreter();
208 
209 	if (setjmp(jb))
210 		return;
211 
212 	interact();			/* doesn't return */
213 
214 	exit(0);
215 }
216 
217 /*
218  * Set the 'current device' by (if possible) recovering the boot device as
219  * supplied by the initial bootstrap.
220  */
221 static void
222 extract_currdev(void)
223 {
224 	struct disk_devdesc dev;
225 	struct devdesc *dd;
226 #if defined(USERBOOT_ZFS_SUPPORT)
227 	struct zfs_devdesc zdev;
228 
229 	if (userboot_zfs_found) {
230 
231 		/* Leave the pool/root guid's unassigned */
232 		bzero(&zdev, sizeof(zdev));
233 		zdev.dd.d_dev = &zfs_dev;
234 
235 		init_zfs_bootenv(zfs_fmtdev(&zdev));
236 		dd = &zdev.dd;
237 	} else
238 #endif
239 
240 	if (userboot_disk_maxunit > 0) {
241 		dev.dd.d_dev = &userboot_disk;
242 		dev.dd.d_unit = 0;
243 		dev.d_slice = 0;
244 		dev.d_partition = 0;
245 		/*
246 		 * If we cannot auto-detect the partition type then
247 		 * access the disk as a raw device.
248 		 */
249 		if (dev.dd.d_dev->dv_open(NULL, &dev)) {
250 			dev.d_slice = -1;
251 			dev.d_partition = -1;
252 		}
253 		dd = &dev.dd;
254 	} else {
255 		dev.dd.d_dev = &host_dev;
256 		dev.dd.d_unit = 0;
257 		dd = &dev.dd;
258 	}
259 
260 	env_setenv("currdev", EV_VOLATILE, userboot_fmtdev(dd),
261 	    userboot_setcurrdev, env_nounset);
262 	env_setenv("loaddev", EV_VOLATILE, userboot_fmtdev(dd),
263 	    env_noset, env_nounset);
264 }
265 
266 #if defined(USERBOOT_ZFS_SUPPORT)
267 static void
268 userboot_zfs_probe(void)
269 {
270 	char devname[32];
271 	uint64_t pool_guid;
272 	int unit;
273 
274 	/*
275 	 * Open all the disks we can find and see if we can reconstruct
276 	 * ZFS pools from them. Record if any were found.
277 	 */
278 	for (unit = 0; unit < userboot_disk_maxunit; unit++) {
279 		sprintf(devname, "disk%d:", unit);
280 		pool_guid = 0;
281 		zfs_probe_dev(devname, &pool_guid);
282 		if (pool_guid != 0)
283 			userboot_zfs_found = 1;
284 	}
285 }
286 #endif
287 
288 COMMAND_SET(quit, "quit", "exit the loader", command_quit);
289 
290 static int
291 command_quit(int argc, char *argv[])
292 {
293 
294 	exit(USERBOOT_EXIT_QUIT);
295 	return (CMD_OK);
296 }
297 
298 COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
299 
300 static int
301 command_reboot(int argc, char *argv[])
302 {
303 
304 	exit(USERBOOT_EXIT_REBOOT);
305 	return (CMD_OK);
306 }
307