Compilation native de modules kernel sur Raspberry Pi

Publié par cpb
Mar 06 2014

2014-03-06On 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.

18 Réponses

  1. Naft dit :

    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

    • cpb dit :

      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.

  2. Naft dit :

    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.

  3. Naft dit :

    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?

  4. Delta dit :

    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

  5. Axel dit :

    « 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

  6. Dnis dit :

    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.

    • cpb dit :

      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.

  7. grim_lokason dit :

    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

  8. Malik dit :

    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 ?

  9. Malik dit :

    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+ ?

  10. […] the same source this post also describes well what you have to do to be able to compile a kernel module on a raspberry […]

  11. Darksa dit :

    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/

URL de trackback pour cette page