sylveos

Toy Operating System
Log | Files | Refs

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(&regs);
     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(&regs);
     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 }