Skip to content

Commit

Permalink
trivial: Add a script to dump IFD contents from dual-SPI chips
Browse files Browse the repository at this point in the history
  • Loading branch information
hughsie committed Jan 11, 2024
1 parent 1b6f239 commit dbaaf15
Showing 1 changed file with 185 additions and 0 deletions.
185 changes: 185 additions & 0 deletions contrib/dump-mtd-ifd.py
@@ -0,0 +1,185 @@
#!/usr/bin/python3
#
# Copyright (C) 2024 Richard Hughes <richard@hughsie.com>
#
# SPDX-License-Identifier: LGPL-2.1+
#
# pylint: disable=invalid-name,missing-docstring,too-few-public-methods,too-many-locals

import sys
import os
import struct
import io
from typing import List, Optional
import argparse


class IfdPartition:
REGION_NAMES = [
"desc",
"bios",
"me",
"gbe",
"platform",
"devexp",
"bios2",
"ec",
"ie",
"10gbe",
]

def __init__(self, region: int = 0, offset: int = 0, size: int = 0) -> None:
self.region: int = region
self.offset: int = offset
self.size: int = size

def __str__(self) -> str:
try:
region_name: str = IfdPartition.REGION_NAMES[self.region]
except IndexError:
region_name = "unknown"
return (
"IfdPartition("
f"region=0x{self.region} ({region_name}), "
f"offset=0x{self.offset:x}, "
f"size=0x{self.size:x})"
)


def _read_partitions(f: io.BufferedReader) -> bytearray:
f.seek(0)
blob_ifd: bytes = f.read(0x1000)
(
signature,
descriptor_map0,
descriptor_map1,
descriptor_map2,
) = struct.unpack_from("<16xIIII", blob_ifd, offset=0)
if signature != 0x0FF0A55A:
sys.exit(f"Not IFD signature 0x0FF0A55A, got 0x{signature:X}")

# read out the descriptor maps
print(f"descriptor_map0=0x{descriptor_map0:X}")
print(f"descriptor_map1=0x{descriptor_map1:X}")
print(f"descriptor_map2=0x{descriptor_map2:X}")

# default to all partitions
num_regions = (descriptor_map0 >> 24) & 0b111
if num_regions == 0:
num_regions = 10
print(f"num_regions=0x{num_regions:X}")

# read out FREGs
flash_region_base_addr = (descriptor_map0 >> 12) & 0x00000FF0
print(f"flash_region_base_addr=0x{flash_region_base_addr:X}")
flash_descriptor_regs: List[int] = []
for region in range(num_regions):
flash_descriptor_regs.append(
struct.unpack_from(
"<I", blob_ifd, offset=flash_region_base_addr + (region * 4)
)[0]
)
for region in range(num_regions):
print(f"flash_descriptor_reg{region}=0x{flash_descriptor_regs[region]:X}")

# parse each partition
fregs: List[IfdPartition] = []
for i in range(num_regions):
freg_base: int = (flash_descriptor_regs[i] << 12) & 0x07FFF000
freg_limit: int = ((flash_descriptor_regs[i] >> 4) & 0x07FFF000) | 0x00000FFF

# invalid
if freg_base > freg_limit:
continue

fregs.append(
IfdPartition(region=i, offset=freg_base, size=freg_limit - freg_base)
)

# create a binary blob big enough
image_size: int = 0
for freg in fregs:
if freg.offset + freg.size > image_size:
image_size = freg.offset + freg.size
print(f"image_size=0x{image_size:x}")
blob: bytearray = bytearray(image_size)

# copy each partition
for freg in fregs:
print("reading...", freg)
try:
f.seek(freg.offset)
blob[freg.offset : freg.offset + freg.size] = f.read(freg.size)
except OSError as e:
print(f"failed to read: {e}")
return blob


def _read_device_to_file(devname: str, filename: Optional[str]) -> None:
# grab system info from sysfs
if not filename:
filename = ""
for sysfs_fn in [
"/sys/class/dmi/id/sys_vendor",
"/sys/class/dmi/id/product_family",
"/sys/class/dmi/id/product_name",
"/sys/class/dmi/id/product_sku",
]:
try:
with open(sysfs_fn, "rb") as f:
if filename:
filename += "-"
filename += (
f.read().decode().replace("\n", "").replace(" ", "_").lower()
)
except FileNotFoundError:
pass
if filename:
filename += ".bin"
else:
filename = "bios.bin"

# check this device name is what we expect
print(f"checking {devname}...")
try:
with open(f"/sys/class/mtd/{os.path.basename(devname)}/name", "rb") as f_name:
name = f_name.read().decode().replace("\n", "")
except FileNotFoundError as e:
sys.exit(str(e))
if name != "BIOS":
sys.exit(f"Not Intel Corporation PCH SPI Controller, got {name}")

# read the IFD header, then each partition
try:
with open(devname, "rb") as f_in:
print(f"reading from {devname}...")
blob = _read_partitions(f_in)
except PermissionError as e:
sys.exit(f"cannot read mtd device: {e}")
print(f"writing {filename}...")
with open(filename, "wb") as f_out:
f_out.write(blob)
print("done!")


if __name__ == "__main__":
# both have defaults
parser = argparse.ArgumentParser(
prog="dump-mtd-ifd", description="Dump local SPI contents using MTD"
)
parser.add_argument(
"--filename",
action="store",
type=str,
help="Output filename",
default=None,
)
parser.add_argument(
"--devname",
action="store",
type=str,
help="Device name, e.g. /dev/mtd0",
default="/dev/mtd0",
)
args = parser.parse_args()
_read_device_to_file(args.devname, args.filename)

0 comments on commit dbaaf15

Please sign in to comment.