// SPDX-License-Identifier: GPL-2.0-only /* * Kernel Debug Core * * Maintainer: Jason Wessel <[email protected]> * * Copyright (C) 2000-2001 VERITAS Software Corporation. * Copyright (C) 2002-2004 Timesys Corporation * Copyright (C) 2003-2004 Amit S. Kale <[email protected]> * Copyright (C) 2004 Pavel Machek <[email protected]> * Copyright (C) 2004-2006 Tom Rini <[email protected]> * Copyright (C) 2004-2006 LinSysSoft Technologies Pvt. Ltd. * Copyright (C) 2005-2009 Wind River Systems, Inc. * Copyright (C) 2007 MontaVista Software, Inc. * Copyright (C) 2008 Red Hat, Inc., Ingo Molnar <[email protected]> * * Contributors at various stages not listed above: * Jason Wessel ( [email protected] ) * George Anzinger <[email protected]> * Anurekh Saxena ([email protected]) * Lake Stevens Instrument Division (Glenn Engel) * Jim Kingdon, Cygnus Support. * * Original KGDB stub: David Grothe <[email protected]>, * Tigran Aivazian <[email protected]> */ #include <linux/kernel.h> #include <linux/sched/signal.h> #include <linux/kgdb.h> #include <linux/kdb.h> #include <linux/serial_core.h> #include <linux/reboot.h> #include <linux/uaccess.h> #include <asm/cacheflush.h> #include <asm/unaligned.h> #include "debug_core.h" #define KGDB_MAX_THREAD_QUERY … /* Our I/O buffers. */ static char remcom_in_buffer[BUFMAX]; static char remcom_out_buffer[BUFMAX]; static int gdbstub_use_prev_in_buf; static int gdbstub_prev_in_buf_pos; /* Storage for the registers, in GDB format. */ static unsigned long gdb_regs[(NUMREGBYTES + sizeof(unsigned long) - 1) / sizeof(unsigned long)]; /* * GDB remote protocol parser: */ #ifdef CONFIG_KGDB_KDB static int gdbstub_read_wait(void) { … } #else static int gdbstub_read_wait(void) { int ret = dbg_io_ops->read_char(); while (ret == NO_POLL_CHAR) ret = dbg_io_ops->read_char(); return ret; } #endif /* scan for the sequence $<data>#<checksum> */ static void get_packet(char *buffer) { … } /* * Send the packet in buffer. * Check for gdb connection if asked for. */ static void put_packet(char *buffer) { … } static char gdbmsgbuf[BUFMAX + 1]; void gdbstub_msg_write(const char *s, int len) { … } /* * Convert the memory pointed to by mem into hex, placing result in * buf. Return a pointer to the last char put in buf (null). May * return an error. */ char *kgdb_mem2hex(char *mem, char *buf, int count) { … } /* * Convert the hex array pointed to by buf into binary to be placed in * mem. Return a pointer to the character AFTER the last byte * written. May return an error. */ int kgdb_hex2mem(char *buf, char *mem, int count) { … } /* * While we find nice hex chars, build a long_val. * Return number of chars processed. */ int kgdb_hex2long(char **ptr, unsigned long *long_val) { … } /* * Copy the binary array pointed to by buf into mem. Fix $, #, and * 0x7d escaped with 0x7d. Return -EFAULT on failure or 0 on success. * The input buf is overwritten with the result to write to mem. */ static int kgdb_ebin2mem(char *buf, char *mem, int count) { … } #if DBG_MAX_REG_NUM > 0 void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) { … } void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) { … } #endif /* DBG_MAX_REG_NUM > 0 */ /* Write memory due to an 'M' or 'X' packet. */ static int write_mem_msg(int binary) { … } static void error_packet(char *pkt, int error) { … } /* * Thread ID accessors. We represent a flat TID space to GDB, where * the per CPU idle threads (which under Linux all have PID 0) are * remapped to negative TIDs. */ #define BUF_THREAD_ID_SIZE … static char *pack_threadid(char *pkt, unsigned char *id) { … } static void int_to_threadref(unsigned char *id, int value) { … } static struct task_struct *getthread(struct pt_regs *regs, int tid) { … } /* * Remap normal tasks to their real PID, * CPU shadow threads are mapped to -CPU - 2 */ static inline int shadow_pid(int realpid) { … } /* * All the functions that start with gdb_cmd are the various * operations to implement the handlers for the gdbserial protocol * where KGDB is communicating with an external debugger */ /* Handle the '?' status packets */ static void gdb_cmd_status(struct kgdb_state *ks) { … } static void gdb_get_regs_helper(struct kgdb_state *ks) { … } /* Handle the 'g' get registers request */ static void gdb_cmd_getregs(struct kgdb_state *ks) { … } /* Handle the 'G' set registers request */ static void gdb_cmd_setregs(struct kgdb_state *ks) { … } /* Handle the 'm' memory read bytes */ static void gdb_cmd_memread(struct kgdb_state *ks) { … } /* Handle the 'M' memory write bytes */ static void gdb_cmd_memwrite(struct kgdb_state *ks) { … } #if DBG_MAX_REG_NUM > 0 static char *gdb_hex_reg_helper(int regnum, char *out) { … } /* Handle the 'p' individual register get */ static void gdb_cmd_reg_get(struct kgdb_state *ks) { … } /* Handle the 'P' individual register set */ static void gdb_cmd_reg_set(struct kgdb_state *ks) { … } #endif /* DBG_MAX_REG_NUM > 0 */ /* Handle the 'X' memory binary write bytes */ static void gdb_cmd_binwrite(struct kgdb_state *ks) { … } /* Handle the 'D' or 'k', detach or kill packets */ static void gdb_cmd_detachkill(struct kgdb_state *ks) { … } /* Handle the 'R' reboot packets */ static int gdb_cmd_reboot(struct kgdb_state *ks) { … } /* Handle the 'q' query packets */ static void gdb_cmd_query(struct kgdb_state *ks) { … } /* Handle the 'H' task query packets */ static void gdb_cmd_task(struct kgdb_state *ks) { … } /* Handle the 'T' thread query packets */ static void gdb_cmd_thread(struct kgdb_state *ks) { … } /* Handle the 'z' or 'Z' breakpoint remove or set packets */ static void gdb_cmd_break(struct kgdb_state *ks) { … } /* Handle the 'C' signal / exception passing packets */ static int gdb_cmd_exception_pass(struct kgdb_state *ks) { … } /* * This function performs all gdbserial command processing */ int gdb_serial_stub(struct kgdb_state *ks) { … } int gdbstub_state(struct kgdb_state *ks, char *cmd) { … } /** * gdbstub_exit - Send an exit message to GDB * @status: The exit code to report. */ void gdbstub_exit(int status) { … }