vendredi 14 juin 2019

Chapitre 61 : Accès à un site web par son nom et aux données d’une page en assembleur ARM


Au chapitre 56 lors de la présentation des fonctions des sockets, je vous avais demandé de réfléchir à la récupération de l’adresse IP d’un site en utilisant la commande Linux ping puisqu’il n’existe pas à ma connaissance d’un appel système Linux qui effectue cela. Il aurait été possible et plus simple d’utiliser la fonction de la librairie C gethostbyname, mais dans ce cas, autant tout programmer en C !!!
Dans ce chapitre, nous allons effectuer l’accès à un site web en fonction de son nom (www.toto par exemple) en utilisant non pas la commande ping mais la commande host.
Pour cela, nous devons résoudre quelques petits problèmes : lancer depuis un programme assembleur l commande Linux host, récupérer son résultat, analyser son contenu pour extraire et convertir l’adresse IP. Ensuite nous réutiliserons les instructions liées à la création et à l’utilisation d’une socket vues au chapitre 56.
Pour illustrer cela, nous allons nous connecter au site de météoFrance, charger la page /previsions-meteo-france/bulletin-France, et extraire le texte du bulletin météo du jour. ( voir le source du programme).
Pour lancer la commande host, nous utilisons l’appel système Linux execve code x0b en lui passant comme argument une zone mémoire qui contient l’adresse du nom de l’exécutable à lancer (/usr/bin/host) et l’adresse du nom du site pour lequel nous recherchons l’adresse IP. Hélas, cet appel système ne retourne rien et s’arrête si son exécution réussit !!. mais il affiche le résultat dans la console de sortie.
Il nous faut donc exécuter l’appel système dans un thread (fils) et attendre sa fin pour récupérer son résultat dans le thread parent. Dans ce thread, nous utilisons l’appel système WAIT4 pour attendre le signal de fin du fils et pour récupérer le résultat de la console, il nous faut créer un pipe puis une dérivation (appel système dup2 code 0x3F pour enfin lire les données par l’appel système READ.
Maintenant, que nous avons le résultat de la commande host dans un buffer mémoire, il nous suffit d’extraire l’adresse IP par la fonction extchaine et de la convertir en adresse IP de la structure sockaddr_in necessaire à la création de la socket.
Ensuite nous utilisons les fonctions de création, connexion , lancement de la requête et lecture des données de la socket pour alimenter un buffer contenant la page complète. Pour terminer, il faut rechercher les balises encadrant le texte à afficher pour l’extraire, effectuer quelques remplacements (<br/>) et l’afficher.
J’ai fait simple et donc le texte affiché peut nécessiter encore quelques corrections !!
Ce programme peut être adapté pour accéder à d’autres informations d’autres sites.

Ce que j’ai bien sûr fait et cela m’a fait découvrir qu’il ne fonctionnait pas pour les sites sécurisés  (protocole https). J’ai donc dû l’adapter pour résoudre ce problème en utilisant la librairie Openssl disponible sur le Raspberry.
Pour pouvoir créer un programme assembleur avec cette librairie, nous devons d’abord installer le package libssl-dev et modifier les options pour le linker : -lssl -lcrypto
Pour l’exemple, notre programme va afficher le cours du bitcoin en se connectant sur le site bitcoin.fr et en chargeant la page /le-cours-du-bitcoin/.
Nous reprenons toute la partie de notre premier programme concernant le lancement de la commande host, la récupération de l’adresse IP et la création de la socket ( le port utilisé sera le 443 à la place du port habituel 80).
A partir de là, nous allons faire appel aux fonctions de la librairie openssl pour initialiser les données nécessaires(OPENSSL_init_crypto, ERR_load_BIO_strings), créer une connexion sécurisée (OPENSSL_init_ssl) et effectuer le lien entre cette connexion et la socket(SSL_set_fd). Puis nous lancerons la requête par l’appel SSL_write et récupérerons la réponse par SSL_read. Remarque : il est nécessaire de programmer une boucle de lecture car c’est le serveur qui pilote l’envoi des données par bloc).
Ensuite dans la routine analyseReponse, nous cherchons les mots clés précedents le cours du bitcoin que nous extrayons avec la sous routine extChaine et nous terminons par l’affichage du libellé et du cours.
Je n’ai pas trop regardé en détail les possibilités de la librairie openssl, ni le détail des paramètres à passer aux différentes fonctions par manque de temps !!!

mardi 4 juin 2019

Chapitre 60 : utilisation du timer à l'aide d'un driver spécifique Linux


Dans le chapitre 25, j’avais déjà donné un programme assembleur qui utilisait le timer du BCM2835 mais ce programme devait être lancé par sudo pour autoriser le mapping en mémoire des registres du timer.
Après plusieurs recherches, j’ai trouvé une solution pour éviter le lancement par sudo sur le site : https://mindplusplus.wordpress.com/2013/08/09/accessing-the-raspberry-pis-1mhz-timer-via-kernel-driver/
Et qui va en plus simplifier la lecture car il n’est plus nécessaire d’effectuer le mapping.
Il s’agit de mettre en place un driver spécifique Linux qui autorisera la lecture du registre du Timer. Mais hélas, nous n’allons pas écrire ce driver en assembleur, nous allons nous contenter de récupérer le source C, de créer les objets particuliers grâce au Makefile donné par le même site.En effet, la compilation doit s’effectuer avec des contraintes particulières de création de ce type de module.

 Ensuite nous écrirons un programme en assembleur pour tester le timer au travers de ce driver.
Tout d’abord la compilation du programme C nécessite la mise en place du package : raspberrypi-kernel-headers avec la commande : sudo apt-get install raspberrypi-kernel-headers après avoir mis à jour votre raspberry avec les commandes update habituelles (En effet le package doit être au même niveau de version que le kernel).
Puis il faut créer sur votre raspberry le répertoire : bcm2708_usec et récupérer sur le site le programme source et le makeFile en faisant bien attention de ne pas modifier les noms bcm2708_usec.c et Makefile (attention à la majuscule).
Remarque : pour les curieux, voir les discussions en anglais sur le nommage des bcm !!!
Suivant le modèle de votre raspberry, modifier dans le source bcm2708_usec.c, l’adresse de TIMER_PAGE_BASE soit  0x3F003000 pour le pi2 et 3 soit 0x20003000 pour le pi1.
Se placer dans le répertoire bcm2708_usec et lancer la construction des objets en tapant make.
Corriger les erreurs éventuelles !!!! et vérifier la présence du module objet bcm2708_usec.ko.
Maintenant il faut activer et déclarer ce nouveau module dans le système linux par
sudo /sbin/insmod ./bcm2708_usec.ko
puis
sudo /bin/mknod -m 444 /dev/bcm2708_usec c 240 0
Remarque : en cas d’erreur vous pouvez désinstaller le module par la commande :
sudo /sbin/rmmod ./bcm2708_usec.ko
Pour vérifier vous pouvez lancer la commande mais il faudra la tuer par un control-C :
od -t u8 /dev/bcm2708_usec

Si tout va bien, il vous reste à récupérer mon programme assembleur testTimerSp.s pour voir l’utilisation du timer.
Dans ce programme, nous commençons par ouvrir ce nouveau driver par le call system OPEN en lui passant le nom /dev/bcm2708_usec et nous récupérons comme tout fichier unix, le File Descriptor.
Avec celui ci nous nous contentons de lire le contenu du registre du Timer par le call system READ une première fois, puis de mettre en attente le programme une seconde avec le call system SLEEP, de relire le contenu du timer et d’afficher la différence des 2 lectures. Nous terminons par l’appel système CLOSE pour fermer le driver.
L’exécution s’effectue sans avoir besoin de taper sudo nom-du-programme et l’affichage indique bien une valeur autour de 1000000, ce qui correspond bien au nombre de microsecondes contenues dans une seconde.
La valeur calculée est supérieure car l’appel systeme SLEEP nécessite quelques microsecondes pour s’exécuter !!
Curieusement, le résultat n’est pas toujours identique, et je ne suis pas arrivé à expliquer pourquoi (multitaches ? mémoire 1er niveau ?, interruptions ?).

Dans ce deuxième programme, nous allons mesurer le temps d’exécution de sous routines. Il effectue 8 mesures (8 pour faciliter la division en utilisant le décalage de 3 bits à droite) de l’exécution d’un million de sous routines.
Pour cela après l’ouverture du driver bcm2708_usec, nous appelons la routine chrono en lui passant le File-Descriptor récupéré lors de l’ouverture précédente dans r0 et l’adresse de la sous routine à mesurer dans r1.
Dans la routine chrono, nous trouvons une boucle qui  effectue 8 mesures de temps et qui se décompose en une lecture du compteur du timer par l’appel système Read, l’exécution d’une boucle qui appelle 1 million de fois la sous routine à mesurer puis qui lit une deuxième fois le compteur du timer. Puis elle calcule la différence et affiche le résultat.
A la fin de cette boucle, nous affichons la moyenne des mesures car les résultats de chaque mesure ne sont pas identiques.
A l’exécution, nous remarquons pour la première routine des temps très variables !! C’est pourquoi nous exécutons une deuxième fois la mesure de la sous routine vide en fin du programme. Cela nous donne une valeur de référence qui permet calculer les temps internes des autres sous-routines.

Remarque 1 : Il doit être possible de copier et modifier le programme C pour effectuer des accès à d’autres parties du périphérique du raspberry.

Remarque 2 : maintenant les sources des programmes de ce blog sont stockés sur GITHUB  et non plus sur le drive de Google pour des facilités de consultation et d'extraction.

Remarque 3 : à chaque maj de Raspbian, vous devez recompiler le module C et le réactiver en effectuant les mêmes manipulations.