diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
commit | beb116954b9b7f3bb56412b2494b562f02b864b1 (patch) | |
tree | 120e997879884e1b9d93b265221b939d2ef1ade1 /arch/mips/kernel | |
parent | 908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff) |
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'arch/mips/kernel')
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, ®s); -} - -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, ®s); + 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, ®s); - 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, ¤t->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) { |