]> CyberLeo.Net >> Repos - FreeBSD/releng/8.0.git/blob - sys/boot/uboot/common/metadata.c
Adjust to reflect 8.0-RELEASE.
[FreeBSD/releng/8.0.git] / sys / boot / uboot / common / metadata.c
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$");
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  */
49 static 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
68 static int
69 md_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  */
145 static vm_offset_t
146 md_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
228 static vm_offset_t
229 md_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  */
258 static int
259 md_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  */
368 int
369 md_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 }