FreeBSD por defecto proporciona una imagen del kernel genérica, esta incluye una gran cantidad de drivers para soportar el mayor número de hardware posible, además incluye las funcionalidades mas ampliamente utilizadas así como deshabilitadas las menos comunes.
Compilando el kernel conseguiremos varios beneficios:
- Mayor rapidez en el arranque del sistema, dado que el kernel sólo tiene que probar el hardware que realmente tiene el equipo.
- Menor consumo de RAM ya que hemos eliminado las partes innecesarias.
- Menor superficie de ataque al eliminar partes del código que ya no estarán presentes.
- Habilitar el soporte para un hardware en concreto que en GENERIC viene deshabilitado.
- Probar alguna funcionalidad mas exótica que no viene habilitada por defecto.
Instalamos Git:
Las fuentes del SO se guardan en /usr/src, esto es un dataset ZFS.
NAME USED AVAIL REFER MOUNTPOINT
zroot/usr/src 827M 27.2G 827M /usr/src
Por lo tanto si queremos partir desde cero debemos recrearlo:
zfs destroy zroot/usr/src
zfs create zroot/usr/src
Clonamos el repo en el directorio /usr/src:
La rama principal del repositorio de Git corresponde con la CURRENT , nosotros queremos permanecer en la RELEASE, mas concretamente en la misma versión con la que fueron compiladas las core userlandtools de nuestro sistema, esto nos evitará muchos problemas extraños y difíciles de debugear(como que algunas conexiones de red fallen y otras no).
Consultamos la versión de las userlands:
13.0-RELEASE-p11
Podemos ver las branches en esta web , la mas actual 13.0 RELEASE es:
releng/13.0
Cambiamos a la branch deseada:
git checkout releng/13.0
Con el tiempo querremos actualizar nuestro repositorio para mantener al día el código fuente del kernel, para ello podemos optar por dos métodos.
Manualmente:
git pull --rebase
Mediante freebsd-update:
Tan solo debemos asegurarnos de que esté configurado para actualizar también el código fuente del kernel, por defecto ya viene habilitado.
Components src world kernel
src indica que se deben actualizar tanto las fuentes del kernel como las de las core userlantools.
En mi caso la necesidad de recompilar el kernel es habilitar el polling de la tarjeta de red, dicha funcionalidad viene deshabilitada por defecto y me gustaría hacer varios test de rendimiento de red con esta opción habilitada y deshabilitada.
Primero comprobamos que imagen de kernel tenemos cargada actualmente:
FreeBSD MightyMax.alfaexploit.com 13.0-RELEASE-p11 FreeBSD 13.0-RELEASE-p11 #0: Tue Mar 15 09:36:28 UTC 2022 root@amd64-builder.daemonology.net:/usr/obj/usr/src/amd64.amd64/sys/GENERIC amd64
La configuración de dicho kernel se encuentra en el repositorio de Git que hemos clonado, así que puedo comprobar que el polling no está habilitado:
Procedemos con la recompilación, primero hacemos una copia de la configuración genérica a una personalizada:
mkdir /root/kernel_configs
cp GENERIC /root/kernel_configs/KR0M
ln -s /root/kernel_configs/KR0M
total 79
drwxr-xr-x 2 root wheel 14 Mar 31 22:20 .
drwxr-xr-x 14 root wheel 15 Apr 9 2021 ..
-rw-r--r-- 1 root wheel 412 Apr 9 2021 DEFAULTS
-rw-r--r-- 1 root wheel 14944 Apr 9 2021 GENERIC
-rw-r--r-- 1 root wheel 1230 Apr 9 2021 GENERIC-KCSAN
-rw-r--r-- 1 root wheel 786 Apr 9 2021 GENERIC-MMCCAM
-rw-r--r-- 1 root wheel 754 Apr 9 2021 GENERIC.hints
lrwxr-xr-x 1 root wheel 25 Mar 31 22:20 KR0M -> /root/kernel_configs/KR0M
-rw-r--r-- 1 root wheel 85 Apr 9 2021 LINT
-rw-r--r-- 1 root wheel 138 Apr 9 2021 LINT-NOINET
-rw-r--r-- 1 root wheel 112 Apr 9 2021 LINT-NOINET6
-rw-r--r-- 1 root wheel 345 Apr 9 2021 LINT-NOIP
-rw-r--r-- 1 root wheel 5469 Apr 9 2021 MINIMAL
-rw-r--r-- 1 root wheel 19199 Apr 9 2021 NOTES
Ahora procedemos con la configuración de las opciones, el formato es muy simple , cada línea contiene una keyword que representa un dispositivo o subsistema, un argumento y una breve descripción.
Algunas opciones resultan obvias por su propio nombre pero otras no, si la breve descripción no es suficiente podemos consultar mas detalles en el fichero NOTES.
/usr/src/sys/$(uname -m)/conf/NOTES
Los ficheros de configuración del kernel permiten el uso de includes, esto nos facilita mucho la vida ya que podemos partir del kernel genérico y añadir o eliminar ciertas opciones:
include GENERIC
ident KR0M
options DEVICE_POLLING
NOTA: En caso de querer deshabilitar opciones utilizaríamos las sentencias nooptions/nodevice en el fichero de configuración.
De este modo tendremos exactamente el mismo kernel que GENERIC pero con la opción de polling habilitada.
Finalmente procedemos con la compilación:
make -j2 buildkernel KERNCONF=KR0M INSTKERNNAME=kernel.kr0m
Se ha definido la variable INSTKERNNAME para que no sobreescriba el kernel original, ya que luego es muy complicado mantener el sistema operativo actualizado con nuestro kernel custom corriendo y una versión actualizada de la imagen GENERIC como recomiendan aquí , de este modo freebsd-update actualizará /boot/kernel al margen de /boot/kernel.kr0m
Si fuese necesario podríamos afinar mas el proceso de compilación mediante el fichero /etc/make.conf
En mi caso la compilación ha tardado:
real 42m47.379s
user 59m46.393s
sys 5m46.886s
En un equipo tan modesto como este:
hw.model: Intel(R) Core(TM)2 Duo CPU P8400 @ 2.26GHz
hw.machine: amd64
hw.ncpu: 2
3.71024 GB
Copiamos la imagen del kernel nuevo y sus módulos a /boot/kernel.kr0m:
Se ha definido la variable INSTKERNNAME para que no sobreescriba el kernel original, ya que luego es muy complicado mantener el sistema operativo actualizado con nuestro kernel custom corriendo y una versión actualizada de la imagen GENERIC como recomiendan aquí , de este modo freebsd-update actualizará /boot/kernel al margen de /boot/kernel.kr0m
Podemos ver el kernel nuevo en /boot/kernel.kr0m:
drwxr-xr-x 2 root wheel 809 Apr 7 18:02 kernel
drwxr-xr-x 2 root wheel 809 Apr 8 17:41 kernel.kr0m
drwxr-xr-x 2 root wheel 809 Apr 7 22:55 kernel.kr0m.old
NOTA: Si ya teníamos un kernel llamado kernel.kr0m este será backupeado a kernel.kr0m.old.
Retocamos nuestro loader para que cargue la imagen correcta:
kernel=kernel.kr0m
Reiniciamos para probar el kernel nuevo:
Comprobamos que imagen tenemos cargada:
FreeBSD MightyMax.alfaexploit.com 13.0-RELEASE-p11 FreeBSD 13.0-RELEASE-p11 #0 releng/13.0-n244791-312522780e8-dirty: Thu Apr 7 21:23:31 CEST 2022 root@MightyMax.alfaexploit.com:/usr/obj/usr/src/amd64.amd64/sys/KR0M amd64
Updates:
Hay ocasiones en las que una actualización no actualizará el kernel pero si otras partes del sistema, este nivel de parcheo del sistema se puede consultar mediante el comando:
13.0-RELEASE-p11
Para que nuestro custom kernel refleje correctamente el patchlevel del sistema debemos recompilarlo de nuevo, de este modo la imagen será generada con la información actualizada del patchlevel. Con el kernel GENERIC pasa lo mismo, si no cargamos la imagen nueva tampoco refleja correctamente el patchlevel.
Resumiendo
- En todas las actualizaciones debemos recompilar el kernel para que cuadre con la versión de las core userlandtools y las fuentes tanto de las core userlandtools como del kernel.
- Si además queremos reflejar correctamente el patchlevel de las core userlandtools también debemos reiniciar con el kernel recién compilado.
Troubleshooting:
Si nuestro nuevo kernel tuviese problemas para arrancar siempre podremos cargar la imagen GENERIC o la imagen anterior desde el boot loader de FreeBSD, tan solo seleccionaríamos la opción “Escape to loader prompt” y teclear el nombre de la imagen.
Escape to loader prompt
boot kernel
boot kernel.kr0m.old
Puede darse el caso en el que el kernel arranque pero algunos comandos como ps, vmstat o conexiones de forma aparentemente random fallen, esto es debido a que las utilidades del sistema fueron compiladas para una versión del kernel muy distinta a la actual.
Para solventarlo debemos proceder de forma distinta dependiendo de si compilamos “world” las core userland tools o utilizamos la versión binaria de estas:
- Source: Recompilar e instalar las core userlandtools utilizando una versión de las fuentes a la par con la versión del kernel.
- Binarios: Se debería actualizar mediante freebsd-update a una versión de FreeBSD que lleve los binarios compilados con la misma versión de kernel que estemos utilizando.
Podemos consultar la versión del kernel y las userlandtools del siguiente modo:
- freebsd-version -r -> Kernel que está corriendo actualmente.
- freebsd-version -k -> Kernel instalado pero que todavía no ha cargado porque no se ha reiniciado el equipo.
- freebsd-version -u -> Versión de las core userlandtools.
Si no disponemos de una imagen GENERIC por la razón que sea podemos regenerarla del siguiente modo:
make -j2 kernel __MAKE_CONF=/dev/null SRCCONF=/dev/null
Este proceso solo tiene como requisitos no haber tocado la configuración GENERIC ni compilar con parámetros retocados en /etc/make.conf
Dejo algunos enlaces de referencia que pueden resultar de ayuda:
https://docs.freebsd.org/en/books/handbook/kernelconfig/
https://docs.freebsd.org/en/books/handbook/mirrors/index.html#git