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}