From: Stefan Sperling Subject: iwx: support pnvm data in firmware image To: tech@openbsd.org Date: Tue, 3 Mar 2026 15:25:49 +0100 Newer firmware images for BZ devices have a segment which contains data that was previously distributed in separate .pnvm files. If the firmware image contains pnvm data we must use it instead of the external file. The external file might be incompatible. ok? diff /usr/src path + /usr/src commit - c58e64aeb4e092373382ec4900136445f091367c blob - da9e3d2d42cdbb343313ea89ddfa6a89e1d39a31 file + sys/dev/pci/if_iwx.c --- sys/dev/pci/if_iwx.c +++ sys/dev/pci/if_iwx.c @@ -1183,6 +1183,9 @@ iwx_fw_info_free(struct iwx_fw_info *fw) free(fw->iml, M_DEVBUF, fw->iml_len); fw->iml = NULL; fw->iml_len = 0; + free(fw->pnvm, M_DEVBUF, fw->pnvm_len); + fw->pnvm = NULL; + fw->pnvm_len = 0; } #define IWX_FW_ADDR_CACHE_CONTROL 0xC0000000 @@ -1550,6 +1553,19 @@ iwx_read_firmware(struct iwx_softc *sc) case IWX_UCODE_TLV_FW_RECOVERY_INFO: break; + case IWX_UCODE_TLV_PNVM_DATA: + if (fw->pnvm != NULL) + break; + fw->pnvm = malloc(tlv_len, M_DEVBUF, + M_WAITOK | M_CANFAIL); + if (fw->pnvm == NULL) { + err = ENOMEM; + goto parse_out; + } + memcpy(fw->pnvm, tlv_data, tlv_len); + fw->pnvm_len = tlv_len; + break; + case IWX_UCODE_TLV_FW_FSEQ_VERSION: case IWX_UCODE_TLV_PHY_INTEGRATION_VERSION: case IWX_UCODE_TLV_FW_NUM_STATIONS: @@ -4294,6 +4310,7 @@ iwx_load_pnvm(struct iwx_softc *sc) int s, err = 0; u_char *pnvm_data = NULL; size_t pnvm_size = 0; + struct iwx_fw_info *fw = &sc->sc_fw; if (sc->sc_sku_id[0] == 0 && sc->sc_sku_id[1] == 0 && @@ -4302,18 +4319,28 @@ iwx_load_pnvm(struct iwx_softc *sc) if (sc->sc_pnvm_name) { if (sc->pnvm_dma.vaddr == NULL) { - err = loadfirmware(sc->sc_pnvm_name, - &pnvm_data, &pnvm_size); - if (err) { - printf("%s: could not read %s (error %d)\n", - DEVNAME(sc), sc->sc_pnvm_name, err); - return err; - } + /* Prefer PNVM data embedded in firmware image. */ + if (fw->pnvm) { + err = iwx_pnvm_parse(sc, fw->pnvm, + fw->pnvm_len); + if (err && err != ENOENT) + return err; + } else { + err = loadfirmware(sc->sc_pnvm_name, + &pnvm_data, &pnvm_size); + if (err) { + printf("%s: could not read %s " + "(error %d)\n", + DEVNAME(sc), sc->sc_pnvm_name, + err); + return err; + } - err = iwx_pnvm_parse(sc, pnvm_data, pnvm_size); - if (err && err != ENOENT) { - free(pnvm_data, M_DEVBUF, pnvm_size); - return err; + err = iwx_pnvm_parse(sc, pnvm_data, pnvm_size); + if (err && err != ENOENT) { + free(pnvm_data, M_DEVBUF, pnvm_size); + return err; + } } } else iwx_ctxt_info_gen3_set_pnvm(sc); commit - c58e64aeb4e092373382ec4900136445f091367c blob - 1af2759f445d0b05c35efd4ec8ff9ddab3ca419c file + sys/dev/pci/if_iwxreg.h --- sys/dev/pci/if_iwxreg.h +++ sys/dev/pci/if_iwxreg.h @@ -1584,6 +1584,7 @@ struct iwx_ucode_header { #define IWX_UCODE_TLV_SEC_TABLE_ADDR 66 #define IWX_UCODE_TLV_D3_KEK_KCK_ADDR 67 #define IWX_UCODE_TLV_CURRENT_PC 68 +#define IWX_UCODE_TLV_PNVM_DATA 74 #define IWX_UCODE_TLV_CONST_BASE 0x100 #define IWX_UCODE_TLV_FW_NUM_STATIONS (IWX_UCODE_TLV_CONST_BASE + 0) commit - c58e64aeb4e092373382ec4900136445f091367c blob - 7be82818bc54423e510492e7b7e8974a0f63000d file + sys/dev/pci/if_iwxvar.h --- sys/dev/pci/if_iwxvar.h +++ sys/dev/pci/if_iwxvar.h @@ -173,6 +173,13 @@ struct iwx_fw_info { /* Copy of firmware image loader found in file. */ uint8_t *iml; size_t iml_len; + + /* + * Copy of PNVM image found in file. + * Used in preference to external .pnvm file if present. + */ + uint8_t *pnvm; + size_t pnvm_len; }; struct iwx_nvm_data {