Trasteando con módulos del kernel Linux

por | octubre 22, 2017

Tarde o temprano te va a tocar. Vamos a hacer un repaso práctico y humano sobre cómo trabajar con los módulos de Linux.

Lsmod – Lista de módulos del sistema

El primer paso es tener un vistazo de todos los módulos que tenemos en nuestro sistema con lsmod:

[root@jota-pc ~]# lsmod 
Module                  Size  Used by
nls_ascii              16384  1
nls_cp437              20480  1
vfat                   20480  1
fat                    69632  1 vfat
vhost_net              20480  3
vhost                  45056  1 vhost_net
macvtap                24576  1 vhost_net
macvlan                24576  1 macvtap
tun                    28672  7 vhost_net
ses                    20480  0
enclosure              16384  1 ses
scsi_transport_sas     45056  1 ses
uas                    24576  1
usb_storage            73728  4 uas
fuse                   98304  9
ebtable_filter         16384  0
ebtables               36864  1 ebtable_filter
pci_stub               16384  1
vboxpci                24576  0
vboxnetadp             28672  0
vboxnetflt             28672  0
vboxdrv               458752  3 vboxnetadp,vboxnetflt,vboxpci
bridge                135168  0
stp                    16384  1 bridge
llc                    16384  2 bridge,stp
ip6table_filter        16384  0
ip6_tables             28672  1 ip6table_filter
nf_log_ipv4            16384  1
nf_log_common          16384  1 nf_log_ipv4
...

De esta manera podemos ver las dependencias que existen entre módulos observando la columna Used by. Dichas dependencias también podemos observarlas con depmod -v:

[root@jota-pc ~]# depmod -v | grep -i nvidia
/lib/modules/4.9.0-4-amd64/updates/dkms/nvidia-modeset.ko needs "nvidia_register_module": /lib/modules/4.9.0-4-amd64/updates/dkms/nvidia.ko
/lib/modules/4.9.0-4-amd64/updates/dkms/nvidia-drm.ko needs "drm_framebuffer_cleanup": /lib/modules/4.9.0-4-amd64/kernel/drivers/gpu/drm/drm.ko
/lib/modules/4.9.0-4-amd64/updates/dkms/nvidia-drm.ko needs "drm_atomic_helper_plane_destroy_state": /lib/modules/4.9.0-4-amd64/kernel/drivers/gpu/drm/drm_kms_helper.ko
/lib/modules/4.9.0-4-amd64/updates/dkms/nvidia-drm.ko needs "nvKmsKapiGetFunctionsTable": /lib/modules/4.9.0-4-amd64/updates/dkms/nvidia-modeset.ko
/lib/modules/4.9.0-4-amd64/updates/dkms/nvidia-uvm.ko needs "nvUvmInterfaceDisableAccessCntr": /lib/modules/4.9.0-4-amd64/updates/dkms/nvidia.ko

También podemos listar los módulos de nuestro sistema haciendo cat /proc/modules

[root@jota-pc ~]# cat /proc/modules 
nls_ascii 16384 1 - Live 0xffffffffc1cb8000
nls_cp437 20480 1 - Live 0xffffffffc0f93000
vfat 20480 1 - Live 0xffffffffc0f8d000
fat 69632 1 vfat, Live 0xffffffffc1ca6000
vhost_net 20480 3 - Live 0xffffffffc0f87000
vhost 45056 1 vhost_net, Live 0xffffffffc0f74000
macvtap 24576 1 vhost_net, Live 0xffffffffc0ad2000
macvlan 24576 1 macvtap, Live 0xffffffffc0f6d000
tun 28672 7 vhost_net, Live 0xffffffffc0ac1000
ses 20480 0 - Live 0xffffffffc0acc000
enclosure 16384 1 ses, Live 0xffffffffc0a2b000
scsi_transport_sas 45056 1 ses, Live 0xffffffffc09fa000
uas 24576 1 - Live 0xffffffffc0a24000
usb_storage 73728 4 uas, Live 0xffffffffc0aae000
fuse 98304 9 - Live 0xffffffffc0a95000
ebtable_filter 16384 0 - Live 0xffffffffc09e0000
ebtables 36864 1 ebtable_filter, Live 0xffffffffc09f0000
pci_stub 16384 1 - Live 0xffffffffc09eb000
vboxpci 24576 0 - Live 0xffffffffc09d9000 (O)
vboxnetadp 28672 0 - Live 0xffffffffc09d1000 (O)
vboxnetflt 28672 0 - Live 0xffffffffc09c5000 (O)
vboxdrv 458752 3 vboxpci,vboxnetadp,vboxnetflt, Live 0xffffffffc1c35000 (O)
bridge 135168 0 - Live 0xffffffffc0a73000
stp 16384 1 bridge, Live 0xffffffffc0931000
...

Modinfo – Información concreta de un módulo

Una vez tenemos una lista de todos los módulos, podemos indagar en cada uno de ellos en busca de información más detallada. Con modinfo obtenemos información de un módulo en concreto: autor, licencia, nombre del fichero de módulo, dependencias… Como pista, los ficheros de módulos tienen sufijo .ko (ej.: nvidia.ko) que significa Kernel Object y son compilados para cada versión del kernel Linux que tengamos.

Así, por ejemplo en mi caso para el módulo vboxpci:

[root@jota-pc ~]# modinfo vboxpci
filename:       /lib/modules/4.9.0-4-amd64/misc/vboxpci.ko
version:        5.1.30 r118389
license:        GPL
description:    Oracle VM VirtualBox PCI access Driver
author:         Oracle Corporation
srcversion:     599E658DBEB70A080F1FE14
depends:        vboxdrv
vermagic:       4.9.0-4-amd64 SMP mod_unload modversions 

Información del módulo de la gráfica Nvidia:

[root@jota-pc lib]# modinfo nvidia
filename:       /lib/modules/4.9.0-3-amd64/updates/dkms/nvidia.ko
alias:          char-major-195-*
version:        384.90
supported:      external
license:        NVIDIA
srcversion:     9D546A76FA9D9523F03995D
alias:          pci:v000010DEd00000E00sv*sd*bc04sc80i00*
alias:          pci:v000010DEd*sv*sd*bc03sc02i00*
alias:          pci:v000010DEd*sv*sd*bc03sc00i00*
depends:        
vermagic:       4.9.0-3-amd64 SMP mod_unload modversions 
parm:           NVreg_Mobile:int
parm:           NVreg_ResmanDebugLevel:int
parm:           NVreg_RmLogonRC:int
parm:           NVreg_ModifyDeviceFiles:int
parm:           NVreg_DeviceFileUID:int
parm:           NVreg_DeviceFileGID:int
parm:           NVreg_DeviceFileMode:int
parm:           NVreg_UpdateMemoryTypes:int
parm:           NVreg_InitializeSystemMemoryAllocations:int
parm:           NVreg_UsePageAttributeTable:int
parm:           NVreg_MapRegistersEarly:int
parm:           NVreg_RegisterForACPIEvents:int
parm:           NVreg_CheckPCIConfigSpace:int
parm:           NVreg_EnablePCIeGen3:int
parm:           NVreg_EnableMSI:int
parm:           NVreg_TCEBypassMode:int
parm:           NVreg_UseThreadedInterrupts:int
parm:           NVreg_EnableStreamMemOPs:int
parm:           NVreg_MemoryPoolSize:int
parm:           NVreg_RegistryDwords:charp
parm:           NVreg_RegistryDwordsPerDevice:charp
parm:           NVreg_RmMsg:charp
parm:           NVreg_AssignGpus:charp

¿Qué módulo está utilizando mi dispositivo (PCI)?

Podemos obtener esta información con la opción -k de lspci siempre que tengamos un Kernel Linux 2.6 o superior:

[root@jota-pc ~]# lspci -k
...
00:1f.2 SATA controller: Intel Corporation 6 Series/C200 Series Chipset Family SATA AHCI Controller (rev 05)
	Subsystem: ASUSTeK Computer Inc. P8 series motherboard
	Kernel driver in use: ahci
	Kernel modules: ahci
00:1f.3 SMBus: Intel Corporation 6 Series/C200 Series Chipset Family SMBus Controller (rev 05)
	Subsystem: ASUSTeK Computer Inc. P8 series motherboard
	Kernel driver in use: i801_smbus
	Kernel modules: i2c_i801
01:00.0 VGA compatible controller: NVIDIA Corporation GM206 [GeForce GTX 960] (rev a1)
	Subsystem: ASUSTeK Computer Inc. GM206 [GeForce GTX 960]
	Kernel driver in use: nvidia
	Kernel modules: nouveau, nvidia_drm, nvidia
01:00.1 Audio device: NVIDIA Corporation Device 0fba (rev a1)
	Subsystem: ASUSTeK Computer Inc. Device 8520
	Kernel driver in use: snd_hda_intel
	Kernel modules: snd_hda_intel
02:00.0 Network controller: Ralink corp. RT3090 Wireless 802.11n 1T/1R PCIe
	Subsystem: Lite-On Communications Inc RT3090 Wireless 802.11n 1T/1R PCIe
	Kernel driver in use: rt2800pci
	Kernel modules: rt2800pci
03:00.0 USB controller: ASMedia Technology Inc. ASM1042 SuperSpeed USB Host Controller
	Subsystem: ASUSTeK Computer Inc. P8B WS Motherboard
	Kernel driver in use: xhci_hcd
	Kernel modules: xhci_pci
...

Todos estos dispositivos PCI estarán mapeados en /proc/bus/pci/devices. Podemos hacer un cat aunque el resultado no es demasiado legible, por lo que es mejor filtrar con awk por la columna nº 18 en la que podemos encontrar todos los drivers utilizados:

[root@jota-pc pci]# cat devices | awk '{print $18}'
snb_uncore
pcieport
mei_me
e1000e
ehci-pci
snd_hda_intel
pcieport
pcieport
pcieport
pcieport
ehci-pci
lpc_ich
ahci
i801_smbus
nvidia
snd_hda_intel
rt2800pci
xhci_hcd
xhci_hcd
ahci

Cargar y descargar un módulos

Para cargar un módulo podemos utilizar insmod o modprobe. Es recomendable utilizar el segundo ya que tiene en cuenta las dependencias que tiene un módulo para cargarlas de forma automática:

# Insmod
insmod /lib/modules/4.9.0-3-amd64/updates/dkms/nvidia.ko

# Modprobe
modprobe nvidia

Si queremos descargar un módulo podemos utilizar modprobe -r o rmmod:

# Modprobe
modprobe -r nvidia

# Rmmod
rmmod nvidia

¿Kernel con soporte para carga de módulos?

Existen varias formas de comprobar si nuestro kernel tiene soporte para carga de módulos. En primer lugar si hacemos un cat /proc/modules obtendremos un resultado con la lista de módulos cargados en el kernel:

nvidia_uvm 659456 0 - Live 0xffffffffc1e1f000 (PO)
vhost_net 20480 3 - Live 0xffffffffc0f76000
vhost 45056 1 vhost_net, Live 0xffffffffc0f6a000
macvtap 24576 1 vhost_net, Live 0xffffffffc0de0000
macvlan 24576 1 macvtap, Live 0xffffffffc0c8d000
tun 28672 7 vhost_net, Live 0xffffffffc0bbe000
fuse 98304 9 - Live 0xffffffffc0dc3000
ebtable_filter 16384 0 - Live 0xffffffffc0bb9000
ebtables 36864 1 ebtable_filter, Live 0xffffffffc0bab000
pci_stub 16384 1 - Live 0xffffffffc0ba6000
vboxpci 24576 0 - Live 0xffffffffc0b9b000 (O)
vboxnetadp 28672 0 - Live 0xffffffffc0b93000 (O)
vboxnetflt 28672 0 - Live 0xffffffffc0aa9000 (O)
vboxdrv 458752 3 vboxpci,vboxnetadp,vboxnetflt, Live 0xffffffffc1dae000 (O)
bridge 135168 0 - Live 0xffffffffc0b6d000
stp 16384 1 bridge, Live 0xffffffffc0aa4000
llc 16384 2 bridge,stp, Live 0xffffffffc0a52000
ip6table_filter 16384 0 - Live 0xffffffffc0a37000
ip6_tables 28672 1 ip6table_filter, Live 0xffffffffc0b65000
nf_log_ipv4 16384 1 - Live 0xffffffffc0a28000
nf_log_common 16384 1 nf_log_ipv4, Live 0xffffffffc0a0c000
xt_LOG 16384 1 - Live 0xffffffffc0850000
xt_limit 16384 1 - Live 0xffffffffc09bf000

Dicho resultado también podríamos verificarlo también lanzando lsmod.

Además, por ejemplo suponiendo que tengamos un kernel 4.9.0-4 para arquitectura 64 bits, tendremos un subdirectorio dentro de /lib/modules/ correspondiente a esa versión del kernel con configuración relativa a carga de módulos, dependencias, etc…:

[jota@jota-pc ~]$ ls -l /lib/modules/4.9.0-4-amd64/ 
total 4152
lrwxrwxrwx  1 root root      36 Sep 28 19:27 build -> /usr/src/linux-headers-4.9.0-4-amd64
drwxr-xr-x 12 root root    4096 Oct  7 18:24 kernel
drwxr-xr-x  2 root root    4096 Oct 16 22:00 misc
-rw-r--r--  1 root root 1012238 Oct 19 07:01 modules.alias
-rw-r--r--  1 root root  971143 Oct 19 07:01 modules.alias.bin
-rw-r--r--  1 root root    4018 Sep 28 19:27 modules.builtin
-rw-r--r--  1 root root    5327 Oct 19 07:01 modules.builtin.bin
-rw-r--r--  1 root root  399424 Oct 19 07:01 modules.dep
-rw-r--r--  1 root root  550668 Oct 19 07:01 modules.dep.bin
-rw-r--r--  1 root root     402 Oct 19 07:01 modules.devname
-rw-r--r--  1 root root  133512 Sep 28 19:27 modules.order
-rw-r--r--  1 root root     523 Oct 19 07:01 modules.softdep
-rw-r--r--  1 root root  510617 Oct 19 07:01 modules.symbols
-rw-r--r--  1 root root  625163 Oct 19 07:01 modules.symbols.bin
lrwxrwxrwx  1 root root      37 Sep 28 19:27 source -> /usr/src/linux-headers-4.9.0-4-common
drwxr-xr-x  3 root root    4096 Oct  7 18:25 updates

Dadas estas pistas, si nuestro kernel no tuviera soporte para carga de módulos no debería existir ni el directorio de módulos correspondiente anterior ni /proc/modules. Además, lsmod no deberia tampoco arrojar ningún resultado si lo lanzamos.

¿Cómo habilitamos el soporte para la carga de módulos? O bien compilamos un nuevo kernel o recompilamos el existente activando la opción «Enable loadable module support» cuando estemos generando el fichero .config en el proceso de compilación.

Para estos menesteres os puede resultar útil el artículo sobre cómo compilar un kernel Linux de forma genérica y también para el caso particular de Debian.