153197fc4SJason Wessel /* 253197fc4SJason Wessel * Kernel Debug Core 353197fc4SJason Wessel * 453197fc4SJason Wessel * Maintainer: Jason Wessel <jason.wessel@windriver.com> 553197fc4SJason Wessel * 653197fc4SJason Wessel * Copyright (C) 2000-2001 VERITAS Software Corporation. 753197fc4SJason Wessel * Copyright (C) 2002-2004 Timesys Corporation 853197fc4SJason Wessel * Copyright (C) 2003-2004 Amit S. Kale <amitkale@linsyssoft.com> 9a2531293SPavel Machek * Copyright (C) 2004 Pavel Machek <pavel@ucw.cz> 1053197fc4SJason Wessel * Copyright (C) 2004-2006 Tom Rini <trini@kernel.crashing.org> 1153197fc4SJason Wessel * Copyright (C) 2004-2006 LinSysSoft Technologies Pvt. Ltd. 1253197fc4SJason Wessel * Copyright (C) 2005-2009 Wind River Systems, Inc. 1353197fc4SJason Wessel * Copyright (C) 2007 MontaVista Software, Inc. 1453197fc4SJason Wessel * Copyright (C) 2008 Red Hat, Inc., Ingo Molnar <mingo@redhat.com> 1553197fc4SJason Wessel * 1653197fc4SJason Wessel * Contributors at various stages not listed above: 1753197fc4SJason Wessel * Jason Wessel ( jason.wessel@windriver.com ) 1853197fc4SJason Wessel * George Anzinger <george@mvista.com> 1953197fc4SJason Wessel * Anurekh Saxena (anurekh.saxena@timesys.com) 2053197fc4SJason Wessel * Lake Stevens Instrument Division (Glenn Engel) 2153197fc4SJason Wessel * Jim Kingdon, Cygnus Support. 2253197fc4SJason Wessel * 2353197fc4SJason Wessel * Original KGDB stub: David Grothe <dave@gcom.com>, 2453197fc4SJason Wessel * Tigran Aivazian <tigran@sco.com> 2553197fc4SJason Wessel * 2653197fc4SJason Wessel * This file is licensed under the terms of the GNU General Public License 2753197fc4SJason Wessel * version 2. This program is licensed "as is" without any warranty of any 2853197fc4SJason Wessel * kind, whether express or implied. 2953197fc4SJason Wessel */ 3053197fc4SJason Wessel 3153197fc4SJason Wessel #include <linux/kernel.h> 323f07c014SIngo Molnar #include <linux/sched/signal.h> 3353197fc4SJason Wessel #include <linux/kgdb.h> 34f5316b4aSJason Wessel #include <linux/kdb.h> 3516559ae4SGreg Kroah-Hartman #include <linux/serial_core.h> 3653197fc4SJason Wessel #include <linux/reboot.h> 3753197fc4SJason Wessel #include <linux/uaccess.h> 3853197fc4SJason Wessel #include <asm/cacheflush.h> 3953197fc4SJason Wessel #include <asm/unaligned.h> 4053197fc4SJason Wessel #include "debug_core.h" 4153197fc4SJason Wessel 4253197fc4SJason Wessel #define KGDB_MAX_THREAD_QUERY 17 4353197fc4SJason Wessel 4453197fc4SJason Wessel /* Our I/O buffers. */ 4553197fc4SJason Wessel static char remcom_in_buffer[BUFMAX]; 4653197fc4SJason Wessel static char remcom_out_buffer[BUFMAX]; 47f679c498SJason Wessel static int gdbstub_use_prev_in_buf; 48f679c498SJason Wessel static int gdbstub_prev_in_buf_pos; 4953197fc4SJason Wessel 5053197fc4SJason Wessel /* Storage for the registers, in GDB format. */ 5153197fc4SJason Wessel static unsigned long gdb_regs[(NUMREGBYTES + 5253197fc4SJason Wessel sizeof(unsigned long) - 1) / 5353197fc4SJason Wessel sizeof(unsigned long)]; 5453197fc4SJason Wessel 5553197fc4SJason Wessel /* 5653197fc4SJason Wessel * GDB remote protocol parser: 5753197fc4SJason Wessel */ 5853197fc4SJason Wessel 59f5316b4aSJason Wessel #ifdef CONFIG_KGDB_KDB 60f5316b4aSJason Wessel static int gdbstub_read_wait(void) 61f5316b4aSJason Wessel { 62f5316b4aSJason Wessel int ret = -1; 63f5316b4aSJason Wessel int i; 64f5316b4aSJason Wessel 65f679c498SJason Wessel if (unlikely(gdbstub_use_prev_in_buf)) { 66f679c498SJason Wessel if (gdbstub_prev_in_buf_pos < gdbstub_use_prev_in_buf) 67f679c498SJason Wessel return remcom_in_buffer[gdbstub_prev_in_buf_pos++]; 68f679c498SJason Wessel else 69f679c498SJason Wessel gdbstub_use_prev_in_buf = 0; 70f679c498SJason Wessel } 71f679c498SJason Wessel 72f5316b4aSJason Wessel /* poll any additional I/O interfaces that are defined */ 73f5316b4aSJason Wessel while (ret < 0) 74f5316b4aSJason Wessel for (i = 0; kdb_poll_funcs[i] != NULL; i++) { 75f5316b4aSJason Wessel ret = kdb_poll_funcs[i](); 76f5316b4aSJason Wessel if (ret > 0) 77f5316b4aSJason Wessel break; 78f5316b4aSJason Wessel } 79f5316b4aSJason Wessel return ret; 80f5316b4aSJason Wessel } 81f5316b4aSJason Wessel #else 82f5316b4aSJason Wessel static int gdbstub_read_wait(void) 83f5316b4aSJason Wessel { 84f5316b4aSJason Wessel int ret = dbg_io_ops->read_char(); 85f5316b4aSJason Wessel while (ret == NO_POLL_CHAR) 86f5316b4aSJason Wessel ret = dbg_io_ops->read_char(); 87f5316b4aSJason Wessel return ret; 88f5316b4aSJason Wessel } 89f5316b4aSJason Wessel #endif 9053197fc4SJason Wessel /* scan for the sequence $<data>#<checksum> */ 9153197fc4SJason Wessel static void get_packet(char *buffer) 9253197fc4SJason Wessel { 9353197fc4SJason Wessel unsigned char checksum; 9453197fc4SJason Wessel unsigned char xmitcsum; 9553197fc4SJason Wessel int count; 9653197fc4SJason Wessel char ch; 9753197fc4SJason Wessel 9853197fc4SJason Wessel do { 9953197fc4SJason Wessel /* 10053197fc4SJason Wessel * Spin and wait around for the start character, ignore all 10153197fc4SJason Wessel * other characters: 10253197fc4SJason Wessel */ 103f5316b4aSJason Wessel while ((ch = (gdbstub_read_wait())) != '$') 10453197fc4SJason Wessel /* nothing */; 10553197fc4SJason Wessel 10653197fc4SJason Wessel kgdb_connected = 1; 10753197fc4SJason Wessel checksum = 0; 10853197fc4SJason Wessel xmitcsum = -1; 10953197fc4SJason Wessel 11053197fc4SJason Wessel count = 0; 11153197fc4SJason Wessel 11253197fc4SJason Wessel /* 11353197fc4SJason Wessel * now, read until a # or end of buffer is found: 11453197fc4SJason Wessel */ 11553197fc4SJason Wessel while (count < (BUFMAX - 1)) { 116f5316b4aSJason Wessel ch = gdbstub_read_wait(); 11753197fc4SJason Wessel if (ch == '#') 11853197fc4SJason Wessel break; 11953197fc4SJason Wessel checksum = checksum + ch; 12053197fc4SJason Wessel buffer[count] = ch; 12153197fc4SJason Wessel count = count + 1; 12253197fc4SJason Wessel } 12353197fc4SJason Wessel 12453197fc4SJason Wessel if (ch == '#') { 125a9fa20a7SAndy Shevchenko xmitcsum = hex_to_bin(gdbstub_read_wait()) << 4; 126a9fa20a7SAndy Shevchenko xmitcsum += hex_to_bin(gdbstub_read_wait()); 12753197fc4SJason Wessel 12853197fc4SJason Wessel if (checksum != xmitcsum) 12953197fc4SJason Wessel /* failed checksum */ 13053197fc4SJason Wessel dbg_io_ops->write_char('-'); 13153197fc4SJason Wessel else 13253197fc4SJason Wessel /* successful transfer */ 13353197fc4SJason Wessel dbg_io_ops->write_char('+'); 13453197fc4SJason Wessel if (dbg_io_ops->flush) 13553197fc4SJason Wessel dbg_io_ops->flush(); 13653197fc4SJason Wessel } 137f679c498SJason Wessel buffer[count] = 0; 13853197fc4SJason Wessel } while (checksum != xmitcsum); 13953197fc4SJason Wessel } 14053197fc4SJason Wessel 14153197fc4SJason Wessel /* 14253197fc4SJason Wessel * Send the packet in buffer. 14353197fc4SJason Wessel * Check for gdb connection if asked for. 14453197fc4SJason Wessel */ 14553197fc4SJason Wessel static void put_packet(char *buffer) 14653197fc4SJason Wessel { 14753197fc4SJason Wessel unsigned char checksum; 14853197fc4SJason Wessel int count; 14953197fc4SJason Wessel char ch; 15053197fc4SJason Wessel 15153197fc4SJason Wessel /* 15253197fc4SJason Wessel * $<packet info>#<checksum>. 15353197fc4SJason Wessel */ 15453197fc4SJason Wessel while (1) { 15553197fc4SJason Wessel dbg_io_ops->write_char('$'); 15653197fc4SJason Wessel checksum = 0; 15753197fc4SJason Wessel count = 0; 15853197fc4SJason Wessel 15953197fc4SJason Wessel while ((ch = buffer[count])) { 16053197fc4SJason Wessel dbg_io_ops->write_char(ch); 16153197fc4SJason Wessel checksum += ch; 16253197fc4SJason Wessel count++; 16353197fc4SJason Wessel } 16453197fc4SJason Wessel 16553197fc4SJason Wessel dbg_io_ops->write_char('#'); 16653197fc4SJason Wessel dbg_io_ops->write_char(hex_asc_hi(checksum)); 16753197fc4SJason Wessel dbg_io_ops->write_char(hex_asc_lo(checksum)); 16853197fc4SJason Wessel if (dbg_io_ops->flush) 16953197fc4SJason Wessel dbg_io_ops->flush(); 17053197fc4SJason Wessel 17153197fc4SJason Wessel /* Now see what we get in reply. */ 172f5316b4aSJason Wessel ch = gdbstub_read_wait(); 17353197fc4SJason Wessel 17453197fc4SJason Wessel if (ch == 3) 175f5316b4aSJason Wessel ch = gdbstub_read_wait(); 17653197fc4SJason Wessel 17753197fc4SJason Wessel /* If we get an ACK, we are done. */ 17853197fc4SJason Wessel if (ch == '+') 17953197fc4SJason Wessel return; 18053197fc4SJason Wessel 18153197fc4SJason Wessel /* 18253197fc4SJason Wessel * If we get the start of another packet, this means 18353197fc4SJason Wessel * that GDB is attempting to reconnect. We will NAK 18453197fc4SJason Wessel * the packet being sent, and stop trying to send this 18553197fc4SJason Wessel * packet. 18653197fc4SJason Wessel */ 18753197fc4SJason Wessel if (ch == '$') { 18853197fc4SJason Wessel dbg_io_ops->write_char('-'); 18953197fc4SJason Wessel if (dbg_io_ops->flush) 19053197fc4SJason Wessel dbg_io_ops->flush(); 19153197fc4SJason Wessel return; 19253197fc4SJason Wessel } 19353197fc4SJason Wessel } 19453197fc4SJason Wessel } 19553197fc4SJason Wessel 19653197fc4SJason Wessel static char gdbmsgbuf[BUFMAX + 1]; 19753197fc4SJason Wessel 19853197fc4SJason Wessel void gdbstub_msg_write(const char *s, int len) 19953197fc4SJason Wessel { 20053197fc4SJason Wessel char *bufptr; 20153197fc4SJason Wessel int wcount; 20253197fc4SJason Wessel int i; 20353197fc4SJason Wessel 204a0de055cSJason Wessel if (len == 0) 205a0de055cSJason Wessel len = strlen(s); 206a0de055cSJason Wessel 20753197fc4SJason Wessel /* 'O'utput */ 20853197fc4SJason Wessel gdbmsgbuf[0] = 'O'; 20953197fc4SJason Wessel 21053197fc4SJason Wessel /* Fill and send buffers... */ 21153197fc4SJason Wessel while (len > 0) { 21253197fc4SJason Wessel bufptr = gdbmsgbuf + 1; 21353197fc4SJason Wessel 21453197fc4SJason Wessel /* Calculate how many this time */ 21553197fc4SJason Wessel if ((len << 1) > (BUFMAX - 2)) 21653197fc4SJason Wessel wcount = (BUFMAX - 2) >> 1; 21753197fc4SJason Wessel else 21853197fc4SJason Wessel wcount = len; 21953197fc4SJason Wessel 22053197fc4SJason Wessel /* Pack in hex chars */ 22153197fc4SJason Wessel for (i = 0; i < wcount; i++) 22250e1499fSAndy Shevchenko bufptr = hex_byte_pack(bufptr, s[i]); 22353197fc4SJason Wessel *bufptr = '\0'; 22453197fc4SJason Wessel 22553197fc4SJason Wessel /* Move up */ 22653197fc4SJason Wessel s += wcount; 22753197fc4SJason Wessel len -= wcount; 22853197fc4SJason Wessel 22953197fc4SJason Wessel /* Write packet */ 23053197fc4SJason Wessel put_packet(gdbmsgbuf); 23153197fc4SJason Wessel } 23253197fc4SJason Wessel } 23353197fc4SJason Wessel 23453197fc4SJason Wessel /* 23553197fc4SJason Wessel * Convert the memory pointed to by mem into hex, placing result in 23653197fc4SJason Wessel * buf. Return a pointer to the last char put in buf (null). May 23753197fc4SJason Wessel * return an error. 23853197fc4SJason Wessel */ 23955751145SJason Wessel char *kgdb_mem2hex(char *mem, char *buf, int count) 24053197fc4SJason Wessel { 24153197fc4SJason Wessel char *tmp; 24253197fc4SJason Wessel int err; 24353197fc4SJason Wessel 24453197fc4SJason Wessel /* 24553197fc4SJason Wessel * We use the upper half of buf as an intermediate buffer for the 24653197fc4SJason Wessel * raw memory copy. Hex conversion will work against this one. 24753197fc4SJason Wessel */ 24853197fc4SJason Wessel tmp = buf + count; 24953197fc4SJason Wessel 250fe557319SChristoph Hellwig err = copy_from_kernel_nofault(tmp, mem, count); 25155751145SJason Wessel if (err) 25255751145SJason Wessel return NULL; 25353197fc4SJason Wessel while (count > 0) { 25450e1499fSAndy Shevchenko buf = hex_byte_pack(buf, *tmp); 25553197fc4SJason Wessel tmp++; 25653197fc4SJason Wessel count--; 25753197fc4SJason Wessel } 25853197fc4SJason Wessel *buf = 0; 25953197fc4SJason Wessel 26055751145SJason Wessel return buf; 26153197fc4SJason Wessel } 26253197fc4SJason Wessel 26353197fc4SJason Wessel /* 26453197fc4SJason Wessel * Convert the hex array pointed to by buf into binary to be placed in 26553197fc4SJason Wessel * mem. Return a pointer to the character AFTER the last byte 26653197fc4SJason Wessel * written. May return an error. 26753197fc4SJason Wessel */ 26853197fc4SJason Wessel int kgdb_hex2mem(char *buf, char *mem, int count) 26953197fc4SJason Wessel { 27053197fc4SJason Wessel char *tmp_raw; 27153197fc4SJason Wessel char *tmp_hex; 27253197fc4SJason Wessel 27353197fc4SJason Wessel /* 27453197fc4SJason Wessel * We use the upper half of buf as an intermediate buffer for the 27553197fc4SJason Wessel * raw memory that is converted from hex. 27653197fc4SJason Wessel */ 27753197fc4SJason Wessel tmp_raw = buf + count * 2; 27853197fc4SJason Wessel 27953197fc4SJason Wessel tmp_hex = tmp_raw - 1; 28053197fc4SJason Wessel while (tmp_hex >= buf) { 28153197fc4SJason Wessel tmp_raw--; 282a9fa20a7SAndy Shevchenko *tmp_raw = hex_to_bin(*tmp_hex--); 283a9fa20a7SAndy Shevchenko *tmp_raw |= hex_to_bin(*tmp_hex--) << 4; 28453197fc4SJason Wessel } 28553197fc4SJason Wessel 286fe557319SChristoph Hellwig return copy_to_kernel_nofault(mem, tmp_raw, count); 28753197fc4SJason Wessel } 28853197fc4SJason Wessel 28953197fc4SJason Wessel /* 29053197fc4SJason Wessel * While we find nice hex chars, build a long_val. 29153197fc4SJason Wessel * Return number of chars processed. 29253197fc4SJason Wessel */ 29353197fc4SJason Wessel int kgdb_hex2long(char **ptr, unsigned long *long_val) 29453197fc4SJason Wessel { 29553197fc4SJason Wessel int hex_val; 29653197fc4SJason Wessel int num = 0; 29753197fc4SJason Wessel int negate = 0; 29853197fc4SJason Wessel 29953197fc4SJason Wessel *long_val = 0; 30053197fc4SJason Wessel 30153197fc4SJason Wessel if (**ptr == '-') { 30253197fc4SJason Wessel negate = 1; 30353197fc4SJason Wessel (*ptr)++; 30453197fc4SJason Wessel } 30553197fc4SJason Wessel while (**ptr) { 306a9fa20a7SAndy Shevchenko hex_val = hex_to_bin(**ptr); 30753197fc4SJason Wessel if (hex_val < 0) 30853197fc4SJason Wessel break; 30953197fc4SJason Wessel 31053197fc4SJason Wessel *long_val = (*long_val << 4) | hex_val; 31153197fc4SJason Wessel num++; 31253197fc4SJason Wessel (*ptr)++; 31353197fc4SJason Wessel } 31453197fc4SJason Wessel 31553197fc4SJason Wessel if (negate) 31653197fc4SJason Wessel *long_val = -*long_val; 31753197fc4SJason Wessel 31853197fc4SJason Wessel return num; 31953197fc4SJason Wessel } 32053197fc4SJason Wessel 32153197fc4SJason Wessel /* 32253197fc4SJason Wessel * Copy the binary array pointed to by buf into mem. Fix $, #, and 32353197fc4SJason Wessel * 0x7d escaped with 0x7d. Return -EFAULT on failure or 0 on success. 32453197fc4SJason Wessel * The input buf is overwitten with the result to write to mem. 32553197fc4SJason Wessel */ 32653197fc4SJason Wessel static int kgdb_ebin2mem(char *buf, char *mem, int count) 32753197fc4SJason Wessel { 32853197fc4SJason Wessel int size = 0; 32953197fc4SJason Wessel char *c = buf; 33053197fc4SJason Wessel 33153197fc4SJason Wessel while (count-- > 0) { 33253197fc4SJason Wessel c[size] = *buf++; 33353197fc4SJason Wessel if (c[size] == 0x7d) 33453197fc4SJason Wessel c[size] = *buf++ ^ 0x20; 33553197fc4SJason Wessel size++; 33653197fc4SJason Wessel } 33753197fc4SJason Wessel 338fe557319SChristoph Hellwig return copy_to_kernel_nofault(mem, c, size); 33953197fc4SJason Wessel } 34053197fc4SJason Wessel 341534af108SJason Wessel #if DBG_MAX_REG_NUM > 0 342534af108SJason Wessel void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) 343534af108SJason Wessel { 344534af108SJason Wessel int i; 345534af108SJason Wessel int idx = 0; 346534af108SJason Wessel char *ptr = (char *)gdb_regs; 347534af108SJason Wessel 348534af108SJason Wessel for (i = 0; i < DBG_MAX_REG_NUM; i++) { 349534af108SJason Wessel dbg_get_reg(i, ptr + idx, regs); 350534af108SJason Wessel idx += dbg_reg_def[i].size; 351534af108SJason Wessel } 352534af108SJason Wessel } 353534af108SJason Wessel 354534af108SJason Wessel void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) 355534af108SJason Wessel { 356534af108SJason Wessel int i; 357534af108SJason Wessel int idx = 0; 358534af108SJason Wessel char *ptr = (char *)gdb_regs; 359534af108SJason Wessel 360534af108SJason Wessel for (i = 0; i < DBG_MAX_REG_NUM; i++) { 361534af108SJason Wessel dbg_set_reg(i, ptr + idx, regs); 362534af108SJason Wessel idx += dbg_reg_def[i].size; 363534af108SJason Wessel } 364534af108SJason Wessel } 365534af108SJason Wessel #endif /* DBG_MAX_REG_NUM > 0 */ 366534af108SJason Wessel 36753197fc4SJason Wessel /* Write memory due to an 'M' or 'X' packet. */ 36853197fc4SJason Wessel static int write_mem_msg(int binary) 36953197fc4SJason Wessel { 37053197fc4SJason Wessel char *ptr = &remcom_in_buffer[1]; 37153197fc4SJason Wessel unsigned long addr; 37253197fc4SJason Wessel unsigned long length; 37353197fc4SJason Wessel int err; 37453197fc4SJason Wessel 37553197fc4SJason Wessel if (kgdb_hex2long(&ptr, &addr) > 0 && *(ptr++) == ',' && 37653197fc4SJason Wessel kgdb_hex2long(&ptr, &length) > 0 && *(ptr++) == ':') { 37753197fc4SJason Wessel if (binary) 37853197fc4SJason Wessel err = kgdb_ebin2mem(ptr, (char *)addr, length); 37953197fc4SJason Wessel else 38053197fc4SJason Wessel err = kgdb_hex2mem(ptr, (char *)addr, length); 38153197fc4SJason Wessel if (err) 38253197fc4SJason Wessel return err; 38353197fc4SJason Wessel if (CACHE_FLUSH_IS_SAFE) 38453197fc4SJason Wessel flush_icache_range(addr, addr + length); 38553197fc4SJason Wessel return 0; 38653197fc4SJason Wessel } 38753197fc4SJason Wessel 38853197fc4SJason Wessel return -EINVAL; 38953197fc4SJason Wessel } 39053197fc4SJason Wessel 39153197fc4SJason Wessel static void error_packet(char *pkt, int error) 39253197fc4SJason Wessel { 39353197fc4SJason Wessel error = -error; 39453197fc4SJason Wessel pkt[0] = 'E'; 39553197fc4SJason Wessel pkt[1] = hex_asc[(error / 10)]; 39653197fc4SJason Wessel pkt[2] = hex_asc[(error % 10)]; 39753197fc4SJason Wessel pkt[3] = '\0'; 39853197fc4SJason Wessel } 39953197fc4SJason Wessel 40053197fc4SJason Wessel /* 40153197fc4SJason Wessel * Thread ID accessors. We represent a flat TID space to GDB, where 40253197fc4SJason Wessel * the per CPU idle threads (which under Linux all have PID 0) are 40353197fc4SJason Wessel * remapped to negative TIDs. 40453197fc4SJason Wessel */ 40553197fc4SJason Wessel 40684a0bd5bSJason Wessel #define BUF_THREAD_ID_SIZE 8 40753197fc4SJason Wessel 40853197fc4SJason Wessel static char *pack_threadid(char *pkt, unsigned char *id) 40953197fc4SJason Wessel { 41084a0bd5bSJason Wessel unsigned char *limit; 41184a0bd5bSJason Wessel int lzero = 1; 41253197fc4SJason Wessel 41384a0bd5bSJason Wessel limit = id + (BUF_THREAD_ID_SIZE / 2); 41484a0bd5bSJason Wessel while (id < limit) { 41584a0bd5bSJason Wessel if (!lzero || *id != 0) { 41650e1499fSAndy Shevchenko pkt = hex_byte_pack(pkt, *id); 41784a0bd5bSJason Wessel lzero = 0; 41884a0bd5bSJason Wessel } 41984a0bd5bSJason Wessel id++; 42084a0bd5bSJason Wessel } 42184a0bd5bSJason Wessel 42284a0bd5bSJason Wessel if (lzero) 42350e1499fSAndy Shevchenko pkt = hex_byte_pack(pkt, 0); 42453197fc4SJason Wessel 42553197fc4SJason Wessel return pkt; 42653197fc4SJason Wessel } 42753197fc4SJason Wessel 42853197fc4SJason Wessel static void int_to_threadref(unsigned char *id, int value) 42953197fc4SJason Wessel { 43084a0bd5bSJason Wessel put_unaligned_be32(value, id); 43153197fc4SJason Wessel } 43253197fc4SJason Wessel 43353197fc4SJason Wessel static struct task_struct *getthread(struct pt_regs *regs, int tid) 43453197fc4SJason Wessel { 43553197fc4SJason Wessel /* 43653197fc4SJason Wessel * Non-positive TIDs are remapped to the cpu shadow information 43753197fc4SJason Wessel */ 43853197fc4SJason Wessel if (tid == 0 || tid == -1) 43953197fc4SJason Wessel tid = -atomic_read(&kgdb_active) - 2; 44053197fc4SJason Wessel if (tid < -1 && tid > -NR_CPUS - 2) { 44153197fc4SJason Wessel if (kgdb_info[-tid - 2].task) 44253197fc4SJason Wessel return kgdb_info[-tid - 2].task; 44353197fc4SJason Wessel else 44453197fc4SJason Wessel return idle_task(-tid - 2); 44553197fc4SJason Wessel } 44653197fc4SJason Wessel if (tid <= 0) { 44753197fc4SJason Wessel printk(KERN_ERR "KGDB: Internal thread select error\n"); 44853197fc4SJason Wessel dump_stack(); 44953197fc4SJason Wessel return NULL; 45053197fc4SJason Wessel } 45153197fc4SJason Wessel 45253197fc4SJason Wessel /* 45353197fc4SJason Wessel * find_task_by_pid_ns() does not take the tasklist lock anymore 45453197fc4SJason Wessel * but is nicely RCU locked - hence is a pretty resilient 45553197fc4SJason Wessel * thing to use: 45653197fc4SJason Wessel */ 45753197fc4SJason Wessel return find_task_by_pid_ns(tid, &init_pid_ns); 45853197fc4SJason Wessel } 45953197fc4SJason Wessel 46053197fc4SJason Wessel 46153197fc4SJason Wessel /* 46253197fc4SJason Wessel * Remap normal tasks to their real PID, 46353197fc4SJason Wessel * CPU shadow threads are mapped to -CPU - 2 46453197fc4SJason Wessel */ 46553197fc4SJason Wessel static inline int shadow_pid(int realpid) 46653197fc4SJason Wessel { 46753197fc4SJason Wessel if (realpid) 46853197fc4SJason Wessel return realpid; 46953197fc4SJason Wessel 47053197fc4SJason Wessel return -raw_smp_processor_id() - 2; 47153197fc4SJason Wessel } 47253197fc4SJason Wessel 47353197fc4SJason Wessel /* 47453197fc4SJason Wessel * All the functions that start with gdb_cmd are the various 47553197fc4SJason Wessel * operations to implement the handlers for the gdbserial protocol 47653197fc4SJason Wessel * where KGDB is communicating with an external debugger 47753197fc4SJason Wessel */ 47853197fc4SJason Wessel 47953197fc4SJason Wessel /* Handle the '?' status packets */ 48053197fc4SJason Wessel static void gdb_cmd_status(struct kgdb_state *ks) 48153197fc4SJason Wessel { 48253197fc4SJason Wessel /* 48353197fc4SJason Wessel * We know that this packet is only sent 48453197fc4SJason Wessel * during initial connect. So to be safe, 48553197fc4SJason Wessel * we clear out our breakpoints now in case 48653197fc4SJason Wessel * GDB is reconnecting. 48753197fc4SJason Wessel */ 48853197fc4SJason Wessel dbg_remove_all_break(); 48953197fc4SJason Wessel 49053197fc4SJason Wessel remcom_out_buffer[0] = 'S'; 49150e1499fSAndy Shevchenko hex_byte_pack(&remcom_out_buffer[1], ks->signo); 49253197fc4SJason Wessel } 49353197fc4SJason Wessel 49455751145SJason Wessel static void gdb_get_regs_helper(struct kgdb_state *ks) 49553197fc4SJason Wessel { 49653197fc4SJason Wessel struct task_struct *thread; 49753197fc4SJason Wessel void *local_debuggerinfo; 49853197fc4SJason Wessel int i; 49953197fc4SJason Wessel 50053197fc4SJason Wessel thread = kgdb_usethread; 50153197fc4SJason Wessel if (!thread) { 50253197fc4SJason Wessel thread = kgdb_info[ks->cpu].task; 50353197fc4SJason Wessel local_debuggerinfo = kgdb_info[ks->cpu].debuggerinfo; 50453197fc4SJason Wessel } else { 50553197fc4SJason Wessel local_debuggerinfo = NULL; 50653197fc4SJason Wessel for_each_online_cpu(i) { 50753197fc4SJason Wessel /* 50853197fc4SJason Wessel * Try to find the task on some other 50953197fc4SJason Wessel * or possibly this node if we do not 51053197fc4SJason Wessel * find the matching task then we try 51153197fc4SJason Wessel * to approximate the results. 51253197fc4SJason Wessel */ 51353197fc4SJason Wessel if (thread == kgdb_info[i].task) 51453197fc4SJason Wessel local_debuggerinfo = kgdb_info[i].debuggerinfo; 51553197fc4SJason Wessel } 51653197fc4SJason Wessel } 51753197fc4SJason Wessel 51853197fc4SJason Wessel /* 51953197fc4SJason Wessel * All threads that don't have debuggerinfo should be 52053197fc4SJason Wessel * in schedule() sleeping, since all other CPUs 52153197fc4SJason Wessel * are in kgdb_wait, and thus have debuggerinfo. 52253197fc4SJason Wessel */ 52353197fc4SJason Wessel if (local_debuggerinfo) { 52453197fc4SJason Wessel pt_regs_to_gdb_regs(gdb_regs, local_debuggerinfo); 52553197fc4SJason Wessel } else { 52653197fc4SJason Wessel /* 52753197fc4SJason Wessel * Pull stuff saved during switch_to; nothing 52853197fc4SJason Wessel * else is accessible (or even particularly 52953197fc4SJason Wessel * relevant). 53053197fc4SJason Wessel * 53153197fc4SJason Wessel * This should be enough for a stack trace. 53253197fc4SJason Wessel */ 53353197fc4SJason Wessel sleeping_thread_to_gdb_regs(gdb_regs, thread); 53453197fc4SJason Wessel } 53555751145SJason Wessel } 53655751145SJason Wessel 53755751145SJason Wessel /* Handle the 'g' get registers request */ 53855751145SJason Wessel static void gdb_cmd_getregs(struct kgdb_state *ks) 53955751145SJason Wessel { 54055751145SJason Wessel gdb_get_regs_helper(ks); 54153197fc4SJason Wessel kgdb_mem2hex((char *)gdb_regs, remcom_out_buffer, NUMREGBYTES); 54253197fc4SJason Wessel } 54353197fc4SJason Wessel 54453197fc4SJason Wessel /* Handle the 'G' set registers request */ 54553197fc4SJason Wessel static void gdb_cmd_setregs(struct kgdb_state *ks) 54653197fc4SJason Wessel { 54753197fc4SJason Wessel kgdb_hex2mem(&remcom_in_buffer[1], (char *)gdb_regs, NUMREGBYTES); 54853197fc4SJason Wessel 54953197fc4SJason Wessel if (kgdb_usethread && kgdb_usethread != current) { 55053197fc4SJason Wessel error_packet(remcom_out_buffer, -EINVAL); 55153197fc4SJason Wessel } else { 55253197fc4SJason Wessel gdb_regs_to_pt_regs(gdb_regs, ks->linux_regs); 55353197fc4SJason Wessel strcpy(remcom_out_buffer, "OK"); 55453197fc4SJason Wessel } 55553197fc4SJason Wessel } 55653197fc4SJason Wessel 55753197fc4SJason Wessel /* Handle the 'm' memory read bytes */ 55853197fc4SJason Wessel static void gdb_cmd_memread(struct kgdb_state *ks) 55953197fc4SJason Wessel { 56053197fc4SJason Wessel char *ptr = &remcom_in_buffer[1]; 56153197fc4SJason Wessel unsigned long length; 56253197fc4SJason Wessel unsigned long addr; 56355751145SJason Wessel char *err; 56453197fc4SJason Wessel 56553197fc4SJason Wessel if (kgdb_hex2long(&ptr, &addr) > 0 && *ptr++ == ',' && 56653197fc4SJason Wessel kgdb_hex2long(&ptr, &length) > 0) { 56753197fc4SJason Wessel err = kgdb_mem2hex((char *)addr, remcom_out_buffer, length); 56855751145SJason Wessel if (!err) 56955751145SJason Wessel error_packet(remcom_out_buffer, -EINVAL); 57053197fc4SJason Wessel } else { 57153197fc4SJason Wessel error_packet(remcom_out_buffer, -EINVAL); 57253197fc4SJason Wessel } 57353197fc4SJason Wessel } 57453197fc4SJason Wessel 57553197fc4SJason Wessel /* Handle the 'M' memory write bytes */ 57653197fc4SJason Wessel static void gdb_cmd_memwrite(struct kgdb_state *ks) 57753197fc4SJason Wessel { 57853197fc4SJason Wessel int err = write_mem_msg(0); 57953197fc4SJason Wessel 58053197fc4SJason Wessel if (err) 58153197fc4SJason Wessel error_packet(remcom_out_buffer, err); 58253197fc4SJason Wessel else 58353197fc4SJason Wessel strcpy(remcom_out_buffer, "OK"); 58453197fc4SJason Wessel } 58553197fc4SJason Wessel 58655751145SJason Wessel #if DBG_MAX_REG_NUM > 0 58755751145SJason Wessel static char *gdb_hex_reg_helper(int regnum, char *out) 58855751145SJason Wessel { 58955751145SJason Wessel int i; 59055751145SJason Wessel int offset = 0; 59155751145SJason Wessel 59255751145SJason Wessel for (i = 0; i < regnum; i++) 59355751145SJason Wessel offset += dbg_reg_def[i].size; 59455751145SJason Wessel return kgdb_mem2hex((char *)gdb_regs + offset, out, 59555751145SJason Wessel dbg_reg_def[i].size); 59655751145SJason Wessel } 59755751145SJason Wessel 59855751145SJason Wessel /* Handle the 'p' individual regster get */ 59955751145SJason Wessel static void gdb_cmd_reg_get(struct kgdb_state *ks) 60055751145SJason Wessel { 60155751145SJason Wessel unsigned long regnum; 60255751145SJason Wessel char *ptr = &remcom_in_buffer[1]; 60355751145SJason Wessel 60455751145SJason Wessel kgdb_hex2long(&ptr, ®num); 60555751145SJason Wessel if (regnum >= DBG_MAX_REG_NUM) { 60655751145SJason Wessel error_packet(remcom_out_buffer, -EINVAL); 60755751145SJason Wessel return; 60855751145SJason Wessel } 60955751145SJason Wessel gdb_get_regs_helper(ks); 61055751145SJason Wessel gdb_hex_reg_helper(regnum, remcom_out_buffer); 61155751145SJason Wessel } 61255751145SJason Wessel 61355751145SJason Wessel /* Handle the 'P' individual regster set */ 61455751145SJason Wessel static void gdb_cmd_reg_set(struct kgdb_state *ks) 61555751145SJason Wessel { 61655751145SJason Wessel unsigned long regnum; 61755751145SJason Wessel char *ptr = &remcom_in_buffer[1]; 6186d855b1dSJason Wessel int i = 0; 61955751145SJason Wessel 62055751145SJason Wessel kgdb_hex2long(&ptr, ®num); 62155751145SJason Wessel if (*ptr++ != '=' || 62255751145SJason Wessel !(!kgdb_usethread || kgdb_usethread == current) || 62355751145SJason Wessel !dbg_get_reg(regnum, gdb_regs, ks->linux_regs)) { 62455751145SJason Wessel error_packet(remcom_out_buffer, -EINVAL); 62555751145SJason Wessel return; 62655751145SJason Wessel } 6276d855b1dSJason Wessel memset(gdb_regs, 0, sizeof(gdb_regs)); 6286d855b1dSJason Wessel while (i < sizeof(gdb_regs) * 2) 6296d855b1dSJason Wessel if (hex_to_bin(ptr[i]) >= 0) 6306d855b1dSJason Wessel i++; 6316d855b1dSJason Wessel else 6326d855b1dSJason Wessel break; 6336d855b1dSJason Wessel i = i / 2; 6346d855b1dSJason Wessel kgdb_hex2mem(ptr, (char *)gdb_regs, i); 63555751145SJason Wessel dbg_set_reg(regnum, gdb_regs, ks->linux_regs); 63655751145SJason Wessel strcpy(remcom_out_buffer, "OK"); 63755751145SJason Wessel } 63855751145SJason Wessel #endif /* DBG_MAX_REG_NUM > 0 */ 63955751145SJason Wessel 64053197fc4SJason Wessel /* Handle the 'X' memory binary write bytes */ 64153197fc4SJason Wessel static void gdb_cmd_binwrite(struct kgdb_state *ks) 64253197fc4SJason Wessel { 64353197fc4SJason Wessel int err = write_mem_msg(1); 64453197fc4SJason Wessel 64553197fc4SJason Wessel if (err) 64653197fc4SJason Wessel error_packet(remcom_out_buffer, err); 64753197fc4SJason Wessel else 64853197fc4SJason Wessel strcpy(remcom_out_buffer, "OK"); 64953197fc4SJason Wessel } 65053197fc4SJason Wessel 65153197fc4SJason Wessel /* Handle the 'D' or 'k', detach or kill packets */ 65253197fc4SJason Wessel static void gdb_cmd_detachkill(struct kgdb_state *ks) 65353197fc4SJason Wessel { 65453197fc4SJason Wessel int error; 65553197fc4SJason Wessel 65653197fc4SJason Wessel /* The detach case */ 65753197fc4SJason Wessel if (remcom_in_buffer[0] == 'D') { 65853197fc4SJason Wessel error = dbg_remove_all_break(); 65953197fc4SJason Wessel if (error < 0) { 66053197fc4SJason Wessel error_packet(remcom_out_buffer, error); 66153197fc4SJason Wessel } else { 66253197fc4SJason Wessel strcpy(remcom_out_buffer, "OK"); 66353197fc4SJason Wessel kgdb_connected = 0; 66453197fc4SJason Wessel } 66553197fc4SJason Wessel put_packet(remcom_out_buffer); 66653197fc4SJason Wessel } else { 66753197fc4SJason Wessel /* 66853197fc4SJason Wessel * Assume the kill case, with no exit code checking, 66953197fc4SJason Wessel * trying to force detach the debugger: 67053197fc4SJason Wessel */ 67153197fc4SJason Wessel dbg_remove_all_break(); 67253197fc4SJason Wessel kgdb_connected = 0; 67353197fc4SJason Wessel } 67453197fc4SJason Wessel } 67553197fc4SJason Wessel 67653197fc4SJason Wessel /* Handle the 'R' reboot packets */ 67753197fc4SJason Wessel static int gdb_cmd_reboot(struct kgdb_state *ks) 67853197fc4SJason Wessel { 67953197fc4SJason Wessel /* For now, only honor R0 */ 68053197fc4SJason Wessel if (strcmp(remcom_in_buffer, "R0") == 0) { 68153197fc4SJason Wessel printk(KERN_CRIT "Executing emergency reboot\n"); 68253197fc4SJason Wessel strcpy(remcom_out_buffer, "OK"); 68353197fc4SJason Wessel put_packet(remcom_out_buffer); 68453197fc4SJason Wessel 68553197fc4SJason Wessel /* 68653197fc4SJason Wessel * Execution should not return from 68753197fc4SJason Wessel * machine_emergency_restart() 68853197fc4SJason Wessel */ 68953197fc4SJason Wessel machine_emergency_restart(); 69053197fc4SJason Wessel kgdb_connected = 0; 69153197fc4SJason Wessel 69253197fc4SJason Wessel return 1; 69353197fc4SJason Wessel } 69453197fc4SJason Wessel return 0; 69553197fc4SJason Wessel } 69653197fc4SJason Wessel 69753197fc4SJason Wessel /* Handle the 'q' query packets */ 69853197fc4SJason Wessel static void gdb_cmd_query(struct kgdb_state *ks) 69953197fc4SJason Wessel { 70053197fc4SJason Wessel struct task_struct *g; 70153197fc4SJason Wessel struct task_struct *p; 70284a0bd5bSJason Wessel unsigned char thref[BUF_THREAD_ID_SIZE]; 70353197fc4SJason Wessel char *ptr; 70453197fc4SJason Wessel int i; 70553197fc4SJason Wessel int cpu; 70653197fc4SJason Wessel int finished = 0; 70753197fc4SJason Wessel 70853197fc4SJason Wessel switch (remcom_in_buffer[1]) { 70953197fc4SJason Wessel case 's': 71053197fc4SJason Wessel case 'f': 711fb82c0ffSJason Wessel if (memcmp(remcom_in_buffer + 2, "ThreadInfo", 10)) 71253197fc4SJason Wessel break; 71353197fc4SJason Wessel 71453197fc4SJason Wessel i = 0; 71553197fc4SJason Wessel remcom_out_buffer[0] = 'm'; 71653197fc4SJason Wessel ptr = remcom_out_buffer + 1; 71753197fc4SJason Wessel if (remcom_in_buffer[1] == 'f') { 71853197fc4SJason Wessel /* Each cpu is a shadow thread */ 71953197fc4SJason Wessel for_each_online_cpu(cpu) { 72053197fc4SJason Wessel ks->thr_query = 0; 72153197fc4SJason Wessel int_to_threadref(thref, -cpu - 2); 72284a0bd5bSJason Wessel ptr = pack_threadid(ptr, thref); 72353197fc4SJason Wessel *(ptr++) = ','; 72453197fc4SJason Wessel i++; 72553197fc4SJason Wessel } 72653197fc4SJason Wessel } 72753197fc4SJason Wessel 72853197fc4SJason Wessel do_each_thread(g, p) { 72953197fc4SJason Wessel if (i >= ks->thr_query && !finished) { 73053197fc4SJason Wessel int_to_threadref(thref, p->pid); 73184a0bd5bSJason Wessel ptr = pack_threadid(ptr, thref); 73253197fc4SJason Wessel *(ptr++) = ','; 73353197fc4SJason Wessel ks->thr_query++; 73453197fc4SJason Wessel if (ks->thr_query % KGDB_MAX_THREAD_QUERY == 0) 73553197fc4SJason Wessel finished = 1; 73653197fc4SJason Wessel } 73753197fc4SJason Wessel i++; 73853197fc4SJason Wessel } while_each_thread(g, p); 73953197fc4SJason Wessel 74053197fc4SJason Wessel *(--ptr) = '\0'; 74153197fc4SJason Wessel break; 74253197fc4SJason Wessel 74353197fc4SJason Wessel case 'C': 74453197fc4SJason Wessel /* Current thread id */ 74553197fc4SJason Wessel strcpy(remcom_out_buffer, "QC"); 74653197fc4SJason Wessel ks->threadid = shadow_pid(current->pid); 74753197fc4SJason Wessel int_to_threadref(thref, ks->threadid); 74853197fc4SJason Wessel pack_threadid(remcom_out_buffer + 2, thref); 74953197fc4SJason Wessel break; 75053197fc4SJason Wessel case 'T': 751fb82c0ffSJason Wessel if (memcmp(remcom_in_buffer + 1, "ThreadExtraInfo,", 16)) 75253197fc4SJason Wessel break; 753fb82c0ffSJason Wessel 75453197fc4SJason Wessel ks->threadid = 0; 75553197fc4SJason Wessel ptr = remcom_in_buffer + 17; 75653197fc4SJason Wessel kgdb_hex2long(&ptr, &ks->threadid); 75753197fc4SJason Wessel if (!getthread(ks->linux_regs, ks->threadid)) { 75853197fc4SJason Wessel error_packet(remcom_out_buffer, -EINVAL); 75953197fc4SJason Wessel break; 76053197fc4SJason Wessel } 76153197fc4SJason Wessel if ((int)ks->threadid > 0) { 76253197fc4SJason Wessel kgdb_mem2hex(getthread(ks->linux_regs, 76353197fc4SJason Wessel ks->threadid)->comm, 76453197fc4SJason Wessel remcom_out_buffer, 16); 76553197fc4SJason Wessel } else { 76653197fc4SJason Wessel static char tmpstr[23 + BUF_THREAD_ID_SIZE]; 76753197fc4SJason Wessel 76853197fc4SJason Wessel sprintf(tmpstr, "shadowCPU%d", 76953197fc4SJason Wessel (int)(-ks->threadid - 2)); 77053197fc4SJason Wessel kgdb_mem2hex(tmpstr, remcom_out_buffer, strlen(tmpstr)); 77153197fc4SJason Wessel } 77253197fc4SJason Wessel break; 773a0de055cSJason Wessel #ifdef CONFIG_KGDB_KDB 774a0de055cSJason Wessel case 'R': 775a0de055cSJason Wessel if (strncmp(remcom_in_buffer, "qRcmd,", 6) == 0) { 776a0de055cSJason Wessel int len = strlen(remcom_in_buffer + 6); 777a0de055cSJason Wessel 778a0de055cSJason Wessel if ((len % 2) != 0) { 779a0de055cSJason Wessel strcpy(remcom_out_buffer, "E01"); 780a0de055cSJason Wessel break; 781a0de055cSJason Wessel } 782a0de055cSJason Wessel kgdb_hex2mem(remcom_in_buffer + 6, 783a0de055cSJason Wessel remcom_out_buffer, len); 784a0de055cSJason Wessel len = len / 2; 785a0de055cSJason Wessel remcom_out_buffer[len++] = 0; 786a0de055cSJason Wessel 78700370b8fSMatt Klein kdb_common_init_state(ks); 788a0de055cSJason Wessel kdb_parse(remcom_out_buffer); 78900370b8fSMatt Klein kdb_common_deinit_state(); 79000370b8fSMatt Klein 791a0de055cSJason Wessel strcpy(remcom_out_buffer, "OK"); 792a0de055cSJason Wessel } 793a0de055cSJason Wessel break; 794a0de055cSJason Wessel #endif 7958c080d3aSVincent Chen #ifdef CONFIG_HAVE_ARCH_KGDB_QXFER_PKT 7968c080d3aSVincent Chen case 'S': 7978c080d3aSVincent Chen if (!strncmp(remcom_in_buffer, "qSupported:", 11)) 7988c080d3aSVincent Chen strcpy(remcom_out_buffer, kgdb_arch_gdb_stub_feature); 7998c080d3aSVincent Chen break; 8008c080d3aSVincent Chen case 'X': 8018c080d3aSVincent Chen if (!strncmp(remcom_in_buffer, "qXfer:", 6)) 8028c080d3aSVincent Chen kgdb_arch_handle_qxfer_pkt(remcom_in_buffer, 8038c080d3aSVincent Chen remcom_out_buffer); 8048c080d3aSVincent Chen break; 8058c080d3aSVincent Chen #endif 8068c080d3aSVincent Chen default: 8078c080d3aSVincent Chen break; 80853197fc4SJason Wessel } 80953197fc4SJason Wessel } 81053197fc4SJason Wessel 81153197fc4SJason Wessel /* Handle the 'H' task query packets */ 81253197fc4SJason Wessel static void gdb_cmd_task(struct kgdb_state *ks) 81353197fc4SJason Wessel { 81453197fc4SJason Wessel struct task_struct *thread; 81553197fc4SJason Wessel char *ptr; 81653197fc4SJason Wessel 81753197fc4SJason Wessel switch (remcom_in_buffer[1]) { 81853197fc4SJason Wessel case 'g': 81953197fc4SJason Wessel ptr = &remcom_in_buffer[2]; 82053197fc4SJason Wessel kgdb_hex2long(&ptr, &ks->threadid); 82153197fc4SJason Wessel thread = getthread(ks->linux_regs, ks->threadid); 82253197fc4SJason Wessel if (!thread && ks->threadid > 0) { 82353197fc4SJason Wessel error_packet(remcom_out_buffer, -EINVAL); 82453197fc4SJason Wessel break; 82553197fc4SJason Wessel } 82653197fc4SJason Wessel kgdb_usethread = thread; 82753197fc4SJason Wessel ks->kgdb_usethreadid = ks->threadid; 82853197fc4SJason Wessel strcpy(remcom_out_buffer, "OK"); 82953197fc4SJason Wessel break; 83053197fc4SJason Wessel case 'c': 83153197fc4SJason Wessel ptr = &remcom_in_buffer[2]; 83253197fc4SJason Wessel kgdb_hex2long(&ptr, &ks->threadid); 83353197fc4SJason Wessel if (!ks->threadid) { 83453197fc4SJason Wessel kgdb_contthread = NULL; 83553197fc4SJason Wessel } else { 83653197fc4SJason Wessel thread = getthread(ks->linux_regs, ks->threadid); 83753197fc4SJason Wessel if (!thread && ks->threadid > 0) { 83853197fc4SJason Wessel error_packet(remcom_out_buffer, -EINVAL); 83953197fc4SJason Wessel break; 84053197fc4SJason Wessel } 84153197fc4SJason Wessel kgdb_contthread = thread; 84253197fc4SJason Wessel } 84353197fc4SJason Wessel strcpy(remcom_out_buffer, "OK"); 84453197fc4SJason Wessel break; 84553197fc4SJason Wessel } 84653197fc4SJason Wessel } 84753197fc4SJason Wessel 84853197fc4SJason Wessel /* Handle the 'T' thread query packets */ 84953197fc4SJason Wessel static void gdb_cmd_thread(struct kgdb_state *ks) 85053197fc4SJason Wessel { 85153197fc4SJason Wessel char *ptr = &remcom_in_buffer[1]; 85253197fc4SJason Wessel struct task_struct *thread; 85353197fc4SJason Wessel 85453197fc4SJason Wessel kgdb_hex2long(&ptr, &ks->threadid); 85553197fc4SJason Wessel thread = getthread(ks->linux_regs, ks->threadid); 85653197fc4SJason Wessel if (thread) 85753197fc4SJason Wessel strcpy(remcom_out_buffer, "OK"); 85853197fc4SJason Wessel else 85953197fc4SJason Wessel error_packet(remcom_out_buffer, -EINVAL); 86053197fc4SJason Wessel } 86153197fc4SJason Wessel 86253197fc4SJason Wessel /* Handle the 'z' or 'Z' breakpoint remove or set packets */ 86353197fc4SJason Wessel static void gdb_cmd_break(struct kgdb_state *ks) 86453197fc4SJason Wessel { 86553197fc4SJason Wessel /* 86653197fc4SJason Wessel * Since GDB-5.3, it's been drafted that '0' is a software 86753197fc4SJason Wessel * breakpoint, '1' is a hardware breakpoint, so let's do that. 86853197fc4SJason Wessel */ 86953197fc4SJason Wessel char *bpt_type = &remcom_in_buffer[1]; 87053197fc4SJason Wessel char *ptr = &remcom_in_buffer[2]; 87153197fc4SJason Wessel unsigned long addr; 87253197fc4SJason Wessel unsigned long length; 87353197fc4SJason Wessel int error = 0; 87453197fc4SJason Wessel 87553197fc4SJason Wessel if (arch_kgdb_ops.set_hw_breakpoint && *bpt_type >= '1') { 87653197fc4SJason Wessel /* Unsupported */ 87753197fc4SJason Wessel if (*bpt_type > '4') 87853197fc4SJason Wessel return; 87953197fc4SJason Wessel } else { 88053197fc4SJason Wessel if (*bpt_type != '0' && *bpt_type != '1') 88153197fc4SJason Wessel /* Unsupported. */ 88253197fc4SJason Wessel return; 88353197fc4SJason Wessel } 88453197fc4SJason Wessel 88553197fc4SJason Wessel /* 88653197fc4SJason Wessel * Test if this is a hardware breakpoint, and 88753197fc4SJason Wessel * if we support it: 88853197fc4SJason Wessel */ 88953197fc4SJason Wessel if (*bpt_type == '1' && !(arch_kgdb_ops.flags & KGDB_HW_BREAKPOINT)) 89053197fc4SJason Wessel /* Unsupported. */ 89153197fc4SJason Wessel return; 89253197fc4SJason Wessel 89353197fc4SJason Wessel if (*(ptr++) != ',') { 89453197fc4SJason Wessel error_packet(remcom_out_buffer, -EINVAL); 89553197fc4SJason Wessel return; 89653197fc4SJason Wessel } 89753197fc4SJason Wessel if (!kgdb_hex2long(&ptr, &addr)) { 89853197fc4SJason Wessel error_packet(remcom_out_buffer, -EINVAL); 89953197fc4SJason Wessel return; 90053197fc4SJason Wessel } 90153197fc4SJason Wessel if (*(ptr++) != ',' || 90253197fc4SJason Wessel !kgdb_hex2long(&ptr, &length)) { 90353197fc4SJason Wessel error_packet(remcom_out_buffer, -EINVAL); 90453197fc4SJason Wessel return; 90553197fc4SJason Wessel } 90653197fc4SJason Wessel 90753197fc4SJason Wessel if (remcom_in_buffer[0] == 'Z' && *bpt_type == '0') 90853197fc4SJason Wessel error = dbg_set_sw_break(addr); 90953197fc4SJason Wessel else if (remcom_in_buffer[0] == 'z' && *bpt_type == '0') 91053197fc4SJason Wessel error = dbg_remove_sw_break(addr); 91153197fc4SJason Wessel else if (remcom_in_buffer[0] == 'Z') 91253197fc4SJason Wessel error = arch_kgdb_ops.set_hw_breakpoint(addr, 91353197fc4SJason Wessel (int)length, *bpt_type - '0'); 91453197fc4SJason Wessel else if (remcom_in_buffer[0] == 'z') 91553197fc4SJason Wessel error = arch_kgdb_ops.remove_hw_breakpoint(addr, 91653197fc4SJason Wessel (int) length, *bpt_type - '0'); 91753197fc4SJason Wessel 91853197fc4SJason Wessel if (error == 0) 91953197fc4SJason Wessel strcpy(remcom_out_buffer, "OK"); 92053197fc4SJason Wessel else 92153197fc4SJason Wessel error_packet(remcom_out_buffer, error); 92253197fc4SJason Wessel } 92353197fc4SJason Wessel 92453197fc4SJason Wessel /* Handle the 'C' signal / exception passing packets */ 92553197fc4SJason Wessel static int gdb_cmd_exception_pass(struct kgdb_state *ks) 92653197fc4SJason Wessel { 92753197fc4SJason Wessel /* C09 == pass exception 92853197fc4SJason Wessel * C15 == detach kgdb, pass exception 92953197fc4SJason Wessel */ 93053197fc4SJason Wessel if (remcom_in_buffer[1] == '0' && remcom_in_buffer[2] == '9') { 93153197fc4SJason Wessel 93253197fc4SJason Wessel ks->pass_exception = 1; 93353197fc4SJason Wessel remcom_in_buffer[0] = 'c'; 93453197fc4SJason Wessel 93553197fc4SJason Wessel } else if (remcom_in_buffer[1] == '1' && remcom_in_buffer[2] == '5') { 93653197fc4SJason Wessel 93753197fc4SJason Wessel ks->pass_exception = 1; 93853197fc4SJason Wessel remcom_in_buffer[0] = 'D'; 93953197fc4SJason Wessel dbg_remove_all_break(); 94053197fc4SJason Wessel kgdb_connected = 0; 94153197fc4SJason Wessel return 1; 94253197fc4SJason Wessel 94353197fc4SJason Wessel } else { 94453197fc4SJason Wessel gdbstub_msg_write("KGDB only knows signal 9 (pass)" 94553197fc4SJason Wessel " and 15 (pass and disconnect)\n" 94653197fc4SJason Wessel "Executing a continue without signal passing\n", 0); 94753197fc4SJason Wessel remcom_in_buffer[0] = 'c'; 94853197fc4SJason Wessel } 94953197fc4SJason Wessel 95053197fc4SJason Wessel /* Indicate fall through */ 95153197fc4SJason Wessel return -1; 95253197fc4SJason Wessel } 95353197fc4SJason Wessel 95453197fc4SJason Wessel /* 95553197fc4SJason Wessel * This function performs all gdbserial command procesing 95653197fc4SJason Wessel */ 95753197fc4SJason Wessel int gdb_serial_stub(struct kgdb_state *ks) 95853197fc4SJason Wessel { 95953197fc4SJason Wessel int error = 0; 96053197fc4SJason Wessel int tmp; 96153197fc4SJason Wessel 96255751145SJason Wessel /* Initialize comm buffer and globals. */ 96353197fc4SJason Wessel memset(remcom_out_buffer, 0, sizeof(remcom_out_buffer)); 96455751145SJason Wessel kgdb_usethread = kgdb_info[ks->cpu].task; 96555751145SJason Wessel ks->kgdb_usethreadid = shadow_pid(kgdb_info[ks->cpu].task->pid); 96655751145SJason Wessel ks->pass_exception = 0; 96753197fc4SJason Wessel 96853197fc4SJason Wessel if (kgdb_connected) { 96984a0bd5bSJason Wessel unsigned char thref[BUF_THREAD_ID_SIZE]; 97053197fc4SJason Wessel char *ptr; 97153197fc4SJason Wessel 97253197fc4SJason Wessel /* Reply to host that an exception has occurred */ 97353197fc4SJason Wessel ptr = remcom_out_buffer; 97453197fc4SJason Wessel *ptr++ = 'T'; 97550e1499fSAndy Shevchenko ptr = hex_byte_pack(ptr, ks->signo); 97653197fc4SJason Wessel ptr += strlen(strcpy(ptr, "thread:")); 97753197fc4SJason Wessel int_to_threadref(thref, shadow_pid(current->pid)); 97853197fc4SJason Wessel ptr = pack_threadid(ptr, thref); 97953197fc4SJason Wessel *ptr++ = ';'; 98053197fc4SJason Wessel put_packet(remcom_out_buffer); 98153197fc4SJason Wessel } 98253197fc4SJason Wessel 98353197fc4SJason Wessel while (1) { 98453197fc4SJason Wessel error = 0; 98553197fc4SJason Wessel 98653197fc4SJason Wessel /* Clear the out buffer. */ 98753197fc4SJason Wessel memset(remcom_out_buffer, 0, sizeof(remcom_out_buffer)); 98853197fc4SJason Wessel 98953197fc4SJason Wessel get_packet(remcom_in_buffer); 99053197fc4SJason Wessel 99153197fc4SJason Wessel switch (remcom_in_buffer[0]) { 99253197fc4SJason Wessel case '?': /* gdbserial status */ 99353197fc4SJason Wessel gdb_cmd_status(ks); 99453197fc4SJason Wessel break; 99553197fc4SJason Wessel case 'g': /* return the value of the CPU registers */ 99653197fc4SJason Wessel gdb_cmd_getregs(ks); 99753197fc4SJason Wessel break; 99853197fc4SJason Wessel case 'G': /* set the value of the CPU registers - return OK */ 99953197fc4SJason Wessel gdb_cmd_setregs(ks); 100053197fc4SJason Wessel break; 100153197fc4SJason Wessel case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ 100253197fc4SJason Wessel gdb_cmd_memread(ks); 100353197fc4SJason Wessel break; 100453197fc4SJason Wessel case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA..AA */ 100553197fc4SJason Wessel gdb_cmd_memwrite(ks); 100653197fc4SJason Wessel break; 100755751145SJason Wessel #if DBG_MAX_REG_NUM > 0 100855751145SJason Wessel case 'p': /* pXX Return gdb register XX (in hex) */ 100955751145SJason Wessel gdb_cmd_reg_get(ks); 101055751145SJason Wessel break; 101155751145SJason Wessel case 'P': /* PXX=aaaa Set gdb register XX to aaaa (in hex) */ 101255751145SJason Wessel gdb_cmd_reg_set(ks); 101355751145SJason Wessel break; 101455751145SJason Wessel #endif /* DBG_MAX_REG_NUM > 0 */ 101553197fc4SJason Wessel case 'X': /* XAA..AA,LLLL: Write LLLL bytes at address AA..AA */ 101653197fc4SJason Wessel gdb_cmd_binwrite(ks); 101753197fc4SJason Wessel break; 101853197fc4SJason Wessel /* kill or detach. KGDB should treat this like a 101953197fc4SJason Wessel * continue. 102053197fc4SJason Wessel */ 102153197fc4SJason Wessel case 'D': /* Debugger detach */ 102253197fc4SJason Wessel case 'k': /* Debugger detach via kill */ 102353197fc4SJason Wessel gdb_cmd_detachkill(ks); 102453197fc4SJason Wessel goto default_handle; 102553197fc4SJason Wessel case 'R': /* Reboot */ 102653197fc4SJason Wessel if (gdb_cmd_reboot(ks)) 102753197fc4SJason Wessel goto default_handle; 102853197fc4SJason Wessel break; 102953197fc4SJason Wessel case 'q': /* query command */ 103053197fc4SJason Wessel gdb_cmd_query(ks); 103153197fc4SJason Wessel break; 103253197fc4SJason Wessel case 'H': /* task related */ 103353197fc4SJason Wessel gdb_cmd_task(ks); 103453197fc4SJason Wessel break; 103553197fc4SJason Wessel case 'T': /* Query thread status */ 103653197fc4SJason Wessel gdb_cmd_thread(ks); 103753197fc4SJason Wessel break; 103853197fc4SJason Wessel case 'z': /* Break point remove */ 103953197fc4SJason Wessel case 'Z': /* Break point set */ 104053197fc4SJason Wessel gdb_cmd_break(ks); 104153197fc4SJason Wessel break; 1042dcc78711SJason Wessel #ifdef CONFIG_KGDB_KDB 1043dcc78711SJason Wessel case '3': /* Escape into back into kdb */ 1044dcc78711SJason Wessel if (remcom_in_buffer[1] == '\0') { 1045dcc78711SJason Wessel gdb_cmd_detachkill(ks); 1046dcc78711SJason Wessel return DBG_PASS_EVENT; 1047dcc78711SJason Wessel } 1048dcc78711SJason Wessel #endif 1049*df561f66SGustavo A. R. Silva fallthrough; 105053197fc4SJason Wessel case 'C': /* Exception passing */ 105153197fc4SJason Wessel tmp = gdb_cmd_exception_pass(ks); 105253197fc4SJason Wessel if (tmp > 0) 105353197fc4SJason Wessel goto default_handle; 105453197fc4SJason Wessel if (tmp == 0) 105553197fc4SJason Wessel break; 1056*df561f66SGustavo A. R. Silva fallthrough; /* on tmp < 0 */ 105753197fc4SJason Wessel case 'c': /* Continue packet */ 105853197fc4SJason Wessel case 's': /* Single step packet */ 105953197fc4SJason Wessel if (kgdb_contthread && kgdb_contthread != current) { 106053197fc4SJason Wessel /* Can't switch threads in kgdb */ 106153197fc4SJason Wessel error_packet(remcom_out_buffer, -EINVAL); 106253197fc4SJason Wessel break; 106353197fc4SJason Wessel } 106453197fc4SJason Wessel dbg_activate_sw_breakpoints(); 1065*df561f66SGustavo A. R. Silva fallthrough; /* to default processing */ 106653197fc4SJason Wessel default: 106753197fc4SJason Wessel default_handle: 106853197fc4SJason Wessel error = kgdb_arch_handle_exception(ks->ex_vector, 106953197fc4SJason Wessel ks->signo, 107053197fc4SJason Wessel ks->err_code, 107153197fc4SJason Wessel remcom_in_buffer, 107253197fc4SJason Wessel remcom_out_buffer, 107353197fc4SJason Wessel ks->linux_regs); 107453197fc4SJason Wessel /* 107553197fc4SJason Wessel * Leave cmd processing on error, detach, 107653197fc4SJason Wessel * kill, continue, or single step. 107753197fc4SJason Wessel */ 107853197fc4SJason Wessel if (error >= 0 || remcom_in_buffer[0] == 'D' || 107953197fc4SJason Wessel remcom_in_buffer[0] == 'k') { 108053197fc4SJason Wessel error = 0; 108153197fc4SJason Wessel goto kgdb_exit; 108253197fc4SJason Wessel } 108353197fc4SJason Wessel 108453197fc4SJason Wessel } 108553197fc4SJason Wessel 108653197fc4SJason Wessel /* reply to the request */ 108753197fc4SJason Wessel put_packet(remcom_out_buffer); 108853197fc4SJason Wessel } 108953197fc4SJason Wessel 109053197fc4SJason Wessel kgdb_exit: 109153197fc4SJason Wessel if (ks->pass_exception) 109253197fc4SJason Wessel error = 1; 109353197fc4SJason Wessel return error; 109453197fc4SJason Wessel } 1095dcc78711SJason Wessel 1096dcc78711SJason Wessel int gdbstub_state(struct kgdb_state *ks, char *cmd) 1097dcc78711SJason Wessel { 1098dcc78711SJason Wessel int error; 1099dcc78711SJason Wessel 1100dcc78711SJason Wessel switch (cmd[0]) { 1101dcc78711SJason Wessel case 'e': 1102dcc78711SJason Wessel error = kgdb_arch_handle_exception(ks->ex_vector, 1103dcc78711SJason Wessel ks->signo, 1104dcc78711SJason Wessel ks->err_code, 1105dcc78711SJason Wessel remcom_in_buffer, 1106dcc78711SJason Wessel remcom_out_buffer, 1107dcc78711SJason Wessel ks->linux_regs); 1108dcc78711SJason Wessel return error; 1109dcc78711SJason Wessel case 's': 1110dcc78711SJason Wessel case 'c': 11114cc168eaSGustavo A. R. Silva strscpy(remcom_in_buffer, cmd, sizeof(remcom_in_buffer)); 1112dcc78711SJason Wessel return 0; 1113f679c498SJason Wessel case '$': 11144cc168eaSGustavo A. R. Silva strscpy(remcom_in_buffer, cmd, sizeof(remcom_in_buffer)); 1115f679c498SJason Wessel gdbstub_use_prev_in_buf = strlen(remcom_in_buffer); 1116f679c498SJason Wessel gdbstub_prev_in_buf_pos = 0; 1117f679c498SJason Wessel return 0; 1118dcc78711SJason Wessel } 1119dcc78711SJason Wessel dbg_io_ops->write_char('+'); 1120dcc78711SJason Wessel put_packet(remcom_out_buffer); 1121dcc78711SJason Wessel return 0; 1122dcc78711SJason Wessel } 1123d57f078bSDavid Howells 1124d57f078bSDavid Howells /** 1125d57f078bSDavid Howells * gdbstub_exit - Send an exit message to GDB 1126d57f078bSDavid Howells * @status: The exit code to report. 1127d57f078bSDavid Howells */ 1128d57f078bSDavid Howells void gdbstub_exit(int status) 1129d57f078bSDavid Howells { 1130d57f078bSDavid Howells unsigned char checksum, ch, buffer[3]; 1131d57f078bSDavid Howells int loop; 1132d57f078bSDavid Howells 11332366e047SJason Wessel if (!kgdb_connected) 11342366e047SJason Wessel return; 11352366e047SJason Wessel kgdb_connected = 0; 11362366e047SJason Wessel 11372366e047SJason Wessel if (!dbg_io_ops || dbg_kdb_mode) 11382366e047SJason Wessel return; 11392366e047SJason Wessel 1140d57f078bSDavid Howells buffer[0] = 'W'; 1141d57f078bSDavid Howells buffer[1] = hex_asc_hi(status); 1142d57f078bSDavid Howells buffer[2] = hex_asc_lo(status); 1143d57f078bSDavid Howells 1144d57f078bSDavid Howells dbg_io_ops->write_char('$'); 1145d57f078bSDavid Howells checksum = 0; 1146d57f078bSDavid Howells 1147d57f078bSDavid Howells for (loop = 0; loop < 3; loop++) { 1148d57f078bSDavid Howells ch = buffer[loop]; 1149d57f078bSDavid Howells checksum += ch; 1150d57f078bSDavid Howells dbg_io_ops->write_char(ch); 1151d57f078bSDavid Howells } 1152d57f078bSDavid Howells 1153d57f078bSDavid Howells dbg_io_ops->write_char('#'); 1154d57f078bSDavid Howells dbg_io_ops->write_char(hex_asc_hi(checksum)); 1155d57f078bSDavid Howells dbg_io_ops->write_char(hex_asc_lo(checksum)); 1156d57f078bSDavid Howells 1157d57f078bSDavid Howells /* make sure the output is flushed, lest the bootloader clobber it */ 11589fbe465eSJan Kiszka if (dbg_io_ops->flush) 1159d57f078bSDavid Howells dbg_io_ops->flush(); 1160d57f078bSDavid Howells } 1161