Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

k# Coreboot Guides

Practical, hands-on guides for flashing upstream coreboot on Lenovo hardware.

Every guide on this site has been written from real-world experience — hardware on the bench, clips on chips, serial consoles scrolling.

Guides

MachinePlatformPayloadKey Feature
W541HaswellSeaBIOS + iPXEWorking Nvidia K1100M dGPU
T580Kaby Lake REDK II + iPXEdeguard + Thunderbolt
M920qCoffee LakeEDK IIdeguard, dual-chip 24 MiB flash

Tools

Common procedures shared across all guides — programmer setup, wiring, build dependencies, troubleshooting.

ThinkPad W541 — Definitive Upstream Coreboot Guide with Working dGPU (K1100M / K2100M)

Firmware: Upstream coreboot (NOT Libreboot/lbmk — under Libreboot the dGPU does not appear on the PCI bus at all)
Board definition: lenovo/t540p (covers W540/W541)
GPU approach: Hybrid — libgfxinit initialises the Intel HD 4600 iGPU + extracted Nvidia VGA ROM initialises the K1100M or K2100M
Tested: K1100M ✓ confirmed working | K2100M — should work in principle (same extraction method, different PCI ID), not personally tested
Host OS: Debian / Ubuntu
External programmer: Raspberry Pi 3/4 (linux_spi) or Raspberry Pi Pico (RP2040) running Libreboot serprog firmware
Last validated: February 2026


Why Upstream Coreboot, Not Libreboot

This guide took about two days of work in total. The Libreboot path was taken first — it’s well-documented, the tooling is polished, and the W541 is officially supported. The problem emerged when the dGPU was needed: under Libreboot the K1100M/K2100M does not appear on the PCI bus at all — fully absent from lspci, not merely disabled or power-gated. Libreboot’s w541_12mb target uses NRI (libre raminit, no MRC blob) and does not load a VGA option ROM for the Quadro by design.

Switching to upstream coreboot turned out to be the more straightforward path once the blob extraction workflow was established. The .config structure is well-understood, the Nvidia VGA ROM mechanism works cleanly, and the result is a machine with both GPUs functional.


Part 1 — Hardware and Programmer Setup

For SPI programmer setup (Raspberry Pi or Pico), flashprog installation, GPIO wiring tables, and parts lists see Tools — SPI Programmers.

W541-Specific Additional Hardware

ItemNotes
SDK08 test clip (or IC test hook)For holding /CS on the idle chip high — individual spring-loaded probes let you solder an inline resistor to a single pin. An SOIC-8 clip also works but engages all 8 pins unnecessarily
Resistor for /CS idle line47Ω is the textbook value but anything from ~47Ω to 1kΩ works — 1kΩ confirmed working. Solder inline to the SDK08 probe wire

W541-Specific Wiring Notes

The standard GPIO wiring from Tools applies. On the W541 you additionally need:

  • /WP (Pin 3) must be actively driven high — confirmed required; without it, writes fail
  • /HOLD (Pin 7) — not wired in the tested setup and it worked. The PCB has a pull-up on this line, but it may be marginal. If you see intermittent failures, add a wire to 3.3V. It is always safe. See Tools — /WP and /HOLD Troubleshooting
  • Idle chip /CS — the W541 has two flash chips sharing MOSI/MISO traces. When clipping one chip, hold pin 1 (/CS) of the other chip HIGH through a resistor to 3.3V. See Part 3 for full details

Pi: Two 3.3V distribution points — Pin 17 for VCC + /WP, Pin 1 → 47Ω for idle chip /CS.

Pico: Pin 36 carries VCC + /WP + idle chip /CS (via 47Ω) — three connections minimum, four if you add /HOLD. Pin 37 (3V3_EN) is NOT a power output — do not use it.

flashprog Command Syntax

Pi 3/4:

-p linux_spi:dev=/dev/spidev0.0,spispeed=1000

Pico:

-p serprog:dev=/dev/ttyACM0,spispeed=16M

All other options (chip names, read/write/verify flags, filenames) are identical between programmers.


Part 3 — W541 Dual Flash Chip Layout

ChipLocationSizeRole
Chip 1Left, near white ribbon connector8 MiBchip1.bin / bottom.rom
Chip 2Right, closer to board edge4 MiBchip2.bin / top.rom

Both chips share MOSI/MISO PCB traces at zero ohms. When you clip Chip 1 and power it, Chip 2 also powers up parasitically and its /CS pin floats — causing random interference with reads and writes. The fix is to hold pin 1 (/CS) of the idle chip HIGH through a resistor to 3.3V. 47Ω is the textbook value; anything from ~47Ω to 1kΩ works — 1kΩ has been confirmed working. This is the only connection needed on the idle chip — everything else is already handled by the shared PCB traces.

SOIC-8 pinout reference:

      ┌───────┐
 /CS 1│●      │8 VCC
  DO 2│       │7 /HOLD
 /WP 3│       │6 CLK
 GND 4│       │5 DI
      └───────┘

Pin 1 = dot on IC package. Pin numbering goes anti-clockwise.


Part 4 — Disassembly and Reading the Factory ROM

4.1 Disassembly

The W541’s flash chips are on the underside of the motherboard, which means the motherboard has to come out fully to access them — this is not a quick bottom-panel job. Search YouTube or iFixit for “ThinkPad W541 motherboard removal” before starting; there are good video guides. The process involves removing the keyboard, palmrest, display assembly, and several ribbon cables before the board lifts out.

Key steps:

  1. Remove battery and AC power. Hold power button 10 seconds to discharge caps.
  2. Remove bottom cover screws and pop the cover.
  3. Disconnect all ribbon cables, antenna wires, and connectors as you work through the disassembly.
  4. Lift the motherboard out. The two SOIC-8 flash chips are on the underside, near the LCD cable area — small, close together.
  5. Clean chip legs with IPA before clipping.

Do not fully reassemble until you’ve confirmed a successful boot test. Bench-test first: just AC power + display cable.

4.2 Triple-Read Both Chips

Chip 1 (8 MiB): full clip on Chip 1, idle hook on Chip 2 pin 1

# Pi 3/4:
sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 -r chip1_r1.bin
sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 -r chip1_r2.bin
sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 -r chip1_r3.bin

# Pico:
# sudo flashprog -p serprog:dev=/dev/ttyACM0,spispeed=16M -r chip1_r1.bin
# (same for r2, r3)

sha256sum chip1_r1.bin chip1_r2.bin chip1_r3.bin
# All three must match. Size: exactly 8388608 bytes.

Chip 2 (4 MiB): move clip to Chip 2, move idle hook to Chip 1 pin 1

# Pi 3/4:
sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 -r chip2_r1.bin
sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 -r chip2_r2.bin
sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 -r chip2_r3.bin

sha256sum chip2_r1.bin chip2_r2.bin chip2_r3.bin
# All three must match. Size: exactly 4194304 bytes.

Merge into a single 12 MiB backup:

cat chip1_r1.bin chip2_r1.bin > factory_12mb.bin
ls -la factory_12mb.bin
# Must be 12582912 bytes

Keep factory_12mb.bin somewhere safe — it’s your recovery image.

Inconsistent reads? Clean chip legs with IPA. Try spispeed=512 (Pi) or spispeed=4M (Pico). Specify chip manually: flashprog ... -c "W25Q64.V". Confirm idle chip’s /CS is held high.


Part 5 — Extract Blobs from the Factory Dump

All blob extraction is done from the factory dump you just read — no tools need to run on the W541 itself.

5.1 Clone Coreboot and Build Utilities

See Tools — ifdtool for initial coreboot clone and ifdtool build. Also build cbfstool:

5.2 Extract Flash Regions with ifdtool

util/ifdtool/ifdtool -x ../factory_12mb.bin

Produces:

flashregion_0_flashdescriptor.bin   (Intel Flash Descriptor / IFD)
flashregion_1_bios.bin              (BIOS region — Lenovo UEFI + embedded ROMs)
flashregion_2_intel_me.bin          (Intel ME firmware)
flashregion_3_gbe.bin               (Gigabit Ethernet NVM)

Note your MAC address before proceeding:

strings flashregion_3_gbe.bin | grep -E "([0-9A-Fa-f]{2}:){5}" | head -3

5.3 Extract the Nvidia K1100M VGA ROM and Intel VBT using UEFIExtract

UEFIExtract (from LongSoft’s UEFITool project) is the proven reliable tool for this — it understands the UEFI volume structure inside flashregion_1_bios.bin and extracts all sections cleanly, including embedded PCI option ROMs.

5.3a Get UEFIExtract

cd ~  # or wherever you're working
wget https://github.com/LongSoft/UEFITool/releases/download/A72/UEFIExtract_NE_A72_x64_linux.zip
unzip UEFIExtract_NE_A72_x64_linux.zip
chmod +x UEFIExtract

5.3b Extract the Full BIOS Region Tree

./UEFIExtract flashregion_1_bios.bin all
# Creates: flashregion_1_bios.bin.dump/
# (a directory tree of every UEFI section, each as a separate body.bin)

5.3c Find the Nvidia VGA ROM by PCI Vendor/Device ID

GPUPCI IDlittle-endian in ROMStatus
Quadro K1100M10DE:0FF6de 10 f6 0fTested and confirmed working
Quadro K2100M10DE:11FCde 10 fc 11Should work in principle — same extraction method, same coreboot config pattern, not personally tested

The W541 factory BIOS actually contains both Nvidia option ROMs regardless of which GPU is fitted — the Python script will find and extract whichever ones are present. Only the ROM matching your actual GPU needs to go into the coreboot build.

This Python script walks the dump, parses every valid PCI option ROM header, and auto-extracts any Nvidia ROM it finds:

python3 - <<'EOF'
import os, struct

for root, dirs, files in os.walk('flashregion_1_bios.bin.dump'):
    for name in files:
        if name == 'body.bin':
            path = os.path.join(root, name)
            try:
                with open(path, 'rb') as f:
                    d = f.read()
                # Must start with PCI ROM magic 55 AA
                if len(d) > 0x1a and d[0] == 0x55 and d[1] == 0xAA:
                    # Pointer to PCI Data Structure is at offset 0x18 (little-endian word)
                    off = struct.unpack_from('<H', d, 0x18)[0]
                    if off + 8 <= len(d) and d[off:off+4] == b'PCIR':
                        vid = struct.unpack_from('<H', d, off+4)[0]
                        did = struct.unpack_from('<H', d, off+6)[0]
                        sz  = d[2] * 512
                        print(f'{path}  vendor=0x{vid:04x} device=0x{did:04x} size={sz} bytes')
                        if vid == 0x10DE:
                            outname = f'pci10de_{did:04x}.rom'
                            open(outname, 'wb').write(d[:sz] if sz else d)
                            print(f'  >>> EXTRACTED to {outname}')
            except:
                pass
EOF

Expected output will include lines for both Nvidia ROMs if the factory BIOS contains them:

...  vendor=0x10de device=0x0ff6 size=... bytes
  >>> EXTRACTED to pci10de_0ff6.rom    ← K1100M
...  vendor=0x10de device=0x11fc size=... bytes
  >>> EXTRACTED to pci10de_11fc.rom    ← K2100M

Rename whichever matches your GPU:

# K1100M:
mv pci10de_0ff6.rom nvidia_k1100m.rom
hexdump -C nvidia_k1100m.rom | head -3
strings nvidia_k1100m.rom | grep -i nvidia | head -3

# K2100M:
# mv pci10de_11fc.rom nvidia_k2100m.rom
# hexdump -C nvidia_k2100m.rom | head -3
# strings nvidia_k2100m.rom | grep -i nvidia | head -3

Both ROMs must start with 55 AA and contain an NVIDIA string. Keep both extractions safe regardless — you only embed one in the coreboot build.

5.3d Find and Extract the Intel VBT

The Intel GOP/VBT is embedded under a specific GUID path in the dump tree. The script above will have shown a line like:

flashregion_1_bios.bin.dump/1 7A9354D9-.../0 9E21FD93-.../0 Compressed section/
  0 Volume image section/0 A881D567-.../128 29206FC2-.../0 Raw section/body.bin
  vendor=0x8086 device=0x0406 size=... bytes

Copy that specific body.bin as your Intel VBT/VGA ROM:

cp "flashregion_1_bios.bin.dump/1 7A9354D9-0468-444A-81CE-0BF617D890DF/\
0 9E21FD93-9C72-4C15-8C4B-E77F1DB2D792/0 Compressed section/\
0 Volume image section/0 A881D567-6CB0-4EEE-8435-2E72D33E45B5/\
128 29206FC2-9EAB-4612-ACA1-1E3D098FB1B3/0 Raw section/body.bin" \
intel_vbt.bin

# Verify: look for $VBT signature
hexdump -C intel_vbt.bin | grep -m3 ""
strings intel_vbt.bin | grep -i vbt | head -3

The exact directory path will match what the scan printed — the GUIDs above are from the W541 factory BIOS. If your dump path differs slightly (different section numbering), use the path the Python script reported for the 0x8086:0x0406 ROM.

5.5 Get mrc.bin (Haswell Memory Reference Code)

The MRC blob is the same across all Haswell platforms. If you have a working T440p coreboot setup, reuse that mrc.bin directly — it works on the W541.

# Check if already present in coreboot blobs submodule
ls 3rdparty/blobs/northbridge/intel/haswell/mrc.bin

If not available, use chromeos one:

cd util/chromeos
./crosfirmware.sh peppy
../cbfstool/cbfstool coreboot-*.bin extract -f mrc.bin -n mrc.bin -r RO_SECTION
cp mrc.bin ~/coreboot-blobs/

5.6 Intel ME — handled by the build

No manual me_cleaner step is needed. The .config includes CONFIG_USE_ME_CLEANER=y with -S args, so coreboot runs me_cleaner on the raw ME binary automatically at build time. Just pass flashregion_2_intel_me.bin directly — no pre-processing required.


Part 6 — Organise Blobs

The .config uses absolute paths for all blobs. The simplest approach is a flat coreboot-blobs/ directory alongside the coreboot tree:

mkdir -p ~/coreboot/coreboot-blobs

cp flashregion_0_flashdescriptor.bin  ~/coreboot/coreboot-blobs/
cp flashregion_2_intel_me.bin         ~/coreboot/coreboot-blobs/   # raw — no pre-cleaning needed
cp flashregion_3_gbe.bin              ~/coreboot/coreboot-blobs/
cp mrc.bin                            ~/coreboot/coreboot-blobs/

# VBT — must go to this exact path in the coreboot source tree
cp intel_vbt.bin /home/chrisf4lc0n/Downloads/coreboot/src/mainboard/lenovo/haswell/variants/w541

# Nvidia ROM — in coreboot-blobs alongside the others
cp nvidia_k1100m.rom  ~/coreboot/coreboot-blobs/pci10de,0ff6.rom    # K1100M
# cp nvidia_k2100m.rom  ~/coreboot/coreboot-blobs/pci10de,11fc.rom  # K2100M

The VBT is the only blob that must live inside the coreboot source tree — src/mainboard/lenovo/t540p/data.vbt. Everything else is referenced by absolute path in .config and can live wherever is convenient.


Part 7 — Coreboot .config

This is the actual working .config used for this build. Save it as .config inside the coreboot source directory, then update every absolute path under # Chipset — blob paths to match your own directory layout before running make olddefconfig.

# Automatically generated file; DO NOT EDIT.
# coreboot configuration

#
# General Setup
#
CONFIG_LOCALVERSION=""
CONFIG_CBFS_PREFIX="fallback"
CONFIG_COMPILER_GCC=y
CONFIG_USE_OPTION_TABLE=y
CONFIG_COMPRESS_RAMSTAGE_LZMA=y
CONFIG_SEPARATE_ROMSTAGE=y
CONFIG_INCLUDE_CONFIG_FILE=y
CONFIG_COLLECT_TIMESTAMPS=y
CONFIG_USE_BLOBS=y
CONFIG_HAVE_ASAN_IN_ROMSTAGE=y
CONFIG_HAVE_ASAN_IN_RAMSTAGE=y
CONFIG_TSEG_STAGE_CACHE=y

#
# Mainboard
#
CONFIG_VENDOR_LENOVO=y
CONFIG_MAINBOARD_FAMILY="ThinkPad W541"
CONFIG_MAINBOARD_PART_NUMBER="ThinkPad W541"
CONFIG_MAINBOARD_VERSION="1.0"
CONFIG_MAINBOARD_DIR="lenovo/haswell"
CONFIG_MAINBOARD_VENDOR="LENOVO"
CONFIG_MAINBOARD_SMBIOS_MANUFACTURER="LENOVO"
CONFIG_MAINBOARD_SMBIOS_PRODUCT_NAME="ThinkPad W541"
CONFIG_MAINBOARD_POWER_FAILURE_STATE=0
CONFIG_HAVE_POWER_STATE_AFTER_FAILURE=y
CONFIG_HAVE_POWER_STATE_PREVIOUS_AFTER_FAILURE=y
CONFIG_POWER_STATE_OFF_AFTER_FAILURE=y
CONFIG_BOARD_LENOVO_THINKPAD_W541=y
CONFIG_BOARD_LENOVO_HASWELL_COMMON=y
CONFIG_VBOOT_SLOTS_RW_AB=y
CONFIG_VARIANT_DIR="w541"
CONFIG_OVERRIDE_DEVICETREE=""
CONFIG_DEVICETREE="variants/$(CONFIG_VARIANT_DIR)/devicetree.cb"
CONFIG_FMDFILE=""
CONFIG_NO_POST=y
CONFIG_SYSTEM_TYPE_LAPTOP=y

#
# ROM / CBFS
#
CONFIG_CBFS_SIZE=0x600000
CONFIG_BOARD_ROMSIZE_KB_12288=y
CONFIG_COREBOOT_ROMSIZE_KB_12288=y
CONFIG_COREBOOT_ROMSIZE_KB=12288
CONFIG_ROM_SIZE=0x00c00000

#
# Memory / Cache
#
CONFIG_DIMM_MAX=4
CONFIG_DIMM_SPD_SIZE=256
CONFIG_MAX_CPUS=8
CONFIG_MAX_SOCKET=1
CONFIG_HEAP_SIZE=0x100000
CONFIG_DCACHE_RAM_BASE=0xff7c0000
CONFIG_DCACHE_RAM_SIZE=0x10000
CONFIG_DCACHE_BSP_STACK_SIZE=0x2000
CONFIG_MEMLAYOUT_LD_FILE="src/arch/x86/memlayout.ld"

#
# PCIe
#
CONFIG_PCIEXP_ASPM=y
CONFIG_PCIEXP_L1_SUB_STATE=y
CONFIG_PCIEXP_CLK_PM=y
CONFIG_PCIEXP_AER=y
CONFIG_CARDBUS_PLUGIN_SUPPORT=y

#
# VGA BIOS
#
CONFIG_VGA_BIOS=y
CONFIG_VGA_BIOS_ID="10de,0ff6"
CONFIG_VGA_BIOS_FILE="coreboot-blobs/pci10de,0ff6.rom"
CONFIG_ONBOARD_VGA_IS_PRIMARY=y
CONFIG_INTEL_GMA_VBT_FILE="src/mainboard/$(MAINBOARDDIR)/variants/$(VARIANT_DIR)/data.vbt"
CONFIG_LINEAR_FRAMEBUFFER_MAX_HEIGHT=1600
CONFIG_LINEAR_FRAMEBUFFER_MAX_WIDTH=2560
CONFIG_GFX_GMA_PANEL_1_PORT="DP3"
CONFIG_GFX_GMA_PANEL_1_ON_EDP=y
CONFIG_D3COLD_SUPPORT=y

#
# Input / PS2 / CMOS
#
CONFIG_PS2K_EISAID="LEN0071"
CONFIG_PS2M_EISAID="LEN004A"
CONFIG_THINKPADEC_HKEY_EISAID="LEN0068"
CONFIG_PC_CMOS_BASE_PORT_BANK1=0x72
CONFIG_CMOS_DEFAULT_FILE="src/mainboard/$(MAINBOARDDIR)/cmos.default"
CONFIG_CMOS_LAYOUT_FILE="src/mainboard/$(MAINBOARDDIR)/cmos.layout"

#
# Intel Firmware Blobs (IFD / ME / GBE / MRC)
#
CONFIG_HAVE_INTEL_FIRMWARE=y
CONFIG_HAVE_IFD_BIN=y
CONFIG_IFD_BIN_PATH="coreboot-blobs/flashregion_0_flashdescriptor.bin"
CONFIG_ME_BIN_PATH="coreboot-blobs/flashregion_2_intel_me.bin"
CONFIG_GBE_BIN_PATH="coreboot-blobs/flashregion_3_gbe.bin"
CONFIG_ME_CLEANER_ARGS="-S"
CONFIG_HAVE_ME_BIN=y
CONFIG_ME_REGION_ALLOW_CPU_READ_ACCESS=y
CONFIG_USE_ME_CLEANER=y
CONFIG_MAINBOARD_USES_IFD_GBE_REGION=y
CONFIG_HAVE_GBE_BIN=y
CONFIG_UNLOCK_FLASH_REGIONS=y
CONFIG_HAVE_MRC=y
CONFIG_MRC_FILE="coreboot-blobs/mrc.bin"
CONFIG_MRC_SETTINGS_CACHE_SIZE=0x10000
CONFIG_CACHE_MRC_SETTINGS=y

#
# Chipset / SoC
#
CONFIG_CHIPSET_DEVICETREE=""
CONFIG_CBFS_MCACHE_SIZE=0x4000
CONFIG_ROMSTAGE_ADDR=0x2000000
CONFIG_VERSTAGE_ADDR=0x2000000
CONFIG_SMM_TSEG_SIZE=0x800000
CONFIG_SMM_RESERVED_SIZE=0x100000
CONFIG_SMM_MODULE_STACK_SIZE=0x400
CONFIG_SERIRQ_CONTINUOUS_MODE=y
CONFIG_CPU_PT_ROM_MAP_GB=512
CONFIG_PRERAM_CBFS_CACHE_SIZE=0x4000
CONFIG_DOMAIN_RESOURCE_32BIT_LIMIT=0xf0000000
CONFIG_EHCI_BAR=0xe8000000
CONFIG_ACPI_CPU_STRING="CP%02X"
CONFIG_STACK_SIZE=0x2000
CONFIG_IED_REGION_SIZE=0x400000
CONFIG_INTEL_GMA_BCLV_OFFSET=0x48254
CONFIG_INTEL_GMA_BCLV_WIDTH=16
CONFIG_INTEL_GMA_BCLM_OFFSET=0xc8256
CONFIG_INTEL_GMA_BCLM_WIDTH=16
CONFIG_BOOTBLOCK_IN_CBFS=y
CONFIG_DCACHE_RAM_MRC_VAR_SIZE=0x30000
CONFIG_HPET_MIN_TICKS=0x80
CONFIG_FIXED_MCHBAR_MMIO_BASE=0xfed10000
CONFIG_FIXED_DMIBAR_MMIO_BASE=0xfed18000
CONFIG_FIXED_EPBAR_MMIO_BASE=0xfed19000
CONFIG_PCIEXP_COMMON_CLOCK=y
CONFIG_DISABLE_ME_PCI=y
CONFIG_CPU_INTEL_NUM_FIT_ENTRIES=6
CONFIG_SOC_PHYSICAL_ADDRESS_WIDTH=0
CONFIG_DEBUG_STACK_OVERFLOW_BREAKPOINTS=y
CONFIG_RAMSTAGE_CBFS_CACHE_SIZE=0x4000
CONFIG_CBFS_CACHE_ALIGN=8
CONFIG_FIXED_SMBUS_IO_BASE=0x400
CONFIG_UART_BITBANG_TX_DELAY_MS=5

#
# CPU (Intel Haswell)
#
CONFIG_CPU_INTEL_HASWELL=y
CONFIG_CPU_INTEL_FIRMWARE_INTERFACE_TABLE=y
CONFIG_CPU_INTEL_COMMON=y
CONFIG_CPU_INTEL_COMMON_TIMEBASE=y
CONFIG_CPU_INTEL_COMMON_VOLTAGE=y
CONFIG_CPU_INTEL_COMMON_SMM=y
CONFIG_ENABLE_VMX=y
CONFIG_SET_IA32_FC_LOCK_BIT=y
CONFIG_SET_MSR_AESNI_LOCK_BIT=y
CONFIG_PARALLEL_MP=y
CONFIG_XAPIC_ONLY=y
CONFIG_UDELAY_TSC=y
CONFIG_TSC_MONOTONIC_TIMER=y
CONFIG_TSC_SYNC_MFENCE=y
CONFIG_HAVE_SMI_HANDLER=y
CONFIG_SMM_TSEG=y
CONFIG_SMM_PCI_RESOURCE_STORE_NUM_SLOTS=8
CONFIG_AP_STACK_SIZE=0x800
CONFIG_SMP=y
CONFIG_SSE=y
CONFIG_SSE2=y
CONFIG_SUPPORT_CPU_UCODE_IN_CBFS=y
CONFIG_USE_CPU_MICROCODE_CBFS_BINS=y
CONFIG_CPU_MICROCODE_CBFS_DEFAULT_BINS=y

#
# Northbridge (Intel Haswell)
#
CONFIG_NORTHBRIDGE_INTEL_HASWELL=y
CONFIG_HASWELL_HIDE_PEG_FROM_MRC=y

#
# Southbridge (Intel Lynx Point)
#
CONFIG_INTEL_DESCRIPTOR_MODE_REQUIRED=y
CONFIG_INTEL_DESCRIPTOR_MODE_CAPABLE=y
CONFIG_SOUTHBRIDGE_INTEL_LYNXPOINT=y
CONFIG_FINALIZE_USB_ROUTE_XHCI=y
CONFIG_SOUTHBRIDGE_INTEL_COMMON_RESET=y
CONFIG_SOUTHBRIDGE_INTEL_COMMON_RTC=y
CONFIG_SOUTHBRIDGE_INTEL_COMMON_PMCLIB=y
CONFIG_SOUTHBRIDGE_INTEL_COMMON_PMBASE=y
CONFIG_SOUTHBRIDGE_INTEL_COMMON_GPIO=y
CONFIG_SOUTHBRIDGE_INTEL_COMMON_EARLY_SMBUS=y
CONFIG_SOUTHBRIDGE_INTEL_COMMON_SMBUS=y
CONFIG_SOUTHBRIDGE_INTEL_COMMON_SPI=y
CONFIG_SOUTHBRIDGE_INTEL_COMMON_SPI_ICH9=y
CONFIG_SOUTHBRIDGE_INTEL_COMMON_PIRQ_ACPI_GEN=y
CONFIG_SOUTHBRIDGE_INTEL_COMMON_RCBA_PIRQ=y
CONFIG_HAVE_INTEL_CHIPSET_LOCKDOWN=y
CONFIG_INTEL_CHIPSET_LOCKDOWN=y
CONFIG_SOUTHBRIDGE_INTEL_COMMON_FINALIZE=y
CONFIG_SOUTHBRIDGE_INTEL_COMMON_USB_DEBUG=y
CONFIG_TCO_SPACE_NOT_YET_SPLIT=y
CONFIG_SOUTHBRIDGE_INTEL_COMMON_WATCHDOG=y
CONFIG_FIXED_RCBA_MMIO_BASE=0xfed1c000
CONFIG_RCBA_LENGTH=0x4000

#
# Embedded Controller (Lenovo H8 / PMH7)
#
CONFIG_EC_ACPI=y
CONFIG_EC_LENOVO_H8=y
CONFIG_H8_BEEP_ON_DEATH=y
CONFIG_H8_FLASH_LEDS_ON_DEATH=y
CONFIG_H8_SUPPORT_BT_ON_WIFI=y
CONFIG_H8_HAS_BAT_THRESHOLDS_IMPL=y
CONFIG_H8_HAS_PRIMARY_FN_KEYS=y
CONFIG_H8_HAS_LEDLOGO=y
CONFIG_EC_LENOVO_PMH7=y

#
# Architecture / Boot Stages (x86-32)
#
CONFIG_ACPI_FNKEY_GEN_SCANCODE=0
CONFIG_ARCH_X86=y
CONFIG_ARCH_BOOTBLOCK_X86_32=y
CONFIG_ARCH_VERSTAGE_X86_32=y
CONFIG_ARCH_ROMSTAGE_X86_32=y
CONFIG_ARCH_POSTCAR_X86_32=y
CONFIG_ARCH_RAMSTAGE_X86_32=y
CONFIG_ARCH_ALL_STAGES_X86_32=y
CONFIG_RESERVED_PHYSICAL_ADDRESS_BITS_SUPPORT=y
CONFIG_X86_TOP4G_BOOTMEDIA_MAP=y
CONFIG_POSTRAM_CBFS_CACHE_IN_BSS=y
CONFIG_PC80_SYSTEM=y
CONFIG_HAVE_CMOS_DEFAULT=y
CONFIG_POSTCAR_STAGE=y
CONFIG_BOOTBLOCK_SIMPLE=y
CONFIG_COLLECT_TIMESTAMPS_TSC=y
CONFIG_HAVE_CF9_RESET=y
CONFIG_DEBUG_HW_BREAKPOINTS=y
CONFIG_DEBUG_NULL_DEREF_BREAKPOINTS=y
CONFIG_X86_BOOTBLOCK_EXTRA_PROGRAM_SZ=0
CONFIG_DEFAULT_EBDA_LOWMEM=0x100000
CONFIG_DEFAULT_EBDA_SEGMENT=0xF600
CONFIG_DEFAULT_EBDA_SIZE=0x400

#
# Graphics (libgfxinit / Intel GMA)
#
CONFIG_HAVE_VGA_TEXT_FRAMEBUFFER=y
CONFIG_HAVE_LINEAR_FRAMEBUFFER=y
CONFIG_VGA_ROM_RUN_DEFAULT=y
CONFIG_MAINBOARD_HAS_LIBGFXINIT=y
CONFIG_MAINBOARD_USE_LIBGFXINIT=y
CONFIG_NO_EARLY_GFX_INIT=y
CONFIG_GENERIC_LINEAR_FRAMEBUFFER=y
CONFIG_LINEAR_FRAMEBUFFER=y
CONFIG_DEFAULT_SCREEN_ROTATION_INT=0
CONFIG_INTEL_DDI=y
CONFIG_INTEL_GMA_ACPI=y
CONFIG_VBT_CBFS_COMPRESSION_LZMA=y
CONFIG_VBT_CBFS_COMPRESSION_ALGORITHM="lzma"
CONFIG_GFX_GMA=y
CONFIG_GFX_GMA_DYN_CPU=y
CONFIG_GFX_GMA_GENERATION="Haswell"
CONFIG_GFX_GMA_PCH="Lynx_Point"
CONFIG_GFX_GMA_PANEL_2_PORT="Disabled"
CONFIG_GFX_GMA_ANALOG_I2C_PORT="PCH_DAC"
CONFIG_INTEL_GMA_HAVE_VBT=y
CONFIG_INTEL_GMA_ADD_VBT=y

#
# PCI / Devices
#
CONFIG_PCI=y
CONFIG_ECAM_MMCONF_SUPPORT=y
CONFIG_ECAM_MMCONF_BASE_ADDRESS=0xf0000000
CONFIG_ECAM_MMCONF_BUS_NUMBER=64
CONFIG_ECAM_MMCONF_LENGTH=0x04000000
CONFIG_PCIX_PLUGIN_SUPPORT=y
CONFIG_PCIEXP_PLUGIN_SUPPORT=y
CONFIG_PCI_ALLOW_BUS_MASTER=y
CONFIG_PCI_SET_BUS_MASTER_PCI_BRIDGES=y
CONFIG_PCI_ALLOW_BUS_MASTER_ANY_DEVICE=y
CONFIG_SUBSYSTEM_VENDOR_ID=0x0000
CONFIG_SUBSYSTEM_DEVICE_ID=0x0000
CONFIG_AZALIA_HDA_CODEC_SUPPORT=y
CONFIG_AZALIA_USE_LEGACY_VERB_TABLE=y
CONFIG_I2C_TRANSFER_TIMEOUT_US=500000
CONFIG_RESOURCE_ALLOCATION_TOP_DOWN=y
CONFIG_DRAM_SUPPORT_DDR3=y
CONFIG_DRIVERS_INTEL_WIFI=y
CONFIG_DRIVERS_WIFI_GENERIC=y
CONFIG_DRIVERS_MTK_WIFI=y

#
# Generic Drivers / SPI Flash / SMM Store
#
CONFIG_SPI_FLASH=y
CONFIG_SPI_FLASH_INCLUDE_ALL_DRIVERS=y
CONFIG_SPI_FLASH_WINBOND=y
CONFIG_SPI_FLASH_GIGADEVICE=y
CONFIG_SPI_FLASH_STMICRO=y
CONFIG_SPI_FLASH_ADESTO=y
CONFIG_SPI_FLASH_AMIC=y
CONFIG_SPI_FLASH_ATMEL=y
CONFIG_SPI_FLASH_EON=y
CONFIG_SPI_FLASH_MACRONIX=y
CONFIG_SPI_FLASH_SPANSION=y
CONFIG_SPI_FLASH_SST=y
CONFIG_SPI_FLASH_ISSI=y
CONFIG_SPI_FLASH_SMM=y
CONFIG_BOOT_DEVICE_SPI_FLASH=y
CONFIG_BOOT_DEVICE_SPI_FLASH_BUS=0
CONFIG_BOOT_DEVICE_SPI_FLASH_RW_NOMMAP=y
CONFIG_BOOT_DEVICE_SPI_FLASH_RW_NOMMAP_EARLY=y
CONFIG_BOOT_DEVICE_MEMORY_MAPPED=y
CONFIG_BOOT_DEVICE_SUPPORTS_WRITES=y
CONFIG_SMMSTORE=y
CONFIG_SMMSTORE_V2=y
CONFIG_SMMSTORE_SIZE=0x80000
CONFIG_HAVE_EM100PRO_SPI_CONSOLE_SUPPORT=y
CONFIG_NO_UART_ON_SUPERIO=y
CONFIG_HAVE_USBDEBUG=y
CONFIG_HAVE_USBDEBUG_OPTIONS=y
CONFIG_USBDEBUG_HCD_INDEX=2
CONFIG_DRIVERS_PS2_KEYBOARD=y
CONFIG_DRIVERS_MC146818=y
CONFIG_PC_CMOS_BASE_PORT_BANK0=0x70

#
# Trusted Platform Module
#
CONFIG_TPM1=y
CONFIG_TPM=y
CONFIG_MAINBOARD_HAS_TPM1=y
CONFIG_MEMORY_MAPPED_TPM=y
CONFIG_TPM_TIS_BASE_ADDRESS=0xfed40000
CONFIG_CRB_TPM_BASE_ADDRESS=0xfed40000
CONFIG_TPM_PIRQ=0x0
CONFIG_TPM_INIT_RAMSTAGE=y
CONFIG_PCR_BOOT_MODE=1
CONFIG_PCR_HWID=1
CONFIG_PCR_SRTM=2
CONFIG_PCR_FW_VER=10
CONFIG_PCR_RUNTIME_DATA=3

#
# Security / Boot Media Lock
#
CONFIG_PLATFORM_HAS_DRAM_CLEAR=y
CONFIG_BOOTMEDIA_LOCK_NONE=y

#
# ACPI
#
CONFIG_ACPI_HAVE_PCAT_8259=y
CONFIG_ACPI_INTEL_HARDWARE_SLEEP_VALUES=y
CONFIG_ACPI_SOC_NVS=y
CONFIG_ACPI_NO_CUSTOM_MADT=y
CONFIG_ACPI_COMMON_MADT_LAPIC=y
CONFIG_ACPI_COMMON_MADT_IOAPIC=y
CONFIG_HAVE_ACPI_TABLES=y
CONFIG_HAVE_ACPI_RESUME=y
CONFIG_RESUME_PATH_SAME_AS_BOOT=y
CONFIG_RTC=y
CONFIG_IOAPIC=y
CONFIG_USE_WATCHDOG_ON_BOOT=y
CONFIG_HAVE_MONOTONIC_TIMER=y
CONFIG_HAVE_OPTION_TABLE=y

#
# Console
#
CONFIG_BOOTBLOCK_CONSOLE=y
CONFIG_POSTCAR_CONSOLE=y
CONFIG_SQUELCH_EARLY_SMP=y
CONFIG_CONSOLE_CBMEM=y
CONFIG_CONSOLE_CBMEM_BUFFER_SIZE=0x20000
CONFIG_PRERAM_CBMEM_CONSOLE_SIZE=0xc00
CONFIG_DEFAULT_CONSOLE_LOGLEVEL_7=y
CONFIG_DEFAULT_CONSOLE_LOGLEVEL=7
CONFIG_CONSOLE_USE_LOGLEVEL_PREFIX=y
CONFIG_CONSOLE_USE_ANSI_ESCAPES=y
CONFIG_HWBASE_DEBUG_CB=y

#
# System Tables / SMBIOS
#
CONFIG_GENERATE_SMBIOS_TABLES=y
CONFIG_BIOS_VENDOR="coreboot"
CONFIG_MAINBOARD_SERIAL_NUMBER="123456789"

#
# Payload (SeaBIOS)
#
CONFIG_PAYLOAD_SEABIOS=y
CONFIG_PAYLOAD_FILE="payloads/external/SeaBIOS/seabios/out/bios.bin.elf"
CONFIG_PAYLOAD_CONFIGFILE=""
CONFIG_PAYLOAD_BUILD_SEABIOS=y
CONFIG_SEABIOS_STABLE=y
CONFIG_SEABIOS_VGA_COREBOOT=y
CONFIG_PAYLOAD_VGABIOS_FILE="payloads/external/SeaBIOS/seabios/out/vgabios.bin"
CONFIG_SEABIOS_DEBUG_LEVEL=-1
CONFIG_SEABIOS_HARDWARE_IRQ=y
CONFIG_SEABIOS_PS2_TIMEOUT=5000
#
# Boot Order
# /pci@i0cf8/*@1f,2/drive@0/disk@0
# /pci@i0cf8/usb@1a/hub@1/storage@2/*@0/*@0,0
# /pci@i0cf8/*@19
# /pci@i0cf8/pci-bridge@1c/*@0
# /pci@i0cf8/*@1f,2/drive@5/disk@0
#
CONFIG_SEABIOS_BOOTORDER_FILE=""

#
# PXE / iPXE
#
CONFIG_PXE=y
CONFIG_PXE_ROM_ID="8086,153a"
CONFIG_BUILD_IPXE=y
CONFIG_IPXE_STABLE=y

#
# Payload Compression
#
CONFIG_COMPRESSED_PAYLOAD_LZMA=y
CONFIG_COMPRESS_SECONDARY_PAYLOAD=y

#
# Secondary Payloads
#
CONFIG_COREINFO_SECONDARY_PAYLOAD=y
CONFIG_MEMTEST_SECONDARY_PAYLOAD=y
CONFIG_NVRAMCUI_SECONDARY_PAYLOAD=y
CONFIG_MEMTEST86PLUS_V6=y
CONFIG_MEMTEST86PLUS_ARCH_64=y
CONFIG_MEMTEST_STABLE=y

#
# Debugging
#
CONFIG_HAVE_DEBUG_SMBUS=y
CONFIG_HAVE_EM100_SUPPORT=y

#
# Runtime Libraries
#
CONFIG_RAMSTAGE_ADA=y
CONFIG_RAMSTAGE_LIBHWBASE=y
CONFIG_HWBASE_DYNAMIC_MMIO=y
CONFIG_HWBASE_DEFAULT_MMCONF=0xf0000000
CONFIG_HWBASE_DIRECT_PCIDEV=y
CONFIG_DECOMPRESS_OFAST=y

#
# Boot Logo
#
CONFIG_PLATFORM_POST_RENDER_DELAY_SEC=5
CONFIG_PLATFORM_OFF_MODE_CHARGING_INDICATOR_LOGO_PATH="3rdparty/blobs/mainboard/$(MAINBOARDDIR)/off_mode_charging.bmp"

#
# Build
#
CONFIG_WARNINGS_ARE_ERRORS=y
CONFIG_MAX_REBOOT_CNT=3
CONFIG_RELOCATABLE_MODULES=y
CONFIG_GENERIC_GPIO_LIB=y
CONFIG_HAVE_BOOTBLOCK=y
CONFIG_HAVE_ROMSTAGE=y
CONFIG_HAVE_RAMSTAGE=y

Path note: All _PATH and _FILE entries above use /home/user/coreboot/... as a placeholder. Replace with your actual username and directory layout. The VBT has no path entry here — coreboot picks it up automatically from src/mainboard/lenovo/t540p/data.vbt inside the source tree (placed in Part 6).


Part 7a — Optional: PXE Boot via iPXE

If you want network boot, coreboot can build iPXE from source and embed it directly in the ROM. No pre-built ROM file is needed — the coreboot build system fetches and compiles iPXE automatically when CONFIG_BUILD_IPXE=y is set.

How it works

The W541’s onboard Gigabit Ethernet is an Intel I217-LM (PCI ID 8086:153a). Setting CONFIG_PXE_ROM_ID="8086,153a" tells coreboot to associate the compiled iPXE ROM with that specific NIC. SeaBIOS then finds it and makes the NIC available as a PXE boot option.

What the iPXE options add

The default iPXE build covers standard PXE/DHCP network boot which is all you need for a local network boot server like iVentoy. HTTPS and trust/certificate options are not enabled — local network boot servers don’t typically serve over HTTPS and there’s no benefit to the extra complexity.

Secondary payloads

The PXE config also adds two secondary payloads accessible from the SeaBIOS boot menu:

  • Coreinfo — shows system information, CBFS contents, and memory map; useful for diagnostics
  • Memtest86+ v6 (64-bit) — full memory test

Part 8 — Build Coreboot

8.1 Install Build Dependencies

See Tools — Build Dependencies. The W541 additionally needs:

sudo apt install -y imagemagick gcc-multilib libc6-dev-i386 unifont

8.2 Build the Toolchain

See Tools — Cross-Compiler Toolchain.

8.3 Configure and Build

# Apply the .config you saved above
make olddefconfig

# (Optional) verify or tweak settings interactively
make menuconfig

# Build
make -j$(nproc)

Output: build/coreboot.rom

Verify the size:

ls -la build/coreboot.rom
# Must be exactly 12582912 bytes (12 MiB)

Part 9 — Split the ROM for Dual-Chip Flashing

dd if=build/coreboot.rom of=bottom.rom bs=1M count=8
dd if=build/coreboot.rom of=top.rom    bs=1M skip=8

ls -la bottom.rom top.rom
# bottom.rom = 8388608 bytes  → Chip 1 (near white connector)
# top.rom    = 4194304 bytes  → Chip 2 (near board edge)

If you flash the wrong size to a chip, flashprog refuses with a size mismatch — no damage, just swap them.


Part 10 — Flash

10.1 Flash Chip 1 (8 MiB)

Full clip on Chip 1, 47Ω hook on Chip 2 pin 1 → 3.3V.

# Pi 3/4:
sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 -w bottom.rom
sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 -v bottom.rom

# Pico:
# sudo flashprog -p serprog:dev=/dev/ttyACM0,spispeed=16M -w bottom.rom
# sudo flashprog -p serprog:dev=/dev/ttyACM0,spispeed=16M -v bottom.rom

# Must report: VERIFIED

10.2 Flash Chip 2 (4 MiB)

Move clip to Chip 2, move hook to Chip 1 pin 1 → 3.3V.

# Pi 3/4:
sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 -w top.rom
sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 -v top.rom

# Pico:
# sudo flashprog -p serprog:dev=/dev/ttyACM0,spispeed=16M -w top.rom
# sudo flashprog -p serprog:dev=/dev/ttyACM0,spispeed=16M -v top.rom

# Must report: VERIFIED

Part 11 — First Boot Test

Do not fully reassemble before testing. The W541 can be bench-tested with just the motherboard partially installed. You only need:

  • Motherboard seated in the chassis (or on a non-conductive surface)
  • CPU and GPU cooler attached (thermal paste applied, fans connected)
  • Power button ribbon connected
  • Display cable connected and screen laid flat or propped up
  • AC adapter connected (battery not required)
  • USB keyboard plugged in

You do not need: the palmrest, keyboard, bottom cover, battery, or any other ribbons. This keeps the board accessible in case you need to re-clip for a recovery flash.

Boot procedure:

  1. Disconnect the Pi/Pico from USB (remove all programmer wiring from the flash chips first).
  2. Connect AC power.
  3. Press the power button — you should see a coreboot/SeaBIOS/GRUB screen within 30 seconds.
  4. Both GPUs should initialise: coreboot logs at the top of the screen will show iGPU init and the Nvidia VGA ROM executing.
  5. Once you have confirmed a successful boot, proceed to the nvramtool step before full reassembly.

If no display after 2 minutes: The board is still accessible — re-attach clips and flash factory_12mb.bin back (split it the same way):

dd if=factory_12mb.bin of=factory_chip1.bin bs=1M count=8
dd if=factory_12mb.bin of=factory_chip2.bin bs=1M skip=8
# Flash factory_chip1.bin to Chip 1, factory_chip2.bin to Chip 2
# (same flashprog commands as Part 10, substitute filenames)

Part 12 — Post-Flash: Enable Both GPUs with nvramtool

Coreboot’s CMOS defaults may not enable the discrete GPU. nvramtool reads and writes coreboot’s CMOS option table — it must run on the W541 itself (it needs access to the physical CMOS of the machine whose firmware you want to configure).

12.1 Build nvramtool

Build nvramtool on your host/build machine (or on the Pi if that’s more convenient), then copy the binary to the W541 via USB stick, SCP, or any other method:

# On your build machine:
cd coreboot/util/nvramtool
make
# Binary: coreboot/util/nvramtool/nvramtool

# Transfer to the W541 (example via USB stick):
cp nvramtool /media/$USER/<usb>/
# On the W541 after boot:
# sudo cp /media/$USER/<usb>/nvramtool /usr/local/bin/
# sudo chmod +x /usr/local/bin/nvramtool

Alternatively, if the W541 has network access after first boot, build it directly on the machine:

# On the W541 itself (needs build-essential):
sudo apt install build-essential
cd coreboot/util/nvramtool   # or copy the source tree over
make
sudo make install

12.2 List Available CMOS Options

sudo nvramtool -l
# Look for graphics/GPU-related entries, e.g.:
#   hybrid_graphics_mode  (or similar)
#   peg_deassert_wake

The W541/T540p board typically exposes an option along the lines of:

graphics_mode = [auto | discrete | integrated]

12.3 Enable Both GPUs

# List current values
sudo nvramtool -a

# Enable hybrid/both GPU mode — check the exact option name from step 12.2
sudo nvramtool -w hybrid_graphics_mode=enable

# Alternatively, if the option is named differently:
sudo nvramtool -w graphics=both

After setting, reboot the W541. On next boot coreboot reads the CMOS value and enables both the iGPU and K1100M.

12.4 Verify Both GPUs Are Present

lspci | grep -E "VGA|3D|Display"
# Expected:
# 00:02.0 VGA compatible controller: Intel Corporation 4th Gen Core ... Integrated Graphics
# 01:00.0 3D controller: NVIDIA Corporation GK107GLM [Quadro K1100M]

Part 13 — Post-Install: What Works and What Doesn’t

What works

  • Intel HD 4600 iGPU — fully functional, internal display, libgfxinit init at boot
  • Nvidia K1100M dGPU — appears on PCI bus, initialised by VGA ROM at boot, usable via nouveau or Nvidia 390xx driver ✓ tested
  • External displays via dock — functional once the dGPU is up (dock outputs route through the Nvidia chip)
  • USB, storage, networking, audio — all normal
  • Internal re-flashing via flashprog -p internal — works once coreboot is running
  • General Linux desktop use — stable

What does not work

  • Suspend / sleep (S3)confirmed broken. The machine will appear to suspend but does not resume correctly. Do not rely on suspend.
  • Hibernate — not tested; likely broken for the same reasons.

Power draw

With the dGPU enabled via this method, the K1100M is always active — there is no runtime power-gating or Optimus-style switching off. Expect noticeably higher idle power draw and more fan activity compared to stock or Libreboot (iGPU-only). Battery life will be shorter than with Libreboot.

Disabling suspend and hibernate in the OS

Since suspend is broken, disable it to prevent accidental triggering. The exact method varies by desktop environment and distro — search for how to disable suspend and lid-close sleep for your specific setup. On systemd-based systems systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target is the blunt instrument that works everywhere.


Part 14 — dGPU Driver Setup

lspci | grep -E "VGA|3D|Display"
# 00:02.0 VGA compatible controller: Intel Corporation 4th Gen Core ... Integrated Graphics
# 01:00.0 3D controller: NVIDIA Corporation GK107GLM [Quadro K1100M]

nouveau (default, no install needed):

# GPU offload via PRIME
DRI_PRIME=1 <application>

Proprietary Nvidia 390xx (legacy, EOL):

sudo apt install nvidia-legacy-390xx-driver

If thinkpad_acpi fails to load:

# /etc/modprobe.d/thinkpad_acpi.conf
options thinkpad_acpi force_load=1

Part 15 — Internal Re-Flashing (Future Updates)

See Tools — Internal Re-Flashing. For the W541, use the full ROM write:

sudo flashprog -p internal -w build/coreboot.rom

If your kernel restricts iomem access:

# Add to GRUB_CMDLINE_LINUX in /etc/default/grub:
GRUB_CMDLINE_LINUX="iomem=relaxed"
sudo update-grub
# Reboot, then retry

Internal flashing uses the full 12 MiB ROM — no splitting needed.


Part 15 — Troubleshooting

Nvidia ROM not found / Python script finds nothing: Confirm UEFIExtract ran successfully and flashregion_1_bios.bin.dump/ was created. Re-run the Python script and check it’s walking that directory. If the dump exists but no 0x10DE ROM is found, check flashregion_1_bios.bin is the correct region (8 MiB BIOS region, not the full 12 MiB image). As a last resort the PCI sysfs method (echo 1 | sudo tee /sys/bus/pci/devices/0000:01:00.0/rom && dd if=.../rom of=nvidia.rom) can extract it from a running system with nouveau loaded.

Reads fail or are inconsistent: IPA on chip legs. Try spispeed=512 (Pi) or spispeed=4M (Pico). Specify chip: flashprog ... -c "W25Q64.V". Confirm idle chip’s /CS is held high.

Boot shows black screen: Both chips verified? If yes, suspect the Nvidia VGA ROM — verify 55 aa signature and strings nvidia_k1100m.rom | grep -i nvidia. Check .config VGA ROM path and PCI ID (10de,0ff6 for K1100M, 10de,11fc for K2100M — must match your actual GPU). Try building without the Nvidia ROM first to confirm the iGPU init works, then add it back.

K1100M not in lspci after boot: Run the nvramtool step (Part 12) to enable both GPUs in CMOS. Also check ACPI power management: sudo sh -c 'echo on > /sys/bus/pci/devices/0000:01:00.0/power/control'.

nvramtool option names don’t match: Run sudo nvramtool -l to list all available CMOS options on your specific build. Option names depend on the coreboot board source at the time of your build. Cross-reference with src/mainboard/lenovo/t540p/cmos.layout in the coreboot source tree.

build/coreboot.rom is wrong size: The flash map (board.fmd) must match the actual chip layout. The W541 is 12 MiB total (8+4). If the build produces something else, check CONFIG_CBFS_SIZE and CONFIG_FMDFILE.


Appendix A — What Was Discarded / Doesn’t Work

  • lbmk (Libreboot build system) for anything other than pico-serprog — lbmk is used in this guide for one purpose only: building the serprog firmware UF2 for the Raspberry Pi Pico. The coreboot ROM is built entirely from upstream coreboot. If you’re using a Pi 3/4, lbmk is not needed at all.
  • Running me_cleaner manually as a separate step — not needed. CONFIG_USE_ME_CLEANER=y in the .config handles ME neutering at build time; just pass the raw flashregion_2_intel_me.bin.
  • intel_bios_dumper / PCI sysfs extraction from a running system — unnecessary. Both the Nvidia ROM and the Intel VBT are reliably extracted from the factory dump using UEFIExtract without needing the W541 to be running at all.
  • stacksmashing pico-serprog — use Libreboot’s fork (multi-CS, tuned drive strength).
  • Raspberry Pi Pico 2 (RP2350) — serprog stability issues; use original Pico (RP2040) only.
  • flashrom — replaced by flashprog; use flashprog throughout.
  • Wrong Pico pinout (GP3↔GP5 swapped) — GP5=/CS, GP3=MOSI as shown in Part 2.
  • Skipping /HOLD (Pin 7) wiring — not wired in the tested setup and it worked. However the PCB pull-up may be marginal on some boards; if you see unexplained intermittent read failures, add a wire to 3.3V. It is always safe to do so.
  • Holding pin 7 of idle chip — it’s pin 1 (/CS) that matters for the idle chip. SDK08 test clips are ideal for this since you only need to probe a single pin, and you can solder the 47Ω resistor inline on the probe wire.
  • Single-chip assumption — the W541 has two chips; any guide that doesn’t address this produces unreliable results.
  • CONFIG_BOARD_LENOVO_THINKPAD_W541 vs T540P — the W541 uses the T540p board definition in upstream coreboot; check that the board Kconfig option matches what make menuconfig shows for your coreboot revision.

Appendix B — Blob Summary

BlobSourceDestination in coreboot tree
mrc.binT440p coreboot build (Haswell-universal)coreboot-blobs/mrc.bin
flashregion_2_intel_me.binfactory dump → ifdtool — passed raw; CONFIG_USE_ME_CLEANER=y cleans it at build timecoreboot-blobs/flashregion_2_intel_me.bin
flashregion_3_gbe.binfactory dump → ifdtoolcoreboot-blobs/flashregion_3_gbe.bin
flashregion_0_flashdescriptor.binfactory dump → ifdtoolcoreboot-blobs/flashregion_0_flashdescriptor.bin
data.vbtfactory dump → ifdtool → UEFIExtract → $VBT sectionsrc/mainboard/lenovo/t540p/data.vbt (inside coreboot source tree)
pci10de,0ff6.romfactory dump → ifdtool → UEFIExtract → Python PCI scan (K1100M)coreboot-blobs/pci10de,0ff6.rom
pci10de,11fc.romfactory dump → ifdtool → UEFIExtract → Python PCI scan (K2100M) — not tested end-to-endcoreboot-blobs/pci10de,11fc.rom

Appendix C — Programmer Comparison

See Tools — Programmer Comparison.



Credits and References

Coreboot .config — based on and modified from the ThinkPad W541 coreboot + Tianocore guide by Reventon1988 on Reddit:
https://www.reddit.com/r/coreboot/comments/12oeag8/thinkpad_w541_coreboottianocore_guide/

SPI wiring and programmer setup — Libreboot’s SPI flashing documentation is the authoritative reference for both the Pi 3/4 and Pico serprog wiring:
https://libreboot.org/docs/install/spi.html

Everything else was cobbled together from various forum posts, the coreboot wiki, repair.wiki, and whatever turned up in searches. The Python scripting for ROM extraction in particular was assembled with heavy assistance from Claude.

Validated hands-on, February 2026. K1100M confirmed working. K2100M untested.

Lenovo ThinkPad T580 — Coreboot Flash Guide

Flashing coreboot with EDK2/UEFI payload to the Lenovo ThinkPad T580, including Intel Boot Guard bypass via deguard, Intel ME neutering, and PXE/Network Boot support.

Author: chrisf4lc0n

Status: Tested and working — flashed via Raspberry Pi 4 using flashprog with linux_spi.

Upstream docs: doc.coreboot.org — T470s/T480/T480s/T580/X280


Overview

FeatureValue
CPUIntel 8th Gen Core (Kaby Lake Refresh — i5-8250U / i7-8550U / i7-8650U)
ChipsetIntel Sunrise Point LP (100-series PCH-LP)
DRAM2× SO-DIMM DDR4-2400, max 32 GB (no soldered RAM)
Intel MEv11.x (Skylake/Kaby Lake family) — deguard required
Boot GuardEnabled by Lenovo — must be bypassed with deguard
Flash chip1× Winbond W25Q128.V (16 MiB, SOIC-8)
Thunderbolt flash1× separate SOIC-8 (~1 MiB, near board edge)
SoC coreboot dirsoc/intel/skylake
Mainboard coreboot dirmainboard/lenovo/t580
PayloadMrChromebox EDK2 (UEFI) with PXE/Network Boot

Flash Layout (Vendor)

00000000:00000fff  flash descriptor (fd)
00001000:00002fff  gbe
00003000:006fffff  me
00700000:00ffffff  bios

Coreboot Status

Working: Intel UHD 620 (libgfxinit), internal display (eDP), Mini DisplayPort, Alpine Ridge Thunderbolt 3 (USB data + PCIe via lower USB-C), USB 3.0/2.0, USB-C (charge + DP alt), Gigabit Ethernet, WiFi, Bluetooth, WWAN slot, SATA, NVMe, M.2 B+M key NVMe in WWAN slot, keyboard, TrackPoint, TrackPad, touchscreen (if equipped), S3 suspend/resume, VT-x, VT-d, internal flashing (after initial flash with unlocked IFD).

Note: Thunderbolt support requires DRIVERS_INTEL_DTBT and the T580-specific GPIO changes. This was enabled for T580 in Gerrit change by Krzysztof Sokol, following the T480/T480s/X280 enablement in commit 1f12249ec0 by Matt DeVillier. If building from a coreboot revision before this patch was merged, apply the changes manually — see the Notes section.

Known issues: Some Fn+F{1-12} key mappings, headphone jack auto-detection (both outputs work with manual PulseAudio selection), Nvidia dGPU support is finicky on models that have it.


What You Need

For SPI programmer setup (Raspberry Pi or Pico), flashprog installation, GPIO wiring, and parts lists see Tools — SPI Programmers.

This guide was tested with a Raspberry Pi 4 using linux_spi. The T580 has a single 16 MiB flash chip — no dual-chip complications.

T580-Specific Notes

  • The main flash chip is a single SOIC-8 (W25Q128.V, 16 MiB) near the memory slots
  • There is a separate smaller SOIC-8 for the Thunderbolt controller (~1 MiB, near the board edge by the hinges) — physically narrower than the main chip, extra care seating the clip
  • If you accidentally clip the wrong chip, flashprog will report an unexpected flash size — just move the clip
  • Phillips screwdriver for bottom panel removal

For build dependencies and cross-compiler see Tools — Build Dependencies and Tools — Cross-Compiler.


Step 1 — Disassembly

  1. Power off completely. Disconnect the AC adapter.
  2. Remove all screws from the bottom panel and gently pry off the chassis.
  3. Disconnect the internal battery connector.
  4. Remove the CMOS battery (yellow coin cell).

⚠️ ALL power sources must be disconnected before clipping onto any chip. With power connected, the controllers may be active and interfere with reads/writes.

Refer to the Lenovo T580 Hardware Maintenance Manual for detailed disassembly photos.


Step 2 — Read the Vendor Firmware

Clip onto the main system flash chip (the larger 16 MiB SOIC-8). See Tools — Triple-Read Methodology for the general procedure.

flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=4000 -r stock_t580_1.bin
flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=4000 -r stock_t580_2.bin
flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=4000 -r stock_t580_3.bin

sha512sum stock_t580_*.bin
# All three checksums MUST match.

⚠️ Do NOT proceed if checksums don’t match. Reseat the clip, check wiring, and dump again. Try spispeed=512 if you get inconsistent reads. See Tools — /WP and /HOLD Troubleshooting if problems persist.

Keep these stock dumps safe. They are your recovery path if anything goes wrong.


Step 3 — Extract Blobs from Vendor Firmware

On your build machine (not the Pi), extract the flash regions from one of the verified dumps. Build ifdtool if you haven’t already — see Tools — ifdtool.

3.1 — Clone the Coreboot Repository

git clone https://review.coreboot.org/coreboot.git
cd coreboot
git submodule update --init --checkout

Updating an existing checkout:

cd coreboot
git checkout main
git pull
git submodule update --init --checkout

This tracks the rolling main branch. If you prefer a stable snapshot, checkout a release tag: git checkout 26.03 (check available tags with git tag -l | sort -V | tail -5).

3.2 — Extract Regions

mkdir -p binaries
cp /path/to/stock_t580_1.bin binaries/bios.bin

make -C util/ifdtool
util/ifdtool/ifdtool -x -p sklkbl binaries/bios.bin

This produces:

  • flashregion_0_flashdescriptor.bin → rename to binaries/ifd.bin
  • flashregion_1_bios.bin (not needed — coreboot replaces this)
  • flashregion_2_intel_me.bin (used as input for deguard — see below)
  • flashregion_3_gbe.bin → rename to binaries/gbe.bin
mv flashregion_0_flashdescriptor.bin binaries/ifd.bin
mv flashregion_3_gbe.bin binaries/gbe.bin

Step 4 — Deguard (Boot Guard Bypass)

The T580 has Intel Boot Guard enabled. The deguard utility exploits CVE-2017-5705 in Intel ME v11.x to disable Boot Guard by replacing fused FPF configuration with zeroed values.

4.1 — Clone deguard

cd ..
git clone "https://review.coreboot.org/deguard"

4.2 — Download and Extract the Donor ME Binary

The donor ME binary comes from a Dell firmware updater. Download from the Internet Archive mirror:

wget -O Inspiron_5468_1.3.0.exe \
  "https://web.archive.org/web/20241110222323/https://dl.dell.com/FOLDER04573471M/1/Inspiron_5468_1.3.0.exe"

Extract using Dell_PFS_Extract.py:

pip install --break-system-packages pefile
python3 Dell_PFS_Extract.py Inspiron_5468_1.3.0.exe

The file you need from the extracted output is 1 -- 3 Intel Management Engine (Non-VPro) Update v11.6.0.1126.bin:

  • File size: 2,031,616 bytes (0x1f0000)
  • SHA256: 912271bb3ff2cf0e2e27ccfb94337baaca027e6c90b4245f9807a592c8a652e1
sha256sum 'Inspiron_5468_1.3.0.exe_extracted/1 -- 3 Intel Management Engine (Non-VPro) Update v11.6.0.1126.bin'

Copy it to the binaries folder:

cp 'Inspiron_5468_1.3.0.exe_extracted/1 -- 3 Intel Management Engine (Non-VPro) Update v11.6.0.1126.bin' \
  coreboot/binaries/me_donor.bin

4.3 — Generate the Deguarded ME Image

cd deguard

./finalimage.py \
  --delta data/delta/thinkpad_t580 \
  --version 11.6.0.1126 \
  --pch LP \
  --sku 2M \
  --fake-fpfs data/fpfs/zero \
  --input ../coreboot/binaries/me_donor.bin \
  --output ../coreboot/binaries/me_deguarded.bin

Note: The --delta must match your exact model. If data/delta/thinkpad_t580 doesn’t exist in your checkout, generate it from your vendor dump:

./generatedelta.py --input ../coreboot/binaries/bios.bin --output data/delta/thinkpad_t580

Then discard /home/secureboot from the delta and re-run finalimage.py.

4.4 — Set the HAP Bit on the IFD

The deguarded ME requires the HAP bit enabled in the flash descriptor, or the system will not boot:

cd ../coreboot
util/ifdtool/ifdtool -p sklkbl -M 1 binaries/ifd.bin
mv binaries/ifd.bin binaries/ifd.bin.orig
mv binaries/ifd.bin.new binaries/ifd.bin

4.5 — (Optional) Expand BIOS Region

You can safely skip this step. The deguarded ME (2 MB) fits within the stock ME region (7 MB) — it just leaves unused space. The layout expansion reclaims that space for a larger CBFS, but can fail with “Regions would overlap” on some IFDs.

If you want to try it, save this as layout.txt:

00000000:00000fff fd
001f4000:00ffffff bios
00003000:001f3fff me
00001000:00002fff gbe

Then apply it:

util/ifdtool/ifdtool -p sklkbl -n layout.txt binaries/ifd.bin
mv binaries/ifd.bin.new binaries/ifd.bin

If this succeeds, add CONFIG_CBFS_SIZE=0xE0C000 to your defconfig. If it fails, carry on — the stock layout works fine.


Step 5 — Build Coreboot

Install build dependencies and cross-compiler if not done already — see Tools — Build Dependencies and Tools — Cross-Compiler.

5.1 — Configure with defconfig

Create defconfig in the coreboot root:

CONFIG_VENDOR_LENOVO=y
CONFIG_BOARD_LENOVO_T580=y
CONFIG_IFD_BIN_PATH="binaries/ifd.bin"
CONFIG_ME_BIN_PATH="binaries/me_deguarded.bin"
CONFIG_GBE_BIN_PATH="binaries/gbe.bin"
CONFIG_HAVE_IFD_BIN=y
CONFIG_HAVE_ME_BIN=y
CONFIG_HAVE_GBE_BIN=y
CONFIG_PAYLOAD_EDK2=y
CONFIG_EDK2_NETWORK_STACK=y
CONFIG_EDK2_IPXE_OPTION_NAME="Network Boot"
CONFIG_EDK2_BOOTSPLASH_FILE=""
CONFIG_EDK2_FOLLOW_BGRT_SPEC=y
CONFIG_MAINBOARD_SMBIOS_PRODUCT_NAME="ThinkPad T580"

Note: No need to specify CONFIG_EDK2_REPOSITORY or CONFIG_EDK2_TAG_OR_REV — coreboot’s built-in defaults point to the correct MrChromebox EDK2 branch. Overriding these can cause version mismatches.

Load it:

cp defconfig .config
make olddefconfig

5.2 — (Optional) Fine-Tune with menuconfig

make menuconfig

Key paths to verify:

Mainboard --->
  Mainboard vendor: Lenovo
  Mainboard model: ThinkPad T580
  ROM chip size: 16 MiB

Chipset --->
  [*] Enable Hyperthreading
  [*] Enable VMX

Payload --->
  Add a payload: edk2
  edk2 payload --->
    (leave Repository and Revision at defaults)
    [*] Enable Network in EDK2
    iPXE boot option name: Network Boot

EDK2 includes: UEFI Shell, memory test, NVRAM configuration, Secure Boot, and TPM 2.0 support. PXE/HTTP network boot must be explicitly enabled via CONFIG_EDK2_NETWORK_STACK=y and CONFIG_EDK2_IPXE_OPTION_NAME="Network Boot" — not on by default.

5.3 — Build

make -j$(nproc)

Verify:

ls -la build/coreboot.rom
# Should be exactly 16 MiB (16777216 bytes)

build/cbfstool build/coreboot.rom print

Step 6 — Set MAC Address

The GbE region contains the Ethernet MAC address. Since you extracted gbe.bin from your own T580’s vendor firmware, it already has the correct MAC — no action needed.

If you sourced the GbE blob elsewhere, use Libreboot’s nvmutil:

git clone https://codeberg.org/libreboot/lbmk.git
cd lbmk/util/nvmutil && make
./nvm /path/to/build/coreboot.rom setmac XX:XX:XX:XX:XX:XX

Step 7 — Flash Coreboot

Transfer the ROM to the Pi and flash the main chip (16 MiB):

scp build/coreboot.rom pi@raspi4flasher:~/

On the Pi, with the clip on the main system flash:

flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=4000 -w coreboot.rom

Wait for VERIFIED. — if verification fails, do NOT unclip, reflash. Try spispeed=2000 if it fails repeatedly.


Older TB3 firmware has a bug where debug logging fills the TB flash, eventually bricking the Thunderbolt controller. Lenovo released a firmware update. The Libreboot project also provides a patched tb.bin in their vendor files.

8.1 — Dump TB Chip

Clip onto the smaller TB chip (near the board edge). This chip is physically narrower — extra care seating all 8 pins. Start at lower speed:

flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=512 -r tbt_backup_1.bin
flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=512 -r tbt_backup_2.bin
flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=512 -r tbt_backup_3.bin
sha512sum tbt_backup_*.bin
# All three MUST match.

8.2 — Erase, Zero, Boot, Flash

# Erase the TB chip
flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=2000 -E

# Write zeroes
dd if=/dev/zero of=null.bin bs=1M count=1
flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=2000 -w null.bin

Unclip, reconnect power, boot the machine. Wait for the UEFI boot menu (be patient — the TB controller reinitialising on blank flash takes a while). Confirm the system boots, then shut down and disconnect all power.

Re-clip onto the TB chip and flash the updated firmware:

flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=2000 -w tb.bin

The intermediate boot with blank flash is critical — the TB controller needs to reinitialise before accepting new firmware.


Step 9 — First Boot and Verification

  1. Unclip the programmer.
  2. Reconnect the CMOS battery.
  3. Reconnect the internal battery.
  4. Replace the bottom panel.
  5. Connect AC power and boot.

You should see the coreboot/EDK2 splash and then the UEFI boot menu.

Verify from the OS

# Coreboot running
sudo dmidecode -t bios
# Should show "coreboot" as BIOS vendor

# ME disabled (HAP bit set)
sudo dmesg | grep -i "mei\|heci"
# ME in disabled/non-functional state

# PXE/Network Boot available
# Enter UEFI setup (Escape/F2 at boot) → Boot Manager
# "UEFI PXEv4" and/or "UEFI HTTPv4" entries should be visible

Subsequent Internal Flashing

See Tools — Internal Re-Flashing. For the T580, flash the BIOS region only:

sudo flashrom -p internal -w /path/to/build/coreboot.rom --ifd -i bios -N

This leaves the descriptor, ME, and GbE untouched.


Quick Reference — What Goes Where

FileSourceDestination
ifd.binifdtool -x of vendor dump + HAP bit setbinaries/ifd.bin
gbe.binifdtool -x of vendor dumpbinaries/gbe.bin
me_deguarded.bindeguard finalimage.py from Dell donor MEbinaries/me_deguarded.bin
FSP-M, FSP-Sgit submodule (auto)3rdparty/fsp/KabylakeFspBinPkg/
Microcodegit submodule (auto)3rdparty/intel-microcode/

Full .config Reference

Click to expand full .config for T580 + EDK2 + Network Boot
#
# coreboot — ThinkPad T580 with EDK2/UEFI + PXE Network Boot
#

# Mainboard
CONFIG_VENDOR_LENOVO=y
CONFIG_BOARD_LENOVO_T580=y
CONFIG_MAINBOARD_SMBIOS_PRODUCT_NAME="ThinkPad T580"

# Blob paths
CONFIG_IFD_BIN_PATH="binaries/ifd.bin"
CONFIG_ME_BIN_PATH="binaries/me_deguarded.bin"
CONFIG_GBE_BIN_PATH="binaries/gbe.bin"
CONFIG_HAVE_IFD_BIN=y
CONFIG_HAVE_ME_BIN=y
CONFIG_HAVE_GBE_BIN=y

# CBFS — only add this if you successfully expanded the IFD layout (Step 4.5):
# CONFIG_CBFS_SIZE=0xE0C000

# Chipset
CONFIG_HAVE_INTEL_FIRMWARE=y

# CPU
CONFIG_SKYLAKE_SOC_MODEL_KABYLAKE_REFRESH=y
CONFIG_HYPERTHREADING=y
CONFIG_INTEL_VMX=y

# Graphics
CONFIG_MAINBOARD_HAS_LIBGFXINIT=y
CONFIG_DRIVERS_INTEL_GMA_HAVE_VBT=y

# Payload — MrChromebox EDK2 with Network Boot
CONFIG_PAYLOAD_EDK2=y
CONFIG_EDK2_NETWORK_STACK=y
CONFIG_EDK2_IPXE_OPTION_NAME="Network Boot"
CONFIG_EDK2_BOOTSPLASH_FILE=""
CONFIG_EDK2_FOLLOW_BGRT_SPEC=y

# TPM
CONFIG_TPM2=y

Note: Some Kconfig symbols are auto-selected by the board config. The EDK2 repository and branch are handled by coreboot’s built-in defaults — do not override unless you have a specific reason. Run cp defconfig .config && make olddefconfig followed by make menuconfig to see the full resolved config.


Troubleshooting

Flash Chip Not Detected

  1. Check clip contact — ensure solid seating on all 8 pins.
  2. Verify pin 1 alignment (dot on chip → pin 1 on clip).
  3. Try different /WP and /HOLD states — see Tools — /WP and /HOLD Troubleshooting.
  4. Try lower SPI speed: spispeed=512.

System Won’t Boot After Flash

  1. Reconnect the external programmer.
  2. Flash back the original backup: flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=4000 -w stock_t580_1.bin
  3. Verify deguard ME was generated correctly — wrong delta or missing HAP bit will brick.
  4. Disconnect all power for 60 seconds (including CMOS) then retry.

Black Screen / No Display

  1. Ensure libgfxinit is enabled in coreboot config.
  2. Try an external display via Mini DisplayPort.
  3. If you have a dGPU model, check the known issues.

PXE Boot Not Showing

  1. Verify CONFIG_EDK2_NETWORK_STACK=y and CONFIG_EDK2_IPXE_OPTION_NAME="Network Boot" were set before build.
  2. Check UEFI Boot Manager (Escape at boot) for UEFI PXEv4 / UEFI HTTPv4 entries.
  3. Ensure Ethernet cable is connected — PXE entries only appear with link detected.

Headphone Jack / Audio Issues

Both headphone jack and speakers work, but auto-detection doesn’t switch automatically. Use PulseAudio/PipeWire to manually select the output device.


Recovery

External flash recovery is always possible:

  1. Reconnect the SOIC-8 clip and external programmer.
  2. Flash the original vendor backup: flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=4000 -w stock_t580_1.bin
  3. Once coreboot is installed, internal recovery via flashrom -p internal also works for non-catastrophic issues.

Notes

  • No CPU whitelist under coreboot — any Kaby Lake Refresh CPU works (i5-8250U, i5-8350U, i7-8550U, i7-8650U).
  • me_cleaner warning: On some Skylake/KBL ThinkPads (notably T470s), truncating the ME with me_cleaner can disable PCIe devices (NVMe, WLAN, WWAN). The deguard approach already disables ME post-boot without truncation — additional me_cleaner is optional and should be tested carefully.
  • Thunderbolt controller is on the Alpine Ridge chip with its own separate SPI flash — independent of the main coreboot flash.
  • EC firmware is separate from the SPI flash and is not affected by coreboot flashing. For EC UART debugging, EC firmware version 1.22 (N24HT37W) is needed — corresponding to BIOS versions 1.39 to 1.54. Install via Lenovo’s BIOS updater before flashing coreboot.
  • HyperThreading is enabled in this config. Libreboot disables it by default for Spectre/Meltdown mitigation. Set CONFIG_HYPERTHREADING=n if you want the same.

References


Changelog

  • 2026-04-03: Added Thunderbolt 3 support — T580 TBT enablement patch submitted to coreboot Gerrit. GPIO and Kconfig changes mirroring T480/T480s/X280.
  • 2026-04-02: Initial guide — coreboot with EDK2/MrChromebox, deguard, PXE/Network Boot, TB firmware update.

Lenovo ThinkCentre M920q — Coreboot Flash Guide

Tested and verified guide for flashing coreboot on the Lenovo ThinkCentre M920q. Should also work on the M720q, M920x, and ThinkStation P330 Tiny (same board, untested).

Overview

FeatureValue
CPUIntel Core 8th/9th Gen (Coffee Lake / Coffee Lake Refresh)
ChipsetIntel Q370 (Cannon Lake PCH-H)
DRAM2× SO-DIMM DDR4-2400/2666, max 64 GB
ME versionv12
Super I/ONCT6686D-L (Nuvoton)
TPMInfineon SLB 9670VQ2.0 (TPM 2.0)
Boot GuardNot fused — direct external flash works
SoC coreboot directorysoc/intel/cannonlake

What Works

  • USB 3.0 / 2.0 (front and rear)
  • USB-C (charging and data)
  • Gigabit Ethernet
  • SATA and NVMe
  • HDMI
  • WiFi slot
  • TPM 2.0
  • Internal speaker
  • COM1 serial (via daughter board)
  • PCIe x8 riser slot (tested with BA7H70 Rev 1.2 riser + Intel X540-T2 10GbE)
  • S3 suspend/resume
  • PXE boot (via edk2 built-in UEFI network stack)

Known Issues

  • Non-T (65W+) CPUs do not boot under coreboot on M920q — The M920q VRM lacks a power phase (PU404) present on the M920x/P330 Tiny. Non-T CPUs (i7-8700, i9-9900, etc.) fail during early FSP init due to insufficient VRM current delivery. The stock Lenovo BIOS has vendor-specific workarounds; coreboot uses Intel reference FSP values which expect spec-compliant VRM. Use T-series (35W) CPUs only (i3-8100T, i5-8500T, i7-8700T, i9-9900T, etc.). See the M920x/P330 section below for 65W support
  • Front audio jacks do not work (upstream coreboot bug)
  • DIMM2 slot does not workcoreboot bug #592. Only DIMM1 is usable. Use a single larger SO-DIMM if more RAM is needed
  • ME Communication Controller (PCI 16.0) remains visible with HAP bit set — this is normal hardware enumeration, the ME firmware is disabled

Board Variants — M720q / M920q / M920x / P330 Tiny

The M920q, M920x, M720q, and ThinkStation P330 Tiny all share the same base motherboard (EQ370 NM-B551 / IQ3X0IL) with different SMD component populations. The same coreboot ROM should work on all four variants. Only the M920q has been tested — the others should work in principle but are unconfirmed.

The M920x and P330 Tiny have an additional VRM phase (PU404) and associated passives that allow 65W non-T CPU operation. The M720q has the same VRM limitation as the M920q — T-series CPUs only.

VariantVRM65W CPUCoreboot Tested
M920qStandardNo — T-series onlyYes
M720qStandardNo — T-series onlyNo (should work)
M920xExtra phase (PU404)YesNo (should work)
P330 TinyExtra phase (PU404)YesNo (should work)

Flash Chips

The M920q has two SOIC-8 SPI flash chips, both 3.3V, soldered (not socketed):

ChipBoard LabelModelSizeflashprog -c stringPull-up Notes
BIOS1U31W25Q128JV16 MiBW25Q128.VBoard has on-board pull-ups — do not hold /WP or /HOLD high. If reads/writes fail, see Tools — /WP and /HOLD Troubleshooting
BIOS2U32W25Q64JV8 MiBW25Q64JV-.QNo on-board pull-ups — /WP (pin 3) and /HOLD (pin 7) must be held HIGH manually

Total ROM size: 24 MiB (16 + 8).


Prerequisites

Hardware

  • Raspberry Pi (3/4/5) or Raspberry Pi Pico (RP2040) — see Tools — SPI Programmers for setup, wiring, and flashprog installation
  • SOIC-8 test clip (Pomona 5250 or equivalent)
  • Dupont jumper wires (include a splitter from any RPi 3.3V pin to Pomona clip pins 3 and 7 for BIOS2 /WP and /HOLD pull-ups)

Important: Disconnect the M920q power adapter before attaching the programmer. Do not supply mains power while flashing.


Step 1: Read Original Firmware

Triple-read each chip — see Tools — Triple-Read Methodology. All three SHA256 checksums must match before proceeding. If they don’t, reseat the clip and read again.

BIOS1 (16 MiB)

Do not hold /WP or /HOLD high — the board has on-board pull-ups for this chip. If reads fail, see Tools — /WP and /HOLD Troubleshooting.

sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 \
  -c "W25Q128.V" -r m920q_bios1_dump1.bin
sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 \
  -c "W25Q128.V" -r m920q_bios1_dump2.bin
sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 \
  -c "W25Q128.V" -r m920q_bios1_dump3.bin

sha256sum m920q_bios1_dump*.bin

BIOS2 (8 MiB)

Move clip to BIOS2. Hold /WP (pin 3) and /HOLD (pin 7) HIGH (split a Dupont wire from any RPi 3.3V pin to both pins on the Pomona clip).

sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 \
  -c "W25Q64JV-.Q" -r m920q_bios2_dump1.bin
sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 \
  -c "W25Q64JV-.Q" -r m920q_bios2_dump2.bin
sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 \
  -c "W25Q64JV-.Q" -r m920q_bios2_dump3.bin

sha256sum m920q_bios2_dump*.bin

Troubleshooting: If BIOS2 is not detected, check that /WP and /HOLD are held HIGH — unlike BIOS1, BIOS2 has no on-board pull-ups. You may also try lowering spispeed to 512 if detection is unreliable. For general /WP and /HOLD troubleshooting, see Tools — /WP and /HOLD Troubleshooting.

Combine and Back Up

cat m920q_bios1_dump1.bin m920q_bios2_dump1.bin > m920q_original_24mb.bin

ls -la m920q_original_24mb.bin
# Must be exactly 25165824 bytes (24 MiB)

Back up all dump files to a safe off-machine location. These are your recovery lifeline.


Step 2: Extract Regions and Set HAP Bit

Perform these steps on your build machine. Build ifdtool if you haven’t already — see Tools — ifdtool.

Extract Regions

util/ifdtool/ifdtool -p cnl -x /path/to/m920q_original_24mb.bin

The -p cnl flag specifies Cannon Lake platform. This produces:

Output FileContents
flashregion_0_flashdescriptor.binIntel Flash Descriptor
flashregion_1_bios.binBIOS region (coreboot replaces this — not needed)
flashregion_2_intel_me.binIntel CSME firmware
flashregion_3_gbe.binGigabit Ethernet NVM (contains MAC address)

Set HAP Bit on Descriptor

The HAP (High Assurance Platform) bit tells the ME to enter a graceful disabled state after early initialisation.

Do NOT use me_cleaner on this platform. The M920q uses ME v12. Stripping ME modules with me_cleaner puts ME v12 into an error state and can prevent boot. The coreboot project recommends using the HAP bit rather than me_cleaner.

util/ifdtool/ifdtool -p cnl -M 1 flashregion_0_flashdescriptor.bin
mv flashregion_0_flashdescriptor.bin flashregion_0_flashdescriptor.bin.orig
mv flashregion_0_flashdescriptor.bin.new flashregion_0_flashdescriptor.bin

Place Blobs

mkdir -p 3rdparty/blobs/mainboard/lenovo/m720q_m920q

cp flashregion_0_flashdescriptor.bin \
   3rdparty/blobs/mainboard/lenovo/m720q_m920q/descriptor.bin

cp flashregion_2_intel_me.bin \
   3rdparty/blobs/mainboard/lenovo/m720q_m920q/me.bin

cp flashregion_3_gbe.bin \
   3rdparty/blobs/mainboard/lenovo/m720q_m920q/gbe.bin

The ME binary must be the original unmodified version extracted from your dump. The VBT (data.vbt) is already bundled in the coreboot source tree at src/mainboard/lenovo/m720q_m920q/data.vbt. The FSP binaries are fetched automatically via git submodules (3rdparty/fsp/CoffeeLakeFspBinPkg/).


Step 3: Configure and Build

Install build dependencies and cross-compiler if not done already — see Tools — Build Dependencies and Tools — Cross-Compiler.

Using the Provided defconfig

Save the following as defconfig in your coreboot root directory. It configures:

  • edk2 payload (MrChromebox fork) with PXE, UEFI Shell, TPM 2.0, Secure Boot
  • SMMStore v2 for persistent UEFI NVRAM
  • libgfxinit for native graphics initialisation
  • Serial console on COM1 at 115200 baud
CONFIG_VENDOR_LENOVO=y
CONFIG_BOARD_LENOVO_M920Q=y
CONFIG_IFD_BIN_PATH="3rdparty/blobs/mainboard/lenovo/m720q_m920q/descriptor.bin"
CONFIG_ME_BIN_PATH="3rdparty/blobs/mainboard/lenovo/m720q_m920q/me.bin"
CONFIG_GBE_BIN_PATH="3rdparty/blobs/mainboard/lenovo/m720q_m920q/gbe.bin"
# CONFIG_USE_ME_CLEANER is not set
CONFIG_PAYLOAD_EDK2=y
CONFIG_EDK2_REPO_MRCHROMEBOX=y
CONFIG_EDK2_UEFIPAYLOAD=y
CONFIG_SMMSTORE=y
CONFIG_SMMSTORE_V2=y
# iPXE network boot — adds "Network Boot" to edk2 boot menu
CONFIG_EDK2_ENABLE_IPXE=y
CONFIG_EDK2_IPXE_OPTION_NAME="Network Boot"
# CONFIG_RUN_FSP_GOP is not set
CONFIG_CONSOLE_SERIAL=y
CONFIG_TTYS0_BAUD=115200
CONFIG_DEFAULT_CONSOLE_LOGLEVEL=7

Then build:

make distclean
cp defconfig .config
make olddefconfig
make -j$(nproc)

Verify Output

ls -la build/coreboot.rom
# Must be 25165824 bytes (24 MiB)

./build/cbfstool build/coreboot.rom print
# Should list: bootblock, romstage, ramstage, dsdt.aml, payload (edk2),
# cpu_microcode_blob.bin, vbt.bin, fspm.bin, fsps.bin, etc.

Step 4: Split and Flash

Split the ROM

The 24 MiB ROM must be split to match the two physical flash chips:

dd if=build/coreboot.rom of=build/coreboot_bios1.rom bs=1M count=16
dd if=build/coreboot.rom of=build/coreboot_bios2.rom bs=8M skip=2

# Sanity check — recombine and compare
cat build/coreboot_bios1.rom build/coreboot_bios2.rom > build/coreboot_recombined.rom
sha256sum build/coreboot.rom build/coreboot_recombined.rom
# Must match

Flash BIOS1 (16 MiB)

Do not hold /WP or /HOLD high.

sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 \
  -c "W25Q128.V" -w coreboot_bios1.rom

# Verify
sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 \
  -c "W25Q128.V" -r verify_bios1.bin
sha256sum coreboot_bios1.rom verify_bios1.bin

Flash BIOS2 (8 MiB)

Move clip to BIOS2. Hold /WP (pin 3) and /HOLD (pin 7) HIGH (same 3.3V splitter as reading).

sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 \
  -c "W25Q64JV-.Q" -w coreboot_bios2.rom

# Verify
sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 \
  -c "W25Q64JV-.Q" -r verify_bios2.bin
sha256sum coreboot_bios2.rom verify_bios2.bin

Step 5: First Boot

  1. Install RAM in DIMM1 only — DIMM2 does not work under coreboot (bug #592)
  2. Connect a display via HDMI (try DisplayPort if no output on HDMI)
  3. Power on and wait up to 2 minutes — the first boot performs memory training with no display output. Subsequent boots are fast (MRC cache)
  4. The edk2 splash screen (coreboot hare) should appear. Press Esc to enter the boot menu

If It Doesn’t Boot

  • Verify RAM is in DIMM1 only
  • Try both display outputs (HDMI and DisplayPort)
  • Wait the full 2 minutes — memory training can be slow
  • If still no output, flash your original backup dumps to restore stock firmware (see Recovery below) and review your build configuration

Step 6: Post-Boot Verification

ME Status

sudo dmesg | grep -i mei

With HAP bit set, you should see only mei_hdcp binding for HDCP content protection via the i915 graphics driver:

mei_hdcp 0000:00:16.0-b638ab7e-94e2-4ea2-a552-d1c54b627f04: bound 0000:00:02.0 (ops i915 hdcp_ops [i915])

There should be no mei_me driver initialising and no AMT-related messages. The HECI controller at 00:16.0 will still appear in lspci as Communication controller: Intel Corporation Cannon Lake PCH HECI Controller — this is normal hardware enumeration. HAP prevents the ME firmware from actively running while the PCH hardware remains visible on the PCI bus.


Recovery

If the machine doesn’t boot, flash your original backup dumps externally:

# BIOS1 (do not hold /WP or /HOLD high)
sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 \
  -c "W25Q128.V" -w m920q_bios1_dump1.bin

# BIOS2 (hold /WP and /HOLD HIGH)
sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=1000 \
  -c "W25Q64JV-.Q" -w m920q_bios2_dump1.bin

Internal Flashing (Subsequent Updates)

See Tools — Internal Re-Flashing. For the M920q, use the BIOS-region-only method:

sudo flashrom -p internal -N -w coreboot.rom --ifd -i bios

This writes only the BIOS region, leaving the descriptor, ME, and GbE regions untouched. No need to split the ROM for internal flashing.



Quick Reference

ItemDetails
ROM size24 MiB (two chips: 16 + 8)
BIOS1 splitdd if=coreboot.rom of=bios1.rom bs=1M count=16
BIOS2 splitdd if=coreboot.rom of=bios2.rom bs=8M skip=2
BIOS2 quirk/WP + /HOLD must be held HIGH externally
HAP bitifdtool -p cnl -M 1 descriptor.bin
DIMMSlot 1 only (bug #592)
CPUT-series (35W) only on M920q — 65W CPUs fail under coreboot (VRM limitation)
65W CPU supportUse M920x or P330 Tiny (extra VRM phase — untested with coreboot)
iPXE / PXE bootCONFIG_EDK2_ENABLE_IPXE=y in defconfig
Boot GuardNot fused — no deguard needed
ME disableHAP bit only — do NOT use me_cleaner (ME v12)
Same board familyM720q, M920q (tested), M920x, P330 Tiny — all EQ370 NM-B551

References

License

This guide is released under CC BY-SA 4.0.

Tools & Common Procedures

Shared tools, wiring, and procedures used across all coreboot guides on this site. Individual guides reference sections here rather than duplicating them.


SPI Programmers

Do not use a CH341A unless you have confirmed it uses 3.3V logic on data lines. Many clones output 5V on MOSI/MISO/CLK which can damage the SPI flash and the PCH.

What You Need

ItemNotes
SOIC-8 test clipPomona 5250 is best; generic clips work
Female–female Dupont wires10–15 wires
IPA (isopropyl alcohol)Clean chip legs before clipping
Raspberry Pi 3/4/5 or Raspberry Pi Pico (RP2040)See below for differences

flashprog vs flashrom: Use flashprog (Libreboot’s fork of flashrom). It is more robust in practice — fewer detection failures, better chip support. Commands are identical; just replace flashrom with flashprog in any guide. All examples on this site use flashprog.

Choosing a Programmer

Raspberry Pi 3/4/5 — the more convenient option. Runs headless with SSH, so you can flash from any terminal on your network while the Pi sits next to the open machine with all wires connected. No screen needed. Has two 3.3V pins (physical pins 1 and 17) making multi-wire distribution easy.

Raspberry Pi Pico (RP2040) — cheapest option but requires a host computer physically nearby running flashprog over USB serial. Only one 3.3V output pin (pin 36) which needs multi-way splits or a breadboard. Do not use Pico 2 (RP2350) — serprog stability issues.

Method A — Raspberry Pi 3B / 3B+ / 4

Parts:

ItemNotes
Raspberry Pi 3B, 3B+, or 4Raspberry Pi OS Lite is fine
MicroSD card (8 GB+)With Raspberry Pi OS flashed
5V PSUStable power matters
Ethernet or WiFiSSH in headless

Enable SPI on the Pi

sudo raspi-config

Navigate to Interface Options → SPI and enable it. Reboot.

ls /dev/spidev*
# Should show: /dev/spidev0.0  /dev/spidev0.1

Build flashprog on the Pi

sudo apt install git build-essential libpci-dev libusb-1.0-0-dev libftdi1-dev \
  libgpiod-dev meson ninja-build pkg-config

git clone https://review.sourcearcade.org/flashprog.git
cd flashprog
meson setup builddir
ninja -C builddir
sudo ninja -C builddir install

Verify:

sudo flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=512
# Expected: "No EEPROM/flash device found." — correct, nothing connected

Pi GPIO → SOIC-8 Wiring

Pi GPIO header:                    SOIC-8 chip:

Pin 24 — GPIO8/SPI0_CE0 ────────── Pin 1  (/CS)
Pin 21 — GPIO9/SPI0_MISO ───────── Pin 2  (MISO/DO)
Pin 17 — 3.3V ──────────┬────────── Pin 8  (VCC)
                        └──────────── (board-specific connections)
Pin 25 — GND ────────────────────── Pin 4  (GND)
Pin 19 — GPIO10/SPI0_MOSI ──────── Pin 5  (MOSI/DI)
Pin 23 — GPIO11/SPI0_SCLK ──────── Pin 6  (CLK)
Pi Physical PinPi SignalSOIC-8 PinSignalRequired?
24GPIO8 / CE01/CSYes
21GPIO9 / MISO2MISO (DO)Yes
173.3V3/WPNo by default — see /WP and /HOLD Troubleshooting
25GND4GNDYes
19GPIO10 / MOSI5MOSI (DI)Yes
23GPIO11 / SCLK6CLKYes
7/HOLDNo by default — see /WP and /HOLD Troubleshooting
173.3V8VCCYes

Verify your wiring against Libreboot’s SPI guide before first use — it covers the Pi 3/4 GPIO pinout in detail and is the authoritative reference: https://libreboot.org/docs/install/spi.html

Pi flashprog command syntax

-p linux_spi:dev=/dev/spidev0.0,spispeed=1000

Method B — Raspberry Pi Pico (RP2040)

Flash serprog Firmware onto the Pico

Use Libreboot’s pico-serprog — it has multi-CS handling and tuned 12 mA drive strength.

git clone https://codeberg.org/libreboot/lbmk
cd lbmk
./mk dependencies debian
./mk -b pico-serprog
# Output: bin/serprog_pico/serprog_pico.uf2

Flash onto the Pico:

  1. Hold BOOTSEL, plug Pico into USB, release BOOTSEL — mounts as RPI-RP2
  2. cp bin/serprog_pico/serprog_pico.uf2 /media/$USER/RPI-RP2/
  3. Pico reboots. Verify: sudo dmesg | grep ttyACMttyACM0: USB ACM device
sudo usermod -aG dialout $USER   # avoid sudo on every flashprog call (re-login after)

Install flashprog on the host machine:

sudo apt install flashprog
# If not available: sudo apt install flashrom  (commands are identical)

Pico GPIO → SOIC-8 Wiring

Critical: GP3 = MOSI and GP5 = /CS. These are commonly documented in reverse. Using the wrong mapping gives garbage reads.

Pico (corner near USB):

Pin 4  — GP2  — SCK  ──────────────── SOIC-8 Pin 6
Pin 5  — GP3  — MOSI ──────────────── SOIC-8 Pin 5
Pin 6  — GP4  — MISO ──────────────── SOIC-8 Pin 2
Pin 7  — GP5  — /CS  ──────────────── SOIC-8 Pin 1
Pin 36 — 3V3(OUT) ──┬──────────────── SOIC-8 Pin 8 (VCC)
                    └───────────────── (board-specific connections)
Pin 38 — GND ──────────────────────── SOIC-8 Pin 4
SOIC-8 PinSignalPico Physical PinPico GPIORequired?
1/CSPin 7GP5Yes
2MISOPin 6GP4Yes
3/WPNo by default — see /WP and /HOLD Troubleshooting
4GNDPin 38GNDYes
5MOSIPin 5GP3Yes
6SCKPin 4GP2Yes
7/HOLDNo by default — see /WP and /HOLD Troubleshooting
8VCCPin 363V3(OUT)Yes

Pin 37 (3V3_EN) is NOT a power output — do not use it.

Pico flashprog command syntax

-p serprog:dev=/dev/ttyACM0,spispeed=16M

SOIC-8 Pinout Reference

      ┌───────┐
 /CS 1│●      │8 VCC
  DO 2│       │7 /HOLD
 /WP 3│       │6 CLK
 GND 4│       │5 DI
      └───────┘

Pin 1 = dot on IC package. Pin numbering goes anti-clockwise.


Triple-Read Methodology

Always take three reads of every chip. All three SHA256 checksums must match before proceeding. If they don’t, reseat the clip, clean chip legs with IPA, and read again.

# Example (adjust programmer, chip string, and filename per your board)
sudo flashprog -p <programmer> -c "<chip>" -r dump1.bin
sudo flashprog -p <programmer> -c "<chip>" -r dump2.bin
sudo flashprog -p <programmer> -c "<chip>" -r dump3.bin

sha256sum dump*.bin
# All three MUST match

Troubleshooting inconsistent reads:

  • Clean chip legs with IPA
  • Try lower speed: spispeed=512 (Pi) or spispeed=4M (Pico)
  • Specify chip explicitly: flashprog ... -c "<chip_string>"
  • On dual-chip boards: confirm idle chip’s /CS is held high
  • Try different /WP and /HOLD states — see below

/WP and /HOLD Troubleshooting

Rule of thumb: do not hold /WP (pin 3) or /HOLD (pin 7) high by default. Many boards have on-board pull-ups on these lines, and actively driving them can sometimes interfere. Start with only the six core connections (VCC, GND, /CS, MOSI, MISO, CLK) and leave /WP and /HOLD floating.

If reads are inconsistent, writes fail, or the chip is not detected, work through these /WP and /HOLD combinations until one works:

/WP (pin 3)/HOLD (pin 7)Try when
floatingfloatingStart here — default, no extra wires
HIGH (3.3V)floatingWrites fail or chip reports write-protected
floatingHIGH (3.3V)Reads corrupt mid-transfer, chip pauses randomly
HIGH (3.3V)HIGH (3.3V)Both issues, or nothing else works

Both /WP and /HOLD are active-low signals. Driving them high is always electrically safe — it cannot damage the chip. Driving them high simply ensures write-protect is disabled (/WP) and the chip never pauses (/HOLD).

Some boards pull these lines high through on-board resistors and do not need external wiring. Others leave them floating or weakly pulled, and the state becomes marginal — especially with longer clip wires acting as antennas. If you see intermittent failures that cleaning and speed changes don’t fix, /WP and /HOLD state is the next thing to try.

Per-board notes: Individual guides note which /WP and /HOLD wiring is confirmed working for that board. The combinations above are a general fallback when the default doesn’t work or when working with a board not covered by these guides.


ifdtool — Extract Flash Regions

Build ifdtool from the coreboot tree:

git clone https://review.coreboot.org/coreboot.git
cd coreboot
git submodule update --init --checkout

cd util/ifdtool && make && cd ../..

Extract regions from a full flash dump:

util/ifdtool/ifdtool -x <dump.bin>              # Haswell / Broadwell
util/ifdtool/ifdtool -p cnl -x <dump.bin>        # Coffee Lake (Cannon Lake PCH)

Produces:

Output FileContents
flashregion_0_flashdescriptor.binIntel Flash Descriptor
flashregion_1_bios.binBIOS region
flashregion_2_intel_me.binIntel CSME firmware
flashregion_3_gbe.binGigabit Ethernet NVM (contains MAC address)

Build Dependencies (Debian / Ubuntu)

sudo apt install -y \
  build-essential gnat flex bison libncurses-dev wget zlib1g-dev \
  git python3 libelf-dev nasm pkg-config libssl-dev m4 iasl acpica-tools curl

The W541 guide additionally uses: imagemagick gcc-multilib libc6-dev-i386 unifont


Cross-Compiler Toolchain

Build once per coreboot checkout:

cd coreboot
make crossgcc-i386 CPUS=$(nproc)
# Takes 30–90 minutes on first run

Internal Re-Flashing (Subsequent Updates)

Once running coreboot, subsequent updates can be done internally without an external programmer.

Single-chip boards (e.g. T580):

sudo flashrom -p internal -w coreboot.rom

Multi-chip boards writing BIOS region only (e.g. M920q):

sudo flashrom -p internal -N -w coreboot.rom --ifd -i bios

Full ROM write (e.g. W541):

sudo flashprog -p internal -w build/coreboot.rom

If your kernel restricts iomem access:

# Add to GRUB_CMDLINE_LINUX in /etc/default/grub:
GRUB_CMDLINE_LINUX="iomem=relaxed"
sudo update-grub
# Reboot, then retry

Internal flashing uses the full combined ROM — no splitting needed.


Programmer Comparison

Raspberry Pi 3/4Raspberry Pi Pico
Read speed~500 KB/s at 1 MHz~2 MB/s at 16 MHz
3.3V pins2 (pins 1 and 17) — easy multi-wire crimps1 (pin 36) — needs multi-way split or breadboard
flashprog interfacelinux_spi (native kernel driver)serprog (USB serial)
SetupFull OS, raspi-config, build flashprogFlash UF2, done
Remote flashingYes — headless SSH from any terminalNo — host computer must be physically nearby
VerdictMore convenient overallCheapest, fastest reads

References