Input/Output Subsystem
Subsystem: dev.io
(hereafter io
)
To simplify and standardize input/output operations for devices, a specialized subsystem was implemented.
It offers a platform-independent API for working with MMIO space and I/O ports, as well as the ability to implement your own I/O mechanism.
Additionally, for security purposes, the system provides functionality for managing and monitoring the address ranges of input/output spaces, binding them to specific devices. This helps resolve conflicts that arise when multiple drivers attempt to access the same I/O addresses.
caution
Of course, you can bypass the I/O space reservation functions for your device and directly use the read/write functions. However, this is strongly discouraged and such code is generally not allowed in the kernel.
I/O Mechanism io.Mechanism
An I/O mechanism is an abstraction that allows you to conveniently use different access methods, abstracting all specific code.
Mechanisms are entirely comptime
structures and do not add runtime overhead.
Mechanisms are useful for dynamically switching between different ways of working with a device. Often, devices may support both I/O ports and MMIO. Using mechanisms, you can write code that is independent of the I/O method and dynamically select the appropriate mechanism at runtime.
Another use case for mechanisms is with the Registers API.
Creating a mechanism (all parameters must be known at compile time):
io.Mechanism(AddrType, DataType, readFn, writeFn, ?initFn) type
-
AddrType
: the address type used by the mechanism, e.g.,u32
orusize
. -
DataType
: the data type, used to define the size and alignment for read/write operations, e.g.,u8
,u16
,u32
, etc. -
readFn
: the read function implementation.Should look like:
fn read(address: AddrType) DataType
-
writeFn
: the write function implementation.Should look like:
fn write(address: AddrType, data: DataType) void
-
initFn
: an initialization function used to run during runtime when creating an instance of the structure. This is an optional parameter and can be set tonull
.Should look like:
fn init(base: AddrType, size: AddrType) anyerror!AddrType
The function initializes and must return the base address (potentially modified) if successful.
Example Usage
const MyIoType = io.Mechanism(u16, u8, read, write, init);
fn init(base: u16, size: u16) !u16 {
_ = io.request("My I/O", base, size, .io_ports) orelse return error.IoBusy;
return base;
}
fn read(addr: u16) u8 {
// Read byte from I/O port
return io.inb(addr);
}
fn write(addr: u16, data: u8) void {
// Write byte to port
io.outb(addr, data);
}
pub fn use() void {
try MyIoType.init(0xCF8, 0x4);
MyIoType.write(0xCF8, 0x8000_0000);
_ = MyIoType.read(0xCFC);
}
I/O Management API
io.request(
comptime name: [:0]const u8,
base: usize,
size: usize,
comptime io_type: Type
) ?usize
Reserves a specific range of I/O space for device.
name
: the name of the region, used for informational purposes only.base
: the base address of the I/O space.size
: the size of the space relative to the base address.io_type
: the type of space, which can beio_ports
ormmio
.
The function returns null
if the request fails, for example, if the space is already occupied by another device.
io.release(base: usize, comptime io_type: Type) void
Releases the reserved range of I/O space.
base
: the base address specified duringio.request
.io_type
: the I/O type specified duringio.request
.
I/O Ports
Mechanism
io.IoPortsMechanism(
comptime name: [:0]const u8,
comptime bus_width: io.BusWidth
) type
Returns a mechanism that implements I/O operations through I/O ports.
Implements init
for automatic io.request
invocation.
-
name
: the name used during mechanism initialization in theio.request
call. -
bus_width
: defines the data size for read/write operations.Supported options are:
.byte
: u8,.word
: u16,.dword
: u32;.qword
is not supported.
API Functions
Reading:
io.inb(address); // read one byte.
io.inw(address); // read one word.
io.inl(address); // read a double word.
Writing:
io.outb(address, data); // write one byte.
io.outw(address, data); // write one word.
io.outl(address, data); // write a double word.
MMIO Space
Mechanism
io.MmioMechanism(
comptime name: [:0]const u8,
comptime bus_width: io.BusWidth
) type
Returns a mechanism that implements I/O operations through MMIO space.
Implements init
for automatic io.request
invocation and maps the provided MMIO base address to virtual address space. init
returns a virtual address.
-
name
: the name used during mechanism initialization in theio.request
call. -
bus_width
: defines the data size for read/write operations.Supported options are:
.byte
: u8,.word
: u16,.dword
: u32,.qword
: u64.
API Functions
Reading:
io.readb(address); // read one byte.
io.readw(address); // read one word.
io.readl(address); // read a double word.
io.readq(address); // read a quad word.
Writing:
io.writeb(address, data); // write one byte.
io.writew(address, data); // write one word.
io.writel(address, data); // write a double word.
io.writeq(address, data); // write a quad word.