1 /*
2 * Copyright (c) 2023 Marek Vedral <vedrama5@fel.cvut.cz>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <kernel_internal.h>
9 #include <zephyr/arch/arm/gdbstub.h>
10 #include <zephyr/debug/gdbstub.h>
11
12 /* Position of each register in the packet - n-th register in the ctx.registers array needs to be
13 * the packet_pos[n]-th byte of the g (read all registers) packet. See struct arm_register_names in
14 * GDB file gdb/arm-tdep.c, which defines these positions.
15 */
16 static const int packet_pos[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 41};
17
18 /* Required struct */
19 static struct gdb_ctx ctx;
20
21 /* Return true if BKPT instruction caused the current entry */
is_bkpt(unsigned int exc_cause)22 static int is_bkpt(unsigned int exc_cause)
23 {
24 int ret = 0;
25
26 if (exc_cause == GDB_EXCEPTION_BREAKPOINT) {
27 /* Get the instruction */
28 unsigned int instr = sys_read32(ctx.registers[PC]);
29 /* Try to check the instruction encoding */
30 int ist = ((ctx.registers[SPSR] & BIT(SPSR_J)) >> (SPSR_J - 1)) |
31 ((ctx.registers[SPSR] & BIT(SPSR_T)) >> SPSR_T);
32
33 if (ist == SPSR_ISETSTATE_ARM) {
34 /* ARM instruction set state */
35 ret = ((instr & 0xFF00000) == 0x1200000) && ((instr & 0xF0) == 0x70);
36 } else if (ist != SPSR_ISETSTATE_JAZELLE) {
37 /* Thumb or ThumbEE encoding */
38 ret = ((instr & 0xFF00) == 0xBE00);
39 }
40 }
41 return ret;
42 }
43
44 /* Wrapper function to save and restore execution c */
z_gdb_entry(struct arch_esf * esf,unsigned int exc_cause)45 void z_gdb_entry(struct arch_esf *esf, unsigned int exc_cause)
46 {
47 /* Disable the hardware breakpoint in case it was set */
48 __asm__ volatile("mcr p14, 0, %0, c0, c0, 5" ::"r"(0x0) :);
49
50 ctx.exception = exc_cause;
51 /* save the registers */
52 ctx.registers[R0] = esf->basic.r0;
53 ctx.registers[R1] = esf->basic.r1;
54 ctx.registers[R2] = esf->basic.r2;
55 ctx.registers[R3] = esf->basic.r3;
56 /* The EXTRA_EXCEPTION_INFO kernel option ensures these regs are set */
57 ctx.registers[R4] = esf->extra_info.callee->v1;
58 ctx.registers[R5] = esf->extra_info.callee->v2;
59 ctx.registers[R6] = esf->extra_info.callee->v3;
60 ctx.registers[R7] = esf->extra_info.callee->v4;
61 ctx.registers[R8] = esf->extra_info.callee->v5;
62 ctx.registers[R9] = esf->extra_info.callee->v6;
63 ctx.registers[R10] = esf->extra_info.callee->v7;
64 ctx.registers[R11] = esf->extra_info.callee->v8;
65 ctx.registers[R13] = esf->extra_info.callee->psp;
66
67 ctx.registers[R12] = esf->basic.r12;
68 ctx.registers[LR] = esf->basic.lr;
69 ctx.registers[PC] = esf->basic.pc;
70 ctx.registers[SPSR] = esf->basic.xpsr;
71
72 /* True if entering after a BKPT instruction */
73 const int bkpt_entry = is_bkpt(exc_cause);
74
75 z_gdb_main_loop(&ctx);
76
77 /* The registers part of EXTRA_EXCEPTION_INFO are read-only - the excpetion return code
78 * does not restore them, thus we don't need to do so here
79 */
80 esf->basic.r0 = ctx.registers[R0];
81 esf->basic.r1 = ctx.registers[R1];
82 esf->basic.r2 = ctx.registers[R2];
83 esf->basic.r3 = ctx.registers[R3];
84 esf->basic.r12 = ctx.registers[R12];
85 esf->basic.lr = ctx.registers[LR];
86 esf->basic.pc = ctx.registers[PC];
87 esf->basic.xpsr = ctx.registers[SPSR];
88 /* TODO: restore regs from extra exc. info */
89
90 if (bkpt_entry) {
91 /* Apply this offset, so that the process won't be affected by the
92 * BKPT instruction
93 */
94 esf->basic.pc += 0x4;
95 }
96 esf->basic.xpsr = ctx.registers[SPSR];
97 }
98
arch_gdb_init(void)99 void arch_gdb_init(void)
100 {
101 uint32_t reg_val;
102 /* Enable the monitor debug mode */
103 __asm__ volatile("mrc p14, 0, %0, c0, c2, 2" : "=r"(reg_val)::);
104 reg_val |= DBGDSCR_MONITOR_MODE_EN;
105 __asm__ volatile("mcr p14, 0, %0, c0, c2, 2" ::"r"(reg_val) :);
106
107 /* Generate the Prefetch abort exception */
108 __asm__ volatile("BKPT");
109 }
110
arch_gdb_continue(void)111 void arch_gdb_continue(void)
112 {
113 /* No need to do anything, return to the code. */
114 }
115
arch_gdb_step(void)116 void arch_gdb_step(void)
117 {
118 /* Set the hardware breakpoint */
119 uint32_t reg_val = ctx.registers[PC];
120 /* set BVR (Breakpoint value register) to PC, make sure it is word aligned */
121 reg_val &= ~(0x3);
122 __asm__ volatile("mcr p14, 0, %0, c0, c0, 4" ::"r"(reg_val) :);
123
124 reg_val = 0;
125 /* Address mismatch */
126 reg_val |= (DBGDBCR_MEANING_ADDR_MISMATCH & DBGDBCR_MEANING_MASK) << DBGDBCR_MEANING_SHIFT;
127 /* Match any other instruction */
128 reg_val |= (0xF & DBGDBCR_BYTE_ADDR_MASK) << DBGDBCR_BYTE_ADDR_SHIFT;
129 /* Breakpoint enable */
130 reg_val |= DBGDBCR_BRK_EN_MASK;
131 __asm__ volatile("mcr p14, 0, %0, c0, c0, 5" ::"r"(reg_val) :);
132 }
133
arch_gdb_reg_readall(struct gdb_ctx * c,uint8_t * buf,size_t buflen)134 size_t arch_gdb_reg_readall(struct gdb_ctx *c, uint8_t *buf, size_t buflen)
135 {
136 int ret = 0;
137 /* All other registers are not supported */
138 memset(buf, 'x', buflen);
139 for (int i = 0; i < GDB_NUM_REGS; i++) {
140 /* offset inside the packet */
141 int pos = packet_pos[i] * 8;
142 int r = bin2hex((const uint8_t *)(c->registers + i), 4, buf + pos, buflen - pos);
143 /* remove the newline character placed by the bin2hex function */
144 buf[pos + 8] = 'x';
145 if (r == 0) {
146 ret = 0;
147 break;
148 }
149 ret += r;
150 }
151
152 if (ret) {
153 /* Since we don't support some floating point registers, set the packet size
154 * manually
155 */
156 ret = GDB_READALL_PACKET_SIZE;
157 }
158 return ret;
159 }
160
arch_gdb_reg_writeall(struct gdb_ctx * c,uint8_t * hex,size_t hexlen)161 size_t arch_gdb_reg_writeall(struct gdb_ctx *c, uint8_t *hex, size_t hexlen)
162 {
163 int ret = 0;
164
165 for (unsigned int i = 0; i < hexlen; i += 8) {
166 if (hex[i] != 'x') {
167 /* check if the stub supports this register */
168 for (unsigned int j = 0; j < GDB_NUM_REGS; j++) {
169 if (packet_pos[j] != i) {
170 continue;
171 }
172 int r = hex2bin(hex + i * 8, 8, (uint8_t *)(c->registers + j), 4);
173
174 if (r == 0) {
175 return 0;
176 }
177 ret += r;
178 }
179 }
180 }
181 return ret;
182 }
183
arch_gdb_reg_readone(struct gdb_ctx * c,uint8_t * buf,size_t buflen,uint32_t regno)184 size_t arch_gdb_reg_readone(struct gdb_ctx *c, uint8_t *buf, size_t buflen, uint32_t regno)
185 {
186 /* Reading four bytes (could be any return value except 0, which would indicate an error) */
187 int ret = 4;
188 /* Fill the buffer with 'x' in case the stub does not support the required register */
189 memset(buf, 'x', 8);
190 if (regno == SPSR_REG_IDX) {
191 /* The SPSR register is at the end, we have to check separately */
192 ret = bin2hex((uint8_t *)(c->registers + GDB_NUM_REGS - 1), 4, buf, buflen);
193 } else {
194 /* Check which of our registers corresponds to regnum */
195 for (int i = 0; i < GDB_NUM_REGS; i++) {
196 if (packet_pos[i] == regno) {
197 ret = bin2hex((uint8_t *)(c->registers + i), 4, buf, buflen);
198 break;
199 }
200 }
201 }
202 return ret;
203 }
204
arch_gdb_reg_writeone(struct gdb_ctx * c,uint8_t * hex,size_t hexlen,uint32_t regno)205 size_t arch_gdb_reg_writeone(struct gdb_ctx *c, uint8_t *hex, size_t hexlen, uint32_t regno)
206 {
207 int ret = 0;
208 /* Set the value of a register */
209 if (hexlen != 8) {
210 return ret;
211 }
212
213 if (regno < (GDB_NUM_REGS - 1)) {
214 /* Again, check the corresponding register index */
215 for (int i = 0; i < GDB_NUM_REGS; i++) {
216 if (packet_pos[i] == regno) {
217 ret = hex2bin(hex, hexlen, (uint8_t *)(c->registers + i), 4);
218 break;
219 }
220 }
221 } else if (regno == SPSR_REG_IDX) {
222 ret = hex2bin(hex, hexlen, (uint8_t *)(c->registers + GDB_NUM_REGS - 1), 4);
223 }
224 return ret;
225 }
226