SOS et JavaScript

Présentation

Vous connaissez peut-être SOS (Simple Operating System): un OS à but pédagogique créé par David Decotigny et Thomas Petazzoni. La description de son fonctionnement a fait l’objet d’une dizaine d’articles parus dans le magazine Gnu/Linux Magazine France entre 2004 et 2006. Il est composé principalement d’un noyau 32 bits multitâche conçu pour fonctionner sur une architecture de type x86 PC.

Or en mai 2011 Fabrice Bellard (QEMU, FFMPEG…) présente JavaScript PC Emulator qui, comme son nom l’indique, est un émulateur x86 codé en JavaScript. Celui-ci permet de faire tourner un système Linux dans son navigateur Web.

Je me suis alors empressé d’essayer d’exécuter SOS dans cet émulateur. Malgré ma faible expérience en programmation assembleur, j’ai tout de même voulu tenter ma chance.

http://gibusperon.free.fr/sos/jslinux.htm

Le code source original de SOS utilisé comme base est celui paru avec l’article 9, premier volet. En effet, l’émulateur permet de communiquer avec l’OS par l’intermédiaire du port série et cette version est la première à intégrer un pilote de terminal série ainsi qu’un shell de base. Les versions suivantes apportent surtout le support des périphériques en mode bloc, et n’auraient pas d’utilité dans ce contexte.

Chargement en mémoire

Pour commencer, le script jslinux.js crée une machine virtuelle en instanciant un objet de type PCEmulator. Une méthode permet ensuite de charger des fichiers binaires directement dans la mémoire de la cible. Dans le cas de Linux sont chargés le noyau, un initrd au format ext2 non compressé, un binaire linuxstart.bin qui remplit en partie le rôle de bootloader et une chaine de caractères représentant la ligne de commande de boot.

Par contre, les binaires exécutables ne doivent pas être au même format que s’ils étaient chargés avec GRUB. Pour le noyau Linux, il faut utiliser un dump mémoire du fichier vmlinux (la version non compressée sans routines de setup). Il faut donc modifier le Makefile de SOS pour générer non plus un exécutable au format ELF compressé mais un dump de celui-ci obtenu avec objcopy -O binary sos.elf sos.bin

De plus, Linux et SOS ne sont pas situés au même emplacement en mémoire: 0×100000 pour Linux et 0×200000 pour SOS. Plutôt que de modifier le script de liaison support/sos.lds, il est plus simple de modifier l’adresse de chargement dans le script jslinux.js.

Le bootloader

La section Technical Notes du site de JSLinux fournit le code source du bootloader.

Dans le cas de SOS, pas besoin d’initrd ou de ligne de commande. Par contre le binaire linuxstart.bin reste nécessaire: il constitue le point d’entrée de la machine virtuelle. Par rapport à un bootloader classique, son code est simplifié du fait que les exécutables ont déjà été chargés en mémoire et que l’émulateur démarre directement en mode protégé 32 bits (en effet le mode réel 16 bits n’est pas du tout implémenté dans JavaScript PC Emulator). Il effectue les opérations suivantes:

  • Mettre en place une pile temporaire dans la mémoire basse (0×1000-0×3000)
  • Initialiser une structure en mémoire basse (contenant entre autre la taille de la mémoire haute, l’adresse et la taille de l’initrd, la ligne de commande) qui sera transmise au noyau (adresse dans le registre SI)
  • Mettre en place la segmentation en chargeant une GDT basique
  • Sauter au point d’entrée du noyau

Seulement, contrairement à Linux qui utilise une structure de données qui lui est propre, SOS se base sur le standard Multiboot pour récupérer les informations en provenance du bootloader. Le code de linuxstart.bin est modifié pour initialiser une structure de type multiboot_info et y renseigner la taille de la mémoire haute (la quantité de mémoire totale en ko à laquelle on soustrait 1Mo de mémoire basse). L’adresse de cette structure est passée à SOS par le registre EBX tandis que EAX contient une constante particulière qui indique un chargement multiboot.

Dernier détail: pour Linux, linuxstart.bin effectue un saut absolu vers l’adresse de chargement du noyau. Le point d’entrée de SOS lui est situé 4ko plus loin: le code est préfixé d’une entête multiboot destinée au bootloader (inutilisée dans notre cas) et aligné sur la page mémoire suivante.

Première exécution

A ce stade, le lancement de SOS dans l’émulateur provoque le blocage du navigateur jusqu’à ce que celui-ci propose d’arrêter le script qui lui semble boucler. Pour m’aider à trouver la portion de code en cause, une fonction basique d’affichage sur le port série a été ajoutée dans le fichier drivers/printjs.c (le pilote terminal série n’est disponible que vers la fin de l’initialisation du noyau).

Après un débogage laborieux, les instructions d’empilement et de dépilement de mots sur la pile, respectivement pushw et popw, s’avèrent être la source du problème. Or celles-ci interviennent régulièrement dans le code de SOS, principalement lors des changements de contextes où les registres de segment 16bits sont sauvegardés sur la pile.

Comment se fait-il alors que le noyau Linux ne soit pas impacté par ce problème? La raison, objdump -d vmlinux | grep "\(pushw\|popw\)" ne renvoie aucun résultat: Linux n’utilise jamais ces instructions. De plus, Fabrice Bellard indique dans la section "Technical Notes" du site de Javascript PC Emulator: A few seldom used instructions are missing. Il y a donc de fortes chances que ces instructions ne soient pas émulées.

La solution consiste à remplacer tous les appels à pushw et popw par un code qui manipule manuellement la pile. Par exemple pour empiler le registre SS:

pushw %ss

est remplacé par:

subl  $2, %esp     //Décrémente le pointeur de pile ESP
movw  %ss, (%esp)  //Affecte la valeur du registre SS en fin de pile

Idem pour le dépilement de SS:

popw  %ss

est remplacé par:

movw  (%esp), %ss  //Attribue la valeur en fin de pile au registre SS
addl  $2, %esp     //Incrémente ESP

Après modification, SOS se lance et l’on accède à un shell basique. (uname, ls, cat, touch…)Un petit souci d’affichage apparait lors de l’appui sur la touche Entrée. Ceci est corrigé en forçant l’envoi d’un retour chariot ‘\r’ lors de l’affichage d’une chaîne de caractère se terminant par ‘\n’.

Les pilotes

On peut remarquer lors du lancement de Linux une ligne qui indique le temps de boot. Un compteur est initialisé au lancement de la machine virtuelle dans le code javascript jslinux.js. Il suffit alors, une fois le noyau chargé, de récupérer la valeur courante du compteur en millisecondes sur le bus d’entrées/sorties à l’adresse 0x3CC. Au chargement de SOS, vous pouvez également le voir apparaître, il vaut environ 400ms sur mon portable avec Firefox 5 par exemple.

Enfin, il reste une dernière fonction à ajouter pour profiter pleinement des fonctionnalités de l’émulateur: le presse-papier. Le patch fourni par Fabrice Bellard destiné au noyau Linux contient le code d’un driver en mode caractère.Celui-ci permet par l’intermédiaire du fichier spécial /dev/clipboard de recevoir ou d’envoyer du texte vers la fenêtre du presse-papier située à droite du terminal.

La modèle de pilote de SOS étant largement inspiré de celui de Linux, ce code est facilement transposable. Le fichier drivers/jsclipboard.c implémente les fonctions de base open, close, read et write. En résumé, le pilote utilise les entrées/sorties suivantes:

Adresse     I/O  Opération
0x3C0-0x3C3 In   Récupérer la taille totale du presse-papier
     -      Out  Vider le presse-papier
0x3C4-0x3C7 In   Récupérer la position dans le fichier
     -      Out  Affecter la position dans le fichier
0x3C8-0x3CB In   Lire le presse-papier
     -      Out  Ecrire dans le presse-papier
0x3CC-0x3CF In   Récupérer la valeur du compteur de boot
     -      Out  Synchroniser le presse-papier

Le fichier spécial /dev/clipboard d’identifiant majeur 4 et d’identifiant mineur 0 est créé dans le processus init (userland/init.c) par l’appel système mknod. Il est désormais possible de lire le contenu du presse papier en ligne de commande par cat /dev/clipboard.

L’écriture dans le presse papier n’est pas accessible directement par cat fichier > /dev/clipboard car l’opérateur de redirection ‘>’ n’est pas supporté dans le shell de SOS. Par contre une commande toclipboard a été ajoutée dans userland/shell.c pour y remédier.

Ces commandes permettent de copier le contenu d’un fichier dans le presse-papier:

$ edit fichier
Ligne 1
Ligne 2

$ toclipboard fichier

[Sources de SOS sous JavaScript]

[Patch de l'article 9]

Merci à Fabrice Bellard pour m’avoir autorisé à redistribuer JavaScript PC Emulator

About these ads

One thought on “SOS et JavaScript

  1. Waouh, j’aime !
    Et en plus ça marche ™ !

    Je suis bien content que SOS ait pu faire quelques émules, ça donne envie de s’y remettre… d’autant plus qu’avec Thomas on a le (début du) code de quelques articles sous le coude, manque (presque) plus qu’à rédiger du texte.

    Bravo pour cette splendide démonstration et ce billet très intéressant !

    PS : Pas de doute, on devrait sérieusement penser à créer le fan club de Fabrice Bellard !
    PPS : On aurait vraiment du ajouter une commande "help", parce que je suis bien incapable de me souvenir du genre de choses qu’on peut taper dans le shell en dehors de "ls" :)
    PPPS : Serait-il possible de proposer un patch sur les sources "officielles", en plus du tarball ?

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s