/*
* File: arch/blackfin/kernel/traps.c
* Based on:
* Author: Hamish Macdonald
*
* Created:
* Description: uses S/W interrupt 15 for the system calls
*
* Modified:
* Copyright 2004-2006 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* 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, see the file COPYING, or write
* to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/fs.h>
#include <linux/rbtree.h>
#include <asm/traps.h>
#include <asm/cacheflush.h>
#include <asm/cplb.h>
#include <asm/blackfin.h>
#include <asm/irq_handler.h>
#include <linux/irq.h>
#include <asm/trace.h>
#include <asm/fixed_code.h>
#ifdef CONFIG_KGDB
# include <linux/kgdb.h>
# define CHK_DEBUGGER_TRAP() \
do { \
kgdb_handle_exception(trapnr, sig, info.si_code, fp); \
} while (0)
# define CHK_DEBUGGER_TRAP_MAYBE() \
do { \
if (kgdb_connected) \
CHK_DEBUGGER_TRAP(); \
} while (0)
#else
# define CHK_DEBUGGER_TRAP() do { } while (0)
# define CHK_DEBUGGER_TRAP_MAYBE() do { } while (0)
#endif
#ifdef CONFIG_DEBUG_VERBOSE
#define verbose_printk(fmt, arg...) \
printk(fmt, ##arg)
#else
#define verbose_printk(fmt, arg...) \
({ if (0) printk(fmt, ##arg); 0; })
#endif
/* Initiate the event table handler */
void __init trap_init(void)
{
CSYNC();
bfin_write_EVT3(trap);
CSYNC();
}
static void decode_address(char *buf, unsigned long address)
{
#ifdef CONFIG_DEBUG_VERBOSE
struct vm_list_struct *vml;
struct task_struct *p;
struct mm_struct *mm;
unsigned long flags, offset;
unsigned char in_atomic = (bfin_read_IPEND() & 0x10) || in_atomic();
struct rb_node *n;
#ifdef CONFIG_KALLSYMS
unsigned long symsize;
const char *symname;
char *modname;
char *delim = ":";
char namebuf[128];
/* look up the address and see if we are in kernel space */
symname = kallsyms_lookup(address, &symsize, &offset, &modname, namebuf);
if (symname) {
/* yeah! kernel space! */
if (!modname)
modname = delim = "";
sprintf(buf, "<0x%p> { %s%s%s%s + 0x%lx }",
(void *)address, delim, modname, delim, symname,
(unsigned long)offset);
return;
}
#endif
/* Problem in fixed code section? */
if (address >= FIXED_CODE_START && address < FIXED_CODE_END) {
sprintf(buf, "<0x%p> /* Maybe fixed code section */", (void *)address);
return;
}
/* Problem somewhere before the kernel start address */
if (address < CONFIG_BOOT_LOAD) {
sprintf(buf, "<0x%p> /* Maybe null pointer? */", (void *)address);
return;
}
/* looks like we're off in user-land, so let's walk all the
* mappings of all our processes and see if we can't be a whee
* bit more specific
*/
write_lock_irqsave(&tasklist_lock, flags);
for_each_process(p) {
mm = (in_atomic ? p->mm : get_task_mm