Rewrite the ROM walk logic to correctly iterate over non-STI ROMs (and skip
authormiod <miod@openbsd.org>
Mon, 20 Feb 2023 11:31:16 +0000 (11:31 +0000)
committermiod <miod@openbsd.org>
Mon, 20 Feb 2023 11:31:16 +0000 (11:31 +0000)
them), such as x86 bios ROMs.

sys/dev/pci/sti_pci.c

index 9145240..4375f11 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: sti_pci.c,v 1.10 2023/02/20 09:08:47 miod Exp $       */
+/*     $OpenBSD: sti_pci.c,v 1.11 2023/02/20 11:31:16 miod Exp $       */
 
 /*
  * Copyright (c) 2006, 2007, 2023 Miodrag Vallat.
@@ -162,21 +162,61 @@ sti_local_printf(struct sti_softc *sc, const char *fmt, ...)
 
 #define printf(fmt, ...) sti_local_printf(sc, fmt, ## __VA_ARGS__)
 
+/*
+ * PCI ROM Data Structure (6.3.1.2)
+ */
+
+struct pcirom_ds {
+       uint32_t signature;
+       uint16_t vid;
+       uint16_t pid;
+       uint16_t reserved_8;
+       uint16_t dslen;
+       uint8_t rev;
+       uint8_t class[3];
+       uint16_t romlen;
+       uint16_t level;
+       uint8_t arch;
+       uint8_t indicator;
+       uint16_t reserved_16;
+};
+
+/*
+ * Callback data used while walking PCI ROM images to pick the most
+ * appropriate image.
+ */
+
+struct sti_pcirom_walk_ctx {
+#ifdef STIDEBUG
+       struct sti_softc *sc;
+#endif
+       bus_addr_t romoffs;
+};
+
+int    sti_pcirom_check(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
+           const struct pcirom_ds *, void *);
+int    sti_pcirom_walk(struct sti_softc *, bus_space_tag_t, bus_space_handle_t,
+           bus_size_t, int (*)(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
+           const struct pcirom_ds *, void *), void *);
+
 /*
  * Grovel the STI ROM image.
  */
+
 int
 sti_check_rom(struct sti_pci_softc *spc, struct pci_attach_args *pa)
 {
        struct sti_softc *sc = &spc->sc_base;
        pcireg_t address, mask;
        bus_space_handle_t romh;
-       bus_size_t romsize, subsize, stiromsize;
-       bus_addr_t selected, offs, suboffs;
-       u_int32_t tmp;
+       bus_size_t romsize, stiromsize;
+       bus_addr_t offs;
+       uint8_t region_bars[STI_REGION_MAX];
        int i;
        int rc;
 
+       struct sti_pcirom_walk_ctx ctx;
+
        /* sort of inline sti_pci_enable_rom(sc) */
        address = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_ROM_REG);
        pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_ROM_REG, ~PCI_ROM_ENABLE);
@@ -195,138 +235,50 @@ sti_check_rom(struct sti_pci_softc *spc, struct pci_attach_args *pa)
        if (rc != 0) {
                printf("%s: can't map PCI ROM (%d)\n",
                    sc->sc_dev.dv_xname, rc);
-               goto fail2;
+               goto disable_return;
        }
 
        /*
         * Iterate over the ROM images, pick the best candidate.
         */
 
-       selected = (bus_addr_t)-1;
-       for (offs = 0; offs < romsize; offs += subsize) {
-               /*
-                * Check for a valid ROM header.
-                */
-               tmp = bus_space_read_4(pa->pa_memt, romh, offs + 0);
-               tmp = letoh32(tmp);
-               if (tmp != 0x55aa0000) {
-                       if (offs == 0) {
-                               printf("%s: invalid PCI ROM header signature"
-                                   " (%08x)\n",
-                                   sc->sc_dev.dv_xname, tmp);
-                               rc = EINVAL;
-                       }
-                       break;
-               }
-
-               /*
-                * Check ROM type.
-                */
-               tmp = bus_space_read_4(pa->pa_memt, romh, offs + 4);
-               tmp = letoh32(tmp);
-               if (tmp != 0x00000001) {        /* 1 == STI ROM */
-                       if (offs == 0) {
-                               printf("%s: invalid PCI ROM type (%08x)\n",
-                                   sc->sc_dev.dv_xname, tmp);
-                               rc = EINVAL;
-                       }
-                       break;
-               }
-
-               subsize = (bus_addr_t)bus_space_read_2(pa->pa_memt, romh,
-                   offs + 0x0c);
-               subsize <<= 9;
-
-#ifdef STIDEBUG
-               printf("ROM offset %08lx size %08lx type %08x",
-                   offs, subsize, tmp);
-#endif
-
-               /*
-                * Check for a valid ROM data structure.
-                * We do not need it except to know what architecture the ROM
-                * code is for.
-                */
-
-               suboffs = offs +(bus_addr_t)bus_space_read_2(pa->pa_memt, romh,
-                   offs + 0x18);
-               tmp = bus_space_read_4(pa->pa_memt, romh, suboffs + 0);
-               tmp = letoh32(tmp);
-               if (tmp != 0x50434952) {        /* PCIR */
-                       if (offs == 0) {
-                               printf("%s: invalid PCI data signature"
-                                   " (%08x)\n",
-                                   sc->sc_dev.dv_xname, tmp);
-                               rc = EINVAL;
-                       } else {
 #ifdef STIDEBUG
-                               printf(" invalid PCI data signature %08x\n",
-                                   tmp);
+       ctx.sc = sc;
 #endif
-                               continue;
-                       }
-               }
+       ctx.romoffs = (bus_addr_t)-1;
+       rc = sti_pcirom_walk(sc, pa->pa_memt, romh, romsize, sti_pcirom_check,
+           &ctx);
 
-               tmp = bus_space_read_1(pa->pa_memt, romh, suboffs + 0x14);
-#ifdef STIDEBUG
-               printf(" code %02x", tmp);
-#endif
-
-               switch (tmp) {
-#ifdef __hppa__
-               case 0x10:
-                       if (selected == (bus_addr_t)-1)
-                               selected = offs;
-                       break;
-#endif
-#ifdef __i386__
-               case 0x00:
-                       if (selected == (bus_addr_t)-1)
-                               selected = offs;
-                       break;
-#endif
-               default:
-#ifdef STIDEBUG
-                       printf(" (wrong architecture)");
-#endif
-                       break;
-               }
-
-#ifdef STIDEBUG
-               if (selected == offs)
-                       printf(" -> SELECTED");
-               printf("\n");
-#endif
-       }
-
-       if (selected == (bus_addr_t)-1) {
+       if (ctx.romoffs == (bus_addr_t)-1) {
                if (rc == 0) {
                        printf("%s: found no ROM with correct microcode"
                            " architecture\n", sc->sc_dev.dv_xname);
                        rc = ENOEXEC;
                }
-               goto fail;
+               goto unmap_disable_return;
        }
 
        /*
         * Read the STI region BAR assignments.
         */
 
-       offs = selected +
-           (bus_addr_t)bus_space_read_2(pa->pa_memt, romh, selected + 0x0e);
+       offs = ctx.romoffs +
+           (bus_addr_t)bus_space_read_2(pa->pa_memt, romh, ctx.romoffs + 0x0e);
+       bus_space_read_region_1(pa->pa_memt, romh, offs, region_bars,
+           STI_REGION_MAX);
        for (i = 0; i < STI_REGION_MAX; i++) {
-               rc = sti_readbar(sc, pa, i,
-                   bus_space_read_1(pa->pa_memt, romh, offs + i));
+               rc = sti_readbar(sc, pa, i, region_bars[i]);
                if (rc != 0)
-                       goto fail;
+                       goto unmap_disable_return;
        }
 
        /*
-        * Find out where the STI ROM itself lies, and its size.
+        * Find out where the STI ROM itself lies within the PCI ROM,
+        * and its size.
         */
 
-       offs = selected +
-           (bus_addr_t)bus_space_read_4(pa->pa_memt, romh, selected + 0x08);
+       offs = ctx.romoffs +
+           (bus_addr_t)bus_space_read_4(pa->pa_memt, romh, ctx.romoffs + 0x08);
        stiromsize = (bus_addr_t)bus_space_read_4(pa->pa_memt, romh,
            offs + 0x18);
        stiromsize = letoh32(stiromsize);
@@ -342,15 +294,15 @@ sti_check_rom(struct sti_pci_softc *spc, struct pci_attach_args *pa)
        if (rc != 0) {
                printf("%s: can't map STI ROM (%d)\n",
                    sc->sc_dev.dv_xname, rc);
-               goto fail2;
+               goto disable_return;
        }
 
        sti_pci_disable_rom(sc);
        return 0;
 
-fail:
+unmap_disable_return:
        bus_space_unmap(pa->pa_memt, romh, romsize);
-fail2:
+disable_return:
        sti_pci_disable_rom(sc);
        return rc;
 }
@@ -389,7 +341,6 @@ sti_readbar(struct sti_softc *sc, struct pci_attach_args *pa, u_int region,
                rc = pci_mem_find(pa->pa_pc, pa->pa_tag, bar,
                    &addr, &size, NULL);
        }
-
        if (rc != 0) {
                printf("%s: invalid bar %02x for region %d\n",
                    sc->sc_dev.dv_xname, bar, region);
@@ -399,3 +350,227 @@ sti_readbar(struct sti_softc *sc, struct pci_attach_args *pa, u_int region,
        sc->bases[region] = addr;
        return 0;
 }
+
+/*
+ * Check a PCI ROM image for an STI ROM.
+ */
+
+int
+sti_pcirom_check(bus_space_tag_t romt, bus_space_handle_t romh, bus_addr_t offs,
+    const struct pcirom_ds *ds, void *v)
+{
+       struct sti_pcirom_walk_ctx *ctx = v;
+#ifdef STIDEBUG
+       struct sti_softc *sc = ctx->sc;
+#endif
+       uint32_t tmp32;
+
+       /*
+        * Check for a valid STI ROM header.
+        */
+
+       tmp32 = bus_space_read_4(romt, romh, offs + 0);
+       tmp32 = letoh32(tmp32);
+       if (tmp32 != 0x55aa0000) {
+               /* Not an STI ROM image, onto the next */
+#ifdef STIDEBUG
+               printf("Invalid HP ROM signature (%08x)\n", tmp32);
+#endif
+               return 0;
+       }
+
+       /*
+        * Check ROM type.
+        */
+
+       tmp32 = bus_space_read_4(romt, romh, offs + 4);
+       tmp32 = letoh32(tmp32);
+       if (tmp32 != 0x00000001) {      /* 1 == STI ROM */
+#ifdef STIDEBUG
+               printf("Unknown HP ROM type (%08x)\n", tmp32);
+#endif
+               return 0;
+       }
+
+#ifdef STIDEBUG
+       printf("ROM architecture code %02x", ds->arch);
+#endif
+       switch (ds->arch) {
+#ifdef __hppa__
+       /*
+        * The PCI specification assigns value 0x02 to PA-RISC, but
+        * according to the STI specification (and to hardware),
+        * the correct value to check for is 0x10.
+        */
+       case 0x10:
+               if (ctx->romoffs == (bus_addr_t)-1)
+                       ctx->romoffs = offs;
+               break;
+#endif
+#ifdef __i386__
+       case 0x00:
+               if (ctx->romoffs == (bus_addr_t)-1)
+                       ctx->romoffs = offs;
+               break;
+#endif
+       default:
+#ifdef STIDEBUG
+               printf(" (wrong architecture)");
+#endif
+               break;
+       }
+
+#ifdef STIDEBUG
+       if (ctx->romoffs == offs)
+               printf(" -> SELECTED");
+       printf("\n");
+#endif
+
+       return 0;
+}
+
+/*
+ * Iterate over all PCI ROM images.
+ * This code is absolutely not related to sti(4) and could be moved
+ * elsewhere in case other drivers have a need for it, but is kept
+ * here for now due to the printf() wrapper requirement (which is why
+ * there is an otherwise unnecessary softc argument).
+ */
+
+int
+sti_pcirom_walk(struct sti_softc *sc, bus_space_tag_t romt,
+    bus_space_handle_t romh, bus_size_t romsize, int (*cb)(bus_space_tag_t,
+    bus_space_handle_t, bus_addr_t, const struct pcirom_ds *, void *),
+    void *cbarg)
+{
+       bus_addr_t offs;
+       bus_size_t subsize;
+       int rc = 0;
+
+       for (offs = 0; offs < romsize; offs += subsize) {
+               struct pcirom_ds ds;
+               bus_addr_t dsoffs;
+               uint16_t tmp16;
+
+#ifdef STIDEBUG
+               printf("Checking for ROM image at offset %08lx/%08lx\n",
+                   offs, romsize);
+#endif
+
+               /*
+                * Check for a valid ROM header (6.3.1.1).
+                */
+
+               tmp16 = bus_space_read_2(romt, romh, offs + 0);
+               tmp16 = letoh16(tmp16);
+               if (tmp16 != 0x55aa) {
+                       if (offs == 0) {
+                               printf("%s: invalid PCI ROM header signature"
+                                   " (%04x)\n",
+                                   sc->sc_dev.dv_xname, tmp16);
+                               rc = EINVAL;
+                       }
+                       break;
+               }
+
+               /*
+                * Check for a valid ROM data structure (6.3.1.2).
+                */
+
+               dsoffs = (bus_addr_t)bus_space_read_2(romt, romh, offs + 0x18);
+#ifdef STIDEBUG
+               printf("PCI DS offset %04lx\n", dsoffs);
+#endif
+               if ((dsoffs & 0x03) != 0 || dsoffs < 0x1a ||
+                   offs + dsoffs + sizeof(struct pcirom_ds) > romsize) {
+                       if (offs == 0) {
+                               printf("%s: ill-formed PCI Data Structure"
+                                   " (offset %04lx)\n",
+                                   sc->sc_dev.dv_xname, dsoffs);
+                               rc = EINVAL;
+                       } else {
+#ifdef STIDEBUG
+                               printf("Ill-formed PCI Data Structure"
+                                   " (offset %04lx)\n", dsoffs);
+#endif
+                       }
+                       break;
+               }
+
+               bus_space_read_region_1(romt, romh, dsoffs,
+                   (uint8_t *)&ds, sizeof ds);
+               /* convert sizes to host endianness */
+               ds.dslen = letoh16(ds.dslen);
+               ds.romlen = letoh16(ds.romlen);
+#if 0 /* not used in this code */
+               ds.vid = letoh16(ds.vid);
+               ds.pid = letoh16(ds.pid);
+               ds.level = letoh16(ds.level);
+#endif
+               if (ds.signature != 0x50434952) {       /* PCIR */
+                       if (offs == 0) {
+                               printf("%s: invalid PCI data signature"
+                                   " (%08x)\n",
+                                   sc->sc_dev.dv_xname, ds.signature);
+                               rc = EINVAL;
+                       } else {
+#ifdef STIDEBUG
+                               printf(" invalid PCI data signature %08x\n",
+                                   ds.signature);
+#endif
+                       }
+                       break;
+               }
+
+#ifdef STIDEBUG
+               printf("PCI DS length %04lx\n", ds.dslen);
+#endif
+               if (ds.dslen < sizeof ds || dsoffs + ds.dslen > romsize) {
+                       if (offs == 0) {
+                               printf("%s: ill-formed PCI Data Structure"
+                                   " (size %04lx)\n",
+                                   sc->sc_dev.dv_xname, ds.dslen);
+                               rc = EINVAL;
+                       } else {
+#ifdef STIDEBUG
+                               printf("Ill-formed PCI Data Structure"
+                                   " (size %04lx)\n", ds.dslen);
+#endif
+                       }
+                       break;
+               }
+
+               subsize = ((bus_size_t)ds.romlen) << 9;
+#ifdef STIDEBUG
+               printf("ROM image size %08lx\n", subsize);
+#endif
+               if (subsize == 0 || offs + subsize > romsize) {
+                       if (offs == 0) {
+                               printf("%s: invalid ROM image size"
+                                   " (%04lx)\n",
+                                   sc->sc_dev.dv_xname, subsize);
+                               rc = EINVAL;
+                       } else {
+#ifdef STIDEBUG
+                               printf("Invalid ROM image size"
+                                   " (%04lx)\n", subsize);
+#endif
+                       }
+                       break;
+               }
+
+               rc = (*cb)(romt, romh, offs, &ds, cbarg);
+               if (rc != 0)
+                       break;
+
+               if ((ds.indicator & 0x80) != 0) {
+                       /* no more ROM images */
+#ifdef STIDEBUG
+                       printf("no more ROM images\n");
+#endif
+                       break;
+               }
+       }
+
+       return rc;
+}