Dans un premier billet, nous avons vu qu’il était très simple de reprogrammer une mémoire de type EEPROM sous linux. Malheureusement, nous avions rencontré des soucis d’accès au périphérique que nous devons maintenant contourner. Dans ce second billet, je vous propose donc d’étudier les outils des débogage I2C Tools et leur utilisation sur un système embarqué.
Permission denied
« Permission denied » est probablement l’erreur la plus courante sous Linux. C’est aussi la moins déconcertante: si make me a sandwich
ne fonctionne pas, sudo make me a sandwich
le devrait. En embarqué, la commande sudo
n’a pas de sens puisque nous sommes déjà root. Alors, si ce n’est pas un problème d’utilisateur, pourquoi le système ne nous laisse-t-il pas écrire dans l’EEPROM?
Use the source, Luke!
Allons jeter un œil au code source du pilote. Celui-ci dispose bien des routines pour lire et écrire dans l’EEPROM, mais il y a un élément plus intéressant : l’option AT24_FLAG_READONLY
. Le pilote permet en effet aux développeurs de supprimer l’accès en écriture. En définissant cette option, le développeur empêche les applications d’utilisateur de modifier le contenu de la mémoire. L’option peut être déclarée dans des platform_data
ou propriété « read-only » pour les systèmes supportant l’OpenFirmware (CONFIG_OF).
Pour faire court, sous Linux, nous pouvons classer les périphériques en deux types : les périphériques découvrables par le matériel (souris USB, carte PCI, …) et ceux qui doivent être « figés dans le code » au niveau du kernel (I2C, SPI, …). Notre mémoire EEPROM I2C tombe dans la deuxième catégorie.
I2C Tools
Si le pilote ne nous permet pas d’écrire, nous avons néanmoins une alternative : envoyer des commandes d’écriture directement sur le bus I2C. C’est la raison d’être de la chaîne d’outils I2C Tools, qui comprend quatre binaires:
i2cset
envoie des commandes d’écriture à un périphérique,i2cget
envoie des commandes de lecture,i2cprobe
détecte les périphériques présents sur le bus,- et
i2cdump
lit l’intégralité des registres d’un périphérique.
Problème : je n’ai pas accès au système de compilation de ce système et l’ingénieur matériel qui m’accompagne n’en a pas connaissance non plus. Réfléchissons : sur quel système travaillons-nous?
# cat /proc/cpuinfo
Processor : ARM926EJ-S rev 5 (v5l)
Un ARM9, formidable! Je travaille présentement sur un autre projet équipé de ce CPU. Je devrais pouvoir utiliser la version d’i2c-tools compilée pour ARM de mon projet sur cet équipement. La version de Linux n’est pas la même (2.6.30 contre 3.2.27), mais l’API i2c.h n’a probablement pas bougé entre les deux. Essayons :
# i2cget -y 2 0x54
Error: Could not set address to 0x54: Device or resource busy
Le binaire fonctionne, l’API n’a pas dû bouger.
Opération à coeur ouvert
Évidemment le pilote de l’EEPROM a le contrôle sur ce périphérique et Linux ne nous permet pas de le hacker. Les outils I2C peuvent forcer la porte avec l’option -f
, mais il y a un risque de contention du bus. Par exemple, si deux accès concurrents s’entrelacent, ils risquent de corrompre notre périphérique I2C. Nous allons plutôt utiliser la méthode élégante : déconnecter le lien entre le pilote et le périphérique. Cette méthode est disponible depuis linux-2.6.13 et est décrite sur LWN. L’idée est de supprimer le lien qui existe entre l’EEPROM et son pilote : at24. Même de cette façon, il y a un risque de corruption comme le décrit cet échange de la liste de discussion du kernel. Dans notre cas, au pire, une application accédant à l’EEPROM va planter. Jetons un œil à ce lien entre notre EEPROM et son pilote :
# cd /sys/devices/platform/i2c_davinci.2/i2c-2/2-0054/driver
# ls -l
lrwxrwxrwx 1 root root 0 Feb 1 03:43 0-0050 -> ../../../../devices/platform/i2c_davinci.0/i2c-0/0-0050
lrwxrwxrwx 1 root root 0 Feb 1 03:43 2-0054 -> ../../../../devices/platform/i2c_davinci.2/i2c-2/2-0054
--w------- 1 root root 4096 Feb 1 03:43 bind
--w------- 1 root root 4096 Feb 1 03:43 uevent
--w------- 1 root root 4096 Feb 1 03:43 unbind
Ici, on peut voir que le pilote est associé à deux mémoires EEPROM différentes : L’une à l’adresse 0x50 sur le bus i2c-0. L’autre à l’adresse 0x54 sur le bus i2c-2. C’est la deuxième qui nous intéresse. Essayons de retirer le pilote :
# echo 2-0054 > unbind
# i2cdump -y 2 0x54
No size specified (using byte-data access)
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: 4b 5a 20 20 20 20 20 20 20 20 20 20 20 20 20 20 KZ
10: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
20: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
30: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 4e 50 NP
40: 45 4e 30 31 20 20 20 20 20 20 20 20 20 20 20 20 EN01
Bingo! Essayons maintenant d’écrire un octet à l’adresse 0x44, par exemple :
# i2cset -y 2 0x54 0x44 0x5a
# i2cget -y 2 0x54 0x44
0x5a
Bingo! Quoi de plus simple? Désormais nous pouvons écrire un petit script shell ou programme C pour réécrire les octets qui nous intéressent spécifiquement et l’exécuter sur chaque équipement présentant le défaut initial. N’oublions pas de recoudre le patient, c’est à dire de reconnecter le pilote :
# echo 2-0054 > bind
[ 643.972940] at24 2-0054: 512 byte 24c04 EEPROM, writable, 1 bytes/write
Et vérifions que le patient est bien guéri en lisant l’octet modifié :
# hexdump -C 2-0054/eeprom -n 1 -s 0x44
00000044 5a |Z|
Mission réussie grâce à un petit « hack » rapide et efficace. Inutile de brancher un JTAG ou de recompiler quoi que ce soit. Reste à convaincre le chef de projet que la solution logicielle vaut le coup, ce qui risque d’être une autre histoire…