tawasuyu

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1572Shell 203Rust 109Markdown 993728 Definiciones

⎇ 2907e74a8f41b07296102c14adc378cea53f57f9 recipes/firmware-metal.toml TOML · 172 líneas · 9.5 KB Historia crudo

feat(firmware): el firmware de una PC x86 COMPLETO, y el hotplug declarado en base 0c84ec1f5 · sergio ·

1# firmware-metal — el firmware de UNA MÁQUINA PERSONAL x86, completo.
2#
3# ══ POR QUÉ ESTA RECETA REEMPLAZA A `firmware-intel` ═══════════════════════════════════════════
4# `firmware-intel` (publicada unas horas antes, el 2026-10-15) llevaba 120 MB: iwlwifi, i915,
5# regulatory y SOF. Dejaba afuera **el ethernet, el bluetooth y las GPU que no son Intel**, y el
6# operador lo dijo sin anestesia:
7#
8#   *«que ni ethernet funciona. no sé por qué me lo pones tan pobre, como si no supieras lo que
9#    necesita un sistema»*
10#
11# Tenía razón, y el error era de método: recorté por tamaño **antes de medir**. Con el árbol ya
12# sellado se puede medir, y lo que una PC puede necesitar es más de la mitad del tarball. La lista de
13# abajo sale de `du -sh` sobre el artefacto de `linux-firmware`, familia por familia, no de criterio.
14#
15# ══ LO QUE ENTRA, CON SU PESO MEDIDO (2026-10-15, linux-firmware 20260910) ═════════════════════
16#
17#   WIFI ─ 641 MB
18#     intel/iwlwifi 287M · mrvl 81M · mediatek 71M · ath11k 70M · ath12k 47M · rtw89 34M
19#     ath10k 20M · brcm 12M · ti-connectivity 9M · cypress 6M · libertas 2M · rtlwifi 1M
20#     rtw88 0,9M · ath9k_htc 0,1M
21#   BLUETOOTH ─ 33 MB   intel/ibt-* 27M · qca 5M · rtl_bt 1,5M
22#   ETHERNET ─ 8 MB     cxgb4 4,6M · bnx2x 2,6M · rtl_nic 0,3M · bnx2 · cxgb3 · qlogic · tigon
23#   GRÁFICOS ─ 309 MB   nvidia 154M · amdgpu 115M · i915 27M · radeon 7,4M · xe 5,4M
24#   PORTÁTIL ─ 49 MB    intel/ipu 26M (cámara IPU6) · intel/vsc 11M · intel/ish 2,7M (sensor hub,
25#                       y por ahí pasa el touchpad I2C de muchos portátiles nuevos)
26#   AUDIO               cirrus 8,6M + el SOF que aporta la dep `sof-firmware`
27#   regulatory.db       sin él el supplicant no puede elegir canal
28#
29#   ≈ 1,05 GB. El `product-rootfs` pesa 264–358 MB, así que esto multiplica el medio por cuatro.
30#   Es el precio de que la imagen arranque con red en una máquina que nadie midió antes, y es la
31#   decisión que el operador tomó explícitamente.
32#
33# ══ LO QUE QUEDA AFUERA, Y NO ES OLVIDO ════════════════════════════════════════════════════════
34#   qcom 525M        SoC ARM (Adreno, Snapdragon). No hay PC x86 que lo pida.
35#   netronome 140M · mellanox 107M · qed 20M · liquidio 5M · intel/ice 5,4M
36#                    NIC de datacenter de 25/100 G. Si alguna vez takana corre en un servidor con
37#                    una de ésas, es otra derivada de dos líneas — así está pensado el mecanismo.
38#   intel/qat 3,7M · intel/vpu 4M   acelerador de cripto y NPU: no hacen arrancar nada.
39#
40# ══ POR QUÉ LAS GPU NO-INTEL **SÍ** VAN, corrigiendo lo que escribí en `firmware-intel` ════════
41# Ahí dije que `amdgpu`/`radeon` quedaban afuera «por un motivo técnico: takana pinta por
42# mesa-swrast, software puro». Eso estaba MAL y es el error más caro de los dos: sin su firmware
43# **`amdgpu` no inicializa el dispositivo**, y sin dispositivo no hay KMS ni `/dev/dri/card0` ⇒ el
44# compositor no tiene a qué abrirle nada y la pantalla queda muerta. Pintar por software no evita
45# necesitar que la GPU encienda. Lo mismo `nouveau` con el GSP de las NVIDIA nuevas.
46#
47# ══ SIN COMPRIMIR, Y CON LOS ENLACES ═══════════════════════════════════════════════════════════
48# El kernel `linux-metal` no habilita `FW_LOADER_COMPRESS`: un `.zst` se lee igual que un fichero
49# ausente. Y se copia **preservando los symlinks** (`cp -a`), porque el nombre que el driver pide es
50# muchas veces el del ENLACE y no el del blob: `iwlwifi-QuZ-a0-hr-b0-77.ucode` cuelga de la raíz y
51# apunta a `intel/iwlwifi/…`. Son los 2415 enlaces que `linux-firmware` reconstruye desde WHENCE;
52# copiar con `cp -L` los resolvería y el driver volvería a no encontrar su nombre.
53name    = "firmware-metal"
54version = "20260910"          # sigue a linux-firmware: no tiene versión propia
55license = "LicenseRef-linux-firmware-various"
56
57[source]
58dir = "firmware-metal"
59
60[build]
61compiler = "zig-cc"
62target   = "x86_64-linux-musl"
63link     = "static"
64strip_debug = false
65flags   = []
66
67[build.phases]
68configure = "echo 'firmware-metal: nada que configurar'"
69compile   = "echo 'firmware-metal: nada que compilar — son blobs de la dep'"
70install = '''
71set -e
72SRC=/lib/firmware
73DST=/out/lib/firmware
74[ -d "$SRC" ] || { echo "firmware-metal: no está el árbol de linux-firmware en $SRC" >&2; exit 1; }
75[ -f "$SRC/WHENCE" ] || { echo "firmware-metal: falta WHENCE — la dep no se materializó entera" >&2; exit 1; }
76mkdir -p "$DST"
77
78# ── 1. Las familias, con sus enlaces internos intactos ────────────────────────────────────────
79FAMILIAS="intel/iwlwifi rtw88 rtw89 rtlwifi ath9k_htc ath10k ath11k ath12k brcm mediatek mrvl
80ti-connectivity cypress libertas rtl_bt qca rtl_nic bnx2 bnx2x tigon cxgb3 cxgb4 qlogic
81i915 xe amdgpu radeon nvidia intel/ish intel/vsc intel/ipu cirrus"
82puestas=0
83for d in $FAMILIAS; do
84	if [ -d "$SRC/$d" ]; then
85		mkdir -p "$DST/$(dirname "$d")"
86		cp -a "$SRC/$d" "$DST/$(dirname "$d")/"
87		puestas=$((puestas+1))
88	else
89		# Se dice, no se calla: una familia que desaparece entre versiones de linux-firmware es
90		# una capacidad que la imagen pierde en silencio.
91		echo "firmware-metal: ⚠ $SRC/$d no existe en esta versión del árbol" >&2
92	fi
93done
94[ "$puestas" -ge 25 ] || { echo "firmware-metal: sólo $puestas familias de las 32 declaradas — el árbol no es el que se espera" >&2; exit 1; }
95
96# ── 2. Bluetooth Intel y la base regulatoria: ficheros, no directorios ────────────────────────
97mkdir -p "$DST/intel"
98bt=0
99for f in "$SRC"/intel/ibt-*; do
100	[ -e "$f" ] || continue
101	cp -a "$f" "$DST/intel/" && bt=$((bt+1))
102done
103[ "$bt" -gt 0 ] || echo "firmware-metal: ⚠ 0 blobs de bluetooth Intel" >&2
104for f in regulatory.db regulatory.db.p7s; do
105	[ -e "$SRC/$f" ] && cp -a "$SRC/$f" "$DST/"
106done
107[ -e "$DST/regulatory.db" ] || { echo "firmware-metal: falta regulatory.db — ningún wifi podría elegir canal" >&2; exit 1; }
108
109# ── 3. Audio SOF (lo aporta la dep sof-firmware) ──────────────────────────────────────────────
110# Los `.ri` firmados van TODOS: el que carga lo decide el PCI id de la máquina, no nosotros. El
111# audio no aborta la receta —una máquina sin sonido es usable y una sin red no— pero se dice.
112snd=0
113if [ -d "$SRC/intel/sof" ]; then
114	cp -a "$SRC/intel/sof" "$DST/intel/" && snd=$((snd+1))
115fi
116if [ -d "$SRC/intel/sof-tplg" ]; then
117	cp -a "$SRC/intel/sof-tplg" "$DST/intel/" && snd=$((snd+1))
118fi
119[ "$snd" -gt 0 ] || echo "firmware-metal: ⚠ sin SOF — esta imagen va a quedar sin audio HDA" >&2
120
121# ── 4. LOS ENLACES DE LA RAÍZ: el nombre que el driver PIDE ───────────────────────────────────
122# `iwlwifi-QuZ-a0-hr-b0-77.ucode` vive en la raíz como ENLACE a `intel/iwlwifi/…`. Sin esta vuelta,
123# el blob está adentro y el driver —que pide por el nombre de la raíz— no lo encuentra: es
124# exactamente el fallo que `linux-firmware` documenta en su propio encabezado.
125raiz=0
126for l in "$SRC"/*; do
127	[ -L "$l" ] || continue
128	n=$(basename "$l")
129	destino=$(readlink "$l")
130	# Sólo si su destino cayó dentro de lo que copiamos: un enlace colgante es peor que no tenerlo,
131	# porque `request_firmware` abre y falla en vez de buscar el siguiente candidato.
132	if [ -e "$DST/$destino" ]; then
133		ln -sf "$destino" "$DST/$n"
134		raiz=$((raiz+1))
135	fi
136done
137echo "firmware-metal: $raiz enlaces de la raíz reconstruidos"
138
139# ── 5. Enlaces colgantes DENTRO de lo copiado: se resuelven desde el fuente ────────────────────
140# Una familia puede enlazar a un blob de otra que no copiamos (`ath11k/… -> ../ath10k/…`). Si el
141# destino existe en el árbol original, se trae el fichero; si no, se retira el enlace.
142colgantes=0; traidos=0
143for l in $(find "$DST" -type l); do
144	[ -e "$l" ] && continue
145	rel=$(echo "$l" | sed "s|^$DST/||")
146	if [ -e "$SRC/$rel" ]; then
147		rm -f "$l"
148		mkdir -p "$(dirname "$l")"
149		cp -L "$SRC/$rel" "$l" && traidos=$((traidos+1))
150	else
151		rm -f "$l"; colgantes=$((colgantes+1))
152	fi
153done
154echo "firmware-metal: $traidos enlaces colgantes resueltos copiando el blob, $colgantes retirados"
155
156# ── 6. Los controles que importan para el metal de un desconocido ─────────────────────────────
157falta=0
158for c in intel/iwlwifi rtw89 ath11k mediatek brcm rtl_nic i915 amdgpu nvidia regulatory.db; do
159	[ -e "$DST/$c" ] || { echo "firmware-metal: ✗ falta $c" >&2; falta=$((falta+1)); }
160done
161# El wifi Intel es el caso más probable de todos: si no quedaron sus .ucode, esto no sirve.
162w=$(find "$DST/intel/iwlwifi" -name "*.ucode" 2>/dev/null | wc -l)
163[ "$w" -ge 40 ] || { echo "firmware-metal: sólo $w .ucode de iwlwifi (esperaba >40)" >&2; falta=$((falta+1)); }
164[ "$falta" = 0 ] || { echo "firmware-metal: $falta control(es) en rojo" >&2; exit 1; }
165
166n=$(find "$DST" \( -type f -o -type l \) | wc -l)
167k=$(du -sk "$DST" | cut -f1)
168echo "firmware-metal: $n entradas, $((k/1024)) MiB — wifi/bluetooth/ethernet/gráficos/audio de una PC x86"
169'''
170
171[deps]
172build = ["linux-firmware", "sof-firmware", "wireless-regdb"]