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
| Machine | Platform | Payload | Key Feature |
|---|---|---|---|
| W541 | Haswell | SeaBIOS + iPXE | Working Nvidia K1100M dGPU |
| T580 | Kaby Lake R | EDK II + iPXE | deguard + Thunderbolt |
| M920q | Coffee Lake | EDK II | deguard, 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 —libgfxinitinitialises 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
| Item | Notes |
|---|---|
| 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 line | 47Ω 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
| Chip | Location | Size | Role |
|---|---|---|---|
| Chip 1 | Left, near white ribbon connector | 8 MiB | chip1.bin / bottom.rom |
| Chip 2 | Right, closer to board edge | 4 MiB | chip2.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:
- Remove battery and AC power. Hold power button 10 seconds to discharge caps.
- Remove bottom cover screws and pop the cover.
- Disconnect all ribbon cables, antenna wires, and connectors as you work through the disassembly.
- Lift the motherboard out. The two SOIC-8 flash chips are on the underside, near the LCD cable area — small, close together.
- 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) orspispeed=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
| GPU | PCI ID | little-endian in ROM | Status |
|---|---|---|---|
| Quadro K1100M | 10DE:0FF6 | de 10 f6 0f | ✓ Tested and confirmed working |
| Quadro K2100M | 10DE:11FC | de 10 fc 11 | Should 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:0x0406ROM.
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
_PATHand_FILEentries 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 fromsrc/mainboard/lenovo/t540p/data.vbtinside 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:
- Disconnect the Pi/Pico from USB (remove all programmer wiring from the flash chips first).
- Connect AC power.
- Press the power button — you should see a coreboot/SeaBIOS/GRUB screen within 30 seconds.
- Both GPUs should initialise: coreboot logs at the top of the screen will show iGPU init and the Nvidia VGA ROM executing.
- 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=yin the.confighandles ME neutering at build time; just pass the rawflashregion_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_W541vsT540P— the W541 uses the T540p board definition in upstream coreboot; check that the board Kconfig option matches whatmake menuconfigshows for your coreboot revision.
Appendix B — Blob Summary
| Blob | Source | Destination in coreboot tree |
|---|---|---|
mrc.bin | T440p coreboot build (Haswell-universal) | coreboot-blobs/mrc.bin |
flashregion_2_intel_me.bin | factory dump → ifdtool — passed raw; CONFIG_USE_ME_CLEANER=y cleans it at build time | coreboot-blobs/flashregion_2_intel_me.bin |
flashregion_3_gbe.bin | factory dump → ifdtool | coreboot-blobs/flashregion_3_gbe.bin |
flashregion_0_flashdescriptor.bin | factory dump → ifdtool | coreboot-blobs/flashregion_0_flashdescriptor.bin |
data.vbt | factory dump → ifdtool → UEFIExtract → $VBT section | src/mainboard/lenovo/t540p/data.vbt (inside coreboot source tree) |
pci10de,0ff6.rom | factory dump → ifdtool → UEFIExtract → Python PCI scan (K1100M) | coreboot-blobs/pci10de,0ff6.rom |
pci10de,11fc.rom | factory dump → ifdtool → UEFIExtract → Python PCI scan (K2100M) — not tested end-to-end | coreboot-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
| Feature | Value |
|---|---|
| CPU | Intel 8th Gen Core (Kaby Lake Refresh — i5-8250U / i7-8550U / i7-8650U) |
| Chipset | Intel Sunrise Point LP (100-series PCH-LP) |
| DRAM | 2× SO-DIMM DDR4-2400, max 32 GB (no soldered RAM) |
| Intel ME | v11.x (Skylake/Kaby Lake family) — deguard required |
| Boot Guard | Enabled by Lenovo — must be bypassed with deguard |
| Flash chip | 1× Winbond W25Q128.V (16 MiB, SOIC-8) |
| Thunderbolt flash | 1× separate SOIC-8 (~1 MiB, near board edge) |
| SoC coreboot dir | soc/intel/skylake |
| Mainboard coreboot dir | mainboard/lenovo/t580 |
| Payload | MrChromebox 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_DTBTand the T580-specific GPIO changes. This was enabled for T580 in Gerrit change by Krzysztof Sokol, following the T480/T480s/X280 enablement in commit1f12249ec0by 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
- Power off completely. Disconnect the AC adapter.
- Remove all screws from the bottom panel and gently pry off the chassis.
- Disconnect the internal battery connector.
- 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=512if 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 --checkoutThis tracks the rolling
mainbranch. If you prefer a stable snapshot, checkout a release tag:git checkout 26.03(check available tags withgit 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 tobinaries/ifd.binflashregion_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 tobinaries/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
--deltamust match your exact model. Ifdata/delta/thinkpad_t580doesn’t exist in your checkout, generate it from your vendor dump:./generatedelta.py --input ../coreboot/binaries/bios.bin --output data/delta/thinkpad_t580Then discard
/home/securebootfrom the delta and re-runfinalimage.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_REPOSITORYorCONFIG_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=yandCONFIG_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.
Step 8 — Thunderbolt Firmware Update (Recommended)
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
- Unclip the programmer.
- Reconnect the CMOS battery.
- Reconnect the internal battery.
- Replace the bottom panel.
- 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
| File | Source | Destination |
|---|---|---|
ifd.bin | ifdtool -x of vendor dump + HAP bit set | binaries/ifd.bin |
gbe.bin | ifdtool -x of vendor dump | binaries/gbe.bin |
me_deguarded.bin | deguard finalimage.py from Dell donor ME | binaries/me_deguarded.bin |
| FSP-M, FSP-S | git submodule (auto) | 3rdparty/fsp/KabylakeFspBinPkg/ |
| Microcode | git 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 olddefconfigfollowed bymake menuconfigto see the full resolved config.
Troubleshooting
Flash Chip Not Detected
- Check clip contact — ensure solid seating on all 8 pins.
- Verify pin 1 alignment (dot on chip → pin 1 on clip).
- Try different /WP and /HOLD states — see Tools — /WP and /HOLD Troubleshooting.
- Try lower SPI speed:
spispeed=512.
System Won’t Boot After Flash
- Reconnect the external programmer.
- Flash back the original backup:
flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=4000 -w stock_t580_1.bin - Verify deguard ME was generated correctly — wrong delta or missing HAP bit will brick.
- Disconnect all power for 60 seconds (including CMOS) then retry.
Black Screen / No Display
- Ensure
libgfxinitis enabled in coreboot config. - Try an external display via Mini DisplayPort.
- If you have a dGPU model, check the known issues.
PXE Boot Not Showing
- Verify
CONFIG_EDK2_NETWORK_STACK=yandCONFIG_EDK2_IPXE_OPTION_NAME="Network Boot"were set before build. - Check UEFI Boot Manager (Escape at boot) for
UEFI PXEv4/UEFI HTTPv4entries. - 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:
- Reconnect the SOIC-8 clip and external programmer.
- Flash the original vendor backup:
flashprog -p linux_spi:dev=/dev/spidev0.0,spispeed=4000 -w stock_t580_1.bin - Once coreboot is installed, internal recovery via
flashrom -p internalalso 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_cleanercan disable PCIe devices (NVMe, WLAN, WWAN). The deguard approach already disables ME post-boot without truncation — additionalme_cleaneris 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=nif you want the same.
References
- Coreboot T470s/T480/T480s/T580/X280 docs
- Deguard utility docs
- Libreboot T480/T580 install guide (includes TB firmware details)
- MrChromebox EDK2
- Lenovo T580 HMM
- Lenovo TB3 firmware update
- Dell_PFS_Extract.py
- Gerrit #88490 — T480/T480s/X280 TBT enablement (Matt DeVillier)
- Tools & Common Procedures — programmer setup, wiring, build dependencies
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
| Feature | Value |
|---|---|
| CPU | Intel Core 8th/9th Gen (Coffee Lake / Coffee Lake Refresh) |
| Chipset | Intel Q370 (Cannon Lake PCH-H) |
| DRAM | 2× SO-DIMM DDR4-2400/2666, max 64 GB |
| ME version | v12 |
| Super I/O | NCT6686D-L (Nuvoton) |
| TPM | Infineon SLB 9670VQ2.0 (TPM 2.0) |
| Boot Guard | Not fused — direct external flash works |
| SoC coreboot directory | soc/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 work — coreboot 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.
| Variant | VRM | 65W CPU | Coreboot Tested |
|---|---|---|---|
| M920q | Standard | No — T-series only | Yes |
| M720q | Standard | No — T-series only | No (should work) |
| M920x | Extra phase (PU404) | Yes | No (should work) |
| P330 Tiny | Extra phase (PU404) | Yes | No (should work) |
Flash Chips
The M920q has two SOIC-8 SPI flash chips, both 3.3V, soldered (not socketed):
| Chip | Board Label | Model | Size | flashprog -c string | Pull-up Notes |
|---|---|---|---|---|---|
| BIOS1 | U31 | W25Q128JV | 16 MiB | W25Q128.V | Board has on-board pull-ups — do not hold /WP or /HOLD high. If reads/writes fail, see Tools — /WP and /HOLD Troubleshooting |
| BIOS2 | U32 | W25Q64JV | 8 MiB | W25Q64JV-.Q | No 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
spispeedto512if 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 File | Contents |
|---|---|
flashregion_0_flashdescriptor.bin | Intel Flash Descriptor |
flashregion_1_bios.bin | BIOS region (coreboot replaces this — not needed) |
flashregion_2_intel_me.bin | Intel CSME firmware |
flashregion_3_gbe.bin | Gigabit 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
- Install RAM in DIMM1 only — DIMM2 does not work under coreboot (bug #592)
- Connect a display via HDMI (try DisplayPort if no output on HDMI)
- Power on and wait up to 2 minutes — the first boot performs memory training with no display output. Subsequent boots are fast (MRC cache)
- 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
| Item | Details |
|---|---|
| ROM size | 24 MiB (two chips: 16 + 8) |
| BIOS1 split | dd if=coreboot.rom of=bios1.rom bs=1M count=16 |
| BIOS2 split | dd if=coreboot.rom of=bios2.rom bs=8M skip=2 |
| BIOS2 quirk | /WP + /HOLD must be held HIGH externally |
| HAP bit | ifdtool -p cnl -M 1 descriptor.bin |
| DIMM | Slot 1 only (bug #592) |
| CPU | T-series (35W) only on M920q — 65W CPUs fail under coreboot (VRM limitation) |
| 65W CPU support | Use M920x or P330 Tiny (extra VRM phase — untested with coreboot) |
| iPXE / PXE boot | CONFIG_EDK2_ENABLE_IPXE=y in defconfig |
| Boot Guard | Not fused — no deguard needed |
| ME disable | HAP bit only — do NOT use me_cleaner (ME v12) |
| Same board family | M720q, M920q (tested), M920x, P330 Tiny — all EQ370 NM-B551 |
References
- Coreboot M920q documentation
- Gerrit #80609 — original M920q port by Maciej Pijanowski (3mdeb)
- Bug #592 — DIMM2 not working
- TinySecrets — M920q/M920x VRM differences (documents hardware differences between board variants)
- Coreboot FAQ — ME and HAP bit
- flashprog
- MrChromebox edk2 features
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
| Item | Notes |
|---|---|
| SOIC-8 test clip | Pomona 5250 is best; generic clips work |
| Female–female Dupont wires | 10–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
flashromwithflashprogin 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:
| Item | Notes |
|---|---|
| Raspberry Pi 3B, 3B+, or 4 | Raspberry Pi OS Lite is fine |
| MicroSD card (8 GB+) | With Raspberry Pi OS flashed |
| 5V PSU | Stable power matters |
| Ethernet or WiFi | SSH 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 Pin | Pi Signal | SOIC-8 Pin | Signal | Required? |
|---|---|---|---|---|
| 24 | GPIO8 / CE0 | 1 | /CS | Yes |
| 21 | GPIO9 / MISO | 2 | MISO (DO) | Yes |
| 17 | 3.3V | 3 | /WP | No by default — see /WP and /HOLD Troubleshooting |
| 25 | GND | 4 | GND | Yes |
| 19 | GPIO10 / MOSI | 5 | MOSI (DI) | Yes |
| 23 | GPIO11 / SCLK | 6 | CLK | Yes |
| — | — | 7 | /HOLD | No by default — see /WP and /HOLD Troubleshooting |
| 17 | 3.3V | 8 | VCC | Yes |
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:
- Hold BOOTSEL, plug Pico into USB, release BOOTSEL — mounts as
RPI-RP2 cp bin/serprog_pico/serprog_pico.uf2 /media/$USER/RPI-RP2/- Pico reboots. Verify:
sudo dmesg | grep ttyACM→ttyACM0: 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 Pin | Signal | Pico Physical Pin | Pico GPIO | Required? |
|---|---|---|---|---|
| 1 | /CS | Pin 7 | GP5 | Yes |
| 2 | MISO | Pin 6 | GP4 | Yes |
| 3 | /WP | — | — | No by default — see /WP and /HOLD Troubleshooting |
| 4 | GND | Pin 38 | GND | Yes |
| 5 | MOSI | Pin 5 | GP3 | Yes |
| 6 | SCK | Pin 4 | GP2 | Yes |
| 7 | /HOLD | — | — | No by default — see /WP and /HOLD Troubleshooting |
| 8 | VCC | Pin 36 | 3V3(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) orspispeed=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 |
|---|---|---|
| floating | floating | Start here — default, no extra wires |
| HIGH (3.3V) | floating | Writes fail or chip reports write-protected |
| floating | HIGH (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 File | Contents |
|---|---|
flashregion_0_flashdescriptor.bin | Intel Flash Descriptor |
flashregion_1_bios.bin | BIOS region |
flashregion_2_intel_me.bin | Intel CSME firmware |
flashregion_3_gbe.bin | Gigabit 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/4 | Raspberry Pi Pico | |
|---|---|---|
| Read speed | ~500 KB/s at 1 MHz | ~2 MB/s at 16 MHz |
| 3.3V pins | 2 (pins 1 and 17) — easy multi-wire crimps | 1 (pin 36) — needs multi-way split or breadboard |
| flashprog interface | linux_spi (native kernel driver) | serprog (USB serial) |
| Setup | Full OS, raspi-config, build flashprog | Flash UF2, done |
| Remote flashing | Yes — headless SSH from any terminal | No — host computer must be physically nearby |
| Verdict | More convenient overall | Cheapest, fastest reads |
References
- Libreboot SPI flashing guide — authoritative wiring reference for Pi 3/4 and Pico
- flashprog
- pico-serprog (Libreboot fork)
- Coreboot FAQ — ME and HAP bit