[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.10.2.3

1.10.2.3! martin      1: /* $NetBSD$ */
1.10.2.2  christos    2:
                      3: /*-
                      4:  * Copyright (c) 2019 Jason R. Thorpe
                      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 "efienv.h"
                     32: #include "efifdt.h"
                     33: #include "efiacpi.h"
                     34:
                     35: #include <sys/reboot.h>
                     36:
1.10.2.3! martin     37: extern char twiddle_toggle;
        !            38:
1.10.2.2  christos   39: u_long load_offset = 0;
                     40:
                     41: #define        FDT_SPACE       (4 * 1024 * 1024)
                     42: #define        FDT_ALIGN       ((2 * 1024 * 1024) - 1)
                     43:
1.10.2.3! martin     44: static EFI_PHYSICAL_ADDRESS initrd_addr, dtb_addr, rndseed_addr;
        !            45: static u_long initrd_size = 0, dtb_size = 0, rndseed_size = 0;
1.10.2.2  christos   46:
                     47: static int
                     48: load_file(const char *path, u_long extra, bool quiet_errors,
                     49:     EFI_PHYSICAL_ADDRESS *paddr, u_long *psize)
                     50: {
                     51:        EFI_STATUS status;
                     52:        struct stat st;
                     53:        ssize_t len;
                     54:        ssize_t expectedlen;
                     55:        int fd;
                     56:
                     57:        if (strlen(path) == 0)
                     58:                return 0;
                     59:
                     60:        fd = open(path, 0);
                     61:        if (fd < 0) {
                     62:                if (!quiet_errors) {
                     63:                        printf("boot: failed to open %s: %s\n", path,
                     64:                            strerror(errno));
                     65:                }
                     66:                return errno;
                     67:        }
                     68:        if (fstat(fd, &st) < 0) {
                     69:                printf("boot: failed to fstat %s: %s\n", path, strerror(errno));
                     70:                close(fd);
                     71:                return errno;
                     72:        }
                     73:        if (st.st_size == 0) {
                     74:                if (!quiet_errors) {
                     75:                        printf("boot: empty file %s\n", path);
                     76:                }
                     77:                close(fd);
                     78:                return EINVAL;
                     79:        }
                     80:
                     81:        expectedlen = st.st_size;
                     82:        *psize = st.st_size + extra;
                     83:
                     84: #ifdef EFIBOOT_ALLOCATE_MAX_ADDRESS
                     85:        *paddr = EFIBOOT_ALLOCATE_MAX_ADDRESS;
                     86:        status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData,
                     87:            EFI_SIZE_TO_PAGES(*psize), paddr);
                     88: #else
                     89:        *paddr = 0;
                     90:        status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAnyPages, EfiLoaderData,
                     91:            EFI_SIZE_TO_PAGES(*psize), paddr);
                     92: #endif
                     93:        if (EFI_ERROR(status)) {
                     94:                printf("Failed to allocate %lu bytes for %s (error %lu)\n",
                     95:                    *psize, path, (u_long)status);
                     96:                close(fd);
                     97:                *paddr = 0;
                     98:                return ENOMEM;
                     99:        }
                    100:
                    101:        printf("boot: loading %s ", path);
                    102:        len = read(fd, (void *)(uintptr_t)*paddr, expectedlen);
                    103:        close(fd);
                    104:
                    105:        if (len != expectedlen) {
                    106:                if (len < 0) {
                    107:                        printf(": %s\n", strerror(errno));
                    108:                } else {
                    109:                        printf(": returned %ld (expected %ld)\n", len,
                    110:                            expectedlen);
                    111:                }
                    112:                return EIO;
                    113:        }
                    114:
                    115:        printf("done.\n");
                    116:
                    117:        efi_dcache_flush(*paddr, *psize);
                    118:
                    119:        return 0;
                    120: }
                    121:
                    122: static const char default_efibootplist_path[] = "/etc/efiboot.plist";
                    123:
                    124: /* This is here because load_file() is here. */
                    125: void
                    126: load_efibootplist(bool default_fallback)
                    127: {
                    128:        EFI_PHYSICAL_ADDRESS plist_addr = 0;
                    129:        u_long plist_size = 0;
                    130:        prop_dictionary_t plist = NULL, oplist = NULL;
                    131:        bool load_quietly = false;
1.10.2.3! martin    132:        bool old_twiddle_toggle = twiddle_toggle;
1.10.2.2  christos  133:
                    134:        const char *path = get_efibootplist_path();
                    135:        if (path == NULL || strlen(path) == 0) {
                    136:                if (!default_fallback)
                    137:                        return;
                    138:                path = default_efibootplist_path;
                    139:                load_quietly = true;
                    140:        }
                    141:
1.10.2.3! martin    142:        twiddle_toggle = load_quietly;
        !           143:
1.10.2.2  christos  144:        /*
                    145:         * Fudge the size so we can ensure the resulting buffer
                    146:         * is NUL-terminated for convenience.
                    147:         */
                    148:        if (load_file(path, 1, load_quietly, &plist_addr, &plist_size) != 0 ||
                    149:            plist_addr == 0) {
                    150:                /* Error messages have already been displayed. */
                    151:                goto out;
                    152:        }
                    153:        char *plist_buf = (char *)((uintptr_t)plist_addr);
                    154:        plist_buf[plist_size - 1] = '\0';
                    155:
                    156:        plist = prop_dictionary_internalize(plist_buf);
                    157:        if (plist == NULL) {
                    158:                printf("boot: unable to parse plist '%s'\n", path);
                    159:                goto out;
                    160:        }
                    161:
                    162: out:
                    163:        oplist = efibootplist;
                    164:
1.10.2.3! martin    165:        twiddle_toggle = old_twiddle_toggle;
        !           166:
1.10.2.2  christos  167:        /*
                    168:         * If we had a failure, create an empty one for
                    169:         * convenience.  But a failure should not clobber
                    170:         * an in-memory plist we already have.
                    171:         */
                    172:        if (plist == NULL &&
                    173:            (oplist == NULL || prop_dictionary_count(oplist) == 0))
                    174:                plist = prop_dictionary_create();
                    175:
                    176: #ifdef EFIBOOT_DEBUG
                    177:        printf(">> load_efibootplist: oplist = 0x%lx, plist = 0x%lx\n",
                    178:            (u_long)oplist, (u_long)plist);
                    179: #endif
                    180:
                    181:        if (plist_addr) {
                    182:                uefi_call_wrapper(BS->FreePages, 2, plist_addr,
                    183:                    EFI_SIZE_TO_PAGES(plist_size));
                    184:        }
                    185:
                    186:        if (plist) {
                    187:                efibootplist = plist;
                    188:                efi_env_from_efibootplist();
                    189:
                    190:                if (oplist)
                    191:                        prop_object_release(oplist);
                    192:        }
                    193: }
                    194:
                    195: static void
                    196: apply_overlay(void *dtbo)
                    197: {
                    198:
                    199:        if (!efi_fdt_overlay_is_compatible(dtbo)) {
                    200:                printf("boot: incompatible overlay\n");
                    201:        }
                    202:
                    203:        int fdterr;
                    204:
                    205:        if (efi_fdt_overlay_apply(dtbo, &fdterr) != 0) {
                    206:                printf("boot: error %d applying overlay\n", fdterr);
                    207:        }
                    208: }
                    209:
                    210: static void
                    211: apply_overlay_file(const char *path)
                    212: {
                    213:        EFI_PHYSICAL_ADDRESS dtbo_addr;
                    214:        u_long dtbo_size;
                    215:
                    216:        if (strlen(path) == 0)
                    217:                return;
                    218:
                    219:        if (load_file(path, 0, false, &dtbo_addr, &dtbo_size) != 0 ||
                    220:            dtbo_addr == 0) {
                    221:                /* Error messages have already been displayed. */
                    222:                goto out;
                    223:        }
                    224:
                    225:        apply_overlay((void *)(uintptr_t)dtbo_addr);
                    226:
                    227: out:
                    228:        if (dtbo_addr) {
                    229:                uefi_call_wrapper(BS->FreePages, 2, dtbo_addr,
                    230:                    EFI_SIZE_TO_PAGES(dtbo_size));
                    231:        }
                    232: }
                    233:
                    234: #define        DT_OVERLAYS_PROP        "device-tree-overlays"
                    235:
                    236: static void
                    237: load_fdt_overlays(void)
                    238: {
                    239:        /*
                    240:         * We support loading device tree overlays specified in efiboot.plist
                    241:         * using the following schema:
                    242:         *
                    243:         *      <key>device-tree-overlays</key>
                    244:         *      <array>
                    245:         *              <string>/path/to/some/overlay.dtbo</string>
                    246:         *              <string>hd0e:/some/other/overlay.dtbo</string>
                    247:         *      </array>
                    248:         *
                    249:         * The overlays are loaded in array order.
                    250:         */
                    251:        prop_array_t overlays = prop_dictionary_get(efibootplist,
                    252:            DT_OVERLAYS_PROP);
                    253:        if (overlays == NULL) {
                    254: #ifdef EFIBOOT_DEBUG
                    255:                printf("boot: no device-tree-overlays\n");
                    256: #endif
                    257:                return;
                    258:        }
                    259:        if (prop_object_type(overlays) != PROP_TYPE_ARRAY) {
                    260:                printf("boot: invalid %s\n", DT_OVERLAYS_PROP);
                    261:                return;
                    262:        }
                    263:
                    264:        prop_object_iterator_t iter = prop_array_iterator(overlays);
                    265:        prop_string_t pathobj;
                    266:        while ((pathobj = prop_object_iterator_next(iter)) != NULL) {
                    267:                if (prop_object_type(pathobj) != PROP_TYPE_STRING) {
                    268:                        printf("boot: invalid %s entry\n", DT_OVERLAYS_PROP);
                    269:                        continue;
                    270:                }
                    271:                apply_overlay_file(prop_string_cstring_nocopy(pathobj));
                    272:        }
                    273:        prop_object_iterator_release(iter);
                    274: }
                    275:
                    276: int
                    277: exec_netbsd(const char *fname, const char *args)
                    278: {
                    279:        EFI_PHYSICAL_ADDRESS addr;
                    280:        u_long marks[MARK_MAX], alloc_size;
                    281:        EFI_STATUS status;
                    282:        int fd, ohowto;
                    283:
                    284:        load_file(get_initrd_path(), 0, false, &initrd_addr, &initrd_size);
                    285:        load_file(get_dtb_path(), 0, false, &dtb_addr, &dtb_size);
                    286:
                    287:        memset(marks, 0, sizeof(marks));
                    288:        ohowto = howto;
                    289:        howto |= AB_SILENT;
                    290:        fd = loadfile(fname, marks, COUNT_KERNEL | LOAD_NOTE);
                    291:        howto = ohowto;
                    292:        if (fd < 0) {
                    293:                printf("boot: %s: %s\n", fname, strerror(errno));
                    294:                return EIO;
                    295:        }
                    296:        close(fd);
                    297:        marks[MARK_END] = (((u_long) marks[MARK_END] + sizeof(int) - 1)) & (-sizeof(int));
                    298:        alloc_size = marks[MARK_END] - marks[MARK_START] + FDT_SPACE + EFIBOOT_ALIGN;
                    299:
                    300: #ifdef EFIBOOT_ALLOCATE_MAX_ADDRESS
                    301:        addr = EFIBOOT_ALLOCATE_MAX_ADDRESS;
                    302:        status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData,
                    303:            EFI_SIZE_TO_PAGES(alloc_size), &addr);
                    304: #else
                    305:        addr = 0;
                    306:        status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAnyPages, EfiLoaderData,
                    307:            EFI_SIZE_TO_PAGES(alloc_size), &addr);
                    308: #endif
                    309:        if (EFI_ERROR(status)) {
                    310:                printf("Failed to allocate %lu bytes for kernel image (error %lu)\n",
                    311:                    alloc_size, (u_long)status);
                    312:                return ENOMEM;
                    313:        }
                    314:
                    315:        memset(marks, 0, sizeof(marks));
                    316:        load_offset = (addr + EFIBOOT_ALIGN) & ~(EFIBOOT_ALIGN - 1);
                    317:        fd = loadfile(fname, marks, LOAD_KERNEL);
                    318:        if (fd < 0) {
                    319:                printf("boot: %s: %s\n", fname, strerror(errno));
                    320:                goto cleanup;
                    321:        }
                    322:        close(fd);
                    323:        load_offset = 0;
                    324:
                    325: #ifdef EFIBOOT_ACPI
                    326:        if (efi_acpi_available()) {
                    327:                efi_acpi_create_fdt();
                    328:        } else
                    329: #endif
                    330:        if (dtb_addr && efi_fdt_set_data((void *)(uintptr_t)dtb_addr) != 0) {
                    331:                printf("boot: invalid DTB data\n");
                    332:                goto cleanup;
                    333:        }
                    334:
                    335:        if (efi_fdt_size() > 0) {
1.10.2.3! martin    336:                /*
        !           337:                 * Load the rndseed as late as possible -- after we
        !           338:                 * have committed to using fdt and executing this
        !           339:                 * kernel -- so that it doesn't hang around in memory
        !           340:                 * if we have to bail or the kernel won't use it.
        !           341:                 */
        !           342:                load_file(get_rndseed_path(), 0, false,
        !           343:                    &rndseed_addr, &rndseed_size);
        !           344:
1.10.2.2  christos  345:                efi_fdt_init((marks[MARK_END] + FDT_ALIGN) & ~FDT_ALIGN, FDT_ALIGN + 1);
                    346:                load_fdt_overlays();
                    347:                efi_fdt_initrd(initrd_addr, initrd_size);
1.10.2.3! martin    348:                efi_fdt_rndseed(rndseed_addr, rndseed_size);
1.10.2.2  christos  349:                efi_fdt_bootargs(args);
                    350:                efi_fdt_memory_map();
                    351:        }
                    352:
                    353:        efi_cleanup();
                    354:
                    355:        if (efi_fdt_size() > 0) {
                    356:                efi_fdt_fini();
                    357:        }
                    358:
                    359:        efi_boot_kernel(marks);
                    360:
                    361:        /* This should not happen.. */
                    362:        printf("boot returned\n");
                    363:
                    364: cleanup:
                    365:        uefi_call_wrapper(BS->FreePages, 2, addr, EFI_SIZE_TO_PAGES(alloc_size));
                    366:        if (initrd_addr) {
                    367:                uefi_call_wrapper(BS->FreePages, 2, initrd_addr, EFI_SIZE_TO_PAGES(initrd_size));
                    368:                initrd_addr = 0;
                    369:                initrd_size = 0;
                    370:        }
                    371:        if (dtb_addr) {
                    372:                uefi_call_wrapper(BS->FreePages, 2, dtb_addr, EFI_SIZE_TO_PAGES(dtb_size));
                    373:                dtb_addr = 0;
                    374:                dtb_size = 0;
                    375:        }
                    376:        return EIO;
                    377: }

CVSweb <webmaster@jp.NetBSD.org>