feat: add hardware breakpoints

This commit is contained in:
Rekai Nyangadzayi Musuka 2023-01-26 20:44:55 -06:00
parent 4bca44e5f2
commit 59b6b51466
3 changed files with 124 additions and 8 deletions

View File

@ -2,6 +2,7 @@ const std = @import("std");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Emulator = @import("lib.zig").Emulator; const Emulator = @import("lib.zig").Emulator;
const State = @import("State.zig");
const target = @import("Server.zig").target; const target = @import("Server.zig").target;
const memory_map = @import("Server.zig").memory_map; const memory_map = @import("Server.zig").memory_map;
@ -45,7 +46,7 @@ const String = union(enum) {
} }
}; };
pub fn parse(self: *Self, allocator: Allocator, emu: Emulator) !String { pub fn parse(self: *Self, allocator: Allocator, state: *State, emu: Emulator) !String {
switch (self.contents[0]) { switch (self.contents[0]) {
// Required // Required
'?' => { '?' => {
@ -87,9 +88,6 @@ pub fn parse(self: *Self, allocator: Allocator, emu: Emulator) !String {
{ {
var i: u32 = 0; var i: u32 = 0;
while (i < len) : (i += 1) { while (i < len) : (i += 1) {
const value = emu.read(addr + i);
log.debug("read 0x{X:0>2} from 0x{X:0>8}", .{ value, addr + i });
// writes the formatted integer to the buffer, returns a slice to the buffer but we ignore that // writes the formatted integer to the buffer, returns a slice to the buffer but we ignore that
_ = std.fmt.bufPrintIntToSlice(ret[i * 2 ..][0..2], emu.read(addr + i), 16, .lower, .{ .fill = '0', .width = 2 }); _ = std.fmt.bufPrintIntToSlice(ret[i * 2 ..][0..2], emu.read(addr + i), 16, .lower, .{ .fill = '0', .width = 2 });
} }
@ -98,15 +96,63 @@ pub fn parse(self: *Self, allocator: Allocator, emu: Emulator) !String {
return .{ .alloc = ret }; return .{ .alloc = ret };
}, },
'M' => @panic("TODO: Memory Write"), 'M' => @panic("TODO: Memory Write"),
'c' => @panic("TODO: Continue"), 'c' => {
while (true) {
emu.step();
const r = emu.registers();
const is_thumb = emu.cpsr() >> 5 & 1 == 1;
const r15 = r[15] -| if (is_thumb) @as(u32, 4) else 8;
if (state.hw_bkpt.isHit(r15)) {
return .{ .static = "T05 hwbreak;" };
}
}
},
's' => { 's' => {
// var tokens = std.mem.tokenize(u8, self.contents[1..], " "); // var tokens = std.mem.tokenize(u8, self.contents[1..], " ");
// const addr = if (tokens.next()) |s| try std.fmt.parseInt(u32, s, 16) else null; // const addr = if (tokens.next()) |s| try std.fmt.parseInt(u32, s, 16) else null;
emu.step(); emu.step();
return .{ .static = "S05" }; // Signal.Trap
},
const ret = try std.fmt.allocPrint(allocator, "S{x:0>2}", .{@enumToInt(Signal.Trap)}); // Breakpoints
return .{ .alloc = ret }; 'z' => switch (self.contents[1]) {
'0' => @panic("TODO: Remove Software Breakpoint"),
'1' => {
var tokens = std.mem.tokenize(u8, self.contents[2..], ",");
const addr_str = tokens.next() orelse return .{ .static = "E9999" };
// const kind_str = tokens.next() orelse return .{ .static = "E9999" };
const addr = try std.fmt.parseInt(u32, addr_str, 16);
// const kind = try std.fmt.parseInt(u32, kind_str, 16);
state.hw_bkpt.remove(addr);
return .{ .static = "OK" };
},
'2' => @panic("TODO: Remove Write Watchpoint"),
'3' => @panic("TODO: Remove Read Watchpoint"),
'4' => @panic("TODO: Remove Access Watchpoint"),
else => return .{ .static = "" },
},
'Z' => switch (self.contents[1]) {
'0' => @panic("TODO: Insert Software Breakpoint"),
'1' => {
var tokens = std.mem.tokenize(u8, self.contents[2..], ",");
const addr_str = tokens.next() orelse return .{ .static = "E9999" };
const kind_str = tokens.next() orelse return .{ .static = "E9999" };
const addr = try std.fmt.parseInt(u32, addr_str, 16);
const kind = try std.fmt.parseInt(u32, kind_str, 16);
try state.hw_bkpt.add(addr, kind);
return .{ .static = "OK" };
},
'2' => @panic("TODO: Insert Write Watchpoint"),
'3' => @panic("TODO: Insert Read Watchpoint"),
'4' => @panic("TODO: Insert Access Watchpoint"),
else => return .{ .static = "" },
}, },
// Optional // Optional

View File

@ -1,6 +1,7 @@
const std = @import("std"); const std = @import("std");
const network = @import("network"); const network = @import("network");
const Packet = @import("Packet.zig"); const Packet = @import("Packet.zig");
const State = @import("State.zig");
const Socket = network.Socket; const Socket = network.Socket;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
@ -59,6 +60,7 @@ pkt_cache: ?[]const u8 = null,
client: Socket, client: Socket,
_socket: Socket, _socket: Socket,
state: State = .{},
emu: Emulator, emu: Emulator,
pub fn init(emulator: Emulator) !Self { pub fn init(emulator: Emulator) !Self {
@ -129,7 +131,7 @@ fn handlePacket(self: *Self, allocator: Allocator, input: []const u8) !Action {
var packet = Packet.from(allocator, input) catch return .nack; var packet = Packet.from(allocator, input) catch return .nack;
defer packet.deinit(allocator); defer packet.deinit(allocator);
var string = packet.parse(allocator, self.emu) catch return .nack; var string = packet.parse(allocator, &self.state, self.emu) catch return .nack;
defer string.deinit(allocator); defer string.deinit(allocator);
const reply = string.inner(); const reply = string.inner();

68
src/State.zig Normal file
View File

@ -0,0 +1,68 @@
const std = @import("std");
hw_bkpt: HwBkpt = .{},
const HwBkpt = struct {
const log = std.log.scoped(.HwBkpt);
list: [2]?Bkpt = .{ null, null },
pub fn isHit(self: *const @This(), addr: u32) bool {
for (self.list) |bkpt_opt| {
if (bkpt_opt == null) continue;
const bkpt = bkpt_opt.?;
if (bkpt.addr == addr) return true;
}
return false;
}
pub fn add(self: *@This(), addr: u32, kind: u32) !void {
for (self.list) |*bkpt_opt| {
if (bkpt_opt.* != null) {
const bkpt = bkpt_opt.*.?;
if (bkpt.addr == addr) return; // makes this fn indempotent
continue;
}
bkpt_opt.* = .{ .addr = addr, .kind = try Bkpt.Kind.from(u32, kind) };
log.debug("Added Breakpoint at 0x{X:0>8}", .{addr});
return;
}
return error.OutOfSpace;
}
pub fn remove(self: *@This(), addr: u32) void {
for (self.list) |*bkpt_opt| {
if (bkpt_opt.* == null) continue;
const bkpt = bkpt_opt.*.?; // FIXME: bkpt_opt.?.addr works though?
log.debug("Removed Breakpoint at 0x{X:0>8}", .{addr});
if (bkpt.addr == addr) bkpt_opt.* = null;
}
}
};
const Bkpt = struct {
addr: u32,
kind: Kind,
const Kind = enum(u3) {
Arm = 2,
Thumb = 4,
pub fn from(comptime T: type, num: T) !@This() {
comptime std.debug.assert(@typeInfo(T) == .Int);
return switch (num) {
2 => .Arm,
4 => .Thumb,
else => error.UnknownBkptKind,
};
}
};
};