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