Looking for Computer Science & Information Technology online
courses ?
Check my new web site: https://www.yesik.it !
Le noyau Linux est un noyau monolithique modulaire. Concrètement, cela veut dire qu'il est possible de dynamiquement y charger des modules logiciels pour en étendre les fonctionnalités. Écrire un nouveau module est donc une des solutions à envisager quand on veut adapter le système d'exploitation aux spécificités matérielles d'une plate-forme embarquée. Sauf que sur un système embarqué, les ressources sont souvent limités – ce qui rend nécessaire la compilation croisée. Comme il s'agit d'une plate-forme populaire et bon marché, voici donc comment procéder à la compilation croisée d'un module Linux pour Rasberry Pi.
Sommaire
Toolchain
Avant d'aller plus loin, vous devez disposer sur votre machine hôte (celle qui sert à compiler) de l'ensemble des outils nécessaires à la compilation pour votre cible. C'est ce qu'on appelle la toolchain. Si ce n'est déjà fait, je vous renvoie sur l'article compagnon de celui-ci: Compilation croisée facile pour Raspberry Pi.
Cross-compilation module pour RPi
Récupérer les sources du noyau
Pour pouvoir compiler un module pour le noyau Linux, vous devez ensuite disposer des sources adéquates. Il s'agit bien ici des sources du noyau de la cible. Et pas celles de l'hôte. Dans mon cas, ma carte Raspberry Pi tourne sous Raspbian (basé sur le noyau Linux 3.2.27). J'ai donc téléchargé l'archive correspondante à partir du dépôt GitHub adéquat:
sylvain@arm-builder:~$ mkdir ${HOME}/kernel sylvain@arm-builder:~$ cd ${HOME}/kernel sylvain@arm-builder:~/kernel wget https://github.com/raspberrypi/linux/archive/rpi-3.2.27.tar.gz -O - | tar xzf -
En plus des sources à proprement parler, vous avez besoin de la configuration du noyau. Celle-ci peut être directement récupérée dans le pseudo-système de fichiers /proc de la cible:
sylvain@arm-builder:~/kernel$ cd linux-rpi-3.2.27 sylvain@arm-builder:~/kernel/linux-rpi-3.2.27$ ssh pi@10.129.36.203 cat /proc/config.gz | gunzip - > linux-rpi-3.2.27/.config
Mais les sources et la configuration ne suffisent pas. Il faut aussi compiler le noyau. Ce n'est pas compliqué; juste long…
sylvain@arm-builder:~/kernel/linux-rpi-3.2.27$ make ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- oldconfig sylvain@arm-builder:~/kernel/linux-rpi-3.2.27$ make ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- prepare # L'option -j9 indique au make de lancer jusqu'à 9 processus en parallèle. # À ajuster en fonction du nombre de "cœurs" de votre machine sylvain@arm-builder:~/kernel/linux-rpi-3.2.27$ make -j9 ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi-

CROSS_COMPILE=arm-unknown-linux-gnueabi-
La macro CROSS_COMPILE sert à indiquer le préfixe des outils de la toolchain. Cela permet au make d'invoquer le bon compilateur – c'est à dire celui de la cible et non pas celui de l'hôte par erreur.
Dans mon cas, avec une toolchain générée en conservant au maximum les options par défaut de crosstool-NG, ces outils sont:
- arm-unknown-linux-gnueabi-gcc
- arm-unknown-linux-gnueabi-as
- arm-unknown-linux-gnueabi-ld
- …
Avec une autre toolchain il faudra sans doute ajuster l'option CROSS_COMPILE pour l'adapter.
Compiler son propre module
Une fois les outils en place, écrire son propre module revient à écrire un programme C contenant les points d'entrée init_module (appelé par le kernel au chargement du module) et cleanup_module (appelé par le kernel au déchargement du module):
#include <linux/module.h> /* Needed by all modules */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("sylvain@chicoree.fr, 2012"); MODULE_DESCRIPTION("Demo module for cross compiling"); int init_module(void) { // Print a message in the kernel log printk("Hello world\n"); // A non 0 return means init_module failed; module can't be loaded. return 0; } void cleanup_module(void) { // Print a message in the kernel log printk("Goodbye world\n"); }
La construction du module à partir de ce fichier source se faisant avec les makefiles spécifiques du kernel, l'usage est d'utiliser un fichier Makefile local au répertoire du module, et qui se contente d'invoquer de manière récursive ses homologues présents dans le dossier du kernel:
# # My Module Makefile # obj-m := my-module.o KDIR := /home/sylvain/kernel/linux-rpi-3.2.27/ PWD := $(shell pwd) all: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: $(MAKE) -C $(KDIR) M=$(PWD) clean
Pensez à modifier dans le fichier ci-dessus la ligne KDIR:= pour refléter l'emplacement des sources du kernel sur votre machine. Puis, lors de l'invocation du make, n'oubliez pas de définir les macros ARCH et CROSS_COMPILE adéquate pour votre cible:
# Construction du module sylvain@arm-builder:~/test$ make ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- # Copie sur la cible sylvain@arm-builder:~/test$ scp my-module.ko pi@10.129.36.203:.
Pour tester, il suffit de charger/décharger le module du noyau de la cible, chacune de ces opération faisant apparaitre un message dans le journal du système (grâce aux printk présents dans le code source):
# Test sylvain@arm-builder:~/test$ ssh pi@10.129.36.203 pi@raspberrypi ~ $ sudo insmod my-module.ko pi@raspberrypi ~ $ dmesg [...] [69370.606868] Hello world pi@raspberrypi ~ $ sudo rmmod my_module pi@raspberrypi ~ $ dmesg [...] [69370.606868] Hello world [69411.891597] Goodbye world
Comme vous le voyez, une fois les outils en place, compiler un module, puis le charger ou décharger de la cible n'est pas compliqué. Ce qui l'est, c'est d'écrire le module qui correspond à votre besoin. Et pour ça, je ne peux pas trop vous aider…
Ressources
- http://elinux.org/RPi_Kernel_Compilation
- http://blog.markloiseau.com/2012/04/hello-world-loadable-kernel-module-tutorial/