Skip to content

Commit dfd83ea

Browse files
committed
gk7205v200: failsafe rescue mode + flash-free DRAM boot counter + crash log
Builds the recovery loop on top of the pstore region (#2392) and the bootcount arm/disarm (#2391), converting the counter off the NOR env. - Move the boot counter from the U-Boot env to a single DRAM word at 0x41f20000 (a no-map reserved region, board DTS): S99bootok clears it through /dev/mem once majestic is proven healthy, and sysupgrade arms it the same way. Writing the env every boot is NOR wear and a mid-write brick risk; a healthy boot now writes zero flash. On a U-Boot/kernel without the region the write lands in spare reserved RAM -- harmless. - rcS gains a failsafe branch: when U-Boot appends "failsafe" to the cmdline after bootlimit failed boots, bring up only logging, mdev, networking and SSH -- skip the SDK and the streamer -- so a crashlooping camera lands reachable instead of looping. The overlay stays mounted (claim intact); the counter is reset on entry so failsafe does not re-escalate. - S98crashlog preserves a crashed boot's pstore log into /etc/crash on the next normal boot (gzipped, capped to the latest, pstore ring then freed) and rcS drops a breadcrumb when it fell into failsafe, for the WebUI to offer the owner. Overlay-only, no flash on a clean boot. Proven on hardware, both allocators: gk7205v200 (64M) and gk7205v300 (128M) -- crashloop escalates to failsafe and self-recovers, pstore panic is harvested, counter resets on a healthy boot.
1 parent 26e5ccd commit dfd83ea

5 files changed

Lines changed: 116 additions & 49 deletions

File tree

br-ext-chip-goke/board/gk7205v200/gk7205v300.generic.config

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2393,7 +2393,16 @@ CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=1
23932393
# CONFIG_QNX4FS_FS is not set
23942394
# CONFIG_QNX6FS_FS is not set
23952395
# CONFIG_ROMFS_FS is not set
2396-
# CONFIG_PSTORE is not set
2396+
CONFIG_PSTORE=y
2397+
# Compression is a mandatory choice on this 4.9 kernel (it has no "none"); ZLIB
2398+
# is its default and the only one whose workspace is preallocated at register
2399+
# time, so it stays safe to compress from inside a panic. The others stay off.
2400+
CONFIG_PSTORE_ZLIB_COMPRESS=y
2401+
# CONFIG_PSTORE_LZO_COMPRESS is not set
2402+
# CONFIG_PSTORE_LZ4_COMPRESS is not set
2403+
CONFIG_PSTORE_CONSOLE=y
2404+
CONFIG_PSTORE_PMSG=y
2405+
CONFIG_PSTORE_RAM=y
23972406
# CONFIG_SYSV_FS is not set
23982407
# CONFIG_UFS_FS is not set
23992408
CONFIG_NETWORK_FILESYSTEMS=y
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/bin/sh
2+
# After a crash, preserve the pstore log where the WebUI can offer it to the
3+
# owner, then free the pstore ring for the next one. pstore itself survives a
4+
# warm reset but not a cold power-cycle; copying the record into the overlay
5+
# lets the notice and the download outlive the plug being pulled. The bundle is
6+
# capped to the latest crash (overwritten each time), so the overlay never grows
7+
# without bound, and a clean boot -- no records -- writes nothing at all. This is
8+
# the capture half of the owner-pull crash report (WebUI is the other half); it
9+
# pairs with the failsafe breadcrumb in rcS and with S99bootok.
10+
case "$1" in
11+
start|"") ;;
12+
*) exit 0 ;;
13+
esac
14+
15+
PSTORE=/sys/fs/pstore
16+
CRASH=/etc/crash
17+
18+
# Nothing captured last boot -> ordinary boot, leave no trace.
19+
ls "$PSTORE"/dmesg-* >/dev/null 2>&1 || exit 0
20+
mkdir -p "$CRASH" 2>/dev/null || exit 0
21+
22+
# busybox tar has no built-in -z here, so compress through gzip and confirm the
23+
# result is non-empty before trusting it.
24+
if tar cf - -C "$PSTORE" . 2>/dev/null | gzip > "$CRASH/crash.tar.gz.tmp" 2>/dev/null \
25+
&& [ -s "$CRASH/crash.tar.gz.tmp" ]; then
26+
mv "$CRASH/crash.tar.gz.tmp" "$CRASH/crash.tar.gz" 2>/dev/null
27+
{
28+
echo "utc=$(date -u '+%Y-%m-%d %H:%M:%S' 2>/dev/null)"
29+
echo "records=$(ls -1 "$PSTORE"/dmesg-* 2>/dev/null | wc -l)"
30+
echo "bytes=$(wc -c < "$CRASH/crash.tar.gz" 2>/dev/null)"
31+
} > "$CRASH/pending" 2>/dev/null
32+
logger -s -t crashlog "preserved pstore crash log to $CRASH/crash.tar.gz" 2>/dev/null
33+
rm -f "$PSTORE"/dmesg-* 2>/dev/null
34+
else
35+
rm -f "$CRASH/crash.tar.gz.tmp" 2>/dev/null
36+
fi
37+
exit 0
Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,30 @@
11
#!/bin/sh
2-
# Disarm the bootloader's boot-count recovery once the camera has reached a
3-
# healthy userspace. sysupgrade sets upgrade_available=1 before a flash; a
4-
# U-Boot that supports boot-count recovery keeps counting boots until this
5-
# clears the flag, and reflashes a known-good image if a broken one never gets
6-
# here. On a U-Boot without boot-count logic the variable is ignored, so this
7-
# is a no-op beyond one environment read. A normal (unarmed) boot costs a
8-
# single fw_printenv and writes nothing.
2+
# Clear the bootloader's boot counter once the camera has reached a healthy
3+
# userspace. U-Boot increments a counter every boot and, if it is never cleared,
4+
# boots FAILSAFE (network + SSH only) after bootlimit tries -- so a camera that
5+
# boots but never streams lands somewhere reachable instead of looping forever.
6+
#
7+
# The counter is a single DRAM word (a no-map reserved region at 0x41f20000, see
8+
# the board DTS), NOT the U-Boot env: we clear it through /dev/mem, writing no
9+
# flash at all. On a U-Boot/kernel without this the write just lands in spare
10+
# reserved RAM nobody reads, so it is harmless. A normal boot writes nothing to
11+
# flash, ever.
912
case "$1" in
1013
start|"") ;;
1114
*) exit 0 ;;
1215
esac
1316

14-
command -v fw_printenv >/dev/null 2>&1 || exit 0
15-
[ "$(fw_printenv -n upgrade_available 2>/dev/null)" = "1" ] || exit 0
17+
command -v devmem >/dev/null 2>&1 || exit 0
18+
BOOTCOUNT_ADDR=0x41f20000
1619

17-
# Armed. Disarm only once the new image is PROVEN healthy -- and prove it,
18-
# don't just glimpse it: pidof sees majestic the instant S95majestic forks,
19-
# before the pipeline is up, so a streamer that crashes during startup would
20-
# look healthy for a moment. Require it to stay up across a sustained window;
21-
# if it dies the window restarts, and if it never holds we leave the flag armed
22-
# so U-Boot escalates to recovery instead of looping on an image that boots but
23-
# does not stream. An image built without majestic is healthy once it boots
24-
# this far. Backgrounded so nothing here holds up boot.
20+
# Clear only once the image is PROVEN healthy -- and prove it, don't just glimpse
21+
# it: pidof sees majestic the instant S95majestic forks, before the pipeline is
22+
# up, so a streamer that crashes during startup would look healthy for a moment.
23+
# Require it to stay up across a sustained window; if it dies the window
24+
# restarts, and if it never holds we leave the counter so U-Boot escalates to
25+
# failsafe instead of looping on an image that boots but does not stream. An
26+
# image built without majestic is healthy once it boots this far. Backgrounded
27+
# so nothing here holds up boot.
2528
{
2629
need=8 # consecutive good checks (~16s) before trusting the image
2730
ok=0
@@ -30,13 +33,7 @@ command -v fw_printenv >/dev/null 2>&1 || exit 0
3033
if [ ! -x /usr/bin/majestic ] || pidof majestic >/dev/null 2>&1; then
3134
ok=$((ok + 1))
3235
if [ "$ok" -ge "$need" ]; then
33-
if fw_setenv upgrade_available 0 2>/dev/null &&
34-
fw_setenv bootcount 0 2>/dev/null; then
35-
exit 0
36-
fi
37-
# Environment readable but not writable: say so, or every
38-
# healthy boot silently leaves U-Boot counting toward recovery.
39-
logger -s -t bootok "healthy but could not clear upgrade_available (U-Boot env not writable) -- bootloader will keep counting" 2>/dev/null
36+
devmem "$BOOTCOUNT_ADDR" 32 0 2>/dev/null
4037
exit 0
4138
fi
4239
else
@@ -45,5 +42,5 @@ command -v fw_printenv >/dev/null 2>&1 || exit 0
4542
sleep 2
4643
tries=$((tries + 1))
4744
done
48-
logger -s -t bootok "majestic did not stay up; leaving boot-count recovery armed" 2>/dev/null
45+
logger -s -t bootok "majestic did not stay up; leaving the boot counter for U-Boot to escalate to failsafe" 2>/dev/null
4946
} &

general/overlay/etc/init.d/rcS

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,36 @@ export UPGRADE=$(fw_printenv -n upgrade)
55
# that an unset TZ would use, so a missing /etc/TZ must leave TZ alone.
66
[ -r /etc/TZ ] && export TZ=$(cat /etc/TZ)
77

8+
# Failsafe rescue mode. The bootloader appends "failsafe" to the kernel command
9+
# line after repeated failed boots (and it can be set by hand); here we bring up
10+
# only logging, device nodes, networking and SSH, and skip the SDK (S70vendor),
11+
# the streamer (S95majestic) and every other service, so nothing that could be
12+
# crashlooping runs. The overlay is still mounted (see /init), so the camera
13+
# stays claimed -- SSH auth works -- and its config and /sys/fs/pstore logs are
14+
# left intact for inspection. Nothing is wiped: a reachable camera to diagnose
15+
# and fix, instead of an unreachable one someone has to reset "blind".
16+
if grep -qw failsafe /proc/cmdline 2>/dev/null; then
17+
echo "*** FAILSAFE: bringing up network + SSH only, services skipped ***"
18+
for s in S01syslogd S29pstore S38mdev S31hostname S40network S50dropbear; do
19+
[ -x "/etc/init.d/$s" ] && "/etc/init.d/$s" start
20+
done
21+
# Leave a breadcrumb the WebUI reads on the next normal boot, so a fault that
22+
# left no pstore panic -- a service that simply never came up -- still shows
23+
# the owner "this camera recovered from a crash". Overlay-only, no flash.
24+
mkdir -p /etc/crash 2>/dev/null && \
25+
printf 'reason=bootlimit\nutc=%s\n' \
26+
"$(date -u '+%Y-%m-%d %H:%M:%S' 2>/dev/null)" \
27+
> /etc/crash/failsafe 2>/dev/null
28+
29+
# Reaching failsafe counts as "handled": clear the bootloader's DRAM boot
30+
# counter (no flash) so once the operator fixes the fault and reboots, the
31+
# next boot is a normal one. If they reboot without fixing it, three more
32+
# failed boots simply land back here. Cold power-cycle also clears it.
33+
command -v devmem >/dev/null 2>&1 && devmem 0x41f20000 32 0 2>/dev/null
34+
echo "*** FAILSAFE: ready. SSH in to inspect (pstore, config) and recover. ***"
35+
exit 0
36+
fi
37+
838
for i in /etc/init.d/S??*; do
939
[ ! -f "$i" ] && continue
1040
case "$i" in

general/overlay/usr/sbin/sysupgrade

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,9 @@ die() {
9898
if [ "1" = "$flash_touched" ] || [ "1" = "$_ramfs_phase" ]; then
9999
reboot_system
100100
else
101-
# Nothing was written, so the camera is unchanged: drop any recovery arm
102-
# set before the flash phase, or a boot that never flashed would still be
103-
# counted. (A failure AFTER enter_ramfs reboots via the branch above
104-
# instead, and S99bootok disarms on the next healthy boot -- bootcount
105-
# only advances one per reboot and the limit is several, so no false
106-
# escalation.)
107-
command -v fw_setenv >/dev/null 2>&1 && fw_setenv upgrade_available 0 2>/dev/null
101+
# Nothing was written, so the camera is unchanged. The DRAM boot counter
102+
# is cleared only on the flash path below (and clearing it is harmless
103+
# even if no flash follows), so there is nothing to undo here.
108104
restore_resources
109105
# Only ever remove a lock this process owns. die() is reachable before
110106
# create_lock has run (get_system_info, called from print_sysinfo, can
@@ -1348,23 +1344,21 @@ if [ "1" = "$skip_reboot" ] && [ "1" = "$root_on_flash" ] &&
13481344
sleep "$abort_wait"
13491345
fi
13501346

1351-
# Arm the bootloader's boot-count recovery for this flash. If the freshly
1352-
# written image never reaches a healthy userspace, a U-Boot that supports
1353-
# boot-count recovery falls back to reflashing a known-good image instead of
1354-
# rebooting into the broken one for ever; S99bootok disarms this once majestic
1355-
# is up. On an older or vendor-supplied U-Boot with no boot-count logic these
1356-
# are unused environment variables it ignores, so arming is harmless there.
1357-
# Set here, on the still-normal system, before enter_ramfs moves the MTD
1358-
# environment out of reach.
1347+
# Clear the bootloader's boot counter so the freshly written image starts with a
1348+
# clean slate: if it never reaches a healthy userspace, U-Boot boots FAILSAFE
1349+
# (network + SSH only) after bootlimit tries instead of looping on a broken
1350+
# image, and S99bootok clears the counter again once majestic is up. The counter
1351+
# is a no-map DRAM word (0x41f20000, see the board DTS) -- this writes no flash,
1352+
# unlike an env-backed counter whose per-boot writes wear NOR and risk a bricked
1353+
# env on a power cut. On a U-Boot/kernel without it the write lands in spare
1354+
# reserved RAM nobody reads. Done on the still-normal system, before enter_ramfs
1355+
# moves things out of reach.
1356+
command -v devmem >/dev/null 2>&1 && devmem 0x41f20000 32 0 2>/dev/null
1357+
1358+
# A camera that ever ran the pre-verify U-Boot has verify=n saved in flash,
1359+
# where it still overrides the built-in default; drop the key so the kernel
1360+
# image is CRC-checked on boot again.
13591361
if command -v fw_setenv >/dev/null 2>&1; then
1360-
if fw_setenv upgrade_available 1 2>/dev/null && fw_setenv bootcount 0 2>/dev/null; then
1361-
:
1362-
else
1363-
echo_c 33 "Warning: could not arm boot-count recovery (U-Boot env not writable?); the upgrade proceeds without it."
1364-
fi
1365-
# A camera that ever ran the pre-verify U-Boot has verify=n saved in flash,
1366-
# where it still overrides the built-in default; drop the key so the kernel
1367-
# image is CRC-checked on boot again.
13681362
fw_setenv verify 2>/dev/null ||
13691363
echo_c 33 "Warning: could not clear a saved verify=n; a corrupt kernel may not be caught on boot."
13701364
fi

0 commit comments

Comments
 (0)