#!/bin/sh # probar-firmware-initramfs.sh — ¿entra al initramfs el firmware que ESTE hardware pidió, y sólo ese? # # Sin root y sin hardware: un `dmesg` de mentira (copiado del arranque real del operador), un árbol # de firmware de juguete y un cpio con su `/init`. Lo que se mide es lo que decide si una máquina # arranca con red o sin red. # # sh scripts/probar-firmware-initramfs.sh set -eu ROOT="$(cd "$(dirname "$0")/.." && pwd)" . "$ROOT/scripts/lib/firmware-initramfs.sh" W=$(mktemp -d); trap 'rm -rf "$W"' EXIT fail() { echo "✗ FALLO: $*"; exit 1; } # ── El dmesg: las tres formas que emite el kernel, más ruido que NO debe casar ───────────────── cat > "$W/dmesg.log" <<'EOF' [ 0.481237] iwlwifi 0000:00:14.3: Detected Intel(R) Wi-Fi 6 AX201 160MHz [ 0.492003] iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-QuZ-a0-hr-b0-77.ucode failed with error -2 [ 0.492101] iwlwifi 0000:00:14.3: no suitable firmware found! [ 0.501119] i915 0000:00:02.0: Direct firmware load for i915/tgl_dmc_ver2_12.bin failed with error -2 [ 0.511200] platform regulatory.0: firmware: failed to load regulatory.db (-2) [ 0.521300] r8169 0000:03:00.0: Falling back to sysfs fallback for: rtl_nic/rtl8168h-2.fw [ 0.600000] usb 1-2: new high-speed USB device number 3 using xhci_hcd [ 0.610000] EXT4-fs (nvme1n1p2): mounted filesystem with ordered data mode EOF echo "== 1: la lista sale del dmesg, completa y sin ruido ==" LISTA=$(firmware_pedido_del_dmesg "$W/dmesg.log") N=$(printf '%s\n' "$LISTA" | wc -l) [ "$N" = 4 ] || { printf '%s\n' "$LISTA"; fail "esperaba 4 pedidos y conté $N"; } for esperado in iwlwifi-QuZ-a0-hr-b0-77.ucode i915/tgl_dmc_ver2_12.bin regulatory.db rtl_nic/rtl8168h-2.fw; do printf '%s\n' "$LISTA" | grep -qx "$esperado" || fail "falta «$esperado» en la lista" done printf '%s\n' "$LISTA" | grep -q "xhci\|EXT4\|Detected\|no suitable" && fail "casó una línea que no es un pedido de firmware" echo " ✓ los 4 pedidos, en los tres formatos, y ninguna línea de ruido" # ── El árbol de firmware de la imagen y el initramfs de juguete ─────────────────────────────── mkdir -p "$W/img/lib/firmware/i915" "$W/img/lib/firmware/rtl_nic" "$W/img/lib/firmware/intel/iwlwifi" # El wifi vive adentro y se pide por el nombre de la RAÍZ: en la imagen eso es un enlace, igual que # los 2415 que `linux-firmware` reconstruye desde WHENCE. dd if=/dev/zero of="$W/img/lib/firmware/intel/iwlwifi/iwlwifi-QuZ-a0-hr-b0-77.ucode" bs=1k count=140 2>/dev/null ln -s intel/iwlwifi/iwlwifi-QuZ-a0-hr-b0-77.ucode "$W/img/lib/firmware/iwlwifi-QuZ-a0-hr-b0-77.ucode" dd if=/dev/zero of="$W/img/lib/firmware/i915/tgl_dmc_ver2_12.bin" bs=1k count=20 2>/dev/null dd if=/dev/zero of="$W/img/lib/firmware/regulatory.db" bs=1k count=7 2>/dev/null # `rtl_nic/rtl8168h-2.fw` NO se pone: es el caso «el hardware lo pide y la imagen no lo trae». mkdir -p "$W/ird/bin" printf '#!/bin/sh\necho init de juguete\n' > "$W/ird/init"; chmod 755 "$W/ird/init" printf 'busybox\n' > "$W/ird/bin/busybox" ( cd "$W/ird" && find . | cpio -o -H newc --quiet | gzip -9 > "$W/pivote.cpio.gz" ) ANTES=$(wc -c < "$W/pivote.cpio.gz") echo "== 2: el injerto pone lo que hay, nombra lo que falta y conserva el /init ==" SALIDA=$(firmware_al_initramfs "$W/pivote.cpio.gz" "$W/img" "$W/dmesg.log") printf '%s\n' "$SALIDA" | sed 's/^/ /' echo "$SALIDA" | grep -q "3 blob(s) injertados" || fail "esperaba 3 injertados (el cuarto no está en la imagen)" echo "$SALIDA" | grep -q "rtl_nic/rtl8168h-2.fw.*no lo trae" || fail "no nombró el blob que falta" DENTRO=$(gzip -dc "$W/pivote.cpio.gz" | cpio -t --quiet 2>/dev/null) for e in "lib/firmware/iwlwifi-QuZ-a0-hr-b0-77.ucode" "lib/firmware/i915/tgl_dmc_ver2_12.bin" "lib/firmware/regulatory.db" "init" "bin/busybox"; do printf '%s\n' "$DENTRO" | grep -qx "\(\./\)\?$e" || { printf '%s\n' "$DENTRO"; fail "«$e» no quedó dentro del initramfs"; } done printf '%s\n' "$DENTRO" | grep -q "rtl8168h" && fail "metió un blob que la imagen no tiene" echo " ✓ los 3 adentro, el /init y el busybox intactos, y nada inventado" # El wifi se pide por el nombre de la raíz y en la imagen es un ENLACE: adentro tiene que ir el # CONTENIDO, porque en el initramfs no hay a dónde apuntar. Si viajara como enlace, el driver # abriría un symlink roto — que falla igual que un fichero ausente y se diagnostica peor. mkdir -p "$W/ver" && ( cd "$W/ver" && gzip -dc "$W/pivote.cpio.gz" | cpio -idm --quiet ) [ -f "$W/ver/lib/firmware/iwlwifi-QuZ-a0-hr-b0-77.ucode" ] || fail "el .ucode no es un fichero regular adentro" [ -L "$W/ver/lib/firmware/iwlwifi-QuZ-a0-hr-b0-77.ucode" ] && fail "viajó como enlace: adentro no hay a dónde apuntar" [ "$(wc -c < "$W/ver/lib/firmware/iwlwifi-QuZ-a0-hr-b0-77.ucode")" = "143360" ] || fail "el .ucode llegó truncado" echo " ✓ el blob que en la imagen es un enlace viaja como FICHERO (143360 B)" [ "$(wc -c < "$W/pivote.cpio.gz")" -gt "$ANTES" ] || fail "el initramfs no creció" [ -r "$W/pivote.cpio.gz.antes-del-firmware" ] || fail "no dejó respaldo del initramfs original" echo " ✓ creció de $ANTES a $(wc -c < "$W/pivote.cpio.gz") bytes, con respaldo del original" echo "== 3: BRAZO DE CONTROL — una máquina donde no faltó nada no se toca ==" cat > "$W/dmesg-ok.log" <<'EOF' [ 0.492003] iwlwifi 0000:00:14.3: loaded firmware version 77.a0a4b1e2.0 op_mode iwlmvm [ 0.501119] i915 0000:00:02.0: [drm] Finished loading DMC firmware i915/tgl_dmc_ver2_12.bin EOF ( cd "$W/ird" && find . | cpio -o -H newc --quiet | gzip -9 > "$W/limpio.cpio.gz" ) SUMA_ANTES=$(sha256sum < "$W/limpio.cpio.gz") SAL2=$(firmware_al_initramfs "$W/limpio.cpio.gz" "$W/img" "$W/dmesg-ok.log") echo "$SAL2" | grep -q "nada que injertar" || { echo "$SAL2"; fail "debió decir que no hay nada que injertar"; } [ "$(sha256sum < "$W/limpio.cpio.gz")" = "$SUMA_ANTES" ] || fail "tocó un initramfs que no hacía falta tocar" echo " ✓ sin pedidos fallidos: el initramfs queda BIT A BIT igual" echo "== 4: lo que no se deja hacer ==" cat > "$W/dmesg-malo.log" <<'EOF' [ 0.1] x: Direct firmware load for ../../etc/shadow failed with error -2 [ 0.2] y: Direct firmware load for /etc/passwd failed with error -2 EOF ( cd "$W/ird" && find . | cpio -o -H newc --quiet | gzip -9 > "$W/malo.cpio.gz" ) # ⚠ Con `2>&1`: los avisos de ruta sospechosa van por stderr —son avisos, no salida— y `$(...)` # sólo captura stdout. Sin esto el test fallaba sobre un código que hacía lo correcto. SAL3=$(firmware_al_initramfs "$W/malo.cpio.gz" "$W/img" "$W/dmesg-malo.log" 2>&1 || true) echo "$SAL3" | grep -q "sospechosa" || { echo "$SAL3"; fail "no descartó las rutas con .. ni las absolutas"; } echo " ✓ una ruta con «..» o absoluta se descarta nombrándola (el dmesg es texto, y esto escribe ficheros)" echo echo "✓✓ probar-firmware-initramfs: el firmware que pidió ESTE hardware entra, y sólo ese"