jeudi 14 mars 2019

Chapitre 56 : Fonctions des sockets, accès à une page WEB en assembleur


Vous trouverez sur Internet, plusieurs tutoriels sur les fonctions pour gérer les sockets avec des exemples de serveurs et de clients écrits en langage C. Ces exemples m’ont servi à une adaptation à l’assembleur sur le Raspberry.
Pour la partie théorique des sockets je vous renvoie à ces tutoriels (par exemple : https://broux.developpez.com/articles/c/sockets/)
Tout d’abord, nous allons voir un petit programme amusant !! qui permet l’exécution de commandes linux d’un process à l’autre et qui nous permettra de voir l’utilisation des fonctions nécessaires.
Ces fonctions font appel à des call system linux qui curieusement ne sont pas référencés sur le site http://syscalls.kernelgrok.com/. Pour retrouver les différents codes à utiliser il faut lister un fichier se trouvant sur le raspberry avec la commande :
More /usr/include/arm-linux-gnueabihf/asm/unistd.h
Puis rechercher sur Internet, les syntaxes des commandes pour avoir les paramètres à passer à chaque fonction.
Dans ce premier programme, nous trouvons en plus des descriptions habituelles une structure de type sockaddr qui contient le type de connexion à utiliser, le port ici 4444 et l’adresse IP ici 0.0.0.0.
Dans la  partie code, nous commençons par créer la socket en utilisant le call system 281 et nous vérifions le code erreur retourné. Si la création est OK nous conservons le descripteur du socket dans le registre r4.
Puis nous enchainons sur les fonctions bind (282) qui affecte l’adresse contenue dans la structure à la socket
 listen(284) qui référence la socket comme passive cad en attente de connexion d’un client
 accept(285) qui accepte les connections avec la socket.
Enfin nous terminons avec les fonctions de duplication des descripteurs des entrées sorties standard (STDIN, STDOUT et STDERR) et nous terminons par l’appel execve qui va lancer la commande demandée à l’aide du schell.
Après compilation, ce programme doit être lancé en arrière plan comme ceci : testsocket &
En fait ce programme va écouter ce qui arrive sur le port 4444. Pour envoyer une commande sur ce port il faut d’abord exécuter la commande :
netcat -vv 0.0.0.0 4444   
Vous aurez le message suivant :
Connection to 0.0.0.0 4444 port [tcp/*] succeeded!
Qui indique le succès de la connexion. Il ne vous reste plus qu’à taper des commandes linux comme ls ou uname –a pour voir s’afficher les résultats.
Evidement dans ce cas ce programme n’a que peu d’intérêt mais vous pouvez le lancer dans une session et la lancer la commande netcat dans une autre session et c’est déjà plus intéressant. Je vous laisse le soin de trouver d’autres utilisations !!!
Pour arrêter le programme il suffit d’effectuer un control-C.

Après cette première réussite, je me suis lancé dans l’écriture d’un programme qui accède à une page d’un site web. La page d’accueil de Google étant assez lourde, j’ai pris comme exemple une petite page (/Exemple_01.php) du site ordi-senior.fr mais j’ai laissé en commentaire les adresses du site google.fr si vous voulez y accéder.
Après quelques problèmes de mise au point, ce programme fonctionne mais il faudra le compléter par des requêtes plus élaborées et par une analyse des pages web pour en extraire les données pertinentes !!!
Tout d’abord, j’ai remplacé la structure fixe du programme précèdent par une structure vide dont les données seront alimentées dans le code du programme. La réservation de l’espace nécessaire est faite dans la section .bbs.
Dans le code, nous commençons par alimenter la structure avec l’adresse ip du site à contacter et le port. Pour les sites web, le port est toujours 80 (attention il doit être stocké sur 2 octets). Pour l’adresse IP, soit on stocke les 4 parties de l’adresse octet par octet soit on stocke la totalité en hexadécimal en respectant l’ordre inverse).
Remarque : normalement, nous devrions trouver l’adresse ip d’un site en effectuant une requête à un serveur dns mais pour l’instant il nous faudra effectuer au préalable un ping nom_du_site pour avoir son adresse ip et la saisir dans le programme (quoique on pourrait programmer l’appel à la commande ping , récupérer le résultat et l’analyser pour extraire l’adresse : exercice à faire !!).
Puis nous créons la socket avec le call system connect (281)  puis nous effectuons une connexion avec l’identifiant de la socket et l’adresse de la structure précédente.
Si tout se passe bien nous préparons l’envoi de la requête qui est ici une simple chaine de caractères. Comme amélioration, il faudra plutôt créer une chaine en concaténant les informations fixes avec 2 informations variables : la ressource demandée (ici /Exemple_01.php) et le nom de l’hôte (ici ordi-senior.fr). La requête est envoyé avec le call system Send (289) qui nécessite de connaitre la longueur de la requête. Nous la calculons pour la passer par r2.
Ensuite nous entrons dans une boucle de réception des résultats. Celle-ci est nécessaire car les pages peuvent avoir des tailles importantes et le serveur va retourner les données par bloc de la taille du buffer. La fin des résultats est donnée par le code 0xA. Nous plaçons un zéro binaire à la fin de chaque bloc de données lu ce qui nous permet ici de l’afficher par notre routine standard. La fonction à utiliser est recv (291) à laquelle il faut passer l’adresse du buffer de réception et sa longueur.
Enfin le programme se termine par la fermeture de la socket.

mercredi 20 février 2019

Chapitre 55 : Accès à une base de donnée mariadb (ou mysql) en assembleur.


Pour cela, il faut d’abord avoir mis en place une base de données de ce type sur le Raspberry. Pour cela je vous renvoie à la nombreuse documentation disponible sur internet. Sur mon système, j’ai mis en place la base de donnée mariaDb à l’aide du package mariadb-server. Ensuite j’ai crée une base testvinc et une table département avec 2 champs, le N° du département et le nom du département. Pour ces premiers tests, je n’ai crée que 2 enregistrements dans la table. J’ai crée aussi un nouvel utilisateur pgmasm avec des droits réduits sur l’accès à la base testvinc. Pour être sûr que tout est ok, il faut mieux déjà vérifier par la commande mysql, que ce nouvel utilisateur peut bien afficher le contenu de la table (et éventuellement la modifier). Cela peut vous éviter des recherches inutiles lors de l’exécution du programme assembleur. Par exemple dans mon cas, le programme n’affichait pas la table et je n’avais pas fait cette vérification. Ce n’est qu’après en vérifiant par mysql, que j’avais créé la table dans la base mysql et pas dans ma base testvinc (Voui, il faut penser à changer de base par use testvinc avant de créer une table !!!).

Pour créer l’exécutable et faire fonctionner ce programme, il faut faire appel à des librairies de mariadb que l’on obtient en installant les packages supplémentaires :  libmariadbd-dev et libmariadbclient-dev. Pour vérifier et connaitre les options à ajouter au linker il suffit de taper la commande mysql_config --cflags –libs .
Sur mon système, celle-ci a retourné les 2 lignes :
-I/usr/include/mysql
-L/usr/lib/arm-linux-gnueabihf  -lmariadbclient -lpthread -lz -lm –ldl
La directive include ne nous concerne pas (concerne le C) et nous ajoutons les autres directives à gcc qui ici nous sert de linker après la compilation du programme assembleur.
La documentation des fonctions mysql peut être trouvée ici (mais en anglais) :
https://dev.mysql.com/doc/refman/8.0/en/c-api.html

Dans le programme assembleur (voir le source ici) nous commençons à initialiser les structures de mariadb par l’appel de la fonction mysql_init et nous testons le code retour.
Puis nous établissons une connexion à la base crée précédemment avec l’identifiant utilisateur crée et son mot de passe par l’appel de la fonction bl mysql_real_connect. Vous remarquerez que cette fonction a besoin de 8 paramètres dont les 4 premiers sont passés par les registres r0 à r3 et les autres par la pile dans l’ordre inverse de celui attendu par la procédure. Rappelez vous pour cela les premiers chapitres de ce blog ou les appels des fonctions X11 !! Et au retour de la procédure il faut penser à réaligner la pile avec l’instruction add sp,#16  (4 paramètres de 4 octets chacun !!). Puis il faut tester le code retour pour vérifier la bonne connexion. En cas d’erreur il faut revérifier votre saisie du nom de la base, du nom utilisateur et du mot de passe et l’ordre des paramètres.

Puis nous lançons une requête qui est contenue en totalité dans une chaîne de caractères et dont nous passons l’adresse à la fonction mysql_query par le registre r1. Le registre r0 contient le pointeur vers les structures mariadb. Comme ce pointeur va être très utilisé nous le conservons dans le registre r10. Remarque : la requête ne doit se terminer par le point virgule comme dans les commandes directes sous Mysql.
Nous récupérons le set des résultats par l’appel de la fonction mysql_store_result mais nous pouvons aussi utiliser mysql_use_result mais qui offre moins de possibilité. Après le test du code retour, nous conversons le pointeur du set de résultat dans r9, et nous utilisons la fonction mysql_fetch_field qui va nous donner l’adresse des données du premier champ. Nous affichons les noms des premières données à savoir le nom du champ (ou son alias) le nom du champ d’origine, le nom de la table (ou son alias) le nom de la table d’origine et le nom de la base utilisée. Nous refaisons un nouvel appel à cette fonction pour avoir les données du 2 ième champ.

Maintenant, nous allons afficher tous les champs de toutes les rangées de la base. D’abord nous récupérons le nombre de champ de chaque rangée par la fonction mysql_num_fields. Puis nous balayons chaque rangée par appel successif de la fonction mysql_fetch_row qui retourne l’adresse de chaque rangée ou zéro quand il n’y a plus de rangée. Avec l’adresse de chaque rangée, nous balayons les pointeurs de chaque champ pour afficher leurs valeurs.
Rien de bien compliqué !! En fin nous libérons les ressources du résultat et nous fermons la base de données utilisée.

Ce premier programme nous permet de voir l’utilisation de ces fonctions de base. Dans le deuxième programme, nous allons effectuer des mises à jour de notre base. Mais tout d’abord, j’ai récupéré la description de la structure des données des champs et je l’ai adapté au langage assembleur. Nous voyons que en plus des noms, nous pouvons récupérer le type de chaque champ et sa longueur. Cela permet d’envisager de nombreuses possibilité d’utilisation.
Ce deuxième programme contient une boucle de saisie du code département, du nom et de leur insertion dans la base. Après la saisie du code, nous vérifions que celui-ci n’est pas déjà connu dans la base. Remarque : comme nous l’avons déjà vu, l’appel au call system de lecture de Linux retourne une chaine de caractère avec un 0x0A final qu’il nous faut transformer en 0X00 final.

Nous concaténons le code saisi au début de la requête sous forme de chaine de caractère. Si le code est Ok, nous demandons la saisie du nom puis nous créons la requête de mise à jour, en concaténant tous les éléments. Nous vérifions la bonne exécution de la mise à jour puis nous bouclons sur une autre saisie jusqu’à la saisie du code 999 qui terminera la saisie.
Nous terminons par l’affichage complet de la base comme vu dans le programme précédant.

Il ne reste plus qu’à regarder toutes les autres fonctions et à les expérimenter !!

lundi 4 février 2019

Chapitre 54 : Calcul sur des nombres entiers doubles


Après tous ces chapitres sur le développement en assembleur sans OS, nous revenons à des exemples de programmes avec Linux. Dans ce chapitre nous allons voir des routines de calcul sur des nombres entiers doubles qui sont codés sur 64 bits soit 8 octets ce qui permet de traiter des entiers jusqu’à 2<<63 – 1 soit : 9223372036854775807 en non signé et +4611686018427387903 en signé.
Pour cela nous commençons par déclarer plusieurs nombres positifs et négatifs avec la pseudo instruction .quad. et nous réservons dans la .bbs des zones de 8 octets pour stocker des résultats intermédiaires.
Source du programme ici .
Dans le code, nous commençons par afficher par la macro habituelle les zones mémoires pour voir le stockage des doubles : La partie basse est dans les 4 premiers octets, la partie haute dans les 4 suivants.
Pour vérifier les calculs suivants, il nous faut commencer par afficher correctement un double et première difficulté il nous faut écrire une routine qui divise un double contenu dans 2 registres par 10. C’est le rôle des routines conversionDoubleU et divisionReg64U. Puis il nous faut aussi écrire les routines pour afficher des doubles signés : conversionDoubleS et divisionReg64S. Les divisions utilisent un algorithme classique de division binaire et ne divisent un double que par une valeur contenue dans un seul registre.
Pour lire un double ou stocker un double en mémoire, nous disposons de l’instruction ldrd r0,r1,[r2] et de l’instruction strd r0,r1,[r2] avec bien sûr r2 contenant l’adresse mémoire du double, r0 la partie basse et r1 la partie haute. Nous pouvons utiliser d’autres registres en condition que le premier soit pair et que le second soit le suivant (il y a une erreur de compilation pour r5,r8,[r2] par exemple !!).
Ensuite nous écrivons une routine de saisie d’un double à partir de la lecture d’une chaine de caractère à l’aide du système call READ. La conversion de la chaine stocke le double dans une zone en mémoire après avoir vérifié le dépassement de capacité éventuel.
Les autres routines correspondent à la division, addition soustraction multiplication signés et non signés de 2 nombres de 64 bits. Certaines routines utilisent les doubles stockés en mémoire et d’autres dans les registres. La difficulté est de penser à traiter les 2 registres qui contiennent le double et à bien reporter les retenues éventuelles. Il faut aussi veiller à bien utiliser les codes des comparaisons suivant que les nombres traités sont signées ou non. Par exemple pour la partie basse, il faut utiliser les codes hi (> non signé) lo (< non signé) et pour les parties hautes gt ou lt.
Enfin le programme se termine par une extraction de racine carrée entière par la méthode de héron et par des routines de comparaisons.
Il ne vous reste plus qu’à écrire les routines manquantes et d’optimiser celles qui sont dans ce programme !!
Ah j’oubliais, dans le fichier des macros, j’ai ajouté une macro qui affiche un simple libellé dans la console. Cela permet un petit gain de temps pour incluse un libellé entre les différents tests des routines.

vendredi 4 janvier 2019

Chapitre 53 : Baremetal utilisation d'un clavier, autonomie du Raspberry


Donc après de nombreuses heures d’effort, de multiples tests avec différents drivers et clavier, et de 2 cartes SD détériorées à force de les rentrer et sortir des connecteurs, j’ai réussi à faire fonctionner un clavier sur le port USB du Raspberry.
Au final, j’ai utilisé un driver USB écrit en langage C et trouvé sur github à l’adresse :
Merci beaucoup au développeur de ces programmes. Pour celui qui veut les utiliser, il faut télécharger tout le projet, le décompresser dans un répertoire de votre pc puis recompiler ( par gcc) les dossiers lib et env pour créer 2 librairies libuspi.a libuspienv.a. Remarque : la documentation est très très succincte mais les fonctions fournies permettent de gérer le clavier, la souris, une clé (je ne l’ai pas testé) et d’autres périphériques.
Il faut auparavant modifier quelques paramètres comme indiqué dans la brève documentation pour prendre en compte le type de Raspberry, le type de clavier (français) etc. Je n’ai eu aucun problème pour compiler ces projets.
Les 2 librairies seront recopiées dans le projet du Raspberry et il faudra modifier le makefile pour que le linker les prenne en compte.
Dans notre nouveau projet, copié de nos précédents projets, il faut modifier le fichier boot.s pour autoriser les interruptions FIQ et IRQ (voir instruction ligne 69) puis dans le module kernel.s, ajouter une nouvelle commande « clavier » qui effectuera le nouveau traitement.
En utilisant l’exemple en C donné par le projet uspi de github, nous programmons l’appel aux fonctions d’initialisation de l’environnement et du port USB. Nous testons si cela se passe bien puis nous vérifions la bonne détection du clavier et de la souris. S’ils sont présents, nous appelons une fonction d’enregistrement de notr procédure à appeler si une touche a été enfoncée ou si un bouton de la souris a été cliqué.
Ici, pour le clavier, nous nous contentons d’enregistrer les touches dans un buffer jusqu’à l’appui de la touche <enter> et nous affichons ce buffer par notre fonction uart_send_string. Pour la souris, nous nous contentons d’afficher le contenu des registres.
Cela nous permet de tester différents claviers et souris, de vérifier les valeurs des touches etc. Par exemple j’ai essayé d’ajouter le caractère € euro dans le fichier keymap_fr.h (dans le dossier lib) mais sans résultat. Si vous modifiez ce fichier pour corriger des touches, il faut penser à supprimer l’objet keymap.o pour forcer la compilation avec le nouveau fichier keymap_fr, recompiler la librairie libuspi.a et la recopier dans votre projet.
Maintenant que tout cela fonctionne, il nous faut réécrire le module kernel.s pour ne plus passer par la mini uart mais rendre au raspberry toute son autonomie.
Il faut commencer par mettre en place un affichage des lignes sur l’écran relié au raspberry en utilisant le framebuffer. Donc nous ajoutons dans le module framebuffer.s les instructions pour afficher les lignes successivement. Il faut aussi prévoir un décalage des lignes lorsque l’écran est plein. Mais rien n’est prévu pour afficher les premières lignes lorsque celles-ci n’apparaissent plus à l’écran.
Ensuite nous créons un module usb.s qui contiendra les appels aux fonctions d’initialisation de la librairie uspi. ATTENTION, cette librairie est écrite en C et certaines fonctions d’initialisation modifient la gestion du cache, de la mémoire et des interruptions. Ceci peut avoir des conséquences sue vos propres initialisations. Ici dans ce programme, j’ai constaté un écrasement de la mémoire dans la partie .data. Je n’ai pas trouvé la raison mais j’ai du ajouté dans le module framebuffer.s un décalage (label bidon J ) et une recopie de la table des message pour la mailbox. Il faudrait donc creuser un peu plus pour stabiliser ce fonctionnement !!. Dans ce module, nous ajoutons une fonction de lecture des touches et une fonction de lecture de chaine de caractères qui appellera la fonction précédente. Ces fonctions sont succinctes !!! car il manque la gestion de toutes les touches de fonction.
Dans les autres modules, j’ai laissé les appels à l’affichage par la miniuart. Si nécessaire, il faut modifier ces fonctions pour afficher les résultats sur l’écran.
Au niveau des commandes, je n’ai laissé que les commandes rep, lecture, affiche comme exemple. Il ne vous reste plus qu’à compléter suivant vos besoins !!!
Tout le projet est ici. Il est abondamment commenté pour être facilement modifiable.

mercredi 12 décembre 2018

Chapitre 52 : Baremetal Raspberry : executer un programme externe


Je n’ai toujours pas trouvé de solution au problème du clavier relié au port USB du Raspberry. Donc en attendant, nous allons voir comment exécuter un programme externe à partir de notre petit OS !! Pour cela nous créons un programme pgm1.s qui n’effectuera que l’envoi d’un message par l’Uart. Je laisse en plus quelques modules utilitaires pour vérification. Le programme est compilé et linké pour fournir un exécutable de type ELF32 (puisque mon raspberry est en 32 bits). Je n’ai pas regardé dans ce premier cas, toutes les contraintes à respecter et donc la seule est de linker les modules avec l’option –r qui va créer les données nécessaires à la <relocalisation> de l’exécutable. En effet, nous n’allons pas charger cet exécutable aux adresses définies par le linker (par exemple 8000h) mais à nos propres adresses et donc il est nécessaire de recalculer un certain nombre d’instructions pour avoir une exécution correcte.
L’exécutable (pgm1.elf) ainsi crée sera stocké dans le répertoire principal de la carte SD (avec le kernel.img) car lors d’un chapitre précédent sur la carte SD, je n’ai pas écrit de fonction pour lire un fichier dans des sous-répertoires de la carte SD (il faut bien que je vous laisse un peu de travail !!).
Vous remarquerez aussi que ce petit programme utilise les modules développes précédemment et donc va stocker en mémoire des instructions qui y figurent déjà. Il serait préférable de développer un mécanisme pour utiliser les fonctions déjà écrites et chargées dans le kernel.img. On verra peut être cela un autre jour !!!
L’utilitaire readelf (disponible sous raspbian ou windows) permet de visualiser les entités du programme. Voici par exemple le début des données de relocalisation :
 
Nous pouvons y voir que la fonction uart_send_string qui se trouve à l’emplacement 0x98c est utilisé au déplacement 0x140 du programme et que cette table commence à l’adresse 0x2ab4 et quelle contient 91 symboles qui seront à relocaliser.
Revenons au module kernel.s de notre OS. Nous y ajoutons une commande exec pour lancer l’exécution de notre petit programme externe (lancement par exec pgm1.elf) puis nous créerons un nouveau module execPgm.s qui contiendra les fonctions nécessaires à cette exécution.
Dans ce module, nous commençons à lire toutes les données du fichier contenant le programme à executer. Comme nous ne connaissons pas la taille, nous chargeons les données sur le tas. Le tas est la zone mémoire comprise entre la fin de la section bss et la pile et qui est complétement libre. Pour cela nous gérons une adresse du tas que nous ferons évoluer à chaque réservation de place nécessaire. Nous conservons l’adresse de début de stockage dans le registre r10 car cette adresse va servir de référence pour traiter toutes les donneés d’un programme de type ELF32.
Ensuite nous vérifions que les données lues correspondent à un format ELF, relogeable et pour un processeur ARM en récupérant les données dans l’entête du fichier grâce à la description d’une structure. Si le fichier est valide, nous récupérons aussi la valeur du point d’entrée du programme et les décalages des différentes tables (voir sur Internet la description d’un programme ELF32) à savoir la table des entêtes sections, la table des noms de section etc.
Puis nous allons analyser la table des entêtes sections pour identifier chaque section du programme. Pour certaines nous nous contentions de mettre à jour le décalage pour les retrouver et pour d’autres nous les recopions sur le tas (.text, .data). En effet, ces sections doivent être alignées sur des frontières particulières (multiple de 2 donnée par sectheaderElf_addralign). Bien entendu nous stockons ces nouvelles adresses dans la structure générale du programme.
Maintenant, il faut analyser la table des relocations pour recalculer les adresses de symboles en fonction des nouvelles adresses des sections .data et .text.  Pour chaque emplacement (décalage par rapport au début de la section) il faut retrouver le déplacement de la position du symbole pour remplacer son adresse. Suivant le type de relocation, il faut effectuer des opérations plus ou moins compliquées. Dans la documentation ARM nous retrouvons pour chaque type l’opération à effectuer. Par exemple pour le type R_ARM_ABS32 nous trouvons l’opération (S +A) | T, ce qui signifie ajouter la nouvelle valeur à l’ancienne et nous ne tenons pas compte de la valeur de T qui correspond à l’exécution d’instructions en thumb ce qui n’est jamais le cas ici.
Pour le type R_ARM_CALL c’est une opération plus compliquée et pour les autres types, je n’ai pas eu à les programmer car sur mes petits programmes je n’ai pas trouvé de cas. Si dans un programme, il y a un cas non traité, un message d’anomalie est affiché avec le code concerné et il faudra donc rechercher l’opération à effectuer et la programmer.
Dans ce programme, il y a un petit problème, car le résultat de la dernière opération est décalé de 4 octets et donc j’ai du les enlever avant d’exécuter le programme externe. Je n’ai pas trouvé l’origine de ce décalage : incompréhension ou erreur de ma part ?
Si tout est ok, on peut lancer le programme en effectuant simplement un saut au point d’entrée qui sera calculé.
Il faudrait améliorer le programme pour sauvegarder l’adresse de la pile  dans une zone mémoire fixe au cas où le programme appelé retourne une adresse de pile fausse !! Et aussi sauver et restaurer les registres si nécessaire.
Après l’exécution, nous remettons l’adresse du tas au début pour permettre l’exécution d’un autre programme sans perte d’espace mémoire.
Et tout le projet est ici.