Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 24 Mar 2011 17:07:50 +0000 (10:07 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 24 Mar 2011 17:07:50 +0000 (10:07 -0700)
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-2.6-mn10300:
  MN10300: gcc 4.6 vs am33 inline assembly
  MN10300: Deprecate gdbstub
  MN10300: Allow KGDB to use the MN10300 serial ports
  MN10300: Emulate single stepping in KGDB on MN10300
  MN10300: Generalise kernel debugger kernel halt, reboot or power off hook
  KGDB: Notify GDB of machine halt, reboot or power off
  MN10300: Use KGDB
  MN10300: Create generic kernel debugger hooks
  MN10300: Create general kernel debugger cache flushing
  MN10300: Introduce a general config option for kernel debugger hooks
  MN10300: The icache invalidate functions should disable the icache first
  MN10300: gdbstub: Restrict single-stepping to non-preemptable non-SMP configs

39 files changed:
arch/mn10300/Kconfig
arch/mn10300/Kconfig.debug
arch/mn10300/include/asm/debugger.h [new file with mode: 0644]
arch/mn10300/include/asm/div64.h
arch/mn10300/include/asm/fpu.h
arch/mn10300/include/asm/irqflags.h
arch/mn10300/include/asm/kgdb.h [new file with mode: 0644]
arch/mn10300/include/asm/smp.h
arch/mn10300/include/asm/thread_info.h
arch/mn10300/kernel/Makefile
arch/mn10300/kernel/entry.S
arch/mn10300/kernel/fpu.c
arch/mn10300/kernel/gdb-cache.S [deleted file]
arch/mn10300/kernel/gdb-io-ttysm.c
arch/mn10300/kernel/gdb-stub.c
arch/mn10300/kernel/internal.h
arch/mn10300/kernel/irq.c
arch/mn10300/kernel/kgdb.c [new file with mode: 0644]
arch/mn10300/kernel/mn10300-serial.c
arch/mn10300/kernel/process.c
arch/mn10300/kernel/smp.c
arch/mn10300/kernel/switch_to.S
arch/mn10300/kernel/traps.c
arch/mn10300/mm/Kconfig.cache
arch/mn10300/mm/Makefile
arch/mn10300/mm/cache-dbg-flush-by-reg.S [new file with mode: 0644]
arch/mn10300/mm/cache-dbg-flush-by-tag.S [new file with mode: 0644]
arch/mn10300/mm/cache-dbg-inv-by-reg.S [new file with mode: 0644]
arch/mn10300/mm/cache-dbg-inv-by-tag.S [new file with mode: 0644]
arch/mn10300/mm/cache-dbg-inv.S [new file with mode: 0644]
arch/mn10300/mm/cache-flush-by-tag.S
arch/mn10300/mm/cache-inv-by-reg.S
arch/mn10300/mm/cache-inv-by-tag.S
arch/mn10300/mm/cache.inc [new file with mode: 0644]
arch/mn10300/mm/fault.c
arch/mn10300/proc-mn103e010/include/proc/cache.h
arch/mn10300/proc-mn2ws0050/include/proc/cache.h
include/linux/kgdb.h
kernel/debug/gdbstub.c

index 10971be4306156a5eddc22746246c7a80dd83ea7..d8ab97a73db288db6b601fd13fd2baaa278f6809 100644 (file)
@@ -3,6 +3,8 @@ config MN10300
        select HAVE_OPROFILE
        select HAVE_GENERIC_HARDIRQS
        select GENERIC_HARDIRQS_NO_DEPRECATED
+       select HAVE_ARCH_TRACEHOOK
+       select HAVE_ARCH_KGDB
 
 config AM33_2
        def_bool n
@@ -401,9 +403,9 @@ comment "[!] NOTE: A lower number/level indicates a higher priority (0 is highes
 comment "____Non-maskable interrupt levels____"
 comment "The following must be set to a higher priority than local_irq_disable() and on-chip serial"
 
-config GDBSTUB_IRQ_LEVEL
-       int "GDBSTUB interrupt priority"
-       depends on GDBSTUB
+config DEBUGGER_IRQ_LEVEL
+       int "DEBUGGER interrupt priority"
+       depends on KERNEL_DEBUGGER
        range 0 1 if LINUX_CLI_LEVEL = 2
        range 0 2 if LINUX_CLI_LEVEL = 3
        range 0 3 if LINUX_CLI_LEVEL = 4
@@ -437,7 +439,7 @@ config LINUX_CLI_LEVEL
          EPSW.IM from 7.  Any interrupt is permitted for which the level is
          lower than EPSW.IM.
 
-         Certain interrupts, such as GDBSTUB and virtual MN10300 on-chip
+         Certain interrupts, such as DEBUGGER and virtual MN10300 on-chip
          serial DMA interrupts are allowed to interrupt normal disabled
          sections.
 
index ce83c74b3fd714abf68fca4a4d2beef024545c83..bdbfd444a9ff9cf5569baa685a906e5f8800801d 100644 (file)
@@ -36,7 +36,7 @@ config KPROBES
 
 config GDBSTUB
        bool "Remote GDB kernel debugging"
-       depends on DEBUG_KERNEL
+       depends on DEBUG_KERNEL && DEPRECATED
        select DEBUG_INFO
        select FRAME_POINTER
        help
@@ -46,6 +46,9 @@ config GDBSTUB
          RAM to avoid excessive linking time. This is only useful for kernel
          hackers. If unsure, say N.
 
+         This is deprecated in favour of KGDB and will be removed in a later
+         version.
+
 config GDBSTUB_IMMEDIATE
        bool "Break into GDB stub immediately"
        depends on GDBSTUB
@@ -54,6 +57,14 @@ config GDBSTUB_IMMEDIATE
          possible, leaving the program counter at the beginning of
          start_kernel() in init/main.c.
 
+config GDBSTUB_ALLOW_SINGLE_STEP
+       bool "Allow software single-stepping in GDB stub"
+       depends on GDBSTUB && !SMP && !PREEMPT
+       help
+         Allow GDB stub to perform software single-stepping through the
+         kernel.  This doesn't work very well on SMP or preemptible kernels as
+         it uses temporary breakpoints to emulate single-stepping.
+
 config GDB_CONSOLE
        bool "Console output to GDB"
        depends on GDBSTUB
@@ -142,3 +153,7 @@ config GDBSTUB_ON_TTYSx
        default y
 
 endmenu
+
+config KERNEL_DEBUGGER
+       def_bool y
+       depends on GDBSTUB || KGDB
diff --git a/arch/mn10300/include/asm/debugger.h b/arch/mn10300/include/asm/debugger.h
new file mode 100644 (file)
index 0000000..e1d3b08
--- /dev/null
@@ -0,0 +1,43 @@
+/* Kernel debugger for MN10300
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _ASM_DEBUGGER_H
+#define _ASM_DEBUGGER_H
+
+#if defined(CONFIG_KERNEL_DEBUGGER)
+
+extern int debugger_intercept(enum exception_code, int, int, struct pt_regs *);
+extern int at_debugger_breakpoint(struct pt_regs *);
+
+#ifndef CONFIG_MN10300_DEBUGGER_CACHE_NO_FLUSH
+extern void debugger_local_cache_flushinv(void);
+extern void debugger_local_cache_flushinv_one(u8 *);
+#else
+static inline void debugger_local_cache_flushinv(void) {}
+static inline void debugger_local_cache_flushinv_one(u8 *addr) {}
+#endif
+
+#else /* CONFIG_KERNEL_DEBUGGER */
+
+static inline int debugger_intercept(enum exception_code excep,
+                                    int signo, int si_code,
+                                    struct pt_regs *regs)
+{
+       return 0;
+}
+
+static inline int at_debugger_breakpoint(struct pt_regs *regs)
+{
+       return 0;
+}
+
+#endif /* CONFIG_KERNEL_DEBUGGER */
+#endif /* _ASM_DEBUGGER_H */
index 34dcb8e68309e0331b12d5ce2051a05da04e4c3f..503efab2a516988bbb024666227cb6c279c0b9f7 100644 (file)
 
 extern void ____unhandled_size_in_do_div___(void);
 
+/*
+ * Beginning with gcc 4.6, the MDR register is represented explicitly.  We
+ * must, therefore, at least explicitly clobber the register when we make
+ * changes to it.  The following assembly fragments *could* be rearranged in
+ * order to leave the moves to/from the MDR register to the compiler, but the
+ * gains would be minimal at best.
+ */
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+# define CLOBBER_MDR_CC                "mdr", "cc"
+#else
+# define CLOBBER_MDR_CC                "cc"
+#endif
+
 /*
  * divide n by base, leaving the result in n and returning the remainder
  * - we can do this quite efficiently on the MN10300 by cascading the divides
@@ -29,7 +42,7 @@ extern void ____unhandled_size_in_do_div___(void);
                    "mov        mdr,%1  \n"                             \
                    : "+r"(n), "=d"(__rem)                              \
                    : "r"(base), "1"(__rem)                             \
-                   : "cc"                                              \
+                   : CLOBBER_MDR_CC                                    \
                    );                                                  \
        } else if (sizeof(n) <= 8) {                                    \
                union {                                                 \
@@ -48,7 +61,7 @@ extern void ____unhandled_size_in_do_div___(void);
                    : "=d"(__rem), "=r"(__quot.w[1]), "=r"(__quot.w[0]) \
                    : "r"(base), "0"(__rem), "1"(__quot.w[1]),          \
                      "2"(__quot.w[0])                                  \
-                   : "cc"                                              \
+                   : CLOBBER_MDR_CC                                    \
                    );                                                  \
                n = __quot.l;                                           \
        } else {                                                        \
@@ -72,7 +85,7 @@ unsigned __muldiv64u(unsigned val, unsigned mult, unsigned div)
                                         * MDR = MDR:val%div */
            : "=r"(result)
            : "0"(val), "ir"(mult), "r"(div)
-           : "cc"
+           : CLOBBER_MDR_CC
            );
 
        return result;
@@ -93,7 +106,7 @@ signed __muldiv64s(signed val, signed mult, signed div)
                                         * MDR = MDR:val%div */
            : "=r"(result)
            : "0"(val), "ir"(mult), "r"(div)
-           : "cc"
+           : CLOBBER_MDR_CC
            );
 
        return result;
index b7625de8eade6754edf14f5de0340d9a0aa76e57..738ff72659d52307cdbc6195f768db79843c4c19 100644 (file)
@@ -55,7 +55,6 @@ static inline void clear_using_fpu(struct task_struct *tsk)
 
 extern asmlinkage void fpu_kill_state(struct task_struct *);
 extern asmlinkage void fpu_exception(struct pt_regs *, enum exception_code);
-extern asmlinkage void fpu_invalid_op(struct pt_regs *, enum exception_code);
 extern asmlinkage void fpu_init_state(void);
 extern asmlinkage void fpu_save(struct fpu_state_struct *);
 extern int fpu_setup_sigcontext(struct fpucontext *buf);
@@ -113,7 +112,6 @@ static inline void flush_fpu(void)
 
 extern asmlinkage
 void unexpected_fpu_exception(struct pt_regs *, enum exception_code);
-#define fpu_invalid_op unexpected_fpu_exception
 #define fpu_exception unexpected_fpu_exception
 
 struct task_struct;
index 7a7ae12c7119e42f9c83943dec0ad14ab9dfa7a7..678f68d5f37bb7ef819ab900302ff9e14c61705d 100644 (file)
@@ -20,7 +20,7 @@
 /*
  * interrupt control
  * - "disabled": run in IM1/2
- *   - level 0 - GDB stub
+ *   - level 0 - kernel debugger
  *   - level 1 - virtual serial DMA (if present)
  *   - level 5 - normal interrupt priority
  *   - level 6 - timer interrupt
diff --git a/arch/mn10300/include/asm/kgdb.h b/arch/mn10300/include/asm/kgdb.h
new file mode 100644 (file)
index 0000000..eb245f1
--- /dev/null
@@ -0,0 +1,81 @@
+/* Kernel debugger for MN10300
+ *
+ * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _ASM_KGDB_H
+#define _ASM_KGDB_H
+
+/*
+ * BUFMAX defines the maximum number of characters in inbound/outbound
+ * buffers at least NUMREGBYTES*2 are needed for register packets
+ * Longer buffer is needed to list all threads
+ */
+#define BUFMAX                 1024
+
+/*
+ * Note that this register image is in a different order than the register
+ * image that Linux produces at interrupt time.
+ */
+enum regnames {
+       GDB_FR_D0               = 0,
+       GDB_FR_D1               = 1,
+       GDB_FR_D2               = 2,
+       GDB_FR_D3               = 3,
+       GDB_FR_A0               = 4,
+       GDB_FR_A1               = 5,
+       GDB_FR_A2               = 6,
+       GDB_FR_A3               = 7,
+
+       GDB_FR_SP               = 8,
+       GDB_FR_PC               = 9,
+       GDB_FR_MDR              = 10,
+       GDB_FR_EPSW             = 11,
+       GDB_FR_LIR              = 12,
+       GDB_FR_LAR              = 13,
+       GDB_FR_MDRQ             = 14,
+
+       GDB_FR_E0               = 15,
+       GDB_FR_E1               = 16,
+       GDB_FR_E2               = 17,
+       GDB_FR_E3               = 18,
+       GDB_FR_E4               = 19,
+       GDB_FR_E5               = 20,
+       GDB_FR_E6               = 21,
+       GDB_FR_E7               = 22,
+
+       GDB_FR_SSP              = 23,
+       GDB_FR_MSP              = 24,
+       GDB_FR_USP              = 25,
+       GDB_FR_MCRH             = 26,
+       GDB_FR_MCRL             = 27,
+       GDB_FR_MCVF             = 28,
+
+       GDB_FR_FPCR             = 29,
+       GDB_FR_DUMMY0           = 30,
+       GDB_FR_DUMMY1           = 31,
+
+       GDB_FR_FS0              = 32,
+
+       GDB_FR_SIZE             = 64,
+};
+
+#define GDB_ORIG_D0            41
+#define NUMREGBYTES            (GDB_FR_SIZE*4)
+
+static inline void arch_kgdb_breakpoint(void)
+{
+       asm(".globl __arch_kgdb_breakpoint; __arch_kgdb_breakpoint: break");
+}
+extern u8 __arch_kgdb_breakpoint;
+
+#define BREAK_INSTR_SIZE       1
+#define CACHE_FLUSH_IS_SAFE    1
+
+#endif /* _ASM_KGDB_H */
index a3930e43a958d1e4077c028afd94b122963032cb..6745dbe649441d906cc76de00df7c496961ba1d7 100644 (file)
@@ -34,7 +34,7 @@
 #define LOCAL_TIMER_IPI                193
 #define FLUSH_CACHE_IPI                194
 #define CALL_FUNCTION_NMI_IPI  195
-#define GDB_NMI_IPI            196
+#define DEBUGGER_NMI_IPI       196
 
 #define SMP_BOOT_IRQ           195
 
@@ -43,6 +43,7 @@
 #define LOCAL_TIMER_GxICR_LV   GxICR_LEVEL_4
 #define FLUSH_CACHE_GxICR_LV   GxICR_LEVEL_0
 #define SMP_BOOT_GxICR_LV      GxICR_LEVEL_0
+#define DEBUGGER_GxICR_LV      CONFIG_DEBUGGER_IRQ_LEVEL
 
 #define TIME_OUT_COUNT_BOOT_IPI        100
 #define DELAY_TIME_BOOT_IPI    75000
@@ -61,8 +62,9 @@
  * An alternate way of dealing with this could be to use the EPSW.S bits to
  * cache this information for systems with up to four CPUs.
  */
+#define arch_smp_processor_id()        (CPUID)
 #if 0
-#define raw_smp_processor_id() (CPUID)
+#define raw_smp_processor_id() (arch_smp_processor_id())
 #else
 #define raw_smp_processor_id() (current_thread_info()->cpu)
 #endif
index 8d53f09c878d6592e9b7eeeffc3430a457783293..87c213002d4c9b30834b88aff215262160193541 100644 (file)
@@ -131,7 +131,11 @@ static inline unsigned long current_stack_pointer(void)
                kmalloc_node(THREAD_SIZE, GFP_KERNEL, node)
 #endif
 
+#ifndef CONFIG_KGDB
 #define free_thread_info(ti)   kfree((ti))
+#else
+extern void free_thread_info(struct thread_info *);
+#endif
 #define get_thread_info(ti)    get_task_struct((ti)->task)
 #define put_thread_info(ti)    put_task_struct((ti)->task)
 
index a06a2e10051d52b7a53e1227826325427cff9c0d..47ed30fe8178c45c5d99f3ec490feb41770e7c5c 100644 (file)
@@ -21,11 +21,8 @@ obj-$(CONFIG_GDBSTUB) += gdb-stub.o gdb-low.o
 obj-$(CONFIG_GDBSTUB_ON_TTYSx) += gdb-io-serial.o gdb-io-serial-low.o
 obj-$(CONFIG_GDBSTUB_ON_TTYSMx) += gdb-io-ttysm.o gdb-io-ttysm-low.o
 
-ifeq ($(CONFIG_MN10300_CACHE_ENABLED),y)
-obj-$(CONFIG_GDBSTUB) += gdb-cache.o
-endif
-
 obj-$(CONFIG_MN10300_RTC) += rtc.o
 obj-$(CONFIG_PROFILE) += profile.o profile-low.o
 obj-$(CONFIG_MODULES) += module.o
 obj-$(CONFIG_KPROBES) += kprobes.o
+obj-$(CONFIG_KGDB) += kgdb.o
index f00b9bafcd3ebb7aa8d53e0e8db61aedf15652e2..fb93ad720b82665cb5abbf1add396fdfd2e53ec4 100644 (file)
@@ -266,7 +266,11 @@ ENTRY(raw_bus_error)
 
 ###############################################################################
 #
-# Miscellaneous exception entry points
+# NMI exception entry points
+#
+# This is used by ordinary interrupt channels that have the GxICR_NMI bit set
+# in addition to the main NMI and Watchdog channels.  SMP NMI IPIs use this
+# facility.
 #
 ###############################################################################
 ENTRY(nmi_handler)
@@ -281,7 +285,7 @@ ENTRY(nmi_handler)
        and     NMIAGR_GN,d0
        lsr     0x2,d0
        cmp     CALL_FUNCTION_NMI_IPI,d0
-       bne     5f                      # if not call function, jump
+       bne     nmi_not_smp_callfunc    # if not call function, jump
 
        # function call nmi ipi
        add     4,sp                    # no need to store TBR
@@ -295,59 +299,38 @@ ENTRY(nmi_handler)
        call    smp_nmi_call_function_interrupt[],0
        RESTORE_ALL
 
-5:
-#ifdef CONFIG_GDBSTUB
-       cmp     GDB_NMI_IPI,d0
-       bne     3f                      # if not gdb nmi ipi, jump
+nmi_not_smp_callfunc:
+#ifdef CONFIG_KERNEL_DEBUGGER
+       cmp     DEBUGGER_NMI_IPI,d0
+       bne     nmi_not_debugger        # if not kernel debugger NMI IPI, jump
 
-       # gdb nmi ipi
+       # kernel debugger NMI IPI
        add     4,sp                    # no need to store TBR
        mov     GxICR_DETECT,d0         # clear NMI
-       movbu   d0,(GxICR(GDB_NMI_IPI))
-       movhu   (GxICR(GDB_NMI_IPI)),d0
+       movbu   d0,(GxICR(DEBUGGER_NMI_IPI))
+       movhu   (GxICR(DEBUGGER_NMI_IPI)),d0
        and     ~EPSW_NMID,epsw         # enable NMI
-#ifdef CONFIG_MN10300_CACHE_ENABLED
-       mov     (gdbstub_nmi_opr_type),d0
-       cmp     GDBSTUB_NMI_CACHE_PURGE,d0
-       bne     4f                      # if not gdb cache purge, jump
-
-       # gdb cache purge nmi ipi
-       add     -20,sp
-       mov     d1,(4,sp)
-       mov     a0,(8,sp)
-       mov     a1,(12,sp)
-       mov     mdr,d0
-       mov     d0,(16,sp)
-       call    gdbstub_local_purge_cache[],0
-       mov     0x1,d0
-       mov     (CPUID),d1
-       asl     d1,d0
-       mov     gdbstub_nmi_cpumask,a0
-       bclr    d0,(a0)
-       mov     (4,sp),d1
-       mov     (8,sp),a0
-       mov     (12,sp),a1
-       mov     (16,sp),d0
-       mov     d0,mdr
-       add     20,sp
-       mov     (sp),d0
-       add     4,sp
-       rti
-4:
-#endif /* CONFIG_MN10300_CACHE_ENABLED */
-       # gdb wait nmi ipi
+
        mov     (sp),d0
        SAVE_ALL
-       call    gdbstub_nmi_wait[],0
+       mov     fp,d0                   # arg 0: stacked register file
+       mov     a2,d1                   # arg 1: exception number
+       call    debugger_nmi_interrupt[],0
        RESTORE_ALL
-3:
-#endif /* CONFIG_GDBSTUB */
+
+nmi_not_debugger:
+#endif /* CONFIG_KERNEL_DEBUGGER */
        mov     (sp),d0                 # restore TBR to d0
        add     4,sp
 #endif /* CONFIG_SMP */
 
        bra     __common_exception_nonmi
 
+###############################################################################
+#
+# General exception entry point
+#
+###############################################################################
 ENTRY(__common_exception)
        add     -4,sp
        mov     d0,(sp)
index 5f9c3fa19a85fb4459987789229387352cf1a809..bb5fa7df6c4425f5ea856fb5625dd8d785f78493 100644 (file)
@@ -69,24 +69,6 @@ asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code)
        force_sig_info(SIGFPE, &info, tsk);
 }
 
-/*
- * handle an FPU invalid_op exception
- * - Derived from DO_EINFO() macro in arch/mn10300/kernel/traps.c
- */
-asmlinkage void fpu_invalid_op(struct pt_regs *regs, enum exception_code code)
-{
-       siginfo_t info;
-
-       if (!user_mode(regs))
-               die_if_no_fixup("FPU invalid opcode", regs, code);
-
-       info.si_signo = SIGILL;
-       info.si_errno = 0;
-       info.si_code = ILL_COPROC;
-       info.si_addr = (void *) regs->pc;
-       force_sig_info(info.si_signo, &info, current);
-}
-
 /*
  * save the FPU state to a signal context
  */
diff --git a/arch/mn10300/kernel/gdb-cache.S b/arch/mn10300/kernel/gdb-cache.S
deleted file mode 100644 (file)
index 1108bad..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-###############################################################################
-#
-# MN10300 Low-level cache purging routines for gdbstub
-#
-# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
-# Written by David Howells (dhowells@redhat.com)
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public Licence
-# as published by the Free Software Foundation; either version
-# 2 of the Licence, or (at your option) any later version.
-#
-###############################################################################
-#include <linux/sys.h>
-#include <linux/linkage.h>
-#include <asm/smp.h>
-#include <asm/cache.h>
-#include <asm/cpu-regs.h>
-#include <asm/exceptions.h>
-#include <asm/frame.inc>
-#include <asm/serial-regs.h>
-
-       .text
-
-###############################################################################
-#
-# GDB stub cache purge
-#
-###############################################################################
-       .type   gdbstub_purge_cache,@function
-ENTRY(gdbstub_purge_cache)
-       #######################################################################
-       # read the addresses tagged in the cache's tag RAM and attempt to flush
-       # those addresses specifically
-       # - we rely on the hardware to filter out invalid tag entry addresses
-       mov     DCACHE_TAG(0,0),a0              # dcache tag RAM access address
-       mov     DCACHE_PURGE(0,0),a1            # dcache purge request address
-       mov     L1_CACHE_NWAYS*L1_CACHE_NENTRIES,d1  # total number of entries
-
-mn10300_dcache_flush_loop:
-       mov     (a0),d0
-       and     L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0
-       or      L1_CACHE_TAG_VALID,d0           # retain valid entries in the
-                                               # cache
-       mov     d0,(a1)                         # conditional purge
-
-mn10300_dcache_flush_skip:
-       add     L1_CACHE_BYTES,a0
-       add     L1_CACHE_BYTES,a1
-       add     -1,d1
-       bne     mn10300_dcache_flush_loop
-
-;;     # unconditionally flush and invalidate the dcache
-;;     mov     DCACHE_PURGE(0,0),a1            # dcache purge request address
-;;     mov     L1_CACHE_NWAYS*L1_CACHE_NENTRIES,d1     # total number of
-;;                                                     # entries
-;;
-;; gdbstub_purge_cache__dcache_loop:
-;;     mov     (a1),d0                         # unconditional purge
-;;
-;;     add     L1_CACHE_BYTES,a1
-;;     add     -1,d1
-;;     bne     gdbstub_purge_cache__dcache_loop
-
-       #######################################################################
-       # now invalidate the icache
-       mov     CHCTR,a0
-       movhu   (a0),a1
-
-       mov     epsw,d1
-       and     ~EPSW_IE,epsw
-       nop
-       nop
-
-       # disable the icache
-       and     ~CHCTR_ICEN,d0
-       movhu   d0,(a0)
-
-       # and wait for it to calm down
-       setlb
-       movhu   (a0),d0
-       btst    CHCTR_ICBUSY,d0
-       lne
-
-       # invalidate
-       or      CHCTR_ICINV,d0
-       movhu   d0,(a0)
-
-       # wait for the cache to finish
-       mov     CHCTR,a0
-       setlb
-       movhu   (a0),d0
-       btst    CHCTR_ICBUSY,d0
-       lne
-
-       # and reenable it
-       movhu   a1,(a0)
-       movhu   (a0),d0                 # read back to flush
-                                       # (SIGILLs all over without this)
-
-       mov     d1,epsw
-
-       ret     [],0
-
-       .size   gdbstub_purge_cache,.-gdbstub_purge_cache
index abdeea153c89643184df8f7b1c13805a504b0b7a..c859cacbb9c3bfc94008f251430b038c3744ffac 100644 (file)
@@ -59,10 +59,10 @@ void __init gdbstub_io_init(void)
 
        /* we want to get serial receive interrupts */
        set_intr_level(gdbstub_port->rx_irq,
-               NUM2GxICR_LEVEL(CONFIG_GDBSTUB_IRQ_LEVEL));
+               NUM2GxICR_LEVEL(CONFIG_DEBUGGER_IRQ_LEVEL));
        set_intr_level(gdbstub_port->tx_irq,
-               NUM2GxICR_LEVEL(CONFIG_GDBSTUB_IRQ_LEVEL));
-       set_intr_stub(NUM2EXCEP_IRQ_LEVEL(CONFIG_GDBSTUB_IRQ_LEVEL),
+               NUM2GxICR_LEVEL(CONFIG_DEBUGGER_IRQ_LEVEL));
+       set_intr_stub(NUM2EXCEP_IRQ_LEVEL(CONFIG_DEBUGGER_IRQ_LEVEL),
                gdbstub_io_rx_handler);
 
        *gdbstub_port->rx_icr |= GxICR_ENABLE;
@@ -88,7 +88,7 @@ void __init gdbstub_io_init(void)
 
        /* permit level 0 IRQs only */
        arch_local_change_intr_mask_level(
-               NUM2EPSW_IM(CONFIG_GDBSTUB_IRQ_LEVEL + 1));
+               NUM2EPSW_IM(CONFIG_DEBUGGER_IRQ_LEVEL + 1));
 }
 
 /*
index b169d99d9f20e8fd4b5da5ea353b70f02819844b..538266b2c9bc6cd0d87e9886d4f2509e61847664 100644 (file)
 #include <asm/system.h>
 #include <asm/gdb-stub.h>
 #include <asm/exceptions.h>
-#include <asm/cacheflush.h>
+#include <asm/debugger.h>
 #include <asm/serial-regs.h>
 #include <asm/busctl-regs.h>
 #include <unit/leds.h>
@@ -405,6 +405,7 @@ static int hexToInt(char **ptr, int *intValue)
        return (numChars);
 }
 
+#ifdef CONFIG_GDBSTUB_ALLOW_SINGLE_STEP
 /*
  * We single-step by setting breakpoints. When an exception
  * is handled, we need to restore the instructions hoisted
@@ -729,6 +730,7 @@ static int gdbstub_single_step(struct pt_regs *regs)
        __gdbstub_restore_bp();
        return -EFAULT;
 }
+#endif /* CONFIG_GDBSTUB_ALLOW_SINGLE_STEP */
 
 #ifdef CONFIG_GDBSTUB_CONSOLE
 
@@ -1171,7 +1173,7 @@ int gdbstub_clear_breakpoint(u8 *addr, int len)
 
 /*
  * This function does all command processing for interfacing to gdb
- * - returns 1 if the exception should be skipped, 0 otherwise.
+ * - returns 0 if the exception should be skipped, -ERROR otherwise.
  */
 static int gdbstub(struct pt_regs *regs, enum exception_code excep)
 {
@@ -1186,7 +1188,7 @@ static int gdbstub(struct pt_regs *regs, enum exception_code excep)
        int loop;
 
        if (excep == EXCEP_FPU_DISABLED)
-               return 0;
+               return -ENOTSUPP;
 
        gdbstub_flush_caches = 0;
 
@@ -1195,7 +1197,7 @@ static int gdbstub(struct pt_regs *regs, enum exception_code excep)
        asm volatile("mov mdr,%0" : "=d"(mdr));
        local_save_flags(epsw);
        arch_local_change_intr_mask_level(
-               NUM2EPSW_IM(CONFIG_GDBSTUB_IRQ_LEVEL + 1));
+               NUM2EPSW_IM(CONFIG_DEBUGGER_IRQ_LEVEL + 1));
 
        gdbstub_store_fpu();
 
@@ -1208,11 +1210,13 @@ static int gdbstub(struct pt_regs *regs, enum exception_code excep)
        /* if we were single stepping, restore the opcodes hoisted for the
         * breakpoint[s] */
        broke = 0;
+#ifdef CONFIG_GDBSTUB_ALLOW_SINGLE_STEP
        if ((step_bp[0].addr && step_bp[0].addr == (u8 *) regs->pc) ||
            (step_bp[1].addr && step_bp[1].addr == (u8 *) regs->pc))
                broke = 1;
 
        __gdbstub_restore_bp();
+#endif
 
        if (gdbstub_rx_unget) {
                sigval = SIGINT;
@@ -1548,17 +1552,21 @@ packet_waiting:
                         * Step to next instruction
                         */
                case 's':
-                       /*
-                        * using the T flag doesn't seem to perform single
+                       /* Using the T flag doesn't seem to perform single
                         * stepping (it seems to wind up being caught by the
                         * JTAG unit), so we have to use breakpoints and
                         * continue instead.
                         */
+#ifdef CONFIG_GDBSTUB_ALLOW_SINGLE_STEP
                        if (gdbstub_single_step(regs) < 0)
                                /* ignore any fault error for now */
                                gdbstub_printk("unable to set single-step"
                                               " bp\n");
                        goto done;
+#else
+                       gdbstub_strcpy(output_buffer, "E01");
+                       break;
+#endif
 
                        /*
                         * Set baud rate (bBB)
@@ -1657,7 +1665,7 @@ done:
         * NB: We flush both caches, just to be sure...
         */
        if (gdbstub_flush_caches)
-               gdbstub_purge_cache();
+               debugger_local_cache_flushinv();
 
        gdbstub_load_fpu();
        mn10300_set_gdbleds(0);
@@ -1667,14 +1675,23 @@ done:
        touch_softlockup_watchdog();
 
        local_irq_restore(epsw);
-       return 1;
+       return 0;
+}
+
+/*
+ * Determine if we hit a debugger special breakpoint that needs skipping over
+ * automatically.
+ */
+int at_debugger_breakpoint(struct pt_regs *regs)
+{
+       return 0;
 }
 
 /*
  * handle event interception
  */
-asmlinkage int gdbstub_intercept(struct pt_regs *regs,
-                                enum exception_code excep)
+asmlinkage int debugger_intercept(enum exception_code excep,
+                                 int signo, int si_code, struct pt_regs *regs)
 {
        static u8 notfirst = 1;
        int ret;
@@ -1688,7 +1705,7 @@ asmlinkage int gdbstub_intercept(struct pt_regs *regs,
                asm("mov mdr,%0" : "=d"(mdr));
 
                gdbstub_entry(
-                       "--> gdbstub_intercept(%p,%04x) [MDR=%lx PC=%lx]\n",
+                       "--> debugger_intercept(%p,%04x) [MDR=%lx PC=%lx]\n",
                        regs, excep, mdr, regs->pc);
 
                gdbstub_entry(
@@ -1722,7 +1739,7 @@ asmlinkage int gdbstub_intercept(struct pt_regs *regs,
 
        ret = gdbstub(regs, excep);
 
-       gdbstub_entry("<-- gdbstub_intercept()\n");
+       gdbstub_entry("<-- debugger_intercept()\n");
        gdbstub_busy = 0;
        return ret;
 }
index ea946613f46d12fb8f57cea490428e740f03015c..a5ac755dd69f4acbc8c6d213c47c285b8af41098 100644 (file)
@@ -29,6 +29,13 @@ extern void ret_from_fork(struct task_struct *) __attribute__((noreturn));
 extern void mn10300_low_ipi_handler(void);
 #endif
 
+/*
+ * smp.c
+ */
+#ifdef CONFIG_SMP
+extern void smp_jump_to_debugger(void);
+#endif
+
 /*
  * time.c
  */
index f09fed5e6afc21a1f1349cafef258e6cdebed57c..5f7fc3eb45e60807c58a4888fce1bc5ca24e83ce 100644 (file)
@@ -153,7 +153,7 @@ mn10300_cpupic_setaffinity(struct irq_data *d, const struct cpumask *mask,
        case LOCAL_TIMER_IPI:
        case FLUSH_CACHE_IPI:
        case CALL_FUNCTION_NMI_IPI:
-       case GDB_NMI_IPI:
+       case DEBUGGER_NMI_IPI:
 #ifdef CONFIG_MN10300_TTYSM0
        case SC0RXIRQ:
        case SC0TXIRQ:
diff --git a/arch/mn10300/kernel/kgdb.c b/arch/mn10300/kernel/kgdb.c
new file mode 100644 (file)
index 0000000..f6c981d
--- /dev/null
@@ -0,0 +1,502 @@
+/* kgdb support for MN10300
+ *
+ * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/slab.h>
+#include <linux/ptrace.h>
+#include <linux/kgdb.h>
+#include <linux/uaccess.h>
+#include <unit/leds.h>
+#include <unit/serial.h>
+#include <asm/debugger.h>
+#include <asm/serial-regs.h>
+#include "internal.h"
+
+/*
+ * Software single-stepping breakpoint save (used by __switch_to())
+ */
+static struct thread_info *kgdb_sstep_thread;
+u8 *kgdb_sstep_bp_addr[2];
+u8 kgdb_sstep_bp[2];
+
+/*
+ * Copy kernel exception frame registers to the GDB register file
+ */
+void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+       unsigned long ssp = (unsigned long) (regs + 1);
+
+       gdb_regs[GDB_FR_D0]     = regs->d0;
+       gdb_regs[GDB_FR_D1]     = regs->d1;
+       gdb_regs[GDB_FR_D2]     = regs->d2;
+       gdb_regs[GDB_FR_D3]     = regs->d3;
+       gdb_regs[GDB_FR_A0]     = regs->a0;
+       gdb_regs[GDB_FR_A1]     = regs->a1;
+       gdb_regs[GDB_FR_A2]     = regs->a2;
+       gdb_regs[GDB_FR_A3]     = regs->a3;
+       gdb_regs[GDB_FR_SP]     = (regs->epsw & EPSW_nSL) ? regs->sp : ssp;
+       gdb_regs[GDB_FR_PC]     = regs->pc;
+       gdb_regs[GDB_FR_MDR]    = regs->mdr;
+       gdb_regs[GDB_FR_EPSW]   = regs->epsw;
+       gdb_regs[GDB_FR_LIR]    = regs->lir;
+       gdb_regs[GDB_FR_LAR]    = regs->lar;
+       gdb_regs[GDB_FR_MDRQ]   = regs->mdrq;
+       gdb_regs[GDB_FR_E0]     = regs->e0;
+       gdb_regs[GDB_FR_E1]     = regs->e1;
+       gdb_regs[GDB_FR_E2]     = regs->e2;
+       gdb_regs[GDB_FR_E3]     = regs->e3;
+       gdb_regs[GDB_FR_E4]     = regs->e4;
+       gdb_regs[GDB_FR_E5]     = regs->e5;
+       gdb_regs[GDB_FR_E6]     = regs->e6;
+       gdb_regs[GDB_FR_E7]     = regs->e7;
+       gdb_regs[GDB_FR_SSP]    = ssp;
+       gdb_regs[GDB_FR_MSP]    = 0;
+       gdb_regs[GDB_FR_USP]    = regs->sp;
+       gdb_regs[GDB_FR_MCRH]   = regs->mcrh;
+       gdb_regs[GDB_FR_MCRL]   = regs->mcrl;
+       gdb_regs[GDB_FR_MCVF]   = regs->mcvf;
+       gdb_regs[GDB_FR_DUMMY0] = 0;
+       gdb_regs[GDB_FR_DUMMY1] = 0;
+       gdb_regs[GDB_FR_FS0]    = 0;
+}
+
+/*
+ * Extracts kernel SP/PC values understandable by gdb from the values
+ * saved by switch_to().
+ */
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+       gdb_regs[GDB_FR_SSP]    = p->thread.sp;
+       gdb_regs[GDB_FR_PC]     = p->thread.pc;
+       gdb_regs[GDB_FR_A3]     = p->thread.a3;
+       gdb_regs[GDB_FR_USP]    = p->thread.usp;
+       gdb_regs[GDB_FR_FPCR]   = p->thread.fpu_state.fpcr;
+}
+
+/*
+ * Fill kernel exception frame registers from the GDB register file
+ */
+void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+       regs->d0        = gdb_regs[GDB_FR_D0];
+       regs->d1        = gdb_regs[GDB_FR_D1];
+       regs->d2        = gdb_regs[GDB_FR_D2];
+       regs->d3        = gdb_regs[GDB_FR_D3];
+       regs->a0        = gdb_regs[GDB_FR_A0];
+       regs->a1        = gdb_regs[GDB_FR_A1];
+       regs->a2        = gdb_regs[GDB_FR_A2];
+       regs->a3        = gdb_regs[GDB_FR_A3];
+       regs->sp        = gdb_regs[GDB_FR_SP];
+       regs->pc        = gdb_regs[GDB_FR_PC];
+       regs->mdr       = gdb_regs[GDB_FR_MDR];
+       regs->epsw      = gdb_regs[GDB_FR_EPSW];
+       regs->lir       = gdb_regs[GDB_FR_LIR];
+       regs->lar       = gdb_regs[GDB_FR_LAR];
+       regs->mdrq      = gdb_regs[GDB_FR_MDRQ];
+       regs->e0        = gdb_regs[GDB_FR_E0];
+       regs->e1        = gdb_regs[GDB_FR_E1];
+       regs->e2        = gdb_regs[GDB_FR_E2];
+       regs->e3        = gdb_regs[GDB_FR_E3];
+       regs->e4        = gdb_regs[GDB_FR_E4];
+       regs->e5        = gdb_regs[GDB_FR_E5];
+       regs->e6        = gdb_regs[GDB_FR_E6];
+       regs->e7        = gdb_regs[GDB_FR_E7];
+       regs->sp        = gdb_regs[GDB_FR_SSP];
+       /* gdb_regs[GDB_FR_MSP]; */
+       // regs->usp    = gdb_regs[GDB_FR_USP];
+       regs->mcrh      = gdb_regs[GDB_FR_MCRH];
+       regs->mcrl      = gdb_regs[GDB_FR_MCRL];
+       regs->mcvf      = gdb_regs[GDB_FR_MCVF];
+       /* gdb_regs[GDB_FR_DUMMY0]; */
+       /* gdb_regs[GDB_FR_DUMMY1]; */
+
+       // regs->fpcr   = gdb_regs[GDB_FR_FPCR];
+       // regs->fs0    = gdb_regs[GDB_FR_FS0];
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+       .gdb_bpt_instr  = { 0xff },
+       .flags          = KGDB_HW_BREAKPOINT,
+};
+
+static const unsigned char mn10300_kgdb_insn_sizes[256] =
+{
+       /* 1  2  3  4  5  6  7  8  9  a  b  c  d  e  f */
+       1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, /* 0 */
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 1 */
+       2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, /* 2 */
+       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, /* 3 */
+       1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, /* 4 */
+       1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, /* 5 */
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */
+       2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 8 */
+       2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 9 */
+       2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* a */
+       2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* b */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, /* c */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */
+       0, 2, 2, 2, 2, 2, 2, 4, 0, 3, 0, 4, 0, 6, 7, 1  /* f */
+};
+
+/*
+ * Attempt to emulate single stepping by means of breakpoint instructions.
+ * Although there is a single-step trace flag in EPSW, its use is not
+ * sufficiently documented and is only intended for use with the JTAG debugger.
+ */
+static int kgdb_arch_do_singlestep(struct pt_regs *regs)
+{
+       unsigned long arg;
+       unsigned size;
+       u8 *pc = (u8 *)regs->pc, *sp = (u8 *)(regs + 1), cur;
+       u8 *x = NULL, *y = NULL;
+       int ret;
+
+       ret = probe_kernel_read(&cur, pc, 1);
+       if (ret < 0)
+               return ret;
+
+       size = mn10300_kgdb_insn_sizes[cur];
+       if (size > 0) {
+               x = pc + size;
+               goto set_x;
+       }
+
+       switch (cur) {
+               /* Bxx (d8,PC) */
+       case 0xc0 ... 0xca:
+               ret = probe_kernel_read(&arg, pc + 1, 1);
+               if (ret < 0)
+                       return ret;
+               x = pc + 2;
+               if (arg >= 0 && arg <= 2)
+                       goto set_x;
+               y = pc + (s8)arg;
+               goto set_x_and_y;
+
+               /* LXX (d8,PC) */
+       case 0xd0 ... 0xda:
+               x = pc + 1;
+               if (regs->pc == regs->lar)
+                       goto set_x;
+               y = (u8 *)regs->lar;
+               goto set_x_and_y;
+
+               /* SETLB - loads the next four bytes into the LIR register
+                * (which mustn't include a breakpoint instruction) */
+       case 0xdb:
+               x = pc + 5;
+               goto set_x;
+
+               /* JMP (d16,PC) or CALL (d16,PC) */
+       case 0xcc:
+       case 0xcd:
+               ret = probe_kernel_read(&arg, pc + 1, 2);
+               if (ret < 0)
+                       return ret;
+               x = pc + (s16)arg;
+               goto set_x;
+
+               /* JMP (d32,PC) or CALL (d32,PC) */
+       case 0xdc:
+       case 0xdd:
+               ret = probe_kernel_read(&arg, pc + 1, 4);
+               if (ret < 0)
+                       return ret;
+               x = pc + (s32)arg;
+               goto set_x;
+
+               /* RETF */
+       case 0xde:
+               x = (u8 *)regs->mdr;
+               goto set_x;
+
+               /* RET */
+       case 0xdf:
+               ret = probe_kernel_read(&arg, pc + 2, 1);
+               if (ret < 0)
+                       return ret;
+               ret = probe_kernel_read(&x, sp + (s8)arg, 4);
+               if (ret < 0)
+                       return ret;
+               goto set_x;
+
+       case 0xf0:
+               ret = probe_kernel_read(&cur, pc + 1, 1);
+               if (ret < 0)
+                       return ret;
+
+               if (cur >= 0xf0 && cur <= 0xf7) {
+                       /* JMP (An) / CALLS (An) */
+                       switch (cur & 3) {
+                       case 0: x = (u8 *)regs->a0; break;
+                       case 1: x = (u8 *)regs->a1; break;
+                       case 2: x = (u8 *)regs->a2; break;
+                       case 3: x = (u8 *)regs->a3; break;
+                       }
+                       goto set_x;
+               } else if (cur == 0xfc) {
+                       /* RETS */
+                       ret = probe_kernel_read(&x, sp, 4);
+                       if (ret < 0)
+                               return ret;
+                       goto set_x;
+               } else if (cur == 0xfd) {
+                       /* RTI */
+                       ret = probe_kernel_read(&x, sp + 4, 4);
+                       if (ret < 0)
+                               return ret;
+                       goto set_x;
+               } else {
+                       x = pc + 2;
+                       goto set_x;
+               }
+               break;
+
+               /* potential 3-byte conditional branches */
+       case 0xf8:
+               ret = probe_kernel_read(&cur, pc + 1, 1);
+               if (ret < 0)
+                       return ret;
+               x = pc + 3;
+
+               if (cur >= 0xe8 && cur <= 0xeb) {
+                       ret = probe_kernel_read(&arg, pc + 2, 1);
+                       if (ret < 0)
+                               return ret;
+                       if (arg >= 0 && arg <= 3)
+                               goto set_x;
+                       y = pc + (s8)arg;
+                       goto set_x_and_y;
+               }
+               goto set_x;
+
+       case 0xfa:
+               ret = probe_kernel_read(&cur, pc + 1, 1);
+               if (ret < 0)
+                       return ret;
+
+               if (cur == 0xff) {
+                       /* CALLS (d16,PC) */
+                       ret = probe_kernel_read(&arg, pc + 2, 2);
+                       if (ret < 0)
+                               return ret;
+                       x = pc + (s16)arg;
+                       goto set_x;
+               }
+
+               x = pc + 4;
+               goto set_x;
+
+       case 0xfc:
+               ret = probe_kernel_read(&cur, pc + 1, 1);
+               if (ret < 0)
+                       return ret;
+
+               if (cur == 0xff) {
+                       /* CALLS (d32,PC) */
+                       ret = probe_kernel_read(&arg, pc + 2, 4);
+                       if (ret < 0)
+                               return ret;
+                       x = pc + (s32)arg;
+                       goto set_x;
+               }
+
+               x = pc + 6;
+               goto set_x;
+       }
+
+       return 0;
+
+set_x:
+       kgdb_sstep_bp_addr[0] = x;
+       kgdb_sstep_bp_addr[1] = NULL;
+       ret = probe_kernel_read(&kgdb_sstep_bp[0], x, 1);
+       if (ret < 0)
+               return ret;
+       ret = probe_kernel_write(x, &arch_kgdb_ops.gdb_bpt_instr, 1);
+       if (ret < 0)
+               return ret;
+       kgdb_sstep_thread = current_thread_info();
+       debugger_local_cache_flushinv_one(x);
+       return ret;
+
+set_x_and_y:
+       kgdb_sstep_bp_addr[0] = x;
+       kgdb_sstep_bp_addr[1] = y;
+       ret = probe_kernel_read(&kgdb_sstep_bp[0], x, 1);
+       if (ret < 0)
+               return ret;
+       ret = probe_kernel_read(&kgdb_sstep_bp[1], y, 1);
+       if (ret < 0)
+               return ret;
+       ret = probe_kernel_write(x, &arch_kgdb_ops.gdb_bpt_instr, 1);
+       if (ret < 0)
+               return ret;
+       ret = probe_kernel_write(y, &arch_kgdb_ops.gdb_bpt_instr, 1);
+       if (ret < 0) {
+               probe_kernel_write(kgdb_sstep_bp_addr[0],
+                                  &kgdb_sstep_bp[0], 1);
+       } else {
+               kgdb_sstep_thread = current_thread_info();
+       }
+       debugger_local_cache_flushinv_one(x);
+       debugger_local_cache_flushinv_one(y);
+       return ret;
+}
+
+/*
+ * Remove emplaced single-step breakpoints, returning true if we hit one of
+ * them.
+ */
+static bool kgdb_arch_undo_singlestep(struct pt_regs *regs)
+{
+       bool hit = false;
+       u8 *x = kgdb_sstep_bp_addr[0], *y = kgdb_sstep_bp_addr[1];
+       u8 opcode;
+
+       if (kgdb_sstep_thread == current_thread_info()) {
+               if (x) {
+                       if (x == (u8 *)regs->pc)
+                               hit = true;
+                       if (probe_kernel_read(&opcode, x,
+                                             1) < 0 ||
+                           opcode != 0xff)
+                               BUG();
+                       probe_kernel_write(x, &kgdb_sstep_bp[0], 1);
+                       debugger_local_cache_flushinv_one(x);
+               }
+               if (y) {
+                       if (y == (u8 *)regs->pc)
+                               hit = true;
+                       if (probe_kernel_read(&opcode, y,
+                                             1) < 0 ||
+                           opcode != 0xff)
+                               BUG();
+                       probe_kernel_write(y, &kgdb_sstep_bp[1], 1);
+                       debugger_local_cache_flushinv_one(y);
+               }
+       }
+
+       kgdb_sstep_bp_addr[0] = NULL;
+       kgdb_sstep_bp_addr[1] = NULL;
+       kgdb_sstep_thread = NULL;
+       return hit;
+}
+
+/*
+ * Catch a single-step-pending thread being deleted and make sure the global
+ * single-step state is cleared.  At this point the breakpoints should have
+ * been removed by __switch_to().
+ */
+void free_thread_info(struct thread_info *ti)
+{
+       if (kgdb_sstep_thread == ti) {
+               kgdb_sstep_thread = NULL;
+
+               /* However, we may now be running in degraded mode, with most
+                * of the CPUs disabled until such a time as KGDB is reentered,
+                * so force immediate reentry */
+               kgdb_breakpoint();
+       }
+       kfree(ti);
+}
+
+/*
+ * Handle unknown packets and [CcsDk] packets
+ * - at this point breakpoints have been installed
+ */
+int kgdb_arch_handle_exception(int vector, int signo, int err_code,
+                              char *remcom_in_buffer, char *remcom_out_buffer,
+                              struct pt_regs *regs)
+{
+       long addr;
+       char *ptr;
+
+       switch (remcom_in_buffer[0]) {
+       case 'c':
+       case 's':
+               /* try to read optional parameter, pc unchanged if no parm */
+               ptr = &remcom_in_buffer[1];
+               if (kgdb_hex2long(&ptr, &addr))
+                       regs->pc = addr;
+       case 'D':
+       case 'k':
+               atomic_set(&kgdb_cpu_doing_single_step, -1);
+
+               if (remcom_in_buffer[0] == 's') {
+                       kgdb_arch_do_singlestep(regs);
+                       kgdb_single_step = 1;
+                       atomic_set(&kgdb_cpu_doing_single_step,
+                                  raw_smp_processor_id());
+               }
+               return 0;
+       }
+       return -1; /* this means that we do not want to exit from the handler */
+}
+
+/*
+ * Handle event interception
+ * - returns 0 if the exception should be skipped, -ERROR otherwise.
+ */
+int debugger_intercept(enum exception_code excep, int signo, int si_code,
+                      struct pt_regs *regs)
+{
+       int ret;
+
+       if (kgdb_arch_undo_singlestep(regs)) {
+               excep = EXCEP_TRAP;
+               signo = SIGTRAP;
+               si_code = TRAP_TRACE;
+       }
+
+       ret = kgdb_handle_exception(excep, signo, si_code, regs);
+
+       debugger_local_cache_flushinv();
+
+       return ret;
+}
+
+/*
+ * Determine if we've hit a debugger special breakpoint
+ */
+int at_debugger_breakpoint(struct pt_regs *regs)
+{
+       return regs->pc == (unsigned long)&__arch_kgdb_breakpoint;
+}
+
+/*
+ * Initialise kgdb
+ */
+int kgdb_arch_init(void)
+{
+       return 0;
+}
+
+/*
+ * Do something, perhaps, but don't know what.
+ */
+void kgdb_arch_exit(void)
+{
+}
+
+#ifdef CONFIG_SMP
+void debugger_nmi_interrupt(struct pt_regs *regs, enum exception_code code)
+{
+       kgdb_nmicallback(arch_smp_processor_id(), regs);
+       debugger_local_cache_flushinv();
+}
+
+void kgdb_roundup_cpus(unsigned long flags)
+{
+       smp_jump_to_debugger();
+}
+#endif
index 93c53739cfc94c58ba7d694af171dcfaf0f284df..efca426a2ed4e7241e4a8a74d7b29a49c29a2973 100644 (file)
@@ -119,6 +119,10 @@ static int mn10300_serial_request_port(struct uart_port *);
 static void mn10300_serial_config_port(struct uart_port *, int);
 static int mn10300_serial_verify_port(struct uart_port *,
                                        struct serial_struct *);
+#ifdef CONFIG_CONSOLE_POLL
+static void mn10300_serial_poll_put_char(struct uart_port *, unsigned char);
+static int mn10300_serial_poll_get_char(struct uart_port *);
+#endif
 
 static const struct uart_ops mn10300_serial_ops = {
        .tx_empty       = mn10300_serial_tx_empty,
@@ -138,6 +142,10 @@ static const struct uart_ops mn10300_serial_ops = {
        .request_port   = mn10300_serial_request_port,
        .config_port    = mn10300_serial_config_port,
        .verify_port    = mn10300_serial_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+       .poll_put_char  = mn10300_serial_poll_put_char,
+       .poll_get_char  = mn10300_serial_poll_get_char,
+#endif
 };
 
 static irqreturn_t mn10300_serial_interrupt(int irq, void *dev_id);
@@ -1634,3 +1642,70 @@ static int __init mn10300_serial_console_init(void)
 
 console_initcall(mn10300_serial_console_init);
 #endif
+
+#ifdef CONFIG_CONSOLE_POLL
+/*
+ * Polled character reception for the kernel debugger
+ */
+static int mn10300_serial_poll_get_char(struct uart_port *_port)
+{
+       struct mn10300_serial_port *port =
+               container_of(_port, struct mn10300_serial_port, uart);
+       unsigned ix;
+       u8 st, ch;
+
+       _enter("%s", port->name);
+
+       do {
+               /* pull chars out of the hat */
+               ix = port->rx_outp;
+               if (ix == port->rx_inp)
+                       return NO_POLL_CHAR;
+
+               ch = port->rx_buffer[ix++];
+               st = port->rx_buffer[ix++];
+               smp_rmb();
+               port->rx_outp = ix & (MNSC_BUFFER_SIZE - 1);
+
+       } while (st & (SC01STR_FEF | SC01STR_PEF | SC01STR_OEF));
+
+       return ch;
+}
+
+
+/*
+ * Polled character transmission for the kernel debugger
+ */
+static void mn10300_serial_poll_put_char(struct uart_port *_port,
+                                        unsigned char ch)
+{
+       struct mn10300_serial_port *port =
+               container_of(_port, struct mn10300_serial_port, uart);
+       u8 intr, tmp;
+
+       /* wait for the transmitter to finish anything it might be doing (and
+        * this includes the virtual DMA handler, so it might take a while) */
+       while (*port->_status & (SC01STR_TBF | SC01STR_TXF))
+               continue;
+
+       /* disable the Tx ready interrupt */
+       intr = *port->_intr;
+       *port->_intr = intr & ~SC01ICR_TI;
+       tmp = *port->_intr;
+
+       if (ch == 0x0a) {
+               *(u8 *) port->_txb = 0x0d;
+               while (*port->_status & SC01STR_TBF)
+                       continue;
+       }
+
+       *(u8 *) port->_txb = ch;
+       while (*port->_status & SC01STR_TBF)
+               continue;
+
+       /* restore the Tx interrupt flag */
+       *port->_intr = intr;
+       tmp = *port->_intr;
+}
+
+#endif /* CONFIG_CONSOLE_POLL */
index e1b14a6ed544675bf7929dc7861876a1a3bf2cc8..28eec3102535c86c91649b336aeb0b9f5677b75c 100644 (file)
@@ -135,7 +135,7 @@ void release_segments(struct mm_struct *mm)
 
 void machine_restart(char *cmd)
 {
-#ifdef CONFIG_GDBSTUB
+#ifdef CONFIG_KERNEL_DEBUGGER
        gdbstub_exit(0);
 #endif
 
@@ -148,14 +148,14 @@ void machine_restart(char *cmd)
 
 void machine_halt(void)
 {
-#ifdef CONFIG_GDBSTUB
+#ifdef CONFIG_KERNEL_DEBUGGER
        gdbstub_exit(0);
 #endif
 }
 
 void machine_power_off(void)
 {
-#ifdef CONFIG_GDBSTUB
+#ifdef CONFIG_KERNEL_DEBUGGER
        gdbstub_exit(0);
 #endif
 }
index 1ebb79f1650d74b2fb38407694f44f17a18771ab..51c02f97dceaa924aaba64383efcbb93032ad313 100644 (file)
@@ -439,6 +439,22 @@ int smp_nmi_call_function(smp_call_func_t func, void *info, int wait)
        return ret;
 }
 
+/**
+ * smp_jump_to_debugger - Make other CPUs enter the debugger by sending an IPI
+ *
+ * Send a non-maskable request to all other CPUs in the system, instructing
+ * them to jump into the debugger.  The caller is responsible for checking that
+ * the other CPUs responded to the instruction.
+ *
+ * The caller should make sure that this CPU's debugger IPI is disabled.
+ */
+void smp_jump_to_debugger(void)
+{
+       if (num_online_cpus() > 1)
+               /* Send a message to all other CPUs */
+               send_IPI_allbutself(DEBUGGER_NMI_IPI);
+}
+
 /**
  * stop_this_cpu - Callback to stop a CPU.
  * @unused: Callback context (ignored).
@@ -603,7 +619,7 @@ static void __init smp_cpu_init(void)
 /**
  * smp_prepare_cpu_init - Initialise CPU in startup_secondary
  *
- * Set interrupt level 0-6 setting and init ICR of gdbstub.
+ * Set interrupt level 0-6 setting and init ICR of the kernel debugger.
  */
 void smp_prepare_cpu_init(void)
 {
@@ -622,15 +638,15 @@ void smp_prepare_cpu_init(void)
        for (loop = 0; loop < GxICR_NUM_IRQS; loop++)
                GxICR(loop) = GxICR_LEVEL_6 | GxICR_DETECT;
 
-#ifdef CONFIG_GDBSTUB
-       /* initialise GDB-stub */
+#ifdef CONFIG_KERNEL_DEBUGGER
+       /* initialise the kernel debugger interrupt */
        do {
                unsigned long flags;
                u16 tmp16;
 
                flags = arch_local_cli_save();
-               GxICR(GDB_NMI_IPI) = GxICR_NMI | GxICR_ENABLE | GxICR_DETECT;
-               tmp16 = GxICR(GDB_NMI_IPI);
+               GxICR(DEBUGGER_NMI_IPI) = GxICR_NMI | GxICR_ENABLE | GxICR_DETECT;
+               tmp16 = GxICR(DEBUGGER_NMI_IPI);
                arch_local_irq_restore(flags);
        } while (0);
 #endif
index 9074d0fb8788fbc5187d6d77b026a271c91050f5..de3e74fc9ea04980099b6a6c68546e311e7cb565 100644 (file)
@@ -39,11 +39,17 @@ ENTRY(__switch_to)
 
        # save prev context
        mov     __switch_back,d0
-       mov     d0,(THREAD_PC,a0)
        mov     sp,a2
        mov     a2,(THREAD_SP,a0)
        mov     a3,(THREAD_A3,a0)
 
+#ifdef CONFIG_KGDB
+       btst    0xff,(kgdb_single_step)
+       bne     __switch_to__lift_sstep_bp
+__switch_to__continue:
+#endif
+       mov     d0,(THREAD_PC,a0)
+
        mov     (THREAD_A3,a1),a3
        mov     (THREAD_SP,a1),a2
 
@@ -68,3 +74,106 @@ ENTRY(__switch_to)
 __switch_back:
        and     ~EPSW_NMID,epsw
        ret     [d2,d3,a2,a3,exreg1],32
+
+#ifdef CONFIG_KGDB
+###############################################################################
+#
+# Lift the single-step breakpoints when the task being traced is switched out
+# A0 = prev
+# A1 = next
+#
+###############################################################################
+__switch_to__lift_sstep_bp:
+       add     -12,sp
+       mov     a0,e4
+       mov     a1,e5
+
+       # Clear the single-step flag to prevent us coming this way until we get
+       # switched back in
+       bclr    0xff,(kgdb_single_step)
+
+       # Remove first breakpoint
+       mov     (kgdb_sstep_bp_addr),a2
+       cmp     0,a2
+       beq     1f
+       movbu   (kgdb_sstep_bp),d0
+       movbu   d0,(a2)
+#if defined(CONFIG_MN10300_CACHE_FLUSH_ICACHE) || defined(CONFIG_MN10300_CACHE_INV_ICACHE)
+       mov     a2,d0
+       mov     a2,d1
+       add     1,d1
+       calls   flush_icache_range
+#endif
+1:
+
+       # Remove second breakpoint
+       mov     (kgdb_sstep_bp_addr+4),a2
+       cmp     0,a2
+       beq     2f
+       movbu   (kgdb_sstep_bp+1),d0
+       movbu   d0,(a2)
+#if defined(CONFIG_MN10300_CACHE_FLUSH_ICACHE) || defined(CONFIG_MN10300_CACHE_INV_ICACHE)
+       mov     a2,d0
+       mov     a2,d1
+       add     1,d1
+       calls   flush_icache_range
+#endif
+2:
+
+       # Change the resumption address and return
+       mov     __switch_back__reinstall_sstep_bp,d0
+       mov     e4,a0
+       mov     e5,a1
+       add     12,sp
+       bra     __switch_to__continue
+
+###############################################################################
+#
+# Reinstall the single-step breakpoints when the task being traced is switched
+# back in (A1 points to the new thread_struct).
+#
+###############################################################################
+__switch_back__reinstall_sstep_bp:
+       add     -12,sp
+       mov     a0,e4                   # save the return value
+       mov     0xff,d3
+
+       # Reinstall first breakpoint
+       mov     (kgdb_sstep_bp_addr),a2
+       cmp     0,a2
+       beq     1f
+       movbu   (a2),d0
+       movbu   d0,(kgdb_sstep_bp)
+       movbu   d3,(a2)
+#if defined(CONFIG_MN10300_CACHE_FLUSH_ICACHE) || defined(CONFIG_MN10300_CACHE_INV_ICACHE)
+       mov     a2,d0
+       mov     a2,d1
+       add     1,d1
+       calls   flush_icache_range
+#endif
+1:
+
+       # Reinstall second breakpoint
+       mov     (kgdb_sstep_bp_addr+4),a2
+       cmp     0,a2
+       beq     2f
+       movbu   (a2),d0
+       movbu   d0,(kgdb_sstep_bp+1)
+       movbu   d3,(a2)
+#if defined(CONFIG_MN10300_CACHE_FLUSH_ICACHE) || defined(CONFIG_MN10300_CACHE_INV_ICACHE)
+       mov     a2,d0
+       mov     a2,d1
+       add     1,d1
+       calls   flush_icache_range
+#endif
+2:
+
+       mov     d3,(kgdb_single_step)
+
+       # Restore the return value (the previous thread_struct pointer)
+       mov     e4,a0
+       mov     a0,d0
+       add     12,sp
+       bra     __switch_back
+
+#endif /* CONFIG_KGDB */
index b90c3f160c77b0e598fe90578d9362de54e4a1f1..f03cb278828f400c1e66580be491c30da507e9b3 100644 (file)
@@ -38,8 +38,9 @@
 #include <asm/busctl-regs.h>
 #include <unit/leds.h>
 #include <asm/fpu.h>
-#include <asm/gdb-stub.h>
 #include <asm/sections.h>
+#include <asm/debugger.h>
+#include "internal.h"
 
 #if (CONFIG_INTERRUPT_VECTOR_BASE & 0xffffff)
 #error "INTERRUPT_VECTOR_BASE not aligned to 16MiB boundary!"
@@ -49,63 +50,169 @@ int kstack_depth_to_print = 24;
 
 spinlock_t die_lock = __SPIN_LOCK_UNLOCKED(die_lock);
 
-ATOMIC_NOTIFIER_HEAD(mn10300_die_chain);
+struct exception_to_signal_map {
+       u8      signo;
+       u32     si_code;
+};
+
+static const struct exception_to_signal_map exception_to_signal_map[256] = {
+       /* MMU exceptions */
+       [EXCEP_ITLBMISS >> 3]   = { 0, 0 },
+       [EXCEP_DTLBMISS >> 3]   = { 0, 0 },
+       [EXCEP_IAERROR >> 3]    = { 0, 0 },
+       [EXCEP_DAERROR >> 3]    = { 0, 0 },
+
+       /* system exceptions */
+       [EXCEP_TRAP >> 3]       = { SIGTRAP,    TRAP_BRKPT },
+       [EXCEP_ISTEP >> 3]      = { SIGTRAP,    TRAP_TRACE },   /* Monitor */
+       [EXCEP_IBREAK >> 3]     = { SIGTRAP,    TRAP_HWBKPT },  /* Monitor */
+       [EXCEP_OBREAK >> 3]     = { SIGTRAP,    TRAP_HWBKPT },  /* Monitor */
+       [EXCEP_PRIVINS >> 3]    = { SIGILL,     ILL_PRVOPC },
+       [EXCEP_UNIMPINS >> 3]   = { SIGILL,     ILL_ILLOPC },
+       [EXCEP_UNIMPEXINS >> 3] = { SIGILL,     ILL_ILLOPC },
+       [EXCEP_MEMERR >> 3]     = { SIGSEGV,    SEGV_ACCERR },
+       [EXCEP_MISALIGN >> 3]   = { SIGBUS,     BUS_ADRALN },
+       [EXCEP_BUSERROR >> 3]   = { SIGBUS,     BUS_ADRERR },
+       [EXCEP_ILLINSACC >> 3]  = { SIGSEGV,    SEGV_ACCERR },
+       [EXCEP_ILLDATACC >> 3]  = { SIGSEGV,    SEGV_ACCERR },
+       [EXCEP_IOINSACC >> 3]   = { SIGSEGV,    SEGV_ACCERR },
+       [EXCEP_PRIVINSACC >> 3] = { SIGSEGV,    SEGV_ACCERR }, /* userspace */
+       [EXCEP_PRIVDATACC >> 3] = { SIGSEGV,    SEGV_ACCERR }, /* userspace */
+       [EXCEP_DATINSACC >> 3]  = { SIGSEGV,    SEGV_ACCERR },
+       [EXCEP_DOUBLE_FAULT >> 3] = { SIGILL,   ILL_BADSTK },
+
+       /* FPU exceptions */
+       [EXCEP_FPU_DISABLED >> 3] = { SIGILL,   ILL_COPROC },
+       [EXCEP_FPU_UNIMPINS >> 3] = { SIGILL,   ILL_COPROC },
+       [EXCEP_FPU_OPERATION >> 3] = { SIGFPE,  FPE_INTDIV },
+
+       /* interrupts */
+       [EXCEP_WDT >> 3]        = { SIGALRM,    0 },
+       [EXCEP_NMI >> 3]        = { SIGQUIT,    0 },
+       [EXCEP_IRQ_LEVEL0 >> 3] = { SIGINT,     0 },
+       [EXCEP_IRQ_LEVEL1 >> 3] = { 0, 0 },
+       [EXCEP_IRQ_LEVEL2 >> 3] = { 0, 0 },
+       [EXCEP_IRQ_LEVEL3 >> 3] = { 0, 0 },
+       [EXCEP_IRQ_LEVEL4 >> 3] = { 0, 0 },
+       [EXCEP_IRQ_LEVEL5 >> 3] = { 0, 0 },
+       [EXCEP_IRQ_LEVEL6 >> 3] = { 0, 0 },
+
+       /* system calls */
+       [EXCEP_SYSCALL0 >> 3]   = { 0, 0 },
+       [EXCEP_SYSCALL1 >> 3]   = { SIGILL,     ILL_ILLTRP },
+       [EXCEP_SYSCALL2 >> 3]   = { SIGILL,     ILL_ILLTRP },
+       [EXCEP_SYSCALL3 >> 3]   = { SIGILL,     ILL_ILLTRP },
+       [EXCEP_SYSCALL4 >> 3]   = { SIGILL,     ILL_ILLTRP },
+       [EXCEP_SYSCALL5 >> 3]   = { SIGILL,     ILL_ILLTRP },
+       [EXCEP_SYSCALL6 >> 3]   = { SIGILL,     ILL_ILLTRP },
+       [EXCEP_SYSCALL7 >> 3]   = { SIGILL,     ILL_ILLTRP },
+       [EXCEP_SYSCALL8 >> 3]   = { SIGILL,     ILL_ILLTRP },
+       [EXCEP_SYSCALL9 >> 3]   = { SIGILL,     ILL_ILLTRP },
+       [EXCEP_SYSCALL10 >> 3]  = { SIGILL,     ILL_ILLTRP },
+       [EXCEP_SYSCALL11 >> 3]  = { SIGILL,     ILL_ILLTRP },
+       [EXCEP_SYSCALL12 >> 3]  = { SIGILL,     ILL_ILLTRP },
+       [EXCEP_SYSCALL13 >> 3]  = { SIGILL,     ILL_ILLTRP },
+       [EXCEP_SYSCALL14 >> 3]  = { SIGILL,     ILL_ILLTRP },
+       [EXCEP_SYSCALL15 >> 3]  = { SIGABRT,    0 },
+};
 
 /*
- * These constants are for searching for possible module text
- * segments. MODULE_RANGE is a guess of how much space is likely
- * to be vmalloced.
+ * Handle kernel exceptions.
+ *
+ * See if there's a fixup handler we can force a jump to when an exception
+ * happens due to something kernel code did
  */
-#define MODULE_RANGE (8 * 1024 * 1024)
-
-#define DO_ERROR(signr, prologue, str, name)                   \
-asmlinkage void name(struct pt_regs *regs, u32 intcode)                \
-{                                                              \
-       prologue;                                               \
-       if (die_if_no_fixup(str, regs, intcode))                \
-               return;                                         \
-       force_sig(signr, current);                              \
-}
+int die_if_no_fixup(const char *str, struct pt_regs *regs,
+                   enum exception_code code)
+{
+       u8 opcode;
+       int signo, si_code;
+
+       if (user_mode(regs))
+               return 0;
+
+       peripheral_leds_display_exception(code);
+
+       signo = exception_to_signal_map[code >> 3].signo;
+       si_code = exception_to_signal_map[code >> 3].si_code;
+
+       switch (code) {
+               /* see if we can fixup the kernel accessing memory */
+       case EXCEP_ITLBMISS:
+       case EXCEP_DTLBMISS:
+       case EXCEP_IAERROR:
+       case EXCEP_DAERROR:
+       case EXCEP_MEMERR:
+       case EXCEP_MISALIGN:
+       case EXCEP_BUSERROR:
+       case EXCEP_ILLDATACC:
+       case EXCEP_IOINSACC:
+       case EXCEP_PRIVINSACC:
+       case EXCEP_PRIVDATACC:
+       case EXCEP_DATINSACC:
+               if (fixup_exception(regs))
+                       return 1;
+               break;
 
-#define DO_EINFO(signr, prologue, str, name, sicode)                   \
-asmlinkage void name(struct pt_regs *regs, u32 intcode)                        \
-{                                                                      \
-       siginfo_t info;                                                 \
-       prologue;                                                       \
-       if (die_if_no_fixup(str, regs, intcode))                        \
-               return;                                                 \
-       info.si_signo = signr;                                          \
-       if (signr == SIGILL && sicode == ILL_ILLOPC) {                  \
-               uint8_t opcode;                                         \
-               if (get_user(opcode, (uint8_t __user *)regs->pc) == 0)  \
-                       if (opcode == 0xff)                             \
-                               info.si_signo = SIGTRAP;                \
-       }                                                               \
-       info.si_errno = 0;                                              \
-       info.si_code = sicode;                                          \
-       info.si_addr = (void *) regs->pc;                               \
-       force_sig_info(info.si_signo, &info, current);                  \
+       case EXCEP_TRAP:
+       case EXCEP_UNIMPINS:
+               if (get_user(opcode, (uint8_t __user *)regs->pc) != 0)
+                       break;
+               if (opcode == 0xff) {
+                       if (notify_die(DIE_BREAKPOINT, str, regs, code, 0, 0))
+                               return 1;
+                       if (at_debugger_breakpoint(regs))
+                               regs->pc++;
+                       signo = SIGTRAP;
+                       si_code = TRAP_BRKPT;
+               }
+               break;
+
+       case EXCEP_SYSCALL1 ... EXCEP_SYSCALL14:
+               /* syscall return addr is _after_ the instruction */
+               regs->pc -= 2;
+               break;
+
+       case EXCEP_SYSCALL15:
+               if (report_bug(regs->pc, regs) == BUG_TRAP_TYPE_WARN)
+                       return 1;
+
+               /* syscall return addr is _after_ the instruction */
+               regs->pc -= 2;
+               break;
+
+       default:
+               break;
+       }
+
+       if (debugger_intercept(code, signo, si_code, regs) == 0)
+               return 1;
+
+       if (notify_die(DIE_GPF, str, regs, code, 0, 0))
+               return 1;
+
+       /* make the process die as the last resort */
+       die(str, regs, code);
 }
 
-DO_ERROR(SIGTRAP, {}, "trap",                  trap);
-DO_ERROR(SIGSEGV, {}, "ibreak",                        ibreak);
-DO_ERROR(SIGSEGV, {}, "obreak",                        obreak);
-DO_EINFO(SIGSEGV, {}, "access error",          access_error,   SEGV_ACCERR);
-DO_EINFO(SIGSEGV, {}, "insn access error",     insn_acc_error, SEGV_ACCERR);
-DO_EINFO(SIGSEGV, {}, "data access error",     data_acc_error, SEGV_ACCERR);
-DO_EINFO(SIGILL,  {}, "privileged opcode",     priv_op,        ILL_PRVOPC);
-DO_EINFO(SIGILL,  {}, "invalid opcode",                invalid_op,     ILL_ILLOPC);
-DO_EINFO(SIGILL,  {}, "invalid ex opcode",     invalid_exop,   ILL_ILLOPC);
-DO_EINFO(SIGBUS,  {}, "invalid address",       mem_error,      BUS_ADRERR);
-DO_EINFO(SIGBUS,  {}, "bus error",             bus_error,      BUS_ADRERR);
-
-DO_ERROR(SIGTRAP,
-#ifndef CONFIG_MN10300_USING_JTAG
-        DCR &= ~0x0001,
-#else
-        {},
-#endif
-        "single step", istep);
+/*
+ * General exception handler
+ */
+asmlinkage void handle_exception(struct pt_regs *regs, u32 intcode)
+{
+       siginfo_t info;
+
+       /* deal with kernel exceptions here */
+       if (die_if_no_fixup(NULL, regs, intcode))
+               return;
+
+       /* otherwise it's a userspace exception */
+       info.si_signo = exception_to_signal_map[intcode >> 3].signo;
+       info.si_code = exception_to_signal_map[intcode >> 3].si_code;
+       info.si_errno = 0;
+       info.si_addr = (void *) regs->pc;
+       force_sig_info(info.si_signo, &info, current);
+}
 
 /*
  * handle NMI
@@ -113,10 +220,8 @@ DO_ERROR(SIGTRAP,
 asmlinkage void nmi(struct pt_regs *regs, enum exception_code code)
 {
        /* see if gdbstub wants to deal with it */
-#ifdef CONFIG_GDBSTUB
-       if (gdbstub_intercept(regs, code))
+       if (debugger_intercept(code, SIGQUIT, 0, regs))
                return;
-#endif
 
        printk(KERN_WARNING "--- Register Dump ---\n");
        show_registers(regs);
@@ -128,29 +233,36 @@ asmlinkage void nmi(struct pt_regs *regs, enum exception_code code)
  */
 void show_trace(unsigned long *sp)
 {
-       unsigned long *stack, addr, module_start, module_end;
-       int i;
-
-       printk(KERN_EMERG "\nCall Trace:");
-
-       stack = sp;
-       i = 0;
-       module_start = VMALLOC_START;
-       module_end = VMALLOC_END;
+       unsigned long bottom, stack, addr, fp, raslot;
+
+       printk(KERN_EMERG "\nCall Trace:\n");
+
+       //stack = (unsigned long)sp;
+       asm("mov sp,%0" : "=a"(stack));
+       asm("mov a3,%0" : "=r"(fp));
+
+       raslot = ULONG_MAX;
+       bottom = (stack + THREAD_SIZE) & ~(THREAD_SIZE - 1);
+       for (; stack < bottom; stack += sizeof(addr)) {
+               addr = *(unsigned long *)stack;
+               if (stack == fp) {
+                       if (addr > stack && addr < bottom) {
+                               fp = addr;
+                               raslot = stack + sizeof(addr);
+                               continue;
+                       }
+                       fp = 0;
+                       raslot = ULONG_MAX;
+               }
 
-       while (((long) stack & (THREAD_SIZE - 1)) != 0) {
-               addr = *stack++;
                if (__kernel_text_address(addr)) {
-#if 1
                        printk(" [<%08lx>]", addr);
+                       if (stack >= raslot)
+                               raslot = ULONG_MAX;
+                       else
+                               printk(" ?");
                        print_symbol(" %s", addr);
                        printk("\n");
-#else
-                       if ((i % 6) == 0)
-                               printk(KERN_EMERG "  ");
-                       printk("[<%08lx>] ", addr);
-                       i++;
-#endif
                }
        }
 
@@ -322,86 +434,6 @@ void die(const char *str, struct pt_regs *regs, enum exception_code code)
        do_exit(SIGSEGV);
 }
 
-/*
- * see if there's a fixup handler we can force a jump to when an exception
- * happens due to something kernel code did
- */
-int die_if_no_fixup(const char *str, struct pt_regs *regs,
-                   enum exception_code code)
-{
-       if (user_mode(regs))
-               return 0;
-
-       peripheral_leds_display_exception(code);
-
-       switch (code) {
-               /* see if we can fixup the kernel accessing memory */
-       case EXCEP_ITLBMISS:
-       case EXCEP_DTLBMISS:
-       case EXCEP_IAERROR:
-       case EXCEP_DAERROR:
-       case EXCEP_MEMERR:
-       case EXCEP_MISALIGN:
-       case EXCEP_BUSERROR:
-       case EXCEP_ILLDATACC:
-       case EXCEP_IOINSACC:
-       case EXCEP_PRIVINSACC:
-       case EXCEP_PRIVDATACC:
-       case EXCEP_DATINSACC:
-               if (fixup_exception(regs))
-                       return 1;
-       case EXCEP_UNIMPINS:
-               if (regs->pc && *(uint8_t *)regs->pc == 0xff)
-                       if (notify_die(DIE_BREAKPOINT, str, regs, code, 0, 0))
-                               return 1;
-               break;
-       default:
-               break;
-       }
-
-       /* see if gdbstub wants to deal with it */
-#ifdef CONFIG_GDBSTUB
-       if (gdbstub_intercept(regs, code))
-               return 1;
-#endif
-
-       if (notify_die(DIE_GPF, str, regs, code, 0, 0))
-               return 1;
-
-       /* make the process die as the last resort */
-       die(str, regs, code);
-}
-
-/*
- * handle unsupported syscall instructions (syscall 1-15)
- */
-static asmlinkage void unsupported_syscall(struct pt_regs *regs,
-                                          enum exception_code code)
-{
-       struct task_struct *tsk = current;
-       siginfo_t info;
-
-       /* catch a kernel BUG() */
-       if (code == EXCEP_SYSCALL15 && !user_mode(regs)) {
-               if (report_bug(regs->pc, regs) == BUG_TRAP_TYPE_BUG) {
-#ifdef CONFIG_GDBSTUB
-                       gdbstub_intercept(regs, code);
-#endif
-               }
-       }
-
-       regs->pc -= 2; /* syscall return addr is _after_ the instruction */
-
-       die_if_no_fixup("An unsupported syscall insn was used by the kernel\n",
-                       regs, code);
-
-       info.si_signo   = SIGILL;
-       info.si_errno   = ENOSYS;
-       info.si_code    = ILL_ILLTRP;
-       info.si_addr    = (void *) regs->pc;
-       force_sig_info(SIGILL, &info, tsk);
-}
-
 /*
  * display the register file when the stack pointer gets clobbered
  */
@@ -481,10 +513,8 @@ asmlinkage void uninitialised_exception(struct pt_regs *regs,
 {
 
        /* see if gdbstub wants to deal with it */
-#ifdef CONFIG_GDBSTUB
-       if (gdbstub_intercept(regs, code))
+       if (debugger_intercept(code, SIGSYS, 0, regs) == 0)
                return;
-#endif
 
        peripheral_leds_display_exception(code);
        printk(KERN_EMERG "Uninitialised Exception 0x%04x\n", code & 0xFFFF);
@@ -549,43 +579,43 @@ void __init set_intr_stub(enum exception_code code, void *handler)
  */
 void __init trap_init(void)
 {
-       set_excp_vector(EXCEP_TRAP,             trap);
-       set_excp_vector(EXCEP_ISTEP,            istep);
-       set_excp_vector(EXCEP_IBREAK,           ibreak);
-       set_excp_vector(EXCEP_OBREAK,           obreak);
-
-       set_excp_vector(EXCEP_PRIVINS,          priv_op);
-       set_excp_vector(EXCEP_UNIMPINS,         invalid_op);
-       set_excp_vector(EXCEP_UNIMPEXINS,       invalid_exop);
-       set_excp_vector(EXCEP_MEMERR,           mem_error);
+       set_excp_vector(EXCEP_TRAP,             handle_exception);
+       set_excp_vector(EXCEP_ISTEP,            handle_exception);
+       set_excp_vector(EXCEP_IBREAK,           handle_exception);
+       set_excp_vector(EXCEP_OBREAK,           handle_exception);
+
+       set_excp_vector(EXCEP_PRIVINS,          handle_exception);
+       set_excp_vector(EXCEP_UNIMPINS,         handle_exception);
+       set_excp_vector(EXCEP_UNIMPEXINS,       handle_exception);
+       set_excp_vector(EXCEP_MEMERR,           handle_exception);
        set_excp_vector(EXCEP_MISALIGN,         misalignment);
-       set_excp_vector(EXCEP_BUSERROR,         bus_error);
-       set_excp_vector(EXCEP_ILLINSACC,        insn_acc_error);
-       set_excp_vector(EXCEP_ILLDATACC,        data_acc_error);
-       set_excp_vector(EXCEP_IOINSACC,         insn_acc_error);
-       set_excp_vector(EXCEP_PRIVINSACC,       insn_acc_error);
-       set_excp_vector(EXCEP_PRIVDATACC,       data_acc_error);
-       set_excp_vector(EXCEP_DATINSACC,        insn_acc_error);
-       set_excp_vector(EXCEP_FPU_UNIMPINS,     fpu_invalid_op);
+       set_excp_vector(EXCEP_BUSERROR,         handle_exception);
+       set_excp_vector(EXCEP_ILLINSACC,        handle_exception);
+       set_excp_vector(EXCEP_ILLDATACC,        handle_exception);
+       set_excp_vector(EXCEP_IOINSACC,         handle_exception);
+       set_excp_vector(EXCEP_PRIVINSACC,       handle_exception);
+       set_excp_vector(EXCEP_PRIVDATACC,       handle_exception);
+       set_excp_vector(EXCEP_DATINSACC,        handle_exception);
+       set_excp_vector(EXCEP_FPU_UNIMPINS,     handle_exception);
        set_excp_vector(EXCEP_FPU_OPERATION,    fpu_exception);
 
        set_excp_vector(EXCEP_NMI,              nmi);
 
-       set_excp_vector(EXCEP_SYSCALL1,         unsupported_syscall);
-       set_excp_vector(EXCEP_SYSCALL2,         unsupported_syscall);
-       set_excp_vector(EXCEP_SYSCALL3,         unsupported_syscall);
-       set_excp_vector(EXCEP_SYSCALL4,         unsupported_syscall);
-       set_excp_vector(EXCEP_SYSCALL5,         unsupported_syscall);
-       set_excp_vector(EXCEP_SYSCALL6,         unsupported_syscall);
-       set_excp_vector(EXCEP_SYSCALL7,         unsupported_syscall);
-       set_excp_vector(EXCEP_SYSCALL8,         unsupported_syscall);
-       set_excp_vector(EXCEP_SYSCALL9,         unsupported_syscall);
-       set_excp_vector(EXCEP_SYSCALL10,        unsupported_syscall);
-       set_excp_vector(EXCEP_SYSCALL11,        unsupported_syscall);
-       set_excp_vector(EXCEP_SYSCALL12,        unsupported_syscall);
-       set_excp_vector(EXCEP_SYSCALL13,        unsupported_syscall);
-       set_excp_vector(EXCEP_SYSCALL14,        unsupported_syscall);
-       set_excp_vector(EXCEP_SYSCALL15,        unsupported_syscall);
+       set_excp_vector(EXCEP_SYSCALL1,         handle_exception);
+       set_excp_vector(EXCEP_SYSCALL2,         handle_exception);
+       set_excp_vector(EXCEP_SYSCALL3,         handle_exception);
+       set_excp_vector(EXCEP_SYSCALL4,         handle_exception);
+       set_excp_vector(EXCEP_SYSCALL5,         handle_exception);
+       set_excp_vector(EXCEP_SYSCALL6,         handle_exception);
+       set_excp_vector(EXCEP_SYSCALL7,         handle_exception);
+       set_excp_vector(EXCEP_SYSCALL8,         handle_exception);
+       set_excp_vector(EXCEP_SYSCALL9,         handle_exception);
+       set_excp_vector(EXCEP_SYSCALL10,        handle_exception);
+       set_excp_vector(EXCEP_SYSCALL11,        handle_exception);
+       set_excp_vector(EXCEP_SYSCALL12,        handle_exception);
+       set_excp_vector(EXCEP_SYSCALL13,        handle_exception);
+       set_excp_vector(EXCEP_SYSCALL14,        handle_exception);
+       set_excp_vector(EXCEP_SYSCALL15,        handle_exception);
 }
 
 /*
index c4fd923a55a09803b0c06f13485598968345c0d9..bfbe52691f2c8feb474be5af3d9eccaea1e4e287 100644 (file)
@@ -99,3 +99,49 @@ config MN10300_CACHE_INV_ICACHE
        help
          Set if we need the icache to be invalidated, even if the dcache is in
          write-through mode and doesn't need flushing.
+
+#
+# The kernel debugger gets its own separate cache flushing functions
+#
+config MN10300_DEBUGGER_CACHE_FLUSH_BY_TAG
+       def_bool y if KERNEL_DEBUGGER && \
+                       MN10300_CACHE_WBACK && \
+                       !MN10300_CACHE_SNOOP && \
+                       MN10300_CACHE_MANAGE_BY_TAG
+       help
+         Set if the debugger needs to flush the dcache and invalidate the
+         icache using the cache tag registers to make breakpoints work.
+
+config MN10300_DEBUGGER_CACHE_FLUSH_BY_REG
+       def_bool y if KERNEL_DEBUGGER && \
+                       MN10300_CACHE_WBACK && \
+                       !MN10300_CACHE_SNOOP && \
+                       MN10300_CACHE_MANAGE_BY_REG
+       help
+         Set if the debugger needs to flush the dcache and invalidate the
+         icache using automatic purge registers to make breakpoints work.
+
+config MN10300_DEBUGGER_CACHE_INV_BY_TAG
+       def_bool y if KERNEL_DEBUGGER && \
+                       MN10300_CACHE_WTHRU && \
+                       !MN10300_CACHE_SNOOP && \
+                       MN10300_CACHE_MANAGE_BY_TAG
+       help
+         Set if the debugger needs to invalidate the icache using the cache
+         tag registers to make breakpoints work.
+
+config MN10300_DEBUGGER_CACHE_INV_BY_REG
+       def_bool y if KERNEL_DEBUGGER && \
+                       MN10300_CACHE_WTHRU && \
+                       !MN10300_CACHE_SNOOP && \
+                       MN10300_CACHE_MANAGE_BY_REG
+       help
+         Set if the debugger needs to invalidate the icache using automatic
+         purge registers to make breakpoints work.
+
+config MN10300_DEBUGGER_CACHE_NO_FLUSH
+       def_bool y if KERNEL_DEBUGGER && \
+                       (MN10300_CACHE_DISABLED || MN10300_CACHE_SNOOP)
+       help
+         Set if the debugger does not need to flush the dcache and/or
+         invalidate the icache to make breakpoints work.
index 203fee23f7d70efebe0b6c11eb1bfdb6dc3cae6c..11f38466ac28e17a7e3c2518a5a9679a1ad35d3b 100644 (file)
@@ -13,6 +13,15 @@ cacheflush-$(CONFIG_MN10300_CACHE_INV_BY_REG) += cache-inv-by-reg.o
 cacheflush-$(CONFIG_MN10300_CACHE_FLUSH_BY_TAG) += cache-flush-by-tag.o
 cacheflush-$(CONFIG_MN10300_CACHE_FLUSH_BY_REG) += cache-flush-by-reg.o
 
+cacheflush-$(CONFIG_MN10300_DEBUGGER_CACHE_FLUSH_BY_TAG) += \
+       cache-dbg-flush-by-tag.o cache-dbg-inv-by-tag.o
+cacheflush-$(CONFIG_MN10300_DEBUGGER_CACHE_FLUSH_BY_REG) += \
+       cache-dbg-flush-by-reg.o
+cacheflush-$(CONFIG_MN10300_DEBUGGER_CACHE_INV_BY_TAG) += \
+       cache-dbg-inv-by-tag.o cache-dbg-inv.o
+cacheflush-$(CONFIG_MN10300_DEBUGGER_CACHE_INV_BY_REG) += \
+       cache-dbg-inv-by-reg.o cache-dbg-inv.o
+
 cacheflush-$(CONFIG_MN10300_CACHE_DISABLED) := cache-disabled.o
 
 obj-y := \
diff --git a/arch/mn10300/mm/cache-dbg-flush-by-reg.S b/arch/mn10300/mm/cache-dbg-flush-by-reg.S
new file mode 100644 (file)
index 0000000..665919f
--- /dev/null
@@ -0,0 +1,160 @@
+/* MN10300 CPU cache invalidation routines, using automatic purge registers
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/sys.h>
+#include <linux/linkage.h>
+#include <asm/smp.h>
+#include <asm/page.h>
+#include <asm/cache.h>
+#include <asm/irqflags.h>
+#include <asm/cacheflush.h>
+#include "cache.inc"
+
+       .am33_2
+
+###############################################################################
+#
+# void debugger_local_cache_flushinv(void)
+# Flush the entire data cache back to RAM and invalidate the icache
+#
+###############################################################################
+       ALIGN
+       .globl  debugger_local_cache_flushinv
+        .type  debugger_local_cache_flushinv,@function
+debugger_local_cache_flushinv:
+       #
+       # firstly flush the dcache
+       #
+       movhu   (CHCTR),d0
+       btst    CHCTR_DCEN|CHCTR_ICEN,d0
+       beq     debugger_local_cache_flushinv_end
+
+       mov     DCPGCR,a0
+
+       mov     epsw,d1
+       and     ~EPSW_IE,epsw
+       or      EPSW_NMID,epsw
+       nop
+
+       btst    CHCTR_DCEN,d0
+       beq     debugger_local_cache_flushinv_no_dcache
+
+       # wait for busy bit of area purge
+       setlb
+       mov     (a0),d0
+       btst    DCPGCR_DCPGBSY,d0
+       lne
+
+       # set mask
+       clr     d0
+       mov     d0,(DCPGMR)
+
+       # area purge
+       #
+       # DCPGCR = DCPGCR_DCP
+       #
+       mov     DCPGCR_DCP,d0
+       mov     d0,(a0)
+
+       # wait for busy bit of area purge
+       setlb
+       mov     (a0),d0
+       btst    DCPGCR_DCPGBSY,d0
+       lne
+
+debugger_local_cache_flushinv_no_dcache:
+       #
+       # secondly, invalidate the icache if it is enabled
+       #
+       mov     CHCTR,a0
+       movhu   (a0),d0
+       btst    CHCTR_ICEN,d0
+       beq     debugger_local_cache_flushinv_done
+
+       invalidate_icache 0
+
+debugger_local_cache_flushinv_done:
+       mov     d1,epsw
+
+debugger_local_cache_flushinv_end:
+       ret     [],0
+       .size   debugger_local_cache_flushinv,.-debugger_local_cache_flushinv
+
+###############################################################################
+#
+# void debugger_local_cache_flushinv_one(u8 *addr)
+#
+# Invalidate one particular cacheline if it's in the icache
+#
+###############################################################################
+       ALIGN
+       .globl  debugger_local_cache_flushinv_one
+       .type   debugger_local_cache_flushinv_one,@function
+debugger_local_cache_flushinv_one:
+       movhu   (CHCTR),d1
+       btst    CHCTR_DCEN|CHCTR_ICEN,d1
+       beq     debugger_local_cache_flushinv_one_end
+       btst    CHCTR_DCEN,d1
+       beq     debugger_local_cache_flushinv_one_no_dcache
+
+       # round cacheline addr down
+       and     L1_CACHE_TAG_MASK,d0
+       mov     d0,a1
+       mov     d0,d1
+
+       # determine the dcache purge control reg address
+       mov     DCACHE_PURGE(0,0),a0
+       and     L1_CACHE_TAG_ENTRY,d0
+       add     d0,a0
+
+       # retain valid entries in the cache
+       or      L1_CACHE_TAG_VALID,d1
+
+       # conditionally purge this line in all ways
+       mov     d1,(L1_CACHE_WAYDISP*0,a0)
+
+debugger_local_cache_flushinv_no_dcache:
+       #
+       # now try to flush the icache
+       #
+       mov     CHCTR,a0
+       movhu   (a0),d0
+       btst    CHCTR_ICEN,d0
+       beq     mn10300_local_icache_inv_range_reg_end
+
+       LOCAL_CLI_SAVE(d1)
+
+       mov     ICIVCR,a0
+
+       # wait for the invalidator to quiesce
+       setlb
+       mov     (a0),d0
+       btst    ICIVCR_ICIVBSY,d0
+       lne
+
+       # set the mask
+       mov     L1_CACHE_TAG_MASK,d0
+       mov     d0,(ICIVMR)
+
+       # invalidate the cache line at the given address
+       or      ICIVCR_ICI,a1
+       mov     a1,(a0)
+
+       # wait for the invalidator to quiesce again
+       setlb
+       mov     (a0),d0
+       btst    ICIVCR_ICIVBSY,d0
+       lne
+
+       LOCAL_IRQ_RESTORE(d1)
+
+debugger_local_cache_flushinv_one_end:
+       ret     [],0
+       .size   debugger_local_cache_flushinv_one,.-debugger_local_cache_flushinv_one
diff --git a/arch/mn10300/mm/cache-dbg-flush-by-tag.S b/arch/mn10300/mm/cache-dbg-flush-by-tag.S
new file mode 100644 (file)
index 0000000..bf56930
--- /dev/null
@@ -0,0 +1,114 @@
+/* MN10300 CPU cache invalidation routines, using direct tag flushing
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/sys.h>
+#include <linux/linkage.h>
+#include <asm/smp.h>
+#include <asm/page.h>
+#include <asm/cache.h>
+#include <asm/irqflags.h>
+#include <asm/cacheflush.h>
+#include "cache.inc"
+
+       .am33_2
+
+###############################################################################
+#
+# void debugger_local_cache_flushinv(void)
+#
+# Flush the entire data cache back to RAM and invalidate the icache
+#
+###############################################################################
+       ALIGN
+       .globl  debugger_local_cache_flushinv
+        .type  debugger_local_cache_flushinv,@function
+debugger_local_cache_flushinv:
+       #
+       # firstly flush the dcache
+       #
+       movhu   (CHCTR),d0
+       btst    CHCTR_DCEN|CHCTR_ICEN,d0
+       beq     debugger_local_cache_flushinv_end
+
+       btst    CHCTR_DCEN,d0
+       beq     debugger_local_cache_flushinv_no_dcache
+
+       # read the addresses tagged in the cache's tag RAM and attempt to flush
+       # those addresses specifically
+       # - we rely on the hardware to filter out invalid tag entry addresses
+       mov     DCACHE_TAG(0,0),a0              # dcache tag RAM access address
+       mov     DCACHE_PURGE(0,0),a1            # dcache purge request address
+       mov     L1_CACHE_NWAYS*L1_CACHE_NENTRIES,e0  # total number of entries
+
+mn10300_local_dcache_flush_loop:
+       mov     (a0),d0
+       and     L1_CACHE_TAG_MASK,d0
+       or      L1_CACHE_TAG_VALID,d0           # retain valid entries in the
+                                               # cache
+       mov     d0,(a1)                         # conditional purge
+
+       add     L1_CACHE_BYTES,a0
+       add     L1_CACHE_BYTES,a1
+       add     -1,e0
+       bne     mn10300_local_dcache_flush_loop
+
+debugger_local_cache_flushinv_no_dcache:
+       #
+       # secondly, invalidate the icache if it is enabled
+       #
+       mov     CHCTR,a0
+       movhu   (a0),d0
+       btst    CHCTR_ICEN,d0
+       beq     debugger_local_cache_flushinv_end
+
+       invalidate_icache 1
+
+debugger_local_cache_flushinv_end:
+       ret     [],0
+       .size   debugger_local_cache_flushinv,.-debugger_local_cache_flushinv
+
+###############################################################################
+#
+# void debugger_local_cache_flushinv_one(u8 *addr)
+#
+# Invalidate one particular cacheline if it's in the icache
+#
+###############################################################################
+       ALIGN
+       .globl  debugger_local_cache_flushinv_one
+       .type   debugger_local_cache_flushinv_one,@function
+debugger_local_cache_flushinv_one:
+       movhu   (CHCTR),d1
+       btst    CHCTR_DCEN|CHCTR_ICEN,d1
+       beq     debugger_local_cache_flushinv_one_end
+       btst    CHCTR_DCEN,d1
+       beq     debugger_local_cache_flushinv_one_icache
+
+       # round cacheline addr down
+       and     L1_CACHE_TAG_MASK,d0
+       mov     d0,a1
+
+       # determine the dcache purge control reg address
+       mov     DCACHE_PURGE(0,0),a0
+       and     L1_CACHE_TAG_ENTRY,d0
+       add     d0,a0
+
+       # retain valid entries in the cache
+       or      L1_CACHE_TAG_VALID,a1
+
+       # conditionally purge this line in all ways
+       mov     a1,(L1_CACHE_WAYDISP*0,a0)
+
+       # now go and do the icache
+       bra     debugger_local_cache_flushinv_one_icache
+
+debugger_local_cache_flushinv_one_end:
+       ret     [],0
+       .size   debugger_local_cache_flushinv_one,.-debugger_local_cache_flushinv_one
diff --git a/arch/mn10300/mm/cache-dbg-inv-by-reg.S b/arch/mn10300/mm/cache-dbg-inv-by-reg.S
new file mode 100644 (file)
index 0000000..c4e6252
--- /dev/null
@@ -0,0 +1,69 @@
+/* MN10300 CPU cache invalidation routines, using automatic purge registers
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/sys.h>
+#include <linux/linkage.h>
+#include <asm/cache.h>
+#include <asm/irqflags.h>
+#include <asm/cacheflush.h>
+#include "cache.inc"
+
+       .am33_2
+
+       .globl  debugger_local_cache_flushinv_one
+
+###############################################################################
+#
+# void debugger_local_cache_flushinv_one(u8 *addr)
+#
+# Invalidate one particular cacheline if it's in the icache
+#
+###############################################################################
+       ALIGN
+       .globl  debugger_local_cache_flushinv_one
+       .type   debugger_local_cache_flushinv_one,@function
+debugger_local_cache_flushinv_one:
+       mov     d0,a1
+
+       mov     CHCTR,a0
+       movhu   (a0),d0
+       btst    CHCTR_ICEN,d0
+       beq     mn10300_local_icache_inv_range_reg_end
+
+       LOCAL_CLI_SAVE(d1)
+
+       mov     ICIVCR,a0
+
+       # wait for the invalidator to quiesce
+       setlb
+       mov     (a0),d0
+       btst    ICIVCR_ICIVBSY,d0
+       lne
+
+       # set the mask
+       mov     ~L1_CACHE_TAG_MASK,d0
+       mov     d0,(ICIVMR)
+
+       # invalidate the cache line at the given address
+       and     ~L1_CACHE_TAG_MASK,a1
+       or      ICIVCR_ICI,a1
+       mov     a1,(a0)
+
+       # wait for the invalidator to quiesce again
+       setlb
+       mov     (a0),d0
+       btst    ICIVCR_ICIVBSY,d0
+       lne
+
+       LOCAL_IRQ_RESTORE(d1)
+
+mn10300_local_icache_inv_range_reg_end:
+       ret     [],0
+       .size   debugger_local_cache_flushinv_one,.-debugger_local_cache_flushinv_one
diff --git a/arch/mn10300/mm/cache-dbg-inv-by-tag.S b/arch/mn10300/mm/cache-dbg-inv-by-tag.S
new file mode 100644 (file)
index 0000000..d8ec821
--- /dev/null
@@ -0,0 +1,120 @@
+/* MN10300 CPU cache invalidation routines, using direct tag flushing
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/sys.h>
+#include <linux/linkage.h>
+#include <asm/smp.h>
+#include <asm/page.h>
+#include <asm/cache.h>
+#include <asm/irqflags.h>
+#include <asm/cacheflush.h>
+#include "cache.inc"
+
+       .am33_2
+
+       .globl  debugger_local_cache_flushinv_one_icache
+
+###############################################################################
+#
+# void debugger_local_cache_flushinv_one(u8 *addr)
+#
+# Invalidate one particular cacheline if it's in the icache
+#
+###############################################################################
+       ALIGN
+       .globl  debugger_local_cache_flushinv_one_icache
+       .type   debugger_local_cache_flushinv_one_icache,@function
+debugger_local_cache_flushinv_one_icache:
+       movm    [d3,a2],(sp)
+
+       mov     CHCTR,a2
+       movhu   (a2),d0
+       btst    CHCTR_ICEN,d0
+       beq     debugger_local_cache_flushinv_one_icache_end
+
+       mov     d0,a1
+       and     L1_CACHE_TAG_MASK,a1
+
+       # read the tags from the tag RAM, and if they indicate a matching valid
+       # cache line then we invalidate that line
+       mov     ICACHE_TAG(0,0),a0
+       mov     a1,d0
+       and     L1_CACHE_TAG_ENTRY,d0
+       add     d0,a0                           # starting icache tag RAM
+                                               # access address
+
+       and     ~(L1_CACHE_DISPARITY-1),a1      # determine comparator base
+       or      L1_CACHE_TAG_VALID,a1
+       mov     L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_VALID,d1
+
+       LOCAL_CLI_SAVE(d3)
+
+       # disable the icache
+       movhu   (a2),d0
+       and     ~CHCTR_ICEN,d0
+       movhu   d0,(a2)
+
+       # and wait for it to calm down
+       setlb
+       movhu   (a2),d0
+       btst    CHCTR_ICBUSY,d0
+       lne
+
+       # check all the way tags for this cache entry
+       mov     (a0),d0                         # read the tag in the way 0 slot
+       xor     a1,d0
+       and     d1,d0
+       beq     debugger_local_icache_kill      # jump if matched
+
+       add     L1_CACHE_WAYDISP,a0
+       mov     (a0),d0                         # read the tag in the way 1 slot
+       xor     a1,d0
+       and     d1,d0
+       beq     debugger_local_icache_kill      # jump if matched
+
+       add     L1_CACHE_WAYDISP,a0
+       mov     (a0),d0                         # read the tag in the way 2 slot
+       xor     a1,d0
+       and     d1,d0
+       beq     debugger_local_icache_kill      # jump if matched
+
+       add     L1_CACHE_WAYDISP,a0
+       mov     (a0),d0                         # read the tag in the way 3 slot
+       xor     a1,d0
+       and     d1,d0
+       bne     debugger_local_icache_finish    # jump if not matched
+
+debugger_local_icache_kill:
+       mov     d0,(a0)                         # kill the tag (D0 is 0 at this point)
+
+debugger_local_icache_finish:
+       # wait for the cache to finish what it's doing
+       setlb
+       movhu   (a2),d0
+       btst    CHCTR_ICBUSY,d0
+       lne
+
+       # and reenable it
+       or      CHCTR_ICEN,d0
+       movhu   d0,(a2)
+       movhu   (a2),d0
+
+       # re-enable interrupts
+       LOCAL_IRQ_RESTORE(d3)
+
+debugger_local_cache_flushinv_one_icache_end:
+       ret     [d3,a2],8
+       .size   debugger_local_cache_flushinv_one_icache,.-debugger_local_cache_flushinv_one_icache
+
+#ifdef CONFIG_MN10300_DEBUGGER_CACHE_INV_BY_TAG
+       .globl  debugger_local_cache_flushinv_one
+       .type   debugger_local_cache_flushinv_one,@function
+debugger_local_cache_flushinv_one = debugger_local_cache_flushinv_one_icache
+#endif
diff --git a/arch/mn10300/mm/cache-dbg-inv.S b/arch/mn10300/mm/cache-dbg-inv.S
new file mode 100644 (file)
index 0000000..eba2d6d
--- /dev/null
@@ -0,0 +1,47 @@
+/* MN10300 CPU cache invalidation routines
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/sys.h>
+#include <linux/linkage.h>
+#include <asm/smp.h>
+#include <asm/page.h>
+#include <asm/cache.h>
+#include <asm/irqflags.h>
+#include <asm/cacheflush.h>
+#include "cache.inc"
+
+       .am33_2
+
+       .globl  debugger_local_cache_flushinv
+
+###############################################################################
+#
+# void debugger_local_cache_flushinv(void)
+#
+# Invalidate the entire icache
+#
+###############################################################################
+       ALIGN
+       .globl  debugger_local_cache_flushinv
+        .type  debugger_local_cache_flushinv,@function
+debugger_local_cache_flushinv:
+       #
+       # we only need to invalidate the icache in this cache mode
+       #
+       mov     CHCTR,a0
+       movhu   (a0),d0
+       btst    CHCTR_ICEN,d0
+       beq     debugger_local_cache_flushinv_end
+
+       invalidate_icache 1
+
+debugger_local_cache_flushinv_end:
+       ret     [],0
+       .size   debugger_local_cache_flushinv,.-debugger_local_cache_flushinv
index 5cd6a27dd63e04448bde98836e8eff937824240b..1ddc068492429fc5903f0f776d42c6798d7eb76f 100644 (file)
@@ -62,7 +62,7 @@ mn10300_local_dcache_flush:
 
 mn10300_local_dcache_flush_loop:
        mov     (a0),d0
-       and     L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0
+       and     L1_CACHE_TAG_MASK,d0
        or      L1_CACHE_TAG_VALID,d0           # retain valid entries in the
                                                # cache
        mov     d0,(a1)                         # conditional purge
@@ -112,11 +112,11 @@ mn10300_local_dcache_flush_range:
 1:
 
        # round start addr down
-       and     L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0
+       and     L1_CACHE_TAG_MASK,d0
        mov     d0,a1
 
        add     L1_CACHE_BYTES,d1                       # round end addr up
-       and     L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d1
+       and     L1_CACHE_TAG_MASK,d1
 
        # write a request to flush all instances of an address from the cache
        mov     DCACHE_PURGE(0,0),a0
@@ -215,12 +215,11 @@ mn10300_local_dcache_flush_inv_range:
        bra     mn10300_local_dcache_flush_inv
 1:
 
-       and     L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0      # round start
-                                                               # addr down
+       and     L1_CACHE_TAG_MASK,d0            # round start addr down
        mov     d0,a1
 
-       add     L1_CACHE_BYTES,d1                       # round end addr up
-       and     L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d1
+       add     L1_CACHE_BYTES,d1               # round end addr up
+       and     L1_CACHE_TAG_MASK,d1
 
        # write a request to flush and invalidate all instances of an address
        # from the cache
index c8950861ed779c5da127fbc562bc03ee58a72d55..a60825b91e7724fc0a7e6e54f17bd1059995a7fb 100644 (file)
@@ -15,6 +15,7 @@
 #include <asm/cache.h>
 #include <asm/irqflags.h>
 #include <asm/cacheflush.h>
+#include "cache.inc"
 
 #define mn10300_local_dcache_inv_range_intr_interval \
        +((1 << MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL) - 1)
@@ -62,10 +63,7 @@ mn10300_local_icache_inv:
        btst    CHCTR_ICEN,d0
        beq     mn10300_local_icache_inv_end
 
-       # invalidate
-       or      CHCTR_ICINV,d0
-       movhu   d0,(a0)
-       movhu   (a0),d0
+       invalidate_icache 1
 
 mn10300_local_icache_inv_end:
        ret     [],0
@@ -87,11 +85,8 @@ mn10300_local_dcache_inv:
        btst    CHCTR_DCEN,d0
        beq     mn10300_local_dcache_inv_end
 
-       # invalidate
-       or      CHCTR_DCINV,d0
-       movhu   d0,(a0)
-       movhu   (a0),d0
-
+       invalidate_dcache 1
+       
 mn10300_local_dcache_inv_end:
        ret     [],0
        .size   mn10300_local_dcache_inv,.-mn10300_local_dcache_inv
@@ -121,9 +116,9 @@ mn10300_local_dcache_inv_range:
        # and if they're not cacheline-aligned, we must flush any bits outside
        # the range that share cachelines with stuff inside the range
 #ifdef CONFIG_MN10300_CACHE_WBACK
-       btst    ~(L1_CACHE_BYTES-1),d0
+       btst    ~L1_CACHE_TAG_MASK,d0
        bne     1f
-       btst    ~(L1_CACHE_BYTES-1),d1
+       btst    ~L1_CACHE_TAG_MASK,d1
        beq     2f
 1:
        bra     mn10300_local_dcache_flush_inv_range
@@ -141,12 +136,11 @@ mn10300_local_dcache_inv_range:
        # writeback mode, in which case we would be in flush and invalidate by
        # now
 #ifndef CONFIG_MN10300_CACHE_WBACK
-       and     L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0      # round start
-                                                               # addr down
+       and     L1_CACHE_TAG_MASK,d0    # round start addr down
 
        mov     L1_CACHE_BYTES-1,d2
        add     d2,d1
-       and     L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d1      # round end addr up
+       and     L1_CACHE_TAG_MASK,d1    # round end addr up
 #endif /* !CONFIG_MN10300_CACHE_WBACK */
 
        sub     d0,d1,d2                # calculate the total size
index e9713b40c0ff4823ead75d4e81b09a71138e9531..ccedce9c144d30459b8d092959247d4713ee03f0 100644 (file)
@@ -15,6 +15,7 @@
 #include <asm/cache.h>
 #include <asm/irqflags.h>
 #include <asm/cacheflush.h>
+#include "cache.inc"
 
 #define mn10300_local_dcache_inv_range_intr_interval \
        +((1 << MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL) - 1)
@@ -70,43 +71,7 @@ mn10300_local_icache_inv:
        btst    CHCTR_ICEN,d0
        beq     mn10300_local_icache_inv_end
 
-#if defined(CONFIG_AM33_2) || defined(CONFIG_AM33_3)
-       LOCAL_CLI_SAVE(d1)
-
-       # disable the icache
-       and     ~CHCTR_ICEN,d0
-       movhu   d0,(a0)
-
-       # and wait for it to calm down
-       setlb
-       movhu   (a0),d0
-       btst    CHCTR_ICBUSY,d0
-       lne
-
-       # invalidate
-       or      CHCTR_ICINV,d0
-       movhu   d0,(a0)
-
-       # wait for the cache to finish
-       mov     CHCTR,a0
-       setlb
-       movhu   (a0),d0
-       btst    CHCTR_ICBUSY,d0
-       lne
-
-       # and reenable it
-       and     ~CHCTR_ICINV,d0
-       or      CHCTR_ICEN,d0
-       movhu   d0,(a0)
-       movhu   (a0),d0
-
-       LOCAL_IRQ_RESTORE(d1)
-#else /* CONFIG_AM33_2 || CONFIG_AM33_3 */
-       # invalidate
-       or      CHCTR_ICINV,d0
-       movhu   d0,(a0)
-       movhu   (a0),d0
-#endif /* CONFIG_AM33_2 || CONFIG_AM33_3 */
+       invalidate_icache 1
 
 mn10300_local_icache_inv_end:
        ret     [],0
@@ -128,43 +93,7 @@ mn10300_local_dcache_inv:
        btst    CHCTR_DCEN,d0
        beq     mn10300_local_dcache_inv_end
 
-#if defined(CONFIG_AM33_2) || defined(CONFIG_AM33_3)
-       LOCAL_CLI_SAVE(d1)
-
-       # disable the dcache
-       and     ~CHCTR_DCEN,d0
-       movhu   d0,(a0)
-
-       # and wait for it to calm down
-       setlb
-       movhu   (a0),d0
-       btst    CHCTR_DCBUSY,d0
-       lne
-
-       # invalidate
-       or      CHCTR_DCINV,d0
-       movhu   d0,(a0)
-
-       # wait for the cache to finish
-       mov     CHCTR,a0
-       setlb
-       movhu   (a0),d0
-       btst    CHCTR_DCBUSY,d0
-       lne
-
-       # and reenable it
-       and     ~CHCTR_DCINV,d0
-       or      CHCTR_DCEN,d0
-       movhu   d0,(a0)
-       movhu   (a0),d0
-
-       LOCAL_IRQ_RESTORE(d1)
-#else /* CONFIG_AM33_2 || CONFIG_AM33_3 */
-       # invalidate
-       or      CHCTR_DCINV,d0
-       movhu   d0,(a0)
-       movhu   (a0),d0
-#endif /* CONFIG_AM33_2 || CONFIG_AM33_3 */
+       invalidate_dcache 1
 
 mn10300_local_dcache_inv_end:
        ret     [],0
@@ -195,9 +124,9 @@ mn10300_local_dcache_inv_range:
        # and if they're not cacheline-aligned, we must flush any bits outside
        # the range that share cachelines with stuff inside the range
 #ifdef CONFIG_MN10300_CACHE_WBACK
-       btst    ~(L1_CACHE_BYTES-1),d0
+       btst    ~L1_CACHE_TAG_MASK,d0
        bne     1f
-       btst    ~(L1_CACHE_BYTES-1),d1
+       btst    ~L1_CACHE_TAG_MASK,d1
        beq     2f
 1:
        bra     mn10300_local_dcache_flush_inv_range
@@ -212,11 +141,10 @@ mn10300_local_dcache_inv_range:
        beq     mn10300_local_dcache_inv_range_end
 
 #ifndef CONFIG_MN10300_CACHE_WBACK
-       and     L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0      # round start
-                                                               # addr down
+       and     L1_CACHE_TAG_MASK,d0            # round start addr down
 
        add     L1_CACHE_BYTES,d1               # round end addr up
-       and     L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d1
+       and     L1_CACHE_TAG_MASK,d1
 #endif /* !CONFIG_MN10300_CACHE_WBACK */
        mov     d0,a1
 
diff --git a/arch/mn10300/mm/cache.inc b/arch/mn10300/mm/cache.inc
new file mode 100644 (file)
index 0000000..394a119
--- /dev/null
@@ -0,0 +1,133 @@
+/* MN10300 CPU core caching macros -*- asm -*-
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+
+###############################################################################
+#
+# Invalidate the instruction cache.
+#      A0: Should hold CHCTR
+#      D0: Should have been read from CHCTR
+#      D1: Will be clobbered
+#
+# On some cores it is necessary to disable the icache whilst we do this.
+#
+###############################################################################
+       .macro invalidate_icache,disable_irq
+
+#if defined(CONFIG_AM33_2) || defined(CONFIG_AM33_3)
+       .if \disable_irq
+       # don't want an interrupt routine seeing a disabled cache
+       mov     epsw,d1
+       and     ~EPSW_IE,epsw
+       or      EPSW_NMID,epsw
+       nop
+       nop
+       .endif
+
+       # disable the icache
+       and     ~CHCTR_ICEN,d0
+       movhu   d0,(a0)
+
+       # and wait for it to calm down
+       setlb
+       movhu   (a0),d0
+       btst    CHCTR_ICBUSY,d0
+       lne
+
+       # invalidate
+       or      CHCTR_ICINV,d0
+       movhu   d0,(a0)
+
+       # wait for the cache to finish
+       setlb
+       movhu   (a0),d0
+       btst    CHCTR_ICBUSY,d0
+       lne
+
+       # and reenable it
+       or      CHCTR_ICEN,d0
+       movhu   d0,(a0)
+       movhu   (a0),d0
+
+       .if \disable_irq
+       LOCAL_IRQ_RESTORE(d1)
+       .endif
+
+#else /* CONFIG_AM33_2 || CONFIG_AM33_3 */
+
+       # invalidate
+       or      CHCTR_ICINV,d0
+       movhu   d0,(a0)
+       movhu   (a0),d0
+
+#endif /* CONFIG_AM33_2 || CONFIG_AM33_3 */
+       .endm
+
+###############################################################################
+#
+# Invalidate the data cache.
+#      A0: Should hold CHCTR
+#      D0: Should have been read from CHCTR
+#      D1: Will be clobbered
+#
+# On some cores it is necessary to disable the dcache whilst we do this.
+#
+###############################################################################
+       .macro invalidate_dcache,disable_irq
+
+#if defined(CONFIG_AM33_2) || defined(CONFIG_AM33_3)
+       .if \disable_irq
+       # don't want an interrupt routine seeing a disabled cache
+       mov     epsw,d1
+       and     ~EPSW_IE,epsw
+       or      EPSW_NMID,epsw
+       nop
+       nop
+       .endif
+       
+       # disable the dcache
+       and     ~CHCTR_DCEN,d0
+       movhu   d0,(a0)
+
+       # and wait for it to calm down
+       setlb
+       movhu   (a0),d0
+       btst    CHCTR_DCBUSY,d0
+       lne
+
+       # invalidate
+       or      CHCTR_DCINV,d0
+       movhu   d0,(a0)
+
+       # wait for the cache to finish
+       setlb
+       movhu   (a0),d0
+       btst    CHCTR_DCBUSY,d0
+       lne
+
+       # and reenable it
+       or      CHCTR_DCEN,d0
+       movhu   d0,(a0)
+       movhu   (a0),d0
+
+       .if \disable_irq
+       LOCAL_IRQ_RESTORE(d1)
+       .endif
+
+#else /* CONFIG_AM33_2 || CONFIG_AM33_3 */
+
+       # invalidate
+       or      CHCTR_DCINV,d0
+       movhu   d0,(a0)
+       movhu   (a0),d0
+
+#endif /* CONFIG_AM33_2 || CONFIG_AM33_3 */
+       .endm
index 59c3da49d9d9e4ad08108bf2f1eebe8182f46e0c..0945409a802219cb8b1d7f09ef5410941a4756c0 100644 (file)
@@ -28,8 +28,9 @@
 #include <asm/uaccess.h>
 #include <asm/pgalloc.h>
 #include <asm/hardirq.h>
-#include <asm/gdb-stub.h>
 #include <asm/cpu-regs.h>
+#include <asm/debugger.h>
+#include <asm/gdb-stub.h>
 
 /*
  * Unlock any spinlocks which will prevent us from getting the
@@ -306,10 +307,8 @@ no_context:
        printk(" printing pc:\n");
        printk(KERN_ALERT "%08lx\n", regs->pc);
 
-#ifdef CONFIG_GDBSTUB
-       gdbstub_intercept(
-               regs, fault_code & 0x00010000 ? EXCEP_IAERROR : EXCEP_DAERROR);
-#endif
+       debugger_intercept(fault_code & 0x00010000 ? EXCEP_IAERROR : EXCEP_DAERROR,
+                          SIGSEGV, SEGV_ACCERR, regs);
 
        page = PTBR;
        page = ((unsigned long *) __va(page))[address >> 22];
index c1528004163ce8fe54418368ce3aea757e31104f..967d144f307e5572d24b06e78ff50298366ce331 100644 (file)
@@ -23,6 +23,7 @@
 #define L1_CACHE_TAG_DIRTY     0x00000008      /* data cache tag dirty bit */
 #define L1_CACHE_TAG_ENTRY     0x00000ff0      /* cache tag entry address mask */
 #define L1_CACHE_TAG_ADDRESS   0xfffff000      /* cache tag line address mask */
+#define L1_CACHE_TAG_MASK      +(L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY)
 
 /*
  * specification of the interval between interrupt checking intervals whilst
index cafd7b5b55b444836a7714793eb9725c3e4f5740..bcb5df2d892f99c76012c491707a64223fa61784 100644 (file)
@@ -29,6 +29,7 @@
 #define L1_CACHE_TAG_DIRTY     0x00000008      /* data cache tag dirty bit */
 #define L1_CACHE_TAG_ENTRY     0x00000fe0      /* cache tag entry address mask */
 #define L1_CACHE_TAG_ADDRESS   0xfffff000      /* cache tag line address mask */
+#define L1_CACHE_TAG_MASK      +(L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY)
 
 /*
  * specification of the interval between interrupt checking intervals whilst
index 092e4250a4583f735140221d5f4dd7a4667d3d69..10ca03d0a250e280682479c4228536120c2cee4f 100644 (file)
@@ -297,6 +297,7 @@ extern int
 kgdb_handle_exception(int ex_vector, int signo, int err_code,
                      struct pt_regs *regs);
 extern int kgdb_nmicallback(int cpu, void *regs);
+extern void gdbstub_exit(int status);
 
 extern int                     kgdb_single_step;
 extern atomic_t                        kgdb_active;
index 481a7bd2dfe752267ba3273a1bb8e4a972c17fa5..a11db956dd62c4c5a5fa22699ccd3662a88f00bc 100644 (file)
@@ -1093,3 +1093,33 @@ int gdbstub_state(struct kgdb_state *ks, char *cmd)
        put_packet(remcom_out_buffer);
        return 0;
 }
+
+/**
+ * gdbstub_exit - Send an exit message to GDB
+ * @status: The exit code to report.
+ */
+void gdbstub_exit(int status)
+{
+       unsigned char checksum, ch, buffer[3];
+       int loop;
+
+       buffer[0] = 'W';
+       buffer[1] = hex_asc_hi(status);
+       buffer[2] = hex_asc_lo(status);
+
+       dbg_io_ops->write_char('$');
+       checksum = 0;
+
+       for (loop = 0; loop < 3; loop++) {
+               ch = buffer[loop];
+               checksum += ch;
+               dbg_io_ops->write_char(ch);
+       }
+
+       dbg_io_ops->write_char('#');
+       dbg_io_ops->write_char(hex_asc_hi(checksum));
+       dbg_io_ops->write_char(hex_asc_lo(checksum));
+
+       /* make sure the output is flushed, lest the bootloader clobber it */
+       dbg_io_ops->flush();
+}