What you can make
GodspeedOS runs four kinds of program, and godspeed new makes every one, plus
libraries for them to share. Each kind is a different answer to one question: whose life does
the program live? Pick the kind, and the template is the smallest program of it that works.
Service
The system's. Started at boot, restarted if it dies.
Utility
Its own. Runs when typed, ends with a status, pipes.
App
The operator's. Has the screen and keyboard while it runs.
Driver
Its device's. Given one device, by what the bus says it is.
Library
Nobody's. Built into what uses it, tested on your workstation.
What every kind shares
-
Make it, and check it
$ godspeed new <kind> <name> --publisher <you> $ cd <name> $ godspeed check # the project, its toolchain, and its contract against the OS's schema $ godspeed build --all # one ELF for each of x86_64, aarch64, armv7, riscv64
Every template is written on the public standard library,
gs, and nothing below it. CI makes, checks and builds every kind for all four architectures against the GodspeedOS release it names, so a template that does not build is a bug in this CLI. -
Package and publish it
$ godspeed package # every file, with its size and SHA-256, before anything leaves $ godspeed publish # signed, uploaded, installable as you/<name>
No network on the machine?
godspeed package --localwrites one.gspfile, signed with your key, that a machine installs once its operator trusts that key. Publishing has the one-time setup. -
Install it, on a Godspeed machine
gsh> godspeed fetch <you>/<name> gsh> godspeed install <you>/<name>
fetchdownloads it and checks every signature;installstarts only a package already fetched. Installing asks the operator for what the contract requests, by name, and grants nothing else. The contract asks; the machine decides.
Service works
A program the system keeps running: the supervisor starts it at every boot and restarts it if it dies. The shape of every server, logger and background job.
$ godspeed new service clock
#[no_mangle]
pub extern "C" fn service_main(ctx: ServiceContext) -> ! {
ctx.log("clock: started");
gs::ipc::park(&ctx)
}
- Given: what its contract requests and the operator approves -
log_writein the template, and send peers by name. - Upgraded live: put
handover = trueunder its contract's[capabilities], andgodspeed upgradehands the old version's state to the new one with no restart. - Needs another package? Name it in
godspeed.toml, and the machine fetches, installs and starts it first. That needs a machine on GodspeedOS v0.30.0 or later:[requires] "alice/notes-store" = "^1.2.0"
A requirement grants nothing: the contract still names every peer it talks to.
Utility works
A program that does one job and ends, like a built-in command: typed at gsh> by its name,
given its arguments, its output shown or piped, its status said.
$ godspeed new utility greet
let mut buf = [0u8; gs::program::ARGS_MAX];
let args = gs::program::args(&ctx, &mut buf);
let mut out = gs::program::Output::new(&ctx);
let _ = writeln!(out, "hello, {}", if args.is_empty() { "world" } else { args });
out.finish(0)
gsh> greet Bankole hello, Bankole gsh> greet Bankole | count 1 line, 2 words, 15 bytes
- Given: its arguments and an output stream, through
gs::program. It ends withfinish(status)orgs::program::exit, and is never restarted for it.
App works
A program the operator uses: it has the screen and the keyboard while it runs, and gives them back when it ends - even if it crashes.
$ godspeed new app notes
let app = gs::app::App::open(&ctx);
app.println("This screen is notes' until a key is pressed.");
let _key = app.wait_key();
app.close(0)
- Given: the console, through
gs::app- asked of the operator by name atgodspeed install, because a program that holds the keyboard reads what is typed. - An app cannot start a pipe: it has the screen, not a stream. Windows, and graphics beyond the console's grid, are not offered.
Driver works
A program that drives one device. Its contract names the device by what the bus says it is - a PCI class, or a USB vendor and product - never an address, and the machine finds which device that is.
$ godspeed new driver sensor
[capabilities] hw_pci_class = "00ff00" # QEMU's teaching device, `edu` hw_pci_bar = 0 # its registers are in BAR 0 hw_pci_irq = true # its interrupt: the OS picks the vector hw_dma = "8KiB" # a DMA arena, memory the device reads and writes itself
// Shortened: the template says which grant is missing where this writes `?`.
let regs = gs::driver::registers(&ctx)?; // the register window
let irq = Irq::granted(&ctx); // the interrupt
let mut layout = gs::driver::arena(&ctx)?.layout();
let out = layout.take("out", 64, 64)?; // a named part of the DMA arena
regs.write32(STATUS, RAISE_ON_FACTORIAL);
regs.write32(FACTORIAL, 5);
match irq.wait(&ctx, Budget::ms(1000)) { // bounded by time, never by a count
Woke::Interrupt => { /* clear it, then */ irq.rearm(&ctx) }
_ => ctx.log("sensor: the device did not answer"),
}
- Given, all through
gs::driver, the library every driver in GodspeedOS is written on: its device's register window (registers), its interrupt when the contract asks (irq), and a DMA arena when the contract asks and the operator agrees (arena, at most 2 MiB). Where no IOMMU confines the device - every Raspberry Pi and the VisionFive, an Intel PC, and any PC device the kernel leaves in passthrough - a driver with an arena can reach all of memory, so the install asks about it by a question of its own (memory, besidedevice), on every machine. gs::driver, and the SDK.gs::driveris the way to write one and the part the compatibility promise covers; a driver may also call the SDK (godspeed_sdk), which refuses nothing and grants nothing, but may change between releases. A gap ings::driveris closed there when a real driver needs it.- A device GodspeedOS already drives is taken over: installing the package stops the OS's driver for that device, and uninstalling it starts that driver again.
- A USB device has no registers of its own, so its driver names it by
hw_usb = "0403:6001", with no interrupt or DMA of its own (they are its host's), and reaches it through its host withgs::driver::bus: who it is and where, control transfers, bulk OUT and bulk IN, and a replug request. It is started while the device is attached. Its host binds one vendor-specific device (interface class 0xFF) per host:xhcion the PCs, the Pi 4 and the VisionFive;dwc2on the Pi 2, high-speed only;ehcion the T630's back sockets, which sends no notice, so a driver there asks for a bulk IN again. - The template drives QEMU's
edu(-device edu) by all three grants: installed, it logsdevice id 0x010000ed, alive true, 5! = 120 by interrupt, 64 bytes through DMA and back intact. - The interrupt, the arena, a device taken over and a USB device need GodspeedOS v0.31.0 or later. On v0.30.0 a driver package is given its registers only.
Library works
Reusable no_std Rust for the other kinds. Never installed: it is built into what uses it, so
it requires no packages. Its tests run on your workstation.
$ godspeed new library parser $ godspeed test