Accueil

Débogage en espace noyau

Le débogage en espace noyau est délicat à mettre en oeuvre et concerne essentiellement les éléments tels que : le noyau, les modules et le bootloader. Pour arriver à déboguer chacun de ces éléments, il existe peu de solutions. Ces dernières se réduisent soit à l'exécution dans l'espace utilisateur d'un des trois éléments à déboguer dans une machine virtuelle soit à l'utilisation d'une sonde matérielle JTAG connectée sur la carte cible. La première solution comporte un avantage indéniable : celui du coût nul. Dans son dernier ouvrage, Pierre Ficheux1 utilise l'émulateur QEMU dans le but de déboguer un noyau, un module puis un bootloader avec toutes les détails des différentes étapes.

La solution avec une sonde JTAG est celle la plus chère mais comporte un avantage indéniable celui de se mettre à l'abri d'éventuelles erreurs de simulation. Dans le monde des sondes JTAG, il y en a pour toutes les bourses. D'abord, il y a celles au coût faramineux comportant matériel spécifique par fondeur de puce et logiciel propriétaire, celles dite JTAG universelles avec mis en oeuvre d'un logiciel propriétaire, celles à port USB et mise en oeuvre du logiciel libre OpenOCD2(Open On-Chip Debugger), plus récemment d'UrJTAG3 et enfin celles que l'on peut faire soi-même dite "Wiggler" sur port parallèle.
Avant de se lancer dans l'achat ou la conception d'une sonde JTAG, il convient de bien connaitre ses besoins. Si il s'agit ponctuellement pour un particulier de flasher une zone mémoire parce que le nouveau bootloader fraichement compilé et installé refuse de démarrer, la solution d'une sonde USB avec OpenOCD répond totalement à ce besoin. Par contre, si il s'agit d'optimiser l'empreinte mémoire d'une carte de prototypage destinée à être commercialisée à grande échelle, il convient de s'orienter plutôt vers une solution professionnelle et nécessairement plus chère.
Dans un soucis de présentation d'une solution à base de matériel peu cher, nous nous équipons donc d'une sonde JTAG de type Amontec JTAGkey-Tiny4 peu cher (25 euros) et mettant en oeuvre le logiciel libre OpenOCD.
Avant d'aller plus loin, nous définissons ce qu'est une sonde JTAG et ce que nous pouvons attendre d'elle

Sonde JTAG

Initialement le terme JTAG (Joint Test Action Group) désigne un groupe d'industriel qui s'est formé en 1985 dans le but de normaliser les tests pour circuits imprimés. Le but initial était de détecter les éventuels problèmes de soudure induisant des dysfonctionnements voire la destruction des cartes testées. En 1990, cette norme est devenue officiellement une norme IEEE au numéro 1149.1-1990 Standard Test Access Port and Boundary-Scan Architecture5. Bien que le nom de la norme est plutôt Boundary Scan ou TAP (Test Access Port), le terme JTAG est toujours utilisé par abus de langage.

La norme JTAG utilise un bus série synchrone composé de quatre signaux de contrôle obligatoires et un autre optionnel pour la réinitialisation de la communication dit TRST, (Test ReSeT). Le coeur du bus se compose du signal d'entrée de donnée dit TDI (Test Data Input) et d'un autre de sortie dit TDO (Test Data Output). Pour prévenir le CPU de la mise en communication par JTAG, le signal TMS (Test Mode Select) se charge de cela. Le tout est cadencé par un signal d'horloge TCK, (Test ClocK).
La carte électronique répondant à la norme JTAG doit comporter trois registres:
Les sondes JTAG permettent un accès en lecture/écriture à tout type de mémoire embarqué (flash, RAM). Elles permettent également d'avoir un accès au coeur du processeur (registres, mémoires internes) et peuvent programmer des circuits logiques programmables (FPGA). La norme suit l'évolution et la croissance des systèmes embarqués toujours plus complexe. Elle comprend désormais le déboguage en mémoire Flash et le contrôle des ressources consommées par les programmes (le terme utilisé est le profilage : optimisation de la consommation de ressource).

OpenOCD

OpenOCD (Open On-Chip Debugger) a été initialement développé par Dominic Rath au cour de sa thèse à l'Université des Sciences d'Augsburg. A présent, OpenOCD est clairement un projet libre développé par une communauté mondiale de développeur. OpenOCD permet de donner l'accès JTAG à des terminaux initialement non prévus pour cela. Il fait la traduction logiciel entre la sonde JTAG et un terminal tel que Telnet ou GDB. OpenOCD est capable d'exécuter des scripts écrits en langage TCL. Les scripts permettent d'écrire des fonctions lançant un certain nombre de commande permettant d'automatiser un certain nombre d'opération comme le flashage de la mémoire d'une série de carte identique. L'installation peut se faire soit en utilisant le gestionnaire de paquet de votre distribution soit en téléchargeant l'archive6. La documentation nous donne des exemples de configuration de sonde JTAG et de cartes embarquées nativement prises en charges. Nous allons utiliser une sonde USB-JTAG peu chère (25-30€) la JTAGkey-tiny7 de la société Amontec. Cette sonde est conçue par Amontec autour de la puce FT2232. Cette dernière est conçue par la société "Future Technology Devices International" (FTDI)8 qui est spécialisée dans la conception et la vente de module de conversion pour l'USB. Comme l'annonce Amontec, la sonde JTAG est supportée par OpenOCD et son fichier de configuration jtagkey.cfg se trouve bien parmi la documentation. Le paramétrage de la sonde ne suffit pas car le fichier de configuration doit également comporter celui de la carte cible. Soit nous entrons la configuration de la carte dans le fichier de configuration de la sonde, soit nous spécifions autant de fichier de configuration au démarrage d'OpenOCD avec autant d'option -f. Nous allons partir du fichier de configuration jtagkey.cfg que nous renommons openocd-kb9200.cfg pour le compléter avec les caractéristiques de la carte Kwikbyte kb9202. Pour arriver à nos fins, comme notre carte est pilotée par un processeur Atmel AT91RM9200, nous allons ajouter les lignes de configuration du fichier at91rm9200.cfg situé dans le répertoire target du répertoire OpenOCD à notre fichier de configuration openocd-kb9200.cfg.
nous y ajoutons:
Il est possible d'ajouter dans ce ficher des procédures, des fonctions et bien d'autres commandes9. Pour avoir une idée de la puissance d'OpenOCD, il convient de lire les fichiers de configuration des autres sondes et des cartes nativement supportées. Nous allons pas complexifier inutilement le fichier de configuration.

Nous avons au final :

telnet_port 4444

gdb_port 3333

interface ft2232

ft2232_device_desc "Amontec JTAGkey"

ft2232_layout jtagkey

ft2232_vid_pid 0x0403 0xcff8

adapter_khz 3000

jtag_ntrst_delay 100

jtag_nsrst_delay 100

reset_config trst_and_srst

if { [info exists CHIPNAME] } {

set _CHIPNAME CHIPNAME

}else {

set _CHIPNAME at91rm9200

}

if { [info exists ENDIAN] } {

set _ENDIAN $ENDIAN

} else {

set _ENDIAN little

}

if { [info exists CPUTAPID] } {

set _CPUTAPID $CPUTAPID

} else {

set _CPUTAPID 0x05b0203f

}

# Never allow the following!

if { $_CPUTAPID == 0x15b0203f } {

echo "-------------------"

echo "- ERROR: -"

echo "- ERROR: TapID 0x15b0203f is wrong for at91rm9200 -"

echo "- ERROR: The chip/board has a JTAG select pin/jumper -"

echo "- ERROR: -"

echo "- ERROR: In one position (0x05b0203f) it selects the -"

echo "- ERROR: ARM CPU, in the other position (0x1b0203f) -"

echo "- ERROR: it selects boundry-scan not the ARM -"

echo "- ERROR: -"

echo "-------------------"

jtag newtap $_CHIPNAME cpu −irlen 4 −ircapture 0x1 −irmask 0xf −expected−id $_CPUTAPID

# Create the GDB Target

set _TARGETNAME $_CHIPNAME.cpu

target create $_TARGETNAME arm920t -endian $_ENDIAN −chain−position $_TARGETNAME

# AT91RM9200 has a 16K block of sram @ 0x0020.0000

$_TARGETNAME configure -work-area-phys 0x00200000  -work-area-size 0x4000 -work-area-backup 1

# This chip has a DCC ... use it

arm7_9 dcc_downloads enable

Exemple d'utilisation de la sonde JTAG avec un programme

Nous allons prendre pour exemple un programme exemple.c qui compare un à un 10 variables et affiche la plus grande de toutes. Nous le compilons avec des messages de débogages supplémentaires (option -g) pour fonctionner sur une cible ARM reposant sur la bibliothèque uClibc. Nous allons le charger sur la cible et l'exécuter. Voici les différentes étapes pour commencer à déboguer notre programme avec une sonde JTAG: Nous allons tester notre fichier de configuration en lançant la commande suivante depuis un terminal openocd -f opencd-kb9200.cfg.

Figure 1.1: OpenOCD en attente de connexion

Comme nous le voyons, aucun message d'erreur n'apparait. La sonde se met en attente de connexion. Il suffit ensuite de démarrer la carte cible. Puis depuis un autre terminal, initions une connexion par GDB ou par Telnet en suivant le protocole suivant:
Pour notre exemple de débogage, nous lançons le client GDB pour ARM en passant en paramètre le nom du fichier à déboguer dans un mode "quiet" en entrant arm-unknown-uclibcgnueabi-gdb exemple-uc -q. Le message "Reading symbols from ..." apparait au moment du lancement du client GDB ce qui nous indique que les messages supplémentaires de débogages générés au moment de la compilation de l'exécutable sont lisibles et interprétables par le client GDB.

Depuis le prompt (gdb), connectons-nous sur la carte en tapant : target remote :3333.

 

Figure 1.2: Connexion à OpenOCD + Débogage

 

Depuis GDB, pour faire passer les commandes propres à OpenOCD, il suffit de les faire précéder par monitor ou en alias mon.

Nous allons arrêter la carte en tapant mon halt et vérifier qu'elle est bien arrêtée en entrant dans le terminal mon poll. Nous chargeons ensuite l'exécutable sur la cible en tapant load. Puis nous lançons le débogage du programme en tapant "n" (alias de next). Nous pouvons voir la progression du débogage du programme en le listant par la commande list.
La liste des commandes d'OpenOCD s'obtient en tapant depuis le client GDB mon help. Les principales commandes concernent:
Voici un échantillon des principales commandes pour piloter une carte, flasher la mémoire, copier une image dans la flash et afficher les informations:
Présentons maintenant un exemple de débogage du chargeur de démarrage (bootloader) U-boot.

Déboguons le bootloader U-Boot

Nous rappelons rapidement la compilation d'U-Boot pour une carte cible de type Kwikbyte KB9202C. Depuis le répertoire de la dernière version d'U-boot, configurons le fichier Makefile pour la carte cible en tapant:
make kb9202_config
Puis lançons la compilation:
make CROSS_COMPILE=arm-unknown-linux-uclibcgnueabi-
Après compilation, nous obtenons l'image de U-Boot sous trois formats différents :
Nous allons lancer le débogage du premier fichier u-boot. La méthode de débogage reste la même que l'exemple précédent. Tout d'abord, il faut démarrer la carte de préférence jusqu'au prompt du bootloader. Puis démarrer OpenOCD ainsi que le client GDB avec en paramètre le fichier ELF à déboguer:
arm-unknown-linux-uclibcgnueabi-gdb u-boot -q.

Figure 1.3: OpenOCD en attente de connexion

Dès que le prompt de GDB apparait, nous nous connectons à la cible et nous l'arrêtons :
target remote :3333
mon halt

Nous chargeons le fichier ELF u-boot par load et nous commençons à le deboguer pas à pas ou par portions de code en tapant la lettre s ou n plusieurs fois ou par retour chariot successif après avoir entrée la commande une fois. Pour savoir où nous nous situons dans le code, il suffit de rentrer la commande where. Dans la capture d'écran suivante, nous voyons que le programme U-Boot commence à s'exécuter à partir du fichier cpu/arm920t/start.s situé dans le répertoire /cpu/arm920t

 

Figure 1.4: Débogage de U-Boot

Et le noyau

Quel débogueur ?  

Linus Torvalds a été très réticent pour permettre aux développeurs d'implémenter nativement un outil permettant de déboguer le noyau. C'est ainsi que parallèlement au développement de celui-ci, un certain nombre de projets spécialisés dans le comportement et le traçage (LTTng Linux Trace Toolkit Next Generation), d'analyse de crash (LKCD Linux Kernel Crash Dump de Red Hat), et des debogueurs noyaux KDB (Linux kernel debugger) et KGDB ont vu le jour afin de palier à ce manque criant de débogueur. Le principe de la plupart de ces projets se résume à patcher le noyau et à utiliser des commandes spécifiques pour obtenir les informations de débogage que l'on souhaite. Il a fallut que les équipes de développeur de KDB et KGDB respectent un certain nombre de conditions données par Linus Torvalds pour avoir l'autorisation d'intégrer leur projet dans le tronc commun du code source du noyau (KDB est intégré depuis les versions 2.4.0 et KGDB est intégré pour la première fois dans le noyau 2.6.26). Ce n'est que dans la version 2.6.34 du noyau que les codes sources de KDB et KGDB ont fusionnés.
KDB à l'avantage de s'exécuter directement sur la machine dans lequel le noyau s'exécute mais à la mauvaise idée de faire du débogage au niveau du code machine (affichage du code en assembleur). En revanche, KGDB débogue au niveau du code source ce qui rend le travail de lecture plus aisé. Les inconvénients de KGDB tiennent à la nécessité d'alourdir le noyau de message supplémentaires de débogage et, en plus, à la nécessité d'utiliser une autre machine hôte pourvue du client de débogage GDB parfaitement fonctionnel. Ainsi, pour KGDB, le débogage noyau nécessite au préalable d'activer l'option "Compile the Kernel with debug info" à partir du menu de configuration du noyau. Il est également possible de pousser le niveau des messages de débogage en fonction de se que l'on souhaite déboguer (accès mémoires, dépassement de pile, spinlock ...) en explorant les possibilités qu'offre le menu "Kernel hacking" du noyau. Le principe du débogage avec KGDB nécessite deux noyaux : le noyau obtenu après compilation vmlinux avec tous les informations de débogages et ce même noyau compressé zImage avec en plus un en-tête contenant des informations pour le bootloader U-Boot le plus souvent nommé uImage. Ce dernier est chargé sur la carte cible et démarré par U-Boot. Le noyau se décompresse et commence à s'exécuter puis s'arrête sur l'attente d'une connexion (le plus souvent) par le port série. Sur le PC hôte, le client GDB lit les informations de débogage du noyau natif vmlinux, puis se connecte au serveur GDB sur la cible, commence alors la difficile phase de débogage. Avec KDB et KGDB, nous pouvons enfin inspecter la mémoire, les registres, la liste des processus, mettre des points d'arrêts, etc..

Footnotes:

1Linux embarqué - 3ème éditions - edition Eyrolles
2http://openocd.sourceforge.net
3http://urjtag.org
4http://www.amontec.com/jtagkey-tiny.shtml
5http://standards.ieee.org/findstds/standard/1149.7-2009.html
6http://sourceforge.net/projects/openocd/files/openocd/
7http://www.amontec.com
8http://www.ftdichip.com/index.html
9http://openocd.sourceforge.net/doc/html/Command-and-Driver-Index.html#Command-and-Driver-Index


File translated from TEX by TTH, version 4.03.
On 1 Oct 2012, 15:34.