xref: /freebsd-13-stable/stand/kboot/main.c (revision 3d497e17ebd33fe0f58d773e35ab994d750258d6)
1 /*-
2  * Copyright (C) 2010-2014 Nathan Whitehorn
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 #include <stand.h>
28 #include <sys/param.h>
29 #include <sys/boot.h>
30 #include <fdt_platform.h>
31 
32 #include <machine/cpufunc.h>
33 #include <bootstrap.h>
34 #include "host_syscall.h"
35 #include "kboot.h"
36 
37 struct arch_switch	archsw;
38 extern void *_end;
39 
40 int kboot_getdev(void **vdev, const char *devspec, const char **path);
41 ssize_t kboot_copyin(const void *src, vm_offset_t dest, const size_t len);
42 ssize_t kboot_copyout(vm_offset_t src, void *dest, const size_t len);
43 ssize_t kboot_readin(readin_handle_t fd, vm_offset_t dest, const size_t len);
44 int kboot_autoload(void);
45 uint64_t kboot_loadaddr(u_int type, void *data, uint64_t addr);
46 static void kboot_kseg_get(int *nseg, void **ptr);
47 static void kboot_zfs_probe(void);
48 
49 extern int command_fdt_internal(int argc, char *argv[]);
50 
51 /*
52  * NB: getdev should likely be identical to this most places, except maybe
53  * we should move to storing the length of the platform devdesc.
54  */
55 int
kboot_getdev(void ** vdev,const char * devspec,const char ** path)56 kboot_getdev(void **vdev, const char *devspec, const char **path)
57 {
58 	int rv;
59 	struct devdesc **dev = (struct devdesc **)vdev;
60 
61 	/*
62 	 * If it looks like this is just a path and no device, go with the
63 	 * current device.
64 	 */
65 	if (devspec == NULL || strchr(devspec, ':') == NULL) {
66 		if (((rv = devparse(dev, getenv("currdev"), NULL)) == 0) &&
67 		    (path != NULL))
68 			*path = devspec;
69 		return (rv);
70 	}
71 
72 	/*
73 	 * Try to parse the device name off the beginning of the devspec
74 	 */
75 	return (devparse(dev, devspec, path));
76 }
77 
78 static int
parse_args(int argc,const char ** argv)79 parse_args(int argc, const char **argv)
80 {
81 	int howto = 0;
82 
83 	/*
84 	 * When run as init, sometimes argv[0] is a EFI-ESP path, other times
85 	 * it's the name of the init program, and sometimes it's a placeholder
86 	 * string, so we exclude it here. For the other args, look for DOS-like
87 	 * and Unix-like absolte paths and exclude parsing it if we find that,
88 	 * otherwise parse it as a command arg (so looking for '-X', 'foo' or
89 	 * 'foo=bar'). This is a little different than EFI where it argv[0]
90 	 * often times is the first argument passed in. There are cases when
91 	 * linux-booting via EFI that we have the EFI path we used to run
92 	 * bootXXX.efi as the arguments to init, so we need to exclude the paths
93 	 * there as well.
94 	 */
95 	for (int i = 1; i < argc; i++) {
96 		if (argv[i][0] != '\\' && argv[i][0] != '/') {
97 			howto |= boot_parse_arg(argv[i]);
98 		}
99 	}
100 
101 	return (howto);
102 }
103 
104 static vm_offset_t rsdp;
105 
106 static vm_offset_t
kboot_rsdp_from_efi(void)107 kboot_rsdp_from_efi(void)
108 {
109 	char buffer[512 + 1];
110 	char *walker, *ep;
111 
112 	if (!file2str("/sys/firmware/efi/systab", buffer, sizeof(buffer)))
113 		return (0);	/* Not an EFI system */
114 	ep = buffer + strlen(buffer);
115 	walker = buffer;
116 	while (walker < ep) {
117 		if (strncmp("ACPI20=", walker, 7) == 0)
118 			return((vm_offset_t)strtoull(walker + 7, NULL, 0));
119 		if (strncmp("ACPI=", walker, 5) == 0)
120 			return((vm_offset_t)strtoull(walker + 5, NULL, 0));
121 		walker += strcspn(walker, "\n");
122 	}
123 	return (0);
124 }
125 
126 static void
find_acpi(void)127 find_acpi(void)
128 {
129 	rsdp = kboot_rsdp_from_efi();
130 #if 0	/* maybe for amd64 */
131 	if (rsdp == 0)
132 		rsdp = find_rsdp_arch();
133 #endif
134 }
135 
136 vm_offset_t
acpi_rsdp(void)137 acpi_rsdp(void)
138 {
139 	return (rsdp);
140 }
141 
142 bool
has_acpi(void)143 has_acpi(void)
144 {
145 	return rsdp != 0;
146 }
147 
148 int
main(int argc,const char ** argv)149 main(int argc, const char **argv)
150 {
151 	void *heapbase;
152 	const size_t heapsize = 128*1024*1024;
153 	const char *bootdev;
154 
155 	archsw.arch_getdev = kboot_getdev;
156 	archsw.arch_copyin = kboot_copyin;
157 	archsw.arch_copyout = kboot_copyout;
158 	archsw.arch_readin = kboot_readin;
159 	archsw.arch_autoload = kboot_autoload;
160 	archsw.arch_loadaddr = kboot_loadaddr;
161 	archsw.arch_kexec_kseg_get = kboot_kseg_get;
162 	archsw.arch_zfs_probe = kboot_zfs_probe;
163 
164 	/* Give us a sane world if we're running as init */
165 	do_init();
166 
167 	/*
168 	 * Setup the heap 15MB should be plenty
169 	 */
170 	heapbase = host_getmem(heapsize);
171 	setheap(heapbase, heapbase + heapsize);
172 
173 	/* Parse the command line args -- ignoring for now the console selection */
174 	parse_args(argc, argv);
175 
176 	/*
177 	 * Set up console.
178 	 */
179 	cons_probe();
180 
181 	/* Initialize all the devices */
182 	devinit();
183 
184 	bootdev = getenv("bootdev");
185 	if (bootdev == NULL)
186 		bootdev="zfs:";
187 	hostfs_root = getenv("hostfs_root");
188 	if (hostfs_root == NULL)
189 		hostfs_root = "/";
190 #if defined(LOADER_ZFS_SUPPORT)
191 	if (strcmp(bootdev, "zfs:") == 0) {
192 		/*
193 		 * Pseudo device that says go find the right ZFS pool. This will be
194 		 * the first pool that we find that passes the sanity checks (eg looks
195 		 * like it might be vbootable) and sets currdev to the right thing based
196 		 * on active BEs, etc
197 		 */
198 		hostdisk_zfs_find_default();
199 	} else
200 #endif
201 	{
202 		/*
203 		 * Otherwise, honor what's on the command line. If we've been
204 		 * given a specific ZFS partition, then we'll honor it w/o BE
205 		 * processing that would otherwise pick a different snapshot to
206 		 * boot than the default one in the pool.
207 		 */
208 		set_currdev(bootdev);
209 	}
210 
211 	printf("Boot device: %s with hostfs_root %s\n", bootdev, hostfs_root);
212 
213 	printf("\n%s", bootprog_info);
214 
215 	setenv("LINES", "24", 1);
216 	setenv("usefdt", "1", 1);
217 
218 	/*
219 	 * Find acpi, if it exists
220 	 */
221 	find_acpi();
222 
223 	interact();			/* doesn't return */
224 
225 	return (0);
226 }
227 
228 void
exit(int code)229 exit(int code)
230 {
231 	host_exit(code);
232 	__unreachable();
233 }
234 
235 void
delay(int usecs)236 delay(int usecs)
237 {
238 	struct host_timeval tvi, tv;
239 	uint64_t ti, t;
240 	host_gettimeofday(&tvi, NULL);
241 	ti = tvi.tv_sec*1000000 + tvi.tv_usec;
242 	do {
243 		host_gettimeofday(&tv, NULL);
244 		t = tv.tv_sec*1000000 + tv.tv_usec;
245 	} while (t < ti + usecs);
246 }
247 
248 time_t
getsecs(void)249 getsecs(void)
250 {
251 	struct host_timeval tv;
252 	host_gettimeofday(&tv, NULL);
253 	return (tv.tv_sec);
254 }
255 
256 time_t
time(time_t * tloc)257 time(time_t *tloc)
258 {
259 	time_t rv;
260 
261 	rv = getsecs();
262 	if (tloc != NULL)
263 		*tloc = rv;
264 
265 	return (rv);
266 }
267 
268 struct host_kexec_segment loaded_segments[HOST_KEXEC_SEGMENT_MAX];
269 int nkexec_segments = 0;
270 
271 static ssize_t
get_phys_buffer(vm_offset_t dest,const size_t len,void ** buf)272 get_phys_buffer(vm_offset_t dest, const size_t len, void **buf)
273 {
274 	int i = 0;
275 	const size_t segsize = 8*1024*1024;
276 
277 	if (nkexec_segments == HOST_KEXEC_SEGMENT_MAX)
278 		panic("Tried to load too many kexec segments");
279 	for (i = 0; i < nkexec_segments; i++) {
280 		if (dest >= (vm_offset_t)loaded_segments[i].mem &&
281 		    dest < (vm_offset_t)loaded_segments[i].mem +
282 		    loaded_segments[i].memsz)
283 			goto out;
284 	}
285 
286 	loaded_segments[nkexec_segments].buf = host_getmem(segsize);
287 	loaded_segments[nkexec_segments].bufsz = segsize;
288 	loaded_segments[nkexec_segments].mem = (void *)rounddown2(dest,segsize);
289 	loaded_segments[nkexec_segments].memsz = segsize;
290 
291 	i = nkexec_segments;
292 	nkexec_segments++;
293 
294 out:
295 	*buf = loaded_segments[i].buf + (dest -
296 	    (vm_offset_t)loaded_segments[i].mem);
297 	return (min(len,loaded_segments[i].bufsz - (dest -
298 	    (vm_offset_t)loaded_segments[i].mem)));
299 }
300 
301 ssize_t
kboot_copyin(const void * src,vm_offset_t dest,const size_t len)302 kboot_copyin(const void *src, vm_offset_t dest, const size_t len)
303 {
304 	ssize_t segsize, remainder;
305 	void *destbuf;
306 
307 	remainder = len;
308 	do {
309 		segsize = get_phys_buffer(dest, remainder, &destbuf);
310 		bcopy(src, destbuf, segsize);
311 		remainder -= segsize;
312 		src += segsize;
313 		dest += segsize;
314 	} while (remainder > 0);
315 
316 	return (len);
317 }
318 
319 ssize_t
kboot_copyout(vm_offset_t src,void * dest,const size_t len)320 kboot_copyout(vm_offset_t src, void *dest, const size_t len)
321 {
322 	ssize_t segsize, remainder;
323 	void *srcbuf;
324 
325 	remainder = len;
326 	do {
327 		segsize = get_phys_buffer(src, remainder, &srcbuf);
328 		bcopy(srcbuf, dest, segsize);
329 		remainder -= segsize;
330 		src += segsize;
331 		dest += segsize;
332 	} while (remainder > 0);
333 
334 	return (len);
335 }
336 
337 ssize_t
kboot_readin(readin_handle_t fd,vm_offset_t dest,const size_t len)338 kboot_readin(readin_handle_t fd, vm_offset_t dest, const size_t len)
339 {
340 	void            *buf;
341 	size_t          resid, chunk, get;
342 	ssize_t         got;
343 	vm_offset_t     p;
344 
345 	p = dest;
346 
347 	chunk = min(PAGE_SIZE, len);
348 	buf = malloc(chunk);
349 	if (buf == NULL) {
350 		printf("kboot_readin: buf malloc failed\n");
351 		return (0);
352 	}
353 
354 	for (resid = len; resid > 0; resid -= got, p += got) {
355 		get = min(chunk, resid);
356 		got = VECTX_READ(fd, buf, get);
357 		if (got <= 0) {
358 			if (got < 0)
359 				printf("kboot_readin: read failed\n");
360 			break;
361 		}
362 
363 		kboot_copyin(buf, p, got);
364 	}
365 
366 	free (buf);
367 	return (len - resid);
368 }
369 
370 int
kboot_autoload(void)371 kboot_autoload(void)
372 {
373 
374 	return (0);
375 }
376 
377 uint64_t
kboot_loadaddr(u_int type,void * data,uint64_t addr)378 kboot_loadaddr(u_int type, void *data, uint64_t addr)
379 {
380 
381 	if (type == LOAD_ELF)
382 		addr = roundup(addr, PAGE_SIZE);
383 	else
384 		addr += kboot_get_phys_load_segment();
385 
386 	return (addr);
387 }
388 
389 static void
kboot_kseg_get(int * nseg,void ** ptr)390 kboot_kseg_get(int *nseg, void **ptr)
391 {
392 #if 0
393 	int a;
394 
395 	for (a = 0; a < nkexec_segments; a++) {
396 		printf("kseg_get: %jx %jx %jx %jx\n",
397 			(uintmax_t)loaded_segments[a].buf,
398 			(uintmax_t)loaded_segments[a].bufsz,
399 			(uintmax_t)loaded_segments[a].mem,
400 			(uintmax_t)loaded_segments[a].memsz);
401 	}
402 #endif
403 
404 	*nseg = nkexec_segments;
405 	*ptr = &loaded_segments[0];
406 }
407 
408 static void
kboot_zfs_probe(void)409 kboot_zfs_probe(void)
410 {
411 #if defined(LOADER_ZFS_SUPPORT)
412 	/*
413 	 * Open all the disks and partitions we can find to see if there are ZFS
414 	 * pools on them.
415 	 */
416 	hostdisk_zfs_probe();
417 #endif
418 }
419 
420 /*
421  * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
422  * and declaring it as extern is in contradiction with COMMAND_SET() macro
423  * (which uses static pointer), we're defining wrapper function, which
424  * calls the proper fdt handling routine.
425  */
426 static int
command_fdt(int argc,char * argv[])427 command_fdt(int argc, char *argv[])
428 {
429 
430 	return (command_fdt_internal(argc, argv));
431 }
432 
433 COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
434