TockOS Hail

From
Jump to navigation Jump to search

TockOS

Idee

Bei Tock handelt es sich um ein Betriebssystem für den Embedded-Bereich, welches u.a. Plattformen wie Cortex-M und RISCV unterstützt. Besonders dabei ist, dass dabei der Kernel als auch die Treiber in der Programmiersprache Rust geschrieben sind. Dabei werden die Speicherschutzmechanismen von Rust genutzt, um für Safety und Security innerhalb des Kernels aber auch der Anwendungen zu gewährleisten. Dabei sind 2 Techniken für den Speicherschutz besonders hervorzuheben:

1. Compile-Time Memory-Safety durch den Rust Compiler

2. Nutzung von Speicherschutzeinheiten um Anwendungen vom Kernel zu isolieren


Implementierungen

Insbesondere existieren bisher 2 Implementierung von Bibliotheken, welche eine Schnittstelle für die Anwendungsentwicklung in Tock zur Verfügung stellen. Zum einen die libtock-c, welche C-Bindings bereitstellt und die libtock-rs, welche direkt Rust-Bindings fr die Anwendungentwicklung bereitstellt.


Weitere Informationen zu Tock sind in der Dokumentation des Github Repositories von Tock zu finden.


Prozessmanagement

Findet in einer Anwendung ein fehlerhafter Speicherzugriff statt, der zur Compile-Zeit nicht erkannt wird, so crasht nicht nur die Anwendung selbst. Zusätzlich wird im Kernel von TockOS eine panic ausgelöst, welche den Kernel ebenfalls zum Absturz bringt, sodass danach ein Reset erforderlich ist. Der Kernel entscheidet letztendlich, wie er mit der Applikation verfährt.

Dabei wird nach der Art des Fehlers entschieden, wie der Kernel handelt:

    fn set_fault_state(&self) {
        // Use the per-process fault policy to determine what action the kernel
        // should take since the process faulted.
        let action = self.fault_policy.action(self);

        match action {
            FaultAction::Panic => {
                // process faulted. Panic and print status
                self.state.update(State::Faulted);
                panic!("Process {} had a fault", self.process_name);
            }
            FaultAction::Restart => {
                self.try_restart(None);
            }
            FaultAction::Stop => {
                // This looks a lot like restart, except we just leave the app
                // how it faulted and mark it as `Faulted`. By clearing
                // all of the app's todo work it will not be scheduled, and
                // clearing all of the grant regions will cause capsules to drop
                // this app as well.
                self.terminate(None);
                self.state.update(State::Faulted);
            }
        }
    }

Aktuell ist im Kernel festgelegt, dass falls Apps abstürzen, dann auch der Kernel durch ein von selbst ausgelöstes PANIC crasht. Damit soll es für die Entwickler einfacher ist, die Apps zu debuggen. Daher hier ein Ausschnitt aus dem Kernel, welcher genau diese Fehler erzeugt.


    // Configure application fault policy
    let fault_policy = static_init!(
        kernel::process::ThresholdRestartThenPanicFaultPolicy,
        kernel::process::ThresholdRestartThenPanicFaultPolicy::new(4)
    );

Somit wäre es also leicht möglich, dem Kernel andere Handlungsanweisungen zu geben, falls eine App crasht. Aktuell ist der Panic gesetzt, da es sich hierbei um ein Development Board handelt. Es wäre aber auch einach möglich, das falls eine App crasht, der Prozess, in dem die App läuft, zu terminieren und den Kernel einfach weiterlaufen zu lassen. Diese Einstellungen im Kernel sind spezifisch für jedes Board in einer Konfigurationsdatei editierbar.

Tockloader

Der Tockloader dient dazu, kompilierte Binärdateien auf den Flash-Speicher eines Chips zu laden. Außerdem kann darüber weiterhn mit TockOS kommuniziert werden, welches ebenfalls auf einem solchen Chip laufen muss.

Initialisierung (Linux)

Vor der Installation des Tockloaders müssen zuerst einige wichtige Tools installiert werden:

Rust: $ curl https://sh.rustup.rs -sSf | sh

Compiler für Cortex-M-Boards: $ sudo apt install gcc-arm-none-eabi

Nun kann auch der Tockloader installiert werden: $ pip3 install -U --user tockloader


Compile Kernel

Zusätzlich kann man noch den Tock-Kernel installieren und kompilieren, um ggf. den Kernel auf dem Board zu updaten:

$ git clone https://github.com/tock/tock

Anschließend in das Verzeichnis wechseln und kompilieren:

$ make

Letztendlich kann der neu kompilierte Kernel auch auf das Board geflasht werden.

$tockloader flash


Bedienung

Hier sind ein paar nützliche Befehle aufgelistet, um Apps mithilfe des Tockloaders mit libtock-c zu installieren/deinstallieren und den Status des Systems abzufragen:

  • Apps installieren: $ tockloader install
  • Apps deinstallieren (löscht alle installierten Apps): $ tockloader erase-apps
  • Von serial output lesen: $ tockloader listen
  • Alle installierten Apps anzeigen: $ tockloader list
  • Alle Befehle anzeigen lassen: $ tockloader help

libtock-c

Die Blbiothek libtock-c ermöglicht es, Anwendungen in C für TockOS zu schreiben, indem dafür die notwendigen C-Bindings zur Verfügung gestellt werden. Dabei werden bereits einige Beispiele für Anwendungen bereitgestellt, welche direkt kompiliert und direkt über den Tockloader installiert werden können. Die libtock-c baisert auf einer eigenen Implementierung der C-Standardbibliothek für Embedded-Systeme, welche auf den Namen 'newlib' getauft wurde und mit der libtock-c mitgeliefert wird. Von daher ist es inherent nicht möglich, den POSIX-Standard einzuhalten während der Entwicklung von Anwendungen mit dieser Bibliothek. Zusätzlich gibt es noch Erweiterungen für Lua und C++, welche ebenfalls direkt mitgeliefert werden.


Beispiel-Apps installieren

Zunächst benötigt man die libtock-c:

$ git clone https://github.com/tock/libtock-c.git

Anschließend in das Verzeichnis wechseln:

$ cd libtock-c/examples

Nun kann eine Beispiel-App ausgewählt werden (z.B. Blink):

$ cd blink

Die Binärdatei des Beispiels kann nun mit 'make' gebaut werden:

$ make

Nun muss diese nur noch mithilfe des Tockloaders installiert werden:

$ tockloader install

Anschließend sollte die App ausgeführt werden (im Falle von Blink sollte die LED aufleuchten).

libtock-rs

Ähnlich zur libtock-c stellt libtock-rs das Rust-Userland von Tock als Rust-Library dar. Bisher sind die Rust-Analoge der alarm-, buttons- und console-APIs verfügbar, weiterhin gibt es Unterstützung für die Konsole des Tock-Kernels und ein Debug-Interface. Um die libtock-rs zu verwenden, muss man einige Eigenheiten der Build-Umgebung beachten: Es sind einige spezielle Flags an rustc vonnöten, welche die Kompilierung für die entsprechenden ARM-Targets ermöglichen und das Linking kontrollieren. Zu finden sind diese Optionen in libtock-rs/.cargo/config, wo auch der mit der Library mitgelieferte "Runner" konfiguriert wird. Dieser ist dafür verantwortlich, die als ELF im cargo-Buildprozess kompilierte Binary in ein Tock Application Bundle (.tab) umzuwandeln, welches dann ebenso mit dem Runner auf das Board geflasht werden kann. Ein typisches Projekt könnte so aussehen:

 app
 |
 +- .cargo
 |  |
 |  +- config (mit Linker- und Runner-Konfiguration)
 |
 +- runner (aus libtock-rs übernommen)
 |
 +- src (Quellcode der App)
 |
 +- Cargo.toml:
 |   [package]
 |   ...
 |   [dependencies]
 |   libtock = { path = "<relativer Pfad zu libtock-rs>" }
 |   libtock_buttons = { path = "<relativer Pfad zu libtock-rs>/apis/buttons" }                      # Pro benutzter API von libtock
 |   ...
 |   [...]
 |   ...
 |   [workspace]
 |   members = [ "runner" ]                                                                          # Um den Runner sichtbar zu machen
 |
 +- build.sh:
     export LIBTOCK_PLATFORM=hail                                                                    # Plattform setzen
     cargo build --release --target thumbv7em-none-eabi                                              # Projekt kompilieren
     cargo run -p runner --relase target/thumbv7em-none-eabi/release/<App-ELF> --deploy=tockloader   # In .tab konvertieren & flashen

Mit dieser Projektstruktur kann man einfache Rust-Apps für Tock programmieren. Es ist jedoch zu beachten, dass das Rust-Userland von libtock-rs nur mit Tock 2.0 funktioniert, wozu wir einen entsprechend neuen Tock-Kernel kompilieren und flashen mussten. Dies hat die Kompatibilität mit Teilen des libtock-c-Userlands zerstört, viele Apps waren aber weiterhin funktionsfähig.

Für das im Board verfügbare Bluetooth-Radio gab es in libtock-rs bislang keine API, daher haben wir im Zuge des Projektes begonnen, eine solche zu implementieren, wobei wir uns recht eng an die C-Implementierung in libtock-c gehalten haben.

Hail Board

Komponenten des IoT-Boards

Das Tock Hail IoT-Development Board besitzt folgende Komponenten mit folgenden Funktionen:

Komponenten des Hail-Boards
Name Spezifikation Beschreibung Technische Details
SAM4L Cortex-M4 48 - 120 Mhz, 32 - 160 kB RAM, 128-2048 kB Flash Memory Microcontroller für sehr energiesparende Embedded-Systeme, ARM CPU Dokumentation SAM4L-Package

Dokumentation Cortex-M4

nRF51822 BLE Radio 2.4 Ghz, 16/32 kB RAM, 128/256 kB Das BLE (Bluetooth Low Energy) Radio ist in der Lage, zu anderen Geräten Bluetooth-Verbindungen aufzubauen. Dokumentation
SI7021 Temperature and Humidity Sensor - Messung von Temperatur und Luftfeuchtigkeit -
ISL29035 Light Sensor 16-Bit ADC, 11s - 105ms RT Messung von Helligkeit in Lumen Dokumentation
FXOS8700CQ 6-axis Accelerometer and Magnetometer 6-axis e-compass (T range), acceleration, ODR 1.5 Hz - 800 Hz Sensor zur Bestimmung von Drehungen, Geschwindigkeit etc. Andwendung für digitale Kompasse, Bewegungserkennung oder Erkennung der Orientierung des Geräts. Dokumentation
RGB LED 3 LEDs (R,G,B) Ansteuerbare LED mit 3 Kanälen (rot,grün,blau) -
User push-button - Programmierbarer Knopf (sehr klein) -


Photon Pinout

TockHail Pinout.png

PINS
Label Beschreibung
A0-A5 Analog-to-Digital Pin,
DAC Digital-to-Analog Output, kann als digitaler Pin oder DAC genutzt werden
WKP Wakeup-Pin, um Modul aus Sleep-Modus zu reaktivieren
RX Dient als UART (Universal Asynchronous Receiver / Transmitter) RX, kann aber auch als digitaler GPIO genutzt werden
TX Dient als UART (Universal Asynchronous Receiver / Transmitter) TX, kann aber auch als digitaler GPIO genutzt werden
GND Ground-Pin, für Spannungmessung
VIN Input/output Pin mit (3.6-)
D0-D7 Digitale GPIO Input Pins, alternativ auch digitale GPIO Pins
3V3 3V Spannung anliegend, Stromversorgung
RST Reset-Pin
VBAT Batterie zum sichern von Registern und SRAM und RTC, wenn Strom von 3V3 nicht verfügbar


Ansteuerbare Pins


Über GPIO (General Purpose Input/Output) lassen sich die einzelnen Pins im Programm ansteuern. Dabei scheint es mehrere Modis zu geben, in denen die Konfiguration der Pins laufen kann:

  • JTAG (Joint Test Action Group): Debug- und Testmodus für Hardware und integrierte Schaltungen. Damit können einzelne Komponenten auf Funktionalität getestet werden wobei spezielle Teile von ICs erst dann aktiviert werden.
  • SPI1 (Serial Peripheral Interface): Hierbei handelt es sich um ein Bus-System, mit dem digitale Schaltungen nach dem Master-Slave-System miteinander verbunden werden können. Dabei können viele Teilnehmer gleichzeitig über diesen Bus verbunden werden und der Master legt fest, mit welchem Slave er dabei kommunizieren will.
  • TIM3: General Purpose Timer

Dabei ist es uns gelungen, in der aktuellen Konfugration die Pins D0,D1,D6 und D7 anzusteuern. Die Pins D2 und D5 waren immer aktiv dabei.

Die Pins lassen sich folgendermaßen über das GPIO-Interface ansteuern dabei:

#include <stdio.h>
#include 'ble.h'
#include 'led.h'
#include 'timer.h'

// Set to test a certain pin
#define PIN_NUM 0 

int main (void) {

  // LED will light up if a certain pin is activated
   gpio_set(PIN_NUM);
   gpio_enable_output(PIN_NUM);
   led_on(DRIVER_NUM_LEDS);
   delay_ms(5000);
   gpio_disable(PIN_NUM);
   gpio_clear(PIN_NUM);
   led_off(DRIVER_NUM_LEDS);

  return 0;
}

Dabei haben sich folgende Pins mit einem bestimmten Index ansteuern lassen:

Indexe ansteuerbarer Pins
Index ID
0 D0
1 D1
2 D6
3 D7

BLE API für Rust

Aktuell besitzt das Rust-Userland in der libtock-rs keine BLE-Bindings, um damit in Anwendungen vernünftig Bluetooth-Verbindungen aufzubauen und darüber zu kommunizieren. Daher haben wir uns es als ein Teil unseres Projekts zur Aufgabe gemacht, genau solche Bindings für das Userland in Rust zu schreiben. Dazu ist es notwendig, die vom Treiber bereitgestellten Funktionen und Systemrufe in die neue API für das Userland einzubinden. Alle Treiber von TockOS sind als sog. "Capsules" geschrieben, welche dann direkt mit dem Kernel kommunizieren:


Die finale API beinhaltet Funktionen zum Starten und Stoppen des BLE-Advertisings.

Dennoch werden für eine korrekte Funktionalität noch viele weitere Bibliotheken benötigt, um mit dem BLE-Interface sinnvoll arbeiten zu können. Diese sind bisher nur in der libtock-c vorhanden (im Verzeichnis 'simple_ble'). Der Aufwand für diese zusätzlichen Bibliotheken Rust-Bindings zu entwickeln, wäre um ein vielfaches größer. Zwar existieren Rust Crates für BLE, welche eine API zur Verfügng stellen, jedoch leider nicht für dieses Modell des BLE-Radios, sodass es schwierig ist, jenes im Rust-Userland zum Laufen zu bekommen.

TO-DO

1. GPIO Communication via external devices

2. BLE userland bindings

3. Kernle panic debuggen

References

TockOS Introduction

TockOS Kernel

Libtock-c

Libtock-rs

Pin Layout

Tock Hail IoT Development Board

BLE with Tock

Tock Bootloader