summaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/kernel')
-rw-r--r--arch/mips/kernel/Makefile81
-rw-r--r--arch/mips/kernel/branch.c193
-rw-r--r--arch/mips/kernel/entry.S661
-rw-r--r--arch/mips/kernel/gdb-low.S2
-rw-r--r--arch/mips/kernel/gdb-stub.c8
-rw-r--r--arch/mips/kernel/head.S422
-rw-r--r--arch/mips/kernel/ipc.c109
-rw-r--r--arch/mips/kernel/irq.c344
-rw-r--r--arch/mips/kernel/jazzdma.c518
-rw-r--r--arch/mips/kernel/ksyms.c40
-rw-r--r--arch/mips/kernel/magnum4000.S261
-rw-r--r--arch/mips/kernel/pica.S252
-rw-r--r--arch/mips/kernel/proc.c62
-rw-r--r--arch/mips/kernel/process.c207
-rw-r--r--arch/mips/kernel/ptrace.c282
-rw-r--r--arch/mips/kernel/r4xx0.S732
-rw-r--r--arch/mips/kernel/setup.c328
-rw-r--r--arch/mips/kernel/signal.c442
-rw-r--r--arch/mips/kernel/syscall.c280
-rw-r--r--arch/mips/kernel/syscalls.h205
-rw-r--r--arch/mips/kernel/sysmips.c113
-rw-r--r--arch/mips/kernel/tags.c70
-rw-r--r--arch/mips/kernel/time.c292
-rw-r--r--arch/mips/kernel/traps.c451
-rw-r--r--arch/mips/kernel/tyne.S114
-rw-r--r--arch/mips/kernel/tynedma.c36
-rw-r--r--arch/mips/kernel/unaligned.c457
-rw-r--r--arch/mips/kernel/vm86.c7
28 files changed, 3434 insertions, 3535 deletions
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index 0086f60cf..b76c723b2 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -1,94 +1,51 @@
#
-# Makefile for the linux kernel.
+# Makefile for the Linux/MIPS 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).
#
-.c.s:
- $(CC) $(CFLAGS) -S $<
-.s.o:
- $(AS) $(ASFLAGS) -o $*.o $<
-.c.o:
- $(CC) $(CFLAGS) -c $<
.S.s:
$(CPP) $(CFLAGS) $< -o $*.s
.S.o:
$(CC) $(CFLAGS) -c $< -o $*.o
-OBJS = process.o signal.o entry.o traps.o irq.o ptrace.o vm86.o ioport.o \
- setup.o bios32.o tynedma.o
-
-include ../../../.config
+all: kernel.o head.o
+EXTRA_ASFLAGS = -mips3 -mcpu=r4000
+O_TARGET := kernel.o
+O_OBJS := branch.o process.o signal.o entry.o traps.o irq.o ptrace.o vm86.o \
+ ioport.o setup.o syscall.o sysmips.o time.o bios32.o ipc.o ksyms.o \
+ unaligned.o tags.o
#
# Kernel debugging
#
-
ifdef CONFIG_REMOTE_DEBUG
-OBJS += gdb-low.o gdb-stub.o
+O_OBJS += gdb-low.o gdb-stub.o
endif
#
-# Board specific code
+# Depending from some other kernel option
#
-
-ifdef CONFIG_MIPS_JAZZ
-OBJS += jazzdma.o
-endif
-
-ifdef CONFIG_ACER_PICA_61
-OBJS += pica.o
-endif
-
-ifdef CONFIG_DESKSTATION_TYNE
-OBJS += tyne.o
-endif
-
-ifdef CONFIG_MIPS_MAGNUM_4000
-OBJS += magnum4000.o
+ifdef CONFIG_PROC_FS
+O_OBJS += proc.o
endif
#
-# CPU model specific code
+# Since we add the same object files to O_OBJS for different configurations.
+# O_OBJS might contain duplicate files. We correct this by filtering out
+# duplicate files. Just to avoid users having to know about all the
+# compatibility stuff between various boards and boards.
#
-ifdef CONFIG_CPU_R4X00
-OBJS += r4xx0.o
-endif
-
-ifdef CONFIG_CPU_R4600
-OBJS += r4xx0.o
-endif
+O_OBJS := $(sort $(O_OBJS))
all: kernel.o head.o
entry.o: entry.S
-
+exception.o: exception.S
head.o: head.S
-magnum4000.o: magnum4000.S
-
-pica.o: pica.S
-
-r4xx0.o: r4xx0.S
-
-tyne.o: tyne.S
-
-kernel.o: $(OBJS)
- $(LD) -r -o kernel.o $(OBJS)
- sync
-
-dep:
- $(CPP) -M *.[cS] > .depend
+clean:
-modules:
-
-dummy:
-
-#
-# include a dependency file if one exists
-#
-ifeq (.depend,$(wildcard .depend))
-include .depend
-endif
+include $(TOPDIR)/Rules.make
diff --git a/arch/mips/kernel/branch.c b/arch/mips/kernel/branch.c
new file mode 100644
index 000000000..32705b320
--- /dev/null
+++ b/arch/mips/kernel/branch.c
@@ -0,0 +1,193 @@
+/*
+ * Branch and jump emulation.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1996 by Ralf Baechle
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <asm/branch.h>
+#include <asm/inst.h>
+#include <asm/ptrace.h>
+#include <asm/uaccess.h>
+
+/*
+ * Compute the return address and do emulate branch and instruction
+ * simulation, if required.
+ */
+int __compute_return_epc(struct pt_regs *regs)
+{
+ unsigned int *addr, bit, fcr31;
+ long epc;
+ union mips_instruction insn;
+
+ epc = regs->cp0_epc;
+ if (epc & 3) {
+ printk("%s: unaligned epc - sending SIGBUS.\n", current->comm);
+ force_sig(SIGBUS, current);
+ return -EFAULT;
+ }
+
+ /*
+ * Read the instruction
+ */
+ addr = (unsigned int *) (unsigned long) epc;
+ if (__get_user(insn.word, addr)) {
+ force_sig(SIGSEGV, current);
+ return -EFAULT;
+ }
+
+ regs->regs[0] = 0;
+ switch (insn.i_format.opcode) {
+ /*
+ * jr and jalr are in r_format format.
+ */
+ case spec_op:
+ switch (insn.r_format.func) {
+ case jalr_op:
+ regs->regs[insn.r_format.rd] = epc + 8;
+ /* Fall through */
+ case jr_op:
+ regs->cp0_epc = regs->regs[insn.r_format.rs];
+ break;
+ }
+ break;
+
+ /*
+ * This group contains:
+ * bltz_op, bgez_op, bltzl_op, bgezl_op,
+ * bltzal_op, bgezal_op, bltzall_op, bgezall_op.
+ */
+ case bcond_op:
+ switch (insn.i_format.rt) {
+ case bltz_op:
+ case bltzl_op:
+ if (regs->regs[insn.i_format.rs] < 0)
+ epc = epc + 4 + (insn.i_format.simmediate << 2);
+ else
+ epc += 8;
+ regs->cp0_epc = epc;
+ break;
+
+ case bgez_op:
+ case bgezl_op:
+ if (regs->regs[insn.i_format.rs] >= 0)
+ epc = epc + 4 + (insn.i_format.simmediate << 2);
+ else
+ epc += 8;
+ regs->cp0_epc = epc;
+ break;
+
+ case bltzal_op:
+ case bltzall_op:
+ regs->regs[31] = epc + 8;
+ if (regs->regs[insn.i_format.rs] < 0)
+ epc = epc + 4 + (insn.i_format.simmediate << 2);
+ else
+ epc += 8;
+ regs->cp0_epc = epc;
+ break;
+
+ case bgezal_op:
+ case bgezall_op:
+ regs->regs[31] = epc + 8;
+ if (regs->regs[insn.i_format.rs] >= 0)
+ epc = epc + 4 + (insn.i_format.simmediate << 2);
+ else
+ epc += 8;
+ regs->cp0_epc = epc;
+ break;
+ }
+ break;
+
+ /*
+ * These are unconditional and in j_format.
+ */
+ case jal_op:
+ regs->regs[31] = regs->cp0_epc + 8;
+ case j_op:
+ epc += 4;
+ epc >>= 28;
+ epc <<= 28;
+ epc |= (insn.j_format.target << 2);
+ regs->cp0_epc = epc;
+ break;
+
+ /*
+ * These are conditional and in i_format.
+ */
+ case beq_op:
+ case beql_op:
+ if (regs->regs[insn.i_format.rs] ==
+ regs->regs[insn.i_format.rt])
+ epc = epc + 4 + (insn.i_format.simmediate << 2);
+ else
+ epc += 8;
+ regs->cp0_epc = epc;
+ break;
+
+ case bne_op:
+ case bnel_op:
+ if (regs->regs[insn.i_format.rs] !=
+ regs->regs[insn.i_format.rt])
+ epc = epc + 4 + (insn.i_format.simmediate << 2);
+ else
+ epc += 8;
+ regs->cp0_epc = epc;
+ break;
+
+ case blez_op: /* not really i_format */
+ case blezl_op:
+ /* rt field assumed to be zero */
+ if (regs->regs[insn.i_format.rs] <= 0)
+ epc = epc + 4 + (insn.i_format.simmediate << 2);
+ else
+ epc += 8;
+ regs->cp0_epc = epc;
+ break;
+
+ case bgtz_op:
+ case bgtzl_op:
+ /* rt field assumed to be zero */
+ if (regs->regs[insn.i_format.rs] > 0)
+ epc = epc + 4 + (insn.i_format.simmediate << 2);
+ else
+ epc += 8;
+ regs->cp0_epc = epc;
+ break;
+
+ /*
+ * And now the FPA/cp1 branch instructions.
+ */
+ case cop1_op:
+ asm ("cfc1\t%0,$31":"=r" (fcr31));
+ bit = (insn.i_format.rt >> 2);
+ bit += bit ? 24 : 23;
+ switch (insn.i_format.rt) {
+ case 0: /* bc1f */
+ case 2: /* bc1fl */
+ if (~fcr31 & (1 << bit))
+ epc = epc + 4 + (insn.i_format.simmediate << 2);
+ else
+ epc += 8;
+ regs->cp0_epc = epc;
+ break;
+
+ case 1: /* bc1t */
+ case 3: /* bc1tl */
+ if (fcr31 & (1 << bit))
+ epc = epc + 4 + (insn.i_format.simmediate << 2);
+ else
+ epc += 8;
+ regs->cp0_epc = epc;
+ break;
+ }
+ break;
+ }
+
+ return 0;
+}
diff --git a/arch/mips/kernel/entry.S b/arch/mips/kernel/entry.S
index 787e2bbf4..6072afae2 100644
--- a/arch/mips/kernel/entry.S
+++ b/arch/mips/kernel/entry.S
@@ -1,28 +1,31 @@
/*
- * arch/mips/kernel/entry.S
+ * Low level exception handling
*
- * Copyright (C) 1994, 1995 Waldorf Electronics
- * written by Ralf Baechle and Andreas Busse
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1994, 1995 by Ralf Baechle
*/
/*
* entry.S contains the system-call and fault low-level handling routines.
* This also contains the timer-interrupt handler, as well as all interrupts
- * and faults that can result in a task-switch. The ISA dependend TLB
- * code is in arch/mips/kernel/tlb.S
+ * and faults that can result in a task-switch. The ISA dependent TLB
+ * code is in arch/mips/<ISA-level>/<cputype>.S
*/
-
#include <linux/sys.h>
#include <asm/asm.h>
#include <asm/errno.h>
-#include <asm/segment.h>
#include <asm/mipsregs.h>
#include <asm/mipsconfig.h>
-#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/stackframe.h>
#include <asm/processor.h>
+#include <asm/regdef.h>
+#include <asm/fpregdef.h>
+#include <asm/unistd.h>
/*
* These are offsets into the task-struct.
@@ -36,6 +39,16 @@ flags = 20
errno = 24
exec_domain = 60
+#ifdef __SMP__
+#error "Fix this for SMP"
+#else
+#define current current_set
+#endif
+
+/*
+ * Heia ... The %lo, %hi and %HI stuff is too strong for the ELF assembler
+ * and the ABI to cope with ...
+ */
.text
.set noreorder
.align 4
@@ -50,126 +63,16 @@ handle_bottom_half:
jal do_bottom_half
mtc0 t0,CP0_STATUS # delay slot
mtc0 s3,CP0_STATUS # Restore old IRQ state
- j 9f
+ b 9f
sw s1,%lo(intr_count)(s0) # delay slot
-reschedule:
- lui ra,%hi(ret_from_sys_call)
- j schedule
- addiu ra,%lo(ret_from_sys_call) # delay slot
-
- .align 5
- NESTED(handle_sys, FR_SIZE, sp)
- .set noat
- SAVE_ALL
- STI
- .set at
- /*
- * Compute return address. We assume that syscalls never
- * appear in delay slots. For the Linux/MIPS libc this
- * assumption is always true.
- */
- lw t3,FR_EPC(sp)
- lw s1,FR_REG2(sp)
- li t0,-ENOSYS
- addiu t3,4
- sw t3,FR_EPC(sp)
- li t2,NR_syscalls
- bge s1,t2,ret_from_sys_call
- sw t0,FR_REG2(sp) # delay slot
- sll s1,PTRLOG
- lw s1,sys_call_table(s1)
- lw s0,current
-
- beqz s1,ret_from_sys_call
- lw t0,flags(s0) # delay slot
- sll t0,26 # PF_TRACESYS
- bltz t0,1f
- sw zero,errno(s0) # delay slot
-
-#if 0
- lw t0,FR_ORIG_REG2(sp)
- beq t0,4,1f
- nop
- la t0,sys_call_names
- lw t1,FR_ORIG_REG2(sp)
- sll t1,2
- addu t0,t1
- lw a1,(t0)
- PRINT("%s(")
- lw a1,FR_REG4(sp)
- lw a2,FR_REG5(sp)
- lw a3,FR_REG6(sp)
- PRINT("%08lx, %08lx, %08lx, ")
- lw a1,FR_REG7(sp)
- lw a2,FR_EPC(sp)
- lw a3,FR_REG31(sp)
- PRINT("%08lx) epc %08lx ra %08lx ")
-1:
-#endif
- lw a0,FR_REG4(sp)
- lw a1,FR_REG5(sp)
- lw a2,FR_REG6(sp)
- lw a3,FR_REG7(sp)
- lw t0,FR_REG3(sp)
- jalr s1 # do the real work
- sw t0,PTRSIZE*4(sp) # delay slot
-
-#if 0
- lw t0,FR_ORIG_REG2(sp)
- beq t0,4,1f
- nop
- sw v0,xxx
- lw a1,xxx
- PRINT("res %08lx\n")
- lw v0,xxx
- .data
-xxx: .word 0
- .text
-1:
-#endif
-
- lw t0,errno(s0)
- sw v0,FR_REG2(sp) # save return value
- subu t0,zero,t0
- beqz t0,ret_from_sys_call
+reschedule: jal schedule
nop # delay slot
- /*
- * Fixme: should set error flag
- */
- j ret_from_sys_call
- sw t0,FR_REG2(sp) # delay slot
-
- .align 4
-1: jal syscall_trace
- nop # delay slot
-
- lw a0,FR_REG4(sp)
- lw a1,FR_REG5(sp)
- lw a2,FR_REG6(sp)
- lw a3,FR_REG7(sp)
- lw t0,FR_REG3(sp)
- jalr s1 # do the real work
- sw t0,PTRSIZE*4(sp) # delay slot
-
- lw t0,errno(s0)
- sw v0,FR_REG2(sp)
- subu t0,zero,t0 # delay slot
- beqz t0,1f
- nop # delay slot
- /*
- * Fixme: should set error flag
- */
-1: jal syscall_trace
- sw t0,FR_REG2(sp) # delay slot
-
- .align 4
- .globl ret_from_sys_call
-ret_from_sys_call:
+EXPORT(ret_from_sys_call)
lw t0,intr_count # bottom half
bnez t0,return
-9:
- lw t0,bh_mask # delay slot
+
+9: lw t0,bh_mask # delay slot
lw t1,bh_active # unused delay slot
and t0,t1
bnez t0,handle_bottom_half
@@ -187,15 +90,11 @@ ret_from_sys_call:
lw s0,current
lw t0,task
- lw t1,state(s0) # state
- beq s0,t0,return # task[0] cannot have signals
- lw t0,counter(s0) # counter
- bnez t1,reschedule # state == 0 ?
lw a0,blocked(s0)
+ beq s0,t0,return # task[0] cannot have signals
# save blocked in a0 for
# signal handling
- beqz t0,reschedule # counter == 0 ?
- lw t0,signal(s0)
+ lw t0,signal(s0) # delay slot
nor t1,zero,a0
and t1,t0,t1
beqz t1,return
@@ -203,423 +102,225 @@ ret_from_sys_call:
jal do_signal
move a1,sp # delay slot
-
+
.set noat
- .globl return
-return: RESTORE_ALL
+EXPORT(return)
+ RESTORE_ALL
ERET
.set at
- END(handle_sys)
/*
- * Beware: interrupt, fast_interrupt and bad_interrupt have unusal
- * calling conventions!
+ * Beware: timer_interrupt, interrupt, fast_interrupt and bad_interrupt
+ * have unusual calling conventions to speedup the mess.
*
- * t1 - interrupt number
+ * a0 - interrupt number
* s2 - destroyed
* return values:
* v0 - return routine
+ *
+ * The timer interrupt is handled specially to insure that the jiffies
+ * variable is updated at all times. Specifically, the timer interrupt is
+ * just like the complete handlers except that it is invoked with interrupts
+ * disabled and should never re-enable them. If other interrupts were
+ * allowed to be processed while the timer interrupt is active, then the
+ * other interrupts would have to avoid using the jiffies variable for delay
+ * and interval timing operations to avoid hanging the system.
*/
.text
.set at
.align 5
- NESTED(interrupt, FR_SIZE, sp)
+NESTED(timer_interrupt, FR_SIZE, sp)
+ move s2,ra
+ jal do_IRQ
+ move a1,sp # delay slot
+ .set reorder
+ la v0,ret_from_sys_call
+ jr s2
+ .set noreorder
+ END(timer_interrupt)
+
+ .align 5
+NESTED(interrupt, FR_SIZE, sp)
move s2,ra
mfc0 t0,CP0_STATUS # enable IRQs
ori t0,0x1f
xori t0,0x1e
mtc0 t0,CP0_STATUS
- move a0,t1
jal do_IRQ
move a1,sp # delay slot
mfc0 t0,CP0_STATUS # disable IRQs
ori t0,1
xori t0,1
+ mtc0 t0,CP0_STATUS
+ .set reorder
la v0,ret_from_sys_call
jr s2
- mtc0 t0,CP0_STATUS # delay slot
+ .set noreorder
END(interrupt)
.align 5
- NESTED(fast_interrupt, FR_SIZE, sp)
+NESTED(fast_interrupt, FR_SIZE, sp)
+ .set reorder
move s2,ra
- move a0,t1
jal do_fast_IRQ
- move a1,sp # delay slot
- lui v0,%hi(return)
+ la v0,return
jr s2
- addiu v0,%lo(return) # delay slot
+ .set noreorder
END(fast_interrupt)
- LEAF(bad_interrupt)
/*
* Don't return & unblock the pic
*/
+LEAF(bad_interrupt)
+ .set reorder
+ lw t0,%lo(intr_count)(s3)
+ subu t0,1
+ .set noreorder
j return
- nop
+ sw t0,%lo(intr_count)(s3) # delay slot
END(bad_interrupt)
- .align 5
- LEAF(spurious_interrupt)
+ .text
+ .align 5
+LEAF(spurious_interrupt)
/*
- * Nothing happened... (whistle)
+ * Someone tried to fool us by sending an interrupt but we
+ * couldn't find a cause for it.
*/
- lui t1,%hi(spurious_count)
- lw t0,%lo(spurious_count)(t1)
- la v0,return
- addiu t0,1
- jr ra
- sw t0,%lo(spurious_count)(t1)
+ lui t1,%hi(spurious_count)
+ lw t0,%lo(spurious_count)(t1)
+ la v0,return
+ addiu t0,1
+ jr ra
+ sw t0,%lo(spurious_count)(t1)
END(spurious_interrupt)
-
-
/*
- * Build a default exception handler for the other R4x00 exceptions
+ * Build a default exception handler for the exceptions that don't need
+ * special handlers. If you didn't know yet - I *like* playing games with
+ * the C preprocessor ...
*/
-#define BUILD_HANDLER(exception) \
+#define __BUILD_clear_none(exception) \
+ REG_S sp,FR_ORIG_REG2(sp); /* sp < 0 */
+#define __BUILD_clear_sys(exception) \
+ REG_S v0,FR_ORIG_REG2(sp); \
+ REG_S a3,FR_ORIG_REG7(sp);
+#define __BUILD_clear_fpe(exception) \
+ REG_S sp,FR_ORIG_REG2(sp); /* sp < 0 */ \
+ cfc1 a1,fcr31; \
+ li a2,~(0x3f<<12); \
+ and a2,a1; \
+ ctc1 a2,fcr31;
+#define __BUILD_clear_watch(exception) \
+ REG_S sp,FR_ORIG_REG2(sp); /* sp < 0 */ \
+ mtc0 zero,CP0_WATCHLO; \
+ mtc0 zero,CP0_WATCHHI
+#define __BUILD_clear_ade(exception) \
+ REG_S sp,FR_ORIG_REG2(sp); /* sp < 0 */ \
+ MFC0 t0,CP0_BADVADDR; \
+ REG_S t0,FR_BADVADDR(sp);
+#define __BUILD_silent(exception)
+#if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2)
+#define fmt "Got %s at %08x.\n"
+#endif
+#if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4)
+#define fmt "Got %s at %016Lx.\n"
+#endif
+#define __BUILD_verbose(exception) \
+ la a1,8f; \
+ TEXT (#exception); \
+ REG_L a2,FR_EPC(sp); \
+ PRINT(fmt)
+#define __BUILD_count(exception) \
+ .set reorder; \
+ lw t0,exception_count_##exception; \
+ addiu t0,1; \
+ sw t0,exception_count_##exception; \
+ .set noreorder; \
+ .data; \
+EXPORT(exception_count_##exception); \
+ .word 0; \
+ .text;
+#define BUILD_HANDLER(exception,handler,clear,verbose) \
+ .text; \
.align 5; \
NESTED(handle_##exception, FR_SIZE, sp); \
.set noat; \
SAVE_ALL; \
+ __BUILD_clear_##clear(exception); \
STI; \
.set at; \
- la a1,8f; \
- TEXT (#exception); \
- lw a2,FR_EPC(sp); \
- PRINT("Got %s at %08x.\n"); \
- li a0,0; \
- li t0,-1; /* not a sys call */ \
- sw t0,FR_ORIG_REG2(sp); \
- jal do_##exception; \
+ __BUILD_##verbose(exception); \
+ REG_S sp,FR_ORIG_REG2(sp); /* not a sys call */ \
+ jal do_##handler; \
move a0,sp; /* delay slot */ \
j ret_from_sys_call; \
nop; /* delay slot */ \
END(handle_##exception)
- BUILD_HANDLER(adel)
- BUILD_HANDLER(ades)
- BUILD_HANDLER(ibe)
- BUILD_HANDLER(dbe)
- BUILD_HANDLER(ov)
- BUILD_HANDLER(fpe)
- BUILD_HANDLER(bp)
- BUILD_HANDLER(tr)
- BUILD_HANDLER(ri)
- BUILD_HANDLER(cpu)
- BUILD_HANDLER(vcei)
- BUILD_HANDLER(vced)
- BUILD_HANDLER(watch)
- BUILD_HANDLER(reserved)
-
+ BUILD_HANDLER(adel,ade,ade,silent) /* #4 */
+ BUILD_HANDLER(ades,ade,ade,silent) /* #5 */
+ BUILD_HANDLER(ibe,ibe,none,verbose) /* #6 */
+ BUILD_HANDLER(dbe,dbe,none,verbose) /* #7 */
+ BUILD_HANDLER(sys,sys,none,silent) /* #8 */
+ BUILD_HANDLER(bp,bp,none,silent) /* #9 */
+ BUILD_HANDLER(ri,ri,none,silent) /* #10 */
+ BUILD_HANDLER(cpu,cpu,none,silent) /* #11 */
+ BUILD_HANDLER(ov,ov,none,silent) /* #12 */
+ BUILD_HANDLER(tr,tr,none,silent) /* #13 */
+ BUILD_HANDLER(vcei,vcei,none,verbose) /* #14 */
+ BUILD_HANDLER(fpe,fpe,fpe,silent) /* #15 */
+ BUILD_HANDLER(watch,watch,watch,verbose) /* #23 */
+ BUILD_HANDLER(vced,vced,none,verbose) /* #31 */
+ BUILD_HANDLER(reserved,reserved,none,verbose) /* others */
/*
* Exception handler table with 32 entries.
* This might be extended to handle software exceptions
*/
.bss
- .align 2
- EXPORT(exception_handlers)
- .fill 32,4,0
+ .align PTRLOG
+EXPORT(exception_handlers)
+ .fill 32,PTRSIZE,0
+
+/*
+ * Interrupt handler table with 16 entries.
+ */
+EXPORT(IRQ_vectors)
+ .fill 16,PTRSIZE,0
/*
* Table of syscalls
*/
.data
- EXPORT(sys_call_table)
- PTR sys_setup /* 0 */
- PTR sys_exit
- PTR sys_fork
- PTR sys_read
- PTR sys_write
- PTR sys_open /* 5 */
- PTR sys_close
- PTR sys_waitpid
- PTR sys_creat
- PTR sys_link
- PTR sys_unlink /* 10 */
- PTR sys_execve
- PTR sys_chdir
- PTR sys_time
- PTR sys_mknod
- PTR sys_chmod /* 15 */
- PTR sys_chown
- PTR sys_break
- PTR sys_stat
- PTR sys_lseek
- PTR sys_getpid /* 20 */
- PTR sys_mount
- PTR sys_umount
- PTR sys_setuid
- PTR sys_getuid
- PTR sys_stime /* 25 */
- PTR sys_ptrace
- PTR sys_alarm
- PTR sys_fstat
- PTR sys_pause
- PTR sys_utime /* 30 */
- PTR sys_stty
- PTR sys_gtty
- PTR sys_access
- PTR sys_nice
- PTR sys_ftime /* 35 */
- PTR sys_sync
- PTR sys_kill
- PTR sys_rename
- PTR sys_mkdir
- PTR sys_rmdir /* 40 */
- PTR sys_dup
- PTR sys_pipe
- PTR sys_times
- PTR sys_prof
- PTR sys_brk /* 45 */
- PTR sys_setgid
- PTR sys_getgid
- PTR sys_signal
- PTR sys_geteuid
- PTR sys_getegid /* 50 */
- PTR sys_acct
- PTR sys_phys
- PTR sys_lock
- PTR sys_ioctl
- PTR sys_fcntl /* 55 */
- PTR sys_mpx
- PTR sys_setpgid
- PTR sys_ulimit
- PTR sys_olduname
- PTR sys_umask /* 60 */
- PTR sys_chroot
- PTR sys_ustat
- PTR sys_dup2
- PTR sys_getppid
- PTR sys_getpgrp /* 65 */
- PTR sys_setsid
- PTR sys_sigaction
- PTR sys_sgetmask
- PTR sys_ssetmask
- PTR sys_setreuid /* 70 */
- PTR sys_setregid
- PTR sys_sigsuspend
- PTR sys_sigpending
- PTR sys_sethostname
- PTR sys_setrlimit /* 75 */
- PTR sys_getrlimit
- PTR sys_getrusage
- PTR sys_gettimeofday
- PTR sys_settimeofday
- PTR sys_getgroups /* 80 */
- PTR sys_setgroups
- PTR sys_select
- PTR sys_symlink
- PTR sys_lstat
- PTR sys_readlink /* 85 */
- PTR sys_uselib
- PTR sys_swapon
- PTR sys_reboot
- PTR old_readdir
- PTR sys_mmap /* 90 */
- PTR sys_munmap
- PTR sys_truncate
- PTR sys_ftruncate
- PTR sys_fchmod
- PTR sys_fchown /* 95 */
- PTR sys_getpriority
- PTR sys_setpriority
- PTR sys_profil
- PTR sys_statfs
- PTR sys_fstatfs /* 100 */
- PTR sys_ioperm
- PTR sys_socketcall
- PTR sys_syslog
- PTR sys_setitimer
- PTR sys_getitimer /* 105 */
- PTR sys_newstat
- PTR sys_newlstat
- PTR sys_newfstat
- PTR sys_uname
- PTR sys_iopl /* 110 */
- PTR sys_vhangup
- PTR sys_idle
- PTR sys_vm86
- PTR sys_wait4
- PTR sys_swapoff /* 115 */
- PTR sys_sysinfo
- PTR sys_ipc
- PTR sys_fsync
- PTR sys_sigreturn
- PTR sys_clone /* 120 */
- PTR sys_setdomainname
- PTR sys_newuname
- PTR 0 #sys_modify_ldt
- PTR sys_adjtimex
- PTR sys_mprotect /* 125 */
- PTR sys_sigprocmask
- PTR sys_create_module
- PTR sys_init_module
- PTR sys_delete_module
- PTR sys_get_kernel_syms /* 130 */
- PTR sys_quotactl
- PTR sys_getpgid
- PTR sys_fchdir
- PTR sys_bdflush
- PTR sys_sysfs /* 135 */
- PTR sys_personality
- PTR 0 /* for afs_syscall */
- PTR sys_setfsuid
- PTR sys_setfsgid
- PTR sys_llseek /* 140 */
- PTR sys_getdents
- PTR sys_select
- PTR sys_flock
- .space (NR_syscalls-140)*4
+ .align PTRLOG
+EXPORT(sys_call_table)
+ /*
+ * Reserved space for all the SVR4, SVR, BSD43 and POSIX
+ * flavoured syscalls.
+ */
+ .space (__NR_Linux)*PTRSIZE
- .bss
- EXPORT(IRQ_vectors)
- .fill 16,4,0
+ /*
+ * Linux flavoured syscalls.
+ */
+#define SYS(call, narg) PTR call
+#include "syscalls.h"
- .text
-sys_call_names:
- TTABLE ("setup")
- TTABLE ("exit")
- TTABLE ("fork")
- TTABLE ("read")
- TTABLE ("write")
- TTABLE ("open")
- TTABLE ("close")
- TTABLE ("waitpid")
- TTABLE ("creat")
- TTABLE ("link")
- TTABLE ("unlink")
- TTABLE ("execve")
- TTABLE ("chdir")
- TTABLE ("time")
- TTABLE ("mknod")
- TTABLE ("chmod")
- TTABLE ("chown")
- TTABLE ("break")
- TTABLE ("stat")
- TTABLE ("lseek")
- TTABLE ("getpid")
- TTABLE ("mount")
- TTABLE ("umount")
- TTABLE ("setuid")
- TTABLE ("getuid")
- TTABLE ("stime")
- TTABLE ("ptrace")
- TTABLE ("alarm")
- TTABLE ("fstat")
- TTABLE ("pause")
- TTABLE ("utime")
- TTABLE ("stty")
- TTABLE ("gtty")
- TTABLE ("access")
- TTABLE ("nice")
- TTABLE ("ftime")
- TTABLE ("sync")
- TTABLE ("kill")
- TTABLE ("rename")
- TTABLE ("mkdir")
- TTABLE ("rmdir")
- TTABLE ("dup")
- TTABLE ("pipe")
- TTABLE ("times")
- TTABLE ("prof")
- TTABLE ("brk")
- TTABLE ("setgid")
- TTABLE ("getgid")
- TTABLE ("signal")
- TTABLE ("geteuid")
- TTABLE ("getegid")
- TTABLE ("acct")
- TTABLE ("phys")
- TTABLE ("lock")
- TTABLE ("ioctl")
- TTABLE ("fcntl")
- TTABLE ("mpx")
- TTABLE ("setpgid")
- TTABLE ("ulimit")
- TTABLE ("olduname")
- TTABLE ("umask")
- TTABLE ("chroot")
- TTABLE ("ustat")
- TTABLE ("dup2")
- TTABLE ("getppid")
- TTABLE ("getpgrp")
- TTABLE ("setsid")
- TTABLE ("sigaction")
- TTABLE ("sgetmask")
- TTABLE ("ssetmask")
- TTABLE ("setreuid")
- TTABLE ("setregid")
- TTABLE ("sigsuspend")
- TTABLE ("sigpending")
- TTABLE ("sethostname")
- TTABLE ("setrlimit")
- TTABLE ("getrlimit")
- TTABLE ("getrusage")
- TTABLE ("gettimeofday")
- TTABLE ("settimeofday")
- TTABLE ("getgroups")
- TTABLE ("setgroups")
- TTABLE ("select")
- TTABLE ("symlink")
- TTABLE ("lstat")
- TTABLE ("readlink")
- TTABLE ("uselib")
- TTABLE ("swapon")
- TTABLE ("reboot")
- TTABLE ("readdir")
- TTABLE ("mmap")
- TTABLE ("munmap")
- TTABLE ("truncate")
- TTABLE ("ftruncate")
- TTABLE ("fchmod")
- TTABLE ("fchown")
- TTABLE ("getpriority")
- TTABLE ("setpriority")
- TTABLE ("profil")
- TTABLE ("statfs")
- TTABLE ("fstatfs")
- TTABLE ("ioperm")
- TTABLE ("socketcall")
- TTABLE ("syslog")
- TTABLE ("setitimer")
- TTABLE ("getitimer")
- TTABLE ("newstat")
- TTABLE ("newlstat")
- TTABLE ("newfstat")
- TTABLE ("uname")
- TTABLE ("iopl")
- TTABLE ("vhangup")
- TTABLE ("idle")
- TTABLE ("vm86")
- TTABLE ("wait4")
- TTABLE ("swapoff")
- TTABLE ("sysinfo")
- TTABLE ("ipc")
- TTABLE ("fsync")
- TTABLE ("sigreturn")
- TTABLE ("clone")
- TTABLE ("setdomainname")
- TTABLE ("newuname")
- TTABLE ("modify_ldt (unused)")
- TTABLE ("adjtimex")
- TTABLE ("mprotect")
- TTABLE ("sigprocmask")
- TTABLE ("create_module")
- TTABLE ("init_module")
- TTABLE ("delete_module")
- TTABLE ("get_kernel_syms")
- TTABLE ("quotactl")
- TTABLE ("getpgid")
- TTABLE ("fchdir")
- TTABLE ("bdflush")
- TTABLE ("sysfs")
- TTABLE ("personality")
- TTABLE ("afs_syscall") /* for afs_syscall */
- TTABLE ("setfsuid")
- TTABLE ("setfsgid")
- TTABLE ("llseek")
- TTABLE ("sys_getdents")
- TTABLE ("sys_select")
- TTABLE ("sys_flock")
+/*
+ * Number of arguments of each syscall
+ * FIXME: This table contains huge empty areas wasting memory.
+ */
+EXPORT(sys_narg_table)
+ /*
+ * Reserved space for all the SVR4, SVR, BSD43 and POSIX
+ * flavoured syscalls.
+ */
+ .space (__NR_Linux)
+
+ /*
+ * Linux flavoured syscalls.
+ */
+#undef SYS
+#define SYS(call, narg) .byte narg
+#include "syscalls.h"
diff --git a/arch/mips/kernel/gdb-low.S b/arch/mips/kernel/gdb-low.S
index ea775e732..9b948a845 100644
--- a/arch/mips/kernel/gdb-low.S
+++ b/arch/mips/kernel/gdb-low.S
@@ -9,9 +9,9 @@
#include <linux/sys.h>
#include <asm/asm.h>
-#include <asm/segment.h>
#include <asm/mipsregs.h>
#include <asm/mipsconfig.h>
+#include <asm/regdef.h>
#include <asm/stackframe.h>
#include <asm/gdb-stub.h>
diff --git a/arch/mips/kernel/gdb-stub.c b/arch/mips/kernel/gdb-stub.c
index 7708feac0..9a3240152 100644
--- a/arch/mips/kernel/gdb-stub.c
+++ b/arch/mips/kernel/gdb-stub.c
@@ -68,10 +68,10 @@
#include <linux/signal.h>
#include <linux/kernel.h>
+#include <asm/addrspace.h>
#include <asm/asm.h>
#include <asm/mipsregs.h>
-#include <asm/segment.h>
-#include <asm/cachectl.h>
+#include <asm/cache.h>
#include <asm/system.h>
#include <asm/gdb-stub.h>
@@ -407,7 +407,7 @@ static int hexToInt(char **ptr, int *intValue)
}
/*
- * This function does all command procesing for interfacing to gdb. It
+ * This function does all command processing for interfacing to gdb. It
* returns 1 if you should skip the instruction at the trap address, 0
* otherwise.
*/
@@ -605,7 +605,7 @@ void handle_exception (struct gdb_regs *regs)
* NB: We flush both caches, just to be sure...
*/
- sys_cacheflush((void *)KSEG0,KSEG1-KSEG0,BCACHE);
+ cacheflush((void *)KSEG0, KSEG1-KSEG0, CF_BCACHE|CF_ALL);
return;
/* NOTREACHED */
break;
diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S
index a2cb43de3..82de12ff5 100644
--- a/arch/mips/kernel/head.S
+++ b/arch/mips/kernel/head.S
@@ -1,16 +1,30 @@
/*
* arch/mips/kernel/head.S
*
- * Copyright (C) 1994, 1995 Waldorf Electronics
+ * Copyright (C) 1994, 1995 Waldorf Electronics, 1996 Paul M. Antoine
* Written by Ralf Baechle and Andreas Busse
+ * Modified for DECStation and hence R3000 support by Paul M. Antoine
+ * Additional R3000 support by Didier Frick <dfrick@dial.eunet.ch>
+ * for ACN S.A, Copyright (C) 1996 by ACN S.A
*
* Head.S contains the MIPS exception handler and startup code.
+ *
+ * FIXME: Note that the #ifdef's for R4X00 assume R3000 is the #else
+ * case, which is a little naughty. We also do NOT need the
+ * dec_entry goo at the begining of all this - PMA
+ * FIXME: This #ifdef stuff is ugly and I should move the tlb/exception
+ * handler code out into some other file - Ralf
+ * Take the zillions of (_MIPS_ISA == _MIPS_ISA_MIPSx) as a temporary
+ * solution. I know how they look ...
*/
+#include <linux/config.h> /* For the DECstation hacks */
#include <linux/tasks.h>
+#include <asm/addrspace.h>
#include <asm/asm.h>
-#include <asm/segment.h>
-#include <asm/cachectl.h>
+#include <asm/processor.h>
+#include <asm/regdef.h>
+#include <asm/cache.h>
#include <asm/mipsregs.h>
#include <asm/mipsconfig.h>
#include <asm/stackframe.h>
@@ -18,14 +32,33 @@
#define PAGE_SIZE 0x1000
+/*
+ * FIXME: I still think the following should be in an include file (see
+ * also the reference in arch/mips/mips1/r3000.S - PMA
+ */
+#if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2)
+#define MODE_GLOBAL 0x0100 /* shared for all processes */
+#define MODE_ALIAS 0x00e0
+#endif
+#if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4) || \
+ (_MIPS_ISA == _MIPS_ISA_MIPS5)
#define MODE_GLOBAL 0x0001 /* shared for all processes */
#define MODE_ALIAS 0x0016 /* uncachable */
+#endif
- .text
- .set mips3
/*
+ * The two symbols begin_except and end_except mark the range that is copied
+ * to KSEG0 on startup.
+ */
+EXPORT(begin_except)
+ .text
+/*
* This is space for the interrupt handlers.
- * They are located at virtual address KSEG[01] (physical 0x0)
+ * After trap_init() they are located at virtual address KSEG0.
+ *
+ * For some machine where the kernel doesn't get directly loaded to KSEG0
+ * the exceptionhandler get copied to KSEG0. They therefore must be
+ * relocatable code.
*/
/*
* TLB refill, EXL == 0
@@ -33,6 +66,17 @@
.set noreorder
.set noat
LEAF(except_vec0)
+#if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2)
+ mfc0 k1,CP0_CONTEXT
+ nop
+ lw k0,(k1) # May cause another exception
+ mfc0 k1,CP0_EPC # Get the return address
+ srl k0,12 # Convert to EntryLo format
+ mtc0 k0,CP0_ENTRYLO0
+#endif
+#if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4) || \
+ (_MIPS_ISA == _MIPS_ISA_MIPS5)
+ .set mips3
dmfc0 k1,CP0_CONTEXT
dsra k1,1
lwu k0,(k1) # May cause another exception
@@ -41,37 +85,103 @@
dsrl k1,6 # Convert to EntryLo format
dmtc0 k0,CP0_ENTRYLO0
dmtc0 k1,CP0_ENTRYLO1
+#endif
+#ifndef CONFIG_OPTIMIZE_R4600
nop # Needed for R4[04]00 pipeline
+#endif
tlbwr
nop # Needed for R4[04]00 pipeline
nop
+#ifndef CONFIG_OPTIMIZE_R4600
nop
+#endif
+#if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2)
+ jr k1
+ rfe
+#endif
+#if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4) || \
+ (_MIPS_ISA == _MIPS_ISA_MIPS5)
eret
/*
- * Workaround for R4000 bug. For explanation see MIPS
- * docs. Note that this that obscure that it wont almost
- * never happen. Well, but Mips writes about it's bugs.
+ * Partial workaround for R4000 bug. For explanation see
+ * MIPS docs. Note that this that obscure that it wont
+ * almost never happen. Well, but Mips writes about it's bugs.
*/
nop
eret
+#endif
END(except_vec0)
+/******************************************************************************/
+
/*
* XTLB refill, EXL == 0
- * Should never be reached
+ * Should never be reached on R4000.
*/
.org except_vec0+0x80
- LEAF(except_vec1)
- PANIC("XTLB Refill exception.\n")
-1: j 1b
+ NESTED(except_vec1, 0, sp)
+ .set noat
+ /*
+ * Register saving is delayed as long as we don't know
+ * which registers really need to be saved.
+ */
+#if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2)
+ mfc0 k1,CP0_CONTEXT
nop
+ lw k0,(k1) # May cause another exception
+ mfc0 k1,CP0_EPC # Get the return address
+ srl k0,12 # Convert to EntryLo format
+ mtc0 k0,CP0_ENTRYLO0
+#endif
+#if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4) || \
+ (_MIPS_ISA == _MIPS_ISA_MIPS5)
+ mfc0 k1,CP0_CONTEXT
+ dsra k1,1
+ lwu k0,(k1) # May cause another exception
+ lwu k1,4(k1)
+ dsrl k0,6 # Convert to EntryLo format
+ dsrl k1,6 # Convert to EntryLo format
+ dmtc0 k0,CP0_ENTRYLO0
+ dmtc0 k1,CP0_ENTRYLO1
+#endif
+ nop # Needed for R4[04]00 pipeline
+ tlbwr
+ nop # Needed for R4[04]00 pipeline
+ nop
+#if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2)
+ jr k1
+ rfe
+#endif
+#if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4) || \
+ (_MIPS_ISA == _MIPS_ISA_MIPS5)
+ nop
+ eret
+#endif
+ /*
+ * Partial workaround for R4000 bug. For explanation see
+ * MIPS docs. Note that this that obscure that it wont
+ * almost never happen. Well, but Mips writes about it's bugs.
+ */
+ nop
+ eret
END(except_vec1)
+/******************************************************************************/
+
/*
* Cache Error
*/
.org except_vec1+0x80
LEAF(except_vec2)
+#if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4)
+ /*
+ * On the R3000, this is the "Uncached TLB Miss" handler.
+ */
+ j except_vec0
+ nop
+#endif
+#if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4) || \
+ (_MIPS_ISA == _MIPS_ISA_MIPS5)
/*
* Famous last words: unreached
*/
@@ -79,8 +189,11 @@
PRINT("Cache error exception: c0_errorepc == %08x\n")
1: j 1b
nop
+#endif
END(except_vec2)
+/******************************************************************************/
+
/*
* General exception vector.
*/
@@ -90,6 +203,8 @@
/*
* Register saving is delayed as long as we don't know
* which registers really need to be saved.
+ * Except for k1 which MUST be preserved to allow
+ * nested TLB refill exceptions on the R3000.
*/
mfc0 k1,CP0_CAUSE
la k0,exception_handlers
@@ -107,46 +222,89 @@
END(except_vec3)
.set at
+EXPORT(end_except)
+
/******************************************************************************/
/*
* Kernel entry
*/
.set noreorder
+
NESTED(kernel_entry, 16, sp)
/*
+ * The following two symbols are used for kernel profiling.
+ */
+ EXPORT(stext)
+ EXPORT(_stext)
+
+ /*
+ * Initialize the global pointer, if required.
+ */
+ LOAD_GP
+
+ /*
+ * First setup stack for kernel and init
+ */
+ la sp,init_user_stack+(KERNEL_STACK_SIZE-4*SZREG)
+ la t0,init_kernel_stack+(KERNEL_STACK_SIZE)
+ LONG_S t0,kernelsp
+
+ /*
* Clear BSS first so that there are no surprises...
*/
la t0,_edata
la t1,_end
- sw zero,(t0)
-1: addiu t0,4
+ sb zero,(t0)
+1: addiu t0,1
+#if (_MIPS_ISA == _MIPS_ISA_MIPS1)
+ /*
+ * Paul, this clears one word too much - Ralf
+ */
+ bne t0,t1,1b
+#endif
+#if (_MIPS_ISA == _MIPS_ISA_MIPS2) || \
+ (_MIPS_ISA == _MIPS_ISA_MIPS3) || \
+ (_MIPS_ISA == _MIPS_ISA_MIPS4)
bnel t0,t1,1b
- sw zero,(t0)
+#endif
+ sb zero,(t0) # delay slot
+
+ /*
+ * Get the memory upper limit the bootloader passed to us
+ * in a0
+ */
+ sw a0,mips_memory_upper
+
+ /*
+ * Get the very one tags we need early in the boot process
+ */
+ jal bi_EarlySnarf
+ nop
/*
* Initialize low level part of memory management
+ * First flush the TLB to make sure that we don't get a
+ * TLB shutdown during wire_mappings.
*/
jal tlbflush
+#if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2)
+ nop
+#endif
+#if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4)
mtc0 zero,CP0_WIRED # delay slot
+#endif
jal wire_mappings
nop
- jal tlbflush
- nop
-
- /*
- * Stack for kernel and init
- */
- la sp,init_user_stack+PAGE_SIZE-24
- la t0,init_kernel_stack+PAGE_SIZE
- sw t0,kernelsp
/*
* Disable coprocessors
*/
mfc0 t0,CP0_STATUS
- li t1,~(ST0_CU0|ST0_CU1|ST0_CU2|ST0_CU3)
+ li t1,~(ST0_CU1|ST0_CU2|ST0_CU3)
and t0,t1
+ li t1,ST0_CU0
+ or t0,ST0_CU0
mtc0 t0,CP0_STATUS
1: jal start_kernel
@@ -167,13 +325,18 @@
* Get base address of map0 table for the
* the board we're running on
*/
- la t0,boot_info
- lw t1,OFFSET_BOOTINFO_MACHTYPE(t0)
- la t0,map0table
- sll t1,PTRLOG # machtype used as index
+ lw t1,mips_machgroup # mips_machgroup is set by
+ # bi_EarlySnarf()
+ la t0,map0table
+ sll t1,PTRLOG # machgroup used as index
addu t0,t1
- lw t0,(t0) # get base address
-
+ lw t1,mips_machtype # mips_machtype is set by
+ # bi_EarlySnarf()
+ lw t0,(t0) # load table @ for the group
+ sll t1,PTRLOG # machtype used as index
+ addu t0,t1
+ lw t0,(t0) # load table @ for the box
+ nop
/*
* Get number of wired TLB entries and
* loop over selected map0 table.
@@ -182,18 +345,33 @@
move t2,zero # TLB entry counter
addiu t3,t1,1 # wire one additional entry
beqz t1,2f # null, exit
+#if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2)
+ nop
+#endif
+#if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4)
mtc0 t3,CP0_WIRED # delay slot
+#endif
addiu t0,8
1: lw t4,24(t0) # PageMask
ld t5,0(t0) # entryHi
ld t6,8(t0) # entryLo0
+#if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4)
ld t7,16(t0) # entryLo1
+#endif
addiu t2,1 # increment ctr
mtc0 t2,CP0_INDEX # set TLB entry
+#if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2)
+ nop
+ mtc0 t5,CP0_ENTRYHI
+ nop
+ mtc0 t6,CP0_ENTRYLO0
+#endif
+#if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4)
mtc0 t4,CP0_PAGEMASK
dmtc0 t5,CP0_ENTRYHI
dmtc0 t6,CP0_ENTRYLO0
dmtc0 t7,CP0_ENTRYLO1
+#endif
addiu t0,32
bne t1,t2,1b # next TLB entry
tlbwi # delay slot
@@ -202,21 +380,40 @@
* We use only 4k pages. Therefore the PageMask register
* is expected to be setup for 4k pages.
*/
-2: li t0,PM_4K
+2:
+#if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4)
+ li t0,PM_4K
mtc0 t0,CP0_PAGEMASK
+#endif
/*
* Now map the pagetables
*/
mtc0 zero,CP0_INDEX
la t0,TLB_ROOT
+#if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2)
+ mtc0 t0,CP0_ENTRYHI
+ nop
+#endif
+#if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4)
dmtc0 t0,CP0_ENTRYHI
+#endif
la t0,swapper_pg_dir-KSEG1
+#if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2)
+ srl t0,12
+#endif
+#if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4)
srl t0,6
+#endif
ori t0,(MODE_ALIAS|MODE_GLOBAL) # uncachable, dirty, valid
+#if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2)
+ mtc0 t0,CP0_ENTRYLO0
+#endif
+#if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4)
dmtc0 t0,CP0_ENTRYLO0
li t0,MODE_GLOBAL
dmtc0 t0,CP0_ENTRYLO1
+#endif
nop
tlbwi # delayed
@@ -227,54 +424,18 @@
* NEVER be changed.
*/
li t0,TLBMAP
+#if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2)
+ srl t0,1 # this is a guess!
+ mtc0 t0,CP0_CONTEXT
+#endif
+#if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4)
dsll t0,1
dmtc0 t0,CP0_CONTEXT
+#endif
jr ra # delay slot
nop
END(wire_mappings)
-/*
- * Just for debugging...
- */
- .set noreorder
- LEAF(beep)
- lw t0,beepflag
- nop
- bnez t0,1f
- lbu t0,0xe2000061
- xori t0,3
- sb t0,0xe2000061
- li t0,1
- sw t0,beepflag
-1: jr ra
- nop
- END(beep)
-
- .bss
-beepflag: .word 0
- .text
-
-/*
- * Compute kernel code checksum to check kernel code against corruption
- */
- LEAF(csum)
- jal sys_cacheflush
- move t8,ra # delay slot
- li t0,KSEG1
- la t1,final
- li t2,KSEG1
- or t0,t2
- or t1,t2
- move v0,zero
-1: lw t2,(t0)
- addiu t0,4
- bne t0,t1,1b
- xor v0,t2
- jr t8
- nop
- END(csum)
-final:
-
.data
/*
* Build an entry for table of wired entries
@@ -292,6 +453,11 @@ final:
* following by EntryHi/EntryLo pairs and page mask.
* Since everything must be quad-aligned (8) we insert
* some dummy zeros.
+ *
+ * Keep in mind that the PFN does not depend on the page size in the
+ * TLB page mask register. See milo's lib/dumptlb.c for how to decode
+ * and encode these entries. Don't see the same routine in the linux
+ * kernel distribution, since it is older and unreliable.
*/
/*
@@ -299,25 +465,56 @@ final:
* Add your own stuff here but don't forget to define your
* target system in bootinfo.h
*/
+/* First indirection level on the 'group' */
+map0table: PTR map0table_unknown # machgroup = unknown
+ PTR map0table_jazz # machgroup = JAZZ
+ PTR map0table_dec # machgroup = DEC
+ PTR map0table_arc # machgroup = ARC
+ PTR map0table_sni_rm # machgroup = SNI_RM
+ PTR map0table_acn # machgroup = ACN
+ .word 0 # pad
+
+/* table for group 'unknown' */
+map0table_unknown: PTR map0_dummy # machtype = unknown
+ .word 0 # pad
+
+/* table for group 'Jazz' */
+map0table_jazz: PTR map0_pica61 # Acer Pica-61
+ PTR map0_magnum4000 # MIPS Magnum 4000PC (RC4030)
+ PTR map0_magnum4000 # Olivetti M700 (*same* table)
+ .word 0 # pad
-map0table: PTR map0_dummy # machtype = unknown
- PTR map0_rpc # Deskstation rPC44
+/* table for group 'Dec' */
+map0table_dec: PTR map0_dummy # DEC Personal DECStation 5000/2x (for now)
+ .word 0 # pad
+
+/* table for group 'ARC' */
+map0table_arc: PTR map0_rpc # Deskstation rPC44
PTR map0_tyne # Deskstation Tyne
- PTR map0_pica61 # Acer Pica-61
- PTR map0_magnum4000 # MIPS Magnum 4000PC (RC4030)
+/* table for group 'SNI_RM' */
+map0table_sni_rm: PTR map0_sni_rm200_pci # SNI RM200 PCI
+ .word 0
+
+/* table for group 'ACN' */
+map0table_acn: PTR map0_dummy # ACN mips board
+ .word 0
+
+/* dummy table */
map0_dummy: .word 0 # 0 entries
.align 3
/*
- * Initial mappings for Deskstation rPC boards.
- * RB: Untested goodie - I don't have such a board.
+ * Deskstation rpc44 mappings. This machine has its EISA bus at physical
+ * address 0xa0000000 which we map for 32M, but that doesn't match EISA
+ * spec. Not sure what to do about this. Its I/O ports are memory mapped
+ * at physical memory location 0xb0000000.
*/
map0_rpc: .word 2 # no. of wired TLB entries
.word 0 # pad for alignment
-MAPDATA(0xffffffffe0000000, 0x04020017, 0x00000001, PM_1M) # VESA DMA cache
-MAPDATA(0xffffffffe2000000, 0x24000017, 0x04000017, PM_16M) # VESA I/O and memory space
+MAPDATA(0xffffffffe0000000, 0x02800017, 0x00000011, PM_16M) # ISA Memory space
+MAPDATA(0xffffffffe2000000, 0x02c00017, 0x00000011, PM_64K) # ISA I/O Space
/*
* Initial mappings for Deskstation Tyne boards.
@@ -325,23 +522,18 @@ MAPDATA(0xffffffffe2000000, 0x24000017, 0x04000017, PM_16M) # VESA I/O and memor
map0_tyne: .word 2 # no. of wired TLB entries
.word 0 # pad for alignment
-MAPDATA(0xffffffffe0000000, 0x04020017, 0x00000001, PM_1M) # VESA DMA cache
+MAPDATA(0xffffffffe0000000, 0x04020017, 0x00000011, PM_1M) # VESA DMA cache
MAPDATA(0xffffffffe2000000, 0x24000017, 0x04000017, PM_16M) # VESA I/O and memory space
/*
* Initial mapping for ACER PICA-61 boards.
- * FIXME: These are rather preliminary since many drivers, such as serial,
- * parallel, scsi and ethernet need some changes to distinguish between "local"
- * (built-in) and "optional" (ISA/PCI) I/O hardware. Local video ram is mapped
- * to the same location as the bios maps it to. Console driver has been changed
- * accordingly (new video type: VIDEO_TYPE_PICA_S3).
* FIXME: Remove or merge some of the mappings.
*/
map0_pica61: .word 7 # no. wired TLB entries
.word 0 # dummy
-MAPDATA(0xffffffffe0000000, 0x02000017, 0x00000001, PM_64K) # Local I/O space
-MAPDATA(0xffffffffe0100000, 0x03c00017, 0x00000001, PM_4K) # Interrupt source register
+MAPDATA(0xffffffffe0000000, 0x02000017, 0x00000011, PM_64K) # Local I/O space
+MAPDATA(0xffffffffe0100000, 0x03c00017, 0x00000011, PM_4K) # Interrupt source register
MAPDATA(0xffffffffe0200000, 0x01800017, 0x01804017, PM_1M) # Local video control
MAPDATA(0xffffffffe0400000, 0x01808017, 0x0180c017, PM_1M) # Extended video control
MAPDATA(0xffffffffe0800000, 0x01000017, 0x01010017, PM_4M) # Local video memory (BIOS mapping)
@@ -350,23 +542,27 @@ MAPDATA(0xffffffffffffe000, 0x00000001, 0x0001ffd7, PM_4K) # PCR (???)
/*
* Initial mapping for Mips Magnum 4000PC systems.
- * Do you believe me now that the Acer and Mips boxes are nearly the same ? :-)
* FIXME: Remove or merge some of the mappings.
*/
-
map0_magnum4000:
.word 8 # no. wired TLB entries
.word 0 # dummy
-MAPDATA(0xffffffffe1000000, 0x03ffc013, 0x00000001, 0x7e000) # 0
-MAPDATA(0xffffffffe0000000, 0x02000017, 0x00000001, 0x1e000) # 1 local I/O
-MAPDATA(0xffffffffe0100000, 0x03c00017, 0x00000001, 0) # 2 IRQ source
-MAPDATA(0xffffffffe0200000, 0x01800017, 0x01804017, 0x1fe000) # 3 local video ctrl
-MAPDATA(0xffffffffe0400000, 0x01808017, 0x0180c017, 0x1fe000) # 4 ext. video ctrl
-MAPDATA(0xffffffffe0800000, 0x01000017, 0x01010017, 0x7fe000) # 5 local video mem.
-MAPDATA(0xffffffffe2000000, 0x02400017, 0x02440017, 0x1ffe000) # 6 ISA I/O and mem.
-MAPDATA(0xffffffffffffe000, 0x00000001, 0x0001ffd7, 0) # 7 PCR
+MAPDATA(0xffffffffe1000000, 0x03ffc013, 0x00000011, PM_256K) # 0
+MAPDATA(0xffffffffe0000000, 0x02000017, 0x00000011, PM_64K) # 1 local I/O
+MAPDATA(0xffffffffe0100000, 0x03c00017, 0x00000011, PM_4K) # 2 IRQ source
+MAPDATA(0xffffffffe0200000, 0x01800017, 0x01804017, PM_1M) # 3 local video ctrl
+MAPDATA(0xffffffffe0400000, 0x01808017, 0x0180c017, PM_1M) # 4 ext. video ctrl
+MAPDATA(0xffffffffe0800000, 0x01000017, 0x01010017, PM_4M) # 5 local video mem.
+MAPDATA(0xffffffffe2000000, 0x02400017, 0x02440017, PM_16M) # 6 ISA I/O and mem.
+MAPDATA(0xffffffffffffe000, 0x00000001, 0x0001ffd7, PM_4K) # 7 PCR
+/*
+ * The RM200 doesn't need any wired entries.
+ */
+map0_sni_rm200_pci:
+ .word 0 # no. wired TLB entries
+ .word 0 # dummy
.text
@@ -374,35 +570,19 @@ MAPDATA(0xffffffffffffe000, 0x00000001, 0x0001ffd7, 0) # 7 PCR
.globl swapper_pg_dir
swapper_pg_dir = . + (KSEG1-KSEG0)
-/*
- * The page tables are initialized to only 4MB here - the final page
- * tables are set up later depending on memory size.
- */
.org 0x2000
- EXPORT(pg0)
-
- .org 0x3000
EXPORT(empty_bad_page)
- .org 0x4000
+ .org 0x3000
EXPORT(empty_bad_page_table)
- .org 0x5000
+ .org 0x4000
EXPORT(empty_zero_page)
- .org 0x6000
+ .org 0x5000
EXPORT(invalid_pte_table)
- .org 0x7000
-
-/*
- * floppy_track_buffer is used to buffer one track of floppy data: it
- * has to be separate from the tmp_floppy area, as otherwise a single-
- * sector read/write can mess it up. It can contain one full cylinder (sic) of
- * data (36*2*512 bytes).
- */
- EXPORT(floppy_track_buffer)
- .fill 512*2*36,1,0
+ .org 0x6000
EXPORT(cache_error_buffer)
.fill 32*4,1,0
diff --git a/arch/mips/kernel/ipc.c b/arch/mips/kernel/ipc.c
new file mode 100644
index 000000000..336965acf
--- /dev/null
+++ b/arch/mips/kernel/ipc.c
@@ -0,0 +1,109 @@
+/*
+ * linux/arch/mips/kernel/ipc.c
+ *
+ * This file contains various random system calls that
+ * have a non-standard calling sequence on the Linux/MIPS
+ * platform.
+ */
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/sem.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+
+/*
+ * sys_ipc() is the de-multiplexer for the SysV IPC calls..
+ *
+ * This is really horribly ugly. FIXME: Get rid of this wrapper.
+ */
+asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr, long fifth)
+{
+#ifdef CONFIG_SYSVIPC
+ int version;
+
+ version = call >> 16; /* hack for backward compatibility */
+ call &= 0xffff;
+
+ if (call <= SEMCTL)
+ 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;
+ int err;
+ if (!ptr)
+ return -EINVAL;
+ if ((err = verify_area (VERIFY_READ, ptr, sizeof(long))))
+ return err;
+ get_from_user(fourth.__pad, ptr);
+ return sys_semctl (first, second, third, fourth);
+ }
+ default:
+ return -EINVAL;
+ }
+ if (call <= MSGCTL)
+ switch (call) {
+ case MSGSND:
+ return sys_msgsnd (first, (struct msgbuf *) ptr,
+ second, third);
+ case MSGRCV:
+ switch (version) {
+ case 0: {
+ struct ipc_kludge tmp;
+ int err;
+ if (!ptr)
+ return -EINVAL;
+ if ((err = verify_area (VERIFY_READ, ptr, sizeof(tmp))))
+ return err;
+ memcpy_fromfs (&tmp,(struct ipc_kludge *) ptr,
+ sizeof (tmp));
+ return sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, third);
+ }
+ case 1: 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);
+ default:
+ return -EINVAL;
+ }
+ if (call <= SHMCTL)
+ switch (call) {
+ case SHMAT:
+ switch (version) {
+ case 0: default: {
+ ulong raddr;
+ int err;
+ if ((err = verify_area(VERIFY_WRITE, (ulong*) third, sizeof(ulong))))
+ return err;
+ err = sys_shmat (first, (char *) ptr, second, &raddr);
+ if (err)
+ return err;
+ put_user (raddr, (ulong *) third);
+ return 0;
+ }
+ case 1: /* iBCS2 emulator entry point */
+ if (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;
+ }
+ return -EINVAL;
+#else /* CONFIG_SYSVIPC */
+ return -ENOSYS;
+#endif /* CONFIG_SYSVIPC */
+}
diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c
index 608a0b431..a6f257c88 100644
--- a/arch/mips/kernel/irq.c
+++ b/arch/mips/kernel/irq.c
@@ -8,125 +8,120 @@
* 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.
- */
-
-/*
- * 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.
- */
-
-/*
- * Mips support by Ralf Baechle and Andreas Busse
*
- * The Deskstation Tyne is almost completely like an IBM compatible PC with
- * another type of microprocessor. Therefore this code is almost completely
- * the same. More work needs to be done to support Acer PICA and other
- * machines.
+ * Mips support by Ralf Baechle and Andreas Busse
*/
-
-#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/kernel_stat.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/interrupt.h>
+#include <linux/ioport.h>
#include <linux/timex.h>
+#include <linux/malloc.h>
+#include <linux/random.h>
#include <asm/bitops.h>
#include <asm/bootinfo.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/mipsregs.h>
#include <asm/jazz.h>
+#include <asm/mipsregs.h>
#include <asm/system.h>
+#include <asm/vector.h>
unsigned char cache_21 = 0xff;
unsigned char cache_A1 = 0xff;
unsigned long spurious_count = 0;
-void disable_irq(unsigned int irq_nr)
+/*
+ * (un)mask_irq, disable_irq() and enable_irq() only handle (E)ISA and
+ * PCI devices. Other onboard hardware needs specific routines.
+ */
+static inline void mask_irq(unsigned int irq_nr)
{
- unsigned long flags;
unsigned char mask;
mask = 1 << (irq_nr & 7);
- save_flags(flags);
if (irq_nr < 8) {
- cli();
cache_21 |= mask;
outb(cache_21,0x21);
- restore_flags(flags);
- return;
+ } else {
+ cache_A1 |= mask;
+ outb(cache_A1,0xA1);
}
- cli();
- cache_A1 |= mask;
- outb(cache_A1,0xA1);
- restore_flags(flags);
}
-void enable_irq(unsigned int irq_nr)
+static inline void unmask_irq(unsigned int irq_nr)
{
- unsigned long flags;
unsigned char mask;
mask = ~(1 << (irq_nr & 7));
- save_flags(flags);
if (irq_nr < 8) {
- cli();
cache_21 &= mask;
outb(cache_21,0x21);
- restore_flags(flags);
- return;
+ } else {
+ cache_A1 &= mask;
+ outb(cache_A1,0xA1);
}
+}
+
+void disable_irq(unsigned int irq_nr)
+{
+ unsigned long flags;
+
+ save_flags(flags);
cli();
- cache_A1 &= mask;
- outb(cache_A1,0xA1);
+ 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);
}
/*
- * Pointers to the low-level handlers: first the general ones, then the
- * fast ones, then the bad ones.
+ * Low-level interrupt handlers: first the timer interrupt, then the
+ * general, then the fast and finally the bad interrupt handler.
*/
+extern void timer_interrupt(void);
extern void interrupt(void);
extern void fast_interrupt(void);
extern void bad_interrupt(void);
-/*
- * Initial irq handlers.
- */
-struct irqaction {
- void (*handler)(int, struct pt_regs *);
- unsigned long flags;
- unsigned long mask;
- const char *name;
-};
-
-static struct irqaction irq_action[16] = {
- { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
- { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
- { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
- { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
- { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
- { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
- { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
- { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }
+static struct irqaction *irq_action[16] = {
+ 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 = irq_action;
+ struct irqaction * action;
- for (i = 0 ; i < 16 ; i++, action++) {
- if (!action->handler)
+ for (i = 0 ; i < 16 ; i++) {
+ action = irq_action[i];
+ if (!action)
continue;
- len += sprintf(buf+len, "%2d: %8d %c %s\n",
+ len += sprintf(buf+len, "%2d: %8u %c %s",
i, kstat.interrupts[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;
}
@@ -140,14 +135,17 @@ int get_irq_list(char *buf)
*/
asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
{
- struct irqaction * action = irq + irq_action;
-#if 0
-if (irq > 0) {
- printk("in do_IRQ with irq=%d\n",irq);
-}
-#endif
+ struct irqaction * action = *(irq + irq_action);
+ int do_random = 0;
+
kstat.interrupts[irq]++;
- action->handler(irq, regs);
+ while (action) {
+ do_random |= action->flags;
+ action->handler(irq, action->dev_id, regs);
+ action = action->next;
+ }
+ if (do_random & SA_SAMPLE_RANDOM)
+ add_interrupt_randomness(irq);
}
/*
@@ -157,129 +155,159 @@ if (irq > 0) {
*/
asmlinkage void do_fast_IRQ(int irq)
{
- struct irqaction * action = irq + irq_action;
+ struct irqaction * action = *(irq + irq_action);
+ int do_random = 0;
kstat.interrupts[irq]++;
- action->handler(irq, NULL);
+ while (action) {
+ do_random |= action->flags;
+ action->handler(irq, action->dev_id, NULL);
+ action = action->next;
+ }
+ if (do_random & SA_SAMPLE_RANDOM)
+ add_interrupt_randomness(irq);
}
-#define SA_PROBE SA_ONESHOT
+/*
+ * Used only for setup of PC style interrupts and therefore still
+ * called setup_x86_irq. Later on I'll provide a machine specific
+ * function with similar purpose. Idea is to put all interrupts
+ * in a single table and differenciate them just by number.
+ */
+int setup_x86_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);
-int request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *),
- unsigned long irqflags, const char * devname)
+ save_flags(flags);
+ cli();
+ *p = new;
+
+ if (!shared) {
+ if (new->flags & SA_INTERRUPT)
+ set_int_vector(irq,fast_interrupt);
+ else
+ if (irq == 0)
+ set_int_vector(irq,timer_interrupt);
+ else
+ set_int_vector(irq,interrupt);
+ unmask_irq(irq);
+ }
+ restore_flags(flags);
+ return 0;
+}
+
+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;
- unsigned long flags;
if (irq > 15)
return -EINVAL;
- action = irq + irq_action;
- if (action->handler)
- return -EBUSY;
if (!handler)
return -EINVAL;
- save_flags(flags);
- cli();
+
+ 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;
- if (!(action->flags & SA_PROBE)) { /* SA_ONESHOT is used by probing */
- /*
- * FIXME: Does the SA_INTERRUPT flag make any sense on MIPS???
- */
- if (action->flags & SA_INTERRUPT)
- set_int_vector(irq,fast_interrupt);
- else
- set_int_vector(irq,interrupt);
- }
- if (irq < 8) {
- cache_21 &= ~(1<<irq);
- outb(cache_21,0x21);
- } else {
- cache_21 &= ~(1<<2);
- cache_A1 &= ~(1<<(irq-8));
- outb(cache_21,0x21);
- outb(cache_A1,0xA1);
- }
- restore_flags(flags);
- return 0;
-}
+ action->next = NULL;
+ action->dev_id = dev_id;
+
+ retval = setup_x86_irq(irq, action);
-void free_irq(unsigned int irq)
+ if (retval)
+ kfree(action);
+ return retval;
+}
+
+void free_irq(unsigned int irq, void *dev_id)
{
- struct irqaction * action = irq + irq_action;
+ struct irqaction * action, **p;
unsigned long flags;
if (irq > 15) {
printk("Trying to free IRQ%d\n",irq);
return;
}
- if (!action->handler) {
- printk("Trying to free free IRQ%d\n",irq);
+ 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);
+ }
+ restore_flags(flags);
+ kfree(action);
return;
}
- save_flags(flags);
- cli();
- if (irq < 8) {
- cache_21 |= 1 << irq;
- outb(cache_21,0x21);
- } else {
- cache_A1 |= 1 << (irq-8);
- outb(cache_A1,0xA1);
- }
- set_int_vector(irq,bad_interrupt);
- action->handler = NULL;
- action->flags = 0;
- action->mask = 0;
- action->name = NULL;
- restore_flags(flags);
+ printk("Trying to free free IRQ%d\n",irq);
}
-static void no_action(int cpl, struct pt_regs * regs) { }
-
-unsigned int probe_irq_on (void)
+unsigned long probe_irq_on (void)
{
unsigned int i, irqs = 0, irqmask;
unsigned long delay;
- /* first, snaffle up any unassigned irqs */
+ /* first, enable any unassigned irqs */
for (i = 15; i > 0; i--) {
- if (!request_irq(i, no_action, SA_PROBE, "probe")) {
+ if (!irq_action[i]) {
enable_irq(i);
irqs |= (1 << i);
}
}
/* wait for spurious interrupts to mask themselves out again */
- for (delay = jiffies + 2; delay > jiffies; ); /* min 10ms delay */
+ for (delay = jiffies + HZ/10; delay > jiffies; )
+ /* about 100ms delay */;
/* now filter out any obviously spurious interrupts */
irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21;
- for (i = 15; i > 0; i--) {
- if (irqs & (1 << i) & irqmask) {
- irqs ^= (1 << i);
- free_irq(i);
- }
- }
-#ifdef DEBUG
- printk("probe_irq_on: irqs=0x%04x irqmask=0x%04x\n", irqs, irqmask);
-#endif
- return irqs;
+ return irqs & ~irqmask;
}
-int probe_irq_off (unsigned int irqs)
+int probe_irq_off (unsigned long irqs)
{
unsigned int i, irqmask;
irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21;
- for (i = 15; i > 0; i--) {
- if (irqs & (1 << i)) {
- free_irq(i);
- }
- }
#ifdef DEBUG
- printk("probe_irq_off: irqs=0x%04x irqmask=0x%04x\n", irqs, irqmask);
+ printk("probe_irq_off: irqs=0x%04lx irqmask=0x%04x\n", irqs, irqmask);
#endif
irqs &= irqmask;
if (!irqs)
@@ -294,41 +322,7 @@ void init_IRQ(void)
{
int i;
- switch (boot_info.machtype) {
- case MACH_MIPS_MAGNUM_4000:
- case MACH_ACER_PICA_61:
- r4030_write_reg16(JAZZ_IO_IRQ_ENABLE,
- JAZZ_IE_ETHERNET |
- JAZZ_IE_SERIAL1 |
- JAZZ_IE_SERIAL2 |
- JAZZ_IE_PARALLEL |
- JAZZ_IE_FLOPPY);
- r4030_read_reg16(JAZZ_IO_IRQ_SOURCE); /* clear pending IRQs */
- set_cp0_status(ST0_IM, IE_IRQ4 | IE_IRQ1);
- /* set the clock to 100 Hz */
- r4030_write_reg32(JAZZ_TIMER_INTERVAL, 9);
- break;
- case MACH_DESKSTATION_TYNE:
- /* set the clock to 100 Hz */
- outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */
- outb_p(LATCH & 0xff , 0x40); /* LSB */
- outb(LATCH >> 8 , 0x40); /* MSB */
-
- if (request_irq(2, no_action, SA_INTERRUPT, "cascade"))
- printk("Unable to get IRQ2 for cascade\n");
- break;
- default:
- panic("Unknown machtype in init_IRQ");
- }
-
for (i = 0; i < 16 ; i++)
set_int_vector(i, bad_interrupt);
-
- /* initialize the bottom half routines. */
- for (i = 0; i < 32; i++) {
- bh_base[i].routine = NULL;
- bh_base[i].data = NULL;
- }
- bh_active = 0;
- intr_count = 0;
+ irq_setup();
}
diff --git a/arch/mips/kernel/jazzdma.c b/arch/mips/kernel/jazzdma.c
deleted file mode 100644
index 1d535e716..000000000
--- a/arch/mips/kernel/jazzdma.c
+++ /dev/null
@@ -1,518 +0,0 @@
-/*
- * jazzdma.c
- *
- * Mips Jazz DMA controller support
- * (C) 1995 Andreas Busse
- *
- * NOTE: Some of the argument checkings could be removed when
- * things have settled down. Also, instead of returning 0xffffffff
- * on failure of vdma_alloc() one could leave page #0 unused
- * and return the more usual NULL pointer as logical address.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <asm/mipsregs.h>
-#include <asm/mipsconfig.h>
-#include <asm/jazz.h>
-#include <asm/io.h>
-#include <asm/segment.h>
-#include <asm/dma.h>
-#include <asm/jazzdma.h>
-
-
-static unsigned long vdma_pagetable_start = 0;
-static unsigned long vdma_pagetable_end = 0;
-
-/*
- * Debug stuff
- */
-
-#define DEBUG_VDMA 0
-#define vdma_debug ((DEBUG_VDMA) ? debuglvl : 0)
-
-static int debuglvl = 3;
-
-/*
- * Local prototypes
- */
-
-static void vdma_pgtbl_init(void);
-
-
-/*
- * Initialize the Jazz R4030 dma controller
- */
-
-unsigned long vdma_init(unsigned long memory_start, unsigned long memory_end)
-{
-
- /*
- * Allocate 32k of memory for DMA page tables.
- * This needs to be page aligned and should be
- * uncached to avoid cache flushing after every
- * update.
- */
-
- vdma_pagetable_start = KSEG1ADDR((memory_start + 4095) & ~ 4095);
- vdma_pagetable_end = vdma_pagetable_start + VDMA_PGTBL_SIZE;
-
-
- /*
- * Clear the R4030 translation table
- */
-
- vdma_pgtbl_init();
-
- r4030_write_reg32(JAZZ_R4030_TRSTBL_BASE,PHYSADDR(vdma_pagetable_start));
- r4030_write_reg32(JAZZ_R4030_TRSTBL_LIM,VDMA_PGTBL_SIZE);
- r4030_write_reg32(JAZZ_R4030_TRSTBL_INV,0);
-
- printk("VDMA: R4030 DMA pagetables initialized.\n");
- return(KSEG0ADDR(vdma_pagetable_end));
-}
-
-/*
- * Allocate DMA pagetables using a simple first-fit algorithm
- */
-
-unsigned long vdma_alloc(unsigned long paddr, unsigned long size)
-{
- VDMA_PGTBL_ENTRY *entry = (VDMA_PGTBL_ENTRY *)vdma_pagetable_start;
- int first;
- int last;
- int pages;
- unsigned int frame;
- unsigned long laddr;
- int i;
-
- /* check arguments */
-
- if (paddr > 0x1fffffff) {
- if (vdma_debug)
- printk("vdma_alloc: Invalid physical address: %08lx\n",paddr);
- return (VDMA_ERROR); /* invalid physical address */
- }
- if (size > 0x400000 || size == 0) {
- if (vdma_debug)
- printk("vdma_alloc: Invalid size: %08lx\n",size);
- return (VDMA_ERROR); /* invalid physical address */
- }
-
- /* find free chunk */
-
- pages = (size + 4095) >> 12; /* no. of pages to allocate */
- first = 0;
- while (1) {
- while (entry[first].owner != VDMA_PAGE_EMPTY && first < VDMA_PGTBL_ENTRIES)
- first++;
- if (first+pages > VDMA_PGTBL_ENTRIES) /* nothing free */
- return (VDMA_ERROR);
-
- last = first+1;
- while (entry[last].owner == VDMA_PAGE_EMPTY && last-first < pages)
- last++;
-
- if (last-first == pages)
- break; /* found */
- }
-
- /* mark pages as allocated */
-
- laddr = (first << 12) + (paddr & (VDMA_PAGESIZE-1));
- frame = paddr & ~(VDMA_PAGESIZE-1);
-
- for (i=first; i<last; i++) {
- entry[i].frame = frame;
- entry[i].owner = laddr;
- frame += VDMA_PAGESIZE;
- }
-
- /*
- * update translation table and
- * return logical start address
- */
-
- r4030_write_reg32(JAZZ_R4030_TRSTBL_INV,0);
-
- if (vdma_debug > 1)
- printk("vdma_alloc: Allocated %d pages starting from %08lx\n",
- pages,laddr);
-
- if (vdma_debug > 2) {
- printk("LADDR: ");
- for (i=first; i<last; i++)
- printk("%08x ",i<<12);
- printk("\nPADDR: ");
- for (i=first; i<last; i++)
- printk("%08x ",entry[i].frame);
- printk("\nOWNER: ");
- for (i=first; i<last; i++)
- printk("%08x ",entry[i].owner);
- printk("\n");
- }
-
- return(laddr);
-}
-
-
-/*
- * Free previously allocated dma translation pages
- * Note that this does NOT change the translation table,
- * it just marks the free'd pages as unused!
- */
-
-int vdma_free(unsigned long laddr)
-{
- VDMA_PGTBL_ENTRY *pgtbl = (VDMA_PGTBL_ENTRY *)vdma_pagetable_start;
- int i;
-
- i = laddr >> 12;
-
- if (pgtbl[i].owner != laddr) {
- printk("vdma_free: trying to free other's dma pages, laddr=%8lx\n",laddr);
- return -1;
- }
-
- while (pgtbl[i].owner == laddr && i < VDMA_PGTBL_ENTRIES) {
- pgtbl[i].owner = VDMA_PAGE_EMPTY;
- i++;
- }
-
- if (vdma_debug > 1)
- printk("vdma_free: freed %ld pages starting from %08lx\n",
- i-(laddr>>12),laddr);
-
- return 0;
-}
-
-/*
- * Map certain page(s) to another physical address.
- * Caller must have allocated the page(s) before.
- */
-
-int vdma_remap(unsigned long laddr, unsigned long paddr, unsigned long size)
-{
- VDMA_PGTBL_ENTRY *pgtbl = (VDMA_PGTBL_ENTRY *)vdma_pagetable_start;
- int first;
- int pages;
-
- if (laddr > 0xffffff) {
- if (vdma_debug)
- printk("vdma_map: Invalid logical address: %08lx\n",laddr);
- return -EINVAL; /* invalid logical address */
- }
- if (paddr > 0x1fffffff) {
- if (vdma_debug)
- printk("vdma_map: Invalid physical address: %08lx\n",paddr);
- return -EINVAL; /* invalid physical address */
- }
-
- pages = (((paddr & (VDMA_PAGESIZE-1)) + size) >> 12) + 1;
- first = laddr >> 12;
- if (vdma_debug)
- printk("vdma_remap: first=%x, pages=%x\n",first,pages);
- if (first+pages > VDMA_PGTBL_ENTRIES) {
- if (vdma_debug)
- printk("vdma_alloc: Invalid size: %08lx\n",size);
- return -EINVAL;
- }
-
- paddr &= ~(VDMA_PAGESIZE-1);
- while (pages > 0 && first < VDMA_PGTBL_ENTRIES) {
- if (pgtbl[first].owner != laddr) {
- if (vdma_debug)
- printk("Trying to remap other's pages.\n");
- return -EPERM; /* not owner */
- }
- pgtbl[first].frame = paddr;
- paddr += VDMA_PAGESIZE;
- first++;
- pages--;
- }
-
- /* update translation table */
-
- r4030_write_reg32(JAZZ_R4030_TRSTBL_INV,0);
-
-
- if (vdma_debug > 2) {
- int i;
- pages = (((paddr & (VDMA_PAGESIZE-1)) + size) >> 12) + 1;
- first = laddr >> 12;
- printk("LADDR: ");
- for (i=first; i<first+pages; i++)
- printk("%08x ",i<<12);
- printk("\nPADDR: ");
- for (i=first; i<first+pages; i++)
- printk("%08x ",pgtbl[i].frame);
- printk("\nOWNER: ");
- for (i=first; i<first+pages; i++)
- printk("%08x ",pgtbl[i].owner);
- printk("\n");
- }
-
- return 0;
-}
-
-/*
- * Translate a physical address to a logical address.
- * This will return the logical address of the first
- * match.
- */
-
-unsigned long vdma_phys2log(unsigned long paddr)
-{
- int i;
- int frame;
- VDMA_PGTBL_ENTRY *pgtbl = (VDMA_PGTBL_ENTRY *)vdma_pagetable_start;
-
- frame = paddr & ~(VDMA_PAGESIZE-1);
-
- for (i=0; i<VDMA_PGTBL_ENTRIES; i++) {
- if (pgtbl[i].frame == frame)
- break;
- }
-
- if (i == VDMA_PGTBL_ENTRIES)
- return(0xffffffff);
-
- return((i<<12) + (paddr & (VDMA_PAGESIZE-1)));
-}
-
-/*
- * Translate a logical DMA address to a physical address
- */
-unsigned long vdma_log2phys(unsigned long laddr)
-{
- VDMA_PGTBL_ENTRY *pgtbl = (VDMA_PGTBL_ENTRY *)vdma_pagetable_start;
-
- return(pgtbl[laddr >> 12].frame + (laddr & (VDMA_PAGESIZE-1)));
-}
-
-
-/*
- * initialize the pagetable with a one-to-one mapping of
- * the first 16 Mbytes of main memory and declare all
- * entries to be unused. Using this method will at least
- * allow some early device driver operations to work.
- */
-
-static void vdma_pgtbl_init(void)
-{
- int i;
- unsigned long paddr = 0;
- VDMA_PGTBL_ENTRY *pgtbl = (VDMA_PGTBL_ENTRY *)vdma_pagetable_start;
-
- for (i=0; i<VDMA_PGTBL_ENTRIES; i++) {
- pgtbl[i].frame = paddr;
- pgtbl[i].owner = VDMA_PAGE_EMPTY;
- paddr += VDMA_PAGESIZE;
- }
-
-/* vdma_stats(); */
-}
-
-/*
- * Print DMA statistics
- */
-
-void vdma_stats(void)
-{
- int i;
-
- printk("vdma_stats: CONFIG: %08x\n",
- r4030_read_reg32(JAZZ_R4030_CONFIG));
- printk("R4030 translation table base: %08x\n",
- r4030_read_reg32(JAZZ_R4030_TRSTBL_BASE));
- printk("R4030 translation table limit: %08x\n",
- r4030_read_reg32(JAZZ_R4030_TRSTBL_LIM));
- printk("vdma_stats: INV_ADDR: %08x\n",
- r4030_read_reg32(JAZZ_R4030_INV_ADDR));
- printk("vdma_stats: R_FAIL_ADDR: %08x\n",
- r4030_read_reg32(JAZZ_R4030_R_FAIL_ADDR));
- printk("vdma_stats: M_FAIL_ADDR: %08x\n",
- r4030_read_reg32(JAZZ_R4030_M_FAIL_ADDR));
- printk("vdma_stats: IRQ_SOURCE: %08x\n",
- r4030_read_reg32(JAZZ_R4030_IRQ_SOURCE));
- printk("vdma_stats: I386_ERROR: %08x\n",
- r4030_read_reg32(JAZZ_R4030_I386_ERROR));
- printk("vdma_chnl_modes: ");
- for (i=0; i<8; i++)
- printk("%04x ",(unsigned)r4030_read_reg32(JAZZ_R4030_CHNL_MODE+(i<<5)));
- printk("\n");
- printk("vdma_chnl_enables: ");
- for (i=0; i<8; i++)
- printk("%04x ",(unsigned)r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(i<<5)));
- printk("\n");
-}
-
-
-/*
- * DMA transfer functions
- */
-
-/*
- * Enable a DMA channel. Also clear any error conditions.
- */
-void vdma_enable(int channel)
-{
- int status;
-
- if (vdma_debug)
- printk("vdma_enable: channel %d\n",channel);
-
- /*
- * Check error conditions first
- */
- status = r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5));
- if (status & 0x400)
- printk("VDMA: Channel %d: Address error!\n",channel);
- if (status & 0x200)
- printk("VDMA: Channel %d: Memory error!\n",channel);
-
- /*
- * Clear all interrupt flags
- */
- r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5),
- R4030_TC_INTR | R4030_MEM_INTR | R4030_ADDR_INTR);
-
- /*
- * Enable the desired channel
- */
- r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5),
- r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5)) |
- R4030_CHNL_ENABLE);
-}
-
-/*
- * Disable a DMA channel
- */
-void vdma_disable(int channel)
-{
- if (vdma_debug)
- {
- int status = r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5));
-
- printk("vdma_disable: channel %d\n",channel);
- printk("VDMA: channel %d status: %04x (%s) mode: %02x addr: %06x count: %06x\n",
- channel,status,((status & 0x600) ? "ERROR" : "OK"),
- (unsigned)r4030_read_reg32(JAZZ_R4030_CHNL_MODE+(channel<<5)),
- (unsigned)r4030_read_reg32(JAZZ_R4030_CHNL_ADDR+(channel<<5)),
- (unsigned)r4030_read_reg32(JAZZ_R4030_CHNL_COUNT+(channel<<5)));
- }
-
- r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5),
- r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5)) &
- ~R4030_CHNL_ENABLE);
-
- /*
- * After disabling a DMA channel a remote bus register should be
- * read to ensure that the current DMA acknowledge cycle is completed.
- */
-
- *((volatile unsigned int *)JAZZ_DUMMY_DEVICE);
-}
-
-/*
- * Set DMA mode. This function accepts the mode values used
- * to set a PC-style DMA controller. For the SCSI and FDC
- * channels, we also set the default modes each time we're
- * called.
- * NOTE: The FAST and BURST dma modes are supported by the
- * R4030 Rev. 2 and PICA chipsets only. I leave them disabled
- * for now.
- */
-void vdma_set_mode(int channel, int mode)
-{
- if (vdma_debug)
- printk("vdma_set_mode: channel %d, mode 0x%x\n",channel,mode);
-
- switch(channel)
- {
- case JAZZ_SCSI_DMA: /* scsi */
- r4030_write_reg32(JAZZ_R4030_CHNL_MODE+(channel<<5),
-/* R4030_MODE_FAST | */
-/* R4030_MODE_BURST | */
- R4030_MODE_INTR_EN |
- R4030_MODE_WIDTH_16 |
- R4030_MODE_ATIME_80);
- break;
-
- case JAZZ_FLOPPY_DMA: /* floppy */
- r4030_write_reg32(JAZZ_R4030_CHNL_MODE+(channel<<5),
-/* R4030_MODE_FAST | */
-/* R4030_MODE_BURST | */
- R4030_MODE_INTR_EN |
- R4030_MODE_WIDTH_8 |
- R4030_MODE_ATIME_120);
- break;
-
- case JAZZ_AUDIOL_DMA:
- case JAZZ_AUDIOR_DMA:
- printk("VDMA: Audio DMA not supported yet.\n");
- break;
-
- default:
- printk("VDMA: vdma_set_mode() called with unsupported channel %d!\n",channel);
- }
-
- switch(mode)
- {
- case DMA_MODE_READ:
- r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5),
- r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5)) &
- ~R4030_CHNL_WRITE);
- break;
-
- case DMA_MODE_WRITE:
- r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5),
- r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5)) |
- R4030_CHNL_WRITE);
- break;
-
- default:
- printk("VDMA: vdma_set_mode() called with unknown dma mode 0x%x\n",mode);
- }
-}
-
-/*
- * Set Transfer Address
- */
-void vdma_set_addr(int channel, long addr)
-{
- if (vdma_debug)
- printk("vdma_set_addr: channel %d, addr %lx\n",channel,addr);
-
- r4030_write_reg32(JAZZ_R4030_CHNL_ADDR+(channel<<5),addr);
-}
-
-/*
- * Set Transfer Count
- */
-void vdma_set_count(int channel, int count)
-{
- if (vdma_debug)
- printk("vdma_set_count: channel %d, count %08x\n",channel,(unsigned)count);
-
- r4030_write_reg32(JAZZ_R4030_CHNL_COUNT+(channel<<5),count);
-}
-
-/*
- * Get Residual
- */
-int vdma_get_residue(int channel)
-{
- int residual;
-
- residual = r4030_read_reg32(JAZZ_R4030_CHNL_COUNT+(channel<<5));
-
- if (vdma_debug)
- printk("vdma_get_residual: channel %d: residual=%d\n",channel,residual);
-
- return(residual);
-}
-
-
-/* end of file jazzdma.h */
diff --git a/arch/mips/kernel/ksyms.c b/arch/mips/kernel/ksyms.c
new file mode 100644
index 000000000..c80bf588a
--- /dev/null
+++ b/arch/mips/kernel/ksyms.c
@@ -0,0 +1,40 @@
+/*
+ * Export MIPS-specific functions needed for loadable modules.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1996 by Ralf Baechle
+ */
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <asm/cache.h>
+#include <asm/dma.h>
+#include <asm/floppy.h>
+#include <asm/io.h>
+
+static struct symbol_table arch_symbol_table = {
+#include <linux/symtab_begin.h>
+ X(EISA_bus),
+ /*
+ * String functions
+ */
+ X(__generic_memset_b),
+ X(__generic_memset_dw),
+ /*
+ * Functions to control caches.
+ */
+ X(cacheflush),
+ X(fd_cacheflush),
+ /*
+ * Base address of ports for Intel style I/O.
+ */
+ X(port_base),
+#include <linux/symtab_end.h>
+};
+
+void arch_syms_export(void)
+{
+ register_symtab(&arch_symbol_table);
+}
diff --git a/arch/mips/kernel/magnum4000.S b/arch/mips/kernel/magnum4000.S
deleted file mode 100644
index 45e62d88d..000000000
--- a/arch/mips/kernel/magnum4000.S
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * arch/mips/kernel/magnum4000.S
- *
- * Copyright (C) 1995 Waldorf Electronics
- * written by Ralf Baechle and Andreas Busse
- */
-#include <asm/asm.h>
-#include <asm/mipsregs.h>
-#include <asm/jazz.h>
-#include <asm/stackframe.h>
-
-/*
- * mips_magnum_4000_handle_int: Interrupt handler for Mips Magnum 4000
- */
- .set noreorder
-
- NESTED(mips_magnum_4000_handle_int, FR_SIZE, ra)
- .set noat
- SAVE_ALL
- CLI
- .set at
-
- /*
- * Get pending interrupts
- */
- mfc0 t0,CP0_CAUSE # get pending interrupts
- mfc0 t1,CP0_STATUS # get enabled interrupts
- and t0,t1 # isolate allowed ones
- andi t0,0xff00 # isolate pending bits
- beqz t0,spurious_interrupt
- sll t0,16 # delay slot
-
- /*
- * Find irq with highest priority
- * FIXME: This is slow
- */
- la t1,ll_vectors
-1: bltz t0,2f # found pending irq
- sll t0,1
- b 1b
- subu t1,PTRSIZE # delay slot
-
- /*
- * Do the low-level stuff
- */
-2: lw t0,(t1)
- jr t0
- nop # delay slot
- END(mips_magnum_4000_handle_int)
-
-/*
- * Used for keyboard driver's fake_keyboard_interrupt()
- */
-ll_sw0: li s1,~IE_SW0
- mfc0 t0,CP0_CAUSE
- and t0,s1
- mtc0 t0,CP0_CAUSE
- PRINT("sw0 received...\n")
- li t1,1
- b call_real
- li t3,PTRSIZE # delay slot, re-map to irq level 1
-
-ll_sw1: li s1,~IE_SW1
- PANIC("Unimplemented sw1 handler")
-
-ll_local_dma: li s1,~IE_IRQ0
- PANIC("Unimplemented local_dma handler")
-
-ll_local_dev: lbu t0,JAZZ_IO_IRQ_SOURCE
-#if __mips == 3
- dsll t0,1
- ld t0,local_vector(t0)
-#else /* 32 bit */
- lw t0,local_vector(t0)
-#endif
- jr t0
- nop
-
-
-loc_no_irq: PANIC("Unimplemented loc_no_irq handler")
-loc_sound: PANIC("Unimplemented loc_sound handler")
-loc_video: PANIC("Unimplemented loc_video handler")
-loc_scsi: PANIC("Unimplemented loc_scsi handler")
-
-/*
- * Keyboard interrupt handler
- */
-loc_keyboard: li s1,~JAZZ_IE_KEYBOARD
- li t1,JAZZ_KEYBOARD_IRQ
- b loc_call
- li t3,PTRSIZE*JAZZ_KEYBOARD_IRQ # delay slot
-
-/*
- * Ethernet interrupt handler, remapped to level 2
- */
-loc_ethernet: /* PRINT ("ethernet IRQ\n"); */
- li s1,~JAZZ_IE_ETHERNET
- li t1,JAZZ_ETHERNET_IRQ
- b loc_call
- li t3,PTRSIZE*JAZZ_ETHERNET_IRQ # delay slot
-
-
-loc_mouse: PANIC("Unimplemented loc_mouse handler")
-
-/*
- * Serial port 1 IRQ, remapped to level 3
- */
-loc_serial1: li s1,~JAZZ_IE_SERIAL1
- li t1,JAZZ_SERIAL1_IRQ
- b loc_call
- li t3,PTRSIZE*JAZZ_SERIAL1_IRQ # delay slot
-
-/*
- * Serial port 2 IRQ, remapped to level 4
- */
-loc_serial2: li s1,~JAZZ_IE_SERIAL2
- li t1,JAZZ_SERIAL2_IRQ
- b loc_call
- li t3,PTRSIZE*JAZZ_SERIAL2_IRQ # delay slot
-
-/*
- * Parallel port IRQ, remapped to level 5
- */
-loc_parallel: li s1,~JAZZ_IE_PARALLEL
- li t1,JAZZ_PARALLEL_IRQ
- b loc_call
- li t3,PTRSIZE*JAZZ_PARALLEL_IRQ # delay slot
-
-/*
- * Floppy IRQ, remapped to level 6
- */
-loc_floppy: li s1,~JAZZ_IE_FLOPPY
- li t1,JAZZ_FLOPPY_IRQ
- b loc_call
- li t3,PTRSIZE*JAZZ_FLOPPY_IRQ # delay slot
-
-/*
- * Now call the real handler
- */
-loc_call: lui s3,%hi(intr_count)
- lw t2,%lo(intr_count)(s3)
- la t0,IRQ_vectors # delay slot
- addiu t2,1
- sw t2,%lo(intr_count)(s3)
-
- /*
- * Temporarily disable interrupt source
- */
- lhu t2,JAZZ_IO_IRQ_ENABLE
- addu t0,t3 # make ptr to IRQ handler
- lw t0,(t0)
- and t2,s1 # delay slot
- sh t2,JAZZ_IO_IRQ_ENABLE
- jalr t0 # call IRQ handler
- nor s1,zero,s1 # delay slot
-
- /*
- * Reenable interrupt
- */
- lhu t2,JAZZ_IO_IRQ_ENABLE
- lw t1,%lo(intr_count)(s3) # delay slot
- or t2,s1
- sh t2,JAZZ_IO_IRQ_ENABLE
-
- subu t1,1
- jr v0
- sw t1,%lo(intr_count)(s3)
-
-ll_eisa_irq: li s1,~IE_IRQ2
- PANIC("Unimplemented eisa_irq handler")
-
-ll_eisa_nmi: li s1,~IE_IRQ3
- PANIC("Unimplemented eisa_nmi handler")
-
-/*
- * Timer IRQ
- * We remap the timer irq to be more similar to a IBM compatible
- */
-ll_timer: lw t0,JAZZ_TIMER_REGISTER # timer irq cleared on read
- li s1,~IE_IRQ4
- li t1,0
- b call_real
- li t3,0 # delay slot, re-map to irq level 0
-
-/*
- * CPU count/compare IRQ (unused)
- */
-ll_count: j return
- mtc0 zero,CP0_COMPARE
-
-/*
- * Now call the real handler
- */
-call_real: lui s3,%hi(intr_count)
- lw t2,%lo(intr_count)(s3)
- la t0,IRQ_vectors # delay slot
- addiu t2,1
- sw t2,%lo(intr_count)(s3)
-
- /*
- * temporarily disable interrupt
- */
- mfc0 t2,CP0_STATUS
- and t2,s1
-
- addu t0,t3
- lw t0,(t0)
- mtc0 t2,CP0_STATUS # delay slot
- jalr t0
- nor s1,zero,s1 # delay slot
-
- /*
- * reenable interrupt
- */
- mfc0 t2,CP0_STATUS
- or t2,s1
- mtc0 t2,CP0_STATUS
-
- lw t2,%lo(intr_count)(s3)
- subu t2,1
-
- jr v0
- sw t2,%lo(intr_count)(s3)
-
-/*
- * Just for debugging...
- */
- LEAF(drawline)
- li t1,0xffffffff
- li t2,0x100
-1: sw t1,(a0)
- addiu a0,a0,4
- addiu t2,t2,-1
- bnez t2,1b
- nop
- jr ra
- nop
- END(drawline)
-
-
- .data
- PTR ll_sw0 # SW0
- PTR ll_sw1 # SW1
- PTR ll_local_dma # Local DMA
- PTR ll_local_dev # Local devices
- PTR ll_eisa_irq # EISA IRQ
- PTR ll_eisa_nmi # EISA NMI
- PTR ll_timer # Timer
-ll_vectors: PTR ll_count # Count/Compare IRQ
-
-local_vector: PTR loc_no_irq
- PTR loc_parallel
- PTR loc_floppy
- PTR loc_sound
- PTR loc_video
- PTR loc_ethernet
- PTR loc_scsi
- PTR loc_keyboard
- PTR loc_mouse
- PTR loc_serial1
- PTR loc_serial2
diff --git a/arch/mips/kernel/pica.S b/arch/mips/kernel/pica.S
deleted file mode 100644
index 036aa3139..000000000
--- a/arch/mips/kernel/pica.S
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * arch/mips/kernel/pica.S
- *
- * Copyright (C) 1995 Waldorf Electronics
- * written by Ralf Baechle and Andreas Busse
- *
- * Acer PICA 61 specific stuff
- */
-#include <asm/asm.h>
-#include <asm/mipsregs.h>
-#include <asm/jazz.h>
-#include <asm/pica.h>
-#include <asm/stackframe.h>
-
-/*
- * acer_pica_61_handle_int: Interrupt handler for the ACER Pica-61 boards
- * FIXME: this is *very* experimental!
- */
- .set noreorder
-
- NESTED(acer_pica_61_handle_int, FR_SIZE, ra)
- .set noat
- SAVE_ALL
- CLI
- .set at
-
- /*
- * Get pending interrupts
- */
- mfc0 t0,CP0_CAUSE # get pending interrupts
- mfc0 t1,CP0_STATUS # get enabled interrupts
- and t0,t1 # isolate allowed ones
- andi t0,0xff00 # isolate pending bits
- beqz t0,spurious_interrupt
- sll t0,16 # delay slot
-
- /*
- * Find irq with highest priority
- * FIXME: This is slow - use binary search
- */
- la t1,ll_vectors
-1: bltz t0,2f # found pending irq
- sll t0,1
- b 1b
- subu t1,PTRSIZE # delay slot
-
- /*
- * Do the low-level stuff
- */
-2: lw t0,(t1)
- jr t0
- nop # delay slot
- END(acer_pica_61_handle_int)
-
-/*
- * Used for keyboard driver's fake_keyboard_interrupt()
- */
-ll_sw0: li s1,~IE_SW0
- mfc0 t0,CP0_CAUSE
- and t0,s1
- mtc0 t0,CP0_CAUSE
- PRINT("sw0 received...\n")
- li t1,1
- b call_real
- li t3,PTRSIZE # delay slot, re-map to irq level 1
-
-ll_sw1: li s1,~IE_SW1
- PANIC("Unimplemented sw1 handler")
-
-ll_local_dma: li s1,~IE_IRQ0
- PANIC("Unimplemented local_dma handler")
-
-ll_local_dev: lbu t0,JAZZ_IO_IRQ_SOURCE
-#if __mips == 3
- dsll t0,1
- ld t0,local_vector(t0)
-#else /* 32 bit */
- lw t0,local_vector(t0)
-#endif
- jr t0
- nop
-
-
-loc_no_irq: PANIC("Unimplemented loc_no_irq handler")
-/*
- * Parallel port IRQ, remapped to level 5
- */
-loc_parallel: li s1,~JAZZ_IE_PARALLEL
- li t1,JAZZ_PARALLEL_IRQ
- b loc_call
- li t3,PTRSIZE*JAZZ_PARALLEL_IRQ # delay slot
-
-/*
- * Floppy IRQ, remapped to level 6
- */
-loc_floppy: li s1,~JAZZ_IE_FLOPPY
- li t1,JAZZ_FLOPPY_IRQ
- b loc_call
- li t3,PTRSIZE*JAZZ_FLOPPY_IRQ # delay slot
-
-/*
- * Now call the real handler
- */
-loc_call: lui s3,%hi(intr_count)
- lw t2,%lo(intr_count)(s3)
- la t0,IRQ_vectors # delay slot
- addiu t2,1
- sw t2,%lo(intr_count)(s3)
-
- /*
- * Temporarily disable interrupt source
- */
- lhu t2,JAZZ_IO_IRQ_ENABLE
- addu t0,t3 # make ptr to IRQ handler
- lw t0,(t0)
- and t2,s1 # delay slot
- sh t2,JAZZ_IO_IRQ_ENABLE
- jalr t0 # call IRQ handler
- nor s1,zero,s1 # delay slot
-
- /*
- * Reenable interrupt
- */
- lhu t2,JAZZ_IO_IRQ_ENABLE
- lw t1,%lo(intr_count)(s3) # delay slot
- or t2,s1
- sh t2,JAZZ_IO_IRQ_ENABLE
-
- subu t1,1
- jr v0
- sw t1,%lo(intr_count)(s3) # delay slot
-
-ll_isa_irq: li s1,~IE_IRQ2
- PANIC("Unimplemented isa_irq handler")
-
-ll_isa_nmi: li s1,~IE_IRQ3
- PANIC("Unimplemented isa_nmi handler")
-
-/*
- * Timer IRQ
- * We remap the timer irq to be more similar to an IBM compatible
- */
-ll_timer: lw zero,JAZZ_TIMER_REGISTER # timer irq cleared on read
- li s1,~IE_IRQ4
- li t1,0
- b call_real
- li t3,0 # delay slot, re-map to irq level 0
-
-/*
- * CPU count/compare IRQ (unused)
- */
-ll_count: j return
- mtc0 zero,CP0_COMPARE
-
-/*
- * Now call the real handler
- */
-call_real: lui s3,%hi(intr_count)
- lw t2,%lo(intr_count)(s3)
- la t0,IRQ_vectors
- addiu t2,1
- sw t2,%lo(intr_count)(s3)
-
- /*
- * temporarily disable interrupt
- */
- mfc0 t2,CP0_STATUS
- and t2,s1
-
- addu t0,t3
- lw t0,(t0)
- mtc0 t2,CP0_STATUS # delay slot
- jalr t0
- nor s1,zero,s1 # delay slot
-
- /*
- * reenable interrupt
- */
- mfc0 t2,CP0_STATUS
- or t2,s1
- mtc0 t2,CP0_STATUS
-
- lw t2,%lo(intr_count)(s3)
- subu t2,1
-
- jr v0
- sw t2,%lo(intr_count)(s3)
-
- .data
- PTR ll_sw0 # SW0
- PTR ll_sw1 # SW1
- PTR ll_local_dma # Local DMA
- PTR ll_local_dev # Local devices
- PTR ll_isa_irq # ISA IRQ
- PTR ll_isa_nmi # ISA NMI
- PTR ll_timer # Timer
-ll_vectors: PTR ll_count # Count/Compare IRQ
-
-
-/*
- * Sound? What sound hardware (whistle) ???
- */
-loc_sound: PANIC("Unimplemented loc_sound handler")
-loc_video: PANIC("Unimplemented loc_video handler")
-
-/*
- * Ethernet interrupt handler, remapped to level 2
- */
-loc_ethernet: li s1,~JAZZ_IE_ETHERNET
- li t1,JAZZ_ETHERNET_IRQ
- b loc_call
- li t3,PTRSIZE*JAZZ_ETHERNET_IRQ # delay slot
-
-loc_scsi: PANIC("Unimplemented loc_scsi handler")
-
-/*
- * Keyboard interrupt handler
- */
-loc_keyboard: li s1,~JAZZ_IE_KEYBOARD
- li t1,JAZZ_KEYBOARD_IRQ
- b loc_call
- li t3,PTRSIZE*JAZZ_KEYBOARD_IRQ # re-map to irq level 1
-
-loc_mouse: PANIC("Unimplemented loc_mouse handler")
-
-/*
- * Serial port 1 IRQ, remapped to level 3
- */
-loc_serial1: li s1,~JAZZ_IE_SERIAL1
- li t1,JAZZ_SERIAL1_IRQ
- b loc_call
- li t3,PTRSIZE*JAZZ_SERIAL1_IRQ # delay slot
-
-/*
- * Serial port 2 IRQ, remapped to level 4
- */
-loc_serial2: li s1,~JAZZ_IE_SERIAL2
- li t1,JAZZ_SERIAL2_IRQ
- b loc_call
- li t3,PTRSIZE*JAZZ_SERIAL2_IRQ # delay slot
-
-local_vector: PTR loc_no_irq
- PTR loc_parallel
- PTR loc_floppy
- PTR loc_sound
- PTR loc_video
- PTR loc_ethernet
- PTR loc_scsi
- PTR loc_keyboard
- PTR loc_mouse
- PTR loc_serial1
- PTR loc_serial2
diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c
new file mode 100644
index 000000000..0e2803dd3
--- /dev/null
+++ b/arch/mips/kernel/proc.c
@@ -0,0 +1,62 @@
+/*
+ * linux/arch/mips/kernel/proc.c
+ *
+ * Copyright (C) 1995, 1996 Ralf Baechle
+ */
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <asm/bootinfo.h>
+#include <asm/mipsregs.h>
+
+unsigned long dflushes = 0;
+unsigned long iflushes = 0;
+unsigned long unaligned_instructions;
+
+/*
+ * BUFFER is PAGE_SIZE bytes long.
+ *
+ * Currently /proc/cpuinfo is being abused to print data about the
+ * number of date/instruction cacheflushes.
+ */
+int get_cpuinfo(char *buffer)
+{
+ const char *cpu_name[] = CPU_NAMES;
+ const char *mach_group_names[] = GROUP_NAMES;
+ const char *mach_unknown_names[] = GROUP_UNKNOWN_NAMES;
+ const char *mach_jazz_names[] = GROUP_JAZZ_NAMES;
+ const char *mach_dec_names[] = GROUP_DEC_NAMES;
+ const char *mach_arc_names[] = GROUP_ARC_NAMES;
+ const char *mach_sni_rm_names[] = GROUP_SNI_RM_NAMES;
+ const char **mach_group_to_name[] = { mach_unknown_names, mach_jazz_names,
+ mach_dec_names, mach_arc_names, mach_sni_rm_names};
+ unsigned int version = read_32bit_cp0_register(CP0_PRID);
+ int len;
+
+ len = sprintf(buffer, "cpu\t\t\t: MIPS\n");
+ len += sprintf(buffer + len, "cpu model\t\t: %s V%d.%d\n",
+ cpu_name[mips_cputype <= CPU_LAST ?
+ mips_cputype :
+ CPU_UNKNOWN],
+ (version >> 4) & 0x0f,
+ version & 0x0f);
+ len += sprintf(buffer + len, "system type\t\t: %s %s\n",
+ mach_group_names[mips_machgroup],
+ mach_group_to_name[mips_machgroup][mips_machtype]);
+ len += sprintf(buffer + len, "BogoMIPS\t\t: %lu.%02lu\n",
+ (loops_per_sec + 2500) / 500000,
+ ((loops_per_sec + 2500) / 5000) % 100);
+#if defined (__MIPSEB__)
+ len += sprintf(buffer + len, "byteorder\t\t: big endian\n");
+#endif
+#if defined (__MIPSEL__)
+ len += sprintf(buffer + len, "byteorder\t\t: little endian\n");
+#endif
+ len += sprintf(buffer + len, "D-cache flushes\t\t: %lu\n",
+ dflushes);
+ len += sprintf(buffer + len, "I-cache flushes\t\t: %lu\n",
+ iflushes);
+ len += sprintf(buffer + len, "unaligned accesses\t: %lu\n",
+ unaligned_instructions);
+
+ return len;
+}
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index dd69c3208..2ce906ea4 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -2,13 +2,13 @@
* linux/arch/mips/kernel/process.c
*
* Copyright (C) 1995 Ralf Baechle
- * written by Ralf Baechle
- */
-
-/*
- * This file handles the architecture-dependent parts of process handling..
+ *
+ * Modified for R3000/DECStation support by Paul M. Antoine 1995, 1996
+ *
+ * This file handles the architecture-dependent parts of initialization,
+ * though it does not yet currently fully support the DECStation,
+ * or R3000 - PMA.
*/
-
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -17,13 +17,16 @@
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/malloc.h>
-#include <linux/ldt.h>
+#include <linux/mman.h>
+#include <linux/sys.h>
#include <linux/user.h>
#include <linux/a.out.h>
#include <asm/bootinfo.h>
-#include <asm/segment.h>
+#include <asm/cache.h>
+#include <asm/uaccess.h>
#include <asm/pgtable.h>
+#include <asm/sgidefs.h>
#include <asm/system.h>
#include <asm/mipsregs.h>
#include <asm/processor.h>
@@ -32,185 +35,111 @@
asmlinkage void ret_from_sys_call(void);
-asmlinkage int sys_pipe(unsigned long * fildes)
-{
- int fd[2];
- int error;
-
- error = verify_area(VERIFY_WRITE,fildes,8);
- if (error)
- return error;
- error = do_pipe(fd);
- if (error)
- return error;
- put_fs_long(fd[0],0+fildes);
- put_fs_long(fd[1],1+fildes);
- return 0;
-}
-
-asmlinkage int sys_idle(void)
-{
- if (current->pid != 0)
- return -EPERM;
-
- /* endless idle loop with no priority at all */
- current->counter = -100;
- for (;;) {
- /*
- * R4[26]00 have wait, R4[04]00 don't.
- */
- if (wait_available && !need_resched)
- __asm__(".set\tmips3\n\t"
- "wait\n\t"
- ".set\tmips0\n\t");
- schedule();
- }
-}
-
-/*
- * This routine reboots the machine by asking the keyboard
- * controller to pulse the reset-line low. We try that for a while,
- * and if it doesn't work, we do some other stupid things.
- * Should be ok for Deskstation Tynes. Reseting others needs to be
- * investigated...
- */
-static inline void kb_wait(void)
-{
- int i;
-
- for (i=0; i<0x10000; i++)
- if ((inb_p(0x64) & 0x02) == 0)
- break;
-}
-
/*
- * Hard reset for Deskstation Tyne
- * No hint how this works on Pica boards.
+ * Free current thread data structures etc..
*/
-void hard_reset_now(void)
-{
- int i, j;
-
- sti();
- for (;;) {
- for (i=0; i<100; i++) {
- kb_wait();
- for(j = 0; j < 100000 ; j++)
- /* nothing */;
- outb(0xfe,0x64); /* pulse reset low */
- }
- }
-}
-
-void show_regs(struct pt_regs * regs)
+void exit_thread(void)
{
/*
- * Saved main processor registers
- */
- printk("$0 : %08x %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
- 0, regs->reg1, regs->reg2, regs->reg3,
- regs->reg4, regs->reg5, regs->reg6, regs->reg7);
- printk("$8 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
- regs->reg8, regs->reg9, regs->reg10, regs->reg11,
- regs->reg12, regs->reg13, regs->reg14, regs->reg15);
- printk("$16: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
- regs->reg16, regs->reg17, regs->reg18, regs->reg19,
- regs->reg20, regs->reg21, regs->reg22, regs->reg23);
- printk("$24: %08lx %08lx %08lx %08lx %08lx %08lx\n",
- regs->reg24, regs->reg25, regs->reg28, regs->reg29,
- regs->reg30, regs->reg31);
-
- /*
- * Saved cp0 registers
+ * Nothing to do
*/
- printk("epc : %08lx\nStatus: %08lx\nCause : %08lx\n",
- regs->cp0_epc, regs->cp0_status, regs->cp0_cause);
}
-/*
- * Free current thread data structures etc..
- */
-void exit_thread(void)
+void flush_thread(void)
{
/*
* Nothing to do
*/
}
-void flush_thread(void)
+void release_thread(struct task_struct *dead_task)
{
/*
* Nothing to do
*/
}
-
+
void copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
- struct task_struct * p, struct pt_regs * regs)
+ struct task_struct * p, struct pt_regs * regs)
{
struct pt_regs * childregs;
+ long childksp;
+ childksp = p->kernel_stack_page + KERNEL_STACK_SIZE - 8;
/*
* set up new TSS
*/
childregs = ((struct pt_regs *) (p->kernel_stack_page + PAGE_SIZE)) - 1;
*childregs = *regs;
- childregs->reg2 = 0;
- regs->reg2 = p->pid;
- childregs->reg29 = usp;
- p->tss.ksp = (p->kernel_stack_page + PAGE_SIZE - 8);
- p->tss.reg29 = (unsigned long) childregs; /* new sp */
- p->tss.reg31 = (unsigned long) ret_from_sys_call;
+ childregs->regs[2] = (__register_t) 0; /* Child gets zero as return value */
+ childregs->regs[7] = (__register_t) 0; /* Clear error flag */
+ regs->regs[2] = (__register_t) p->pid;
+ if (childregs->cp0_status & ST0_CU0)
+ childregs->regs[29] = (__register_t) childksp;
+ else
+ childregs->regs[29] = (__register_t) usp;
+ p->tss.ksp = childksp;
+ p->tss.reg29 = (__register_t)(long) childregs; /* new sp */
+ p->tss.reg31 = (__register_t) ret_from_sys_call;
+
+ /*
+ * Copy thread specific flags.
+ */
+ p->tss.mflags = p->tss.mflags;
/*
* New tasks loose permission to use the fpu. This accelerates context
* switching for most programs since they don't use the fpu.
*/
+#if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2)
p->tss.cp0_status = read_32bit_cp0_register(CP0_STATUS) &
- ~(ST0_CU3|ST0_CU2|ST0_CU1|ST0_KSU|ST0_ERL|ST0_EXL);
+ ~(ST0_CU3|ST0_CU2|ST0_CU1);
+#endif
+#if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4)
+ p->tss.cp0_status = read_32bit_cp0_register(CP0_STATUS) &
+ ~(ST0_CU3|ST0_CU2|ST0_CU1|ST0_KSU|ST0_ERL|ST0_EXL);
+#endif
childregs->cp0_status &= ~(ST0_CU3|ST0_CU2|ST0_CU1);
}
/*
- * fill in the user structure for a core dump..
+ * Do necessary setup to start up a newly executed thread.
*/
-void dump_thread(struct pt_regs * regs, struct user * dump)
+extern void (*switch_to_user_mode)(struct pt_regs *regs);
+
+void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp)
{
+ set_fs(USER_DS);
+ regs->cp0_epc = (__register_t) pc;
/*
- * To do...
+ * New thread loses kernel privileges.
*/
+ switch_to_user_mode(regs);
+ regs->regs[29] = (__register_t) sp;
+ regs->regs[31] = 0;
}
-asmlinkage int sys_fork(struct pt_regs regs)
-{
- return do_fork(COPYVM | SIGCHLD, regs.reg29, &regs);
-}
-
-asmlinkage int sys_clone(struct pt_regs regs)
+/*
+ * fill in the fpu structure for a core dump..
+ *
+ * Actually this is "int dump_fpu (struct pt_regs * regs, struct user *fpu)"
+ */
+int dump_fpu (int shutup_the_gcc_warning_about_elf_fpregset_t)
{
- unsigned long clone_flags;
- unsigned long newsp;
+ int fpvalid = 0;
+ /*
+ * To do...
+ */
- newsp = regs.reg4;
- clone_flags = regs.reg5;
- if (!newsp)
- newsp = regs.reg29;
- if (newsp == regs.reg29)
- clone_flags |= COPYVM;
- return do_fork(clone_flags, newsp, &regs);
+ return fpvalid;
}
/*
- * sys_execve() executes a new program.
+ * fill in the user structure for a core dump..
*/
-asmlinkage int sys_execve(struct pt_regs regs)
+void dump_thread(struct pt_regs * regs, struct user * dump)
{
- int error;
- char * filename;
-
- error = getname((char *) regs.reg4, &filename);
- if (error)
- return error;
- error = do_execve(filename, (char **) regs.reg5, (char **) regs.reg6, &regs);
- putname(filename);
- return error;
+ /*
+ * To do...
+ */
}
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c
index 6f35ceb67..93fae9961 100644
--- a/arch/mips/kernel/ptrace.c
+++ b/arch/mips/kernel/ptrace.c
@@ -1,7 +1,12 @@
-/* ptrace.c */
-/* By Ross Biro 1/23/92 */
-/* edited by Linus Torvalds */
-
+/*
+ * Ptrace(2) syscall for MIPS. Based on arch/i386/kernel/ptrace.c.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1995, 1996 by Ralf Baechle.
+ */
#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/sched.h>
@@ -10,11 +15,10 @@
#include <linux/ptrace.h>
#include <linux/user.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/system.h>
-#if 0
/*
* does not yet catch signals sent when the child dies.
* in exit.c or in signal.c.
@@ -24,9 +28,6 @@
/* 1 = access 0 = no access */
#define FLAG_MASK 0x00044dd5
-/* set's the trap flag. */
-#define TRAP_FLAG 0x100
-
/*
* this is the number to subtract from the top of the stack. To find
* the local frame.
@@ -34,7 +35,8 @@
#define MAGICNUMBER 68
/* change a pid into a task struct. */
-static inline struct task_struct * get_task(int pid)
+static inline struct task_struct *
+get_task(int pid)
{
int i;
@@ -46,16 +48,15 @@ static inline struct task_struct * get_task(int pid)
}
/*
- * this routine will get a word off of the processes privileged stack.
- * the offset is how far from the base addr as stored in the TSS.
- * this routine assumes that all the privileged stacks are in our
- * data space.
+ * This routine will get a word off of the processes privileged stack.
+ * The offset is how far from the base addr as stored in the TSS.
*/
-static inline int get_stack_long(struct task_struct *task, int offset)
+static inline int
+get_stack_long(struct task_struct *task, int offset)
{
unsigned char *stack;
- stack = (unsigned char *)task->tss.esp0;
+ stack = (unsigned char *)(unsigned long)task->tss.reg29;
stack += offset;
return (*((int *)stack));
}
@@ -66,12 +67,13 @@ static inline int get_stack_long(struct task_struct *task, int offset)
* this routine assumes that all the privileged stacks are in our
* data space.
*/
-static inline int put_stack_long(struct task_struct *task, int offset,
+static inline int
+put_stack_long(struct task_struct *task, int offset,
unsigned long data)
{
unsigned char * stack;
- stack = (unsigned char *) task->tss.esp0;
+ stack = (unsigned char *)(unsigned long)task->tss.reg29;
stack += offset;
*(unsigned long *) stack = data;
return 0;
@@ -83,16 +85,19 @@ static inline int put_stack_long(struct task_struct *task, int offset,
* and that it is in the task area before calling this: this routine does
* no checking.
*/
-static unsigned long get_long(struct vm_area_struct * vma, unsigned long addr)
+static unsigned long
+get_long(struct task_struct * tsk,
+ struct vm_area_struct * vma, unsigned long addr)
{
pgd_t * pgdir;
+ pmd_t * pgmiddle;
pte_t * pgtable;
unsigned long page;
repeat:
- pgdir = PAGE_DIR_OFFSET(vma->vm_task, addr);
+ pgdir = pgd_offset(vma->vm_mm, addr);
if (pgd_none(*pgdir)) {
- do_no_page(vma, addr, 0);
+ do_no_page(tsk, vma, addr, 0);
goto repeat;
}
if (pgd_bad(*pgdir)) {
@@ -100,14 +105,23 @@ repeat:
pgd_clear(pgdir);
return 0;
}
- pgtable = (pte_t *) (PAGE_PTR(addr) + pgd_page(*pgdir));
+ pgmiddle = pmd_offset(pgdir, addr);
+ if (pmd_none(*pgmiddle)) {
+ do_no_page(tsk, vma, addr, 0);
+ goto repeat;
+ }
+ if (pmd_bad(*pgmiddle)) {
+ printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
+ pmd_clear(pgmiddle);
+ return 0;
+ }
+ pgtable = pte_offset(pgmiddle, addr);
if (!pte_present(*pgtable)) {
- do_no_page(vma, addr, 0);
+ do_no_page(tsk, vma, addr, 0);
goto repeat;
}
- page = pte_page(*pgtable);
/* this is a hack for non-kernel-mapped video buffers and similar */
- if (page >= high_memory)
+ if (MAP_NR(page) < max_mapnr)
return 0;
page += addr & ~PAGE_MASK;
return *(unsigned long *) page;
@@ -122,17 +136,19 @@ repeat:
* Now keeps R/W state of page so that a text page stays readonly
* even if a debugger scribbles breakpoints into it. -M.U-
*/
-static void put_long(struct vm_area_struct * vma, unsigned long addr,
- unsigned long data)
+static void
+put_long(struct task_struct * tsk,
+ struct vm_area_struct * vma, unsigned long addr, unsigned long data)
{
pgd_t *pgdir;
+ pmd_t *pgmiddle;
pte_t *pgtable;
unsigned long page;
repeat:
- pgdir = PAGE_DIR_OFFSET(vma->vm_task, addr);
+ pgdir = pgd_offset(vma->vm_mm, addr);
if (!pgd_present(*pgdir)) {
- do_no_page(vma, addr, 1);
+ do_no_page(tsk, vma, addr, 1);
goto repeat;
}
if (pgd_bad(*pgdir)) {
@@ -140,33 +156,47 @@ repeat:
pgd_clear(pgdir);
return;
}
- pgtable = (pte_t *) (PAGE_PTR(addr) + pgd_page(*pgdir));
+ pgmiddle = pmd_offset(pgdir, addr);
+ if (pmd_none(*pgmiddle)) {
+ do_no_page(tsk, vma, addr, 1);
+ goto repeat;
+ }
+ if (pmd_bad(*pgmiddle)) {
+ printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
+ pmd_clear(pgmiddle);
+ return;
+ }
+ pgtable = pte_offset(pgmiddle, addr);
if (!pte_present(*pgtable)) {
- do_no_page(vma, addr, 1);
+ do_no_page(tsk, vma, addr, 1);
goto repeat;
}
page = pte_page(*pgtable);
if (!pte_write(*pgtable)) {
- do_wp_page(vma, addr, 1);
+ do_wp_page(tsk, vma, addr, 1);
goto repeat;
}
-/* this is a hack for non-kernel-mapped video buffers and similar */
- if (page < high_memory) {
- page += addr & ~PAGE_MASK;
- *(unsigned long *) page = data;
- }
-/* we're bypassing pagetables, so we have to set the dirty bit ourselves */
-/* this should also re-instate whatever read-only mode there was before */
- *pgtable = pte_mkdirty(mk_pte(page, vma->vm_page_prot));
- invalidate();
+ /*
+ * This is a hack for non-kernel-mapped video buffers and similar
+ */
+ if (page >= high_memory)
+ *(unsigned long *) (page + (addr & ~PAGE_MASK)) = data;
+ /*
+ * We're bypassing pagetables, so we have to set the dirty bit
+ * ourselves. This should also re-instate whatever read-only mode
+ * there was before
+ */
+ set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
+ flush_tlb();
}
-static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr)
+static struct vm_area_struct *
+find_extend_vma(struct task_struct * tsk, unsigned long addr)
{
struct vm_area_struct * vma;
addr &= PAGE_MASK;
- vma = find_vma(tsk, addr);
+ vma = find_vma(tsk->mm, addr);
if (!vma)
return NULL;
if (vma->vm_start <= addr)
@@ -184,8 +214,9 @@ static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigne
* This routine checks the page boundaries, and that the offset is
* within the task area. It then calls get_long() to read a long.
*/
-static int read_long(struct task_struct * tsk, unsigned long addr,
- unsigned long * result)
+static int
+read_long(struct task_struct * tsk, unsigned long addr,
+ unsigned long * result)
{
struct vm_area_struct * vma = find_extend_vma(tsk, addr);
@@ -200,8 +231,8 @@ static int read_long(struct task_struct * tsk, unsigned long addr,
if (!vma_high || vma_high->vm_start != vma->vm_end)
return -EIO;
}
- low = get_long(vma, addr & ~(sizeof(long)-1));
- high = get_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1));
+ low = get_long(tsk, vma, addr & ~(sizeof(long)-1));
+ high = get_long(tsk, vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1));
switch (addr & (sizeof(long)-1)) {
case 1:
low >>= 8;
@@ -218,7 +249,7 @@ static int read_long(struct task_struct * tsk, unsigned long addr,
}
*result = low;
} else
- *result = get_long(vma, addr);
+ *result = get_long(tsk, vma, addr);
return 0;
}
@@ -226,8 +257,9 @@ static int read_long(struct task_struct * tsk, unsigned long addr,
* This routine checks the page boundaries, and that the offset is
* within the task area. It then calls put_long() to write a long.
*/
-static int write_long(struct task_struct * tsk, unsigned long addr,
- unsigned long data)
+static int
+write_long(struct task_struct * tsk, unsigned long addr,
+ unsigned long data)
{
struct vm_area_struct * vma = find_extend_vma(tsk, addr);
@@ -242,8 +274,8 @@ static int write_long(struct task_struct * tsk, unsigned long addr,
if (!vma_high || vma_high->vm_start != vma->vm_end)
return -EIO;
}
- low = get_long(vma, addr & ~(sizeof(long)-1));
- high = get_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1));
+ low = get_long(tsk, vma, addr & ~(sizeof(long)-1));
+ high = get_long(tsk, vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1));
switch (addr & (sizeof(long)-1)) {
case 0: /* shouldn't happen, but safety first */
low = data;
@@ -267,23 +299,18 @@ static int write_long(struct task_struct * tsk, unsigned long addr,
high |= data >> 8;
break;
}
- put_long(vma, addr & ~(sizeof(long)-1),low);
- put_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1),high);
+ put_long(tsk, vma, addr & ~(sizeof(long)-1),low);
+ put_long(tsk, vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1),high);
} else
- put_long(vma, addr, data);
+ put_long(tsk, vma, addr, data);
return 0;
}
-#endif
-asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
+asmlinkage int
+sys_ptrace(long request, long pid, long addr, long data)
{
-#if 1
- return -ENOSYS;
-#else
struct task_struct *child;
struct user * dummy;
- int i;
-
dummy = NULL;
@@ -304,8 +331,10 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
return -EPERM;
if ((!child->dumpable ||
(current->uid != child->euid) ||
+ (current->uid != child->suid) ||
(current->uid != child->uid) ||
(current->gid != child->egid) ||
+ (current->gid != child->sgid) ||
(current->gid != child->gid)) && !suser())
return -EPERM;
/* the same process cannot be attached many times */
@@ -330,7 +359,6 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
return -ESRCH;
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;
@@ -341,22 +369,21 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
return res;
res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
if (!res)
- put_fs_long(tmp,(unsigned long *) data);
+ put_user(tmp, (unsigned long *)data);
return res;
}
- /* read the word at location addr in the USER area. */
+#if 0
+ /*
+ * Read the word at location addr in the USER area.
+ */
case PTRACE_PEEKUSR: {
unsigned long tmp;
int res;
- if ((addr & 3) || addr < 0 ||
- addr > sizeof(struct user) - 3)
+ if (addr < 0 || addr > sizeof(struct user) - 3)
return -EIO;
- res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
- if (res)
- return res;
tmp = 0; /* Default return condition */
if(addr < 17*sizeof(long)) {
addr = addr >> 2; /* temporary hack. */
@@ -366,23 +393,23 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
addr == FS || addr == GS ||
addr == CS || addr == SS)
tmp &= 0xffff;
- };
- if(addr >= (long) &dummy->u_debugreg[0] &&
- addr <= (long) &dummy->u_debugreg[7]){
- addr -= (long) &dummy->u_debugreg[0];
- addr = addr >> 2;
- tmp = child->debugreg[addr];
- };
- put_fs_long(tmp,(unsigned long *) data);
+ }
+ put_user(tmp, (unsigned long *)data);
return 0;
}
-
- /* when I and D space are separate, this will have to be fixed. */
- case PTRACE_POKETEXT: /* write the word at location addr. */
+#endif
+ /*
+ * Write the word at location addr.
+ */
+ case PTRACE_POKETEXT:
case PTRACE_POKEDATA:
return write_long(child,addr,data);
- case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
+ /*
+ * Write the word at location addr in the user area.
+ */
+ case PTRACE_POKEUSR:
+#if 0
if ((addr & 3) || addr < 0 ||
addr > sizeof(struct user) - 3)
return -EIO;
@@ -404,112 +431,79 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
}
/* Do not allow the user to set the debug register for kernel
address space */
- if(addr < 17){
+ if(addr < 17) {
if (put_stack_long(child, sizeof(long)*addr-MAGICNUMBER, data))
return -EIO;
return 0;
- };
-
- /* We need to be very careful here. We implicitly
- want to modify a portion of the task_struct, and we
- have to be selective about what portions we allow someone
- to modify. */
-
- addr = addr << 2; /* Convert back again */
- if(addr >= (long) &dummy->u_debugreg[0] &&
- addr <= (long) &dummy->u_debugreg[7]){
-
- if(addr == (long) &dummy->u_debugreg[4]) return -EIO;
- if(addr == (long) &dummy->u_debugreg[5]) return -EIO;
- if(addr < (long) &dummy->u_debugreg[4] &&
- ((unsigned long) data) >= 0xbffffffd) return -EIO;
-
- if(addr == (long) &dummy->u_debugreg[7]) {
- data &= ~DR_CONTROL_RESERVED;
- for(i=0; i<4; i++)
- if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
- return -EIO;
- };
-
- addr -= (long) &dummy->u_debugreg;
- addr = addr >> 2;
- child->debugreg[addr] = data;
- return 0;
- };
+ }
+
return -EIO;
+#endif
- case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
+ /*
+ * Continue and stop at next (return from) syscall.
+ */
+ case PTRACE_SYSCALL:
case PTRACE_CONT: { /* restart after signal. */
- long tmp;
-
- if ((unsigned long) data > NSIG)
+ if ((unsigned long) data > _NSIG)
return -EIO;
if (request == PTRACE_SYSCALL)
child->flags |= PF_TRACESYS;
else
child->flags &= ~PF_TRACESYS;
child->exit_code = data;
- child->state = TASK_RUNNING;
- /* make sure the single step bit is not set. */
- tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;
- put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
+ wake_up_process(child);
return 0;
}
-/*
- * 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.
- */
+ /*
+ * 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;
-
- child->state = TASK_RUNNING;
+ if (child->state == TASK_ZOMBIE) /* already dead */
+ return 0;
+ wake_up_process(child);
child->exit_code = SIGKILL;
- /* make sure the single step bit is not set. */
- tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;
- put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
return 0;
}
case PTRACE_SINGLESTEP: { /* set the trap flag. */
- long tmp;
+ /*
+ * Not supported yet.
+ */
+ return -ENOSYS;
if ((unsigned long) data > NSIG)
return -EIO;
- child->flags &= ~PF_TRACESYS;
- tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) | TRAP_FLAG;
- put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
- child->state = TASK_RUNNING;
+ wake_up_process(child);
child->exit_code = data;
- /* give it a chance to run. */
+ /*
+ * give it a chance to run.
+ */
return 0;
}
case PTRACE_DETACH: { /* detach a process that was attached. */
- long tmp;
-
if ((unsigned long) data > NSIG)
return -EIO;
child->flags &= ~(PF_PTRACED|PF_TRACESYS);
- child->state = TASK_RUNNING;
+ wake_up_process(child);
child->exit_code = data;
REMOVE_LINKS(child);
child->p_pptr = child->p_opptr;
SET_LINKS(child);
- /* make sure the single step bit is not set. */
- tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;
- put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
return 0;
}
default:
return -EIO;
}
-#endif
}
-asmlinkage void syscall_trace(void)
+asmlinkage void
+syscall_trace(void)
{
if ((current->flags & (PF_PTRACED|PF_TRACESYS))
!= (PF_PTRACED|PF_TRACESYS))
diff --git a/arch/mips/kernel/r4xx0.S b/arch/mips/kernel/r4xx0.S
deleted file mode 100644
index a68b32243..000000000
--- a/arch/mips/kernel/r4xx0.S
+++ /dev/null
@@ -1,732 +0,0 @@
-/*
- * arch/mips/kernel/r4xx0.S
- *
- * Copyright (C) 1994, 1995 Waldorf Electronics
- * Written by Ralf Baechle and Andreas Busse
- *
- * This file contains most of the R4xx0 specific routines.
- *
- * This code is evil magic. Read appendix f (coprozessor 0 hazards) of
- * all R4xx0 manuals and think about that MIPS means "Microprocessor without
- * Interlocked Pipeline Stages" before you even think about changing this code!
- */
-#include <linux/autoconf.h>
-
-#include <asm/asm.h>
-#include <asm/bootinfo.h>
-#include <asm/cachectl.h>
-#include <asm/mipsconfig.h>
-#include <asm/mipsregs.h>
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/processor.h>
-#include <asm/mipsregs.h>
-#include <asm/segment.h>
-#include <asm/stackframe.h>
-
-MODE_ALIAS = 0x0016 # uncachable
-
- .text
- .set mips3
- .set noreorder
-
- .align 5
- NESTED(handle_tlbl, FR_SIZE, sp)
- .set noat
- /*
- * Check whether this is a refill or an invalid exception
- *
- * NOTE: Some MIPS manuals say that the R4x00 sets the
- * BadVAddr only when EXL == 0. This is wrong - BadVAddr
- * is being set for all Reload, Invalid and Modified
- * exceptions.
- */
- mfc0 k0,CP0_BADVADDR
- mfc0 k1,CP0_ENTRYHI
- ori k0,0x1fff
- xori k0,0x1fff
- andi k1,0xff
- or k0,k1
- mfc0 k1,CP0_ENTRYHI
- mtc0 k0,CP0_ENTRYHI
- nop # for R4[04]00 pipeline
- nop
- nop
- tlbp
- nop # for R4[04]00 pipeline
- nop
- mfc0 k0,CP0_INDEX
- bgez k0,invalid_tlbl # bad addr in c0_badvaddr
- mtc0 k1,CP0_ENTRYHI # delay slot
- /*
- * Damn... The next nop is required on my R4400PC V5.0, but
- * I don't know why - at least there is no documented
- * reason as for the others :-(
- */
- nop
-
-#ifdef CONFIG_DEBUG_TLB
- /*
- * OK, this is a double fault. Let's see whether this is
- * due to an invalid entry in the page_table.
- */
- dmfc0 k0,CP0_BADVADDR
- srl k0,12
- sll k0,2
- lui k1,%HI(TLBMAP)
- addu k0,k1
- lw k1,(k0)
- andi k1,(_PAGE_PRESENT|_PAGE_ACCESSED)
- bnez k1,reload_pgd_entries
- nop # delay slot
-
- .set noat
- SAVE_ALL
- .set at
- PRINT("Double fault caused by invalid entries in pgd:\n")
- dmfc0 a1,CP0_BADVADDR
- PRINT("Double fault address : %08lx\n")
- dmfc0 a1,CP0_EPC
- PRINT("c0_epc : %08lx\n")
- jal show_regs
- move a0,sp
- .set noat
- STI
- .set at
- PANIC("Corrupted pagedir")
- .set noat
-
-reload_pgd_entries:
-#endif /* CONFIG_DEBUG_TLB */
-
- /*
- * Load missing pair of entries from the pgd and return.
- */
- dmfc0 k1,CP0_CONTEXT
- dsra k1,1
- lwu k0,(k1) # Never causes nested exception
- lwu k1,4(k1)
- dsrl k0,6 # Convert to EntryLo format
- dsrl k1,6 # Convert to EntryLo format
- dmtc0 k0,CP0_ENTRYLO0
- dmtc0 k1,CP0_ENTRYLO1
- nop # for R4[04]00 pipeline
- tlbwr
- nop # for R4[04]00 pipeline
- nop
- nop
- /*
- * We don't know whether the original access was read or
- * write, so return and see what happens...
- */
- eret
-
- /*
- * Handle invalid exception
- *
- * There are two possible causes for an invalid (tlbl)
- * exception:
- * 1) pages with present bit set but the valid bit clear
- * 2) nonexistant pages
- * Case one needs fast handling, therefore don't save
- * registers yet.
- *
- * k0 contains c0_index.
- */
-invalid_tlbl: /*
- * Remove entry so we don't need to care later
- * For sake of the R4000 V2.2 pipeline the tlbwi insn
- * has been moved down. Moving it around is juggling with
- * explosives...
- */
- lui k1,0x0008
- or k0,k1
- dsll k0,13
- dmtc0 k0,CP0_ENTRYHI
- dmtc0 zero,CP0_ENTRYLO0
- dmtc0 zero,CP0_ENTRYLO1
- /*
- * Test present bit in entry
- */
- dmfc0 k0,CP0_BADVADDR
- srl k0,12
- sll k0,2
- tlbwi # do not move!
- lui k1,%HI(TLBMAP)
- addu k0,k1
- lw k1,(k0)
- andi k1,(_PAGE_PRESENT|_PAGE_READ)
- xori k1,(_PAGE_PRESENT|_PAGE_READ)
- bnez k1,nopage_tlbl
- /*
- * Present and read bits are set -> set valid and accessed bits
- */
- lw k1,(k0) # delay slot
- ori k1,(_PAGE_VALID|_PAGE_ACCESSED)
- sw k1,(k0)
- eret
-
- /*
- * Page doesn't exist. Lots of work which is less important
- * for speed needs to be done, so hand it all over to the
- * kernel memory management routines.
- */
-nopage_tlbl: SAVE_ALL
- STI
- .set at
- /*
- * a0 (struct pt_regs *) regs
- * a1 (unsigned long) 0 for read access
- */
- move a0,sp
- jal do_page_fault
- li a1,0 # delay slot
- j ret_from_sys_call
- nop # delay slot
- END(handle_tlbl)
-
- .text
- .align 5
- NESTED(handle_tlbs, FR_SIZE, sp)
- .set noat
- /*
- * It is impossible that is a nested reload exception.
- * Therefore this must be a invalid exception.
- * Two possible cases:
- * 1) Page exists but not dirty.
- * 2) Page doesn't exist yet. Hand over to the kernel.
- *
- * Test whether present bit in entry is set
- */
- dmfc0 k0,CP0_BADVADDR
- srl k0,12
- sll k0,2
- lui k1,%HI(TLBMAP)
- addu k0,k1
- lw k1,(k0)
- tlbp # find faulting entry
- andi k1,(_PAGE_PRESENT|_PAGE_WRITE)
- xori k1,(_PAGE_PRESENT|_PAGE_WRITE)
- bnez k1,nopage_tlbs
- /*
- * Present and writable bits set: set accessed and dirty bits.
- */
- lw k1,(k0) # delay slot
- ori k1,k1,(_PAGE_ACCESSED|_PAGE_MODIFIED| \
- _PAGE_VALID|_PAGE_DIRTY)
- sw k1,(k0)
- /*
- * Now reload the entry into the TLB
- */
- ori k0,0x0004
- xori k0,0x0004
- lw k1,4(k0)
- lw k0,(k0)
- srl k1,6
- srl k0,6
- dmtc0 k1,CP0_ENTRYLO1
- dmtc0 k0,CP0_ENTRYLO0
- nop # for R4[04]00 pipeline
- tlbwi
- nop # for R4[04]00 pipeline
- nop
- nop
- eret
-
- /*
- * Page doesn't exist. Lots of work which is less important
- * for speed needs to be done, so hand it all over to the
- * kernel memory management routines.
- */
-nopage_tlbs:
-#if 0
- .set mips3
- SAVE_ALL
- .set mips0
- PRINT("nopage_tlbs\n")
- .set mips3
- RESTORE_ALL
- .set mips3
- j 1f
- nop
-#endif
-nowrite_mod:
-#if 0
- .set mips3
- SAVE_ALL
- .set mips0
- PRINT("nopage_mod\n")
- .set mips3
- RESTORE_ALL
- .set mips3
- j 1f
- nop
-1:
-#endif
- /*
- * Remove entry so we don't need to care later
- */
- mfc0 k0,CP0_INDEX
-#ifdef CONFIG_DEBUG_TLB
- bgez k0,2f
- nop
- /*
- * We got a tlbs exception but found no matching entry in
- * the tlb. This should never happen. Paranoia makes us
- * check it, though.
- */
- SAVE_ALL
- jal show_regs
- move a0,sp
- .set at
- mfc0 a1,CP0_BADVADDR
- PRINT("c0_badvaddr == %08lx\n")
- mfc0 a1,CP0_INDEX
- PRINT("c0_index == %08x\n")
- mfc0 a1,CP0_ENTRYHI
- PRINT("c0_entryhi == %08x\n")
- jal dump_tlb_nonwired
- nop
- .set noat
- STI
- .set at
- PANIC("Tlbs or tlbm exception with no matching entry in tlb")
-1: j 1b
- nop
-2:
-#endif /* CONFIG_DEBUG_TLB */
- lui k1,0x0008
- or k0,k1
- dsll k0,13
- dmtc0 k0,CP0_ENTRYHI
- dmtc0 zero,CP0_ENTRYLO0
- dmtc0 zero,CP0_ENTRYLO1
- nop # for R4[04]00 pipeline
- nop # R4000 V2.2 requires 4 NOPs
- nop
- nop
- tlbwi
- .set noat
- SAVE_ALL
- STI
- .set at
- /*
- * a0 (struct pt_regs *) regs
- * a1 (unsigned long) 1 for write access
- */
- move a0,sp
- jal do_page_fault
- li a1,1 # delay slot
- j ret_from_sys_call
- nop # delay slot
- END(handle_tlbs)
-
- .align 5
- NESTED(handle_mod, FR_SIZE, sp)
- .set noat
- /*
- * Two possible cases:
- * 1) Page is writable but not dirty -> set dirty and return
- * 2) Page is not writable -> call C handler
- */
- dmfc0 k0,CP0_BADVADDR
- srl k0,12
- sll k0,2
- lui k1,%HI(TLBMAP)
- addu k0,k1
- lw k1,(k0)
- tlbp # find faulting entry
- andi k1,_PAGE_WRITE
- beqz k1,nowrite_mod
- /*
- * Present and writable bits set: set accessed and dirty bits.
- */
- lw k1,(k0) # delay slot
- ori k1,(_PAGE_ACCESSED|_PAGE_DIRTY)
- sw k1,(k0)
- /*
- * Now reload the entry into the tlb
- */
- ori k0,0x0004
- xori k0,0x0004
- lw k1,4(k0)
- lw k0,(k0)
- srl k1,6
- srl k0,6
- dmtc0 k1,CP0_ENTRYLO1
- dmtc0 k0,CP0_ENTRYLO0
- nop # for R4[04]00 pipeline
- nop
- nop
- tlbwi
- nop # for R4[04]00 pipeline
- nop
- nop
- eret
- END(handle_mod)
- .set at
-
- .set noreorder
- LEAF(tlbflush)
- li t0,PM_4K
- mtc0 t0,CP0_PAGEMASK
- la t0,boot_info
- lw t0,OFFSET_BOOTINFO_TLB_ENTRIES(t0)
- dmtc0 zero,CP0_ENTRYLO0
- dmtc0 zero,CP0_ENTRYLO1
- mfc0 t2,CP0_WIRED
-1: subu t0,1
- mtc0 t0,CP0_INDEX
- lui t1,0x0008
- or t1,t0,t1
- dsll t1,13
- dmtc0 t1,CP0_ENTRYHI
- bne t2,t0,1b
- tlbwi # delay slot
- jr ra
- nop
- END(tlbflush)
-
- /*
- * Code necessary to switch tasks on an Linux/MIPS machine.
- */
- .align 5
- LEAF(resume)
- /*
- * Current task's task_struct
- */
- lui t5,%hi(current)
- lw t0,%lo(current)(t5)
-
- /*
- * Save status register
- */
- mfc0 t1,CP0_STATUS
- addu t0,a1 # Add tss offset
- sw t1,TOFF_CP0_STATUS(t0)
-
- /*
- * Disable interrupts
- */
- ori t2,t1,0x1f
- xori t2,0x1e
- mtc0 t2,CP0_STATUS
-
- /*
- * Save non-scratch registers
- * All other registers have been saved on the kernel stack
- */
- sw s0,TOFF_REG16(t0)
- sw s1,TOFF_REG17(t0)
- sw s2,TOFF_REG18(t0)
- sw s3,TOFF_REG19(t0)
- sw s4,TOFF_REG20(t0)
- sw s5,TOFF_REG21(t0)
- sw s6,TOFF_REG22(t0)
- sw s7,TOFF_REG23(t0)
- sw gp,TOFF_REG28(t0)
- sw sp,TOFF_REG29(t0)
- sw fp,TOFF_REG30(t0)
-
- /*
- * Save floating point state
- */
- sll t2,t1,2
- bgez t2,2f
- sw ra,TOFF_REG31(t0) # delay slot
- sll t2,t1,5
- bgez t2,1f
- sdc1 $f0,(TOFF_FPU+0)(t0) # delay slot
- /*
- * Store the 16 odd double precision registers
- */
- sdc1 $f1,(TOFF_FPU+8)(t0)
- sdc1 $f3,(TOFF_FPU+24)(t0)
- sdc1 $f5,(TOFF_FPU+40)(t0)
- sdc1 $f7,(TOFF_FPU+56)(t0)
- sdc1 $f9,(TOFF_FPU+72)(t0)
- sdc1 $f11,(TOFF_FPU+88)(t0)
- sdc1 $f13,(TOFF_FPU+104)(t0)
- sdc1 $f15,(TOFF_FPU+120)(t0)
- sdc1 $f17,(TOFF_FPU+136)(t0)
- sdc1 $f19,(TOFF_FPU+152)(t0)
- sdc1 $f21,(TOFF_FPU+168)(t0)
- sdc1 $f23,(TOFF_FPU+184)(t0)
- sdc1 $f25,(TOFF_FPU+200)(t0)
- sdc1 $f27,(TOFF_FPU+216)(t0)
- sdc1 $f29,(TOFF_FPU+232)(t0)
- sdc1 $f31,(TOFF_FPU+248)(t0)
-
- /*
- * Store the 16 even double precision registers
- */
-1: cfc1 t1,$31
- sdc1 $f2,(TOFF_FPU+16)(t0)
- sdc1 $f4,(TOFF_FPU+32)(t0)
- sdc1 $f6,(TOFF_FPU+48)(t0)
- sdc1 $f8,(TOFF_FPU+64)(t0)
- sdc1 $f10,(TOFF_FPU+80)(t0)
- sdc1 $f12,(TOFF_FPU+96)(t0)
- sdc1 $f14,(TOFF_FPU+112)(t0)
- sdc1 $f16,(TOFF_FPU+128)(t0)
- sdc1 $f18,(TOFF_FPU+144)(t0)
- sdc1 $f20,(TOFF_FPU+160)(t0)
- sdc1 $f22,(TOFF_FPU+176)(t0)
- sdc1 $f24,(TOFF_FPU+192)(t0)
- sdc1 $f26,(TOFF_FPU+208)(t0)
- sdc1 $f28,(TOFF_FPU+224)(t0)
- sdc1 $f30,(TOFF_FPU+240)(t0)
- sw t1,(TOFF_FPU+256)(t0)
-
- /*
- * Switch current task
- */
-2: sw a0,%lo(current)(t5)
- addu a0,a1 # Add tss offset
-
- /*
- * Switch address space
- */
-
- /*
- * (Choose new ASID for process)
- * This isn't really required, but would speed up
- * context switching.
- */
-
- /*
- * Switch the root pointer
- */
- lw t0,TOFF_PG_DIR(a0)
- li t1,TLB_ROOT
- mtc0 t1,CP0_ENTRYHI
- mtc0 zero,CP0_INDEX
- srl t0,6
- ori t0,MODE_ALIAS
- mtc0 t0,CP0_ENTRYLO0
- mtc0 zero,CP0_ENTRYLO1
- lw a2,TOFF_CP0_STATUS(a0)
-
- /*
- * Flush tlb
- * (probably not needed, doesn't clobber a0-a3)
- */
- jal tlbflush
- tlbwi # delay slot
-
- /*
- * Restore fpu state:
- * - cp0 status register bits
- * - fp gp registers
- * - cp1 status/control register
- */
- ori t1,a2,1 # pipeline magic
- xori t1,1
- mtc0 t1,CP0_STATUS
- sll t0,a2,2
- bgez t0,2f
- sll t0,a2,5 # delay slot
- bgez t0,1f
- ldc1 $f0,(TOFF_FPU+0)(a0) # delay slot
- /*
- * Restore the 16 odd double precision registers only
- * when enabled in the cp0 status register.
- */
- ldc1 $f1,(TOFF_FPU+8)(a0)
- ldc1 $f3,(TOFF_FPU+24)(a0)
- ldc1 $f5,(TOFF_FPU+40)(a0)
- ldc1 $f7,(TOFF_FPU+56)(a0)
- ldc1 $f9,(TOFF_FPU+72)(a0)
- ldc1 $f11,(TOFF_FPU+88)(a0)
- ldc1 $f13,(TOFF_FPU+104)(a0)
- ldc1 $f15,(TOFF_FPU+120)(a0)
- ldc1 $f17,(TOFF_FPU+136)(a0)
- ldc1 $f19,(TOFF_FPU+152)(a0)
- ldc1 $f21,(TOFF_FPU+168)(a0)
- ldc1 $f23,(TOFF_FPU+184)(a0)
- ldc1 $f25,(TOFF_FPU+200)(a0)
- ldc1 $f27,(TOFF_FPU+216)(a0)
- ldc1 $f29,(TOFF_FPU+232)(a0)
- ldc1 $f31,(TOFF_FPU+248)(a0)
-
- /*
- * Restore the 16 even double precision registers
- * when cp1 was enabled in the cp0 status register.
- */
-1: lw t0,(TOFF_FPU+256)(a0)
- ldc1 $f2,(TOFF_FPU+16)(a0)
- ldc1 $f4,(TOFF_FPU+32)(a0)
- ldc1 $f6,(TOFF_FPU+48)(a0)
- ldc1 $f8,(TOFF_FPU+64)(a0)
- ldc1 $f10,(TOFF_FPU+80)(a0)
- ldc1 $f12,(TOFF_FPU+96)(a0)
- ldc1 $f14,(TOFF_FPU+112)(a0)
- ldc1 $f16,(TOFF_FPU+128)(a0)
- ldc1 $f18,(TOFF_FPU+144)(a0)
- ldc1 $f20,(TOFF_FPU+160)(a0)
- ldc1 $f22,(TOFF_FPU+176)(a0)
- ldc1 $f24,(TOFF_FPU+192)(a0)
- ldc1 $f26,(TOFF_FPU+208)(a0)
- ldc1 $f28,(TOFF_FPU+224)(a0)
- ldc1 $f30,(TOFF_FPU+240)(a0)
- ctc1 t0,$31
-
- /*
- * Restore non-scratch registers
- */
-2: lw s0,TOFF_REG16(a0)
- lw s1,TOFF_REG17(a0)
- lw s2,TOFF_REG18(a0)
- lw s3,TOFF_REG19(a0)
- lw s4,TOFF_REG20(a0)
- lw s5,TOFF_REG21(a0)
- lw s6,TOFF_REG22(a0)
- lw s7,TOFF_REG23(a0)
- lw gp,TOFF_REG28(a0)
- lw sp,TOFF_REG29(a0)
- lw fp,TOFF_REG30(a0)
- lw ra,TOFF_REG31(a0)
-
- /*
- * Restore status register
- */
- lw t0,TOFF_KSP(a0)
- sw t0,kernelsp
-
- jr ra
- mtc0 a2,CP0_STATUS # delay slot
- END(resume)
-
-/*
- * Some bits in the config register
- */
-#define CONFIG_DB (1<<4)
-#define CONFIG_IB (1<<5)
-
-/*
- * Flush instruction/data caches
- *
- * Parameters: a0 - starting address to flush
- * a1 - size of area to be flushed
- * a2 - which caches to be flushed
- *
- * FIXME: - ignores parameters in a0/a1
- * - doesn't know about second level caches
- */
- .set noreorder
- LEAF(sys_cacheflush)
- andi t1,a2,DCACHE
- beqz t1,do_icache
- li t0,KSEG0 # delay slot
-
- /*
- * Writeback data cache, even lines
- */
- li t1,CACHELINES-1
-1: cache Index_Writeback_Inv_D,0(t0)
- cache Index_Writeback_Inv_D,32(t0)
- cache Index_Writeback_Inv_D,64(t0)
- cache Index_Writeback_Inv_D,96(t0)
- cache Index_Writeback_Inv_D,128(t0)
- cache Index_Writeback_Inv_D,160(t0)
- cache Index_Writeback_Inv_D,192(t0)
- cache Index_Writeback_Inv_D,224(t0)
- cache Index_Writeback_Inv_D,256(t0)
- cache Index_Writeback_Inv_D,288(t0)
- cache Index_Writeback_Inv_D,320(t0)
- cache Index_Writeback_Inv_D,352(t0)
- cache Index_Writeback_Inv_D,384(t0)
- cache Index_Writeback_Inv_D,416(t0)
- cache Index_Writeback_Inv_D,448(t0)
- cache Index_Writeback_Inv_D,480(t0)
- addiu t0,512
- bnez t1,1b
- subu t1,1
-
- /*
- * Writeback data cache, odd lines
- * Only needed for 16 byte line size
- */
- mfc0 t1,CP0_CONFIG
- andi t1,CONFIG_DB
- bnez t1,do_icache
- li t1,CACHELINES-1
-1: cache Index_Writeback_Inv_D,16(t0)
- cache Index_Writeback_Inv_D,48(t0)
- cache Index_Writeback_Inv_D,80(t0)
- cache Index_Writeback_Inv_D,112(t0)
- cache Index_Writeback_Inv_D,144(t0)
- cache Index_Writeback_Inv_D,176(t0)
- cache Index_Writeback_Inv_D,208(t0)
- cache Index_Writeback_Inv_D,240(t0)
- cache Index_Writeback_Inv_D,272(t0)
- cache Index_Writeback_Inv_D,304(t0)
- cache Index_Writeback_Inv_D,336(t0)
- cache Index_Writeback_Inv_D,368(t0)
- cache Index_Writeback_Inv_D,400(t0)
- cache Index_Writeback_Inv_D,432(t0)
- cache Index_Writeback_Inv_D,464(t0)
- cache Index_Writeback_Inv_D,496(t0)
- addiu t0,512
- bnez t1,1b
- subu t1,1
-
-do_icache: andi t1,a2,ICACHE
- beqz t1,done
-
- /*
- * Flush instruction cache, even lines
- */
- lui t0,0x8000
- li t1,CACHELINES-1
-1: cache Index_Invalidate_I,0(t0)
- cache Index_Invalidate_I,32(t0)
- cache Index_Invalidate_I,64(t0)
- cache Index_Invalidate_I,96(t0)
- cache Index_Invalidate_I,128(t0)
- cache Index_Invalidate_I,160(t0)
- cache Index_Invalidate_I,192(t0)
- cache Index_Invalidate_I,224(t0)
- cache Index_Invalidate_I,256(t0)
- cache Index_Invalidate_I,288(t0)
- cache Index_Invalidate_I,320(t0)
- cache Index_Invalidate_I,352(t0)
- cache Index_Invalidate_I,384(t0)
- cache Index_Invalidate_I,416(t0)
- cache Index_Invalidate_I,448(t0)
- cache Index_Invalidate_I,480(t0)
- addiu t0,512
- bnez t1,1b
- subu t1,1
-
- /*
- * Flush instruction cache, even lines
- * Only needed for 16 byte line size
- */
- mfc0 t1,CP0_CONFIG
- andi t1,CONFIG_IB
- bnez t1,done
- li t1,CACHELINES-1
-1: cache Index_Invalidate_I,16(t0)
- cache Index_Invalidate_I,48(t0)
- cache Index_Invalidate_I,80(t0)
- cache Index_Invalidate_I,112(t0)
- cache Index_Invalidate_I,144(t0)
- cache Index_Invalidate_I,176(t0)
- cache Index_Invalidate_I,208(t0)
- cache Index_Invalidate_I,240(t0)
- cache Index_Invalidate_I,272(t0)
- cache Index_Invalidate_I,304(t0)
- cache Index_Invalidate_I,336(t0)
- cache Index_Invalidate_I,368(t0)
- cache Index_Invalidate_I,400(t0)
- cache Index_Invalidate_I,432(t0)
- cache Index_Invalidate_I,464(t0)
- cache Index_Invalidate_I,496(t0)
- addiu t0,512
- bnez t1,1b
- subu t1,1
-
-done: j ra
- nop
- END(sys_cacheflush)
diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c
index f5037fbd7..30304abda 100644
--- a/arch/mips/kernel/setup.c
+++ b/arch/mips/kernel/setup.c
@@ -2,10 +2,12 @@
* linux/arch/mips/kernel/setup.c
*
* Copyright (C) 1995 Linus Torvalds
- * Copyright (C) 1995 Ralf Baechle
+ * Copyright (C) 1995, 1996 Ralf Baechle
+ * Copyright (C) 1996 Stoned Elipot
*/
-
+#include <linux/config.h>
#include <linux/errno.h>
+#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
@@ -14,15 +16,20 @@
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/malloc.h>
-#include <linux/ldt.h>
#include <linux/user.h>
+#include <linux/utsname.h>
#include <linux/a.out.h>
#include <linux/tty.h>
+#ifdef CONFIG_BLK_DEV_RAM
+#include <linux/blk.h>
+#endif
#include <asm/asm.h>
#include <asm/bootinfo.h>
+#include <asm/cache.h>
+#include <asm/io.h>
#include <asm/vector.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/stackframe.h>
#include <asm/system.h>
@@ -31,177 +38,256 @@
*/
struct feature *feature;
-#ifdef CONFIG_ACER_PICA_61
-void acer_pica_61_handle_int(void);
-struct feature acer_pica_61_feature = {
- acer_pica_61_handle_int
-};
-#endif
-#ifdef CONFIG_DECSTATION
-void decstation_handle_handle_int(void);
-struct feature decstation_feature = {
- decstation_handle_handle_int
-};
-#endif
-#ifdef CONFIG_DESKSTATION_RPC44
-void deskstation_rpc44_handle_int(void);
-struct feature deskstation_rpc44_feature = {
- deskstation_rpc44_handle_int
-};
-#endif
-#ifdef CONFIG_DESKSTATION_TYNE
-void deskstation_tyne_handle_int(void);
-struct feature deskstation_tyne_feature = {
- deskstation_tyne_handle_int
-};
-#endif
-#ifdef CONFIG_MIPS_MAGNUM_4000
-void mips_magnum_4000_handle_int(void);
-struct feature mips_magnum_4000_feature = {
- mips_magnum_4000_handle_int
-};
-#endif
+/*
+ * What to do to keep the caches consistent with memory
+ * We don't use the normal cacheflush routine to keep Tyne caches happier.
+ */
+void (*fd_cacheflush)(const void *addr, size_t size);
/*
- * Tell us the machine setup..
+ * Not all of the MIPS CPUs have the "wait" instruction available. This
+ * is set to true if it is available. The wait instruction stops the
+ * pipeline and reduces the power consumption of the CPU very much.
*/
-char wait_available; /* set if the "wait" instruction available */
+char wait_available;
/*
- * Bus types ..
+ * There are several bus types available for MIPS machines. "RISC PC"
+ * type machines have ISA, EISA or PCI available, some DECstations have
+ * Turbochannel, SGI has GIO, there are lots of VME boxes ...
+ * This flag is set if a EISA slots are available.
*/
int EISA_bus = 0;
/*
- * Setup options
+ * Do a hardware reset.
*/
-struct drive_info_struct drive_info;
-struct screen_info screen_info = SCREEN_INFO;
+void (*hard_reset_now)(void);
+
+/*
+ * Milo passes some information to the kernel that looks like as if it
+ * had been returned by a Intel PC BIOS. Milo doesn't fill the passed
+ * drive_info and Linux can find out about this anyway, so I'm going to
+ * remove this sometime. screen_info contains information about the
+ * resolution of the text screen. For VGA graphics based machine this
+ * information is being use to continue the screen output just below
+ * the BIOS printed text and with the same text resolution.
+ */
+struct drive_info_struct drive_info = DEFAULT_DRIVE_INFO;
+struct screen_info screen_info = DEFAULT_SCREEN_INFO;
+
+/*
+ * setup informations
+ *
+ * These are intialized so they are in the .data section
+ */
+unsigned long mips_memory_upper = KSEG0; /* this is set by kernel_entry() */
+unsigned long mips_cputype = CPU_UNKNOWN;
+unsigned long mips_machtype = MACH_UNKNOWN; /* this is set by bi_EarlySnarf() */
+unsigned long mips_machgroup = MACH_GROUP_UNKNOWN; /* this is set by bi_EarlySnarf() */
+unsigned long mips_tlb_entries = 48; /* this is set by bi_EarlySnarf() */
+unsigned long mips_vram_base = KSEG0;
-unsigned char aux_device_present;
-extern int ramdisk_size;
extern int root_mountflags;
extern int _end;
extern char empty_zero_page[PAGE_SIZE];
/*
- * Initialise this structure so that it will be placed in the
- * .data section of the object file
- */
-struct bootinfo boot_info = BOOT_INFO;
-
-/*
* This is set up by the setup-routine at boot-time
*/
#define PARAM empty_zero_page
-#if 0
-#define ORIG_ROOT_DEV (*(unsigned short *) (PARAM+0x1FC))
-#define AUX_DEVICE_INFO (*(unsigned char *) (PARAM+0x1FF))
-#endif
+#define LOADER_TYPE (*(unsigned char *) (PARAM+0x210))
+#define INITRD_START (*(unsigned long *) (PARAM+0x218))
+#define INITRD_SIZE (*(unsigned long *) (PARAM+0x21c))
static char command_line[CL_SIZE] = { 0, };
+ char saved_command_line[CL_SIZE];
-#if 0
/*
- * Code for easy access to new style bootinfo
- *
- * Parameter: tag -- taglist entry
- *
- * returns : (tag *) -- pointer to taglist entry, NULL for not found
+ * The board specific setup routine sets irq_setup to point to a board
+ * specific setup routine.
*/
-tag *
-bi_TagFind(enum bi_tag tag)
+void (*irq_setup)(void);
+
+static void default_irq_setup(void)
{
- /* TBD */
- return 0;
+ panic("Unknown machtype in init_IRQ");
}
-/*
- * Only for taglist creators (bootloaders)
- *
- * Parameter: tag -- (enum bi_tag) taglist entry
- *
- * returns : 1 -- success
- * 0 -- failure
- */
-int
-bi_TagAdd(enum bi_tag tag, unsigned long size, void *tagdata)
+static void default_fd_cacheflush(const void *addr, size_t size)
{
- /* TBD */
- return 0;
}
-#endif /* 0 */
-void setup_arch(char **cmdline_p,
- unsigned long * memory_start_p, unsigned long * memory_end_p)
+static asmlinkage void
+default_cacheflush(unsigned long addr, unsigned long nbytes, unsigned int flags)
{
- unsigned long memory_start, memory_end;
+ /*
+ * Someone didn't set his cacheflush() handler ...
+ */
+ panic("default_cacheflush() called.\n");
+}
+asmlinkage void (*cacheflush)(unsigned long addr, unsigned long nbytes, unsigned int flags) = default_cacheflush;
- switch(boot_info.machtype)
- {
-#ifdef CONFIG_ACER_PICA_61
- case MACH_ACER_PICA_61:
- feature = &acer_pica_61_feature;
+static __inline__ void
+cpu_init(void)
+{
+ asmlinkage void handle_reserved(void);
+ void mips1_cpu_init(void);
+ void mips2_cpu_init(void);
+ void mips3_cpu_init(void);
+ void mips4_cpu_init(void);
+ int i;
+
+ /*
+ * Setup default vectors
+ */
+ for (i=0;i<=31;i++)
+ set_except_vector(i, handle_reserved);
+
+ switch(mips_cputype) {
+#ifdef CONFIG_CPU_R3000
+ case CPU_R2000: case CPU_R3000: case CPU_R3000A: case CPU_R3041:
+ case CPU_R3051: case CPU_R3052: case CPU_R3081: case CPU_R3081E:
+ mips1_cpu_init();
+ break;
+#endif
+#ifdef CONFIG_CPU_R6000
+ case CPU_R6000: case CPU_R6000A:
+ mips2_cpu_init();
break;
#endif
-#ifdef CONFIG_DECSTATION
- case MACH_DECSTATION:
- feature = &decstation_feature;
+#ifdef CONFIG_CPU_R4X00
+ case CPU_R4000MC: case CPU_R4400MC: case CPU_R4000SC:
+ case CPU_R4400SC: case CPU_R4000PC: case CPU_R4400PC:
+ case CPU_R4200: case CPU_R4300: /* case CPU_R4640: */
+ case CPU_R4600: case CPU_R4700:
+ mips3_cpu_init();
break;
#endif
-#ifdef CONFIG_DESKSTATION_RPC
- case MACH_DESKSTATION_RPC:
- feature = &deskstation_rpc44_feature;
+#ifdef CONFIG_CPU_R8000
+ case CPU_R8000: case CPU_R5000:
+ printk("Detected unsupported CPU type %s.\n",
+ cpu_names[mips_cputype]);
+ panic("Can't handle CPU");
break;
#endif
-#ifdef CONFIG_DESKSTATION_TYNE
- case MACH_DESKSTATION_TYNE:
- feature = &deskstation_tyne_feature;
+#ifdef CONFIG_CPU_R10000
+ case CPU_R10000:
+ mips4_cpu_init();
+#endif
+ case CPU_UNKNOWN:
+ default:
+ panic("Unknown or unsupported CPU type");
+ }
+}
+
+void setup_arch(char **cmdline_p,
+ unsigned long * memory_start_p, unsigned long * memory_end_p)
+{
+ unsigned long memory_end;
+ tag* atag;
+ void decstation_setup(void);
+ void deskstation_setup(void);
+ void jazz_setup(void);
+ void sni_rm200_pci_setup(void);
+
+ /* Perhaps a lot of tags are not getting 'snarfed' - */
+ /* please help yourself */
+
+ atag = bi_TagFind(tag_cputype);
+ memcpy(&mips_cputype, TAGVALPTR(atag), atag->size);
+
+ cpu_init();
+
+ atag = bi_TagFind(tag_vram_base);
+ memcpy(&mips_vram_base, TAGVALPTR(atag), atag->size);
+
+ irq_setup = default_irq_setup;
+ fd_cacheflush = default_fd_cacheflush;
+
+ switch(mips_machgroup)
+ {
+#ifdef CONFIG_MIPS_DECSTATION
+ case MACH_GROUP_DEC:
+ decstation_setup();
break;
#endif
-#ifdef CONFIG_MIPS_MAGNUM_4000
- case MACH_MIPS_MAGNUM_4000:
- feature = &mips_magnum_4000_feature;
+#if defined(CONFIG_MIPS_ARC)
+/* Perhaps arch/mips/deskstation should be renommed arch/mips/arc.
+ * For now CONFIG_MIPS_ARC means DeskStation. -Stoned.
+ */
+ case MACH_GROUP_ARC:
+ deskstation_setup();
+ break;
+#endif
+#ifdef CONFIG_MIPS_JAZZ
+ case MACH_GROUP_JAZZ:
+ jazz_setup();
+ break;
+#endif
+#ifdef CONFIG_SNI_RM200_PCI
+ case MACH_GROUP_SNI_RM:
+ sni_rm200_pci_setup();
break;
#endif
default:
panic("Unsupported architecture");
}
-#if 0
- ROOT_DEV = ORIG_ROOT_DEV;
-#else
- ROOT_DEV = 0x021c; /* fd0H1440 */
-/* ROOT_DEV = 0x0101; */ /* ram */
-/* ROOT_DEV = 0x00ff; */ /* NFS */
+ atag = bi_TagFind(tag_drive_info);
+ memcpy(&drive_info, TAGVALPTR(atag), atag->size);
+
+ memory_end = mips_memory_upper;
+ /*
+ * Due to prefetching and similar mechanism the CPU sometimes
+ * generates addresses beyond the end of memory. We leave the size
+ * of one cache line at the end of memory unused to make shure we
+ * don't catch this type of bus errors.
+ */
+ memory_end -= 32;
+ memory_end &= PAGE_MASK;
+
+#ifdef CONFIG_BLK_DEV_RAM
+ rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK;
+ rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0);
+ rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0);
#endif
- memcpy(&drive_info, &boot_info.drive_info, sizeof(drive_info));
-#if 0
- aux_device_present = AUX_DEVICE_INFO;
+#ifdef CONFIG_MAX_16M
+ /*
+ * There is a quite large number of different PC chipset based boards
+ * available and so I include this option here just in case ...
+ */
+ if (memory_end > PAGE_OFFSET + 16*1024*1024)
+ memory_end = PAGE_OFFSET + 16*1024*1024;
#endif
- memory_end = boot_info.memupper;
- memory_end &= PAGE_MASK;
- ramdisk_size = boot_info.ramdisk_size;
- if (boot_info.mount_root_rdonly)
- root_mountflags |= MS_RDONLY;
- memory_start = (unsigned long) &_end;
- memory_start += (ramdisk_size << 10);
+ atag= bi_TagFind(tag_screen_info);
+ if (atag)
+ memcpy(&screen_info, TAGVALPTR(atag), atag->size);
+
+ atag = bi_TagFind(tag_command_line);
+ if (atag)
+ memcpy(&command_line, TAGVALPTR(atag), atag->size);
+ printk("Command line: '%s'\n", command_line);
+
+ memcpy(saved_command_line, command_line, CL_SIZE);
+ saved_command_line[CL_SIZE-1] = '\0';
*cmdline_p = command_line;
- *memory_start_p = memory_start;
+ *memory_start_p = (unsigned long) &_end;
*memory_end_p = memory_end;
-#if 0
- /*
- * Check that struct pt_regs is defined properly
- * (Should be optimized away, but gcc 2.6.3 is too bad..)
- */
- if (FR_SIZE != sizeof(struct pt_regs) ||
- FR_SIZE & 7)
- {
- panic("Check_definition_of_struct_pt_regs\n");
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (LOADER_TYPE) {
+ initrd_start = INITRD_START ? INITRD_START + PAGE_OFFSET : 0;
+ initrd_end = initrd_start+INITRD_SIZE;
+ if (initrd_end > memory_end) {
+ printk("initrd extends beyond end of memory "
+ "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
+ initrd_end,memory_end);
+ initrd_start = 0;
+ }
}
#endif
}
diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c
index ea00551a9..b744823b1 100644
--- a/arch/mips/kernel/signal.c
+++ b/arch/mips/kernel/signal.c
@@ -2,8 +2,8 @@
* linux/arch/mips/kernel/signal.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 1994, 1995, 1996 Ralf Baechle
*/
-
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/kernel.h>
@@ -13,158 +13,305 @@
#include <linux/ptrace.h>
#include <linux/unistd.h>
-#include <asm/segment.h>
-#include <asm/cachectl.h>
+#include <asm/asm.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/cache.h>
+#include <asm/mipsconfig.h>
+#include <asm/sgidefs.h>
+
+/*
+ * Linux/MIPS misstreats the SA_NOMASK flag for signal handlers.
+ * Actually this is a bug in libc that was made visible by the POSIX.1
+ * changes in Linux/MIPS 2.0.1. To keep old binaries alive enable
+ * this define but note that this is just a hack with sideeffects, not a
+ * perfect compatibility mode. This will go away, so rebuild your
+ * executables with libc 960709 or newer.
+ */
+#define CONF_NOMASK_BUG_COMPAT
#define _S(nr) (1<<((nr)-1))
#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options);
+asmlinkage int do_signal(unsigned long oldmask, struct pt_regs *regs);
+
+asmlinkage void (*save_fp_context)(struct sigcontext *sc);
+extern asmlinkage void (*restore_fp_context)(struct sigcontext *sc);
+
+asmlinkage int sys_sigprocmask(int how, sigset_t *set, sigset_t *oset)
+{
+ k_sigset_t new_set, old_set = current->blocked;
+
+ if (set) {
+ if (!access_ok(VERIFY_READ, set, sizeof(sigset_t)))
+ return -EFAULT;
+
+ __get_user(new_set, to_k_sigset_t(set));
+ new_set &= _BLOCKABLE;
+
+ switch (how) {
+ case SIG_BLOCK:
+ current->blocked |= new_set;
+ break;
+ case SIG_UNBLOCK:
+ current->blocked &= ~new_set;
+ break;
+ case SIG_SETMASK:
+ current->blocked = new_set;
+ break;
+ /*
+ * SGI goodie: Just set the low 32 bits of 'blocked' even
+ * for 128 bit sigset_t.
+ */
+ case SIG_SETMASK32:
+ current->blocked = new_set;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ if (oset) {
+ if(!access_ok(VERIFY_WRITE, oset, sizeof(sigset_t)))
+ return -EFAULT;
+ __put_user(old_set, &oset->__sigbits[0]);
+ __put_user(0, &oset->__sigbits[1]);
+ __put_user(0, &oset->__sigbits[2]);
+ __put_user(0, &oset->__sigbits[3]);
+ }
+
+ return 0;
+}
/*
- * atomically swap in the new signal mask, and wait for a signal.
+ * Atomically swap in the new signal mask, and wait for a signal.
*/
-asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, unsigned long set)
+asmlinkage int sys_sigsuspend(struct pt_regs *regs)
{
- unsigned long mask;
- struct pt_regs * regs = (struct pt_regs *) &restart;
+ unsigned int mask;
+ sigset_t *uset;
+ k_sigset_t kset;
mask = current->blocked;
- current->blocked = set & _BLOCKABLE;
- regs->reg2 = -EINTR;
+ uset = (sigset_t *)(long) regs->regs[4];
+ if (!access_ok(VERIFY_READ, uset, sizeof(sigset_t)))
+ return -EFAULT;
+
+ __get_user(kset, to_k_sigset_t(uset));
+
+ current->blocked = kset & _BLOCKABLE;
+ regs->regs[2] = -EINTR;
while (1) {
current->state = TASK_INTERRUPTIBLE;
schedule();
- if (do_signal(mask,regs))
+ if (do_signal(mask, regs))
return -EINTR;
}
+
+ return -EINTR;
}
-/*
- * This sets regs->reg29 even though we don't actually use sigstacks yet..
- */
-asmlinkage int sys_sigreturn(unsigned long __unused)
+asmlinkage int sys_sigreturn(struct pt_regs *regs)
{
- struct sigcontext_struct context;
- struct pt_regs * regs;
+ struct sigcontext *context;
+ int i;
- regs = (struct pt_regs *) &__unused;
- if (verify_area(VERIFY_READ, (void *) regs->reg29, sizeof(context)))
+ /*
+ * We don't support fixing ADEL/ADES exceptions for signal stack frames.
+ * No big loss - who doesn't care about the alignment of this stack
+ * really deserves to loose.
+ */
+ context = (struct sigcontext *)(long) regs->regs[29];
+ if (!access_ok(VERIFY_READ, context, sizeof(struct sigcontext)) ||
+ (regs->regs[29] & (SZREG - 1)))
goto badframe;
- memcpy_fromfs(&context,(void *) regs->reg29, sizeof(context));
- current->blocked = context.oldmask & _BLOCKABLE;
- regs->reg1 = context.sc_at;
- regs->reg2 = context.sc_v0;
- regs->reg3 = context.sc_v1;
- regs->reg4 = context.sc_a0;
- regs->reg5 = context.sc_a1;
- regs->reg6 = context.sc_a2;
- regs->reg7 = context.sc_a3;
- regs->reg8 = context.sc_t0;
- regs->reg9 = context.sc_t1;
- regs->reg10 = context.sc_t2;
- regs->reg11 = context.sc_t3;
- regs->reg12 = context.sc_t4;
- regs->reg13 = context.sc_t5;
- regs->reg14 = context.sc_t6;
- regs->reg15 = context.sc_t7;
- regs->reg16 = context.sc_s0;
- regs->reg17 = context.sc_s1;
- regs->reg18 = context.sc_s2;
- regs->reg19 = context.sc_s3;
- regs->reg20 = context.sc_s4;
- regs->reg21 = context.sc_s5;
- regs->reg22 = context.sc_s6;
- regs->reg23 = context.sc_s7;
- regs->reg24 = context.sc_t8;
- regs->reg25 = context.sc_t9;
+
+ current->blocked = context->sc_sigset.__sigbits[0] & _BLOCKABLE;
+ regs->cp0_epc = context->sc_pc;
+#if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2)
+ for(i = 31;i >= 0;i--)
+ __get_user(regs->regs[i], &context->sc_regs[i]);
+#endif
+#if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4)
/*
- * Skip k0/k1
+ * We only allow user processes in 64bit mode (n32, 64 bit ABI) to
+ * restore the upper half of registers.
*/
- regs->reg28 = context.sc_gp;
- regs->reg29 = context.sc_sp;
- regs->reg30 = context.sc_fp;
- regs->reg31 = context.sc_ra;
- regs->cp0_epc = context.sc_epc;
- regs->cp0_cause = context.sc_cause;
+ if (read_32bit_cp0_register(CP0_STATUS) & ST0_UX)
+ for(i = 31;i >= 0;i--)
+ __get_user(regs->regs[i], &context->sc_regs[i]);
+ else
+ for(i = 31;i >= 0;i--) {
+ __get_user(regs->regs[i], &context->sc_regs[i]);
+ regs->regs[i] = (int) regs->regs[i];
+ }
+#endif
+ __get_user(regs->hi, &context->sc_mdhi);
+ __get_user(regs->lo, &context->sc_mdlo);
+ restore_fp_context(context);
/*
- * disable syscall checks
+ * Disable syscall checks
*/
regs->orig_reg2 = -1;
- return regs->orig_reg2;
+
+ /*
+ * Don't let your children do this ...
+ */
+ asm( "move\t$29,%0\n\t"
+ "j\tret_from_sys_call"
+ :/* no outputs */
+ :"r" (regs));
+ /* Unreached */
+
badframe:
do_exit(SIGSEGV);
}
/*
* Set up a signal frame...
+ *
+ * This routine is somewhat complicated by the fact that if the kernel may be
+ * entered by an exception other than a system call; e.g. a bus error or other
+ * "bad" exception. If this is the case, then *all* the context on the kernel
+ * stack frame must be saved.
+ *
+ * For a large number of exceptions, the stack frame format is the same
+ * as that which will be created when the process traps back to the kernel
+ * when finished executing the signal handler. In this case, nothing
+ * must be done. This information is saved on the user stack and restored
+ * when the signal handler is returned.
+ *
+ * The signal handler will be called with ra pointing to code1 (see below) and
+ * signal number and pointer to the saved sigcontext as the two parameters.
+ *
+ * usp -> [unused] ; first free word on stack
+ * arg save space ; 16/32 bytes arg. save space
+ * code1 (addiu sp,#1-offset) ; pop stackframe
+ * code2 (li v0,__NR_sigreturn) ; syscall number
+ * code3 (syscall) ; do sigreturn(2)
+ * #1| $0, at, v0, v1, a0, a1, a2, a3 ; All integer registers
+ * | t0, t1, t2, t3, t4, t5, t6, t7 ; $0, k0 and k1 are placeholders
+ * | s0, s1, s2, s3, s4, s5, s6, s7
+ * | k0, k1, t8, t9, gp, sp, fp, ra;
+ * | epc ; old program counter
+ * | cause ; CP0 cause register
+ * | oldmask
*/
-static void setup_frame(struct sigaction * sa, unsigned long ** fp,
- unsigned long pc, struct pt_regs *regs,
+
+struct sc {
+ unsigned long ass[4];
+ unsigned int code[4];
+ struct sigcontext scc;
+};
+#define scc_offset ((size_t)&((struct sc *)0)->scc)
+
+static void setup_frame(struct sigaction * sa, struct pt_regs *regs,
int signr, unsigned long oldmask)
{
- unsigned long * frame;
+ struct sc *frame;
+ struct sigcontext *sc;
+ int i;
+
+ frame = (struct sc *) (long) regs->regs[29];
+ frame--;
- frame = *fp;
- frame -= 32;
- if (verify_area(VERIFY_WRITE,frame,21*4))
- do_exit(SIGSEGV);
- /*
- * set up the "normal" stack seen by the signal handler
- */
- put_fs_long(regs->reg1 , frame );
- put_fs_long(regs->reg2 , frame+ 1);
- put_fs_long(regs->reg3 , frame+ 2);
- put_fs_long(regs->reg4 , frame+ 3);
- put_fs_long(regs->reg5 , frame+ 4);
- put_fs_long(regs->reg6 , frame+ 5);
- put_fs_long(regs->reg7 , frame+ 6);
- put_fs_long(regs->reg8 , frame+ 7);
- put_fs_long(regs->reg9 , frame+ 8);
- put_fs_long(regs->reg10, frame+ 9);
- put_fs_long(regs->reg11, frame+10);
- put_fs_long(regs->reg12, frame+11);
- put_fs_long(regs->reg13, frame+12);
- put_fs_long(regs->reg14, frame+13);
- put_fs_long(regs->reg15, frame+14);
- put_fs_long(regs->reg16, frame+15);
- put_fs_long(regs->reg17, frame+16);
- put_fs_long(regs->reg18, frame+17);
- put_fs_long(regs->reg19, frame+18);
- put_fs_long(regs->reg20, frame+19);
- put_fs_long(regs->reg21, frame+20);
- put_fs_long(regs->reg22, frame+21);
- put_fs_long(regs->reg23, frame+22);
- put_fs_long(regs->reg24, frame+23);
- put_fs_long(regs->reg25, frame+24);
/*
- * Don't copy k0/k1
+ * We realign the stack to an adequate boundary for the architecture.
+ * The assignment to sc had to be moved over the if to prevent
+ * GCC from throwing warnings.
*/
- put_fs_long(regs->reg28, frame+25);
- put_fs_long(regs->reg29, frame+26);
- put_fs_long(regs->reg30, frame+27);
- put_fs_long(regs->reg31, frame+28);
- put_fs_long(pc , frame+29);
- put_fs_long(oldmask , frame+30);
+ frame = (struct sc *)((unsigned long)frame & ALMASK);
+ sc = &frame->scc;
+ if (!access_ok(VERIFY_WRITE, frame, sizeof (struct sc))) {
+ do_exit(SIGSEGV);
+ return;
+ }
+
/*
- * set up the return code...
+ * Set up the return code ...
*
* .set noreorder
- * .set noat
- * syscall
+ * addiu sp,24
* li v0,__NR_sigreturn
- * .set at
+ * syscall
* .set reorder
*/
- put_fs_long(0x24020077, frame+31); /* li $2,119 */
- put_fs_long(0x000000c0, frame+32); /* syscall */
- *fp = frame;
+ __put_user(0x27bd0000 + scc_offset, &frame->code[0]);
+ __put_user(0x24020000 + __NR_sigreturn, &frame->code[1]);
+ __put_user(0x0000000c, &frame->code[2]);
+
+ /*
+ * Flush caches so that the instructions will be correctly executed.
+ */
+ cacheflush((unsigned long)frame->code, sizeof (frame->code),
+ CF_BCACHE|CF_ALL);
+
/*
- * Flush caches so the instructions will be correctly executed.
+ * Set up the "normal" sigcontext
*/
- sys_cacheflush(frame, 32*4, BCACHE);
+ sc->sc_pc = regs->cp0_epc; /* Program counter */
+ sc->sc_status = regs->cp0_status; /* Status register */
+ for(i = 31;i >= 0;i--)
+ __put_user(regs->regs[i], &sc->sc_regs[i]);
+ save_fp_context(sc);
+ __put_user(regs->hi, &sc->sc_mdhi);
+ __put_user(regs->lo, &sc->sc_mdlo);
+ __put_user(regs->cp0_cause, &sc->sc_cause);
+ __put_user((regs->cp0_status & ST0_CU0) != 0, &sc->sc_ownedfp);
+ __put_user(oldmask, &sc->sc_sigset.__sigbits[0]);
+ __put_user(0, &sc->sc_sigset.__sigbits[1]);
+ __put_user(0, &sc->sc_sigset.__sigbits[2]);
+ __put_user(0, &sc->sc_sigset.__sigbits[3]);
+
+ regs->regs[4] = signr; /* Args for handler */
+ regs->regs[5] = (long) frame; /* Ptr to sigcontext */
+ regs->regs[29] = (unsigned long) frame; /* Stack pointer */
+ regs->regs[31] = (unsigned long) frame->code; /* Return address */
+ regs->cp0_epc = regs->regs[25] /* "return" to the first handler */
+ = (unsigned long) sa->sa_handler;
+}
+
+/*
+ * OK, we're invoking a handler
+ */
+static inline void
+handle_signal(unsigned long signr, struct sigaction *sa,
+ unsigned long oldmask, struct pt_regs * regs)
+{
+ /* are we from a failed system call? */
+ if (regs->orig_reg2 >= 0 && regs->regs[7]) {
+ /* If so, check system call restarting.. */
+ switch (regs->regs[2]) {
+ case ERESTARTNOHAND:
+ regs->regs[2] = EINTR;
+ break;
+
+ case ERESTARTSYS:
+ if (!(sa->sa_flags & SA_RESTART)) {
+ regs->regs[2] = EINTR;
+ break;
+ }
+ /* fallthrough */
+ case ERESTARTNOINTR:
+ regs->regs[7] = regs->orig_reg7;
+ regs->cp0_epc -= 8;
+ }
+ }
+
+ /* set up the stack frame */
+ setup_frame(sa, regs, signr, oldmask);
+
+ if (sa->sa_flags & SA_ONESHOT)
+ sa->sa_handler = NULL;
+#ifdef CONF_NOMASK_BUG_COMPAT
+ current->blocked |= *to_k_sigset_t(&sa->sa_mask);
+#else
+ if (!(sa->sa_flags & SA_NOMASK))
+ current->blocked |= (*to_k_sigset_t(&sa->sa_mask) |
+ _S(signr)) & _BLOCKABLE;
+#endif
}
/*
@@ -179,16 +326,13 @@ static void setup_frame(struct sigaction * sa, unsigned long ** fp,
asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs)
{
unsigned long mask = ~current->blocked;
- unsigned long handler_signal = 0;
- unsigned long *frame = NULL;
- unsigned long pc = 0;
unsigned long signr;
struct sigaction * sa;
while ((signr = current->signal & mask)) {
signr = ffz(~signr);
clear_bit(signr, &current->signal);
- sa = current->sigaction + signr;
+ sa = current->sig->action + signr;
signr++;
if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
current->exit_code = signr;
@@ -204,7 +348,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs)
current->signal |= _S(signr);
continue;
}
- sa = current->sigaction + signr - 1;
+ sa = current->sig->action + signr - 1;
}
if (sa->sa_handler == SIG_IGN) {
if (signr != SIGCHLD)
@@ -221,19 +365,23 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs)
case SIGCONT: case SIGCHLD: case SIGWINCH:
continue;
- case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU:
+ case SIGTSTP: case SIGTTIN: case SIGTTOU:
+ if (is_orphaned_pgrp(current->pgrp))
+ continue;
+ case SIGSTOP:
if (current->flags & PF_PTRACED)
continue;
current->state = TASK_STOPPED;
current->exit_code = signr;
- if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags &
+ if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags &
SA_NOCLDSTOP))
notify_parent(current);
schedule();
continue;
case SIGQUIT: case SIGILL: case SIGTRAP:
- case SIGIOT: case SIGFPE: case SIGSEGV: case SIGBUS:
+ case SIGABRT: case SIGFPE: case SIGSEGV:
+ case SIGBUS:
if (current->binfmt && current->binfmt->core_dump) {
if (current->binfmt->core_dump(signr, regs))
signr |= 0x80;
@@ -241,55 +389,33 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs)
/* fall through */
default:
current->signal |= _S(signr & 0x7f);
+ current->flags |= PF_SIGNALED;
do_exit(signr);
}
}
- /*
- * OK, we're invoking a handler
- */
- if (regs->orig_reg2 >= 0) {
- if (regs->reg2 == -ERESTARTNOHAND ||
- (regs->reg2 == -ERESTARTSYS &&
- !(sa->sa_flags & SA_RESTART)))
- regs->reg2 = -EINTR;
- }
- handler_signal |= 1 << (signr-1);
- mask &= ~sa->sa_mask;
+ handle_signal(signr, sa, oldmask, regs);
+ return 1;
}
- if (regs->orig_reg2 >= 0 &&
- (regs->reg2 == -ERESTARTNOHAND ||
- regs->reg2 == -ERESTARTSYS ||
- regs->reg2 == -ERESTARTNOINTR)) {
- regs->reg2 = regs->orig_reg2;
- regs->cp0_epc -= 4;
- }
- if (!handler_signal) /* no handler will be called - return 0 */
- return 0;
- pc = regs->cp0_epc;
- frame = (unsigned long *) regs->reg29;
- signr = 1;
- sa = current->sigaction;
- for (mask = 1 ; mask ; sa++,signr++,mask += mask) {
- if (mask > handler_signal)
- break;
- if (!(mask & handler_signal))
- continue;
- setup_frame(sa,&frame,pc,regs,signr,oldmask);
- pc = (unsigned long) sa->sa_handler;
- if (sa->sa_flags & SA_ONESHOT)
- sa->sa_handler = NULL;
- /*
- * force a kernel-mode page-in of the signal
- * handler to reduce races
- */
- __asm__("lw\t$0,(%0)"
- : /* no output */
- :"r" ((char *) pc));
- current->blocked |= sa->sa_mask;
- oldmask |= sa->sa_mask;
+
+ /* Did we come from a system call? */
+ if (regs->orig_reg2 >= 0) {
+ /* Restart the system call - no handlers present */
+ if (regs->regs[2] == -ERESTARTNOHAND ||
+ regs->regs[2] == -ERESTARTSYS ||
+ regs->regs[2] == -ERESTARTNOINTR) {
+ regs->regs[2] = regs->orig_reg2;
+ regs->cp0_epc -= 8;
+ }
}
- regs->reg29 = (unsigned long) frame;
- regs->cp0_epc = pc; /* "return" to the first handler */
+ return 0;
+}
- return 1;
+/*
+ * The signal(2) syscall is no longer available in the kernel
+ * because GNU libc doesn't use it. Maybe I'll add it back to the
+ * kernel for the binary compatibility stuff.
+ */
+asmlinkage unsigned long sys_signal(int signum, __sighandler_t handler)
+{
+ return -ENOSYS;
}
diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c
new file mode 100644
index 000000000..144a3c905
--- /dev/null
+++ b/arch/mips/kernel/syscall.c
@@ -0,0 +1,280 @@
+/*
+ * MIPS specific syscall handling functions and syscalls
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1995, 1996 by Ralf Baechle
+ *
+ * TODO: Implement the compatibility syscalls.
+ * Don't waste that much memory for empty entries in the syscall
+ * table.
+ */
+#undef CONF_PRINT_SYSCALLS
+
+#include <linux/linkage.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/sched.h>
+#include <linux/unistd.h>
+#include <asm/branch.h>
+#include <asm/ptrace.h>
+#include <asm/uaccess.h>
+#include <asm/signal.h>
+
+extern asmlinkage void syscall_trace(void);
+typedef asmlinkage int (*syscall_t)(void *a0,...);
+extern asmlinkage int do_syscalls(struct pt_regs *regs, syscall_t fun,
+ int narg);
+extern syscall_t sys_call_table[];
+extern unsigned char sys_narg_table[];
+
+/*
+ * The pipe syscall has a unusual calling convention. We return the two
+ * filedescriptors in the result registers v0/v1. The syscall wrapper
+ * from libc places these results in the array to which the argument of
+ * pipe points to. This is like other MIPS operating systems and unlike
+ * Linux/i386 where the kernel itself places the results in the file
+ * descriptor array itself. This calling convention also has the advantage
+ * of lower overhead because we don't need to call verify_area.
+ */
+asmlinkage int sys_pipe(struct pt_regs *regs)
+{
+ int fd[2];
+ int error;
+
+ error = do_pipe(fd);
+ if (error)
+ return error;
+ regs->regs[3] = fd[1];
+ return fd[0];
+}
+
+asmlinkage unsigned long sys_mmap(unsigned long addr, size_t len, int prot,
+ int flags, int fd, off_t offset)
+{
+ struct file * file = NULL;
+
+ if (!(flags & MAP_ANONYMOUS)) {
+ if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+ return -EBADF;
+ }
+ flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
+
+ return do_mmap(file, addr, len, prot, flags, offset);
+}
+
+asmlinkage int sys_idle(void)
+{
+ if (current->pid != 0)
+ return -EPERM;
+
+ /* endless idle loop with no priority at all */
+ current->counter = -100;
+ for (;;) {
+ /*
+ * Not all MIPS R-series CPUs have the wait instruction.
+ * FIXME: We should save power by reducing the clock where
+ * possible.
+ */
+ if (wait_available && !need_resched)
+ __asm__(".set\tmips3\n\t"
+ "wait\n\t"
+ ".set\tmips0\n\t");
+ schedule();
+ }
+}
+
+#if 0
+/*
+ * RISC/os compatible SysV flavoured fork(2) syscall.
+ *
+ * This call has a different calling sequence:
+ * child return value: pid of parent, secondary result = 1.
+ * parent return value: pid of child, secondary result value = 0.
+ * error: errno, secondary result = 0.
+ */
+asmlinkage int sys_sysv_fork(struct pt_regs *regs)
+{
+ int pid;
+
+ pid = do_fork(SIGCHLD, regs->regs[29], regs);
+ if (pid == 0) { /* child */
+ regs->regs[3] = 1;
+ return current->p_pptr->pid;
+ } /* parent or error */
+
+ regs->regs[3] = 0;
+ return pid;
+}
+#endif
+
+/*
+ * Normal Linux fork(2) syscall
+ */
+asmlinkage int sys_fork(struct pt_regs *regs)
+{
+ return do_fork(SIGCHLD, regs->regs[29], regs);
+}
+
+asmlinkage int sys_clone(struct pt_regs *regs)
+{
+ unsigned long clone_flags;
+ unsigned long newsp;
+
+ clone_flags = regs->regs[4];
+ newsp = regs->regs[5];
+ if (!newsp)
+ newsp = regs->regs[29];
+ return do_fork(clone_flags, newsp, regs);
+}
+
+/*
+ * sys_execve() executes a new program.
+ */
+asmlinkage int sys_execve(struct pt_regs *regs)
+{
+ int error;
+ char * filename;
+
+ error = getname((char *) (long)regs->regs[4], &filename);
+ if (error)
+ return error;
+ error = do_execve(filename, (char **) (long)regs->regs[5],
+ (char **) (long)regs->regs[6], regs);
+ putname(filename);
+
+ return error;
+}
+
+/*
+ * Do the indirect syscall syscall.
+ */
+asmlinkage int sys_syscall(unsigned long a0, unsigned long a1, unsigned long a2,
+ unsigned long a3, unsigned long a4, unsigned long a5,
+ unsigned long a6)
+{
+ syscall_t syscall;
+
+ if (a0 > __NR_Linux + __NR_Linux_syscalls)
+ return -ENOSYS;
+
+ syscall = sys_call_table[a0];
+ /*
+ * Prevent stack overflow by recursive
+ * syscall(__NR_syscall, __NR_syscall,...);
+ */
+ if (syscall == (syscall_t) sys_syscall)
+ return -EINVAL;
+
+ if (syscall == NULL)
+ return -ENOSYS;
+
+ return syscall((void *)a0, a1, a2, a3, a4, a5, a6);
+}
+
+/*
+ * If we ever come here the user sp is bad. Zap the process right away.
+ * Due to the bad stack signaling wouldn't work.
+ */
+asmlinkage void bad_stack(void)
+{
+ do_exit(SIGSEGV);
+}
+
+#ifdef CONF_PRINT_SYSCALLS
+#define SYS(fun, narg) #fun,
+static char *sfnames[] = {
+#include "syscalls.h"
+};
+#endif
+
+asmlinkage void do_sys(struct pt_regs *regs)
+{
+ unsigned long syscallnr, usp;
+ syscall_t syscall;
+ int errno, narg;
+
+ /* Skip syscall instruction */
+ if (delay_slot(regs)) {
+ /*
+ * By convention "li v0,<syscallno>" is always preceeding
+ * the syscall instruction. So if we're in a delay slot
+ * userland is screwed up.
+ */
+ force_sig(SIGILL, current);
+ return;
+ }
+ regs->cp0_epc += 4;
+
+ syscallnr = regs->regs[2];
+ if (syscallnr > (__NR_Linux + __NR_Linux_syscalls))
+ goto illegal_syscall;
+ syscall = sys_call_table[syscallnr];
+
+#ifdef CONF_PRINT_SYSCALLS
+ printk("do_sys(): %s()", sfnames[syscallnr - __NR_Linux]);
+#endif
+ narg = sys_narg_table[syscallnr];
+ if (narg > 4) {
+ /*
+ * Verify that we can safely get the additional parameters
+ * from the user stack.
+ */
+ usp = regs->regs[29];
+ if (usp & 3) {
+ printk("unaligned usp\n");
+ do_exit(SIGBUS);
+ return;
+ }
+
+ if (!access_ok(VERIFY_READ, (void *) (usp + 16),
+ (narg - 4) * sizeof(unsigned long))) {
+ errno = -EFAULT;
+ goto syscall_error;
+ }
+ }
+
+ if ((current->flags & PF_TRACESYS) == 0) {
+ errno = do_syscalls(regs, syscall, narg);
+ if (errno < 0)
+ goto syscall_error;
+
+ regs->regs[2] = errno;
+ regs->regs[7] = 0;
+ } else {
+ syscall_trace();
+
+ errno = do_syscalls(regs, syscall, narg);
+ if (errno < 0) {
+ regs->regs[2] = -errno;
+ regs->regs[7] = 1;
+ } else {
+ regs->regs[2] = errno;
+ regs->regs[7] = 0;
+ }
+
+ syscall_trace();
+ }
+#ifdef CONF_PRINT_SYSCALLS
+ printk(" returning: normal\n");
+#endif
+ return;
+
+syscall_error:
+ regs->regs[2] = -errno;
+ regs->regs[7] = 1;
+#ifdef CONF_PRINT_SYSCALLS
+ printk(" returning: syscall_error, errno=%d\n", -errno);
+#endif
+ return;
+
+illegal_syscall:
+ regs->regs[2] = ENOSYS;
+ regs->regs[7] = 1;
+#ifdef CONF_PRINT_SYSCALLS
+ printk(" returning: illegal_syscall\n");
+#endif
+ return;
+}
diff --git a/arch/mips/kernel/syscalls.h b/arch/mips/kernel/syscalls.h
new file mode 100644
index 000000000..6a398c92d
--- /dev/null
+++ b/arch/mips/kernel/syscalls.h
@@ -0,0 +1,205 @@
+/*
+ * List of Linux/MIPS syscalls.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1995, 1996 by Ralf Baechle
+ */
+
+/*
+ * This file is being included twice - once to build a list of all
+ * syscalls and once to build a table of how many arguments each syscall
+ * accepts. Syscalls that receive a pointer to the saved registers are
+ * marked as having zero arguments.
+ *
+ * The binary compatibility calls are still missing in this list.
+ */
+SYS(sys_syscall, 7) /* 4000 */
+SYS(sys_exit, 1)
+SYS(sys_fork, 0)
+SYS(sys_read, 3)
+SYS(sys_write, 3)
+SYS(sys_open, 3) /* 4005 */
+SYS(sys_close, 3)
+SYS(sys_waitpid, 3)
+SYS(sys_creat, 2)
+SYS(sys_link, 2)
+SYS(sys_unlink, 1) /* 4010 */
+SYS(sys_execve, 0)
+SYS(sys_chdir, 1)
+SYS(sys_time, 1)
+SYS(sys_mknod, 3)
+SYS(sys_chmod, 2) /* 4015 */
+SYS(sys_chown, 3)
+SYS(sys_break, 0)
+SYS(sys_stat, 2)
+SYS(sys_lseek, 3)
+SYS(sys_getpid, 0) /* 4020 */
+SYS(sys_mount, 5)
+SYS(sys_umount, 1)
+SYS(sys_setuid, 1)
+SYS(sys_getuid, 0)
+SYS(sys_stime, 1) /* 4025 */
+SYS(sys_ptrace, 4)
+SYS(sys_alarm, 1)
+SYS(sys_fstat, 2)
+SYS(sys_pause, 0)
+SYS(sys_utime, 2) /* 4030 */
+SYS(sys_stty, 0)
+SYS(sys_gtty, 0)
+SYS(sys_access, 2)
+SYS(sys_nice, 1)
+SYS(sys_ftime, 0) /* 4035 */
+SYS(sys_sync, 0)
+SYS(sys_kill, 2)
+SYS(sys_rename, 2)
+SYS(sys_mkdir, 2)
+SYS(sys_rmdir, 1) /* 4040 */
+SYS(sys_dup, 1)
+SYS(sys_pipe, 0)
+SYS(sys_times, 1)
+SYS(sys_prof, 0)
+SYS(sys_brk, 1) /* 4045 */
+SYS(sys_setgid, 1)
+SYS(sys_getgid, 0)
+SYS(sys_signal, 2)
+SYS(sys_geteuid, 0)
+SYS(sys_getegid, 0) /* 4050 */
+SYS(sys_acct, 0)
+SYS(sys_phys, 0)
+SYS(sys_lock, 0)
+SYS(sys_ioctl, 3)
+SYS(sys_fcntl, 3) /* 4055 */
+SYS(sys_mpx, 2)
+SYS(sys_setpgid, 2)
+SYS(sys_ulimit, 0)
+SYS(sys_olduname, 1)
+SYS(sys_umask, 1) /* 4060 */
+SYS(sys_chroot, 1)
+SYS(sys_ustat, 2)
+SYS(sys_dup2, 2)
+SYS(sys_getppid, 0)
+SYS(sys_getpgrp, 0) /* 4065 */
+SYS(sys_setsid, 0)
+SYS(sys_sigaction, 3)
+SYS(sys_sgetmask, 0)
+SYS(sys_ssetmask, 1)
+SYS(sys_setreuid, 2) /* 4070 */
+SYS(sys_setregid, 2)
+SYS(sys_sigsuspend, 0)
+SYS(sys_sigpending, 1)
+SYS(sys_sethostname, 2)
+SYS(sys_setrlimit, 2) /* 4075 */
+SYS(sys_getrlimit, 2)
+SYS(sys_getrusage, 2)
+SYS(sys_gettimeofday, 2)
+SYS(sys_settimeofday, 2)
+SYS(sys_getgroups, 2) /* 4080 */
+SYS(sys_setgroups, 2)
+SYS(sys_ni_syscall, 0) /* old_select */
+SYS(sys_symlink, 2)
+SYS(sys_lstat, 2)
+SYS(sys_readlink, 3) /* 4085 */
+SYS(sys_uselib, 1)
+SYS(sys_swapon, 2)
+SYS(sys_reboot, 3)
+SYS(old_readdir, 3)
+SYS(sys_mmap, 6) /* 4090 */
+SYS(sys_munmap, 2)
+SYS(sys_truncate, 2)
+SYS(sys_ftruncate, 2)
+SYS(sys_fchmod, 2)
+SYS(sys_fchown, 3) /* 4095 */
+SYS(sys_getpriority, 2)
+SYS(sys_setpriority, 3)
+SYS(sys_profil, 0)
+SYS(sys_statfs, 2)
+SYS(sys_fstatfs, 2) /* 4100 */
+SYS(sys_ioperm, 3)
+SYS(sys_socketcall, 2)
+SYS(sys_syslog, 3)
+SYS(sys_setitimer, 3)
+SYS(sys_getitimer, 2) /* 4105 */
+SYS(sys_newstat, 2)
+SYS(sys_newlstat, 2)
+SYS(sys_newfstat, 2)
+SYS(sys_uname, 1)
+SYS(sys_iopl, 0) /* Well, actually 17 args ... */ /* 4110 */
+SYS(sys_vhangup, 0)
+SYS(sys_idle, 0)
+SYS(sys_vm86, 1)
+SYS(sys_wait4, 4)
+SYS(sys_swapoff, 1) /* 4115 */
+SYS(sys_sysinfo, 1)
+SYS(sys_ipc, 6)
+SYS(sys_fsync, 1)
+SYS(sys_sigreturn, 0)
+SYS(sys_clone, 0) /* 4120 */
+SYS(sys_setdomainname, 2)
+SYS(sys_newuname, 1)
+SYS(sys_ni_syscall, 0) /* sys_modify_ldt */
+SYS(sys_adjtimex, 1)
+SYS(sys_mprotect, 3) /* 4125 */
+SYS(sys_sigprocmask, 3)
+SYS(sys_create_module, 2)
+SYS(sys_init_module, 5)
+SYS(sys_delete_module, 1)
+SYS(sys_get_kernel_syms, 1) /* 4130 */
+SYS(sys_quotactl, 0)
+SYS(sys_getpgid, 1)
+SYS(sys_fchdir, 1)
+SYS(sys_bdflush, 2)
+SYS(sys_sysfs, 3) /* 4135 */
+SYS(sys_personality, 1)
+SYS(sys_ni_syscall, 0) /* for afs_syscall */
+SYS(sys_setfsuid, 1)
+SYS(sys_setfsgid, 1)
+SYS(sys_llseek, 5) /* 4140 */
+SYS(sys_getdents, 3)
+SYS(sys_select, 5)
+SYS(sys_flock, 2)
+SYS(sys_msync, 3)
+SYS(sys_readv, 3) /* 4145 */
+SYS(sys_writev, 3)
+SYS(sys_cacheflush, 3)
+SYS(sys_cachectl, 3)
+SYS(sys_sysmips, 4)
+SYS(sys_setup, 0) /* 4150 */
+SYS(sys_getsid, 1)
+SYS(sys_fdatasync, 0)
+SYS(sys_sysctl, 1)
+SYS(sys_mlock, 2)
+SYS(sys_munlock, 2) /* 4155 */
+SYS(sys_mlockall, 1)
+SYS(sys_munlockall, 0)
+SYS(sys_sched_setparam,2)
+SYS(sys_sched_getparam,2)
+SYS(sys_sched_setscheduler,3) /* 4160 */
+SYS(sys_sched_getscheduler,1)
+SYS(sys_sched_yield,0)
+SYS(sys_sched_get_priority_max,1)
+SYS(sys_sched_get_priority_min,1)
+SYS(sys_sched_rr_get_interval,2) /* 4165 */
+SYS(sys_nanosleep,2)
+SYS(sys_mremap,4)
+SYS(sys_accept, 3)
+SYS(sys_bind, 3)
+SYS(sys_connect, 3) /* 4170 */
+SYS(sys_getpeername, 3)
+SYS(sys_getsockname, 3)
+SYS(sys_getsockopt, 5)
+SYS(sys_listen, 2)
+SYS(sys_recv, 4) /* 4175 */
+SYS(sys_recvfrom, 6)
+SYS(sys_recvmsg, 3)
+SYS(sys_send, 4)
+SYS(sys_sendmsg, 3)
+SYS(sys_sendto, 6) /* 4180 */
+SYS(sys_setsockopt, 5)
+SYS(sys_shutdown, 2)
+SYS(sys_socket, 3)
+SYS(sys_socketpair, 4)
+SYS(sys_setresuid, 3) /* 4185 */
+SYS(sys_getresuid, 3)
diff --git a/arch/mips/kernel/sysmips.c b/arch/mips/kernel/sysmips.c
new file mode 100644
index 000000000..d15aaa4dd
--- /dev/null
+++ b/arch/mips/kernel/sysmips.c
@@ -0,0 +1,113 @@
+/*
+ * MIPS specific syscalls
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1995 by Ralf Baechle
+ */
+#include <linux/errno.h>
+#include <linux/linkage.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/utsname.h>
+
+#include <asm/cachectl.h>
+#include <asm/cache.h>
+#include <asm/ipc.h>
+#include <asm/uaccess.h>
+#include <asm/sysmips.h>
+
+static inline size_t
+strnlen_user(const char *s, size_t count)
+{
+ return strnlen(s, count);
+}
+
+asmlinkage int
+sys_sysmips(int cmd, int arg1, int arg2, int arg3)
+{
+ int *p;
+ char *name;
+ int flags, tmp, len, retval;
+
+ switch(cmd)
+ {
+ case SETNAME:
+ if (!suser())
+ return -EPERM;
+ name = (char *) arg1;
+ len = strnlen_user(name, retval);
+ if (len < 0)
+ retval = len;
+ break;
+ if (len == 0 || len > __NEW_UTS_LEN)
+ retval = -EINVAL;
+ break;
+ copy_from_user(system_utsname.nodename, name, len);
+ system_utsname.nodename[len] = '\0';
+ return 0;
+
+ case MIPS_ATOMIC_SET:
+ /* This is broken in case of page faults and SMP ...
+ Risc/OS fauls after maximum 20 tries with EAGAIN. */
+ p = (int *) arg1;
+ retval = verify_area(VERIFY_WRITE, p, sizeof(*p));
+ if (retval)
+ return retval;
+ save_flags(flags);
+ cli();
+ retval = *p;
+ *p = arg2;
+ restore_flags(flags);
+ break;
+
+ case MIPS_FIXADE:
+ tmp = current->tss.mflags & ~3;
+ current->tss.mflags = tmp | (arg1 & 3);
+ retval = 0;
+ break;
+
+ case FLUSH_CACHE:
+ cacheflush(0, ~0, CF_BCACHE|CF_ALL);
+ break;
+
+ case MIPS_RDNVRAM:
+ retval = -EIO;
+ break;
+
+ default:
+ retval = -EINVAL;
+ break;
+ }
+
+ return retval;
+}
+
+asmlinkage int
+sys_cacheflush(void *addr, int nbytes, int cache)
+{
+ unsigned int rw;
+ int ok;
+
+ if ((cache & ~(DCACHE | ICACHE)) != 0)
+ return -EINVAL;
+ rw = (cache & DCACHE) ? VERIFY_WRITE : VERIFY_READ;
+ if (!access_ok(rw, addr, nbytes))
+ return -EFAULT;
+
+ cacheflush((unsigned long)addr, (unsigned long)nbytes, cache|CF_ALL);
+
+ return 0;
+}
+
+/*
+ * No implemented yet ...
+ */
+asmlinkage int
+sys_cachectl(char *addr, int nbytes, int op)
+{
+ return -ENOSYS;
+}
diff --git a/arch/mips/kernel/tags.c b/arch/mips/kernel/tags.c
new file mode 100644
index 000000000..4bf480c54
--- /dev/null
+++ b/arch/mips/kernel/tags.c
@@ -0,0 +1,70 @@
+/*
+ * linux/arch/mips/kernel/tags.c
+ *
+ * Copyright (C) 1996 Stoned Elipot
+ */
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <asm/bootinfo.h>
+
+/*
+ * Parse the tags present in upper memory to find out
+ * a pecular one.
+ *
+ * Parameter: type - tag type to find
+ *
+ * returns : NULL - failure
+ * !NULL - pointer on the tag structure found
+ */
+tag *
+bi_TagFind(enum bi_tag type)
+{
+ tag* t = (tag*)(mips_memory_upper - sizeof(tag));
+
+ while((t->tag != tag_dummy) && (t->tag != type))
+ t = (tag*)(NEXTTAGPTR(t));
+
+ if (t->tag == tag_dummy) /* tag not found */
+ return (tag*)NULL;
+
+ return t;
+}
+
+/*
+ * Snarf from the tag list in memory end some tags needed
+ * before the kernel reachs setup_arch()
+ *
+ * add yours here if you want to, but *beware*: the kernel var
+ * that will hold the values you want to snarf have to be
+ * in .data section of the kernel, so initialized in to whatever
+ * value in the kernel's sources.
+ */
+void bi_EarlySnarf(void)
+{
+ tag* atag;
+
+ /* for wire_mappings() */
+ atag = bi_TagFind(tag_machgroup);
+ if (atag)
+ memcpy(&mips_machgroup, TAGVALPTR(atag), atag->size);
+ else
+ /* useless for boxes without text video mode but....*/
+ panic("machine group not specified by bootloader");
+
+ atag = bi_TagFind(tag_machtype);
+ if (atag)
+ memcpy(&mips_machtype, TAGVALPTR(atag), atag->size);
+ else
+ /* useless for boxes without text video mode but....*/
+ panic("machine type not specified by bootloader");
+
+ /* for tlbflush() */
+ atag = bi_TagFind(tag_tlb_entries);
+ if (atag)
+ memcpy(&mips_tlb_entries, TAGVALPTR(atag), atag->size);
+ else
+ /* useless for boxes without text video mode but....*/
+ panic("number of TLB entries not specified by bootloader");
+ return;
+}
diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c
new file mode 100644
index 000000000..2dd1d54ee
--- /dev/null
+++ b/arch/mips/kernel/time.c
@@ -0,0 +1,292 @@
+/*
+ * linux/arch/mips/kernel/time.c
+ *
+ * Copyright (C) 1991, 1992, 1995 Linus Torvalds
+ *
+ * This file contains the time handling details for PC-style clocks as
+ * found in some MIPS systems.
+ */
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+#include <asm/bootinfo.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <linux/mc146818rtc.h>
+#include <linux/timex.h>
+
+/* This function must be called with interrupts disabled
+ * It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs
+ *
+ * However, the pc-audio speaker driver changes the divisor so that
+ * it gets interrupted rather more often - it loads 64 into the
+ * counter rather than 11932! This has an adverse impact on
+ * do_gettimeoffset() -- it stops working! What is also not
+ * good is that the interval that our timer function gets called
+ * is no longer 10.0002 ms, but 9.9767 ms. To get around this
+ * would require using a different timing source. Maybe someone
+ * could use the RTC - I know that this can interrupt at frequencies
+ * ranging from 8192Hz to 2Hz. If I had the energy, I'd somehow fix
+ * it so that at startup, the timer code in sched.c would select
+ * using either the RTC or the 8253 timer. The decision would be
+ * based on whether there was any other device around that needed
+ * to trample on the 8253. I'd set up the RTC to interrupt at 1024 Hz,
+ * and then do some jiggery to have a version of do_timer that
+ * advanced the clock by 1/1024 s. Every time that reached over 1/100
+ * of a second, then do all the old code. If the time was kept correct
+ * then do_gettimeoffset could just return 0 - there is no low order
+ * divider that can be accessed.
+ *
+ * Ideally, you would be able to use the RTC for the speaker driver,
+ * but it appears that the speaker driver really needs interrupt more
+ * often than every 120 us or so.
+ *
+ * Anyway, this needs more thought.... pjsg (1993-08-28)
+ *
+ * If you are really that interested, you should be reading
+ * comp.protocols.time.ntp!
+ */
+
+#define TICK_SIZE tick
+
+static unsigned long do_slow_gettimeoffset(void)
+{
+ int count;
+ unsigned long offset = 0;
+
+ /* timer count may underflow right here */
+ outb_p(0x00, 0x43); /* latch the count ASAP */
+ count = inb_p(0x40); /* read the latched count */
+ count |= inb(0x40) << 8;
+ /* we know probability of underflow is always MUCH less than 1% */
+ if (count > (LATCH - LATCH/100)) {
+ /* check for pending timer interrupt */
+ outb_p(0x0a, 0x20);
+ if (inb(0x20) & 1)
+ offset = TICK_SIZE;
+ }
+ count = ((LATCH-1) - count) * TICK_SIZE;
+ count = (count + LATCH/2) / LATCH;
+ return offset + 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_state = TIME_BAD;
+ time_maxerror = MAXPHASE;
+ time_esterror = MAXPHASE;
+ sti();
+}
+
+/*
+ * In order to set the CMOS clock precisely, set_rtc_mmss has to be
+ * called 500 ms after the second nowtime has started, because when
+ * nowtime is written into the registers of the CMOS clock, it will
+ * jump to the next second precisely 500 ms later. Check the Motorola
+ * MC146818A or Dallas DS12887 data sheet for details.
+ */
+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;
+
+ save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */
+ CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
+
+ save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */
+ CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
+
+ cmos_minutes = CMOS_READ(RTC_MINUTES);
+ if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+ 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) {
+ if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+ BIN_TO_BCD(real_seconds);
+ BIN_TO_BCD(real_minutes);
+ }
+ CMOS_WRITE(real_seconds,RTC_SECONDS);
+ CMOS_WRITE(real_minutes,RTC_MINUTES);
+ } else
+ retval = -1;
+
+ /* The following flags have to be released exactly in this order,
+ * otherwise the DS12887 (popular MC146818A clone with integrated
+ * battery and crystal) will not reset the oscillator and will not
+ * update precisely 500 ms later. You won't find this mentioned in
+ * the Dallas Semiconductor data sheets, but who believes data
+ * sheets anyway ... -- Markus Kuhn
+ */
+ CMOS_WRITE(save_control, RTC_CONTROL);
+ CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
+
+ return retval;
+}
+
+/* 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 void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+ 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_state != TIME_BAD && 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; /* do it again in 60 s */
+ /* As we return to user mode fire off the other CPU schedulers.. this is
+ basically because we don't yet share IRQ's around. This message is
+ rigged to be safe on the 386 - basically it's a hack, so don't look
+ closely for now.. */
+ smp_message_pass(MSG_ALL_BUT_SELF, MSG_RESCHEDULE, 0L, 0);
+}
+
+/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
+ * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
+ * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
+ *
+ * [For the Julian calendar (which was used in Russia before 1917,
+ * Britain & colonies before 1752, anywhere else before 1582,
+ * and is still in use by some communities) leave out the
+ * -year/100+year/400 terms, and add 10.]
+ *
+ * This algorithm was first published by Gauss (I think).
+ *
+ * WARNING: this function will overflow on 2106-02-07 06:28:16 on
+ * machines were long is 32-bit! (However, as time_t is signed, we
+ * will already get problems at other places on 2038-01-19 03:14:08)
+ */
+static inline unsigned long mktime(unsigned int year, unsigned int mon,
+ unsigned int day, unsigned int hour,
+ unsigned int min, unsigned int sec)
+{
+ if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */
+ mon += 12; /* Puts Feb last since it has leap day */
+ year -= 1;
+ }
+ return (((
+ (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
+ year*365 - 719499
+ )*24 + hour /* now have hours */
+ )*60 + min /* now have minutes */
+ )*60 + sec; /* finally seconds */
+}
+
+static struct irqaction irq0 = { timer_interrupt, 0, 0, "timer", NULL, NULL};
+
+void (*board_time_init)(struct irqaction *irq);
+
+void time_init(void)
+{
+ unsigned int year, mon, day, hour, min, sec;
+ int i;
+
+ /* The Linux interpretation of the CMOS clock register contents:
+ * When the Update-In-Progress (UIP) flag goes from 1 to 0, the
+ * RTC registers show the second which has precisely just started.
+ * Let's hope other operating systems interpret the RTC the same way.
+ */
+ /* read RTC exactly on falling edge of update flag */
+ for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */
+ if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
+ break;
+ for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */
+ if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
+ break;
+ do { /* Isn't this overkill ? UIP above should guarantee consistency */
+ 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);
+ } while (sec != CMOS_READ(RTC_SECONDS));
+ if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+ {
+ 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 0 /* the IBM way */
+ if ((year += 1900) < 1970)
+ year += 100;
+#else
+ /* true for all MIPS machines? */
+ year += 1980;
+#endif
+ xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
+ xtime.tv_usec = 0;
+
+ /* FIXME: If we have the CPU hardware time counters, use them */
+ board_time_init(&irq0);
+}
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 36e8a31e8..0fb16f1a5 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -1,39 +1,27 @@
/*
- * arch/mips/kernel/traps.c
+ * arch/mips/kernel/traps.c
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
- */
-
-/*
- * 'traps.c' handles hardware traps and faults after we have saved some
- * state in 'asm.s'. Currently mostly a debugging-aid, will be extended
- * to mainly kill the offending process (probably by giving it a signal,
- * but possibly by killing it outright if necessary).
*
- * FIXME: This is the place for a fpu emulator.
+ * Copyright (C) 1994, 1995, 1996 by Ralf Baechle
+ * Copyright (C) 1994, 1995, 1996 by Paul M. Antoine
*/
-#include <linux/head.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/signal.h>
-#include <linux/string.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/ptrace.h>
-#include <linux/config.h>
-#include <linux/timer.h>
#include <linux/mm.h>
+#include <asm/branch.h>
+#include <asm/cache.h>
+#include <asm/jazz.h>
#include <asm/vector.h>
-#include <asm/page.h>
#include <asm/pgtable.h>
-#include <asm/system.h>
-#include <asm/segment.h>
#include <asm/io.h>
-#include <asm/mipsregs.h>
#include <asm/bootinfo.h>
+#include <asm/sgidefs.h>
+#include <asm/uaccess.h>
+#include <asm/watch.h>
+
+#undef CONF_DEBUG_EXCEPTIONS
static inline void console_verbose(void)
{
@@ -63,20 +51,15 @@ extern asmlinkage void handle_ri(void);
extern asmlinkage void handle_cpu(void);
extern asmlinkage void handle_ov(void);
extern asmlinkage void handle_tr(void);
-extern asmlinkage void handle_vcei(void);
extern asmlinkage void handle_fpe(void);
-extern asmlinkage void handle_vced(void);
extern asmlinkage void handle_watch(void);
-extern asmlinkage void handle_reserved(void);
-static char *cpu_names[] = CPU_NAMES;
+char *cpu_names[] = CPU_NAMES;
-/*
- * Fix address errors. This is slow, so try not to use it. This is
- * disabled by default, anyway.
- */
-int fix_ade_enabled = 0;
-unsigned long page_colour_mask;
+unsigned int watch_available = 0;
+
+void (*ibe_board_handler)(struct pt_regs *regs);
+void (*dbe_board_handler)(struct pt_regs *regs);
int kstack_depth_to_print = 24;
@@ -86,6 +69,10 @@ int kstack_depth_to_print = 24;
*/
#define MODULE_RANGE (8*1024*1024)
+/*
+ * This routine abuses get_user()/put_user() to reference pointers
+ * with at least a bit of error checking ...
+ */
void die_if_kernel(char * str, struct pt_regs * regs, long err)
{
int i;
@@ -93,11 +80,23 @@ void die_if_kernel(char * str, struct pt_regs * regs, long err)
u32 *sp, *pc, addr, module_start, module_end;
extern char start_kernel, _etext;
- if ((regs->cp0_status & (ST0_ERL|ST0_EXL)) == 0)
+ /*
+ * Just return if in user mode.
+ */
+#if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2)
+ if (!((regs)->cp0_status & 0x4))
return;
+#endif
+#if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4)
+ if (!(regs->cp0_status & 0x18))
+ return;
+#endif
- sp = (u32 *)regs->reg29;
- pc = (u32 *)regs->cp0_epc;
+ /*
+ * Yes, these double casts are required ...
+ */
+ sp = (u32 *)(unsigned long)regs->regs[29];
+ pc = (u32 *)(unsigned long)regs->cp0_epc;
console_verbose();
printk("%s: %08lx\n", str, err );
@@ -105,11 +104,6 @@ void die_if_kernel(char * str, struct pt_regs * regs, long err)
show_regs(regs);
/*
- * Some goodies...
- */
- printk("Int : %ld\n", regs->interrupt);
-
- /*
* Dump the stack
*/
if (STACK_MAGIC != *(u32 *)current->kernel_stack_page)
@@ -119,20 +113,30 @@ void die_if_kernel(char * str, struct pt_regs * regs, long err)
for(i=0;i<5;i++)
printk("%08x ", *sp++);
stack = (int *) sp;
+
for(i=0; i < kstack_depth_to_print; i++) {
+ unsigned int stackdata;
+
if (((u32) stack & (PAGE_SIZE -1)) == 0)
break;
if (i && ((i % 8) == 0))
printk("\n ");
- printk("%08lx ", get_user_long(stack++));
+ if (get_user(stackdata, stack++) < 0) {
+ printk("(Bad stack address)");
+ break;
+ }
+ printk("%08x ", stackdata);
}
printk("\nCall Trace: ");
stack = (int *)sp;
i = 1;
module_start = VMALLOC_START;
module_end = module_start + MODULE_RANGE;
- while (((u32)stack & (PAGE_SIZE -1)) != 0) {
- addr = get_user_long(stack++);
+ while (((unsigned long)stack & (PAGE_SIZE -1)) != 0) {
+ if (get_user(addr, stack++) < 0) {
+ printk("(Bad address)\n");
+ break;
+ }
/*
* If the address is either in the text segment of the
* kernel, or in the region which contains vmalloc'ed
@@ -152,123 +156,128 @@ void die_if_kernel(char * str, struct pt_regs * regs, long err)
}
printk("\nCode : ");
- for(i=0;i<5;i++)
- printk("%08x ", *pc++);
- printk("\n");
+ if ((KSEGX(pc) == KSEG0 || KSEGX(pc) == KSEG1) &&
+ (((unsigned long) pc & 3) == 0))
+ {
+ for(i=0;i<5;i++)
+ printk("%08x ", *pc++);
+ printk("\n");
+ }
+ else
+ printk("(Bad address in epc)\n");
do_exit(SIGSEGV);
}
-static void
-fix_ade(struct pt_regs *regs, int write)
+static void default_be_board_handler(struct pt_regs *regs)
{
- printk("Received address error (ade%c)\n", write ? 's' : 'l');
- panic("Fixing address errors not implemented yet");
+ /*
+ * Assume it would be too dangerous to continue ...
+ */
+ force_sig(SIGBUS, current);
}
-void do_adel(struct pt_regs *regs)
+void do_ibe(struct pt_regs *regs)
{
- unsigned long pc = regs->cp0_epc;
- int i;
-
- if(fix_ade_enabled)
- {
- fix_ade(regs, 0);
- return;
- }
-#if 0
- for(i=0; i<NR_TASKS;i++)
- if(task[i] && task[i]->pid >= 2)
- {
- printk("Process %d\n", task[i]->pid);
- dump_list_process(task[i], pc);
- }
-#endif
- show_regs(regs);
-while(1);
- dump_tlb_nonwired();
- send_sig(SIGSEGV, current, 1);
+ ibe_board_handler(regs);
}
-void do_ades(struct pt_regs *regs)
+void do_dbe(struct pt_regs *regs)
{
- unsigned long pc = regs->cp0_epc;
- int i;
-
- if(fix_ade_enabled)
- {
- fix_ade(regs, 1);
- return;
- }
-while(1);
- for(i=0; i<NR_TASKS;i++)
- if(task[i] && task[i]->pid >= 2)
- {
- printk("Process %d\n", task[i]->pid);
- dump_list_process(task[i], pc);
- }
- show_regs(regs);
- dump_tlb_nonwired();
- send_sig(SIGSEGV, current, 1);
+ dbe_board_handler(regs);
}
-/*
- * The ibe/dbe exceptions are signaled by onboard hardware and should get
- * a board specific handlers to get maximum available information. Bus
- * errors are always symptom of hardware malfunction or a kernel error.
- *
- * FIXME: Linux/68k sends a SIGSEGV for a buserror which seems to be wrong.
- * This is certainly wrong. Actually, all hardware errors (ades,adel,ibe,dbe)
- * are bus errors and therefor should send a SIGBUS! (Andy)
- */
-void do_ibe(struct pt_regs *regs)
+void do_ov(struct pt_regs *regs)
{
-while(1);
- send_sig(SIGBUS, current, 1);
+#ifdef CONF_DEBUG_EXCEPTIONS
+ show_regs(regs);
+#endif
+ if (compute_return_epc(regs))
+ return;
+ force_sig(SIGFPE, current);
}
-void do_dbe(struct pt_regs *regs)
+void do_fpe(struct pt_regs *regs, unsigned int fcr31)
{
-while(1);
- send_sig(SIGBUS, current, 1);
+#ifdef CONF_DEBUG_EXCEPTIONS
+ show_regs(regs);
+#endif
+ if (compute_return_epc(regs))
+ return;
+ force_sig(SIGFPE, current);
}
-void do_ov(struct pt_regs *regs)
+static inline int get_insn_opcode(struct pt_regs *regs, unsigned int *opcode)
{
-while(1);
- send_sig(SIGFPE, current, 1);
+ unsigned int *addr;
+
+ addr = (unsigned int *) (unsigned long) regs->cp0_epc;
+ if (regs->cp0_cause & CAUSEF_BD)
+ addr += 4;
+
+ if (get_user(*opcode, addr)) {
+ force_sig(SIGSEGV, current);
+ return -EFAULT;
+ }
+
+ return 0;
}
-void do_fpe(struct pt_regs *regs)
+static __inline__ void
+do_bp_and_tr(struct pt_regs *regs, char *exc, unsigned int trapcode)
{
-while(1);
- send_sig(SIGFPE, current, 1);
+ /*
+ * (A quick test says that IRIX 5.3 sends SIGTRAP for all break
+ * insns, even for break codes that indicate arithmetic failures.
+ * Wiered ...)
+ */
+ force_sig(SIGTRAP, current);
+#ifdef CONF_DEBUG_EXCEPTIONS
+ show_regs(regs);
+#endif
+ if (compute_return_epc(regs))
+ return;
}
void do_bp(struct pt_regs *regs)
{
-while(1);
- send_sig(SIGILL, current, 1);
+ unsigned int opcode, bcode;
+
+ /*
+ * There is the ancient bug in the MIPS assemblers that the break
+ * code starts left to bit 16 instead to bit 6 in the opcode.
+ * Gas is bug-compatible ...
+ */
+ if (get_insn_opcode(regs, &opcode))
+ return;
+ bcode = ((opcode >> 16) & ((1 << 20) - 1));
+
+ do_bp_and_tr(regs, "bp", bcode);
}
void do_tr(struct pt_regs *regs)
{
-while(1);
- send_sig(SIGILL, current, 1);
+ unsigned int opcode, bcode;
+
+ if (get_insn_opcode(regs, &opcode))
+ return;
+ bcode = ((opcode >> 6) & ((1 << 20) - 1));
+
+ do_bp_and_tr(regs, "tr", bcode);
}
+/*
+ * TODO: add emulation of higher ISAs' instruction. In particular
+ * interest in MUL, MAD MADU has been expressed such that R4640/R4650
+ * code can be run on other MIPS CPUs.
+ */
void do_ri(struct pt_regs *regs)
{
- int i;
-
- for(i=0; i<NR_TASKS;i++)
- if(task[i] && task[i]->pid >= 2)
- {
- printk("Process %d\n", task[i]->pid);
- dump_list_process(task[i], 0x7ffff000);
- }
+#ifdef CONF_DEBUG_EXCEPTIONS
show_regs(regs);
-while(1);
- send_sig(SIGILL, current, 1);
+#endif
+ if (compute_return_epc(regs))
+ return;
+ force_sig(SIGILL, current);
}
void do_cpu(struct pt_regs *regs)
@@ -276,53 +285,40 @@ void do_cpu(struct pt_regs *regs)
unsigned int cpid;
cpid = (regs->cp0_cause >> CAUSEB_CE) & 3;
- switch(cpid)
- {
- case 1:
+ if (cpid == 1) {
regs->cp0_status |= ST0_CU1;
- break;
- case 3:
- /*
- * This is a guess how to handle MIPS IV -
- * I don't have a manual.
- */
- if((boot_info.cputype == CPU_R8000) ||
- (boot_info.cputype == CPU_R10000))
- {
- regs->cp0_status |= ST0_CU3;
- break;
- }
- case 0:
- /*
- * CPU for cp0 can only happen in user mode
- */
- case 2:
- send_sig(SIGILL, current, 1);
- break;
+ return;
}
+
+ force_sig(SIGILL, current);
}
void do_vcei(struct pt_regs *regs)
{
/*
- * Only possible on R4[04]00[SM]C. No handler because
- * I don't have such a cpu.
+ * Only possible on R4[04]00[SM]C. No handler because I don't have
+ * such a cpu. Theory says this exception doesn't happen.
*/
- panic("Caught VCEI exception - can't handle yet\n");
+ panic("Caught VCEI exception - should not happen");
}
void do_vced(struct pt_regs *regs)
{
/*
- * Only possible on R4[04]00[SM]C. No handler because
- * I don't have such a cpu.
+ * Only possible on R4[04]00[SM]C. No handler because I don't have
+ * such a cpu. Theory says this exception doesn't happen.
*/
- panic("Caught VCED exception - can't handle yet\n");
+ panic("Caught VCE exception - should not happen");
}
void do_watch(struct pt_regs *regs)
{
- panic("Caught WATCH exception - can't handle yet\n");
+ /*
+ * We use the watch exception where available to detect stack
+ * overflows.
+ */
+ show_regs(regs);
+ panic("Caught WATCH exception - probably caused by stack overflow.");
}
void do_reserved(struct pt_regs *regs)
@@ -332,46 +328,56 @@ void do_reserved(struct pt_regs *regs)
* Most probably caused by a new unknown cpu type or
* after another deadly hard/software error.
*/
- panic("Caught reserved exception - can't handle.\n");
+ panic("Caught reserved exception - should not happen.");
}
-void trap_init(void)
+static void watch_init(unsigned long cputype)
{
- unsigned long i;
-
- if(boot_info.machtype == MACH_MIPS_MAGNUM_4000)
- EISA_bus = 1;
+ switch(cputype) {
+ case CPU_R10000:
+ case CPU_R4000MC:
+ case CPU_R4400MC:
+ case CPU_R4000SC:
+ case CPU_R4400SC:
+ case CPU_R4000PC:
+ case CPU_R4400PC:
+ case CPU_R4200:
+ /* case CPU_R4300: */
+ set_except_vector(23, handle_watch);
+ watch_available = 1;
+ break;
+ }
+}
+void trap_init(void)
+{
/*
- * Setup default vectors
+ * Only some CPUs have the watch exception.
*/
- for (i=0;i<=31;i++)
- set_except_vector(i, handle_reserved);
+ watch_init(mips_cputype);
/*
* Handling the following exceptions depends mostly of the cpu type
*/
- switch(boot_info.cputype) {
- case CPU_R4000MC:
- case CPU_R4400MC:
- case CPU_R4000SC:
- case CPU_R4400SC:
+ switch(mips_cputype) {
+ case CPU_R10000:
/*
- * Handlers not implemented yet. If should every be used
- * it's a bug in the Linux/MIPS kernel, anyway.
+ * The R10000 is in most aspects similar to the R4400. It
+ * should get some special optimizations.
*/
- set_except_vector(14, handle_vcei);
- set_except_vector(31, handle_vced);
- case CPU_R4000PC:
- case CPU_R4400PC:
- case CPU_R4200:
- /* case CPU_R4300: */
+ write_32bit_cp0_register(CP0_FRAMEMASK, 0);
+ set_cp0_status(ST0_XX, ST0_XX);
/*
- * Use watch exception to trap on access to address zero
+ * The R10k might even work for Linux/MIPS - but we're paranoid
+ * and refuse to run until this is tested on real silicon
*/
- set_except_vector(23, handle_watch);
- watch_set(KSEG0, 3);
- case CPU_R4600:
+ panic("CPU too expensive - making holiday in the ANDES!");
+ break;
+
+ case CPU_R4000MC: case CPU_R4400MC: case CPU_R4000SC:
+ case CPU_R4400SC: case CPU_R4000PC: case CPU_R4400PC:
+ case CPU_R4200: /*case CPU_R4300: case CPU_R4640: */
+ case CPU_R4600: case CPU_R4700:
set_except_vector(1, handle_mod);
set_except_vector(2, handle_tlbl);
set_except_vector(3, handle_tlbs);
@@ -392,47 +398,64 @@ void trap_init(void)
set_except_vector(12, handle_ov);
set_except_vector(13, handle_tr);
set_except_vector(15, handle_fpe);
+ break;
+ case CPU_R6000: case CPU_R6000A:
+#if 0
+ /* The R6000 is the only R-series CPU that features a machine
+ * check exception (similar to the R4000 cache error) and
+ * unaligned ldc1/sdc1 exception. The handlers have not been
+ * written yet. Well, anyway there is no R6000 machine on the
+ * current list of targets for Linux/MIPS.
+ */
+ set_except_vector(14, handle_mc);
+ set_except_vector(15, handle_ndc);
+#endif
+ case CPU_R2000: case CPU_R3000: case CPU_R3000A: case CPU_R3041:
+ case CPU_R3051: case CPU_R3052: case CPU_R3081: case CPU_R3081E:
+ /*
+ * Clear BEV, we are ready to handle exceptions using
+ * the in-RAM dispatchers. This will not be useful on all
+ * machines, but it can't hurt (the worst that can happen is
+ * that BEV is already 0).
+ */
+ set_cp0_status(ST0_BEV,0);
+
+ /*
+ * Actually don't know about these, but let's guess - PMA
+ */
+ set_except_vector(1, handle_mod);
+ set_except_vector(2, handle_tlbl);
+ set_except_vector(3, handle_tlbs);
+ set_except_vector(4, handle_adel);
+ set_except_vector(5, handle_ades);
/*
- * Compute mask for page_colour(). This is based on the
- * size of the data cache. Does the size of the icache
- * need to be accounted for?
+ * The Data Bus Error/ Instruction Bus Errors are signaled
+ * by external hardware. Therefore these two expection have
+ * board specific handlers.
*/
- i = read_32bit_cp0_register(CP0_CONFIG);
- i = (i >> 26) & 7;
- page_colour_mask = 1 << (12 + i);
+ set_except_vector(6, handle_ibe);
+ set_except_vector(7, handle_dbe);
+ ibe_board_handler = default_be_board_handler;
+ dbe_board_handler = default_be_board_handler;
+
+ set_except_vector(8, handle_sys);
+ set_except_vector(9, handle_bp);
+ set_except_vector(10, handle_ri);
+ set_except_vector(11, handle_cpu);
+ set_except_vector(12, handle_ov);
+ set_except_vector(13, handle_tr);
+ set_except_vector(15, handle_fpe);
break;
- case CPU_R2000:
- case CPU_R3000:
- case CPU_R3000A:
- case CPU_R3041:
- case CPU_R3051:
- case CPU_R3052:
- case CPU_R3081:
- case CPU_R3081E:
- case CPU_R6000:
- case CPU_R6000A:
- case CPU_R8000:
+
+ case CPU_R8000: case CPU_R5000:
printk("Detected unsupported CPU type %s.\n",
- cpu_names[boot_info.cputype]);
- panic("Can't handle CPU\n");
+ cpu_names[mips_cputype]);
+ panic("Can't handle CPU");
break;
- /*
- * The R10000 is in most aspects similar to the R4400. It however
- * should get some special optimizations.
- */
- case CPU_R10000:
- write_32bit_cp0_register(CP0_FRAMEMASK, 0);
- panic("CPU too expensive - making holiday in the ANDES!");
- break;
case CPU_UNKNOWN:
default:
- panic("Unknown CPU type");
+ panic("Unsupported CPU type");
}
-
- /*
- * The interrupt handler depends most of the board type.
- */
- set_except_vector(0, feature->handle_int);
}
diff --git a/arch/mips/kernel/tyne.S b/arch/mips/kernel/tyne.S
deleted file mode 100644
index 912f6d414..000000000
--- a/arch/mips/kernel/tyne.S
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * arch/mips/kernel/tyne.S
- *
- * Deskstation Tyne specific Assembler code
- *
- * Copyright (C) 1994, 1995 Waldorf Electronics
- * written by Ralf Baechle and Andreas Busse
- */
-#include <asm/asm.h>
-#include <asm/mipsconfig.h>
-#include <asm/mipsregs.h>
-#include <asm/stackframe.h>
-
-/*
- * Deskstation Tyne interrupt handler
- */
- .text
- .set noreorder
- .set noat
- .align 5
- NESTED(deskstation_tyne_handle_int, FR_SIZE, sp)
- SAVE_ALL
- CLI
- .set at
- lui s0,%hi(PORT_BASE)
- li t1,0x0f
- sb t1,%lo(PORT_BASE+0x20)(s0) # poll command
- lb t1,%lo(PORT_BASE+0x20)(s0) # read result
- li s1,1
- bgtz t1,Lpoll_second
- andi t1,t1,7
- /*
- * Acknowledge first pic
- */
- lb t2,%lo(PORT_BASE+0x21)(s0)
- lui s4,%hi(cache_21)
- lb t0,%lo(cache_21)(s4)
- sllv s1,s1,t1
- or t0,t0,s1
- sb t0,%lo(cache_21)(s4)
- sb t0,%lo(PORT_BASE+0x21)(s0)
- lui s3,%hi(intr_count)
- lw t0,%lo(intr_count)(s3)
- li t2,0x20
- sb t2,%lo(PORT_BASE+0x20)(s0)
- /*
- * Now call the real handler
- */
- la t3,IRQ_vectors
- sll t2,t1,2
- addu t3,t3,t2
- lw t3,(t3)
- addiu t0,t0,1
- jalr t3
- sw t0,%lo(intr_count)(s3) # delay slot
- lw t0,%lo(intr_count)(s3)
- /*
- * Unblock first pic
- */
- lbu t1,%lo(PORT_BASE+0x21)(s0)
- lb t1,%lo(cache_21)(s4)
- subu t0,t0,1
- sw t0,%lo(intr_count)(s3)
- nor s1,zero,s1
- and t1,t1,s1
- sb t1,%lo(cache_21)(s4)
- jr v0
- sb t1,%lo(PORT_BASE+0x21)(s0) # delay slot
-
- .align 5
-Lpoll_second: li t1,0x0f
- sb t1,%lo(PORT_BASE+0xa0)(s0) # poll command
- lb t1,%lo(PORT_BASE+0xa0)(s0) # read result
- lui s4,%hi(cache_A1)
- bgtz t1,spurious_interrupt
- andi t1,t1,7
- /*
- * Acknowledge second pic
- */
- lbu t2,%lo(PORT_BASE+0xa1)(s0)
- lb t3,%lo(cache_A1)(s4)
- sllv s1,s1,t1
- or t3,t3,s1
- sb t3,%lo(cache_A1)(s4)
- sb t3,%lo(PORT_BASE+0xa1)(s0)
- li t3,0x20
- sb t3,%lo(PORT_BASE+0xa0)(s0)
- lui s3,%hi(intr_count)
- lw t0,%lo(intr_count)(s3)
- sb t3,%lo(PORT_BASE+0x20)(s0)
- /*
- * Now call the real handler
- */
- la t0,IRQ_vectors
- sll t2,t1,2
- addu t0,t0,t2
- lw t0,32(t0)
- addiu t0,t0,1
- jalr t0
- sw t0,%lo(intr_count)(s3) # delay slot
- lw t0,%lo(intr_count)(s3)
- /*
- * Unblock second pic
- */
- lb t1,%lo(PORT_BASE+0xa1)(s0)
- lb t1,%lo(cache_A1)(s4)
- subu t0,t0,1
- lw t0,%lo(intr_count)(s3)
- nor s1,zero,s1
- and t1,t1,s1
- sb t1,%lo(cache_A1)(s4)
- jr v0
- sb t1,%lo(PORT_BASE+0xa1)(s0) # delay slot
- END(deskstation_tyne_handle_int)
diff --git a/arch/mips/kernel/tynedma.c b/arch/mips/kernel/tynedma.c
deleted file mode 100644
index 04846cddd..000000000
--- a/arch/mips/kernel/tynedma.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Tiny Tyne DMA buffer allocator
- *
- * Copyright (C) 1995 Ralf Baechle
- */
-#include <linux/autoconf.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <asm/bootinfo.h>
-
-#ifdef CONFIG_DESKSTATION_TYNE
-
-static unsigned long allocated;
-
-/*
- * Not very sophisticated, but should suffice for now...
- */
-unsigned long deskstation_tyne_dma_alloc(size_t size)
-{
- unsigned long ret = allocated;
- allocated += size;
- if (allocated > boot_info.dma_cache_size)
- ret = -1;
- return ret;
-}
-
-void deskstation_tyne_dma_init(void)
-{
- if (boot_info.machtype != MACH_DESKSTATION_TYNE)
- return;
- allocated = 0;
- printk ("Deskstation Tyne DMA (%luk) buffer initialized.\n",
- boot_info.dma_cache_size >> 10);
-}
-
-#endif /* CONFIG_DESKSTATION_TYNE */
diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c
new file mode 100644
index 000000000..8bf2ad9b7
--- /dev/null
+++ b/arch/mips/kernel/unaligned.c
@@ -0,0 +1,457 @@
+/*
+ * Handle unaligned accesses by emulation.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1996 by Ralf Baechle
+ *
+ * This file contains exception handler for address error exception with the
+ * special capability to execute faulting instructions in software. The
+ * handler does not try to handle the case when the program counter points
+ * to an address not aligned to a word boundary.
+ *
+ * Putting data to unaligned addresses is a bad practice even on Intel where
+ * only the performance is affected. Much worse is that such code is non-
+ * portable. Due to several programs that die on MIPS due to alignment
+ * problems I decieded to implement this handler anyway though I originally
+ * didn't intend to do this at all for user code.
+ *
+ * For now I enable fixing of address errors by default to make life easier.
+ * I however intend to disable this somewhen in the future when the alignment
+ * problems with user programs have been fixed. For programmers this is the
+ * right way to go.
+ *
+ * Fixing address errors is a per process option. The option is inherited
+ * across fork(2) and execve(2) calls. If you really want to use the
+ * option in your user programs - I discourage the use of the software
+ * emulation strongly - use the following code in your userland stuff:
+ *
+ * #include <sys/sysmips.h>
+ *
+ * ...
+ * sysmips(MIPS_FIXADE, x);
+ * ...
+ *
+ * The parameter x is 0 for disabeling software emulation. Set bit 0 for
+ * enabeling software emulation and bit 1 for enabeling printing debug
+ * messages into syslog to aid finding address errors in programs.
+ *
+ * The logging feature is an addition over RISC/os and IRIX where only the
+ * values 0 and 1 are acceptable values for x. I'll probably remove this
+ * hack later on.
+ *
+ * Below a little program to play around with this feature.
+ *
+ * #include <stdio.h>
+ * #include <asm/sysmips.h>
+ *
+ * struct foo {
+ * unsigned char bar[8];
+ * };
+ *
+ * main(int argc, char *argv[])
+ * {
+ * struct foo x = {0, 1, 2, 3, 4, 5, 6, 7};
+ * unsigned int *p = (unsigned int *) (x.bar + 3);
+ * int i;
+ *
+ * if (argc > 1)
+ * sysmips(MIPS_FIXADE, atoi(argv[1]));
+ *
+ * printf("*p = %08lx\n", *p);
+ *
+ * *p = 0xdeadface;
+ *
+ * for(i = 0; i <= 7; i++)
+ * printf("%02x ", x.bar[i]);
+ * printf("\n");
+ * }
+ *
+ * Until I've written the code to handle branch delay slots it may happen
+ * that the kernel receives an ades/adel instruction from an insn in a
+ * branch delay slot but is unable to handle this case. The kernel knows
+ * this fact and therefore will kill the process. For most code you can
+ * fix this temporarily by compiling with flags -fno-delayed-branch -Wa,-O0.
+ *
+ * Coprozessor loads are not supported; I think this case is unimportant
+ * in the practice.
+ *
+ * TODO: Handle ndc (attempted store to doubleword in uncached memory)
+ * exception for the R6000.
+ * A store crossing a page boundary might be executed only partially.
+ * Undo the partial store in this case.
+ */
+#include <linux/mm.h>
+#include <linux/signal.h>
+#include <asm/branch.h>
+#include <asm/byteorder.h>
+#include <asm/inst.h>
+#include <asm/uaccess.h>
+
+#undef CONF_NO_UNALIGNED_KERNEL_ACCESS
+#undef CONF_LOG_UNALIGNED_ACCESSES
+
+#define STR(x) __STR(x)
+#define __STR(x) #x
+
+/*
+ * User code may only access USEG; kernel code may access the
+ * entire address space.
+ */
+#define check_axs(p,a,s) \
+ if ((long)(~(pc) & ((a) | ((a)+(s)))) < 0) \
+ goto sigbus;
+
+static __inline__ void
+emulate_load_store_insn(struct pt_regs *regs, unsigned long addr, unsigned long pc)
+{
+ union mips_instruction insn;
+ __register_t value;
+
+ regs->regs[0] = 0;
+ /*
+ * This load never faults.
+ */
+ __get_user(insn.word, (unsigned int *)pc);
+
+ switch (insn.i_format.opcode) {
+ /*
+ * These are instructions that a compiler doesn't generate. We
+ * can assume therefore that the code is MIPS-aware and
+ * really buggy. Emulating these instructions would break the
+ * semantics anyway.
+ */
+ case ll_op:
+ case lld_op:
+ case sc_op:
+ case scd_op:
+
+ /*
+ * For these instructions the only way to create an address
+ * error is an attempted access to kernel/supervisor address
+ * space.
+ */
+ case ldl_op:
+ case ldr_op:
+ case lwl_op:
+ case lwr_op:
+ case sdl_op:
+ case sdr_op:
+ case swl_op:
+ case swr_op:
+ case lb_op:
+ case lbu_op:
+ case sb_op:
+ goto sigbus;
+
+ /*
+ * The remaining opcodes are the ones that are really of interrest.
+ */
+ case lh_op:
+ check_axs(pc, addr, 2);
+ __asm__(
+ ".set\tnoat\n"
+#ifdef __BIG_ENDIAN
+ "1:\tlb\t%0,0(%1)\n"
+ "2:\tlbu\t$1,1(%1)\n\t"
+#endif
+#ifdef __LITTLE_ENDIAN
+ "1:\tlb\t%0,1(%1)\n"
+ "2:\tlbu\t$1,0(%1)\n\t"
+#endif
+ "sll\t%0,0x8\n\t"
+ "or\t%0,$1\n\t"
+ ".set\tat\n\t"
+ ".section\t__ex_table,\"a\"\n\t"
+ STR(PTR)"\t1b,%2\n\t"
+ STR(PTR)"\t2b,%2\n\t"
+ ".text"
+ :"=&r" (value)
+ :"r" (addr), "i" (&&fault)
+ :"$1");
+ regs->regs[insn.i_format.rt] = value;
+ return;
+
+ case lw_op:
+ check_axs(pc, addr, 4);
+ __asm__(
+#ifdef __BIG_ENDIAN
+ "1:\tlwl\t%0,(%1)\n"
+ "2:\tlwr\t%0,3(%1)\n\t"
+#endif
+#ifdef __LITTLE_ENDIAN
+ "1:\tlwl\t%0,3(%1)\n"
+ "2:\tlwr\t%0,(%1)\n\t"
+#endif
+ ".section\t__ex_table,\"a\"\n\t"
+ STR(PTR)"\t1b,%2\n\t"
+ STR(PTR)"\t2b,%2\n\t"
+ ".text"
+ :"=&r" (value)
+ :"r" (addr), "i" (&&fault));
+ regs->regs[insn.i_format.rt] = value;
+ return;
+
+ case lhu_op:
+ check_axs(pc, addr, 2);
+ __asm__(
+ ".set\tnoat\n"
+#ifdef __BIG_ENDIAN
+ "1:\tlbu\t%0,0(%1)\n"
+ "2:\tlbu\t$1,1(%1)\n\t"
+#endif
+#ifdef __LITTLE_ENDIAN
+ "1:\tlbu\t%0,1(%1)\n"
+ "2:\tlbu\t$1,0(%1)\n\t"
+#endif
+ "sll\t%0,0x8\n\t"
+ "or\t%0,$1\n\t"
+ ".set\tat\n\t"
+ ".section\t__ex_table,\"a\"\n\t"
+ STR(PTR)"\t1b,%2\n\t"
+ STR(PTR)"\t2b,%2\n\t"
+ ".text"
+ :"=&r" (value)
+ :"r" (addr), "i" (&&fault)
+ :"$1");
+ regs->regs[insn.i_format.rt] = value;
+ return;
+
+ case lwu_op:
+ check_axs(pc, addr, 4);
+ __asm__(
+#ifdef __BIG_ENDIAN
+ "1:\tlwl\t%0,(%1)\n"
+ "2:\tlwr\t%0,3(%1)\n\t"
+#endif
+#ifdef __LITTLE_ENDIAN
+ "1:\tlwl\t%0,3(%1)\n"
+ "2:\tlwr\t%0,(%1)\n\t"
+#endif
+ ".section\t__ex_table,\"a\"\n\t"
+ STR(PTR)"\t1b,%2\n\t"
+ STR(PTR)"\t2b,%2\n\t"
+ ".text"
+ :"=&r" (value)
+ :"r" (addr), "i" (&&fault));
+ value &= 0xffffffff;
+ regs->regs[insn.i_format.rt] = value;
+ return;
+
+ case ld_op:
+ check_axs(pc, addr, 8);
+ __asm__(
+ ".set\tmips3\n"
+#ifdef __BIG_ENDIAN
+ "1:\tldl\t%0,(%1)\n"
+ "2:\tldr\t%0,7(%1)\n\t"
+#endif
+#ifdef __LITTLE_ENDIAN
+ "1:\tldl\t%0,7(%1)\n"
+ "2:\tldr\t%0,(%1)\n\t"
+#endif
+ ".set\tmips0\n\t"
+ ".section\t__ex_table,\"a\"\n\t"
+ STR(PTR)"\t1b,%2\n\t"
+ STR(PTR)"\t2b,%2\n\t"
+ ".text"
+ :"=&r" (value)
+ :"r" (addr), "i" (&&fault));
+ regs->regs[insn.i_format.rt] = value;
+ return;
+
+ case sh_op:
+ check_axs(pc, addr, 2);
+ value = regs->regs[insn.i_format.rt];
+ __asm__(
+#ifdef __BIG_ENDIAN
+ ".set\tnoat\n"
+ "1:\tsb\t%0,1(%1)\n\t"
+ "srl\t$1,%0,0x8\n"
+ "2:\tsb\t$1,0(%1)\n\t"
+ ".set\tat\n\t"
+#endif
+#ifdef __LITTLE_ENDIAN
+ ".set\tnoat\n"
+ "1:\tsb\t%0,0(%1)\n\t"
+ "srl\t$1,%0,0x8\n"
+ "2:\tsb\t$1,1(%1)\n\t"
+ ".set\tat\n\t"
+#endif
+ ".section\t__ex_table,\"a\"\n\t"
+ STR(PTR)"\t1b,%2\n\t"
+ STR(PTR)"\t2b,%2\n\t"
+ ".text"
+ : /* no outputs */
+ :"r" (value), "r" (addr), "i" (&&fault)
+ :"$1");
+ return;
+
+ case sw_op:
+ check_axs(pc, addr, 4);
+ value = regs->regs[insn.i_format.rt];
+ __asm__(
+#ifdef __BIG_ENDIAN
+ "1:\tswl\t%0,(%1)\n"
+ "2:\tswr\t%0,3(%1)\n\t"
+#endif
+#ifdef __LITTLE_ENDIAN
+ "1:\tswl\t%0,3(%1)\n"
+ "2:\tswr\t%0,(%1)\n\t"
+#endif
+ ".section\t__ex_table,\"a\"\n\t"
+ STR(PTR)"\t1b,%2\n\t"
+ STR(PTR)"\t2b,%2\n\t"
+ ".text"
+ : /* no outputs */
+ :"r" (value), "r" (addr), "i" (&&fault));
+ return;
+
+ case sd_op:
+ check_axs(pc, addr, 8);
+ value = regs->regs[insn.i_format.rt];
+ __asm__(
+ ".set\tmips3\n"
+#ifdef __BIG_ENDIAN
+ "1:\tsdl\t%0,(%1)\n"
+ "2:\tsdr\t%0,7(%1)\n\t"
+#endif
+#ifdef __LITTLE_ENDIAN
+ "1:\tsdl\t%0,7(%1)\n"
+ "2:\tsdr\t%0,(%1)\n\t"
+#endif
+ ".set\tmips0\n\t"
+ ".section\t__ex_table,\"a\"\n\t"
+ STR(PTR)"\t1b,%2\n\t"
+ STR(PTR)"\t2b,%2\n\t"
+ ".text"
+ : /* no outputs */
+ :"r" (value), "r" (addr), "i" (&&fault));
+ return;
+
+ case lwc1_op:
+ case ldc1_op:
+ case swc1_op:
+ case sdc1_op:
+ /*
+ * I herewith declare: this does not happen. So send SIGBUS.
+ */
+ goto sigbus;
+
+ case lwc2_op:
+ case ldc2_op:
+ case swc2_op:
+ case sdc2_op:
+ /*
+ * These are the coprozessor 2 load/stores. The current
+ * implementations don't use cp2 and cp2 should always be
+ * disabled in c0_status. So send SIGILL.
+ * (No longer true: The Sony Praystation uses cp2 for
+ * 3D matrix operations. Dunno if that thingy has a MMU ...)
+ */
+ default:
+ /*
+ * Pheeee... We encountered an yet unknown instruction ...
+ */
+ force_sig(SIGILL, current);
+ }
+ return;
+
+fault:
+ send_sig(SIGSEGV, current, 1);
+ return;
+sigbus:
+ send_sig(SIGBUS, current, 1);
+ return;
+}
+
+unsigned long unaligned_instructions;
+
+static __inline__ void
+fix_ade(struct pt_regs *regs, unsigned long pc)
+{
+ /*
+ * Did we catch a fault trying to load an instruction?
+ */
+ if (regs->cp0_badvaddr == pc) {
+ /*
+ * Phee... Either the code is severly messed up or the
+ * process tried to activate some MIPS16 code.
+ */
+ force_sig(SIGBUS, current);
+ }
+
+ /*
+ * Ok, this wasn't a failed instruction load. The CPU was capable of
+ * reading the instruction and faulted after this. So we don't need
+ * to verify_area the address of the instrucion. We still don't
+ * know whether the address used was legal and therefore need to do
+ * verify_area(). The CPU already did the checking for legal
+ * instructions for us, so we don't need to do this.
+ */
+ emulate_load_store_insn(regs, regs->cp0_badvaddr, pc);
+ unaligned_instructions++;
+}
+
+#define kernel_address(x) ((long)(x) < 0)
+
+asmlinkage void
+do_ade(struct pt_regs *regs)
+{
+ __register_t pc = regs->cp0_epc;
+ __register_t badvaddr __attribute__ ((unused)) = regs->cp0_badvaddr;
+ char *adels;
+
+ adels = (((regs->cp0_cause & CAUSEF_EXCCODE) >>
+ CAUSEB_EXCCODE) == 4) ? "adel" : "ades";
+
+#ifdef CONF_NO_UNALIGNED_KERNEL_ACCESS
+ /*
+ * In an ideal world there are no unaligned accesses by the kernel.
+ * So be a bit noisy ...
+ */
+ if (kernel_address(badvaddr) && !user_mode(regs)) {
+ show_regs(regs);
+#ifdef __mips64
+ panic("Caught %s exception in kernel mode accessing %016Lx.",
+ adels, badvaddr);
+#else
+ panic("Caught %s exception in kernel mode accessing %08lx.",
+ adels, badvaddr);
+#endif
+ }
+#endif /* CONF_NO_UNALIGNED_KERNEL_ACCESS */
+
+#ifdef CONF_LOG_UNALIGNED_ACCESSES
+ if (current->tss.mflags & MF_LOGADE) {
+ __register_t logpc = pc;
+ if (regs->cp0_cause & CAUSEF_BD)
+ logpc += 4;
+#ifdef __mips64
+ printk(KERN_DEBUG
+ "Caught %s in '%s' at 0x%016Lx accessing 0x%016Lx.\n",
+ adels, current->comm, logpc, regs->cp0_badvaddr);
+#else
+ printk(KERN_DEBUG
+ "Caught %s in '%s' at 0x%08lx accessing 0x%08lx.\n",
+ adels, current->comm, logpc, regs->cp0_badvaddr);
+#endif
+ }
+#endif /* CONF_LOG_UNALIGNED_ACCESSES */
+
+ if (compute_return_epc(regs))
+ return;
+ if(current->tss.mflags & MF_FIXADE) {
+ pc += ((regs->cp0_cause & CAUSEF_BD) ? 4 : 0);
+ fix_ade(regs, pc);
+ return;
+ }
+
+#ifdef CONF_DEBUG_EXCEPTIONS
+ show_regs(regs);
+#endif
+
+ force_sig(SIGBUS, current);
+}
diff --git a/arch/mips/kernel/vm86.c b/arch/mips/kernel/vm86.c
index 454b35fe0..53627201a 100644
--- a/arch/mips/kernel/vm86.c
+++ b/arch/mips/kernel/vm86.c
@@ -1,12 +1,13 @@
/*
- * arch/mips/vm86.c
+ * arch/mips/kernel/vm86.c
*
- * Copyright (C) 1994 Waldorf GMBH,
+ * Copyright (C) 1994, 1996 Waldorf GMBH,
* written by Ralf Baechle
*/
#include <linux/linkage.h>
#include <linux/errno.h>
-#include <linux/vm86.h>
+
+struct vm86_struct;
asmlinkage int sys_vm86(struct vm86_struct * v86)
{