1 Punkte von GN⁺ 2024-04-07 | 1 Kommentare | Auf WhatsApp teilen
  • UEFIRC ist ein grafischer IRC-Client, der in der UEFI-Pre-Boot-Umgebung der Mainboard-Firmware läuft, noch bevor das Betriebssystem startet, und zeigt, dass sich selbst in einer Bootloader-Umgebung eine UI und Netzwerkfunktionen umsetzen lassen, die fast an normale Apps heranreichen
  • Die Implementierung nutzt die von UEFI für Network Boot bereitgestellten NIC-Treiber und den TCP-Stack, während das vmnet-Netzwerk-Backend für QEMU die Entwicklung ermöglicht
  • Die schwierigste Stelle war der Umgang mit dem UEFI-TCP-Protokoll in Rust; globaler Zustand, reentrante Callbacks, Scatter-Gather-Buffer sowie Events, Tokens, Handles und Protokolle greifen dabei komplex ineinander
  • Die GUI ist eine nach UEFI übertragene Version von axles Rust-GUI-Toolkit und TrueType-Renderer; zugleich wurden zur Unterstützung von Mauseingaben, Scrollbars und Text-Rendering in Scroll Views auch Verbesserungen an libgui vorgenommen
  • Das Ergebnis ist weniger ein praktischer IRC-Client als vielmehr ein ausgefeiltes Spaßprojekt, dient aber als Werkzeug, um sich innerhalb von UEFI per IRC über den UEFI-TCP/IP-Stack zu beschweren

Was UEFIRC macht

  • UEFIRC ist ein grafischer IRC-Client, der unter UEFI läuft
  • Er ist in Rust geschrieben und nutzt ein GUI-Toolkit sowie einen TrueType-Renderer, die für den axle-Userspace entwickelt wurden
  • Man kann sich mit einem IRC-Server verbinden, chatten und Nachrichten lesen
  • Für die Entwicklung wurde das vmnet-Netzwerk-Backend für QEMU verwendet

UEFI als Laufzeitumgebung

  • Der Bootloader eines Betriebssystems wird mithilfe der im ROM des Mainboards gespeicherten Firmware geladen
  • Das frühere BIOS hatte viele Einschränkungen; als Ersatz wurde der UEFI-Standard geschaffen
    • BIOS verlangt, dass der Bootloader im 16-bit-Modus startet
    • Es gibt außerdem die Anforderung, dass der Loader der ersten Stufe in 512 Byte passen muss
  • UEFI platziert den Bootloader von Anfang an in einer 64-bit-Umgebung und bietet APIs für Dinge wie das Umschalten der VESA-Displayauflösung, Speicherallokation und den Zugriff auf das EFI-Dateisystem
  • Gegenüber BIOS ist das ein großer Fortschritt, wird aber zugleich oft als überentwickelt angesehen

Network-Boot-Funktionen für IRC wiederverwenden

  • Manche Bootloader können das Betriebssystem über das Netzwerk statt von einem lokalen Block-Device laden
  • Um diesen Anwendungsfall zu unterstützen, muss die UEFI-Firmware einen Netzwerk-Stack enthalten
    • NIC-Treiber
    • TCP-Implementierung
    • APIs, über die Anwendungen in der Pre-Boot-Umgebung auf diesen Stack zugreifen können
  • Ein Bootloader muss nicht zwingend nur ein Betriebssystem laden, daher kann in derselben Umgebung auch ein IRC-Client laufen

Die Schwierigkeiten mit UEFI-TCP in Rust

  • Der schwierigste Teil des Projekts war die Implementierung eines UEFI-TCP-Protokoll-Clients in Rust
  • Das UEFI-TCP-Protokoll verlangt Datenlebensdauern und Interaktionen, die sich in Rust nur schwer ausdrücken lassen
    • globaler Zustand
    • reentrante Callbacks
    • Scatter-Gather-Buffer
    • Events, Tokens, Handles und Protokolle
  • Um Memory Leaks und Use-after-free in TCP-Empfangspuffern zu beseitigen, wurde der Rust-Code tagelang getestet

Die Verwirrung um NOTIFY_SIGNAL und NOTIFY_WAIT

  • Beim UEFI-Event-API ist das Verhalten anhand der Namen schwer vorherzusagen
  • Wenn NOTIFY_SIGNAL gesetzt ist, wird beim Eintreten des Events ein Callback aufgerufen, und die Verwendung von wait() ist ein Fehler
  • Wenn NOTIFY_WAIT gesetzt ist und wait() aufgerufen wird, kann UEFI den Callback mehrfach ausführen, bevor das Event eintritt; sobald es eintritt, wird wait() freigegeben
  • Die beiden Modi geben selbst demselben Callback eine völlig andere Bedeutung
    • NOTIFY_SIGNAL: Das Event ist eingetreten, jetzt ist es Zeit für den nächsten Schritt
    • NOTIFY_WAIT: Das Event ist noch nicht eingetreten, jetzt sollte man Fortschritt anstoßen
  • Um empfangene Paketdaten asynchron zu puffern, wurde letztlich eine NOTIFY_WAIT-Schleife zusammen mit einem Timer für kurze Timeouts verwendet

Unterstützung für Maus und Cursor

  • Für einen IRC-Client ist eine Maus nicht zwingend nötig, sie lässt die App aber interaktiver wirken
  • Über das Simple Pointer Protocol von UEFI werden Mausbewegungen und Tasten eingelesen und als Cursor-Positionsfeedback in die GUI eingespeist
  • Das Simple Pointer Protocol unterstützt kein Scrollrad
    • In UEFIRC muss man daher Pfeiltasten verwenden oder die Scrollbar mit dem Cursor ziehen
  • In der standardmäßigen OVMF-UEFI-Firmware ließen sich keine Maus-Events erhalten, daher wurde eine angepasste UEFI-Firmware mit den nötigen Treibern und Protokollen wie UsbMouseDxe gebaut
  • Damit sich UEFIRC in QEMU testen lässt, wurde diese UEFI-Firmware auch in die Releases hochgeladen

Skalierung von Mausbewegungen

  • Der Maustreiber meldet keine absoluten Positionen, sondern Positionsänderungen
  • Eine lineare Skalierung, die einfach delta_x und delta_y addiert, fühlt sich träge an
  • Betriebssysteme verwenden eher eine Skalierung, die sowohl schnelle Bewegung als auch feine Anpassung ermöglicht
  • In der Beispielimplementierung wird die Cursorbewegung verstärkt, indem auf die Summe der Absolutwerte der Bewegung log2() angewandt und das Ergebnis als Faktor genutzt wird
  • Ein Cursor mit linearer Bewegung lässt die gesamte Umgebung leicht langsam und wenig reaktionsfreudig wirken

IRC-Nachrichten modellieren

  • Die Modellierung von IRC-Nachrichten war vergleichsweise einfach und angenehm
  • IRC verwendet ein textbasiertes Zeilenformat, das sich leicht parsen lässt
  • Allerdings bringt die über Jahrzehnte gewachsene Erweiterungsgeschichte auch Lasten mit sich, weil nur ein Teil davon standardisiert ist

libgui unter UEFI verwenden

  • axles Rust-GUI-Toolkit war bereits weitgehend darauf vorbereitet, auch außerhalb des axle-Kontexts eingesetzt zu werden, daher war die Ausführung unter UEFI nicht besonders schwierig
  • Die zentrale Aufgabe bestand darin, eine AwmWindow-Implementierung bereitzustellen, die sich innerhalb von UEFI verwenden lässt
  • Danach konnten viele Funktionen von libgui unverändert genutzt werden
    • Event-Management
    • Font-Rendering
    • Layer-Komposition
    • View-Dekoration
    • komplexe Komponenten wie Scroll Views

Scrollbars und Text-Rendering in Scroll Views

  • In axles C-basiertem libgui gab es bereits Scrollbar-Funktionalität, in der Rust-Version fehlten jedoch noch einige Teile
  • Da die Hauptinteraktion von UEFIRC in einer mit Text gefüllten Scroll View stattfindet, wurde die Scrollbar-Funktionalität in Rust-libgui erneut implementiert
  • Scroll Views verursachen höhere Pixel-Rendering-Kosten als Views mit fester Größe
    • Bei einer View mit fester Größe kann man einfach an einen RGB-Buffer der Größe width * height denken
    • Eine Scroll View muss dagegen mit einer unendlich erweiterbaren Leinwand umgehen
  • axles Rust-GUI-Toolkit verarbeitet Scroll Views tile-basiert
    • Jedes Tile ist ein quadratischer Pixel-Buffer mit einer Kantenlänge von einigen hundert Pixeln
    • Es werden nur die Tiles allokiert, die für die Bereiche benötigt werden, in denen tatsächlich Inhalte gerendert werden
    • Die sichtbaren Tiles werden berechnet und zum finalen Bild zusammengesetzt
  • Wenn der TrueType-Renderer für jedes Pixel eines Glyphen putpixel() aufruft, kennt die Scroll View den gesamten Renderbereich nicht im Voraus, was ineffizient ist
  • Um das zu lösen, wurde der Polygon-Stack um grundlegende Zeichenprimitive wie Linien, Kreise und Rechtecke erweitert
    • Die Scroll View kann dadurch im Voraus erkennen, dass ein großes Polygon gezeichnet wird, und die benötigten Tiles rechtzeitig allokieren
    • Dass das Füllen beliebiger Polygone als grundlegendes Primitive dient, gefällt dem Autor zwar nicht, funktioniert in der Praxis aber gut

Durch UEFIRC verbesserte libgui

Ein völlig unnötiges Ergebnis

  • Der IRC-Client selbst ist als ausgefeiltes Spaßprojekt für den praktischen Einsatz nur begrenzt nützlich
  • Wenn man sich über den UEFI-TCP/IP-Stack ärgert, kann man ihn als Werkzeug nutzen, um diesen Frust auszusprechen
  • Zum Schluss wurde aus UEFI heraus noch der UEFI-#edk2-Entwickler-IRC-Channel betreten und ein Gruß hinterlassen

1 Kommentare

 
GN⁺ 2024-04-07
Hacker-News-Kommentare
  • Aus Spaß wurde ein grafischer IRC-Client gebaut, der nur in der UEFI-Pre-Boot-Umgebung läuft, inklusive übertriebener Features wie TrueType-Schriftarten, Cursor und GUI-Dekorationen
    Ursprünglich war es als schnelles und leichtgewichtiges Projekt gedacht, weil die Arbeit an einem von Grund auf selbstgebauten GPS-Empfänger ermüdend geworden war, aber wie so oft dauerte es viel länger als erwartet
    In den Beitrag floss auch ziemlich viel Zeit in Visualisierungen, die zeigen, wie die Scroll-Ansicht modelliert und in einen statischen Viewport gerendert wird, und es wäre schön, wenn sich jemand das gern anschaut
    Zuerst kam die Idee auf, einen Twitter-Client zu bauen, ganz nach dem Motto „Lass uns etwas in UEFI hineinzwängen, das dort nichts zu suchen hat“, aber da bereits jemand mit dem HTTP-Protokoll von UEFI etwas Gutes gebaut hatte, sollte HTTP vermieden werden
    Daher fiel die Wahl auf IRC, das über TCP läuft und zugleich diesen Social-Media-Charakter hat, der überhaupt nicht zur Pre-Boot-Umgebung passt

    • Auch wenn es sich „wie etwas anfühlt, das nicht einmal in die Nähe einer Pre-Boot-Umgebung gehört“, wirkt es gleichzeitig wie genau der richtige Ort, wenn man Hilfe bei Boot-Problemen erbitten will
    • Ich würde gern das unnötig riesige Betriebssystem und all den sonstigen Ballast loswerden und stattdessen zu kleinerem, einfacherem UEFI wechseln. Es würde schneller starten und „Embedded“-Entwicklung leichter machen
      Natürlich ist das ein Witz. Zumindest teilweise
      Ich bin Minimalist, brauche also weder GUI noch Maus, und selbst UEFI scheint schon mehr zu sein, als ich brauche
      Der erwähnte Twitter-Client ist hier: https://github.com/arata-nvm/mitnal
    • Wenn Software zu groß ist, um sie in UEFI hineinzupressen, dann sollte man sie wohl von vornherein als unnötig aufgeblähte Software betrachten. Früher reichten zwei 360-KB-Disketten
    • Wirklich cool. Ich habe mich schon länger gefragt, ob man VPN-Zugangsdaten in UEFI speichern und das System dann einen Server kontaktieren lassen könnte, um per PXE über das Netzwerk zu booten
      Das scheint eine ziemlich gute Möglichkeit zu sein, um ein entferntes System, dessen Installation komplett kaputt ist und das nicht mehr normal bootet, automatisch wiederherzustellen, vielleicht sogar auf sichere Weise
    • Mich interessiert mehr die Geschichte mit dem von Grund auf selbstgebauten GPS-Empfänger
  • Wirklich großartig. Es zeigt auch gut, dass unter dem, was die meisten als das System betrachten, oft wesentlich komplexere und mächtigere Software liegt, als man denkt
    Oft wird fälschlich angenommen, das Betriebssystem sei die „unterste Schicht“ des Software-Stacks, doch in Wirklichkeit gibt es firmwareartige Software, die das System tatsächlich kontrolliert
    Manchmal erledigt sie ihre Aufgabe und verschwindet, manchmal bleibt sie während der gesamten Laufzeit des Systems vorhanden, in einem Zustand, den selbst das Betriebssystem womöglich gar nicht bemerkt
    Es gibt die Haltung „Das ist doch nur Low-Level-Code zur Geräteansteuerung, dort passiert nichts wirklich Ernstes“, aber wenn sich dort sogar ein IRC-Client unterbringen lässt, dann kann man sich auch allerlei andere bösartige Dinge gut vorstellen

  • „Warum?“ Was soll das überhaupt für eine Frage sein? Wegen genau dieses Geists komme ich zu HN
    „Die schrecklichste Erkenntnis kam über mich. Es gab keinen Grund für das, was ich getan hatte. Ich wusste, warum ich es getan hatte. Ich hatte es getan, weil es Spaß gemacht zu haben schien. Aber ich dachte, sie würden mich fragen: ‚Warum um alles in der Welt hast du so etwas getan?‘, und wenn ich keinen plausiblen Grund hätte, würden sie mich in die Psychiatrie stecken.“ — Boyd Rice

  • Du musst dich selbst nicht kleinmachen. Hier gibt es ein Projekt für einen Botnet-Command-and-Control-Client
    Die UI ist allerdings etwas komisch

  • Wirklich cool. Ich wusste nicht, dass die UEFI-API so zugänglich und gut dokumentiert ist
    Mich würde interessieren, wie der Entwicklungszyklus aussah. Vermutlich lief das in einer VM, aber musste dafür jedes Mal wirklich „gebootet“ werden, wenn der Client gestartet werden sollte?

    • Der übliche Arbeitsablauf bestand darin, eine QEMU-Instanz mit der UEFI-Anwendung zu booten
      Das Hauptskript für die Ausführung erzeugte ein EFI-Dateisystem mit einem neuen Build von UEFIRC und übergab dieses dann an QEMU
      Beim Bau der GUI wurde dieser Overhead jedoch ziemlich lästig, daher wurde die App so eingerichtet, dass sie sowohl für echtes UEFI als auch für eine Host-Umgebung auf dem Mac gebaut werden konnte
      Je nach Build-Flag zeichnet das GUI-Toolkit entweder direkt in den von UEFI bereitgestellten Framebuffer oder bindet sich an das Window-System des Mac an, um Events zu senden und zu empfangen
      Der Overhead dieses Dual-Target-Ansatzes ist sogar im Entry Point zu sehen: https://github.com/codyd51/uefirc/blob/main/src/main.rs
      Das Parsen von IRC-Nachrichten brauchte keine besondere Verzierung und wurde daher als Sammlung von Unit-Tests entwickelt, die direkt auf dem Mac laufen; ein Teil davon ist hier zu sehen: https://github.com/codyd51/uefirc/blob/main/src/irc/response...
    • QEMU kann UEFI-Apps ausführen
  • Eines Tages möchte ich das Schreiben eines Betriebssystems für meinen IRC-Bot abschließen, der immer noch läuft
    Das ist vielleicht die nutzloseste Aussage überhaupt, aber nichtlineare Mausbewegung, also Beschleunigung, ist die erste Einstellung, die ich beim Booten eines neuen Betriebssystems deaktiviere. Irgendwie tun mir davon tatsächlich die Hände weh
    Auf dem Mac gibt es zum Beispiel das kostenlose linearmouse, unter Windows kann man die Beschleunigung einfach abschalten. Unter Linux ist es natürlich leicht
    Mit Mausbeschleunigung ist es schwieriger, ein Gefühl für die Zuordnung zwischen der tatsächlich bewegten Mausstrecke und der Bewegung auf dem Bildschirm zu entwickeln, und langfristig halte ich die Nutzung ohne Beschleunigung für effizienter
    Das habe ich von Gamern gelernt, und ich denke, dass Gamer immer noch gute Gründe dafür haben

    • Ich ändere meine Mauseinstellungen nicht und weiß daher nicht, was der Standard ist, aber ich kann trotzdem verdeckte Bildschirmbereiche präzise anklicken
      So oder so gewöhnt man sich vermutlich daran, ähnlich wie das Gaspedal im Auto normalerweise auch nicht direkt auf die Geschwindigkeit abgebildet ist
  • Wenn man fragt „Warum?“, dann deshalb, weil bei der Einführung von UEFI genau solche Low-Level-Anwendungen versprochen wurden
    Die Leute hinter UEFI träumten sogar davon, jene Linux-basierten Mini-Betriebssysteme nur fürs Internet zu ersetzen, auf die man bei manchen Herstellern während des Bootens per Tastendruck zugreifen konnte. An den Namen erinnere ich mich nicht mehr

    • Das waren die Quick View / Quick Boot-Funktionen, wie man sie früher etwa bei Dell sah. Meist booteten sie direkt in ein paar Produktivitäts-Apps
      Ich habe auf YouTube ein Video gesehen, das das ausführlich behandelt hat; soweit ich mich erinnere, war es anfangs ein abgespecktes Linux oder ein anderes angepasstes Betriebssystem, wurde später zu UEFI-Apps migriert und verlor dann irgendwann einfach an Relevanz
  • Guter Beitrag. Er erinnert an den Aprilscherz des barebox-Bootloaders vor zwei Jahren: Wenn alle anderen Boot-Ziele fehlschlagen, verbindet er einen mit #barebox[1]
    Dort lag der Fokus darauf, barebox um TCP-Unterstützung zu erweitern, und es gab keine schicken GUI-Elemente wie hier
    Die Oberfläche war nur eine Kommandozeile, und wenn barebox als EFI-Payload gebaut wurde, konnte es auf EFI GOP zeichnen
    [1]: https://lore.barebox.org/barebox/20220401145902.GF4351@telli...

  • Ich musste sofort an ein aktuelles Video von Cathode Ray Dude denken. Darin ging es um den „E-Mail-Client“ von HP, in Wirklichkeit ein Outlook-Plugin namens QuickLook, das ebenfalls auf diese Weise umgesetzt und als Produkt veröffentlicht wurde: https://www.youtube.com/watch?v=ssob-7sGVWs
    Im Video kommen auch noch seltsamere Dinge vor, die HP gemacht hat. Dieses Projekt hier schafft allerdings sogar Networking, also den schwierigen Teil, den QuickLook vermieden hat

  • Die Visualisierungen im Beitrag sind erstaunlich gelungen und beeindruckend