// SPDX-License-Identifier: GPL-2.0 /* * Real-GSP boot stage 3c support: VBIOS access and FWSEC image parsing. * * Port of the distribution driver's Turing ROM extraction path, reduced to * what the FWSEC/FRTS run needs: raw 32-bit reads through the PROM window, * the legacy PCI ROM header / expansion-ROM walk, the BIT header scan and * the falcon-ucode table lookup for the FWSEC application. No device writes. */ #include #include #include #include #include #include #include #include #include "gsp_phys_vbios.h" /* Legacy PCI ROM / PCI data structure constants. */ #define GSP_PHYS_ROM_SIG_STD 0xaa55 #define GSP_PHYS_ROM_SIG_NV 0x4e56 #define GSP_PHYS_ROM_SIG_NV2 0xbb77 #define GSP_PHYS_PCIR_SIG 0x52494350 #define GSP_PHYS_PCIR_SIG_NV 0x5344504e #define GSP_PHYS_PCIR_SIG_NV2 0x53494752 #define GSP_PHYS_PCIR_LAST_IMAGE BIT(7) #define GSP_PHYS_ROM_BLOCK_SIZE 512 #define GSP_PHYS_PCIR_STRUCT_LEN 0x0a #define GSP_PHYS_PCIR_IMAGE_LEN 0x10 #define GSP_PHYS_PCIR_CODE_TYPE 0x14 #define GSP_PHYS_PCIR_LAST_IMAGE_OFF 0x15 #define GSP_PHYS_PCIR_PTR 0x18 #define GSP_PHYS_NPDE_SIG 0x4544504e #define GSP_PHYS_NPDE_REV_10 0x0100 #define GSP_PHYS_NPDE_REV_11 0x0101 #define GSP_PHYS_NPDE_LEN 0x06 #define GSP_PHYS_NPDE_REV 0x04 #define GSP_PHYS_NPDE_SUBIMAGE_LEN 0x08 #define GSP_PHYS_NPDE_LAST_IMAGE 0x0a #define GSP_PHYS_NPDE_CODE_TYPE_VBIOS_BASE 0x00 #define GSP_PHYS_NPDE_CODE_TYPE_VBIOS_EXT 0xe0 /* IFR (image format record) fixed fields at the start of the PROM image. */ #define GSP_PHYS_IFR_FIXED1_VERSIONSW 0x0000ff00 #define GSP_PHYS_IFR_FIXED1_VERSIONSW_SHIFT 8 #define GSP_PHYS_IFR_FIXED1_FIXED_DATA_SIZE 0x7fff0000 #define GSP_PHYS_IFR_FIXED1_FIXED_DATA_SIZE_SHIFT 16 #define GSP_PHYS_IFR_FIXED2_TOTAL_DATA_SIZE 0x000fffff #define GSP_PHYS_IFR_SIGNATURE 0x4947564e #define GSP_PHYS_ROM_DIRECTORY_IDENTIFIER 0x44524652 #define GSP_PHYS_ROM_DIRECTORY_SKIP 4096 /* BIOS Information Table (BIT) header and tokens. */ #define GSP_PHYS_BIT_ID 0xb8ff #define GSP_PHYS_BIT_SIGNATURE 0x00544942 #define GSP_PHYS_BIT_HEADER_SIZE_OFF 8 #define GSP_PHYS_BIT_TOKEN_SIZE_OFF 9 #define GSP_PHYS_BIT_TOKEN_ENTRIES_OFF 10 #define GSP_PHYS_BIT_TOKEN_BIOSDATA 0x42 #define GSP_PHYS_BIT_TOKEN_FALCON_DATA 0x70 #define GSP_PHYS_BIT_BIOSDATA_SIZE_MIN 5 #define GSP_PHYS_BIT_FALCON_DATA_V2_SIZE_MIN 4 #define GSP_PHYS_FALCON_UCODE_TABLE_VERSION 1 #define GSP_PHYS_FALCON_UCODE_TABLE_HDR_SIZE 6 #define GSP_PHYS_FALCON_UCODE_ENTRY_SIZE 6 #define GSP_PHYS_FALCON_UCODE_DESC_V2_SIZE 60 #define GSP_PHYS_FALCON_UCODE_DESC_V3_SIZE 44 /* * Read one aligned 32-bit word from the PROM window. The window exposes only * whole 32-bit registers; smaller/unaligned reads are assembled from them by * gsp_phys_prom_read(), exactly like the distribution driver does. */ static bool gsp_phys_prom_read32(void __iomem *bar0, u32 offset, u32 *value) { if (offset > GSP_PHYS_VBIOS_MAX_SIZE - sizeof(u32)) return false; *value = readl(bar0 + GSP_PHYS_VBIOS_PROM_WINDOW + offset); return true; } /* * Read 1-4 bytes at an arbitrary PROM offset via 32-bit window reads. An * unaligned access is assembled from one or two aligned words; the returned * value is little endian (first byte at the lowest offset is the LSB). */ static bool gsp_phys_prom_read(void __iomem *bar0, u32 offset, u32 size, u32 *value) { union { u32 word[2]; u8 byte[8]; } buf; u32 byte_index; bool second; if (size == 0 || size > sizeof(u32)) return false; if (offset > GSP_PHYS_VBIOS_MAX_SIZE - size) return false; byte_index = offset & (sizeof(u32) - 1); offset -= byte_index; byte_index += size; second = byte_index > sizeof(u32); if (!gsp_phys_prom_read32(bar0, offset, &buf.word[0])) return false; if (second && !gsp_phys_prom_read32(bar0, offset + sizeof(u32), &buf.word[1])) return false; *value = 0; while (size--) *value = (*value << 8) + buf.byte[--byte_index]; return true; } static bool gsp_phys_prom_read8(void __iomem *bar0, u32 offset, u32 *value) { return gsp_phys_prom_read(bar0, offset, 1, value); } static bool gsp_phys_prom_read16(void __iomem *bar0, u32 offset, u32 *value) { return gsp_phys_prom_read(bar0, offset, 2, value); } /* Read 1-4 bytes out of the already-copied image, little endian. */ static bool gsp_phys_img_read(const struct gsp_phys_vbios *img, u32 offset, u32 size, u32 *value) { u32 v = 0, i; if (size == 0 || size > sizeof(u32)) return false; if (offset > img->size || size > img->size - offset) return false; for (i = 0; i < size; i++) v |= (u32)img->data[offset + i] << (8 * i); *value = v; return true; } /* * Locate the legacy PCI ROM image: either the image starts at PROM offset 0 * (standard/nv signature) or the IFR record points at the image start. IFR * versions 1/2 use the extended data-size field for the image offset; version * 3 stores the flash-status offset at the total-data-size offset and the ROM * directory 4096 bytes after that. */ static int gsp_phys_vbios_find_pci_header(void __iomem *bar0, u32 *pci_offset, u32 *ifr_version) { u32 fixed0, fixed1, fixed2; u32 image_offset = 0; u32 data_size, flash_status, directory_sig, version; *ifr_version = 0; if (!gsp_phys_prom_read32(bar0, 0, &fixed0) || !gsp_phys_prom_read32(bar0, 4, &fixed1) || !gsp_phys_prom_read32(bar0, 8, &fixed2)) return -EIO; if (fixed0 != GSP_PHYS_IFR_SIGNATURE) { /* No IFR record: the image starts at offset 0. */ *pci_offset = 0; return 0; } version = (fixed1 & GSP_PHYS_IFR_FIXED1_VERSIONSW) >> GSP_PHYS_IFR_FIXED1_VERSIONSW_SHIFT; switch (version) { case 1: case 2: data_size = (fixed1 & GSP_PHYS_IFR_FIXED1_FIXED_DATA_SIZE) >> GSP_PHYS_IFR_FIXED1_FIXED_DATA_SIZE_SHIFT; if (!gsp_phys_prom_read32(bar0, data_size + 4, &image_offset)) return -EIO; break; case 3: data_size = fixed2 & GSP_PHYS_IFR_FIXED2_TOTAL_DATA_SIZE; if (!gsp_phys_prom_read32(bar0, data_size, &flash_status)) return -EIO; if (flash_status > GSP_PHYS_VBIOS_MAX_SIZE - GSP_PHYS_ROM_DIRECTORY_SKIP - 8) return -EINVAL; if (!gsp_phys_prom_read32(bar0, flash_status + GSP_PHYS_ROM_DIRECTORY_SKIP, &directory_sig)) return -EIO; if (directory_sig != GSP_PHYS_ROM_DIRECTORY_IDENTIFIER) { pr_err("vgsp-vbios: ROM directory not found (0x%08x)\n", directory_sig); return -EINVAL; } if (!gsp_phys_prom_read32(bar0, flash_status + GSP_PHYS_ROM_DIRECTORY_SKIP + 8, &image_offset)) return -EIO; break; default: pr_err("vgsp-vbios: unsupported IFR version %u\n", version); return -EINVAL; } if (image_offset & 0x3) { pr_err("vgsp-vbios: IFR image offset 0x%x is not 4-byte aligned\n", image_offset); return -EINVAL; } if (image_offset > GSP_PHYS_VBIOS_MAX_SIZE - sizeof(u32)) { pr_err("vgsp-vbios: IFR image offset 0x%x out of range\n", image_offset); return -EINVAL; } *ifr_version = version; *pci_offset = image_offset; return 0; } static bool gsp_phys_pci_data_sig_valid(u32 sig) { return sig == GSP_PHYS_PCIR_SIG || sig == GSP_PHYS_PCIR_SIG_NV || sig == GSP_PHYS_PCIR_SIG_NV2; } /* * Walk the PCI expansion ROM chain: every block starts with a ROM header * whose 0x18 pointer gives the PCI data structure; the optional NVIDIA PCI * data extension can override the sub-image length and the last-image flag. * Returns the total image size relative to pci_offset and the expansion ROM * base (the region that carries the BIT and the falcon ucode table). */ static int gsp_phys_vbios_locate_expansion_roms(void __iomem *bar0, u32 pci_offset, u32 *bios_size, u32 *expansion_rom_offset) { u32 curr_block = pci_offset; u32 ext_rom_offset = 0, base_rom_size = 0; u32 block_offset = 0, block_size = 0; u32 iterations = GSP_PHYS_VBIOS_MAX_SIZE / GSP_PHYS_ROM_BLOCK_SIZE + 8; u32 iter; for (iter = 0; iter < iterations; iter++) { u32 pcir_ptr, base, sig, img_len, sub_img_len, code_type; u32 struct_len, ext_at, ext_sig, ext_rev, ext_len; bool last_image; if (!gsp_phys_prom_read16(bar0, curr_block + GSP_PHYS_PCIR_PTR, &pcir_ptr)) return -EIO; if ((u64)curr_block + pcir_ptr > GSP_PHYS_VBIOS_MAX_SIZE - 2) return -EINVAL; base = curr_block + pcir_ptr; if (!gsp_phys_prom_read32(bar0, base, &sig)) return -EIO; if (!gsp_phys_pci_data_sig_valid(sig)) { pr_err("vgsp-vbios: invalid PCI data signature 0x%08x at 0x%x\n", sig, base); return -EINVAL; } { u32 v; if (!gsp_phys_prom_read8(bar0, base + GSP_PHYS_PCIR_LAST_IMAGE_OFF, &v)) return -EIO; last_image = (v & GSP_PHYS_PCIR_LAST_IMAGE) != 0; } if (!gsp_phys_prom_read16(bar0, base + GSP_PHYS_PCIR_IMAGE_LEN, &img_len)) return -EIO; sub_img_len = img_len; if (!gsp_phys_prom_read16(bar0, base + GSP_PHYS_PCIR_STRUCT_LEN, &struct_len)) return -EIO; ext_at = (base + struct_len + 0xf) & ~0xfu; if (gsp_phys_prom_read32(bar0, ext_at, &ext_sig) && ext_sig == GSP_PHYS_NPDE_SIG && gsp_phys_prom_read16(bar0, ext_at + GSP_PHYS_NPDE_REV, &ext_rev) && (ext_rev == GSP_PHYS_NPDE_REV_10 || ext_rev == GSP_PHYS_NPDE_REV_11)) { if (!gsp_phys_prom_read16(bar0, ext_at + GSP_PHYS_NPDE_LEN, &ext_len)) return -EIO; if (!gsp_phys_prom_read16(bar0, ext_at + GSP_PHYS_NPDE_SUBIMAGE_LEN, &sub_img_len)) return -EIO; if (GSP_PHYS_NPDE_LAST_IMAGE + 1 <= ext_len) { u32 v; if (!gsp_phys_prom_read8(bar0, ext_at + GSP_PHYS_NPDE_LAST_IMAGE, &v)) return -EIO; last_image = (v & GSP_PHYS_PCIR_LAST_IMAGE) != 0; } else if (sub_img_len < img_len) { last_image = false; } } if (!gsp_phys_prom_read8(bar0, base + GSP_PHYS_PCIR_CODE_TYPE, &code_type)) return -EIO; block_offset = curr_block - pci_offset; block_size = sub_img_len * GSP_PHYS_ROM_BLOCK_SIZE; if (ext_rom_offset == 0 && code_type == GSP_PHYS_NPDE_CODE_TYPE_VBIOS_EXT) ext_rom_offset = block_offset; else if (base_rom_size == 0 && code_type == GSP_PHYS_NPDE_CODE_TYPE_VBIOS_BASE) base_rom_size = block_size; if (last_image) break; if (sub_img_len == 0) { pr_err("vgsp-vbios: zero-sized ROM block at 0x%x\n", curr_block); return -EINVAL; } curr_block += sub_img_len * GSP_PHYS_ROM_BLOCK_SIZE; if (curr_block > GSP_PHYS_VBIOS_MAX_SIZE) return -EINVAL; } if (iter == iterations) return -EINVAL; if (bios_size) *bios_size = block_offset + block_size; if (expansion_rom_offset) { if (ext_rom_offset > 0 && base_rom_size > 0) *expansion_rom_offset = ext_rom_offset - base_rom_size; else *expansion_rom_offset = 0; } return 0; } static int gsp_phys_vbios_copy_image(struct gsp_phys_vbios *img, void __iomem *bar0, u32 pci_offset, u32 bios_size) { u32 aligned, i; u8 *buf; if (bios_size == 0 || bios_size > GSP_PHYS_VBIOS_MAX_SIZE) return -EINVAL; buf = kvzalloc(bios_size, GFP_KERNEL); if (!buf) return -ENOMEM; aligned = bios_size & ~0x3u; for (i = 0; i < aligned; i += 4) { u32 word; if (!gsp_phys_prom_read32(bar0, pci_offset + i, &word)) { kvfree(buf); return -EIO; } put_unaligned_le32(word, buf + i); } for (; i < bios_size; i++) { u32 byte; if (!gsp_phys_prom_read8(bar0, pci_offset + i, &byte)) { kvfree(buf); return -EIO; } buf[i] = (u8)byte; } img->data = buf; img->size = bios_size; img->pci_offset = pci_offset; return 0; } int gsp_phys_vbios_read(struct gsp_phys_vbios *img, struct pci_dev *pdev, void __iomem *bar0) { u32 pci_offset = 0, bios_size = 0, expansion_rom_offset = 0; u32 rom_sig, rom_end, ifr_version = 0; int ret; if (!img || !bar0) return -EINVAL; memset(img, 0, sizeof(*img)); if (!gsp_phys_prom_read16(bar0, 0, &rom_sig)) return -EIO; if (rom_sig != GSP_PHYS_ROM_SIG_STD && rom_sig != GSP_PHYS_ROM_SIG_NV && rom_sig != GSP_PHYS_ROM_SIG_NV2) { ret = gsp_phys_vbios_find_pci_header(bar0, &pci_offset, &ifr_version); if (ret) return ret; /* Re-read the ROM signature at the IFR image start. */ if (!gsp_phys_prom_read16(bar0, pci_offset, &rom_sig)) return -EIO; } if (rom_sig != GSP_PHYS_ROM_SIG_STD && rom_sig != GSP_PHYS_ROM_SIG_NV && rom_sig != GSP_PHYS_ROM_SIG_NV2) { pr_err("vgsp-vbios: no valid ROM signature at offset 0x%x (0x%04x)\n", pci_offset, rom_sig); return -EINVAL; } ret = gsp_phys_vbios_locate_expansion_roms(bar0, pci_offset, &bios_size, &expansion_rom_offset); if (ret) { pr_err("vgsp-vbios: expansion ROM walk failed: %d\n", ret); return ret; } if (check_add_overflow(pci_offset, bios_size, &rom_end) || rom_end > GSP_PHYS_VBIOS_MAX_SIZE) { pr_err("vgsp-vbios: expansion ROM span 0x%x+0x%x exceeds the window\n", pci_offset, bios_size); return -EINVAL; } ret = gsp_phys_vbios_copy_image(img, bar0, pci_offset, bios_size); if (ret) return ret; img->ifr_version = ifr_version; img->expansion_rom_offset = expansion_rom_offset; pr_info("vgsp-vbios: ROM on %s: pciOffset=0x%x size=0x%x expansionRomOffset=0x%x IFRversion=%u\n", pci_name(pdev), pci_offset, bios_size, expansion_rom_offset, ifr_version); return 0; } void gsp_phys_vbios_free(struct gsp_phys_vbios *img) { if (!img) return; kvfree(img->data); memset(img, 0, sizeof(*img)); } /* Find the BIT header: id 0xB8FF + "BIT\0" signature, header checksum 0. */ static int gsp_phys_vbios_find_bit(const struct gsp_phys_vbios *img, u32 *bit_addr) { u32 addr; for (addr = 0; addr + 4 <= img->size; addr++) { u32 id, sig, header_size, checksum = 0, j; if (!gsp_phys_img_read(img, addr, 2, &id) || id != GSP_PHYS_BIT_ID) continue; if (!gsp_phys_img_read(img, addr + 2, 4, &sig) || sig != GSP_PHYS_BIT_SIGNATURE) continue; if (!gsp_phys_img_read(img, addr + GSP_PHYS_BIT_HEADER_SIZE_OFF, 1, &header_size)) continue; if (header_size == 0 || header_size > img->size - addr) continue; for (j = 0; j < header_size; j++) { u32 b; if (!gsp_phys_img_read(img, addr + j, 1, &b)) break; checksum += b; } if (j != header_size) continue; if ((checksum & 0xff) == 0) { *bit_addr = addr; return 0; } } return -ENOENT; } static bool gsp_phys_fwsec_appid_wanted(u32 app_id, bool use_debug_app) { if (app_id == GSP_PHYS_FWSEC_APPID_FIRMWARE_SEC_LIC) return true; if (use_debug_app) return app_id == GSP_PHYS_FWSEC_APPID_DEBUG; return app_id == GSP_PHYS_FWSEC_APPID_PROD; } static int gsp_phys_vbios_parse_desc(const struct gsp_phys_vbios *img, u32 desc_offset, u32 app_id, u32 target_id, struct gsp_phys_fwsec_desc *desc) { u32 vdesc, version, size, fields[14]; u32 i, image_start, image_end; if (!gsp_phys_img_read(img, desc_offset, 4, &vdesc)) return -EIO; /* The flags bit0 must announce a version; 0 means unavailable. */ if ((vdesc & 0x1) == 0) return 0; version = (vdesc >> 8) & 0xff; size = (vdesc >> 16) & 0xffff; if (version == 3 && size >= GSP_PHYS_FALCON_UCODE_DESC_V3_SIZE) { pr_err("vgsp-vbios: FWSEC appId=0x%x is a V3 (boot-from-HS) descriptor; this GPU generation boots FWSEC with the generic SEC2 boot-loader (V2) only - cannot proceed\n", app_id); return -EOPNOTSUPP; } if (version != 2 || size < GSP_PHYS_FALCON_UCODE_DESC_V2_SIZE) return 1; /* not a usable descriptor; keep looking */ if (desc_offset > img->size || size > img->size - desc_offset) return -EINVAL; for (i = 0; i < ARRAY_SIZE(fields); i++) { if (!gsp_phys_img_read(img, desc_offset + 4 + i * 4, 4, &fields[i])) return -EIO; } /* V2 fields: Stored, Uncompressed, VirtualEntry, Interface, IMEMPhys, * IMEMLoad, IMEMVirt, IMEMSecBase, IMEMSecSize, DMEMOffset, DMEMPhys, * DMEMLoad, altIMEM, altDMEM. */ memset(desc, 0, sizeof(*desc)); desc->app_id = app_id; desc->target_id = target_id; desc->desc_version = version; desc->desc_size = size; desc->desc_offset = desc_offset; desc->virtual_entry = fields[2]; desc->interface_offset = fields[3]; desc->imem_phys_base = fields[4]; desc->imem_load_size = fields[5]; desc->imem_virt_base = fields[6]; desc->imem_sec_base = fields[7]; desc->imem_sec_size = fields[8]; desc->dmem_offset = fields[9]; desc->dmem_phys_base = fields[10]; desc->dmem_load_size = fields[11]; if (desc->imem_load_size == 0 || desc->dmem_load_size == 0) { pr_err("vgsp-vbios: FWSEC descriptor has a zero-sized IMEM/DMEM image (imem=0x%x dmem=0x%x)\n", desc->imem_load_size, desc->dmem_load_size); return -EINVAL; } if (desc->imem_sec_size > desc->imem_load_size) { pr_err("vgsp-vbios: FWSEC secure IMEM size 0x%x exceeds load size 0x%x\n", desc->imem_sec_size, desc->imem_load_size); return -EINVAL; } /* Code is stored immediately after the descriptor, data after code. */ image_start = desc_offset + size; if (image_start > img->size || desc->imem_load_size > img->size - image_start) { pr_err("vgsp-vbios: FWSEC IMEM image runs past the ROM copy\n"); return -EINVAL; } if (desc->dmem_offset > img->size - image_start) { pr_err("vgsp-vbios: FWSEC DMEM offset runs past the ROM copy\n"); return -EINVAL; } image_end = image_start + desc->dmem_offset; if (desc->dmem_load_size > img->size - image_end) { pr_err("vgsp-vbios: FWSEC DMEM image runs past the ROM copy\n"); return -EINVAL; } if (desc->interface_offset >= desc->dmem_load_size) { pr_err("vgsp-vbios: FWSEC interface offset 0x%x outside DMEM image (0x%x)\n", desc->interface_offset, desc->dmem_load_size); return -EINVAL; } pr_info("vgsp-vbios: FWSEC entry appId=0x%x targetId=%u desc=V%u size=%u at 0x%x\n", app_id, target_id, version, size, desc_offset); pr_info("vgsp-vbios: FWSEC image: code at 0x%x (0x%x bytes) data at 0x%x (0x%x bytes)\n", image_start, desc->imem_load_size, image_start + desc->dmem_offset, desc->dmem_load_size); pr_info("vgsp-vbios: FWSEC IMEM base/load/virt/secBase/secSize=0x%x/0x%x/0x%x/0x%x/0x%x DMEM off/phys/load=0x%x/0x%x/0x%x interfaceOff=0x%x entry=0x%x\n", desc->imem_phys_base, desc->imem_load_size, desc->imem_virt_base, desc->imem_sec_base, desc->imem_sec_size, desc->dmem_offset, desc->dmem_phys_base, desc->dmem_load_size, desc->interface_offset, desc->virtual_entry); return 0; } /* * Parse the copied VBIOS for the FWSEC falcon-ucode descriptor: find the BIT * header, walk its tokens to the falcon-ucode table, then walk the table * entries for the requested FWSEC application. Also reports the combined * VBIOS version from the BIOSDATA token when present. */ int gsp_phys_vbios_parse_fwsec(struct gsp_phys_vbios *img, bool use_debug_app, struct gsp_phys_fwsec_desc *desc) { u32 bit_addr, header_size, token_size, token_entries; u64 version = 0; int ret; if (!img || !img->data || !desc) return -EINVAL; ret = gsp_phys_vbios_find_bit(img, &bit_addr); if (ret) { pr_err("vgsp-vbios: BIT header not found in %zu-byte ROM image\n", img->size); return ret; } if (!gsp_phys_img_read(img, bit_addr + GSP_PHYS_BIT_HEADER_SIZE_OFF, 1, &header_size) || !gsp_phys_img_read(img, bit_addr + GSP_PHYS_BIT_TOKEN_SIZE_OFF, 1, &token_size) || !gsp_phys_img_read(img, bit_addr + GSP_PHYS_BIT_TOKEN_ENTRIES_OFF, 1, &token_entries)) { pr_err("vgsp-vbios: truncated BIT header at 0x%x\n", bit_addr); return -EINVAL; } img->bit_addr = bit_addr; img->bit_token_size = token_size; img->bit_token_entries = token_entries; pr_info("vgsp-vbios: BIT header at 0x%x headerSize=%u tokenSize=%u tokens=%u\n", bit_addr, header_size, token_size, token_entries); pr_info("vgsp-vbios: BIT token type: %u-byte tokens (%s)\n", token_size, token_size >= GSP_PHYS_BIT_TOKEN_SIZE_8 ? "DataPtr u32 at offset 4" : "DataPtr u16 at offset 4"); if (token_size < GSP_PHYS_BIT_TOKEN_SIZE_6) { pr_err("vgsp-vbios: invalid BIT token size %u\n", token_size); return -EINVAL; } { u32 i; for (i = 0; i < token_entries; i++) { u32 token_offset = bit_addr + header_size + i * token_size; u32 token_id, data_version, data_size, data_ptr; u32 table_ptr; if (!gsp_phys_img_read(img, token_offset, 1, &token_id) || !gsp_phys_img_read(img, token_offset + 1, 1, &data_version) || !gsp_phys_img_read(img, token_offset + 2, 2, &data_size)) continue; if (token_size >= GSP_PHYS_BIT_TOKEN_SIZE_8) { if (!gsp_phys_img_read(img, token_offset + 4, 4, &data_ptr)) continue; } else { /* * 6-byte tokens ("2b2w": TokenId, DataVersion, * DataSize u16, DataPtr u16) carry a 16-bit * DataPtr at offset 4 which zero-extends into * the u32. A token whose DataPtr does not fit * the image is skipped, matching a failed * whole-token read in the reference. */ if (!gsp_phys_img_read(img, token_offset + 4, 2, &data_ptr)) continue; } if (token_id == GSP_PHYS_BIT_TOKEN_BIOSDATA && (data_version == 1 || data_version == 2) && data_size > GSP_PHYS_BIT_BIOSDATA_SIZE_MIN) { u32 binver, oem; if (gsp_phys_img_read(img, data_ptr, 4, &binver) && gsp_phys_img_read(img, data_ptr + 4, 1, &oem)) { version = ((u64)binver << 8) | oem; img->version = version; } } if (token_id != GSP_PHYS_BIT_TOKEN_FALCON_DATA || data_version != 2 || data_size < GSP_PHYS_BIT_FALCON_DATA_V2_SIZE_MIN) continue; if (!gsp_phys_img_read(img, data_ptr, 4, &table_ptr)) continue; { u32 table_offset, table_version, table_hdr_size; u32 entry_size, entry_count, j; if (check_add_overflow(img->expansion_rom_offset, table_ptr, &table_offset)) continue; if (!gsp_phys_img_read(img, table_offset, 1, &table_version) || !gsp_phys_img_read(img, table_offset + 1, 1, &table_hdr_size) || !gsp_phys_img_read(img, table_offset + 2, 1, &entry_size) || !gsp_phys_img_read(img, table_offset + 3, 1, &entry_count)) continue; if (table_version != GSP_PHYS_FALCON_UCODE_TABLE_VERSION || table_hdr_size < GSP_PHYS_FALCON_UCODE_TABLE_HDR_SIZE || entry_size < GSP_PHYS_FALCON_UCODE_ENTRY_SIZE) continue; for (j = 0; j < entry_count; j++) { u32 entry_offset = table_offset + table_hdr_size + j * entry_size; u32 app_id, target_id, desc_ptr; if (!gsp_phys_img_read(img, entry_offset, 1, &app_id) || !gsp_phys_img_read(img, entry_offset + 1, 1, &target_id) || !gsp_phys_img_read(img, entry_offset + 2, 4, &desc_ptr)) continue; if (!gsp_phys_fwsec_appid_wanted( app_id, use_debug_app)) continue; pr_info("vgsp-vbios: BIT falcon entry %u: appId=0x%x targetId=%u descPtr=0x%x\n", j, app_id, target_id, desc_ptr); ret = gsp_phys_vbios_parse_desc( img, img->expansion_rom_offset + desc_ptr, app_id, target_id, desc); if (ret < 0) return ret; if (ret == 0) { pr_info("vgsp-vbios: VBIOS version (binver<<8|oem) = 0x%llx\n", version); return 0; } } } } } pr_err("vgsp-vbios: no usable FWSEC V2 descriptor in the BIT (debug app %s)\n", use_debug_app ? "wanted" : "not wanted"); return -ENOENT; }