Linux Embedded

Le blog des technologies libres et embarquées

Linux MMUless

Avant-propos

uCLinux on Palm Pilot IIICela fait bien des années que Linux fonctionne sur des architectures sans MMU. Les plus anciens se souviendront de cette image d'un Pingouin sur un Palm Pilot III. Cependant, 1998 est déjà loin, et depuis qu'en est-il ?

Palm Inc. a disparu, remplacé par des smartphones où il faut taper sur des touches pour écrire. Le projet uCLinux a également fermé ses portes en 2016.

Pourtant, dans les sources du noyau Linux, le support d'architecture sans MMU semble être toujours présent, entre autres pour le processeur ARM Cortex-M.

stm32Les microcontrôleurs ARM Cortex-M sont des produits récents et performants pour une consommation réduite.

Ils sont principalement dédiés à un usage embarqué sur batterie ou dans un environnement contraint voire temps réel.

Les projets nécessitant Linux sur MCU ne sont pas nombreux. Souvent ils ne sont que des démonstrations de contributeurs sans application pratique. Le seul projet qui ait abouti à un produit est (à ma connaissance, je vous laisse commenter sur le sujet bien volontiers) Accordo 2 de STMicroelectronics qui propose une solution pour l'automobile à base de ARM Cortex-R4 et de noyau Linux. Mais ce produit a été abandonné pour passer sur des CPU plus classiques.

 

Les contraintes d'un MCU

Avant tout chose il faut comprendre pourquoi Linux n'est pas fait pour fonctionner sur MCU.

L'espace mémoire

La plupart des MCU ne sont fournis qu'avec une flash d'une taille limitée. Dans l'exemple que nous allons aborder, nous n'avons que 2 Mo d'espace pour le stockage alors que la taille d'un noyau Linux atteint rapidement une taille de 5 à 10 Mo sans le système de fichiers.

Il est donc nécessaire de faire rentrer le tout aux forceps sur le stockage. Il y a de bons articles sur le sujet et nous n'aborderons que les limitations que nous avons appliquées pour arriver à démarrer notre exemple.

Pour l'usage de la RAM nous sommes souvent très loin des 4 Go et nous avons souvent l'avantage de pouvoir exécuter le code du noyau directement sur la mémoire de stockage.

La MMU et la création de processus

mmu

La MMU permet la conversion entre l’adressage virtuelle et la mémoire physique. Chaque processus a un accès à un même adressage virtuel sur un support physique différent.

L’intérêt de l’adressage virtuel est multiple :

  • gestion des collisions de mémoire entre processus

  • relocalisation physique des données sans perte d’informations sur les pointeurs

  • limitation de l’accès au matériel par des processus non-privilégiés

fork with mmu Grace à cela, les processus sont totalement indépendants et un processus enfant peut hériter du contenu de la mémoire de son parent sans pour autant avoir accès à l'original. Cette dernière fonctionnalité est très importante dans le création de processus avec l'usage de l'appel système "fork".

 

La création de processus sans MMU

L'appel système "fork" nécessite une copie de la mémoire du parent pour l'enfant. Si le parent possède un pointeur sur une zone mémoire comme un chaîne de caractères alors le pointeur de l'enfant contient l'adresse dans la mémoire du parent. L'enfant ne modifierait pas le contenu de sa mémoire mais celle de son parent avec tous les problèmes que cela engendre.

L'appel système "fork" est donc à proscrire dans un système sans MMU et doit être remplacé par un usage adéquat de "clone" ou de "vfork". Cependant, ce n'est pas fait par la majorité des applications comme Systemd ou Apache. Sur certaines plateformes (il y a quelques temps),  "fork" et "vfork" étaient la même fonction quand Linux était utilisé sans MMU, mais ce n'est actuellement pas le cas sur ARM, il est donc nécessaire de ne pas installer ces applications.

fork without MMU

L'usage de la mémoire pour les bibliothèques

L'un des intérêts de la MMU est de pouvoir configurer une zone mémoire virtuelle qui contiendrait le code des bibliothèques chargées pour tous les processus. Sans MMU, cela devient compliqué car Linux utilise normalement des bibliothèques "relogeables" c'est à dire que l'adresse de chaque symbole est indépendant de l'adresse physique de celui-ci.

Sans MMU, 3 solutions s'offrent à nous :

  • Avoir des binaires statiques, chaque application  est compilée avec ses bibliothèques et donc le code de celles-ci peut être dupliqué pour chaque processus l'utilisant.
  • Placer systématiquement les bibliothèques au même endroit sur la mémoire physique et donner un mécanisme au "dynamic linker" pour retrouver l'offset de chaque bibliothèques. Cette solution limite le nombre de bibliothèques utilisables, et oblige de préparer finement le système lors de sa génération.
  • Mettre en place le support d'un format de binaire qui permet de sauvegarder l'offset de chaque appel à une fonction d'une bibliothèque. C'est le format ELF-FDPIC, qui est basé sur le format ELF et  disponible pour la plateforme ARM grâce au travail de STMicroelectronics.

Le support de format FDPIC est disponible pour ARM dans le noyau Linux depuis la version 4.15. Il nécessite une version appropriée de GCC et du "dynamic linker".

Linux sur stm32f469i Discovery

stmm32f469i

Cette carte est basée sur un MCU ARM Cortex-M4 de STMicroelectronics. La "datasheet"  annonce 2 Mo de flash, 324 Ko de cache, 16 Mo de SDRAM (sur 32bits), un lecteur de micro-SD, un JTAG ST-Link, un bel écran tactile de 800x480, un support audio, et de nombreuses entrées sorties ouvrant de nombreuses possibilités de test.

 

Création d'une image avec Buildroot

Nous partirons de la branche 2020.08.x de Buildroot qui est récente et contient un premier support pour notre carte. Nous y apporterons notre touche de personnalisation dans une premier temps pour utiliser un noyau plus récent (4.19) que le noyau par défaut (4.11).

$ make -C /opt/buildroot O=$PWD/output_stm32 stm32f469_disco_defconfig
$ cd output_stm32
$ make menuconfig
$ make
BuildRoot "Toolchain" menuBuildRoot "Kernel" menuBuildRoot "Filesystem" menu

Nous allons donc modifier la version du noyau mais également utiliser la compression "xz" pour le code du noyau et l' "initramfs" qui sera notre seul root-filesystem.

Nous voyons vite qu'un patch présent dans Buildroot est devenu inutile avec le changement de version du noyau.

$ rm /opt/buildroot/board/stmicroelectronics/stm32f469-disco/patches/linux/0001-ARM-stm32f249-disco-don-t-force-init-in-chosen-boota.patch
$ make

Le résultat est décevant car nous obtenons un fichier "images/xipImage" à flasher qui fait 2, 6 Mo, soit bien trop volumineux pour être installé sur nos 2 Mo de flash. Commence alors un jeu d'optimisation.

$ make linux-menuconfig
$ make

 

Kernel "General Setup" menuKernel "System type" menuKernel "System type - STMicroelectronics" menu

Kernel "File systems" menu

 

L'optimisation du noyau est très laborieuse, et le résultat peut être frustrant. Nous vous conseillons d'utiliser  "OpenOCD" pour suivre le démarrage de votre noyau, car bien souvent vous n'aurez même pas de traces.

Mise à jour de la carte

Il est maintenant l'heure de tester notre image. Pour cela 2 options s'offrent à nous. Buildroot propose un script basé sur "OpenOCD" pour "flasher" la mémoire de la carte. Mais cette solution ne vérifie pas la taille des fichiers installés et nous préférerons donc l'outil "st-flash".

Si vous utilisez Ubuntu, il faudra construire "st-flash" à partir des sources car le paquet binaire  semblait pas fonctionner lors de nos tests. Cette étape est cependant simple et rapide. Le projet utilise CMake, et n'a  d'aucune adaptation.

Pour mettre à jour notre carte, il faut tout d'abord installer :

  1. le bootloader afboot, généré par Buildroot à l'adresse 0x8000000
  2. le device tree à l'adresse 0x08004000
  3. l'image du noyau à l'adresse 0x08008000
$ st-flash write images/stm32f429i-disco.bin 0x08000000
$ st-flash write images/stm32f469-disco.dtb 0x08004000
$ st-flash write images/xipImage 0x08008000

Le support FDPIC

Cette fonction est disponible dans le noyau que nous utilisons mais Buildroot n'est pas adapté pour produire le système et un  certain nombre de patch sont nécessaires. Ceux-ci sont en cours de test par un développeur de Buildroot et pourraient être rapidement intégrés à la branche mainline du projet.

Pour vous donner un avant-goût, il est nécessaire d'utiliser GCC 10.x, de modifier la gestion de configuration de l'architecture ARM afin de permettre l'usage de l'option -mfdpic avec GCC. Enfin, il faut modifier le préfixe de la chaîne de compilation. A noter que ni le noyau, ni le bootloader ne doivent prendre en compte l'option -mfdpic, qui n'est là que pour les applications du système.

Nous ne nous avancerons pas plus sur le sujet car si le noyau fonctionne, ce n'est pas le cas de BusyBox lors de nos tests. Le noyau termine donc son exécution sur une erreur de l'application "init".

Les cas d'usage possibles

L'utilisation d'un MCU apporte des contraintes importantes sur le matériel et donc sur le logiciel, ce qui en fait le plus souvent un choix peu judicieux pour Linux.

Dans notre évaluation de Linux sur la carte Discovery, nous avons un noyau tellement réduit qu'il est impossible de véritablement l'utiliser.

L'usage de Linux sur MCU s'approche plus de l'exploit technique que d'une mise en production. Même la plupart des  "Proof Of Concept" ne ferait que démentir son usage.

 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée.