diff options
Diffstat (limited to 'arch/mn10300/kernel')
31 files changed, 1456 insertions, 1378 deletions
diff --git a/arch/mn10300/kernel/Makefile b/arch/mn10300/kernel/Makefile index 8f5f1e81baf..561029f7fa4 100644 --- a/arch/mn10300/kernel/Makefile +++ b/arch/mn10300/kernel/Makefile @@ -1,14 +1,15 @@ # # Makefile for the MN10300-specific core kernel code # -extra-y := head.o init_task.o vmlinux.lds +extra-y := head.o vmlinux.lds fpu-obj-y := fpu-nofpu.o fpu-nofpu-low.o fpu-obj-$(CONFIG_FPU) := fpu.o fpu-low.o obj-y := process.o signal.o entry.o traps.o irq.o \ - ptrace.o setup.o time.o sys_mn10300.o io.o kthread.o \ - switch_to.o mn10300_ksyms.o kernel_execve.o $(fpu-obj-y) + ptrace.o setup.o time.o sys_mn10300.o io.o \ + switch_to.o mn10300_ksyms.o $(fpu-obj-y) \ + csrc-mn10300.o cevt-mn10300.o obj-$(CONFIG_SMP) += smp.o smp-low.o @@ -20,13 +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_CSRC_MN10300) += csrc-mn10300.o -obj-$(CONFIG_CEVT_MN10300) += cevt-mn10300.o +obj-$(CONFIG_KGDB) += kgdb.o diff --git a/arch/mn10300/kernel/asm-offsets.c b/arch/mn10300/kernel/asm-offsets.c index 96f24fab7de..47b3bb0c04f 100644 --- a/arch/mn10300/kernel/asm-offsets.c +++ b/arch/mn10300/kernel/asm-offsets.c @@ -96,7 +96,7 @@ void foo(void) OFFSET(__rx_outp, mn10300_serial_port, rx_outp); OFFSET(__uart_state, mn10300_serial_port, uart.state); OFFSET(__tx_xchar, mn10300_serial_port, tx_xchar); - OFFSET(__tx_break, mn10300_serial_port, tx_break); + OFFSET(__tx_flags, mn10300_serial_port, tx_flags); OFFSET(__intr_flags, mn10300_serial_port, intr_flags); OFFSET(__rx_icr, mn10300_serial_port, rx_icr); OFFSET(__tx_icr, mn10300_serial_port, tx_icr); diff --git a/arch/mn10300/kernel/cevt-mn10300.c b/arch/mn10300/kernel/cevt-mn10300.c index d4cb535bf78..60f64ca1752 100644 --- a/arch/mn10300/kernel/cevt-mn10300.c +++ b/arch/mn10300/kernel/cevt-mn10300.c @@ -70,6 +70,16 @@ static void event_handler(struct clock_event_device *dev) { } +static inline void setup_jiffies_interrupt(int irq, + struct irqaction *action) +{ + u16 tmp; + setup_irq(irq, action); + set_intr_level(irq, NUM2GxICR_LEVEL(CONFIG_TIMER_IRQ_LEVEL)); + GxICR(irq) |= GxICR_ENABLE | GxICR_DETECT | GxICR_REQUEST; + tmp = GxICR(irq); +} + int __init init_clockevents(void) { struct clock_event_device *cd; @@ -89,9 +99,10 @@ int __init init_clockevents(void) cd->name = "Timestamp"; cd->features = CLOCK_EVT_FEAT_ONESHOT; - /* Calculate the min / max delta */ - clockevent_set_clock(cd, MN10300_JCCLK); + /* Calculate shift/mult. We want to spawn at least 1 second */ + clockevents_calc_mult_shift(cd, MN10300_JCCLK, 1); + /* Calculate the min / max delta */ cd->max_delta_ns = clockevent_delta2ns(TMJCBR_MAX, cd); cd->min_delta_ns = clockevent_delta2ns(100, cd); @@ -102,7 +113,7 @@ int __init init_clockevents(void) cd->set_next_event = next_event; iact = &per_cpu(timer_irq, cpu); - iact->flags = IRQF_DISABLED | IRQF_SHARED | IRQF_TIMER; + iact->flags = IRQF_SHARED | IRQF_TIMER; iact->handler = timer_interrupt; clockevents_register_device(cd); @@ -110,9 +121,9 @@ int __init init_clockevents(void) #if defined(CONFIG_SMP) && !defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) /* setup timer irq affinity so it only runs on this cpu */ { - struct irq_desc *desc; - desc = irq_to_desc(cd->irq); - cpumask_copy(desc->affinity, cpumask_of(cpu)); + struct irq_data *data; + data = irq_get_irq_data(cd->irq); + cpumask_copy(data->affinity, cpumask_of(cpu)); iact->flags |= IRQF_NOBALANCING; } #endif diff --git a/arch/mn10300/kernel/csrc-mn10300.c b/arch/mn10300/kernel/csrc-mn10300.c index ba2f0c4d6e0..45644cf18c4 100644 --- a/arch/mn10300/kernel/csrc-mn10300.c +++ b/arch/mn10300/kernel/csrc-mn10300.c @@ -29,7 +29,6 @@ static struct clocksource clocksource_mn10300 = { int __init init_clocksource(void) { startup_timestamp_counter(); - clocksource_set_clock(&clocksource_mn10300, MN10300_TSCCLK); - clocksource_register(&clocksource_mn10300); + clocksource_register_hz(&clocksource_mn10300, MN10300_TSCCLK); return 0; } diff --git a/arch/mn10300/kernel/entry.S b/arch/mn10300/kernel/entry.S index f00b9bafcd3..177d61de51c 100644 --- a/arch/mn10300/kernel/entry.S +++ b/arch/mn10300/kernel/entry.S @@ -15,7 +15,6 @@ #include <linux/sys.h> #include <linux/linkage.h> #include <asm/smp.h> -#include <asm/system.h> #include <asm/irqflags.h> #include <asm/thread_info.h> #include <asm/intctl-regs.h> @@ -56,6 +55,16 @@ ENTRY(ret_from_fork) mov d0,(REG_D0,fp) jmp syscall_exit +ENTRY(ret_from_kernel_thread) + call schedule_tail[],0 + mov (REG_D0,fp),d0 + mov (REG_A0,fp),a0 + calls (a0) + GET_THREAD_INFO a2 # A2 must be set on return from sys_exit() + clr d0 + mov d0,(REG_D0,fp) + jmp syscall_exit + ############################################################################### # # system call handler @@ -95,10 +104,14 @@ restore_all: ############################################################################### ALIGN syscall_exit_work: - btst _TIF_SYSCALL_TRACE,d2 - beq work_pending + mov (REG_EPSW,fp),d0 + and EPSW_nSL,d0 + beq resume_kernel # returning to supervisor mode + LOCAL_IRQ_ENABLE # could let syscall_trace_exit() call # schedule() instead + btst _TIF_SYSCALL_TRACE,d2 + beq work_pending mov fp,d0 call syscall_trace_exit[],0 # do_syscall_trace(regs) jmp resume_userspace @@ -111,6 +124,7 @@ work_pending: work_resched: call schedule[],0 +resume_userspace: # make sure we don't miss an interrupt setting need_resched or # sigpending between sampling and the rti LOCAL_IRQ_DISABLE @@ -119,6 +133,8 @@ work_resched: mov (TI_flags,a2),d2 btst _TIF_WORK_MASK,d2 beq restore_all + + LOCAL_IRQ_ENABLE btst _TIF_NEED_RESCHED,d2 bne work_resched @@ -155,21 +171,10 @@ ret_from_intr: mov (REG_EPSW,fp),d0 # need to deliver signals before # returning to userspace and EPSW_nSL,d0 - beq resume_kernel # returning to supervisor mode - -ENTRY(resume_userspace) - # make sure we don't miss an interrupt setting need_resched or - # sigpending between sampling and the rti - LOCAL_IRQ_DISABLE - - # is there any work to be done on int/exception return? - mov (TI_flags,a2),d2 - btst _TIF_WORK_MASK,d2 - bne work_pending - jmp restore_all + bne resume_userspace # returning to userspace #ifdef CONFIG_PREEMPT -ENTRY(resume_kernel) +resume_kernel: LOCAL_IRQ_DISABLE mov (TI_preempt_count,a2),d0 # non-zero preempt_count ? cmp 0,d0 @@ -184,6 +189,8 @@ need_resched: bne restore_all call preempt_schedule_irq[],0 jmp need_resched +#else + jmp resume_kernel #endif @@ -266,7 +273,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 +292,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 +306,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) @@ -606,7 +596,7 @@ ENTRY(sys_call_table) .long sys_ni_syscall /* vm86 */ .long sys_ni_syscall /* Old sys_query_module */ .long sys_poll - .long sys_nfsservctl + .long sys_ni_syscall /* was nfsservctl */ .long sys_setresgid16 /* 170 */ .long sys_getresgid16 .long sys_prctl @@ -776,6 +766,7 @@ ENTRY(sys_call_table) .long sys_rt_tgsigqueueinfo .long sys_perf_event_open .long sys_recvmmsg + .long sys_setns nr_syscalls=(.-sys_call_table)/4 diff --git a/arch/mn10300/kernel/fpu.c b/arch/mn10300/kernel/fpu.c index 5f9c3fa19a8..064fa194de2 100644 --- a/arch/mn10300/kernel/fpu.c +++ b/arch/mn10300/kernel/fpu.c @@ -12,7 +12,6 @@ #include <asm/fpu.h> #include <asm/elf.h> #include <asm/exceptions.h> -#include <asm/system.h> #ifdef CONFIG_LAZY_SAVE_FPU struct task_struct *fpu_state_owner; @@ -70,24 +69,6 @@ asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code) } /* - * 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 */ int fpu_setup_sigcontext(struct fpucontext *fpucontext) diff --git a/arch/mn10300/kernel/gdb-cache.S b/arch/mn10300/kernel/gdb-cache.S deleted file mode 100644 index 1108badc3d3..00000000000 --- a/arch/mn10300/kernel/gdb-cache.S +++ /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 diff --git a/arch/mn10300/kernel/gdb-io-serial.c b/arch/mn10300/kernel/gdb-io-serial.c index f28dc99c6f7..df51242744c 100644 --- a/arch/mn10300/kernel/gdb-io-serial.c +++ b/arch/mn10300/kernel/gdb-io-serial.c @@ -18,7 +18,6 @@ #include <linux/nmi.h> #include <asm/pgtable.h> -#include <asm/system.h> #include <asm/gdb-stub.h> #include <asm/exceptions.h> #include <asm/serial-regs.h> diff --git a/arch/mn10300/kernel/gdb-io-ttysm.c b/arch/mn10300/kernel/gdb-io-ttysm.c index abdeea153c8..caae8cac9db 100644 --- a/arch/mn10300/kernel/gdb-io-ttysm.c +++ b/arch/mn10300/kernel/gdb-io-ttysm.c @@ -17,7 +17,6 @@ #include <linux/init.h> #include <linux/tty.h> #include <asm/pgtable.h> -#include <asm/system.h> #include <asm/gdb-stub.h> #include <asm/exceptions.h> #include <unit/clock.h> @@ -59,10 +58,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 +87,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)); } /* diff --git a/arch/mn10300/kernel/gdb-stub.c b/arch/mn10300/kernel/gdb-stub.c index b169d99d9f2..a128c57b586 100644 --- a/arch/mn10300/kernel/gdb-stub.c +++ b/arch/mn10300/kernel/gdb-stub.c @@ -130,10 +130,9 @@ #include <linux/bug.h> #include <asm/pgtable.h> -#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 +404,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 +729,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 @@ -796,7 +797,7 @@ unsigned char *mem2hex(const void *_mem, char *buf, int count, int may_fault) if ((u32) mem & 1 && count >= 1) { if (gdbstub_read_byte(mem, ch) != 0) return 0; - buf = pack_hex_byte(buf, ch[0]); + buf = hex_byte_pack(buf, ch[0]); mem++; count--; } @@ -804,8 +805,8 @@ unsigned char *mem2hex(const void *_mem, char *buf, int count, int may_fault) if ((u32) mem & 3 && count >= 2) { if (gdbstub_read_word(mem, ch) != 0) return 0; - buf = pack_hex_byte(buf, ch[0]); - buf = pack_hex_byte(buf, ch[1]); + buf = hex_byte_pack(buf, ch[0]); + buf = hex_byte_pack(buf, ch[1]); mem += 2; count -= 2; } @@ -813,10 +814,10 @@ unsigned char *mem2hex(const void *_mem, char *buf, int count, int may_fault) while (count >= 4) { if (gdbstub_read_dword(mem, ch) != 0) return 0; - buf = pack_hex_byte(buf, ch[0]); - buf = pack_hex_byte(buf, ch[1]); - buf = pack_hex_byte(buf, ch[2]); - buf = pack_hex_byte(buf, ch[3]); + buf = hex_byte_pack(buf, ch[0]); + buf = hex_byte_pack(buf, ch[1]); + buf = hex_byte_pack(buf, ch[2]); + buf = hex_byte_pack(buf, ch[3]); mem += 4; count -= 4; } @@ -824,8 +825,8 @@ unsigned char *mem2hex(const void *_mem, char *buf, int count, int may_fault) if (count >= 2) { if (gdbstub_read_word(mem, ch) != 0) return 0; - buf = pack_hex_byte(buf, ch[0]); - buf = pack_hex_byte(buf, ch[1]); + buf = hex_byte_pack(buf, ch[0]); + buf = hex_byte_pack(buf, ch[1]); mem += 2; count -= 2; } @@ -833,7 +834,7 @@ unsigned char *mem2hex(const void *_mem, char *buf, int count, int may_fault) if (count >= 1) { if (gdbstub_read_byte(mem, ch) != 0) return 0; - buf = pack_hex_byte(buf, ch[0]); + buf = hex_byte_pack(buf, ch[0]); } *buf = 0; @@ -1171,7 +1172,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 +1187,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 +1196,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 +1209,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; @@ -1269,13 +1272,13 @@ static int gdbstub(struct pt_regs *regs, enum exception_code excep) ptr = mem2hex(title, ptr, sizeof(title) - 1, 0); hx = hex_asc_hi(excep >> 8); - ptr = pack_hex_byte(ptr, hx); + ptr = hex_byte_pack(ptr, hx); hx = hex_asc_lo(excep >> 8); - ptr = pack_hex_byte(ptr, hx); + ptr = hex_byte_pack(ptr, hx); hx = hex_asc_hi(excep); - ptr = pack_hex_byte(ptr, hx); + ptr = hex_byte_pack(ptr, hx); hx = hex_asc_lo(excep); - ptr = pack_hex_byte(ptr, hx); + ptr = hex_byte_pack(ptr, hx); ptr = mem2hex(crlf, ptr, sizeof(crlf) - 1, 0); *ptr = 0; @@ -1287,21 +1290,21 @@ static int gdbstub(struct pt_regs *regs, enum exception_code excep) ptr = mem2hex(tbcberr, ptr, sizeof(tbcberr) - 1, 0); hx = hex_asc_hi(bcberr >> 24); - ptr = pack_hex_byte(ptr, hx); + ptr = hex_byte_pack(ptr, hx); hx = hex_asc_lo(bcberr >> 24); - ptr = pack_hex_byte(ptr, hx); + ptr = hex_byte_pack(ptr, hx); hx = hex_asc_hi(bcberr >> 16); - ptr = pack_hex_byte(ptr, hx); + ptr = hex_byte_pack(ptr, hx); hx = hex_asc_lo(bcberr >> 16); - ptr = pack_hex_byte(ptr, hx); + ptr = hex_byte_pack(ptr, hx); hx = hex_asc_hi(bcberr >> 8); - ptr = pack_hex_byte(ptr, hx); + ptr = hex_byte_pack(ptr, hx); hx = hex_asc_lo(bcberr >> 8); - ptr = pack_hex_byte(ptr, hx); + ptr = hex_byte_pack(ptr, hx); hx = hex_asc_hi(bcberr); - ptr = pack_hex_byte(ptr, hx); + ptr = hex_byte_pack(ptr, hx); hx = hex_asc_lo(bcberr); - ptr = pack_hex_byte(ptr, hx); + ptr = hex_byte_pack(ptr, hx); ptr = mem2hex(crlf, ptr, sizeof(crlf) - 1, 0); *ptr = 0; @@ -1317,12 +1320,12 @@ static int gdbstub(struct pt_regs *regs, enum exception_code excep) * Send trap type (converted to signal) */ *ptr++ = 'T'; - ptr = pack_hex_byte(ptr, sigval); + ptr = hex_byte_pack(ptr, sigval); /* * Send Error PC */ - ptr = pack_hex_byte(ptr, GDB_REGID_PC); + ptr = hex_byte_pack(ptr, GDB_REGID_PC); *ptr++ = ':'; ptr = mem2hex(®s->pc, ptr, 4, 0); *ptr++ = ';'; @@ -1330,7 +1333,7 @@ static int gdbstub(struct pt_regs *regs, enum exception_code excep) /* * Send frame pointer */ - ptr = pack_hex_byte(ptr, GDB_REGID_FP); + ptr = hex_byte_pack(ptr, GDB_REGID_FP); *ptr++ = ':'; ptr = mem2hex(®s->a3, ptr, 4, 0); *ptr++ = ';'; @@ -1339,7 +1342,7 @@ static int gdbstub(struct pt_regs *regs, enum exception_code excep) * Send stack pointer */ ssp = (unsigned long) (regs + 1); - ptr = pack_hex_byte(ptr, GDB_REGID_SP); + ptr = hex_byte_pack(ptr, GDB_REGID_SP); *ptr++ = ':'; ptr = mem2hex(&ssp, ptr, 4, 0); *ptr++ = ';'; @@ -1548,17 +1551,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 +1664,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 +1674,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 +1704,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 +1738,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; } diff --git a/arch/mn10300/kernel/init_task.c b/arch/mn10300/kernel/init_task.c deleted file mode 100644 index a481b043bea..00000000000 --- a/arch/mn10300/kernel/init_task.c +++ /dev/null @@ -1,39 +0,0 @@ -/* MN10300 Initial task definitions - * - * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. - * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. - * - * 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/mm.h> -#include <linux/module.h> -#include <linux/sched.h> -#include <linux/init.h> -#include <linux/init_task.h> -#include <linux/fs.h> -#include <linux/mqueue.h> -#include <asm/uaccess.h> -#include <asm/pgtable.h> - -static struct signal_struct init_signals = INIT_SIGNALS(init_signals); -static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); -/* - * Initial thread structure. - * - * We need to make sure that this is THREAD_SIZE aligned due to the - * way process stacks are handled. This is done by having a special - * "init_task" linker map entry.. - */ -union thread_union init_thread_union __init_task_data = - { INIT_THREAD_INFO(init_task) }; - -/* - * Initial task structure. - * - * All other task structs will be allocated on slabs in fork.c - */ -struct task_struct init_task = INIT_TASK(init_task); -EXPORT_SYMBOL(init_task); diff --git a/arch/mn10300/kernel/internal.h b/arch/mn10300/kernel/internal.h index 6a064ab5af0..561785581f6 100644 --- a/arch/mn10300/kernel/internal.h +++ b/arch/mn10300/kernel/internal.h @@ -9,18 +9,16 @@ * 2 of the Licence, or (at your option) any later version. */ +#include <linux/irqreturn.h> + struct clocksource; struct clock_event_device; /* - * kthread.S - */ -extern int kernel_thread_helper(int); - -/* * entry.S */ extern void ret_from_fork(struct task_struct *) __attribute__((noreturn)); +extern void ret_from_kernel_thread(struct task_struct *) __attribute__((noreturn)); /* * smp-low.S @@ -30,16 +28,13 @@ extern void mn10300_low_ipi_handler(void); #endif /* - * time.c + * smp.c */ -extern irqreturn_t local_timer_interrupt(void); +#ifdef CONFIG_SMP +extern void smp_jump_to_debugger(void); +#endif /* * time.c */ -#ifdef CONFIG_CEVT_MN10300 -extern void clockevent_set_clock(struct clock_event_device *, unsigned int); -#endif -#ifdef CONFIG_CSRC_MN10300 -extern void clocksource_set_clock(struct clocksource *, unsigned int); -#endif +extern irqreturn_t local_timer_interrupt(void); diff --git a/arch/mn10300/kernel/irq.c b/arch/mn10300/kernel/irq.c index ac11754ecec..6ab3b73efcf 100644 --- a/arch/mn10300/kernel/irq.c +++ b/arch/mn10300/kernel/irq.c @@ -37,8 +37,9 @@ atomic_t irq_err_count; /* * MN10300 interrupt controller operations */ -static void mn10300_cpupic_ack(unsigned int irq) +static void mn10300_cpupic_ack(struct irq_data *d) { + unsigned int irq = d->irq; unsigned long flags; u16 tmp; @@ -61,13 +62,14 @@ static void __mask_and_set_icr(unsigned int irq, arch_local_irq_restore(flags); } -static void mn10300_cpupic_mask(unsigned int irq) +static void mn10300_cpupic_mask(struct irq_data *d) { - __mask_and_set_icr(irq, GxICR_LEVEL, 0); + __mask_and_set_icr(d->irq, GxICR_LEVEL, 0); } -static void mn10300_cpupic_mask_ack(unsigned int irq) +static void mn10300_cpupic_mask_ack(struct irq_data *d) { + unsigned int irq = d->irq; #ifdef CONFIG_SMP unsigned long flags; u16 tmp; @@ -85,7 +87,7 @@ static void mn10300_cpupic_mask_ack(unsigned int irq) tmp2 = GxICR(irq); irq_affinity_online[irq] = - any_online_cpu(*irq_desc[irq].affinity); + cpumask_any_and(d->affinity, cpu_online_mask); CROSS_GxICR(irq, irq_affinity_online[irq]) = (tmp & (GxICR_LEVEL | GxICR_ENABLE)) | GxICR_DETECT; tmp = CROSS_GxICR(irq, irq_affinity_online[irq]); @@ -97,13 +99,14 @@ static void mn10300_cpupic_mask_ack(unsigned int irq) #endif /* CONFIG_SMP */ } -static void mn10300_cpupic_unmask(unsigned int irq) +static void mn10300_cpupic_unmask(struct irq_data *d) { - __mask_and_set_icr(irq, GxICR_LEVEL, GxICR_ENABLE); + __mask_and_set_icr(d->irq, GxICR_LEVEL, GxICR_ENABLE); } -static void mn10300_cpupic_unmask_clear(unsigned int irq) +static void mn10300_cpupic_unmask_clear(struct irq_data *d) { + unsigned int irq = d->irq; /* the MN10300 PIC latches its interrupt request bit, even after the * device has ceased to assert its interrupt line and the interrupt * channel has been disabled in the PIC, so for level-triggered @@ -121,7 +124,8 @@ static void mn10300_cpupic_unmask_clear(unsigned int irq) } else { tmp = GxICR(irq); - irq_affinity_online[irq] = any_online_cpu(*irq_desc[irq].affinity); + irq_affinity_online[irq] = cpumask_any_and(d->affinity, + cpu_online_mask); CROSS_GxICR(irq, irq_affinity_online[irq]) = (tmp & GxICR_LEVEL) | GxICR_ENABLE | GxICR_DETECT; tmp = CROSS_GxICR(irq, irq_affinity_online[irq]); } @@ -134,60 +138,15 @@ static void mn10300_cpupic_unmask_clear(unsigned int irq) #ifdef CONFIG_SMP static int -mn10300_cpupic_setaffinity(unsigned int irq, const struct cpumask *mask) +mn10300_cpupic_setaffinity(struct irq_data *d, const struct cpumask *mask, + bool force) { unsigned long flags; - int err; flags = arch_local_cli_save(); - - /* check irq no */ - switch (irq) { - case TMJCIRQ: - case RESCHEDULE_IPI: - case CALL_FUNC_SINGLE_IPI: - case LOCAL_TIMER_IPI: - case FLUSH_CACHE_IPI: - case CALL_FUNCTION_NMI_IPI: - case GDB_NMI_IPI: -#ifdef CONFIG_MN10300_TTYSM0 - case SC0RXIRQ: - case SC0TXIRQ: -#ifdef CONFIG_MN10300_TTYSM0_TIMER8 - case TM8IRQ: -#elif CONFIG_MN10300_TTYSM0_TIMER2 - case TM2IRQ: -#endif /* CONFIG_MN10300_TTYSM0_TIMER8 */ -#endif /* CONFIG_MN10300_TTYSM0 */ - -#ifdef CONFIG_MN10300_TTYSM1 - case SC1RXIRQ: - case SC1TXIRQ: -#ifdef CONFIG_MN10300_TTYSM1_TIMER12 - case TM12IRQ: -#elif CONFIG_MN10300_TTYSM1_TIMER9 - case TM9IRQ: -#elif CONFIG_MN10300_TTYSM1_TIMER3 - case TM3IRQ: -#endif /* CONFIG_MN10300_TTYSM1_TIMER12 */ -#endif /* CONFIG_MN10300_TTYSM1 */ - -#ifdef CONFIG_MN10300_TTYSM2 - case SC2RXIRQ: - case SC2TXIRQ: - case TM10IRQ: -#endif /* CONFIG_MN10300_TTYSM2 */ - err = -1; - break; - - default: - set_bit(irq, irq_affinity_request); - err = 0; - break; - } - + set_bit(d->irq, irq_affinity_request); arch_local_irq_restore(flags); - return err; + return 0; } #endif /* CONFIG_SMP */ @@ -202,15 +161,15 @@ mn10300_cpupic_setaffinity(unsigned int irq, const struct cpumask *mask) * mask_ack() is provided), and mask_ack() just masks. */ static struct irq_chip mn10300_cpu_pic_level = { - .name = "cpu_l", - .disable = mn10300_cpupic_mask, - .enable = mn10300_cpupic_unmask_clear, - .ack = NULL, - .mask = mn10300_cpupic_mask, - .mask_ack = mn10300_cpupic_mask, - .unmask = mn10300_cpupic_unmask_clear, + .name = "cpu_l", + .irq_disable = mn10300_cpupic_mask, + .irq_enable = mn10300_cpupic_unmask_clear, + .irq_ack = NULL, + .irq_mask = mn10300_cpupic_mask, + .irq_mask_ack = mn10300_cpupic_mask, + .irq_unmask = mn10300_cpupic_unmask_clear, #ifdef CONFIG_SMP - .set_affinity = mn10300_cpupic_setaffinity, + .irq_set_affinity = mn10300_cpupic_setaffinity, #endif }; @@ -220,15 +179,15 @@ static struct irq_chip mn10300_cpu_pic_level = { * We use the latch clearing function of the PIC as the 'ACK' function. */ static struct irq_chip mn10300_cpu_pic_edge = { - .name = "cpu_e", - .disable = mn10300_cpupic_mask, - .enable = mn10300_cpupic_unmask, - .ack = mn10300_cpupic_ack, - .mask = mn10300_cpupic_mask, - .mask_ack = mn10300_cpupic_mask_ack, - .unmask = mn10300_cpupic_unmask, + .name = "cpu_e", + .irq_disable = mn10300_cpupic_mask, + .irq_enable = mn10300_cpupic_unmask, + .irq_ack = mn10300_cpupic_ack, + .irq_mask = mn10300_cpupic_mask, + .irq_mask_ack = mn10300_cpupic_mask_ack, + .irq_unmask = mn10300_cpupic_unmask, #ifdef CONFIG_SMP - .set_affinity = mn10300_cpupic_setaffinity, + .irq_set_affinity = mn10300_cpupic_setaffinity, #endif }; @@ -252,39 +211,13 @@ void set_intr_level(int irq, u16 level) __mask_and_set_icr(irq, GxICR_ENABLE, level); } -void mn10300_intc_set_level(unsigned int irq, unsigned int level) -{ - set_intr_level(irq, NUM2GxICR_LEVEL(level) & GxICR_LEVEL); -} - -void mn10300_intc_clear(unsigned int irq) -{ - __mask_and_set_icr(irq, GxICR_LEVEL | GxICR_ENABLE, GxICR_DETECT); -} - -void mn10300_intc_set(unsigned int irq) -{ - __mask_and_set_icr(irq, 0, GxICR_REQUEST | GxICR_DETECT); -} - -void mn10300_intc_enable(unsigned int irq) -{ - mn10300_cpupic_unmask(irq); -} - -void mn10300_intc_disable(unsigned int irq) -{ - mn10300_cpupic_mask(irq); -} - /* * mark an interrupt to be ACK'd after interrupt handlers have been run rather * than before - * - see Documentation/mn10300/features.txt */ void mn10300_set_lateack_irq_type(int irq) { - set_irq_chip_and_handler(irq, &mn10300_cpu_pic_level, + irq_set_chip_and_handler(irq, &mn10300_cpu_pic_level, handle_level_irq); } @@ -296,12 +229,12 @@ void __init init_IRQ(void) int irq; for (irq = 0; irq < NR_IRQS; irq++) - if (irq_desc[irq].chip == &no_irq_chip) + if (irq_get_chip(irq) == &no_irq_chip) /* due to the PIC latching interrupt requests, even * when the IRQ is disabled, IRQ_PENDING is superfluous * and we can use handle_level_irq() for edge-triggered * interrupts */ - set_irq_chip_and_handler(irq, &mn10300_cpu_pic_edge, + irq_set_chip_and_handler(irq, &mn10300_cpu_pic_edge, handle_level_irq); unit_init_IRQ(); @@ -356,91 +289,42 @@ asmlinkage void do_IRQ(void) /* * Display interrupt management information through /proc/interrupts */ -int show_interrupts(struct seq_file *p, void *v) +int arch_show_interrupts(struct seq_file *p, int prec) { - int i = *(loff_t *) v, j, cpu; - struct irqaction *action; - unsigned long flags; - - switch (i) { - /* display column title bar naming CPUs */ - case 0: - seq_printf(p, " "); - for (j = 0; j < NR_CPUS; j++) - if (cpu_online(j)) - seq_printf(p, "CPU%d ", j); - seq_putc(p, '\n'); - break; - - /* display information rows, one per active CPU */ - case 1 ... NR_IRQS - 1: - raw_spin_lock_irqsave(&irq_desc[i].lock, flags); - - action = irq_desc[i].action; - if (action) { - seq_printf(p, "%3d: ", i); - for_each_present_cpu(cpu) - seq_printf(p, "%10u ", kstat_irqs_cpu(i, cpu)); - - if (i < NR_CPU_IRQS) - seq_printf(p, " %14s.%u", - irq_desc[i].chip->name, - (GxICR(i) & GxICR_LEVEL) >> - GxICR_LEVEL_SHIFT); - else - seq_printf(p, " %14s", - irq_desc[i].chip->name); - - seq_printf(p, " %s", action->name); - - for (action = action->next; - action; - action = action->next) - seq_printf(p, ", %s", action->name); - - seq_putc(p, '\n'); - } - - raw_spin_unlock_irqrestore(&irq_desc[i].lock, flags); - break; - - /* polish off with NMI and error counters */ - case NR_IRQS: #ifdef CONFIG_MN10300_WD_TIMER - seq_printf(p, "NMI: "); - for (j = 0; j < NR_CPUS; j++) - if (cpu_online(j)) - seq_printf(p, "%10u ", nmi_count(j)); - seq_putc(p, '\n'); -#endif + int j; - seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count)); - break; - } + seq_printf(p, "%*s: ", prec, "NMI"); + for (j = 0; j < NR_CPUS; j++) + if (cpu_online(j)) + seq_printf(p, "%10u ", nmi_count(j)); + seq_putc(p, '\n'); +#endif + seq_printf(p, "%*s: ", prec, "ERR"); + seq_printf(p, "%10u\n", atomic_read(&irq_err_count)); return 0; } #ifdef CONFIG_HOTPLUG_CPU void migrate_irqs(void) { - irq_desc_t *desc; int irq; unsigned int self, new; unsigned long flags; self = smp_processor_id(); for (irq = 0; irq < NR_IRQS; irq++) { - desc = irq_desc + irq; + struct irq_data *data = irq_get_irq_data(irq); - if (desc->status == IRQ_PER_CPU) + if (irqd_is_per_cpu(data)) continue; - if (cpu_isset(self, irq_desc[irq].affinity) && - !cpus_intersects(irq_affinity[irq], cpu_online_map)) { + if (cpumask_test_cpu(self, &data->affinity) && + !cpumask_intersects(&irq_affinity[irq], cpu_online_mask)) { int cpu_id; - cpu_id = first_cpu(cpu_online_map); - cpu_set(cpu_id, irq_desc[irq].affinity); + cpu_id = cpumask_first(cpu_online_mask); + cpumask_set_cpu(cpu_id, &data->affinity); } /* We need to operate irq_affinity_online atomically. */ arch_local_cli_save(flags); @@ -451,7 +335,8 @@ void migrate_irqs(void) GxICR(irq) = x & GxICR_LEVEL; tmp = GxICR(irq); - new = any_online_cpu(irq_desc[irq].affinity); + new = cpumask_any_and(&data->affinity, + cpu_online_mask); irq_affinity_online[irq] = new; CROSS_GxICR(irq, new) = diff --git a/arch/mn10300/kernel/kernel_execve.S b/arch/mn10300/kernel/kernel_execve.S deleted file mode 100644 index 86039f10526..00000000000 --- a/arch/mn10300/kernel/kernel_execve.S +++ /dev/null @@ -1,37 +0,0 @@ -/* MN10300 In-kernel program execution - * - * 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/linkage.h> -#include <asm/unistd.h> - -############################################################################### -# -# Do a system call from kernel instead of calling sys_execve so we end up with -# proper pt_regs. -# -# int kernel_execve(const char *filename, char *const argv[], -# char *const envp[]) -# -# On entry: D0/D1/8(SP): arguments to function -# On return: D0: syscall return. -# -############################################################################### - .globl kernel_execve - .type kernel_execve,@function -kernel_execve: - mov a3,a1 - mov d0,a0 - mov (12,sp),a3 - mov +__NR_execve,d0 - syscall 0 - mov a1,a3 - rets - - .size kernel_execve,.-kernel_execve diff --git a/arch/mn10300/kernel/kgdb.c b/arch/mn10300/kernel/kgdb.c new file mode 100644 index 00000000000..99770823451 --- /dev/null +++ b/arch/mn10300/kernel/kgdb.c @@ -0,0 +1,501 @@ +/* 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 arch_release_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(); + } +} + +/* + * 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 diff --git a/arch/mn10300/kernel/kthread.S b/arch/mn10300/kernel/kthread.S deleted file mode 100644 index b5ae467ac5e..00000000000 --- a/arch/mn10300/kernel/kthread.S +++ /dev/null @@ -1,31 +0,0 @@ -/* MN10300 Kernel thread trampoline function - * - * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. - * Written by Mark Salter (msalter@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. - */ - .text - -############################################################################### -# -# kernel_thread_helper - trampoline for kernel_thread() -# -# On entry: -# A2 = address of function to call -# D2 = function argument -# -############################################################################### - .globl kernel_thread_helper - .type kernel_thread_helper,@function -kernel_thread_helper: - mov do_exit,d1 - mov d1,(sp) - mov d1,mdr - mov d2,d0 - jmp (a2) - - .size kernel_thread_helper,.-kernel_thread_helper diff --git a/arch/mn10300/kernel/mn10300-serial-low.S b/arch/mn10300/kernel/mn10300-serial-low.S index dfc1b6f2fa9..b95e76caf4f 100644 --- a/arch/mn10300/kernel/mn10300-serial-low.S +++ b/arch/mn10300/kernel/mn10300-serial-low.S @@ -118,8 +118,8 @@ ENTRY(mn10300_serial_vdma_tx_handler) movbu d2,(e3) # ACK the interrupt movhu (e3),d2 # flush - btst 0x01,(__tx_break,a3) # handle transmit break request - bne mnsc_vdma_tx_break + btst 0xFF,(__tx_flags,a3) # handle transmit flags + bne mnsc_vdma_tx_flags movbu (SCxSTR,e2),d2 # don't try and transmit a char if the # buffer is not empty @@ -171,10 +171,13 @@ mnsc_vdma_tx_empty: bset MNSCx_TX_EMPTY,(__intr_flags,a3) bra mnsc_vdma_tx_done -mnsc_vdma_tx_break: +mnsc_vdma_tx_flags: + btst MNSCx_TX_STOP,(__tx_flags,a3) + bne mnsc_vdma_tx_stop movhu (SCxCTR,e2),d2 # turn on break mode or SC01CTR_BKE,d2 movhu d2,(SCxCTR,e2) +mnsc_vdma_tx_stop: mov +(NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL)|GxICR_DETECT),d2 movhu d2,(e3) # disable transmit interrupts on this # channel diff --git a/arch/mn10300/kernel/mn10300-serial.c b/arch/mn10300/kernel/mn10300-serial.c index 996384dba45..7ecf69879e2 100644 --- a/arch/mn10300/kernel/mn10300-serial.c +++ b/arch/mn10300/kernel/mn10300-serial.c @@ -36,7 +36,6 @@ static const char serial_revdate[] = "2007-11-06"; #include <linux/console.h> #include <linux/sysrq.h> -#include <asm/system.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/bitops.h> @@ -119,6 +118,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 +141,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); @@ -384,19 +391,51 @@ static void mn10300_serial_mask_ack(unsigned int irq) arch_local_irq_restore(flags); } -static void mn10300_serial_nop(unsigned int irq) +static void mn10300_serial_chip_mask_ack(struct irq_data *d) +{ + mn10300_serial_mask_ack(d->irq); +} + +static void mn10300_serial_nop(struct irq_data *d) { } static struct irq_chip mn10300_serial_pic = { .name = "mnserial", - .ack = mn10300_serial_mask_ack, - .mask = mn10300_serial_mask_ack, - .mask_ack = mn10300_serial_mask_ack, - .unmask = mn10300_serial_nop, - .end = mn10300_serial_nop, + .irq_ack = mn10300_serial_chip_mask_ack, + .irq_mask = mn10300_serial_chip_mask_ack, + .irq_mask_ack = mn10300_serial_chip_mask_ack, + .irq_unmask = mn10300_serial_nop, }; +static void mn10300_serial_low_mask(struct irq_data *d) +{ + unsigned long flags; + u16 tmp; + + flags = arch_local_cli_save(); + GxICR(d->irq) = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL); + tmp = GxICR(d->irq); /* flush write buffer */ + arch_local_irq_restore(flags); +} + +static void mn10300_serial_low_unmask(struct irq_data *d) +{ + unsigned long flags; + u16 tmp; + + flags = arch_local_cli_save(); + GxICR(d->irq) = + NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL) | GxICR_ENABLE; + tmp = GxICR(d->irq); /* flush write buffer */ + arch_local_irq_restore(flags); +} + +static struct irq_chip mn10300_serial_low_pic = { + .name = "mnserial-low", + .irq_mask = mn10300_serial_low_mask, + .irq_unmask = mn10300_serial_low_unmask, +}; /* * serial virtual DMA interrupt jump table @@ -405,25 +444,53 @@ struct mn10300_serial_int mn10300_serial_int_tbl[NR_IRQS]; static void mn10300_serial_dis_tx_intr(struct mn10300_serial_port *port) { - unsigned long flags; + int retries = 100; u16 x; - flags = arch_local_cli_save(); - *port->tx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL); - x = *port->tx_icr; - arch_local_irq_restore(flags); + /* nothing to do if irq isn't set up */ + if (!mn10300_serial_int_tbl[port->tx_irq].port) + return; + + port->tx_flags |= MNSCx_TX_STOP; + mb(); + + /* + * Here we wait for the irq to be disabled. Either it already is + * disabled or we wait some number of retries for the VDMA handler + * to disable it. The retries give the VDMA handler enough time to + * run to completion if it was already in progress. If the VDMA IRQ + * is enabled but the handler is not yet running when arrive here, + * the STOP flag will prevent the handler from conflicting with the + * driver code following this loop. + */ + while ((*port->tx_icr & GxICR_ENABLE) && retries-- > 0) + ; + if (retries <= 0) { + *port->tx_icr = + NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL); + x = *port->tx_icr; + } } static void mn10300_serial_en_tx_intr(struct mn10300_serial_port *port) { - unsigned long flags; u16 x; - flags = arch_local_cli_save(); + /* nothing to do if irq isn't set up */ + if (!mn10300_serial_int_tbl[port->tx_irq].port) + return; + + /* stop vdma irq if not already stopped */ + if (!(port->tx_flags & MNSCx_TX_STOP)) + mn10300_serial_dis_tx_intr(port); + + port->tx_flags &= ~MNSCx_TX_STOP; + mb(); + *port->tx_icr = - NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL) | GxICR_ENABLE; + NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL) | + GxICR_ENABLE | GxICR_REQUEST | GxICR_DETECT; x = *port->tx_icr; - arch_local_irq_restore(flags); } static void mn10300_serial_dis_rx_intr(struct mn10300_serial_port *port) @@ -457,7 +524,7 @@ static int mask_test_and_clear(volatile u8 *ptr, u8 mask) static void mn10300_serial_receive_interrupt(struct mn10300_serial_port *port) { struct uart_icount *icount = &port->uart.icount; - struct tty_struct *tty = port->uart.state->port.tty; + struct tty_port *tport = &port->uart.state->port; unsigned ix; int count; u8 st, ch, push, status, overrun; @@ -467,25 +534,26 @@ static void mn10300_serial_receive_interrupt(struct mn10300_serial_port *port) push = 0; count = CIRC_CNT(port->rx_inp, port->rx_outp, MNSC_BUFFER_SIZE); - count = tty_buffer_request_room(tty, count); + count = tty_buffer_request_room(tport, count); if (count == 0) { - if (!tty->low_latency) - tty_flip_buffer_push(tty); + if (!tport->low_latency) + tty_flip_buffer_push(tport); return; } try_again: /* pull chars out of the hat */ - ix = port->rx_outp; - if (ix == port->rx_inp) { - if (push && !tty->low_latency) - tty_flip_buffer_push(tty); + ix = ACCESS_ONCE(port->rx_outp); + if (CIRC_CNT(port->rx_inp, ix, MNSC_BUFFER_SIZE) == 0) { + if (push && !tport->low_latency) + tty_flip_buffer_push(tport); return; } + smp_read_barrier_depends(); ch = port->rx_buffer[ix++]; st = port->rx_buffer[ix++]; - smp_rmb(); + smp_mb(); port->rx_outp = ix & (MNSC_BUFFER_SIZE - 1); port->uart.icount.rx++; @@ -598,19 +666,19 @@ insert: else flag = TTY_NORMAL; - tty_insert_flip_char(tty, ch, flag); + tty_insert_flip_char(tport, ch, flag); } /* overrun is special, since it's reported immediately, and doesn't * affect the current character */ if (overrun) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_insert_flip_char(tport, 0, TTY_OVERRUN); count--; if (count <= 0) { - if (!tty->low_latency) - tty_flip_buffer_push(tty); + if (!tport->low_latency) + tty_flip_buffer_push(tport); return; } @@ -767,8 +835,6 @@ static void mn10300_serial_start_tx(struct uart_port *_port) struct mn10300_serial_port *port = container_of(_port, struct mn10300_serial_port, uart); - u16 x; - _enter("%s{%lu}", port->name, CIRC_CNT(&port->uart.state->xmit.head, @@ -776,14 +842,7 @@ static void mn10300_serial_start_tx(struct uart_port *_port) UART_XMIT_SIZE)); /* kick the virtual DMA controller */ - arch_local_cli(); - x = *port->tx_icr; - x |= GxICR_ENABLE; - - if (*port->_status & SC01STR_TBF) - x &= ~(GxICR_REQUEST | GxICR_DETECT); - else - x |= GxICR_REQUEST | GxICR_DETECT; + mn10300_serial_en_tx_intr(port); _debug("CTR=%04hx ICR=%02hx STR=%04x TMD=%02hx TBR=%04hx ICR=%04hx", *port->_control, *port->_intr, *port->_status, @@ -791,10 +850,6 @@ static void mn10300_serial_start_tx(struct uart_port *_port) (port->div_timer == MNSCx_DIV_TIMER_8BIT) ? *(volatile u8 *)port->_tmxbr : *port->_tmxbr, *port->tx_icr); - - *port->tx_icr = x; - x = *port->tx_icr; - arch_local_sti(); } /* @@ -804,13 +859,17 @@ static void mn10300_serial_send_xchar(struct uart_port *_port, char ch) { struct mn10300_serial_port *port = container_of(_port, struct mn10300_serial_port, uart); + unsigned long flags; _enter("%s,%02x", port->name, ch); if (likely(port->gdbstub)) { port->tx_xchar = ch; - if (ch) + if (ch) { + spin_lock_irqsave(&port->uart.lock, flags); mn10300_serial_en_tx_intr(port); + spin_unlock_irqrestore(&port->uart.lock, flags); + } } } @@ -871,18 +930,21 @@ static void mn10300_serial_break_ctl(struct uart_port *_port, int ctl) { struct mn10300_serial_port *port = container_of(_port, struct mn10300_serial_port, uart); + unsigned long flags; _enter("%s,%d", port->name, ctl); + spin_lock_irqsave(&port->uart.lock, flags); if (ctl) { /* tell the virtual DMA handler to assert BREAK */ - port->tx_break = 1; + port->tx_flags |= MNSCx_TX_BREAK; mn10300_serial_en_tx_intr(port); } else { - port->tx_break = 0; + port->tx_flags &= ~MNSCx_TX_BREAK; *port->_control &= ~SC01CTR_BKE; mn10300_serial_en_tx_intr(port); } + spin_unlock_irqrestore(&port->uart.lock, flags); } /* @@ -905,6 +967,7 @@ static int mn10300_serial_startup(struct uart_port *_port) return -ENOMEM; port->rx_inp = port->rx_outp = 0; + port->tx_flags = 0; /* finally, enable the device */ *port->_intr = SC01ICR_TI; @@ -917,22 +980,23 @@ static int mn10300_serial_startup(struct uart_port *_port) pint->port = port; pint->vdma = mn10300_serial_vdma_tx_handler; - set_intr_level(port->rx_irq, - NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL)); - set_intr_level(port->tx_irq, - NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL)); - set_irq_chip(port->tm_irq, &mn10300_serial_pic); + irq_set_chip(port->rx_irq, &mn10300_serial_low_pic); + irq_set_chip(port->tx_irq, &mn10300_serial_low_pic); + irq_set_chip(port->tm_irq, &mn10300_serial_pic); if (request_irq(port->rx_irq, mn10300_serial_interrupt, - IRQF_DISABLED, port->rx_name, port) < 0) + IRQF_NOBALANCING, + port->rx_name, port) < 0) goto error; if (request_irq(port->tx_irq, mn10300_serial_interrupt, - IRQF_DISABLED, port->tx_name, port) < 0) + IRQF_NOBALANCING, + port->tx_name, port) < 0) goto error2; if (request_irq(port->tm_irq, mn10300_serial_interrupt, - IRQF_DISABLED, port->tm_name, port) < 0) + IRQF_NOBALANCING, + port->tm_name, port) < 0) goto error3; mn10300_serial_mask_ack(port->tm_irq); @@ -953,14 +1017,22 @@ error: */ static void mn10300_serial_shutdown(struct uart_port *_port) { + unsigned long flags; u16 x; struct mn10300_serial_port *port = container_of(_port, struct mn10300_serial_port, uart); _enter("%s", port->name); + spin_lock_irqsave(&_port->lock, flags); + mn10300_serial_dis_tx_intr(port); + + *port->rx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL); + x = *port->rx_icr; + port->tx_flags = 0; + spin_unlock_irqrestore(&_port->lock, flags); + /* disable the serial port and its baud rate timer */ - port->tx_break = 0; *port->_control &= ~(SC01CTR_TXE | SC01CTR_RXE | SC01CTR_BKE); *port->_tmxmd = 0; @@ -975,12 +1047,8 @@ static void mn10300_serial_shutdown(struct uart_port *_port) free_irq(port->rx_irq, port); free_irq(port->tx_irq, port); - arch_local_cli(); - *port->rx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL); - x = *port->rx_icr; - *port->tx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL); - x = *port->tx_icr; - arch_local_sti(); + mn10300_serial_int_tbl[port->tx_irq].port = NULL; + mn10300_serial_int_tbl[port->rx_irq].port = NULL; } /* @@ -1306,7 +1374,8 @@ timer_okay: if ((new->c_cflag & CREAD) == 0) port->uart.ignore_status_mask |= (1 << TTY_NORMAL); - scxctr |= *port->_control & (SC01CTR_TXE | SC01CTR_RXE | SC01CTR_BKE); + scxctr |= SC01CTR_TXE | SC01CTR_RXE; + scxctr |= *port->_control & SC01CTR_BKE; *port->_control = scxctr; spin_unlock_irqrestore(&port->uart.lock, flags); @@ -1508,17 +1577,24 @@ static void mn10300_serial_console_write(struct console *co, { struct mn10300_serial_port *port; unsigned i; - u16 scxctr, txicr, tmp; + u16 scxctr; u8 tmxmd; + unsigned long flags; + int locked = 1; port = mn10300_serial_ports[co->index]; + local_irq_save(flags); + if (port->uart.sysrq) { + /* mn10300_serial_interrupt() already took the lock */ + locked = 0; + } else if (oops_in_progress) { + locked = spin_trylock(&port->uart.lock); + } else + spin_lock(&port->uart.lock); + /* firstly hijack the serial port from the "virtual DMA" controller */ - arch_local_cli(); - txicr = *port->tx_icr; - *port->tx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL); - tmp = *port->tx_icr; - arch_local_sti(); + mn10300_serial_dis_tx_intr(port); /* the transmitter may be disabled */ scxctr = *port->_control; @@ -1554,12 +1630,12 @@ static void mn10300_serial_console_write(struct console *co, while (*port->_status & SC01STR_TBF) continue; - *(u8 *) port->_txb = ch; + *port->_txb = ch; if (ch == 0x0a) { while (*port->_status & SC01STR_TBF) continue; - *(u8 *) port->_txb = 0xd; + *port->_txb = 0xd; } } @@ -1572,10 +1648,11 @@ static void mn10300_serial_console_write(struct console *co, if (!(scxctr & SC01CTR_TXE)) *port->_control = scxctr; - arch_local_cli(); - *port->tx_icr = txicr; - tmp = *port->tx_icr; - arch_local_sti(); + mn10300_serial_en_tx_intr(port); + + if (locked) + spin_unlock(&port->uart.lock); + local_irq_restore(flags); } /* @@ -1630,3 +1707,81 @@ 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); + + if (mn10300_serial_int_tbl[port->rx_irq].port != NULL) { + do { + /* pull chars out of the hat */ + ix = ACCESS_ONCE(port->rx_outp); + if (CIRC_CNT(port->rx_inp, ix, MNSC_BUFFER_SIZE) == 0) + return NO_POLL_CHAR; + + smp_read_barrier_depends(); + ch = port->rx_buffer[ix++]; + st = port->rx_buffer[ix++]; + smp_mb(); + port->rx_outp = ix & (MNSC_BUFFER_SIZE - 1); + + } while (st & (SC01STR_FEF | SC01STR_PEF | SC01STR_OEF)); + } else { + do { + st = *port->_status; + if (st & (SC01STR_FEF | SC01STR_PEF | SC01STR_OEF)) + continue; + } while (!(st & SC01STR_RBF)); + + ch = *port->_rxb; + } + + 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) { + *port->_txb = 0x0d; + while (*port->_status & SC01STR_TBF) + continue; + } + + *port->_txb = ch; + while (*port->_status & SC01STR_TBF) + continue; + + /* restore the Tx interrupt flag */ + *port->_intr = intr; + tmp = *port->_intr; +} + +#endif /* CONFIG_CONSOLE_POLL */ diff --git a/arch/mn10300/kernel/mn10300-serial.h b/arch/mn10300/kernel/mn10300-serial.h index 6796499bf78..01791c68ea1 100644 --- a/arch/mn10300/kernel/mn10300-serial.h +++ b/arch/mn10300/kernel/mn10300-serial.h @@ -29,6 +29,10 @@ #define MNSCx_TX_SPACE 0x04 #define MNSCx_TX_EMPTY 0x08 +/* tx_flags bits */ +#define MNSCx_TX_BREAK 0x01 +#define MNSCx_TX_STOP 0x02 + #ifndef __ASSEMBLY__ struct mn10300_serial_port { @@ -36,7 +40,7 @@ struct mn10300_serial_port { unsigned rx_inp; /* pointer to rx input offset */ unsigned rx_outp; /* pointer to rx output offset */ u8 tx_xchar; /* high-priority XON/XOFF buffer */ - u8 tx_break; /* transmit break request */ + u8 tx_flags; /* transmit break/stop request */ u8 intr_flags; /* interrupt flags */ volatile u16 *rx_icr; /* Rx interrupt control register */ volatile u16 *tx_icr; /* Tx interrupt control register */ @@ -54,8 +58,8 @@ struct mn10300_serial_port { volatile u16 *_control; /* control register pointer */ volatile u8 *_status; /* status register pointer */ volatile u8 *_intr; /* interrupt register pointer */ - volatile void *_rxb; /* receive buffer register pointer */ - volatile void *_txb; /* transmit buffer register pointer */ + volatile u8 *_rxb; /* receive buffer register pointer */ + volatile u8 *_txb; /* transmit buffer register pointer */ volatile u16 *_tmicr; /* timer interrupt control register */ volatile u8 *_tmxmd; /* baud rate timer mode register */ volatile u16 *_tmxbr; /* baud rate timer base register */ diff --git a/arch/mn10300/kernel/mn10300-watchdog.c b/arch/mn10300/kernel/mn10300-watchdog.c index c5e12bfd9fc..a2d8e6938d6 100644 --- a/arch/mn10300/kernel/mn10300-watchdog.c +++ b/arch/mn10300/kernel/mn10300-watchdog.c @@ -18,8 +18,7 @@ #include <linux/kernel_stat.h> #include <linux/nmi.h> #include <asm/processor.h> -#include <asm/system.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <asm/intctl-regs.h> #include <asm/rtc-regs.h> #include <asm/div64.h> @@ -143,7 +142,7 @@ void watchdog_interrupt(struct pt_regs *regs, enum exception_code excep) NMICR = NMICR_WDIF; nmi_count(smp_processor_id())++; - kstat_incr_irqs_this_cpu(irq, irq_to_desc(irq)); + kstat_incr_irq_this_cpu(irq); for_each_online_cpu(cpu) { diff --git a/arch/mn10300/kernel/module.c b/arch/mn10300/kernel/module.c index 196a111e2e2..216ad23c957 100644 --- a/arch/mn10300/kernel/module.c +++ b/arch/mn10300/kernel/module.c @@ -32,36 +32,6 @@ #define DEBUGP(fmt, ...) #endif -/* - * allocate storage for a module - */ -void *module_alloc(unsigned long size) -{ - if (size == 0) - return NULL; - return vmalloc_exec(size); -} - -/* - * free memory returned from module_alloc() - */ -void module_free(struct module *mod, void *module_region) -{ - vfree(module_region); -} - -/* - * allow the arch to fix up the section table - * - we don't need anything special - */ -int module_frob_arch_sections(Elf_Ehdr *hdr, - Elf_Shdr *sechdrs, - char *secstrings, - struct module *mod) -{ - return 0; -} - static void reloc_put16(uint8_t *p, uint32_t val) { p[0] = val & 0xff; @@ -81,20 +51,6 @@ static void reloc_put32(uint8_t *p, uint32_t val) } /* - * apply a REL relocation - */ -int apply_relocate(Elf32_Shdr *sechdrs, - const char *strtab, - unsigned int symindex, - unsigned int relsec, - struct module *me) -{ - printk(KERN_ERR "module %s: RELOCATION unsupported\n", - me->name); - return -ENOEXEC; -} - -/* * apply a RELA relocation */ int apply_relocate_add(Elf32_Shdr *sechdrs, @@ -198,20 +154,3 @@ int apply_relocate_add(Elf32_Shdr *sechdrs, } return 0; } - -/* - * finish loading the module - */ -int module_finalize(const Elf_Ehdr *hdr, - const Elf_Shdr *sechdrs, - struct module *me) -{ - return 0; -} - -/* - * finish clearing the module - */ -void module_arch_cleanup(struct module *mod) -{ -} diff --git a/arch/mn10300/kernel/process.c b/arch/mn10300/kernel/process.c index e1b14a6ed54..3707da583d0 100644 --- a/arch/mn10300/kernel/process.c +++ b/arch/mn10300/kernel/process.c @@ -25,9 +25,9 @@ #include <linux/err.h> #include <linux/fs.h> #include <linux/slab.h> +#include <linux/rcupdate.h> #include <asm/uaccess.h> #include <asm/pgtable.h> -#include <asm/system.h> #include <asm/io.h> #include <asm/processor.h> #include <asm/mmu_context.h> @@ -37,12 +37,6 @@ #include "internal.h" /* - * power management idle function, if any.. - */ -void (*pm_idle)(void); -EXPORT_SYMBOL(pm_idle); - -/* * return saved PC of a blocked thread. */ unsigned long thread_saved_pc(struct task_struct *tsk) @@ -56,78 +50,19 @@ unsigned long thread_saved_pc(struct task_struct *tsk) void (*pm_power_off)(void); EXPORT_SYMBOL(pm_power_off); -#if !defined(CONFIG_SMP) || defined(CONFIG_HOTPLUG_CPU) -/* - * we use this if we don't have any better idle routine - */ -static void default_idle(void) -{ - local_irq_disable(); - if (!need_resched()) - safe_halt(); - else - local_irq_enable(); -} - -#else /* !CONFIG_SMP || CONFIG_HOTPLUG_CPU */ /* * On SMP it's slightly faster (but much more power-consuming!) * to poll the ->work.need_resched flag instead of waiting for the * cross-CPU IPI to arrive. Use this option with caution. + * + * tglx: No idea why this depends on HOTPLUG_CPU !?! */ -static inline void poll_idle(void) -{ - int oldval; - - local_irq_enable(); - - /* - * Deal with another CPU just having chosen a thread to - * run here: - */ - oldval = test_and_clear_thread_flag(TIF_NEED_RESCHED); - - if (!oldval) { - set_thread_flag(TIF_POLLING_NRFLAG); - while (!need_resched()) - cpu_relax(); - clear_thread_flag(TIF_POLLING_NRFLAG); - } else { - set_need_resched(); - } -} -#endif /* !CONFIG_SMP || CONFIG_HOTPLUG_CPU */ - -/* - * the idle thread - * - there's no useful work to be done, so just try to conserve power and have - * a low exit latency (ie sit in a loop waiting for somebody to say that - * they'd like to reschedule) - */ -void cpu_idle(void) +#if !defined(CONFIG_SMP) || defined(CONFIG_HOTPLUG_CPU) +void arch_cpu_idle(void) { - /* endless idle loop with no priority at all */ - for (;;) { - while (!need_resched()) { - void (*idle)(void); - - smp_rmb(); - idle = pm_idle; - if (!idle) { -#if defined(CONFIG_SMP) && !defined(CONFIG_HOTPLUG_CPU) - idle = poll_idle; -#else /* CONFIG_SMP && !CONFIG_HOTPLUG_CPU */ - idle = default_idle; -#endif /* CONFIG_SMP && !CONFIG_HOTPLUG_CPU */ - } - idle(); - } - - preempt_enable_no_resched(); - schedule(); - preempt_disable(); - } + safe_halt(); } +#endif void release_segments(struct mm_struct *mm) { @@ -135,7 +70,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,44 +83,24 @@ 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 } void show_regs(struct pt_regs *regs) { + show_regs_print_info(KERN_DEFAULT); } /* - * create a kernel thread - */ -int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) -{ - struct pt_regs regs; - - memset(®s, 0, sizeof(regs)); - - regs.a2 = (unsigned long) fn; - regs.d2 = (unsigned long) arg; - regs.pc = (unsigned long) kernel_thread_helper; - local_save_flags(regs.epsw); - regs.epsw |= EPSW_IE | EPSW_IM_7; - - /* Ok, create the new process.. */ - return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, - NULL, NULL); -} -EXPORT_SYMBOL(kernel_thread); - -/* * free current thread data structures etc.. */ void exit_thread(void) @@ -211,12 +126,14 @@ void copy_segments(struct task_struct *p, struct mm_struct *new_mm) } /* - * this gets called before we allocate a new thread and copy the current task - * into it so that we can store lazy state into memory + * this gets called so that we can store lazy state into memory and copy the + * current task into the new thread. */ -void prepare_to_copy(struct task_struct *tsk) +int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) { - unlazy_fpu(tsk); + unlazy_fpu(src); + *dst = *src; + return 0; } /* @@ -225,95 +142,48 @@ void prepare_to_copy(struct task_struct *tsk) */ int copy_thread(unsigned long clone_flags, unsigned long c_usp, unsigned long ustk_size, - struct task_struct *p, struct pt_regs *kregs) + struct task_struct *p) { struct thread_info *ti = task_thread_info(p); - struct pt_regs *c_uregs, *c_kregs, *uregs; + struct pt_regs *c_regs; unsigned long c_ksp; - uregs = current->thread.uregs; - c_ksp = (unsigned long) task_stack_page(p) + THREAD_SIZE; /* allocate the userspace exception frame and set it up */ c_ksp -= sizeof(struct pt_regs); - c_uregs = (struct pt_regs *) c_ksp; - - p->thread.uregs = c_uregs; - *c_uregs = *uregs; - c_uregs->sp = c_usp; - c_uregs->epsw &= ~EPSW_FE; /* my FPU */ - + c_regs = (struct pt_regs *) c_ksp; c_ksp -= 12; /* allocate function call ABI slack */ - /* the new TLS pointer is passed in as arg #5 to sys_clone() */ - if (clone_flags & CLONE_SETTLS) - c_uregs->e2 = current_frame()->d3; - - /* set up the return kernel frame if called from kernel_thread() */ - c_kregs = c_uregs; - if (kregs != uregs) { - c_ksp -= sizeof(struct pt_regs); - c_kregs = (struct pt_regs *) c_ksp; - *c_kregs = *kregs; - c_kregs->sp = c_usp; - c_kregs->next = c_uregs; -#ifdef CONFIG_MN10300_CURRENT_IN_E2 - c_kregs->e2 = (unsigned long) p; /* current */ -#endif - - c_ksp -= 12; /* allocate function call ABI slack */ - } - /* set up things up so the scheduler can start the new task */ - ti->frame = c_kregs; - p->thread.a3 = (unsigned long) c_kregs; + p->thread.uregs = c_regs; + ti->frame = c_regs; + p->thread.a3 = (unsigned long) c_regs; p->thread.sp = c_ksp; - p->thread.pc = (unsigned long) ret_from_fork; - p->thread.wchan = (unsigned long) ret_from_fork; + p->thread.wchan = p->thread.pc; p->thread.usp = c_usp; - return 0; -} - -/* - * clone a process - * - tlsptr is retrieved by copy_thread() from current_frame()->d3 - */ -asmlinkage long sys_clone(unsigned long clone_flags, unsigned long newsp, - int __user *parent_tidptr, int __user *child_tidptr, - int __user *tlsptr) -{ - return do_fork(clone_flags, newsp ?: current_frame()->sp, - current_frame(), 0, parent_tidptr, child_tidptr); -} - -asmlinkage long sys_fork(void) -{ - return do_fork(SIGCHLD, current_frame()->sp, - current_frame(), 0, NULL, NULL); -} + if (unlikely(p->flags & PF_KTHREAD)) { + memset(c_regs, 0, sizeof(struct pt_regs)); + c_regs->a0 = c_usp; /* function */ + c_regs->d0 = ustk_size; /* argument */ + local_save_flags(c_regs->epsw); + c_regs->epsw |= EPSW_IE | EPSW_IM_7; + p->thread.pc = (unsigned long) ret_from_kernel_thread; + return 0; + } + *c_regs = *current_pt_regs(); + if (c_usp) + c_regs->sp = c_usp; + c_regs->epsw &= ~EPSW_FE; /* my FPU */ -asmlinkage long sys_vfork(void) -{ - return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, current_frame()->sp, - current_frame(), 0, NULL, NULL); -} + /* the new TLS pointer is passed in as arg #5 to sys_clone() */ + if (clone_flags & CLONE_SETTLS) + c_regs->e2 = current_frame()->d3; -asmlinkage long sys_execve(const char __user *name, - const char __user *const __user *argv, - const char __user *const __user *envp) -{ - char *filename; - int error; + p->thread.pc = (unsigned long) ret_from_fork; - filename = getname(name); - error = PTR_ERR(filename); - if (IS_ERR(filename)) - return error; - error = do_execve(filename, argv, envp, current_frame()); - putname(filename); - return error; + return 0; } unsigned long get_wchan(struct task_struct *p) diff --git a/arch/mn10300/kernel/ptrace.c b/arch/mn10300/kernel/ptrace.c index 5c0b07e6100..5bd58514e73 100644 --- a/arch/mn10300/kernel/ptrace.c +++ b/arch/mn10300/kernel/ptrace.c @@ -21,7 +21,6 @@ #include <linux/tracehook.h> #include <asm/uaccess.h> #include <asm/pgtable.h> -#include <asm/system.h> #include <asm/processor.h> #include <asm/cacheflush.h> #include <asm/fpu.h> diff --git a/arch/mn10300/kernel/setup.c b/arch/mn10300/kernel/setup.c index 9e7a3209a3e..2ad7f32fa12 100644 --- a/arch/mn10300/kernel/setup.c +++ b/arch/mn10300/kernel/setup.c @@ -26,7 +26,6 @@ #include <asm/processor.h> #include <linux/console.h> #include <asm/uaccess.h> -#include <asm/system.h> #include <asm/setup.h> #include <asm/io.h> #include <asm/smp.h> @@ -36,9 +35,7 @@ struct mn10300_cpuinfo boot_cpu_data; -/* For PCI or other memory-mapped resources */ -unsigned long pci_mem_start = 0x18000000; - +static char __initdata cmd_line[COMMAND_LINE_SIZE]; char redboot_command_line[COMMAND_LINE_SIZE] = "console=ttyS0,115200 root=/dev/mtdblock3 rw"; @@ -75,45 +72,19 @@ static const char *const mn10300_cputypes[] = { }; /* - * + * Pick out the memory size. We look for mem=size, + * where size is "size[KkMm]" */ -static void __init parse_mem_cmdline(char **cmdline_p) +static int __init early_mem(char *p) { - char *from, *to, c; - - /* save unparsed command line copy for /proc/cmdline */ - strcpy(boot_command_line, redboot_command_line); - - /* see if there's an explicit memory size option */ - from = redboot_command_line; - to = redboot_command_line; - c = ' '; - - for (;;) { - if (c == ' ' && !memcmp(from, "mem=", 4)) { - if (to != redboot_command_line) - to--; - memory_size = memparse(from + 4, &from); - } - - c = *(from++); - if (!c) - break; - - *(to++) = c; - } - - *to = '\0'; - *cmdline_p = redboot_command_line; + memory_size = memparse(p, &p); if (memory_size == 0) panic("Memory size not known\n"); - memory_end = (unsigned long) CONFIG_KERNEL_RAM_BASE_ADDRESS + - memory_size; - if (memory_end > phys_memory_end) - memory_end = phys_memory_end; + return 0; } +early_param("mem", early_mem); /* * architecture specific setup @@ -126,7 +97,20 @@ void __init setup_arch(char **cmdline_p) cpu_init(); unit_setup(); smp_init_cpus(); - parse_mem_cmdline(cmdline_p); + + /* save unparsed command line copy for /proc/cmdline */ + strlcpy(boot_command_line, redboot_command_line, COMMAND_LINE_SIZE); + + /* populate cmd_line too for later use, preserving boot_command_line */ + strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE); + *cmdline_p = cmd_line; + + parse_early_param(); + + memory_end = (unsigned long) CONFIG_KERNEL_RAM_BASE_ADDRESS + + memory_size; + if (memory_end > phys_memory_end) + memory_end = phys_memory_end; init_mm.start_code = (unsigned long)&_text; init_mm.end_code = (unsigned long) &_etext; diff --git a/arch/mn10300/kernel/signal.c b/arch/mn10300/kernel/signal.c index 690f4e9507d..9dfac5cd16e 100644 --- a/arch/mn10300/kernel/signal.c +++ b/arch/mn10300/kernel/signal.c @@ -31,69 +31,6 @@ #define DEBUG_SIG 0 -#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) - -/* - * atomically swap in the new signal mask, and wait for a signal. - */ -asmlinkage long sys_sigsuspend(int history0, int history1, old_sigset_t mask) -{ - mask &= _BLOCKABLE; - spin_lock_irq(¤t->sighand->siglock); - current->saved_sigmask = current->blocked; - siginitset(¤t->blocked, mask); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - current->state = TASK_INTERRUPTIBLE; - schedule(); - set_thread_flag(TIF_RESTORE_SIGMASK); - return -ERESTARTNOHAND; -} - -/* - * set signal action syscall - */ -asmlinkage long sys_sigaction(int sig, - const struct old_sigaction __user *act, - struct old_sigaction __user *oact) -{ - struct k_sigaction new_ka, old_ka; - int ret; - - if (act) { - old_sigset_t mask; - if (verify_area(VERIFY_READ, act, sizeof(*act)) || - __get_user(new_ka.sa.sa_handler, &act->sa_handler) || - __get_user(new_ka.sa.sa_restorer, &act->sa_restorer) || - __get_user(new_ka.sa.sa_flags, &act->sa_flags) || - __get_user(mask, &act->sa_mask)) - return -EFAULT; - siginitset(&new_ka.sa.sa_mask, mask); - } - - ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); - - if (!ret && oact) { - if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || - __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || - __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer) || - __put_user(old_ka.sa.sa_flags, &oact->sa_flags) || - __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask)) - return -EFAULT; - } - - return ret; -} - -/* - * set alternate signal stack syscall - */ -asmlinkage long sys_sigaltstack(const stack_t __user *uss, stack_t *uoss) -{ - return do_sigaltstack(uss, uoss, current_frame()->sp); -} - /* * do a signal return; undo the signal stack. */ @@ -171,11 +108,7 @@ asmlinkage long sys_sigreturn(void) sizeof(frame->extramask))) goto badframe; - sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + set_current_blocked(&set); if (restore_sigcontext(current_frame(), &frame->sc, &d0)) goto badframe; @@ -202,17 +135,12 @@ asmlinkage long sys_rt_sigreturn(void) if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) goto badframe; - sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + set_current_blocked(&set); if (restore_sigcontext(current_frame(), &frame->uc.uc_mcontext, &d0)) goto badframe; - if (do_sigaltstack(&frame->uc.uc_stack, NULL, current_frame()->sp) == - -EFAULT) + if (restore_altstack(&frame->uc.uc_stack)) goto badframe; return d0; @@ -335,10 +263,6 @@ static int setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, regs->d0 = sig; regs->d1 = (unsigned long) &frame->sc; - /* the tracer may want to single-step inside the handler */ - if (test_thread_flag(TIF_SINGLESTEP)) - ptrace_notify(SIGTRAP); - #if DEBUG_SIG printk(KERN_DEBUG "SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n", sig, current->comm, current->pid, frame, regs->pc, @@ -381,9 +305,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, /* create the ucontext. */ if (__put_user(0, &frame->uc.uc_flags) || __put_user(0, &frame->uc.uc_link) || - __put_user((void *)current->sas_ss_sp, &frame->uc.uc_stack.ss_sp) || - __put_user(sas_ss_flags(regs->sp), &frame->uc.uc_stack.ss_flags) || - __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size) || + __save_altstack(&frame->uc.uc_stack, regs->sp) || setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpuctx, regs, set->sig[0]) || __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set))) @@ -416,10 +338,6 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, regs->d0 = sig; regs->d1 = (long) &frame->info; - /* the tracer may want to single-step inside the handler */ - if (test_thread_flag(TIF_SINGLESTEP)) - ptrace_notify(SIGTRAP); - #if DEBUG_SIG printk(KERN_DEBUG "SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n", sig, current->comm, current->pid, frame, regs->pc, @@ -444,8 +362,9 @@ static inline void stepback(struct pt_regs *regs) */ static int handle_signal(int sig, siginfo_t *info, struct k_sigaction *ka, - sigset_t *oldset, struct pt_regs *regs) + struct pt_regs *regs) { + sigset_t *oldset = sigmask_to_save(); int ret; /* Are we from a system call? */ @@ -475,18 +394,12 @@ static int handle_signal(int sig, ret = setup_rt_frame(sig, ka, info, oldset, regs); else ret = setup_frame(sig, ka, oldset, regs); + if (ret) + return ret; - if (ret == 0) { - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked, ¤t->blocked, - &ka->sa.sa_mask); - if (!(ka->sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked, sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - } - - return ret; + signal_delivered(sig, info, ka, regs, + test_thread_flag(TIF_SINGLESTEP)); + return 0; } /* @@ -496,31 +409,11 @@ static void do_signal(struct pt_regs *regs) { struct k_sigaction ka; siginfo_t info; - sigset_t *oldset; int signr; - /* we want the common case to go fast, which is why we may in certain - * cases get here from kernel mode */ - if (!user_mode(regs)) - return; - - if (test_thread_flag(TIF_RESTORE_SIGMASK)) - oldset = ¤t->saved_sigmask; - else - oldset = ¤t->blocked; - signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { - if (handle_signal(signr, &info, &ka, oldset, regs) == 0) { - /* a signal was successfully delivered; the saved - * sigmask will have been stored in the signal frame, - * and will be restored by sigreturn, so we can simply - * clear the TIF_RESTORE_SIGMASK flag */ - if (test_thread_flag(TIF_RESTORE_SIGMASK)) - clear_thread_flag(TIF_RESTORE_SIGMASK); - - tracehook_signal_handler(signr, &info, &ka, regs, - test_thread_flag(TIF_SINGLESTEP)); + if (handle_signal(signr, &info, &ka, regs) == 0) { } return; @@ -546,10 +439,7 @@ static void do_signal(struct pt_regs *regs) /* if there's no signal to deliver, we just put the saved sigmask * back */ - if (test_thread_flag(TIF_RESTORE_SIGMASK)) { - clear_thread_flag(TIF_RESTORE_SIGMASK); - sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); - } + restore_saved_sigmask(); } /* @@ -569,13 +459,11 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags) } /* deal with pending signal delivery */ - if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK)) + if (thread_info_flags & _TIF_SIGPENDING) do_signal(regs); if (thread_info_flags & _TIF_NOTIFY_RESUME) { clear_thread_flag(TIF_NOTIFY_RESUME); tracehook_notify_resume(current_frame()); - if (current->replacement_session_keyring) - key_replace_session_keyring(); } } diff --git a/arch/mn10300/kernel/smp-low.S b/arch/mn10300/kernel/smp-low.S index 72938cefc05..71f1b2faaa0 100644 --- a/arch/mn10300/kernel/smp-low.S +++ b/arch/mn10300/kernel/smp-low.S @@ -13,9 +13,9 @@ #include <linux/sys.h> #include <linux/linkage.h> #include <asm/smp.h> -#include <asm/system.h> #include <asm/thread_info.h> #include <asm/cpu-regs.h> +#include <asm/intctl-regs.h> #include <proc/smp-regs.h> #include <asm/asm-offsets.h> #include <asm/frame.inc> diff --git a/arch/mn10300/kernel/smp.c b/arch/mn10300/kernel/smp.c index 0dcd1c686ba..f984193718b 100644 --- a/arch/mn10300/kernel/smp.c +++ b/arch/mn10300/kernel/smp.c @@ -24,8 +24,8 @@ #include <linux/sched.h> #include <linux/profile.h> #include <linux/smp.h> +#include <linux/cpu.h> #include <asm/tlbflush.h> -#include <asm/system.h> #include <asm/bitops.h> #include <asm/processor.h> #include <asm/bug.h> @@ -39,7 +39,6 @@ #include "internal.h" #ifdef CONFIG_HOTPLUG_CPU -#include <linux/cpu.h> #include <asm/cacheflush.h> static unsigned long sleep_mode[NR_CPUS]; @@ -113,15 +112,17 @@ static void init_ipi(void); */ static void mn10300_ipi_disable(unsigned int irq); static void mn10300_ipi_enable(unsigned int irq); -static void mn10300_ipi_ack(unsigned int irq); -static void mn10300_ipi_nop(unsigned int irq); +static void mn10300_ipi_chip_disable(struct irq_data *d); +static void mn10300_ipi_chip_enable(struct irq_data *d); +static void mn10300_ipi_ack(struct irq_data *d); +static void mn10300_ipi_nop(struct irq_data *d); static struct irq_chip mn10300_ipi_type = { .name = "cpu_ipi", - .disable = mn10300_ipi_disable, - .enable = mn10300_ipi_enable, - .ack = mn10300_ipi_ack, - .eoi = mn10300_ipi_nop + .irq_disable = mn10300_ipi_chip_disable, + .irq_enable = mn10300_ipi_chip_enable, + .irq_ack = mn10300_ipi_ack, + .irq_eoi = mn10300_ipi_nop }; static irqreturn_t smp_reschedule_interrupt(int irq, void *dev_id); @@ -129,10 +130,12 @@ static irqreturn_t smp_call_function_interrupt(int irq, void *dev_id); static struct irqaction reschedule_ipi = { .handler = smp_reschedule_interrupt, + .flags = IRQF_NOBALANCING, .name = "smp reschedule IPI" }; static struct irqaction call_function_ipi = { .handler = smp_call_function_interrupt, + .flags = IRQF_NOBALANCING, .name = "smp call function IPI" }; @@ -140,7 +143,7 @@ static struct irqaction call_function_ipi = { static irqreturn_t smp_ipi_timer_interrupt(int irq, void *dev_id); static struct irqaction local_timer_ipi = { .handler = smp_ipi_timer_interrupt, - .flags = IRQF_DISABLED, + .flags = IRQF_NOBALANCING, .name = "smp local timer IPI" }; #endif @@ -154,15 +157,15 @@ static void init_ipi(void) u16 tmp16; /* set up the reschedule IPI */ - set_irq_chip_and_handler(RESCHEDULE_IPI, - &mn10300_ipi_type, handle_percpu_irq); + irq_set_chip_and_handler(RESCHEDULE_IPI, &mn10300_ipi_type, + handle_percpu_irq); setup_irq(RESCHEDULE_IPI, &reschedule_ipi); set_intr_level(RESCHEDULE_IPI, RESCHEDULE_GxICR_LV); mn10300_ipi_enable(RESCHEDULE_IPI); /* set up the call function IPI */ - set_irq_chip_and_handler(CALL_FUNC_SINGLE_IPI, - &mn10300_ipi_type, handle_percpu_irq); + irq_set_chip_and_handler(CALL_FUNC_SINGLE_IPI, &mn10300_ipi_type, + handle_percpu_irq); setup_irq(CALL_FUNC_SINGLE_IPI, &call_function_ipi); set_intr_level(CALL_FUNC_SINGLE_IPI, CALL_FUNCTION_GxICR_LV); mn10300_ipi_enable(CALL_FUNC_SINGLE_IPI); @@ -170,8 +173,8 @@ static void init_ipi(void) /* set up the local timer IPI */ #if !defined(CONFIG_GENERIC_CLOCKEVENTS) || \ defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) - set_irq_chip_and_handler(LOCAL_TIMER_IPI, - &mn10300_ipi_type, handle_percpu_irq); + irq_set_chip_and_handler(LOCAL_TIMER_IPI, &mn10300_ipi_type, + handle_percpu_irq); setup_irq(LOCAL_TIMER_IPI, &local_timer_ipi); set_intr_level(LOCAL_TIMER_IPI, LOCAL_TIMER_GxICR_LV); mn10300_ipi_enable(LOCAL_TIMER_IPI); @@ -179,6 +182,7 @@ static void init_ipi(void) #ifdef CONFIG_MN10300_CACHE_ENABLED /* set up the cache flush IPI */ + irq_set_chip(FLUSH_CACHE_IPI, &mn10300_ipi_type); flags = arch_local_cli_save(); __set_intr_stub(NUM2EXCEP_IRQ_LEVEL(FLUSH_CACHE_GxICR_LV), mn10300_low_ipi_handler); @@ -188,6 +192,7 @@ static void init_ipi(void) #endif /* set up the NMI call function IPI */ + irq_set_chip(CALL_FUNCTION_NMI_IPI, &mn10300_ipi_type); flags = arch_local_cli_save(); GxICR(CALL_FUNCTION_NMI_IPI) = GxICR_NMI | GxICR_ENABLE | GxICR_DETECT; tmp16 = GxICR(CALL_FUNCTION_NMI_IPI); @@ -198,6 +203,10 @@ static void init_ipi(void) __set_intr_stub(NUM2EXCEP_IRQ_LEVEL(SMP_BOOT_GxICR_LV), mn10300_low_ipi_handler); arch_local_irq_restore(flags); + +#ifdef CONFIG_KERNEL_DEBUGGER + irq_set_chip(DEBUGGER_NMI_IPI, &mn10300_ipi_type); +#endif } /** @@ -236,6 +245,11 @@ static void mn10300_ipi_enable(unsigned int irq) arch_local_irq_restore(flags); } +static void mn10300_ipi_chip_enable(struct irq_data *d) +{ + mn10300_ipi_enable(d->irq); +} + /** * mn10300_ipi_disable - Disable an IPI * @irq: The IPI to be disabled. @@ -254,6 +268,12 @@ static void mn10300_ipi_disable(unsigned int irq) arch_local_irq_restore(flags); } +static void mn10300_ipi_chip_disable(struct irq_data *d) +{ + mn10300_ipi_disable(d->irq); +} + + /** * mn10300_ipi_ack - Acknowledge an IPI interrupt in the PIC * @irq: The IPI to be acknowledged. @@ -261,8 +281,9 @@ static void mn10300_ipi_disable(unsigned int irq) * Clear the interrupt detection flag for the IPI on the appropriate interrupt * channel in the PIC. */ -static void mn10300_ipi_ack(unsigned int irq) +static void mn10300_ipi_ack(struct irq_data *d) { + unsigned int irq = d->irq; unsigned long flags; u16 tmp; @@ -276,7 +297,7 @@ static void mn10300_ipi_ack(unsigned int irq) * mn10300_ipi_nop - Dummy IPI action * @irq: The IPI to be acted upon. */ -static void mn10300_ipi_nop(unsigned int irq) +static void mn10300_ipi_nop(struct irq_data *d) { } @@ -295,7 +316,7 @@ static void send_IPI_mask(const cpumask_t *cpumask, int irq) u16 tmp; for (i = 0; i < NR_CPUS; i++) { - if (cpu_isset(i, *cpumask)) { + if (cpumask_test_cpu(i, cpumask)) { /* send IPI */ tmp = CROSS_GxICR(irq, i); CROSS_GxICR(irq, i) = @@ -328,8 +349,8 @@ void send_IPI_allbutself(int irq) { cpumask_t cpumask; - cpumask = cpu_online_map; - cpu_clear(smp_processor_id(), cpumask); + cpumask_copy(&cpumask, cpu_online_mask); + cpumask_clear_cpu(smp_processor_id(), &cpumask); send_IPI_mask(&cpumask, irq); } @@ -379,8 +400,8 @@ int smp_nmi_call_function(smp_call_func_t func, void *info, int wait) data.func = func; data.info = info; - data.started = cpu_online_map; - cpu_clear(smp_processor_id(), data.started); + cpumask_copy(&data.started, cpu_online_mask); + cpumask_clear_cpu(smp_processor_id(), &data.started); data.wait = wait; if (wait) data.finished = data.started; @@ -396,14 +417,14 @@ int smp_nmi_call_function(smp_call_func_t func, void *info, int wait) if (CALL_FUNCTION_NMI_IPI_TIMEOUT > 0) { for (cnt = 0; cnt < CALL_FUNCTION_NMI_IPI_TIMEOUT && - !cpus_empty(data.started); + !cpumask_empty(&data.started); cnt++) mdelay(1); if (wait && cnt < CALL_FUNCTION_NMI_IPI_TIMEOUT) { for (cnt = 0; cnt < CALL_FUNCTION_NMI_IPI_TIMEOUT && - !cpus_empty(data.finished); + !cpumask_empty(&data.finished); cnt++) mdelay(1); } @@ -414,10 +435,10 @@ int smp_nmi_call_function(smp_call_func_t func, void *info, int wait) } else { /* If timeout value is zero, wait until cpumask has been * cleared */ - while (!cpus_empty(data.started)) + while (!cpumask_empty(&data.started)) barrier(); if (wait) - while (!cpus_empty(data.finished)) + while (!cpumask_empty(&data.finished)) barrier(); } @@ -426,6 +447,22 @@ int smp_nmi_call_function(smp_call_func_t func, void *info, int wait) } /** + * 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). */ @@ -442,12 +479,12 @@ void stop_this_cpu(void *unused) #endif /* CONFIG_GDBSTUB */ flags = arch_local_cli_save(); - cpu_clear(smp_processor_id(), cpu_online_map); + set_cpu_online(smp_processor_id(), false); while (!stopflag) cpu_relax(); - cpu_set(smp_processor_id(), cpu_online_map); + set_cpu_online(smp_processor_id(), true); arch_local_irq_restore(flags); } @@ -464,14 +501,11 @@ void smp_send_stop(void) * @irq: The interrupt number. * @dev_id: The device ID. * - * We need do nothing here, since the scheduling will be effected on our way - * back through entry.S. - * * Returns IRQ_HANDLED to indicate we handled the interrupt successfully. */ static irqreturn_t smp_reschedule_interrupt(int irq, void *dev_id) { - /* do nothing */ + scheduler_ipi(); return IRQ_HANDLED; } @@ -502,12 +536,13 @@ void smp_nmi_call_function_interrupt(void) * execute the function */ smp_mb(); - cpu_clear(smp_processor_id(), nmi_call_data->started); + cpumask_clear_cpu(smp_processor_id(), &nmi_call_data->started); (*func)(info); if (wait) { smp_mb(); - cpu_clear(smp_processor_id(), nmi_call_data->finished); + cpumask_clear_cpu(smp_processor_id(), + &nmi_call_data->finished); } } @@ -589,7 +624,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) { @@ -608,15 +643,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 @@ -630,7 +665,7 @@ int __init start_secondary(void *unused) { smp_cpu_init(); smp_callin(); - while (!cpu_isset(smp_processor_id(), smp_commenced_mask)) + while (!cpumask_test_cpu(smp_processor_id(), &smp_commenced_mask)) cpu_relax(); local_flush_tlb(); @@ -640,7 +675,7 @@ int __init start_secondary(void *unused) #ifdef CONFIG_GENERIC_CLOCKEVENTS init_clockevents(); #endif - cpu_idle(); + cpu_startup_entry(CPUHP_ONLINE); return 0; } @@ -753,13 +788,14 @@ static int __init do_boot_cpu(int phy_id) if (send_status == 0) { /* Allow AP to start initializing */ - cpu_set(cpu_id, cpu_callout_map); + cpumask_set_cpu(cpu_id, &cpu_callout_map); /* Wait for setting cpu_callin_map */ timeout = 0; do { udelay(1000); - callin_status = cpu_isset(cpu_id, cpu_callin_map); + callin_status = cpumask_test_cpu(cpu_id, + &cpu_callin_map); } while (callin_status == 0 && timeout++ < 5000); if (callin_status == 0) @@ -769,9 +805,9 @@ static int __init do_boot_cpu(int phy_id) } if (send_status == GxICR_REQUEST || callin_status == 0) { - cpu_clear(cpu_id, cpu_callout_map); - cpu_clear(cpu_id, cpu_callin_map); - cpu_clear(cpu_id, cpu_initialized); + cpumask_clear_cpu(cpu_id, &cpu_callout_map); + cpumask_clear_cpu(cpu_id, &cpu_callin_map); + cpumask_clear_cpu(cpu_id, &cpu_initialized); cpucount--; return 1; } @@ -806,7 +842,7 @@ static void __init smp_callin(void) cpu = smp_processor_id(); timeout = jiffies + (2 * HZ); - if (cpu_isset(cpu, cpu_callin_map)) { + if (cpumask_test_cpu(cpu, &cpu_callin_map)) { printk(KERN_ERR "CPU#%d already present.\n", cpu); BUG(); } @@ -814,7 +850,7 @@ static void __init smp_callin(void) /* Wait for AP startup 2s total */ while (time_before(jiffies, timeout)) { - if (cpu_isset(cpu, cpu_callout_map)) + if (cpumask_test_cpu(cpu, &cpu_callout_map)) break; cpu_relax(); } @@ -834,11 +870,11 @@ static void __init smp_callin(void) smp_store_cpu_info(cpu); /* Allow the boot processor to continue */ - cpu_set(cpu, cpu_callin_map); + cpumask_set_cpu(cpu, &cpu_callin_map); } /** - * smp_online - Set cpu_online_map + * smp_online - Set cpu_online_mask */ static void __init smp_online(void) { @@ -846,10 +882,11 @@ static void __init smp_online(void) cpu = smp_processor_id(); - local_irq_enable(); + notify_cpu_starting(cpu); - cpu_set(cpu, cpu_online_map); - smp_wmb(); + set_cpu_online(cpu, true); + + local_irq_enable(); } /** @@ -865,13 +902,13 @@ void __init smp_cpus_done(unsigned int max_cpus) /* * smp_prepare_boot_cpu - Set up stuff for the boot processor. * - * Set up the cpu_online_map, cpu_callout_map and cpu_callin_map of the boot + * Set up the cpu_online_mask, cpu_callout_map and cpu_callin_map of the boot * processor (CPU 0). */ -void __devinit smp_prepare_boot_cpu(void) +void smp_prepare_boot_cpu(void) { - cpu_set(0, cpu_callout_map); - cpu_set(0, cpu_callin_map); + cpumask_set_cpu(0, &cpu_callout_map); + cpumask_set_cpu(0, &cpu_callin_map); current_thread_info()->cpu = 0; } @@ -893,27 +930,25 @@ void initialize_secondary(void) * __cpu_up - Set smp_commenced_mask for the nominated CPU * @cpu: The target CPU. */ -int __devinit __cpu_up(unsigned int cpu) +int __cpu_up(unsigned int cpu, struct task_struct *tidle) { int timeout; #ifdef CONFIG_HOTPLUG_CPU - if (num_online_cpus() == 1) - disable_hlt(); if (sleep_mode[cpu]) run_wakeup_cpu(cpu); #endif /* CONFIG_HOTPLUG_CPU */ - cpu_set(cpu, smp_commenced_mask); + cpumask_set_cpu(cpu, &smp_commenced_mask); /* Wait 5s total for a response */ for (timeout = 0 ; timeout < 5000 ; timeout++) { - if (cpu_isset(cpu, cpu_online_map)) + if (cpu_online(cpu)) break; udelay(1000); } - BUG_ON(!cpu_isset(cpu, cpu_online_map)); + BUG_ON(!cpu_online(cpu)); return 0; } @@ -959,16 +994,13 @@ int __cpu_disable(void) return -EBUSY; migrate_irqs(); - cpu_clear(cpu, current->active_mm->cpu_vm_mask); + cpumask_clear_cpu(cpu, &mm_cpumask(current->active_mm)); return 0; } void __cpu_die(unsigned int cpu) { run_sleep_cpu(cpu); - - if (num_online_cpus() == 1) - enable_hlt(); } #ifdef CONFIG_MN10300_CACHE_ENABLED @@ -1064,13 +1096,13 @@ static int hotplug_cpu_nmi_call_function(cpumask_t cpumask, do { mn10300_local_dcache_inv_range(start, end); barrier(); - } while (!cpus_empty(nmi_call_func_mask_data.started)); + } while (!cpumask_empty(&nmi_call_func_mask_data.started)); if (wait) { do { mn10300_local_dcache_inv_range(start, end); barrier(); - } while (!cpus_empty(nmi_call_func_mask_data.finished)); + } while (!cpumask_empty(&nmi_call_func_mask_data.finished)); } spin_unlock(&smp_nmi_call_lock); @@ -1081,9 +1113,9 @@ static void restart_wakeup_cpu(void) { unsigned int cpu = smp_processor_id(); - cpu_set(cpu, cpu_callin_map); + cpumask_set_cpu(cpu, &cpu_callin_map); local_flush_tlb(); - cpu_set(cpu, cpu_online_map); + set_cpu_online(cpu, true); smp_wmb(); } @@ -1114,8 +1146,9 @@ static void sleep_cpu(void *unused) static void run_sleep_cpu(unsigned int cpu) { unsigned long flags; - cpumask_t cpumask = cpumask_of(cpu); + cpumask_t cpumask; + cpumask_copy(&cpumask, &cpumask_of(cpu)); flags = arch_local_cli_save(); hotplug_cpu_nmi_call_function(cpumask, prepare_sleep_cpu, NULL, 1); hotplug_cpu_nmi_call_function(cpumask, sleep_cpu, NULL, 0); diff --git a/arch/mn10300/kernel/switch_to.S b/arch/mn10300/kernel/switch_to.S index 9074d0fb878..de3e74fc9ea 100644 --- a/arch/mn10300/kernel/switch_to.S +++ b/arch/mn10300/kernel/switch_to.S @@ -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 */ diff --git a/arch/mn10300/kernel/time.c b/arch/mn10300/kernel/time.c index 75da468090b..67c6416a58f 100644 --- a/arch/mn10300/kernel/time.c +++ b/arch/mn10300/kernel/time.c @@ -93,83 +93,6 @@ irqreturn_t local_timer_interrupt(void) return IRQ_HANDLED; } -#ifndef CONFIG_GENERIC_TIME -/* - * advance the kernel's time keeping clocks (xtime and jiffies) - * - we use Timer 0 & 1 cascaded as a clock to nudge us the next time - * there's a need to update - */ -static irqreturn_t timer_interrupt(int irq, void *dev_id) -{ - unsigned tsc, elapse; - irqreturn_t ret; - - write_seqlock(&xtime_lock); - - while (tsc = get_cycles(), - elapse = tsc - mn10300_last_tsc, /* time elapsed since last - * tick */ - elapse > MN10300_TSC_PER_HZ - ) { - mn10300_last_tsc += MN10300_TSC_PER_HZ; - - /* advance the kernel's time tracking system */ - do_timer(1); - } - - write_sequnlock(&xtime_lock); - - ret = local_timer_interrupt(); -#ifdef CONFIG_SMP - send_IPI_allbutself(LOCAL_TIMER_IPI); -#endif - return ret; -} - -static struct irqaction timer_irq = { - .handler = timer_interrupt, - .flags = IRQF_DISABLED | IRQF_SHARED | IRQF_TIMER, - .name = "timer", -}; -#endif /* CONFIG_GENERIC_TIME */ - -#ifdef CONFIG_CSRC_MN10300 -void __init clocksource_set_clock(struct clocksource *cs, unsigned int clock) -{ - u64 temp; - u32 shift; - - /* Find a shift value */ - for (shift = 32; shift > 0; shift--) { - temp = (u64) NSEC_PER_SEC << shift; - do_div(temp, clock); - if ((temp >> 32) == 0) - break; - } - cs->shift = shift; - cs->mult = (u32) temp; -} -#endif - -#if CONFIG_CEVT_MN10300 -void __cpuinit clockevent_set_clock(struct clock_event_device *cd, - unsigned int clock) -{ - u64 temp; - u32 shift; - - /* Find a shift value */ - for (shift = 32; shift > 0; shift--) { - temp = (u64) clock << shift; - do_div(temp, NSEC_PER_SEC); - if ((temp >> 32) == 0) - break; - } - cd->shift = shift; - cd->mult = (u32) temp; -} -#endif - /* * initialise the various timers used by the main part of the kernel */ @@ -181,11 +104,7 @@ void __init time_init(void) */ TMPSCNT |= TMPSCNT_ENABLE; -#ifdef CONFIG_GENERIC_TIME init_clocksource(); -#else - startup_timestamp_counter(); -#endif printk(KERN_INFO "timestamp counter I/O clock running at %lu.%02lu" @@ -194,12 +113,7 @@ void __init time_init(void) mn10300_last_tsc = read_timestamp_counter(); -#ifdef CONFIG_GENERIC_CLOCKEVENTS init_clockevents(); -#else - reload_jiffies_counter(MN10300_JC_PER_HZ - 1); - setup_jiffies_interrupt(TMJCIRQ, &timer_irq, CONFIG_TIMER_IRQ_LEVEL); -#endif #ifdef CONFIG_MN10300_WD_TIMER /* start the watchdog timer */ diff --git a/arch/mn10300/kernel/traps.c b/arch/mn10300/kernel/traps.c index b90c3f160c7..a7a987c7954 100644 --- a/arch/mn10300/kernel/traps.c +++ b/arch/mn10300/kernel/traps.c @@ -26,11 +26,11 @@ #include <linux/kdebug.h> #include <linux/bug.h> #include <linux/irq.h> +#include <linux/export.h> #include <asm/processor.h> -#include <asm/system.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <asm/io.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <asm/smp.h> #include <asm/pgalloc.h> #include <asm/cacheflush.h> @@ -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; + + case EXCEP_TRAP: + case EXCEP_UNIMPINS: + if (probe_kernel_read(&opcode, (u8 *)regs->pc, 1) < 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; -#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); \ + 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 } } @@ -182,17 +294,6 @@ void show_stack(struct task_struct *task, unsigned long *sp) } /* - * the architecture-independent dump_stack generator - */ -void dump_stack(void) -{ - unsigned long stack; - - show_stack(current, &stack); -} -EXPORT_SYMBOL(dump_stack); - -/* * dump the register file in the specified exception frame */ void show_registers_only(struct pt_regs *regs) @@ -323,86 +424,6 @@ void die(const char *str, struct pt_regs *regs, enum exception_code code) } /* - * 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 */ asmlinkage void do_double_fault(struct pt_regs *regs) @@ -481,10 +502,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 +568,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); } /* diff --git a/arch/mn10300/kernel/vmlinux.lds.S b/arch/mn10300/kernel/vmlinux.lds.S index febbeee7f2f..13c4814c29f 100644 --- a/arch/mn10300/kernel/vmlinux.lds.S +++ b/arch/mn10300/kernel/vmlinux.lds.S @@ -44,6 +44,7 @@ SECTIONS RO_DATA(PAGE_SIZE) /* writeable */ + _sdata = .; /* Start of rw data section */ RW_DATA_SECTION(32, PAGE_SIZE, THREAD_SIZE) _edata = .; @@ -70,7 +71,7 @@ SECTIONS .exit.text : { EXIT_TEXT; } .exit.data : { EXIT_DATA; } - PERCPU(PAGE_SIZE) + PERCPU_SECTION(32) . = ALIGN(PAGE_SIZE); __init_end = .; /* freed after init ends here */ |
