]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/boot/uboot/common/metadata.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.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 #include <sys/boot.h>
37
38 #include <machine/elf.h>
39 #include <machine/metadata.h>
40
41 #include "api_public.h"
42 #include "bootstrap.h"
43 #include "glue.h"
44
45 #if defined(LOADER_FDT_SUPPORT)
46 #include <fdt_platform.h>
47 #endif
48
49 static int
50 md_getboothowto(char *kargs)
51 {
52         char    *cp;
53         char    *p;
54         int     howto;
55         int     active;
56         int     i;
57
58         /* Parse kargs */
59         howto = 0;
60         if (kargs != NULL) {
61                 cp = kargs;
62                 active = 0;
63                 while (*cp != 0) {
64                         if (!active && (*cp == '-'))
65                                 active = 1;
66                         else if (active)
67                                 switch (*cp) {
68                                 case 'a':
69                                         howto |= RB_ASKNAME;
70                                         break;
71                                 case 'C':
72                                         howto |= RB_CDROM;
73                                         break;
74                                 case 'd':
75                                         howto |= RB_KDB;
76                                         break;
77                                 case 'D':
78                                         howto |= RB_MULTIPLE;
79                                         break;
80                                 case 'm':
81                                         howto |= RB_MUTE;
82                                         break;
83                                 case 'g':
84                                         howto |= RB_GDB;
85                                         break;
86                                 case 'h':
87                                         howto |= RB_SERIAL;
88                                         break;
89                                 case 'p':
90                                         howto |= RB_PAUSE;
91                                         break;
92                                 case 'r':
93                                         howto |= RB_DFLTROOT;
94                                         break;
95                                 case 's':
96                                         howto |= RB_SINGLE;
97                                         break;
98                                 case 'v':
99                                         howto |= RB_VERBOSE;
100                                         break;
101                                 default:
102                                         active = 0;
103                                         break;
104                                 }
105                                 cp++;
106                 }
107         }
108
109         /* get equivalents from the environment */
110         for (i = 0; howto_names[i].ev != NULL; i++) {
111                 if (getenv(howto_names[i].ev) != NULL)
112                         howto |= howto_names[i].mask;
113         }
114         if ((p = getenv("console"))) {
115                 if (!strcmp(p, "comconsole"))
116                         howto |= RB_SERIAL;
117                 if (!strcmp(p, "nullconsole"))
118                         howto |= RB_MUTE;
119         }
120
121         return(howto);
122 }
123
124 /*
125  * Copy the environment into the load area starting at (addr).
126  * Each variable is formatted as <name>=<value>, with a single nul
127  * separating each variable, and a double nul terminating the environment.
128  */
129 static vm_offset_t
130 md_copyenv(vm_offset_t addr)
131 {
132         struct env_var  *ep;
133
134         /* traverse the environment */
135         for (ep = environ; ep != NULL; ep = ep->ev_next) {
136                 archsw.arch_copyin(ep->ev_name, addr, strlen(ep->ev_name));
137                 addr += strlen(ep->ev_name);
138                 archsw.arch_copyin("=", addr, 1);
139                 addr++;
140                 if (ep->ev_value != NULL) {
141                         archsw.arch_copyin(ep->ev_value, addr,
142                             strlen(ep->ev_value));
143                         addr += strlen(ep->ev_value);
144                 }
145                 archsw.arch_copyin("", addr, 1);
146                 addr++;
147         }
148         archsw.arch_copyin("", addr, 1);
149         addr++;
150         return(addr);
151 }
152
153 /*
154  * Copy module-related data into the load area, where it can be
155  * used as a directory for loaded modules.
156  *
157  * Module data is presented in a self-describing format.  Each datum
158  * is preceded by a 32-bit identifier and a 32-bit size field.
159  *
160  * Currently, the following data are saved:
161  *
162  * MOD_NAME     (variable)              module name (string)
163  * MOD_TYPE     (variable)              module type (string)
164  * MOD_ARGS     (variable)              module parameters (string)
165  * MOD_ADDR     sizeof(vm_offset_t)     module load address
166  * MOD_SIZE     sizeof(size_t)          module size
167  * MOD_METADATA (variable)              type-specific metadata
168  */
169 #define COPY32(v, a, c) {                       \
170     u_int32_t   x = (v);                        \
171     if (c)                                      \
172         archsw.arch_copyin(&x, a, sizeof(x));   \
173     a += sizeof(x);                             \
174 }
175
176 #define MOD_STR(t, a, s, c) {                   \
177     COPY32(t, a, c);                            \
178     COPY32(strlen(s) + 1, a, c)                 \
179     if (c)                                      \
180         archsw.arch_copyin(s, a, strlen(s) + 1);\
181     a += roundup(strlen(s) + 1, sizeof(u_long));\
182 }
183
184 #define MOD_NAME(a, s, c)       MOD_STR(MODINFO_NAME, a, s, c)
185 #define MOD_TYPE(a, s, c)       MOD_STR(MODINFO_TYPE, a, s, c)
186 #define MOD_ARGS(a, s, c)       MOD_STR(MODINFO_ARGS, a, s, c)
187
188 #define MOD_VAR(t, a, s, c) {                   \
189     COPY32(t, a, c);                            \
190     COPY32(sizeof(s), a, c);                    \
191     if (c)                                      \
192         archsw.arch_copyin(&s, a, sizeof(s));   \
193     a += roundup(sizeof(s), sizeof(u_long));    \
194 }
195
196 #define MOD_ADDR(a, s, c)       MOD_VAR(MODINFO_ADDR, a, s, c)
197 #define MOD_SIZE(a, s, c)       MOD_VAR(MODINFO_SIZE, a, s, c)
198
199 #define MOD_METADATA(a, mm, c) {                \
200     COPY32(MODINFO_METADATA | mm->md_type, a, c);\
201     COPY32(mm->md_size, a, c);                  \
202     if (c)                                      \
203         archsw.arch_copyin(mm->md_data, a, mm->md_size);\
204     a += roundup(mm->md_size, sizeof(u_long));  \
205 }
206
207 #define MOD_END(a, c) {                         \
208     COPY32(MODINFO_END, a, c);                  \
209     COPY32(0, a, c);                            \
210 }
211
212 static vm_offset_t
213 md_copymodules(vm_offset_t addr)
214 {
215         struct preloaded_file   *fp;
216         struct file_metadata    *md;
217         int                     c;
218         vm_offset_t a;
219
220         c = addr != 0;
221         /* start with the first module on the list, should be the kernel */
222         for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
223
224                 MOD_NAME(addr, fp->f_name, c);  /* this field must be first */
225                 MOD_TYPE(addr, fp->f_type, c);
226                 if (fp->f_args)
227                         MOD_ARGS(addr, fp->f_args, c);
228                 a = fp->f_addr - __elfN(relocation_offset);
229                 MOD_ADDR(addr, a, c);
230                 MOD_SIZE(addr, fp->f_size, c);
231                 for (md = fp->f_metadata; md != NULL; md = md->md_next) {
232                         if (!(md->md_type & MODINFOMD_NOCOPY))
233                                 MOD_METADATA(addr, md, c);
234                 }
235         }
236         MOD_END(addr, c);
237         return(addr);
238 }
239
240 /*
241  * Load the information expected by a kernel.
242  *
243  * - The 'boothowto' argument is constructed
244  * - The 'bootdev' argument is constructed
245  * - The kernel environment is copied into kernel space.
246  * - Module metadata are formatted and placed in kernel space.
247  */
248 int
249 md_load(char *args, vm_offset_t *modulep)
250 {
251         struct preloaded_file   *kfp, *bfp;
252         struct preloaded_file   *xp;
253         struct file_metadata    *md;
254         struct bootinfo         *bip;
255         vm_offset_t             kernend;
256         vm_offset_t             addr;
257         vm_offset_t             envp;
258         vm_offset_t             size;
259         vm_offset_t             vaddr;
260 #if defined(LOADER_FDT_SUPPORT)
261         vm_offset_t             dtbp;
262         int                     dtb_size;
263 #endif
264         char                    *rootdevname;
265         int                     howto;
266         int                     i;
267
268         /*
269          * These metadata addreses must be converted for kernel after
270          * relocation.
271          */
272         uint32_t                mdt[] = {
273             MODINFOMD_SSYM, MODINFOMD_ESYM, MODINFOMD_KERNEND,
274             MODINFOMD_ENVP,
275 #if defined(LOADER_FDT_SUPPORT)
276             MODINFOMD_DTBP
277 #endif
278         };
279
280         howto = md_getboothowto(args);
281
282         /*
283          * Allow the environment variable 'rootdev' to override the supplied
284          * device. This should perhaps go to MI code and/or have $rootdev
285          * tested/set by MI code before launching the kernel.
286          */
287         rootdevname = getenv("rootdev");
288         if (rootdevname == NULL)
289                 rootdevname = getenv("currdev");
290         /* Try reading the /etc/fstab file to select the root device */
291         getrootmount(rootdevname);
292
293         /* Find the last module in the chain */
294         addr = 0;
295         for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
296                 if (addr < (xp->f_addr + xp->f_size))
297                         addr = xp->f_addr + xp->f_size;
298         }
299         /* Pad to a page boundary */
300         addr = roundup(addr, PAGE_SIZE);
301
302         /* Copy our environment */
303         envp = addr;
304         addr = md_copyenv(addr);
305
306         /* Pad to a page boundary */
307         addr = roundup(addr, PAGE_SIZE);
308
309 #if defined(LOADER_FDT_SUPPORT)
310         /* Handle device tree blob */
311         dtbp = addr;
312         dtb_size = fdt_copy(addr);
313                 
314         /* Pad to a page boundary */
315         if (dtb_size)
316                 addr += roundup(dtb_size, PAGE_SIZE);
317 #endif
318
319         kernend = 0;
320         kfp = file_findfile(NULL, "elf32 kernel");
321         if (kfp == NULL)
322                 kfp = file_findfile(NULL, "elf kernel");
323         if (kfp == NULL)
324                 panic("can't find kernel file");
325         file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
326         file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
327
328 #if defined(LOADER_FDT_SUPPORT)
329         if (dtb_size)
330                 file_addmetadata(kfp, MODINFOMD_DTBP, sizeof dtbp, &dtbp);
331         else
332                 pager_output("WARNING! Trying to fire up the kernel, but no "
333                     "device tree blob found!\n");
334 #endif
335
336         file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
337
338         /* Figure out the size and location of the metadata */
339         *modulep = addr;
340         size = md_copymodules(0);
341         kernend = roundup(addr + size, PAGE_SIZE);
342
343         /* Provide MODINFOMD_KERNEND */
344         md = file_findmetadata(kfp, MODINFOMD_KERNEND);
345         bcopy(&kernend, md->md_data, sizeof kernend);
346
347         /* Convert addresses to the final VA */
348         *modulep -= __elfN(relocation_offset);
349
350         /* Do relocation fixup on metadata of each module. */
351         for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
352                 for (i = 0; i < sizeof mdt / sizeof mdt[0]; i++) {
353                         md = file_findmetadata(xp, mdt[i]);
354                         if (md) {
355                                 bcopy(md->md_data, &vaddr, sizeof vaddr);
356                                 vaddr -= __elfN(relocation_offset);
357                                 bcopy(&vaddr, md->md_data, sizeof vaddr);
358                         }
359                 }
360         }
361
362         /* Only now copy actual modules and metadata */
363         (void)md_copymodules(addr);
364
365         return (0);
366 }