diff --git a/build.zig b/build.zig index eb4ff17d..51dce25e 100644 --- a/build.zig +++ b/build.zig @@ -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(©_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(©_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(©_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(©_kernel.step); + + break :makeRpiImage copy_start; + }, else => unreachable, }; make_iso.step.dependOn(&exec.step); @@ -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; +} diff --git a/src/kernel/arch/aarch64/arch.zig b/src/kernel/arch/aarch64/arch.zig index 0872e78a..c8601de7 100644 --- a/src/kernel/arch/aarch64/arch.zig +++ b/src/kernel/arch/aarch64/arch.zig @@ -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; @@ -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 { @@ -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(); +} diff --git a/src/kernel/arch/aarch64/boot.zig b/src/kernel/arch/aarch64/boot.zig index a3f1f9d0..e10b7e27 100644 --- a/src/kernel/arch/aarch64/boot.zig +++ b/src/kernel/arch/aarch64/boot.zig @@ -1,47 +1,61 @@ -const rpi = @import("rpi.zig"); +const arch = @import("arch.zig"); +const Cpu = arch.Cpu; const interrupts = @import("interrupts.zig"); +const rpi = @import("rpi.zig"); -export var kernel_stack: [16 * 1024]u8 align(16) linksection(".bss.stack") = undefined; +const number_of_cores = 4; +const per_core_stack_size = 16 * 1024; +export var kernel_stack: [number_of_cores * per_core_stack_size]u8 align(16) linksection(".bss.stack") = undefined; extern fn kmain(payload: *const rpi.RaspberryPiBoard) void; extern var KERNEL_STACK_END: *usize; export fn _start() linksection(".text.boot") callconv(.Naked) noreturn { - // Halt all cores other than the primary core, until the kernel has multicore support - const core_id = asm ("mrs %[res], mpidr_el1" - : [res] "=r" (-> usize) - ) & 3; - if (core_id != 0) { + // Give each of four cores one-fourth of the reserved stack space + asm volatile ( + \\ mrs x0, mpidr_el1 + \\ and x0, x0, #0x3 + \\ add x0, x0, #1 // core number + 1 therefore 1..4 + \\ mov x1, #16 * 1024 // per_core_stack_size + \\ mul x0, x0, x1 + \\ ldr x1, =kernel_stack + \\ add x0, x0, x1 + \\ mov sp, x0 + : + : + : "x0", "x1" + ); + + start(); // must start a proper zig function to get x29 (frame pointer) initialized! +} + +fn start() noreturn { + // Halt all cores other than core 0, until the kernel has multicore support + if (Cpu.mpidr.el(1).read() & 0x3 != 0) { while (true) { - asm volatile ("wfe"); + Cpu.wfe(); } } - // Setup the stack - asm volatile ("mov sp, %[stack_end]" - : - : [stack_end] "r" (@ptrCast([*]u8, &KERNEL_STACK_END)) - ); + // Give all exception levels the same vector table + Cpu.vbar.el(1).write(@ptrToInt(&interrupts.exception_table)); + Cpu.vbar.el(2).write(@ptrToInt(&interrupts.exception_table)); + Cpu.vbar.el(3).write(@ptrToInt(&interrupts.exception_table)); - // Setup the exception table - asm volatile ( - \\msr vbar_el1, %[table_addr] - \\msr vbar_el2, %[table_addr] - \\msr vbar_el3, %[table_addr] - : - : [table_addr] "r" (@ptrToInt(&interrupts.exception_table)) - ); + interrupts.exception_handler_depth = 0; - // The rpi puts the board part number in midr_el1 - const board = detectBoard(); + arch.enableFlatMmu(); + + board = detectBoard(); + arch.initMmioAddress(&board); kmain(&board); while (true) {} } +var board: rpi.RaspberryPiBoard = undefined; + fn detectBoard() rpi.RaspberryPiBoard { - const part_number = @truncate(u12, asm ("mrs %[res], midr_el1" - : [res] "=r" (-> usize) - ) >> 4); + const part_number = @truncate(u12, Cpu.midr.el(1).read() >> 4); return rpi.RaspberryPiBoard.fromPartNumber(part_number) orelse @panic("Unrecognised part number"); } diff --git a/src/kernel/arch/aarch64/interrupts.zig b/src/kernel/arch/aarch64/interrupts.zig index 52207f96..60ce1416 100644 --- a/src/kernel/arch/aarch64/interrupts.zig +++ b/src/kernel/arch/aarch64/interrupts.zig @@ -1,5 +1,7 @@ const arch = @import("arch.zig"); +const Cpu = arch.Cpu; const log = @import("../../log.zig"); +const rpi = @import("rpi.zig"); pub extern var exception_table: *usize; @@ -59,27 +61,44 @@ const ExceptionCategory = enum(u2) { const ExceptionClass = enum(u6) { instruction_abort = 0x21, + pc_alignment = 0x22, data_abort = 0x25, sp_alignment = 0x26, _, }; +pub var exception_handler_depth: u32 = undefined; + export fn exceptionHandler() noreturn { - const exception_entry_offset = @truncate(u32, lr() & 0x780); + exception_handler_depth += 1; + if (exception_handler_depth > 1) { + if (exception_handler_depth == 2) { + log.logError("\n", .{}); + log.logError("arm exception taken when already active!\n", .{}); + } + rpi.spinLed(50); + } + const exception_entry_offset = @truncate(u32, Cpu.lr.read() & 0x780); var elr_elx: usize = undefined; var esr_elx: usize = undefined; var far_elx: usize = undefined; + var mair_elx: usize = undefined; var sctlr_elx: usize = undefined; var spsr_elx: usize = undefined; + var tcr_elx: usize = undefined; + var ttbr0_elx: usize = undefined; var vbar_elx: usize = undefined; inline for ([_]u32{ 1, 2, 3 }) |exception_level| { if (exception_level == currentExceptionLevel()) { - elr_elx = mrsEl("elr_el", exception_level); - esr_elx = mrsEl("esr_el", exception_level); - far_elx = mrsEl("far_el", exception_level); - sctlr_elx = mrsEl("sctlr_el", exception_level); - spsr_elx = mrsEl("spsr_el", exception_level); - vbar_elx = mrsEl("vbar_el", exception_level); + elr_elx = Cpu.elr.el(exception_level).read(); + esr_elx = Cpu.esr.el(exception_level).read(); + far_elx = Cpu.far.el(exception_level).read(); + mair_elx = Cpu.mair.el(exception_level).read(); + sctlr_elx = Cpu.sctlr.el(exception_level).read(); + spsr_elx = Cpu.spsr.el(exception_level).read(); + tcr_elx = Cpu.tcr.el(exception_level).read(); + ttbr0_elx = Cpu.ttbr0.el(exception_level).read(); + vbar_elx = Cpu.vbar.el(exception_level).read(); } } const esr_elx_class = @intToEnum(ExceptionClass, @truncate(u6, esr_elx >> 26)); @@ -98,6 +117,24 @@ export fn exceptionHandler() noreturn { log.logError(" data abort in level {} (while using sp_el{} and not sp_el0)\n", .{ currentExceptionLevel(), currentExceptionLevel() }); log.logError(" 32 bit instruction at 0x{x} accessing 0x{x}\n", .{ elr_elx, far_elx }); }, + 0x21 => { + if (far_elx == 0x1) { + seen_previously = true; + log.logError("this exception has been seen previously in development\n", .{}); + log.logError(" data abort in level {} (while using sp_el{} and not sp_el0)\n", .{ currentExceptionLevel(), currentExceptionLevel() }); + log.logError(" 32 bit instruction at 0x{x} accessing 0x{x}\n", .{ elr_elx, far_elx }); + log.logError(" test 32 bit read of address 0x1\n", .{}); + } else { + seen_previously = true; + log.logError("this exception has been seen previously in development\n", .{}); + log.logError(" data abort, read, alignment fault ...\n", .{}); + } + }, + 0x61 => { + seen_previously = true; + log.logError("this exception has been seen previously in development\n", .{}); + log.logError(" data abort, write, alignment\n", .{}); + }, else => {}, } }, @@ -123,59 +160,16 @@ export fn exceptionHandler() noreturn { log.logError(" esr_el{} 0x{x}: {}, 32 bit instruction {}, iss 0x{x}\n", .{ currentExceptionLevel(), esr_elx, esr_elx_class, esr_elx_instruction_is_32_bits, esr_elx_iss }); log.logError(" exception entry offset 0x{x} {} {}\n", .{ exception_entry_offset, @intToEnum(ExceptionTakenFrom, @truncate(u2, exception_entry_offset >> 9)), @intToEnum(ExceptionCategory, @truncate(u2, exception_entry_offset >> 7)) }); log.logError(" far_el{} 0x{x}\n", .{ currentExceptionLevel(), far_elx }); + log.logError(" mair_el{} 0x{x}\n", .{ currentExceptionLevel(), mair_elx }); log.logError(" sctlr_el{} 0x{x}\n", .{ currentExceptionLevel(), sctlr_elx }); log.logError(" spsr_el{} 0x{x}\n", .{ currentExceptionLevel(), spsr_elx }); + log.logError(" tcr_el{} 0x{x}\n", .{ currentExceptionLevel(), tcr_elx }); + log.logError(" ttbr0_el{} 0x{x}\n", .{ currentExceptionLevel(), ttbr0_elx }); log.logError(" vbar_el{} 0x{x}\n", .{ currentExceptionLevel(), vbar_elx }); - while (true) {} -} - -inline fn lr() usize { - return register("lr"); -} - -inline fn sp() usize { - return register("sp"); -} - -fn cpsr() usize { - return mrs("cpsr"); -} - -fn spsr() usize { - return mrs("spsr"); -} - -fn sctlr() usize { - var word = asm ("mrc p15, 0, %[word], c1, c0, 0" - : [word] "=r" (-> usize) - ); - return word; -} - -inline fn mrs(comptime register_name: []const u8) usize { - const word = asm ("mrs %[word], " ++ register_name - : [word] "=r" (-> usize) - ); - return word; -} - -inline fn register(comptime register_name: []const u8) usize { - const word = asm ("mov %[word], " ++ register_name - : [word] "=r" (-> usize) - ); - return word; -} - -inline fn mrsEl(comptime register_name: []const u8, comptime exception_level: u32) usize { - const exception_level_string = switch (exception_level) { - 1 => "1", - 2 => "2", - 3 => "3", - else => unreachable, - }; - return mrs(register_name ++ exception_level_string); + log.logError("exception done\n", .{}); + rpi.spinLed(100); } pub inline fn currentExceptionLevel() u2 { - return @truncate(u2, mrs("CurrentEL") >> 2); + return @truncate(u2, Cpu.CurrentEL.read() >> 2); } diff --git a/src/kernel/arch/aarch64/mmio.zig b/src/kernel/arch/aarch64/mmio.zig index 65892625..7f900f08 100644 --- a/src/kernel/arch/aarch64/mmio.zig +++ b/src/kernel/arch/aarch64/mmio.zig @@ -1,10 +1,42 @@ -const GPIO_OFFSET: usize = 0x200000; -const UART_OFFSET: usize = GPIO_OFFSET + 0x1000; +const log = @import("../../log.zig"); +const std = @import("std"); + +const MMIO_OFFSET: usize = 0x200000; +const GPIO_OFFSET: usize = MMIO_OFFSET + 0x0000; +const UART_OFFSET: usize = MMIO_OFFSET + 0x1000; +const AUX_OFFSET: usize = MMIO_OFFSET + 0x15000; const MBOX_OFFSET: usize = 0xB880; pub const Register = enum(usize) { + AUX_IRQ = AUX_OFFSET + 0x00, + AUX_ENABLES = AUX_OFFSET + 0x04, + AUX_MU_IO_REG = AUX_OFFSET + 0x40, + AUX_MU_IER_REG = AUX_OFFSET + 0x44, + AUX_MU_IIR_REG = AUX_OFFSET + 0x48, + AUX_MU_LCR_REG = AUX_OFFSET + 0x4c, + AUX_MU_MCR_REG = AUX_OFFSET + 0x50, + AUX_MU_LSR_REG = AUX_OFFSET + 0x54, + AUX_MU_MSR_REG = AUX_OFFSET + 0x58, + AUX_MU_SCRATCH = AUX_OFFSET + 0x5c, + AUX_MU_CNTL_REG = AUX_OFFSET + 0x60, + AUX_MU_STAT_REG = AUX_OFFSET + 0x64, + AUX_MU_BAUD_REG = AUX_OFFSET + 0x68, + GPIO_FSEL0 = GPIO_OFFSET + 0x00, + GPIO_FSEL1 = GPIO_OFFSET + 0x04, + GPIO_FSEL2 = GPIO_OFFSET + 0x08, + GPIO_FSEL3 = GPIO_OFFSET + 0x0c, + GPIO_FSEL4 = GPIO_OFFSET + 0x10, + GPIO_FSEL5 = GPIO_OFFSET + 0x14, + GPIO_SET0 = GPIO_OFFSET + 0x1c, + GPIO_SET1 = GPIO_OFFSET + 0x20, + GPIO_CLR0 = GPIO_OFFSET + 0x28, + GPIO_CLR1 = GPIO_OFFSET + 0x2c, GPIO_PULL = GPIO_OFFSET + 0x94, - GPIO_PULL_CLK = GPIO_OFFSET + 0x98, + GPIO_PULL_CLK0 = GPIO_OFFSET + 0x98, + GPIO_PULL_CLK1 = GPIO_OFFSET + 0x9c, + MBOX_STATUS = MBOX_OFFSET + 0x18, + MBOX_WRITE = MBOX_OFFSET + 0x20, + MBOX_READ = MBOX_OFFSET + 0x00, UART_DATA = UART_OFFSET + 0x00, UART_FLAGS = UART_OFFSET + 0x18, UART_CONTROL = UART_OFFSET + 0x30, @@ -13,19 +45,49 @@ pub const Register = enum(usize) { UART_BAUD_FRAC = UART_OFFSET + 0x28, UART_LINE_CONTROL = UART_OFFSET + 0x2C, UART_INT_MASK = UART_OFFSET + 0x38, - MBOX_STATUS = MBOX_OFFSET + 0x18, - MBOX_WRITE = MBOX_OFFSET + 0x20, - MBOX_READ = MBOX_OFFSET + 0x00, }; pub fn write(mmio_base: usize, reg: Register, data: u32) void { const addr = mmio_base + @enumToInt(reg); - const ptr = @intToPtr(*u32, addr); + const ptr = @intToPtr(*volatile u32, addr); ptr.* = data; } pub fn read(mmio_base: usize, reg: Register) u32 { const addr = mmio_base + @enumToInt(reg); - const ptr = @intToPtr(*u32, addr); + const ptr = @intToPtr(*volatile u32, addr); return ptr.*; } + +const GPIO_MAX_PIN = 53; + +pub const RegisterBitField = struct { + register: Register, + shifted_mask: u32, + shifted_data: u32, + + pub fn at(f: *RegisterBitField, mmio_base: usize, first_register: Register, data: anytype, field_index: usize) void { + const field_width = @bitSizeOf(@TypeOf(data)); + const fields_per_register: usize = 32 / field_width; + const mask = (@as(u32, 1) << field_width) - 1; + const shift_count = @truncate(u5, ((field_index % fields_per_register) * field_width)); + f.register = @intToEnum(Register, @enumToInt(first_register) + field_index / fields_per_register * 4); + f.shifted_mask = mask << shift_count; + f.shifted_data = @as(u32, data) << shift_count; + } +}; + +pub fn writeClock(mmio_base: usize, first_register: Register, data: anytype, field_index: usize) void { + var field: RegisterBitField = undefined; + field.at(mmio_base, first_register, data, field_index); + write(mmio_base, field.register, field.shifted_data); +} + +pub fn readModifyWriteField(mmio_base: usize, first_register: Register, data: anytype, field_index: usize) void { + var field: RegisterBitField = undefined; + field.at(mmio_base, first_register, data, field_index); + var modified = read(mmio_base, field.register); + modified &= ~field.shifted_mask; + modified |= field.shifted_data; + write(mmio_base, field.register, modified); +} diff --git a/src/kernel/arch/aarch64/rpi-sdcard/bootcode.bin b/src/kernel/arch/aarch64/rpi-sdcard/bootcode.bin new file mode 100644 index 00000000..28982489 Binary files /dev/null and b/src/kernel/arch/aarch64/rpi-sdcard/bootcode.bin differ diff --git a/src/kernel/arch/aarch64/rpi-sdcard/config.txt b/src/kernel/arch/aarch64/rpi-sdcard/config.txt new file mode 100644 index 00000000..a461a5fd --- /dev/null +++ b/src/kernel/arch/aarch64/rpi-sdcard/config.txt @@ -0,0 +1,4 @@ +arm_64bit=1 +disable_commandline_tags=1 +kernel=kernel8-load-at-zero.img +old_kernel=1 diff --git a/src/kernel/arch/aarch64/rpi-sdcard/fixup.dat b/src/kernel/arch/aarch64/rpi-sdcard/fixup.dat new file mode 100644 index 00000000..88363225 Binary files /dev/null and b/src/kernel/arch/aarch64/rpi-sdcard/fixup.dat differ diff --git a/src/kernel/arch/aarch64/rpi-sdcard/start.elf b/src/kernel/arch/aarch64/rpi-sdcard/start.elf new file mode 100644 index 00000000..d40bc010 Binary files /dev/null and b/src/kernel/arch/aarch64/rpi-sdcard/start.elf differ diff --git a/src/kernel/arch/aarch64/rpi.zig b/src/kernel/arch/aarch64/rpi.zig index 9cbe9e03..4b3de64d 100644 --- a/src/kernel/arch/aarch64/rpi.zig +++ b/src/kernel/arch/aarch64/rpi.zig @@ -1,3 +1,6 @@ +const arch = @import("arch.zig"); +const mmio = @import("mmio.zig"); + /// The supported aarch64-capable raspberry pi boards pub const RaspberryPiBoard = enum { RaspberryPi3, @@ -25,3 +28,101 @@ pub const RaspberryPiBoard = enum { }; } }; + +/// Spin the cpu while flashing the led +/// +/// IN: flashing period in number of loops +/// +/// Does not return +/// +pub fn spinLed(period: u32) noreturn { + const activity_led = 29; + pinSetFunction(activity_led, .Output); + var i: u32 = 0; + var j: u32 = 0; + while (true) : (j += 1) { + if (j % (period * 1000 / 2) == 0) { + i += 1; + pinWrite(activity_led, @truncate(u1, i)); + } + } +} + +/// Set the pin function +/// +/// IN: pin_index is the bcm pin number +/// IN: function is the PinFunction, such as input, output, or internally routed (alternate) +/// +pub fn pinSetFunction(pin_index: u6, function: PinFunction) void { + mmio.readModifyWriteField(arch.mmio_addr, .GPIO_FSEL0, @enumToInt(function), pin_index); +} + +/// Set the pin pull up/down +/// +/// IN: pin_index is the bcm pin number +/// IN: pull is taken from PinPull, such as up, down, or none +/// +pub fn pinSetPull(pin_index: u6, pull: PinPull) void { + mmio.write(arch.mmio_addr, .GPIO_PULL, @enumToInt(pull)); + delay(150); + mmio.writeClock(arch.mmio_addr, .GPIO_PULL_CLK0, @as(u1, 1), pin_index); + delay(150); +} + +/// Set both the pin function and pull up +/// +/// IN: pin_index is the bcm pin number +/// IN: pull is taken from PinPull, such as up, down, or none +/// IN: function is the PinFunction, such as input, output, or internally routed (alternate) +/// +pub fn pinSetPullUpAndFunction(pin_index: u6, pull: PinPull, function: PinFunction) void { + pinSetPull(pin_index, pull); + pinSetFunction(pin_index, function); +} + +/// Write 0 or 1 to the selectged pin +/// +/// IN: pin_index is the bcm pin number +/// IN: zero_or_one is the value written to the pin (0 or 1) +/// +fn pinWrite(pin_index: u6, zero_or_one: u1) void { + mmio.writeClock(arch.mmio_addr, if (zero_or_one == 0) mmio.Register.GPIO_CLR0 else .GPIO_SET0, @as(u1, 1), pin_index); +} + +/// Available pin functions +const PinFunction = enum(u3) { + /// The pin functions as an input + Input = 0, + /// The pin functions as an output + Output = 1, + /// The pin functions as an output using internal routing alternatice 0 + AlternateFunction0 = 4, + /// The pin functions as an output using internal routing alternatice 1 + AlternateFunction1 = 5, + /// The pin functions as an output using internal routing alternatice 2 + AlternateFunction2 = 6, + /// The pin functions as an output using internal routing alternatice 3 + AlternateFunction3 = 7, + /// The pin functions as an output using internal routing alternatice 4 + AlternateFunction4 = 3, + /// The pin functions as an output using internal routing alternatice 5 + AlternateFunction5 = 2, +}; + +/// Available pin pull/up down states +const PinPull = enum { + None, + Down, + Up, +}; + +/// Delay for a fixed amount of time +/// +/// IN: count in cpu cycles +/// +fn delay(count: usize) void { + var i: usize = 0; + while (i < count) : (i += 1) { + asm volatile ("mov x0, x0"); + } +}