Skip to content

Commit 4e113ad

Browse files
committed
Add CBFS/IFD validation for coreboot builds
Introduce validation to ensure coreboot CBFS size matches IFD BIOS region: - Add bin/validate_cbfs_ifd_fit.sh: New script to validate CBFS size against Intel Flash Descriptor (IFD) BIOS region and provide space usage statistics - Add Makefile targets: validate_cbfs_ifd and fix_cbfs_ifd for manual validation and automatic fixes - Integrate validation into coreboot module build process to catch misconfigs at compile time - Add validation to final ROM image generation step - Add informational hint to Makefile when V=1 verbose flag is not set This prevents shipping firmware with CBFS sizes that exceed the available BIOS region, catching configuration errors early in the build process. Signed-off-by: Thierry Laurion <insurgo@riseup.net>
1 parent 3b656d0 commit 4e113ad

3 files changed

Lines changed: 373 additions & 1 deletion

File tree

Makefile

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ $(info **LOADAVG** (default: 1.5 times CPUS, e.g., 'make LOADAVG=54'))
6565
$(info **AVAILABLE_MEM_GB** (default: memory available on the system in GB, e.g., 'make AVAILABLE_MEM_GB=4'))
6666
$(info **MEM_PER_JOB_GB** (default: 1GB per job, e.g., 'make MEM_PER_JOB_GB=2'))
6767
$(info ----------------------------------------------------------------------)
68+
ifeq "$(V)" ""
69+
$(info Hint: If the build fails, re-run with V=1 for full configure + validation output)
70+
$(info Verbose example: ./docker_repro.sh make BOARD=$(BOARD) V=1)
71+
$(info ----------------------------------------------------------------------)
72+
endif
6873
$(info !!!!!! Build starts !!!!!!)
6974

7075

@@ -284,6 +289,34 @@ all payload:
284289
@sha256sum $< | tee -a "$(HASHES)"
285290
@stat -c "%8s:%n" $< | tee -a "$(SIZES)"
286291

292+
# Validate coreboot CBFS size against IFD BIOS region
293+
validate_cbfs_ifd:
294+
ifneq ($(CONFIG_COREBOOT),)
295+
@echo "Validating $(BOARD) CBFS size against IFD BIOS region..."
296+
@$(pwd)/bin/validate_cbfs_ifd_fit.sh \
297+
--coreboot-dir "$(build)/$(coreboot_dir)" \
298+
--board-dir "$(build)/$(BOARD)" \
299+
--config "$(pwd)/$(CONFIG_COREBOOT_CONFIG)" || exit 1
300+
@echo "✓ CBFS configuration is valid"
301+
else
302+
@echo "Board $(BOARD) does not use coreboot, skipping validation"
303+
endif
304+
305+
# Auto-fix coreboot CBFS size to match IFD BIOS region
306+
fix_cbfs_ifd:
307+
ifneq ($(CONFIG_COREBOOT),)
308+
@echo "Auto-fixing $(BOARD) CBFS size to match IFD BIOS region..."
309+
@$(pwd)/bin/validate_cbfs_ifd_fit.sh \
310+
--coreboot-dir "$(build)/$(coreboot_dir)" \
311+
--board-dir "$(build)/$(BOARD)" \
312+
--config "$(pwd)/$(CONFIG_COREBOOT_CONFIG)" \
313+
--fix || exit 1
314+
@echo ""
315+
@echo "If CONFIG_CBFS_SIZE was adjusted, next build will use the new size."
316+
else
317+
@echo "Board $(BOARD) does not use coreboot, nothing to fix"
318+
endif
319+
287320
# Disable all built in rules
288321
.INTERMEDIATE:
289322
.SUFFIXES:

bin/validate_cbfs_ifd_fit.sh

Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
#!/bin/bash
2+
# SPDX-License-Identifier: GPL-3.0-or-later
3+
# Validate that CBFS size fits within IFD BIOS region
4+
# and report space usage statistics
5+
6+
set -e
7+
8+
usage() {
9+
cat <<EOF
10+
Usage: $0 --coreboot-dir <path> --board-dir <path> --config <path> [--fix]
11+
12+
Validates that CONFIG_CBFS_SIZE from coreboot config matches the BIOS region
13+
size reported by the Intel Flash Descriptor (IFD), and provides space usage
14+
statistics from cbfstool.
15+
16+
Options:
17+
--coreboot-dir Path to coreboot build directory
18+
--board-dir Path to board build directory
19+
--config Path to coreboot config file
20+
--fix Automatically fix CONFIG_CBFS_SIZE to match IFD BIOS region
21+
--help Show this help message
22+
23+
Exit codes:
24+
0: Validation passed (or fix applied successfully, or tools not available yet)
25+
1: Validation failed - CONFIG_CBFS_SIZE exceeds IFD BIOS region
26+
EOF
27+
exit "${1:-0}"
28+
}
29+
30+
# Parse arguments
31+
FIX_MODE=0
32+
while [ $# -gt 0 ]; do
33+
case "$1" in
34+
--coreboot-dir)
35+
COREBOOT_DIR="$2"
36+
shift 2
37+
;;
38+
--board-dir)
39+
BOARD_DIR="$2"
40+
shift 2
41+
;;
42+
--config)
43+
CONFIG_FILE="$2"
44+
shift 2
45+
;;
46+
--fix)
47+
FIX_MODE=1
48+
shift
49+
;;
50+
--help)
51+
usage 0
52+
;;
53+
*)
54+
echo "Unknown option: $1" >&2
55+
usage 1
56+
;;
57+
esac
58+
done
59+
60+
# Validate required arguments
61+
if [ -z "$COREBOOT_DIR" ] || [ -z "$BOARD_DIR" ] || [ -z "$CONFIG_FILE" ]; then
62+
echo "Error: Missing required arguments" >&2
63+
usage 1
64+
fi
65+
66+
# Check if tools exist
67+
CBFSTOOL="$COREBOOT_DIR/cbfstool"
68+
IFDTOOL="$COREBOOT_DIR/util/ifdtool/ifdtool"
69+
70+
if [ ! -x "$CBFSTOOL" ]; then
71+
echo "Warning: cbfstool not found at $CBFSTOOL" >&2
72+
echo "Skipping CBFS analysis (coreboot not built yet)" >&2
73+
CBFSTOOL=""
74+
fi
75+
76+
if [ ! -x "$IFDTOOL" ]; then
77+
echo "Warning: ifdtool not found at $IFDTOOL" >&2
78+
echo "Skipping IFD validation (coreboot not built yet)" >&2
79+
IFDTOOL=""
80+
fi
81+
82+
# Extract CONFIG_CBFS_SIZE from config
83+
CBFS_SIZE=$(grep "^CONFIG_CBFS_SIZE=" "$CONFIG_FILE" | cut -d= -f2)
84+
if [ -z "$CBFS_SIZE" ]; then
85+
echo "Error: CONFIG_CBFS_SIZE not found in $CONFIG_FILE" >&2
86+
exit 1
87+
fi
88+
89+
# Convert to decimal
90+
CBFS_SIZE_DEC=$((CBFS_SIZE))
91+
92+
# Extract IFD path from config
93+
IFD_PATH=$(grep "^CONFIG_IFD_BIN_PATH=" "$CONFIG_FILE" | cut -d'"' -f2)
94+
95+
# Resolve relative IFD path to absolute, preferring coreboot base dir
96+
if [ -n "$IFD_PATH" ] && [[ "$IFD_PATH" != /* ]] && [[ "$IFD_PATH" != *"@"* ]]; then
97+
COREBOOT_BASE_DIR="$(dirname "$COREBOOT_DIR")"
98+
if [ -d "$COREBOOT_BASE_DIR" ]; then
99+
IFD_PATH="$COREBOOT_BASE_DIR/$IFD_PATH"
100+
else
101+
IFD_PATH="$PWD/$IFD_PATH"
102+
fi
103+
fi
104+
105+
# If IFD path uses @BLOB_DIR@, resolve it
106+
# @BLOB_DIR@ typically expands to blobs/ from the repo root
107+
if [[ "$IFD_PATH" == *"@BLOB_DIR@"* ]]; then
108+
# Try to find the Heads repo root (go up from coreboot-dir)
109+
# COREBOOT_DIR is like /home/user/heads/build/x86/coreboot-25.09/BOARD
110+
# So we need to go up 4 levels: BOARD -> coreboot-25.09 -> x86 -> build -> heads
111+
if [ -d "$COREBOOT_DIR" ]; then
112+
REPO_ROOT=$(cd "$COREBOOT_DIR/../../../../" 2>/dev/null && pwd || echo "")
113+
if [ -n "$REPO_ROOT" ] && [ -d "$REPO_ROOT/blobs" ]; then
114+
IFD_PATH="${IFD_PATH/@BLOB_DIR@/$REPO_ROOT/blobs}"
115+
fi
116+
fi
117+
fi
118+
119+
# If IFD path uses @BLOB_DIR@, we need to resolve it
120+
# For now, skip validation if no IFD or if path is not resolved
121+
if [ -z "$IFD_PATH" ] || [[ "$IFD_PATH" == *"@"* ]]; then
122+
# Try to find the IFD in the coreboot build
123+
BUILD_IFD="$COREBOOT_DIR/flashmap_descriptor.bin"
124+
if [ ! -f "$BUILD_IFD" ]; then
125+
echo "Info: No IFD validation possible (CONFIG_IFD_BIN_PATH=$IFD_PATH, build IFD not found)"
126+
echo "Skipping IFD vs CBFS size validation"
127+
IFD_VALIDATION_SKIPPED=1
128+
# Still report CBFS space usage even without IFD
129+
if [ -n "$CBFSTOOL" ] && [ -f "$COREBOOT_DIR/coreboot.rom" ]; then
130+
echo ""
131+
CBFS_OUTPUT=$("$CBFSTOOL" "$COREBOOT_DIR/coreboot.rom" print 2>&1 || true)
132+
FREE_BYTES=$(echo "$CBFS_OUTPUT" | awk '/\(empty\)/ {sum += $4} END {print sum+0}')
133+
FREE_KB=$((FREE_BYTES / 1024))
134+
echo "CBFS configured size: $CBFS_SIZE ($CBFS_SIZE_DEC bytes)"
135+
echo "CBFS Free Space: $FREE_BYTES bytes ($FREE_KB KiB)"
136+
echo ""
137+
fi
138+
else
139+
IFD_PATH="$BUILD_IFD"
140+
fi
141+
fi
142+
143+
# Perform IFD validation if we have a path
144+
if [ -z "$IFD_VALIDATION_SKIPPED" ] && [ -f "$IFD_PATH" ] && [ -n "$IFDTOOL" ]; then
145+
echo "==================================================================="
146+
echo "IFD vs CBFS Size Validation"
147+
echo "==================================================================="
148+
149+
# Try to get platform-specific ifdtool flag
150+
PLATFORM=""
151+
# First: check explicit CONFIG_IFD_CHIPSET
152+
PLATFORM=$(grep '^CONFIG_IFD_CHIPSET=' "$CONFIG_FILE" | cut -d'"' -f2 || true)
153+
# Second: auto-detect for Haswell/Broadwell (they need ifd2 flag)
154+
if [ -z "$PLATFORM" ]; then
155+
if grep -qE 'CONFIG_SOUTHBRIDGE_INTEL_LYNXPOINT|CONFIG_SOUTHBRIDGE_INTEL_WILDCATPOINT' "$CONFIG_FILE"; then
156+
PLATFORM="ifd2"
157+
echo "Auto-detected platform: ifd2 (Haswell/Broadwell)"
158+
fi
159+
fi
160+
161+
# Run ifdtool to parse the descriptor
162+
IFD_OUTPUT=""
163+
if [ -n "$PLATFORM" ]; then
164+
# Try with platform flag first
165+
IFD_OUTPUT=$("$IFDTOOL" --platform "$PLATFORM" -d "$IFD_PATH" 2>/dev/null || true)
166+
if [ -n "$IFD_OUTPUT" ]; then
167+
echo "Using platform-specific parse: $PLATFORM"
168+
else
169+
# Platform flag failed, fall back to generic
170+
echo "Warning: --platform $PLATFORM failed, using generic parse"
171+
IFD_OUTPUT=$("$IFDTOOL" -d "$IFD_PATH" 2>/dev/null || true)
172+
fi
173+
else
174+
# No platform needed (e.g., Sandy/Ivy Bridge), use generic parse
175+
IFD_OUTPUT=$("$IFDTOOL" -d "$IFD_PATH" 2>/dev/null || true)
176+
fi
177+
178+
179+
# Extract BIOS region from IFD output
180+
BIOS_REGION=$(echo "$IFD_OUTPUT" | grep "Flash Region 1 (BIOS):" | head -1)
181+
if [ -z "$BIOS_REGION" ]; then
182+
echo "Error: Could not find BIOS region in IFD" >&2
183+
exit 1
184+
fi
185+
186+
# Parse BIOS region addresses (format: "00021000 - 00bfffff")
187+
BIOS_START=$(echo "$BIOS_REGION" | awk '{print $(NF-2)}')
188+
BIOS_END=$(echo "$BIOS_REGION" | awk '{print $NF}')
189+
BIOS_SIZE=$(( 0x$BIOS_END - 0x$BIOS_START + 1 ))
190+
BIOS_SIZE_KB=$((BIOS_SIZE / 1024))
191+
CBFS_SIZE_KB=$((CBFS_SIZE_DEC / 1024))
192+
193+
echo "IFD BIOS Region: 0x$BIOS_START - 0x$BIOS_END"
194+
echo "IFD BIOS Size: 0x$(printf '%X' $BIOS_SIZE) ($BIOS_SIZE_KB KiB)"
195+
echo "CONFIG_CBFS_SIZE: $CBFS_SIZE ($CBFS_SIZE_KB KiB)"
196+
echo ""
197+
198+
# CASE 1: CONFIG_CBFS_SIZE is too large
199+
if [ $CBFS_SIZE_DEC -gt $BIOS_SIZE ]; then
200+
OVERFLOW=$(( CBFS_SIZE_DEC - BIOS_SIZE ))
201+
OVERFLOW_KB=$((OVERFLOW / 1024))
202+
203+
if [ $FIX_MODE -eq 1 ]; then
204+
# Check if current CBFS content will fit after shrinking
205+
if [ -n "$CBFSTOOL" ] && [ -f "$COREBOOT_DIR/coreboot.rom" ]; then
206+
CBFS_PRINT=$("$CBFSTOOL" "$COREBOOT_DIR/coreboot.rom" print 2>/dev/null || true)
207+
FREE_BYTES=$(echo "$CBFS_PRINT" | awk '/\(empty\)/ {sum += $4} END {print sum+0}')
208+
USED_BYTES=$(( CBFS_SIZE_DEC - FREE_BYTES ))
209+
USED_KB=$((USED_BYTES / 1024))
210+
211+
if [ $USED_BYTES -gt $BIOS_SIZE ]; then
212+
echo "❌ Cannot shrink: Current CBFS content ($USED_KB KiB) won't fit in IFD BIOS region ($BIOS_SIZE_KB KiB)" >&2
213+
echo " Remove payloads/modules before retrying" >&2
214+
exit 1
215+
fi
216+
fi
217+
218+
# Perform shrink to exact IFD size
219+
SHRINK_KB=$(( (CBFS_SIZE_DEC - BIOS_SIZE) / 1024 ))
220+
echo "🔧 Shrinking CONFIG_CBFS_SIZE by $SHRINK_KB KiB"
221+
sed -i "s/CONFIG_CBFS_SIZE=0x[0-9A-Fa-f]*/CONFIG_CBFS_SIZE=0x$(printf '%X' $BIOS_SIZE)/" "$CONFIG_FILE"
222+
echo "✓ Updated: CONFIG_CBFS_SIZE=0x$(printf '%X' $BIOS_SIZE)"
223+
exit 0
224+
else
225+
# Report error
226+
echo "❌ VALIDATION FAILED: CONFIG_CBFS_SIZE exceeds IFD BIOS region by $OVERFLOW_KB KiB"
227+
echo ""
228+
echo "Fix: Set CONFIG_CBFS_SIZE=0x$(printf '%X' $BIOS_SIZE) in $CONFIG_FILE"
229+
if [ -n "$BOARD" ]; then
230+
echo "Or run: make BOARD=$BOARD fix_cbfs_ifd"
231+
fi
232+
exit 1
233+
fi
234+
fi
235+
236+
# CASE 2: CONFIG_CBFS_SIZE equals IFD size
237+
if [ $CBFS_SIZE_DEC -eq $BIOS_SIZE ]; then
238+
echo "✓ CONFIG_CBFS_SIZE exactly matches IFD BIOS region"
239+
fi
240+
241+
# CASE 3: CONFIG_CBFS_SIZE is smaller than IFD (normal case)
242+
if [ $CBFS_SIZE_DEC -lt $BIOS_SIZE ]; then
243+
FREE_SPACE=$(( BIOS_SIZE - CBFS_SIZE_DEC ))
244+
FREE_SPACE_KB=$((FREE_SPACE / 1024))
245+
FREE_BYTES=$(( FREE_SPACE % 1024 ))
246+
echo "✓ CONFIG_CBFS_SIZE fits within IFD BIOS region"
247+
248+
if [ $FREE_SPACE_KB -eq 0 ] && [ $FREE_SPACE -gt 0 ]; then
249+
echo " Unused IFD capacity: $FREE_SPACE bytes (< 1 KiB)"
250+
else
251+
echo " Unused IFD capacity: $FREE_SPACE_KB KiB"
252+
fi
253+
254+
# Only expand if explicitly requested via fix_cbfs_ifd
255+
if [ $FIX_MODE -eq 1 ]; then
256+
# CRITICAL: Intel SPI flash architecture limitation
257+
# Intel chipsets only memory-map the top 16 MiB of SPI flash to the fixed decode
258+
# window at 0xFF000000-0xFFFFFFFF (just below 4GB boundary). This is where the CPU
259+
# must execute XIP (Execute-In-Place) boot stages.
260+
#
261+
# cbfstool enforces DEFAULT_DECODE_WINDOW_MAX_SIZE = 16 MiB and will fail with
262+
# "Assertion `IS_HOST_SPACE_ADDRESS(host_space_address)' failed" when converting
263+
# XIP stages if CBFS_SIZE > 16 MiB, because the calculated addresses fall outside
264+
# the memory-mapped region.
265+
#
266+
# Exceeding 16 MiB will brick hardware - the CPU cannot fetch boot code from
267+
# addresses outside the decode window.
268+
#
269+
# References:
270+
# - coreboot util/cbfstool/cbfstool.c: DEFAULT_DECODE_WINDOW_MAX_SIZE
271+
# - coreboot util/cbfstool/fit.c: "FIT must reside in the top 16MiB"
272+
# - IS_HOST_SPACE_ADDRESS macro: checks if address is in memory-mapped space
273+
274+
MAX_CBFS_SIZE=0x1000000 # 16 MiB - Intel SPI decode window limit
275+
276+
# Calculate safe expansion target: min(IFD BIOS size, 16 MiB limit)
277+
if [ $BIOS_SIZE -gt $MAX_CBFS_SIZE ]; then
278+
TARGET_SIZE=$MAX_CBFS_SIZE
279+
TARGET_SIZE_KB=$((TARGET_SIZE / 1024))
280+
BIOS_SIZE_MB=$((BIOS_SIZE / 1024 / 1024))
281+
echo ""
282+
echo "⚠️ IFD BIOS region ($BIOS_SIZE_MB MiB) exceeds Intel 16 MiB decode window limit"
283+
echo " Capping CONFIG_CBFS_SIZE at 0x$(printf '%X' $MAX_CBFS_SIZE) ($TARGET_SIZE_KB KiB)"
284+
echo " Reason: Intel chipsets only memory-map top 16 MiB of SPI flash"
285+
echo " Exceeding this limit will brick hardware (CPU cannot execute boot code)"
286+
else
287+
TARGET_SIZE=$BIOS_SIZE
288+
fi
289+
290+
GAIN=$(( TARGET_SIZE - CBFS_SIZE_DEC ))
291+
GAIN_KB=$((GAIN / 1024))
292+
293+
# Only expand if gain is > 128 KiB
294+
if [ $GAIN -gt 131072 ]; then
295+
echo ""
296+
echo "🔧 Expanding CONFIG_CBFS_SIZE by $GAIN_KB KiB"
297+
sed -i "s/CONFIG_CBFS_SIZE=0x[0-9A-Fa-f]*/CONFIG_CBFS_SIZE=0x$(printf '%X' $TARGET_SIZE)/" "$CONFIG_FILE"
298+
echo "✓ Updated: CONFIG_CBFS_SIZE=0x$(printf '%X' $TARGET_SIZE)"
299+
exit 0
300+
else
301+
echo " Note: Expansion gain too small ($GAIN_KB KiB < 128 KiB threshold), keeping current size"
302+
fi
303+
fi
304+
fi
305+
306+
# Report CBFS free space
307+
if [ -n "$CBFSTOOL" ] && [ -f "$COREBOOT_DIR/coreboot.rom" ]; then
308+
echo ""
309+
CBFS_OUTPUT=$("$CBFSTOOL" "$COREBOOT_DIR/coreboot.rom" print 2>&1 || true)
310+
FREE_BYTES=$(echo "$CBFS_OUTPUT" | awk '/\(empty\)/ {sum += $4} END {print sum+0}')
311+
FREE_KB=$((FREE_BYTES / 1024))
312+
echo "CBFS Free Space: $FREE_BYTES bytes ($FREE_KB KiB)"
313+
fi
314+
315+
echo ""
316+
echo "==================================================================="
317+
echo "✓ Validation complete"
318+
echo "==================================================================="
319+
fi
320+
321+
exit 0

0 commit comments

Comments
 (0)