mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-10 01:12:45 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
18
arch/cris/arch-v32/kernel/Makefile
Normal file
18
arch/cris/arch-v32/kernel/Makefile
Normal file
|
@ -0,0 +1,18 @@
|
|||
#
|
||||
# Makefile for the linux kernel.
|
||||
#
|
||||
|
||||
extra-y := head.o
|
||||
|
||||
|
||||
obj-y := entry.o traps.o irq.o debugport.o \
|
||||
process.o ptrace.o setup.o signal.o traps.o time.o \
|
||||
cache.o cacheflush.o
|
||||
|
||||
obj-$(CONFIG_SMP) += smp.o
|
||||
obj-$(CONFIG_ETRAX_KGDB) += kgdb.o kgdb_asm.o
|
||||
obj-$(CONFIG_ETRAX_FAST_TIMER) += fasttimer.o
|
||||
obj-$(CONFIG_MODULES) += crisksyms.o
|
||||
|
||||
clean:
|
||||
|
33
arch/cris/arch-v32/kernel/cache.c
Normal file
33
arch/cris/arch-v32/kernel/cache.c
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include <linux/module.h>
|
||||
#include <asm/io.h>
|
||||
#include <arch/cache.h>
|
||||
#include <arch/hwregs/dma.h>
|
||||
|
||||
/* This file is used to workaround a cache bug, Guinness TR 106. */
|
||||
|
||||
inline void flush_dma_descr(struct dma_descr_data *descr, int flush_buf)
|
||||
{
|
||||
/* Flush descriptor to make sure we get correct in_eop and after. */
|
||||
asm volatile ("ftagd [%0]" :: "r" (descr));
|
||||
/* Flush buffer pointed out by descriptor. */
|
||||
if (flush_buf)
|
||||
cris_flush_cache_range(phys_to_virt((unsigned)descr->buf),
|
||||
(unsigned)(descr->after - descr->buf));
|
||||
}
|
||||
EXPORT_SYMBOL(flush_dma_descr);
|
||||
|
||||
void flush_dma_list(struct dma_descr_data *descr)
|
||||
{
|
||||
while (1) {
|
||||
flush_dma_descr(descr, 1);
|
||||
if (descr->eol)
|
||||
break;
|
||||
descr = phys_to_virt((unsigned)descr->next);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(flush_dma_list);
|
||||
|
||||
/* From cacheflush.S */
|
||||
EXPORT_SYMBOL(cris_flush_cache);
|
||||
/* From cacheflush.S */
|
||||
EXPORT_SYMBOL(cris_flush_cache_range);
|
99
arch/cris/arch-v32/kernel/cacheflush.S
Normal file
99
arch/cris/arch-v32/kernel/cacheflush.S
Normal file
|
@ -0,0 +1,99 @@
|
|||
.global cris_flush_cache_range
|
||||
.type cris_flush_cache_range, @function
|
||||
cris_flush_cache_range:
|
||||
move.d 1024, $r12
|
||||
cmp.d $r11, $r12
|
||||
bhi cris_flush_1KB
|
||||
nop
|
||||
add.d $r10, $r11
|
||||
ftagd [$r10]
|
||||
cris_flush_last:
|
||||
addq 32, $r10
|
||||
cmp.d $r11, $r10
|
||||
blt cris_flush_last
|
||||
ftagd [$r10]
|
||||
ret
|
||||
nop
|
||||
cris_flush_1KB:
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ftagd [$r10]
|
||||
addq 32, $r10
|
||||
ba cris_flush_cache_range
|
||||
sub.d $r12, $r11
|
||||
.size cris_flush_cache_range, . - cris_flush_cache_range
|
||||
|
||||
.global cris_flush_cache
|
||||
.type cris_flush_cache, @function
|
||||
cris_flush_cache:
|
||||
moveq 0, $r10
|
||||
cris_flush_line:
|
||||
move.d 16*1024, $r11
|
||||
addq 16, $r10
|
||||
cmp.d $r10, $r11
|
||||
blt cris_flush_line
|
||||
fidxd [$r10]
|
||||
ret
|
||||
nop
|
||||
.size cris_flush_cache, . - cris_flush_cache
|
||||
|
28
arch/cris/arch-v32/kernel/crisksyms.c
Normal file
28
arch/cris/arch-v32/kernel/crisksyms.c
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/irq.h>
|
||||
#include <arch/dma.h>
|
||||
#include <arch/intmem.h>
|
||||
#include <mach/pinmux.h>
|
||||
#include <arch/io.h>
|
||||
|
||||
/* Functions for allocating DMA channels */
|
||||
EXPORT_SYMBOL(crisv32_request_dma);
|
||||
EXPORT_SYMBOL(crisv32_free_dma);
|
||||
|
||||
/* Functions for handling internal RAM */
|
||||
EXPORT_SYMBOL(crisv32_intmem_alloc);
|
||||
EXPORT_SYMBOL(crisv32_intmem_free);
|
||||
EXPORT_SYMBOL(crisv32_intmem_phys_to_virt);
|
||||
EXPORT_SYMBOL(crisv32_intmem_virt_to_phys);
|
||||
|
||||
/* Functions for handling pinmux */
|
||||
EXPORT_SYMBOL(crisv32_pinmux_alloc);
|
||||
EXPORT_SYMBOL(crisv32_pinmux_alloc_fixed);
|
||||
EXPORT_SYMBOL(crisv32_pinmux_dealloc);
|
||||
EXPORT_SYMBOL(crisv32_pinmux_dealloc_fixed);
|
||||
EXPORT_SYMBOL(crisv32_io_get_name);
|
||||
EXPORT_SYMBOL(crisv32_io_get);
|
||||
|
||||
/* Functions masking/unmasking interrupts */
|
||||
EXPORT_SYMBOL(crisv32_mask_irq);
|
||||
EXPORT_SYMBOL(crisv32_unmask_irq);
|
203
arch/cris/arch-v32/kernel/debugport.c
Normal file
203
arch/cris/arch-v32/kernel/debugport.c
Normal file
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* Copyright (C) 2003, Axis Communications AB.
|
||||
*/
|
||||
|
||||
#include <linux/console.h>
|
||||
#include <linux/init.h>
|
||||
#include <hwregs/reg_rdwr.h>
|
||||
#include <hwregs/reg_map.h>
|
||||
#include <hwregs/ser_defs.h>
|
||||
#include <hwregs/dma_defs.h>
|
||||
#include <mach/pinmux.h>
|
||||
|
||||
struct dbg_port
|
||||
{
|
||||
unsigned char nbr;
|
||||
unsigned long instance;
|
||||
unsigned int started;
|
||||
unsigned long baudrate;
|
||||
unsigned char parity;
|
||||
unsigned int bits;
|
||||
};
|
||||
|
||||
struct dbg_port ports[] =
|
||||
{
|
||||
{
|
||||
0,
|
||||
regi_ser0,
|
||||
0,
|
||||
115200,
|
||||
'N',
|
||||
8
|
||||
},
|
||||
{
|
||||
1,
|
||||
regi_ser1,
|
||||
0,
|
||||
115200,
|
||||
'N',
|
||||
8
|
||||
},
|
||||
{
|
||||
2,
|
||||
regi_ser2,
|
||||
0,
|
||||
115200,
|
||||
'N',
|
||||
8
|
||||
},
|
||||
{
|
||||
3,
|
||||
regi_ser3,
|
||||
0,
|
||||
115200,
|
||||
'N',
|
||||
8
|
||||
},
|
||||
#if CONFIG_ETRAX_SERIAL_PORTS == 5
|
||||
{
|
||||
4,
|
||||
regi_ser4,
|
||||
0,
|
||||
115200,
|
||||
'N',
|
||||
8
|
||||
},
|
||||
#endif
|
||||
};
|
||||
static struct dbg_port *port =
|
||||
#if defined(CONFIG_ETRAX_DEBUG_PORT0)
|
||||
&ports[0];
|
||||
#elif defined(CONFIG_ETRAX_DEBUG_PORT1)
|
||||
&ports[1];
|
||||
#elif defined(CONFIG_ETRAX_DEBUG_PORT2)
|
||||
&ports[2];
|
||||
#elif defined(CONFIG_ETRAX_DEBUG_PORT3)
|
||||
&ports[3];
|
||||
#elif defined(CONFIG_ETRAX_DEBUG_PORT4)
|
||||
&ports[4];
|
||||
#else
|
||||
NULL;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ETRAX_KGDB
|
||||
static struct dbg_port *kgdb_port =
|
||||
#if defined(CONFIG_ETRAX_KGDB_PORT0)
|
||||
&ports[0];
|
||||
#elif defined(CONFIG_ETRAX_KGDB_PORT1)
|
||||
&ports[1];
|
||||
#elif defined(CONFIG_ETRAX_KGDB_PORT2)
|
||||
&ports[2];
|
||||
#elif defined(CONFIG_ETRAX_KGDB_PORT3)
|
||||
&ports[3];
|
||||
#elif defined(CONFIG_ETRAX_KGDB_PORT4)
|
||||
&ports[4];
|
||||
#else
|
||||
NULL;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static void
|
||||
start_port(struct dbg_port* p)
|
||||
{
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
if (p->started)
|
||||
return;
|
||||
p->started = 1;
|
||||
|
||||
if (p->nbr == 1)
|
||||
crisv32_pinmux_alloc_fixed(pinmux_ser1);
|
||||
else if (p->nbr == 2)
|
||||
crisv32_pinmux_alloc_fixed(pinmux_ser2);
|
||||
else if (p->nbr == 3)
|
||||
crisv32_pinmux_alloc_fixed(pinmux_ser3);
|
||||
#if CONFIG_ETRAX_SERIAL_PORTS == 5
|
||||
else if (p->nbr == 4)
|
||||
crisv32_pinmux_alloc_fixed(pinmux_ser4);
|
||||
#endif
|
||||
|
||||
/* Set up serial port registers */
|
||||
reg_ser_rw_tr_ctrl tr_ctrl = {0};
|
||||
reg_ser_rw_tr_dma_en tr_dma_en = {0};
|
||||
|
||||
reg_ser_rw_rec_ctrl rec_ctrl = {0};
|
||||
reg_ser_rw_tr_baud_div tr_baud_div = {0};
|
||||
reg_ser_rw_rec_baud_div rec_baud_div = {0};
|
||||
|
||||
tr_ctrl.base_freq = rec_ctrl.base_freq = regk_ser_f29_493;
|
||||
tr_dma_en.en = rec_ctrl.dma_mode = regk_ser_no;
|
||||
tr_baud_div.div = rec_baud_div.div = 29493000 / p->baudrate / 8;
|
||||
tr_ctrl.en = rec_ctrl.en = 1;
|
||||
|
||||
if (p->parity == 'O')
|
||||
{
|
||||
tr_ctrl.par_en = regk_ser_yes;
|
||||
tr_ctrl.par = regk_ser_odd;
|
||||
rec_ctrl.par_en = regk_ser_yes;
|
||||
rec_ctrl.par = regk_ser_odd;
|
||||
}
|
||||
else if (p->parity == 'E')
|
||||
{
|
||||
tr_ctrl.par_en = regk_ser_yes;
|
||||
tr_ctrl.par = regk_ser_even;
|
||||
rec_ctrl.par_en = regk_ser_yes;
|
||||
rec_ctrl.par = regk_ser_odd;
|
||||
}
|
||||
|
||||
if (p->bits == 7)
|
||||
{
|
||||
tr_ctrl.data_bits = regk_ser_bits7;
|
||||
rec_ctrl.data_bits = regk_ser_bits7;
|
||||
}
|
||||
|
||||
REG_WR (ser, p->instance, rw_tr_baud_div, tr_baud_div);
|
||||
REG_WR (ser, p->instance, rw_rec_baud_div, rec_baud_div);
|
||||
REG_WR (ser, p->instance, rw_tr_dma_en, tr_dma_en);
|
||||
REG_WR (ser, p->instance, rw_tr_ctrl, tr_ctrl);
|
||||
REG_WR (ser, p->instance, rw_rec_ctrl, rec_ctrl);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ETRAX_KGDB
|
||||
/* Use polling to get a single character from the kernel debug port */
|
||||
int
|
||||
getDebugChar(void)
|
||||
{
|
||||
reg_ser_rs_stat_din stat;
|
||||
reg_ser_rw_ack_intr ack_intr = { 0 };
|
||||
|
||||
do {
|
||||
stat = REG_RD(ser, kgdb_port->instance, rs_stat_din);
|
||||
} while (!stat.dav);
|
||||
|
||||
/* Ack the data_avail interrupt. */
|
||||
ack_intr.dav = 1;
|
||||
REG_WR(ser, kgdb_port->instance, rw_ack_intr, ack_intr);
|
||||
|
||||
return stat.data;
|
||||
}
|
||||
|
||||
/* Use polling to put a single character to the kernel debug port */
|
||||
void
|
||||
putDebugChar(int val)
|
||||
{
|
||||
reg_ser_r_stat_din stat;
|
||||
do {
|
||||
stat = REG_RD(ser, kgdb_port->instance, r_stat_din);
|
||||
} while (!stat.tr_rdy);
|
||||
REG_WR_INT(ser, kgdb_port->instance, rw_dout, val);
|
||||
}
|
||||
#endif /* CONFIG_ETRAX_KGDB */
|
||||
|
||||
/* Register console for printk's, etc. */
|
||||
int __init
|
||||
init_etrax_debug(void)
|
||||
{
|
||||
start_port(port);
|
||||
|
||||
#ifdef CONFIG_ETRAX_KGDB
|
||||
start_port(kgdb_port);
|
||||
#endif /* CONFIG_ETRAX_KGDB */
|
||||
return 0;
|
||||
}
|
915
arch/cris/arch-v32/kernel/entry.S
Normal file
915
arch/cris/arch-v32/kernel/entry.S
Normal file
|
@ -0,0 +1,915 @@
|
|||
/*
|
||||
* Copyright (C) 2000-2003 Axis Communications AB
|
||||
*
|
||||
* Authors: Bjorn Wesen (bjornw@axis.com)
|
||||
* Tobias Anderberg (tobiasa@axis.com), CRISv32 port.
|
||||
*
|
||||
* Code for the system-call and fault low-level handling routines.
|
||||
*
|
||||
* NOTE: This code handles signal-recognition, which happens every time
|
||||
* after a timer-interrupt and after each system call.
|
||||
*
|
||||
* Stack layout in 'ret_from_system_call':
|
||||
* ptrace needs to have all regs on the stack.
|
||||
* if the order here is changed, it needs to be
|
||||
* updated in fork.c:copy_process, signal.c:do_signal,
|
||||
* ptrace.c and ptrace.h
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <linux/sys.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
|
||||
#include <hwregs/asm/reg_map_asm.h>
|
||||
#include <hwregs/asm/intr_vect_defs_asm.h>
|
||||
|
||||
;; Exported functions.
|
||||
.globl system_call
|
||||
.globl ret_from_intr
|
||||
.globl ret_from_fork
|
||||
.globl ret_from_kernel_thread
|
||||
.globl resume
|
||||
.globl multiple_interrupt
|
||||
.globl nmi_interrupt
|
||||
.globl spurious_interrupt
|
||||
.globl do_sigtrap
|
||||
.globl gdb_handle_exception
|
||||
.globl sys_call_table
|
||||
|
||||
; Check if preemptive kernel scheduling should be done.
|
||||
#ifdef CONFIG_PREEMPT
|
||||
_resume_kernel:
|
||||
di
|
||||
; Load current task struct.
|
||||
movs.w -8192, $r0 ; THREAD_SIZE = 8192
|
||||
and.d $sp, $r0
|
||||
|
||||
addoq +TI_preempt_count, $r0, $acr
|
||||
move.d [$acr], $r10 ; Preemption disabled?
|
||||
bne _Rexit
|
||||
nop
|
||||
|
||||
_need_resched:
|
||||
addoq +TI_flags, $r0, $acr
|
||||
move.d [$acr], $r10
|
||||
btstq TIF_NEED_RESCHED, $r10 ; Check if need_resched is set.
|
||||
bpl _Rexit
|
||||
nop
|
||||
|
||||
; Do preemptive kernel scheduling.
|
||||
jsr preempt_schedule_irq
|
||||
nop
|
||||
|
||||
; Load new task struct.
|
||||
movs.w -8192, $r0 ; THREAD_SIZE = 8192.
|
||||
and.d $sp, $r0
|
||||
|
||||
; One more time with new task.
|
||||
ba _need_resched
|
||||
nop
|
||||
#else
|
||||
#define _resume_kernel _Rexit
|
||||
#endif
|
||||
|
||||
; Called at exit from fork. schedule_tail must be called to drop
|
||||
; spinlock if CONFIG_PREEMPT.
|
||||
.type ret_from_fork,@function
|
||||
ret_from_fork:
|
||||
jsr schedule_tail
|
||||
nop
|
||||
ba ret_from_sys_call
|
||||
nop
|
||||
.size ret_from_fork, . - ret_from_fork
|
||||
|
||||
.type ret_from_kernel_thread,@function
|
||||
ret_from_kernel_thread:
|
||||
jsr schedule_tail
|
||||
nop
|
||||
move.d $r2, $r10
|
||||
jsr $r1
|
||||
nop
|
||||
moveq 0, $r9 ; no syscall restarts, TYVM...
|
||||
ba ret_from_sys_call
|
||||
nop
|
||||
.size ret_from_kernel_thread, . - ret_from_kernel_thread
|
||||
|
||||
.type ret_from_intr,@function
|
||||
ret_from_intr:
|
||||
;; Check for resched if preemptive kernel, or if we're going back to
|
||||
;; user-mode. This test matches the user_regs(regs) macro. Don't simply
|
||||
;; test CCS since that doesn't necessarily reflect what mode we'll
|
||||
;; return into.
|
||||
addoq +PT_ccs, $sp, $acr
|
||||
move.d [$acr], $r0
|
||||
btstq 16, $r0 ; User-mode flag.
|
||||
bpl _resume_kernel
|
||||
.size ret_from_intr, . - ret_from_intr + 2 ; +2 includes the dslot.
|
||||
|
||||
; Note that di below is in delay slot.
|
||||
.type _resume_userspace,@function
|
||||
_resume_userspace:
|
||||
di ; So need_resched and sigpending don't change.
|
||||
|
||||
movs.w -8192, $r0 ; THREAD_SIZE == 8192
|
||||
and.d $sp, $r0
|
||||
|
||||
addoq +TI_flags, $r0, $acr ; current->work
|
||||
move.d [$acr], $r10
|
||||
and.d _TIF_WORK_MASK, $r10 ; Work to be done on return?
|
||||
bne _work_pending
|
||||
nop
|
||||
ba _Rexit
|
||||
nop
|
||||
.size _resume_userspace, . - _resume_userspace
|
||||
|
||||
;; The system_call is called by a BREAK instruction, which looks pretty
|
||||
;; much like any other exception.
|
||||
;;
|
||||
;; System calls can't be made from interrupts but we still stack ERP
|
||||
;; to have a complete stack frame.
|
||||
;;
|
||||
;; In r9 we have the wanted syscall number. Arguments come in r10,r11,r12,
|
||||
;; r13,mof,srp
|
||||
;;
|
||||
;; This function looks on the _surface_ like spaghetti programming, but it's
|
||||
;; really designed so that the fast-path does not force cache-loading of
|
||||
;; non-used instructions. Only the non-common cases cause the outlined code
|
||||
;; to run..
|
||||
|
||||
.type system_call,@function
|
||||
system_call:
|
||||
;; Stack-frame similar to the irq heads, which is reversed in
|
||||
;; ret_from_sys_call.
|
||||
|
||||
sub.d 92, $sp ; Skip EXS and EDA.
|
||||
movem $r13, [$sp]
|
||||
move.d $sp, $r8
|
||||
addq 14*4, $r8
|
||||
move.d $acr, $r0
|
||||
move $srs, $r1
|
||||
move $mof, $r2
|
||||
move $spc, $r3
|
||||
move $ccs, $r4
|
||||
move $srp, $r5
|
||||
move $erp, $r6
|
||||
subq 4, $sp
|
||||
movem $r6, [$r8]
|
||||
ei ; Enable interrupts while processing syscalls.
|
||||
move.d $r10, [$sp]
|
||||
|
||||
; Set S-bit when kernel debugging to keep hardware breakpoints active.
|
||||
#ifdef CONFIG_ETRAX_KGDB
|
||||
move $ccs, $r0
|
||||
or.d (1<<9), $r0
|
||||
move $r0, $ccs
|
||||
#endif
|
||||
|
||||
movs.w -ENOSYS, $r0
|
||||
addoq +PT_r10, $sp, $acr
|
||||
move.d $r0, [$acr]
|
||||
|
||||
;; Check if this process is syscall-traced.
|
||||
movs.w -8192, $r0 ; THREAD_SIZE == 8192
|
||||
and.d $sp, $r0
|
||||
|
||||
addoq +TI_flags, $r0, $acr
|
||||
move.d [$acr], $r0
|
||||
btstq TIF_SYSCALL_TRACE, $r0
|
||||
bmi _syscall_trace_entry
|
||||
nop
|
||||
|
||||
_syscall_traced:
|
||||
;; Check for sanity in the requested syscall number.
|
||||
cmpu.w NR_syscalls, $r9
|
||||
bhs ret_from_sys_call
|
||||
lslq 2, $r9 ; Multiply by 4, in the delay slot.
|
||||
|
||||
;; The location on the stack for the register structure is passed as a
|
||||
;; seventh argument. Some system calls need this.
|
||||
move.d $sp, $r0
|
||||
subq 4, $sp
|
||||
move.d $r0, [$sp]
|
||||
|
||||
;; The registers carrying parameters (R10-R13) are intact. The optional
|
||||
;; fifth and sixth parameters is in MOF and SRP respectively. Put them
|
||||
;; back on the stack.
|
||||
subq 4, $sp
|
||||
move $srp, [$sp]
|
||||
subq 4, $sp
|
||||
move $mof, [$sp]
|
||||
|
||||
;; Actually to the system call.
|
||||
addo.d +sys_call_table, $r9, $acr
|
||||
move.d [$acr], $acr
|
||||
jsr $acr
|
||||
nop
|
||||
|
||||
addq 3*4, $sp ; Pop the mof, srp and regs parameters.
|
||||
addoq +PT_r10, $sp, $acr
|
||||
move.d $r10, [$acr] ; Save the return value.
|
||||
|
||||
moveq 1, $r9 ; "Parameter" to ret_from_sys_call to
|
||||
; show it was a sys call.
|
||||
|
||||
;; Fall through into ret_from_sys_call to return.
|
||||
|
||||
ret_from_sys_call:
|
||||
;; R9 is a parameter:
|
||||
;; >= 1 from syscall
|
||||
;; 0 from irq
|
||||
|
||||
;; Get the current task-struct pointer.
|
||||
movs.w -8192, $r0 ; THREAD_SIZE == 8192
|
||||
and.d $sp, $r0
|
||||
|
||||
di ; Make sure need_resched and sigpending don't change.
|
||||
|
||||
addoq +TI_flags, $r0, $acr
|
||||
move.d [$acr], $r1
|
||||
and.d _TIF_ALLWORK_MASK, $r1
|
||||
bne _syscall_exit_work
|
||||
nop
|
||||
.size system_call, . - system_call
|
||||
|
||||
.type _Rexit,@function
|
||||
_Rexit:
|
||||
;; This epilogue MUST match the prologues in multiple_interrupt, irq.h
|
||||
;; and ptregs.h.
|
||||
addq 4, $sp ; Skip orig_r10.
|
||||
movem [$sp+], $r13 ; Registers R0-R13.
|
||||
move.d [$sp+], $acr
|
||||
move [$sp], $srs
|
||||
addq 4, $sp
|
||||
move [$sp+], $mof
|
||||
move [$sp+], $spc
|
||||
move [$sp+], $ccs
|
||||
move [$sp+], $srp
|
||||
move [$sp+], $erp
|
||||
addq 8, $sp ; Skip EXS, EDA.
|
||||
jump $erp
|
||||
rfe ; Restore condition code stack in delay-slot.
|
||||
.size _Rexit, . - _Rexit
|
||||
|
||||
;; We get here after doing a syscall if extra work might need to be done
|
||||
;; perform syscall exit tracing if needed.
|
||||
|
||||
.type _syscall_exit_work,@function
|
||||
_syscall_exit_work:
|
||||
;; R0 contains current at this point and irq's are disabled.
|
||||
|
||||
addoq +TI_flags, $r0, $acr
|
||||
move.d [$acr], $r1
|
||||
btstq TIF_SYSCALL_TRACE, $r1
|
||||
bpl _work_pending
|
||||
nop
|
||||
ei
|
||||
move.d $r9, $r1 ; Preserve R9.
|
||||
jsr do_syscall_trace
|
||||
nop
|
||||
move.d $r1, $r9
|
||||
ba _resume_userspace
|
||||
nop
|
||||
.size _syscall_exit_work, . - _syscall_exit_work
|
||||
|
||||
.type _work_pending,@function
|
||||
_work_pending:
|
||||
addoq +TI_flags, $r0, $acr
|
||||
move.d [$acr], $r10
|
||||
btstq TIF_NEED_RESCHED, $r10 ; Need resched?
|
||||
bpl _work_notifysig ; No, must be signal/notify.
|
||||
nop
|
||||
.size _work_pending, . - _work_pending
|
||||
|
||||
.type _work_resched,@function
|
||||
_work_resched:
|
||||
move.d $r9, $r1 ; Preserve R9.
|
||||
jsr schedule
|
||||
nop
|
||||
move.d $r1, $r9
|
||||
di
|
||||
|
||||
addoq +TI_flags, $r0, $acr
|
||||
move.d [$acr], $r1
|
||||
and.d _TIF_WORK_MASK, $r1 ; Ignore sycall trace counter.
|
||||
beq _Rexit
|
||||
nop
|
||||
btstq TIF_NEED_RESCHED, $r1
|
||||
bmi _work_resched ; current->work.need_resched.
|
||||
nop
|
||||
.size _work_resched, . - _work_resched
|
||||
|
||||
.type _work_notifysig,@function
|
||||
_work_notifysig:
|
||||
;; Deal with pending signals and notify-resume requests.
|
||||
|
||||
addoq +TI_flags, $r0, $acr
|
||||
move.d [$acr], $r12 ; The thread_info_flags parameter.
|
||||
move.d $sp, $r11 ; The regs param.
|
||||
jsr do_notify_resume
|
||||
move.d $r9, $r10 ; do_notify_resume syscall/irq param.
|
||||
|
||||
ba _Rexit
|
||||
nop
|
||||
.size _work_notifysig, . - _work_notifysig
|
||||
|
||||
;; We get here as a sidetrack when we've entered a syscall with the
|
||||
;; trace-bit set. We need to call do_syscall_trace and then continue
|
||||
;; with the call.
|
||||
|
||||
_syscall_trace_entry:
|
||||
;; PT_r10 in the frame contains -ENOSYS as required, at this point.
|
||||
|
||||
jsr do_syscall_trace
|
||||
nop
|
||||
|
||||
;; Now re-enter the syscall code to do the syscall itself. We need to
|
||||
;; restore R9 here to contain the wanted syscall, and the other
|
||||
;; parameter-bearing registers.
|
||||
addoq +PT_r9, $sp, $acr
|
||||
move.d [$acr], $r9
|
||||
addoq +PT_orig_r10, $sp, $acr
|
||||
move.d [$acr], $r10 ; PT_r10 is already -ENOSYS.
|
||||
addoq +PT_r11, $sp, $acr
|
||||
move.d [$acr], $r11
|
||||
addoq +PT_r12, $sp, $acr
|
||||
move.d [$acr], $r12
|
||||
addoq +PT_r13, $sp, $acr
|
||||
move.d [$acr], $r13
|
||||
addoq +PT_mof, $sp, $acr
|
||||
move [$acr], $mof
|
||||
addoq +PT_srp, $sp, $acr
|
||||
move [$acr], $srp
|
||||
|
||||
ba _syscall_traced
|
||||
nop
|
||||
|
||||
;; Resume performs the actual task-switching, by switching stack
|
||||
;; pointers. Input arguments are:
|
||||
;;
|
||||
;; R10 = prev
|
||||
;; R11 = next
|
||||
;; R12 = thread offset in task struct.
|
||||
;;
|
||||
;; Returns old current in R10.
|
||||
|
||||
.type resume,@function
|
||||
resume:
|
||||
subq 4, $sp ; Make space for srp.
|
||||
|
||||
add.d $r12, $r10 ; R10 = current tasks tss.
|
||||
addoq +THREAD_ccs, $r10, $acr
|
||||
move $srp, [$sp] ; Keep old/new PC on the stack.
|
||||
move $ccs, [$acr] ; Save IRQ enable state.
|
||||
di
|
||||
|
||||
addoq +THREAD_usp, $r10, $acr
|
||||
subq 10*4, $sp ; Make room for R9.
|
||||
move $usp, [$acr] ; Save user-mode stackpointer.
|
||||
|
||||
;; See copy_thread for the reason why register R9 is saved.
|
||||
movem $r9, [$sp] ; Save non-scratch registers and R9.
|
||||
|
||||
addoq +THREAD_ksp, $r10, $acr
|
||||
move.d $sp, $r10 ; Return last running task in R10.
|
||||
move.d $sp, [$acr] ; Save kernel SP for old task.
|
||||
|
||||
and.d -8192, $r10 ; Get thread_info from stackpointer.
|
||||
addoq +TI_task, $r10, $acr
|
||||
add.d $r12, $r11 ; Find the new tasks tss.
|
||||
move.d [$acr], $r10 ; Get task.
|
||||
addoq +THREAD_ksp, $r11, $acr
|
||||
move.d [$acr], $sp ; Switch to new stackframe.
|
||||
addoq +THREAD_usp, $r11, $acr
|
||||
movem [$sp+], $r9 ; Restore non-scratch registers and R9.
|
||||
|
||||
move [$acr], $usp ; Restore user-mode stackpointer.
|
||||
|
||||
addoq +THREAD_ccs, $r11, $acr
|
||||
move.d [$sp+], $r11
|
||||
jump $r11 ; Restore PC.
|
||||
move [$acr], $ccs ; Restore IRQ enable status.
|
||||
.size resume, . - resume
|
||||
|
||||
nmi_interrupt:
|
||||
|
||||
;; If we receive a watchdog interrupt while it is not expected, then set
|
||||
;; up a canonical frame and dump register contents before dying.
|
||||
|
||||
;; This prologue MUST match the one in irq.h and the struct in ptregs.h!
|
||||
subq 12, $sp ; Skip EXS, EDA.
|
||||
move $nrp, [$sp]
|
||||
subq 4, $sp
|
||||
move $srp, [$sp]
|
||||
subq 4, $sp
|
||||
move $ccs, [$sp]
|
||||
subq 4, $sp
|
||||
move $spc, [$sp]
|
||||
subq 4, $sp
|
||||
move $mof, [$sp]
|
||||
subq 4, $sp
|
||||
move $srs, [$sp]
|
||||
subq 4, $sp
|
||||
move.d $acr, [$sp]
|
||||
subq 14*4, $sp ; Make room for R0-R13.
|
||||
movem $r13, [$sp] ; Push R0-R13.
|
||||
subq 4, $sp
|
||||
move.d $r10, [$sp] ; Push orig_r10.
|
||||
move.d REG_ADDR(intr_vect, regi_irq, r_nmi), $r0
|
||||
move.d [$r0], $r0
|
||||
btstq REG_BIT(intr_vect, r_nmi, watchdog), $r0
|
||||
bpl 1f
|
||||
nop
|
||||
jsr handle_watchdog_bite ; In time.c.
|
||||
move.d $sp, $r10 ; Pointer to registers
|
||||
1: btstq REG_BIT(intr_vect, r_nmi, ext), $r0
|
||||
bpl 1f
|
||||
nop
|
||||
jsr handle_nmi
|
||||
move.d $sp, $r10 ; Pointer to registers
|
||||
1: addq 4, $sp ; Skip orig_r10
|
||||
movem [$sp+], $r13
|
||||
move.d [$sp+], $acr
|
||||
move [$sp], $srs
|
||||
addq 4, $sp
|
||||
move [$sp+], $mof
|
||||
move [$sp+], $spc
|
||||
move [$sp+], $ccs
|
||||
move [$sp+], $srp
|
||||
move [$sp+], $nrp
|
||||
addq 8, $sp ; Skip EXS, EDA.
|
||||
jump $nrp
|
||||
rfn
|
||||
|
||||
.comm cause_of_death, 4 ;; Don't declare this anywhere.
|
||||
|
||||
spurious_interrupt:
|
||||
di
|
||||
jump hard_reset_now
|
||||
nop
|
||||
|
||||
;; This handles the case when multiple interrupts arrive at the same
|
||||
;; time. Jump to the first set interrupt bit in a priority fashion. The
|
||||
;; hardware will call the unserved interrupts after the handler
|
||||
;; finishes.
|
||||
.type multiple_interrupt, @function
|
||||
multiple_interrupt:
|
||||
;; This prologue MUST match the one in irq.h and the struct in ptregs.h!
|
||||
subq 12, $sp ; Skip EXS, EDA.
|
||||
move $erp, [$sp]
|
||||
subq 4, $sp
|
||||
move $srp, [$sp]
|
||||
subq 4, $sp
|
||||
move $ccs, [$sp]
|
||||
subq 4, $sp
|
||||
move $spc, [$sp]
|
||||
subq 4, $sp
|
||||
move $mof, [$sp]
|
||||
subq 4, $sp
|
||||
move $srs, [$sp]
|
||||
subq 4, $sp
|
||||
move.d $acr, [$sp]
|
||||
subq 14*4, $sp ; Make room for R0-R13.
|
||||
movem $r13, [$sp] ; Push R0-R13.
|
||||
subq 4, $sp
|
||||
move.d $r10, [$sp] ; Push orig_r10.
|
||||
|
||||
; Set S-bit when kernel debugging to keep hardware breakpoints active.
|
||||
#ifdef CONFIG_ETRAX_KGDB
|
||||
move $ccs, $r0
|
||||
or.d (1<<9), $r0
|
||||
move $r0, $ccs
|
||||
#endif
|
||||
|
||||
jsr crisv32_do_multiple
|
||||
move.d $sp, $r10
|
||||
jump ret_from_intr
|
||||
nop
|
||||
.size multiple_interrupt, . - multiple_interrupt
|
||||
|
||||
do_sigtrap:
|
||||
;; Sigtraps the process that executed the BREAK instruction. Creates a
|
||||
;; frame that Rexit expects.
|
||||
subq 4, $sp
|
||||
move $eda, [$sp]
|
||||
subq 4, $sp
|
||||
move $exs, [$sp]
|
||||
subq 4, $sp
|
||||
move $erp, [$sp]
|
||||
subq 4, $sp
|
||||
move $srp, [$sp]
|
||||
subq 4, $sp
|
||||
move $ccs, [$sp]
|
||||
subq 4, $sp
|
||||
move $spc, [$sp]
|
||||
subq 4, $sp
|
||||
move $mof, [$sp]
|
||||
subq 4, $sp
|
||||
move $srs, [$sp]
|
||||
subq 4, $sp
|
||||
move.d $acr, [$sp]
|
||||
di ; Need to disable irq's at this point.
|
||||
subq 14*4, $sp ; Make room for r0-r13.
|
||||
movem $r13, [$sp] ; Push the r0-r13 registers.
|
||||
subq 4, $sp
|
||||
move.d $r10, [$sp] ; Push orig_r10.
|
||||
|
||||
movs.w -8192, $r9 ; THREAD_SIZE == 8192
|
||||
and.d $sp, $r9
|
||||
|
||||
;; thread_info as first parameter
|
||||
move.d $r9, $r10
|
||||
moveq 5, $r11 ; SIGTRAP as second argument.
|
||||
jsr ugdb_trap_user
|
||||
nop
|
||||
jump ret_from_intr ; Use the return routine for interrupts.
|
||||
nop
|
||||
|
||||
gdb_handle_exception:
|
||||
subq 4, $sp
|
||||
move.d $r0, [$sp]
|
||||
#ifdef CONFIG_ETRAX_KGDB
|
||||
move $ccs, $r0 ; U-flag not affected by previous insns.
|
||||
btstq 16, $r0 ; Test the U-flag.
|
||||
bmi _ugdb_handle_exception ; Go to user mode debugging.
|
||||
nop ; Empty delay-slot (cannot pop R0 here).
|
||||
ba kgdb_handle_exception ; Go to kernel debugging.
|
||||
move.d [$sp+], $r0 ; Restore R0 in delay slot.
|
||||
#endif
|
||||
|
||||
_ugdb_handle_exception:
|
||||
ba do_sigtrap ; SIGTRAP the offending process.
|
||||
move.d [$sp+], $r0 ; Restore R0 in delay slot.
|
||||
|
||||
.data
|
||||
|
||||
.section .rodata,"a"
|
||||
sys_call_table:
|
||||
.long sys_restart_syscall ; 0 - old "setup()" system call, used
|
||||
; for restarting.
|
||||
.long sys_exit
|
||||
.long sys_fork
|
||||
.long sys_read
|
||||
.long sys_write
|
||||
.long sys_open /* 5 */
|
||||
.long sys_close
|
||||
.long sys_waitpid
|
||||
.long sys_creat
|
||||
.long sys_link
|
||||
.long sys_unlink /* 10 */
|
||||
.long sys_execve
|
||||
.long sys_chdir
|
||||
.long sys_time
|
||||
.long sys_mknod
|
||||
.long sys_chmod /* 15 */
|
||||
.long sys_lchown16
|
||||
.long sys_ni_syscall /* old break syscall holder */
|
||||
.long sys_stat
|
||||
.long sys_lseek
|
||||
.long sys_getpid /* 20 */
|
||||
.long sys_mount
|
||||
.long sys_oldumount
|
||||
.long sys_setuid16
|
||||
.long sys_getuid16
|
||||
.long sys_stime /* 25 */
|
||||
.long sys_ptrace
|
||||
.long sys_alarm
|
||||
.long sys_fstat
|
||||
.long sys_pause
|
||||
.long sys_utime /* 30 */
|
||||
.long sys_ni_syscall /* old stty syscall holder */
|
||||
.long sys_ni_syscall /* old gtty syscall holder */
|
||||
.long sys_access
|
||||
.long sys_nice
|
||||
.long sys_ni_syscall /* 35 old ftime syscall holder */
|
||||
.long sys_sync
|
||||
.long sys_kill
|
||||
.long sys_rename
|
||||
.long sys_mkdir
|
||||
.long sys_rmdir /* 40 */
|
||||
.long sys_dup
|
||||
.long sys_pipe
|
||||
.long sys_times
|
||||
.long sys_ni_syscall /* old prof syscall holder */
|
||||
.long sys_brk /* 45 */
|
||||
.long sys_setgid16
|
||||
.long sys_getgid16
|
||||
.long sys_signal
|
||||
.long sys_geteuid16
|
||||
.long sys_getegid16 /* 50 */
|
||||
.long sys_acct
|
||||
.long sys_umount /* recycled never used phys( */
|
||||
.long sys_ni_syscall /* old lock syscall holder */
|
||||
.long sys_ioctl
|
||||
.long sys_fcntl /* 55 */
|
||||
.long sys_ni_syscall /* old mpx syscall holder */
|
||||
.long sys_setpgid
|
||||
.long sys_ni_syscall /* old ulimit syscall holder */
|
||||
.long sys_ni_syscall /* old sys_olduname holder */
|
||||
.long sys_umask /* 60 */
|
||||
.long sys_chroot
|
||||
.long sys_ustat
|
||||
.long sys_dup2
|
||||
.long sys_getppid
|
||||
.long sys_getpgrp /* 65 */
|
||||
.long sys_setsid
|
||||
.long sys_sigaction
|
||||
.long sys_sgetmask
|
||||
.long sys_ssetmask
|
||||
.long sys_setreuid16 /* 70 */
|
||||
.long sys_setregid16
|
||||
.long sys_sigsuspend
|
||||
.long sys_sigpending
|
||||
.long sys_sethostname
|
||||
.long sys_setrlimit /* 75 */
|
||||
.long sys_old_getrlimit
|
||||
.long sys_getrusage
|
||||
.long sys_gettimeofday
|
||||
.long sys_settimeofday
|
||||
.long sys_getgroups16 /* 80 */
|
||||
.long sys_setgroups16
|
||||
.long sys_select /* was old_select in Linux/E100 */
|
||||
.long sys_symlink
|
||||
.long sys_lstat
|
||||
.long sys_readlink /* 85 */
|
||||
.long sys_uselib
|
||||
.long sys_swapon
|
||||
.long sys_reboot
|
||||
.long sys_old_readdir
|
||||
.long sys_old_mmap /* 90 */
|
||||
.long sys_munmap
|
||||
.long sys_truncate
|
||||
.long sys_ftruncate
|
||||
.long sys_fchmod
|
||||
.long sys_fchown16 /* 95 */
|
||||
.long sys_getpriority
|
||||
.long sys_setpriority
|
||||
.long sys_ni_syscall /* old profil syscall holder */
|
||||
.long sys_statfs
|
||||
.long sys_fstatfs /* 100 */
|
||||
.long sys_ni_syscall /* sys_ioperm in i386 */
|
||||
.long sys_socketcall
|
||||
.long sys_syslog
|
||||
.long sys_setitimer
|
||||
.long sys_getitimer /* 105 */
|
||||
.long sys_newstat
|
||||
.long sys_newlstat
|
||||
.long sys_newfstat
|
||||
.long sys_ni_syscall /* old sys_uname holder */
|
||||
.long sys_ni_syscall /* sys_iopl in i386 */
|
||||
.long sys_vhangup
|
||||
.long sys_ni_syscall /* old "idle" system call */
|
||||
.long sys_ni_syscall /* vm86old in i386 */
|
||||
.long sys_wait4
|
||||
.long sys_swapoff /* 115 */
|
||||
.long sys_sysinfo
|
||||
.long sys_ipc
|
||||
.long sys_fsync
|
||||
.long sys_sigreturn
|
||||
.long sys_clone /* 120 */
|
||||
.long sys_setdomainname
|
||||
.long sys_newuname
|
||||
.long sys_ni_syscall /* sys_modify_ldt */
|
||||
.long sys_adjtimex
|
||||
.long sys_mprotect /* 125 */
|
||||
.long sys_sigprocmask
|
||||
.long sys_ni_syscall /* old "create_module" */
|
||||
.long sys_init_module
|
||||
.long sys_delete_module
|
||||
.long sys_ni_syscall /* 130: old "get_kernel_syms" */
|
||||
.long sys_quotactl
|
||||
.long sys_getpgid
|
||||
.long sys_fchdir
|
||||
.long sys_bdflush
|
||||
.long sys_sysfs /* 135 */
|
||||
.long sys_personality
|
||||
.long sys_ni_syscall /* for afs_syscall */
|
||||
.long sys_setfsuid16
|
||||
.long sys_setfsgid16
|
||||
.long sys_llseek /* 140 */
|
||||
.long sys_getdents
|
||||
.long sys_select
|
||||
.long sys_flock
|
||||
.long sys_msync
|
||||
.long sys_readv /* 145 */
|
||||
.long sys_writev
|
||||
.long sys_getsid
|
||||
.long sys_fdatasync
|
||||
.long sys_sysctl
|
||||
.long sys_mlock /* 150 */
|
||||
.long sys_munlock
|
||||
.long sys_mlockall
|
||||
.long sys_munlockall
|
||||
.long sys_sched_setparam
|
||||
.long sys_sched_getparam /* 155 */
|
||||
.long sys_sched_setscheduler
|
||||
.long sys_sched_getscheduler
|
||||
.long sys_sched_yield
|
||||
.long sys_sched_get_priority_max
|
||||
.long sys_sched_get_priority_min /* 160 */
|
||||
.long sys_sched_rr_get_interval
|
||||
.long sys_nanosleep
|
||||
.long sys_mremap
|
||||
.long sys_setresuid16
|
||||
.long sys_getresuid16 /* 165 */
|
||||
.long sys_ni_syscall /* sys_vm86 */
|
||||
.long sys_ni_syscall /* Old sys_query_module */
|
||||
.long sys_poll
|
||||
.long sys_ni_syscall /* Old nfsservctl */
|
||||
.long sys_setresgid16 /* 170 */
|
||||
.long sys_getresgid16
|
||||
.long sys_prctl
|
||||
.long sys_rt_sigreturn
|
||||
.long sys_rt_sigaction
|
||||
.long sys_rt_sigprocmask /* 175 */
|
||||
.long sys_rt_sigpending
|
||||
.long sys_rt_sigtimedwait
|
||||
.long sys_rt_sigqueueinfo
|
||||
.long sys_rt_sigsuspend
|
||||
.long sys_pread64 /* 180 */
|
||||
.long sys_pwrite64
|
||||
.long sys_chown16
|
||||
.long sys_getcwd
|
||||
.long sys_capget
|
||||
.long sys_capset /* 185 */
|
||||
.long sys_sigaltstack
|
||||
.long sys_sendfile
|
||||
.long sys_ni_syscall /* streams1 */
|
||||
.long sys_ni_syscall /* streams2 */
|
||||
.long sys_vfork /* 190 */
|
||||
.long sys_getrlimit
|
||||
.long sys_mmap2
|
||||
.long sys_truncate64
|
||||
.long sys_ftruncate64
|
||||
.long sys_stat64 /* 195 */
|
||||
.long sys_lstat64
|
||||
.long sys_fstat64
|
||||
.long sys_lchown
|
||||
.long sys_getuid
|
||||
.long sys_getgid /* 200 */
|
||||
.long sys_geteuid
|
||||
.long sys_getegid
|
||||
.long sys_setreuid
|
||||
.long sys_setregid
|
||||
.long sys_getgroups /* 205 */
|
||||
.long sys_setgroups
|
||||
.long sys_fchown
|
||||
.long sys_setresuid
|
||||
.long sys_getresuid
|
||||
.long sys_setresgid /* 210 */
|
||||
.long sys_getresgid
|
||||
.long sys_chown
|
||||
.long sys_setuid
|
||||
.long sys_setgid
|
||||
.long sys_setfsuid /* 215 */
|
||||
.long sys_setfsgid
|
||||
.long sys_pivot_root
|
||||
.long sys_mincore
|
||||
.long sys_madvise
|
||||
.long sys_getdents64 /* 220 */
|
||||
.long sys_fcntl64
|
||||
.long sys_ni_syscall /* reserved for TUX */
|
||||
.long sys_ni_syscall
|
||||
.long sys_gettid
|
||||
.long sys_readahead /* 225 */
|
||||
.long sys_setxattr
|
||||
.long sys_lsetxattr
|
||||
.long sys_fsetxattr
|
||||
.long sys_getxattr
|
||||
.long sys_lgetxattr /* 230 */
|
||||
.long sys_fgetxattr
|
||||
.long sys_listxattr
|
||||
.long sys_llistxattr
|
||||
.long sys_flistxattr
|
||||
.long sys_removexattr /* 235 */
|
||||
.long sys_lremovexattr
|
||||
.long sys_fremovexattr
|
||||
.long sys_tkill
|
||||
.long sys_sendfile64
|
||||
.long sys_futex /* 240 */
|
||||
.long sys_sched_setaffinity
|
||||
.long sys_sched_getaffinity
|
||||
.long sys_ni_syscall /* sys_set_thread_area */
|
||||
.long sys_ni_syscall /* sys_get_thread_area */
|
||||
.long sys_io_setup /* 245 */
|
||||
.long sys_io_destroy
|
||||
.long sys_io_getevents
|
||||
.long sys_io_submit
|
||||
.long sys_io_cancel
|
||||
.long sys_fadvise64 /* 250 */
|
||||
.long sys_ni_syscall
|
||||
.long sys_exit_group
|
||||
.long sys_lookup_dcookie
|
||||
.long sys_epoll_create
|
||||
.long sys_epoll_ctl /* 255 */
|
||||
.long sys_epoll_wait
|
||||
.long sys_remap_file_pages
|
||||
.long sys_set_tid_address
|
||||
.long sys_timer_create
|
||||
.long sys_timer_settime /* 260 */
|
||||
.long sys_timer_gettime
|
||||
.long sys_timer_getoverrun
|
||||
.long sys_timer_delete
|
||||
.long sys_clock_settime
|
||||
.long sys_clock_gettime /* 265 */
|
||||
.long sys_clock_getres
|
||||
.long sys_clock_nanosleep
|
||||
.long sys_statfs64
|
||||
.long sys_fstatfs64
|
||||
.long sys_tgkill /* 270 */
|
||||
.long sys_utimes
|
||||
.long sys_fadvise64_64
|
||||
.long sys_ni_syscall /* sys_vserver */
|
||||
.long sys_ni_syscall /* sys_mbind */
|
||||
.long sys_ni_syscall /* 275 sys_get_mempolicy */
|
||||
.long sys_ni_syscall /* sys_set_mempolicy */
|
||||
.long sys_mq_open
|
||||
.long sys_mq_unlink
|
||||
.long sys_mq_timedsend
|
||||
.long sys_mq_timedreceive /* 280 */
|
||||
.long sys_mq_notify
|
||||
.long sys_mq_getsetattr
|
||||
.long sys_ni_syscall /* reserved for kexec */
|
||||
.long sys_waitid
|
||||
.long sys_ni_syscall /* 285 */ /* available */
|
||||
.long sys_add_key
|
||||
.long sys_request_key
|
||||
.long sys_keyctl
|
||||
.long sys_ioprio_set
|
||||
.long sys_ioprio_get /* 290 */
|
||||
.long sys_inotify_init
|
||||
.long sys_inotify_add_watch
|
||||
.long sys_inotify_rm_watch
|
||||
.long sys_migrate_pages
|
||||
.long sys_openat /* 295 */
|
||||
.long sys_mkdirat
|
||||
.long sys_mknodat
|
||||
.long sys_fchownat
|
||||
.long sys_futimesat
|
||||
.long sys_fstatat64 /* 300 */
|
||||
.long sys_unlinkat
|
||||
.long sys_renameat
|
||||
.long sys_linkat
|
||||
.long sys_symlinkat
|
||||
.long sys_readlinkat /* 305 */
|
||||
.long sys_fchmodat
|
||||
.long sys_faccessat
|
||||
.long sys_pselect6
|
||||
.long sys_ppoll
|
||||
.long sys_unshare /* 310 */
|
||||
.long sys_set_robust_list
|
||||
.long sys_get_robust_list
|
||||
.long sys_splice
|
||||
.long sys_sync_file_range
|
||||
.long sys_tee /* 315 */
|
||||
.long sys_vmsplice
|
||||
.long sys_move_pages
|
||||
.long sys_getcpu
|
||||
.long sys_epoll_pwait
|
||||
.long sys_utimensat /* 320 */
|
||||
.long sys_signalfd
|
||||
.long sys_timerfd_create
|
||||
.long sys_eventfd
|
||||
.long sys_fallocate
|
||||
.long sys_timerfd_settime /* 325 */
|
||||
.long sys_timerfd_gettime
|
||||
.long sys_signalfd4
|
||||
.long sys_eventfd2
|
||||
.long sys_epoll_create1
|
||||
.long sys_dup3 /* 330 */
|
||||
.long sys_pipe2
|
||||
.long sys_inotify_init1
|
||||
.long sys_preadv
|
||||
.long sys_pwritev
|
||||
.long sys_setns /* 335 */
|
||||
.long sys_name_to_handle_at
|
||||
.long sys_open_by_handle_at
|
||||
.long sys_rt_tgsigqueueinfo
|
||||
.long sys_perf_event_open
|
||||
.long sys_recvmmsg /* 340 */
|
||||
.long sys_accept4
|
||||
.long sys_fanotify_init
|
||||
.long sys_fanotify_mark
|
||||
.long sys_prlimit64
|
||||
.long sys_clock_adjtime /* 345 */
|
||||
.long sys_syncfs
|
||||
.long sys_sendmmsg
|
||||
.long sys_process_vm_readv
|
||||
.long sys_process_vm_writev
|
||||
.long sys_kcmp /* 350 */
|
||||
.long sys_finit_module
|
||||
|
||||
/*
|
||||
* NOTE!! This doesn't have to be exact - we just have
|
||||
* to make sure we have _enough_ of the "sys_ni_syscall"
|
||||
* entries. Don't panic if you notice that this hasn't
|
||||
* been shrunk every time we add a new system call.
|
||||
*/
|
||||
|
||||
.rept NR_syscalls - (.-sys_call_table) / 4
|
||||
.long sys_ni_syscall
|
||||
.endr
|
||||
|
800
arch/cris/arch-v32/kernel/fasttimer.c
Normal file
800
arch/cris/arch-v32/kernel/fasttimer.c
Normal file
|
@ -0,0 +1,800 @@
|
|||
/*
|
||||
* linux/arch/cris/kernel/fasttimer.c
|
||||
*
|
||||
* Fast timers for ETRAX FS
|
||||
*
|
||||
* Copyright (C) 2000-2006 Axis Communications AB, Lund, Sweden
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/param.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
|
||||
#include <hwregs/reg_map.h>
|
||||
#include <hwregs/reg_rdwr.h>
|
||||
#include <hwregs/timer_defs.h>
|
||||
#include <asm/fasttimer.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
/*
|
||||
* timer0 is running at 100MHz and generating jiffies timer ticks
|
||||
* at 100 or 1000 HZ.
|
||||
* fasttimer gives an API that gives timers that expire "between" the jiffies
|
||||
* giving microsecond resolution (10 ns).
|
||||
* fasttimer uses reg_timer_rw_trig register to get interrupt when
|
||||
* r_time reaches a certain value.
|
||||
*/
|
||||
|
||||
|
||||
#define DEBUG_LOG_INCLUDED
|
||||
#define FAST_TIMER_LOG
|
||||
/* #define FAST_TIMER_TEST */
|
||||
|
||||
#define FAST_TIMER_SANITY_CHECKS
|
||||
|
||||
#ifdef FAST_TIMER_SANITY_CHECKS
|
||||
static int sanity_failed;
|
||||
#endif
|
||||
|
||||
#define D1(x)
|
||||
#define D2(x)
|
||||
#define DP(x)
|
||||
|
||||
static unsigned int fast_timer_running;
|
||||
static unsigned int fast_timers_added;
|
||||
static unsigned int fast_timers_started;
|
||||
static unsigned int fast_timers_expired;
|
||||
static unsigned int fast_timers_deleted;
|
||||
static unsigned int fast_timer_is_init;
|
||||
static unsigned int fast_timer_ints;
|
||||
|
||||
struct fast_timer *fast_timer_list = NULL;
|
||||
|
||||
#ifdef DEBUG_LOG_INCLUDED
|
||||
#define DEBUG_LOG_MAX 128
|
||||
static const char * debug_log_string[DEBUG_LOG_MAX];
|
||||
static unsigned long debug_log_value[DEBUG_LOG_MAX];
|
||||
static unsigned int debug_log_cnt;
|
||||
static unsigned int debug_log_cnt_wrapped;
|
||||
|
||||
#define DEBUG_LOG(string, value) \
|
||||
{ \
|
||||
unsigned long log_flags; \
|
||||
local_irq_save(log_flags); \
|
||||
debug_log_string[debug_log_cnt] = (string); \
|
||||
debug_log_value[debug_log_cnt] = (unsigned long)(value); \
|
||||
if (++debug_log_cnt >= DEBUG_LOG_MAX) \
|
||||
{ \
|
||||
debug_log_cnt = debug_log_cnt % DEBUG_LOG_MAX; \
|
||||
debug_log_cnt_wrapped = 1; \
|
||||
} \
|
||||
local_irq_restore(log_flags); \
|
||||
}
|
||||
#else
|
||||
#define DEBUG_LOG(string, value)
|
||||
#endif
|
||||
|
||||
|
||||
#define NUM_TIMER_STATS 16
|
||||
#ifdef FAST_TIMER_LOG
|
||||
struct fast_timer timer_added_log[NUM_TIMER_STATS];
|
||||
struct fast_timer timer_started_log[NUM_TIMER_STATS];
|
||||
struct fast_timer timer_expired_log[NUM_TIMER_STATS];
|
||||
#endif
|
||||
|
||||
int timer_div_settings[NUM_TIMER_STATS];
|
||||
int timer_delay_settings[NUM_TIMER_STATS];
|
||||
|
||||
struct work_struct fast_work;
|
||||
|
||||
static void
|
||||
timer_trig_handler(struct work_struct *work);
|
||||
|
||||
|
||||
|
||||
/* Not true gettimeofday, only checks the jiffies (uptime) + useconds */
|
||||
inline void do_gettimeofday_fast(struct fasttime_t *tv)
|
||||
{
|
||||
tv->tv_jiff = jiffies;
|
||||
tv->tv_usec = GET_JIFFIES_USEC();
|
||||
}
|
||||
|
||||
inline int fasttime_cmp(struct fasttime_t *t0, struct fasttime_t *t1)
|
||||
{
|
||||
/* Compare jiffies. Takes care of wrapping */
|
||||
if (time_before(t0->tv_jiff, t1->tv_jiff))
|
||||
return -1;
|
||||
else if (time_after(t0->tv_jiff, t1->tv_jiff))
|
||||
return 1;
|
||||
|
||||
/* Compare us */
|
||||
if (t0->tv_usec < t1->tv_usec)
|
||||
return -1;
|
||||
else if (t0->tv_usec > t1->tv_usec)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called with ints off */
|
||||
inline void start_timer_trig(unsigned long delay_us)
|
||||
{
|
||||
reg_timer_rw_ack_intr ack_intr = { 0 };
|
||||
reg_timer_rw_intr_mask intr_mask;
|
||||
reg_timer_rw_trig trig;
|
||||
reg_timer_rw_trig_cfg trig_cfg = { 0 };
|
||||
reg_timer_r_time r_time0;
|
||||
reg_timer_r_time r_time1;
|
||||
unsigned char trig_wrap;
|
||||
unsigned char time_wrap;
|
||||
|
||||
r_time0 = REG_RD(timer, regi_timer0, r_time);
|
||||
|
||||
D1(printk("start_timer_trig : %d us freq: %i div: %i\n",
|
||||
delay_us, freq_index, div));
|
||||
/* Clear trig irq */
|
||||
intr_mask = REG_RD(timer, regi_timer0, rw_intr_mask);
|
||||
intr_mask.trig = 0;
|
||||
REG_WR(timer, regi_timer0, rw_intr_mask, intr_mask);
|
||||
|
||||
/* Set timer values and check if trigger wraps. */
|
||||
/* r_time is 100MHz (10 ns resolution) */
|
||||
trig_wrap = (trig = r_time0 + delay_us*(1000/10)) < r_time0;
|
||||
|
||||
timer_div_settings[fast_timers_started % NUM_TIMER_STATS] = trig;
|
||||
timer_delay_settings[fast_timers_started % NUM_TIMER_STATS] = delay_us;
|
||||
|
||||
/* Ack interrupt */
|
||||
ack_intr.trig = 1;
|
||||
REG_WR(timer, regi_timer0, rw_ack_intr, ack_intr);
|
||||
|
||||
/* Start timer */
|
||||
REG_WR(timer, regi_timer0, rw_trig, trig);
|
||||
trig_cfg.tmr = regk_timer_time;
|
||||
REG_WR(timer, regi_timer0, rw_trig_cfg, trig_cfg);
|
||||
|
||||
/* Check if we have already passed the trig time */
|
||||
r_time1 = REG_RD(timer, regi_timer0, r_time);
|
||||
time_wrap = r_time1 < r_time0;
|
||||
|
||||
if ((trig_wrap && !time_wrap) || (r_time1 < trig)) {
|
||||
/* No, Enable trig irq */
|
||||
intr_mask = REG_RD(timer, regi_timer0, rw_intr_mask);
|
||||
intr_mask.trig = 1;
|
||||
REG_WR(timer, regi_timer0, rw_intr_mask, intr_mask);
|
||||
fast_timers_started++;
|
||||
fast_timer_running = 1;
|
||||
} else {
|
||||
/* We have passed the time, disable trig point, ack intr */
|
||||
trig_cfg.tmr = regk_timer_off;
|
||||
REG_WR(timer, regi_timer0, rw_trig_cfg, trig_cfg);
|
||||
REG_WR(timer, regi_timer0, rw_ack_intr, ack_intr);
|
||||
/* call the int routine */
|
||||
INIT_WORK(&fast_work, timer_trig_handler);
|
||||
schedule_work(&fast_work);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* In version 1.4 this function takes 27 - 50 us */
|
||||
void start_one_shot_timer(struct fast_timer *t,
|
||||
fast_timer_function_type *function,
|
||||
unsigned long data,
|
||||
unsigned long delay_us,
|
||||
const char *name)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct fast_timer *tmp;
|
||||
|
||||
D1(printk("sft %s %d us\n", name, delay_us));
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
do_gettimeofday_fast(&t->tv_set);
|
||||
tmp = fast_timer_list;
|
||||
|
||||
#ifdef FAST_TIMER_SANITY_CHECKS
|
||||
/* Check so this is not in the list already... */
|
||||
while (tmp != NULL) {
|
||||
if (tmp == t) {
|
||||
printk(KERN_DEBUG
|
||||
"timer name: %s data: 0x%08lX already "
|
||||
"in list!\n", name, data);
|
||||
sanity_failed++;
|
||||
goto done;
|
||||
} else
|
||||
tmp = tmp->next;
|
||||
}
|
||||
tmp = fast_timer_list;
|
||||
#endif
|
||||
|
||||
t->delay_us = delay_us;
|
||||
t->function = function;
|
||||
t->data = data;
|
||||
t->name = name;
|
||||
|
||||
t->tv_expires.tv_usec = t->tv_set.tv_usec + delay_us % 1000000;
|
||||
t->tv_expires.tv_jiff = t->tv_set.tv_jiff + delay_us / 1000000 / HZ;
|
||||
if (t->tv_expires.tv_usec > 1000000) {
|
||||
t->tv_expires.tv_usec -= 1000000;
|
||||
t->tv_expires.tv_jiff += HZ;
|
||||
}
|
||||
#ifdef FAST_TIMER_LOG
|
||||
timer_added_log[fast_timers_added % NUM_TIMER_STATS] = *t;
|
||||
#endif
|
||||
fast_timers_added++;
|
||||
|
||||
/* Check if this should timeout before anything else */
|
||||
if (tmp == NULL || fasttime_cmp(&t->tv_expires, &tmp->tv_expires) < 0) {
|
||||
/* Put first in list and modify the timer value */
|
||||
t->prev = NULL;
|
||||
t->next = fast_timer_list;
|
||||
if (fast_timer_list)
|
||||
fast_timer_list->prev = t;
|
||||
fast_timer_list = t;
|
||||
#ifdef FAST_TIMER_LOG
|
||||
timer_started_log[fast_timers_started % NUM_TIMER_STATS] = *t;
|
||||
#endif
|
||||
start_timer_trig(delay_us);
|
||||
} else {
|
||||
/* Put in correct place in list */
|
||||
while (tmp->next &&
|
||||
fasttime_cmp(&t->tv_expires, &tmp->next->tv_expires) > 0)
|
||||
tmp = tmp->next;
|
||||
/* Insert t after tmp */
|
||||
t->prev = tmp;
|
||||
t->next = tmp->next;
|
||||
if (tmp->next)
|
||||
{
|
||||
tmp->next->prev = t;
|
||||
}
|
||||
tmp->next = t;
|
||||
}
|
||||
|
||||
D2(printk("start_one_shot_timer: %d us done\n", delay_us));
|
||||
|
||||
done:
|
||||
local_irq_restore(flags);
|
||||
} /* start_one_shot_timer */
|
||||
|
||||
static inline int fast_timer_pending (const struct fast_timer * t)
|
||||
{
|
||||
return (t->next != NULL) || (t->prev != NULL) || (t == fast_timer_list);
|
||||
}
|
||||
|
||||
static inline int detach_fast_timer (struct fast_timer *t)
|
||||
{
|
||||
struct fast_timer *next, *prev;
|
||||
if (!fast_timer_pending(t))
|
||||
return 0;
|
||||
next = t->next;
|
||||
prev = t->prev;
|
||||
if (next)
|
||||
next->prev = prev;
|
||||
if (prev)
|
||||
prev->next = next;
|
||||
else
|
||||
fast_timer_list = next;
|
||||
fast_timers_deleted++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int del_fast_timer(struct fast_timer * t)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
local_irq_save(flags);
|
||||
ret = detach_fast_timer(t);
|
||||
t->next = t->prev = NULL;
|
||||
local_irq_restore(flags);
|
||||
return ret;
|
||||
} /* del_fast_timer */
|
||||
|
||||
|
||||
/* Interrupt routines or functions called in interrupt context */
|
||||
|
||||
/* Timer interrupt handler for trig interrupts */
|
||||
|
||||
static irqreturn_t
|
||||
timer_trig_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
reg_timer_r_masked_intr masked_intr;
|
||||
/* Check if the timer interrupt is for us (a trig int) */
|
||||
masked_intr = REG_RD(timer, regi_timer0, r_masked_intr);
|
||||
if (!masked_intr.trig)
|
||||
return IRQ_NONE;
|
||||
timer_trig_handler(NULL);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void timer_trig_handler(struct work_struct *work)
|
||||
{
|
||||
reg_timer_rw_ack_intr ack_intr = { 0 };
|
||||
reg_timer_rw_intr_mask intr_mask;
|
||||
reg_timer_rw_trig_cfg trig_cfg = { 0 };
|
||||
struct fast_timer *t;
|
||||
unsigned long flags;
|
||||
|
||||
/* We keep interrupts disabled not only when we modify the
|
||||
* fast timer list, but any time we hold a reference to a
|
||||
* timer in the list, since del_fast_timer may be called
|
||||
* from (another) interrupt context. Thus, the only time
|
||||
* when interrupts are enabled is when calling the timer
|
||||
* callback function.
|
||||
*/
|
||||
local_irq_save(flags);
|
||||
|
||||
/* Clear timer trig interrupt */
|
||||
intr_mask = REG_RD(timer, regi_timer0, rw_intr_mask);
|
||||
intr_mask.trig = 0;
|
||||
REG_WR(timer, regi_timer0, rw_intr_mask, intr_mask);
|
||||
|
||||
/* First stop timer, then ack interrupt */
|
||||
/* Stop timer */
|
||||
trig_cfg.tmr = regk_timer_off;
|
||||
REG_WR(timer, regi_timer0, rw_trig_cfg, trig_cfg);
|
||||
|
||||
/* Ack interrupt */
|
||||
ack_intr.trig = 1;
|
||||
REG_WR(timer, regi_timer0, rw_ack_intr, ack_intr);
|
||||
|
||||
fast_timer_running = 0;
|
||||
fast_timer_ints++;
|
||||
|
||||
fast_timer_function_type *f;
|
||||
unsigned long d;
|
||||
|
||||
t = fast_timer_list;
|
||||
while (t) {
|
||||
struct fasttime_t tv;
|
||||
|
||||
/* Has it really expired? */
|
||||
do_gettimeofday_fast(&tv);
|
||||
D1(printk(KERN_DEBUG
|
||||
"t: %is %06ius\n", tv.tv_jiff, tv.tv_usec));
|
||||
|
||||
if (fasttime_cmp(&t->tv_expires, &tv) <= 0) {
|
||||
/* Yes it has expired */
|
||||
#ifdef FAST_TIMER_LOG
|
||||
timer_expired_log[fast_timers_expired % NUM_TIMER_STATS] = *t;
|
||||
#endif
|
||||
fast_timers_expired++;
|
||||
|
||||
/* Remove this timer before call, since it may reuse the timer */
|
||||
if (t->prev)
|
||||
t->prev->next = t->next;
|
||||
else
|
||||
fast_timer_list = t->next;
|
||||
if (t->next)
|
||||
t->next->prev = t->prev;
|
||||
t->prev = NULL;
|
||||
t->next = NULL;
|
||||
|
||||
/* Save function callback data before enabling
|
||||
* interrupts, since the timer may be removed and we
|
||||
* don't know how it was allocated (e.g. ->function
|
||||
* and ->data may become overwritten after deletion
|
||||
* if the timer was stack-allocated).
|
||||
*/
|
||||
f = t->function;
|
||||
d = t->data;
|
||||
|
||||
if (f != NULL) {
|
||||
/* Run the callback function with interrupts
|
||||
* enabled. */
|
||||
local_irq_restore(flags);
|
||||
f(d);
|
||||
local_irq_save(flags);
|
||||
} else
|
||||
DEBUG_LOG("!trimertrig %i function==NULL!\n", fast_timer_ints);
|
||||
} else {
|
||||
/* Timer is to early, let's set it again using the normal routines */
|
||||
D1(printk(".\n"));
|
||||
}
|
||||
|
||||
t = fast_timer_list;
|
||||
if (t != NULL) {
|
||||
/* Start next timer.. */
|
||||
long us = 0;
|
||||
struct fasttime_t tv;
|
||||
|
||||
do_gettimeofday_fast(&tv);
|
||||
|
||||
/* time_after_eq takes care of wrapping */
|
||||
if (time_after_eq(t->tv_expires.tv_jiff, tv.tv_jiff))
|
||||
us = ((t->tv_expires.tv_jiff - tv.tv_jiff) *
|
||||
1000000 / HZ + t->tv_expires.tv_usec -
|
||||
tv.tv_usec);
|
||||
|
||||
if (us > 0) {
|
||||
if (!fast_timer_running) {
|
||||
#ifdef FAST_TIMER_LOG
|
||||
timer_started_log[fast_timers_started % NUM_TIMER_STATS] = *t;
|
||||
#endif
|
||||
start_timer_trig(us);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
/* Timer already expired, let's handle it better late than never.
|
||||
* The normal loop handles it
|
||||
*/
|
||||
D1(printk("e! %d\n", us));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
||||
if (!t)
|
||||
D1(printk("ttrig stop!\n"));
|
||||
}
|
||||
|
||||
static void wake_up_func(unsigned long data)
|
||||
{
|
||||
wait_queue_head_t *sleep_wait_p = (wait_queue_head_t*)data;
|
||||
wake_up(sleep_wait_p);
|
||||
}
|
||||
|
||||
|
||||
/* Useful API */
|
||||
|
||||
void schedule_usleep(unsigned long us)
|
||||
{
|
||||
struct fast_timer t;
|
||||
wait_queue_head_t sleep_wait;
|
||||
init_waitqueue_head(&sleep_wait);
|
||||
|
||||
D1(printk("schedule_usleep(%d)\n", us));
|
||||
start_one_shot_timer(&t, wake_up_func, (unsigned long)&sleep_wait, us,
|
||||
"usleep");
|
||||
/* Uninterruptible sleep on the fast timer. (The condition is
|
||||
* somewhat redundant since the timer is what wakes us up.) */
|
||||
wait_event(sleep_wait, !fast_timer_pending(&t));
|
||||
|
||||
D1(printk("done schedule_usleep(%d)\n", us));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
/* This value is very much based on testing */
|
||||
#define BIG_BUF_SIZE (500 + NUM_TIMER_STATS * 300)
|
||||
|
||||
static int proc_fasttimer_show(struct seq_file *m, void *v)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i = 0;
|
||||
int num_to_show;
|
||||
struct fasttime_t tv;
|
||||
struct fast_timer *t, *nextt;
|
||||
|
||||
do_gettimeofday_fast(&tv);
|
||||
|
||||
seq_printf(m, "Fast timers added: %i\n", fast_timers_added);
|
||||
seq_printf(m, "Fast timers started: %i\n", fast_timers_started);
|
||||
seq_printf(m, "Fast timer interrupts: %i\n", fast_timer_ints);
|
||||
seq_printf(m, "Fast timers expired: %i\n", fast_timers_expired);
|
||||
seq_printf(m, "Fast timers deleted: %i\n", fast_timers_deleted);
|
||||
seq_printf(m, "Fast timer running: %s\n",
|
||||
fast_timer_running ? "yes" : "no");
|
||||
seq_printf(m, "Current time: %lu.%06lu\n",
|
||||
(unsigned long)tv.tv_jiff,
|
||||
(unsigned long)tv.tv_usec);
|
||||
#ifdef FAST_TIMER_SANITY_CHECKS
|
||||
seq_printf(m, "Sanity failed: %i\n", sanity_failed);
|
||||
#endif
|
||||
seq_putc(m, '\n');
|
||||
|
||||
#ifdef DEBUG_LOG_INCLUDED
|
||||
{
|
||||
int end_i = debug_log_cnt;
|
||||
i = 0;
|
||||
|
||||
if (debug_log_cnt_wrapped)
|
||||
i = debug_log_cnt;
|
||||
|
||||
while ((i != end_i || debug_log_cnt_wrapped)) {
|
||||
if (seq_printf(m, debug_log_string[i], debug_log_value[i]) < 0)
|
||||
return 0;
|
||||
i = (i+1) % DEBUG_LOG_MAX;
|
||||
}
|
||||
}
|
||||
seq_putc(m, '\n');
|
||||
#endif
|
||||
|
||||
num_to_show = (fast_timers_started < NUM_TIMER_STATS ? fast_timers_started:
|
||||
NUM_TIMER_STATS);
|
||||
seq_printf(m, "Timers started: %i\n", fast_timers_started);
|
||||
for (i = 0; i < num_to_show; i++) {
|
||||
int cur = (fast_timers_started - i - 1) % NUM_TIMER_STATS;
|
||||
|
||||
#if 1 //ndef FAST_TIMER_LOG
|
||||
seq_printf(m, "div: %i delay: %i"
|
||||
"\n",
|
||||
timer_div_settings[cur],
|
||||
timer_delay_settings[cur]);
|
||||
#endif
|
||||
#ifdef FAST_TIMER_LOG
|
||||
t = &timer_started_log[cur];
|
||||
if (seq_printf(m, "%-14s s: %6lu.%06lu e: %6lu.%06lu "
|
||||
"d: %6li us data: 0x%08lX"
|
||||
"\n",
|
||||
t->name,
|
||||
(unsigned long)t->tv_set.tv_jiff,
|
||||
(unsigned long)t->tv_set.tv_usec,
|
||||
(unsigned long)t->tv_expires.tv_jiff,
|
||||
(unsigned long)t->tv_expires.tv_usec,
|
||||
t->delay_us,
|
||||
t->data) < 0)
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
seq_putc(m, '\n');
|
||||
|
||||
#ifdef FAST_TIMER_LOG
|
||||
num_to_show = (fast_timers_added < NUM_TIMER_STATS ? fast_timers_added:
|
||||
NUM_TIMER_STATS);
|
||||
seq_printf(m, "Timers added: %i\n", fast_timers_added);
|
||||
for (i = 0; i < num_to_show; i++) {
|
||||
t = &timer_added_log[(fast_timers_added - i - 1) % NUM_TIMER_STATS];
|
||||
if (seq_printf(m, "%-14s s: %6lu.%06lu e: %6lu.%06lu "
|
||||
"d: %6li us data: 0x%08lX"
|
||||
"\n",
|
||||
t->name,
|
||||
(unsigned long)t->tv_set.tv_jiff,
|
||||
(unsigned long)t->tv_set.tv_usec,
|
||||
(unsigned long)t->tv_expires.tv_jiff,
|
||||
(unsigned long)t->tv_expires.tv_usec,
|
||||
t->delay_us,
|
||||
t->data) < 0)
|
||||
return 0;
|
||||
}
|
||||
seq_putc(m, '\n');
|
||||
|
||||
num_to_show = (fast_timers_expired < NUM_TIMER_STATS ? fast_timers_expired:
|
||||
NUM_TIMER_STATS);
|
||||
seq_printf(m, "Timers expired: %i\n", fast_timers_expired);
|
||||
for (i = 0; i < num_to_show; i++){
|
||||
t = &timer_expired_log[(fast_timers_expired - i - 1) % NUM_TIMER_STATS];
|
||||
if (seq_printf(m, "%-14s s: %6lu.%06lu e: %6lu.%06lu "
|
||||
"d: %6li us data: 0x%08lX"
|
||||
"\n",
|
||||
t->name,
|
||||
(unsigned long)t->tv_set.tv_jiff,
|
||||
(unsigned long)t->tv_set.tv_usec,
|
||||
(unsigned long)t->tv_expires.tv_jiff,
|
||||
(unsigned long)t->tv_expires.tv_usec,
|
||||
t->delay_us,
|
||||
t->data) < 0)
|
||||
return 0;
|
||||
}
|
||||
seq_putc(m, '\n');
|
||||
#endif
|
||||
|
||||
seq_puts(m, "Active timers:\n");
|
||||
local_irq_save(flags);
|
||||
t = fast_timer_list;
|
||||
while (t != NULL){
|
||||
nextt = t->next;
|
||||
local_irq_restore(flags);
|
||||
if (seq_printf(m, "%-14s s: %6lu.%06lu e: %6lu.%06lu "
|
||||
"d: %6li us data: 0x%08lX"
|
||||
/* " func: 0x%08lX" */
|
||||
"\n",
|
||||
t->name,
|
||||
(unsigned long)t->tv_set.tv_jiff,
|
||||
(unsigned long)t->tv_set.tv_usec,
|
||||
(unsigned long)t->tv_expires.tv_jiff,
|
||||
(unsigned long)t->tv_expires.tv_usec,
|
||||
t->delay_us,
|
||||
t->data
|
||||
/* , t->function */
|
||||
) < 0)
|
||||
return 0;
|
||||
local_irq_save(flags);
|
||||
if (t->next != nextt)
|
||||
printk("timer removed!\n");
|
||||
t = nextt;
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_fasttimer_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open_size(file, proc_fasttimer_show, PDE_DATA(inode), BIG_BUF_SIZE);
|
||||
}
|
||||
|
||||
static const struct file_operations proc_fasttimer_fops = {
|
||||
.open = proc_fasttimer_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
#endif /* PROC_FS */
|
||||
|
||||
#ifdef FAST_TIMER_TEST
|
||||
static volatile unsigned long i = 0;
|
||||
static volatile int num_test_timeout = 0;
|
||||
static struct fast_timer tr[10];
|
||||
static int exp_num[10];
|
||||
|
||||
static struct fasttime_t tv_exp[100];
|
||||
|
||||
static void test_timeout(unsigned long data)
|
||||
{
|
||||
do_gettimeofday_fast(&tv_exp[data]);
|
||||
exp_num[data] = num_test_timeout;
|
||||
|
||||
num_test_timeout++;
|
||||
}
|
||||
|
||||
static void test_timeout1(unsigned long data)
|
||||
{
|
||||
do_gettimeofday_fast(&tv_exp[data]);
|
||||
exp_num[data] = num_test_timeout;
|
||||
if (data < 7)
|
||||
{
|
||||
start_one_shot_timer(&tr[i], test_timeout1, i, 1000, "timeout1");
|
||||
i++;
|
||||
}
|
||||
num_test_timeout++;
|
||||
}
|
||||
|
||||
DP(
|
||||
static char buf0[2000];
|
||||
static char buf1[2000];
|
||||
static char buf2[2000];
|
||||
static char buf3[2000];
|
||||
static char buf4[2000];
|
||||
);
|
||||
|
||||
static char buf5[6000];
|
||||
static int j_u[1000];
|
||||
|
||||
static void fast_timer_test(void)
|
||||
{
|
||||
int prev_num;
|
||||
int j;
|
||||
|
||||
struct fasttime_t tv, tv0, tv1, tv2;
|
||||
|
||||
printk("fast_timer_test() start\n");
|
||||
do_gettimeofday_fast(&tv);
|
||||
|
||||
for (j = 0; j < 1000; j++)
|
||||
{
|
||||
j_u[j] = GET_JIFFIES_USEC();
|
||||
}
|
||||
for (j = 0; j < 100; j++)
|
||||
{
|
||||
do_gettimeofday_fast(&tv_exp[j]);
|
||||
}
|
||||
printk(KERN_DEBUG "fast_timer_test() %is %06i\n", tv.tv_jiff, tv.tv_usec);
|
||||
|
||||
for (j = 0; j < 1000; j++)
|
||||
{
|
||||
printk(KERN_DEBUG "%i %i %i %i %i\n",
|
||||
j_u[j], j_u[j+1], j_u[j+2], j_u[j+3], j_u[j+4]);
|
||||
j += 4;
|
||||
}
|
||||
for (j = 0; j < 100; j++)
|
||||
{
|
||||
printk(KERN_DEBUG "%i.%i %i.%i %i.%i %i.%i %i.%i\n",
|
||||
tv_exp[j].tv_jiff, tv_exp[j].tv_usec,
|
||||
tv_exp[j+1].tv_jiff, tv_exp[j+1].tv_usec,
|
||||
tv_exp[j+2].tv_jiff, tv_exp[j+2].tv_usec,
|
||||
tv_exp[j+3].tv_jiff, tv_exp[j+3].tv_usec,
|
||||
tv_exp[j+4].tv_jiff, tv_exp[j+4].tv_usec);
|
||||
j += 4;
|
||||
}
|
||||
do_gettimeofday_fast(&tv0);
|
||||
start_one_shot_timer(&tr[i], test_timeout, i, 50000, "test0");
|
||||
DP(proc_fasttimer_read(buf0, NULL, 0, 0, 0));
|
||||
i++;
|
||||
start_one_shot_timer(&tr[i], test_timeout, i, 70000, "test1");
|
||||
DP(proc_fasttimer_read(buf1, NULL, 0, 0, 0));
|
||||
i++;
|
||||
start_one_shot_timer(&tr[i], test_timeout, i, 40000, "test2");
|
||||
DP(proc_fasttimer_read(buf2, NULL, 0, 0, 0));
|
||||
i++;
|
||||
start_one_shot_timer(&tr[i], test_timeout, i, 60000, "test3");
|
||||
DP(proc_fasttimer_read(buf3, NULL, 0, 0, 0));
|
||||
i++;
|
||||
start_one_shot_timer(&tr[i], test_timeout1, i, 55000, "test4xx");
|
||||
DP(proc_fasttimer_read(buf4, NULL, 0, 0, 0));
|
||||
i++;
|
||||
do_gettimeofday_fast(&tv1);
|
||||
|
||||
proc_fasttimer_read(buf5, NULL, 0, 0, 0);
|
||||
|
||||
prev_num = num_test_timeout;
|
||||
while (num_test_timeout < i)
|
||||
{
|
||||
if (num_test_timeout != prev_num)
|
||||
prev_num = num_test_timeout;
|
||||
}
|
||||
do_gettimeofday_fast(&tv2);
|
||||
printk(KERN_INFO "Timers started %is %06i\n",
|
||||
tv0.tv_jiff, tv0.tv_usec);
|
||||
printk(KERN_INFO "Timers started at %is %06i\n",
|
||||
tv1.tv_jiff, tv1.tv_usec);
|
||||
printk(KERN_INFO "Timers done %is %06i\n",
|
||||
tv2.tv_jiff, tv2.tv_usec);
|
||||
DP(printk("buf0:\n");
|
||||
printk(buf0);
|
||||
printk("buf1:\n");
|
||||
printk(buf1);
|
||||
printk("buf2:\n");
|
||||
printk(buf2);
|
||||
printk("buf3:\n");
|
||||
printk(buf3);
|
||||
printk("buf4:\n");
|
||||
printk(buf4);
|
||||
);
|
||||
printk("buf5:\n");
|
||||
printk(buf5);
|
||||
|
||||
printk("timers set:\n");
|
||||
for(j = 0; j<i; j++)
|
||||
{
|
||||
struct fast_timer *t = &tr[j];
|
||||
printk("%-10s set: %6is %06ius exp: %6is %06ius "
|
||||
"data: 0x%08X func: 0x%08X\n",
|
||||
t->name,
|
||||
t->tv_set.tv_jiff,
|
||||
t->tv_set.tv_usec,
|
||||
t->tv_expires.tv_jiff,
|
||||
t->tv_expires.tv_usec,
|
||||
t->data,
|
||||
t->function
|
||||
);
|
||||
|
||||
printk(" del: %6ius did exp: %6is %06ius as #%i error: %6li\n",
|
||||
t->delay_us,
|
||||
tv_exp[j].tv_jiff,
|
||||
tv_exp[j].tv_usec,
|
||||
exp_num[j],
|
||||
(tv_exp[j].tv_jiff - t->tv_expires.tv_jiff) *
|
||||
1000000 + tv_exp[j].tv_usec -
|
||||
t->tv_expires.tv_usec);
|
||||
}
|
||||
proc_fasttimer_read(buf5, NULL, 0, 0, 0);
|
||||
printk("buf5 after all done:\n");
|
||||
printk(buf5);
|
||||
printk("fast_timer_test() done\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int fast_timer_init(void)
|
||||
{
|
||||
/* For some reason, request_irq() hangs when called froom time_init() */
|
||||
if (!fast_timer_is_init)
|
||||
{
|
||||
printk("fast_timer_init()\n");
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
proc_create("fasttimer", 0, NULL, &proc_fasttimer_fops);
|
||||
#endif /* PROC_FS */
|
||||
if (request_irq(TIMER0_INTR_VECT, timer_trig_interrupt,
|
||||
IRQF_SHARED,
|
||||
"fast timer int", &fast_timer_list))
|
||||
printk(KERN_ERR "err: fasttimer irq\n");
|
||||
fast_timer_is_init = 1;
|
||||
#ifdef FAST_TIMER_TEST
|
||||
printk("do test\n");
|
||||
fast_timer_test();
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
__initcall(fast_timer_init);
|
472
arch/cris/arch-v32/kernel/head.S
Normal file
472
arch/cris/arch-v32/kernel/head.S
Normal file
|
@ -0,0 +1,472 @@
|
|||
/*
|
||||
* CRISv32 kernel startup code.
|
||||
*
|
||||
* Copyright (C) 2003, Axis Communications AB
|
||||
*/
|
||||
|
||||
#define ASSEMBLER_MACROS_ONLY
|
||||
|
||||
/*
|
||||
* The macros found in mmu_defs_asm.h uses the ## concatenation operator, so
|
||||
* -traditional must not be used when assembling this file.
|
||||
*/
|
||||
#include <arch/memmap.h>
|
||||
#include <hwregs/reg_rdwr.h>
|
||||
#include <hwregs/intr_vect.h>
|
||||
#include <hwregs/asm/mmu_defs_asm.h>
|
||||
#include <hwregs/asm/reg_map_asm.h>
|
||||
#include <mach/startup.inc>
|
||||
|
||||
#define CRAMFS_MAGIC 0x28cd3d45
|
||||
#define JHEAD_MAGIC 0x1FF528A6
|
||||
#define JHEAD_SIZE 8
|
||||
#define RAM_INIT_MAGIC 0x56902387
|
||||
#define COMMAND_LINE_MAGIC 0x87109563
|
||||
#define NAND_BOOT_MAGIC 0x9a9db001
|
||||
|
||||
;; NOTE: R8 and R9 carry information from the decompressor (if the
|
||||
;; kernel was compressed). They must not be used in the code below
|
||||
;; until they are read!
|
||||
|
||||
;; Exported symbols.
|
||||
.global etrax_irv
|
||||
.global romfs_start
|
||||
.global romfs_length
|
||||
.global romfs_in_flash
|
||||
.global nand_boot
|
||||
.global swapper_pg_dir
|
||||
|
||||
.text
|
||||
tstart:
|
||||
;; This is the entry point of the kernel. The CPU is currently in
|
||||
;; supervisor mode.
|
||||
;;
|
||||
;; 0x00000000 if flash.
|
||||
;; 0x40004000 if DRAM.
|
||||
;;
|
||||
di
|
||||
|
||||
START_CLOCKS
|
||||
|
||||
SETUP_WAIT_STATES
|
||||
|
||||
GIO_INIT
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
secondary_cpu_entry: /* Entry point for secondary CPUs */
|
||||
di
|
||||
#endif
|
||||
|
||||
;; Setup and enable the MMU. Use same configuration for both the data
|
||||
;; and the instruction MMU.
|
||||
;;
|
||||
;; Note; 3 cycles is needed for a bank-select to take effect. Further;
|
||||
;; bank 1 is the instruction MMU, bank 2 is the data MMU.
|
||||
|
||||
#ifdef CONFIG_CRIS_MACH_ARTPEC3
|
||||
move.d REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 8) \
|
||||
| REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 4) \
|
||||
| REG_FIELD(mmu, rw_mm_kbase_hi, base_d, 5) \
|
||||
| REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb), $r0
|
||||
#else
|
||||
move.d REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 8) \
|
||||
| REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 4) \
|
||||
| REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb), $r0
|
||||
#endif
|
||||
|
||||
;; Temporary map of 0x40 -> 0x40 and 0x00 -> 0x00.
|
||||
move.d REG_FIELD(mmu, rw_mm_kbase_lo, base_4, 4) \
|
||||
| REG_FIELD(mmu, rw_mm_kbase_lo, base_0, 0), $r1
|
||||
|
||||
;; Enable certain page protections and setup linear mapping
|
||||
;; for f,e,c,b,4,0.
|
||||
|
||||
;; ARTPEC-3:
|
||||
;; c,d used for linear kernel mapping, up to 512 MB
|
||||
;; e used for vmalloc
|
||||
;; f unused, but page mapped to get page faults
|
||||
|
||||
;; ETRAX FS:
|
||||
;; c used for linear kernel mapping, up to 256 MB
|
||||
;; d used for vmalloc
|
||||
;; e,f used for memory-mapped NOR flash
|
||||
|
||||
#ifdef CONFIG_CRIS_MACH_ARTPEC3
|
||||
move.d REG_STATE(mmu, rw_mm_cfg, we, on) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, acc, on) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, ex, on) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, inv, on) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_f, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_e, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_d, linear) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_c, linear) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_b, linear) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_a, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_9, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_8, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_7, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_6, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_5, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_4, linear) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_3, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_2, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_1, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_0, linear), $r2
|
||||
#else
|
||||
move.d REG_STATE(mmu, rw_mm_cfg, we, on) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, acc, on) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, ex, on) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, inv, on) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_f, linear) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_e, linear) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_d, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_c, linear) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_b, linear) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_a, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_9, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_8, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_7, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_6, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_5, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_4, linear) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_3, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_2, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_1, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_0, linear), $r2
|
||||
#endif
|
||||
|
||||
;; Update instruction MMU.
|
||||
move 1, $srs
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
move $r0, $s2 ; kbase_hi.
|
||||
move $r1, $s1 ; kbase_lo.
|
||||
move $r2, $s0 ; mm_cfg, virtual memory configuration.
|
||||
|
||||
;; Update data MMU.
|
||||
move 2, $srs
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
move $r0, $s2 ; kbase_hi.
|
||||
move $r1, $s1 ; kbase_lo
|
||||
move $r2, $s0 ; mm_cfg, virtual memory configuration.
|
||||
|
||||
;; Enable data and instruction MMU.
|
||||
move 0, $srs
|
||||
moveq 0xf, $r0 ; IMMU, DMMU, DCache, Icache on
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
move $r0, $s0
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
;; Read CPU ID
|
||||
move 0, $srs
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
move $s12, $r0
|
||||
cmpq 0, $r0
|
||||
beq master_cpu
|
||||
nop
|
||||
slave_cpu:
|
||||
; Time to boot-up. Get stack location provided by master CPU.
|
||||
move.d smp_init_current_idle_thread, $r1
|
||||
move.d [$r1], $sp
|
||||
add.d 8192, $sp
|
||||
move.d ebp_start, $r0 ; Defined in linker-script.
|
||||
move $r0, $ebp
|
||||
jsr smp_callin
|
||||
nop
|
||||
master_cpu:
|
||||
/* Set up entry point for secondary CPUs. The boot ROM has set up
|
||||
* EBP at start of internal memory. The CPU will get there
|
||||
* later when we issue an IPI to them... */
|
||||
move.d MEM_INTMEM_START + IPI_INTR_VECT * 4, $r0
|
||||
move.d secondary_cpu_entry, $r1
|
||||
move.d $r1, [$r0]
|
||||
#endif
|
||||
; Check if starting from DRAM (network->RAM boot or unpacked
|
||||
; compressed kernel), or directly from flash.
|
||||
lapcq ., $r0
|
||||
and.d 0x7fffffff, $r0 ; Mask off the non-cache bit.
|
||||
cmp.d 0x10000, $r0 ; Arbitrary, something above this code.
|
||||
blo _inflash0
|
||||
nop
|
||||
|
||||
jump _inram ; Jump to cached RAM.
|
||||
nop
|
||||
|
||||
;; Jumpgate.
|
||||
_inflash0:
|
||||
jump _inflash
|
||||
nop
|
||||
|
||||
;; Put the following in a section so that storage for it can be
|
||||
;; reclaimed after init is finished.
|
||||
.section ".init.text", "ax"
|
||||
|
||||
_inflash:
|
||||
|
||||
;; Initialize DRAM.
|
||||
cmp.d RAM_INIT_MAGIC, $r8 ; Already initialized?
|
||||
beq _dram_initialized
|
||||
nop
|
||||
|
||||
#if defined CONFIG_ETRAXFS
|
||||
#include "../mach-fs/dram_init.S"
|
||||
#elif defined CONFIG_CRIS_MACH_ARTPEC3
|
||||
#include "../mach-a3/dram_init.S"
|
||||
#else
|
||||
#error Only ETRAXFS and ARTPEC-3 supported!
|
||||
#endif
|
||||
|
||||
|
||||
_dram_initialized:
|
||||
;; Copy the text and data section to DRAM. This depends on that the
|
||||
;; variables used below are correctly set up by the linker script.
|
||||
;; The calculated value stored in R4 is used below.
|
||||
;; Leave the cramfs file system (piggybacked after the kernel) in flash.
|
||||
moveq 0, $r0 ; Source.
|
||||
move.d text_start, $r1 ; Destination.
|
||||
move.d __vmlinux_end, $r2
|
||||
move.d $r2, $r4
|
||||
sub.d $r1, $r4
|
||||
1: move.w [$r0+], $r3
|
||||
move.w $r3, [$r1+]
|
||||
cmp.d $r2, $r1
|
||||
blo 1b
|
||||
nop
|
||||
|
||||
;; Check for cramfs.
|
||||
moveq 0, $r0
|
||||
move.d romfs_length, $r1
|
||||
move.d $r0, [$r1]
|
||||
move.d [$r4], $r0 ; cramfs_super.magic
|
||||
cmp.d CRAMFS_MAGIC, $r0
|
||||
bne 1f
|
||||
nop
|
||||
|
||||
;; Set length and start of cramfs, set romfs_in_flash flag
|
||||
addoq +4, $r4, $acr
|
||||
move.d [$acr], $r0
|
||||
move.d romfs_length, $r1
|
||||
move.d $r0, [$r1]
|
||||
add.d 0xf0000000, $r4 ; Add cached flash start in virtual memory.
|
||||
move.d romfs_start, $r1
|
||||
move.d $r4, [$r1]
|
||||
1: moveq 1, $r0
|
||||
move.d romfs_in_flash, $r1
|
||||
move.d $r0, [$r1]
|
||||
|
||||
jump _start_it ; Jump to cached code.
|
||||
nop
|
||||
|
||||
_inram:
|
||||
;; Check if booting from NAND flash; if so, set appropriate flags
|
||||
;; and move on.
|
||||
cmp.d NAND_BOOT_MAGIC, $r12
|
||||
bne move_cramfs ; not nand, jump
|
||||
moveq 1, $r0
|
||||
move.d nand_boot, $r1 ; tell axisflashmap we're booting from NAND
|
||||
move.d $r0, [$r1]
|
||||
moveq 0, $r0 ; tell axisflashmap romfs is not in
|
||||
move.d romfs_in_flash, $r1 ; (directly accessed) flash
|
||||
move.d $r0, [$r1]
|
||||
jump _start_it ; continue with boot
|
||||
nop
|
||||
|
||||
move_cramfs:
|
||||
;; kernel is in DRAM.
|
||||
;; Must figure out if there is a piggybacked rootfs image or not.
|
||||
;; Set romfs_length to 0 => no rootfs image available by default.
|
||||
moveq 0, $r0
|
||||
move.d romfs_length, $r1
|
||||
move.d $r0, [$r1]
|
||||
|
||||
;; The kernel could have been unpacked to DRAM by the loader, but
|
||||
;; the cramfs image could still be in the flash immediately
|
||||
;; following the compressed kernel image. The loader passes the address
|
||||
;; of the byte succeeding the last compressed byte in the flash in
|
||||
;; register R9 when starting the kernel.
|
||||
cmp.d 0x0ffffff8, $r9
|
||||
bhs _no_romfs_in_flash ; R9 points outside the flash area.
|
||||
nop
|
||||
;; cramfs rootfs might to be in flash. Check for it.
|
||||
move.d [$r9], $r0 ; cramfs_super.magic
|
||||
cmp.d CRAMFS_MAGIC, $r0
|
||||
bne _no_romfs_in_flash
|
||||
nop
|
||||
|
||||
;; found cramfs in flash. set address and size, and romfs_in_flash flag.
|
||||
addoq +4, $r9, $acr
|
||||
move.d [$acr], $r0
|
||||
move.d romfs_length, $r1
|
||||
move.d $r0, [$r1]
|
||||
add.d 0xf0000000, $r9 ; Add cached flash start in virtual memory.
|
||||
move.d romfs_start, $r1
|
||||
move.d $r9, [$r1]
|
||||
moveq 1, $r0
|
||||
move.d romfs_in_flash, $r1
|
||||
move.d $r0, [$r1]
|
||||
|
||||
jump _start_it ; Jump to cached code.
|
||||
nop
|
||||
|
||||
_no_romfs_in_flash:
|
||||
;; No romfs in flash, so look for cramfs, or jffs2 with jhead,
|
||||
;; after kernel in RAM, as is the case with network->RAM boot.
|
||||
;; For cramfs, partition starts with magic and length.
|
||||
;; For jffs2, a jhead is prepended which contains with magic and length.
|
||||
;; The jhead is not part of the jffs2 partition however.
|
||||
#ifndef CONFIG_ETRAXFS_SIM
|
||||
move.d __bss_start, $r0
|
||||
#else
|
||||
move.d __end, $r0
|
||||
#endif
|
||||
move.d [$r0], $r1
|
||||
cmp.d CRAMFS_MAGIC, $r1 ; cramfs magic?
|
||||
beq 2f ; yes, jump
|
||||
nop
|
||||
cmp.d JHEAD_MAGIC, $r1 ; jffs2 (jhead) magic?
|
||||
bne 4f ; no, skip copy
|
||||
nop
|
||||
addq 4, $r0 ; location of jffs2 size
|
||||
move.d [$r0+], $r2 ; fetch jffs2 size -> r2
|
||||
; r0 now points to start of jffs2
|
||||
ba 3f
|
||||
nop
|
||||
2:
|
||||
addoq +4, $r0, $acr ; location of cramfs size
|
||||
move.d [$acr], $r2 ; fetch cramfs size -> r2
|
||||
; r0 still points to start of cramfs
|
||||
3:
|
||||
;; Now, move the root fs to after kernel's BSS
|
||||
|
||||
move.d _end, $r1 ; start of cramfs -> r1
|
||||
move.d romfs_start, $r3
|
||||
move.d $r1, [$r3] ; store at romfs_start (for axisflashmap)
|
||||
move.d romfs_length, $r3
|
||||
move.d $r2, [$r3] ; store size at romfs_length
|
||||
|
||||
add.d $r2, $r0 ; copy from end and downwards
|
||||
add.d $r2, $r1
|
||||
|
||||
lsrq 1, $r2 ; Size is in bytes, we copy words.
|
||||
addq 1, $r2
|
||||
1:
|
||||
move.w [$r0], $r3
|
||||
move.w $r3, [$r1]
|
||||
subq 2, $r0
|
||||
subq 2, $r1
|
||||
subq 1, $r2
|
||||
bne 1b
|
||||
nop
|
||||
|
||||
4:
|
||||
;; BSS move done.
|
||||
;; Clear romfs_in_flash flag, as we now know romfs is in DRAM
|
||||
;; Also clear nand_boot flag; if we got here, we know we've not
|
||||
;; booted from NAND flash.
|
||||
moveq 0, $r0
|
||||
move.d romfs_in_flash, $r1
|
||||
move.d $r0, [$r1]
|
||||
moveq 0, $r0
|
||||
move.d nand_boot, $r1
|
||||
move.d $r0, [$r1]
|
||||
|
||||
jump _start_it ; Jump to cached code.
|
||||
nop
|
||||
|
||||
_start_it:
|
||||
|
||||
;; Check if kernel command line is supplied
|
||||
cmp.d COMMAND_LINE_MAGIC, $r10
|
||||
bne no_command_line
|
||||
nop
|
||||
|
||||
move.d 256, $r13
|
||||
move.d cris_command_line, $r10
|
||||
or.d 0x80000000, $r11 ; Make it virtual
|
||||
1:
|
||||
move.b [$r11+], $r1
|
||||
move.b $r1, [$r10+]
|
||||
subq 1, $r13
|
||||
bne 1b
|
||||
nop
|
||||
|
||||
no_command_line:
|
||||
|
||||
;; The kernel stack contains a task structure for each task. This
|
||||
;; the initial kernel stack is in the same page as the init_task,
|
||||
;; but starts at the top of the page, i.e. + 8192 bytes.
|
||||
move.d init_thread_union + 8192, $sp
|
||||
move.d ebp_start, $r0 ; Defined in linker-script.
|
||||
move $r0, $ebp
|
||||
move.d etrax_irv, $r1 ; Set the exception base register and pointer.
|
||||
move.d $r0, [$r1]
|
||||
|
||||
;; Clear the BSS region from _bss_start to _end.
|
||||
move.d __bss_start, $r0
|
||||
move.d _end, $r1
|
||||
1: clear.d [$r0+]
|
||||
cmp.d $r1, $r0
|
||||
blo 1b
|
||||
nop
|
||||
|
||||
; Initialize registers to increase determinism
|
||||
move.d __bss_start, $r0
|
||||
movem [$r0], $r13
|
||||
|
||||
#ifdef CONFIG_ETRAX_L2CACHE
|
||||
jsr l2cache_init
|
||||
nop
|
||||
#endif
|
||||
|
||||
jump start_kernel ; Jump to start_kernel() in init/main.c.
|
||||
nop
|
||||
|
||||
.data
|
||||
etrax_irv:
|
||||
.dword 0
|
||||
|
||||
; Variables for communication with the Axis flash map driver (axisflashmap),
|
||||
; and for setting up memory in arch/cris/kernel/setup.c .
|
||||
|
||||
; romfs_start is set to the start of the root file system, if it exists
|
||||
; in directly accessible memory (i.e. NOR Flash when booting from Flash,
|
||||
; or RAM when booting directly from a network-downloaded RAM image)
|
||||
romfs_start:
|
||||
.dword 0
|
||||
|
||||
; romfs_length is set to the size of the root file system image, if it exists
|
||||
; in directly accessible memory (see romfs_start). Otherwise it is set to 0.
|
||||
romfs_length:
|
||||
.dword 0
|
||||
|
||||
; romfs_in_flash is set to 1 if the root file system resides in directly
|
||||
; accessible flash memory (i.e. NOR flash). It is set to 0 for RAM boot
|
||||
; or NAND flash boot.
|
||||
romfs_in_flash:
|
||||
.dword 0
|
||||
|
||||
; nand_boot is set to 1 when the kernel has been booted from NAND flash
|
||||
nand_boot:
|
||||
.dword 0
|
||||
|
||||
swapper_pg_dir = 0xc0002000
|
||||
|
||||
.section ".init.data", "aw"
|
||||
|
||||
#if defined CONFIG_ETRAXFS
|
||||
#include "../mach-fs/hw_settings.S"
|
||||
#elif defined CONFIG_CRIS_MACH_ARTPEC3
|
||||
#include "../mach-a3/hw_settings.S"
|
||||
#else
|
||||
#error Only ETRAXFS and ARTPEC-3 supported!
|
||||
#endif
|
501
arch/cris/arch-v32/kernel/irq.c
Normal file
501
arch/cris/arch-v32/kernel/irq.c
Normal file
|
@ -0,0 +1,501 @@
|
|||
/*
|
||||
* Copyright (C) 2003, Axis Communications AB.
|
||||
*/
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/profile.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/threads.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <hwregs/reg_map.h>
|
||||
#include <hwregs/reg_rdwr.h>
|
||||
#include <hwregs/intr_vect.h>
|
||||
#include <hwregs/intr_vect_defs.h>
|
||||
|
||||
#define CPU_FIXED -1
|
||||
|
||||
/* IRQ masks (refer to comment for crisv32_do_multiple) */
|
||||
#if TIMER0_INTR_VECT - FIRST_IRQ < 32
|
||||
#define TIMER_MASK (1 << (TIMER0_INTR_VECT - FIRST_IRQ))
|
||||
#undef TIMER_VECT1
|
||||
#else
|
||||
#define TIMER_MASK (1 << (TIMER0_INTR_VECT - FIRST_IRQ - 32))
|
||||
#define TIMER_VECT1
|
||||
#endif
|
||||
#ifdef CONFIG_ETRAX_KGDB
|
||||
#if defined(CONFIG_ETRAX_KGDB_PORT0)
|
||||
#define IGNOREMASK (1 << (SER0_INTR_VECT - FIRST_IRQ))
|
||||
#elif defined(CONFIG_ETRAX_KGDB_PORT1)
|
||||
#define IGNOREMASK (1 << (SER1_INTR_VECT - FIRST_IRQ))
|
||||
#elif defined(CONFIG_ETRAX_KGB_PORT2)
|
||||
#define IGNOREMASK (1 << (SER2_INTR_VECT - FIRST_IRQ))
|
||||
#elif defined(CONFIG_ETRAX_KGDB_PORT3)
|
||||
#define IGNOREMASK (1 << (SER3_INTR_VECT - FIRST_IRQ))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
DEFINE_SPINLOCK(irq_lock);
|
||||
|
||||
struct cris_irq_allocation
|
||||
{
|
||||
int cpu; /* The CPU to which the IRQ is currently allocated. */
|
||||
cpumask_t mask; /* The CPUs to which the IRQ may be allocated. */
|
||||
};
|
||||
|
||||
struct cris_irq_allocation irq_allocations[NR_REAL_IRQS] =
|
||||
{ [0 ... NR_REAL_IRQS - 1] = {0, CPU_MASK_ALL} };
|
||||
|
||||
static unsigned long irq_regs[NR_CPUS] =
|
||||
{
|
||||
regi_irq,
|
||||
#ifdef CONFIG_SMP
|
||||
regi_irq2,
|
||||
#endif
|
||||
};
|
||||
|
||||
#if NR_REAL_IRQS > 32
|
||||
#define NBR_REGS 2
|
||||
#else
|
||||
#define NBR_REGS 1
|
||||
#endif
|
||||
|
||||
unsigned long cpu_irq_counters[NR_CPUS];
|
||||
unsigned long irq_counters[NR_REAL_IRQS];
|
||||
|
||||
/* From irq.c. */
|
||||
extern void weird_irq(void);
|
||||
|
||||
/* From entry.S. */
|
||||
extern void system_call(void);
|
||||
extern void nmi_interrupt(void);
|
||||
extern void multiple_interrupt(void);
|
||||
extern void gdb_handle_exception(void);
|
||||
extern void i_mmu_refill(void);
|
||||
extern void i_mmu_invalid(void);
|
||||
extern void i_mmu_access(void);
|
||||
extern void i_mmu_execute(void);
|
||||
extern void d_mmu_refill(void);
|
||||
extern void d_mmu_invalid(void);
|
||||
extern void d_mmu_access(void);
|
||||
extern void d_mmu_write(void);
|
||||
|
||||
/* From kgdb.c. */
|
||||
extern void kgdb_init(void);
|
||||
extern void breakpoint(void);
|
||||
|
||||
/* From traps.c. */
|
||||
extern void breakh_BUG(void);
|
||||
|
||||
/*
|
||||
* Build the IRQ handler stubs using macros from irq.h.
|
||||
*/
|
||||
#ifdef CONFIG_CRIS_MACH_ARTPEC3
|
||||
BUILD_TIMER_IRQ(0x31, 0)
|
||||
#else
|
||||
BUILD_IRQ(0x31)
|
||||
#endif
|
||||
BUILD_IRQ(0x32)
|
||||
BUILD_IRQ(0x33)
|
||||
BUILD_IRQ(0x34)
|
||||
BUILD_IRQ(0x35)
|
||||
BUILD_IRQ(0x36)
|
||||
BUILD_IRQ(0x37)
|
||||
BUILD_IRQ(0x38)
|
||||
BUILD_IRQ(0x39)
|
||||
BUILD_IRQ(0x3a)
|
||||
BUILD_IRQ(0x3b)
|
||||
BUILD_IRQ(0x3c)
|
||||
BUILD_IRQ(0x3d)
|
||||
BUILD_IRQ(0x3e)
|
||||
BUILD_IRQ(0x3f)
|
||||
BUILD_IRQ(0x40)
|
||||
BUILD_IRQ(0x41)
|
||||
BUILD_IRQ(0x42)
|
||||
BUILD_IRQ(0x43)
|
||||
BUILD_IRQ(0x44)
|
||||
BUILD_IRQ(0x45)
|
||||
BUILD_IRQ(0x46)
|
||||
BUILD_IRQ(0x47)
|
||||
BUILD_IRQ(0x48)
|
||||
BUILD_IRQ(0x49)
|
||||
BUILD_IRQ(0x4a)
|
||||
#ifdef CONFIG_ETRAXFS
|
||||
BUILD_TIMER_IRQ(0x4b, 0)
|
||||
#else
|
||||
BUILD_IRQ(0x4b)
|
||||
#endif
|
||||
BUILD_IRQ(0x4c)
|
||||
BUILD_IRQ(0x4d)
|
||||
BUILD_IRQ(0x4e)
|
||||
BUILD_IRQ(0x4f)
|
||||
BUILD_IRQ(0x50)
|
||||
#if MACH_IRQS > 32
|
||||
BUILD_IRQ(0x51)
|
||||
BUILD_IRQ(0x52)
|
||||
BUILD_IRQ(0x53)
|
||||
BUILD_IRQ(0x54)
|
||||
BUILD_IRQ(0x55)
|
||||
BUILD_IRQ(0x56)
|
||||
BUILD_IRQ(0x57)
|
||||
BUILD_IRQ(0x58)
|
||||
BUILD_IRQ(0x59)
|
||||
BUILD_IRQ(0x5a)
|
||||
BUILD_IRQ(0x5b)
|
||||
BUILD_IRQ(0x5c)
|
||||
BUILD_IRQ(0x5d)
|
||||
BUILD_IRQ(0x5e)
|
||||
BUILD_IRQ(0x5f)
|
||||
BUILD_IRQ(0x60)
|
||||
BUILD_IRQ(0x61)
|
||||
BUILD_IRQ(0x62)
|
||||
BUILD_IRQ(0x63)
|
||||
BUILD_IRQ(0x64)
|
||||
BUILD_IRQ(0x65)
|
||||
BUILD_IRQ(0x66)
|
||||
BUILD_IRQ(0x67)
|
||||
BUILD_IRQ(0x68)
|
||||
BUILD_IRQ(0x69)
|
||||
BUILD_IRQ(0x6a)
|
||||
BUILD_IRQ(0x6b)
|
||||
BUILD_IRQ(0x6c)
|
||||
BUILD_IRQ(0x6d)
|
||||
BUILD_IRQ(0x6e)
|
||||
BUILD_IRQ(0x6f)
|
||||
BUILD_IRQ(0x70)
|
||||
#endif
|
||||
|
||||
/* Pointers to the low-level handlers. */
|
||||
static void (*interrupt[MACH_IRQS])(void) = {
|
||||
IRQ0x31_interrupt, IRQ0x32_interrupt, IRQ0x33_interrupt,
|
||||
IRQ0x34_interrupt, IRQ0x35_interrupt, IRQ0x36_interrupt,
|
||||
IRQ0x37_interrupt, IRQ0x38_interrupt, IRQ0x39_interrupt,
|
||||
IRQ0x3a_interrupt, IRQ0x3b_interrupt, IRQ0x3c_interrupt,
|
||||
IRQ0x3d_interrupt, IRQ0x3e_interrupt, IRQ0x3f_interrupt,
|
||||
IRQ0x40_interrupt, IRQ0x41_interrupt, IRQ0x42_interrupt,
|
||||
IRQ0x43_interrupt, IRQ0x44_interrupt, IRQ0x45_interrupt,
|
||||
IRQ0x46_interrupt, IRQ0x47_interrupt, IRQ0x48_interrupt,
|
||||
IRQ0x49_interrupt, IRQ0x4a_interrupt, IRQ0x4b_interrupt,
|
||||
IRQ0x4c_interrupt, IRQ0x4d_interrupt, IRQ0x4e_interrupt,
|
||||
IRQ0x4f_interrupt, IRQ0x50_interrupt,
|
||||
#if MACH_IRQS > 32
|
||||
IRQ0x51_interrupt, IRQ0x52_interrupt, IRQ0x53_interrupt,
|
||||
IRQ0x54_interrupt, IRQ0x55_interrupt, IRQ0x56_interrupt,
|
||||
IRQ0x57_interrupt, IRQ0x58_interrupt, IRQ0x59_interrupt,
|
||||
IRQ0x5a_interrupt, IRQ0x5b_interrupt, IRQ0x5c_interrupt,
|
||||
IRQ0x5d_interrupt, IRQ0x5e_interrupt, IRQ0x5f_interrupt,
|
||||
IRQ0x60_interrupt, IRQ0x61_interrupt, IRQ0x62_interrupt,
|
||||
IRQ0x63_interrupt, IRQ0x64_interrupt, IRQ0x65_interrupt,
|
||||
IRQ0x66_interrupt, IRQ0x67_interrupt, IRQ0x68_interrupt,
|
||||
IRQ0x69_interrupt, IRQ0x6a_interrupt, IRQ0x6b_interrupt,
|
||||
IRQ0x6c_interrupt, IRQ0x6d_interrupt, IRQ0x6e_interrupt,
|
||||
IRQ0x6f_interrupt, IRQ0x70_interrupt,
|
||||
#endif
|
||||
};
|
||||
|
||||
void
|
||||
block_irq(int irq, int cpu)
|
||||
{
|
||||
int intr_mask;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&irq_lock, flags);
|
||||
/* Remember, 1 let thru, 0 block. */
|
||||
if (irq - FIRST_IRQ < 32) {
|
||||
intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu],
|
||||
rw_mask, 0);
|
||||
intr_mask &= ~(1 << (irq - FIRST_IRQ));
|
||||
REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask,
|
||||
0, intr_mask);
|
||||
} else {
|
||||
intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu],
|
||||
rw_mask, 1);
|
||||
intr_mask &= ~(1 << (irq - FIRST_IRQ - 32));
|
||||
REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask,
|
||||
1, intr_mask);
|
||||
}
|
||||
spin_unlock_irqrestore(&irq_lock, flags);
|
||||
}
|
||||
|
||||
void
|
||||
unblock_irq(int irq, int cpu)
|
||||
{
|
||||
int intr_mask;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&irq_lock, flags);
|
||||
/* Remember, 1 let thru, 0 block. */
|
||||
if (irq - FIRST_IRQ < 32) {
|
||||
intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu],
|
||||
rw_mask, 0);
|
||||
intr_mask |= (1 << (irq - FIRST_IRQ));
|
||||
REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask,
|
||||
0, intr_mask);
|
||||
} else {
|
||||
intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu],
|
||||
rw_mask, 1);
|
||||
intr_mask |= (1 << (irq - FIRST_IRQ - 32));
|
||||
REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask,
|
||||
1, intr_mask);
|
||||
}
|
||||
spin_unlock_irqrestore(&irq_lock, flags);
|
||||
}
|
||||
|
||||
/* Find out which CPU the irq should be allocated to. */
|
||||
static int irq_cpu(int irq)
|
||||
{
|
||||
int cpu;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&irq_lock, flags);
|
||||
cpu = irq_allocations[irq - FIRST_IRQ].cpu;
|
||||
|
||||
/* Fixed interrupts stay on the local CPU. */
|
||||
if (cpu == CPU_FIXED)
|
||||
{
|
||||
spin_unlock_irqrestore(&irq_lock, flags);
|
||||
return smp_processor_id();
|
||||
}
|
||||
|
||||
|
||||
/* Let the interrupt stay if possible */
|
||||
if (cpumask_test_cpu(cpu, &irq_allocations[irq - FIRST_IRQ].mask))
|
||||
goto out;
|
||||
|
||||
/* IRQ must be moved to another CPU. */
|
||||
cpu = cpumask_first(&irq_allocations[irq - FIRST_IRQ].mask);
|
||||
irq_allocations[irq - FIRST_IRQ].cpu = cpu;
|
||||
out:
|
||||
spin_unlock_irqrestore(&irq_lock, flags);
|
||||
return cpu;
|
||||
}
|
||||
|
||||
void crisv32_mask_irq(int irq)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for (cpu = 0; cpu < NR_CPUS; cpu++)
|
||||
block_irq(irq, cpu);
|
||||
}
|
||||
|
||||
void crisv32_unmask_irq(int irq)
|
||||
{
|
||||
unblock_irq(irq, irq_cpu(irq));
|
||||
}
|
||||
|
||||
|
||||
static void enable_crisv32_irq(struct irq_data *data)
|
||||
{
|
||||
crisv32_unmask_irq(data->irq);
|
||||
}
|
||||
|
||||
static void disable_crisv32_irq(struct irq_data *data)
|
||||
{
|
||||
crisv32_mask_irq(data->irq);
|
||||
}
|
||||
|
||||
static int set_affinity_crisv32_irq(struct irq_data *data,
|
||||
const struct cpumask *dest, bool force)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&irq_lock, flags);
|
||||
irq_allocations[data->irq - FIRST_IRQ].mask = *dest;
|
||||
spin_unlock_irqrestore(&irq_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_chip crisv32_irq_type = {
|
||||
.name = "CRISv32",
|
||||
.irq_shutdown = disable_crisv32_irq,
|
||||
.irq_enable = enable_crisv32_irq,
|
||||
.irq_disable = disable_crisv32_irq,
|
||||
.irq_set_affinity = set_affinity_crisv32_irq,
|
||||
};
|
||||
|
||||
void
|
||||
set_exception_vector(int n, irqvectptr addr)
|
||||
{
|
||||
etrax_irv->v[n] = (irqvectptr) addr;
|
||||
}
|
||||
|
||||
extern void do_IRQ(int irq, struct pt_regs * regs);
|
||||
|
||||
void
|
||||
crisv32_do_IRQ(int irq, int block, struct pt_regs* regs)
|
||||
{
|
||||
/* Interrupts that may not be moved to another CPU may
|
||||
* skip blocking. This is currently only valid for the
|
||||
* timer IRQ and the IPI and is used for the timer
|
||||
* interrupt to avoid watchdog starvation.
|
||||
*/
|
||||
if (!block) {
|
||||
do_IRQ(irq, regs);
|
||||
return;
|
||||
}
|
||||
|
||||
block_irq(irq, smp_processor_id());
|
||||
do_IRQ(irq, regs);
|
||||
|
||||
unblock_irq(irq, irq_cpu(irq));
|
||||
}
|
||||
|
||||
/* If multiple interrupts occur simultaneously we get a multiple
|
||||
* interrupt from the CPU and software has to sort out which
|
||||
* interrupts that happened. There are two special cases here:
|
||||
*
|
||||
* 1. Timer interrupts may never be blocked because of the
|
||||
* watchdog (refer to comment in include/asr/arch/irq.h)
|
||||
* 2. GDB serial port IRQs are unhandled here and will be handled
|
||||
* as a single IRQ when it strikes again because the GDB
|
||||
* stubb wants to save the registers in its own fashion.
|
||||
*/
|
||||
void
|
||||
crisv32_do_multiple(struct pt_regs* regs)
|
||||
{
|
||||
int cpu;
|
||||
int mask;
|
||||
int masked[NBR_REGS];
|
||||
int bit;
|
||||
int i;
|
||||
|
||||
cpu = smp_processor_id();
|
||||
|
||||
/* An extra irq_enter here to prevent softIRQs to run after
|
||||
* each do_IRQ. This will decrease the interrupt latency.
|
||||
*/
|
||||
irq_enter();
|
||||
|
||||
for (i = 0; i < NBR_REGS; i++) {
|
||||
/* Get which IRQs that happened. */
|
||||
masked[i] = REG_RD_INT_VECT(intr_vect, irq_regs[cpu],
|
||||
r_masked_vect, i);
|
||||
|
||||
/* Calculate new IRQ mask with these IRQs disabled. */
|
||||
mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i);
|
||||
mask &= ~masked[i];
|
||||
|
||||
/* Timer IRQ is never masked */
|
||||
#ifdef TIMER_VECT1
|
||||
if ((i == 1) && (masked[0] & TIMER_MASK))
|
||||
mask |= TIMER_MASK;
|
||||
#else
|
||||
if ((i == 0) && (masked[0] & TIMER_MASK))
|
||||
mask |= TIMER_MASK;
|
||||
#endif
|
||||
/* Block all the IRQs */
|
||||
REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i, mask);
|
||||
|
||||
/* Check for timer IRQ and handle it special. */
|
||||
#ifdef TIMER_VECT1
|
||||
if ((i == 1) && (masked[i] & TIMER_MASK)) {
|
||||
masked[i] &= ~TIMER_MASK;
|
||||
do_IRQ(TIMER0_INTR_VECT, regs);
|
||||
}
|
||||
#else
|
||||
if ((i == 0) && (masked[i] & TIMER_MASK)) {
|
||||
masked[i] &= ~TIMER_MASK;
|
||||
do_IRQ(TIMER0_INTR_VECT, regs);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef IGNORE_MASK
|
||||
/* Remove IRQs that can't be handled as multiple. */
|
||||
masked[0] &= ~IGNORE_MASK;
|
||||
#endif
|
||||
|
||||
/* Handle the rest of the IRQs. */
|
||||
for (i = 0; i < NBR_REGS; i++) {
|
||||
for (bit = 0; bit < 32; bit++) {
|
||||
if (masked[i] & (1 << bit))
|
||||
do_IRQ(bit + FIRST_IRQ + i*32, regs);
|
||||
}
|
||||
}
|
||||
|
||||
/* Unblock all the IRQs. */
|
||||
for (i = 0; i < NBR_REGS; i++) {
|
||||
mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i);
|
||||
mask |= masked[i];
|
||||
REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i, mask);
|
||||
}
|
||||
|
||||
/* This irq_exit() will trigger the soft IRQs. */
|
||||
irq_exit();
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called by start_kernel. It fixes the IRQ masks and setup the
|
||||
* interrupt vector table to point to bad_interrupt pointers.
|
||||
*/
|
||||
void __init
|
||||
init_IRQ(void)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
reg_intr_vect_rw_mask vect_mask = {0};
|
||||
|
||||
/* Clear all interrupts masks. */
|
||||
for (i = 0; i < NBR_REGS; i++)
|
||||
REG_WR_VECT(intr_vect, regi_irq, rw_mask, i, vect_mask);
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
etrax_irv->v[i] = weird_irq;
|
||||
|
||||
/* Point all IRQ's to bad handlers. */
|
||||
for (i = FIRST_IRQ, j = 0; j < NR_IRQS; i++, j++) {
|
||||
irq_set_chip_and_handler(j, &crisv32_irq_type,
|
||||
handle_simple_irq);
|
||||
set_exception_vector(i, interrupt[j]);
|
||||
}
|
||||
|
||||
/* Mark Timer and IPI IRQs as CPU local */
|
||||
irq_allocations[TIMER0_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED;
|
||||
irq_set_status_flags(TIMER0_INTR_VECT, IRQ_PER_CPU);
|
||||
irq_allocations[IPI_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED;
|
||||
irq_set_status_flags(IPI_INTR_VECT, IRQ_PER_CPU);
|
||||
|
||||
set_exception_vector(0x00, nmi_interrupt);
|
||||
set_exception_vector(0x30, multiple_interrupt);
|
||||
|
||||
/* Set up handler for various MMU bus faults. */
|
||||
set_exception_vector(0x04, i_mmu_refill);
|
||||
set_exception_vector(0x05, i_mmu_invalid);
|
||||
set_exception_vector(0x06, i_mmu_access);
|
||||
set_exception_vector(0x07, i_mmu_execute);
|
||||
set_exception_vector(0x08, d_mmu_refill);
|
||||
set_exception_vector(0x09, d_mmu_invalid);
|
||||
set_exception_vector(0x0a, d_mmu_access);
|
||||
set_exception_vector(0x0b, d_mmu_write);
|
||||
|
||||
#ifdef CONFIG_BUG
|
||||
/* Break 14 handler, used to implement cheap BUG(). */
|
||||
set_exception_vector(0x1e, breakh_BUG);
|
||||
#endif
|
||||
|
||||
/* The system-call trap is reached by "break 13". */
|
||||
set_exception_vector(0x1d, system_call);
|
||||
|
||||
/* Exception handlers for debugging, both user-mode and kernel-mode. */
|
||||
|
||||
/* Break 8. */
|
||||
set_exception_vector(0x18, gdb_handle_exception);
|
||||
/* Hardware single step. */
|
||||
set_exception_vector(0x3, gdb_handle_exception);
|
||||
/* Hardware breakpoint. */
|
||||
set_exception_vector(0xc, gdb_handle_exception);
|
||||
|
||||
#ifdef CONFIG_ETRAX_KGDB
|
||||
kgdb_init();
|
||||
/* Everything is set up; now trap the kernel. */
|
||||
breakpoint();
|
||||
#endif
|
||||
}
|
||||
|
1612
arch/cris/arch-v32/kernel/kgdb.c
Normal file
1612
arch/cris/arch-v32/kernel/kgdb.c
Normal file
File diff suppressed because it is too large
Load diff
551
arch/cris/arch-v32/kernel/kgdb_asm.S
Normal file
551
arch/cris/arch-v32/kernel/kgdb_asm.S
Normal file
|
@ -0,0 +1,551 @@
|
|||
/*
|
||||
* Copyright (C) 2004 Axis Communications AB
|
||||
*
|
||||
* Code for handling break 8, hardware breakpoint, single step, and serial
|
||||
* port exceptions for kernel debugging purposes.
|
||||
*/
|
||||
|
||||
#include <hwregs/intr_vect.h>
|
||||
|
||||
;; Exported functions.
|
||||
.globl kgdb_handle_exception
|
||||
|
||||
kgdb_handle_exception:
|
||||
|
||||
;; Create a register image of the caller.
|
||||
;;
|
||||
;; First of all, save the ACR on the stack since we need it for address calculations.
|
||||
;; We put it into the register struct later.
|
||||
|
||||
subq 4, $sp
|
||||
move.d $acr, [$sp]
|
||||
|
||||
;; Now we are free to use ACR all we want.
|
||||
;; If we were running this handler with interrupts on, we would have to be careful
|
||||
;; to save and restore CCS manually, but since we aren't we treat it like every other
|
||||
;; register.
|
||||
|
||||
move.d reg, $acr
|
||||
move.d $r0, [$acr] ; Save R0 (start of register struct)
|
||||
addq 4, $acr
|
||||
move.d $r1, [$acr] ; Save R1
|
||||
addq 4, $acr
|
||||
move.d $r2, [$acr] ; Save R2
|
||||
addq 4, $acr
|
||||
move.d $r3, [$acr] ; Save R3
|
||||
addq 4, $acr
|
||||
move.d $r4, [$acr] ; Save R4
|
||||
addq 4, $acr
|
||||
move.d $r5, [$acr] ; Save R5
|
||||
addq 4, $acr
|
||||
move.d $r6, [$acr] ; Save R6
|
||||
addq 4, $acr
|
||||
move.d $r7, [$acr] ; Save R7
|
||||
addq 4, $acr
|
||||
move.d $r8, [$acr] ; Save R8
|
||||
addq 4, $acr
|
||||
move.d $r9, [$acr] ; Save R9
|
||||
addq 4, $acr
|
||||
move.d $r10, [$acr] ; Save R10
|
||||
addq 4, $acr
|
||||
move.d $r11, [$acr] ; Save R11
|
||||
addq 4, $acr
|
||||
move.d $r12, [$acr] ; Save R12
|
||||
addq 4, $acr
|
||||
move.d $r13, [$acr] ; Save R13
|
||||
addq 4, $acr
|
||||
move.d $sp, [$acr] ; Save SP (R14)
|
||||
addq 4, $acr
|
||||
|
||||
;; The ACR register is already saved on the stack, so pop it from there.
|
||||
move.d [$sp],$r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
|
||||
move $bz, [$acr]
|
||||
addq 1, $acr
|
||||
move $vr, [$acr]
|
||||
addq 1, $acr
|
||||
move $pid, [$acr]
|
||||
addq 4, $acr
|
||||
move $srs, [$acr]
|
||||
addq 1, $acr
|
||||
move $wz, [$acr]
|
||||
addq 2, $acr
|
||||
move $exs, [$acr]
|
||||
addq 4, $acr
|
||||
move $eda, [$acr]
|
||||
addq 4, $acr
|
||||
move $mof, [$acr]
|
||||
addq 4, $acr
|
||||
move $dz, [$acr]
|
||||
addq 4, $acr
|
||||
move $ebp, [$acr]
|
||||
addq 4, $acr
|
||||
move $erp, [$acr]
|
||||
addq 4, $acr
|
||||
move $srp, [$acr]
|
||||
addq 4, $acr
|
||||
move $nrp, [$acr]
|
||||
addq 4, $acr
|
||||
move $ccs, [$acr]
|
||||
addq 4, $acr
|
||||
move $usp, [$acr]
|
||||
addq 4, $acr
|
||||
move $spc, [$acr]
|
||||
addq 4, $acr
|
||||
|
||||
;; Skip the pseudo-PC.
|
||||
addq 4, $acr
|
||||
|
||||
;; Save the support registers in bank 0 - 3.
|
||||
clear.d $r1 ; Bank counter
|
||||
move.d sreg, $acr
|
||||
|
||||
;; Bank 0
|
||||
move $r1, $srs
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
move $s0, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s1, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s2, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s3, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s4, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s5, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s6, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s7, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s8, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s9, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s10, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s11, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s12, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
|
||||
;; Nothing in S13 - S15, bank 0
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
|
||||
;; Bank 1 and bank 2 have the same layout, hence the loop.
|
||||
addq 1, $r1
|
||||
1:
|
||||
move $r1, $srs
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
move $s0, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s1, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s2, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s3, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s4, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s5, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s6, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
|
||||
;; Nothing in S7 - S15, bank 1 and 2
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
|
||||
addq 1, $r1
|
||||
cmpq 3, $r1
|
||||
bne 1b
|
||||
nop
|
||||
|
||||
;; Bank 3
|
||||
move $r1, $srs
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
move $s0, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s1, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s2, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s3, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s4, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s5, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s6, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s7, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s8, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s9, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s10, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s11, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s12, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s13, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s14, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
;; Nothing in S15, bank 3
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
|
||||
;; Check what got us here: get IDX field of EXS.
|
||||
move $exs, $r10
|
||||
and.d 0xff00, $r10
|
||||
lsrq 8, $r10
|
||||
#if defined(CONFIG_ETRAX_KGDB_PORT0)
|
||||
cmp.d SER0_INTR_VECT, $r10 ; IRQ for serial port 0
|
||||
beq sigint
|
||||
nop
|
||||
#elif defined(CONFIG_ETRAX_KGDB_PORT1)
|
||||
cmp.d SER1_INTR_VECT, $r10 ; IRQ for serial port 1
|
||||
beq sigint
|
||||
nop
|
||||
#elif defined(CONFIG_ETRAX_KGDB_PORT2)
|
||||
cmp.d SER2_INTR_VECT, $r10 ; IRQ for serial port 2
|
||||
beq sigint
|
||||
nop
|
||||
#elif defined(CONFIG_ETRAX_KGDB_PORT3)
|
||||
cmp.d SER3_INTR_VECT, $r10 ; IRQ for serial port 3
|
||||
beq sigint
|
||||
nop
|
||||
#endif
|
||||
;; Multiple interrupt must be due to serial break.
|
||||
cmp.d 0x30, $r10 ; Multiple interrupt
|
||||
beq sigint
|
||||
nop
|
||||
;; Neither of those? Then it's a sigtrap.
|
||||
ba handle_comm
|
||||
moveq 5, $r10 ; Set SIGTRAP (delay slot)
|
||||
|
||||
sigint:
|
||||
;; Serial interrupt; get character
|
||||
jsr getDebugChar
|
||||
nop ; Delay slot
|
||||
cmp.b 3, $r10 ; \003 (Ctrl-C)?
|
||||
bne return ; No, get out of here
|
||||
nop
|
||||
moveq 2, $r10 ; Set SIGINT
|
||||
|
||||
;;
|
||||
;; Handle the communication
|
||||
;;
|
||||
handle_comm:
|
||||
move.d internal_stack+1020, $sp ; Use the internal stack which grows upwards
|
||||
jsr handle_exception ; Interactive routine
|
||||
nop
|
||||
|
||||
;;
|
||||
;; Return to the caller
|
||||
;;
|
||||
return:
|
||||
|
||||
;; First of all, write the support registers.
|
||||
clear.d $r1 ; Bank counter
|
||||
move.d sreg, $acr
|
||||
|
||||
;; Bank 0
|
||||
move $r1, $srs
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s0
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s1
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s2
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s3
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s4
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s5
|
||||
addq 4, $acr
|
||||
|
||||
;; Nothing in S6 - S7, bank 0.
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s8
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s9
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s10
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s11
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s12
|
||||
addq 4, $acr
|
||||
|
||||
;; Nothing in S13 - S15, bank 0
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
|
||||
;; Bank 1 and bank 2 have the same layout, hence the loop.
|
||||
addq 1, $r1
|
||||
2:
|
||||
move $r1, $srs
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s0
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s1
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s2
|
||||
addq 4, $acr
|
||||
|
||||
;; S3 (MM_CAUSE) is read-only.
|
||||
addq 4, $acr
|
||||
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s4
|
||||
addq 4, $acr
|
||||
|
||||
;; FIXME: Actually write S5/S6? (Affects MM_CAUSE.)
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
|
||||
;; Nothing in S7 - S15, bank 1 and 2
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
|
||||
addq 1, $r1
|
||||
cmpq 3, $r1
|
||||
bne 2b
|
||||
nop
|
||||
|
||||
;; Bank 3
|
||||
move $r1, $srs
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s0
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s1
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s2
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s3
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s4
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s5
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s6
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s7
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s8
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s9
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s10
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s11
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s12
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s13
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s14
|
||||
addq 4, $acr
|
||||
|
||||
;; Nothing in S15, bank 3
|
||||
addq 4, $acr
|
||||
|
||||
;; Now, move on to the regular register restoration process.
|
||||
|
||||
move.d reg, $acr ; Reset ACR to point at the beginning of the register image
|
||||
move.d [$acr], $r0 ; Restore R0
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r1 ; Restore R1
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r2 ; Restore R2
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r3 ; Restore R3
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r4 ; Restore R4
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r5 ; Restore R5
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r6 ; Restore R6
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r7 ; Restore R7
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r8 ; Restore R8
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r9 ; Restore R9
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r10 ; Restore R10
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r11 ; Restore R11
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r12 ; Restore R12
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r13 ; Restore R13
|
||||
|
||||
;;
|
||||
;; We restore all registers, even though some of them probably haven't changed.
|
||||
;;
|
||||
|
||||
addq 4, $acr
|
||||
move.d [$acr], $sp ; Restore SP (R14)
|
||||
|
||||
;; ACR cannot be restored just yet.
|
||||
addq 8, $acr
|
||||
|
||||
;; Skip BZ, VR.
|
||||
addq 2, $acr
|
||||
|
||||
move [$acr], $pid ; Restore PID
|
||||
addq 4, $acr
|
||||
move [$acr], $srs ; Restore SRS
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
addq 1, $acr
|
||||
|
||||
;; Skip WZ.
|
||||
addq 2, $acr
|
||||
|
||||
move [$acr], $exs ; Restore EXS.
|
||||
addq 4, $acr
|
||||
move [$acr], $eda ; Restore EDA.
|
||||
addq 4, $acr
|
||||
move [$acr], $mof ; Restore MOF.
|
||||
|
||||
;; Skip DZ.
|
||||
addq 8, $acr
|
||||
|
||||
move [$acr], $ebp ; Restore EBP.
|
||||
addq 4, $acr
|
||||
move [$acr], $erp ; Restore ERP.
|
||||
addq 4, $acr
|
||||
move [$acr], $srp ; Restore SRP.
|
||||
addq 4, $acr
|
||||
move [$acr], $nrp ; Restore NRP.
|
||||
addq 4, $acr
|
||||
move [$acr], $ccs ; Restore CCS like an ordinary register.
|
||||
addq 4, $acr
|
||||
move [$acr], $usp ; Restore USP
|
||||
addq 4, $acr
|
||||
move [$acr], $spc ; Restore SPC
|
||||
; No restoration of pseudo-PC of course.
|
||||
|
||||
move.d reg, $acr ; Reset ACR to point at the beginning of the register image
|
||||
add.d 15*4, $acr
|
||||
move.d [$acr], $acr ; Finally, restore ACR.
|
||||
rete ; Same as jump ERP
|
||||
rfe ; Shifts CCS
|
184
arch/cris/arch-v32/kernel/process.c
Normal file
184
arch/cris/arch-v32/kernel/process.c
Normal file
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* Copyright (C) 2000-2003 Axis Communications AB
|
||||
*
|
||||
* Authors: Bjorn Wesen (bjornw@axis.com)
|
||||
* Mikael Starvik (starvik@axis.com)
|
||||
* Tobias Anderberg (tobiasa@axis.com), CRISv32 port.
|
||||
*
|
||||
* This file handles the architecture-dependent parts of process handling..
|
||||
*/
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/fs.h>
|
||||
#include <hwregs/reg_rdwr.h>
|
||||
#include <hwregs/reg_map.h>
|
||||
#include <hwregs/timer_defs.h>
|
||||
#include <hwregs/intr_vect_defs.h>
|
||||
#include <linux/ptrace.h>
|
||||
|
||||
extern void stop_watchdog(void);
|
||||
|
||||
/* We use this if we don't have any better idle routine. */
|
||||
void default_idle(void)
|
||||
{
|
||||
/* Halt until exception. */
|
||||
__asm__ volatile("ei \n\t"
|
||||
"halt ");
|
||||
}
|
||||
|
||||
/*
|
||||
* Free current thread data structures etc..
|
||||
*/
|
||||
|
||||
extern void deconfigure_bp(long pid);
|
||||
void exit_thread(void)
|
||||
{
|
||||
deconfigure_bp(current->pid);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the watchdog is enabled, disable interrupts and enter an infinite loop.
|
||||
* The watchdog will reset the CPU after 0.1s. If the watchdog isn't enabled
|
||||
* then enable it and wait.
|
||||
*/
|
||||
extern void arch_enable_nmi(void);
|
||||
|
||||
void
|
||||
hard_reset_now(void)
|
||||
{
|
||||
/*
|
||||
* Don't declare this variable elsewhere. We don't want any other
|
||||
* code to know about it than the watchdog handler in entry.S and
|
||||
* this code, implementing hard reset through the watchdog.
|
||||
*/
|
||||
#if defined(CONFIG_ETRAX_WATCHDOG)
|
||||
extern int cause_of_death;
|
||||
#endif
|
||||
|
||||
printk("*** HARD RESET ***\n");
|
||||
local_irq_disable();
|
||||
|
||||
#if defined(CONFIG_ETRAX_WATCHDOG)
|
||||
cause_of_death = 0xbedead;
|
||||
#else
|
||||
{
|
||||
reg_timer_rw_wd_ctrl wd_ctrl = {0};
|
||||
|
||||
stop_watchdog();
|
||||
|
||||
wd_ctrl.key = 16; /* Arbitrary key. */
|
||||
wd_ctrl.cnt = 1; /* Minimum time. */
|
||||
wd_ctrl.cmd = regk_timer_start;
|
||||
|
||||
arch_enable_nmi();
|
||||
REG_WR(timer, regi_timer0, rw_wd_ctrl, wd_ctrl);
|
||||
}
|
||||
#endif
|
||||
|
||||
while (1)
|
||||
; /* Wait for reset. */
|
||||
}
|
||||
|
||||
/*
|
||||
* Return saved PC of a blocked thread.
|
||||
*/
|
||||
unsigned long thread_saved_pc(struct task_struct *t)
|
||||
{
|
||||
return task_pt_regs(t)->erp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup the child's kernel stack with a pt_regs and call switch_stack() on it.
|
||||
* It will be unnested during _resume and _ret_from_sys_call when the new thread
|
||||
* is scheduled.
|
||||
*
|
||||
* Also setup the thread switching structure which is used to keep
|
||||
* thread-specific data during _resumes.
|
||||
*/
|
||||
|
||||
extern asmlinkage void ret_from_fork(void);
|
||||
extern asmlinkage void ret_from_kernel_thread(void);
|
||||
|
||||
int
|
||||
copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||
unsigned long arg, struct task_struct *p)
|
||||
{
|
||||
struct pt_regs *childregs = task_pt_regs(p);
|
||||
struct switch_stack *swstack = ((struct switch_stack *) childregs) - 1;
|
||||
|
||||
/*
|
||||
* Put the pt_regs structure at the end of the new kernel stack page and
|
||||
* fix it up. Note: the task_struct doubles as the kernel stack for the
|
||||
* task.
|
||||
*/
|
||||
if (unlikely(p->flags & PF_KTHREAD)) {
|
||||
memset(swstack, 0,
|
||||
sizeof(struct switch_stack) + sizeof(struct pt_regs));
|
||||
swstack->r1 = usp;
|
||||
swstack->r2 = arg;
|
||||
childregs->ccs = 1 << (I_CCS_BITNR + CCS_SHIFT);
|
||||
swstack->return_ip = (unsigned long) ret_from_kernel_thread;
|
||||
p->thread.ksp = (unsigned long) swstack;
|
||||
p->thread.usp = 0;
|
||||
return 0;
|
||||
}
|
||||
*childregs = *current_pt_regs(); /* Struct copy of pt_regs. */
|
||||
childregs->r10 = 0; /* Child returns 0 after a fork/clone. */
|
||||
|
||||
/* Set a new TLS ?
|
||||
* The TLS is in $mof because it is the 5th argument to sys_clone.
|
||||
*/
|
||||
if (p->mm && (clone_flags & CLONE_SETTLS)) {
|
||||
task_thread_info(p)->tls = childregs->mof;
|
||||
}
|
||||
|
||||
/* Put the switch stack right below the pt_regs. */
|
||||
|
||||
/* Parameter to ret_from_sys_call. 0 is don't restart the syscall. */
|
||||
swstack->r9 = 0;
|
||||
|
||||
/*
|
||||
* We want to return into ret_from_sys_call after the _resume.
|
||||
* ret_from_fork will call ret_from_sys_call.
|
||||
*/
|
||||
swstack->return_ip = (unsigned long) ret_from_fork;
|
||||
|
||||
/* Fix the user-mode and kernel-mode stackpointer. */
|
||||
p->thread.usp = usp ?: rdusp();
|
||||
p->thread.ksp = (unsigned long) swstack;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long
|
||||
get_wchan(struct task_struct *p)
|
||||
{
|
||||
/* TODO */
|
||||
return 0;
|
||||
}
|
||||
#undef last_sched
|
||||
#undef first_sched
|
||||
|
||||
void show_regs(struct pt_regs * regs)
|
||||
{
|
||||
unsigned long usp = rdusp();
|
||||
|
||||
show_regs_print_info(KERN_DEFAULT);
|
||||
|
||||
printk("ERP: %08lx SRP: %08lx CCS: %08lx USP: %08lx MOF: %08lx\n",
|
||||
regs->erp, regs->srp, regs->ccs, usp, regs->mof);
|
||||
|
||||
printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n",
|
||||
regs->r0, regs->r1, regs->r2, regs->r3);
|
||||
|
||||
printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n",
|
||||
regs->r4, regs->r5, regs->r6, regs->r7);
|
||||
|
||||
printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n",
|
||||
regs->r8, regs->r9, regs->r10, regs->r11);
|
||||
|
||||
printk("r12: %08lx r13: %08lx oR10: %08lx\n",
|
||||
regs->r12, regs->r13, regs->orig_r10);
|
||||
}
|
490
arch/cris/arch-v32/kernel/ptrace.c
Normal file
490
arch/cris/arch-v32/kernel/ptrace.c
Normal file
|
@ -0,0 +1,490 @@
|
|||
/*
|
||||
* Copyright (C) 2000-2007, Axis Communications AB.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/user.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/security.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/processor.h>
|
||||
#include <arch/hwregs/supp_reg.h>
|
||||
|
||||
/*
|
||||
* Determines which bits in CCS the user has access to.
|
||||
* 1 = access, 0 = no access.
|
||||
*/
|
||||
#define CCS_MASK 0x00087c00 /* SXNZVC */
|
||||
|
||||
#define SBIT_USER (1 << (S_CCS_BITNR + CCS_SHIFT))
|
||||
|
||||
static int put_debugreg(long pid, unsigned int regno, long data);
|
||||
static long get_debugreg(long pid, unsigned int regno);
|
||||
static unsigned long get_pseudo_pc(struct task_struct *child);
|
||||
void deconfigure_bp(long pid);
|
||||
|
||||
extern unsigned long cris_signal_return_page;
|
||||
|
||||
/*
|
||||
* Get contents of register REGNO in task TASK.
|
||||
*/
|
||||
long get_reg(struct task_struct *task, unsigned int regno)
|
||||
{
|
||||
/* USP is a special case, it's not in the pt_regs struct but
|
||||
* in the tasks thread struct
|
||||
*/
|
||||
unsigned long ret;
|
||||
|
||||
if (regno <= PT_EDA)
|
||||
ret = ((unsigned long *)task_pt_regs(task))[regno];
|
||||
else if (regno == PT_USP)
|
||||
ret = task->thread.usp;
|
||||
else if (regno == PT_PPC)
|
||||
ret = get_pseudo_pc(task);
|
||||
else if (regno <= PT_MAX)
|
||||
ret = get_debugreg(task->pid, regno);
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write contents of register REGNO in task TASK.
|
||||
*/
|
||||
int put_reg(struct task_struct *task, unsigned int regno, unsigned long data)
|
||||
{
|
||||
if (regno <= PT_EDA)
|
||||
((unsigned long *)task_pt_regs(task))[regno] = data;
|
||||
else if (regno == PT_USP)
|
||||
task->thread.usp = data;
|
||||
else if (regno == PT_PPC) {
|
||||
/* Write pseudo-PC to ERP only if changed. */
|
||||
if (data != get_pseudo_pc(task))
|
||||
task_pt_regs(task)->erp = data;
|
||||
} else if (regno <= PT_MAX)
|
||||
return put_debugreg(task->pid, regno, data);
|
||||
else
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void user_enable_single_step(struct task_struct *child)
|
||||
{
|
||||
unsigned long tmp;
|
||||
|
||||
/*
|
||||
* Set up SPC if not set already (in which case we have no other
|
||||
* choice but to trust it).
|
||||
*/
|
||||
if (!get_reg(child, PT_SPC)) {
|
||||
/* In case we're stopped in a delay slot. */
|
||||
tmp = get_reg(child, PT_ERP) & ~1;
|
||||
put_reg(child, PT_SPC, tmp);
|
||||
}
|
||||
tmp = get_reg(child, PT_CCS) | SBIT_USER;
|
||||
put_reg(child, PT_CCS, tmp);
|
||||
}
|
||||
|
||||
void user_disable_single_step(struct task_struct *child)
|
||||
{
|
||||
put_reg(child, PT_SPC, 0);
|
||||
|
||||
if (!get_debugreg(child->pid, PT_BP_CTRL)) {
|
||||
unsigned long tmp;
|
||||
/* If no h/w bp configured, disable S bit. */
|
||||
tmp = get_reg(child, PT_CCS) & ~SBIT_USER;
|
||||
put_reg(child, PT_CCS, tmp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by kernel/ptrace.c when detaching.
|
||||
*
|
||||
* Make sure the single step bit is not set.
|
||||
*/
|
||||
void
|
||||
ptrace_disable(struct task_struct *child)
|
||||
{
|
||||
/* Deconfigure SPC and S-bit. */
|
||||
user_disable_single_step(child);
|
||||
put_reg(child, PT_SPC, 0);
|
||||
|
||||
/* Deconfigure any watchpoints associated with the child. */
|
||||
deconfigure_bp(child->pid);
|
||||
}
|
||||
|
||||
|
||||
long arch_ptrace(struct task_struct *child, long request,
|
||||
unsigned long addr, unsigned long data)
|
||||
{
|
||||
int ret;
|
||||
unsigned int regno = addr >> 2;
|
||||
unsigned long __user *datap = (unsigned long __user *)data;
|
||||
|
||||
switch (request) {
|
||||
/* Read word at location address. */
|
||||
case PTRACE_PEEKTEXT:
|
||||
case PTRACE_PEEKDATA: {
|
||||
unsigned long tmp;
|
||||
int copied;
|
||||
|
||||
ret = -EIO;
|
||||
|
||||
/* The signal trampoline page is outside the normal user-addressable
|
||||
* space but still accessible. This is hack to make it possible to
|
||||
* access the signal handler code in GDB.
|
||||
*/
|
||||
if ((addr & PAGE_MASK) == cris_signal_return_page) {
|
||||
/* The trampoline page is globally mapped, no page table to traverse.*/
|
||||
tmp = *(unsigned long*)addr;
|
||||
} else {
|
||||
copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
|
||||
|
||||
if (copied != sizeof(tmp))
|
||||
break;
|
||||
}
|
||||
|
||||
ret = put_user(tmp,datap);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Read the word at location address in the USER area. */
|
||||
case PTRACE_PEEKUSR: {
|
||||
unsigned long tmp;
|
||||
|
||||
ret = -EIO;
|
||||
if ((addr & 3) || regno > PT_MAX)
|
||||
break;
|
||||
|
||||
tmp = get_reg(child, regno);
|
||||
ret = put_user(tmp, datap);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Write the word at location address. */
|
||||
case PTRACE_POKETEXT:
|
||||
case PTRACE_POKEDATA:
|
||||
ret = generic_ptrace_pokedata(child, addr, data);
|
||||
break;
|
||||
|
||||
/* Write the word at location address in the USER area. */
|
||||
case PTRACE_POKEUSR:
|
||||
ret = -EIO;
|
||||
if ((addr & 3) || regno > PT_MAX)
|
||||
break;
|
||||
|
||||
if (regno == PT_CCS) {
|
||||
/* don't allow the tracing process to change stuff like
|
||||
* interrupt enable, kernel/user bit, dma enables etc.
|
||||
*/
|
||||
data &= CCS_MASK;
|
||||
data |= get_reg(child, PT_CCS) & ~CCS_MASK;
|
||||
}
|
||||
if (put_reg(child, regno, data))
|
||||
break;
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
/* Get all GP registers from the child. */
|
||||
case PTRACE_GETREGS: {
|
||||
int i;
|
||||
unsigned long tmp;
|
||||
|
||||
for (i = 0; i <= PT_MAX; i++) {
|
||||
tmp = get_reg(child, i);
|
||||
|
||||
if (put_user(tmp, datap)) {
|
||||
ret = -EFAULT;
|
||||
goto out_tsk;
|
||||
}
|
||||
|
||||
datap++;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set all GP registers in the child. */
|
||||
case PTRACE_SETREGS: {
|
||||
int i;
|
||||
unsigned long tmp;
|
||||
|
||||
for (i = 0; i <= PT_MAX; i++) {
|
||||
if (get_user(tmp, datap)) {
|
||||
ret = -EFAULT;
|
||||
goto out_tsk;
|
||||
}
|
||||
|
||||
if (i == PT_CCS) {
|
||||
tmp &= CCS_MASK;
|
||||
tmp |= get_reg(child, PT_CCS) & ~CCS_MASK;
|
||||
}
|
||||
|
||||
put_reg(child, i, tmp);
|
||||
datap++;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ret = ptrace_request(child, request, addr, data);
|
||||
break;
|
||||
}
|
||||
|
||||
out_tsk:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void do_syscall_trace(void)
|
||||
{
|
||||
if (!test_thread_flag(TIF_SYSCALL_TRACE))
|
||||
return;
|
||||
|
||||
if (!(current->ptrace & PT_PTRACED))
|
||||
return;
|
||||
|
||||
/* the 0x80 provides a way for the tracing parent to distinguish
|
||||
between a syscall stop and SIGTRAP delivery */
|
||||
ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
|
||||
? 0x80 : 0));
|
||||
|
||||
/*
|
||||
* This isn't the same as continuing with a signal, but it will do for
|
||||
* normal use.
|
||||
*/
|
||||
if (current->exit_code) {
|
||||
send_sig(current->exit_code, current, 1);
|
||||
current->exit_code = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns the size of an instruction that has a delay slot. */
|
||||
|
||||
static int insn_size(struct task_struct *child, unsigned long pc)
|
||||
{
|
||||
unsigned long opcode;
|
||||
int copied;
|
||||
int opsize = 0;
|
||||
|
||||
/* Read the opcode at pc (do what PTRACE_PEEKTEXT would do). */
|
||||
copied = access_process_vm(child, pc, &opcode, sizeof(opcode), 0);
|
||||
if (copied != sizeof(opcode))
|
||||
return 0;
|
||||
|
||||
switch ((opcode & 0x0f00) >> 8) {
|
||||
case 0x0:
|
||||
case 0x9:
|
||||
case 0xb:
|
||||
opsize = 2;
|
||||
break;
|
||||
case 0xe:
|
||||
case 0xf:
|
||||
opsize = 6;
|
||||
break;
|
||||
case 0xd:
|
||||
/* Could be 4 or 6; check more bits. */
|
||||
if ((opcode & 0xff) == 0xff)
|
||||
opsize = 4;
|
||||
else
|
||||
opsize = 6;
|
||||
break;
|
||||
default:
|
||||
panic("ERROR: Couldn't find size of opcode 0x%lx at 0x%lx\n",
|
||||
opcode, pc);
|
||||
}
|
||||
|
||||
return opsize;
|
||||
}
|
||||
|
||||
static unsigned long get_pseudo_pc(struct task_struct *child)
|
||||
{
|
||||
/* Default value for PC is ERP. */
|
||||
unsigned long pc = get_reg(child, PT_ERP);
|
||||
|
||||
if (pc & 0x1) {
|
||||
unsigned long spc = get_reg(child, PT_SPC);
|
||||
/* Delay slot bit set. Report as stopped on proper
|
||||
instruction. */
|
||||
if (spc) {
|
||||
/* Rely on SPC if set. FIXME: We might want to check
|
||||
that EXS indicates we stopped due to a single-step
|
||||
exception. */
|
||||
pc = spc;
|
||||
} else {
|
||||
/* Calculate the PC from the size of the instruction
|
||||
that the delay slot we're in belongs to. */
|
||||
pc += insn_size(child, pc & ~1) - 1;
|
||||
}
|
||||
}
|
||||
return pc;
|
||||
}
|
||||
|
||||
static long bp_owner = 0;
|
||||
|
||||
/* Reachable from exit_thread in signal.c, so not static. */
|
||||
void deconfigure_bp(long pid)
|
||||
{
|
||||
int bp;
|
||||
|
||||
/* Only deconfigure if the pid is the owner. */
|
||||
if (bp_owner != pid)
|
||||
return;
|
||||
|
||||
for (bp = 0; bp < 6; bp++) {
|
||||
unsigned long tmp;
|
||||
/* Deconfigure start and end address (also gets rid of ownership). */
|
||||
put_debugreg(pid, PT_BP + 3 + (bp * 2), 0);
|
||||
put_debugreg(pid, PT_BP + 4 + (bp * 2), 0);
|
||||
|
||||
/* Deconfigure relevant bits in control register. */
|
||||
tmp = get_debugreg(pid, PT_BP_CTRL) & ~(3 << (2 + (bp * 4)));
|
||||
put_debugreg(pid, PT_BP_CTRL, tmp);
|
||||
}
|
||||
/* No owner now. */
|
||||
bp_owner = 0;
|
||||
}
|
||||
|
||||
static int put_debugreg(long pid, unsigned int regno, long data)
|
||||
{
|
||||
int ret = 0;
|
||||
register int old_srs;
|
||||
|
||||
#ifdef CONFIG_ETRAX_KGDB
|
||||
/* Ignore write, but pretend it was ok if value is 0
|
||||
(we don't want POKEUSR/SETREGS failing unnessecarily). */
|
||||
return (data == 0) ? ret : -1;
|
||||
#endif
|
||||
|
||||
/* Simple owner management. */
|
||||
if (!bp_owner)
|
||||
bp_owner = pid;
|
||||
else if (bp_owner != pid) {
|
||||
/* Ignore write, but pretend it was ok if value is 0
|
||||
(we don't want POKEUSR/SETREGS failing unnessecarily). */
|
||||
return (data == 0) ? ret : -1;
|
||||
}
|
||||
|
||||
/* Remember old SRS. */
|
||||
SPEC_REG_RD(SPEC_REG_SRS, old_srs);
|
||||
/* Switch to BP bank. */
|
||||
SUPP_BANK_SEL(BANK_BP);
|
||||
|
||||
switch (regno - PT_BP) {
|
||||
case 0:
|
||||
SUPP_REG_WR(0, data); break;
|
||||
case 1:
|
||||
case 2:
|
||||
if (data)
|
||||
ret = -1;
|
||||
break;
|
||||
case 3:
|
||||
SUPP_REG_WR(3, data); break;
|
||||
case 4:
|
||||
SUPP_REG_WR(4, data); break;
|
||||
case 5:
|
||||
SUPP_REG_WR(5, data); break;
|
||||
case 6:
|
||||
SUPP_REG_WR(6, data); break;
|
||||
case 7:
|
||||
SUPP_REG_WR(7, data); break;
|
||||
case 8:
|
||||
SUPP_REG_WR(8, data); break;
|
||||
case 9:
|
||||
SUPP_REG_WR(9, data); break;
|
||||
case 10:
|
||||
SUPP_REG_WR(10, data); break;
|
||||
case 11:
|
||||
SUPP_REG_WR(11, data); break;
|
||||
case 12:
|
||||
SUPP_REG_WR(12, data); break;
|
||||
case 13:
|
||||
SUPP_REG_WR(13, data); break;
|
||||
case 14:
|
||||
SUPP_REG_WR(14, data); break;
|
||||
default:
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Restore SRS. */
|
||||
SPEC_REG_WR(SPEC_REG_SRS, old_srs);
|
||||
/* Just for show. */
|
||||
NOP();
|
||||
NOP();
|
||||
NOP();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long get_debugreg(long pid, unsigned int regno)
|
||||
{
|
||||
register int old_srs;
|
||||
register long data;
|
||||
|
||||
if (pid != bp_owner) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Remember old SRS. */
|
||||
SPEC_REG_RD(SPEC_REG_SRS, old_srs);
|
||||
/* Switch to BP bank. */
|
||||
SUPP_BANK_SEL(BANK_BP);
|
||||
|
||||
switch (regno - PT_BP) {
|
||||
case 0:
|
||||
SUPP_REG_RD(0, data); break;
|
||||
case 1:
|
||||
case 2:
|
||||
/* error return value? */
|
||||
data = 0;
|
||||
break;
|
||||
case 3:
|
||||
SUPP_REG_RD(3, data); break;
|
||||
case 4:
|
||||
SUPP_REG_RD(4, data); break;
|
||||
case 5:
|
||||
SUPP_REG_RD(5, data); break;
|
||||
case 6:
|
||||
SUPP_REG_RD(6, data); break;
|
||||
case 7:
|
||||
SUPP_REG_RD(7, data); break;
|
||||
case 8:
|
||||
SUPP_REG_RD(8, data); break;
|
||||
case 9:
|
||||
SUPP_REG_RD(9, data); break;
|
||||
case 10:
|
||||
SUPP_REG_RD(10, data); break;
|
||||
case 11:
|
||||
SUPP_REG_RD(11, data); break;
|
||||
case 12:
|
||||
SUPP_REG_RD(12, data); break;
|
||||
case 13:
|
||||
SUPP_REG_RD(13, data); break;
|
||||
case 14:
|
||||
SUPP_REG_RD(14, data); break;
|
||||
default:
|
||||
/* error return value? */
|
||||
data = 0;
|
||||
}
|
||||
|
||||
/* Restore SRS. */
|
||||
SPEC_REG_WR(SPEC_REG_SRS, old_srs);
|
||||
/* Just for show. */
|
||||
NOP();
|
||||
NOP();
|
||||
NOP();
|
||||
|
||||
return data;
|
||||
}
|
173
arch/cris/arch-v32/kernel/setup.c
Normal file
173
arch/cris/arch-v32/kernel/setup.c
Normal file
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* Display CPU info in /proc/cpuinfo.
|
||||
*
|
||||
* Copyright (C) 2003, Axis Communications AB.
|
||||
*/
|
||||
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/param.h>
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
#define HAS_FPU 0x0001
|
||||
#define HAS_MMU 0x0002
|
||||
#define HAS_ETHERNET100 0x0004
|
||||
#define HAS_TOKENRING 0x0008
|
||||
#define HAS_SCSI 0x0010
|
||||
#define HAS_ATA 0x0020
|
||||
#define HAS_USB 0x0040
|
||||
#define HAS_IRQ_BUG 0x0080
|
||||
#define HAS_MMU_BUG 0x0100
|
||||
|
||||
struct cpu_info {
|
||||
char *cpu_model;
|
||||
unsigned short rev;
|
||||
unsigned short cache_size;
|
||||
unsigned short flags;
|
||||
};
|
||||
|
||||
/* Some of these model are here for historical reasons only. */
|
||||
static struct cpu_info cpinfo[] = {
|
||||
{"ETRAX 1", 0, 0, 0},
|
||||
{"ETRAX 2", 1, 0, 0},
|
||||
{"ETRAX 3", 2, 0, 0},
|
||||
{"ETRAX 4", 3, 0, 0},
|
||||
{"Simulator", 7, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA},
|
||||
{"ETRAX 100", 8, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_IRQ_BUG},
|
||||
{"ETRAX 100", 9, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA},
|
||||
|
||||
{"ETRAX 100LX", 10, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB
|
||||
| HAS_MMU | HAS_MMU_BUG},
|
||||
|
||||
{"ETRAX 100LX v2", 11, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB
|
||||
| HAS_MMU},
|
||||
#ifdef CONFIG_ETRAXFS
|
||||
{"ETRAX FS", 32, 32, HAS_ETHERNET100 | HAS_ATA | HAS_MMU},
|
||||
#else
|
||||
{"ARTPEC-3", 32, 32, HAS_ETHERNET100 | HAS_MMU},
|
||||
#endif
|
||||
{"Unknown", 0, 0, 0}
|
||||
};
|
||||
|
||||
int show_cpuinfo(struct seq_file *m, void *v)
|
||||
{
|
||||
int i;
|
||||
int cpu = (int)v - 1;
|
||||
unsigned long revision;
|
||||
struct cpu_info *info;
|
||||
|
||||
info = &cpinfo[ARRAY_SIZE(cpinfo) - 1];
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
if (!cpu_online(cpu))
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
revision = rdvr();
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cpinfo); i++) {
|
||||
if (cpinfo[i].rev == revision) {
|
||||
info = &cpinfo[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return seq_printf(m,
|
||||
"processor\t: %d\n"
|
||||
"cpu\t\t: CRIS\n"
|
||||
"cpu revision\t: %lu\n"
|
||||
"cpu model\t: %s\n"
|
||||
"cache size\t: %d KB\n"
|
||||
"fpu\t\t: %s\n"
|
||||
"mmu\t\t: %s\n"
|
||||
"mmu DMA bug\t: %s\n"
|
||||
"ethernet\t: %s Mbps\n"
|
||||
"token ring\t: %s\n"
|
||||
"scsi\t\t: %s\n"
|
||||
"ata\t\t: %s\n"
|
||||
"usb\t\t: %s\n"
|
||||
"bogomips\t: %lu.%02lu\n\n",
|
||||
|
||||
cpu,
|
||||
revision,
|
||||
info->cpu_model,
|
||||
info->cache_size,
|
||||
info->flags & HAS_FPU ? "yes" : "no",
|
||||
info->flags & HAS_MMU ? "yes" : "no",
|
||||
info->flags & HAS_MMU_BUG ? "yes" : "no",
|
||||
info->flags & HAS_ETHERNET100 ? "10/100" : "10",
|
||||
info->flags & HAS_TOKENRING ? "4/16 Mbps" : "no",
|
||||
info->flags & HAS_SCSI ? "yes" : "no",
|
||||
info->flags & HAS_ATA ? "yes" : "no",
|
||||
info->flags & HAS_USB ? "yes" : "no",
|
||||
(loops_per_jiffy * HZ + 500) / 500000,
|
||||
((loops_per_jiffy * HZ + 500) / 5000) % 100);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
void show_etrax_copyright(void)
|
||||
{
|
||||
#ifdef CONFIG_ETRAXFS
|
||||
printk(KERN_INFO "Linux/CRISv32 port on ETRAX FS "
|
||||
"(C) 2003, 2004 Axis Communications AB\n");
|
||||
#else
|
||||
printk(KERN_INFO "Linux/CRISv32 port on ARTPEC-3 "
|
||||
"(C) 2003-2009 Axis Communications AB\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct i2c_board_info __initdata i2c_info[] = {
|
||||
{I2C_BOARD_INFO("camblock", 0x43)},
|
||||
{I2C_BOARD_INFO("tmp100", 0x48)},
|
||||
{I2C_BOARD_INFO("tmp100", 0x4A)},
|
||||
{I2C_BOARD_INFO("tmp100", 0x4C)},
|
||||
{I2C_BOARD_INFO("tmp100", 0x4D)},
|
||||
{I2C_BOARD_INFO("tmp100", 0x4E)},
|
||||
#ifdef CONFIG_RTC_DRV_PCF8563
|
||||
{I2C_BOARD_INFO("pcf8563", 0x51)},
|
||||
#endif
|
||||
#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
|
||||
{I2C_BOARD_INFO("vgpio", 0x20)},
|
||||
{I2C_BOARD_INFO("vgpio", 0x21)},
|
||||
#endif
|
||||
{I2C_BOARD_INFO("pca9536", 0x41)},
|
||||
{I2C_BOARD_INFO("fnp300", 0x40)},
|
||||
{I2C_BOARD_INFO("fnp300", 0x42)},
|
||||
{I2C_BOARD_INFO("adc101", 0x54)},
|
||||
};
|
||||
|
||||
static struct i2c_board_info __initdata i2c_info2[] = {
|
||||
{I2C_BOARD_INFO("camblock", 0x43)},
|
||||
{I2C_BOARD_INFO("tmp100", 0x48)},
|
||||
{I2C_BOARD_INFO("tmp100", 0x4A)},
|
||||
{I2C_BOARD_INFO("tmp100", 0x4C)},
|
||||
{I2C_BOARD_INFO("tmp100", 0x4D)},
|
||||
{I2C_BOARD_INFO("tmp100", 0x4E)},
|
||||
#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
|
||||
{I2C_BOARD_INFO("vgpio", 0x20)},
|
||||
{I2C_BOARD_INFO("vgpio", 0x21)},
|
||||
#endif
|
||||
{I2C_BOARD_INFO("pca9536", 0x41)},
|
||||
{I2C_BOARD_INFO("fnp300", 0x40)},
|
||||
{I2C_BOARD_INFO("fnp300", 0x42)},
|
||||
{I2C_BOARD_INFO("adc101", 0x54)},
|
||||
};
|
||||
|
||||
static struct i2c_board_info __initdata i2c_info3[] = {
|
||||
{I2C_BOARD_INFO("adc101", 0x54)},
|
||||
};
|
||||
|
||||
static int __init etrax_init(void)
|
||||
{
|
||||
i2c_register_board_info(0, i2c_info, ARRAY_SIZE(i2c_info));
|
||||
i2c_register_board_info(1, i2c_info2, ARRAY_SIZE(i2c_info2));
|
||||
i2c_register_board_info(2, i2c_info3, ARRAY_SIZE(i2c_info3));
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(etrax_init);
|
537
arch/cris/arch-v32/kernel/signal.c
Normal file
537
arch/cris/arch-v32/kernel/signal.c
Normal file
|
@ -0,0 +1,537 @@
|
|||
/*
|
||||
* Copyright (C) 2003, Axis Communications AB.
|
||||
*/
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/ucontext.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <arch/ptrace.h>
|
||||
#include <arch/hwregs/cpu_vect.h>
|
||||
|
||||
extern unsigned long cris_signal_return_page;
|
||||
|
||||
/*
|
||||
* A syscall in CRIS is really a "break 13" instruction, which is 2
|
||||
* bytes. The registers is manipulated so upon return the instruction
|
||||
* will be executed again.
|
||||
*
|
||||
* This relies on that PC points to the instruction after the break call.
|
||||
*/
|
||||
#define RESTART_CRIS_SYS(regs) regs->r10 = regs->orig_r10; regs->erp -= 2;
|
||||
|
||||
/* Signal frames. */
|
||||
struct signal_frame {
|
||||
struct sigcontext sc;
|
||||
unsigned long extramask[_NSIG_WORDS - 1];
|
||||
unsigned char retcode[8]; /* Trampoline code. */
|
||||
};
|
||||
|
||||
struct rt_signal_frame {
|
||||
struct siginfo *pinfo;
|
||||
void *puc;
|
||||
struct siginfo info;
|
||||
struct ucontext uc;
|
||||
unsigned char retcode[8]; /* Trampoline code. */
|
||||
};
|
||||
|
||||
void do_signal(int restart, struct pt_regs *regs);
|
||||
void keep_debug_flags(unsigned long oldccs, unsigned long oldspc,
|
||||
struct pt_regs *regs);
|
||||
|
||||
static int
|
||||
restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
|
||||
{
|
||||
unsigned int err = 0;
|
||||
unsigned long old_usp;
|
||||
|
||||
/* Always make any pending restarted system calls return -EINTR */
|
||||
current_thread_info()->restart_block.fn = do_no_restart_syscall;
|
||||
|
||||
/*
|
||||
* Restore the registers from &sc->regs. sc is already checked
|
||||
* for VERIFY_READ since the signal_frame was previously
|
||||
* checked in sys_sigreturn().
|
||||
*/
|
||||
if (__copy_from_user(regs, sc, sizeof(struct pt_regs)))
|
||||
goto badframe;
|
||||
|
||||
/* Make that the user-mode flag is set. */
|
||||
regs->ccs |= (1 << (U_CCS_BITNR + CCS_SHIFT));
|
||||
|
||||
/* Restore the old USP. */
|
||||
err |= __get_user(old_usp, &sc->usp);
|
||||
wrusp(old_usp);
|
||||
|
||||
return err;
|
||||
|
||||
badframe:
|
||||
return 1;
|
||||
}
|
||||
|
||||
asmlinkage int sys_sigreturn(void)
|
||||
{
|
||||
struct pt_regs *regs = current_pt_regs();
|
||||
sigset_t set;
|
||||
struct signal_frame __user *frame;
|
||||
unsigned long oldspc = regs->spc;
|
||||
unsigned long oldccs = regs->ccs;
|
||||
|
||||
frame = (struct signal_frame *) rdusp();
|
||||
|
||||
/*
|
||||
* Since the signal is stacked on a dword boundary, the frame
|
||||
* should be dword aligned here as well. It it's not, then the
|
||||
* user is trying some funny business.
|
||||
*/
|
||||
if (((long)frame) & 3)
|
||||
goto badframe;
|
||||
|
||||
if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
|
||||
goto badframe;
|
||||
|
||||
if (__get_user(set.sig[0], &frame->sc.oldmask) ||
|
||||
(_NSIG_WORDS > 1 && __copy_from_user(&set.sig[1],
|
||||
frame->extramask,
|
||||
sizeof(frame->extramask))))
|
||||
goto badframe;
|
||||
|
||||
set_current_blocked(&set);
|
||||
|
||||
if (restore_sigcontext(regs, &frame->sc))
|
||||
goto badframe;
|
||||
|
||||
keep_debug_flags(oldccs, oldspc, regs);
|
||||
|
||||
return regs->r10;
|
||||
|
||||
badframe:
|
||||
force_sig(SIGSEGV, current);
|
||||
return 0;
|
||||
}
|
||||
|
||||
asmlinkage int sys_rt_sigreturn(void)
|
||||
{
|
||||
struct pt_regs *regs = current_pt_regs();
|
||||
sigset_t set;
|
||||
struct rt_signal_frame __user *frame;
|
||||
unsigned long oldspc = regs->spc;
|
||||
unsigned long oldccs = regs->ccs;
|
||||
|
||||
frame = (struct rt_signal_frame *) rdusp();
|
||||
|
||||
/*
|
||||
* Since the signal is stacked on a dword boundary, the frame
|
||||
* should be dword aligned here as well. It it's not, then the
|
||||
* user is trying some funny business.
|
||||
*/
|
||||
if (((long)frame) & 3)
|
||||
goto badframe;
|
||||
|
||||
if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
|
||||
goto badframe;
|
||||
|
||||
if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
|
||||
goto badframe;
|
||||
|
||||
set_current_blocked(&set);
|
||||
|
||||
if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
|
||||
goto badframe;
|
||||
|
||||
if (restore_altstack(&frame->uc.uc_stack))
|
||||
goto badframe;
|
||||
|
||||
keep_debug_flags(oldccs, oldspc, regs);
|
||||
|
||||
return regs->r10;
|
||||
|
||||
badframe:
|
||||
force_sig(SIGSEGV, current);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Setup a signal frame. */
|
||||
static int
|
||||
setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
|
||||
unsigned long mask)
|
||||
{
|
||||
int err;
|
||||
unsigned long usp;
|
||||
|
||||
err = 0;
|
||||
usp = rdusp();
|
||||
|
||||
/*
|
||||
* Copy the registers. They are located first in sc, so it's
|
||||
* possible to use sc directly.
|
||||
*/
|
||||
err |= __copy_to_user(sc, regs, sizeof(struct pt_regs));
|
||||
|
||||
err |= __put_user(mask, &sc->oldmask);
|
||||
err |= __put_user(usp, &sc->usp);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Figure out where to put the new signal frame - usually on the stack. */
|
||||
static inline void __user *
|
||||
get_sigframe(struct ksignal *ksig, size_t frame_size)
|
||||
{
|
||||
unsigned long sp = sigsp(rdusp(), ksig);
|
||||
|
||||
/* Make sure the frame is dword-aligned. */
|
||||
sp &= ~3;
|
||||
|
||||
return (void __user *)(sp - frame_size);
|
||||
}
|
||||
|
||||
/* Grab and setup a signal frame.
|
||||
*
|
||||
* Basically a lot of state-info is stacked, and arranged for the
|
||||
* user-mode program to return to the kernel using either a trampiline
|
||||
* which performs the syscall sigreturn(), or a provided user-mode
|
||||
* trampoline.
|
||||
*/
|
||||
static int
|
||||
setup_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs)
|
||||
{
|
||||
int err;
|
||||
unsigned long return_ip;
|
||||
struct signal_frame __user *frame;
|
||||
|
||||
err = 0;
|
||||
frame = get_sigframe(ksig, sizeof(*frame));
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
|
||||
return -EFAULT;
|
||||
|
||||
err |= setup_sigcontext(&frame->sc, regs, set->sig[0]);
|
||||
|
||||
if (err)
|
||||
return -EFAULT;
|
||||
|
||||
if (_NSIG_WORDS > 1) {
|
||||
err |= __copy_to_user(frame->extramask, &set->sig[1],
|
||||
sizeof(frame->extramask));
|
||||
}
|
||||
|
||||
if (err)
|
||||
return -EFAULT;
|
||||
|
||||
/*
|
||||
* Set up to return from user-space. If provided, use a stub
|
||||
* already located in user-space.
|
||||
*/
|
||||
if (ksig->ka.sa.sa_flags & SA_RESTORER) {
|
||||
return_ip = (unsigned long)ksig->ka.sa.sa_restorer;
|
||||
} else {
|
||||
/* Trampoline - the desired return ip is in the signal return page. */
|
||||
return_ip = cris_signal_return_page;
|
||||
|
||||
/*
|
||||
* This is movu.w __NR_sigreturn, r9; break 13;
|
||||
*
|
||||
* WE DO NOT USE IT ANY MORE! It's only left here for historical
|
||||
* reasons and because gdb uses it as a signature to notice
|
||||
* signal handler stack frames.
|
||||
*/
|
||||
err |= __put_user(0x9c5f, (short __user*)(frame->retcode+0));
|
||||
err |= __put_user(__NR_sigreturn, (short __user*)(frame->retcode+2));
|
||||
err |= __put_user(0xe93d, (short __user*)(frame->retcode+4));
|
||||
}
|
||||
|
||||
if (err)
|
||||
return -EFAULT;
|
||||
|
||||
/*
|
||||
* Set up registers for signal handler.
|
||||
*
|
||||
* Where the code enters now.
|
||||
* Where the code enter later.
|
||||
* First argument, signo.
|
||||
*/
|
||||
regs->erp = (unsigned long) ksig->ka.sa.sa_handler;
|
||||
regs->srp = return_ip;
|
||||
regs->r10 = ksig->sig;
|
||||
|
||||
/* Actually move the USP to reflect the stacked frame. */
|
||||
wrusp((unsigned long)frame);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs)
|
||||
{
|
||||
int err;
|
||||
unsigned long return_ip;
|
||||
struct rt_signal_frame __user *frame;
|
||||
|
||||
err = 0;
|
||||
frame = get_sigframe(ksig, sizeof(*frame));
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
|
||||
return -EFAULT;
|
||||
|
||||
/* TODO: what is the current->exec_domain stuff and invmap ? */
|
||||
|
||||
err |= __put_user(&frame->info, &frame->pinfo);
|
||||
err |= __put_user(&frame->uc, &frame->puc);
|
||||
err |= copy_siginfo_to_user(&frame->info, &ksig->info);
|
||||
|
||||
if (err)
|
||||
return -EFAULT;
|
||||
|
||||
/* Clear all the bits of the ucontext we don't use. */
|
||||
err |= __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext));
|
||||
err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]);
|
||||
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
|
||||
err |= __save_altstack(&frame->uc.uc_stack, rdusp());
|
||||
|
||||
if (err)
|
||||
return -EFAULT;
|
||||
|
||||
/*
|
||||
* Set up to return from user-space. If provided, use a stub
|
||||
* already located in user-space.
|
||||
*/
|
||||
if (ksig->ka.sa.sa_flags & SA_RESTORER) {
|
||||
return_ip = (unsigned long) ksig->ka.sa.sa_restorer;
|
||||
} else {
|
||||
/* Trampoline - the desired return ip is in the signal return page. */
|
||||
return_ip = cris_signal_return_page + 6;
|
||||
|
||||
/*
|
||||
* This is movu.w __NR_rt_sigreturn, r9; break 13;
|
||||
*
|
||||
* WE DO NOT USE IT ANY MORE! It's only left here for historical
|
||||
* reasons and because gdb uses it as a signature to notice
|
||||
* signal handler stack frames.
|
||||
*/
|
||||
err |= __put_user(0x9c5f, (short __user*)(frame->retcode+0));
|
||||
|
||||
err |= __put_user(__NR_rt_sigreturn,
|
||||
(short __user*)(frame->retcode+2));
|
||||
|
||||
err |= __put_user(0xe93d, (short __user*)(frame->retcode+4));
|
||||
}
|
||||
|
||||
if (err)
|
||||
return -EFAULT;
|
||||
|
||||
/*
|
||||
* Set up registers for signal handler.
|
||||
*
|
||||
* Where the code enters now.
|
||||
* Where the code enters later.
|
||||
* First argument is signo.
|
||||
* Second argument is (siginfo_t *).
|
||||
* Third argument is unused.
|
||||
*/
|
||||
regs->erp = (unsigned long) ksig->ka.sa.sa_handler;
|
||||
regs->srp = return_ip;
|
||||
regs->r10 = ksig->sig;
|
||||
regs->r11 = (unsigned long) &frame->info;
|
||||
regs->r12 = 0;
|
||||
|
||||
/* Actually move the usp to reflect the stacked frame. */
|
||||
wrusp((unsigned long)frame);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Invoke a signal handler to, well, handle the signal. */
|
||||
static inline void
|
||||
handle_signal(int canrestart, struct ksignal *ksig, struct pt_regs *regs)
|
||||
{
|
||||
sigset_t *oldset = sigmask_to_save();
|
||||
int ret;
|
||||
|
||||
/* Check if this got called from a system call. */
|
||||
if (canrestart) {
|
||||
/* If so, check system call restarting. */
|
||||
switch (regs->r10) {
|
||||
case -ERESTART_RESTARTBLOCK:
|
||||
case -ERESTARTNOHAND:
|
||||
/*
|
||||
* This means that the syscall should
|
||||
* only be restarted if there was no
|
||||
* handler for the signal, and since
|
||||
* this point isn't reached unless
|
||||
* there is a handler, there's no need
|
||||
* to restart.
|
||||
*/
|
||||
regs->r10 = -EINTR;
|
||||
break;
|
||||
|
||||
case -ERESTARTSYS:
|
||||
/*
|
||||
* This means restart the syscall if
|
||||
* there is no handler, or the handler
|
||||
* was registered with SA_RESTART.
|
||||
*/
|
||||
if (!(ksig->ka.sa.sa_flags & SA_RESTART)) {
|
||||
regs->r10 = -EINTR;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Fall through. */
|
||||
|
||||
case -ERESTARTNOINTR:
|
||||
/*
|
||||
* This means that the syscall should
|
||||
* be called again after the signal
|
||||
* handler returns.
|
||||
*/
|
||||
RESTART_CRIS_SYS(regs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set up the stack frame. */
|
||||
if (ksig->ka.sa.sa_flags & SA_SIGINFO)
|
||||
ret = setup_rt_frame(ksig, oldset, regs);
|
||||
else
|
||||
ret = setup_frame(ksig, oldset, regs);
|
||||
|
||||
signal_setup_done(ret, ksig, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that 'init' is a special process: it doesn't get signals it doesn't
|
||||
* want to handle. Thus you cannot kill init even with a SIGKILL even by
|
||||
* mistake.
|
||||
*
|
||||
* Also note that the regs structure given here as an argument, is the latest
|
||||
* pushed pt_regs. It may or may not be the same as the first pushed registers
|
||||
* when the initial usermode->kernelmode transition took place. Therefore
|
||||
* we can use user_mode(regs) to see if we came directly from kernel or user
|
||||
* mode below.
|
||||
*/
|
||||
void
|
||||
do_signal(int canrestart, struct pt_regs *regs)
|
||||
{
|
||||
struct ksignal ksig;
|
||||
|
||||
/*
|
||||
* The common case should go fast, which is why this point is
|
||||
* reached from kernel-mode. If that's the case, just return
|
||||
* without doing anything.
|
||||
*/
|
||||
if (!user_mode(regs))
|
||||
return;
|
||||
|
||||
if (get_signal(&ksig)) {
|
||||
/* Whee! Actually deliver the signal. */
|
||||
handle_signal(canrestart, &ksig, regs);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Got here from a system call? */
|
||||
if (canrestart) {
|
||||
/* Restart the system call - no handlers present. */
|
||||
if (regs->r10 == -ERESTARTNOHAND ||
|
||||
regs->r10 == -ERESTARTSYS ||
|
||||
regs->r10 == -ERESTARTNOINTR) {
|
||||
RESTART_CRIS_SYS(regs);
|
||||
}
|
||||
|
||||
if (regs->r10 == -ERESTART_RESTARTBLOCK){
|
||||
regs->r9 = __NR_restart_syscall;
|
||||
regs->erp -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* if there's no signal to deliver, we just put the saved sigmask
|
||||
* back */
|
||||
restore_saved_sigmask();
|
||||
}
|
||||
|
||||
asmlinkage void
|
||||
ugdb_trap_user(struct thread_info *ti, int sig)
|
||||
{
|
||||
if (((user_regs(ti)->exs & 0xff00) >> 8) != SINGLE_STEP_INTR_VECT) {
|
||||
/* Zero single-step PC if the reason we stopped wasn't a single
|
||||
step exception. This is to avoid relying on it when it isn't
|
||||
reliable. */
|
||||
user_regs(ti)->spc = 0;
|
||||
}
|
||||
/* FIXME: Filter out false h/w breakpoint hits (i.e. EDA
|
||||
not within any configured h/w breakpoint range). Synchronize with
|
||||
what already exists for kernel debugging. */
|
||||
if (((user_regs(ti)->exs & 0xff00) >> 8) == BREAK_8_INTR_VECT) {
|
||||
/* Break 8: subtract 2 from ERP unless in a delay slot. */
|
||||
if (!(user_regs(ti)->erp & 0x1))
|
||||
user_regs(ti)->erp -= 2;
|
||||
}
|
||||
sys_kill(ti->task->pid, sig);
|
||||
}
|
||||
|
||||
void
|
||||
keep_debug_flags(unsigned long oldccs, unsigned long oldspc,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
if (oldccs & (1 << Q_CCS_BITNR)) {
|
||||
/* Pending single step due to single-stepping the break 13
|
||||
in the signal trampoline: keep the Q flag. */
|
||||
regs->ccs |= (1 << Q_CCS_BITNR);
|
||||
/* S flag should be set - complain if it's not. */
|
||||
if (!(oldccs & (1 << (S_CCS_BITNR + CCS_SHIFT)))) {
|
||||
printk("Q flag but no S flag?");
|
||||
}
|
||||
regs->ccs |= (1 << (S_CCS_BITNR + CCS_SHIFT));
|
||||
/* Assume the SPC is valid and interesting. */
|
||||
regs->spc = oldspc;
|
||||
|
||||
} else if (oldccs & (1 << (S_CCS_BITNR + CCS_SHIFT))) {
|
||||
/* If a h/w bp was set in the signal handler we need
|
||||
to keep the S flag. */
|
||||
regs->ccs |= (1 << (S_CCS_BITNR + CCS_SHIFT));
|
||||
/* Don't keep the old SPC though; if we got here due to
|
||||
a single-step, the Q flag should have been set. */
|
||||
} else if (regs->spc) {
|
||||
/* If we were single-stepping *before* the signal was taken,
|
||||
we don't want to restore that state now, because GDB will
|
||||
have forgotten all about it. */
|
||||
regs->spc = 0;
|
||||
regs->ccs &= ~(1 << (S_CCS_BITNR + CCS_SHIFT));
|
||||
}
|
||||
}
|
||||
|
||||
/* Set up the trampolines on the signal return page. */
|
||||
int __init
|
||||
cris_init_signal(void)
|
||||
{
|
||||
u16* data = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
|
||||
/* This is movu.w __NR_sigreturn, r9; break 13; */
|
||||
data[0] = 0x9c5f;
|
||||
data[1] = __NR_sigreturn;
|
||||
data[2] = 0xe93d;
|
||||
/* This is movu.w __NR_rt_sigreturn, r9; break 13; */
|
||||
data[3] = 0x9c5f;
|
||||
data[4] = __NR_rt_sigreturn;
|
||||
data[5] = 0xe93d;
|
||||
|
||||
/* Map to userspace with appropriate permissions (no write access...) */
|
||||
cris_signal_return_page = (unsigned long)
|
||||
__ioremap_prot(virt_to_phys(data), PAGE_SIZE, PAGE_SIGNAL_TRAMPOLINE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__initcall(cris_init_signal);
|
358
arch/cris/arch-v32/kernel/smp.c
Normal file
358
arch/cris/arch-v32/kernel/smp.c
Normal file
|
@ -0,0 +1,358 @@
|
|||
#include <linux/types.h>
|
||||
#include <asm/delay.h>
|
||||
#include <irq.h>
|
||||
#include <hwregs/intr_vect.h>
|
||||
#include <hwregs/intr_vect_defs.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <hwregs/asm/mmu_defs_asm.h>
|
||||
#include <hwregs/supp_reg.h>
|
||||
#include <linux/atomic.h>
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/timex.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define IPI_SCHEDULE 1
|
||||
#define IPI_CALL 2
|
||||
#define IPI_FLUSH_TLB 4
|
||||
#define IPI_BOOT 8
|
||||
|
||||
#define FLUSH_ALL (void*)0xffffffff
|
||||
|
||||
/* Vector of locks used for various atomic operations */
|
||||
spinlock_t cris_atomic_locks[] = {
|
||||
[0 ... LOCK_COUNT - 1] = __SPIN_LOCK_UNLOCKED(cris_atomic_locks)
|
||||
};
|
||||
|
||||
/* CPU masks */
|
||||
cpumask_t phys_cpu_present_map = CPU_MASK_NONE;
|
||||
EXPORT_SYMBOL(phys_cpu_present_map);
|
||||
|
||||
/* Variables used during SMP boot */
|
||||
volatile int cpu_now_booting = 0;
|
||||
volatile struct thread_info *smp_init_current_idle_thread;
|
||||
|
||||
/* Variables used during IPI */
|
||||
static DEFINE_SPINLOCK(call_lock);
|
||||
static DEFINE_SPINLOCK(tlbstate_lock);
|
||||
|
||||
struct call_data_struct {
|
||||
void (*func) (void *info);
|
||||
void *info;
|
||||
int wait;
|
||||
};
|
||||
|
||||
static struct call_data_struct * call_data;
|
||||
|
||||
static struct mm_struct* flush_mm;
|
||||
static struct vm_area_struct* flush_vma;
|
||||
static unsigned long flush_addr;
|
||||
|
||||
/* Mode registers */
|
||||
static unsigned long irq_regs[NR_CPUS] = {
|
||||
regi_irq,
|
||||
regi_irq2
|
||||
};
|
||||
|
||||
static irqreturn_t crisv32_ipi_interrupt(int irq, void *dev_id);
|
||||
static int send_ipi(int vector, int wait, cpumask_t cpu_mask);
|
||||
static struct irqaction irq_ipi = {
|
||||
.handler = crisv32_ipi_interrupt,
|
||||
.flags = 0,
|
||||
.name = "ipi",
|
||||
};
|
||||
|
||||
extern void cris_mmu_init(void);
|
||||
extern void cris_timer_init(void);
|
||||
|
||||
/* SMP initialization */
|
||||
void __init smp_prepare_cpus(unsigned int max_cpus)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* From now on we can expect IPIs so set them up */
|
||||
setup_irq(IPI_INTR_VECT, &irq_ipi);
|
||||
|
||||
/* Mark all possible CPUs as present */
|
||||
for (i = 0; i < max_cpus; i++)
|
||||
cpumask_set_cpu(i, &phys_cpu_present_map);
|
||||
}
|
||||
|
||||
void smp_prepare_boot_cpu(void)
|
||||
{
|
||||
/* PGD pointer has moved after per_cpu initialization so
|
||||
* update the MMU.
|
||||
*/
|
||||
pgd_t **pgd;
|
||||
pgd = (pgd_t**)&per_cpu(current_pgd, smp_processor_id());
|
||||
|
||||
SUPP_BANK_SEL(1);
|
||||
SUPP_REG_WR(RW_MM_TLB_PGD, pgd);
|
||||
SUPP_BANK_SEL(2);
|
||||
SUPP_REG_WR(RW_MM_TLB_PGD, pgd);
|
||||
|
||||
set_cpu_online(0, true);
|
||||
cpumask_set_cpu(0, &phys_cpu_present_map);
|
||||
set_cpu_possible(0, true);
|
||||
}
|
||||
|
||||
void __init smp_cpus_done(unsigned int max_cpus)
|
||||
{
|
||||
}
|
||||
|
||||
/* Bring one cpu online.*/
|
||||
static int __init
|
||||
smp_boot_one_cpu(int cpuid, struct task_struct idle)
|
||||
{
|
||||
unsigned timeout;
|
||||
cpumask_t cpu_mask;
|
||||
|
||||
cpumask_clear(&cpu_mask);
|
||||
task_thread_info(idle)->cpu = cpuid;
|
||||
|
||||
/* Information to the CPU that is about to boot */
|
||||
smp_init_current_idle_thread = task_thread_info(idle);
|
||||
cpu_now_booting = cpuid;
|
||||
|
||||
/* Kick it */
|
||||
set_cpu_online(cpuid, true);
|
||||
cpumask_set_cpu(cpuid, &cpu_mask);
|
||||
send_ipi(IPI_BOOT, 0, cpu_mask);
|
||||
set_cpu_online(cpuid, false);
|
||||
|
||||
/* Wait for CPU to come online */
|
||||
for (timeout = 0; timeout < 10000; timeout++) {
|
||||
if(cpu_online(cpuid)) {
|
||||
cpu_now_booting = 0;
|
||||
smp_init_current_idle_thread = NULL;
|
||||
return 0; /* CPU online */
|
||||
}
|
||||
udelay(100);
|
||||
barrier();
|
||||
}
|
||||
|
||||
printk(KERN_CRIT "SMP: CPU:%d is stuck.\n", cpuid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Secondary CPUs starts using C here. Here we need to setup CPU
|
||||
* specific stuff such as the local timer and the MMU. */
|
||||
void __init smp_callin(void)
|
||||
{
|
||||
int cpu = cpu_now_booting;
|
||||
reg_intr_vect_rw_mask vect_mask = {0};
|
||||
|
||||
/* Initialise the idle task for this CPU */
|
||||
atomic_inc(&init_mm.mm_count);
|
||||
current->active_mm = &init_mm;
|
||||
|
||||
/* Set up MMU */
|
||||
cris_mmu_init();
|
||||
__flush_tlb_all();
|
||||
|
||||
/* Setup local timer. */
|
||||
cris_timer_init();
|
||||
|
||||
/* Enable IRQ and idle */
|
||||
REG_WR(intr_vect, irq_regs[cpu], rw_mask, vect_mask);
|
||||
crisv32_unmask_irq(IPI_INTR_VECT);
|
||||
crisv32_unmask_irq(TIMER0_INTR_VECT);
|
||||
preempt_disable();
|
||||
notify_cpu_starting(cpu);
|
||||
local_irq_enable();
|
||||
|
||||
set_cpu_online(cpu, true);
|
||||
cpu_startup_entry(CPUHP_ONLINE);
|
||||
}
|
||||
|
||||
/* Stop execution on this CPU.*/
|
||||
void stop_this_cpu(void* dummy)
|
||||
{
|
||||
local_irq_disable();
|
||||
asm volatile("halt");
|
||||
}
|
||||
|
||||
/* Other calls */
|
||||
void smp_send_stop(void)
|
||||
{
|
||||
smp_call_function(stop_this_cpu, NULL, 0);
|
||||
}
|
||||
|
||||
int setup_profiling_timer(unsigned int multiplier)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
/* cache_decay_ticks is used by the scheduler to decide if a process
|
||||
* is "hot" on one CPU. A higher value means a higher penalty to move
|
||||
* a process to another CPU. Our cache is rather small so we report
|
||||
* 1 tick.
|
||||
*/
|
||||
unsigned long cache_decay_ticks = 1;
|
||||
|
||||
int __cpu_up(unsigned int cpu, struct task_struct *tidle)
|
||||
{
|
||||
smp_boot_one_cpu(cpu, tidle);
|
||||
return cpu_online(cpu) ? 0 : -ENOSYS;
|
||||
}
|
||||
|
||||
void smp_send_reschedule(int cpu)
|
||||
{
|
||||
cpumask_t cpu_mask;
|
||||
cpumask_clear(&cpu_mask);
|
||||
cpumask_set_cpu(cpu, &cpu_mask);
|
||||
send_ipi(IPI_SCHEDULE, 0, cpu_mask);
|
||||
}
|
||||
|
||||
/* TLB flushing
|
||||
*
|
||||
* Flush needs to be done on the local CPU and on any other CPU that
|
||||
* may have the same mapping. The mm->cpu_vm_mask is used to keep track
|
||||
* of which CPUs that a specific process has been executed on.
|
||||
*/
|
||||
void flush_tlb_common(struct mm_struct* mm, struct vm_area_struct* vma, unsigned long addr)
|
||||
{
|
||||
unsigned long flags;
|
||||
cpumask_t cpu_mask;
|
||||
|
||||
spin_lock_irqsave(&tlbstate_lock, flags);
|
||||
cpu_mask = (mm == FLUSH_ALL ? cpu_all_mask : *mm_cpumask(mm));
|
||||
cpumask_clear_cpu(smp_processor_id(), &cpu_mask);
|
||||
flush_mm = mm;
|
||||
flush_vma = vma;
|
||||
flush_addr = addr;
|
||||
send_ipi(IPI_FLUSH_TLB, 1, cpu_mask);
|
||||
spin_unlock_irqrestore(&tlbstate_lock, flags);
|
||||
}
|
||||
|
||||
void flush_tlb_all(void)
|
||||
{
|
||||
__flush_tlb_all();
|
||||
flush_tlb_common(FLUSH_ALL, FLUSH_ALL, 0);
|
||||
}
|
||||
|
||||
void flush_tlb_mm(struct mm_struct *mm)
|
||||
{
|
||||
__flush_tlb_mm(mm);
|
||||
flush_tlb_common(mm, FLUSH_ALL, 0);
|
||||
/* No more mappings in other CPUs */
|
||||
cpumask_clear(mm_cpumask(mm));
|
||||
cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm));
|
||||
}
|
||||
|
||||
void flush_tlb_page(struct vm_area_struct *vma,
|
||||
unsigned long addr)
|
||||
{
|
||||
__flush_tlb_page(vma, addr);
|
||||
flush_tlb_common(vma->vm_mm, vma, addr);
|
||||
}
|
||||
|
||||
/* Inter processor interrupts
|
||||
*
|
||||
* The IPIs are used for:
|
||||
* * Force a schedule on a CPU
|
||||
* * FLush TLB on other CPUs
|
||||
* * Call a function on other CPUs
|
||||
*/
|
||||
|
||||
int send_ipi(int vector, int wait, cpumask_t cpu_mask)
|
||||
{
|
||||
int i = 0;
|
||||
reg_intr_vect_rw_ipi ipi = REG_RD(intr_vect, irq_regs[i], rw_ipi);
|
||||
int ret = 0;
|
||||
|
||||
/* Calculate CPUs to send to. */
|
||||
cpumask_and(&cpu_mask, &cpu_mask, cpu_online_mask);
|
||||
|
||||
/* Send the IPI. */
|
||||
for_each_cpu(i, &cpu_mask)
|
||||
{
|
||||
ipi.vector |= vector;
|
||||
REG_WR(intr_vect, irq_regs[i], rw_ipi, ipi);
|
||||
}
|
||||
|
||||
/* Wait for IPI to finish on other CPUS */
|
||||
if (wait) {
|
||||
for_each_cpu(i, &cpu_mask) {
|
||||
int j;
|
||||
for (j = 0 ; j < 1000; j++) {
|
||||
ipi = REG_RD(intr_vect, irq_regs[i], rw_ipi);
|
||||
if (!ipi.vector)
|
||||
break;
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
/* Timeout? */
|
||||
if (ipi.vector) {
|
||||
printk("SMP call timeout from %d to %d\n", smp_processor_id(), i);
|
||||
ret = -ETIMEDOUT;
|
||||
dump_stack();
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* You must not call this function with disabled interrupts or from a
|
||||
* hardware interrupt handler or from a bottom half handler.
|
||||
*/
|
||||
int smp_call_function(void (*func)(void *info), void *info, int wait)
|
||||
{
|
||||
cpumask_t cpu_mask;
|
||||
struct call_data_struct data;
|
||||
int ret;
|
||||
|
||||
cpumask_setall(&cpu_mask);
|
||||
cpumask_clear_cpu(smp_processor_id(), &cpu_mask);
|
||||
|
||||
WARN_ON(irqs_disabled());
|
||||
|
||||
data.func = func;
|
||||
data.info = info;
|
||||
data.wait = wait;
|
||||
|
||||
spin_lock(&call_lock);
|
||||
call_data = &data;
|
||||
ret = send_ipi(IPI_CALL, wait, cpu_mask);
|
||||
spin_unlock(&call_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
irqreturn_t crisv32_ipi_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
void (*func) (void *info) = call_data->func;
|
||||
void *info = call_data->info;
|
||||
reg_intr_vect_rw_ipi ipi;
|
||||
|
||||
ipi = REG_RD(intr_vect, irq_regs[smp_processor_id()], rw_ipi);
|
||||
|
||||
if (ipi.vector & IPI_SCHEDULE) {
|
||||
scheduler_ipi();
|
||||
}
|
||||
if (ipi.vector & IPI_CALL) {
|
||||
func(info);
|
||||
}
|
||||
if (ipi.vector & IPI_FLUSH_TLB) {
|
||||
if (flush_mm == FLUSH_ALL)
|
||||
__flush_tlb_all();
|
||||
else if (flush_vma == FLUSH_ALL)
|
||||
__flush_tlb_mm(flush_mm);
|
||||
else
|
||||
__flush_tlb_page(flush_vma, flush_addr);
|
||||
}
|
||||
|
||||
ipi.vector = 0;
|
||||
REG_WR(intr_vect, irq_regs[smp_processor_id()], rw_ipi, ipi);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
317
arch/cris/arch-v32/kernel/time.c
Normal file
317
arch/cris/arch-v32/kernel/time.c
Normal file
|
@ -0,0 +1,317 @@
|
|||
/*
|
||||
* linux/arch/cris/arch-v32/kernel/time.c
|
||||
*
|
||||
* Copyright (C) 2003-2010 Axis Communications AB
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/timex.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/swap.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/threads.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <asm/types.h>
|
||||
#include <asm/signal.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/delay.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/irq_regs.h>
|
||||
|
||||
#include <hwregs/reg_map.h>
|
||||
#include <hwregs/reg_rdwr.h>
|
||||
#include <hwregs/timer_defs.h>
|
||||
#include <hwregs/intr_vect_defs.h>
|
||||
#ifdef CONFIG_CRIS_MACH_ARTPEC3
|
||||
#include <hwregs/clkgen_defs.h>
|
||||
#endif
|
||||
|
||||
/* Watchdog defines */
|
||||
#define ETRAX_WD_KEY_MASK 0x7F /* key is 7 bit */
|
||||
#define ETRAX_WD_HZ 763 /* watchdog counts at 763 Hz */
|
||||
/* Number of 763 counts before watchdog bites */
|
||||
#define ETRAX_WD_CNT ((2*ETRAX_WD_HZ)/HZ + 1)
|
||||
|
||||
/* Register the continuos readonly timer available in FS and ARTPEC-3. */
|
||||
static cycle_t read_cont_rotime(struct clocksource *cs)
|
||||
{
|
||||
return (u32)REG_RD(timer, regi_timer0, r_time);
|
||||
}
|
||||
|
||||
static struct clocksource cont_rotime = {
|
||||
.name = "crisv32_rotime",
|
||||
.rating = 300,
|
||||
.read = read_cont_rotime,
|
||||
.mask = CLOCKSOURCE_MASK(32),
|
||||
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
||||
};
|
||||
|
||||
static int __init etrax_init_cont_rotime(void)
|
||||
{
|
||||
clocksource_register_khz(&cont_rotime, 100000);
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(etrax_init_cont_rotime);
|
||||
|
||||
|
||||
unsigned long timer_regs[NR_CPUS] =
|
||||
{
|
||||
regi_timer0,
|
||||
#ifdef CONFIG_SMP
|
||||
regi_timer2
|
||||
#endif
|
||||
};
|
||||
|
||||
extern int set_rtc_mmss(unsigned long nowtime);
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
static int
|
||||
cris_time_freq_notifier(struct notifier_block *nb, unsigned long val,
|
||||
void *data);
|
||||
|
||||
static struct notifier_block cris_time_freq_notifier_block = {
|
||||
.notifier_call = cris_time_freq_notifier,
|
||||
};
|
||||
#endif
|
||||
|
||||
unsigned long get_ns_in_jiffie(void)
|
||||
{
|
||||
reg_timer_r_tmr0_data data;
|
||||
unsigned long ns;
|
||||
|
||||
data = REG_RD(timer, regi_timer0, r_tmr0_data);
|
||||
ns = (TIMER0_DIV - data) * 10;
|
||||
return ns;
|
||||
}
|
||||
|
||||
|
||||
/* From timer MDS describing the hardware watchdog:
|
||||
* 4.3.1 Watchdog Operation
|
||||
* The watchdog timer is an 8-bit timer with a configurable start value.
|
||||
* Once started the watchdog counts downwards with a frequency of 763 Hz
|
||||
* (100/131072 MHz). When the watchdog counts down to 1, it generates an
|
||||
* NMI (Non Maskable Interrupt), and when it counts down to 0, it resets the
|
||||
* chip.
|
||||
*/
|
||||
/* This gives us 1.3 ms to do something useful when the NMI comes */
|
||||
|
||||
/* Right now, starting the watchdog is the same as resetting it */
|
||||
#define start_watchdog reset_watchdog
|
||||
|
||||
#if defined(CONFIG_ETRAX_WATCHDOG)
|
||||
static short int watchdog_key = 42; /* arbitrary 7 bit number */
|
||||
#endif
|
||||
|
||||
/* Number of pages to consider "out of memory". It is normal that the memory
|
||||
* is used though, so set this really low. */
|
||||
#define WATCHDOG_MIN_FREE_PAGES 8
|
||||
|
||||
void reset_watchdog(void)
|
||||
{
|
||||
#if defined(CONFIG_ETRAX_WATCHDOG)
|
||||
reg_timer_rw_wd_ctrl wd_ctrl = { 0 };
|
||||
|
||||
/* Only keep watchdog happy as long as we have memory left! */
|
||||
if(nr_free_pages() > WATCHDOG_MIN_FREE_PAGES) {
|
||||
/* Reset the watchdog with the inverse of the old key */
|
||||
/* Invert key, which is 7 bits */
|
||||
watchdog_key ^= ETRAX_WD_KEY_MASK;
|
||||
wd_ctrl.cnt = ETRAX_WD_CNT;
|
||||
wd_ctrl.cmd = regk_timer_start;
|
||||
wd_ctrl.key = watchdog_key;
|
||||
REG_WR(timer, regi_timer0, rw_wd_ctrl, wd_ctrl);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* stop the watchdog - we still need the correct key */
|
||||
|
||||
void stop_watchdog(void)
|
||||
{
|
||||
#if defined(CONFIG_ETRAX_WATCHDOG)
|
||||
reg_timer_rw_wd_ctrl wd_ctrl = { 0 };
|
||||
watchdog_key ^= ETRAX_WD_KEY_MASK; /* invert key, which is 7 bits */
|
||||
wd_ctrl.cnt = ETRAX_WD_CNT;
|
||||
wd_ctrl.cmd = regk_timer_stop;
|
||||
wd_ctrl.key = watchdog_key;
|
||||
REG_WR(timer, regi_timer0, rw_wd_ctrl, wd_ctrl);
|
||||
#endif
|
||||
}
|
||||
|
||||
extern void show_registers(struct pt_regs *regs);
|
||||
|
||||
void handle_watchdog_bite(struct pt_regs *regs)
|
||||
{
|
||||
#if defined(CONFIG_ETRAX_WATCHDOG)
|
||||
extern int cause_of_death;
|
||||
|
||||
oops_in_progress = 1;
|
||||
printk(KERN_WARNING "Watchdog bite\n");
|
||||
|
||||
/* Check if forced restart or unexpected watchdog */
|
||||
if (cause_of_death == 0xbedead) {
|
||||
#ifdef CONFIG_CRIS_MACH_ARTPEC3
|
||||
/* There is a bug in Artpec-3 (voodoo TR 78) that requires
|
||||
* us to go to lower frequency for the reset to be reliable
|
||||
*/
|
||||
reg_clkgen_rw_clk_ctrl ctrl =
|
||||
REG_RD(clkgen, regi_clkgen, rw_clk_ctrl);
|
||||
ctrl.pll = 0;
|
||||
REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, ctrl);
|
||||
#endif
|
||||
while(1);
|
||||
}
|
||||
|
||||
/* Unexpected watchdog, stop the watchdog and dump registers. */
|
||||
stop_watchdog();
|
||||
printk(KERN_WARNING "Oops: bitten by watchdog\n");
|
||||
show_registers(regs);
|
||||
oops_in_progress = 0;
|
||||
#ifndef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
|
||||
reset_watchdog();
|
||||
#endif
|
||||
while(1) /* nothing */;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* timer_interrupt() needs to keep up the real-time clock,
|
||||
* as well as call the "xtime_update()" routine every clocktick.
|
||||
*/
|
||||
extern void cris_do_profile(struct pt_regs *regs);
|
||||
|
||||
static inline irqreturn_t timer_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct pt_regs *regs = get_irq_regs();
|
||||
int cpu = smp_processor_id();
|
||||
reg_timer_r_masked_intr masked_intr;
|
||||
reg_timer_rw_ack_intr ack_intr = { 0 };
|
||||
|
||||
/* Check if the timer interrupt is for us (a tmr0 int) */
|
||||
masked_intr = REG_RD(timer, timer_regs[cpu], r_masked_intr);
|
||||
if (!masked_intr.tmr0)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* Acknowledge the timer irq. */
|
||||
ack_intr.tmr0 = 1;
|
||||
REG_WR(timer, timer_regs[cpu], rw_ack_intr, ack_intr);
|
||||
|
||||
/* Reset watchdog otherwise it resets us! */
|
||||
reset_watchdog();
|
||||
|
||||
/* Update statistics. */
|
||||
update_process_times(user_mode(regs));
|
||||
|
||||
cris_do_profile(regs); /* Save profiling information */
|
||||
|
||||
/* The master CPU is responsible for the time keeping. */
|
||||
if (cpu != 0)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
/* Call the real timer interrupt handler */
|
||||
xtime_update(1);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Timer is IRQF_SHARED so drivers can add stuff to the timer irq chain. */
|
||||
static struct irqaction irq_timer = {
|
||||
.handler = timer_interrupt,
|
||||
.flags = IRQF_SHARED,
|
||||
.name = "timer"
|
||||
};
|
||||
|
||||
void __init cris_timer_init(void)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
reg_timer_rw_tmr0_ctrl tmr0_ctrl = { 0 };
|
||||
reg_timer_rw_tmr0_div tmr0_div = TIMER0_DIV;
|
||||
reg_timer_rw_intr_mask timer_intr_mask;
|
||||
|
||||
/* Setup the etrax timers.
|
||||
* Base frequency is 100MHz, divider 1000000 -> 100 HZ
|
||||
* We use timer0, so timer1 is free.
|
||||
* The trig timer is used by the fasttimer API if enabled.
|
||||
*/
|
||||
|
||||
tmr0_ctrl.op = regk_timer_ld;
|
||||
tmr0_ctrl.freq = regk_timer_f100;
|
||||
REG_WR(timer, timer_regs[cpu], rw_tmr0_div, tmr0_div);
|
||||
REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Load */
|
||||
tmr0_ctrl.op = regk_timer_run;
|
||||
REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Start */
|
||||
|
||||
/* Enable the timer irq. */
|
||||
timer_intr_mask = REG_RD(timer, timer_regs[cpu], rw_intr_mask);
|
||||
timer_intr_mask.tmr0 = 1;
|
||||
REG_WR(timer, timer_regs[cpu], rw_intr_mask, timer_intr_mask);
|
||||
}
|
||||
|
||||
void __init time_init(void)
|
||||
{
|
||||
reg_intr_vect_rw_mask intr_mask;
|
||||
|
||||
/* Probe for the RTC and read it if it exists.
|
||||
* Before the RTC can be probed the loops_per_usec variable needs
|
||||
* to be initialized to make usleep work. A better value for
|
||||
* loops_per_usec is calculated by the kernel later once the
|
||||
* clock has started.
|
||||
*/
|
||||
loops_per_usec = 50;
|
||||
|
||||
/* Start CPU local timer. */
|
||||
cris_timer_init();
|
||||
|
||||
/* Enable the timer irq in global config. */
|
||||
intr_mask = REG_RD_VECT(intr_vect, regi_irq, rw_mask, 1);
|
||||
intr_mask.timer0 = 1;
|
||||
REG_WR_VECT(intr_vect, regi_irq, rw_mask, 1, intr_mask);
|
||||
|
||||
/* Now actually register the timer irq handler that calls
|
||||
* timer_interrupt(). */
|
||||
setup_irq(TIMER0_INTR_VECT, &irq_timer);
|
||||
|
||||
/* Enable watchdog if we should use one. */
|
||||
|
||||
#if defined(CONFIG_ETRAX_WATCHDOG)
|
||||
printk(KERN_INFO "Enabling watchdog...\n");
|
||||
start_watchdog();
|
||||
|
||||
/* If we use the hardware watchdog, we want to trap it as an NMI
|
||||
* and dump registers before it resets us. For this to happen, we
|
||||
* must set the "m" NMI enable flag (which once set, is unset only
|
||||
* when an NMI is taken). */
|
||||
{
|
||||
unsigned long flags;
|
||||
local_save_flags(flags);
|
||||
flags |= (1<<30); /* NMI M flag is at bit 30 */
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
cpufreq_register_notifier(&cris_time_freq_notifier_block,
|
||||
CPUFREQ_TRANSITION_NOTIFIER);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
static int
|
||||
cris_time_freq_notifier(struct notifier_block *nb, unsigned long val,
|
||||
void *data)
|
||||
{
|
||||
struct cpufreq_freqs *freqs = data;
|
||||
if (val == CPUFREQ_POSTCHANGE) {
|
||||
reg_timer_r_tmr0_data data;
|
||||
reg_timer_rw_tmr0_div div = (freqs->new * 500) / HZ;
|
||||
do {
|
||||
data = REG_RD(timer, timer_regs[freqs->cpu],
|
||||
r_tmr0_data);
|
||||
} while (data > 20);
|
||||
REG_WR(timer, timer_regs[freqs->cpu], rw_tmr0_div, div);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
193
arch/cris/arch-v32/kernel/traps.c
Normal file
193
arch/cris/arch-v32/kernel/traps.c
Normal file
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2006, Axis Communications AB.
|
||||
*/
|
||||
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/module.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <hwregs/supp_reg.h>
|
||||
#include <hwregs/intr_vect_defs.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
void show_registers(struct pt_regs *regs)
|
||||
{
|
||||
/*
|
||||
* It's possible to use either the USP register or current->thread.usp.
|
||||
* USP might not correspond to the current process for all cases this
|
||||
* function is called, and current->thread.usp isn't up to date for the
|
||||
* current process. Experience shows that using USP is the way to go.
|
||||
*/
|
||||
unsigned long usp = rdusp();
|
||||
unsigned long d_mmu_cause;
|
||||
unsigned long i_mmu_cause;
|
||||
|
||||
printk("CPU: %d\n", smp_processor_id());
|
||||
|
||||
printk("ERP: %08lx SRP: %08lx CCS: %08lx USP: %08lx MOF: %08lx\n",
|
||||
regs->erp, regs->srp, regs->ccs, usp, regs->mof);
|
||||
|
||||
printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n",
|
||||
regs->r0, regs->r1, regs->r2, regs->r3);
|
||||
|
||||
printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n",
|
||||
regs->r4, regs->r5, regs->r6, regs->r7);
|
||||
|
||||
printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n",
|
||||
regs->r8, regs->r9, regs->r10, regs->r11);
|
||||
|
||||
printk("r12: %08lx r13: %08lx oR10: %08lx acr: %08lx\n",
|
||||
regs->r12, regs->r13, regs->orig_r10, regs->acr);
|
||||
|
||||
printk(" sp: %08lx\n", (unsigned long)regs);
|
||||
|
||||
SUPP_BANK_SEL(BANK_IM);
|
||||
SUPP_REG_RD(RW_MM_CAUSE, i_mmu_cause);
|
||||
|
||||
SUPP_BANK_SEL(BANK_DM);
|
||||
SUPP_REG_RD(RW_MM_CAUSE, d_mmu_cause);
|
||||
|
||||
printk(" Data MMU Cause: %08lx\n", d_mmu_cause);
|
||||
printk("Instruction MMU Cause: %08lx\n", i_mmu_cause);
|
||||
|
||||
printk("Process %s (pid: %d, stackpage=%08lx)\n",
|
||||
current->comm, current->pid, (unsigned long)current);
|
||||
|
||||
/*
|
||||
* When in-kernel, we also print out the stack and code at the
|
||||
* time of the fault..
|
||||
*/
|
||||
if (!user_mode(regs)) {
|
||||
int i;
|
||||
|
||||
show_stack(NULL, (unsigned long *)usp);
|
||||
|
||||
/*
|
||||
* If the previous stack-dump wasn't a kernel one, dump the
|
||||
* kernel stack now.
|
||||
*/
|
||||
if (usp != 0)
|
||||
show_stack(NULL, NULL);
|
||||
|
||||
printk("\nCode: ");
|
||||
|
||||
if (regs->erp < PAGE_OFFSET)
|
||||
goto bad_value;
|
||||
|
||||
/*
|
||||
* Quite often the value at regs->erp doesn't point to the
|
||||
* interesting instruction, which often is the previous
|
||||
* instruction. So dump at an offset large enough that the
|
||||
* instruction decoding should be in sync at the interesting
|
||||
* point, but small enough to fit on a row. The regs->erp
|
||||
* location is pointed out in a ksymoops-friendly way by
|
||||
* wrapping the byte for that address in parenthesises.
|
||||
*/
|
||||
for (i = -12; i < 12; i++) {
|
||||
unsigned char c;
|
||||
|
||||
if (__get_user(c, &((unsigned char *)regs->erp)[i])) {
|
||||
bad_value:
|
||||
printk(" Bad IP value.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
printk("(%02x) ", c);
|
||||
else
|
||||
printk("%02x ", c);
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void arch_enable_nmi(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
local_save_flags(flags);
|
||||
flags |= (1 << 30); /* NMI M flag is at bit 30 */
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
extern void (*nmi_handler)(struct pt_regs *);
|
||||
void handle_nmi(struct pt_regs *regs)
|
||||
{
|
||||
#ifdef CONFIG_ETRAXFS
|
||||
reg_intr_vect_r_nmi r;
|
||||
#endif
|
||||
|
||||
if (nmi_handler)
|
||||
nmi_handler(regs);
|
||||
|
||||
#ifdef CONFIG_ETRAXFS
|
||||
/* Wait until nmi is no longer active. */
|
||||
do {
|
||||
r = REG_RD(intr_vect, regi_irq, r_nmi);
|
||||
} while (r.ext == regk_intr_vect_on);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_BUG
|
||||
extern void die_if_kernel(const char *str, struct pt_regs *regs, long err);
|
||||
|
||||
/* Copy of the regs at BUG() time. */
|
||||
struct pt_regs BUG_regs;
|
||||
|
||||
void do_BUG(char *file, unsigned int line)
|
||||
{
|
||||
printk("kernel BUG at %s:%d!\n", file, line);
|
||||
die_if_kernel("Oops", &BUG_regs, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(do_BUG);
|
||||
|
||||
void fixup_BUG(struct pt_regs *regs)
|
||||
{
|
||||
BUG_regs = *regs;
|
||||
|
||||
#ifdef CONFIG_DEBUG_BUGVERBOSE
|
||||
/*
|
||||
* Fixup the BUG arguments through exception handlers.
|
||||
*/
|
||||
{
|
||||
const struct exception_table_entry *fixup;
|
||||
|
||||
/*
|
||||
* ERP points at the "break 14" + 2, compensate for the 2
|
||||
* bytes.
|
||||
*/
|
||||
fixup = search_exception_tables(instruction_pointer(regs) - 2);
|
||||
if (fixup) {
|
||||
/* Adjust the instruction pointer in the stackframe. */
|
||||
instruction_pointer(regs) = fixup->fixup;
|
||||
arch_fixup(regs);
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* Dont try to lookup the filename + line, just dump regs. */
|
||||
do_BUG("unknown", 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Break 14 handler. Save regs and jump into the fixup_BUG.
|
||||
*/
|
||||
__asm__ ( ".text\n\t"
|
||||
".global breakh_BUG\n\t"
|
||||
"breakh_BUG:\n\t"
|
||||
SAVE_ALL
|
||||
KGDB_FIXUP
|
||||
"move.d $sp, $r10\n\t"
|
||||
"jsr fixup_BUG\n\t"
|
||||
"nop\n\t"
|
||||
"jump ret_from_intr\n\t"
|
||||
"nop\n\t");
|
||||
|
||||
|
||||
#ifdef CONFIG_DEBUG_BUGVERBOSE
|
||||
void
|
||||
handle_BUG(struct pt_regs *regs)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue