1264095Semaste/*-
2264095Semaste * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3264095Semaste * Copyright (c) 2004, 2006 Marcel Moolenaar
4264095Semaste * Copyright (c) 2014 The FreeBSD Foundation
5264095Semaste * All rights reserved.
6264095Semaste *
7264095Semaste * Redistribution and use in source and binary forms, with or without
8264095Semaste * modification, are permitted provided that the following conditions
9264095Semaste * are met:
10264095Semaste * 1. Redistributions of source code must retain the above copyright
11264095Semaste *    notice, this list of conditions and the following disclaimer.
12264095Semaste * 2. Redistributions in binary form must reproduce the above copyright
13264095Semaste *    notice, this list of conditions and the following disclaimer in the
14264095Semaste *    documentation and/or other materials provided with the distribution.
15264095Semaste *
16264095Semaste * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17264095Semaste * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18264095Semaste * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19264095Semaste * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20264095Semaste * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21264095Semaste * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22264095Semaste * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23264095Semaste * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24264095Semaste * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25264095Semaste * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26264095Semaste * SUCH DAMAGE.
27264095Semaste */
28264095Semaste
29264095Semaste#include <sys/cdefs.h>
30264095Semaste__FBSDID("$FreeBSD$");
31264095Semaste
32264095Semaste#include <stand.h>
33264095Semaste#include <string.h>
34264095Semaste#include <sys/param.h>
35264095Semaste#include <sys/reboot.h>
36264095Semaste#include <sys/linker.h>
37264095Semaste#include <sys/boot.h>
38264095Semaste#include <machine/cpufunc.h>
39264095Semaste#include <machine/metadata.h>
40264095Semaste#include <machine/psl.h>
41264095Semaste#include <machine/specialreg.h>
42264095Semaste
43264095Semaste#include <efi.h>
44264095Semaste#include <efilib.h>
45264095Semaste
46264095Semaste#include "bootstrap.h"
47264095Semaste#include "framebuffer.h"
48264095Semaste#include "x86_efi.h"
49264095Semaste
50264095SemasteUINTN x86_efi_mapkey;
51264095Semaste
52264095Semastestatic const char howto_switches[] = "aCdrgDmphsv";
53264095Semastestatic int howto_masks[] = {
54264095Semaste	RB_ASKNAME, RB_CDROM, RB_KDB, RB_DFLTROOT, RB_GDB, RB_MULTIPLE,
55264095Semaste	RB_MUTE, RB_PAUSE, RB_SERIAL, RB_SINGLE, RB_VERBOSE
56264095Semaste};
57264095Semaste
58264095Semastestatic int
59264095Semastebi_getboothowto(char *kargs)
60264095Semaste{
61264095Semaste	const char *sw;
62264095Semaste	char *opts;
63271880Semaste	char *console;
64264095Semaste	int howto, i;
65264095Semaste
66264095Semaste	howto = 0;
67264095Semaste
68264095Semaste	/* Get the boot options from the environment first. */
69264095Semaste	for (i = 0; howto_names[i].ev != NULL; i++) {
70264095Semaste		if (getenv(howto_names[i].ev) != NULL)
71264095Semaste			howto |= howto_names[i].mask;
72264095Semaste	}
73264095Semaste
74271880Semaste	console = getenv("console");
75271880Semaste	if (console != NULL) {
76271880Semaste		if (strcmp(console, "comconsole") == 0)
77271880Semaste			howto |= RB_SERIAL;
78271880Semaste		if (strcmp(console, "nullconsole") == 0)
79271880Semaste			howto |= RB_MUTE;
80271880Semaste	}
81271880Semaste
82264095Semaste	/* Parse kargs */
83264095Semaste	if (kargs == NULL)
84264095Semaste		return (howto);
85264095Semaste
86264095Semaste	opts = strchr(kargs, '-');
87264095Semaste	while (opts != NULL) {
88264095Semaste		while (*(++opts) != '\0') {
89264095Semaste			sw = strchr(howto_switches, *opts);
90264095Semaste			if (sw == NULL)
91264095Semaste				break;
92264095Semaste			howto |= howto_masks[sw - howto_switches];
93264095Semaste		}
94264095Semaste		opts = strchr(opts, '-');
95264095Semaste	}
96264095Semaste
97264095Semaste	return (howto);
98264095Semaste}
99264095Semaste
100264095Semaste/*
101264095Semaste * Copy the environment into the load area starting at (addr).
102264095Semaste * Each variable is formatted as <name>=<value>, with a single nul
103264095Semaste * separating each variable, and a double nul terminating the environment.
104264095Semaste */
105264095Semastestatic vm_offset_t
106264095Semastebi_copyenv(vm_offset_t start)
107264095Semaste{
108264095Semaste	struct env_var *ep;
109264095Semaste	vm_offset_t addr, last;
110264095Semaste	size_t len;
111264095Semaste
112264095Semaste	addr = last = start;
113264095Semaste
114264095Semaste	/* Traverse the environment. */
115264095Semaste	for (ep = environ; ep != NULL; ep = ep->ev_next) {
116264095Semaste		len = strlen(ep->ev_name);
117264095Semaste		if (x86_efi_copyin(ep->ev_name, addr, len) != len)
118264095Semaste			break;
119264095Semaste		addr += len;
120264095Semaste		if (x86_efi_copyin("=", addr, 1) != 1)
121264095Semaste			break;
122264095Semaste		addr++;
123264095Semaste		if (ep->ev_value != NULL) {
124264095Semaste			len = strlen(ep->ev_value);
125264095Semaste			if (x86_efi_copyin(ep->ev_value, addr, len) != len)
126264095Semaste				break;
127264095Semaste			addr += len;
128264095Semaste		}
129264095Semaste		if (x86_efi_copyin("", addr, 1) != 1)
130264095Semaste			break;
131264095Semaste		last = ++addr;
132264095Semaste	}
133264095Semaste
134264095Semaste	if (x86_efi_copyin("", last++, 1) != 1)
135264095Semaste		last = start;
136264095Semaste	return(last);
137264095Semaste}
138264095Semaste
139264095Semaste/*
140264095Semaste * Copy module-related data into the load area, where it can be
141264095Semaste * used as a directory for loaded modules.
142264095Semaste *
143264095Semaste * Module data is presented in a self-describing format.  Each datum
144264095Semaste * is preceded by a 32-bit identifier and a 32-bit size field.
145264095Semaste *
146264095Semaste * Currently, the following data are saved:
147264095Semaste *
148264095Semaste * MOD_NAME	(variable)		module name (string)
149264095Semaste * MOD_TYPE	(variable)		module type (string)
150264095Semaste * MOD_ARGS	(variable)		module parameters (string)
151264095Semaste * MOD_ADDR	sizeof(vm_offset_t)	module load address
152264095Semaste * MOD_SIZE	sizeof(size_t)		module size
153264095Semaste * MOD_METADATA	(variable)		type-specific metadata
154264095Semaste */
155264095Semaste#define	COPY32(v, a, c) {					\
156264095Semaste	uint32_t x = (v);					\
157264095Semaste	if (c)							\
158264095Semaste		x86_efi_copyin(&x, a, sizeof(x));		\
159264095Semaste	a += sizeof(x);						\
160264095Semaste}
161264095Semaste
162264095Semaste#define	MOD_STR(t, a, s, c) {					\
163264095Semaste	COPY32(t, a, c);					\
164264095Semaste	COPY32(strlen(s) + 1, a, c);				\
165264095Semaste	if (c)							\
166264095Semaste		x86_efi_copyin(s, a, strlen(s) + 1);		\
167264095Semaste	a += roundup(strlen(s) + 1, sizeof(uint64_t));		\
168264095Semaste}
169264095Semaste
170264095Semaste#define	MOD_NAME(a, s, c)	MOD_STR(MODINFO_NAME, a, s, c)
171264095Semaste#define	MOD_TYPE(a, s, c)	MOD_STR(MODINFO_TYPE, a, s, c)
172264095Semaste#define	MOD_ARGS(a, s, c)	MOD_STR(MODINFO_ARGS, a, s, c)
173264095Semaste
174264095Semaste#define	MOD_VAR(t, a, s, c) {					\
175264095Semaste	COPY32(t, a, c);					\
176264095Semaste	COPY32(sizeof(s), a, c);				\
177264095Semaste	if (c)							\
178264095Semaste		x86_efi_copyin(&s, a, sizeof(s));		\
179264095Semaste	a += roundup(sizeof(s), sizeof(uint64_t));		\
180264095Semaste}
181264095Semaste
182264095Semaste#define	MOD_ADDR(a, s, c)	MOD_VAR(MODINFO_ADDR, a, s, c)
183264095Semaste#define	MOD_SIZE(a, s, c)	MOD_VAR(MODINFO_SIZE, a, s, c)
184264095Semaste
185264095Semaste#define	MOD_METADATA(a, mm, c) {				\
186264095Semaste	COPY32(MODINFO_METADATA | mm->md_type, a, c);		\
187264095Semaste	COPY32(mm->md_size, a, c);				\
188264095Semaste	if (c)							\
189264095Semaste		x86_efi_copyin(mm->md_data, a, mm->md_size);	\
190264095Semaste	a += roundup(mm->md_size, sizeof(uint64_t));		\
191264095Semaste}
192264095Semaste
193264095Semaste#define	MOD_END(a, c) {						\
194264095Semaste	COPY32(MODINFO_END, a, c);				\
195264095Semaste	COPY32(0, a, c);					\
196264095Semaste}
197264095Semaste
198264095Semastestatic vm_offset_t
199264095Semastebi_copymodules(vm_offset_t addr)
200264095Semaste{
201264095Semaste	struct preloaded_file *fp;
202264095Semaste	struct file_metadata *md;
203264095Semaste	int c;
204264095Semaste	uint64_t v;
205264095Semaste
206264095Semaste	c = addr != 0;
207264095Semaste	/* Start with the first module on the list, should be the kernel. */
208264095Semaste	for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
209264095Semaste		MOD_NAME(addr, fp->f_name, c); /* This must come first. */
210264095Semaste		MOD_TYPE(addr, fp->f_type, c);
211264095Semaste		if (fp->f_args)
212264095Semaste			MOD_ARGS(addr, fp->f_args, c);
213264095Semaste		v = fp->f_addr;
214264095Semaste		MOD_ADDR(addr, v, c);
215264095Semaste		v = fp->f_size;
216264095Semaste		MOD_SIZE(addr, v, c);
217264095Semaste		for (md = fp->f_metadata; md != NULL; md = md->md_next)
218264095Semaste			if (!(md->md_type & MODINFOMD_NOCOPY))
219264095Semaste				MOD_METADATA(addr, md, c);
220264095Semaste	}
221264095Semaste	MOD_END(addr, c);
222264095Semaste	return(addr);
223264095Semaste}
224264095Semaste
225264095Semastestatic int
226264095Semastebi_load_efi_data(struct preloaded_file *kfp)
227264095Semaste{
228264095Semaste	EFI_MEMORY_DESCRIPTOR *mm;
229264095Semaste	EFI_PHYSICAL_ADDRESS addr;
230264095Semaste	EFI_STATUS status;
231264095Semaste	size_t efisz;
232264095Semaste	UINTN mmsz, pages, sz;
233264095Semaste	UINT32 mmver;
234264095Semaste	struct efi_map_header *efihdr;
235264095Semaste	struct efi_fb efifb;
236264095Semaste
237264095Semaste	if (efi_find_framebuffer(&efifb) == 0)
238264095Semaste		file_addmetadata(kfp, MODINFOMD_EFI_FB, sizeof(efifb), &efifb);
239264095Semaste
240264095Semaste	efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf;
241264095Semaste
242264095Semaste	/*
243264095Semaste	 * Allocate enough pages to hold the bootinfo block and the memory
244264095Semaste	 * map EFI will return to us. The memory map has an unknown size,
245264095Semaste	 * so we have to determine that first. Note that the AllocatePages
246264095Semaste	 * call can itself modify the memory map, so we have to take that
247264095Semaste	 * into account as well. The changes to the memory map are caused
248264095Semaste	 * by splitting a range of free memory into two (AFAICT), so that
249264095Semaste	 * one is marked as being loader data.
250264095Semaste	 */
251264095Semaste	sz = 0;
252264095Semaste	BS->GetMemoryMap(&sz, NULL, &x86_efi_mapkey, &mmsz, &mmver);
253264095Semaste	sz += mmsz;
254264095Semaste	sz = (sz + 0xf) & ~0xf;
255264095Semaste	pages = EFI_SIZE_TO_PAGES(sz + efisz);
256264095Semaste	status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, pages,
257264095Semaste	    &addr);
258264095Semaste	if (EFI_ERROR(status)) {
259264095Semaste		printf("%s: AllocatePages() returned 0x%lx\n", __func__,
260264095Semaste		    (long)status);
261264095Semaste		return (ENOMEM);
262264095Semaste	}
263264095Semaste
264264095Semaste	/*
265264095Semaste	 * Read the memory map and stash it after bootinfo. Align the
266264095Semaste	 * memory map on a 16-byte boundary (the bootinfo block is page
267264095Semaste	 * aligned).
268264095Semaste	 */
269264095Semaste	efihdr = (struct efi_map_header *)addr;
270264095Semaste	mm = (void *)((uint8_t *)efihdr + efisz);
271264095Semaste	sz = (EFI_PAGE_SIZE * pages) - efisz;
272264095Semaste	status = BS->GetMemoryMap(&sz, mm, &x86_efi_mapkey, &mmsz, &mmver);
273264095Semaste	if (EFI_ERROR(status)) {
274264095Semaste		printf("%s: GetMemoryMap() returned 0x%lx\n", __func__,
275264095Semaste		    (long)status);
276264095Semaste		return (EINVAL);
277264095Semaste	}
278264095Semaste
279264095Semaste	efihdr->memory_size = sz;
280264095Semaste	efihdr->descriptor_size = mmsz;
281264095Semaste	efihdr->descriptor_version = mmver;
282264095Semaste
283264095Semaste	file_addmetadata(kfp, MODINFOMD_EFI_MAP, efisz + sz, efihdr);
284264095Semaste
285264095Semaste	return (0);
286264095Semaste}
287264095Semaste
288264095Semaste/*
289264095Semaste * Load the information expected by an amd64 kernel.
290264095Semaste *
291264095Semaste * - The 'boothowto' argument is constructed.
292264095Semaste * - The 'bootdev' argument is constructed.
293264095Semaste * - The 'bootinfo' struct is constructed, and copied into the kernel space.
294264095Semaste * - The kernel environment is copied into kernel space.
295264095Semaste * - Module metadata are formatted and placed in kernel space.
296264095Semaste */
297264095Semasteint
298264095Semastebi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp)
299264095Semaste{
300264095Semaste	struct preloaded_file *xp, *kfp;
301264095Semaste	struct devdesc *rootdev;
302264095Semaste	struct file_metadata *md;
303264095Semaste	vm_offset_t addr;
304264095Semaste	uint64_t kernend;
305264095Semaste	uint64_t envp;
306264095Semaste	vm_offset_t size;
307264095Semaste	char *rootdevname;
308264095Semaste	int howto;
309264095Semaste
310264095Semaste	howto = bi_getboothowto(args);
311264095Semaste
312264095Semaste	/*
313264095Semaste	 * Allow the environment variable 'rootdev' to override the supplied
314264095Semaste	 * device. This should perhaps go to MI code and/or have $rootdev
315264095Semaste	 * tested/set by MI code before launching the kernel.
316264095Semaste	 */
317264095Semaste	rootdevname = getenv("rootdev");
318264095Semaste	x86_efi_getdev((void**)(&rootdev), rootdevname, NULL);
319264095Semaste	if (rootdev == NULL) {
320264095Semaste		printf("Can't determine root device.\n");
321264095Semaste		return(EINVAL);
322264095Semaste	}
323264095Semaste
324264095Semaste	/* Try reading the /etc/fstab file to select the root device */
325264095Semaste	getrootmount(x86_efi_fmtdev((void *)rootdev));
326264095Semaste
327264095Semaste	addr = 0;
328264095Semaste	for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
329264095Semaste		if (addr < (xp->f_addr + xp->f_size))
330264095Semaste			addr = xp->f_addr + xp->f_size;
331264095Semaste	}
332264095Semaste
333264095Semaste	/* Pad to a page boundary. */
334264095Semaste	addr = roundup(addr, PAGE_SIZE);
335264095Semaste
336264095Semaste	/* Copy our environment. */
337264095Semaste	envp = addr;
338264095Semaste	addr = bi_copyenv(addr);
339264095Semaste
340264095Semaste	/* Pad to a page boundary. */
341264095Semaste	addr = roundup(addr, PAGE_SIZE);
342264095Semaste
343264095Semaste	kfp = file_findfile(NULL, "elf kernel");
344264095Semaste	if (kfp == NULL)
345264095Semaste		kfp = file_findfile(NULL, "elf64 kernel");
346264095Semaste	if (kfp == NULL)
347264095Semaste		panic("can't find kernel file");
348264095Semaste	kernend = 0;	/* fill it in later */
349264095Semaste	file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
350264095Semaste	file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
351264095Semaste	file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
352264095Semaste
353264095Semaste	bi_load_efi_data(kfp);
354264095Semaste
355264095Semaste	/* Figure out the size and location of the metadata. */
356264095Semaste	*modulep = addr;
357264095Semaste	size = bi_copymodules(0);
358264095Semaste	kernend = roundup(addr + size, PAGE_SIZE);
359264095Semaste	*kernendp = kernend;
360264095Semaste
361264095Semaste	/* patch MODINFOMD_KERNEND */
362264095Semaste	md = file_findmetadata(kfp, MODINFOMD_KERNEND);
363264095Semaste	bcopy(&kernend, md->md_data, sizeof kernend);
364264095Semaste
365264095Semaste	/* Copy module list and metadata. */
366264095Semaste	(void)bi_copymodules(addr);
367264095Semaste
368264095Semaste	return (0);
369264095Semaste}
370