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