1*1e44dc38Skettenis /* $OpenBSD: efiboot.c,v 1.13 2017/08/23 18:03:54 kettenis 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> 22f24071e5Spatrick #include <dev/cons.h> 23f24071e5Spatrick #include <sys/disklabel.h> 24f24071e5Spatrick 25f24071e5Spatrick #include <efi.h> 26f24071e5Spatrick #include <efiapi.h> 27f24071e5Spatrick #include <efiprot.h> 28f24071e5Spatrick #include <eficonsctl.h> 29f24071e5Spatrick 30f24071e5Spatrick #include <lib/libkern/libkern.h> 31f24071e5Spatrick #include <stand/boot/cmd.h> 32f24071e5Spatrick 33f24071e5Spatrick #include "disk.h" 34f24071e5Spatrick #include "eficall.h" 35f24071e5Spatrick #include "fdt.h" 36f24071e5Spatrick #include "libsa.h" 37f24071e5Spatrick 38f24071e5Spatrick EFI_SYSTEM_TABLE *ST; 39f24071e5Spatrick EFI_BOOT_SERVICES *BS; 40f24071e5Spatrick EFI_RUNTIME_SERVICES *RS; 41f673f4ddSkettenis EFI_HANDLE IH, efi_bootdp; 42f24071e5Spatrick 43fa854029Spatrick EFI_PHYSICAL_ADDRESS heap; 44fa854029Spatrick UINTN heapsiz = 1 * 1024 * 1024; 45f673f4ddSkettenis EFI_MEMORY_DESCRIPTOR *mmap; 46fa854029Spatrick UINTN mmap_key; 47fa854029Spatrick UINTN mmap_ndesc; 48fa854029Spatrick UINTN mmap_descsiz; 49f4dd1aebSkettenis UINT32 mmap_version; 50f24071e5Spatrick 51f24071e5Spatrick static EFI_GUID imgp_guid = LOADED_IMAGE_PROTOCOL; 52f24071e5Spatrick static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL; 53f24071e5Spatrick static EFI_GUID devp_guid = DEVICE_PATH_PROTOCOL; 54*1e44dc38Skettenis static EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; 55f24071e5Spatrick 56c6d7d0adSpatrick static int efi_device_path_depth(EFI_DEVICE_PATH *dp, int); 57c6d7d0adSpatrick static int efi_device_path_ncmp(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *, int); 58fa854029Spatrick static void efi_heap_init(void); 59fa854029Spatrick static void efi_memprobe_internal(void); 60f24071e5Spatrick static void efi_timer_init(void); 61f24071e5Spatrick static void efi_timer_cleanup(void); 62f24071e5Spatrick static EFI_STATUS efi_memprobe_find(UINTN, UINTN, EFI_PHYSICAL_ADDRESS *); 63f24071e5Spatrick 64f24071e5Spatrick EFI_STATUS 65f24071e5Spatrick efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab) 66f24071e5Spatrick { 67f24071e5Spatrick extern char *progname; 68f24071e5Spatrick EFI_LOADED_IMAGE *imgp; 69f24071e5Spatrick EFI_DEVICE_PATH *dp = NULL; 70f24071e5Spatrick EFI_STATUS status; 71f24071e5Spatrick 72f24071e5Spatrick ST = systab; 73f24071e5Spatrick BS = ST->BootServices; 74f24071e5Spatrick IH = image; 75f24071e5Spatrick 76f24071e5Spatrick status = EFI_CALL(BS->HandleProtocol, image, &imgp_guid, 77f24071e5Spatrick (void **)&imgp); 78f24071e5Spatrick if (status == EFI_SUCCESS) 79f24071e5Spatrick status = EFI_CALL(BS->HandleProtocol, imgp->DeviceHandle, 80f24071e5Spatrick &devp_guid, (void **)&dp); 81f24071e5Spatrick if (status == EFI_SUCCESS) 82f24071e5Spatrick efi_bootdp = dp; 83f24071e5Spatrick 84f24071e5Spatrick progname = "BOOTAA64"; 85f24071e5Spatrick 86f24071e5Spatrick boot(0); 87f24071e5Spatrick 88f24071e5Spatrick return (EFI_SUCCESS); 89f24071e5Spatrick } 90f24071e5Spatrick 91f24071e5Spatrick static SIMPLE_TEXT_OUTPUT_INTERFACE *conout; 92f24071e5Spatrick static SIMPLE_INPUT_INTERFACE *conin; 93f24071e5Spatrick 94f24071e5Spatrick void 95f24071e5Spatrick efi_cons_probe(struct consdev *cn) 96f24071e5Spatrick { 97f24071e5Spatrick cn->cn_pri = CN_MIDPRI; 98f24071e5Spatrick cn->cn_dev = makedev(12, 0); 99f24071e5Spatrick } 100f24071e5Spatrick 101f24071e5Spatrick void 102f24071e5Spatrick efi_cons_init(struct consdev *cp) 103f24071e5Spatrick { 104f24071e5Spatrick conin = ST->ConIn; 105f24071e5Spatrick conout = ST->ConOut; 106f24071e5Spatrick } 107f24071e5Spatrick 108f24071e5Spatrick int 109f24071e5Spatrick efi_cons_getc(dev_t dev) 110f24071e5Spatrick { 111f24071e5Spatrick EFI_INPUT_KEY key; 112f24071e5Spatrick EFI_STATUS status; 113f24071e5Spatrick #if 0 114f24071e5Spatrick UINTN dummy; 115f24071e5Spatrick #endif 116f24071e5Spatrick static int lastchar = 0; 117f24071e5Spatrick 118f24071e5Spatrick if (lastchar) { 119f24071e5Spatrick int r = lastchar; 120f24071e5Spatrick if ((dev & 0x80) == 0) 121f24071e5Spatrick lastchar = 0; 122f24071e5Spatrick return (r); 123f24071e5Spatrick } 124f24071e5Spatrick 125f24071e5Spatrick status = conin->ReadKeyStroke(conin, &key); 126f24071e5Spatrick while (status == EFI_NOT_READY) { 127f24071e5Spatrick if (dev & 0x80) 128f24071e5Spatrick return (0); 129f24071e5Spatrick /* 130f24071e5Spatrick * XXX The implementation of WaitForEvent() in U-boot 131f24071e5Spatrick * is broken and neverreturns. 132f24071e5Spatrick */ 133f24071e5Spatrick #if 0 134f24071e5Spatrick BS->WaitForEvent(1, &conin->WaitForKey, &dummy); 135f24071e5Spatrick #endif 136f24071e5Spatrick status = conin->ReadKeyStroke(conin, &key); 137f24071e5Spatrick } 138f24071e5Spatrick 139f24071e5Spatrick if (dev & 0x80) 140f24071e5Spatrick lastchar = key.UnicodeChar; 141f24071e5Spatrick 142f24071e5Spatrick return (key.UnicodeChar); 143f24071e5Spatrick } 144f24071e5Spatrick 145f24071e5Spatrick void 146f24071e5Spatrick efi_cons_putc(dev_t dev, int c) 147f24071e5Spatrick { 148f24071e5Spatrick CHAR16 buf[2]; 149f24071e5Spatrick 150f24071e5Spatrick if (c == '\n') 151f24071e5Spatrick efi_cons_putc(dev, '\r'); 152f24071e5Spatrick 153f24071e5Spatrick buf[0] = c; 154f24071e5Spatrick buf[1] = 0; 155f24071e5Spatrick 156f24071e5Spatrick conout->OutputString(conout, buf); 157f24071e5Spatrick } 158f24071e5Spatrick 159f24071e5Spatrick static void 160f24071e5Spatrick efi_heap_init(void) 161f24071e5Spatrick { 162f24071e5Spatrick EFI_STATUS status; 163f24071e5Spatrick 164f24071e5Spatrick status = EFI_CALL(BS->AllocatePages, AllocateAnyPages, EfiLoaderData, 165f24071e5Spatrick EFI_SIZE_TO_PAGES(heapsiz), &heap); 166f24071e5Spatrick if (status != EFI_SUCCESS) 167f24071e5Spatrick panic("BS->AllocatePages()"); 168f24071e5Spatrick } 169f24071e5Spatrick 170f24071e5Spatrick EFI_BLOCK_IO *disk; 171f24071e5Spatrick 172f24071e5Spatrick void 173f24071e5Spatrick efi_diskprobe(void) 174f24071e5Spatrick { 175c6d7d0adSpatrick int i, depth = -1; 176f24071e5Spatrick UINTN sz; 177f24071e5Spatrick EFI_STATUS status; 178f24071e5Spatrick EFI_HANDLE *handles = NULL; 179f24071e5Spatrick EFI_BLOCK_IO *blkio; 180f24071e5Spatrick EFI_BLOCK_IO_MEDIA *media; 181c6d7d0adSpatrick EFI_DEVICE_PATH *dp; 182f24071e5Spatrick 183f24071e5Spatrick sz = 0; 184f24071e5Spatrick status = EFI_CALL(BS->LocateHandle, ByProtocol, &blkio_guid, 0, &sz, 0); 185f24071e5Spatrick if (status == EFI_BUFFER_TOO_SMALL) { 186f24071e5Spatrick handles = alloc(sz); 187f24071e5Spatrick status = EFI_CALL(BS->LocateHandle, ByProtocol, &blkio_guid, 188f24071e5Spatrick 0, &sz, handles); 189f24071e5Spatrick } 190f24071e5Spatrick if (handles == NULL || EFI_ERROR(status)) 191f24071e5Spatrick panic("BS->LocateHandle() returns %d", status); 192f24071e5Spatrick 193c6d7d0adSpatrick if (efi_bootdp != NULL) 194c6d7d0adSpatrick depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH); 195c6d7d0adSpatrick 196c94e7257Skettenis /* 197c94e7257Skettenis * U-Boot incorrectly represents devices with a single 198c94e7257Skettenis * MEDIA_DEVICE_PATH component. In that case include that 199c94e7257Skettenis * component into the matching, otherwise we'll blindly select 200c94e7257Skettenis * the first device. 201c94e7257Skettenis */ 202c94e7257Skettenis if (depth == 0) 203c94e7257Skettenis depth = 1; 204c94e7257Skettenis 205f24071e5Spatrick for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) { 206f24071e5Spatrick status = EFI_CALL(BS->HandleProtocol, handles[i], &blkio_guid, 207f24071e5Spatrick (void **)&blkio); 208f24071e5Spatrick if (EFI_ERROR(status)) 209f24071e5Spatrick panic("BS->HandleProtocol() returns %d", status); 210f24071e5Spatrick 211f24071e5Spatrick media = blkio->Media; 212f24071e5Spatrick if (media->LogicalPartition || !media->MediaPresent) 213f24071e5Spatrick continue; 214f24071e5Spatrick 215c6d7d0adSpatrick if (efi_bootdp == NULL || depth == -1) 216c6d7d0adSpatrick continue; 217f24071e5Spatrick status = EFI_CALL(BS->HandleProtocol, handles[i], &devp_guid, 218f24071e5Spatrick (void **)&dp); 219f24071e5Spatrick if (EFI_ERROR(status)) 220c6d7d0adSpatrick continue; 221c6d7d0adSpatrick if (efi_device_path_ncmp(efi_bootdp, dp, depth) == 0) { 222f24071e5Spatrick disk = blkio; 223f24071e5Spatrick break; 224f24071e5Spatrick } 225f24071e5Spatrick } 226f24071e5Spatrick 227f24071e5Spatrick free(handles, sz); 228f24071e5Spatrick } 229f24071e5Spatrick 230c94e7257Skettenis /* 231c94e7257Skettenis * Determine the number of nodes up to, but not including, the first 232c94e7257Skettenis * node of the specified type. 233c94e7257Skettenis */ 234c6d7d0adSpatrick static int 235c6d7d0adSpatrick efi_device_path_depth(EFI_DEVICE_PATH *dp, int dptype) 236c6d7d0adSpatrick { 237c6d7d0adSpatrick int i; 238c6d7d0adSpatrick 239c6d7d0adSpatrick for (i = 0; !IsDevicePathEnd(dp); dp = NextDevicePathNode(dp), i++) { 240c6d7d0adSpatrick if (DevicePathType(dp) == dptype) 241c94e7257Skettenis return (i); 242c6d7d0adSpatrick } 243c6d7d0adSpatrick 244c6d7d0adSpatrick return (-1); 245c6d7d0adSpatrick } 246c6d7d0adSpatrick 247c6d7d0adSpatrick static int 248c6d7d0adSpatrick efi_device_path_ncmp(EFI_DEVICE_PATH *dpa, EFI_DEVICE_PATH *dpb, int deptn) 249c6d7d0adSpatrick { 250c6d7d0adSpatrick int i, cmp; 251c6d7d0adSpatrick 252c6d7d0adSpatrick for (i = 0; i < deptn; i++) { 253c6d7d0adSpatrick if (IsDevicePathEnd(dpa) || IsDevicePathEnd(dpb)) 254c6d7d0adSpatrick return ((IsDevicePathEnd(dpa) && IsDevicePathEnd(dpb)) 255c6d7d0adSpatrick ? 0 : (IsDevicePathEnd(dpa))? -1 : 1); 256c6d7d0adSpatrick cmp = DevicePathNodeLength(dpa) - DevicePathNodeLength(dpb); 257c6d7d0adSpatrick if (cmp) 258c6d7d0adSpatrick return (cmp); 259c6d7d0adSpatrick cmp = memcmp(dpa, dpb, DevicePathNodeLength(dpa)); 260c6d7d0adSpatrick if (cmp) 261c6d7d0adSpatrick return (cmp); 262c6d7d0adSpatrick dpa = NextDevicePathNode(dpa); 263c6d7d0adSpatrick dpb = NextDevicePathNode(dpb); 264c6d7d0adSpatrick } 265c6d7d0adSpatrick 266c6d7d0adSpatrick return (0); 267c6d7d0adSpatrick } 268c6d7d0adSpatrick 269*1e44dc38Skettenis void 270*1e44dc38Skettenis efi_framebuffer(void) 271*1e44dc38Skettenis { 272*1e44dc38Skettenis EFI_GRAPHICS_OUTPUT *gop; 273*1e44dc38Skettenis EFI_STATUS status; 274*1e44dc38Skettenis void *node, *child; 275*1e44dc38Skettenis uint32_t acells, scells; 276*1e44dc38Skettenis uint64_t base, size; 277*1e44dc38Skettenis uint32_t reg[4]; 278*1e44dc38Skettenis uint32_t width, height, stride; 279*1e44dc38Skettenis char *format; 280*1e44dc38Skettenis 281*1e44dc38Skettenis /* 282*1e44dc38Skettenis * Don't create a "simple-framebuffer" node if we already have 283*1e44dc38Skettenis * one. Besides "/chosen", we also check under "/" since that 284*1e44dc38Skettenis * is where the Raspberry Pi firmware puts it. 285*1e44dc38Skettenis */ 286*1e44dc38Skettenis node = fdt_find_node("/chosen"); 287*1e44dc38Skettenis for (child = fdt_child_node(node); child; 288*1e44dc38Skettenis child = fdt_next_node(child)) { 289*1e44dc38Skettenis if (fdt_node_is_compatible(child, "simple-framebuffer")) 290*1e44dc38Skettenis return; 291*1e44dc38Skettenis } 292*1e44dc38Skettenis node = fdt_find_node("/"); 293*1e44dc38Skettenis for (child = fdt_child_node(node); child; 294*1e44dc38Skettenis child = fdt_next_node(child)) { 295*1e44dc38Skettenis if (fdt_node_is_compatible(child, "simple-framebuffer")) 296*1e44dc38Skettenis return; 297*1e44dc38Skettenis } 298*1e44dc38Skettenis 299*1e44dc38Skettenis status = EFI_CALL(BS->LocateProtocol, &gop_guid, NULL, (void **)&gop); 300*1e44dc38Skettenis if (status != EFI_SUCCESS) 301*1e44dc38Skettenis return; 302*1e44dc38Skettenis 303*1e44dc38Skettenis /* Paranoia! */ 304*1e44dc38Skettenis if (gop == NULL || gop->Mode == NULL || gop->Mode->Info == NULL) 305*1e44dc38Skettenis return; 306*1e44dc38Skettenis 307*1e44dc38Skettenis /* We only support 32-bit pixel modes for now. */ 308*1e44dc38Skettenis switch (gop->Mode->Info->PixelFormat) { 309*1e44dc38Skettenis case PixelRedGreenBlueReserved8BitPerColor: 310*1e44dc38Skettenis format = "a8r8g8b8"; 311*1e44dc38Skettenis break; 312*1e44dc38Skettenis case PixelBlueGreenRedReserved8BitPerColor: 313*1e44dc38Skettenis format = "a8b8g8r8"; 314*1e44dc38Skettenis break; 315*1e44dc38Skettenis default: 316*1e44dc38Skettenis return; 317*1e44dc38Skettenis } 318*1e44dc38Skettenis 319*1e44dc38Skettenis base = gop->Mode->FrameBufferBase; 320*1e44dc38Skettenis size = gop->Mode->FrameBufferSize; 321*1e44dc38Skettenis width = htobe32(gop->Mode->Info->HorizontalResolution); 322*1e44dc38Skettenis height = htobe32(gop->Mode->Info->VerticalResolution); 323*1e44dc38Skettenis stride = htobe32(gop->Mode->Info->PixelsPerScanLine * 4); 324*1e44dc38Skettenis 325*1e44dc38Skettenis node = fdt_find_node("/"); 326*1e44dc38Skettenis if (fdt_node_property_int(node, "#address-cells", &acells) != 1) 327*1e44dc38Skettenis acells = 1; 328*1e44dc38Skettenis if (fdt_node_property_int(node, "#size-cells", &scells) != 1) 329*1e44dc38Skettenis scells = 1; 330*1e44dc38Skettenis if (acells > 2 || scells > 2) 331*1e44dc38Skettenis return; 332*1e44dc38Skettenis if (acells >= 1) 333*1e44dc38Skettenis reg[0] = htobe32(base); 334*1e44dc38Skettenis if (acells == 2) { 335*1e44dc38Skettenis reg[1] = reg[0]; 336*1e44dc38Skettenis reg[0] = htobe32(base >> 32); 337*1e44dc38Skettenis } 338*1e44dc38Skettenis if (scells >= 1) 339*1e44dc38Skettenis reg[acells] = htobe32(size); 340*1e44dc38Skettenis if (scells == 2) { 341*1e44dc38Skettenis reg[acells + 1] = reg[acells]; 342*1e44dc38Skettenis reg[acells] = htobe32(size >> 32); 343*1e44dc38Skettenis } 344*1e44dc38Skettenis 345*1e44dc38Skettenis node = fdt_find_node("/chosen"); 346*1e44dc38Skettenis fdt_node_add_node(node, "framebuffer", &child); 347*1e44dc38Skettenis fdt_node_add_property(child, "status", "okay", strlen("okay") + 1); 348*1e44dc38Skettenis fdt_node_add_property(child, "format", format, strlen(format) + 1); 349*1e44dc38Skettenis fdt_node_add_property(child, "stride", &stride, 4); 350*1e44dc38Skettenis fdt_node_add_property(child, "height", &height, 4); 351*1e44dc38Skettenis fdt_node_add_property(child, "width", &width, 4); 352*1e44dc38Skettenis fdt_node_add_property(child, "reg", reg, (acells + scells) * 4); 353*1e44dc38Skettenis fdt_node_add_property(child, "compatible", 354*1e44dc38Skettenis "simple-framebuffer", strlen("simple-framebuffer") + 1); 355*1e44dc38Skettenis } 356*1e44dc38Skettenis 357f24071e5Spatrick static EFI_GUID fdt_guid = FDT_TABLE_GUID; 358f24071e5Spatrick 359f24071e5Spatrick #define efi_guidcmp(_a, _b) memcmp((_a), (_b), sizeof(EFI_GUID)) 360f24071e5Spatrick 361f24071e5Spatrick void * 362f24071e5Spatrick efi_makebootargs(char *bootargs) 363f24071e5Spatrick { 364f24071e5Spatrick void *fdt = NULL; 365f4dd1aebSkettenis u_char bootduid[8]; 366f4dd1aebSkettenis u_char zero[8] = { 0 }; 367f4dd1aebSkettenis uint64_t uefi_system_table = htobe64((uintptr_t)ST); 368f24071e5Spatrick void *node; 369f24071e5Spatrick size_t len; 370f24071e5Spatrick int i; 371f24071e5Spatrick 372f24071e5Spatrick for (i = 0; i < ST->NumberOfTableEntries; i++) { 373f24071e5Spatrick if (efi_guidcmp(&fdt_guid, 374f24071e5Spatrick &ST->ConfigurationTable[i].VendorGuid) == 0) 375f24071e5Spatrick fdt = ST->ConfigurationTable[i].VendorTable; 376f24071e5Spatrick } 377f24071e5Spatrick 378f24071e5Spatrick if (!fdt_init(fdt)) 379f24071e5Spatrick return NULL; 380f24071e5Spatrick 381f24071e5Spatrick node = fdt_find_node("/chosen"); 382f24071e5Spatrick if (!node) 383f24071e5Spatrick return NULL; 384f24071e5Spatrick 385f24071e5Spatrick len = strlen(bootargs) + 1; 386f24071e5Spatrick fdt_node_add_property(node, "bootargs", bootargs, len); 387f24071e5Spatrick 388f24071e5Spatrick /* Pass DUID of the boot disk. */ 389f24071e5Spatrick memcpy(&bootduid, diskinfo.disklabel.d_uid, sizeof(bootduid)); 390f24071e5Spatrick if (memcmp(bootduid, zero, sizeof(bootduid)) != 0) { 391f24071e5Spatrick fdt_node_add_property(node, "openbsd,bootduid", bootduid, 392f24071e5Spatrick sizeof(bootduid)); 393f24071e5Spatrick } 394f24071e5Spatrick 395f4dd1aebSkettenis /* Pass EFI system table. */ 396f4dd1aebSkettenis fdt_node_add_property(node, "openbsd,uefi-system-table", 397f4dd1aebSkettenis &uefi_system_table, sizeof(uefi_system_table)); 398f4dd1aebSkettenis 399f4dd1aebSkettenis /* Placeholders for EFI memory map. */ 400f4dd1aebSkettenis fdt_node_add_property(node, "openbsd,uefi-mmap-start", zero, 8); 401f4dd1aebSkettenis fdt_node_add_property(node, "openbsd,uefi-mmap-size", zero, 4); 402f4dd1aebSkettenis fdt_node_add_property(node, "openbsd,uefi-mmap-desc-size", zero, 4); 403f4dd1aebSkettenis fdt_node_add_property(node, "openbsd,uefi-mmap-desc-ver", zero, 4); 404f4dd1aebSkettenis 405*1e44dc38Skettenis efi_framebuffer(); 406*1e44dc38Skettenis 407f24071e5Spatrick fdt_finalize(); 408f24071e5Spatrick 409f24071e5Spatrick return fdt; 410f24071e5Spatrick } 411f24071e5Spatrick 412f4dd1aebSkettenis void 413f4dd1aebSkettenis efi_updatefdt(void) 414f4dd1aebSkettenis { 415f4dd1aebSkettenis uint64_t uefi_mmap_start = htobe64((uintptr_t)mmap); 416f4dd1aebSkettenis uint32_t uefi_mmap_size = htobe32(mmap_ndesc * mmap_descsiz); 417f4dd1aebSkettenis uint32_t uefi_mmap_desc_size = htobe32(mmap_descsiz); 418f4dd1aebSkettenis uint32_t uefi_mmap_desc_ver = htobe32(mmap_version); 419f4dd1aebSkettenis void *node; 420f4dd1aebSkettenis 421f4dd1aebSkettenis node = fdt_find_node("/chosen"); 422f4dd1aebSkettenis if (!node) 423f4dd1aebSkettenis return; 424f4dd1aebSkettenis 425f4dd1aebSkettenis /* Pass EFI memory map. */ 426f4dd1aebSkettenis fdt_node_set_property(node, "openbsd,uefi-mmap-start", 427f4dd1aebSkettenis &uefi_mmap_start, sizeof(uefi_mmap_start)); 428f4dd1aebSkettenis fdt_node_set_property(node, "openbsd,uefi-mmap-size", 429f4dd1aebSkettenis &uefi_mmap_size, sizeof(uefi_mmap_size)); 430f4dd1aebSkettenis fdt_node_set_property(node, "openbsd,uefi-mmap-desc-size", 431f4dd1aebSkettenis &uefi_mmap_desc_size, sizeof(uefi_mmap_desc_size)); 432f4dd1aebSkettenis fdt_node_set_property(node, "openbsd,uefi-mmap-desc-ver", 433f4dd1aebSkettenis &uefi_mmap_desc_ver, sizeof(uefi_mmap_desc_ver)); 434f4dd1aebSkettenis 435f4dd1aebSkettenis fdt_finalize(); 436f4dd1aebSkettenis } 437f4dd1aebSkettenis 438f24071e5Spatrick u_long efi_loadaddr; 439f24071e5Spatrick 440f24071e5Spatrick void 441f24071e5Spatrick machdep(void) 442f24071e5Spatrick { 443f24071e5Spatrick EFI_PHYSICAL_ADDRESS addr; 444f24071e5Spatrick 445f24071e5Spatrick cninit(); 446f24071e5Spatrick efi_heap_init(); 447f24071e5Spatrick 448f24071e5Spatrick /* 4499a118d7eSpatrick * The kernel expects to be loaded into a block of memory aligned 4509a118d7eSpatrick * on a 2MB boundary. We allocate a block of 64MB of memory, which 4519a118d7eSpatrick * gives us plenty of room for growth. 452f24071e5Spatrick */ 4539a118d7eSpatrick if (efi_memprobe_find(EFI_SIZE_TO_PAGES(64 * 1024 * 1024), 4549a118d7eSpatrick 0x200000, &addr) != EFI_SUCCESS) 455f24071e5Spatrick printf("Can't allocate memory\n"); 456f24071e5Spatrick efi_loadaddr = addr; 457f24071e5Spatrick 458f24071e5Spatrick efi_timer_init(); 459f24071e5Spatrick efi_diskprobe(); 460f24071e5Spatrick } 461f24071e5Spatrick 462f24071e5Spatrick void 463f24071e5Spatrick efi_cleanup(void) 464f24071e5Spatrick { 465fa854029Spatrick int retry; 466fa854029Spatrick EFI_STATUS status; 467fa854029Spatrick 468f24071e5Spatrick efi_timer_cleanup(); 469f24071e5Spatrick 470fa854029Spatrick /* retry once in case of failure */ 471fa854029Spatrick for (retry = 1; retry >= 0; retry--) { 472fa854029Spatrick efi_memprobe_internal(); /* sync the current map */ 473f4dd1aebSkettenis efi_updatefdt(); 474fa854029Spatrick status = EFI_CALL(BS->ExitBootServices, IH, mmap_key); 475fa854029Spatrick if (status == EFI_SUCCESS) 476fa854029Spatrick break; 477fa854029Spatrick if (retry == 0) 478fa854029Spatrick panic("ExitBootServices failed (%d)", status); 479fa854029Spatrick } 480f24071e5Spatrick } 481f24071e5Spatrick 482f24071e5Spatrick void 483f24071e5Spatrick _rtt(void) 484f24071e5Spatrick { 485f24071e5Spatrick #ifdef EFI_DEBUG 486f24071e5Spatrick printf("Hit any key to reboot\n"); 487f24071e5Spatrick efi_cons_getc(0); 488f24071e5Spatrick #endif 489f24071e5Spatrick RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL); 490111d6387Skettenis for (;;) 491111d6387Skettenis continue; 492f24071e5Spatrick } 493f24071e5Spatrick 494f24071e5Spatrick /* 495f24071e5Spatrick * U-Boot only implements the GetTime() Runtime Service if it has been 496f24071e5Spatrick * configured with CONFIG_DM_RTC. Most board configurations don't 497f24071e5Spatrick * include that option, so we can't use it to implement our boot 498f24071e5Spatrick * prompt timeout. Instead we use timer events to simulate a clock 499f24071e5Spatrick * that ticks ever second. 500f24071e5Spatrick */ 501f24071e5Spatrick 502f24071e5Spatrick EFI_EVENT timer; 503f24071e5Spatrick int ticks; 504f24071e5Spatrick 505f24071e5Spatrick static VOID 506f24071e5Spatrick efi_timer(EFI_EVENT event, VOID *context) 507f24071e5Spatrick { 508f24071e5Spatrick ticks++; 509f24071e5Spatrick } 510f24071e5Spatrick 511f24071e5Spatrick static void 512f24071e5Spatrick efi_timer_init(void) 513f24071e5Spatrick { 514f24071e5Spatrick EFI_STATUS status; 515f24071e5Spatrick 516a4d88df4Sjsg status = BS->CreateEvent(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, 517f24071e5Spatrick efi_timer, NULL, &timer); 518f24071e5Spatrick if (status == EFI_SUCCESS) 519f24071e5Spatrick status = BS->SetTimer(timer, TimerPeriodic, 10000000); 520f24071e5Spatrick if (EFI_ERROR(status)) 521f24071e5Spatrick printf("Can't create timer\n"); 522f24071e5Spatrick } 523f24071e5Spatrick 524f24071e5Spatrick static void 525f24071e5Spatrick efi_timer_cleanup(void) 526f24071e5Spatrick { 527f24071e5Spatrick BS->CloseEvent(timer); 528f24071e5Spatrick } 529f24071e5Spatrick 530f24071e5Spatrick time_t 531f24071e5Spatrick getsecs(void) 532f24071e5Spatrick { 533f24071e5Spatrick return ticks; 534f24071e5Spatrick } 535f24071e5Spatrick 536f24071e5Spatrick /* 537f24071e5Spatrick * Various device-related bits. 538f24071e5Spatrick */ 539f24071e5Spatrick 540f24071e5Spatrick void 541f24071e5Spatrick devboot(dev_t dev, char *p) 542f24071e5Spatrick { 543f24071e5Spatrick strlcpy(p, "sd0a", 5); 544f24071e5Spatrick } 545f24071e5Spatrick 546f24071e5Spatrick int 547f24071e5Spatrick cnspeed(dev_t dev, int sp) 548f24071e5Spatrick { 549f24071e5Spatrick return 115200; 550f24071e5Spatrick } 551f24071e5Spatrick 552f24071e5Spatrick char * 553f24071e5Spatrick ttyname(int fd) 554f24071e5Spatrick { 555f24071e5Spatrick return "com0"; 556f24071e5Spatrick } 557f24071e5Spatrick 558f24071e5Spatrick dev_t 559f24071e5Spatrick ttydev(char *name) 560f24071e5Spatrick { 561f24071e5Spatrick return NODEV; 562f24071e5Spatrick } 563f24071e5Spatrick 564f24071e5Spatrick #define MAXDEVNAME 16 565f24071e5Spatrick 566f24071e5Spatrick /* 567f24071e5Spatrick * Parse a device spec. 568f24071e5Spatrick * 569f24071e5Spatrick * [A-Za-z]*[0-9]*[A-Za-z]:file 570f24071e5Spatrick * dev uint part 571f24071e5Spatrick */ 572f24071e5Spatrick int 573f24071e5Spatrick devparse(const char *fname, int *dev, int *unit, int *part, const char **file) 574f24071e5Spatrick { 575f24071e5Spatrick const char *s; 576f24071e5Spatrick 577f24071e5Spatrick *unit = 0; /* default to wd0a */ 578f24071e5Spatrick *part = 0; 579f24071e5Spatrick *dev = 0; 580f24071e5Spatrick 581f24071e5Spatrick s = strchr(fname, ':'); 582f24071e5Spatrick if (s != NULL) { 583f24071e5Spatrick int devlen; 584f24071e5Spatrick int i, u, p = 0; 585f24071e5Spatrick struct devsw *dp; 586f24071e5Spatrick char devname[MAXDEVNAME]; 587f24071e5Spatrick 588f24071e5Spatrick devlen = s - fname; 589f24071e5Spatrick if (devlen > MAXDEVNAME) 590f24071e5Spatrick return (EINVAL); 591f24071e5Spatrick 592f24071e5Spatrick /* extract device name */ 593f24071e5Spatrick for (i = 0; isalpha(fname[i]) && (i < devlen); i++) 594f24071e5Spatrick devname[i] = fname[i]; 595f24071e5Spatrick devname[i] = 0; 596f24071e5Spatrick 597f24071e5Spatrick if (!isdigit(fname[i])) 598f24071e5Spatrick return (EUNIT); 599f24071e5Spatrick 600f24071e5Spatrick /* device number */ 601f24071e5Spatrick for (u = 0; isdigit(fname[i]) && (i < devlen); i++) 602f24071e5Spatrick u = u * 10 + (fname[i] - '0'); 603f24071e5Spatrick 604f24071e5Spatrick if (!isalpha(fname[i])) 605f24071e5Spatrick return (EPART); 606f24071e5Spatrick 607f24071e5Spatrick /* partition number */ 608f24071e5Spatrick if (i < devlen) 609f24071e5Spatrick p = fname[i++] - 'a'; 610f24071e5Spatrick 611f24071e5Spatrick if (i != devlen) 612f24071e5Spatrick return (ENXIO); 613f24071e5Spatrick 614f24071e5Spatrick /* check device name */ 615f24071e5Spatrick for (dp = devsw, i = 0; i < ndevs; dp++, i++) { 616f24071e5Spatrick if (dp->dv_name && !strcmp(devname, dp->dv_name)) 617f24071e5Spatrick break; 618f24071e5Spatrick } 619f24071e5Spatrick 620f24071e5Spatrick if (i >= ndevs) 621f24071e5Spatrick return (ENXIO); 622f24071e5Spatrick 623f24071e5Spatrick *unit = u; 624f24071e5Spatrick *part = p; 625f24071e5Spatrick *dev = i; 626f24071e5Spatrick fname = ++s; 627f24071e5Spatrick } 628f24071e5Spatrick 629f24071e5Spatrick *file = fname; 630f24071e5Spatrick 631f24071e5Spatrick return (0); 632f24071e5Spatrick } 633f24071e5Spatrick 634f24071e5Spatrick int 635f24071e5Spatrick devopen(struct open_file *f, const char *fname, char **file) 636f24071e5Spatrick { 637f24071e5Spatrick struct devsw *dp; 638f24071e5Spatrick int dev, unit, part, error; 639f24071e5Spatrick 640f24071e5Spatrick error = devparse(fname, &dev, &unit, &part, (const char **)file); 641f24071e5Spatrick if (error) 642f24071e5Spatrick return (error); 643f24071e5Spatrick 644f24071e5Spatrick dp = &devsw[0]; 645f24071e5Spatrick f->f_dev = dp; 646f24071e5Spatrick 647f24071e5Spatrick return (*dp->dv_open)(f, unit, part); 648f24071e5Spatrick } 649f24071e5Spatrick 650fa854029Spatrick static void 651fa854029Spatrick efi_memprobe_internal(void) 652fa854029Spatrick { 653fa854029Spatrick EFI_STATUS status; 654fa854029Spatrick UINTN mapkey, mmsiz, siz; 655fa854029Spatrick UINT32 mmver; 656fa854029Spatrick EFI_MEMORY_DESCRIPTOR *mm; 657fa854029Spatrick int n; 658fa854029Spatrick 659fa854029Spatrick free(mmap, mmap_ndesc * mmap_descsiz); 660fa854029Spatrick 661fa854029Spatrick siz = 0; 662fa854029Spatrick status = EFI_CALL(BS->GetMemoryMap, &siz, NULL, &mapkey, &mmsiz, 663fa854029Spatrick &mmver); 664fa854029Spatrick if (status != EFI_BUFFER_TOO_SMALL) 665fa854029Spatrick panic("cannot get the size of memory map"); 666fa854029Spatrick mm = alloc(siz); 667fa854029Spatrick status = EFI_CALL(BS->GetMemoryMap, &siz, mm, &mapkey, &mmsiz, &mmver); 668fa854029Spatrick if (status != EFI_SUCCESS) 669fa854029Spatrick panic("cannot get the memory map"); 670fa854029Spatrick n = siz / mmsiz; 671fa854029Spatrick mmap = mm; 672fa854029Spatrick mmap_key = mapkey; 673fa854029Spatrick mmap_ndesc = n; 674fa854029Spatrick mmap_descsiz = mmsiz; 675f4dd1aebSkettenis mmap_version = mmver; 676fa854029Spatrick } 677fa854029Spatrick 678f24071e5Spatrick /* 679f24071e5Spatrick * 64-bit ARMs can have a much wider memory mapping, as in somewhere 680f24071e5Spatrick * after the 32-bit region. To cope with our alignment requirement, 681f24071e5Spatrick * use the memory table to find a place where we can fit. 682f24071e5Spatrick */ 683f24071e5Spatrick static EFI_STATUS 684f24071e5Spatrick efi_memprobe_find(UINTN pages, UINTN align, EFI_PHYSICAL_ADDRESS *addr) 685f24071e5Spatrick { 686fa854029Spatrick EFI_MEMORY_DESCRIPTOR *mm; 687fa854029Spatrick int i, j; 688f24071e5Spatrick 689f24071e5Spatrick if (align < EFI_PAGE_SIZE) 690f24071e5Spatrick return EFI_INVALID_PARAMETER; 691f24071e5Spatrick 692fa854029Spatrick efi_memprobe_internal(); /* sync the current map */ 693f24071e5Spatrick 694fa854029Spatrick for (i = 0, mm = mmap; i < mmap_ndesc; 695fa854029Spatrick i++, mm = NextMemoryDescriptor(mm, mmap_descsiz)) { 696f24071e5Spatrick if (mm->Type != EfiConventionalMemory) 697f24071e5Spatrick continue; 698f24071e5Spatrick 699f24071e5Spatrick if (mm->NumberOfPages < pages) 700f24071e5Spatrick continue; 701f24071e5Spatrick 702f2c0e408Skettenis for (j = 0; j < mm->NumberOfPages; j++) { 703f24071e5Spatrick EFI_PHYSICAL_ADDRESS paddr; 704f24071e5Spatrick 705f24071e5Spatrick if (mm->NumberOfPages - j < pages) 706f24071e5Spatrick break; 707f24071e5Spatrick 708f24071e5Spatrick paddr = mm->PhysicalStart + (j * EFI_PAGE_SIZE); 70973dc1409Spatrick if (paddr & (align - 1)) 71073dc1409Spatrick continue; 71173dc1409Spatrick 71273dc1409Spatrick if (EFI_CALL(BS->AllocatePages, AllocateAddress, 71373dc1409Spatrick EfiLoaderData, pages, &paddr) == EFI_SUCCESS) { 714f24071e5Spatrick *addr = paddr; 715f24071e5Spatrick return EFI_SUCCESS; 716f24071e5Spatrick } 717f24071e5Spatrick } 718f24071e5Spatrick } 719f24071e5Spatrick return EFI_OUT_OF_RESOURCES; 720f24071e5Spatrick } 721111d6387Skettenis 722111d6387Skettenis /* 723111d6387Skettenis * Commands 724111d6387Skettenis */ 725111d6387Skettenis 726111d6387Skettenis int Xexit_efi(void); 727111d6387Skettenis int Xpoweroff_efi(void); 728111d6387Skettenis 729111d6387Skettenis const struct cmd_table cmd_machine[] = { 730111d6387Skettenis { "exit", CMDT_CMD, Xexit_efi }, 731111d6387Skettenis { "poweroff", CMDT_CMD, Xpoweroff_efi }, 732111d6387Skettenis { NULL, 0 } 733111d6387Skettenis }; 734111d6387Skettenis 735111d6387Skettenis int 736111d6387Skettenis Xexit_efi(void) 737111d6387Skettenis { 738111d6387Skettenis EFI_CALL(BS->Exit, IH, 0, 0, NULL); 739111d6387Skettenis for (;;) 740111d6387Skettenis continue; 741111d6387Skettenis return (0); 742111d6387Skettenis } 743111d6387Skettenis 744111d6387Skettenis int 745111d6387Skettenis Xpoweroff_efi(void) 746111d6387Skettenis { 747111d6387Skettenis EFI_CALL(RS->ResetSystem, EfiResetShutdown, EFI_SUCCESS, 0, NULL); 748111d6387Skettenis return (0); 749111d6387Skettenis } 750