sylveos

Toy Operating System
Log | Files | Refs

commit 061f53af5e373ac8abe45148cd43c7760b27ae66
parent 2dde7c6696c589cec1f9250194be33037b81a491
Author: Sylvia Ivory <git@sivory.net>
Date:   Thu,  5 Feb 2026 22:13:24 -0800

Add mailbox

Diffstat:
Mboot/root.zig | 5+++++
Api/devices/mailbox.zig | 167+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpi/root.zig | 3++-
Aprograms/info.zig | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 260 insertions(+), 1 deletion(-)

diff --git a/boot/root.zig b/boot/root.zig @@ -39,6 +39,7 @@ export fn kmain() void { uart.write_slice("Enabling Interrupts\n"); uart.enable_interrupts() catch {}; pi.interrupts.enable_interrupts(); + pi.cycle_counter_init(); uart.write_slice("Entering program\n"); uart.flush(); @@ -46,6 +47,9 @@ export fn kmain() void { @import("program").main() catch |e| { uart.writer.print("program returned error: {t}\n", .{e}) catch {}; }; + + uart.flush(); + pi.reboot(); } export fn abort() noreturn { @@ -60,6 +64,7 @@ fn panic_handler(msg: []const u8, trace_addr: ?usize) noreturn { uart.write_slice("kernel panic: "); uart.write_slice(msg); uart.write_slice("\n"); + uart.flush(); var it = std.debug.StackIterator.init(trace_addr, null); var ix: usize = 0; diff --git a/pi/devices/mailbox.zig b/pi/devices/mailbox.zig @@ -0,0 +1,167 @@ +const std = @import("std"); + +const mem = @import("../mem.zig"); + +const BOX_STATUS = mem.BASE_ADDRESS + 0xB898; +const BOX_READ = mem.BASE_ADDRESS + 0xB880; +const BOX_WRITE = mem.BASE_ADDRESS + 0xB8A0; + +const BOX_FULL: u32 = 1 << 31; +const BOX_EMPTY: u32 = 1 << 30; +const BOX_CHANNEL: u32 = 8; + +const Error = error{ + Invalidresponse, +}; + +fn Message(comptime T: type) type { + return packed struct { + message_size: u32, // msg[0] + request_code: u32, // msg[1] + message_tag: u32, // msg[2] + bytes_available: u32, // msg[3] + response_code: u32, // msg[4] + data: T, // msg[5..] + end_tag: u32, // msg[..] + + const Self = @This(); + + pub fn new(tag: u32) Self { + return .{ + .message_size = @sizeOf(Self), + .request_code = 0, + .message_tag = tag, + .bytes_available = @sizeOf(T), + .response_code = 0, + .data = std.mem.zeroes(T), + .end_tag = 0, + }; + } + + pub fn send(self: *align(16) Self) !u32 { + mem.barrier(.Write); + + while ((mem.get_u32(@ptrFromInt(BOX_STATUS)) & BOX_FULL) != 0) {} + + mem.put_u32_barrier(@ptrFromInt(BOX_WRITE), @intFromPtr(self) | BOX_CHANNEL); + + while ((mem.get_u32(@ptrFromInt(BOX_STATUS)) & BOX_EMPTY) != 0) {} + + const value = mem.get_u32_barrier(@ptrFromInt(BOX_READ)); + + if ((value & 0xf) != BOX_CHANNEL) return Error.Invalidresponse; + + return value; + } + }; +} + +const SERIAL_TAG = 0x00010004; +pub fn get_serial_number() !u64 { + var message: Message(u64) align(16) = .new(SERIAL_TAG); + _ = try message.send(); + + return message.data; +} + +const ARM_MEMORY_INFO_TAG = 0x00010005; +const VC_MEMORY_INFO_TAG = 0x00010006; +const MemoryInfo = packed struct(u64) { + base_address: u32, + memory_size: u32, +}; + +pub fn get_arm_memory_info() !MemoryInfo { + var message: Message(MemoryInfo) align(16) = .new(ARM_MEMORY_INFO_TAG); + _ = try message.send(); + + return message.data; +} + +pub fn get_vc_memory_info() !MemoryInfo { + var message: Message(MemoryInfo) align(16) = .new(VC_MEMORY_INFO_TAG); + _ = try message.send(); + + return message.data; +} + +const MODEL_TAG = 0x00010001; +pub fn get_board_model() !u32 { + var message: Message(u32) align(16) = .new(MODEL_TAG); + _ = try message.send(); + + return message.data; +} + +const REVISION_TAG = 0x00010002; +pub fn get_board_revision() !u32 { + var message: Message(u32) align(16) = .new(REVISION_TAG); + _ = try message.send(); + + return message.data; +} + +const TEMP_TAG = 0x00030006; +pub const TemperatureId = enum(u32) { + SoC = 0, + ARM = 1, +}; +pub fn get_temperature(id: TemperatureId) !u32 { + const TemperatureRequest = packed struct(u64) { + id: u32, + value: u32, + }; + var message: Message(TemperatureRequest) align(16) = .new(TEMP_TAG); + message.data.id = @intFromEnum(id); + _ = try message.send(); + + return message.data.value; +} + +pub const ClockId = enum(u32) { + EMMC = 1, + UART = 2, + ARM = 3, + Core = 4, + V3D = 5, + H254 = 6, + ISP = 7, + SDRAM = 8, + Pixel = 9, + PWM = 10, + HEVC = 11, + EMMC2 = 12, + M2MC = 13, + PixelBvB = 14, +}; + +pub const ClockRate = enum(u32) { + Current = 0x00030002, + Real = 0x00030047, + Max = 0x00030004, + Min = 0x00030007, +}; + +pub fn get_clock_rate(id: ClockId, rate: ClockRate) !u32 { + const GetClockRateRequest = packed struct(u64) { + id: u32, + rate: u32, + }; + var message: Message(GetClockRateRequest) align(16) = .new(@intFromEnum(rate)); + message.data.id = @intFromEnum(id); + _ = try message.send(); + + return message.data.rate; +} + +const CLOCK_HZ_SET = 0x00038002; +pub fn set_clock_rate(id: ClockId, rate: u32, skip_turbo: bool) !u32 { + const SetClockRateRequest = packed struct(u96) { id: u32, rate: u32, skip_turbo: u32 }; + var message: Message(SetClockRateRequest) align(16) = .new(CLOCK_HZ_SET); + message.data.id = @intFromEnum(id); + message.data.skip_turbo = if (skip_turbo) 1 else 0; + message.data.rate = rate; + _ = try message.send(); + + return message.data.rate; +} diff --git a/pi/root.zig b/pi/root.zig @@ -10,6 +10,7 @@ pub const devices = struct { pub const gpio = @import("./devices/gpio.zig"); pub const mini_uart = @import("./devices/mini-uart.zig"); pub const timer = @import("./devices/timer.zig"); + pub const mailbox = @import("./devices/mailbox.zig"); }; pub inline fn cycle_counter_init() void { @@ -21,7 +22,7 @@ pub inline fn cycle_counter_init() void { } pub inline fn cycle_counter_read() u32 { - return asm volatile ("MCR p15, 0, %[result], c15, c12, 1" + return asm volatile ("MRC p15, 0, %[result], c15, c12, 1" : [result] "=r" (-> u32), ); } diff --git a/programs/info.zig b/programs/info.zig @@ -0,0 +1,86 @@ +const std = @import("std"); +const pi = @import("pi"); + +const uart = pi.devices.mini_uart; +const mailbox = pi.devices.mailbox; +const clock = pi.devices.clock; + +fn benchmark(iters: u32) !void { + const start: i64 = pi.cycle_counter_read(); + + const s = clock.current_count(); + while ((clock.current_count() - s) < 1000 * 1000) {} + + const end: i64 = pi.cycle_counter_read(); + + try uart.writer.print(" cycles per second = {d}\n", .{end - start}); + uart.flush(); + + var buffer: [1024 * 1024]u8 = .{0} ** (1024 * 1024); + for (0..buffer.len) |i| { + buffer[i] = @truncate(i % 256); + } + + const now = clock.current_count(); + + for (0..iters) |_| { + std.mem.doNotOptimizeAway(std.hash.Crc32.hash(&buffer)); + } + + try uart.writer.print(" Crc32 1Mb * {d} = {D}\n", .{ iters, (clock.current_count() - now) * 1000 }); + uart.flush(); +} + +pub fn main() !void { + const serial_number = try mailbox.get_serial_number(); + const memory_info = try mailbox.get_arm_memory_info(); + const board_model = try mailbox.get_board_model(); + const board_revision = try mailbox.get_board_revision(); + const temperature = try mailbox.get_temperature(.ARM); + const clock_current = try mailbox.get_clock_rate(.ARM, .Current); + const clock_real = try mailbox.get_clock_rate(.ARM, .Real); + const clock_min = try mailbox.get_clock_rate(.ARM, .Min); + const clock_max = try mailbox.get_clock_rate(.ARM, .Max); + + try uart.writer.print("serial number: {d}\n", .{serial_number}); + uart.flush(); + + try uart.writer.print("memory size: {d}\n", .{memory_info.memory_size}); + uart.flush(); + + try uart.writer.print("memory base address: 0x{X}\n", .{memory_info.base_address}); + uart.flush(); + + try uart.writer.print("board model: {d}\n", .{board_model}); + uart.flush(); + + try uart.writer.print("board revision: {X}\n", .{board_revision}); + uart.flush(); + + try uart.writer.print("temperature: {d}C\n", .{temperature / 1000}); + uart.flush(); + + try uart.writer.print("clock current: {d}hz\n", .{clock_current}); + uart.flush(); + + try uart.writer.print("clock real: {d}hz\n", .{clock_real}); + uart.flush(); + + try uart.writer.print("clock min: {d}hz\n", .{clock_min}); + uart.flush(); + + try uart.writer.print("clock max: {d}hz\n", .{clock_max}); + uart.flush(); + + try uart.writer.print("before overclock:\n", .{}); + uart.flush(); + + try benchmark(10); + + _ = try mailbox.set_clock_rate(.ARM, 1_000_000_000, true); + + try uart.writer.print("after overclock:\n", .{}); + uart.flush(); + + try benchmark(10); +}