xref: /freebsd/stand/efi/loader/bootinfo.c (revision 3468ddce672350a6d974b4f0fdf3f4a56eaab0a0)
1 /*-
2  * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3  * Copyright (c) 2004, 2006 Marcel Moolenaar
4  * Copyright (c) 2014 The FreeBSD Foundation
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <stand.h>
33 #include <string.h>
34 #include <sys/param.h>
35 #include <sys/linker.h>
36 #include <sys/reboot.h>
37 #include <sys/boot.h>
38 #include <machine/cpufunc.h>
39 #include <machine/elf.h>
40 #include <machine/metadata.h>
41 #include <machine/psl.h>
42 
43 #include <efi.h>
44 #include <efilib.h>
45 
46 #include "bootstrap.h"
47 #include "loader_efi.h"
48 
49 #if defined(__amd64__)
50 #include <machine/specialreg.h>
51 #endif
52 
53 #include "framebuffer.h"
54 
55 #if defined(LOADER_FDT_SUPPORT)
56 #include <fdt_platform.h>
57 #endif
58 
59 #ifdef LOADER_GELI_SUPPORT
60 #include "geliboot.h"
61 #endif
62 
63 int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp);
64 
65 extern EFI_SYSTEM_TABLE	*ST;
66 
67 static int
68 bi_getboothowto(char *kargs)
69 {
70 	const char *sw, *tmp;
71 	char *opts;
72 	char *console;
73 	int howto, speed, port;
74 	char buf[50];
75 
76 	howto = boot_parse_cmdline(kargs);
77 	howto |= boot_env_to_howto();
78 
79 	console = getenv("console");
80 	if (console != NULL) {
81 		if (strcmp(console, "comconsole") == 0)
82 			howto |= RB_SERIAL;
83 		if (strcmp(console, "nullconsole") == 0)
84 			howto |= RB_MUTE;
85 		if (strcmp(console, "efi") == 0) {
86 			/*
87 			 * If we found a com port and com speed, we need to tell
88 			 * the kernel where the serial port is, and how
89 			 * fast. Ideally, we'd get the port from ACPI, but that
90 			 * isn't running in the loader. Do the next best thing
91 			 * by allowing it to be set by a loader.conf variable,
92 			 * either a EFI specific one, or the compatible
93 			 * comconsole_port if not. PCI support is needed, but
94 			 * for that we'd ideally refactor the
95 			 * libi386/comconsole.c code to have identical behavior.
96 			 */
97 			tmp = getenv("efi_com_speed");
98 			if (tmp != NULL) {
99 				speed = strtol(tmp, NULL, 0);
100 				tmp = getenv("efi_com_port");
101 				if (tmp == NULL)
102 					tmp = getenv("comconsole_port");
103 				/* XXX fallback to EFI variable set in rc.d? */
104 				if (tmp != NULL)
105 					port = strtol(tmp, NULL, 0);
106 				else
107 					port = 0x3f8;
108 				snprintf(buf, sizeof(buf), "io:%d,br:%d", port,
109 				    speed);
110 				env_setenv("hw.uart.console", EV_VOLATILE, buf,
111 				    NULL, NULL);
112 			}
113 		}
114 	}
115 
116 	return (howto);
117 }
118 
119 /*
120  * Copy the environment into the load area starting at (addr).
121  * Each variable is formatted as <name>=<value>, with a single nul
122  * separating each variable, and a double nul terminating the environment.
123  */
124 static vm_offset_t
125 bi_copyenv(vm_offset_t start)
126 {
127 	struct env_var *ep;
128 	vm_offset_t addr, last;
129 	size_t len;
130 
131 	addr = last = start;
132 
133 	/* Traverse the environment. */
134 	for (ep = environ; ep != NULL; ep = ep->ev_next) {
135 		len = strlen(ep->ev_name);
136 		if ((size_t)archsw.arch_copyin(ep->ev_name, addr, len) != len)
137 			break;
138 		addr += len;
139 		if (archsw.arch_copyin("=", addr, 1) != 1)
140 			break;
141 		addr++;
142 		if (ep->ev_value != NULL) {
143 			len = strlen(ep->ev_value);
144 			if ((size_t)archsw.arch_copyin(ep->ev_value, addr, len) != len)
145 				break;
146 			addr += len;
147 		}
148 		if (archsw.arch_copyin("", addr, 1) != 1)
149 			break;
150 		last = ++addr;
151 	}
152 
153 	if (archsw.arch_copyin("", last++, 1) != 1)
154 		last = start;
155 	return(last);
156 }
157 
158 /*
159  * Copy module-related data into the load area, where it can be
160  * used as a directory for loaded modules.
161  *
162  * Module data is presented in a self-describing format.  Each datum
163  * is preceded by a 32-bit identifier and a 32-bit size field.
164  *
165  * Currently, the following data are saved:
166  *
167  * MOD_NAME	(variable)		module name (string)
168  * MOD_TYPE	(variable)		module type (string)
169  * MOD_ARGS	(variable)		module parameters (string)
170  * MOD_ADDR	sizeof(vm_offset_t)	module load address
171  * MOD_SIZE	sizeof(size_t)		module size
172  * MOD_METADATA	(variable)		type-specific metadata
173  */
174 #define	COPY32(v, a, c) {					\
175 	uint32_t x = (v);					\
176 	if (c)							\
177 		archsw.arch_copyin(&x, a, sizeof(x));		\
178 	a += sizeof(x);						\
179 }
180 
181 #define	MOD_STR(t, a, s, c) {					\
182 	COPY32(t, a, c);					\
183 	COPY32(strlen(s) + 1, a, c);				\
184 	if (c)							\
185 		archsw.arch_copyin(s, a, strlen(s) + 1);	\
186 	a += roundup(strlen(s) + 1, sizeof(u_long));		\
187 }
188 
189 #define	MOD_NAME(a, s, c)	MOD_STR(MODINFO_NAME, a, s, c)
190 #define	MOD_TYPE(a, s, c)	MOD_STR(MODINFO_TYPE, a, s, c)
191 #define	MOD_ARGS(a, s, c)	MOD_STR(MODINFO_ARGS, a, s, c)
192 
193 #define	MOD_VAR(t, a, s, c) {					\
194 	COPY32(t, a, c);					\
195 	COPY32(sizeof(s), a, c);				\
196 	if (c)							\
197 		archsw.arch_copyin(&s, a, sizeof(s));		\
198 	a += roundup(sizeof(s), sizeof(u_long));		\
199 }
200 
201 #define	MOD_ADDR(a, s, c)	MOD_VAR(MODINFO_ADDR, a, s, c)
202 #define	MOD_SIZE(a, s, c)	MOD_VAR(MODINFO_SIZE, a, s, c)
203 
204 #define	MOD_METADATA(a, mm, c) {				\
205 	COPY32(MODINFO_METADATA | mm->md_type, a, c);		\
206 	COPY32(mm->md_size, a, c);				\
207 	if (c)							\
208 		archsw.arch_copyin(mm->md_data, a, mm->md_size);	\
209 	a += roundup(mm->md_size, sizeof(u_long));		\
210 }
211 
212 #define	MOD_END(a, c) {						\
213 	COPY32(MODINFO_END, a, c);				\
214 	COPY32(0, a, c);					\
215 }
216 
217 static vm_offset_t
218 bi_copymodules(vm_offset_t addr)
219 {
220 	struct preloaded_file *fp;
221 	struct file_metadata *md;
222 	int c;
223 	uint64_t v;
224 
225 	c = addr != 0;
226 	/* Start with the first module on the list, should be the kernel. */
227 	for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
228 		MOD_NAME(addr, fp->f_name, c); /* This must come first. */
229 		MOD_TYPE(addr, fp->f_type, c);
230 		if (fp->f_args)
231 			MOD_ARGS(addr, fp->f_args, c);
232 		v = fp->f_addr;
233 #if defined(__arm__)
234 		v -= __elfN(relocation_offset);
235 #endif
236 		MOD_ADDR(addr, v, c);
237 		v = fp->f_size;
238 		MOD_SIZE(addr, v, c);
239 		for (md = fp->f_metadata; md != NULL; md = md->md_next)
240 			if (!(md->md_type & MODINFOMD_NOCOPY))
241 				MOD_METADATA(addr, md, c);
242 	}
243 	MOD_END(addr, c);
244 	return(addr);
245 }
246 
247 static EFI_STATUS
248 efi_do_vmap(EFI_MEMORY_DESCRIPTOR *mm, UINTN sz, UINTN mmsz, UINT32 mmver)
249 {
250 	EFI_MEMORY_DESCRIPTOR *desc, *viter, *vmap;
251 	EFI_STATUS ret;
252 	int curr, ndesc, nset;
253 
254 	nset = 0;
255 	desc = mm;
256 	ndesc = sz / mmsz;
257 	vmap = malloc(sz);
258 	if (vmap == NULL)
259 		/* This isn't really an EFI error case, but pretend it is */
260 		return (EFI_OUT_OF_RESOURCES);
261 	viter = vmap;
262 	for (curr = 0; curr < ndesc;
263 	    curr++, desc = NextMemoryDescriptor(desc, mmsz)) {
264 		if ((desc->Attribute & EFI_MEMORY_RUNTIME) != 0) {
265 			++nset;
266 			desc->VirtualStart = desc->PhysicalStart;
267 			*viter = *desc;
268 			viter = NextMemoryDescriptor(viter, mmsz);
269 		}
270 	}
271 	ret = RS->SetVirtualAddressMap(nset * mmsz, mmsz, mmver, vmap);
272 	free(vmap);
273 	return (ret);
274 }
275 
276 static int
277 bi_load_efi_data(struct preloaded_file *kfp)
278 {
279 	EFI_MEMORY_DESCRIPTOR *mm;
280 	EFI_PHYSICAL_ADDRESS addr;
281 	EFI_STATUS status;
282 	const char *efi_novmap;
283 	size_t efisz;
284 	UINTN efi_mapkey;
285 	UINTN mmsz, pages, retry, sz;
286 	UINT32 mmver;
287 	struct efi_map_header *efihdr;
288 	bool do_vmap;
289 
290 #if defined(__amd64__) || defined(__aarch64__)
291 	struct efi_fb efifb;
292 
293 	if (efi_find_framebuffer(&efifb) == 0) {
294 		printf("EFI framebuffer information:\n");
295 		printf("addr, size     0x%jx, 0x%jx\n", efifb.fb_addr,
296 		    efifb.fb_size);
297 		printf("dimensions     %d x %d\n", efifb.fb_width,
298 		    efifb.fb_height);
299 		printf("stride         %d\n", efifb.fb_stride);
300 		printf("masks          0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
301 		    efifb.fb_mask_red, efifb.fb_mask_green, efifb.fb_mask_blue,
302 		    efifb.fb_mask_reserved);
303 
304 		file_addmetadata(kfp, MODINFOMD_EFI_FB, sizeof(efifb), &efifb);
305 	}
306 #endif
307 
308 	do_vmap = true;
309 	efi_novmap = getenv("efi_disable_vmap");
310 	if (efi_novmap != NULL)
311 		do_vmap = strcasecmp(efi_novmap, "YES") != 0;
312 
313 	efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf;
314 
315 	/*
316 	 * Assgin size of EFI_MEMORY_DESCRIPTOR to keep compatible with
317 	 * u-boot which doesn't fill this value when buffer for memory
318 	 * descriptors is too small (eg. 0 to obtain memory map size)
319 	 */
320 	mmsz = sizeof(EFI_MEMORY_DESCRIPTOR);
321 
322 	/*
323 	 * It is possible that the first call to ExitBootServices may change
324 	 * the map key. Fetch a new map key and retry ExitBootServices in that
325 	 * case.
326 	 */
327 	for (retry = 2; retry > 0; retry--) {
328 		/*
329 		 * Allocate enough pages to hold the bootinfo block and the
330 		 * memory map EFI will return to us. The memory map has an
331 		 * unknown size, so we have to determine that first. Note that
332 		 * the AllocatePages call can itself modify the memory map, so
333 		 * we have to take that into account as well. The changes to
334 		 * the memory map are caused by splitting a range of free
335 		 * memory into two (AFAICT), so that one is marked as being
336 		 * loader data.
337 		 */
338 		sz = 0;
339 		BS->GetMemoryMap(&sz, NULL, &efi_mapkey, &mmsz, &mmver);
340 		sz += mmsz;
341 		sz = (sz + 0xf) & ~0xf;
342 		pages = EFI_SIZE_TO_PAGES(sz + efisz);
343 		status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
344 		     pages, &addr);
345 		if (EFI_ERROR(status)) {
346 			printf("%s: AllocatePages error %lu\n", __func__,
347 			    EFI_ERROR_CODE(status));
348 			return (ENOMEM);
349 		}
350 
351 		/*
352 		 * Read the memory map and stash it after bootinfo. Align the
353 		 * memory map on a 16-byte boundary (the bootinfo block is page
354 		 * aligned).
355 		 */
356 		efihdr = (struct efi_map_header *)addr;
357 		mm = (void *)((uint8_t *)efihdr + efisz);
358 		sz = (EFI_PAGE_SIZE * pages) - efisz;
359 
360 		status = BS->GetMemoryMap(&sz, mm, &efi_mapkey, &mmsz, &mmver);
361 		if (EFI_ERROR(status)) {
362 			printf("%s: GetMemoryMap error %lu\n", __func__,
363 			    EFI_ERROR_CODE(status));
364 			return (EINVAL);
365 		}
366 		status = BS->ExitBootServices(IH, efi_mapkey);
367 		if (EFI_ERROR(status) == 0) {
368 			/*
369 			 * This may be disabled by setting efi_disable_vmap in
370 			 * loader.conf(5). By default we will setup the virtual
371 			 * map entries.
372 			 */
373 			if (do_vmap)
374 				efi_do_vmap(mm, sz, mmsz, mmver);
375 			efihdr->memory_size = sz;
376 			efihdr->descriptor_size = mmsz;
377 			efihdr->descriptor_version = mmver;
378 			file_addmetadata(kfp, MODINFOMD_EFI_MAP, efisz + sz,
379 			    efihdr);
380 			return (0);
381 		}
382 		BS->FreePages(addr, pages);
383 	}
384 	printf("ExitBootServices error %lu\n", EFI_ERROR_CODE(status));
385 	return (EINVAL);
386 }
387 
388 /*
389  * Load the information expected by an amd64 kernel.
390  *
391  * - The 'boothowto' argument is constructed.
392  * - The 'bootdev' argument is constructed.
393  * - The 'bootinfo' struct is constructed, and copied into the kernel space.
394  * - The kernel environment is copied into kernel space.
395  * - Module metadata are formatted and placed in kernel space.
396  */
397 int
398 bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp)
399 {
400 	struct preloaded_file *xp, *kfp;
401 	struct devdesc *rootdev;
402 	struct file_metadata *md;
403 	vm_offset_t addr;
404 	uint64_t kernend;
405 	uint64_t envp;
406 	vm_offset_t size;
407 	char *rootdevname;
408 	int howto;
409 #if defined(LOADER_FDT_SUPPORT)
410 	vm_offset_t dtbp;
411 	int dtb_size;
412 #endif
413 #if defined(__arm__)
414 	vm_offset_t vaddr;
415 	size_t i;
416 	/*
417 	 * These metadata addreses must be converted for kernel after
418 	 * relocation.
419 	 */
420 	uint32_t		mdt[] = {
421 	    MODINFOMD_SSYM, MODINFOMD_ESYM, MODINFOMD_KERNEND,
422 	    MODINFOMD_ENVP,
423 #if defined(LOADER_FDT_SUPPORT)
424 	    MODINFOMD_DTBP
425 #endif
426 	};
427 #endif
428 
429 	howto = bi_getboothowto(args);
430 
431 	/*
432 	 * Allow the environment variable 'rootdev' to override the supplied
433 	 * device. This should perhaps go to MI code and/or have $rootdev
434 	 * tested/set by MI code before launching the kernel.
435 	 */
436 	rootdevname = getenv("rootdev");
437 	archsw.arch_getdev((void**)(&rootdev), rootdevname, NULL);
438 	if (rootdev == NULL) {
439 		printf("Can't determine root device.\n");
440 		return(EINVAL);
441 	}
442 
443 	/* Try reading the /etc/fstab file to select the root device */
444 	getrootmount(efi_fmtdev((void *)rootdev));
445 
446 	addr = 0;
447 	for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
448 		if (addr < (xp->f_addr + xp->f_size))
449 			addr = xp->f_addr + xp->f_size;
450 	}
451 
452 	/* Pad to a page boundary. */
453 	addr = roundup(addr, PAGE_SIZE);
454 
455 	/* Copy our environment. */
456 	envp = addr;
457 	addr = bi_copyenv(addr);
458 
459 	/* Pad to a page boundary. */
460 	addr = roundup(addr, PAGE_SIZE);
461 
462 #if defined(LOADER_FDT_SUPPORT)
463 	/* Handle device tree blob */
464 	dtbp = addr;
465 	dtb_size = fdt_copy(addr);
466 
467 	/* Pad to a page boundary */
468 	if (dtb_size)
469 		addr += roundup(dtb_size, PAGE_SIZE);
470 #endif
471 
472 	kfp = file_findfile(NULL, "elf kernel");
473 	if (kfp == NULL)
474 		kfp = file_findfile(NULL, "elf64 kernel");
475 	if (kfp == NULL)
476 		panic("can't find kernel file");
477 	kernend = 0;	/* fill it in later */
478 	file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
479 	file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
480 #if defined(LOADER_FDT_SUPPORT)
481 	if (dtb_size)
482 		file_addmetadata(kfp, MODINFOMD_DTBP, sizeof dtbp, &dtbp);
483 	else
484 		printf("WARNING! Trying to fire up the kernel, but no "
485 		    "device tree blob found!\n");
486 #endif
487 	file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
488 	file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof ST, &ST);
489 #ifdef LOADER_GELI_SUPPORT
490 	geli_export_key_metadata(kfp);
491 #endif
492 	bi_load_efi_data(kfp);
493 
494 	/* Figure out the size and location of the metadata. */
495 	*modulep = addr;
496 	size = bi_copymodules(0);
497 	kernend = roundup(addr + size, PAGE_SIZE);
498 	*kernendp = kernend;
499 
500 	/* patch MODINFOMD_KERNEND */
501 	md = file_findmetadata(kfp, MODINFOMD_KERNEND);
502 	bcopy(&kernend, md->md_data, sizeof kernend);
503 
504 #if defined(__arm__)
505 	*modulep -= __elfN(relocation_offset);
506 
507 	/* Do relocation fixup on metadata of each module. */
508 	for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
509 		for (i = 0; i < nitems(mdt); i++) {
510 			md = file_findmetadata(xp, mdt[i]);
511 			if (md) {
512 				bcopy(md->md_data, &vaddr, sizeof vaddr);
513 				vaddr -= __elfN(relocation_offset);
514 				bcopy(&vaddr, md->md_data, sizeof vaddr);
515 			}
516 		}
517 	}
518 #endif
519 
520 	/* Copy module list and metadata. */
521 	(void)bi_copymodules(addr);
522 
523 	return (0);
524 }
525