Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ set(BUS_SOURCES

set(COMMON_SOURCES
${PROJECT_SOURCE_DIR}/common/debug.c
${PROJECT_SOURCE_DIR}/common/debug_hooks.c
${PROJECT_SOURCE_DIR}/common/hash_table.c
${PROJECT_SOURCE_DIR}/common/one_hot.c
${PROJECT_SOURCE_DIR}/common/reciprocal.c
Expand Down Expand Up @@ -370,7 +371,6 @@ set(VR4300_SOURCES
${PROJECT_SOURCE_DIR}/vr4300/cpu.c
${PROJECT_SOURCE_DIR}/vr4300/dcache.c
${PROJECT_SOURCE_DIR}/vr4300/decoder.c
${PROJECT_SOURCE_DIR}/vr4300/debug.c
${PROJECT_SOURCE_DIR}/vr4300/fault.c
${PROJECT_SOURCE_DIR}/vr4300/functions.c
${PROJECT_SOURCE_DIR}/vr4300/icache.c
Expand Down
47 changes: 47 additions & 0 deletions common/debug_hooks.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@

#include "debug_hooks.h"

cen64_cold void debug_init(struct debug_hooks* debug, enum debug_source source) {
hash_table_init(&debug->breakpoints, 0);
debug->break_handler = NULL;
debug->break_handler_data = NULL;
debug->source = source;
}

cen64_cold void debug_cleanup(struct debug_hooks* debug) {
hash_table_free(&debug->breakpoints);
}

cen64_cold void debug_check_breakpoints(struct debug_hooks* debug, uint64_t pc) {
if (debug->break_handler) {
enum debug_break_reason reason = DEBUG_BREAK_REASON_NONE;
if (hash_table_get(&debug->breakpoints, (unsigned long)pc, NULL)) {
reason = DEBUG_BREAK_REASON_BREAKPOINT;
} else if (debug->signals & DEBUG_SIGNALS_BREAK) {
reason = DEBUG_BREAK_REASON_PAUSE;
}

if (reason != DEBUG_BREAK_REASON_NONE) {
debug->signals &= ~DEBUG_SIGNALS_BREAK;
debug->break_handler(debug->break_handler_data, reason, debug->source);
}
}
}

cen64_cold void debug_exception(struct debug_hooks* debug) {
if (debug->break_handler) {
debug->break_handler(debug->break_handler_data, DEBUG_BREAK_REASON_EXCEPTION, debug->source);
}
}

cen64_cold void debug_set_breakpoint(struct debug_hooks* debug, uint64_t pc) {
hash_table_set(&debug->breakpoints, (unsigned long)pc, 1);
}

cen64_cold void debug_remove_breakpoint(struct debug_hooks* debug, uint64_t pc) {
hash_table_delete(&debug->breakpoints, (unsigned long)pc);
}

cen64_cold void debug_signal(struct debug_hooks* debug, enum debug_signals signal) {
debug->signals |= signal;
}
45 changes: 45 additions & 0 deletions common/debug_hooks.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@

#ifndef __common_debug_hooks__
#define __common_debug_hooks__

#include "common.h"
#include "common/hash_table.h"

enum debug_break_reason {
DEBUG_BREAK_REASON_NONE,
DEBUG_BREAK_REASON_BREAKPOINT,
DEBUG_BREAK_REASON_EXCEPTION,
DEBUG_BREAK_REASON_PAUSE,
};

enum debug_source {
DEBUG_SOURCE_VR4300,
DEBUG_SOURCE_RSP,
};

typedef void (*debug_break_handler)(void* data, enum debug_break_reason reason, enum debug_source source);

enum debug_signals {
DEBUG_SIGNALS_BREAK = 0x000000001,
};

struct debug_hooks {
struct hash_table breakpoints;
debug_break_handler break_handler;
void* break_handler_data;
unsigned signals;
enum debug_source source;
};

void debug_init(struct debug_hooks* debug, enum debug_source source);

void debug_cleanup(struct debug_hooks* debug);
void debug_check_breakpoints(struct debug_hooks* debug, uint64_t pc);
void debug_exception(struct debug_hooks* debug);
void debug_set_breakpoint(struct debug_hooks* debug, uint64_t pc);
void debug_remove_breakpoint(struct debug_hooks* debug, uint64_t pc);

void debug_signal(struct debug_hooks* debug, enum debug_signals signal);


#endif
4 changes: 3 additions & 1 deletion device/device.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "ri/controller.h"
#include "si/controller.h"
#include "rsp/cpu.h"
#include "rsp/interface.h"
#include "thread.h"
#include "vi/controller.h"
#include "vr4300/interface.h"
Expand Down Expand Up @@ -349,6 +350,7 @@ int device_debug_spin(struct cen64_device *device) {
return 0;
}

cen64_cold void device_connect_debugger(struct cen64_device *device, void* break_handler_data, vr4300_debug_break_handler break_handler) {
cen64_cold void device_connect_debugger(struct cen64_device *device, void* break_handler_data, debug_break_handler break_handler) {
vr4300_connect_debugger(device->vr4300, break_handler_data, break_handler);
rsp_connect_debugger(&device->rsp, break_handler_data, break_handler);
}
2 changes: 1 addition & 1 deletion device/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ cen64_cold struct cen64_device *device_create(struct cen64_device *device,
cen64_cold void device_exit(struct bus_controller *bus);
cen64_cold void device_run(struct cen64_device *device);

cen64_cold void device_connect_debugger(struct cen64_device *device, void* break_handler_data, vr4300_debug_break_handler break_handler);
cen64_cold void device_connect_debugger(struct cen64_device *device, void* break_handler_data, debug_break_handler break_handler);

#endif

6 changes: 3 additions & 3 deletions gdb/gdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
#include <unistd.h>
#endif

cen64_cold void gdb_handle_breakpoint(void* data, enum vr4300_debug_break_reason reason);
cen64_cold void gdb_handle_breakpoint(void* data, enum debug_break_reason reason, enum debug_source source);

int gdb_read(struct gdb* gdb) {
if (gdb->pending_data >= MAX_GDB_PACKET_SIZE) {
Expand Down Expand Up @@ -142,7 +142,7 @@ CEN64_THREAD_RETURN_TYPE gdb_thread(void *opaque) {
return CEN64_THREAD_RETURN_VAL;
}

cen64_cold void gdb_handle_breakpoint(void* data, enum vr4300_debug_break_reason reason) {
cen64_cold void gdb_handle_breakpoint(void* data, enum debug_break_reason reason, enum debug_source source) {
struct gdb* gdb = (struct gdb*)data;

debug("Stopping at 0x%08x\n", (uint32_t)vr4300_get_pc(gdb->device->vr4300));
Expand All @@ -154,7 +154,7 @@ cen64_cold void gdb_handle_breakpoint(void* data, enum vr4300_debug_break_reason
vr4300_remove_breakpoint(gdb->device->vr4300, 0xFFFFFFFF80000000ULL);
cen64_cv_signal(&gdb->client_semaphore);
} else {
gdb_send_stop_reply(gdb, reason == VR4300_DEBUG_BREAK_REASON_BREAKPOINT);
gdb_send_stop_reply(gdb, reason == DEBUG_BREAK_REASON_BREAKPOINT, source);
}

cen64_mutex_lock(&gdb->client_mutex);
Expand Down
1 change: 1 addition & 0 deletions gdb/gdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ enum gdb_flags {
GDB_FLAGS_INITIAL = 0x1,
GDB_FLAGS_CONNECTED = 0x2,
GDB_FLAGS_PAUSED = 0x4,
GDB_FLAGS_RSP_SELECTED = 0x8,
};

struct gdb {
Expand Down
133 changes: 118 additions & 15 deletions gdb/protocol.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "gdb/protocol.h"
#include "gdb/gdb.h"
#include "vr4300/cpu.h"
#include "rsp/interface.h"

#include <inttypes.h>

Expand All @@ -25,11 +26,22 @@
#include <unistd.h>
#endif

#define GDB_GLOBAL_THREAD_ID 1
#define GDB_RSP_THEAD_ID 2

#define GDB_GET_EXC_CODE(cause) (((cause) >> 2) & 0x1f)
#define GDB_TRANSLATE_PC(pc) ((uint64_t)(pc) | 0xFFFFFFFF00000000ULL)
#define GDB_GLOBAL_THREAD_ID 1
#define GDB_STR_STARTS_WITH(str, const_str) (strncmp(str, const_str, sizeof const_str - 1) == 0)

#define GDB_RSP_MEMORY_BASE 0x04000000
#define GDB_RSP_INSTRUCTION_BASE 0x04001000
#define GDB_RSP_INSTRUCTION_END 0x04002000
#define GDB_RSP_INSTRUCTION_SIZE 0x1000

// not standard just used to allow GDB to access vector registers
#define GDB_RSP_VREG_BASE 0x04020000
#define GDB_RSP_VREG_LEN 512

static int gdb_signals[32] = {
2, // SIGINT
11, // SIGSEGV
Expand Down Expand Up @@ -65,6 +77,34 @@ static int gdb_signals[32] = {
0, // reserved
};

bool gdb_is_rsp_addr(uint64_t addr) {
return addr >= GDB_RSP_MEMORY_BASE && addr < GDB_RSP_INSTRUCTION_END;
}

bool gdb_is_rsp_instruction(uint64_t addr) {
return (addr & 0xFFFFFFFF) < GDB_RSP_INSTRUCTION_SIZE;
}

bool gdb_is_rsp_register(uint64_t addr) {
return addr >= GDB_RSP_VREG_BASE && addr < GDB_RSP_VREG_BASE + GDB_RSP_VREG_LEN;
}

void gdb_set_breakpoint(struct cen64_device* device, uint64_t addr) {
if (gdb_is_rsp_instruction(addr)) {
rsp_set_breakpoint(&device->rsp, addr & 0xFFF);
} else {
vr4300_set_breakpoint(device->vr4300, addr);
}
}

void gdb_remove_breakpoint(struct cen64_device* device, uint64_t addr) {
if (gdb_is_rsp_instruction(addr)) {
rsp_remove_breakpoint(&device->rsp, addr & 0xFFF);
} else {
vr4300_remove_breakpoint(device->vr4300, addr);
}
}

int gdb_read_hex_digit(char character) {
if (character >= 'a' && character <= 'f') {
return 10 + character - 'a';
Expand Down Expand Up @@ -110,6 +150,17 @@ char* gdb_write_hex64(char* target, uint64_t data, int data_size) {
return target;
}

char* gdb_write_hexstring(char* target, const char* src) {
while (*src) {
*target++ = gdb_hex_letters[(*src >> 4) & 0xF];
*target++ = gdb_hex_letters[(*src >> 0) & 0xF];
src++;
}

return target;

}

int gdb_apply_checksum(char* message) {
char* message_start = message;
if (*message == '$') {
Expand Down Expand Up @@ -207,7 +258,7 @@ void gdb_handle_v(struct gdb* gdb, const char* command_start, const char *comman
}
}

void gdb_reply_registers(struct gdb* gdb) {
void gdb_reply_vr4300_registers(struct gdb* gdb) {
char* current = gdb->output_buffer;
*current++ = '$';

Expand Down Expand Up @@ -236,6 +287,43 @@ void gdb_reply_registers(struct gdb* gdb) {
gdb_send(gdb);
}

void gdb_reply_rsp_registers(struct gdb* gdb) {
char* current = gdb->output_buffer;
*current++ = '$';

// R0
current = gdb_write_hex64(current, 0, sizeof(uint64_t));
for (int i = RSP_REGISTER_AT; i <= RSP_REGISTER_RA; i++) {
current = gdb_write_hex64(current, rsp_get_register(&gdb->device->rsp, i), sizeof(uint64_t));
}

current = gdb_write_hex64(current, 0, sizeof(uint64_t)); // VR4300_CP0_REGISTER_STATUS
current = gdb_write_hex64(current, 0, sizeof(uint64_t)); // VR4300_REGISTER_LO
current = gdb_write_hex64(current, 0, sizeof(uint64_t)); // VR4300_REGISTER_HI
current = gdb_write_hex64(current, 0, sizeof(uint64_t)); // VR4300_CP0_REGISTER_BADVADDR
current = gdb_write_hex64(current, 0, sizeof(uint64_t)); // VR4300_CP0_REGISTER_CAUSE
current = gdb_write_hex64(current, rsp_get_pc(&gdb->device->rsp), sizeof(uint64_t));

for (int i = VR4300_REGISTER_CP1_0; i <= VR4300_REGISTER_CP1_31; i++) {
current = gdb_write_hex64(current, vr4300_get_register(gdb->device->vr4300, i), sizeof(uint64_t));
}

current = gdb_write_hex64(current, vr4300_get_register(gdb->device->vr4300, VR4300_CP1_FCR31), sizeof(uint64_t));

*current++ = '#';
*current++ = '\0';

gdb_send(gdb);
}

void gdb_reply_registers(struct gdb* gdb) {
if (gdb->flags & GDB_FLAGS_RSP_SELECTED) {
gdb_reply_rsp_registers(gdb);
} else {
gdb_reply_vr4300_registers(gdb);
}
}

void gdb_reply_memory(struct gdb* gdb, const char* command_start, const char *command_end) {
char* current = gdb->output_buffer;
*current++ = '$';
Expand All @@ -255,14 +343,21 @@ void gdb_reply_memory(struct gdb* gdb, const char* command_start, const char *co
for (int curr = 0; curr < len + byteShift; curr += 4) {
uint32_t word = 0;
int32_t vaddr = alignedAddr + curr;

if (!vr4300_read_word_vaddr(gdb->device->vr4300, vaddr, &word)) {
debug("Bad vaddr %08x\n", vaddr);
}

// if (curr > 0x20) {
// word = curr;
// }
if ((gdb->flags & GDB_FLAGS_RSP_SELECTED) && gdb_is_rsp_instruction(vaddr)) {
bus_read_word(&gdb->device->bus, vaddr + GDB_RSP_INSTRUCTION_BASE, &word);
} else if (gdb_is_rsp_register(vaddr)) {
int32_t relative = (vaddr - GDB_RSP_VREG_BASE) / sizeof(uint16_t);
int32_t registerIndex = relative / 8;
int32_t elementIndex = relative % 8;

word = ((int32_t)gdb->device->rsp.cp2.regs[registerIndex].e[elementIndex] << 16) |
(int32_t)gdb->device->rsp.cp2.regs[registerIndex].e[elementIndex + 1];
} else if (gdb_is_rsp_addr(vaddr)) {
bus_read_word(&gdb->device->bus, vaddr, &word);
} else {
vr4300_read_word_vaddr(gdb->device->vr4300, vaddr, &word);
}

current = gdb_write_hex64(current, word, sizeof(uint32_t));
}
Expand Down Expand Up @@ -296,7 +391,7 @@ void gdb_handle_packet(struct gdb* gdb, const char* command_start, const char* c
gdb_send_literal(gdb, "$#00");
break;
case '?':
gdb_send_stop_reply(gdb, false);
gdb_send_stop_reply(gdb, false, DEBUG_SOURCE_VR4300);
break;
case 'g':
gdb_reply_registers(gdb);
Expand All @@ -315,9 +410,9 @@ void gdb_handle_packet(struct gdb* gdb, const char* command_start, const char* c
uint64_t addr = GDB_TRANSLATE_PC(gdb_parse_hex(&command_start[3], 4));

if (*command_start == 'z') {
vr4300_remove_breakpoint(gdb->device->vr4300, addr);
gdb_remove_breakpoint(gdb->device, addr);
} else {
vr4300_set_breakpoint(gdb->device->vr4300, addr);
gdb_set_breakpoint(gdb->device, addr);
}

return gdb_send_literal(gdb, "$OK#9a");
Expand All @@ -331,21 +426,29 @@ void gdb_handle_packet(struct gdb* gdb, const char* command_start, const char* c
}
}

cen64_cold void gdb_send_stop_reply(struct gdb* gdb, bool is_breakpoint) {
cen64_cold void gdb_send_stop_reply(struct gdb* gdb, bool is_breakpoint, enum debug_source source) {
char* current = gdb->output_buffer;
int exc_code;

if (is_breakpoint) {
if (source == DEBUG_SOURCE_RSP) {
gdb->flags |= GDB_FLAGS_RSP_SELECTED;
exc_code = 9;
} else {
exc_code = GDB_GET_EXC_CODE(vr4300_get_register(gdb->device->vr4300, VR4300_CP0_REGISTER_CAUSE));
gdb->flags &= ~GDB_FLAGS_RSP_SELECTED;
if (is_breakpoint) {
exc_code = 9;
} else {
exc_code = GDB_GET_EXC_CODE(vr4300_get_register(gdb->device->vr4300, VR4300_CP0_REGISTER_CAUSE));
}
}

current += sprintf(current, "$T%02x", gdb_signals[exc_code]);
if (is_breakpoint) {
current += sprintf(current, "swbreak:");
}
current += sprintf(current, "thread:%d;", GDB_GLOBAL_THREAD_ID);
*current++ = '#';
*current++ = '\0';

gdb_send(gdb);
}
Loading