-
Notifications
You must be signed in to change notification settings - Fork 107
/
Copy pathgdbstub.c
144 lines (112 loc) · 3.26 KB
/
gdbstub.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/*
* rv32emu is freely redistributable under the MIT License. See the file
* "LICENSE" for information on usage and redistribution of this file.
*/
#if !RV32_HAS(GDBSTUB)
#error "Do not manage to build this file unless you enable gdbstub support."
#endif
#include <assert.h>
#include <errno.h>
#include "mini-gdbstub/include/gdbstub.h"
#include "breakpoint.h"
#include "riscv.h"
#include "riscv_private.h"
static int rv_read_reg(void *args, int regno, size_t *data)
{
riscv_t *rv = (riscv_t *) args;
if (unlikely(regno > 32))
return EFAULT;
if (regno == 32)
*data = rv_get_pc(rv);
else
*data = rv_get_reg(rv, regno);
return 0;
}
static int rv_write_reg(void *args, int regno, size_t data)
{
if (unlikely(regno > 32))
return EFAULT;
riscv_t *rv = (riscv_t *) args;
if (regno == 32)
rv_set_pc(rv, data);
else
rv_set_reg(rv, regno, data);
return 0;
}
static int rv_read_mem(void *args, size_t addr, size_t len, void *val)
{
riscv_t *rv = (riscv_t *) args;
int err = 0;
for (size_t i = 0; i < len; i++) {
/* FIXME: This is implemented as a simple workaround for reading
* an invalid address. We may have to do error handling in the
* mem_read_* function directly.
*/
*((uint8_t *) val + i) = rv->io.mem_read_b(rv, addr + i);
}
return err;
}
static int rv_write_mem(void *args, size_t addr, size_t len, void *val)
{
riscv_t *rv = (riscv_t *) args;
for (size_t i = 0; i < len; i++)
rv->io.mem_write_b(rv, addr + i, *((uint8_t *) val + i));
return 0;
}
static inline bool rv_is_interrupt(riscv_t *rv)
{
return __atomic_load_n(&rv->is_interrupted, __ATOMIC_RELAXED);
}
static gdb_action_t rv_cont(void *args)
{
riscv_t *rv = (riscv_t *) args;
assert(rv);
for (; !rv_has_halted(rv) && !rv_is_interrupt(rv);) {
if (breakpoint_map_find(rv->breakpoint_map, rv_get_pc(rv)))
break;
rv_step_debug(rv);
}
/* Clear the interrupt if it's pending */
__atomic_store_n(&rv->is_interrupted, false, __ATOMIC_RELAXED);
return ACT_RESUME;
}
static gdb_action_t rv_stepi(void *args)
{
riscv_t *rv = (riscv_t *) args;
assert(rv);
rv_step_debug(rv);
return ACT_RESUME;
}
static bool rv_set_bp(void *args, size_t addr, bp_type_t type)
{
riscv_t *rv = (riscv_t *) args;
if (type != BP_SOFTWARE)
return false;
return breakpoint_map_insert(rv->breakpoint_map, addr);
}
static bool rv_del_bp(void *args, size_t addr, bp_type_t type)
{
riscv_t *rv = (riscv_t *) args;
if (type != BP_SOFTWARE)
return false;
/* When there is no matched breakpoint, no further action is taken */
breakpoint_map_del(rv->breakpoint_map, addr);
return true;
}
static void rv_on_interrupt(void *args)
{
riscv_t *rv = (riscv_t *) args;
/* Notify the emulator to break out the for loop in rv_cont */
__atomic_store_n(&rv->is_interrupted, true, __ATOMIC_RELAXED);
}
const struct target_ops gdbstub_ops = {
.read_reg = rv_read_reg,
.write_reg = rv_write_reg,
.read_mem = rv_read_mem,
.write_mem = rv_write_mem,
.cont = rv_cont,
.stepi = rv_stepi,
.set_bp = rv_set_bp,
.del_bp = rv_del_bp,
.on_interrupt = rv_on_interrupt,
};