feat: implement software interrupts
This commit is contained in:
		@@ -49,10 +49,7 @@ const String = union(enum) {
 | 
			
		||||
pub fn parse(self: *Self, allocator: Allocator, emu: *Emulator) !String {
 | 
			
		||||
    switch (self.contents[0]) {
 | 
			
		||||
        // Required
 | 
			
		||||
        '?' => {
 | 
			
		||||
            const ret = try std.fmt.allocPrint(allocator, "S{x:0>2}", .{@enumToInt(Signal.Int)});
 | 
			
		||||
            return .{ .alloc = ret };
 | 
			
		||||
        },
 | 
			
		||||
        '?' => return .{ .static = "T05" }, // FIXME: which errno?
 | 
			
		||||
        'g' => {
 | 
			
		||||
            const r = emu.registers();
 | 
			
		||||
            const cpsr = emu.cpsr();
 | 
			
		||||
@@ -140,45 +137,55 @@ pub fn parse(self: *Self, allocator: Allocator, emu: *Emulator) !String {
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        // Breakpoints
 | 
			
		||||
        'z' => switch (self.contents[1]) {
 | 
			
		||||
            '0' => return .{ .static = "" }, //TODO: Remove Software Breakpoint
 | 
			
		||||
            '1' => {
 | 
			
		||||
                var tokens = std.mem.tokenize(u8, self.contents[2..], ",");
 | 
			
		||||
        'z' => {
 | 
			
		||||
            var tokens = std.mem.tokenize(u8, self.contents[2..], ",");
 | 
			
		||||
 | 
			
		||||
                const addr_str = tokens.next() orelse return error.InvalidPacket;
 | 
			
		||||
                const addr = try std.fmt.parseInt(u32, addr_str, 16);
 | 
			
		||||
            const addr_str = tokens.next() orelse return error.InvalidPacket;
 | 
			
		||||
            const addr = try std.fmt.parseInt(u32, addr_str, 16);
 | 
			
		||||
 | 
			
		||||
                emu.state.hw_bkpt.remove(addr);
 | 
			
		||||
                return .{ .static = "OK" };
 | 
			
		||||
            },
 | 
			
		||||
            '2' => return .{ .static = "" }, // TODO: Remove Write Watchpoint
 | 
			
		||||
            '3' => return .{ .static = "" }, // TODO: Remove Read Watchpoint
 | 
			
		||||
            '4' => return .{ .static = "" }, // TODO: Remove Access Watchpoint
 | 
			
		||||
            else => return .{ .static = "" },
 | 
			
		||||
            switch (self.contents[1]) {
 | 
			
		||||
                '0' => {
 | 
			
		||||
                    emu.removeBkpt(.Software, addr);
 | 
			
		||||
                    return .{ .static = "OK" };
 | 
			
		||||
                },
 | 
			
		||||
                '1' => {
 | 
			
		||||
                    emu.removeBkpt(.Hardware, addr);
 | 
			
		||||
                    return .{ .static = "OK" };
 | 
			
		||||
                },
 | 
			
		||||
                '2' => return .{ .static = "" }, // TODO: Remove Write Watchpoint
 | 
			
		||||
                '3' => return .{ .static = "" }, // TODO: Remove Read Watchpoint
 | 
			
		||||
                '4' => return .{ .static = "" }, // TODO: Remove Access Watchpoint
 | 
			
		||||
                else => return .{ .static = "" },
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        'Z' => switch (self.contents[1]) {
 | 
			
		||||
            '0' => return .{ .static = "" }, //TODO: Insert Software Breakpoint
 | 
			
		||||
            '1' => {
 | 
			
		||||
                var tokens = std.mem.tokenize(u8, self.contents[2..], ",");
 | 
			
		||||
                const addr_str = tokens.next() orelse return error.InvalidPacket;
 | 
			
		||||
                const kind_str = tokens.next() orelse return error.InvalidPacket;
 | 
			
		||||
        'Z' => {
 | 
			
		||||
            var tokens = std.mem.tokenize(u8, self.contents[2..], ",");
 | 
			
		||||
            const addr_str = tokens.next() orelse return error.InvalidPacket;
 | 
			
		||||
            const kind_str = tokens.next() orelse return error.InvalidPacket;
 | 
			
		||||
 | 
			
		||||
                const addr = try std.fmt.parseInt(u32, addr_str, 16);
 | 
			
		||||
                const kind = try std.fmt.parseInt(u32, kind_str, 16);
 | 
			
		||||
            const addr = try std.fmt.parseInt(u32, addr_str, 16);
 | 
			
		||||
            const kind = try std.fmt.parseInt(u32, kind_str, 16);
 | 
			
		||||
 | 
			
		||||
                emu.state.hw_bkpt.add(addr, kind) catch |e| {
 | 
			
		||||
                    switch (e) {
 | 
			
		||||
                        error.OutOfSpace => return .{ .static = "E22" }, // FIXME: Which errno?
 | 
			
		||||
                        else => return e,
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
            switch (self.contents[1]) {
 | 
			
		||||
                '0' => {
 | 
			
		||||
                    try emu.addBkpt(.Software, addr, kind);
 | 
			
		||||
                    return .{ .static = "OK" };
 | 
			
		||||
                },
 | 
			
		||||
                '1' => {
 | 
			
		||||
                    emu.addBkpt(.Hardware, addr, kind) catch |e| {
 | 
			
		||||
                        switch (e) {
 | 
			
		||||
                            error.OutOfSpace => return .{ .static = "E22" }, // FIXME: which errno?
 | 
			
		||||
                            else => return e,
 | 
			
		||||
                        }
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                return .{ .static = "OK" };
 | 
			
		||||
            },
 | 
			
		||||
            '2' => return .{ .static = "" }, // TODO: Insert Write Watchpoint
 | 
			
		||||
            '3' => return .{ .static = "" }, // TODO: Insert Read Watchpoint
 | 
			
		||||
            '4' => return .{ .static = "" }, // TODO: Insert Access Watchpoint
 | 
			
		||||
            else => return .{ .static = "" },
 | 
			
		||||
                    return .{ .static = "OK" };
 | 
			
		||||
                },
 | 
			
		||||
                '2' => return .{ .static = "" }, // TODO: Insert Write Watchpoint
 | 
			
		||||
                '3' => return .{ .static = "" }, // TODO: Insert Read Watchpoint
 | 
			
		||||
                '4' => return .{ .static = "" }, // TODO: Insert Access Watchpoint
 | 
			
		||||
                else => return .{ .static = "" },
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        // TODO: Figure out the difference between 'M' and 'X'
 | 
			
		||||
@@ -202,7 +209,7 @@ pub fn parse(self: *Self, allocator: Allocator, emu: *Emulator) !String {
 | 
			
		||||
            if (substr(self.contents[1..], "Attached")) return .{ .static = "1" }; // Tell GDB we're attached to a process
 | 
			
		||||
 | 
			
		||||
            if (substr(self.contents[1..], "Supported")) {
 | 
			
		||||
                const format = "PacketSize={x:};qXfer:features:read+;qXfer:memory-map:read+";
 | 
			
		||||
                const format = "PacketSize={x:};swbreak+;hwbreak+;qXfer:features:read+;qXfer:memory-map:read+";
 | 
			
		||||
                // TODO: Anything else?
 | 
			
		||||
 | 
			
		||||
                const ret = try std.fmt.allocPrint(allocator, format, .{Self.max_len});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,62 @@
 | 
			
		||||
const std = @import("std");
 | 
			
		||||
 | 
			
		||||
const Allocator = std.mem.Allocator;
 | 
			
		||||
const ArrayList = std.ArrayList;
 | 
			
		||||
 | 
			
		||||
hw_bkpt: HwBkpt = .{},
 | 
			
		||||
sw_bkpt: SwBkpt,
 | 
			
		||||
 | 
			
		||||
pub fn init(allocator: Allocator) @This() {
 | 
			
		||||
    return .{ .sw_bkpt = SwBkpt.init(allocator) };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn deinit(self: *@This()) void {
 | 
			
		||||
    self.sw_bkpt.deinit();
 | 
			
		||||
    self.* = undefined;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const SwBkpt = struct {
 | 
			
		||||
    const log = std.log.scoped(.SwBkpt);
 | 
			
		||||
 | 
			
		||||
    list: std.ArrayList(Bkpt),
 | 
			
		||||
 | 
			
		||||
    pub fn init(allocator: Allocator) @This() {
 | 
			
		||||
        return .{ .list = ArrayList(Bkpt).init(allocator) };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn deinit(self: *@This()) void {
 | 
			
		||||
        self.deinit();
 | 
			
		||||
        self.* = undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn isHit(self: *const @This(), addr: u32) bool {
 | 
			
		||||
        for (self.list.items) |bkpt| {
 | 
			
		||||
            if (bkpt.addr == addr) return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn add(self: *@This(), addr: u32, kind: u32) !void {
 | 
			
		||||
        for (self.list.items) |bkpt| {
 | 
			
		||||
            if (bkpt.addr == addr) return; // indempotent
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try self.list.append(.{ .addr = addr, .kind = try Bkpt.Kind.from(u32, kind) });
 | 
			
		||||
        log.warn("Added Breakpoint at 0x{X:0>8}", .{addr});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn remove(self: *@This(), addr: u32) void {
 | 
			
		||||
        for (self.list.items) |bkpt, i| {
 | 
			
		||||
            if (bkpt.addr == addr) {
 | 
			
		||||
                _ = self.list.orderedRemove(i);
 | 
			
		||||
                log.debug("Removed Breakpoint at 0x{X:0>8}", .{addr});
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const HwBkpt = struct {
 | 
			
		||||
    const log = std.log.scoped(.HwBkpt);
 | 
			
		||||
@@ -9,9 +65,7 @@ const HwBkpt = struct {
 | 
			
		||||
 | 
			
		||||
    pub fn isHit(self: *const @This(), addr: u32) bool {
 | 
			
		||||
        for (self.list) |bkpt_opt| {
 | 
			
		||||
            if (bkpt_opt == null) continue;
 | 
			
		||||
            const bkpt = bkpt_opt.?;
 | 
			
		||||
 | 
			
		||||
            const bkpt = bkpt_opt orelse continue;
 | 
			
		||||
            if (bkpt.addr == addr) return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -20,17 +74,14 @@ const HwBkpt = struct {
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
            if (bkpt_opt.*) |bkpt| {
 | 
			
		||||
                if (bkpt.addr == addr) return; // idempotent
 | 
			
		||||
            } else {
 | 
			
		||||
                bkpt_opt.* = .{ .addr = addr, .kind = try Bkpt.Kind.from(u32, kind) };
 | 
			
		||||
                log.debug("Added Breakpoint at 0x{X:0>8}", .{addr});
 | 
			
		||||
 | 
			
		||||
                continue;
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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;
 | 
			
		||||
@@ -38,11 +89,14 @@ const HwBkpt = struct {
 | 
			
		||||
 | 
			
		||||
    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?
 | 
			
		||||
            const bkpt = bkpt_opt.* orelse continue;
 | 
			
		||||
 | 
			
		||||
            log.debug("Removed Breakpoint at 0x{X:0>8}", .{addr});
 | 
			
		||||
            if (bkpt.addr == addr) bkpt_opt.* = null;
 | 
			
		||||
            if (bkpt.addr == addr) {
 | 
			
		||||
                bkpt_opt.* = null;
 | 
			
		||||
                log.debug("Removed Breakpoint at 0x{X:0>8}", .{addr});
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										41
									
								
								src/lib.zig
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								src/lib.zig
									
									
									
									
									
								
							@@ -1,3 +1,6 @@
 | 
			
		||||
const std = @import("std");
 | 
			
		||||
const Allocator = std.mem.Allocator;
 | 
			
		||||
 | 
			
		||||
/// Re-export of the server interface
 | 
			
		||||
pub const Server = @import("Server.zig");
 | 
			
		||||
const State = @import("State.zig");
 | 
			
		||||
@@ -13,7 +16,7 @@ pub const Emulator = struct {
 | 
			
		||||
        SingleStep: void,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    state: State = .{},
 | 
			
		||||
    state: State,
 | 
			
		||||
 | 
			
		||||
    ptr: *anyopaque,
 | 
			
		||||
 | 
			
		||||
@@ -25,7 +28,7 @@ pub const Emulator = struct {
 | 
			
		||||
 | 
			
		||||
    stepFn: *const fn (*anyopaque) void,
 | 
			
		||||
 | 
			
		||||
    pub fn init(ptr: anytype) Self {
 | 
			
		||||
    pub fn init(allocator: Allocator, ptr: anytype) Self {
 | 
			
		||||
        const Ptr = @TypeOf(ptr);
 | 
			
		||||
        const ptr_info = @typeInfo(Ptr);
 | 
			
		||||
 | 
			
		||||
@@ -66,7 +69,21 @@ pub const Emulator = struct {
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return .{ .ptr = ptr, .readFn = gen.readImpl, .writeFn = gen.writeImpl, .registersFn = gen.registersImpl, .cpsrFn = gen.cpsrImpl, .stepFn = gen.stepImpl };
 | 
			
		||||
        return .{
 | 
			
		||||
            .ptr = ptr,
 | 
			
		||||
            .readFn = gen.readImpl,
 | 
			
		||||
            .writeFn = gen.writeImpl,
 | 
			
		||||
            .registersFn = gen.registersImpl,
 | 
			
		||||
            .cpsrFn = gen.cpsrImpl,
 | 
			
		||||
            .stepFn = gen.stepImpl,
 | 
			
		||||
 | 
			
		||||
            .state = State.init(allocator),
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn deinit(self: *Self) void {
 | 
			
		||||
        self.state.deinit();
 | 
			
		||||
        self.* = undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub inline fn read(self: Self, addr: u32) u8 {
 | 
			
		||||
@@ -104,8 +121,26 @@ pub const Emulator = struct {
 | 
			
		||||
 | 
			
		||||
        const r15 = r[15] -| if (is_thumb) @as(u32, 4) else 8;
 | 
			
		||||
 | 
			
		||||
        if (self.state.sw_bkpt.isHit(r15)) return .{ .Trap = .SwBkpt };
 | 
			
		||||
        if (self.state.hw_bkpt.isHit(r15)) return .{ .Trap = .HwBkpt };
 | 
			
		||||
 | 
			
		||||
        return .SingleStep;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const BkptType = enum { Hardware, Software };
 | 
			
		||||
 | 
			
		||||
    // TODO: Consider properly implementing Software interrupts?
 | 
			
		||||
    pub fn addBkpt(self: *Self, comptime @"type": BkptType, addr: u32, kind: u32) !void {
 | 
			
		||||
        switch (@"type") {
 | 
			
		||||
            .Hardware => try self.state.hw_bkpt.add(addr, kind),
 | 
			
		||||
            .Software => try self.state.sw_bkpt.add(addr, kind),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn removeBkpt(self: *Self, comptime @"type": BkptType, addr: u32) void {
 | 
			
		||||
        switch (@"type") {
 | 
			
		||||
            .Hardware => self.state.hw_bkpt.remove(addr),
 | 
			
		||||
            .Software => self.state.sw_bkpt.remove(addr),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user