1176349Smarcel/*-
2176349Smarcel * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3182724Sraj * Copyright (C) 2006 Semihalf, Piotr Kruszynski <ppk@semihalf.com>
4182724Sraj * Copyright (C) 2007-2008 Semihalf, Rafal Jaworowski <raj@semihalf.com>
5176349Smarcel * All rights reserved.
6176349Smarcel *
7176349Smarcel * Redistribution and use in source and binary forms, with or without
8176349Smarcel * modification, are permitted provided that the following conditions
9176349Smarcel * are met:
10176349Smarcel * 1. Redistributions of source code must retain the above copyright
11176349Smarcel *    notice, this list of conditions and the following disclaimer.
12176349Smarcel * 2. Redistributions in binary form must reproduce the above copyright
13176349Smarcel *    notice, this list of conditions and the following disclaimer in the
14176349Smarcel *    documentation and/or other materials provided with the distribution.
15176349Smarcel *
16176349Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17176349Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18176349Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19176349Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20176349Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21176349Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22176349Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23176349Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24176349Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25176349Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26176349Smarcel * SUCH DAMAGE.
27176349Smarcel */
28176349Smarcel
29176349Smarcel#include <sys/cdefs.h>
30176349Smarcel__FBSDID("$FreeBSD$");
31176349Smarcel
32176349Smarcel#include <stand.h>
33176349Smarcel#include <sys/param.h>
34176349Smarcel#include <sys/reboot.h>
35176349Smarcel#include <sys/linker.h>
36176349Smarcel
37176349Smarcel#include <machine/elf.h>
38176349Smarcel#include <machine/metadata.h>
39176349Smarcel
40176349Smarcel#include "api_public.h"
41176349Smarcel#include "bootstrap.h"
42176487Smarcel#include "glue.h"
43176349Smarcel
44208538Sraj#if defined(LOADER_FDT_SUPPORT)
45208538Srajextern int fdt_fixup(void);
46208538Sraj#endif
47208538Sraj
48176349Smarcel/*
49176349Smarcel * Return a 'boothowto' value corresponding to the kernel arguments in
50176349Smarcel * (kargs) and any relevant environment variables.
51176349Smarcel */
52176489Smarcelstatic struct
53176349Smarcel{
54176489Smarcel	const char	*ev;
55176489Smarcel	int		mask;
56176349Smarcel} howto_names[] = {
57176489Smarcel	{"boot_askname",	RB_ASKNAME},
58176489Smarcel	{"boot_cdrom",		RB_CDROM},
59176489Smarcel	{"boot_ddb",		RB_KDB},
60176489Smarcel	{"boot_dfltroot",	RB_DFLTROOT},
61176489Smarcel	{"boot_gdb",		RB_GDB},
62176489Smarcel	{"boot_multicons",	RB_MULTIPLE},
63176489Smarcel	{"boot_mute",		RB_MUTE},
64176489Smarcel	{"boot_pause",		RB_PAUSE},
65176489Smarcel	{"boot_serial",		RB_SERIAL},
66176489Smarcel	{"boot_single",		RB_SINGLE},
67176489Smarcel	{"boot_verbose",	RB_VERBOSE},
68176489Smarcel	{NULL,			0}
69176349Smarcel};
70176349Smarcel
71176487Smarcelstatic int
72176349Smarcelmd_getboothowto(char *kargs)
73176349Smarcel{
74176489Smarcel	char	*cp;
75176489Smarcel	int	howto;
76176489Smarcel	int	active;
77176489Smarcel	int	i;
78176489Smarcel
79176489Smarcel	/* Parse kargs */
80176489Smarcel	howto = 0;
81176489Smarcel	if (kargs != NULL) {
82176489Smarcel		cp = kargs;
83176489Smarcel		active = 0;
84176489Smarcel		while (*cp != 0) {
85176489Smarcel			if (!active && (*cp == '-'))
86176489Smarcel				active = 1;
87176489Smarcel			else if (active)
88176489Smarcel				switch (*cp) {
89176489Smarcel				case 'a':
90176489Smarcel					howto |= RB_ASKNAME;
91176489Smarcel					break;
92176489Smarcel				case 'C':
93176489Smarcel					howto |= RB_CDROM;
94176489Smarcel					break;
95176489Smarcel				case 'd':
96176489Smarcel					howto |= RB_KDB;
97176489Smarcel					break;
98176489Smarcel				case 'D':
99176489Smarcel					howto |= RB_MULTIPLE;
100176489Smarcel					break;
101176489Smarcel				case 'm':
102176489Smarcel					howto |= RB_MUTE;
103176489Smarcel					break;
104176489Smarcel				case 'g':
105176489Smarcel					howto |= RB_GDB;
106176489Smarcel					break;
107176489Smarcel				case 'h':
108176489Smarcel					howto |= RB_SERIAL;
109176489Smarcel					break;
110176489Smarcel				case 'p':
111176489Smarcel					howto |= RB_PAUSE;
112176489Smarcel					break;
113176489Smarcel				case 'r':
114176489Smarcel					howto |= RB_DFLTROOT;
115176489Smarcel					break;
116176489Smarcel				case 's':
117176489Smarcel					howto |= RB_SINGLE;
118176489Smarcel					break;
119176489Smarcel				case 'v':
120176489Smarcel					howto |= RB_VERBOSE;
121176489Smarcel					break;
122176489Smarcel				default:
123176489Smarcel					active = 0;
124176489Smarcel					break;
125176489Smarcel				}
126176489Smarcel				cp++;
127176349Smarcel		}
128176349Smarcel	}
129176489Smarcel
130176489Smarcel	/* get equivalents from the environment */
131176489Smarcel	for (i = 0; howto_names[i].ev != NULL; i++) {
132176489Smarcel		if (getenv(howto_names[i].ev) != NULL)
133176489Smarcel			howto |= howto_names[i].mask;
134176489Smarcel	}
135176489Smarcel	if (!strcmp(getenv("console"), "comconsole"))
136176489Smarcel		howto |= RB_SERIAL;
137176489Smarcel	if (!strcmp(getenv("console"), "nullconsole"))
138176489Smarcel		howto |= RB_MUTE;
139176489Smarcel
140176489Smarcel	return(howto);
141176349Smarcel}
142176349Smarcel
143176349Smarcel/*
144176349Smarcel * Copy the environment into the load area starting at (addr).
145176349Smarcel * Each variable is formatted as <name>=<value>, with a single nul
146176349Smarcel * separating each variable, and a double nul terminating the environment.
147176349Smarcel */
148176487Smarcelstatic vm_offset_t
149176349Smarcelmd_copyenv(vm_offset_t addr)
150176349Smarcel{
151176489Smarcel	struct env_var	*ep;
152176489Smarcel
153176489Smarcel	/* traverse the environment */
154176489Smarcel	for (ep = environ; ep != NULL; ep = ep->ev_next) {
155176489Smarcel		archsw.arch_copyin(ep->ev_name, addr, strlen(ep->ev_name));
156176489Smarcel		addr += strlen(ep->ev_name);
157176489Smarcel		archsw.arch_copyin("=", addr, 1);
158176489Smarcel		addr++;
159176489Smarcel		if (ep->ev_value != NULL) {
160176489Smarcel			archsw.arch_copyin(ep->ev_value, addr,
161176489Smarcel			    strlen(ep->ev_value));
162176489Smarcel			addr += strlen(ep->ev_value);
163176489Smarcel		}
164176489Smarcel		archsw.arch_copyin("", addr, 1);
165176489Smarcel		addr++;
166176349Smarcel	}
167176349Smarcel	archsw.arch_copyin("", addr, 1);
168176349Smarcel	addr++;
169176489Smarcel	return(addr);
170176349Smarcel}
171176349Smarcel
172176349Smarcel/*
173176349Smarcel * Copy module-related data into the load area, where it can be
174176349Smarcel * used as a directory for loaded modules.
175176349Smarcel *
176176349Smarcel * Module data is presented in a self-describing format.  Each datum
177176349Smarcel * is preceded by a 32-bit identifier and a 32-bit size field.
178176349Smarcel *
179176349Smarcel * Currently, the following data are saved:
180176349Smarcel *
181176349Smarcel * MOD_NAME	(variable)		module name (string)
182176349Smarcel * MOD_TYPE	(variable)		module type (string)
183176349Smarcel * MOD_ARGS	(variable)		module parameters (string)
184176349Smarcel * MOD_ADDR	sizeof(vm_offset_t)	module load address
185176349Smarcel * MOD_SIZE	sizeof(size_t)		module size
186176349Smarcel * MOD_METADATA	(variable)		type-specific metadata
187176349Smarcel */
188176489Smarcel#define	COPY32(v, a, c) {			\
189176349Smarcel    u_int32_t	x = (v);			\
190176349Smarcel    if (c)					\
191176489Smarcel	archsw.arch_copyin(&x, a, sizeof(x));	\
192176349Smarcel    a += sizeof(x);				\
193176349Smarcel}
194176349Smarcel
195176489Smarcel#define	MOD_STR(t, a, s, c) {			\
196176349Smarcel    COPY32(t, a, c);				\
197176349Smarcel    COPY32(strlen(s) + 1, a, c)			\
198176349Smarcel    if (c)					\
199176489Smarcel	archsw.arch_copyin(s, a, strlen(s) + 1);\
200176349Smarcel    a += roundup(strlen(s) + 1, sizeof(u_long));\
201176349Smarcel}
202176349Smarcel
203176489Smarcel#define	MOD_NAME(a, s, c)	MOD_STR(MODINFO_NAME, a, s, c)
204176489Smarcel#define	MOD_TYPE(a, s, c)	MOD_STR(MODINFO_TYPE, a, s, c)
205176489Smarcel#define	MOD_ARGS(a, s, c)	MOD_STR(MODINFO_ARGS, a, s, c)
206176349Smarcel
207176489Smarcel#define	MOD_VAR(t, a, s, c) {			\
208176349Smarcel    COPY32(t, a, c);				\
209176349Smarcel    COPY32(sizeof(s), a, c);			\
210176349Smarcel    if (c)					\
211176489Smarcel	archsw.arch_copyin(&s, a, sizeof(s));	\
212176349Smarcel    a += roundup(sizeof(s), sizeof(u_long));	\
213176349Smarcel}
214176349Smarcel
215176489Smarcel#define	MOD_ADDR(a, s, c)	MOD_VAR(MODINFO_ADDR, a, s, c)
216176489Smarcel#define	MOD_SIZE(a, s, c)	MOD_VAR(MODINFO_SIZE, a, s, c)
217176349Smarcel
218176489Smarcel#define	MOD_METADATA(a, mm, c) {		\
219176349Smarcel    COPY32(MODINFO_METADATA | mm->md_type, a, c);\
220176349Smarcel    COPY32(mm->md_size, a, c);			\
221176349Smarcel    if (c)					\
222176489Smarcel	archsw.arch_copyin(mm->md_data, a, mm->md_size);\
223176349Smarcel    a += roundup(mm->md_size, sizeof(u_long));	\
224176349Smarcel}
225176349Smarcel
226176489Smarcel#define	MOD_END(a, c) {				\
227176349Smarcel    COPY32(MODINFO_END, a, c);			\
228176349Smarcel    COPY32(0, a, c);				\
229176349Smarcel}
230176349Smarcel
231176487Smarcelstatic vm_offset_t
232176349Smarcelmd_copymodules(vm_offset_t addr)
233176349Smarcel{
234176489Smarcel	struct preloaded_file	*fp;
235176489Smarcel	struct file_metadata	*md;
236176489Smarcel	int			c;
237199534Sraj	vm_offset_t a;
238176349Smarcel
239176489Smarcel	c = addr != 0;
240176489Smarcel	/* start with the first module on the list, should be the kernel */
241176489Smarcel	for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
242176349Smarcel
243176489Smarcel		MOD_NAME(addr, fp->f_name, c);	/* this field must be first */
244176489Smarcel		MOD_TYPE(addr, fp->f_type, c);
245176489Smarcel		if (fp->f_args)
246176489Smarcel			MOD_ARGS(addr, fp->f_args, c);
247199534Sraj		a = fp->f_addr - __elfN(relocation_offset);
248199534Sraj		MOD_ADDR(addr, a, c);
249176489Smarcel		MOD_SIZE(addr, fp->f_size, c);
250176489Smarcel		for (md = fp->f_metadata; md != NULL; md = md->md_next) {
251176489Smarcel			if (!(md->md_type & MODINFOMD_NOCOPY))
252176489Smarcel				MOD_METADATA(addr, md, c);
253176489Smarcel		}
254176349Smarcel	}
255176489Smarcel	MOD_END(addr, c);
256176489Smarcel	return(addr);
257176349Smarcel}
258176349Smarcel
259176349Smarcel/*
260208538Sraj * Load the information expected by a kernel.
261176349Smarcel *
262176349Smarcel * - The 'boothowto' argument is constructed
263176349Smarcel * - The 'bootdev' argument is constructed
264176349Smarcel * - The kernel environment is copied into kernel space.
265176349Smarcel * - Module metadata are formatted and placed in kernel space.
266176349Smarcel */
267176349Smarcelint
268176349Smarcelmd_load(char *args, vm_offset_t *modulep)
269176349Smarcel{
270208538Sraj	struct preloaded_file	*kfp, *bfp;
271176489Smarcel	struct preloaded_file	*xp;
272176489Smarcel	struct file_metadata	*md;
273176489Smarcel	struct bootinfo		*bip;
274176489Smarcel	vm_offset_t		kernend;
275176489Smarcel	vm_offset_t		addr;
276176489Smarcel	vm_offset_t		envp;
277176489Smarcel	vm_offset_t		size;
278176489Smarcel	vm_offset_t		vaddr;
279208538Sraj	vm_offset_t		dtbp;
280176489Smarcel	char			*rootdevname;
281176489Smarcel	int			howto;
282176489Smarcel	int			i;
283176349Smarcel
284176489Smarcel	/*
285176489Smarcel	 * These metadata addreses must be converted for kernel after
286176489Smarcel	 * relocation.
287176489Smarcel	 */
288176489Smarcel	uint32_t		mdt[] = {
289208538Sraj	    MODINFOMD_SSYM, MODINFOMD_ESYM, MODINFOMD_KERNEND,
290208538Sraj	    MODINFOMD_ENVP,
291208538Sraj#if defined(LOADER_FDT_SUPPORT)
292208538Sraj	    MODINFOMD_DTBP
293208538Sraj#endif
294176489Smarcel	};
295176349Smarcel
296176489Smarcel	howto = md_getboothowto(args);
297176349Smarcel
298176489Smarcel	/*
299176489Smarcel	 * Allow the environment variable 'rootdev' to override the supplied
300176489Smarcel	 * device. This should perhaps go to MI code and/or have $rootdev
301176489Smarcel	 * tested/set by MI code before launching the kernel.
302176489Smarcel	 */
303176489Smarcel	rootdevname = getenv("rootdev");
304176489Smarcel	if (rootdevname == NULL)
305176489Smarcel		rootdevname = getenv("currdev");
306176489Smarcel	/* Try reading the /etc/fstab file to select the root device */
307176489Smarcel	getrootmount(rootdevname);
308176349Smarcel
309208538Sraj	/* Find the last module in the chain */
310176489Smarcel	addr = 0;
311176489Smarcel	for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
312176489Smarcel		if (addr < (xp->f_addr + xp->f_size))
313176489Smarcel			addr = xp->f_addr + xp->f_size;
314176489Smarcel	}
315208538Sraj	/* Pad to a page boundary */
316176489Smarcel	addr = roundup(addr, PAGE_SIZE);
317176349Smarcel
318208538Sraj	/* Copy our environment */
319176489Smarcel	envp = addr;
320176489Smarcel	addr = md_copyenv(addr);
321176349Smarcel
322208538Sraj	/* Pad to a page boundary */
323176489Smarcel	addr = roundup(addr, PAGE_SIZE);
324176349Smarcel
325176489Smarcel	kernend = 0;
326176489Smarcel	kfp = file_findfile(NULL, "elf32 kernel");
327176489Smarcel	if (kfp == NULL)
328176489Smarcel		kfp = file_findfile(NULL, "elf kernel");
329176489Smarcel	if (kfp == NULL)
330176489Smarcel		panic("can't find kernel file");
331176489Smarcel	file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
332208538Sraj	file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
333208538Sraj
334208538Sraj#if defined(LOADER_FDT_SUPPORT)
335208538Sraj	/* Handle device tree blob */
336234558Sraj	dtbp = fdt_fixup();
337234558Sraj	if (dtbp != (vm_offset_t)NULL)
338234558Sraj		file_addmetadata(kfp, MODINFOMD_DTBP, sizeof dtbp, &dtbp);
339234558Sraj	else
340234558Sraj		pager_output("WARNING! Trying to fire up the kernel, but no "
341234558Sraj		    "device tree blob found!\n");
342208538Sraj#endif
343208538Sraj
344176489Smarcel	file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
345176349Smarcel
346208538Sraj	/* Figure out the size and location of the metadata */
347176489Smarcel	*modulep = addr;
348176489Smarcel	size = md_copymodules(0);
349176489Smarcel	kernend = roundup(addr + size, PAGE_SIZE);
350176349Smarcel
351208538Sraj	/* Provide MODINFOMD_KERNEND */
352176489Smarcel	md = file_findmetadata(kfp, MODINFOMD_KERNEND);
353176489Smarcel	bcopy(&kernend, md->md_data, sizeof kernend);
354176349Smarcel
355176489Smarcel	/* Convert addresses to the final VA */
356176489Smarcel	*modulep -= __elfN(relocation_offset);
357176349Smarcel
358176489Smarcel	for (i = 0; i < sizeof mdt / sizeof mdt[0]; i++) {
359176489Smarcel		md = file_findmetadata(kfp, mdt[i]);
360176489Smarcel		if (md) {
361176489Smarcel			bcopy(md->md_data, &vaddr, sizeof vaddr);
362176489Smarcel			vaddr -= __elfN(relocation_offset);
363176489Smarcel			bcopy(&vaddr, md->md_data, sizeof vaddr);
364176489Smarcel		}
365176489Smarcel	}
366208538Sraj
367208538Sraj	/* Only now copy actual modules and metadata */
368176489Smarcel	(void)md_copymodules(addr);
369176349Smarcel
370208538Sraj	return (0);
371176349Smarcel}
372