[PATCH] Linux Kernel GDB tracepoint module 2010-10-09

From: Hui Zhu
Date: Sat Oct 09 2010 - 02:46:03 EST


KGTP(https://code.google.com/p/kgtp/) is Linux Kernel GDB tracepoint module.
It make Linux Kernel supply a GDB remote debug interface. Then GDB in
current machine or remote machine(with netcat) can debug Linux through
GDB tracepoint without stop the Linux Kernel.
It support X86-32, X86-64, MIPS and ARM.

Now, KGTP 2010-10-09 release.
The change of this version is:
Add support of all QTFrame gdbrsp package. Commands in
http://sourceware.org/gdb/current/onlinedocs/gdb/tfind.html are
supported.
Fix pc bug make tdump work OK. Command in
http://sourceware.org/gdb/current/onlinedocs/gdb/tdump.html is
support.
Fix some bugs.

Please goto http://code.google.com/p/kgtp/wiki/HOWTO to get more info
about how to use KGTP.

And according to the comments of Christoph. I make a patch for Linux
Kernel and make it looks OK with checkpatch.pl.

Signed-off-by: Hui Zhu <teawater@xxxxxxxxx>
---
arch/arm/include/asm/gtp.h | 12
arch/mips/include/asm/gtp.h | 16
arch/x86/include/asm/gtp.h | 16
lib/Kconfig.debug | 8
lib/Makefile | 2
lib/gtp.c | 3466 ++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 3520 insertions(+)

--- /dev/null
+++ b/arch/arm/include/asm/gtp.h
@@ -0,0 +1,12 @@
+#ifndef _ASM_ARM_GTP_H_
+#define _ASM_ARM_GTP_H_
+
+#define ULONGEST uint64_t
+#define LONGEST int64_t
+#define CORE_ADDR unsigned long
+
+#define GTP_REGS_PC(regs) ((regs)->uregs[15])
+
+#define GTP_GDBRSP_REG_SIZE 336
+
+#endif
--- /dev/null
+++ b/arch/mips/include/asm/gtp.h
@@ -0,0 +1,16 @@
+#ifndef _ASM_MIPS_GTP_H_
+#define _ASM_MIPS_GTP_H_
+
+#define ULONGEST uint64_t
+#define LONGEST int64_t
+#define CORE_ADDR unsigned long
+
+#define GTP_REGS_PC(regs) ((regs)->cp0_epc)
+
+#ifdef CONFIG_32BIT
+#define GTP_GDBRSP_REG_SIZE 304
+#else
+#define GTP_GDBRSP_REG_SIZE 608
+#endif
+
+#endif
--- /dev/null
+++ b/arch/x86/include/asm/gtp.h
@@ -0,0 +1,16 @@
+#ifndef _ASM_X86_GTP_H_
+#define _ASM_X86_GTP_H_
+
+#define ULONGEST uint64_t
+#define LONGEST int64_t
+#define CORE_ADDR unsigned long
+
+#define GTP_REGS_PC(regs) ((regs)->ip)
+
+#ifdef CONFIG_X86_32
+#define GTP_GDBRSP_REG_SIZE 128
+#else
+#define GTP_GDBRSP_REG_SIZE 296
+#endif
+
+#endif
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1150,6 +1150,14 @@ config ATOMIC64_SELFTEST

If unsure, say N.

+config GTP
+ tristate "GDB tracepoint support"
+ depends on X86 || ARM || MIPS
+ depends on KPROBES
+ depends on PROC_FS
+ ---help---
+ Supply GDB tracepoint interface in /proc/gtp.
+
source "samples/Kconfig"

source "lib/Kconfig.kgdb"
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -106,6 +106,8 @@ obj-$(CONFIG_GENERIC_ATOMIC64) += atomic

obj-$(CONFIG_ATOMIC64_SELFTEST) += atomic64_test.o

+obj-$(CONFIG_GTP) += gtp.o
+
hostprogs-y := gen_crc32table
clean-files := crc32table.h

--- /dev/null
+++ b/lib/gtp.c
@@ -0,0 +1,3466 @@
+/*
+ * Kernel GDB tracepoint module.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright(C) Hui Zhu (teawater@xxxxxxxxx), 2010
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+#include <linux/poll.h>
+#include <linux/kprobes.h>
+#include <asm/gtp.h>
+
+#ifdef GTPDEBUG
+#define GTP_DEBUG KERN_WARNING
+#endif
+
+#define GTP_RW_MAX 16384
+
+#define GTP_FRAME_SIZE 5242880
+#define GTP_FRAME_HEAD_SIZE (1 + sizeof(char *) + sizeof(ULONGEST))
+#define GTP_FRAME_REG_SIZE (1 + sizeof(char *) + sizeof(struct pt_regs))
+#define GTP_FRAME_MEM_SIZE (1 + sizeof(char *) \
+ + sizeof(struct gtp_frame_mem))
+#define GTP_FRAME_VAR_SIZE (1 + sizeof(char *) \
+ + sizeof(struct gtp_frame_var))
+
+#define TOHEX(h) ((h) > 9 ? (h) + 'a' - 10 : (h) + '0')
+
+struct action_agent_exp {
+ unsigned int size;
+ uint8_t *buf;
+ int need_var_lock;
+};
+
+struct action_m {
+ int regnum;
+ CORE_ADDR offset;
+ size_t size;
+};
+
+struct action {
+ struct action *next;
+ char type;
+ char *src;
+ union {
+ ULONGEST reg_mask;
+ struct action_agent_exp exp;
+ struct action_m m;
+ } u;
+};
+
+struct gtpsrc {
+ struct gtpsrc *next;
+ char *src;
+};
+
+enum gtp_stop_type {
+ gtp_stop_normal = 0,
+ gtp_stop_frame_full,
+ gtp_stop_efault,
+ gtp_stop_access_wrong_reg,
+ gtp_stop_agent_expr_code_error,
+ gtp_stop_agent_expr_stack_overflow,
+};
+
+struct gtp_entry {
+ struct gtp_entry *next;
+ int disable;
+ ULONGEST num;
+ ULONGEST addr;
+ ULONGEST step;
+ ULONGEST pass;
+ int nopass;
+ atomic_t current_pass;
+ int kpreg;
+ struct kprobe kp;
+ struct work_struct work;
+ enum gtp_stop_type reason;
+ struct action *cond;
+ struct action *action_list;
+ struct gtpsrc *src;
+};
+
+struct gtp_var {
+ struct gtp_var *next;
+ unsigned int num;
+ uint64_t val;
+ char *src;
+};
+
+struct gtp_frame_mem {
+ CORE_ADDR addr;
+ size_t size;
+};
+
+struct gtp_frame_var {
+ unsigned int num;
+ uint64_t val;
+};
+
+struct gtpro_entry {
+ struct gtpro_entry *next;
+ CORE_ADDR start;
+ CORE_ADDR end;
+};
+
+static struct gtp_entry *gtp_list;
+static struct gtp_entry *current_gtp;
+static struct action *current_gtp_action;
+static struct gtpsrc *current_gtp_src;
+
+static struct workqueue_struct *gtp_wq;
+
+static char gtp_read_ack;
+static char *gtp_rw_buf;
+static char *gtp_rw_bufp;
+static size_t gtp_rw_size;
+
+static int gtp_start;
+
+static int gtp_disconnected_tracing;
+static int gtp_circular;
+
+static DEFINE_SPINLOCK(gtp_var_lock);
+static struct gtp_var *gtp_var_list;
+static unsigned int gtp_var_head;
+static unsigned int gtp_var_tail;
+static struct gtp_var **gtp_var_array;
+static struct gtp_var *current_gtp_var;
+#define GTP_VAR_CURRENT_TASK_ID 1
+static struct gtp_var gtp_var_current_task = {
+ .next = NULL,
+ .num = GTP_VAR_CURRENT_TASK_ID,
+ .src = "0:1:63757272656e745f7461736b",
+};
+
+static DEFINE_SPINLOCK(gtp_frame_lock);
+static char *gtp_frame;
+static char *gtp_frame_r_start;
+static char *gtp_frame_w_start;
+static char *gtp_frame_end;
+static int gtp_frame_is_circular;
+static char *gtp_frame_current;
+static int gtp_frame_current_num;
+static atomic_t gtp_frame_create;
+
+static struct gtpro_entry *gtpro_list;
+
+#ifdef CONFIG_X86
+static ULONGEST
+gtp_action_reg_read(struct pt_regs *regs, struct gtp_entry *tpe, int num)
+{
+ ULONGEST ret;
+
+ switch (num) {
+#ifdef CONFIG_X86_32
+ case 0:
+ ret = regs->ax;
+ break;
+ case 1:
+ ret = regs->cx;
+ break;
+ case 2:
+ ret = regs->dx;
+ break;
+ case 3:
+ ret = regs->bx;
+ break;
+ case 4:
+ ret = (ULONGEST)(CORE_ADDR)&regs->sp;
+ break;
+ case 5:
+ ret = regs->bp;
+ break;
+ case 6:
+ ret = regs->si;
+ break;
+ case 7:
+ ret = regs->di;
+ break;
+ case 8:
+ ret = regs->ip - 1;
+ break;
+ case 9:
+ ret = regs->flags;
+ break;
+ case 10:
+ ret = regs->cs;
+ break;
+ case 11:
+ ret = regs->ss;
+ break;
+ case 12:
+ ret = regs->ds;
+ break;
+ case 13:
+ ret = regs->es;
+ break;
+ case 14:
+ ret = regs->fs;
+ break;
+ case 15:
+ ret = regs->gs;
+ break;
+#else
+ case 0:
+ ret = regs->ax;
+ break;
+ case 1:
+ ret = regs->bx;
+ break;
+ case 2:
+ ret = regs->cx;
+ break;
+ case 3:
+ ret = regs->dx;
+ break;
+ case 4:
+ ret = regs->si;
+ break;
+ case 5:
+ ret = regs->di;
+ break;
+ case 6:
+ ret = regs->bp;
+ break;
+ case 7:
+ ret = regs->sp;
+ break;
+ case 8:
+ ret = regs->r8;
+ break;
+ case 9:
+ ret = regs->r9;
+ break;
+ case 10:
+ ret = regs->r10;
+ break;
+ case 11:
+ ret = regs->r11;
+ break;
+ case 12:
+ ret = regs->r12;
+ break;
+ case 13:
+ ret = regs->r13;
+ break;
+ case 14:
+ ret = regs->r14;
+ break;
+ case 15:
+ ret = regs->r15;
+ break;
+ case 16:
+ ret = regs->ip - 1;
+ break;
+ case 17:
+ ret = regs->flags;
+ break;
+ case 18:
+ ret = regs->cs;
+ break;
+ case 19:
+ ret = regs->ss;
+ break;
+#endif
+ default:
+ ret = 0;
+ tpe->reason = gtp_stop_access_wrong_reg;
+ break;
+ }
+
+ return ret;
+}
+
+static void
+gtp_regs2ascii(struct pt_regs *regs, char *buf)
+{
+#ifdef CONFIG_X86_32
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_regs2ascii: ax = 0x%x\n",
+ (unsigned int) regs->ax);
+ printk(GTP_DEBUG "gtp_regs2ascii: cx = 0x%x\n",
+ (unsigned int) regs->cx);
+ printk(GTP_DEBUG "gtp_regs2ascii: dx = 0x%x\n",
+ (unsigned int) regs->dx);
+ printk(GTP_DEBUG "gtp_regs2ascii: bx = 0x%x\n",
+ (unsigned int) regs->bx);
+ printk(GTP_DEBUG "gtp_regs2ascii: sp = 0x%x\n",
+ (unsigned int) regs->sp);
+ printk(GTP_DEBUG "gtp_regs2ascii: bp = 0x%x\n",
+ (unsigned int) regs->bp);
+ printk(GTP_DEBUG "gtp_regs2ascii: si = 0x%x\n",
+ (unsigned int) regs->si);
+ printk(GTP_DEBUG "gtp_regs2ascii: di = 0x%x\n",
+ (unsigned int) regs->di);
+ printk(GTP_DEBUG "gtp_regs2ascii: ip = 0x%x\n",
+ (unsigned int) regs->ip);
+ printk(GTP_DEBUG "gtp_regs2ascii: flags = 0x%x\n",
+ (unsigned int) regs->flags);
+ printk(GTP_DEBUG "gtp_regs2ascii: cs = 0x%x\n",
+ (unsigned int) regs->cs);
+ printk(GTP_DEBUG "gtp_regs2ascii: ss = 0x%x\n",
+ (unsigned int) regs->ss);
+ printk(GTP_DEBUG "gtp_regs2ascii: ds = 0x%x\n",
+ (unsigned int) regs->ds);
+ printk(GTP_DEBUG "gtp_regs2ascii: es = 0x%x\n",
+ (unsigned int) regs->es);
+ printk(GTP_DEBUG "gtp_regs2ascii: fs = 0x%x\n",
+ (unsigned int) regs->fs);
+ printk(GTP_DEBUG "gtp_regs2ascii: gs = 0x%x\n",
+ (unsigned int) regs->gs);
+#endif
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->ax));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->cx));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->dx));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->bx));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->sp));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->bp));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->si));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->di));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->ip));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->flags));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->cs));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->ss));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->ds));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->es));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->fs));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->gs));
+ buf += 8;
+#else
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_regs2ascii: ax = 0x%lx\n", regs->ax);
+ printk(GTP_DEBUG "gtp_regs2ascii: bx = 0x%lx\n", regs->bx);
+ printk(GTP_DEBUG "gtp_regs2ascii: cx = 0x%lx\n", regs->cx);
+ printk(GTP_DEBUG "gtp_regs2ascii: dx = 0x%lx\n", regs->dx);
+ printk(GTP_DEBUG "gtp_regs2ascii: si = 0x%lx\n", regs->si);
+ printk(GTP_DEBUG "gtp_regs2ascii: di = 0x%lx\n", regs->di);
+ printk(GTP_DEBUG "gtp_regs2ascii: bp = 0x%lx\n", regs->bp);
+ printk(GTP_DEBUG "gtp_regs2ascii: sp = 0x%lx\n", regs->sp);
+ printk(GTP_DEBUG "gtp_regs2ascii: r8 = 0x%lx\n", regs->r8);
+ printk(GTP_DEBUG "gtp_regs2ascii: r9 = 0x%lx\n", regs->r9);
+ printk(GTP_DEBUG "gtp_regs2ascii: r10 = 0x%lx\n", regs->r10);
+ printk(GTP_DEBUG "gtp_regs2ascii: r11 = 0x%lx\n", regs->r11);
+ printk(GTP_DEBUG "gtp_regs2ascii: r12 = 0x%lx\n", regs->r12);
+ printk(GTP_DEBUG "gtp_regs2ascii: r13 = 0x%lx\n", regs->r13);
+ printk(GTP_DEBUG "gtp_regs2ascii: r14 = 0x%lx\n", regs->r14);
+ printk(GTP_DEBUG "gtp_regs2ascii: r15 = 0x%lx\n", regs->r15);
+ printk(GTP_DEBUG "gtp_regs2ascii: ip = 0x%lx\n", regs->ip);
+ printk(GTP_DEBUG "gtp_regs2ascii: flags = 0x%lx\n", regs->flags);
+ printk(GTP_DEBUG "gtp_regs2ascii: cs = 0x%lx\n", regs->cs);
+ printk(GTP_DEBUG "gtp_regs2ascii: ss = 0x%lx\n", regs->ss);
+#endif
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->ax));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->bx));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->cx));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->dx));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->si));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->di));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->bp));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->sp));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->r8));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->r9));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->r10));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->r11));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->r12));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->r13));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->r14));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->r15));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->ip));
+ buf += 16;
+ sprintf(buf, "%08x",
+ (unsigned int) swab32((unsigned int)regs->flags));
+ buf += 8;
+ sprintf(buf, "%08x",
+ (unsigned int) swab32((unsigned int)regs->cs));
+ buf += 8;
+ sprintf(buf, "%08x",
+ (unsigned int) swab32((unsigned int)regs->ss));
+ buf += 8;
+#endif
+}
+#endif
+
+#ifdef CONFIG_MIPS
+static ULONGEST
+gtp_action_reg_read(struct pt_regs *regs, struct gtp_entry *tpe, int num)
+{
+ ULONGEST ret;
+
+ if (num > 90) {
+ /* GDB convert the reg number to a GDB
+ [1 * gdbarch_num_regs .. 2 * gdbarch_num_regs) REGNUM
+ in function mips_dwarf_dwarf2_ecoff_reg_to_regnum. */
+ num -= 90;
+ }
+
+ if (num >= 0 && num < 31) {
+ ret = regs->regs[num];
+ } else {
+ switch (num) {
+ case 32:
+ ret = regs->cp0_status;
+ break;
+ case 33:
+ ret = regs->lo;
+ break;
+ case 34:
+ ret = regs->hi;
+ break;
+ case 35:
+ ret = regs->cp0_badvaddr;
+ break;
+ case 36:
+ ret = regs->cp0_cause;
+ break;
+ case 37:
+ ret = regs->cp0_epc;
+ break;
+ default:
+ ret = 0;
+ tpe->reason = gtp_stop_access_wrong_reg;
+ break;
+ }
+ }
+
+ return ret;
+};
+
+static void
+gtp_regs2ascii(struct pt_regs *regs, char *buf)
+{
+#ifdef GTP_DEBUG
+ {
+ int i;
+
+ for (i = 0; i < 32; i++)
+ printk(GTP_DEBUG "gtp_gdbrsp_g: r%d = 0x%lx\n", i,
+ regs->regs[i]);
+ }
+ printk(GTP_DEBUG "gtp_gdbrsp_g: status = 0x%lx\n",
+ regs->cp0_status);
+ printk(GTP_DEBUG "gtp_gdbrsp_g: lo = 0x%lx\n", regs->lo);
+ printk(GTP_DEBUG "gtp_gdbrsp_g: hi = 0x%lx\n", regs->hi);
+ printk(GTP_DEBUG "gtp_gdbrsp_g: badvaddr = 0x%lx\n",
+ regs->cp0_badvaddr);
+ printk(GTP_DEBUG "gtp_gdbrsp_g: cause = 0x%lx\n", regs->cp0_cause);
+ printk(GTP_DEBUG "gtp_gdbrsp_g: pc = 0x%lx\n", regs->cp0_epc);
+#endif
+
+#ifdef CONFIG_32BIT
+#define OUTFORMAT "%08lx"
+#define REGSIZE 8
+#ifdef __LITTLE_ENDIAN
+#define SWAB(a) swab32(a)
+#else
+#define SWAB(a) (a)
+#endif
+#else
+#define OUTFORMAT "%016lx"
+#define REGSIZE 16
+#ifdef __LITTLE_ENDIAN
+#define SWAB(a) swab64(a)
+#else
+#define SWAB(a) (a)
+#endif
+#endif
+ {
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ sprintf(buf, OUTFORMAT,
+ (unsigned long) SWAB(regs->regs[i]));
+ buf += REGSIZE;
+ }
+ }
+
+ sprintf(buf, OUTFORMAT,
+ (unsigned long) SWAB(regs->cp0_status));
+ buf += REGSIZE;
+ sprintf(buf, OUTFORMAT,
+ (unsigned long) SWAB(regs->lo));
+ buf += REGSIZE;
+ sprintf(buf, OUTFORMAT,
+ (unsigned long) SWAB(regs->hi));
+ buf += REGSIZE;
+ sprintf(buf, OUTFORMAT,
+ (unsigned long) SWAB(regs->cp0_badvaddr));
+ buf += REGSIZE;
+ sprintf(buf, OUTFORMAT,
+ (unsigned long) SWAB(regs->cp0_cause));
+ buf += REGSIZE;
+ sprintf(buf, OUTFORMAT,
+ (unsigned long) SWAB(regs->cp0_epc));
+ buf += REGSIZE;
+#undef OUTFORMAT
+#undef REGSIZE
+#undef SWAB
+}
+#endif
+
+#ifdef CONFIG_ARM
+static ULONGEST
+gtp_action_reg_read(struct pt_regs *regs, struct gtp_entry *tpe, int num)
+{
+ if (num >= 0 && num < 16)
+ return regs->uregs[num];
+ else if (num == 25)
+ return regs->uregs[16];
+
+ tpe->reason = gtp_stop_access_wrong_reg;
+ return 0;
+}
+
+static void
+gtp_regs2ascii(struct pt_regs *regs, char *buf)
+{
+#ifdef __LITTLE_ENDIAN
+#define SWAB(a) swab32(a)
+#else
+#define SWAB(a) (a)
+#endif
+ int i;
+
+ for (i = 0; i < 16; i++) {
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_gdbrsp_g: r%d = 0x%lx\n",
+ i, regs->uregs[i]);
+#endif
+ sprintf(buf, "%08lx", (unsigned long) SWAB(regs->uregs[i]));
+ buf += 8;
+ }
+
+ /* f0-f7 fps */
+ memset(buf, '0', 200);
+ buf += 200;
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_gdbrsp_g: cpsr = 0x%lx\n", regs->uregs[16]);
+#endif
+ sprintf(buf, "%08lx",
+ (unsigned long) SWAB(regs->uregs[16]));
+ buf += 8;
+#undef SWAB
+}
+#endif
+
+static char *
+gtp_frame_next(char *frame)
+{
+ switch (frame[0]) {
+ case 'h':
+ frame += GTP_FRAME_HEAD_SIZE;
+ break;
+ case 'r':
+ frame += GTP_FRAME_REG_SIZE;
+ break;
+ case 'm': {
+ struct gtp_frame_mem *gfm;
+
+ gfm = (struct gtp_frame_mem *)
+ (frame + 1 + sizeof(char *));
+ frame += GTP_FRAME_MEM_SIZE;
+ frame += gfm->size;
+ }
+ break;
+ case 'v':
+ frame += GTP_FRAME_VAR_SIZE;
+ break;
+ case 'z':
+ frame = gtp_frame;
+ break;
+ default:
+ return NULL;
+ break;
+ }
+
+ return frame;
+}
+
+static char *
+gtp_frame_alloc(size_t size)
+{
+ unsigned long flags;
+ char *ret = NULL;
+
+ if (size > GTP_FRAME_SIZE)
+ return NULL;
+
+ spin_lock_irqsave(&gtp_frame_lock, flags);
+
+ if (gtp_frame_w_start + size > gtp_frame_end) {
+ if (gtp_circular) {
+ gtp_frame_is_circular = 1;
+ if (gtp_frame_w_start != gtp_frame_end)
+ gtp_frame_w_start[0] = 'z';
+ gtp_frame_w_start = gtp_frame;
+ gtp_frame_r_start = gtp_frame;
+ } else
+ goto out;
+ }
+
+ if (gtp_frame_is_circular) {
+ while (gtp_frame_w_start <= gtp_frame_r_start
+ && gtp_frame_w_start + size > gtp_frame_r_start) {
+ char *tmp = gtp_frame_next(gtp_frame_r_start);
+ if (tmp == NULL)
+ goto out;
+ gtp_frame_r_start = tmp;
+ }
+ }
+
+ ret = gtp_frame_w_start;
+ gtp_frame_w_start += size;
+
+out:
+ spin_unlock_irqrestore(&gtp_frame_lock, flags);
+ return ret;
+}
+
+static char * *
+gtp_action_head(struct gtp_entry *tpe)
+{
+ char *tmp;
+ char **nextp;
+ ULONGEST *trace_nump;
+
+ /* Get the head. */
+ tmp = gtp_frame_alloc(GTP_FRAME_HEAD_SIZE);
+ if (!tmp) {
+ tpe->reason = gtp_stop_frame_full;
+ return NULL;
+ }
+
+ tmp[0] = 'h';
+ tmp++;
+
+ nextp = (char **)tmp;
+ *nextp = NULL;
+ tmp += sizeof(char *);
+
+ trace_nump = (ULONGEST *)tmp;
+ *trace_nump = tpe->num;
+
+ atomic_inc(&gtp_frame_create);
+
+ return nextp;
+}
+
+struct gtp_trace_s {
+ struct gtp_entry *tpe;
+ struct pt_regs *regs;
+ char **next;
+};
+
+static int
+gtp_action_memory_read(struct gtp_trace_s *gts, int reg, CORE_ADDR addr,
+ size_t size)
+{
+ char *tmp;
+ struct gtp_frame_mem *fm;
+
+ if (reg >= 0)
+ addr += (CORE_ADDR) gtp_action_reg_read(gts->regs,
+ gts->tpe, reg);
+ if (gts->tpe->reason != gtp_stop_normal)
+ return -1;
+
+ if (gts->next == NULL) {
+ gts->next = gtp_action_head(gts->tpe);
+ if (!gts->next)
+ return -1;
+ }
+
+ tmp = gtp_frame_alloc(GTP_FRAME_MEM_SIZE + size);
+ if (!tmp) {
+ gts->tpe->reason = gtp_stop_frame_full;
+ return -1;
+ }
+ *gts->next = tmp;
+
+ tmp[0] = 'm';
+ tmp++;
+
+ gts->next = (char **)tmp;
+ *gts->next = NULL;
+ tmp += sizeof(char *);
+
+ fm = (struct gtp_frame_mem *)tmp;
+ fm->addr = addr;
+ fm->size = size;
+ tmp += sizeof(struct gtp_frame_mem);
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_action_memory_read: id:%d %p %u\n",
+ (int)gts->tpe->num, (void *)addr, (unsigned int)size);
+#endif
+
+ if (probe_kernel_read(tmp, (void *)addr, size)) {
+ gts->tpe->reason = gtp_stop_efault;
+ memset(tmp, 0, size);
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_action_memory_read: id:%d read %p %u "
+ "get error.\n", (int)gts->tpe->num,
+ (void *)addr, (unsigned int)size);
+#endif
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+gtp_action_r(struct gtp_trace_s *gts, struct action *ae)
+{
+ struct pt_regs *regs;
+ char *tmp;
+
+ if (gts->next == NULL) {
+ gts->next = gtp_action_head(gts->tpe);
+ if (!gts->next)
+ return -1;
+ }
+
+ tmp = gtp_frame_alloc(GTP_FRAME_REG_SIZE);
+ if (!tmp) {
+ gts->tpe->reason = gtp_stop_frame_full;
+ return -1;
+ }
+ *gts->next = tmp;
+
+ tmp[0] = 'r';
+ tmp++;
+
+ gts->next = (char **)tmp;
+ *gts->next = NULL;
+ tmp += sizeof(char *);
+
+ regs = (struct pt_regs *)tmp;
+
+ memcpy(regs, gts->regs, sizeof(struct pt_regs));
+#ifdef CONFIG_X86_32
+ regs->sp = (unsigned long)&regs->sp;
+#endif /* CONFIG_X86_32 */
+#ifdef CONFIG_X86
+ regs->ip -= 1;
+#endif /* CONFIG_X86 */
+
+ return 0;
+}
+
+static struct gtp_var *
+gtp_gtp_var_array_find(unsigned int num)
+{
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_gtp_var_array_find: num:%u %u %u\n",
+ gtp_var_head, gtp_var_tail, num);
+#endif
+
+ if (num < gtp_var_head || num > gtp_var_tail)
+ return NULL;
+
+ return gtp_var_array[num - gtp_var_head];
+}
+
+static int
+gtp_collect_var(struct gtp_trace_s *gts, struct gtp_var *tve)
+{
+ struct gtp_frame_var *fvar;
+ char *tmp;
+
+ if (gts->next == NULL) {
+ gts->next = gtp_action_head(gts->tpe);
+ if (!gts->next)
+ return -1;
+ }
+
+ tmp = gtp_frame_alloc(GTP_FRAME_VAR_SIZE);
+ if (!tmp) {
+ gts->tpe->reason = gtp_stop_frame_full;
+ return -1;
+ }
+ *gts->next = tmp;
+
+ tmp[0] = 'v';
+ tmp++;
+
+ gts->next = (char **)tmp;
+ *gts->next = NULL;
+ tmp += sizeof(char *);
+
+ fvar = (struct gtp_frame_var *) tmp;
+ fvar->num = tve->num;
+ if (tve->num == GTP_VAR_CURRENT_TASK_ID)
+ fvar->val = (uint64_t)(CORE_ADDR)get_current();
+ else
+ fvar->val = tve->val;
+
+ return 0;
+}
+
+static int
+gtp_action_x(struct gtp_trace_s *gts, struct action *ae, int *run)
+{
+ int ret = 0;
+ unsigned int pc = 0, sp = 0;
+ ULONGEST top = 0;
+ int arg;
+#define STACK_MAX 30
+ ULONGEST stack[STACK_MAX];
+ union {
+ union {
+ uint8_t bytes[1];
+ uint8_t val;
+ } u8;
+ union {
+ uint8_t bytes[2];
+ uint16_t val;
+ } u16;
+ union {
+ uint8_t bytes[4];
+ uint32_t val;
+ } u32;
+ union {
+ uint8_t bytes[8];
+ ULONGEST val;
+ } u64;
+ } cnv;
+ unsigned long flags = 0;
+
+ if (ae->u.exp.need_var_lock)
+ spin_lock_irqsave(&gtp_var_lock, flags);
+
+ while (pc < ae->u.exp.size) {
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_parse_x: cmd %x\n", ae->u.exp.buf[pc]);
+#endif
+
+ switch (ae->u.exp.buf[pc++]) {
+ /* add */
+ case 0x02:
+ if (sp)
+ top += stack[--sp];
+ else
+ goto code_error_out;
+ break;
+ /* sub */
+ case 0x03:
+ if (sp)
+ top = stack[--sp] - top;
+ else
+ goto code_error_out;
+ break;
+ /* mul */
+ case 0x04:
+ if (sp)
+ top *= stack[--sp];
+ else
+ goto code_error_out;
+ break;
+#ifndef CONFIG_MIPS
+ /* div_signed */
+ case 0x05:
+ if (top && sp) {
+ LONGEST l = (LONGEST) stack[--sp];
+ do_div(l, (LONGEST) top);
+ top = l;
+ } else
+ goto code_error_out;
+ break;
+ /* div_unsigned */
+ case 0x06:
+ if (top && sp) {
+ ULONGEST ul = stack[--sp];
+ do_div(ul, top);
+ top = ul;
+ } else
+ goto code_error_out;
+ break;
+ /* rem_signed */
+ case 0x07:
+ if (top && sp) {
+ LONGEST l1 = (LONGEST) stack[--sp];
+ LONGEST l2 = (LONGEST) top;
+ top = do_div(l1, l2);
+ } else
+ goto code_error_out;
+ break;
+ /* rem_unsigned */
+ case 0x08:
+ if (top && sp) {
+ ULONGEST ul1 = stack[--sp];
+ ULONGEST ul2 = top;
+ top = do_div(ul1, ul2);
+ } else
+ goto code_error_out;
+ break;
+#endif
+ /* lsh */
+ case 0x09:
+ if (sp)
+ top = stack[--sp] << top;
+ else
+ goto code_error_out;
+ break;
+ /* rsh_signed */
+ case 0x0a:
+ if (sp)
+ top = ((LONGEST) stack[--sp]) >> top;
+ else
+ goto code_error_out;
+ break;
+ /* rsh_unsigned */
+ case 0x0b:
+ if (sp)
+ top = stack[--sp] >> top;
+ else
+ goto code_error_out;
+ break;
+ /* trace */
+ case 0x0c:
+ if (sp > 1) {
+ if (gtp_action_memory_read
+ (gts, -1, (CORE_ADDR) stack[--sp],
+ (size_t) top))
+ goto out;
+ if (--sp >= 0)
+ top = stack[sp];
+ } else
+ goto code_error_out;
+ break;
+ /* trace_quick */
+ case 0x0d:
+ if (gtp_action_memory_read
+ (gts, -1, (CORE_ADDR) top,
+ (size_t) ae->u.exp.buf[pc++]))
+ goto out;
+ break;
+ /* log_not */
+ case 0x0e:
+ top = !top;
+ break;
+ /* bit_and */
+ case 0x0f:
+ if (sp)
+ top &= stack[--sp];
+ else
+ goto code_error_out;
+ break;
+ /* bit_or */
+ case 0x10:
+ if (sp)
+ top |= stack[--sp];
+ else
+ goto code_error_out;
+ break;
+ /* bit_xor */
+ case 0x11:
+ if (sp)
+ top ^= stack[--sp];
+ else
+ goto code_error_out;
+ break;
+ /* bit_not */
+ case 0x12:
+ top = ~top;
+ break;
+ /* equal */
+ case 0x13:
+ if (sp)
+ top = (stack[--sp] == top);
+ else
+ goto code_error_out;
+ break;
+ /* less_signed */
+ case 0x14:
+ if (sp)
+ top = (((LONGEST) stack[--sp])
+ < ((LONGEST) top));
+ else
+ goto code_error_out;
+ break;
+ /* less_unsigned */
+ case 0x15:
+ if (sp)
+ top = (stack[--sp] < top);
+ else
+ goto code_error_out;
+ break;
+ /* ext */
+ case 0x16:
+ arg = ae->u.exp.buf[pc++];
+ if (arg < (sizeof(LONGEST) * 8)) {
+ LONGEST mask = 1 << (arg - 1);
+ top &= ((LONGEST) 1 << arg) - 1;
+ top = (top ^ mask) - mask;
+ }
+ break;
+ /* ref8 */
+ case 0x17:
+ if (probe_kernel_read
+ (cnv.u8.bytes,
+ (void *)(CORE_ADDR)top, 1))
+ goto code_error_out;
+ top = (ULONGEST) cnv.u8.val;
+ break;
+ /* ref16 */
+ case 0x18:
+ if (probe_kernel_read
+ (cnv.u16.bytes,
+ (void *)(CORE_ADDR)top, 2))
+ goto code_error_out;
+ top = (ULONGEST) cnv.u16.val;
+ break;
+ /* ref32 */
+ case 0x19:
+ if (probe_kernel_read
+ (cnv.u32.bytes,
+ (void *)(CORE_ADDR)top, 4))
+ goto code_error_out;
+ top = (ULONGEST) cnv.u32.val;
+ break;
+ /* ref64 */
+ case 0x1a:
+ if (probe_kernel_read
+ (cnv.u64.bytes,
+ (void *)(CORE_ADDR)top, 8))
+ goto code_error_out;
+ top = (ULONGEST) cnv.u64.val;
+ break;
+ /* if_goto */
+ case 0x20:
+ if (top)
+ pc = (ae->u.exp.buf[pc] << 8)
+ + (ae->u.exp.buf[pc + 1]);
+ else
+ pc += 2;
+ if (sp && --sp >= 0)
+ top = stack[sp];
+ break;
+ /* goto */
+ case 0x21:
+ pc = (ae->u.exp.buf[pc] << 8)
+ + (ae->u.exp.buf[pc + 1]);
+ break;
+ /* const8 */
+ case 0x22:
+ stack[sp++] = top;
+ top = ae->u.exp.buf[pc++];
+ break;
+ /* const16 */
+ case 0x23:
+ stack[sp++] = top;
+ top = ae->u.exp.buf[pc++];
+ top = (top << 8) + ae->u.exp.buf[pc++];
+ break;
+ /* const32 */
+ case 0x24:
+ stack[sp++] = top;
+ top = ae->u.exp.buf[pc++];
+ top = (top << 8) + ae->u.exp.buf[pc++];
+ top = (top << 8) + ae->u.exp.buf[pc++];
+ top = (top << 8) + ae->u.exp.buf[pc++];
+ break;
+ /* const64 */
+ case 0x25:
+ stack[sp++] = top;
+ top = ae->u.exp.buf[pc++];
+ top = (top << 8) + ae->u.exp.buf[pc++];
+ top = (top << 8) + ae->u.exp.buf[pc++];
+ top = (top << 8) + ae->u.exp.buf[pc++];
+ top = (top << 8) + ae->u.exp.buf[pc++];
+ top = (top << 8) + ae->u.exp.buf[pc++];
+ top = (top << 8) + ae->u.exp.buf[pc++];
+ top = (top << 8) + ae->u.exp.buf[pc++];
+ break;
+ /* reg */
+ case 0x26:
+ stack[sp++] = top;
+ arg = ae->u.exp.buf[pc++];
+ arg = (arg << 8) + ae->u.exp.buf[pc++];
+ top = gtp_action_reg_read(gts->regs, gts->tpe, arg);
+ if (gts->tpe->reason != gtp_stop_normal)
+ goto error_out;
+ break;
+ /* end */
+ case 0x27:
+ if (run)
+ *run = (int)top;
+ goto out;
+ break;
+ /* dup */
+ case 0x28:
+ stack[sp++] = top;
+ break;
+ /* pop */
+ case 0x29:
+ if (sp && --sp >= 0)
+ top = stack[sp];
+ break;
+ /* zero_ext */
+ case 0x2a:
+ arg = ae->u.exp.buf[pc++];
+ if (arg < (sizeof(LONGEST) * 8))
+ top &= ((LONGEST) 1 << arg) - 1;
+ break;
+ /* swap */
+ case 0x2b:
+ if (sp) {
+ stack[sp] = top;
+ top = stack[sp - 1];
+ stack[sp - 1] = stack[sp];
+ } else
+ goto code_error_out;
+ break;
+ /* getv */
+ case 0x2c: {
+ struct gtp_var *tve;
+
+ arg = ae->u.exp.buf[pc++];
+ arg = (arg << 8) + ae->u.exp.buf[pc++];
+
+ tve = gtp_gtp_var_array_find(arg);
+ if (!tve)
+ goto code_error_out;
+
+ stack[sp++] = top;
+ if (tve->num == GTP_VAR_CURRENT_TASK_ID)
+ top = (uint64_t)(CORE_ADDR)
+ get_current();
+ else
+ top = (uint64_t)tve->val;
+ }
+ break;
+ /* setv */
+ case 0x2d: {
+ struct gtp_var *tve;
+
+ arg = ae->u.exp.buf[pc++];
+ arg = (arg << 8) + ae->u.exp.buf[pc++];
+
+ tve = gtp_gtp_var_array_find(arg);
+ if (!tve)
+ goto code_error_out;
+ if (tve->num == GTP_VAR_CURRENT_TASK_ID)
+ goto code_error_out;
+
+ tve->val = (uint64_t)top;
+ }
+ break;
+ /* tracev */
+ case 0x2e: {
+ struct gtp_var *tve;
+
+ arg = ae->u.exp.buf[pc++];
+ arg = (arg << 8) + ae->u.exp.buf[pc++];
+
+ tve = gtp_gtp_var_array_find(arg);
+ if (!tve)
+ goto code_error_out;
+
+ if (gtp_collect_var(gts, tve))
+ goto out;
+ }
+ break;
+ }
+
+ if (sp > STACK_MAX - 5) {
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_action_x: stack overflow.\n");
+#endif
+ gts->tpe->reason = gtp_stop_agent_expr_stack_overflow;
+ goto error_out;
+ }
+ }
+code_error_out:
+ gts->tpe->reason = gtp_stop_agent_expr_code_error;
+error_out:
+ ret = -1;
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_action_x: tracepoint %d "
+ "action X get error in pc %u.\n",
+ (int)tpe->num, pc);
+#endif
+out:
+ if (ae->u.exp.need_var_lock)
+ spin_unlock_irqrestore(&gtp_var_lock, flags);
+ return ret;
+}
+
+static int
+gtp_kp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct gtp_trace_s gts;
+ struct action *ae;
+
+ gts.tpe = container_of(p, struct gtp_entry, kp);
+ gts.regs = regs;
+ gts.next = NULL;
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_kp_pre_handler: tracepoint %d\n",
+ (int)gts.tpe->num);
+#endif
+
+ if (gts.tpe->kpreg == 0)
+ return 0;
+
+ /* Condition. */
+ if (gts.tpe->cond) {
+ int run;
+
+ if (gtp_action_x(&gts, gts.tpe->cond, &run))
+ goto tpe_stop;
+ if (!run)
+ return 0;
+ }
+
+ /* Pass. */
+ if (!gts.tpe->nopass) {
+ if (atomic_dec_return(&gts.tpe->current_pass) < 0)
+ goto tpe_stop;
+ }
+
+ /* Handle actions. */
+ for (ae = gts.tpe->action_list; ae; ae = ae->next) {
+ switch (ae->type) {
+ case 'R':
+ if (gtp_action_r(&gts, ae))
+ goto tpe_stop;
+ break;
+ case 'X':
+ if (gtp_action_x(&gts, ae, NULL))
+ goto tpe_stop;
+ break;
+ case 'M':
+ if (gtp_action_memory_read(&gts, ae->u.m.regnum,
+ ae->u.m.offset,
+ ae->u.m.size))
+ goto tpe_stop;
+ break;
+ }
+ }
+
+ return 0;
+
+tpe_stop:
+ gts.tpe->kpreg = 0;
+ queue_work(gtp_wq, &gts.tpe->work);
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_kp_pre_handler: tracepoint %d stop.\n",
+ (int)gts.tpe->num);
+#endif
+ return 0;
+}
+
+static struct action *
+gtp_action_alloc(char *pkg)
+{
+ struct action *ret;
+
+ ret = kmalloc(sizeof(struct action), GFP_KERNEL);
+ if (!ret)
+ goto out;
+
+ memset(ret, '\0', sizeof(struct action));
+ ret->type = pkg[0];
+ ret->src = pkg;
+
+out:
+ return ret;
+}
+
+static void
+gtp_action_release(struct action *ae)
+{
+ struct action *ae2;
+
+ while (ae) {
+ ae2 = ae;
+ ae = ae->next;
+ /* Release ae2. */
+ switch (ae2->type) {
+ case 'X':
+ kfree(ae2->u.exp.buf);
+ break;
+ }
+ kfree(ae2->src);
+ kfree(ae2);
+ }
+}
+
+static void
+gtp_src_release(struct gtpsrc *src)
+{
+ struct gtpsrc *src2;
+
+ while (src) {
+ src2 = src;
+ src = src->next;
+ kfree(src2->src);
+ kfree(src2);
+ }
+}
+
+static void
+gtp_stop(struct work_struct *work)
+{
+ struct gtp_entry *tpe = container_of(work,
+ struct gtp_entry, work);
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_stop: tracepoint %d\n", (int)tpe->num);
+#endif
+
+ unregister_kprobe(&tpe->kp);
+}
+
+static struct gtp_entry *
+gtp_list_add(ULONGEST num, ULONGEST addr)
+{
+ struct gtp_entry *ret = kmalloc(sizeof(struct gtp_entry),
+ GFP_KERNEL);
+
+ if (!ret)
+ goto out;
+ memset(ret, '\0', sizeof(struct gtp_entry));
+ ret->num = num;
+ ret->addr = addr;
+ ret->kp.addr = (kprobe_opcode_t *) (CORE_ADDR)addr;
+ ret->kp.pre_handler = gtp_kp_pre_handler;
+ INIT_WORK(&ret->work, gtp_stop);
+
+ /* Add to gtp_list. */
+ ret->next = gtp_list;
+ gtp_list = ret;
+
+out:
+ return ret;
+}
+
+static struct gtp_entry *
+gtp_list_find(ULONGEST num, ULONGEST addr)
+{
+ struct gtp_entry *tpe;
+
+ for (tpe = gtp_list; tpe; tpe = tpe->next) {
+ if (tpe->num == num && tpe->addr == addr)
+ return tpe;
+ }
+
+ return NULL;
+}
+
+static struct gtp_entry *
+gtp_list_find_onlynum(ULONGEST num)
+{
+ struct gtp_entry *tpe;
+
+ for (tpe = gtp_list; tpe; tpe = tpe->next) {
+ if (tpe->num == num)
+ return tpe;
+ }
+
+ return NULL;
+}
+
+static void
+gtp_list_release(void)
+{
+ struct gtp_entry *tpe;
+
+ while (gtp_list) {
+ tpe = gtp_list;
+ gtp_list = gtp_list->next;
+ gtp_action_release(tpe->cond);
+ gtp_action_release(tpe->action_list);
+ gtp_src_release(tpe->src);
+ kfree(tpe);
+ }
+
+ current_gtp = NULL;
+ current_gtp_action = NULL;
+ current_gtp_src = NULL;
+}
+
+static void
+gtp_frame_reset(void)
+{
+ gtp_frame_r_start = gtp_frame;
+ gtp_frame_w_start = gtp_frame;
+ gtp_frame_end = gtp_frame + GTP_FRAME_SIZE;
+ gtp_frame_is_circular = 0;
+ gtp_frame_current = NULL;
+ gtp_frame_current_num = 0;
+ atomic_set(&gtp_frame_create, 0);
+}
+
+static int
+hex2int(char hex, int *i)
+{
+ if ((hex >= '0') && (hex <= '9')) {
+ *i = hex - '0';
+ return 1;
+ }
+ if ((hex >= 'a') && (hex <= 'f')) {
+ *i = hex - 'a' + 10;
+ return 1;
+ }
+ if ((hex >= 'A') && (hex <= 'F')) {
+ *i = hex - 'A' + 10;
+ return 1;
+ }
+
+ return 0;
+}
+
+static char *
+hex2ulongest(char *pkg, ULONGEST *u64)
+{
+ int i;
+
+ *u64 = 0;
+ while (hex2int(pkg[0], &i)) {
+ pkg++;
+ *u64 = (*u64) << 4;
+ *u64 |= i & 0xf;
+ }
+
+ return pkg;
+}
+
+static char *
+string2hex(char *pkg, char *out)
+{
+ char *ret = out;
+
+ while (pkg[0]) {
+ sprintf(out, "%x", pkg[0]);
+ pkg++;
+ out += 2;
+ }
+
+ return ret;
+}
+
+static char *
+gtp_strdup(char *begin, char *end)
+{
+ int len;
+ char *ret;
+
+ if (end)
+ len = end - begin;
+ else
+ len = strlen(begin);
+
+ ret = kmalloc(len + 1, GFP_KERNEL);
+ if (ret == NULL)
+ return NULL;
+
+ strncpy(ret, begin, len);
+ ret[len] = '\0';
+
+ return ret;
+}
+
+static void
+gtpro_list_clear(void)
+{
+ struct gtpro_entry *e;
+
+ while (gtpro_list) {
+ e = gtpro_list;
+ gtpro_list = gtpro_list->next;
+ kfree(e);
+ }
+}
+
+static struct gtpro_entry *
+gtpro_list_add(CORE_ADDR start, CORE_ADDR end)
+{
+ struct gtpro_entry *e;
+
+ e = kmalloc(sizeof(struct gtpro_entry), GFP_KERNEL);
+ if (e == NULL)
+ goto out;
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtpro_list_add: %p %p\n", (void *)start, (void *)end);
+#endif
+
+ e->start = start;
+ e->end = end;
+
+ e->next = gtpro_list;
+ gtpro_list = e;
+
+out:
+ return e;
+}
+
+static struct gtp_var *
+gtp_var_add(unsigned int num, uint64_t val, char *src)
+{
+ struct gtp_var *var = kmalloc(sizeof(struct gtp_var), GFP_KERNEL);
+ if (!var)
+ goto out;
+
+ var->num = num;
+ var->val = val;
+
+ var->src = gtp_strdup(src, NULL);
+ if (var->src == NULL) {
+ kfree(var);
+ var = NULL;
+ goto out;
+ }
+
+ var->next = gtp_var_list;
+ gtp_var_list = var;
+ gtp_var_head = min(var->num, gtp_var_head);
+ gtp_var_tail = max(var->num, gtp_var_tail);
+
+out:
+ return var;
+}
+
+static struct gtp_var *
+gtp_var_find(unsigned int num)
+{
+ struct gtp_var *ret = NULL;
+
+ if (num >= gtp_var_head && num <= gtp_var_tail) {
+ for (ret = gtp_var_list; ret; ret = ret->next) {
+ if (ret->num == num)
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static void
+gtp_var_release(void)
+{
+ struct gtp_var *tve;
+
+ gtp_var_head = GTP_VAR_CURRENT_TASK_ID;
+ gtp_var_tail = GTP_VAR_CURRENT_TASK_ID;
+ current_gtp_var = NULL;
+
+ while (gtp_var_list != &gtp_var_current_task) {
+ tve = gtp_var_list;
+ gtp_var_list = gtp_var_list->next;
+ kfree(tve->src);
+ kfree(tve);
+ }
+}
+
+static int
+gtp_gdbrsp_qtstop(void)
+{
+ struct gtp_entry *tpe;
+
+ if (!gtp_start)
+ return -EBUSY;
+
+ flush_workqueue(gtp_wq);
+
+ for (tpe = gtp_list; tpe; tpe = tpe->next) {
+ if (tpe->kpreg) {
+ unregister_kprobe(&tpe->kp);
+ tpe->kpreg = 0;
+ }
+ }
+
+ kfree(gtp_var_array);
+ gtp_var_array = NULL;
+
+ gtp_start = 0;
+
+ return 0;
+}
+
+static int
+gtp_gdbrsp_qtinit(void)
+{
+ if (gtp_start)
+ gtp_gdbrsp_qtstop();
+
+ gtp_list_release();
+
+ if (gtp_frame)
+ gtp_frame_reset();
+
+ gtpro_list_clear();
+
+ gtp_var_release();
+
+ return 0;
+}
+
+static int
+gtp_gdbrsp_qtstart(void)
+{
+ struct gtp_entry *tpe;
+ struct gtp_var *tve;
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_gdbrsp_qtstart\n");
+#endif
+
+ if (gtp_start)
+ return -EBUSY;
+
+ if (!gtp_frame) {
+ gtp_frame = vmalloc(GTP_FRAME_SIZE);
+ if (!gtp_frame)
+ return -ENOMEM;
+
+ gtp_frame_reset();
+ }
+
+ gtp_start = 1;
+
+ gtp_var_array = kmalloc(sizeof(struct gtp_var *)
+ * (gtp_var_tail - gtp_var_head + 1),
+ GFP_KERNEL);
+ if (!gtp_var_array) {
+ gtp_gdbrsp_qtstop();
+ return -ENOMEM;
+ }
+ memset(gtp_var_array, '\0', sizeof(struct gtp_var *)
+ * (gtp_var_tail - gtp_var_head + 1));
+ for (tve = gtp_var_list; tve; tve = tve->next)
+ gtp_var_array[tve->num - gtp_var_head] = tve;
+
+ for (tpe = gtp_list; tpe; tpe = tpe->next) {
+ if (!tpe->disable && tpe->action_list) {
+ int ret;
+
+ if (!tpe->nopass)
+ atomic_set(&tpe->current_pass, tpe->pass);
+ ret = register_kprobe(&tpe->kp);
+ if (ret < 0) {
+ gtp_gdbrsp_qtstop();
+ return ret;
+ }
+ tpe->kpreg = 1;
+ }
+ tpe->reason = gtp_stop_normal;
+ }
+
+ return 0;
+}
+
+struct gtp_x_goto {
+ struct gtp_x_goto *next;
+ unsigned int addr;
+ int non_goto_done;
+};
+
+static struct gtp_x_goto *
+gtp_x_goto_find(struct gtp_x_goto *list, unsigned int pc)
+{
+ struct gtp_x_goto *ret = NULL;
+
+ for (ret = list; ret; ret = ret->next) {
+ if (ret->addr == pc)
+ break;
+ }
+
+ return ret;
+}
+
+static struct gtp_x_goto *
+gtp_x_goto_add(struct gtp_x_goto **list, unsigned int pc, int non_goto_done)
+{
+ struct gtp_x_goto *ret;
+
+ ret = kmalloc(sizeof(struct gtp_x_goto), GFP_KERNEL);
+ if (!ret)
+ goto out;
+
+ ret->addr = pc;
+ ret->non_goto_done = non_goto_done;
+
+ if (*list) {
+ ret->next = *list;
+ *list = ret;
+ } else {
+ ret->next = NULL;
+ *list = ret;
+ }
+
+out:
+ return ret;
+}
+
+struct gtp_x_var {
+ struct gtp_x_var *next;
+ unsigned int num;
+ unsigned int flags;
+};
+
+static int
+gtp_x_var_add(struct gtp_x_var **list, unsigned int num, unsigned int flag)
+{
+ struct gtp_x_var *curv;
+
+ for (curv = *list; curv; curv = curv->next) {
+ if (curv->num == num)
+ break;
+ }
+
+ if (!curv) {
+ curv = kmalloc(sizeof(struct gtp_x_var), GFP_KERNEL);
+ if (!curv)
+ return -ENOMEM;
+ curv->num = num;
+ curv->flags = 0;
+ if (*list) {
+ curv->next = *list;
+ *list = curv;
+ } else {
+ curv->next = NULL;
+ *list = curv;
+ }
+ }
+
+ curv->flags |= flag;
+
+ return 0;
+}
+
+static int
+gtp_check_x(struct action *ae)
+{
+ int ret = -EINVAL;
+ unsigned int pc = 0;
+ struct gtp_x_goto *glist = NULL, *gtmp;
+ struct gtp_x_var *vlist = NULL, *vtmp;
+
+reswitch:
+ while (pc < ae->u.exp.size) {
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_check_x: cmd %x\n", ae->u.exp.buf[pc]);
+#endif
+ switch (ae->u.exp.buf[pc++]) {
+ /* add */
+ case 0x02:
+ /* sub */
+ case 0x03:
+ /* mul */
+ case 0x04:
+ /* lsh */
+ case 0x09:
+ /* rsh_signed */
+ case 0x0a:
+ /* rsh_unsigned */
+ case 0x0b:
+ /* trace */
+ case 0x0c:
+ /* log_not */
+ case 0x0e:
+ /* bit_and */
+ case 0x0f:
+ /* bit_or */
+ case 0x10:
+ /* bit_xor */
+ case 0x11:
+ /* bit_not */
+ case 0x12:
+ /* equal */
+ case 0x13:
+ /* less_signed */
+ case 0x14:
+ /* less_unsigned */
+ case 0x15:
+ /* ref8 */
+ case 0x17:
+ /* ref16 */
+ case 0x18:
+ /* ref32 */
+ case 0x19:
+ /* ref64 */
+ case 0x1a:
+ /* dup */
+ case 0x28:
+ /* pop */
+ case 0x29:
+ /* swap */
+ case 0x2b:
+ break;
+
+ /* trace_quick */
+ case 0x0d:
+ /* ext */
+ case 0x16:
+ /* const8 */
+ case 0x22:
+ /* zero_ext */
+ case 0x2a:
+ if (pc >= ae->u.exp.size)
+ goto release_out;
+ pc++;
+ break;
+
+ /* const16 */
+ case 0x23:
+ /* reg */
+ case 0x26:
+ if (pc + 1 >= ae->u.exp.size)
+ goto release_out;
+ pc += 2;
+ break;
+ /* const32 */
+ case 0x24:
+ if (pc + 3 >= ae->u.exp.size)
+ goto release_out;
+ pc += 4;
+ break;
+ /* const64 */
+ case 0x25:
+ if (pc + 7 >= ae->u.exp.size)
+ goto release_out;
+ pc += 8;
+ break;
+
+
+ /* if_goto */
+ case 0x20:
+ if (pc + 1 >= ae->u.exp.size)
+ goto release_out;
+ gtmp = gtp_x_goto_find(glist, pc);
+ if (gtmp) {
+ if (gtmp->non_goto_done)
+ goto out;
+ else {
+ gtmp->non_goto_done = 1;
+ pc += 2;
+ }
+ } else {
+ if (!gtp_x_goto_add(&glist, pc, 0)) {
+ ret = -ENOMEM;
+ goto release_out;
+ }
+ pc = (ae->u.exp.buf[pc] << 8)
+ + (ae->u.exp.buf[pc + 1]);
+ }
+ break;
+ /* goto */
+ case 0x21:
+ if (pc + 1 >= ae->u.exp.size)
+ goto release_out;
+ gtmp = gtp_x_goto_find(glist, pc);
+ if (gtmp)
+ goto out;
+ else {
+ if (!gtp_x_goto_add(&glist, pc, 1)) {
+ ret = -ENOMEM;
+ goto release_out;
+ }
+ pc = (ae->u.exp.buf[pc] << 8)
+ + (ae->u.exp.buf[pc + 1]);
+ }
+ break;
+
+ /* end */
+ case 0x27:
+ goto out;
+ break;
+
+ /* getv */
+ case 0x2c: {
+ int arg;
+
+ if (pc + 1 >= ae->u.exp.size)
+ goto release_out;
+ arg = ae->u.exp.buf[pc++];
+ arg = (arg << 8) + ae->u.exp.buf[pc++];
+ if (gtp_x_var_add(&vlist, arg, 1)) {
+ ret = -ENOMEM;
+ goto release_out;
+ }
+ }
+ break;
+ /* setv */
+ case 0x2d: {
+ int arg;
+
+ if (pc + 1 >= ae->u.exp.size)
+ goto release_out;
+ arg = ae->u.exp.buf[pc++];
+ arg = (arg << 8) + ae->u.exp.buf[pc++];
+ if (gtp_x_var_add(&vlist, arg, 2)) {
+ ret = -ENOMEM;
+ goto release_out;
+ }
+ }
+ break;
+
+ /* tracev */
+ case 0x2e: {
+ int arg;
+
+ if (pc + 1 >= ae->u.exp.size)
+ goto release_out;
+ arg = ae->u.exp.buf[pc++];
+ arg = (arg << 8) + ae->u.exp.buf[pc++];
+ if (gtp_x_var_add(&vlist, arg, 4)) {
+ ret = -ENOMEM;
+ goto release_out;
+ }
+ }
+ break;
+
+ /* div_signed */
+ case 0x05:
+ /* div_unsigned */
+ case 0x06:
+ /* rem_signed */
+ case 0x07:
+ /* rem_unsigned */
+ case 0x08:
+#ifdef CONFIG_MIPS
+ /* XXX, mips don't have 64 bit div. */
+ goto release_out;
+#endif
+ break;
+
+ /* float */
+ case 0x01:
+ /* ref_float */
+ case 0x1b:
+ /* ref_double */
+ case 0x1c:
+ /* ref_long_double */
+ case 0x1d:
+ /* l_to_d */
+ case 0x1e:
+ /* d_to_l */
+ case 0x1f:
+ /* trace16 */
+ case 0x30:
+ default:
+ goto release_out;
+ break;
+ }
+ }
+ goto release_out;
+
+out:
+ for (gtmp = glist; gtmp; gtmp = gtmp->next) {
+ if (!gtmp->non_goto_done)
+ break;
+ }
+ if (gtmp) {
+ pc = gtmp->addr + 2;
+ gtmp->non_goto_done = 1;
+ goto reswitch;
+ }
+ ret = 0;
+
+release_out:
+ while (glist) {
+ gtmp = glist;
+ glist = glist->next;
+ kfree(gtmp);
+ }
+ while (vlist) {
+ vtmp = vlist;
+ vlist = vlist->next;
+ if ((vtmp->flags & 2)
+ && ((vtmp->flags & 1) || (vtmp->flags & 4)))
+ ae->u.exp.need_var_lock = 1;
+ kfree(vtmp);
+ }
+
+ return ret;
+}
+
+static int
+gtp_parse_x(struct action *ae, char **pkgp)
+{
+ ULONGEST size;
+ int ret = 0, i, h, l;
+ char *pkg = *pkgp;
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_parse_x: %s\n", pkg);
+#endif
+
+ if (pkg[0] == '\0') {
+ ret = -EINVAL;
+ goto out;
+ }
+ pkg = hex2ulongest(pkg, &size);
+ if (pkg[0] != ',') {
+ ret = -EINVAL;
+ goto out;
+ }
+ ae->u.exp.size = (unsigned int)size;
+ pkg++;
+
+ ae->u.exp.buf = kmalloc(ae->u.exp.size, GFP_KERNEL);
+ if (!ae->u.exp.buf)
+ return -ENOMEM;
+
+ for (i = 0; i < ae->u.exp.size
+ && hex2int(pkg[0], &h) && hex2int(pkg[1], &l);
+ i++) {
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_parse_x: %s %d %d\n", pkg, h, l);
+#endif
+ ae->u.exp.buf[i] = (h << 4) | l;
+ pkg += 2;
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_parse_x: %x\n", ae->u.exp.buf[i]);
+#endif
+ }
+ if (i != ae->u.exp.size) {
+ kfree(ae->u.exp.buf);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ae->u.exp.need_var_lock = 0;
+
+ ret = gtp_check_x(ae);
+ if (ret)
+ kfree(ae->u.exp.buf);
+
+out:
+ *pkgp = pkg;
+ return ret;
+}
+
+static int
+gtp_gdbrsp_qtdp(char *pkg)
+{
+ int addnew = 1;
+ ULONGEST num, addr;
+ struct gtp_entry *tpe;
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_gdbrsp_qtdp: %s\n", pkg);
+#endif
+
+ if (gtp_start)
+ return -EBUSY;
+
+ if (pkg[0] == '-') {
+ pkg++;
+ addnew = 0;
+ }
+
+ /* Get num and addr. */
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg = hex2ulongest(pkg, &num);
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg++;
+ pkg = hex2ulongest(pkg, &addr);
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg++;
+
+ tpe = gtp_list_find(num, addr);
+ if (addnew) {
+ if (tpe)
+ return -EINVAL;
+
+ tpe = gtp_list_add(num, addr);
+ if (tpe == NULL)
+ return -ENOMEM;
+
+ if (pkg[0] == 'D')
+ tpe->disable = 1;
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg++;
+
+ /* Get step and pass. */
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg++;
+ pkg = hex2ulongest(pkg, &tpe->step);
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg++;
+ pkg = hex2ulongest(pkg, &tpe->pass);
+ if (tpe->pass == 0)
+ tpe->nopass = 1;
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg++;
+ }
+
+ if (tpe) {
+ /* Add action to tpe. */
+ int step_action = 0;
+
+ if (pkg[0] == 'S') {
+ pkg++;
+ step_action = 1;
+ /* XXX: Still not support step. */
+ return 1;
+ }
+ while (pkg[0]) {
+ struct action *ae = NULL, *atail = NULL;
+
+ switch (pkg[0]) {
+ case 'M': {
+ int is_neg = 0;
+ ULONGEST ulongtmp;
+
+ ae = gtp_action_alloc(pkg);
+ if (!ae)
+ return -ENOMEM;
+ pkg++;
+ if (pkg[0] == '-') {
+ is_neg = 1;
+ pkg++;
+ }
+ pkg = hex2ulongest(pkg, &ulongtmp);
+ ae->u.m.regnum = (int)ulongtmp;
+ if (is_neg)
+ ae->u.m.regnum
+ = -ae->u.m.regnum;
+ if (pkg[0] == '\0') {
+ kfree(ae);
+ return -EINVAL;
+ }
+ pkg++;
+ pkg = hex2ulongest(pkg, &ulongtmp);
+ ae->u.m.offset = (CORE_ADDR)ulongtmp;
+ if (pkg[0] == '\0') {
+ kfree(ae);
+ return -EINVAL;
+ }
+ pkg++;
+ pkg = hex2ulongest(pkg, &ulongtmp);
+ ae->u.m.size = (size_t)ulongtmp;
+ }
+ break;
+ case 'R':
+ /* XXX: reg_mask is ignore. */
+ ae = gtp_action_alloc(pkg);
+ if (!ae)
+ return -ENOMEM;
+ pkg++;
+ pkg = hex2ulongest(pkg,
+ &ae->u.reg_mask);
+ break;
+ case 'X': {
+ int ret;
+
+ ae = gtp_action_alloc(pkg);
+ if (!ae)
+ return -ENOMEM;
+ pkg++;
+ ret = gtp_parse_x(ae, &pkg);
+ if (ret < 0) {
+ kfree(ae);
+ return ret;
+ }
+#ifdef GTP_DEBUG
+ if (ae->u.exp.need_var_lock)
+ printk(GTP_DEBUG
+ "gtp_gdbrsp_qtdp: "
+ "ae need var lock.\n");
+#endif
+ }
+ break;
+ case '-':
+ pkg++;
+ break;
+ default:
+ /* XXX: Not support. */
+ return 1;
+ }
+
+ if (ae) {
+ /* Save the src. */
+ ae->src = gtp_strdup(ae->src, pkg);
+ if (ae->src == NULL) {
+ kfree(ae);
+ return -ENOMEM;
+ }
+ /* Add ae to tpe. */
+ if (ae->type == 'X' && addnew && !tpe->cond) {
+ tpe->cond = ae;
+ tpe->cond->next = NULL;
+ } else if (!tpe->action_list) {
+ tpe->action_list = ae;
+ atail = ae;
+ } else {
+ if (atail == NULL)
+ for (atail = tpe->action_list;
+ atail->next;
+ atail = atail->next)
+ ;
+ atail->next = ae;
+ atail = ae;
+ }
+ }
+ }
+ } else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int
+gtp_gdbrsp_qtdpsrc(char *pkg)
+{
+ ULONGEST num, addr;
+ struct gtpsrc *src, *srctail;
+ struct gtp_entry *tpe;
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_gdbrsp_qtdpsrc: %s\n", pkg);
+#endif
+
+ if (gtp_start)
+ return -EBUSY;
+
+ /* Get num and addr. */
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg = hex2ulongest(pkg, &num);
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg++;
+ pkg = hex2ulongest(pkg, &addr);
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg++;
+ tpe = gtp_list_find(num, addr);
+ if (tpe == NULL)
+ return -EINVAL;
+
+ src = kmalloc(sizeof(struct gtpsrc), GFP_KERNEL);
+ if (src == NULL)
+ return -ENOMEM;
+ src->next = NULL;
+ src->src = gtp_strdup(pkg, NULL);
+ if (src->src == NULL) {
+ kfree(src);
+ return -ENOMEM;
+ }
+
+ if (tpe->src) {
+ for (srctail = tpe->src; srctail->next;
+ srctail = srctail->next)
+ ;
+ srctail->next = src;
+ } else
+ tpe->src = src;
+
+ return 0;
+}
+
+static int
+gtp_gdbrsp_qtdisconnected(char *pkg)
+{
+ ULONGEST setting;
+
+ if (pkg[0] == '\0')
+ return -EINVAL;
+
+ hex2ulongest(pkg, &setting);
+ gtp_disconnected_tracing = (int)setting;
+
+ return 0;
+}
+
+static int
+gtp_gdbrsp_qtbuffer(char *pkg)
+{
+ if (strncmp("circular:", pkg, 9) == 0) {
+ ULONGEST setting;
+
+ pkg += 9;
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ hex2ulongest(pkg, &setting);
+ gtp_circular = (int)setting;
+
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+gtp_frame_head_find_addr(char *cur, int inside, unsigned long lo,
+ unsigned long hi)
+{
+ char *tmp;
+ int tfnum = gtp_frame_current_num;
+
+ if (cur)
+ tmp = cur;
+ else
+ tmp = gtp_frame_r_start;
+
+ do {
+ if (tmp[0] == 'h') {
+ if (tfnum != gtp_frame_current_num) {
+ char *next;
+ struct pt_regs *regs = NULL;
+
+ for (next = *(char **)(tmp + 1); next;
+ next = *(char **)(next + 1)) {
+ if (next[0] == 'r') {
+ regs = (struct pt_regs *)
+ (next + 1
+ + sizeof(char *));
+ break;
+ }
+ }
+ if (regs
+ && ((inside
+ && GTP_REGS_PC(regs) >= lo
+ && GTP_REGS_PC(regs) <= hi)
+ || (!inside
+ && (GTP_REGS_PC(regs) < lo
+ || GTP_REGS_PC(regs) > hi)))) {
+ gtp_frame_current_num = tfnum;
+ gtp_frame_current = tmp;
+ return 0;
+ }
+ }
+ tfnum++;
+ }
+
+ tmp = gtp_frame_next(tmp);
+ if (!tmp)
+ break;
+
+ if (tmp == gtp_frame_end)
+ tmp = gtp_frame;
+ } while (tmp != gtp_frame_w_start);
+
+ return -1;
+}
+
+static int
+gtp_frame_head_find_trace(char *cur, ULONGEST trace)
+{
+ char *tmp;
+ int tfnum = gtp_frame_current_num;
+
+ if (cur)
+ tmp = cur;
+ else
+ tmp = gtp_frame_r_start;
+
+ do {
+ if (tmp[0] == 'h') {
+ if (tfnum != gtp_frame_current_num) {
+ if (trace == *(ULONGEST *)
+ (tmp + 1 + sizeof(char *))) {
+ gtp_frame_current_num = tfnum;
+ gtp_frame_current = tmp;
+ return 0;
+ }
+ }
+ tfnum++;
+ }
+
+ tmp = gtp_frame_next(tmp);
+ if (!tmp)
+ break;
+
+ if (tmp == gtp_frame_end)
+ tmp = gtp_frame;
+ } while (tmp != gtp_frame_w_start);
+
+ return -1;
+}
+
+static int
+gtp_frame_head_find_num(int num)
+{
+ char *tmp = gtp_frame_r_start;
+ int tfnum = 0;
+
+ do {
+ if (tmp[0] == 'h') {
+ if (tfnum == num) {
+ gtp_frame_current_num = num;
+ gtp_frame_current = tmp;
+ return 0;
+ }
+ tfnum++;
+ }
+
+ tmp = gtp_frame_next(tmp);
+ if (!tmp)
+ break;
+
+ if (tmp == gtp_frame_end)
+ tmp = gtp_frame;
+ } while (tmp != gtp_frame_w_start);
+
+ return -1;
+}
+
+static int
+gtp_gdbrsp_qtframe(char *pkg)
+{
+ int ret = -1;
+
+ if (gtp_start)
+ return -EBUSY;
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_gdbrsp_qtframe: %s\n", pkg);
+#endif
+
+ if (atomic_read(&gtp_frame_create) == 0)
+ goto out;
+
+ if (strncmp(pkg, "pc:", 3) == 0) {
+ ULONGEST addr;
+
+ pkg += 3;
+
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ hex2ulongest(pkg, &addr);
+
+ ret = gtp_frame_head_find_addr(gtp_frame_current, 1,
+ (unsigned long)addr,
+ (unsigned long)addr);
+ } else if (strncmp(pkg, "tdp:", 4) == 0) {
+ ULONGEST trace;
+
+ pkg += 4;
+
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ hex2ulongest(pkg, &trace);
+
+ ret = gtp_frame_head_find_trace(gtp_frame_current, trace);
+ } else if (strncmp(pkg, "range:", 6) == 0) {
+ ULONGEST start, end;
+
+ pkg += 6;
+
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg = hex2ulongest(pkg, &start);
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg++;
+ hex2ulongest(pkg, &end);
+
+ ret = gtp_frame_head_find_addr(gtp_frame_current, 1,
+ (unsigned long)start,
+ (unsigned long)end);
+ } else if (strncmp(pkg, "outside:", 8) == 0) {
+ ULONGEST start, end;
+
+ pkg += 8;
+
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg = hex2ulongest(pkg, &start);
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg++;
+ hex2ulongest(pkg, &end);
+
+ ret = gtp_frame_head_find_addr(gtp_frame_current, 0,
+ (unsigned long)start,
+ (unsigned long)end);
+ } else {
+ ULONGEST num;
+
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ hex2ulongest(pkg, &num);
+
+ if (((int) num) < 0) {
+ /* Return to current. */
+ gtp_frame_current = NULL;
+ gtp_frame_current_num = 0;
+
+ return 0;
+ }
+ ret = gtp_frame_head_find_num((int) num);
+ }
+
+out:
+ if (ret) {
+ strcpy(gtp_rw_bufp, "F-1");
+ gtp_rw_bufp += 3;
+ gtp_rw_size += 3;
+ } else {
+ sprintf(gtp_rw_bufp, "F%xT%x",
+ gtp_frame_current_num,
+ (unsigned int)
+ *(ULONGEST *)(gtp_frame_current + 1 + sizeof(char *)));
+ gtp_rw_size += strlen(gtp_rw_bufp);
+ gtp_rw_bufp += strlen(gtp_rw_bufp);
+ }
+ return 1;
+}
+
+static int
+gtp_gdbrsp_qtro(char *pkg)
+{
+ ULONGEST start, end;
+
+ gtpro_list_clear();
+
+ while (pkg[0]) {
+ pkg = hex2ulongest(pkg, &start);
+ if (pkg[0] != ',')
+ return -EINVAL;
+ pkg++;
+ pkg = hex2ulongest(pkg, &end);
+ if (pkg[0])
+ pkg++;
+
+ if (gtpro_list_add((CORE_ADDR)start, (CORE_ADDR)end) == NULL)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int
+gtp_gdbrsp_qtdv(char *pkg)
+{
+ ULONGEST num, val;
+ struct gtp_var *var;
+ char *src;
+
+ pkg = hex2ulongest(pkg, &num);
+ if (num == GTP_VAR_CURRENT_TASK_ID)
+ return 0;
+ if (pkg[0] != ':')
+ return -EINVAL;
+ pkg++;
+ src = pkg;
+ pkg = hex2ulongest(pkg, &val);
+ if (pkg[0] != ':')
+ return -EINVAL;
+ pkg++;
+
+ var = gtp_var_find(num);
+ if (var)
+ return -EINVAL;
+
+ if (!gtp_var_add((unsigned int)num, (uint64_t)val, src))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int
+gtp_gdbrsp_QT(char *pkg)
+{
+ int ret = 1;
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_gdbrsp_QT: %s\n", pkg);
+#endif
+
+ if (strcmp("init", pkg) == 0)
+ ret = gtp_gdbrsp_qtinit();
+ else if (strcmp("Stop", pkg) == 0)
+ ret = gtp_gdbrsp_qtstop();
+ else if (strcmp("Start", pkg) == 0)
+ ret = gtp_gdbrsp_qtstart();
+ else if (strncmp("DP:", pkg, 3) == 0)
+ ret = gtp_gdbrsp_qtdp(pkg + 3);
+ else if (strncmp("DPsrc:", pkg, 6) == 0)
+ ret = gtp_gdbrsp_qtdpsrc(pkg + 6);
+ else if (strncmp("Disconnected:", pkg, 13) == 0)
+ ret = gtp_gdbrsp_qtdisconnected(pkg + 13);
+ else if (strncmp("Buffer:", pkg, 7) == 0)
+ ret = gtp_gdbrsp_qtbuffer(pkg + 7);
+ else if (strncmp("Frame:", pkg, 6) == 0)
+ ret = gtp_gdbrsp_qtframe(pkg + 6);
+ else if (strncmp("ro:", pkg, 3) == 0)
+ ret = gtp_gdbrsp_qtro(pkg + 3);
+ else if (strncmp("DV:", pkg, 3) == 0)
+ ret = gtp_gdbrsp_qtdv(pkg + 3);
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_gdbrsp_QT: return %d\n", ret);
+#endif
+
+ return ret;
+}
+
+static int
+gtp_gdbrsp_qtstatus(void)
+{
+ struct gtp_entry *tpe;
+ int tfnum = 0;
+ unsigned long flags;
+ CORE_ADDR tmpaddr;
+
+ for (tpe = gtp_list; tpe; tpe = tpe->next) {
+ if (tpe->reason != gtp_stop_normal)
+ break;
+ }
+
+ if (gtp_start && tpe) /* Tpe is stop, stop all tpes. */
+ gtp_gdbrsp_qtstop();
+
+ sprintf(gtp_rw_bufp, "T%x;", gtp_start ? 1 : 0);
+ gtp_rw_bufp += 3;
+ gtp_rw_size += 3;
+
+ if (!gtp_frame) {
+ sprintf(gtp_rw_bufp, "tnotrun:0;");
+ gtp_rw_bufp += 10;
+ gtp_rw_size += 10;
+ } else if (!tpe || (tpe && tpe->reason == gtp_stop_normal)) {
+ sprintf(gtp_rw_bufp, "tstop:0;");
+ gtp_rw_bufp += 8;
+ gtp_rw_size += 8;
+ } else {
+ char outtmp[100];
+
+ switch (tpe->reason) {
+ case gtp_stop_frame_full:
+ sprintf(gtp_rw_bufp, "tfull:%lx;",
+ (unsigned long)tpe->num);
+ break;
+ case gtp_stop_efault:
+ sprintf(gtp_rw_bufp, "terror:%s:%lx;",
+ string2hex("read memory false", outtmp),
+ (unsigned long)tpe->num);
+ break;
+ case gtp_stop_access_wrong_reg:
+ sprintf(gtp_rw_bufp, "terror:%s:%lx;",
+ string2hex("access wrong register", outtmp),
+ (unsigned long)tpe->num);
+ break;
+ case gtp_stop_agent_expr_code_error:
+ sprintf(gtp_rw_bufp, "terror:%s:%lx;",
+ string2hex("agent expression code error",
+ outtmp),
+ (unsigned long)tpe->num);
+ break;
+ case gtp_stop_agent_expr_stack_overflow:
+ sprintf(gtp_rw_bufp, "terror:%s:%lx;",
+ string2hex("agent expression stack overflow",
+ outtmp),
+ (unsigned long)tpe->num);
+ break;
+ default:
+ gtp_rw_bufp[0] = '\0';
+ break;
+ }
+
+ gtp_rw_size += strlen(gtp_rw_bufp);
+ gtp_rw_bufp += strlen(gtp_rw_bufp);
+ }
+
+ if (atomic_read(&gtp_frame_create)) {
+ char *tmp = gtp_frame_r_start;
+
+ do {
+ if (tmp[0] == 'h')
+ tfnum++;
+
+ tmp = gtp_frame_next(tmp);
+ if (!tmp)
+ break;
+
+ if (tmp == gtp_frame_end)
+ tmp = gtp_frame;
+ } while (tmp != gtp_frame_w_start);
+ }
+ sprintf(gtp_rw_bufp, "tframes:%x;", tfnum);
+ gtp_rw_size += strlen(gtp_rw_bufp);
+ gtp_rw_bufp += strlen(gtp_rw_bufp);
+
+ sprintf(gtp_rw_bufp, "tcreated:%x;", atomic_read(&gtp_frame_create));
+ gtp_rw_size += strlen(gtp_rw_bufp);
+ gtp_rw_bufp += strlen(gtp_rw_bufp);
+
+ sprintf(gtp_rw_bufp, "tsize:%x;", GTP_FRAME_SIZE);
+ gtp_rw_size += strlen(gtp_rw_bufp);
+ gtp_rw_bufp += strlen(gtp_rw_bufp);
+
+ spin_lock_irqsave(&gtp_frame_lock, flags);
+ tmpaddr = GTP_FRAME_SIZE
+ - (max(gtp_frame_w_start, gtp_frame_r_start)
+ - min(gtp_frame_w_start, gtp_frame_r_start));
+ spin_unlock_irqrestore(&gtp_frame_lock, flags);
+ if (tmpaddr == GTP_FRAME_SIZE && atomic_read(&gtp_frame_create) != 0)
+ tmpaddr = 0;
+ sprintf(gtp_rw_bufp, "tfree:%lx;", (unsigned long)tmpaddr);
+ gtp_rw_size += strlen(gtp_rw_bufp);
+ gtp_rw_bufp += strlen(gtp_rw_bufp);
+
+ sprintf(gtp_rw_bufp, "circular:%x;", gtp_circular);
+ gtp_rw_size += strlen(gtp_rw_bufp);
+ gtp_rw_bufp += strlen(gtp_rw_bufp);
+
+ sprintf(gtp_rw_bufp, "disconn:%x", gtp_disconnected_tracing);
+ gtp_rw_size += strlen(gtp_rw_bufp);
+ gtp_rw_bufp += strlen(gtp_rw_bufp);
+
+ return 1;
+}
+
+static void
+gtp_report_tracepoint(void)
+{
+ sprintf(gtp_rw_bufp, "T%lx:%lx:%c:%lx:%lx",
+ (unsigned long)current_gtp->num,
+ (unsigned long)current_gtp->addr,
+ (current_gtp->disable ? 'D' : 'E'),
+ (unsigned long)current_gtp->step,
+ (unsigned long)current_gtp->pass);
+ gtp_rw_size += strlen(gtp_rw_bufp);
+ gtp_rw_bufp += strlen(gtp_rw_bufp);
+}
+
+static void
+gtp_report_action(void)
+{
+ sprintf(gtp_rw_bufp, "A%lx:%lx:%s",
+ (unsigned long)current_gtp->num,
+ (unsigned long)current_gtp->addr,
+ current_gtp_action->src);
+ gtp_rw_size += strlen(gtp_rw_bufp);
+ gtp_rw_bufp += strlen(gtp_rw_bufp);
+}
+
+static void
+gtp_report_src(void)
+{
+ sprintf(gtp_rw_bufp, "Z%lx:%lx:%s",
+ (unsigned long)current_gtp->num,
+ (unsigned long)current_gtp->addr,
+ current_gtp_src->src);
+ gtp_rw_size += strlen(gtp_rw_bufp);
+ gtp_rw_bufp += strlen(gtp_rw_bufp);
+}
+
+static void
+gtp_current_set_check(void)
+{
+ if (current_gtp_src == NULL)
+ current_gtp = current_gtp->next;
+}
+
+static void
+gtp_current_action_check(void)
+{
+ if (current_gtp_action == NULL) {
+ current_gtp_src = current_gtp->src;
+ gtp_current_set_check();
+ }
+}
+
+static int
+gtp_gdbrsp_qtfp(void)
+{
+ if (gtp_list) {
+ current_gtp = gtp_list;
+ gtp_report_tracepoint();
+ current_gtp_action = current_gtp->action_list;
+ gtp_current_action_check();
+ } else {
+ gtp_rw_bufp[0] = 'l';
+ gtp_rw_size += 1;
+ gtp_rw_bufp += 1;
+ }
+
+ return 1;
+}
+
+static int
+gtp_gdbrsp_qtsp(void)
+{
+ if (current_gtp_action) {
+ gtp_report_action();
+ current_gtp_action = current_gtp_action->next;
+ gtp_current_action_check();
+ goto out;
+ }
+
+ if (current_gtp_src) {
+ gtp_report_src();
+ current_gtp_src = current_gtp_src->next;
+ gtp_current_set_check();
+ goto out;
+ }
+
+ if (current_gtp) {
+ gtp_report_tracepoint();
+ current_gtp_action = current_gtp->action_list;
+ gtp_current_action_check();
+ } else {
+ gtp_rw_bufp[0] = 'l';
+ gtp_rw_size += 1;
+ gtp_rw_bufp += 1;
+ }
+out:
+ return 1;
+}
+
+static void
+gtp_report_var(void)
+{
+ sprintf(gtp_rw_bufp, "%x:%s", current_gtp_var->num,
+ current_gtp_var->src);
+ gtp_rw_size += strlen(gtp_rw_bufp);
+ gtp_rw_bufp += strlen(gtp_rw_bufp);
+}
+
+static int
+gtp_gdbrsp_qtfsv(int f)
+{
+ if (f)
+ current_gtp_var = gtp_var_list;
+
+ if (current_gtp_var) {
+ gtp_report_var();
+ current_gtp_var = current_gtp_var->next;
+ } else {
+ gtp_rw_bufp[0] = 'l';
+ gtp_rw_size += 1;
+ gtp_rw_bufp += 1;
+ }
+
+ return 1;
+}
+
+static int
+gtp_gdbrsp_qtv(char *pkg)
+{
+ ULONGEST num;
+ struct gtp_var *var = NULL;
+ struct gtp_frame_var *vr = NULL;
+ uint64_t val;
+
+ pkg = hex2ulongest(pkg, &num);
+
+ if (gtp_start || !gtp_frame_current) {
+ if (num != GTP_VAR_CURRENT_TASK_ID) {
+ var = gtp_var_find(num);
+ if (var)
+ val = var->val;
+ }
+ } else {
+ char *next;
+
+ for (next = *(char **)(gtp_frame_current + 1); next;
+ next = *(char **)(next + 1)) {
+ if (next[0] == 'v') {
+ vr = (struct gtp_frame_var *)
+ (next + 1 + sizeof(char *));
+ if (vr->num == (unsigned int)num)
+ goto while_stop;
+ }
+ }
+while_stop:
+ if (vr)
+ val = vr->val;
+ }
+
+ if (var || vr) {
+ sprintf(gtp_rw_bufp, "V%08x%08x",
+ (unsigned int) (val >> 32),
+ (unsigned int) (val & 0xffffffff));
+ gtp_rw_size += strlen(gtp_rw_bufp);
+ gtp_rw_bufp += strlen(gtp_rw_bufp);
+ } else {
+ gtp_rw_bufp[0] = 'U';
+ gtp_rw_size += 1;
+ gtp_rw_bufp += 1;
+ }
+
+ return 1;
+}
+
+static int
+gtp_gdbrsp_qT(char *pkg)
+{
+ int ret = 1;
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_gdbrsp_qT: %s\n", pkg);
+#endif
+
+ if (strcmp("Status", pkg) == 0)
+ ret = gtp_gdbrsp_qtstatus();
+ else if (strcmp("fP", pkg) == 0)
+ ret = gtp_gdbrsp_qtfp();
+ else if (strcmp("sP", pkg) == 0)
+ ret = gtp_gdbrsp_qtsp();
+ else if (strcmp("fV", pkg) == 0)
+ ret = gtp_gdbrsp_qtfsv(1);
+ else if (strcmp("sV", pkg) == 0)
+ ret = gtp_gdbrsp_qtfsv(0);
+ else if (strncmp("V:", pkg, 2) == 0)
+ ret = gtp_gdbrsp_qtv(pkg + 2);
+
+ return ret;
+}
+
+static uint8_t gtp_m_buffer[0xffff];
+
+static int
+gtp_gdbrsp_m(char *pkg)
+{
+ int i;
+ ULONGEST addr, len;
+
+ /* Get add and len. */
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg = hex2ulongest(pkg, &addr);
+ if (pkg[0] != ',')
+ return -EINVAL;
+ pkg++;
+ pkg = hex2ulongest(pkg, &len);
+ if (len == 0)
+ return -EINVAL;
+ len &= 0xffff;
+ len = (ULONGEST) min((int)((GTP_RW_MAX - 4 - gtp_rw_size) / 2),
+ (int)len);
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_gdbrsp_m: addr = 0x%lx len = %d\n",
+ (unsigned long) addr, (int) len);
+#endif
+
+ if (gtp_start || !gtp_frame_current) {
+ if (probe_kernel_read(gtp_m_buffer, (void *)(CORE_ADDR)addr,
+ (size_t)len))
+ return -EFAULT;
+ } else {
+ char *next;
+ int ret;
+
+ /* The following part is for gtpro support.
+ It is not available because it make disassemble cannot
+ work when select a trace frame. */
+#if 0
+ struct gtpro_entry *gtroe;
+
+ memset(gtp_m_buffer, 0, len);
+
+ /* Read the gtpro. */
+ for (gtroe = gtpro_list; gtroe; gtroe = gtroe->next) {
+ CORE_ADDR cur_start, cur_end;
+
+ cur_start = max(gtroe->start, (CORE_ADDR)addr);
+ cur_end = min(gtroe->end, ((CORE_ADDR)(addr + len)));
+ if (cur_start < cur_end) {
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_gdbrsp_m: ro read "
+ "start = 0x%lx end = 0x%lx\n",
+ (unsigned long) cur_start,
+ (unsigned long) cur_end);
+#endif
+ if (probe_kernel_read(gtp_m_buffer,
+ (void *)cur_start,
+ (size_t)(cur_end
+ - cur_start)))
+ return -EFAULT;
+ }
+ }
+#endif
+ ret = probe_kernel_read(gtp_m_buffer, (void *)(CORE_ADDR)addr,
+ (size_t)len);
+
+ for (next = *(char **)(gtp_frame_current + 1); next;
+ next = *(char **)(next + 1)) {
+ if (next[0] == 'm') {
+ struct gtp_frame_mem *mr;
+ ULONGEST cur_start, cur_end;
+ uint8_t *buf;
+
+ mr = (struct gtp_frame_mem *)
+ (next + 1 + sizeof(char *));
+ buf = next + GTP_FRAME_MEM_SIZE;
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_gdbrsp_m: section "
+ "addr = 0x%lx size = %lu\n",
+ (unsigned long) mr->addr,
+ (unsigned long) mr->size);
+#endif
+ cur_start = max(((ULONGEST)mr->addr), addr);
+ cur_end = min(((ULONGEST)mr->addr
+ + mr->size),
+ (addr + len));
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_gdbrsp_m: read "
+ "start = 0x%lx end = 0x%lx\n",
+ (unsigned long) cur_start,
+ (unsigned long) cur_end);
+#endif
+ if (cur_start < cur_end) {
+ memcpy(gtp_m_buffer,
+ buf + cur_start - mr->addr,
+ cur_end - cur_start);
+ ret = 0;
+ }
+ }
+ }
+
+ if (ret)
+ return -EFAULT;
+ }
+
+ for (i = 0; i < (int)len; i++) {
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_gdbrsp_m: %d %02x\n", i, gtp_m_buffer[i]);
+#endif
+ sprintf(gtp_rw_bufp, "%02x", gtp_m_buffer[i]);
+ gtp_rw_bufp += 2;
+ gtp_rw_size += 2;
+ }
+
+ return 1;
+}
+
+static int
+gtp_gdbrsp_g(void)
+{
+ char *next;
+ struct pt_regs *regs;
+
+ if (GTP_RW_MAX - 4 - gtp_rw_size < GTP_GDBRSP_REG_SIZE)
+ return -E2BIG;
+
+ if (gtp_start || !gtp_frame_current) {
+ memset(gtp_rw_bufp, '0', GTP_GDBRSP_REG_SIZE);
+ goto out;
+ }
+
+ /* Get the regs. */
+ regs = NULL;
+ for (next = *(char **)(gtp_frame_current + 1); next;
+ next = *(char **)(next + 1)) {
+ if (next[0] == 'r') {
+ regs = (struct pt_regs *)
+ (next + 1 + sizeof(char *));
+ break;
+ }
+ }
+ if (regs)
+ gtp_regs2ascii(regs, gtp_rw_bufp);
+ else {
+ struct pt_regs pregs;
+ struct gtp_entry *tpe;
+
+ memset(&pregs, '\0', sizeof(struct pt_regs));
+ tpe = gtp_list_find_onlynum(*(ULONGEST *)(gtp_frame_current
+ + 1
+ + sizeof(char *)));
+ if (tpe)
+ GTP_REGS_PC(&pregs) = (unsigned long)tpe->addr;
+ gtp_regs2ascii(&pregs, gtp_rw_bufp);
+ }
+out:
+ gtp_rw_bufp += GTP_GDBRSP_REG_SIZE;
+ gtp_rw_size += GTP_GDBRSP_REG_SIZE;
+
+ return 1;
+}
+
+static DECLARE_MUTEX(gtp_rw_lock);
+static DECLARE_WAIT_QUEUE_HEAD(gtp_rw_wq);
+static unsigned int gtp_rw_count;
+
+static int
+gtp_open(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_open\n");
+#endif
+
+ down(&gtp_rw_lock);
+ if (gtp_rw_count == 0) {
+ gtp_read_ack = 0;
+ gtp_rw_buf = vmalloc(GTP_RW_MAX);
+ if (!gtp_rw_buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+ gtp_rw_count++;
+
+out:
+ up(&gtp_rw_lock);
+ return ret;
+}
+
+static int
+gtp_release(struct inode *inode, struct file *file)
+{
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_release\n");
+#endif
+
+ down(&gtp_rw_lock);
+ gtp_rw_count--;
+ if (gtp_rw_count == 0) {
+ vfree(gtp_rw_buf);
+
+ if (!gtp_disconnected_tracing) {
+ gtp_gdbrsp_qtstop();
+ if (gtp_frame) {
+ vfree(gtp_frame);
+ gtp_frame = NULL;
+ }
+ gtp_gdbrsp_qtinit();
+ }
+ }
+ up(&gtp_rw_lock);
+
+ return 0;
+}
+
+static long
+gtp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_ioctl: %x\n", cmd);
+#endif
+
+ return 0;
+}
+
+static ssize_t
+gtp_write(struct file *file, const char __user *buf, size_t size,
+ loff_t *ppos)
+{
+ char *rsppkg = NULL;
+ int i, ret;
+ unsigned char csum = 0;
+
+ if (down_interruptible(&gtp_rw_lock))
+ return -EINTR;
+
+ if (size == 0) {
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_write: try write 0 size.\n");
+#endif
+ goto error_out;
+ }
+
+ size = min(size, (size_t) GTP_RW_MAX);
+ if (copy_from_user(gtp_rw_buf, buf, size)) {
+ size = -EFAULT;
+ goto error_out;
+ }
+
+ if (gtp_rw_buf[0] == '+' || gtp_rw_buf[0] == '-'
+ || gtp_rw_buf[0] == '\3') {
+ if (gtp_rw_buf[0] == '+')
+ gtp_rw_size = 0;
+ size = 1;
+ goto out;
+ }
+
+ if (size < 4) {
+ gtp_read_ack = '-';
+ goto out;
+ }
+ /* Check format and crc and get the rsppkg. */
+ for (i = 0; i < size - 2; i++) {
+ if (rsppkg == NULL) {
+ if (gtp_rw_buf[i] == '$')
+ rsppkg = gtp_rw_buf + i + 1;
+ } else {
+ if (gtp_rw_buf[i] == '#')
+ break;
+ else
+ csum += gtp_rw_buf[i];
+ }
+ }
+ if (rsppkg && gtp_rw_buf[i] == '#') {
+ /* Format is OK. Check crc. */
+ unsigned char c1, c2;
+
+ gtp_rw_buf[i] = '\0';
+
+ c1 = gtp_rw_buf[i+1];
+ c2 = gtp_rw_buf[i+2];
+ if (csum == (c1 << 4) + c2) {
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_write: crc error\n");
+#endif
+ gtp_read_ack = '-';
+ goto out;
+ }
+ } else {
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_write: format error\n");
+#endif
+ gtp_read_ack = '-';
+ goto out;
+ }
+ gtp_read_ack = '+';
+
+ wake_up_interruptible_nr(&gtp_rw_wq, 1);
+
+ up(&gtp_rw_lock);
+ if (down_interruptible(&gtp_rw_lock))
+ return -EINTR;
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_write: %s\n", rsppkg);
+#endif
+
+ /* Handle rsppkg and put return to gtp_rw_buf. */
+ gtp_rw_buf[0] = '$';
+ gtp_rw_bufp = gtp_rw_buf + 1;
+ gtp_rw_size = 0;
+ ret = 1;
+ switch (rsppkg[0]) {
+ case '?':
+ strcpy(gtp_rw_bufp, "S05");
+ gtp_rw_bufp += 3;
+ gtp_rw_size += 3;
+ break;
+ case 'g':
+ ret = gtp_gdbrsp_g();
+ break;
+ case 'm':
+ ret = gtp_gdbrsp_m(rsppkg + 1);
+ break;
+ case 'Q':
+ if (rsppkg[1] == 'T')
+ ret = gtp_gdbrsp_QT(rsppkg + 2);
+ break;
+ case 'q':
+ if (rsppkg[1] == 'T')
+ ret = gtp_gdbrsp_qT(rsppkg + 2);
+ else if (strncmp("qSupported", rsppkg, 10) == 0) {
+ strcpy(gtp_rw_bufp,
+ "ConditionalTracepoints+;"
+ "TracepointSource+;DisconnectedTracing+");
+ gtp_rw_size += strlen(gtp_rw_bufp);
+ gtp_rw_bufp += strlen(gtp_rw_bufp);
+ ret = 1;
+ }
+ break;
+ }
+ if (ret == 0) {
+ strcpy(gtp_rw_bufp, "OK");
+ gtp_rw_bufp += 2;
+ gtp_rw_size += 2;
+ } else if (ret < 0) {
+ sprintf(gtp_rw_bufp, "E%02x", -ret);
+ gtp_rw_bufp += 3;
+ gtp_rw_size += 3;
+ }
+
+ gtp_rw_bufp[0] = '#';
+ csum = 0;
+ for (i = 1; i < gtp_rw_size + 1; i++)
+ csum += gtp_rw_buf[i];
+ gtp_rw_bufp[1] = TOHEX(csum >> 4);
+ gtp_rw_bufp[2] = TOHEX(csum & 0x0f);
+ gtp_rw_bufp = gtp_rw_buf;
+ gtp_rw_size += 4;
+
+out:
+ wake_up_interruptible_nr(&gtp_rw_wq, 1);
+error_out:
+ up(&gtp_rw_lock);
+ return size;
+}
+
+static ssize_t
+gtp_read(struct file *file, char __user *buf, size_t size,
+ loff_t *ppos)
+{
+ int err;
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_read\n");
+#endif
+
+ if (size == 0)
+ goto out;
+
+ if (down_interruptible(&gtp_rw_lock))
+ return -EINTR;
+
+ if (gtp_read_ack) {
+ err = put_user(gtp_read_ack, buf);
+ if (err) {
+ size = -err;
+ goto out;
+ }
+ gtp_read_ack = 0;
+ size = 1;
+ goto out;
+ }
+
+ size = min(gtp_rw_size, size);
+ if (size == 0)
+ goto out;
+ if (copy_to_user(buf, gtp_rw_bufp, size)) {
+ size = -EFAULT;
+ goto out;
+ }
+ gtp_rw_bufp += size;
+ gtp_rw_size -= size;
+
+out:
+ up(&gtp_rw_lock);
+ return size;
+}
+
+static unsigned int
+gtp_poll(struct file *file, poll_table *wait)
+{
+ unsigned int mask = POLLOUT | POLLWRNORM;
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_poll\n");
+#endif
+
+ down(&gtp_rw_lock);
+ poll_wait(file, &gtp_rw_wq, wait);
+ if (gtp_read_ack || gtp_rw_size)
+ mask |= POLLIN | POLLRDNORM;
+ up(&gtp_rw_lock);
+
+ return mask;
+}
+
+static const struct file_operations proc_gtp_operations = {
+ .owner = THIS_MODULE,
+ .open = gtp_open,
+ .release = gtp_release,
+ .unlocked_ioctl = gtp_ioctl,
+ .compat_ioctl = gtp_ioctl,
+ .read = gtp_read,
+ .write = gtp_write,
+ .poll = gtp_poll,
+};
+
+static int __init gtp_init(void)
+{
+ gtp_list = NULL;
+ gtp_read_ack = 0;
+ gtp_rw_bufp = NULL;
+ gtp_rw_size = 0;
+ gtp_start = 0;
+ gtp_disconnected_tracing = 0;
+ gtp_circular = 0;
+ gtp_var_list = &gtp_var_current_task;
+ gtp_var_head = GTP_VAR_CURRENT_TASK_ID;
+ gtp_var_tail = GTP_VAR_CURRENT_TASK_ID;
+ gtp_var_array = NULL;
+ current_gtp_var = NULL;
+ gtp_frame = NULL;
+ gtp_frame_r_start = NULL;
+ gtp_frame_w_start = NULL;
+ gtp_frame_end = NULL;
+ gtp_frame_is_circular = 0;
+ gtp_frame_current = NULL;
+ gtp_frame_current_num = 0;
+ atomic_set(&gtp_frame_create, 0);
+ gtp_rw_count = 0;
+ current_gtp = NULL;
+ current_gtp_action = NULL;
+ current_gtp_src = NULL;
+ gtpro_list = NULL;
+
+ gtp_wq = create_singlethread_workqueue("gtpd");
+ if (gtp_wq == NULL)
+ return -ENOMEM;
+
+ if (proc_create("gtp", S_IFIFO | S_IRUSR | S_IWUSR, NULL,
+ &proc_gtp_operations) == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void __exit gtp_exit(void)
+{
+ remove_proc_entry("gtp", NULL);
+
+ gtp_gdbrsp_qtstop();
+ if (gtp_frame) {
+ vfree(gtp_frame);
+ gtp_frame = NULL;
+ }
+ gtp_gdbrsp_qtinit();
+
+ destroy_workqueue(gtp_wq);
+}
+
+module_init(gtp_init)
+module_exit(gtp_exit)
+
+MODULE_AUTHOR("Hui Zhu");
+MODULE_LICENSE("GPL");
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/