Parfois, cela fait du bien de se souvenir du passé et du chemin parcouru, mais c'est encore mieux de le revivre. 2.11BSD est la dernière version de la branche 2BSD (Second Berkeley Software Distribution) de la famille UNIX. Édité originellement par le CSRG et l'association USENIX, ce système fonctionnait sur mini-ordinateur DEC PDP-11 et fait désormais, et pour toujours, partie de la grande histoire des UNIX. Aujourd'hui, nous allons revivre cette histoire et faire fonctionner un PDP-11 sur un simple ESP32 pour exécuter 2.11BSD !
Le projet que nous allons utiliser est celui créé par Jeroen Domburg (alias Spritetm) consistant en un portage du simulateur SIMH [1] sur ESP32. Initialement développé sur une carte ESP-WROVER-KIT, il l'a ensuite décliné pour un montage sur mesure [2], prenant la forme d'un minuscule terminal VT52/IE15 équipé d'un écran LCD 320×240 pixels. Ici, faute de matériel dédié, nous allons nous en tenir à la plus simple, mais suffisante, expression du projet.
Il vous faudra une carte ESP32 équipé d'une PSRAM permettant d'étendre la mémoire vive du microcontrôleur. Il existe plusieurs modèles de cartes, Wemos ou autre, disposant de ce composant, et il est bien sûr également possible d'intégrer manuellement une PSRAM (SPI) à une carte existante. Personnellement, j'ai opté pour la solution de facilité, en réutilisant une ESP32Cam que nous avions étudiée dans le numéro 33 [3]. Celle-ci est équipée d'une PSRAM et d'un emplacement pour microSD et conviendra tout à fait. Elle a simplement été débarrassée de son module caméra et de la LED blanche (clignotant insupportablement durant les accès SD). Vous aurez également besoin d'un adaptateur USB/série, puisque l'ESP32Cam ne dispose pas d'interface permettant la programmation. Le module pourra alimenter l'ESP32 et le mode de programmation sera activé en mettant la broche IO0 à la masse juste avant le reset.
1. Le projet ESPPDP
Pour construire votre propre minuscule simulateur de PDP-11, vous aurez besoin de l'environnement de développement Espressif ESP-IDF (cf. l'article sur le sujet dans Hackable 34 [4]), configuré et prêt à servir (variables d'environnement initialisées avec un . export.sh). La version 5.0 vient de sortir, mais j'ai utilisé la 4.4.1, et le créateur du projet initial a vérifié le bon fonctionnement lui-même avec une 4.2.
Vous devrez ensuite cloner le dépôt Git :
Puis vous placer dans le répertoire esppdp/firmware pour utiliser la commande idf.py menuconfig afin de choisir le type de carte utilisé. Nous avons le choix entre « Esp32-Wrover-Kit » et « Final dedicated board », via les menus « ESP-PDP11 Configuration », puis « Hardware to run on ». J'ai choisi la seconde option après avoir rapidement parcouru le schéma du montage [2], mis à disposition dans le dépôt Git. Certes, l'ESP32Cam ne dispose pas d'écran LCD, mais les GPIO utilisés n'interfèrent pas avec les composants présents sur le périphérique. Il n'y a donc pas de modification véritablement nécessaire à faire dans le code (mais un « nettoyage » est prévu pour plus tard).
Une fois la configuration validée, il vous suffira de passer l'ESP32Cam en mode programmation (GPIO0 à la masse) et lancer la construction avec un idf.py all, puis le flashage du firmware avec idf.py flash monitor. Ceci aura pour effet de programmer le microcontrôleur et d'enchaîner immédiatement sur le démarrage du moniteur série.
Par défaut, une image d'un support de stockage est directement embarquée en SPIFFS (avec un Tetris en baremetal sur PDP-11). Cependant, pour utiliser le système 2.11BSD, vous devrez préparer une carte microSD. Dans le répertoire hard-disk-image se trouve une archive rq.dsk.zip qui, après désarchivage, devra être copiée sur le support préalablement formaté en FAT32. Lorsque le firmware démarre, il cherche automatiquement le fichier en question, et s'il le trouve, il l'utilisera comme un disque dur pour le PDP-11.
2. Utilisation de 2.11BSD
Directement suite à la programmation de la flash, retirez la connexion de GPIO0 à la masse et utilisez le bouton de reset. Si tout se passe correctement, vous devrez voir une série de messages défiler à l'écran, puis s'arrêter sur :
C'est l'invite du chargeur du PDP-11 qui attend votre feu vert, appuyez simplement sur « Entrée » et soyez patient. Le boot va alors se poursuivre :
Cette fois, nous avons une invite de shell UNIX, mais nous sommes en mode mono-utilisateur. C'est une simple phase préliminaire du boot BSD. Là, utilisez la séquence Ctrl+D pour quitter ce mode et laisser init passer dans le runlevel multi-utilisateur :
Entrez root, puis esppdp11 en guise de mot de passe et vous voici connecté à votre serveur 2.11BSD. Jeroen Domburg a créé un petit outil permettant de configurer facilement la connexion Wi-Fi. Pour cela, invoquez simplement la commande wifid -connect suivie, en argument, du SSID de votre point d'accès Wi-Fi et du mot de passe associé. Cette configuration perdurera d'un démarrage à l'autre et vous donnera immédiatement une connexion au Net. Chose que vous pouvez vérifier avec :
Ceci signifie que la machine peut accéder à l'extérieur, mais aussi qu'il est possible d'y accéder depuis n'importe quel hôte du réseau. Mais tous les services en route ne permettent pas l'utilisation du compte root. Vous devez donc créer un utilisateur standard (oui, il en existe déjà un mais ce n’est pas amusant). Pour cela, préparez la ligne suivante dans un coin : hackable::102:10::::Hackable magazine:/usr/home/hackable:/bin/sh. Il s'agit d'une entrée classique de /etc/passwd avec, par exemple, hackable comme nom d'utilisateur, 102 comme UID, 10 pour le GID (staff), /usr/home/hackable pour son $HOME et /bin/sh comme shell par défaut.
Connectez-vous en root au système (idéalement en Telnet puisque vous avez l'IP) et spécifiez tout d'abord votre type de terminal (sinon vi râle plus tard) :
Ajoutez ensuite la fameuse ligne à master.passwd (avec un copier-coller) et régénérez la base de mots de passe :
Vérifiez ensuite (c'est important) l'utilisateur créé avec :
Ceci lance l'éditeur vi et vous affiche les informations. Il n'y a rien à changer, mais en sauvegardant et en quittant simplement (:wq), l'outil va réinitialiser /etc/passwd et en faire disparaître les mots de passe chiffrés provenant de master.passwd. Nous pouvons alors créer le répertoire personnel de l'utilisateur :
Et enfin, si vous voulez que celui-ci puisse utiliser su, ajoutez-le dans le groupe wheel en éditant le fichier /etc/group avec vi. Inutile de connaître les vieilles commandes du Vi original ici, un simple A (majuscule) vous placera au bout de la première ligne, en mode insertion. Ajoutez alors ,hackable, puis enregistrer le fichier avec [Echap], puis :w! (le fichier est en lecture seule). Enfin, quittez avec :q.
Dans un autre terminal, connectez-vous en Telnet et saisissez le nom de l'utilisateur créé :
Il n'a pas de mot de passe, vous obtenez directement un shell. Changez alors le mot de passe avec :
À présent que l'utilisateur existe et possède un mot de passe, vous pouvez vous connecter au serveur FTP pour prendre et déposer des fichiers. Quels fichiers ? Pourquoi pas le binaire d'un programme que vous aurez compilé sur la machine cliente ?
3. Compiler ses programmes sur PC (ou RPi)
Le 2.11BSD fonctionnant sur le PDP-11 simulé par l'ESP32 dispose d'un compilateur C parfaitement utilisable. Cependant, ceci est d'une lenteur « historique » et quelque chose de très peu ergonomique (vous avez sans doute remarqué que le la console ne réagit pas comme un terminal moderne). Pour développer pour le système et cette machine mythique, nous pouvons cependant construire un compilateur croisé. Nous pourrons alors développer sur une plateforme moderne pour le système cible.
Nous allons devoir construire une chaîne de compilation et, avant toutes choses, nous devons installer dans notre système (Ubuntu, Debian, Raspbian, Devuan, etc.), les paquets nécessaires : libmpc-dev, libmpfr-dev et libgmp-dev. Ceci fait, nous pouvons nous placer dans le sous-répertoire gcc-pdp11/ du dépôt. Jeroen Domburg a déjà fait une partie du travail pour nous, en nous fournissant une Libc pour la plateforme, mais nous devons faire tout le reste.
Nous commençons donc par créer un répertoire crosstool à cet endroit, destiné à accueillir le cross-compilateur. Puis nous récupérons et désarchivons les binutils et GCC :
Nous en profitons pour créer une variable d'environnement avec le répertoire de destination à utiliser :
Commençons par les binutils :
Adaptez la valeur du paramètre -j en fonction de votre processeur. Remarquez que les notes de Jeroen (gcc-pdp11/notes.txt) utilisent une cible pdp11-bsd2.11 qui ne semble pas plaire du tout au GAS (assembleur GNU) des binutils 2.35, et que downgrader à 2.25 soulève des problèmes de formats de binaires par la suite).
Nous passons ensuite à GCC, mais notez qu'il ne faut pas compiler directement dans le répertoire source (ce n'est pas l'usage et également source de problèmes tantôt). Nous créons donc un répertoire pour l'occasion :
Nous avons à présent un compilateur fonctionnel dans $CROSSDIR, mais ce n'est pas suffisant. Nous devons ajouter la Libc et les fichiers d’en-tête adéquats. Les libs sont fournies et nous n'avons qu'à les copier :
Puis nous passons aux fichiers en-tête provenant du projet RetroBSD. Les véritables fichiers de 2.11BSD ne sont pas compatibles avec un compilateur récent :
Tout est presque prêt mais nous devons d'abord corriger un problème. Le compilateur utilise une LibGCC qui elle-même a besoin de la présence d'une fonction _atexit(), qui n'existe pas dans la Libc d'origine. Nous devons donc corriger le problème en créant une fausse fonction, puis ajouter le fichier objet dans la bibliothèque statique libc.a. Le fichier source (atexit.c) est le suivant :
Et nous compilons et installons avec :
Remarquez que nous commençons tout d'abord par modifier temporairement le chemin de recherche des exécutables ($PATH) pour que le shell trouve le compilateur et ses outils. Nous pouvons maintenant passer à la partie la plus jouissive (oui, et je pèse mes mots) puisque nous allons écrire notre premier code pour le 2.11BSD fonctionnant sur un PDP-11 !
Voici notre source C :
Et voici notre Makefile :
Notez que nous utilisons prudemment le script pour l'éditeur de liens présent dans esppdp/wifid, les sources de l'outil wifid de configuration de la connexion Wi-Fi, qui devra donc être copié dans le même répertoire. Un simple make permettra de produire le binaire hello qu'il nous suffira de transférer sur la cible (en FTP) et nous donner le plaisir de faire ceci :
Et voilà comment on peut développer un code sur un PC GNU/Linux, en 2022 (2023 sans doute quand vous lirez ceci), pour une machine légendaire de plus de 30 ans faisant fonctionner un système non moins mythique, à l'origine de tant de bonnes choses.
4. Pour finir et petite astuce
Il n'y a qu'une conclusion possible à cet article, celle où je tire mon chapeau à monsieur Jeroen Domburg [5], doublé d'une révérence en prime, car le travail accompli est tout bonnement merveilleux. Visitez son site, il est plein de projets du même acabit, tout simplement bluffant (comme sa décoration pour sapin de Noël, permettant de jouer à Doom [6]).
Si vous voulez vous faire plaisir avec un PDP-11 mais n'avez pas d'ESP32 adapté sous la main, vous pouvez également utiliser SIMH sur différentes plateformes et une image disque 2.11BSD est disponible sur le dépôt GitHub de « Eunuchs » [7] à cet effet.
Et je finirai simplement avec une petite astuce, si vous voulez transférer « facilement » des binaires sans liaison d'aucune sorte avec votre hôte 2.11BSD :
Références
[1] https://github.com/simh/simh
[2] https://spritesmods.com/minipdp11/esppdp-schematic.pdf
[3] https://connect.ed-diamond.com/Hackable/hk-033
[4] https://connect.ed-diamond.com/Hackable/hk-034/developpement-esp32-avec-le-nouveau-esp-idf-4.0
[6] https://www.youtube.com/watch?v=u9ODhV40aEI
[7] https://github.com/eunuchs/unix-archive/tree/master/PDP-11/Boot_Images/2.11_on_Simh