bootinfo.c revision 344378
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: stable/11/stand/efi/loader/bootinfo.c 344378 2019-02-20 19:19:24Z kevans $");
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
59int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp);
60
61extern EFI_SYSTEM_TABLE	*ST;
62
63static int
64bi_getboothowto(char *kargs)
65{
66	const char *sw;
67	char *opts;
68	char *console;
69	int howto;
70
71	howto = boot_parse_cmdline(kargs);
72	howto |= boot_env_to_howto();
73
74	console = getenv("console");
75	if (console != NULL) {
76		if (strcmp(console, "comconsole") == 0)
77			howto |= RB_SERIAL;
78		if (strcmp(console, "nullconsole") == 0)
79			howto |= RB_MUTE;
80	}
81
82	return (howto);
83}
84
85/*
86 * Copy the environment into the load area starting at (addr).
87 * Each variable is formatted as <name>=<value>, with a single nul
88 * separating each variable, and a double nul terminating the environment.
89 */
90static vm_offset_t
91bi_copyenv(vm_offset_t start)
92{
93	struct env_var *ep;
94	vm_offset_t addr, last;
95	size_t len;
96
97	addr = last = start;
98
99	/* Traverse the environment. */
100	for (ep = environ; ep != NULL; ep = ep->ev_next) {
101		len = strlen(ep->ev_name);
102		if ((size_t)archsw.arch_copyin(ep->ev_name, addr, len) != len)
103			break;
104		addr += len;
105		if (archsw.arch_copyin("=", addr, 1) != 1)
106			break;
107		addr++;
108		if (ep->ev_value != NULL) {
109			len = strlen(ep->ev_value);
110			if ((size_t)archsw.arch_copyin(ep->ev_value, addr, len) != len)
111				break;
112			addr += len;
113		}
114		if (archsw.arch_copyin("", addr, 1) != 1)
115			break;
116		last = ++addr;
117	}
118
119	if (archsw.arch_copyin("", last++, 1) != 1)
120		last = start;
121	return(last);
122}
123
124/*
125 * Copy module-related data into the load area, where it can be
126 * used as a directory for loaded modules.
127 *
128 * Module data is presented in a self-describing format.  Each datum
129 * is preceded by a 32-bit identifier and a 32-bit size field.
130 *
131 * Currently, the following data are saved:
132 *
133 * MOD_NAME	(variable)		module name (string)
134 * MOD_TYPE	(variable)		module type (string)
135 * MOD_ARGS	(variable)		module parameters (string)
136 * MOD_ADDR	sizeof(vm_offset_t)	module load address
137 * MOD_SIZE	sizeof(size_t)		module size
138 * MOD_METADATA	(variable)		type-specific metadata
139 */
140#define	COPY32(v, a, c) {					\
141	uint32_t x = (v);					\
142	if (c)							\
143		archsw.arch_copyin(&x, a, sizeof(x));		\
144	a += sizeof(x);						\
145}
146
147#define	MOD_STR(t, a, s, c) {					\
148	COPY32(t, a, c);					\
149	COPY32(strlen(s) + 1, a, c);				\
150	if (c)							\
151		archsw.arch_copyin(s, a, strlen(s) + 1);	\
152	a += roundup(strlen(s) + 1, sizeof(u_long));		\
153}
154
155#define	MOD_NAME(a, s, c)	MOD_STR(MODINFO_NAME, a, s, c)
156#define	MOD_TYPE(a, s, c)	MOD_STR(MODINFO_TYPE, a, s, c)
157#define	MOD_ARGS(a, s, c)	MOD_STR(MODINFO_ARGS, a, s, c)
158
159#define	MOD_VAR(t, a, s, c) {					\
160	COPY32(t, a, c);					\
161	COPY32(sizeof(s), a, c);				\
162	if (c)							\
163		archsw.arch_copyin(&s, a, sizeof(s));		\
164	a += roundup(sizeof(s), sizeof(u_long));		\
165}
166
167#define	MOD_ADDR(a, s, c)	MOD_VAR(MODINFO_ADDR, a, s, c)
168#define	MOD_SIZE(a, s, c)	MOD_VAR(MODINFO_SIZE, a, s, c)
169
170#define	MOD_METADATA(a, mm, c) {				\
171	COPY32(MODINFO_METADATA | mm->md_type, a, c);		\
172	COPY32(mm->md_size, a, c);				\
173	if (c)							\
174		archsw.arch_copyin(mm->md_data, a, mm->md_size);	\
175	a += roundup(mm->md_size, sizeof(u_long));		\
176}
177
178#define	MOD_END(a, c) {						\
179	COPY32(MODINFO_END, a, c);				\
180	COPY32(0, a, c);					\
181}
182
183static vm_offset_t
184bi_copymodules(vm_offset_t addr)
185{
186	struct preloaded_file *fp;
187	struct file_metadata *md;
188	int c;
189	uint64_t v;
190
191	c = addr != 0;
192	/* Start with the first module on the list, should be the kernel. */
193	for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
194		MOD_NAME(addr, fp->f_name, c); /* This must come first. */
195		MOD_TYPE(addr, fp->f_type, c);
196		if (fp->f_args)
197			MOD_ARGS(addr, fp->f_args, c);
198		v = fp->f_addr;
199#if defined(__arm__)
200		v -= __elfN(relocation_offset);
201#endif
202		MOD_ADDR(addr, v, c);
203		v = fp->f_size;
204		MOD_SIZE(addr, v, c);
205		for (md = fp->f_metadata; md != NULL; md = md->md_next)
206			if (!(md->md_type & MODINFOMD_NOCOPY))
207				MOD_METADATA(addr, md, c);
208	}
209	MOD_END(addr, c);
210	return(addr);
211}
212
213static EFI_STATUS
214efi_do_vmap(EFI_MEMORY_DESCRIPTOR *mm, UINTN sz, UINTN mmsz, UINT32 mmver)
215{
216	EFI_MEMORY_DESCRIPTOR *desc, *viter, *vmap;
217	EFI_STATUS ret;
218	int curr, ndesc, nset;
219
220	nset = 0;
221	desc = mm;
222	ndesc = sz / mmsz;
223	vmap = malloc(sz);
224	if (vmap == NULL)
225		/* This isn't really an EFI error case, but pretend it is */
226		return (EFI_OUT_OF_RESOURCES);
227	viter = vmap;
228	for (curr = 0; curr < ndesc;
229	    curr++, desc = NextMemoryDescriptor(desc, mmsz)) {
230		if ((desc->Attribute & EFI_MEMORY_RUNTIME) != 0) {
231			++nset;
232			desc->VirtualStart = desc->PhysicalStart;
233			*viter = *desc;
234			viter = NextMemoryDescriptor(viter, mmsz);
235		}
236	}
237	ret = RS->SetVirtualAddressMap(nset * mmsz, mmsz, mmver, vmap);
238	free(vmap);
239	return (ret);
240}
241
242static int
243bi_load_efi_data(struct preloaded_file *kfp)
244{
245	EFI_MEMORY_DESCRIPTOR *mm;
246	EFI_PHYSICAL_ADDRESS addr;
247	EFI_STATUS status;
248	const char *efi_novmap;
249	size_t efisz;
250	UINTN efi_mapkey;
251	UINTN mmsz, pages, retry, sz;
252	UINT32 mmver;
253	struct efi_map_header *efihdr;
254	bool do_vmap;
255
256#if defined(__amd64__) || defined(__aarch64__)
257	struct efi_fb efifb;
258
259	if (efi_find_framebuffer(&efifb) == 0) {
260		printf("EFI framebuffer information:\n");
261		printf("addr, size     0x%jx, 0x%jx\n", efifb.fb_addr,
262		    efifb.fb_size);
263		printf("dimensions     %d x %d\n", efifb.fb_width,
264		    efifb.fb_height);
265		printf("stride         %d\n", efifb.fb_stride);
266		printf("masks          0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
267		    efifb.fb_mask_red, efifb.fb_mask_green, efifb.fb_mask_blue,
268		    efifb.fb_mask_reserved);
269
270		file_addmetadata(kfp, MODINFOMD_EFI_FB, sizeof(efifb), &efifb);
271	}
272#endif
273
274	do_vmap = true;
275	efi_novmap = getenv("efi_disable_vmap");
276	if (efi_novmap != NULL)
277		do_vmap = strcasecmp(efi_novmap, "YES") != 0;
278
279	efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf;
280
281	/*
282	 * Assgin size of EFI_MEMORY_DESCRIPTOR to keep compatible with
283	 * u-boot which doesn't fill this value when buffer for memory
284	 * descriptors is too small (eg. 0 to obtain memory map size)
285	 */
286	mmsz = sizeof(EFI_MEMORY_DESCRIPTOR);
287
288	/*
289	 * It is possible that the first call to ExitBootServices may change
290	 * the map key. Fetch a new map key and retry ExitBootServices in that
291	 * case.
292	 */
293	for (retry = 2; retry > 0; retry--) {
294		/*
295		 * Allocate enough pages to hold the bootinfo block and the
296		 * memory map EFI will return to us. The memory map has an
297		 * unknown size, so we have to determine that first. Note that
298		 * the AllocatePages call can itself modify the memory map, so
299		 * we have to take that into account as well. The changes to
300		 * the memory map are caused by splitting a range of free
301		 * memory into two (AFAICT), so that one is marked as being
302		 * loader data.
303		 */
304		sz = 0;
305		BS->GetMemoryMap(&sz, NULL, &efi_mapkey, &mmsz, &mmver);
306		sz += mmsz;
307		sz = (sz + 0xf) & ~0xf;
308		pages = EFI_SIZE_TO_PAGES(sz + efisz);
309		status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
310		     pages, &addr);
311		if (EFI_ERROR(status)) {
312			printf("%s: AllocatePages error %lu\n", __func__,
313			    EFI_ERROR_CODE(status));
314			return (ENOMEM);
315		}
316
317		/*
318		 * Read the memory map and stash it after bootinfo. Align the
319		 * memory map on a 16-byte boundary (the bootinfo block is page
320		 * aligned).
321		 */
322		efihdr = (struct efi_map_header *)(uintptr_t)addr;
323		mm = (void *)((uint8_t *)efihdr + efisz);
324		sz = (EFI_PAGE_SIZE * pages) - efisz;
325
326		status = BS->GetMemoryMap(&sz, mm, &efi_mapkey, &mmsz, &mmver);
327		if (EFI_ERROR(status)) {
328			printf("%s: GetMemoryMap error %lu\n", __func__,
329			    EFI_ERROR_CODE(status));
330			return (EINVAL);
331		}
332		status = BS->ExitBootServices(IH, efi_mapkey);
333		if (EFI_ERROR(status) == 0) {
334			/*
335			 * This may be disabled by setting efi_disable_vmap in
336			 * loader.conf(5). By default we will setup the virtual
337			 * map entries.
338			 */
339			if (do_vmap)
340				efi_do_vmap(mm, sz, mmsz, mmver);
341			efihdr->memory_size = sz;
342			efihdr->descriptor_size = mmsz;
343			efihdr->descriptor_version = mmver;
344			file_addmetadata(kfp, MODINFOMD_EFI_MAP, efisz + sz,
345			    efihdr);
346			return (0);
347		}
348		BS->FreePages(addr, pages);
349	}
350	printf("ExitBootServices error %lu\n", EFI_ERROR_CODE(status));
351	return (EINVAL);
352}
353
354/*
355 * Load the information expected by an amd64 kernel.
356 *
357 * - The 'boothowto' argument is constructed.
358 * - The 'bootdev' argument is constructed.
359 * - The 'bootinfo' struct is constructed, and copied into the kernel space.
360 * - The kernel environment is copied into kernel space.
361 * - Module metadata are formatted and placed in kernel space.
362 */
363int
364bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp)
365{
366	struct preloaded_file *xp, *kfp;
367	struct devdesc *rootdev;
368	struct file_metadata *md;
369	vm_offset_t addr;
370	uint64_t kernend;
371	uint64_t envp;
372	vm_offset_t size;
373	char *rootdevname;
374	int howto;
375#if defined(LOADER_FDT_SUPPORT)
376	vm_offset_t dtbp;
377	int dtb_size;
378#endif
379#if defined(__arm__)
380	vm_offset_t vaddr;
381	size_t i;
382	/*
383	 * These metadata addreses must be converted for kernel after
384	 * relocation.
385	 */
386	uint32_t		mdt[] = {
387	    MODINFOMD_SSYM, MODINFOMD_ESYM, MODINFOMD_KERNEND,
388	    MODINFOMD_ENVP,
389#if defined(LOADER_FDT_SUPPORT)
390	    MODINFOMD_DTBP
391#endif
392	};
393#endif
394
395	howto = bi_getboothowto(args);
396
397	/*
398	 * Allow the environment variable 'rootdev' to override the supplied
399	 * device. This should perhaps go to MI code and/or have $rootdev
400	 * tested/set by MI code before launching the kernel.
401	 */
402	rootdevname = getenv("rootdev");
403	archsw.arch_getdev((void**)(&rootdev), rootdevname, NULL);
404	if (rootdev == NULL) {
405		printf("Can't determine root device.\n");
406		return(EINVAL);
407	}
408
409	/* Try reading the /etc/fstab file to select the root device */
410	getrootmount(efi_fmtdev((void *)rootdev));
411
412	addr = 0;
413	for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
414		if (addr < (xp->f_addr + xp->f_size))
415			addr = xp->f_addr + xp->f_size;
416	}
417
418	/* Pad to a page boundary. */
419	addr = roundup(addr, PAGE_SIZE);
420
421	/* Copy our environment. */
422	envp = addr;
423	addr = bi_copyenv(addr);
424
425	/* Pad to a page boundary. */
426	addr = roundup(addr, PAGE_SIZE);
427
428#if defined(LOADER_FDT_SUPPORT)
429	/* Handle device tree blob */
430	dtbp = addr;
431	dtb_size = fdt_copy(addr);
432
433	/* Pad to a page boundary */
434	if (dtb_size)
435		addr += roundup(dtb_size, PAGE_SIZE);
436#endif
437
438	kfp = file_findfile(NULL, "elf kernel");
439	if (kfp == NULL)
440		kfp = file_findfile(NULL, "elf64 kernel");
441	if (kfp == NULL)
442		panic("can't find kernel file");
443	kernend = 0;	/* fill it in later */
444	file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
445	file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
446#if defined(LOADER_FDT_SUPPORT)
447	if (dtb_size)
448		file_addmetadata(kfp, MODINFOMD_DTBP, sizeof dtbp, &dtbp);
449	else
450		printf("WARNING! Trying to fire up the kernel, but no "
451		    "device tree blob found!\n");
452#endif
453	file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
454	file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof ST, &ST);
455
456	bi_load_efi_data(kfp);
457
458	/* Figure out the size and location of the metadata. */
459	*modulep = addr;
460	size = bi_copymodules(0);
461	kernend = roundup(addr + size, PAGE_SIZE);
462	*kernendp = kernend;
463
464	/* patch MODINFOMD_KERNEND */
465	md = file_findmetadata(kfp, MODINFOMD_KERNEND);
466	bcopy(&kernend, md->md_data, sizeof kernend);
467
468#if defined(__arm__)
469	*modulep -= __elfN(relocation_offset);
470
471	/* Do relocation fixup on metadata of each module. */
472	for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
473		for (i = 0; i < nitems(mdt); i++) {
474			md = file_findmetadata(xp, mdt[i]);
475			if (md) {
476				bcopy(md->md_data, &vaddr, sizeof vaddr);
477				vaddr -= __elfN(relocation_offset);
478				bcopy(&vaddr, md->md_data, sizeof vaddr);
479			}
480		}
481	}
482#endif
483
484	/* Copy module list and metadata. */
485	(void)bi_copymodules(addr);
486
487	return (0);
488}
489