This is the mail archive of the
archer@sourceware.org
mailing list for the Archer project.
Re: linux kernel gdb stub for userspace processes, prototypeversion 3
- From: Srikar Dronamraju <srikar at linux dot vnet dot ibm dot com>
- To: "Frank Ch. Eigler" <fche at redhat dot com>
- Cc: systemtap at sourceware dot org, utrace-devel at redhat dot com, archer at sourceware dot org
- Date: Thu, 9 Jul 2009 21:12:53 +0530
- Subject: Re: linux kernel gdb stub for userspace processes, prototypeversion 3
- References: <20090707042639.GC25935@redhat.com>
- Reply-to: Srikar Dronamraju <srikar at linux dot vnet dot ibm dot com>
Hi Frank,
Current gdb stub seems to cause a SIGSTOP when a signal handler is
called. And this behaviour is not consistent.
This behaviour is different from when gdb was invoked on the program
without the stap.
I believe the STOP at signal handler would only happen when
UTRACE_SIGNAL_HANDLER or UTRACE_SIGNAL_REPORT before the utrace-gdb
quiesce handler gets called.
The patch is as below.
commit eb53493b0208bc036dc570560ac3449aed450fbf
Author: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Date: Thu Jul 9 21:03:45 2009 +0530
make sure quiesce doesnt make the process to stop at the signal handler
diff --git a/kernel/utrace-gdb.c b/kernel/utrace-gdb.c
index 3835761..cbed911 100644
--- a/kernel/utrace-gdb.c
+++ b/kernel/utrace-gdb.c
@@ -10,7 +10,7 @@
* Red Hat Author: Frank Ch. Eigler
*/
-/* #define DEBUG 1 */
+#define DEBUG 1
#include <asm/syscall.h>
#include <asm/signal.h>
@@ -249,6 +249,7 @@ u32 gdb_utrace_report_signal(u32 action,
if (utrace_signal_action(action) == UTRACE_SIGNAL_REPORT ||
utrace_signal_action(action) == UTRACE_SIGNAL_HANDLER) { /* case 5 */
/* NB: disregard p->at_quiesce_do */
+ p->at_quiesce_do = UTRACE_RESUME;
ret = UTRACE_RESUME | utrace_signal_action(action);
} else if (p->skip_signals > 0 /*&& kern_p*/) { /* case 4 */
p->skip_signals --;
> Hi -
>
> Further to http://sourceware.org/ml/systemtap/2009-q2/msg00969.html, I
> attach another snapshot of my gdb-stub in linux-kernel prototype. It's
> working a lot better. Usage is as before:
>
> % PROCESS &
> [1] 21175
> % gdb PROCESS
> (gdb) target remote /proc/21175/gdb
> (gdb) # whatever strikes your fancy
>
> Known limitations:
> - http://sourceware.org/ml/gdb/2009-07/msg00036.html
> (occasional "Remote failure reply: E....." error)
> - only for single-threaded programs
> - x86-64 and x86 only
> - floating poing registers not yet done
> - checkpatch.pl not yet satisfied
>
> This patch should apply to recent utrace-patched kernels.
>
> - FChE
>
>
> diff --git a/fs/proc/base.c b/fs/proc/base.c
> index 3326bbf..0afb05a 100644
> --- a/fs/proc/base.c
> +++ b/fs/proc/base.c
> @@ -77,6 +77,7 @@
> #include <linux/audit.h>
> #include <linux/poll.h>
> #include <linux/nsproxy.h>
> +#include <linux/utrace.h>
> #include <linux/oom.h>
> #include <linux/elf.h>
> #include <linux/pid_namespace.h>
> @@ -2542,6 +2543,9 @@ static const struct pid_entry tgid_base_stuff[] = {
> #ifdef CONFIG_TASK_IO_ACCOUNTING
> INF("io", S_IRUGO, proc_tgid_io_accounting),
> #endif
> +#ifdef CONFIG_UTRACE_GDB
> + REG("gdb", S_IRUSR|S_IWUSR, proc_gdb_operations),
> +#endif
> };
>
> static int proc_tgid_base_readdir(struct file * filp,
> diff --git a/include/linux/utrace.h b/include/linux/utrace.h
> index f877ec6..f33a5da 100644
> --- a/include/linux/utrace.h
> +++ b/include/linux/utrace.h
> @@ -689,4 +689,8 @@ static inline __must_check int utrace_barrier_pid(struct pid *pid,
>
> #endif /* CONFIG_UTRACE */
>
> +#ifdef CONFIG_UTRACE_GDB
> +extern const struct file_operations proc_gdb_operations;
> +#endif
> +
> #endif /* linux/utrace.h */
> diff --git a/init/Kconfig b/init/Kconfig
> index a6987df..7ffa60d 100644
> --- a/init/Kconfig
> +++ b/init/Kconfig
> @@ -1158,6 +1158,14 @@ menuconfig UTRACE
> kernel interface exported to kernel modules, to track events in
> user threads, extract and change user thread state.
>
> +config UTRACE_GDB
> + bool "/proc/<pid>/gdb file for gdb remote connection"
> + select UTRACE
> + default y
> + help
> + Enable the utrace-based /proc/<pid>/gdb process debugging
> + interface, for connection using the gdb remote protocol.
> +
> source "block/Kconfig"
>
> config PREEMPT_NOTIFIERS
> diff --git a/kernel/Makefile b/kernel/Makefile
> index a79634e..21457e6 100644
> --- a/kernel/Makefile
> +++ b/kernel/Makefile
> @@ -69,6 +69,7 @@ obj-$(CONFIG_RESOURCE_COUNTERS) += res_counter.o
> obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
> obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o
> obj-$(CONFIG_UTRACE) += utrace.o
> +obj-$(CONFIG_UTRACE_GDB) += utrace-gdb.o
> obj-$(CONFIG_AUDIT) += audit.o auditfilter.o
> obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
> obj-$(CONFIG_AUDIT_TREE) += audit_tree.o
> diff --git a/kernel/utrace-gdb.c b/kernel/utrace-gdb.c
> new file mode 100644
> index 0000000..3835761
> --- /dev/null
> +++ b/kernel/utrace-gdb.c
> @@ -0,0 +1,1148 @@
> +/*
> + * utrace-based gdb remote protocol server for user processes
> + *
> + * Copyright (C) 2009 Red Hat, Inc. All rights reserved.
> + *
> + * This copyrighted material is made available to anyone wishing to use,
> + * modify, copy, or redistribute it subject to the terms and conditions
> + * of the GNU General Public License v.2.
> + *
> + * Red Hat Author: Frank Ch. Eigler
> + */
> +
> +/* #define DEBUG 1 */
> +
> +#include <asm/syscall.h>
> +#include <asm/signal.h>
> +#include <linux/ptrace.h>
> +#include <linux/err.h>
> +#include <linux/pid.h>
> +#include <linux/sched.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/mm.h>
> +#include <linux/proc_fs.h>
> +#include <linux/ctype.h>
> +#include <linux/regset.h>
> +#include <linux/utrace.h>
> +#include <linux/tracehook.h>
> +
> +
> +
> +/** struct gdb_connection - Tracks one active gdb-process session.
> + */
> +
> +#define GDB_BUFMAX 4096
> +
> +
> +struct gdb_connection {
> + pid_t target;
> + struct utrace_engine *engine;
> +
> + /* changed under output_mutex */
> + int at_quiesce_do;
> + unsigned char stopcode[GDB_BUFMAX]; // set <=> at_quiesce_do = UTRACE_STOP
> + int skip_signals;
> + int stop_signals;
> + /* XXX: per-thread later */
> +
> + char output_buf[GDB_BUFMAX];
> + size_t output_buf_size;
> + loff_t output_buf_read;
> + struct mutex output_mutex;
> + wait_queue_head_t output_wait;
> +
> + char input_buf[GDB_BUFMAX];
> + size_t input_buf_size;
> + struct mutex input_mutex;
> + wait_queue_head_t input_wait;
> +
> + struct list_head link;
> +};
> +
> +
> +static LIST_HEAD(gdb_connections);
> +static DEFINE_MUTEX(gdb_connections_mutex);
> +static const struct utrace_engine_ops gdb_utrace_ops;
> +
> +
> +
> +/* ------------------------------------------------------------------------ */
> +
> +static unsigned byteme (unsigned char hex1, unsigned char hex2)
> +{
> + return (isdigit(hex1) ? hex1-'0' : tolower(hex1)-'a'+10) * 16 +
> + (isdigit(hex2) ? hex2-'0' : tolower(hex2)-'a'+10);
> +}
> +
> +
> +
> +/* Begin a new packet. Add the $, and remember where we put it.
> + * Return the offset for later checksum addition via
> + * push_output_packet_end. */
> +static size_t push_output_packet_start (struct gdb_connection *p)
> +{
> + size_t start = p->output_buf_size;
> +
> + BUG_ON (p->output_buf_size + 1 >= GDB_BUFMAX);
> + p->output_buf[p->output_buf_size++] = '$';
> + return start;
> +}
> +
> +
> +/* Add a character to the output queue. Assumes output_mutex held. */
> +static void push_output (struct gdb_connection *p, unsigned char c)
> +{
> + /* We know some space must exist; we check for this in
> + proc_gdb_write() for example. */
> + BUG_ON (p->output_buf_size >= GDB_BUFMAX);
> + p->output_buf[p->output_buf_size++] = c;
> +}
> +
> +
> +static char hex[] = "0123456789ABCDEF";
> +
> +/* Add a byte (hexified) to the output queue. Assumes output_mutex held. */
> +static void push_output_hex (struct gdb_connection *p, unsigned char c)
> +{
> + /* We know some space must exist; we check for this in
> + proc_gdb_write() for example. */
> + BUG_ON (p->output_buf_size >= GDB_BUFMAX);
> + p->output_buf[p->output_buf_size++] = hex[(c & 0xf0) >> 4];
> + p->output_buf[p->output_buf_size++] = hex[(c & 0x0f) >> 0];
> +}
> +
> +
> +/* Finish the last packet. Starting after the given '$' offset, compute
> + * the checksum and append it. */
> +static void push_output_packet_end (struct gdb_connection *p, size_t start)
> +{
> + unsigned char checksum = 0;
> + int i;
> +
> + BUG_ON (p->output_buf_size + 3 >= GDB_BUFMAX);
> + BUG_ON (p->output_buf[start] != '$');
> +
> + for (i=start+1; i<p->output_buf_size; i++)
> + checksum += p->output_buf[i];
> +
> + p->output_buf[p->output_buf_size++] = '#';
> + p->output_buf[p->output_buf_size++] = hex[(checksum & 0xf0) >> 4];
> + p->output_buf[p->output_buf_size++] = hex[(checksum & 0x0f) >> 0];
> +}
> +
> +
> +/* Add a complete packet payload to the output queue. */
> +static void push_output_packet (struct gdb_connection *p, const char *s)
> +{
> + size_t ss = strlen(s);
> + size_t start;
> + int i;
> +
> + start = push_output_packet_start(p);
> + for (i=0; i<ss; i++)
> + push_output(p, s[i]);
> + push_output_packet_end(p, start);
> +}
> +
> +
> +
> +/* ------------------------------------------------------------------------ */
> +
> +/* utrace callbacks */
> +
> +
> +u32 gdb_utrace_report_quiesce(enum utrace_resume_action action,
> + struct utrace_engine *engine,
> + struct task_struct *task,
> + unsigned long event)
> +{
> + struct gdb_connection *p = engine->data;
> + pr_debug ("report_quiesce %d event 0x%lx 0x%x->0x%x\n", task->pid,
> + event, action, p->at_quiesce_do);
> +
> + return p->at_quiesce_do;
> +}
> +
> +
> +u32 gdb_utrace_report_clone(enum utrace_resume_action action,
> + struct utrace_engine *engine,
> + struct task_struct *parent,
> + unsigned long clone_flags,
> + struct task_struct *child)
> +{
> + pr_debug ("report_clone %d->%d\n", parent->pid, child->pid);
> +
> + if (clone_flags & CLONE_THREAD) {
> + printk (KERN_WARNING "unsupported multithreading on /proc/%d/gdb.\n",
> + task_pid_nr (parent));
> + }
> + /* XXX: is there anything else to do here? */
> + return UTRACE_RESUME;
> +}
> +
> +
> +u32 gdb_utrace_report_exec(enum utrace_resume_action action,
> + struct utrace_engine *engine,
> + struct task_struct *task,
> + const struct linux_binfmt *fmt,
> + const struct linux_binprm *bprm,
> + struct pt_regs *regs)
> +{
> + /* XXX: Model an exec as if it were an exit. */
> + struct gdb_connection *p = engine->data;
> +
> + pr_debug ("report_exec %d->%s\n", task->pid, task->comm);
> +
> + mutex_lock(&p->output_mutex);
> +
> + p->at_quiesce_do = UTRACE_STOP;
> + snprintf (p->stopcode, GDB_BUFMAX, "W%02x", 0);
> + push_output_packet (p, p->stopcode);
> +
> + mutex_unlock(&p->output_mutex);
> + wake_up(&p->output_wait);
> +
> + /* Suspend the exec operation, to ensure that the connected gdb
> + receives the notification packet, and lets us go. */
> + return UTRACE_STOP;
> +}
> +
> +
> +u32 gdb_utrace_report_signal(u32 action,
> + struct utrace_engine *engine,
> + struct task_struct *task,
> + struct pt_regs *regs,
> + siginfo_t *info,
> + const struct k_sigaction *orig_ka,
> + struct k_sigaction *return_ka)
> +{
> + struct gdb_connection *p = engine->data;
> + u32 ret = action;
> + int kern_p;
> +
> + mutex_lock(&p->output_mutex);
> +
> + kern_p = (info != SEND_SIG_NOINFO && (is_si_special(info) || SI_FROMKERNEL(info)));
> +
> + pr_debug ("report_signal %d (0x%x) kern %d skip %d stop %d\n",
> + task->pid, action, kern_p, p->skip_signals, p->stop_signals);
> +
> + /* The target is about to receive a signal. There are several
> + * cases:
> + *
> + * 1) This is an ordinary signal. We UTRACE_STOP to notify gdb.
> + *
> + * 2) This is a SIGTRAP arising from a breakpoint. We UTRACE_STOP.
> + *
> + * 3) This is a signal our code injected to stop the process, in lieu
> + * of UTRACE_INTERRUPT. We UTRACE_STOP | UTRACE_SIGNAL_IGN.
> + *
> + * 4) This is a signal our code injected on behalf of gdb (C/S/I packets).
> + * We UTRACE_RESUME.
> + *
> + * 5) This is a UTRACE_SIGNAL_REPORT or UTRACE_SIGNAL_HANDLER event.
> + * Just let utrace continue, as these signal events of minor internal
> + * interest.
> + */
> +
> + if (utrace_signal_action(action) == UTRACE_SIGNAL_REPORT ||
> + utrace_signal_action(action) == UTRACE_SIGNAL_HANDLER) { /* case 5 */
> + /* NB: disregard p->at_quiesce_do */
> + ret = UTRACE_RESUME | utrace_signal_action(action);
> + } else if (p->skip_signals > 0 /*&& kern_p*/) { /* case 4 */
> + p->skip_signals --;
> + p->at_quiesce_do = UTRACE_RESUME;
> + ret = UTRACE_RESUME; /* deliver */
> + } else if (p->stop_signals > 0 /*&& kern_p*/) { /* Case 3 */
> + p->stop_signals --;
> + snprintf (p->stopcode, GDB_BUFMAX, "S%02x", info->si_signo);
> + push_output_packet (p, p->stopcode);
> + p->at_quiesce_do = UTRACE_STOP;
> + ret = UTRACE_STOP | UTRACE_SIGNAL_IGN;
> + } else { /* Cases 1, 2 */
> + snprintf (p->stopcode, GDB_BUFMAX, "S%02x", info->si_signo);
> + push_output_packet (p, p->stopcode);
> + p->at_quiesce_do = UTRACE_STOP;
> + ret = UTRACE_STOP;
> + }
> +
> + pr_debug ("action 0x%x\n", ret);
> +
> + mutex_unlock(&p->output_mutex);
> + wake_up(&p->output_wait);
> +
> + return ret;
> +}
> +
> +
> +u32 gdb_utrace_report_exit(enum utrace_resume_action action,
> + struct utrace_engine *engine,
> + struct task_struct *task,
> + long orig_code, long *code)
> +{
> + struct gdb_connection *p = engine->data;
> +
> + pr_debug ("report_exit %d (%lx)\n", task->pid, (unsigned long) orig_code);
> +
> + mutex_lock(&p->output_mutex);
> +
> + p->at_quiesce_do = UTRACE_STOP;
> + snprintf (p->stopcode, GDB_BUFMAX,
> + "W%02x", (unsigned)(orig_code & 0xFF));
> + push_output_packet (p, p->stopcode);
> +
> + mutex_unlock(&p->output_mutex);
> + wake_up(&p->output_wait);
> +
> + /* Suspend the exit operation, to ensure that the connected gdb
> + receives the notification packet, and lets us go. */
> + return UTRACE_STOP;
> +}
> +
> +
> +u32 gdb_utrace_report_death(struct utrace_engine *engine,
> + struct task_struct *task,
> + bool group_dead, int signal)
> +{
> + struct gdb_connection *p = engine->data;
> +
> + pr_debug ("report_death %d (%d)\n", task->pid, signal);
> +
> + mutex_lock(&p->output_mutex);
> +
> + p->at_quiesce_do = UTRACE_DETACH;
> + snprintf (p->stopcode, GDB_BUFMAX, "X%2x", (unsigned)(signal & 0xFF));
> + push_output_packet (p, p->stopcode);
> +
> + p->engine = NULL;
> +
> + mutex_unlock(&p->output_mutex);
> + wake_up(&p->output_wait);
> +
> + return UTRACE_DETACH;
> +}
> +
> +
> +
> +static const struct utrace_engine_ops gdb_utrace_ops = {
> + .report_quiesce = gdb_utrace_report_quiesce,
> + .report_signal = gdb_utrace_report_signal,
> + .report_death = gdb_utrace_report_death,
> + .report_exit = gdb_utrace_report_exit,
> + .report_exec = gdb_utrace_report_exec,
> + .report_clone = gdb_utrace_report_clone,
> + /* XXX: syscall trapping is also possible. */
> +};
> +
> +
> +
> +/* XXX: arch-dependent lookup of gdb remote protocol register
> + * numbering. The register numbers (user-side) & expected sizes come
> + * from gdb's regformats/FOO-linux.dat. The regset (kernel-side)
> + * numbers could come from offsetof/sizeof constructs based upon each
> + * arch's asm/user*.h.
> + */
> +
> +struct gdb_map_regset {
> + unsigned pos; /* regset offset */
> + unsigned count; /* regset byte count */
> + unsigned rsn; /* regset number */
> + unsigned bytes; /* gdb's view of register width; <= count */
> +};
> +
> +struct gdb_map_regset arch_i386_map_regset[] = {
> + [0]={ /* eax */ 6*4, 4, NT_PRSTATUS, 4, },
> + [1]={ /* ecx */ 1*4, 4, NT_PRSTATUS, 4, },
> + [2]={ /* edx */ 2*4, 4, NT_PRSTATUS, 4, },
> + [3]={ /* ebx */ 0*4, 4, NT_PRSTATUS, 4, },
> + [4]={ /* esp */ 15*4, 4, NT_PRSTATUS, 4, },
> + [5]={ /* ebp */ 5*4, 4, NT_PRSTATUS, 4, },
> + [6]={ /* esi */ 3*4, 4, NT_PRSTATUS, 4, },
> + [7]={ /* edi */ 4*4, 4, NT_PRSTATUS, 4, },
> + [8]={ /* eip */ 12*4, 4, NT_PRSTATUS, 4, },
> + [9]={ /* eflags */ 14*4, 4, NT_PRSTATUS, 4, },
> + [10]={ /* cs */ 13*4, 4, NT_PRSTATUS, 4, },
> + [11]={ /* ss */ 16*4, 4, NT_PRSTATUS, 4, },
> + [12]={ /* ds */ 7*4, 4, NT_PRSTATUS, 4, },
> + [13]={ /* es */ 8*4, 4, NT_PRSTATUS, 4, },
> + [14]={ /* fs */ 9*4, 4, NT_PRSTATUS, 4, },
> + [15]={ /* gs */ 10*4, 4, NT_PRSTATUS, 4, },
> + [16]={ /* st0 */ 0, 0, NT_PRFPREG, 10, },
> + [17]={ /* st1 */ 0, 0, NT_PRFPREG, 10, },
> + [18]={ /* st2 */ 0, 0, NT_PRFPREG, 10, },
> + [19]={ /* st3 */ 0, 0, NT_PRFPREG, 10, },
> + [20]={ /* st4 */ 0, 0, NT_PRFPREG, 10, },
> + [21]={ /* st5 */ 0, 0, NT_PRFPREG, 10, },
> + [22]={ /* st6 */ 0, 0, NT_PRFPREG, 10, },
> + [23]={ /* st7 */ 0, 0, NT_PRFPREG, 10, },
> + [24]={ /* fctrl */ 0, 0, NT_PRFPREG, 4, },
> + [25]={ /* fstat */ 0, 0, NT_PRFPREG, 4, },
> + [26]={ /* ftag */ 0, 0, NT_PRFPREG, 4, },
> + [27]={ /* fiseg */ 0, 0, NT_PRFPREG, 4, },
> + [28]={ /* fioff */ 0, 0, NT_PRFPREG, 4, },
> + [29]={ /* foseg */ 0, 0, NT_PRFPREG, 4, },
> + [30]={ /* fooff */ 0, 0, NT_PRFPREG, 4, },
> + [31]={ /* fop */ 0, 0, NT_PRFPREG, 4, },
> + [32]={ /* xmm0 */ 0, 0, NT_PRFPREG, 16, },
> + [33]={ /* xmm1 */ 0, 0, NT_PRFPREG, 16, },
> + [34]={ /* xmm2 */ 0, 0, NT_PRFPREG, 16, },
> + [35]={ /* xmm3 */ 0, 0, NT_PRFPREG, 16, },
> + [36]={ /* xmm4 */ 0, 0, NT_PRFPREG, 16, },
> + [37]={ /* xmm5 */ 0, 0, NT_PRFPREG, 16, },
> + [38]={ /* xmm6 */ 0, 0, NT_PRFPREG, 16, },
> + [39]={ /* xmm7 */ 0, 0, NT_PRFPREG, 16, },
> + [40]={ /* mxcsr */ 0, 0, NT_PRFPREG, 4, },
> + [41]={ /* orig_eax*/ 0, 0, NT_PRSTATUS, 4, },
> +};
> +
> +
> +struct gdb_map_regset arch_x86_64_map_regset[] = {
> + [0]={ /* rax */ 10*8, 8, NT_PRSTATUS, 8, },
> + [1]={ /* rbx */ 5*8, 8, NT_PRSTATUS, 8, },
> + [2]={ /* rcx */ 11*8, 8, NT_PRSTATUS, 8, },
> + [3]={ /* rdx */ 12*8, 8, NT_PRSTATUS, 8, },
> + [4]={ /* rsi */ 13*8, 8, NT_PRSTATUS, 8, },
> + [5]={ /* rdi */ 14*8, 8, NT_PRSTATUS, 8, },
> + [6]={ /* rbp */ 4*8, 8, NT_PRSTATUS, 8, },
> + [7]={ /* rsp */ 19*8, 8, NT_PRSTATUS, 8, },
> + [8]={ /* r8 */ 9*8, 8, NT_PRSTATUS, 8, },
> + [9]={ /* r9 */ 8*8, 8, NT_PRSTATUS, 8, },
> + [10]={ /* r10 */ 7*8, 8, NT_PRSTATUS, 8, },
> + [11]={ /* r11 */ 6*8, 8, NT_PRSTATUS, 8, },
> + [12]={ /* r12 */ 3*8, 8, NT_PRSTATUS, 8, },
> + [13]={ /* r13 */ 2*8, 8, NT_PRSTATUS, 8, },
> + [14]={ /* r14 */ 1*8, 8, NT_PRSTATUS, 8, },
> + [15]={ /* r15 */ 0*8, 8, NT_PRSTATUS, 8, },
> + [16]={ /* rip */ 16*8, 8, NT_PRSTATUS, 8, },
> + [17]={ /* flags */ 18*8, 8, NT_PRSTATUS, 4, },
> + [18]={ /* cs */ 17*8, 8, NT_PRSTATUS, 4, },
> + [19]={ /* ss */ 20*8, 8, NT_PRSTATUS, 4, },
> + [20]={ /* ds */ 23*8, 8, NT_PRSTATUS, 4, },
> + [21]={ /* es */ 24*8, 8, NT_PRSTATUS, 4, },
> + [22]={ /* fs */ 25*8, 8, NT_PRSTATUS, 4, },
> + [23]={ /* gs */ 26*8, 8, NT_PRSTATUS, 4, },
> + [24]={ /* st0 */ 0, 0, NT_PRFPREG, 10, },
> + [25]={ /* st1 */ 0, 0, NT_PRFPREG, 10, },
> + [26]={ /* st2 */ 0, 0, NT_PRFPREG, 10, },
> + [27]={ /* st3 */ 0, 0, NT_PRFPREG, 10, },
> + [28]={ /* st4 */ 0, 0, NT_PRFPREG, 10, },
> + [29]={ /* st5 */ 0, 0, NT_PRFPREG, 10, },
> + [30]={ /* st6 */ 0, 0, NT_PRFPREG, 10, },
> + [31]={ /* st7 */ 0, 0, NT_PRFPREG, 10, },
> + [32]={ /* fctrl */ 0, 0, NT_PRFPREG, 4, },
> + [33]={ /* fstat */ 0, 0, NT_PRFPREG, 4, },
> + [34]={ /* ftag */ 0, 0, NT_PRFPREG, 4, },
> + [35]={ /* fiseg */ 0, 0, NT_PRFPREG, 4, },
> + [36]={ /* fioff */ 0, 0, NT_PRFPREG, 4, },
> + [37]={ /* foseg */ 0, 0, NT_PRFPREG, 4, },
> + [38]={ /* fooff */ 0, 0, NT_PRFPREG, 4, },
> + [39]={ /* fop */ 0, 0, NT_PRFPREG, 4, },
> + [40]={ /* xmm0 */ 0, 0, NT_PRFPREG, 16, },
> + [41]={ /* xmm1 */ 0, 0, NT_PRFPREG, 16, },
> + [42]={ /* xmm2 */ 0, 0, NT_PRFPREG, 16, },
> + [43]={ /* xmm3 */ 0, 0, NT_PRFPREG, 16, },
> + [44]={ /* xmm4 */ 0, 0, NT_PRFPREG, 16, },
> + [45]={ /* xmm5 */ 0, 0, NT_PRFPREG, 16, },
> + [46]={ /* xmm6 */ 0, 0, NT_PRFPREG, 16, },
> + [47]={ /* xmm7 */ 0, 0, NT_PRFPREG, 16, },
> + [48]={ /* xmm8 */ 0, 0, NT_PRFPREG, 16, },
> + [49]={ /* xmm9 */ 0, 0, NT_PRFPREG, 16, },
> + [50]={ /* xmm10 */ 0, 0, NT_PRFPREG, 16, },
> + [51]={ /* xmm11 */ 0, 0, NT_PRFPREG, 16, },
> + [52]={ /* xmm12 */ 0, 0, NT_PRFPREG, 16, },
> + [53]={ /* xmm13 */ 0, 0, NT_PRFPREG, 16, },
> + [54]={ /* xmm14 */ 0, 0, NT_PRFPREG, 16, },
> + [55]={ /* xmm15 */ 0, 0, NT_PRFPREG, 16, },
> + [56]={ /* mxcsr */ 0, 0, NT_PRFPREG, 4, },
> + [57]={ /* orig_rax*/ 15*8, 8, NT_PRSTATUS, 8, },
> +};
> +
> +
> +
> +static int gdb_remote_register_info(struct gdb_connection *p,
> + struct task_struct *task,
> + unsigned number,
> + unsigned *pos, unsigned *count,
> + unsigned *bytes)
> +{
> + const struct user_regset_view *rs = task_user_regset_view(task);
> + int rsn = -1;
> +
> + if(rs == 0)
> + return -ENOENT;
> +
> + /* pr_debug ("gdb_remote_register_info rs=%p rs->n=%u\n", rs, rs->n); */
> +
> +#define GMRSIZE (sizeof(struct gdb_map_regset))
> +
> + if(rs->e_machine == EM_386) {
> + if (number < sizeof(arch_i386_map_regset)/GMRSIZE) {
> + *pos = arch_i386_map_regset[number].pos;
> + *count = arch_i386_map_regset[number].count;
> + *bytes = arch_i386_map_regset[number].bytes;
> + rsn = arch_i386_map_regset[number].rsn;
> + }
> + } else if(rs->e_machine == EM_X86_64) {
> + if (number < sizeof(arch_x86_64_map_regset)/GMRSIZE) {
> + *pos = arch_x86_64_map_regset[number].pos;
> + *count = arch_x86_64_map_regset[number].count;
> + *bytes = arch_x86_64_map_regset[number].bytes;
> + rsn = arch_x86_64_map_regset[number].rsn;
> + }
> + } /* else ... rsn stays -1. */
> +
> +#undef GMRSIZE
> +
> + /* Now map to the per-architecture regset index, based on the
> + elf core_note_type we found. */
> + if (rsn >= 0) {
> + unsigned j;
> + for(j=0; j<rs->n; j++) {
> + if(rs->regsets[j].core_note_type == rsn)
> + return j;
> + }
> + }
> +
> + /* Invalid machines, register numbers, rsns, or unset rsns all
> + * fall through here.
> + */
> + return -ENOENT;
> +}
> +
> +
> +
> +/* Process an entire, checksum-confirmed $command# at the front of
> + * p->input_buf[]. The input and output mutexes are being held.
> + */
> +static void handle_gdb_command_packet (struct gdb_connection *p, struct task_struct *task)
> +{
> + unsigned long arg1, arg2, arg3;
> + size_t op_start;
> + int rc = 0;
> + int i, j;
> +
> + pr_debug ("gdb packet code %c\n", p->input_buf[1]);
> +
> + switch (p->input_buf[1]) {
> + case '?':
> + if (p->at_quiesce_do != UTRACE_STOP) {
> + /* shouldn't happen */
> + send_sig(SIGTRAP, task, 1);
> +#if 0
> + rc = utrace_control (task, p->engine, UTRACE_INTERRUPT);
> + if (rc == -EINPROGRESS)
> + rc = utrace_barrier(task, p->engine);
> +#endif
> +
> + /* Note that we don't enqueue a reply packet here,
> + but make gdb wait for a response from the
> + utrace report_FOO callbacks. */
> + p->skip_signals ++;
> + } else {
> + push_output_packet (p, p->stopcode);
> + }
> + break;
> +
> + case 'i': /* [ADDR[,NNN]] */
> + case 's': /* [ADDR] */
> + /* XXX: if !arch_has_single_step() ... then what? */
> + case 'c': /* [ADDR] */
> + rc = sscanf(& p->input_buf[2], "%lx,%lx", &arg1, &arg2);
> + if (rc >= 1) { /* Have a PC? */
> + /* XXX: set it */
> + }
> + if (rc >= 2) { /* ,NNN present */
> + /* XXX: disregard it. */
> + }
> + /* XXX: args ignored */
> + p->stopcode[0]='\0';
> + p->at_quiesce_do =
> + ((p->input_buf[1]=='c' || !arch_has_single_step())
> + ? UTRACE_RESUME : UTRACE_SINGLESTEP);
> + if (p->at_quiesce_do == UTRACE_SINGLESTEP)
> + p->stop_signals ++;
> + utrace_control (task, p->engine, p->at_quiesce_do);
> + break;
> + case 'C': /* SIG[;ADDR] */
> + case 'S': /* SIG[;ADDR] */
> + /* XXX: if !arch_has_single_step() ... then what? */
> + case 'I': /* SIG[;ADDR[,NNN?]] */
> + rc = sscanf(& p->input_buf[2], "%lx;%lx,%lx", &arg1, &arg2, &arg3);
> + if (rc >= 1) { /* SIG present */
> + send_sig ((int)arg1, task, 1);
> + }
> + if (rc >= 2) { /* ;ADDR present */
> + /* XXX: not done */
> + }
> + if (rc >= 3) { /* ,NNN present */
> + /* XXX: disregard it. */
> + }
> + p->skip_signals ++;
> + p->stopcode[0]='\0';
> + p->at_quiesce_do =
> + ((p->input_buf[1]=='C' || !arch_has_single_step())
> + ? UTRACE_RESUME : UTRACE_SINGLESTEP);
> + if (p->at_quiesce_do == UTRACE_SINGLESTEP)
> + p->stop_signals ++;
> + utrace_control (task, p->engine, p->at_quiesce_do);
> + /* Response will come at next report_signal. */
> + break;
> + case 'D':
> + push_output_packet (p, "OK");
> + /* NB: the .release fop callback performs actual utrace detach. */
> + break;
> + case 'g':
> + op_start = push_output_packet_start(p);
> + /* GDB_BUFMAX stands for some random large number,
> + * known to be larger than the number of gdb indexed
> + * registers. */
> + for (i=0; i<GDB_BUFMAX; i++) {
> + unsigned rs_count;
> + unsigned rs_pos;
> + unsigned bytes;
> + const struct user_regset_view* rsv;
> + const struct user_regset* rs;
> + unsigned char reg_contents[16]; /* maximum reg. width */
> +
> + int rsn = gdb_remote_register_info(p, task, i,
> + &rs_pos, &rs_count,
> + &bytes);
> +
> + if (rsn < 0)
> + break;
> +
> + /* If we want to extract register data, make sure
> + we're fetching at least that much. */
> + BUG_ON (rs_count > 0 && rs_count < bytes);
> + /* Assert reg_contents size is right. */
> + BUG_ON(sizeof(reg_contents) < bytes ||
> + sizeof(reg_contents) < rs_count);
> +
> + if (rs_count) { /* real register */
> + rsv = task_user_regset_view(task);
> + BUG_ON(rsn >= rsv->n);
> + rs = & rsv->regsets[rsn];
> +
> + /* Extract the register value into reg_contents[]. */
> + rc = (rs->get) (task, rs, rs_pos, rs_count,
> + reg_contents, NULL);
> + if (rc)
> + break;
> + } else { /* dummy value */
> + memset (reg_contents, 0, sizeof(reg_contents));
> + }
> +
> + /* Hex-dump it. */
> + /* pr_debug ("gdb register %d => rsn %d p%u c%u b%u (",
> + i, rsn, rs_pos, rs_count, bytes); */
> + /* XXX: endianness adjust for count != bytes */
> + for(j=0; j<bytes; j++) {
> + /* pr_debug("%02x", reg_contents[j]);*/
> + push_output_hex(p, reg_contents[j]);
> + }
> + /* pr_debug(")\n"); */
> +
> + }
> + push_output_packet_end(p, op_start);
> + break;
> + case 'G':
> + i = 0;
> + op_start = 2; /* use as input pointer, past $G in command */
> + while(p->input_buf[op_start] != '#' &&
> + op_start < p->input_buf_size) {
> + unsigned rs_count;
> + unsigned rs_pos;
> + unsigned bytes;
> + const struct user_regset_view* rsv;
> + const struct user_regset* rs;
> + unsigned char reg_contents[16]; /* maximum reg. width */
> +
> + int rsn = gdb_remote_register_info(p, task, i,
> + &rs_pos, &rs_count,
> + &bytes);
> + /* pr_debug ("gdb register %d => rsn %d p%u c%u b%u\n",
> + i, rsn, rs_pos, rs_count, bytes); */
> +
> + if (rsn < 0)
> + break;
> +
> + /* If we want to extract register data, make sure
> + we're fetching at least that much. */
> + BUG_ON(rs_count > 0 && rs_count < bytes);
> + /* Assert reg_contents size is right. */
> + BUG_ON(sizeof(reg_contents) < bytes ||
> + sizeof(reg_contents) < rs_count);
> +
> + /* Remaining packet too short? */
> + if ((op_start + 2*bytes + 3) < p->input_buf_size)
> + break;
> +
> + /* 0-fill the register copy. XXX initialize
> + * it from rs->get() instead?
> + */
> + memset (reg_contents, 0, sizeof(reg_contents));
> +
> + /* Hex-unconvert all the bytes. */
> + /* XXX: endianness adjust for count != bytes */
> + for(j=0; j<bytes; j++)
> + reg_contents[j]=byteme(p->input_buf[op_start+2*j],
> + p->input_buf[op_start+2*j+1]);
> + op_start += 2*bytes;
> +
> + if (rs_count) { /* real register */
> + BUG_ON(rs_count > sizeof(reg_contents));
> + rsv = task_user_regset_view(task);
> + BUG_ON(rsn >= rsv->n);
> + rs = & rsv->regsets[rsn];
> +
> + /* Set the register value from reg_contents[]. */
> + rc = (rs->set) (task, rs, rs_pos, rs_count,
> + reg_contents, NULL);
> + if (rc)
> + break;
> + } else { /* dummy register */
> + ;
> + }
> + }
> + if (p->input_buf[op_start] == '#' && rc == 0)
> + push_output_packet (p, "OK");
> + else
> + push_output_packet (p, "E01");
> + break;
> + case 'p': /* REG */
> + break;
> + case 'P': /* REG=VAL */
> + break;
> + case 'm': /* ADDR,LENGTH */
> + rc = sscanf(& p->input_buf[2], "%lx,%lx", &arg1, &arg2);
> + if (rc != 2)
> + push_output_packet(p, "E01");
> + else {
> + size_t o = push_output_packet_start (p);
> + while (arg2 > 0) {
> + unsigned char value;
> +
> + /* Simply stop looping if requested
> + length was too large. gdb will
> + probably retry from this point
> + on. */
> + if (p->output_buf_size + 5 > GDB_BUFMAX)
> + break;
> +
> + rc = access_process_vm(task, arg1, &value, 1, 0);
> + if (rc != 1)
> + break; /* EFAULT */
> + else
> + push_output_hex (p, value);
> +
> + arg1++;
> + arg2--;
> + }
> + push_output_packet_end (p, o);
> + }
> + break;
> + case 'M': /* ADDR,LENGTH:XX */
> + /* `i' will index p->input_buf to consume XX hex bytes. */
> + rc = sscanf(& p->input_buf[2], "%lx,%lx:%n",
> + &arg1, &arg2, &i);
> + op_start = i + 2; /* Skip the leading $M also. */
> + if (rc < 2) {
> + push_output_packet(p, "E01");
> + break;
> + }
> + while (arg2 > 0) {
> + unsigned char value;
> +
> + /* Check that enough input bytes left for
> + * these two hex chars, plus the #XX checksum.
> + */
> + if (i+4 >= p->input_buf_size)
> + break;
> +
> + value = byteme(p->input_buf[i],
> + p->input_buf[i+1]);
> + rc = access_process_vm(task, arg1, &value, 1, 1);
> + if (rc != 1)
> + break; /* EFAULT */
> +
> + i += 2;
> + arg1++;
> + arg2--;
> + }
> + if (arg2 != 0)
> + push_output_packet(p, "E02");
> + else
> + push_output_packet(p, "OK");
> + break;
> + default:
> + push_output_packet (p, "");
> + }
> +}
> +
> +
> +
> +
> +/* ------------------------------------------------------------------------ */
> +
> +/* gdb control callbacks */
> +
> +#define get_proc_task(inode) get_pid_task(PROC_I((inode))->pid, PIDTYPE_PID)
> +
> +static int proc_gdb_open(struct inode *inode, struct file *filp)
> +{
> + struct task_struct *task = get_proc_task(inode);
> + int ret = -EBUSY;
> + struct gdb_connection *p;
> + struct list_head *l;
> +
> + pr_debug ("opened /proc/%d/gdb\n", task->pid);
> +
> + /* Reject kernel threads. */
> + if (task->flags & PF_KTHREAD) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + /* Reject if connection is for other than tg-leader thread. */
> + if (task_pid_nr(task) != task_tgid_nr(task)) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + mutex_lock (& gdb_connections_mutex);
> +
> + /* Reject if a connection exists for the thread group
> + * leader.
> + */
> + list_for_each(l, &gdb_connections) {
> + p = list_entry (l, struct gdb_connection, link);
> + if (p->target == task_tgid_nr(task)) {
> + ret = -EBUSY;
> + goto out_mutex;
> + }
> + }
> + /* (Don't unlock yet, to defeat a race of two concurrent opens.) */
> +
> + p = kzalloc(sizeof (struct gdb_connection), GFP_KERNEL);
> + if (!p) {
> + ret = -ENOMEM;
> + goto out_mutex;
> + }
> +
> + /* Send initial ping to gdb. */
> + push_output_packet (p, "");
> +
> + mutex_init(& p->output_mutex);
> + init_waitqueue_head(& p->output_wait);
> +
> + mutex_init(& p->input_mutex);
> + init_waitqueue_head(& p->input_wait);
> +
> + p->target = task->tgid;
> +
> + /* NB: During attach, we don't want to bother the target.
> + Soon though a send_sig will interrupt it. */
> + p->at_quiesce_do = UTRACE_RESUME;
> +
> + p->engine = utrace_attach_task(task,
> + UTRACE_ATTACH_CREATE |
> + UTRACE_ATTACH_EXCLUSIVE,
> + &gdb_utrace_ops,
> + p);
> + if (IS_ERR(p->engine) || p->engine==NULL) {
> + ret = -EINVAL;
> + goto out_free;
> + }
> +
> + ret = utrace_set_events(task, p->engine,
> + UTRACE_EVENT_SIGNAL_ALL|
> + UTRACE_EVENT(QUIESCE)|
> + UTRACE_EVENT(DEATH)|
> + UTRACE_EVENT(EXIT)|
> + UTRACE_EVENT(EXEC)|
> + UTRACE_EVENT(CLONE));
> + pr_debug ("utrace_set_events sent, ret=%d\n", ret);
> + if (!ret)
> + ;
> +
> + filp->private_data = p;
> +
> + INIT_LIST_HEAD(& p->link);
> + list_add(&gdb_connections, &p->link);
> +
> + p->stop_signals ++;
> + send_sig(SIGTRAP, task, 1);
> +#if 0
> + ret = utrace_control(task, p->engine, UTRACE_INTERRUPT);
> + if (ret == -EINPROGRESS)
> + ret = utrace_barrier(task, p->engine);
> +#endif
> +
> + goto out_mutex;
> +
> +out_free:
> + kfree(p);
> +out_mutex:
> + mutex_unlock (& gdb_connections_mutex);
> +out:
> + return ret;
> +}
> +
> +
> +static int proc_gdb_release(struct inode *inode, struct file *filp)
> +{
> + struct task_struct *task = get_proc_task(inode);
> + struct gdb_connection *p = filp->private_data;
> + int ret = 0;
> +
> + mutex_lock (& gdb_connections_mutex);
> +
> + if (task == NULL) {
> + /* The thread is already gone; report_death was already called. */
> + pr_debug ("gdb %d releasing old\n", p->target);
> + } else {
> + pr_debug ("gdb %d releasing current\n", p->target);
> +
> + ret = utrace_set_events(task, p->engine, 0);
> + if (ret == -EINPROGRESS)
> + ret = utrace_barrier(task, p->engine);
> + /* No more callbacks will be received! */
> +
> + ret = utrace_control(task, p->engine, UTRACE_DETACH); /* => RESUME */
> + if (ret == -EINPROGRESS)
> + ret = utrace_barrier(task, p->engine);
> +
> + utrace_engine_put (p->engine);
> + }
> +
> + list_del(&p->link);
> + kfree(p);
> +
> + mutex_unlock (& gdb_connections_mutex);
> +
> + return ret;
> +}
> +
> +
> +
> +static int proc_gdb_ioctl(struct inode *inode, struct file *file,
> + unsigned int cmd, unsigned long arg)
> +{
> + /* XXX: GDB usually thinks that a file name for "target
> + * remote" implies a serial port with tty-ish ioctl's
> + * available. We pretend to accept them all. */
> + return 0;
> +}
> +
> +
> +
> +static ssize_t proc_gdb_read(struct file *filp, char __user *buf,
> + size_t count, loff_t *ppos)
> +{
> + struct gdb_connection *p = filp->private_data;
> + struct task_struct *task;
> + int rc = 0;
> + size_t len;
> +
> + task = find_task_by_vpid (p->target);
> + if (!task)
> + return -EINVAL;
> +
> + if ((p->output_buf_size <= p->output_buf_read) &&
> + filp->f_flags & O_NONBLOCK)
> + return -EAGAIN;
> +
> +again:
> + rc = wait_event_interruptible (p->output_wait,
> + (p->output_buf_size > p->output_buf_read));
> + if (rc)
> + goto out;
> +
> + mutex_lock(&p->output_mutex);
> +
> + if(p->output_buf_size <= p->output_buf_read) {
> + mutex_unlock(&p->output_mutex);
> + goto again;
> + }
> +
> + len = min (count, (size_t)(p->output_buf_size - p->output_buf_read));
> + if (copy_to_user (buf, & p->output_buf[p->output_buf_read], len)) {
> + rc = -EFAULT;
> + goto out_unlock;
> + }
> +
> + pr_debug ("sent %u bytes (%ld left) data (%.*s)\n",
> + (unsigned)len,
> + ((long)p->output_buf_size-(long)p->output_buf_read)-len,
> + (int)len, & p->output_buf[p->output_buf_read]);
> +
> + p->output_buf_read += len;
> + rc = len;
> +
> + /* If whole packet is consumed, reset for next one. */
> + BUG_ON (p->output_buf_read > p->output_buf_size);
> + if (p->output_buf_read == p->output_buf_size) {
> + p->output_buf_read = 0;
> + p->output_buf_size = 0;
> + }
> +
> +out_unlock:
> + mutex_unlock(&p->output_mutex);
> +
> +out:
> + return rc;
> +}
> +
> +
> +static ssize_t proc_gdb_write(struct file *filp, const char __user *buf,
> + size_t count, loff_t *ppos)
> +{
> + struct gdb_connection *p = filp->private_data;
> + size_t last_input_buf_size;
> + struct task_struct *task;
> + size_t len;
> + int ret = 0;
> +
> + task = find_task_by_vpid (p->target);
> + if (!task)
> + return -EINVAL;
> +
> +again:
> + ret = wait_event_interruptible (p->input_wait,
> + (p->input_buf_size < GDB_BUFMAX));
> + if (ret)
> + goto out;
> +
> + mutex_lock(&p->input_mutex);
> + if (p->input_buf_size == GDB_BUFMAX) {
> + mutex_unlock(&p->input_mutex);
> + goto again;
> + }
> + mutex_lock(&p->output_mutex);
> +
> + /* We now know there is some room in the input buffer. Upon
> + entry, the input_buf will either be empty, or contain a
> + partial gdb request packet. */
> +
> + /* Copy the data. */
> + len = min (count, (size_t)(GDB_BUFMAX - p->input_buf_size));
> + if (copy_from_user (& p->input_buf[p->input_buf_size], buf, len)) {
> + ret = -EFAULT;
> + goto out_unlock;
> + }
> +
> + /* pr_debug ("received data %.*s\n", (int)len, & p->input_buf[p->input_buf_size]); */
> +
> + p->input_buf_size += len;
> + ret = len;
> +
> + /* Process any packets in the buffer to restore the incoming
> + invariant. (Normal GDB will not send more than one packet
> + before waiting for a response.) */
> +
> + /* We iterate until we can no longer shrink the input buffer. Usually
> + we will not iterate more than once, since there may be one +/-
> + ack byte and/or one gdb packet. */
> + last_input_buf_size = 0;
> + while (p->input_buf_size
> + && p->input_buf_size != last_input_buf_size) {
> + last_input_buf_size = p->input_buf_size;
> +
> + if (p->input_buf[0] == '+') {
> + /* This must have been an ack to our
> + * previously output packet.
> + * Consume the input.
> + */
> + memmove (&p->input_buf[0], &p->input_buf[1], --p->input_buf_size);
> + } else if (p->input_buf[0] == '-') {
> + /* Whoops, a nak. Unfortunately, we don't
> + * handle transmission errors by
> + * retransmitting the last output_buf; it's
> + * already gone. OTOH we should not encounter
> + * transmission errors on a reliable channel
> + * such as a read syscall.
> + * Consume the input.
> + */
> + printk(KERN_WARNING "Unexpected NAK received"
> + "on /proc/%d/gdb connection.\n", task_pid_nr(task));
> + memmove (&p->input_buf[0], &p->input_buf[1], --p->input_buf_size);
> + } else if (p->input_buf[0] == 3) { /* ^C == INTR */
> + /* NB: don't overwrite 'ret'. */
> + pr_debug ("received gdb interrupt\n");
> + p->stop_signals ++;
> + send_sig(SIGTRAP, task, 1);
> +#if 0
> + int rc = utrace_control(task, p->engine, UTRACE_INTERRUPT);
> + if (rc == -EINPROGRESS)
> + rc = utrace_barrier(task, p->engine);
> +#endif
> + /* p->at_quiesce_do will be set in report_signal(SIGNAL_REPORT) */
> + /* NB: this packet does not generate an +/- ack.
> + Consume the input. */
> + memmove (&p->input_buf[0], &p->input_buf[1], --p->input_buf_size);
> + } else if (p->input_buf[0] == '$') { /* command packet */
> + int j;
> + unsigned char checksum = 0;
> + for (j=1; j<p->input_buf_size-2; j++) {
> + if (p->input_buf[j] == '#') {
> + unsigned char checksum2;
> + checksum2 = byteme (p->input_buf[j+1],
> + p->input_buf[j+2]);
> + pr_debug ("received gdb packet %.*s\n",
> + j+3, & p->input_buf[0]);
> + if (checksum == checksum2) {
> + push_output (p, '+');
> + handle_gdb_command_packet (p, task);
> + } else {
> + push_output (p, '-');
> + }
> + /* Consume the whole packet. */
> + p->input_buf_size -= (j+3);
> + memmove(&p->input_buf[0], &p->input_buf[j+3],
> + p->input_buf_size);
> + break;
> + } else {
> + checksum += p->input_buf[j];
> + }
> + } /* End searching for end of packet */
> +
> + /* We may not have found the #<hex><hex>
> + * checksum. If so, leave the partial packet
> + * in input_buf. Since input_buf_size will
> + * not have decreased, the while() loop above
> + * will detect a fixpoint and exit.
> + *
> + * Alternately, there could be another gdb packet
> + * just behind the one we just consumed. In this
> + * we'll iterate one more time in this loop.
> + */
> + } else { /* junk character */
> + printk(KERN_WARNING "Unexpected character (%x) received"
> + " on /proc/%d/gdb connection.\n",
> + (int) p->input_buf[0], task_pid_nr(task));
> + /* Consume the input. */
> + memmove (&p->input_buf[0], &p->input_buf[1], --p->input_buf_size);
> + }
> + }
> +
> +out_unlock:
> + wake_up(&p->input_wait); /* Probably have more room in input_buf. */
> + wake_up(&p->output_wait); /* Probably have data in output_buf. */
> +
> + mutex_unlock(&p->output_mutex);
> + mutex_unlock(&p->input_mutex);
> +out:
> + return ret;
> +}
> +
> +
> +const struct file_operations proc_gdb_operations = {
> + .open = proc_gdb_open,
> + .read = proc_gdb_read,
> + .write = proc_gdb_write,
> + .release = proc_gdb_release,
> + .ioctl = proc_gdb_ioctl,
> +};
> +
> +