metadata.c revision 176349
1247738Sbapt/*-
2247738Sbapt * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3247738Sbapt * Copyright (C) 2007 Semihalf, Piotr Kruszynski <ppk@semihalf.com>
4247738Sbapt * All rights reserved.
5247738Sbapt *
6247738Sbapt * Redistribution and use in source and binary forms, with or without
7247738Sbapt * modification, are permitted provided that the following conditions
8247738Sbapt * are met:
9247738Sbapt * 1. Redistributions of source code must retain the above copyright
10247738Sbapt *    notice, this list of conditions and the following disclaimer.
11247738Sbapt * 2. Redistributions in binary form must reproduce the above copyright
12247738Sbapt *    notice, this list of conditions and the following disclaimer in the
13247738Sbapt *    documentation and/or other materials provided with the distribution.
14247738Sbapt *
15247738Sbapt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16247738Sbapt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17247738Sbapt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18247738Sbapt * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19247738Sbapt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20247738Sbapt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21247738Sbapt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22247738Sbapt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23247738Sbapt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24247738Sbapt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25247738Sbapt * SUCH DAMAGE.
26247738Sbapt */
27247738Sbapt
28247738Sbapt#include <sys/cdefs.h>
29247738Sbapt__FBSDID("$FreeBSD: head/sys/boot/powerpc/uboot/metadata.c 176349 2008-02-16 22:40:55Z marcel $");
30247738Sbapt
31247738Sbapt#include <stand.h>
32247738Sbapt#include <sys/param.h>
33247738Sbapt#include <sys/reboot.h>
34247738Sbapt#include <sys/linker.h>
35247738Sbapt
36247738Sbapt#include <machine/elf.h>
37247738Sbapt#include <machine/metadata.h>
38247738Sbapt#include <machine/bootinfo.h>
39247738Sbapt
40247738Sbapt#include "api_public.h"
41247738Sbapt#include "bootstrap.h"
42247738Sbapt
43247738Sbapt/* XXX should this go into header? */
44247738Sbaptstruct sys_info *ub_get_sys_info(void);
45247738Sbapt
46247738Sbapt/*
47247738Sbapt * Return a 'boothowto' value corresponding to the kernel arguments in
48247738Sbapt * (kargs) and any relevant environment variables.
49247738Sbapt */
50247738Sbaptstatic struct
51247738Sbapt{
52247738Sbapt    const char	*ev;
53247738Sbapt    int		mask;
54247738Sbapt} howto_names[] = {
55247738Sbapt    {"boot_askname",	RB_ASKNAME},
56247738Sbapt    {"boot_cdrom",	RB_CDROM},
57247738Sbapt    {"boot_ddb",	RB_KDB},
58247738Sbapt    {"boot_dfltroot",	RB_DFLTROOT},
59247738Sbapt    {"boot_gdb",	RB_GDB},
60247738Sbapt    {"boot_multicons",	RB_MULTIPLE},
61247738Sbapt    {"boot_mute",	RB_MUTE},
62247738Sbapt    {"boot_pause",	RB_PAUSE},
63247738Sbapt    {"boot_serial",	RB_SERIAL},
64247738Sbapt    {"boot_single",	RB_SINGLE},
65247738Sbapt    {"boot_verbose",	RB_VERBOSE},
66247738Sbapt    {NULL,	0}
67247738Sbapt};
68247738Sbapt
69247738Sbaptint
70247738Sbaptmd_getboothowto(char *kargs)
71247738Sbapt{
72247738Sbapt    char	*cp;
73247738Sbapt    int		howto;
74247738Sbapt    int		active;
75247738Sbapt    int		i;
76247738Sbapt
77247738Sbapt    /* Parse kargs */
78247738Sbapt    howto = 0;
79247738Sbapt    if (kargs != NULL) {
80247738Sbapt	cp = kargs;
81247738Sbapt	active = 0;
82247738Sbapt	while (*cp != 0) {
83247738Sbapt	    if (!active && (*cp == '-')) {
84247738Sbapt		active = 1;
85	    } else if (active)
86		switch (*cp) {
87		case 'a':
88		    howto |= RB_ASKNAME;
89		    break;
90		case 'C':
91		    howto |= RB_CDROM;
92		    break;
93		case 'd':
94		    howto |= RB_KDB;
95		    break;
96		case 'D':
97		    howto |= RB_MULTIPLE;
98		    break;
99		case 'm':
100		    howto |= RB_MUTE;
101		    break;
102		case 'g':
103		    howto |= RB_GDB;
104		    break;
105		case 'h':
106		    howto |= RB_SERIAL;
107		    break;
108		case 'p':
109		    howto |= RB_PAUSE;
110		    break;
111		case 'r':
112		    howto |= RB_DFLTROOT;
113		    break;
114		case 's':
115		    howto |= RB_SINGLE;
116		    break;
117		case 'v':
118		    howto |= RB_VERBOSE;
119		    break;
120		default:
121		    active = 0;
122		    break;
123		}
124	    cp++;
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    if (!strcmp(getenv("console"), "comconsole"))
132	howto |= RB_SERIAL;
133    if (!strcmp(getenv("console"), "nullconsole"))
134	howto |= RB_MUTE;
135    return(howto);
136}
137
138/*
139 * Copy the environment into the load area starting at (addr).
140 * Each variable is formatted as <name>=<value>, with a single nul
141 * separating each variable, and a double nul terminating the environment.
142 */
143vm_offset_t
144md_copyenv(vm_offset_t addr)
145{
146    struct env_var	*ep;
147
148    /* traverse the environment */
149    for (ep = environ; ep != NULL; ep = ep->ev_next) {
150	archsw.arch_copyin(ep->ev_name, addr, strlen(ep->ev_name));
151	addr += strlen(ep->ev_name);
152	archsw.arch_copyin("=", addr, 1);
153	addr++;
154	if (ep->ev_value != NULL) {
155	    archsw.arch_copyin(ep->ev_value, addr, strlen(ep->ev_value));
156	    addr += strlen(ep->ev_value);
157	}
158	archsw.arch_copyin("", addr, 1);
159	addr++;
160    }
161    archsw.arch_copyin("", addr, 1);
162    addr++;
163    return(addr);
164}
165
166/*
167 * Copy module-related data into the load area, where it can be
168 * used as a directory for loaded modules.
169 *
170 * Module data is presented in a self-describing format.  Each datum
171 * is preceded by a 32-bit identifier and a 32-bit size field.
172 *
173 * Currently, the following data are saved:
174 *
175 * MOD_NAME	(variable)		module name (string)
176 * MOD_TYPE	(variable)		module type (string)
177 * MOD_ARGS	(variable)		module parameters (string)
178 * MOD_ADDR	sizeof(vm_offset_t)	module load address
179 * MOD_SIZE	sizeof(size_t)		module size
180 * MOD_METADATA	(variable)		type-specific metadata
181 */
182#define COPY32(v, a, c) {			\
183    u_int32_t	x = (v);			\
184    if (c)					\
185        archsw.arch_copyin(&x, a, sizeof(x));	\
186    a += sizeof(x);				\
187}
188
189#define MOD_STR(t, a, s, c) {			\
190    COPY32(t, a, c);				\
191    COPY32(strlen(s) + 1, a, c)			\
192    if (c)					\
193        archsw.arch_copyin(s, a, strlen(s) + 1);\
194    a += roundup(strlen(s) + 1, sizeof(u_long));\
195}
196
197#define MOD_NAME(a, s, c)	MOD_STR(MODINFO_NAME, a, s, c)
198#define MOD_TYPE(a, s, c)	MOD_STR(MODINFO_TYPE, a, s, c)
199#define MOD_ARGS(a, s, c)	MOD_STR(MODINFO_ARGS, a, s, c)
200
201#define MOD_VAR(t, a, s, c) {			\
202    COPY32(t, a, c);				\
203    COPY32(sizeof(s), a, c);			\
204    if (c)					\
205        archsw.arch_copyin(&s, a, sizeof(s));	\
206    a += roundup(sizeof(s), sizeof(u_long));	\
207}
208
209#define MOD_ADDR(a, s, c)	MOD_VAR(MODINFO_ADDR, a, s, c)
210#define MOD_SIZE(a, s, c)	MOD_VAR(MODINFO_SIZE, a, s, c)
211
212#define MOD_METADATA(a, mm, c) {		\
213    COPY32(MODINFO_METADATA | mm->md_type, a, c);\
214    COPY32(mm->md_size, a, c);			\
215    if (c)					\
216        archsw.arch_copyin(mm->md_data, a, mm->md_size);\
217    a += roundup(mm->md_size, sizeof(u_long));	\
218}
219
220#define MOD_END(a, c) {				\
221    COPY32(MODINFO_END, a, c);			\
222    COPY32(0, a, c);				\
223}
224
225vm_offset_t
226md_copymodules(vm_offset_t addr)
227{
228    struct preloaded_file	*fp;
229    struct file_metadata	*md;
230    int				c;
231
232    c = addr != 0;
233    /* start with the first module on the list, should be the kernel */
234    for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
235
236	MOD_NAME(addr, fp->f_name, c);	/* this field must come first */
237	MOD_TYPE(addr, fp->f_type, c);
238	if (fp->f_args)
239	    MOD_ARGS(addr, fp->f_args, c);
240	MOD_ADDR(addr, fp->f_addr, c);
241	MOD_SIZE(addr, fp->f_size, c);
242	for (md = fp->f_metadata; md != NULL; md = md->md_next) {
243	    if (!(md->md_type & MODINFOMD_NOCOPY)) {
244		MOD_METADATA(addr, md, c);
245	    }
246	}
247    }
248    MOD_END(addr, c);
249    return(addr);
250}
251
252/*
253 * Load the information expected by a powerpc kernel.
254 *
255 * - The 'boothowto' argument is constructed
256 * - The 'bootdev' argument is constructed
257 * - The kernel environment is copied into kernel space.
258 * - Module metadata are formatted and placed in kernel space.
259 */
260int
261md_load(char *args, vm_offset_t *modulep)
262{
263    struct bootinfo		bootinfo;
264    struct preloaded_file	*kfp;
265    struct preloaded_file	*xp;
266    struct file_metadata	*md;
267    vm_offset_t			kernend;
268    vm_offset_t			addr;
269    vm_offset_t			envp;
270    vm_offset_t			size;
271    vm_offset_t			vaddr;
272    struct sys_info		*si;
273    char			*rootdevname;
274    int				howto;
275    int				i;
276
277    /* This metadata addreses must be converted for kernel after relocation */
278    uint32_t			mdt[] = { MODINFOMD_SSYM, MODINFOMD_ESYM,
279	    				MODINFOMD_KERNEND, MODINFOMD_ENVP };
280
281    howto = md_getboothowto(args);
282
283    /*
284     * Allow the environment variable 'rootdev' to override the supplied device
285     * This should perhaps go to MI code and/or have $rootdev tested/set by
286     * MI code before launching the kernel.
287     */
288    rootdevname = getenv("rootdev");
289    if (rootdevname == NULL)
290	    rootdevname = getenv("currdev");
291    /* Try reading the /etc/fstab file to select the root device */
292    getrootmount(rootdevname);
293
294    /* find the last module in the chain */
295    addr = 0;
296    for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
297	if (addr < (xp->f_addr + xp->f_size))
298	    addr = xp->f_addr + xp->f_size;
299    }
300    /* pad to a page boundary */
301    addr = roundup(addr, PAGE_SIZE);
302
303    /* copy our environment */
304    envp = addr;
305    addr = md_copyenv(addr);
306
307    /* pad to a page boundary */
308    addr = roundup(addr, PAGE_SIZE);
309
310    /* Fill information structure */
311    if (!(si = ub_get_sys_info()))
312	panic("can't retrieve U-Boot sysinfo");
313
314    /* Extract mem info */
315    for (i = 0; i < si->mr_no; i++)
316        if (si->mr[i].flags == MR_ATTR_DRAM) {
317	    bootinfo.mem_base = si->mr[i].start;
318	    bootinfo.mem_size = si->mr[i].size;
319	    break;
320	}
321
322    if (i == si->mr_no)
323        panic("can't retrieve memory info");
324
325    bootinfo.version = 1;
326    bootinfo.bar_base = si->bar;
327    bootinfo.cpu_clk = si->clk_cpu;
328    bootinfo.bus_clk = si->clk_bus;
329
330#if 0
331    memcpy(bootinfo.eth0_addr, bd->bi_enetaddr, sizeof(bootinfo.eth0_addr));
332#ifdef CONFIG_HAS_ETH1
333    memcpy(bootinfo.eth1_addr, bd->bi_enet1addr, sizeof(bootinfo.eth1_addr));
334#endif
335#endif
336
337    kernend = 0;
338    kfp = file_findfile(NULL, "elf32 kernel");
339    if (kfp == NULL)
340	kfp = file_findfile(NULL, "elf kernel");
341    if (kfp == NULL)
342	panic("can't find kernel file");
343    file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
344    file_addmetadata(kfp, MODINFOMD_BOOTINFO, sizeof bootinfo, &bootinfo);
345    file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
346    file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
347
348    *modulep = addr;
349    size = md_copymodules(0);
350    kernend = roundup(addr + size, PAGE_SIZE);
351
352    md = file_findmetadata(kfp, MODINFOMD_KERNEND);
353    bcopy(&kernend, md->md_data, sizeof kernend);
354
355    /* Convert addresses to the final VA */
356    *modulep -= __elfN(relocation_offset);
357
358    for (i = 0; i < sizeof mdt / sizeof mdt[0]; i++) {
359    	md = file_findmetadata(kfp, mdt[i]);
360    	if (md) {
361		bcopy(md->md_data, &vaddr, sizeof vaddr);
362		vaddr -= __elfN(relocation_offset);
363		bcopy(&vaddr, md->md_data, sizeof vaddr);
364    	}
365    }
366    (void)md_copymodules(addr);
367
368    return(0);
369}
370