Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 121 additions & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,90 @@ pub fn build(b: *Builder) !void {

const make_iso = switch (target.getCpuArch()) {
.i386 => b.addSystemCommand(&[_][]const u8{ "./makeiso.sh", boot_path, modules_path, iso_dir_path, exec.getOutputPath(), output_iso }),
.aarch64 => b.addSystemCommand(&[_][]const u8{ "aarch64-linux-gnu-objcopy", exec.getOutputPath(), "-O", "binary", try fs.path.join(b.allocator, &[_][]const u8{ exec.output_dir.?, "kernel8.img" }) }),
.aarch64 => makeRpiImage: {
const elf = try fs.path.join(b.allocator, &[_][]const u8{ exec.output_dir.?, "pluto.elf" });
const kernel_image = try fs.path.join(b.allocator, &[_][]const u8{ b.cache_root, "kernel8.img" });
const kernel_load_at_zero_image = try fs.path.join(b.allocator, &[_][]const u8{ b.cache_root, "kernel8-load-at-zero.img" });
const sdcard_image = try fs.path.join(b.allocator, &[_][]const u8{ b.cache_root, "rpi-sdcard.img" });

const objcopy = b.addSystemCommand(&[_][]const u8{
"aarch64-linux-gnu-objcopy",
elf,
"-O",
"binary",
kernel_image,
});
objcopy.step.dependOn(&exec.step);

const make_kernel_load_at_zero = addCustomStep(b, MakeKernelLoadAtZeroStep{
.input_name = kernel_image,
.output_name = kernel_load_at_zero_image,
});
make_kernel_load_at_zero.step.dependOn(&objcopy.step);

const allocate_sdcard_image = b.addSystemCommand(&[_][]const u8{
"fallocate",
"-l",
"64M",
sdcard_image,
});
allocate_sdcard_image.step.dependOn(&make_kernel_load_at_zero.step);

const format_sdcard_image = b.addSystemCommand(&[_][]const u8{
"mformat",
"-i",
sdcard_image,
"-F",
});
format_sdcard_image.step.dependOn(&allocate_sdcard_image.step);

const copy_bootcode = b.addSystemCommand(&[_][]const u8{
"mcopy",
"-i",
sdcard_image,
"src/kernel/arch/aarch64/rpi-sdcard/bootcode.bin",
"::bootcode.bin",
});
copy_bootcode.step.dependOn(&format_sdcard_image.step);

const copy_config = b.addSystemCommand(&[_][]const u8{
"mcopy",
"-i",
sdcard_image,
"src/kernel/arch/aarch64/rpi-sdcard/config.txt",
"::config.txt",
});
copy_config.step.dependOn(&copy_bootcode.step);

const copy_fixup = b.addSystemCommand(&[_][]const u8{
"mcopy",
"-i",
sdcard_image,
"src/kernel/arch/aarch64/rpi-sdcard/fixup.dat",
"::fixup.dat",
});
copy_fixup.step.dependOn(&copy_config.step);

const copy_kernel = b.addSystemCommand(&[_][]const u8{
"mcopy",
"-i",
sdcard_image,
kernel_load_at_zero_image,
"::kernel8-load-at-zero.img",
});
copy_kernel.step.dependOn(&copy_fixup.step);

const copy_start = b.addSystemCommand(&[_][]const u8{
"mcopy",
"-i",
sdcard_image,
"src/kernel/arch/aarch64/rpi-sdcard/start.elf",
"::start.elf",
});
copy_start.step.dependOn(&copy_kernel.step);

break :makeRpiImage copy_start;
},
else => unreachable,
};
make_iso.step.dependOn(&exec.step);
Expand Down Expand Up @@ -161,3 +244,40 @@ pub fn build(b: *Builder) !void {
});
debug_step.dependOn(&debug_cmd.step);
}

const MakeKernelLoadAtZeroStep = struct {
step: std.build.Step = undefined,
input_name: []const u8,
output_name: []const u8,
pub fn make(step: *std.build.Step) anyerror!void {
const self = @fieldParentPtr(MakeKernelLoadAtZeroStep, "step", step);
const cwd = fs.cwd();
const image = try cwd.openFile(self.input_name, fs.File.OpenFlags{});
defer image.close();
const kernel_load_at_zero_image = try cwd.createFile(self.output_name, fs.File.CreateFlags{});
defer kernel_load_at_zero_image.close();
// armstub not yet working, therefore: b 0x80000 (which is 0x14020000)
_ = try kernel_load_at_zero_image.write(&[4]u8{ 0x00, 0x00, 0x02, 0x14 });
// followed by 0 filler until 0x80000
var i: usize = 4;
while (i < 0x80000) : (i += 1) {
_ = try kernel_load_at_zero_image.write(&[1]u8{0x00});
}
// followed finally by kernel that starts at 0x80000
var read_buf: [1]u8 = undefined;
while (true) {
var n = try image.read(&read_buf);
if (n == 0) {
break;
}
_ = try kernel_load_at_zero_image.write(&read_buf);
}
}
};

pub fn addCustomStep(self: *std.build.Builder, customStep: anytype) *@TypeOf(customStep) {
var allocated = self.allocator.create(@TypeOf(customStep)) catch unreachable;
allocated.* = customStep;
allocated.*.step = std.build.Step.init(.Custom, @typeName(@TypeOf(customStep)), self.allocator, @TypeOf(customStep).make);
return allocated;
}
187 changes: 146 additions & 41 deletions src/kernel/arch/aarch64/arch.zig
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ pub const KERNEL_VMM_PAYLOAD: VmmPayload = 0;

// The system clock frequency in Hz
const SYSTEM_CLOCK: usize = 700000000;
const UART_BAUD_RATE: usize = 115200;

var mmio_addr: usize = undefined;
pub var mmio_addr: usize = undefined;

extern var KERNEL_PHYSADDR_START: *u32;
extern var KERNEL_PHYSADDR_END: *u32;
Expand All @@ -33,52 +32,53 @@ pub fn initTTY(boot_payload: BootPayload) TTY {
return undefined;
}

pub fn initSerial(board: BootPayload) Serial {
pub fn initMmioAddress(board: *rpi.RaspberryPiBoard) void {
mmio_addr = board.mmioAddress();
// Disable uart
mmio.write(mmio_addr, mmio.Register.UART_CONTROL, 0);
// Disable pull up/down for all GPIO pins
mmio.write(mmio_addr, mmio.Register.GPIO_PULL, 0);
// Disable pull up/down for pins 14 and 15
mmio.write(mmio_addr, mmio.Register.GPIO_PULL_CLK, (1 << 14) | (1 << 15));
// Apply pull up/down change for pins 14 and 15
mmio.write(mmio_addr, mmio.Register.GPIO_PULL_CLK, 0);
// Clear pending uart interrupts
mmio.write(mmio_addr, mmio.Register.UART_INT_CONTROL, 0x7FF);

// For models after rpi 2 (those supporting aarch64) the uart clock is dependent on the system clock
// so set it to a known constant rather than calculating it
const cmd: []const volatile u32 align(16) = &[_]u32{ 36, 0, 0x38002, 12, 8, 2, SYSTEM_CLOCK, 0, 0 };
const cmd_addr = (@intCast(u32, @ptrToInt(&cmd)) & ~@as(u32, 0xF)) | 8;
// Wait until the mailbox isn't busy
while ((mmio.read(mmio_addr, mmio.Register.MBOX_STATUS) & 0x80000000) != 0) {}
mmio.write(mmio_addr, mmio.Register.MBOX_WRITE, cmd_addr);
// Wait for the correct response
while ((mmio.read(mmio_addr, mmio.Register.MBOX_STATUS) & 0x40000000) != 0 or mmio.read(mmio_addr, mmio.Register.MBOX_READ) != cmd_addr) {}

// Setup baudrate
const divisor: f32 = @intToFloat(f32, SYSTEM_CLOCK) / @intToFloat(f32, 16 * UART_BAUD_RATE);
const divisor_int: u32 = @floatToInt(u32, divisor);
const divisor_frac: u32 = @floatToInt(u32, (divisor - @intToFloat(f32, divisor_int)) * 64.0 + 0.5);
mmio.write(mmio_addr, mmio.Register.UART_BAUD_INT, divisor_int);
mmio.write(mmio_addr, mmio.Register.UART_BAUD_FRAC, divisor_frac);

// Enable FIFO, 8 bit words, 1 stop bit and no parity bit
mmio.write(mmio_addr, mmio.Register.UART_LINE_CONTROL, 1 << 4 | 1 << 5 | 1 << 6);
// Mask all interrupts
mmio.write(mmio_addr, mmio.Register.UART_INT_MASK, 1 << 1 | 1 << 4 | 1 << 5 | 1 << 6 | 1 << 7 | 1 << 8 | 1 << 9 | 1 << 10);
// Enable UART0 receive and transmit
mmio.write(mmio_addr, mmio.Register.UART_CONTROL, 1 << 0 | 1 << 8 | 1 << 9);
}

// The auxiliary uart (uart1) is the primary uart used for the console on pi3b and up.
// The main uart (uart0) is used for the on-board bluetooth device on these boards.
// See https://www.raspberrypi.org/documentation/configuration/uart.md
// Note that config.txt contains enable_uart=1 which locks the core clock frequency
// which is required for a stable baud rate on the auxiliary uart (uart1.)
//
// However, qemu does not implement uart1. Therefore on qemu we use uart0 for the text console.
// Furthermore uart0 on qemu does not need any initialization.
//
pub fn initSerial(board: BootPayload) Serial {
if (!Cpu.isQemu()) {
// On an actual rpi, initialize uart1 to 115200 baud on pins 14 and 15:
rpi.pinSetPullUpAndFunction(14, .None, .AlternateFunction5);
rpi.pinSetPullUpAndFunction(15, .None, .AlternateFunction5);
mmio.write(mmio_addr, .AUX_ENABLES, 1);
mmio.write(mmio_addr, .AUX_MU_IER_REG, 0);
mmio.write(mmio_addr, .AUX_MU_CNTL_REG, 0);
mmio.write(mmio_addr, .AUX_MU_LCR_REG, 3);
mmio.write(mmio_addr, .AUX_MU_MCR_REG, 0);
mmio.write(mmio_addr, .AUX_MU_IER_REG, 0);
mmio.write(mmio_addr, .AUX_MU_IIR_REG, 0xc6);
mmio.write(mmio_addr, .AUX_MU_BAUD_REG, 270);
mmio.write(mmio_addr, .AUX_MU_CNTL_REG, 3);
}
return .{
.write = uartWriteByte,
};
}

fn uartWriteByte(byte: u8) void {
// Wait until the UART is ready to transmit
while ((mmio.read(mmio_addr, mmio.Register.UART_FLAGS) & (1 << 5)) != 0) {}
mmio.write(mmio_addr, mmio.Register.UART_DATA, byte);
pub fn uartWriteByte(byte: u8) void {
// if ascii line feed then first send carriage return
if (byte == 10) {
uartWriteByte(13);
}
if (Cpu.isQemu()) {
// Since qemu does not implement uart1, qemu uses uart0 for the console:
while (mmio.read(mmio_addr, .UART_FLAGS) & (1 << 5) != 0) {}
mmio.write(mmio_addr, .UART_DATA, byte);
} else {
// On an actual rpi, use uart1:
while (mmio.read(mmio_addr, .AUX_MU_LSR_REG) & (1 << 5) == 0) {}
mmio.write(mmio_addr, .AUX_MU_IO_REG, byte);
}
}

pub fn initMem(payload: BootPayload) std.mem.Allocator.Error!mem.MemProfile {
Expand Down Expand Up @@ -122,3 +122,108 @@ pub fn haltNoInterrupts() noreturn {
pub fn spinWait() noreturn {
while (true) {}
}

pub const Cpu = struct {
pub const cntfrq = systemRegisterPerExceptionLevel("cntfrq");
pub const CurrentEL = systemRegister("CurrentEL");
pub const elr = systemRegisterPerExceptionLevel("elr");
pub const esr = systemRegisterPerExceptionLevel("esr");
pub const far = systemRegisterPerExceptionLevel("far");
pub const lr = cpuRegister("lr");
pub const mair = systemRegisterPerExceptionLevel("mair");
pub const midr = systemRegisterPerExceptionLevel("midr");
pub const mpidr = systemRegisterPerExceptionLevel("mpidr");
pub const sctlr = systemRegisterPerExceptionLevel("sctlr");
pub const sp = cpuRegister("sp");
pub const spsr = systemRegisterPerExceptionLevel("spsr");
pub const tcr = systemRegisterPerExceptionLevel("tcr");
pub const ttbr0 = systemRegisterPerExceptionLevel("ttbr0");
pub const vbar = systemRegisterPerExceptionLevel("vbar");

fn cpuRegister(comptime register_name: []const u8) type {
return struct {
pub inline fn read() usize {
const data = asm ("mov %[data], " ++ register_name
: [data] "=r" (-> usize)
);
return data;
}
pub inline fn write(data: usize) void {
asm volatile ("mov " ++ register_name ++ ", %[data]"
:
: [data] "r" (data)
);
}
};
}
fn systemRegisterPerExceptionLevel(comptime register_name: []const u8) type {
return struct {
pub inline fn el(exception_level: u2) type {
const level_string = switch (exception_level) {
0 => "0",
1 => "1",
2 => "2",
3 => "3",
};
return systemRegister(register_name ++ "_el" ++ level_string);
}
};
}
fn systemRegister(comptime register_name: []const u8) type {
return struct {
pub inline fn read() usize {
const word = asm ("mrs %[word], " ++ register_name
: [word] "=r" (-> usize)
);
return word;
}
pub inline fn readSetWrite(bits: usize) void {
write(read() | bits);
}
pub inline fn write(data: usize) void {
asm volatile ("msr " ++ register_name ++ ", %[data]"
:
: [data] "r" (data)
);
}
};
}
pub inline fn isb() void {
asm volatile (
\\ isb
);
}

pub fn isQemu() bool {
return cntfrq.el(0).read() != 0;
}

pub inline fn wfe() void {
asm volatile (
\\ wfe
);
}
};

// map 1GB to ram except last 16MB to mmio
pub fn enableFlatMmu() void {
const level_2_table = @intToPtr([*]usize, 0x50000)[0..2];
level_2_table[0] = 0x60000 | 0x3;
level_2_table[1] = 0x70000 | 0x3;
const level_3_page_table = @intToPtr([*]usize, 0x60000)[0 .. 2 * 8 * 1024];
const page_size = 64 * 1024;
const start_of_mmio = level_3_page_table.len - (16 * 1024 * 1024 / page_size);
var index: usize = 0;
while (index < start_of_mmio) : (index += 1) {
level_3_page_table[index] = index * page_size + 0x0703; // normal pte=3 attr index=0 inner shareable=3 af=1
}
while (index < level_3_page_table.len) : (index += 1) {
level_3_page_table[index] = index * page_size + 0x0607; // device pte=3 attr index=1 outer shareable=2 af=1
}
Cpu.mair.el(3).write(0x04ff);
Cpu.tcr.el(3).readSetWrite(0x80804022);
Cpu.ttbr0.el(3).write(0x50000);
Cpu.isb();
Cpu.sctlr.el(3).readSetWrite(0x1);
Cpu.isb();
}
Loading