debug.zig (5108B)
1 const pi = @import("pi"); 2 3 const interrupts = pi.interrupts; 4 const debug = pi.debug; 5 const uart = pi.devices.mini_uart; 6 const faults = pi.faults; 7 const switching = pi.switching; 8 9 var scheduler_registers: interrupts.Registers = undefined; 10 var last_registers: interrupts.Registers = undefined; 11 var count: usize = 0; 12 13 fn dump_registers(regs: *const interrupts.Registers) void { 14 uart.writer.print("Registers\n", .{}) catch {}; 15 for (regs.gp, 0..) |r, i| { 16 uart.writer.print(" r{d} = 0x{X}\n", .{ i, r }) catch {}; 17 } 18 uart.writer.print(" sp = 0x{X}\n lr = 0x{X}\n pc = 0x{X}\n", .{ regs.sp, regs.lr, regs.pc }) catch {}; 19 uart.writer.print(" psr = {s}\n", .{@tagName(regs.psr.mode)}) catch {}; 20 uart.flush(); 21 } 22 23 fn single_step_handler(regs: interrupts.Registers) void { 24 count += 1; 25 26 uart.writer.print("fault: 0x{X} @ 0x{X}: {d}\n", .{ pi.mem.get_u32(@ptrFromInt(regs.pc)), regs.pc, count }) catch {}; 27 dump_registers(®s); 28 uart.flush(); 29 30 debug.set_breakpoint(0, regs.pc, .IMVAMismatch); 31 uart.flush(); 32 33 // My guess why this is necessary is since regs lives in the stack 34 // That will cause an explosion when the stack is switched 35 last_registers = regs; 36 switching.restore_state_user(&last_registers); 37 } 38 39 fn watchpoint_handler(regs: interrupts.Registers) void { 40 count += 1; 41 42 uart.writer.print("fault: 0x{X} @ 0x{X}: {d}\n", .{ pi.mem.get_u32(@ptrFromInt(regs.pc)), regs.pc, count }) catch {}; 43 uart.writer.print(" -> accessed address 0x{X}\n", .{debug.WCR.get_address(0)}) catch {}; 44 uart.flush(); 45 46 last_registers = regs; 47 switching.restore_state(&last_registers); 48 } 49 50 fn syscall_handler(regs: interrupts.Registers) void { 51 uart.writer.print("got syscall at level {s} @ 0x{X}!\n", .{ @tagName(regs.psr.mode), regs.pc }) catch {}; 52 uart.writer.print(" -> restoring to 0x{X}\n", .{regs.pc}) catch {}; 53 54 const sys = regs.gp[0]; 55 const arg0 = regs.gp[1]; 56 57 switch (sys) { 58 1 => { 59 uart.write_slice("exiting process\n"); 60 uart.flush(); 61 62 switching.restore_state(&scheduler_registers); 63 }, 64 2 => { 65 uart.write_byte(@truncate(arg0)); 66 }, 67 else => { 68 uart.write_slice("illegal syscall\n"); 69 }, 70 } 71 72 switching.restore_state(®s); 73 } 74 75 fn count_registers() callconv(.naked) void { 76 asm volatile ( 77 \\ mov r0, 0 78 \\ mov r1, 1 79 \\ mov r2, 2 80 \\ mov r3, 3 81 \\ mov r4, 4 82 \\ mov r5, 5 83 \\ mov r6, 6 84 \\ mov r7, 7 85 \\ mov r8, 8 86 \\ mov r9, 9 87 \\ mov r10, 10 88 \\ mov r11, 11 89 \\ mov r12, 12 90 \\ mov r0, 1 91 \\ bx lr 92 ); 93 } 94 95 // do null pointer bs 96 fn watchpoint_test() callconv(.naked) void { 97 asm volatile ( 98 \\ mov r0, #8 99 \\ ldr r0, [r0] 100 \\ str r0, [r0] 101 \\ bx lr 102 ); 103 } 104 105 fn exit_trampoline() callconv(.naked) void { 106 asm volatile ( 107 \\ mov r1, r0 108 \\ mov r0, 1 109 \\ swi 1 110 ); 111 } 112 113 pub fn main() !void { 114 interrupts.disable_interrupts(); 115 uart.set_tx_interrupts(false); 116 117 const didr = debug.DIDR.get(); 118 try uart.writer.print("Debug ID\n", .{}); 119 try uart.writer.print(" revision = {d}\n variant = {d}\n debug revision = {d}\n debug version = {d}\n", .{ 120 didr.revision, 121 didr.variant, 122 didr.debug_revision, 123 didr.debug_version, 124 }); 125 try uart.writer.print(" breakpoints = {d}\n watchpoints = {d}\n", .{ didr.breakpoint_pairs + 1, didr.watchpoint_pairs + 1 }); 126 127 interrupts.set_exception_handler(.PrefetchAbort, single_step_handler); 128 interrupts.set_exception_handler(.DataAbort, watchpoint_handler); 129 interrupts.set_exception_handler(.SoftwareInterrupt, syscall_handler); 130 try uart.writer.print("Set exception handlers\n", .{}); 131 132 try uart.writer.print("Running as {s}\n", .{@tagName(pi.PSR.get_c().mode)}); 133 debug.enable_monitor_mode(); 134 try uart.writer.print("Enabled monitor mode\n", .{}); 135 136 debug.set_breakpoint(0, 0, .IMVAMismatch); 137 try uart.writer.print("Set breakpoint\n", .{}); 138 139 var psr = pi.PSR.get_c(); 140 psr.mode = .User; 141 142 var new_registers: interrupts.Registers = .{ 143 .gp = .{0} ** 13, 144 .pc = @intFromPtr(&count_registers), 145 .sp = 0, 146 .lr = @intFromPtr(&exit_trampoline), 147 .psr = psr, 148 }; 149 dump_registers(&new_registers); 150 151 try uart.writer.print("Testing stepping\n", .{}); 152 switching.switch_state(&scheduler_registers, &new_registers); 153 try uart.writer.print("Stepping success\n", .{}); 154 155 try uart.writer.print("Testing watchpoint\n", .{}); 156 new_registers = .{ 157 .gp = .{0} ** 13, 158 .pc = @intFromPtr(&watchpoint_test), 159 .sp = 0, 160 .lr = @intFromPtr(&exit_trampoline), 161 .psr = psr, 162 }; 163 debug.clear_breakpoint(0); 164 debug.set_watchpoint(0, 0x8, .Either); 165 count = 0; 166 switching.switch_state(&scheduler_registers, &new_registers); 167 try uart.writer.print("Watchpoint success\n", .{}); 168 169 debug.disable_monitor_mode(); 170 171 while (true) {} 172 }