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.