The ACPI tables on the Lenovo x13s are incomplete and unusable. For
authorpatrick <patrick@openbsd.org>
Wed, 7 Dec 2022 23:04:26 +0000 (23:04 +0000)
committerpatrick <patrick@openbsd.org>
Wed, 7 Dec 2022 23:04:26 +0000 (23:04 +0000)
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@

sys/arch/arm64/stand/efiboot/Makefile
sys/arch/arm64/stand/efiboot/conf.c
sys/arch/arm64/stand/efiboot/efiboot.c
sys/arch/arm64/stand/efiboot/smbios.c [new file with mode: 0644]
sys/stand/efi/include/efiapi.h

index e7f6a3d..6cbb987 100644 (file)
@@ -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}/../../../..
index 6b348e7..ae2813e 100644 (file)
@@ -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[] = {
index ecadf5f..970b8fa 100644 (file)
@@ -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 (file)
index 0000000..158e73a
--- /dev/null
@@ -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);
+       }
+}
index 75578f2..f45f2dd 100644 (file)
@@ -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 } }