diff options
Diffstat (limited to 'arch/cris/kernel')
-rw-r--r-- | arch/cris/kernel/Makefile | 25 | ||||
-rw-r--r-- | arch/cris/kernel/debugport.c | 242 | ||||
-rw-r--r-- | arch/cris/kernel/entry.S | 738 | ||||
-rw-r--r-- | arch/cris/kernel/head.S | 519 | ||||
-rw-r--r-- | arch/cris/kernel/hexify.c | 31 | ||||
-rw-r--r-- | arch/cris/kernel/irq.c | 467 | ||||
-rw-r--r-- | arch/cris/kernel/kgdb.c | 1540 | ||||
-rw-r--r-- | arch/cris/kernel/ksyms.c | 2 | ||||
-rw-r--r-- | arch/cris/kernel/process.c | 327 | ||||
-rw-r--r-- | arch/cris/kernel/ptrace.c | 340 | ||||
-rw-r--r-- | arch/cris/kernel/semaphore.c | 238 | ||||
-rw-r--r-- | arch/cris/kernel/setup.c | 264 | ||||
-rw-r--r-- | arch/cris/kernel/shadows.c | 20 | ||||
-rw-r--r-- | arch/cris/kernel/signal.c | 667 | ||||
-rw-r--r-- | arch/cris/kernel/sys_cris.c | 201 | ||||
-rw-r--r-- | arch/cris/kernel/time.c | 453 | ||||
-rw-r--r-- | arch/cris/kernel/traps.c | 167 |
17 files changed, 6241 insertions, 0 deletions
diff --git a/arch/cris/kernel/Makefile b/arch/cris/kernel/Makefile new file mode 100644 index 000000000..0681e4807 --- /dev/null +++ b/arch/cris/kernel/Makefile @@ -0,0 +1,25 @@ +# $Id: Makefile,v 1.3 2001/01/10 21:11:07 bjornw Exp $ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +.S.o: + $(CC) $(AFLAGS) -traditional -c $< -o $*.o + +all: kernel.o head.o + +O_TARGET := kernel.o +obj-y := process.o signal.o entry.o traps.o irq.o \ + ptrace.o setup.o time.o sys_cris.o shadows.o \ + debugport.o semaphore.o + +obj-$(CONFIG_KGDB) += kgdb.o + +clean: + +include $(TOPDIR)/Rules.make diff --git a/arch/cris/kernel/debugport.c b/arch/cris/kernel/debugport.c new file mode 100644 index 000000000..a2b29be95 --- /dev/null +++ b/arch/cris/kernel/debugport.c @@ -0,0 +1,242 @@ +/* Serialport functions for debugging + * + * Copyright (c) 2000 Axis Communications AB + * + * Authors: Bjorn Wesen + * + * Exports: + * console_print_etrax(char *buf) + * int getDebugChar() + * putDebugChar(int) + * enableDebugIRQ() + * init_etrax_debug() + * + * $Log: debugport.c,v $ + * Revision 1.4 2000/10/06 12:37:26 bjornw + * Use physical addresses when talking to DMA + * + * + */ + +#include <linux/config.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/major.h> + +#include <asm/system.h> +#include <asm/svinto.h> +#include <asm/io.h> /* Get SIMCOUT. */ + +/* Which serial-port is our debug port ? */ + +#if defined(CONFIG_DEBUG_PORT0) || defined(CONFIG_DEBUG_PORT_NULL) +#define DEBUG_PORT_IDX 0 +#define DEBUG_OCMD R_DMA_CH6_CMD +#define DEBUG_FIRST R_DMA_CH6_FIRST +#define DEBUG_OCLRINT R_DMA_CH6_CLR_INTR +#define DEBUG_STATUS R_DMA_CH6_STATUS +#define DEBUG_READ R_SERIAL0_READ +#define DEBUG_WRITE R_SERIAL0_TR_DATA +#define DEBUG_TR_CTRL R_SERIAL0_TR_CTRL +#define DEBUG_REC_CTRL R_SERIAL0_REC_CTRL +#define DEBUG_IRQ IO_STATE(R_IRQ_MASK1_SET, ser0_data, set) +#define DEBUG_DMA_IRQ_CLR IO_STATE(R_IRQ_MASK2_CLR, dma6_descr, clr) +#endif + +#ifdef CONFIG_DEBUG_PORT1 +#define DEBUG_PORT_IDX 1 +#define DEBUG_OCMD R_DMA_CH8_CMD +#define DEBUG_FIRST R_DMA_CH8_FIRST +#define DEBUG_OCLRINT R_DMA_CH8_CLR_INTR +#define DEBUG_STATUS R_DMA_CH8_STATUS +#define DEBUG_READ R_SERIAL1_READ +#define DEBUG_WRITE R_SERIAL1_TR_DATA +#define DEBUG_TR_CTRL R_SERIAL1_TR_CTRL +#define DEBUG_REC_CTRL R_SERIAL1_REC_CTRL +#define DEBUG_IRQ IO_STATE(R_IRQ_MASK1_SET, ser1_data, set) +#define DEBUG_DMA_IRQ_CLR IO_STATE(R_IRQ_MASK2_CLR, dma8_descr, clr) +#endif + +#ifdef CONFIG_DEBUG_PORT2 +#define DEBUG_PORT_IDX 2 +#define DEBUG_OCMD R_DMA_CH2_CMD +#define DEBUG_FIRST R_DMA_CH2_FIRST +#define DEBUG_OCLRINT R_DMA_CH2_CLR_INTR +#define DEBUG_STATUS R_DMA_CH2_STATUS +#define DEBUG_READ R_SERIAL2_READ +#define DEBUG_WRITE R_SERIAL2_TR_DATA +#define DEBUG_TR_CTRL R_SERIAL2_TR_CTRL +#define DEBUG_REC_CTRL R_SERIAL2_REC_CTRL +#define DEBUG_IRQ IO_STATE(R_IRQ_MASK1_SET, ser2_data, set) +#define DEBUG_DMA_IRQ_CLR IO_STATE(R_IRQ_MASK2_CLR, dma2_descr, clr) +#endif + +#ifdef CONFIG_DEBUG_PORT3 +#define DEBUG_PORT_IDX 3 +#define DEBUG_OCMD R_DMA_CH4_CMD +#define DEBUG_FIRST R_DMA_CH4_FIRST +#define DEBUG_OCLRINT R_DMA_CH4_CLR_INTR +#define DEBUG_STATUS R_DMA_CH4_STATUS +#define DEBUG_READ R_SERIAL3_READ +#define DEBUG_WRITE R_SERIAL3_TR_DATA +#define DEBUG_TR_CTRL R_SERIAL3_TR_CTRL +#define DEBUG_REC_CTRL R_SERIAL3_REC_CTRL +#define DEBUG_IRQ IO_STATE(R_IRQ_MASK1_SET, ser3_data, set) +#define DEBUG_DMA_IRQ_CLR IO_STATE(R_IRQ_MASK2_CLR, dma4_descr, clr) +#endif + +/* Write a string of count length to the console (debug port) using DMA, polled + * for completion. Interrupts are disabled during the whole process. Some + * caution needs to be taken to not interfere with ttyS business on this port. + */ + +static void +console_write(struct console *co, const char *buf, unsigned int len) +{ + static struct etrax_dma_descr descr; + unsigned long flags; + int in_progress; + +#ifdef CONFIG_DEBUG_PORT_NULL + /* no debug printout at all */ + return; +#endif + +#ifdef CONFIG_SVINTO_SIM + /* no use to simulate the serial debug output */ + SIMCOUT(buf,len); + return; +#endif + + save_flags(flags); + cli(); + +#ifdef CONFIG_KGDB + /* kgdb needs to output debug info using the gdb protocol */ + putDebugString(buf, len); + restore_flags(flags); + return; +#endif + + /* make sure the transmitter is enabled. + * NOTE: this overrides any setting done in ttySx, to 8N1, no auto-CTS. + * in the future, move the tr/rec_ctrl shadows from etrax100ser.c to + * shadows.c and use it here as well... + */ + + *DEBUG_TR_CTRL = 0x40; + + /* if the tty has some ongoing business, remember it */ + + in_progress = *DEBUG_OCMD & 7; + + if(in_progress) { + /* wait until the output dma channel is ready */ + + while(*DEBUG_OCMD & 7) /* nothing */ ; + } + + descr.ctrl = d_eol; + descr.sw_len = len; + descr.buf = __pa(buf); + + *DEBUG_FIRST = __pa(&descr); /* write to R_DMAx_FIRST */ + *DEBUG_OCMD = 1; /* dma command start -> R_DMAx_CMD */ + + /* wait until the output dma channel is ready again */ + + while(*DEBUG_OCMD & 7) /* nothing */; + + /* clear pending interrupts so we don't get a surprise below */ + + if(in_progress) + *DEBUG_OCLRINT = 2; /* only clear EOP, leave DESCR for the tty */ + else + *DEBUG_OCLRINT = 3; /* clear both EOP and DESCR */ + + while(*DEBUG_STATUS & 0x7f); /* wait until output FIFO is empty as well */ + + restore_flags(flags); +} + +/* legacy function */ + +void +console_print_etrax(const char *buf) +{ + console_write(NULL, buf, strlen(buf)); +} + +/* Use polling to get a single character FROM the debug port */ + +int +getDebugChar(void) +{ + unsigned long readval; + + do { + readval = *DEBUG_READ; + } while(!(readval & IO_MASK(R_SERIAL0_READ, data_avail))); + + return (readval & IO_MASK(R_SERIAL0_READ, data_in)); +} + +/* Use polling to put a single character to the debug port */ + +void +putDebugChar(int val) +{ + while(!(*DEBUG_READ & IO_MASK(R_SERIAL0_READ, tr_ready))) ; +; + *DEBUG_WRITE = val; +} + +/* Enable irq for receiving chars on the debug port, used by kgdb */ + +void +enableDebugIRQ(void) +{ + *R_IRQ_MASK1_SET = DEBUG_IRQ; + /* use R_VECT_MASK directly, since we really bypass Linux normal + * IRQ handling in kgdb anyway, we don't need to use enable_irq + */ + *R_VECT_MASK_SET = IO_STATE(R_VECT_MASK_SET, serial, set); + + *DEBUG_REC_CTRL = IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable); +} + +static kdev_t +console_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, 64 + c->index); +} + +static int __init +console_setup(struct console *co, char *options) +{ + return 0; +} + +static struct console sercons = { + "ttyS", + console_write, + NULL, + console_device, + NULL, + NULL, + console_setup, + CON_PRINTBUFFER, + DEBUG_PORT_IDX, + 0, + NULL +}; + +/* + * Register console (for printk's etc) + */ + +void __init +init_etrax_debug(void) +{ + register_console(&sercons); +} diff --git a/arch/cris/kernel/entry.S b/arch/cris/kernel/entry.S new file mode 100644 index 000000000..1af62eb2c --- /dev/null +++ b/arch/cris/kernel/entry.S @@ -0,0 +1,738 @@ +/* $Id: entry.S,v 1.11 2001/01/10 21:13:29 bjornw Exp $ + * + * linux/arch/cris/entry.S + * + * Copyright (C) 2000 Axis Communications AB + * + * Authors: Bjorn Wesen (bjornw@axis.com) + * + * $Log: entry.S,v $ + * Revision 1.11 2001/01/10 21:13:29 bjornw + * SYMBOL_NAME is defined incorrectly for the compiler options we currently use + * + * Revision 1.10 2000/12/18 23:47:56 bjornw + * * Added syscall trace support (ptrace), completely untested of course + * * Removed redundant check for NULL entries in syscall_table + * + * Revision 1.9 2000/11/21 16:40:51 bjornw + * * New frame type used when an SBFS frame needs to be popped without + * actually restarting the instruction + * * Enable interrupts in signal_return (they did so in x86, I hope it's a good + * idea) + * + * Revision 1.8 2000/11/17 16:53:35 bjornw + * Added detection of frame-type in Rexit, so that mmu_bus_fault can + * use ret_from_intr in the return-path to check for signals (like SEGV) + * and other foul things that might have occured during the fault. + * + * Revision 1.7 2000/10/06 15:04:28 bjornw + * Include mof in register savings + * + * Revision 1.6 2000/09/12 16:02:44 bjornw + * Linux-2.4.0-test7 derived updates + * + * Revision 1.5 2000/08/17 15:35:15 bjornw + * 2.4.0-test6 changed local_irq_count and friends API + * + * Revision 1.4 2000/08/02 13:59:30 bjornw + * Removed olduname and uname from the syscall list + * + * Revision 1.3 2000/07/31 13:32:58 bjornw + * * Export ret_from_intr + * * _resume updated (prev/last tjohejsan) + * * timer_interrupt obsolete + * * SIGSEGV detection in mmu_bus_fault temporarily disabled + * + * + */ + +/* + * entry.S contains 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> + + ;; functions exported from this file + + .globl _system_call + .globl _ret_from_intr + .globl _ret_from_sys_call + .globl _resume + .globl _multiple_interrupt + .globl _hwbreakpoint + .globl _IRQ1_interrupt + .globl _timer_interrupt + .globl _timer_shortcut + .globl _spurious_interrupt + .globl _hw_bp_trigs + .globl _mmu_bus_fault + + .globl _sys_call_table + + ;; syscall error codes + +LENOSYS = 38 + + ;; offsets into the task_struct (found at sp aligned to THREAD_SIZE, 8192) + ;; linux/sched.h + +LTASK_SIGPENDING = 8 +LTASK_NEEDRESCHED = 20 +LTASK_PTRACE = 24 + + ;; some pt_regs offsets (from ptrace.h) + +LORIG_R10 = 4 +LR13 = 8 +LR12 = 12 +LR11 = 16 +LR10 = 20 +LR1 = 56 +LR0 = 60 +LDCCR = 68 +LSRP = 72 +LIRP = 76 + + ;; below are various parts of system_call which are not in the fast-path + + ;; handle software irqs + +handle_softirq: + push r9 + jsr _do_softirq ; call the C routine for softirq handling + pop r9 + + ;; fall-through + +_ret_from_intr: + ;; check for resched only if we're going back to user-mode + + move ccr, r0 + btstq 8, r0 ; U-flag + bpl Rexit ; go back directly + nop + ba ret_with_reschedule ; go back but check schedule and signals first + nop + +reschedule: + ;; keep r9 intact + push r9 + jsr _schedule + pop r9 + ba _ret_from_sys_call + nop + + ;; return but call do_signal first +signal_return: + ei ; we can get here from an interrupt + move.d r9,r10 ; do_signals syscall/irq param + moveq 0,r11 ; oldset param - 0 in this case + move.d sp,r12 ; another argument to do_signal (the regs param) + jsr _do_signal ; arch/cris/kernel/signal.c + ba Rexit + nop + + ;; The system_call is called by a BREAK instruction, which works like + ;; an interrupt call but it stores the return PC in BRP instead of IRP. + ;; Since we dont really want to have two epilogues (one for system calls + ;; and one for interrupts) we push the contents of BRP instead of IRP in the + ;; system call prologue, to make it look like an ordinary interrupt on the + ;; stackframe. + ;; + ;; Since we can't have system calls inside interrupts, it should not matter + ;; that we don't stack IRP. + ;; + ;; In r1 we have the wanted syscall number. Arguments come in r10,r11,r12,r13,r0 + ;; + ;; 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.. + +_system_call: + ;; stack-frame similar to the irq heads, which is reversed in ret_from_sys_call + push brp ; this is normally push irp + push srp + push dccr + push mof + subq 14*4,sp ; make room for r0-r13 + movem r13,[sp] ; push r0-r13 + push r10 ; push orig_r10 + clear.d [sp=sp-4] ; frametype == 0, normal stackframe + + move.d r10,r2 ; save for later + + movs.w -LENOSYS,r10 + move.d r10,[sp+LR10] ; put the default return value in r10 in the frame + + move.d sp,r10 + jsr _set_esp0 ; save top of frame (clobbers r9...) + + ;; check if this process is syscall-traced + + move.d sp, r10 + and.d -8192, r10 ; THREAD_SIZE == 8192 + move.d [r10+LTASK_PTRACE],r10 + btstq 2, r10 ; PT_TRACESYS + bmi tracesys + nop + + ;; check for sanity in the requested syscall number + + cmpu.w NR_syscalls,r1 + bcc _ret_from_sys_call + lslq 2,r1 ; multiply by 4, in the delay slot + + ;; read the system call vector into r1 + + move.d [r1+_sys_call_table],r1 + + ;; the parameter carrying registers r11, r12 and 13 are intact - restore r10. + ;; the fifth parameter (if any) was in r0, and we need to put it on the stack + + push r0 + move.d r2,r10 + + jsr r1 ; actually call the corresponding system call + addq 4,sp ; pop the r0 parameter + move.d r10,[sp+LR10] ; 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 - if 1, we came from a syscall, if 0, from an irq + + ;; check if any bottom halves need service + + move.d [_irq_stat],r0 ; softirq_active + and.d [_irq_stat+4],r0 ; softirq_mask + bne handle_softirq + nop + +ret_with_reschedule: + ;; first get the current task-struct pointer (see top for defs) + + move.d sp, r0 + and.d -8192, r0 ; THREAD_SIZE == 8192 + + ;; see if we want to reschedule into another process + + test.d [r0+LTASK_NEEDRESCHED] + bne reschedule + nop + + ;; see if we need to run signal checks (important that r9 is intact here) + + test.d [r0+LTASK_SIGPENDING] + bne signal_return + nop + +Rexit: + ;; this epilogue MUST match the prologues in multiple_interrupt, irq.h and ptregs.h + pop r10 ; frametype + bne RBFexit ; was not CRIS_FRAME_NORMAL, handle otherwise + addq 4,sp ; skip orig_r10, in delayslot + movem [sp+],r13 ; registers r0-r13 + pop mof ; multiply overflow register + pop dccr ; condition codes + pop srp ; subroutine return pointer + jmpu [sp+] ; return by popping irp and jumping there + ;; jmpu takes the U-flag into account to see if we return to + ;; user-mode or kernel mode. + +RBFexit: + cmpq 2, r10 ; was it CRIS_FRAME_FIXUP ? + beq 2f + movem [sp+],r13 ; registers r0-r13, in delay slot + pop mof ; multiply overflow register + pop dccr ; condition codes + pop srp ; subroutine return pointer + rbf [sp+] ; return by popping the CPU status + +2: pop mof ; multiply overflow register + pop dccr ; condition codes + pop srp ; subroutine return pointer + ;; now we have a 4-word SBFS frame which we do not want to restore + ;; using RBF since we have made a fixup. instead we would like to + ;; just get the PC value to restart it with, and skip the rest of + ;; the frame. + pop irp ; fixup location will be here + pop p8 ; null pop + pop p8 ; null pop + reti ; return to IRP, taking U-flag into account + pop p8 ; null pop in delayslot + + +tracesys: + ;; this first invocation of syscall_trace _requires_ that + ;; LR10 in the frame contains -LENOSYS (as is set in the beginning + ;; of system_call + + jsr _syscall_trace + + ;; now we should more or less do the same things as in the system_call + ;; but since our argument regs got clobbered during syscall_trace and + ;; because syscall_trace might want to alter them, we need to reload them + ;; from the stack-frame as we use them. + + ;; check for sanity in the requested syscall number + + move.d [sp+LR1], r1 + movs.w -LENOSYS, r10 + cmpu.w NR_syscalls,r1 + bcc 1f + lslq 2,r1 ; multiply by 4, in the delay slot + + ;; read the system call vector entry into r1 + + move.d [r1+_sys_call_table],r1 + + ;; restore r10, r11, r12, r13 and r0 into the needed registers + + move.d [sp+LORIG_R10], r10 ; LR10 is already filled with -LENOSYS + move.d [sp+LR11], r11 + move.d [sp+LR12], r12 + move.d [sp+LR13], r13 + move.d [sp+LR0], r0 + + ;; the fifth parameter needs to be put on the stack for the system + ;; call to find it + + push r0 + jsr r1 ; actually call the system-call + addq 4,sp ; pop the r0 parameter + +1: move.d r10,[sp+LR10] ; save the return value + + ;; second call of syscall_trace, to let it grab the results + + jsr _syscall_trace + + moveq 1,r9 ; "parameter" to ret_from_sys_call to show it was a sys call + ba _ret_from_sys_call + nop + + ;; from asm/processor.h, the thread_struct + +LTHREAD_KSP = 0 +LTHREAD_USP = 4 +LTHREAD_ESP0 = 8 +LTHREAD_DCCR = 12 + + ;; _resume performs the actual task-switching, by switching stack pointers + ;; input arguments: r10 = prev, r11 = next, r12 = thread offset in task struct + ;; returns old current in r10 + ;; + ;; TODO: see the i386 version. The switch_to which calls resume in our version + ;; could really be an inline asm of this. + +_resume: + push srp ; we keep the old/new PC on the stack + add.d r12, r10 ; r10 = current tasks tss + move dccr, [r10+LTHREAD_DCCR] ; save irq enable state + di + + move usp, [r10+LTHREAD_USP] ; save user-mode stackpointer + + subq 10*4, sp + movem r9, [sp] ; save non-scratch registers + + move.d sp, [r10+LTHREAD_KSP] ; save the kernel stack pointer for the old task + move.d sp, r10 ; return last running task in r10 + and.d -8192, r10 ; get task ptr from stackpointer + add.d r12, r11 ; find the new tasks tss + move.d [r11+LTHREAD_KSP], sp ; switch into the new stackframe by restoring kernel sp + + movem [sp+], r9 ; restore non-scratch registers + + move [r11+LTHREAD_USP], usp ; restore user-mode stackpointer + + move [r11+LTHREAD_DCCR], dccr ; restore irq enable status + jump [sp+] ; restore PC + + ;; This is the MMU bus fault handler. + ;; It needs to stack the CPU status and overall is different + ;; from the other interrupt handlers. + +_mmu_bus_fault: + sbfs [sp=sp-16] ; push the internal CPU status + ;; the first longword in the sbfs frame was the interrupted PC + ;; which fits nicely with the "IRP" slot in pt_regs normally used to + ;; contain the return address. used by Oops to print kernel errors.. + push srp ; make a stackframe similar to pt_regs + push dccr + push mof + di + subq 14*4, sp + movem r13, [sp] + push r10 ; dummy orig_r10 + moveq 1, r10 + push r10 ; frametype == 1, BUSFAULT frame type + + moveq 0, r9 ; busfault is equivalent to an irq + + move.d sp, r10 ; pt_regs argument to handle_mmu_bus_fault + + jsr _handle_mmu_bus_fault ; in arch/cris/mm/fault.c + + ;; now we need to return through the normal path, we cannot just + ;; do the RBFexit since we might have killed off the running + ;; process due to a SEGV, scheduled due to a page blocking or + ;; whatever. + + ba _ret_from_intr + nop + + ;; special handlers for breakpoint and NMI +#if 0 +_hwbreakpoint: + push dccr + di + push r10 + push r11 + push r12 + push r13 + clearf b + move brp,r11 + move.d [_hw_bp_msg],r10 + jsr _printk + setf b + pop r13 + pop r12 + pop r11 + pop r10 + pop dccr + retb + nop +#else +_hwbreakpoint: + push dccr + di +#if 1 + push r10 + push r11 + move.d [_hw_bp_trig_ptr],r10 + move.d [r10],r11 + cmp.d 42,r11 + beq nobp + nop + move brp,r11 + move.d r11,[r10+] + move.d r10,[_hw_bp_trig_ptr] +nobp: pop r11 + pop r10 +#endif + pop dccr + retb + nop +#endif + +_IRQ1_interrupt: +_spurious_interrupt: + di + move.b 4,r0 + move.b r0,[0xb0000030] +basse2: ba basse2 + nop + + ;; this handles the case when multiple interrupts arrive at the same time + ;; we jump to the first set interrupt bit in a priority fashion + ;; the hardware will call the unserved interrupts after the handler finishes + +_multiple_interrupt: + ;; this prologue MUST match the one in irq.h and the struct in ptregs.h!!! + push irp + push srp + push dccr + push mof + di + subq 14*4,sp + movem r13,[sp] + push r10 ; push orig_r10 + clear.d [sp=sp-4] ; frametype == 0, normal frame + + move.d _irq_shortcuts + 8,r1 + moveq 2,r2 ; first bit we care about is the timer0 irq + move.d [0xb00000d8],r0 ; read the irq bits that triggered the multiple irq +multloop: + btst r2,r0 ; check for the irq given by bit r2 + bmi do_shortcut ; actually do the shortcut + nop + addq 1,r2 ; next vector bit - remember this is in the delay slot! + addq 4,r1 ; next vector + cmpq 26,r2 + bne multloop ; process all irq's up to and including number 25 + nop + + ;; strange, we didn't get any set vector bits.. oh well, just return + + ba Rexit + nop + +do_shortcut: + test.d [r1] + beq Rexit + nop + jump [r1] ; jump to the irq handlers shortcut + + + .data + +_hw_bp_trigs: + .space 64*4 +_hw_bp_trig_ptr: + .dword _hw_bp_trigs + +/* linux/linkage.h got it wrong for this compiler currently */ + +#undef SYMBOL_NAME +#define SYMBOL_NAME(X) _/**/X + +_sys_call_table: + .long SYMBOL_NAME(sys_ni_syscall) /* 0 - old "setup()" system call*/ + .long SYMBOL_NAME(sys_exit) + .long SYMBOL_NAME(sys_fork) + .long SYMBOL_NAME(sys_read) + .long SYMBOL_NAME(sys_write) + .long SYMBOL_NAME(sys_open) /* 5 */ + .long SYMBOL_NAME(sys_close) + .long SYMBOL_NAME(sys_waitpid) + .long SYMBOL_NAME(sys_creat) + .long SYMBOL_NAME(sys_link) + .long SYMBOL_NAME(sys_unlink) /* 10 */ + .long SYMBOL_NAME(sys_execve) + .long SYMBOL_NAME(sys_chdir) + .long SYMBOL_NAME(sys_time) + .long SYMBOL_NAME(sys_mknod) + .long SYMBOL_NAME(sys_chmod) /* 15 */ + .long SYMBOL_NAME(sys_lchown16) + .long SYMBOL_NAME(sys_ni_syscall) /* old break syscall holder */ + .long SYMBOL_NAME(sys_stat) + .long SYMBOL_NAME(sys_lseek) + .long SYMBOL_NAME(sys_getpid) /* 20 */ + .long SYMBOL_NAME(sys_mount) + .long SYMBOL_NAME(sys_oldumount) + .long SYMBOL_NAME(sys_setuid16) + .long SYMBOL_NAME(sys_getuid16) + .long SYMBOL_NAME(sys_stime) /* 25 */ + .long SYMBOL_NAME(sys_ptrace) + .long SYMBOL_NAME(sys_alarm) + .long SYMBOL_NAME(sys_fstat) + .long SYMBOL_NAME(sys_pause) + .long SYMBOL_NAME(sys_utime) /* 30 */ + .long SYMBOL_NAME(sys_ni_syscall) /* old stty syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* old gtty syscall holder */ + .long SYMBOL_NAME(sys_access) + .long SYMBOL_NAME(sys_nice) + .long SYMBOL_NAME(sys_ni_syscall) /* 35 old ftime syscall holder */ + .long SYMBOL_NAME(sys_sync) + .long SYMBOL_NAME(sys_kill) + .long SYMBOL_NAME(sys_rename) + .long SYMBOL_NAME(sys_mkdir) + .long SYMBOL_NAME(sys_rmdir) /* 40 */ + .long SYMBOL_NAME(sys_dup) + .long SYMBOL_NAME(sys_pipe) + .long SYMBOL_NAME(sys_times) + .long SYMBOL_NAME(sys_ni_syscall) /* old prof syscall holder */ + .long SYMBOL_NAME(sys_brk) /* 45 */ + .long SYMBOL_NAME(sys_setgid16) + .long SYMBOL_NAME(sys_getgid16) + .long SYMBOL_NAME(sys_signal) + .long SYMBOL_NAME(sys_geteuid16) + .long SYMBOL_NAME(sys_getegid16) /* 50 */ + .long SYMBOL_NAME(sys_acct) + .long SYMBOL_NAME(sys_umount) /* recycled never used phys() */ + .long SYMBOL_NAME(sys_ni_syscall) /* old lock syscall holder */ + .long SYMBOL_NAME(sys_ioctl) + .long SYMBOL_NAME(sys_fcntl) /* 55 */ + .long SYMBOL_NAME(sys_ni_syscall) /* old mpx syscall holder */ + .long SYMBOL_NAME(sys_setpgid) + .long SYMBOL_NAME(sys_ni_syscall) /* old ulimit syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* old sys_olduname holder */ + .long SYMBOL_NAME(sys_umask) /* 60 */ + .long SYMBOL_NAME(sys_chroot) + .long SYMBOL_NAME(sys_ustat) + .long SYMBOL_NAME(sys_dup2) + .long SYMBOL_NAME(sys_getppid) + .long SYMBOL_NAME(sys_getpgrp) /* 65 */ + .long SYMBOL_NAME(sys_setsid) + .long SYMBOL_NAME(sys_sigaction) + .long SYMBOL_NAME(sys_sgetmask) + .long SYMBOL_NAME(sys_ssetmask) + .long SYMBOL_NAME(sys_setreuid16) /* 70 */ + .long SYMBOL_NAME(sys_setregid16) + .long SYMBOL_NAME(sys_sigsuspend) + .long SYMBOL_NAME(sys_sigpending) + .long SYMBOL_NAME(sys_sethostname) + .long SYMBOL_NAME(sys_setrlimit) /* 75 */ + .long SYMBOL_NAME(sys_old_getrlimit) + .long SYMBOL_NAME(sys_getrusage) + .long SYMBOL_NAME(sys_gettimeofday) + .long SYMBOL_NAME(sys_settimeofday) + .long SYMBOL_NAME(sys_getgroups16) /* 80 */ + .long SYMBOL_NAME(sys_setgroups16) + .long SYMBOL_NAME(sys_select) /* was old_select in Linux/E100 */ + .long SYMBOL_NAME(sys_symlink) + .long SYMBOL_NAME(sys_lstat) + .long SYMBOL_NAME(sys_readlink) /* 85 */ + .long SYMBOL_NAME(sys_uselib) + .long SYMBOL_NAME(sys_swapon) + .long SYMBOL_NAME(sys_reboot) + .long SYMBOL_NAME(old_readdir) + .long SYMBOL_NAME(old_mmap) /* 90 */ + .long SYMBOL_NAME(sys_munmap) + .long SYMBOL_NAME(sys_truncate) + .long SYMBOL_NAME(sys_ftruncate) + .long SYMBOL_NAME(sys_fchmod) + .long SYMBOL_NAME(sys_fchown16) /* 95 */ + .long SYMBOL_NAME(sys_getpriority) + .long SYMBOL_NAME(sys_setpriority) + .long SYMBOL_NAME(sys_ni_syscall) /* old profil syscall holder */ + .long SYMBOL_NAME(sys_statfs) + .long SYMBOL_NAME(sys_fstatfs) /* 100 */ + .long SYMBOL_NAME(sys_ni_syscall) /* sys_ioperm in i386 */ + .long SYMBOL_NAME(sys_socketcall) + .long SYMBOL_NAME(sys_syslog) + .long SYMBOL_NAME(sys_setitimer) + .long SYMBOL_NAME(sys_getitimer) /* 105 */ + .long SYMBOL_NAME(sys_newstat) + .long SYMBOL_NAME(sys_newlstat) + .long SYMBOL_NAME(sys_newfstat) + .long SYMBOL_NAME(sys_ni_syscall) /* old sys_uname holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* sys_iopl in i386 */ + .long SYMBOL_NAME(sys_vhangup) + .long SYMBOL_NAME(sys_ni_syscall) /* old "idle" system call */ + .long SYMBOL_NAME(sys_ni_syscall) /* vm86old in i386 */ + .long SYMBOL_NAME(sys_wait4) + .long SYMBOL_NAME(sys_swapoff) /* 115 */ + .long SYMBOL_NAME(sys_sysinfo) + .long SYMBOL_NAME(sys_ipc) + .long SYMBOL_NAME(sys_fsync) + .long SYMBOL_NAME(sys_sigreturn) + .long SYMBOL_NAME(sys_clone) /* 120 */ + .long SYMBOL_NAME(sys_setdomainname) + .long SYMBOL_NAME(sys_newuname) + .long SYMBOL_NAME(sys_ni_syscall) /* TODO sys_modify_ldt - do something ?*/ + .long SYMBOL_NAME(sys_adjtimex) + .long SYMBOL_NAME(sys_mprotect) /* 125 */ + .long SYMBOL_NAME(sys_sigprocmask) + .long SYMBOL_NAME(sys_create_module) + .long SYMBOL_NAME(sys_init_module) + .long SYMBOL_NAME(sys_delete_module) + .long SYMBOL_NAME(sys_get_kernel_syms) /* 130 */ + .long SYMBOL_NAME(sys_quotactl) + .long SYMBOL_NAME(sys_getpgid) + .long SYMBOL_NAME(sys_fchdir) + .long SYMBOL_NAME(sys_bdflush) + .long SYMBOL_NAME(sys_sysfs) /* 135 */ + .long SYMBOL_NAME(sys_personality) + .long SYMBOL_NAME(sys_ni_syscall) /* for afs_syscall */ + .long SYMBOL_NAME(sys_setfsuid16) + .long SYMBOL_NAME(sys_setfsgid16) + .long SYMBOL_NAME(sys_llseek) /* 140 */ + .long SYMBOL_NAME(sys_getdents) + .long SYMBOL_NAME(sys_select) + .long SYMBOL_NAME(sys_flock) + .long SYMBOL_NAME(sys_msync) + .long SYMBOL_NAME(sys_readv) /* 145 */ + .long SYMBOL_NAME(sys_writev) + .long SYMBOL_NAME(sys_getsid) + .long SYMBOL_NAME(sys_fdatasync) + .long SYMBOL_NAME(sys_sysctl) + .long SYMBOL_NAME(sys_mlock) /* 150 */ + .long SYMBOL_NAME(sys_munlock) + .long SYMBOL_NAME(sys_mlockall) + .long SYMBOL_NAME(sys_munlockall) + .long SYMBOL_NAME(sys_sched_setparam) + .long SYMBOL_NAME(sys_sched_getparam) /* 155 */ + .long SYMBOL_NAME(sys_sched_setscheduler) + .long SYMBOL_NAME(sys_sched_getscheduler) + .long SYMBOL_NAME(sys_sched_yield) + .long SYMBOL_NAME(sys_sched_get_priority_max) + .long SYMBOL_NAME(sys_sched_get_priority_min) /* 160 */ + .long SYMBOL_NAME(sys_sched_rr_get_interval) + .long SYMBOL_NAME(sys_nanosleep) + .long SYMBOL_NAME(sys_mremap) + .long SYMBOL_NAME(sys_setresuid16) + .long SYMBOL_NAME(sys_getresuid16) /* 165 */ + .long SYMBOL_NAME(sys_ni_syscall) /* sys_vm86 */ + .long SYMBOL_NAME(sys_query_module) + .long SYMBOL_NAME(sys_poll) + .long SYMBOL_NAME(sys_nfsservctl) + .long SYMBOL_NAME(sys_setresgid16) /* 170 */ + .long SYMBOL_NAME(sys_getresgid16) + .long SYMBOL_NAME(sys_prctl) + .long SYMBOL_NAME(sys_rt_sigreturn) + .long SYMBOL_NAME(sys_rt_sigaction) + .long SYMBOL_NAME(sys_rt_sigprocmask) /* 175 */ + .long SYMBOL_NAME(sys_rt_sigpending) + .long SYMBOL_NAME(sys_rt_sigtimedwait) + .long SYMBOL_NAME(sys_rt_sigqueueinfo) + .long SYMBOL_NAME(sys_rt_sigsuspend) + .long SYMBOL_NAME(sys_pread) /* 180 */ + .long SYMBOL_NAME(sys_pwrite) + .long SYMBOL_NAME(sys_chown16) + .long SYMBOL_NAME(sys_getcwd) + .long SYMBOL_NAME(sys_capget) + .long SYMBOL_NAME(sys_capset) /* 185 */ + .long SYMBOL_NAME(sys_sigaltstack) + .long SYMBOL_NAME(sys_sendfile) + .long SYMBOL_NAME(sys_ni_syscall) /* streams1 */ + .long SYMBOL_NAME(sys_ni_syscall) /* streams2 */ + .long SYMBOL_NAME(sys_vfork) /* 190 */ + .long SYMBOL_NAME(sys_getrlimit) + .long SYMBOL_NAME(sys_mmap2) + .long SYMBOL_NAME(sys_truncate64) + .long SYMBOL_NAME(sys_ftruncate64) + .long SYMBOL_NAME(sys_stat64) /* 195 */ + .long SYMBOL_NAME(sys_lstat64) + .long SYMBOL_NAME(sys_fstat64) + .long SYMBOL_NAME(sys_lchown) + .long SYMBOL_NAME(sys_getuid) + .long SYMBOL_NAME(sys_getgid) /* 200 */ + .long SYMBOL_NAME(sys_geteuid) + .long SYMBOL_NAME(sys_getegid) + .long SYMBOL_NAME(sys_setreuid) + .long SYMBOL_NAME(sys_setregid) + .long SYMBOL_NAME(sys_getgroups) /* 205 */ + .long SYMBOL_NAME(sys_setgroups) + .long SYMBOL_NAME(sys_fchown) + .long SYMBOL_NAME(sys_setresuid) + .long SYMBOL_NAME(sys_getresuid) + .long SYMBOL_NAME(sys_setresgid) /* 210 */ + .long SYMBOL_NAME(sys_getresgid) + .long SYMBOL_NAME(sys_chown) + .long SYMBOL_NAME(sys_setuid) + .long SYMBOL_NAME(sys_setgid) + .long SYMBOL_NAME(sys_setfsuid) /* 215 */ + .long SYMBOL_NAME(sys_setfsgid) + .long SYMBOL_NAME(sys_pivot_root) + .long SYMBOL_NAME(sys_mincore) + .long SYMBOL_NAME(sys_madvise) + .long SYMBOL_NAME(sys_getdents64) /* 220 */ + + /* + * 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. + */ + + ;; TODO: this needs to actually generate sys_ni_syscall entires + ;; since we now have removed the check for NULL entries in this + ;; table in system_call! + + .space (NR_syscalls-220)*4 + diff --git a/arch/cris/kernel/head.S b/arch/cris/kernel/head.S new file mode 100644 index 000000000..b436aa843 --- /dev/null +++ b/arch/cris/kernel/head.S @@ -0,0 +1,519 @@ + ;; $Id: head.S,v 1.11 2001/01/16 16:31:38 bjornw Exp $ + ;; + ;; Head of the kernel - alter with care + ;; + ;; Copyright (C) 2000, 2001 Axis Communications AB + ;; + ;; Authors: Bjorn Wesen (bjornw@axis.com) + ;; + ;; $Log: head.S,v $ + ;; Revision 1.11 2001/01/16 16:31:38 bjornw + ;; * Changed name and semantics of running_from_flash to romfs_in_flash, + ;; set by head.S to indicate to setup.c whether there is a cramfs image + ;; after the kernels BSS or not. Should work for all three boot-cases + ;; (DRAM with cramfs in DRAM, DRAM with cramfs in flash (compressed boot), + ;; and flash with cramfs in flash) + ;; + ;; Revision 1.10 2001/01/16 14:12:21 bjornw + ;; * Check for cramfs start passed in r9 from the decompressor, if all other + ;; cramfs options fail (if we boot from DRAM but don't find a cramfs image + ;; after the kernel in DRAM, it is probably still in the flash) + ;; * Check magic in cramfs detection when booting from flash directly + ;; + ;; Revision 1.9 2001/01/15 17:17:02 bjornw + ;; * Corrected the code that detects the cramfs lengths + ;; * Added a comment saying that the above does not work due to other + ;; reasons.. + ;; + ;; Revision 1.8 2001/01/15 16:27:51 jonashg + ;; Made boot after flashing work. + ;; * end destination is __vmlinux_end in RAM. + ;; * _romfs_start moved because of virtual memory. + ;; + ;; Revision 1.7 2000/11/21 13:55:29 bjornw + ;; Use CONFIG_CRIS_LOW_MAP for the low VM map instead of explicit CPU type + ;; + ;; Revision 1.6 2000/10/06 12:36:55 bjornw + ;; Forgot swapper_pg_dir when changing memory map.. + ;; + ;; Revision 1.5 2000/10/04 16:49:30 bjornw + ;; * Fixed memory mapping in LX + ;; * Check for cramfs instead of romfs + ;; + ;; + +#include <linux/config.h> +#define ASSEMBLER_MACROS_ONLY +#include <asm/sv_addr_ag.h> + +#define CRAMFS_MAGIC 0x28cd3d45 + + ;; exported symbols + + .globl _etrax_irv + .globl _romfs_start + .globl _romfs_length + .globl _romfs_in_flash + .globl _swapper_pg_dir + + .text + + ;; This is the entry point of the kernel. We are in supervisor mode. + ;; 0x00000000 if Flash, 0x40004000 if DRAM + ;; since etrax actually starts at address 2 when booting from flash, we + ;; put a nop (2 bytes) here first so we dont accidentally skip the di + ;; + ;; NOTICE! The register r9 is used as a parameter carrying register from + ;; the decompressor (if the kernel was compressed). It should not be + ;; used in the code below until it is read. + + nop + di + + ;; First setup the kseg_c mapping from where the kernel is linked + ;; to 0x40000000 (where the actual DRAM resides) otherwise + ;; we cannot do very much! See arch/cris/README.mm + ;; + ;; Notice that since we're potentially running at 0x00 or 0x40 right now, + ;; we will get a fault as soon as we enable the MMU if we dont + ;; temporarily map those segments linearily. + ;; + ;; Due to a bug in Etrax-100 LX version 1 we need to map the memory + ;; slightly different. We also let the simulator get this mapping for now. + +#ifdef CONFIG_CRIS_LOW_MAP + move.d 0x0800b000, r0 ; kseg mappings + move.d r0, [R_MMU_KBASE_HI] + + move.d 0x04040000, r0 ; temporary map of 0x40->0x40 and 0x00->0x00 + move.d r0, [R_MMU_KBASE_LO] + + move.d 0x80074871, r0 ; mmu enable, segs e,b,6,5,4,0 segment mapped + move.d r0, [R_MMU_CONFIG] +#else + move.d 0x0804b000, r0 ; kseg mappings + move.d r0, [R_MMU_KBASE_HI] + + move.d 0x00040000, r0 ; temporary map of 0x40->0x40 and 0x00->0x00 + move.d r0, [R_MMU_KBASE_LO] + + move.d 0x8007d811, r0 ; mmu enable, segs f,e,c,b,4,0 segment mapped + move.d r0, [R_MMU_CONFIG] +#endif + + ;; Now we need to sort out the segments and their locations in RAM or + ;; Flash. The image in the Flash (or in DRAM) consists of 3 pieces: + ;; 1) kernel text, 2) kernel data, 3) ROM filesystem image + ;; But the linker has linked the kernel to expect this layout in + ;; DRAM memory: + ;; 1) kernel text, 2) kernel data, 3) kernel BSS + ;; (the location of the ROM filesystem is determined by the krom driver) + ;; If we boot this from Flash, we want to keep the ROM filesystem in + ;; the flash, we want to copy the text and need to copy the data to DRAM. + ;; But if we boot from DRAM, we need to move the ROMFS image + ;; from its position after kernel data, to after kernel BSS, BEFORE the + ;; kernel starts using the BSS area (since its "overlayed" with the ROMFS) + ;; + ;; In both cases, we start in un-cached mode, and need to jump into a + ;; cached PC after we're done fiddling around with the segments. + ;; + ;; arch/etrax100/etrax100.ld sets some symbols that define the start + ;; and end of each segment. + + ;; Check if we start from DRAM or FLASH by testing PC + + move.d pc,r0 + and.d 0x7fffffff,r0 ; get rid of the non-cache bit + cmp.d 0x10000,r0 ; arbitrary... just something above this code + bcs inflash + nop + + jump inram ; enter cached ram + +inflash: + +#ifndef CONFIG_SVINTO_SIM + + ;; We need to setup the bus registers before we start using the DRAM + + move.d DEF_R_WAITSTATES, r0 + move.d r0, [R_WAITSTATES] + + move.d DEF_R_BUS_CONFIG, r0 + move.d r0, [R_BUS_CONFIG] + + move.d DEF_R_DRAM_CONFIG, r0 + move.d r0, [R_DRAM_CONFIG] + + move.d DEF_R_DRAM_TIMING, r0 + move.d r0, [R_DRAM_TIMING] + +#endif + ;; Copy text+data to DRAM + ;; This is fragile - the calculation of r4 as the image size depends + ;; on that the labels below actually are the first and last positions + ;; in the linker-script. + ;; + ;; Then the locating of the cramfs image depends on the aforementioned + ;; image being located in the flash at 0. This is most often not true, + ;; thus the following does not work (normally there is a rescue-block + ;; between the physical start of the flash and the flash-image start, + ;; and when run with compression, the kernel is actually unpacked to + ;; DRAM and we never get here in the first place :)) + + moveq 0, r0 ; source + move.d _text_start, r1 ; destination + move.d __vmlinux_end, r2 ; end destination + move.d r2, r4 + sub.d r1, r4 ; r4=__vmlinux_end in flash, used below +1: move.w [r0+], r3 + move.w r3, [r1+] + cmp.d r2, r1 + bcs 1b + nop + + ;; We keep the cramfs in the flash. + ;; There might be none, but that does not matter because + ;; we don't do anything than read some bytes here. + + moveq 0, r0 + move.d r0, [_romfs_length] ; default if there is no cramfs + + move.d [r4], r0 ; cramfs_super.magic + cmp.d CRAMFS_MAGIC, r0 + bne 1f + nop + move.d [r4 + 4], r0 ; cramfs_super.size + move.d r0, [_romfs_length] +#ifdef CONFIG_CRIS_LOW_MAP + add.d 0x50000000, r4 ; add flash start in virtual memory (cached) +#else + add.d 0xf0000000, r4 ; add flash start in virtual memory (cached) +#endif + move.d r4, [_romfs_start] +1: + moveq 1, r0 + move.d r0, [_romfs_in_flash] + + jump start_it ; enter code, cached this time + +inram: + ;; Move the ROM fs to after BSS end. This assumes that the cramfs + ;; second longword contains the length of the cramfs + + moveq 0, r0 + move.d r0, [_romfs_length] ; default if there is no cramfs + + ;; First check if there is a cramfs (magic value) + ;; Notice that we check for cramfs magic value - which is + ;; the "rom fs" we'll possibly use in 2.4 if not JFFS (which does + ;; not need this mechanism anyway) + + move.d __vmlinux_end, r0 ; the image will be after the vmlinux end address + move.d [r0], r1 ; cramfs assumes same endian on host/target + cmp.d CRAMFS_MAGIC, r1; magic value in cramfs superblock + bne no_romfs_in_ram + nop + + ;; Ok. What is its size ? + + move.d [r0 + 4], r2 ; cramfs_super.size (again, no need to swapwb) + + ;; We want to copy it to the end of the BSS + + move.d _end, r1 + + ;; Remember values so cramfs and setup can find this info + + move.d r1, [_romfs_start] ; new romfs location + move.d r2, [_romfs_length] + + ;; We need to copy it backwards, since they can be overlapping + + add.d r2, r0 + add.d r2, r1 + + ;; Go ahead. Make my loop. + + lsrq 1, r2 ; size is in bytes, we copy words + +1: move.w [r0=r0-2],r3 + move.w r3,[r1=r1-2] + subq 1, r2 + bne 1b + nop + + ;; Dont worry that the BSS is tainted. It will be cleared later. + + moveq 0, r0 + move.d r0, [_romfs_in_flash] + + jump start_it ; better skip the additional cramfs check below + +no_romfs_in_ram: + + ;; We have still one other possibility at this point - the kernel + ;; could have been unpacked to DRAM by the loader, but the cramfs + ;; image was still in the Flash directly after the compressed kernel + ;; image. The loader passes the address of the byte succeeding the + ;; last compressed byte in the flash in the register r9 when starting + ;; the kernel. Check if r9 points to a decent cramfs image! + ;; (Notice that if this is not booted from the loader, r9 will be + ;; garbage but we do sanity checks on it, the chance that it points + ;; to a cramfs magic is small.. ) + + cmp.d 0x0ffffff8, r9 + bcc 1f ; r9 points outside the flash area + nop + move.d [r9], r0 ; cramfs_super.magic + cmp.d CRAMFS_MAGIC, r0 + bne 1f + nop + move.d [r9+4], r0 ; cramfs_super.length + move.d r0, [_romfs_length] +#ifdef CONFIG_CRIS_LOW_MAP + add.d 0x50000000, r9 ; add flash start in virtual memory (cached) +#else + add.d 0xf0000000, r9 ; add flash start in virtual memory (cached) +#endif + move.d r9, [_romfs_start] + + moveq 1, r0 + move.d r0, [_romfs_in_flash] +1: + + jump start_it ; enter code, cached this time + +start_it: + ;; the kernel stack is overlayed with the task structure for each + ;; task. thus the initial kernel stack is in the same page as the + ;; init_task (but starts in the top of the page, size 8192) + move.d _init_task_union + 8192,sp + move.d _ibr_start,r0 ; this symbol is set by the linker script + move r0,ibr + move.d r0,[_etrax_irv] ; set the interrupt base register and pointer + + ;; Clear BSS region, from _bss_start to _end + + move.d __bss_start, r0 + move.d _end, r1 +1: clear.d [r0+] + cmp.d r1, r0 + bcs 1b + nop + +#ifdef CONFIG_BLK_DEV_ETRAXIDE + ;; disable ATA before enabling it in genconfig below + moveq 0,r0 + move.d r0,[R_ATA_CTRL_DATA] + move.d r0,[R_ATA_TRANSFER_CNT] + move.d r0,[R_ATA_CONFIG] +#if 0 + move.d R_PORT_G_DATA,r1 + move.d r0,[r1]; assert ATA bus-reset + nop + nop + nop + nop + nop + nop + move.d 0x08000000,r0 + move.d r0,[r1] +#endif +#endif + +#ifdef CONFIG_JULIETTE + ;; configure external DMA channel 0 before enabling it in genconfig + + moveq 0,r0 + move.d r0,[R_EXT_DMA_0_ADDR] + move.d 0x860000,r0 ; cnt enable, word size, output, stop, size 0 + move.d r0,[R_EXT_DMA_0_CMD] + + ;; reset dma4 and wait for completion + + moveq 4,r0 + move.b r0,[R_DMA_CH4_CMD] +w4u: move.b [R_DMA_CH4_CMD],r0 + and.b 7,r0 + cmp.b 4,r0 + beq w4u + nop + + ;; reset dma5 and wait for completion + + moveq 4,r0 + move.b r0,[R_DMA_CH5_CMD] +w5u: move.b [R_DMA_CH5_CMD],r0 + and.b 7,r0 + cmp.b 4,r0 + beq w5u + nop +#endif + + ;; Etrax product HW genconfig setup + + moveq 0,r0 +#if !defined(CONFIG_KGDB) && !defined(CONFIG_DMA_MEMCPY) + or.d 0x140000,r0 ; DMA channels 6 and 7 to ser0, kgdb doesnt want DMA +#endif +#if !defined(CONFIG_KGDB) || !defined(CONFIG_DEBUG_PORT1) + or.d 0xc00000,r0 ; DMA channels 8 and 9 to ser1, kgdb doesnt want DMA +#endif +#ifdef CONFIG_DMA_MEMCPY + or.d 0x003c0000,r0 ; 6/7 memory-memory DMA +#endif +#ifdef CONFIG_ETRAX100_SERIAL_PORT2 + or.d 0x2808,r0 ; DMA channels 2 and 3 to serport 2, port 2 enabled +#endif +#ifdef CONFIG_ETRAX100_SERIAL_PORT3 + or.d 0x28100,r0 ; DMA channels 4 and 5 to serport 3, port 3 enabled +#endif +#if defined(CONFIG_ETRAX100_PARALLEL_PORT0) || defined(CONFIG_ETRAX_ETHERNET_LPSLAVE) + or.w 0x4,r0 ; parport 0 enabled using DMA 2/3 +#endif +#if defined(CONFIG_ETRAX100_PARALLEL_PORT1) || defined(CONFIG_ETRAX_ETHERNET_LPSLAVE) + or.w 0x80,r0 ; parport 1 enabled using DMA 4/5 +#endif +#ifdef CONFIG_BLK_DEV_ETRAXIDE + or.d 0x3c02,r0 ; DMA channels 2 and 3 to ATA, ATA enabled +#endif +#ifdef CONFIG_JULIETTE + or.d 0x3c000,r0 ; DMA channels 4 and 5 to EXTDMA0, for Juliette +#ifndef CONFIG_BLK_DEV_ETRAXIDE + or.d 0x41,r0 ; HACK for now! To make G27 connected for the RTC +#endif +#endif + move.d r0,[_genconfig_shadow] ; init a shadow register of R_GEN_CONFIG + +#ifndef CONFIG_SVINTO_SIM + move.d r0,[R_GEN_CONFIG] + +#if 0 + moveq 4,r0 + move.b r0,[R_DMA_CH6_CMD] ; reset (ser0 dma out) + move.b r0,[R_DMA_CH7_CMD] ; reset (ser0 dma in) +w61: move.b [R_DMA_CH6_CMD],r0 ; wait for reset cycle to finish + and.b 7,r0 + cmp.b 4,r0 + beq w61 + nop +w71: move.b [R_DMA_CH7_CMD],r0 ; wait for reset cycle to finish + and.b 7,r0 + cmp.b 4,r0 + beq w71 + nop +#endif + + moveq 4,r0 + move.b r0,[R_DMA_CH8_CMD] ; reset (ser1 dma out) + move.b r0,[R_DMA_CH9_CMD] ; reset (ser1 dma in) +w81: move.b [R_DMA_CH8_CMD],r0 ; wait for reset cycle to finish + and.b 7,r0 + cmp.b 4,r0 + beq w81 + nop +w91: move.b [R_DMA_CH9_CMD],r0 ; wait for reset cycle to finish + and.b 7,r0 + cmp.b 4,r0 + beq w91 + nop + + ;; setup port PA and PB default initial directions and data + ;; including their shadow registers + + move.b DEF_R_PORT_PA_DIR,r0 + move.b r0,[_port_pa_dir_shadow] + move.b r0,[R_PORT_PA_DIR] + move.b DEF_R_PORT_PA_DATA,r0 + move.b r0,[_port_pa_data_shadow] + move.b r0,[R_PORT_PA_DATA] + + move.b DEF_R_PORT_PB_CONFIG,r0 + move.b r0,[_port_pb_config_shadow] + move.b r0,[R_PORT_PB_CONFIG] + move.b DEF_R_PORT_PB_DIR,r0 + move.b r0,[_port_pb_dir_shadow] + move.b r0,[R_PORT_PB_DIR] + move.b DEF_R_PORT_PB_DATA,r0 + move.b r0,[_port_pb_data_shadow] + move.b r0,[R_PORT_PB_DATA] + + moveq 0,r0 + move.d r0,[_port_g_data_shadow] + move.d r0,[R_PORT_G_DATA] + + ;; setup the serial port 0 at 115200 baud for debug purposes + + moveq 0,r0 + move.d r0,[R_SERIAL0_XOFF] + + move.b 0x99,r0 + move.b r0,[R_SERIAL0_BAUD] ; 115.2kbaud for both transmit and receive + + move.b 0x40,r0 ; rec enable + move.b r0,[R_SERIAL0_REC_CTRL] + + move.b 0x40,r0 ; tr enable + move.b r0,[R_SERIAL0_TR_CTRL] + + ;; setup the serial port 1 at 115200 baud for debug purposes + + moveq 0,r0 + move.d r0,[R_SERIAL1_XOFF] + + move.b 0x99,r0 + move.b r0,[R_SERIAL1_BAUD] ; 115.2kbaud for both transmit and receive + + move.b 0x40,r0 ; rec enable + move.b r0,[R_SERIAL1_REC_CTRL] + + move.b 0x40,r0 ; tr enable + move.b r0,[R_SERIAL1_TR_CTRL] + +#ifdef CONFIG_ETRAX_90000000_LEDS + ;; clear LED's on Stallone and Olga boards + moveq -1,r0 + move.d r0,[_port_90000000_shadow] + move.d r0,[0x90000000] +#endif + +#ifdef CONFIG_ETRAX100_SERIAL_PORT3 + ;; setup the serial port 3 at 115200 baud for debug purposes + + moveq 0,r0 + move.d r0,[R_SERIAL3_XOFF] + + move.b 0x99,r0 + move.b r0,[R_SERIAL3_BAUD] ; 115.2kbaud for both transmit and receive + + move.b 0x40,r0 ; rec enable + move.b r0,[R_SERIAL3_REC_CTRL] + + move.b 0x40,r0 ; tr enable + move.b r0,[R_SERIAL3_TR_CTRL] +#endif + +#endif /* CONFIG_SVINTO_SIM */ + + jump _start_kernel ; jump into the C-function _start_kernel in init/main.c + + + .data +_etrax_irv: + .dword 0 +_romfs_start: + .dword 0 +_romfs_length: + .dword 0 +_romfs_in_flash: + .dword 0 + + ;; put some special pages at the beginning of the kernel aligned + ;; to page boundaries - the kernel cannot start until after this + +#ifdef CONFIG_CRIS_LOW_MAP +_swapper_pg_dir = 0x60002000 +#else +_swapper_pg_dir = 0xc0002000 +#endif diff --git a/arch/cris/kernel/hexify.c b/arch/cris/kernel/hexify.c new file mode 100644 index 000000000..daa331fec --- /dev/null +++ b/arch/cris/kernel/hexify.c @@ -0,0 +1,31 @@ +#include <stdio.h> + + +void main() +{ + int c; + int comma=0; + int count=0; + while((c=getchar())!=EOF) + { + unsigned char x=c; + if(comma) + printf(","); + else + comma=1; + if(count==8) + { + count=0; + printf("\n"); + } + if(count==0) + printf("\t"); + printf("0x%02X",c); + count++; + } + if(count) + printf("\n"); + exit(0); +} + + diff --git a/arch/cris/kernel/irq.c b/arch/cris/kernel/irq.c new file mode 100644 index 000000000..e55f94d20 --- /dev/null +++ b/arch/cris/kernel/irq.c @@ -0,0 +1,467 @@ +/* $Id: irq.c,v 1.5 2000/08/17 15:35:15 bjornw Exp $ + * + * linux/arch/cris/kernel/irq.c + * + * Copyright (c) 2000 Axis Communications AB + * + * Authors: Bjorn Wesen (bjornw@axis.com) + * + * This file contains the code used by various IRQ handling routines: + * asking for different IRQ's should be done through these routines + * instead of just grabbing them. Thus setups with different IRQ numbers + * shouldn't result in any weird surprises, and installing new handlers + * should be easier. + * + * Notice Linux/CRIS: these routines do not care about SMP + * + */ + +/* + * IRQ's are in fact implemented a bit like signal handlers for the kernel. + * Naturally it's not a 1:1 relation, but there are similarities. + */ + +#include <linux/config.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/timex.h> +#include <linux/malloc.h> +#include <linux/random.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/bitops.h> + +#include <asm/svinto.h> + +char *hw_bp_msg = "BP 0x%x\n"; + +static inline void +mask_irq(unsigned int irq_nr) +{ + *R_VECT_MASK_CLR = 1 << irq_nr; +} + +static inline void +unmask_irq(unsigned int irq_nr) +{ + *R_VECT_MASK_SET = 1 << irq_nr; +} + +void +disable_irq(unsigned int irq_nr) +{ + unsigned long flags; + + save_flags(flags); + cli(); + mask_irq(irq_nr); + restore_flags(flags); +} + +void +enable_irq(unsigned int irq_nr) +{ + unsigned long flags; + save_flags(flags); + cli(); + unmask_irq(irq_nr); + restore_flags(flags); +} + +unsigned long +probe_irq_on() +{ + return 0; +} + +int +probe_irq_off(unsigned long x) +{ + return 0; +} + +irqvectptr irq_shortcuts[NR_IRQS]; /* vector of shortcut jumps after the irq prologue */ + +/* don't use set_int_vector, it bypasses the linux interrupt handlers. it is + * global just so that the kernel gdb can use it. + */ + +void +set_int_vector(int n, irqvectptr addr, irqvectptr saddr) +{ + /* remember the shortcut entry point, after the prologue */ + + irq_shortcuts[n] = saddr; + + etrax_irv->v[n + 0x20] = (irqvectptr)addr; +} + +/* the breakpoint vector is obviously not made just like the normal irq handlers + * but needs to contain _code_ to jump to addr. + * + * the BREAK n instruction jumps to IBR + n * 8 + */ + +void +set_break_vector(int n, irqvectptr addr) +{ + unsigned short *jinstr = (unsigned short *)&etrax_irv->v[n*2]; + unsigned long *jaddr = (unsigned long *)(jinstr + 1); + + /* if you don't know what this does, do not touch it! */ + + *jinstr = 0x0d3f; + *jaddr = (unsigned long)addr; + + /* 00000026 <clrlop+1a> 3f0d82000000 jump 0x82 */ +} + + +/* + * This builds up the IRQ handler stubs using some ugly macros in irq.h + * + * These macros create the low-level assembly IRQ routines that do all + * the operations that are needed. They are also written to be fast - and to + * disable interrupts as little as humanly possible. + * + */ + +/* IRQ0 and 1 are special traps */ +void hwbreakpoint(void); +void IRQ1_interrupt(void); +BUILD_IRQ(2, 0x04) /* the timer interrupt */ +BUILD_IRQ(3, 0x08) +BUILD_IRQ(4, 0x10) +BUILD_IRQ(5, 0x20) +BUILD_IRQ(6, 0x40) +BUILD_IRQ(7, 0x80) +BUILD_IRQ(8, 0x100) +BUILD_IRQ(9, 0x200) +BUILD_IRQ(10, 0x400) +BUILD_IRQ(11, 0x800) +BUILD_IRQ(12, 0x1000) +BUILD_IRQ(13, 0x2000) +void mmu_bus_fault(void); /* IRQ 14 is the bus fault interrupt */ +void multiple_interrupt(void); /* IRQ 15 is the multiple IRQ interrupt */ +BUILD_IRQ(16, 0x10000) +BUILD_IRQ(17, 0x20000) +BUILD_IRQ(18, 0x40000) +BUILD_IRQ(19, 0x80000) +BUILD_IRQ(20, 0x100000) +BUILD_IRQ(21, 0x200000) +BUILD_IRQ(22, 0x400000) +BUILD_IRQ(23, 0x800000) +BUILD_IRQ(24, 0x1000000) +BUILD_IRQ(25, 0x2000000) + +/* + * Pointers to the low-level handlers + */ + +static void (*interrupt[NR_IRQS])(void) = { + NULL, NULL, IRQ2_interrupt, IRQ3_interrupt, + IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt, + IRQ8_interrupt, IRQ9_interrupt, IRQ10_interrupt, IRQ11_interrupt, + IRQ12_interrupt, IRQ13_interrupt, NULL, NULL, + IRQ16_interrupt, IRQ17_interrupt, IRQ18_interrupt, IRQ19_interrupt, + IRQ20_interrupt, IRQ21_interrupt, IRQ22_interrupt, IRQ23_interrupt, + IRQ24_interrupt, IRQ25_interrupt +}; + +static void (*sinterrupt[NR_IRQS])(void) = { + NULL, NULL, sIRQ2_interrupt, sIRQ3_interrupt, + sIRQ4_interrupt, sIRQ5_interrupt, sIRQ6_interrupt, sIRQ7_interrupt, + sIRQ8_interrupt, sIRQ9_interrupt, sIRQ10_interrupt, sIRQ11_interrupt, + sIRQ12_interrupt, sIRQ13_interrupt, NULL, NULL, + sIRQ16_interrupt, sIRQ17_interrupt, sIRQ18_interrupt, sIRQ19_interrupt, + sIRQ20_interrupt, sIRQ21_interrupt, sIRQ22_interrupt, sIRQ23_interrupt, + sIRQ24_interrupt, sIRQ25_interrupt +}; + +static void (*bad_interrupt[NR_IRQS])(void) = { + NULL, NULL, + NULL, bad_IRQ3_interrupt, + bad_IRQ4_interrupt, bad_IRQ5_interrupt, + bad_IRQ6_interrupt, bad_IRQ7_interrupt, + bad_IRQ8_interrupt, bad_IRQ9_interrupt, + bad_IRQ10_interrupt, bad_IRQ11_interrupt, + bad_IRQ12_interrupt, bad_IRQ13_interrupt, + NULL, NULL, + bad_IRQ16_interrupt, bad_IRQ17_interrupt, + bad_IRQ18_interrupt, bad_IRQ19_interrupt, + bad_IRQ20_interrupt, bad_IRQ21_interrupt, + bad_IRQ22_interrupt, bad_IRQ23_interrupt, + bad_IRQ24_interrupt, bad_IRQ25_interrupt +}; + +/* + * Initial irq handlers. + */ + +static struct irqaction *irq_action[NR_IRQS] = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL +}; + +int get_irq_list(char *buf) +{ + int i, len = 0; + struct irqaction * action; + + for (i = 0; i < NR_IRQS; i++) { + action = irq_action[i]; + if (!action) + continue; + len += sprintf(buf+len, "%2d: %10u %c %s", + i, kstat.irqs[0][i], + (action->flags & SA_INTERRUPT) ? '+' : ' ', + action->name); + for (action = action->next; action; action = action->next) { + len += sprintf(buf+len, ",%s %s", + (action->flags & SA_INTERRUPT) ? " +" : "", + action->name); + } + len += sprintf(buf+len, "\n"); + } + return len; +} + +/* called by the assembler IRQ entry functions defined in irq.h + * to dispatch the interrupts to registred handlers + * interrupts are disabled upon entry - depending on if the + * interrupt was registred with SA_INTERRUPT or not, interrupts + * are re-enabled or not. + */ + +asmlinkage void do_IRQ(int irq, struct pt_regs * regs) +{ + struct irqaction *action; + int do_random, cpu; + + cpu = smp_processor_id(); + irq_enter(cpu); + kstat.irqs[cpu][irq]++; + + action = *(irq + irq_action); + if (action) { + if (!(action->flags & SA_INTERRUPT)) + __sti(); + action = *(irq + irq_action); + do_random = 0; + do { + do_random |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); + if (do_random & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + __cli(); + } + irq_exit(cpu); + + if (softirq_active(cpu) & softirq_mask(cpu)) + do_softirq(); + + /* unmasking and bottom half handling is done magically for us. */ +} + +/* this function links in a handler into the chain of handlers for the + given irq, and if the irq has never been registred, the appropriate + handler is entered into the interrupt vector +*/ + +int setup_etrax_irq(int irq, struct irqaction * new) +{ + int shared = 0; + struct irqaction *old, **p; + unsigned long flags; + + p = irq_action + irq; + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & new->flags & SA_SHIRQ)) + return -EBUSY; + + /* Can't share interrupts unless both are same type */ + if ((old->flags ^ new->flags) & SA_INTERRUPT) + return -EBUSY; + + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + shared = 1; + } + + if (new->flags & SA_SAMPLE_RANDOM) + rand_initialize_irq(irq); + + save_flags(flags); + cli(); + *p = new; + + if (!shared) { + /* if the irq wasn't registred before, enter it into the vector table + and unmask it physically + */ + set_int_vector(irq, interrupt[irq], sinterrupt[irq]); + unmask_irq(irq); + } + + restore_flags(flags); + return 0; +} + +/* this function is called by a driver to register an irq handler + Valid flags: + SA_INTERRUPT -> it's a fast interrupt, handler called with irq disabled and + no signal checking etc is performed upon exit + SA_SHIRQ -> the interrupt can be shared between different handlers, the handler + is required to check if the irq was "aimed" at it explicitely + SA_RANDOM -> the interrupt will add to the random generators entropy +*/ + +int request_irq(unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, + const char * devname, + void *dev_id) +{ + int retval; + struct irqaction * action; + + /* interrupts 0 and 1 are hardware breakpoint and NMI and we can't support + these yet. interrupt 15 is the multiple irq, it's special. */ + + if(irq < 2 || irq == 15 || irq >= NR_IRQS) + return -EINVAL; + + if(!handler) + return -EINVAL; + + /* allocate and fill in a handler structure and setup the irq */ + + action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) + return -ENOMEM; + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->next = NULL; + action->dev_id = dev_id; + + retval = setup_etrax_irq(irq, action); + + if (retval) + kfree(action); + return retval; +} + +void free_irq(unsigned int irq, void *dev_id) +{ + struct irqaction * action, **p; + unsigned long flags; + + if (irq >= NR_IRQS) { + printk("Trying to free IRQ%d\n",irq); + return; + } + for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) { + if (action->dev_id != dev_id) + continue; + + /* Found it - now free it */ + save_flags(flags); + cli(); + *p = action->next; + if (!irq[irq_action]) { + mask_irq(irq); + set_int_vector(irq, bad_interrupt[irq], 0); + } + restore_flags(flags); + kfree(action); + return; + } + printk("Trying to free free IRQ%d\n",irq); +} + +void weird_irq(void) +{ + __asm__("di"); + printk("weird irq\n"); + while(1); +} + +/* init_IRQ() is called by start_kernel and is responsible for fixing IRQ masks and + setting the irq vector table to point to bad_interrupt ptrs. +*/ + +void system_call(void); /* from entry.S */ + +void init_IRQ(void) +{ + int i; + + /* clear all interrupt masks */ + +#ifndef CONFIG_SVINTO_SIM + *R_IRQ_MASK0_CLR = 0xffffffff; + *R_IRQ_MASK1_CLR = 0xffffffff; + *R_IRQ_MASK2_CLR = 0xffffffff; +#endif + + *R_VECT_MASK_CLR = 0xffffffff; + + /* clear the shortcut entry points */ + + for(i = 0; i < NR_IRQS; i++) + irq_shortcuts[i] = NULL; + + for (i = 0; i < 256; i++) + etrax_irv->v[i] = weird_irq; + + /* set all etrax irq's to the bad handlers */ + for (i = 2; i < NR_IRQS; i++) + set_int_vector(i, bad_interrupt[i], 0); + + /* except IRQ 15 which is the multiple-IRQ handler on Etrax100 */ + + set_int_vector(15, multiple_interrupt, 0); + + /* 0 and 1 which are special breakpoint/NMI traps */ + + set_int_vector(0, hwbreakpoint, 0); + set_int_vector(1, IRQ1_interrupt, 0); + + /* and irq 14 which is the mmu bus fault handler */ + + set_int_vector(14, mmu_bus_fault, 0); + + /* setup the system-call trap, which is reached by BREAK 13 */ + + set_break_vector(13, system_call); + +#ifdef CONFIG_KGDB + /* setup kgdb if its enabled, and break into the debugger */ + + kgdb_init(); + + breakpoint(); +#endif + +} diff --git a/arch/cris/kernel/kgdb.c b/arch/cris/kernel/kgdb.c new file mode 100644 index 000000000..98d427b05 --- /dev/null +++ b/arch/cris/kernel/kgdb.c @@ -0,0 +1,1540 @@ +/*!************************************************************************** +*! +*! FILE NAME : kgdb.c +*! +*! DESCRIPTION: Implementation of the gdb stub with respect to ETRAX 100. +*! It is a mix of arch/m68k/kernel/kgdb.c and cris_stub.c. +*! +*!--------------------------------------------------------------------------- +*! HISTORY +*! +*! DATE NAME CHANGES +*! ---- ---- ------- +*! Apr 26 1999 Hendrik Ruijter Initial version. +*! May 6 1999 Hendrik Ruijter Removed call to strlen in libc and removed +*! struct assignment as it generates calls to +*! memcpy in libc. +*! Jun 17 1999 Hendrik Ruijter Added gdb 4.18 support. 'X', 'qC' and 'qL'. +*! Jul 21 1999 Bjorn Wesen eLinux port +*! +*! $Log: kgdb.c,v $ +*! Revision 1.2 2001/01/12 14:22:25 orjanf +*! Updated kernel debugging support to work with ETRAX 100LX. +*! +*! Revision 1.1 2000/07/10 16:25:21 bjornw +*! Initial revision +*! +*! Revision 1.1.1.1 1999/12/03 14:57:31 bjornw +*! * Initial version of arch/cris, the latest CRIS architecture with an MMU. +*! Mostly copied from arch/etrax100 with appropriate renames of files. +*! The mm/ subdir is copied from arch/i386. +*! This does not compile yet at all. +*! +*! +*! Revision 1.4 1999/07/22 17:25:25 bjornw +*! Dont wait for + in putpacket if we havent hit the initial breakpoint yet. Added a kgdb_init function which sets up the break and irq vectors. +*! +*! Revision 1.3 1999/07/21 19:51:18 bjornw +*! Check if the interrupting char is a ctrl-C, ignore otherwise. +*! +*! Revision 1.2 1999/07/21 18:09:39 bjornw +*! Ported to eLinux architecture, and added some kgdb documentation. +*! +*! +*!--------------------------------------------------------------------------- +*! +*! $Id: kgdb.c,v 1.2 2001/01/12 14:22:25 orjanf Exp $ +*! +*! (C) Copyright 1999, Axis Communications AB, LUND, SWEDEN +*! +*!**************************************************************************/ +/* @(#) cris_stub.c 1.3 06/17/99 */ + +/* + * kgdb usage notes: + * ----------------- + * + * If you select CONFIG_KGDB in the configuration, the kernel will be built + * with different gcc flags: "-g" is added to get debug infos, and + * "-fomit-frame-pointer" is omitted to make debugging easier. Since the + * resulting kernel will be quite big (approx. > 7 MB), it will be stripped + * before compresion. Such a kernel will behave just as usually, except if + * given a "debug=<device>" command line option. (Only serial devices are + * allowed for <device>, i.e. no printers or the like; possible values are + * machine depedend and are the same as for the usual debug device, the one + * for logging kernel messages.) If that option is given and the device can be + * initialized, the kernel will connect to the remote gdb in trap_init(). The + * serial parameters are fixed to 8N1 and 115200 bps, for easyness of + * implementation. + * + * To start a debugging session, start that gdb with the debugging kernel + * image (the one with the symbols, vmlinux.debug) named on the command line. + * This file will be used by gdb to get symbol and debugging infos about the + * kernel. Next, select remote debug mode by + * target remote <device> + * where <device> is the name of the serial device over which the debugged + * machine is connected. Maybe you have to adjust the baud rate by + * set remotebaud <rate> + * or also other parameters with stty: + * shell stty ... </dev/... + * If the kernel to debug has already booted, it waited for gdb and now + * connects, and you'll see a breakpoint being reported. If the kernel isn't + * running yet, start it now. The order of gdb and the kernel doesn't matter. + * Another thing worth knowing about in the getting-started phase is how to + * debug the remote protocol itself. This is activated with + * set remotedebug 1 + * gdb will then print out each packet sent or received. You'll also get some + * messages about the gdb stub on the console of the debugged machine. + * + * If all that works, you can use lots of the usual debugging techniques on + * the kernel, e.g. inspecting and changing variables/memory, setting + * breakpoints, single stepping and so on. It's also possible to interrupt the + * debugged kernel by pressing C-c in gdb. Have fun! :-) + * + * The gdb stub is entered (and thus the remote gdb gets control) in the + * following situations: + * + * - If breakpoint() is called. This is just after kgdb initialization, or if + * a breakpoint() call has been put somewhere into the kernel source. + * (Breakpoints can of course also be set the usual way in gdb.) + * In eLinux, we call breakpoint() in init/main.c after IRQ initialization. + * + * - If there is a kernel exception, i.e. bad_super_trap() or die_if_kernel() + * are entered. All the CPU exceptions are mapped to (more or less..., see + * the hard_trap_info array below) appropriate signal, which are reported + * to gdb. die_if_kernel() is usually called after some kind of access + * error and thus is reported as SIGSEGV. + * + * - When panic() is called. This is reported as SIGABRT. + * + * - If C-c is received over the serial line, which is treated as + * SIGINT. + * + * Of course, all these signals are just faked for gdb, since there is no + * signal concept as such for the kernel. It also isn't possible --obviously-- + * to set signal handlers from inside gdb, or restart the kernel with a + * signal. + * + * Current limitations: + * + * - While the kernel is stopped, interrupts are disabled for safety reasons + * (i.e., variables not changing magically or the like). But this also + * means that the clock isn't running anymore, and that interrupts from the + * hardware may get lost/not be served in time. This can cause some device + * errors... + * + * - When single-stepping, only one instruction of the current thread is + * executed, but interrupts are allowed for that time and will be serviced + * if pending. Be prepared for that. + * + * - All debugging happens in kernel virtual address space. There's no way to + * access physical memory not mapped in kernel space, or to access user + * space. A way to work around this is using get_user_long & Co. in gdb + * expressions, but only for the current process. + * + * - Interrupting the kernel only works if interrupts are currently allowed, + * and the interrupt of the serial line isn't blocked by some other means + * (IPL too high, disabled, ...) + * + * - The gdb stub is currently not reentrant, i.e. errors that happen therein + * (e.g. accesing invalid memory) may not be caught correctly. This could + * be removed in future by introducing a stack of struct registers. + * + */ + +/* + * To enable debugger support, two things need to happen. One, a + * call to kgdb_init() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * bBB..BB Set baud rate to BB..BB OK or BNN, then sets + * baud rate + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $<packet info>#<checksum>. + * + * where + * <packet info> :: <characters representing the command or response> + * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>> + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + */ + + +#include <linux/string.h> +#include <linux/signal.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/linkage.h> + +#include <asm/setup.h> +#include <asm/ptrace.h> + +#include <asm/svinto.h> +#include <asm/irq.h> + +static int kgdb_started = 0; + +/********************************* Register image ****************************/ +/* Use the order of registers as defined in "AXIS ETRAX CRIS Programmer's + Reference", p. 1-1, with the additional register definitions of the + ETRAX 100LX in cris-opc.h. + There are 16 general 32-bit registers, R0-R15, where R14 is the stack + pointer, SP, and R15 is the program counter, PC. + There are 16 special registers, P0-P15, where three of the unimplemented + registers, P0, P4 and P8, are reserved as zero-registers. A read from + any of these registers returns zero and a write has no effect. */ + +typedef +struct register_image +{ + /* Offset */ + unsigned int r0; /* 0x00 */ + unsigned int r1; /* 0x04 */ + unsigned int r2; /* 0x08 */ + unsigned int r3; /* 0x0C */ + unsigned int r4; /* 0x10 */ + unsigned int r5; /* 0x14 */ + unsigned int r6; /* 0x18 */ + unsigned int r7; /* 0x1C */ + unsigned int r8; /* 0x20 Frame pointer */ + unsigned int r9; /* 0x24 */ + unsigned int r10; /* 0x28 */ + unsigned int r11; /* 0x2C */ + unsigned int r12; /* 0x30 */ + unsigned int r13; /* 0x34 */ + unsigned int sp; /* 0x38 Stack pointer */ + unsigned int pc; /* 0x3C Program counter */ + + unsigned char p0; /* 0x40 8-bit zero-register */ + unsigned char vr; /* 0x41 Version register */ + + unsigned short p4; /* 0x42 16-bit zero-register */ + unsigned short ccr; /* 0x44 Condition code register */ + + unsigned int mof; /* 0x46 Multiply overflow register */ + + unsigned int p8; /* 0x4A 32-bit zero-register */ + unsigned int ibr; /* 0x4E Interrupt base register */ + unsigned int irp; /* 0x52 Interrupt return pointer */ + unsigned int srp; /* 0x56 Subroutine return pointer */ + unsigned int bar; /* 0x5A Breakpoint address register */ + unsigned int dccr; /* 0x5E Double condition code register */ + unsigned int brp; /* 0x62 Breakpoint return pointer (pc in caller) */ + unsigned int usp; /* 0x66 User mode stack pointer */ +} registers; + +/************** Prototypes for local library functions ***********************/ + +/* Copy of strcpy from libc. */ +static char *gdb_cris_strcpy (char *s1, const char *s2); + +/* Copy of strlen from libc. */ +static int gdb_cris_strlen (const char *s); + +/* Copy of memchr from libc. */ +static void *gdb_cris_memchr (const void *s, int c, int n); + +/* Copy of strtol from libc. Does only support base 16. */ +static int gdb_cris_strtol (const char *s, char **endptr, int base); + +/********************** Prototypes for local functions. **********************/ +/* Copy the content of a register image into another. The size n is + the size of the register image. Due to struct assignment generation of + memcpy in libc. */ +static void copy_registers (registers *dptr, registers *sptr, int n); + +/* Copy the stored registers from the stack. Put the register contents + of thread thread_id in the struct reg. */ +static void copy_registers_from_stack (int thread_id, registers *reg); + +/* Copy the registers to the stack. Put the register contents of thread + thread_id from struct reg to the stack. */ +static void copy_registers_to_stack (int thread_id, registers *reg); + +/* Write a value to a specified register regno in the register image + of the current thread. */ +static int write_register (int regno, char *val); + +/* Write a value to a specified register in the stack of a thread other + than the current thread. */ +static write_stack_register (int thread_id, int regno, char *valptr); + +/* Read a value from a specified register in the register image. Returns the + status of the read operation. The register value is returned in valptr. */ +static int read_register (char regno, unsigned int *valptr); + +/* Serial port, reads one character. ETRAX 100 specific. from debugport.c */ +int getDebugChar (void); + +/* Serial port, writes one character. ETRAX 100 specific. from debugport.c */ +void putDebugChar (int val); + +void enableDebugIRQ (void); + +/* Returns the character equivalent of a nibble, bit 7, 6, 5, and 4 of a byte, + represented by int x. */ +static char highhex (int x); + +/* Returns the character equivalent of a nibble, bit 3, 2, 1, and 0 of a byte, + represented by int x. */ +static char lowhex (int x); + +/* Returns the integer equivalent of a hexadecimal character. */ +static int hex (char ch); + +/* Convert the memory, pointed to by mem into hexadecimal representation. + Put the result in buf, and return a pointer to the last character + in buf (null). */ +static char *mem2hex (char *buf, unsigned char *mem, int count); + +/* Convert the array, in hexadecimal representation, pointed to by buf into + binary representation. Put the result in mem, and return a pointer to + the character after the last byte written. */ +static unsigned char *hex2mem (unsigned char *mem, char *buf, int count); + +/* Put the content of the array, in binary representation, pointed to by buf + into memory pointed to by mem, and return a pointer to + the character after the last byte written. */ +static unsigned char *bin2mem (unsigned char *mem, unsigned char *buf, int count); + +/* Await the sequence $<data>#<checksum> and store <data> in the array buffer + returned. */ +static void getpacket (char *buffer); + +/* Send $<data>#<checksum> from the <data> in the array buffer. */ +static void putpacket (char *buffer); + +/* Build and send a response packet in order to inform the host the + stub is stopped. */ +static void stub_is_stopped (int sigval); + +/* All expected commands are sent from remote.c. Send a response according + to the description in remote.c. */ +static void handle_exception (int sigval); + +/* Performs a complete re-start from scratch. ETRAX specific. */ +static void kill_restart (void); + +/******************** Prototypes for global functions. ***********************/ + +/* The string str is prepended with the GDB printout token and sent. */ +void putDebugString (const unsigned char *str, int length); /* used by etrax100ser.c */ + +/* The hook for both static (compiled) and dynamic breakpoints set by GDB. + ETRAX 100 specific. */ +void handle_breakpoint (void); /* used by irq.c */ + +/* The hook for an interrupt generated by GDB. ETRAX 100 specific. */ +void handle_interrupt (void); /* used by irq.c */ + +/* A static breakpoint to be used at startup. */ +void breakpoint (void); /* called by init/main.c */ + +/* From osys_int.c, executing_task contains the number of the current + executing task in osys. Does not know of object-oriented threads. */ +extern unsigned char executing_task; + +/* The number of characters used for a 64 bit thread identifier. */ +#define HEXCHARS_IN_THREAD_ID 16 + +/* Avoid warning as the internal_stack is not used in the C-code. */ +#define USEDVAR(name) { if (name) { ; } } +#define USEDFUN(name) { void (*pf)(void) = (void *)name; USEDVAR(pf) } + +/********************************** Packet I/O ******************************/ +/* BUFMAX defines the maximum number of characters in + inbound/outbound buffers */ +#define BUFMAX 512 + +/* Run-length encoding maximum length. Send 64 at most. */ +#define RUNLENMAX 64 + +/* Definition of all valid hexadecimal characters */ +static const char hexchars[] = "0123456789abcdef"; + +/* The inbound/outbound buffers used in packet I/O */ +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; + +/* Error and warning messages. */ +enum error_type +{ + SUCCESS, E01, E02, E03, E04, E05, E06, E07 +}; +static char *error_message[] = +{ + "", + "E01 Set current or general thread - H[c,g] - internal error.", + "E02 Change register content - P - cannot change read-only register.", + "E03 Thread is not alive.", /* T, not used. */ + "E04 The command is not supported - [s,C,S,!,R,d,r] - internal error.", + "E05 Change register content - P - the register is not implemented..", + "E06 Change memory content - M - internal error.", + "E07 Change register content - P - the register is not stored on the stack" +}; +/********************************* Register image ****************************/ +/* Use the order of registers as defined in "AXIS ETRAX CRIS Programmer's + Reference", p. 1-1, with the additional register definitions of the + ETRAX 100LX in cris-opc.h. + There are 16 general 32-bit registers, R0-R15, where R14 is the stack + pointer, SP, and R15 is the program counter, PC. + There are 16 special registers, P0-P15, where three of the unimplemented + registers, P0, P4 and P8, are reserved as zero-registers. A read from + any of these registers returns zero and a write has no effect. */ +enum register_name +{ + R0, R1, R2, R3, + R4, R5, R6, R7, + R8, R9, R10, R11, + R12, R13, SP, PC, + P0, VR, P2, P3, + P4, CCR, P6, MOF, + P8, IBR, IRP, SRP, + BAR, DCCR, BRP, USP +}; + +/* The register sizes of the registers in register_name. An unimplemented register + is designated by size 0 in this array. */ +static int register_size[] = +{ + 4, 4, 4, 4, + 4, 4, 4, 4, + 4, 4, 4, 4, + 4, 4, 4, 4, + 1, 1, 0, 0, + 2, 2, 0, 4, + 4, 4, 4, 4, + 4, 4, 4, 4 +}; + +/* Contains the register image of the executing thread in the assembler + part of the code in order to avoid horrible addressing modes. */ +static registers reg; + +/* FIXME: Should this be used? Delete otherwise. */ +/* Contains the assumed consistency state of the register image. Uses the + enum error_type for state information. */ +static int consistency_status = SUCCESS; + +/********************************** Handle exceptions ************************/ +/* The variable reg contains the register image associated with the + current_thread_c variable. It is a complete register image created at + entry. The reg_g contains a register image of a task where the general + registers are taken from the stack and all special registers are taken + from the executing task. It is associated with current_thread_g and used + in order to provide access mainly for 'g', 'G' and 'P'. +*/ + +/* Need two task id pointers in order to handle Hct and Hgt commands. */ +static int current_thread_c = 0; +static int current_thread_g = 0; + +/* Need two register images in order to handle Hct and Hgt commands. The + variable reg_g is in addition to reg above. */ +static registers reg_g; + +/********************************** Breakpoint *******************************/ +/* Use an internal stack in the breakpoint and interrupt response routines */ +#define INTERNAL_STACK_SIZE 1024 +static char internal_stack[INTERNAL_STACK_SIZE]; + +/* Due to the breakpoint return pointer, a state variable is needed to keep + track of whether it is a static (compiled) or dynamic (gdb-invoked) + breakpoint to be handled. A static breakpoint uses the content of register + BRP as it is whereas a dynamic breakpoint requires subtraction with 2 + in order to execute the instruction. The first breakpoint is static. */ +static unsigned char is_dyn_brkp = 0; + +/********************************* String library ****************************/ +/* Single-step over library functions creates trap loops. */ + +/* Copy char s2[] to s1[]. */ +static char* +gdb_cris_strcpy (char *s1, const char *s2) +{ + char *s = s1; + + for (s = s1; (*s++ = *s2++) != '\0'; ) + ; + return (s1); +} + +/* Find length of s[]. */ +static int +gdb_cris_strlen (const char *s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; sc++) + ; + return (sc - s); +} + +/* Find first occurrence of c in s[n]. */ +static void* +gdb_cris_memchr (const void *s, int c, int n) +{ + const unsigned char uc = c; + const unsigned char *su; + + for (su = s; 0 < n; ++su, --n) + if (*su == uc) + return ((void *)su); + return (NULL); +} +/******************************* Standard library ****************************/ +/* Single-step over library functions creates trap loops. */ +/* Convert string to long. */ +static int +gdb_cris_strtol (const char *s, char **endptr, int base) +{ + char *s1; + char *sd; + int x = 0; + + for (s1 = (char*)s; (sd = gdb_cris_memchr(hexchars, *s1, base)) != NULL; ++s1) + x = x * base + (sd - hexchars); + + if (endptr) + { + /* Unconverted suffix is stored in endptr unless endptr is NULL. */ + *endptr = s1; + } + + return x; +} + +int +double_this(int x) +{ + return 2 * x; +} + +/********************************* Register image ****************************/ +/* Copy the content of a register image into another. The size n is + the size of the register image. Due to struct assignment generation of + memcpy in libc. */ +static void +copy_registers (registers *dptr, registers *sptr, int n) +{ + unsigned char *dreg; + unsigned char *sreg; + + for (dreg = (unsigned char*)dptr, sreg = (unsigned char*)sptr; n > 0; n--) + *dreg++ = *sreg++; +} + +#ifdef PROCESS_SUPPORT +/* Copy the stored registers from the stack. Put the register contents + of thread thread_id in the struct reg. */ +static void +copy_registers_from_stack (int thread_id, registers *regptr) +{ + int j; + stack_registers *s = (stack_registers *)stack_list[thread_id]; + unsigned int *d = (unsigned int *)regptr; + + for (j = 13; j >= 0; j--) + *d++ = s->r[j]; + regptr->sp = (unsigned int)stack_list[thread_id]; + regptr->pc = s->pc; + regptr->dccr = s->dccr; + regptr->srp = s->srp; +} + +/* Copy the registers to the stack. Put the register contents of thread + thread_id from struct reg to the stack. */ +static void +copy_registers_to_stack (int thread_id, registers *regptr) +{ + int i; + stack_registers *d = (stack_registers *)stack_list[thread_id]; + unsigned int *s = (unsigned int *)regptr; + + for (i = 0; i < 14; i++) { + d->r[i] = *s++; + } + d->pc = regptr->pc; + d->dccr = regptr->dccr; + d->srp = regptr->srp; +} +#endif + +/* Write a value to a specified register in the register image of the current + thread. Returns status code SUCCESS, E02 or E05. */ +static int +write_register (int regno, char *val) +{ + int status = SUCCESS; + registers *current_reg = ® + + if (regno >= R0 && regno <= PC) { + /* 32-bit register with simple offset. */ + hex2mem ((unsigned char *)current_reg + regno * sizeof(unsigned int), + val, sizeof(unsigned int)); + } + else if (regno == P0 || regno == VR || regno == P4 || regno == P8) { + /* Do not support read-only registers. */ + status = E02; + } + else if (regno == CCR) { + /* 16 bit register with complex offset. (P4 is read-only, P6 is not implemented, + and P7 (MOF) is 32 bits in ETRAX 100LX. */ + hex2mem ((unsigned char *)&(current_reg->ccr) + (regno-CCR) * sizeof(unsigned short), + val, sizeof(unsigned short)); + } + else if (regno >= MOF && regno <= USP) { + /* 32 bit register with complex offset. (P8 has been taken care of.) */ + hex2mem ((unsigned char *)&(current_reg->ibr) + (regno-IBR) * sizeof(unsigned int), + val, sizeof(unsigned int)); + } + else { + /* Do not support nonexisting or unimplemented registers (P2, P3, and P6). */ + status = E05; + } + return status; +} + +#ifdef PROCESS_SUPPORT +/* Write a value to a specified register in the stack of a thread other + than the current thread. Returns status code SUCCESS or E07. */ +static int +write_stack_register (int thread_id, int regno, char *valptr) +{ + int status = SUCCESS; + stack_registers *d = (stack_registers *)stack_list[thread_id]; + unsigned int val; + + hex2mem ((unsigned char *)&val, valptr, sizeof(unsigned int)); + if (regno >= R0 && regno < SP) { + d->r[regno] = val; + } + else if (regno == SP) { + stack_list[thread_id] = val; + } + else if (regno == PC) { + d->pc = val; + } + else if (regno == SRP) { + d->srp = val; + } + else if (regno == DCCR) { + d->dccr = val; + } + else { + /* Do not support registers in the current thread. */ + status = E07; + } + return status; +} +#endif + +/* Read a value from a specified register in the register image. Returns the + value in the register or -1 for non-implemented registers. + Should check consistency_status after a call which may be E05 after changes + in the implementation. */ +static int +read_register (char regno, unsigned int *valptr) +{ + registers *current_reg = ® + + if (regno >= R0 && regno <= PC) { + /* 32-bit register with simple offset. */ + *valptr = *(unsigned int *)((char *)current_reg + regno * sizeof(unsigned int)); + return SUCCESS; + } + else if (regno == P0 || regno == VR) { + /* 8 bit register with complex offset. */ + *valptr = (unsigned int)(*(unsigned char *) + ((char *)&(current_reg->p0) + (regno-P0) * sizeof(char))); + return SUCCESS; + } + else if (regno == P4 || regno == CCR) { + /* 16 bit register with complex offset. */ + *valptr = (unsigned int)(*(unsigned short *) + ((char *)&(current_reg->p4) + (regno-P4) * sizeof(unsigned short))); + return SUCCESS; + } + else if (regno >= MOF && regno <= USP) { + /* 32 bit register with complex offset. */ + *valptr = *(unsigned int *)((char *)&(current_reg->p8) + + (regno-P8) * sizeof(unsigned int)); + return SUCCESS; + } + else { + /* Do not support nonexisting or unimplemented registers (P2, P3, and P6). */ + consistency_status = E05; + return E05; + } +} + +/********************************** Packet I/O ******************************/ +/* Returns the character equivalent of a nibble, bit 7, 6, 5, and 4 of a byte, + represented by int x. */ +static inline char +highhex(int x) +{ + return hexchars[(x >> 4) & 0xf]; +} + +/* Returns the character equivalent of a nibble, bit 3, 2, 1, and 0 of a byte, + represented by int x. */ +static inline char +lowhex(int x) +{ + return hexchars[x & 0xf]; +} + +/* Returns the integer equivalent of a hexadecimal character. */ +static int +hex (char ch) +{ + if ((ch >= 'a') && (ch <= 'f')) + return (ch - 'a' + 10); + if ((ch >= '0') && (ch <= '9')) + return (ch - '0'); + if ((ch >= 'A') && (ch <= 'F')) + return (ch - 'A' + 10); + return (-1); +} + +/* Convert the memory, pointed to by mem into hexadecimal representation. + Put the result in buf, and return a pointer to the last character + in buf (null). */ + +static int do_printk = 0; + +static char * +mem2hex(char *buf, unsigned char *mem, int count) +{ + int i; + int ch; + + if (mem == NULL) { + /* Bogus read from m0. FIXME: What constitutes a valid address? */ + for (i = 0; i < count; i++) { + *buf++ = '0'; + *buf++ = '0'; + } + } else { + /* Valid mem address. */ + for (i = 0; i < count; i++) { + ch = *mem++; + *buf++ = highhex (ch); + *buf++ = lowhex (ch); + } + } + + /* Terminate properly. */ + *buf = '\0'; + return (buf); +} + +/* Convert the array, in hexadecimal representation, pointed to by buf into + binary representation. Put the result in mem, and return a pointer to + the character after the last byte written. */ +static unsigned char* +hex2mem (unsigned char *mem, char *buf, int count) +{ + int i; + unsigned char ch; + for (i = 0; i < count; i++) { + ch = hex (*buf++) << 4; + ch = ch + hex (*buf++); + *mem++ = ch; + } + return (mem); +} + +/* Put the content of the array, in binary representation, pointed to by buf + into memory pointed to by mem, and return a pointer to the character after + the last byte written. + Gdb will escape $, #, and the escape char (0x7d). */ +static unsigned char* +bin2mem (unsigned char *mem, unsigned char *buf, int count) +{ + int i; + unsigned char *next; + for (i = 0; i < count; i++) { + /* Check for any escaped characters. Be paranoid and + only unescape chars that should be escaped. */ + if (*buf == 0x7d) { + next = buf + 1; + if (*next == 0x3 || *next == 0x4 || *next == 0x5D) /* #, $, ESC */ + { + buf++; + *buf += 0x20; + } + } + *mem++ = *buf++; + } + return (mem); +} + +/* Await the sequence $<data>#<checksum> and store <data> in the array buffer + returned. */ +static void +getpacket (char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + char ch; + do { + while ((ch = getDebugChar ()) != '$') + /* Wait for the start character $ and ignore all other characters */; + checksum = 0; + xmitcsum = -1; + count = 0; + /* Read until a # or the end of the buffer is reached */ + while (count < BUFMAX) { + ch = getDebugChar (); + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + buffer[count] = '\0'; + + if (ch == '#') { + xmitcsum = hex (getDebugChar ()) << 4; + xmitcsum += hex (getDebugChar ()); + if (checksum != xmitcsum) { + /* Wrong checksum */ + putDebugChar ('-'); + } + else { + /* Correct checksum */ + putDebugChar ('+'); + /* If sequence characters are received, reply with them */ + if (buffer[2] == ':') { + putDebugChar (buffer[0]); + putDebugChar (buffer[1]); + /* Remove the sequence characters from the buffer */ + count = gdb_cris_strlen (buffer); + for (i = 3; i <= count; i++) + buffer[i - 3] = buffer[i]; + } + } + } + } while (checksum != xmitcsum); +} + +/* Send $<data>#<checksum> from the <data> in the array buffer. */ + +static void +putpacket(char *buffer) +{ + int checksum; + int runlen; + int encode; + + do { + char *src = buffer; + putDebugChar ('$'); + checksum = 0; + while (*src) { + /* Do run length encoding */ + putDebugChar (*src); + checksum += *src; + runlen = 0; + while (runlen < RUNLENMAX && *src == src[runlen]) { + runlen++; + } + if (runlen > 3) { + /* Got a useful amount */ + putDebugChar ('*'); + checksum += '*'; + encode = runlen + ' ' - 4; + putDebugChar (encode); + checksum += encode; + src += runlen; + } + else { + src++; + } + } + putDebugChar ('#'); + putDebugChar (highhex (checksum)); + putDebugChar (lowhex (checksum)); + } while(kgdb_started && (getDebugChar() != '+')); +} + +/* The string str is prepended with the GDB printout token and sent. Required + in traditional implementations. */ +void +putDebugString (const unsigned char *str, int length) +{ + remcomOutBuffer[0] = 'O'; + mem2hex(&remcomOutBuffer[1], (unsigned char *)str, length); + putpacket(remcomOutBuffer); +} + +/********************************** Handle exceptions ************************/ +/* Build and send a response packet in order to inform the host the + stub is stopped. TAAn...:r...;n...:r...;n...:r...; + AA = signal number + n... = register number (hex) + r... = register contents + n... = `thread' + r... = thread process ID. This is a hex integer. + n... = other string not starting with valid hex digit. + gdb should ignore this n,r pair and go on to the next. + This way we can extend the protocol. */ +static void +stub_is_stopped(int sigval) +{ + char *ptr = remcomOutBuffer; + int regno; + + unsigned int reg_cont; + int status; + + /* Send trap type (converted to signal) */ + + *ptr++ = 'T'; + *ptr++ = highhex (sigval); + *ptr++ = lowhex (sigval); + + /* Send register contents. We probably only need to send the + * PC, frame pointer and stack pointer here. Other registers will be + * explicitely asked for. But for now, send all. + */ + + for (regno = R0; regno <= USP; regno++) { + /* Store n...:r...; for the registers in the buffer. */ + + status = read_register (regno, ®_cont); + + if (status == SUCCESS) { + + *ptr++ = highhex (regno); + *ptr++ = lowhex (regno); + *ptr++ = ':'; + + ptr = mem2hex(ptr, (unsigned char *)®_cont, + register_size[regno]); + *ptr++ = ';'; + } + + } + +#ifdef PROCESS_SUPPORT + /* Store the registers of the executing thread. Assume that both step, + continue, and register content requests are with respect to this + thread. The executing task is from the operating system scheduler. */ + + current_thread_c = executing_task; + current_thread_g = executing_task; + + /* A struct assignment translates into a libc memcpy call. Avoid + all libc functions in order to prevent recursive break points. */ + copy_registers (®_g, ®, sizeof(registers)); + + /* Store thread:r...; with the executing task TID. */ + gdb_cris_strcpy (&remcomOutBuffer[pos], "thread:"); + pos += gdb_cris_strlen ("thread:"); + remcomOutBuffer[pos++] = highhex (executing_task); + remcomOutBuffer[pos++] = lowhex (executing_task); + gdb_cris_strcpy (&remcomOutBuffer[pos], ";"); +#endif + + /* null-terminate and send it off */ + + *ptr = 0; + + putpacket (remcomOutBuffer); +} + +/* All expected commands are sent from remote.c. Send a response according + to the description in remote.c. */ +static void +handle_exception (int sigval) +{ + /* Avoid warning of not used. */ + + USEDFUN(handle_exception); + USEDVAR(internal_stack[0]); + + /* Send response. */ + + stub_is_stopped (sigval); + + for (;;) { + remcomOutBuffer[0] = '\0'; + getpacket (remcomInBuffer); + switch (remcomInBuffer[0]) { + case 'g': + /* Read registers: g + Success: Each byte of register data is described by two hex digits. + Registers are in the internal order for GDB, and the bytes + in a register are in the same order the machine uses. + Failure: void. */ + + { +#ifdef PROCESS_SUPPORT + /* Use the special register content in the executing thread. */ + copy_registers (®_g, ®, sizeof(registers)); + /* Replace the content available on the stack. */ + if (current_thread_g != executing_task) { + copy_registers_from_stack (current_thread_g, ®_g); + } + mem2hex ((unsigned char *)remcomOutBuffer, (unsigned char *)®_g, sizeof(registers)); +#else + mem2hex(remcomOutBuffer, (char *)®, sizeof(registers)); +#endif + } + break; + + case 'G': + /* Write registers. GXX..XX + Each byte of register data is described by two hex digits. + Success: OK + Failure: void. */ +#ifdef PROCESS_SUPPORT + hex2mem ((unsigned char *)®_g, &remcomInBuffer[1], sizeof(registers)); + if (current_thread_g == executing_task) { + copy_registers (®, ®_g, sizeof(registers)); + } + else { + copy_registers_to_stack(current_thread_g, ®_g); + } +#else + hex2mem((char *)®, &remcomInBuffer[1], sizeof(registers)); +#endif + gdb_cris_strcpy (remcomOutBuffer, "OK"); + break; + + case 'P': + /* Write register. Pn...=r... + Write register n..., hex value without 0x, with value r..., + which contains a hex value without 0x and two hex digits + for each byte in the register (target byte order). P1f=11223344 means + set register 31 to 44332211. + Success: OK + Failure: E02, E05 */ + { + char *suffix; + int regno = gdb_cris_strtol (&remcomInBuffer[1], &suffix, 16); + int status; +#ifdef PROCESS_SUPPORT + if (current_thread_g =! executing_task) + status = write_stack_register (current_thread_g, regno, suffix+1); + else +#endif + status = write_register (regno, suffix+1); + + switch (status) { + case E02: + /* Do not support read-only registers. */ + gdb_cris_strcpy (remcomOutBuffer, error_message[E02]); + break; + case E05: + /* Do not support non-existing registers. */ + gdb_cris_strcpy (remcomOutBuffer, error_message[E05]); + break; + case E07: + /* Do not support non-existing registers on the stack. */ + gdb_cris_strcpy (remcomOutBuffer, error_message[E07]); + break; + default: + /* Valid register number. */ + gdb_cris_strcpy (remcomOutBuffer, "OK"); + break; + } + } + break; + + case 'm': + /* Read from memory. mAA..AA,LLLL + AA..AA is the address and LLLL is the length. + Success: XX..XX is the memory content. Can be fewer bytes than + requested if only part of the data may be read. m6000120a,6c means + retrieve 108 byte from base address 6000120a. + Failure: void. */ + { + char *suffix; + unsigned char *addr = (unsigned char *)gdb_cris_strtol(&remcomInBuffer[1], + &suffix, 16); int length = gdb_cris_strtol(suffix+1, 0, 16); + + mem2hex(remcomOutBuffer, addr, length); + } + break; + + case 'X': + /* Write to memory. XAA..AA,LLLL:XX..XX + AA..AA is the start address, LLLL is the number of bytes, and + XX..XX is the binary data. + Success: OK + Failure: void. */ + case 'M': + /* Write to memory. MAA..AA,LLLL:XX..XX + AA..AA is the start address, LLLL is the number of bytes, and + XX..XX is the hexadecimal data. + Success: OK + Failure: void. */ + { + char *lenptr; + char *dataptr; + unsigned char *addr = (unsigned char *)gdb_cris_strtol(&remcomInBuffer[1], + &lenptr, 16); + int length = gdb_cris_strtol(lenptr+1, &dataptr, 16); + if (*lenptr == ',' && *dataptr == ':') { + if (remcomInBuffer[0] == 'M') { + hex2mem(addr, dataptr + 1, length); + } + else /* X */ { + bin2mem(addr, dataptr + 1, length); + } + gdb_cris_strcpy (remcomOutBuffer, "OK"); + } + else { + gdb_cris_strcpy (remcomOutBuffer, error_message[E06]); + } + } + break; + + case 'c': + /* Continue execution. cAA..AA + AA..AA is the address where execution is resumed. If AA..AA is + omitted, resume at the present address. + Success: return to the executing thread. + Failure: will never know. */ + if (remcomInBuffer[1] != '\0') { + reg.pc = gdb_cris_strtol (&remcomInBuffer[1], 0, 16); + } + enableDebugIRQ(); + return; + + case 's': + /* Step. sAA..AA + AA..AA is the address where execution is resumed. If AA..AA is + omitted, resume at the present address. Success: return to the + executing thread. Failure: will never know. + + Should never be invoked. The single-step is implemented on + the host side. If ever invoked, it is an internal error E04. */ + gdb_cris_strcpy (remcomOutBuffer, error_message[E04]); + putpacket (remcomOutBuffer); + return; + + case '?': + /* The last signal which caused a stop. ? + Success: SAA, where AA is the signal number. + Failure: void. */ + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = highhex (sigval); + remcomOutBuffer[2] = lowhex (sigval); + remcomOutBuffer[3] = 0; + break; + + case 'D': + /* Detach from host. D + Success: OK, and return to the executing thread. + Failure: will never know */ + putpacket ("OK"); + return; + + case 'k': + case 'r': + /* kill request or reset request. + Success: restart of target. + Failure: will never know. */ + kill_restart (); + break; + + case 'C': + case 'S': + case '!': + case 'R': + case 'd': + /* Continue with signal sig. Csig;AA..AA + Step with signal sig. Ssig;AA..AA + Use the extended remote protocol. ! + Restart the target system. R0 + Toggle debug flag. d + Search backwards. tAA:PP,MM + Not supported: E04 */ + gdb_cris_strcpy (remcomOutBuffer, error_message[E04]); + break; +#ifdef PROCESS_SUPPORT + + case 'T': + /* Thread alive. TXX + Is thread XX alive? + Success: OK, thread XX is alive. + Failure: E03, thread XX is dead. */ + { + int thread_id = (int)gdb_cris_strtol (&remcomInBuffer[1], 0, 16); + /* Cannot tell whether it is alive or not. */ + if (thread_id >= 0 && thread_id < number_of_tasks) + gdb_cris_strcpy (remcomOutBuffer, "OK"); + } + break; + + case 'H': + /* Set thread for subsequent operations: Hct + c = 'c' for thread used in step and continue; + t can be -1 for all threads. + c = 'g' for thread used in other operations. + t = 0 means pick any thread. + Success: OK + Failure: E01 */ + { + int thread_id = gdb_cris_strtol (&remcomInBuffer[2], 0, 16); + if (remcomInBuffer[1] == 'c') { + /* c = 'c' for thread used in step and continue */ + /* Do not change current_thread_c here. It would create a mess in + the scheduler. */ + gdb_cris_strcpy (remcomOutBuffer, "OK"); + } + else if (remcomInBuffer[1] == 'g') { + /* c = 'g' for thread used in other operations. + t = 0 means pick any thread. Impossible since the scheduler does + not allow that. */ + if (thread_id >= 0 && thread_id < number_of_tasks) { + current_thread_g = thread_id; + gdb_cris_strcpy (remcomOutBuffer, "OK"); + } + else { + /* Not expected - send an error message. */ + gdb_cris_strcpy (remcomOutBuffer, error_message[E01]); + } + } + else { + /* Not expected - send an error message. */ + gdb_cris_strcpy (remcomOutBuffer, error_message[E01]); + } + } + break; + + case 'q': + case 'Q': + /* Query of general interest. qXXXX + Set general value XXXX. QXXXX=yyyy */ + { + int pos; + int nextpos; + int thread_id; + + switch (remcomInBuffer[1]) { + case 'C': + /* Identify the remote current thread. */ + gdb_cris_strcpy (&remcomOutBuffer[0], "QC"); + remcomOutBuffer[2] = highhex (current_thread_c); + remcomOutBuffer[3] = lowhex (current_thread_c); + remcomOutBuffer[4] = '\0'; + break; + case 'L': + gdb_cris_strcpy (&remcomOutBuffer[0], "QM"); + /* Reply with number of threads. */ + if (os_is_started()) { + remcomOutBuffer[2] = highhex (number_of_tasks); + remcomOutBuffer[3] = lowhex (number_of_tasks); + } + else { + remcomOutBuffer[2] = highhex (0); + remcomOutBuffer[3] = lowhex (1); + } + /* Done with the reply. */ + remcomOutBuffer[4] = lowhex (1); + pos = 5; + /* Expects the argument thread id. */ + for (; pos < (5 + HEXCHARS_IN_THREAD_ID); pos++) + remcomOutBuffer[pos] = remcomInBuffer[pos]; + /* Reply with the thread identifiers. */ + if (os_is_started()) { + /* Store the thread identifiers of all tasks. */ + for (thread_id = 0; thread_id < number_of_tasks; thread_id++) { + nextpos = pos + HEXCHARS_IN_THREAD_ID - 1; + for (; pos < nextpos; pos ++) + remcomOutBuffer[pos] = lowhex (0); + remcomOutBuffer[pos++] = lowhex (thread_id); + } + } + else { + /* Store the thread identifier of the boot task. */ + nextpos = pos + HEXCHARS_IN_THREAD_ID - 1; + for (; pos < nextpos; pos ++) + remcomOutBuffer[pos] = lowhex (0); + remcomOutBuffer[pos++] = lowhex (current_thread_c); + } + remcomOutBuffer[pos] = '\0'; + break; + default: + /* Not supported: "" */ + /* Request information about section offsets: qOffsets. */ + remcomOutBuffer[0] = 0; + break; + } + } + break; +#endif /* PROCESS_SUPPORT */ + + default: + /* The stub should ignore other request and send an empty + response ($#<checksum>). This way we can extend the protocol and GDB + can tell whether the stub it is talking to uses the old or the new. */ + remcomOutBuffer[0] = 0; + break; + } + putpacket(remcomOutBuffer); + } +} + +/* The jump is to the address 0x00000002. Performs a complete re-start + from scratch. */ +static void +kill_restart () +{ + __asm__ volatile ("jump 2"); +} + +/********************************** Breakpoint *******************************/ +/* The hook for both a static (compiled) and a dynamic breakpoint set by GDB. + An internal stack is used by the stub. The register image of the caller is + stored in the structure register_image. + Interactive communication with the host is handled by handle_exception and + finally the register image is restored. */ + +void kgdb_handle_breakpoint(void); + +asm (" + .global _kgdb_handle_breakpoint +_kgdb_handle_breakpoint: +;; +;; Response to the break-instruction +;; +;; Create a register image of the caller +;; + move dccr,[_reg+0x5E] ; Save the flags in DCCR before disable interrupts + di ; Disable interrupts + move.d r0,[_reg] ; Save R0 + move.d r1,[_reg+0x04] ; Save R1 + move.d r2,[_reg+0x08] ; Save R2 + move.d r3,[_reg+0x0C] ; Save R3 + move.d r4,[_reg+0x10] ; Save R4 + move.d r5,[_reg+0x14] ; Save R5 + move.d r6,[_reg+0x18] ; Save R6 + move.d r7,[_reg+0x1C] ; Save R7 + move.d r8,[_reg+0x20] ; Save R8 + move.d r9,[_reg+0x24] ; Save R9 + move.d r10,[_reg+0x28] ; Save R10 + move.d r11,[_reg+0x2C] ; Save R11 + move.d r12,[_reg+0x30] ; Save R12 + move.d r13,[_reg+0x34] ; Save R13 + move.d sp,[_reg+0x38] ; Save SP (R14) +;; Due to the old assembler-versions BRP might not be recognized + .word 0xE670 ; move brp,r0 + subq 2,r0 ; Set to address of previous instruction. + move.d r0,[_reg+0x3c] ; Save the address in PC (R15) + clear.b [_reg+0x40] ; Clear P0 + move vr,[_reg+0x41] ; Save special register P1 + clear.w [_reg+0x42] ; Clear P4 + move ccr,[_reg+0x44] ; Save special register CCR + move mof,[_reg+0x46] ; P7 + clear.d [_reg+0x4A] ; Clear P8 + move ibr,[_reg+0x4E] ; P9, + move irp,[_reg+0x52] ; P10, + move srp,[_reg+0x56] ; P11, + move dtp0,[_reg+0x5A] ; P12, register BAR, assembler might not know BAR + ; P13, register DCCR already saved +;; Due to the old assembler-versions BRP might not be recognized + .word 0xE670 ; move brp,r0 +;; Static (compiled) breakpoints must return to the next instruction in order +;; to avoid infinite loops. Dynamic (gdb-invoked) must restore the instruction +;; in order to execute it when execution is continued. + test.b [_is_dyn_brkp] ; Is this a dynamic breakpoint? + beq is_static ; No, a static breakpoint + nop + subq 2,r0 ; rerun the instruction the break replaced +is_static: + moveq 1,r1 + move.b r1,[_is_dyn_brkp] ; Set the state variable to dynamic breakpoint + move.d r0,[_reg+0x62] ; Save the return address in BRP + move usp,[_reg+0x66] ; USP +;; +;; Handle the communication +;; + move.d _internal_stack+1020,sp ; Use the internal stack which grows upward + moveq 5,r10 ; SIGTRAP + jsr _handle_exception ; Interactive routine +;; +;; Return to the caller +;; + move.d [_reg],r0 ; Restore R0 + move.d [_reg+0x04],r1 ; Restore R1 + move.d [_reg+0x08],r2 ; Restore R2 + move.d [_reg+0x0C],r3 ; Restore R3 + move.d [_reg+0x10],r4 ; Restore R4 + move.d [_reg+0x14],r5 ; Restore R5 + move.d [_reg+0x18],r6 ; Restore R6 + move.d [_reg+0x1C],r7 ; Restore R7 + move.d [_reg+0x20],r8 ; Restore R8 + move.d [_reg+0x24],r9 ; Restore R9 + move.d [_reg+0x28],r10 ; Restore R10 + move.d [_reg+0x2C],r11 ; Restore R11 + move.d [_reg+0x30],r12 ; Restore R12 + move.d [_reg+0x34],r13 ; Restore R13 +;; +;; FIXME: Which registers should be restored? +;; + move.d [_reg+0x38],sp ; Restore SP (R14) + move [_reg+0x56],srp ; Restore the subroutine return pointer. + move [_reg+0x5E],dccr ; Restore DCCR + move [_reg+0x66],usp ; Restore USP + jump [_reg+0x62] ; A jump to the content in register BRP works. + nop ; +"); + +/* The hook for an interrupt generated by GDB. An internal stack is used + by the stub. The register image of the caller is stored in the structure + register_image. Interactive communication with the host is handled by + handle_exception and finally the register image is restored. Due to the + old assembler which does not recognise the break instruction and the + breakpoint return pointer hex-code is used. */ + +void kgdb_handle_serial(void); + +asm (" + .global _kgdb_handle_serial +_kgdb_handle_serial: +;; +;; Response to a serial interrupt +;; + + move dccr,[_reg+0x5E] ; Save the flags in DCCR + di ; Disable interrupts + move.d r0,[_reg] ; Save R0 + move.d r1,[_reg+0x04] ; Save R1 + move.d r2,[_reg+0x08] ; Save R2 + move.d r3,[_reg+0x0C] ; Save R3 + move.d r4,[_reg+0x10] ; Save R4 + move.d r5,[_reg+0x14] ; Save R5 + move.d r6,[_reg+0x18] ; Save R6 + move.d r7,[_reg+0x1C] ; Save R7 + move.d r8,[_reg+0x20] ; Save R8 + move.d r9,[_reg+0x24] ; Save R9 + move.d r10,[_reg+0x28] ; Save R10 + move.d r11,[_reg+0x2C] ; Save R11 + move.d r12,[_reg+0x30] ; Save R12 + move.d r13,[_reg+0x34] ; Save R13 + move.d sp,[_reg+0x38] ; Save SP (R14) + move irp,[_reg+0x3c] ; Save the address in PC (R15) + clear.b [_reg+0x40] ; Clear P0 + move vr,[_reg+0x41] ; Save special register P1, + clear.w [_reg+0x42] ; Clear P4 + move ccr,[_reg+0x44] ; Save special register CCR + move mof,[_reg+0x46] ; P7 + clear.d [_reg+0x4A] ; Clear P8 + move ibr,[_reg+0x4E] ; P9, + move irp,[_reg+0x52] ; P10, + move srp,[_reg+0x56] ; P11, + move dtp0,[_reg+0x5A] ; P12, register BAR, assembler might not know BAR + ; P13, register DCCR already saved +;; Due to the old assembler-versions BRP might not be recognized + .word 0xE670 ; move brp,r0 + move.d r0,[_reg+0x62] ; Save the return address in BRP + move usp,[_reg+0x66] ; USP + +;; get the serial character (from debugport.c) and check if its a ctrl-c + + jsr _getDebugChar + cmp.b 3, r10 + bne goback + nop + +;; +;; Handle the communication +;; + move.d _internal_stack+1020,sp ; Use the internal stack + moveq 2,r10 ; SIGINT + jsr _handle_exception ; Interactive routine + +goback: +;; +;; Return to the caller +;; + move.d [_reg],r0 ; Restore R0 + move.d [_reg+0x04],r1 ; Restore R1 + move.d [_reg+0x08],r2 ; Restore R2 + move.d [_reg+0x0C],r3 ; Restore R3 + move.d [_reg+0x10],r4 ; Restore R4 + move.d [_reg+0x14],r5 ; Restore R5 + move.d [_reg+0x18],r6 ; Restore R6 + move.d [_reg+0x1C],r7 ; Restore R7 + move.d [_reg+0x20],r8 ; Restore R8 + move.d [_reg+0x24],r9 ; Restore R9 + move.d [_reg+0x28],r10 ; Restore R10 + move.d [_reg+0x2C],r11 ; Restore R11 + move.d [_reg+0x30],r12 ; Restore R12 + move.d [_reg+0x34],r13 ; Restore R13 +;; +;; FIXME: Which registers should be restored? +;; + move.d [_reg+0x38],sp ; Restore SP (R14) + move [_reg+0x56],srp ; Restore the subroutine return pointer. + move [_reg+0x5E],dccr ; Restore DCCR + move [_reg+0x66],usp ; Restore USP + reti ; Return from the interrupt routine + nop +"); + +/* Use this static breakpoint in the start-up only. */ + +void +breakpoint(void) +{ + kgdb_started = 1; + is_dyn_brkp = 0; /* This is a static, not a dynamic breakpoint. */ + __asm__ volatile ("break 8"); /* Jump to handle_breakpoint. */ +} + +/* initialize kgdb. doesn't break into the debugger, but sets up irq and ports */ + +void +kgdb_init(void) +{ + /* could initialize debug port as well but it's done in head.S already... */ + + set_break_vector(8, kgdb_handle_breakpoint); + set_int_vector(8, kgdb_handle_serial, 0); + + enableDebugIRQ(); +} + +/****************************** End of file **********************************/ diff --git a/arch/cris/kernel/ksyms.c b/arch/cris/kernel/ksyms.c new file mode 100644 index 000000000..fa48796dc --- /dev/null +++ b/arch/cris/kernel/ksyms.c @@ -0,0 +1,2 @@ +/* no kernel support yet */ + diff --git a/arch/cris/kernel/process.c b/arch/cris/kernel/process.c new file mode 100644 index 000000000..901449879 --- /dev/null +++ b/arch/cris/kernel/process.c @@ -0,0 +1,327 @@ +/* $Id: process.c,v 1.8 2000/09/13 14:34:13 bjornw Exp $ + * + * linux/arch/cris/kernel/process.c + * + * Copyright (C) 1995 Linus Torvalds + * Copyright (C) 2000 Axis Communications AB + * + * Authors: Bjorn Wesen (bjornw@axis.com) + * + */ + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#define __KERNEL_SYSCALLS__ +#include <stdarg.h> + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/interrupt.h> +#include <linux/delay.h> + +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/io.h> +#include <linux/smp.h> + +//#define DEBUG + +/* + * Initial task structure. Make this a per-architecture thing, + * because different architectures tend to have different + * alignment requirements and potentially different initial + * setup. + */ + +static struct vm_area_struct init_mmap = INIT_MMAP; +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS; +struct mm_struct init_mm = INIT_MM(init_mm); + +/* + * Initial task structure. + * + * We need to make sure that this is 8192-byte aligned due to the + * way process stacks are handled. This is done by having a special + * "init_task" linker map entry.. + */ + +union task_union init_task_union + __attribute__((__section__(".data.init_task"))) = + { INIT_TASK(init_task_union.task) }; + +static int hlt_counter=0; + +/* in a system call, set_esp0 is called to remember the stack frame, therefore + in the implementation of syscalls we can use that value to access the stack + frame and saved registers. +*/ + +#define currentregs ((struct pt_regs *)current->thread.esp0) + +asmlinkage void set_esp0(unsigned long ssp) +{ + current->thread.esp0 = ssp; +} + +void disable_hlt(void) +{ + hlt_counter++; +} + +void enable_hlt(void) +{ + hlt_counter--; +} + +int cpu_idle(void *unused) +{ + while(1) { + current->counter = -100; + schedule(); + } +} + +/* if the watchdog is enabled, we can simply disable interrupts and go + * into an eternal loop, and the watchdog will reset the CPU after 0.1s + */ + +void hard_reset_now (void) +{ + printk("*** HARD RESET ***\n"); + cli(); + while(1) /* waiting for RETRIBUTION! */ ; +} + +void machine_restart(void) +{ + hard_reset_now(); +} + +/* can't do much here... */ + +void machine_halt(void) +{ +} + +void machine_power_off(void) +{ +} + +/* + * This is the mechanism for creating a new kernel thread. + * + * NOTE! Only a kernel-only process(ie the swapper or direct descendants + * who haven't done an "execve()") should use this: it will work within + * a system call from a "real" process, but the process memory space will + * not be free'd until both the parent and the child have exited. + */ + +int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + register long __a __asm__ ("r10"); + + __asm__ __volatile__ + ("movu.w %1,r1\n\t" /* r1 contains syscall number, to sys_clone */ + "clear.d r10\n\t" /* r10 is argument 1 to clone */ + "move.d %2,r11\n\t" /* r11 is argument 2 to clone, the flags */ + "break 13\n\t" /* call sys_clone, this will fork */ + "test.d r10\n\t" /* parent or child? child returns 0 here. */ + "bne 1f\n\t" /* jump if parent */ + "nop\n\t" /* delay slot */ + "move.d %4,r10\n\t" /* set argument to function to call */ + "jsr %5\n\t" /* call specified function */ + "movu.w %3,r1\n\t" /* r1 is sys_exit syscall number */ + "moveq -1,r10\n\t" /* Give a really bad exit-value */ + "break 13\n\t" /* call sys_exit, killing the child */ + "1:\n\t" + : "=r" (__a) + : "g" (__NR_clone), "r" (flags | CLONE_VM), "g" (__NR_exit), + "r" (arg), "r" (fn) + : "r10", "r11", "r1"); + + return __a; +} + + + +void flush_thread(void) +{ +} + +asmlinkage void ret_from_sys_call(void); + +/* setup the child's kernel stack with a pt_regs and switch_stack on it. + * it will be un-nested 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. + * + */ + +int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, + unsigned long unused, + struct task_struct *p, struct pt_regs *regs) +{ + struct pt_regs * childregs; + struct switch_stack *swstack; + + /* put the pt_regs structure at the end of the new kernel stack page and fix it up + * remember that the task_struct doubles as the kernel stack for the task + */ + + childregs = ((struct pt_regs *) ((unsigned long)p + THREAD_SIZE)) - 1; + + *childregs = *regs; /* struct copy of pt_regs */ + + childregs->r10 = 0; /* child returns 0 after a fork/clone */ + + /* put the switch stack right below the pt_regs */ + + swstack = ((struct switch_stack *)childregs) - 1; + + swstack->r9 = 0; /* parameter to ret_from_sys_call, 0 == dont restart the syscall */ + + /* we want to return into ret_from_sys_call after the _resume */ + + swstack->return_ip = (unsigned long) ret_from_sys_call; + + /* fix the user-mode stackpointer */ + + p->thread.usp = usp; + + /* and the kernel-mode one */ + + p->thread.ksp = (unsigned long) swstack; + + /* esp0 keeps the pt_regs stacked structure pointer */ + + p->thread.esp0 = (unsigned long) childregs; + +#ifdef DEBUG + printk("kern_stack_page 0x%x, used stack %d, thread.usp 0x%x, usp 0x%x\n", + current->kernel_stack_page, usedstack, p->thread.usp, usp); +#endif + return 0; +} + +/* + * fill in the user structure for a core dump.. + */ +void dump_thread(struct pt_regs * regs, struct user * dump) +{ + int i; +#if 0 +/* changed the size calculations - should hopefully work better. lbt */ + dump->magic = CMAGIC; + dump->start_code = 0; + dump->start_stack = regs->esp & ~(PAGE_SIZE - 1); + dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT; + dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT; + dump->u_dsize -= dump->u_tsize; + dump->u_ssize = 0; + for (i = 0; i < 8; i++) + dump->u_debugreg[i] = current->debugreg[i]; + + if (dump->start_stack < TASK_SIZE) + dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT; + + dump->regs = *regs; + + dump->u_fpvalid = dump_fpu (regs, &dump->i387); +#endif +} + +asmlinkage int sys_fork(void) +{ + return do_fork(SIGCHLD, rdusp(), currentregs, 0); +} + +/* if newusp is 0, we just grab the old usp */ + +asmlinkage int sys_clone(unsigned long newusp, unsigned long flags) +{ + if (!newusp) + newusp = rdusp(); + return do_fork(flags, newusp, currentregs, 0); +} + +/* vfork is a system call in i386 because of register-pressure - maybe + * we can remove it and handle it in libc but we put it here until then. + */ + +asmlinkage int sys_vfork(void) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), currentregs, 0); +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage int sys_execve(const char *fname, char **argv, char **envp) +{ + int error; + char *filename; + + filename = getname(fname); + error = PTR_ERR(filename); + + if (IS_ERR(filename)) + goto out; + error = do_execve(filename, argv, envp, currentregs); + putname(filename); + out: + return error; +} + +/* + * These bracket the sleeping functions.. + */ + +extern void scheduling_functions_start_here(void); +extern void scheduling_functions_end_here(void); +#define first_sched ((unsigned long) scheduling_functions_start_here) +#define last_sched ((unsigned long) scheduling_functions_end_here) + +unsigned long get_wchan(struct task_struct *p) +{ +#if 0 + /* YURGH. TODO. */ + + unsigned long ebp, esp, eip; + unsigned long stack_page; + int count = 0; + if (!p || p == current || p->state == TASK_RUNNING) + return 0; + stack_page = (unsigned long)p; + esp = p->thread.esp; + if (!stack_page || esp < stack_page || esp > 8188+stack_page) + return 0; + /* include/asm-i386/system.h:switch_to() pushes ebp last. */ + ebp = *(unsigned long *) esp; + do { + if (ebp < stack_page || ebp > 8184+stack_page) + return 0; + eip = *(unsigned long *) (ebp+4); + if (eip < first_sched || eip >= last_sched) + return eip; + ebp = *(unsigned long *) ebp; + } while (count++ < 16); +#endif + return 0; +} +#undef last_sched +#undef first_sched diff --git a/arch/cris/kernel/ptrace.c b/arch/cris/kernel/ptrace.c new file mode 100644 index 000000000..384952211 --- /dev/null +++ b/arch/cris/kernel/ptrace.c @@ -0,0 +1,340 @@ +/* + * linux/arch/cris/kernel/ptrace.c + * + * Parts taken from the m68k port. + * + * Copyright (c) 2000 Axis Communications AB + * + * Authors: Bjorn Wesen + * + * $Log: ptrace.c,v $ + * Revision 1.3 2000/12/18 23:45:25 bjornw + * Linux/CRIS first version + * + * + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/user.h> + +#include <asm/uaccess.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/processor.h> + +/* + * does not yet catch signals sent when the child dies. + * in exit.c or in signal.c. + */ + +/* determines which bits in DCCR the user has access to. */ +/* 1 = access 0 = no access */ +#define DCCR_MASK 0x0000001f /* XNZVC */ + +/* + * Get contents of register REGNO in task TASK. + */ +static inline 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 + */ + + if (regno == PT_USP) + return task->thread.usp; + else if (regno <= PT_MAX) + return ((unsigned long *)(task->thread.esp0))[regno]; + else + return 0; +} + +/* + * Write contents of register REGNO in task TASK. + */ +static inline int put_reg(struct task_struct *task, unsigned int regno, + unsigned long data) +{ + unsigned long *addr; + + if (regno == PT_USP) + task->thread.usp = data; + else if (regno <= PT_MAX) + ((unsigned long *)(task->thread.esp0))[regno] = data; + else + return -1; + return 0; +} + +asmlinkage int sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int ret; + + lock_kernel(); + ret = -EPERM; + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->ptrace & PT_PTRACED) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; + } + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (!child) + goto out; + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out_tsk; + if (request == PTRACE_ATTACH) { + if (child == current) + goto out_tsk; + if ((!child->dumpable || + (current->uid != child->euid) || + (current->uid != child->suid) || + (current->uid != child->uid) || + (current->gid != child->egid) || + (current->gid != child->sgid) || + (!cap_issubset(child->cap_permitted, current->cap_permitted)) || + (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) + goto out_tsk; + /* the same process cannot be attached many times */ + if (child->ptrace & PT_PTRACED) + goto out_tsk; + child->ptrace |= PT_PTRACED; + + write_lock_irq(&tasklist_lock); + if (child->p_pptr != current) { + REMOVE_LINKS(child); + child->p_pptr = current; + SET_LINKS(child); + } + write_unlock_irq(&tasklist_lock); + + send_sig(SIGSTOP, child, 1); + ret = 0; + goto out_tsk; + } + ret = -ESRCH; + if (!(child->ptrace & PT_PTRACED)) + goto out_tsk; + if (child->state != TASK_STOPPED) { + if (request != PTRACE_KILL) + goto out_tsk; + } + if (child->p_pptr != current) + goto out_tsk; + + switch (request) { + /* when I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: { + unsigned long tmp; + int copied; + + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + if (copied != sizeof(tmp)) + break; + ret = put_user(tmp,(unsigned long *) data); + break; + } + + /* read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: { + unsigned long tmp; + + ret = -EIO; + if ((addr & 3) || addr < 0 || addr >= sizeof(struct user)) + break; + + tmp = 0; /* Default return condition */ + ret = -EIO; + if (addr < sizeof(struct pt_regs)) { + tmp = get_reg(child, addr >> 2); + ret = put_user(tmp, (unsigned long *)data); + } + break; + } + + /* when I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + ret = 0; + if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) + break; + ret = -EIO; + break; + + case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ + ret = -EIO; + if ((addr & 3) || addr < 0 || addr >= sizeof(struct user)) + break; + + if (addr < sizeof(struct pt_regs)) { + addr >>= 2; + + if (addr == PT_DCCR) { + /* don't allow the tracing process to change stuff like + * interrupt enable, kernel/user bit, dma enables etc. + */ + data &= DCCR_MASK; + data |= get_reg(child, PT_DCCR) & ~DCCR_MASK; + } + if (put_reg(child, addr, data)) + break; + ret = 0; + } + break; + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: { /* restart after signal. */ + long tmp; + + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + if (request == PTRACE_SYSCALL) + child->ptrace |= PT_TRACESYS; + else + child->ptrace &= ~PT_TRACESYS; + child->exit_code = data; + /* TODO: make sure any pending breakpoint is killed */ + wake_up_process(child); + ret = 0; + break; + } + +/* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: { + long tmp; + + ret = 0; + if (child->state == TASK_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + /* TODO: make sure any pending breakpoint is killed */ + wake_up_process(child); + break; + } + + case PTRACE_SINGLESTEP: { /* set the trap flag. */ + long tmp; + + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + child->ptrace &= ~PT_TRACESYS; + + /* TODO: set some clever breakpoint mechanism... */ + + child->exit_code = data; + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + break; + } + + case PTRACE_DETACH: { /* detach a process that was attached. */ + long tmp; + + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + child->ptrace &= ~(PT_PTRACED | PT_TRACESYS); + child->exit_code = data; + write_lock_irq(&tasklist_lock); + REMOVE_LINKS(child); + child->p_pptr = child->p_opptr; + SET_LINKS(child); + write_unlock_irq(&tasklist_lock); + /* TODO: make sure any pending breakpoint is killed */ + wake_up_process(child); + ret = 0; + break; + } + + case PTRACE_GETREGS: { /* Get all gp regs from the child. */ + int i; + unsigned long tmp; + for (i = 0; i <= PT_MAX; i++) { + tmp = get_reg(child, i); + if (put_user(tmp, (unsigned long *) data)) { + ret = -EFAULT; + break; + } + data += sizeof(long); + } + ret = 0; + break; + } + + case PTRACE_SETREGS: { /* Set all gp regs in the child. */ + int i; + unsigned long tmp; + for (i = 0; i <= PT_MAX; i++) { + if (get_user(tmp, (unsigned long *) data)) { + ret = -EFAULT; + break; + } + if (i == PT_DCCR) { + tmp &= DCCR_MASK; + tmp |= get_reg(child, PT_DCCR) & ~DCCR_MASK; + } + put_reg(child, i, tmp); + data += sizeof(long); + } + ret = 0; + break; + } + + default: + ret = -EIO; + break; + } +out_tsk: + free_task_struct(child); +out: + unlock_kernel(); + return ret; +} + +asmlinkage void syscall_trace(void) +{ + if ((current->ptrace & (PT_PTRACED | PT_TRACESYS)) != + (PT_PTRACED | PT_TRACESYS)) + return; + /* TODO: make a way to distinguish between a syscall stop and SIGTRAP + * delivery like in the i386 port ? + */ + current->exit_code = SIGTRAP; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} diff --git a/arch/cris/kernel/semaphore.c b/arch/cris/kernel/semaphore.c new file mode 100644 index 000000000..5a9478f03 --- /dev/null +++ b/arch/cris/kernel/semaphore.c @@ -0,0 +1,238 @@ +/* + * Generic semaphore code. Buyer beware. Do your own + * specific changes in <asm/semaphore-helper.h> + */ + +#include <linux/sched.h> +#include <asm/semaphore-helper.h> + +/* + * Semaphores are implemented using a two-way counter: + * The "count" variable is decremented for each process + * that tries to sleep, while the "waking" variable is + * incremented when the "up()" code goes to wake up waiting + * processes. + * + * Notably, the inline "up()" and "down()" functions can + * efficiently test if they need to do any extra work (up + * needs to do something only if count was negative before + * the increment operation. + * + * waking_non_zero() (from asm/semaphore.h) must execute + * atomically. + * + * When __up() is called, the count was negative before + * incrementing it, and we need to wake up somebody. + * + * This routine adds one to the count of processes that need to + * wake up and exit. ALL waiting processes actually wake up but + * only the one that gets to the "waking" field first will gate + * through and acquire the semaphore. The others will go back + * to sleep. + * + * Note that these functions are only called when there is + * contention on the lock, and as such all this is the + * "non-critical" part of the whole semaphore business. The + * critical part is the inline stuff in <asm/semaphore.h> + * where we want to avoid any extra jumps and calls. + */ +void __up(struct semaphore *sem) +{ + wake_one_more(sem); + wake_up(&sem->wait); +} + +/* + * Perform the "down" function. Return zero for semaphore acquired, + * return negative for signalled out of the function. + * + * If called from __down, the return is ignored and the wait loop is + * not interruptible. This means that a task waiting on a semaphore + * using "down()" cannot be killed until someone does an "up()" on + * the semaphore. + * + * If called from __down_interruptible, the return value gets checked + * upon return. If the return value is negative then the task continues + * with the negative value in the return register (it can be tested by + * the caller). + * + * Either form may be used in conjunction with "up()". + * + */ + +#define DOWN_VAR \ + struct task_struct *tsk = current; \ + wait_queue_t wait; \ + init_waitqueue_entry(&wait, tsk); + +#define DOWN_HEAD(task_state) \ + \ + \ + tsk->state = (task_state); \ + add_wait_queue(&sem->wait, &wait); \ + \ + /* \ + * Ok, we're set up. sem->count is known to be less than zero \ + * so we must wait. \ + * \ + * We can let go the lock for purposes of waiting. \ + * We re-acquire it after awaking so as to protect \ + * all semaphore operations. \ + * \ + * If "up()" is called before we call waking_non_zero() then \ + * we will catch it right away. If it is called later then \ + * we will have to go through a wakeup cycle to catch it. \ + * \ + * Multiple waiters contend for the semaphore lock to see \ + * who gets to gate through and who has to wait some more. \ + */ \ + for (;;) { + +#define DOWN_TAIL(task_state) \ + tsk->state = (task_state); \ + } \ + tsk->state = TASK_RUNNING; \ + remove_wait_queue(&sem->wait, &wait); + +void __down(struct semaphore * sem) +{ + DOWN_VAR + DOWN_HEAD(TASK_UNINTERRUPTIBLE) + if (waking_non_zero(sem)) + break; + schedule(); + DOWN_TAIL(TASK_UNINTERRUPTIBLE) +} + +int __down_interruptible(struct semaphore * sem) +{ + int ret = 0; + DOWN_VAR + DOWN_HEAD(TASK_INTERRUPTIBLE) + + ret = waking_non_zero_interruptible(sem, tsk); + if (ret) + { + if (ret == 1) + /* ret != 0 only if we get interrupted -arca */ + ret = 0; + break; + } + schedule(); + DOWN_TAIL(TASK_INTERRUPTIBLE) + return ret; +} + +int __down_trylock(struct semaphore * sem) +{ + return waking_non_zero_trylock(sem); +} + +/* + * RW Semaphores + */ +void +__down_read(struct rw_semaphore *sem, int count) +{ + DOWN_VAR; + + retry_down: + if (count < 0) { + /* Wait for the lock to become unbiased. Readers + are non-exclusive. */ + + /* This takes care of granting the lock. */ + up_read(sem); + + add_wait_queue(&sem->wait, &wait); + while (atomic_read(&sem->count) < 0) { + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if (atomic_read(&sem->count) >= 0) + break; + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; + + mb(); + count = atomic_dec_return(&sem->count); + if (count <= 0) + goto retry_down; + } else { + add_wait_queue(&sem->wait, &wait); + + while (1) { + if (test_and_clear_bit(0, &sem->granted)) + break; + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if ((sem->granted & 1) == 0) + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; + } +} + +void +__down_write(struct rw_semaphore *sem, int count) +{ + DOWN_VAR; + + retry_down: + if (count + RW_LOCK_BIAS < 0) { + up_write(sem); + + add_wait_queue_exclusive(&sem->wait, &wait); + + while (atomic_read(&sem->count) < 0) { + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if (atomic_read(&sem->count) >= RW_LOCK_BIAS) + break; + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; + + mb(); + count = atomic_sub_return(RW_LOCK_BIAS, &sem->count); + if (count != 0) + goto retry_down; + } else { + /* Put ourselves at the end of the list. */ + add_wait_queue_exclusive(&sem->write_bias_wait, &wait); + + while (1) { + if (test_and_clear_bit(1, &sem->granted)) + break; + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if ((sem->granted & 2) == 0) + schedule(); + } + + remove_wait_queue(&sem->write_bias_wait, &wait); + tsk->state = TASK_RUNNING; + + /* If the lock is currently unbiased, awaken the sleepers. + FIXME: This wakes up the readers early in a bit of a + stampede -> bad! */ + if (atomic_read(&sem->count) >= 0) + wake_up(&sem->wait); + } +} + +void +__rwsem_wake(struct rw_semaphore *sem, unsigned long readers) +{ + if (readers) { + if (test_and_set_bit(0, &sem->granted)) + BUG(); + wake_up(&sem->wait); + } else { + if (test_and_set_bit(1, &sem->granted)) + BUG(); + wake_up(&sem->write_bias_wait); + } +} diff --git a/arch/cris/kernel/setup.c b/arch/cris/kernel/setup.c new file mode 100644 index 000000000..ff7b3e6ac --- /dev/null +++ b/arch/cris/kernel/setup.c @@ -0,0 +1,264 @@ +/* $Id: setup.c,v 1.8 2001/01/16 16:31:38 bjornw Exp $ + * + * linux/arch/cris/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Copyright (c) 2000 Axis Communications AB + */ + +/* + * This file handles the architecture-dependent parts of initialization + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/tty.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/config.h> +#include <linux/init.h> +#include <linux/bootmem.h> + +#include <asm/segment.h> +#include <asm/system.h> +#include <asm/smp.h> +#include <asm/types.h> +#include <asm/svinto.h> + +/* + * Setup options + */ +struct drive_info_struct { char dummy[32]; } drive_info; +struct screen_info screen_info; + +unsigned char aux_device_present; + +#ifdef CONFIG_BLK_DEV_RAM +extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ +extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ +extern int rd_image_start; /* starting block # of image */ +#endif + +extern int root_mountflags; +extern char _etext, _edata, _end; + +#define COMMAND_LINE_SIZE 256 + +static char command_line[COMMAND_LINE_SIZE] = { 0, }; + char saved_command_line[COMMAND_LINE_SIZE]; + +extern const unsigned long text_start, edata; /* set by the linker script */ + +extern unsigned long romfs_start, romfs_length, romfs_in_flash; /* from head.S */ + +/* This mainly sets up the memory area, and can be really confusing. + * + * The physical DRAM is virtually mapped into dram_start to dram_end + * (usually c0000000 to c0000000 + DRAM size). The physical address is + * given by the macro __pa(). + * + * In this DRAM, the kernel code and data is loaded, in the beginning. + * It really starts at c00a0000 to make room for some special pages - + * the start address is text_start. The kernel data ends at _end. After + * this the ROM filesystem is appended (if there is any). + * + * Between this address and dram_end, we have RAM pages usable to the + * boot code and the system. + * + */ + +void __init setup_arch(char **cmdline_p) +{ + unsigned long bootmap_size; + unsigned long start_pfn, max_pfn; + unsigned long memory_start; + extern void console_print_etrax(const char *b); + +#if (defined(CONFIG_CHR_DEV_FLASH) || defined(CONFIG_BLK_DEV_FLASH)) + /* TODO: move this into flash_init I think */ + flash_probe(); +#endif + + /* register an initial console printing routine for printk's */ + + init_etrax_debug(); + + /* we should really poll for DRAM size! */ + + high_memory = &dram_end; + + if(romfs_in_flash || !romfs_length) { + /* if we have the romfs in flash, or if there is no rom filesystem, + * our free area starts directly after the BSS + */ + memory_start = (unsigned long) &_end; + } else { + /* otherwise the free area starts after the ROM filesystem */ + printk("ROM fs in RAM, size %d bytes\n", romfs_length); + memory_start = romfs_start + romfs_length; + } + + /* process 1's initial memory region is the kernel code/data */ + + init_mm.start_code = (unsigned long) &text_start; + init_mm.end_code = (unsigned long) &_etext; + init_mm.end_data = (unsigned long) &_edata; + init_mm.brk = (unsigned long) &_end; + +#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) +#define PFN_PHYS(x) ((x) << PAGE_SHIFT) + + /* min_low_pfn points to the start of DRAM, start_pfn points + * to the first DRAM pages after the kernel, and max_low_pfn + * to the end of DRAM. + */ + + /* + * partially used pages are not usable - thus + * we are rounding upwards: + */ + + start_pfn = PFN_UP(memory_start); /* usually c0000000 + kernel + romfs */ + max_pfn = PFN_DOWN((unsigned long)high_memory); /* usually c0000000 + dram size */ + + /* + * Initialize the boot-time allocator (start, end) + * + * We give it access to all our DRAM, but we could as well just have + * given it a small slice. No point in doing that though, unless we + * have non-contiguous memory and want the boot-stuff to be in, say, + * the smallest area. + * + * It will put a bitmap of the allocated pages in the beginning + * of the range we give it, but it won't mark the bitmaps pages + * as reserved. We have to do that ourselves below. + * + * We need to use init_bootmem_node instead of init_bootmem + * because our map starts at a quite high address (min_low_pfn). + */ + + max_low_pfn = max_pfn; + min_low_pfn = PAGE_OFFSET >> PAGE_SHIFT; + + bootmap_size = init_bootmem_node(NODE_DATA(0), start_pfn, + min_low_pfn, + max_low_pfn); + + /* And free all memory not belonging to the kernel (addr, size) */ + + free_bootmem(PFN_PHYS(start_pfn), PFN_PHYS(max_pfn - start_pfn)); + + /* + * Reserve the bootmem bitmap itself as well. We do this in two + * steps (first step was init_bootmem()) because this catches + * the (very unlikely) case of us accidentally initializing the + * bootmem allocator with an invalid RAM area. + * + * Arguments are start, size + */ + + reserve_bootmem(PFN_PHYS(start_pfn), bootmap_size); + + /* paging_init() sets up the MMU and marks all pages as reserved */ + + paging_init(); + + /* we dont use a command line yet, so just let it be an empty string */ + + *cmdline_p = command_line; + strcpy(command_line, "root=/dev/rom"); /* use the appended romdisk as root */ + + /* give credit for the CRIS port */ + + printk("Linux/CRIS port on ETRAX 100LX (c) 2000 Axis Communications AB\n"); + +} + +#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 + +static struct cpu_info { + char *model; + unsigned short cache; + unsigned short flags; +} cpu_info[] = { + { "ETRAX 1", 0, 0 }, + { "ETRAX 2", 0, 0 }, /* Don't say it HAS_TOKENRING - there are + lethal bugs in that chip that + prevents T-R from ever working. + Never go there, and never lead anyone + into believing it can work. BTW: + Anyone working on a T-R network + driver? :-) :-) :-) :-/ */ + { "ETRAX 3", 0, HAS_TOKENRING }, + { "ETRAX 4", 0, HAS_TOKENRING | HAS_SCSI }, + { "Unknown", 0, 0 }, + { "Unknown", 0, 0 }, + { "Unknown", 0, 0 }, + { "Simulator", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA }, + { "ETRAX 100", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_IRQ_BUG }, + { "ETRAX 100", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA }, + { "ETRAX 100LX", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU }, + { "Unknown", 0, 0 }, +}; + +/* + * BUFFER is PAGE_SIZE bytes long. + */ +int get_cpuinfo(char *buffer) +{ + int revision; +#ifndef CONFIG_SVINTO_SIM + unsigned char tmp; + + __asm__ volatile ("move vr,%0" : "=rm" (tmp)); + revision = tmp; +#else + /* Fake a revision for the simulator */ + revision = 7; +#endif + + return sprintf(buffer, + "cpu\t\t: CRIS\n" + "cpu revision\t: %d\n" + "cpu model\t: %s\n" + "cache size\t: %d kB\n" + "fpu\t\t: %s\n" + "mmu\t\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", + + revision, + cpu_info[revision].model, + cpu_info[revision].cache, + cpu_info[revision].flags & HAS_FPU ? "yes" : "no", + cpu_info[revision].flags & HAS_MMU ? "yes" : "no", + cpu_info[revision].flags & HAS_ETHERNET100 ? "10/100" : "10", + cpu_info[revision].flags & HAS_TOKENRING ? "4/16 Mbps" : "no", + cpu_info[revision].flags & HAS_SCSI ? "yes" : "no", + cpu_info[revision].flags & HAS_ATA ? "yes" : "no", + cpu_info[revision].flags & HAS_USB ? "yes" : "no", + (loops_per_jiffy * HZ + 500) / 100000, + ((loops_per_jiffy * HZ + 500) / 1000) % 100); +} +#endif /* CONFIG_PROC_FS */ diff --git a/arch/cris/kernel/shadows.c b/arch/cris/kernel/shadows.c new file mode 100644 index 000000000..0a6449f4c --- /dev/null +++ b/arch/cris/kernel/shadows.c @@ -0,0 +1,20 @@ +/* $Id: shadows.c,v 1.1 2000/07/10 16:25:21 bjornw Exp $ + * + * Various Etrax shadow registers. Defines for these are in include/asm-etrax100/io.h + */ + +#include <linux/config.h> + +unsigned long genconfig_shadow = 42; +unsigned long port_g_data_shadow = 42; +unsigned char port_pa_dir_shadow = 42; +unsigned char port_pa_data_shadow = 42; +unsigned char port_pb_i2c_shadow = 42; +unsigned char port_pb_config_shadow = 42; +unsigned char port_pb_dir_shadow = 42; +unsigned char port_pb_data_shadow = 42; +unsigned long r_timer_ctrl_shadow = 42; + +#ifdef CONFIG_ETRAX_90000000_LEDS +unsigned long port_90000000_shadow = 42; +#endif diff --git a/arch/cris/kernel/signal.c b/arch/cris/kernel/signal.c new file mode 100644 index 000000000..8aab31a45 --- /dev/null +++ b/arch/cris/kernel/signal.c @@ -0,0 +1,667 @@ +/* + * linux/arch/cris/kernel/signal.c + * + * Based on arch/i386/kernel/signal.c by + * Copyright (C) 1991, 1992 Linus Torvalds + * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson * + * + * Ideas also taken from arch/arm. + * + * Copyright (C) 2000 Axis Communications AB + * + * Authors: Bjorn Wesen (bjornw@axis.com) + * + */ + +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.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 <asm/processor.h> +#include <asm/ucontext.h> +#include <asm/uaccess.h> + +#define DEBUG_SIG 0 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +/* a syscall in Linux/CRIS is a break 13 instruction which is 2 bytes */ +/* manipulate regs so that upon return, it will be re-executed */ + +#define RESTART_CRIS_SYS(regs) regs->r10 = regs->orig_r10; regs->irp -= 2; + +int sys_wait4(pid_t pid, unsigned long *stat_addr, + int options, unsigned long *ru); + +int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs); + +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + /* First 32bits of unions are always present. */ + err |= __put_user(from->si_pid, &to->si_pid); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ +int +sys_sigsuspend(old_sigset_t mask) +{ + struct pt_regs * regs = (struct pt_regs *)current_regs(); + sigset_t saveset; + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + regs->r10 = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(0, &saveset, regs)) + return -EINTR; + } +} + +int +sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize) +{ + struct pt_regs * regs = (struct pt_regs *)current_regs(); + sigset_t saveset, newset; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + regs->r10 = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(0, &saveset, regs)) + return -EINTR; + } +} + +int +sys_sigaction(int sig, const struct old_sigaction *act, + struct old_sigaction *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset_t mask; + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return ret; +} + +int +sys_sigaltstack(const stack_t *uss, stack_t *uoss) +{ + return do_sigaltstack(uss, uoss, rdusp()); +} + + +/* + * Do a signal return; undo the signal stack. + */ + +struct sigframe { + struct sigcontext sc; + unsigned long extramask[_NSIG_WORDS-1]; + unsigned char retcode[8]; /* trampoline code */ +}; + +struct rt_sigframe { + struct siginfo *pinfo; + void *puc; + struct siginfo info; + struct ucontext uc; + unsigned char retcode[8]; /* trampoline code */ +}; + + +static int +restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc) +{ + unsigned int err = 0; + unsigned long old_usp; + + /* restore the regs from &sc->regs (same as sc, since regs is first) + * (sc is already checked for VERIFY_READ since the sigframe was + * checked in sys_sigreturn previously) + */ + + if (__copy_from_user(regs, sc, sizeof(struct pt_regs))) + goto badframe; + + /* make sure the U-flag is set so user-mode cannot fool us */ + + regs->dccr |= 1 << 8; + + /* restore the old USP as it was before we stacked the sc etc. + * (we cannot just pop the sigcontext since we aligned the sp and + * stuff after pushing it) + */ + + err |= __get_user(old_usp, &sc->usp); + + wrusp(old_usp); + + /* TODO: the other ports use regs->orig_XX to disable syscall checks + * after this completes, but we don't use that mechanism. maybe we can + * use it now ? + */ + + return err; + +badframe: + return 1; +} + +asmlinkage int sys_sigreturn(void) +{ + struct pt_regs *regs = (struct pt_regs *)current_regs(); + struct sigframe *frame = (struct sigframe *)rdusp(); + sigset_t set; + + /* + * Since we stacked the signal on a dword boundary, + * then frame should be dword aligned here. If it's + * not, then the user is trying to mess with us. + */ + if (((long)frame) & 3) + goto badframe; + + if (verify_area(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; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + if (restore_sigcontext(regs, &frame->sc)) + goto badframe; + + /* TODO: SIGTRAP when single-stepping as in arm ? */ + + return regs->r10; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +asmlinkage int sys_rt_sigreturn(void) +{ + struct pt_regs *regs = (struct pt_regs *)current_regs(); + struct rt_sigframe *frame = (struct rt_sigframe *)rdusp(); + sigset_t set; + stack_t st; + + /* + * Since we stacked the signal on a dword boundary, + * then frame should be dword aligned here. If it's + * not, then the user is trying to mess with us. + */ + if (((long)frame) & 3) + goto badframe; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) + goto badframe; + + if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st))) + goto badframe; + /* It is more difficult to avoid calling this function than to + call it and ignore errors. */ + do_sigaltstack(&st, NULL, rdusp()); + + return regs->r10; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* + * Set up a signal frame. + */ + +static int +setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs, unsigned long mask) +{ + int err = 0; + unsigned long usp = rdusp(); + + /* copy the regs. they are first in sc so we can use sc directly */ + + err |= __copy_to_user(sc, regs, sizeof(struct pt_regs)); + + /* then some other stuff */ + + err |= __put_user(mask, &sc->oldmask); + + err |= __put_user(usp, &sc->usp); + + return err; +} + +/* figure out where we want to put the new signal frame - usually on the stack */ + +static inline void * +get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) +{ + unsigned long sp = rdusp(); + + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa.sa_flags & SA_ONSTACK) { + if (! on_sig_stack(sp)) + sp = current->sas_ss_sp + current->sas_ss_size; + } + + /* make sure the frame is dword-aligned */ + + sp &= ~3; + + return (void *)(sp - frame_size); +} + +/* grab and setup a signal frame. + * + * basically we stack a lot of state info, and arrange for the + * user-mode program to return to the kernel using either a + * trampoline which performs the syscall sigreturn, or a provided + * user-mode trampoline. + */ + +static void setup_frame(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs * regs) +{ + struct sigframe *frame; + unsigned long return_ip; + int err = 0; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + err |= setup_sigcontext(&frame->sc, regs, set->sig[0]); + if (err) + goto give_sigsegv; + + if (_NSIG_WORDS > 1) { + err |= __copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask)); + } + if (err) + goto give_sigsegv; + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & SA_RESTORER) { + return_ip = (unsigned long)ka->sa.sa_restorer; + } else { + /* trampoline - the desired return ip is the retcode itself */ + return_ip = (unsigned long)&frame->retcode; + /* This is movu.w __NR_sigreturn, r1; break 13; */ + /* TODO: check byteorder */ + err |= __put_user(0x1c5f, (short *)(frame->retcode+0)); + err |= __put_user(__NR_sigreturn, (short *)(frame->retcode+2)); + err |= __put_user(0xe93d, (short *)(frame->retcode+4)); + } + + if (err) + goto give_sigsegv; + + /* Set up registers for signal handler */ + + regs->irp = (unsigned long) ka->sa.sa_handler; /* what we enter NOW */ + regs->srp = return_ip; /* what we enter LATER */ + + /* actually move the usp to reflect the stacked frame */ + + wrusp((unsigned long)frame); + + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + +static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs * regs) +{ + struct rt_sigframe *frame; + unsigned long return_ip; + int err = 0; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + err |= __put_user(&frame->info, &frame->pinfo); + err |= __put_user(&frame->uc, &frame->puc); + err |= copy_siginfo_to_user(&frame->info, info); + if (err) + goto give_sigsegv; + + /* 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)); + + if (err) + goto give_sigsegv; + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & SA_RESTORER) { + return_ip = (unsigned long)ka->sa.sa_restorer; + } else { + /* trampoline - the desired return ip is the retcode itself */ + return_ip = (unsigned long)&frame->retcode; + /* This is movu.w __NR_sigreturn, r1; break 13; */ + /* TODO: check byteorder */ + err |= __put_user(0x1c5f, (short *)(frame->retcode+0)); + err |= __put_user(__NR_sigreturn, (short *)(frame->retcode+2)); + err |= __put_user(0xe93d, (short *)(frame->retcode+4)); + } + + if (err) + goto give_sigsegv; + + /* TODO what is the current->exec_domain stuff and invmap ? */ + + /* Set up registers for signal handler */ + + regs->irp = (unsigned long) ka->sa.sa_handler; /* what we enter NOW */ + regs->srp = return_ip; /* what we enter LATER */ + + /* actually move the usp to reflect the stacked frame */ + + wrusp((unsigned long)frame); + + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + +/* + * OK, we're invoking a handler + */ + +static inline void +handle_signal(int canrestart, unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) +{ + /* Are we from a system call? */ + if (canrestart) { + /* If so, check system call restarting.. */ + switch (regs->r10) { + case -ERESTARTNOHAND: + /* ERESTARTNOHAND means that the syscall should only be + restarted if there was no handler for the signal, and since + we only get here if there is a handler, we dont restart */ + regs->r10 = -EINTR; + break; + + case -ERESTARTSYS: + /* ERESTARTSYS means to restart the syscall if there is no + handler or the handler was registered with SA_RESTART */ + if (!(ka->sa.sa_flags & SA_RESTART)) { + regs->r10 = -EINTR; + break; + } + /* fallthrough */ + case -ERESTARTNOINTR: + /* ERESTARTNOINTR means that the syscall should be called again + after the signal handler returns. */ + RESTART_CRIS_SYS(regs); + } + } + + /* Set up the stack frame */ + if (ka->sa.sa_flags & SA_SIGINFO) + setup_rt_frame(sig, ka, info, oldset, regs); + else + setup_frame(sig, ka, oldset, regs); + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sigmask_lock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + } +} + +/* + * 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. + */ +int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs) +{ + siginfo_t info; + struct k_sigaction *ka; + + /* + * We want the common case to go fast, which + * is why we may in certain cases get here from + * kernel mode. Just return without doing anything + * if so. + */ + if (!user_mode(regs)) + return 1; + + if (!oldset) + oldset = ¤t->blocked; + + for (;;) { + unsigned long signr; + + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + + if (!signr) + break; + + if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ + current->exit_code = signr; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + + /* We're back. Did the debugger cancel the sig? */ + if (!(signr = current->exit_code)) + continue; + current->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ + if (signr == SIGSTOP) + continue; + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); + continue; + } + } + + ka = ¤t->sig->action[signr-1]; + if (ka->sa.sa_handler == SIG_IGN) { + if (signr != SIGCHLD) + continue; + /* Check for SIGCHLD: it's special. */ + while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) + /* nothing */; + continue; + } + + if (ka->sa.sa_handler == SIG_DFL) { + int exit_code = signr; + + /* Init gets no signals it doesn't want. */ + if (current->pid == 1) + continue; + + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(current->pgrp)) + continue; + /* FALLTHRU */ + + case SIGSTOP: + current->state = TASK_STOPPED; + current->exit_code = signr; + if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) + notify_parent(current, SIGCHLD); + schedule(); + continue; + + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: + case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: + if (do_coredump(signr, regs)) + exit_code |= 0x80; + /* FALLTHRU */ + + default: + lock_kernel(); + sigaddset(¤t->pending.signal, signr); + recalc_sigpending(current); + current->flags |= PF_SIGNALED; + do_exit(exit_code); + /* NOTREACHED */ + } + } + + /* Whee! Actually deliver the signal. */ + handle_signal(canrestart, signr, ka, &info, oldset, regs); + return 1; + } + + /* Did we come 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); + } + } + return 0; +} diff --git a/arch/cris/kernel/sys_cris.c b/arch/cris/kernel/sys_cris.c new file mode 100644 index 000000000..cfcb097f9 --- /dev/null +++ b/arch/cris/kernel/sys_cris.c @@ -0,0 +1,201 @@ +/* $Id: sys_cris.c,v 1.3 2000/08/02 13:59:02 bjornw Exp $ + * + * linux/arch/cris/kernel/sys_etrax.c + * + * This file contains various random system calls that + * have a non-standard calling sequence on some platforms. + * Since we don't have to do any backwards compatibility, our + * versions are done in the most "normal" way possible. + * + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/sem.h> +#include <linux/msg.h> +#include <linux/shm.h> +#include <linux/stat.h> +#include <linux/mman.h> +#include <linux/file.h> + +#include <asm/uaccess.h> +#include <asm/ipc.h> +#include <asm/segment.h> + +/* + * sys_pipe() is the normal C calling standard for creating + * a pipe. It's not the way Unix traditionally does this, though. + */ +asmlinkage int sys_pipe(unsigned long * fildes) +{ + int fd[2]; + int error; + + lock_kernel(); + error = do_pipe(fd); + unlock_kernel(); + if (!error) { + if (copy_to_user(fildes, fd, 2*sizeof(int))) + error = -EFAULT; + } + return error; +} + +/* sys_mmap used to take a ptr to a buffer instead containing the args + * but we support syscalls with 6 arguments now + */ + +asmlinkage unsigned long sys_mmap(unsigned long addr, size_t len, + unsigned long prot, unsigned long flags, + unsigned long fd, off_t offset) +{ + struct file * file = NULL; + int ret = -EBADF; + + lock_kernel(); + if (!(flags & MAP_ANONYMOUS)) { + if (!(file = fget(fd))) + goto out; + } + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + down(¤t->mm->mmap_sem); + ret = do_mmap(file, addr, len, prot, flags, offset); + up(¤t->mm->mmap_sem); + if (file) + fput(file); + out: + unlock_kernel(); + return ret; +} + +/* common code for old and new mmaps */ +static inline long +do_mmap2(unsigned long addr, unsigned long len, unsigned long prot, + unsigned long flags, unsigned long fd, unsigned long pgoff) +{ + int error = -EBADF; + struct file * file = NULL; + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + if (!(flags & MAP_ANONYMOUS)) { + file = fget(fd); + if (!file) + goto out; + } + + down(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up(¤t->mm->mmap_sem); + + if (file) + fput(file); +out: + return error; +} + +asmlinkage unsigned long old_mmap(unsigned long addr, size_t len, int prot, + int flags, int fd, off_t offset) +{ + return do_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT); +} + +asmlinkage long +sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot, + unsigned long flags, unsigned long fd, unsigned long pgoff) +{ + return do_mmap2(addr, len, prot, flags, fd, pgoff); +} + +/* + * sys_ipc() is the de-multiplexer for the SysV IPC calls.. + * + * This is really horribly ugly. (same as arch/i386) + */ + +asmlinkage int sys_ipc (uint call, int first, int second, + int third, void *ptr, long fifth) +{ + int version, ret; + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + switch (call) { + case SEMOP: + return sys_semop (first, (struct sembuf *)ptr, second); + case SEMGET: + return sys_semget (first, second, third); + case SEMCTL: { + union semun fourth; + if (!ptr) + return -EINVAL; + if (get_user(fourth.__pad, (void **) ptr)) + return -EFAULT; + return sys_semctl (first, second, third, fourth); + } + + case MSGSND: + return sys_msgsnd (first, (struct msgbuf *) ptr, + second, third); + case MSGRCV: + switch (version) { + case 0: { + struct ipc_kludge tmp; + if (!ptr) + return -EINVAL; + + if (copy_from_user(&tmp, + (struct ipc_kludge *) ptr, + sizeof (tmp))) + return -EFAULT; + return sys_msgrcv (first, tmp.msgp, second, + tmp.msgtyp, third); + } + default: + return sys_msgrcv (first, + (struct msgbuf *) ptr, + second, fifth, third); + } + case MSGGET: + return sys_msgget ((key_t) first, second); + case MSGCTL: + return sys_msgctl (first, second, (struct msqid_ds *) ptr); + + case SHMAT: + switch (version) { + default: { + ulong raddr; + ret = sys_shmat (first, (char *) ptr, second, &raddr); + if (ret) + return ret; + return put_user (raddr, (ulong *) third); + } + case 1: /* iBCS2 emulator entry point */ + if (!segment_eq(get_fs(), get_ds())) + return -EINVAL; + return sys_shmat (first, (char *) ptr, second, (ulong *) third); + } + case SHMDT: + return sys_shmdt ((char *)ptr); + case SHMGET: + return sys_shmget (first, second, third); + case SHMCTL: + return sys_shmctl (first, second, + (struct shmid_ds *) ptr); + default: + return -EINVAL; + } +} + +/* apparently this is legacy - if we don't need this in Linux/CRIS we can remove it. */ + +asmlinkage int sys_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; +} diff --git a/arch/cris/kernel/time.c b/arch/cris/kernel/time.c new file mode 100644 index 000000000..d46ed47fa --- /dev/null +++ b/arch/cris/kernel/time.c @@ -0,0 +1,453 @@ +/* $Id: time.c,v 1.4 2000/10/17 14:44:58 bjornw Exp $ + * + * linux/arch/cris/kernel/time.c + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * Copyright (C) 1999, 2000 Axis Communications AB + * + * 1994-07-02 Alan Modra + * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime + * 1995-03-26 Markus Kuhn + * fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887 + * precision CMOS clock update + * 1996-05-03 Ingo Molnar + * fixed time warps in do_[slow|fast]_gettimeoffset() + * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 + * "A Kernel Model for Precision Timekeeping" by Dave Mills + * + * Linux/CRIS specific code: + * + * Authors: Bjorn Wesen + * + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/time.h> +#include <linux/delay.h> + +#include <asm/segment.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/delay.h> +#include <asm/rtc.h> + +#include <linux/timex.h> +#include <linux/config.h> + +#include <asm/svinto.h> + +static int have_rtc; /* used to remember if we have an RTC or not */ + +/* define this if you need to use print_timestamp */ +/* it will make jiffies at 96 hz instead of 100 hz though */ +#undef USE_CASCADE_TIMERS + +extern int setup_etrax_irq(int, struct irqaction *); + +#define TICK_SIZE tick + +static unsigned long do_slow_gettimeoffset(void) +{ + unsigned long count; + + static unsigned long count_p = LATCH; /* for the first call after boot */ + static unsigned long jiffies_p = 0; + + /* + * cache volatile jiffies temporarily; we have IRQs turned off. + */ + unsigned long jiffies_t; + + /* The timer interrupt comes from Etrax timer 0. In order to get + * better precision, we check the current value. It might have + * underflowed already though. + */ + +#ifndef CONFIG_SVINTO_SIM + /* Not available in the xsim simulator. */ + count = *R_TIMER0_DATA; +#else + count = 0; +#endif + + jiffies_t = jiffies; + + /* + * avoiding timer inconsistencies (they are rare, but they happen)... + * there are three kinds of problems that must be avoided here: + * 1. the timer counter underflows + * 2. hardware problem with the timer, not giving us continuous time, + * the counter does small "jumps" upwards on some Pentium systems, + * thus causes time warps + * 3. we are after the timer interrupt, but the bottom half handler + * hasn't executed yet. + */ + if( jiffies_t == jiffies_p ) { + if( count > count_p ) { + } + } else + jiffies_p = jiffies_t; + + count_p = count; + + count = ((LATCH-1) - count) * TICK_SIZE; + count = (count + LATCH/2) / LATCH; + + return count; +} + +static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset; + +/* + * This version of gettimeofday has near microsecond resolution. + */ +void do_gettimeofday(struct timeval *tv) +{ + unsigned long flags; + + save_flags(flags); + cli(); + *tv = xtime; + tv->tv_usec += do_gettimeoffset(); + if (tv->tv_usec >= 1000000) { + tv->tv_usec -= 1000000; + tv->tv_sec++; + } + restore_flags(flags); +} + +void do_settimeofday(struct timeval *tv) +{ + cli(); + /* This is revolting. We need to set the xtime.tv_usec + * correctly. However, the value in this location is + * is value at the last tick. + * Discover what correction gettimeofday + * would have done, and then undo it! + */ + tv->tv_usec -= do_gettimeoffset(); + + if (tv->tv_usec < 0) { + tv->tv_usec += 1000000; + tv->tv_sec--; + } + + xtime = *tv; + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_state = TIME_ERROR; /* p. 24, (a) */ + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + sti(); +} + + +/* + * BUG: This routine does not handle hour overflow properly; it just + * sets the minutes. Usually you'll only notice that after reboot! + */ + +static int set_rtc_mmss(unsigned long nowtime) +{ + int retval = 0; + int real_seconds, real_minutes, cmos_minutes; + unsigned char save_control, save_freq_select; + + printk("set_rtc_mmss(%d)\n", nowtime); + + if(!have_rtc) + return 0; + + cmos_minutes = CMOS_READ(RTC_MINUTES); + BCD_TO_BIN(cmos_minutes); + + /* + * since we're only adjusting minutes and seconds, + * don't interfere with hour overflow. This avoids + * messing with unknown time zones but requires your + * RTC not to be off by more than 15 minutes + */ + real_seconds = nowtime % 60; + real_minutes = nowtime / 60; + if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) + real_minutes += 30; /* correct for half hour time zone */ + real_minutes %= 60; + + if (abs(real_minutes - cmos_minutes) < 30) { + BIN_TO_BCD(real_seconds); + BIN_TO_BCD(real_minutes); + CMOS_WRITE(real_seconds,RTC_SECONDS); + CMOS_WRITE(real_minutes,RTC_MINUTES); + } else { + printk(KERN_WARNING + "set_rtc_mmss: can't update from %d to %d\n", + cmos_minutes, real_minutes); + retval = -1; + } + + return retval; +} + +/* Except from the Etrax100 HSDD about the built-in watchdog: + * + * 3.10.4 Watchdog timer + + * When the watchdog timer is started, it generates an NMI if the watchdog + * isn't restarted or stopped within 0.1 s. If it still isn't restarted or + * stopped after an additional 3.3 ms, the watchdog resets the chip. + * The watchdog timer is stopped after reset. The watchdog timer is controlled + * by the R_WATCHDOG register. The R_WATCHDOG register contains an enable bit + * and a 3-bit key value. The effect of writing to the R_WATCHDOG register is + * described in the table below: + * + * Watchdog Value written: + * state: To enable: To key: Operation: + * -------- ---------- ------- ---------- + * stopped 0 X No effect. + * stopped 1 key_val Start watchdog with key = key_val. + * started 0 ~key Stop watchdog + * started 1 ~key Restart watchdog with key = ~key. + * started X new_key_val Change key to new_key_val. + * + * Note: '~' is the bitwise NOT operator. + * + */ + +/* right now, starting the watchdog is the same as resetting it */ +#define start_watchdog reset_watchdog + +static int watchdog_key = 0; /* arbitrary number */ + +/* number of pages to consider "out of memory". it is normal that the memory + * is used though, so put this really low. + */ + +#define WATCHDOG_MIN_FREE_PAGES 8 + +extern int nr_free_pages; + +static inline void +reset_watchdog(void) +{ +#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) + /* 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 */ + watchdog_key ^= 0x7; /* invert key, which is 3 bits */ + *R_WATCHDOG = IO_FIELD(R_WATCHDOG, key, watchdog_key) | + IO_STATE(R_WATCHDOG, enable, start); + } +#endif +} + +/* last time the cmos clock got updated */ +static long last_rtc_update = 0; + +/* + * timer_interrupt() needs to keep up the real-time clock, + * as well as call the "do_timer()" routine every clocktick + */ + +//static unsigned short myjiff; /* used by our debug routine print_timestamp */ + +static inline void +timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + /* acknowledge the timer irq */ + +#ifdef USE_CASCADE_TIMERS + *R_TIMER_CTRL = + IO_FIELD( R_TIMER_CTRL, timerdiv1, 0) | + IO_FIELD( R_TIMER_CTRL, timerdiv0, 0) | + IO_STATE( R_TIMER_CTRL, i1, clr) | + IO_STATE( R_TIMER_CTRL, tm1, run) | + IO_STATE( R_TIMER_CTRL, clksel1, cascade0) | + IO_STATE( R_TIMER_CTRL, i0, clr) | + IO_STATE( R_TIMER_CTRL, tm0, run) | + IO_STATE( R_TIMER_CTRL, clksel0, c6250kHz); +#else + *R_TIMER_CTRL = r_timer_ctrl_shadow | + IO_STATE(R_TIMER_CTRL, i0, clr); +#endif + + /* reset watchdog otherwise it resets us! */ + + reset_watchdog(); + + /* call the real timer interrupt handler */ + + do_timer(regs); + + /* + * If we have an externally synchronized Linux clock, then update + * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be + * called as close as possible to 500 ms before the new second starts. + */ + + if ((time_status & STA_UNSYNC) == 0 && + xtime.tv_sec > last_rtc_update + 660 && + xtime.tv_usec > 500000 - (tick >> 1) && + xtime.tv_usec < 500000 + (tick >> 1)) + if (set_rtc_mmss(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else + last_rtc_update = xtime.tv_sec - 600; + +} + +#if 0 +/* some old debug code for testing the microsecond timing of packets */ +static unsigned int lastjiff; + +void print_timestamp(const char *s) +{ + unsigned long flags; + unsigned int newjiff; + save_flags(flags); + cli(); + newjiff = (myjiff << 16) | (unsigned short)(-*R_TIMER01_DATA); + printk("%s: %x (%x)\n", s, newjiff, newjiff - lastjiff); + lastjiff = newjiff; + restore_flags(flags); +} +#endif + +/* grab the time from the RTC chip */ + +unsigned long +get_cmos_time(void) +{ + unsigned int year, mon, day, hour, min, sec; + int i; + + sec = CMOS_READ(RTC_SECONDS); + min = CMOS_READ(RTC_MINUTES); + hour = CMOS_READ(RTC_HOURS); + day = CMOS_READ(RTC_DAY_OF_MONTH); + mon = CMOS_READ(RTC_MONTH); + year = CMOS_READ(RTC_YEAR); + + printk("rtc: sec 0x%x min 0x%x hour 0x%x day 0x%x mon 0x%x year 0x%x\n", + sec, min, hour, day, mon, year); + + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + + if ((year += 1900) < 1970) + year += 100; + + return mktime(year, mon, day, hour, min, sec); +} + +/* update xtime from the CMOS settings. used when /dev/rtc gets a SET_TIME. + * TODO: this doesn't reset the fancy NTP phase stuff as do_settimeofday does. + */ + +void +update_xtime_from_cmos(void) +{ + if(have_rtc) { + xtime.tv_sec = get_cmos_time(); + xtime.tv_usec = 0; + } +} + +/* timer is SA_SHIRQ so drivers can add stuff to the timer irq chain + * it needs to be SA_INTERRUPT to make the jiffies update work properly + */ + +static struct irqaction irq2 = { timer_interrupt, SA_SHIRQ | SA_INTERRUPT, + 0, "timer", NULL, NULL}; + +void __init +time_init(void) +{ + /* probe for the RTC and read it if it exists */ + + if(RTC_INIT() < 0) { + /* no RTC, start at 1980 */ + xtime.tv_sec = 0; + xtime.tv_usec = 0; + have_rtc = 0; + } else { + /* get the current time */ + have_rtc = 1; + update_xtime_from_cmos(); + } + + /* Setup the etrax timers + * Base frequency is 19200 hz, divider 192 -> 100 hz as Linux wants + * In normal mode, we use timer0, so timer1 is free. In cascade + * mode (which we sometimes use for debugging) both timers are used. + * Remember that linux/timex.h contains #defines that rely on the + * timer settings below (hz and divide factor) !!! + */ + +#ifdef USE_CASCADE_TIMERS + *R_TIMER_CTRL = + IO_FIELD( R_TIMER_CTRL, timerdiv1, 0) | + IO_FIELD( R_TIMER_CTRL, timerdiv0, 0) | + IO_STATE( R_TIMER_CTRL, i1, nop) | + IO_STATE( R_TIMER_CTRL, tm1, stop_ld) | + IO_STATE( R_TIMER_CTRL, clksel1, cascade0) | + IO_STATE( R_TIMER_CTRL, i0, nop) | + IO_STATE( R_TIMER_CTRL, tm0, stop_ld) | + IO_STATE( R_TIMER_CTRL, clksel0, c6250kHz); + + *R_TIMER_CTRL = r_timer_ctrl_shadow = + IO_FIELD( R_TIMER_CTRL, timerdiv1, 0) | + IO_FIELD( R_TIMER_CTRL, timerdiv0, 0) | + IO_STATE( R_TIMER_CTRL, i1, nop) | + IO_STATE( R_TIMER_CTRL, tm1, run) | + IO_STATE( R_TIMER_CTRL, clksel1, cascade0) | + IO_STATE( R_TIMER_CTRL, i0, nop) | + IO_STATE( R_TIMER_CTRL, tm0, run) | + IO_STATE( R_TIMER_CTRL, clksel0, c6250kHz); +#else + *R_TIMER_CTRL = + IO_FIELD(R_TIMER_CTRL, timerdiv1, 192) | + IO_FIELD(R_TIMER_CTRL, timerdiv0, 192) | + IO_STATE(R_TIMER_CTRL, i1, nop) | + IO_STATE(R_TIMER_CTRL, tm1, stop_ld) | + IO_STATE(R_TIMER_CTRL, clksel1, c19k2Hz) | + IO_STATE(R_TIMER_CTRL, i0, nop) | + IO_STATE(R_TIMER_CTRL, tm0, stop_ld) | + IO_STATE(R_TIMER_CTRL, clksel0, c19k2Hz); + + *R_TIMER_CTRL = r_timer_ctrl_shadow = + IO_FIELD(R_TIMER_CTRL, timerdiv1, 192) | + IO_FIELD(R_TIMER_CTRL, timerdiv0, 192) | + IO_STATE(R_TIMER_CTRL, i1, nop) | + IO_STATE(R_TIMER_CTRL, tm1, run) | + IO_STATE(R_TIMER_CTRL, clksel1, c19k2Hz) | + IO_STATE(R_TIMER_CTRL, i0, nop) | + IO_STATE(R_TIMER_CTRL, tm0, run) | + IO_STATE(R_TIMER_CTRL, clksel0, c19k2Hz); +#endif + + *R_IRQ_MASK0_SET = + IO_STATE(R_IRQ_MASK0_SET, timer0, set); /* unmask the timer irq */ + + /* now actually register the timer irq handler that calls timer_interrupt() */ + + setup_etrax_irq(2, &irq2); /* irq 2 is the timer0 irq in etrax */ + + /* enable watchdog if we should use one */ + +#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) + printk("Enabling watchdog...\n"); + start_watchdog(); +#endif + +} diff --git a/arch/cris/kernel/traps.c b/arch/cris/kernel/traps.c new file mode 100644 index 000000000..9994487d4 --- /dev/null +++ b/arch/cris/kernel/traps.c @@ -0,0 +1,167 @@ +/* $Id: traps.c,v 1.3 2000/10/04 16:50:06 bjornw Exp $ + * + * linux/arch/cris/traps.c + * + * Etrax100 does not have any hardware traps, only IRQ's, which we setup + * in irq.c instead. Here we just define the die_if_kernel Oops'er. + * + * Copyright (C) 2000 Axis Communications AB + * + * Authors: Bjorn Wesen + * + */ + +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/timer.h> +#include <linux/mm.h> + +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/io.h> +#include <asm/pgtable.h> + +int kstack_depth_to_print = 24; + +/* + * These constants are for searching for possible module text + * segments. MODULE_RANGE is a guess of how much space is likely + * to be vmalloced. + */ + +#define MODULE_RANGE (8*1024*1024) + +void show_stack(unsigned long *sp) +{ + unsigned long *stack, addr, module_start, module_end; + int i; + extern char _stext, _etext; + + // debugging aid: "show_stack(NULL);" prints the + // back trace for this cpu. + + if(sp == NULL) + sp = (unsigned long*)rdsp(); + + stack = sp; + + for(i = 0; i < kstack_depth_to_print; i++) { + if (((long) stack & (THREAD_SIZE-1)) == 0) + break; + if (i && ((i % 8) == 0)) + printk("\n "); + printk("%08lx ", *stack++); + } + + printk("\nCall Trace: "); + stack = sp; + i = 1; + module_start = VMALLOC_START; + module_end = VMALLOC_END; + while (((long) stack & (THREAD_SIZE-1)) != 0) { + addr = *stack++; + /* + * If the address is either in the text segment of the + * kernel, or in the region which contains vmalloc'ed + * memory, it *may* be the address of a calling + * routine; if so, print it so that someone tracing + * down the cause of the crash will be able to figure + * out the call path that was taken. + */ + if (((addr >= (unsigned long) &_stext) && + (addr <= (unsigned long) &_etext)) || + ((addr >= module_start) && (addr <= module_end))) { + if (i && ((i % 8) == 0)) + printk("\n "); + printk("[<%08lx>] ", addr); + i++; + } + } +} + +#if 0 +/* displays a short stack trace */ + +int show_stack() +{ + unsigned long *sp = (unsigned long *)rdusp(); + int i; + printk("Stack dump [0x%08lx]:\n", (unsigned long)sp); + for(i = 0; i < 16; i++) + printk("sp + %d: 0x%08lx\n", i*4, sp[i]); + return 0; +} +#endif + +void show_registers(struct pt_regs * regs) +{ + unsigned long usp = rdusp(); + + printk("IRP: %08lx SRP: %08lx CCR: %08lx USP: %08lx\n", + regs->irp, regs->srp, regs->dccr, usp ); + 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); + printk("Process %s (pid: %d, stackpage=%08lx)\n", + current->comm, current->pid, (unsigned long)current); + + // TODO, fix in_kernel detection + +#if 0 + /* + * When in-kernel, we also print out the stack and code at the + * time of the fault.. + */ + if (1) { + + printk("\nStack: "); + show_stack((unsigned long*)usp); + + printk("\nCode: "); + if(regs->irp < PAGE_OFFSET) + goto bad; + + for(i = 0; i < 20; i++) + { + unsigned char c; + if(__get_user(c, &((unsigned char*)regs->irp)[i])) { +bad: + printk(" Bad IP value."); + break; + } + printk("%02x ", c); + } + } + printk("\n"); +#endif +} + + + +void die_if_kernel(const char * str, struct pt_regs * regs, long err) +{ + if(user_mode(regs)) + return; + + printk("%s: %04lx\n", str, err & 0xffff); + + show_registers(regs); + show_stack(NULL); /* print backtrace for kernel stack on this CPU */ + + do_exit(SIGSEGV); +} + +void __init trap_init(void) +{ + + +} |