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: 513 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: 2149 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
2+2=5
Perdu
Lephenixnoir En ligne Administrateur Points: 18201 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: 513 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: 234 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: 513 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é
Lephenixnoir En ligne Administrateur Points: 18201 Défis: 142 Message

Citer : Posté le 01/06/2020 10:15 | #


Salut ! Donc quelques questions pour situer un peu où tu es et où tu vas maintenant.

Le bug de loader il est corrigé depuis la dernière fois ?
Le multi-processus ça se passe bien du coup ? L'ordonnanceur est-il dans un état suffisant pour l'instant ?
Est-ce que tu as pu régler le problème de performances lié au TTY ?
Pour l'implémentation du tas, tu as une idée particulière de la taille ou la méthode que tu vas utiliser ?
Comme drivers matériels du coup, tu as le T6K11, le KEYSC, le TMU, l'UBC... est-ce que veux/envisages/planifies d'utiliser les ETMU, le DMA, ou les ports séries/USB ?
En parlant d'USB, à quel point est-ce tu penses que la recherche que tu as faite jusqu'à présent s'approche de ce qui est nécessaire pour attaquer un driver ? Est-ce que tu as juste une poignée de registres et c'est loin d'être tout ? Ou à l'inverse, est-ce que tu as la plupart des infos nécessaires ?

Je me doute bien que tu as pas fait tout ça en même temps, c'est surtout pour essayer de suivre !
Yatis Hors ligne Membre Points: 513 Défis: 0 Message

Citer : Posté le 01/06/2020 15:29 | #


C'est gentil de demander des nouvelles du projet
Avant toute chose je tiens à m'excuser du manque de nouvelles / infos à propos de ce projet (pratiquement 3 mois, honte à moi) mais le manque de motivation ainsi que la fin d'année on fait que j'ai avancé le projet en sous-marin.

Si ça peut vous "rassurer" le projet n'est pas à l'abandon car une chose est entrée en jeu entre mon dernier poste sur le topic et aujourd'hui : j'ai proposé Vhex comme projet HUB (projet annexe) à Epitech (je suis étudiant là-bas) pour essayer de valider une partie de mon année avec ( ) et pour me forcer à avoir une deadline. Et comme il a été accepté, je dois rendre quelque chose de correcte, propre et montrable (ce que j'estime n'être absolument pas le cas pour l'instant).
Bref, passons aux questions

Le bug de loader il est corrigé depuis la dernière fois ?

Bien sûr ! Je l'ai corrigé quelques jours après. Le problème venait de ma primitive de lecture pour la SMEM qui faisait n'importe quoi quand j'essayais de lire un fragment de données autre que le premier. Parce qu'il faut savoir que les données d'un fichier sont divisé en blocs éparpillés un peu partout dans la ROM donc il faut faire attention quand on change de fragments si on ne veut pas lire n'importe quoi dans la ROM (l'erreur était vraiment toute simple, j'avais une variable que je mettais à 0 en pleins milieux du code pour aucune raison >_> ).

Le multi-processus ça se passe bien du coup ? L'ordonnanceur est-il dans un état suffisant pour l'instant ?

Le multi-processus ce passe bien pour le moment. Actuellement je suis en train de mettre en place un système pour que les processus puissent supporter plusieurs contextes d'exécution à la fois. Ce mécanisme est indispensable pour que la gestion des signaux puisse être accessible à l'utilisateur (via les syscall signal() et sigaction() (autre que les signaux, il y a atexit() et on exit() qui ont besoin de ce mécanisme)).

En parlant de signaux, une partie y est implémentés est fonctionnels. On peut peut mettre en pause, interrompre et quitter un processus via le TTY (CTRL+Z, CTRL+C, CTRL+D). Aussi, un signal peut être envoyé si une erreur est détectée par le kernel.

Pour ce qui est de l'ordonnanceur, il n'est toujours pas préemptif mais est stable pour l'instant.

D'ailleurs, voila la liste des signaux ainsi que les syscalls implémentées (certains sont en cours d'implémentations):
Cliquez pour découvrir
Cliquez pour recouvrir

Signaux
-= ISO C99 =-
SIGINT (Interactive attention signal)
SIGILL (Illegal instruction)
SIGABRT (Abnormal termination (no core dump generated yet))
SIGSEGV (Invalid access to storage)
SIGTERM (Termination request)

-= Historical signals specified by POSIX =-
SIGTRAP (Trace/breakpoint trap)
SIGKILL (Killed)
SIGBUS (Bus error)
SIGSYS (Bad system call)

-= New(er) POSIX signals =-
SIGSTOP (Stop, unblockable)
SIGTSTP (Keyboard stop)
SIGCONT (Continue)
SIGCHLD (Child terminated or stopped)

Appel système
Manipulation de fichier 
open()
close()
read()
write()
pread()
pwrite()
lseek()
opendir() (not implemented correctly)
readdir() (not implemented correctly)
closedir() (not implemented correctly)

Processus
_exit()
fork_execve() (custom, fork() + execve())
wait()
waitpid()
getpid()
getppid()
setpgid()
getpgid()
chdir() (not implemented correctly)
fchdir() (not implemented correctly)
atexit() (not implemented correctly)
onexit() (not implemented correctly)

Signaux UNIX
signal()
sigaction() (not implemented correctly)
kill() (not implemented correctly)
sigprocmask()
sigreturn()

Thread (en pause pour l'instant)
thread_create()
thread_cancel()
thread_atexit()
thread_on_exit()

Gestion de la mémoire
process_heap_alloc()
process_heap_free()
process_heap_realloc()



Est-ce que tu as pu régler le problème de performances lié au TTY ?

Yep. J'ai pu remplacer une bonne partie des opérations atomiques par des mutex et j'ai pu mettre en place un système qui permet d'afficher le TTY en "parallèle", ce qui permet de sortir du syscall "en avance" et surtout de permettre à l'ordonnanceur d'effectuer son travail en même temps que l'utilisation du TTY.

Pour faire simple, les données à afficher est mis dans un buffer (interne au TTY) qui est vidé / affiché de manière monotone. (16 hz pour être précis donc l'affichage est plus lent MAIS il a l'avantage de fluidifier l'exécution des processus en parallèle).


Pour l'implémentation du tas, tu as une idée particulière de la taille ou la méthode que tu vas utiliser ?

Je me rends vraiment compte que je n'ai plus donné de nouvelles depuis longtemps
Le tas est implémenté et pour être honnête je n'ai aucune idée du nom de l'algorithme que j'ai utilisé même si au départ je voulais implementer les arbres AVL. Mais comme j'avais déjà une gestion du tas dans GladOS j'ai repris mes travaux et refactor le code. Je fais donc ma propre tambouille a ce niveau. Ça va être complexe de résumer car il y a plein de subtilité et surtout 2 niveaux de gestion : L'allocation de page et l'allocation du tas.

Pour l'allocation des pages c'est simple: en premier je regarde combien de RAM je dispose puis, je la sépare en deux parties ("cache" et zone libre). Ensuite c'est simple : J'ai une liste chainée (basé sur le "cache") qui indique les "zones" de pages utilisées. Chaque "nœud" de la liste contient juste le numéro de la première page ainsi que le numéro de la dernière. Donc pour trouver un emplacement libre, je calcule le gap entre deux "nœud" et je prends le premier emplacement libre ("first-fit" donc).

Pour ce qui est de l'allocation du tas c'est un peu plus complexe car je ne peux pas avoir un tas linéaire. Donc chaque processus contient une liste de "zone de mémoire" qui font office de "mini-tas". Une zone peut être de n'importe quelle taille et n'est jamais libérée sauf lorsque le processus en question meurt. À chaque appelle de malloc() (par exemple) je parcours la liste des zones en regardant s'il y a de la place dans l'une d'elles. Si aucune place est trouvée dans la liste, je demande au kernel une zone de la taille indiquée par l'utilisateur (et comme le kernel se base sur la taille des pages, une zone allouée sera probablement un peu plus grande, ce qui permet de stocker potentiellement des choses a la fin d'une zone fraichement allouée).

Ce n'est pas la meilleure façon de faire mais il est complexe de mettre en place quelque chose de propre (surtout impossible de fournir sbrk et brk() proprement) d'où les syscall process_heap_*() dans la liste des syscalls. Je referai cette partie quand j'estimerai que le projet est suffisamment mature pour passer à un stade d'optimisation

Je suis très mauvais en explication, si tu as des questions, n'hésite pas

Comme drivers matériels du coup, tu as le T6K11, le KEYSC, le TMU, l'UBC... est-ce que veux/envisages/planifies d'utiliser les ETMU, le DMA, ou les ports séries/USB

La gestion des drivers a totalement été refait depuis la dernière fois et tout est chargé "à-la volée" en fonction des spécificités hardware et de la version de l'OS de Casio. Je pense effectivement implémenter un driver pour le port séries (qui sera utilisable via les sockets ou par un device dans /dev/scif, je ne sais pas encore) et un driver pour l'USB (un device pour le coup, qui transformera la calculatrice en modem). Pour ce qui est des autres drivers, bien sûr ! Je compte gérer le plus de module hardware possible


En parlant d'USB, à quel point est-ce tu penses que la recherche que tu as faite jusqu'à présent s'approche de ce qui est nécessaire pour attaquer un driver ? Est-ce que tu as juste une poignée de registres et c'est loin d'être tout ? Ou à l'inverse, est-ce que tu as la plupart des infos nécessaires ?

Pour l'USB, j'ai quelques registres documentés et j'arrive à signaler à l'hôte que j'existe sur le bus, ce qui me génère une interruption (0xa20)...et je n'arrive pas à la clear. Les registres ne correspondent (de ce que j'ai pu tester) pas au SH7724. Par exemple, le module ne peut pas jouer le rôle d'hôte (on s'en fout mais ça explique pourquoi certains bits disparaissent).

J'ai fait des tentatives (en décembre dernier) de désassemblage du syscall Comme_open(), des menus "cachés" ainsi que les handlers de Casio mais je n'ai vraiment pas trouvé grand-chose :
* Casio utilise le même buffer de 256 octets utilisé par le SCIF (on ne peut pas utiliser le port 3pins et l'USB en même temps).
* Casio utilise le channel 5 du DMA
* Casio semble faire de l'attente active en scannant fréquemment le VBUS pour savoir si l'utilisateur a branché un câble.
* Le clavier ainsi que la RTC sont utilisés dans USB_open() pour des raisons que j'ignore encore.

De cet échec, j'ai fait un prototype d'auto-débogueur pour tracer le syscall mais je ne pouvais pas allez bien loin (car je déboguais l'OS de Casio....avec l'OS de Casio donc à un moment ça a ses limites ) et du coup je suis reparti de 0, ce qui a donné l'origine de Vhex.

Mais récemment j'ai voulu porter Vhex sur Graph35+II E et Graph90+E mais Casio ayant changé le système de fichiers, il était impossible de loader un exécutable depuis la SMEM sans provoquer de crash et pourtant j'utilisais Bfile_*() (cependant, grâce aux découvertes de Lephe je devrai pouvoir utiliser Bfile de manière stable). Lephe a donc décidé de chercher comment être compatible avec Bfile, moi j'ai faits le choix de déassembler les syscalls de Casio pour faire une primitive de lecture propre. Mais comme c'est INCROYABLEMENT long, il me fallait un traceur rapidement. J'ai donc fait un fork de Vhex et retiré beaucoup de choses (mutli-processus, device, ...) histoire de faire un traceur "simple" (...spoiler: je me suis retrouvé à débuguer du multi-threading qui plantaient).

Ceci dit, j'ai réussi à avoir quelque chose d'assez poussé : je pouvais tracer plusieurs syscall en parallèle, poser des breakpoints ou je voulais, ignorer des instructions, des notes s'affichaient si je détectais une adresse connue (syscall, registre, etc.) et je disposais de plusieurs outils :
* [F1] - désassembleur
* [F2] - analyseur de contexte
* [F3] - hexdump
* [F4] - callgraph
* [F5] - rien
* [F6] - kernel log
Et j'ai pu désassembler avec une grande rapidité 80% du syscall Bfile_OpenFile() de la Graph35+II E (qui est encore PIRE que celui de la Graph35+). Mais malheureusement, non seulement je n'ai pas réussi à comprendre le system de fichier mais en plus je suis tombé sur un bug (venant des threads) assez compliquer à trouver et au bout de quelques jours de recherche, j'ai abandonné le projet pour retourner sur Vhex car le temps commence à me manquer.

Mais cette expérience m'a permis de prendre dû recule sur le projet et voila ce que j'ai en tête: j'aimerais pouvoir faire plusieurs projets autour de Vhex:
* un unikernel: juste pour développer des programmes UNIX avec du multiprocessus et faire un peu de RE trainquiloubilou (ce que je suis en train dev d'actuellement)
* un OS: qui aura pour but de remplacer Casio complètement (GaldOS)
* un débogueur: ce dont j'ai parlé plus haut, un débogueur/traceur pour analyser finement le fonctionnement de l'OS de Casio.

Tout ça, j'aimerais que ce sois basé sur le noyau que je développe actuellement, il faut donc quelque chose d'extrêmement propre, souple et bien architecturer pour pouvoir dev sans se soucier du noyau (exactement comme Gint). Je compte donc détacher le shell ainsi que les programmes de tests de Vhex histoire d'avoir le noyau à poil et pouvoir lui faire lire un fichier de configuration (utilisation du MMU, choix du premier exécutable à lancer, log level, ...). Les projets autours seront comme les différentes distributions de Linux.

On en est pas là et ma confiance en moi est foireuse au possible mais c'est carrément faisable avec mes connaissances actuelles. Reste à savoir comment mettre à jour proprement le noyau en restant indépendant vis-à-vis des projets (en gros j'aimerait que les dev n'ai pas à avoir le code du noyau avec eux dans leurs repos, juste des libs (dynamique si possible))
Lephenixnoir En ligne Administrateur Points: 18201 Défis: 142 Message

Citer : Posté le 02/06/2020 11:41 | #


Le problème venait de ma primitive de lecture pour la SMEM qui faisait n'importe quoi quand j'essayais de lire un fragment de données autre que le premier.

Mais du coup quelle partie du filesystem est-ce que tu maîtrises jusqu'ici ? Ton driver permet de lire les fichiers dans la version homebrew du filesystem qui était dans l'OS 2, c'est ça ? Par opposition à Fugue qui est utilisé sur la Prizm et dans l'OS 3.

Actuellement je suis en train de mettre en place un système pour que les processus puissent supporter plusieurs contextes d'exécution à la fois. Ce mécanisme est indispensable pour que la gestion des signaux puisse être accessible à l'utilisateur (via les syscall signal() et sigaction() (autre que les signaux, il y a atexit() et on exit() qui ont besoin de ce mécanisme)).

Tu ne peux pas juste sauvegarder l'état du processus sur la pile quand tu rentres dans un gestionnaire de signal, pareil que pour le traitement d'une interruption ? Ça n'a pas l'air si différent en termes de fonctionnalités. Ou est-ce qu'il y a des particularités aux signaux qui t'empêchent de faire ça ?

D'ailleurs, voila la liste des signaux ainsi que les syscalls implémentées (certains sont en cours d'implémentations):

Impressionnant ! On peut déjà en écrire des applis sympa avec ça.

Pour faire simple, les données à afficher est mis dans un buffer (interne au TTY) qui est vidé / affiché de manière monotone. (16 hz pour être précis donc l'affichage est plus lent MAIS il a l'avantage de fluidifier l'exécution des processus en parallèle).

Ah, parfait ! Une sorte de synchronisation avec le framerate de l'écran (qu'on suppose être 16 Hz vu que plus vite ça ne se voit pas). Parfait donc, bien joué.

Pour l'allocation des pages c'est simple: en premier je regarde combien de RAM je dispose puis, je la sépare en deux parties ("cache" et zone libre). Ensuite c'est simple : J'ai une liste chainée (basé sur le "cache") qui indique les "zones" de pages utilisées. Chaque "nœud" de la liste contient juste le numéro de la première page ainsi que le numéro de la dernière. Donc pour trouver un emplacement libre, je calcule le gap entre deux "nœud" et je prends le premier emplacement libre ("first-fit" donc).

Hmm un first fit pour un allocateur de pages, surprenant mais pourquoi pas. Donc là sauf erreur de ma part les pages que tu alloues sont en accès direct (sans MMU), c'est pour ça que tu dois trouver des zones aussi grandes que ce qu'on demande ? Parce que si tu alloues qu'une seule page à la fois, ou disposes du MMU pour donner à volonté une illusion de continuité, c'est plus courant de juste garder l'ensemble des pages libres et donner la première à chaque fois.

Pour ce qui est de l'allocation du tas c'est un peu plus complexe car je ne peux pas avoir un tas linéaire.

Ça c'est vraiment la dèche ha ha. Pourquoi est-ce que tu ne libères pas les zones si tout ce qui est dedans est free() ? Ça me paraît être une mauvaise idée de les garder parce que ça monopolise de la mémoire pour rien (et les arguments plus élaborés comme la fragmentation que tu as si tu libères et réalloues trop souvent les zones ont besoin de chiffres pour être crédibles).

Quelle est la taille typique d'une zone et comment cherches-tu un espace libre dans une zone ? C'est un first-fit aussi ?

Je pense effectivement implémenter un driver pour le port séries (qui sera utilisable via les sockets ou par un device dans /dev/scif, je ne sais pas encore) et un driver pour l'USB (un device pour le coup, qui transformera la calculatrice en modem). Pour ce qui est des autres drivers, bien sûr ! Je compte gérer le plus de module hardware possible

Ah ouais, pas mal ! Si j'implémente un driver USB dans gint ce sera très certainement sur ta doc/ton code donc j'attends avec impatience de voir ce qui va se passer sur ce front. En attendant, je ferai sans doute un add-in de transfert avec fxlib parce que la Graph 35+E II et la Graph 90+E ont un workflow assez lent avec le SCSI. On verra ce qui se présente...

Pour l'USB, j'ai quelques registres documentés et j'arrive à signaler à l'hôte que j'existe sur le bus, ce qui me génère une interruption (0xa20)...et je n'arrive pas à la clear. Les registres ne correspondent (de ce que j'ai pu tester) pas au SH7724. Par exemple, le module ne peut pas jouer le rôle d'hôte (on s'en fout mais ça explique pourquoi certains bits disparaissent).

Hmm, ok, je vois un peu l'idée. Il doit y avoir pas mal d'exploration à faire. J'envisage d'écrire un "détecteur de registres" même si c'est un peu risqué.

* Casio utilise le même buffer de 256 octets utilisé par le SCIF (on ne peut pas utiliser le port 3pins et l'USB en même temps).
* Casio utilise le channel 5 du DMA
* Casio semble faire de l'attente active en scannant fréquemment le VBUS pour savoir si l'utilisateur a branché un câble.
* Le clavier ainsi que la RTC sont utilisés dans USB_open() pour des raisons que j'ignore encore.

Hmm, oui ok, rien de totalement inattendu, mais c'est bon à savoir. Merci pour tout ça ! Les bouts désassemblés sont documentés/uploadés sur la bible ?

(cependant, grâce aux découvertes de Lephe je devrai pouvoir utiliser Bfile de manière stable).

Attention, moi je le fais pendant que le matériel es contrôlé par fxlib, et je me suis occupé uniquement de comment reprendre proprement le contrôle après coup sans interrompre BFile. C'est un début, mais ça ne suffit probablement pas (?)

Lephe a donc décidé de chercher comment être compatible avec Bfile, moi j'ai faits le choix de déassembler les syscalls de Casio pour faire une primitive de lecture propre.

En vrai BFile c'est surtout un truc pour remplir les trous à défaut de mieux. Dans la mesure où il y a deux FS différents et pas encore de primitives d'écriture, je suis obligé de faire des compromis si je veux que les jeux développés avec gint puissent avoir ne serait-ce que des sauvegardes. x_x

Ceci dit, j'ai réussi à avoir quelque chose d'assez poussé : je pouvais tracer plusieurs syscall en parallèle, poser des breakpoints ou je voulais, ignorer des instructions, des notes s'affichaient si je détectais une adresse connue (syscall, registre, etc.) et je disposais de plusieurs outils :
* [F1] - désassembleur
* [F2] - analyseur de contexte
* [F3] - hexdump
* [F4] - callgraph
* [F5] - rien
* [F6] - kernel log

Impressionnant ! Le g1a est quelque part ? Des instructions d'utilisation ?

* un unikernel: juste pour développer des programmes UNIX avec du multiprocessus et faire un peu de RE trainquiloubilou (ce que je suis en train dev d'actuellement)
* un OS: qui aura pour but de remplacer Casio complètement (GaldOS)
* un débogueur: ce dont j'ai parlé plus haut, un débogueur/traceur pour analyser finement le fonctionnement de l'OS de Casio.

Bien résumé ! Donc si je suis bien, le débogueur c'est un programme qui embarque l'unikernel un peu comme gintctl embarque gint ?

On en est pas là et ma confiance en moi est foireuse au possible mais c'est carrément faisable avec mes connaissances actuelles.

En attendant t'es encore là et ça avance toujours donc tout ça n'est pas perdu !
Yatis Hors ligne Membre Points: 513 Défis: 0 Message

Citer : Posté le 02/06/2020 17:41 | # | Fichier joint


Mais du coup quelle partie du filesystem est-ce que tu maîtrises jusqu'ici ? Ton driver permet de lire les fichiers dans la version homebrew du filesystem qui était dans l'OS 2, c'est ça ? Par opposition à Fugue qui est utilisé sur la Prizm et dans l'OS 3.

J'arrive à lire des fichiers de la SMEM avec mes propres primitifs, à aucun moment j'utilise Bfile. Pour ce qui est de Fugue (Graph35+II et Graph90), j'ai fait de gros wrappers autour des Bfile_*(). Seulement je crash pour aucune raison au bout de la deuxième lecture de fichier (donc le shell se charge mais pas les exécutables pour l'instant).

J'ai donc tracé le syscall Bfile_OpenFile() dans l'espoir de me passer de Fugue et d'avoir quelque chose de "propre" pour accéder a la SMEM. C'est toujours en cours de RE et c'est loin d'être finis car Fugue est exclusivement en RAM, ce qui n'était pas le cas avant. Donc je dois me taper des fonctions immenses (vraiment) sur des structures non documentées en RAM. C'est long. Ceci-dit, je pensais tracer une des primitives d'écriture en ROM, ce qui forcera Fugue à se "recharger en RAM" et me permettra de desceller le fonctionnement de ce truc.

D'ailleurs, je viens de checker les messages d'erreur "cachés" dans l'OS de la Graph35+ et il se trouve que ce sont exactement les mêmes que Fugue donc il n’est pas impossible que depuis le début Casio utilise Fugue. Ça expliquerait pourquoi je suis tombé plusieurs fois sur des adresses connues comme 0xa0270000 quand je recherchais des endroits de l'OS ou Caiso écrits en EEPROM sur la Graph35+IIE.

Tu ne peux pas juste sauvegarder l'état du processus sur la pile quand tu rentres dans un gestionnaire de signal, pareil que pour le traitement d'une interruption ? Ça n'a pas l'air si différent en termes de fonctionnalités. Ou est-ce qu'il y a des particularités aux signaux qui t'empêchent de faire ça ?

Eh bien figures-toi que je me suis fait la même réflexion dans la journée et oui tu as raison. Je ne sais pas pourquoi j'ai voulu faire autrement xD Au passage, j'en profite pour dire que j'ai implémenté correctement signal() et sigreturn() et j'ai refait l'ordonnanceur (il n’est pas toujours pas préemptif mais bien plus "propre" en termes d'architecture).

Hmm un first fit pour un allocateur de pages, surprenant mais pourquoi pas. Donc là sauf erreur de ma part les pages que tu alloues sont en accès direct (sans MMU), c'est pour ça que tu dois trouver des zones aussi grandes que ce qu'on demande ? Parce que si tu alloues qu'une seule page à la fois, ou disposes du MMU pour donner à volonté une illusion de continuité, c'est plus courant de juste garder l'ensemble des pages libres et donner la première à chaque fois.

C'est ce que je faisais avec GladOS mais dans Vhex je ne touche pas au MMU parce qu'a la base, je voulais faire de ce noyau un environnement de développement et de RE sur l'OS de Casio donc ça implique de ne pas toucher au MMU. Donc oui, je suis obligé d'allouer des zones entières.

Pourquoi est-ce que tu ne libères pas les zones si tout ce qui est dedans est free() ? Ça me paraît être une mauvaise idée de les garder parce que ça monopolise de la mémoire pour rien (et les arguments plus élaborés comme la fragmentation que tu as si tu libères et réalloues trop souvent les zones ont besoin de chiffres pour être crédibles).

Je pars du principe que si l'utilisateur a demandé une grosse taille et qu'il la libère, il risque de la redemander. Donc pour éviter de grosse opérations pour la gestion de la mémoire et de bloquer le processus, je préfère ne pas relacher les zones allouées (pour rappel aucun signal n'est pris en compte tant que l'utilisateur est dans un appel système).

Quelle est la taille typique d'une zone et comment cherches-tu un espace libre dans une zone ? C'est un first-fit aussi ?

Toutes les recherches sont en first fit pour l'instant car ce n'est pas ma priorité d'avoir une gestion de la mémoire ultra sophistiquer. Mais le jour ou le noyau devient mature, je referai cette partie.

Ah ouais, pas mal ! Si j'implémente un driver USB dans gint ce sera très certainement sur ta doc/ton code donc j'attends avec impatience de voir ce qui va se passer sur ce front.

J'ai peur qu'il se passe encore quelque temps avant que je me penche dessus. Voilà en gros ma "TODO list" :
* 1) Réorganiser le projet (car certaines parties ne me plaisent pas)
* 2) Implémentation correcte des signaux.
* 3) Portage sur Graph35+IIE (en partie faite, il manque plus qu'à tester le Bfile)
* 4) Portage sur Graph90 (Il faut revoir beaucoup de choses, notamment le dessin et l'organisation du repo)
* 5) Isoler complètement le noyau des librairies.
* 5) Reprendre le prototype de traceur que j'ai commencé (et qui est cassé actuellement)
* 6) Faire des primitives propres pour Fugue.
* 7) Faire un driver USB.
* 8) Faire un driver pour l'écriture en EEPROM (Bfile ?)
Donc il reste encore beaucoup de choses à faire. Je pense pouvoir atteindre le point 3 cette semaine mais le reste risque de prendre beaucoup plus de temps.

J'envisage d'écrire un "détecteur de registres" même si c'est un peu risqué.

Comment ça ? (si c'est ce que je pense, c'est plus que risqué xD)

Les bouts désassemblés sont documentés/uploadés sur la bible ?

J'ai la mauvaise habitude de ne rien documenter nulle part d'autre que dans le fichier ou je désassemble. Donc si je peux mettre sur la Bible les OS que j'ai commencé à désassembler, en accès libre je suis partant mais il y aura potentiellement des zones "sensibles". À voir si c'est vraiment gênant.

Attention, moi je le fais pendant que le matériel es contrôlé par fxlib, et je me suis occupé uniquement de comment reprendre proprement le contrôle après coup sans interrompre BFile.

C'est exactement ce que je fais de mon côté aussi. Quand je monte le système de fichiers de Casio (que j'ai appelé smemfs), j'utilise Bfile_Find*() pour faire un dump complet de stockage (probablement ce que fait Fugue d'ailleurs). Le reste des primitives (open, read et close) sont des wrappers autour des Bfile_*().

Impressionnant ! Le g1a est quelque part ? Des instructions d'utilisation ?

Malheureusement non, j'ai la fâcheuse tendance à commit uniquement quand quelque chose est stable. Du coup, j'ai cassé le repo sans le vouloir. Mais j'ai un proto (fichier joint, il est compatible avec toutes les graph mono) qui est vaguement fonctionnelle sauf que le retour menu provoque une corruption de mémoire et certaine opcodes sont mal traduitent (malin). D'ailleurs, j'ai appris que récemment (par toi) que Casio a eu l'idée du siècle en mappant l'adresse 0 (NULL pour les intimes) vers le début de la ROM. Du coup je peux faire un truc du style:

uint32_t tab = NULL;
uint32_t a = tab[3];

Sans aucune exception. C'est qui est honteux et très fourbe car je me suis rendu compte que certaine variable n'était pas initialisée correctement durant le boot du noyau. Bref c'est d'une tristesse absolue cette OS de Casio.
Utilisation / notes pour le traceur
Cliquer pour enrouler

Le tracer à un seul but ici, tracer le syscall Bfile_OpenFile().
Pour revenir au menu c'est la touche [MENU] mais il y a de forte chances pour que ça provoque de la corruption de mémoire on reste bloqué dans le traceur (malin) du coup il faut mieux reset pour sortir du truc >_<

[F1] - menu désassembleur
* [UP] / [DOWN] scrolle.
* [-] - déplace le prochain break point vers le haut.
* [+] - déplace le prochain break point vers le bas.
* [EXE] continuer l'exécution du programme jusqu'au prochain break point.
* [OTN] force le programme à reprendre la ou le prochain break point a lieu (indispensable pour skip les instructions qui bloquent les interruptions ! Si SR.BL est mis à 1, les breaks ne point seront ignorés !)

[F2] - menu contexte et [F3] - hexdump
* [UP] / [DOWN] scrolle
* [LEFT/RIGHT] scrolle


Donc si je suis bien, le débogueur c'est un programme qui embarque l'unikernel un peu comme gintctl embarque gint ?

Ouais mais sur le long terme, les développeurs auront des libraires dynamiques pour communiquer avec le noyau et ils pourront créer un fichier de configuration (*.vhex?) pour choisir :
I] l'architecture du noyau:
* unikernel: Il s'installe en RAM et ne rentre en aucun cas en conflit avec Casio.
* kernel: Il prend le contrôle complet de la machine (donc pas de retour menu possible).
* OS-receiver (par défaut(?), si aucun fichier de config n'est trouvé): permettant de flasher (via l'USB) l'OS de Casio ou GladOS quand il sera disponible.
II] Les différents modules du kernel à charger au démarrage (ordonnanceur, thread, processus, VFS, ...)
III] Le nom du projet
IV] Le log level
V] Le choix du device pour écrire les logs du kernel (TTY, USB, series?)

Et je pense offrir la possibilité d'avoir du multi-boot (une sorte de Grub) pour pouvoir choisir quoi lancer avec le noyau *-*

Bref, on en est vraiment pas à ce point là d'avancement mais ça me semble faisable (il y a beaucoup de choses à prendre en compte encore mais voila l'idée que je me fais du noyau), à voir à l'avenir
Lephenixnoir En ligne Administrateur Points: 18201 Défis: 142 Message

Citer : Posté le 02/06/2020 19:26 | #


Donc je dois me taper des fonctions immenses (vraiment) sur des structures non documentées en RAM. C'est long.

Mais on sait que c'est du FAT 12/16/32 ou du VFAT. Pourquoi ne pas simplement commencer par là ? Ça m'étonnerait que tu puisses pas chercher dans la RAM des entrées de la table des fichiers.

Ceci-dit, je pensais tracer une des primitives d'écriture en ROM, ce qui forcera Fugue à se "recharger en RAM" et me permettra de desceller le fonctionnement de ce truc.

On en a déjà parlé, mais je vois pas pourquoi tu veux absolument que tout se passe en ROM. Tu as de toute façon besoin de deux implémentations du système de fichier pour l'ancien FS et le nouveau, alors si la table des fichiers est en RAM pourquoi s'emmerder avec des acrobaties qui ne servent à rien ? :x

C'est ce que je faisais avec GladOS mais dans Vhex je ne touche pas au MMU parce qu'a la base, je voulais faire de ce noyau un environnement de développement et de RE sur l'OS de Casio donc ça implique de ne pas toucher au MMU.

Et comment tu conçois ton interaction avec l'OS ? Parce qu'autant dans gint je peux juste prendre le contrôle du matériel et le rendre quand ça m'arrange, autant là avec le débogueur tu as besoin d'exécuter du code de l'OS en même temps que le kernel. Quel est le modèle ?

Je pars du principe que si l'utilisateur a demandé une grosse taille et qu'il la libère, il risque de la redemander. Donc pour éviter de grosse opérations pour la gestion de la mémoire et de bloquer le processus, je préfère ne pas relacher la mémoire (pour rappel aucun signal n'est pris en compte tant que l'utilisateur est dans un appel système).

Je te laisserai faire les expérimentations si l'envie t'en prend, mais tu vas surtout perdre beaucoup de mémoire ! Perso j'y crois pas trop. Je pense que c'est plus intelligent de rendre la zone, et le processus pourra décider tout seul de ne pas free() s'il considère qu'il en aura besoin de nouveau plus tard. Comme ça il peut agir en connaissance de cause selon la taille de la zone, etc. Imagine un vecteur qui ne fait que grandir, chaque zone libérée serait inutilisable donc conservée pour rien.

Toutes les recherches sont en first fit pour l'instant car ce n'est pas ma priorité d'avoir une gestion de la mémoire ultra sophistiquer. Mais le jour ou le noyau devient mature, je referai cette partie.

C'est honnête. Et du coup c'est quoi la taille moyenne d'une zone ? :3

* 4) Portage sur Graph90 (Il faut revoir beaucoup de choses, notamment le dessin et l'organisation du repo)

Bon courage, moi j'ai repris gint de quasiment zéro et ça m'a pris trèès longtemps, sans doute plus que nécessaire. Je te conseille pas de faire pareil.

C'est une sacrée liste, hésite pas à poster des updates ici. Y'a deux points 5 aussi.

Comment ça ? (si c'est ce que je pense, c'est plus que risqué xD)

Première passe, tu cherches des adresses qui ne renvoient pas la même valeur selon la taille de l'accès. Ça détectera tous les registres dont au moins un des bits est égal à 1 à ce moment-là (parce que s'il n'y a pas de registre ça renvoie 0 en général), donc... probablement la majorité. Deuxième passe, tu cherches dans les trous, tu cherches des modules SH7*** qui ont un agencement relatif similaire, des occurrences dans le code de l'OS, etc. Troisième passe, si tu as un match avec un module, on peut tenter d'écrire dans les registres pour faire des tests préliminaires pour un driver.

J'ai la mauvaise habitude de ne rien documenter nulle part d'autre que dans le fichier ou je désassemble. Donc si je peux mettre sur la Bible les OS que j'ai commencé à désassembler, en accès libre je suis partant mais il y aura potentiellement des zones "sensibles". À voir si c'est vraiment gênant.

J'ai uploadé un petit nombre de fichiers désassemblés déjà. Assure-toi juste qu'il n'y a rien d'explicitement sensible (ou tu fais semblant de pas savoir ce que c'est).

C'est exactement ce que je fais de mon côté aussi. Quand je monte le système de fichiers de Casio (que j'ai appelé smemfs), j'utilise Bfile_Find*() pour faire un dump complet de stockage (probablement ce que fait Fugue d'ailleurs). Le reste des primitives (open, read et close) sont des wrappers autour des Bfile_*().

Mais du coup ton kernel contrôle le matériel donc c'est pas ce que je fais du tout... right? Ou alors tu décharges posément les drivers pendant que tu montes le fs et tu n'en as jamais parlé ?

Ouais mais sur le long terme, les développeurs auront des libraires dynamiques pour communiquer avec le noyau

Et le noyau il est chargé quand ? Parce que c'est bien malin d'avoir une interface mais il faut que le truc soit lancé. Et en quoi est-ce que ça diffère des syscalls, qui sont la méthode canonique de communication userspace/noyau ?

* unikernel: Il s'installe en RAM et ne rentre en aucun cas en conflit avec Casio.

Soit dit en passant la notion d'unikernel englobe tous les noyaux qui sont sous formes de libs et intégrés directement dans l'exécutable, donc selon les libs partagées que tu auras le terme sera pas forcément approprié.

Et je pense offrir la possibilité d'avoir du multi-boot (une sorte de Grub) pour pouvoir choisir quoi lancer avec le noyau *-*

Euh si c'est le noyau qui lance des exécutables c'est juste un userspace. Un multiboot ce serait si t'as plusieurs noyaux !
Yatis Hors ligne Membre Points: 513 Défis: 0 Message

Citer : Posté le 02/06/2020 23:24 | #


Mais on sait que c'est du FAT 12/16/32 ou du VFAT. Pourquoi ne pas simplement commencer par là ? Ça m'étonnerait que tu puisses pas chercher dans la RAM des entrées de la table des fichiers.
[...]
On en a déjà parlé, mais je vois pas pourquoi tu veux absolument que tout se passe en ROM. Tu as de toute façon besoin de deux implémentations du système de fichier pour l'ancien FS et le nouveau, alors si la table des fichiers est en RAM pourquoi s'emmerder avec des acrobaties qui ne servent à rien ? :x

J'ai déjà fait des recherches en RAM et je n'ai absolument rien trouvé. C'est vraiment frustrant x)

Je ne veux pas forcément que tout se passe en ROM. Je veux juste comprendre ou et comment le FS est organisé en ROM pour pouvoir y accéder proprement. Parce que, crois-moi, Fugue + Bfile c'est d'une lenteur affolante en plus d'être incroyablement lourd et dangereux avec tous les buffers utilisés par Casio. Et on ne peut pas utiliser l'API de Fugue car justement il est en RAM donc pas à une adresse fixe donc impossible de communiquer avec lui "proprement". Encore une fois, mon objectif est de me débarrasser définitivement de Fugue. Et je pense avoir quelque piste sur l'organisation du nouveau FS mais je n'ai pas vraiment le temps de faire des recherches plus avancé maintenant.

Mais du coup ton kernel contrôle le matériel donc c'est pas ce que je fais du tout... right? Ou alors tu décharges posément les drivers pendant que tu montes le fs et tu n'en as jamais parlé ?

Ah ! Oui j'ai oublié de dire que je décharge les drivers et restaure tout l'environnement de Casio avant d'utiliser quoique ce sois qui a un lien avec l'OS

Et le noyau il est chargé quand ? Parce que c'est bien malin d'avoir une interface mais il faut que le truc soit lancé. Et en quoi est-ce que ça diffère des syscalls, qui sont la méthode canonique de communication userspace/noyau ?
[...]
Euh si c'est le noyau qui lance des exécutables c'est juste un userspace. Un multiboot ce serait si t'as plusieurs noyaux !

Actuellement, le noyau est un addin g1a / g3a. Lorsqu'il démarre il charge tous les drivers possibles en fonction de l'OS et du MPU (clavier, power, cpg, intc, vbr, ubc, tmu, ecran, keysc) ainsi que ses "modules" (mémoire, ordonnanceur, VFS, devices, processus). Ensuite il essaie de créer le premier processus (qui n'est pas init() mais directement le shell (qui se trouve dans /mnt/casio/VHEX/shell.elf)). Puis roulez jeunesse.

Mais par la suite j'aimerais que le g1a/g3a devienne un BIOS. Et là j'hésite. Sois il se base sur les syscalls de Casio (le plus simple) ou alors il chargera le noyau avec le strict minimum (vbr + driver clavier/écran) et disposera de ses propres primitifs de lecture pour la SMEM. Ensuite il cherchera dans la SMEM tous les fichiers de configuration avec une extension bien précise (*.vhex par exemple) qui lui dicteront comment charger le reste du noyau. Si plusieurs fichiers sont détectés, alors il y aura un menu pour choisir quelle configuration on veut (comme un Grub. Les deux programmes seront au sein du même addin).

Mais du coup j'ai le même problème qu'avec GladOS: ça implique que le noyau doit pouvoir être relocalisé (je commence à comprendre l'intérêt de GNU/Hurd, je me tâte à partir sur un truc comme ça ). Le plus simple serait d'avoir un addin qui fait office de BIOS/Grub et le noyau (un fichier ELF certainement) dans la SMEM qui sera chargée dynamiquement en RAM en fonction de la config choisie. Mais comme je suis têtu, j'aimerais avoir qu'un seul g1a (pour des raisons pratiques). Donc ça impliquerait de pouvoir relocaliser ou on veut (ASLR?) du code venant d'un addin vers la RAM, il faudra faire des tests pour voir comment faire ça proprement car je ne suis pas sûr que ce sois faisable facilement (x_x)

Et comment tu conçois ton interaction avec l'OS ? Parce qu'autant dans gint je peux juste prendre le contrôle du matériel et le rendre quand ça m'arrange, autant là avec le débogueur tu as besoin d'exécuter du code de l'OS en même temps que le kernel. Quel est le modèle ?

En fait pour être plus précis, quand je trace un syscall, deux threads sont créés : un qui se contente d'exécuter le syscall et un autre qui exécute uniquement l'handler de l'UBC donc tout est isolé. (@note: il n'y a pas d'ordonnanceur dans le débogueur, pour changer de contexte j'utilise un syscall dédié. C'est d'ailleurs pour ça que j'aimerais pouvoir spécifier les modules du noyau à charger)

Une fois le premier break point posé, je décharge mes drivers et je change de contexte pour passer sur le thread qui va exécuter le syscall de Casio. Lorsqu'un break point est atteint une interruption à lieu SAUF où'au lieu d'utiliser la VBR, l'UBC utilise le registre DBR (qui joue le même rôle que la VBR. On peu choisir entre VBR/DBR en configurant l'UBC correctement). Ce qui me permet de brancher dans mon kernel uniquement avec un Break Point Après il me reste juste à sauvegarder le contexte actuel (thread syscall), charger le contexte du thread qui exécute l'handler (thread UBC) puis je recharge les drivers et PAF ça fonctionne.

Bon courage, moi j'ai repris gint de quasiment zéro et ça m'a pris trèès longtemps, sans doute plus que nécessaire. Je te conseille pas de faire pareil.

En théorie je n'ai aucun problème à porter le noyau sur la Graph90 (bon, il faut que fasse le driver DMA, quelque ajustement pour le TTY (pour la couleur) et il faut que je revois l'organisation générale du projet pour générer un g1a et un g3a) mais en matière de code je ne devrai pas avoir de problème d'architecture.

Et du coup c'est quoi la taille moyenne d'une zone ? :3

Une page fait 1ko et le malloc() demande des zones qui sont tronquées "((size + 1023) / 1024) * 1024 " histoire de pouvoir demander au kernel un nombre précis de pages "qui se suivent". Ensuite je les ajoute au processus (elles sont cachées). Quand il meure toutes les pages sont relâché "manuellement" par le noyau. Comme ça je suis sûr que la mémoire est complément libéré.

Soit dit en passant la notion d'unikernel englobe tous les noyaux qui sont sous formes de libs et intégrés directement dans l'exécutable, donc selon les libs partagées que tu auras le terme sera pas forcément approprié.

Sérieux ? Moi je pensais vraiment qu'un unikernel désignait un noyau qui partage le même espace mémoire que les processus (donc pas de gestion de mémoire virtuelle). Mince. Mais du coup ça s'appelle comment ce que je suis en train de faire ? xD
Lephenixnoir En ligne Administrateur Points: 18201 Défis: 142 Message

Citer : Posté le 03/06/2020 08:54 | #


Je veux juste comprendre ou et comment le FS est organisé en ROM pour pouvoir y accéder proprement. Parce que, crois-moi, Fugue + Bfile c'est d'une lenteur affolante en plus d'être incroyablement lourd et dangereux avec tous les buffers utilisés par Casio.

Ça d'accord, mais la lenteur c'est pas parce qu'il y a une partie en RAM. Déjà c'était déjà lent avant. Et en plus parcourir une table dans la RAM ça va sensiblement plus vite que faire pareil dans la ROM (à mitiger avec l'effet du cache). Personnellement je n'ai encore vu aucune explication crédible de la lenteur de BFile, donc il me paraît être bien trop tôt pour décider comment il faut faire pour aller vite.

Et on ne peut pas utiliser l'API de Fugue car justement il est en RAM donc pas à une adresse fixe donc impossible de communiquer avec lui "proprement".

Qui a dit que le code de Fugue était dans la RAM ? Y'a pas la moindre raison, et t'as jamais mentionné ça avant.

Encore une fois, mon objectif est de me débarrasser définitivement de Fugue. Et je pense avoir quelque piste sur l'organisation du nouveau FS mais je n'ai pas vraiment le temps de faire des recherches plus avancé maintenant.

Ça peut pas être aussi simple que "se débarrasser définitivement du fs en place". Soit tu te moques de préserver fs codé par Casio et alors t'as aucune raison de le rétro-analyser puisqu'il te suffit de mettre de l'ext à la place dans la ROM. Soit tu veux le préserver et alors tu pourras pas éviter de travailler avec/pour Fugue parce que la cohérence de ce fs dépend de structures de données de Fugue. Même si c'est du FAT standard, si Fugue maintient la table des fichiers dans la RAM, tu seras bien obligé de faire pareil. L'histoire de "Fugue relit peut-être la table dans la ROM" c'est une idée complètement infondée et non vérifiée que j'ai émise pour voir si charger la table des fichiers en RAM restait viable même en présence de crash/reset. Y'a zéro garantie que ça se fait, et surtout il y aura d'autres problèmes ailleurs. Tu peux pas cohabiter avec le code existant et l'ignorer complètement.

Ah ! Oui j'ai oublié de dire que je décharge les drivers et restaure tout l'environnement de Casio avant d'utiliser quoique ce sois qui a un lien avec l'OS

Ah c'est donc bien le truc que j'ai galéré à faire marcher à trois reprises. Attention aux ETMU, au DMA et aux TLB, même si maintenant tu le sais...

Mais par la suite j'aimerais que le g1a/g3a devienne un BIOS.

Donc plus l approche unikernel. Quel est l'avantage de ça ? Qu'est-ce qu'on y gagne par rapport à juste intégrer le noyau dans les add-ins ?

Mais comme je suis têtu, j'aimerais avoir qu'un seul g1a (pour des raisons pratiques).

Tu peux intégrer l'ELF du noyau dans le g1a du bootloader mais ça casse tout l'intérêt de séparer les deux.

(ASLR?)

Ça a pas besoin d'être aléatoire, l'ASLR est un patch pour des failles de sécurité dans lesquelles tu peux modifier/tricher avec le code des programmes si tu sais à quelle adresse ils sont chargés. Vois Return-to-libc par exemple.

Une fois le premier break point posé, je décharge mes drivers et je change de contexte pour passer sur le thread qui va exécuter le syscall de Casio.

Donc tu changes le contrôle de tout le matériel (UBC exclus) en permanence. Jolie acrobatie ! J'ai tellement galéré à rendre ça stable dans gint que je me demande comment ça peut paraître si facile quand c'est toi qui le racontes.

Une page fait 1ko et le malloc() demande des zones qui sont tronquées "((size + 1023) / 1024) * 1024 " histoire de pouvoir demander au kernel un nombre précis de pages "qui se suivent".

Ok, ça a l'air raisonnable. Au passage ça s'écrit aussi (size + 1023) & ~1023 cette chose (mais tu le sais certainement déjà >_>).

Ajouté le 03/06/2020 à 08:55 :
Sérieux ? Moi je pensais vraiment qu'un unikernel désignait un noyau qui partage le même espace mémoire que les processus (donc pas de gestion de mémoire virtuelle). Mince. Mais du coup ça s'appelle comment ce que je suis en train de faire ? xD

C'est plus un kernel monolithique du coup. Que tu te fasses chain-load de façon compliquée par un bootloader caché dans un g1a lancé par CasioWin n'y change pas grand-chose.
Yatis Hors ligne Membre Points: 513 Défis: 0 Message

Citer : Posté le 03/06/2020 12:04 | #


Ça d'accord, mais la lenteur c'est pas parce qu'il y a une partie en RAM. Déjà c'était déjà lent avant. Et en plus parcourir une table dans la RAM ça va sensiblement plus vite que faire pareil dans la ROM (à mitiger avec l'effet du cache). Personnellement je n'ai encore vu aucune explication crédible de la lenteur de BFile, donc il me paraît être bien trop tôt pour décider comment il faut faire pour aller vite.

Hoo non la lenteur vient du fait que Fugue ne se base pas sur des caractères étendu (uint16_t) pour stocker le path mais sur des chars classiques (uint8_t). Donc le path qu'on envoie au Bfile et traité une première fois par Casio puis envoyer à Fugue qui le check à son tour (au passage il "s'auto-check lui-même"). Ensuite Fugue vérifie que le path contient de caractère valide (ASCII et SHIFT-JIS) et le converti comme ça : "\\fls0\vhex.g1a" -> "\vhex.g1a". Casio repend la main et reconvertit le path de Fugue en uint16_t, vérifie si l'OS accepte les caractères étendu (en fonction de l'OS qu'on utilise), check si le path est valide (taille des noms fichiers + type de caractère + taille de l'extension (s’il en détecte une) + noms spéciaux, genre . et .., etc, etc, ...). Il finit par compter combien de niveau il y a dans le path (ex: "\VHEX\shell.elf" = 2 niveaux).

Ensuite il récupère le dernier fichier du path ("\VHEX\shell.elf" -> "shell.elf"), retire les espaces (uniquement ceux qui se trouvent devant le nom) et regarde s’il y a une extension (si oui, il regarde si l'extension est connue) puis demande à Fugue de trouver le fichier. Une fois trouvé il regarde si le fichier a un parent. Si oui alors Fugue + Casio réanalysent le path, récupère le nom du fichier parent ("\VHEX\shell.elf" -> "VHEX") et font une recherche avec Fugue, s’il est trouvé il regarde s’il est lié avec le premier fichier et s'il y a encore un parent, ils recommencent à analyser le path.

Les manipulations avec le path sont extrêmement lentes car Casio reconvertie le path super souvent et (Fugue/Casio) vérifient la validité *complète* du path à chaque fois. Et là, tu as seulement 60% de ce qu'il se passe avec Bfile_OpenFile() car il y a des parties que je ne n'ai pas encore réussie à déchiffrer.

Qui a dit que le code de Fugue était dans la RAM ? Y'a pas la moindre raison, et t'as jamais mentionné ça avant.

Une grosse partie de Fugue est en RAM et aucun syscall n'est appelé dans Bfile_OpenFile() faisant référence à ce truc. Donc on ne peut pas l'utiliser via des syscalls et les structures en RAM ne sont pas fixes.

Soit tu veux le préserver et alors tu pourras pas éviter de travailler avec/pour Fugue parce que la cohérence de ce fs dépend de structures de données de Fugue.
[...]
Tu peux pas cohabiter avec le code existant et l'ignorer complètement.

Je veux seulement pouvoir y accéder en lecture donc si, je peux largement me passer de Fugue. Le jour où je fais de l'écriture, oui, il faudra que je trouve un moyen pour être compatible avec Fugue (ou simplement trouver le moyen de le forcer à se régénérer en RAM, si c'est possible).

Donc plus l approche unikernel. Quel est l'avantage de ça ? Qu'est-ce qu'on y gagne par rapport à juste intégrer le noyau dans les add-ins ?

Ça permettrait de supporter et choisir entre plusieurs configurations du noyau et de le charger dynamiquement en RAM par "modules" au lieu de le charger complètement à chaque fois. (Le noyau serait un peu comme une librairie dynamique en gros). De plus, si le BIOS/Gurb (je ne suis pas sûr du nom) devient assez élaborer, on peut imaginer pouvoir en faire une interface pour des transferts de fichiers et / ou un moyen de flasher des OS customs.

Donc si je résume mon idée, il y aura 3 projets distincts : le BIOS/Grub (pas sûr que ce sois les meilleurs termes pour ce truc), le noyau (reste encore à définir la forme qu'il prendra) et les librairies (dynamique si possible)

Tu peux intégrer l'ELF du noyau dans le g1a du bootloader mais ça casse tout l'intérêt de séparer les deux

Oui tu as raison. C'est très con comme idée dit comme ça xD (Je pensais plus à l'aspect pratique d'avoir qu'un seul addin à installer au lieu d'un g1a et d'un ELF. Mais ça casse l'intérêt du projet, malin )

je me demande comment ça peut paraître si facile quand c'est toi qui le racontes.

Il suffit d'être parano et de partir du principe que le code peut être exécuté par plusieurs entités en même temps et être interrompue en plein milieu d'une opération. Comme ça, ça force à sécuriser le code. Mais je suis surpris de voir à quel point c'est stable alors que je ne gère pas la DMA et les ETMU.
Lephenixnoir En ligne Administrateur Points: 18201 Défis: 142 Message

Citer : Posté le 03/06/2020 13:21 | #


Hoo non la lenteur vient du fait que Fugue ne se base pas sur des caractères étendu (uint16_t) pour stocker le path mais sur des chars classiques (uint8_t).

C'est pas lent, ça. Je viens de mesurer avec libprof que convertir 100 fois "\\fls0\vhex.g1a" dans les deux sens prend 359 µs (oui le cache aide beaucoup et les variables locales sur la pile sont couvertes par le cache !). En plus ça n'a rien à voir avec la lenteur d'écriture dans un fichier, celle qui fait que sauvegarder 1 Mo de la ROM prend pas loin d'une minute. À la limite ça peut être un problème pour les fonctions de recherche et le fait que MEMORY est de plus en plus lent quand le nombre de fichiers augmente, mais encore faut-il s'assurer que ce n'est ni la fragmentation ni la GUI ni whatever.

Donc autant je suis persuadé aussi qu'on peut faire plus vite que BFile, autant pour l'instant je suis pas du tout convaincu qu'on sait où est le bottleneck dans l'histoire. En plus si le problème était vraiment là tu pourrais utiliser directement Fugue avec des u8 classiques dans Vhex (avec utf8 derrière si l'envie t'en prend) et les performances seraient très bien, donc je suis presque sûr que tu d'autres raisons de vouloir le contourner.

Les manipulations avec le path sont extrêmement lentes car Casio reconvertie le path super souvent et (Fugue/Casio) vérifient la validité *complète* du path à chaque fois.

Si la validité c'est juste que ça commence "\\fls0\", on s'en remettra. Si ça parcourait la table de fichiers en RAM, alors tu saurais certainement la trouver (^^"), et en tous cas c'est pas si long parce que RAM+cache encore une fois.

Oublions pas que le CPU tourne à 0.12 GHz (c'est beaucoup écrit comme ça hein ?) donc il faut un peu plus que de l'overhead mondain pour expliquer des lenteurs caractéristiques. Moi je serais plus intéressé de regarder les specs de la puce de ROM (qu'on a d'ailleurs), la config du BSC et l'utilisation du DMA dans l'affaire. Le reste tu n'auras pas de difficultés à le faire suffisamment rapide, et je pense que même si la version Casio est moche, ce n'est sans doute pas le coeur du problème.

Une grosse partie de Fugue est en RAM et aucun syscall n'est appelé dans Bfile_OpenFile() faisant référence à ce truc. Donc on ne peut pas l'utiliser via des syscalls et les structures en RAM ne sont pas fixes.

Qu'il n'y a pas de syscalls dédiés, pas de problème, mais le code est quand même presque toujours dans la ROM ? Le seul code en RAM actuellement connu c'est les syscalls flash dans la mémoire RS. Je vois pas de raison que ce soit fait autrement que r2=0x80machin et jsr @r2, et comme tu n'as pas encore dit explicitement le contraire (eg. r2=0x88machin et jmp @r2) ni publié d'extraits désassemblés je sais toujours pas. x_x

Mais qu'on ne puisse pas l'appeler directement, ok, et que les structures soient pas fixes, ok. Après tu peux toujours essayer de les trouver en détectant les structures par une recherche dans la RAM (comme les tables ACPI sur x86), ou en faisant des acrobaties pétées comme analyser dynamiquement le code pour trouver les adresses, un peu comme dans GetGetKeyToMainMenuReturnFlag() ici où le code donné par SimLo désassemble une instruction pour trouver un flag dans la RAM. J'ai aucune idée de si ces machins sont viables, mais je m'attends pas à ce que tu puisses l'éviter. Si la table des fichiers est en cache en RAM t'auras pas trop le choix parce que la ROM sera probablement pas à jour tout le temps. Imagine que tu transfères un add-in et que le nouveaux fichier g1a est listé que dans la table en RAM jusqu'à ce que tu éteignes la calculatrice. Ça pourrait arriver ça.

Je veux seulement pouvoir y accéder en lecture donc si, je peux largement me passer de Fugue. Le jour où je fais de l'écriture, oui, il faudra que je trouve un moyen pour être compatible avec Fugue (ou simplement trouver le moyen de le forcer à se régénérer en RAM, si c'est possible).

Ah mais c'est pas la même affaire tout de suite, il fallait le dire avaaaaant. (Je sais même pas quel smiley mettre là, c'est te dire le désarroi.)

Ça permettrait de supporter et choisir entre plusieurs configurations du noyau et de le charger dynamiquement en RAM par "modules" au lieu de le charger complètement à chaque fois.

Euh, comme une lib statique donc ? gint ne contient que les drivers que ton add-in utilise et ce dès la compilation. Je vois moyennement l'intérêt de vouloir charger différents modules d'une fois sur l'autre si c'est le même add-in qui tourne.

De plus, si le BIOS/Gurb (je ne suis pas sûr du nom) devient assez élaborer, on peut imaginer pouvoir en faire une interface pour des transferts de fichiers et / ou un moyen de flasher des OS customs.

Et pourquoi mettre ça dans le bootloader au lieu de juste avoir une appli normale qui importe sa version du noyau ?

Il suffit d'être parano et de partir du principe que le code peut être exécuté par plusieurs entités en même temps et être interrompue en plein milieu d'une opération. Comme ça, ça force à sécuriser le code. Mais je suis surpris de voir à quel point c'est stable alors que je ne gère pas la DMA et les ETMU.

Le problème c'est que tu ne peux pas te permettre d'être interrompu en plein milieu d'une opération sur le DMA parce que (par exemple) l'écran attend les données. Il ne s'agit pas d'interrompre que le code mais aussi le matériel, ce qui change pas mal de trucs. Bon, pour les ETMU je pense que maintenant que j'ai trouvé le trick des écritures lentes ça ira mieux. Pour le DMA, j'attends de voir si ça te pose des problèmes. Et pour le TLB c'est pareil, en général quand tu exécutes ton code tu fais l'hypothèse qu'il est mappé, et changer ça est très chiant parce que si le code pour recharger le TLB est démappé... tu vois le truc. Mais t'as beaucoup plus de trucs en RAM que moi donc ça aide je suppose.
Yatis Hors ligne Membre Points: 513 Défis: 0 Message

Citer : Posté le 23/06/2020 13:09 | #


Donc autant je suis persuadé aussi qu'on peut faire plus vite que BFile, autant pour l'instant je suis pas du tout convaincu qu'on sait où est le bottleneck dans l'histoire.

Tu as raison pour le bottleneck mais il n'empêche que le code derrière les Bfile est horrible et il y a plein d'opérations répétées mille fois pour pas grand-chose, ce qui rend la RE du truc compliqué.

En plus si le problème était vraiment là tu pourrais utiliser directement Fugue avec des u8 classiques dans Vhex (avec utf8 derrière si l'envie t'en prend) et les performances seraient très bien, donc je suis presque sûr que tu as d'autres raisons de vouloir le contourner.

Oui j'ai plusieurs raisons de vouloir contourner le BFile(). Premièrement parce que ce n'est pas un FS "open source" donc d'un point de vue "morale" je ne suis pas super chaud d'utiliser ce truc sans l'avoir documenté un minimum. Secondement, Bfile utilise (pour certaines primitives telles que Bfile_WriteFile()) le DMA avec (possiblement) des callbacks et il me semble aussi qu'a chaque écriture dans la ROM, Casio flush le TLB (pour des raisons de synchronisation). Il faut donc, pour éviter tout problème avec Bfile restaurer TOUT l'environnement de Casio pour qu'ils puissent reprendre la main ; ce qui implique, par conséquent, un blocage complet du noyau le temps que la primitive se finisse correctement (blocage, qui pourrait être beaucoup moins important si j'ai des primitives customs).

Je vois pas de raison que ce soit fait autrement que r2=0x80machin et jsr @r2, et comme tu n'as pas encore dit explicitement le contraire (eg. r2=0x88machin et jmp @r2) ni publié d'extraits désassemblés je sais toujours pas. x_x

En fait je m'attendais tellement à ce que Casio utilise le syscall 0x01C8 flash read() (comme pour l'ancien FS) que je n'ai pas cherché à comprendre les différentes structures de données de Fuge. Je retracerai les primitives Bfile correctement une fois le driver USB sera fait (je m'explique plus bas).

Euh, comme une lib statique donc ? gint ne contient que les drivers que ton add-in utilise et ce dès la compilation. Je vois moyennement l'intérêt de vouloir charger différents modules d'une fois sur l'autre si c'est le même add-in qui tourne.
[...]
Et pourquoi mettre ça dans le bootloader au lieu de juste avoir une appli normale qui importe sa version du noyau ?

Le fait d'avoir un bootloader à part me permettrait de passer d'un noyau monolithique non modulaire à un noyau monolithique modulaire. Ça permet de faire beaucoup de choses mais voila en gros ce qu'il m'intéresse avec cette nouvelle architecture :
* le noyau sera directement compatible avec toutes les machines (il faudra juste choisir le bon bootloader pour la calto)
* le noyau pourra être entièrement en RAM (ce qui va permettre d'éviter des problèmes avec le démappage des pages de la TLB)
* ça permettra de choisir le module qu'on souhaite utiliser (donc les modules pourront être dev à part).
* ça permettra de charger uniquement les driver appropriés (donc les drivers pourront être dev à part).
* le noyau pourra être porté sur d'autres architectures hardwares (AVR, ARM, Z80, ...) sans trop de difficulté(?).
* les différentes parties (drivers, module, ...(?)) pourront être ajoutés / créer facilement car totalement indépendant du noyau.
* ça permettra d'isoler correctement toutes les parties du noyau comme ça si quelqu'un veut ajouter un driver, un module ou une lib il pourra le faire sans avoir à se taper toute le doc du noyau (qui n'existe pas d'ailleurs, malin <_< ).

D'ailleurs j'ai des news du projet.
Il y a quelques semaines j'ai commencé à refactor beaucoup de choses car l'architecture "visuel" du projet (dossiers, nom de fichier, ...) me plaisait pas. De plus, j'ai corrigé / amélioré certaines parties (notamment l'ordonnanceur, qui est presque prêt à être préemptif ) malheureusement, j'ai un problème de deadlock quelque part (probablement le TTY qui fait encore des siennes). Mais comme je compte refaire l'architecture "logique" du noyau pour passer à un kernel monolithique modulaire, je ne vais pas chercher à corriger les problèmes qui trainent car les modifications seront beaucoup trop importantes et risquent fortement de changer le fonctionnement de pas mal de chose.

J'aimerais avoir votre avis pour l'organisation du projet car finalement il y aura (au moins) 3 parties distinctes :
* le bootloader (basé uniquement sur les syscall de Casio pour fonctionner, ce qui assurera une portabilité à toute épreuve).
* le noyau (est-ce que je sépare les différents modules ? est-ce que je sépare les différents drivers ?)
* les librairies (glibc / newlib, bopti, topti, Revolution-FX, MonochromLib, ...)
Est-ce que je fais un repos avec tout dedans ? Est-ce que je fais 3 dépôts différents ? Est-ce qu'on peut faire des "groupe de dépôts" sur Gitea ? Est-ce que vous pensez qu'il serait préférable de passer sur une licence autre que CC0 (car je crois qu'elle n'est pas applicable à 100% en France(?))?
(@note: "est-ce que je sépare les différents [...]" -> est-ce que je fais un dépôt à part ?)

Actuellement je ne suis pas sur le projet, je fais une pause.
Je suis actuellement à la recherche d'info sur le module hardware USB car je pense en avoir besoin pour envoyer des logs de debug sur mon laptop (en implémentant la classe CDC/ADC)...et j'ai envie de m'amuser aussi

Mais c'est vraiment compliqué car le module semble ne pas correspondre a la doc du SH7724 (d'après mes tests) mais il est curieusement proche du SH7724 en matière d'adresse. Donc en ce moment, j'essaie de tracer les syscall Comm_*() pour avoir au moins le protocole d'initialisation / power-on du module mais c'est vraiment complexe car Casio initialise l'USB via un timer ainsi que les interruptions hardwares du module donc c'est tendu à réunir toutes les infos.

Ceci-dit ça avance, j'ai le protocole pour "allumer" le module USB, détecter quand un câble est branché et dire à l'hôte "Hey je suis un device USB :D". Mais je n'arrive pas à configurer la suite, les registres semblent différents et je suis bloqué dans mon gestionnaire d'interruption de l'USB (et je ne sais pas si c'est parce que je ne réponds pas à l'hôte ou si c'est parce que j'oublie d'effacer un flag). Bref je tâtonne mais ça avance un petit peu plus chaque jour

Pour ce qui est de la suite, je compte finir le driver USB et retourner sur le projet en attaquant avec la conception du bootloader (donc probablement après les "vacances")
Lephenixnoir En ligne Administrateur Points: 18201 Défis: 142 Message

Citer : Posté le 23/06/2020 21:14 | #


Secondement, Bfile utilise (pour certaines primitives telles que Bfile_WriteFile()) le DMA avec (possiblement) des callbacks et il me semble aussi qu'a chaque écriture dans la ROM, Casio flush le TLB (pour des raisons de synchronisation). Il faut donc, pour éviter tout problème avec Bfile restaurer TOUT l'environnement de Casio pour qu'ils puissent reprendre la main ; ce qui implique, par conséquent, un blocage complet du noyau le temps que la primitive se finisse correctement (blocage, qui pourrait être beaucoup moins important si j'ai des primitives customs).

C'est exact, il utilise bien le DMA et il flushe bien le TLB, ce que je peux d'ailleurs voir dans gintctl... avec les mêmes inconvénients que tu décris.

En fait je m'attendais tellement à ce que Casio utilise le syscall 0x01C8 flash read() (comme pour l'ancien FS)

Pourquoi il utiliserait ça quand on peut aller directement sur la ROM dans P2 ? Plus rapide ?

Le fait d'avoir un bootloader à part me permettrait de passer d'un noyau monolithique non modulaire à un noyau monolithique modulaire. Ça permet de faire beaucoup de choses mais voila en gros ce qu'il m'intéresse avec cette nouvelle architecture :
* le noyau sera directement compatible avec toutes les machines (il faudra juste choisir le bon bootloader pour la calto)
* le noyau pourra être entièrement en RAM (ce qui va permettre d'éviter des problèmes avec le démappage des pages de la TLB)
* ça permettra de choisir le module qu'on souhaite utiliser (donc les modules pourront être dev à part).
* ça permettra de charger uniquement les driver appropriés (donc les drivers pourront être dev à part).
* le noyau pourra être porté sur d'autres architectures hardwares (AVR, ARM, Z80, ...) sans trop de difficulté(?).
* les différentes parties (drivers, module, ...(?)) pourront être ajoutés / créer facilement car totalement indépendant du noyau.
* ça permettra d'isoler correctement toutes les parties du noyau comme ça si quelqu'un veut ajouter un driver, un module ou une lib il pourra le faire sans avoir à se taper toute le doc du noyau (qui n'existe pas d'ailleurs, malin <_< ).

Hmm en fait je crois qu'on n'est surtout pas d'accord sur la terminologie. Mais peu importe, l'idée est sensée donc y'a rien à ajouter.

Pour ton architecture, je pense qu'il est sensé d'avoir le bootloader et tous les modules dans le même dépôt même s'ils sont compilés indépendamment les uns des autres. Les autres trucs comme newlib t'auras pas le choix, newlib est un dépôt à elle toute seule.

Mais c'est vraiment compliqué car le module semble ne pas correspondre a la doc du SH7724 (d'après mes tests) mais il est curieusement proche du SH7724 en matière d'adresse.

Je pense qu'on a résolu cette question à l'oral tout à l'heure du coup !

(Si quelqu'un d'autre lit : on a décompilé des bouts de l'émulateur et trouvé les adresses et bitmasks des registres USB, et ça correspond totalement au SH7724.)

Ceci-dit ça avance, j'ai le protocole pour "allumer" le module USB, détecter quand un câble est branché et dire à l'hôte "Hey je suis un device USB :D". Mais je n'arrive pas à configurer la suite, les registres semblent différents et je suis bloqué dans mon gestionnaire d'interruption de l'USB (et je ne sais pas si c'est parce que je ne réponds pas à l'hôte ou si c'est parce que j'oublie d'effacer un flag). Bref je tâtonne mais ça avance un petit peu plus chaque jour

Excellent ! Je suis impatient d'en voir plus

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 53 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