Seuls les membres ayant 30 points peuvent parler sur le chat.

Forum Casio - Projets de programmation


Index du Forum » Projets de programmation » Vhex - Une plateforme de développement
Yatis Hors ligne Membre Points: 491 Défis: 0 Message

Vhex - Une plateforme de développement

Posté le 09/03/2020 10:54

Salut !

Personnellement je suis encore sceptique à l'idée que ce projet intéresse(ra ?) des membres de la communauté mais certaines personnes m'ont poussé à écrire ce topic pour mettre en avant mes avancés sur le projet. Comme le projet risque d'être long, je pense l'organiser comme Ninestars le fait pour Windmill : comme un journal de bord. Je tiens à préciser qu'il risque d'y avoir beaucoup de blabla technique et je ferrai au mieux pour rendre ça compréhensible.

Vhex est à la base un désassembleur que j'avais écrit pour m'aider à faire de la retro-ingénierie "on-calc" et trouver certaine infos en RAM concernant les Bfile_*. Il y a quelque mois, lors du développement de GladOS (un OS qui a pour but de remplacer complètement Casio de l'EEPROM), je suis partie a la recherche du module USB pour envoyer les logs de debug sur mon laptop plutôt que sur le petit écran de la calto. Au bout de plusieurs semaines, je n'ai rien trouvé (pas la moindre adresse x_x) et j'ai eu l'idée d'utiliser l'User Break Controller, un module hardware qui me permet de poser des points d'arrêt n'importe où pour suivre certain syscall de Casio et me faciliter la tache de désassemblage. J'ai donc fait un prototype de débogueur / traceur, ce qui m'a parmi de suivre le déroulement du syscall 0x135 Bdisp_GetVRAM_Address() instruction par instruction. Mais comme j'utilisais l'OS de Casio pour déboguer... l'OS de Casio, je me suis confronté pour la première fois au terme de "Réentrance". Pour palier a ce problème il fallait donc que je trouve un moyen pour embarquer mon gestionnaire d'interruption et quelque drivers (écran, clavier) pour survivre dans mon handler de l'UBC.
Et....c'est la ou tout a basculé.

Malheureusement (?), je me suis dit qu'il serait temps d'avoir une vraie plateforme de développement et de rétro-ingénierie sur la calto. J'ai donc commencer la création d'un kernel *nix-like qui aura un environnement multi processus et d'autre petite chose sympathique comme la gestion de librairies dynamique, un débogueur pas-à-pas, un compilot asm, un éditeur de texte et plus si affinité En théorie, on devrait arriver à un kernel où l'on pourra développer, compiler, tester et documenter des projets depuis la calculatrice. Bien sûr, nous en sommes est pas la mais j'ai de bon espoirs pour y parvenir un jour.

En parallèle et avec Lephenixnoir, on a commencé un projet qui se nomme fxdoc.
Le but est très simple, centraliser la documentation de l'OS de Casio pour aider le développement de projet plus ambitieux, écrire des drivers bien plus rapides que ceux mis à disposition par Casio ou encore pour connaitre les faiblesses / limitations de certain module fournis pas Casio (Comme le système de fichiers en écriture, le clavier, etc...). Ce projet là me montre l'intérêt que pourra avoir Vhex avec le temps (notamment déboguer l'OS pas-a-pas), ce qui me motive beaucoup en ce moment
(PS: d'ailleurs, si vous voulez participer ou avoir des nouvelles des différents projets, passez sur le channel dev )

» Dépôt Gitea Yatis/Vhex-kernel «


Avant d'écrire la première page du journal, j'aimerai faire une liste de ce que j'ai déjà :
* un bootstrap
* un bootloader
* un Virtual File System
* un File System de type EXT2 en RAM.
* un scheduler
* un loader ELF
* divers driver: écran (T6K11, T6K11 variante), clavier (KEYSC), timers (TMU)
* un accès au système de fichier de Casio (en lecture seulement)
* la gestion des devices à la UNIX: tty
* la gestion des syscall via l'instruction trapa.
* un prototype de débogueur pas-a-pas utilisant l'UBC.
(PS: cette partie sera mies a jours au fur et à mesure des updates)


Jour 1 - J'ai tout cassé
Cliquer pour enrouler
Jour 1 - J'ai tout cassé.

J'ai tout cassé,

Avant de tout casser, j'avais un problème avec le multi processus, j'avais des bugs étranges lors des tests avec plusieurs processus en parallèle et j'ai mis pas mal de temps à comprendre d'où venais le problème. Cela venait de l'affichage (merci Lephe pour m'avoir aidé sur ce coup). Pour faire très simple la zone de mémoire (la vram) ou je dessinais étais partagée entre les processus et le kernel. Donc quand un processus commençais à dessiner, un autre processus pouvais prendre le relai et dessiner et / ou afficher la vram alors qu'elle était encore en train d'être utiliser par un autre processus ce qui aboutissais a des artéfacts visuel très étrange. Mais je tiens à signaler que le changement de contexte (passer d'un processus a un autre et d'un processus au kernel via les appelles systèmes) fonctionne biens

Pour résoudre le problème j'ai décidé de faire du ménage et de changer l'organisation du projet car je me suis rendu compte que le module de "dessins" et les fonctions triviales (du style strcpy, strcmp, memcpy)... étaient dupliqués (une copie pour le kernel et une autre pour les utilisateurs). De plus, certaine partie du kernel étais mal pensé d'un point de vue architecture (nom de dossier qui étais pas assez explicite, des fichiers au mauvais endroit, etc). J'ai donc déplacé, renommé et (c'est la une de mes erreurs) modifié certain mécanisme du kernel (la gestion des TTY surtout). Il en résulte une architecture qui me satisfait en terme d'architecture de fichiers mais certaine partie en termes de code sont incroyablement mal pensé (x_x) Notamment la façon d'afficher les logs de "debug" du kernel car j'utilise quelques fonctions de la lib de dessin pour afficher du texte a l'écran, alors que le kernel ne devrai pas avoir a dessiné quoi que ce sois.

Je vais donc implémenter le device /dev/console pour afficher des logs de debug proprement et faire un wrapper de printf() histoire de pouvoir formater un peu la sortie sur l'écran. Une fois cette modification effectuée et que le code fonctionnera de nouveau, je compte refaire toute la gestion des processus ainsi que loader ELF qui est actuellement semis-fonctionnel. Mais ça j'en parlerais plus tard

Pour résumer :
J'ai changé une bonne partie de l'architecture du projet et actuellement ça compile mais j'ai de gros problème de crash et plus rien ne s'affiche a l'écran x)


Jour 2 - J'ai tout réparé
Cliquer pour enrouler
Jours 2 - J'ai tout réparé

La dernière fois je vous expliquais que j'avais cassé tout le projet en voulant faire un refactor au niveau de l'organisation des fichiers.

J'ai corrigé tous les problèmes de crash et je suis de nouveau avec quelque chose qui fonctionne Maintenant la partie "dessins" est totalement indépendante et extérieure au kernel. J'en ai aussi profité pour implémenté le device earlyterm pour avoir des infos venant du noyau, ce qui m'a permis de dégager pas mal de code de debug assez lourd et sale. (PS: le device est est caché, ce qui signifie qu'un programme utilisateur ne pourra pas y accéder avec les primitives du VFS).

D'ailleurs, voila une courte vidéo de démonstration.
Dans cette vidéo, on voit le kernel initialiser la machine ainsi que ses différents modules (FS, VFS, FHS, Scheduler) puis lance un exécutable depuis la SMEM, ici un shell. Comme vous l'auriez remarqué, le shell ne peut rien exécuter pour l'instant car je dois mettre en place certaine protection avant de pouvoir lancer plusieurs processus en parallèle (mutex et sémaphore entre autre). (Je tiens à préciser qu'au moment où le shell est exécuté le kernel est en mode "multi-processus" (le scheduler est activé) et le shell utilise exclusivement les syscall pour communiquer avec le noyau ).



Mon prochain objectif et de refaire totalement le loader ELF car j'ai un gros problème avec : Les programmes utilisateurs ne peuvent pas utiliser de variables globales sous peine de faire planter le processus. Je m'explique. Le but de Vhex est d'inspecter l'OS ce qui me demande d'interférer le moins possible avec ce que touche Casio. Ce qui implique que le MMU (le module hardware qui gère la mémoire virtuelle) ne peut pas être utilisé (pour beaucoup de raisons) et le fait qu'il ne puisse être utilisé pose de gros problème de sécurité et m'oblige à faire tenir tout le projet dans 256ko de RAM.

Pour qu'un exécutable puisse être lancé dans ce genre de configuration, il doit pouvoir être relocalisé n'importe ou dans la RAM. Pour ça il faut qu'il soit indépendant vis-à-vis de l'adressage mémoire (ça concerne donc le code ainsi que tous les symboles globaux). Pour ce qui est du code ce n'est pas bien compliqué, il suffit de demander au compilateur de générer le code approprier (on parle dans ce cas de Position Independent Code (PIC)) Mais pour ce qui est des globales...c'est une autre histoire. Je dois relocalisera la main les symboles qui sont considéré comme "globaux", ce qui est tout de suite un peu plus technique à faire.

Actuellement je relocalise rien du tout, ce qui explique pourquoi je peux seulement exécuter du code.
Je me dis que quitte à relocaliser chaque symbole a la main autant mettre en place la gestion des librairies dynamique au passage car le principe a vaguement similaire et ça sera biens pratique par la suite. Pour ceux qu'ils ne le savent pas, le principe des librairies dynamique est de pouvoir créer des exécutables avec des fonctions qui seront chargés uniquement lors du chargement du programme. L'intérêt ? avoir qu'une seule copie des fonctions en RAM, ce qui permet de gagner de la place et d'éviter les doublons de code en RAM (ce qui sera indispensable vu le peu de mémoire que l'on a à disposition >_< )

En résumer :
* L'architecture en termes de fichiers me plait et est suffisamment robuste pour que je puisse passer a la suite.
* Le prochain objectif saura de refaire le loader ELF pour relocaliser les symboles qui doivent l'être.
* Mettre en place la gestion des librairies dynamique.


Jour 3 - Préparation du terrain
Cliquer pour enrouler
Jour 3 - Préparation du terrain

Il y a quelques jours, je vous expliquais qu'il fallait que je refasse le loader ELF (une partie du kernel qui s'occupe de charger un exécutable depuis l'EEPROM dans la RAM) pour qu'on puisse utiliser les globales dans un programme utilisateur. C'est chose faites ! Toutes les adresses qui doivent être relocalisées sont remplacées dynamiquement pendant le chargement des exécutables ! Les programmes sont donc compilé en PIE et non plus en PIC. (une idée que Lephe avait lancée il y a de ça 1 an !)

La dernière fois, j'ai aussi mentionné le fait que je voulais implémenter la gestion des librairies dynamiques.
Malheureusement, je n'arrive pas encore à créer de fichier "shared object" (les fameux .so sous Linux). Visiblement le flags -shared (indispensable pour créer ce genre de fichiers) semble n'avoir aucun effet. J'ai donc essayer en recompiler gcc et binutils en faisant bien attention de bien activer manuellement la création de librairies dynamique mais rien à faire. Lephe m'a conseillé d'envoyer un mail à gcc-help pour leur demander conseil mais en deux jours je n'ai toujours pas eu de nouvelle.

D'un autre côté, ce problème de librairies dynamiques est plutôt bien tombé.
Actuellement, le support des librairies dynamiques n'aurait pas pu être à 100% compatible avec l'état actuel du kernel car je ne peux assurer la sécurité des mémoires partagées. Puis l'intérêt de pouvoir gérer ce genre de mécanisme est de soulager la RAM en partageant du code qui est souvent utilisé entre les processus (les exécutables prennent moins de place en RAM, en gros). Mais comme je n'ai pas encore la gestion du multi-processus ça n'a pas grande utilité pour l'instant (bien que l'ordonnanceur sois présent est activé).

Du coup, je me suis penché sur la partie "multi-processus" que j'avais commencé à mettre en place.
Et avec du recul, je me rends compte que beaucoup de choses sont totalement mal pensé et j'ai commencé à remettre un peu d'ordre, histoire de préparer le terrain avant que je mette à jour la gestion des processus.
Dans ce qui est de mettre un peu d'ordre, j'ai refait toute la partie "librairies", me donnant maintenant deux librairies : libc (string, stdio, unistd, ....) et display (pour tout ce qui touche au dessin). Cette partie est maintenant suffisamment isolée pour pouvoir y travailler en parallèle sans casser le noyau
J'ai aussi mis en place l'ouverture automatique du TTY quand le processus le demande (on a plus besoin de faire un fd = open("/dev/tty", O_RDWR)). Ce qui nous donne quelque chose de bien plus simple pour la création d'un programme basique :

#include <lib/unistd.h>
#include <lib/stdio.h>

int main(void)
{
    char buffer[12];
    
    printf("Planet Casio <3\n");
    while (1)
    {
        printf(">"),
        read(STDIN_FILENO, buffer, 12);
    }
}


Le prochain objectif et de mettre en place la gestion des processus "simple" c'est-à-dire sans la notion de "communication interprocessus" (IPC).
Le but étant de tester l'ordonnanceur et de voir si le kernel est bien "isolé" de tout problème de "non-réentrance" (et accessoirement d'avoir une preuve qu'un tel projet est viable). Si tout se passe bien, je devrai pouvoir vous présenter la commande tree en actions d'ici la prochaine update du topic

Pour résumer :
* Les programmes sont maintenant correctement relocalisés en RAM
* J'ai mis à jours toute la partie "librairies" et c'est suffisamment isolées pour pouvoir travailler dessus en parallèle.
* J'ai commencé à changer certains mécanismes du noyau comme le fait d'ouvrir automatiquement un TTY quand un processus le demande.
* Je bosse actuellement sur le multi-processus.


Jour 4 - ça dévient intéressant


J'ai de bonne nouvelle
La première, c'est que le kernel supporte maintenant l'exécution de plusieurs processus en (pseudo) parallèle
La seconde, j'ai réglé pas mal de problème qui traînaient avec le TTY
La troisième, j'ai enfin réussi à créer des librairies dynamiques (il suffit juste les compiler en PIE) mais rien n'est fait pour les gérer actuellement.
Et la dernière, l'user space commence à devenir vivant !! Voilà la liste de tout les appelles système qui sont utilisable :
Cliquez pour découvrir
Cliquez pour recouvrir

Manipulation de fichiers
open
read
write
close
lseek

Processus
fexecve (fork() + execve(), custom)
getpid
getppid
getpgid
_exit
wait
waitpid
setgpid


Et je vous donne, comme promis la dernière fois, une vidéo de démo ainsi que le code source du programme :

int main(void)
{
    char c;
    int fd;

    fd = open("/mnt/casio/VHEX/text.txt", O_RDONLY);
    if (fd < 0) {
        dprintf(STDERR_FILENO, "unable to open test file\n");
        return (84);
    }

    while (read(fd, &c, 1) == 1)
        write(STDOUT_FILENO, &c, 1);
    return (0);
}



Comme vous pouvez le voir, j'affiche caractère par caractère un fichier texte (de 2051 octets) et....bah ce n'est pas incroyable en termes de perf'
Mais ce ne signifie pas que le multi-process est lent, c'est juste la pire des manières de procéder pour afficher un fichier (c'est volontaire ici) car il y a un appel système pour lire l'octet du fichier (c'est très lent, surtout pour les Bfile ) et un autre pour l'envoyer dans le TTY.
Puis il faut savoir que je dessine a chaque fois tout ce qui est visible à l'écran, c'est surtout ça qui prend du temps. Mais c'est complètement améliorable et je pense pouvoir aller au moins 3 fois plus vite en optimisant un petit peu
(PS: le PID, PPID et PGID qu'on voit apparaitre au début sont ceux du shell et non ceux du processus qui lit le fichier)

Alors je sais, je vous avais dit que la vidéo de démo se serai un tree, mais je ne peux pas encore le faire pour certaines raisons.
Déjà, il me faut une gestion du tas pour chaque processus (malloc(), free() et compagnie) mais aussi parce que malheureusement, j'ai un problème avec le loader. Il lui arrive, de temps en temps, de ne pas réussir à relocaliser les symboles, ça ce manifeste de deux manières :
* Soit il n'arrive pas à charger la section .shstrtab qui contient le nom de toutes les sections (très pratique, voir indispensable, pour déboguer).
* Soit il ne relocalise rien et j'ai une exception qui se lève quand le programme est exécuté (ce qui est normal car le programme est corrompu).

En deux jours je n'ai pas réussi à mettre la main sur le fautif (je sais que le curseur du fichier passe inexplicablement à -1) et ça commence à me faire peur car le problème peut venir de n'importe ou (ça ressemble fortement a une corruption de stack x_x). Heureusement, le problème semble toujours venir du même endroit mais il n'y a pas grand-chose à part un read() et un lseek(). C'est d'ailleurs pour ça que j'ai fait ce programme de tests qu'on voit sur la démo. Il m'a permis de voir qu'environ 2000 read() ne posais pas de problème de corruption. Il n'est donc pas impossible que cela vienne de plus haut. Bref, j'ai des pistes mais c'est très frustrant et surtout ça me bloque pour continuer.

Pour ce qui est des objectifs future :
* Corriger ce bug de loader (x_x)
* Refaire la gestion de la mémoire (allocation par pages, histoire de réduire les pertes).
* Implémenter la gestion du tas pour les processus (et le kernel du coup)
* Passer les arguments argc et argv au programme utilisateur.


Kikoodx Hors ligne Membre Points: 1939 Défis: 11 Message

Citer : Posté le 09/03/2020 14:18 | #


Le projet m'intéresse en tant que spectateur (j'y comprend pas grand chose )
Je suivrai les avancées, le topic est compréhensible
Lephenixnoir Hors ligne Administrateur Points: 17447 Défis: 142 Message

Citer : Posté le 10/03/2020 15:50 | #


En voilà une affaire intéressante ! Plus besoin d'être un ninja pour savoir ce que tu fais maintenant

J'ai envie de dire plein de trucs mais je vais te laisser continuer à présenter pour l'instant
Yatis Hors ligne Membre Points: 491 Défis: 0 Message

Citer : Posté le 15/03/2020 23:21 | #


Le deuxième jour est arrivé

J'ai envie de dire plein de trucs mais je vais te laisser continuer à présenter pour l'instant

Non dit moi. Je suis vraiment nul pour faire de la communication sur un projet (surtout aussi technique que ça) alors je pense que sans aucun retour je ne pourrais jamais être compris par qui que ce sois à part moi x)

Ajouté le 21/03/2020 à 21:58 :
Le troisième jour est arrivé
Rader Hors ligne Membre Points: 225 Défis: 0 Message

Citer : Posté le 21/03/2020 22:00 | #


Ah, le lien vers wikipedia pour PIE renvoie vers PIC
Oh nvm j'ai pas bien lu la page wikipedia xD
Yatis Hors ligne Membre Points: 491 Défis: 0 Message

Citer : Posté le 21/03/2020 22:10 | #


Ah, le lien vers wikipedia pour PIE renvoie vers PIC

Oui c'est normal mais du coup je l'ai changé pour un lien sur stackoverflow ou quelqu'un explique l'intérêt du PIE

Ajouté le 25/03/2020 à 13:23 :
Le quatrième jour est arrivé

LienAjouter une imageAjouter une vidéoAjouter un lien vers un profilAjouter du codeCiterAjouter un spoiler(texte affichable/masquable par un clic)Ajouter une barre de progressionItaliqueGrasSoulignéAfficher du texte barréCentréJustifiéPlus petitPlus grandPlus de smileys !
Cliquez pour épingler Cliquez pour détacher Cliquez pour fermer
Alignement de l'image: Redimensionnement de l'image (en pixel):
Afficher la liste des membres
Pour coloriser votre code, cliquez ici.
Sinon cliquez sur le bouton ci-dessous.
:bow: :cool: :good: :love: ^^
:omg: :fusil: :aie: :argh: :mdr:
:boulet2: :thx: :champ: :whistle: :bounce:
valider
 :)  ;)  :D  :p
 :lol:  8)  :(  :@
 0_0  :oops:  :grr:  :E
 :O  :sry:  :mmm:  :waza:
 :'(  :here:  ^^  >:)

Σ π θ ± α β γ δ Δ σ λ
Veuillez donner la réponse en chiffre
Vous devez activer le Javascript dans votre navigateur pour pouvoir valider ce formulaire.

Si vous n'avez pas volontairement désactivé cette fonctionnalité de votre navigateur, il s'agit probablement d'un bug : contactez l'équipe de Planète Casio.

Planète Casio v42 © créé par Neuronix et Muelsaco 2004 - 2020 | Il y a 29 connectés | Nous contacter | Qui sommes-nous ? | Licences et remerciements

Planète Casio est un site communautaire non affilié à Casio. Toute reproduction de Planète Casio, même partielle, est interdite.
Les programmes et autres publications présentes sur Planète Casio restent la propriété de leurs auteurs et peuvent être soumis à des licences ou copyrights.
CASIO est une marque déposée par CASIO Computer Co., Ltd