/* $OpenBSD: efiboot.c,v 1.62 2024/11/12 20:49:42 tobhe Exp $ */ /* * Copyright (c) 2015 YASUOKA Masahiko * Copyright (c) 2016 Mark Kettenis * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libsa.h" #include "disk.h" #include "softraid_arm64.h" #include "efidev.h" #include "efiboot.h" #include "efidt.h" #include "fdt.h" EFI_SYSTEM_TABLE *ST; EFI_BOOT_SERVICES *BS; EFI_RUNTIME_SERVICES *RS; EFI_HANDLE IH, efi_bootdp; void *fdt_sys = NULL; void *fdt_override = NULL; size_t fdt_override_size; void *smbios = NULL; EFI_PHYSICAL_ADDRESS heap; UINTN heapsiz = 1 * 1024 * 1024; EFI_MEMORY_DESCRIPTOR *mmap; UINTN mmap_key; UINTN mmap_ndesc; UINTN mmap_descsiz; UINT32 mmap_version; static EFI_GUID imgp_guid = LOADED_IMAGE_PROTOCOL; static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL; static EFI_GUID devp_guid = DEVICE_PATH_PROTOCOL; static EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; static EFI_GUID fdt_guid = FDT_TABLE_GUID; static EFI_GUID smbios_guid = SMBIOS_TABLE_GUID; static EFI_GUID smbios3_guid = SMBIOS3_TABLE_GUID; static EFI_GUID dt_fixup_guid = EFI_DT_FIXUP_PROTOCOL_GUID; #define efi_guidcmp(_a, _b) memcmp((_a), (_b), sizeof(EFI_GUID)) int efi_device_path_depth(EFI_DEVICE_PATH *dp, int); int efi_device_path_ncmp(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *, int); static void efi_heap_init(void); static void efi_memprobe_internal(void); static void efi_timer_init(void); static void efi_timer_cleanup(void); static EFI_STATUS efi_memprobe_find(UINTN, UINTN, EFI_MEMORY_TYPE, EFI_PHYSICAL_ADDRESS *); void *efi_fdt(void); int fdt_load_override(char *); extern void smbios_init(void *); EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab) { extern char *progname; EFI_LOADED_IMAGE *imgp; EFI_DEVICE_PATH *dp = NULL; EFI_STATUS status; int i; ST = systab; BS = ST->BootServices; RS = ST->RuntimeServices; IH = image; /* disable reset by watchdog after 5 minutes */ BS->SetWatchdogTimer(0, 0, 0, NULL); status = BS->HandleProtocol(image, &imgp_guid, (void **)&imgp); if (status == EFI_SUCCESS) status = BS->HandleProtocol(imgp->DeviceHandle, &devp_guid, (void **)&dp); if (status == EFI_SUCCESS) efi_bootdp = dp; for (i = 0; i < ST->NumberOfTableEntries; i++) { if (efi_guidcmp(&fdt_guid, &ST->ConfigurationTable[i].VendorGuid) == 0) fdt_sys = ST->ConfigurationTable[i].VendorTable; if (efi_guidcmp(&smbios_guid, &ST->ConfigurationTable[i].VendorGuid) == 0) smbios = ST->ConfigurationTable[i].VendorTable; if (efi_guidcmp(&smbios3_guid, &ST->ConfigurationTable[i].VendorGuid) == 0) smbios = ST->ConfigurationTable[i].VendorTable; } fdt_init(fdt_sys); progname = "BOOTAA64"; boot(0); return (EFI_SUCCESS); } static SIMPLE_TEXT_OUTPUT_INTERFACE *conout; static SIMPLE_INPUT_INTERFACE *conin; /* * The device majors for these don't match the ones used by the * kernel. That's fine. They're just used as an index into the cdevs * array and never passed on to the kernel. */ static dev_t serial = makedev(1, 0); static dev_t framebuffer = makedev(2, 0); static char framebuffer_path[128]; void efi_cons_probe(struct consdev *cn) { cn->cn_pri = CN_MIDPRI; cn->cn_dev = makedev(0, 0); } void efi_cons_init(struct consdev *cp) { conin = ST->ConIn; conout = ST->ConOut; } int efi_cons_getc(dev_t dev) { EFI_INPUT_KEY key; EFI_STATUS status; #if 0 UINTN dummy; #endif static int lastchar = 0; if (lastchar) { int r = lastchar; if ((dev & 0x80) == 0) lastchar = 0; return (r); } status = conin->ReadKeyStroke(conin, &key); while (status == EFI_NOT_READY || key.UnicodeChar == 0) { if (dev & 0x80) return (0); /* * XXX The implementation of WaitForEvent() in U-boot * is broken and neverreturns. */ #if 0 BS->WaitForEvent(1, &conin->WaitForKey, &dummy); #endif status = conin->ReadKeyStroke(conin, &key); } if (dev & 0x80) lastchar = key.UnicodeChar; return (key.UnicodeChar); } void efi_cons_putc(dev_t dev, int c) { CHAR16 buf[2]; if (c == '\n') efi_cons_putc(dev, '\r'); buf[0] = c; buf[1] = 0; conout->OutputString(conout, buf); } void efi_com_probe(struct consdev *cn) { cn->cn_pri = CN_LOWPRI; cn->cn_dev = serial; } void efi_com_init(struct consdev *cn) { conin = ST->ConIn; conout = ST->ConOut; } int efi_com_getc(dev_t dev) { return efi_cons_getc(dev); } void efi_com_putc(dev_t dev, int c) { efi_cons_putc(dev, c); } void efi_fb_probe(struct consdev *cn) { cn->cn_pri = CN_LOWPRI; cn->cn_dev = framebuffer; } void efi_fb_init(struct consdev *cn) { conin = ST->ConIn; conout = ST->ConOut; } int efi_fb_getc(dev_t dev) { return efi_cons_getc(dev); } void efi_fb_putc(dev_t dev, int c) { efi_cons_putc(dev, c); } static void efi_heap_init(void) { EFI_STATUS status; status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, EFI_SIZE_TO_PAGES(heapsiz), &heap); if (status != EFI_SUCCESS) panic("BS->AllocatePages()"); } struct disklist_lh disklist; struct diskinfo *bootdev_dip; void efi_diskprobe(void) { int i, bootdev = 0, depth = -1; UINTN sz; EFI_STATUS status; EFI_HANDLE *handles = NULL; EFI_BLOCK_IO *blkio; EFI_BLOCK_IO_MEDIA *media; struct diskinfo *di; EFI_DEVICE_PATH *dp; TAILQ_INIT(&disklist); sz = 0; status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, 0); if (status == EFI_BUFFER_TOO_SMALL) { handles = alloc(sz); status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, handles); } if (handles == NULL || EFI_ERROR(status)) return; if (efi_bootdp != NULL) depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH); /* * U-Boot incorrectly represents devices with a single * MEDIA_DEVICE_PATH component. In that case include that * component into the matching, otherwise we'll blindly select * the first device. */ if (depth == 0) depth = 1; for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) { status = BS->HandleProtocol(handles[i], &blkio_guid, (void **)&blkio); if (EFI_ERROR(status)) panic("BS->HandleProtocol() returns %d", status); media = blkio->Media; if (media->LogicalPartition || !media->MediaPresent) continue; di = alloc(sizeof(struct diskinfo)); efid_init(di, blkio); if (efi_bootdp == NULL || depth == -1 || bootdev != 0) goto next; status = BS->HandleProtocol(handles[i], &devp_guid, (void **)&dp); if (EFI_ERROR(status)) goto next; if (efi_device_path_ncmp(efi_bootdp, dp, depth) == 0) { TAILQ_INSERT_HEAD(&disklist, di, list); bootdev_dip = di; bootdev = 1; continue; } next: TAILQ_INSERT_TAIL(&disklist, di, list); } free(handles, sz); /* Print available disks and probe for softraid. */ i = 0; printf("disks:"); TAILQ_FOREACH(di, &disklist, list) { printf(" sd%d%s", i, di == bootdev_dip ? "*" : ""); i++; } srprobe(); printf("\n"); } /* * Determine the number of nodes up to, but not including, the first * node of the specified type. */ int efi_device_path_depth(EFI_DEVICE_PATH *dp, int dptype) { int i; for (i = 0; !IsDevicePathEnd(dp); dp = NextDevicePathNode(dp), i++) { if (DevicePathType(dp) == dptype) return (i); } return (i); } int efi_device_path_ncmp(EFI_DEVICE_PATH *dpa, EFI_DEVICE_PATH *dpb, int deptn) { int i, cmp; for (i = 0; i < deptn; i++) { if (IsDevicePathEnd(dpa) || IsDevicePathEnd(dpb)) return ((IsDevicePathEnd(dpa) && IsDevicePathEnd(dpb)) ? 0 : (IsDevicePathEnd(dpa))? -1 : 1); cmp = DevicePathNodeLength(dpa) - DevicePathNodeLength(dpb); if (cmp) return (cmp); cmp = memcmp(dpa, dpb, DevicePathNodeLength(dpa)); if (cmp) return (cmp); dpa = NextDevicePathNode(dpa); dpb = NextDevicePathNode(dpb); } return (0); } void efi_framebuffer(void) { EFI_GRAPHICS_OUTPUT *gop; EFI_STATUS status; void *node, *child; uint32_t acells, scells; uint64_t base, size; uint32_t reg[4]; uint32_t width, height, stride, pxsize; char *format; char *prop; /* * Don't create a "simple-framebuffer" node if we already have * one. Besides "/chosen", we also check under "/" since that * is where the Raspberry Pi firmware puts it. */ node = fdt_find_node("/chosen"); for (child = fdt_child_node(node); child; child = fdt_next_node(child)) { if (!fdt_node_is_compatible(child, "simple-framebuffer")) continue; if (!fdt_node_property(child, "status", &prop) || strcmp(prop, "okay") == 0) { strlcpy(framebuffer_path, "/chosen/", sizeof(framebuffer_path)); strlcat(framebuffer_path, fdt_node_name(child), sizeof(framebuffer_path)); return; } } node = fdt_find_node("/"); for (child = fdt_child_node(node); child; child = fdt_next_node(child)) { if (!fdt_node_is_compatible(child, "simple-framebuffer")) continue; if (!fdt_node_property(child, "status", &prop) || strcmp(prop, "okay") == 0) { strlcpy(framebuffer_path, "/", sizeof(framebuffer_path)); strlcat(framebuffer_path, fdt_node_name(child), sizeof(framebuffer_path)); return; } } status = BS->LocateProtocol(&gop_guid, NULL, (void **)&gop); if (status != EFI_SUCCESS) return; /* Paranoia! */ if (gop == NULL || gop->Mode == NULL || gop->Mode->Info == NULL) return; switch (gop->Mode->Info->PixelFormat) { case PixelRedGreenBlueReserved8BitPerColor: format = "x8b8g8r8"; pxsize = 4; break; case PixelBlueGreenRedReserved8BitPerColor: format = "x8r8g8b8"; pxsize = 4; break; case PixelBitMask: { EFI_PIXEL_BITMASK *bm = &gop->Mode->Info->PixelInformation; if (bm->RedMask == 0xf800 && bm->GreenMask == 0x07e0 && bm->BlueMask == 0x001f) { format = "r5g6b5"; pxsize = 2; break; } printf("Unsupported PixelInformation bitmasks\n"); /* FALLTHROUGH */ } default: printf("Unsupported PixelFormat %d, not adding " "\"simple-framebuffer\" DT node\n", gop->Mode->Info->PixelFormat); return; } base = gop->Mode->FrameBufferBase; size = gop->Mode->FrameBufferSize; width = htobe32(gop->Mode->Info->HorizontalResolution); height = htobe32(gop->Mode->Info->VerticalResolution); stride = htobe32(gop->Mode->Info->PixelsPerScanLine * pxsize); node = fdt_find_node("/"); if (fdt_node_property_int(node, "#address-cells", &acells) != 1) acells = 1; if (fdt_node_property_int(node, "#size-cells", &scells) != 1) scells = 1; if (acells > 2 || scells > 2) return; if (acells >= 1) reg[0] = htobe32(base); if (acells == 2) { reg[1] = reg[0]; reg[0] = htobe32(base >> 32); } if (scells >= 1) reg[acells] = htobe32(size); if (scells == 2) { reg[acells + 1] = reg[acells]; reg[acells] = htobe32(size >> 32); } node = fdt_find_node("/chosen"); fdt_node_add_node(node, "framebuffer", &child); fdt_node_add_property(child, "status", "okay", strlen("okay") + 1); fdt_node_add_property(child, "format", format, strlen(format) + 1); fdt_node_add_property(child, "stride", &stride, 4); fdt_node_add_property(child, "height", &height, 4); fdt_node_add_property(child, "width", &width, 4); fdt_node_add_property(child, "reg", reg, (acells + scells) * 4); fdt_node_add_property(child, "compatible", "simple-framebuffer", strlen("simple-framebuffer") + 1); strlcpy(framebuffer_path, "/chosen/framebuffer", sizeof(framebuffer_path)); } void efi_console(void) { void *node; if (major(cn_tab->cn_dev) == major(serial)) { char *serial_path; char alias[16]; int len; /* Construct alias and resolve it. */ snprintf(alias, sizeof(alias), "serial%d", minor(cn_tab->cn_dev)); node = fdt_find_node("/aliases"); len = fdt_node_property(node, alias, &serial_path); if (len <= 0) return; /* Point stdout-path at the serial node. */ node = fdt_find_node("/chosen"); fdt_node_add_property(node, "stdout-path", serial_path, strlen(serial_path) + 1); } else if (major(cn_tab->cn_dev) == major(framebuffer)) { if (strlen(framebuffer_path) == 0) return; /* Point stdout-path at the framebuffer node. */ node = fdt_find_node("/chosen"); fdt_node_add_property(node, "stdout-path", framebuffer_path, strlen(framebuffer_path) + 1); } } uint64_t dma_constraint[2] = { 0, -1 }; void efi_dma_constraint(void) { void *node; char *prop; uint32_t *propint; uint64_t base, size; uint32_t pacells, pscells; uint32_t acells, scells; int len; node = fdt_find_node("/"); if (fdt_node_property_int(node, "#address-cells", &pacells) != 1) pacells = 1; if (fdt_node_property_int(node, "#size-cells", &pscells) != 1) pscells = 1; if (pacells > 2 || pscells > 2) return; node = fdt_find_node("/soc"); if (node != NULL) { if (fdt_node_property_int(node, "#address-cells", &acells) != 1) acells = pacells; if (fdt_node_property_int(node, "#size-cells", &scells) != 1) scells = pscells; if (acells > 2 || scells > 2) return; len = fdt_node_property(node, "dma-ranges", &prop); propint = (uint32_t *)prop; if (len == (acells + pacells + scells) * sizeof(uint32_t)) { base = betoh32(propint[acells]); if (pacells == 2) base = (base << 32) | betoh32(propint[acells + 1]); size = betoh32(propint[acells + pacells]); if (scells == 2) size = (size << 32) | betoh32(propint[acells + pacells + 1]); dma_constraint[0] = htobe64(base); dma_constraint[1] = htobe64(base + size - 1); } } /* * Some SoC's have DMA constraints that aren't explicitly * advertised. */ node = fdt_find_node("/"); if (fdt_node_is_compatible(node, "brcm,bcm2711")) dma_constraint[1] = htobe64(0x3bffffff); if (fdt_node_is_compatible(node, "rockchip,rk3566") || fdt_node_is_compatible(node, "rockchip,rk3568") || fdt_node_is_compatible(node, "rockchip,rk3588") || fdt_node_is_compatible(node, "rockchip,rk3588s")) dma_constraint[1] = htobe64(0xffffffff); if (fdt_node_is_compatible(node, "qcom,sc8280xp") || fdt_node_is_compatible(node, "qcom,x1e80100")) dma_constraint[1] = htobe64(0xffffffff); /* Pass DMA constraint. */ node = fdt_find_node("/chosen"); fdt_node_add_property(node, "openbsd,dma-constraint", dma_constraint, sizeof(dma_constraint)); } int acpi = 0; char *bootmac = NULL; void * efi_makebootargs(char *bootargs, int howto) { struct sr_boot_volume *bv; u_char bootduid[8]; u_char zero[8] = { 0 }; uint64_t uefi_system_table = htobe64((uintptr_t)ST); uint32_t boothowto = htobe32(howto); EFI_PHYSICAL_ADDRESS addr; void *node, *fdt; size_t len; fdt = efi_fdt(); if (fdt == NULL || acpi) fdt = efi_acpi(); if (!fdt_get_size(fdt)) return NULL; len = roundup(fdt_get_size(fdt) + PAGE_SIZE, PAGE_SIZE); if (BS->AllocatePages(AllocateAnyPages, EfiLoaderData, EFI_SIZE_TO_PAGES(len), &addr) == EFI_SUCCESS) { memcpy((void *)addr, fdt, fdt_get_size(fdt)); ((struct fdt_head *)addr)->fh_size = htobe32(len); fdt = (void *)addr; } if (!fdt_init(fdt)) return NULL; /* Create common nodes which might not exist when using mach dtb */ node = fdt_find_node("/aliases"); if (node == NULL) fdt_node_add_node(fdt_find_node("/"), "aliases", &node); node = fdt_find_node("/chosen"); if (node == NULL) fdt_node_add_node(fdt_find_node("/"), "chosen", &node); node = fdt_find_node("/chosen"); len = strlen(bootargs) + 1; fdt_node_add_property(node, "bootargs", bootargs, len); fdt_node_add_property(node, "openbsd,boothowto", &boothowto, sizeof(boothowto)); /* Pass DUID of the boot disk. */ if (bootdev_dip) { memcpy(&bootduid, bootdev_dip->disklabel.d_uid, sizeof(bootduid)); if (memcmp(bootduid, zero, sizeof(bootduid)) != 0) { fdt_node_add_property(node, "openbsd,bootduid", bootduid, sizeof(bootduid)); } if (bootdev_dip->sr_vol != NULL) { bv = bootdev_dip->sr_vol; fdt_node_add_property(node, "openbsd,sr-bootuuid", &bv->sbv_uuid, sizeof(bv->sbv_uuid)); if (bv->sbv_maskkey != NULL) fdt_node_add_property(node, "openbsd,sr-bootkey", bv->sbv_maskkey, SR_CRYPTO_MAXKEYBYTES); } } sr_clear_keys(); /* Pass netboot interface address. */ if (bootmac) fdt_node_add_property(node, "openbsd,bootmac", bootmac, 6); /* Pass EFI system table. */ fdt_node_add_property(node, "openbsd,uefi-system-table", &uefi_system_table, sizeof(uefi_system_table)); /* Placeholders for EFI memory map. */ fdt_node_add_property(node, "openbsd,uefi-mmap-start", zero, 8); fdt_node_add_property(node, "openbsd,uefi-mmap-size", zero, 4); fdt_node_add_property(node, "openbsd,uefi-mmap-desc-size", zero, 4); fdt_node_add_property(node, "openbsd,uefi-mmap-desc-ver", zero, 4); efi_framebuffer(); efi_console(); efi_dma_constraint(); fdt_finalize(); return fdt; } void efi_updatefdt(void) { uint64_t uefi_mmap_start = htobe64((uintptr_t)mmap); uint32_t uefi_mmap_size = htobe32(mmap_ndesc * mmap_descsiz); uint32_t uefi_mmap_desc_size = htobe32(mmap_descsiz); uint32_t uefi_mmap_desc_ver = htobe32(mmap_version); void *node; node = fdt_find_node("/chosen"); if (!node) return; /* Pass EFI memory map. */ fdt_node_set_property(node, "openbsd,uefi-mmap-start", &uefi_mmap_start, sizeof(uefi_mmap_start)); fdt_node_set_property(node, "openbsd,uefi-mmap-size", &uefi_mmap_size, sizeof(uefi_mmap_size)); fdt_node_set_property(node, "openbsd,uefi-mmap-desc-size", &uefi_mmap_desc_size, sizeof(uefi_mmap_desc_size)); fdt_node_set_property(node, "openbsd,uefi-mmap-desc-ver", &uefi_mmap_desc_ver, sizeof(uefi_mmap_desc_ver)); fdt_finalize(); } u_long efi_loadaddr; void machdep(void) { EFI_PHYSICAL_ADDRESS addr; cninit(); efi_heap_init(); smbios_init(smbios); /* * The kernel expects to be loaded into a block of memory aligned * on a 2MB boundary. We allocate a block of 64MB of memory, which * gives us plenty of room for growth. */ if (efi_memprobe_find(EFI_SIZE_TO_PAGES(64 * 1024 * 1024), 0x200000, EfiLoaderCode, &addr) != EFI_SUCCESS) printf("Can't allocate memory\n"); efi_loadaddr = addr; efi_timer_init(); efi_diskprobe(); efi_pxeprobe(); } void efi_cleanup(void) { int retry; EFI_STATUS status; efi_timer_cleanup(); /* retry once in case of failure */ for (retry = 1; retry >= 0; retry--) { efi_memprobe_internal(); /* sync the current map */ efi_updatefdt(); status = BS->ExitBootServices(IH, mmap_key); if (status == EFI_SUCCESS) break; if (retry == 0) panic("ExitBootServices failed (%d)", status); } } void _rtt(void) { #ifdef EFI_DEBUG printf("Hit any key to reboot\n"); efi_cons_getc(0); #endif RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL); for (;;) continue; } /* * U-Boot only implements the GetTime() Runtime Service if it has been * configured with CONFIG_DM_RTC. Most board configurations don't * include that option, so we can't use it to implement our boot * prompt timeout. Instead we use timer events to simulate a clock * that ticks ever second. */ EFI_EVENT timer; int ticks; static VOID efi_timer(EFI_EVENT event, VOID *context) { ticks++; } static void efi_timer_init(void) { EFI_STATUS status; status = BS->CreateEvent(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, efi_timer, NULL, &timer); if (status == EFI_SUCCESS) status = BS->SetTimer(timer, TimerPeriodic, 10000000); if (EFI_ERROR(status)) printf("Can't create timer\n"); } static void efi_timer_cleanup(void) { BS->CloseEvent(timer); } time_t getsecs(void) { return ticks; } /* * Various device-related bits. */ void devboot(dev_t dev, char *p) { struct sr_boot_volume *bv; struct sr_boot_chunk *bc; struct diskinfo *dip; int sd_boot_vol = 0; int sr_boot_vol = -1; int part_type = FS_UNUSED; if (bootdev_dip == NULL) { strlcpy(p, "tftp0a", 7); return; } /* * If there is no BSD disklabel on the boot device, boot from * the ESP instead. */ if ((bootdev_dip->flags & DISKINFO_FLAG_GOODLABEL) == 0) { strlcpy(p, "esp0a", 6); return; } TAILQ_FOREACH(dip, &disklist, list) { if (bootdev_dip == dip) break; sd_boot_vol++; } /* * Determine the partition type for the 'a' partition of the * boot device. */ part_type = bootdev_dip->disklabel.d_partitions[0].p_fstype; /* * See if we booted from a disk that is a member of a bootable * softraid volume. */ SLIST_FOREACH(bv, &sr_volumes, sbv_link) { SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) if (bc->sbc_diskinfo == bootdev_dip) sr_boot_vol = bv->sbv_unit; if (sr_boot_vol != -1) break; } if (sr_boot_vol != -1 && part_type != FS_BSDFFS) { strlcpy(p, "sr0a", 5); p[2] = '0' + sr_boot_vol; return; } strlcpy(p, "sd0a", 5); p[2] = '0' + sd_boot_vol; } const char cdevs[][4] = { "cons", "com", "fb" }; const int ncdevs = nitems(cdevs); int cnspeed(dev_t dev, int sp) { return 115200; } char ttyname_buf[8]; char * ttyname(int fd) { snprintf(ttyname_buf, sizeof ttyname_buf, "%s%d", cdevs[major(cn_tab->cn_dev)], minor(cn_tab->cn_dev)); return ttyname_buf; } dev_t ttydev(char *name) { int i, unit = -1; char *no = name + strlen(name) - 1; while (no >= name && *no >= '0' && *no <= '9') unit = (unit < 0 ? 0 : (unit * 10)) + *no-- - '0'; if (no < name || unit < 0) return NODEV; for (i = 0; i < ncdevs; i++) if (strncmp(name, cdevs[i], no - name + 1) == 0) return makedev(i, unit); return NODEV; } #define MAXDEVNAME 16 /* * Parse a device spec. * * [A-Za-z]*[0-9]*[A-Za-z]:file * dev uint part */ int devparse(const char *fname, int *dev, int *unit, int *part, const char **file) { const char *s; *unit = 0; /* default to wd0a */ *part = 0; *dev = 0; s = strchr(fname, ':'); if (s != NULL) { int devlen; int i, u, p = 0; struct devsw *dp; char devname[MAXDEVNAME]; devlen = s - fname; if (devlen > MAXDEVNAME) return (EINVAL); /* extract device name */ for (i = 0; isalpha(fname[i]) && (i < devlen); i++) devname[i] = fname[i]; devname[i] = 0; if (!isdigit(fname[i])) return (EUNIT); /* device number */ for (u = 0; isdigit(fname[i]) && (i < devlen); i++) u = u * 10 + (fname[i] - '0'); if (!isalpha(fname[i])) return (EPART); /* partition number */ if (i < devlen) p = fname[i++] - 'a'; if (i != devlen) return (ENXIO); /* check device name */ for (dp = devsw, i = 0; i < ndevs; dp++, i++) { if (dp->dv_name && !strcmp(devname, dp->dv_name)) break; } if (i >= ndevs) return (ENXIO); *unit = u; *part = p; *dev = i; fname = ++s; } *file = fname; return (0); } int devopen(struct open_file *f, const char *fname, char **file) { struct devsw *dp; int dev, unit, part, error; error = devparse(fname, &dev, &unit, &part, (const char **)file); if (error) return (error); dp = &devsw[dev]; f->f_dev = dp; if (strcmp("tftp", dp->dv_name) != 0) { /* * Clear bootmac, to signal that we loaded this file from a * non-network device. */ bootmac = NULL; } return (*dp->dv_open)(f, unit, part); } static void efi_memprobe_internal(void) { EFI_STATUS status; UINTN mapkey, mmsiz, siz; UINT32 mmver; EFI_MEMORY_DESCRIPTOR *mm; int n; free(mmap, mmap_ndesc * mmap_descsiz); siz = 0; status = BS->GetMemoryMap(&siz, NULL, &mapkey, &mmsiz, &mmver); if (status != EFI_BUFFER_TOO_SMALL) panic("cannot get the size of memory map"); mm = alloc(siz); status = BS->GetMemoryMap(&siz, mm, &mapkey, &mmsiz, &mmver); if (status != EFI_SUCCESS) panic("cannot get the memory map"); n = siz / mmsiz; mmap = mm; mmap_key = mapkey; mmap_ndesc = n; mmap_descsiz = mmsiz; mmap_version = mmver; } /* * 64-bit ARMs can have a much wider memory mapping, as in somewhere * after the 32-bit region. To cope with our alignment requirement, * use the memory table to find a place where we can fit. */ static EFI_STATUS efi_memprobe_find(UINTN pages, UINTN align, EFI_MEMORY_TYPE type, EFI_PHYSICAL_ADDRESS *addr) { EFI_MEMORY_DESCRIPTOR *mm; int i, j; if (align < EFI_PAGE_SIZE) return EFI_INVALID_PARAMETER; efi_memprobe_internal(); /* sync the current map */ for (i = 0, mm = mmap; i < mmap_ndesc; i++, mm = NextMemoryDescriptor(mm, mmap_descsiz)) { if (mm->Type != EfiConventionalMemory) continue; if (mm->NumberOfPages < pages) continue; for (j = 0; j < mm->NumberOfPages; j++) { EFI_PHYSICAL_ADDRESS paddr; if (mm->NumberOfPages - j < pages) break; paddr = mm->PhysicalStart + (j * EFI_PAGE_SIZE); if (paddr & (align - 1)) continue; if (BS->AllocatePages(AllocateAddress, type, pages, &paddr) == EFI_SUCCESS) { *addr = paddr; return EFI_SUCCESS; } } } return EFI_OUT_OF_RESOURCES; } int mdrandom(char *buf, size_t buflen) { char *random; void *node; int i, len, ret = -1; node = fdt_find_node("/chosen"); if (!node) return -1; len = fdt_node_property(node, "rng-seed", &random); if (len > 0) { for (i = 0; i < buflen; i++) buf[i] ^= random[i % len]; ret = 0; } len = fdt_node_property(node, "kaslr-seed", &random); if (len > 0) { for (i = 0; i < buflen; i++) buf[i] ^= random[i % len]; ret = 0; } return ret; } #define FW_PATH "/etc/firmware/dtb/" struct smbios_dtb { const char *vendor; const char *prod; const char *dtb; } smbios_dtb[] = { /* Keep the list below sorted by vendor */ { "ASUS", "ASUS Vivobook S 15 S5507", "qcom/x1e80100-asus-vivobook-s15.dtb" }, { "HP", "HP OmniBook X Laptop 14-fe0xxx", "qcom/x1e80100-hp-omnibook-x14.dtb" }, { "LENOVO", "21BX", "qcom/sc8280xp-lenovo-thinkpad-x13s.dtb" }, { "LENOVO", "21BY", "qcom/sc8280xp-lenovo-thinkpad-x13s.dtb" }, { "LENOVO", "21N1", "qcom/x1e78100-lenovo-thinkpad-t14s.dtb" }, { "LENOVO", "21N2", "qcom/x1e78100-lenovo-thinkpad-t14s.dtb" }, { "LENOVO", "83ED", "qcom/x1e80100-lenovo-yoga-slim7x.dtb" }, { "SAMSUNG", "Galaxy Book4 Edge", "qcom/x1e80100-samsung-galaxy-book4-edge.dtb" }, }; void * efi_fdt(void) { extern char *hw_vendor, *hw_prod; size_t vendorlen, prodlen; char dtb[256]; int i; /* 'mach dtb' has precedence */ if (fdt_override != NULL) return fdt_override; /* Return system provided one */ if (hw_vendor == NULL || hw_prod == NULL) return fdt_sys; for (i = 0; i < nitems(smbios_dtb); i++) { vendorlen = strlen(smbios_dtb[i].vendor); prodlen = strlen(smbios_dtb[i].prod); if (strncmp(hw_vendor, smbios_dtb[i].vendor, vendorlen) == 0 && strncmp(hw_prod, smbios_dtb[i].prod, prodlen) == 0) { snprintf(dtb, sizeof(dtb), "%s%s", FW_PATH, smbios_dtb[i].dtb); fdt_load_override(dtb); /* TODO: find a better mechanism */ cnset(ttydev("fb0")); } } return fdt_override ? fdt_override : fdt_sys; } int fdt_load_override(char *file) { EFI_DT_FIXUP_PROTOCOL *dt_fixup; EFI_PHYSICAL_ADDRESS addr; char path[MAXPATHLEN]; EFI_STATUS status; struct stat sb; size_t dt_size; UINTN sz; int fd; if (file == NULL && fdt_override) { BS->FreePages((uint64_t)fdt_override, EFI_SIZE_TO_PAGES(fdt_override_size)); fdt_override = NULL; fdt_init(fdt_sys); return 0; } snprintf(path, sizeof(path), "%s:%s", cmd.bootdev, file); fd = open(path, O_RDONLY); if (fd < 0 || fstat(fd, &sb) == -1) { printf("cannot open %s\n", path); return 0; } dt_size = sb.st_size; retry: if (efi_memprobe_find(EFI_SIZE_TO_PAGES(dt_size), PAGE_SIZE, EfiLoaderData, &addr) != EFI_SUCCESS) { printf("cannot allocate memory for %s\n", path); return 0; } if (read(fd, (void *)addr, sb.st_size) != sb.st_size) { printf("cannot read from %s\n", path); return 0; } status = BS->LocateProtocol(&dt_fixup_guid, NULL, (void **)&dt_fixup); if (status == EFI_SUCCESS) { sz = dt_size; status = dt_fixup->Fixup(dt_fixup, (void *)addr, &sz, EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY); if (status == EFI_BUFFER_TOO_SMALL) { BS->FreePages(addr, EFI_SIZE_TO_PAGES(dt_size)); lseek(fd, 0, SEEK_SET); dt_size = sz; goto retry; } if (status != EFI_SUCCESS) panic("DT fixup failed: 0x%lx", status); } if (!fdt_init((void *)addr)) { printf("invalid device tree\n"); BS->FreePages(addr, EFI_SIZE_TO_PAGES(dt_size)); return 0; } if (fdt_override) { BS->FreePages((uint64_t)fdt_override, EFI_SIZE_TO_PAGES(fdt_override_size)); fdt_override = NULL; } fdt_override = (void *)addr; fdt_override_size = dt_size; return 0; } /* * Commands */ int Xacpi_efi(void); int Xdtb_efi(void); int Xexit_efi(void); int Xpoweroff_efi(void); const struct cmd_table cmd_machine[] = { { "acpi", CMDT_CMD, Xacpi_efi }, { "dtb", CMDT_CMD, Xdtb_efi }, { "exit", CMDT_CMD, Xexit_efi }, { "poweroff", CMDT_CMD, Xpoweroff_efi }, { NULL, 0 } }; int Xacpi_efi(void) { acpi = 1; return (0); } int Xdtb_efi(void) { if (cmd.argc == 1) { fdt_load_override(NULL); return (0); } if (cmd.argc != 2) { printf("dtb file\n"); return (0); } return fdt_load_override(cmd.argv[1]); } int Xexit_efi(void) { BS->Exit(IH, 0, 0, NULL); for (;;) continue; return (0); } int Xpoweroff_efi(void) { RS->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL); return (0); }