Esta pagina se ve mejor con JavaScript habilitado

Compilación del kernel en FreeBSD

 ·  🎃 kr0m

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:

pkg install git

Las fuentes del SO se guardan en /usr/src, esto es un dataset ZFS.

zfs list

NAME                                         USED  AVAIL     REFER  MOUNTPOINT
zroot/usr/src                                827M  27.2G      827M  /usr/src

Por lo tanto si queremos partir desde cero debemos recrearlo:

umount /usr/src
zfs destroy zroot/usr/src
zfs create zroot/usr/src

Clonamos el repo en el directorio /usr/src:

git clone -o freebsd https://git.FreeBSD.org/src.git /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:

freebsd-version -u

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:

cd /usr/src
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:

cd /usr/src
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.

vi /etc/freebsd-update.conf

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:

uname -a

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:

grep DEVICE_POLLING /usr/src/sys/$(uname -m)/conf/GENERIC

Procedemos con la recompilación, primero hacemos una copia de la configuración genérica a una personalizada:

cd /usr/src/sys/$(uname -m)/conf/
mkdir /root/kernel_configs
cp GENERIC /root/kernel_configs/KR0M
ln -s /root/kernel_configs/KR0M

ls -la
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:

vi KR0M

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:

cd /usr/src
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:

sysctl hw.model hw.machine hw.ncpu

hw.model: Intel(R) Core(TM)2 Duo CPU     P8400  @ 2.26GHz
hw.machine: amd64
hw.ncpu: 2
sysctl -n hw.physmem | awk ‘{ byte =$1 /1024/1024/1024; print byte " GB" }’
3.71024 GB

Copiamos la imagen del kernel nuevo y sus módulos a /boot/kernel.kr0m:

make -j2 installkernel 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

Podemos ver el kernel nuevo en /boot/kernel.kr0m:

ls -la /boot/|grep kernel

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:

vi /boot/loader.conf

kernel=kernel.kr0m

Reiniciamos para probar el kernel nuevo:

shutdown -r now

Comprobamos que imagen tenemos cargada:

uname -a

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:

uname -r

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:

cd /usr/src
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

Si te ha gustado el artículo puedes invitarme a un RedBull aquí