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
- MRC cache corruption with HT-enabled CPUs — coreboot’s memory training cache appears to capture invalid state when used with hyperthreaded Coffee Lake CPUs (i7-8700, i7-8700T, i9-9900T). Once written, this poisoned cache prevents subsequent boots even with non-HT CPUs until the BIOS region is reflashed. Workaround: disable MRC cache entirely (see CPU Compatibility section). Root cause not yet identified.
- Soft reboot from OS hangs — power cycle required —
rebootfrom Linux or BSD leaves the system in a black screen state. Caused by the ACPI reset path on these coreboot ports. Workaround: see Post-Install OS Configuration. - Warm reboot occasionally fails after disabling MRC cache — without cached training data, FSP-M trains memory from scratch on every boot, including warm reboots. Occasionally trips up. Power cycle always recovers. Acceptable tradeoff for the MRC workaround.
- HT defaults to disabled at first boot — the M920q port exposes a
hyper_threadingCMOS option butcmos.defaultdoesn’t set it, so fresh CMOS reads as 0 = Disable. Addhyper_threading=Enabletosrc/mainboard/lenovo/m720q_m920q/cmos.defaultbefore building, or toggle at runtime withnvramtoolafter first boot. - 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
CPU Compatibility
| CPU | Cores / Threads | TDP | HT | Status |
|---|---|---|---|---|
| i5-8500T | 6C / 6T | 35W | No | ✅ Works out of the box (developer tested) |
| i3-8100T | 4C / 4T | 35W | No | ✅ Works out of the box |
| i7-8700T | 6C / 12T | 35W | Yes | ⚠️ Works with MRC cache disabled |
| i7-8700 | 6C / 12T | 65W | Yes | ⚠️ Untested with MRC cache disabled — likely works |
| i9-9900T | 8C / 16T | 35W | Yes | ❓ Untested — likely needs MRC cache disabled |
For HT-enabled CPUs (any i7/i9 Coffee Lake), disable MRC cache by adding the following to your defconfig before building:
# CONFIG_CACHE_MRC_SETTINGS is not set
Then rebuild with make olddefconfig && make clean && make -j$(nproc).
Important warning about CPU swapping: if you boot the system with an HT-enabled CPU while MRC cache is enabled, the cache becomes poisoned and all subsequent boots will fail — including with previously working non-HT CPUs — until you reflash the BIOS region. Either disable MRC cache from the start, or always reflash before swapping CPUs.
The trade-off for disabling MRC cache: cold boot is ~1-2 seconds slower (full memory training every boot instead of using cached values), and warm reboots occasionally fail and require a power cycle to recover.
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.
| Variant | Coreboot Tested |
|---|---|
| M920q | Yes |
| M720q | No (should work — same board) |
| M920x | No (should work — same board) |
| P330 Tiny | No (should work — same board) |
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 | /WP and /HOLD pulled high by board — no action needed |
| BIOS2 | U32 | W25Q64JV | 8 MiB | W25Q64JV-.Q | /WP (pin 3) and /HOLD (pin 7) must be held HIGH manually |
Total ROM size: 24 MiB (16 + 8).
Prerequisites
For SPI programmer setup (Raspberry Pi or Pico), flashprog installation, GPIO wiring tables, and parts lists see Tools — SPI Programmers.
M920q-Specific Notes
In addition to the standard programmer setup in Tools, the M920q requires:
- A 3.3V splitter wire for the BIOS2 chip — split a Dupont wire from any RPi 3.3V pin to both pin 3 (/WP) and pin 7 (/HOLD) on the Pomona clip. BIOS1 has on-board pull-ups for these pins; BIOS2 does not.
- See Tools — /WP and /HOLD Troubleshooting for general guidance on pull-up issues.
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 for the general procedure. All three SHA256 checksums must match before proceeding. If they don’t, reseat the clip and read again.
BIOS1 (16 MiB)
No extra pull-ups required.
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 using the 3.3V splitter described in Prerequisites.
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, verify /WP and /HOLD are held HIGH. BIOS1 has on-board pull-ups for these pins; BIOS2 does not. You may also try lowering
spispeedto512if detection is unreliable. See Tools — /WP and /HOLD Troubleshooting for more.
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. For ifdtool build instructions 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
For build dependencies and cross-compiler toolchain setup 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
#
# Lenovo ThinkCentre M920q coreboot defconfig
#
# Payload: edk2 (MrChromebox) — PXE, UEFI Shell, TPM 2.0, Secure Boot, NVRAM
# ME: HAP bit set in descriptor (no me_cleaner)
#
# CPU compatibility:
# Works out of the box: i5-8500T, i3-8100T (no HT)
# Works with MRC cache disabled: i7-8700T (HT)
# Untested but likely works with MRC cache disabled: i7-8700, i9-9900T
#
# For HT-enabled CPUs (any i7/i9 Coffee Lake), uncomment this line:
# # CONFIG_CACHE_MRC_SETTINGS is not set
# Tradeoff: ~1-2s slower cold boot, occasional warm reboot fails
# WARNING: Booting an HT CPU without this poisons MRC cache for ALL CPUs
#
# Known issues:
# - Soft reboot from OS hangs — requires reboot=pci (Linux) or
# hw.acpi.handle_reboot=0 (FreeBSD/OPNsense) post-install
# - DIMM2 slot broken (bug #592) — use DIMM1 only
# - Front audio jacks don't work
# - BIOS2 chip needs /WP + /HOLD held HIGH when flashing externally
#
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
# Uncomment for HT-enabled CPUs (i7-8700, i7-8700T, i9-9900T, etc.):
# # CONFIG_CACHE_MRC_SETTINGS 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)
No extra pull-ups needed.
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.
Post-Install OS Configuration
Soft Reboot Fix (Required)
After installing an OS, you’ll likely find that reboot from the command line leaves the system in a black-screen hang state requiring a power cycle. This affects both Linux and BSD distributions and is caused by the ACPI reset path on these coreboot ports failing to properly trigger a chipset reset. The fix is to bypass the ACPI reset path entirely and use a direct CF9 chipset reset, which is functionally equivalent to a hardware reset.
Behaviour under Windows is unknown — neither tested nor reported. BSD-family systems (FreeBSD, OPNsense, pfSense) are assumed to be affected and have been confirmed to need the same workaround.
Linux (Debian, Ubuntu, Arch, Fedora, etc.)
Edit /etc/default/grub and add reboot=pci to the kernel command line:
GRUB_CMDLINE_LINUX_DEFAULT="quiet reboot=pci"
Then update GRUB:
sudo update-grub # Debian/Ubuntu
sudo grub-mkconfig -o /boot/grub/grub.cfg # Arch/Fedora
For systemd-boot or other bootloaders, add reboot=pci to the kernel command line in the relevant boot entry config.
FreeBSD / OPNsense / pfSense
Add a loader tunable:
echo 'hw.acpi.handle_reboot="0"' >> /boot/loader.conf.local
On OPNsense this can also be set via the GUI: System → Settings → Tunables → Add → hw.acpi.handle_reboot = 0.
This setting takes effect on the next reboot, so the first reboot after applying it will still need a power cycle. Subsequent reboots should work cleanly.
Recovery
If the machine doesn’t boot, flash your original backup dumps externally:
# BIOS1 (no pull-ups needed)
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, flash the BIOS region only:
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) |
| Works out of the box | i5-8500T, i3-8100T (no HT) |
| Works with MRC cache disabled | i7-8700T (HT) |
| MRC cache workaround | # CONFIG_CACHE_MRC_SETTINGS is not set in defconfig |
| CPU swap warning | Booting an HT CPU with MRC cache enabled poisons the cache for ALL CPUs — must reflash |
| Soft reboot fix (Linux) | reboot=pci kernel parameter |
| Soft reboot fix (FreeBSD/OPNsense) | hw.acpi.handle_reboot="0" loader tunable |
| 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
- Coreboot FAQ — ME and HAP bit
- flashprog
- MrChromebox edk2 features
- Tools & Common Procedures — programmer setup, wiring, build dependencies
License
This guide is released under CC BY-SA 4.0.