Linux Moduli

Da Andreabont's Wiki.

Il kernel Linux è modulare, si possono quindi compilare dei moduli e aggiungerli (e rimuoverli) a run-time. I moduli girano in kernel-space, è quindi necessario prestare molta attenzione per evitare di generare errori lato kernel fino a provocare il blocco del sistema (kernel panic). Nello specifico va notato che non sono disponibili molti degli "aiuti" forniti in user-space, come ad esempio la pulizia della memoria al termine del processo. In kernel-space non ci sono i processi, e alla rimozione del modulo va pulita la memoria usata.

Compilare un modulo

Qui effettueremo un semplice "hello world!" nel kernel, mostrando le basi per la compilazione di un modulo.

Codice sorgente

Un modulo è un'insieme di file C, contenente una collezione di funzioni che verranno chiamate dal kernel stesso quando necessario. La logica con cui si programmano i moduli ricorda infatti molto il paradigma ad eventi.

#include <linux/module.h>	/* Necessario per tutti i moduli */
#include <linux/kernel.h>	/* Necessario per KERN_INFO */
#include <linux/init.h>		/* Necessario per le MACRO */

MODULE_LICENSE("GPL");          /* MACRO per settare la licenza */
MODULE_AUTHOR("Author");        /* MACRO per settare l'autore del modulo */
MODULE_DESCRIPTION("Desc");     /* MACRO per settare la descrizione del modulo */
MODULE_VERSION("1.0.0");        /* MACRO per settare la versione del modulo */

static int number = 0;                /* Variabile che conterrà il parametro e valore di default */
module_param(number, int, S_IRUGO);   /* Dichiarazione del parametro del modulo e della variabile che lo conterrà in sola lettura */

/* Funzione chiamata al caricamento, obbligatoria */
static int __init startup(void)
{
	printk(KERN_INFO "Plugged module with number=%d\n", number);
	return 0;
}

/* Funzione chiamata all'uscita per pulizia (consigliata) */
static void __exit shutdown(void)
{
	printk(KERN_INFO "Unplugging module\n");
}

module_init(startup);           /* Dichiaro la funzione di caricamento */
module_exit(shutdown);          /* Dichiaro la funzione di uscita */

Makefile

Per compilare è caldamente consigliato l'uso dei makefile, qui un esempio ponendo che il codice sopra scritto sia contenuto nel file hello.c. Una volta compilato si otterrà un file hello.ko (kernel object) che sarà possibile inserire nel kernel della vostra macchina.

obj-m += hello.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Firmare il modulo

Se il kernel ha abilitato la modalità EFI_SECURE_BOOT_SIG_ENFORCE allora accetterà di caricare solo i moduli kernel firmati attraverso una chiave MOK. Per poter inserire il modulo appena compilato, avendo a disposizione la chiave MOK privata, è possibile firmare il modulo in questo modo:

sudo /usr/src/linux-headers-$(uname -r)/scripts/sign-file sha256 <chiave_mok_privata> <certificato_der> <modulo_ko>

Gestire un modulo

Ottenere informazioni dal modulo

Con il comando modinfo è possibile ottenere informazioni sul modulo:

$> modinfo hello.ko           
filename:       /path-to/hello.ko
version:        1.0.0
description:    Desc
author:         Author
license:        GPL
srcversion:     CAAC6A8A124985999F4C24B
depends:        
vermagic:       3.16.7-29-desktop SMP preempt mod_unload modversions 
parm:           number:int

Se il modulo è stato caricato è possibile ottenere informazioni sul caricamento tramite:

$> systool -v -m hello
Module = "hello"

  Attributes:
    coresize            = "12536"
    initsize            = "0"
    initstate           = "live"
    refcnt              = "0"
    srcversion          = "EE7549F33C78CDF56AFE600"
    taint               = "O"
    uevent              = <store method only>
    version             = "1.0.0"

  Parameters:
    number              = "0"
...

Inserire il modulo nel kernel

E' possibile inserire (da root) il modulo tramite il comando insmod:

insmod hello.ko

Che produrrà la scrittura nei log del kernel del testo:

[ 5643.763190] Plugged module with number=0

Notare che non avendo fornito un parametro, verrà usato il valore di default "0". Per passare dei parametri è necessario scriverli dopo il nome del modulo in formato "chiave=valore":

insmod hello.ko number=1000

Che produrrà un log del genere:

[ 5662.322078] Plugged module with number=1000

Visualizzare moduli caricati

Per visualizzare i moduli caricati è possibile usare il comando lsmod, nel nostro esempio vedremo:

Module                  Size  Used by
hello                  12536  0 
...

Rimuovere il modulo

Per rimuovere il modulo caricato è possibile usare il comando rmmod:

rmmod hello.ko

Il quale produrrà nei log del kernel del testo:

[ 7095.428839] Unplugging module