Skip to content

Commit 8f9dcb0

Browse files
committed
initrd/tests/wp: add WP test and debug scripts
wp-debug: collects flashprog wp status/list/verbose output, HSFS/FLOCKDN state via setpci, /proc/mtd, and dmesg SPI lines into a single report. Run and attach when filing flash write protection issues. wp-test: functional test suite for the opaque/hwseq WP path on Intel PCH100+ hardware. Tests 1-6 validate read paths (wp status, wp list, FLOCKDN detection, mode accuracy). Tests 7-8 exercise write paths (wp disable, wp range + wp enable) and are skipped when FLOCKDN=1 prevents PRR modification. Both scripts strip --ifd, --image <region>, and -i <region> from CONFIG_FLASH_OPTIONS before passing to flashprog wp subcommands, which do not accept layout flags. Expected results: Before lock_chip (FLOCKDN=0): PASS=8 FAIL=0 SKIP=0 After lock_chip (FLOCKDN=1): PASS=6 FAIL=0 SKIP=2 Signed-off-by: Thierry Laurion <insurgo@riseup.net>
1 parent 4f10f45 commit 8f9dcb0

File tree

2 files changed

+275
-0
lines changed

2 files changed

+275
-0
lines changed

initrd/tests/wp/wp-debug

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#!/bin/sh
2+
# shellcheck disable=SC2086 # $PROG_OPTS intentionally unquoted for word splitting
3+
# WP debug info collector for internal Intel SPI (opaque/hwseq) programmer.
4+
# Run this on target hardware and paste output back for analysis.
5+
#
6+
# Usage: wp-debug [flashprog-programmer-opts]
7+
# e.g. wp-debug --programmer internal
8+
# wp-debug --programmer internal --ifd -i bios
9+
#
10+
# If no args given, uses CONFIG_FLASH_OPTIONS from environment (stripped to
11+
# programmer args only).
12+
13+
set -e
14+
15+
PROG_OPTS="${*}"
16+
17+
if [ -z "$PROG_OPTS" ] && [ -n "$CONFIG_FLASH_OPTIONS" ]; then
18+
PROG_OPTS="$(echo "$CONFIG_FLASH_OPTIONS" | \
19+
sed 's/^flashprog[[:space:]]*//' | \
20+
sed 's/--progress[[:space:]]*//')"
21+
fi
22+
23+
if [ -z "$PROG_OPTS" ]; then
24+
PROG_OPTS="--programmer internal"
25+
fi
26+
27+
# Strip layout flags: 'flashprog wp' subcommands do not accept --ifd or -i.
28+
PROG_OPTS="$(echo "$PROG_OPTS" | \
29+
sed 's/--ifd[[:space:]]*//' | \
30+
sed 's/--image[[:space:]]*[a-z][a-z]*[[:space:]]*//g' | \
31+
sed 's/-i[[:space:]]*[a-z][a-z]*[[:space:]]*//g' | \
32+
sed 's/[[:space:]]*$//')"
33+
34+
SEP="----------------------------------------"
35+
36+
echo "$SEP"
37+
echo "flashprog WP debug report"
38+
echo "Date: $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
39+
echo "Programmer opts: $PROG_OPTS"
40+
echo "$SEP"
41+
42+
echo ""
43+
echo "[1] flashprog wp status"
44+
echo "$SEP"
45+
flashprog wp status $PROG_OPTS 2>&1 || echo "(exit code $?)"
46+
47+
echo ""
48+
echo "[2] flashprog wp list"
49+
echo "$SEP"
50+
flashprog wp list $PROG_OPTS 2>&1 || echo "(exit code $?)"
51+
52+
echo ""
53+
echo "[3] flashprog wp status --verbose (verbose - shows PRR register reads)"
54+
echo "$SEP"
55+
flashprog wp status $PROG_OPTS --verbose 2>&1 || echo "(exit code $?)"
56+
57+
echo ""
58+
echo "[4] HSFS register (FLOCKDN state)"
59+
echo "$SEP"
60+
# HSFS is at offset 0x04 in the PCH SPI BAR.
61+
# The SPI BAR base is readable from PCI config space:
62+
# Bus 0, Dev 31, Fn 5 (LPC/SPI), offset 0x10 (SPIBAR MMIO)
63+
# We use setpci if available, otherwise note it's unavailable.
64+
if command -v setpci >/dev/null 2>&1; then
65+
spibar_raw="$(setpci -s 00:1f.5 0x10.L 2>/dev/null)" || spibar_raw=""
66+
if [ -n "$spibar_raw" ]; then
67+
echo "PCI 00:1f.5 SPIBAR (0x10): 0x${spibar_raw}"
68+
# Mask off lower 12 bits (BAR flags) to get MMIO base
69+
spibar_base=$(( 0x${spibar_raw} & 0xFFFFF000 ))
70+
printf "SPIBAR MMIO base: 0x%x\n" "$spibar_base"
71+
echo "Note: HSFS at SPIBAR+0x04 - use devmem2 or /dev/mem if available"
72+
else
73+
echo "setpci: could not read 00:1f.5 SPIBAR"
74+
fi
75+
else
76+
echo "setpci not available - cannot read raw SPIBAR"
77+
fi
78+
79+
echo ""
80+
echo "[5] /proc/mtd (flash regions visible to kernel)"
81+
echo "$SEP"
82+
cat /proc/mtd 2>/dev/null || echo "(not available)"
83+
84+
echo ""
85+
echo "[6] dmesg SPI/ICH lines"
86+
echo "$SEP"
87+
dmesg 2>/dev/null | grep -iE 'spi|ich|flashprog|lockdn|flockdn|protected.range' | tail -30 || echo "(dmesg not available)"
88+
89+
echo ""
90+
echo "$SEP"
91+
echo "End of WP debug report"
92+
echo "$SEP"

initrd/tests/wp/wp-test

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
#!/bin/sh
2+
# shellcheck disable=SC2086 # $PROG_OPTS intentionally unquoted for word splitting
3+
# WP functional test for internal Intel SPI (opaque/hwseq) programmer.
4+
# Tests wp status, wp list, and command exit codes.
5+
#
6+
# Usage: wp-test [flashprog-programmer-opts]
7+
#
8+
# On PCH100+ (Meteor Lake etc.) coreboot pre-programs PRRs with WP=1 as
9+
# preparation for the kexec lockdown, but FLOCKDN=0 during heads execution
10+
# means that protection is not yet enforced. wp status correctly reports
11+
# 'disabled' in this case. wp enable/disable commands are tested for exit
12+
# code only; persistence across invocations requires FLOCKDN=1.
13+
14+
PASS=0
15+
FAIL=0
16+
SKIP=0
17+
18+
SEP="========================================"
19+
20+
pass() { echo "PASS: $1"; PASS=$(( PASS + 1 )); }
21+
fail() { echo "FAIL: $1"; FAIL=$(( FAIL + 1 )); }
22+
skip() { echo "SKIP: $1"; SKIP=$(( SKIP + 1 )); }
23+
info() { echo "INFO: $1"; }
24+
25+
PROG_OPTS="${*}"
26+
if [ -z "$PROG_OPTS" ] && [ -n "$CONFIG_FLASH_OPTIONS" ]; then
27+
PROG_OPTS="$(echo "$CONFIG_FLASH_OPTIONS" | \
28+
sed 's/^flashprog[[:space:]]*//' | \
29+
sed 's/--progress[[:space:]]*//')"
30+
fi
31+
if [ -z "$PROG_OPTS" ]; then
32+
PROG_OPTS="--programmer internal"
33+
fi
34+
35+
# Strip layout flags: 'flashprog wp' subcommands do not accept --ifd or -i.
36+
PROG_OPTS="$(echo "$PROG_OPTS" | \
37+
sed 's/--ifd[[:space:]]*//' | \
38+
sed 's/--image[[:space:]]*[a-z][a-z]*[[:space:]]*//g' | \
39+
sed 's/-i[[:space:]]*[a-z][a-z]*[[:space:]]*//g' | \
40+
sed 's/[[:space:]]*$//')"
41+
42+
43+
echo "$SEP"
44+
echo "flashprog WP functional test"
45+
echo "Programmer opts: $PROG_OPTS"
46+
echo "$SEP"
47+
echo ""
48+
49+
# --- Test 1: wp status exits cleanly ---
50+
echo "--- Test 1: wp status succeeds on opaque/hwseq programmer ---"
51+
status_out="$(flashprog wp status $PROG_OPTS 2>&1)"
52+
status_rc=$?
53+
echo "$status_out"
54+
if [ "$status_rc" -eq 0 ]; then
55+
pass "wp status exit code 0 (opaque programmer WP read path works)"
56+
else
57+
fail "wp status failed (rc=$status_rc) - opaque WP dispatch not reached"
58+
fi
59+
60+
# --- Test 2: wp status reports a recognised protection mode ---
61+
echo ""
62+
echo "--- Test 2: wp status reports a valid PCH PRR protection mode ---"
63+
if echo "$status_out" | grep -qiE 'Protection mode:'; then
64+
mode="$(echo "$status_out" | grep -i 'Protection mode:' | head -1)"
65+
pass "wp status shows '$mode'"
66+
else
67+
fail "wp status missing 'Protection mode:' field - ich_hwseq_wp_read_cfg not reached"
68+
info "Raw output: $status_out"
69+
fi
70+
71+
# --- Test 3: wp list exits cleanly ---
72+
echo ""
73+
echo "--- Test 3: wp list succeeds on opaque/hwseq programmer ---"
74+
list_out="$(flashprog wp list $PROG_OPTS 2>&1)"
75+
list_rc=$?
76+
echo "$list_out"
77+
if [ "$list_rc" -eq 0 ]; then
78+
pass "wp list exit code 0 (ich_hwseq_wp_get_ranges reached)"
79+
else
80+
fail "wp list failed (rc=$list_rc)"
81+
fi
82+
83+
# --- Test 4: wp list returns chip-size-derived ranges ---
84+
echo ""
85+
echo "--- Test 4: wp list returns power-of-2 top ranges derived from chip size ---"
86+
range_count="$(echo "$list_out" | grep -c 'start=0x' || true)"
87+
if [ "$range_count" -gt 2 ]; then
88+
chip_size_hex="$(echo "$list_out" | grep 'length=' | tail -1 | \
89+
grep -oE 'length=0x[0-9a-fA-F]+' | grep -oE '0x[0-9a-fA-F]+')"
90+
pass "wp list returned $range_count ranges; chip size $chip_size_hex"
91+
else
92+
fail "wp list returned $range_count ranges (expected >2 power-of-2 entries)"
93+
info "Raw output: $list_out"
94+
fi
95+
96+
# --- Test 5: FLOCKDN state ---
97+
echo ""
98+
echo "--- Test 5: PCH SPI FLOCKDN state ---"
99+
verbose_out="$(flashprog wp status $PROG_OPTS --verbose 2>&1)"
100+
if echo "$verbose_out" | grep -qi 'locked down\|Configuration is locked'; then
101+
info "FLOCKDN is SET - PRRs are hardware-locked, protection is enforced"
102+
flockdn_set=1
103+
else
104+
info "FLOCKDN is NOT set - PRR protection not enforced (pre-kexec state)"
105+
info "coreboot pre-programs PRRs but locks them only at kexec time"
106+
flockdn_set=0
107+
fi
108+
pass "FLOCKDN detection completed (see INFO above)"
109+
110+
# --- Test 6: wp status reports correct enforcement state ---
111+
echo ""
112+
echo "--- Test 6: wp status reflects actual enforcement (FLOCKDN-aware) ---"
113+
if [ "$flockdn_set" -eq 0 ]; then
114+
# FLOCKDN=0: ich9_set_pr clears WP bits successfully, so no protection
115+
# is actually enforced regardless of what coreboot wrote to PRRs.
116+
if echo "$status_out" | grep -qi 'disabled'; then
117+
pass "wp status reports 'disabled' (FLOCKDN=0, protection not enforced)"
118+
else
119+
fail "wp status should report 'disabled' when FLOCKDN=0 (PRRs are not locked)"
120+
info "Status output: $status_out"
121+
fi
122+
else
123+
# FLOCKDN=1: ich9_set_pr cannot clear WP bits, protection is enforced.
124+
if echo "$status_out" | grep -qi 'hardware'; then
125+
pass "wp status reports 'hardware' (FLOCKDN=1, protection enforced)"
126+
else
127+
info "wp status: $status_out"
128+
pass "FLOCKDN=1 detected; wp status result noted above"
129+
fi
130+
fi
131+
132+
# --- Tests 7+: wp command exit-code tests ---
133+
echo ""
134+
echo "--- Test 7: wp disable exits cleanly ---"
135+
dis_out="$(flashprog wp disable $PROG_OPTS 2>&1)"
136+
dis_rc=$?
137+
echo "$dis_out"
138+
if [ "$dis_rc" -eq 0 ]; then
139+
pass "wp disable returned exit code 0"
140+
elif echo "$dis_out" | grep -qi 'locked\|FLOCKDN\|Configuration is locked'; then
141+
skip "wp disable blocked by FLOCKDN (PRR registers are frozen)"
142+
else
143+
fail "wp disable failed (rc=$dis_rc)"
144+
fi
145+
146+
echo ""
147+
echo "--- Test 8: wp range + wp enable exit cleanly ---"
148+
chip_size_hex="$(echo "$list_out" | grep 'length=' | tail -1 | \
149+
grep -oE 'length=0x[0-9a-fA-F]+' | grep -oE '0x[0-9a-fA-F]+')"
150+
if [ -z "$chip_size_hex" ]; then
151+
skip "wp range/enable - could not determine chip size from wp list output"
152+
else
153+
chip_size=$(( chip_size_hex ))
154+
range_start=$(( chip_size - 4096 ))
155+
range_len=4096
156+
printf "Using top 4KB range: start=0x%x len=0x%x\n" "$range_start" "$range_len"
157+
range_arg="$(printf '0x%x,0x%x' "$range_start" "$range_len")"
158+
range_out="$(flashprog wp range $PROG_OPTS "$range_arg" 2>&1)"
159+
range_rc=$?
160+
echo "$range_out"
161+
en_out="$(flashprog wp enable $PROG_OPTS 2>&1)"
162+
en_rc=$?
163+
echo "$en_out"
164+
if [ "$range_rc" -eq 0 ] && [ "$en_rc" -eq 0 ]; then
165+
pass "wp range + wp enable returned exit code 0"
166+
if [ "$flockdn_set" -eq 0 ]; then
167+
info "FLOCKDN=0: wp enable writes PRR but protection is not persistently" \
168+
"enforced; FLOCKDN is set only at kexec time by coreboot/heads"
169+
fi
170+
elif echo "$range_out$en_out" | grep -qi 'locked\|FLOCKDN\|Configuration is locked'; then
171+
skip "wp range/enable blocked by FLOCKDN"
172+
else
173+
fail "wp range/enable failed (range_rc=$range_rc en_rc=$en_rc)"
174+
fi
175+
fi
176+
177+
# --- Summary ---
178+
echo ""
179+
echo "$SEP"
180+
echo "Results: PASS=$PASS FAIL=$FAIL SKIP=$SKIP"
181+
echo "$SEP"
182+
183+
[ "$FAIL" -eq 0 ]

0 commit comments

Comments
 (0)