]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/boot/uboot/common/metadata.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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         vm_offset_t a;
235
236         c = addr != 0;
237         /* start with the first module on the list, should be the kernel */
238         for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
239
240                 MOD_NAME(addr, fp->f_name, c);  /* this field must be first */
241                 MOD_TYPE(addr, fp->f_type, c);
242                 if (fp->f_args)
243                         MOD_ARGS(addr, fp->f_args, c);
244                 a = fp->f_addr - __elfN(relocation_offset);
245                 MOD_ADDR(addr, a, c);
246                 MOD_SIZE(addr, fp->f_size, c);
247                 for (md = fp->f_metadata; md != NULL; md = md->md_next) {
248                         if (!(md->md_type & MODINFOMD_NOCOPY))
249                                 MOD_METADATA(addr, md, c);
250                 }
251         }
252         MOD_END(addr, c);
253         return(addr);
254 }
255
256 /*
257  * Prepare the bootinfo structure. Put a ptr to the allocated struct in addr,
258  * return size.
259  */
260 static int
261 md_bootinfo(struct bootinfo **addr)
262 {
263 #define TMP_MAX_ETH     8
264 #define TMP_MAX_MR      8
265         struct bootinfo         *bi;
266         struct bi_mem_region    tmp_mr[TMP_MAX_MR];
267         struct bi_eth_addr      tmp_eth[TMP_MAX_ETH];
268         struct sys_info         *si;
269         char                    *str, *end;
270         const char              *env;
271         void                    *ptr;
272         u_int8_t                tmp_addr[6];
273         int                     i, n, mr_no, eth_no, size;
274
275         if ((si = ub_get_sys_info()) == NULL)
276                 panic("can't retrieve U-Boot sysinfo");
277
278         /*
279          * Handle mem regions (we only care about DRAM)
280          */
281         for (i = 0, mr_no = 0; i < si->mr_no; i++) {
282                 if (si->mr[i].flags == MR_ATTR_DRAM) {
283                         if (mr_no >= TMP_MAX_MR) {
284                                 printf("too many memory regions: %d\n", mr_no);
285                                 break;
286                         }
287                         tmp_mr[mr_no].mem_base = si->mr[i].start;
288                         tmp_mr[mr_no].mem_size = si->mr[i].size;
289                         mr_no++;
290                         continue;
291                 }
292         }
293         if (mr_no == 0)
294                 panic("can't retrieve RAM info");
295
296         size = (mr_no * sizeof(struct bi_mem_region) - sizeof(bi->bi_data));
297
298         /*
299          * Handle Ethernet addresses: parse u-boot env for eth%daddr
300          */
301         env = NULL;
302         eth_no = 0;
303         while ((env = ub_env_enum(env)) != NULL) {
304                 if (strncmp(env, "eth", 3) == 0 &&
305                     strncmp(env + (strlen(env) - 4), "addr", 4) == 0) {
306
307                         /* Extract interface number */
308                         i = strtol(env + 3, &end, 10);
309                         if (end == (env + 3))
310                                 /* 'ethaddr' means interface 0 address */
311                                 n = 0;
312                         else
313                                 n = i;
314
315                         if (n >= TMP_MAX_MR) {
316                                 printf("Ethernet interface number too high: %d. "
317                                     "Skipping...\n");
318                                 continue;
319                         }
320
321                         str = ub_env_get(env);
322                         for (i = 0; i < 6; i++) {
323                                 tmp_addr[i] = str ? strtol(str, &end, 16) : 0;
324                                 if (str)
325                                         str = (*end) ? end + 1 : end;
326
327                                 tmp_eth[n].mac_addr[i] = tmp_addr[i];
328                         }
329
330                         /* eth_no is 1-based number of all interfaces defined */
331                         if (n + 1 > eth_no)
332                                 eth_no = n + 1;
333                 }
334         }
335
336         size += (eth_no * sizeof(struct bi_eth_addr)) + sizeof(struct bootinfo);
337
338         /*
339          * Once its whole size is calculated, allocate space for the bootinfo
340          * and copy over the contents from temp containers.
341          */
342         if ((bi = malloc(size)) == NULL)
343                 panic("can't allocate mem for bootinfo");
344
345         ptr = (struct bi_mem_region *)bi->bi_data;
346         bcopy(tmp_mr, ptr, mr_no * sizeof(struct bi_mem_region));
347         ptr += mr_no * sizeof(struct bi_mem_region);
348         bcopy(tmp_eth, ptr, eth_no * sizeof(struct bi_eth_addr));
349
350         bi->bi_mem_reg_no = mr_no;
351         bi->bi_eth_addr_no = eth_no;
352         bi->bi_version = BI_VERSION;
353         bi->bi_bar_base = si->bar;
354         bi->bi_cpu_clk = si->clk_cpu;
355         bi->bi_bus_clk = si->clk_bus;
356
357         *addr = bi;
358
359         return (size);
360 }
361
362 /*
363  * Load the information expected by a powerpc kernel.
364  *
365  * - The 'boothowto' argument is constructed
366  * - The 'bootdev' argument is constructed
367  * - The kernel environment is copied into kernel space.
368  * - Module metadata are formatted and placed in kernel space.
369  */
370 int
371 md_load(char *args, vm_offset_t *modulep)
372 {
373         struct preloaded_file   *kfp;
374         struct preloaded_file   *xp;
375         struct file_metadata    *md;
376         struct bootinfo         *bip;
377         vm_offset_t             kernend;
378         vm_offset_t             addr;
379         vm_offset_t             envp;
380         vm_offset_t             size;
381         vm_offset_t             vaddr;
382         char                    *rootdevname;
383         int                     howto;
384         int                     bisize;
385         int                     i;
386
387         /*
388          * These metadata addreses must be converted for kernel after
389          * relocation.
390          */
391         uint32_t                mdt[] = {
392             MODINFOMD_SSYM, MODINFOMD_ESYM, MODINFOMD_KERNEND, MODINFOMD_ENVP
393         };
394
395         howto = md_getboothowto(args);
396
397         /*
398          * Allow the environment variable 'rootdev' to override the supplied
399          * device. This should perhaps go to MI code and/or have $rootdev
400          * tested/set by MI code before launching the kernel.
401          */
402         rootdevname = getenv("rootdev");
403         if (rootdevname == NULL)
404                 rootdevname = getenv("currdev");
405         /* Try reading the /etc/fstab file to select the root device */
406         getrootmount(rootdevname);
407
408         /* find the last module in the chain */
409         addr = 0;
410         for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
411                 if (addr < (xp->f_addr + xp->f_size))
412                         addr = xp->f_addr + xp->f_size;
413         }
414         /* pad to a page boundary */
415         addr = roundup(addr, PAGE_SIZE);
416
417         /* copy our environment */
418         envp = addr;
419         addr = md_copyenv(addr);
420
421         /* pad to a page boundary */
422         addr = roundup(addr, PAGE_SIZE);
423
424         /* prepare bootinfo */
425         bisize = md_bootinfo(&bip);
426
427         kernend = 0;
428         kfp = file_findfile(NULL, "elf32 kernel");
429         if (kfp == NULL)
430                 kfp = file_findfile(NULL, "elf kernel");
431         if (kfp == NULL)
432                 panic("can't find kernel file");
433         file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
434         file_addmetadata(kfp, MODINFOMD_BOOTINFO, bisize, bip);
435         file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
436         file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
437
438         *modulep = addr;
439         size = md_copymodules(0);
440         kernend = roundup(addr + size, PAGE_SIZE);
441
442         md = file_findmetadata(kfp, MODINFOMD_KERNEND);
443         bcopy(&kernend, md->md_data, sizeof kernend);
444
445         /* Convert addresses to the final VA */
446         *modulep -= __elfN(relocation_offset);
447
448         for (i = 0; i < sizeof mdt / sizeof mdt[0]; i++) {
449                 md = file_findmetadata(kfp, mdt[i]);
450                 if (md) {
451                         bcopy(md->md_data, &vaddr, sizeof vaddr);
452                         vaddr -= __elfN(relocation_offset);
453                         bcopy(&vaddr, md->md_data, sizeof vaddr);
454                 }
455         }
456         (void)md_copymodules(addr);
457
458         return(0);
459 }