Skip to main content

godspeed/driver/
registers.rs

1// SPDX-License-Identifier: Apache-2.0
2//! A driver's REGISTERS: the window onto its device that the kernel granted at spawn.
3//!
4//! # Why this exists
5//!
6//! A driver must name its register window and rely on it, so the window is part of how a Godspeed
7//! driver is written, and comes through `gs` (`docs/driver-library.md`, step 3). Every driver reached past
8//! the standard library for it, with the SDK's `ctx.mmio()`; this is the same grant, in the shape the promise
9//! covers. AHCI and GENET were the first to take it here, and by the end of that day (2026-10-19) every
10//! compiled driver did; `scripts/one_way_check.py` holds a new one to it.
11//!
12//! # What it is, and is not
13//!
14//! The registers of the device the spawn request named, and nothing else: the kernel resolved the device
15//! by its class and mapped exactly its window (CLAUDE.md 12.3, 14.1). [`registers`] answers `None` when no
16//! window was granted - a service that is not a driver, or a device that is not there - and a driver says
17//! so and parks rather than guessing.
18//!
19//! Every access is checked: an offset past the window, or one not aligned to the access's size, is
20//! refused loudly and ends the driver alone (the SDK's audited layer does the access, CLAUDE.md 18.1). A
21//! register's MEANING - its offset, its bits, the order a device wants them written in - is the
22//! device's, and stays in its driver.
23//!
24//! ```ignore
25//! let Some(regs) = gs::driver::registers(&ctx) else {
26//!     ctx.log("mydev: no device was granted - nothing to drive");
27//!     gs::ipc::park(&ctx)
28//! };
29//! let id = regs.read32(0x00);
30//! regs.write32(0x04, 1);
31//! ```
32
33#[cfg(not(test))]
34use godspeed_sdk::mmio::Mmio;
35#[cfg(not(test))]
36use godspeed_sdk::service_context::ServiceContext;
37
38/// The register window the kernel granted this driver. Copy it freely: it is a view of the one window,
39/// and every copy reaches the same registers.
40#[cfg(not(test))]
41#[derive(Clone, Copy)]
42pub struct Registers {
43    mmio: Mmio,
44}
45
46/// This driver's register window, or `None` when the spawn granted none.
47#[cfg(not(test))]
48pub fn registers(ctx: &ServiceContext) -> Option<Registers> {
49    ctx.mmio().map(|mmio| Registers { mmio })
50}
51
52#[cfg(not(test))]
53impl Registers {
54    /// The window's length in bytes: the last register is below it.
55    pub fn len(&self) -> usize {
56        self.mmio.len()
57    }
58
59    /// Read the 8-bit register at `off` bytes into the window.
60    pub fn read8(&self, off: usize) -> u8 {
61        self.mmio.read8(off)
62    }
63
64    /// Read the 16-bit register at `off`, which must be 2-byte aligned.
65    pub fn read16(&self, off: usize) -> u16 {
66        self.mmio.read16(off)
67    }
68
69    /// Read the 32-bit register at `off`, which must be 4-byte aligned.
70    pub fn read32(&self, off: usize) -> u32 {
71        self.mmio.read32(off)
72    }
73
74    /// Read the 64-bit register at `off`, which must be 8-byte aligned.
75    pub fn read64(&self, off: usize) -> u64 {
76        self.mmio.read64(off)
77    }
78
79    /// Write the 8-bit register at `off`.
80    pub fn write8(&self, off: usize, val: u8) {
81        self.mmio.write8(off, val)
82    }
83
84    /// Write the 16-bit register at `off`, which must be 2-byte aligned.
85    pub fn write16(&self, off: usize, val: u16) {
86        self.mmio.write16(off, val)
87    }
88
89    /// Write the 32-bit register at `off`, which must be 4-byte aligned.
90    pub fn write32(&self, off: usize, val: u32) {
91        self.mmio.write32(off, val)
92    }
93
94    /// Write the 64-bit register at `off`, which must be 8-byte aligned.
95    pub fn write64(&self, off: usize, val: u64) {
96        self.mmio.write64(off, val)
97    }
98}