On m’a demandé à plusieurs reprises comment compiler les modules pour le noyau du Raspberry Pi que j’ai présenté dans différents articles ou ceux que je propose en illustration de mes sessions de formation. J’emploie généralement une cross-compilation, c’est à dire un compilateur spécifique installé sur un PC pour produire du code pour le processeur à cœur ARM du Raspberry Pi. J’ai déjà présenté cette solution dans plusieurs articles (par exemple celui-ci, celui-ci ou la série d’articles pour Linux Magazine).
Il existe toutefois une autre solution plus simple : la compilation native en utilisant une distribution courante pour Raspberry Pi.
Problématique
Pour pouvoir compiler un module du noyau, il est indispensable de disposer des fichiers d’en-tête et de configuration du noyau cible. Pour une plate-forme embarquée, cela signifie généralement que l’on réalise la compilation des modules supplémentaires sur la même machine que celle qui a servi à produire le noyau.
Lorsque le noyau précompilé est livré avec le matériel, la mise en œuvre de modules personnalisés peut devenir un véritable casse-tête pour l’intégrateur qui se retrouve pris en sandwich entre différents fournisseurs. C’est pour cela que je préconise toujours d’essayer de disposer de toute la solution de compilation lors de l’industrialisation d’un système embarqué.
Quand nous souhaitons compiler un nouveau module pour un noyau fourni par une distribution Linux, par exemple sur un PC, aucun souci : il suffit d’installer le package contenant les fichiers d’en-tête et de configuration nécessaires (linux-headers
sur Debian et dérivés, kernel-devel
sur RedHat, etc.) et d’utiliser les outils de compilation standards.
Le problème qui se pose avec les principales distributions pour le Raspberry Pi, notamment la Raspbian que je prendrai en exemple dans cet article, est qu’elles ne fournissent pas les packages nécessaires à la compilation des modules. Il est possible d’installer des packages « alternatifs » fournis par des sources non-officielles, mais tant qu’à faire, autant re-générer le noyau et disposer d’un environnement de compilation de modules parfaitement sain. Cela va prendre un temps de calcul assez conséquent (une dizaine d’heures), mais est à la portée de n’importe quel utilisateur du Raspberry Pi.
Compilation d’un noyau pour Raspbian
J’ai téléchargé la dernière version de la distribution Raspbian disponible à ce jour (2014-01-07-wheezy), mais je pense que les manipulations décrites ci-dessous doivent être réalisables sur toute distribution qui propose l’ensemble des outils de compilation (make
, gcc
, etc.) natifs sur le Raspberry Pi.
Téléchargement des sources modifiées pour Raspberry Pi
Tout d’abord, récupérons les sources de Linux incluant les patches spécifiques pour le Raspberry. Attention, il faudra disposer d’environ 2 Go de libre sur la carte SD pour la compilation du noyau. Il peut donc être nécessaire de faire un peu de ménage avant de commencer. Personnellement, j’ai retiré les packages de l’environnement X-Window car sur mon Raspberry Pi de test, je ne me connecte qu’en liaison série ou par le réseau (avec ssh
).
Le téléchargement s’effectue ainsi :
[~]$ git clone http://github.com/raspberrypi/linux rpi-kernel [...] [~]$
Le transfert (1.6 Go) prend environ une heure.
[~]$ cd rpi-kernel [rpi-kernel]$
Configuration du noyau
Vérifions le numéro de version du noyau téléchargé.
[rpi-kernel]$ head Makefile VERSION = 3 PATCHLEVEL = 10 SUBLEVEL = 32 [...] [rpi-kernel]$ uname -r 3.10.25+ [rpi-kernel]$
Le noyau téléchargé et celui fourni par Raspbian sont tous les deux de la branche 3.10. Il est donc possible d’utiliser la configuration de notre noyau courant pour compiler le nouveau.
[rpi-kernel]$ zcat /proc/config.gz > ./.config [rpi-kernel]$
Une autre solution serait de prendre la configuration par défaut pour le Raspberry Pi avec :
[rpi-kernel]$ make bcmrpi_defconfig [...] [rpi-kernel]$
Pour observer et ajuster éventuellement la configuration (notamment en modifiant General Setup -> Local version
pour ajouter nos initiales), il nous faut installer le package ncurses-devel afin de profiter des menus semi-graphiques. Toutefois, ce package porte un nom différent dans les distributions Debian/Raspbian.
[rpi-kernel]$ sudo apt-get install libncurses5-dev [...] [rpi-kernel]$ make menuconfig
Compilation
La compilation nécessite l’utilitaire bc
non installé par défaut sur Raspbian. Ajoutons-le :
[rpi-kernel]$ sudo apt-get install bc [...] [rpi-kernel]$
Une fois la configuration fignolée, on lance la compilation.
[rpi-kernel]$ make
Comptez entre 8 et 10 heures de compilation en fonction de ce que vous avez ajouté ou retiré du noyau, ainsi que de la rapidité de votre carte SD…
Installation
Le lendemain matin, nous finalisons l’installation.
[rpi-kernel]$ sudo make modules_install [...]
On voit alors apparaître l’arborescence /lib/modules/3.10.32
contenant les modules du kernel.
Puis installons le nouveau noyau en prenant garde de conserver le précédent au cas où…
[rpi-kernel]$ sudo cp /boot/kernel.img /boot/kernel.bkup [rpi-kernel]$ sudo cp arch/arm/boot/zImage /boot/kernel.img [rpi-kernel]$ sudo reboot
Essai de l’environnement de compilation
Après avoir redémarré, nous vérifions que le noyau soit bien celui que nous avons généré.
[rpi-kernel]$ uname -r 3.10.32+ [rpi-kernel]$
Compilation d’un module personnalisé
Copions le fichier suivant dans notre répertoire personnel sur le Raspberry Pi. Il s’agit d’une sorte de « Hello World » pour le noyau, c’est le premier exemple que je présente dans mes formations sur la programmation de drivers Linux.
khello.c: #include <linux/module.h> static int __init khello_init (void) { printk(KERN_INFO "%s : HELLO\n", THIS_MODULE->name); return 0; } static void __exit khello_exit (void) { printk(KERN_INFO "%s : BYE!\n", THIS_MODULE->name); } module_init(khello_init); module_exit(khello_exit); MODULE_LICENSE("GPL");
Dans le même répertoire, copions le Makefile suivant. Attention, il est important de respecter les tabulations en début de ligne, il faut s’assurer qu’elles ne sont pas remplacées par des espaces.
Makefile: ifneq (${KERNELRELEASE},) obj-m = khello.o else KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build MODULE_DIR := $(shell pwd) .PHONY: all all: modules .PHONY:modules modules: ${MAKE} -C ${KERNEL_DIR} SUBDIRS=${MODULE_DIR} modules clean: rm -f *.o *.ko *.mod.c .*.o .*.ko .*.mod.c .*.cmd *~ rm -f Module.symvers Module.markers modules.order rm -rf .tmp_versions endif
Nous pouvons à présent compiler notre module.
[~]$ make make -C /lib/modules/3.10.32-perso+/build SUBDIRS=/home/pi modules make[1]: Entering directory `/home/pi/rpi-kernel' CC [M] /home/pi/khello.o Building modules, stage 2. MODPOST 1 modules CC /home/pi/khello.mod.o LD [M] /home/pi/khello.ko make[1]: Leaving directory `/home/pi/rpi-kernel' [~]$ ls khello.c khello.mod.c khello.o modules.order rpi-kernel khello.ko khello.mod.o Makefile Module.symvers [~]$
Essai du module
Le module est bien présent, chargeons-le:
[~]$ sudo insmod khello.ko [~]$ lsmod Module Size Used by khello 683 0 snd_bcm2835 16118 0 snd_soc_bcm2708_i2s 5462 0 regmap_mmio 2778 1 snd_soc_bcm2708_i2s snd_soc_core 132437 1 snd_soc_bcm2708_i2s [...] [~]$
Le module est bien chargé dans le kernel, vérifions si son message d’initialisation est visible.
[~]$ dmesg |tail [ 3.029926] smsc95xx v1.0.4 [ 3.108995] smsc95xx 1-1.1:1.0 eth0: register 'smsc95xx' at usb-bcm2708_usb-1.1, smsc95xx USB 2.0 Ethernet, b8:27:eb:c9:e5:8d [ 3.953891] udevd[157]: starting version 175 [ 6.215100] bcm2708-i2s bcm2708-i2s.0: Failed to create debugfs directory [ 11.279103] EXT4-fs (mmcblk0p2): re-mounted. Opts: (null) [ 11.765049] EXT4-fs (mmcblk0p2): re-mounted. Opts: (null) [ 20.017728] smsc95xx 1-1.1:1.0 eth0: hardware isn't capable of remote wakeup [ 21.550910] smsc95xx 1-1.1:1.0 eth0: link up, 100Mbps, full-duplex, lpa 0x45E1 [ 24.950826] Adding 102396k swap on /var/swap. Priority:-1 extents:1 across:102396k SSFS [ 5697.509567] khello : HELLO [~]$
Nous allons retirer le module de la mémoire du kernel.
[~]$ sudo rmmod khello pi@raspberrypi:~$ dmesg |tail [ 3.108995] smsc95xx 1-1.1:1.0 eth0: register 'smsc95xx' at usb-bcm2708_usb-1.1, smsc95xx USB 2.0 Ethernet, b8:27:eb:c9:e5:8d [ 3.953891] udevd[157]: starting version 175 [ 6.215100] bcm2708-i2s bcm2708-i2s.0: Failed to create debugfs directory [ 11.279103] EXT4-fs (mmcblk0p2): re-mounted. Opts: (null) [ 11.765049] EXT4-fs (mmcblk0p2): re-mounted. Opts: (null) [ 20.017728] smsc95xx 1-1.1:1.0 eth0: hardware isn't capable of remote wakeup [ 21.550910] smsc95xx 1-1.1:1.0 eth0: link up, 100Mbps, full-duplex, lpa 0x45E1 [ 24.950826] Adding 102396k swap on /var/swap. Priority:-1 extents:1 across:102396k SSFS [ 5697.509567] khello : HELLO [ 5760.410545] khello : BYE! [~]$
Conclusion
Nous voyons qu’il est tout à fait possible d’utiliser un Raspberry Pi pour réaliser des compilations natives de modules du noyau. Cela réclame un temps de calcul important, mais rien ne nous oblige à rester à proximité. L’intérêt est alors de disposer d’une petite plate-forme embarquée conviviale (grâce à l’ensemble des applications fournies par la distribution) que nous pouvons utiliser pour développer facilement nos propres modules noyau.
Bonjour, Merci bien pour vos articles forts instructifs. J’ai un problème à la fin du tuoriel, je n’arrive pas à booter sur mon nouveau kernel.img, via la console série le kernel est décompresser et puis plus rien. Pouvez vous me donner des pistes pour résoudre ce problème? Merci
Bonjour,
Comment avez-vous obtenu le fichier
.config
?Il faut vérifier que le support pour le système de fichiers de la partition Root (ext2 ou ext4 en général) soit bien intégré statiquement dans la configuration du noyau.
Bonsoir,
Pour obtenir le fichier « .config », j’ai utiliser la commande: zcat /proc/config.gz > ./.config
En suite, j’ai utiliser la commande: make menuconfig, j’ai réalisé les réglages comme voulu et j’ai sauvegardé avant de lancer la compilation.
Je vais faire la vérification pour voir si le support système de fichier est intégré statiquement dans la configuration noyau.
Merci bien.
Bonsoir, je n’arrive pas à faire la vérification lors de la configuration du kernel. Souhaitez vous que je vous transmette mon fichier de configuration pour m’aider?
Oui, en effet je pourrai le comparer avec le mien.
Envoyez-moi le fichier plutôt par mail (christophe@blaess.fr)
Bjr,
Où je peux trouver les sources en forme d’archive pour faciliter le téléchargement? svp
Ou si qqn peux compresser les sources et les uploader qqs part car j’ai une connexion très lente !!
Merci
« Comptez entre 8 et 10 heures de compilation »
Que penses-tu de l’utilisation de distcc à ce moment? Pour un autre programme, je suis passé d’une compilation de 8h à 30min.
Je ne sais pas si la config de ton rpi au moment de cette compilation permet d’utiliser distcc mais je donne l’idée ^^
J’avais trouvé un bon tuto pour configurer facilement distcc:
http://jeremy-nicola.info/portfolio-item/cross-compilation-distributed-compilation-for-the-raspberry-pi/
Si mes souvenirs sont bon il y a une erreur dans le tuto sur un lien symbolique
C’est une très bonne idée, je vais faire des essais. Ça me donne un bon sujet d’article.
Bonjour,
J’ai beaucoup de mal à démarrer votre tuto, car la permière ligne bloque par manque de place.
J’utilise Wheezy raspbian avec une carte de 8G.
Est-ce vraiment trop juste où faut-il utiliser un rasbian plus léger.
Mais lequel???
Ci-dessous l’erreur:
remote: Counting objects: 3970980, done.
fatal: write error: No space left on device00.77 MiB | 991 KiB/s
Merci d’avance.
Bonjour,
Si vous n’avez pas suffisamment de place pour télécharger le noyau sur une carte de 8Go, je pense que vous n’avez pas lancé « raspi-config » puis « Expand filesystem » pour occuper toute la carte SD.
Merci pour ces infos, je ne connaissais pas ces options.
Maintenant, c’est en cours de compilation depuis midi.
Courage, ça doit commencer à approcher de la fin…
Ce n’est qu’un petit processeur embarqué après tout, on lui réclame un gros boulot 😉
Bonjour,
Une autre technique pour le faire rapidement, avoir un environnement arm sur une machine x86/64 auquel on accède via un chroot !
C’est comme ça que je compile mes binaires gentoo pour mon rasp avant de les déployer
Pour un peu plus d’info :
https://www.gentoo.org/proj/en/base/embedded/handbook/?part=1&chap=5
Bonjour, merci pour votre article très intéressant.
Par contre j’ai un problème après le reboot, en tapant uname -r, j’ai toujours l’ancienne version du noyau, je ne comprend pas d’où sa vient. J’ai aussi essayé en copiant zImage en kernel.img, mais j’ai le même résultat. Vous auriez une idée sur le problème ?
J’ai suivi la procédure décrite ici : https://www.raspberrypi.org/documentation/linux/kernel/building.md
Avant de commencer en tapant uname -r j’obtiens 3.18.11
Après le make modules_install j’ai le dossier 3.18.13-v7+ qui se crée dans le dossier lib.
Je copie l’ Image en kernel.img je reboot et après avoir tapé uname -r, je suis toujours en 3.18.11, je change le Makefile pour qui utilise le dossier 3.18.13-v7+, sa fonctionne pour compiler mais pour charger le module cela me met invalid module format. Ce qui est logique je pense étant donné que mon noyau n’est pas à la bonne version.
J’ai recommencé 3 fois, je ne vois vraiment pas comment faire, es-ce que je peux trouver les anciens fichier pour que le dossier crée après le make modules_install soit le 3.18.11-v7+ ?
J’ai finalement trouvé la solution ici : https://www.raspberrypi.org/forums/viewtopic.php?f=66&t=103040
[…] the same source this post also describes well what you have to do to be able to compile a kernel module on a raspberry […]
Dans mon cas pour une raspberry pi 3 sur raspbian, pour installer le nouveau noyau il faut remplacer cette instruction :
sudo cp arch/arm/boot/zImage /boot/kernel.img
par :
sudo cp arch/arm/boot/zImage /boot/kernel7.img
De même pour l’instruction précdente.
Source :
https://www.disk91.com/2015/technology/systems/rf433-raspberry-pi-gpio-kernel-driver-for-interrupt-management/