Linux Embedded

Le blog des technologies libres et embarquées

Modification du noyau Linux AOSP

Introduction

Dans les précédents articles, nous avons vu comment produire une image AOSP utilisable dans l'émulateur Android. Nous avons également décrit les principales fonctions de l'outil ADB (Android Debug Bridge) indispensable au développement Android « système ».

Dans cet article nous allons décrire la procédure de mise à jour du noyau Linux utilisé par l'émulateur Android. En effet, le noyau utilisé jusqu'à présent n'a pas été compilé car il est fourni sous forme binaire par AOSP dans le répertoire prebuilts.

$ ls -l prebuilts/qemu-kernel/arm/

total 17736

drwxr-xr-x 2 pierre pierre 4096 Oct 16 11:33 2.6

-rw-r--r-- 1 pierre pierre 15872 Oct 16 11:33 LINUX_KERNEL_COPYING

-rw-r--r-- 1 pierre pierre 210 Oct 16 11:33 README

-rwxr-xr-x 1 pierre pierre 2231328 Oct 16 11:33 kernel-qemu

-rwxr-xr-x 1 pierre pierre 2372056 Oct 16 11:33 kernel-qemu-armv7

-rwxr-xr-x 1 pierre pierre 7705 Oct 16 11:33 rebuild.sh

-rwxr-xr-x 1 pierre pierre 5913492 Oct 16 11:33 vmlinux-qemu

-rwxr-xr-x 1 pierre pierre 7602368 Oct 16 11:33 vmlinux-qemu-armv7

Ce noyau a un certain nombre de limitations, entre-autres de ne pas disposer du support des modules dynamiques.

$ adb shell

root@generic:/ # lsmod

/proc/modules: No such file or directory

Ce point peut devenir un problème si l'on veut ajouter des pilotes à la plate-forme dans le cas d'une cible réelle, ce qui arrive fréquemment dans le cas d'applications industrielles. Nous utiliserons l'émulateur mais la procédure est strictement identique pour une cible réelle.

Compilation du noyau Linux pour Android

Les sources du noyau ne sont pas fournies avec AOSP et il est nécessaire de les obtenir auprès d'un dépôt Git hébergé par Google. Pour cela on utilise la commande :

$ git clone https://android.googlesource.com/kernel/goldfish.git

A l'issue du téléchargement, on peut visualiser les différentes branches du dépôt par :

$ cd goldfish

$ git branch -r

 origin/HEAD -> origin/master

 origin/android-goldfish-2.6.29

 origin/android-goldfish-3.10

 origin/android-goldfish-3.4

 origin/linux-goldfish-3.0-wip

 origin/master

Nous allons nous intéresser à la version 3.4 et nous créons donc une branche locale dans ce but.

$ git checkout -t origin/android-goldfish-3.4 -b 3.4

A l'issue de cette commande, nous obtenons l'arborescence des sources du noyau Linux pour la cible goldfish.

$ ls -l

total 504

drwxrwxr-x 29 pierre pierre 4096 19 févr. 14:26 arch

drwxrwxr-x 3 pierre pierre 4096 19 févr. 14:26 block

-rw-rw-r-- 1 pierre pierre 18693 19 févr. 14:25 COPYING

-rw-rw-r-- 1 pierre pierre 94984 19 févr. 14:25 CREDITS

drwxrwxr-x 3 pierre pierre 4096 19 févr. 14:26 crypto

La procédure de compilation « croisée » du noyau est identique à celle utilisée dans le cas de GNU/Linux. Le compilateur croisée est fourni dans AOSP. Une fois de plus, il est donc nécessaire de charger les variables d'environnement utilisées par AOSP.

$ cd work

$ source build/envsetup.sh

$ lunch 1

$ type arm-eabi-gcc

arm-eabi-gcc is /home/pierre/Android/work_google/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7/bin/arm-eabi-gcc

Lorsque nous revenons dans le répertoire des sources du noyau, if faut positionner un certain nombre de variables d'environnement, soit :

$ export ARCH=arm

$ export SUBARCH=arm

$ export CROSS_COMPILE=arm-eabi-

La prochaine étape consiste à choisir un fichier de configuration pour le noyau à compiler. Dans notre cas nous utilisons une plate-forme goldfish / ARMv7 d’où la commande :

$ make goldfish_armv7_defconfig

La compilation d'un tel noyau produira une version strictement identique à celle actuellement utilisée. Notre but est de modifier légèrement cette configuration, en particulier de disposer du support des modules dynamiques. Pour cela nous éditons la configuration du noyau par la commande :

$ make menuconfig

Ce qui conduit à l'affichage de l'écran suivant :

Figure 1. Configuration du noyau

Nous constatons que la configuration courante ne dispose effectivement pas du support des modules car l'option Enable loadable module support n'est pas activée. Pour l'activer, il suffit de cocher l'option correspondante ainsi que toutes les options du sous-menu associé :

Figure 2. Activation du support des modules

On peut alors compiler le noyau par la commande make. L'image zImage est disponible dans le répertoire arch/arm/boot. Un nouveau test avec l'émulateur est réalisable par la commande :

$ emulator -kernel arch/arm/boot/zImage &

Une session ADB permet de vérifier la nouvelle version du noyau ainsi que le bon fonctionnement de la commande lsmod.

$ adb shell

root@generic:/ # cat /proc/version

Linux version 3.4.0 (pierre@XPS13-pf) (gcc version 4.7 (GCC) ) #2 PREEMPT Thu Feb 13 18:07:02 CET 2014

root@generic:/ # lsmod

Développement et test d'un module noyau

Nous pouvons à présent développer un module de test « Hello World » que nous chargerons dans l'émulateur Android utilisant ce nouveau noyau. L'API de développement noyau d'Android est identique à celle de GNU/Linux et nous pouvons donc utiliser les même principes que nous allons rappeler brièvement.

Un module noyau est constitué de deux fonctions principales nommées module_init() et module_exit(). La première est appelée lors du chargement du module dans la mémoire du noyau (par insmod ou modprobe) et la seconde appelée lors du dé-chargement de ce même module (par rmmod).

Nous pouvons donc en déduire le code source d'un module très simple basé sur la fonction printk(), équivalente à printf() mais en espace noyau.

#include <linux/module.h>

#include <linux/kernel.h>

MODULE_LICENSE("GPL");

static int __init hello_init(void)

{

  printk (KERN_INFO "Hello World\n");

  return 0;

}

static void __exit hello_exit(void)

{

  printk (KERN_INFO "Goodbye, cruel world!\n");

}

module_init(hello_init);

module_exit(hello_exit);

La compilation du module utilise un fichier Makefile spécial que nous décrivons ci-après. Le fichier indique que la compilation du module nécessite la présence des sources du noyau.

KDIR= $(HOME)/Android/goldfish_kernel

PWD= $(shell pwd)

obj-m := helloworld.o

all:

    $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

install:

    $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules_install

clean:

    rm -f *~ Module.markers Modules.symvers

    $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean

Une fois les variables ARCH et CROSS_COMPILE renseignées, on peut compiler le module par la commande make, ce qui produit un fichier helloworld.ko. Ce module peut être copié sur la cible en utilisant ADB :

$ adb push helloworld.ko /data

On peut ensuite insérer le module par insmod puis vérifier les traces d'affichage en utilisant la commande dmesg.

$ adb shell

root@generic:/ # insmod /data/helloworld.ko

root@generic:/ # lsmod

helloworld 585 0 - Live 0x00000000 (O)

root@generic:/ # dmesg

...

<6>Hello World

On peut également retirer le module de la mémoire par rmmod.

root@generic:/ # rmmod helloworld

root@generic:/ # dmesg

<6>Goodbye, cruel world!

Conclusion

Nous avons pu réaliser assez rapidement une personnalisation de l'image AOSP en modifiant le noyau utilisé. Malgré sa simplicité, le module compilé et testé constitue les prémisses d'une solution d'ajout de fonctionnalité « matérielle » à un système Android. Nous verrons ce point dans une prochaine publication en étudiant la HAL (Hardware Abstraction Layer) Android et l'utilisation de JNI (Java Native Interface) dans une application Java.

Bibliographie

 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée.