20 KiB
Travail Pratique 3
Objectif
Le but du travail est de manipuler le source d'un noyeau linux en le patchant et en le compilant avec notre chaine de compilation croisée pour le mettre sur notre carte
Preparation
[Q1] : Quelle est la version du noyau sur votre machine de développement ?
Sur ma machine perso je suis sur macos mais sur la machine virtuelle X86 que j'ai mise en place pour ces TP on peut faire la commande uname -ir
Et on a ce resultat
uname -ir
6.8.0-45-generic x86_64
J'ai donc une version stable du kernel linux 6.8.0
Obtenir les sources du noyau Linux
[Q2] : Quelle est la dernière version stable du noyau Linux selon le site officiel (indiquez la date de consultation) ?
La dernière version stable au moment ou je regarde sur www.kernel.org le 10 octobre 2024 est la 6.11.3
[Q3] : Quelle est la dernière version stable longterm du noyau Linux possédant la plus longue EOL (End Of Life) ? Quelle est sa EOL (indiquez la date de consultation) ?
Les versions long term support au moment ou je regarde sur www.kernel.org le 10 octobre 2024 sont:
- la 6.6.56 Support jusqu'à Décembre 2026
- la 6.1.112 Support jusqu'à Décembre 2026
- la 5.15.167 Support jusqu'à Décembre 2026
- la 5.10.226 Support jusqu'à Décembre 2026
Elles expirent donc toutes en décembre 2026... bizarre mais bon on va prendre la plus récente quand même qui dans notre cas es la 6.6.56
Pour accèder à toutes les versions de linux disponibles et pas juste celles qui sont mises en avance sur kernel.org on peut aller sur https://www.kernel.org/pub/linux/kernel/ et voir directement chaque version.
Dans notre cas on veut la mainline 6.5
wget https://mirrors.edge.kernel.org/pub/linux/kernel/v6.x/linux-6.5.tar.xz
[Q4] : Est-ce que la combinaison de cette version du noyau et votre chaîne de compilation croisée risque de poser problème ? Justifiez en développant votre réponse.
j'ai trouvé la version des headers de notre version de chaine de compilation et c'est la 4.19.315pour trouver cette info il suffit de retourner sur le site de bootlin ou on a telechargé cette dernière.
On peut donc voir que nos headers sont bien plus vieux que notre noyeau linux et c'est bien car les noyeaux sont rétrocompatibles. Le problème aurait été si notre chaine de compilation était plus récente que notre noyeau et la ca aurait été problematique.
[Q5] : Quel est le nombre total de fichiers sources (.c, .h, et .S) du noyau que vous venez de télécharger ? Déterminer cette valeur en utilisant seulement une seule exécution de la commande find (astuces : find accepte l’argument -o pour réaliser un “ou” logique et pensez à utiliser pipe sur la sortie de find avec le compteur de lignes wc -l).
find ./ \( -name "*.h" -o -name "*.c" -o -name "*.S" \) | wc -l
Avec cette commande on peut chercher plusieurs noms de fichiers différents. Dans le cas présent je me suis posé à la racine du kernel.
-name permet de spécifier que l'on veut filtrer sur le nom du fichier
-o permet d'indiquer un OR pour dire que on chercher plusieurs noms de fichiers possibles
Le résultat est : 56888 fichiers c'est un peu abusé mais bon ca comprend toutes les architetures possibles drivers etc...
[Q6] : Quels fichiers patch devez vous donc télécharger ?
Pour rappel, on a pas telechargé la version 6.6.56 mais la version mainline 6.5 pour permettre de s'amuser avec les patchs. Mais évidemment si on devait le faire pour de vrai en perdant le moins de temps possible on utiliserait juste git ou on prendrait tout simplement la dernière version directement sur kernel.org
Si on rergarde la nomenclature de notre version 6.6.56 et 6.1.112 on peut voir que la version téléchargée est 5 versions mineures derrière la version cible.
Pour patcher notre kernel on doit donc passer par deux étapes :
1: passer à la version Mainline (ici passer de 6.5 à 6.6) 2: passer de la version Mainline à la version longterm avec les différentes modifications (ici de passer de 6.6 à 6.6.112)
Quand on se balade sur https://www.kernel.org/pub/linux/kernel/ que en plsu des distributions on peut tomber sur des patchs. Le premier qui nous intéresse est le patch nommé patch-6.6.xz c'est le patch implicite qui permet de passer de la version 6.5 à la version 6.6 ce qui est exactement ce que l'on cherche.
Ensuite pour finir on peut prendre le patch patch-6.6.56.xz qui nous permettras de passer de la 6.6 à la 6.6.56. J'avoue que c'était pas hyper clair mais dans le repertoire incr on a des patchs de 6.6.50 vers 6.6.51 et ils sont très petits alors que la le patch 6.6.56 fait 3m ce qui est énorme donc je pense que c'est bien pour passer de mainline à 56
Maintenant on a bien installé notre kernel version 6.5 et no patchs 6.6 et 6.6.56
Pour les appliquer on peut utiliser les commandes
xz -d patch-6.6.xz pour décompresser les patchs deja
patch -p1 --dry-run < ../patch-6.6
Le -p1 indique qu'il faut skip le premier nom de directory pour taper directement dans le bon directory. Dans notre cas 1 suffit car on est déja à la racine de notre kernel
--dry-run permet de ne pas faire de modifs si il y a une erreur. La dans notre cas tout se fait sans erreur donc on peut refaire le patch sans le --dry-run
Ca prend quelques secondes et on a plein de texte qui fuse mais normalement aucune erreur et après cette commande on a donc un linux kernel mainline 6.6
Il faut donc désormais appliquer le patch 6.6.56
patch -p1 --dry-run < ../patch-6.6.56
et ensuite sans le --dry-run
et voila, normalement on devrait avoir un linux 6.6.56 prêt à être compilé.
On peut verifier avec la commande make kernelversion
make kernelversion
6.6.56
Préparation à la compilation croisée du noyau
On n'oublie pas de mettre notre variable d'environnement ARCH sur arm
echo $ARCH
arm
C'est important ensuite pour la compilation croisée
Configuration du noyau
J'ai voulu faire un make help mais j'ai eu des erreurs bizarre alors j'ai fait un make mrproper et ensuite le make help a fonctionné
Dans une des lignes de make help on peut lire
sama5_defconfig - Build for sama5
[Q7] : Quelle configuration avez-vous choisie ?
Dans une des lignes de make help on peut lire
sama5_defconfig - Build for sama5
Il faudrait donc utiliser sama5_defconfig
make sama5_defconfig
[Q8] : Comment pouvez-vous vous assurez avec une assez bonne certitude qu’il s’agit de la bonne configuration ?
make menuconfig
On peut y voir des mentions de ARM, de AT91 que la target est 32 bits que l'on a un MMU
Bon après je vois nulle part mention du CPU exact que l'on a mais c'est déja pas mal
Compilation croisée du noyau
make
Bon chez moi ca prend un certain temps car la machine virtuelle est pas au top
Après genre 5min de compilation pouf erreur "certs/extract-cert.c:21:10: fatal error: openssl/bio.h: No such file or directory 21 | #include <openssl/bio.h>" par ce que sinon c'est pas drole
On va donc installer les bons outils et essayer à nouveau.
sudo apt install libssl-dev
La compilation est vraiment lente sur ma machine il faudrait la rendre multithreadée et peut-être la faire sur une machine x86 native la prochaine fois.
J'ai retenté une seconde fois avec -j ce qui permet de profiter des différents coeurs de la machine et à la fin de compilation on recoit
make -j
CALL scripts/checksyscalls.sh
Kernel: arch/arm/boot/Image is ready
Kernel: arch/arm/boot/zImage is ready
Ce qui devrait nous donner des indices sur ou se trouvent les inmages compilées
[Q9] : Une fois la compilation du noyau terminée, où se trouve le noyau compilé et quelle est sa taille ?
On peut aller regarder sous arch/arm/boot/
à l'intérieur on peur voir
Image Makefile bootp compressed deflate_xip_data.sh dts install.sh zImage
On a donc une image et une zImage
du -sh Image
12M Image
et pour la zimage
du -sh zImage
5.5M zImage
[Q10] : Aussi, quel est le fichier de Device Tree Blob (binaire) pour votre carte Sama5D3 Xplained et quelle est sa taille ?
Le fichier device tree blob se trouve lui un peu plus bas dans arch/arm/boot/dts/microchip la on a une liste assez enorme de board mais parmis ces dernières on peut trouver une qui se nomme
ls | grep sama5d3_xplained
at91-sama5d3_xplained.dtb
at91-sama5d3_xplained.dts
Et dans ces noms on veut le blob binaire et donc on peut prendre le fichier qui finit en .dtb
du -sh at91-sama5d3_xplained.dtb
40K at91-sama5d3_xplained.dtb
Elle fait donc 40kb
Le fichier DTS est le fichier lisible par un humain avec tous les peripheriques et memoires de notre carte en particulier.
[Q11] : Quelle est la nouvelle taille du noyau et quel pourcentage de taille en espace disque avez-vous ainsi gagné par rapport au noyau précédent ?
Il va ici falloir retourner dans notre configuration
make menuconfig
et ensuite à partir de la on peut recompiler notre kernel
make -j
Et on retourne sous arch/arm/boot/ pour voir notre noyeau nouvellement compilé.
du -sh zImage
3.9M zImage
On gagne un peu de place par rapport à Gzip mais comparé aux 12mb originaux on gagne pas mal de place
Chargement et boot du noyau avec U-Boot
Comme ma
On va charger le noyeau à l'addresse 0x21000000
tftp 0x21000000 192.168.1.240:zImage
=> tftp 0x21000000 192.168.1.240:zImage
ethernet@f0028000: PHY present at 7
ethernet@f0028000: Starting autonegotiation...
ethernet@f0028000: Autonegotiation complete
ethernet@f0028000: link up, 1000Mbps full-duplex (lpa: 0x7800)
Using ethernet@f0028000 device
TFTP from server 192.168.1.240; our IP address is 192.168.1.201
Filename 'zImage'.
Load address: 0x21000000
Loading: #################################################################
#################################################################
#################################################################
#################################################################
##################
720.7 KiB/s
done
Bytes transferred = 4076944 (3e3590 hex)
letsgooo (au cas ou note pour plus tard, le dossier partagé par le serveur macos tftp se trouve à /private/tftp)
ensuite pour le device tree blob à l'adresse 0x22000000
tftp 0x22000000 192.168.1.240:device_tree.dtb
=> tftp 0x22000000 192.168.1.240:device_tree.dtb
ethernet@f0028000: PHY present at 7
ethernet@f0028000: Starting autonegotiation...
ethernet@f0028000: Autonegotiation complete
ethernet@f0028000: link up, 1000Mbps full-duplex (lpa: 0x7800)
Using ethernet@f0028000 device
TFTP from server 192.168.1.240; our IP address is 192.168.1.201
Filename 'device_tree.dtb'.
Load address: 0x22000000
Loading: ###
7.8 KiB/s
done
Bytes transferred = 40674 (9ee2 hex)
On se rend pas compte mais ca m'a pris tellement de temps de faire fonctionner le tftp entre ma VM -> mon laptop -> sama5d3 que la de voir que ca fonctionne ca me rend heureux
[Q12] : Indiquez la séquence de commandes vous ayant permis de charger noyau et DTB et booter dessus.
Je suis allé chercher sur google et je suis tombé sur ce site https://docs.u-boot.org/en/stable/usage/cmd/bootz.html qui est la doc de la commande bootz
on peut donc créér la commande suivante
bootz 0x21000000 - 0x22000000
0x21... c'est l'addresse du kernel
"-" c'est par ce que on a pas d'infos sur la taille de la ram que l'on veut utiliser etc...
0x22... c'est l'addresse de notre DTB
Mais c'est bizarre... Quand j'utilise la commande bootz il ne se passe absolument rien... Pas d'erreur rien. Comme si j'avais entré une string vide que dalle.
Je regarde dans la doc et je peux voir que pour faire fonctionner bootz il faut que dans la config ca soit autorisé. Mais quand je verifier mon .config de u-boot
grep CONFIG_CMD_BOOTZ .config
CONFIG_CMD_BOOTZ=y
Tout a l'air ok donc je comprends pas trop
en fait j'ai redémarré ma board entre le moment ou j'ai tftp mes fichiers et du coup l'info a été perdue. Et la commande bootz a été codée visiblement pour ne rien dire...
Bref maintenant voici le résultat
=> bootz 0x21000000 - 0x22000000
## Flattened Device Tree blob at 22000000
Booting using the fdt blob at 0x22000000
Loading Device Tree to 2fb29000, end 2fb35ee1 ... OK
Starting kernel ...
[Q13] : Après avoir lu les messages d'erreur du noyau, expliquez aussi simplement que possible, quelle est la raison de la “panique” (ou crash) du noyau ?
---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,253) ]---
Voici l'erreur que on a quand on essaie de booter sur le kernel.
Le kernel n'est pas content car il n'arrive pas à charger le rootfs
[Q14] : Donnez le “code” U-Boot que vous avez écrit pour réaliser ce boot automatique.
setenv bootmenu_2 'Boot from local =echo "booting from local"'
voici le code d'exemple pour ajouter une troisème entrée de bootmenu qui fait un simple echo..
setenv bootmenu_3 'Boot from tftp=tftp 0x21000000 192.168.100.240:zImage && tftp 0x22000000 192.168.100.240:device_tree.dtb && bootz 0x21000000 - 0x22000000'
Grâce à cette commande j'ai ajouté un nouveau menu qui quand on le selectionne va directement récupèrer tous les fichiers depuis notre machine et va ensuite lancer le kernel
## Flasher le noyau et le Device Tree dans la NAND
[Q15] : Quelle sont les tailles, en bytes et KBytes, réservées dans la flash NAND au noyau et au Device Tree ?
On peut voir dans le resultat de la commande TFTP que le kernel fait 4076944 bytes ou à peu près 4mb.
Cela veut dire qu'il est stocké entre les addresses 0x21000000 et 0x213e3590
Pour ce qui est du DTB, 40674 bytes ou 40kb sont utilisés et il est donc stocké entre les addresses 0x22000000 et 0x22009ee2
[Q16] : Comment pouvez-vous vous assurer que la NAND a bien été effacée ?
Pour effacer de la nand on peut utiliser la commande nand erase [start] [size]
Dans notre cas si on suit le diagramme on peut voir que on va écrire le kernel entre les addresses 0x160000 et 0x700000 et le DTB entre les addresses 0x700000 et 0x720000
Donc on va nand erase 0x160000 0x720000
=> nand erase 0x160000 0x720000
NAND erase: device 0 offset 0x160000, size 0x720000
Erasing at 0x860000 -- 100% complete.
OK
pour verifier que la NAND a bien été clean à ces addresses on peut faire un nand dump 0x160000 0x100 pour avoir un apercu de l'état de la NAND à cette addresse
=> nand dump 0x160000 0x100
Page 00160000 dump:
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff...
On peut voir que cette zone de la NAND a bien été remplie avec des 0xFF
[Q17] : Donnez le contenu des scripts update_kernel et update_dtb.
Pour flasher le contenu de ce qui se trouve dans notre flash dans notre nand on peut utiliser nand write x y z
x indique l'addresse dans la flash ou commencent les infos que on veut flasher y indique l'addresse dans la NAND ou on doit flasher le contenu de notre flash z indique la taille de l'information que on va écrire
Pour notre kernel on a donc
nand write 0x21000000 0x160000 0x3e3590
=> nand write 0x21000000 0x160000 0x3e3590
NAND write: device 0 offset 0x160000, size 0x3e3590
4076944 bytes written: OK
=>
Pour le DTB
nand write 0x22000000 0x700000 0x9ee2
=> nand write 0x22000000 0x700000 0x9ee2
NAND write: device 0 offset 0x700000, size 0x9ee2
40674 bytes written: OK
Pour créér des scripts on peut utiliser cette commande
setenv monscript 'command;command...'
Le script update_kernel doit faire deux choses que l'on sait deja faire :
- Load le kernel depuis notre machine de développement dans la RAM en utilisant tftp
- Erase la NAND aux bonnes addresses et flasher notre nouveau kernel et DTB dedans
setenv update_kernel 'tftp 0x21000000 192.168.100.240:zImage && nand erase 0x160000 0x700000 && nand write 0x21000000 0x160000 0x3e3590'
setenv update_dtb 'tftp 0x22000000 192.168.100.240:device_tree.dtb && nand erase 0x700000 0x720000 && nand write 0x22000000 0x700000 0x9ee2'
Ci dessus est le script qui devrait faire tout ce qui a été mentionné
on peut les run en faisant
run update_kernel
run update_dtb
Booter le noyau depuis la NAND
[Q18] : Donner les commandes U-Boot que vous avez écrites par accomplir ceci.
Pour booter depuis la NAND il faut charger le contenu de la NAND dans la memoire flash.
Donc pour commencer on peut faire un reset de la carte pour être sûr que la flash est bien vide.
Pour verifier :
=> bootz 0x21000000 - 0x22000000
=>
Ensuite la syntaxe pour lire dans la NAND est la même que pour l'écrire
nand read x y z
x est l'addresse en ram dans laquelle on va mettre ce que on lit
y est l'addresse en NAND ou se trouve notre data
z est la taille du kernel ou DTB à lire
nand read 0x21000000 0x160000 0x3e3590
nand read 0x22000000 0x700000 0x9ee2
et ensuite on peut faire bootz 0x21000000 - 0x22000000
on peut mettre tout ca dans un petit script
setenv boot_kernel 'nand read 0x21000000 0x160000 0x3e3590 && nand read 0x22000000 0x700000 0x9ee2 && bootz 0x21000000 - 0x22000000'
Et quand on le lance :
=> run boot_kernel
NAND read: device 0 offset 0x160000, size 0x3e3590
4076944 bytes read: OK
NAND read: device 0 offset 0x700000, size 0x9ee2
40674 bytes read: OK
## Flattened Device Tree blob at 22000000
Booting using the fdt blob at 0x22000000
Loading Device Tree to 2fb29000, end 2fb35ee1 ... OK
Starting kernel ...
Maintenant on créée un bootmenu
setenv bootmenu_4 'Boot from NAND=run boot_kernel'
Pour faire en sorte que ce soit cette dernière qui soit appelée après le timeout (ici 4 car c'est le quatrième bootmenu)
setenv bootmenu_default 4
Et on peut configurer le timeout de la sorte (ici cinq secondes)
setenv bootdelay 5
et pour rappel, toujours utiliser saveenv avant de reboot
Et maintenant on peut simplement reboot la carte et voir que on va boot automatiquement après cinq secondes sur le kernel stocké en NAND
[Q19] : Donnez le contenu de vos scripts permettant de ne flasher que l’espace nécessaire dans la NAND de votre carte.
Dans les variables d'environnement on peut voir une variable nommée filesize
On peut donc ajouter une petite routine dans nos scripts pour faire en sorte que la valeur de taille du fichier ne soit pas hard codée
Voici le script update_kernel final
setenv update_kernel 'tftp 0x21000000 192.168.100.240:zImage && nand erase 0x160000 ${kernel_size} && setenv kernel_size ${filesize} && nand write 0x21000000 0x160000 0x3e3590'
(avant de run ce script pour la première fois il faut faire un premier tftp du kernel puis setenv kernel_size ${filesize} car le script a besoin que cette variable soit set au moins une fois)
voici le script update_dtb
setenv update_dtb 'tftp 0x22000000 192.168.100.240:device_tree.dtb && nand erase 0x700000 ${dtb_size} && setenv dtb_size ${filesize} && nand write 0x22000000 0x700000 0x9ee2'
(avant de run ce script pour la première fois il faut faire un premier tftp du dtb puis setenv dtb_size ${filesize} car le script a besoin que cette variables soit set au moins une fois)
Pour faire simple dans ces deux scripts on commence par tftp le nouveau kernel, si et seulement si cela marche alors on supprime le contenu de la NAND de la taille de l'ancien kernel (important de sauvegarder la valeur de l'ancien kernel car le tftp va overwrite l'ancienne valeur de filesize) ensuite on sauve la taille de ce nouveau kernel et on va le flasher dans la NAND.