diff options
author | Miod Vallat <miod@cvs.openbsd.org> | 2023-02-20 11:31:17 +0000 |
---|---|---|
committer | Miod Vallat <miod@cvs.openbsd.org> | 2023-02-20 11:31:17 +0000 |
commit | 72989257935e7bb4f8dced0eadcfa95bbf6ad80c (patch) | |
tree | 24e2369bfced0a8d9e8a7aeafa7078f30571aabf /sys | |
parent | 3f4d59aa34ad7dbfe791add5510cf3c32e02fc96 (diff) |
Rewrite the ROM walk logic to correctly iterate over non-STI ROMs (and skip
them), such as x86 bios ROMs.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/pci/sti_pci.c | 401 |
1 files changed, 288 insertions, 113 deletions
diff --git a/sys/dev/pci/sti_pci.c b/sys/dev/pci/sti_pci.c index 9145240177e..4375f11d2e6 100644 --- a/sys/dev/pci/sti_pci.c +++ b/sys/dev/pci/sti_pci.c @@ -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. @@ -163,20 +163,60 @@ 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; +} |