Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,13 @@ workflows:
- x86-musl-cross-make [cross compiler]

# ── coreboot 25.09 boards (alphabetical) ───────────────────────────────
- build:
name: dell-optiplex-9020-sff
target: dell-optiplex-9020-sff
subcommand: ""
requires:
- EOL_t480-hotp-maximized [seed:coreboot-25.09]

- build:
name: EOL_m900_tower-hotp-maximized
target: EOL_m900_tower-hotp-maximized
Expand Down
5 changes: 5 additions & 0 deletions blobs/optiplex_9020/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# me.bin is downloaded and neutralized on demand by download-clean-me.
# original_dell_bios.bin is the user's private BIOS backup (if they use the
# extract script instead). Neither should be committed.
me.bin
original_dell_bios.bin
83 changes: 83 additions & 0 deletions blobs/optiplex_9020/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# OptiPlex 9020 SFF Blobs

- [Overview](#overview)
- [Blob Strategy](#blob-strategy)
- [Building with Default Blobs](#building-with-default-blobs)
- [Using Your Own Blobs](#using-your-own-blobs)

## Overview

Heads on the Dell OptiPlex 9020 SFF requires three small binary blobs:

| Blob | Size | Source |
|-----------|----------|----------------------------------------------------------|
| `ifd.bin` | 4 KB | Intel Flash Descriptor, shipped in-tree (anonymized) |
| `me.bin` | ~120 KB | Intel ME, downloaded and neutralized by `download-clean-me` |
| `gbe.bin` | 8 KB | Intel Gigabit Ethernet config, shipped in-tree (anonymized MAC) |

`ifd.bin` and `gbe.bin` are committed to the repository directly. They contain
no personal data: the GbE MAC is set to the anonymized `00:de:ad:c0:ff:ee`,
matching the convention used by other Heads boards. The IFD has its ME region
shrunk and BIOS region expanded, ready for a neutralized ME.

`me.bin` is the only blob that is **not** committed (it is proprietary Intel
firmware). It is downloaded on demand from the public Lenovo installer
`glrg22ww.exe` -- the same source used by the t440p -- because the ME firmware
is platform-generic for Lynx Point, not vendor-specific.

## Blob Strategy

The Intel ME on the 9020 (Lynx Point, ME 9.x) is **neutralized and shrunk**
using [me_cleaner](https://github.com/corna/me_cleaner) with the flags
`-r -t`:

- `-r` removes non-essential ME modules (TDT, FPF, HOSTCOMM, SESSMGR, ...)
- `-t` truncates the ME region to the minimum bootable size (~120 KB)

Result: ME shrunk from 5 MB to ~120 KB. The remaining FTPR module is required
for power/clock management during boot; it cannot be removed without bricking
the board. The ME's RSA signature remains valid (verified by me_cleaner), so
the board boots.

**Notably NOT required** (this is the blob-minimized setup):

- `mrc.bin` -- eliminated. Haswell native RAM initialization (NRI) is used
instead, via coreboot's `CONFIG_USE_NATIVE_RAMINIT=y`.
- FSP -- Haswell does not use Intel Firmware Support Package.
- Intel ME in full -- only the ~120 KB FTPR bring-up module remains; all AMT,
networking, anti-theft and backdoor modules removed.

> **Note on "completely removing" the ME:** The ME is physically present in
> the Lynx Point PCH silicon. Software cannot remove hardware. me_cleaner's
> neutralization is the maximum achievable on this platform.

## Building with Default Blobs

The standard build path downloads and neutralizes the ME automatically:

```console
$ make BOARD=dell-optiplex-9020-sff
```

The build system invokes `download-clean-me` to fetch the Lenovo installer,
extract `ME9.1_5M_Production.bin`, and run me_cleaner. No manual steps
required.

## Using Your Own Blobs

If you prefer to extract the blobs from your own Dell BIOS backup (for
example, to preserve your original LAN MAC address), build Heads once to
download the coreboot sources, then run the extraction script:

```console
$ make BOARD=dell-optiplex-9020-sff # downloads coreboot sources

$ export COREBOOT_DIR="./build/x86/coreboot-25.09/"
$ ./blobs/optiplex_9020/extract /path/to/original_dell_bios.bin ./blobs/optiplex_9020

$ make BOARD=dell-optiplex-9020-sff # rebuild with your blobs
```

The extraction script performs a more aggressive neutralization
(`me_cleaner -S -r -t -d`, including soft-disable via the AltMeDisable/HAP
bit) and preserves your hardware's original MAC address.
73 changes: 73 additions & 0 deletions blobs/optiplex_9020/download-clean-me
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#!/usr/bin/env bash
# =============================================================================
# Download Intel ME firmware for the Dell OptiPlex 9020 SFF, neutralize it,
# and shrink it to the minimum bootable size.
#
# The 9020 SFF is a Haswell + Lynx Point platform (Intel ME 9.1). It shares
# the same ME firmware generation as the ThinkPad T440p, so this script
# reuses the publicly available Lenovo installer (glrg22ww.exe) as the ME
# source. The ME firmware is platform-generic for Lynx Point, not
# vendor-specific.
#
# Usage: ./download-clean-me <path_to_output_directory>
#
# Requires COREBOOT_DIR pointing at a built coreboot source tree (for
# util/me_cleaner) and the innoextract tool.
# =============================================================================
set -e

function usage() {
echo -n \
"Usage: $(basename "$0") path_to_output_directory
Download Intel ME firmware from Lenovo, neutralize, and shrink.
"
}

# Expected SHA256 of the resulting neutralized me.bin.
# Sourced from the Lenovo glrg22ww.exe installer (same as the t440p).
ME_BIN_HASH="b7cf4c0cf514bbf279d9fddb12c34fca5c1c23e94b000c26275369b924ab9c25"

if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
if [[ "${1:-}" == "--help" ]]; then
usage
else
if [[ -z "${COREBOOT_DIR}" ]]; then
echo "ERROR: No COREBOOT_DIR variable defined."
exit 1
fi

output_dir="$(realpath "${1:-./}")"

if [[ ! -f "${output_dir}/me.bin" ]]; then
# Unpack Lenovo's Windows installer into a temporary directory and
# extract the Intel ME blob.
pushd "$(mktemp -d)"

curl -O https://download.lenovo.com/pccbbs/mobiles/glrg22ww.exe
innoextract glrg22ww.exe

mv app/ME9.1_5M_Production.bin "${COREBOOT_DIR}/util/me_cleaner"

popd

# Neutralize and shrink Intel ME. Note that this doesn't include
# --soft-disable to set the "ME Disable" or "ME Disable B" (e.g.,
# High Assurance Program) bits, as they are defined within the Flash
# Descriptor.
# https://github.com/corna/me_cleaner/wiki/External-flashing#neutralize-and-shrink-intel-me-useful-only-for-coreboot
pushd "${COREBOOT_DIR}/util/me_cleaner"

python me_cleaner.py -r -t -O me_shrinked.bin ME9.1_5M_Production.bin

mv me_shrinked.bin "${output_dir}/me.bin"
rm ./*.bin

popd
fi

if ! echo "${ME_BIN_HASH} ${output_dir}/me.bin" | sha256sum --check; then
echo "ERROR: SHA256 checksum for me.bin doesn't match."
exit 1
fi
fi
fi
92 changes: 92 additions & 0 deletions blobs/optiplex_9020/extract
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#!/usr/bin/env bash
# =============================================================================
# Extract Intel firmware blobs (IFD, ME, GbE) from an original Dell OptiPlex
# 9020 SFF BIOS backup, and neutralize the Intel ME.
#
# Usage: ./extract <path_to_original_rom.bin> <path_to_output_directory>
#
# Produces in the output directory:
# ifd.bin - Intel Flash Descriptor (resized: ME region shrunk, BIOS grown)
# me.bin - Intel ME firmware (neutralized + soft-disabled + truncated)
# gbe.bin - Intel Gigabit Ethernet configuration (incl. LAN MAC address)
#
# Modelled on blobs/t440p/extract. Requires COREBOOT_DIR pointing at a built
# coreboot source tree (for util/me_cleaner and util/ifdtool).
# =============================================================================
set -e

function usage() {
echo -n \
"Usage: $(basename "$0") path_to_original_rom path_to_output_directory
Extract Intel firmware from the original Dell OptiPlex 9020 SFF BIOS backup.
Neutralizes the ME via me_cleaner (-S -r -t -d) and shrinks the IFD regions.
"
}

if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
if [[ "${1:-}" == "--help" ]]; then
usage
else
if [[ -z "${COREBOOT_DIR}" ]]; then
echo "ERROR: No COREBOOT_DIR variable defined."
echo " Export COREBOOT_DIR=/path/to/built/coreboot source tree."
exit 1
fi

original_rom="$(realpath "$1")"
output_dir="$(realpath "${2:-./}")"

if [[ ! -f "${original_rom}" ]]; then
echo "ERROR: original ROM not found: ${original_rom}"
exit 1
fi

echo ">>> Source ROM: ${original_rom} ($(stat -c%s "${original_rom}") bytes)"
echo ">>> Output dir: ${output_dir}"
echo

# Step 1: Neutralize Intel ME + resize the IFD regions in one pass.
# Flags (same as t440p reference):
# -S soft-disable (set AltMeDisable / HAP bit in PCHSTRP10)
# -r remove (strip non-essential ME modules)
# -t truncate (shrink ME region to minimum bootable size)
# -d deactivate (additionally disable ME features)
# -O output full image (descriptor + neutered ME)
# -D output shrinked descriptor
# -M output truncated ME image
echo ">>> me_cleaner: neutralizing + soft-disabling Intel ME..."
pushd "${COREBOOT_DIR}/util/me_cleaner"

python me_cleaner.py -S -r -t -d \
-O out.bin \
-D ifd_shrinked.bin \
-M me_shrinked.bin \
"${original_rom}"

mv ifd_shrinked.bin "${output_dir}/ifd.bin"
mv me_shrinked.bin "${output_dir}/me.bin"
rm -f ./*.bin

popd
echo " -> ifd.bin ($(stat -c%s "${output_dir}/ifd.bin") bytes)"
echo " -> me.bin ($(stat -c%s "${output_dir}/me.bin") bytes)"

# Step 2: Extract the Intel Gigabit Ethernet (GbE) configuration blob
# (contains the LAN MAC address) using ifdtool.
echo ">>> ifdtool: extracting GbE configuration blob..."
pushd "${COREBOOT_DIR}/util/ifdtool"

make
./ifdtool -x "${original_rom}"

mv flashregion_3_gbe.bin "${output_dir}/gbe.bin"
rm -f flashregion_*.bin

popd
echo " -> gbe.bin ($(stat -c%s "${output_dir}/gbe.bin") bytes)"

echo
echo ">>> Done. Blobs in ${output_dir}:"
ls -la "${output_dir}"/ifd.bin "${output_dir}"/me.bin "${output_dir}"/gbe.bin
fi
fi
Binary file added blobs/optiplex_9020/gbe.bin
Binary file not shown.
2 changes: 2 additions & 0 deletions blobs/optiplex_9020/hashes.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
4bbe2955e5b417fda53b29da8a864ae726de79af2629991ead1ee24536d6de1a ifd.bin
fa6baa411b46272b7398940d7edf2864742b94967205f9dea15f8772b766acc3 gbe.bin
Binary file added blobs/optiplex_9020/ifd.bin
Binary file not shown.
Loading