chore: move structs to separate files
This commit is contained in:
		
							
								
								
									
										166
									
								
								src/Packet.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								src/Packet.zig
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,166 @@
 | 
				
			|||||||
 | 
					const std = @import("std");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const target = @import("Server.zig").target;
 | 
				
			||||||
 | 
					const Allocator = std.mem.Allocator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Self = @This();
 | 
				
			||||||
 | 
					const log = std.log.scoped(.Packet);
 | 
				
			||||||
 | 
					pub const max_len: usize = 0x1000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					contents: []const u8,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn from(allocator: Allocator, str: []const u8) !Self {
 | 
				
			||||||
 | 
					    var tokens = std.mem.tokenize(u8, str, "$#");
 | 
				
			||||||
 | 
					    const contents = tokens.next() orelse return error.InvalidPacket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const chksum_str = tokens.next() orelse return error.MissingCheckSum;
 | 
				
			||||||
 | 
					    const chksum = std.fmt.parseInt(u8, chksum_str, 16) catch return error.InvalidChecksum;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // log.info("Contents: {s}", .{contents});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!Self.verify(contents, chksum)) return error.ChecksumMismatch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return .{ .contents = try allocator.dupe(u8, contents) };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const String = union(enum) {
 | 
				
			||||||
 | 
					    alloc: []const u8,
 | 
				
			||||||
 | 
					    static: []const u8,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn inner(self: *const @This()) []const u8 {
 | 
				
			||||||
 | 
					        return switch (self.*) {
 | 
				
			||||||
 | 
					            .static => |str| str,
 | 
				
			||||||
 | 
					            .alloc => |str| str,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn deinit(self: *@This(), allocator: Allocator) void {
 | 
				
			||||||
 | 
					        switch (self.*) {
 | 
				
			||||||
 | 
					            .alloc => |string| allocator.free(string),
 | 
				
			||||||
 | 
					            .static => {},
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.* = undefined;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn parse(self: *Self, allocator: Allocator) !String {
 | 
				
			||||||
 | 
					    switch (self.contents[0]) {
 | 
				
			||||||
 | 
					        // Required
 | 
				
			||||||
 | 
					        '?' => {
 | 
				
			||||||
 | 
					            const ret: Signal = .Trap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Deallocated by the caller
 | 
				
			||||||
 | 
					            return .{ .alloc = try std.fmt.allocPrint(allocator, "T{x:0>2}thread:1;", .{@enumToInt(ret)}) };
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'g', 'G' => @panic("TODO: Register Access"),
 | 
				
			||||||
 | 
					        'm', 'M' => @panic("TODO: Memory Access"),
 | 
				
			||||||
 | 
					        'c' => @panic("TODO: Continue"),
 | 
				
			||||||
 | 
					        's' => @panic("TODO: Step"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Optional
 | 
				
			||||||
 | 
					        'H' => {
 | 
				
			||||||
 | 
					            log.warn("{s}", .{self.contents});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            switch (self.contents[1]) {
 | 
				
			||||||
 | 
					                'g', 'c' => return .{ .static = "OK" },
 | 
				
			||||||
 | 
					                else => {
 | 
				
			||||||
 | 
					                    log.warn("Unimplemented: {s}", .{self.contents});
 | 
				
			||||||
 | 
					                    return .{ .static = "" };
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'v' => {
 | 
				
			||||||
 | 
					            if (substr(self.contents[1..], "MustReplyEmpty")) {
 | 
				
			||||||
 | 
					                return .{ .static = "" };
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            log.warn("Unimplemented: {s}", .{self.contents});
 | 
				
			||||||
 | 
					            return .{ .static = "" };
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'q' => {
 | 
				
			||||||
 | 
					            if (self.contents[1] == 'C' and self.contents.len == 2) return .{ .static = "QC1" };
 | 
				
			||||||
 | 
					            if (substr(self.contents[1..], "fThreadInfo")) return .{ .static = "m1" };
 | 
				
			||||||
 | 
					            if (substr(self.contents[1..], "sThreadInfo")) return .{ .static = "l" };
 | 
				
			||||||
 | 
					            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+";
 | 
				
			||||||
 | 
					                // TODO: Anything else?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                const ret = try std.fmt.allocPrint(allocator, format, .{Self.max_len});
 | 
				
			||||||
 | 
					                return .{ .alloc = ret };
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (substr(self.contents[1..], "Xfer:features:read")) {
 | 
				
			||||||
 | 
					                var tokens = std.mem.tokenize(u8, self.contents[1..], ":,");
 | 
				
			||||||
 | 
					                _ = tokens.next(); // qXfer
 | 
				
			||||||
 | 
					                _ = tokens.next(); // features
 | 
				
			||||||
 | 
					                _ = tokens.next(); // read
 | 
				
			||||||
 | 
					                const annex = tokens.next() orelse return .{ .static = "E00" };
 | 
				
			||||||
 | 
					                const offset_str = tokens.next() orelse return .{ .static = "E00" };
 | 
				
			||||||
 | 
					                const length_str = tokens.next() orelse return .{ .static = "E00" };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (std.mem.eql(u8, annex, "target.xml")) {
 | 
				
			||||||
 | 
					                    log.info("Providing ARMv4T target description", .{});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    const offset = try std.fmt.parseInt(usize, offset_str, 16);
 | 
				
			||||||
 | 
					                    const length = try std.fmt.parseInt(usize, length_str, 16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // + 2 to account for the "m " in the response
 | 
				
			||||||
 | 
					                    // subtract offset so that the allocated buffer isn't
 | 
				
			||||||
 | 
					                    // larger than it needs to be TODO: Test this?
 | 
				
			||||||
 | 
					                    const len = @min(length, (target.len + 1) - offset);
 | 
				
			||||||
 | 
					                    const ret = try allocator.alloc(u8, len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    ret[0] = if (ret.len < length) 'l' else 'm';
 | 
				
			||||||
 | 
					                    std.mem.copy(u8, ret[1..], target[offset..]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return .{ .alloc = ret };
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    log.err("Unexpected Annex: {s}", .{annex});
 | 
				
			||||||
 | 
					                    return .{ .static = "E00" };
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return .{ .static = "" };
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            log.warn("Unimplemented: {s}", .{self.contents});
 | 
				
			||||||
 | 
					            return .{ .static = "" };
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        else => {
 | 
				
			||||||
 | 
					            log.warn("Unknown:  {s}", .{self.contents});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return .{ .static = "" };
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn substr(haystack: []const u8, needle: []const u8) bool {
 | 
				
			||||||
 | 
					    return std.mem.indexOf(u8, haystack, needle) != null;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn deinit(self: *Self, allocator: Allocator) void {
 | 
				
			||||||
 | 
					    allocator.free(self.contents);
 | 
				
			||||||
 | 
					    self.* = undefined;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn checksum(input: []const u8) u8 {
 | 
				
			||||||
 | 
					    var sum: usize = 0;
 | 
				
			||||||
 | 
					    for (input) |char| sum += char;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return @truncate(u8, sum);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn verify(input: []const u8, chksum: u8) bool {
 | 
				
			||||||
 | 
					    return Self.checksum(input) == chksum;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Signal = enum(u32) {
 | 
				
			||||||
 | 
					    Hup, // Hangup
 | 
				
			||||||
 | 
					    Int, // Interrupt
 | 
				
			||||||
 | 
					    Quit, // Quit
 | 
				
			||||||
 | 
					    Ill, // Illegal Instruction
 | 
				
			||||||
 | 
					    Trap, // Trace/Breakponit trap
 | 
				
			||||||
 | 
					    Abrt, // Aborted
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										141
									
								
								src/Server.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								src/Server.zig
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,141 @@
 | 
				
			|||||||
 | 
					const std = @import("std");
 | 
				
			||||||
 | 
					const network = @import("network");
 | 
				
			||||||
 | 
					const Packet = @import("Packet.zig");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Socket = network.Socket;
 | 
				
			||||||
 | 
					const Allocator = std.mem.Allocator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Self = @This();
 | 
				
			||||||
 | 
					const log = std.log.scoped(.Server);
 | 
				
			||||||
 | 
					const port: u16 = 2424;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub const target: []const u8 =
 | 
				
			||||||
 | 
					    \\<target version="1.0">
 | 
				
			||||||
 | 
					    \\    <architecture>armv4t</architecture>
 | 
				
			||||||
 | 
					    \\    <feature name="org.gnu.gdb.arm.core">
 | 
				
			||||||
 | 
					    \\        <reg name="r0" bitsize="32" type="uint32"/>
 | 
				
			||||||
 | 
					    \\        <reg name="r1" bitsize="32" type="uint32"/>
 | 
				
			||||||
 | 
					    \\        <reg name="r2" bitsize="32" type="uint32"/>
 | 
				
			||||||
 | 
					    \\        <reg name="r3" bitsize="32" type="uint32"/>
 | 
				
			||||||
 | 
					    \\        <reg name="r4" bitsize="32" type="uint32"/>
 | 
				
			||||||
 | 
					    \\        <reg name="r5" bitsize="32" type="uint32"/>
 | 
				
			||||||
 | 
					    \\        <reg name="r6" bitsize="32" type="uint32"/>
 | 
				
			||||||
 | 
					    \\        <reg name="r7" bitsize="32" type="uint32"/>
 | 
				
			||||||
 | 
					    \\        <reg name="r8" bitsize="32" type="uint32"/>
 | 
				
			||||||
 | 
					    \\        <reg name="r9" bitsize="32" type="uint32"/>
 | 
				
			||||||
 | 
					    \\        <reg name="r10" bitsize="32" type="uint32"/>
 | 
				
			||||||
 | 
					    \\        <reg name="r11" bitsize="32" type="uint32"/>
 | 
				
			||||||
 | 
					    \\        <reg name="r12" bitsize="32" type="uint32"/>
 | 
				
			||||||
 | 
					    \\        <reg name="sp" bitsize="32" type="data_ptr"/>
 | 
				
			||||||
 | 
					    \\        <reg name="lr" bitsize="32"/>
 | 
				
			||||||
 | 
					    \\        <reg name="pc" bitsize="32" type="code_ptr"/>
 | 
				
			||||||
 | 
					    \\
 | 
				
			||||||
 | 
					    \\        <reg name="cpsr" bitsize="32" regnum="25"/>
 | 
				
			||||||
 | 
					    \\    </feature>
 | 
				
			||||||
 | 
					    \\</target>
 | 
				
			||||||
 | 
					;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FIXME: Shouldn't this be a Packet Struct?
 | 
				
			||||||
 | 
					pkt_cache: ?[]const u8 = null,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					client: Socket,
 | 
				
			||||||
 | 
					_socket: Socket,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn init() !Self {
 | 
				
			||||||
 | 
					    try network.init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var socket = try Socket.create(.ipv4, .tcp);
 | 
				
			||||||
 | 
					    try socket.bindToPort(port);
 | 
				
			||||||
 | 
					    try socket.listen();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var client = try socket.accept(); // TODO: This blocks, is this OK?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const endpoint = try client.getLocalEndPoint();
 | 
				
			||||||
 | 
					    log.info("client connected from {}", .{endpoint});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return .{ ._socket = socket, .client = client };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn deinit(self: *Self, allocator: Allocator) void {
 | 
				
			||||||
 | 
					    self.reset(allocator);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    self.client.close();
 | 
				
			||||||
 | 
					    self._socket.close();
 | 
				
			||||||
 | 
					    network.deinit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    self.* = undefined;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Action = union(enum) {
 | 
				
			||||||
 | 
					    nothing,
 | 
				
			||||||
 | 
					    send: []const u8,
 | 
				
			||||||
 | 
					    retry,
 | 
				
			||||||
 | 
					    ack,
 | 
				
			||||||
 | 
					    nack,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn run(self: *Self, allocator: Allocator) !void {
 | 
				
			||||||
 | 
					    var buf: [Packet.max_len]u8 = undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (true) {
 | 
				
			||||||
 | 
					        const len = try self.client.receive(&buf);
 | 
				
			||||||
 | 
					        if (len == 0) break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const action = try Self.parse(allocator, buf[0..len]);
 | 
				
			||||||
 | 
					        try self.send(allocator, action);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn parse(allocator: Allocator, input: []const u8) !Action {
 | 
				
			||||||
 | 
					    return switch (input[0]) {
 | 
				
			||||||
 | 
					        '+' => .nothing,
 | 
				
			||||||
 | 
					        '-' => .retry,
 | 
				
			||||||
 | 
					        '$' => blk: {
 | 
				
			||||||
 | 
					            // Packet
 | 
				
			||||||
 | 
					            var packet = Packet.from(allocator, input) catch return .nack;
 | 
				
			||||||
 | 
					            defer packet.deinit(allocator);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var string = packet.parse(allocator) catch return .nack;
 | 
				
			||||||
 | 
					            defer string.deinit(allocator);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const reply = string.inner();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // deallocated by the caller
 | 
				
			||||||
 | 
					            const response = try std.fmt.allocPrint(allocator, "${s}#{x:0>2}", .{ reply, Packet.checksum(reply) });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            break :blk .{ .send = response };
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        else => std.debug.panic("Unknown: {s}", .{input}),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn send(self: *Self, allocator: Allocator, action: Action) !void {
 | 
				
			||||||
 | 
					    switch (action) {
 | 
				
			||||||
 | 
					        .send => |pkt| {
 | 
				
			||||||
 | 
					            _ = try self.client.send("+"); // ACK
 | 
				
			||||||
 | 
					            _ = try self.client.send(pkt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.reset(allocator);
 | 
				
			||||||
 | 
					            self.pkt_cache = pkt;
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        .retry => {
 | 
				
			||||||
 | 
					            log.warn("received nack, resending: \"{?s}\"", .{self.pkt_cache});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (self.pkt_cache) |pkt| _ = try self.client.send(pkt); // FIXME: is an ack to a nack necessary?
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        .ack => {
 | 
				
			||||||
 | 
					            _ = try self.client.send("+");
 | 
				
			||||||
 | 
					            self.reset(allocator);
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        .nack => {
 | 
				
			||||||
 | 
					            _ = try self.client.send("-");
 | 
				
			||||||
 | 
					            self.reset(allocator);
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        .nothing => self.reset(allocator),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn reset(self: *Self, allocator: Allocator) void {
 | 
				
			||||||
 | 
					    if (self.pkt_cache) |pkt| allocator.free(pkt);
 | 
				
			||||||
 | 
					    self.pkt_cache = null;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										278
									
								
								src/main.zig
									
									
									
									
									
								
							
							
						
						
									
										278
									
								
								src/main.zig
									
									
									
									
									
								
							@@ -1,10 +1,11 @@
 | 
				
			|||||||
const std = @import("std");
 | 
					const std = @import("std");
 | 
				
			||||||
const network = @import("network");
 | 
					const network = @import("network");
 | 
				
			||||||
 | 
					const Server = @import("Server.zig");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Allocator = std.mem.Allocator;
 | 
					const Allocator = std.mem.Allocator;
 | 
				
			||||||
const Socket = network.Socket;
 | 
					const Socket = network.Socket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const target: []const u8 =
 | 
					pub const target: []const u8 =
 | 
				
			||||||
    \\<target version="1.0">
 | 
					    \\<target version="1.0">
 | 
				
			||||||
    \\    <architecture>armv4t</architecture>
 | 
					    \\    <architecture>armv4t</architecture>
 | 
				
			||||||
    \\    <feature name="org.gnu.gdb.arm.core">
 | 
					    \\    <feature name="org.gnu.gdb.arm.core">
 | 
				
			||||||
@@ -45,278 +46,3 @@ pub fn main() !void {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    log.info("Client disconnected", .{});
 | 
					    log.info("Client disconnected", .{});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
const Server = struct {
 | 
					 | 
				
			||||||
    const Self = @This();
 | 
					 | 
				
			||||||
    const log = std.log.scoped(.Server);
 | 
					 | 
				
			||||||
    const port: u16 = 2424;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // FIXME: Shouldn't this be a Packet Struct?
 | 
					 | 
				
			||||||
    pkt_cache: ?[]const u8 = null,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    client: Socket,
 | 
					 | 
				
			||||||
    _socket: Socket,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn init() !Self {
 | 
					 | 
				
			||||||
        try network.init();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        var socket = try Socket.create(.ipv4, .tcp);
 | 
					 | 
				
			||||||
        try socket.bindToPort(port);
 | 
					 | 
				
			||||||
        try socket.listen();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        var client = try socket.accept(); // TODO: This blocks, is this OK?
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const endpoint = try client.getLocalEndPoint();
 | 
					 | 
				
			||||||
        log.info("client connected from {}", .{endpoint});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return .{ ._socket = socket, .client = client };
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn deinit(self: *Self, allocator: Allocator) void {
 | 
					 | 
				
			||||||
        self.reset(allocator);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.client.close();
 | 
					 | 
				
			||||||
        self._socket.close();
 | 
					 | 
				
			||||||
        network.deinit();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.* = undefined;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const Action = union(enum) {
 | 
					 | 
				
			||||||
        nothing,
 | 
					 | 
				
			||||||
        send: []const u8,
 | 
					 | 
				
			||||||
        retry,
 | 
					 | 
				
			||||||
        ack,
 | 
					 | 
				
			||||||
        nack,
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn run(self: *Self, allocator: Allocator) !void {
 | 
					 | 
				
			||||||
        var buf: [Packet.max_len]u8 = undefined;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        while (true) {
 | 
					 | 
				
			||||||
            const len = try self.client.receive(&buf);
 | 
					 | 
				
			||||||
            if (len == 0) break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            const action = try Self.parse(allocator, buf[0..len]);
 | 
					 | 
				
			||||||
            try self.send(allocator, action);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn parse(allocator: Allocator, input: []const u8) !Action {
 | 
					 | 
				
			||||||
        return switch (input[0]) {
 | 
					 | 
				
			||||||
            '+' => .nothing,
 | 
					 | 
				
			||||||
            '-' => .retry,
 | 
					 | 
				
			||||||
            '$' => blk: {
 | 
					 | 
				
			||||||
                // Packet
 | 
					 | 
				
			||||||
                var packet = Packet.from(allocator, input) catch return .nack;
 | 
					 | 
				
			||||||
                defer packet.deinit(allocator);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                var string = packet.parse(allocator) catch return .nack;
 | 
					 | 
				
			||||||
                defer string.deinit(allocator);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                const reply = string.inner();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // deallocated by the caller
 | 
					 | 
				
			||||||
                const response = try std.fmt.allocPrint(allocator, "${s}#{x:0>2}", .{ reply, Packet.checksum(reply) });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                break :blk .{ .send = response };
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            else => std.debug.panic("Unknown: {s}", .{input}),
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn send(self: *Self, allocator: Allocator, action: Action) !void {
 | 
					 | 
				
			||||||
        switch (action) {
 | 
					 | 
				
			||||||
            .send => |pkt| {
 | 
					 | 
				
			||||||
                _ = try self.client.send("+"); // ACK
 | 
					 | 
				
			||||||
                _ = try self.client.send(pkt);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                self.reset(allocator);
 | 
					 | 
				
			||||||
                self.pkt_cache = pkt;
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            .retry => {
 | 
					 | 
				
			||||||
                log.warn("received nack, resending: \"{?s}\"", .{self.pkt_cache});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (self.pkt_cache) |pkt| _ = try self.client.send(pkt); // FIXME: is an ack to a nack necessary?
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            .ack => {
 | 
					 | 
				
			||||||
                _ = try self.client.send("+");
 | 
					 | 
				
			||||||
                self.reset(allocator);
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            .nack => {
 | 
					 | 
				
			||||||
                _ = try self.client.send("-");
 | 
					 | 
				
			||||||
                self.reset(allocator);
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            .nothing => self.reset(allocator),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn reset(self: *Self, allocator: Allocator) void {
 | 
					 | 
				
			||||||
        if (self.pkt_cache) |pkt| allocator.free(pkt);
 | 
					 | 
				
			||||||
        self.pkt_cache = null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const Packet = struct {
 | 
					 | 
				
			||||||
    const Self = @This();
 | 
					 | 
				
			||||||
    const log = std.log.scoped(.Packet);
 | 
					 | 
				
			||||||
    const max_len: usize = 0x1000;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    contents: []const u8,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn from(allocator: Allocator, str: []const u8) !Self {
 | 
					 | 
				
			||||||
        var tokens = std.mem.tokenize(u8, str, "$#");
 | 
					 | 
				
			||||||
        const contents = tokens.next() orelse return error.InvalidPacket;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const chksum_str = tokens.next() orelse return error.MissingCheckSum;
 | 
					 | 
				
			||||||
        const chksum = std.fmt.parseInt(u8, chksum_str, 16) catch return error.InvalidChecksum;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // log.info("Contents: {s}", .{contents});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (!Self.verify(contents, chksum)) return error.ChecksumMismatch;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return .{ .contents = try allocator.dupe(u8, contents) };
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const String = union(enum) {
 | 
					 | 
				
			||||||
        alloc: []const u8,
 | 
					 | 
				
			||||||
        static: []const u8,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        pub fn inner(self: *const @This()) []const u8 {
 | 
					 | 
				
			||||||
            return switch (self.*) {
 | 
					 | 
				
			||||||
                .static => |str| str,
 | 
					 | 
				
			||||||
                .alloc => |str| str,
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        pub fn deinit(self: *@This(), allocator: Allocator) void {
 | 
					 | 
				
			||||||
            switch (self.*) {
 | 
					 | 
				
			||||||
                .alloc => |string| allocator.free(string),
 | 
					 | 
				
			||||||
                .static => {},
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            self.* = undefined;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn parse(self: *Self, allocator: Allocator) !String {
 | 
					 | 
				
			||||||
        switch (self.contents[0]) {
 | 
					 | 
				
			||||||
            // Required
 | 
					 | 
				
			||||||
            '?' => {
 | 
					 | 
				
			||||||
                const ret: Signal = .Trap;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // Deallocated by the caller
 | 
					 | 
				
			||||||
                return .{ .alloc = try std.fmt.allocPrint(allocator, "T{x:0>2}thread:1;", .{@enumToInt(ret)}) };
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            'g', 'G' => @panic("TODO: Register Access"),
 | 
					 | 
				
			||||||
            'm', 'M' => @panic("TODO: Memory Access"),
 | 
					 | 
				
			||||||
            'c' => @panic("TODO: Continue"),
 | 
					 | 
				
			||||||
            's' => @panic("TODO: Step"),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Optional
 | 
					 | 
				
			||||||
            'H' => {
 | 
					 | 
				
			||||||
                log.warn("{s}", .{self.contents});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                switch (self.contents[1]) {
 | 
					 | 
				
			||||||
                    'g', 'c' => return .{ .static = "OK" },
 | 
					 | 
				
			||||||
                    else => {
 | 
					 | 
				
			||||||
                        log.warn("Unimplemented: {s}", .{self.contents});
 | 
					 | 
				
			||||||
                        return .{ .static = "" };
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            'v' => {
 | 
					 | 
				
			||||||
                if (substr(self.contents[1..], "MustReplyEmpty")) {
 | 
					 | 
				
			||||||
                    return .{ .static = "" };
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                log.warn("Unimplemented: {s}", .{self.contents});
 | 
					 | 
				
			||||||
                return .{ .static = "" };
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            'q' => {
 | 
					 | 
				
			||||||
                if (self.contents[1] == 'C' and self.contents.len == 2) return .{ .static = "QC1" };
 | 
					 | 
				
			||||||
                if (substr(self.contents[1..], "fThreadInfo")) return .{ .static = "m1" };
 | 
					 | 
				
			||||||
                if (substr(self.contents[1..], "sThreadInfo")) return .{ .static = "l" };
 | 
					 | 
				
			||||||
                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+";
 | 
					 | 
				
			||||||
                    // TODO: Anything else?
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    const ret = try std.fmt.allocPrint(allocator, format, .{Packet.max_len});
 | 
					 | 
				
			||||||
                    return .{ .alloc = ret };
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (substr(self.contents[1..], "Xfer:features:read")) {
 | 
					 | 
				
			||||||
                    var tokens = std.mem.tokenize(u8, self.contents[1..], ":,");
 | 
					 | 
				
			||||||
                    _ = tokens.next(); // qXfer
 | 
					 | 
				
			||||||
                    _ = tokens.next(); // features
 | 
					 | 
				
			||||||
                    _ = tokens.next(); // read
 | 
					 | 
				
			||||||
                    const annex = tokens.next() orelse return .{ .static = "E00" };
 | 
					 | 
				
			||||||
                    const offset_str = tokens.next() orelse return .{ .static = "E00" };
 | 
					 | 
				
			||||||
                    const length_str = tokens.next() orelse return .{ .static = "E00" };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (std.mem.eql(u8, annex, "target.xml")) {
 | 
					 | 
				
			||||||
                        log.info("Providing ARMv4T target description", .{});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        const offset = try std.fmt.parseInt(usize, offset_str, 16);
 | 
					 | 
				
			||||||
                        const length = try std.fmt.parseInt(usize, length_str, 16);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        // + 2 to account for the "m " in the response
 | 
					 | 
				
			||||||
                        // subtract offset so that the allocated buffer isn't
 | 
					 | 
				
			||||||
                        // larger than it needs to be TODO: Test this?
 | 
					 | 
				
			||||||
                        const len = @min(length, (target.len + 1) - offset);
 | 
					 | 
				
			||||||
                        const ret = try allocator.alloc(u8, len);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        ret[0] = if (ret.len < length) 'l' else 'm';
 | 
					 | 
				
			||||||
                        std.mem.copy(u8, ret[1..], target[offset..]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        return .{ .alloc = ret };
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        log.err("Unexpected Annex: {s}", .{annex});
 | 
					 | 
				
			||||||
                        return .{ .static = "E00" };
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    return .{ .static = "" };
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                log.warn("Unimplemented: {s}", .{self.contents});
 | 
					 | 
				
			||||||
                return .{ .static = "" };
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            else => {
 | 
					 | 
				
			||||||
                log.warn("Unknown:  {s}", .{self.contents});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                return .{ .static = "" };
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn substr(haystack: []const u8, needle: []const u8) bool {
 | 
					 | 
				
			||||||
        return std.mem.indexOf(u8, haystack, needle) != null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn deinit(self: *Self, allocator: Allocator) void {
 | 
					 | 
				
			||||||
        allocator.free(self.contents);
 | 
					 | 
				
			||||||
        self.* = undefined;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn checksum(input: []const u8) u8 {
 | 
					 | 
				
			||||||
        var sum: usize = 0;
 | 
					 | 
				
			||||||
        for (input) |char| sum += char;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return @truncate(u8, sum);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn verify(input: []const u8, chksum: u8) bool {
 | 
					 | 
				
			||||||
        return Self.checksum(input) == chksum;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const Signal = enum(u32) {
 | 
					 | 
				
			||||||
    Hup, // Hangup
 | 
					 | 
				
			||||||
    Int, // Interrupt
 | 
					 | 
				
			||||||
    Quit, // Quit
 | 
					 | 
				
			||||||
    Ill, // Illegal Instruction
 | 
					 | 
				
			||||||
    Trap, // Trace/Breakponit trap
 | 
					 | 
				
			||||||
    Abrt, // Aborted
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user