diff options
author | Patrick Wildt <patrick@cvs.openbsd.org> | 2022-12-07 23:04:27 +0000 |
---|---|---|
committer | Patrick Wildt <patrick@cvs.openbsd.org> | 2022-12-07 23:04:27 +0000 |
commit | 6da2d446cb37dee042525832dfc849b0114eb138 (patch) | |
tree | 2523e75f60e65b7136dda47680882b62414c9ba9 | |
parent | 3d37e91d299e8e84f7cef3f64b106d63ff6d2d9c (diff) |
The ACPI tables on the Lenovo x13s are incomplete and unusable. For
that reason, identify that we're running on that machine by looking at
the SMBIOS tables and load alternate device tree binaries from disk.
ok kettenis@
-rw-r--r-- | sys/arch/arm64/stand/efiboot/Makefile | 4 | ||||
-rw-r--r-- | sys/arch/arm64/stand/efiboot/conf.c | 4 | ||||
-rw-r--r-- | sys/arch/arm64/stand/efiboot/efiboot.c | 128 | ||||
-rw-r--r-- | sys/arch/arm64/stand/efiboot/smbios.c | 373 | ||||
-rw-r--r-- | sys/stand/efi/include/efiapi.h | 4 |
5 files changed, 480 insertions, 33 deletions
diff --git a/sys/arch/arm64/stand/efiboot/Makefile b/sys/arch/arm64/stand/efiboot/Makefile index e7f6a3d30ec..6cbb987af65 100644 --- a/sys/arch/arm64/stand/efiboot/Makefile +++ b/sys/arch/arm64/stand/efiboot/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.16 2022/07/30 21:06:54 patrick Exp $ +# $OpenBSD: Makefile,v 1.17 2022/12/07 23:04:26 patrick Exp $ NOMAN= # @@ -9,7 +9,7 @@ OBJFMT= binary INSTALL_STRIP= BINDIR= /usr/mdec SRCS= start.S self_reloc.c efiboot.c conf.c exec.c efiacpi.c -SRCS+= efidev.c efipxe.c efirng.c fdt.c +SRCS+= efidev.c efipxe.c efirng.c fdt.c smbios.c SRCS+= softraid_arm64.c S= ${.CURDIR}/../../../.. diff --git a/sys/arch/arm64/stand/efiboot/conf.c b/sys/arch/arm64/stand/efiboot/conf.c index 6b348e7f6b2..ae2813eee16 100644 --- a/sys/arch/arm64/stand/efiboot/conf.c +++ b/sys/arch/arm64/stand/efiboot/conf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: conf.c,v 1.41 2022/11/05 19:01:51 patrick Exp $ */ +/* $OpenBSD: conf.c,v 1.42 2022/12/07 23:04:26 patrick Exp $ */ /* * Copyright (c) 1996 Michael Shalayeff @@ -46,7 +46,7 @@ #include "efipxe.h" #include "softraid_arm64.h" -const char version[] = "1.13"; +const char version[] = "1.14"; int debug = 0; struct fs_ops file_system[] = { diff --git a/sys/arch/arm64/stand/efiboot/efiboot.c b/sys/arch/arm64/stand/efiboot/efiboot.c index ecadf5f4aab..970b8fa0a35 100644 --- a/sys/arch/arm64/stand/efiboot/efiboot.c +++ b/sys/arch/arm64/stand/efiboot/efiboot.c @@ -1,4 +1,4 @@ -/* $OpenBSD: efiboot.c,v 1.44 2022/11/05 19:00:31 patrick Exp $ */ +/* $OpenBSD: efiboot.c,v 1.45 2022/12/07 23:04:26 patrick Exp $ */ /* * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> @@ -47,7 +47,10 @@ EFI_SYSTEM_TABLE *ST; EFI_BOOT_SERVICES *BS; EFI_RUNTIME_SERVICES *RS; EFI_HANDLE IH, efi_bootdp; -void *fdt = NULL; +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; @@ -62,6 +65,8 @@ 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; #define efi_guidcmp(_a, _b) memcmp((_a), (_b), sizeof(EFI_GUID)) @@ -73,6 +78,9 @@ 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) @@ -102,9 +110,15 @@ efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab) for (i = 0; i < ST->NumberOfTableEntries; i++) { if (efi_guidcmp(&fdt_guid, &ST->ConfigurationTable[i].VendorGuid) == 0) - fdt = ST->ConfigurationTable[i].VendorTable; + 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); + fdt_init(fdt_sys); progname = "BOOTAA64"; @@ -584,9 +598,10 @@ efi_makebootargs(char *bootargs, int howto) uint64_t uefi_system_table = htobe64((uintptr_t)ST); uint32_t boothowto = htobe32(howto); EFI_PHYSICAL_ADDRESS addr; - void *node; + void *node, *fdt; size_t len; + fdt = efi_fdt(); if (fdt == NULL || acpi) fdt = efi_acpi(); @@ -698,6 +713,7 @@ machdep(void) cninit(); efi_heap_init(); + smbios_init(smbios); /* * The kernel expects to be loaded into a block of memory aligned @@ -1072,6 +1088,79 @@ mdrandom(char *buf, size_t buflen) return ret; } +#define FW_PATH "/etc/firmware/dtb/" + +void * +efi_fdt(void) +{ + extern char *hw_vendor, *hw_prod; + + /* '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; + + if (strcmp(hw_vendor, "LENOVO") == 0 && + strncmp(hw_prod, "21BX", 4) == 0) + fdt_load_override(FW_PATH + "qcom/sc8280xp-lenovo-thinkpad-x13s.dtb"); + + return fdt_override ? fdt_override : fdt_sys; +} + +int +fdt_load_override(char *file) +{ + EFI_PHYSICAL_ADDRESS addr; + char path[MAXPATHLEN]; + struct stat sb; + 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; + } + if (efi_memprobe_find(EFI_SIZE_TO_PAGES(sb.st_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; + } + + if (!fdt_init((void *)addr)) { + printf("invalid device tree\n"); + BS->FreePages(addr, EFI_SIZE_TO_PAGES(sb.st_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 = sb.st_size; + return 0; +} + /* * Commands */ @@ -1099,36 +1188,17 @@ Xacpi_efi(void) int Xdtb_efi(void) { - EFI_PHYSICAL_ADDRESS addr; - char path[MAXPATHLEN]; - struct stat sb; - int fd; - - if (cmd.argc != 2) { - printf("dtb file\n"); + if (cmd.argc == 1) { + fdt_load_override(NULL); return (0); } - snprintf(path, sizeof(path), "%s:%s", cmd.bootdev, cmd.argv[1]); - - fd = open(path, O_RDONLY); - if (fd < 0 || fstat(fd, &sb) == -1) { - printf("cannot open %s\n", path); - return (0); - } - if (efi_memprobe_find(EFI_SIZE_TO_PAGES(sb.st_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); + if (cmd.argc != 2) { + printf("dtb file\n"); return (0); } - fdt = (void *)addr; - fdt_init(fdt); - return (0); + return fdt_load_override(cmd.argv[1]); } int diff --git a/sys/arch/arm64/stand/efiboot/smbios.c b/sys/arch/arm64/stand/efiboot/smbios.c new file mode 100644 index 00000000000..158e73a7121 --- /dev/null +++ b/sys/arch/arm64/stand/efiboot/smbios.c @@ -0,0 +1,373 @@ +/* $OpenBSD: smbios.c,v 1.1 2022/12/07 23:04:26 patrick Exp $ */ +/* + * Copyright (c) 2006 Gordon Willem Klok <gklok@cogeco.ca> + * Copyright (c) 2019 Mark Kettenis <kettenis@openbsd.org> + * + * 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 <sys/param.h> + +#include <machine/smbiosvar.h> + +#include <lib/libkern/libkern.h> +#include <stand/boot/cmd.h> + +#include "libsa.h" + +#undef DPRINTF +#if defined(SMBIOSDEBUG) +#define DPRINTF(x...) do { printf(x); } while(0) +#else +#define DPRINTF(x...) +#endif + +struct smbios_entry smbios_entry; + +const char *smbios_uninfo[] = { + "System", + "Not ", + "To be", + "SYS-" +}; + +char smbios_bios_date[64]; +char smbios_board_vendor[64]; +char smbios_board_prod[64]; +char smbios_board_serial[64]; + +void smbios_info(void); +char *fixstring(char *); + +char *hw_vendor, *hw_prod, *hw_ver, *hw_serial; + +void +smbios_init(void *smbios) +{ + struct smbios_struct_bios *sb; + struct smbtable bios; + char scratch[64]; + char *sminfop; + uint64_t addr; + + if (smbios == NULL) + return; + + if (strncmp(smbios, "_SM_", 4) == 0) { + struct smbhdr *hdr = smbios; + uint8_t *p, checksum = 0; + int i; + + if (hdr->len != sizeof(*hdr)) + return; + for (i = 0, p = (uint8_t *)hdr; i < hdr->len; i++) + checksum += p[i]; + if (checksum != 0) + return; + + DPRINTF("SMBIOS %d.%d", hdr->majrev, hdr->minrev); + + smbios_entry.len = hdr->size; + smbios_entry.mjr = hdr->majrev; + smbios_entry.min = hdr->minrev; + smbios_entry.count = hdr->count; + + addr = hdr->addr; + } else if (strncmp(smbios, "_SM3_", 5) == 0) { + struct smb3hdr *hdr = smbios; + uint8_t *p, checksum = 0; + int i; + + if (hdr->len != sizeof(*hdr) || hdr->epr != 0x01) + return; + for (i = 0, p = (uint8_t *)hdr; i < hdr->len; i++) + checksum += p[i]; + if (checksum != 0) + return; + + DPRINTF("SMBIOS %d.%d.%d", hdr->majrev, hdr->minrev, + hdr->docrev); + + smbios_entry.len = hdr->size; + smbios_entry.mjr = hdr->majrev; + smbios_entry.min = hdr->minrev; + smbios_entry.count = -1; + + addr = hdr->addr; + } else { + DPRINTF("Unsupported SMBIOS entry point\n"); + return; + } + + smbios_entry.addr = (uint8_t *)addr; + + bios.cookie = 0; + if (smbios_find_table(SMBIOS_TYPE_BIOS, &bios)) { + sb = bios.tblhdr; + DPRINTF("SMBIOS:"); + if ((smbios_get_string(&bios, sb->vendor, + scratch, sizeof(scratch))) != NULL) + DPRINTF(" vendor %s", + fixstring(scratch)); + if ((smbios_get_string(&bios, sb->version, + scratch, sizeof(scratch))) != NULL) + DPRINTF(" version \"%s\"", + fixstring(scratch)); + if ((smbios_get_string(&bios, sb->release, + scratch, sizeof(scratch))) != NULL) { + sminfop = fixstring(scratch); + if (sminfop != NULL) { + strlcpy(smbios_bios_date, + sminfop, + sizeof(smbios_bios_date)); + DPRINTF(" date %s", sminfop); + } + } + + smbios_info(); + DPRINTF("\n"); + } + + return; +} + +/* + * smbios_find_table() takes a caller supplied smbios struct type and + * a pointer to a handle (struct smbtable) returning one if the structure + * is successfully located and zero otherwise. Callers should take care + * to initialize the cookie field of the smbtable structure to zero before + * the first invocation of this function. + * Multiple tables of the same type can be located by repeatedly calling + * smbios_find_table with the same arguments. + */ +int +smbios_find_table(uint8_t type, struct smbtable *st) +{ + uint8_t *va, *end; + struct smbtblhdr *hdr; + int ret = 0, tcount = 1; + + va = smbios_entry.addr; + end = va + smbios_entry.len; + + /* + * The cookie field of the smtable structure is used to locate + * multiple instances of a table of an arbitrary type. Following the + * successful location of a table, the type is encoded as bits 0:7 of + * the cookie value, the offset in terms of the number of structures + * preceding that referenced by the handle is encoded in bits 15:31. + */ + if ((st->cookie & 0xfff) == type && st->cookie >> 16) { + if ((uint8_t *)st->hdr >= va && (uint8_t *)st->hdr < end) { + hdr = st->hdr; + if (hdr->type == type) { + va = (uint8_t *)hdr + hdr->size; + for (; va + 1 < end; va++) + if (*va == 0 && *(va + 1) == 0) + break; + va += 2; + tcount = st->cookie >> 16; + } + } + } + for (; va + sizeof(struct smbtblhdr) < end && + tcount <= smbios_entry.count; tcount++) { + hdr = (struct smbtblhdr *)va; + if (hdr->type == type) { + ret = 1; + st->hdr = hdr; + st->tblhdr = va + sizeof(struct smbtblhdr); + st->cookie = (tcount + 1) << 16 | type; + break; + } + if (hdr->type == SMBIOS_TYPE_EOT) + break; + va += hdr->size; + for (; va + 1 < end; va++) + if (*va == 0 && *(va + 1) == 0) + break; + va += 2; + } + return ret; +} + +char * +smbios_get_string(struct smbtable *st, uint8_t indx, char *dest, size_t len) +{ + uint8_t *va, *end; + char *ret = NULL; + int i; + + va = (uint8_t *)st->hdr + st->hdr->size; + end = smbios_entry.addr + smbios_entry.len; + for (i = 1; va < end && i < indx && *va; i++) + while (*va++) + ; + if (i == indx) { + if (va + len < end) { + ret = dest; + memcpy(ret, va, len); + ret[len - 1] = '\0'; + } + } + + return ret; +} + +char * +fixstring(char *s) +{ + char *p, *e; +#if 0 + int i; + + for (i = 0; i < nitems(smbios_uninfo); i++) + if ((strncasecmp(s, smbios_uninfo[i], + strlen(smbios_uninfo[i]))) == 0) + return NULL; +#endif + /* + * Remove leading and trailing whitespace + */ + for (p = s; *p == ' '; p++) + ; + /* + * Special case entire string is whitespace + */ + if (p == s + strlen(s)) + return NULL; + for (e = s + strlen(s) - 1; e > s && *e == ' '; e--) + ; + if (p > s || e < s + strlen(s) - 1) { + memmove(s, p, e - p + 1); + s[e - p + 1] = '\0'; + } + + return s; +} + +void +smbios_info(void) +{ + char *sminfop, sminfo[64]; + struct smbtable stbl, btbl; + struct smbios_sys *sys; + struct smbios_board *board; + int infolen, havebb; + char *p; + + if (smbios_entry.mjr < 2) + return; + /* + * According to the spec the system table among others is required, + * if it is not we do not bother with this smbios implementation. + */ + stbl.cookie = btbl.cookie = 0; + if (!smbios_find_table(SMBIOS_TYPE_SYSTEM, &stbl)) + return; + havebb = smbios_find_table(SMBIOS_TYPE_BASEBOARD, &btbl); + + sys = (struct smbios_sys *)stbl.tblhdr; + if (havebb) { + board = (struct smbios_board *)btbl.tblhdr; + + sminfop = NULL; + if ((p = smbios_get_string(&btbl, board->vendor, + sminfo, sizeof(sminfo))) != NULL) + sminfop = fixstring(p); + if (sminfop) + strlcpy(smbios_board_vendor, sminfop, + sizeof(smbios_board_vendor)); + + sminfop = NULL; + if ((p = smbios_get_string(&btbl, board->product, + sminfo, sizeof(sminfo))) != NULL) + sminfop = fixstring(p); + if (sminfop) + strlcpy(smbios_board_prod, sminfop, + sizeof(smbios_board_prod)); + + sminfop = NULL; + if ((p = smbios_get_string(&btbl, board->serial, + sminfo, sizeof(sminfo))) != NULL) + sminfop = fixstring(p); + if (sminfop) + strlcpy(smbios_board_serial, sminfop, + sizeof(smbios_board_serial)); + } + /* + * Some smbios implementations have no system vendor or + * product strings, some have very uninformative data which is + * harder to work around and we must rely upon various + * heuristics to detect this. In both cases we attempt to fall + * back on the base board information in the perhaps naive + * belief that motherboard vendors will supply this + * information. + */ + sminfop = NULL; + if ((p = smbios_get_string(&stbl, sys->vendor, sminfo, + sizeof(sminfo))) != NULL) + sminfop = fixstring(p); + if (sminfop == NULL) { + if (havebb) { + if ((p = smbios_get_string(&btbl, board->vendor, + sminfo, sizeof(sminfo))) != NULL) + sminfop = fixstring(p); + } + } + if (sminfop) { + infolen = strlen(sminfop) + 1; + hw_vendor = alloc(infolen); + if (hw_vendor) + strlcpy(hw_vendor, sminfop, infolen); + sminfop = NULL; + } + if ((p = smbios_get_string(&stbl, sys->product, sminfo, + sizeof(sminfo))) != NULL) + sminfop = fixstring(p); + if (sminfop == NULL) { + if (havebb) { + if ((p = smbios_get_string(&btbl, board->product, + sminfo, sizeof(sminfo))) != NULL) + sminfop = fixstring(p); + } + } + if (sminfop) { + infolen = strlen(sminfop) + 1; + hw_prod = alloc(infolen); + if (hw_prod) + strlcpy(hw_prod, sminfop, infolen); + sminfop = NULL; + } + if (hw_vendor != NULL && hw_prod != NULL) + DPRINTF("\nSMBIOS: %s %s", hw_vendor, hw_prod); + if ((p = smbios_get_string(&stbl, sys->version, sminfo, + sizeof(sminfo))) != NULL) + sminfop = fixstring(p); + if (sminfop) { + infolen = strlen(sminfop) + 1; + hw_ver = alloc(infolen); + if (hw_ver) + strlcpy(hw_ver, sminfop, infolen); + sminfop = NULL; + } + if ((p = smbios_get_string(&stbl, sys->serial, sminfo, + sizeof(sminfo))) != NULL) + sminfop = fixstring(p); + if (sminfop) { + infolen = strlen(sminfop) + 1; + hw_serial = alloc(infolen); + if (hw_serial) + strlcpy(hw_serial, sminfop, infolen); + } +} diff --git a/sys/stand/efi/include/efiapi.h b/sys/stand/efi/include/efiapi.h index 75578f27f24..f45f2ddac97 100644 --- a/sys/stand/efi/include/efiapi.h +++ b/sys/stand/efi/include/efiapi.h @@ -843,6 +843,10 @@ typedef struct { { 0xeb9d2d31, 0x2d88, 0x11d3, \ { 0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } } +#define SMBIOS3_TABLE_GUID \ + { 0xf2fd1544, 0x9794, 0x4a2c, \ + { 0x99, 0x2e, 0xe5, 0xbb, 0xcf, 0x20, 0xe3, 0x94 } } + #define SAL_SYSTEM_TABLE_GUID \ { 0xeb9d2d32, 0x2d88, 0x11d3, \ { 0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } } |