<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2024-09-03T13:45:12+02:00</updated><id>/feed.xml</id><title type="html">SnowflakeOS’s blog</title><subtitle>Locally grown blog posts</subtitle><entry><title type="html">HRP - Notes de voyage</title><link href="/hiking/hiking-the-pyrenees/" rel="alternate" type="text/html" title="HRP - Notes de voyage" /><published>2021-01-23T00:00:00+01:00</published><updated>2021-01-23T00:00:00+01:00</updated><id>/hiking/hiking-the-pyrenees</id><content type="html" xml:base="/hiking/hiking-the-pyrenees/"><![CDATA[<figure>
    <img src="/assets/hrp/hrp-path.jpg" />
</figure>
<p>En août 2020, j’ai entrepris de marcher le long de la Haute Route Pyrénéenne (HRP), depuis Cauterets en direction de Banyuls-sur-Mer, la Méditerranée. J’ai documenté le voyage au jour le jour, dont le résumé est fait ici.</p>

<p>Avant le covid, le plan était de randonner le Kungsleden en Suède ; plan tombé à l’eau au profit d’une alternative locale, plus technique et finalement plus ambitieuse. C’était ma première randonnée en solo, et de loin la plus longue, et j’en garde des souvenirs géniaux - ce moi d’août était facilement le meilleur de 2020 pour moi. Pas un seul réveil difficile, pas une seule journée ordinaire. Jour après jour, j’ai pris note de mes journées, ce qui a donné le post ici présent.</p>

<p>La liste de mon équipement est disponible sur <a href="https://lighterpack.com/r/a304r5">lighterpack</a>. En terme de préparation, j’avais mon expérience passée, des traces GPX, et le <a href="https://www.cicerone.co.uk/the-pyrenean-haute-route-third">guide de Tom Martens</a><sup class="tooltip">[1]<span>the man can write, believe it</span></sup> au format ebook.</p>

<h2 id="journal">Journal</h2>

<h3 id="day-1-cauterets-to-oulettes-de-gaube">Day 1: Cauterets to Oulettes de Gaube</h3>

<p>Bivouac au pied du Vignemale, face Nord. Pas de difficultés ce matin à la descente du train, ni logistiques ni autres. De Cauterets, le chemin longe une rivière à l’ombre, nombreuses cascades. Cagnard l’après-midi lors de l’ascension vers le refuge de la Oulette de Gaube. Je croise à ce refuge un gars ayant l’air ultralight, à son sac et à sa sawyer mini<sup class="tooltip">[2]<span>j’apprends qu’elles peuvent se visser sur les bouteilles de Perrier !</span></sup>. K, parti de Hendaye le 6 juin. Rennais ! Il attendait sa trail family. M, prof de philo, D, vieux à qui j’ai peu parlé (il vient de perdre son portable: coup dur)<sup class="tooltip">[3]<span>quelqu’un l’a trouvé, il l’a récupéré à Bagnères de Luchon !</span></sup>, V et P.</p>

<p>Ils tentent le Petit Vignemale demain matin, je m’engage à les suivre, sans promesse sur le reste de la journée: l’étape d’aujourd’hui leur a semblé facile, reposante, alors qu’elle m’a tué. J’avais pour plan de bivouaquer n’importe où avant Gavarnie, faire mes courses le lendemain matin, et potentiellement suivre la HRP. Je crois maintenant que c’est ce que je vais faire. Marcher seul et sans contraintes m’attire plus pour l’instant. Quitte à retrouver ce groupe plus tard. Ces gens sont tellement chill, j’aurais aimé pouvoir discuter correctement avec eux. Ils sont tous plus agés que moi, ont un job, se connaissent depuis quelques semaines ; ils ont leurs codes. Un orage éclate à quelques kilomètres. La pluie s’intensifie.</p>

<figure>
    <img src="/assets/hrp/hrp-day1.jpg" />
    <figcaption>Le refuge des Oulettes, au pied du Vignemale</figcaption>
</figure>

<h3 id="day-2-oulettes-de-gaubes-to-gavarnie">Day 2: Oulettes de Gaubes to Gavarnie</h3>

<p>Gros orage hier soir qui a secoué la tente déjà mal pitchée. It held. La tente de V&amp;P a eu moins de chance, un anneau de cassé<sup class="tooltip">[4]<span>je les reverrai le lendemain, puis plus</span></sup>. Ce matin, les grégaires du GR n’étaient plus deter à faire le Petit Vignemale, à cause du vent, entre autres. Je les quitte, gravis la Hourquette d’Ossoue puis le Petit Vignemale. 3 km de haut ! À la descente je retrouve les gens à Baysellance, je n’y reste pas. Ils me rattrapent vite. Premier river crossing pieds trempés. Au barrage d’Ossoue, pause de quelques heures pour moi. Le groupe repart direction Gavarnie, moi je vise le refuge d’avant. La fin de journée est superbe: personne ou presque sur le GR. Au moment de poser ma tente au refuge de la Grange de Holle, M débarque, puis les autres: ils n’ont pas trouvé de place au camping de Gavarnie. Je suis décidé à prendre la HRP le lendemain.</p>

<figure>
    <img src="/assets/hrp/hrp-day2.jpg" />
    <figcaption>Descente dans la vallée d'Ossoue</figcaption>
</figure>

<h3 id="day-3-gavarnie-to-héas">Day 3: Gavarnie to Héas</h3>

<p>Extra solide journée, que j’avais pensé courte. Ascension épuisante à la Hourquette d’Alans le matin, après avoir fait des courses à Gavarnie, village au final sympathique. Acheté une serviette et de la bouffe: erreur, oublié le fromage, et la florette passe mal. Après-midi dans le cirque d’Estaube, chaleur écrasante. J’y ai vu une marmotte en mangeant, et un curieux animal que j’aurais dit félin<sup class="tooltip">[5]<span>sûrement une autre marmotte</span></sup>.</p>

<p>Lessive avant le lac des Gloriettes, destination de picnic prisée. J’opte pour contourner le lac par le Sud, évitant le bitume pris par la HRP, direction cabane des Groutes. J’apprends alors ce qu’IGN appelle “autre chemin”, et que celui-ci remonte à 2000m… Glorieuse solitude avec magnifique vue. Descente raide à Héas où je pose ma tente au camping attenant à l’auberge de la Munia, les Cairns. Ambiance <em>Comme un Avion</em>, et surtout, une douche ! Boutons aux épaules, je change de haut. Tant pis pour mes bras, je mettrais de la crème solaire<sup class="tooltip">[6]<span>pas de manches sur celui-ci :/</span></sup>. Demain objectif Lacs de Barroude, et le lendemain, Parzán.</p>

<figure>
    <img src="/assets/hrp/hrp-day3.jpg" />
    <figcaption>Pitch perfect au camping d'Héas</figcaption>
</figure>

<h3 id="day-4-héas-to-lacs-de-barroude">Day 4: Héas to Lacs de Barroude</h3>

<p>Seulement la moitié d’une étape selon Cicerone, effectivement j’arrive tôt. C’est une très bonne chose, les lacs de Barroude sont mon idée du paradis sur Terre. Le mur de Barroude surplombe deux lacs issus d’un glacier. La vallée est verdoyante et accidentée. Des centaines de moutons. Le plus grand des deux lacs possède quelques îlots et deux presqu’îles. À mon arrivée, j’aperçois un randonneur chillant sur la première presqu’île, sorte de volcan miniature. Je m’installe sur la suivante. Baignade, juste une minute, l’eau est fraîche. Je me suis écorché l’orteil gauche :/ Fissuré ? J’ai ici un gros cal douloureux depuis un moment. En venant ici, la Hourquette d’Héas était à couper le souffle. Un trou de deux mètres dans la roche sépare deux vallées, avec un souffle extraordinaire. Comme si tout le vent d’une vallée passait là pour rejoindre l’autre. Le dénivelé ne m’a pas posé de soucis aujourd’hui. Habitude ? Demain, Parzán, journée très facile si mon orteil coopère.</p>

<p>Vraiment pas satisfait de mon système de bouffe. Selon mes estimations, j’ai mangé 1614 kcal aujourd’hui… Avec fromage ça aurait été 2000 kcal, toujours pas ouf.</p>

<figure>
    <img src="/assets/hrp/hrp-day4.jpg" />
    <figcaption>Départ des lacs de Barroude</figcaption>
</figure>

<h3 id="day-5-barroude-to-parzán">Day 5: Barroude to Parzán</h3>

<p>Passage en Espagne au Port de Barroude ce matin ! Des vieux m’indiquaient des isards sur mon chemin mais nulle trace. Bain près de la grande route - la chaleur me tue. Courses ratées à Parzán: 3600 kcal pour trois jours → j’allais mourrir de faim. Demain je pars à Bielsa chercher un distributeur de billets, plus de bouffe, de quoi me sustenir et payer le repas d’auberge à Viados ou Soula. La montée depuis Parzán a été rude, entre la chaleur, le manque d’eau et l’incertitude sur la proximité d’un spot campable, que je finis par trouver près d’une cascade<sup class="tooltip">[7]<span>bouchons d’oreil recommandés</span></sup>. À Parzán, j’ai acheté des limes, qui viennent à bout de mon problème d’orteil.</p>

<p>Parler espagnol me fait perdre 50 points de QI.</p>

<figure>
    <img src="/assets/hrp/hrp-day5.jpg" />
    <figcaption>En s'éloignant de Parzán, dans la montée</figcaption>
</figure>

<h3 id="day-6-parzán-to-añes-cruces">Day 6: Parzán to Añes Cruces</h3>

<p>Impossible de rejoindre Bielsa: le stop ne marche pas. Provisions++ à Parzán (oréos !) et je remonte cette satané pente. Un vieux me demande un indice: ai-je vu sa femme<sup class="tooltip">[8]<span> non ! j’ai pas eu le fin mot de l’histoire</span></sup> ? Le camping près du refuge de Viados est fermé<sup class="tooltip">[9]<span>et pas moyen de camper au refuge: il est en dessous de 2000m</span></sup>… Peu importe, j’avais placé cette journée sous le signe de la sueur et de la fatigue. Je push jusqu’à la cabane d’Añes Cruces, dépassant quatre espagnols que je retrouverai là bas. Devant la cabane, des Allemands m’indiquent la présence d’un français à l’intérieur. Un HRPiste UL ! On discute de gear, de plans, du terrain<sup class="tooltip">[10]<span>”s’il fallait démonter sa tente chaque fois que y’a de l’orage, on aurait pas fini” - une philosophie que j’apprécie vraiment</span></sup>. Il tente de la faire en 3 semaines mais me dis prendre du retard. 6h30 tous les jours → midi sieste. Beaucoup d’expérience et de détermination, c’est impressionnant. Après une nuit passée près d’une souris dans le mur, à mal dormir sur le sol de la cabane, il me dit s’appeler T, T d’un certain forum de randonnée :)</p>

<figure>
    <img src="/assets/hrp/hrp-day6.jpg" />
    <figcaption>Le soleil se lève sur Añes Cruces, vu de très loin</figcaption>
</figure>

<h3 id="day-7-añes-cruces-to-loudenvielle">Day 7: Añes Cruces to Loudenvielle</h3>

<p>La route vers le refuge de la Soula en France est magnifique. Je monte au port d’Aigües Tortes par des “sentiers” rocailleux, géniaux. En haut, un désert de scree impressionnant, des grands oiseaux, et une source formant une large mare. La Soula me rappelle S.T.A.L.K.E.R, le refuge est au beau milieu d’une station hydroélectrique. La suite, la partie amazonie d’Océanopolis. On voyait le nuage par dessus depuis la frontière<sup class="tooltip">[11]<span>il faisait si beau côté espagnol</span></sup>, je serai encore dedans le lendemain. Dans la forêt descendant à Port de Prats, je croise un groupe de 7 ou 8 marcheurs, qui me trollent (“pas droit de couper”) d’abord, puis qui valident mon plan camping. Cool guys. Je prends une bonne heure d’avance dans la descente et fait la moitié du chemin jusque Loudenvielle avant qu’ils me rattrapent et m’y déposent, m’indiquant le distributeur de billets.</p>

<p>Je rejoins le camping, a priori complet, où à l’accueil, K vient de prendre la dernière place, qu’il me propose de partager. Il l’avait déniché en appelant l’office du tourisme ! Incroyable type, incroyable chance pour moi. Douche, puis bonne discussion avec K. Après cette randonnée, il veut sortir de la société, vivre en nomade ; a été réalisateur, solide cinéphile. Quelqu’un de très très cool. Malchance, il renverse de la superglue sur ses lunettes ce qui le force à faire du stop jusqu’à Bagnères - “À quoi bon marcher ici si on n’y voit rien”. On échange nos numéros pour s’y retrouver.</p>

<figure>
    <img src="/assets/hrp/hrp-day7.jpg" />
    <figcaption>Brume côté français, vue du port d'Aigües Tortes</figcaption>
</figure>

<h3 id="day-8-loudenvielle-to-lacs-despingo">Day 8: Loudenvielle to Lacs d’Espingo</h3>

<p>Je ne repars du camping qu’à 11h, il pleuvait avant. Re-douche. Je croise D, passe le col d’Esquierry puis monte au lac d’Oô, montée interminable et surpeuplée. Lac qui me paraît surcoté, amoindri par sa popularité et le barrage qui s’y construit. On m’indique “eau pas potable” au refuge, et plus haut je trempe ma chaussure en en refaisant. Vers 20h j’approche d’Espingo, et un gars me rattrape. Il s’appelle J, est Australien, vit en France, est chercheur en machine learning et visite les lacs d’Espingo plus ou moins sur un coup de tête. Wow ! Chillax, on trouve un campsite au bord du second lac, derniers arrivés parmis une douzaine de campeurs. La brume couvre tout au dessus de nous, c’est magnifique. Il acheté sa tente pour l’occasion, est en jeans et baskets, mange dans une assiette<sup class="tooltip">[12]<span>respect, il allait plus vite que moi</span></sup>, ça change du monde que je croise habituellement. Je filtre, mange puis produit du Lindt: malade, je l’avais senti venir à Oô. J est allé méditer plus loin. Le matin, condensation à bloc.</p>

<figure>
    <img src="/assets/hrp/hrp-day8.jpg" />
    <figcaption>Devant un des lacs d'Espingo, sous la brume</figcaption>
</figure>

<h3 id="day-9-lacs-despingo-to-bagnères-de-luchon">Day 9: Lacs d’Espingo to Bagnères de Luchon</h3>

<p>Grand froid le matin. Couplé à la condensation, je ne sens plus mes mains après avoir rangé ma tente. Au revoir à J et je pars. Peu d’eau avant je ne sais quel col où je sèche ma tente. Après celui-ci, autoroute vers Bagnères. À Superbagnères, station de ski, je traverse un troupeau de vaches. Une femme a la même idée en sens inverse et se fait frapper des cornes d’une mère<sup class="tooltip">[13]<span>première fois que je vois ça; rien de grave</span></sup>. J’indique le GR à un père et son fils, perdus. Je me sens comme porteur de l’idéal randonnesque. À Bagnères, glace, ice tea, courses, et je croise K. On boit un godet. Il a réparé ses lunettes et prend un zéro demain. Ne me suivra pas sur la HRP: mal au genou, et donc peu rassuré par la mention “very steep descent”, E grade du guide. Une fois de plus, on se quitte. Au camping, the usual. Beaucoup de route demain avant même de commencer l’étape. Hope for the best.</p>

<figure>
    <img src="/assets/hrp/hrp-day9.jpg" />
    <figcaption>Superbagnères</figcaption>
</figure>

<h3 id="day-10-bagnères-to-col-de-mulleres">Day 10: Bagnères to Col de Mulleres</h3>

<p>Journée écrasante, physiquement mais aussi moralement. Plus de chemin pour rejoindre la HRP qu’anticipé, pas aidé par mon manque d’expérience en stop. Passé le refuge de Benasque, j’arrive à la Besurta vers 15h, début de l’étape. Ici, un monde fou, venu en bus, qui passe toutes les demi-heures. Chaleur. Passé le trou du Toro et le plateau d’Aigualluts, les touristes sont remplacés par des vaches. L’ascension devient technique, grisante. il est 17h+, je perds un peu mon sang-froid. Pas raisonnable de passer le col à cette heure-ci, d’ailleurs quand l’atteindrai-je ? Camper si tôt quand mes jambes fonctionnent et que l’ascension est si belle ? Je grimpe, repérant les lieux de bivouac possibles. Finalement, je décide que trop c’est trop, que Mulleres sera demain, et je fais demi-tour jusque près d’un étang de glacier, ~2450m. Je pitch ma tente avec des cailloux pour la première fois.</p>

<p>19h30, des espagnols passent ma tente, ils vont grimper le col. Je questionne mon choix de remettre l’ascension à demain, j’aurais finalement pu dormir en cabane. Mon corps me dit que j’ai bien fait. À moins d’un orage cette nuit, mais vu le grand ciel bleu, comment serait-ce posssible ?</p>

<p>20h+: en fait le sol sous ma tente est froid. Dehors, il fait encore chaud. J’ai pas l’expérience d’un espagnol, faut pas que je m’y compare. Maintenant je saurai.</p>

<p>Qu’est-ce que c’est beau. Seul, pas une vache, dans la vallée d’Escaleta. Demain matin sera glacial, et le soleil ne se lèvera pas sur ma tente avant 10h au moins, caché par je ne sais quelle barrière rocheuse adjacente au Tuc de Mulleres. Such is life. Pour l’instant, j’adore être ici. Je m’en veux un peu d’avoir laissé K. Je me promets de le rejoindre au moins une fois, lui payer un godet.</p>

<figure>
    <img src="/assets/hrp/hrp-day10.jpg" />
    <figcaption>Mon spot de bivouac, au bord de l'eau</figcaption>
</figure>

<h3 id="day-11-mulleres-to-lac-deth-cap-deth-port">Day 11: Mulleres to Lac deth Cap deth Port</h3>

<p>Montée au col longue. Ce que je prends d’abord pour le col est en fait de l’autre côté du Tuc, que je monte - 3010m. Un alpiniste du coin partage son expérience, il connaît tous les sommets à la ronde. Le guide disait du col “extremely steep descent”: very true. La suite est un chaos de pierres infini et une demie forêt jusqu’à Espitau de Vielha où je fais une courte sieste. Je décide de suivre le GR11 jusqu’au refuge de la Restanca, gagnant 3 heures, mais manquant Lac de Mar que le guide assurait des plus beaux. Tant pis. Par SMS, K me dit qu’il va arrêter son chemin à Bagnères, son mal de genou ne passant pas - coup dur.</p>

<p>Je commence cette étape suivante à 16h. Soleil. Forêt escarpée. Marche facile à l’ombre à partir de 18h+. Arrivé 21h20 j’atteins la Restanca où je suis content de ne pas rester: que des français en grands groupes types dayhikers. Je pousse jusqu’au lac indiqué campable par le guide. Mon arrivée dérange un groupe de quatre ou cinq isards. Leur position me révèle un bon spot de camping, solide nuit.</p>

<figure>
    <img src="/assets/hrp/hrp-day11.jpg" />
    <figcaption>Le col de Mulleres, au creux de l'anse. "Extremely steep".</figcaption>
</figure>

<h3 id="day-12-restanca-to-salardú">Day 12: Restanca to Salardú</h3>

<p>Beaucoup de touristes autour du refuge de la Restanca, les chemins sont loins d’être calmes. Picnic dans un coin sympa, m’étant trompé de chemin, suivant bêtement ma trace GPX. Deux dames (allemandes ?) m’interpellent, reconnaissant un fellow HRPiste. Elle vont également se ravitailler. On discute, je les dépasse. Ma chaussure droite a un problème: un bout de semelle se décolle. Trois bouts de duct tape tiennent jusqu’à Salardú, où, surprise, il n’y a pas de superglue. Je loge au refuge Juli Soler. La patronne a fait la HRP, parle un bon français. Seul dans une chambre pour quatre, covid oblige. Repas extra complet. Je dors mal though: chaleur, et problème de se rendre à Vielha le lendemain, mission superglue.</p>

<figure>
    <img src="/assets/hrp/hrp-day12.jpg" />
    <figcaption>Premier lac de la journée</figcaption>
</figure>

<h3 id="day-13-salardú-to-airoto">Day 13: Salardú to Airoto</h3>

<p>Je manque le bus → stop to Vielha. J’attends 9h que le supermarché ouvre → no superglue, je cherche le Casino indiqué, galère et trouve. À 10h, je prends le bus pour Salardú. 10h35, ma chaussure est réparée, on the road again. J’oublie près d’un banc mon buff, il me manque déjà. Passés les premiers Estanys de Baciver surpeuplés, je mange aux Rosaris près des chevaux, regardant ma future montagne, le Tuc de Marimanha. Approche quasi frontale d’une pente raide, ça marche. En haut, je m’aperçois que le tonnerre continue de gronder à l’Ouest, j’accélère. Le col d’Airoto requiert un peu d’escalade inversée, et j’y fait ma première chute: RAS. La section “giant boulders” ne déçoit pas. J’ai trop poussé la descente, je remonte une pente à 80% sur 100m jusqu’au prochain col: je skip le refuge dont j’entends venir des éclats de voix depuis une heure. Je trouve à camper dans la descente du col, avant l’endroit indiqué dans le guide, un tarn plus bas. J’ai peu d’eau mais je suis claqué.</p>

<figure>
    <img src="/assets/hrp/hrp-day13.jpg" />
    <figcaption>Au col d'Airoto, devant atteindre le col en haut à gauche</figcaption>
</figure>

<h3 id="day-14-airoto-to-alós-disil">Day 14: Airoto to Alós d’Isil++</h3>

<p>Dans la decente, je fais un combat de regard avec un isard. Je fais mon eau/lessive/dej au petit lac plus bas. Sur l’autre rive, quelqu’un y fait également ses affaires. En repartant, je le salue, lui demande s’il fait la HRP - “Oui, c’est la HRP”. Il est Belge, parle français. “Quel guide utilises-tu ?” “Cicerone, en anglais” “C’est moi qui l’ait écrit !” C’est Tom Martens, mon guide en personne ! Il ressemble à mon prof de maths de spé. Passionné et passionnant, me questionne sur mon chemin, mon ravitaillement, mon séjour au refuge de Salardú… Il grimpe les 3K des Pyrénées, écrit un guide sur leur ascension. N’imagine pas un été sans grimper les Pyrénées. Me montre une nouvelle route vers Alós d’Isil trouvée la veille. Me donne sa carte, on se salue. Pas pensé assez tôt à la photo, regrets là dessus. Ma journée est faite dans tous les cas.</p>

<p>Je trouve facilement cette nouvelle route, qui longe un ruisseau, et j’atteins Alós à midi. Joli et vieux village ressemblant à Méranges. En repartant, je discute avec un genre de teneur de mini-expo historique et lui donne ma carte devenue inutile du Val d’Aran, au cas où il croise quelqu’un allant dans l’autre sens. Il me prévient d’un orage pour le soir. En mangeant près de la Noguera, je l’entends déjà, et peu après je sors le kway. Vers 15h peut-être c’est le déluge et la grêle. Je marche dans le ruisseau ayant remplacé les chemins. Mon short est trempé également. Mode automatique. J’ai froids aux mains. J’atteins le petit lac à 2000m+ et plante ma tente. Je mets ce qu’il me reste de sec et m’enquiltifie, ce qui conduit à une sieste sous le restant d’orage. Je mange, trop tôt. Ressors faire de l’eau et laver mon short au passage, en caleçon. Je dors mal. Pas d’orage pourtant.</p>

<figure>
    <img src="/assets/hrp/hrp-day14b.jpg" />
    <figcaption>La carte d'un vrai</figcaption>
</figure>

<h3 id="day-15-alós-to-tavascan">Day 15: Alós to Tavascan</h3>

<p>Je rejoins un vieux HRPiste en montant le col. J’ai mis du temps à me lever: réveillé 8h45. Mes rêves, comme toutes les nuits j’ai l’impression, sont menaçants. Un L4D2 trop difficile, une rando HRP avec G&amp;S&amp;G où mes semelles se sont entièrement décollées… On parle un peu gear<sup class="tooltip">[14]<span>il a un sac katabatic !</span></sup>, je le dépasse.</p>

<p>L’estany major est grandiose, une mer intérieure avec houle: vent et pluie pendant 20 minutes. Je passe trois lacs et atteins Enric Pujol, où je me pose pour manger. S’y trouve un HRPiste sur le départ, sympa. En faisant une pause plus tard, un couple me dépasse. Fascinés par le bon état de mes chaussures. Lui a remplacé les siennes, elle va très prochainement devoir le faire. Ils sont partis de Cauterets le même jour que moi<sup class="tooltip">[15]<span>comme beaucoup de français, ils suivent un autre guide que le mien: je ne les reverrai pas</span></sup> ! J’atteins le camping de Bordes de Graus à Tavascan, un peu bruyant mais sympa.</p>

<figure>
    <img src="/assets/hrp/hrp-day15.jpg" />
    <figcaption>Les trois lacs et le refuge Enric Pujol</figcaption>
</figure>

<h3 id="day-16-tavascan-to-certascan">Day 16: Tavascan to Certascan</h3>

<p>Peu de choix à l’épicerie de Tavascan - joli village. Je prends des conserves pour la première fois, on verra. Reparti en stop, et rapidemment à Noarre. Trop rapidemment peut-être, maintenant que mon sac est très lourd: les tendons de ma cheville gauche/intérieur crissent. Pas mal de monde jusqu’à l’étang précédant le col de Certascan. Je souffre, j’ai vraiment peur que ça dure, ça mettrait fin à ma marche. Je renonce au pic à cause de ça, alors que Tom me l’avait décrit comme facile &amp; worth it. En plus de ça, ma poche de filtrage d’eau s’est fissurée ; en attendant de trouver une bouteille de Perrier<sup class="tooltip">[16]<span>K m’avait raconté avoir oublié la sienne le premier jour, crevé de soif, le premier groupe de vieux auquel il a demandé en avait une</span></sup>, plaquer ma bouteille contre le filtre fonctionne tolérablement.</p>

<p>Descente du col, je croise un type, Jamie-looking, avec un gros sac mais ne faisant pas la HRP: il se balade avec un ami<sup class="tooltip">[17]<span>j’ai envie de l’appeler Fred, malgré l’absence de ressemblance</span></sup> qu’il dit berger, portant une semaine de bouffe. On discute philosophie de la marche. Vaut-il mieux aller loin et vite, ou pas loin en prenant tout son temps ? Dans tous les cas, j’ai fais un converti à l’ultralight - même sans aller loin, autant pas se casser le dos. Ils campent au troisième lac de Romedo. En arrivant au refuge de Certascan, il photographie une pleine lune<sup class="tooltip">[18]<span>“des souvenirs pour la vie”</span></sup>. Je m’y prends un sandwich. K avait raison, pas grand chose dedans. Un peu par erreur, j’ai campé au lac d’avant celui de Jamie &amp; co - tout le coin est paradisiaque. Pour prendre leur noms et leurs ballistos<sup class="tooltip">[19]<span>ces deux là avaient les meilleurs délires</span></sup>, j’irai leur dire bonjour demain matin. Fasse Cauchy que ma jambe aille mieux alors.</p>

<figure>
    <img src="/assets/hrp/hrp-day16.jpg" />
    <figcaption>Noarre, village inaccessible par la route</figcaption>
</figure>

<h3 id="day-17-certascan-to-pla-de-boet">Day 17: Certascan to Pla de Boet</h3>

<p>Petite pluie et vent, très gros vent le matin. Je skip le salut à Fred &amp; Jamie, pas le courage d’être dehors par ce temps sans avancer. D’abord coincé derrière un grand groupe de randonneurs à la semaine dans la descente des lacs de Romedo, je vais ensuite me perdre dans une vallée sans nom où se trouve le Riu de Guiló, au Nord Nord Est quand je devais aller plein Sud… Perdu une heure, une heure et demie dans un relatif mauvais temps. Ma jambe marche ! J’ai des relances, mais dans mes deux jambes alternativement. J’ai dû abuser de la droite en compensant, puis de la gauche… Une fois dans la bonne vallée, gigantesque descente sans croiser âme qui vive, magnifique, sauvage. J’entame mes boîtes. Au fond de la vallée, au pont de Boavi, quelques touristes, familles etc… Disparus en haut de la remontée majeure.</p>

<p>Là commence un orage. Je pousse un peu plus loin que nécessaire mais cette fois ma tente est plantée avant le gros de la tempête. Terrible pitch à 5 sardines. Grêle, vent qui lance ma tente entière dans toutes les directions. La sardine arrière droite paraît prête à lâcher mais tient bon contre tout bon sens. Je finis par sortir le quilt le vent calmé. Après 1h30 de pause, je décampe. Un isard s’enfuit près des lacs de Baborte. Il est tard mais je me sens de continuer. Grande descente avec pas mal de forêt jusque Pla de Boet. 1850m, en dessous de l’altitude légale de camping, pas pensé. Un couple d’espagnols y campe déjà, 21h. Ils devaient grimper le pic d’Estats aujourd’hui mais ont remis au lendemain à cause de la météo - good call. Ils m’aident à monter ma tente.</p>

<figure>
    <img src="/assets/hrp/hrp-day17.jpg" />
    <figcaption>La montagne après l'orage</figcaption>
</figure>

<h3 id="day-18-boet-to-refuge-de-sorteny-andorre">Day 18: Boet to Refuge de Sorteny, Andorre</h3>

<p>Mal dormi, et condensation. Quelques tentes dans l’interminable montée vers Port de Boet, la frontière. Même dans la descente vers la Soucarrane je me sens assez faible. Pas plus mal au jambes que d’habitude, mais pas de puissance derrière. Grosse boîte de tortellinis pour compenser. Port de Rat, difficile alors que le chemin n’a rien d’exceptionnellement dur. Je me trompe d’ailleurs avant de commencer son ascension, je descends trop bas, de ~150m. Coup au moral qui m’aide pas non plus. Paracetamol en haut. Je descends jusqu’à la station de ski d’Arcalis en Andorre où je m’arrête profiter de la wifi, les plans de retrouvaille avec G&amp;S se montent.</p>

<p>En repartant, 16h20, je me sens à 100% voire plus. Objectif Ordino, courses. Pas certain que le bus passe où je suis sur la G3<sup class="tooltip">[20]<span>connaissant maintenant les marqueurs d’arrêts de bus, il serait passé ^^</span></sup>, je rejoins El Serrat à grande vitesse. J’attrape de justesse le bus à 20 centimes et me retrouve à Ordino, courses satisfaisantes. Fort attrait pour les hamburgers à l’affiche des restos. Charmante ville qui paraît très moderne, en tout cas très bien modernisée. Reparti en bus to El Serrat, j’atteins le refuge de la Sorteny sans soucis. S’y trouvent des lits libres (gratuits et en métal) ! Je mange comme un ogre près d’un ruisseau et dors, tardivement, pas par faute d’essayer.</p>

<figure>
    <img src="/assets/hrp/hrp-day18.jpg" />
    <figcaption>La ville d'Ordino</figcaption>
</figure>

<h3 id="day-19-sorteny-to-refuge-de-juclar">Day 19: Sorteny to Refuge de Juclar</h3>

<p>“Route-finding is very easy in Andorra” disait Tom, mais je me perds dans la première demi-heure, n’ayant pas suivi à la lettre ses instructions. Tous les chemins sur OSM n’existent pas jusqu’au bout ! Faible encore, mais mieux. Un gars me dépasse portant un sac et un vélo. Il grimpe comme moi jusqu’au col del Meners. De là, un certain nombre de touristes, mais chills. Légère fausse route (équivalente), j’approche la cabane de Coms de Jan par l’autre côté. J’y retrouve les deux HRPistes croisées avant Salardú. Elles sont belges, s’appellent N &amp; K’. On marche ensemble jusqu’à la cabana Sorda où elles s’arrêtent, 15h30.</p>

<p>Les voir m’a redonné la pêche. Après une pause, je repars pour Juclar. Dure marche de 2h30, j’attrape des coups de soleil et une cloque à l’orteil gauche, indolore. Dormir est payant à Juclar, je pensais y trouver des lits libres… Je suis short de 10€ mais la patronne m’offre nuit et souper quand même. Soupe, boeuf, riz, “there’s more if you want” - mon amour pour cette dame est infini.</p>

<figure>
    <img src="/assets/hrp/hrp-day19.jpg" />
    <figcaption>En descendant du col del Meners</figcaption>
</figure>

<h3 id="day-20-juclar-to-lhospitalet-près-landorre">Day 20: Juclar to L’Hospitalet-Près-l’Andorre</h3>

<p>Jolis lacs passé le col d’Albe. Peu de gens par ici, deux trois petits groupes. Ceux là semblent du coin, plantent leurs tentes et pêchent pour certains. Ils ont de la montée à faire depuis L’Hospitalet pourtant. Du GR, du GR, du GR… Cette ville a un style très particulier. Un pipeline qui la traverse, du bitume noir entre les veilles maisons à moitié retapées. Camping peucher though. Les plans sont faits, G&amp;S me rejoignent ici demain soir. Mon zéro day est arrivé !</p>

<figure>
    <img src="/assets/hrp/hrp-day20.jpg" />
    <figcaption>Descente vers L'Hospitalet</figcaption>
</figure>

<h3 id="day-21-lhospitalet-près-landorre">Day 21: L’Hospitalet-Près-l’Andorre</h3>

<p>Direction boulangerie pour le petit dej, espérant croiser les belges, N &amp; K’, malgré l’heure tardive - 10h. Je me trouve un banc face à la mairie et au retour les croise sur la terrace du bar-épicerie. Elles étaient je crois là à l’aller mais je les avais pas reconnues. Elles repartent en Belgique en bus dans 20 minutes ! Finirons une prochaine fois. Elles s’attendaient à croiser au gîte un groupe de trois français, je dis que je leur passerai le bonjour à l’occasion<sup class="tooltip">[21]<span>finalement jamais croisés; on ne suivait pas le même guide</span></sup> et les quitte. Lessive, siestes, douche, le bus arrive, 19h. Here they are! Avec des gros sacs, bouffe en conserve. Camping. S a crevé son tapis de sol sans cause probable, réparé avec le kit du xtherm de G.</p>

<figure>
    <img src="/assets/hrp/hrp-day21.jpg" />
    <figcaption>L'Hospitalet-Près-L'Andorre</figcaption>
</figure>

<h3 id="day-22-lhospitalet-près-landorre-to-étang-des-fourrats">Day 22: L’Hospitalet-Près-l’Andorre to Étang des Fourrats</h3>

<p>Chemin tranquille, Cauterets-sque jusqu’au refuge des Bésines où on s’arrête manger. Après-midi de belle montagne, pleine de lacs, quelques passages rocheux, et une forêt étrangement sparse d’épicéas avant les Fourrats. L’étang est juste devant le Carlit, magnifique. Tom déconnait pas. G discute avec des marmottes ; on sèche nos affaires. Plein de gens campent un peu plus bas que nous.</p>

<figure>
    <img src="/assets/hrp/hrp-day22.jpg" />
    <figcaption>Entre les Bésines et le Carlit</figcaption>
</figure>

<h3 id="day-23-étang-des-fourrats-to-saillagouse">Day 23: Étang des Fourrats to Saillagouse</h3>

<p>Carlit ! Suite à un pari sur ma frilosité jambière, je porte le short rose de S, et pour bien faire, son kway 80s-style. G, et particulièrement S, galèrent dans la montée. Elle est longue, et constante. Au sommet en 45 minutes, j’y trouve un vieux apparemment ici depuis un bon moment. Photos, on discute. Il est de Saint-Brieuc, dans l’association <a href="https://lecheminsauvage.com/">Le Chemin Sauvage</a> qui cherche à créer un chemin en V sur toute la France, évitant les grandes villes. Saint-Brieuc, Carlit, Ardennes, le V. Après 15 minutes, le sommet se charge de monde. Photos de groupe dans les nuages, en tenue, et on redescent l’autre versant. Monde fou, et tout commence à m’agacer. Les gens, leurs chiens, leurs familles, la lenteur de G&amp;S dont je ne réalise pas qu’ils sont crevés. L’atteinte des Bouillouses est misérable, sauf pour S me parlant de sa semaine mario.</p>

<p>Tout est mieux à l’étape qui suit et je retrouve une forme de civilité. Courses limitées à Bolquère, et mon parrain nous prend sur la route vers Eyne: escale chez de la famille à moi, A&amp;G’ - ça fait longtemps que je veux retourner les voir, cette rando est l’occasion de faire d’une pierre deux coups. Repas de soupe, saucisson, salade, oeufs, superbe. Lérot.</p>

<figure>
    <img src="/assets/hrp/hrp-day23.jpg" />
    <figcaption>Le Carlit, face Ouest</figcaption>
</figure>

<h3 id="day-24-saillagouse-to-noufonts">Day 24: Saillagouse to Noufonts</h3>

<p>Petit déjeuner parfait, pain beurre, confiture, miel, … Courses au marché puis à Spar, pour quatre jours. Rôti, glace à midi, c’est glorieux. Puis il est temps de partir, ~15h, G’ nous dépose à la reprise du chemin. Jolie remontée de la vallée d’Eyne. On suit Tom qui nous décrit les crêtes comme campables, et décidons de poursuivre là dessus. “Yer fond of me lucky charms!”. Coucher de soleil. On descend du col de Noufonts pour camper, bon terrain mais beaucoup de vent, dure nuit.</p>

<figure>
    <img src="/assets/hrp/hrp-day24.jpg" />
    <figcaption>Le soir sur les crêtes, après la vallée d'Eyne</figcaption>
</figure>

<h3 id="day-25-noufonts-to-pla-guillem">Day 25: Noufonts to Pla Guillem</h3>

<p>De retour sur la crête, on continue vers le pic des Batiments. Tous un peu fatigués aujourd’hui, planté de tente tardif hier soir. Du chemin à faire pourtant, nos plans nous amènent chez de la famille à S d’ici quelques jours. Tom annonçait là “for experienced hikers, the route will be pure fun”, et l’homme ne survend rien: les chemins passent à ras des crètes et sont principalement un dédale de roche en vrac. Un bon monde en haut, et pour la suite de la journée. Pause déjeuner au refuge d’Ull de Ter, il fait sacrément chaud. Passé le port de Mourens qui nous ramène en France, on a droit à de la montagne arrondie, c’est une première.</p>

<p>On s’arrête manger à Rotja, conteneur en métal servant d’abri. Pas les premiers sur place, une dame y est installée ; elle randonne une boucle dans le coin. Il fait froid, on repart, marchant vite. Rattrapés par la nuit. On cherche la cabane de Pla Guillem à la lampe torche - ça nous évite la tente, le vent et les chiens de berger. Trouvée. C’est grand, vide, bien.</p>

<figure>
    <img src="/assets/hrp/hrp-day25.jpg" />
    <figcaption>Les crêtes vers le Pic des Bastiments</figcaption>
</figure>

<h3 id="day-26-pla-guillem-to-refuge-des-cortalets">Day 26: Pla Guillem to Refuge des Cortalets</h3>

<p>Départ compliqué le matin: faute de voir un chemin, navigation à vue qui nous fait couper un temps à travers brousse. S se fait mal à la cheville, on ralenti. On se dit qu’on passera la nuit en refuge, aux Cortalets. On passe les Mariailles, pause à l’ombre avant le Canigou. Chaleur d’un autre genre, on se croirait en Corse. L’escalade de la cheminée vers le sommet est incroyable, de la roche brute, explosée à la dynamite il y a plus de cent ans. Vue sur une mer de nuages. Aux Cortalets, pas de place, malgré nos tentatives de réservations. Repli sur leurs bières, brownies &amp; chocolats.</p>

<figure>
    <img src="/assets/hrp/hrp-day26.jpg" />
    <figcaption>La cheminée du Canigou</figcaption>
</figure>

<h3 id="day-27-cortalets-to-céret">Day 27: Cortalets to Céret</h3>

<p>Grosse journée. Le plan, atteindre Arles-sur-Tech, où de la famille à S nous amènera en escale à Céret, G&amp;S y resteront. Bloqués par des vaches, pause à la maison forestière de l’Estanyol. Le chemin est effectivement forestier, bien à l’ombre, il fait beau. Plus que de la descente jusqu’à Arles, 282 m d’altitude. On en fait même une partie en courant, G&amp;S sont deter comme jamais. Première fois que je vois un chêne liège. Baignade en rivière, eau fraîche.</p>

<p>Arrivés à Arles par un labyrinthe de canaux puis finalement son cimetierre, on est accueillis par H&amp;S’, qui nous amènent à Céret en twingo. Ces anges absolus nous payent le resto ; vengeance sur les Cortalets, également appréciée chaude. H, prof d’anglais, a fait le PCT. J’espère faire de même un de ces jours, je suis sûr maintenant que j’adorerais ça. Leur maison est d’un style monumental, en mode <em>Sunset Boulevard</em>, et ils ont un hamac d’appoint - how cool is that?</p>

<figure>
    <img src="/assets/hrp/hrp-day27.jpg" />
    <figcaption>Le meilleur des deux mondes à Céret</figcaption>
</figure>

<h3 id="day-28-arles-sur-tech-to-las-illas">Day 28: Arles-sur-Tech to Las Illas</h3>

<p>Petit dej, courses, repartir seul fait bizarre. J’en oublie de remercier H&amp;S’, de retour à Arles. Je fais quelques arrangements de retour, tant que j’ai du réseau, puis grimpe. Même style de chemins qu’en arrivant, parfois comme des tobogans de roche. Une pêche d’enfer en première partie de journée. Oeufs durs à midi &lt;3. Je croise mes premiers sangliers, puis deux beaux chevaux, dans une forêt de hêtres étrangement calme. J’ai mal à l’orteil gauche en marchant, ce que j’attribue à la baignade précédente, une glissade quelconque - pas de certitude.</p>

<p>J’atteins Montalba d’Amélie, une communauté perdue dans la montagne, dont je ne verrai que la fontaine. J’y discute avec un couple de GRdistes qui m’avaient dépassé pendant ma pause déjeuner, ils me demandent si le col plus loin se campe - “open spot on a ridge” dit Tom, je n’en sais pas plus<sup class="tooltip">[22]<span>c’était finalement un superbe spot de bivouac</span></sup>. “Je vise Las Illas ce soir”, “… Vous courrez ?”. Non, j’espère m’en passer, mais c’est un challenge ^^ Je grimpe le Roc de France par une voie tout à fait innovante<sup class="tooltip">[23]<span>absurde</span></sup> après m’être trompé de chemin. Première vue de la Méditerranée, de ma vie je crois. De là, descente jusque Las Illas avec un bref et presque final passage en Espagne. Tom recommendait un joli spot de gazon, j’y trouve deux GRdistes discutant de la fin du voyage. Triste pensée.</p>

<figure>
    <img src="/assets/hrp/hrp-day28.jpg" />
    <figcaption>Du plat pays vu de Roc de France, la Méditerranée au loin</figcaption>
</figure>

<h3 id="day-29-las-illas-to-la-tagnarède">Day 29: Las Illas to La Tagnarède</h3>

<p>Un vieux me donne une foultitude d’indices pour naviguer la route type suburbs en zigzags sortant de Las Illas, je ne réussi à en retenir aucun mais arrive tout de même au bon endroit: une route de terre, “surveillée par satellite” - alright. La route est longue, longue, sans distraction. Elle mène finalement au Perthus, par une forêt de chênes lièges. Il fait une chaleur assomante.</p>

<p>Il est midi à Le Perthus, je m’arrête dans ce que je prends pour un bistrot faisant des burgers à emporter ; c’est en fait un vrai restaurant. Que mieux. Un seau de limonade, une bouteille de mayo, que demande le peuple. Je repars après une petite pause sur un banc à examiner mon orteil: on m’a entendu crier dans la montagne à plus d’une occasion, en butant sur un caillou. Je fais gaffe, et ça va. Grosse montée jusqu’au col de l’Ouillat, où je fais une heure de sieste. Je continue jusqu’au soir, tranquillement. J’ai hâte d’atteindre le pic de Sailfort, aperçu au Roc de France.</p>

<figure>
    <img src="/assets/hrp/hrp-day29.jpg" />
    <figcaption>Last wild pitch</figcaption>
</figure>

<h3 id="day-30-tagnarède-to-banyuls">Day 30: Tagnarède to Banyuls</h3>

<p>Pas de déception à Sailfort, c’est vraiment un dernier grand rempart avant la mer. Je me trompe de chemin pour en descendre, me retrouve bêtement coincé sur une face et obligé de faire demi-tour. Pas important, et ça me fait croiser le “Refuge Tomy”, un abri improbable dans un bout de roche. Mais l’eau m’inquiète un peu, j’en bois des litres et des litres. Un peu plus loin, un robinet est juste planté là. Déjeuner, sieste.</p>

<p>En recollant un morceau de semelle, un couple de randonneurs retraités me dépassent. Je les rattrape, on fait le dernier bout de route ensemble vers Banyuls. Lui finit la Trans’pyr complète en sens inverse<sup class="tooltip">[24]<span>autre façon de dire HRP, de ce que je comprends</span></sup>, sa femme l’accompagne pour la dernière semaine. Toutes les nuits en refuge, problèmes de dos oblige, mais il marche à un sacré bon rythme. Sa semelle gauche tient à un fil - littéralement.</p>

<p>Photos devant la mairie de Banyuls, on se quitte là. Baignade à la plage, l’eau est chaude comme elle n’a jamais été dans le Finistère. Office du tourisme, camping, pizza sur la plage. Au camping, je retrouve un des GRdistes de Las Illas ; j’ai par pur hasard choisi l’emplacement à côté du sien. Derniers préparatifs pour le train de retour, l’emménagement en coloc trois jours plus tard… Dur de dormir quand c’est la dernière nuit, puis cette chaleur, constante, incomparable.</p>

<figure>
    <img src="/assets/hrp/hrp-day30.jpg" />
    <figcaption>Traditionnelle photo devant la fresque, face à la plage</figcaption>
</figure>

<h2 id="postface">Postface</h2>

<p>Est-ce vraiment une thru-hike, quand il me manque la partie Hendaye-Cauterets ? Hehe je ne sais pas, mais je compte bien compléter ça ! Les Pyrénées sont vraiment superbes, et de ce que j’en entends, le Pays basque est encore bien différent des hautes Pyrénées et des Pyrénées orientales. J’aimerais aussi voir de plus près l’Aneto et tout le massif de la Maladeta, le Pic du Midi d’Ossau, et le plus possible de vallées perdues là dedans. Sachant à quel point ce type d’aventure me plaît, j’ai juste envie de pousser le challenge plus loin.</p>

<p>Dans tous les cas, je dois un grand merci à ceux qui m’ont accueilli sur le chemin, A&amp;G’, H&amp;S’, la patronne de Juclar, puis à tous ceux qui ont marché avec moi, en particulier G&amp;S qui m’ont rejoint pour une semaine superbe, et bien sûr K, à qui je dois clairement une bière :)</p>]]></content><author><name>Johan Manuel</name></author><category term="hiking" /><category term="hiking" /><category term="pyrenees" /><category term="hrp" /><category term="thru-hike" /><summary type="html"><![CDATA[En août 2020, j’ai entrepris de marcher le long de la Haute Route Pyrénéenne (HRP), depuis Cauterets en direction de Banyuls-sur-Mer, la Méditerranée. J’ai documenté le voyage au jour le jour, dont le résumé est fait ici.]]></summary></entry><entry><title type="html">Porting Doom</title><link href="/porting-doom/" rel="alternate" type="text/html" title="Porting Doom" /><published>2020-12-15T00:00:00+01:00</published><updated>2020-12-15T00:00:00+01:00</updated><id>/porting-doom</id><content type="html" xml:base="/porting-doom/"><![CDATA[<p><img src="/assets/doom.jpg" alt="Doom running on SnowflakeOS" class="thumbnail" title="The Doomslayer has awoken" />
Some things in life are inevitable. The passing of seasons, the fall of empires, and the porting of Doom to random platforms. In this post, we’ll investigate this last phenomenon, and how it came to happen in SnowflakeOS.</p>

<h2 id="doooom">Doooom</h2>

<p>“Why doom?”, no one asks? Because doom’s awesome, that’s why. Everyone, once in a while, feels like annihilating truckloads of demons with their bare hands or bare chainsaws. Don’t they? Yes, yes they do. I have a slight preference for the more recent Doom games<sup class="tooltip">[1]<span>not having experienced the firsts when they came out</span></sup> myself, but first things first, eh.<br />
Doom’s code is surprisingly self-sufficient, requiring mostly just<sup class="tooltip">[2]<span>this was not trivial :/</span></sup> a working libc, but being a relatively big program, it’s a perfect stress test for any platform. Running doom is proof of being able to run awesomeness.</p>

<p>Last time I tried porting doom (to be precise, <a href="https://github.com/ozkl/doomgeneric">doomgeneric</a>), I admitted defeat, promising myself I’d get back at it with better tools. So, what did it take? Simple things:</p>
<ul>
  <li>loading a larger file system: /u/TheMonax pointed out a simple fix for a stupidity of mine that limited the size of grub modules I could load, fixed <a href="https://github.com/29jm/SnowflakeOS/commit/7d9494271329675e5f13a378012c58b301199cbd">here</a></li>
  <li>making static variables part of userspace executables, aka <code class="language-plaintext highlighter-rouge">PROGBITS</code>: this way, the kernel doesn’t need to guess how much memory the program needs for its globals and statics, it’s all accounted for in the program size<sup class="tooltip">[3]<span>of course, an ELF loader would be the better fix</span></sup>; changed in <a href="https://github.com/29jm/SnowflakeOS/commit/889b92c82311bb996428a2a6a5ef078d7b31953e">this commit</a></li>
  <li>support for command line arguments: doom could have run without, but I wanted those anyway, added <a href="https://github.com/29jm/SnowflakeOS/commit/88ecc9ad0d5f865b07472ad3bd12f8f6665edab9">here</a></li>
  <li>many file-related functions: <code class="language-plaintext highlighter-rouge">chdir</code>, <code class="language-plaintext highlighter-rouge">remove</code>, <code class="language-plaintext highlighter-rouge">rename</code>, <code class="language-plaintext highlighter-rouge">ftell</code>/<code class="language-plaintext highlighter-rouge">fseek</code>, <code class="language-plaintext highlighter-rouge">fflush</code>, <code class="language-plaintext highlighter-rouge">stat</code>… added in various commits all over the place</li>
  <li>string formatting functions: <code class="language-plaintext highlighter-rouge">sprintf</code> &amp; co, which I added by porting <a href="https://github.com/nothings/stb/blob/master/stb_sprintf.h">stb_printf</a> in <a href="https://github.com/29jm/SnowflakeOS/commit/3b55ee5bd97c606feb61457edd9bc0cdba67cc61">this commit</a></li>
  <li>fewer bugs: one caused a buffer overflow in the ext2 driver when reading a file at an offset, fixed (<a href="https://github.com/29jm/SnowflakeOS/commit/aa3ca5024a965d7195eba1203d04a2e1877cfb37">here</a>), another was in <code class="language-plaintext highlighter-rouge">strncpy</code><sup class="tooltip">[4]<span>egregious, I know</span></sup>, fixed in <a href="https://github.com/29jm/SnowflakeOS/commit/ad444d3dec854d737e44df26ab18fb5edd7a955a">this commit</a>…</li>
  <li>working 64-bit arithmetic: simple things like 64-bit division is compiled by gcc as a call to <code class="language-plaintext highlighter-rouge">__divdi3(int64_t a, int64_t b)</code>, which I had at first implemented as… <code class="language-plaintext highlighter-rouge">return a / b</code>, which did not quite work out. gcc provides an implementation in libgcc, but this time I decided I’d rather have the source for those<sup class="tooltip">[5]<span>probably a bad idea</span></sup>, and included <a href="https://github.com/glitchub/arith64">arith64</a> <a href="https://github.com/29jm/SnowflakeOS/commit/92e21abe8246b15ab6d33a4d2f996032a6b5696e">there</a>.</li>
</ul>

<p>Once everything’s there, all that’s left to do is make small adjustments to the Makefile so that doom gets linked with the same linker script and assembly prologue as the other apps, and voilà!</p>

<p>Doom compiles and runs with no hacks at all on our part. All credits to John Carmack :)</p>

<figure>
    <video controls="">
    <source src="/assets/doom.mp4" type="video/mp4" />
    </video> 
    <figcaption>It's not that just that I'm bad, there's also this old "keyboard drops keypresses" thing... ;)</figcaption>
</figure>

<h2 id="that-which-is-not-doom-but-is-still-cool">That which is not doom but is still cool</h2>

<p>Me remembering that porting doom was an option is pretty recent relative to this blog post. Other things were done!</p>

<p>SnowflakeOS now <a href="https://github.com/29jm/SnowflakeOS/commit/b9510a491fbd88fa86445308884dfe52cb57a427">prints stacktraces</a>, with function names if it crashes in the kernel. This has been super useful for all subsequent development, and at little cost, too. All that’s required code-wise is listed in the <a href="https://wiki.osdev.org/Stack_Trace">wiki</a>, and adding symbols to that takes just little more; here’s what’s done in SnowflakeOS:</p>
<ol>
  <li>at link time, grab the kernel’s symbol map generated by the linker: <code class="language-plaintext highlighter-rouge">ld ... -Map=linker.map</code></li>
  <li>declutter it with awk magic: <code class="language-plaintext highlighter-rouge">awk '$1 ~ /0x[0-9a-f]{16}/ {print substr($1, 3), $2}' linker.map &gt; symbols.map</code>, yielding lines like “0xabcdef some_func”</li>
  <li>load it as a grub module, though I’ll change it at some point so that it’s loaded from the file system</li>
  <li>when traversing stack frames, look up each address in that file and print the corresponding symbol</li>
</ol>

<p>A contributor wanted to work on a new process scheduler, which would have been near impossible given the spaghetti-like nature of this subsystem then, so I took this opportunity to <a href="https://github.com/29jm/SnowflakeOS/commit/da2cd0987b54b38ef61a03e210a6e79eed5cac06">refactor the process code</a>, which is now scheduler-independent. Schedulers implement a generic interface, basically a <code class="language-plaintext highlighter-rouge">sched_next</code>, <code class="language-plaintext highlighter-rouge">sched_add</code> and <code class="language-plaintext highlighter-rouge">sched_exit</code> functions, and the process switching code deals with those. This design looks sufficient to cover our use-cases, but we’ll have to see how well it accomodates something other than a round robin scheduler.<br />
Making this change was <em>hard</em>. At some point, nothing worked anymore and I had no idea why. I took a deep dive into the whole thing again, like I had for some of the older posts on here, and as soon as I understood it again, it started working. I fixed some bugs in the process, or rather, things that worked by accident. For instance, my clock in the bochs emulator became fast, which it turns out is the normal behavior. No idea what was happening before. There was also <a href="https://github.com/29jm/SnowflakeOS/commit/b1b4dd4c79c21d86e037e5d34e57dff393e9f47c">this cool bug</a>, in which the code worked in all but <code class="language-plaintext highlighter-rouge">-O2+</code> builds, due to me being dumb and gcc doing god-like work.</p>

<p>We now have a virtual file system! That means we can seamlessly mix different file systems into a single folder hierarchy, by mounting them wherever we want. As it happens, the only file system we have support for is ext2, so this feature was tested with two ext2 images. I also briefly made a fake<sup class="tooltip">[6]<span>no idea of the terminology here, but think /proc stuff</span></sup> file system whose files were the open windows of the wm, mounted on /wm, and processes owning windows owned the corresponding file descriptors, so that when they exited, <code class="language-plaintext highlighter-rouge">close</code> was called on the window files, automatically closing the windows. I like the idea, but the implementation was a bit too hacky so I didn’t keep it, though I think it’ll resurface later.</p>

<p>This one is big to me: <code class="language-plaintext highlighter-rouge">stdout</code> <a href="https://github.com/29jm/SnowflakeOS/commit/f426ba00fc5905b0c91e28b8edfaef6c78f52cfd">is a thing</a>. Ever wonder what the hell <code class="language-plaintext highlighter-rouge">stdout</code> is, how it works? For the longest time this was entirely unclear to me. I still don’t have a definitive answer on linux, but on SnowflakeOS, I’ve found a way to do it that makes sense to me. By default, a process inherits the file descriptors of its parent - as is tradition - including the one referring to <code class="language-plaintext highlighter-rouge">stdout</code>. But say, the first process, it doesn’t inherit anything, it has to acquire an <code class="language-plaintext highlighter-rouge">stdout</code>. In SnowflakeOS, a process can declare<sup class="tooltip">[7]<span>through a syscall</span></sup> itself as being a “terminal”, which gives it this somewhat special file descriptor, <code class="language-plaintext highlighter-rouge">stdout</code>: it refers to a file that can handle read/write operations but is entirely in memory, as a circular buffer. The app that declared itself as a terminal is then expected to <em>read</em> from <code class="language-plaintext highlighter-rouge">stdout</code>, and do something with it, like draw its content in its window. Child processes<sup class="tooltip">[8]<span>there’s technically no such thing in SnowflakeOS, but a process does start another</span></sup> inherit this exact <code class="language-plaintext highlighter-rouge">stdout</code>, thus calling <code class="language-plaintext highlighter-rouge">fprintf(stdout, "stuff")</code> writes to the circular buffer that the parent terminal is reading from. With one or many terminals, it all works out.</p>
<figure>
    <img src="/assets/cat.jpg" />
    <figcaption>There's a prompt problem, yes, because hacks</figcaption>
</figure>

<p>On the UI side, the <code class="language-plaintext highlighter-rouge">calc</code> app finally works! It’s been sitting there, its interface done but not connected to anything, but no longer, thanks to <a href="https://github.com/the-grue">@the-grue</a>’s work, who also contributed the new mouse cursor that you can see in the doom video!</p>
<figure>
    <img src="/assets/calc.jpg" />
    <figcaption>No dimension hardcoded here</figcaption>
</figure>

<p>Finally, I’ve finally taken some time to read (gnu) <a href="https://www.gnu.org/software/make/manual/make.html">make’s documentation</a><sup class="tooltip">[9]<span>it’s very well written, fwiw</span></sup> properly, and I fixed a few remaining issues with files being rebuilt for no reason, most importantly regenerating the ISO, which is one of the longest operation of the build.</p>

<h2 id="that-which-did-not-fit-in-the-other-categories">That which did not fit in the other categories</h2>

<p>I’ve begun working on some documentation for the project, things that would help someone understand the project and contribute, which would be awesome. There’s now a <a href="https://github.com/29jm/SnowflakeOS/blob/master/CONTRIBUTING.md">CONTRIBUTING.md</a> that goes through the usual points. It also describes how to setup <code class="language-plaintext highlighter-rouge">clang-format</code>, another new addition that I too will abide by. I even used it to format doom’s source, making it a bit more comfortable to debug. There is also a project <a href="https://github.com/29jm/SnowflakeOS/wiki">wiki</a>, two pages now, but more will come with documentation on the various subsystems and how they interact. If a topic you’d like to see covered is missing, I take requests :)</p>

<p>Unrelatedly, SnowflakeOS bugs out on real hardware/virtual box (see issue <a href="https://github.com/29jm/SnowflakeOS/issues/18">#18</a>). I haven’t given this bug my full attention yet, but I bet it’ll be <em>pretty hard</em> to figure that one out. If anyone reading this has any tips, I’ll take them all!</p>

<p>Doom was a long term goal, so, what next? I’m not out of ideas yet, here are a few to end this post:</p>
<ul>
  <li>ACPI support: includes a switch to multiboot2</li>
  <li>A hierarchy of processes, <code class="language-plaintext highlighter-rouge">fork</code> &amp; friends</li>
  <li>A better desktop</li>
  <li>Hard disk support</li>
  <li>Fixing some of these <code class="language-plaintext highlighter-rouge">TODO</code>s…</li>
</ul>

<p>Hopefully one of those will be done by next post, see you then :)</p>]]></content><author><name>Johan Manuel</name></author><category term="development" /><category term="osdev" /><category term="hobby-os" /><category term="c" /><summary type="html"><![CDATA[Some things in life are inevitable. The passing of seasons, the fall of empires, and the porting of Doom to random platforms. In this post, we’ll investigate this last phenomenon, and how it came to happen in SnowflakeOS.]]></summary></entry><entry><title type="html">Filesystems for dummies</title><link href="/filesystems-for-dummies/" rel="alternate" type="text/html" title="Filesystems for dummies" /><published>2020-10-17T00:00:00+02:00</published><updated>2020-10-17T00:00:00+02:00</updated><id>/filesystems-for-dummies</id><content type="html" xml:base="/filesystems-for-dummies/"><![CDATA[<p><img src="/assets/sos-corpo2.jpg" alt="Showing off the flex new background" class="thumbnail" title="The dummy in question is the author, fwiw" />
Welcome to a new post from this very irregular blog! After busy summer holidays spent hiking in the Pyrenees, college has begun again and with it, the peace required for osdev work to resume. Last time I worked on SnowflakeOS, I’d gone all in on UI work, left unfinished and unpolished. Having entirely forgotten about that work, I booted up the project and thought: “why no files? let there be files”, and now, files sort of are. Let’s see how they work, and how they don’t.</p>

<p>But first, take a look at that whole new logo, designed by the magnificent <a href="https://github.com/sylvain-kern">sylvain-kern</a> &lt;3 The sign of a new era of prosperity for SnowflakeOS, to be sure.</p>

<h2 id="a-disk-wherefore-ſhis-demonic-inſtrument">A disk? Wherefore ſhis demonic inſtrument?</h2>

<p>The fact is, we don’t have a disk driver of any kind. Those don’t look fun to me right now, the easiest thing to write would be an ATA PIO driver, which is an old an decrepit standard<sup class="tooltip">[1]<span>I’ll end up loving it at some point &lt;3</span></sup>. I deciced I wouldn’t bother for now, having a much faster short-term solution in mind: loading the filesystem as a GRUB module.</p>

<p>This is just a matter of generating the filesystem with <code class="language-plaintext highlighter-rouge">mkfs.ext2</code> and placing it in the modules directory. The kernel then sees it as just another module, so we make an exception for it and feed it into the ext2 driver.</p>

<p><em>Edit 19/10/2020:</em> while the problem described in the following parapgraph was indeed present in SnowflakeOS at the time of writing, it was in fact trivial to solve, and solved in <a href="https://github.com/29jm/SnowflakeOS/commit/7d9494271329675e5f13a378012c58b301199cbd">7d94942</a>. Thanks /u/TheMonax :)</p>

<p>The thing with modules in SnowflakeOS though is that they can’t exceed a certain size, around 3 MiB. The reason for that is that the physical memory manager stores its bitmap just after the kernel and its modules in memory, and when the PMM runs, the kernel has 4 MiB mapped for itself. If modules grow too large, the bitmap ends up unmapped and <em>fun</em> things happen. And it’s too late then to map more memory: paging code has to be able to allocate physical pages, which requires a valid bitmap, etc…</p>

<p>All in all, our disk shall take the form of a pointer to a large area of memory containing the filesystem. In order to facilitate the transition to a real disk later, I decided to constrain myself to block sized reads and writes, hopefully that’s how they work, modulo their block size.</p>

<h2 id="ext2-fundamentals">Ext2 fundamentals</h2>

<figure>
    <img src="/assets/files-app.png" />
    <figcaption>I tried my hand at a file explorer...</figcaption>
</figure>

<p>In the beginning, there was the block. The block is the unit of size in an ext2 filesystem: they divide the volume into parts of equal size<sup class="tooltip">[3]<span>1 KiB per block in my tests</span></sup>, much like how pages are the unit of division for memory. They’re also a form of addressing, because blocks are numbered, and data is always pointed to in the form of a block number.</p>

<p>Blocks contain the filesystem structures themselves: something called the superblock that contains properties of the filesystem, the allocation bitmaps for blocks themselves, for inodes… Inodes, what are they? They’re a structure somewhere in a specific block, described by an inode number, that describes a file, with a file being either a regular file, a directory, or something more esoteric entirely that could still conceivably be called a file by unix gurus. Inodes structures are of fixed size though; the actual file data is only pointed to by block pointers in the structures. For maximum fun and fragmentation potential, this block pointer isn’t simply a block pointer and a length, no, rather it’s twelve direct block pointers, a pointer to a block containing a list of block pointers, a pointer to a block containing pointers to other blocks containing block pointers, and another level of that on top. Allocating such blocks is the very definition of <a href="https://github.com/29jm/SnowflakeOS/blob/3ec5e7113e425b6ff6b9e775f5d65ca545558f49/kernel/src/misc/ext2.c#L402-L508">elegance</a><sup class="tooltip">[2]<span>sarcasm, please send help</span></sup>.</p>

<p>Anyway, the rest is most beautifully described by the online book <a href="http://www.nongnu.org/ext2-doc/ext2.html">The Second Extended Filesystem</a> by Dave Poirier, and less beautifully and exhaustively described by the current <code class="language-plaintext highlighter-rouge">ext2</code> code in SnowflakeOS, <a href="https://github.com/29jm/SnowflakeOS/blob/3ec5e7113e425b6ff6b9e775f5d65ca545558f49/kernel/src/misc/ext2.c">here</a>.</p>

<p>Let’s take a look at the userspace side of files now, here are the calls a <code class="language-plaintext highlighter-rouge">fopen</code> call triggers right now:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">1</span><span class="p">.</span> <span class="n">fopen</span><span class="p">(</span><span class="s">"/some/path"</span><span class="p">,</span> <span class="s">"r"</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">FILE</span><span class="o">*</span><span class="p">;</span>
<span class="mi">2</span><span class="p">.</span> <span class="err">↪</span> <span class="n">syscall2</span><span class="p">(</span><span class="n">SYS_OPEN</span><span class="p">,</span> <span class="n">path</span><span class="p">,</span> <span class="n">O_READ</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">fd</span><span class="p">;</span>
<span class="mi">3</span><span class="p">.</span>  <span class="err">↪</span> <span class="n">syscall_open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">O_READ</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">eax</span> <span class="o">=</span> <span class="n">fd</span><span class="p">;</span>
<span class="mi">4</span><span class="p">.</span>   <span class="err">↪</span> <span class="n">proc_open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">O_READ</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">fd</span><span class="p">;</span>
<span class="mi">5</span><span class="p">.</span>    <span class="err">↪</span> <span class="n">fs_open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">O_READ</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">fd</span><span class="p">;</span>
<span class="mi">6</span><span class="p">.</span>     <span class="err">↪</span> <span class="n">ext2_open</span><span class="p">(</span><span class="n">path</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">inode</span><span class="p">;</span>
</code></pre></div></div>

<p>Quite a few layers to this particular onion, and it’s bound to get worse as abstractions replace hardcoded choices. In particular, a VFS<sup class="tooltip">[3]<span>Virtual File System</span></sup> is still missing, though maybe it could live in the <code class="language-plaintext highlighter-rouge">fs</code> layer here, in <a href="https://github.com/29jm/SnowflakeOS/blob/3ec5e7113e425b6ff6b9e775f5d65ca545558f49/kernel/src/misc/ext2.c#L402-L508">fs.c</a>.</p>

<p>With the basics down, SnowflakeOS can now load its wallpaper from disk instead of hardcoding it in a header file; it’s cleaner but the real improvement is the compilation speed: parsing a 2.3 MiB header file takes time. Also, a file explorer was quickly thrown together, resulting in the design hellspawn pictured above.</p>

<h2 id="bugs-in-the-machinery">Bugs in the machinery</h2>

<p><em>Warning: this part gets technical</em></p>

<p>As usual, I’ve had a healthy dose of madness-inducing bugs this session. Most of them affected a particularly fundamental part of the OS, the memory side of things. I always have mixed feelings about those: one one hand I’m thankful to have found them, on the other hand, it makes me realise that before fixing them, SnowflakeOS ran basically by pure chance. That should be my tag line honestly, “SnowflakeOS, the only luck-powered OS in existence”.</p>

<p>The first bug appeared with the introduction of <a href="https://github.com/29jm/SnowflakeOS/commit/d120ecfcd3226c3fe74ac92b4267db608b3f7187">ext2 read support</a>. Suddenly, my terminal app started crashing when I removed a <code class="language-plaintext highlighter-rouge">printf</code> call from its source. Upon inspection, I found that it crashed because the program code I loaded was, in that case, random garbage<sup class="tooltip">[4]<span>as opposed to the actual code, which is the regular kind of garbage</span></sup>. Turned out I was doing something dumb, <code class="language-plaintext highlighter-rouge">memcpy</code>ing the code from the physical address given by GRUB, when I should have been copying it from the kernel memory in higher half. Indeed, I only identity map around 1 MiB at the start of physical memory, but I map a whole 4 MiB large page of it to higher half addresses starting at <code class="language-plaintext highlighter-rouge">0xC0000000</code>. The GRUB module containing my terminal code ended up outside of this identity mapped memory, and so reading from it resulted<sup class="tooltip">[5]<span>how it didn’t crash is a mystery to me</span></sup> in random garbage being loaded.<br />
Now, while the terminal still crashed in some cases, it ran under specific conditions<sup class="tooltip">[6]<span>bugs seem to live in 1e6-dimensional space</span></sup>. But a character or two got scrambled, much like it did <a href="/advanced-memory-allocation/">back in the day</a>. Now this one was a quick fix; I’d had my head in memory code for a whole day at that point, and re-reading <code class="language-plaintext highlighter-rouge">pmm</code> code I noticed I wasn’t marking module memory as taken, only the kernel’s. What that implies is that the <code class="language-plaintext highlighter-rouge">pmm</code> is free to allocate this memory, which in SnowflakeOS’s case it does, when starting other processes or allocating new page tables.</p>

<p>Understandably, crashing under any condition is not reasonable; a bug persisted. Like the previous commit message hinted at, I started investigating <code class="language-plaintext highlighter-rouge">malloc</code> code, which seemed to cause the crash. Debugging that code isn’t a very pleasant thought to me; this is the realm of pointer arithmetic, raw memory shaped into blocks by sheer willpower, not stuff to mess with. And I trusted that code, too, so having to debug it was disappointing.<br />
My standard, first-approach mode of debugging with <code class="language-plaintext highlighter-rouge">printf</code> was out of question here: adding a printf made the crash disappear in most cases; I resorted once again to the ever-trustful (if slow) bochs<sup class="tooltip">[7]<span>I have one gigantic complaint about it though: bringing up the stack or page tables makes it freeze entirely now, and it didn’t use to be the case. I haven’t gone through the motions of finding out if a change in my code is at fault or if it’s really bochs though. But really, why would it crash? It can give me a linear dump of my stack, why would it crash displaying it slightly differently?</span></sup>. How peculiar, my static, global variable to the last allocated block was initialised to a non-zero, random-looking value, which caused the initialisation code to be skipped, leading to a segfault when traversing the block list.<br />
It was my understanding that static variables lived in the program code I was generating, for instance, I thought that if I’d added a static array of size 4 KiB, my executable would grow by that much. I knew that wasn’t the case for “standard” executable files in ELF format for instance, but for some reason I’d assumed that flat binaries worked like that, for my convenience. They don’t! To explain the rest of this bug, let me quote myself, in <a href="https://github.com/29jm/SnowflakeOS/commit/bad19b3c081ed56d7127cda3276b0d18438d7dd6">this commit</a>:</p>

<blockquote>
  <p>Alright, I learned something today:</p>
  <ul>
    <li>flat binaries can use addresses past their size to store static variables,</li>
    <li>there’s no way to tell how much memory a flat binary expects to have
for static variables.</li>
  </ul>

  <p>The bug was very much related to the aforementionned cool facts. It so
happened that my terminal program was 0x2ff1 bytes long, juuust short of
three pages, and it placed the global, static variable <code class="language-plaintext highlighter-rouge">used_memory</code> at
address 0x3000. But when loading the program, I allocated three pages
for it, so that program’s malloc memory pool ended up starting at…
0x3000 exactly. On malloc(n), <code class="language-plaintext highlighter-rouge">used_memory</code> was increased by n, thus the
first block’s <code class="language-plaintext highlighter-rouge">next</code> member got assigned n instead of staying null. What
does the next allocation check? If the previous block has a successor.
Guess what? it does, it’s located at… n. And so, the allocator
returned an address corresponding to garbage at the beginning of the
program’s code… Ah, the marvels of osdev.</p>
</blockquote>

<p>A real, proper fix would require an executable format a bit less primitive than raw binaries, but I haven’t gotten to that point yet. What I did was to allocate one more page than needed by the code, and <code class="language-plaintext highlighter-rouge">memset</code> it all to zero to ensure proper initialisation. I really need to get going on an ELF parser, I doubt that a real-world program would be satisfied by a page worth of static variables.</p>

<p>The troubles weren’t over yet though, as a very similar bug happened shortly after fixing that last one: after adding <code class="language-plaintext highlighter-rouge">strncmp</code> to my libc, my terminal stopped working <em>again</em>. Adding a function to my libc has one effect: increasing program size, and therefore the total space occupied by GRUB modules. This time though, everything appeared to be in order. No memory corruption, but a crash while mapping pages in <code class="language-plaintext highlighter-rouge">malloc</code>’s initialisation. This crash happened at a specific iteration of the loop in charge of mapping a span of pages; it made no sense, the code was correct, dammit. As explained in <a href="https://github.com/29jm/SnowflakeOS/commit/7cf702e74a2f697e97554a3c7a001792e2180bdf">this commit</a>, I figured it out after taking a day off. Paging code being correct, the physical memory manager had to be at fault<sup class="tooltip">[9]<span>everything’s obvious in retrospect</span></sup>. From there, it was a quick fix: I noticed an odd looking calculation:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">pmm_deinit_region</span><span class="p">(</span><span class="kt">uintptr_t</span> <span class="n">addr</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">size</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">uint32_t</span> <span class="n">base_block</span> <span class="o">=</span> <span class="n">addr</span><span class="o">/</span><span class="n">PMM_BLOCK_SIZE</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">num</span> <span class="o">=</span> <span class="n">size</span><span class="o">/</span><span class="n">PMM_BLOCK_SIZE</span><span class="p">;</span>
    <span class="p">...;</span>
</code></pre></div></div>

<p>It’s not obvious unless you’ve been bitten by it before, but the issue is on the third line. When you have thirteen eggs, and twelve eggs per box, you need two boxes. Yet, <code class="language-plaintext highlighter-rouge">13 / 12 == 1</code>. But if by chance your number of eggs was a multiple of 12, you would’ve had no bug, which I guess was the case until now. Anyway, I already had a function to deal with that in <a href="https://github.com/29jm/SnowflakeOS/blob/21f066197e9d4a2b0c13b91393bd3ae060f7a6c3/kernel/include/kernel/sys.h#L24-L32">sys.h</a>:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* When you can't divide a person in half.
 */</span>
<span class="k">static</span> <span class="kt">uint32_t</span> <span class="nf">divide_up</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="n">n</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">d</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="o">%</span> <span class="n">d</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">n</span> <span class="o">/</span> <span class="n">d</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="mi">1</span> <span class="o">+</span> <span class="n">n</span> <span class="o">/</span> <span class="n">d</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The same mistake appeared a few times in my <code class="language-plaintext highlighter-rouge">pmm</code> code, a sign of its age really. This fixed, everything was back in order. All of this was a real test of patience, though very much necessary to keep the project going, and very useful for me to get back into the lower level details of memory management.</p>

<h2 id="clang--ubsan">Clang &amp; UBSan</h2>

<p>Having spent a good deal of time tracking down bugs, I thought it good to prioritize setting up a few tools to catch them more easily, or even prevent them. Setting up ubsan in particular had been on my todo list for a while: it’s a compiler tool that pimps up your code to catch undefined behaviors at runtime, and for some reason I thought it was a clang-exclusivity, so I set out to make my Makefile compiler-agnostic and clang-proof.</p>

<h3 id="clang">Clang</h3>

<p>First thing I did was replacing <code class="language-plaintext highlighter-rouge">CC</code> with <code class="language-plaintext highlighter-rouge">clang</code>, and check the results. The results were mostly linker errors. Surprisingly, <code class="language-plaintext highlighter-rouge">clang</code> seems to call out to the system’s <code class="language-plaintext highlighter-rouge">gcc</code> for a lot of things<sup class="tooltip">[10]<span>which I’ve forgotten</span></sup>, and it uses the system’s <code class="language-plaintext highlighter-rouge">ld</code> too. Anyway, I basically had to do four things:</p>
<ul>
  <li>Use <code class="language-plaintext highlighter-rouge">ld</code> for compilation phases instead of <code class="language-plaintext highlighter-rouge">CC</code>: <code class="language-plaintext highlighter-rouge">clang</code> calls <code class="language-plaintext highlighter-rouge">ld</code> there, but with somewhat crap arguments, it’s far simpler to call <code class="language-plaintext highlighter-rouge">ld</code> directly and have control over them,</li>
  <li>Call <code class="language-plaintext highlighter-rouge">as</code> directly, not <code class="language-plaintext highlighter-rouge">CC</code>: while <code class="language-plaintext highlighter-rouge">clang</code> can compile GNU assembly, it wasn’t keen on doing so with the specific options I wanted to give it,</li>
  <li>Remove <code class="language-plaintext highlighter-rouge">-lgcc</code> from <code class="language-plaintext highlighter-rouge">LDFLAGS</code>: I don’t remember why it was there in the first place,</li>
  <li>Add <code class="language-plaintext highlighter-rouge">-target i386-pc-none-eabi -m32 -mno-mmx -mno-sse -mno-sse2</code> to <code class="language-plaintext highlighter-rouge">CFLAGS</code>: the first two are to tell <code class="language-plaintext highlighter-rouge">clang</code> to cross-compile, the last three prevent it from assuming too much about our instruction set<sup class="tooltip">[11]<span>it used SSE instructions to compile <code class="language-plaintext highlighter-rouge">printf</code>…</span></sup>.</li>
</ul>

<p>Somewhere in the conversion process I learned that gcc also had ubsan support… No matter! Clang support brings something very very welcome: the possibility to test and develop SnowflakeOS without having to compile a cross-compiler<sup class="tooltip">[12]<span>To be honest, I’ve never tried to compile SnowflakeOS with my system’s gcc</span></sup>.</p>

<p>Using <code class="language-plaintext highlighter-rouge">clang</code> is now as simple as uncommenting the relevant lines in the main Makefile!</p>

<h3 id="ubsan">UBSan</h3>

<p>Enabling usbsan, on linux for instance, is as simple as adding <code class="language-plaintext highlighter-rouge">-fsanitize=undefined</code> to your compiler flags. When cross-compiling however, you can’t do that, you need to implement its (thankfully compact) <a href="https://github.com/29jm/SnowflakeOS/blob/5a0b82feb7c16e08778c5248f39127c18eecadcc/libc/src/ubsan.c">runtime</a>. This runtime is just the collection of functions that’ll get called when some type of undefined behavior is detected.<br />
A typical handler looks something like that:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">__ubsan_handle_out_of_bounds</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">data</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">index</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">ubsan_out_of_bounds_data_t</span><span class="o">*</span> <span class="n">d</span> <span class="o">=</span> <span class="p">(</span><span class="n">ubsan_out_of_bounds_data_t</span><span class="o">*</span><span class="p">)</span> <span class="n">data</span><span class="p">;</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"[ubsan] out of bounds at index %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="p">(</span><span class="kt">uint32_t</span><span class="p">)</span> <span class="n">index</span><span class="p">);</span>
    <span class="n">ub_panic_at</span><span class="p">(</span><span class="o">&amp;</span><span class="n">d</span><span class="o">-&gt;</span><span class="n">location</span><span class="p">,</span> <span class="s">"out of bounds"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>It instantly caught an out of bounds error in my keyboard driver, and the fact that my kernel stacks weren’t aligned to 4 bytes, two pretty cool results. Also, it complained about <code class="language-plaintext highlighter-rouge">NULL</code> pointer dereferencing in my process loading code, which is fair enough, so I moved my userspace’s entry point to <code class="language-plaintext highlighter-rouge">0x1000</code> for good measure<sup class="tooltip">[13]<span>feeling more and more guilty about not having an ELF loader right now :/</span></sup>. For what it’s worth, you can get the structures and prototypes of whatever’s missing from gcc’s <a href="https://github.com/gcc-mirror/gcc/blob/master/libsanitizer/ubsan/ubsan_handlers.h">source here</a>.</p>

<h2 id="apart-from-that">Apart from that…</h2>

<h3 id="paint">Paint</h3>

<figure>
    <img src="/assets/not-paint.png" />
    <figcaption>Still not as glorious as the real thing, yes, but now with an icon</figcaption>
</figure>

<p>I pushed some UI code I’d written at the beginning of summer to github, and rewrote the paint clone with it; this is close to the entirety of <a href="https://github.com/29jm/SnowflakeOS/blob/3ec5e7113e425b6ff6b9e775f5d65ca545558f49/modules/src/paint.c">its code</a>:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ui_app_t</span> <span class="n">paint</span> <span class="o">=</span> <span class="n">ui_app_new</span><span class="p">(</span><span class="n">win</span><span class="p">,</span> <span class="n">fd</span> <span class="o">?</span> <span class="n">icon</span> <span class="o">:</span> <span class="nb">NULL</span><span class="p">);</span>

<span class="n">vbox_t</span><span class="o">*</span> <span class="n">vbox</span> <span class="o">=</span> <span class="n">vbox_new</span><span class="p">();</span>
<span class="n">ui_set_root</span><span class="p">(</span><span class="n">paint</span><span class="p">,</span> <span class="p">(</span><span class="n">widget_t</span><span class="o">*</span><span class="p">)</span> <span class="n">vbox</span><span class="p">);</span>

<span class="n">hbox_t</span><span class="o">*</span> <span class="n">menu</span> <span class="o">=</span> <span class="n">hbox_new</span><span class="p">();</span>
<span class="n">menu</span><span class="o">-&gt;</span><span class="n">widget</span><span class="p">.</span><span class="n">flags</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="n">UI_EXPAND_VERTICAL</span><span class="p">;</span>
<span class="n">menu</span><span class="o">-&gt;</span><span class="n">widget</span><span class="p">.</span><span class="n">bounds</span><span class="p">.</span><span class="n">h</span> <span class="o">=</span> <span class="mi">20</span><span class="p">;</span>
<span class="n">vbox_add</span><span class="p">(</span><span class="n">vbox</span><span class="p">,</span> <span class="p">(</span><span class="n">widget_t</span><span class="o">*</span><span class="p">)</span> <span class="n">menu</span><span class="p">);</span>

<span class="n">canvas</span> <span class="o">=</span> <span class="n">canvas_new</span><span class="p">();</span>
<span class="n">vbox_add</span><span class="p">(</span><span class="n">vbox</span><span class="p">,</span> <span class="p">(</span><span class="n">widget_t</span><span class="o">*</span><span class="p">)</span> <span class="n">canvas</span><span class="p">);</span>

<span class="k">for</span> <span class="p">(</span><span class="kt">uint32_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">colors</span><span class="p">)</span><span class="o">/</span><span class="k">sizeof</span><span class="p">(</span><span class="n">colors</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">color_button_t</span><span class="o">*</span> <span class="n">cbutton</span> <span class="o">=</span> <span class="n">color_button_new</span><span class="p">(</span><span class="n">colors</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">canvas</span><span class="o">-&gt;</span><span class="n">color</span><span class="p">);</span>
    <span class="n">hbox_add</span><span class="p">(</span><span class="n">menu</span><span class="p">,</span> <span class="p">(</span><span class="n">widget_t</span><span class="o">*</span><span class="p">)</span> <span class="n">cbutton</span><span class="p">);</span>
<span class="p">}</span>

<span class="n">button_t</span><span class="o">*</span> <span class="n">button</span> <span class="o">=</span> <span class="n">button_new</span><span class="p">(</span><span class="s">"Clear"</span><span class="p">);</span>
<span class="n">button</span><span class="o">-&gt;</span><span class="n">on_click</span> <span class="o">=</span> <span class="n">on_clear_clicked</span><span class="p">;</span>
<span class="n">hbox_add</span><span class="p">(</span><span class="n">menu</span><span class="p">,</span> <span class="p">(</span><span class="n">widget_t</span><span class="o">*</span><span class="p">)</span> <span class="n">button</span><span class="p">);</span>

<span class="k">while</span> <span class="p">(</span><span class="n">running</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">ui_handle_input</span><span class="p">(</span><span class="n">paint</span><span class="p">,</span> <span class="n">snow_get_event</span><span class="p">());</span>
    <span class="n">ui_draw</span><span class="p">(</span><span class="n">paint</span><span class="p">);</span>
    <span class="n">snow_render_window</span><span class="p">(</span><span class="n">win</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="doom-a-reality-check">Doom, a reality check</h3>

<p>So, now that I had a some form of file support (who needs more than read/write anyway?), I thought trying a doom port was in my reach. I got it to compile easily enough, I got it to link pretty quickly too, I even managed to get it to run without crashing, but this is when I realized I didn’t have the drive space to even store one <code class="language-plaintext highlighter-rouge">.iwad</code> file Doom requires. Anyway, the executable loops doing nothing at all, not even opening a window.</p>

<p>It was quite cool to confront my libc with a real-world use of a libc. I’m missing all of the <code class="language-plaintext highlighter-rouge">sprintf</code> family of functions, many file operations like <code class="language-plaintext highlighter-rouge">rename</code>, <code class="language-plaintext highlighter-rouge">remove</code>, <code class="language-plaintext highlighter-rouge">fseek</code>… but overall, it could be worse. Something that’s getting pressing here is disk space. GRUB modules limit me to 4 MiB, my lack of png decoding makes just the background 2.3 MiB large, it’s getting cramped in there. A disk driver will have to be attempted sooner rather than later.</p>

<p>Osdev work just never ends. It’s the software version of gardening.</p>

<h4 id="on-that-note">On that note…</h4>

<p>I’ll see you next time, hopefully talking about ELF loading and disk drivers and as many bugs as possible :)</p>]]></content><author><name>Johan Manuel</name></author><category term="development" /><category term="osdev" /><category term="hobby-os" /><category term="c" /><summary type="html"><![CDATA[Welcome to a new post from this very irregular blog! After busy summer holidays spent hiking in the Pyrenees, college has begun again and with it, the peace required for osdev work to resume. Last time I worked on SnowflakeOS, I’d gone all in on UI work, left unfinished and unpolished. Having entirely forgotten about that work, I booted up the project and thought: “why no files? let there be files”, and now, files sort of are. Let’s see how they work, and how they don’t.]]></summary></entry><entry><title type="html">A terminal, at last</title><link href="/a-terminal-at-last/" rel="alternate" type="text/html" title="A terminal, at last" /><published>2020-05-24T00:00:00+02:00</published><updated>2020-05-24T00:00:00+02:00</updated><id>/a-terminal-at-last</id><content type="html" xml:base="/a-terminal-at-last/"><![CDATA[<p><img src="/assets/sos-paint.jpg" alt="picture" class="thumbnail" title="The author wasn't fucking around _this_ week." />
Let’s face it, it’s hard to get excited about a kernel from just barebone demos of barely functional systems. In this article, I propose a radical solution: actually implementing useful userspace programs, namely a terminal, and ye old copycat of paint.</p>

<p>But wait, you scream, last time you didn’t have moving windows, a mouse pointer, or the ability to get input from userspace, how come now we’re implementing a terminal?<br />
Right, right, let’s get it over with.</p>

<h2 id="communicating-with-the-wm">Communicating with the wm</h2>

<p>If you recall <a href="/of-mice-and-keyboards/">this post</a>, our keyboard and mouse drivers were in pretty fine shape, useless though they were then. We’ll use them right away to register callbacks, set to fire when a key is pressed, or released, and when something happens to the mouse:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">init_wm</span><span class="p">()</span> <span class="p">{</span>
    <span class="p">...;</span>
    <span class="n">mouse_set_callback</span><span class="p">(</span><span class="n">wm_mouse_callback</span><span class="p">);</span>
    <span class="n">kbd_set_callback</span><span class="p">(</span><span class="n">wm_kbd_callback</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Let’s talk about how we handle mouse events first. We want clicks to push windows to the front, we want mouse drags to move windows, and we want some of these events to reach the affected window. I say some, because of a choice made in the wm: windows can be dragged from anywhere in their rectangle, and they can’t opt out. Therefore there can’t be drag events within windows, the cursor doesn’t move relative to the dragged window anyway.<br />
All of that takes some code, about ninety lines total. It ain’t thrilling, so I won’t force it upon your eyes, dear reader, but it’s <a href="https://github.com/29jm/SnowflakeOS/blob/1dd718af791f4fd869e94f6ecbc9b98d1a3f6c9c/kernel/src/misc/wm/wm.c#L407-L501">right here</a> if needed.</p>

<p>A point more worthy of being highlighted is how exactly the wm tells a window “you’ve been clicked here”, or “the mouse moved from here to there”. In most (all?) other OS, the wm is in userspace and uses IPC and some bespoke protocol to speak with its clients.<br />
In SnowflakeOS, clients poll the wm using a system call, <code class="language-plaintext highlighter-rouge">snow_get_event</code>, which is really a call to <code class="language-plaintext highlighter-rouge">syscall2(SYS_WM, WM_CMD_EVENT, wm_event_t* event)</code><sup class="tooltip">[<a href="https://github.com/29jm/SnowflakeOS/blob/1dd718af791f4fd869e94f6ecbc9b98d1a3f6c9c/snow/src/gui.c#L80-L91">1</a>]<span>all wm commands go through the SYS_WM syscall</span></sup>. The structure returned, <code class="language-plaintext highlighter-rouge">wm_event_t</code>, is a copy of the kernel-side, per-window <code class="language-plaintext highlighter-rouge">wm_event_t</code> object, and contains approximately the following fields:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
    <span class="kt">uint32_t</span> <span class="n">mask</span><span class="p">;</span> <span class="c1">// describes valid fields</span>
    <span class="n">wm_mouse_event_t</span> <span class="n">mouse</span><span class="p">;</span>
    <span class="n">wm_kbd_event_t</span> <span class="n">kbd</span><span class="p">;</span>
<span class="p">}</span> <span class="n">wm_event_t</span><span class="p">;</span>
</code></pre></div></div>

<p>where <code class="language-plaintext highlighter-rouge">mouse</code> and <code class="language-plaintext highlighter-rouge">kbd</code> are defined in somewhat obvious ways in <a href="https://github.com/29jm/SnowflakeOS/blob/1dd718af791f4fd869e94f6ecbc9b98d1a3f6c9c/kernel/include/kernel/uapi/uapi_wm.h">uapi_wm.h</a><sup class="tooltip">[2]<span>thanks to Protura’s dev for suggesting this way of sharing kernel headers!</span></sup>. So, clients poll the wm for this structure, and that’s the client side of it. The kernel, wm side of it is pretty straightforward: the mouse and keyboard callbacks fill <code class="language-plaintext highlighter-rouge">mask</code> and other fields as needed, for instance in the keyboard handler:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">wm_kbd_callback</span><span class="p">(</span><span class="n">kbd_event_t</span> <span class="n">event</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">windows</span><span class="o">-&gt;</span><span class="n">count</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">wm_window_t</span><span class="o">*</span> <span class="n">win</span> <span class="o">=</span> <span class="n">list_last</span><span class="p">(</span><span class="n">windows</span><span class="p">);</span>

        <span class="n">win</span><span class="o">-&gt;</span><span class="n">event</span><span class="p">.</span><span class="n">mask</span> <span class="o">|=</span> <span class="n">WM_EVENT_KBD</span><span class="p">;</span>
        <span class="n">win</span><span class="o">-&gt;</span><span class="n">event</span><span class="p">.</span><span class="n">kbd</span><span class="p">.</span><span class="n">keycode</span> <span class="o">=</span> <span class="n">event</span><span class="p">.</span><span class="n">keycode</span><span class="p">;</span>
        <span class="n">win</span><span class="o">-&gt;</span><span class="n">event</span><span class="p">.</span><span class="n">kbd</span><span class="p">.</span><span class="n">pressed</span> <span class="o">=</span> <span class="n">event</span><span class="p">.</span><span class="n">pressed</span><span class="p">;</span>
        <span class="n">win</span><span class="o">-&gt;</span><span class="n">event</span><span class="p">.</span><span class="n">kbd</span><span class="p">.</span><span class="n">repr</span> <span class="o">=</span> <span class="n">event</span><span class="p">.</span><span class="n">repr</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>and events are cleared once they’ve been queried:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">wm_get_event</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="n">win_id</span><span class="p">,</span> <span class="n">wm_event_t</span><span class="o">*</span> <span class="n">event</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">wm_window_t</span><span class="o">*</span> <span class="n">win</span> <span class="o">=</span> <span class="n">wm_get_window</span><span class="p">(</span><span class="n">win_id</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">win</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="o">*</span><span class="n">event</span> <span class="o">=</span> <span class="n">win</span><span class="o">-&gt;</span><span class="n">event</span><span class="p">;</span>
    <span class="n">memset</span><span class="p">(</span><span class="o">&amp;</span><span class="n">win</span><span class="o">-&gt;</span><span class="n">event</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">wm_event_t</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I’m sure some of you are wondering where event queues fit in there. I’ve heard of them, but I don’t practice<sup class="tooltip">[3]<span>I’ll make a queue in the keyboard driver for sure</span></sup>. What do I do if two keys are pressed, and the event structure hasn’t been retrieved in between? I drop a keypress.</p>

<p>Just take your time when writing stuff, it’s the zen of SnowflakeOS.</p>

<h2 id="a-terminal">A terminal</h2>

<figure>
    <img src="/assets/terminal.png" />
    <figcaption>The classic, the irreplaceable.</figcaption>
</figure>

<p>Here’s something I haven’t done in a long time, if ever: writing C apps. There’s a very real difference between kernel code and application code, I think. And I suck at writing actual C programs. C feels much less friendly to me in this space, I guess in large part because I don’t know what I’m doing. For instance I’ve felt the need to make my own string object and related functions. C’s basic string handling functions are notoriously terrible though, I’m surprised it’s the first time I felt the need to replace them.</p>

<p>Anyway, what does a terminal do? Usually, it runs a single program, the shell, and it handles printing its output nice and tidy, which includes handling escape sequences (we had those, <a href="https://github.com/29jm/SnowflakeOS/commit/1e1c45656152f428ebfdc0b919bd08a1074580b0">a long time ago</a>), line wrapping, sometimes mouse handling I guess. I actually don’t known much more than that. SnowflakeOS has an <code class="language-plaintext highlighter-rouge">exec</code> system call<sup class="tooltip">[<a href="https://github.com/29jm/SnowflakeOS/commit/6444c76b939975f91c96133118b1ea7dd58ecfe3">4</a>]<span>this is new too! not much work. this one is an actual link btw.</span></sup>, but no concept of child process, forks, etc… so we can’t have that traditional terminal-shell separation just yet. For the same reason, external processes won’t be able to print to the terminal, only builtin commands. Well <em>whatever</em><sup class="tooltip">[5]<span>though this will be fixed</span></sup>, we just want a fancy way to start paint ;)</p>

<p>The terminal follows the same basic structure of every graphical app ever: handle input, redraw, loop. Let’s take a look at input handling:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">while</span> <span class="p">(</span><span class="n">running</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">wm_event_t</span> <span class="n">event</span> <span class="o">=</span> <span class="n">snow_get_event</span><span class="p">(</span><span class="n">win</span><span class="p">);</span>
    <span class="p">...;</span>
    <span class="c1">// Skip kbd handling &amp; redrawing in this case</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">key_pressed</span> <span class="o">||</span> <span class="n">focus_changed</span><span class="p">))</span> <span class="p">{</span>
        <span class="k">continue</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="p">...;</span>

    <span class="k">switch</span> <span class="p">(</span><span class="n">event</span><span class="p">.</span><span class="n">kbd</span><span class="p">.</span><span class="n">keycode</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">case</span> <span class="n">KBD_ENTER</span><span class="p">:</span>
        <span class="k">case</span> <span class="n">KBD_KP_ENTER</span><span class="p">:</span>
            <span class="n">str_append</span><span class="p">(</span><span class="n">text_buf</span><span class="p">,</span> <span class="n">input_buf</span><span class="o">-&gt;</span><span class="n">buf</span><span class="p">);</span>
            <span class="n">str_append</span><span class="p">(</span><span class="n">text_buf</span><span class="p">,</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
            <span class="n">interpret_cmd</span><span class="p">(</span><span class="n">text_buf</span><span class="p">,</span> <span class="n">input_buf</span><span class="p">);</span>
            <span class="n">input_buf</span><span class="o">-&gt;</span><span class="n">buf</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span>
            <span class="n">input_buf</span><span class="o">-&gt;</span><span class="n">len</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
            <span class="n">str_append</span><span class="p">(</span><span class="n">text_buf</span><span class="p">,</span> <span class="n">prompt</span><span class="p">);</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="k">case</span> <span class="n">KBD_BACKSPACE</span><span class="p">:</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">input_buf</span><span class="o">-&gt;</span><span class="n">len</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">input_buf</span><span class="o">-&gt;</span><span class="n">buf</span><span class="p">[</span><span class="n">input_buf</span><span class="o">-&gt;</span><span class="n">len</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span>
                <span class="n">input_buf</span><span class="o">-&gt;</span><span class="n">len</span> <span class="o">-=</span> <span class="mi">1</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="nl">default:</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">key</span><span class="p">.</span><span class="n">keycode</span> <span class="o">&lt;</span> <span class="n">KBD_KP_ENTER</span><span class="p">)</span> <span class="p">{</span>
                <span class="kt">char</span> <span class="n">str</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="s">"</span><span class="se">\0\0</span><span class="s">"</span><span class="p">;</span>
                <span class="n">str</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">key</span><span class="p">.</span><span class="n">repr</span><span class="p">;</span>
                <span class="n">str_append</span><span class="p">(</span><span class="n">input_buf</span><span class="p">,</span> <span class="n">str</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>That’s pretty ugly switch, let me explain. I chose to have two text buffers to represent the text displayed on the terminal. One, <code class="language-plaintext highlighter-rouge">input_buf</code>, contains the current line of user input, and it can be edited, and the other, <code class="language-plaintext highlighter-rouge">text_buf</code>, contains all the rest. It makes sense then that pressing enter would append the input to the static buffer, interpret that input, and clear it. Currently the terminal handles editing through backspace, no arrow keys yet. Other keys aren’t special (we just require they be printable), and are just appended to the input buffer.<br />
My <code class="language-plaintext highlighter-rouge">str_t</code> type makes things a bit ugly there, I haven’t taken the time to make enough utility functions. It’s useful because  <code class="language-plaintext highlighter-rouge">str_t</code> has no length limit as <code class="language-plaintext highlighter-rouge">str_append</code> reallocates if needed, which happens when <code class="language-plaintext highlighter-rouge">text_buf</code> grows.</p>

<p>The next step is to interpret the input we got, which is done here:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">interpret_cmd</span><span class="p">(</span><span class="n">str_t</span><span class="o">*</span> <span class="n">text_buf</span><span class="p">,</span> <span class="n">str_t</span><span class="o">*</span> <span class="n">input_buf</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">char</span><span class="o">*</span> <span class="n">cmd</span> <span class="o">=</span> <span class="n">input_buf</span><span class="o">-&gt;</span><span class="n">buf</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">strcmp</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="s">""</span><span class="p">))</span> <span class="p">{</span>
        <span class="k">return</span><span class="p">;</span>
    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">strcmp</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="s">"uname"</span><span class="p">))</span> <span class="p">{</span>
        <span class="n">str_append</span><span class="p">(</span><span class="n">text_buf</span><span class="p">,</span> <span class="s">"SnowflakeOS 0.5</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">strcmp</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="s">"ls"</span><span class="p">))</span> <span class="p">{</span>
        <span class="n">str_append</span><span class="p">(</span><span class="n">text_buf</span><span class="p">,</span> <span class="s">"No."</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">strcmp</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="s">"dmesg"</span><span class="p">))</span> <span class="p">{</span>
        <span class="kt">char</span> <span class="n">klog</span><span class="p">[</span><span class="mi">2048</span><span class="p">];</span>
        <span class="n">sys_info_t</span> <span class="n">info</span><span class="p">;</span>
        <span class="n">info</span><span class="p">.</span><span class="n">kernel_log</span> <span class="o">=</span> <span class="n">klog</span><span class="p">;</span>
        <span class="n">syscall2</span><span class="p">(</span><span class="n">SYS_INFO</span><span class="p">,</span> <span class="n">SYS_INFO_LOG</span><span class="p">,</span> <span class="p">(</span><span class="kt">uintptr_t</span><span class="p">)</span> <span class="o">&amp;</span><span class="n">info</span><span class="p">);</span>
        <span class="n">str_append</span><span class="p">(</span><span class="n">text_buf</span><span class="p">,</span> <span class="n">klog</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">strcmp</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="s">"exit"</span><span class="p">))</span> <span class="p">{</span>
        <span class="n">running</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="kt">int32_t</span> <span class="n">ret</span> <span class="o">=</span> <span class="n">syscall1</span><span class="p">(</span><span class="n">SYS_EXEC</span><span class="p">,</span> <span class="p">(</span><span class="kt">uintptr_t</span><span class="p">)</span> <span class="n">cmd</span><span class="p">);</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">str_append</span><span class="p">(</span><span class="n">text_buf</span><span class="p">,</span> <span class="s">"invalid command: "</span><span class="p">);</span>
            <span class="n">str_append</span><span class="p">(</span><span class="n">text_buf</span><span class="p">,</span> <span class="n">cmd</span><span class="p">);</span>
            <span class="n">str_append</span><span class="p">(</span><span class="n">text_buf</span><span class="p">,</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Spot the funky <code class="language-plaintext highlighter-rouge">dmesg</code> here! The API to get the kernel log is dreadful, but now I can actually debug things from within QEMU:</p>

<p><img src="/assets/dmesg.jpg" alt="isn't it glorious" title="the calc is a lie" /></p>

<p>Finally, we get to redrawing the terminal. We have the tools to draw text, we have the text, let’s do this.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">redraw</span><span class="p">(</span><span class="n">str_t</span><span class="o">*</span> <span class="n">text_buf</span><span class="p">,</span> <span class="k">const</span> <span class="n">str_t</span><span class="o">*</span> <span class="n">input_buf</span><span class="p">)</span> <span class="p">{</span>
    <span class="cm">/* Title bar, background... */</span>
    <span class="p">...;</span>

    <span class="cm">/* Text content */</span>

    <span class="c1">// Temporarily concatenate the input and a cursor</span>
    <span class="n">str_append</span><span class="p">(</span><span class="n">text_buf</span><span class="p">,</span> <span class="n">input_buf</span><span class="o">-&gt;</span><span class="n">buf</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">cursor</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">str_append</span><span class="p">(</span><span class="n">text_buf</span><span class="p">,</span> <span class="s">"_"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kt">char</span><span class="o">*</span> <span class="n">text_view</span> <span class="o">=</span> <span class="n">text_buf</span><span class="o">-&gt;</span><span class="n">buf</span><span class="p">;</span>
    <span class="kt">char</span><span class="o">*</span> <span class="n">line_buf</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">max_col</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
    <span class="kt">uint32_t</span> <span class="n">n_lines</span> <span class="o">=</span> <span class="n">count_lines</span><span class="p">(</span><span class="n">text_buf</span><span class="p">);</span>
    <span class="kt">uint32_t</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">22</span><span class="p">;</span> <span class="c1">// below the title bar</span>

    <span class="c1">// Scroll the view as needed</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">n_lines</span> <span class="o">&gt;</span> <span class="n">max_line</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">uint32_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n_lines</span> <span class="o">-</span> <span class="n">max_line</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">text_view</span> <span class="o">=</span> <span class="n">scroll_view</span><span class="p">(</span><span class="n">text_view</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// Draw line by line, wrapping text</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">text_view</span> <span class="o">&lt;</span> <span class="o">&amp;</span><span class="n">text_buf</span><span class="o">-&gt;</span><span class="n">buf</span><span class="p">[</span><span class="n">text_buf</span><span class="o">-&gt;</span><span class="n">len</span><span class="p">])</span> <span class="p">{</span>
        <span class="kt">char</span><span class="o">*</span> <span class="n">lf</span> <span class="o">=</span> <span class="n">strchrnul</span><span class="p">(</span><span class="n">text_view</span><span class="p">,</span> <span class="sc">'\n'</span><span class="p">);</span>
        <span class="kt">uint32_t</span> <span class="n">line_len</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint32_t</span><span class="p">)</span> <span class="p">(</span><span class="n">lf</span> <span class="o">-</span> <span class="n">text_view</span><span class="p">);</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">line_len</span> <span class="o">&lt;=</span> <span class="n">max_col</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">strncpy</span><span class="p">(</span><span class="n">line_buf</span><span class="p">,</span> <span class="n">text_view</span><span class="p">,</span> <span class="n">line_len</span><span class="p">);</span>
            <span class="n">line_buf</span><span class="p">[</span><span class="n">line_len</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span>
            <span class="n">text_view</span> <span class="o">+=</span> <span class="n">line_len</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// +1 discards linefeed</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">strncpy</span><span class="p">(</span><span class="n">line_buf</span><span class="p">,</span> <span class="n">text_view</span><span class="p">,</span> <span class="n">max_col</span><span class="p">);</span>
            <span class="n">line_buf</span><span class="p">[</span><span class="n">max_col</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span>
            <span class="n">text_view</span> <span class="o">+=</span> <span class="n">max_col</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="n">snow_draw_string</span><span class="p">(</span><span class="n">win</span><span class="o">-&gt;</span><span class="n">fb</span><span class="p">,</span> <span class="n">line_buf</span><span class="p">,</span> <span class="n">margin</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">text_color</span><span class="p">);</span>

        <span class="n">y</span> <span class="o">+=</span> <span class="n">char_height</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// De-concatenate the input</span>
    <span class="n">text_buf</span><span class="o">-&gt;</span><span class="n">buf</span><span class="p">[</span><span class="n">text_buf</span><span class="o">-&gt;</span><span class="n">len</span> <span class="o">-</span> <span class="n">input_buf</span><span class="o">-&gt;</span><span class="n">len</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span>
    <span class="n">text_buf</span><span class="o">-&gt;</span><span class="n">len</span> <span class="o">-=</span> <span class="n">input_buf</span><span class="o">-&gt;</span><span class="n">len</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">cursor</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">text_buf</span><span class="o">-&gt;</span><span class="n">buf</span><span class="p">[</span><span class="n">text_buf</span><span class="o">-&gt;</span><span class="n">len</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span>
        <span class="n">text_buf</span><span class="o">-&gt;</span><span class="n">len</span> <span class="o">-=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// Update the window</span>
    <span class="n">snow_render_window</span><span class="p">(</span><span class="n">win</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Let’s break it down. First, we make sure to work with only one text buffer by merging the input with the static text, we don’t care to distinguish those here. We even include a blinking cursor for sanity reasons. Then, we make sure to draw the “bottom” of the buffer by scrolling if needed. If what this means is unclear, try spamming commands in a newly opened terminal, it’ll scroll the view when you reach the bottom. Finally, we draw the text line by line, keeping in mind that a line either ends with a line feed, or by reaching the right side of the window.<br />
If you’re like me and didn’t know about it, <code class="language-plaintext highlighter-rouge">strchrnul</code> returns the the address of the trailing null byte in a string if nothing matches the query, instead of returning <code class="language-plaintext highlighter-rouge">NULL</code> like the classic <code class="language-plaintext highlighter-rouge">strchr</code> would.</p>

<p>All in all, we now have a working terminal.</p>

<h2 id="paint">Paint</h2>

<figure>
    <img src="/assets/paint.png" />
    <figcaption>"Snowflakistan". I blame my mouse driver.</figcaption>
</figure>

<p>Kernel development is an art, or so some think. I enjoy consensus and wanted to address the concerns of naysayers, and with that goal in mind set out to make my kernel art-able. What program then could be better suited to artistic expression than the humble paint?</p>

<p>The code here has even fewer bells and whistles than the terminal, and I won’t dare bore you with it. Get input, if click, toggle drawing, if mouse move and drawing, draw a line, loop. Note that because of window dragging mechanics you can’t keep pressing the mouse to draw, you have to release it. I think it’s not totally senseless UX-wise<sup class="tooltip">[6]<span>it mostly is though, yes</span></sup>, as you’re free to focus only on the movement of your hand.</p>

<p>But, but, but, the five cool, old-school buttons on the top left are of some interest. I’ve started making a GUI toolkit, and what you’re really seeing here are three color picker buttons and two normal buttons in a horizontal layout. This code is really a work in progress by any measure, but working on it has been pretty interesting so far. I’m taking a GTK-like approach, because it’s the only C GUI toolkit I’ve ever touched. Thankfully I barely remember any of it, so I’m free to make the same mistakes it did, but also new and cooler ones.</p>

<p>In our paint version, this toolkit is used in a very hackish way, but it gives a general idea of how things will look:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* Setup the UI */</span>
<span class="n">hbox_t</span><span class="o">*</span> <span class="n">picker</span> <span class="o">=</span> <span class="n">hbox_new</span><span class="p">();</span>
<span class="c1">// No parent/root widget, so we position it manually</span>
<span class="n">picker</span><span class="o">-&gt;</span><span class="n">widget</span><span class="p">.</span><span class="n">bounds</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">fb_x</span> <span class="o">+</span> <span class="mi">10</span><span class="p">;</span>
<span class="n">picker</span><span class="o">-&gt;</span><span class="n">widget</span><span class="p">.</span><span class="n">bounds</span><span class="p">.</span><span class="n">y</span> <span class="o">=</span> <span class="n">fb_y</span><span class="p">;</span>

<span class="c1">// `color` is defined earlier</span>
<span class="n">hbox_add</span><span class="p">(</span><span class="n">picker</span><span class="p">,</span> <span class="p">(</span><span class="n">widget_t</span><span class="o">*</span><span class="p">)</span> <span class="n">color_button_new</span><span class="p">(</span><span class="mh">0x000000</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">color</span><span class="p">));</span>
<span class="n">hbox_add</span><span class="p">(</span><span class="n">picker</span><span class="p">,</span> <span class="p">(</span><span class="n">widget_t</span><span class="o">*</span><span class="p">)</span> <span class="n">color_button_new</span><span class="p">(</span><span class="mh">0x513CBC</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">color</span><span class="p">));</span>
<span class="n">hbox_add</span><span class="p">(</span><span class="n">picker</span><span class="p">,</span> <span class="p">(</span><span class="n">widget_t</span><span class="o">*</span><span class="p">)</span> <span class="n">color_button_new</span><span class="p">(</span><span class="mh">0xFC0A5A</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">color</span><span class="p">));</span>

<span class="n">button_t</span><span class="o">*</span> <span class="n">exit_button</span> <span class="o">=</span> <span class="n">button_new</span><span class="p">(</span><span class="s">"exit"</span><span class="p">);</span>
<span class="n">exit_button</span><span class="o">-&gt;</span><span class="n">widget</span><span class="p">.</span><span class="n">on_click</span> <span class="o">=</span> <span class="p">(</span><span class="n">widget_clicked_t</span><span class="p">)</span> <span class="n">on_exit_clicked</span><span class="p">;</span>
<span class="n">hbox_add</span><span class="p">(</span><span class="n">picker</span><span class="p">,</span> <span class="p">(</span><span class="n">widget_t</span><span class="o">*</span><span class="p">)</span> <span class="n">exit_button</span><span class="p">);</span>

<span class="n">button_t</span><span class="o">*</span> <span class="n">clear_button</span> <span class="o">=</span> <span class="n">button_new</span><span class="p">(</span><span class="s">"clear"</span><span class="p">);</span>
<span class="n">clear_button</span><span class="o">-&gt;</span><span class="n">widget</span><span class="p">.</span><span class="n">on_click</span> <span class="o">=</span> <span class="p">(</span><span class="n">widget_clicked_t</span><span class="p">)</span> <span class="n">on_clear_clicked</span><span class="p">;</span>
<span class="n">hbox_add</span><span class="p">(</span><span class="n">picker</span><span class="p">,</span> <span class="p">(</span><span class="n">widget_t</span><span class="o">*</span><span class="p">)</span> <span class="n">clear_button</span><span class="p">);</span>

<span class="p">...;</span> <span class="c1">// Later, in the program loop</span>

<span class="cm">/* Give these lads some input */</span>
<span class="k">if</span> <span class="p">(</span><span class="n">point_in_rect</span><span class="p">(</span><span class="n">pos</span><span class="p">,</span> <span class="n">picker</span><span class="o">-&gt;</span><span class="n">widget</span><span class="p">.</span><span class="n">bounds</span><span class="p">))</span> <span class="p">{</span>
    <span class="n">picker</span><span class="o">-&gt;</span><span class="n">widget</span><span class="p">.</span><span class="n">on_click</span><span class="p">((</span><span class="n">widget_t</span><span class="o">*</span><span class="p">)</span> <span class="n">picker</span><span class="p">,</span> <span class="n">pos</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Anyway, you can paint stuff now. It’s plenty fast in QEMU, but that could still be easily improved: right now we tell the wm to update the whole window rect<sup class="tooltip">[7]<span>clipping rules still apply in wm land, of course</span></sup>, when we could tell it to update only the small square containing the new line we just drew. Another big improvement, and not just to paint, would be to store the mouse’s position as a pair of floats instead of ints, because right now small movements are basically ignored due to rounding errors in the wm’s code. One advantage is that it’s really easy to draw squares right now, but unless a sizeable fraction of users turns out to be rabbid fans of the <a href="https://en.wikipedia.org/wiki/Suprematism">Suprematist</a> movement, I think it’s worth fixing.</p>

<hr />

<p>On a final note, I wanted to thank /r/osdev’s users for sharing their progress, in particular skiftOS’s developer, whose beautiful UI and OS reminded me to try a little harder, because the results are clearly worth it.</p>]]></content><author><name>Johan Manuel</name></author><category term="development" /><category term="osdev" /><category term="hobby-os" /><category term="c" /><summary type="html"><![CDATA[Let’s face it, it’s hard to get excited about a kernel from just barebone demos of barely functional systems. In this article, I propose a radical solution: actually implementing useful userspace programs, namely a terminal, and ye old copycat of paint.]]></summary></entry><entry><title type="html">A need for speed</title><link href="/a-need-for-speed/" rel="alternate" type="text/html" title="A need for speed" /><published>2020-05-08T00:00:00+02:00</published><updated>2020-05-08T00:00:00+02:00</updated><id>/a-need-for-speed</id><content type="html" xml:base="/a-need-for-speed/"><![CDATA[<p><img src="/assets/snowy_bg.jpg" alt="thumbnail" class="thumbnail" title="I choose the worst wallpapers" />
At the end of the last post we had a pretty solid memory allocator. Where does that take us though? Well in some cases, making hundreds of small allocations can lead to thousandfold improvements. Today, we reach for performance!</p>

<blockquote>
  <p>Wait a minute… this has nothing to do with kernel dev</p>
</blockquote>

<p>Guess again! SnowflakeOS’s window manager is in the kernel<sup>[<a href="" title="don't bully me">1</a>]</sup>. It is quite rude for a system call to take a whole second to return, and sadly this is the situation we found ourselves in.</p>

<h2 id="how-did-we-end-up-like-this">How did we end up like this?</h2>

<p>It’s relatively easy to make a slow window manager, which is what we did. Just redraw all of your windows when you wish to redraw one, hell, redraw your whole desktop when you move the mouse!</p>

<p>Of course, don’t forget to use an off-screen buffer so that you can copy the whole screen buffer twice every chance you get.</p>

<p>To be fair, I don’t write window managers everyday :)</p>

<h2 id="hello-clipping-my-old-friend">Hello clipping my old friend</h2>

<p>The key to performance, always, is to not do things. And indeed, not doing much will be the path to our goal here, through an arcane concept called clipping.</p>

<blockquote>
  <p>I’d work all night if it meant nothing got done.</p>
  <ul>
    <li>Ron Swanson</li>
  </ul>
</blockquote>

<h3 id="quick-overview">Quick overview</h3>

<p>Consider this situation, in which a window needs redrawing:</p>

<p><img src="/assets/lshape.png" alt="needs redrawing" /></p>

<p>We can only see an L-shaped portion of it, clearly we don’t want to redraw more than that. It’s not easy copying an L-shape from a big array of pixels though, imagine the look of that <code class="language-plaintext highlighter-rouge">for</code> loop :)</p>

<p>This is why we’re going to cut up this L into rectangles, like this:</p>

<p><img src="/assets/lshape_split.png" alt="split window" /></p>

<p>Much better, now this is something we can work with!</p>

<p>This whole “cutting stuff up into rectangles” is very visual, easy for us to do, but it’s not easy to see how to turn it into an algorithm. It’ll be some work, but it’ll pay off!</p>

<h3 id="implementation">Implementation</h3>

<p>As mentionned a few articles ago, <a href="http://www.trackze.ro/tag/windowing-systems-by-example/">this awesome series of articles</a> will be the basis for SnowflakeOS’s implementation of clipping. Many thanks to the author!</p>

<h4 id="rectangle-splitting">Rectangle splitting</h4>

<p>Our basic need is to be able to “split a rectangle by another”: given a rectangle to be split, <code class="language-plaintext highlighter-rouge">R</code>, and a rectangle that covers it <code class="language-plaintext highlighter-rouge">S</code> (for <em>splitting rectangle</em>), we want to get new rectangles, called <em>clipping rectangles of <code class="language-plaintext highlighter-rouge">R</code></em>, satisfying the following conditions:</p>
<ul>
  <li>their union must cover the area <code class="language-plaintext highlighter-rouge">R \ S</code>,</li>
  <li>they must be disjoint: no two of them should intersect.</li>
</ul>

<p>In the above image for instance, “redraw me” was split by “doom.exe”, which produced two clipping rectangles, marked 1 and 2.</p>

<p>Clipping rectangles are never unique, but for our purpose they may as well be.</p>

<p>The core idea of the splitting algorithm is to examine each edge of <code class="language-plaintext highlighter-rouge">S</code> and check if it cuts through <code class="language-plaintext highlighter-rouge">R</code>. If it does, we have created a first clipping rectangle, and we can repeat the operation for the next edges, only now with a smaller rectangle to cut.</p>

<p>Here’s the algorithm:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">list_t</span><span class="o">*</span> <span class="nf">rect_split_by</span><span class="p">(</span><span class="n">rect_t</span> <span class="n">rect</span><span class="p">,</span> <span class="n">rect_t</span> <span class="n">split</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">list_t</span><span class="o">*</span> <span class="n">list</span> <span class="o">=</span> <span class="n">list_new</span><span class="p">();</span>
    <span class="n">rect_t</span><span class="o">*</span> <span class="n">tmp</span><span class="p">;</span>

    <span class="c1">// Split by the left edge</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">split</span><span class="p">.</span><span class="n">left</span> <span class="o">&gt;=</span> <span class="n">rect</span><span class="p">.</span><span class="n">left</span> <span class="o">&amp;&amp;</span> <span class="n">split</span><span class="p">.</span><span class="n">left</span> <span class="o">&lt;=</span> <span class="n">rect</span><span class="p">.</span><span class="n">right</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">tmp</span> <span class="o">=</span> <span class="n">rect_new</span><span class="p">(</span><span class="n">rect</span><span class="p">.</span><span class="n">top</span><span class="p">,</span> <span class="n">rect</span><span class="p">.</span><span class="n">left</span><span class="p">,</span> <span class="n">rect</span><span class="p">.</span><span class="n">bottom</span><span class="p">,</span> <span class="n">split</span><span class="p">.</span><span class="n">left</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
        <span class="n">list_add</span><span class="p">(</span><span class="n">list</span><span class="p">,</span> <span class="n">tmp</span><span class="p">);</span>
        <span class="n">rect</span><span class="p">.</span><span class="n">left</span> <span class="o">=</span> <span class="n">split</span><span class="p">.</span><span class="n">left</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// Split by the top edge</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">split</span><span class="p">.</span><span class="n">top</span> <span class="o">&gt;=</span> <span class="n">rect</span><span class="p">.</span><span class="n">top</span> <span class="o">&amp;&amp;</span> <span class="n">split</span><span class="p">.</span><span class="n">top</span> <span class="o">&lt;=</span> <span class="n">rect</span><span class="p">.</span><span class="n">bottom</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">tmp</span> <span class="o">=</span> <span class="n">rect_new</span><span class="p">(</span><span class="n">rect</span><span class="p">.</span><span class="n">top</span><span class="p">,</span> <span class="n">rect</span><span class="p">.</span><span class="n">left</span><span class="p">,</span> <span class="n">split</span><span class="p">.</span><span class="n">top</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">rect</span><span class="p">.</span><span class="n">right</span><span class="p">);</span>
        <span class="n">list_add</span><span class="p">(</span><span class="n">list</span><span class="p">,</span> <span class="n">tmp</span><span class="p">);</span>
        <span class="n">rect</span><span class="p">.</span><span class="n">top</span> <span class="o">=</span> <span class="n">split</span><span class="p">.</span><span class="n">top</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// Split by the right edge</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">split</span><span class="p">.</span><span class="n">right</span> <span class="o">&gt;=</span> <span class="n">rect</span><span class="p">.</span><span class="n">left</span> <span class="o">&amp;&amp;</span> <span class="n">split</span><span class="p">.</span><span class="n">right</span> <span class="o">&lt;=</span> <span class="n">rect</span><span class="p">.</span><span class="n">right</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">tmp</span> <span class="o">=</span> <span class="n">rect_new</span><span class="p">(</span><span class="n">rect</span><span class="p">.</span><span class="n">top</span><span class="p">,</span> <span class="n">split</span><span class="p">.</span><span class="n">right</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">rect</span><span class="p">.</span><span class="n">bottom</span><span class="p">,</span> <span class="n">rect</span><span class="p">.</span><span class="n">right</span><span class="p">);</span>
        <span class="n">list_add</span><span class="p">(</span><span class="n">list</span><span class="p">,</span> <span class="n">tmp</span><span class="p">);</span>
        <span class="n">rect</span><span class="p">.</span><span class="n">right</span> <span class="o">=</span> <span class="n">split</span><span class="p">.</span><span class="n">right</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// Split by the bottom edge</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">split</span><span class="p">.</span><span class="n">bottom</span> <span class="o">&gt;=</span> <span class="n">rect</span><span class="p">.</span><span class="n">top</span> <span class="o">&amp;&amp;</span> <span class="n">split</span><span class="p">.</span><span class="n">bottom</span> <span class="o">&lt;=</span> <span class="n">rect</span><span class="p">.</span><span class="n">bottom</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">tmp</span> <span class="o">=</span> <span class="n">rect_new</span><span class="p">(</span><span class="n">split</span><span class="p">.</span><span class="n">bottom</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">rect</span><span class="p">.</span><span class="n">left</span><span class="p">,</span> <span class="n">rect</span><span class="p">.</span><span class="n">bottom</span><span class="p">,</span> <span class="n">rect</span><span class="p">.</span><span class="n">right</span><span class="p">);</span>
        <span class="n">list_add</span><span class="p">(</span><span class="n">list</span><span class="p">,</span> <span class="n">tmp</span><span class="p">);</span>
        <span class="n">rect</span><span class="p">.</span><span class="n">bottom</span> <span class="o">=</span> <span class="n">split</span><span class="p">.</span><span class="n">bottom</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">list</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Not an easy read, for sure. I won’t detail the <code class="language-plaintext highlighter-rouge">list_t</code> and <code class="language-plaintext highlighter-rouge">rect_t</code> types and associated functions, but you can trust that they do what they say. The list implementation can be found <a href="https://github.com/29jm/SnowflakeOS/blob/50b726c2be2c0f9e3e57aa7d262b9bc048687777/kernel/src/misc/list.c">here</a>, and operations on <code class="language-plaintext highlighter-rouge">rect_t</code> can be found <a href="https://github.com/29jm/SnowflakeOS/blob/59d0379ca3df1a7eb1a3fbf6914e49a134f47e97/kernel/src/misc/wm/rect.c">here</a>.</p>

<h4 id="some-more-convenient-tools">Some more convenient tools</h4>

<p>The previous algorithm solves our previous situation perfectly, but suppose now that two windows cover the one we wish to redraw:</p>

<p><img src="/assets/2cover.png" alt="covered window" /></p>

<p>Say we split our window by “doom.exe 2”, and we get two clipping rectangles out of it. One of those is going to intersect with the “doom.exe 3” window, and this is no good, we’d be drawing a hidden part of the window.</p>

<p>What can we do? Well, let’s just split each one of our newly-acquired clipping rectangles by that second window! We’ll get a new list of clipping rectangles for each clipping rectangle intersecting with “doom.exe 3”… What we want is to keep only those new clips, and not the old ones. Well, there’s your algorithm.</p>

<p>To put it another way: given a list of clipping rectangles, and a splitting rectangle <code class="language-plaintext highlighter-rouge">R</code>, this algorithm punches an <code class="language-plaintext highlighter-rouge">R</code>-shaped hole in the area covered by the clips, while maintaining the two conditions listed previously.</p>

<p>The implementation is a bit easier to reason about this time:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">rect_subtract_clip_rect</span><span class="p">(</span><span class="n">list_t</span><span class="o">*</span> <span class="n">rects</span><span class="p">,</span> <span class="n">rect_t</span> <span class="n">clip</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">uint32_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">rects</span><span class="o">-&gt;</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">rect_t</span><span class="o">*</span> <span class="n">current</span> <span class="o">=</span> <span class="n">list_get_at</span><span class="p">(</span><span class="n">rects</span><span class="p">,</span> <span class="n">i</span><span class="p">);</span> <span class="c1">// O(n²)</span>

        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">rect_intersect</span><span class="p">(</span><span class="o">*</span><span class="n">current</span><span class="p">,</span> <span class="n">clip</span><span class="p">))</span> <span class="p">{</span>
            <span class="k">continue</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="n">rect_t</span><span class="o">*</span> <span class="n">rect</span> <span class="o">=</span> <span class="n">list_remove_at</span><span class="p">(</span><span class="n">rects</span><span class="p">,</span> <span class="n">i</span><span class="p">);</span>
        <span class="n">list_t</span><span class="o">*</span> <span class="n">splits</span> <span class="o">=</span> <span class="n">rect_split_by</span><span class="p">(</span><span class="o">*</span><span class="n">rect</span><span class="p">,</span> <span class="n">clip</span><span class="p">);</span>
        <span class="kt">uint32_t</span> <span class="n">n_splits</span> <span class="o">=</span> <span class="n">splits</span><span class="o">-&gt;</span><span class="n">count</span><span class="p">;</span>

        <span class="k">while</span> <span class="p">(</span><span class="n">splits</span><span class="o">-&gt;</span><span class="n">count</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">list_add_front</span><span class="p">(</span><span class="n">rects</span><span class="p">,</span> <span class="n">list_remove_at</span><span class="p">(</span><span class="n">splits</span><span class="p">,</span> <span class="mi">0</span><span class="p">));</span>
        <span class="p">}</span>

        <span class="n">kfree</span><span class="p">(</span><span class="n">current</span><span class="p">);</span>
        <span class="n">kfree</span><span class="p">(</span><span class="n">splits</span><span class="p">);</span>

        <span class="c1">// Skip the rects we inserted at the front and those already checked</span>
        <span class="c1">// Mind the end of loop increment</span>
        <span class="n">i</span> <span class="o">=</span> <span class="n">n_splits</span> <span class="o">+</span> <span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The subtility is that we’re both removing and adding rectangles in our list of clips each iteration, so we need to keep a good track of where we are in our loop. The original author just set <code class="language-plaintext highlighter-rouge">i = 0</code> at the end of the loop, which works great of course because the new clips we create never intersect with <code class="language-plaintext highlighter-rouge">clip</code>, but it wastes like, 30 clock cycles… :)</p>

<p>The cool thing with this new algorithm is that it superseeds the previous one entirely. Indeed, we don’t need a special case when we want to split a window: just put it in a list, and call the algorithm! Credit to the first one of course, it powers the whole thing.</p>

<h3 id="its-how-you-use-it">It’s how you use it</h3>

<p>Good, the hard work is done. We can draw stuff efficiently now, we have the technology!</p>

<h4 id="drawing-a-window">Drawing a window</h4>

<p>Consider our window’s rectangle. List all of the windows covering it, and punch a hole in the rectangle for each of them. Draw the areas of the window described by the clipping rectangles obtained. Simple as that!</p>

<p>Translated word for word<sup>[<a href="" title="slight overstatement">2</a>]</sup> in <code class="language-plaintext highlighter-rouge">C</code>:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">wm_draw_window</span><span class="p">(</span><span class="n">wm_window_t</span><span class="o">*</span> <span class="n">win</span><span class="p">,</span> <span class="n">rect_t</span> <span class="n">rect</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">rect_t</span> <span class="n">win_rect</span> <span class="o">=</span> <span class="n">rect_from_window</span><span class="p">(</span><span class="n">win</span><span class="p">);</span>
    <span class="n">list_t</span><span class="o">*</span> <span class="n">clip_windows</span> <span class="o">=</span> <span class="n">wm_get_windows_above</span><span class="p">(</span><span class="n">win</span><span class="p">);</span>
    <span class="n">list_t</span><span class="o">*</span> <span class="n">clip_rects</span> <span class="o">=</span> <span class="n">list_new</span><span class="p">();</span>

    <span class="n">list_add</span><span class="p">(</span><span class="n">clip_rects</span><span class="p">,</span> <span class="n">rect</span><span class="p">);</span>

    <span class="c1">// Punch a hole for each covering window</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">clip_windows</span><span class="o">-&gt;</span><span class="n">count</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">wm_window_t</span><span class="o">*</span> <span class="n">cw</span> <span class="o">=</span> <span class="n">list_remove_at</span><span class="p">(</span><span class="n">clip_windows</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
        <span class="n">rect_t</span> <span class="n">clip</span> <span class="o">=</span> <span class="n">rect_from_window</span><span class="p">(</span><span class="n">cw</span><span class="p">);</span>
        <span class="n">rect_subtract_clip_rect</span><span class="p">(</span><span class="n">clip_rects</span><span class="p">,</span> <span class="n">clip</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="c1">// Draw whatever is left in our clipping rects</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">uint32_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">clip_rects</span><span class="o">-&gt;</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">rect_t</span><span class="o">*</span> <span class="n">clip</span> <span class="o">=</span> <span class="n">list_get_at</span><span class="p">(</span><span class="n">clip_rects</span><span class="p">,</span> <span class="n">i</span><span class="p">);</span> <span class="c1">// O(n²)</span>

        <span class="c1">// Fun edge case</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">rect_intersect</span><span class="p">(</span><span class="o">*</span><span class="n">clip</span><span class="p">,</span> <span class="n">win_rect</span><span class="p">))</span> <span class="p">{</span>
            <span class="k">continue</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="n">wm_partial_draw_window</span><span class="p">(</span><span class="n">win</span><span class="p">,</span> <span class="o">*</span><span class="n">clip</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">rect_clear_clipped</span><span class="p">(</span><span class="n">clip_rects</span><span class="p">);</span>
    <span class="n">kfree</span><span class="p">(</span><span class="n">clip_rects</span><span class="p">);</span>
    <span class="n">kfree</span><span class="p">(</span><span class="n">clip_windows</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Notice the <code class="language-plaintext highlighter-rouge">wm_partial_draw_window</code> function call: it’s the only function that does any actual pixel work. It’s both mundane and insane (“the land of off-by-ones” you may say), and you can check it out <a href="https://github.com/29jm/SnowflakeOS/blob/59d0379ca3df1a7eb1a3fbf6914e49a134f47e97/kernel/src/misc/wm/wm.c#L154-L187">here</a>.</p>

<h4 id="drawing-part-of-the-screen">Drawing part of the screen</h4>

<p>Imagine you’re closing a window. Then you have to redraw whatever was below that window, and that could be like, several windows. Do we redraw them entirely? Of course not, we can just redraw the parts of them that was covered by the closed window.</p>

<p>This is what led to the second parameter of <code class="language-plaintext highlighter-rouge">wm_draw_window</code>, i.e. a <code class="language-plaintext highlighter-rouge">rect</code> that says “draw within this area”. It’s used in the following short function that implements the redrawing of an area:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">wm_refresh_partial</span><span class="p">(</span><span class="n">rect_t</span> <span class="n">clip</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">uint32_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">windows</span><span class="o">-&gt;</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">wm_window_t</span><span class="o">*</span> <span class="n">win</span> <span class="o">=</span> <span class="n">list_get_at</span><span class="p">(</span><span class="n">windows</span><span class="p">,</span> <span class="n">i</span><span class="p">);</span> <span class="c1">// O(n²)</span>
        <span class="n">rect_t</span> <span class="n">rect</span> <span class="o">=</span> <span class="n">rect_from_window</span><span class="p">(</span><span class="n">win</span><span class="p">);</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">rect_intersect</span><span class="p">(</span><span class="n">clip</span><span class="p">,</span> <span class="n">rect</span><span class="p">))</span> <span class="p">{</span>
            <span class="n">wm_draw_window</span><span class="p">(</span><span class="n">win</span><span class="p">,</span> <span class="n">clip</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>What happens when a part of the screen you want to redraw isn’t covered by any window? As you may read above, nothing. Thankfully this doesn’t happen<sup>[<a href="" title="well, nothing _does_ happen">3</a>]</sup>, because there’s a huge window that draws the wallpaper… Ahem, I’ll get to it at some point ^^’</p>

<p>As a quick aside, you may have noticed the <code class="language-plaintext highlighter-rouge">O(n²)</code> sprinkled here and there in the code. Those are reminders for me to replace the list implementation I used: while really easy to use, iterating such a list automatically has quadratic complexity, which is obviously ridiculous. I doubt that it matters at all until you reach an absurd amount of windows, but it irks me a good bit. I’ll take <a href="https://github.com/torvalds/linux/blob/master/include/linux/list.h">Linux’s</a> <code class="language-plaintext highlighter-rouge">list.h</code> to replace it, it looks just perfect.</p>

<h2 id="performance">Performance</h2>

<p>Let’s see if we can get some numbers in here, check that all this work wasn’t in vain.</p>

<p>Our <a href="https://github.com/29jm/SnowflakeOS/blob/59d0379ca3df1a7eb1a3fbf6914e49a134f47e97/modules/src/test.c">test</a> will be spawning a hundred windows from a single process, plus the wallpaper. We will record the whole thing, and count the frames needed to go from a black screen to the 100<sup>th</sup> window.</p>

<h4 id="before-clipping-as-of-march-14th">Before clipping, as of March 14th</h4>

<video controls="">
  <source src="/assets/hundred_wins_before.mp4" type="video/mp4" />
</video>

<p>It took 172 frames to get from the wallpaper to the last window, or 5.74 seconds.</p>

<h4 id="after-clipping">After clipping</h4>

<video controls="">
  <source src="/assets/hundred_wins_after.mp4" type="video/mp4" />
</video>

<p>Now, it takes 11 frames, or 0.37 seconds. This is an improvement of about <strong>1500%</strong>…</p>

<h3 id="mission-accomplished">Mission accomplished!</h3>

<p>We will for sure get smooth mouse movements and smooth window dragging in the next article now <sup>[<a href="" title="plot twist: we already do">4</a>]</sup>, until next time!</p>]]></content><author><name>Johan Manuel</name></author><category term="development" /><category term="osdev" /><category term="hobby-os" /><category term="c" /><summary type="html"><![CDATA[At the end of the last post we had a pretty solid memory allocator. Where does that take us though? Well in some cases, making hundreds of small allocations can lead to thousandfold improvements. Today, we reach for performance!]]></summary></entry><entry><title type="html">Taming memory allocators</title><link href="/advanced-memory-allocation/" rel="alternate" type="text/html" title="Taming memory allocators" /><published>2020-03-07T00:00:00+01:00</published><updated>2020-03-07T00:00:00+01:00</updated><id>/advanced-memory-allocation</id><content type="html" xml:base="/advanced-memory-allocation/"><![CDATA[<p><img src="/assets/sos-spam.jpg" alt="Current state of the GUI" class="thumbnail" title="This used to be a 17 MiB gif, it got shot down. Notice that window staying on top of all others?" />
Today I’ll be writing about memory allocation, a fairly fundamental topic, perhaps one that most encounter faily early in their OS development journey. Yet I’ve only now started to really get into it, now that I feel like it’s needed. And it turned out to be fun after all!</p>

<h2 id="the-basics">The basics</h2>

<p>I decided to take guidance from <a href="http://dmitrysoshnikov.com/compilers/writing-a-memory-allocator/">this post</a> by Dmitry Soshnikov, implementing in this post only the basics, to get to a point at which I can freely call <code class="language-plaintext highlighter-rouge">kmalloc</code> and <code class="language-plaintext highlighter-rouge">kfree</code>, without throwing away too much memory. And perhaps <code class="language-plaintext highlighter-rouge">malloc</code> and <code class="language-plaintext highlighter-rouge">free</code> in userspace later?</p>

<p>There is some terminology to get down before diving into the details:</p>

<ul>
  <li><em>memory allocator</em>: a function that a program can call that returns an address to which the program can freely write. Typically, <code class="language-plaintext highlighter-rouge">malloc</code>. Different allocators have different qualities, such as performance, minimizing memory fragmentation…</li>
  <li><em>memory block</em>: a contiguous range of memory addresses, with a few attributes such as whether it’s in use or has been freed, its address and size… These attributes are stored in the block’s <em>header</em>.</li>
  <li><em>alignment</em>: an address <code class="language-plaintext highlighter-rouge">addr</code> is said to be <code class="language-plaintext highlighter-rouge">N</code>-aligned if <code class="language-plaintext highlighter-rouge">addr % N == 0</code>. It’s important for a kernel allocator to be able to allocate buffers with specific alignments, as we’ll later see.</li>
</ul>

<h2 id="our-previous-allocator-now-too-simple">Our previous allocator: now too simple</h2>

<p>Previously, SnowflakeOS used what’s called a <em>bump allocator</em>, i.e. an allocator that keeps track of the last block only, and that always allocates after that last block, with no means of freeing previous blocks individually.</p>

<p>I would’ve liked to keep that design as the implementation is concise and easy to understand, but unfortunately it’s now too simple for my use. Not being able to reuse blocks is the deal breaker here, as the window manager will have to do a lot of small and short-lived allocations, and the goal is to not to run out of memory in five seconds.</p>

<p>The new allocator will have to keep one feature from its predecessor, the ability to hand out addresses with a specific alignment. This is strictly needed, as we need to be able to remap a newly acquired page from our allocator, and pages boundaries are multiples of 4 KiB. See for instance <a href="https://github.com/29jm/SnowflakeOS/blob/132529e3bec0855597b769510ececd3f9213a8a9/kernel/src/devices/term.c#L53-L55">this use case</a>.</p>

<h2 id="the-new-allocator">The new allocator</h2>

<p>We’ll write a <em>first-fit</em> memory allocator that can deliver arbitrarily-aligned addresses. You can find the whole source <a href="https://github.com/29jm/SnowflakeOS/blob/3433a8c4abcc9a3193813b940882558ff623875d/kernel/src/mem/mem.c">here</a>, and an updated version for the end of this post <a href="https://github.com/29jm/SnowflakeOS/blob/132529e3bec0855597b769510ececd3f9213a8a9/libc/src/stdlib/malloc.c">here</a>.</p>

<p>First, our blocks are defined by the following struct. The first two members constitute the header:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">typedef</span> <span class="k">struct</span> <span class="n">_mem_block_t</span> <span class="p">{</span>
    <span class="k">struct</span> <span class="n">_mem_block_t</span><span class="o">*</span> <span class="n">next</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">size</span><span class="p">;</span> <span class="c1">// We use the last bit as a 'used' flag</span>
    <span class="kt">uint8_t</span> <span class="n">data</span><span class="p">[];</span>
<span class="p">}</span> <span class="n">mem_block_t</span><span class="p">;</span>
</code></pre></div></div>
<p>That last member is what’s called a “flexible array member” in C99. It’s an array without a given dimension, i.e. we can manage its size manually by saying “I know that the memory after this struct is mine, let me access it though this member”. Here, it’ll be the pointer returned by our <code class="language-plaintext highlighter-rouge">kmalloc</code> function.</p>

<p>And secondly, we use a simple first-fit design, i.e. when allocating something, we first look through our list of blocks and see if there’s a free one that fits our criteria of size and alignment. The global algorithm is as follows:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span><span class="o">*</span> <span class="nf">kamalloc</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="n">size</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">align</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">size</span> <span class="o">=</span> <span class="n">align_to</span><span class="p">(</span><span class="n">size</span><span class="p">,</span> <span class="mi">8</span><span class="p">);</span>

    <span class="n">mem_block_t</span><span class="o">*</span> <span class="n">block</span> <span class="o">=</span> <span class="n">mem_find_block</span><span class="p">(</span><span class="n">size</span><span class="p">,</span> <span class="n">align</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">block</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">block</span><span class="o">-&gt;</span><span class="n">size</span> <span class="o">|=</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// Mark it as used</span>
        <span class="k">return</span> <span class="n">block</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">;</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="n">block</span> <span class="o">=</span> <span class="n">mem_new_block</span><span class="p">(</span><span class="n">size</span><span class="p">,</span> <span class="n">align</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="p">((</span><span class="kt">uintptr_t</span><span class="p">)</span> <span class="n">block</span><span class="o">-&gt;</span><span class="n">data</span> <span class="o">&gt;</span> <span class="n">KERNEL_HEAP_BEGIN</span> <span class="o">+</span> <span class="n">KERNEL_HEAP_SIZE</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"[MEM] The kernel ran out of memory!"</span><span class="p">);</span>
        <span class="n">abort</span><span class="p">();</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">block</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>And the “first-fit” logic is implemented in <code class="language-plaintext highlighter-rouge">mem_find_block</code> here, in no particular magic:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">mem_block_t</span><span class="o">*</span> <span class="nf">mem_find_block</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="n">size</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">align</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">bottom</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">mem_block_t</span><span class="o">*</span> <span class="n">block</span> <span class="o">=</span> <span class="n">bottom</span><span class="p">;</span>

    <span class="k">while</span> <span class="p">(</span><span class="n">block</span><span class="o">-&gt;</span><span class="n">size</span> <span class="o">&lt;</span> <span class="n">size</span> <span class="o">||</span> <span class="n">block</span><span class="o">-&gt;</span><span class="n">size</span> <span class="o">&amp;</span> <span class="mi">1</span> <span class="o">||</span> <span class="o">!</span><span class="n">mem_is_aligned</span><span class="p">(</span><span class="n">block</span><span class="p">,</span> <span class="n">align</span><span class="p">))</span> <span class="p">{</span>
        <span class="n">block</span> <span class="o">=</span> <span class="n">block</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">;</span>

        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">block</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">block</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The load-bearing portion of our allocator is in the creation of blocks, in <code class="language-plaintext highlighter-rouge">mem_new_block</code>:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">mem_block_t</span><span class="o">*</span> <span class="nf">mem_new_block</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="n">size</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">align</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">const</span> <span class="kt">uint32_t</span> <span class="n">header_size</span> <span class="o">=</span> <span class="n">offsetof</span><span class="p">(</span><span class="n">mem_block_t</span><span class="p">,</span> <span class="n">data</span><span class="p">);</span>

    <span class="c1">// We start the heap right where the first allocation works</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">top</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">uintptr_t</span> <span class="n">addr</span> <span class="o">=</span> <span class="n">align_to</span><span class="p">(</span><span class="n">KERNEL_HEAP_BEGIN</span><span class="o">+</span><span class="n">header_size</span><span class="p">,</span> <span class="n">align</span><span class="p">)</span> <span class="o">-</span> <span class="n">header_size</span><span class="p">;</span>
        <span class="n">bottom</span> <span class="o">=</span> <span class="p">(</span><span class="n">mem_block_t</span><span class="o">*</span><span class="p">)</span> <span class="n">addr</span><span class="p">;</span>
        <span class="n">top</span> <span class="o">=</span> <span class="n">bottom</span><span class="p">;</span>
        <span class="n">top</span><span class="o">-&gt;</span><span class="n">size</span> <span class="o">=</span> <span class="n">size</span> <span class="o">|</span> <span class="mi">1</span><span class="p">;</span>
        <span class="n">top</span><span class="o">-&gt;</span><span class="n">next</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>

        <span class="k">return</span> <span class="n">top</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// I did the math and we always have next_aligned &gt;= next.</span>
    <span class="kt">uintptr_t</span> <span class="n">next</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uintptr_t</span><span class="p">)</span> <span class="n">top</span> <span class="o">+</span> <span class="n">mem_block_size</span><span class="p">(</span><span class="n">top</span><span class="p">);</span>
    <span class="kt">uintptr_t</span> <span class="n">next_aligned</span> <span class="o">=</span> <span class="n">align_to</span><span class="p">(</span><span class="n">next</span><span class="o">+</span><span class="n">header_size</span><span class="p">,</span> <span class="n">align</span><span class="p">)</span> <span class="o">-</span> <span class="n">header_size</span><span class="p">;</span>

    <span class="n">mem_block_t</span><span class="o">*</span> <span class="n">block</span> <span class="o">=</span> <span class="p">(</span><span class="n">mem_block_t</span><span class="o">*</span><span class="p">)</span> <span class="n">next_aligned</span><span class="p">;</span>
    <span class="n">block</span><span class="o">-&gt;</span><span class="n">size</span> <span class="o">=</span> <span class="n">size</span> <span class="o">|</span> <span class="mi">1</span><span class="p">;</span>
    <span class="n">block</span><span class="o">-&gt;</span><span class="n">next</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>

    <span class="c1">// Insert a free block between top and our aligned block, if there's enough</span>
    <span class="c1">// space. That block is 8-bytes aligned.</span>
    <span class="n">next</span> <span class="o">=</span> <span class="n">align_to</span><span class="p">(</span><span class="n">next</span><span class="o">+</span><span class="n">header_size</span><span class="p">,</span> <span class="n">MIN_ALIGN</span><span class="p">)</span> <span class="o">-</span> <span class="n">header_size</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">next_aligned</span> <span class="o">-</span> <span class="n">next</span> <span class="o">&gt;</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">mem_block_t</span><span class="p">)</span> <span class="o">+</span> <span class="n">MIN_ALIGN</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">mem_block_t</span><span class="o">*</span> <span class="n">filler</span> <span class="o">=</span> <span class="p">(</span><span class="n">mem_block_t</span><span class="o">*</span><span class="p">)</span> <span class="n">next</span><span class="p">;</span>
        <span class="n">filler</span><span class="o">-&gt;</span><span class="n">size</span> <span class="o">=</span> <span class="n">next_aligned</span> <span class="o">-</span> <span class="n">next</span> <span class="o">-</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">mem_block_t</span><span class="p">);</span>
        <span class="n">top</span><span class="o">-&gt;</span><span class="n">next</span> <span class="o">=</span> <span class="n">filler</span><span class="p">;</span>
        <span class="n">top</span> <span class="o">=</span> <span class="n">filler</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">top</span><span class="o">-&gt;</span><span class="n">next</span> <span class="o">=</span> <span class="n">block</span><span class="p">;</span>
    <span class="n">top</span> <span class="o">=</span> <span class="n">block</span><span class="p">;</span>

    <span class="k">return</span> <span class="n">block</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Notice that second <code class="language-plaintext highlighter-rouge">if</code>: as we want to support arbitrary alignment of the blocks we hand out, we want to prevent space from being wasted in between blocks, so unused blocks will be created to fill the gaps as they appear. For instance, imagine the heap is at 0x40, and a 0x1000-aligned block is requested. Then a gap of about <code class="language-plaintext highlighter-rouge">0x1000-0x40=0xFC0</code> bytes will be created between the first block and the new one. We’ll create a block there with minimum alignment to fill the gap.</p>

<p>Note that the pages that consitute the memory we’ll be distributing are already mapped in the kernel. That way the kernel can allocate after starting to execute in multiple page directories, without having to mirror the paging changes in each process. This is where the preallocation is done in <a href="https://github.com/29jm/SnowflakeOS/blob/132529e3bec0855597b769510ececd3f9213a8a9/kernel/src/mem/paging.c#L38-L41">paging.c</a>:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="c1">// Setup the kernel heap</span>
    <span class="n">heap</span> <span class="o">=</span> <span class="n">KERNEL_HEAP_BEGIN</span><span class="p">;</span>
    <span class="kt">uintptr_t</span> <span class="n">heap_phys</span> <span class="o">=</span> <span class="n">pmm_alloc_pages</span><span class="p">(</span><span class="n">KERNEL_HEAP_SIZE</span><span class="o">/</span><span class="mh">0x1000</span><span class="p">);</span>
    <span class="n">paging_map_pages</span><span class="p">(</span><span class="n">KERNEL_HEAP_BEGIN</span><span class="p">,</span> <span class="n">heap_phys</span><span class="p">,</span> <span class="n">KERNEL_HEAP_SIZE</span><span class="o">/</span><span class="mh">0x1000</span><span class="p">,</span> <span class="n">PAGE_RW</span><span class="p">);</span>
</code></pre></div></div>

<h2 id="think-of-the-userspace-children">Think of the (userspace) children!</h2>

<h3 id="porting-the-allocator-to-userspace">Porting the allocator to userspace</h3>

<p>Sure, the kernel and its window manager are what will be stressing memory the most for a while, and we could get away with keeping a bump allocator for our userspace <code class="language-plaintext highlighter-rouge">malloc</code>. That memory is freed on exit anyway. But where’s the fun in that? Can’t we adapt our code so that it works in both the kernel and in userspace?</p>

<p>Of course we can. We already have a build-level mechanism for that with our C library, which is built twice: once for the kernel with the <code class="language-plaintext highlighter-rouge">_KERNEL_</code> preprocessor symbol defined, and a second time for userspace.</p>

<p>There are two things that we’ll have to adapt for userspace:</p>
<ol>
  <li>Our allocated blocks will now live after our program in memory, i.e. at <code class="language-plaintext highlighter-rouge">sbrk(0)</code>, and not after our kernel executable.</li>
  <li>Whereas the kernel has its whole memory pool preallocated, that makes no sense for userspace, so we’ll have to call <code class="language-plaintext highlighter-rouge">sbrk</code> regularly to ask the kernel for more memory.</li>
</ol>

<p>To address the first point, I added the following bit of code to the beginning of <code class="language-plaintext highlighter-rouge">malloc</code>:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="c1">// If this is the first allocation, setup the block list:</span>
    <span class="c1">// it starts with an empty, used block, in order to avoid edge cases.</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">top</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">const</span> <span class="kt">uint32_t</span> <span class="n">header_size</span> <span class="o">=</span> <span class="n">offsetof</span><span class="p">(</span><span class="n">mem_block_t</span><span class="p">,</span> <span class="n">data</span><span class="p">);</span>

<span class="cp">#ifdef _KERNEL_
</span>        <span class="kt">uintptr_t</span> <span class="n">addr</span> <span class="o">=</span> <span class="n">KERNEL_HEAP_BEGIN</span><span class="p">;</span>
<span class="cp">#else
</span>        <span class="kt">uintptr_t</span> <span class="n">addr</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uintptr_t</span><span class="p">)</span> <span class="n">sbrk</span><span class="p">(</span><span class="n">header_size</span><span class="p">);</span>
<span class="cp">#endif
</span>        <span class="n">bottom</span> <span class="o">=</span> <span class="p">(</span><span class="n">mem_block_t</span><span class="o">*</span><span class="p">)</span> <span class="n">addr</span><span class="p">;</span>
        <span class="n">top</span> <span class="o">=</span> <span class="n">bottom</span><span class="p">;</span>
        <span class="n">top</span><span class="o">-&gt;</span><span class="n">size</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// That means used, of size 0</span>
        <span class="n">top</span><span class="o">-&gt;</span><span class="n">next</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>And to address the second point, I added this distinction before calling <code class="language-plaintext highlighter-rouge">mem_new_block</code>:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>        <span class="c1">// We'll have to allocate a new block, so we check if we haven't</span>
        <span class="c1">// exceeded the memory we can distribute.</span>
        <span class="kt">uintptr_t</span> <span class="n">end</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uintptr_t</span><span class="p">)</span> <span class="n">top</span> <span class="o">+</span> <span class="n">mem_block_size</span><span class="p">(</span><span class="n">top</span><span class="p">)</span> <span class="o">+</span> <span class="n">header_size</span><span class="p">,</span> <span class="n">align</span><span class="p">;</span>
        <span class="n">end</span> <span class="o">=</span> <span class="n">align_to</span><span class="p">(</span><span class="n">end</span><span class="p">,</span> <span class="n">align</span><span class="p">)</span> <span class="o">+</span> <span class="n">size</span><span class="p">;</span>
<span class="cp">#ifdef _KERNEL_
</span>        <span class="c1">// The kernel can't allocate more</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">end</span> <span class="o">&gt;</span> <span class="n">KERNEL_HEAP_BEGIN</span> <span class="o">+</span> <span class="n">KERNEL_HEAP_SIZE</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">printf</span><span class="p">(</span><span class="s">"[MEM] The kernel ran out of memory!"</span><span class="p">);</span>
            <span class="n">abort</span><span class="p">();</span>
        <span class="p">}</span>
<span class="cp">#else
</span>        <span class="c1">// But userspace can ask the kernel for more</span>
        <span class="kt">uintptr_t</span> <span class="n">brk</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uintptr_t</span><span class="p">)</span> <span class="n">sbrk</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">end</span> <span class="o">&gt;</span> <span class="n">brk</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">sbrk</span><span class="p">(</span><span class="n">end</span> <span class="o">-</span> <span class="n">brk</span><span class="p">);</span>
        <span class="p">}</span>
<span class="cp">#endif
</span>
        <span class="n">block</span> <span class="o">=</span> <span class="n">mem_new_block</span><span class="p">(</span><span class="n">size</span><span class="p">,</span> <span class="n">align</span><span class="p">);</span>
</code></pre></div></div>

<h3 id="testing-it">Testing it</h3>

<video controls="">
  <source src="/assets/spam-win.mp4" type="video/mp4" />
</video>

<p>To test that new <code class="language-plaintext highlighter-rouge">malloc</code>, I made <a href="https://github.com/29jm/SnowflakeOS/blob/132529e3bec0855597b769510ececd3f9213a8a9/modules/src/test.c">a program</a> to open and close windows continually while keeping the number of windows constant, which you can see in action above.</p>

<p>To be somewhat scientific, I counted the number of calls to <code class="language-plaintext highlighter-rouge">sbrk</code>. If everything was right, this program would call it a few times, then blocks would be reused <em>ad infinitum</em>.</p>

<p>And it did! With 20 windows, I counted 69 <code class="language-plaintext highlighter-rouge">sbrk</code>s, and no signs of more coming up even after five minutes of frenetic window respawning.</p>

<h3 id="a-point-on-kerneluserspace-interactions">A point on kernel/userspace interactions</h3>

<p>It may not be clear what the code paths are for the userspace version of <code class="language-plaintext highlighter-rouge">malloc</code>, so I’ll detail them a bit.</p>

<p>When a program calls <code class="language-plaintext highlighter-rouge">malloc</code>, execution stays in userspace, because the allocator is in the C library linked to it, along with everything else. If <code class="language-plaintext highlighter-rouge">malloc</code>’s memory pool needs expansion (i.e. there’s no room to add a free block), the <code class="language-plaintext highlighter-rouge">sbrk</code> system call is run, and execution jumps <a href="https://github.com/29jm/SnowflakeOS/blob/132529e3bec0855597b769510ececd3f9213a8a9/kernel/src/sys/proc.c#L278-L320">in the kernel</a>. That system call maps pages as needed to expand the heap of the program. The process of mapping those pages may itself involve <a href="https://github.com/29jm/SnowflakeOS/blob/132529e3bec0855597b769510ececd3f9213a8a9/kernel/src/mem/paging.c#L68">allocating memory</a> for the kernel to create new page tables, but in this case, the kernel calls <code class="language-plaintext highlighter-rouge">pmm_alloc_page</code> to get a fresh page of physical memory directly, so <code class="language-plaintext highlighter-rouge">kmalloc</code> is never involved.</p>

<p>It would have been pretty neat to have <code class="language-plaintext highlighter-rouge">malloc</code> call <code class="language-plaintext highlighter-rouge">kmalloc</code>, wouldn’t it? I like the idea of a piece of code calling another compilation of itself, anyway.</p>

<p>This is what <code class="language-plaintext highlighter-rouge">putchar</code> does, so at least such cross-source calling goodness is done somewhere. A call to <code class="language-plaintext highlighter-rouge">putchar</code> in userspace translates to the <code class="language-plaintext highlighter-rouge">putchar</code> <a href="https://github.com/29jm/SnowflakeOS/blob/132529e3bec0855597b769510ececd3f9213a8a9/kernel/src/sys/syscall.c#L74">system call</a> which calls the kernel version of <code class="language-plaintext highlighter-rouge">putchar</code>, which is about two lines above the first call in the <a href="https://github.com/29jm/SnowflakeOS/blob/132529e3bec0855597b769510ececd3f9213a8a9/libc/src/stdio/putchar.c">source</a>:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">putchar</span><span class="p">(</span><span class="kt">int</span> <span class="n">c</span><span class="p">)</span> <span class="p">{</span>
<span class="cp">#ifdef _KERNEL_
</span>    <span class="n">term_putchar</span><span class="p">(</span><span class="n">c</span><span class="p">);</span>
    <span class="n">serial_write</span><span class="p">((</span><span class="kt">char</span><span class="p">)</span> <span class="n">c</span><span class="p">);</span>
<span class="cp">#else
</span>    <span class="n">asm</span> <span class="p">(</span>
        <span class="s">"mov $3, %%eax</span><span class="se">\n</span><span class="s">"</span>
        <span class="s">"mov %[c], %%ecx</span><span class="se">\n</span><span class="s">"</span>
        <span class="s">"int $0x30</span><span class="se">\n</span><span class="s">"</span> <span class="c1">// Syscall</span>
        <span class="o">::</span> <span class="p">[</span><span class="n">c</span><span class="p">]</span> <span class="s">"r"</span> <span class="p">(</span><span class="n">c</span><span class="p">)</span>
        <span class="o">:</span> <span class="s">"%eax"</span>
    <span class="p">);</span>
<span class="cp">#endif
</span>    <span class="k">return</span> <span class="n">c</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Neat.</p>

<h2 id="miscellaneous-bugs-crushed-since-last-time">Miscellaneous bugs crushed since last time</h2>

<h3 id="a-scrambled-w">A scrambled ‘w’</h3>

<p>When I first tested my new kernel allocator, it seemed to work fine except for one detail. The ‘w’ of “SnowflakeOS” in the top left corner of the background and in the title bar of my window looked all wrong:</p>

<p><img src="/assets/scrambled_w.png" alt="scrambled w" /></p>

<p>And only when compiling without the nice blue background and identity mapping more pages than needed at the beginning of memory. Which I did then, otherwise I perhaps wouldn’t have spotted this bug.</p>

<p>I fixed it in <a href="https://github.com/29jm/SnowflakeOS/commit/ca7fedf2468319ecbeb503cf67d1031f2f5cb622">this commit</a>, basically by paying attention to where my GRUB modules (i.e. my programs) were in memory, and protecting that memory. Indeed, those modules were loaded right after my kernel in memory, and guess what I used that area for? The bitmap of my physical memory manager. That’s not a story a James Molloy would tell you<sup>[<a href="" title="I owe much to his tutorials &lt;3">1</a>]</sup>.</p>

<p>Now I check exactly where my modules end and place my physical memory manager after that, and I identity map exactly the right number of pages to be able to copy the modules into kernel memory.</p>

<h3 id="classic-windows">Classic windows</h3>

<p>The gif at the top of this post looks somewhat okay, but it took some effort. Basically, I wanted a program that spawned <code class="language-plaintext highlighter-rouge">N</code> windows then closed the oldest ones and replaced them, in a loop. The first iteration of that popup-spamming program would either spawn +oo windows, or spawn <code class="language-plaintext highlighter-rouge">N</code> windows then get stuck in an infinite loop somewhere.</p>

<p>The problem, explained in <a href="https://github.com/29jm/SnowflakeOS/commit/c16b531d3073cc15d5a9ccdcf0bbc70186c1d755">this commit</a>, was that I had failed to maintain the integrity of my doubly linked list of windows when deleting an element, and the list turned into a circular list when traversed backwards, leading to an infinite loop in <code class="language-plaintext highlighter-rouge">wm_count_windows</code>.</p>

<h2 id="till-next-time">Till next time</h2>

<p>That’s it for this post, which is already far too long, too late and all over the place.<br />
Thank you for reading till the end!</p>]]></content><author><name>Johan Manuel</name></author><category term="development" /><category term="osdev" /><category term="hobby-os" /><category term="c" /><summary type="html"><![CDATA[Today I’ll be writing about memory allocation, a fairly fundamental topic, perhaps one that most encounter faily early in their OS development journey. Yet I’ve only now started to really get into it, now that I feel like it’s needed. And it turned out to be fun after all!]]></summary></entry><entry><title type="html">Room for graphical improvement</title><link href="/room-for-improvements/" rel="alternate" type="text/html" title="Room for graphical improvement" /><published>2019-12-30T00:00:00+01:00</published><updated>2019-12-30T00:00:00+01:00</updated><id>/room-for-improvements</id><content type="html" xml:base="/room-for-improvements/"><![CDATA[<p><img src="/assets/sos-with-bg.jpg" alt="Current state of the GUI" class="thumbnail" title="It's always christmas with SnowflakeOS" />
In the last post, I presented the first working version of SnowflakeOS’s window manager. While it worked, it had<sup>[<a href="" title="still has">1</a>]</sup> a few important shortcomings.</p>

<h2 id="wm-design-simple-is-too-simple">WM design: simple is too simple</h2>

<h3 id="in-the-last-post">In the last post</h3>

<p>Here’s how it worked:</p>

<ol>
  <li>The WM held a state: which window had to be drawn next to correctly on top of others</li>
  <li>Windows had to call the WM in a loop in order not to block others</li>
</ol>

<p>The rationale was that having the windows call the WM allowed for a single buffer per window. Performance wasn’t great either: framerate was limited by the slowest window, drawing a single frame took as many system calls as there are windows, and each window had to be copied entirely. Plus the final off-screen buffer to framebuffer copy. Last but not least, drawing in a loop when the screen doesn’t even require redrawing is plain dumb.</p>

<h3 id="a-slight-improvement">A slight improvement</h3>

<p>Since the last post, I decided I could spare the RAM to build something less outrageous, so now it works like this:</p>

<ol>
  <li>When a window calls the WM, its buffer is copied in the kernel</li>
  <li>All windows are redrawn from their in-kernel buffers</li>
  <li>The off-screen buffer is copied to the framebuffer</li>
</ol>

<p>Now the screen is only refreshed as needed, in a single system call, but when it needs to be, it’s still incredibly slow. For a video mode of 1024x768x32, we’re copying at least 6 MiB per refresh. Still sort of outrageous.</p>

<h3 id="reinventing-the-wheel-from-second-hand-principles">Reinventing the wheel from second-hand principles</h3>

<p>Doing a bit of searching, I found <a href="http://www.trackze.ro/tag/windowing-systems-by-example/">a magnificent series of blog posts</a> by Joe Marlin<sup>[<a href="" title="Joe, I had to steal your footnotes, for I could not steal your style">2</a>]</sup> in which he implements a window manager from scratch in C, taking proper design and performance in consideration. Finding information about the algorithms and architecture of window managers is surprisingly difficult, which is why Joe’s posts are of such value.</p>

<p>One of the techniques described that I really want to implement is clipping, to avoid copying so much memory and redrawing things that haven’t changed. There’s also a neat GUI system described there, but I don’t want to have it live in the WM so I’ll make my own way there.</p>

<h2 id="miscellaneous-improvements-since-last-time">Miscellaneous improvements since last time</h2>

<h3 id="bug-hunting">Bug hunting</h3>

<p>I’ve spent most of my development time on this item, with two outstanding bugs.</p>

<p>The first was a page fault occuring only when optimisations were turned when compiling the kernel. I fixed it in <a href="https://github.com/29jm/SnowflakeOS/commit/4089a7460f31153ea7f5d2734f5a538c6918e4da">this commit</a> and while the precise instruction causing the fault escaped me, I know it was a result of two things:</p>

<ul>
  <li>I used <code class="language-plaintext highlighter-rouge">ebx</code> to pass arguments in my system calls without saving its value: it’s a callee-saved register in the C calling convention. It was only a matter of time before it caused a bug.</li>
  <li>Even after fixing the above point, not qualifying my inline assembly system calls with <code class="language-plaintext highlighter-rouge">volatile</code> left my code crashing. I guess gcc tried something funny during optimisation there.</li>
</ul>

<p>The <a href="https://github.com/29jm/SnowflakeOS/commit/5bbd545037487fc8f9f935b3b7f5755e9bfdd0d6">second bug</a> was with my background window (shown at the top of this post) being shifted 50 pixels to the right. Specifically, the top row began at the 51th pixel, thus shifting the rest of the image, and drawing 50 pixels past the end of the buffer. The worst is that this bug occured only with <code class="language-plaintext highlighter-rouge">-O2</code> optimisations turned on, and only on bochs, not on QEMU. This made me think it had to be a memory error, caused by me triggering undefined behavior somewhere, as I had reworked my <code class="language-plaintext highlighter-rouge">malloc</code> implementation just before<sup>[<a href="" title="see the very next section">3</a>]</sup>.<br />
It turned out to be a lot more mundane: with optimisations on, my scheduler switched from the “background window” program ealier than in other cases, so it opened its window after the other program. Can you guess what my window-placing code does? It shifts new windows 50 pixels to the right of the last one. The first window was placed at x=0, the background at x=50.</p>

<p>I was rooting for a much more interesting resolution for that second bug! That’ll teach me not to make too many assumptions while debugging, and not to be okay with drawing outside buffers. And my window-placing code is now clearly marked as “radioactive garbage”.</p>

<h3 id="gradual-improvements-to-malloc">Gradual improvements to malloc</h3>

<p>I’ve <a href="https://github.com/29jm/SnowflakeOS/blob/5bbd545037487fc8f9f935b3b7f5755e9bfdd0d6/kernel/src/sys/proc.c#L278-L320">implemented</a> the <code class="language-plaintext highlighter-rouge">sbrk</code><sup>[<a href="" title="it stands for 'set break'">4</a>]</sup> system call as a first step to get a free-able malloc implementation. It’s useful for allocating and deallocating memory after the program’s code. Handling page boudaries make the code somewhat hard to understand. If the program asks for <code class="language-plaintext highlighter-rouge">n</code> more bytes, do we need to allocate a new page? several? Same thing for deallocation.</p>

<p>For the first time<sup>[<a href="" title="I repent, I swear!">5</a>]</sup>, I spent some time reading about memory allocators, on this clear and concise <a href="http://dmitrysoshnikov.com/compilers/writing-a-memory-allocator/">site</a> in particular. It’s pretty much a requirement for implementing clipping in my window manager, so that’s what comes next.</p>

<h3 id="putting-programs-to-sleep">Putting programs to sleep</h3>

<p>Finally, long-standing useless system call number 2 works, <a href="https://github.com/29jm/SnowflakeOS/blob/5bbd545037487fc8f9f935b3b7f5755e9bfdd0d6/kernel/src/sys/proc.c#L273-L276">processes can sleep</a>! Well, most of the time, there are still two issues:</p>

<ul>
  <li>When all processes sleep, one has to run anyway. To avoid this situation, I need to add an “idle” process that does nothing yet never sleeps.</li>
  <li>Sleep doesn’t work on bochs, as it’s a bit more anal than QEMU about the FPU<sup>[<a href="" title="Floating Point Unit">6</a>]</sup> not being setup. I compute the number of timer ticks to sleep using <code class="language-plaintext highlighter-rouge">(ms/1000.0)*TIMER_FREQ</code>, and without initialising the FPU, this always equals 0 on bochs.</li>
</ul>

<p>Setting up the FPU isn’t entirely trivial as it’s a part of the execution context of a process that isn’t saved on task switch, so it needs special care. It’s on the shortlist though, it’s pretty important.</p>

<h3 id="background-improvements">Background improvements</h3>

<p>Notice how the wallpaper doesn’t look like a graphical glitch anymore? I picked a background, converted it to raw RGB values, stuck it in a C header with <code class="language-plaintext highlighter-rouge">xxd -i</code> and loaded it in the buffer of my background window. At 14 MiB of header file, it’s outright heavy, but thankfully once compiled it compresses down to around 2 MiB. A PNG parser is somewhere on my todo list :)</p>]]></content><author><name>Johan Manuel</name></author><category term="development" /><category term="osdev" /><category term="hobby-os" /><category term="c" /><summary type="html"><![CDATA[In the last post, I presented the first working version of SnowflakeOS’s window manager. While it worked, it had[1] a few important shortcomings.]]></summary></entry><entry><title type="html">Graphics: from pixels to windows</title><link href="/window-manager/" rel="alternate" type="text/html" title="Graphics: from pixels to windows" /><published>2019-12-15T00:00:00+01:00</published><updated>2019-12-15T00:00:00+01:00</updated><id>/window-manager</id><content type="html" xml:base="/window-manager/"><![CDATA[<p><img src="/assets/kerning.jpg" alt="Current state of the GUI" class="thumbnail" title="The background is generated by setting the ith pixel to 'i | i*512 | i % 512'" />
With SnowflakeOS starting to have more of the pieces a proper hobby OS should have, it was time to make this state of affair visible from the outside. Graphics!
Ironically, by switching away from text mode, we lose the immediate ability to print text, but as you’ll see we’re going to get it all back. At some point anyway; what’s presented here is  the beginning of this process.</p>

<h2 id="the-boring-part-switching-video-mode">The boring part: switching video mode</h2>

<p>A prerequisite to doing anything is to get the screen in a state in which we can access individual pixels instead of just characters. The traditional way of doing so is to manually call BIOS functions to get available modes and choose one while the computer is still executing in real mode. In the case of SnowflakeOS, this isn’t practical: the kernel is loaded by GRUB, so it starts executing in protected mode, where we can’t access BIOS functions. There are two ways around that:</p>
<ul>
  <li>Enable virtual 8086 mode - an emulation of real mode - long enough to set the video mode, then get back to protected mode</li>
  <li>Ask GRUB to set the correct video mode before loading our kernel</li>
</ul>

<p>The first option is the one most advertised on the osdev wiki, and at first it was the only one I knew about. Switching to virtual 8086 mode is easy enough, it involves faking an interrupt return in order to set the <code class="language-plaintext highlighter-rouge">VM</code> bit in the <code class="language-plaintext highlighter-rouge">eflags</code> register, kind of how we <a href="https://github.com/29jm/SnowflakeOS/blob/738c417d87460b82eafc2f9718ebc59b6c449de9/kernel/src/sys/proc.c#L202-L223">switched to ring 3</a> for the first usermode process. I don’t known much about this mode given that I didn’t end up implementing support for it, but there were several issues I could see taking a lot of work to resolve: how does execution get back to the kernel? do I have to implement support for virtual 8086 tasks within my scheduler? do I need to compile the executing code in 16-bit mode? what happens to my kernel stack?…
Far too much work that feels like writing boilerplate code. I was very happy to discover an alternative.</p>

<p>The second option is simpler by a long shot. Asking GRUB to set the video mode is as easy as modifying the <a href="https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Header-layout">multiboot header</a> that’s sitting at the very beginning of our kernel binary:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">.</span><span class="n">section</span> <span class="p">.</span><span class="n">multiboot</span>
    <span class="p">.</span><span class="kt">long</span> <span class="n">MAGIC</span>
    <span class="p">.</span><span class="kt">long</span> <span class="n">FLAGS_PAGE_ALIGN</span> <span class="o">|</span> <span class="n">FLAGS_MEMORY</span> <span class="o">|</span> <span class="n">FLAGS_GRAPHICS</span>
    <span class="p">.</span><span class="kt">long</span> <span class="o">-</span><span class="p">(</span><span class="n">MAGIC</span> <span class="o">+</span> <span class="n">FLAGS</span><span class="p">)</span> <span class="err">#</span> <span class="n">checksum</span>
    <span class="p">.</span><span class="kt">long</span> <span class="mh">0x00000000</span>
    <span class="p">.</span><span class="kt">long</span> <span class="mh">0x00000000</span>
    <span class="p">.</span><span class="kt">long</span> <span class="mh">0x00000000</span>
    <span class="p">.</span><span class="kt">long</span> <span class="mh">0x00000000</span>
    <span class="p">.</span><span class="kt">long</span> <span class="mh">0x00000000</span>
    <span class="p">.</span><span class="kt">long</span> <span class="mi">0</span>    <span class="err">#</span> <span class="mi">0</span> <span class="k">for</span> <span class="n">a</span> <span class="n">linear</span> <span class="n">framebuffer</span><span class="p">,</span> <span class="mi">1</span> <span class="k">for</span> <span class="n">text</span> <span class="n">mode</span>
    <span class="p">.</span><span class="kt">long</span> <span class="mi">1024</span> <span class="err">#</span> <span class="n">width</span>
    <span class="p">.</span><span class="kt">long</span> <span class="mi">768</span>  <span class="err">#</span> <span class="n">height</span>
    <span class="p">.</span><span class="kt">long</span> <span class="mi">32</span>   <span class="err">#</span> <span class="n">bpp</span>
</code></pre></div></div>
<p>The downside is that you can’t choose a resolution or bit depth dynamically: if the precise mode you asked for isn’t available, GRUB will choose one for you. That sounds like a fair tradeoff to me.</p>

<p>GRUB gives us a framebuffer described by the following entries in the <a href="https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format">multiboot structure</a>:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
    <span class="kt">uint64_t</span> <span class="n">address</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">pitch</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">width</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">height</span><span class="p">;</span>
    <span class="kt">uint8_t</span> <span class="n">bpp</span><span class="p">;</span>
    <span class="kt">uint8_t</span> <span class="n">type</span><span class="p">;</span>
    <span class="c1">// Technically, the layout of the following fields depends on the value of `type`</span>
    <span class="kt">uint8_t</span> <span class="n">red_position</span><span class="p">,</span> <span class="n">red_mask_size</span><span class="p">;</span>
    <span class="kt">uint8_t</span> <span class="n">green_position</span><span class="p">,</span> <span class="n">green_mask_size</span><span class="p">;</span>
    <span class="kt">uint8_t</span> <span class="n">blue_position</span><span class="p">,</span> <span class="n">blue_mask_size</span><span class="p">;</span>
<span class="p">}</span> <span class="n">__attribute__</span> <span class="p">((</span><span class="n">packed</span><span class="p">))</span> <span class="n">fb_info_t</span><span class="p">;</span>
</code></pre></div></div>
<p>Some fields are a bit obscure here: <code class="language-plaintext highlighter-rouge">pitch</code> is the number of bytes per rows, <code class="language-plaintext highlighter-rouge">bpp</code> is the bit depth, <code class="language-plaintext highlighter-rouge">type</code> should be 0 (otherwise GRUB gave us text buffer), and the color fields indicate the pixel layout.<br />
I think the reason <code class="language-plaintext highlighter-rouge">pitch</code> is given here is that there can be padding bytes between each “line” of pixels, so it may not necessarily equal <code class="language-plaintext highlighter-rouge">width*bpp/8</code>, though it does for QEMU and Bochs.</p>

<p>The <code class="language-plaintext highlighter-rouge">address</code> field refers to a physical address, so we mustn’t forget to map it after enabling paging. For a 1024x768x32 mode, the framebuffer is 3 MiB large, so it’s not exactly a trivial allocation in the kernel heap, where I chose to map it. An alternative would be to map it on request in processes’ address spaces where addresses are aplenty, but I’d rather not expose it raw to usermode applications.</p>

<h2 id="rocking-that-framebuffer">Rocking that framebuffer</h2>

<h3 id="plotting-pixels">Plotting pixels</h3>

<p>Now that we have an address to write to, plotting a pixel is a matter of computing its offset and knowing its format. The address of the pixel at (x, y) is given by</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">uint32_t</span><span class="o">*</span> <span class="n">offset</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint32_t</span><span class="o">*</span><span class="p">)</span> <span class="p">(</span><span class="n">address</span> <span class="o">+</span> <span class="n">y</span><span class="o">*</span><span class="n">pitch</span> <span class="o">+</span> <span class="n">x</span><span class="o">*</span><span class="n">bpp</span><span class="o">/</span><span class="mi">8</span><span class="p">);</span>
</code></pre></div></div>

<p>Once you’re there, all that remains is implementing some drawing primitives. Rectangles, lines, borders…<br />
Line drawing algorithms are somewhat convoluted, there are several and I ended up implementing Bresenham’s algorithm which is well-detailed on <a href="https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm">wikipedia</a>.</p>

<h3 id="getting-our-text-back-the-psf-format">Getting our text back, the PSF format</h3>

<figure>
    <img src="/assets/psf_font.png" />
    <figcaption>128 characters ought to be enough for anybody</figcaption>
</figure>

<p>The most straightforward way to draw text has to be through bitmap fonts. In a bitmap font, a character is represented by an array of bits, with say each <code class="language-plaintext highlighter-rouge">n</code> bits representing a line of pixels in the character. For example, here’s a very crude ‘O’:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">0</span> <span class="mi">1</span> <span class="mi">0</span>
<span class="mi">1</span> <span class="mi">0</span> <span class="mi">1</span>
<span class="mi">0</span> <span class="mi">1</span> <span class="mi">0</span>
</code></pre></div></div>
<p>It turns out there’s a dead easy format still in wide use: <a href="https://www.win.tue.nl/~aeb/linux/kbd/font-formats-1.html">PSF</a>, for <em>PC Screen Font</em>, used by virtual consoles in Linux.</p>

<p>There are two versions of this format:</p>
<ul>
  <li>PSF1: fixed number of characters, 8 pixels wide, variable character height</li>
  <li>PSF2: variable number of characters, variable character size</li>
</ul>

<p>Both formats have a short header at the beginning of the file. On my machine, all fonts in <code class="language-plaintext highlighter-rouge">/usr/share/kbd/consolefonts</code> seem to be PSF1. In this format, the header looks like this:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
    <span class="kt">uint8_t</span> <span class="n">magic</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>
    <span class="kt">uint8_t</span> <span class="n">mode</span><span class="p">;</span>
    <span class="kt">uint8_t</span> <span class="n">height</span><span class="p">;</span>
<span class="p">}</span> <span class="n">font_header_t</span><span class="p">;</span>
</code></pre></div></div>
<p>Where <code class="language-plaintext highlighter-rouge">magic</code> should equal <code class="language-plaintext highlighter-rouge">0x36, 0x04</code>, <code class="language-plaintext highlighter-rouge">mode</code> contains information about the number of characters and unicode support (which I haven’t dealt with), and <code class="language-plaintext highlighter-rouge">height</code> is the height of each character in pixels.<br />
The actual font starts right after the header, so the offset of an ASCII character <code class="language-plaintext highlighter-rouge">c</code> in the font file, in bytes, is given by</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">uint32_t</span> <span class="n">offset</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">font_header_t</span><span class="p">)</span> <span class="o">+</span> <span class="n">c</span><span class="o">*</span><span class="n">height</span><span class="p">;</span>
</code></pre></div></div>
<p>Drawing a character is then a matter of checking individual bits, line by line, and plotting pixels accordingly.<br />
I extracted the font used in my console with</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">setfont</span> <span class="o">-</span><span class="n">o</span> <span class="n">font</span><span class="p">.</span><span class="n">psf</span><span class="p">;</span> <span class="n">xxd</span> <span class="o">-</span><span class="n">i</span> <span class="n">font</span><span class="p">.</span><span class="n">psf</span> <span class="o">&gt;</span> <span class="n">font</span><span class="p">.</span><span class="n">h</span>
</code></pre></div></div>
<p>and used that one. The characters are those shown in the image above, in 8x16 format.</p>

<h2 id="from-rectangles-to-windows-a-window-manager">From rectangles to windows: a window manager</h2>

<p>Until now we’ve only drawn shapes the the screen. What turns a shape into a window? By my definition, a window manager (WM).</p>

<h3 id="design">Design</h3>

<p>In SnowflakeOS, the window manager handles only two concepts:</p>
<ul>
  <li><strong>windows</strong>: those are rectangular buffers with an (x, y) position, a z-order and a few flags</li>
  <li><strong>focus</strong>: who gets user input?</li>
</ul>

<p>It knows nothing of window decorations, concepts of desktop background, taskbar, or even mouse pointer. Those things will be windows, managed by userspace applications, clients of the WM.<br />
As far as I know, this minimalist approach is somewhat close to how weston works, a window manager for wayland.</p>

<p>The WM needs to communicate with applications wishing to use windows. Usually, the WM is a userspace program, so this is done using a form of inter-process communication. I decided against implementing such a system for now - I tried my hand at a virtual file system, pipes and all, but I felt I didn’t have enough background to properly design anything, or to implement the usual APIs of Unixes.<br />
Thus, I implemented the WM in the kernel, and programs communicate with it through system calls. It’s a pretty crude and non-general way of communicating, but it works for now.</p>

<h3 id="the-userspace-api">The userspace API</h3>

<p>I introduced a library for SnowflakeOS programs, thoughtfully named <a href="https://github.com/29jm/SnowflakeOS/blob/738c417d87460b82eafc2f9718ebc59b6c449de9/snow/include/snow.h">snow</a>, which wraps system calls in pretty C functions.<br />
It offers a <code class="language-plaintext highlighter-rouge">snow_open_window</code> function which allocates a buffer of the window’s size. Drawing functions then write to that buffer, and the program asks for that window to be drawn to screen by calling <code class="language-plaintext highlighter-rouge">snow_render_window</code>. There are no GUI functions right now - only mockups - but they’ll sit between those two calls. Closing the window is then done though <code class="language-plaintext highlighter-rouge">snow_close_window</code>.</p>

<p>Here’s an example of what can be done right now:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;snow.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">window_t</span><span class="o">*</span> <span class="n">win</span> <span class="o">=</span> <span class="n">snow_open_window</span><span class="p">(</span><span class="s">"A static window"</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">150</span><span class="p">,</span> <span class="n">WM_NORMAL</span><span class="p">);</span>

    <span class="n">snow_draw_window</span><span class="p">(</span><span class="n">win</span><span class="p">);</span> <span class="c1">// Draws the title bar and borders</span>
    <span class="n">snow_draw_string</span><span class="p">(</span><span class="n">win</span><span class="o">-&gt;</span><span class="n">fb</span><span class="p">,</span> <span class="s">"Lorem Ipsum"</span><span class="p">,</span> <span class="mi">45</span><span class="p">,</span> <span class="mi">55</span><span class="p">,</span> <span class="mh">0x00AA1100</span><span class="p">);</span>
    <span class="n">snow_draw_border</span><span class="p">(</span><span class="n">win</span><span class="o">-&gt;</span><span class="n">fb</span><span class="p">,</span> <span class="mi">40</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="s">"Lorem Ipsum"</span><span class="p">)</span><span class="o">*</span><span class="mi">8</span><span class="o">+</span><span class="mi">10</span><span class="p">,</span> <span class="mi">26</span><span class="p">,</span> <span class="mh">0x000000</span><span class="p">);</span>

    <span class="k">while</span> <span class="p">(</span><span class="nb">true</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">snow_render_window</span><span class="p">(</span><span class="n">win</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">snow_close_window</span><span class="p">(</span><span class="n">win</span><span class="p">);</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Giving this result:</p>

<figure>
    <img src="/assets/static_window.png" />
    <figcaption>that look has to change, I know</figcaption>
</figure>

<h3 id="implementation">Implementation</h3>

<h4 id="registering-a-window">Registering a window</h4>

<p>This means appending a given buffer to a list of windows to be drawn, and assigning it a z-order and unique id, a window being defined as</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">typedef</span> <span class="k">struct</span> <span class="n">_wm_window_t</span> <span class="p">{</span>
    <span class="k">struct</span> <span class="n">_wm_window_t</span><span class="o">*</span> <span class="n">next</span><span class="p">;</span>
    <span class="k">struct</span> <span class="n">_wm_window_t</span><span class="o">*</span> <span class="n">prev</span><span class="p">;</span>
    <span class="n">fb_t</span> <span class="n">fb</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">x</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">y</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">z</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">id</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">flags</span><span class="p">;</span>
<span class="p">}</span> <span class="n">wm_window_t</span><span class="p">;</span>
</code></pre></div></div>
<p>This is my second use of intrusive lists in SnowflakeOS. I really ought to make a utility library to handle those, as I’ve had to write some very repetitive code in functions such as <code class="language-plaintext highlighter-rouge">wm_find_with_id</code>, <code class="language-plaintext highlighter-rouge">wm_find_with_flags</code> or <code class="language-plaintext highlighter-rouge">wm_find_with_z</code>. The lack of lambdas in C can really be felt in this situation.</p>

<h4 id="handling-z-order">Handling z-order</h4>

<p>This was less trivial than anticipated! Here’s how I handled it.
Z-orders are consecutive natural numbers, with 0 being the least visible window (e.g. a desktop background), and higher numbers being drawn on top of lower numbers.<br />
A newly opened window is assigned the highest z-order. When a window is closed, z-orders are shifted so that there is no gap between 0 and the highest z.<br />
Windows with a flag like <code class="language-plaintext highlighter-rouge">WM_FOREGROUND</code> will always stay on top of others, and similarly, those with <code class="language-plaintext highlighter-rouge">WM_BACKGROUND</code> fight for the lowest z-order.</p>

<p>I’m making things up as I go, to be honest. I’m sure I’ll figure out the good from the bad in due time :)</p>

<h4 id="drawing-a-full-frame">Drawing a full frame</h4>

<p><em>Edit: this is a poor implementation, see the next post for a better one.</em></p>

<p>The goal here is to draw windows in the correct z-order. Keeping in mind that window buffers are in the clients’ address spaces, we have at least two options:</p>
<ol>
  <li>at a regular interval, iterate over our windows in correct z-order, switch to their respective address space and copy their buffer to the screen</li>
  <li>when a client tells the WM that its window can be drawn, check if it’s their turn and if so draw them. Otherwise, lose a frame for that window.</li>
</ol>

<p>With option 1, I don’t know how to avoid drawing a window to the screen when its buffer may be in a “partially drawn” state, plus the method of switching address spaces is pretty barbaric, and I’m sure very slow.</p>

<p>I went for option 2, which I believe <a href="https://github.com/29jm/SnowflakeOS/blob/738c417d87460b82eafc2f9718ebc59b6c449de9/kernel/src/misc/wm.c">the code</a> can explain quite well:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">wm_render_window</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="n">win_id</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">wm_window_t</span><span class="o">*</span> <span class="n">win</span> <span class="o">=</span> <span class="n">wm_get_window</span><span class="p">(</span><span class="n">win_id</span><span class="p">);</span>

    <span class="c1">// If it's not our turn, return</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">win</span><span class="o">-&gt;</span><span class="n">z</span> <span class="o">!=</span> <span class="n">current_z</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// Render the window to the off-screen buffer</span>
    <span class="kt">uint32_t</span><span class="o">*</span> <span class="n">off</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint32_t</span><span class="o">*</span><span class="p">)</span> <span class="p">(</span><span class="n">fb</span><span class="p">.</span><span class="n">address</span> <span class="o">+</span> <span class="n">win</span><span class="o">-&gt;</span><span class="n">y</span><span class="o">*</span><span class="n">fb</span><span class="p">.</span><span class="n">pitch</span> <span class="o">+</span> <span class="n">win</span><span class="o">-&gt;</span><span class="n">x</span><span class="o">*</span><span class="n">fb</span><span class="p">.</span><span class="n">bpp</span><span class="o">/</span><span class="mi">8</span><span class="p">);</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">uint32_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">win</span><span class="o">-&gt;</span><span class="n">fb</span><span class="p">.</span><span class="n">height</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">memcpy</span><span class="p">(</span><span class="n">off</span><span class="p">,</span> <span class="p">(</span><span class="kt">void</span><span class="o">*</span><span class="p">)</span> <span class="p">(</span><span class="n">win</span><span class="o">-&gt;</span><span class="n">fb</span><span class="p">.</span><span class="n">address</span> <span class="o">+</span> <span class="n">i</span><span class="o">*</span><span class="n">win</span><span class="o">-&gt;</span><span class="n">fb</span><span class="p">.</span><span class="n">pitch</span><span class="p">),</span> <span class="n">win</span><span class="o">-&gt;</span><span class="n">fb</span><span class="p">.</span><span class="n">pitch</span><span class="p">);</span>
        <span class="n">off</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint32_t</span><span class="o">*</span><span class="p">)</span> <span class="p">((</span><span class="kt">uintptr_t</span><span class="p">)</span> <span class="n">off</span> <span class="o">+</span> <span class="n">fb</span><span class="p">.</span><span class="n">pitch</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="c1">// If all windows are drawn, write to the screen</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">current_z</span> <span class="o">==</span> <span class="n">wm_get_max_z</span><span class="p">())</span> <span class="p">{</span>
        <span class="n">current_z</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="n">fb_render</span><span class="p">(</span><span class="n">fb</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="n">current_z</span><span class="o">++</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Apart from dropping draw calls, this method has the downside that a slow client will slow the whole thing down: the WM will wait for its draw call and drop all others in the mean time.<br />
Good thing SnowflakeOS apps are always lightning fast ;)</p>

<h2 id="the-next-steps">The next steps</h2>

<p>In no particular order, this is what I want to get to:</p>
<ul>
  <li>cleaning up the code: these changes brought a lot of mess</li>
  <li>windows need to get keyboard and mouse events
    <ul>
      <li>a mouse pointer</li>
      <li>moving windows around</li>
      <li>porting my “Mandelbrot visualizer” app to SnowflakeOS</li>
    </ul>
  </li>
  <li>C++ support in userspace</li>
  <li>a GUI system</li>
</ul>

<h2 id="long-time-no-c">Long time no C</h2>

<p>It’s been a while since the last entry in this blog, but I’m hoping to pick up the pace. I’ve been very busy programming-wise with school projects, but less so now.<br />
One can’t reasonably spend their time writing C#, can they?</p>]]></content><author><name>Johan Manuel</name></author><category term="development" /><category term="osdev" /><category term="hobby-os" /><category term="c" /><summary type="html"><![CDATA[With SnowflakeOS starting to have more of the pieces a proper hobby OS should have, it was time to make this state of affair visible from the outside. Graphics! Ironically, by switching away from text mode, we lose the immediate ability to print text, but as you’ll see we’re going to get it all back. At some point anyway; what’s presented here is the beginning of this process.]]></summary></entry><entry><title type="html">Mouse support and other PS/2 shenanigans</title><link href="/of-mice-and-keyboards/" rel="alternate" type="text/html" title="Mouse support and other PS/2 shenanigans" /><published>2019-10-14T00:00:00+02:00</published><updated>2019-10-14T00:00:00+02:00</updated><id>/of-mice-and-keyboards</id><content type="html" xml:base="/of-mice-and-keyboards/"><![CDATA[<p><img src="/assets/sos-kbd.png" alt="Keyboard and mouse both working" class="thumbnail" title="Notice the stray 'a'. Thanks, QEMU, I'll debug that some other day." />
At the beginning of last week, I was looking over my keyboard code, still wondering what kind of interface could be exposed to userspace and be useful, and also wondering why my scan codes seemed to have no physical relation to any known keyboard layouts.<br />
So I went over to OSDev’s article about <a href="https://wiki.osdev.org/Keyboard">PS/2 keyboards</a>, which sent me to the article about the <a href="https://wiki.osdev.org/%228042%22_PS/2_Controller">PS/2 controller</a>, and I knew I wanted to do things properly, and at the same time, gain mouse support.</p>

<p>Here above you can see keyboard input being written to the screen, and mouse coordinates on the bottom right corner - wait for it.</p>

<h2 id="the-ps2-controller">The PS/2 controller</h2>

<p>Source: <a href="https://github.com/29jm/SnowflakeOS/blob/357ecc40169c2b8e02c7866ea383171cf436def4/kernel/src/devices/ps2.c">ps2.c</a>, <a href="https://github.com/29jm/SnowflakeOS/blob/357ecc40169c2b8e02c7866ea383171cf436def4/kernel/include/kernel/ps2.h">ps2.h</a></p>

<p>“PS/2” stands for “Personal System/2” and is the old green or purple round port which fit old keyboards. These ports were linked to the PS/2 “8042” controller, an old chip which, miraculously, still manages to exist in some form in modern computers. Indeed, while PS/2 devices have been replaced by USB ones, the BIOS (most of them anyway) offers an emulation of the 8042 on top of USB. Ideally I’d implement the USB protocol, but this is an OS project, not an USB project, and dealing with PS/2 devices is easy in comparison.</p>

<p>The steps to initialize the PS/2 controller to some base state are numerous and detailed in the relevant section of the wiki page. I’ve implemented them in <a href="https://github.com/29jm/SnowflakeOS/blob/357ecc40169c2b8e02c7866ea383171cf436def4/kernel/src/devices/ps2.c#L12-L158">ps2.c</a>, a ~140 lines function full of hopefully well commented IO. Most of it looks something like this:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Give the controller a command</span>
<span class="n">ps2_write</span><span class="p">(</span><span class="n">PS2_CMD</span><span class="p">,</span> <span class="n">PS2_WRITE_CONFIG</span><span class="p">);</span>
<span class="c1">// and its associated data byte</span>
<span class="n">ps2_write</span><span class="p">(</span><span class="n">PS2_DATA</span><span class="p">,</span> <span class="n">config</span><span class="p">);</span>
</code></pre></div></div>

<p>Where <code class="language-plaintext highlighter-rouge">ps2_write(port, byte)</code> is a wrapper around the x86 instruction <code class="language-plaintext highlighter-rouge">outb</code> which writes a byte to an IO port. This function also makes sure the controller is ready to receive a byte, and similarly, <code class="language-plaintext highlighter-rouge">ps2_read(port)</code> makes sure the controller has sent us a byte.</p>

<p>The outline of the initialization steps is that first, you need to pray that there really is a PS/2 controller to talk to: the correct way to do this is to query the ACPI tables, but hey, QEMU and Bochs are guaranteed to have one. Then, you need to test if there is a second controller (which usually handles the mouse) and run self-tests. The last step is to reset devices plugged into our functionning PS/2 controllers. Finally, we query their identity - keyboard, mice - and start the relevant device drivers.</p>

<p>Implementing the various steps didn’t take me long; but chasing its bugs did. Specifically, a bug that appeared only on QEMU. After initialization of the PS/2 controllers, my keyboard code stopped working. The keyboard simply didn’t send IRQs. And to get the mouse working, at first I needed to disable my keyboard code!<br />
I finally figured it out by looking at the controller’s configuration byte: I was inadvertently setting a bit that disabled the keyboard clock. Somehow Bochs doesn’t care if it’s set when enabling IRQs from devices, it just unsets it, however QEMU doesn’t let it fly.</p>

<h2 id="a-ps2-mouse-driver">A PS/2 mouse driver</h2>

<p><img src="/assets/mouse_crash.gif" alt="Moving my mouse to the left crashed Bochs" title="I have *no* idea why or how this is animated" /></p>

<p>I’ve had weird crashes implementing this. This happened when I moved my mouse to the left in Bochs!</p>

<p>Source: <a href="https://github.com/29jm/SnowflakeOS/blob/357ecc40169c2b8e02c7866ea383171cf436def4/kernel/src/devices/mouse.c">mouse.c</a>, <a href="https://github.com/29jm/SnowflakeOS/blob/357ecc40169c2b8e02c7866ea383171cf436def4/kernel/include/kernel/mouse.h">mouse.h</a></p>

<p>First, one needs to enable reporting from the mouse, it then starts sending out IRQs on line 12. Each IRQ corresponds to a byte available for reading from the PS/2 controller’s data port, <code class="language-plaintext highlighter-rouge">0x60</code>. The bytes must be treated in packets of three to four depending on the type of mouse we detected, or features we enabled. The bytes are sent in this order:</p>

<ul>
  <li>flags: direction of the x and y movements, state of mice buttons, others…</li>
  <li>x movement</li>
  <li>y movement</li>
</ul>

<p>and if there is a fourth byte, it contains scroll wheel movements and the state of buttons four and five of the mouse. Here’s the code receiving the bytes:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">mouse_handle_interrupt</span><span class="p">(</span><span class="n">registers_t</span><span class="o">*</span> <span class="n">regs</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">UNUSED</span><span class="p">(</span><span class="n">regs</span><span class="p">);</span>

    <span class="kt">uint8_t</span> <span class="n">byte</span> <span class="o">=</span> <span class="n">ps2_read</span><span class="p">(</span><span class="n">PS2_DATA</span><span class="p">);</span>

    <span class="c1">// Try to stay synchronized by discarding obviously out of place bytes</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">current_byte</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="p">(</span><span class="n">byte</span> <span class="o">&amp;</span> <span class="n">MOUSE_ALWAYS_SET</span><span class="p">))</span> <span class="p">{</span>
        <span class="k">return</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">packet</span><span class="p">[</span><span class="n">current_byte</span><span class="p">]</span> <span class="o">=</span> <span class="n">byte</span><span class="p">;</span>
    <span class="n">current_byte</span> <span class="o">=</span> <span class="p">(</span><span class="n">current_byte</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="n">bytes_per_packet</span><span class="p">;</span>

    <span class="c1">// We've received a full packet</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">current_byte</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">mouse_handle_packet</span><span class="p">();</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>All we need to do then in <code class="language-plaintext highlighter-rouge">mouse_handle_packet</code> is keeping track of the mouse movements, and at a later time, making them available to userspace. Then, and only then, we’ll get a mouse pointer.</p>

<h2 id="a-better-ps2-keyboard-driver">A better PS/2 keyboard driver</h2>

<p>Source: <a href="https://github.com/29jm/SnowflakeOS/blob/617e66a7107bd5821ef381a64598fa33c8891c08/kernel/src/devices/kbd.c">kbd.c</a>, <a href="https://github.com/29jm/SnowflakeOS/blob/617e66a7107bd5821ef381a64598fa33c8891c08/kernel/include/kernel/kbd.h">kbd.h</a></p>

<p>Now that I was initializing the PS/2 controller instead of letting it do its thing, my driver was working all funky. The reason lies in scan code sets.</p>

<p>First things first, a scan code is one or more bytes sent by the keyboard when a key event happens. For instance, pressing ‘D’ on my keyboard may send <code class="language-plaintext highlighter-rouge">0x23</code>, and releasing it may send <code class="language-plaintext highlighter-rouge">0xF0</code> followed by <code class="language-plaintext highlighter-rouge">0x23</code>. Some keys - in some scan code sets - send up to 8 bytes!</p>

<p>Now, a scan code set is the map between a physical key and the bytes the keyboard sends, and there are basically 3 of them. The previous example was true for scan code set 2; in scan code set 1, pressing ‘D’ would have caused the keyboard to send <code class="language-plaintext highlighter-rouge">0x20</code>, and releasing it would have given <code class="language-plaintext highlighter-rouge">0xA0</code>.</p>

<p>When I did zero PS/2 controller initialization, my keyboard defaulted to scan code set 1, with a twist: scan code translation, i.e. the controller converting scan codes to old IBM-PC compatible scan codes. This scan code set and weird translation mechanism are too vintage even for SnowflakeOS; it was time to handle scan code set 2.</p>

<p>In this shiny new 1983 scan code set, things are a bit more complicated than with scan code set 1. There are two categories of keys:<br />
There are the simple keys, which send a one-byte scan code when pressed, and <code class="language-plaintext highlighter-rouge">0xF0</code> followed by that same scan code when released.<br />
Then there are the other keys, which send multibyte scan codes. They can be identified as they send an <code class="language-plaintext highlighter-rouge">0xE0</code> byte first, followed by a <code class="language-plaintext highlighter-rouge">0xF0</code> byte in case of a release event, followed by one or more bytes of scan code.</p>

<p>Now keep in mind that we receive bytes one at a time in our interrupt handler, so we need to keep track of previously received bytes until we’ve identified a whole key event, and the difficulty is in the variable length of such packets. Obviously, what we need is some kind of state machine and a buffer to hold our bytes. Here’s the function in <a href="https://github.com/29jm/SnowflakeOS/blob/617e66a7107bd5821ef381a64598fa33c8891c08/kernel/src/devices/kbd.c#L133-L179">kbd.c</a> in charge of updating the state of the driver’s state machine:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">bool</span> <span class="nf">kbd_process_byte</span><span class="p">(</span><span class="n">kbd_context_t</span><span class="o">*</span> <span class="n">ctx</span><span class="p">,</span> <span class="kt">uint8_t</span> <span class="n">sc</span><span class="p">,</span> <span class="n">kbd_event_t</span><span class="o">*</span> <span class="n">event</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">scancode</span><span class="p">[</span><span class="n">ctx</span><span class="o">-&gt;</span><span class="n">current</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">sc</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">sc_pos</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">current</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>

    <span class="k">switch</span> <span class="p">(</span><span class="n">ctx</span><span class="o">-&gt;</span><span class="n">state</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">case</span> <span class="n">KBD_NORMAL</span><span class="p">:</span> <span class="c1">// Not in the middle of a scancode</span>
            <span class="n">event</span><span class="o">-&gt;</span><span class="n">pressed</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>

            <span class="k">if</span> <span class="p">(</span><span class="n">sc</span> <span class="o">==</span> <span class="mh">0xF0</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">state</span> <span class="o">=</span> <span class="n">KBD_RELEASE_SHORT</span><span class="p">;</span>
            <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">sc</span> <span class="o">==</span> <span class="mh">0xE0</span> <span class="o">||</span> <span class="n">sc</span> <span class="o">==</span> <span class="mh">0xE1</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">state</span> <span class="o">=</span> <span class="n">KBD_CONTINUE</span><span class="p">;</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">current</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
                <span class="n">event</span><span class="o">-&gt;</span><span class="n">key_code</span> <span class="o">=</span> <span class="n">simple_sc_to_kc</span><span class="p">[</span><span class="n">sc</span><span class="p">];</span>
            <span class="p">}</span>

            <span class="k">break</span><span class="p">;</span>
        <span class="k">case</span> <span class="n">KBD_RELEASE_SHORT</span><span class="p">:</span> <span class="c1">// We received `0xF0` previously</span>
            <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">state</span> <span class="o">=</span> <span class="n">KBD_NORMAL</span><span class="p">;</span>
            <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">current</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
            <span class="n">event</span><span class="o">-&gt;</span><span class="n">key_code</span> <span class="o">=</span> <span class="n">simple_sc_to_kc</span><span class="p">[</span><span class="n">sc</span><span class="p">];</span>
            <span class="n">event</span><span class="o">-&gt;</span><span class="n">pressed</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>

            <span class="k">break</span><span class="p">;</span>
        <span class="k">case</span> <span class="n">KBD_CONTINUE</span><span class="p">:</span> <span class="c1">// We received `0xE0` at some point before</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">sc</span> <span class="o">==</span> <span class="mh">0xF0</span> <span class="o">&amp;&amp;</span> <span class="n">sc_pos</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">event</span><span class="o">-&gt;</span><span class="n">pressed</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
                <span class="k">break</span><span class="p">;</span>
            <span class="p">}</span>

            <span class="k">if</span> <span class="p">(</span><span class="n">kbd_is_valid_scancode</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ctx</span><span class="o">-&gt;</span><span class="n">scancode</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">sc_pos</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">event</span><span class="o">-&gt;</span><span class="n">key_code</span><span class="p">))</span> <span class="p">{</span>
                <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">state</span> <span class="o">=</span> <span class="n">KBD_NORMAL</span><span class="p">;</span>
                <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">current</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
            <span class="p">}</span>

            <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">state</span> <span class="o">==</span> <span class="n">KBD_NORMAL</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>It’s quite a big function, and still most of the heavy lifting is done in <code class="language-plaintext highlighter-rouge">kbd_is_valid_scancode(bytes, len, &amp;key_code)</code>, in charge of identifying valid multibyte scancodes and translating those into key codes. Our <code class="language-plaintext highlighter-rouge">kbd_process_byte</code> function indicates that a valid scan code has been received by returning <code class="language-plaintext highlighter-rouge">true</code>, and makes the key event available through its <code class="language-plaintext highlighter-rouge">event</code> parameter.<br />
If you’re really paying attention, you may notice a possible buffer overflow with <code class="language-plaintext highlighter-rouge">ctx-&gt;scancode[ctx-&gt;current++]</code>, but thankfully <code class="language-plaintext highlighter-rouge">kbd_is_valid_scancode</code> is guaranteed to return <code class="language-plaintext highlighter-rouge">true</code> before that… Hmm, this is a bit too clunky, perhaps I’ll put a proper check back in just in case I ever modify <code class="language-plaintext highlighter-rouge">kbd_is_valid_scancode</code>’s interface in the future.</p>

<p>Anyway, SnowflakeOS can now handle a full QWERTY layout. This is a bit dumb as I myself have a French, AZERTY layout; let’s just say I’m being international :)<br />
Ideally I’d move most of the keycode translation stuff to userspace where a keymap could be loaded, and there’d be no more problems.</p>]]></content><author><name>Johan Manuel</name></author><category term="development" /><category term="osdev" /><category term="hobby-os" /><category term="c" /><summary type="html"><![CDATA[At the beginning of last week, I was looking over my keyboard code, still wondering what kind of interface could be exposed to userspace and be useful, and also wondering why my scan codes seemed to have no physical relation to any known keyboard layouts. So I went over to OSDev’s article about PS/2 keyboards, which sent me to the article about the PS/2 controller, and I knew I wanted to do things properly, and at the same time, gain mouse support.]]></summary></entry><entry><title type="html">On context switching and C programs in userland</title><link href="/switches-and-knobs/" rel="alternate" type="text/html" title="On context switching and C programs in userland" /><published>2019-10-06T00:00:00+02:00</published><updated>2019-10-06T00:00:00+02:00</updated><id>/switches-and-knobs</id><content type="html" xml:base="/switches-and-knobs/"><![CDATA[<p><img src="/assets/garbage.jpg" alt="Executing garbage" class="thumbnail" title="It's abstract art, okay?" />
In the last post, I discussed how I implemented collaborative execution in SnowflakeOS through the <code class="language-plaintext highlighter-rouge">iret</code> instruction. Well, at that time the implementation wasn’t finished, even though I thought it was: I wasn’t restoring general purpose registers. This led to some pretty nice bugs, as pictured above.</p>

<h2 id="context-switching">Context switching</h2>

<p>I noticed that issue and at first decided to tackle it my own way, <code class="language-plaintext highlighter-rouge">mov</code>ing the contents of my <code class="language-plaintext highlighter-rouge">registers_t</code> structure to the corresponding registers, but it proved a bit difficult. It would have been doable with more thought, but instead I searched the internet for the “usual” way to restore context.</p>

<p>It turns out there’s a very elegant way to do it: instead of using <code class="language-plaintext highlighter-rouge">iret</code> everytime, simply switch stack and let the execution get back to the interrupt handler by just letting execution reach the end of the function.<br />
This works because the stack state of every interrupted process is the same when getting to the stack-switching part of execution: what we do is simply pop registers from the next process’s stack, not the one that was last interrupted.<br />
For now the subroutine is implemented in assembly as I mainly copied it from <a href="https://wiki.osdev.org/Multitasking_Systems">a wiki page</a> but I should be able to turn it into <code class="language-plaintext highlighter-rouge">C</code> no problem, contrary to what the page says. Here’s the current code:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">proc_switch_process:</span> <span class="cp"># void proc_switch_process();
</span>    <span class="cp"># Save register state
</span>    <span class="n">push</span> <span class="o">%</span><span class="n">ebx</span>
    <span class="n">push</span> <span class="o">%</span><span class="n">esi</span>
    <span class="n">push</span> <span class="o">%</span><span class="n">edi</span>
    <span class="n">push</span> <span class="o">%</span><span class="n">ebp</span>

    <span class="cp"># current_process-&gt;esp = %esp
</span>    <span class="n">mov</span> <span class="n">current_process</span><span class="p">,</span> <span class="o">%</span><span class="n">eax</span>
    <span class="n">mov</span> <span class="o">%</span><span class="n">esp</span><span class="p">,</span> <span class="mi">24</span><span class="p">(</span><span class="o">%</span><span class="n">eax</span><span class="p">)</span>

    <span class="cp"># %eax = current_process = current_process-&gt;next
</span>    <span class="n">mov</span> <span class="p">(</span><span class="o">%</span><span class="n">eax</span><span class="p">),</span> <span class="o">%</span><span class="n">eax</span>
    <span class="n">mov</span> <span class="o">%</span><span class="n">eax</span><span class="p">,</span> <span class="n">current_process</span>

    <span class="cp"># Set esp0 to the next process's kernel stack in the TSS
</span>    <span class="n">push</span> <span class="o">%</span><span class="n">eax</span>
    <span class="n">push</span> <span class="mi">20</span><span class="p">(</span><span class="o">%</span><span class="n">eax</span><span class="p">)</span> <span class="err">#</span> <span class="n">kernel_stack</span>
    <span class="n">call</span> <span class="n">gdt_set_kernel_stack</span>
    <span class="n">add</span> <span class="err">$</span><span class="mi">4</span><span class="p">,</span> <span class="o">%</span><span class="n">esp</span>
    <span class="n">pop</span> <span class="o">%</span><span class="n">eax</span>

    <span class="cp"># Switch to the next process's kernel stack
</span>    <span class="n">mov</span> <span class="mi">24</span><span class="p">(</span><span class="o">%</span><span class="n">eax</span><span class="p">),</span> <span class="o">%</span><span class="n">esp</span>

    <span class="cp"># Switch page directory
</span>    <span class="n">mov</span> <span class="mi">16</span><span class="p">(</span><span class="o">%</span><span class="n">eax</span><span class="p">),</span> <span class="o">%</span><span class="n">ebx</span> <span class="err">#</span> <span class="n">directory</span>
    <span class="n">mov</span> <span class="o">%</span><span class="n">ebx</span><span class="p">,</span> <span class="o">%</span><span class="n">cr3</span>

    <span class="cp"># Restore registers from the next process's kernel stack
</span>    <span class="n">pop</span> <span class="o">%</span><span class="n">ebp</span>
    <span class="n">pop</span> <span class="o">%</span><span class="n">edi</span>
    <span class="n">pop</span> <span class="o">%</span><span class="n">esi</span>
    <span class="n">pop</span> <span class="o">%</span><span class="n">ebx</span>

    <span class="n">ret</span>
</code></pre></div></div>

<p>That leaves the problem of how to switch to tasks which haven’t been started yet, and thus haven’t had the chance to be interrupted: we can’t switch to their kernel stack to restore the process’s context, there’s nothing there. I haven’t thought this through, but I don’t think we can <code class="language-plaintext highlighter-rouge">iret</code> manually a second time, we’d mess up the kernel stack of the currently executing process.<br />
I opted for the solution of setting up that stack manually in <code class="language-plaintext highlighter-rouge">proc_run_code</code>. It’s ugly (<a href="https://github.com/29jm/SnowflakeOS/blob/f14f7cc4b6b176170910cfb65911bc8e7826257e/kernel/src/sys/proc.c#L92-L134">see for yourselves</a>), but hey, it works. I’ll make something nicer at some point, I haven’t researched how it’s usually done.</p>

<p>Implementing preemptive multitasking, i.e. interrupting and resuming tasks without asking them was then simply a matter of calling <code class="language-plaintext highlighter-rouge">proc_switch_context</code> from my timer interrupt handler.</p>

<p>If you look at the commit implementing all this, <a href="https://github.com/29jm/SnowflakeOS/commit/f14f7cc4b6b176170910cfb65911bc8e7826257e#diff-332df72cc6226373195d53da4685f4e6R216">here</a>, you’ll notice that I’m not calling <code class="language-plaintext highlighter-rouge">iret</code> when first entering usermode. And yet the code seemed to work, and it in fact sort of did! By a miracle of chance, the <code class="language-plaintext highlighter-rouge">ret</code> instruction for the function <code class="language-plaintext highlighter-rouge">proc_enter_usermode</code> popped the pushed <code class="language-plaintext highlighter-rouge">eip = 0</code> from my inline assembly, thereby calling my process code. Of course with a simple <code class="language-plaintext highlighter-rouge">ret</code> the execution was still in ring 0, but on subsequent switches, everything was as right as ever.</p>

<h2 id="ongoing-code-documentation">Ongoing code documentation</h2>

<p>Understanding and debugging that new context-switching method took me quite a while and led me to improve my interrupt code. It’s now pretty well documented, see for instance <a href="https://github.com/29jm/SnowflakeOS/blob/cd91aa6c16e68f14c5c784ccef5de4e9969f967e/kernel/src/cpu/asm/isr.S">isr.S</a> or <a href="https://github.com/29jm/SnowflakeOS/blob/cd91aa6c16e68f14c5c784ccef5de4e9969f967e/kernel/include/kernel/gdt.h">gdt.h</a>. I had several misunderstandings in that area of the code, notably differences between <code class="language-plaintext highlighter-rouge">ISRs</code> and <code class="language-plaintext highlighter-rouge">IRQs</code>, their relation with the <code class="language-plaintext highlighter-rouge">IDT</code> and the <code class="language-plaintext highlighter-rouge">GDT</code>… Now it’s all good.</p>

<p>Quick explanation. The <code class="language-plaintext highlighter-rouge">IDT</code> is a table that stores pointers to interrupt handlers along with details like which code segment to use when switching execution to the handler, etc… <code class="language-plaintext highlighter-rouge">ISRs</code> are one type of interrupts, numbered from 0 to 32 and also called “exceptions”, and <code class="language-plaintext highlighter-rouge">IRQs</code> are another, numbered from 32 to 47, also called “hardware exceptions”. The <code class="language-plaintext highlighter-rouge">GDT</code> describes memory segments referred to in the <code class="language-plaintext highlighter-rouge">IDT</code>.<br />
It’s interesting to note that the <code class="language-plaintext highlighter-rouge">IDT</code> and <code class="language-plaintext highlighter-rouge">GDT</code> have very similar structures, and that both are particularly horrid. For instance, the address of the start of a memory segment is split into three non-contiguous parts in a <code class="language-plaintext highlighter-rouge">GDT</code> entry: two of 8 bits and one of 16. Crazy stuff.</p>

<h2 id="rebuilding-the-build-system">Rebuilding the build system</h2>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">SnowflakeOS</span> <span class="err">$</span> <span class="n">time</span> <span class="n">make</span> <span class="n">SnowflakeOS</span><span class="p">.</span><span class="n">iso</span>
<span class="p">[...]</span>
<span class="n">real</span> <span class="mi">0</span><span class="n">m1</span><span class="p">,</span><span class="mi">140</span><span class="n">s</span>
<span class="n">user</span> <span class="mi">0</span><span class="n">m0</span><span class="p">,</span><span class="mi">733</span><span class="n">s</span>
<span class="n">sys</span>  <span class="mi">0</span><span class="n">m0</span><span class="p">,</span><span class="mi">347</span><span class="n">s</span>
</code></pre></div></div>

<p>With such progess in my userland, I now had to have a straightforward way of building programs. At first I hacked my libc’s <code class="language-plaintext highlighter-rouge">Makefile</code> to build <code class="language-plaintext highlighter-rouge">C</code> programs and link them with <code class="language-plaintext highlighter-rouge">libc</code>. I don’t know exactly what was wrong, but I couldn’t get GCC to compile them to flat binaries. I then looked into making an ELF loader, but it looked difficult to get right. Then I decided it was time to simplify my build system and do things correctly.</p>

<p>I replaced my interdependent shell scripts with a simple <a href="https://github.com/29jm/SnowflakeOS/blob/cd91aa6c16e68f14c5c784ccef5de4e9969f967e/Makefile"><code class="language-plaintext highlighter-rouge">Makefile</code></a> combining all of their functionalities. It would now be pretty easy to automate the whole cross-compiler toolchain building phase in there too.<br />
The build process is still fundamentaly the exact same: first headers are copied to a fakeroot environment, then code is compiled per-project (a project being the kernel, the libc and modules, for now) using the system headers from the fakeroot directory.<br />
There’s a slight problem as this <code class="language-plaintext highlighter-rouge">Makefile</code> relies on the order of compilation of projects which isn’t specified very strictly, so running <code class="language-plaintext highlighter-rouge">make -j</code> (parallel compilation) will cause errors.</p>

<p>After that, I managed to get module compilation in working order.</p>

<h2 id="userland-programs-as-grub-modules">Userland programs as GRUB modules</h2>

<p><img src="/assets/executing-c.png" alt="A C program printing &quot;Hello, C world&quot; on the screen" /></p>

<p>Notice the “Hello, C world” line on here? That’s <a href="https://github.com/29jm/SnowflakeOS/blob/cd91aa6c16e68f14c5c784ccef5de4e9969f967e/modules/src/test.c">a usermode process</a> calling my libc’s <code class="language-plaintext highlighter-rouge">printf</code> implementation, which itself uses my <code class="language-plaintext highlighter-rouge">putchar</code> system call to print characters:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">putchar</span><span class="p">(</span><span class="kt">int</span> <span class="n">c</span><span class="p">)</span> <span class="p">{</span>
<span class="cp">#ifdef _KERNEL_
</span>    <span class="n">term_putchar</span><span class="p">(</span><span class="n">c</span><span class="p">);</span>
<span class="cp">#else
</span>    <span class="n">asm</span> <span class="p">(</span>
        <span class="s">"mov $3, %%eax</span><span class="se">\n</span><span class="s">"</span>
        <span class="s">"mov %[c], %%ebx</span><span class="se">\n</span><span class="s">"</span>
        <span class="s">"int $0x30</span><span class="se">\n</span><span class="s">"</span>
        <span class="o">:</span>
        <span class="o">:</span> <span class="p">[</span><span class="n">c</span><span class="p">]</span> <span class="s">"r"</span> <span class="p">(</span><span class="n">c</span><span class="p">)</span>
        <span class="o">:</span> <span class="s">"%eax"</span>
    <span class="p">);</span>
<span class="cp">#endif
</span>    <span class="k">return</span> <span class="n">c</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>At the bottom right of the screenshot is the currently executing process. Also, notice how I sneakily increased the version number when writing this article :)</p>

<p>There’s one peculiar thing at play here worth noting: I thought it good to follow the advice from <a href="https://littleosbook.github.io/#using-c-for-user-mode-programs">here</a>, to have an assembly prologue to call <code class="language-plaintext highlighter-rouge">main</code> in my <code class="language-plaintext highlighter-rouge">C</code> programs and to call <code class="language-plaintext highlighter-rouge">exit</code> with <code class="language-plaintext highlighter-rouge">main</code>’s return value. And to push <code class="language-plaintext highlighter-rouge">main</code>’s arguments too, in the future. But I couldn’t get GCC to put my prologue code at the entry point: it always, <em>always</em> places a call to <code class="language-plaintext highlighter-rouge">main</code> as the first instruction, then a few <code class="language-plaintext highlighter-rouge">nop</code>s, and only then my prologue, which of course is more of an epilogue at this point.<br />
It’s enough to call <code class="language-plaintext highlighter-rouge">exit</code>, and I guess if I really want my <code class="language-plaintext highlighter-rouge">argc</code> and <code class="language-plaintext highlighter-rouge">argv</code> I’ll set up the process stack myself, I’ve done that before ;)</p>]]></content><author><name>Johan Manuel</name></author><category term="development" /><category term="osdev" /><category term="hobby-os" /><category term="c" /><summary type="html"><![CDATA[In the last post, I discussed how I implemented collaborative execution in SnowflakeOS through the iret instruction. Well, at that time the implementation wasn’t finished, even though I thought it was: I wasn’t restoring general purpose registers. This led to some pretty nice bugs, as pictured above.]]></summary></entry></feed>