1*6f83097eSpatrick /* $OpenBSD: efiboot.c,v 1.19 2018/08/19 14:09:41 patrick Exp $ */ 2f24071e5Spatrick 3f24071e5Spatrick /* 4f24071e5Spatrick * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> 5f24071e5Spatrick * Copyright (c) 2016 Mark Kettenis 6f24071e5Spatrick * 7f24071e5Spatrick * Permission to use, copy, modify, and distribute this software for any 8f24071e5Spatrick * purpose with or without fee is hereby granted, provided that the above 9f24071e5Spatrick * copyright notice and this permission notice appear in all copies. 10f24071e5Spatrick * 11f24071e5Spatrick * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12f24071e5Spatrick * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13f24071e5Spatrick * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14f24071e5Spatrick * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15f24071e5Spatrick * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16f24071e5Spatrick * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17f24071e5Spatrick * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18f24071e5Spatrick */ 19f24071e5Spatrick 20f24071e5Spatrick #include <sys/param.h> 21f24071e5Spatrick #include <sys/queue.h> 22*6f83097eSpatrick #include <sys/stat.h> 23f24071e5Spatrick #include <dev/cons.h> 24f24071e5Spatrick #include <sys/disklabel.h> 25f24071e5Spatrick 26f24071e5Spatrick #include <efi.h> 27f24071e5Spatrick #include <efiapi.h> 28f24071e5Spatrick #include <efiprot.h> 29f24071e5Spatrick #include <eficonsctl.h> 30f24071e5Spatrick 31f24071e5Spatrick #include <lib/libkern/libkern.h> 32f24071e5Spatrick #include <stand/boot/cmd.h> 33f24071e5Spatrick 34f24071e5Spatrick #include "disk.h" 3533e5575aSpatrick #include "efiboot.h" 36f24071e5Spatrick #include "eficall.h" 37f24071e5Spatrick #include "fdt.h" 38f24071e5Spatrick #include "libsa.h" 39f24071e5Spatrick 40f24071e5Spatrick EFI_SYSTEM_TABLE *ST; 41f24071e5Spatrick EFI_BOOT_SERVICES *BS; 42f24071e5Spatrick EFI_RUNTIME_SERVICES *RS; 43f673f4ddSkettenis EFI_HANDLE IH, efi_bootdp; 44f24071e5Spatrick 45fa854029Spatrick EFI_PHYSICAL_ADDRESS heap; 46fa854029Spatrick UINTN heapsiz = 1 * 1024 * 1024; 47f673f4ddSkettenis EFI_MEMORY_DESCRIPTOR *mmap; 48fa854029Spatrick UINTN mmap_key; 49fa854029Spatrick UINTN mmap_ndesc; 50fa854029Spatrick UINTN mmap_descsiz; 51f4dd1aebSkettenis UINT32 mmap_version; 52f24071e5Spatrick 53f24071e5Spatrick static EFI_GUID imgp_guid = LOADED_IMAGE_PROTOCOL; 54f24071e5Spatrick static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL; 55f24071e5Spatrick static EFI_GUID devp_guid = DEVICE_PATH_PROTOCOL; 561e44dc38Skettenis static EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; 57f24071e5Spatrick 5833e5575aSpatrick int efi_device_path_depth(EFI_DEVICE_PATH *dp, int); 5933e5575aSpatrick int efi_device_path_ncmp(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *, int); 60fa854029Spatrick static void efi_heap_init(void); 61fa854029Spatrick static void efi_memprobe_internal(void); 62f24071e5Spatrick static void efi_timer_init(void); 63f24071e5Spatrick static void efi_timer_cleanup(void); 64f24071e5Spatrick static EFI_STATUS efi_memprobe_find(UINTN, UINTN, EFI_PHYSICAL_ADDRESS *); 65f24071e5Spatrick 66f24071e5Spatrick EFI_STATUS 67f24071e5Spatrick efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab) 68f24071e5Spatrick { 69f24071e5Spatrick extern char *progname; 70f24071e5Spatrick EFI_LOADED_IMAGE *imgp; 71f24071e5Spatrick EFI_DEVICE_PATH *dp = NULL; 72f24071e5Spatrick EFI_STATUS status; 73f24071e5Spatrick 74f24071e5Spatrick ST = systab; 75f24071e5Spatrick BS = ST->BootServices; 76f24071e5Spatrick IH = image; 77f24071e5Spatrick 78f0da534cSjsg /* disable reset by watchdog after 5 minutes */ 79f0da534cSjsg EFI_CALL(BS->SetWatchdogTimer, 0, 0, 0, NULL); 80f0da534cSjsg 81f24071e5Spatrick status = EFI_CALL(BS->HandleProtocol, image, &imgp_guid, 82f24071e5Spatrick (void **)&imgp); 83f24071e5Spatrick if (status == EFI_SUCCESS) 84f24071e5Spatrick status = EFI_CALL(BS->HandleProtocol, imgp->DeviceHandle, 85f24071e5Spatrick &devp_guid, (void **)&dp); 86f24071e5Spatrick if (status == EFI_SUCCESS) 87f24071e5Spatrick efi_bootdp = dp; 88f24071e5Spatrick 89f24071e5Spatrick progname = "BOOTAA64"; 90f24071e5Spatrick 91f24071e5Spatrick boot(0); 92f24071e5Spatrick 93f24071e5Spatrick return (EFI_SUCCESS); 94f24071e5Spatrick } 95f24071e5Spatrick 96f24071e5Spatrick static SIMPLE_TEXT_OUTPUT_INTERFACE *conout; 97f24071e5Spatrick static SIMPLE_INPUT_INTERFACE *conin; 98f24071e5Spatrick 99f24071e5Spatrick void 100f24071e5Spatrick efi_cons_probe(struct consdev *cn) 101f24071e5Spatrick { 102f24071e5Spatrick cn->cn_pri = CN_MIDPRI; 103f24071e5Spatrick cn->cn_dev = makedev(12, 0); 104f24071e5Spatrick } 105f24071e5Spatrick 106f24071e5Spatrick void 107f24071e5Spatrick efi_cons_init(struct consdev *cp) 108f24071e5Spatrick { 109f24071e5Spatrick conin = ST->ConIn; 110f24071e5Spatrick conout = ST->ConOut; 111f24071e5Spatrick } 112f24071e5Spatrick 113f24071e5Spatrick int 114f24071e5Spatrick efi_cons_getc(dev_t dev) 115f24071e5Spatrick { 116f24071e5Spatrick EFI_INPUT_KEY key; 117f24071e5Spatrick EFI_STATUS status; 118f24071e5Spatrick #if 0 119f24071e5Spatrick UINTN dummy; 120f24071e5Spatrick #endif 121f24071e5Spatrick static int lastchar = 0; 122f24071e5Spatrick 123f24071e5Spatrick if (lastchar) { 124f24071e5Spatrick int r = lastchar; 125f24071e5Spatrick if ((dev & 0x80) == 0) 126f24071e5Spatrick lastchar = 0; 127f24071e5Spatrick return (r); 128f24071e5Spatrick } 129f24071e5Spatrick 130f24071e5Spatrick status = conin->ReadKeyStroke(conin, &key); 131f24071e5Spatrick while (status == EFI_NOT_READY) { 132f24071e5Spatrick if (dev & 0x80) 133f24071e5Spatrick return (0); 134f24071e5Spatrick /* 135f24071e5Spatrick * XXX The implementation of WaitForEvent() in U-boot 136f24071e5Spatrick * is broken and neverreturns. 137f24071e5Spatrick */ 138f24071e5Spatrick #if 0 139f24071e5Spatrick BS->WaitForEvent(1, &conin->WaitForKey, &dummy); 140f24071e5Spatrick #endif 141f24071e5Spatrick status = conin->ReadKeyStroke(conin, &key); 142f24071e5Spatrick } 143f24071e5Spatrick 144f24071e5Spatrick if (dev & 0x80) 145f24071e5Spatrick lastchar = key.UnicodeChar; 146f24071e5Spatrick 147f24071e5Spatrick return (key.UnicodeChar); 148f24071e5Spatrick } 149f24071e5Spatrick 150f24071e5Spatrick void 151f24071e5Spatrick efi_cons_putc(dev_t dev, int c) 152f24071e5Spatrick { 153f24071e5Spatrick CHAR16 buf[2]; 154f24071e5Spatrick 155f24071e5Spatrick if (c == '\n') 156f24071e5Spatrick efi_cons_putc(dev, '\r'); 157f24071e5Spatrick 158f24071e5Spatrick buf[0] = c; 159f24071e5Spatrick buf[1] = 0; 160f24071e5Spatrick 161f24071e5Spatrick conout->OutputString(conout, buf); 162f24071e5Spatrick } 163f24071e5Spatrick 164f24071e5Spatrick static void 165f24071e5Spatrick efi_heap_init(void) 166f24071e5Spatrick { 167f24071e5Spatrick EFI_STATUS status; 168f24071e5Spatrick 169f24071e5Spatrick status = EFI_CALL(BS->AllocatePages, AllocateAnyPages, EfiLoaderData, 170f24071e5Spatrick EFI_SIZE_TO_PAGES(heapsiz), &heap); 171f24071e5Spatrick if (status != EFI_SUCCESS) 172f24071e5Spatrick panic("BS->AllocatePages()"); 173f24071e5Spatrick } 174f24071e5Spatrick 175f24071e5Spatrick EFI_BLOCK_IO *disk; 176f24071e5Spatrick 177f24071e5Spatrick void 178f24071e5Spatrick efi_diskprobe(void) 179f24071e5Spatrick { 180c6d7d0adSpatrick int i, depth = -1; 181f24071e5Spatrick UINTN sz; 182f24071e5Spatrick EFI_STATUS status; 183f24071e5Spatrick EFI_HANDLE *handles = NULL; 184f24071e5Spatrick EFI_BLOCK_IO *blkio; 185f24071e5Spatrick EFI_BLOCK_IO_MEDIA *media; 186c6d7d0adSpatrick EFI_DEVICE_PATH *dp; 187f24071e5Spatrick 188f24071e5Spatrick sz = 0; 189f24071e5Spatrick status = EFI_CALL(BS->LocateHandle, ByProtocol, &blkio_guid, 0, &sz, 0); 190f24071e5Spatrick if (status == EFI_BUFFER_TOO_SMALL) { 191f24071e5Spatrick handles = alloc(sz); 192f24071e5Spatrick status = EFI_CALL(BS->LocateHandle, ByProtocol, &blkio_guid, 193f24071e5Spatrick 0, &sz, handles); 194f24071e5Spatrick } 195f24071e5Spatrick if (handles == NULL || EFI_ERROR(status)) 19633e5575aSpatrick return; 197f24071e5Spatrick 198c6d7d0adSpatrick if (efi_bootdp != NULL) 199c6d7d0adSpatrick depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH); 200c6d7d0adSpatrick 201c94e7257Skettenis /* 202c94e7257Skettenis * U-Boot incorrectly represents devices with a single 203c94e7257Skettenis * MEDIA_DEVICE_PATH component. In that case include that 204c94e7257Skettenis * component into the matching, otherwise we'll blindly select 205c94e7257Skettenis * the first device. 206c94e7257Skettenis */ 207c94e7257Skettenis if (depth == 0) 208c94e7257Skettenis depth = 1; 209c94e7257Skettenis 210f24071e5Spatrick for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) { 211f24071e5Spatrick status = EFI_CALL(BS->HandleProtocol, handles[i], &blkio_guid, 212f24071e5Spatrick (void **)&blkio); 213f24071e5Spatrick if (EFI_ERROR(status)) 214f24071e5Spatrick panic("BS->HandleProtocol() returns %d", status); 215f24071e5Spatrick 216f24071e5Spatrick media = blkio->Media; 217f24071e5Spatrick if (media->LogicalPartition || !media->MediaPresent) 218f24071e5Spatrick continue; 219f24071e5Spatrick 220c6d7d0adSpatrick if (efi_bootdp == NULL || depth == -1) 221c6d7d0adSpatrick continue; 222f24071e5Spatrick status = EFI_CALL(BS->HandleProtocol, handles[i], &devp_guid, 223f24071e5Spatrick (void **)&dp); 224f24071e5Spatrick if (EFI_ERROR(status)) 225c6d7d0adSpatrick continue; 226c6d7d0adSpatrick if (efi_device_path_ncmp(efi_bootdp, dp, depth) == 0) { 227f24071e5Spatrick disk = blkio; 228f24071e5Spatrick break; 229f24071e5Spatrick } 230f24071e5Spatrick } 231f24071e5Spatrick 232f24071e5Spatrick free(handles, sz); 233f24071e5Spatrick } 234f24071e5Spatrick 235c94e7257Skettenis /* 236c94e7257Skettenis * Determine the number of nodes up to, but not including, the first 237c94e7257Skettenis * node of the specified type. 238c94e7257Skettenis */ 23933e5575aSpatrick int 240c6d7d0adSpatrick efi_device_path_depth(EFI_DEVICE_PATH *dp, int dptype) 241c6d7d0adSpatrick { 242c6d7d0adSpatrick int i; 243c6d7d0adSpatrick 244c6d7d0adSpatrick for (i = 0; !IsDevicePathEnd(dp); dp = NextDevicePathNode(dp), i++) { 245c6d7d0adSpatrick if (DevicePathType(dp) == dptype) 246c94e7257Skettenis return (i); 247c6d7d0adSpatrick } 248c6d7d0adSpatrick 249c6d7d0adSpatrick return (-1); 250c6d7d0adSpatrick } 251c6d7d0adSpatrick 25233e5575aSpatrick int 253c6d7d0adSpatrick efi_device_path_ncmp(EFI_DEVICE_PATH *dpa, EFI_DEVICE_PATH *dpb, int deptn) 254c6d7d0adSpatrick { 255c6d7d0adSpatrick int i, cmp; 256c6d7d0adSpatrick 257c6d7d0adSpatrick for (i = 0; i < deptn; i++) { 258c6d7d0adSpatrick if (IsDevicePathEnd(dpa) || IsDevicePathEnd(dpb)) 259c6d7d0adSpatrick return ((IsDevicePathEnd(dpa) && IsDevicePathEnd(dpb)) 260c6d7d0adSpatrick ? 0 : (IsDevicePathEnd(dpa))? -1 : 1); 261c6d7d0adSpatrick cmp = DevicePathNodeLength(dpa) - DevicePathNodeLength(dpb); 262c6d7d0adSpatrick if (cmp) 263c6d7d0adSpatrick return (cmp); 264c6d7d0adSpatrick cmp = memcmp(dpa, dpb, DevicePathNodeLength(dpa)); 265c6d7d0adSpatrick if (cmp) 266c6d7d0adSpatrick return (cmp); 267c6d7d0adSpatrick dpa = NextDevicePathNode(dpa); 268c6d7d0adSpatrick dpb = NextDevicePathNode(dpb); 269c6d7d0adSpatrick } 270c6d7d0adSpatrick 271c6d7d0adSpatrick return (0); 272c6d7d0adSpatrick } 273c6d7d0adSpatrick 2741e44dc38Skettenis void 2751e44dc38Skettenis efi_framebuffer(void) 2761e44dc38Skettenis { 2771e44dc38Skettenis EFI_GRAPHICS_OUTPUT *gop; 2781e44dc38Skettenis EFI_STATUS status; 2791e44dc38Skettenis void *node, *child; 2801e44dc38Skettenis uint32_t acells, scells; 2811e44dc38Skettenis uint64_t base, size; 2821e44dc38Skettenis uint32_t reg[4]; 2831e44dc38Skettenis uint32_t width, height, stride; 2841e44dc38Skettenis char *format; 2851e44dc38Skettenis 2861e44dc38Skettenis /* 2871e44dc38Skettenis * Don't create a "simple-framebuffer" node if we already have 2881e44dc38Skettenis * one. Besides "/chosen", we also check under "/" since that 2891e44dc38Skettenis * is where the Raspberry Pi firmware puts it. 2901e44dc38Skettenis */ 2911e44dc38Skettenis node = fdt_find_node("/chosen"); 2921e44dc38Skettenis for (child = fdt_child_node(node); child; 2931e44dc38Skettenis child = fdt_next_node(child)) { 2941e44dc38Skettenis if (fdt_node_is_compatible(child, "simple-framebuffer")) 2951e44dc38Skettenis return; 2961e44dc38Skettenis } 2971e44dc38Skettenis node = fdt_find_node("/"); 2981e44dc38Skettenis for (child = fdt_child_node(node); child; 2991e44dc38Skettenis child = fdt_next_node(child)) { 3001e44dc38Skettenis if (fdt_node_is_compatible(child, "simple-framebuffer")) 3011e44dc38Skettenis return; 3021e44dc38Skettenis } 3031e44dc38Skettenis 3041e44dc38Skettenis status = EFI_CALL(BS->LocateProtocol, &gop_guid, NULL, (void **)&gop); 3051e44dc38Skettenis if (status != EFI_SUCCESS) 3061e44dc38Skettenis return; 3071e44dc38Skettenis 3081e44dc38Skettenis /* Paranoia! */ 3091e44dc38Skettenis if (gop == NULL || gop->Mode == NULL || gop->Mode->Info == NULL) 3101e44dc38Skettenis return; 3111e44dc38Skettenis 3121e44dc38Skettenis /* We only support 32-bit pixel modes for now. */ 3131e44dc38Skettenis switch (gop->Mode->Info->PixelFormat) { 3141e44dc38Skettenis case PixelRedGreenBlueReserved8BitPerColor: 3151e44dc38Skettenis format = "a8r8g8b8"; 3161e44dc38Skettenis break; 3171e44dc38Skettenis case PixelBlueGreenRedReserved8BitPerColor: 3181e44dc38Skettenis format = "a8b8g8r8"; 3191e44dc38Skettenis break; 3201e44dc38Skettenis default: 3211e44dc38Skettenis return; 3221e44dc38Skettenis } 3231e44dc38Skettenis 3241e44dc38Skettenis base = gop->Mode->FrameBufferBase; 3251e44dc38Skettenis size = gop->Mode->FrameBufferSize; 3261e44dc38Skettenis width = htobe32(gop->Mode->Info->HorizontalResolution); 3271e44dc38Skettenis height = htobe32(gop->Mode->Info->VerticalResolution); 3281e44dc38Skettenis stride = htobe32(gop->Mode->Info->PixelsPerScanLine * 4); 3291e44dc38Skettenis 3301e44dc38Skettenis node = fdt_find_node("/"); 3311e44dc38Skettenis if (fdt_node_property_int(node, "#address-cells", &acells) != 1) 3321e44dc38Skettenis acells = 1; 3331e44dc38Skettenis if (fdt_node_property_int(node, "#size-cells", &scells) != 1) 3341e44dc38Skettenis scells = 1; 3351e44dc38Skettenis if (acells > 2 || scells > 2) 3361e44dc38Skettenis return; 3371e44dc38Skettenis if (acells >= 1) 3381e44dc38Skettenis reg[0] = htobe32(base); 3391e44dc38Skettenis if (acells == 2) { 3401e44dc38Skettenis reg[1] = reg[0]; 3411e44dc38Skettenis reg[0] = htobe32(base >> 32); 3421e44dc38Skettenis } 3431e44dc38Skettenis if (scells >= 1) 3441e44dc38Skettenis reg[acells] = htobe32(size); 3451e44dc38Skettenis if (scells == 2) { 3461e44dc38Skettenis reg[acells + 1] = reg[acells]; 3471e44dc38Skettenis reg[acells] = htobe32(size >> 32); 3481e44dc38Skettenis } 3491e44dc38Skettenis 3501e44dc38Skettenis node = fdt_find_node("/chosen"); 3511e44dc38Skettenis fdt_node_add_node(node, "framebuffer", &child); 3521e44dc38Skettenis fdt_node_add_property(child, "status", "okay", strlen("okay") + 1); 3531e44dc38Skettenis fdt_node_add_property(child, "format", format, strlen(format) + 1); 3541e44dc38Skettenis fdt_node_add_property(child, "stride", &stride, 4); 3551e44dc38Skettenis fdt_node_add_property(child, "height", &height, 4); 3561e44dc38Skettenis fdt_node_add_property(child, "width", &width, 4); 3571e44dc38Skettenis fdt_node_add_property(child, "reg", reg, (acells + scells) * 4); 3581e44dc38Skettenis fdt_node_add_property(child, "compatible", 3591e44dc38Skettenis "simple-framebuffer", strlen("simple-framebuffer") + 1); 3601e44dc38Skettenis } 3611e44dc38Skettenis 362be07ee62Skettenis int acpi = 0; 363*6f83097eSpatrick void *fdt = NULL; 3649ccb13abSnaddy char *bootmac = NULL; 365f24071e5Spatrick static EFI_GUID fdt_guid = FDT_TABLE_GUID; 366f24071e5Spatrick 367f24071e5Spatrick #define efi_guidcmp(_a, _b) memcmp((_a), (_b), sizeof(EFI_GUID)) 368f24071e5Spatrick 369f24071e5Spatrick void * 370f24071e5Spatrick efi_makebootargs(char *bootargs) 371f24071e5Spatrick { 372f4dd1aebSkettenis u_char bootduid[8]; 373f4dd1aebSkettenis u_char zero[8] = { 0 }; 374f4dd1aebSkettenis uint64_t uefi_system_table = htobe64((uintptr_t)ST); 375f24071e5Spatrick void *node; 376f24071e5Spatrick size_t len; 377f24071e5Spatrick int i; 378f24071e5Spatrick 379*6f83097eSpatrick if (fdt == NULL) { 380f24071e5Spatrick for (i = 0; i < ST->NumberOfTableEntries; i++) { 381f24071e5Spatrick if (efi_guidcmp(&fdt_guid, 382f24071e5Spatrick &ST->ConfigurationTable[i].VendorGuid) == 0) 383f24071e5Spatrick fdt = ST->ConfigurationTable[i].VendorTable; 384f24071e5Spatrick } 385*6f83097eSpatrick } 386f24071e5Spatrick 387be07ee62Skettenis if (fdt == NULL || acpi) 3889a1480b1Skettenis fdt = efi_acpi(); 3899a1480b1Skettenis 390f24071e5Spatrick if (!fdt_init(fdt)) 391f24071e5Spatrick return NULL; 392f24071e5Spatrick 393f24071e5Spatrick node = fdt_find_node("/chosen"); 394f24071e5Spatrick if (!node) 395f24071e5Spatrick return NULL; 396f24071e5Spatrick 397f24071e5Spatrick len = strlen(bootargs) + 1; 398f24071e5Spatrick fdt_node_add_property(node, "bootargs", bootargs, len); 399f24071e5Spatrick 400f24071e5Spatrick /* Pass DUID of the boot disk. */ 401f24071e5Spatrick memcpy(&bootduid, diskinfo.disklabel.d_uid, sizeof(bootduid)); 402f24071e5Spatrick if (memcmp(bootduid, zero, sizeof(bootduid)) != 0) { 403f24071e5Spatrick fdt_node_add_property(node, "openbsd,bootduid", bootduid, 404f24071e5Spatrick sizeof(bootduid)); 405f24071e5Spatrick } 406f24071e5Spatrick 4079ccb13abSnaddy /* Pass netboot interface address. */ 4089ccb13abSnaddy if (bootmac) 4099ccb13abSnaddy fdt_node_add_property(node, "openbsd,bootmac", bootmac, 6); 4109ccb13abSnaddy 411f4dd1aebSkettenis /* Pass EFI system table. */ 412f4dd1aebSkettenis fdt_node_add_property(node, "openbsd,uefi-system-table", 413f4dd1aebSkettenis &uefi_system_table, sizeof(uefi_system_table)); 414f4dd1aebSkettenis 415f4dd1aebSkettenis /* Placeholders for EFI memory map. */ 416f4dd1aebSkettenis fdt_node_add_property(node, "openbsd,uefi-mmap-start", zero, 8); 417f4dd1aebSkettenis fdt_node_add_property(node, "openbsd,uefi-mmap-size", zero, 4); 418f4dd1aebSkettenis fdt_node_add_property(node, "openbsd,uefi-mmap-desc-size", zero, 4); 419f4dd1aebSkettenis fdt_node_add_property(node, "openbsd,uefi-mmap-desc-ver", zero, 4); 420f4dd1aebSkettenis 4211e44dc38Skettenis efi_framebuffer(); 4221e44dc38Skettenis 423f24071e5Spatrick fdt_finalize(); 424f24071e5Spatrick 425f24071e5Spatrick return fdt; 426f24071e5Spatrick } 427f24071e5Spatrick 428f4dd1aebSkettenis void 429f4dd1aebSkettenis efi_updatefdt(void) 430f4dd1aebSkettenis { 431f4dd1aebSkettenis uint64_t uefi_mmap_start = htobe64((uintptr_t)mmap); 432f4dd1aebSkettenis uint32_t uefi_mmap_size = htobe32(mmap_ndesc * mmap_descsiz); 433f4dd1aebSkettenis uint32_t uefi_mmap_desc_size = htobe32(mmap_descsiz); 434f4dd1aebSkettenis uint32_t uefi_mmap_desc_ver = htobe32(mmap_version); 435f4dd1aebSkettenis void *node; 436f4dd1aebSkettenis 437f4dd1aebSkettenis node = fdt_find_node("/chosen"); 438f4dd1aebSkettenis if (!node) 439f4dd1aebSkettenis return; 440f4dd1aebSkettenis 441f4dd1aebSkettenis /* Pass EFI memory map. */ 442f4dd1aebSkettenis fdt_node_set_property(node, "openbsd,uefi-mmap-start", 443f4dd1aebSkettenis &uefi_mmap_start, sizeof(uefi_mmap_start)); 444f4dd1aebSkettenis fdt_node_set_property(node, "openbsd,uefi-mmap-size", 445f4dd1aebSkettenis &uefi_mmap_size, sizeof(uefi_mmap_size)); 446f4dd1aebSkettenis fdt_node_set_property(node, "openbsd,uefi-mmap-desc-size", 447f4dd1aebSkettenis &uefi_mmap_desc_size, sizeof(uefi_mmap_desc_size)); 448f4dd1aebSkettenis fdt_node_set_property(node, "openbsd,uefi-mmap-desc-ver", 449f4dd1aebSkettenis &uefi_mmap_desc_ver, sizeof(uefi_mmap_desc_ver)); 450f4dd1aebSkettenis 451f4dd1aebSkettenis fdt_finalize(); 452f4dd1aebSkettenis } 453f4dd1aebSkettenis 454f24071e5Spatrick u_long efi_loadaddr; 455f24071e5Spatrick 456f24071e5Spatrick void 457f24071e5Spatrick machdep(void) 458f24071e5Spatrick { 459f24071e5Spatrick EFI_PHYSICAL_ADDRESS addr; 460f24071e5Spatrick 461f24071e5Spatrick cninit(); 462f24071e5Spatrick efi_heap_init(); 463f24071e5Spatrick 464f24071e5Spatrick /* 4659a118d7eSpatrick * The kernel expects to be loaded into a block of memory aligned 4669a118d7eSpatrick * on a 2MB boundary. We allocate a block of 64MB of memory, which 4679a118d7eSpatrick * gives us plenty of room for growth. 468f24071e5Spatrick */ 4699a118d7eSpatrick if (efi_memprobe_find(EFI_SIZE_TO_PAGES(64 * 1024 * 1024), 4709a118d7eSpatrick 0x200000, &addr) != EFI_SUCCESS) 471f24071e5Spatrick printf("Can't allocate memory\n"); 472f24071e5Spatrick efi_loadaddr = addr; 473f24071e5Spatrick 474f24071e5Spatrick efi_timer_init(); 475f24071e5Spatrick efi_diskprobe(); 47633e5575aSpatrick efi_pxeprobe(); 477f24071e5Spatrick } 478f24071e5Spatrick 479f24071e5Spatrick void 480f24071e5Spatrick efi_cleanup(void) 481f24071e5Spatrick { 482fa854029Spatrick int retry; 483fa854029Spatrick EFI_STATUS status; 484fa854029Spatrick 485f24071e5Spatrick efi_timer_cleanup(); 486f24071e5Spatrick 487fa854029Spatrick /* retry once in case of failure */ 488fa854029Spatrick for (retry = 1; retry >= 0; retry--) { 489fa854029Spatrick efi_memprobe_internal(); /* sync the current map */ 490f4dd1aebSkettenis efi_updatefdt(); 491fa854029Spatrick status = EFI_CALL(BS->ExitBootServices, IH, mmap_key); 492fa854029Spatrick if (status == EFI_SUCCESS) 493fa854029Spatrick break; 494fa854029Spatrick if (retry == 0) 495fa854029Spatrick panic("ExitBootServices failed (%d)", status); 496fa854029Spatrick } 497f24071e5Spatrick } 498f24071e5Spatrick 499f24071e5Spatrick void 500f24071e5Spatrick _rtt(void) 501f24071e5Spatrick { 502f24071e5Spatrick #ifdef EFI_DEBUG 503f24071e5Spatrick printf("Hit any key to reboot\n"); 504f24071e5Spatrick efi_cons_getc(0); 505f24071e5Spatrick #endif 506f24071e5Spatrick RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL); 507111d6387Skettenis for (;;) 508111d6387Skettenis continue; 509f24071e5Spatrick } 510f24071e5Spatrick 511f24071e5Spatrick /* 512f24071e5Spatrick * U-Boot only implements the GetTime() Runtime Service if it has been 513f24071e5Spatrick * configured with CONFIG_DM_RTC. Most board configurations don't 514f24071e5Spatrick * include that option, so we can't use it to implement our boot 515f24071e5Spatrick * prompt timeout. Instead we use timer events to simulate a clock 516f24071e5Spatrick * that ticks ever second. 517f24071e5Spatrick */ 518f24071e5Spatrick 519f24071e5Spatrick EFI_EVENT timer; 520f24071e5Spatrick int ticks; 521f24071e5Spatrick 522f24071e5Spatrick static VOID 523f24071e5Spatrick efi_timer(EFI_EVENT event, VOID *context) 524f24071e5Spatrick { 525f24071e5Spatrick ticks++; 526f24071e5Spatrick } 527f24071e5Spatrick 528f24071e5Spatrick static void 529f24071e5Spatrick efi_timer_init(void) 530f24071e5Spatrick { 531f24071e5Spatrick EFI_STATUS status; 532f24071e5Spatrick 533a4d88df4Sjsg status = BS->CreateEvent(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, 534f24071e5Spatrick efi_timer, NULL, &timer); 535f24071e5Spatrick if (status == EFI_SUCCESS) 536f24071e5Spatrick status = BS->SetTimer(timer, TimerPeriodic, 10000000); 537f24071e5Spatrick if (EFI_ERROR(status)) 538f24071e5Spatrick printf("Can't create timer\n"); 539f24071e5Spatrick } 540f24071e5Spatrick 541f24071e5Spatrick static void 542f24071e5Spatrick efi_timer_cleanup(void) 543f24071e5Spatrick { 544f24071e5Spatrick BS->CloseEvent(timer); 545f24071e5Spatrick } 546f24071e5Spatrick 547f24071e5Spatrick time_t 548f24071e5Spatrick getsecs(void) 549f24071e5Spatrick { 550f24071e5Spatrick return ticks; 551f24071e5Spatrick } 552f24071e5Spatrick 553f24071e5Spatrick /* 554f24071e5Spatrick * Various device-related bits. 555f24071e5Spatrick */ 556f24071e5Spatrick 557f24071e5Spatrick void 558f24071e5Spatrick devboot(dev_t dev, char *p) 559f24071e5Spatrick { 56033e5575aSpatrick if (disk) 561f24071e5Spatrick strlcpy(p, "sd0a", 5); 56233e5575aSpatrick else 56333e5575aSpatrick strlcpy(p, "tftp0a", 7); 564f24071e5Spatrick } 565f24071e5Spatrick 566f24071e5Spatrick int 567f24071e5Spatrick cnspeed(dev_t dev, int sp) 568f24071e5Spatrick { 569f24071e5Spatrick return 115200; 570f24071e5Spatrick } 571f24071e5Spatrick 572f24071e5Spatrick char * 573f24071e5Spatrick ttyname(int fd) 574f24071e5Spatrick { 575f24071e5Spatrick return "com0"; 576f24071e5Spatrick } 577f24071e5Spatrick 578f24071e5Spatrick dev_t 579f24071e5Spatrick ttydev(char *name) 580f24071e5Spatrick { 581f24071e5Spatrick return NODEV; 582f24071e5Spatrick } 583f24071e5Spatrick 584f24071e5Spatrick #define MAXDEVNAME 16 585f24071e5Spatrick 586f24071e5Spatrick /* 587f24071e5Spatrick * Parse a device spec. 588f24071e5Spatrick * 589f24071e5Spatrick * [A-Za-z]*[0-9]*[A-Za-z]:file 590f24071e5Spatrick * dev uint part 591f24071e5Spatrick */ 592f24071e5Spatrick int 593f24071e5Spatrick devparse(const char *fname, int *dev, int *unit, int *part, const char **file) 594f24071e5Spatrick { 595f24071e5Spatrick const char *s; 596f24071e5Spatrick 597f24071e5Spatrick *unit = 0; /* default to wd0a */ 598f24071e5Spatrick *part = 0; 599f24071e5Spatrick *dev = 0; 600f24071e5Spatrick 601f24071e5Spatrick s = strchr(fname, ':'); 602f24071e5Spatrick if (s != NULL) { 603f24071e5Spatrick int devlen; 604f24071e5Spatrick int i, u, p = 0; 605f24071e5Spatrick struct devsw *dp; 606f24071e5Spatrick char devname[MAXDEVNAME]; 607f24071e5Spatrick 608f24071e5Spatrick devlen = s - fname; 609f24071e5Spatrick if (devlen > MAXDEVNAME) 610f24071e5Spatrick return (EINVAL); 611f24071e5Spatrick 612f24071e5Spatrick /* extract device name */ 613f24071e5Spatrick for (i = 0; isalpha(fname[i]) && (i < devlen); i++) 614f24071e5Spatrick devname[i] = fname[i]; 615f24071e5Spatrick devname[i] = 0; 616f24071e5Spatrick 617f24071e5Spatrick if (!isdigit(fname[i])) 618f24071e5Spatrick return (EUNIT); 619f24071e5Spatrick 620f24071e5Spatrick /* device number */ 621f24071e5Spatrick for (u = 0; isdigit(fname[i]) && (i < devlen); i++) 622f24071e5Spatrick u = u * 10 + (fname[i] - '0'); 623f24071e5Spatrick 624f24071e5Spatrick if (!isalpha(fname[i])) 625f24071e5Spatrick return (EPART); 626f24071e5Spatrick 627f24071e5Spatrick /* partition number */ 628f24071e5Spatrick if (i < devlen) 629f24071e5Spatrick p = fname[i++] - 'a'; 630f24071e5Spatrick 631f24071e5Spatrick if (i != devlen) 632f24071e5Spatrick return (ENXIO); 633f24071e5Spatrick 634f24071e5Spatrick /* check device name */ 635f24071e5Spatrick for (dp = devsw, i = 0; i < ndevs; dp++, i++) { 636f24071e5Spatrick if (dp->dv_name && !strcmp(devname, dp->dv_name)) 637f24071e5Spatrick break; 638f24071e5Spatrick } 639f24071e5Spatrick 640f24071e5Spatrick if (i >= ndevs) 641f24071e5Spatrick return (ENXIO); 642f24071e5Spatrick 643f24071e5Spatrick *unit = u; 644f24071e5Spatrick *part = p; 645f24071e5Spatrick *dev = i; 646f24071e5Spatrick fname = ++s; 647f24071e5Spatrick } 648f24071e5Spatrick 649f24071e5Spatrick *file = fname; 650f24071e5Spatrick 651f24071e5Spatrick return (0); 652f24071e5Spatrick } 653f24071e5Spatrick 654f24071e5Spatrick int 655f24071e5Spatrick devopen(struct open_file *f, const char *fname, char **file) 656f24071e5Spatrick { 657f24071e5Spatrick struct devsw *dp; 658f24071e5Spatrick int dev, unit, part, error; 659f24071e5Spatrick 660f24071e5Spatrick error = devparse(fname, &dev, &unit, &part, (const char **)file); 661f24071e5Spatrick if (error) 662f24071e5Spatrick return (error); 663f24071e5Spatrick 66433e5575aSpatrick dp = &devsw[dev]; 665f24071e5Spatrick f->f_dev = dp; 666f24071e5Spatrick 667f24071e5Spatrick return (*dp->dv_open)(f, unit, part); 668f24071e5Spatrick } 669f24071e5Spatrick 670fa854029Spatrick static void 671fa854029Spatrick efi_memprobe_internal(void) 672fa854029Spatrick { 673fa854029Spatrick EFI_STATUS status; 674fa854029Spatrick UINTN mapkey, mmsiz, siz; 675fa854029Spatrick UINT32 mmver; 676fa854029Spatrick EFI_MEMORY_DESCRIPTOR *mm; 677fa854029Spatrick int n; 678fa854029Spatrick 679fa854029Spatrick free(mmap, mmap_ndesc * mmap_descsiz); 680fa854029Spatrick 681fa854029Spatrick siz = 0; 682fa854029Spatrick status = EFI_CALL(BS->GetMemoryMap, &siz, NULL, &mapkey, &mmsiz, 683fa854029Spatrick &mmver); 684fa854029Spatrick if (status != EFI_BUFFER_TOO_SMALL) 685fa854029Spatrick panic("cannot get the size of memory map"); 686fa854029Spatrick mm = alloc(siz); 687fa854029Spatrick status = EFI_CALL(BS->GetMemoryMap, &siz, mm, &mapkey, &mmsiz, &mmver); 688fa854029Spatrick if (status != EFI_SUCCESS) 689fa854029Spatrick panic("cannot get the memory map"); 690fa854029Spatrick n = siz / mmsiz; 691fa854029Spatrick mmap = mm; 692fa854029Spatrick mmap_key = mapkey; 693fa854029Spatrick mmap_ndesc = n; 694fa854029Spatrick mmap_descsiz = mmsiz; 695f4dd1aebSkettenis mmap_version = mmver; 696fa854029Spatrick } 697fa854029Spatrick 698f24071e5Spatrick /* 699f24071e5Spatrick * 64-bit ARMs can have a much wider memory mapping, as in somewhere 700f24071e5Spatrick * after the 32-bit region. To cope with our alignment requirement, 701f24071e5Spatrick * use the memory table to find a place where we can fit. 702f24071e5Spatrick */ 703f24071e5Spatrick static EFI_STATUS 704f24071e5Spatrick efi_memprobe_find(UINTN pages, UINTN align, EFI_PHYSICAL_ADDRESS *addr) 705f24071e5Spatrick { 706fa854029Spatrick EFI_MEMORY_DESCRIPTOR *mm; 707fa854029Spatrick int i, j; 708f24071e5Spatrick 709f24071e5Spatrick if (align < EFI_PAGE_SIZE) 710f24071e5Spatrick return EFI_INVALID_PARAMETER; 711f24071e5Spatrick 712fa854029Spatrick efi_memprobe_internal(); /* sync the current map */ 713f24071e5Spatrick 714fa854029Spatrick for (i = 0, mm = mmap; i < mmap_ndesc; 715fa854029Spatrick i++, mm = NextMemoryDescriptor(mm, mmap_descsiz)) { 716f24071e5Spatrick if (mm->Type != EfiConventionalMemory) 717f24071e5Spatrick continue; 718f24071e5Spatrick 719f24071e5Spatrick if (mm->NumberOfPages < pages) 720f24071e5Spatrick continue; 721f24071e5Spatrick 722f2c0e408Skettenis for (j = 0; j < mm->NumberOfPages; j++) { 723f24071e5Spatrick EFI_PHYSICAL_ADDRESS paddr; 724f24071e5Spatrick 725f24071e5Spatrick if (mm->NumberOfPages - j < pages) 726f24071e5Spatrick break; 727f24071e5Spatrick 728f24071e5Spatrick paddr = mm->PhysicalStart + (j * EFI_PAGE_SIZE); 72973dc1409Spatrick if (paddr & (align - 1)) 73073dc1409Spatrick continue; 73173dc1409Spatrick 73273dc1409Spatrick if (EFI_CALL(BS->AllocatePages, AllocateAddress, 73373dc1409Spatrick EfiLoaderData, pages, &paddr) == EFI_SUCCESS) { 734f24071e5Spatrick *addr = paddr; 735f24071e5Spatrick return EFI_SUCCESS; 736f24071e5Spatrick } 737f24071e5Spatrick } 738f24071e5Spatrick } 739f24071e5Spatrick return EFI_OUT_OF_RESOURCES; 740f24071e5Spatrick } 741111d6387Skettenis 742111d6387Skettenis /* 743111d6387Skettenis * Commands 744111d6387Skettenis */ 745111d6387Skettenis 746be07ee62Skettenis int Xacpi_efi(void); 747*6f83097eSpatrick int Xdtb_efi(void); 748111d6387Skettenis int Xexit_efi(void); 749111d6387Skettenis int Xpoweroff_efi(void); 750111d6387Skettenis 751111d6387Skettenis const struct cmd_table cmd_machine[] = { 752be07ee62Skettenis { "acpi", CMDT_CMD, Xacpi_efi }, 753*6f83097eSpatrick { "dtb", CMDT_CMD, Xdtb_efi }, 754111d6387Skettenis { "exit", CMDT_CMD, Xexit_efi }, 755111d6387Skettenis { "poweroff", CMDT_CMD, Xpoweroff_efi }, 756111d6387Skettenis { NULL, 0 } 757111d6387Skettenis }; 758111d6387Skettenis 759111d6387Skettenis int 760be07ee62Skettenis Xacpi_efi(void) 761be07ee62Skettenis { 762be07ee62Skettenis acpi = 1; 763be07ee62Skettenis return (0); 764be07ee62Skettenis } 765be07ee62Skettenis 766be07ee62Skettenis int 767*6f83097eSpatrick Xdtb_efi(void) 768*6f83097eSpatrick { 769*6f83097eSpatrick EFI_PHYSICAL_ADDRESS addr; 770*6f83097eSpatrick char path[MAXPATHLEN]; 771*6f83097eSpatrick struct stat sb; 772*6f83097eSpatrick int fd; 773*6f83097eSpatrick 774*6f83097eSpatrick #define O_RDONLY 0 775*6f83097eSpatrick 776*6f83097eSpatrick if (cmd.argc != 2) 777*6f83097eSpatrick return (1); 778*6f83097eSpatrick 779*6f83097eSpatrick snprintf(path, sizeof(path), "%s:%s", cmd.bootdev, cmd.argv[1]); 780*6f83097eSpatrick 781*6f83097eSpatrick fd = open(path, O_RDONLY); 782*6f83097eSpatrick if (fd < 0 || fstat(fd, &sb) == -1) { 783*6f83097eSpatrick printf("cannot open %s\n", path); 784*6f83097eSpatrick return (1); 785*6f83097eSpatrick } 786*6f83097eSpatrick if (efi_memprobe_find(EFI_SIZE_TO_PAGES(sb.st_size), 787*6f83097eSpatrick 0x1000, &addr) != EFI_SUCCESS) { 788*6f83097eSpatrick printf("cannot allocate memory for %s\n", path); 789*6f83097eSpatrick return (1); 790*6f83097eSpatrick } 791*6f83097eSpatrick if (read(fd, (void *)addr, sb.st_size) != sb.st_size) { 792*6f83097eSpatrick printf("cannot read from %s\n", path); 793*6f83097eSpatrick return (1); 794*6f83097eSpatrick } 795*6f83097eSpatrick 796*6f83097eSpatrick fdt = (void *)addr; 797*6f83097eSpatrick return (0); 798*6f83097eSpatrick } 799*6f83097eSpatrick 800*6f83097eSpatrick int 801111d6387Skettenis Xexit_efi(void) 802111d6387Skettenis { 803111d6387Skettenis EFI_CALL(BS->Exit, IH, 0, 0, NULL); 804111d6387Skettenis for (;;) 805111d6387Skettenis continue; 806111d6387Skettenis return (0); 807111d6387Skettenis } 808111d6387Skettenis 809111d6387Skettenis int 810111d6387Skettenis Xpoweroff_efi(void) 811111d6387Skettenis { 812111d6387Skettenis EFI_CALL(RS->ResetSystem, EfiResetShutdown, EFI_SUCCESS, 0, NULL); 813111d6387Skettenis return (0); 814111d6387Skettenis } 815