M2 SETI B4 / MS SE SE758


TP Votre premier module

Introduction

Objectifs

L’objectif de ce TP est de compiler et charger votre premier module noyau.

Environnement de TP

Répertoire de travail

Vous aller créer un répertoire de travail pour y stocker les différents fichiers nécessaires pour les travaux pratiques.

Si vous travaillez sur les machines de TP de l’école, compte-tenu de la taille des fichiers à récupérer, je vous conseille de mettre votre répertoire de travail dans /home/users/xxx (où xxx est votre nom d’utilisateur) afin de ne pas saturer votre répertoire personnel (qui est limité en taille). Faites attention par contre, /home/users/xxx est un répertoire local à la machine sur laquelle vous êtes (il n’est pas partagé entre les machines comme l’est votre répertoire personnel) et il n’est pas sauvegardé.

$ export TPROOT=~/se758  # Si vous êtes sur votre machine
$ export TPROOT=/home/users/xxx/se758 # Si vous êtes sur une machine de l'école (adaptez xxx)
$ mkdir $TPROOT
$ cd $TPROOT

Dans la suite des TP, nous ferons référence à ce répertoire de travail par l’intermédiaire de la variable d’environnement TPROOT.

Chaîne de compilation

Vous aurez besoin par la suite de la chaîne de compilation arm-linux-gnueabihf fournie par Linaro en version 7.4.1.

Si vous êtes sur une machine de l’école, ajoutez simplement le répertoire /comelec/softs/opt/gnu_tools_for_arm/gcc-linaro-7.4.1-2019.02-x86_64_arm-linux-gnueabihf/bin dans votre variable d’environnement PATH.

Si vous êtes sur votre machine personnelle, téléchargez-la ici : https://releases.linaro.org/components/toolchain/binaries/7.4-2019.02/arm-linux-gnueabihf/gcc-linaro-7.4.1-2019.02-x86_64_arm-linux-gnueabihf.tar.xz, puis décompressez-la quelque part (par exemple dans votre répertoire de TP) et ajoutez le chemin /xxx/gcc-linaro-7.4.1-2019.02-x86_64_arm-linux-gnueabihf/bin (en remplaçant /xxx/ par le chemin correct) dans votre variable d’environnement PATH.

Dans les deux cas, une fois votre variable PATH ajustée, tapez la commande :

$ arm-linux-gnueabihf-gcc --version

et vérifiez que le résultat est bien celui-ci :

arm-linux-gnueabihf-gcc (Linaro GCC 7.4-2019.02) 7.4.1 20181213 [linaro-7.4-2019.02 revision 56ec6f6b99cc167ff0c2f8e1a2eed33b1edc85d4]

Noyau pré-compilé

Pour pouvoir compiler un module, vous aurez besoin des en-têtes du noyau utilisé ainsi que quelques autres informations.

Récupérez donc l’archive contenant les sources du noyau et le résultat de sa compilation linux_build.tar.xz (300 Mo, 1,9 Go après désarchivage).

Décompressez l’archive dans votre répertoire de travail :

$ tar xJf linux_build.tar.xz

QEMU

Pour simuler un système embarqué complet à base de processeur ARM, nous allons utiliser QEMU. En plus de ne pas avoir besoin de matériel physique pour expérimenter, Qemu permet également de facilement tester, mais aussi débugger votre système embarqué.

Plus précisément, nous allons utiliser qemu-system-arm qui est un émulateur de machine complète, en l’occurrence, une carte de développement Arm Versatile Express munie d’une carte d’extension CoreTile Express A9x4 à base de Cortex-A9.

Les détails de ce que QEMU simule concernant cette carte sont disponibles dans la documentation : https://www.qemu.org/docs/master/system/arm/vexpress.html.

Nous utiliserons une version particulière de qemu-system-arm (elle ajoute à QEMU un périphérique émulé pour lequel vous allez écrire un pilote de périphérique dans la suite du cours).

Téléchargez :

Placez l’exécutable téléchargé dans votre répertoire $TPROOT, puis vérifiez que les droits sont corrects pour l’exécution :

$ chmod u+x qemu-system-arm

Normalement, cet exécutable utilise des bibliothèques partagées qui devraient être disponibles sur votre machine. Néanmoins, vérifiez que toutes les bibliothèques sont présentes en tapant :

$ ldd qemu-system-arm

Si vous désirez, le code source de QEMU ainsi que les modifications apportées sont disponibles dans la branche adxl345 du dépôt https://gitlab.telecom-paris.fr/guillaume.duc/qemu.

Système de fichiers

Le dernier élément à récupérer est un système de fichier minimal pour le linux embarqué tournant dans QEMU. Cette image a été générée en utilisant Buildroot version 2020.02.11 et contient :

Téléchargez le fichier rootfs.cpio.gz.

Vérifiez que tout fonctionne ensemble

Vérifiez que le noyau et l’image fournis démarrent correctement :

$ ./qemu-system-arm -nographic -machine vexpress-a9 -kernel linux-5.10.19/build/arch/arm/boot/zImage \
-dtb linux-5.10.19/build/arch/arm/boot/dts/vexpress-v2p-ca9.dtb -initrd rootfs.cpio.gz

QEMU va démarrer la simulation d’une carte Versatile Express en plaçant dans la mémoire (simulée) de cette carte, le noyau, l’arbre des périphérique et l’image du système de fichier initial fournis. Il va également connecter le port série (virtuel) de cette carte à la console. Donc tout ce que vous verrez s’afficher dans votre terminal proviendra de cette simulation et tout ce que vous tapez sera envoyé, par l’intermédiaire du port série virtuel, à la carte simulée.

Vous devriez voir sur la console le démarrage de Linux puis un écran de login. Vous pouvez vous connecter en tant que root (sans mot de passe).

Pour quitter QEMU, faites ctrl-a c, puis quit (ou q). La simulation s’arrête alors et vous revenez à votre terminal classique sur votre machine.

Votre premier module

Code

Voici le code de votre premier module.

/* first.c */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

static int __init first_init(void)
{
  pr_info("Hello world!\n");
  return 0;
}

static void __exit first_exit(void)
{
  pr_info("Bye\n");
}

module_init(first_init);
module_exit(first_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("My first module");
MODULE_AUTHOR("Me");

Ce module ne fait pas grand chose, à l’exception d’afficher un message lors de son chargement et un autre lors de son déchargement.

Makefile

Voici le fichier Makefile nécessaire pour compiler le module :

ifneq ($(KERNELRELEASE),)
# kbuild part of makefile
obj-m  := first.o

else
# normal makefile
KDIR ?= /lib/modules/`uname -r`/build

default:
    $(MAKE) -C $(KDIR) M=$$PWD
endif

Attention ! La ligne $(MAKE) -C... qui indique la commande à exécuter pour construire la cible default doit être indentée à l’aide d’une tabulation et non d’un ou plusieurs espaces.

Ce Makefile est très bien adapté à la compilation d’un module depuis la machine sur lequel vous voulez l’utiliser (compilation native). Nous verrons par la suite comment l’utiliser dans le contexte de la compilation croisée (cross-compilation).

Outils de gestion des modules

Plusieurs outils vous permettent de manipuler les modules :

Travail à faire

  1. Créez un répertoire de travail pour votre premier module :

    $ cd $TPROOT
    $ mkdir premier_module
    $ cd premier_module
  2. Copiez le module ci-dessus ainsi que le Makefile

  3. Compilez votre module

    $ make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm KDIR=../linux-5.10.19/build/

    La variable CROSS_COMPILE indique le préfixe des outils de la chaîne de compilation que nous utilisons (donc le gcc qui sera appelé est arm-linux-gnueabihf-gcc). La variable ARCH indique que nous compilons pour l’architecture arm. Enfin KDIR pointe vers le répertoire dans lequel le noyau a été compilé (en effet, bien que vous compilez le code de votre module à l’extérieur de l’arbre des sources du noyau, le mécanisme de compilation a besoin notamment des fichiers d’entêtes du noyau).

    Votre module est maintenant dans le fichier first.ko.

  4. Démarrez QEMU :

    $ cd ..
    $ ./qemu-system-arm -nographic -machine vexpress-a9 -kernel linux-5.10.19/build/arch/arm/boot/zImage \
    -dtb linux-5.10.19/build/arch/arm/boot/dts/vexpress-v2p-ca9.dtb -initrd rootfs.cpio.gz \
    -fsdev local,path=premier_module,security_model=mapped,id=mnt -device virtio-9p-device,fsdev=mnt,mount_tag=mnt

    Voici quelques explications sur les différents arguments passés à QEMU :

  5. Après vous être loggué en tant que root, montez le partage avec la machine hôte en tapant, dans la console émulée par QEMU :

    $ mount -t 9p -o trans=virtio mnt /mnt -oversion=9p2000.L,msize=10240

    Si cette commande s’exécute correctement, vous aurez alors accès, dans le répertoire /mnt de la machine émulée par QEMU, au contenu du répertoire premier_module de votre machine (et donc au module que vous venez de compiler).

  6. Chargez votre module (depuis QEMU) :

    $ insmod /mnt/first.ko
  7. Déchargez votre module

    $ rmmod first

Félicitations, vous avez écrit votre premier module pour le noyau.


© Copyright 2020 Guillaume Duc. Le contenu de cette page est mis à disposition selon les termes de la Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 4.0 International (à l'exception des exemples de code tirés du noyau Linux et qui sont distribués sous leurs licences d'origine).

Licence
Creative Commons