Kabosu - Creando cosas
Publicado: 2024-11-10
Etiquetas: Linux, Proyectos, Reto de la Paella
Hace poco comenté que me había propuesto aprender más sobre Linux y que había elegido Alpine por lo minimalista que era. Una de las tareas que me había auto-asignado era programar algo para el kernel para así practicar cómo compilarlo, instalarlo, etc. Como hace tiempo que no hago un reto de la paella decidí que desarrollaría algún módulo o algo relacionado con eso. Mi idea era hacerlo en una tarde pero perdí mucho tiempo, un par de semanas de hecho, hasta que conseguí compilar el kernel así que voy a dividir esto en dos partes.
El primer paso para hacer cambios en el kernel es poder compilarlo e iniciar el sistema con él. Lo había hecho alguna vez en el pasado así que pensaba que iba a ser coser y cantar. Nada más lejos de la realidad. Linux ha cambiado mucho después de tantos años.
Las distribuciones suelen tener un paquete con el código fuente del kernel que facilita mucho las cosas. Tras un par de búsquedas con apk
no lo vi me bajé la última version (6.11.6) de su página principal. Además quería hacerlo de forma más "manual" para aprender un poco el proceso.
El siguiente paso una vez conseguido el código era compilar el kernel sin ningún cambio para ver cómo se hacía.
Leí en algún sitio que antes de compilar había que configurar el kernel usando make menuconfig
. Este comando muestra una interfaz gráfica con miles de opciones para el kernel. Decidí usar la configuración de compilación del kernel que trae Alpine por defecto. Normalmente se guarda en /boot
. En mi caso en /boot/config-6.8.0-49-generic
. Hice una copia y la renombré como .config
.
Tras eso ya ejecuté el comando make
para iniciar la compilación y... falló. Me faltaban múltiples dependencias de compilación: GMP, flex, bison, AWK, ... Podía haber mirado la documentación para saber los requisitos pero yo lo que hice fue ejecutar make
, esperar unos minutos a que fallase, ver el error y buscar el paquete que, a mi juicio, solucionaría el problema. Tras varias iteraciones conseguí que empezara a compilar sin mostrar cosas raras. Creo el siguiente comando instala todos los paquetes necesarios en Alpine:
apk add cmd:make cmd:gcc ncurses ncurses-dev cmd:flex cmd:bison gmp-dev mpc1-dev mpfr-dev openssl-dev elfutils-dev perl gawk diffutils glib-dev musl-dev cmd:g++
Por fin ya parecía que estaba compilando pero tras media hora falló porque faltaba una clave de firma digital.
make[3]: *** No rule to make target '/home/buildozer/.abuild/kernel_signing_key.pem', needed by 'certs/signing_key.x509'. Stop.
make[2]: *** [scripts/Makefile.build:480: certs] Error 2
make[1]: *** [/home/x/linux-6.6.58/Makefile:1921: .] Error 2
make: *** [Makefile:234: __sub-make] Error 2
Hoy en día el kernel tiene la opción de crear firmas digitales para los módulos y rechazar módulos no firmados como medida de seguridad. Estuve leyendo la documentación sobre eso que esto lo hago por aprender cosas nuevas como esta. Al final mi problema era que había copiado el fichero de configuración de Alpine y este tiene la ruta a las claves de cifrado que usan sus desarrolladores y que yo, obviamente, no tengo. La solución fue borrar la ruta /home/buildozer/.abuild/kernel_signing_key.pem
de .config
. Dejando en blanco la opción CONFIG_MODULE_SIG_KEY
se crearán nuevas claves de cifrado durante la compilación.
Una vez solucionados todos los problemas que impedían compilar el kernel dejé a make
haciendo su trabajo y seguí con mi vida. 20 horas tardó en compilar y todos los ficheros del proceso ocuparon 17 gigas de espacio en disco.
El resultado de la compilación es un fichero llamado bzImage
que es la imagen comprimida del kernel. ¡El siguiente paso era instalarla e iniciar el sistema con el nuevo kernel!
En este proceso aprendí:
Era la hora de instalar el kernel. Primero make modules_install
para comprimir, firmar e instalar los módulos.
Luego copié el nuevo kernel a /boot/
. Los mensajes de make
dejaban claro la ubicación de la imagen del kernel tras la compilación.
Kernel: arch/x86/boot/bzImage is ready (#2)
Le puse el nombre /boot/vmlinuz-x
para diferenciarlo del actual kernel de Alpine que se encuentra en /boot/vmlinuz-lts
. También actualicé /boot/extlinux.conf
para que el bootloader cargase la imagen que he compilado yo.
Por supuesto, la primera vez no funcionó. Al reiniciar me salió un error y me soltó en la consola de recuperación. No había creado la imagen initramfs con el nuevo kernel. La creé con el comando mkinitfs -c /etc/mkinitfs/mkinitfs.conf -b / 6.11.6
.
Tras reiniciar, ¡se cargó el nuevo kernel!
localhost:~/linux-6.11.6$ uname -a
Linux localhost 6.11.6 #1 SMP PREEMPT_DYNAMIC Thu Nov 7 21:34:32 UTC 2024 x86_64 Linux
En realidad la primera vez que lo intenté compilar fui muy de listo e intenté cambiar la configuración, quitando cosas que (pensaba) que no necesitaba. El resultado fue cada vez que iniciaba con el nuevo kernel me llevaba a la consola de recuperación de initramfs porque no encontraba el disco duro. El kernel funcionaba ahí pero no podía montar el sistema. Tras varios días peleando a ratos para ver qué módulo se me había olvidado poner me rendí y simplemente hice una compilación de todo los módulos del kernel. Este proceso sí que fue bien a la primera.
En este paso aprendí mucho sobre initramfs. En especial los días que estaba investigando porqué no encontraba el disco duro en el que estaba el sistema.
Como primer paso para modificar el kernel decidí añadir un pequeño mensaje. El kernel escribe mensajes cuando hace ciertas cosas y estos se pueden usando el comando dmesg
. Copié una de las cadenas de texto que vi y la busqué en el código para averiguar cómo la generaban.
localhost:~/linux-6.11.6$ grep -r "Spectre v2 mitigation leaves CPU vulnerable" .
./arch/x86/kernel/cpu/bugs.c:#define RETBLEED_INTEL_MSG "WARNING: Spectre v2 mitigation leaves CPU vulnerable to RETBleed attacks, data leaks possible!\n"
El texto que elegí habla sobre mitigar vulnerabilidades de la CPU. Ya hablé sobre esas cosas hace unos meses. Con grep
encontré la cadena de texto en un fichero llamado bugs.c
. Con otro grep encontré el que parecía ser el comando con el que se escriben los mensajes de dmesg
: pr_err
.
localhost:~/linux-6.11.6$ grep "RETBLEED_INTEL_MSG" arch/x86/kernel/cpu/bugs.c
#define RETBLEED_INTEL_MSG "WARNING: Spectre v2 mitigation leaves CPU vulnerable to RETBleed attacks, data leaks possible!\n"
pr_err(RETBLEED_INTEL_MSG);
Con otra simple búsqueda encontré el fichero en el que se define esa función (en realidad es una macro): include/linux/printk.h
. También había definiciones de macros similares: pr_info
, pr_warn
, etc.
En arch/x86/kernel/cpu/bugs.c
, cerca del pr_error
con el mensaje que había buscado, añadí unos pocos mensajes de mi cosecha:
pr_err("Good morning Vietnam!");
pr_info("info test xxx");
pr_warn("warn test xxx");
Quería ver cómo se mostraban los mensajes de cada uno de los niveles de log. Recompilé el kernel, lo instalé el /boot
, creé una nueva imagen initramfs y reinicié.
Al iniciarse de nuevo el sistema ejecuté dmesg
y mis mensajes aparecían allí. ¡Victoria!
[ 0.095220] RETBleed: WARNING: Spectre v2 mitigation leaves CPU vulnerable to RETBleed attacks, data leaks possible!
[ 0.095221] RETBleed: Good morning Vietnam!
[ 0.095222] RETBleed: info test xxx
[ 0.095223] RETBleed: warn test xxx
[ 0.095224] RETBleed: Vulnerable
Misión cumplida. Pensaba hacer todo esto en una tarde y al final me ha costado un buen puñado de horas a lo largo de dos semanas hasta que he conseguido compilar el kernel e iniciar correctamente el sistema con él. En los próximos días intentaré meter algo relacionado con la paella en el kernel. En estos momentos la idea que tengo en la cabeza es añadir un nuevo directorio llamado /proc/recipes
y que allí puedas obtener recetas de platos valencianos, entre ellos de la paella. No debería ser excesivamente complicado pero nunca se sabe.