2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3 * Copyright (c) 2004, 2006 Marcel Moolenaar
4 * Copyright (c) 2014 The FreeBSD Foundation
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
29 #include <sys/cdefs.h>
33 #include <sys/param.h>
34 #include <sys/reboot.h>
35 #include <sys/linker.h>
37 #include <machine/cpufunc.h>
38 #include <machine/elf.h>
39 #include <machine/metadata.h>
40 #include <machine/psl.h>
45 #include "bootstrap.h"
46 #include "loader_efi.h"
48 #if defined(__amd64__)
49 #include <machine/specialreg.h>
50 #include "framebuffer.h"
53 #if defined(LOADER_FDT_SUPPORT)
54 #include <fdt_platform.h>
57 int bi_load(char *args
, vm_offset_t
*modulep
, vm_offset_t
*kernendp
);
59 extern EFI_SYSTEM_TABLE
*ST
;
61 static const char howto_switches
[] = "aCdrgDmphsv";
62 static int howto_masks
[] = {
63 RB_ASKNAME
, RB_CDROM
, RB_KDB
, RB_DFLTROOT
, RB_GDB
, RB_MULTIPLE
,
64 RB_MUTE
, RB_PAUSE
, RB_SERIAL
, RB_SINGLE
, RB_VERBOSE
68 bi_getboothowto(char *kargs
)
77 /* Get the boot options from the environment first. */
78 for (i
= 0; howto_names
[i
].ev
!= NULL
; i
++) {
79 if (getenv(howto_names
[i
].ev
) != NULL
)
80 howto
|= howto_names
[i
].mask
;
83 console
= getenv("console");
84 if (console
!= NULL
) {
85 if (strcmp(console
, "comconsole") == 0)
87 if (strcmp(console
, "nullconsole") == 0)
95 opts
= strchr(kargs
, '-');
96 while (opts
!= NULL
) {
97 while (*(++opts
) != '\0') {
98 sw
= strchr(howto_switches
, *opts
);
101 howto
|= howto_masks
[sw
- howto_switches
];
103 opts
= strchr(opts
, '-');
110 * Copy the environment into the load area starting at (addr).
111 * Each variable is formatted as <name>=<value>, with a single nul
112 * separating each variable, and a double nul terminating the environment.
115 bi_copyenv(vm_offset_t start
)
118 vm_offset_t addr
, last
;
123 /* Traverse the environment. */
124 for (ep
= environ
; ep
!= NULL
; ep
= ep
->ev_next
) {
125 len
= strlen(ep
->ev_name
);
126 if ((size_t)archsw
.arch_copyin(ep
->ev_name
, addr
, len
) != len
)
129 if (archsw
.arch_copyin("=", addr
, 1) != 1)
132 if (ep
->ev_value
!= NULL
) {
133 len
= strlen(ep
->ev_value
);
134 if ((size_t)archsw
.arch_copyin(ep
->ev_value
, addr
, len
) != len
)
138 if (archsw
.arch_copyin("", addr
, 1) != 1)
143 if (archsw
.arch_copyin("", last
++, 1) != 1)
149 * Copy module-related data into the load area, where it can be
150 * used as a directory for loaded modules.
152 * Module data is presented in a self-describing format. Each datum
153 * is preceded by a 32-bit identifier and a 32-bit size field.
155 * Currently, the following data are saved:
157 * MOD_NAME (variable) module name (string)
158 * MOD_TYPE (variable) module type (string)
159 * MOD_ARGS (variable) module parameters (string)
160 * MOD_ADDR sizeof(vm_offset_t) module load address
161 * MOD_SIZE sizeof(size_t) module size
162 * MOD_METADATA (variable) type-specific metadata
164 #define COPY32(v, a, c) { \
167 archsw.arch_copyin(&x, a, sizeof(x)); \
171 #define MOD_STR(t, a, s, c) { \
173 COPY32(strlen(s) + 1, a, c); \
175 archsw.arch_copyin(s, a, strlen(s) + 1); \
176 a += roundup(strlen(s) + 1, sizeof(u_long)); \
179 #define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c)
180 #define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c)
181 #define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c)
183 #define MOD_VAR(t, a, s, c) { \
185 COPY32(sizeof(s), a, c); \
187 archsw.arch_copyin(&s, a, sizeof(s)); \
188 a += roundup(sizeof(s), sizeof(u_long)); \
191 #define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c)
192 #define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c)
194 #define MOD_METADATA(a, mm, c) { \
195 COPY32(MODINFO_METADATA | mm->md_type, a, c); \
196 COPY32(mm->md_size, a, c); \
198 archsw.arch_copyin(mm->md_data, a, mm->md_size); \
199 a += roundup(mm->md_size, sizeof(u_long)); \
202 #define MOD_END(a, c) { \
203 COPY32(MODINFO_END, a, c); \
208 bi_copymodules(vm_offset_t addr
)
210 struct preloaded_file
*fp
;
211 struct file_metadata
*md
;
216 /* Start with the first module on the list, should be the kernel. */
217 for (fp
= file_findfile(NULL
, NULL
); fp
!= NULL
; fp
= fp
->f_next
) {
218 MOD_NAME(addr
, fp
->f_name
, c
); /* This must come first. */
219 MOD_TYPE(addr
, fp
->f_type
, c
);
221 MOD_ARGS(addr
, fp
->f_args
, c
);
224 v
-= __elfN(relocation_offset
);
226 MOD_ADDR(addr
, v
, c
);
228 MOD_SIZE(addr
, v
, c
);
229 for (md
= fp
->f_metadata
; md
!= NULL
; md
= md
->md_next
)
230 if (!(md
->md_type
& MODINFOMD_NOCOPY
))
231 MOD_METADATA(addr
, md
, c
);
238 bi_load_efi_data(struct preloaded_file
*kfp
)
240 EFI_MEMORY_DESCRIPTOR
*mm
;
241 EFI_PHYSICAL_ADDRESS addr
;
245 UINTN mmsz
, pages
, retry
, sz
;
247 struct efi_map_header
*efihdr
;
249 #if defined(__amd64__)
252 if (efi_find_framebuffer(&efifb
) == 0) {
253 printf("EFI framebuffer information:\n");
254 printf("addr, size 0x%lx, 0x%lx\n", efifb
.fb_addr
,
256 printf("dimensions %d x %d\n", efifb
.fb_width
,
258 printf("stride %d\n", efifb
.fb_stride
);
259 printf("masks 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
260 efifb
.fb_mask_red
, efifb
.fb_mask_green
, efifb
.fb_mask_blue
,
261 efifb
.fb_mask_reserved
);
263 file_addmetadata(kfp
, MODINFOMD_EFI_FB
, sizeof(efifb
), &efifb
);
267 efisz
= (sizeof(struct efi_map_header
) + 0xf) & ~0xf;
270 * It is possible that the first call to ExitBootServices may change
271 * the map key. Fetch a new map key and retry ExitBootServices in that
274 for (retry
= 2; retry
> 0; retry
--) {
276 * Allocate enough pages to hold the bootinfo block and the
277 * memory map EFI will return to us. The memory map has an
278 * unknown size, so we have to determine that first. Note that
279 * the AllocatePages call can itself modify the memory map, so
280 * we have to take that into account as well. The changes to
281 * the memory map are caused by splitting a range of free
282 * memory into two (AFAICT), so that one is marked as being
286 BS
->GetMemoryMap(&sz
, NULL
, &efi_mapkey
, &mmsz
, &mmver
);
288 sz
= (sz
+ 0xf) & ~0xf;
289 pages
= EFI_SIZE_TO_PAGES(sz
+ efisz
);
290 status
= BS
->AllocatePages(AllocateAnyPages
, EfiLoaderData
,
292 if (EFI_ERROR(status
)) {
293 printf("%s: AllocatePages error %lu\n", __func__
,
294 EFI_ERROR_CODE(status
));
299 * Read the memory map and stash it after bootinfo. Align the
300 * memory map on a 16-byte boundary (the bootinfo block is page
303 efihdr
= (struct efi_map_header
*)addr
;
304 mm
= (void *)((uint8_t *)efihdr
+ efisz
);
305 sz
= (EFI_PAGE_SIZE
* pages
) - efisz
;
307 status
= BS
->GetMemoryMap(&sz
, mm
, &efi_mapkey
, &mmsz
, &mmver
);
308 if (EFI_ERROR(status
)) {
309 printf("%s: GetMemoryMap error %lu\n", __func__
,
310 EFI_ERROR_CODE(status
));
313 status
= BS
->ExitBootServices(IH
, efi_mapkey
);
314 if (EFI_ERROR(status
) == 0) {
315 efihdr
->memory_size
= sz
;
316 efihdr
->descriptor_size
= mmsz
;
317 efihdr
->descriptor_version
= mmver
;
318 file_addmetadata(kfp
, MODINFOMD_EFI_MAP
, efisz
+ sz
,
322 BS
->FreePages(addr
, pages
);
324 printf("ExitBootServices error %lu\n", EFI_ERROR_CODE(status
));
329 * Load the information expected by an amd64 kernel.
331 * - The 'boothowto' argument is constructed.
332 * - The 'bootdev' argument is constructed.
333 * - The 'bootinfo' struct is constructed, and copied into the kernel space.
334 * - The kernel environment is copied into kernel space.
335 * - Module metadata are formatted and placed in kernel space.
338 bi_load(char *args
, vm_offset_t
*modulep
, vm_offset_t
*kernendp
)
340 struct preloaded_file
*xp
, *kfp
;
341 struct devdesc
*rootdev
;
342 struct file_metadata
*md
;
349 #if defined(LOADER_FDT_SUPPORT)
357 * These metadata addreses must be converted for kernel after
361 MODINFOMD_SSYM
, MODINFOMD_ESYM
, MODINFOMD_KERNEND
,
363 #if defined(LOADER_FDT_SUPPORT)
369 howto
= bi_getboothowto(args
);
372 * Allow the environment variable 'rootdev' to override the supplied
373 * device. This should perhaps go to MI code and/or have $rootdev
374 * tested/set by MI code before launching the kernel.
376 rootdevname
= getenv("rootdev");
377 archsw
.arch_getdev((void**)(&rootdev
), rootdevname
, NULL
);
378 if (rootdev
== NULL
) {
379 printf("Can't determine root device.\n");
383 /* Try reading the /etc/fstab file to select the root device */
384 getrootmount(efi_fmtdev((void *)rootdev
));
387 for (xp
= file_findfile(NULL
, NULL
); xp
!= NULL
; xp
= xp
->f_next
) {
388 if (addr
< (xp
->f_addr
+ xp
->f_size
))
389 addr
= xp
->f_addr
+ xp
->f_size
;
392 /* Pad to a page boundary. */
393 addr
= roundup(addr
, PAGE_SIZE
);
395 /* Copy our environment. */
397 addr
= bi_copyenv(addr
);
399 /* Pad to a page boundary. */
400 addr
= roundup(addr
, PAGE_SIZE
);
402 #if defined(LOADER_FDT_SUPPORT)
403 /* Handle device tree blob */
405 dtb_size
= fdt_copy(addr
);
407 /* Pad to a page boundary */
409 addr
+= roundup(dtb_size
, PAGE_SIZE
);
412 kfp
= file_findfile(NULL
, "elf kernel");
414 kfp
= file_findfile(NULL
, "elf64 kernel");
416 panic("can't find kernel file");
417 kernend
= 0; /* fill it in later */
418 file_addmetadata(kfp
, MODINFOMD_HOWTO
, sizeof howto
, &howto
);
419 file_addmetadata(kfp
, MODINFOMD_ENVP
, sizeof envp
, &envp
);
420 #if defined(LOADER_FDT_SUPPORT)
422 file_addmetadata(kfp
, MODINFOMD_DTBP
, sizeof dtbp
, &dtbp
);
424 pager_output("WARNING! Trying to fire up the kernel, but no "
425 "device tree blob found!\n");
427 file_addmetadata(kfp
, MODINFOMD_KERNEND
, sizeof kernend
, &kernend
);
428 file_addmetadata(kfp
, MODINFOMD_FW_HANDLE
, sizeof ST
, &ST
);
430 bi_load_efi_data(kfp
);
432 /* Figure out the size and location of the metadata. */
434 size
= bi_copymodules(0);
435 kernend
= roundup(addr
+ size
, PAGE_SIZE
);
438 /* patch MODINFOMD_KERNEND */
439 md
= file_findmetadata(kfp
, MODINFOMD_KERNEND
);
440 bcopy(&kernend
, md
->md_data
, sizeof kernend
);
443 *modulep
-= __elfN(relocation_offset
);
445 /* Do relocation fixup on metadata of each module. */
446 for (xp
= file_findfile(NULL
, NULL
); xp
!= NULL
; xp
= xp
->f_next
) {
447 for (i
= 0; i
< nitems(mdt
); i
++) {
448 md
= file_findmetadata(xp
, mdt
[i
]);
450 bcopy(md
->md_data
, &vaddr
, sizeof vaddr
);
451 vaddr
-= __elfN(relocation_offset
);
452 bcopy(&vaddr
, md
->md_data
, sizeof vaddr
);
458 /* Copy module list and metadata. */
459 (void)bi_copymodules(addr
);