commit 061f53af5e373ac8abe45148cd43c7760b27ae66
parent 2dde7c6696c589cec1f9250194be33037b81a491
Author: Sylvia Ivory <git@sivory.net>
Date: Thu, 5 Feb 2026 22:13:24 -0800
Add mailbox
Diffstat:
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);
+}