Xénomai sur APF28, avec un kernel moderne (3.8)

Cher journal,

Je t’écris aujourd’hui car je viens de voir une news passer sur LinuxFR disant que le kernel long-term 3.0 de Linux n’est plus supporté car cela fait deux ans qu’il est sorti.

Deux ans !

Et dire qu’Armadeus se traine encore le support d’antiquités comme le 2.6.29 pour l’apf27 ou le 2.6.35 pour l’APF28. Il est temps de se lancer dans des trucs un peu moderne comme le device tree, et d’utiliser un xenomai récent.

Je vais donc t’expliquer, mon cher journal, ma démarche d’installation d’un xenomai récent sur un kernel Linux 3.8 (donc récent).

Le kernel 3.8 ou l’arrivée du Device Tree

La raison pour laquelle j’avais utilisé un kernel 3.4.6 pour faire mes premières expérimentations de xenomai était que c’est un noyau suffisamment récent pour avoir des technologies moderne, mais c’est un noyau suffisamment «vieux» pour éviter la révolution du Device Tree.

Sauf que le 3.4.6 n’est plus si neuf et si on veux rester un peu dans la course il faut bien s’y mettre un jour à ce fameux dt. Je me suis donc lancé sur le 3.8 car c’est le noyau le plus récent qui est supporté par adeos pour l’architecture arm.

Device tree ?

Le device tree est une technologie qui s’est imposée sur les architectures arm pour Linux. À l’origine sur les autres architectures, nous avions souvent un hard assez uniforme pour un type de processeur, les architectures des cartes mères en x86 se ressemblent en générale par exemple. Cette diversité limité des architectures permettait d’inscrire en dur dans le code de Linux un fichiers source pour chaque plate-forme (cf le répertoire arch/ dans le code de linux).

Intégrer une nouvelle architecture sur un ancien noyau consiste donc à créer un fichier source du nom de la plate-forme, et y décrire les différentes adresses mémoire, les fréquences d’horloges, les lignes d’interruptions, numéro de gpio, composant présent sur chaque bus. Le tout dans des structures C appelées au démarrage. C’est ce qu’on trouve pour Armadeus dans les fichiers suivant par exemple:


# pour le kernel 2.6.35.3 sur APF28
arch/arm/mach-mx28/apf28dev.c
arch/arm/mach-mx28/mach-apf28.c

Le problème avec l’ARM c’est que le nombre d’architectures développées autour ce processeur est pléthorique (voir /arch/arm/ dans le code Linux), et cela commençait à devenir ingérable. C’est pourquoi il a été décidé de séparer la descriptions des architectures avec le codes (générique) de Linux.

L’idée du device tree est de compiler une fois pour toute le noyau linux pour toutes les architectures du processeur et de démarrer Linux en passant la description de la plate-forme en paramètre. Cela se traduit sous uboot par un binaire à télécharger en plus, heureusement la macro est déjà prête comme expliqué sur le wiki il suffit de faire un run update_dtb :


# donner l'adresse mémoire du dt
setenv fdt_addr_r 41000000
# mettre à jour
run update_dtb

Dans l’idéal, quand cela aura été intégré correctement chez Armadeus, il n’y aura plus qu’un seul binaire du noyau linux pour toutes nos cartes et il suffira de charger le bon device tree pour l’APF que l’on utilise. Ce qui sera un véritable gain en maintenance et permettra à armadeus d’être beaucoup plus à jour avec un moindre effort (un seul noyau à supporter pour toutes les cartes).

Mais nous n’y sommes pas encore, c’est un peu une quête du Grââl pour l’instant 😉

Pour l’instant nous en sommes encore à l’étape de bouts de code poussé dans la mainline armadeus et pas encore officiellement supporté. C’est pourquoi il faut encore faire de nombreuses manipulations pour faire tourner un kernel moderne sur l’APF28.

Installation du Kernel 3.8 pour APF28

Comme dans mon billet précédent, il faut «descendre» une vue récente d’armadeus avec git :


$ git clone git://git.code.sf.net/p/armadeus/code apf28

Puis le configurer pour l’APF28 :


$ cd apf28
$ make apf28_defconfig

Cette dernière commande a pour vocation de configurer buildroot pour notre APF28 avec la configuration officiellement supporté par Armadeus. Mais nous, nous voulons un kernel plus récent (le 3.8 si vous suivez encore), donc nous allons devoir modifier tout ça comme expliqué sur le wiki.


Toolchain  --->
     Kernel Headers (Linux 2.6 (manually specified version))  --->
     (3.8) linux version
 ...
 Kernel  --->
     Kernel version (Custom version)  --->
         (3.8) Kernel version
     ... 
     (40008000) load address (for 3.7+ multi-platform image)
     [*]   Device tree support
     (imx28-apf28dev) Device Tree Source file names
     ...
     Linux Kernel Extensions  --->
         [*] Adeos/Xenomai Real-time patch
         (http://download.gna.org/adeos/patches/v3.x/arm/) Adeos patch URL
         (ipipe-core-3.8-arm-1.patch) Path for Adeos patch file
 ...
 System configuration  --->
     (ttyAMA0) Port to run a getty (login prompt) on
 ...

 Package Selection for the target --->
    Real-Time --->
        [*] Xenomai Userspace
        [*]   Install testsuite
...

Ne pas oublier de virer les patches freescales qui trainent encore pour les anciens noyau dans le fichier buildroot/.config :


BR2_LINUX_KERNEL_PATCH="../patches/linux/$(BR2_LINUX_KERNEL_VERSION)"

Le nom du driver de nand a changé depuis la version utilisé par défaut chez Armadeus. Il est donc nécessaire de le changer dans U-Boot pour qu’il passe les bons paramètre à Linux au démarrage. Pour cela il faut modifier le fichier suivant :


buildroot/target/device/armadeus/apf28/apf28-u-boot-2013.04.h

Et à la ligne 153 mettre la valeur suivante :


#define CONFIG_MTDMAP			"gpmi-nand"

Xenomai 2.6.3

Par defaut, la version de xenomai sur armadeus est la 2.6.2.1. Comme on veut le dernier cri on va changer la version à la 2.6.3 en modifiant le package dans :


buildroot/package/xenomai/xenomai.mk

En modifiant la ligne :


XENOMAI_VERSION = 2.6.3

Et comme pour le 3.4.6 il faut virer les patches qui trainent dans ce répertoire :


$ cd buildroot/package/xenomai/
$ rm adeos-00-compatibility_with_armadeus.patch adeos-01-adeos-prevent_system_freeze_on_mxc_with_gpio_generated_interrupts.patch

On modifie toujours busybox :


make busybox-menuconfig

En ajoutant l’option suivante:


Shells  --->
        Choose your default shell (ash)  --->
    --- ash
    ---   Ash Shell Options
          ...
          [*]   Builtin getopt to parse positional parameters 

make

Après toutes ces modifications on peut enfin lancer la compilation générale:

make

Il semble y avoir une erreur étrange avec le packet xenomai, pour être sur qu’il le compile et l’installe, une fois la première compilation complète réaliser il faut relancer une compilation spécifique du package xenomai:


make xenomai-dirclean;make xenomai;make linux;make;cp -v buildroot/output/images/* /tftpboot/

Installation de la cible

L'installation sur la cible est légèrement différente des binaires officiels car nous avons à installer le fameux devices tree. Et le kernel étant récent, il faut aussi changer le nom de l'uart utilisé pour la console:


$ setenv console console=ttyAMA0,115200n8
$ setenv consoledev ttyAMA0
$ setenv fdt_addr_r 41000000
$ saveenv

Si l'ip du serveur tftp est configurée correctement il suffit de faire un update_all pour (presque) tout installer :


run update_all

À ce niveau on peut faire une tentative de boot, mais nous allons rester bloqué à la décompression du kernel :


BIOS> boot

NAND read: device 0 offset 0x400000, size 0x80000
 524288 bytes read: OK

Loading from nand0, offset 0x500000
   Image Name:   Linux-3.8.0-ipipe
   Created:      2013-10-27  14:55:29 UTC
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    2969768 Bytes = 2.8 MiB
   Load Address: 40008000
   Entry Point:  40008000
## Booting kernel from Legacy Image at 40000000 ...
   Image Name:   Linux-3.8.0-ipipe
   Created:      2013-10-27  14:55:29 UTC
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    2969768 Bytes = 2.8 MiB
   Load Address: 40008000
   Entry Point:  40008000
   Verifying Checksum ... OK
## Flattened Device Tree blob at 41000000
   Booting using the fdt blob at 0x41000000
   Loading Kernel Image ... OK
OK
   Loading Device Tree to 47b33000, end 47b39d9f ... OK

Starting kernel ...

Uncompressing Linux... done, booting the kernel.

Patch adéos

D'après Marek Vasut il y a un problème dans le patch adeos pour imx28, il faut modifier le fichier icoll.c dans linux :


$ vim buildroot/output/build/linux-3.8/arch/arm/mach-mxs/icoll.c

Et modifier la ligne 95 :


-    handle_IRQ(irqnr, regs);
+    ipipe_handle_multi_irq(irqnr, regs);

On peut ensuite tout recompiler pour avoir quelques chose de fonctionnel :

 make linux;make;mv -v buildroot/output/images/* /tftpboot/

Testbench Xenomai

On peut valider le bon démarrage de Xenomai au moyen de la commande suivante :

# dmesg | grep Xeno
I-pipe: head domain Xenomai registered.
Xenomai: hal/arm started.
Xenomai: scheduling class idle registered.
Xenomai: scheduling class rt registered.
Xenomai: real-time nucleus v2.6.3 (Lies and Truths) loaded.
Xenomai: starting native API services.
Xenomai: starting POSIX services.
Xenomai: starting RTDM services.

Pour lancer le testbench complet de Xenomai, ne pas oublier de monter le driver idoine :

modprobe xeno_switchtest

Puis pour lancer le test :

xeno-test

Ce qui donne le résultat suivant sur l'apf28 :


Started child 584: /bin/sh /usr/bin/xeno-test-run-wrapper /usr/bin/xeno-test
+ echo 0
+ /usr/bin/arith
mul: 0x79364d93, shft: 26
integ: 30, frac: 0x4d9364d9364d9364

signed positive operation: 0x03ffffffffffffff * 1000000000 / 33000000
inline calibration: 0x0000000000000000: 375.074 ns, rejected 5/10000
inlined llimd: 0x79364d9364d9362f: 4084.395 ns, rejected 8/10000
inlined llmulshft: 0x79364d92ffffffe1: 94.725 ns, rejected 2/10000
inlined nodiv_llimd: 0x79364d9364d9362f: 167.000 ns, rejected 2/10000
out of line calibration: 0x0000000000000000: 375.174 ns, rejected 3/10000
out of line llimd: 0x79364d9364d9362f: 4084.758 ns, rejected 10/10000
out of line llmulshft: 0x79364d92ffffffe1: 123.900 ns, rejected 1/10000
out of line nodiv_llimd: 0x79364d9364d9362f: 166.625 ns, rejected 4/10000

signed negative operation: 0xfc00000000000001 * 1000000000 / 33000000
inline calibration: 0x0000000000000000: 375.049 ns, rejected 6/10000
inlined llimd: 0x86c9b26c9b26c9d1: 4124.954 ns, rejected 10/10000
inlined llmulshft: 0xd45d172d0000001e: 124.375 ns, rejected 2/10000
inlined nodiv_llimd: 0x86c9b26c9b26c9d1: 166.995 ns, rejected 2/10000
out of line calibration: 0x0000000000000000: 375.187 ns, rejected 4/10000
out of line llimd: 0x86c9b26c9b26c9d1: 4118.808 ns, rejected 10/10000
out of line llmulshft: 0xd45d172d0000001e: 94.737 ns, rejected 3/10000
out of line nodiv_llimd: 0x86c9b26c9b26c9d1: 166.787 ns, rejected 2/10000

unsigned operation: 0x03ffffffffffffff * 1000000000 / 33000000
inline calibration: 0x0000000000000000: 375.012 ns, rejected 7/10000
inlined nodiv_ullimd: 0x79364d9364d9362f: 83.391 ns, rejected 2/10000
out of line calibration: 0x0000000000000000: 375.058 ns, rejected 1/10000
out of line nodiv_ullimd: 0x79364d9364d9362f: 165.141 ns, rejected 4/10000
+ /usr/bin/clocktest -C 42 -T 30
== Tested clock: 42 (CLOCK_HOST_REALTIME)
CPU      ToD offset [us] ToD drift [us/s]      warps max delta [us]
--- -------------------- ---------------- ---------- --------------
  0                  2.7            0.005          0            0.0
+ /usr/bin/switchtest -T 30
== Testing FPU check routines...
== FPU check routines: unimplemented, skipping FPU switches tests.
== Threads: sleeper0-0 rtk0-1 rtk0-2 rtup0-3 rtup0-4 rtus0-5 rtus0-6 rtuo0-7 rtuo0-8
RTT|  00:00:01
RTH|---------cpu|ctx switches|-------total
RTD|           0|        5537|        5537
RTD|           0|        5535|       11072
RTD|           0|        5535|       16607
RTD|           0|        5558|       22165
RTD|           0|        5539|       27704
RTD|           0|        5535|       33239
RTD|           0|        5535|       38774
RTD|           0|        5558|       44332
RTD|           0|        5535|       49867
RTD|           0|        5535|       55402
RTD|           0|        5537|       60939
RTD|           0|        5517|       66456
RTD|           0|        5537|       71993
RTD|           0|        5576|       77569
RTD|           0|        5519|       83088
RTD|           0|        5578|       88666
RTD|           0|        5537|       94203
RTD|           0|        5519|       99722
RTD|           0|        5535|      105257
RTD|           0|        5551|      110808
RTD|           0|        5542|      116350
RTT|  00:00:22
RTH|---------cpu|ctx switches|-------total
RTD|           0|        5537|      121887
RTD|           0|        5578|      127465
RTD|           0|        5517|      132982
RTD|           0|        5537|      138519
RTD|           0|        5578|      144097
RTD|           0|        5519|      149616
RTD|           0|        5578|      155194
RTD|           0|        5535|      160729
RTD|           0|        4657|      165386
+ /usr/bin/cond-torture-native
simple_condwait
relative_condwait
absolute_condwait
sig_norestart_condwait
sig_restart_condwait
sig_norestart_condwait_mutex
sig_restart_condwait_mutex
sig_norestart_double
sig_restart_double
cond_destroy_whilewait
Test OK
+ /usr/bin/cond-torture-posix
simple_condwait
relative_condwait
absolute_condwait
sig_norestart_condwait
sig_restart_condwait
sig_norestart_condwait_mutex
sig_restart_condwait_mutex
sig_norestart_double
sig_restart_double
cond_destroy_whilewait
Test OK
+ /usr/bin/mutex-torture-native
simple_wait
recursive_wait
timed_mutex
mode_switch
pi_wait
lock_stealing
NOTE: lock_stealing mutex_trylock: not supported
deny_stealing
simple_condwait
recursive_condwait
auto_switchback
Test OK
+ /usr/bin/mutex-torture-posix
simple_wait
recursive_wait
errorcheck_wait
timed_mutex
mode_switch
pi_wait
lock_stealing
NOTE: lock_stealing mutex_trylock: not supported
deny_stealing
simple_condwait
recursive_condwait
auto_switchback
Test OK
+ start_load
+ echo start_load
+ check_alive /usr/bin/latency
+ echo check_alive /usr/bin/latency
+ wait_load
Started child 640: dohell 900
Started child 641: /usr/bin/latency
== Sampling period: 1000 us
== Test mode: periodic user-mode task
== All results in microseconds
warming up...
RTT|  00:00:01  (periodic user-mode task, 1000 us period, priority 99)
RTH|----lat min|----lat avg|----lat max|-overrun|---msw|---lat best|--lat worst
RTD|     77.333|     87.708|    108.541|       0|     0|     77.333|    108.541
RTD|     44.166|     87.458|    109.958|       0|     0|     44.166|    109.958
RTD|     66.249|     87.583|    109.166|       0|     0|     44.166|    109.958
RTD|     67.624|     87.874|    108.958|       0|     0|     44.166|    109.958
RTD|     68.583|     87.916|    100.583|       0|     0|     44.166|    109.958
RTD|     66.458|     87.791|     95.416|       0|     0|     44.166|    109.958
RTD|     66.916|     87.791|     97.541|       0|     0|     44.166|    109.958
RTD|     67.374|     87.833|     96.749|       0|     0|     44.166|    109.958
RTD|     68.624|     87.833|     98.416|       0|     0|     44.166|    109.958
RTD|     66.749|     87.833|     97.249|       0|     0|     44.166|    109.958
RTD|     67.249|     87.874|     97.916|       0|     0|     44.166|    109.958
RTD|     67.833|     87.874|     96.708|       0|     0|     44.166|    109.958
RTD|     65.999|     87.791|     98.374|       0|     0|     44.166|    109.958
RTD|     67.541|     87.874|     96.749|       0|     0|     44.166|    109.958
RTD|     67.958|     87.833|     95.249|       0|     0|     44.166|    109.958
RTD|     66.874|     87.916|     98.791|       0|     0|     44.166|    109.958
/usr/bin/xeno-test-run-wrapper: /usr/bin/xeno-test: line 1: can't open /tmp/xeno-test-in-584: Interrupted system call
---|-----------|-----------|-----------|--------|------|-------------------------
RTS|     44.166|     87.791|    109.958|       0|     0|    00:00:17/00:00:17

Conclusion

Et voila, mon cher journal, le résultat de toute mon épopée fantastique. La quête fut longue et fastidieuse, et cette procédure mériterait certainement une configuration dédiée chez armadeus.
Que penserais-tu, mon cher journal d'un :

make apf28_xeno3.8_defconfig

Je suis sur que ça intéresserait du monde, on pourrait même envisager de faire une telle cible pour chaque noyau partiellement supporté dans armadeus. Vu les récents commit il semble y avoir pas mal de personnes qui se lance dans la compilation autre que le kernel officiel armadeus.

Ce contenu a été publié dans embarqué, informatique. Vous pouvez le mettre en favoris avec ce permalien.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *