metadata.c revision 182724
1/*-
2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3 * Copyright (C) 2006 Semihalf, Piotr Kruszynski <ppk@semihalf.com>
4 * Copyright (C) 2007-2008 Semihalf, Rafal Jaworowski <raj@semihalf.com>
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: head/sys/boot/uboot/common/metadata.c 182724 2008-09-03 15:52:05Z raj $");
31
32#include <stand.h>
33#include <sys/param.h>
34#include <sys/reboot.h>
35#include <sys/linker.h>
36
37#include <machine/elf.h>
38#include <machine/metadata.h>
39#include <machine/bootinfo.h>
40
41#include "api_public.h"
42#include "bootstrap.h"
43#include "glue.h"
44
45/*
46 * Return a 'boothowto' value corresponding to the kernel arguments in
47 * (kargs) and any relevant environment variables.
48 */
49static struct
50{
51	const char	*ev;
52	int		mask;
53} howto_names[] = {
54	{"boot_askname",	RB_ASKNAME},
55	{"boot_cdrom",		RB_CDROM},
56	{"boot_ddb",		RB_KDB},
57	{"boot_dfltroot",	RB_DFLTROOT},
58	{"boot_gdb",		RB_GDB},
59	{"boot_multicons",	RB_MULTIPLE},
60	{"boot_mute",		RB_MUTE},
61	{"boot_pause",		RB_PAUSE},
62	{"boot_serial",		RB_SERIAL},
63	{"boot_single",		RB_SINGLE},
64	{"boot_verbose",	RB_VERBOSE},
65	{NULL,			0}
66};
67
68static int
69md_getboothowto(char *kargs)
70{
71	char	*cp;
72	int	howto;
73	int	active;
74	int	i;
75
76	/* Parse kargs */
77	howto = 0;
78	if (kargs != NULL) {
79		cp = kargs;
80		active = 0;
81		while (*cp != 0) {
82			if (!active && (*cp == '-'))
83				active = 1;
84			else if (active)
85				switch (*cp) {
86				case 'a':
87					howto |= RB_ASKNAME;
88					break;
89				case 'C':
90					howto |= RB_CDROM;
91					break;
92				case 'd':
93					howto |= RB_KDB;
94					break;
95				case 'D':
96					howto |= RB_MULTIPLE;
97					break;
98				case 'm':
99					howto |= RB_MUTE;
100					break;
101				case 'g':
102					howto |= RB_GDB;
103					break;
104				case 'h':
105					howto |= RB_SERIAL;
106					break;
107				case 'p':
108					howto |= RB_PAUSE;
109					break;
110				case 'r':
111					howto |= RB_DFLTROOT;
112					break;
113				case 's':
114					howto |= RB_SINGLE;
115					break;
116				case 'v':
117					howto |= RB_VERBOSE;
118					break;
119				default:
120					active = 0;
121					break;
122				}
123				cp++;
124		}
125	}
126
127	/* get equivalents from the environment */
128	for (i = 0; howto_names[i].ev != NULL; i++) {
129		if (getenv(howto_names[i].ev) != NULL)
130			howto |= howto_names[i].mask;
131	}
132	if (!strcmp(getenv("console"), "comconsole"))
133		howto |= RB_SERIAL;
134	if (!strcmp(getenv("console"), "nullconsole"))
135		howto |= RB_MUTE;
136
137	return(howto);
138}
139
140/*
141 * Copy the environment into the load area starting at (addr).
142 * Each variable is formatted as <name>=<value>, with a single nul
143 * separating each variable, and a double nul terminating the environment.
144 */
145static vm_offset_t
146md_copyenv(vm_offset_t addr)
147{
148	struct env_var	*ep;
149
150	/* traverse the environment */
151	for (ep = environ; ep != NULL; ep = ep->ev_next) {
152		archsw.arch_copyin(ep->ev_name, addr, strlen(ep->ev_name));
153		addr += strlen(ep->ev_name);
154		archsw.arch_copyin("=", addr, 1);
155		addr++;
156		if (ep->ev_value != NULL) {
157			archsw.arch_copyin(ep->ev_value, addr,
158			    strlen(ep->ev_value));
159			addr += strlen(ep->ev_value);
160		}
161		archsw.arch_copyin("", addr, 1);
162		addr++;
163	}
164	archsw.arch_copyin("", addr, 1);
165	addr++;
166	return(addr);
167}
168
169/*
170 * Copy module-related data into the load area, where it can be
171 * used as a directory for loaded modules.
172 *
173 * Module data is presented in a self-describing format.  Each datum
174 * is preceded by a 32-bit identifier and a 32-bit size field.
175 *
176 * Currently, the following data are saved:
177 *
178 * MOD_NAME	(variable)		module name (string)
179 * MOD_TYPE	(variable)		module type (string)
180 * MOD_ARGS	(variable)		module parameters (string)
181 * MOD_ADDR	sizeof(vm_offset_t)	module load address
182 * MOD_SIZE	sizeof(size_t)		module size
183 * MOD_METADATA	(variable)		type-specific metadata
184 */
185#define	COPY32(v, a, c) {			\
186    u_int32_t	x = (v);			\
187    if (c)					\
188	archsw.arch_copyin(&x, a, sizeof(x));	\
189    a += sizeof(x);				\
190}
191
192#define	MOD_STR(t, a, s, c) {			\
193    COPY32(t, a, c);				\
194    COPY32(strlen(s) + 1, a, c)			\
195    if (c)					\
196	archsw.arch_copyin(s, a, strlen(s) + 1);\
197    a += roundup(strlen(s) + 1, sizeof(u_long));\
198}
199
200#define	MOD_NAME(a, s, c)	MOD_STR(MODINFO_NAME, a, s, c)
201#define	MOD_TYPE(a, s, c)	MOD_STR(MODINFO_TYPE, a, s, c)
202#define	MOD_ARGS(a, s, c)	MOD_STR(MODINFO_ARGS, a, s, c)
203
204#define	MOD_VAR(t, a, s, c) {			\
205    COPY32(t, a, c);				\
206    COPY32(sizeof(s), a, c);			\
207    if (c)					\
208	archsw.arch_copyin(&s, a, sizeof(s));	\
209    a += roundup(sizeof(s), sizeof(u_long));	\
210}
211
212#define	MOD_ADDR(a, s, c)	MOD_VAR(MODINFO_ADDR, a, s, c)
213#define	MOD_SIZE(a, s, c)	MOD_VAR(MODINFO_SIZE, a, s, c)
214
215#define	MOD_METADATA(a, mm, c) {		\
216    COPY32(MODINFO_METADATA | mm->md_type, a, c);\
217    COPY32(mm->md_size, a, c);			\
218    if (c)					\
219	archsw.arch_copyin(mm->md_data, a, mm->md_size);\
220    a += roundup(mm->md_size, sizeof(u_long));	\
221}
222
223#define	MOD_END(a, c) {				\
224    COPY32(MODINFO_END, a, c);			\
225    COPY32(0, a, c);				\
226}
227
228static vm_offset_t
229md_copymodules(vm_offset_t addr)
230{
231	struct preloaded_file	*fp;
232	struct file_metadata	*md;
233	int			c;
234
235	c = addr != 0;
236	/* start with the first module on the list, should be the kernel */
237	for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
238
239		MOD_NAME(addr, fp->f_name, c);	/* this field must be first */
240		MOD_TYPE(addr, fp->f_type, c);
241		if (fp->f_args)
242			MOD_ARGS(addr, fp->f_args, c);
243		MOD_ADDR(addr, fp->f_addr, c);
244		MOD_SIZE(addr, fp->f_size, c);
245		for (md = fp->f_metadata; md != NULL; md = md->md_next) {
246			if (!(md->md_type & MODINFOMD_NOCOPY))
247				MOD_METADATA(addr, md, c);
248		}
249	}
250	MOD_END(addr, c);
251	return(addr);
252}
253
254/*
255 * Prepare the bootinfo structure. Put a ptr to the allocated struct in addr,
256 * return size.
257 */
258static int
259md_bootinfo(struct bootinfo **addr)
260{
261#define	TMP_MAX_ETH	8
262#define	TMP_MAX_MR	8
263	struct bootinfo		*bi;
264	struct bi_mem_region	tmp_mr[TMP_MAX_MR];
265	struct bi_eth_addr	tmp_eth[TMP_MAX_ETH];
266	struct sys_info		*si;
267	char			*str, *end;
268	const char		*env;
269	void			*ptr;
270	u_int8_t		tmp_addr[6];
271	int			i, n, mr_no, eth_no, size;
272
273	if ((si = ub_get_sys_info()) == NULL)
274		panic("can't retrieve U-Boot sysinfo");
275
276	/*
277	 * Handle mem regions (we only care about DRAM)
278	 */
279	for (i = 0, mr_no = 0; i < si->mr_no; i++) {
280		if (si->mr[i].flags == MR_ATTR_DRAM) {
281			if (mr_no >= TMP_MAX_MR) {
282				printf("too many memory regions: %d\n", mr_no);
283				break;
284			}
285			tmp_mr[mr_no].mem_base = si->mr[i].start;
286			tmp_mr[mr_no].mem_size = si->mr[i].size;
287			mr_no++;
288			continue;
289		}
290	}
291	if (mr_no == 0)
292		panic("can't retrieve RAM info");
293
294	size = (mr_no * sizeof(struct bi_mem_region) - sizeof(bi->bi_data));
295
296	/*
297	 * Handle Ethernet addresses: parse u-boot env for eth%daddr
298	 */
299	env = NULL;
300	eth_no = 0;
301	while ((env = ub_env_enum(env)) != NULL) {
302		if (strncmp(env, "eth", 3) == 0 &&
303		    strncmp(env + (strlen(env) - 4), "addr", 4) == 0) {
304
305			/* Extract interface number */
306			i = strtol(env + 3, &end, 10);
307			if (end == (env + 3))
308				/* 'ethaddr' means interface 0 address */
309				n = 0;
310			else
311				n = i;
312
313			if (n >= TMP_MAX_MR) {
314				printf("Ethernet interface number too high: %d. "
315				    "Skipping...\n");
316				continue;
317			}
318
319			str = ub_env_get(env);
320			for (i = 0; i < 6; i++) {
321				tmp_addr[i] = str ? strtol(str, &end, 16) : 0;
322				if (str)
323					str = (*end) ? end + 1 : end;
324
325				tmp_eth[n].mac_addr[i] = tmp_addr[i];
326			}
327
328			/* eth_no is 1-based number of all interfaces defined */
329			if (n + 1 > eth_no)
330				eth_no = n + 1;
331		}
332	}
333
334	size += (eth_no * sizeof(struct bi_eth_addr)) + sizeof(struct bootinfo);
335
336	/*
337	 * Once its whole size is calculated, allocate space for the bootinfo
338	 * and copy over the contents from temp containers.
339	 */
340	if ((bi = malloc(size)) == NULL)
341		panic("can't allocate mem for bootinfo");
342
343	ptr = (struct bi_mem_region *)bi->bi_data;
344	bcopy(tmp_mr, ptr, mr_no * sizeof(struct bi_mem_region));
345	ptr += mr_no * sizeof(struct bi_mem_region);
346	bcopy(tmp_eth, ptr, eth_no * sizeof(struct bi_eth_addr));
347
348	bi->bi_mem_reg_no = mr_no;
349	bi->bi_eth_addr_no = eth_no;
350	bi->bi_version = BI_VERSION;
351	bi->bi_bar_base = si->bar;
352	bi->bi_cpu_clk = si->clk_cpu;
353	bi->bi_bus_clk = si->clk_bus;
354
355	*addr = bi;
356
357	return (size);
358}
359
360/*
361 * Load the information expected by a powerpc kernel.
362 *
363 * - The 'boothowto' argument is constructed
364 * - The 'bootdev' argument is constructed
365 * - The kernel environment is copied into kernel space.
366 * - Module metadata are formatted and placed in kernel space.
367 */
368int
369md_load(char *args, vm_offset_t *modulep)
370{
371	struct preloaded_file	*kfp;
372	struct preloaded_file	*xp;
373	struct file_metadata	*md;
374	struct bootinfo		*bip;
375	vm_offset_t		kernend;
376	vm_offset_t		addr;
377	vm_offset_t		envp;
378	vm_offset_t		size;
379	vm_offset_t		vaddr;
380	char			*rootdevname;
381	int			howto;
382	int			bisize;
383	int			i;
384
385	/*
386	 * These metadata addreses must be converted for kernel after
387	 * relocation.
388	 */
389	uint32_t		mdt[] = {
390	    MODINFOMD_SSYM, MODINFOMD_ESYM, MODINFOMD_KERNEND, MODINFOMD_ENVP
391	};
392
393	howto = md_getboothowto(args);
394
395	/*
396	 * Allow the environment variable 'rootdev' to override the supplied
397	 * device. This should perhaps go to MI code and/or have $rootdev
398	 * tested/set by MI code before launching the kernel.
399	 */
400	rootdevname = getenv("rootdev");
401	if (rootdevname == NULL)
402		rootdevname = getenv("currdev");
403	/* Try reading the /etc/fstab file to select the root device */
404	getrootmount(rootdevname);
405
406	/* find the last module in the chain */
407	addr = 0;
408	for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
409		if (addr < (xp->f_addr + xp->f_size))
410			addr = xp->f_addr + xp->f_size;
411	}
412	/* pad to a page boundary */
413	addr = roundup(addr, PAGE_SIZE);
414
415	/* copy our environment */
416	envp = addr;
417	addr = md_copyenv(addr);
418
419	/* pad to a page boundary */
420	addr = roundup(addr, PAGE_SIZE);
421
422	/* prepare bootinfo */
423	bisize = md_bootinfo(&bip);
424
425	kernend = 0;
426	kfp = file_findfile(NULL, "elf32 kernel");
427	if (kfp == NULL)
428		kfp = file_findfile(NULL, "elf kernel");
429	if (kfp == NULL)
430		panic("can't find kernel file");
431	file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
432	file_addmetadata(kfp, MODINFOMD_BOOTINFO, bisize, bip);
433	file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
434	file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
435
436	*modulep = addr;
437	size = md_copymodules(0);
438	kernend = roundup(addr + size, PAGE_SIZE);
439
440	md = file_findmetadata(kfp, MODINFOMD_KERNEND);
441	bcopy(&kernend, md->md_data, sizeof kernend);
442
443	/* Convert addresses to the final VA */
444	*modulep -= __elfN(relocation_offset);
445
446	for (i = 0; i < sizeof mdt / sizeof mdt[0]; i++) {
447		md = file_findmetadata(kfp, mdt[i]);
448		if (md) {
449			bcopy(md->md_data, &vaddr, sizeof vaddr);
450			vaddr -= __elfN(relocation_offset);
451			bcopy(&vaddr, md->md_data, sizeof vaddr);
452		}
453	}
454	(void)md_copymodules(addr);
455
456	return(0);
457}
458