Nous avons réussi dans l’article précédent à compiler les bootloaders X-loader et U-boot, ainsi qu’un noyau Linux pour la carte Pandaboard. Le démarrage du système se déroulait très bien, le chargeur X-loader s’initialisant d’abord, puis passant le contrôle à U-boot. Ce dernier plaçait l’image du noyau Linux en mémoire et lui transmettait l’exécution.
Nous avons observé que le noyau commençait par s’auto-décompresser en mémoire, puis détectait les éléments matériels du système, initialisait tous ses composants logiciels, montait la seconde partition de la carte flash à la racine du système de fichiers, et cherchait – en vain – à démarrer le premier processus du système, le processus init.
Nous devons donc lui fournir ce fameux programme init. Il en existe plusieurs versions :
- l’init dit « System V » adopte un comportement classique sous Unix, en examinant le contenu du fichier
/etc/inittab, puis en démarrant des processus en fonction du contenu de ce fichier ;
- l’init du projet Upstart plus récent permet de paralléliser plus de traitements, et d’offrir ainsi une interface visuelle à l’utilisateur alors même que tous les services ne sont pas encore actifs (ce fonctionnement est aussi proposé par le projet Initng mais de manière moins complète) ;
- le projet systemd, jeune et actif, a pour but de regrouper les opérations d’init et de la plupart des démons usuels.
Pour les systèmes embarqués, on fait souvent appel à une version simplifiée de l’init System V, proposée par le projet Busybox. Nous allons l’utiliser pour mettre en place les outils minimum sur un système embarqué.
Système de fichiers
Tout d’abord il nous faut préparer le système de fichiers. Même dans un environnement minimal, Linux nécessite une arborescence de fichiers. Ceux-ci peuvent se trouver sur un disque dur (dans le cas d’un poste de travail, d’un serveur…), sur une carte flash (smartphone, systèmes embarqués…) ou même sur un ramdisk, disque virtuel en mémoire RAM, initialisé au moment du boot à partir d’une image se trouvant généralement dans une mémoire flash.
Dans notre configuration, le système de fichiers résidera sur la seconde partition de la carte SD sur laquelle notre Pandaboard démarre. Nous avions précédemment initialisé cette seconde partition au format Ext3, classique sous Linux. Nous lui avions donné le nom root (pour root filesystem). En insérant la carte flash sur notre ordinateur hôte de développement, la partition se trouve automatiquement montée dans /media/root.
Commençons à créer l’arborescence usuelle.
[~]# cd /media/root/
[root]# ls
lost+found
[root]#
Le répertoire lost+found est automatiquement créé lorsqu’on formatte une partition en Ext3.
[root]# mkdir bin dev etc home lib mnt proc root sbin sys tmp usr var
[root]# mkdir usr/bin usr/sbin etc/init.d
[root]#
Nous rajouterons d’autres répertoires ultérieurement, ceux-ci nous permettront déjà d’avoir un accès minimal sur notre système. Nous pourrions à ce point créer les fichiers spéciaux de /dev représentant les périphériques du système. Sur un système embarqué avec de fortes contraintes d’occupation mémoire, c’est ce que je préconiserais. Ici je préfère tirer profit de la création automatique des fichiers spéciaux grâce à l’utilitaire mdev contenu dans Busybox.
Busybox
Busybox est un outil bien connu des développeurs pour Linux embarqué. Il s’agit d’un projet qui regroupe efficacement en un seul fichier exécutable plus de 300 commandes Unix classiques. Sur cet exécutable (nommé busybox), on crée des liens symboliques ou physiques portant les noms des commandes implémentées (par exemple ls, sh, cp, etc.) Lorsque l’exécutable démarre, il regarde sous quel nom il a été invoqué – en consultant l’argument argv[0] de sa fonction main() – et adopte le comportement correspondant. Outre la réduction de taille liée à la mise en commun du code utilisé par toutes les applets de busybox, il est possible de gagner beaucoup de place car les implémentations des commandes standards sont faites avec le souci de réduction de taille. De plus lors de la compilation de Busybox, on peut choisir les applets et les options que l’on juge utiles pour notre projet.
Busybox implémente une version simple de processus init, capable de lire un fichier /etc/inittab et d’en tirer parti. En l’absence d’un fichier /etc/inittab le processus init de Busybox cherche à exécuter automatiquement un script nommé /etc/init.d/rcS.
Compilons Busybox (dans le fichier de configuration proposé ci-dessous, j’ai activé la plupart des applets disponibles).
[~]$ cd ~/Projets/Panda/
[Panda]$ wget http://www.busybox.net/downloads/busybox-1.18.4.tar.bz2
[...]
2011-05-08 18:09:13 (122 KB/s) - «busybox-1.18.4.tar.bz2» sauvegardé [2130598/2130598]
[Panda]$ tar xjf busybox-1.18.4.tar.bz2
[Panda]$ cd busybox-1.18.4
[busybox-1.18.4]$
[busybox-1.18.4]$ cp ~/config-busybox-1.18.4 ./.config
[busybox-1.18.4]$ make menuconfig
[busybox-1.18.4]$ make CROSS_COMPILE=/cross-arm-linux/usr/bin/arm-linux-
[...]
[busybox-1.18.4]$ ls -l busybox
-rwxrwxr-x 1 cpb cpb 892444 8 mai 19:26 busybox
[busybox-1.18.4]$
Note : la variable CROSS_COMPILE contient le préfixe à ajouter devant les outils standards (gcc, as, ar, etc.) pour accéder aux outils de la toolchain correspondant au processeur Arm. Si vous ne disposez pas de chaîne de compilation croisée, vous pouvez télécharger cette archive à décompresser directement à la racine de votre système de fichiers. Un article à venir dans deux semaines vous proposera plusieurs méthodes pour générer vous-mêmes votre propre toolchain.
Voici une liste des commandes intégrées dans l’exécutable busybox que nous avons compilé.
[, [[, acpid, add-shell, addgroup, adduser, adjtimex, arp, arping, ash, awk, base64, basename, bbconfig, beep, blkid, blockdev, bootchartd, brctl, bunzip2, bzcat, bzip2, cal, cat, catv, chat, chattr, chgrp, chmod, chown, chpasswd, chpst, chroot, chrt, chvt, cksum, clear, cmp, comm, cp, cpio, crond, crontab, cryptpw, cttyhack, cut, date, dc, dd, deallocvt, delgroup, deluser, depmod, devmem, df, dhcprelay, diff, dirname, dmesg, dnsd, dnsdomainname, dos2unix, du, dumpkmap, dumpleases, echo, ed, egrep, eject, env, envdir, envuidgid, ether-wake, expand, expr, fakeidentd, false, fbset, fbsplash, fdflush, fdformat, fdisk, fgconsole, fgrep, find, findfs, flock, fold, free, freeramdisk, fsck, fsck.minix, fsync, ftpd, ftpget, ftpput, fuser, getopt, getty, grep, gunzip, gzip, halt, hd, hdparm, head, hexdump, hostid, hostname, httpd, hush, hwclock, id, ifconfig, ifdown, ifenslave, ifplugd, ifup, inetd, init, insmod, install, ionice, iostat, ip, ipaddr, ipcalc, ipcrm, ipcs, iplink, iproute, iprule, iptunnel, kbd_mode, kill, killall, killall5, klogd, length, less, linux32, linux64, linuxrc, ln, loadfont, loadkmap, logger, login, logname, logread, losetup, ls, lsattr, lsmod, lspci, lsusb, lzcat, lzma, lzop, lzopcat, makedevs, man, md5sum, mdev, mesg, microcom, mkdir, mkdosfs, mke2fs, mkfifo, mkfs.ext2, mkfs.minix, mkfs.vfat, mknod, mkpasswd, mkswap, mktemp, modinfo, modprobe, more, mount, mountpoint, mpstat, mt, mv, nameif, nbd-client, nc, netstat, nice, nmeter, nohup, nslookup, ntpd, od, openvt, passwd, patch, pgrep, pidof, ping, ping6, pipe_progress, pivot_root, pkill, pmap, poweroff, powertop, printenv, printf, ps, pscan, pwd, raidautorun, rdate, rdev, readahead, readlink, readprofile, realpath, reboot, remove-shell, renice, reset, resize, rev, rm, rmdir, rmmod, route, rpm, rpm2cpio, rtcwake, run-parts, runlevel, runsv, runsvdir, rx, script, scriptreplay, sed, seq, setarch, setconsole, setfont, setkeycodes, setlogcons, setsid, setuidgid, sh, sha1sum, sha256sum, sha512sum, showkey, slattach, sleep, smemcap, softlimit, sort, split, start-stop-daemon, stat, strings, stty, su, sulogin, sum, sv, svlogd, swapoff, swapon, switch_root, sync, sysctl, syslogd, tac, tail, tar, tcpsvd, tee, telnet, telnetd, test, tftp, tftpd, time, timeout, top, touch, tr, traceroute, traceroute6, true, tty, ttysize, tunctl, udhcpc, udhcpd, udpsvd, umount, uname, unexpand, uniq, unix2dos, unlzma, unlzop, unxz, unzip, uptime, usleep, uudecode, uuencode, vconfig, vi, vlock, volname, wall, watch, watchdog, wc, wget, which, whoami, xargs, xz, xzcat, yes, zcat, zcip
Nous pouvons observer que la couverture des applications est très large. Certaines nous serons indispensables dès le démarrage.
init : lors du boot, le kernel cherche à démarrer un processus /bin/init (il recherche également dans /sbin et /etc). Il nous faut donc créer un lien symbolique sur busybox qui porte ce nom. Ce processus init va consulter le fichier /etc/inittab et agir en conséquence. En l'absence de ce fichier, il commencera par lancer le script /etc/init.d/rcS.
sh : le script /etc/init.d/rcS est un script shell. Il commence par la ligne shebang "! /bin/sh". Il faut donc qu'un lien de ce nom existe et pointe vers busybox également.
mount : le script de démarrage doit commencer par monter le pseudo-système de fichier proc. Pour cela, la commande mount doit être disponible comme lien vers busybox.
Ensuite, l'exécutable busybox va pouvoir créer les liens sur lui-même avec tous les noms mentionnés plus haut. Installons les quelques fichiers indispensables.
[busybox-1.18.4]$ su
Password:
[busybox-1.18.4]# cp busybox /media/root/bin/
[busybox-1.18.4]# cd /media/root/bin/
[bin]# ln busybox init
[bin]# ln busybox sh
[bin]# ln busybox mount
[bin]#
Puis copions le fichier rcS (à télécharger ici) sans oublier de le rendre exécutable ! (le contenu du script est décrit plus bas)
[bin]# cd /media/root/etc/init.d
[init.d]# cp ~/rcS .
[init.d]# chmod +x rcS
[init.d]#
Il nous faut également installer les bibliothèques dynamiques nécessaires pour le fonctionnement de Busybox. Nous allons procéder de manière un peu excessive en copiant toutes les bibliothèques fournies par la chaîne de compilation. Dans le cas d'un système plus restreint, nous sélectionnerions précisément les bibliothèques indispensables, avec l'aide de la commande arm-linux-ldd se trouvant dans notre toolchain.
[lib]# cp -Rdp /cross-arm-linux/usr/arm-linux/sysroot/lib/* .
[lib]#
Le fichier rcS contient les lignes suivantes.
#! /bin/sh
mount none /proc -t proc
mount none /sys -t sysfs
mount / -o rw,remount
busybox --install
mdev -s
echo /sbin/mdev > /proc/sys/kernel/hotplug
La première ligne indique quel est l'interpréteur à utiliser pour traiter ce script. Le fichier /bin/sh existe, nous l'avons créé sous forme de lien. Ensuite on monte les deux pseudo-systèmes de fichier /proc et /sys, puis on remonte le système de fichier original en lecture et écriture (pendant le boot, le noyau le monte en lecture seule).
Nous pouvons alors demander à Busybox de créer les liens sur son propre fichier exécutable avec les noms de tous les utilitaires qu'il sait représenter.
L'utilitaire mdev que nous démarrons ensuite a pour rôle de créer les fichiers spéciaux de /dev représentant tous les périphériques détectés par le noyau. C'est une version simplifiée du programme udevd (User Device Dameon) que l'on retrouve sur certaines distributions. Nous l'enregistrons également comme utilitaire à invoquer lors d'un hotplug, la détection d'un branchement à chaud de périphérique (par exemple une clé USB). Le script de démarrage proposé ici est vraiment minimal, nous l'étendrons dans le prochain article.
Nous pouvons démonter notre mémoire flash et l'insérer sur la Pandaboard.
[init.d]# cd /media
[media]# umount boot root
[media]#
Démarrons minicom sur notre système hôte, relié au port série de la carte Pandaboard. Et mettons cette dernière sous tension. Nous voyons sur l'écran de minicom les lignes suivantes.
Texas Instruments X-Loader 1.5.0 (May 3 2011 - 07:49:31)
[...]
U-Boot 2011.03-00398-ga621b16-dirty (May 03 2011 - 08:42:19)
[...]
## Booting kernel from Legacy Image at 82000000 ...
Image Name: Linux-2.6.39-rc5-cpb
[...]
Starting kernel ...
Uncompressing Linux... done, booting the kernel.
[ 0.000000] Initializing cgroup subsys cpu
[ 0.000000] Linux version 2.6.39-rc5-cpb (cpb@tr-a-0) (gcc version 4.3.5 (Buildroot 2010.08) ) #1 SMP Tue May 3 11:14:47 1
[ 0.000000] CPU: ARMv7 Processor [411fc092] revision 2 (ARMv7), cr=10c5387f
[...]
[ 3.794860] smsc95xx v1.0.4
[ 3.922637] smsc95xx 1-1.1:1.0: eth0: register 'smsc95xx' at usb-ehci-omap.0-1.1, smsc95xx USB 2.0 Ethernet, b6:f0:b5:2e:9
Please press Enter to activate this console.
Pressons la touche "Entrée", afin d'obtenir le prompt du shell de Busybox
/ # ls
bin home lost+found root tmp
dev lib mnt sbin usr
etc linuxrc proc sys var
/ # uname -a
Linux (none) 2.6.39-rc5-cpb #1 SMP Tue May 3 11:14:47 CEST 2011 armv7l GNU/Linux
/ # cat /proc/cpuinfo
Processor : ARMv7 Processor rev 2 (v7l)
processor : 0
BogoMIPS : 2007.19
processor : 1
BogoMIPS : 1965.18
Features : swp half thumb fastmult vfp edsp thumbee neon vfpv3
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x1
CPU part : 0xc09
CPU revision : 2
Hardware : OMAP4 Panda board
Revision : 0020
Serial : 0000000000000000
/ #
Nous voyons bien que nous sommes connectés sur une carte Pandaboard ; les deux coeurs du processeur OMAP 4 sont visibles. Observons les liens que Busybox a créé sur lui-même.
/ # ls /bin/
[ dumpkmap kbd_mode pmap tail
[[ dumpleases kill powertop tar
add-shell echo killall printenv tcpsvd
addgroup ed killall5 printf tee
adduser egrep length ps telnet
arping eject less pscan test
ash env linux32 pwd tftp
awk envdir linux64 readahead tftpd
base64 envuidgid ln readlink time
basename ether-wake logger realpath timeout
bbconfig expand login remove-shell top
beep expr logname renice touch
bunzip2 false ls reset tr
busybox fdflush lsattr resize traceroute
bzcat fdformat lspci rev traceroute6
bzip2 fgconsole lsusb rm true
cal fgrep lzcat rmdir tty
cat find lzma rpm ttysize
catv flock lzop rpm2cpio udpsvd
chat fold lzopcat rtcwake umount
chattr free md5sum run-parts uname
chgrp fsync mesg runsv unexpand
chmod ftpget microcom runsvdir uniq
chown ftpput mkdir rx unix2dos
chpst fuser mkfifo script unlzma
chrt getopt mknod scriptreplay unlzop
chvt grep mkpasswd sed unxz
cksum gunzip mktemp seq unzip
clear gzip more setarch uptime
cmp hd mount setkeycodes usleep
comm head mountpoint setsid uudecode
cp hexdump mpstat setuidgid uuencode
cpio hostid mt sh vi
crontab hostname mv sha1sum vlock
cryptpw hush nc sha256sum volname
cttyhack id netstat sha512sum wall
cut ifplugd nice showkey watch
date init nmeter sleep wc
dc install nohup smemcap wget
dd ionice nslookup softlimit which
deallocvt iostat od sort whoami
delgroup ip openvt split xargs
deluser ipaddr passwd stat xz
df ipcalc patch strings xzcat
diff ipcrm pgrep stty yes
dirname ipcs pidof su zcat
dmesg iplink ping sum
dnsdomainname iproute ping6 sv
dos2unix iprule pipe_progress sync
du iptunnel pkill tac
/ # ls /sbin/
acpid fsck.minix man route
adjtimex ftpd mdev runlevel
arp getty mkdosfs setconsole
blkid halt mke2fs setfont
blockdev hdparm mkfs.ext2 setlogcons
bootchartd httpd mkfs.minix slattach
brctl hwclock mkfs.vfat start-stop-daemon
chpasswd ifconfig mkswap sulogin
chroot ifdown modinfo svlogd
crond ifenslave modprobe swapoff
depmod ifup nameif swapon
devmem inetd nbd-client switch_root
dhcprelay init ntpd sysctl
dnsd insmod pivot_root syslogd
fakeidentd klogd poweroff telnetd
fbset loadfont raidautorun tunctl
fbsplash loadkmap rdate udhcpc
fdisk logread rdev udhcpd
findfs losetup readprofile vconfig
freeramdisk lsmod reboot watchdog
fsck makedevs rmmod zcip
/ #
Nous pouvons également voir les fichiers spéciaux qui ont été créé par mdev.
/ # ls /dev/
console ptyvf ttydd
cpu_dma_latency ptyw0 ttyde
full ptyw1 ttydf
i2c-1 ptyw2 ttye0
i2c-2 ptyw3 ttye1
i2c-3 ptyw4 ttye2
i2c-4 ptyw5 ttye3
kmem ptyw6 ttye4
kmsg ptyw7 ttye5
loop0 ptyw8 ttye6
loop1 ptyw9 ttye7
loop2 ptywa ttye8
loop3 ptywb ttye9
loop4 ptywc ttyea
loop5 ptywd ttyeb
loop6 ptywe ttyec
loop7 ptywf ttyed
mem ptyx0 ttyee
mice ptyx1 ttyef
mmcblk0 ptyx2 ttyp0
mmcblk0p1 ptyx3 ttyp1
mmcblk0p2 ptyx4 ttyp2
network_latency ptyx5 ttyp3
network_throughput ptyx6 ttyp4
null ptyx7 ttyp5
psaux ptyx8 ttyp6
ptmx ptyx9 ttyp7
ptya0 ptyxa ttyp8
ptya1 ptyxb ttyp9
ptya2 ptyxc ttypa
[...]
ptyu7 ttyc5 ttyzf
ptyu8 ttyc6 ubi_ctrl
ptyu9 ttyc7 urandom
ptyua ttyc8 usbdev1.1
ptyub ttyc9 usbdev1.2
ptyuc ttyca usbdev1.3
ptyud ttycb usbdev2.1
ptyue ttycc usbmon0
ptyuf ttycd usbmon1
ptyv0 ttyce usbmon2
ptyv1 ttycf vcs
ptyv2 ttyd0 vcs1
ptyv3 ttyd1 vcs2
ptyv4 ttyd2 vcs3
ptyv5 ttyd3 vcs4
ptyv6 ttyd4 vcsa
ptyv7 ttyd5 vcsa1
ptyv8 ttyd6 vcsa2
ptyv9 ttyd7 vcsa3
ptyva ttyd8 vcsa4
ptyvb ttyd9 watchdog
ptyvc ttyda zero
ptyvd ttydb
ptyve ttydc
/ # /sbin/halt
swapoff: /etc/fstab: No such file or directory
The system is going down NOW!
Sent SIGTERM to all processes
Sent SIGKILL to[ 973.315673] System halted.
Notez bien que Busybox a créé les liens et les fichiers spéciaux ci-dessus directement sur la carte flash. Si nous la ré-insérons dans l'hôte de développement, nous observerons que tous ces fichiers existent à présent dans les répertoires mentionnés.
Conclusion
Nous avons installé une base minimale pour notre système Linux, et obtenu un accès par un shell sur le port série. Dans le prochain article, nous permettrons une connexion réseau sur notre carte, en utilisant les protocoles telnet et ssh, avant d'offrir une interface via un navigateur web par protocole http.
