1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2018 Intel Corporation. All rights reserved.
4 //
5 // Author: Marcin Rajwa <marcin.rajwa@linux.intel.com>
6
7 /*
8 * GDB Stub - parse incoming GDB packets, control FW accordingly and provide
9 * a reply to the GDB server.
10 *
11 */
12
13 #include <sof/debug/gdb/gdb.h>
14 #include <sof/debug/gdb/ringbuffer.h>
15 #include <sof/lib/cache.h>
16 #include <string.h>
17
18 /* local functions */
19 static int get_hex(unsigned char ch);
20 static int hex_to_int(unsigned char **ptr, int *int_value);
21 static void put_packet(unsigned char *buffer);
22 static unsigned char *get_packet(void);
23 static void gdb_log_exception(char *message);
24 static unsigned char *mem_to_hex(void *mem_,
25 unsigned char *buf, int count);
26 static unsigned char *hex_to_mem(const unsigned char *buf, void *mem_,
27 int count);
28 static inline int gdb_parser(void);
29
30 /* main buffers */
31 static unsigned char remcom_in_buffer[GDB_BUFMAX];
32 static unsigned char remcom_out_buffer[GDB_BUFMAX];
33
34 static const char hex_chars[] = "0123456789abcdef";
35
36 /* registers backup conatiners */
37 int sregs[256];
38 int aregs[64];
39
gdb_init(void)40 void gdb_init(void)
41 {
42 init_buffers();
43 }
44
45 /* scan for the GDB packet sequence $<data>#<check_sum> */
get_packet(void)46 unsigned char *get_packet(void)
47 {
48 unsigned char *buffer = &remcom_in_buffer[0];
49 unsigned char check_sum;
50 unsigned char xmitcsum;
51 int count;
52 unsigned char ch;
53
54 /* wait around for the start character, ignore all other characters */
55 while ((ch = get_debug_char()) != '$')
56 ;
57 retry:
58 check_sum = 0;
59 xmitcsum = -1;
60 count = 0;
61
62 /* now, read until a # or end of buffer is found */
63 while (count < GDB_BUFMAX - 1) {
64 ch = get_debug_char();
65
66 if (ch == '$')
67 goto retry;
68 if (ch == '#')
69 break;
70 check_sum = check_sum + ch;
71 buffer[count] = ch;
72 count++;
73 }
74 /* mark end of the sequence */
75 buffer[count] = 0x00;
76 if (ch == '#') {
77 /* We have request already, now fetch its check_sum */
78 ch = get_debug_char();
79 xmitcsum = get_hex(ch) << 4;
80 ch = get_debug_char();
81 xmitcsum += get_hex(ch);
82
83 if (check_sum != xmitcsum) {
84 /* TODO: handle wrong check_sums */
85 put_debug_char('+');
86 } else {
87 /* successful transfer */
88 put_debug_char('+');
89 /*
90 * if a sequence char is present
91 * reply the sequence ID
92 */
93 if (buffer[2] == ':') {
94 put_debug_char(buffer[0]);
95 put_debug_char(buffer[1]);
96
97 return &buffer[3];
98 }
99 }
100 }
101 return buffer;
102 }
103
104 /* Convert ch from a request to an int */
get_hex(unsigned char ch)105 static int get_hex(unsigned char ch)
106 {
107 if (ch >= 'a' && ch <= 'f')
108 return ch - 'a' + 10;
109 if (ch >= '0' && ch <= '9')
110 return ch - '0';
111 if (ch >= 'A' && ch <= 'F')
112 return ch - 'A' + 10;
113 return -1;
114 }
115
gdb_handle_exception(void)116 void gdb_handle_exception(void)
117 {
118 gdb_log_exception("Hello from GDB!");
119
120 while (gdb_parser())
121 ;/* do nothing */
122 }
123
124 /**
125 * \brief Parse incoming GDB packets.
126 *
127 * Every incoming packet has the format: $packet-data#check-sum
128 * packet-data varies depending on command. Full description
129 * of each command packet can be found in GNU GDB reference manual.
130 */
gdb_parser(void)131 static inline int gdb_parser(void)
132 {
133 unsigned char *request;
134 unsigned int i;
135 int addr;
136 int length;
137 unsigned int windowbase = (4 * sregs[DEBUG_WINDOWBASE]);
138
139 request = get_packet();
140 /* Log any exception caused by debug exception */
141 gdb_debug_info(request);
142 /* Pick incoming request handler */
143 unsigned char command = *request++;
144
145 switch (command) {
146 /* Continue normal program execution and leave debug handler */
147 case 'c':
148 /* incoming packet has the form $c,ADDRESS#CH, where:
149 * c - continue command identifier,
150 * ADDRESS - address on which to continue
151 * CH - two bytes checksum.
152 */
153 if (hex_to_int(&request, &addr))
154 sregs[DEBUG_PC] = addr;
155
156 /* return from exception */
157 return 0;
158 /* insert breakpoint */
159 case 'Z':
160 switch (*request++) {
161 /* HW breakpoint */
162 case '1':
163 /* Incoming packet has the form $ZX,ADDRESS,LEN,#CH, where:
164 * Z - breakpoint command identifier, X - 0/1, SW/HW breakpoint
165 * ADDRESS - address on which breakpoint shall be put
166 * LEN - address length in bytes, CH - two bytes checksum.
167 */
168 if (*request++ == ',' && hex_to_int(&request, &addr) &&
169 *request++ == ',' && hex_to_int(&request, &length)
170 && *request == 0) {
171 for (i = 0; i < DEBUG_NUM_IBREAK; ++i) {
172 if (!(sregs[DEBUG_IBREAKENABLE] &
173 (1 << i)) ||
174 sregs[DEBUG_IBREAKA + i] == addr) {
175 sregs[DEBUG_IBREAKA + i] =
176 addr;
177 sregs[DEBUG_IBREAKENABLE] |=
178 (1 << i);
179 arch_gdb_write_sr(
180 (DEBUG_IBREAKA + i),
181 sregs);
182 arch_gdb_write_sr(
183 DEBUG_IBREAKENABLE,
184 sregs);
185 break;
186 }
187 }
188
189 if (i == DEBUG_NUM_IBREAK) {
190 strcpy((char *)remcom_out_buffer,
191 "E02");
192 } else {
193 strcpy((char *)remcom_out_buffer, "OK");
194 sregs[DEBUG_INTENABLE] &=
195 GDB_DISABLE_LOWER_INTERRUPTS_MASK;
196 arch_gdb_write_sr(DEBUG_INTENABLE,
197 sregs);
198 }
199 } else {
200 strcpy((char *)remcom_out_buffer, "E01");
201 }
202 break;
203 /* SW breakpoints */
204 default:
205 /* send empty response to indicate thet SW breakpoints
206 * are not supported
207 */
208 strcpy((char *)remcom_out_buffer, "");
209 break;
210 }
211 break;
212 /* remove HW breakpoint */
213 case 'z':
214 switch (*request++) {
215 /* Incoming packet has the form $zX,ADDRESS,LEN,#CH, where:
216 * Z - breakpoint command identifier, X - 0/1, SW/HW breakpoint
217 * ADDRESS - address from which breakpoint shall be removed
218 * LEN - address length in bytes, CH - two bytes checksum.
219 */
220 case '1':
221 if (*request++ == ',' && hex_to_int(&request, &addr) &&
222 *request++ == ',' && hex_to_int(&request, &length)) {
223 for (i = 0; i < DEBUG_NUM_IBREAK; ++i) {
224 if (sregs[DEBUG_IBREAKENABLE] &
225 (1 << i) &&
226 sregs[DEBUG_IBREAKA + i] == addr) {
227 sregs[DEBUG_IBREAKENABLE]
228 &= ~(1 << i);
229 arch_gdb_write_sr(
230 DEBUG_IBREAKENABLE,
231 sregs);
232 break;
233 }
234 }
235 if (i == DEBUG_NUM_IBREAK)
236 strcpy((char *)remcom_out_buffer,
237 "E02");
238 else
239 strcpy((char *)remcom_out_buffer, "OK");
240 } else {
241 /* respond with error message */
242 strcpy((char *)remcom_out_buffer, "E01");
243 }
244 break;
245 /* SW breakpoints */
246 default:
247 /* send empty response to indicate thet SW breakpoints
248 * are not supported
249 */
250 strcpy((char *)remcom_out_buffer, "");
251 break;
252 }
253 break;
254 /* single step in the code */
255 case 's':
256 /* incoming packet has the form $s#CH, where:
257 * s - step command identifier,
258 * CH - two bytes checksum.
259 */
260 if (hex_to_int(&request, &addr))
261 sregs[DEBUG_PC] = addr;
262 arch_gdb_single_step(sregs);
263 return 0;
264 /* read register */
265 case 'p':
266 /* Incoming packet has the form $p,REGISTER#CH, where:
267 * p - read register command identifier,
268 * REGISTER - register number to read,
269 * CH - two bytes checksum.
270 */
271 if (hex_to_int(&request, &addr)) {
272 /* read address register in the current window */
273 if (addr < GDB_AR_REG_RANGE) {
274 mem_to_hex(aregs + addr, remcom_out_buffer, 4);
275 } else if (addr == GDB_PC_REG_ID) {
276 /* read PC */
277 mem_to_hex(sregs + DEBUG_PC,
278 remcom_out_buffer, 4);
279 } else if (addr >= GDB_AREG_RANGE &&
280 addr < (GDB_AREG_RANGE + DEBUG_NUM_AREGS)) {
281 mem_to_hex(aregs + ((addr - windowbase) &
282 GDB_REGISTER_MASK),
283 remcom_out_buffer, 4);
284 } else if (addr >= GDB_SPEC_REG_RANGE_START &&
285 addr < GDB_SPEC_REG_RANGE_END) {
286 addr &= GDB_REGISTER_MASK;
287 arch_gdb_read_sr(addr);
288 mem_to_hex(sregs + addr, remcom_out_buffer, 4);
289 } else if (addr >= GDB_SPEC_REG_RANGE_END &&
290 addr < GDB_REG_RANGE_END) {
291 strcpy((char *)remcom_out_buffer,
292 "deadbabe");
293 } else {
294 /* unexpected register number */
295 strcpy((char *)remcom_out_buffer, "E00");
296 }
297 }
298 break;
299 /* write register */
300 case 'P':
301 /* Incoming packet has the form $P,REGISTER#CH, where:
302 * P - write register command identifier,
303 * REGISTER - register number to write,
304 * CH - two bytes checksum.
305 */
306 if (hex_to_int(&request, &addr) && *(request++) == '=') {
307 int ok = 1;
308
309 if (addr < GDB_AR_REG_RANGE) {
310 hex_to_mem(request, aregs + addr, 4);
311 } else if (addr == GDB_PC_REG_ID) {
312 hex_to_mem(request, sregs + DEBUG_PC, 4);
313 } else if (addr >= GDB_AREG_RANGE && addr <
314 GDB_AREG_RANGE + DEBUG_NUM_AREGS) {
315 hex_to_mem(request, aregs +
316 ((addr - windowbase) & GDB_REGISTER_MASK), 4);
317 } else if (addr >= GDB_SPEC_REG_RANGE_START &&
318 addr < GDB_SPEC_REG_RANGE_END) {
319 addr &= GDB_REGISTER_MASK;
320 hex_to_mem(request, sregs + addr, 4);
321 } else {
322 ok = 0;
323 strcpy((char *)remcom_out_buffer, "E00");
324 }
325 if (ok)
326 strcpy((char *)remcom_out_buffer, "OK");
327 }
328 break;
329 /* read memory */
330 case 'm':
331 /* Incoming packet has the form $m,ADDRESS#CH, where:
332 * m - read address command identifier,
333 * ADDRESS - address to read from,
334 * CH - two bytes checksum.
335 */
336 i = hex_to_int(&request, &addr);
337 if (i == GDB_VALID_MEM_ADDRESS_LEN &&
338 ((addr & GDB_VALID_MEM_START_BYTE) >> 28) ==
339 GDB_VALID_MEM_START_BYTE &&
340 *request++ == ',' && hex_to_int(&request, &length)) {
341 if (mem_to_hex((void *)addr, remcom_out_buffer, length))
342 break;
343 /* wrong memory address - respond with error message */
344 strcpy((char *)remcom_out_buffer, "E03");
345 } else {
346 /* wrong packet format - respond with error message */
347 strcpy((char *)remcom_out_buffer, "E01");
348 }
349 break;
350 /* write memory */
351 case 'X': /* binary mode */
352 case 'M':
353 /* Incoming packet has the form $M,ADDRESS#CH, where:
354 * M - write address command identifier,
355 * ADDRESS - address to write into,
356 * CH - two bytes checksum.
357 */
358 if (hex_to_int(&request, &addr) && *request++ == ',' &&
359 hex_to_int(&request, &length) && *request++ == ':') {
360 if (hex_to_mem(request, (void *)addr, length))
361 strcpy((char *)remcom_out_buffer, "OK");
362 else
363 strcpy((char *)remcom_out_buffer, "E03");
364 } else {
365 strcpy((char *)remcom_out_buffer, "E02");
366 }
367 break;
368 default:
369 gdb_log_exception("Unknown GDB command.");
370 break;
371 }
372 /* reply to the request */
373 put_packet(remcom_out_buffer);
374
375 return 1;
376 }
377
378 /*
379 * While we find nice hex chars, build an int.
380 * Return number of chars processed.
381 */
hex_to_int(unsigned char ** ptr,int * int_value)382 static int hex_to_int(unsigned char **ptr, int *int_value)
383 {
384 int num_chars = 0;
385 int hex_value;
386
387 *int_value = 0;
388 if (!ptr)
389 return 0;
390
391 while (**ptr) {
392 hex_value = get_hex(**ptr);
393 if (hex_value < 0)
394 break;
395
396 *int_value = (*int_value << 4) | hex_value;
397 num_chars++;
398 (*ptr)++;
399 }
400 return num_chars;
401 }
402
403 /* Send the packet to the buffer */
put_packet(unsigned char * buffer)404 static void put_packet(unsigned char *buffer)
405 {
406 unsigned char check_sum;
407 int count;
408 unsigned char ch;
409
410 /* $<packet_info>#<check_sum> */
411 do {
412 put_debug_char('$');
413 check_sum = 0;
414 count = 0;
415
416 while ((ch = buffer[count])) {
417 put_debug_char(ch);
418 buffer[count] = 0;
419 check_sum += ch;
420 count += 1;
421 }
422
423 put_debug_char('#');
424 put_debug_char(hex_chars[check_sum >> 4]);
425 put_debug_char(hex_chars[check_sum & 0xf]);
426
427 } while (get_debug_char() != '+');
428 }
429
gdb_log_exception(char * message)430 static void gdb_log_exception(char *message)
431 {
432 while (*message)
433 put_exception_char(*message++);
434 }
435
436 /* Convert the memory pointed to by mem into hex, placing result in buf.
437 * Return a pointer to the last char put in buf (null), in case of mem fault,
438 * return 0.
439 */
mem_to_hex(void * mem_,unsigned char * buf,int count)440 static unsigned char *mem_to_hex(void *mem_, unsigned char *buf,
441 int count)
442 {
443 unsigned char *mem = mem_;
444 unsigned char ch;
445
446 if (!mem || !buf)
447 return NULL;
448 while (count-- > 0) {
449 ch = arch_gdb_load_from_memory(mem);
450 mem++;
451 *buf++ = hex_chars[ch >> 4];
452 *buf++ = hex_chars[ch & 0xf];
453 }
454
455 *buf = 0;
456 return buf;
457 }
458
459 /* convert the hex array pointed to by buf into binary to be placed in mem
460 * return a pointer to the character after the last byte written
461 */
hex_to_mem(const unsigned char * buf,void * mem_,int count)462 static unsigned char *hex_to_mem(const unsigned char *buf, void *mem_,
463 int count)
464 {
465 unsigned char *mem = mem_;
466 int i;
467 unsigned char ch;
468
469 if (!mem || !buf)
470 return NULL;
471 for (i = 0; i < count; i++) {
472 ch = get_hex(*buf++) << 4;
473 ch |= get_hex(*buf++);
474 arch_gdb_memory_load_and_store(mem, ch);
475 mem++;
476 }
477
478 dcache_writeback_region((void *)mem, count);
479 return mem;
480 }
481