[BACK]Return to exec.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / stand / efiboot

Annotation of src/sys/stand/efiboot/exec.c, Revision 1.20

1.20    ! skrll       1: /* $NetBSD: exec.c,v 1.19 2020/10/10 19:17:39 jmcneill Exp $ */
1.1       jmcneill    2:
                      3: /*-
1.10      thorpej     4:  * Copyright (c) 2019 Jason R. Thorpe
1.1       jmcneill    5:  * Copyright (c) 2018 Jared McNeill <jmcneill@invisible.ca>
                      6:  * All rights reserved.
                      7:  *
                      8:  * Redistribution and use in source and binary forms, with or without
                      9:  * modification, are permitted provided that the following conditions
                     10:  * are met:
                     11:  * 1. Redistributions of source code must retain the above copyright
                     12:  *    notice, this list of conditions and the following disclaimer.
                     13:  * 2. Redistributions in binary form must reproduce the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer in the
                     15:  *    documentation and/or other materials provided with the distribution.
                     16:  *
                     17:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     18:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     19:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     20:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     21:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     22:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     23:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     24:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     25:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     26:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     27:  * SUCH DAMAGE.
                     28:  */
                     29:
                     30: #include "efiboot.h"
                     31: #include "efifdt.h"
1.7       jmcneill   32: #include "efiacpi.h"
1.14      riastrad   33: #include "efirng.h"
1.16      jmcneill   34: #include "module.h"
1.17      thorpej    35: #include "overlay.h"
1.1       jmcneill   36:
1.16      jmcneill   37: #include <sys/param.h>
1.6       jmcneill   38: #include <sys/reboot.h>
1.1       jmcneill   39:
1.13      jmcneill   40: extern char twiddle_toggle;
                     41:
1.3       jmcneill   42: u_long load_offset = 0;
                     43:
1.4       jmcneill   44: #define        FDT_SPACE       (4 * 1024 * 1024)
1.20    ! skrll      45: #define        FDT_ALIGN       (2 * 1024 * 1024)
1.4       jmcneill   46:
1.14      riastrad   47: static EFI_PHYSICAL_ADDRESS initrd_addr, dtb_addr, rndseed_addr, efirng_addr;
                     48: static u_long initrd_size = 0, dtb_size = 0, rndseed_size = 0, efirng_size = 0;
1.4       jmcneill   49:
                     50: static int
1.10      thorpej    51: load_file(const char *path, u_long extra, bool quiet_errors,
                     52:     EFI_PHYSICAL_ADDRESS *paddr, u_long *psize)
1.4       jmcneill   53: {
                     54:        EFI_STATUS status;
                     55:        struct stat st;
                     56:        ssize_t len;
1.10      thorpej    57:        ssize_t expectedlen;
1.4       jmcneill   58:        int fd;
                     59:
                     60:        if (strlen(path) == 0)
                     61:                return 0;
                     62:
                     63:        fd = open(path, 0);
                     64:        if (fd < 0) {
1.10      thorpej    65:                if (!quiet_errors) {
                     66:                        printf("boot: failed to open %s: %s\n", path,
                     67:                            strerror(errno));
                     68:                }
1.4       jmcneill   69:                return errno;
                     70:        }
                     71:        if (fstat(fd, &st) < 0) {
                     72:                printf("boot: failed to fstat %s: %s\n", path, strerror(errno));
                     73:                close(fd);
                     74:                return errno;
                     75:        }
                     76:        if (st.st_size == 0) {
1.10      thorpej    77:                if (!quiet_errors) {
                     78:                        printf("boot: empty file %s\n", path);
                     79:                }
1.4       jmcneill   80:                close(fd);
                     81:                return EINVAL;
                     82:        }
                     83:
1.10      thorpej    84:        expectedlen = st.st_size;
                     85:        *psize = st.st_size + extra;
1.4       jmcneill   86:
                     87: #ifdef EFIBOOT_ALLOCATE_MAX_ADDRESS
1.5       jmcneill   88:        *paddr = EFIBOOT_ALLOCATE_MAX_ADDRESS;
1.4       jmcneill   89:        status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData,
1.5       jmcneill   90:            EFI_SIZE_TO_PAGES(*psize), paddr);
1.4       jmcneill   91: #else
1.5       jmcneill   92:        *paddr = 0;
1.4       jmcneill   93:        status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAnyPages, EfiLoaderData,
1.5       jmcneill   94:            EFI_SIZE_TO_PAGES(*psize), paddr);
1.4       jmcneill   95: #endif
                     96:        if (EFI_ERROR(status)) {
1.5       jmcneill   97:                printf("Failed to allocate %lu bytes for %s (error %lu)\n",
1.9       jmcneill   98:                    *psize, path, (u_long)status);
1.4       jmcneill   99:                close(fd);
1.10      thorpej   100:                *paddr = 0;
1.4       jmcneill  101:                return ENOMEM;
                    102:        }
                    103:
                    104:        printf("boot: loading %s ", path);
1.10      thorpej   105:        len = read(fd, (void *)(uintptr_t)*paddr, expectedlen);
1.4       jmcneill  106:        close(fd);
                    107:
1.10      thorpej   108:        if (len != expectedlen) {
                    109:                if (len < 0) {
1.4       jmcneill  110:                        printf(": %s\n", strerror(errno));
1.10      thorpej   111:                } else {
                    112:                        printf(": returned %ld (expected %ld)\n", len,
                    113:                            expectedlen);
                    114:                }
1.4       jmcneill  115:                return EIO;
                    116:        }
                    117:
                    118:        printf("done.\n");
                    119:
1.5       jmcneill  120:        efi_dcache_flush(*paddr, *psize);
1.4       jmcneill  121:
                    122:        return 0;
                    123: }
                    124:
1.10      thorpej   125: static void
1.17      thorpej   126: apply_overlay(const char *path, void *dtbo)
1.10      thorpej   127: {
                    128:
                    129:        if (!efi_fdt_overlay_is_compatible(dtbo)) {
1.17      thorpej   130:                printf("boot: %s: incompatible overlay\n", path);
1.15      thorpej   131:                return;
1.10      thorpej   132:        }
                    133:
                    134:        int fdterr;
                    135:
                    136:        if (efi_fdt_overlay_apply(dtbo, &fdterr) != 0) {
1.17      thorpej   137:                printf("boot: %s: error %d applying overlay\n", path, fdterr);
1.10      thorpej   138:        }
                    139: }
                    140:
                    141: static void
                    142: apply_overlay_file(const char *path)
                    143: {
                    144:        EFI_PHYSICAL_ADDRESS dtbo_addr;
                    145:        u_long dtbo_size;
                    146:
                    147:        if (strlen(path) == 0)
                    148:                return;
                    149:
                    150:        if (load_file(path, 0, false, &dtbo_addr, &dtbo_size) != 0 ||
                    151:            dtbo_addr == 0) {
                    152:                /* Error messages have already been displayed. */
                    153:                goto out;
                    154:        }
                    155:
1.17      thorpej   156:        apply_overlay(path, (void *)(uintptr_t)dtbo_addr);
1.10      thorpej   157:
                    158: out:
                    159:        if (dtbo_addr) {
                    160:                uefi_call_wrapper(BS->FreePages, 2, dtbo_addr,
                    161:                    EFI_SIZE_TO_PAGES(dtbo_size));
                    162:        }
                    163: }
                    164:
                    165: static void
                    166: load_fdt_overlays(void)
                    167: {
1.17      thorpej   168:        if (!dtoverlay_enabled)
1.10      thorpej   169:                return;
                    170:
1.17      thorpej   171:        dtoverlay_foreach(apply_overlay_file);
1.10      thorpej   172: }
                    173:
1.14      riastrad  174: static void
1.16      jmcneill  175: load_module(const char *module_name)
                    176: {
                    177:        EFI_PHYSICAL_ADDRESS addr;
                    178:        u_long size;
                    179:        char path[PATH_MAX];
                    180:
                    181:        snprintf(path, sizeof(path), "%s/%s/%s.kmod", module_prefix,
                    182:            module_name, module_name);
                    183:
                    184:        if (load_file(path, 0, false, &addr, &size) != 0 || addr == 0 || size == 0)
                    185:                return;
                    186:
                    187:        efi_fdt_module(module_name, (u_long)addr, size);
                    188: }
                    189:
                    190: static void
                    191: load_modules(const char *kernel_name)
                    192: {
                    193:        if (!module_enabled)
                    194:                return;
                    195:
                    196:        module_init(kernel_name);
                    197:        module_foreach(load_module);
                    198: }
                    199:
                    200: static void
1.14      riastrad  201: generate_efirng(void)
                    202: {
                    203:        EFI_PHYSICAL_ADDRESS addr;
                    204:        u_long size = EFI_PAGE_SIZE;
                    205:        EFI_STATUS status;
                    206:
                    207:        /* Check whether the RNG is available before bothering.  */
                    208:        if (!efi_rng_available())
                    209:                return;
                    210:
                    211:        /*
                    212:         * Allocate a page.  This is the smallest unit we can pass into
                    213:         * the kernel conveniently.
                    214:         */
                    215: #ifdef EFIBOOT_ALLOCATE_MAX_ADDRESS
                    216:        addr = EFIBOOT_ALLOCATE_MAX_ADDRESS;
                    217:        status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress,
                    218:            EfiLoaderData, EFI_SIZE_TO_PAGES(size), &addr);
                    219: #else
                    220:        addr = 0;
                    221:        status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAnyPages,
                    222:            EfiLoaderData, EFI_SIZE_TO_PAGES(size), &addr);
                    223: #endif
                    224:        if (EFI_ERROR(status)) {
                    225:                Print(L"Failed to allocate page for EFI RNG output: %r\n",
                    226:                    status);
                    227:                return;
                    228:        }
                    229:
                    230:        /* Fill the page with whatever the EFI RNG will do.  */
                    231:        if (efi_rng((void *)(uintptr_t)addr, size)) {
                    232:                uefi_call_wrapper(BS->FreePages, 2, addr, size);
                    233:                return;
                    234:        }
                    235:
                    236:        /* Success!  */
                    237:        efirng_addr = addr;
                    238:        efirng_size = size;
                    239: }
                    240:
1.1       jmcneill  241: int
                    242: exec_netbsd(const char *fname, const char *args)
                    243: {
                    244:        EFI_PHYSICAL_ADDRESS addr;
                    245:        u_long marks[MARK_MAX], alloc_size;
                    246:        EFI_STATUS status;
1.6       jmcneill  247:        int fd, ohowto;
1.1       jmcneill  248:
1.10      thorpej   249:        load_file(get_initrd_path(), 0, false, &initrd_addr, &initrd_size);
                    250:        load_file(get_dtb_path(), 0, false, &dtb_addr, &dtb_size);
1.14      riastrad  251:        generate_efirng();
1.4       jmcneill  252:
1.1       jmcneill  253:        memset(marks, 0, sizeof(marks));
1.6       jmcneill  254:        ohowto = howto;
                    255:        howto |= AB_SILENT;
1.1       jmcneill  256:        fd = loadfile(fname, marks, COUNT_KERNEL | LOAD_NOTE);
1.6       jmcneill  257:        howto = ohowto;
1.1       jmcneill  258:        if (fd < 0) {
                    259:                printf("boot: %s: %s\n", fname, strerror(errno));
                    260:                return EIO;
                    261:        }
                    262:        close(fd);
1.20    ! skrll     263:        marks[MARK_END] = (((u_long) marks[MARK_END] + sizeof(int) - 1)) & -sizeof(int);
1.4       jmcneill  264:        alloc_size = marks[MARK_END] - marks[MARK_START] + FDT_SPACE + EFIBOOT_ALIGN;
1.1       jmcneill  265:
                    266: #ifdef EFIBOOT_ALLOCATE_MAX_ADDRESS
                    267:        addr = EFIBOOT_ALLOCATE_MAX_ADDRESS;
                    268:        status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData,
                    269:            EFI_SIZE_TO_PAGES(alloc_size), &addr);
                    270: #else
                    271:        addr = 0;
                    272:        status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAnyPages, EfiLoaderData,
                    273:            EFI_SIZE_TO_PAGES(alloc_size), &addr);
                    274: #endif
                    275:        if (EFI_ERROR(status)) {
                    276:                printf("Failed to allocate %lu bytes for kernel image (error %lu)\n",
1.9       jmcneill  277:                    alloc_size, (u_long)status);
1.1       jmcneill  278:                return ENOMEM;
                    279:        }
                    280:
                    281:        memset(marks, 0, sizeof(marks));
1.20    ! skrll     282:        load_offset = (addr + EFIBOOT_ALIGN - 1) & -EFIBOOT_ALIGN;
1.1       jmcneill  283:        fd = loadfile(fname, marks, LOAD_KERNEL);
                    284:        if (fd < 0) {
                    285:                printf("boot: %s: %s\n", fname, strerror(errno));
                    286:                goto cleanup;
                    287:        }
                    288:        close(fd);
1.3       jmcneill  289:        load_offset = 0;
1.1       jmcneill  290:
1.7       jmcneill  291: #ifdef EFIBOOT_ACPI
                    292:        if (efi_acpi_available()) {
                    293:                efi_acpi_create_fdt();
                    294:        } else
                    295: #endif
1.9       jmcneill  296:        if (dtb_addr && efi_fdt_set_data((void *)(uintptr_t)dtb_addr) != 0) {
1.5       jmcneill  297:                printf("boot: invalid DTB data\n");
                    298:                goto cleanup;
                    299:        }
                    300:
1.1       jmcneill  301:        if (efi_fdt_size() > 0) {
1.12      riastrad  302:                /*
                    303:                 * Load the rndseed as late as possible -- after we
                    304:                 * have committed to using fdt and executing this
                    305:                 * kernel -- so that it doesn't hang around in memory
                    306:                 * if we have to bail or the kernel won't use it.
                    307:                 */
                    308:                load_file(get_rndseed_path(), 0, false,
                    309:                    &rndseed_addr, &rndseed_size);
                    310:
1.20    ! skrll     311:                efi_fdt_init((marks[MARK_END] + FDT_ALIGN - 1) & -FDT_ALIGN, FDT_ALIGN);
1.16      jmcneill  312:                load_modules(fname);
1.10      thorpej   313:                load_fdt_overlays();
1.4       jmcneill  314:                efi_fdt_initrd(initrd_addr, initrd_size);
1.12      riastrad  315:                efi_fdt_rndseed(rndseed_addr, rndseed_size);
1.14      riastrad  316:                efi_fdt_efirng(efirng_addr, efirng_size);
1.2       jmcneill  317:                efi_fdt_bootargs(args);
1.19      jmcneill  318:                efi_fdt_system_table();
                    319:                efi_fdt_gop();
1.4       jmcneill  320:                efi_fdt_memory_map();
1.8       jmcneill  321:        }
                    322:
                    323:        efi_cleanup();
                    324:
                    325:        if (efi_fdt_size() > 0) {
1.4       jmcneill  326:                efi_fdt_fini();
1.1       jmcneill  327:        }
                    328:
                    329:        efi_boot_kernel(marks);
                    330:
                    331:        /* This should not happen.. */
                    332:        printf("boot returned\n");
                    333:
                    334: cleanup:
                    335:        uefi_call_wrapper(BS->FreePages, 2, addr, EFI_SIZE_TO_PAGES(alloc_size));
1.4       jmcneill  336:        if (initrd_addr) {
                    337:                uefi_call_wrapper(BS->FreePages, 2, initrd_addr, EFI_SIZE_TO_PAGES(initrd_size));
                    338:                initrd_addr = 0;
                    339:                initrd_size = 0;
                    340:        }
1.5       jmcneill  341:        if (dtb_addr) {
                    342:                uefi_call_wrapper(BS->FreePages, 2, dtb_addr, EFI_SIZE_TO_PAGES(dtb_size));
                    343:                dtb_addr = 0;
                    344:                dtb_size = 0;
                    345:        }
1.1       jmcneill  346:        return EIO;
                    347: }

CVSweb <webmaster@jp.NetBSD.org>