From patchwork Mon Sep 16 08:55:49 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vijay Kilari X-Patchwork-Id: 2896021 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id CF150BFF05 for ; Mon, 16 Sep 2013 08:57:42 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id B2B9B201B8 for ; Mon, 16 Sep 2013 08:57:37 +0000 (UTC) Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id D0B0B201B7 for ; Mon, 16 Sep 2013 08:57:35 +0000 (UTC) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1VLUcO-0006GY-5Y; Mon, 16 Sep 2013 08:57:16 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1VLUcG-0005kC-2I; Mon, 16 Sep 2013 08:57:08 +0000 Received: from mail-pd0-x235.google.com ([2607:f8b0:400e:c02::235]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1VLUc9-0005hu-Ac for linux-arm-kernel@lists.infradead.org; Mon, 16 Sep 2013 08:57:06 +0000 Received: by mail-pd0-f181.google.com with SMTP id g10so3826151pdj.26 for ; Mon, 16 Sep 2013 01:56:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=Fv3XVAIOzyeYlQ0ZYTS27FkSDEAePfkl5Zw/ySQMMSI=; b=zVrqF9MJ3VgzQDHVF8ebw7G1rcOAiZrBWovwb0sGMWE6RcbWeMTsTsxCJhdmmSKK1A ft29GwBrQdmbmzdWlQCNcXpJyscVy6FQsuCWzPT0pspYySSYQwp9dijxtSFZ+L3ag/LE z7k37UT/zYZ2n0gV7yqJm/sguwnpBWI9Mx4XcJSe+BOHwkLVBxbN1P9F4sDNiGQ4n/M9 3GrzTaLcckFgMB3mwIrdgvF0O2/IRbLIDdEAHD/I6ByS1WJ64ScMpCdrd9a+cLSSSPZb auOUnKLHT+N7vf10roO4RSPwyIyl3k2xy1rIhOYguTrSzJO1ee2U/9fjXgwtoycHzFJk Vpog== X-Received: by 10.68.197.73 with SMTP id is9mr27690676pbc.75.1379321799868; Mon, 16 Sep 2013 01:56:39 -0700 (PDT) Received: from ubuntu.caveonetworks.com ([115.119.134.194]) by mx.google.com with ESMTPSA id xv2sm23673353pbb.39.1969.12.31.16.00.00 (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Mon, 16 Sep 2013 01:56:39 -0700 (PDT) From: vijay.kilari@gmail.com To: will.deacon@arm.com, sandeepa.prabhu@linaro.org Subject: [RFC PATCH 1/2] Aarch64: KGDB: Add Basic KGDB support Date: Mon, 16 Sep 2013 14:25:49 +0530 Message-Id: <1379321750-907-2-git-send-email-vijay.kilari@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1379321750-907-1-git-send-email-vijay.kilari@gmail.com> References: <1379321750-907-1-git-send-email-vijay.kilari@gmail.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130916_045701_716516_F236520B X-CRM114-Status: GOOD ( 19.46 ) X-Spam-Score: -2.0 (--) Cc: Prasun.Kapoor@caviumnetworks.com, Vijaya Kumar K , linux@arm.linux.org.uk, linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.7 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Vijaya Kumar K Add KGDB debug support for kernel debugging. With these changes, basic KGDB debugging is possible. Target waits for GDB tool on hitting compile time inserted break point and GDB tool can establish connection with target and can set/clear breakpoints and continue. Signed-off-by: Vijaya Kumar K --- arch/arm64/include/asm/kgdb.h | 61 +++++++++ arch/arm64/kernel/Makefile | 1 + arch/arm64/kernel/kgdb.c | 287 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 349 insertions(+) diff --git a/arch/arm64/include/asm/kgdb.h b/arch/arm64/include/asm/kgdb.h new file mode 100644 index 0000000..5b5f59e --- /dev/null +++ b/arch/arm64/include/asm/kgdb.h @@ -0,0 +1,61 @@ +/* + * Aarch64 KGDB support + * + * Author: Vijaya Kumar + * + * contents are extracted from arch/arm/include/kgdb.h + * + */ + +#ifndef __ARM_KGDB_H__ +#define __ARM_KGDB_H__ + +#include + +/* + * Break Instruction encoding + */ + +#define BREAK_INSTR_SIZE 4 +#define KGDB_BREAKINST_ESR_VAL 0xf2000000 +#define KGDB_COMPILED_BREAK_ESR_VAL 0xf2000001 +#define CACHE_FLUSH_IS_SAFE 1 + +#ifndef __ASSEMBLY__ + +static inline void arch_kgdb_breakpoint(void) +{ +#ifndef __ARMEB__ + asm(".word 0xd4200020"); +#else + asm(".word 0x200020d4"); +#endif +} + + +extern void kgdb_handle_bus_error(void); +extern int kgdb_fault_expected; + +#endif /* !__ASSEMBLY__ */ + +/* + * gdb is expecting the following registers layout. + * + * r0-r31: 64bit each + * f0-f31: unused + * fps: unused + * + */ + +#define _GP_REGS 34 +#define _FP_REGS 32 +#define _EXTRA_REGS 2 +#define GDB_MAX_REGS (_GP_REGS + (_FP_REGS * 3) + _EXTRA_REGS) +#define DBG_MAX_REG_NUM (_GP_REGS + _FP_REGS + _EXTRA_REGS) + +#define KGDB_MAX_NO_CPUS 1 +#define BUFMAX 400 +#define NUMREGBYTES (DBG_MAX_REG_NUM << 2) + +#endif /* __ASM_KGDB_H__ */ + diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index f667a09..b46a177 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -20,6 +20,7 @@ arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o smp_psci.o arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o +arm64-obj-$(CONFIG_KGDB) += kgdb.o obj-y += $(arm64-obj-y) vdso/ obj-$(CONFIG_ARM64_ILP32) += vdsoilp32/ diff --git a/arch/arm64/kernel/kgdb.c b/arch/arm64/kernel/kgdb.c new file mode 100644 index 0000000..a3a7712 --- /dev/null +++ b/arch/arm64/kernel/kgdb.c @@ -0,0 +1,287 @@ +/* + * Aarch64 KGDB support. + * + * most part of code copied from arch/arm/kernel/kgdb.c + * + * Author: Vijaya Kumar K + */ + +#include +#include +#include +#include +#include + +struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = +{ + { "x0", 8, offsetof(struct pt_regs, regs[0])}, + { "x1", 8, offsetof(struct pt_regs, regs[1])}, + { "x2", 8, offsetof(struct pt_regs, regs[2])}, + { "x3", 8, offsetof(struct pt_regs, regs[3])}, + { "x4", 8, offsetof(struct pt_regs, regs[4])}, + { "x5", 8, offsetof(struct pt_regs, regs[5])}, + { "x6", 8, offsetof(struct pt_regs, regs[6])}, + { "x7", 8, offsetof(struct pt_regs, regs[7])}, + { "x8", 8, offsetof(struct pt_regs, regs[8])}, + { "x9", 8, offsetof(struct pt_regs, regs[9])}, + { "x10", 8, offsetof(struct pt_regs, regs[10])}, + { "x11", 8, offsetof(struct pt_regs, regs[11])}, + { "x12", 8, offsetof(struct pt_regs, regs[12])}, + { "x13", 8, offsetof(struct pt_regs, regs[13])}, + { "x14", 8, offsetof(struct pt_regs, regs[14])}, + { "x15", 8, offsetof(struct pt_regs, regs[15])}, + { "x16", 8, offsetof(struct pt_regs, regs[16])}, + { "x17", 8, offsetof(struct pt_regs, regs[17])}, + { "x18", 8, offsetof(struct pt_regs, regs[18])}, + { "x19", 8, offsetof(struct pt_regs, regs[19])}, + { "x20", 8, offsetof(struct pt_regs, regs[20])}, + { "x21", 8, offsetof(struct pt_regs, regs[21])}, + { "x22", 8, offsetof(struct pt_regs, regs[22])}, + { "x23", 8, offsetof(struct pt_regs, regs[23])}, + { "x24", 8, offsetof(struct pt_regs, regs[24])}, + { "x25", 8, offsetof(struct pt_regs, regs[25])}, + { "x26", 8, offsetof(struct pt_regs, regs[26])}, + { "x27", 8, offsetof(struct pt_regs, regs[27])}, + { "x28", 8, offsetof(struct pt_regs, regs[28])}, + { "x29", 8, offsetof(struct pt_regs, regs[29])}, + { "x30", 8, offsetof(struct pt_regs, regs[30])}, + { "sp", 8, offsetof(struct pt_regs, sp)}, + { "pc", 8, offsetof(struct pt_regs, pc)}, + { "cpsr", 4, offsetof(struct pt_regs, pstate)}, + { "v0", 16, -1 }, + { "v1", 16, -1 }, + { "v2", 16, -1 }, + { "v3", 16, -1 }, + { "v4", 16, -1 }, + { "v5", 16, -1 }, + { "v6", 16, -1 }, + { "v7", 16, -1 }, + { "v8", 16, -1 }, + { "v9", 16, -1 }, + { "v10", 16, -1 }, + { "v11", 16, -1 }, + { "v12", 16, -1 }, + { "v13", 16, -1 }, + { "v14", 16, -1 }, + { "v15", 16, -1 }, + { "v16", 16, -1 }, + { "v17", 16, -1 }, + { "v18", 16, -1 }, + { "v19", 16, -1 }, + { "v20", 16, -1 }, + { "v21", 16, -1 }, + { "v22", 16, -1 }, + { "v23", 16, -1 }, + { "v24", 16, -1 }, + { "v25", 16, -1 }, + { "v26", 16, -1 }, + { "v27", 16, -1 }, + { "v28", 16, -1 }, + { "v29", 16, -1 }, + { "v30", 16, -1 }, + { "v31", 16, -1 }, + { "fpsr", 4, -1 }, + { "fpcr", 4, -1 }, +}; + +char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs) +{ + if (regno >= DBG_MAX_REG_NUM || regno < 0) + return NULL; + + if (dbg_reg_def[regno].offset != -1) + memcpy(mem, (void *)regs + dbg_reg_def[regno].offset, + dbg_reg_def[regno].size); + else + memset(mem, 0, dbg_reg_def[regno].size); + return dbg_reg_def[regno].name; +} + +int dbg_set_reg(int regno, void *mem, struct pt_regs *regs) +{ + if (regno >= DBG_MAX_REG_NUM || regno < 0) + return -EINVAL; + + if (dbg_reg_def[regno].offset != -1) + memcpy((void *)regs + dbg_reg_def[regno].offset, mem, + dbg_reg_def[regno].size); + return 0; +} + +void +sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *task) +{ + struct pt_regs *thread_regs; + int regno; + int i; + + /* Just making sure... */ + if (task == NULL) + return; + + /* Initialize to zero */ + for (regno = 0; regno < GDB_MAX_REGS; regno++) + gdb_regs[regno] = 0; + + thread_regs = task_pt_regs(task); + + for(i = 0; i < 31; i++) + gdb_regs[i] = thread_regs->regs[i]; + + gdb_regs[31] = thread_regs->sp; + gdb_regs[32] = thread_regs->pc; + gdb_regs[33] = thread_regs->pstate; +} + +void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc) +{ + regs->pc = pc; +} + +static int compiled_break; +int kgdb_arch_handle_exception(int exception_vector, int signo, int err_code, + char *remcom_in_buffer, char *remcom_out_buffer, + struct pt_regs *linux_regs) +{ + unsigned long addr; + char *ptr; + int err; + + switch (remcom_in_buffer[0]) { + case 'D': + case 'k': + case 'c': + /* + * Try to read optional parameter, pc unchanged if no parm. + * If this was a compiled breakpoint, we need to move + * to the next instruction or we will just breakpoint + * over and over again. + */ + ptr = &remcom_in_buffer[1]; + if (kgdb_hex2long(&ptr, &addr)) + linux_regs->pc = addr; + else if (compiled_break == 1) + linux_regs->pc += 4; + + compiled_break = 0; + err = 0; + break; + + default: + err = -1; + } + return err; +} + +static int +kgdb_brk_fn(struct pt_regs *regs, unsigned int esr, unsigned long addr) +{ + kgdb_handle_exception(1, SIGTRAP, 0, regs); + return 0; +} + +static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int esr, + unsigned long addr) +{ + compiled_break = 1; + kgdb_handle_exception(1, SIGTRAP, 0, regs); + + return 0; +} +static struct break_hook kgdb_brkpt_hook = { + .esr_mask = 0xffffffff, + .esr_magic = KGDB_BREAKINST_ESR_VAL, + .fn = kgdb_brk_fn +}; + +static struct break_hook kgdb_compiled_brkpt_hook = { + .esr_mask = 0xffffffff, + .esr_magic = KGDB_COMPILED_BREAK_ESR_VAL, + .fn = kgdb_compiled_brk_fn +}; + +static void kgdb_call_nmi_hook(void *ignored) +{ + kgdb_nmicallback(raw_smp_processor_id(), get_irq_regs()); +} + +void kgdb_roundup_cpus(unsigned long flags) +{ + local_irq_enable(); + smp_call_function(kgdb_call_nmi_hook, NULL, 0); + local_irq_disable(); +} + +static int __kgdb_notify(struct die_args *args, unsigned long cmd) +{ + struct pt_regs *regs = args->regs; + + if (kgdb_handle_exception(1, args->signr, cmd, regs)) + return NOTIFY_DONE; + return NOTIFY_STOP; +} + +static int +kgdb_notify(struct notifier_block *self, unsigned long cmd, void *ptr) +{ + unsigned long flags; + int ret; + + local_irq_save(flags); + ret = __kgdb_notify(ptr, cmd); + local_irq_restore(flags); + + return ret; +} + +static struct notifier_block kgdb_notifier = { + .notifier_call = kgdb_notify, + .priority = -INT_MAX, +}; + +/** + * kgdb_arch_init - Perform any architecture specific initalization. + * + * This function will handle the initalization of any architecture + * specific callbacks. + */ +int kgdb_arch_init(void) +{ + int ret = register_die_notifier(&kgdb_notifier); + + if (ret != 0) + return ret; + + register_break_hook(&kgdb_brkpt_hook); + register_break_hook(&kgdb_compiled_brkpt_hook); + + return 0; +} + +/** + * kgdb_arch_exit - Perform any architecture specific uninitalization. + * + * This function will handle the uninitalization of any architecture + * specific callbacks, for dynamic registration and unregistration. + */ +void kgdb_arch_exit(void) +{ + unregister_break_hook(&kgdb_brkpt_hook); + unregister_break_hook(&kgdb_compiled_brkpt_hook); + unregister_die_notifier(&kgdb_notifier); +} + +/* + * Register our undef instruction hooks with ARM undef core. + * We regsiter a hook specifically looking for the KGB break inst + * and we handle the normal undef case within the do_undefinstr + * handler. + */ +struct kgdb_arch arch_kgdb_ops = { +#ifndef __ARMEB__ + .gdb_bpt_instr = {0x00, 0x00, 0x20, 0xd4} +#else /* ! __ARMEB__ */ + .gdb_bpt_instr = {0xd4, 0x20, 0x00, 0x00} +#endif +}; +