diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-17 00:20:36 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-17 00:20:36 +0200 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/mips/kernel | |
download | linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.xz linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/mips/kernel')
59 files changed, 22890 insertions, 0 deletions
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile new file mode 100644 index 000000000000..a0230ee0f7f4 --- /dev/null +++ b/arch/mips/kernel/Makefile @@ -0,0 +1,65 @@ +# +# Makefile for the Linux/MIPS kernel. +# + +extra-y := head.o init_task.o vmlinux.lds + +obj-y += cpu-probe.o branch.o entry.o genex.o irq.o process.o \ + ptrace.o reset.o semaphore.o setup.o signal.o syscall.o \ + time.o traps.o unaligned.o + +binfmt_irix-objs := irixelf.o irixinv.o irixioctl.o irixsig.o \ + irix5sys.o sysirix.o + +ifdef CONFIG_MODULES +obj-y += mips_ksyms.o module.o +obj-$(CONFIG_MIPS32) += module-elf32.o +obj-$(CONFIG_MIPS64) += module-elf64.o +endif + +obj-$(CONFIG_CPU_R3000) += r2300_fpu.o r2300_switch.o +obj-$(CONFIG_CPU_TX39XX) += r2300_fpu.o r2300_switch.o +obj-$(CONFIG_CPU_TX49XX) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_R4000) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_VR41XX) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_R4300) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_R4X00) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_R5000) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_R5432) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_R8000) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_RM7000) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_RM9000) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_NEVADA) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_R10000) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_SB1) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_MIPS32) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_MIPS64) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_R6000) += r6000_fpu.o r4k_switch.o + +obj-$(CONFIG_SMP) += smp.o + +obj-$(CONFIG_NO_ISA) += dma-no-isa.o +obj-$(CONFIG_I8259) += i8259.o +obj-$(CONFIG_IRQ_CPU) += irq_cpu.o +obj-$(CONFIG_IRQ_CPU_RM7K) += irq-rm7000.o +obj-$(CONFIG_IRQ_CPU_RM9K) += irq-rm9000.o +obj-$(CONFIG_IRQ_MV64340) += irq-mv6434x.o + +obj-$(CONFIG_MIPS32) += scall32-o32.o +obj-$(CONFIG_MIPS64) += scall64-64.o +obj-$(CONFIG_BINFMT_IRIX) += binfmt_irix.o +obj-$(CONFIG_MIPS32_COMPAT) += ioctl32.o linux32.o signal32.o +obj-$(CONFIG_MIPS32_N32) += binfmt_elfn32.o scall64-n32.o signal_n32.o +obj-$(CONFIG_MIPS32_O32) += binfmt_elfo32.o scall64-o32.o ptrace32.o + +obj-$(CONFIG_KGDB) += gdb-low.o gdb-stub.o +obj-$(CONFIG_PROC_FS) += proc.o + +obj-$(CONFIG_MIPS64) += cpu-bugs64.o + +obj-$(CONFIG_GEN_RTC) += genrtc.o + +CFLAGS_cpu-bugs64.o = $(shell if $(CC) $(CFLAGS) -Wa,-mdaddi -c -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-DHAVE_AS_SET_DADDI"; fi) +CFLAGS_ioctl32.o += -Ifs/ + +EXTRA_AFLAGS := $(CFLAGS) diff --git a/arch/mips/kernel/binfmt_elfn32.c b/arch/mips/kernel/binfmt_elfn32.c new file mode 100644 index 000000000000..ed47041f3030 --- /dev/null +++ b/arch/mips/kernel/binfmt_elfn32.c @@ -0,0 +1,119 @@ +/* + * Support for n32 Linux/MIPS ELF binaries. + * + * Copyright (C) 1999, 2001 Ralf Baechle + * Copyright (C) 1999, 2001 Silicon Graphics, Inc. + * + * Heavily inspired by the 32-bit Sparc compat code which is + * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller (davem@redhat.com) + * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek (jj@ultra.linux.cz) + */ + +#define ELF_ARCH EM_MIPS +#define ELF_CLASS ELFCLASS32 +#ifdef __MIPSEB__ +#define ELF_DATA ELFDATA2MSB; +#else /* __MIPSEL__ */ +#define ELF_DATA ELFDATA2LSB; +#endif + +/* ELF register definitions */ +#define ELF_NGREG 45 +#define ELF_NFPREG 33 + +typedef unsigned long elf_greg_t; +typedef elf_greg_t elf_gregset_t[ELF_NGREG]; + +typedef double elf_fpreg_t; +typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG]; + +/* + * This is used to ensure we don't load something for the wrong architecture. + */ +#define elf_check_arch(hdr) \ +({ \ + int __res = 1; \ + struct elfhdr *__h = (hdr); \ + \ + if (__h->e_machine != EM_MIPS) \ + __res = 0; \ + if (__h->e_ident[EI_CLASS] != ELFCLASS32) \ + __res = 0; \ + if (((__h->e_flags & EF_MIPS_ABI2) == 0) || \ + ((__h->e_flags & EF_MIPS_ABI) != 0)) \ + __res = 0; \ + \ + __res; \ +}) + +#define TASK32_SIZE 0x7fff8000UL +#undef ELF_ET_DYN_BASE +#define ELF_ET_DYN_BASE (TASK32_SIZE / 3 * 2) + +#include <asm/processor.h> +#include <linux/module.h> +#include <linux/config.h> +#include <linux/elfcore.h> +#include <linux/compat.h> + +#define elf_prstatus elf_prstatus32 +struct elf_prstatus32 +{ + struct elf_siginfo pr_info; /* Info associated with signal */ + short pr_cursig; /* Current signal */ + unsigned int pr_sigpend; /* Set of pending signals */ + unsigned int pr_sighold; /* Set of held signals */ + pid_t pr_pid; + pid_t pr_ppid; + pid_t pr_pgrp; + pid_t pr_sid; + struct compat_timeval pr_utime; /* User time */ + struct compat_timeval pr_stime; /* System time */ + struct compat_timeval pr_cutime;/* Cumulative user time */ + struct compat_timeval pr_cstime;/* Cumulative system time */ + elf_gregset_t pr_reg; /* GP registers */ + int pr_fpvalid; /* True if math co-processor being used. */ +}; + +#define elf_prpsinfo elf_prpsinfo32 +struct elf_prpsinfo32 +{ + char pr_state; /* numeric process state */ + char pr_sname; /* char for pr_state */ + char pr_zomb; /* zombie */ + char pr_nice; /* nice val */ + unsigned int pr_flag; /* flags */ + __kernel_uid_t pr_uid; + __kernel_gid_t pr_gid; + pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid; + /* Lots missing */ + char pr_fname[16]; /* filename of executable */ + char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ +}; + +#define elf_addr_t u32 +#define elf_caddr_t u32 +#define init_elf_binfmt init_elfn32_binfmt + +#define jiffies_to_timeval jiffies_to_compat_timeval +static __inline__ void +jiffies_to_compat_timeval(unsigned long jiffies, struct compat_timeval *value) +{ + /* + * Convert jiffies to nanoseconds and seperate with + * one divide. + */ + u64 nsec = (u64)jiffies * TICK_NSEC; + value->tv_sec = div_long_long_rem(nsec, NSEC_PER_SEC, &value->tv_usec); + value->tv_usec /= NSEC_PER_USEC; +} + +#define ELF_CORE_EFLAGS EF_MIPS_ABI2 + +MODULE_DESCRIPTION("Binary format loader for compatibility with n32 Linux/MIPS binaries"); +MODULE_AUTHOR("Ralf Baechle (ralf@linux-mips.org)"); + +#undef MODULE_DESCRIPTION +#undef MODULE_AUTHOR + +#include "../../../fs/binfmt_elf.c" diff --git a/arch/mips/kernel/binfmt_elfo32.c b/arch/mips/kernel/binfmt_elfo32.c new file mode 100644 index 000000000000..ee21b18c37a8 --- /dev/null +++ b/arch/mips/kernel/binfmt_elfo32.c @@ -0,0 +1,139 @@ +/* + * Support for o32 Linux/MIPS ELF binaries. + * + * Copyright (C) 1999, 2001 Ralf Baechle + * Copyright (C) 1999, 2001 Silicon Graphics, Inc. + * + * Heavily inspired by the 32-bit Sparc compat code which is + * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller (davem@redhat.com) + * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek (jj@ultra.linux.cz) + */ + +#define ELF_ARCH EM_MIPS +#define ELF_CLASS ELFCLASS32 +#ifdef __MIPSEB__ +#define ELF_DATA ELFDATA2MSB; +#else /* __MIPSEL__ */ +#define ELF_DATA ELFDATA2LSB; +#endif + +/* ELF register definitions */ +#define ELF_NGREG 45 +#define ELF_NFPREG 33 + +typedef unsigned int elf_greg_t; +typedef elf_greg_t elf_gregset_t[ELF_NGREG]; + +typedef double elf_fpreg_t; +typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG]; + +/* + * This is used to ensure we don't load something for the wrong architecture. + */ +#define elf_check_arch(hdr) \ +({ \ + int __res = 1; \ + struct elfhdr *__h = (hdr); \ + \ + if (__h->e_machine != EM_MIPS) \ + __res = 0; \ + if (__h->e_ident[EI_CLASS] != ELFCLASS32) \ + __res = 0; \ + if ((__h->e_flags & EF_MIPS_ABI2) != 0) \ + __res = 0; \ + if (((__h->e_flags & EF_MIPS_ABI) != 0) && \ + ((__h->e_flags & EF_MIPS_ABI) != EF_MIPS_ABI_O32)) \ + __res = 0; \ + \ + __res; \ +}) + +#define TASK32_SIZE 0x7fff8000UL +#undef ELF_ET_DYN_BASE +#define ELF_ET_DYN_BASE (TASK32_SIZE / 3 * 2) + +#include <asm/processor.h> +#include <linux/module.h> +#include <linux/config.h> +#include <linux/elfcore.h> +#include <linux/compat.h> + +#define elf_prstatus elf_prstatus32 +struct elf_prstatus32 +{ + struct elf_siginfo pr_info; /* Info associated with signal */ + short pr_cursig; /* Current signal */ + unsigned int pr_sigpend; /* Set of pending signals */ + unsigned int pr_sighold; /* Set of held signals */ + pid_t pr_pid; + pid_t pr_ppid; + pid_t pr_pgrp; + pid_t pr_sid; + struct compat_timeval pr_utime; /* User time */ + struct compat_timeval pr_stime; /* System time */ + struct compat_timeval pr_cutime;/* Cumulative user time */ + struct compat_timeval pr_cstime;/* Cumulative system time */ + elf_gregset_t pr_reg; /* GP registers */ + int pr_fpvalid; /* True if math co-processor being used. */ +}; + +#define elf_prpsinfo elf_prpsinfo32 +struct elf_prpsinfo32 +{ + char pr_state; /* numeric process state */ + char pr_sname; /* char for pr_state */ + char pr_zomb; /* zombie */ + char pr_nice; /* nice val */ + unsigned int pr_flag; /* flags */ + __kernel_uid_t pr_uid; + __kernel_gid_t pr_gid; + pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid; + /* Lots missing */ + char pr_fname[16]; /* filename of executable */ + char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ +}; + +#define elf_addr_t u32 +#define elf_caddr_t u32 +#define init_elf_binfmt init_elf32_binfmt + +#define jiffies_to_timeval jiffies_to_compat_timeval +static __inline__ void +jiffies_to_compat_timeval(unsigned long jiffies, struct compat_timeval *value) +{ + /* + * Convert jiffies to nanoseconds and seperate with + * one divide. + */ + u64 nsec = (u64)jiffies * TICK_NSEC; + value->tv_sec = div_long_long_rem(nsec, NSEC_PER_SEC, &value->tv_usec); + value->tv_usec /= NSEC_PER_USEC; +} + +#undef ELF_CORE_COPY_REGS +#define ELF_CORE_COPY_REGS(_dest,_regs) elf32_core_copy_regs(_dest,_regs); + +void elf32_core_copy_regs(elf_gregset_t _dest, struct pt_regs *_regs) +{ + int i; + + memset(_dest, 0, sizeof(elf_gregset_t)); + + /* XXXKW the 6 is from EF_REG0 in gdb/gdb/mips-linux-tdep.c, include/asm-mips/reg.h */ + for (i=6; i<38; i++) + _dest[i] = (elf_greg_t) _regs->regs[i-6]; + _dest[i++] = (elf_greg_t) _regs->lo; + _dest[i++] = (elf_greg_t) _regs->hi; + _dest[i++] = (elf_greg_t) _regs->cp0_epc; + _dest[i++] = (elf_greg_t) _regs->cp0_badvaddr; + _dest[i++] = (elf_greg_t) _regs->cp0_status; + _dest[i++] = (elf_greg_t) _regs->cp0_cause; +} + +MODULE_DESCRIPTION("Binary format loader for compatibility with o32 Linux/MIPS binaries"); +MODULE_AUTHOR("Ralf Baechle (ralf@linux-mips.org)"); + +#undef MODULE_DESCRIPTION +#undef MODULE_AUTHOR + +#include "../../../fs/binfmt_elf.c" diff --git a/arch/mips/kernel/branch.c b/arch/mips/kernel/branch.c new file mode 100644 index 000000000000..01117e977a7f --- /dev/null +++ b/arch/mips/kernel/branch.c @@ -0,0 +1,199 @@ +/* + * 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, 97, 2000, 2001 by Ralf Baechle + * Copyright (C) 2001 MIPS Technologies, Inc. + */ +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <asm/branch.h> +#include <asm/cpu.h> +#include <asm/cpu-features.h> +#include <asm/inst.h> +#include <asm/ptrace.h> +#include <asm/uaccess.h> + +/* + * Compute the return address and do emulate branch 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) + goto unaligned; + + /* + * Read the instruction + */ + addr = (unsigned int *) 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 ((long)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 ((long)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 ((long)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 ((long)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 ((long)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 ((long)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: + if (!cpu_has_fpu) + fcr31 = current->thread.fpu.soft.fcr31; + else + asm volatile("cfc1\t%0,$31" : "=r" (fcr31)); + bit = (insn.i_format.rt >> 2); + bit += (bit != 0); + bit += 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; + +unaligned: + printk("%s: unaligned epc - sending SIGBUS.\n", current->comm); + force_sig(SIGBUS, current); + return -EFAULT; +} diff --git a/arch/mips/kernel/cpu-bugs64.c b/arch/mips/kernel/cpu-bugs64.c new file mode 100644 index 000000000000..11ebe5d4c446 --- /dev/null +++ b/arch/mips/kernel/cpu-bugs64.c @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2003, 2004 Maciej W. Rozycki + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include <linux/config.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/ptrace.h> +#include <linux/stddef.h> + +#include <asm/bugs.h> +#include <asm/compiler.h> +#include <asm/cpu.h> +#include <asm/fpu.h> +#include <asm/mipsregs.h> +#include <asm/system.h> + +static inline void align_mod(const int align, const int mod) +{ + asm volatile( + ".set push\n\t" + ".set noreorder\n\t" + ".balign %0\n\t" + ".rept %1\n\t" + "nop\n\t" + ".endr\n\t" + ".set pop" + : + : "n" (align), "n" (mod)); +} + +static inline void mult_sh_align_mod(long *v1, long *v2, long *w, + const int align, const int mod) +{ + unsigned long flags; + int m1, m2; + long p, s, lv1, lv2, lw; + + /* + * We want the multiply and the shift to be isolated from the + * rest of the code to disable gcc optimizations. Hence the + * asm statements that execute nothing, but make gcc not know + * what the values of m1, m2 and s are and what lv2 and p are + * used for. + */ + + local_irq_save(flags); + /* + * The following code leads to a wrong result of the first + * dsll32 when executed on R4000 rev. 2.2 or 3.0 (PRId + * 00000422 or 00000430, respectively). + * + * See "MIPS R4000PC/SC Errata, Processor Revision 2.2 and + * 3.0" by MIPS Technologies, Inc., errata #16 and #28 for + * details. I got no permission to duplicate them here, + * sigh... --macro + */ + asm volatile( + "" + : "=r" (m1), "=r" (m2), "=r" (s) + : "0" (5), "1" (8), "2" (5)); + align_mod(align, mod); + /* + * The trailing nop is needed to fullfill the two-instruction + * requirement between reading hi/lo and staring a mult/div. + * Leaving it out may cause gas insert a nop itself breaking + * the desired alignment of the next chunk. + */ + asm volatile( + ".set push\n\t" + ".set noat\n\t" + ".set noreorder\n\t" + ".set nomacro\n\t" + "mult %2, %3\n\t" + "dsll32 %0, %4, %5\n\t" + "mflo $0\n\t" + "dsll32 %1, %4, %5\n\t" + "nop\n\t" + ".set pop" + : "=&r" (lv1), "=r" (lw) + : "r" (m1), "r" (m2), "r" (s), "I" (0) + : "hi", "lo", GCC_REG_ACCUM); + /* We have to use single integers for m1 and m2 and a double + * one for p to be sure the mulsidi3 gcc's RTL multiplication + * instruction has the workaround applied. Older versions of + * gcc have correct umulsi3 and mulsi3, but other + * multiplication variants lack the workaround. + */ + asm volatile( + "" + : "=r" (m1), "=r" (m2), "=r" (s) + : "0" (m1), "1" (m2), "2" (s)); + align_mod(align, mod); + p = m1 * m2; + lv2 = s << 32; + asm volatile( + "" + : "=r" (lv2) + : "0" (lv2), "r" (p)); + local_irq_restore(flags); + + *v1 = lv1; + *v2 = lv2; + *w = lw; +} + +static inline void check_mult_sh(void) +{ + long v1[8], v2[8], w[8]; + int bug, fix, i; + + printk("Checking for the multiply/shift bug... "); + + /* + * Testing discovered false negatives for certain code offsets + * into cache lines. Hence we test all possible offsets for + * the worst assumption of an R4000 I-cache line width of 32 + * bytes. + * + * We can't use a loop as alignment directives need to be + * immediates. + */ + mult_sh_align_mod(&v1[0], &v2[0], &w[0], 32, 0); + mult_sh_align_mod(&v1[1], &v2[1], &w[1], 32, 1); + mult_sh_align_mod(&v1[2], &v2[2], &w[2], 32, 2); + mult_sh_align_mod(&v1[3], &v2[3], &w[3], 32, 3); + mult_sh_align_mod(&v1[4], &v2[4], &w[4], 32, 4); + mult_sh_align_mod(&v1[5], &v2[5], &w[5], 32, 5); + mult_sh_align_mod(&v1[6], &v2[6], &w[6], 32, 6); + mult_sh_align_mod(&v1[7], &v2[7], &w[7], 32, 7); + + bug = 0; + for (i = 0; i < 8; i++) + if (v1[i] != w[i]) + bug = 1; + + if (bug == 0) { + printk("no.\n"); + return; + } + + printk("yes, workaround... "); + + fix = 1; + for (i = 0; i < 8; i++) + if (v2[i] != w[i]) + fix = 0; + + if (fix == 1) { + printk("yes.\n"); + return; + } + + printk("no.\n"); + panic("Reliable operation impossible!\n" +#ifndef CONFIG_CPU_R4000 + "Configure for R4000 to enable the workaround." +#else + "Please report to <linux-mips@linux-mips.org>." +#endif + ); +} + +static volatile int daddi_ov __initdata = 0; + +asmlinkage void __init do_daddi_ov(struct pt_regs *regs) +{ + daddi_ov = 1; + regs->cp0_epc += 4; +} + +static inline void check_daddi(void) +{ + extern asmlinkage void handle_daddi_ov(void); + unsigned long flags; + void *handler; + long v, tmp; + + printk("Checking for the daddi bug... "); + + local_irq_save(flags); + handler = set_except_vector(12, handle_daddi_ov); + /* + * The following code fails to trigger an overflow exception + * when executed on R4000 rev. 2.2 or 3.0 (PRId 00000422 or + * 00000430, respectively). + * + * See "MIPS R4000PC/SC Errata, Processor Revision 2.2 and + * 3.0" by MIPS Technologies, Inc., erratum #23 for details. + * I got no permission to duplicate it here, sigh... --macro + */ + asm volatile( + ".set push\n\t" + ".set noat\n\t" + ".set noreorder\n\t" + ".set nomacro\n\t" + "addiu %1, $0, %2\n\t" + "dsrl %1, %1, 1\n\t" +#ifdef HAVE_AS_SET_DADDI + ".set daddi\n\t" +#endif + "daddi %0, %1, %3\n\t" + ".set pop" + : "=r" (v), "=&r" (tmp) + : "I" (0xffffffffffffdb9a), "I" (0x1234)); + set_except_vector(12, handler); + local_irq_restore(flags); + + if (daddi_ov) { + printk("no.\n"); + return; + } + + printk("yes, workaround... "); + + local_irq_save(flags); + handler = set_except_vector(12, handle_daddi_ov); + asm volatile( + "addiu %1, $0, %2\n\t" + "dsrl %1, %1, 1\n\t" + "daddi %0, %1, %3" + : "=r" (v), "=&r" (tmp) + : "I" (0xffffffffffffdb9a), "I" (0x1234)); + set_except_vector(12, handler); + local_irq_restore(flags); + + if (daddi_ov) { + printk("yes.\n"); + return; + } + + printk("no.\n"); + panic("Reliable operation impossible!\n" +#if !defined(CONFIG_CPU_R4000) && !defined(CONFIG_CPU_R4400) + "Configure for R4000 or R4400 to enable the workaround." +#else + "Please report to <linux-mips@linux-mips.org>." +#endif + ); +} + +static inline void check_daddiu(void) +{ + long v, w, tmp; + + printk("Checking for the daddiu bug... "); + + /* + * The following code leads to a wrong result of daddiu when + * executed on R4400 rev. 1.0 (PRId 00000440). + * + * See "MIPS R4400PC/SC Errata, Processor Revision 1.0" by + * MIPS Technologies, Inc., erratum #7 for details. + * + * According to "MIPS R4000PC/SC Errata, Processor Revision + * 2.2 and 3.0" by MIPS Technologies, Inc., erratum #41 this + * problem affects R4000 rev. 2.2 and 3.0 (PRId 00000422 and + * 00000430, respectively), too. Testing failed to trigger it + * so far. + * + * I got no permission to duplicate the errata here, sigh... + * --macro + */ + asm volatile( + ".set push\n\t" + ".set noat\n\t" + ".set noreorder\n\t" + ".set nomacro\n\t" + "addiu %2, $0, %3\n\t" + "dsrl %2, %2, 1\n\t" +#ifdef HAVE_AS_SET_DADDI + ".set daddi\n\t" +#endif + "daddiu %0, %2, %4\n\t" + "addiu %1, $0, %4\n\t" + "daddu %1, %2\n\t" + ".set pop" + : "=&r" (v), "=&r" (w), "=&r" (tmp) + : "I" (0xffffffffffffdb9a), "I" (0x1234)); + + if (v == w) { + printk("no.\n"); + return; + } + + printk("yes, workaround... "); + + asm volatile( + "addiu %2, $0, %3\n\t" + "dsrl %2, %2, 1\n\t" + "daddiu %0, %2, %4\n\t" + "addiu %1, $0, %4\n\t" + "daddu %1, %2" + : "=&r" (v), "=&r" (w), "=&r" (tmp) + : "I" (0xffffffffffffdb9a), "I" (0x1234)); + + if (v == w) { + printk("yes.\n"); + return; + } + + printk("no.\n"); + panic("Reliable operation impossible!\n" +#if !defined(CONFIG_CPU_R4000) && !defined(CONFIG_CPU_R4400) + "Configure for R4000 or R4400 to enable the workaround." +#else + "Please report to <linux-mips@linux-mips.org>." +#endif + ); +} + +void __init check_bugs64(void) +{ + check_mult_sh(); + check_daddi(); + check_daddiu(); +} diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c new file mode 100644 index 000000000000..4bb849582314 --- /dev/null +++ b/arch/mips/kernel/cpu-probe.c @@ -0,0 +1,598 @@ +/* + * Processor capabilities determination functions. + * + * Copyright (C) xxxx the Anonymous + * Copyright (C) 2003 Maciej W. Rozycki + * Copyright (C) 1994 - 2003 Ralf Baechle + * Copyright (C) 2001 MIPS Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include <linux/config.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/ptrace.h> +#include <linux/stddef.h> + +#include <asm/bugs.h> +#include <asm/cpu.h> +#include <asm/fpu.h> +#include <asm/mipsregs.h> +#include <asm/system.h> + +/* + * Not all of the MIPS CPUs have the "wait" instruction available. Moreover, + * the implementation of the "wait" feature differs between CPU families. This + * points to the function that implements CPU specific wait. + * The wait instruction stops the pipeline and reduces the power consumption of + * the CPU very much. + */ +void (*cpu_wait)(void) = NULL; + +static void r3081_wait(void) +{ + unsigned long cfg = read_c0_conf(); + write_c0_conf(cfg | R30XX_CONF_HALT); +} + +static void r39xx_wait(void) +{ + unsigned long cfg = read_c0_conf(); + write_c0_conf(cfg | TX39_CONF_HALT); +} + +static void r4k_wait(void) +{ + __asm__(".set\tmips3\n\t" + "wait\n\t" + ".set\tmips0"); +} + +/* + * The Au1xxx wait is available only if we run CONFIG_PM and + * the timer setup found we had a 32KHz counter available. + * There are still problems with functions that may call au1k_wait + * directly, but that will be discovered pretty quickly. + */ +extern void (*au1k_wait_ptr)(void); + +void au1k_wait(void) +{ +#ifdef CONFIG_PM + /* using the wait instruction makes CP0 counter unusable */ + __asm__(".set\tmips3\n\t" + "wait\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + ".set\tmips0"); +#else + __asm__("nop\n\t" + "nop"); +#endif +} + +static inline void check_wait(void) +{ + struct cpuinfo_mips *c = ¤t_cpu_data; + + printk("Checking for 'wait' instruction... "); + switch (c->cputype) { + case CPU_R3081: + case CPU_R3081E: + cpu_wait = r3081_wait; + printk(" available.\n"); + break; + case CPU_TX3927: + cpu_wait = r39xx_wait; + printk(" available.\n"); + break; + case CPU_R4200: +/* case CPU_R4300: */ + case CPU_R4600: + case CPU_R4640: + case CPU_R4650: + case CPU_R4700: + case CPU_R5000: + case CPU_NEVADA: + case CPU_RM7000: + case CPU_RM9000: + case CPU_TX49XX: + case CPU_4KC: + case CPU_4KEC: + case CPU_4KSC: + case CPU_5KC: +/* case CPU_20KC:*/ + case CPU_24K: + case CPU_25KF: + cpu_wait = r4k_wait; + printk(" available.\n"); + break; +#ifdef CONFIG_PM + case CPU_AU1000: + case CPU_AU1100: + case CPU_AU1500: + if (au1k_wait_ptr != NULL) { + cpu_wait = au1k_wait_ptr; + printk(" available.\n"); + } + else { + printk(" unavailable.\n"); + } + break; +#endif + default: + printk(" unavailable.\n"); + break; + } +} + +void __init check_bugs32(void) +{ + check_wait(); +} + +/* + * Probe whether cpu has config register by trying to play with + * alternate cache bit and see whether it matters. + * It's used by cpu_probe to distinguish between R3000A and R3081. + */ +static inline int cpu_has_confreg(void) +{ +#ifdef CONFIG_CPU_R3000 + extern unsigned long r3k_cache_size(unsigned long); + unsigned long size1, size2; + unsigned long cfg = read_c0_conf(); + + size1 = r3k_cache_size(ST0_ISC); + write_c0_conf(cfg ^ R30XX_CONF_AC); + size2 = r3k_cache_size(ST0_ISC); + write_c0_conf(cfg); + return size1 != size2; +#else + return 0; +#endif +} + +/* + * Get the FPU Implementation/Revision. + */ +static inline unsigned long cpu_get_fpu_id(void) +{ + unsigned long tmp, fpu_id; + + tmp = read_c0_status(); + __enable_fpu(); + fpu_id = read_32bit_cp1_register(CP1_REVISION); + write_c0_status(tmp); + return fpu_id; +} + +/* + * Check the CPU has an FPU the official way. + */ +static inline int __cpu_has_fpu(void) +{ + return ((cpu_get_fpu_id() & 0xff00) != FPIR_IMP_NONE); +} + +#define R4K_OPTS (MIPS_CPU_TLB | MIPS_CPU_4KEX | MIPS_CPU_4KTLB \ + | MIPS_CPU_COUNTER) + +static inline void cpu_probe_legacy(struct cpuinfo_mips *c) +{ + switch (c->processor_id & 0xff00) { + case PRID_IMP_R2000: + c->cputype = CPU_R2000; + c->isa_level = MIPS_CPU_ISA_I; + c->options = MIPS_CPU_TLB | MIPS_CPU_NOFPUEX; + if (__cpu_has_fpu()) + c->options |= MIPS_CPU_FPU; + c->tlbsize = 64; + break; + case PRID_IMP_R3000: + if ((c->processor_id & 0xff) == PRID_REV_R3000A) + if (cpu_has_confreg()) + c->cputype = CPU_R3081E; + else + c->cputype = CPU_R3000A; + else + c->cputype = CPU_R3000; + c->isa_level = MIPS_CPU_ISA_I; + c->options = MIPS_CPU_TLB | MIPS_CPU_NOFPUEX; + if (__cpu_has_fpu()) + c->options |= MIPS_CPU_FPU; + c->tlbsize = 64; + break; + case PRID_IMP_R4000: + if (read_c0_config() & CONF_SC) { + if ((c->processor_id & 0xff) >= PRID_REV_R4400) + c->cputype = CPU_R4400PC; + else + c->cputype = CPU_R4000PC; + } else { + if ((c->processor_id & 0xff) >= PRID_REV_R4400) + c->cputype = CPU_R4400SC; + else + c->cputype = CPU_R4000SC; + } + + c->isa_level = MIPS_CPU_ISA_III; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_WATCH | MIPS_CPU_VCE | + MIPS_CPU_LLSC; + c->tlbsize = 48; + break; + case PRID_IMP_VR41XX: + switch (c->processor_id & 0xf0) { +#ifndef CONFIG_VR4181 + case PRID_REV_VR4111: + c->cputype = CPU_VR4111; + break; +#else + case PRID_REV_VR4181: + c->cputype = CPU_VR4181; + break; +#endif + case PRID_REV_VR4121: + c->cputype = CPU_VR4121; + break; + case PRID_REV_VR4122: + if ((c->processor_id & 0xf) < 0x3) + c->cputype = CPU_VR4122; + else + c->cputype = CPU_VR4181A; + break; + case PRID_REV_VR4130: + if ((c->processor_id & 0xf) < 0x4) + c->cputype = CPU_VR4131; + else + c->cputype = CPU_VR4133; + break; + default: + printk(KERN_INFO "Unexpected CPU of NEC VR4100 series\n"); + c->cputype = CPU_VR41XX; + break; + } + c->isa_level = MIPS_CPU_ISA_III; + c->options = R4K_OPTS; + c->tlbsize = 32; + break; + case PRID_IMP_R4300: + c->cputype = CPU_R4300; + c->isa_level = MIPS_CPU_ISA_III; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_LLSC; + c->tlbsize = 32; + break; + case PRID_IMP_R4600: + c->cputype = CPU_R4600; + c->isa_level = MIPS_CPU_ISA_III; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_LLSC; + c->tlbsize = 48; + break; + #if 0 + case PRID_IMP_R4650: + /* + * This processor doesn't have an MMU, so it's not + * "real easy" to run Linux on it. It is left purely + * for documentation. Commented out because it shares + * it's c0_prid id number with the TX3900. + */ + c->cputype = CPU_R4650; + c->isa_level = MIPS_CPU_ISA_III; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_LLSC; + c->tlbsize = 48; + break; + #endif + case PRID_IMP_TX39: + c->isa_level = MIPS_CPU_ISA_I; + c->options = MIPS_CPU_TLB; + + if ((c->processor_id & 0xf0) == (PRID_REV_TX3927 & 0xf0)) { + c->cputype = CPU_TX3927; + c->tlbsize = 64; + } else { + switch (c->processor_id & 0xff) { + case PRID_REV_TX3912: + c->cputype = CPU_TX3912; + c->tlbsize = 32; + break; + case PRID_REV_TX3922: + c->cputype = CPU_TX3922; + c->tlbsize = 64; + break; + default: + c->cputype = CPU_UNKNOWN; + break; + } + } + break; + case PRID_IMP_R4700: + c->cputype = CPU_R4700; + c->isa_level = MIPS_CPU_ISA_III; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_LLSC; + c->tlbsize = 48; + break; + case PRID_IMP_TX49: + c->cputype = CPU_TX49XX; + c->isa_level = MIPS_CPU_ISA_III; + c->options = R4K_OPTS | MIPS_CPU_LLSC; + if (!(c->processor_id & 0x08)) + c->options |= MIPS_CPU_FPU | MIPS_CPU_32FPR; + c->tlbsize = 48; + break; + case PRID_IMP_R5000: + c->cputype = CPU_R5000; + c->isa_level = MIPS_CPU_ISA_IV; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_LLSC; + c->tlbsize = 48; + break; + case PRID_IMP_R5432: + c->cputype = CPU_R5432; + c->isa_level = MIPS_CPU_ISA_IV; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_WATCH | MIPS_CPU_LLSC; + c->tlbsize = 48; + break; + case PRID_IMP_R5500: + c->cputype = CPU_R5500; + c->isa_level = MIPS_CPU_ISA_IV; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_WATCH | MIPS_CPU_LLSC; + c->tlbsize = 48; + break; + case PRID_IMP_NEVADA: + c->cputype = CPU_NEVADA; + c->isa_level = MIPS_CPU_ISA_IV; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_DIVEC | MIPS_CPU_LLSC; + c->tlbsize = 48; + break; + case PRID_IMP_R6000: + c->cputype = CPU_R6000; + c->isa_level = MIPS_CPU_ISA_II; + c->options = MIPS_CPU_TLB | MIPS_CPU_FPU | + MIPS_CPU_LLSC; + c->tlbsize = 32; + break; + case PRID_IMP_R6000A: + c->cputype = CPU_R6000A; + c->isa_level = MIPS_CPU_ISA_II; + c->options = MIPS_CPU_TLB | MIPS_CPU_FPU | + MIPS_CPU_LLSC; + c->tlbsize = 32; + break; + case PRID_IMP_RM7000: + c->cputype = CPU_RM7000; + c->isa_level = MIPS_CPU_ISA_IV; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_LLSC; + /* + * Undocumented RM7000: Bit 29 in the info register of + * the RM7000 v2.0 indicates if the TLB has 48 or 64 + * entries. + * + * 29 1 => 64 entry JTLB + * 0 => 48 entry JTLB + */ + c->tlbsize = (read_c0_info() & (1 << 29)) ? 64 : 48; + break; + case PRID_IMP_RM9000: + c->cputype = CPU_RM9000; + c->isa_level = MIPS_CPU_ISA_IV; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_LLSC; + /* + * Bit 29 in the info register of the RM9000 + * indicates if the TLB has 48 or 64 entries. + * + * 29 1 => 64 entry JTLB + * 0 => 48 entry JTLB + */ + c->tlbsize = (read_c0_info() & (1 << 29)) ? 64 : 48; + break; + case PRID_IMP_R8000: + c->cputype = CPU_R8000; + c->isa_level = MIPS_CPU_ISA_IV; + c->options = MIPS_CPU_TLB | MIPS_CPU_4KEX | + MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_LLSC; + c->tlbsize = 384; /* has weird TLB: 3-way x 128 */ + break; + case PRID_IMP_R10000: + c->cputype = CPU_R10000; + c->isa_level = MIPS_CPU_ISA_IV; + c->options = MIPS_CPU_TLB | MIPS_CPU_4KEX | + MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_COUNTER | MIPS_CPU_WATCH | + MIPS_CPU_LLSC; + c->tlbsize = 64; + break; + case PRID_IMP_R12000: + c->cputype = CPU_R12000; + c->isa_level = MIPS_CPU_ISA_IV; + c->options = MIPS_CPU_TLB | MIPS_CPU_4KEX | + MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_COUNTER | MIPS_CPU_WATCH | + MIPS_CPU_LLSC; + c->tlbsize = 64; + break; + } +} + +static inline void decode_config1(struct cpuinfo_mips *c) +{ + unsigned long config0 = read_c0_config(); + unsigned long config1; + + if ((config0 & (1 << 31)) == 0) + return; /* actually wort a panic() */ + + /* MIPS32 or MIPS64 compliant CPU. Read Config 1 register. */ + c->options = MIPS_CPU_TLB | MIPS_CPU_4KEX | + MIPS_CPU_4KTLB | MIPS_CPU_COUNTER | MIPS_CPU_DIVEC | + MIPS_CPU_LLSC | MIPS_CPU_MCHECK; + config1 = read_c0_config1(); + if (config1 & (1 << 3)) + c->options |= MIPS_CPU_WATCH; + if (config1 & (1 << 2)) + c->options |= MIPS_CPU_MIPS16; + if (config1 & (1 << 1)) + c->options |= MIPS_CPU_EJTAG; + if (config1 & 1) { + c->options |= MIPS_CPU_FPU; + c->options |= MIPS_CPU_32FPR; + } + c->scache.flags = MIPS_CACHE_NOT_PRESENT; + + c->tlbsize = ((config1 >> 25) & 0x3f) + 1; +} + +static inline void cpu_probe_mips(struct cpuinfo_mips *c) +{ + decode_config1(c); + switch (c->processor_id & 0xff00) { + case PRID_IMP_4KC: + c->cputype = CPU_4KC; + c->isa_level = MIPS_CPU_ISA_M32; + break; + case PRID_IMP_4KEC: + c->cputype = CPU_4KEC; + c->isa_level = MIPS_CPU_ISA_M32; + break; + case PRID_IMP_4KSC: + c->cputype = CPU_4KSC; + c->isa_level = MIPS_CPU_ISA_M32; + break; + case PRID_IMP_5KC: + c->cputype = CPU_5KC; + c->isa_level = MIPS_CPU_ISA_M64; + break; + case PRID_IMP_20KC: + c->cputype = CPU_20KC; + c->isa_level = MIPS_CPU_ISA_M64; + break; + case PRID_IMP_24K: + c->cputype = CPU_24K; + c->isa_level = MIPS_CPU_ISA_M32; + break; + case PRID_IMP_25KF: + c->cputype = CPU_25KF; + c->isa_level = MIPS_CPU_ISA_M64; + /* Probe for L2 cache */ + c->scache.flags &= ~MIPS_CACHE_NOT_PRESENT; + break; + } +} + +static inline void cpu_probe_alchemy(struct cpuinfo_mips *c) +{ + decode_config1(c); + switch (c->processor_id & 0xff00) { + case PRID_IMP_AU1_REV1: + case PRID_IMP_AU1_REV2: + switch ((c->processor_id >> 24) & 0xff) { + case 0: + c->cputype = CPU_AU1000; + break; + case 1: + c->cputype = CPU_AU1500; + break; + case 2: + c->cputype = CPU_AU1100; + break; + case 3: + c->cputype = CPU_AU1550; + break; + default: + panic("Unknown Au Core!"); + break; + } + c->isa_level = MIPS_CPU_ISA_M32; + break; + } +} + +static inline void cpu_probe_sibyte(struct cpuinfo_mips *c) +{ + decode_config1(c); + switch (c->processor_id & 0xff00) { + case PRID_IMP_SB1: + c->cputype = CPU_SB1; + c->isa_level = MIPS_CPU_ISA_M64; + c->options = MIPS_CPU_TLB | MIPS_CPU_4KEX | + MIPS_CPU_COUNTER | MIPS_CPU_DIVEC | + MIPS_CPU_MCHECK | MIPS_CPU_EJTAG | + MIPS_CPU_WATCH | MIPS_CPU_LLSC; +#ifndef CONFIG_SB1_PASS_1_WORKAROUNDS + /* FPU in pass1 is known to have issues. */ + c->options |= MIPS_CPU_FPU | MIPS_CPU_32FPR; +#endif + break; + } +} + +static inline void cpu_probe_sandcraft(struct cpuinfo_mips *c) +{ + decode_config1(c); + switch (c->processor_id & 0xff00) { + case PRID_IMP_SR71000: + c->cputype = CPU_SR71000; + c->isa_level = MIPS_CPU_ISA_M64; + c->options = MIPS_CPU_TLB | MIPS_CPU_4KEX | + MIPS_CPU_4KTLB | MIPS_CPU_FPU | + MIPS_CPU_COUNTER | MIPS_CPU_MCHECK; + c->scache.ways = 8; + c->tlbsize = 64; + break; + } +} + +__init void cpu_probe(void) +{ + struct cpuinfo_mips *c = ¤t_cpu_data; + + c->processor_id = PRID_IMP_UNKNOWN; + c->fpu_id = FPIR_IMP_NONE; + c->cputype = CPU_UNKNOWN; + + c->processor_id = read_c0_prid(); + switch (c->processor_id & 0xff0000) { + case PRID_COMP_LEGACY: + cpu_probe_legacy(c); + break; + case PRID_COMP_MIPS: + cpu_probe_mips(c); + break; + case PRID_COMP_ALCHEMY: + cpu_probe_alchemy(c); + break; + case PRID_COMP_SIBYTE: + cpu_probe_sibyte(c); + break; + + case PRID_COMP_SANDCRAFT: + cpu_probe_sandcraft(c); + break; + default: + c->cputype = CPU_UNKNOWN; + } + if (c->options & MIPS_CPU_FPU) + c->fpu_id = cpu_get_fpu_id(); +} + +__init void cpu_report(void) +{ + struct cpuinfo_mips *c = ¤t_cpu_data; + + printk("CPU revision is: %08x\n", c->processor_id); + if (c->options & MIPS_CPU_FPU) + printk("FPU revision is: %08x\n", c->fpu_id); +} diff --git a/arch/mips/kernel/entry.S b/arch/mips/kernel/entry.S new file mode 100644 index 000000000000..5eb429137e06 --- /dev/null +++ b/arch/mips/kernel/entry.S @@ -0,0 +1,155 @@ +/* + * 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 - 2000, 2001, 2003 Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * Copyright (C) 2001 MIPS Technologies, Inc. + */ +#include <linux/config.h> + +#include <asm/asm.h> +#include <asm/asmmacro.h> +#include <asm/regdef.h> +#include <asm/mipsregs.h> +#include <asm/stackframe.h> +#include <asm/isadep.h> +#include <asm/thread_info.h> +#include <asm/war.h> + +#ifdef CONFIG_PREEMPT + .macro preempt_stop reg=t0 + .endm +#else + .macro preempt_stop reg=t0 + local_irq_disable \reg + .endm +#define resume_kernel restore_all +#endif + + .text + .align 5 +FEXPORT(ret_from_exception) + preempt_stop +FEXPORT(ret_from_irq) + LONG_L t0, PT_STATUS(sp) # returning to kernel mode? + andi t0, t0, KU_USER + beqz t0, resume_kernel + +FEXPORT(resume_userspace) + local_irq_disable t0 # make sure we dont miss an + # interrupt setting need_resched + # between sampling and return + LONG_L a2, TI_FLAGS($28) # current->work + andi a2, _TIF_WORK_MASK # (ignoring syscall_trace) + bnez a2, work_pending + j restore_all + +#ifdef CONFIG_PREEMPT +ENTRY(resume_kernel) + lw t0, TI_PRE_COUNT($28) + bnez t0, restore_all +need_resched: + LONG_L t0, TI_FLAGS($28) + andi t1, t0, _TIF_NEED_RESCHED + beqz t1, restore_all + LONG_L t0, PT_STATUS(sp) # Interrupts off? + andi t0, 1 + beqz t0, restore_all + li t0, PREEMPT_ACTIVE + sw t0, TI_PRE_COUNT($28) + local_irq_enable t0 + jal schedule + sw zero, TI_PRE_COUNT($28) + local_irq_disable t0 + b need_resched +#endif + +FEXPORT(ret_from_fork) + jal schedule_tail # a0 = task_t *prev + +FEXPORT(syscall_exit) + local_irq_disable # make sure need_resched and + # signals dont change between + # sampling and return + LONG_L a2, TI_FLAGS($28) # current->work + li t0, _TIF_ALLWORK_MASK + and t0, a2, t0 + bnez t0, syscall_exit_work + +FEXPORT(restore_all) # restore full frame + .set noat + RESTORE_TEMP + RESTORE_AT + RESTORE_STATIC +FEXPORT(restore_partial) # restore partial frame + RESTORE_SOME + RESTORE_SP_AND_RET + .set at + +FEXPORT(work_pending) + andi t0, a2, _TIF_NEED_RESCHED + beqz t0, work_notifysig +work_resched: + jal schedule + + local_irq_disable t0 # make sure need_resched and + # signals dont change between + # sampling and return + LONG_L a2, TI_FLAGS($28) + andi t0, a2, _TIF_WORK_MASK # is there any work to be done + # other than syscall tracing? + beqz t0, restore_all + andi t0, a2, _TIF_NEED_RESCHED + bnez t0, work_resched + +work_notifysig: # deal with pending signals and + # notify-resume requests + move a0, sp + li a1, 0 + jal do_notify_resume # a2 already loaded + j restore_all + +FEXPORT(syscall_exit_work_partial) + SAVE_STATIC +FEXPORT(syscall_exit_work) + LONG_L t0, TI_FLAGS($28) + li t1, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT + and t0, t1 + beqz t0, work_pending # trace bit is set + local_irq_enable # could let do_syscall_trace() + # call schedule() instead + move a0, sp + li a1, 1 + jal do_syscall_trace + b resume_userspace + +/* + * Common spurious interrupt handler. + */ + .text + .align 5 +LEAF(spurious_interrupt) + /* + * Someone tried to fool us by sending an interrupt but we + * couldn't find a cause for it. + */ +#ifdef CONFIG_SMP + lui t1, %hi(irq_err_count) +1: ll t0, %lo(irq_err_count)(t1) + addiu t0, 1 + sc t0, %lo(irq_err_count)(t1) +#if R10000_LLSC_WAR + beqzl t0, 1b +#else + beqz t0, 1b +#endif +#else + lui t1, %hi(irq_err_count) + lw t0, %lo(irq_err_count)(t1) + addiu t0, 1 + sw t0, %lo(irq_err_count)(t1) +#endif + j ret_from_irq + END(spurious_interrupt) diff --git a/arch/mips/kernel/gdb-low.S b/arch/mips/kernel/gdb-low.S new file mode 100644 index 000000000000..ece6ddaf7011 --- /dev/null +++ b/arch/mips/kernel/gdb-low.S @@ -0,0 +1,370 @@ +/* + * gdb-low.S contains the low-level trap handler for the GDB stub. + * + * Copyright (C) 1995 Andreas Busse + */ +#include <linux/config.h> +#include <linux/sys.h> + +#include <asm/asm.h> +#include <asm/errno.h> +#include <asm/mipsregs.h> +#include <asm/regdef.h> +#include <asm/stackframe.h> +#include <asm/gdb-stub.h> + +#ifdef CONFIG_MIPS32 +#define DMFC0 mfc0 +#define DMTC0 mtc0 +#define LDC1 lwc1 +#define SDC1 lwc1 +#endif +#ifdef CONFIG_MIPS64 +#define DMFC0 dmfc0 +#define DMTC0 dmtc0 +#define LDC1 ldc1 +#define SDC1 ldc1 +#endif + +/* + * [jsun] We reserves about 2x GDB_FR_SIZE in stack. The lower (addressed) + * part is used to store registers and passed to exception handler. + * The upper part is reserved for "call func" feature where gdb client + * saves some of the regs, setups call frame and passes args. + * + * A trace shows about 200 bytes are used to store about half of all regs. + * The rest should be big enough for frame setup and passing args. + */ + +/* + * The low level trap handler + */ + .align 5 + NESTED(trap_low, GDB_FR_SIZE, sp) + .set noat + .set noreorder + + mfc0 k0, CP0_STATUS + sll k0, 3 /* extract cu0 bit */ + bltz k0, 1f + move k1, sp + + /* + * Called from user mode, go somewhere else. + */ + lui k1, %hi(saved_vectors) + mfc0 k0, CP0_CAUSE + andi k0, k0, 0x7c + add k1, k1, k0 + lw k0, %lo(saved_vectors)(k1) + jr k0 + nop +1: + move k0, sp + subu sp, k1, GDB_FR_SIZE*2 # see comment above + LONG_S k0, GDB_FR_REG29(sp) + LONG_S $2, GDB_FR_REG2(sp) + +/* + * First save the CP0 and special registers + */ + + mfc0 v0, CP0_STATUS + LONG_S v0, GDB_FR_STATUS(sp) + mfc0 v0, CP0_CAUSE + LONG_S v0, GDB_FR_CAUSE(sp) + DMFC0 v0, CP0_EPC + LONG_S v0, GDB_FR_EPC(sp) + DMFC0 v0, CP0_BADVADDR + LONG_S v0, GDB_FR_BADVADDR(sp) + mfhi v0 + LONG_S v0, GDB_FR_HI(sp) + mflo v0 + LONG_S v0, GDB_FR_LO(sp) + +/* + * Now the integer registers + */ + + LONG_S zero, GDB_FR_REG0(sp) /* I know... */ + LONG_S $1, GDB_FR_REG1(sp) + /* v0 already saved */ + LONG_S $3, GDB_FR_REG3(sp) + LONG_S $4, GDB_FR_REG4(sp) + LONG_S $5, GDB_FR_REG5(sp) + LONG_S $6, GDB_FR_REG6(sp) + LONG_S $7, GDB_FR_REG7(sp) + LONG_S $8, GDB_FR_REG8(sp) + LONG_S $9, GDB_FR_REG9(sp) + LONG_S $10, GDB_FR_REG10(sp) + LONG_S $11, GDB_FR_REG11(sp) + LONG_S $12, GDB_FR_REG12(sp) + LONG_S $13, GDB_FR_REG13(sp) + LONG_S $14, GDB_FR_REG14(sp) + LONG_S $15, GDB_FR_REG15(sp) + LONG_S $16, GDB_FR_REG16(sp) + LONG_S $17, GDB_FR_REG17(sp) + LONG_S $18, GDB_FR_REG18(sp) + LONG_S $19, GDB_FR_REG19(sp) + LONG_S $20, GDB_FR_REG20(sp) + LONG_S $21, GDB_FR_REG21(sp) + LONG_S $22, GDB_FR_REG22(sp) + LONG_S $23, GDB_FR_REG23(sp) + LONG_S $24, GDB_FR_REG24(sp) + LONG_S $25, GDB_FR_REG25(sp) + LONG_S $26, GDB_FR_REG26(sp) + LONG_S $27, GDB_FR_REG27(sp) + LONG_S $28, GDB_FR_REG28(sp) + /* sp already saved */ + LONG_S $30, GDB_FR_REG30(sp) + LONG_S $31, GDB_FR_REG31(sp) + + CLI /* disable interrupts */ + +/* + * Followed by the floating point registers + */ + mfc0 v0, CP0_STATUS /* FPU enabled? */ + srl v0, v0, 16 + andi v0, v0, (ST0_CU1 >> 16) + + beqz v0,2f /* disabled, skip */ + nop + + SDC1 $0, GDB_FR_FPR0(sp) + SDC1 $1, GDB_FR_FPR1(sp) + SDC1 $2, GDB_FR_FPR2(sp) + SDC1 $3, GDB_FR_FPR3(sp) + SDC1 $4, GDB_FR_FPR4(sp) + SDC1 $5, GDB_FR_FPR5(sp) + SDC1 $6, GDB_FR_FPR6(sp) + SDC1 $7, GDB_FR_FPR7(sp) + SDC1 $8, GDB_FR_FPR8(sp) + SDC1 $9, GDB_FR_FPR9(sp) + SDC1 $10, GDB_FR_FPR10(sp) + SDC1 $11, GDB_FR_FPR11(sp) + SDC1 $12, GDB_FR_FPR12(sp) + SDC1 $13, GDB_FR_FPR13(sp) + SDC1 $14, GDB_FR_FPR14(sp) + SDC1 $15, GDB_FR_FPR15(sp) + SDC1 $16, GDB_FR_FPR16(sp) + SDC1 $17, GDB_FR_FPR17(sp) + SDC1 $18, GDB_FR_FPR18(sp) + SDC1 $19, GDB_FR_FPR19(sp) + SDC1 $20, GDB_FR_FPR20(sp) + SDC1 $21, GDB_FR_FPR21(sp) + SDC1 $22, GDB_FR_FPR22(sp) + SDC1 $23, GDB_FR_FPR23(sp) + SDC1 $24, GDB_FR_FPR24(sp) + SDC1 $25, GDB_FR_FPR25(sp) + SDC1 $26, GDB_FR_FPR26(sp) + SDC1 $27, GDB_FR_FPR27(sp) + SDC1 $28, GDB_FR_FPR28(sp) + SDC1 $29, GDB_FR_FPR29(sp) + SDC1 $30, GDB_FR_FPR30(sp) + SDC1 $31, GDB_FR_FPR31(sp) + +/* + * FPU control registers + */ + + cfc1 v0, CP1_STATUS + LONG_S v0, GDB_FR_FSR(sp) + cfc1 v0, CP1_REVISION + LONG_S v0, GDB_FR_FIR(sp) + +/* + * Current stack frame ptr + */ + +2: + LONG_S sp, GDB_FR_FRP(sp) + +/* + * CP0 registers (R4000/R4400 unused registers skipped) + */ + + mfc0 v0, CP0_INDEX + LONG_S v0, GDB_FR_CP0_INDEX(sp) + mfc0 v0, CP0_RANDOM + LONG_S v0, GDB_FR_CP0_RANDOM(sp) + DMFC0 v0, CP0_ENTRYLO0 + LONG_S v0, GDB_FR_CP0_ENTRYLO0(sp) + DMFC0 v0, CP0_ENTRYLO1 + LONG_S v0, GDB_FR_CP0_ENTRYLO1(sp) + DMFC0 v0, CP0_CONTEXT + LONG_S v0, GDB_FR_CP0_CONTEXT(sp) + mfc0 v0, CP0_PAGEMASK + LONG_S v0, GDB_FR_CP0_PAGEMASK(sp) + mfc0 v0, CP0_WIRED + LONG_S v0, GDB_FR_CP0_WIRED(sp) + DMFC0 v0, CP0_ENTRYHI + LONG_S v0, GDB_FR_CP0_ENTRYHI(sp) + mfc0 v0, CP0_PRID + LONG_S v0, GDB_FR_CP0_PRID(sp) + + .set at + +/* + * Continue with the higher level handler + */ + + move a0,sp + + jal handle_exception + nop + +/* + * Restore all writable registers, in reverse order + */ + + .set noat + + LONG_L v0, GDB_FR_CP0_ENTRYHI(sp) + LONG_L v1, GDB_FR_CP0_WIRED(sp) + DMTC0 v0, CP0_ENTRYHI + mtc0 v1, CP0_WIRED + LONG_L v0, GDB_FR_CP0_PAGEMASK(sp) + LONG_L v1, GDB_FR_CP0_ENTRYLO1(sp) + mtc0 v0, CP0_PAGEMASK + DMTC0 v1, CP0_ENTRYLO1 + LONG_L v0, GDB_FR_CP0_ENTRYLO0(sp) + LONG_L v1, GDB_FR_CP0_INDEX(sp) + DMTC0 v0, CP0_ENTRYLO0 + LONG_L v0, GDB_FR_CP0_CONTEXT(sp) + mtc0 v1, CP0_INDEX + DMTC0 v0, CP0_CONTEXT + + +/* + * Next, the floating point registers + */ + mfc0 v0, CP0_STATUS /* check if the FPU is enabled */ + srl v0, v0, 16 + andi v0, v0, (ST0_CU1 >> 16) + + beqz v0, 3f /* disabled, skip */ + nop + + LDC1 $31, GDB_FR_FPR31(sp) + LDC1 $30, GDB_FR_FPR30(sp) + LDC1 $29, GDB_FR_FPR29(sp) + LDC1 $28, GDB_FR_FPR28(sp) + LDC1 $27, GDB_FR_FPR27(sp) + LDC1 $26, GDB_FR_FPR26(sp) + LDC1 $25, GDB_FR_FPR25(sp) + LDC1 $24, GDB_FR_FPR24(sp) + LDC1 $23, GDB_FR_FPR23(sp) + LDC1 $22, GDB_FR_FPR22(sp) + LDC1 $21, GDB_FR_FPR21(sp) + LDC1 $20, GDB_FR_FPR20(sp) + LDC1 $19, GDB_FR_FPR19(sp) + LDC1 $18, GDB_FR_FPR18(sp) + LDC1 $17, GDB_FR_FPR17(sp) + LDC1 $16, GDB_FR_FPR16(sp) + LDC1 $15, GDB_FR_FPR15(sp) + LDC1 $14, GDB_FR_FPR14(sp) + LDC1 $13, GDB_FR_FPR13(sp) + LDC1 $12, GDB_FR_FPR12(sp) + LDC1 $11, GDB_FR_FPR11(sp) + LDC1 $10, GDB_FR_FPR10(sp) + LDC1 $9, GDB_FR_FPR9(sp) + LDC1 $8, GDB_FR_FPR8(sp) + LDC1 $7, GDB_FR_FPR7(sp) + LDC1 $6, GDB_FR_FPR6(sp) + LDC1 $5, GDB_FR_FPR5(sp) + LDC1 $4, GDB_FR_FPR4(sp) + LDC1 $3, GDB_FR_FPR3(sp) + LDC1 $2, GDB_FR_FPR2(sp) + LDC1 $1, GDB_FR_FPR1(sp) + LDC1 $0, GDB_FR_FPR0(sp) + +/* + * Now the CP0 and integer registers + */ + +3: + mfc0 t0, CP0_STATUS + ori t0, 0x1f + xori t0, 0x1f + mtc0 t0, CP0_STATUS + + LONG_L v0, GDB_FR_STATUS(sp) + LONG_L v1, GDB_FR_EPC(sp) + mtc0 v0, CP0_STATUS + DMTC0 v1, CP0_EPC + LONG_L v0, GDB_FR_HI(sp) + LONG_L v1, GDB_FR_LO(sp) + mthi v0 + mtlo v1 + LONG_L $31, GDB_FR_REG31(sp) + LONG_L $30, GDB_FR_REG30(sp) + LONG_L $28, GDB_FR_REG28(sp) + LONG_L $27, GDB_FR_REG27(sp) + LONG_L $26, GDB_FR_REG26(sp) + LONG_L $25, GDB_FR_REG25(sp) + LONG_L $24, GDB_FR_REG24(sp) + LONG_L $23, GDB_FR_REG23(sp) + LONG_L $22, GDB_FR_REG22(sp) + LONG_L $21, GDB_FR_REG21(sp) + LONG_L $20, GDB_FR_REG20(sp) + LONG_L $19, GDB_FR_REG19(sp) + LONG_L $18, GDB_FR_REG18(sp) + LONG_L $17, GDB_FR_REG17(sp) + LONG_L $16, GDB_FR_REG16(sp) + LONG_L $15, GDB_FR_REG15(sp) + LONG_L $14, GDB_FR_REG14(sp) + LONG_L $13, GDB_FR_REG13(sp) + LONG_L $12, GDB_FR_REG12(sp) + LONG_L $11, GDB_FR_REG11(sp) + LONG_L $10, GDB_FR_REG10(sp) + LONG_L $9, GDB_FR_REG9(sp) + LONG_L $8, GDB_FR_REG8(sp) + LONG_L $7, GDB_FR_REG7(sp) + LONG_L $6, GDB_FR_REG6(sp) + LONG_L $5, GDB_FR_REG5(sp) + LONG_L $4, GDB_FR_REG4(sp) + LONG_L $3, GDB_FR_REG3(sp) + LONG_L $2, GDB_FR_REG2(sp) + LONG_L $1, GDB_FR_REG1(sp) +#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) + LONG_L k0, GDB_FR_EPC(sp) + LONG_L $29, GDB_FR_REG29(sp) /* Deallocate stack */ + jr k0 + rfe +#else + LONG_L sp, GDB_FR_REG29(sp) /* Deallocate stack */ + + .set mips3 + eret + .set mips0 +#endif + .set at + .set reorder + END(trap_low) + +LEAF(kgdb_read_byte) +4: lb t0, (a0) + sb t0, (a1) + li v0, 0 + jr ra + .section __ex_table,"a" + PTR 4b, kgdbfault + .previous + END(kgdb_read_byte) + +LEAF(kgdb_write_byte) +5: sb a0, (a1) + li v0, 0 + jr ra + .section __ex_table,"a" + PTR 5b, kgdbfault + .previous + END(kgdb_write_byte) + + .type kgdbfault@function + .ent kgdbfault + +kgdbfault: li v0, -EFAULT + jr ra + .end kgdbfault diff --git a/arch/mips/kernel/gdb-stub.c b/arch/mips/kernel/gdb-stub.c new file mode 100644 index 000000000000..269889302a27 --- /dev/null +++ b/arch/mips/kernel/gdb-stub.c @@ -0,0 +1,1091 @@ +/* + * arch/mips/kernel/gdb-stub.c + * + * Originally written by Glenn Engel, Lake Stevens Instrument Division + * + * Contributed by HP Systems + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse + * Send complaints, suggestions etc. to <andy@waldorf-gmbh.de> + * + * Copyright (C) 1995 Andreas Busse + * + * Copyright (C) 2003 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + */ + +/* + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a BREAK instruction. + * + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * bBB..BB Set baud rate to BB..BB OK or BNN, then sets + * baud rate + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $<packet info>#<checksum>. + * + * where + * <packet info> :: <characters representing the command or response> + * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>> + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + * + * ============== + * MORE EXAMPLES: + * ============== + * + * For reference -- the following are the steps that one + * company took (RidgeRun Inc) to get remote gdb debugging + * going. In this scenario the host machine was a PC and the + * target platform was a Galileo EVB64120A MIPS evaluation + * board. + * + * Step 1: + * First download gdb-5.0.tar.gz from the internet. + * and then build/install the package. + * + * Example: + * $ tar zxf gdb-5.0.tar.gz + * $ cd gdb-5.0 + * $ ./configure --target=mips-linux-elf + * $ make + * $ install + * $ which mips-linux-elf-gdb + * /usr/local/bin/mips-linux-elf-gdb + * + * Step 2: + * Configure linux for remote debugging and build it. + * + * Example: + * $ cd ~/linux + * $ make menuconfig <go to "Kernel Hacking" and turn on remote debugging> + * $ make + * + * Step 3: + * Download the kernel to the remote target and start + * the kernel running. It will promptly halt and wait + * for the host gdb session to connect. It does this + * since the "Kernel Hacking" option has defined + * CONFIG_KGDB which in turn enables your calls + * to: + * set_debug_traps(); + * breakpoint(); + * + * Step 4: + * Start the gdb session on the host. + * + * Example: + * $ mips-linux-elf-gdb vmlinux + * (gdb) set remotebaud 115200 + * (gdb) target remote /dev/ttyS1 + * ...at this point you are connected to + * the remote target and can use gdb + * in the normal fasion. Setting + * breakpoints, single stepping, + * printing variables, etc. + */ +#include <linux/config.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/spinlock.h> +#include <linux/slab.h> +#include <linux/reboot.h> + +#include <asm/asm.h> +#include <asm/cacheflush.h> +#include <asm/mipsregs.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/gdb-stub.h> +#include <asm/inst.h> + +/* + * external low-level support routines + */ + +extern int putDebugChar(char c); /* write a single character */ +extern char getDebugChar(void); /* read and return a single char */ +extern void trap_low(void); + +/* + * breakpoint and test functions + */ +extern void breakpoint(void); +extern void breakinst(void); +extern void async_breakpoint(void); +extern void async_breakinst(void); +extern void adel(void); + +/* + * local prototypes + */ + +static void getpacket(char *buffer); +static void putpacket(char *buffer); +static int computeSignal(int tt); +static int hex(unsigned char ch); +static int hexToInt(char **ptr, int *intValue); +static int hexToLong(char **ptr, long *longValue); +static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault); +void handle_exception(struct gdb_regs *regs); + +int kgdb_enabled; + +/* + * spin locks for smp case + */ +static spinlock_t kgdb_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t kgdb_cpulock[NR_CPUS] = { [0 ... NR_CPUS-1] = SPIN_LOCK_UNLOCKED}; + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound buffers + * at least NUMREGBYTES*2 are needed for register packets + */ +#define BUFMAX 2048 + +static char input_buffer[BUFMAX]; +static char output_buffer[BUFMAX]; +static int initialized; /* !0 means we've been initialized */ +static int kgdb_started; +static const char hexchars[]="0123456789abcdef"; + +/* Used to prevent crashes in memory access. Note that they'll crash anyway if + we haven't set up fault handlers yet... */ +int kgdb_read_byte(unsigned char *address, unsigned char *dest); +int kgdb_write_byte(unsigned char val, unsigned char *dest); + +/* + * Convert ch from a hex digit to an int + */ +static int hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +/* + * scan for the sequence $<data>#<checksum> + */ +static void getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + unsigned char ch; + + do { + /* + * wait around for the start character, + * ignore all other characters + */ + while ((ch = (getDebugChar() & 0x7f)) != '$') ; + + checksum = 0; + xmitcsum = -1; + count = 0; + + /* + * now, read until a # or end of buffer is found + */ + while (count < BUFMAX) { + ch = getDebugChar(); + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + if (count >= BUFMAX) + continue; + + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum |= hex(getDebugChar() & 0x7f); + + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + + /* + * if a sequence char is present, + * reply the sequence ID + */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + + /* + * remove sequence chars from buffer + */ + count = strlen(buffer); + for (i=3; i <= count; i++) + buffer[i-3] = buffer[i]; + } + } + } + } + while (checksum != xmitcsum); +} + +/* + * send the packet in buffer. + */ +static void putpacket(char *buffer) +{ + unsigned char checksum; + int count; + unsigned char ch; + + /* + * $<packet info>#<checksum>. + */ + + do { + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count]) != 0) { + if (!(putDebugChar(ch))) + return; + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum & 0xf]); + + } + while ((getDebugChar() & 0x7f) != '+'); +} + + +/* + * Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null), in case of mem fault, + * return 0. + * may_fault is non-zero if we are reading from arbitrary memory, but is currently + * not used. + */ +static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault) +{ + unsigned char ch; + + while (count-- > 0) { + if (kgdb_read_byte(mem++, &ch) != 0) + return 0; + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch & 0xf]; + } + + *buf = 0; + + return buf; +} + +/* + * convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte written + * may_fault is non-zero if we are reading from arbitrary memory, but is currently + * not used. + */ +static char *hex2mem(char *buf, char *mem, int count, int binary, int may_fault) +{ + int i; + unsigned char ch; + + for (i=0; i<count; i++) + { + if (binary) { + ch = *buf++; + if (ch == 0x7d) + ch = 0x20 ^ *buf++; + } + else { + ch = hex(*buf++) << 4; + ch |= hex(*buf++); + } + if (kgdb_write_byte(ch, mem++) != 0) + return 0; + } + + return mem; +} + +/* + * This table contains the mapping between SPARC hardware trap types, and + * signals, which are primarily what GDB understands. It also indicates + * which hardware traps we need to commandeer when initializing the stub. + */ +static struct hard_trap_info { + unsigned char tt; /* Trap type code for MIPS R3xxx and R4xxx */ + unsigned char signo; /* Signal that we map this trap into */ +} hard_trap_info[] = { + { 6, SIGBUS }, /* instruction bus error */ + { 7, SIGBUS }, /* data bus error */ + { 9, SIGTRAP }, /* break */ + { 10, SIGILL }, /* reserved instruction */ +/* { 11, SIGILL }, */ /* CPU unusable */ + { 12, SIGFPE }, /* overflow */ + { 13, SIGTRAP }, /* trap */ + { 14, SIGSEGV }, /* virtual instruction cache coherency */ + { 15, SIGFPE }, /* floating point exception */ + { 23, SIGSEGV }, /* watch */ + { 31, SIGSEGV }, /* virtual data cache coherency */ + { 0, 0} /* Must be last */ +}; + +/* Save the normal trap handlers for user-mode traps. */ +void *saved_vectors[32]; + +/* + * Set up exception handlers for tracing and breakpoints + */ +void set_debug_traps(void) +{ + struct hard_trap_info *ht; + unsigned long flags; + unsigned char c; + + local_irq_save(flags); + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + saved_vectors[ht->tt] = set_except_vector(ht->tt, trap_low); + + putDebugChar('+'); /* 'hello world' */ + /* + * In case GDB is started before us, ack any packets + * (presumably "$?#xx") sitting there. + */ + while((c = getDebugChar()) != '$'); + while((c = getDebugChar()) != '#'); + c = getDebugChar(); /* eat first csum byte */ + c = getDebugChar(); /* eat second csum byte */ + putDebugChar('+'); /* ack it */ + + initialized = 1; + local_irq_restore(flags); +} + +void restore_debug_traps(void) +{ + struct hard_trap_info *ht; + unsigned long flags; + + local_irq_save(flags); + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + set_except_vector(ht->tt, saved_vectors[ht->tt]); + local_irq_restore(flags); +} + +/* + * Convert the MIPS hardware trap type code to a Unix signal number. + */ +static int computeSignal(int tt) +{ + struct hard_trap_info *ht; + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + if (ht->tt == tt) + return ht->signo; + + return SIGHUP; /* default for things we don't know about */ +} + +/* + * While we find nice hex chars, build an int. + * Return number of chars processed. + */ +static int hexToInt(char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) { + hexValue = hex(**ptr); + if (hexValue < 0) + break; + + *intValue = (*intValue << 4) | hexValue; + numChars ++; + + (*ptr)++; + } + + return (numChars); +} + +static int hexToLong(char **ptr, long *longValue) +{ + int numChars = 0; + int hexValue; + + *longValue = 0; + + while (**ptr) { + hexValue = hex(**ptr); + if (hexValue < 0) + break; + + *longValue = (*longValue << 4) | hexValue; + numChars ++; + + (*ptr)++; + } + + return numChars; +} + + +#if 0 +/* + * Print registers (on target console) + * Used only to debug the stub... + */ +void show_gdbregs(struct gdb_regs * regs) +{ + /* + * Saved main processor registers + */ + printk("$0 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + regs->reg0, 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 %08lx %08lx\n", + regs->reg24, regs->reg25, regs->reg26, regs->reg27, + regs->reg28, regs->reg29, regs->reg30, regs->reg31); + + /* + * Saved cp0 registers + */ + printk("epc : %08lx\nStatus: %08lx\nCause : %08lx\n", + regs->cp0_epc, regs->cp0_status, regs->cp0_cause); +} +#endif /* dead code */ + +/* + * We single-step by setting breakpoints. When an exception + * is handled, we need to restore the instructions hoisted + * when the breakpoints were set. + * + * This is where we save the original instructions. + */ +static struct gdb_bp_save { + unsigned long addr; + unsigned int val; +} step_bp[2]; + +#define BP 0x0000000d /* break opcode */ + +/* + * Set breakpoint instructions for single stepping. + */ +static void single_step(struct gdb_regs *regs) +{ + union mips_instruction insn; + unsigned long targ; + int is_branch, is_cond, i; + + targ = regs->cp0_epc; + insn.word = *(unsigned int *)targ; + is_branch = is_cond = 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: + case jr_op: + targ = *(®s->reg0 + insn.r_format.rs); + is_branch = 1; + break; + } + break; + + /* + * This group contains: + * bltz_op, bgez_op, bltzl_op, bgezl_op, + * bltzal_op, bgezal_op, bltzall_op, bgezall_op. + */ + case bcond_op: + is_branch = is_cond = 1; + targ += 4 + (insn.i_format.simmediate << 2); + break; + + /* + * These are unconditional and in j_format. + */ + case jal_op: + case j_op: + is_branch = 1; + targ += 4; + targ >>= 28; + targ <<= 28; + targ |= (insn.j_format.target << 2); + break; + + /* + * These are conditional. + */ + case beq_op: + case beql_op: + case bne_op: + case bnel_op: + case blez_op: + case blezl_op: + case bgtz_op: + case bgtzl_op: + case cop0_op: + case cop1_op: + case cop2_op: + case cop1x_op: + is_branch = is_cond = 1; + targ += 4 + (insn.i_format.simmediate << 2); + break; + } + + if (is_branch) { + i = 0; + if (is_cond && targ != (regs->cp0_epc + 8)) { + step_bp[i].addr = regs->cp0_epc + 8; + step_bp[i++].val = *(unsigned *)(regs->cp0_epc + 8); + *(unsigned *)(regs->cp0_epc + 8) = BP; + } + step_bp[i].addr = targ; + step_bp[i].val = *(unsigned *)targ; + *(unsigned *)targ = BP; + } else { + step_bp[0].addr = regs->cp0_epc + 4; + step_bp[0].val = *(unsigned *)(regs->cp0_epc + 4); + *(unsigned *)(regs->cp0_epc + 4) = BP; + } +} + +/* + * If asynchronously interrupted by gdb, then we need to set a breakpoint + * at the interrupted instruction so that we wind up stopped with a + * reasonable stack frame. + */ +static struct gdb_bp_save async_bp; + +/* + * Swap the interrupted EPC with our asynchronous breakpoint routine. + * This is safer than stuffing the breakpoint in-place, since no cache + * flushes (or resulting smp_call_functions) are required. The + * assumption is that only one CPU will be handling asynchronous bp's, + * and only one can be active at a time. + */ +extern spinlock_t smp_call_lock; +void set_async_breakpoint(unsigned long *epc) +{ + /* skip breaking into userland */ + if ((*epc & 0x80000000) == 0) + return; + + /* avoid deadlock if someone is make IPC */ + if (spin_is_locked(&smp_call_lock)) + return; + + async_bp.addr = *epc; + *epc = (unsigned long)async_breakpoint; +} + +void kgdb_wait(void *arg) +{ + unsigned flags; + int cpu = smp_processor_id(); + + local_irq_save(flags); + + spin_lock(&kgdb_cpulock[cpu]); + spin_unlock(&kgdb_cpulock[cpu]); + + local_irq_restore(flags); +} + + +/* + * 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. + */ +void handle_exception (struct gdb_regs *regs) +{ + int trap; /* Trap type */ + int sigval; + long addr; + int length; + char *ptr; + unsigned long *stack; + int i; + int bflag = 0; + + kgdb_started = 1; + + /* + * acquire the big kgdb spinlock + */ + if (!spin_trylock(&kgdb_lock)) { + /* + * some other CPU has the lock, we should go back to + * receive the gdb_wait IPC + */ + return; + } + + /* + * If we're in async_breakpoint(), restore the real EPC from + * the breakpoint. + */ + if (regs->cp0_epc == (unsigned long)async_breakinst) { + regs->cp0_epc = async_bp.addr; + async_bp.addr = 0; + } + + /* + * acquire the CPU spinlocks + */ + for (i = num_online_cpus()-1; i >= 0; i--) + if (spin_trylock(&kgdb_cpulock[i]) == 0) + panic("kgdb: couldn't get cpulock %d\n", i); + + /* + * force other cpus to enter kgdb + */ + smp_call_function(kgdb_wait, NULL, 0, 0); + + /* + * If we're in breakpoint() increment the PC + */ + trap = (regs->cp0_cause & 0x7c) >> 2; + if (trap == 9 && regs->cp0_epc == (unsigned long)breakinst) + regs->cp0_epc += 4; + + /* + * If we were single_stepping, restore the opcodes hoisted + * for the breakpoint[s]. + */ + if (step_bp[0].addr) { + *(unsigned *)step_bp[0].addr = step_bp[0].val; + step_bp[0].addr = 0; + + if (step_bp[1].addr) { + *(unsigned *)step_bp[1].addr = step_bp[1].val; + step_bp[1].addr = 0; + } + } + + stack = (long *)regs->reg29; /* stack ptr */ + sigval = computeSignal(trap); + + /* + * reply to host that an exception has occurred + */ + ptr = output_buffer; + + /* + * Send trap type (converted to signal) + */ + *ptr++ = 'T'; + *ptr++ = hexchars[sigval >> 4]; + *ptr++ = hexchars[sigval & 0xf]; + + /* + * Send Error PC + */ + *ptr++ = hexchars[REG_EPC >> 4]; + *ptr++ = hexchars[REG_EPC & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)®s->cp0_epc, ptr, sizeof(long), 0); + *ptr++ = ';'; + + /* + * Send frame pointer + */ + *ptr++ = hexchars[REG_FP >> 4]; + *ptr++ = hexchars[REG_FP & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)®s->reg30, ptr, sizeof(long), 0); + *ptr++ = ';'; + + /* + * Send stack pointer + */ + *ptr++ = hexchars[REG_SP >> 4]; + *ptr++ = hexchars[REG_SP & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)®s->reg29, ptr, sizeof(long), 0); + *ptr++ = ';'; + + *ptr++ = 0; + putpacket(output_buffer); /* send it off... */ + + /* + * Wait for input from remote GDB + */ + while (1) { + output_buffer[0] = 0; + getpacket(input_buffer); + + switch (input_buffer[0]) + { + case '?': + output_buffer[0] = 'S'; + output_buffer[1] = hexchars[sigval >> 4]; + output_buffer[2] = hexchars[sigval & 0xf]; + output_buffer[3] = 0; + break; + + /* + * Detach debugger; let CPU run + */ + case 'D': + putpacket(output_buffer); + goto finish_kgdb; + break; + + case 'd': + /* toggle debug flag */ + break; + + /* + * Return the value of the CPU registers + */ + case 'g': + ptr = output_buffer; + ptr = mem2hex((char *)®s->reg0, ptr, 32*sizeof(long), 0); /* r0...r31 */ + ptr = mem2hex((char *)®s->cp0_status, ptr, 6*sizeof(long), 0); /* cp0 */ + ptr = mem2hex((char *)®s->fpr0, ptr, 32*sizeof(long), 0); /* f0...31 */ + ptr = mem2hex((char *)®s->cp1_fsr, ptr, 2*sizeof(long), 0); /* cp1 */ + ptr = mem2hex((char *)®s->frame_ptr, ptr, 2*sizeof(long), 0); /* frp */ + ptr = mem2hex((char *)®s->cp0_index, ptr, 16*sizeof(long), 0); /* cp0 */ + break; + + /* + * set the value of the CPU registers - return OK + */ + case 'G': + { + ptr = &input_buffer[1]; + hex2mem(ptr, (char *)®s->reg0, 32*sizeof(long), 0, 0); + ptr += 32*(2*sizeof(long)); + hex2mem(ptr, (char *)®s->cp0_status, 6*sizeof(long), 0, 0); + ptr += 6*(2*sizeof(long)); + hex2mem(ptr, (char *)®s->fpr0, 32*sizeof(long), 0, 0); + ptr += 32*(2*sizeof(long)); + hex2mem(ptr, (char *)®s->cp1_fsr, 2*sizeof(long), 0, 0); + ptr += 2*(2*sizeof(long)); + hex2mem(ptr, (char *)®s->frame_ptr, 2*sizeof(long), 0, 0); + ptr += 2*(2*sizeof(long)); + hex2mem(ptr, (char *)®s->cp0_index, 16*sizeof(long), 0, 0); + strcpy(output_buffer,"OK"); + } + break; + + /* + * mAA..AA,LLLL Read LLLL bytes at address AA..AA + */ + case 'm': + ptr = &input_buffer[1]; + + if (hexToLong(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length)) { + if (mem2hex((char *)addr, output_buffer, length, 1)) + break; + strcpy (output_buffer, "E03"); + } else + strcpy(output_buffer,"E01"); + break; + + /* + * XAA..AA,LLLL: Write LLLL escaped binary bytes at address AA.AA + */ + case 'X': + bflag = 1; + /* fall through */ + + /* + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK + */ + case 'M': + ptr = &input_buffer[1]; + + if (hexToLong(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length) + && *ptr++ == ':') { + if (hex2mem(ptr, (char *)addr, length, bflag, 1)) + strcpy(output_buffer, "OK"); + else + strcpy(output_buffer, "E03"); + } + else + strcpy(output_buffer, "E02"); + break; + + /* + * cAA..AA Continue at address AA..AA(optional) + */ + case 'c': + /* try to read optional parameter, pc unchanged if no parm */ + + ptr = &input_buffer[1]; + if (hexToLong(&ptr, &addr)) + regs->cp0_epc = addr; + + goto exit_kgdb_exception; + break; + + /* + * kill the program; let us try to restart the machine + * Reset the whole machine. + */ + case 'k': + case 'r': + machine_restart("kgdb restarts machine"); + break; + + /* + * Step to next instruction + */ + case 's': + /* + * There is no single step insn in the MIPS ISA, so we + * use breakpoints and continue, instead. + */ + single_step(regs); + goto exit_kgdb_exception; + /* NOTREACHED */ + break; + + /* + * Set baud rate (bBB) + * FIXME: Needs to be written + */ + case 'b': + { +#if 0 + int baudrate; + extern void set_timer_3(); + + ptr = &input_buffer[1]; + if (!hexToInt(&ptr, &baudrate)) + { + strcpy(output_buffer,"B01"); + break; + } + + /* Convert baud rate to uart clock divider */ + + switch (baudrate) + { + case 38400: + baudrate = 16; + break; + case 19200: + baudrate = 33; + break; + case 9600: + baudrate = 65; + break; + default: + baudrate = 0; + strcpy(output_buffer,"B02"); + goto x1; + } + + if (baudrate) { + putpacket("OK"); /* Ack before changing speed */ + set_timer_3(baudrate); /* Set it */ + } +#endif + } + break; + + } /* switch */ + + /* + * reply to the request + */ + + putpacket(output_buffer); + + } /* while */ + + return; + +finish_kgdb: + restore_debug_traps(); + +exit_kgdb_exception: + /* release locks so other CPUs can go */ + for (i = num_online_cpus()-1; i >= 0; i--) + spin_unlock(&kgdb_cpulock[i]); + spin_unlock(&kgdb_lock); + + __flush_cache_all(); + return; +} + +/* + * This function will generate a breakpoint exception. It is used at the + * beginning of a program to sync up with a debugger and can be used + * otherwise as a quick means to stop program execution and "break" into + * the debugger. + */ +void breakpoint(void) +{ + if (!initialized) + return; + + __asm__ __volatile__( + ".globl breakinst\n\t" + ".set\tnoreorder\n\t" + "nop\n" + "breakinst:\tbreak\n\t" + "nop\n\t" + ".set\treorder" + ); +} + +/* Nothing but the break; don't pollute any registers */ +void async_breakpoint(void) +{ + __asm__ __volatile__( + ".globl async_breakinst\n\t" + ".set\tnoreorder\n\t" + "nop\n" + "async_breakinst:\tbreak\n\t" + "nop\n\t" + ".set\treorder" + ); +} + +void adel(void) +{ + __asm__ __volatile__( + ".globl\tadel\n\t" + "lui\t$8,0x8000\n\t" + "lw\t$9,1($8)\n\t" + ); +} + +/* + * malloc is needed by gdb client in "call func()", even a private one + * will make gdb happy + */ +static void *malloc(size_t size) +{ + return kmalloc(size, GFP_ATOMIC); +} + +static void free(void *where) +{ + kfree(where); +} + +#ifdef CONFIG_GDB_CONSOLE + +void gdb_putsn(const char *str, int l) +{ + char outbuf[18]; + + if (!kgdb_started) + return; + + outbuf[0]='O'; + + while(l) { + int i = (l>8)?8:l; + mem2hex((char *)str, &outbuf[1], i, 0); + outbuf[(i*2)+1]=0; + putpacket(outbuf); + str += i; + l -= i; + } +} + +static void gdb_console_write(struct console *con, const char *s, unsigned n) +{ + gdb_putsn(s, n); +} + +static struct console gdb_console = { + .name = "gdb", + .write = gdb_console_write, + .flags = CON_PRINTBUFFER, + .index = -1 +}; + +static int __init register_gdb_console(void) +{ + register_console(&gdb_console); + + return 0; +} + +console_initcall(register_gdb_console); + +#endif diff --git a/arch/mips/kernel/genex.S b/arch/mips/kernel/genex.S new file mode 100644 index 000000000000..a5b0a389b063 --- /dev/null +++ b/arch/mips/kernel/genex.S @@ -0,0 +1,302 @@ +/* + * 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 - 2000, 2001, 2003 Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * Copyright (C) 2001 MIPS Technologies, Inc. + * Copyright (C) 2002 Maciej W. Rozycki + */ +#include <linux/config.h> +#include <linux/init.h> + +#include <asm/asm.h> +#include <asm/cacheops.h> +#include <asm/regdef.h> +#include <asm/fpregdef.h> +#include <asm/mipsregs.h> +#include <asm/stackframe.h> +#include <asm/war.h> + +#define PANIC_PIC(msg) \ + .set push; \ + .set reorder; \ + PTR_LA a0,8f; \ + .set noat; \ + PTR_LA AT, panic; \ + jr AT; \ +9: b 9b; \ + .set pop; \ + TEXT(msg) + + __INIT + +NESTED(except_vec0_generic, 0, sp) + PANIC_PIC("Exception vector 0 called") + END(except_vec0_generic) + +NESTED(except_vec1_generic, 0, sp) + PANIC_PIC("Exception vector 1 called") + END(except_vec1_generic) + +/* + * General exception vector for all other CPUs. + * + * Be careful when changing this, it has to be at most 128 bytes + * to fit into space reserved for the exception handler. + */ +NESTED(except_vec3_generic, 0, sp) + .set push + .set noat +#if R5432_CP0_INTERRUPT_WAR + mfc0 k0, CP0_INDEX +#endif + mfc0 k1, CP0_CAUSE + andi k1, k1, 0x7c +#ifdef CONFIG_MIPS64 + dsll k1, k1, 1 +#endif + PTR_L k0, exception_handlers(k1) + jr k0 + .set pop + END(except_vec3_generic) + +/* + * General exception handler for CPUs with virtual coherency exception. + * + * Be careful when changing this, it has to be at most 256 (as a special + * exception) bytes to fit into space reserved for the exception handler. + */ +NESTED(except_vec3_r4000, 0, sp) + .set push + .set mips3 + .set noat + mfc0 k1, CP0_CAUSE + li k0, 31<<2 + andi k1, k1, 0x7c + .set push + .set noreorder + .set nomacro + beq k1, k0, handle_vced + li k0, 14<<2 + beq k1, k0, handle_vcei +#ifdef CONFIG_MIPS64 + dsll k1, k1, 1 +#endif + .set pop + PTR_L k0, exception_handlers(k1) + jr k0 + + /* + * Big shit, we now may have two dirty primary cache lines for the same + * physical address. We can savely invalidate the line pointed to by + * c0_badvaddr because after return from this exception handler the + * load / store will be re-executed. + */ +handle_vced: + DMFC0 k0, CP0_BADVADDR + li k1, -4 # Is this ... + and k0, k1 # ... really needed? + mtc0 zero, CP0_TAGLO + cache Index_Store_Tag_D,(k0) + cache Hit_Writeback_Inv_SD,(k0) +#ifdef CONFIG_PROC_FS + PTR_LA k0, vced_count + lw k1, (k0) + addiu k1, 1 + sw k1, (k0) +#endif + eret + +handle_vcei: + MFC0 k0, CP0_BADVADDR + cache Hit_Writeback_Inv_SD, (k0) # also cleans pi +#ifdef CONFIG_PROC_FS + PTR_LA k0, vcei_count + lw k1, (k0) + addiu k1, 1 + sw k1, (k0) +#endif + eret + .set pop + END(except_vec3_r4000) + +/* + * Special interrupt vector for MIPS64 ISA & embedded MIPS processors. + * This is a dedicated interrupt exception vector which reduces the + * interrupt processing overhead. The jump instruction will be replaced + * at the initialization time. + * + * Be careful when changing this, it has to be at most 128 bytes + * to fit into space reserved for the exception handler. + */ +NESTED(except_vec4, 0, sp) +1: j 1b /* Dummy, will be replaced */ + END(except_vec4) + +/* + * EJTAG debug exception handler. + * The EJTAG debug exception entry point is 0xbfc00480, which + * normally is in the boot PROM, so the boot PROM must do a + * unconditional jump to this vector. + */ +NESTED(except_vec_ejtag_debug, 0, sp) + j ejtag_debug_handler + END(except_vec_ejtag_debug) + + __FINIT + +/* + * EJTAG debug exception handler. + */ +NESTED(ejtag_debug_handler, PT_SIZE, sp) + .set push + .set noat + MTC0 k0, CP0_DESAVE + mfc0 k0, CP0_DEBUG + + sll k0, k0, 30 # Check for SDBBP. + bgez k0, ejtag_return + + PTR_LA k0, ejtag_debug_buffer + LONG_S k1, 0(k0) + SAVE_ALL + move a0, sp + jal ejtag_exception_handler + RESTORE_ALL + PTR_LA k0, ejtag_debug_buffer + LONG_L k1, 0(k0) + +ejtag_return: + MFC0 k0, CP0_DESAVE + .set mips32 + deret + .set pop + END(ejtag_debug_handler) + +/* + * This buffer is reserved for the use of the EJTAG debug + * handler. + */ + .data +EXPORT(ejtag_debug_buffer) + .fill LONGSIZE + .previous + + __INIT + +/* + * NMI debug exception handler for MIPS reference boards. + * The NMI debug exception entry point is 0xbfc00000, which + * normally is in the boot PROM, so the boot PROM must do a + * unconditional jump to this vector. + */ +NESTED(except_vec_nmi, 0, sp) + j nmi_handler + END(except_vec_nmi) + + __FINIT + +NESTED(nmi_handler, PT_SIZE, sp) + .set push + .set noat + .set mips3 + SAVE_ALL + move a0, sp + jal nmi_exception_handler + RESTORE_ALL + eret + .set pop + END(nmi_handler) + + .macro __build_clear_none + .endm + + .macro __build_clear_sti + STI + .endm + + .macro __build_clear_cli + CLI + .endm + + .macro __build_clear_fpe + cfc1 a1, fcr31 + li a2, ~(0x3f << 12) + and a2, a1 + ctc1 a2, fcr31 + STI + .endm + + .macro __build_clear_ade + MFC0 t0, CP0_BADVADDR + PTR_S t0, PT_BVADDR(sp) + KMODE + .endm + + .macro __BUILD_silent exception + .endm + + /* Gas tries to parse the PRINT argument as a string containing + string escapes and emits bogus warnings if it believes to + recognize an unknown escape code. So make the arguments + start with an n and gas will believe \n is ok ... */ + .macro __BUILD_verbose nexception + LONG_L a1, PT_EPC(sp) +#if CONFIG_MIPS32 + PRINT("Got \nexception at %08lx\012") +#endif +#if CONFIG_MIPS64 + PRINT("Got \nexception at %016lx\012") +#endif + .endm + + .macro __BUILD_count exception + LONG_L t0,exception_count_\exception + LONG_ADDIU t0, 1 + LONG_S t0,exception_count_\exception + .comm exception_count\exception, 8, 8 + .endm + + .macro __BUILD_HANDLER exception handler clear verbose ext + .align 5 + NESTED(handle_\exception, PT_SIZE, sp) + .set noat + SAVE_ALL + FEXPORT(handle_\exception\ext) + __BUILD_clear_\clear + .set at + __BUILD_\verbose \exception + move a0, sp + jal do_\handler + j ret_from_exception + END(handle_\exception) + .endm + + .macro BUILD_HANDLER exception handler clear verbose + __BUILD_HANDLER \exception \handler \clear \verbose _int + .endm + + BUILD_HANDLER adel ade ade silent /* #4 */ + BUILD_HANDLER ades ade ade silent /* #5 */ + BUILD_HANDLER ibe be cli silent /* #6 */ + BUILD_HANDLER dbe be cli silent /* #7 */ + BUILD_HANDLER bp bp sti silent /* #9 */ + BUILD_HANDLER ri ri sti silent /* #10 */ + BUILD_HANDLER cpu cpu sti silent /* #11 */ + BUILD_HANDLER ov ov sti silent /* #12 */ + BUILD_HANDLER tr tr sti silent /* #13 */ + BUILD_HANDLER fpe fpe fpe silent /* #15 */ + BUILD_HANDLER mdmx mdmx sti silent /* #22 */ + BUILD_HANDLER watch watch sti verbose /* #23 */ + BUILD_HANDLER mcheck mcheck cli verbose /* #24 */ + BUILD_HANDLER reserved reserved sti verbose /* others */ + +#ifdef CONFIG_MIPS64 +/* A temporary overflow handler used by check_daddi(). */ + + __INIT + + BUILD_HANDLER daddi_ov daddi_ov none silent /* #12 */ +#endif diff --git a/arch/mips/kernel/genrtc.c b/arch/mips/kernel/genrtc.c new file mode 100644 index 000000000000..288bf51ad4ec --- /dev/null +++ b/arch/mips/kernel/genrtc.c @@ -0,0 +1,64 @@ +/* + * A glue layer that provides RTC read/write to drivers/char/genrtc.c driver + * based on MIPS internal RTC routines. It does take care locking + * issues so that we are SMP/Preemption safe. + * + * Copyright (C) 2004 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * Please read the COPYING file for all license details. + */ + +#include <linux/spinlock.h> + +#include <asm/rtc.h> +#include <asm/time.h> + +static spinlock_t mips_rtc_lock = SPIN_LOCK_UNLOCKED; + +unsigned int get_rtc_time(struct rtc_time *time) +{ + unsigned long nowtime; + + spin_lock(&mips_rtc_lock); + nowtime = rtc_get_time(); + to_tm(nowtime, time); + time->tm_year -= 1900; + spin_unlock(&mips_rtc_lock); + + return RTC_24H; +} + +int set_rtc_time(struct rtc_time *time) +{ + unsigned long nowtime; + int ret; + + spin_lock(&mips_rtc_lock); + nowtime = mktime(time->tm_year+1900, time->tm_mon+1, + time->tm_mday, time->tm_hour, time->tm_min, + time->tm_sec); + ret = rtc_set_time(nowtime); + spin_unlock(&mips_rtc_lock); + + return ret; +} + +unsigned int get_rtc_ss(void) +{ + struct rtc_time h; + + get_rtc_time(&h); + return h.tm_sec; +} + +int get_rtc_pll(struct rtc_pll_info *pll) +{ + return -EINVAL; +} + +int set_rtc_pll(struct rtc_pll_info *pll) +{ + return -EINVAL; +} + diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S new file mode 100644 index 000000000000..a64e87d22014 --- /dev/null +++ b/arch/mips/kernel/head.S @@ -0,0 +1,221 @@ +/* + * 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 Waldorf Electronics + * Written by Ralf Baechle and Andreas Busse + * Copyright (C) 1994, 95, 96, 97, 98, 99, 2003 Ralf Baechle + * Copyright (C) 1996 Paul M. Antoine + * Modified for DECStation and hence R3000 support by Paul M. Antoine + * Further modifications by David S. Miller and Harald Koerfgen + * Copyright (C) 1999 Silicon Graphics, Inc. + * Kevin Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com + * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. + */ +#include <linux/config.h> +#include <linux/init.h> +#include <linux/threads.h> + +#include <asm/asm.h> +#include <asm/regdef.h> +#include <asm/page.h> +#include <asm/mipsregs.h> +#include <asm/stackframe.h> +#ifdef CONFIG_SGI_IP27 +#include <asm/sn/addrs.h> +#include <asm/sn/sn0/hubni.h> +#include <asm/sn/klkernvars.h> +#endif + + .macro ARC64_TWIDDLE_PC +#if defined(CONFIG_ARC64) || defined(CONFIG_MAPPED_KERNEL) + /* We get launched at a XKPHYS address but the kernel is linked to + run at a KSEG0 address, so jump there. */ + PTR_LA t0, \@f + jr t0 +\@: +#endif + .endm + +#ifdef CONFIG_SGI_IP27 + /* + * outputs the local nasid into res. IP27 stuff. + */ + .macro GET_NASID_ASM res + dli \res, LOCAL_HUB_ADDR(NI_STATUS_REV_ID) + ld \res, (\res) + and \res, NSRI_NODEID_MASK + dsrl \res, NSRI_NODEID_SHFT + .endm +#endif /* CONFIG_SGI_IP27 */ + + /* + * inputs are the text nasid in t1, data nasid in t2. + */ + .macro MAPPED_KERNEL_SETUP_TLB +#ifdef CONFIG_MAPPED_KERNEL + /* + * This needs to read the nasid - assume 0 for now. + * Drop in 0xffffffffc0000000 in tlbhi, 0+VG in tlblo_0, + * 0+DVG in tlblo_1. + */ + dli t0, 0xffffffffc0000000 + dmtc0 t0, CP0_ENTRYHI + li t0, 0x1c000 # Offset of text into node memory + dsll t1, NASID_SHFT # Shift text nasid into place + dsll t2, NASID_SHFT # Same for data nasid + or t1, t1, t0 # Physical load address of kernel text + or t2, t2, t0 # Physical load address of kernel data + dsrl t1, 12 # 4K pfn + dsrl t2, 12 # 4K pfn + dsll t1, 6 # Get pfn into place + dsll t2, 6 # Get pfn into place + li t0, ((_PAGE_GLOBAL|_PAGE_VALID| _CACHE_CACHABLE_COW) >> 6) + or t0, t0, t1 + mtc0 t0, CP0_ENTRYLO0 # physaddr, VG, cach exlwr + li t0, ((_PAGE_GLOBAL|_PAGE_VALID| _PAGE_DIRTY|_CACHE_CACHABLE_COW) >> 6) + or t0, t0, t2 + mtc0 t0, CP0_ENTRYLO1 # physaddr, DVG, cach exlwr + li t0, 0x1ffe000 # MAPPED_KERN_TLBMASK, TLBPGMASK_16M + mtc0 t0, CP0_PAGEMASK + li t0, 0 # KMAP_INX + mtc0 t0, CP0_INDEX + li t0, 1 + mtc0 t0, CP0_WIRED + tlbwi +#else + mtc0 zero, CP0_WIRED +#endif + .endm + + /* + * For the moment disable interrupts, mark the kernel mode and + * set ST0_KX so that the CPU does not spit fire when using + * 64-bit addresses. A full initialization of the CPU's status + * register is done later in per_cpu_trap_init(). + */ + .macro setup_c0_status set clr + .set push + mfc0 t0, CP0_STATUS + or t0, ST0_CU0|\set|0x1f|\clr + xor t0, 0x1f|\clr + mtc0 t0, CP0_STATUS + .set noreorder + sll zero,3 # ehb + .set pop + .endm + + .macro setup_c0_status_pri +#ifdef CONFIG_MIPS64 + setup_c0_status ST0_KX 0 +#else + setup_c0_status 0 0 +#endif + .endm + + .macro setup_c0_status_sec +#ifdef CONFIG_MIPS64 + setup_c0_status ST0_KX ST0_BEV +#else + setup_c0_status 0 ST0_BEV +#endif + .endm + + /* + * Reserved space for exception handlers. + * Necessary for machines which link their kernels at KSEG0. + */ + .fill 0x400 + +EXPORT(stext) # used for profiling +EXPORT(_stext) + + __INIT + +NESTED(kernel_entry, 16, sp) # kernel entry point + setup_c0_status_pri + +#ifdef CONFIG_SGI_IP27 + GET_NASID_ASM t1 + move t2, t1 # text and data are here + MAPPED_KERNEL_SETUP_TLB +#endif /* IP27 */ + + ARC64_TWIDDLE_PC + + PTR_LA t0, __bss_start # clear .bss + LONG_S zero, (t0) + PTR_LA t1, __bss_stop - LONGSIZE +1: + PTR_ADDIU t0, LONGSIZE + LONG_S zero, (t0) + bne t0, t1, 1b + + LONG_S a0, fw_arg0 # firmware arguments + LONG_S a1, fw_arg1 + LONG_S a2, fw_arg2 + LONG_S a3, fw_arg3 + + PTR_LA $28, init_thread_union + PTR_ADDIU sp, $28, _THREAD_SIZE - 32 + set_saved_sp sp, t0, t1 + PTR_SUBU sp, 4 * SZREG # init stack pointer + + j start_kernel + END(kernel_entry) + +#ifdef CONFIG_SMP +/* + * SMP slave cpus entry point. Board specific code for bootstrap calls this + * function after setting up the stack and gp registers. + */ +NESTED(smp_bootstrap, 16, sp) + setup_c0_status_sec + +#ifdef CONFIG_SGI_IP27 + GET_NASID_ASM t1 + dli t0, KLDIR_OFFSET + (KLI_KERN_VARS * KLDIR_ENT_SIZE) + \ + KLDIR_OFF_POINTER + CAC_BASE + dsll t1, NASID_SHFT + or t0, t0, t1 + ld t0, 0(t0) # t0 points to kern_vars struct + lh t1, KV_RO_NASID_OFFSET(t0) + lh t2, KV_RW_NASID_OFFSET(t0) + MAPPED_KERNEL_SETUP_TLB + ARC64_TWIDDLE_PC +#endif /* CONFIG_SGI_IP27 */ + + j start_secondary + END(smp_bootstrap) +#endif /* CONFIG_SMP */ + + __FINIT + + .comm kernelsp, NR_CPUS * 8, 8 + .comm pgd_current, NR_CPUS * 8, 8 + + .comm fw_arg0, SZREG, SZREG # firmware arguments + .comm fw_arg1, SZREG, SZREG + .comm fw_arg2, SZREG, SZREG + .comm fw_arg3, SZREG, SZREG + + .macro page name, order=0 + .globl \name +\name: .size \name, (_PAGE_SIZE << \order) + .org . + (_PAGE_SIZE << \order) + .type \name, @object + .endm + + .data + .align PAGE_SHIFT + + /* + * ... but on 64-bit we've got three-level pagetables with a + * slightly different layout ... + */ + page swapper_pg_dir, _PGD_ORDER +#ifdef CONFIG_MIPS64 + page invalid_pmd_table, _PMD_ORDER +#endif + page invalid_pte_table, _PTE_ORDER diff --git a/arch/mips/kernel/i8259.c b/arch/mips/kernel/i8259.c new file mode 100644 index 000000000000..7eec7568bfea --- /dev/null +++ b/arch/mips/kernel/i8259.c @@ -0,0 +1,331 @@ +/* + * 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. + * + * Code to handle x86 style IRQs plus some generic interrupt stuff. + * + * Copyright (C) 1992 Linus Torvalds + * Copyright (C) 1994 - 2000 Ralf Baechle + */ +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/sysdev.h> + +#include <asm/i8259.h> +#include <asm/io.h> + +void enable_8259A_irq(unsigned int irq); +void disable_8259A_irq(unsigned int irq); + +/* + * This is the 'legacy' 8259A Programmable Interrupt Controller, + * present in the majority of PC/AT boxes. + * plus some generic x86 specific things if generic specifics makes + * any sense at all. + * this file should become arch/i386/kernel/irq.c when the old irq.c + * moves to arch independent land + */ + +spinlock_t i8259A_lock = SPIN_LOCK_UNLOCKED; + +static void end_8259A_irq (unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)) && + irq_desc[irq].action) + enable_8259A_irq(irq); +} + +#define shutdown_8259A_irq disable_8259A_irq + +void mask_and_ack_8259A(unsigned int); + +static unsigned int startup_8259A_irq(unsigned int irq) +{ + enable_8259A_irq(irq); + + return 0; /* never anything pending */ +} + +static struct hw_interrupt_type i8259A_irq_type = { + "XT-PIC", + startup_8259A_irq, + shutdown_8259A_irq, + enable_8259A_irq, + disable_8259A_irq, + mask_and_ack_8259A, + end_8259A_irq, + NULL +}; + +/* + * 8259A PIC functions to handle ISA devices: + */ + +/* + * This contains the irq mask for both 8259A irq controllers, + */ +static unsigned int cached_irq_mask = 0xffff; + +#define cached_21 (cached_irq_mask) +#define cached_A1 (cached_irq_mask >> 8) + +void disable_8259A_irq(unsigned int irq) +{ + unsigned int mask = 1 << irq; + unsigned long flags; + + spin_lock_irqsave(&i8259A_lock, flags); + cached_irq_mask |= mask; + if (irq & 8) + outb(cached_A1,0xA1); + else + outb(cached_21,0x21); + spin_unlock_irqrestore(&i8259A_lock, flags); +} + +void enable_8259A_irq(unsigned int irq) +{ + unsigned int mask = ~(1 << irq); + unsigned long flags; + + spin_lock_irqsave(&i8259A_lock, flags); + cached_irq_mask &= mask; + if (irq & 8) + outb(cached_A1,0xA1); + else + outb(cached_21,0x21); + spin_unlock_irqrestore(&i8259A_lock, flags); +} + +int i8259A_irq_pending(unsigned int irq) +{ + unsigned int mask = 1 << irq; + unsigned long flags; + int ret; + + spin_lock_irqsave(&i8259A_lock, flags); + if (irq < 8) + ret = inb(0x20) & mask; + else + ret = inb(0xA0) & (mask >> 8); + spin_unlock_irqrestore(&i8259A_lock, flags); + + return ret; +} + +void make_8259A_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + irq_desc[irq].handler = &i8259A_irq_type; + enable_irq(irq); +} + +/* + * This function assumes to be called rarely. Switching between + * 8259A registers is slow. + * This has to be protected by the irq controller spinlock + * before being called. + */ +static inline int i8259A_irq_real(unsigned int irq) +{ + int value; + int irqmask = 1 << irq; + + if (irq < 8) { + outb(0x0B,0x20); /* ISR register */ + value = inb(0x20) & irqmask; + outb(0x0A,0x20); /* back to the IRR register */ + return value; + } + outb(0x0B,0xA0); /* ISR register */ + value = inb(0xA0) & (irqmask >> 8); + outb(0x0A,0xA0); /* back to the IRR register */ + return value; +} + +/* + * Careful! The 8259A is a fragile beast, it pretty + * much _has_ to be done exactly like this (mask it + * first, _then_ send the EOI, and the order of EOI + * to the two 8259s is important! + */ +void mask_and_ack_8259A(unsigned int irq) +{ + unsigned int irqmask = 1 << irq; + unsigned long flags; + + spin_lock_irqsave(&i8259A_lock, flags); + /* + * Lightweight spurious IRQ detection. We do not want to overdo + * spurious IRQ handling - it's usually a sign of hardware problems, so + * we only do the checks we can do without slowing down good hardware + * nnecesserily. + * + * Note that IRQ7 and IRQ15 (the two spurious IRQs usually resulting + * rom the 8259A-1|2 PICs) occur even if the IRQ is masked in the 8259A. + * Thus we can check spurious 8259A IRQs without doing the quite slow + * i8259A_irq_real() call for every IRQ. This does not cover 100% of + * spurious interrupts, but should be enough to warn the user that + * there is something bad going on ... + */ + if (cached_irq_mask & irqmask) + goto spurious_8259A_irq; + cached_irq_mask |= irqmask; + +handle_real_irq: + if (irq & 8) { + inb(0xA1); /* DUMMY - (do we need this?) */ + outb(cached_A1,0xA1); + outb(0x60+(irq&7),0xA0);/* 'Specific EOI' to slave */ + outb(0x62,0x20); /* 'Specific EOI' to master-IRQ2 */ + } else { + inb(0x21); /* DUMMY - (do we need this?) */ + outb(cached_21,0x21); + outb(0x60+irq,0x20); /* 'Specific EOI' to master */ + } + spin_unlock_irqrestore(&i8259A_lock, flags); + return; + +spurious_8259A_irq: + /* + * this is the slow path - should happen rarely. + */ + if (i8259A_irq_real(irq)) + /* + * oops, the IRQ _is_ in service according to the + * 8259A - not spurious, go handle it. + */ + goto handle_real_irq; + + { + static int spurious_irq_mask = 0; + /* + * At this point we can be sure the IRQ is spurious, + * lets ACK and report it. [once per IRQ] + */ + if (!(spurious_irq_mask & irqmask)) { + printk(KERN_DEBUG "spurious 8259A interrupt: IRQ%d.\n", irq); + spurious_irq_mask |= irqmask; + } + atomic_inc(&irq_err_count); + /* + * Theoretically we do not have to handle this IRQ, + * but in Linux this does not cause problems and is + * simpler for us. + */ + goto handle_real_irq; + } +} + +static int i8259A_resume(struct sys_device *dev) +{ + init_8259A(0); + return 0; +} + +static struct sysdev_class i8259_sysdev_class = { + set_kset_name("i8259"), + .resume = i8259A_resume, +}; + +static struct sys_device device_i8259A = { + .id = 0, + .cls = &i8259_sysdev_class, +}; + +static int __init i8259A_init_sysfs(void) +{ + int error = sysdev_class_register(&i8259_sysdev_class); + if (!error) + error = sysdev_register(&device_i8259A); + return error; +} + +device_initcall(i8259A_init_sysfs); + +void __init init_8259A(int auto_eoi) +{ + unsigned long flags; + + spin_lock_irqsave(&i8259A_lock, flags); + + outb(0xff, 0x21); /* mask all of 8259A-1 */ + outb(0xff, 0xA1); /* mask all of 8259A-2 */ + + /* + * outb_p - this has to work on a wide range of PC hardware. + */ + outb_p(0x11, 0x20); /* ICW1: select 8259A-1 init */ + outb_p(0x00, 0x21); /* ICW2: 8259A-1 IR0-7 mapped to 0x00-0x07 */ + outb_p(0x04, 0x21); /* 8259A-1 (the master) has a slave on IR2 */ + if (auto_eoi) + outb_p(0x03, 0x21); /* master does Auto EOI */ + else + outb_p(0x01, 0x21); /* master expects normal EOI */ + + outb_p(0x11, 0xA0); /* ICW1: select 8259A-2 init */ + outb_p(0x08, 0xA1); /* ICW2: 8259A-2 IR0-7 mapped to 0x08-0x0f */ + outb_p(0x02, 0xA1); /* 8259A-2 is a slave on master's IR2 */ + outb_p(0x01, 0xA1); /* (slave's support for AEOI in flat mode + is to be investigated) */ + + if (auto_eoi) + /* + * in AEOI mode we just have to mask the interrupt + * when acking. + */ + i8259A_irq_type.ack = disable_8259A_irq; + else + i8259A_irq_type.ack = mask_and_ack_8259A; + + udelay(100); /* wait for 8259A to initialize */ + + outb(cached_21, 0x21); /* restore master IRQ mask */ + outb(cached_A1, 0xA1); /* restore slave IRQ mask */ + + spin_unlock_irqrestore(&i8259A_lock, flags); +} + +/* + * IRQ2 is cascade interrupt to second interrupt controller + */ +static struct irqaction irq2 = { + no_action, 0, CPU_MASK_NONE, "cascade", NULL, NULL +}; + +static struct resource pic1_io_resource = { + "pic1", 0x20, 0x3f, IORESOURCE_BUSY +}; + +static struct resource pic2_io_resource = { + "pic2", 0xa0, 0xbf, IORESOURCE_BUSY +}; + +/* + * On systems with i8259-style interrupt controllers we assume for + * driver compatibility reasons interrupts 0 - 15 to be the i8295 + * interrupts even if the hardware uses a different interrupt numbering. + */ +void __init init_i8259_irqs (void) +{ + int i; + + request_resource(&ioport_resource, &pic1_io_resource); + request_resource(&ioport_resource, &pic2_io_resource); + + init_8259A(0); + + for (i = 0; i < 16; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = 0; + irq_desc[i].depth = 1; + irq_desc[i].handler = &i8259A_irq_type; + } + + setup_irq(2, &irq2); +} diff --git a/arch/mips/kernel/init_task.c b/arch/mips/kernel/init_task.c new file mode 100644 index 000000000000..aeda7f58391b --- /dev/null +++ b/arch/mips/kernel/init_task.c @@ -0,0 +1,42 @@ +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/init_task.h> +#include <linux/fs.h> +#include <linux/mqueue.h> + +#include <asm/thread_info.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> + +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS(init_signals); +static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); +struct mm_struct init_mm = INIT_MM(init_mm); + +EXPORT_SYMBOL(init_mm); + +/* + * Initial thread structure. + * + * We need to make sure that this is 8192-byte aligned due to the + * way process stacks are handled. This is done by making sure + * the linker maps this in the .text segment right after head.S, + * and making head.S ensure the proper alignment. + * + * The things we do for performance.. + */ +union thread_union init_thread_union + __attribute__((__section__(".data.init_task"), + __aligned__(THREAD_SIZE))) = + { INIT_THREAD_INFO(init_task) }; + +/* + * Initial task structure. + * + * All other task structs will be allocated on slabs in fork.c + */ +struct task_struct init_task = INIT_TASK(init_task); + +EXPORT_SYMBOL(init_task); diff --git a/arch/mips/kernel/ioctl32.c b/arch/mips/kernel/ioctl32.c new file mode 100644 index 000000000000..519cd5d0aebb --- /dev/null +++ b/arch/mips/kernel/ioctl32.c @@ -0,0 +1,58 @@ +/* + * ioctl32.c: Conversion between 32bit and 64bit native ioctls. + * + * Copyright (C) 2000 Silicon Graphics, Inc. + * Written by Ulf Carlsson (ulfc@engr.sgi.com) + * Copyright (C) 2000, 2004 Ralf Baechle + * Copyright (C) 2002, 2003 Maciej W. Rozycki + */ +#define INCLUDES +#include "compat_ioctl.c" + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/compat.h> +#include <linux/ioctl32.h> +#include <linux/syscalls.h> + +#ifdef CONFIG_SIBYTE_TBPROF +#include <asm/sibyte/trace_prof.h> +#endif + +#define A(__x) ((unsigned long)(__x)) + +long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); + +#define CODE +#include "compat_ioctl.c" + +typedef int (* ioctl32_handler_t)(unsigned int, unsigned int, unsigned long, struct file *); + +#define COMPATIBLE_IOCTL(cmd) HANDLE_IOCTL((cmd),sys_ioctl) +#define HANDLE_IOCTL(cmd,handler) { (cmd), (ioctl32_handler_t)(handler), NULL }, +#define IOCTL_TABLE_START \ + struct ioctl_trans ioctl_start[] = { +#define IOCTL_TABLE_END \ + }; + +IOCTL_TABLE_START + +#include <linux/compat_ioctl.h> +#define DECLARES +#include "compat_ioctl.c" + +#ifdef CONFIG_SIBYTE_TBPROF +COMPATIBLE_IOCTL(SBPROF_ZBSTART) +COMPATIBLE_IOCTL(SBPROF_ZBSTOP) +COMPATIBLE_IOCTL(SBPROF_ZBWAITFULL) +#endif /* CONFIG_SIBYTE_TBPROF */ + +/*HANDLE_IOCTL(RTC_IRQP_READ, w_long) +COMPATIBLE_IOCTL(RTC_IRQP_SET) +HANDLE_IOCTL(RTC_EPOCH_READ, w_long) +COMPATIBLE_IOCTL(RTC_EPOCH_SET) +*/ + +IOCTL_TABLE_END + +int ioctl_table_size = ARRAY_SIZE(ioctl_start); diff --git a/arch/mips/kernel/irix5sys.S b/arch/mips/kernel/irix5sys.S new file mode 100644 index 000000000000..eeef891093ed --- /dev/null +++ b/arch/mips/kernel/irix5sys.S @@ -0,0 +1,1041 @@ +/* + * 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. + * + * 32-bit IRIX5 ABI system call table derived from original file 'irix5sys.h' + * created by David S. Miller. + * + * Copyright (C) 1996 - 2004 David S. Miller <dm@engr.sgi.com> + * Copyright (C) 2004 Steven J. Hill <sjhill@realitydiluted.com> + */ +#include <asm/asm.h> + + /* + * Key: + * V == Valid and should work as expected for most cases. + * HV == Half Valid, some things will work, some likely will not + * IV == InValid, certainly will not work at all yet + * ?V == ?'ably Valid, I have not done enough looking into it + * DC == Don't Care, a rats ass we couldn't give + */ + + .macro irix5syscalltable + + sys sys_syscall 0 /* 1000 sysindir() V*/ + sys sys_exit 1 /* 1001 exit() V*/ + sys sys_fork 0 /* 1002 fork() V*/ + sys sys_read 3 /* 1003 read() V*/ + sys sys_write 3 /* 1004 write() V*/ + sys sys_open 3 /* 1005 open() V*/ + sys sys_close 1 /* 1006 close() V*/ + sys irix_unimp 0 /* 1007 (XXX IRIX 4 wait) V*/ + sys sys_creat 2 /* 1008 creat() V*/ + sys sys_link 2 /* 1009 link() V*/ + sys sys_unlink 1 /* 1010 unlink() V*/ + sys irix_exec 0 /* 1011 exec() V*/ + sys sys_chdir 1 /* 1012 chdir() V*/ + sys irix_gtime 0 /* 1013 time() V*/ + sys irix_unimp 0 /* 1014 (XXX IRIX 4 mknod) V*/ + sys sys_chmod 2 /* 1015 chmod() V*/ + sys sys_chown 3 /* 1016 chown() V*/ + sys irix_brk 1 /* 1017 break() V*/ + sys irix_unimp 0 /* 1018 (XXX IRIX 4 stat) V*/ + sys sys_lseek 3 /* 1019 lseek() XXX64bit HV*/ + sys irix_getpid 0 /* 1020 getpid() V*/ + sys irix_mount 6 /* 1021 mount() IV*/ + sys sys_umount 1 /* 1022 umount() V*/ + sys sys_setuid 1 /* 1023 setuid() V*/ + sys irix_getuid 0 /* 1024 getuid() V*/ + sys irix_stime 1 /* 1025 stime() V*/ + sys irix_unimp 4 /* 1026 XXX ptrace() IV*/ + sys irix_alarm 1 /* 1027 alarm() V*/ + sys irix_unimp 0 /* 1028 (XXX IRIX 4 fstat) V*/ + sys irix_pause 0 /* 1029 pause() V*/ + sys sys_utime 2 /* 1030 utime() V*/ + sys irix_unimp 0 /* 1031 nuthin' V*/ + sys irix_unimp 0 /* 1032 nobody home man... V*/ + sys sys_access 2 /* 1033 access() V*/ + sys sys_nice 1 /* 1034 nice() V*/ + sys irix_statfs 2 /* 1035 statfs() V*/ + sys sys_sync 0 /* 1036 sync() V*/ + sys sys_kill 2 /* 1037 kill() V*/ + sys irix_fstatfs 2 /* 1038 fstatfs() V*/ + sys irix_setpgrp 1 /* 1039 setpgrp() V*/ + sys irix_syssgi 0 /* 1040 syssgi() HV*/ + sys sys_dup 1 /* 1041 dup() V*/ + sys sys_pipe 0 /* 1042 pipe() V*/ + sys irix_times 1 /* 1043 times() V*/ + sys irix_unimp 0 /* 1044 XXX profil() IV*/ + sys irix_unimp 0 /* 1045 XXX lock() IV*/ + sys sys_setgid 1 /* 1046 setgid() V*/ + sys irix_getgid 0 /* 1047 getgid() V*/ + sys irix_unimp 0 /* 1048 (XXX IRIX 4 ssig) V*/ + sys irix_msgsys 6 /* 1049 sys_msgsys V*/ + sys sys_sysmips 4 /* 1050 sysmips() HV*/ + sys irix_unimp 0 /* 1051 XXX sysacct() IV*/ + sys irix_shmsys 5 /* 1052 sys_shmsys V*/ + sys irix_semsys 0 /* 1053 sys_semsys V*/ + sys irix_ioctl 3 /* 1054 ioctl() HV*/ + sys irix_uadmin 0 /* 1055 XXX sys_uadmin() HC*/ + sys irix_sysmp 0 /* 1056 sysmp() HV*/ + sys irix_utssys 4 /* 1057 sys_utssys() HV*/ + sys irix_unimp 0 /* 1058 nada enchilada V*/ + sys irix_exece 0 /* 1059 exece() V*/ + sys sys_umask 1 /* 1060 umask() V*/ + sys sys_chroot 1 /* 1061 chroot() V*/ + sys irix_fcntl 3 /* 1062 fcntl() ?V*/ + sys irix_ulimit 2 /* 1063 ulimit() HV*/ + sys irix_unimp 0 /* 1064 XXX AFS shit DC*/ + sys irix_unimp 0 /* 1065 XXX AFS shit DC*/ + sys irix_unimp 0 /* 1066 XXX AFS shit DC*/ + sys irix_unimp 0 /* 1067 XXX AFS shit DC*/ + sys irix_unimp 0 /* 1068 XXX AFS shit DC*/ + sys irix_unimp 0 /* 1069 XXX AFS shit DC*/ + sys irix_unimp 0 /* 1070 XXX AFS shit DC*/ + sys irix_unimp 0 /* 1071 XXX AFS shit DC*/ + sys irix_unimp 0 /* 1072 XXX AFS shit DC*/ + sys irix_unimp 0 /* 1073 XXX AFS shit DC*/ + sys irix_unimp 0 /* 1074 nuttin' V*/ + sys irix_unimp 0 /* 1075 XXX sys_getrlimit64()IV*/ + sys irix_unimp 0 /* 1076 XXX sys_setrlimit64()IV*/ + sys sys_nanosleep 2 /* 1077 nanosleep() V*/ + sys irix_lseek64 5 /* 1078 lseek64() ?V*/ + sys sys_rmdir 1 /* 1079 rmdir() V*/ + sys sys_mkdir 2 /* 1080 mkdir() V*/ + sys sys_getdents 3 /* 1081 getdents() V*/ + sys irix_sginap 1 /* 1082 sys_sginap() V*/ + sys irix_sgikopt 3 /* 1083 sys_sgikopt() DC*/ + sys sys_sysfs 3 /* 1084 sysfs() ?V*/ + sys irix_unimp 0 /* 1085 XXX sys_getmsg() DC*/ + sys irix_unimp 0 /* 1086 XXX sys_putmsg() DC*/ + sys sys_poll 3 /* 1087 poll() V*/ + sys irix_sigreturn 0 /* 1088 sigreturn() ?V*/ + sys sys_accept 3 /* 1089 accept() V*/ + sys sys_bind 3 /* 1090 bind() V*/ + sys sys_connect 3 /* 1091 connect() V*/ + sys irix_gethostid 0 /* 1092 sys_gethostid() ?V*/ + sys sys_getpeername 3 /* 1093 getpeername() V*/ + sys sys_getsockname 3 /* 1094 getsockname() V*/ + sys sys_getsockopt 5 /* 1095 getsockopt() V*/ + sys sys_listen 2 /* 1096 listen() V*/ + sys sys_recv 4 /* 1097 recv() V*/ + sys sys_recvfrom 6 /* 1098 recvfrom() V*/ + sys sys_recvmsg 3 /* 1099 recvmsg() V*/ + sys sys_select 5 /* 1100 select() V*/ + sys sys_send 4 /* 1101 send() V*/ + sys sys_sendmsg 3 /* 1102 sendmsg() V*/ + sys sys_sendto 6 /* 1103 sendto() V*/ + sys irix_sethostid 1 /* 1104 sys_sethostid() ?V*/ + sys sys_setsockopt 5 /* 1105 setsockopt() V*/ + sys sys_shutdown 2 /* 1106 shutdown() ?V*/ + sys irix_socket 3 /* 1107 socket() V*/ + sys sys_gethostname 2 /* 1108 sys_gethostname() ?V*/ + sys sys_sethostname 2 /* 1109 sethostname() ?V*/ + sys irix_getdomainname 2 /* 1110 sys_getdomainname() ?V*/ + sys sys_setdomainname 2 /* 1111 setdomainname() ?V*/ + sys sys_truncate 2 /* 1112 truncate() V*/ + sys sys_ftruncate 2 /* 1113 ftruncate() V*/ + sys sys_rename 2 /* 1114 rename() V*/ + sys sys_symlink 2 /* 1115 symlink() V*/ + sys sys_readlink 3 /* 1116 readlink() V*/ + sys irix_unimp 0 /* 1117 XXX IRIX 4 lstat() DC*/ + sys irix_unimp 0 /* 1118 nothin' V*/ + sys irix_unimp 0 /* 1119 XXX nfs_svc() DC*/ + sys irix_unimp 0 /* 1120 XXX nfs_getfh() DC*/ + sys irix_unimp 0 /* 1121 XXX async_daemon() DC*/ + sys irix_unimp 0 /* 1122 XXX exportfs() DC*/ + sys sys_setregid 2 /* 1123 setregid() V*/ + sys sys_setreuid 2 /* 1124 setreuid() V*/ + sys sys_getitimer 2 /* 1125 getitimer() V*/ + sys sys_setitimer 3 /* 1126 setitimer() V*/ + sys irix_unimp 1 /* 1127 XXX adjtime() IV*/ + sys irix_gettimeofday 1 /* 1128 gettimeofday() V*/ + sys irix_unimp 0 /* 1129 XXX sproc() IV*/ + sys irix_prctl 0 /* 1130 prctl() HV*/ + sys irix_unimp 0 /* 1131 XXX procblk() IV*/ + sys irix_unimp 0 /* 1132 XXX sprocsp() IV*/ + sys irix_unimp 0 /* 1133 XXX sgigsc() IV*/ + sys irix_mmap32 6 /* 1134 mmap() XXXflags? ?V*/ + sys sys_munmap 2 /* 1135 munmap() V*/ + sys sys_mprotect 3 /* 1136 mprotect() V*/ + sys sys_msync 4 /* 1137 msync() V*/ + sys irix_madvise 3 /* 1138 madvise() DC*/ + sys irix_pagelock 3 /* 1139 pagelock() IV*/ + sys irix_getpagesize 0 /* 1140 getpagesize() V*/ + sys irix_quotactl 0 /* 1141 quotactl() V*/ + sys irix_unimp 0 /* 1142 nobody home man V*/ + sys sys_getpgid 1 /* 1143 BSD getpgrp() V*/ + sys irix_BSDsetpgrp 2 /* 1143 BSD setpgrp() V*/ + sys sys_vhangup 0 /* 1144 vhangup() V*/ + sys sys_fsync 1 /* 1145 fsync() V*/ + sys sys_fchdir 1 /* 1146 fchdir() V*/ + sys sys_getrlimit 2 /* 1147 getrlimit() ?V*/ + sys sys_setrlimit 2 /* 1148 setrlimit() ?V*/ + sys sys_cacheflush 3 /* 1150 cacheflush() HV*/ + sys sys_cachectl 3 /* 1151 cachectl() HV*/ + sys sys_fchown 3 /* 1152 fchown() ?V*/ + sys sys_fchmod 2 /* 1153 fchmod() ?V*/ + sys irix_unimp 0 /* 1154 XXX IRIX 4 wait3() V*/ + sys sys_socketpair 4 /* 1155 socketpair() V*/ + sys irix_systeminfo 3 /* 1156 systeminfo() IV*/ + sys irix_uname 1 /* 1157 uname() IV*/ + sys irix_xstat 3 /* 1158 xstat() V*/ + sys irix_lxstat 3 /* 1159 lxstat() V*/ + sys irix_fxstat 3 /* 1160 fxstat() V*/ + sys irix_xmknod 0 /* 1161 xmknod() ?V*/ + sys irix_sigaction 4 /* 1162 sigaction() ?V*/ + sys irix_sigpending 1 /* 1163 sigpending() ?V*/ + sys irix_sigprocmask 3 /* 1164 sigprocmask() ?V*/ + sys irix_sigsuspend 0 /* 1165 sigsuspend() ?V*/ + sys irix_sigpoll_sys 3 /* 1166 sigpoll_sys() IV*/ + sys irix_swapctl 2 /* 1167 swapctl() IV*/ + sys irix_getcontext 0 /* 1168 getcontext() HV*/ + sys irix_setcontext 0 /* 1169 setcontext() HV*/ + sys irix_waitsys 5 /* 1170 waitsys() IV*/ + sys irix_sigstack 2 /* 1171 sigstack() HV*/ + sys irix_sigaltstack 2 /* 1172 sigaltstack() HV*/ + sys irix_sigsendset 2 /* 1173 sigsendset() IV*/ + sys irix_statvfs 2 /* 1174 statvfs() V*/ + sys irix_fstatvfs 2 /* 1175 fstatvfs() V*/ + sys irix_unimp 0 /* 1176 XXX getpmsg() DC*/ + sys irix_unimp 0 /* 1177 XXX putpmsg() DC*/ + sys sys_lchown 3 /* 1178 lchown() V*/ + sys irix_priocntl 0 /* 1179 priocntl() DC*/ + sys irix_sigqueue 4 /* 1180 sigqueue() IV*/ + sys sys_readv 3 /* 1181 readv() V*/ + sys sys_writev 3 /* 1182 writev() V*/ + sys irix_truncate64 4 /* 1183 truncate64() XX32bit HV*/ + sys irix_ftruncate64 4 /* 1184 ftruncate64()XX32bit HV*/ + sys irix_mmap64 0 /* 1185 mmap64() XX32bit HV*/ + sys irix_dmi 0 /* 1186 dmi() DC*/ + sys irix_pread 6 /* 1187 pread() IV*/ + sys irix_pwrite 6 /* 1188 pwrite() IV*/ + sys sys_fsync 1 /* 1189 fdatasync() XXPOSIX HV*/ + sys irix_sgifastpath 7 /* 1190 sgifastpath() WHEEE IV*/ + sys irix_unimp 0 /* 1191 XXX attr_get() DC*/ + sys irix_unimp 0 /* 1192 XXX attr_getf() DC*/ + sys irix_unimp 0 /* 1193 XXX attr_set() DC*/ + sys irix_unimp 0 /* 1194 XXX attr_setf() DC*/ + sys irix_unimp 0 /* 1195 XXX attr_remove() DC*/ + sys irix_unimp 0 /* 1196 XXX attr_removef() DC*/ + sys irix_unimp 0 /* 1197 XXX attr_list() DC*/ + sys irix_unimp 0 /* 1198 XXX attr_listf() DC*/ + sys irix_unimp 0 /* 1199 XXX attr_multi() DC*/ + sys irix_unimp 0 /* 1200 XXX attr_multif() DC*/ + sys irix_statvfs64 2 /* 1201 statvfs64() V*/ + sys irix_fstatvfs64 2 /* 1202 fstatvfs64() V*/ + sys irix_getmountid 2 /* 1203 getmountid()XXXfsids HV*/ + sys irix_nsproc 5 /* 1204 nsproc() IV*/ + sys irix_getdents64 3 /* 1205 getdents64() HV*/ + sys irix_unimp 0 /* 1206 XXX DFS garbage DC*/ + sys irix_ngetdents 4 /* 1207 ngetdents() XXXeop HV*/ + sys irix_ngetdents64 4 /* 1208 ngetdents64() XXXeop HV*/ + sys irix_unimp 0 /* 1209 nothin' V*/ + sys irix_unimp 0 /* 1210 XXX pidsprocsp() */ + sys irix_unimp 0 /* 1211 XXX rexec() */ + sys irix_unimp 0 /* 1212 XXX timer_create() */ + sys irix_unimp 0 /* 1213 XXX timer_delete() */ + sys irix_unimp 0 /* 1214 XXX timer_settime() */ + sys irix_unimp 0 /* 1215 XXX timer_gettime() */ + sys irix_unimp 0 /* 1216 XXX timer_setoverrun() */ + sys sys_sched_rr_get_interval 2 /* 1217 sched_rr_get_interval()V*/ + sys sys_sched_yield 0 /* 1218 sched_yield() V*/ + sys sys_sched_getscheduler 1 /* 1219 sched_getscheduler() V*/ + sys sys_sched_setscheduler 3 /* 1220 sched_setscheduler() V*/ + sys sys_sched_getparam 2 /* 1221 sched_getparam() V*/ + sys sys_sched_setparam 2 /* 1222 sched_setparam() V*/ + sys irix_unimp 0 /* 1223 XXX usync_cntl() */ + sys irix_unimp 0 /* 1224 XXX psema_cntl() */ + sys irix_unimp 0 /* 1225 XXX restartreturn() */ + + /* Just to pad things out nicely. */ + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + sys irix_unimp 0 + + .endm + + /* + * Pre-compute the number of _instruction_ bytes needed to load + * or store the arguments 6-8. Negative values are ignored. + */ + .macro sys function, nargs + PTR \function + LONG (\nargs << 2) - (5 << 2) + .endm + + .align 4 +EXPORT(sys_call_table_irix5) + irix5syscalltable diff --git a/arch/mips/kernel/irixelf.c b/arch/mips/kernel/irixelf.c new file mode 100644 index 000000000000..4af20cd91f9f --- /dev/null +++ b/arch/mips/kernel/irixelf.c @@ -0,0 +1,1326 @@ +/* + * 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. + * + * irixelf.c: Code to load IRIX ELF executables conforming to the MIPS ABI. + * Based off of work by Eric Youngdale. + * + * Copyright (C) 1993 - 1994 Eric Youngdale <ericy@cais.com> + * Copyright (C) 1996 - 2004 David S. Miller <dm@engr.sgi.com> + * Copyright (C) 2004 Steven J. Hill <sjhill@realitydiluted.com> + */ +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/stat.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/mman.h> +#include <linux/a.out.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/signal.h> +#include <linux/binfmts.h> +#include <linux/string.h> +#include <linux/file.h> +#include <linux/fcntl.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/shm.h> +#include <linux/personality.h> +#include <linux/elfcore.h> +#include <linux/smp_lock.h> + +#include <asm/uaccess.h> +#include <asm/mipsregs.h> +#include <asm/prctl.h> + +#define DLINFO_ITEMS 12 + +#include <linux/elf.h> + +#undef DEBUG_ELF + +static int load_irix_binary(struct linux_binprm * bprm, struct pt_regs * regs); +static int load_irix_library(struct file *); +static int irix_core_dump(long signr, struct pt_regs * regs, + struct file *file); + +static struct linux_binfmt irix_format = { + NULL, THIS_MODULE, load_irix_binary, load_irix_library, + irix_core_dump, PAGE_SIZE +}; + +#ifndef elf_addr_t +#define elf_addr_t unsigned long +#endif + +#ifdef DEBUG_ELF +/* Debugging routines. */ +static char *get_elf_p_type(Elf32_Word p_type) +{ + int i = (int) p_type; + + switch(i) { + case PT_NULL: return("PT_NULL"); break; + case PT_LOAD: return("PT_LOAD"); break; + case PT_DYNAMIC: return("PT_DYNAMIC"); break; + case PT_INTERP: return("PT_INTERP"); break; + case PT_NOTE: return("PT_NOTE"); break; + case PT_SHLIB: return("PT_SHLIB"); break; + case PT_PHDR: return("PT_PHDR"); break; + case PT_LOPROC: return("PT_LOPROC/REGINFO"); break; + case PT_HIPROC: return("PT_HIPROC"); break; + default: return("PT_BOGUS"); break; + } +} + +static void print_elfhdr(struct elfhdr *ehp) +{ + int i; + + printk("ELFHDR: e_ident<"); + for(i = 0; i < (EI_NIDENT - 1); i++) printk("%x ", ehp->e_ident[i]); + printk("%x>\n", ehp->e_ident[i]); + printk(" e_type[%04x] e_machine[%04x] e_version[%08lx]\n", + (unsigned short) ehp->e_type, (unsigned short) ehp->e_machine, + (unsigned long) ehp->e_version); + printk(" e_entry[%08lx] e_phoff[%08lx] e_shoff[%08lx] " + "e_flags[%08lx]\n", + (unsigned long) ehp->e_entry, (unsigned long) ehp->e_phoff, + (unsigned long) ehp->e_shoff, (unsigned long) ehp->e_flags); + printk(" e_ehsize[%04x] e_phentsize[%04x] e_phnum[%04x]\n", + (unsigned short) ehp->e_ehsize, (unsigned short) ehp->e_phentsize, + (unsigned short) ehp->e_phnum); + printk(" e_shentsize[%04x] e_shnum[%04x] e_shstrndx[%04x]\n", + (unsigned short) ehp->e_shentsize, (unsigned short) ehp->e_shnum, + (unsigned short) ehp->e_shstrndx); +} + +static void print_phdr(int i, struct elf_phdr *ep) +{ + printk("PHDR[%d]: p_type[%s] p_offset[%08lx] p_vaddr[%08lx] " + "p_paddr[%08lx]\n", i, get_elf_p_type(ep->p_type), + (unsigned long) ep->p_offset, (unsigned long) ep->p_vaddr, + (unsigned long) ep->p_paddr); + printk(" p_filesz[%08lx] p_memsz[%08lx] p_flags[%08lx] " + "p_align[%08lx]\n", (unsigned long) ep->p_filesz, + (unsigned long) ep->p_memsz, (unsigned long) ep->p_flags, + (unsigned long) ep->p_align); +} + +static void dump_phdrs(struct elf_phdr *ep, int pnum) +{ + int i; + + for(i = 0; i < pnum; i++, ep++) { + if((ep->p_type == PT_LOAD) || + (ep->p_type == PT_INTERP) || + (ep->p_type == PT_PHDR)) + print_phdr(i, ep); + } +} +#endif /* (DEBUG_ELF) */ + +static void set_brk(unsigned long start, unsigned long end) +{ + start = PAGE_ALIGN(start); + end = PAGE_ALIGN(end); + if (end <= start) + return; + down_write(¤t->mm->mmap_sem); + do_brk(start, end - start); + up_write(¤t->mm->mmap_sem); +} + + +/* We need to explicitly zero any fractional pages + * after the data section (i.e. bss). This would + * contain the junk from the file that should not + * be in memory. + */ +static void padzero(unsigned long elf_bss) +{ + unsigned long nbyte; + + nbyte = elf_bss & (PAGE_SIZE-1); + if (nbyte) { + nbyte = PAGE_SIZE - nbyte; + clear_user((void *) elf_bss, nbyte); + } +} + +unsigned long * create_irix_tables(char * p, int argc, int envc, + struct elfhdr * exec, unsigned int load_addr, + unsigned int interp_load_addr, + struct pt_regs *regs, struct elf_phdr *ephdr) +{ + elf_addr_t *argv; + elf_addr_t *envp; + elf_addr_t *sp, *csp; + +#ifdef DEBUG_ELF + printk("create_irix_tables: p[%p] argc[%d] envc[%d] " + "load_addr[%08x] interp_load_addr[%08x]\n", + p, argc, envc, load_addr, interp_load_addr); +#endif + sp = (elf_addr_t *) (~15UL & (unsigned long) p); + csp = sp; + csp -= exec ? DLINFO_ITEMS*2 : 2; + csp -= envc+1; + csp -= argc+1; + csp -= 1; /* argc itself */ + if ((unsigned long)csp & 15UL) { + sp -= (16UL - ((unsigned long)csp & 15UL)) / sizeof(*sp); + } + + /* + * Put the ELF interpreter info on the stack + */ +#define NEW_AUX_ENT(nr, id, val) \ + __put_user ((id), sp+(nr*2)); \ + __put_user ((val), sp+(nr*2+1)); \ + + sp -= 2; + NEW_AUX_ENT(0, AT_NULL, 0); + + if(exec) { + sp -= 11*2; + + NEW_AUX_ENT (0, AT_PHDR, load_addr + exec->e_phoff); + NEW_AUX_ENT (1, AT_PHENT, sizeof (struct elf_phdr)); + NEW_AUX_ENT (2, AT_PHNUM, exec->e_phnum); + NEW_AUX_ENT (3, AT_PAGESZ, ELF_EXEC_PAGESIZE); + NEW_AUX_ENT (4, AT_BASE, interp_load_addr); + NEW_AUX_ENT (5, AT_FLAGS, 0); + NEW_AUX_ENT (6, AT_ENTRY, (elf_addr_t) exec->e_entry); + NEW_AUX_ENT (7, AT_UID, (elf_addr_t) current->uid); + NEW_AUX_ENT (8, AT_EUID, (elf_addr_t) current->euid); + NEW_AUX_ENT (9, AT_GID, (elf_addr_t) current->gid); + NEW_AUX_ENT (10, AT_EGID, (elf_addr_t) current->egid); + } +#undef NEW_AUX_ENT + + sp -= envc+1; + envp = sp; + sp -= argc+1; + argv = sp; + + __put_user((elf_addr_t)argc,--sp); + current->mm->arg_start = (unsigned long) p; + while (argc-->0) { + __put_user((unsigned long)p,argv++); + p += strlen_user(p); + } + __put_user((unsigned long) NULL, argv); + current->mm->arg_end = current->mm->env_start = (unsigned long) p; + while (envc-->0) { + __put_user((unsigned long)p,envp++); + p += strlen_user(p); + } + __put_user((unsigned long) NULL, envp); + current->mm->env_end = (unsigned long) p; + return sp; +} + + +/* This is much more generalized than the library routine read function, + * so we keep this separate. Technically the library read function + * is only provided so that we can read a.out libraries that have + * an ELF header. + */ +static unsigned int load_irix_interp(struct elfhdr * interp_elf_ex, + struct file * interpreter, + unsigned int *interp_load_addr) +{ + struct elf_phdr *elf_phdata = NULL; + struct elf_phdr *eppnt; + unsigned int len; + unsigned int load_addr; + int elf_bss; + int retval; + unsigned int last_bss; + int error; + int i; + unsigned int k; + + elf_bss = 0; + last_bss = 0; + error = load_addr = 0; + +#ifdef DEBUG_ELF + print_elfhdr(interp_elf_ex); +#endif + + /* First of all, some simple consistency checks */ + if ((interp_elf_ex->e_type != ET_EXEC && + interp_elf_ex->e_type != ET_DYN) || + !irix_elf_check_arch(interp_elf_ex) || + !interpreter->f_op->mmap) { + printk("IRIX interp has bad e_type %d\n", interp_elf_ex->e_type); + return 0xffffffff; + } + + /* Now read in all of the header information */ + if(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > PAGE_SIZE) { + printk("IRIX interp header bigger than a page (%d)\n", + (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum)); + return 0xffffffff; + } + + elf_phdata = kmalloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, + GFP_KERNEL); + + if(!elf_phdata) { + printk("Cannot kmalloc phdata for IRIX interp.\n"); + return 0xffffffff; + } + + /* If the size of this structure has changed, then punt, since + * we will be doing the wrong thing. + */ + if(interp_elf_ex->e_phentsize != 32) { + printk("IRIX interp e_phentsize == %d != 32 ", + interp_elf_ex->e_phentsize); + kfree(elf_phdata); + return 0xffffffff; + } + + retval = kernel_read(interpreter, interp_elf_ex->e_phoff, + (char *) elf_phdata, + sizeof(struct elf_phdr) * interp_elf_ex->e_phnum); + +#ifdef DEBUG_ELF + dump_phdrs(elf_phdata, interp_elf_ex->e_phnum); +#endif + + eppnt = elf_phdata; + for(i=0; i<interp_elf_ex->e_phnum; i++, eppnt++) { + if(eppnt->p_type == PT_LOAD) { + int elf_type = MAP_PRIVATE | MAP_DENYWRITE; + int elf_prot = 0; + unsigned long vaddr = 0; + if (eppnt->p_flags & PF_R) elf_prot = PROT_READ; + if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE; + if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC; + elf_type |= MAP_FIXED; + vaddr = eppnt->p_vaddr; + +#ifdef DEBUG_ELF + printk("INTERP do_mmap(%p, %08lx, %08lx, %08lx, %08lx, %08lx) ", + interpreter, vaddr, + (unsigned long) (eppnt->p_filesz + (eppnt->p_vaddr & 0xfff)), + (unsigned long) elf_prot, (unsigned long) elf_type, + (unsigned long) (eppnt->p_offset & 0xfffff000)); +#endif + down_write(¤t->mm->mmap_sem); + error = do_mmap(interpreter, vaddr, + eppnt->p_filesz + (eppnt->p_vaddr & 0xfff), + elf_prot, elf_type, + eppnt->p_offset & 0xfffff000); + up_write(¤t->mm->mmap_sem); + + if(error < 0 && error > -1024) { + printk("Aieee IRIX interp mmap error=%d\n", error); + break; /* Real error */ + } +#ifdef DEBUG_ELF + printk("error=%08lx ", (unsigned long) error); +#endif + if(!load_addr && interp_elf_ex->e_type == ET_DYN) { + load_addr = error; +#ifdef DEBUG_ELF + printk("load_addr = error "); +#endif + } + + /* Find the end of the file mapping for this phdr, and keep + * track of the largest address we see for this. + */ + k = eppnt->p_vaddr + eppnt->p_filesz; + if(k > elf_bss) elf_bss = k; + + /* Do the same thing for the memory mapping - between + * elf_bss and last_bss is the bss section. + */ + k = eppnt->p_memsz + eppnt->p_vaddr; + if(k > last_bss) last_bss = k; +#ifdef DEBUG_ELF + printk("\n"); +#endif + } + } + + /* Now use mmap to map the library into memory. */ + if(error < 0 && error > -1024) { +#ifdef DEBUG_ELF + printk("got error %d\n", error); +#endif + kfree(elf_phdata); + return 0xffffffff; + } + + /* Now fill out the bss section. First pad the last page up + * to the page boundary, and then perform a mmap to make sure + * that there are zero-mapped pages up to and including the + * last bss page. + */ +#ifdef DEBUG_ELF + printk("padzero(%08lx) ", (unsigned long) (elf_bss)); +#endif + padzero(elf_bss); + len = (elf_bss + 0xfff) & 0xfffff000; /* What we have mapped so far */ + +#ifdef DEBUG_ELF + printk("last_bss[%08lx] len[%08lx]\n", (unsigned long) last_bss, + (unsigned long) len); +#endif + + /* Map the last of the bss segment */ + if (last_bss > len) { + down_write(¤t->mm->mmap_sem); + do_brk(len, (last_bss - len)); + up_write(¤t->mm->mmap_sem); + } + kfree(elf_phdata); + + *interp_load_addr = load_addr; + return ((unsigned int) interp_elf_ex->e_entry); +} + +/* Check sanity of IRIX elf executable header. */ +static int verify_binary(struct elfhdr *ehp, struct linux_binprm *bprm) +{ + if (memcmp(ehp->e_ident, ELFMAG, SELFMAG) != 0) + return -ENOEXEC; + + /* First of all, some simple consistency checks */ + if((ehp->e_type != ET_EXEC && ehp->e_type != ET_DYN) || + !irix_elf_check_arch(ehp) || !bprm->file->f_op->mmap) { + return -ENOEXEC; + } + + /* Only support MIPS ARCH2 or greater IRIX binaries for now. */ + if(!(ehp->e_flags & EF_MIPS_ARCH) && !(ehp->e_flags & 0x04)) { + return -ENOEXEC; + } + + /* XXX Don't support N32 or 64bit binaries yet because they can + * XXX and do execute 64 bit instructions and expect all registers + * XXX to be 64 bit as well. We need to make the kernel save + * XXX all registers as 64bits on cpu's capable of this at + * XXX exception time plus frob the XTLB exception vector. + */ + if((ehp->e_flags & 0x20)) { + return -ENOEXEC; + } + + return 0; /* It's ok. */ +} + +#define IRIX_INTERP_PREFIX "/usr/gnemul/irix" + +/* Look for an IRIX ELF interpreter. */ +static inline int look_for_irix_interpreter(char **name, + struct file **interpreter, + struct elfhdr *interp_elf_ex, + struct elf_phdr *epp, + struct linux_binprm *bprm, int pnum) +{ + int i; + int retval = -EINVAL; + struct file *file = NULL; + + *name = NULL; + for(i = 0; i < pnum; i++, epp++) { + if (epp->p_type != PT_INTERP) + continue; + + /* It is illegal to have two interpreters for one executable. */ + if (*name != NULL) + goto out; + + *name = kmalloc((epp->p_filesz + strlen(IRIX_INTERP_PREFIX)), + GFP_KERNEL); + if (!*name) + return -ENOMEM; + + strcpy(*name, IRIX_INTERP_PREFIX); + retval = kernel_read(bprm->file, epp->p_offset, (*name + 16), + epp->p_filesz); + if (retval < 0) + goto out; + + file = open_exec(*name); + if (IS_ERR(file)) { + retval = PTR_ERR(file); + goto out; + } + retval = kernel_read(file, 0, bprm->buf, 128); + if (retval < 0) + goto dput_and_out; + + *interp_elf_ex = *(struct elfhdr *) bprm->buf; + } + *interpreter = file; + return 0; + +dput_and_out: + fput(file); +out: + kfree(*name); + return retval; +} + +static inline int verify_irix_interpreter(struct elfhdr *ihp) +{ + if (memcmp(ihp->e_ident, ELFMAG, SELFMAG) != 0) + return -ELIBBAD; + return 0; +} + +#define EXEC_MAP_FLAGS (MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE) + +static inline void map_executable(struct file *fp, struct elf_phdr *epp, int pnum, + unsigned int *estack, unsigned int *laddr, + unsigned int *scode, unsigned int *ebss, + unsigned int *ecode, unsigned int *edata, + unsigned int *ebrk) +{ + unsigned int tmp; + int i, prot; + + for(i = 0; i < pnum; i++, epp++) { + if(epp->p_type != PT_LOAD) + continue; + + /* Map it. */ + prot = (epp->p_flags & PF_R) ? PROT_READ : 0; + prot |= (epp->p_flags & PF_W) ? PROT_WRITE : 0; + prot |= (epp->p_flags & PF_X) ? PROT_EXEC : 0; + down_write(¤t->mm->mmap_sem); + (void) do_mmap(fp, (epp->p_vaddr & 0xfffff000), + (epp->p_filesz + (epp->p_vaddr & 0xfff)), + prot, EXEC_MAP_FLAGS, + (epp->p_offset & 0xfffff000)); + up_write(¤t->mm->mmap_sem); + + /* Fixup location tracking vars. */ + if((epp->p_vaddr & 0xfffff000) < *estack) + *estack = (epp->p_vaddr & 0xfffff000); + if(!*laddr) + *laddr = epp->p_vaddr - epp->p_offset; + if(epp->p_vaddr < *scode) + *scode = epp->p_vaddr; + + tmp = epp->p_vaddr + epp->p_filesz; + if(tmp > *ebss) + *ebss = tmp; + if((epp->p_flags & PF_X) && *ecode < tmp) + *ecode = tmp; + if(*edata < tmp) + *edata = tmp; + + tmp = epp->p_vaddr + epp->p_memsz; + if(tmp > *ebrk) + *ebrk = tmp; + } + +} + +static inline int map_interpreter(struct elf_phdr *epp, struct elfhdr *ihp, + struct file *interp, unsigned int *iladdr, + int pnum, mm_segment_t old_fs, + unsigned int *eentry) +{ + int i; + + *eentry = 0xffffffff; + for(i = 0; i < pnum; i++, epp++) { + if(epp->p_type != PT_INTERP) + continue; + + /* We should have fielded this error elsewhere... */ + if(*eentry != 0xffffffff) + return -1; + + set_fs(old_fs); + *eentry = load_irix_interp(ihp, interp, iladdr); + old_fs = get_fs(); + set_fs(get_ds()); + + fput(interp); + + if (*eentry == 0xffffffff) + return -1; + } + return 0; +} + +/* + * IRIX maps a page at 0x200000 that holds information about the + * process and the system, here we map the page and fill the + * structure + */ +void irix_map_prda_page (void) +{ + unsigned long v; + struct prda *pp; + + down_write(¤t->mm->mmap_sem); + v = do_brk (PRDA_ADDRESS, PAGE_SIZE); + up_write(¤t->mm->mmap_sem); + + if (v < 0) + return; + + pp = (struct prda *) v; + pp->prda_sys.t_pid = current->pid; + pp->prda_sys.t_prid = read_c0_prid(); + pp->prda_sys.t_rpid = current->pid; + + /* We leave the rest set to zero */ +} + + + +/* These are the functions used to load ELF style executables and shared + * libraries. There is no binary dependent code anywhere else. + */ +static int load_irix_binary(struct linux_binprm * bprm, struct pt_regs * regs) +{ + struct elfhdr elf_ex, interp_elf_ex; + struct file *interpreter; + struct elf_phdr *elf_phdata, *elf_ihdr, *elf_ephdr; + unsigned int load_addr, elf_bss, elf_brk; + unsigned int elf_entry, interp_load_addr = 0; + unsigned int start_code, end_code, end_data, elf_stack; + int retval, has_interp, has_ephdr, size, i; + char *elf_interpreter; + mm_segment_t old_fs; + + load_addr = 0; + has_interp = has_ephdr = 0; + elf_ihdr = elf_ephdr = 0; + elf_ex = *((struct elfhdr *) bprm->buf); + retval = -ENOEXEC; + + if (verify_binary(&elf_ex, bprm)) + goto out; + +#ifdef DEBUG_ELF + print_elfhdr(&elf_ex); +#endif + + /* Now read in all of the header information */ + size = elf_ex.e_phentsize * elf_ex.e_phnum; + if (size > 65536) + goto out; + elf_phdata = kmalloc(size, GFP_KERNEL); + if (elf_phdata == NULL) { + retval = -ENOMEM; + goto out; + } + + retval = kernel_read(bprm->file, elf_ex.e_phoff, (char *)elf_phdata, size); + + if (retval < 0) + goto out_free_ph; + +#ifdef DEBUG_ELF + dump_phdrs(elf_phdata, elf_ex.e_phnum); +#endif + + /* Set some things for later. */ + for(i = 0; i < elf_ex.e_phnum; i++) { + switch(elf_phdata[i].p_type) { + case PT_INTERP: + has_interp = 1; + elf_ihdr = &elf_phdata[i]; + break; + case PT_PHDR: + has_ephdr = 1; + elf_ephdr = &elf_phdata[i]; + break; + }; + } +#ifdef DEBUG_ELF + printk("\n"); +#endif + + elf_bss = 0; + elf_brk = 0; + + elf_stack = 0xffffffff; + elf_interpreter = NULL; + start_code = 0xffffffff; + end_code = 0; + end_data = 0; + + retval = look_for_irix_interpreter(&elf_interpreter, + &interpreter, + &interp_elf_ex, elf_phdata, bprm, + elf_ex.e_phnum); + if (retval) + goto out_free_file; + + if (elf_interpreter) { + retval = verify_irix_interpreter(&interp_elf_ex); + if(retval) + goto out_free_interp; + } + + /* OK, we are done with that, now set up the arg stuff, + * and then start this sucker up. + */ + retval = -E2BIG; + if (!bprm->sh_bang && !bprm->p) + goto out_free_interp; + + /* Flush all traces of the currently running executable */ + retval = flush_old_exec(bprm); + if (retval) + goto out_free_dentry; + + /* OK, This is the point of no return */ + current->mm->end_data = 0; + current->mm->end_code = 0; + current->mm->mmap = NULL; + current->flags &= ~PF_FORKNOEXEC; + elf_entry = (unsigned int) elf_ex.e_entry; + + /* Do this so that we can load the interpreter, if need be. We will + * change some of these later. + */ + set_mm_counter(current->mm, rss, 0); + setup_arg_pages(bprm, STACK_TOP, EXSTACK_DEFAULT); + current->mm->start_stack = bprm->p; + + /* At this point, we assume that the image should be loaded at + * fixed address, not at a variable address. + */ + old_fs = get_fs(); + set_fs(get_ds()); + + map_executable(bprm->file, elf_phdata, elf_ex.e_phnum, &elf_stack, + &load_addr, &start_code, &elf_bss, &end_code, + &end_data, &elf_brk); + + if(elf_interpreter) { + retval = map_interpreter(elf_phdata, &interp_elf_ex, + interpreter, &interp_load_addr, + elf_ex.e_phnum, old_fs, &elf_entry); + kfree(elf_interpreter); + if(retval) { + set_fs(old_fs); + printk("Unable to load IRIX ELF interpreter\n"); + send_sig(SIGSEGV, current, 0); + retval = 0; + goto out_free_file; + } + } + + set_fs(old_fs); + + kfree(elf_phdata); + set_personality(PER_IRIX32); + set_binfmt(&irix_format); + compute_creds(bprm); + current->flags &= ~PF_FORKNOEXEC; + bprm->p = (unsigned long) + create_irix_tables((char *)bprm->p, bprm->argc, bprm->envc, + (elf_interpreter ? &elf_ex : NULL), + load_addr, interp_load_addr, regs, elf_ephdr); + current->mm->start_brk = current->mm->brk = elf_brk; + current->mm->end_code = end_code; + current->mm->start_code = start_code; + current->mm->end_data = end_data; + current->mm->start_stack = bprm->p; + + /* Calling set_brk effectively mmaps the pages that we need for the + * bss and break sections. + */ + set_brk(elf_bss, elf_brk); + + /* + * IRIX maps a page at 0x200000 which holds some system + * information. Programs depend on this. + */ + irix_map_prda_page (); + + padzero(elf_bss); + +#ifdef DEBUG_ELF + printk("(start_brk) %lx\n" , (long) current->mm->start_brk); + printk("(end_code) %lx\n" , (long) current->mm->end_code); + printk("(start_code) %lx\n" , (long) current->mm->start_code); + printk("(end_data) %lx\n" , (long) current->mm->end_data); + printk("(start_stack) %lx\n" , (long) current->mm->start_stack); + printk("(brk) %lx\n" , (long) current->mm->brk); +#endif + +#if 0 /* XXX No fucking way dude... */ + /* Why this, you ask??? Well SVr4 maps page 0 as read-only, + * and some applications "depend" upon this behavior. + * Since we do not have the power to recompile these, we + * emulate the SVr4 behavior. Sigh. + */ + down_write(¤t->mm->mmap_sem); + (void) do_mmap(NULL, 0, 4096, PROT_READ | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE, 0); + up_write(¤t->mm->mmap_sem); +#endif + + start_thread(regs, elf_entry, bprm->p); + if (current->ptrace & PT_PTRACED) + send_sig(SIGTRAP, current, 0); + return 0; +out: + return retval; + +out_free_dentry: + allow_write_access(interpreter); + fput(interpreter); +out_free_interp: + if (elf_interpreter) + kfree(elf_interpreter); +out_free_file: +out_free_ph: + kfree (elf_phdata); + goto out; +} + +/* This is really simpleminded and specialized - we are loading an + * a.out library that is given an ELF header. + */ +static int load_irix_library(struct file *file) +{ + struct elfhdr elf_ex; + struct elf_phdr *elf_phdata = NULL; + unsigned int len = 0; + int elf_bss = 0; + int retval; + unsigned int bss; + int error; + int i,j, k; + + error = kernel_read(file, 0, (char *) &elf_ex, sizeof(elf_ex)); + if (error != sizeof(elf_ex)) + return -ENOEXEC; + + if (memcmp(elf_ex.e_ident, ELFMAG, SELFMAG) != 0) + return -ENOEXEC; + + /* First of all, some simple consistency checks. */ + if(elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 || + !irix_elf_check_arch(&elf_ex) || !file->f_op->mmap) + return -ENOEXEC; + + /* Now read in all of the header information. */ + if(sizeof(struct elf_phdr) * elf_ex.e_phnum > PAGE_SIZE) + return -ENOEXEC; + + elf_phdata = kmalloc(sizeof(struct elf_phdr) * elf_ex.e_phnum, GFP_KERNEL); + if (elf_phdata == NULL) + return -ENOMEM; + + retval = kernel_read(file, elf_ex.e_phoff, (char *) elf_phdata, + sizeof(struct elf_phdr) * elf_ex.e_phnum); + + j = 0; + for(i=0; i<elf_ex.e_phnum; i++) + if((elf_phdata + i)->p_type == PT_LOAD) j++; + + if(j != 1) { + kfree(elf_phdata); + return -ENOEXEC; + } + + while(elf_phdata->p_type != PT_LOAD) elf_phdata++; + + /* Now use mmap to map the library into memory. */ + down_write(¤t->mm->mmap_sem); + error = do_mmap(file, + elf_phdata->p_vaddr & 0xfffff000, + elf_phdata->p_filesz + (elf_phdata->p_vaddr & 0xfff), + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE, + elf_phdata->p_offset & 0xfffff000); + up_write(¤t->mm->mmap_sem); + + k = elf_phdata->p_vaddr + elf_phdata->p_filesz; + if (k > elf_bss) elf_bss = k; + + if (error != (elf_phdata->p_vaddr & 0xfffff000)) { + kfree(elf_phdata); + return error; + } + + padzero(elf_bss); + + len = (elf_phdata->p_filesz + elf_phdata->p_vaddr+ 0xfff) & 0xfffff000; + bss = elf_phdata->p_memsz + elf_phdata->p_vaddr; + if (bss > len) { + down_write(¤t->mm->mmap_sem); + do_brk(len, bss-len); + up_write(¤t->mm->mmap_sem); + } + kfree(elf_phdata); + return 0; +} + +/* Called through irix_syssgi() to map an elf image given an FD, + * a phdr ptr USER_PHDRP in userspace, and a count CNT telling how many + * phdrs there are in the USER_PHDRP array. We return the vaddr the + * first phdr was successfully mapped to. + */ +unsigned long irix_mapelf(int fd, struct elf_phdr *user_phdrp, int cnt) +{ + struct elf_phdr *hp; + struct file *filp; + int i, retval; + +#ifdef DEBUG_ELF + printk("irix_mapelf: fd[%d] user_phdrp[%p] cnt[%d]\n", + fd, user_phdrp, cnt); +#endif + + /* First get the verification out of the way. */ + hp = user_phdrp; + if (!access_ok(VERIFY_READ, hp, (sizeof(struct elf_phdr) * cnt))) { +#ifdef DEBUG_ELF + printk("irix_mapelf: access_ok fails!\n"); +#endif + return -EFAULT; + } + +#ifdef DEBUG_ELF + dump_phdrs(user_phdrp, cnt); +#endif + + for(i = 0; i < cnt; i++, hp++) + if(hp->p_type != PT_LOAD) { + printk("irix_mapelf: One section is not PT_LOAD!\n"); + return -ENOEXEC; + } + + filp = fget(fd); + if (!filp) + return -EACCES; + if(!filp->f_op) { + printk("irix_mapelf: Bogon filp!\n"); + fput(filp); + return -EACCES; + } + + hp = user_phdrp; + for(i = 0; i < cnt; i++, hp++) { + int prot; + + prot = (hp->p_flags & PF_R) ? PROT_READ : 0; + prot |= (hp->p_flags & PF_W) ? PROT_WRITE : 0; + prot |= (hp->p_flags & PF_X) ? PROT_EXEC : 0; + down_write(¤t->mm->mmap_sem); + retval = do_mmap(filp, (hp->p_vaddr & 0xfffff000), + (hp->p_filesz + (hp->p_vaddr & 0xfff)), + prot, (MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE), + (hp->p_offset & 0xfffff000)); + up_write(¤t->mm->mmap_sem); + + if(retval != (hp->p_vaddr & 0xfffff000)) { + printk("irix_mapelf: do_mmap fails with %d!\n", retval); + fput(filp); + return retval; + } + } + +#ifdef DEBUG_ELF + printk("irix_mapelf: Success, returning %08lx\n", + (unsigned long) user_phdrp->p_vaddr); +#endif + fput(filp); + return user_phdrp->p_vaddr; +} + +/* + * ELF core dumper + * + * Modelled on fs/exec.c:aout_core_dump() + * Jeremy Fitzhardinge <jeremy@sw.oz.au> + */ + +/* These are the only things you should do on a core-file: use only these + * functions to write out all the necessary info. + */ +static int dump_write(struct file *file, const void *addr, int nr) +{ + return file->f_op->write(file, addr, nr, &file->f_pos) == nr; +} + +static int dump_seek(struct file *file, off_t off) +{ + if (file->f_op->llseek) { + if (file->f_op->llseek(file, off, 0) != off) + return 0; + } else + file->f_pos = off; + return 1; +} + +/* Decide whether a segment is worth dumping; default is yes to be + * sure (missing info is worse than too much; etc). + * Personally I'd include everything, and use the coredump limit... + * + * I think we should skip something. But I am not sure how. H.J. + */ +static inline int maydump(struct vm_area_struct *vma) +{ + if (!(vma->vm_flags & (VM_READ|VM_WRITE|VM_EXEC))) + return 0; +#if 1 + if (vma->vm_flags & (VM_WRITE|VM_GROWSUP|VM_GROWSDOWN)) + return 1; + if (vma->vm_flags & (VM_READ|VM_EXEC|VM_EXECUTABLE|VM_SHARED)) + return 0; +#endif + return 1; +} + +#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) + +/* An ELF note in memory. */ +struct memelfnote +{ + const char *name; + int type; + unsigned int datasz; + void *data; +}; + +static int notesize(struct memelfnote *en) +{ + int sz; + + sz = sizeof(struct elf_note); + sz += roundup(strlen(en->name), 4); + sz += roundup(en->datasz, 4); + + return sz; +} + +/* #define DEBUG */ + +#define DUMP_WRITE(addr, nr) \ + if (!dump_write(file, (addr), (nr))) \ + goto end_coredump; +#define DUMP_SEEK(off) \ + if (!dump_seek(file, (off))) \ + goto end_coredump; + +static int writenote(struct memelfnote *men, struct file *file) +{ + struct elf_note en; + + en.n_namesz = strlen(men->name); + en.n_descsz = men->datasz; + en.n_type = men->type; + + DUMP_WRITE(&en, sizeof(en)); + DUMP_WRITE(men->name, en.n_namesz); + /* XXX - cast from long long to long to avoid need for libgcc.a */ + DUMP_SEEK(roundup((unsigned long)file->f_pos, 4)); /* XXX */ + DUMP_WRITE(men->data, men->datasz); + DUMP_SEEK(roundup((unsigned long)file->f_pos, 4)); /* XXX */ + + return 1; + +end_coredump: + return 0; +} +#undef DUMP_WRITE +#undef DUMP_SEEK + +#define DUMP_WRITE(addr, nr) \ + if (!dump_write(file, (addr), (nr))) \ + goto end_coredump; +#define DUMP_SEEK(off) \ + if (!dump_seek(file, (off))) \ + goto end_coredump; + +/* Actual dumper. + * + * This is a two-pass process; first we find the offsets of the bits, + * and then they are actually written out. If we run out of core limit + * we just truncate. + */ +static int irix_core_dump(long signr, struct pt_regs * regs, struct file *file) +{ + int has_dumped = 0; + mm_segment_t fs; + int segs; + int i; + size_t size; + struct vm_area_struct *vma; + struct elfhdr elf; + off_t offset = 0, dataoff; + int limit = current->signal->rlim[RLIMIT_CORE].rlim_cur; + int numnote = 4; + struct memelfnote notes[4]; + struct elf_prstatus prstatus; /* NT_PRSTATUS */ + elf_fpregset_t fpu; /* NT_PRFPREG */ + struct elf_prpsinfo psinfo; /* NT_PRPSINFO */ + + /* Count what's needed to dump, up to the limit of coredump size. */ + segs = 0; + size = 0; + for(vma = current->mm->mmap; vma != NULL; vma = vma->vm_next) { + if (maydump(vma)) + { + int sz = vma->vm_end-vma->vm_start; + + if (size+sz >= limit) + break; + else + size += sz; + } + + segs++; + } +#ifdef DEBUG + printk("irix_core_dump: %d segs taking %d bytes\n", segs, size); +#endif + + /* Set up header. */ + memcpy(elf.e_ident, ELFMAG, SELFMAG); + elf.e_ident[EI_CLASS] = ELFCLASS32; + elf.e_ident[EI_DATA] = ELFDATA2LSB; + elf.e_ident[EI_VERSION] = EV_CURRENT; + elf.e_ident[EI_OSABI] = ELF_OSABI; + memset(elf.e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD); + + elf.e_type = ET_CORE; + elf.e_machine = ELF_ARCH; + elf.e_version = EV_CURRENT; + elf.e_entry = 0; + elf.e_phoff = sizeof(elf); + elf.e_shoff = 0; + elf.e_flags = 0; + elf.e_ehsize = sizeof(elf); + elf.e_phentsize = sizeof(struct elf_phdr); + elf.e_phnum = segs+1; /* Include notes. */ + elf.e_shentsize = 0; + elf.e_shnum = 0; + elf.e_shstrndx = 0; + + fs = get_fs(); + set_fs(KERNEL_DS); + + has_dumped = 1; + current->flags |= PF_DUMPCORE; + + DUMP_WRITE(&elf, sizeof(elf)); + offset += sizeof(elf); /* Elf header. */ + offset += (segs+1) * sizeof(struct elf_phdr); /* Program headers. */ + + /* Set up the notes in similar form to SVR4 core dumps made + * with info from their /proc. + */ + memset(&psinfo, 0, sizeof(psinfo)); + memset(&prstatus, 0, sizeof(prstatus)); + + notes[0].name = "CORE"; + notes[0].type = NT_PRSTATUS; + notes[0].datasz = sizeof(prstatus); + notes[0].data = &prstatus; + prstatus.pr_info.si_signo = prstatus.pr_cursig = signr; + prstatus.pr_sigpend = current->pending.signal.sig[0]; + prstatus.pr_sighold = current->blocked.sig[0]; + psinfo.pr_pid = prstatus.pr_pid = current->pid; + psinfo.pr_ppid = prstatus.pr_ppid = current->parent->pid; + psinfo.pr_pgrp = prstatus.pr_pgrp = process_group(current); + psinfo.pr_sid = prstatus.pr_sid = current->signal->session; + if (current->pid == current->tgid) { + /* + * This is the record for the group leader. Add in the + * cumulative times of previous dead threads. This total + * won't include the time of each live thread whose state + * is included in the core dump. The final total reported + * to our parent process when it calls wait4 will include + * those sums as well as the little bit more time it takes + * this and each other thread to finish dying after the + * core dump synchronization phase. + */ + jiffies_to_timeval(current->utime + current->signal->utime, + &prstatus.pr_utime); + jiffies_to_timeval(current->stime + current->signal->stime, + &prstatus.pr_stime); + } else { + jiffies_to_timeval(current->utime, &prstatus.pr_utime); + jiffies_to_timeval(current->stime, &prstatus.pr_stime); + } + jiffies_to_timeval(current->signal->cutime, &prstatus.pr_cutime); + jiffies_to_timeval(current->signal->cstime, &prstatus.pr_cstime); + + if (sizeof(elf_gregset_t) != sizeof(struct pt_regs)) { + printk("sizeof(elf_gregset_t) (%d) != sizeof(struct pt_regs) " + "(%d)\n", sizeof(elf_gregset_t), sizeof(struct pt_regs)); + } else { + *(struct pt_regs *)&prstatus.pr_reg = *regs; + } + + notes[1].name = "CORE"; + notes[1].type = NT_PRPSINFO; + notes[1].datasz = sizeof(psinfo); + notes[1].data = &psinfo; + i = current->state ? ffz(~current->state) + 1 : 0; + psinfo.pr_state = i; + psinfo.pr_sname = (i < 0 || i > 5) ? '.' : "RSDZTD"[i]; + psinfo.pr_zomb = psinfo.pr_sname == 'Z'; + psinfo.pr_nice = task_nice(current); + psinfo.pr_flag = current->flags; + psinfo.pr_uid = current->uid; + psinfo.pr_gid = current->gid; + { + int i, len; + + set_fs(fs); + + len = current->mm->arg_end - current->mm->arg_start; + len = len >= ELF_PRARGSZ ? ELF_PRARGSZ : len; + copy_from_user(&psinfo.pr_psargs, + (const char *)current->mm->arg_start, len); + for(i = 0; i < len; i++) + if (psinfo.pr_psargs[i] == 0) + psinfo.pr_psargs[i] = ' '; + psinfo.pr_psargs[len] = 0; + + set_fs(KERNEL_DS); + } + strlcpy(psinfo.pr_fname, current->comm, sizeof(psinfo.pr_fname)); + + notes[2].name = "CORE"; + notes[2].type = NT_TASKSTRUCT; + notes[2].datasz = sizeof(*current); + notes[2].data = current; + + /* Try to dump the FPU. */ + prstatus.pr_fpvalid = dump_fpu (regs, &fpu); + if (!prstatus.pr_fpvalid) { + numnote--; + } else { + notes[3].name = "CORE"; + notes[3].type = NT_PRFPREG; + notes[3].datasz = sizeof(fpu); + notes[3].data = &fpu; + } + + /* Write notes phdr entry. */ + { + struct elf_phdr phdr; + int sz = 0; + + for(i = 0; i < numnote; i++) + sz += notesize(¬es[i]); + + phdr.p_type = PT_NOTE; + phdr.p_offset = offset; + phdr.p_vaddr = 0; + phdr.p_paddr = 0; + phdr.p_filesz = sz; + phdr.p_memsz = 0; + phdr.p_flags = 0; + phdr.p_align = 0; + + offset += phdr.p_filesz; + DUMP_WRITE(&phdr, sizeof(phdr)); + } + + /* Page-align dumped data. */ + dataoff = offset = roundup(offset, PAGE_SIZE); + + /* Write program headers for segments dump. */ + for(vma = current->mm->mmap, i = 0; + i < segs && vma != NULL; vma = vma->vm_next) { + struct elf_phdr phdr; + size_t sz; + + i++; + + sz = vma->vm_end - vma->vm_start; + + phdr.p_type = PT_LOAD; + phdr.p_offset = offset; + phdr.p_vaddr = vma->vm_start; + phdr.p_paddr = 0; + phdr.p_filesz = maydump(vma) ? sz : 0; + phdr.p_memsz = sz; + offset += phdr.p_filesz; + phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0; + if (vma->vm_flags & VM_WRITE) phdr.p_flags |= PF_W; + if (vma->vm_flags & VM_EXEC) phdr.p_flags |= PF_X; + phdr.p_align = PAGE_SIZE; + + DUMP_WRITE(&phdr, sizeof(phdr)); + } + + for(i = 0; i < numnote; i++) + if (!writenote(¬es[i], file)) + goto end_coredump; + + set_fs(fs); + + DUMP_SEEK(dataoff); + + for(i = 0, vma = current->mm->mmap; + i < segs && vma != NULL; + vma = vma->vm_next) { + unsigned long addr = vma->vm_start; + unsigned long len = vma->vm_end - vma->vm_start; + + if (!maydump(vma)) + continue; + i++; +#ifdef DEBUG + printk("elf_core_dump: writing %08lx %lx\n", addr, len); +#endif + DUMP_WRITE((void *)addr, len); + } + + if ((off_t) file->f_pos != offset) { + /* Sanity check. */ + printk("elf_core_dump: file->f_pos (%ld) != offset (%ld)\n", + (off_t) file->f_pos, offset); + } + +end_coredump: + set_fs(fs); + return has_dumped; +} + +static int __init init_irix_binfmt(void) +{ + int init_inventory(void); + extern asmlinkage unsigned long sys_call_table; + extern asmlinkage unsigned long sys_call_table_irix5; + + init_inventory(); + + /* + * Copy the IRIX5 syscall table (8000 bytes) into the main syscall + * table. The IRIX5 calls are located by an offset of 8000 bytes + * from the beginning of the main table. + */ + memcpy((void *) ((unsigned long) &sys_call_table + 8000), + &sys_call_table_irix5, 8000); + + return register_binfmt(&irix_format); +} + +static void __exit exit_irix_binfmt(void) +{ + /* Remove the IRIX ELF loaders. */ + unregister_binfmt(&irix_format); +} + +module_init(init_irix_binfmt) +module_exit(exit_irix_binfmt) diff --git a/arch/mips/kernel/irixinv.c b/arch/mips/kernel/irixinv.c new file mode 100644 index 000000000000..60aa98cd1791 --- /dev/null +++ b/arch/mips/kernel/irixinv.c @@ -0,0 +1,77 @@ +/* + * Support the inventory interface for IRIX binaries + * This is invoked before the mm layer is working, so we do not + * use the linked lists for the inventory yet. + * + * Miguel de Icaza, 1997. + */ +#include <linux/mm.h> +#include <asm/inventory.h> +#include <asm/uaccess.h> + +#define MAX_INVENTORY 50 +int inventory_items = 0; + +static inventory_t inventory [MAX_INVENTORY]; + +void add_to_inventory (int class, int type, int controller, int unit, int state) +{ + inventory_t *ni = &inventory [inventory_items]; + + if (inventory_items == MAX_INVENTORY) + return; + + ni->inv_class = class; + ni->inv_type = type; + ni->inv_controller = controller; + ni->inv_unit = unit; + ni->inv_state = state; + ni->inv_next = ni; + inventory_items++; +} + +int dump_inventory_to_user (void *userbuf, int size) +{ + inventory_t *inv = &inventory [0]; + inventory_t *user = userbuf; + int v; + + if (!access_ok(VERIFY_WRITE, userbuf, size)) + return -EFAULT; + + for (v = 0; v < inventory_items; v++){ + inv = &inventory [v]; + copy_to_user (user, inv, sizeof (inventory_t)); + user++; + } + return inventory_items * sizeof (inventory_t); +} + +int __init init_inventory(void) +{ + /* + * gross hack while we put the right bits all over the kernel + * most likely this will not let just anyone run the X server + * until we put the right values all over the place + */ + add_to_inventory (10, 3, 0, 0, 16400); + add_to_inventory (1, 1, 150, -1, 12); + add_to_inventory (1, 3, 0, 0, 8976); + add_to_inventory (1, 2, 0, 0, 8976); + add_to_inventory (4, 8, 0, 0, 2); + add_to_inventory (5, 5, 0, 0, 1); + add_to_inventory (3, 3, 0, 0, 32768); + add_to_inventory (3, 4, 0, 0, 32768); + add_to_inventory (3, 8, 0, 0, 524288); + add_to_inventory (3, 9, 0, 0, 64); + add_to_inventory (3, 1, 0, 0, 67108864); + add_to_inventory (12, 3, 0, 0, 16); + add_to_inventory (8, 7, 17, 0, 16777472); + add_to_inventory (8, 0, 0, 0, 1); + add_to_inventory (2, 1, 0, 13, 2); + add_to_inventory (2, 2, 0, 2, 0); + add_to_inventory (2, 2, 0, 1, 0); + add_to_inventory (7, 14, 0, 0, 6); + + return 0; +} diff --git a/arch/mips/kernel/irixioctl.c b/arch/mips/kernel/irixioctl.c new file mode 100644 index 000000000000..4cd3d38a22c2 --- /dev/null +++ b/arch/mips/kernel/irixioctl.c @@ -0,0 +1,261 @@ +/* + * irixioctl.c: A fucking mess... + * + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/sockios.h> +#include <linux/syscalls.h> +#include <linux/tty.h> +#include <linux/file.h> + +#include <asm/uaccess.h> +#include <asm/ioctl.h> +#include <asm/ioctls.h> + +#undef DEBUG_IOCTLS +#undef DEBUG_MISSING_IOCTL + +struct irix_termios { + tcflag_t c_iflag, c_oflag, c_cflag, c_lflag; + cc_t c_cc[NCCS]; +}; + +extern void start_tty(struct tty_struct *tty); +static struct tty_struct *get_tty(int fd) +{ + struct file *filp; + struct tty_struct *ttyp = NULL; + + spin_lock(¤t->files->file_lock); + filp = fcheck(fd); + if(filp && filp->private_data) { + ttyp = (struct tty_struct *) filp->private_data; + + if(ttyp->magic != TTY_MAGIC) + ttyp =NULL; + } + spin_unlock(¤t->files->file_lock); + return ttyp; +} + +static struct tty_struct *get_real_tty(struct tty_struct *tp) +{ + if (tp->driver->type == TTY_DRIVER_TYPE_PTY && + tp->driver->subtype == PTY_TYPE_MASTER) + return tp->link; + else + return tp; +} + +asmlinkage int irix_ioctl(int fd, unsigned long cmd, unsigned long arg) +{ + struct tty_struct *tp, *rtp; + mm_segment_t old_fs; + int error = 0; + +#ifdef DEBUG_IOCTLS + printk("[%s:%d] irix_ioctl(%d, ", current->comm, current->pid, fd); +#endif + switch(cmd) { + case 0x00005401: +#ifdef DEBUG_IOCTLS + printk("TCGETA, %08lx) ", arg); +#endif + error = sys_ioctl(fd, TCGETA, arg); + break; + + case 0x0000540d: { + struct termios kt; + struct irix_termios *it = (struct irix_termios *) arg; + +#ifdef DEBUG_IOCTLS + printk("TCGETS, %08lx) ", arg); +#endif + if(!access_ok(VERIFY_WRITE, it, sizeof(*it))) { + error = -EFAULT; + break; + } + old_fs = get_fs(); set_fs(get_ds()); + error = sys_ioctl(fd, TCGETS, (unsigned long) &kt); + set_fs(old_fs); + if (error) + break; + __put_user(kt.c_iflag, &it->c_iflag); + __put_user(kt.c_oflag, &it->c_oflag); + __put_user(kt.c_cflag, &it->c_cflag); + __put_user(kt.c_lflag, &it->c_lflag); + for(error = 0; error < NCCS; error++) + __put_user(kt.c_cc[error], &it->c_cc[error]); + error = 0; + break; + } + + case 0x0000540e: { + struct termios kt; + struct irix_termios *it = (struct irix_termios *) arg; + +#ifdef DEBUG_IOCTLS + printk("TCSETS, %08lx) ", arg); +#endif + if (!access_ok(VERIFY_READ, it, sizeof(*it))) { + error = -EFAULT; + break; + } + old_fs = get_fs(); set_fs(get_ds()); + error = sys_ioctl(fd, TCGETS, (unsigned long) &kt); + set_fs(old_fs); + if(error) + break; + __get_user(kt.c_iflag, &it->c_iflag); + __get_user(kt.c_oflag, &it->c_oflag); + __get_user(kt.c_cflag, &it->c_cflag); + __get_user(kt.c_lflag, &it->c_lflag); + for(error = 0; error < NCCS; error++) + __get_user(kt.c_cc[error], &it->c_cc[error]); + old_fs = get_fs(); set_fs(get_ds()); + error = sys_ioctl(fd, TCSETS, (unsigned long) &kt); + set_fs(old_fs); + break; + } + + case 0x0000540f: +#ifdef DEBUG_IOCTLS + printk("TCSETSW, %08lx) ", arg); +#endif + error = sys_ioctl(fd, TCSETSW, arg); + break; + + case 0x00005471: +#ifdef DEBUG_IOCTLS + printk("TIOCNOTTY, %08lx) ", arg); +#endif + error = sys_ioctl(fd, TIOCNOTTY, arg); + break; + + case 0x00007416: +#ifdef DEBUG_IOCTLS + printk("TIOCGSID, %08lx) ", arg); +#endif + tp = get_tty(fd); + if(!tp) { + error = -EINVAL; + break; + } + rtp = get_real_tty(tp); +#ifdef DEBUG_IOCTLS + printk("rtp->session=%d ", rtp->session); +#endif + error = put_user(rtp->session, (unsigned long *) arg); + break; + + case 0x746e: + /* TIOCSTART, same effect as hitting ^Q */ +#ifdef DEBUG_IOCTLS + printk("TIOCSTART, %08lx) ", arg); +#endif + tp = get_tty(fd); + if(!tp) { + error = -EINVAL; + break; + } + rtp = get_real_tty(tp); + start_tty(rtp); + break; + + case 0x20006968: +#ifdef DEBUG_IOCTLS + printk("SIOCGETLABEL, %08lx) ", arg); +#endif + error = -ENOPKG; + break; + + case 0x40047477: +#ifdef DEBUG_IOCTLS + printk("TIOCGPGRP, %08lx) ", arg); +#endif + error = sys_ioctl(fd, TIOCGPGRP, arg); +#ifdef DEBUG_IOCTLS + printk("arg=%d ", *(int *)arg); +#endif + break; + + case 0x40087468: +#ifdef DEBUG_IOCTLS + printk("TIOCGWINSZ, %08lx) ", arg); +#endif + error = sys_ioctl(fd, TIOCGWINSZ, arg); + break; + + case 0x8004667e: +#ifdef DEBUG_IOCTLS + printk("FIONBIO, %08lx) arg=%d ", arg, *(int *)arg); +#endif + error = sys_ioctl(fd, FIONBIO, arg); + break; + + case 0x80047476: +#ifdef DEBUG_IOCTLS + printk("TIOCSPGRP, %08lx) arg=%d ", arg, *(int *)arg); +#endif + error = sys_ioctl(fd, TIOCSPGRP, arg); + break; + + case 0x8020690c: +#ifdef DEBUG_IOCTLS + printk("SIOCSIFADDR, %08lx) arg=%d ", arg, *(int *)arg); +#endif + error = sys_ioctl(fd, SIOCSIFADDR, arg); + break; + + case 0x80206910: +#ifdef DEBUG_IOCTLS + printk("SIOCSIFFLAGS, %08lx) arg=%d ", arg, *(int *)arg); +#endif + error = sys_ioctl(fd, SIOCSIFFLAGS, arg); + break; + + case 0xc0206911: +#ifdef DEBUG_IOCTLS + printk("SIOCGIFFLAGS, %08lx) arg=%d ", arg, *(int *)arg); +#endif + error = sys_ioctl(fd, SIOCGIFFLAGS, arg); + break; + + case 0xc020691b: +#ifdef DEBUG_IOCTLS + printk("SIOCGIFMETRIC, %08lx) arg=%d ", arg, *(int *)arg); +#endif + error = sys_ioctl(fd, SIOCGIFMETRIC, arg); + break; + + default: { +#ifdef DEBUG_MISSING_IOCTL + char *msg = "Unimplemented IOCTL cmd tell linux@engr.sgi.com\n"; + +#ifdef DEBUG_IOCTLS + printk("UNIMP_IOCTL, %08lx)\n", arg); +#endif + old_fs = get_fs(); set_fs(get_ds()); + sys_write(2, msg, strlen(msg)); + set_fs(old_fs); + printk("[%s:%d] Does unimplemented IRIX ioctl cmd %08lx\n", + current->comm, current->pid, cmd); + do_exit(255); +#else + error = sys_ioctl (fd, cmd, arg); +#endif + } + + }; +#ifdef DEBUG_IOCTLS + printk("error=%d\n", error); +#endif + return error; +} diff --git a/arch/mips/kernel/irixsig.c b/arch/mips/kernel/irixsig.c new file mode 100644 index 000000000000..3f956f809fa4 --- /dev/null +++ b/arch/mips/kernel/irixsig.c @@ -0,0 +1,853 @@ +/* + * irixsig.c: WHEEE, IRIX signals! YOW, am I compatible or what?!?! + * + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * Copyright (C) 1997 - 2000 Ralf Baechle (ralf@gnu.org) + * Copyright (C) 2000 Silicon Graphics, Inc. + */ +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/errno.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/time.h> +#include <linux/ptrace.h> + +#include <asm/ptrace.h> +#include <asm/uaccess.h> + +#undef DEBUG_SIG + +#define _S(nr) (1<<((nr)-1)) + +#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) + +typedef struct { + unsigned long sig[4]; +} irix_sigset_t; + +struct sigctx_irix5 { + u32 rmask, cp0_status; + u64 pc; + u64 regs[32]; + u64 fpregs[32]; + u32 usedfp, fpcsr, fpeir, sstk_flags; + u64 hi, lo; + u64 cp0_cause, cp0_badvaddr, _unused0; + irix_sigset_t sigset; + u64 weird_fpu_thing; + u64 _unused1[31]; +}; + +#ifdef DEBUG_SIG +/* Debugging */ +static inline void dump_irix5_sigctx(struct sigctx_irix5 *c) +{ + int i; + + printk("misc: rmask[%08lx] status[%08lx] pc[%08lx]\n", + (unsigned long) c->rmask, + (unsigned long) c->cp0_status, + (unsigned long) c->pc); + printk("regs: "); + for(i = 0; i < 16; i++) + printk("[%d]<%08lx> ", i, (unsigned long) c->regs[i]); + printk("\nregs: "); + for(i = 16; i < 32; i++) + printk("[%d]<%08lx> ", i, (unsigned long) c->regs[i]); + printk("\nfpregs: "); + for(i = 0; i < 16; i++) + printk("[%d]<%08lx> ", i, (unsigned long) c->fpregs[i]); + printk("\nfpregs: "); + for(i = 16; i < 32; i++) + printk("[%d]<%08lx> ", i, (unsigned long) c->fpregs[i]); + printk("misc: usedfp[%d] fpcsr[%08lx] fpeir[%08lx] stk_flgs[%08lx]\n", + (int) c->usedfp, (unsigned long) c->fpcsr, + (unsigned long) c->fpeir, (unsigned long) c->sstk_flags); + printk("misc: hi[%08lx] lo[%08lx] cause[%08lx] badvaddr[%08lx]\n", + (unsigned long) c->hi, (unsigned long) c->lo, + (unsigned long) c->cp0_cause, (unsigned long) c->cp0_badvaddr); + printk("misc: sigset<0>[%08lx] sigset<1>[%08lx] sigset<2>[%08lx] " + "sigset<3>[%08lx]\n", (unsigned long) c->sigset.sig[0], + (unsigned long) c->sigset.sig[1], + (unsigned long) c->sigset.sig[2], + (unsigned long) c->sigset.sig[3]); +} +#endif + +static void setup_irix_frame(struct k_sigaction *ka, struct pt_regs *regs, + int signr, sigset_t *oldmask) +{ + unsigned long sp; + struct sigctx_irix5 *ctx; + int i; + + sp = regs->regs[29]; + sp -= sizeof(struct sigctx_irix5); + sp &= ~(0xf); + ctx = (struct sigctx_irix5 *) sp; + if (!access_ok(VERIFY_WRITE, ctx, sizeof(*ctx))) + goto segv_and_exit; + + __put_user(0, &ctx->weird_fpu_thing); + __put_user(~(0x00000001), &ctx->rmask); + __put_user(0, &ctx->regs[0]); + for(i = 1; i < 32; i++) + __put_user((u64) regs->regs[i], &ctx->regs[i]); + + __put_user((u64) regs->hi, &ctx->hi); + __put_user((u64) regs->lo, &ctx->lo); + __put_user((u64) regs->cp0_epc, &ctx->pc); + __put_user(!!used_math(), &ctx->usedfp); + __put_user((u64) regs->cp0_cause, &ctx->cp0_cause); + __put_user((u64) regs->cp0_badvaddr, &ctx->cp0_badvaddr); + + __put_user(0, &ctx->sstk_flags); /* XXX sigstack unimp... todo... */ + + __copy_to_user(&ctx->sigset, oldmask, sizeof(irix_sigset_t)); + +#ifdef DEBUG_SIG + dump_irix5_sigctx(ctx); +#endif + + regs->regs[4] = (unsigned long) signr; + regs->regs[5] = 0; /* XXX sigcode XXX */ + regs->regs[6] = regs->regs[29] = sp; + regs->regs[7] = (unsigned long) ka->sa.sa_handler; + regs->regs[25] = regs->cp0_epc = (unsigned long) ka->sa_restorer; + + return; + +segv_and_exit: + force_sigsegv(signr, current); +} + +static void inline +setup_irix_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, + int signr, sigset_t *oldmask, siginfo_t *info) +{ + printk("Aiee: setup_tr_frame wants to be written"); + do_exit(SIGSEGV); +} + +static inline void handle_signal(unsigned long sig, siginfo_t *info, + struct k_sigaction *ka, sigset_t *oldset, struct pt_regs * regs) +{ + switch(regs->regs[0]) { + case ERESTARTNOHAND: + regs->regs[2] = EINTR; + break; + case ERESTARTSYS: + if(!(ka->sa.sa_flags & SA_RESTART)) { + regs->regs[2] = EINTR; + break; + } + /* fallthrough */ + case ERESTARTNOINTR: /* Userland will reload $v0. */ + regs->cp0_epc -= 8; + } + + regs->regs[0] = 0; /* Don't deal with this again. */ + + if (ka->sa.sa_flags & SA_SIGINFO) + setup_irix_rt_frame(ka, regs, sig, oldset, info); + else + setup_irix_frame(ka, regs, sig, oldset); + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } +} + +asmlinkage int do_irix_signal(sigset_t *oldset, struct pt_regs *regs) +{ + struct k_sigaction ka; + siginfo_t info; + int signr; + + /* + * We want the common case to go fast, which is why we may in certain + * cases get here from kernel mode. Just return without doing anything + * if so. + */ + if (!user_mode(regs)) + return 1; + + if (try_to_freeze(0)) + goto no_signal; + + if (!oldset) + oldset = ¤t->blocked; + + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + if (signr > 0) { + handle_signal(signr, &info, &ka, oldset, regs); + return 1; + } + +no_signal: + /* + * Who's code doesn't conform to the restartable syscall convention + * dies here!!! The li instruction, a single machine instruction, + * must directly be followed by the syscall instruction. + */ + if (regs->regs[0]) { + if (regs->regs[2] == ERESTARTNOHAND || + regs->regs[2] == ERESTARTSYS || + regs->regs[2] == ERESTARTNOINTR) { + regs->cp0_epc -= 8; + } + } + return 0; +} + +asmlinkage void +irix_sigreturn(struct pt_regs *regs) +{ + struct sigctx_irix5 *context, *magic; + unsigned long umask, mask; + u64 *fregs; + int sig, i, base = 0; + sigset_t blocked; + + /* Always make any pending restarted system calls return -EINTR */ + current_thread_info()->restart_block.fn = do_no_restart_syscall; + + if (regs->regs[2] == 1000) + base = 1; + + context = (struct sigctx_irix5 *) regs->regs[base + 4]; + magic = (struct sigctx_irix5 *) regs->regs[base + 5]; + sig = (int) regs->regs[base + 6]; +#ifdef DEBUG_SIG + printk("[%s:%d] IRIX sigreturn(scp[%p],ucp[%p],sig[%d])\n", + current->comm, current->pid, context, magic, sig); +#endif + if (!context) + context = magic; + if (!access_ok(VERIFY_READ, context, sizeof(struct sigctx_irix5))) + goto badframe; + +#ifdef DEBUG_SIG + dump_irix5_sigctx(context); +#endif + + __get_user(regs->cp0_epc, &context->pc); + umask = context->rmask; mask = 2; + for (i = 1; i < 32; i++, mask <<= 1) { + if(umask & mask) + __get_user(regs->regs[i], &context->regs[i]); + } + __get_user(regs->hi, &context->hi); + __get_user(regs->lo, &context->lo); + + if ((umask & 1) && context->usedfp) { + fregs = (u64 *) ¤t->thread.fpu; + for(i = 0; i < 32; i++) + fregs[i] = (u64) context->fpregs[i]; + __get_user(current->thread.fpu.hard.fcr31, &context->fpcsr); + } + + /* XXX do sigstack crapola here... XXX */ + + if (__copy_from_user(&blocked, &context->sigset, sizeof(blocked))) + goto badframe; + + sigdelsetmask(&blocked, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = blocked; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + /* + * Don't let your children do this ... + */ + if (current_thread_info()->flags & TIF_SYSCALL_TRACE) + do_syscall_trace(regs, 1); + __asm__ __volatile__( + "move\t$29,%0\n\t" + "j\tsyscall_exit" + :/* no outputs */ + :"r" (®s)); + /* Unreached */ + +badframe: + force_sig(SIGSEGV, current); +} + +struct sigact_irix5 { + int flags; + void (*handler)(int); + u32 sigset[4]; + int _unused0[2]; +}; + +#ifdef DEBUG_SIG +static inline void dump_sigact_irix5(struct sigact_irix5 *p) +{ + printk("<f[%d] hndlr[%08lx] msk[%08lx]>", p->flags, + (unsigned long) p->handler, + (unsigned long) p->sigset[0]); +} +#endif + +asmlinkage int +irix_sigaction(int sig, const struct sigaction *act, + struct sigaction *oact, void *trampoline) +{ + struct k_sigaction new_ka, old_ka; + int ret; + +#ifdef DEBUG_SIG + printk(" (%d,%s,%s,%08lx) ", sig, (!new ? "0" : "NEW"), + (!old ? "0" : "OLD"), trampoline); + if(new) { + dump_sigact_irix5(new); printk(" "); + } +#endif + if (act) { + sigset_t mask; + if (!access_ok(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_flags, &act->sa_flags)) + return -EFAULT; + + __copy_from_user(&mask, &act->sa_mask, sizeof(sigset_t)); + + /* + * Hmmm... methinks IRIX libc always passes a valid trampoline + * value for all invocations of sigaction. Will have to + * investigate. POSIX POSIX, die die die... + */ + new_ka.sa_restorer = trampoline; + } + +/* XXX Implement SIG_SETMASK32 for IRIX compatibility */ + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_flags, &oact->sa_flags)) + return -EFAULT; + __copy_to_user(&old_ka.sa.sa_mask, &oact->sa_mask, + sizeof(sigset_t)); + } + + return ret; +} + +asmlinkage int irix_sigpending(irix_sigset_t *set) +{ + return do_sigpending(set, sizeof(*set)); +} + +asmlinkage int irix_sigprocmask(int how, irix_sigset_t *new, irix_sigset_t *old) +{ + sigset_t oldbits, newbits; + + if (new) { + if (!access_ok(VERIFY_READ, new, sizeof(*new))) + return -EFAULT; + __copy_from_user(&newbits, new, sizeof(unsigned long)*4); + sigdelsetmask(&newbits, ~_BLOCKABLE); + + spin_lock_irq(¤t->sighand->siglock); + oldbits = current->blocked; + + switch(how) { + case 1: + sigorsets(&newbits, &oldbits, &newbits); + break; + + case 2: + sigandsets(&newbits, &oldbits, &newbits); + break; + + case 3: + break; + + case 256: + siginitset(&newbits, newbits.sig[0]); + break; + + default: + return -EINVAL; + } + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } + if(old) { + if (!access_ok(VERIFY_WRITE, old, sizeof(*old))) + return -EFAULT; + __copy_to_user(old, ¤t->blocked, sizeof(unsigned long)*4); + } + + return 0; +} + +asmlinkage int irix_sigsuspend(struct pt_regs *regs) +{ + sigset_t *uset, saveset, newset; + + uset = (sigset_t *) regs->regs[4]; + if (copy_from_user(&newset, uset, sizeof(sigset_t))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + regs->regs[2] = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_irix_signal(&saveset, regs)) + return -EINTR; + } +} + +/* hate hate hate... */ +struct irix5_siginfo { + int sig, code, error; + union { + char unused[128 - (3 * 4)]; /* Safety net. */ + struct { + int pid; + union { + int uid; + struct { + int utime, status, stime; + } child; + } procdata; + } procinfo; + + unsigned long fault_addr; + + struct { + int fd; + long band; + } fileinfo; + + unsigned long sigval; + } stuff; +}; + +static inline unsigned long timespectojiffies(struct timespec *value) +{ + unsigned long sec = (unsigned) value->tv_sec; + long nsec = value->tv_nsec; + + if (sec > (LONG_MAX / HZ)) + return LONG_MAX; + nsec += 1000000000L / HZ - 1; + nsec /= 1000000000L / HZ; + return HZ * sec + nsec; +} + +asmlinkage int irix_sigpoll_sys(unsigned long *set, struct irix5_siginfo *info, + struct timespec *tp) +{ + long expire = MAX_SCHEDULE_TIMEOUT; + sigset_t kset; + int i, sig, error, timeo = 0; + +#ifdef DEBUG_SIG + printk("[%s:%d] irix_sigpoll_sys(%p,%p,%p)\n", + current->comm, current->pid, set, info, tp); +#endif + + /* Must always specify the signal set. */ + if (!set) + return -EINVAL; + + if (!access_ok(VERIFY_READ, set, sizeof(kset))) { + error = -EFAULT; + goto out; + } + + __copy_from_user(&kset, set, sizeof(set)); + if (error) + goto out; + + if (info && clear_user(info, sizeof(*info))) { + error = -EFAULT; + goto out; + } + + if (tp) { + if (!access_ok(VERIFY_READ, tp, sizeof(*tp))) + return -EFAULT; + if (!tp->tv_sec && !tp->tv_nsec) { + error = -EINVAL; + goto out; + } + expire = timespectojiffies(tp)+(tp->tv_sec||tp->tv_nsec); + } + + while(1) { + long tmp = 0; + + current->state = TASK_INTERRUPTIBLE; + expire = schedule_timeout(expire); + + for (i=0; i<=4; i++) + tmp |= (current->pending.signal.sig[i] & kset.sig[i]); + + if (tmp) + break; + if (!expire) { + timeo = 1; + break; + } + if (signal_pending(current)) + return -EINTR; + } + if (timeo) + return -EAGAIN; + + for(sig = 1; i <= 65 /* IRIX_NSIG */; sig++) { + if (sigismember (&kset, sig)) + continue; + if (sigismember (¤t->pending.signal, sig)) { + /* XXX need more than this... */ + if (info) + info->sig = sig; + error = 0; + goto out; + } + } + + /* Should not get here, but do something sane if we do. */ + error = -EINTR; + +out: + return error; +} + +/* This is here because of irix5_siginfo definition. */ +#define IRIX_P_PID 0 +#define IRIX_P_PGID 2 +#define IRIX_P_ALL 7 + +extern int getrusage(struct task_struct *, int, struct rusage __user *); + +#define W_EXITED 1 +#define W_TRAPPED 2 +#define W_STOPPED 4 +#define W_CONT 8 +#define W_NOHANG 64 + +#define W_MASK (W_EXITED | W_TRAPPED | W_STOPPED | W_CONT | W_NOHANG) + +asmlinkage int irix_waitsys(int type, int pid, struct irix5_siginfo *info, + int options, struct rusage *ru) +{ + int flag, retval; + DECLARE_WAITQUEUE(wait, current); + struct task_struct *tsk; + struct task_struct *p; + struct list_head *_p; + + if (!info) { + retval = -EINVAL; + goto out; + } + if (!access_ok(VERIFY_WRITE, info, sizeof(*info))) { + retval = -EFAULT; + goto out; + } + if (ru) { + if (!access_ok(VERIFY_WRITE, ru, sizeof(*ru))) { + retval = -EFAULT; + goto out; + } + } + if (options & ~(W_MASK)) { + retval = -EINVAL; + goto out; + } + if (type != IRIX_P_PID && type != IRIX_P_PGID && type != IRIX_P_ALL) { + retval = -EINVAL; + goto out; + } + add_wait_queue(¤t->signal->wait_chldexit, &wait); +repeat: + flag = 0; + current->state = TASK_INTERRUPTIBLE; + read_lock(&tasklist_lock); + tsk = current; + list_for_each(_p,&tsk->children) { + p = list_entry(_p,struct task_struct,sibling); + if ((type == IRIX_P_PID) && p->pid != pid) + continue; + if ((type == IRIX_P_PGID) && process_group(p) != pid) + continue; + if ((p->exit_signal != SIGCHLD)) + continue; + flag = 1; + switch (p->state) { + case TASK_STOPPED: + if (!p->exit_code) + continue; + if (!(options & (W_TRAPPED|W_STOPPED)) && + !(p->ptrace & PT_PTRACED)) + continue; + read_unlock(&tasklist_lock); + + /* move to end of parent's list to avoid starvation */ + write_lock_irq(&tasklist_lock); + remove_parent(p); + add_parent(p, p->parent); + write_unlock_irq(&tasklist_lock); + retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; + if (!retval && ru) { + retval |= __put_user(SIGCHLD, &info->sig); + retval |= __put_user(0, &info->code); + retval |= __put_user(p->pid, &info->stuff.procinfo.pid); + retval |= __put_user((p->exit_code >> 8) & 0xff, + &info->stuff.procinfo.procdata.child.status); + retval |= __put_user(p->utime, &info->stuff.procinfo.procdata.child.utime); + retval |= __put_user(p->stime, &info->stuff.procinfo.procdata.child.stime); + } + if (!retval) { + p->exit_code = 0; + } + goto end_waitsys; + + case EXIT_ZOMBIE: + current->signal->cutime += p->utime + p->signal->cutime; + current->signal->cstime += p->stime + p->signal->cstime; + if (ru != NULL) + getrusage(p, RUSAGE_BOTH, ru); + __put_user(SIGCHLD, &info->sig); + __put_user(1, &info->code); /* CLD_EXITED */ + __put_user(p->pid, &info->stuff.procinfo.pid); + __put_user((p->exit_code >> 8) & 0xff, + &info->stuff.procinfo.procdata.child.status); + __put_user(p->utime, + &info->stuff.procinfo.procdata.child.utime); + __put_user(p->stime, + &info->stuff.procinfo.procdata.child.stime); + retval = 0; + if (p->real_parent != p->parent) { + write_lock_irq(&tasklist_lock); + remove_parent(p); + p->parent = p->real_parent; + add_parent(p, p->parent); + do_notify_parent(p, SIGCHLD); + write_unlock_irq(&tasklist_lock); + } else + release_task(p); + goto end_waitsys; + default: + continue; + } + tsk = next_thread(tsk); + } + read_unlock(&tasklist_lock); + if (flag) { + retval = 0; + if (options & W_NOHANG) + goto end_waitsys; + retval = -ERESTARTSYS; + if (signal_pending(current)) + goto end_waitsys; + current->state = TASK_INTERRUPTIBLE; + schedule(); + goto repeat; + } + retval = -ECHILD; +end_waitsys: + current->state = TASK_RUNNING; + remove_wait_queue(¤t->signal->wait_chldexit, &wait); + +out: + return retval; +} + +struct irix5_context { + u32 flags; + u32 link; + u32 sigmask[4]; + struct { u32 sp, size, flags; } stack; + int regs[36]; + u32 fpregs[32]; + u32 fpcsr; + u32 _unused0; + u32 _unused1[47]; + u32 weird_graphics_thing; +}; + +asmlinkage int irix_getcontext(struct pt_regs *regs) +{ + int i, base = 0; + struct irix5_context *ctx; + unsigned long flags; + + if (regs->regs[2] == 1000) + base = 1; + ctx = (struct irix5_context *) regs->regs[base + 4]; + +#ifdef DEBUG_SIG + printk("[%s:%d] irix_getcontext(%p)\n", + current->comm, current->pid, ctx); +#endif + + if (!access_ok(VERIFY_WRITE, ctx, sizeof(*ctx))) + return -EFAULT; + + __put_user(current->thread.irix_oldctx, &ctx->link); + + __copy_to_user(&ctx->sigmask, ¤t->blocked, sizeof(irix_sigset_t)); + + /* XXX Do sigstack stuff someday... */ + __put_user(0, &ctx->stack.sp); + __put_user(0, &ctx->stack.size); + __put_user(0, &ctx->stack.flags); + + __put_user(0, &ctx->weird_graphics_thing); + __put_user(0, &ctx->regs[0]); + for (i = 1; i < 32; i++) + __put_user(regs->regs[i], &ctx->regs[i]); + __put_user(regs->lo, &ctx->regs[32]); + __put_user(regs->hi, &ctx->regs[33]); + __put_user(regs->cp0_cause, &ctx->regs[34]); + __put_user(regs->cp0_epc, &ctx->regs[35]); + + flags = 0x0f; + if (!used_math()) { + flags &= ~(0x08); + } else { + /* XXX wheee... */ + printk("Wheee, no code for saving IRIX FPU context yet.\n"); + } + __put_user(flags, &ctx->flags); + + return 0; +} + +asmlinkage unsigned long irix_setcontext(struct pt_regs *regs) +{ + int error, base = 0; + struct irix5_context *ctx; + + if(regs->regs[2] == 1000) + base = 1; + ctx = (struct irix5_context *) regs->regs[base + 4]; + +#ifdef DEBUG_SIG + printk("[%s:%d] irix_setcontext(%p)\n", + current->comm, current->pid, ctx); +#endif + + if (!access_ok(VERIFY_READ, ctx, sizeof(*ctx))) { + error = -EFAULT; + goto out; + } + + if (ctx->flags & 0x02) { + /* XXX sigstack garbage, todo... */ + printk("Wheee, cannot do sigstack stuff in setcontext\n"); + } + + if (ctx->flags & 0x04) { + int i; + + /* XXX extra control block stuff... todo... */ + for(i = 1; i < 32; i++) + regs->regs[i] = ctx->regs[i]; + regs->lo = ctx->regs[32]; + regs->hi = ctx->regs[33]; + regs->cp0_epc = ctx->regs[35]; + } + + if (ctx->flags & 0x08) { + /* XXX fpu context, blah... */ + printk("Wheee, cannot restore FPU context yet...\n"); + } + current->thread.irix_oldctx = ctx->link; + error = regs->regs[2]; + +out: + return error; +} + +struct irix_sigstack { unsigned long sp; int status; }; + +asmlinkage int irix_sigstack(struct irix_sigstack *new, struct irix_sigstack *old) +{ + int error = -EFAULT; + +#ifdef DEBUG_SIG + printk("[%s:%d] irix_sigstack(%p,%p)\n", + current->comm, current->pid, new, old); +#endif + if(new) { + if (!access_ok(VERIFY_READ, new, sizeof(*new))) + goto out; + } + + if(old) { + if (!access_ok(VERIFY_WRITE, old, sizeof(*old))) + goto out; + } + error = 0; + +out: + return error; +} + +struct irix_sigaltstack { unsigned long sp; int size; int status; }; + +asmlinkage int irix_sigaltstack(struct irix_sigaltstack *new, + struct irix_sigaltstack *old) +{ + int error = -EFAULT; + +#ifdef DEBUG_SIG + printk("[%s:%d] irix_sigaltstack(%p,%p)\n", + current->comm, current->pid, new, old); +#endif + if (new) { + if (!access_ok(VERIFY_READ, new, sizeof(*new))) + goto out; + } + + if (old) { + if (!access_ok(VERIFY_WRITE, old, sizeof(*old))) + goto out; + } + error = 0; + +out: + error = 0; + + return error; +} + +struct irix_procset { + int cmd, ltype, lid, rtype, rid; +}; + +asmlinkage int irix_sigsendset(struct irix_procset *pset, int sig) +{ + if (!access_ok(VERIFY_READ, pset, sizeof(*pset))) + return -EFAULT; + +#ifdef DEBUG_SIG + printk("[%s:%d] irix_sigsendset([%d,%d,%d,%d,%d],%d)\n", + current->comm, current->pid, + pset->cmd, pset->ltype, pset->lid, pset->rtype, pset->rid, + sig); +#endif + return -EINVAL; +} diff --git a/arch/mips/kernel/irq-msc01.c b/arch/mips/kernel/irq-msc01.c new file mode 100644 index 000000000000..43c00ac0b88d --- /dev/null +++ b/arch/mips/kernel/irq-msc01.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2004 MIPS Inc + * Author: chris@mips.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <asm/ptrace.h> +#include <linux/sched.h> +#include <linux/kernel_stat.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/msc01_ic.h> + +static unsigned long _icctrl_msc; +#define MSC01_IC_REG_BASE _icctrl_msc + +#define MSCIC_WRITE(reg, data) do { *(volatile u32 *)(reg) = data; } while (0) +#define MSCIC_READ(reg, data) do { data = *(volatile u32 *)(reg); } while (0) + +static unsigned int irq_base; + +/* mask off an interrupt */ +static inline void mask_msc_irq(unsigned int irq) +{ + if (irq < (irq_base + 32)) + MSCIC_WRITE(MSC01_IC_DISL, 1<<(irq - irq_base)); + else + MSCIC_WRITE(MSC01_IC_DISH, 1<<(irq - irq_base - 32)); +} + +/* unmask an interrupt */ +static inline void unmask_msc_irq(unsigned int irq) +{ + if (irq < (irq_base + 32)) + MSCIC_WRITE(MSC01_IC_ENAL, 1<<(irq - irq_base)); + else + MSCIC_WRITE(MSC01_IC_ENAH, 1<<(irq - irq_base - 32)); +} + +/* + * Enables the IRQ on SOC-it + */ +static void enable_msc_irq(unsigned int irq) +{ + unmask_msc_irq(irq); +} + +/* + * Initialize the IRQ on SOC-it + */ +static unsigned int startup_msc_irq(unsigned int irq) +{ + unmask_msc_irq(irq); + return 0; +} + +/* + * Disables the IRQ on SOC-it + */ +static void disable_msc_irq(unsigned int irq) +{ + mask_msc_irq(irq); +} + +/* + * Masks and ACKs an IRQ + */ +static void level_mask_and_ack_msc_irq(unsigned int irq) +{ + mask_msc_irq(irq); + if (!cpu_has_ei) + MSCIC_WRITE(MSC01_IC_EOI, 0); +} + +/* + * Masks and ACKs an IRQ + */ +static void edge_mask_and_ack_msc_irq(unsigned int irq) +{ + mask_msc_irq(irq); + if (!cpu_has_ei) + MSCIC_WRITE(MSC01_IC_EOI, 0); + else { + u32 r; + MSCIC_READ(MSC01_IC_SUP+irq*8, r); + MSCIC_WRITE(MSC01_IC_SUP+irq*8, r | ~MSC01_IC_SUP_EDGE_BIT); + MSCIC_WRITE(MSC01_IC_SUP+irq*8, r); + } +} + +/* + * End IRQ processing + */ +static void end_msc_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + unmask_msc_irq(irq); +} + +/* + * Interrupt handler for interrupts coming from SOC-it. + */ +void ll_msc_irq(struct pt_regs *regs) +{ + unsigned int irq; + + /* read the interrupt vector register */ + MSCIC_READ(MSC01_IC_VEC, irq); + if (irq < 64) + do_IRQ(irq + irq_base, regs); + else { + /* Ignore spurious interrupt */ + } +} + +void +msc_bind_eic_interrupt (unsigned int irq, unsigned int set) +{ + MSCIC_WRITE(MSC01_IC_RAMW, + (irq<<MSC01_IC_RAMW_ADDR_SHF) | (set<<MSC01_IC_RAMW_DATA_SHF)); +} + +#define shutdown_msc_irq disable_msc_irq + +struct hw_interrupt_type msc_levelirq_type = { + "SOC-it-Level", + startup_msc_irq, + shutdown_msc_irq, + enable_msc_irq, + disable_msc_irq, + level_mask_and_ack_msc_irq, + end_msc_irq, + NULL +}; + +struct hw_interrupt_type msc_edgeirq_type = { + "SOC-it-Edge", + startup_msc_irq, + shutdown_msc_irq, + enable_msc_irq, + disable_msc_irq, + edge_mask_and_ack_msc_irq, + end_msc_irq, + NULL +}; + + +void __init init_msc_irqs(unsigned int base, msc_irqmap_t *imp, int nirq) +{ + extern void (*board_bind_eic_interrupt)(unsigned int irq, unsigned int regset); + + _icctrl_msc = (unsigned long) ioremap (MIPS_MSC01_IC_REG_BASE, 0x40000); + + /* Reset interrupt controller - initialises all registers to 0 */ + MSCIC_WRITE(MSC01_IC_RST, MSC01_IC_RST_RST_BIT); + + board_bind_eic_interrupt = &msc_bind_eic_interrupt; + + for (; nirq >= 0; nirq--, imp++) { + int n = imp->im_irq; + + switch (imp->im_type) { + case MSC01_IRQ_EDGE: + irq_desc[base+n].handler = &msc_edgeirq_type; + if (cpu_has_ei) + MSCIC_WRITE(MSC01_IC_SUP+n*8, MSC01_IC_SUP_EDGE_BIT); + else + MSCIC_WRITE(MSC01_IC_SUP+n*8, MSC01_IC_SUP_EDGE_BIT | imp->im_lvl); + break; + case MSC01_IRQ_LEVEL: + irq_desc[base+n].handler = &msc_levelirq_type; + if (cpu_has_ei) + MSCIC_WRITE(MSC01_IC_SUP+n*8, 0); + else + MSCIC_WRITE(MSC01_IC_SUP+n*8, imp->im_lvl); + } + } + + irq_base = base; + + MSCIC_WRITE(MSC01_IC_GENA, MSC01_IC_GENA_GENA_BIT); /* Enable interrupt generation */ + +} diff --git a/arch/mips/kernel/irq-mv6434x.c b/arch/mips/kernel/irq-mv6434x.c new file mode 100644 index 000000000000..088bbbc869e6 --- /dev/null +++ b/arch/mips/kernel/irq-mv6434x.c @@ -0,0 +1,161 @@ +/* + * Copyright 2002 Momentum Computer + * Author: mdharm@momenco.com + * Copyright (C) 2004 Ralf Baechle <ralf@linux-mips.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <asm/ptrace.h> +#include <linux/sched.h> +#include <linux/kernel_stat.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <linux/mv643xx.h> + +static unsigned int irq_base; + +static inline int ls1bit32(unsigned int x) +{ + int b = 31, s; + + s = 16; if (x << 16 == 0) s = 0; b -= s; x <<= s; + s = 8; if (x << 8 == 0) s = 0; b -= s; x <<= s; + s = 4; if (x << 4 == 0) s = 0; b -= s; x <<= s; + s = 2; if (x << 2 == 0) s = 0; b -= s; x <<= s; + s = 1; if (x << 1 == 0) s = 0; b -= s; + + return b; +} + +/* mask off an interrupt -- 1 is enable, 0 is disable */ +static inline void mask_mv64340_irq(unsigned int irq) +{ + uint32_t value; + + if (irq < (irq_base + 32)) { + value = MV_READ(MV64340_INTERRUPT0_MASK_0_LOW); + value &= ~(1 << (irq - irq_base)); + MV_WRITE(MV64340_INTERRUPT0_MASK_0_LOW, value); + } else { + value = MV_READ(MV64340_INTERRUPT0_MASK_0_HIGH); + value &= ~(1 << (irq - irq_base - 32)); + MV_WRITE(MV64340_INTERRUPT0_MASK_0_HIGH, value); + } +} + +/* unmask an interrupt -- 1 is enable, 0 is disable */ +static inline void unmask_mv64340_irq(unsigned int irq) +{ + uint32_t value; + + if (irq < (irq_base + 32)) { + value = MV_READ(MV64340_INTERRUPT0_MASK_0_LOW); + value |= 1 << (irq - irq_base); + MV_WRITE(MV64340_INTERRUPT0_MASK_0_LOW, value); + } else { + value = MV_READ(MV64340_INTERRUPT0_MASK_0_HIGH); + value |= 1 << (irq - irq_base - 32); + MV_WRITE(MV64340_INTERRUPT0_MASK_0_HIGH, value); + } +} + +/* + * Enables the IRQ on Marvell Chip + */ +static void enable_mv64340_irq(unsigned int irq) +{ + unmask_mv64340_irq(irq); +} + +/* + * Initialize the IRQ on Marvell Chip + */ +static unsigned int startup_mv64340_irq(unsigned int irq) +{ + unmask_mv64340_irq(irq); + return 0; +} + +/* + * Disables the IRQ on Marvell Chip + */ +static void disable_mv64340_irq(unsigned int irq) +{ + mask_mv64340_irq(irq); +} + +/* + * Masks and ACKs an IRQ + */ +static void mask_and_ack_mv64340_irq(unsigned int irq) +{ + mask_mv64340_irq(irq); +} + +/* + * End IRQ processing + */ +static void end_mv64340_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + unmask_mv64340_irq(irq); +} + +/* + * Interrupt handler for interrupts coming from the Marvell chip. + * It could be built in ethernet ports etc... + */ +void ll_mv64340_irq(struct pt_regs *regs) +{ + unsigned int irq_src_low, irq_src_high; + unsigned int irq_mask_low, irq_mask_high; + + /* read the interrupt status registers */ + irq_mask_low = MV_READ(MV64340_INTERRUPT0_MASK_0_LOW); + irq_mask_high = MV_READ(MV64340_INTERRUPT0_MASK_0_HIGH); + irq_src_low = MV_READ(MV64340_MAIN_INTERRUPT_CAUSE_LOW); + irq_src_high = MV_READ(MV64340_MAIN_INTERRUPT_CAUSE_HIGH); + + /* mask for just the interrupts we want */ + irq_src_low &= irq_mask_low; + irq_src_high &= irq_mask_high; + + if (irq_src_low) + do_IRQ(ls1bit32(irq_src_low) + irq_base, regs); + else + do_IRQ(ls1bit32(irq_src_high) + irq_base + 32, regs); +} + +#define shutdown_mv64340_irq disable_mv64340_irq + +struct hw_interrupt_type mv64340_irq_type = { + "MV-64340", + startup_mv64340_irq, + shutdown_mv64340_irq, + enable_mv64340_irq, + disable_mv64340_irq, + mask_and_ack_mv64340_irq, + end_mv64340_irq, + NULL +}; + +void __init mv64340_irq_init(unsigned int base) +{ + int i; + + /* Reset irq handlers pointers to NULL */ + for (i = base; i < base + 64; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = 0; + irq_desc[i].depth = 2; + irq_desc[i].handler = &mv64340_irq_type; + } + + irq_base = base; +} diff --git a/arch/mips/kernel/irq-rm7000.c b/arch/mips/kernel/irq-rm7000.c new file mode 100644 index 000000000000..f5d779fd0355 --- /dev/null +++ b/arch/mips/kernel/irq-rm7000.c @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2003 Ralf Baechle + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Handler for RM7000 extended interrupts. These are a non-standard + * feature so we handle them separately from standard interrupts. + */ +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> + +#include <asm/irq_cpu.h> +#include <asm/mipsregs.h> +#include <asm/system.h> + +static int irq_base; + +static inline void unmask_rm7k_irq(unsigned int irq) +{ + set_c0_intcontrol(0x100 << (irq - irq_base)); +} + +static inline void mask_rm7k_irq(unsigned int irq) +{ + clear_c0_intcontrol(0x100 << (irq - irq_base)); +} + +static inline void rm7k_cpu_irq_enable(unsigned int irq) +{ + unsigned long flags; + + local_irq_save(flags); + unmask_rm7k_irq(irq); + local_irq_restore(flags); +} + +static void rm7k_cpu_irq_disable(unsigned int irq) +{ + unsigned long flags; + + local_irq_save(flags); + mask_rm7k_irq(irq); + local_irq_restore(flags); +} + +static unsigned int rm7k_cpu_irq_startup(unsigned int irq) +{ + rm7k_cpu_irq_enable(irq); + + return 0; +} + +#define rm7k_cpu_irq_shutdown rm7k_cpu_irq_disable + +/* + * While we ack the interrupt interrupts are disabled and thus we don't need + * to deal with concurrency issues. Same for rm7k_cpu_irq_end. + */ +static void rm7k_cpu_irq_ack(unsigned int irq) +{ + mask_rm7k_irq(irq); +} + +static void rm7k_cpu_irq_end(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) + unmask_rm7k_irq(irq); +} + +static hw_irq_controller rm7k_irq_controller = { + "RM7000", + rm7k_cpu_irq_startup, + rm7k_cpu_irq_shutdown, + rm7k_cpu_irq_enable, + rm7k_cpu_irq_disable, + rm7k_cpu_irq_ack, + rm7k_cpu_irq_end, +}; + +void __init rm7k_cpu_irq_init(int base) +{ + int i; + + clear_c0_intcontrol(0x00000f00); /* Mask all */ + + for (i = base; i < base + 4; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = NULL; + irq_desc[i].depth = 1; + irq_desc[i].handler = &rm7k_irq_controller; + } + + irq_base = base; +} diff --git a/arch/mips/kernel/irq-rm9000.c b/arch/mips/kernel/irq-rm9000.c new file mode 100644 index 000000000000..bdd130296256 --- /dev/null +++ b/arch/mips/kernel/irq-rm9000.c @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2003 Ralf Baechle + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Handler for RM9000 extended interrupts. These are a non-standard + * feature so we handle them separately from standard interrupts. + */ +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> + +#include <asm/irq_cpu.h> +#include <asm/mipsregs.h> +#include <asm/system.h> + +static int irq_base; + +static inline void unmask_rm9k_irq(unsigned int irq) +{ + set_c0_intcontrol(0x1000 << (irq - irq_base)); +} + +static inline void mask_rm9k_irq(unsigned int irq) +{ + clear_c0_intcontrol(0x1000 << (irq - irq_base)); +} + +static inline void rm9k_cpu_irq_enable(unsigned int irq) +{ + unsigned long flags; + + local_irq_save(flags); + unmask_rm9k_irq(irq); + local_irq_restore(flags); +} + +static void rm9k_cpu_irq_disable(unsigned int irq) +{ + unsigned long flags; + + local_irq_save(flags); + mask_rm9k_irq(irq); + local_irq_restore(flags); +} + +static unsigned int rm9k_cpu_irq_startup(unsigned int irq) +{ + rm9k_cpu_irq_enable(irq); + + return 0; +} + +#define rm9k_cpu_irq_shutdown rm9k_cpu_irq_disable + +/* + * Performance counter interrupts are global on all processors. + */ +static void local_rm9k_perfcounter_irq_startup(void *args) +{ + unsigned int irq = (unsigned int) args; + + rm9k_cpu_irq_enable(irq); +} + +static unsigned int rm9k_perfcounter_irq_startup(unsigned int irq) +{ + on_each_cpu(local_rm9k_perfcounter_irq_startup, (void *) irq, 0, 1); + + return 0; +} + +static void local_rm9k_perfcounter_irq_shutdown(void *args) +{ + unsigned int irq = (unsigned int) args; + unsigned long flags; + + local_irq_save(flags); + mask_rm9k_irq(irq); + local_irq_restore(flags); +} + +static void rm9k_perfcounter_irq_shutdown(unsigned int irq) +{ + on_each_cpu(local_rm9k_perfcounter_irq_shutdown, (void *) irq, 0, 1); +} + + +/* + * While we ack the interrupt interrupts are disabled and thus we don't need + * to deal with concurrency issues. Same for rm9k_cpu_irq_end. + */ +static void rm9k_cpu_irq_ack(unsigned int irq) +{ + mask_rm9k_irq(irq); +} + +static void rm9k_cpu_irq_end(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) + unmask_rm9k_irq(irq); +} + +static hw_irq_controller rm9k_irq_controller = { + "RM9000", + rm9k_cpu_irq_startup, + rm9k_cpu_irq_shutdown, + rm9k_cpu_irq_enable, + rm9k_cpu_irq_disable, + rm9k_cpu_irq_ack, + rm9k_cpu_irq_end, +}; + +static hw_irq_controller rm9k_perfcounter_irq = { + "RM9000", + rm9k_perfcounter_irq_startup, + rm9k_perfcounter_irq_shutdown, + rm9k_cpu_irq_enable, + rm9k_cpu_irq_disable, + rm9k_cpu_irq_ack, + rm9k_cpu_irq_end, +}; + +unsigned int rm9000_perfcount_irq; + +EXPORT_SYMBOL(rm9000_perfcount_irq); + +void __init rm9k_cpu_irq_init(int base) +{ + int i; + + clear_c0_intcontrol(0x0000f000); /* Mask all */ + + for (i = base; i < base + 4; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = NULL; + irq_desc[i].depth = 1; + irq_desc[i].handler = &rm9k_irq_controller; + } + + rm9000_perfcount_irq = base + 1; + irq_desc[rm9000_perfcount_irq].handler = &rm9k_perfcounter_irq; + + irq_base = base; +} diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c new file mode 100644 index 000000000000..441157a1f994 --- /dev/null +++ b/arch/mips/kernel/irq.c @@ -0,0 +1,140 @@ +/* + * 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. + * + * Code to handle x86 style IRQs plus some generic interrupt stuff. + * + * Copyright (C) 1992 Linus Torvalds + * Copyright (C) 1994 - 2000 Ralf Baechle + */ +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel_stat.h> +#include <linux/module.h> +#include <linux/proc_fs.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/random.h> +#include <linux/sched.h> +#include <linux/seq_file.h> +#include <linux/kallsyms.h> + +#include <asm/atomic.h> +#include <asm/system.h> +#include <asm/uaccess.h> + +/* + * 'what should we do if we get a hw irq event on an illegal vector'. + * each architecture has to answer this themselves. + */ +void ack_bad_irq(unsigned int irq) +{ + printk("unexpected IRQ # %d\n", irq); +} + +atomic_t irq_err_count; + +#undef do_IRQ + +/* + * do_IRQ handles all normal device IRQ's (the special + * SMP cross-CPU interrupts have their own specific + * handlers). + */ +asmlinkage unsigned int do_IRQ(unsigned int irq, struct pt_regs *regs) +{ + irq_enter(); + + __do_IRQ(irq, regs); + + irq_exit(); + + return 1; +} + +/* + * Generic, controller-independent functions: + */ + +int show_interrupts(struct seq_file *p, void *v) +{ + int i = *(loff_t *) v, j; + struct irqaction * action; + unsigned long flags; + + if (i == 0) { + seq_printf(p, " "); + for (j=0; j<NR_CPUS; j++) + if (cpu_online(j)) + seq_printf(p, "CPU%d ",j); + seq_putc(p, '\n'); + } + + if (i < NR_IRQS) { + spin_lock_irqsave(&irq_desc[i].lock, flags); + action = irq_desc[i].action; + if (!action) + goto skip; + seq_printf(p, "%3d: ",i); +#ifndef CONFIG_SMP + seq_printf(p, "%10u ", kstat_irqs(i)); +#else + for (j = 0; j < NR_CPUS; j++) + if (cpu_online(j)) + seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); +#endif + seq_printf(p, " %14s", irq_desc[i].handler->typename); + seq_printf(p, " %s", action->name); + + for (action=action->next; action; action = action->next) + seq_printf(p, ", %s", action->name); + + seq_putc(p, '\n'); +skip: + spin_unlock_irqrestore(&irq_desc[i].lock, flags); + } else if (i == NR_IRQS) { + seq_putc(p, '\n'); + seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count)); + } + return 0; +} + +#ifdef CONFIG_KGDB +extern void breakpoint(void); +extern void set_debug_traps(void); + +static int kgdb_flag = 1; +static int __init nokgdb(char *str) +{ + kgdb_flag = 0; + return 1; +} +__setup("nokgdb", nokgdb); +#endif + +void __init init_IRQ(void) +{ + int i; + + for (i = 0; i < NR_IRQS; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = NULL; + irq_desc[i].depth = 1; + irq_desc[i].handler = &no_irq_type; + spin_lock_init(&irq_desc[i].lock); + } + + arch_init_irq(); + +#ifdef CONFIG_KGDB + if (kgdb_flag) { + printk("Wait for gdb client connection ...\n"); + set_debug_traps(); + breakpoint(); + } +#endif +} diff --git a/arch/mips/kernel/irq_cpu.c b/arch/mips/kernel/irq_cpu.c new file mode 100644 index 000000000000..2b936cf1ef70 --- /dev/null +++ b/arch/mips/kernel/irq_cpu.c @@ -0,0 +1,118 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * Copyright (C) 2001 Ralf Baechle + * + * This file define the irq handler for MIPS CPU interrupts. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* + * Almost all MIPS CPUs define 8 interrupt sources. They are typically + * level triggered (i.e., cannot be cleared from CPU; must be cleared from + * device). The first two are software interrupts which we don't really + * use or support. The last one is usually the CPU timer interrupt if + * counter register is present or, for CPUs with an external FPU, by + * convention it's the FPU exception interrupt. + * + * Don't even think about using this on SMP. You have been warned. + * + * This file exports one global function: + * void mips_cpu_irq_init(int irq_base); + */ +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> + +#include <asm/irq_cpu.h> +#include <asm/mipsregs.h> +#include <asm/system.h> + +static int mips_cpu_irq_base; + +static inline void unmask_mips_irq(unsigned int irq) +{ + clear_c0_cause(0x100 << (irq - mips_cpu_irq_base)); + set_c0_status(0x100 << (irq - mips_cpu_irq_base)); +} + +static inline void mask_mips_irq(unsigned int irq) +{ + clear_c0_status(0x100 << (irq - mips_cpu_irq_base)); +} + +static inline void mips_cpu_irq_enable(unsigned int irq) +{ + unsigned long flags; + + local_irq_save(flags); + unmask_mips_irq(irq); + local_irq_restore(flags); +} + +static void mips_cpu_irq_disable(unsigned int irq) +{ + unsigned long flags; + + local_irq_save(flags); + mask_mips_irq(irq); + local_irq_restore(flags); +} + +static unsigned int mips_cpu_irq_startup(unsigned int irq) +{ + mips_cpu_irq_enable(irq); + + return 0; +} + +#define mips_cpu_irq_shutdown mips_cpu_irq_disable + +/* + * While we ack the interrupt interrupts are disabled and thus we don't need + * to deal with concurrency issues. Same for mips_cpu_irq_end. + */ +static void mips_cpu_irq_ack(unsigned int irq) +{ + /* Only necessary for soft interrupts */ + clear_c0_cause(0x100 << (irq - mips_cpu_irq_base)); + + mask_mips_irq(irq); +} + +static void mips_cpu_irq_end(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) + unmask_mips_irq(irq); +} + +static hw_irq_controller mips_cpu_irq_controller = { + "MIPS", + mips_cpu_irq_startup, + mips_cpu_irq_shutdown, + mips_cpu_irq_enable, + mips_cpu_irq_disable, + mips_cpu_irq_ack, + mips_cpu_irq_end, + NULL /* no affinity stuff for UP */ +}; + + +void __init mips_cpu_irq_init(int irq_base) +{ + int i; + + for (i = irq_base; i < irq_base + 8; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = NULL; + irq_desc[i].depth = 1; + irq_desc[i].handler = &mips_cpu_irq_controller; + } + + mips_cpu_irq_base = irq_base; +} diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c new file mode 100644 index 000000000000..993abc868e54 --- /dev/null +++ b/arch/mips/kernel/linux32.c @@ -0,0 +1,1469 @@ +/* + * Conversion between 32-bit and 64-bit native system calls. + * + * Copyright (C) 2000 Silicon Graphics, Inc. + * Written by Ulf Carlsson (ulfc@engr.sgi.com) + * sys32_execve from ia64/ia32 code, Feb 2000, Kanoj Sarcar (kanoj@sgi.com) + */ +#include <linux/config.h> +#include <linux/compiler.h> +#include <linux/mm.h> +#include <linux/errno.h> +#include <linux/file.h> +#include <linux/smp_lock.h> +#include <linux/highuid.h> +#include <linux/dirent.h> +#include <linux/resource.h> +#include <linux/highmem.h> +#include <linux/time.h> +#include <linux/times.h> +#include <linux/poll.h> +#include <linux/slab.h> +#include <linux/skbuff.h> +#include <linux/filter.h> +#include <linux/shm.h> +#include <linux/sem.h> +#include <linux/msg.h> +#include <linux/icmpv6.h> +#include <linux/syscalls.h> +#include <linux/sysctl.h> +#include <linux/utime.h> +#include <linux/utsname.h> +#include <linux/personality.h> +#include <linux/timex.h> +#include <linux/dnotify.h> +#include <linux/module.h> +#include <linux/binfmts.h> +#include <linux/security.h> +#include <linux/compat.h> +#include <linux/vfs.h> + +#include <net/sock.h> +#include <net/scm.h> + +#include <asm/ipc.h> +#include <asm/sim.h> +#include <asm/uaccess.h> +#include <asm/mmu_context.h> +#include <asm/mman.h> + +/* Use this to get at 32-bit user passed pointers. */ +/* A() macro should be used for places where you e.g. + have some internal variable u32 and just want to get + rid of a compiler warning. AA() has to be used in + places where you want to convert a function argument + to 32bit pointer or when you e.g. access pt_regs + structure and want to consider 32bit registers only. + */ +#define A(__x) ((unsigned long)(__x)) +#define AA(__x) ((unsigned long)((int)__x)) + +#ifdef __MIPSEB__ +#define merge_64(r1,r2) ((((r1) & 0xffffffffUL) << 32) + ((r2) & 0xffffffffUL)) +#endif +#ifdef __MIPSEL__ +#define merge_64(r1,r2) ((((r2) & 0xffffffffUL) << 32) + ((r1) & 0xffffffffUL)) +#endif + +/* + * Revalidate the inode. This is required for proper NFS attribute caching. + */ + +int cp_compat_stat(struct kstat *stat, struct compat_stat *statbuf) +{ + struct compat_stat tmp; + + if (!new_valid_dev(stat->dev) || !new_valid_dev(stat->rdev)) + return -EOVERFLOW; + + memset(&tmp, 0, sizeof(tmp)); + tmp.st_dev = new_encode_dev(stat->dev); + tmp.st_ino = stat->ino; + tmp.st_mode = stat->mode; + tmp.st_nlink = stat->nlink; + SET_UID(tmp.st_uid, stat->uid); + SET_GID(tmp.st_gid, stat->gid); + tmp.st_rdev = new_encode_dev(stat->rdev); + tmp.st_size = stat->size; + tmp.st_atime = stat->atime.tv_sec; + tmp.st_mtime = stat->mtime.tv_sec; + tmp.st_ctime = stat->ctime.tv_sec; +#ifdef STAT_HAVE_NSEC + tmp.st_atime_nsec = stat->atime.tv_nsec; + tmp.st_mtime_nsec = stat->mtime.tv_nsec; + tmp.st_ctime_nsec = stat->ctime.tv_nsec; +#endif + tmp.st_blocks = stat->blocks; + tmp.st_blksize = stat->blksize; + return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; +} + +asmlinkage unsigned long +sys32_mmap2(unsigned long addr, unsigned long len, unsigned long prot, + unsigned long flags, unsigned long fd, unsigned long pgoff) +{ + struct file * file = NULL; + unsigned long error; + + error = -EINVAL; + if (!(flags & MAP_ANONYMOUS)) { + error = -EBADF; + file = fget(fd); + if (!file) + goto out; + } + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + + down_write(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up_write(¤t->mm->mmap_sem); + if (file) + fput(file); + +out: + return error; +} + + +asmlinkage int sys_truncate64(const char *path, unsigned int high, + unsigned int low) +{ + if ((int)high < 0) + return -EINVAL; + return sys_truncate(path, ((long) high << 32) | low); +} + +asmlinkage int sys_ftruncate64(unsigned int fd, unsigned int high, + unsigned int low) +{ + if ((int)high < 0) + return -EINVAL; + return sys_ftruncate(fd, ((long) high << 32) | low); +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage int sys32_execve(nabi_no_regargs struct pt_regs regs) +{ + int error; + char * filename; + + filename = getname(compat_ptr(regs.regs[4])); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + error = compat_do_execve(filename, compat_ptr(regs.regs[5]), + compat_ptr(regs.regs[6]), ®s); + putname(filename); + +out: + return error; +} + +struct dirent32 { + unsigned int d_ino; + unsigned int d_off; + unsigned short d_reclen; + char d_name[NAME_MAX + 1]; +}; + +static void +xlate_dirent(void *dirent64, void *dirent32, long n) +{ + long off; + struct dirent *dirp; + struct dirent32 *dirp32; + + off = 0; + while (off < n) { + dirp = (struct dirent *)(dirent64 + off); + dirp32 = (struct dirent32 *)(dirent32 + off); + off += dirp->d_reclen; + dirp32->d_ino = dirp->d_ino; + dirp32->d_off = (unsigned int)dirp->d_off; + dirp32->d_reclen = dirp->d_reclen; + strncpy(dirp32->d_name, dirp->d_name, dirp->d_reclen - ((3 * 4) + 2)); + } + return; +} + +asmlinkage long +sys32_getdents(unsigned int fd, void * dirent32, unsigned int count) +{ + long n; + void *dirent64; + + dirent64 = (void *)((unsigned long)(dirent32 + (sizeof(long) - 1)) & ~(sizeof(long) - 1)); + if ((n = sys_getdents(fd, dirent64, count - (dirent64 - dirent32))) < 0) + return(n); + xlate_dirent(dirent64, dirent32, n); + return(n); +} + +asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count); + +asmlinkage int +sys32_readdir(unsigned int fd, void * dirent32, unsigned int count) +{ + int n; + struct dirent dirent64; + + if ((n = old_readdir(fd, &dirent64, count)) < 0) + return(n); + xlate_dirent(&dirent64, dirent32, dirent64.d_reclen); + return(n); +} + +struct rusage32 { + struct compat_timeval ru_utime; + struct compat_timeval ru_stime; + int ru_maxrss; + int ru_ixrss; + int ru_idrss; + int ru_isrss; + int ru_minflt; + int ru_majflt; + int ru_nswap; + int ru_inblock; + int ru_oublock; + int ru_msgsnd; + int ru_msgrcv; + int ru_nsignals; + int ru_nvcsw; + int ru_nivcsw; +}; + +static int +put_rusage (struct rusage32 *ru, struct rusage *r) +{ + int err; + + if (!access_ok(VERIFY_WRITE, ru, sizeof *ru)) + return -EFAULT; + + err = __put_user (r->ru_utime.tv_sec, &ru->ru_utime.tv_sec); + err |= __put_user (r->ru_utime.tv_usec, &ru->ru_utime.tv_usec); + err |= __put_user (r->ru_stime.tv_sec, &ru->ru_stime.tv_sec); + err |= __put_user (r->ru_stime.tv_usec, &ru->ru_stime.tv_usec); + err |= __put_user (r->ru_maxrss, &ru->ru_maxrss); + err |= __put_user (r->ru_ixrss, &ru->ru_ixrss); + err |= __put_user (r->ru_idrss, &ru->ru_idrss); + err |= __put_user (r->ru_isrss, &ru->ru_isrss); + err |= __put_user (r->ru_minflt, &ru->ru_minflt); + err |= __put_user (r->ru_majflt, &ru->ru_majflt); + err |= __put_user (r->ru_nswap, &ru->ru_nswap); + err |= __put_user (r->ru_inblock, &ru->ru_inblock); + err |= __put_user (r->ru_oublock, &ru->ru_oublock); + err |= __put_user (r->ru_msgsnd, &ru->ru_msgsnd); + err |= __put_user (r->ru_msgrcv, &ru->ru_msgrcv); + err |= __put_user (r->ru_nsignals, &ru->ru_nsignals); + err |= __put_user (r->ru_nvcsw, &ru->ru_nvcsw); + err |= __put_user (r->ru_nivcsw, &ru->ru_nivcsw); + + return err; +} + +asmlinkage int +sys32_wait4(compat_pid_t pid, unsigned int * stat_addr, int options, + struct rusage32 * ru) +{ + if (!ru) + return sys_wait4(pid, stat_addr, options, NULL); + else { + struct rusage r; + int ret; + unsigned int status; + mm_segment_t old_fs = get_fs(); + + set_fs(KERNEL_DS); + ret = sys_wait4(pid, stat_addr ? &status : NULL, options, &r); + set_fs(old_fs); + if (put_rusage (ru, &r)) return -EFAULT; + if (stat_addr && put_user (status, stat_addr)) + return -EFAULT; + return ret; + } +} + +asmlinkage int +sys32_waitpid(compat_pid_t pid, unsigned int *stat_addr, int options) +{ + return sys32_wait4(pid, stat_addr, options, NULL); +} + +struct sysinfo32 { + s32 uptime; + u32 loads[3]; + u32 totalram; + u32 freeram; + u32 sharedram; + u32 bufferram; + u32 totalswap; + u32 freeswap; + u16 procs; + u32 totalhigh; + u32 freehigh; + u32 mem_unit; + char _f[8]; +}; + +asmlinkage int sys32_sysinfo(struct sysinfo32 *info) +{ + struct sysinfo s; + int ret, err; + mm_segment_t old_fs = get_fs (); + + set_fs (KERNEL_DS); + ret = sys_sysinfo(&s); + set_fs (old_fs); + err = put_user (s.uptime, &info->uptime); + err |= __put_user (s.loads[0], &info->loads[0]); + err |= __put_user (s.loads[1], &info->loads[1]); + err |= __put_user (s.loads[2], &info->loads[2]); + err |= __put_user (s.totalram, &info->totalram); + err |= __put_user (s.freeram, &info->freeram); + err |= __put_user (s.sharedram, &info->sharedram); + err |= __put_user (s.bufferram, &info->bufferram); + err |= __put_user (s.totalswap, &info->totalswap); + err |= __put_user (s.freeswap, &info->freeswap); + err |= __put_user (s.procs, &info->procs); + err |= __put_user (s.totalhigh, &info->totalhigh); + err |= __put_user (s.freehigh, &info->freehigh); + err |= __put_user (s.mem_unit, &info->mem_unit); + if (err) + return -EFAULT; + return ret; +} + +#define RLIM_INFINITY32 0x7fffffff +#define RESOURCE32(x) ((x > RLIM_INFINITY32) ? RLIM_INFINITY32 : x) + +struct rlimit32 { + int rlim_cur; + int rlim_max; +}; + +#ifdef __MIPSEB__ +asmlinkage long sys32_truncate64(const char * path, unsigned long __dummy, + int length_hi, int length_lo) +#endif +#ifdef __MIPSEL__ +asmlinkage long sys32_truncate64(const char * path, unsigned long __dummy, + int length_lo, int length_hi) +#endif +{ + loff_t length; + + length = ((unsigned long) length_hi << 32) | (unsigned int) length_lo; + + return sys_truncate(path, length); +} + +#ifdef __MIPSEB__ +asmlinkage long sys32_ftruncate64(unsigned int fd, unsigned long __dummy, + int length_hi, int length_lo) +#endif +#ifdef __MIPSEL__ +asmlinkage long sys32_ftruncate64(unsigned int fd, unsigned long __dummy, + int length_lo, int length_hi) +#endif +{ + loff_t length; + + length = ((unsigned long) length_hi << 32) | (unsigned int) length_lo; + + return sys_ftruncate(fd, length); +} + +static inline long +get_tv32(struct timeval *o, struct compat_timeval *i) +{ + return (!access_ok(VERIFY_READ, i, sizeof(*i)) || + (__get_user(o->tv_sec, &i->tv_sec) | + __get_user(o->tv_usec, &i->tv_usec))); +} + +static inline long +put_tv32(struct compat_timeval *o, struct timeval *i) +{ + return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) || + (__put_user(i->tv_sec, &o->tv_sec) | + __put_user(i->tv_usec, &o->tv_usec))); +} + +extern struct timezone sys_tz; + +asmlinkage int +sys32_gettimeofday(struct compat_timeval *tv, struct timezone *tz) +{ + if (tv) { + struct timeval ktv; + do_gettimeofday(&ktv); + if (put_tv32(tv, &ktv)) + return -EFAULT; + } + if (tz) { + if (copy_to_user(tz, &sys_tz, sizeof(sys_tz))) + return -EFAULT; + } + return 0; +} + +static inline long get_ts32(struct timespec *o, struct compat_timeval *i) +{ + long usec; + + if (!access_ok(VERIFY_READ, i, sizeof(*i))) + return -EFAULT; + if (__get_user(o->tv_sec, &i->tv_sec)) + return -EFAULT; + if (__get_user(usec, &i->tv_usec)) + return -EFAULT; + o->tv_nsec = usec * 1000; + return 0; +} + +asmlinkage int +sys32_settimeofday(struct compat_timeval *tv, struct timezone *tz) +{ + struct timespec kts; + struct timezone ktz; + + if (tv) { + if (get_ts32(&kts, tv)) + return -EFAULT; + } + if (tz) { + if (copy_from_user(&ktz, tz, sizeof(ktz))) + return -EFAULT; + } + + return do_sys_settimeofday(tv ? &kts : NULL, tz ? &ktz : NULL); +} + +asmlinkage int sys32_llseek(unsigned int fd, unsigned int offset_high, + unsigned int offset_low, loff_t * result, + unsigned int origin) +{ + return sys_llseek(fd, offset_high, offset_low, result, origin); +} + +/* From the Single Unix Spec: pread & pwrite act like lseek to pos + op + + lseek back to original location. They fail just like lseek does on + non-seekable files. */ + +asmlinkage ssize_t sys32_pread(unsigned int fd, char * buf, + size_t count, u32 unused, u64 a4, u64 a5) +{ + ssize_t ret; + struct file * file; + ssize_t (*read)(struct file *, char *, size_t, loff_t *); + loff_t pos; + + ret = -EBADF; + file = fget(fd); + if (!file) + goto bad_file; + if (!(file->f_mode & FMODE_READ)) + goto out; + pos = merge_64(a4, a5); + ret = rw_verify_area(READ, file, &pos, count); + if (ret) + goto out; + ret = -EINVAL; + if (!file->f_op || !(read = file->f_op->read)) + goto out; + if (pos < 0) + goto out; + ret = -ESPIPE; + if (!(file->f_mode & FMODE_PREAD)) + goto out; + ret = read(file, buf, count, &pos); + if (ret > 0) + dnotify_parent(file->f_dentry, DN_ACCESS); +out: + fput(file); +bad_file: + return ret; +} + +asmlinkage ssize_t sys32_pwrite(unsigned int fd, const char * buf, + size_t count, u32 unused, u64 a4, u64 a5) +{ + ssize_t ret; + struct file * file; + ssize_t (*write)(struct file *, const char *, size_t, loff_t *); + loff_t pos; + + ret = -EBADF; + file = fget(fd); + if (!file) + goto bad_file; + if (!(file->f_mode & FMODE_WRITE)) + goto out; + pos = merge_64(a4, a5); + ret = rw_verify_area(WRITE, file, &pos, count); + if (ret) + goto out; + ret = -EINVAL; + if (!file->f_op || !(write = file->f_op->write)) + goto out; + if (pos < 0) + goto out; + + ret = -ESPIPE; + if (!(file->f_mode & FMODE_PWRITE)) + goto out; + + ret = write(file, buf, count, &pos); + if (ret > 0) + dnotify_parent(file->f_dentry, DN_MODIFY); +out: + fput(file); +bad_file: + return ret; +} + +asmlinkage int sys32_sched_rr_get_interval(compat_pid_t pid, + struct compat_timespec *interval) +{ + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs (); + + set_fs (KERNEL_DS); + ret = sys_sched_rr_get_interval(pid, &t); + set_fs (old_fs); + if (put_user (t.tv_sec, &interval->tv_sec) || + __put_user (t.tv_nsec, &interval->tv_nsec)) + return -EFAULT; + return ret; +} + +struct msgbuf32 { s32 mtype; char mtext[1]; }; + +struct ipc_perm32 +{ + key_t key; + compat_uid_t uid; + compat_gid_t gid; + compat_uid_t cuid; + compat_gid_t cgid; + compat_mode_t mode; + unsigned short seq; +}; + +struct ipc64_perm32 { + key_t key; + compat_uid_t uid; + compat_gid_t gid; + compat_uid_t cuid; + compat_gid_t cgid; + compat_mode_t mode; + unsigned short seq; + unsigned short __pad1; + unsigned int __unused1; + unsigned int __unused2; +}; + +struct semid_ds32 { + struct ipc_perm32 sem_perm; /* permissions .. see ipc.h */ + compat_time_t sem_otime; /* last semop time */ + compat_time_t sem_ctime; /* last change time */ + u32 sem_base; /* ptr to first semaphore in array */ + u32 sem_pending; /* pending operations to be processed */ + u32 sem_pending_last; /* last pending operation */ + u32 undo; /* undo requests on this array */ + unsigned short sem_nsems; /* no. of semaphores in array */ +}; + +struct semid64_ds32 { + struct ipc64_perm32 sem_perm; + compat_time_t sem_otime; + compat_time_t sem_ctime; + unsigned int sem_nsems; + unsigned int __unused1; + unsigned int __unused2; +}; + +struct msqid_ds32 +{ + struct ipc_perm32 msg_perm; + u32 msg_first; + u32 msg_last; + compat_time_t msg_stime; + compat_time_t msg_rtime; + compat_time_t msg_ctime; + u32 wwait; + u32 rwait; + unsigned short msg_cbytes; + unsigned short msg_qnum; + unsigned short msg_qbytes; + compat_ipc_pid_t msg_lspid; + compat_ipc_pid_t msg_lrpid; +}; + +struct msqid64_ds32 { + struct ipc64_perm32 msg_perm; + compat_time_t msg_stime; + unsigned int __unused1; + compat_time_t msg_rtime; + unsigned int __unused2; + compat_time_t msg_ctime; + unsigned int __unused3; + unsigned int msg_cbytes; + unsigned int msg_qnum; + unsigned int msg_qbytes; + compat_pid_t msg_lspid; + compat_pid_t msg_lrpid; + unsigned int __unused4; + unsigned int __unused5; +}; + +struct shmid_ds32 { + struct ipc_perm32 shm_perm; + int shm_segsz; + compat_time_t shm_atime; + compat_time_t shm_dtime; + compat_time_t shm_ctime; + compat_ipc_pid_t shm_cpid; + compat_ipc_pid_t shm_lpid; + unsigned short shm_nattch; +}; + +struct shmid64_ds32 { + struct ipc64_perm32 shm_perm; + compat_size_t shm_segsz; + compat_time_t shm_atime; + compat_time_t shm_dtime; + compat_time_t shm_ctime; + compat_pid_t shm_cpid; + compat_pid_t shm_lpid; + unsigned int shm_nattch; + unsigned int __unused1; + unsigned int __unused2; +}; + +struct ipc_kludge32 { + u32 msgp; + s32 msgtyp; +}; + +static int +do_sys32_semctl(int first, int second, int third, void *uptr) +{ + union semun fourth; + u32 pad; + int err, err2; + struct semid64_ds s; + mm_segment_t old_fs; + + if (!uptr) + return -EINVAL; + err = -EFAULT; + if (get_user (pad, (u32 *)uptr)) + return err; + if ((third & ~IPC_64) == SETVAL) + fourth.val = (int)pad; + else + fourth.__pad = (void *)A(pad); + switch (third & ~IPC_64) { + case IPC_INFO: + case IPC_RMID: + case IPC_SET: + case SEM_INFO: + case GETVAL: + case GETPID: + case GETNCNT: + case GETZCNT: + case GETALL: + case SETVAL: + case SETALL: + err = sys_semctl (first, second, third, fourth); + break; + + case IPC_STAT: + case SEM_STAT: + fourth.__pad = &s; + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_semctl(first, second, third | IPC_64, fourth); + set_fs(old_fs); + + if (third & IPC_64) { + struct semid64_ds32 *usp64 = (struct semid64_ds32 *) A(pad); + + if (!access_ok(VERIFY_WRITE, usp64, sizeof(*usp64))) { + err = -EFAULT; + break; + } + err2 = __put_user(s.sem_perm.key, &usp64->sem_perm.key); + err2 |= __put_user(s.sem_perm.uid, &usp64->sem_perm.uid); + err2 |= __put_user(s.sem_perm.gid, &usp64->sem_perm.gid); + err2 |= __put_user(s.sem_perm.cuid, &usp64->sem_perm.cuid); + err2 |= __put_user(s.sem_perm.cgid, &usp64->sem_perm.cgid); + err2 |= __put_user(s.sem_perm.mode, &usp64->sem_perm.mode); + err2 |= __put_user(s.sem_perm.seq, &usp64->sem_perm.seq); + err2 |= __put_user(s.sem_otime, &usp64->sem_otime); + err2 |= __put_user(s.sem_ctime, &usp64->sem_ctime); + err2 |= __put_user(s.sem_nsems, &usp64->sem_nsems); + } else { + struct semid_ds32 *usp32 = (struct semid_ds32 *) A(pad); + + if (!access_ok(VERIFY_WRITE, usp32, sizeof(*usp32))) { + err = -EFAULT; + break; + } + err2 = __put_user(s.sem_perm.key, &usp32->sem_perm.key); + err2 |= __put_user(s.sem_perm.uid, &usp32->sem_perm.uid); + err2 |= __put_user(s.sem_perm.gid, &usp32->sem_perm.gid); + err2 |= __put_user(s.sem_perm.cuid, &usp32->sem_perm.cuid); + err2 |= __put_user(s.sem_perm.cgid, &usp32->sem_perm.cgid); + err2 |= __put_user(s.sem_perm.mode, &usp32->sem_perm.mode); + err2 |= __put_user(s.sem_perm.seq, &usp32->sem_perm.seq); + err2 |= __put_user(s.sem_otime, &usp32->sem_otime); + err2 |= __put_user(s.sem_ctime, &usp32->sem_ctime); + err2 |= __put_user(s.sem_nsems, &usp32->sem_nsems); + } + if (err2) + err = -EFAULT; + break; + + default: + err = - EINVAL; + break; + } + + return err; +} + +static int +do_sys32_msgsnd (int first, int second, int third, void *uptr) +{ + struct msgbuf32 *up = (struct msgbuf32 *)uptr; + struct msgbuf *p; + mm_segment_t old_fs; + int err; + + if (second < 0) + return -EINVAL; + p = kmalloc (second + sizeof (struct msgbuf) + + 4, GFP_USER); + if (!p) + return -ENOMEM; + err = get_user (p->mtype, &up->mtype); + if (err) + goto out; + err |= __copy_from_user (p->mtext, &up->mtext, second); + if (err) + goto out; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgsnd (first, p, second, third); + set_fs (old_fs); +out: + kfree (p); + + return err; +} + +static int +do_sys32_msgrcv (int first, int second, int msgtyp, int third, + int version, void *uptr) +{ + struct msgbuf32 *up; + struct msgbuf *p; + mm_segment_t old_fs; + int err; + + if (!version) { + struct ipc_kludge32 *uipck = (struct ipc_kludge32 *)uptr; + struct ipc_kludge32 ipck; + + err = -EINVAL; + if (!uptr) + goto out; + err = -EFAULT; + if (copy_from_user (&ipck, uipck, sizeof (struct ipc_kludge32))) + goto out; + uptr = (void *)AA(ipck.msgp); + msgtyp = ipck.msgtyp; + } + + if (second < 0) + return -EINVAL; + err = -ENOMEM; + p = kmalloc (second + sizeof (struct msgbuf) + 4, GFP_USER); + if (!p) + goto out; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgrcv (first, p, second + 4, msgtyp, third); + set_fs (old_fs); + if (err < 0) + goto free_then_out; + up = (struct msgbuf32 *)uptr; + if (put_user (p->mtype, &up->mtype) || + __copy_to_user (&up->mtext, p->mtext, err)) + err = -EFAULT; +free_then_out: + kfree (p); +out: + return err; +} + +static int +do_sys32_msgctl (int first, int second, void *uptr) +{ + int err = -EINVAL, err2; + struct msqid64_ds m; + struct msqid_ds32 *up32 = (struct msqid_ds32 *)uptr; + struct msqid64_ds32 *up64 = (struct msqid64_ds32 *)uptr; + mm_segment_t old_fs; + + switch (second & ~IPC_64) { + case IPC_INFO: + case IPC_RMID: + case MSG_INFO: + err = sys_msgctl (first, second, (struct msqid_ds *)uptr); + break; + + case IPC_SET: + if (second & IPC_64) { + if (!access_ok(VERIFY_READ, up64, sizeof(*up64))) { + err = -EFAULT; + break; + } + err = __get_user(m.msg_perm.uid, &up64->msg_perm.uid); + err |= __get_user(m.msg_perm.gid, &up64->msg_perm.gid); + err |= __get_user(m.msg_perm.mode, &up64->msg_perm.mode); + err |= __get_user(m.msg_qbytes, &up64->msg_qbytes); + } else { + if (!access_ok(VERIFY_READ, up32, sizeof(*up32))) { + err = -EFAULT; + break; + } + err = __get_user(m.msg_perm.uid, &up32->msg_perm.uid); + err |= __get_user(m.msg_perm.gid, &up32->msg_perm.gid); + err |= __get_user(m.msg_perm.mode, &up32->msg_perm.mode); + err |= __get_user(m.msg_qbytes, &up32->msg_qbytes); + } + if (err) + break; + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_msgctl(first, second | IPC_64, (struct msqid_ds *)&m); + set_fs(old_fs); + break; + + case IPC_STAT: + case MSG_STAT: + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_msgctl(first, second | IPC_64, (struct msqid_ds *)&m); + set_fs(old_fs); + if (second & IPC_64) { + if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) { + err = -EFAULT; + break; + } + err2 = __put_user(m.msg_perm.key, &up64->msg_perm.key); + err2 |= __put_user(m.msg_perm.uid, &up64->msg_perm.uid); + err2 |= __put_user(m.msg_perm.gid, &up64->msg_perm.gid); + err2 |= __put_user(m.msg_perm.cuid, &up64->msg_perm.cuid); + err2 |= __put_user(m.msg_perm.cgid, &up64->msg_perm.cgid); + err2 |= __put_user(m.msg_perm.mode, &up64->msg_perm.mode); + err2 |= __put_user(m.msg_perm.seq, &up64->msg_perm.seq); + err2 |= __put_user(m.msg_stime, &up64->msg_stime); + err2 |= __put_user(m.msg_rtime, &up64->msg_rtime); + err2 |= __put_user(m.msg_ctime, &up64->msg_ctime); + err2 |= __put_user(m.msg_cbytes, &up64->msg_cbytes); + err2 |= __put_user(m.msg_qnum, &up64->msg_qnum); + err2 |= __put_user(m.msg_qbytes, &up64->msg_qbytes); + err2 |= __put_user(m.msg_lspid, &up64->msg_lspid); + err2 |= __put_user(m.msg_lrpid, &up64->msg_lrpid); + if (err2) + err = -EFAULT; + } else { + if (!access_ok(VERIFY_WRITE, up32, sizeof(*up32))) { + err = -EFAULT; + break; + } + err2 = __put_user(m.msg_perm.key, &up32->msg_perm.key); + err2 |= __put_user(m.msg_perm.uid, &up32->msg_perm.uid); + err2 |= __put_user(m.msg_perm.gid, &up32->msg_perm.gid); + err2 |= __put_user(m.msg_perm.cuid, &up32->msg_perm.cuid); + err2 |= __put_user(m.msg_perm.cgid, &up32->msg_perm.cgid); + err2 |= __put_user(m.msg_perm.mode, &up32->msg_perm.mode); + err2 |= __put_user(m.msg_perm.seq, &up32->msg_perm.seq); + err2 |= __put_user(m.msg_stime, &up32->msg_stime); + err2 |= __put_user(m.msg_rtime, &up32->msg_rtime); + err2 |= __put_user(m.msg_ctime, &up32->msg_ctime); + err2 |= __put_user(m.msg_cbytes, &up32->msg_cbytes); + err2 |= __put_user(m.msg_qnum, &up32->msg_qnum); + err2 |= __put_user(m.msg_qbytes, &up32->msg_qbytes); + err2 |= __put_user(m.msg_lspid, &up32->msg_lspid); + err2 |= __put_user(m.msg_lrpid, &up32->msg_lrpid); + if (err2) + err = -EFAULT; + } + break; + } + + return err; +} + +static int +do_sys32_shmat (int first, int second, int third, int version, void *uptr) +{ + unsigned long raddr; + u32 *uaddr = (u32 *)A((u32)third); + int err = -EINVAL; + + if (version == 1) + return err; + err = do_shmat (first, uptr, second, &raddr); + if (err) + return err; + err = put_user (raddr, uaddr); + return err; +} + +struct shm_info32 { + int used_ids; + u32 shm_tot, shm_rss, shm_swp; + u32 swap_attempts, swap_successes; +}; + +static int +do_sys32_shmctl (int first, int second, void *uptr) +{ + struct shmid64_ds32 *up64 = (struct shmid64_ds32 *)uptr; + struct shmid_ds32 *up32 = (struct shmid_ds32 *)uptr; + struct shm_info32 *uip = (struct shm_info32 *)uptr; + int err = -EFAULT, err2; + struct shmid64_ds s64; + mm_segment_t old_fs; + struct shm_info si; + struct shmid_ds s; + + switch (second & ~IPC_64) { + case IPC_INFO: + second = IPC_INFO; /* So that we don't have to translate it */ + case IPC_RMID: + case SHM_LOCK: + case SHM_UNLOCK: + err = sys_shmctl(first, second, (struct shmid_ds *)uptr); + break; + case IPC_SET: + if (second & IPC_64) { + err = get_user(s.shm_perm.uid, &up64->shm_perm.uid); + err |= get_user(s.shm_perm.gid, &up64->shm_perm.gid); + err |= get_user(s.shm_perm.mode, &up64->shm_perm.mode); + } else { + err = get_user(s.shm_perm.uid, &up32->shm_perm.uid); + err |= get_user(s.shm_perm.gid, &up32->shm_perm.gid); + err |= get_user(s.shm_perm.mode, &up32->shm_perm.mode); + } + if (err) + break; + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_shmctl(first, second & ~IPC_64, &s); + set_fs(old_fs); + break; + + case IPC_STAT: + case SHM_STAT: + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_shmctl(first, second | IPC_64, (void *) &s64); + set_fs(old_fs); + if (err < 0) + break; + if (second & IPC_64) { + if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) { + err = -EFAULT; + break; + } + err2 = __put_user(s64.shm_perm.key, &up64->shm_perm.key); + err2 |= __put_user(s64.shm_perm.uid, &up64->shm_perm.uid); + err2 |= __put_user(s64.shm_perm.gid, &up64->shm_perm.gid); + err2 |= __put_user(s64.shm_perm.cuid, &up64->shm_perm.cuid); + err2 |= __put_user(s64.shm_perm.cgid, &up64->shm_perm.cgid); + err2 |= __put_user(s64.shm_perm.mode, &up64->shm_perm.mode); + err2 |= __put_user(s64.shm_perm.seq, &up64->shm_perm.seq); + err2 |= __put_user(s64.shm_atime, &up64->shm_atime); + err2 |= __put_user(s64.shm_dtime, &up64->shm_dtime); + err2 |= __put_user(s64.shm_ctime, &up64->shm_ctime); + err2 |= __put_user(s64.shm_segsz, &up64->shm_segsz); + err2 |= __put_user(s64.shm_nattch, &up64->shm_nattch); + err2 |= __put_user(s64.shm_cpid, &up64->shm_cpid); + err2 |= __put_user(s64.shm_lpid, &up64->shm_lpid); + } else { + if (!access_ok(VERIFY_WRITE, up32, sizeof(*up32))) { + err = -EFAULT; + break; + } + err2 = __put_user(s64.shm_perm.key, &up32->shm_perm.key); + err2 |= __put_user(s64.shm_perm.uid, &up32->shm_perm.uid); + err2 |= __put_user(s64.shm_perm.gid, &up32->shm_perm.gid); + err2 |= __put_user(s64.shm_perm.cuid, &up32->shm_perm.cuid); + err2 |= __put_user(s64.shm_perm.cgid, &up32->shm_perm.cgid); + err2 |= __put_user(s64.shm_perm.mode, &up32->shm_perm.mode); + err2 |= __put_user(s64.shm_perm.seq, &up32->shm_perm.seq); + err2 |= __put_user(s64.shm_atime, &up32->shm_atime); + err2 |= __put_user(s64.shm_dtime, &up32->shm_dtime); + err2 |= __put_user(s64.shm_ctime, &up32->shm_ctime); + err2 |= __put_user(s64.shm_segsz, &up32->shm_segsz); + err2 |= __put_user(s64.shm_nattch, &up32->shm_nattch); + err2 |= __put_user(s64.shm_cpid, &up32->shm_cpid); + err2 |= __put_user(s64.shm_lpid, &up32->shm_lpid); + } + if (err2) + err = -EFAULT; + break; + + case SHM_INFO: + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_shmctl(first, second, (void *)&si); + set_fs(old_fs); + if (err < 0) + break; + err2 = put_user(si.used_ids, &uip->used_ids); + err2 |= __put_user(si.shm_tot, &uip->shm_tot); + err2 |= __put_user(si.shm_rss, &uip->shm_rss); + err2 |= __put_user(si.shm_swp, &uip->shm_swp); + err2 |= __put_user(si.swap_attempts, &uip->swap_attempts); + err2 |= __put_user (si.swap_successes, &uip->swap_successes); + if (err2) + err = -EFAULT; + break; + + default: + err = -EINVAL; + break; + } + + return err; +} + +static int sys32_semtimedop(int semid, struct sembuf *tsems, int nsems, + const struct compat_timespec *timeout32) +{ + struct compat_timespec t32; + struct timespec *t64 = compat_alloc_user_space(sizeof(*t64)); + + if (copy_from_user(&t32, timeout32, sizeof(t32))) + return -EFAULT; + + if (put_user(t32.tv_sec, &t64->tv_sec) || + put_user(t32.tv_nsec, &t64->tv_nsec)) + return -EFAULT; + + return sys_semtimedop(semid, tsems, nsems, t64); +} + +asmlinkage long +sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u32 fifth) +{ + int version, err; + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + switch (call) { + case SEMOP: + /* struct sembuf is the same on 32 and 64bit :)) */ + err = sys_semtimedop (first, (struct sembuf *)AA(ptr), second, + NULL); + break; + case SEMTIMEDOP: + err = sys32_semtimedop (first, (struct sembuf *)AA(ptr), second, + (const struct compat_timespec __user *)AA(fifth)); + break; + case SEMGET: + err = sys_semget (first, second, third); + break; + case SEMCTL: + err = do_sys32_semctl (first, second, third, + (void *)AA(ptr)); + break; + + case MSGSND: + err = do_sys32_msgsnd (first, second, third, + (void *)AA(ptr)); + break; + case MSGRCV: + err = do_sys32_msgrcv (first, second, fifth, third, + version, (void *)AA(ptr)); + break; + case MSGGET: + err = sys_msgget ((key_t) first, second); + break; + case MSGCTL: + err = do_sys32_msgctl (first, second, (void *)AA(ptr)); + break; + + case SHMAT: + err = do_sys32_shmat (first, second, third, + version, (void *)AA(ptr)); + break; + case SHMDT: + err = sys_shmdt ((char *)A(ptr)); + break; + case SHMGET: + err = sys_shmget (first, (unsigned)second, third); + break; + case SHMCTL: + err = do_sys32_shmctl (first, second, (void *)AA(ptr)); + break; + default: + err = -EINVAL; + break; + } + + return err; +} + +asmlinkage long sys32_shmat(int shmid, char __user *shmaddr, + int shmflg, int32_t *addr) +{ + unsigned long raddr; + int err; + + err = do_shmat(shmid, shmaddr, shmflg, &raddr); + if (err) + return err; + + return put_user(raddr, addr); +} + +struct sysctl_args32 +{ + compat_caddr_t name; + int nlen; + compat_caddr_t oldval; + compat_caddr_t oldlenp; + compat_caddr_t newval; + compat_size_t newlen; + unsigned int __unused[4]; +}; + +#ifdef CONFIG_SYSCTL + +asmlinkage long sys32_sysctl(struct sysctl_args32 *args) +{ + struct sysctl_args32 tmp; + int error; + size_t oldlen, *oldlenp = NULL; + unsigned long addr = (((long)&args->__unused[0]) + 7) & ~7; + + if (copy_from_user(&tmp, args, sizeof(tmp))) + return -EFAULT; + + if (tmp.oldval && tmp.oldlenp) { + /* Duh, this is ugly and might not work if sysctl_args + is in read-only memory, but do_sysctl does indirectly + a lot of uaccess in both directions and we'd have to + basically copy the whole sysctl.c here, and + glibc's __sysctl uses rw memory for the structure + anyway. */ + if (get_user(oldlen, (u32 *)A(tmp.oldlenp)) || + put_user(oldlen, (size_t *)addr)) + return -EFAULT; + oldlenp = (size_t *)addr; + } + + lock_kernel(); + error = do_sysctl((int *)A(tmp.name), tmp.nlen, (void *)A(tmp.oldval), + oldlenp, (void *)A(tmp.newval), tmp.newlen); + unlock_kernel(); + if (oldlenp) { + if (!error) { + if (get_user(oldlen, (size_t *)addr) || + put_user(oldlen, (u32 *)A(tmp.oldlenp))) + error = -EFAULT; + } + copy_to_user(args->__unused, tmp.__unused, sizeof(tmp.__unused)); + } + return error; +} + +#endif /* CONFIG_SYSCTL */ + +asmlinkage long sys32_newuname(struct new_utsname * name) +{ + int ret = 0; + + down_read(&uts_sem); + if (copy_to_user(name,&system_utsname,sizeof *name)) + ret = -EFAULT; + up_read(&uts_sem); + + if (current->personality == PER_LINUX32 && !ret) + if (copy_to_user(name->machine, "mips\0\0\0", 8)) + ret = -EFAULT; + + return ret; +} + +asmlinkage int sys32_personality(unsigned long personality) +{ + int ret; + if (current->personality == PER_LINUX32 && personality == PER_LINUX) + personality = PER_LINUX32; + ret = sys_personality(personality); + if (ret == PER_LINUX32) + ret = PER_LINUX; + return ret; +} + +/* ustat compatibility */ +struct ustat32 { + compat_daddr_t f_tfree; + compat_ino_t f_tinode; + char f_fname[6]; + char f_fpack[6]; +}; + +extern asmlinkage long sys_ustat(dev_t dev, struct ustat * ubuf); + +asmlinkage int sys32_ustat(dev_t dev, struct ustat32 * ubuf32) +{ + int err; + struct ustat tmp; + struct ustat32 tmp32; + mm_segment_t old_fs = get_fs(); + + set_fs(KERNEL_DS); + err = sys_ustat(dev, &tmp); + set_fs (old_fs); + + if (err) + goto out; + + memset(&tmp32,0,sizeof(struct ustat32)); + tmp32.f_tfree = tmp.f_tfree; + tmp32.f_tinode = tmp.f_tinode; + + err = copy_to_user(ubuf32,&tmp32,sizeof(struct ustat32)) ? -EFAULT : 0; + +out: + return err; +} + +/* Handle adjtimex compatibility. */ + +struct timex32 { + u32 modes; + s32 offset, freq, maxerror, esterror; + s32 status, constant, precision, tolerance; + struct compat_timeval time; + s32 tick; + s32 ppsfreq, jitter, shift, stabil; + s32 jitcnt, calcnt, errcnt, stbcnt; + s32 :32; s32 :32; s32 :32; s32 :32; + s32 :32; s32 :32; s32 :32; s32 :32; + s32 :32; s32 :32; s32 :32; s32 :32; +}; + +extern int do_adjtimex(struct timex *); + +asmlinkage int sys32_adjtimex(struct timex32 *utp) +{ + struct timex txc; + int ret; + + memset(&txc, 0, sizeof(struct timex)); + + if (get_user(txc.modes, &utp->modes) || + __get_user(txc.offset, &utp->offset) || + __get_user(txc.freq, &utp->freq) || + __get_user(txc.maxerror, &utp->maxerror) || + __get_user(txc.esterror, &utp->esterror) || + __get_user(txc.status, &utp->status) || + __get_user(txc.constant, &utp->constant) || + __get_user(txc.precision, &utp->precision) || + __get_user(txc.tolerance, &utp->tolerance) || + __get_user(txc.time.tv_sec, &utp->time.tv_sec) || + __get_user(txc.time.tv_usec, &utp->time.tv_usec) || + __get_user(txc.tick, &utp->tick) || + __get_user(txc.ppsfreq, &utp->ppsfreq) || + __get_user(txc.jitter, &utp->jitter) || + __get_user(txc.shift, &utp->shift) || + __get_user(txc.stabil, &utp->stabil) || + __get_user(txc.jitcnt, &utp->jitcnt) || + __get_user(txc.calcnt, &utp->calcnt) || + __get_user(txc.errcnt, &utp->errcnt) || + __get_user(txc.stbcnt, &utp->stbcnt)) + return -EFAULT; + + ret = do_adjtimex(&txc); + + if (put_user(txc.modes, &utp->modes) || + __put_user(txc.offset, &utp->offset) || + __put_user(txc.freq, &utp->freq) || + __put_user(txc.maxerror, &utp->maxerror) || + __put_user(txc.esterror, &utp->esterror) || + __put_user(txc.status, &utp->status) || + __put_user(txc.constant, &utp->constant) || + __put_user(txc.precision, &utp->precision) || + __put_user(txc.tolerance, &utp->tolerance) || + __put_user(txc.time.tv_sec, &utp->time.tv_sec) || + __put_user(txc.time.tv_usec, &utp->time.tv_usec) || + __put_user(txc.tick, &utp->tick) || + __put_user(txc.ppsfreq, &utp->ppsfreq) || + __put_user(txc.jitter, &utp->jitter) || + __put_user(txc.shift, &utp->shift) || + __put_user(txc.stabil, &utp->stabil) || + __put_user(txc.jitcnt, &utp->jitcnt) || + __put_user(txc.calcnt, &utp->calcnt) || + __put_user(txc.errcnt, &utp->errcnt) || + __put_user(txc.stbcnt, &utp->stbcnt)) + ret = -EFAULT; + + return ret; +} + +asmlinkage int sys32_sendfile(int out_fd, int in_fd, compat_off_t *offset, + s32 count) +{ + mm_segment_t old_fs = get_fs(); + int ret; + off_t of; + + if (offset && get_user(of, offset)) + return -EFAULT; + + set_fs(KERNEL_DS); + ret = sys_sendfile(out_fd, in_fd, offset ? &of : NULL, count); + set_fs(old_fs); + + if (offset && put_user(of, offset)) + return -EFAULT; + + return ret; +} + +asmlinkage ssize_t sys32_readahead(int fd, u32 pad0, u64 a2, u64 a3, + size_t count) +{ + return sys_readahead(fd, merge_64(a2, a3), count); +} + +/* Argument list sizes for sys_socketcall */ +#define AL(x) ((x) * sizeof(unsigned int)) +static unsigned char socketcall_nargs[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), + AL(3),AL(3),AL(4),AL(4),AL(4),AL(6), + AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)}; +#undef AL + +/* + * System call vectors. + * + * Argument checking cleaned up. Saved 20% in size. + * This function doesn't need to set the kernel lock because + * it is set by the callees. + */ + +asmlinkage long sys32_socketcall(int call, unsigned int *args32) +{ + unsigned int a[6]; + unsigned int a0,a1; + int err; + + extern asmlinkage long sys_socket(int family, int type, int protocol); + extern asmlinkage long sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen); + extern asmlinkage long sys_connect(int fd, struct sockaddr __user *uservaddr, int addrlen); + extern asmlinkage long sys_listen(int fd, int backlog); + extern asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen); + extern asmlinkage long sys_getsockname(int fd, struct sockaddr __user *usockaddr, int __user *usockaddr_len); + extern asmlinkage long sys_getpeername(int fd, struct sockaddr __user *usockaddr, int __user *usockaddr_len); + extern asmlinkage long sys_socketpair(int family, int type, int protocol, int __user *usockvec); + extern asmlinkage long sys_send(int fd, void __user * buff, size_t len, unsigned flags); + extern asmlinkage long sys_sendto(int fd, void __user * buff, size_t len, unsigned flags, + struct sockaddr __user *addr, int addr_len); + extern asmlinkage long sys_recv(int fd, void __user * ubuf, size_t size, unsigned flags); + extern asmlinkage long sys_recvfrom(int fd, void __user * ubuf, size_t size, unsigned flags, + struct sockaddr __user *addr, int __user *addr_len); + extern asmlinkage long sys_shutdown(int fd, int how); + extern asmlinkage long sys_setsockopt(int fd, int level, int optname, char __user *optval, int optlen); + extern asmlinkage long sys_getsockopt(int fd, int level, int optname, char __user *optval, int *optlen); + extern asmlinkage long sys_sendmsg(int fd, struct msghdr __user *msg, unsigned flags); + extern asmlinkage long sys_recvmsg(int fd, struct msghdr __user *msg, unsigned int flags); + + + if(call<1||call>SYS_RECVMSG) + return -EINVAL; + + /* copy_from_user should be SMP safe. */ + if (copy_from_user(a, args32, socketcall_nargs[call])) + return -EFAULT; + + a0=a[0]; + a1=a[1]; + + switch(call) + { + case SYS_SOCKET: + err = sys_socket(a0,a1,a[2]); + break; + case SYS_BIND: + err = sys_bind(a0,(struct sockaddr __user *)A(a1), a[2]); + break; + case SYS_CONNECT: + err = sys_connect(a0, (struct sockaddr __user *)A(a1), a[2]); + break; + case SYS_LISTEN: + err = sys_listen(a0,a1); + break; + case SYS_ACCEPT: + err = sys_accept(a0,(struct sockaddr __user *)A(a1), (int __user *)A(a[2])); + break; + case SYS_GETSOCKNAME: + err = sys_getsockname(a0,(struct sockaddr __user *)A(a1), (int __user *)A(a[2])); + break; + case SYS_GETPEERNAME: + err = sys_getpeername(a0, (struct sockaddr __user *)A(a1), (int __user *)A(a[2])); + break; + case SYS_SOCKETPAIR: + err = sys_socketpair(a0,a1, a[2], (int __user *)A(a[3])); + break; + case SYS_SEND: + err = sys_send(a0, (void __user *)A(a1), a[2], a[3]); + break; + case SYS_SENDTO: + err = sys_sendto(a0,(void __user *)A(a1), a[2], a[3], + (struct sockaddr __user *)A(a[4]), a[5]); + break; + case SYS_RECV: + err = sys_recv(a0, (void __user *)A(a1), a[2], a[3]); + break; + case SYS_RECVFROM: + err = sys_recvfrom(a0, (void __user *)A(a1), a[2], a[3], + (struct sockaddr __user *)A(a[4]), (int __user *)A(a[5])); + break; + case SYS_SHUTDOWN: + err = sys_shutdown(a0,a1); + break; + case SYS_SETSOCKOPT: + err = sys_setsockopt(a0, a1, a[2], (char __user *)A(a[3]), a[4]); + break; + case SYS_GETSOCKOPT: + err = sys_getsockopt(a0, a1, a[2], (char __user *)A(a[3]), (int __user *)A(a[4])); + break; + case SYS_SENDMSG: + err = sys_sendmsg(a0, (struct msghdr __user *) A(a1), a[2]); + break; + case SYS_RECVMSG: + err = sys_recvmsg(a0, (struct msghdr __user *) A(a1), a[2]); + break; + default: + err = -EINVAL; + break; + } + return err; +} diff --git a/arch/mips/kernel/mips_ksyms.c b/arch/mips/kernel/mips_ksyms.c new file mode 100644 index 000000000000..eed29fc9dc82 --- /dev/null +++ b/arch/mips/kernel/mips_ksyms.c @@ -0,0 +1,67 @@ +/* + * 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, 97, 98, 99, 2000, 01, 03, 04, 05 by Ralf Baechle + * Copyright (C) 1999, 2000, 01 Silicon Graphics, Inc. + */ +#include <linux/config.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <asm/checksum.h> +#include <asm/pgtable.h> +#include <asm/uaccess.h> + +extern void *__bzero(void *__s, size_t __count); +extern long __strncpy_from_user_nocheck_asm(char *__to, + const char *__from, long __len); +extern long __strncpy_from_user_asm(char *__to, const char *__from, + long __len); +extern long __strlen_user_nocheck_asm(const char *s); +extern long __strlen_user_asm(const char *s); +extern long __strnlen_user_nocheck_asm(const char *s); +extern long __strnlen_user_asm(const char *s); + +/* + * String functions + */ +EXPORT_SYMBOL(memchr); +EXPORT_SYMBOL(memcmp); +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memmove); +EXPORT_SYMBOL(strcat); +EXPORT_SYMBOL(strchr); +#ifdef CONFIG_MIPS64 +EXPORT_SYMBOL(strncmp); +#endif +EXPORT_SYMBOL(strlen); +EXPORT_SYMBOL(strpbrk); +EXPORT_SYMBOL(strncat); +EXPORT_SYMBOL(strnlen); +EXPORT_SYMBOL(strrchr); +EXPORT_SYMBOL(strstr); + +EXPORT_SYMBOL(kernel_thread); + +/* + * Userspace access stuff. + */ +EXPORT_SYMBOL(__copy_user); +EXPORT_SYMBOL(__bzero); +EXPORT_SYMBOL(__strncpy_from_user_nocheck_asm); +EXPORT_SYMBOL(__strncpy_from_user_asm); +EXPORT_SYMBOL(__strlen_user_nocheck_asm); +EXPORT_SYMBOL(__strlen_user_asm); +EXPORT_SYMBOL(__strnlen_user_nocheck_asm); +EXPORT_SYMBOL(__strnlen_user_asm); + +EXPORT_SYMBOL(csum_partial); + +EXPORT_SYMBOL(invalid_pte_table); +#ifdef CONFIG_GENERIC_IRQ_PROBE +EXPORT_SYMBOL(probe_irq_mask); +#endif diff --git a/arch/mips/kernel/module-elf32.c b/arch/mips/kernel/module-elf32.c new file mode 100644 index 000000000000..ffd216d6d6dc --- /dev/null +++ b/arch/mips/kernel/module-elf32.c @@ -0,0 +1,250 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) 2001 Rusty Russell. + * Copyright (C) 2003, 2004 Ralf Baechle (ralf@linux-mips.org) + */ + +#undef DEBUG + +#include <linux/moduleloader.h> +#include <linux/elf.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/kernel.h> + +struct mips_hi16 { + struct mips_hi16 *next; + Elf32_Addr *addr; + Elf32_Addr value; +}; + +static struct mips_hi16 *mips_hi16_list; + +void *module_alloc(unsigned long size) +{ + if (size == 0) + return NULL; + return vmalloc(size); +} + + +/* Free memory returned from module_alloc */ +void module_free(struct module *mod, void *module_region) +{ + vfree(module_region); + /* FIXME: If module_region == mod->init_region, trim exception + table entries. */ +} + +int module_frob_arch_sections(Elf_Ehdr *hdr, + Elf_Shdr *sechdrs, + char *secstrings, + struct module *mod) +{ + return 0; +} + +static int apply_r_mips_none(struct module *me, uint32_t *location, + Elf32_Addr v) +{ + return 0; +} + +static int apply_r_mips_32(struct module *me, uint32_t *location, + Elf32_Addr v) +{ + *location += v; + + return 0; +} + +static int apply_r_mips_26(struct module *me, uint32_t *location, + Elf32_Addr v) +{ + if (v % 4) { + printk(KERN_ERR "module %s: dangerous relocation\n", me->name); + return -ENOEXEC; + } + + if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) { + printk(KERN_ERR + "module %s: relocation overflow\n", + me->name); + return -ENOEXEC; + } + + *location = (*location & ~0x03ffffff) | + ((*location + (v >> 2)) & 0x03ffffff); + + return 0; +} + +static int apply_r_mips_hi16(struct module *me, uint32_t *location, + Elf32_Addr v) +{ + struct mips_hi16 *n; + + /* + * We cannot relocate this one now because we don't know the value of + * the carry we need to add. Save the information, and let LO16 do the + * actual relocation. + */ + n = kmalloc(sizeof *n, GFP_KERNEL); + if (!n) + return -ENOMEM; + + n->addr = location; + n->value = v; + n->next = mips_hi16_list; + mips_hi16_list = n; + + return 0; +} + +static int apply_r_mips_lo16(struct module *me, uint32_t *location, + Elf32_Addr v) +{ + unsigned long insnlo = *location; + Elf32_Addr val, vallo; + + /* Sign extend the addend we extract from the lo insn. */ + vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000; + + if (mips_hi16_list != NULL) { + struct mips_hi16 *l; + + l = mips_hi16_list; + while (l != NULL) { + struct mips_hi16 *next; + unsigned long insn; + + /* + * The value for the HI16 had best be the same. + */ + if (v != l->value) + goto out_danger; + + /* + * Do the HI16 relocation. Note that we actually don't + * need to know anything about the LO16 itself, except + * where to find the low 16 bits of the addend needed + * by the LO16. + */ + insn = *l->addr; + val = ((insn & 0xffff) << 16) + vallo; + val += v; + + /* + * Account for the sign extension that will happen in + * the low bits. + */ + val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff; + + insn = (insn & ~0xffff) | val; + *l->addr = insn; + + next = l->next; + kfree(l); + l = next; + } + + mips_hi16_list = NULL; + } + + /* + * Ok, we're done with the HI16 relocs. Now deal with the LO16. + */ + val = v + vallo; + insnlo = (insnlo & ~0xffff) | (val & 0xffff); + *location = insnlo; + + return 0; + +out_danger: + printk(KERN_ERR "module %s: dangerous " "relocation\n", me->name); + + return -ENOEXEC; +} + +static int (*reloc_handlers[]) (struct module *me, uint32_t *location, + Elf32_Addr v) = { + [R_MIPS_NONE] = apply_r_mips_none, + [R_MIPS_32] = apply_r_mips_32, + [R_MIPS_26] = apply_r_mips_26, + [R_MIPS_HI16] = apply_r_mips_hi16, + [R_MIPS_LO16] = apply_r_mips_lo16 +}; + +int apply_relocate(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + Elf32_Rel *rel = (void *) sechdrs[relsec].sh_addr; + Elf32_Sym *sym; + uint32_t *location; + unsigned int i; + Elf32_Addr v; + int res; + + pr_debug("Applying relocate section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { + Elf32_Word r_info = rel[i].r_info; + + /* This is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + + rel[i].r_offset; + /* This is the symbol it is referring to */ + sym = (Elf32_Sym *)sechdrs[symindex].sh_addr + + ELF32_R_SYM(r_info); + if (!sym->st_value) { + printk(KERN_WARNING "%s: Unknown symbol %s\n", + me->name, strtab + sym->st_name); + return -ENOENT; + } + + v = sym->st_value; + + res = reloc_handlers[ELF32_R_TYPE(r_info)](me, location, v); + if (res) + return res; + } + + return 0; +} + +int apply_relocate_add(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + /* + * Current binutils always generate .rela relocations. Keep smiling + * if it's empty, abort otherwise. + */ + if (!sechdrs[relsec].sh_size) + return 0; + + printk(KERN_ERR "module %s: ADD RELOCATION unsupported\n", + me->name); + return -ENOEXEC; +} diff --git a/arch/mips/kernel/module-elf64.c b/arch/mips/kernel/module-elf64.c new file mode 100644 index 000000000000..e804792ee1ee --- /dev/null +++ b/arch/mips/kernel/module-elf64.c @@ -0,0 +1,274 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) 2001 Rusty Russell. + * Copyright (C) 2003, 2004 Ralf Baechle (ralf@linux-mips.org) + */ + +#undef DEBUG + +#include <linux/moduleloader.h> +#include <linux/elf.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/kernel.h> + +struct mips_hi16 { + struct mips_hi16 *next; + Elf32_Addr *addr; + Elf64_Addr value; +}; + +static struct mips_hi16 *mips_hi16_list; + +void *module_alloc(unsigned long size) +{ + if (size == 0) + return NULL; + return vmalloc(size); +} + + +/* Free memory returned from module_alloc */ +void module_free(struct module *mod, void *module_region) +{ + vfree(module_region); + /* FIXME: If module_region == mod->init_region, trim exception + table entries. */ +} + +int module_frob_arch_sections(Elf_Ehdr *hdr, + Elf_Shdr *sechdrs, + char *secstrings, + struct module *mod) +{ + return 0; +} + +int apply_relocate(Elf64_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + /* + * We don't want to deal with REL relocations - RELA is so much saner. + */ + if (!sechdrs[relsec].sh_size) + return 0; + + printk(KERN_ERR "module %s: REL relocation unsupported\n", + me->name); + return -ENOEXEC; +} + +static int apply_r_mips_none(struct module *me, uint32_t *location, + Elf64_Addr v) +{ + return 0; +} + +static int apply_r_mips_32(struct module *me, uint32_t *location, + Elf64_Addr v) +{ + *location = v; + + return 0; +} + +static int apply_r_mips_26(struct module *me, uint32_t *location, + Elf64_Addr v) +{ + if (v % 4) { + printk(KERN_ERR "module %s: dangerous relocation\n", me->name); + return -ENOEXEC; + } + + if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) { + printk(KERN_ERR + "module %s: relocation overflow\n", + me->name); + return -ENOEXEC; + } + + *location = (*location & ~0x03ffffff) | ((v >> 2) & 0x03ffffff); + + return 0; +} + +static int apply_r_mips_hi16(struct module *me, uint32_t *location, + Elf64_Addr v) +{ + struct mips_hi16 *n; + + /* + * We cannot relocate this one now because we don't know the value of + * the carry we need to add. Save the information, and let LO16 do the + * actual relocation. + */ + n = kmalloc(sizeof *n, GFP_KERNEL); + if (!n) + return -ENOMEM; + + n->addr = location; + n->value = v; + n->next = mips_hi16_list; + mips_hi16_list = n; + + return 0; +} + +static int apply_r_mips_lo16(struct module *me, uint32_t *location, + Elf64_Addr v) +{ + unsigned long insnlo = *location; + Elf32_Addr val, vallo; + + /* Sign extend the addend we extract from the lo insn. */ + vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000; + + if (mips_hi16_list != NULL) { + struct mips_hi16 *l; + + l = mips_hi16_list; + while (l != NULL) { + struct mips_hi16 *next; + unsigned long insn; + + /* + * The value for the HI16 had best be the same. + */ + if (v != l->value) + goto out_danger; + + /* + * Do the HI16 relocation. Note that we actually don't + * need to know anything about the LO16 itself, except + * where to find the low 16 bits of the addend needed + * by the LO16. + */ + insn = *l->addr; + val = ((insn & 0xffff) << 16) + vallo; + val += v; + + /* + * Account for the sign extension that will happen in + * the low bits. + */ + val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff; + + insn = (insn & ~0xffff) | val; + *l->addr = insn; + + next = l->next; + kfree(l); + l = next; + } + + mips_hi16_list = NULL; + } + + /* + * Ok, we're done with the HI16 relocs. Now deal with the LO16. + */ + insnlo = (insnlo & ~0xffff) | (v & 0xffff); + *location = insnlo; + + return 0; + +out_danger: + printk(KERN_ERR "module %s: dangerous " "relocation\n", me->name); + + return -ENOEXEC; +} + +static int apply_r_mips_64(struct module *me, uint32_t *location, + Elf64_Addr v) +{ + *(uint64_t *) location = v; + + return 0; +} + + +static int apply_r_mips_higher(struct module *me, uint32_t *location, + Elf64_Addr v) +{ + *location = (*location & 0xffff0000) | + ((((long long) v + 0x80008000LL) >> 32) & 0xffff); + + return 0; +} + +static int apply_r_mips_highest(struct module *me, uint32_t *location, + Elf64_Addr v) +{ + *location = (*location & 0xffff0000) | + ((((long long) v + 0x800080008000LL) >> 48) & 0xffff); + + return 0; +} + +static int (*reloc_handlers[]) (struct module *me, uint32_t *location, + Elf64_Addr v) = { + [R_MIPS_NONE] = apply_r_mips_none, + [R_MIPS_32] = apply_r_mips_32, + [R_MIPS_26] = apply_r_mips_26, + [R_MIPS_HI16] = apply_r_mips_hi16, + [R_MIPS_LO16] = apply_r_mips_lo16, + [R_MIPS_64] = apply_r_mips_64, + [R_MIPS_HIGHER] = apply_r_mips_higher, + [R_MIPS_HIGHEST] = apply_r_mips_highest +}; + +int apply_relocate_add(Elf64_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + Elf64_Mips_Rela *rel = (void *) sechdrs[relsec].sh_addr; + Elf64_Sym *sym; + uint32_t *location; + unsigned int i; + Elf64_Addr v; + int res; + + pr_debug("Applying relocate section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { + /* This is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + + rel[i].r_offset; + /* This is the symbol it is referring to */ + sym = (Elf64_Sym *)sechdrs[symindex].sh_addr + rel[i].r_sym; + if (!sym->st_value) { + printk(KERN_WARNING "%s: Unknown symbol %s\n", + me->name, strtab + sym->st_name); + return -ENOENT; + } + + v = sym->st_value; + + res = reloc_handlers[rel[i].r_type](me, location, v); + if (res) + return res; + } + + return 0; +} diff --git a/arch/mips/kernel/module.c b/arch/mips/kernel/module.c new file mode 100644 index 000000000000..458af3c7a639 --- /dev/null +++ b/arch/mips/kernel/module.c @@ -0,0 +1,53 @@ +#include <linux/module.h> +#include <linux/spinlock.h> + +static LIST_HEAD(dbe_list); +static DEFINE_SPINLOCK(dbe_lock); + +/* Given an address, look for it in the module exception tables. */ +const struct exception_table_entry *search_module_dbetables(unsigned long addr) +{ + unsigned long flags; + const struct exception_table_entry *e = NULL; + struct mod_arch_specific *dbe; + + spin_lock_irqsave(&dbe_lock, flags); + list_for_each_entry(dbe, &dbe_list, dbe_list) { + e = search_extable(dbe->dbe_start, dbe->dbe_end - 1, addr); + if (e) + break; + } + spin_unlock_irqrestore(&dbe_lock, flags); + + /* Now, if we found one, we are running inside it now, hence + we cannot unload the module, hence no refcnt needed. */ + return e; +} + +/* Put in dbe list if neccessary. */ +int module_finalize(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + struct module *me) +{ + const Elf_Shdr *s; + char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; + + INIT_LIST_HEAD(&me->arch.dbe_list); + for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { + if (strcmp("__dbe_table", secstrings + s->sh_name) != 0) + continue; + me->arch.dbe_start = (void *)s->sh_addr; + me->arch.dbe_end = (void *)s->sh_addr + s->sh_size; + spin_lock_irq(&dbe_lock); + list_add(&me->arch.dbe_list, &dbe_list); + spin_unlock_irq(&dbe_lock); + } + return 0; +} + +void module_arch_cleanup(struct module *mod) +{ + spin_lock_irq(&dbe_lock); + list_del(&mod->arch.dbe_list); + spin_unlock_irq(&dbe_lock); +} diff --git a/arch/mips/kernel/offset.c b/arch/mips/kernel/offset.c new file mode 100644 index 000000000000..2c11abb5a406 --- /dev/null +++ b/arch/mips/kernel/offset.c @@ -0,0 +1,314 @@ +/* + * offset.c: Calculate pt_regs and task_struct offsets. + * + * Copyright (C) 1996 David S. Miller + * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003 Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * + * Kevin Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com + * Copyright (C) 2000 MIPS Technologies, Inc. + */ +#include <linux/config.h> +#include <linux/compat.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/interrupt.h> + +#include <asm/ptrace.h> +#include <asm/processor.h> + +#define text(t) __asm__("\n@@@" t) +#define _offset(type, member) (&(((type *)NULL)->member)) +#define offset(string, ptr, member) \ + __asm__("\n@@@" string "%0" : : "i" (_offset(ptr, member))) +#define constant(string, member) \ + __asm__("\n@@@" string "%x0" : : "ri" (member)) +#define size(string, size) \ + __asm__("\n@@@" string "%0" : : "i" (sizeof(size))) +#define linefeed text("") + +void output_ptreg_defines(void) +{ + text("/* MIPS pt_regs offsets. */"); + offset("#define PT_R0 ", struct pt_regs, regs[0]); + offset("#define PT_R1 ", struct pt_regs, regs[1]); + offset("#define PT_R2 ", struct pt_regs, regs[2]); + offset("#define PT_R3 ", struct pt_regs, regs[3]); + offset("#define PT_R4 ", struct pt_regs, regs[4]); + offset("#define PT_R5 ", struct pt_regs, regs[5]); + offset("#define PT_R6 ", struct pt_regs, regs[6]); + offset("#define PT_R7 ", struct pt_regs, regs[7]); + offset("#define PT_R8 ", struct pt_regs, regs[8]); + offset("#define PT_R9 ", struct pt_regs, regs[9]); + offset("#define PT_R10 ", struct pt_regs, regs[10]); + offset("#define PT_R11 ", struct pt_regs, regs[11]); + offset("#define PT_R12 ", struct pt_regs, regs[12]); + offset("#define PT_R13 ", struct pt_regs, regs[13]); + offset("#define PT_R14 ", struct pt_regs, regs[14]); + offset("#define PT_R15 ", struct pt_regs, regs[15]); + offset("#define PT_R16 ", struct pt_regs, regs[16]); + offset("#define PT_R17 ", struct pt_regs, regs[17]); + offset("#define PT_R18 ", struct pt_regs, regs[18]); + offset("#define PT_R19 ", struct pt_regs, regs[19]); + offset("#define PT_R20 ", struct pt_regs, regs[20]); + offset("#define PT_R21 ", struct pt_regs, regs[21]); + offset("#define PT_R22 ", struct pt_regs, regs[22]); + offset("#define PT_R23 ", struct pt_regs, regs[23]); + offset("#define PT_R24 ", struct pt_regs, regs[24]); + offset("#define PT_R25 ", struct pt_regs, regs[25]); + offset("#define PT_R26 ", struct pt_regs, regs[26]); + offset("#define PT_R27 ", struct pt_regs, regs[27]); + offset("#define PT_R28 ", struct pt_regs, regs[28]); + offset("#define PT_R29 ", struct pt_regs, regs[29]); + offset("#define PT_R30 ", struct pt_regs, regs[30]); + offset("#define PT_R31 ", struct pt_regs, regs[31]); + offset("#define PT_LO ", struct pt_regs, lo); + offset("#define PT_HI ", struct pt_regs, hi); + offset("#define PT_EPC ", struct pt_regs, cp0_epc); + offset("#define PT_BVADDR ", struct pt_regs, cp0_badvaddr); + offset("#define PT_STATUS ", struct pt_regs, cp0_status); + offset("#define PT_CAUSE ", struct pt_regs, cp0_cause); + size("#define PT_SIZE ", struct pt_regs); + linefeed; +} + +void output_task_defines(void) +{ + text("/* MIPS task_struct offsets. */"); + offset("#define TASK_STATE ", struct task_struct, state); + offset("#define TASK_THREAD_INFO ", struct task_struct, thread_info); + offset("#define TASK_FLAGS ", struct task_struct, flags); + offset("#define TASK_MM ", struct task_struct, mm); + offset("#define TASK_PID ", struct task_struct, pid); + size( "#define TASK_STRUCT_SIZE ", struct task_struct); + linefeed; +} + +void output_thread_info_defines(void) +{ + text("/* MIPS thread_info offsets. */"); + offset("#define TI_TASK ", struct thread_info, task); + offset("#define TI_EXEC_DOMAIN ", struct thread_info, exec_domain); + offset("#define TI_FLAGS ", struct thread_info, flags); + offset("#define TI_CPU ", struct thread_info, cpu); + offset("#define TI_PRE_COUNT ", struct thread_info, preempt_count); + offset("#define TI_ADDR_LIMIT ", struct thread_info, addr_limit); + offset("#define TI_RESTART_BLOCK ", struct thread_info, restart_block); + constant("#define _THREAD_SIZE_ORDER ", THREAD_SIZE_ORDER); + constant("#define _THREAD_SIZE ", THREAD_SIZE); + constant("#define _THREAD_MASK ", THREAD_MASK); + linefeed; +} + +void output_thread_defines(void) +{ + text("/* MIPS specific thread_struct offsets. */"); + offset("#define THREAD_REG16 ", struct task_struct, thread.reg16); + offset("#define THREAD_REG17 ", struct task_struct, thread.reg17); + offset("#define THREAD_REG18 ", struct task_struct, thread.reg18); + offset("#define THREAD_REG19 ", struct task_struct, thread.reg19); + offset("#define THREAD_REG20 ", struct task_struct, thread.reg20); + offset("#define THREAD_REG21 ", struct task_struct, thread.reg21); + offset("#define THREAD_REG22 ", struct task_struct, thread.reg22); + offset("#define THREAD_REG23 ", struct task_struct, thread.reg23); + offset("#define THREAD_REG29 ", struct task_struct, thread.reg29); + offset("#define THREAD_REG30 ", struct task_struct, thread.reg30); + offset("#define THREAD_REG31 ", struct task_struct, thread.reg31); + offset("#define THREAD_STATUS ", struct task_struct, + thread.cp0_status); + offset("#define THREAD_FPU ", struct task_struct, thread.fpu); + + offset("#define THREAD_BVADDR ", struct task_struct, \ + thread.cp0_badvaddr); + offset("#define THREAD_BUADDR ", struct task_struct, \ + thread.cp0_baduaddr); + offset("#define THREAD_ECODE ", struct task_struct, \ + thread.error_code); + offset("#define THREAD_TRAPNO ", struct task_struct, thread.trap_no); + offset("#define THREAD_MFLAGS ", struct task_struct, thread.mflags); + offset("#define THREAD_TRAMP ", struct task_struct, \ + thread.irix_trampoline); + offset("#define THREAD_OLDCTX ", struct task_struct, \ + thread.irix_oldctx); + linefeed; +} + +void output_thread_fpu_defines(void) +{ + offset("#define THREAD_FPR0 ", + struct task_struct, thread.fpu.hard.fpr[0]); + offset("#define THREAD_FPR1 ", + struct task_struct, thread.fpu.hard.fpr[1]); + offset("#define THREAD_FPR2 ", + struct task_struct, thread.fpu.hard.fpr[2]); + offset("#define THREAD_FPR3 ", + struct task_struct, thread.fpu.hard.fpr[3]); + offset("#define THREAD_FPR4 ", + struct task_struct, thread.fpu.hard.fpr[4]); + offset("#define THREAD_FPR5 ", + struct task_struct, thread.fpu.hard.fpr[5]); + offset("#define THREAD_FPR6 ", + struct task_struct, thread.fpu.hard.fpr[6]); + offset("#define THREAD_FPR7 ", + struct task_struct, thread.fpu.hard.fpr[7]); + offset("#define THREAD_FPR8 ", + struct task_struct, thread.fpu.hard.fpr[8]); + offset("#define THREAD_FPR9 ", + struct task_struct, thread.fpu.hard.fpr[9]); + offset("#define THREAD_FPR10 ", + struct task_struct, thread.fpu.hard.fpr[10]); + offset("#define THREAD_FPR11 ", + struct task_struct, thread.fpu.hard.fpr[11]); + offset("#define THREAD_FPR12 ", + struct task_struct, thread.fpu.hard.fpr[12]); + offset("#define THREAD_FPR13 ", + struct task_struct, thread.fpu.hard.fpr[13]); + offset("#define THREAD_FPR14 ", + struct task_struct, thread.fpu.hard.fpr[14]); + offset("#define THREAD_FPR15 ", + struct task_struct, thread.fpu.hard.fpr[15]); + offset("#define THREAD_FPR16 ", + struct task_struct, thread.fpu.hard.fpr[16]); + offset("#define THREAD_FPR17 ", + struct task_struct, thread.fpu.hard.fpr[17]); + offset("#define THREAD_FPR18 ", + struct task_struct, thread.fpu.hard.fpr[18]); + offset("#define THREAD_FPR19 ", + struct task_struct, thread.fpu.hard.fpr[19]); + offset("#define THREAD_FPR20 ", + struct task_struct, thread.fpu.hard.fpr[20]); + offset("#define THREAD_FPR21 ", + struct task_struct, thread.fpu.hard.fpr[21]); + offset("#define THREAD_FPR22 ", + struct task_struct, thread.fpu.hard.fpr[22]); + offset("#define THREAD_FPR23 ", + struct task_struct, thread.fpu.hard.fpr[23]); + offset("#define THREAD_FPR24 ", + struct task_struct, thread.fpu.hard.fpr[24]); + offset("#define THREAD_FPR25 ", + struct task_struct, thread.fpu.hard.fpr[25]); + offset("#define THREAD_FPR26 ", + struct task_struct, thread.fpu.hard.fpr[26]); + offset("#define THREAD_FPR27 ", + struct task_struct, thread.fpu.hard.fpr[27]); + offset("#define THREAD_FPR28 ", + struct task_struct, thread.fpu.hard.fpr[28]); + offset("#define THREAD_FPR29 ", + struct task_struct, thread.fpu.hard.fpr[29]); + offset("#define THREAD_FPR30 ", + struct task_struct, thread.fpu.hard.fpr[30]); + offset("#define THREAD_FPR31 ", + struct task_struct, thread.fpu.hard.fpr[31]); + + offset("#define THREAD_FCR31 ", + struct task_struct, thread.fpu.hard.fcr31); + linefeed; +} + +void output_mm_defines(void) +{ + text("/* Size of struct page */"); + size("#define STRUCT_PAGE_SIZE ", struct page); + linefeed; + text("/* Linux mm_struct offsets. */"); + offset("#define MM_USERS ", struct mm_struct, mm_users); + offset("#define MM_PGD ", struct mm_struct, pgd); + offset("#define MM_CONTEXT ", struct mm_struct, context); + linefeed; + constant("#define _PAGE_SIZE ", PAGE_SIZE); + constant("#define _PAGE_SHIFT ", PAGE_SHIFT); + linefeed; + constant("#define _PGD_T_SIZE ", sizeof(pgd_t)); + constant("#define _PMD_T_SIZE ", sizeof(pmd_t)); + constant("#define _PTE_T_SIZE ", sizeof(pte_t)); + linefeed; + constant("#define _PGD_T_LOG2 ", PGD_T_LOG2); + constant("#define _PMD_T_LOG2 ", PMD_T_LOG2); + constant("#define _PTE_T_LOG2 ", PTE_T_LOG2); + linefeed; + constant("#define _PMD_SHIFT ", PMD_SHIFT); + constant("#define _PGDIR_SHIFT ", PGDIR_SHIFT); + linefeed; + constant("#define _PGD_ORDER ", PGD_ORDER); + constant("#define _PMD_ORDER ", PMD_ORDER); + constant("#define _PTE_ORDER ", PTE_ORDER); + linefeed; + constant("#define _PTRS_PER_PGD ", PTRS_PER_PGD); + constant("#define _PTRS_PER_PMD ", PTRS_PER_PMD); + constant("#define _PTRS_PER_PTE ", PTRS_PER_PTE); + linefeed; +} + +void output_sc_defines(void) +{ + text("/* Linux sigcontext offsets. */"); + offset("#define SC_REGS ", struct sigcontext, sc_regs); + offset("#define SC_FPREGS ", struct sigcontext, sc_fpregs); + offset("#define SC_MDHI ", struct sigcontext, sc_mdhi); + offset("#define SC_MDLO ", struct sigcontext, sc_mdlo); + offset("#define SC_PC ", struct sigcontext, sc_pc); + offset("#define SC_STATUS ", struct sigcontext, sc_status); + offset("#define SC_FPC_CSR ", struct sigcontext, sc_fpc_csr); + offset("#define SC_FPC_EIR ", struct sigcontext, sc_fpc_eir); + offset("#define SC_CAUSE ", struct sigcontext, sc_cause); + offset("#define SC_BADVADDR ", struct sigcontext, sc_badvaddr); + linefeed; +} + +#ifdef CONFIG_MIPS32_COMPAT +void output_sc32_defines(void) +{ + text("/* Linux 32-bit sigcontext offsets. */"); + offset("#define SC32_FPREGS ", struct sigcontext32, sc_fpregs); + offset("#define SC32_FPC_CSR ", struct sigcontext32, sc_fpc_csr); + offset("#define SC32_FPC_EIR ", struct sigcontext32, sc_fpc_eir); + linefeed; +} +#endif + +void output_signal_defined(void) +{ + text("/* Linux signal numbers. */"); + constant("#define _SIGHUP ", SIGHUP); + constant("#define _SIGINT ", SIGINT); + constant("#define _SIGQUIT ", SIGQUIT); + constant("#define _SIGILL ", SIGILL); + constant("#define _SIGTRAP ", SIGTRAP); + constant("#define _SIGIOT ", SIGIOT); + constant("#define _SIGABRT ", SIGABRT); + constant("#define _SIGEMT ", SIGEMT); + constant("#define _SIGFPE ", SIGFPE); + constant("#define _SIGKILL ", SIGKILL); + constant("#define _SIGBUS ", SIGBUS); + constant("#define _SIGSEGV ", SIGSEGV); + constant("#define _SIGSYS ", SIGSYS); + constant("#define _SIGPIPE ", SIGPIPE); + constant("#define _SIGALRM ", SIGALRM); + constant("#define _SIGTERM ", SIGTERM); + constant("#define _SIGUSR1 ", SIGUSR1); + constant("#define _SIGUSR2 ", SIGUSR2); + constant("#define _SIGCHLD ", SIGCHLD); + constant("#define _SIGPWR ", SIGPWR); + constant("#define _SIGWINCH ", SIGWINCH); + constant("#define _SIGURG ", SIGURG); + constant("#define _SIGIO ", SIGIO); + constant("#define _SIGSTOP ", SIGSTOP); + constant("#define _SIGTSTP ", SIGTSTP); + constant("#define _SIGCONT ", SIGCONT); + constant("#define _SIGTTIN ", SIGTTIN); + constant("#define _SIGTTOU ", SIGTTOU); + constant("#define _SIGVTALRM ", SIGVTALRM); + constant("#define _SIGPROF ", SIGPROF); + constant("#define _SIGXCPU ", SIGXCPU); + constant("#define _SIGXFSZ ", SIGXFSZ); + linefeed; +} + +void output_irq_cpustat_t_defines(void) +{ + text("/* Linux irq_cpustat_t offsets. */"); + offset("#define IC_SOFTIRQ_PENDING ", irq_cpustat_t, __softirq_pending); + size("#define IC_IRQ_CPUSTAT_T ", irq_cpustat_t); + linefeed; +} diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c new file mode 100644 index 000000000000..0f159f30e894 --- /dev/null +++ b/arch/mips/kernel/proc.c @@ -0,0 +1,149 @@ +/* + * linux/arch/mips/kernel/proc.c + * + * Copyright (C) 1995, 1996, 2001 Ralf Baechle + * Copyright (C) 2001 MIPS Technologies, Inc. + */ +#include <linux/config.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/seq_file.h> +#include <asm/bootinfo.h> +#include <asm/cpu.h> +#include <asm/cpu-features.h> +#include <asm/mipsregs.h> +#include <asm/processor.h> +#include <asm/watch.h> + +unsigned int vced_count, vcei_count; + +static const char *cpu_name[] = { + [CPU_UNKNOWN] "unknown", + [CPU_R2000] "R2000", + [CPU_R3000] "R3000", + [CPU_R3000A] "R3000A", + [CPU_R3041] "R3041", + [CPU_R3051] "R3051", + [CPU_R3052] "R3052", + [CPU_R3081] "R3081", + [CPU_R3081E] "R3081E", + [CPU_R4000PC] "R4000PC", + [CPU_R4000SC] "R4000SC", + [CPU_R4000MC] "R4000MC", + [CPU_R4200] "R4200", + [CPU_R4400PC] "R4400PC", + [CPU_R4400SC] "R4400SC", + [CPU_R4400MC] "R4400MC", + [CPU_R4600] "R4600", + [CPU_R6000] "R6000", + [CPU_R6000A] "R6000A", + [CPU_R8000] "R8000", + [CPU_R10000] "R10000", + [CPU_R12000] "R12000", + [CPU_R4300] "R4300", + [CPU_R4650] "R4650", + [CPU_R4700] "R4700", + [CPU_R5000] "R5000", + [CPU_R5000A] "R5000A", + [CPU_R4640] "R4640", + [CPU_NEVADA] "Nevada", + [CPU_RM7000] "RM7000", + [CPU_RM9000] "RM9000", + [CPU_R5432] "R5432", + [CPU_4KC] "MIPS 4Kc", + [CPU_5KC] "MIPS 5Kc", + [CPU_R4310] "R4310", + [CPU_SB1] "SiByte SB1", + [CPU_TX3912] "TX3912", + [CPU_TX3922] "TX3922", + [CPU_TX3927] "TX3927", + [CPU_AU1000] "Au1000", + [CPU_AU1500] "Au1500", + [CPU_4KEC] "MIPS 4KEc", + [CPU_4KSC] "MIPS 4KSc", + [CPU_VR41XX] "NEC Vr41xx", + [CPU_R5500] "R5500", + [CPU_TX49XX] "TX49xx", + [CPU_20KC] "MIPS 20Kc", + [CPU_24K] "MIPS 24K", + [CPU_25KF] "MIPS 25Kf", + [CPU_VR4111] "NEC VR4111", + [CPU_VR4121] "NEC VR4121", + [CPU_VR4122] "NEC VR4122", + [CPU_VR4131] "NEC VR4131", + [CPU_VR4133] "NEC VR4133", + [CPU_VR4181] "NEC VR4181", + [CPU_VR4181A] "NEC VR4181A", + [CPU_SR71000] "Sandcraft SR71000" +}; + + +static int show_cpuinfo(struct seq_file *m, void *v) +{ + unsigned int version = current_cpu_data.processor_id; + unsigned int fp_vers = current_cpu_data.fpu_id; + unsigned long n = (unsigned long) v - 1; + char fmt [64]; + +#ifdef CONFIG_SMP + if (!cpu_isset(n, cpu_online_map)) + return 0; +#endif + + /* + * For the first processor also print the system type + */ + if (n == 0) + seq_printf(m, "system type\t\t: %s\n", get_system_type()); + + seq_printf(m, "processor\t\t: %ld\n", n); + sprintf(fmt, "cpu model\t\t: %%s V%%d.%%d%s\n", + cpu_has_fpu ? " FPU V%d.%d" : ""); + seq_printf(m, fmt, cpu_name[current_cpu_data.cputype <= CPU_LAST ? + current_cpu_data.cputype : CPU_UNKNOWN], + (version >> 4) & 0x0f, version & 0x0f, + (fp_vers >> 4) & 0x0f, fp_vers & 0x0f); + seq_printf(m, "BogoMIPS\t\t: %lu.%02lu\n", + loops_per_jiffy / (500000/HZ), + (loops_per_jiffy / (5000/HZ)) % 100); + seq_printf(m, "wait instruction\t: %s\n", cpu_wait ? "yes" : "no"); + seq_printf(m, "microsecond timers\t: %s\n", + cpu_has_counter ? "yes" : "no"); + seq_printf(m, "tlb_entries\t\t: %d\n", current_cpu_data.tlbsize); + seq_printf(m, "extra interrupt vector\t: %s\n", + cpu_has_divec ? "yes" : "no"); + seq_printf(m, "hardware watchpoint\t: %s\n", + cpu_has_watch ? "yes" : "no"); + + sprintf(fmt, "VCE%%c exceptions\t\t: %s\n", + cpu_has_vce ? "%u" : "not available"); + seq_printf(m, fmt, 'D', vced_count); + seq_printf(m, fmt, 'I', vcei_count); + + return 0; +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + unsigned long i = *pos; + + return i < NR_CPUS ? (void *) (i + 1) : NULL; +} + +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return c_start(m, pos); +} + +static void c_stop(struct seq_file *m, void *v) +{ +} + +struct seq_operations cpuinfo_op = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = show_cpuinfo, +}; diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c new file mode 100644 index 000000000000..6e70c42c2058 --- /dev/null +++ b/arch/mips/kernel/process.c @@ -0,0 +1,364 @@ +/* + * 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 - 1999, 2000 by Ralf Baechle and others. + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * Copyright (C) 2004 Thiemo Seufer + */ +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/mman.h> +#include <linux/personality.h> +#include <linux/sys.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/init.h> +#include <linux/completion.h> + +#include <asm/bootinfo.h> +#include <asm/cpu.h> +#include <asm/fpu.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/mipsregs.h> +#include <asm/processor.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/elf.h> +#include <asm/isadep.h> +#include <asm/inst.h> + +/* + * We use this if we don't have any better idle routine.. + * (This to kill: kernel/platform.c. + */ +void default_idle (void) +{ +} + +/* + * The idle thread. There's no useful work to be done, so just try to conserve + * power and have a low exit latency (ie sit in a loop waiting for somebody to + * say that they'd like to reschedule) + */ +ATTRIB_NORET void cpu_idle(void) +{ + /* endless idle loop with no priority at all */ + while (1) { + while (!need_resched()) + if (cpu_wait) + (*cpu_wait)(); + schedule(); + } +} + +asmlinkage void ret_from_fork(void); + +void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp) +{ + unsigned long status; + + /* New thread loses kernel privileges. */ + status = regs->cp0_status & ~(ST0_CU0|ST0_CU1|KU_MASK); +#ifdef CONFIG_MIPS64 + status &= ~ST0_FR; + status |= (current->thread.mflags & MF_32BIT_REGS) ? 0 : ST0_FR; +#endif + status |= KU_USER; + regs->cp0_status = status; + clear_used_math(); + lose_fpu(); + regs->cp0_epc = pc; + regs->regs[29] = sp; + current_thread_info()->addr_limit = USER_DS; +} + +void exit_thread(void) +{ +} + +void flush_thread(void) +{ +} + +int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, + unsigned long unused, struct task_struct *p, struct pt_regs *regs) +{ + struct thread_info *ti = p->thread_info; + struct pt_regs *childregs; + long childksp; + + childksp = (unsigned long)ti + THREAD_SIZE - 32; + + preempt_disable(); + + if (is_fpu_owner()) { + save_fp(p); + } + + preempt_enable(); + + /* set up new TSS. */ + childregs = (struct pt_regs *) childksp - 1; + *childregs = *regs; + childregs->regs[7] = 0; /* Clear error flag */ + +#if defined(CONFIG_BINFMT_IRIX) + if (current->personality != PER_LINUX) { + /* Under IRIX things are a little different. */ + childregs->regs[3] = 1; + regs->regs[3] = 0; + } +#endif + childregs->regs[2] = 0; /* Child gets zero as return value */ + regs->regs[2] = p->pid; + + if (childregs->cp0_status & ST0_CU0) { + childregs->regs[28] = (unsigned long) ti; + childregs->regs[29] = childksp; + ti->addr_limit = KERNEL_DS; + } else { + childregs->regs[29] = usp; + ti->addr_limit = USER_DS; + } + p->thread.reg29 = (unsigned long) childregs; + p->thread.reg31 = (unsigned long) ret_from_fork; + + /* + * New tasks lose permission to use the fpu. This accelerates context + * switching for most programs since they don't use the fpu. + */ + p->thread.cp0_status = read_c0_status() & ~(ST0_CU2|ST0_CU1); + childregs->cp0_status &= ~(ST0_CU2|ST0_CU1); + clear_tsk_thread_flag(p, TIF_USEDFPU); + + return 0; +} + +/* Fill in the fpu structure for a core dump.. */ +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *r) +{ + memcpy(r, ¤t->thread.fpu, sizeof(current->thread.fpu)); + + return 1; +} + +void dump_regs(elf_greg_t *gp, struct pt_regs *regs) +{ + int i; + + for (i = 0; i < EF_R0; i++) + gp[i] = 0; + gp[EF_R0] = 0; + for (i = 1; i <= 31; i++) + gp[EF_R0 + i] = regs->regs[i]; + gp[EF_R26] = 0; + gp[EF_R27] = 0; + gp[EF_LO] = regs->lo; + gp[EF_HI] = regs->hi; + gp[EF_CP0_EPC] = regs->cp0_epc; + gp[EF_CP0_BADVADDR] = regs->cp0_badvaddr; + gp[EF_CP0_STATUS] = regs->cp0_status; + gp[EF_CP0_CAUSE] = regs->cp0_cause; +#ifdef EF_UNUSED0 + gp[EF_UNUSED0] = 0; +#endif +} + +int dump_task_fpu (struct task_struct *t, elf_fpregset_t *fpr) +{ + memcpy(fpr, &t->thread.fpu, sizeof(current->thread.fpu)); + + return 1; +} + +/* + * Create a kernel thread + */ +ATTRIB_NORET void kernel_thread_helper(void *arg, int (*fn)(void *)) +{ + do_exit(fn(arg)); +} + +long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) +{ + struct pt_regs regs; + + memset(®s, 0, sizeof(regs)); + + regs.regs[4] = (unsigned long) arg; + regs.regs[5] = (unsigned long) fn; + regs.cp0_epc = (unsigned long) kernel_thread_helper; + regs.cp0_status = read_c0_status(); +#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) + regs.cp0_status &= ~(ST0_KUP | ST0_IEC); + regs.cp0_status |= ST0_IEP; +#else + regs.cp0_status |= ST0_EXL; +#endif + + /* Ok, create the new process.. */ + return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); +} + +struct mips_frame_info { + int frame_offset; + int pc_offset; +}; +static struct mips_frame_info schedule_frame; +static struct mips_frame_info schedule_timeout_frame; +static struct mips_frame_info sleep_on_frame; +static struct mips_frame_info sleep_on_timeout_frame; +static struct mips_frame_info wait_for_completion_frame; +static int mips_frame_info_initialized; +static int __init get_frame_info(struct mips_frame_info *info, void *func) +{ + int i; + union mips_instruction *ip = (union mips_instruction *)func; + info->pc_offset = -1; + info->frame_offset = -1; + for (i = 0; i < 128; i++, ip++) { + /* if jal, jalr, jr, stop. */ + if (ip->j_format.opcode == jal_op || + (ip->r_format.opcode == spec_op && + (ip->r_format.func == jalr_op || + ip->r_format.func == jr_op))) + break; + + if ( +#ifdef CONFIG_MIPS32 + ip->i_format.opcode == sw_op && +#endif +#ifdef CONFIG_MIPS64 + ip->i_format.opcode == sd_op && +#endif + ip->i_format.rs == 29) + { + /* sw / sd $ra, offset($sp) */ + if (ip->i_format.rt == 31) { + if (info->pc_offset != -1) + break; + info->pc_offset = + ip->i_format.simmediate / sizeof(long); + } + /* sw / sd $s8, offset($sp) */ + if (ip->i_format.rt == 30) { + if (info->frame_offset != -1) + break; + info->frame_offset = + ip->i_format.simmediate / sizeof(long); + } + } + } + if (info->pc_offset == -1 || info->frame_offset == -1) { + printk("Can't analyze prologue code at %p\n", func); + info->pc_offset = -1; + info->frame_offset = -1; + return -1; + } + + return 0; +} + +static int __init frame_info_init(void) +{ + mips_frame_info_initialized = + !get_frame_info(&schedule_frame, schedule) && + !get_frame_info(&schedule_timeout_frame, schedule_timeout) && + !get_frame_info(&sleep_on_frame, sleep_on) && + !get_frame_info(&sleep_on_timeout_frame, sleep_on_timeout) && + !get_frame_info(&wait_for_completion_frame, wait_for_completion); + + return 0; +} + +arch_initcall(frame_info_init); + +/* + * Return saved PC of a blocked thread. + */ +unsigned long thread_saved_pc(struct task_struct *tsk) +{ + struct thread_struct *t = &tsk->thread; + + /* New born processes are a special case */ + if (t->reg31 == (unsigned long) ret_from_fork) + return t->reg31; + + if (schedule_frame.pc_offset < 0) + return 0; + return ((unsigned long *)t->reg29)[schedule_frame.pc_offset]; +} + +/* get_wchan - a maintenance nightmare^W^Wpain in the ass ... */ +unsigned long get_wchan(struct task_struct *p) +{ + unsigned long frame, pc; + + if (!p || p == current || p->state == TASK_RUNNING) + return 0; + + if (!mips_frame_info_initialized) + return 0; + pc = thread_saved_pc(p); + if (!in_sched_functions(pc)) + goto out; + + if (pc >= (unsigned long) sleep_on_timeout) + goto schedule_timeout_caller; + if (pc >= (unsigned long) sleep_on) + goto schedule_caller; + if (pc >= (unsigned long) interruptible_sleep_on_timeout) + goto schedule_timeout_caller; + if (pc >= (unsigned long)interruptible_sleep_on) + goto schedule_caller; + if (pc >= (unsigned long)wait_for_completion) + goto schedule_caller; + goto schedule_timeout_caller; + +schedule_caller: + frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset]; + if (pc >= (unsigned long) sleep_on) + pc = ((unsigned long *)frame)[sleep_on_frame.pc_offset]; + else + pc = ((unsigned long *)frame)[wait_for_completion_frame.pc_offset]; + goto out; + +schedule_timeout_caller: + /* + * The schedule_timeout frame + */ + frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset]; + + /* + * frame now points to sleep_on_timeout's frame + */ + pc = ((unsigned long *)frame)[schedule_timeout_frame.pc_offset]; + + if (in_sched_functions(pc)) { + /* schedule_timeout called by [interruptible_]sleep_on_timeout */ + frame = ((unsigned long *)frame)[schedule_timeout_frame.frame_offset]; + pc = ((unsigned long *)frame)[sleep_on_timeout_frame.pc_offset]; + } + +out: + +#ifdef CONFIG_MIPS64 + if (current->thread.mflags & MF_32BIT_REGS) /* Kludge for 32-bit ps */ + pc &= 0xffffffffUL; +#endif + + return pc; +} + +EXPORT_SYMBOL(get_wchan); diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c new file mode 100644 index 000000000000..a166954a70b3 --- /dev/null +++ b/arch/mips/kernel/ptrace.c @@ -0,0 +1,338 @@ +/* + * 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) 1992 Ross Biro + * Copyright (C) Linus Torvalds + * Copyright (C) 1994, 95, 96, 97, 98, 2000 Ralf Baechle + * Copyright (C) 1996 David S. Miller + * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com + * Copyright (C) 1999 MIPS Technologies, Inc. + * Copyright (C) 2000 Ulf Carlsson + * + * At this time Linux/MIPS64 only supports syscall tracing, even for 32-bit + * binaries. + */ +#include <linux/config.h> +#include <linux/compiler.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/audit.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/user.h> +#include <linux/security.h> +#include <linux/audit.h> + +#include <asm/cpu.h> +#include <asm/fpu.h> +#include <asm/mipsregs.h> +#include <asm/pgtable.h> +#include <asm/page.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/bootinfo.h> + +/* + * Called by kernel/ptrace.c when detaching.. + * + * Make sure single step bits etc are not set. + */ +void ptrace_disable(struct task_struct *child) +{ + /* Nothing to do.. */ +} + +asmlinkage int sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int ret; + +#if 0 + printk("ptrace(r=%d,pid=%d,addr=%08lx,data=%08lx)\n", + (int) request, (int) pid, (unsigned long) addr, + (unsigned long) data); +#endif + lock_kernel(); + ret = -EPERM; + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->ptrace & PT_PTRACED) + goto out; + if ((ret = security_ptrace(current->parent, current))) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; + } + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (!child) + goto out; + + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out_tsk; + + if (request == PTRACE_ATTACH) { + ret = ptrace_attach(child); + goto out_tsk; + } + + ret = ptrace_check_attach(child, request == PTRACE_KILL); + if (ret < 0) + goto out_tsk; + + switch (request) { + /* when I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: { + unsigned long tmp; + int copied; + + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + if (copied != sizeof(tmp)) + break; + ret = put_user(tmp,(unsigned long *) data); + break; + } + + /* Read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: { + struct pt_regs *regs; + unsigned long tmp = 0; + + regs = (struct pt_regs *) ((unsigned long) child->thread_info + + THREAD_SIZE - 32 - sizeof(struct pt_regs)); + ret = 0; /* Default return value. */ + + switch (addr) { + case 0 ... 31: + tmp = regs->regs[addr]; + break; + case FPR_BASE ... FPR_BASE + 31: + if (tsk_used_math(child)) { + fpureg_t *fregs = get_fpu_regs(child); + +#ifdef CONFIG_MIPS32 + /* + * The odd registers are actually the high + * order bits of the values stored in the even + * registers - unless we're using r2k_switch.S. + */ + if (addr & 1) + tmp = (unsigned long) (fregs[((addr & ~1) - 32)] >> 32); + else + tmp = (unsigned long) (fregs[(addr - 32)] & 0xffffffff); +#endif +#ifdef CONFIG_MIPS64 + tmp = fregs[addr - FPR_BASE]; +#endif + } else { + tmp = -1; /* FP not yet used */ + } + break; + case PC: + tmp = regs->cp0_epc; + break; + case CAUSE: + tmp = regs->cp0_cause; + break; + case BADVADDR: + tmp = regs->cp0_badvaddr; + break; + case MMHI: + tmp = regs->hi; + break; + case MMLO: + tmp = regs->lo; + break; + case FPC_CSR: + if (cpu_has_fpu) + tmp = child->thread.fpu.hard.fcr31; + else + tmp = child->thread.fpu.soft.fcr31; + break; + case FPC_EIR: { /* implementation / version register */ + unsigned int flags; + + if (!cpu_has_fpu) + break; + + flags = read_c0_status(); + __enable_fpu(); + __asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp)); + write_c0_status(flags); + break; + } + default: + tmp = 0; + ret = -EIO; + goto out_tsk; + } + ret = put_user(tmp, (unsigned long *) data); + break; + } + + /* when I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + ret = 0; + if (access_process_vm(child, addr, &data, sizeof(data), 1) + == sizeof(data)) + break; + ret = -EIO; + break; + + case PTRACE_POKEUSR: { + struct pt_regs *regs; + ret = 0; + regs = (struct pt_regs *) ((unsigned long) child->thread_info + + THREAD_SIZE - 32 - sizeof(struct pt_regs)); + + switch (addr) { + case 0 ... 31: + regs->regs[addr] = data; + break; + case FPR_BASE ... FPR_BASE + 31: { + fpureg_t *fregs = get_fpu_regs(child); + + if (!tsk_used_math(child)) { + /* FP not yet used */ + memset(&child->thread.fpu.hard, ~0, + sizeof(child->thread.fpu.hard)); + child->thread.fpu.hard.fcr31 = 0; + } +#ifdef CONFIG_MIPS32 + /* + * The odd registers are actually the high order bits + * of the values stored in the even registers - unless + * we're using r2k_switch.S. + */ + if (addr & 1) { + fregs[(addr & ~1) - FPR_BASE] &= 0xffffffff; + fregs[(addr & ~1) - FPR_BASE] |= ((unsigned long long) data) << 32; + } else { + fregs[addr - FPR_BASE] &= ~0xffffffffLL; + fregs[addr - FPR_BASE] |= data; + } +#endif +#ifdef CONFIG_MIPS64 + fregs[addr - FPR_BASE] = data; +#endif + break; + } + case PC: + regs->cp0_epc = data; + break; + case MMHI: + regs->hi = data; + break; + case MMLO: + regs->lo = data; + break; + case FPC_CSR: + if (cpu_has_fpu) + child->thread.fpu.hard.fcr31 = data; + else + child->thread.fpu.soft.fcr31 = data; + break; + default: + /* The rest are not allowed. */ + ret = -EIO; + break; + } + break; + } + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: { /* restart after signal. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + if (request == PTRACE_SYSCALL) { + set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + } + else { + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + } + child->exit_code = data; + wake_up_process(child); + ret = 0; + break; + } + + /* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: + ret = 0; + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + wake_up_process(child); + break; + + case PTRACE_DETACH: /* detach a process that was attached. */ + ret = ptrace_detach(child, data); + break; + + default: + ret = ptrace_request(child, request, addr, data); + break; + } + +out_tsk: + put_task_struct(child); +out: + unlock_kernel(); + return ret; +} + +/* + * Notification of system call entry/exit + * - triggered by current->work.syscall_trace + */ +asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit) +{ + if (unlikely(current->audit_context)) { + if (!entryexit) + audit_syscall_entry(current, regs->regs[2], + regs->regs[4], regs->regs[5], + regs->regs[6], regs->regs[7]); + else + audit_syscall_exit(current, regs->regs[2]); + } + + if (!test_thread_flag(TIF_SYSCALL_TRACE)) + return; + if (!(current->ptrace & PT_PTRACED)) + return; + + /* The 0x80 provides a way for the tracing parent to distinguish + between a syscall stop and SIGTRAP delivery */ + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? + 0x80 : 0)); + + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} diff --git a/arch/mips/kernel/ptrace32.c b/arch/mips/kernel/ptrace32.c new file mode 100644 index 000000000000..611dee919d50 --- /dev/null +++ b/arch/mips/kernel/ptrace32.c @@ -0,0 +1,285 @@ +/* + * 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) 1992 Ross Biro + * Copyright (C) Linus Torvalds + * Copyright (C) 1994, 95, 96, 97, 98, 2000 Ralf Baechle + * Copyright (C) 1996 David S. Miller + * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com + * Copyright (C) 1999 MIPS Technologies, Inc. + * Copyright (C) 2000 Ulf Carlsson + * + * At this time Linux/MIPS64 only supports syscall tracing, even for 32-bit + * binaries. + */ +#include <linux/compiler.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/user.h> +#include <linux/security.h> + +#include <asm/cpu.h> +#include <asm/fpu.h> +#include <asm/mipsregs.h> +#include <asm/pgtable.h> +#include <asm/page.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/bootinfo.h> + +/* + * Tracing a 32-bit process with a 64-bit strace and vice versa will not + * work. I don't know how to fix this. + */ +asmlinkage int sys32_ptrace(int request, int pid, int addr, int data) +{ + struct task_struct *child; + int ret; + +#if 0 + printk("ptrace(r=%d,pid=%d,addr=%08lx,data=%08lx)\n", + (int) request, (int) pid, (unsigned long) addr, + (unsigned long) data); +#endif + lock_kernel(); + ret = -EPERM; + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->ptrace & PT_PTRACED) + goto out; + if ((ret = security_ptrace(current->parent, current))) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; + } + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (!child) + goto out; + + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out_tsk; + + if (request == PTRACE_ATTACH) { + ret = ptrace_attach(child); + goto out_tsk; + } + + ret = ptrace_check_attach(child, request == PTRACE_KILL); + if (ret < 0) + goto out_tsk; + + switch (request) { + /* when I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: { + unsigned int tmp; + int copied; + + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + if (copied != sizeof(tmp)) + break; + ret = put_user(tmp, (unsigned int *) (unsigned long) data); + break; + } + + /* Read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: { + struct pt_regs *regs; + unsigned int tmp; + + regs = (struct pt_regs *) ((unsigned long) child->thread_info + + THREAD_SIZE - 32 - sizeof(struct pt_regs)); + ret = 0; /* Default return value. */ + + switch (addr) { + case 0 ... 31: + tmp = regs->regs[addr]; + break; + case FPR_BASE ... FPR_BASE + 31: + if (tsk_used_math(child)) { + fpureg_t *fregs = get_fpu_regs(child); + + /* + * The odd registers are actually the high + * order bits of the values stored in the even + * registers - unless we're using r2k_switch.S. + */ + if (addr & 1) + tmp = (unsigned long) (fregs[((addr & ~1) - 32)] >> 32); + else + tmp = (unsigned long) (fregs[(addr - 32)] & 0xffffffff); + } else { + tmp = -1; /* FP not yet used */ + } + break; + case PC: + tmp = regs->cp0_epc; + break; + case CAUSE: + tmp = regs->cp0_cause; + break; + case BADVADDR: + tmp = regs->cp0_badvaddr; + break; + case MMHI: + tmp = regs->hi; + break; + case MMLO: + tmp = regs->lo; + break; + case FPC_CSR: + if (cpu_has_fpu) + tmp = child->thread.fpu.hard.fcr31; + else + tmp = child->thread.fpu.soft.fcr31; + break; + case FPC_EIR: { /* implementation / version register */ + unsigned int flags; + + if (!cpu_has_fpu) + break; + + flags = read_c0_status(); + __enable_fpu(); + __asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp)); + write_c0_status(flags); + break; + } + default: + tmp = 0; + ret = -EIO; + goto out_tsk; + } + ret = put_user(tmp, (unsigned *) (unsigned long) data); + break; + } + + /* when I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + ret = 0; + if (access_process_vm(child, addr, &data, sizeof(data), 1) + == sizeof(data)) + break; + ret = -EIO; + break; + + case PTRACE_POKEUSR: { + struct pt_regs *regs; + ret = 0; + regs = (struct pt_regs *) ((unsigned long) child->thread_info + + THREAD_SIZE - 32 - sizeof(struct pt_regs)); + + switch (addr) { + case 0 ... 31: + regs->regs[addr] = data; + break; + case FPR_BASE ... FPR_BASE + 31: { + fpureg_t *fregs = get_fpu_regs(child); + + if (!tsk_used_math(child)) { + /* FP not yet used */ + memset(&child->thread.fpu.hard, ~0, + sizeof(child->thread.fpu.hard)); + child->thread.fpu.hard.fcr31 = 0; + } + /* + * The odd registers are actually the high order bits + * of the values stored in the even registers - unless + * we're using r2k_switch.S. + */ + if (addr & 1) { + fregs[(addr & ~1) - FPR_BASE] &= 0xffffffff; + fregs[(addr & ~1) - FPR_BASE] |= ((unsigned long long) data) << 32; + } else { + fregs[addr - FPR_BASE] &= ~0xffffffffLL; + /* Must cast, lest sign extension fill upper + bits! */ + fregs[addr - FPR_BASE] |= (unsigned int)data; + } + break; + } + case PC: + regs->cp0_epc = data; + break; + case MMHI: + regs->hi = data; + break; + case MMLO: + regs->lo = data; + break; + case FPC_CSR: + if (cpu_has_fpu) + child->thread.fpu.hard.fcr31 = data; + else + child->thread.fpu.soft.fcr31 = data; + break; + default: + /* The rest are not allowed. */ + ret = -EIO; + break; + } + break; + } + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: { /* restart after signal. */ + ret = -EIO; + if ((unsigned int) data > _NSIG) + break; + if (request == PTRACE_SYSCALL) { + set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + } + else { + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + } + child->exit_code = data; + wake_up_process(child); + ret = 0; + break; + } + + /* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: + ret = 0; + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + wake_up_process(child); + break; + + case PTRACE_DETACH: /* detach a process that was attached. */ + ret = ptrace_detach(child, data); + break; + + default: + ret = ptrace_request(child, request, addr, data); + break; + } + +out_tsk: + put_task_struct(child); +out: + unlock_kernel(); + return ret; +} diff --git a/arch/mips/kernel/r2300_fpu.S b/arch/mips/kernel/r2300_fpu.S new file mode 100644 index 000000000000..f83c31f720c4 --- /dev/null +++ b/arch/mips/kernel/r2300_fpu.S @@ -0,0 +1,126 @@ +/* + * 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, 1998 by Ralf Baechle + * + * Multi-arch abstraction and asm macros for easier reading: + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * Further modifications to make this work: + * Copyright (c) 1998 Harald Koerfgen + */ +#include <asm/asm.h> +#include <asm/errno.h> +#include <asm/fpregdef.h> +#include <asm/mipsregs.h> +#include <asm/offset.h> +#include <asm/regdef.h> + +#define EX(a,b) \ +9: a,##b; \ + .section __ex_table,"a"; \ + PTR 9b,bad_stack; \ + .previous + + .set noreorder + .set mips1 + /* Save floating point context */ +LEAF(_save_fp_context) + li v0, 0 # assume success + cfc1 t1,fcr31 + EX(swc1 $f0,(SC_FPREGS+0)(a0)) + EX(swc1 $f1,(SC_FPREGS+8)(a0)) + EX(swc1 $f2,(SC_FPREGS+16)(a0)) + EX(swc1 $f3,(SC_FPREGS+24)(a0)) + EX(swc1 $f4,(SC_FPREGS+32)(a0)) + EX(swc1 $f5,(SC_FPREGS+40)(a0)) + EX(swc1 $f6,(SC_FPREGS+48)(a0)) + EX(swc1 $f7,(SC_FPREGS+56)(a0)) + EX(swc1 $f8,(SC_FPREGS+64)(a0)) + EX(swc1 $f9,(SC_FPREGS+72)(a0)) + EX(swc1 $f10,(SC_FPREGS+80)(a0)) + EX(swc1 $f11,(SC_FPREGS+88)(a0)) + EX(swc1 $f12,(SC_FPREGS+96)(a0)) + EX(swc1 $f13,(SC_FPREGS+104)(a0)) + EX(swc1 $f14,(SC_FPREGS+112)(a0)) + EX(swc1 $f15,(SC_FPREGS+120)(a0)) + EX(swc1 $f16,(SC_FPREGS+128)(a0)) + EX(swc1 $f17,(SC_FPREGS+136)(a0)) + EX(swc1 $f18,(SC_FPREGS+144)(a0)) + EX(swc1 $f19,(SC_FPREGS+152)(a0)) + EX(swc1 $f20,(SC_FPREGS+160)(a0)) + EX(swc1 $f21,(SC_FPREGS+168)(a0)) + EX(swc1 $f22,(SC_FPREGS+176)(a0)) + EX(swc1 $f23,(SC_FPREGS+184)(a0)) + EX(swc1 $f24,(SC_FPREGS+192)(a0)) + EX(swc1 $f25,(SC_FPREGS+200)(a0)) + EX(swc1 $f26,(SC_FPREGS+208)(a0)) + EX(swc1 $f27,(SC_FPREGS+216)(a0)) + EX(swc1 $f28,(SC_FPREGS+224)(a0)) + EX(swc1 $f29,(SC_FPREGS+232)(a0)) + EX(swc1 $f30,(SC_FPREGS+240)(a0)) + EX(swc1 $f31,(SC_FPREGS+248)(a0)) + EX(sw t1,(SC_FPC_CSR)(a0)) + cfc1 t0,$0 # implementation/version + jr ra + .set nomacro + EX(sw t0,(SC_FPC_EIR)(a0)) + .set macro + END(_save_fp_context) + +/* + * Restore FPU state: + * - fp gp registers + * - cp1 status/control register + * + * We base the decision which registers to restore from the signal stack + * frame on the current content of c0_status, not on the content of the + * stack frame which might have been changed by the user. + */ +LEAF(_restore_fp_context) + li v0, 0 # assume success + EX(lw t0,(SC_FPC_CSR)(a0)) + EX(lwc1 $f0,(SC_FPREGS+0)(a0)) + EX(lwc1 $f1,(SC_FPREGS+8)(a0)) + EX(lwc1 $f2,(SC_FPREGS+16)(a0)) + EX(lwc1 $f3,(SC_FPREGS+24)(a0)) + EX(lwc1 $f4,(SC_FPREGS+32)(a0)) + EX(lwc1 $f5,(SC_FPREGS+40)(a0)) + EX(lwc1 $f6,(SC_FPREGS+48)(a0)) + EX(lwc1 $f7,(SC_FPREGS+56)(a0)) + EX(lwc1 $f8,(SC_FPREGS+64)(a0)) + EX(lwc1 $f9,(SC_FPREGS+72)(a0)) + EX(lwc1 $f10,(SC_FPREGS+80)(a0)) + EX(lwc1 $f11,(SC_FPREGS+88)(a0)) + EX(lwc1 $f12,(SC_FPREGS+96)(a0)) + EX(lwc1 $f13,(SC_FPREGS+104)(a0)) + EX(lwc1 $f14,(SC_FPREGS+112)(a0)) + EX(lwc1 $f15,(SC_FPREGS+120)(a0)) + EX(lwc1 $f16,(SC_FPREGS+128)(a0)) + EX(lwc1 $f17,(SC_FPREGS+136)(a0)) + EX(lwc1 $f18,(SC_FPREGS+144)(a0)) + EX(lwc1 $f19,(SC_FPREGS+152)(a0)) + EX(lwc1 $f20,(SC_FPREGS+160)(a0)) + EX(lwc1 $f21,(SC_FPREGS+168)(a0)) + EX(lwc1 $f22,(SC_FPREGS+176)(a0)) + EX(lwc1 $f23,(SC_FPREGS+184)(a0)) + EX(lwc1 $f24,(SC_FPREGS+192)(a0)) + EX(lwc1 $f25,(SC_FPREGS+200)(a0)) + EX(lwc1 $f26,(SC_FPREGS+208)(a0)) + EX(lwc1 $f27,(SC_FPREGS+216)(a0)) + EX(lwc1 $f28,(SC_FPREGS+224)(a0)) + EX(lwc1 $f29,(SC_FPREGS+232)(a0)) + EX(lwc1 $f30,(SC_FPREGS+240)(a0)) + EX(lwc1 $f31,(SC_FPREGS+248)(a0)) + jr ra + ctc1 t0,fcr31 + END(_restore_fp_context) + .set reorder + + .type fault@function + .ent fault +fault: li v0, -EFAULT + jr ra + .end fault diff --git a/arch/mips/kernel/r2300_switch.S b/arch/mips/kernel/r2300_switch.S new file mode 100644 index 000000000000..243e7b629af6 --- /dev/null +++ b/arch/mips/kernel/r2300_switch.S @@ -0,0 +1,174 @@ +/* + * r2300_switch.S: R2300 specific task switching code. + * + * Copyright (C) 1994, 1995, 1996, 1999 by Ralf Baechle + * Copyright (C) 1994, 1995, 1996 by Andreas Busse + * + * Multi-cpu abstraction and macros for easier reading: + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * Further modifications to make this work: + * Copyright (c) 1998-2000 Harald Koerfgen + */ +#include <linux/config.h> +#include <asm/asm.h> +#include <asm/cachectl.h> +#include <asm/fpregdef.h> +#include <asm/mipsregs.h> +#include <asm/offset.h> +#include <asm/page.h> +#include <asm/regdef.h> +#include <asm/stackframe.h> +#include <asm/thread_info.h> + +#include <asm/asmmacro.h> + + .set mips1 + .align 5 + +/* + * Offset to the current process status flags, the first 32 bytes of the + * stack are not used. + */ +#define ST_OFF (_THREAD_SIZE - 32 - PT_SIZE + PT_STATUS) + +/* + * FPU context is saved iff the process has used it's FPU in the current + * time slice as indicated by TIF_USEDFPU. In any case, the CU1 bit for user + * space STATUS register should be 0, so that a process *always* starts its + * userland with FPU disabled after each context switch. + * + * FPU will be enabled as soon as the process accesses FPU again, through + * do_cpu() trap. + */ + +/* + * task_struct *resume(task_struct *prev, task_struct *next, + * struct thread_info *next_ti) ) + */ +LEAF(resume) +#ifndef CONFIG_CPU_HAS_LLSC + sw zero, ll_bit +#endif + mfc0 t1, CP0_STATUS + sw t1, THREAD_STATUS(a0) + cpu_save_nonscratch a0 + sw ra, THREAD_REG31(a0) + + /* + * check if we need to save FPU registers + */ + lw t3, TASK_THREAD_INFO(a0) + lw t0, TI_FLAGS(t3) + li t1, _TIF_USEDFPU + and t2, t0, t1 + beqz t2, 1f + nor t1, zero, t1 + + and t0, t0, t1 + sw t0, TI_FLAGS(t3) + + /* + * clear saved user stack CU1 bit + */ + lw t0, ST_OFF(t3) + li t1, ~ST0_CU1 + and t0, t0, t1 + sw t0, ST_OFF(t3) + + fpu_save_single a0, t0 # clobbers t0 + +1: + /* + * The order of restoring the registers takes care of the race + * updating $28, $29 and kernelsp without disabling ints. + */ + move $28, a2 + cpu_restore_nonscratch a1 + + addiu t1, $28, _THREAD_SIZE - 32 + sw t1, kernelsp + + mfc0 t1, CP0_STATUS /* Do we really need this? */ + li a3, 0xff01 + and t1, a3 + lw a2, THREAD_STATUS(a1) + nor a3, $0, a3 + and a2, a3 + or a2, t1 + mtc0 a2, CP0_STATUS + move v0, a0 + jr ra + END(resume) + +/* + * Save a thread's fp context. + */ +LEAF(_save_fp) + fpu_save_single a0, t1 # clobbers t1 + jr ra + END(_save_fp) + +/* + * Restore a thread's fp context. + */ +LEAF(_restore_fp) + fpu_restore_single a0, t1 # clobbers t1 + jr ra + END(_restore_fp) + +/* + * Load the FPU with signalling NANS. This bit pattern we're using has + * the property that no matter whether considered as single or as double + * precision represents signaling NANS. + * + * We initialize fcr31 to rounding to nearest, no exceptions. + */ + +#define FPU_DEFAULT 0x00000000 + +LEAF(_init_fpu) + mfc0 t0, CP0_STATUS + li t1, ST0_CU1 + or t0, t1 + mtc0 t0, CP0_STATUS + + li t1, FPU_DEFAULT + ctc1 t1, fcr31 + + li t0, -1 + + mtc1 t0, $f0 + mtc1 t0, $f1 + mtc1 t0, $f2 + mtc1 t0, $f3 + mtc1 t0, $f4 + mtc1 t0, $f5 + mtc1 t0, $f6 + mtc1 t0, $f7 + mtc1 t0, $f8 + mtc1 t0, $f9 + mtc1 t0, $f10 + mtc1 t0, $f11 + mtc1 t0, $f12 + mtc1 t0, $f13 + mtc1 t0, $f14 + mtc1 t0, $f15 + mtc1 t0, $f16 + mtc1 t0, $f17 + mtc1 t0, $f18 + mtc1 t0, $f19 + mtc1 t0, $f20 + mtc1 t0, $f21 + mtc1 t0, $f22 + mtc1 t0, $f23 + mtc1 t0, $f24 + mtc1 t0, $f25 + mtc1 t0, $f26 + mtc1 t0, $f27 + mtc1 t0, $f28 + mtc1 t0, $f29 + mtc1 t0, $f30 + mtc1 t0, $f31 + jr ra + END(_init_fpu) diff --git a/arch/mips/kernel/r4k_fpu.S b/arch/mips/kernel/r4k_fpu.S new file mode 100644 index 000000000000..ebb643d8d14c --- /dev/null +++ b/arch/mips/kernel/r4k_fpu.S @@ -0,0 +1,191 @@ +/* + * 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, 98, 99, 2000, 01 Ralf Baechle + * + * Multi-arch abstraction and asm macros for easier reading: + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * Carsten Langgaard, carstenl@mips.com + * Copyright (C) 2000 MIPS Technologies, Inc. + * Copyright (C) 1999, 2001 Silicon Graphics, Inc. + */ +#include <linux/config.h> +#include <asm/asm.h> +#include <asm/errno.h> +#include <asm/fpregdef.h> +#include <asm/mipsregs.h> +#include <asm/offset.h> +#include <asm/regdef.h> + + .macro EX insn, reg, src + .set push + .set nomacro +.ex\@: \insn \reg, \src + .set pop + .section __ex_table,"a" + PTR .ex\@, fault + .previous + .endm + + .set noreorder + .set mips3 + /* Save floating point context */ +LEAF(_save_fp_context) + cfc1 t1, fcr31 + +#ifdef CONFIG_MIPS64 + /* Store the 16 odd double precision registers */ + EX sdc1 $f1, SC_FPREGS+8(a0) + EX sdc1 $f3, SC_FPREGS+24(a0) + EX sdc1 $f5, SC_FPREGS+40(a0) + EX sdc1 $f7, SC_FPREGS+56(a0) + EX sdc1 $f9, SC_FPREGS+72(a0) + EX sdc1 $f11, SC_FPREGS+88(a0) + EX sdc1 $f13, SC_FPREGS+104(a0) + EX sdc1 $f15, SC_FPREGS+120(a0) + EX sdc1 $f17, SC_FPREGS+136(a0) + EX sdc1 $f19, SC_FPREGS+152(a0) + EX sdc1 $f21, SC_FPREGS+168(a0) + EX sdc1 $f23, SC_FPREGS+184(a0) + EX sdc1 $f25, SC_FPREGS+200(a0) + EX sdc1 $f27, SC_FPREGS+216(a0) + EX sdc1 $f29, SC_FPREGS+232(a0) + EX sdc1 $f31, SC_FPREGS+248(a0) +#endif + + /* Store the 16 even double precision registers */ + EX sdc1 $f0, SC_FPREGS+0(a0) + EX sdc1 $f2, SC_FPREGS+16(a0) + EX sdc1 $f4, SC_FPREGS+32(a0) + EX sdc1 $f6, SC_FPREGS+48(a0) + EX sdc1 $f8, SC_FPREGS+64(a0) + EX sdc1 $f10, SC_FPREGS+80(a0) + EX sdc1 $f12, SC_FPREGS+96(a0) + EX sdc1 $f14, SC_FPREGS+112(a0) + EX sdc1 $f16, SC_FPREGS+128(a0) + EX sdc1 $f18, SC_FPREGS+144(a0) + EX sdc1 $f20, SC_FPREGS+160(a0) + EX sdc1 $f22, SC_FPREGS+176(a0) + EX sdc1 $f24, SC_FPREGS+192(a0) + EX sdc1 $f26, SC_FPREGS+208(a0) + EX sdc1 $f28, SC_FPREGS+224(a0) + EX sdc1 $f30, SC_FPREGS+240(a0) + EX sw t1, SC_FPC_CSR(a0) + cfc1 t0, $0 # implementation/version + EX sw t0, SC_FPC_EIR(a0) + + jr ra + li v0, 0 # success + END(_save_fp_context) + +#ifdef CONFIG_MIPS32_COMPAT + /* Save 32-bit process floating point context */ +LEAF(_save_fp_context32) + cfc1 t1, fcr31 + + EX sdc1 $f0, SC32_FPREGS+0(a0) + EX sdc1 $f2, SC32_FPREGS+16(a0) + EX sdc1 $f4, SC32_FPREGS+32(a0) + EX sdc1 $f6, SC32_FPREGS+48(a0) + EX sdc1 $f8, SC32_FPREGS+64(a0) + EX sdc1 $f10, SC32_FPREGS+80(a0) + EX sdc1 $f12, SC32_FPREGS+96(a0) + EX sdc1 $f14, SC32_FPREGS+112(a0) + EX sdc1 $f16, SC32_FPREGS+128(a0) + EX sdc1 $f18, SC32_FPREGS+144(a0) + EX sdc1 $f20, SC32_FPREGS+160(a0) + EX sdc1 $f22, SC32_FPREGS+176(a0) + EX sdc1 $f24, SC32_FPREGS+192(a0) + EX sdc1 $f26, SC32_FPREGS+208(a0) + EX sdc1 $f28, SC32_FPREGS+224(a0) + EX sdc1 $f30, SC32_FPREGS+240(a0) + EX sw t1, SC32_FPC_CSR(a0) + cfc1 t0, $0 # implementation/version + EX sw t0, SC32_FPC_EIR(a0) + + jr ra + li v0, 0 # success + END(_save_fp_context32) +#endif + +/* + * Restore FPU state: + * - fp gp registers + * - cp1 status/control register + */ +LEAF(_restore_fp_context) + EX lw t0, SC_FPC_CSR(a0) +#ifdef CONFIG_MIPS64 + EX ldc1 $f1, SC_FPREGS+8(a0) + EX ldc1 $f3, SC_FPREGS+24(a0) + EX ldc1 $f5, SC_FPREGS+40(a0) + EX ldc1 $f7, SC_FPREGS+56(a0) + EX ldc1 $f9, SC_FPREGS+72(a0) + EX ldc1 $f11, SC_FPREGS+88(a0) + EX ldc1 $f13, SC_FPREGS+104(a0) + EX ldc1 $f15, SC_FPREGS+120(a0) + EX ldc1 $f17, SC_FPREGS+136(a0) + EX ldc1 $f19, SC_FPREGS+152(a0) + EX ldc1 $f21, SC_FPREGS+168(a0) + EX ldc1 $f23, SC_FPREGS+184(a0) + EX ldc1 $f25, SC_FPREGS+200(a0) + EX ldc1 $f27, SC_FPREGS+216(a0) + EX ldc1 $f29, SC_FPREGS+232(a0) + EX ldc1 $f31, SC_FPREGS+248(a0) +#endif + EX ldc1 $f0, SC_FPREGS+0(a0) + EX ldc1 $f2, SC_FPREGS+16(a0) + EX ldc1 $f4, SC_FPREGS+32(a0) + EX ldc1 $f6, SC_FPREGS+48(a0) + EX ldc1 $f8, SC_FPREGS+64(a0) + EX ldc1 $f10, SC_FPREGS+80(a0) + EX ldc1 $f12, SC_FPREGS+96(a0) + EX ldc1 $f14, SC_FPREGS+112(a0) + EX ldc1 $f16, SC_FPREGS+128(a0) + EX ldc1 $f18, SC_FPREGS+144(a0) + EX ldc1 $f20, SC_FPREGS+160(a0) + EX ldc1 $f22, SC_FPREGS+176(a0) + EX ldc1 $f24, SC_FPREGS+192(a0) + EX ldc1 $f26, SC_FPREGS+208(a0) + EX ldc1 $f28, SC_FPREGS+224(a0) + EX ldc1 $f30, SC_FPREGS+240(a0) + ctc1 t0, fcr31 + jr ra + li v0, 0 # success + END(_restore_fp_context) + +#ifdef CONFIG_MIPS32_COMPAT +LEAF(_restore_fp_context32) + /* Restore an o32 sigcontext. */ + EX lw t0, SC32_FPC_CSR(a0) + EX ldc1 $f0, SC32_FPREGS+0(a0) + EX ldc1 $f2, SC32_FPREGS+16(a0) + EX ldc1 $f4, SC32_FPREGS+32(a0) + EX ldc1 $f6, SC32_FPREGS+48(a0) + EX ldc1 $f8, SC32_FPREGS+64(a0) + EX ldc1 $f10, SC32_FPREGS+80(a0) + EX ldc1 $f12, SC32_FPREGS+96(a0) + EX ldc1 $f14, SC32_FPREGS+112(a0) + EX ldc1 $f16, SC32_FPREGS+128(a0) + EX ldc1 $f18, SC32_FPREGS+144(a0) + EX ldc1 $f20, SC32_FPREGS+160(a0) + EX ldc1 $f22, SC32_FPREGS+176(a0) + EX ldc1 $f24, SC32_FPREGS+192(a0) + EX ldc1 $f26, SC32_FPREGS+208(a0) + EX ldc1 $f28, SC32_FPREGS+224(a0) + EX ldc1 $f30, SC32_FPREGS+240(a0) + ctc1 t0, fcr31 + jr ra + li v0, 0 # success + END(_restore_fp_context32) + .set reorder +#endif + + .type fault@function + .ent fault +fault: li v0, -EFAULT # failure + jr ra + .end fault diff --git a/arch/mips/kernel/r4k_switch.S b/arch/mips/kernel/r4k_switch.S new file mode 100644 index 000000000000..1fc3b2eb12bd --- /dev/null +++ b/arch/mips/kernel/r4k_switch.S @@ -0,0 +1,221 @@ +/* + * 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, 1996, 1998, 1999, 2002, 2003 Ralf Baechle + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * Copyright (C) 1994, 1995, 1996, by Andreas Busse + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) 2000 MIPS Technologies, Inc. + * written by Carsten Langgaard, carstenl@mips.com + */ +#include <linux/config.h> +#include <asm/asm.h> +#include <asm/cachectl.h> +#include <asm/fpregdef.h> +#include <asm/mipsregs.h> +#include <asm/offset.h> +#include <asm/page.h> +#include <asm/pgtable-bits.h> +#include <asm/regdef.h> +#include <asm/stackframe.h> +#include <asm/thread_info.h> + +#include <asm/asmmacro.h> + +/* + * Offset to the current process status flags, the first 32 bytes of the + * stack are not used. + */ +#define ST_OFF (_THREAD_SIZE - 32 - PT_SIZE + PT_STATUS) + +/* + * FPU context is saved iff the process has used it's FPU in the current + * time slice as indicated by _TIF_USEDFPU. In any case, the CU1 bit for user + * space STATUS register should be 0, so that a process *always* starts its + * userland with FPU disabled after each context switch. + * + * FPU will be enabled as soon as the process accesses FPU again, through + * do_cpu() trap. + */ + +/* + * task_struct *resume(task_struct *prev, task_struct *next, + * struct thread_info *next_ti) + */ + .align 5 + LEAF(resume) +#ifndef CONFIG_CPU_HAS_LLSC + sw zero, ll_bit +#endif + mfc0 t1, CP0_STATUS + LONG_S t1, THREAD_STATUS(a0) + cpu_save_nonscratch a0 + LONG_S ra, THREAD_REG31(a0) + + /* + * check if we need to save FPU registers + */ + PTR_L t3, TASK_THREAD_INFO(a0) + LONG_L t0, TI_FLAGS(t3) + li t1, _TIF_USEDFPU + and t2, t0, t1 + beqz t2, 1f + nor t1, zero, t1 + + and t0, t0, t1 + LONG_S t0, TI_FLAGS(t3) + + /* + * clear saved user stack CU1 bit + */ + LONG_L t0, ST_OFF(t3) + li t1, ~ST0_CU1 + and t0, t0, t1 + LONG_S t0, ST_OFF(t3) + + fpu_save_double a0 t1 t0 t2 # c0_status passed in t1 + # clobbers t0 and t2 +1: + + /* + * The order of restoring the registers takes care of the race + * updating $28, $29 and kernelsp without disabling ints. + */ + move $28, a2 + cpu_restore_nonscratch a1 + + PTR_ADDIU t0, $28, _THREAD_SIZE - 32 + set_saved_sp t0, t1, t2 + + mfc0 t1, CP0_STATUS /* Do we really need this? */ + li a3, 0xff01 + and t1, a3 + LONG_L a2, THREAD_STATUS(a1) + nor a3, $0, a3 + and a2, a3 + or a2, t1 + mtc0 a2, CP0_STATUS + move v0, a0 + jr ra + END(resume) + +/* + * Save a thread's fp context. + */ +LEAF(_save_fp) +#ifdef CONFIG_MIPS64 + mfc0 t1, CP0_STATUS +#endif + fpu_save_double a0 t1 t0 t2 # clobbers t1 + jr ra + END(_save_fp) + +/* + * Restore a thread's fp context. + */ +LEAF(_restore_fp) + fpu_restore_double a0, t1 # clobbers t1 + jr ra + END(_restore_fp) + +/* + * Load the FPU with signalling NANS. This bit pattern we're using has + * the property that no matter whether considered as single or as double + * precision represents signaling NANS. + * + * We initialize fcr31 to rounding to nearest, no exceptions. + */ + +#define FPU_DEFAULT 0x00000000 + +LEAF(_init_fpu) + mfc0 t0, CP0_STATUS + li t1, ST0_CU1 + or t0, t1 + mtc0 t0, CP0_STATUS + fpu_enable_hazard + + li t1, FPU_DEFAULT + ctc1 t1, fcr31 + + li t1, -1 # SNaN + +#ifdef CONFIG_MIPS64 + sll t0, t0, 5 + bgez t0, 1f # 16 / 32 register mode? + + dmtc1 t1, $f1 + dmtc1 t1, $f3 + dmtc1 t1, $f5 + dmtc1 t1, $f7 + dmtc1 t1, $f9 + dmtc1 t1, $f11 + dmtc1 t1, $f13 + dmtc1 t1, $f15 + dmtc1 t1, $f17 + dmtc1 t1, $f19 + dmtc1 t1, $f21 + dmtc1 t1, $f23 + dmtc1 t1, $f25 + dmtc1 t1, $f27 + dmtc1 t1, $f29 + dmtc1 t1, $f31 +1: +#endif + +#ifdef CONFIG_CPU_MIPS32 + mtc1 t1, $f0 + mtc1 t1, $f1 + mtc1 t1, $f2 + mtc1 t1, $f3 + mtc1 t1, $f4 + mtc1 t1, $f5 + mtc1 t1, $f6 + mtc1 t1, $f7 + mtc1 t1, $f8 + mtc1 t1, $f9 + mtc1 t1, $f10 + mtc1 t1, $f11 + mtc1 t1, $f12 + mtc1 t1, $f13 + mtc1 t1, $f14 + mtc1 t1, $f15 + mtc1 t1, $f16 + mtc1 t1, $f17 + mtc1 t1, $f18 + mtc1 t1, $f19 + mtc1 t1, $f20 + mtc1 t1, $f21 + mtc1 t1, $f22 + mtc1 t1, $f23 + mtc1 t1, $f24 + mtc1 t1, $f25 + mtc1 t1, $f26 + mtc1 t1, $f27 + mtc1 t1, $f28 + mtc1 t1, $f29 + mtc1 t1, $f30 + mtc1 t1, $f31 +#else + .set mips3 + dmtc1 t1, $f0 + dmtc1 t1, $f2 + dmtc1 t1, $f4 + dmtc1 t1, $f6 + dmtc1 t1, $f8 + dmtc1 t1, $f10 + dmtc1 t1, $f12 + dmtc1 t1, $f14 + dmtc1 t1, $f16 + dmtc1 t1, $f18 + dmtc1 t1, $f20 + dmtc1 t1, $f22 + dmtc1 t1, $f24 + dmtc1 t1, $f26 + dmtc1 t1, $f28 + dmtc1 t1, $f30 +#endif + jr ra + END(_init_fpu) diff --git a/arch/mips/kernel/r6000_fpu.S b/arch/mips/kernel/r6000_fpu.S new file mode 100644 index 000000000000..d8d3b13fe57f --- /dev/null +++ b/arch/mips/kernel/r6000_fpu.S @@ -0,0 +1,87 @@ +/* + * r6000_fpu.S: Save/restore floating point context for signal handlers. + * + * 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 + * + * Multi-arch abstraction and asm macros for easier reading: + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + */ +#include <asm/asm.h> +#include <asm/fpregdef.h> +#include <asm/mipsregs.h> +#include <asm/offset.h> +#include <asm/regdef.h> + + .set noreorder + .set mips2 + /* Save floating point context */ + LEAF(_save_fp_context) + mfc0 t0,CP0_STATUS + sll t0,t0,2 + bgez t0,1f + nop + + cfc1 t1,fcr31 + /* Store the 16 double precision registers */ + sdc1 $f0,(SC_FPREGS+0)(a0) + sdc1 $f2,(SC_FPREGS+16)(a0) + sdc1 $f4,(SC_FPREGS+32)(a0) + sdc1 $f6,(SC_FPREGS+48)(a0) + sdc1 $f8,(SC_FPREGS+64)(a0) + sdc1 $f10,(SC_FPREGS+80)(a0) + sdc1 $f12,(SC_FPREGS+96)(a0) + sdc1 $f14,(SC_FPREGS+112)(a0) + sdc1 $f16,(SC_FPREGS+128)(a0) + sdc1 $f18,(SC_FPREGS+144)(a0) + sdc1 $f20,(SC_FPREGS+160)(a0) + sdc1 $f22,(SC_FPREGS+176)(a0) + sdc1 $f24,(SC_FPREGS+192)(a0) + sdc1 $f26,(SC_FPREGS+208)(a0) + sdc1 $f28,(SC_FPREGS+224)(a0) + sdc1 $f30,(SC_FPREGS+240)(a0) + jr ra + sw t0,SC_FPC_CSR(a0) +1: jr ra + nop + END(_save_fp_context) + +/* Restore FPU state: + * - fp gp registers + * - cp1 status/control register + * + * We base the decision which registers to restore from the signal stack + * frame on the current content of c0_status, not on the content of the + * stack frame which might have been changed by the user. + */ + LEAF(_restore_fp_context) + mfc0 t0,CP0_STATUS + sll t0,t0,2 + + bgez t0,1f + lw t0,SC_FPC_CSR(a0) + /* Restore the 16 double precision registers */ + ldc1 $f0,(SC_FPREGS+0)(a0) + ldc1 $f2,(SC_FPREGS+16)(a0) + ldc1 $f4,(SC_FPREGS+32)(a0) + ldc1 $f6,(SC_FPREGS+48)(a0) + ldc1 $f8,(SC_FPREGS+64)(a0) + ldc1 $f10,(SC_FPREGS+80)(a0) + ldc1 $f12,(SC_FPREGS+96)(a0) + ldc1 $f14,(SC_FPREGS+112)(a0) + ldc1 $f16,(SC_FPREGS+128)(a0) + ldc1 $f18,(SC_FPREGS+144)(a0) + ldc1 $f20,(SC_FPREGS+160)(a0) + ldc1 $f22,(SC_FPREGS+176)(a0) + ldc1 $f24,(SC_FPREGS+192)(a0) + ldc1 $f26,(SC_FPREGS+208)(a0) + ldc1 $f28,(SC_FPREGS+224)(a0) + ldc1 $f30,(SC_FPREGS+240)(a0) + jr ra + ctc1 t0,fcr31 +1: jr ra + nop + END(_restore_fp_context) diff --git a/arch/mips/kernel/reset.c b/arch/mips/kernel/reset.c new file mode 100644 index 000000000000..7e0a9821931a --- /dev/null +++ b/arch/mips/kernel/reset.c @@ -0,0 +1,43 @@ +/* + * 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) 2001 by Ralf Baechle + * Copyright (C) 2001 MIPS Technologies, Inc. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/reboot.h> +#include <asm/reboot.h> + +/* + * Urgs ... Too many MIPS machines to handle this in a generic way. + * So handle all using function pointers to machine specific + * functions. + */ +void (*_machine_restart)(char *command); +void (*_machine_halt)(void); +void (*_machine_power_off)(void); + +void machine_restart(char *command) +{ + _machine_restart(command); +} + +EXPORT_SYMBOL(machine_restart); + +void machine_halt(void) +{ + _machine_halt(); +} + +EXPORT_SYMBOL(machine_halt); + +void machine_power_off(void) +{ + _machine_power_off(); +} + +EXPORT_SYMBOL(machine_power_off); diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S new file mode 100644 index 000000000000..344f2e29eb61 --- /dev/null +++ b/arch/mips/kernel/scall32-o32.S @@ -0,0 +1,641 @@ +/* + * 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, 96, 97, 98, 99, 2000, 01, 02 by Ralf Baechle + * Copyright (C) 2001 MIPS Technologies, Inc. + * Copyright (C) 2004 Thiemo Seufer + */ +#include <linux/config.h> +#include <linux/errno.h> +#include <asm/asm.h> +#include <asm/asmmacro.h> +#include <asm/mipsregs.h> +#include <asm/regdef.h> +#include <asm/stackframe.h> +#include <asm/isadep.h> +#include <asm/sysmips.h> +#include <asm/thread_info.h> +#include <asm/unistd.h> +#include <asm/war.h> +#include <asm/offset.h> + +/* Highest syscall used of any syscall flavour */ +#define MAX_SYSCALL_NO __NR_O32_Linux + __NR_O32_Linux_syscalls + + .align 5 +NESTED(handle_sys, PT_SIZE, sp) + .set noat + SAVE_SOME + STI + .set at + + lw t1, PT_EPC(sp) # skip syscall on return + +#if defined(CONFIG_BINFMT_IRIX) + sltiu t0, v0, MAX_SYSCALL_NO + 1 # check syscall number +#else + subu v0, v0, __NR_O32_Linux # check syscall number + sltiu t0, v0, __NR_O32_Linux_syscalls + 1 +#endif + addiu t1, 4 # skip to next instruction + sw t1, PT_EPC(sp) + beqz t0, illegal_syscall + + sll t0, v0, 3 + la t1, sys_call_table + addu t1, t0 + lw t2, (t1) # syscall routine + lw t3, 4(t1) # >= 0 if we need stack arguments + beqz t2, illegal_syscall + + sw a3, PT_R26(sp) # save a3 for syscall restarting + bgez t3, stackargs + +stack_done: + lw t0, TI_FLAGS($28) # syscall tracing enabled? + li t1, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT + and t0, t1 + bnez t0, syscall_trace_entry # -> yes + + jalr t2 # Do The Real Thing (TM) + + li t0, -EMAXERRNO - 1 # error? + sltu t0, t0, v0 + sw t0, PT_R7(sp) # set error flag + beqz t0, 1f + + negu v0 # error + sw v0, PT_R0(sp) # set flag for syscall + # restarting +1: sw v0, PT_R2(sp) # result + +o32_syscall_exit: + local_irq_disable # make sure need_resched and + # signals dont change between + # sampling and return + lw a2, TI_FLAGS($28) # current->work + li t0, _TIF_ALLWORK_MASK + and t0, a2 + bnez t0, o32_syscall_exit_work + + j restore_partial + +o32_syscall_exit_work: + j syscall_exit_work_partial + +/* ------------------------------------------------------------------------ */ + +syscall_trace_entry: + SAVE_STATIC + move s0, t2 + move a0, sp + li a1, 0 + jal do_syscall_trace + + lw a0, PT_R4(sp) # Restore argument registers + lw a1, PT_R5(sp) + lw a2, PT_R6(sp) + lw a3, PT_R7(sp) + jalr s0 + + li t0, -EMAXERRNO - 1 # error? + sltu t0, t0, v0 + sw t0, PT_R7(sp) # set error flag + beqz t0, 1f + + negu v0 # error + sw v0, PT_R0(sp) # set flag for syscall + # restarting +1: sw v0, PT_R2(sp) # result + + j syscall_exit + +/* ------------------------------------------------------------------------ */ + + /* + * More than four arguments. Try to deal with it by copying the + * stack arguments from the user stack to the kernel stack. + * This Sucks (TM). + */ +stackargs: + lw t0, PT_R29(sp) # get old user stack pointer + + /* + * We intentionally keep the kernel stack a little below the top of + * userspace so we don't have to do a slower byte accurate check here. + */ + lw t5, TI_ADDR_LIMIT($28) + addu t4, t0, 32 + and t5, t4 + bltz t5, bad_stack # -> sp is bad + + /* Ok, copy the args from the luser stack to the kernel stack. + * t3 is the precomputed number of instruction bytes needed to + * load or store arguments 6-8. + */ + + la t1, 5f # load up to 3 arguments + subu t1, t3 +1: lw t5, 16(t0) # argument #5 from usp + .set push + .set noreorder + .set nomacro + jr t1 + addiu t1, 6f - 5f + +2: lw t8, 28(t0) # argument #8 from usp +3: lw t7, 24(t0) # argument #7 from usp +4: lw t6, 20(t0) # argument #6 from usp +5: jr t1 + sw t5, 16(sp) # argument #5 to ksp + + sw t8, 28(sp) # argument #8 to ksp + sw t7, 24(sp) # argument #7 to ksp + sw t6, 20(sp) # argument #6 to ksp +6: j stack_done # go back + nop + .set pop + + .section __ex_table,"a" + PTR 1b,bad_stack + PTR 2b,bad_stack + PTR 3b,bad_stack + PTR 4b,bad_stack + .previous + + /* + * The stackpointer for a call with more than 4 arguments is bad. + * We probably should handle this case a bit more drastic. + */ +bad_stack: + negu v0 # error + sw v0, PT_R0(sp) + sw v0, PT_R2(sp) + li t0, 1 # set error flag + sw t0, PT_R7(sp) + j o32_syscall_exit + + /* + * The system call does not exist in this kernel + */ +illegal_syscall: + li v0, -ENOSYS # error + sw v0, PT_R2(sp) + li t0, 1 # set error flag + sw t0, PT_R7(sp) + j o32_syscall_exit + END(handle_sys) + + LEAF(mips_atomic_set) + andi v0, a1, 3 # must be word aligned + bnez v0, bad_alignment + + lw v1, TI_ADDR_LIMIT($28) # in legal address range? + addiu a0, a1, 4 + or a0, a0, a1 + and a0, a0, v1 + bltz a0, bad_address + +#ifdef CONFIG_CPU_HAS_LLSC + /* Ok, this is the ll/sc case. World is sane :-) */ +1: ll v0, (a1) + move a0, a2 +2: sc a0, (a1) +#if R10000_LLSC_WAR + beqzl a0, 1b +#else + beqz a0, 1b +#endif + + .section __ex_table,"a" + PTR 1b, bad_stack + PTR 2b, bad_stack + .previous +#else + sw a1, 16(sp) + sw a2, 20(sp) + + move a0, sp + move a2, a1 + li a1, 1 + jal do_page_fault + + lw a1, 16(sp) + lw a2, 20(sp) + + /* + * At this point the page should be readable and writable unless + * there was no more memory available. + */ +1: lw v0, (a1) +2: sw a2, (a1) + + .section __ex_table,"a" + PTR 1b, no_mem + PTR 2b, no_mem + .previous +#endif + + sw zero, PT_R7(sp) # success + sw v0, PT_R2(sp) # result + + /* Success, so skip usual error handling garbage. */ + lw a2, TI_FLAGS($28) # syscall tracing enabled? + li t0, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT + and t0, a2, t0 + bnez t0, 1f + + j o32_syscall_exit + +1: SAVE_STATIC + move a0, sp + li a1, 1 + jal do_syscall_trace + j syscall_exit + +no_mem: li v0, -ENOMEM + jr ra + +bad_address: + li v0, -EFAULT + jr ra + +bad_alignment: + li v0, -EINVAL + jr ra + END(mips_atomic_set) + + LEAF(sys_sysmips) + beq a0, MIPS_ATOMIC_SET, mips_atomic_set + j _sys_sysmips + END(sys_sysmips) + + LEAF(sys_syscall) +#if defined(CONFIG_BINFMT_IRIX) + sltiu v0, a0, MAX_SYSCALL_NO + 1 # check syscall number +#else + subu t0, a0, __NR_O32_Linux # check syscall number + sltiu v0, t0, __NR_O32_Linux_syscalls + 1 +#endif + sll t1, t0, 3 + beqz v0, einval + + lw t2, sys_call_table(t1) # syscall routine + +#if defined(CONFIG_BINFMT_IRIX) + li v1, 4000 # nr of sys_syscall +#else + li v1, 4000 - __NR_O32_Linux # index of sys_syscall +#endif + beq t0, v1, einval # do not recurse + + /* Some syscalls like execve get their arguments from struct pt_regs + and claim zero arguments in the syscall table. Thus we have to + assume the worst case and shuffle around all potential arguments. + If you want performance, don't use indirect syscalls. */ + + move a0, a1 # shift argument registers + move a1, a2 + move a2, a3 + lw a3, 16(sp) + lw t4, 20(sp) + lw t5, 24(sp) + lw t6, 28(sp) + sw t4, 16(sp) + sw t5, 20(sp) + sw t6, 24(sp) + sw a0, PT_R4(sp) # .. and push back a0 - a3, some + sw a1, PT_R5(sp) # syscalls expect them there + sw a2, PT_R6(sp) + sw a3, PT_R7(sp) + sw a3, PT_R26(sp) # update a3 for syscall restarting + jr t2 + /* Unreached */ + +einval: li v0, -EINVAL + jr ra + END(sys_syscall) + + .macro fifty ptr, nargs, from=1, to=50 + sys \ptr \nargs + .if \to-\from + fifty \ptr,\nargs,"(\from+1)",\to + .endif + .endm + + .macro mille ptr, nargs, from=1, to=20 + fifty \ptr,\nargs + .if \to-\from + mille \ptr,\nargs,"(\from+1)",\to + .endif + .endm + + .macro syscalltable +#if defined(CONFIG_BINFMT_IRIX) + mille sys_ni_syscall 0 /* 0 - 999 SVR4 flavour */ + mille sys_ni_syscall 0 /* 1000 - 1999 32-bit IRIX */ + mille sys_ni_syscall 0 /* 2000 - 2999 BSD43 flavour */ + mille sys_ni_syscall 0 /* 3000 - 3999 POSIX flavour */ +#endif + + sys sys_syscall 8 /* 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 1 + 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_lchown 3 + sys sys_ni_syscall 0 + sys sys_ni_syscall 0 /* was sys_stat */ + sys sys_lseek 3 + sys sys_getpid 0 /* 4020 */ + sys sys_mount 5 + sys sys_oldumount 1 + sys sys_setuid 1 + sys sys_getuid 0 + sys sys_stime 1 /* 4025 */ + sys sys_ptrace 4 + sys sys_alarm 1 + sys sys_ni_syscall 0 /* was sys_fstat */ + sys sys_pause 0 + sys sys_utime 2 /* 4030 */ + sys sys_ni_syscall 0 + sys sys_ni_syscall 0 + sys sys_access 2 + sys sys_nice 1 + sys sys_ni_syscall 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_ni_syscall 0 + sys sys_brk 1 /* 4045 */ + sys sys_setgid 1 + sys sys_getgid 0 + sys sys_ni_syscall 0 /* was signal(2) */ + sys sys_geteuid 0 + sys sys_getegid 0 /* 4050 */ + sys sys_acct 1 + sys sys_umount 2 + sys sys_ni_syscall 0 + sys sys_ioctl 3 + sys sys_fcntl 3 /* 4055 */ + sys sys_ni_syscall 2 + sys sys_setpgid 2 + sys sys_ni_syscall 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_ni_syscall 0 /* was sys_lstat */ + sys sys_readlink 3 /* 4085 */ + sys sys_uselib 1 + sys sys_swapon 2 + sys sys_reboot 3 + sys old_readdir 3 + sys old_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_ni_syscall 0 + sys sys_statfs 2 + sys sys_fstatfs 2 /* 4100 */ + sys sys_ni_syscall 0 /* was ioperm(2) */ + 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_ni_syscall 0 /* 4110 was iopl(2) */ + sys sys_vhangup 0 + sys sys_ni_syscall 0 /* was sys_idle() */ + sys sys_ni_syscall 0 /* was sys_vm86 */ + 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_ni_syscall 0 /* was create_module */ + sys sys_init_module 5 + sys sys_delete_module 1 + sys sys_ni_syscall 0 /* 4130 was get_kernel_syms */ + sys sys_quotactl 4 + 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_ni_syscall 0 /* 4150 */ + sys sys_getsid 1 + sys sys_fdatasync 1 + 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 + sys sys_ni_syscall 0 /* was sys_query_module */ + sys sys_poll 3 + sys sys_nfsservctl 3 + sys sys_setresgid 3 /* 4190 */ + sys sys_getresgid 3 + sys sys_prctl 5 + sys sys_rt_sigreturn 0 + sys sys_rt_sigaction 4 + sys sys_rt_sigprocmask 4 /* 4195 */ + sys sys_rt_sigpending 2 + sys sys_rt_sigtimedwait 4 + sys sys_rt_sigqueueinfo 3 + sys sys_rt_sigsuspend 0 + sys sys_pread64 6 /* 4200 */ + sys sys_pwrite64 6 + sys sys_chown 3 + sys sys_getcwd 2 + sys sys_capget 2 + sys sys_capset 2 /* 4205 */ + sys sys_sigaltstack 0 + sys sys_sendfile 4 + sys sys_ni_syscall 0 + sys sys_ni_syscall 0 + sys sys_mmap2 6 /* 4210 */ + sys sys_truncate64 4 + sys sys_ftruncate64 4 + sys sys_stat64 2 + sys sys_lstat64 2 + sys sys_fstat64 2 /* 4215 */ + sys sys_pivot_root 2 + sys sys_mincore 3 + sys sys_madvise 3 + sys sys_getdents64 3 + sys sys_fcntl64 3 /* 4220 */ + sys sys_ni_syscall 0 + sys sys_gettid 0 + sys sys_readahead 5 + sys sys_setxattr 5 + sys sys_lsetxattr 5 /* 4225 */ + sys sys_fsetxattr 5 + sys sys_getxattr 4 + sys sys_lgetxattr 4 + sys sys_fgetxattr 4 + sys sys_listxattr 3 /* 4230 */ + sys sys_llistxattr 3 + sys sys_flistxattr 3 + sys sys_removexattr 2 + sys sys_lremovexattr 2 + sys sys_fremovexattr 2 /* 4235 */ + sys sys_tkill 2 + sys sys_sendfile64 5 + sys sys_futex 2 + sys sys_sched_setaffinity 3 + sys sys_sched_getaffinity 3 /* 4240 */ + sys sys_io_setup 2 + sys sys_io_destroy 1 + sys sys_io_getevents 5 + sys sys_io_submit 3 + sys sys_io_cancel 3 /* 4245 */ + sys sys_exit_group 1 + sys sys_lookup_dcookie 3 + sys sys_epoll_create 1 + sys sys_epoll_ctl 4 + sys sys_epoll_wait 3 /* 4250 */ + sys sys_remap_file_pages 5 + sys sys_set_tid_address 1 + sys sys_restart_syscall 0 + sys sys_fadvise64_64 7 + sys sys_statfs64 3 /* 4255 */ + sys sys_fstatfs64 2 + sys sys_timer_create 3 + sys sys_timer_settime 4 + sys sys_timer_gettime 2 + sys sys_timer_getoverrun 1 /* 4260 */ + sys sys_timer_delete 1 + sys sys_clock_settime 2 + sys sys_clock_gettime 2 + sys sys_clock_getres 2 + sys sys_clock_nanosleep 4 /* 4265 */ + sys sys_tgkill 3 + sys sys_utimes 2 + sys sys_mbind 4 + sys sys_ni_syscall 0 /* sys_get_mempolicy */ + sys sys_ni_syscall 0 /* 4270 sys_set_mempolicy */ + sys sys_mq_open 4 + sys sys_mq_unlink 1 + sys sys_mq_timedsend 5 + sys sys_mq_timedreceive 5 + sys sys_mq_notify 2 /* 4275 */ + sys sys_mq_getsetattr 3 + sys sys_ni_syscall 0 /* sys_vserver */ + sys sys_waitid 4 + sys sys_ni_syscall 0 /* available, was setaltroot */ + sys sys_add_key 5 + sys sys_request_key 4 + sys sys_keyctl 5 + + .endm + + /* We pre-compute the number of _instruction_ bytes needed to + load or store the arguments 6-8. Negative values are ignored. */ + + .macro sys function, nargs + PTR \function + LONG (\nargs << 2) - (5 << 2) + .endm + + .align 3 + .type sys_call_table,@object +EXPORT(sys_call_table) + syscalltable + .size sys_call_table, . - sys_call_table diff --git a/arch/mips/kernel/scall64-64.S b/arch/mips/kernel/scall64-64.S new file mode 100644 index 000000000000..32efb888160a --- /dev/null +++ b/arch/mips/kernel/scall64-64.S @@ -0,0 +1,451 @@ +/* + * 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, 96, 97, 98, 99, 2000, 01, 02 by Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * Copyright (C) 2001 MIPS Technologies, Inc. + */ +#include <linux/config.h> +#include <linux/errno.h> +#include <asm/asm.h> +#include <asm/asmmacro.h> +#include <asm/mipsregs.h> +#include <asm/regdef.h> +#include <asm/stackframe.h> +#include <asm/offset.h> +#include <asm/sysmips.h> +#include <asm/thread_info.h> +#include <asm/unistd.h> +#include <asm/war.h> + +#ifndef CONFIG_BINFMT_ELF32 +/* Neither O32 nor N32, so define handle_sys here */ +#define handle_sys64 handle_sys +#endif + + .align 5 +NESTED(handle_sys64, PT_SIZE, sp) +#if !defined(CONFIG_MIPS32_O32) && !defined(CONFIG_MIPS32_N32) + /* + * When 32-bit compatibility is configured scall_o32.S + * already did this. + */ + .set noat + SAVE_SOME + STI + .set at +#endif + + dsubu t0, v0, __NR_64_Linux # check syscall number + sltiu t0, t0, __NR_64_Linux_syscalls + 1 +#if !defined(CONFIG_MIPS32_O32) && !defined(CONFIG_MIPS32_N32) + ld t1, PT_EPC(sp) # skip syscall on return + daddiu t1, 4 # skip to next instruction + sd t1, PT_EPC(sp) +#endif + beqz t0, illegal_syscall + + dsll t0, v0, 3 # offset into table + ld t2, (sys_call_table - (__NR_64_Linux * 8))(t0) + # syscall routine + + sd a3, PT_R26(sp) # save a3 for syscall restarting + + li t1, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT + LONG_L t0, TI_FLAGS($28) # syscall tracing enabled? + and t0, t1, t0 + bnez t0, syscall_trace_entry + + jalr t2 # Do The Real Thing (TM) + + li t0, -EMAXERRNO - 1 # error? + sltu t0, t0, v0 + sd t0, PT_R7(sp) # set error flag + beqz t0, 1f + + dnegu v0 # error + sd v0, PT_R0(sp) # set flag for syscall + # restarting +1: sd v0, PT_R2(sp) # result + +n64_syscall_exit: + local_irq_disable # make sure need_resched and + # signals dont change between + # sampling and return + LONG_L a2, TI_FLAGS($28) # current->work + li t0, _TIF_ALLWORK_MASK + and t0, a2, t0 + bnez t0, n64_syscall_exit_work + + j restore_partial + +n64_syscall_exit_work: + j syscall_exit_work_partial + +/* ------------------------------------------------------------------------ */ + +syscall_trace_entry: + SAVE_STATIC + move s0, t2 + move a0, sp + li a1, 0 + jal do_syscall_trace + + ld a0, PT_R4(sp) # Restore argument registers + ld a1, PT_R5(sp) + ld a2, PT_R6(sp) + ld a3, PT_R7(sp) + ld a4, PT_R8(sp) + ld a5, PT_R9(sp) + jalr s0 + + li t0, -EMAXERRNO - 1 # error? + sltu t0, t0, v0 + sd t0, PT_R7(sp) # set error flag + beqz t0, 1f + + dnegu v0 # error + sd v0, PT_R0(sp) # set flag for syscall restarting +1: sd v0, PT_R2(sp) # result + + j syscall_exit + +illegal_syscall: + /* This also isn't a 64-bit syscall, throw an error. */ + li v0, -ENOSYS # error + sd v0, PT_R2(sp) + li t0, 1 # set error flag + sd t0, PT_R7(sp) + j n64_syscall_exit + END(handle_sys64) + + LEAF(mips_atomic_set) + andi v0, a1, 3 # must be word aligned + bnez v0, bad_alignment + + LONG_L v1, TI_ADDR_LIMIT($28) # in legal address range? + LONG_ADDIU a0, a1, 4 + or a0, a0, a1 + and a0, a0, v1 + bltz a0, bad_address + +#ifdef CONFIG_CPU_HAS_LLSC + /* Ok, this is the ll/sc case. World is sane :-) */ +1: ll v0, (a1) + move a0, a2 +2: sc a0, (a1) +#if R10000_LLSC_WAR + beqzl a0, 1b +#else + beqz a0, 1b +#endif + + .section __ex_table,"a" + PTR 1b, bad_stack + PTR 2b, bad_stack + .previous +#else + sw a1, 16(sp) + sw a2, 20(sp) + + move a0, sp + move a2, a1 + li a1, 1 + jal do_page_fault + + lw a1, 16(sp) + lw a2, 20(sp) + + /* + * At this point the page should be readable and writable unless + * there was no more memory available. + */ +1: lw v0, (a1) +2: sw a2, (a1) + + .section __ex_table,"a" + PTR 1b, no_mem + PTR 2b, no_mem + .previous +#endif + + sd zero, PT_R7(sp) # success + sd v0, PT_R2(sp) # result + + /* Success, so skip usual error handling garbage. */ + li t0, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT + LONG_L a2, TI_FLAGS($28) # syscall tracing enabled? + and t0, a2, t0 + bnez t0, 1f + + j n64_syscall_exit + +1: SAVE_STATIC + move a0, sp + li a1, 1 + jal do_syscall_trace + j syscall_exit + +no_mem: li v0, -ENOMEM + jr ra + +bad_address: + li v0, -EFAULT + jr ra + +bad_alignment: + li v0, -EINVAL + jr ra + END(mips_atomic_set) + + LEAF(sys_sysmips) + beq a0, MIPS_ATOMIC_SET, mips_atomic_set + j _sys_sysmips + END(sys_sysmips) + + .align 3 +sys_call_table: + PTR sys_read /* 5000 */ + PTR sys_write + PTR sys_open + PTR sys_close + PTR sys_newstat + PTR sys_newfstat /* 5005 */ + PTR sys_newlstat + PTR sys_poll + PTR sys_lseek + PTR old_mmap + PTR sys_mprotect /* 5010 */ + PTR sys_munmap + PTR sys_brk + PTR sys_rt_sigaction + PTR sys_rt_sigprocmask + PTR sys_ioctl /* 5015 */ + PTR sys_pread64 + PTR sys_pwrite64 + PTR sys_readv + PTR sys_writev + PTR sys_access /* 5020 */ + PTR sys_pipe + PTR sys_select + PTR sys_sched_yield + PTR sys_mremap + PTR sys_msync /* 5025 */ + PTR sys_mincore + PTR sys_madvise + PTR sys_shmget + PTR sys_shmat + PTR sys_shmctl /* 5030 */ + PTR sys_dup + PTR sys_dup2 + PTR sys_pause + PTR sys_nanosleep + PTR sys_getitimer /* 5035 */ + PTR sys_setitimer + PTR sys_alarm + PTR sys_getpid + PTR sys_sendfile64 + PTR sys_socket /* 5040 */ + PTR sys_connect + PTR sys_accept + PTR sys_sendto + PTR sys_recvfrom + PTR sys_sendmsg /* 5045 */ + PTR sys_recvmsg + PTR sys_shutdown + PTR sys_bind + PTR sys_listen + PTR sys_getsockname /* 5050 */ + PTR sys_getpeername + PTR sys_socketpair + PTR sys_setsockopt + PTR sys_getsockopt + PTR sys_clone /* 5055 */ + PTR sys_fork + PTR sys_execve + PTR sys_exit + PTR sys_wait4 + PTR sys_kill /* 5060 */ + PTR sys_newuname + PTR sys_semget + PTR sys_semop + PTR sys_semctl + PTR sys_shmdt /* 5065 */ + PTR sys_msgget + PTR sys_msgsnd + PTR sys_msgrcv + PTR sys_msgctl + PTR sys_fcntl /* 5070 */ + PTR sys_flock + PTR sys_fsync + PTR sys_fdatasync + PTR sys_truncate + PTR sys_ftruncate /* 5075 */ + PTR sys_getdents + PTR sys_getcwd + PTR sys_chdir + PTR sys_fchdir + PTR sys_rename /* 5080 */ + PTR sys_mkdir + PTR sys_rmdir + PTR sys_creat + PTR sys_link + PTR sys_unlink /* 5085 */ + PTR sys_symlink + PTR sys_readlink + PTR sys_chmod + PTR sys_fchmod + PTR sys_chown /* 5090 */ + PTR sys_fchown + PTR sys_lchown + PTR sys_umask + PTR sys_gettimeofday + PTR sys_getrlimit /* 5095 */ + PTR sys_getrusage + PTR sys_sysinfo + PTR sys_times + PTR sys_ptrace + PTR sys_getuid /* 5100 */ + PTR sys_syslog + PTR sys_getgid + PTR sys_setuid + PTR sys_setgid + PTR sys_geteuid /* 5105 */ + PTR sys_getegid + PTR sys_setpgid + PTR sys_getppid + PTR sys_getpgrp + PTR sys_setsid /* 5110 */ + PTR sys_setreuid + PTR sys_setregid + PTR sys_getgroups + PTR sys_setgroups + PTR sys_setresuid /* 5115 */ + PTR sys_getresuid + PTR sys_setresgid + PTR sys_getresgid + PTR sys_getpgid + PTR sys_setfsuid /* 5120 */ + PTR sys_setfsgid + PTR sys_getsid + PTR sys_capget + PTR sys_capset + PTR sys_rt_sigpending /* 5125 */ + PTR sys_rt_sigtimedwait + PTR sys_rt_sigqueueinfo + PTR sys_rt_sigsuspend + PTR sys_sigaltstack + PTR sys_utime /* 5130 */ + PTR sys_mknod + PTR sys_personality + PTR sys_ustat + PTR sys_statfs + PTR sys_fstatfs /* 5135 */ + PTR sys_sysfs + PTR sys_getpriority + PTR sys_setpriority + PTR sys_sched_setparam + PTR sys_sched_getparam /* 5140 */ + PTR sys_sched_setscheduler + PTR sys_sched_getscheduler + PTR sys_sched_get_priority_max + PTR sys_sched_get_priority_min + PTR sys_sched_rr_get_interval /* 5145 */ + PTR sys_mlock + PTR sys_munlock + PTR sys_mlockall + PTR sys_munlockall + PTR sys_vhangup /* 5150 */ + PTR sys_pivot_root + PTR sys_sysctl + PTR sys_prctl + PTR sys_adjtimex + PTR sys_setrlimit /* 5155 */ + PTR sys_chroot + PTR sys_sync + PTR sys_acct + PTR sys_settimeofday + PTR sys_mount /* 5160 */ + PTR sys_umount + PTR sys_swapon + PTR sys_swapoff + PTR sys_reboot + PTR sys_sethostname /* 5165 */ + PTR sys_setdomainname + PTR sys_ni_syscall /* was create_module */ + PTR sys_init_module + PTR sys_delete_module + PTR sys_ni_syscall /* 5170, was get_kernel_syms */ + PTR sys_ni_syscall /* was query_module */ + PTR sys_quotactl + PTR sys_nfsservctl + PTR sys_ni_syscall /* res. for getpmsg */ + PTR sys_ni_syscall /* 5175 for putpmsg */ + PTR sys_ni_syscall /* res. for afs_syscall */ + PTR sys_ni_syscall /* res. for security */ + PTR sys_gettid + PTR sys_readahead + PTR sys_setxattr /* 5180 */ + PTR sys_lsetxattr + PTR sys_fsetxattr + PTR sys_getxattr + PTR sys_lgetxattr + PTR sys_fgetxattr /* 5185 */ + PTR sys_listxattr + PTR sys_llistxattr + PTR sys_flistxattr + PTR sys_removexattr + PTR sys_lremovexattr /* 5190 */ + PTR sys_fremovexattr + PTR sys_tkill + PTR sys_ni_syscall + PTR sys_futex + PTR sys_sched_setaffinity /* 5195 */ + PTR sys_sched_getaffinity + PTR sys_cacheflush + PTR sys_cachectl + PTR sys_sysmips + PTR sys_io_setup /* 5200 */ + PTR sys_io_destroy + PTR sys_io_getevents + PTR sys_io_submit + PTR sys_io_cancel + PTR sys_exit_group /* 5205 */ + PTR sys_lookup_dcookie + PTR sys_epoll_create + PTR sys_epoll_ctl + PTR sys_epoll_wait + PTR sys_remap_file_pages /* 5210 */ + PTR sys_rt_sigreturn + PTR sys_set_tid_address + PTR sys_restart_syscall + PTR sys_semtimedop + PTR sys_fadvise64_64 /* 5215 */ + PTR sys_timer_create + PTR sys_timer_settime + PTR sys_timer_gettime + PTR sys_timer_getoverrun + PTR sys_timer_delete /* 5220 */ + PTR sys_clock_settime + PTR sys_clock_gettime + PTR sys_clock_getres + PTR sys_clock_nanosleep + PTR sys_tgkill /* 5225 */ + PTR sys_utimes + PTR sys_mbind + PTR sys_ni_syscall /* sys_get_mempolicy */ + PTR sys_ni_syscall /* sys_set_mempolicy */ + PTR sys_mq_open /* 5230 */ + PTR sys_mq_unlink + PTR sys_mq_timedsend + PTR sys_mq_timedreceive + PTR sys_mq_notify + PTR sys_mq_getsetattr /* 5235 */ + PTR sys_ni_syscall /* sys_vserver */ + PTR sys_waitid + PTR sys_ni_syscall /* available, was setaltroot */ + PTR sys_add_key + PTR sys_request_key /* 5240 */ + PTR sys_keyctl diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S new file mode 100644 index 000000000000..e52049c87bc3 --- /dev/null +++ b/arch/mips/kernel/scall64-n32.S @@ -0,0 +1,365 @@ +/* + * 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, 96, 97, 98, 99, 2000, 01 by Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * Copyright (C) 2001 MIPS Technologies, Inc. + */ +#include <linux/config.h> +#include <linux/errno.h> +#include <asm/asm.h> +#include <asm/asmmacro.h> +#include <asm/mipsregs.h> +#include <asm/regdef.h> +#include <asm/stackframe.h> +#include <asm/thread_info.h> +#include <asm/unistd.h> + +/* This duplicates the definition from <linux/sched.h> */ +#define PT_TRACESYS 0x00000002 /* tracing system calls */ + +/* This duplicates the definition from <asm/signal.h> */ +#define SIGILL 4 /* Illegal instruction (ANSI). */ + +#ifndef CONFIG_MIPS32_O32 +/* No O32, so define handle_sys here */ +#define handle_sysn32 handle_sys +#endif + + .align 5 +NESTED(handle_sysn32, PT_SIZE, sp) +#ifndef CONFIG_MIPS32_O32 + .set noat + SAVE_SOME + STI + .set at +#endif + + dsubu t0, v0, __NR_N32_Linux # check syscall number + sltiu t0, t0, __NR_N32_Linux_syscalls + 1 + +#ifndef CONFIG_MIPS32_O32 + ld t1, PT_EPC(sp) # skip syscall on return + daddiu t1, 4 # skip to next instruction + sd t1, PT_EPC(sp) +#endif + beqz t0, not_n32_scall + + dsll t0, v0, 3 # offset into table + ld t2, (sysn32_call_table - (__NR_N32_Linux * 8))(t0) + + sd a3, PT_R26(sp) # save a3 for syscall restarting + + li t1, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT + LONG_L t0, TI_FLAGS($28) # syscall tracing enabled? + and t0, t1, t0 + bnez t0, n32_syscall_trace_entry + + jalr t2 # Do The Real Thing (TM) + + li t0, -EMAXERRNO - 1 # error? + sltu t0, t0, v0 + sd t0, PT_R7(sp) # set error flag + beqz t0, 1f + + dnegu v0 # error + sd v0, PT_R0(sp) # set flag for syscall restarting +1: sd v0, PT_R2(sp) # result + + local_irq_disable # make sure need_resched and + # signals dont change between + # sampling and return + LONG_L a2, TI_FLAGS($28) # current->work + li t0, _TIF_ALLWORK_MASK + and t0, a2, t0 + bnez t0, n32_syscall_exit_work + + j restore_partial + +n32_syscall_exit_work: + j syscall_exit_work_partial + +/* ------------------------------------------------------------------------ */ + +n32_syscall_trace_entry: + SAVE_STATIC + move s0, t2 + move a0, sp + li a1, 0 + jal do_syscall_trace + + ld a0, PT_R4(sp) # Restore argument registers + ld a1, PT_R5(sp) + ld a2, PT_R6(sp) + ld a3, PT_R7(sp) + ld a4, PT_R8(sp) + ld a5, PT_R9(sp) + jalr s0 + + li t0, -EMAXERRNO - 1 # error? + sltu t0, t0, v0 + sd t0, PT_R7(sp) # set error flag + beqz t0, 1f + + dnegu v0 # error + sd v0, PT_R0(sp) # set flag for syscall restarting +1: sd v0, PT_R2(sp) # result + + j syscall_exit + +not_n32_scall: + /* This is not an n32 compatibility syscall, pass it on to + the n64 syscall handlers. */ + j handle_sys64 + + END(handle_sysn32) + +EXPORT(sysn32_call_table) + PTR sys_read /* 6000 */ + PTR sys_write + PTR sys_open + PTR sys_close + PTR sys_newstat + PTR sys_newfstat /* 6005 */ + PTR sys_newlstat + PTR sys_poll + PTR sys_lseek + PTR old_mmap + PTR sys_mprotect /* 6010 */ + PTR sys_munmap + PTR sys_brk + PTR sys32_rt_sigaction + PTR sys32_rt_sigprocmask + PTR compat_sys_ioctl /* 6015 */ + PTR sys_pread64 + PTR sys_pwrite64 + PTR compat_sys_readv + PTR compat_sys_writev + PTR sys_access /* 6020 */ + PTR sys_pipe + PTR compat_sys_select + PTR sys_sched_yield + PTR sys_mremap + PTR sys_msync /* 6025 */ + PTR sys_mincore + PTR sys_madvise + PTR sys_shmget + PTR sys32_shmat + PTR sys_shmctl /* 6030 */ + PTR sys_dup + PTR sys_dup2 + PTR sys_pause + PTR compat_sys_nanosleep + PTR compat_sys_getitimer /* 6035 */ + PTR compat_sys_setitimer + PTR sys_alarm + PTR sys_getpid + PTR sys32_sendfile + PTR sys_socket /* 6040 */ + PTR sys_connect + PTR sys_accept + PTR sys_sendto + PTR sys_recvfrom + PTR compat_sys_sendmsg /* 6045 */ + PTR compat_sys_recvmsg + PTR sys_shutdown + PTR sys_bind + PTR sys_listen + PTR sys_getsockname /* 6050 */ + PTR sys_getpeername + PTR sys_socketpair + PTR compat_sys_setsockopt + PTR sys_getsockopt + PTR sys_clone /* 6055 */ + PTR sys_fork + PTR sys32_execve + PTR sys_exit + PTR sys32_wait4 + PTR sys_kill /* 6060 */ + PTR sys32_newuname + PTR sys_semget + PTR sys_semop + PTR sys_semctl + PTR sys_shmdt /* 6065 */ + PTR sys_msgget + PTR sys_msgsnd + PTR sys_msgrcv + PTR sys_msgctl + PTR compat_sys_fcntl /* 6070 */ + PTR sys_flock + PTR sys_fsync + PTR sys_fdatasync + PTR sys_truncate + PTR sys_ftruncate /* 6075 */ + PTR sys32_getdents + PTR sys_getcwd + PTR sys_chdir + PTR sys_fchdir + PTR sys_rename /* 6080 */ + PTR sys_mkdir + PTR sys_rmdir + PTR sys_creat + PTR sys_link + PTR sys_unlink /* 6085 */ + PTR sys_symlink + PTR sys_readlink + PTR sys_chmod + PTR sys_fchmod + PTR sys_chown /* 6090 */ + PTR sys_fchown + PTR sys_lchown + PTR sys_umask + PTR sys32_gettimeofday + PTR compat_sys_getrlimit /* 6095 */ + PTR compat_sys_getrusage + PTR sys32_sysinfo + PTR compat_sys_times + PTR sys_ptrace + PTR sys_getuid /* 6100 */ + PTR sys_syslog + PTR sys_getgid + PTR sys_setuid + PTR sys_setgid + PTR sys_geteuid /* 6105 */ + PTR sys_getegid + PTR sys_setpgid + PTR sys_getppid + PTR sys_getpgrp + PTR sys_setsid /* 6110 */ + PTR sys_setreuid + PTR sys_setregid + PTR sys_getgroups + PTR sys_setgroups + PTR sys_setresuid /* 6115 */ + PTR sys_getresuid + PTR sys_setresgid + PTR sys_getresgid + PTR sys_getpgid + PTR sys_setfsuid /* 6120 */ + PTR sys_setfsgid + PTR sys_getsid + PTR sys_capget + PTR sys_capset + PTR sys32_rt_sigpending /* 6125 */ + PTR compat_sys_rt_sigtimedwait + PTR sys32_rt_sigqueueinfo + PTR sys32_rt_sigsuspend + PTR sys32_sigaltstack + PTR compat_sys_utime /* 6130 */ + PTR sys_mknod + PTR sys32_personality + PTR sys_ustat + PTR compat_sys_statfs + PTR compat_sys_fstatfs /* 6135 */ + PTR sys_sysfs + PTR sys_getpriority + PTR sys_setpriority + PTR sys_sched_setparam + PTR sys_sched_getparam /* 6140 */ + PTR sys_sched_setscheduler + PTR sys_sched_getscheduler + PTR sys_sched_get_priority_max + PTR sys_sched_get_priority_min + PTR sys32_sched_rr_get_interval /* 6145 */ + PTR sys_mlock + PTR sys_munlock + PTR sys_mlockall + PTR sys_munlockall + PTR sys_vhangup /* 6150 */ + PTR sys_pivot_root + PTR sys32_sysctl + PTR sys_prctl + PTR sys32_adjtimex + PTR compat_sys_setrlimit /* 6155 */ + PTR sys_chroot + PTR sys_sync + PTR sys_acct + PTR sys32_settimeofday + PTR sys_mount /* 6160 */ + PTR sys_umount + PTR sys_swapon + PTR sys_swapoff + PTR sys_reboot + PTR sys_sethostname /* 6165 */ + PTR sys_setdomainname + PTR sys_ni_syscall /* was create_module */ + PTR sys_init_module + PTR sys_delete_module + PTR sys_ni_syscall /* 6170, was get_kernel_syms */ + PTR sys_ni_syscall /* was query_module */ + PTR sys_quotactl + PTR sys_nfsservctl + PTR sys_ni_syscall /* res. for getpmsg */ + PTR sys_ni_syscall /* 6175 for putpmsg */ + PTR sys_ni_syscall /* res. for afs_syscall */ + PTR sys_ni_syscall /* res. for security */ + PTR sys_gettid + PTR sys32_readahead + PTR sys_setxattr /* 6180 */ + PTR sys_lsetxattr + PTR sys_fsetxattr + PTR sys_getxattr + PTR sys_lgetxattr + PTR sys_fgetxattr /* 6185 */ + PTR sys_listxattr + PTR sys_llistxattr + PTR sys_flistxattr + PTR sys_removexattr + PTR sys_lremovexattr /* 6190 */ + PTR sys_fremovexattr + PTR sys_tkill + PTR sys_ni_syscall + PTR compat_sys_futex + PTR compat_sys_sched_setaffinity /* 6195 */ + PTR compat_sys_sched_getaffinity + PTR sys_cacheflush + PTR sys_cachectl + PTR sys_sysmips + PTR sys_io_setup /* 6200 */ + PTR sys_io_destroy + PTR sys_io_getevents + PTR sys_io_submit + PTR sys_io_cancel + PTR sys_exit_group /* 6205 */ + PTR sys_lookup_dcookie + PTR sys_epoll_create + PTR sys_epoll_ctl + PTR sys_epoll_wait + PTR sys_remap_file_pages /* 6210 */ + PTR sysn32_rt_sigreturn + PTR sys_fcntl + PTR sys_set_tid_address + PTR sys_restart_syscall + PTR sys_semtimedop /* 6215 */ + PTR sys_fadvise64_64 + PTR compat_sys_statfs64 + PTR compat_sys_fstatfs64 + PTR sys_sendfile64 + PTR sys_timer_create /* 6220 */ + PTR sys_timer_settime + PTR sys_timer_gettime + PTR sys_timer_getoverrun + PTR sys_timer_delete + PTR sys_clock_settime /* 6225 */ + PTR sys_clock_gettime + PTR sys_clock_getres + PTR sys_clock_nanosleep + PTR sys_tgkill + PTR compat_sys_utimes /* 6230 */ + PTR sys_ni_syscall /* sys_mbind */ + PTR sys_ni_syscall /* sys_get_mempolicy */ + PTR sys_ni_syscall /* sys_set_mempolicy */ + PTR compat_sys_mq_open + PTR sys_mq_unlink /* 6235 */ + PTR compat_sys_mq_timedsend + PTR compat_sys_mq_timedreceive + PTR compat_sys_mq_notify + PTR compat_sys_mq_getsetattr + PTR sys_ni_syscall /* 6240, sys_vserver */ + PTR sys_waitid + PTR sys_ni_syscall /* available, was setaltroot */ + PTR sys_add_key + PTR sys_request_key + PTR sys_keyctl /* 6245 */ diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S new file mode 100644 index 000000000000..739f3998d76b --- /dev/null +++ b/arch/mips/kernel/scall64-o32.S @@ -0,0 +1,488 @@ +/* + * 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 - 2000, 2001 by Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * Copyright (C) 2001 MIPS Technologies, Inc. + * Copyright (C) 2004 Thiemo Seufer + * + * Hairy, the userspace application uses a different argument passing + * convention than the kernel, so we have to translate things from o32 + * to ABI64 calling convention. 64-bit syscalls are also processed + * here for now. + */ +#include <linux/config.h> +#include <linux/errno.h> +#include <asm/asm.h> +#include <asm/asmmacro.h> +#include <asm/mipsregs.h> +#include <asm/regdef.h> +#include <asm/stackframe.h> +#include <asm/thread_info.h> +#include <asm/unistd.h> +#include <asm/sysmips.h> + + .align 5 +NESTED(handle_sys, PT_SIZE, sp) + .set noat + SAVE_SOME + STI + .set at + ld t1, PT_EPC(sp) # skip syscall on return + + dsubu t0, v0, __NR_O32_Linux # check syscall number + sltiu t0, t0, __NR_O32_Linux_syscalls + 1 + daddiu t1, 4 # skip to next instruction + sd t1, PT_EPC(sp) + beqz t0, not_o32_scall +#if 0 + SAVE_ALL + move a1, v0 + PRINT("Scall %ld\n") + RESTORE_ALL +#endif + + /* We don't want to stumble over broken sign extensions from + userland. O32 does never use the upper half. */ + sll a0, a0, 0 + sll a1, a1, 0 + sll a2, a2, 0 + sll a3, a3, 0 + + dsll t0, v0, 3 # offset into table + ld t2, (sys_call_table - (__NR_O32_Linux * 8))(t0) + + sd a3, PT_R26(sp) # save a3 for syscall restarting + + /* + * More than four arguments. Try to deal with it by copying the + * stack arguments from the user stack to the kernel stack. + * This Sucks (TM). + * + * We intentionally keep the kernel stack a little below the top of + * userspace so we don't have to do a slower byte accurate check here. + */ + ld t0, PT_R29(sp) # get old user stack pointer + daddu t1, t0, 32 + bltz t1, bad_stack + +1: lw a4, 16(t0) # argument #5 from usp +2: lw a5, 20(t0) # argument #6 from usp +3: lw a6, 24(t0) # argument #7 from usp +4: lw a7, 28(t0) # argument #8 from usp (for indirect syscalls) + + .section __ex_table,"a" + PTR 1b, bad_stack + PTR 2b, bad_stack + PTR 3b, bad_stack + PTR 4b, bad_stack + .previous + + li t1, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT + LONG_L t0, TI_FLAGS($28) # syscall tracing enabled? + and t0, t1, t0 + bnez t0, trace_a_syscall + + jalr t2 # Do The Real Thing (TM) + + li t0, -EMAXERRNO - 1 # error? + sltu t0, t0, v0 + sd t0, PT_R7(sp) # set error flag + beqz t0, 1f + + dnegu v0 # error + sd v0, PT_R0(sp) # flag for syscall restarting +1: sd v0, PT_R2(sp) # result + +o32_syscall_exit: + local_irq_disable # make need_resched and + # signals dont change between + # sampling and return + LONG_L a2, TI_FLAGS($28) + li t0, _TIF_ALLWORK_MASK + and t0, a2, t0 + bnez t0, o32_syscall_exit_work + + j restore_partial + +o32_syscall_exit_work: + j syscall_exit_work_partial + +/* ------------------------------------------------------------------------ */ + +trace_a_syscall: + SAVE_STATIC + sd a4, PT_R8(sp) # Save argument registers + sd a5, PT_R9(sp) + sd a6, PT_R10(sp) + sd a7, PT_R11(sp) # For indirect syscalls + + move s0, t2 # Save syscall pointer + move a0, sp + li a1, 0 + jal do_syscall_trace + + ld a0, PT_R4(sp) # Restore argument registers + ld a1, PT_R5(sp) + ld a2, PT_R6(sp) + ld a3, PT_R7(sp) + ld a4, PT_R8(sp) + ld a5, PT_R9(sp) + ld a6, PT_R10(sp) + ld a7, PT_R11(sp) # For indirect syscalls + jalr s0 + + li t0, -EMAXERRNO - 1 # error? + sltu t0, t0, v0 + sd t0, PT_R7(sp) # set error flag + beqz t0, 1f + + dnegu v0 # error + sd v0, PT_R0(sp) # set flag for syscall restarting +1: sd v0, PT_R2(sp) # result + + j syscall_exit + +/* ------------------------------------------------------------------------ */ + + /* + * The stackpointer for a call with more than 4 arguments is bad. + */ +bad_stack: + dnegu v0 # error + sd v0, PT_R0(sp) + sd v0, PT_R2(sp) + li t0, 1 # set error flag + sd t0, PT_R7(sp) + j o32_syscall_exit + +not_o32_scall: + /* + * This is not an o32 compatibility syscall, pass it on + * to the 64-bit syscall handlers. + */ +#ifdef CONFIG_MIPS32_N32 + j handle_sysn32 +#else + j handle_sys64 +#endif + END(handle_sys) + +LEAF(sys32_syscall) + sltu v0, a0, __NR_O32_Linux + __NR_O32_Linux_syscalls + 1 + beqz v0, einval + + dsll v0, a0, 3 + ld t2, (sys_call_table - (__NR_O32_Linux * 8))(v0) + + li v1, 4000 # indirect syscall number + beq a0, v1, einval # do not recurse + + move a0, a1 # shift argument registers + move a1, a2 + move a2, a3 + move a3, a4 + move a4, a5 + move a5, a6 + move a6, a7 + sd a0, PT_R4(sp) # ... and push back a0 - a3, some + sd a1, PT_R5(sp) # syscalls expect them there + sd a2, PT_R6(sp) + sd a3, PT_R7(sp) + sd a3, PT_R26(sp) # update a3 for syscall restarting + jr t2 + /* Unreached */ + +einval: li v0, -EINVAL + jr ra + END(sys32_syscall) + + .align 3 + .type sys_call_table,@object +sys_call_table: + PTR sys32_syscall /* 4000 */ + PTR sys_exit + PTR sys_fork + PTR sys_read + PTR sys_write + PTR sys_open /* 4005 */ + PTR sys_close + PTR sys_waitpid + PTR sys_creat + PTR sys_link + PTR sys_unlink /* 4010 */ + PTR sys32_execve + PTR sys_chdir + PTR compat_sys_time + PTR sys_mknod + PTR sys_chmod /* 4015 */ + PTR sys_lchown + PTR sys_ni_syscall + PTR sys_ni_syscall /* was sys_stat */ + PTR sys_lseek + PTR sys_getpid /* 4020 */ + PTR sys_mount + PTR sys_oldumount + PTR sys_setuid + PTR sys_getuid + PTR compat_sys_stime /* 4025 */ + PTR sys32_ptrace + PTR sys_alarm + PTR sys_ni_syscall /* was sys_fstat */ + PTR sys_pause + PTR compat_sys_utime /* 4030 */ + PTR sys_ni_syscall + PTR sys_ni_syscall + PTR sys_access + PTR sys_nice + PTR sys_ni_syscall /* 4035 */ + PTR sys_sync + PTR sys_kill + PTR sys_rename + PTR sys_mkdir + PTR sys_rmdir /* 4040 */ + PTR sys_dup + PTR sys_pipe + PTR compat_sys_times + PTR sys_ni_syscall + PTR sys_brk /* 4045 */ + PTR sys_setgid + PTR sys_getgid + PTR sys_ni_syscall /* was signal 2 */ + PTR sys_geteuid + PTR sys_getegid /* 4050 */ + PTR sys_acct + PTR sys_umount + PTR sys_ni_syscall + PTR compat_sys_ioctl + PTR compat_sys_fcntl /* 4055 */ + PTR sys_ni_syscall + PTR sys_setpgid + PTR sys_ni_syscall + PTR sys_olduname + PTR sys_umask /* 4060 */ + PTR sys_chroot + PTR sys32_ustat + PTR sys_dup2 + PTR sys_getppid + PTR sys_getpgrp /* 4065 */ + PTR sys_setsid + PTR sys32_sigaction + PTR sys_sgetmask + PTR sys_ssetmask + PTR sys_setreuid /* 4070 */ + PTR sys_setregid + PTR sys32_sigsuspend + PTR compat_sys_sigpending + PTR sys_sethostname + PTR compat_sys_setrlimit /* 4075 */ + PTR compat_sys_getrlimit + PTR compat_sys_getrusage + PTR sys32_gettimeofday + PTR sys32_settimeofday + PTR sys_getgroups /* 4080 */ + PTR sys_setgroups + PTR sys_ni_syscall /* old_select */ + PTR sys_symlink + PTR sys_ni_syscall /* was sys_lstat */ + PTR sys_readlink /* 4085 */ + PTR sys_uselib + PTR sys_swapon + PTR sys_reboot + PTR sys32_readdir + PTR old_mmap /* 4090 */ + PTR sys_munmap + PTR sys_truncate + PTR sys_ftruncate + PTR sys_fchmod + PTR sys_fchown /* 4095 */ + PTR sys_getpriority + PTR sys_setpriority + PTR sys_ni_syscall + PTR compat_sys_statfs + PTR compat_sys_fstatfs /* 4100 */ + PTR sys_ni_syscall /* sys_ioperm */ + PTR sys32_socketcall + PTR sys_syslog + PTR compat_sys_setitimer + PTR compat_sys_getitimer /* 4105 */ + PTR compat_sys_newstat + PTR compat_sys_newlstat + PTR compat_sys_newfstat + PTR sys_uname + PTR sys_ni_syscall /* sys_ioperm *//* 4110 */ + PTR sys_vhangup + PTR sys_ni_syscall /* was sys_idle */ + PTR sys_ni_syscall /* sys_vm86 */ + PTR sys32_wait4 + PTR sys_swapoff /* 4115 */ + PTR sys32_sysinfo + PTR sys32_ipc + PTR sys_fsync + PTR sys32_sigreturn + PTR sys_clone /* 4120 */ + PTR sys_setdomainname + PTR sys32_newuname + PTR sys_ni_syscall /* sys_modify_ldt */ + PTR sys32_adjtimex + PTR sys_mprotect /* 4125 */ + PTR compat_sys_sigprocmask + PTR sys_ni_syscall /* was creat_module */ + PTR sys_init_module + PTR sys_delete_module + PTR sys_ni_syscall /* 4130, get_kernel_syms */ + PTR sys_quotactl + PTR sys_getpgid + PTR sys_fchdir + PTR sys_bdflush + PTR sys_sysfs /* 4135 */ + PTR sys32_personality + PTR sys_ni_syscall /* for afs_syscall */ + PTR sys_setfsuid + PTR sys_setfsgid + PTR sys32_llseek /* 4140 */ + PTR sys32_getdents + PTR compat_sys_select + PTR sys_flock + PTR sys_msync + PTR compat_sys_readv /* 4145 */ + PTR compat_sys_writev + PTR sys_cacheflush + PTR sys_cachectl + PTR sys_sysmips + PTR sys_ni_syscall /* 4150 */ + PTR sys_getsid + PTR sys_fdatasync + PTR sys32_sysctl + PTR sys_mlock + PTR sys_munlock /* 4155 */ + PTR sys_mlockall + PTR sys_munlockall + PTR sys_sched_setparam + PTR sys_sched_getparam + PTR sys_sched_setscheduler /* 4160 */ + PTR sys_sched_getscheduler + PTR sys_sched_yield + PTR sys_sched_get_priority_max + PTR sys_sched_get_priority_min + PTR sys32_sched_rr_get_interval /* 4165 */ + PTR compat_sys_nanosleep + PTR sys_mremap + PTR sys_accept + PTR sys_bind + PTR sys_connect /* 4170 */ + PTR sys_getpeername + PTR sys_getsockname + PTR sys_getsockopt + PTR sys_listen + PTR sys_recv /* 4175 */ + PTR sys_recvfrom + PTR compat_sys_recvmsg + PTR sys_send + PTR compat_sys_sendmsg + PTR sys_sendto /* 4180 */ + PTR compat_sys_setsockopt + PTR sys_shutdown + PTR sys_socket + PTR sys_socketpair + PTR sys_setresuid /* 4185 */ + PTR sys_getresuid + PTR sys_ni_syscall /* was query_module */ + PTR sys_poll + PTR sys_nfsservctl + PTR sys_setresgid /* 4190 */ + PTR sys_getresgid + PTR sys_prctl + PTR sys32_rt_sigreturn + PTR sys32_rt_sigaction + PTR sys32_rt_sigprocmask /* 4195 */ + PTR sys32_rt_sigpending + PTR compat_sys_rt_sigtimedwait + PTR sys32_rt_sigqueueinfo + PTR sys32_rt_sigsuspend + PTR sys32_pread /* 4200 */ + PTR sys32_pwrite + PTR sys_chown + PTR sys_getcwd + PTR sys_capget + PTR sys_capset /* 4205 */ + PTR sys32_sigaltstack + PTR sys32_sendfile + PTR sys_ni_syscall + PTR sys_ni_syscall + PTR sys32_mmap2 /* 4210 */ + PTR sys32_truncate64 + PTR sys32_ftruncate64 + PTR sys_newstat + PTR sys_newlstat + PTR sys_newfstat /* 4215 */ + PTR sys_pivot_root + PTR sys_mincore + PTR sys_madvise + PTR sys_getdents64 + PTR compat_sys_fcntl64 /* 4220 */ + PTR sys_ni_syscall + PTR sys_gettid + PTR sys32_readahead + PTR sys_setxattr + PTR sys_lsetxattr /* 4225 */ + PTR sys_fsetxattr + PTR sys_getxattr + PTR sys_lgetxattr + PTR sys_fgetxattr + PTR sys_listxattr /* 4230 */ + PTR sys_llistxattr + PTR sys_flistxattr + PTR sys_removexattr + PTR sys_lremovexattr + PTR sys_fremovexattr /* 4235 */ + PTR sys_tkill + PTR sys_sendfile64 + PTR compat_sys_futex + PTR compat_sys_sched_setaffinity + PTR compat_sys_sched_getaffinity /* 4240 */ + PTR sys_io_setup + PTR sys_io_destroy + PTR sys_io_getevents + PTR sys_io_submit + PTR sys_io_cancel /* 4245 */ + PTR sys_exit_group + PTR sys_lookup_dcookie + PTR sys_epoll_create + PTR sys_epoll_ctl + PTR sys_epoll_wait /* 4250 */ + PTR sys_remap_file_pages + PTR sys_set_tid_address + PTR sys_restart_syscall + PTR sys_fadvise64_64 + PTR compat_sys_statfs64 /* 4255 */ + PTR compat_sys_fstatfs64 + PTR sys_timer_create + PTR compat_sys_timer_settime + PTR compat_sys_timer_gettime + PTR sys_timer_getoverrun /* 4260 */ + PTR sys_timer_delete + PTR compat_sys_clock_settime + PTR compat_sys_clock_gettime + PTR compat_sys_clock_getres + PTR compat_sys_clock_nanosleep /* 4265 */ + PTR sys_tgkill + PTR compat_sys_utimes + PTR sys_ni_syscall /* sys_mbind */ + PTR sys_ni_syscall /* sys_get_mempolicy */ + PTR sys_ni_syscall /* 4270 sys_set_mempolicy */ + PTR compat_sys_mq_open + PTR sys_mq_unlink + PTR compat_sys_mq_timedsend + PTR compat_sys_mq_timedreceive + PTR compat_sys_mq_notify /* 4275 */ + PTR compat_sys_mq_getsetattr + PTR sys_ni_syscall /* sys_vserver */ + PTR sys_waitid + PTR sys_ni_syscall /* available, was setaltroot */ + PTR sys_add_key /* 4280 */ + PTR sys_request_key + PTR sys_keyctl + .size sys_call_table,.-sys_call_table diff --git a/arch/mips/kernel/semaphore.c b/arch/mips/kernel/semaphore.c new file mode 100644 index 000000000000..9c40fe5a8e8d --- /dev/null +++ b/arch/mips/kernel/semaphore.c @@ -0,0 +1,164 @@ +/* + * MIPS-specific semaphore code. + * + * Copyright (C) 1999 Cort Dougan <cort@cs.nmt.edu> + * Copyright (C) 2004 Ralf Baechle <ralf@linux-mips.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * April 2001 - Reworked by Paul Mackerras <paulus@samba.org> + * to eliminate the SMP races in the old version between the updates + * of `count' and `waking'. Now we use negative `count' values to + * indicate that some process(es) are waiting for the semaphore. + */ + +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <asm/atomic.h> +#include <asm/cpu-features.h> +#include <asm/errno.h> +#include <asm/semaphore.h> +#include <asm/war.h> +/* + * Atomically update sem->count. + * This does the equivalent of the following: + * + * old_count = sem->count; + * tmp = MAX(old_count, 0) + incr; + * sem->count = tmp; + * return old_count; + * + * On machines without lld/scd we need a spinlock to make the manipulation of + * sem->count and sem->waking atomic. Scalability isn't an issue because + * this lock is used on UP only so it's just an empty variable. + */ +static inline int __sem_update_count(struct semaphore *sem, int incr) +{ + int old_count, tmp; + + if (cpu_has_llsc && R10000_LLSC_WAR) { + __asm__ __volatile__( + "1: ll %0, %2 \n" + " sra %1, %0, 31 \n" + " not %1 \n" + " and %1, %0, %1 \n" + " add %1, %1, %3 \n" + " sc %1, %2 \n" + " beqzl %1, 1b \n" + : "=&r" (old_count), "=&r" (tmp), "=m" (sem->count) + : "r" (incr), "m" (sem->count)); + } else if (cpu_has_llsc) { + __asm__ __volatile__( + "1: ll %0, %2 \n" + " sra %1, %0, 31 \n" + " not %1 \n" + " and %1, %0, %1 \n" + " add %1, %1, %3 \n" + " sc %1, %2 \n" + " beqz %1, 1b \n" + : "=&r" (old_count), "=&r" (tmp), "=m" (sem->count) + : "r" (incr), "m" (sem->count)); + } else { + static DEFINE_SPINLOCK(semaphore_lock); + unsigned long flags; + + spin_lock_irqsave(&semaphore_lock, flags); + old_count = atomic_read(&sem->count); + tmp = max_t(int, old_count, 0) + incr; + atomic_set(&sem->count, tmp); + spin_unlock_irqrestore(&semaphore_lock, flags); + } + + return old_count; +} + +void __up(struct semaphore *sem) +{ + /* + * Note that we incremented count in up() before we came here, + * but that was ineffective since the result was <= 0, and + * any negative value of count is equivalent to 0. + * This ends up setting count to 1, unless count is now > 0 + * (i.e. because some other cpu has called up() in the meantime), + * in which case we just increment count. + */ + __sem_update_count(sem, 1); + wake_up(&sem->wait); +} + +EXPORT_SYMBOL(__up); + +/* + * Note that when we come in to __down or __down_interruptible, + * we have already decremented count, but that decrement was + * ineffective since the result was < 0, and any negative value + * of count is equivalent to 0. + * Thus it is only when we decrement count from some value > 0 + * that we have actually got the semaphore. + */ +void __sched __down(struct semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + __set_task_state(tsk, TASK_UNINTERRUPTIBLE); + add_wait_queue_exclusive(&sem->wait, &wait); + + /* + * Try to get the semaphore. If the count is > 0, then we've + * got the semaphore; we decrement count and exit the loop. + * If the count is 0 or negative, we set it to -1, indicating + * that we are asleep, and then sleep. + */ + while (__sem_update_count(sem, -1) <= 0) { + schedule(); + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + } + remove_wait_queue(&sem->wait, &wait); + __set_task_state(tsk, TASK_RUNNING); + + /* + * If there are any more sleepers, wake one of them up so + * that it can either get the semaphore, or set count to -1 + * indicating that there are still processes sleeping. + */ + wake_up(&sem->wait); +} + +EXPORT_SYMBOL(__down); + +int __sched __down_interruptible(struct semaphore * sem) +{ + int retval = 0; + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + __set_task_state(tsk, TASK_INTERRUPTIBLE); + add_wait_queue_exclusive(&sem->wait, &wait); + + while (__sem_update_count(sem, -1) <= 0) { + if (signal_pending(current)) { + /* + * A signal is pending - give up trying. + * Set sem->count to 0 if it is negative, + * since we are no longer sleeping. + */ + __sem_update_count(sem, 0); + retval = -EINTR; + break; + } + schedule(); + set_task_state(tsk, TASK_INTERRUPTIBLE); + } + remove_wait_queue(&sem->wait, &wait); + __set_task_state(tsk, TASK_RUNNING); + + wake_up(&sem->wait); + return retval; +} + +EXPORT_SYMBOL(__down_interruptible); diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c new file mode 100644 index 000000000000..6018ca25aceb --- /dev/null +++ b/arch/mips/kernel/setup.c @@ -0,0 +1,571 @@ +/* + * 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 Linus Torvalds + * Copyright (C) 1995 Waldorf Electronics + * Copyright (C) 1994, 95, 96, 97, 98, 99, 2000, 01, 02, 03 Ralf Baechle + * Copyright (C) 1996 Stoned Elipot + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) 2000 2001, 2002 Maciej W. Rozycki + */ +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/stddef.h> +#include <linux/string.h> +#include <linux/unistd.h> +#include <linux/slab.h> +#include <linux/user.h> +#include <linux/utsname.h> +#include <linux/a.out.h> +#include <linux/tty.h> +#include <linux/bootmem.h> +#include <linux/initrd.h> +#include <linux/major.h> +#include <linux/kdev_t.h> +#include <linux/root_dev.h> +#include <linux/highmem.h> +#include <linux/console.h> + +#include <asm/addrspace.h> +#include <asm/bootinfo.h> +#include <asm/cpu.h> +#include <asm/sections.h> +#include <asm/setup.h> +#include <asm/system.h> + +struct cpuinfo_mips cpu_data[NR_CPUS]; + +EXPORT_SYMBOL(cpu_data); + +#ifdef CONFIG_VT +struct screen_info screen_info; +#endif + +/* + * Despite it's name this variable is even if we don't have PCI + */ +unsigned int PCI_DMA_BUS_IS_PHYS; + +EXPORT_SYMBOL(PCI_DMA_BUS_IS_PHYS); + +/* + * Setup information + * + * These are initialized so they are in the .data section + */ +unsigned long mips_machtype = MACH_UNKNOWN; +unsigned long mips_machgroup = MACH_GROUP_UNKNOWN; + +EXPORT_SYMBOL(mips_machtype); +EXPORT_SYMBOL(mips_machgroup); + +struct boot_mem_map boot_mem_map; + +static char command_line[CL_SIZE]; + char arcs_cmdline[CL_SIZE]=CONFIG_CMDLINE; + +/* + * mips_io_port_base is the begin of the address space to which x86 style + * I/O ports are mapped. + */ +const unsigned long mips_io_port_base = -1; +EXPORT_SYMBOL(mips_io_port_base); + +/* + * isa_slot_offset is the address where E(ISA) busaddress 0 is mapped + * for the processor. + */ +unsigned long isa_slot_offset; +EXPORT_SYMBOL(isa_slot_offset); + +static struct resource code_resource = { .name = "Kernel code", }; +static struct resource data_resource = { .name = "Kernel data", }; + +void __init add_memory_region(phys_t start, phys_t size, long type) +{ + int x = boot_mem_map.nr_map; + struct boot_mem_map_entry *prev = boot_mem_map.map + x - 1; + + /* + * Try to merge with previous entry if any. This is far less than + * perfect but is sufficient for most real world cases. + */ + if (x && prev->addr + prev->size == start && prev->type == type) { + prev->size += size; + return; + } + + if (x == BOOT_MEM_MAP_MAX) { + printk("Ooops! Too many entries in the memory map!\n"); + return; + } + + boot_mem_map.map[x].addr = start; + boot_mem_map.map[x].size = size; + boot_mem_map.map[x].type = type; + boot_mem_map.nr_map++; +} + +static void __init print_memory_map(void) +{ + int i; + const int field = 2 * sizeof(unsigned long); + + for (i = 0; i < boot_mem_map.nr_map; i++) { + printk(" memory: %0*Lx @ %0*Lx ", + field, (unsigned long long) boot_mem_map.map[i].size, + field, (unsigned long long) boot_mem_map.map[i].addr); + + switch (boot_mem_map.map[i].type) { + case BOOT_MEM_RAM: + printk("(usable)\n"); + break; + case BOOT_MEM_ROM_DATA: + printk("(ROM data)\n"); + break; + case BOOT_MEM_RESERVED: + printk("(reserved)\n"); + break; + default: + printk("type %lu\n", boot_mem_map.map[i].type); + break; + } + } +} + +static inline void parse_cmdline_early(void) +{ + char c = ' ', *to = command_line, *from = saved_command_line; + unsigned long start_at, mem_size; + int len = 0; + int usermem = 0; + + printk("Determined physical RAM map:\n"); + print_memory_map(); + + for (;;) { + /* + * "mem=XXX[kKmM]" defines a memory region from + * 0 to <XXX>, overriding the determined size. + * "mem=XXX[KkmM]@YYY[KkmM]" defines a memory region from + * <YYY> to <YYY>+<XXX>, overriding the determined size. + */ + if (c == ' ' && !memcmp(from, "mem=", 4)) { + if (to != command_line) + to--; + /* + * If a user specifies memory size, we + * blow away any automatically generated + * size. + */ + if (usermem == 0) { + boot_mem_map.nr_map = 0; + usermem = 1; + } + mem_size = memparse(from + 4, &from); + if (*from == '@') + start_at = memparse(from + 1, &from); + else + start_at = 0; + add_memory_region(start_at, mem_size, BOOT_MEM_RAM); + } + c = *(from++); + if (!c) + break; + if (CL_SIZE <= ++len) + break; + *(to++) = c; + } + *to = '\0'; + + if (usermem) { + printk("User-defined physical RAM map:\n"); + print_memory_map(); + } +} + +static inline int parse_rd_cmdline(unsigned long* rd_start, unsigned long* rd_end) +{ + /* + * "rd_start=0xNNNNNNNN" defines the memory address of an initrd + * "rd_size=0xNN" it's size + */ + unsigned long start = 0; + unsigned long size = 0; + unsigned long end; + char cmd_line[CL_SIZE]; + char *start_str; + char *size_str; + char *tmp; + + strcpy(cmd_line, command_line); + *command_line = 0; + tmp = cmd_line; + /* Ignore "rd_start=" strings in other parameters. */ + start_str = strstr(cmd_line, "rd_start="); + if (start_str && start_str != cmd_line && *(start_str - 1) != ' ') + start_str = strstr(start_str, " rd_start="); + while (start_str) { + if (start_str != cmd_line) + strncat(command_line, tmp, start_str - tmp); + start = memparse(start_str + 9, &start_str); + tmp = start_str + 1; + start_str = strstr(start_str, " rd_start="); + } + if (*tmp) + strcat(command_line, tmp); + + strcpy(cmd_line, command_line); + *command_line = 0; + tmp = cmd_line; + /* Ignore "rd_size" strings in other parameters. */ + size_str = strstr(cmd_line, "rd_size="); + if (size_str && size_str != cmd_line && *(size_str - 1) != ' ') + size_str = strstr(size_str, " rd_size="); + while (size_str) { + if (size_str != cmd_line) + strncat(command_line, tmp, size_str - tmp); + size = memparse(size_str + 8, &size_str); + tmp = size_str + 1; + size_str = strstr(size_str, " rd_size="); + } + if (*tmp) + strcat(command_line, tmp); + +#ifdef CONFIG_MIPS64 + /* HACK: Guess if the sign extension was forgotten */ + if (start > 0x0000000080000000 && start < 0x00000000ffffffff) + start |= 0xffffffff00000000; +#endif + + end = start + size; + if (start && end) { + *rd_start = start; + *rd_end = end; + return 1; + } + return 0; +} + +#define PFN_UP(x) (((x) + PAGE_SIZE - 1) >> PAGE_SHIFT) +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) +#define PFN_PHYS(x) ((x) << PAGE_SHIFT) + +#define MAXMEM HIGHMEM_START +#define MAXMEM_PFN PFN_DOWN(MAXMEM) + +static inline void bootmem_init(void) +{ + unsigned long start_pfn; + unsigned long reserved_end = (unsigned long)&_end; +#ifndef CONFIG_SGI_IP27 + unsigned long first_usable_pfn; + unsigned long bootmap_size; + int i; +#endif +#ifdef CONFIG_BLK_DEV_INITRD + int initrd_reserve_bootmem = 0; + + /* Board specific code should have set up initrd_start and initrd_end */ + ROOT_DEV = Root_RAM0; + if (parse_rd_cmdline(&initrd_start, &initrd_end)) { + reserved_end = max(reserved_end, initrd_end); + initrd_reserve_bootmem = 1; + } else { + unsigned long tmp; + u32 *initrd_header; + + tmp = ((reserved_end + PAGE_SIZE-1) & PAGE_MASK) - sizeof(u32) * 2; + if (tmp < reserved_end) + tmp += PAGE_SIZE; + initrd_header = (u32 *)tmp; + if (initrd_header[0] == 0x494E5244) { + initrd_start = (unsigned long)&initrd_header[2]; + initrd_end = initrd_start + initrd_header[1]; + reserved_end = max(reserved_end, initrd_end); + initrd_reserve_bootmem = 1; + } + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + /* + * Partially used pages are not usable - thus + * we are rounding upwards. + */ + start_pfn = PFN_UP(CPHYSADDR(reserved_end)); + +#ifndef CONFIG_SGI_IP27 + /* Find the highest page frame number we have available. */ + max_pfn = 0; + first_usable_pfn = -1UL; + for (i = 0; i < boot_mem_map.nr_map; i++) { + unsigned long start, end; + + if (boot_mem_map.map[i].type != BOOT_MEM_RAM) + continue; + + start = PFN_UP(boot_mem_map.map[i].addr); + end = PFN_DOWN(boot_mem_map.map[i].addr + + boot_mem_map.map[i].size); + + if (start >= end) + continue; + if (end > max_pfn) + max_pfn = end; + if (start < first_usable_pfn) { + if (start > start_pfn) { + first_usable_pfn = start; + } else if (end > start_pfn) { + first_usable_pfn = start_pfn; + } + } + } + + /* + * Determine low and high memory ranges + */ + max_low_pfn = max_pfn; + if (max_low_pfn > MAXMEM_PFN) { + max_low_pfn = MAXMEM_PFN; +#ifndef CONFIG_HIGHMEM + /* Maximum memory usable is what is directly addressable */ + printk(KERN_WARNING "Warning only %ldMB will be used.\n", + MAXMEM >> 20); + printk(KERN_WARNING "Use a HIGHMEM enabled kernel.\n"); +#endif + } + +#ifdef CONFIG_HIGHMEM + /* + * Crude, we really should make a better attempt at detecting + * highstart_pfn + */ + highstart_pfn = highend_pfn = max_pfn; + if (max_pfn > MAXMEM_PFN) { + highstart_pfn = MAXMEM_PFN; + printk(KERN_NOTICE "%ldMB HIGHMEM available.\n", + (highend_pfn - highstart_pfn) >> (20 - PAGE_SHIFT)); + } +#endif + + /* Initialize the boot-time allocator with low memory only. */ + bootmap_size = init_bootmem(first_usable_pfn, max_low_pfn); + + /* + * Register fully available low RAM pages with the bootmem allocator. + */ + for (i = 0; i < boot_mem_map.nr_map; i++) { + unsigned long curr_pfn, last_pfn, size; + + /* + * Reserve usable memory. + */ + if (boot_mem_map.map[i].type != BOOT_MEM_RAM) + continue; + + /* + * We are rounding up the start address of usable memory: + */ + curr_pfn = PFN_UP(boot_mem_map.map[i].addr); + if (curr_pfn >= max_low_pfn) + continue; + if (curr_pfn < start_pfn) + curr_pfn = start_pfn; + + /* + * ... and at the end of the usable range downwards: + */ + last_pfn = PFN_DOWN(boot_mem_map.map[i].addr + + boot_mem_map.map[i].size); + + if (last_pfn > max_low_pfn) + last_pfn = max_low_pfn; + + /* + * Only register lowmem part of lowmem segment with bootmem. + */ + size = last_pfn - curr_pfn; + if (curr_pfn > PFN_DOWN(HIGHMEM_START)) + continue; + if (curr_pfn + size - 1 > PFN_DOWN(HIGHMEM_START)) + size = PFN_DOWN(HIGHMEM_START) - curr_pfn; + if (!size) + continue; + + /* + * ... finally, did all the rounding and playing + * around just make the area go away? + */ + if (last_pfn <= curr_pfn) + continue; + + /* Register lowmem ranges */ + free_bootmem(PFN_PHYS(curr_pfn), PFN_PHYS(size)); + } + + /* Reserve the bootmap memory. */ + reserve_bootmem(PFN_PHYS(first_usable_pfn), bootmap_size); +#endif /* CONFIG_SGI_IP27 */ + +#ifdef CONFIG_BLK_DEV_INITRD + initrd_below_start_ok = 1; + if (initrd_start) { + unsigned long initrd_size = ((unsigned char *)initrd_end) - ((unsigned char *)initrd_start); + printk("Initial ramdisk at: 0x%p (%lu bytes)\n", + (void *)initrd_start, initrd_size); + + if (CPHYSADDR(initrd_end) > PFN_PHYS(max_low_pfn)) { + printk("initrd extends beyond end of memory " + "(0x%0*Lx > 0x%0*Lx)\ndisabling initrd\n", + sizeof(long) * 2, + (unsigned long long)CPHYSADDR(initrd_end), + sizeof(long) * 2, + (unsigned long long)PFN_PHYS(max_low_pfn)); + initrd_start = initrd_end = 0; + initrd_reserve_bootmem = 0; + } + + if (initrd_reserve_bootmem) + reserve_bootmem(CPHYSADDR(initrd_start), initrd_size); + } +#endif /* CONFIG_BLK_DEV_INITRD */ +} + +static inline void resource_init(void) +{ + int i; + +#if defined(CONFIG_MIPS64) && !defined(CONFIG_BUILD_ELF64) + /* + * The 64bit code in 32bit object format trick can't represent + * 64bit wide relocations for linker script symbols. + */ + code_resource.start = CPHYSADDR(&_text); + code_resource.end = CPHYSADDR(&_etext) - 1; + data_resource.start = CPHYSADDR(&_etext); + data_resource.end = CPHYSADDR(&_edata) - 1; +#else + code_resource.start = virt_to_phys(&_text); + code_resource.end = virt_to_phys(&_etext) - 1; + data_resource.start = virt_to_phys(&_etext); + data_resource.end = virt_to_phys(&_edata) - 1; +#endif + + /* + * Request address space for all standard RAM. + */ + for (i = 0; i < boot_mem_map.nr_map; i++) { + struct resource *res; + unsigned long start, end; + + start = boot_mem_map.map[i].addr; + end = boot_mem_map.map[i].addr + boot_mem_map.map[i].size - 1; + if (start >= MAXMEM) + continue; + if (end >= MAXMEM) + end = MAXMEM - 1; + + res = alloc_bootmem(sizeof(struct resource)); + switch (boot_mem_map.map[i].type) { + case BOOT_MEM_RAM: + case BOOT_MEM_ROM_DATA: + res->name = "System RAM"; + break; + case BOOT_MEM_RESERVED: + default: + res->name = "reserved"; + } + + res->start = start; + res->end = end; + + res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; + request_resource(&iomem_resource, res); + + /* + * We don't know which RAM region contains kernel data, + * so we try it repeatedly and let the resource manager + * test it. + */ + request_resource(res, &code_resource); + request_resource(res, &data_resource); + } +} + +#undef PFN_UP +#undef PFN_DOWN +#undef PFN_PHYS + +#undef MAXMEM +#undef MAXMEM_PFN + +static int __initdata earlyinit_debug; + +static int __init earlyinit_debug_setup(char *str) +{ + earlyinit_debug = 1; + return 1; +} +__setup("earlyinit_debug", earlyinit_debug_setup); + +extern initcall_t __earlyinitcall_start, __earlyinitcall_end; + +static void __init do_earlyinitcalls(void) +{ + initcall_t *call, *start, *end; + + start = &__earlyinitcall_start; + end = &__earlyinitcall_end; + + for (call = start; call < end; call++) { + if (earlyinit_debug) + printk("calling earlyinitcall 0x%p\n", *call); + + (*call)(); + } +} + +void __init setup_arch(char **cmdline_p) +{ + cpu_probe(); + prom_init(); + cpu_report(); + +#if defined(CONFIG_VT) +#if defined(CONFIG_VGA_CONSOLE) + conswitchp = &vga_con; +#elif defined(CONFIG_DUMMY_CONSOLE) + conswitchp = &dummy_con; +#endif +#endif + + /* call board setup routine */ + do_earlyinitcalls(); + + strlcpy(command_line, arcs_cmdline, sizeof(command_line)); + strlcpy(saved_command_line, command_line, COMMAND_LINE_SIZE); + + *cmdline_p = command_line; + + parse_cmdline_early(); + bootmem_init(); + paging_init(); + resource_init(); +} + +int __init fpu_disable(char *s) +{ + cpu_data[0].options &= ~MIPS_CPU_FPU; + + return 1; +} + +__setup("nofpu", fpu_disable); diff --git a/arch/mips/kernel/signal-common.h b/arch/mips/kernel/signal-common.h new file mode 100644 index 000000000000..f9234df53253 --- /dev/null +++ b/arch/mips/kernel/signal-common.h @@ -0,0 +1,137 @@ +/* + * 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) 1991, 1992 Linus Torvalds + * Copyright (C) 1994 - 2000 Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + */ + +static inline int +setup_sigcontext(struct pt_regs *regs, struct sigcontext *sc) +{ + int err = 0; + + err |= __put_user(regs->cp0_epc, &sc->sc_pc); + err |= __put_user(regs->cp0_status, &sc->sc_status); + +#define save_gp_reg(i) do { \ + err |= __put_user(regs->regs[i], &sc->sc_regs[i]); \ +} while(0) + __put_user(0, &sc->sc_regs[0]); save_gp_reg(1); save_gp_reg(2); + save_gp_reg(3); save_gp_reg(4); save_gp_reg(5); save_gp_reg(6); + save_gp_reg(7); save_gp_reg(8); save_gp_reg(9); save_gp_reg(10); + save_gp_reg(11); save_gp_reg(12); save_gp_reg(13); save_gp_reg(14); + save_gp_reg(15); save_gp_reg(16); save_gp_reg(17); save_gp_reg(18); + save_gp_reg(19); save_gp_reg(20); save_gp_reg(21); save_gp_reg(22); + save_gp_reg(23); save_gp_reg(24); save_gp_reg(25); save_gp_reg(26); + save_gp_reg(27); save_gp_reg(28); save_gp_reg(29); save_gp_reg(30); + save_gp_reg(31); +#undef save_gp_reg + + err |= __put_user(regs->hi, &sc->sc_mdhi); + err |= __put_user(regs->lo, &sc->sc_mdlo); + err |= __put_user(regs->cp0_cause, &sc->sc_cause); + err |= __put_user(regs->cp0_badvaddr, &sc->sc_badvaddr); + + err |= __put_user(!!used_math(), &sc->sc_used_math); + + if (!used_math()) + goto out; + + /* + * Save FPU state to signal context. Signal handler will "inherit" + * current FPU state. + */ + preempt_disable(); + + if (!is_fpu_owner()) { + own_fpu(); + restore_fp(current); + } + err |= save_fp_context(sc); + + preempt_enable(); + +out: + return err; +} + +static inline int +restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc) +{ + int err = 0; + unsigned int used_math; + + /* Always make any pending restarted system calls return -EINTR */ + current_thread_info()->restart_block.fn = do_no_restart_syscall; + + err |= __get_user(regs->cp0_epc, &sc->sc_pc); + err |= __get_user(regs->hi, &sc->sc_mdhi); + err |= __get_user(regs->lo, &sc->sc_mdlo); + +#define restore_gp_reg(i) do { \ + err |= __get_user(regs->regs[i], &sc->sc_regs[i]); \ +} while(0) + restore_gp_reg( 1); restore_gp_reg( 2); restore_gp_reg( 3); + restore_gp_reg( 4); restore_gp_reg( 5); restore_gp_reg( 6); + restore_gp_reg( 7); restore_gp_reg( 8); restore_gp_reg( 9); + restore_gp_reg(10); restore_gp_reg(11); restore_gp_reg(12); + restore_gp_reg(13); restore_gp_reg(14); restore_gp_reg(15); + restore_gp_reg(16); restore_gp_reg(17); restore_gp_reg(18); + restore_gp_reg(19); restore_gp_reg(20); restore_gp_reg(21); + restore_gp_reg(22); restore_gp_reg(23); restore_gp_reg(24); + restore_gp_reg(25); restore_gp_reg(26); restore_gp_reg(27); + restore_gp_reg(28); restore_gp_reg(29); restore_gp_reg(30); + restore_gp_reg(31); +#undef restore_gp_reg + + err |= __get_user(used_math, &sc->sc_used_math); + conditional_used_math(used_math); + + preempt_disable(); + + if (used_math()) { + /* restore fpu context if we have used it before */ + own_fpu(); + err |= restore_fp_context(sc); + } else { + /* signal handler may have used FPU. Give it up. */ + lose_fpu(); + } + + preempt_enable(); + + return err; +} + +/* + * Determine which stack to use.. + */ +static inline void * +get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size) +{ + unsigned long sp, almask; + + /* Default to using normal stack */ + sp = regs->regs[29]; + + /* + * FPU emulator may have it's own trampoline active just + * above the user stack, 16-bytes before the next lowest + * 16 byte boundary. Try to avoid trashing it. + */ + sp -= 32; + + /* This is the X/Open sanctioned signal stack switching. */ + if ((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags (sp) == 0)) + sp = current->sas_ss_sp + current->sas_ss_size; + + if (PLAT_TRAMPOLINE_STUFF_LINE) + almask = ~(PLAT_TRAMPOLINE_STUFF_LINE - 1); + else + almask = ALMASK; + + return (void *)((sp - frame_size) & almask); +} diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c new file mode 100644 index 000000000000..508026ae5842 --- /dev/null +++ b/arch/mips/kernel/signal.c @@ -0,0 +1,517 @@ +/* + * 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) 1991, 1992 Linus Torvalds + * Copyright (C) 1994 - 2000 Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + */ +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/personality.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/unistd.h> +#include <linux/compiler.h> + +#include <asm/asm.h> +#include <linux/bitops.h> +#include <asm/cacheflush.h> +#include <asm/fpu.h> +#include <asm/sim.h> +#include <asm/uaccess.h> +#include <asm/ucontext.h> +#include <asm/cpu-features.h> + +#include "signal-common.h" + +#define DEBUG_SIG 0 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +static int do_signal(sigset_t *oldset, struct pt_regs *regs); + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ + +#ifdef CONFIG_TRAD_SIGNALS +save_static_function(sys_sigsuspend); +__attribute_used__ noinline static int +_sys_sigsuspend(nabi_no_regargs struct pt_regs regs) +{ + sigset_t *uset, saveset, newset; + + uset = (sigset_t *) regs.regs[4]; + if (copy_from_user(&newset, uset, sizeof(sigset_t))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + regs.regs[2] = EINTR; + regs.regs[7] = 1; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(&saveset, ®s)) + return -EINTR; + } +} +#endif + +save_static_function(sys_rt_sigsuspend); +__attribute_used__ noinline static int +_sys_rt_sigsuspend(nabi_no_regargs struct pt_regs regs) +{ + sigset_t *unewset, saveset, newset; + size_t sigsetsize; + + /* XXX Don't preclude handling different sized sigset_t's. */ + sigsetsize = regs.regs[5]; + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + unewset = (sigset_t *) regs.regs[4]; + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + regs.regs[2] = EINTR; + regs.regs[7] = 1; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(&saveset, ®s)) + return -EINTR; + } +} + +#ifdef CONFIG_TRAD_SIGNALS +asmlinkage int sys_sigaction(int sig, const struct sigaction *act, + struct sigaction *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + int err = 0; + + if (act) { + old_sigset_t mask; + + if (!access_ok(VERIFY_READ, act, sizeof(*act))) + return -EFAULT; + err |= __get_user(new_ka.sa.sa_handler, &act->sa_handler); + err |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); + err |= __get_user(mask, &act->sa_mask.sig[0]); + if (err) + return -EFAULT; + + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact))) + return -EFAULT; + err |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + err |= __put_user(old_ka.sa.sa_handler, &oact->sa_handler); + err |= __put_user(old_ka.sa.sa_mask.sig[0], oact->sa_mask.sig); + err |= __put_user(0, &oact->sa_mask.sig[1]); + err |= __put_user(0, &oact->sa_mask.sig[2]); + err |= __put_user(0, &oact->sa_mask.sig[3]); + if (err) + return -EFAULT; + } + + return ret; +} +#endif + +asmlinkage int sys_sigaltstack(nabi_no_regargs struct pt_regs regs) +{ + const stack_t *uss = (const stack_t *) regs.regs[4]; + stack_t *uoss = (stack_t *) regs.regs[5]; + unsigned long usp = regs.regs[29]; + + return do_sigaltstack(uss, uoss, usp); +} + +#if PLAT_TRAMPOLINE_STUFF_LINE +#define __tramp __attribute__((aligned(PLAT_TRAMPOLINE_STUFF_LINE))) +#else +#define __tramp +#endif + +#ifdef CONFIG_TRAD_SIGNALS +struct sigframe { + u32 sf_ass[4]; /* argument save space for o32 */ + u32 sf_code[2] __tramp; /* signal trampoline */ + struct sigcontext sf_sc __tramp; + sigset_t sf_mask; +}; +#endif + +struct rt_sigframe { + u32 rs_ass[4]; /* argument save space for o32 */ + u32 rs_code[2] __tramp; /* signal trampoline */ + struct siginfo rs_info __tramp; + struct ucontext rs_uc; +}; + +#ifdef CONFIG_TRAD_SIGNALS +save_static_function(sys_sigreturn); +__attribute_used__ noinline static void +_sys_sigreturn(nabi_no_regargs struct pt_regs regs) +{ + struct sigframe *frame; + sigset_t blocked; + + frame = (struct sigframe *) regs.regs[29]; + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&blocked, &frame->sf_mask, sizeof(blocked))) + goto badframe; + + sigdelsetmask(&blocked, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = blocked; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(®s, &frame->sf_sc)) + goto badframe; + + /* + * Don't let your children do this ... + */ + if (current_thread_info()->flags & TIF_SYSCALL_TRACE) + do_syscall_trace(®s, 1); + __asm__ __volatile__( + "move\t$29, %0\n\t" + "j\tsyscall_exit" + :/* no outputs */ + :"r" (®s)); + /* Unreached */ + +badframe: + force_sig(SIGSEGV, current); +} +#endif + +save_static_function(sys_rt_sigreturn); +__attribute_used__ noinline static void +_sys_rt_sigreturn(nabi_no_regargs struct pt_regs regs) +{ + struct rt_sigframe *frame; + sigset_t set; + stack_t st; + + frame = (struct rt_sigframe *) regs.regs[29]; + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->rs_uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(®s, &frame->rs_uc.uc_mcontext)) + goto badframe; + + if (__copy_from_user(&st, &frame->rs_uc.uc_stack, sizeof(st))) + goto badframe; + /* It is more difficult to avoid calling this function than to + call it and ignore errors. */ + do_sigaltstack(&st, NULL, regs.regs[29]); + + /* + * Don't let your children do this ... + */ + __asm__ __volatile__( + "move\t$29, %0\n\t" + "j\tsyscall_exit" + :/* no outputs */ + :"r" (®s)); + /* Unreached */ + +badframe: + force_sig(SIGSEGV, current); +} + +#ifdef CONFIG_TRAD_SIGNALS +static void inline setup_frame(struct k_sigaction * ka, struct pt_regs *regs, + int signr, sigset_t *set) +{ + struct sigframe *frame; + int err = 0; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) + goto give_sigsegv; + + /* + * Set up the return code ... + * + * li v0, __NR_sigreturn + * syscall + */ + if (PLAT_TRAMPOLINE_STUFF_LINE) + __clear_user(frame->sf_code, PLAT_TRAMPOLINE_STUFF_LINE); + err |= __put_user(0x24020000 + __NR_sigreturn, frame->sf_code + 0); + err |= __put_user(0x0000000c , frame->sf_code + 1); + flush_cache_sigtramp((unsigned long) frame->sf_code); + + err |= setup_sigcontext(regs, &frame->sf_sc); + err |= __copy_to_user(&frame->sf_mask, set, sizeof(*set)); + if (err) + goto give_sigsegv; + + /* + * Arguments to signal handler: + * + * a0 = signal number + * a1 = 0 (should be cause) + * a2 = pointer to struct sigcontext + * + * $25 and c0_epc point to the signal handler, $29 points to the + * struct sigframe. + */ + regs->regs[ 4] = signr; + regs->regs[ 5] = 0; + regs->regs[ 6] = (unsigned long) &frame->sf_sc; + regs->regs[29] = (unsigned long) frame; + regs->regs[31] = (unsigned long) frame->sf_code; + regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler; + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%p\n", + current->comm, current->pid, + frame, regs->cp0_epc, frame->regs[31]); +#endif + return; + +give_sigsegv: + force_sigsegv(signr, current); +} +#endif + +static void inline setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, + int signr, sigset_t *set, siginfo_t *info) +{ + struct rt_sigframe *frame; + int err = 0; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) + goto give_sigsegv; + + /* + * Set up the return code ... + * + * li v0, __NR_rt_sigreturn + * syscall + */ + if (PLAT_TRAMPOLINE_STUFF_LINE) + __clear_user(frame->rs_code, PLAT_TRAMPOLINE_STUFF_LINE); + err |= __put_user(0x24020000 + __NR_rt_sigreturn, frame->rs_code + 0); + err |= __put_user(0x0000000c , frame->rs_code + 1); + flush_cache_sigtramp((unsigned long) frame->rs_code); + + /* Create siginfo. */ + err |= copy_siginfo_to_user(&frame->rs_info, info); + + /* Create the ucontext. */ + err |= __put_user(0, &frame->rs_uc.uc_flags); + err |= __put_user(0, &frame->rs_uc.uc_link); + err |= __put_user((void *)current->sas_ss_sp, + &frame->rs_uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(regs->regs[29]), + &frame->rs_uc.uc_stack.ss_flags); + err |= __put_user(current->sas_ss_size, + &frame->rs_uc.uc_stack.ss_size); + err |= setup_sigcontext(regs, &frame->rs_uc.uc_mcontext); + err |= __copy_to_user(&frame->rs_uc.uc_sigmask, set, sizeof(*set)); + + if (err) + goto give_sigsegv; + + /* + * Arguments to signal handler: + * + * a0 = signal number + * a1 = 0 (should be cause) + * a2 = pointer to ucontext + * + * $25 and c0_epc point to the signal handler, $29 points to + * the struct rt_sigframe. + */ + regs->regs[ 4] = signr; + regs->regs[ 5] = (unsigned long) &frame->rs_info; + regs->regs[ 6] = (unsigned long) &frame->rs_uc; + regs->regs[29] = (unsigned long) frame; + regs->regs[31] = (unsigned long) frame->rs_code; + regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler; + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%p\n", + current->comm, current->pid, + frame, regs->cp0_epc, regs->regs[31]); +#endif + return; + +give_sigsegv: + force_sigsegv(signr, current); +} + +extern void setup_rt_frame_n32(struct k_sigaction * ka, + struct pt_regs *regs, int signr, sigset_t *set, siginfo_t *info); + +static inline void handle_signal(unsigned long sig, siginfo_t *info, + struct k_sigaction *ka, sigset_t *oldset, struct pt_regs *regs) +{ + switch(regs->regs[0]) { + case ERESTART_RESTARTBLOCK: + case ERESTARTNOHAND: + regs->regs[2] = EINTR; + break; + case ERESTARTSYS: + if(!(ka->sa.sa_flags & SA_RESTART)) { + regs->regs[2] = EINTR; + break; + } + /* fallthrough */ + case ERESTARTNOINTR: /* Userland will reload $v0. */ + regs->regs[7] = regs->regs[26]; + regs->cp0_epc -= 8; + } + + regs->regs[0] = 0; /* Don't deal with this again. */ + +#ifdef CONFIG_TRAD_SIGNALS + if (ka->sa.sa_flags & SA_SIGINFO) { +#else + if (1) { +#endif +#ifdef CONFIG_MIPS32_N32 + if ((current->thread.mflags & MF_ABI_MASK) == MF_N32) + setup_rt_frame_n32 (ka, regs, sig, oldset, info); + else +#endif + setup_rt_frame(ka, regs, sig, oldset, info); + } +#ifdef CONFIG_TRAD_SIGNALS + else + setup_frame(ka, regs, sig, oldset); +#endif + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } +} + +extern int do_signal32(sigset_t *oldset, struct pt_regs *regs); +extern int do_irix_signal(sigset_t *oldset, struct pt_regs *regs); + +static int do_signal(sigset_t *oldset, struct pt_regs *regs) +{ + struct k_sigaction ka; + siginfo_t info; + int signr; + +#ifdef CONFIG_BINFMT_ELF32 + if ((current->thread.mflags & MF_ABI_MASK) == MF_O32) { + return do_signal32(oldset, regs); + } +#endif + + /* + * We want the common case to go fast, which is why we may in certain + * cases get here from kernel mode. Just return without doing anything + * if so. + */ + if (!user_mode(regs)) + return 1; + + if (try_to_freeze(0)) + goto no_signal; + + if (!oldset) + oldset = ¤t->blocked; + + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + if (signr > 0) { + handle_signal(signr, &info, &ka, oldset, regs); + return 1; + } + +no_signal: + /* + * Who's code doesn't conform to the restartable syscall convention + * dies here!!! The li instruction, a single machine instruction, + * must directly be followed by the syscall instruction. + */ + if (regs->regs[0]) { + if (regs->regs[2] == ERESTARTNOHAND || + regs->regs[2] == ERESTARTSYS || + regs->regs[2] == ERESTARTNOINTR) { + regs->regs[7] = regs->regs[26]; + regs->cp0_epc -= 8; + } + if (regs->regs[2] == ERESTART_RESTARTBLOCK) { + regs->regs[2] = __NR_restart_syscall; + regs->regs[7] = regs->regs[26]; + regs->cp0_epc -= 4; + } + } + return 0; +} + +/* + * notification of userspace execution resumption + * - triggered by current->work.notify_resume + */ +asmlinkage void do_notify_resume(struct pt_regs *regs, sigset_t *oldset, + __u32 thread_info_flags) +{ + /* deal with pending signal delivery */ + if (thread_info_flags & _TIF_SIGPENDING) { +#ifdef CONFIG_BINFMT_ELF32 + if (likely((current->thread.mflags & MF_ABI_MASK) == MF_O32)) { + do_signal32(oldset, regs); + return; + } +#endif +#ifdef CONFIG_BINFMT_IRIX + if (unlikely(current->personality != PER_LINUX)) { + do_irix_signal(oldset, regs); + return; + } +#endif + do_signal(oldset, regs); + } +} diff --git a/arch/mips/kernel/signal32.c b/arch/mips/kernel/signal32.c new file mode 100644 index 000000000000..1f3b19124c01 --- /dev/null +++ b/arch/mips/kernel/signal32.c @@ -0,0 +1,905 @@ +/* + * 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) 1991, 1992 Linus Torvalds + * Copyright (C) 1994 - 2000 Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + */ +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/syscalls.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/compat.h> +#include <linux/suspend.h> +#include <linux/compiler.h> + +#include <asm/asm.h> +#include <linux/bitops.h> +#include <asm/cacheflush.h> +#include <asm/sim.h> +#include <asm/uaccess.h> +#include <asm/ucontext.h> +#include <asm/system.h> +#include <asm/fpu.h> + +#define SI_PAD_SIZE32 ((SI_MAX_SIZE/sizeof(int)) - 3) + +typedef struct compat_siginfo { + int si_signo; + int si_code; + int si_errno; + + union { + int _pad[SI_PAD_SIZE32]; + + /* kill() */ + struct { + compat_pid_t _pid; /* sender's pid */ + compat_uid_t _uid; /* sender's uid */ + } _kill; + + /* SIGCHLD */ + struct { + compat_pid_t _pid; /* which child */ + compat_uid_t _uid; /* sender's uid */ + int _status; /* exit code */ + compat_clock_t _utime; + compat_clock_t _stime; + } _sigchld; + + /* IRIX SIGCHLD */ + struct { + compat_pid_t _pid; /* which child */ + compat_clock_t _utime; + int _status; /* exit code */ + compat_clock_t _stime; + } _irix_sigchld; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ + struct { + s32 _addr; /* faulting insn/memory ref. */ + } _sigfault; + + /* SIGPOLL, SIGXFSZ (To do ...) */ + struct { + int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ + int _fd; + } _sigpoll; + + /* POSIX.1b timers */ + struct { + unsigned int _timer1; + unsigned int _timer2; + } _timer; + + /* POSIX.1b signals */ + struct { + compat_pid_t _pid; /* sender's pid */ + compat_uid_t _uid; /* sender's uid */ + compat_sigval_t _sigval; + } _rt; + + } _sifields; +} compat_siginfo_t; + +/* + * Including <asm/unistd.h> would give use the 64-bit syscall numbers ... + */ +#define __NR_O32_sigreturn 4119 +#define __NR_O32_rt_sigreturn 4193 +#define __NR_O32_restart_syscall 4253 + +#define DEBUG_SIG 0 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +extern int do_signal32(sigset_t *oldset, struct pt_regs *regs); + +/* 32-bit compatibility types */ + +#define _NSIG_BPW32 32 +#define _NSIG_WORDS32 (_NSIG / _NSIG_BPW32) + +typedef struct { + unsigned int sig[_NSIG_WORDS32]; +} sigset_t32; + +typedef unsigned int __sighandler32_t; +typedef void (*vfptr_t)(void); + +struct sigaction32 { + unsigned int sa_flags; + __sighandler32_t sa_handler; + compat_sigset_t sa_mask; +}; + +/* IRIX compatible stack_t */ +typedef struct sigaltstack32 { + s32 ss_sp; + compat_size_t ss_size; + int ss_flags; +} stack32_t; + +struct ucontext32 { + u32 uc_flags; + s32 uc_link; + stack32_t uc_stack; + struct sigcontext32 uc_mcontext; + sigset_t32 uc_sigmask; /* mask last for extensibility */ +}; + +extern void __put_sigset_unknown_nsig(void); +extern void __get_sigset_unknown_nsig(void); + +static inline int put_sigset(const sigset_t *kbuf, compat_sigset_t *ubuf) +{ + int err = 0; + + if (!access_ok(VERIFY_WRITE, ubuf, sizeof(*ubuf))) + return -EFAULT; + + switch (_NSIG_WORDS) { + default: + __put_sigset_unknown_nsig(); + case 2: + err |= __put_user (kbuf->sig[1] >> 32, &ubuf->sig[3]); + err |= __put_user (kbuf->sig[1] & 0xffffffff, &ubuf->sig[2]); + case 1: + err |= __put_user (kbuf->sig[0] >> 32, &ubuf->sig[1]); + err |= __put_user (kbuf->sig[0] & 0xffffffff, &ubuf->sig[0]); + } + + return err; +} + +static inline int get_sigset(sigset_t *kbuf, const compat_sigset_t *ubuf) +{ + int err = 0; + unsigned long sig[4]; + + if (!access_ok(VERIFY_READ, ubuf, sizeof(*ubuf))) + return -EFAULT; + + switch (_NSIG_WORDS) { + default: + __get_sigset_unknown_nsig(); + case 2: + err |= __get_user (sig[3], &ubuf->sig[3]); + err |= __get_user (sig[2], &ubuf->sig[2]); + kbuf->sig[1] = sig[2] | (sig[3] << 32); + case 1: + err |= __get_user (sig[1], &ubuf->sig[1]); + err |= __get_user (sig[0], &ubuf->sig[0]); + kbuf->sig[0] = sig[0] | (sig[1] << 32); + } + + return err; +} + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ + +save_static_function(sys32_sigsuspend); +__attribute_used__ noinline static int +_sys32_sigsuspend(nabi_no_regargs struct pt_regs regs) +{ + compat_sigset_t *uset; + sigset_t newset, saveset; + + uset = (compat_sigset_t *) regs.regs[4]; + if (get_sigset(&newset, uset)) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + regs.regs[2] = EINTR; + regs.regs[7] = 1; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal32(&saveset, ®s)) + return -EINTR; + } +} + +save_static_function(sys32_rt_sigsuspend); +__attribute_used__ noinline static int +_sys32_rt_sigsuspend(nabi_no_regargs struct pt_regs regs) +{ + compat_sigset_t *uset; + sigset_t newset, saveset; + size_t sigsetsize; + + /* XXX Don't preclude handling different sized sigset_t's. */ + sigsetsize = regs.regs[5]; + if (sigsetsize != sizeof(compat_sigset_t)) + return -EINVAL; + + uset = (compat_sigset_t *) regs.regs[4]; + if (get_sigset(&newset, uset)) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + regs.regs[2] = EINTR; + regs.regs[7] = 1; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal32(&saveset, ®s)) + return -EINTR; + } +} + +asmlinkage int sys32_sigaction(int sig, const struct sigaction32 *act, + struct sigaction32 *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + int err = 0; + + if (act) { + old_sigset_t mask; + + if (!access_ok(VERIFY_READ, act, sizeof(*act))) + return -EFAULT; + err |= __get_user((u32)(u64)new_ka.sa.sa_handler, + &act->sa_handler); + err |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); + err |= __get_user(mask, &act->sa_mask.sig[0]); + if (err) + return -EFAULT; + + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact))) + return -EFAULT; + err |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + err |= __put_user((u32)(u64)old_ka.sa.sa_handler, + &oact->sa_handler); + err |= __put_user(old_ka.sa.sa_mask.sig[0], oact->sa_mask.sig); + err |= __put_user(0, &oact->sa_mask.sig[1]); + err |= __put_user(0, &oact->sa_mask.sig[2]); + err |= __put_user(0, &oact->sa_mask.sig[3]); + if (err) + return -EFAULT; + } + + return ret; +} + +asmlinkage int sys32_sigaltstack(nabi_no_regargs struct pt_regs regs) +{ + const stack32_t *uss = (const stack32_t *) regs.regs[4]; + stack32_t *uoss = (stack32_t *) regs.regs[5]; + unsigned long usp = regs.regs[29]; + stack_t kss, koss; + int ret, err = 0; + mm_segment_t old_fs = get_fs(); + s32 sp; + + if (uss) { + if (!access_ok(VERIFY_READ, uss, sizeof(*uss))) + return -EFAULT; + err |= __get_user(sp, &uss->ss_sp); + kss.ss_sp = (void *) (long) sp; + err |= __get_user(kss.ss_size, &uss->ss_size); + err |= __get_user(kss.ss_flags, &uss->ss_flags); + if (err) + return -EFAULT; + } + + set_fs (KERNEL_DS); + ret = do_sigaltstack(uss ? &kss : NULL , uoss ? &koss : NULL, usp); + set_fs (old_fs); + + if (!ret && uoss) { + if (!access_ok(VERIFY_WRITE, uoss, sizeof(*uoss))) + return -EFAULT; + sp = (int) (long) koss.ss_sp; + err |= __put_user(sp, &uoss->ss_sp); + err |= __put_user(koss.ss_size, &uoss->ss_size); + err |= __put_user(koss.ss_flags, &uoss->ss_flags); + if (err) + return -EFAULT; + } + return ret; +} + +static int restore_sigcontext32(struct pt_regs *regs, struct sigcontext32 *sc) +{ + int err = 0; + __u32 used_math; + + /* Always make any pending restarted system calls return -EINTR */ + current_thread_info()->restart_block.fn = do_no_restart_syscall; + + err |= __get_user(regs->cp0_epc, &sc->sc_pc); + err |= __get_user(regs->hi, &sc->sc_mdhi); + err |= __get_user(regs->lo, &sc->sc_mdlo); + +#define restore_gp_reg(i) do { \ + err |= __get_user(regs->regs[i], &sc->sc_regs[i]); \ +} while(0) + restore_gp_reg( 1); restore_gp_reg( 2); restore_gp_reg( 3); + restore_gp_reg( 4); restore_gp_reg( 5); restore_gp_reg( 6); + restore_gp_reg( 7); restore_gp_reg( 8); restore_gp_reg( 9); + restore_gp_reg(10); restore_gp_reg(11); restore_gp_reg(12); + restore_gp_reg(13); restore_gp_reg(14); restore_gp_reg(15); + restore_gp_reg(16); restore_gp_reg(17); restore_gp_reg(18); + restore_gp_reg(19); restore_gp_reg(20); restore_gp_reg(21); + restore_gp_reg(22); restore_gp_reg(23); restore_gp_reg(24); + restore_gp_reg(25); restore_gp_reg(26); restore_gp_reg(27); + restore_gp_reg(28); restore_gp_reg(29); restore_gp_reg(30); + restore_gp_reg(31); +#undef restore_gp_reg + + err |= __get_user(used_math, &sc->sc_used_math); + conditional_used_math(used_math); + + preempt_disable(); + + if (used_math()) { + /* restore fpu context if we have used it before */ + own_fpu(); + err |= restore_fp_context32(sc); + } else { + /* signal handler may have used FPU. Give it up. */ + lose_fpu(); + } + + preempt_enable(); + + return err; +} + +struct sigframe { + u32 sf_ass[4]; /* argument save space for o32 */ + u32 sf_code[2]; /* signal trampoline */ + struct sigcontext32 sf_sc; + sigset_t sf_mask; +}; + +struct rt_sigframe32 { + u32 rs_ass[4]; /* argument save space for o32 */ + u32 rs_code[2]; /* signal trampoline */ + compat_siginfo_t rs_info; + struct ucontext32 rs_uc; +}; + +int copy_siginfo_to_user32(compat_siginfo_t *to, siginfo_t *from) +{ + int err; + + if (!access_ok (VERIFY_WRITE, to, sizeof(compat_siginfo_t))) + return -EFAULT; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. + This routine must convert siginfo from 64bit to 32bit as well + at the same time. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + if (from->si_code < 0) + err |= __copy_to_user(&to->_sifields._pad, &from->_sifields._pad, SI_PAD_SIZE); + else { + switch (from->si_code >> 16) { + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_pid, &to->si_pid); + err |= __put_user(from->si_uid, &to->si_uid); + break; + case __SI_FAULT >> 16: + err |= __put_user((long)from->si_addr, &to->si_addr); + break; + case __SI_POLL >> 16: + err |= __put_user(from->si_band, &to->si_band); + err |= __put_user(from->si_fd, &to->si_fd); + break; + case __SI_RT >> 16: /* This is not generated by the kernel as of now. */ + case __SI_MESGQ >> 16: + err |= __put_user(from->si_pid, &to->si_pid); + err |= __put_user(from->si_uid, &to->si_uid); + err |= __put_user(from->si_int, &to->si_int); + break; + } + } + return err; +} + +save_static_function(sys32_sigreturn); +__attribute_used__ noinline static void +_sys32_sigreturn(nabi_no_regargs struct pt_regs regs) +{ + struct sigframe *frame; + sigset_t blocked; + + frame = (struct sigframe *) regs.regs[29]; + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&blocked, &frame->sf_mask, sizeof(blocked))) + goto badframe; + + sigdelsetmask(&blocked, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = blocked; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext32(®s, &frame->sf_sc)) + goto badframe; + + /* + * Don't let your children do this ... + */ + if (current_thread_info()->flags & TIF_SYSCALL_TRACE) + do_syscall_trace(®s, 1); + __asm__ __volatile__( + "move\t$29, %0\n\t" + "j\tsyscall_exit" + :/* no outputs */ + :"r" (®s)); + /* Unreached */ + +badframe: + force_sig(SIGSEGV, current); +} + +save_static_function(sys32_rt_sigreturn); +__attribute_used__ noinline static void +_sys32_rt_sigreturn(nabi_no_regargs struct pt_regs regs) +{ + struct rt_sigframe32 *frame; + sigset_t set; + stack_t st; + s32 sp; + + frame = (struct rt_sigframe32 *) regs.regs[29]; + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->rs_uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext32(®s, &frame->rs_uc.uc_mcontext)) + goto badframe; + + /* The ucontext contains a stack32_t, so we must convert! */ + if (__get_user(sp, &frame->rs_uc.uc_stack.ss_sp)) + goto badframe; + st.ss_size = (long) sp; + if (__get_user(st.ss_size, &frame->rs_uc.uc_stack.ss_size)) + goto badframe; + if (__get_user(st.ss_flags, &frame->rs_uc.uc_stack.ss_flags)) + goto badframe; + + /* It is more difficult to avoid calling this function than to + call it and ignore errors. */ + do_sigaltstack(&st, NULL, regs.regs[29]); + + /* + * Don't let your children do this ... + */ + __asm__ __volatile__( + "move\t$29, %0\n\t" + "j\tsyscall_exit" + :/* no outputs */ + :"r" (®s)); + /* Unreached */ + +badframe: + force_sig(SIGSEGV, current); +} + +static inline int setup_sigcontext32(struct pt_regs *regs, + struct sigcontext32 *sc) +{ + int err = 0; + + err |= __put_user(regs->cp0_epc, &sc->sc_pc); + err |= __put_user(regs->cp0_status, &sc->sc_status); + +#define save_gp_reg(i) { \ + err |= __put_user(regs->regs[i], &sc->sc_regs[i]); \ +} while(0) + __put_user(0, &sc->sc_regs[0]); save_gp_reg(1); save_gp_reg(2); + save_gp_reg(3); save_gp_reg(4); save_gp_reg(5); save_gp_reg(6); + save_gp_reg(7); save_gp_reg(8); save_gp_reg(9); save_gp_reg(10); + save_gp_reg(11); save_gp_reg(12); save_gp_reg(13); save_gp_reg(14); + save_gp_reg(15); save_gp_reg(16); save_gp_reg(17); save_gp_reg(18); + save_gp_reg(19); save_gp_reg(20); save_gp_reg(21); save_gp_reg(22); + save_gp_reg(23); save_gp_reg(24); save_gp_reg(25); save_gp_reg(26); + save_gp_reg(27); save_gp_reg(28); save_gp_reg(29); save_gp_reg(30); + save_gp_reg(31); +#undef save_gp_reg + + err |= __put_user(regs->hi, &sc->sc_mdhi); + err |= __put_user(regs->lo, &sc->sc_mdlo); + err |= __put_user(regs->cp0_cause, &sc->sc_cause); + err |= __put_user(regs->cp0_badvaddr, &sc->sc_badvaddr); + + err |= __put_user(!!used_math(), &sc->sc_used_math); + + if (!used_math()) + goto out; + + /* + * Save FPU state to signal context. Signal handler will "inherit" + * current FPU state. + */ + preempt_disable(); + + if (!is_fpu_owner()) { + own_fpu(); + restore_fp(current); + } + err |= save_fp_context32(sc); + + preempt_enable(); + +out: + return err; +} + +/* + * Determine which stack to use.. + */ +static inline void *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, + size_t frame_size) +{ + unsigned long sp; + + /* Default to using normal stack */ + sp = regs->regs[29]; + + /* + * FPU emulator may have it's own trampoline active just + * above the user stack, 16-bytes before the next lowest + * 16 byte boundary. Try to avoid trashing it. + */ + sp -= 32; + + /* This is the X/Open sanctioned signal stack switching. */ + if ((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags (sp) == 0)) + sp = current->sas_ss_sp + current->sas_ss_size; + + return (void *)((sp - frame_size) & ALMASK); +} + +static inline void setup_frame(struct k_sigaction * ka, struct pt_regs *regs, + int signr, sigset_t *set) +{ + struct sigframe *frame; + int err = 0; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) + goto give_sigsegv; + + /* + * Set up the return code ... + * + * li v0, __NR_O32_sigreturn + * syscall + */ + err |= __put_user(0x24020000 + __NR_O32_sigreturn, frame->sf_code + 0); + err |= __put_user(0x0000000c , frame->sf_code + 1); + flush_cache_sigtramp((unsigned long) frame->sf_code); + + err |= setup_sigcontext32(regs, &frame->sf_sc); + err |= __copy_to_user(&frame->sf_mask, set, sizeof(*set)); + if (err) + goto give_sigsegv; + + /* + * Arguments to signal handler: + * + * a0 = signal number + * a1 = 0 (should be cause) + * a2 = pointer to struct sigcontext + * + * $25 and c0_epc point to the signal handler, $29 points to the + * struct sigframe. + */ + regs->regs[ 4] = signr; + regs->regs[ 5] = 0; + regs->regs[ 6] = (unsigned long) &frame->sf_sc; + regs->regs[29] = (unsigned long) frame; + regs->regs[31] = (unsigned long) frame->sf_code; + regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler; + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%p\n", + current->comm, current->pid, + frame, regs->cp0_epc, frame->sf_code); +#endif + return; + +give_sigsegv: + force_sigsegv(signr, current); +} + +static inline void setup_rt_frame(struct k_sigaction * ka, + struct pt_regs *regs, int signr, + sigset_t *set, siginfo_t *info) +{ + struct rt_sigframe32 *frame; + int err = 0; + s32 sp; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) + goto give_sigsegv; + + /* Set up to return from userspace. If provided, use a stub already + in userspace. */ + /* + * Set up the return code ... + * + * li v0, __NR_O32_rt_sigreturn + * syscall + */ + err |= __put_user(0x24020000 + __NR_O32_rt_sigreturn, frame->rs_code + 0); + err |= __put_user(0x0000000c , frame->rs_code + 1); + flush_cache_sigtramp((unsigned long) frame->rs_code); + + /* Convert (siginfo_t -> compat_siginfo_t) and copy to user. */ + err |= copy_siginfo_to_user32(&frame->rs_info, info); + + /* Create the ucontext. */ + err |= __put_user(0, &frame->rs_uc.uc_flags); + err |= __put_user(0, &frame->rs_uc.uc_link); + sp = (int) (long) current->sas_ss_sp; + err |= __put_user(sp, + &frame->rs_uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(regs->regs[29]), + &frame->rs_uc.uc_stack.ss_flags); + err |= __put_user(current->sas_ss_size, + &frame->rs_uc.uc_stack.ss_size); + err |= setup_sigcontext32(regs, &frame->rs_uc.uc_mcontext); + err |= __copy_to_user(&frame->rs_uc.uc_sigmask, set, sizeof(*set)); + + if (err) + goto give_sigsegv; + + /* + * Arguments to signal handler: + * + * a0 = signal number + * a1 = 0 (should be cause) + * a2 = pointer to ucontext + * + * $25 and c0_epc point to the signal handler, $29 points to + * the struct rt_sigframe32. + */ + regs->regs[ 4] = signr; + regs->regs[ 5] = (unsigned long) &frame->rs_info; + regs->regs[ 6] = (unsigned long) &frame->rs_uc; + regs->regs[29] = (unsigned long) frame; + regs->regs[31] = (unsigned long) frame->rs_code; + regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler; + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%p\n", + current->comm, current->pid, + frame, regs->cp0_epc, frame->rs_code); +#endif + return; + +give_sigsegv: + force_sigsegv(signr, current); +} + +static inline void handle_signal(unsigned long sig, siginfo_t *info, + struct k_sigaction *ka, sigset_t *oldset, struct pt_regs * regs) +{ + switch (regs->regs[0]) { + case ERESTART_RESTARTBLOCK: + case ERESTARTNOHAND: + regs->regs[2] = EINTR; + break; + case ERESTARTSYS: + if(!(ka->sa.sa_flags & SA_RESTART)) { + regs->regs[2] = EINTR; + break; + } + /* fallthrough */ + case ERESTARTNOINTR: /* Userland will reload $v0. */ + regs->regs[7] = regs->regs[26]; + regs->cp0_epc -= 8; + } + + regs->regs[0] = 0; /* Don't deal with this again. */ + + if (ka->sa.sa_flags & SA_SIGINFO) + setup_rt_frame(ka, regs, sig, oldset, info); + else + setup_frame(ka, regs, sig, oldset); + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } +} + +int do_signal32(sigset_t *oldset, struct pt_regs *regs) +{ + struct k_sigaction ka; + siginfo_t info; + int signr; + + /* + * We want the common case to go fast, which is why we may in certain + * cases get here from kernel mode. Just return without doing anything + * if so. + */ + if (!user_mode(regs)) + return 1; + + if (try_to_freeze(0)) + goto no_signal; + + if (!oldset) + oldset = ¤t->blocked; + + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + if (signr > 0) { + handle_signal(signr, &info, &ka, oldset, regs); + return 1; + } + +no_signal: + /* + * Who's code doesn't conform to the restartable syscall convention + * dies here!!! The li instruction, a single machine instruction, + * must directly be followed by the syscall instruction. + */ + if (regs->regs[0]) { + if (regs->regs[2] == ERESTARTNOHAND || + regs->regs[2] == ERESTARTSYS || + regs->regs[2] == ERESTARTNOINTR) { + regs->regs[7] = regs->regs[26]; + regs->cp0_epc -= 8; + } + if (regs->regs[2] == ERESTART_RESTARTBLOCK) { + regs->regs[2] = __NR_O32_restart_syscall; + regs->regs[7] = regs->regs[26]; + regs->cp0_epc -= 4; + } + } + return 0; +} + +asmlinkage int sys32_rt_sigaction(int sig, const struct sigaction32 *act, + struct sigaction32 *oact, + unsigned int sigsetsize) +{ + struct k_sigaction new_sa, old_sa; + int ret = -EINVAL; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + goto out; + + if (act) { + int err = 0; + + if (!access_ok(VERIFY_READ, act, sizeof(*act))) + return -EFAULT; + err |= __get_user((u32)(u64)new_sa.sa.sa_handler, + &act->sa_handler); + err |= __get_user(new_sa.sa.sa_flags, &act->sa_flags); + err |= get_sigset(&new_sa.sa.sa_mask, &act->sa_mask); + if (err) + return -EFAULT; + } + + ret = do_sigaction(sig, act ? &new_sa : NULL, oact ? &old_sa : NULL); + + if (!ret && oact) { + int err = 0; + + if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact))) + return -EFAULT; + + err |= __put_user((u32)(u64)old_sa.sa.sa_handler, + &oact->sa_handler); + err |= __put_user(old_sa.sa.sa_flags, &oact->sa_flags); + err |= put_sigset(&old_sa.sa.sa_mask, &oact->sa_mask); + if (err) + return -EFAULT; + } +out: + return ret; +} + +asmlinkage int sys32_rt_sigprocmask(int how, compat_sigset_t *set, + compat_sigset_t *oset, unsigned int sigsetsize) +{ + sigset_t old_set, new_set; + int ret; + mm_segment_t old_fs = get_fs(); + + if (set && get_sigset(&new_set, set)) + return -EFAULT; + + set_fs (KERNEL_DS); + ret = sys_rt_sigprocmask(how, set ? &new_set : NULL, + oset ? &old_set : NULL, sigsetsize); + set_fs (old_fs); + + if (!ret && oset && put_sigset(&old_set, oset)) + return -EFAULT; + + return ret; +} + +asmlinkage int sys32_rt_sigpending(compat_sigset_t *uset, + unsigned int sigsetsize) +{ + int ret; + sigset_t set; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_rt_sigpending(&set, sigsetsize); + set_fs (old_fs); + + if (!ret && put_sigset(&set, uset)) + return -EFAULT; + + return ret; +} + +asmlinkage int sys32_rt_sigqueueinfo(int pid, int sig, compat_siginfo_t *uinfo) +{ + siginfo_t info; + int ret; + mm_segment_t old_fs = get_fs(); + + if (copy_from_user (&info, uinfo, 3*sizeof(int)) || + copy_from_user (info._sifields._pad, uinfo->_sifields._pad, SI_PAD_SIZE)) + return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_rt_sigqueueinfo(pid, sig, &info); + set_fs (old_fs); + return ret; +} diff --git a/arch/mips/kernel/signal_n32.c b/arch/mips/kernel/signal_n32.c new file mode 100644 index 000000000000..3544208d4b4b --- /dev/null +++ b/arch/mips/kernel/signal_n32.c @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2003 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/unistd.h> +#include <linux/compat.h> +#include <linux/bitops.h> + +#include <asm/asm.h> +#include <asm/cacheflush.h> +#include <asm/sim.h> +#include <asm/uaccess.h> +#include <asm/ucontext.h> +#include <asm/system.h> +#include <asm/fpu.h> +#include <asm/cpu-features.h> + +#include "signal-common.h" + +/* + * Including <asm/unistd.h> would give use the 64-bit syscall numbers ... + */ +#define __NR_N32_rt_sigreturn 6211 +#define __NR_N32_restart_syscall 6214 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +/* IRIX compatible stack_t */ +typedef struct sigaltstack32 { + s32 ss_sp; + compat_size_t ss_size; + int ss_flags; +} stack32_t; + +struct ucontextn32 { + u32 uc_flags; + s32 uc_link; + stack32_t uc_stack; + struct sigcontext uc_mcontext; + sigset_t uc_sigmask; /* mask last for extensibility */ +}; + +#if PLAT_TRAMPOLINE_STUFF_LINE +#define __tramp __attribute__((aligned(PLAT_TRAMPOLINE_STUFF_LINE))) +#else +#define __tramp +#endif + +struct rt_sigframe_n32 { + u32 rs_ass[4]; /* argument save space for o32 */ + u32 rs_code[2] __tramp; /* signal trampoline */ + struct siginfo rs_info __tramp; + struct ucontextn32 rs_uc; +}; + +save_static_function(sysn32_rt_sigreturn); +__attribute_used__ noinline static void +_sysn32_rt_sigreturn(nabi_no_regargs struct pt_regs regs) +{ + struct rt_sigframe_n32 *frame; + sigset_t set; + stack_t st; + s32 sp; + + frame = (struct rt_sigframe_n32 *) regs.regs[29]; + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->rs_uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(®s, &frame->rs_uc.uc_mcontext)) + goto badframe; + + /* The ucontext contains a stack32_t, so we must convert! */ + if (__get_user(sp, &frame->rs_uc.uc_stack.ss_sp)) + goto badframe; + st.ss_size = (long) sp; + if (__get_user(st.ss_size, &frame->rs_uc.uc_stack.ss_size)) + goto badframe; + if (__get_user(st.ss_flags, &frame->rs_uc.uc_stack.ss_flags)) + goto badframe; + + /* It is more difficult to avoid calling this function than to + call it and ignore errors. */ + do_sigaltstack(&st, NULL, regs.regs[29]); + + /* + * Don't let your children do this ... + */ + __asm__ __volatile__( + "move\t$29, %0\n\t" + "j\tsyscall_exit" + :/* no outputs */ + :"r" (®s)); + /* Unreached */ + +badframe: + force_sig(SIGSEGV, current); +} + +void setup_rt_frame_n32(struct k_sigaction * ka, + struct pt_regs *regs, int signr, sigset_t *set, siginfo_t *info) +{ + struct rt_sigframe_n32 *frame; + int err = 0; + s32 sp; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) + goto give_sigsegv; + + /* + * Set up the return code ... + * + * li v0, __NR_rt_sigreturn + * syscall + */ + if (PLAT_TRAMPOLINE_STUFF_LINE) + __clear_user(frame->rs_code, PLAT_TRAMPOLINE_STUFF_LINE); + err |= __put_user(0x24020000 + __NR_N32_rt_sigreturn, frame->rs_code + 0); + err |= __put_user(0x0000000c , frame->rs_code + 1); + flush_cache_sigtramp((unsigned long) frame->rs_code); + + /* Create siginfo. */ + err |= copy_siginfo_to_user(&frame->rs_info, info); + + /* Create the ucontext. */ + err |= __put_user(0, &frame->rs_uc.uc_flags); + err |= __put_user(0, &frame->rs_uc.uc_link); + sp = (int) (long) current->sas_ss_sp; + err |= __put_user(sp, + &frame->rs_uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(regs->regs[29]), + &frame->rs_uc.uc_stack.ss_flags); + err |= __put_user(current->sas_ss_size, + &frame->rs_uc.uc_stack.ss_size); + err |= setup_sigcontext(regs, &frame->rs_uc.uc_mcontext); + err |= __copy_to_user(&frame->rs_uc.uc_sigmask, set, sizeof(*set)); + + if (err) + goto give_sigsegv; + + /* + * Arguments to signal handler: + * + * a0 = signal number + * a1 = 0 (should be cause) + * a2 = pointer to ucontext + * + * $25 and c0_epc point to the signal handler, $29 points to + * the struct rt_sigframe. + */ + regs->regs[ 4] = signr; + regs->regs[ 5] = (unsigned long) &frame->rs_info; + regs->regs[ 6] = (unsigned long) &frame->rs_uc; + regs->regs[29] = (unsigned long) frame; + regs->regs[31] = (unsigned long) frame->rs_code; + regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler; + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%p\n", + current->comm, current->pid, + frame, regs->cp0_epc, regs->regs[31]); +#endif + return; + +give_sigsegv: + force_sigsegv(signr, current); +} diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c new file mode 100644 index 000000000000..af5cd3b8a396 --- /dev/null +++ b/arch/mips/kernel/smp.c @@ -0,0 +1,425 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) 2000, 2001 Kanoj Sarcar + * Copyright (C) 2000, 2001 Ralf Baechle + * Copyright (C) 2000, 2001 Silicon Graphics, Inc. + * Copyright (C) 2000, 2001, 2003 Broadcom Corporation + */ +#include <linux/cache.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/threads.h> +#include <linux/module.h> +#include <linux/time.h> +#include <linux/timex.h> +#include <linux/sched.h> +#include <linux/cpumask.h> + +#include <asm/atomic.h> +#include <asm/cpu.h> +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/mmu_context.h> +#include <asm/smp.h> + +cpumask_t phys_cpu_present_map; /* Bitmask of available CPUs */ +volatile cpumask_t cpu_callin_map; /* Bitmask of started secondaries */ +cpumask_t cpu_online_map; /* Bitmask of currently online CPUs */ +int __cpu_number_map[NR_CPUS]; /* Map physical to logical */ +int __cpu_logical_map[NR_CPUS]; /* Map logical to physical */ + +EXPORT_SYMBOL(phys_cpu_present_map); +EXPORT_SYMBOL(cpu_online_map); + +static void smp_tune_scheduling (void) +{ + struct cache_desc *cd = ¤t_cpu_data.scache; + unsigned long cachesize; /* kB */ + unsigned long bandwidth = 350; /* MB/s */ + unsigned long cpu_khz; + + /* + * Crude estimate until we actually meassure ... + */ + cpu_khz = loops_per_jiffy * 2 * HZ / 1000; + + /* + * Rough estimation for SMP scheduling, this is the number of + * cycles it takes for a fully memory-limited process to flush + * the SMP-local cache. + * + * (For a P5 this pretty much means we will choose another idle + * CPU almost always at wakeup time (this is due to the small + * L1 cache), on PIIs it's around 50-100 usecs, depending on + * the cache size) + */ + if (!cpu_khz) + return; + + cachesize = cd->linesz * cd->sets * cd->ways; +} + +extern void __init calibrate_delay(void); +extern ATTRIB_NORET void cpu_idle(void); + +/* + * First C code run on the secondary CPUs after being started up by + * the master. + */ +asmlinkage void start_secondary(void) +{ + unsigned int cpu = smp_processor_id(); + + cpu_probe(); + cpu_report(); + per_cpu_trap_init(); + prom_init_secondary(); + + /* + * XXX parity protection should be folded in here when it's converted + * to an option instead of something based on .cputype + */ + + calibrate_delay(); + cpu_data[cpu].udelay_val = loops_per_jiffy; + + prom_smp_finish(); + + cpu_set(cpu, cpu_callin_map); + + cpu_idle(); +} + +DEFINE_SPINLOCK(smp_call_lock); + +struct call_data_struct *call_data; + +/* + * Run a function on all other CPUs. + * <func> The function to run. This must be fast and non-blocking. + * <info> An arbitrary pointer to pass to the function. + * <retry> If true, keep retrying until ready. + * <wait> If true, wait until function has completed on other CPUs. + * [RETURNS] 0 on success, else a negative status code. + * + * Does not return until remote CPUs are nearly ready to execute <func> + * or are or have executed. + * + * You must not call this function with disabled interrupts or from a + * hardware interrupt handler or from a bottom half handler. + */ +int smp_call_function (void (*func) (void *info), void *info, int retry, + int wait) +{ + struct call_data_struct data; + int i, cpus = num_online_cpus() - 1; + int cpu = smp_processor_id(); + + if (!cpus) + return 0; + + /* Can deadlock when called with interrupts disabled */ + WARN_ON(irqs_disabled()); + + data.func = func; + data.info = info; + atomic_set(&data.started, 0); + data.wait = wait; + if (wait) + atomic_set(&data.finished, 0); + + spin_lock(&smp_call_lock); + call_data = &data; + mb(); + + /* Send a message to all other CPUs and wait for them to respond */ + for (i = 0; i < NR_CPUS; i++) + if (cpu_online(i) && i != cpu) + core_send_ipi(i, SMP_CALL_FUNCTION); + + /* Wait for response */ + /* FIXME: lock-up detection, backtrace on lock-up */ + while (atomic_read(&data.started) != cpus) + barrier(); + + if (wait) + while (atomic_read(&data.finished) != cpus) + barrier(); + spin_unlock(&smp_call_lock); + + return 0; +} + +void smp_call_function_interrupt(void) +{ + void (*func) (void *info) = call_data->func; + void *info = call_data->info; + int wait = call_data->wait; + + /* + * Notify initiating CPU that I've grabbed the data and am + * about to execute the function. + */ + mb(); + atomic_inc(&call_data->started); + + /* + * At this point the info structure may be out of scope unless wait==1. + */ + irq_enter(); + (*func)(info); + irq_exit(); + + if (wait) { + mb(); + atomic_inc(&call_data->finished); + } +} + +static void stop_this_cpu(void *dummy) +{ + /* + * Remove this CPU: + */ + cpu_clear(smp_processor_id(), cpu_online_map); + local_irq_enable(); /* May need to service _machine_restart IPI */ + for (;;); /* Wait if available. */ +} + +void smp_send_stop(void) +{ + smp_call_function(stop_this_cpu, NULL, 1, 0); +} + +void __init smp_cpus_done(unsigned int max_cpus) +{ + prom_cpus_done(); +} + +/* called from main before smp_init() */ +void __init smp_prepare_cpus(unsigned int max_cpus) +{ + cpu_data[0].udelay_val = loops_per_jiffy; + init_new_context(current, &init_mm); + current_thread_info()->cpu = 0; + smp_tune_scheduling(); + prom_prepare_cpus(max_cpus); +} + +/* preload SMP state for boot cpu */ +void __devinit smp_prepare_boot_cpu(void) +{ + /* + * This assumes that bootup is always handled by the processor + * with the logic and physical number 0. + */ + __cpu_number_map[0] = 0; + __cpu_logical_map[0] = 0; + cpu_set(0, phys_cpu_present_map); + cpu_set(0, cpu_online_map); + cpu_set(0, cpu_callin_map); +} + +/* + * Startup the CPU with this logical number + */ +static int __init do_boot_cpu(int cpu) +{ + struct task_struct *idle; + + /* + * The following code is purely to make sure + * Linux can schedule processes on this slave. + */ + idle = fork_idle(cpu); + if (IS_ERR(idle)) + panic("failed fork for CPU %d\n", cpu); + + prom_boot_secondary(cpu, idle); + + /* XXXKW timeout */ + while (!cpu_isset(cpu, cpu_callin_map)) + udelay(100); + + cpu_set(cpu, cpu_online_map); + + return 0; +} + +/* + * Called once for each "cpu_possible(cpu)". Needs to spin up the cpu + * and keep control until "cpu_online(cpu)" is set. Note: cpu is + * physical, not logical. + */ +int __devinit __cpu_up(unsigned int cpu) +{ + int ret; + + /* Processor goes to start_secondary(), sets online flag */ + ret = do_boot_cpu(cpu); + if (ret < 0) + return ret; + + return 0; +} + +/* Not really SMP stuff ... */ +int setup_profiling_timer(unsigned int multiplier) +{ + return 0; +} + +static void flush_tlb_all_ipi(void *info) +{ + local_flush_tlb_all(); +} + +void flush_tlb_all(void) +{ + on_each_cpu(flush_tlb_all_ipi, 0, 1, 1); +} + +static void flush_tlb_mm_ipi(void *mm) +{ + local_flush_tlb_mm((struct mm_struct *)mm); +} + +/* + * The following tlb flush calls are invoked when old translations are + * being torn down, or pte attributes are changing. For single threaded + * address spaces, a new context is obtained on the current cpu, and tlb + * context on other cpus are invalidated to force a new context allocation + * at switch_mm time, should the mm ever be used on other cpus. For + * multithreaded address spaces, intercpu interrupts have to be sent. + * Another case where intercpu interrupts are required is when the target + * mm might be active on another cpu (eg debuggers doing the flushes on + * behalf of debugees, kswapd stealing pages from another process etc). + * Kanoj 07/00. + */ + +void flush_tlb_mm(struct mm_struct *mm) +{ + preempt_disable(); + + if ((atomic_read(&mm->mm_users) != 1) || (current->mm != mm)) { + smp_call_function(flush_tlb_mm_ipi, (void *)mm, 1, 1); + } else { + int i; + for (i = 0; i < num_online_cpus(); i++) + if (smp_processor_id() != i) + cpu_context(i, mm) = 0; + } + local_flush_tlb_mm(mm); + + preempt_enable(); +} + +struct flush_tlb_data { + struct vm_area_struct *vma; + unsigned long addr1; + unsigned long addr2; +}; + +static void flush_tlb_range_ipi(void *info) +{ + struct flush_tlb_data *fd = (struct flush_tlb_data *)info; + + local_flush_tlb_range(fd->vma, fd->addr1, fd->addr2); +} + +void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) +{ + struct mm_struct *mm = vma->vm_mm; + + preempt_disable(); + if ((atomic_read(&mm->mm_users) != 1) || (current->mm != mm)) { + struct flush_tlb_data fd; + + fd.vma = vma; + fd.addr1 = start; + fd.addr2 = end; + smp_call_function(flush_tlb_range_ipi, (void *)&fd, 1, 1); + } else { + int i; + for (i = 0; i < num_online_cpus(); i++) + if (smp_processor_id() != i) + cpu_context(i, mm) = 0; + } + local_flush_tlb_range(vma, start, end); + preempt_enable(); +} + +static void flush_tlb_kernel_range_ipi(void *info) +{ + struct flush_tlb_data *fd = (struct flush_tlb_data *)info; + + local_flush_tlb_kernel_range(fd->addr1, fd->addr2); +} + +void flush_tlb_kernel_range(unsigned long start, unsigned long end) +{ + struct flush_tlb_data fd; + + fd.addr1 = start; + fd.addr2 = end; + on_each_cpu(flush_tlb_kernel_range_ipi, (void *)&fd, 1, 1); +} + +static void flush_tlb_page_ipi(void *info) +{ + struct flush_tlb_data *fd = (struct flush_tlb_data *)info; + + local_flush_tlb_page(fd->vma, fd->addr1); +} + +void flush_tlb_page(struct vm_area_struct *vma, unsigned long page) +{ + preempt_disable(); + if ((atomic_read(&vma->vm_mm->mm_users) != 1) || (current->mm != vma->vm_mm)) { + struct flush_tlb_data fd; + + fd.vma = vma; + fd.addr1 = page; + smp_call_function(flush_tlb_page_ipi, (void *)&fd, 1, 1); + } else { + int i; + for (i = 0; i < num_online_cpus(); i++) + if (smp_processor_id() != i) + cpu_context(i, vma->vm_mm) = 0; + } + local_flush_tlb_page(vma, page); + preempt_enable(); +} + +static void flush_tlb_one_ipi(void *info) +{ + unsigned long vaddr = (unsigned long) info; + + local_flush_tlb_one(vaddr); +} + +void flush_tlb_one(unsigned long vaddr) +{ + smp_call_function(flush_tlb_one_ipi, (void *) vaddr, 1, 1); + local_flush_tlb_one(vaddr); +} + +EXPORT_SYMBOL(flush_tlb_page); +EXPORT_SYMBOL(flush_tlb_one); +EXPORT_SYMBOL(cpu_data); +EXPORT_SYMBOL(synchronize_irq); diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c new file mode 100644 index 000000000000..598bfe7426a2 --- /dev/null +++ b/arch/mips/kernel/syscall.c @@ -0,0 +1,407 @@ +/* + * 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, 1997, 2000, 2001, 05 by Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * Copyright (C) 2001 MIPS Technologies, Inc. + */ +#include <linux/a.out.h> +#include <linux/errno.h> +#include <linux/linkage.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/mman.h> +#include <linux/ptrace.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/syscalls.h> +#include <linux/file.h> +#include <linux/slab.h> +#include <linux/utsname.h> +#include <linux/unistd.h> +#include <linux/sem.h> +#include <linux/msg.h> +#include <linux/shm.h> +#include <linux/compiler.h> + +#include <asm/branch.h> +#include <asm/cachectl.h> +#include <asm/cacheflush.h> +#include <asm/ipc.h> +#include <asm/offset.h> +#include <asm/signal.h> +#include <asm/sim.h> +#include <asm/shmparam.h> +#include <asm/sysmips.h> +#include <asm/uaccess.h> + +asmlinkage int sys_pipe(nabi_no_regargs volatile struct pt_regs regs) +{ + int fd[2]; + int error, res; + + error = do_pipe(fd); + if (error) { + res = error; + goto out; + } + regs.regs[3] = fd[1]; + res = fd[0]; +out: + return res; +} + +unsigned long shm_align_mask = PAGE_SIZE - 1; /* Sane caches */ + +#define COLOUR_ALIGN(addr,pgoff) \ + ((((addr) + shm_align_mask) & ~shm_align_mask) + \ + (((pgoff) << PAGE_SHIFT) & shm_align_mask)) + +unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, + unsigned long len, unsigned long pgoff, unsigned long flags) +{ + struct vm_area_struct * vmm; + int do_color_align; + unsigned long task_size; + + task_size = STACK_TOP; + + if (flags & MAP_FIXED) { + /* + * We do not accept a shared mapping if it would violate + * cache aliasing constraints. + */ + if ((flags & MAP_SHARED) && (addr & shm_align_mask)) + return -EINVAL; + return addr; + } + + if (len > task_size) + return -ENOMEM; + do_color_align = 0; + if (filp || (flags & MAP_SHARED)) + do_color_align = 1; + if (addr) { + if (do_color_align) + addr = COLOUR_ALIGN(addr, pgoff); + else + addr = PAGE_ALIGN(addr); + vmm = find_vma(current->mm, addr); + if (task_size - len >= addr && + (!vmm || addr + len <= vmm->vm_start)) + return addr; + } + addr = TASK_UNMAPPED_BASE; + if (do_color_align) + addr = COLOUR_ALIGN(addr, pgoff); + else + addr = PAGE_ALIGN(addr); + + for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) { + /* At this point: (!vmm || addr < vmm->vm_end). */ + if (task_size - len < addr) + return -ENOMEM; + if (!vmm || addr + len <= vmm->vm_start) + return addr; + addr = vmm->vm_end; + if (do_color_align) + addr = COLOUR_ALIGN(addr, pgoff); + } +} + +/* common code for old and new mmaps */ +static inline unsigned long +do_mmap2(unsigned long addr, unsigned long len, unsigned long prot, + unsigned long flags, unsigned long fd, unsigned long pgoff) +{ + unsigned long error = -EBADF; + struct file * file = NULL; + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + if (!(flags & MAP_ANONYMOUS)) { + file = fget(fd); + if (!file) + goto out; + } + + down_write(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up_write(¤t->mm->mmap_sem); + + if (file) + fput(file); +out: + return error; +} + +asmlinkage unsigned long +old_mmap(unsigned long addr, unsigned long len, int prot, + int flags, int fd, off_t offset) +{ + unsigned long result; + + result = -EINVAL; + if (offset & ~PAGE_MASK) + goto out; + + result = do_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT); + +out: + return result; +} + +asmlinkage unsigned long +sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot, + unsigned long flags, unsigned long fd, unsigned long pgoff) +{ + return do_mmap2(addr, len, prot, flags, fd, pgoff); +} + +save_static_function(sys_fork); +__attribute_used__ noinline static int +_sys_fork(nabi_no_regargs struct pt_regs regs) +{ + return do_fork(SIGCHLD, regs.regs[29], ®s, 0, NULL, NULL); +} + +save_static_function(sys_clone); +__attribute_used__ noinline static int +_sys_clone(nabi_no_regargs struct pt_regs regs) +{ + unsigned long clone_flags; + unsigned long newsp; + int *parent_tidptr, *child_tidptr; + + clone_flags = regs.regs[4]; + newsp = regs.regs[5]; + if (!newsp) + newsp = regs.regs[29]; + parent_tidptr = (int *) regs.regs[6]; + child_tidptr = (int *) regs.regs[7]; + return do_fork(clone_flags, newsp, ®s, 0, + parent_tidptr, child_tidptr); +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage int sys_execve(nabi_no_regargs struct pt_regs regs) +{ + int error; + char * filename; + + filename = getname((char *) (long)regs.regs[4]); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + error = do_execve(filename, (char **) (long)regs.regs[5], + (char **) (long)regs.regs[6], ®s); + putname(filename); + +out: + return error; +} + +/* + * Compacrapability ... + */ +asmlinkage int sys_uname(struct old_utsname * name) +{ + if (name && !copy_to_user(name, &system_utsname, sizeof (*name))) + return 0; + return -EFAULT; +} + +/* + * Compacrapability ... + */ +asmlinkage int sys_olduname(struct oldold_utsname * name) +{ + int error; + + if (!name) + return -EFAULT; + if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) + return -EFAULT; + + error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN); + error -= __put_user(0,name->sysname+__OLD_UTS_LEN); + error -= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN); + error -= __put_user(0,name->nodename+__OLD_UTS_LEN); + error -= __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN); + error -= __put_user(0,name->release+__OLD_UTS_LEN); + error -= __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN); + error -= __put_user(0,name->version+__OLD_UTS_LEN); + error -= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN); + error = __put_user(0,name->machine+__OLD_UTS_LEN); + error = error ? -EFAULT : 0; + + return error; +} + +asmlinkage int _sys_sysmips(int cmd, long arg1, int arg2, int arg3) +{ + int tmp, len; + char *name; + + switch(cmd) { + case SETNAME: { + char nodename[__NEW_UTS_LEN + 1]; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + name = (char *) arg1; + + len = strncpy_from_user(nodename, name, __NEW_UTS_LEN); + if (len < 0) + return -EFAULT; + + down_write(&uts_sem); + strncpy(system_utsname.nodename, nodename, len); + nodename[__NEW_UTS_LEN] = '\0'; + strlcpy(system_utsname.nodename, nodename, + sizeof(system_utsname.nodename)); + up_write(&uts_sem); + return 0; + } + + case MIPS_ATOMIC_SET: + printk(KERN_CRIT "How did I get here?\n"); + return -EINVAL; + + case MIPS_FIXADE: + tmp = current->thread.mflags & ~3; + current->thread.mflags = tmp | (arg1 & 3); + return 0; + + case FLUSH_CACHE: + __flush_cache_all(); + return 0; + + case MIPS_RDNVRAM: + return -EIO; + } + + return -EINVAL; +} + +/* + * sys_ipc() is the de-multiplexer for the SysV IPC calls.. + * + * This is really horribly ugly. + */ +asmlinkage int sys_ipc (uint call, int first, int second, + unsigned long third, void *ptr, long fifth) +{ + int version, ret; + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + switch (call) { + case SEMOP: + return sys_semtimedop (first, (struct sembuf *)ptr, second, + NULL); + case SEMTIMEDOP: + return sys_semtimedop (first, (struct sembuf *)ptr, second, + (const struct timespec __user *)fifth); + case SEMGET: + return sys_semget (first, second, third); + case SEMCTL: { + union semun fourth; + if (!ptr) + return -EINVAL; + if (get_user(fourth.__pad, (void **) ptr)) + return -EFAULT; + return sys_semctl (first, second, third, fourth); + } + + case MSGSND: + return sys_msgsnd (first, (struct msgbuf *) ptr, + second, third); + case MSGRCV: + switch (version) { + case 0: { + struct ipc_kludge tmp; + if (!ptr) + return -EINVAL; + + if (copy_from_user(&tmp, + (struct ipc_kludge *) ptr, + sizeof (tmp))) + return -EFAULT; + return sys_msgrcv (first, tmp.msgp, second, + tmp.msgtyp, third); + } + default: + return sys_msgrcv (first, + (struct msgbuf *) ptr, + second, fifth, third); + } + case MSGGET: + return sys_msgget ((key_t) first, second); + case MSGCTL: + return sys_msgctl (first, second, (struct msqid_ds *) ptr); + + case SHMAT: + switch (version) { + default: { + ulong raddr; + ret = do_shmat (first, (char *) ptr, second, &raddr); + if (ret) + return ret; + return put_user (raddr, (ulong *) third); + } + case 1: /* iBCS2 emulator entry point */ + if (!segment_eq(get_fs(), get_ds())) + return -EINVAL; + return do_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 -ENOSYS; + } +} + +/* + * Native ABI that is O32 or N64 version + */ +asmlinkage long sys_shmat(int shmid, char __user *shmaddr, + int shmflg, unsigned long *addr) +{ + unsigned long raddr; + int err; + + err = do_shmat(shmid, shmaddr, shmflg, &raddr); + if (err) + return err; + + return put_user(raddr, addr); +} + +/* + * No implemented yet ... + */ +asmlinkage int sys_cachectl(char *addr, int nbytes, int op) +{ + return -ENOSYS; +} + +/* + * 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); +} diff --git a/arch/mips/kernel/sysirix.c b/arch/mips/kernel/sysirix.c new file mode 100644 index 000000000000..f3bf0e43b8bb --- /dev/null +++ b/arch/mips/kernel/sysirix.c @@ -0,0 +1,2179 @@ +/* + * sysirix.c: IRIX system call emulation. + * + * Copyright (C) 1996 David S. Miller + * Copyright (C) 1997 Miguel de Icaza + * Copyright (C) 1997, 1998, 1999, 2000 Ralf Baechle + */ +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/binfmts.h> +#include <linux/highuid.h> +#include <linux/pagemap.h> +#include <linux/mm.h> +#include <linux/mman.h> +#include <linux/slab.h> +#include <linux/swap.h> +#include <linux/errno.h> +#include <linux/time.h> +#include <linux/timex.h> +#include <linux/times.h> +#include <linux/elf.h> +#include <linux/msg.h> +#include <linux/shm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/utsname.h> +#include <linux/file.h> +#include <linux/vfs.h> +#include <linux/namei.h> +#include <linux/socket.h> +#include <linux/security.h> +#include <linux/syscalls.h> + +#include <asm/ptrace.h> +#include <asm/page.h> +#include <asm/uaccess.h> +#include <asm/inventory.h> + +/* 2,191 lines of complete and utter shit coming up... */ + +extern int max_threads; + +/* The sysmp commands supported thus far. */ +#define MP_NPROCS 1 /* # processor in complex */ +#define MP_NAPROCS 2 /* # active processors in complex */ +#define MP_PGSIZE 14 /* Return system page size in v1. */ + +asmlinkage int irix_sysmp(struct pt_regs *regs) +{ + unsigned long cmd; + int base = 0; + int error = 0; + + if(regs->regs[2] == 1000) + base = 1; + cmd = regs->regs[base + 4]; + switch(cmd) { + case MP_PGSIZE: + error = PAGE_SIZE; + break; + case MP_NPROCS: + case MP_NAPROCS: + error = num_online_cpus(); + break; + default: + printk("SYSMP[%s:%d]: Unsupported opcode %d\n", + current->comm, current->pid, (int)cmd); + error = -EINVAL; + break; + } + + return error; +} + +/* The prctl commands. */ +#define PR_MAXPROCS 1 /* Tasks/user. */ +#define PR_ISBLOCKED 2 /* If blocked, return 1. */ +#define PR_SETSTACKSIZE 3 /* Set largest task stack size. */ +#define PR_GETSTACKSIZE 4 /* Get largest task stack size. */ +#define PR_MAXPPROCS 5 /* Num parallel tasks. */ +#define PR_UNBLKONEXEC 6 /* When task exec/exit's, unblock. */ +#define PR_SETEXITSIG 8 /* When task exit's, set signal. */ +#define PR_RESIDENT 9 /* Make task unswappable. */ +#define PR_ATTACHADDR 10 /* (Re-)Connect a vma to a task. */ +#define PR_DETACHADDR 11 /* Disconnect a vma from a task. */ +#define PR_TERMCHILD 12 /* When parent sleeps with fishes, kill child. */ +#define PR_GETSHMASK 13 /* Get the sproc() share mask. */ +#define PR_GETNSHARE 14 /* Number of share group members. */ +#define PR_COREPID 15 /* Add task pid to name when it core. */ +#define PR_ATTACHADDRPERM 16 /* (Re-)Connect vma, with specified prot. */ +#define PR_PTHREADEXIT 17 /* Kill a pthread without prejudice. */ + +asmlinkage int irix_prctl(struct pt_regs *regs) +{ + unsigned long cmd; + int error = 0, base = 0; + + if (regs->regs[2] == 1000) + base = 1; + cmd = regs->regs[base + 4]; + switch (cmd) { + case PR_MAXPROCS: + printk("irix_prctl[%s:%d]: Wants PR_MAXPROCS\n", + current->comm, current->pid); + error = max_threads; + break; + + case PR_ISBLOCKED: { + struct task_struct *task; + + printk("irix_prctl[%s:%d]: Wants PR_ISBLOCKED\n", + current->comm, current->pid); + read_lock(&tasklist_lock); + task = find_task_by_pid(regs->regs[base + 5]); + error = -ESRCH; + if (error) + error = (task->run_list.next != NULL); + read_unlock(&tasklist_lock); + /* Can _your_ OS find this out that fast? */ + break; + } + + case PR_SETSTACKSIZE: { + long value = regs->regs[base + 5]; + + printk("irix_prctl[%s:%d]: Wants PR_SETSTACKSIZE<%08lx>\n", + current->comm, current->pid, (unsigned long) value); + if (value > RLIM_INFINITY) + value = RLIM_INFINITY; + if (capable(CAP_SYS_ADMIN)) { + task_lock(current->group_leader); + current->signal->rlim[RLIMIT_STACK].rlim_max = + current->signal->rlim[RLIMIT_STACK].rlim_cur = value; + task_unlock(current->group_leader); + error = value; + break; + } + task_lock(current->group_leader); + if (value > current->signal->rlim[RLIMIT_STACK].rlim_max) { + error = -EINVAL; + task_unlock(current->group_leader); + break; + } + current->signal->rlim[RLIMIT_STACK].rlim_cur = value; + task_unlock(current->group_leader); + error = value; + break; + } + + case PR_GETSTACKSIZE: + printk("irix_prctl[%s:%d]: Wants PR_GETSTACKSIZE\n", + current->comm, current->pid); + error = current->signal->rlim[RLIMIT_STACK].rlim_cur; + break; + + case PR_MAXPPROCS: + printk("irix_prctl[%s:%d]: Wants PR_MAXPROCS\n", + current->comm, current->pid); + error = 1; + break; + + case PR_UNBLKONEXEC: + printk("irix_prctl[%s:%d]: Wants PR_UNBLKONEXEC\n", + current->comm, current->pid); + error = -EINVAL; + break; + + case PR_SETEXITSIG: + printk("irix_prctl[%s:%d]: Wants PR_SETEXITSIG\n", + current->comm, current->pid); + + /* We can probably play some game where we set the task + * exit_code to some non-zero value when this is requested, + * and check whether exit_code is already set in do_exit(). + */ + error = -EINVAL; + break; + + case PR_RESIDENT: + printk("irix_prctl[%s:%d]: Wants PR_RESIDENT\n", + current->comm, current->pid); + error = 0; /* Compatibility indeed. */ + break; + + case PR_ATTACHADDR: + printk("irix_prctl[%s:%d]: Wants PR_ATTACHADDR\n", + current->comm, current->pid); + error = -EINVAL; + break; + + case PR_DETACHADDR: + printk("irix_prctl[%s:%d]: Wants PR_DETACHADDR\n", + current->comm, current->pid); + error = -EINVAL; + break; + + case PR_TERMCHILD: + printk("irix_prctl[%s:%d]: Wants PR_TERMCHILD\n", + current->comm, current->pid); + error = -EINVAL; + break; + + case PR_GETSHMASK: + printk("irix_prctl[%s:%d]: Wants PR_GETSHMASK\n", + current->comm, current->pid); + error = -EINVAL; /* Until I have the sproc() stuff in. */ + break; + + case PR_GETNSHARE: + error = 0; /* Until I have the sproc() stuff in. */ + break; + + case PR_COREPID: + printk("irix_prctl[%s:%d]: Wants PR_COREPID\n", + current->comm, current->pid); + error = -EINVAL; + break; + + case PR_ATTACHADDRPERM: + printk("irix_prctl[%s:%d]: Wants PR_ATTACHADDRPERM\n", + current->comm, current->pid); + error = -EINVAL; + break; + + case PR_PTHREADEXIT: + printk("irix_prctl[%s:%d]: Wants PR_PTHREADEXIT\n", + current->comm, current->pid); + do_exit(regs->regs[base + 5]); + + default: + printk("irix_prctl[%s:%d]: Non-existant opcode %d\n", + current->comm, current->pid, (int)cmd); + error = -EINVAL; + break; + } + + return error; +} + +#undef DEBUG_PROCGRPS + +extern unsigned long irix_mapelf(int fd, struct elf_phdr *user_phdrp, int cnt); +extern int getrusage(struct task_struct *p, int who, struct rusage __user *ru); +extern char *prom_getenv(char *name); +extern long prom_setenv(char *name, char *value); + +/* The syssgi commands supported thus far. */ +#define SGI_SYSID 1 /* Return unique per-machine identifier. */ +#define SGI_INVENT 5 /* Fetch inventory */ +# define SGI_INV_SIZEOF 1 +# define SGI_INV_READ 2 +#define SGI_RDNAME 6 /* Return string name of a process. */ +#define SGI_SETNVRAM 8 /* Set PROM variable. */ +#define SGI_GETNVRAM 9 /* Get PROM variable. */ +#define SGI_SETPGID 21 /* Set process group id. */ +#define SGI_SYSCONF 22 /* POSIX sysconf garbage. */ +#define SGI_PATHCONF 24 /* POSIX sysconf garbage. */ +#define SGI_SETGROUPS 40 /* POSIX sysconf garbage. */ +#define SGI_GETGROUPS 41 /* POSIX sysconf garbage. */ +#define SGI_RUSAGE 56 /* BSD style rusage(). */ +#define SGI_SSYNC 62 /* Synchronous fs sync. */ +#define SGI_GETSID 65 /* SysVr4 get session id. */ +#define SGI_ELFMAP 68 /* Map an elf image. */ +#define SGI_TOSSTSAVE 108 /* Toss saved vma's. */ +#define SGI_FP_BCOPY 129 /* Should FPU bcopy be used on this machine? */ +#define SGI_PHYSP 1011 /* Translate virtual into physical page. */ + +asmlinkage int irix_syssgi(struct pt_regs *regs) +{ + unsigned long cmd; + int retval, base = 0; + + if (regs->regs[2] == 1000) + base = 1; + + cmd = regs->regs[base + 4]; + switch(cmd) { + case SGI_SYSID: { + char *buf = (char *) regs->regs[base + 5]; + + /* XXX Use ethernet addr.... */ + retval = clear_user(buf, 64); + break; + } +#if 0 + case SGI_RDNAME: { + int pid = (int) regs->regs[base + 5]; + char *buf = (char *) regs->regs[base + 6]; + struct task_struct *p; + char tcomm[sizeof(current->comm)]; + + if (!access_ok(VERIFY_WRITE, buf, sizeof(tcomm))) { + retval = -EFAULT; + break; + } + read_lock(&tasklist_lock); + p = find_task_by_pid(pid); + if (!p) { + read_unlock(&tasklist_lock); + retval = -ESRCH; + break; + } + get_task_comm(tcomm, p); + read_unlock(&tasklist_lock); + + /* XXX Need to check sizes. */ + copy_to_user(buf, tcomm, sizeof(tcomm)); + retval = 0; + break; + } + + case SGI_GETNVRAM: { + char *name = (char *) regs->regs[base+5]; + char *buf = (char *) regs->regs[base+6]; + char *value; + return -EINVAL; /* til I fix it */ + if (!access_ok(VERIFY_WRITE, buf, 128)) { + retval = -EFAULT; + break; + } + value = prom_getenv(name); /* PROM lock? */ + if (!value) { + retval = -EINVAL; + break; + } + /* Do I strlen() for the length? */ + copy_to_user(buf, value, 128); + retval = 0; + break; + } + + case SGI_SETNVRAM: { + char *name = (char *) regs->regs[base+5]; + char *value = (char *) regs->regs[base+6]; + return -EINVAL; /* til I fix it */ + retval = prom_setenv(name, value); + /* XXX make sure retval conforms to syssgi(2) */ + printk("[%s:%d] setnvram(\"%s\", \"%s\"): retval %d", + current->comm, current->pid, name, value, retval); +/* if (retval == PROM_ENOENT) + retval = -ENOENT; */ + break; + } +#endif + + case SGI_SETPGID: { +#ifdef DEBUG_PROCGRPS + printk("[%s:%d] setpgid(%d, %d) ", + current->comm, current->pid, + (int) regs->regs[base + 5], (int)regs->regs[base + 6]); +#endif + retval = sys_setpgid(regs->regs[base + 5], regs->regs[base + 6]); + +#ifdef DEBUG_PROCGRPS + printk("retval=%d\n", retval); +#endif + } + + case SGI_SYSCONF: { + switch(regs->regs[base + 5]) { + case 1: + retval = (MAX_ARG_PAGES >> 4); /* XXX estimate... */ + goto out; + case 2: + retval = max_threads; + goto out; + case 3: + retval = HZ; + goto out; + case 4: + retval = NGROUPS_MAX; + goto out; + case 5: + retval = NR_OPEN; + goto out; + case 6: + retval = 1; + goto out; + case 7: + retval = 1; + goto out; + case 8: + retval = 199009; + goto out; + case 11: + retval = PAGE_SIZE; + goto out; + case 12: + retval = 4; + goto out; + case 25: + case 26: + case 27: + case 28: + case 29: + case 30: + retval = 0; + goto out; + case 31: + retval = 32; + goto out; + default: + retval = -EINVAL; + goto out; + }; + } + + case SGI_SETGROUPS: + retval = sys_setgroups((int) regs->regs[base + 5], + (gid_t *) regs->regs[base + 6]); + break; + + case SGI_GETGROUPS: + retval = sys_getgroups((int) regs->regs[base + 5], + (gid_t *) regs->regs[base + 6]); + break; + + case SGI_RUSAGE: { + struct rusage *ru = (struct rusage *) regs->regs[base + 6]; + + switch((int) regs->regs[base + 5]) { + case 0: + /* rusage self */ + retval = getrusage(current, RUSAGE_SELF, ru); + goto out; + + case -1: + /* rusage children */ + retval = getrusage(current, RUSAGE_CHILDREN, ru); + goto out; + + default: + retval = -EINVAL; + goto out; + }; + } + + case SGI_SSYNC: + sys_sync(); + retval = 0; + break; + + case SGI_GETSID: +#ifdef DEBUG_PROCGRPS + printk("[%s:%d] getsid(%d) ", current->comm, current->pid, + (int) regs->regs[base + 5]); +#endif + retval = sys_getsid(regs->regs[base + 5]); +#ifdef DEBUG_PROCGRPS + printk("retval=%d\n", retval); +#endif + break; + + case SGI_ELFMAP: + retval = irix_mapelf((int) regs->regs[base + 5], + (struct elf_phdr *) regs->regs[base + 6], + (int) regs->regs[base + 7]); + break; + + case SGI_TOSSTSAVE: + /* XXX We don't need to do anything? */ + retval = 0; + break; + + case SGI_FP_BCOPY: + retval = 0; + break; + + case SGI_PHYSP: { + unsigned long addr = regs->regs[base + 5]; + int *pageno = (int *) (regs->regs[base + 6]); + struct mm_struct *mm = current->mm; + pgd_t *pgdp; + pmd_t *pmdp; + pte_t *ptep; + + if (!access_ok(VERIFY_WRITE, pageno, sizeof(int))) + return -EFAULT; + + down_read(&mm->mmap_sem); + pgdp = pgd_offset(mm, addr); + pmdp = pmd_offset(pgdp, addr); + ptep = pte_offset(pmdp, addr); + retval = -EINVAL; + if (ptep) { + pte_t pte = *ptep; + + if (pte_val(pte) & (_PAGE_VALID | _PAGE_PRESENT)) { + retval = put_user((pte_val(pte) & PAGE_MASK) >> + PAGE_SHIFT, pageno); + } + } + up_read(&mm->mmap_sem); + break; + } + + case SGI_INVENT: { + int arg1 = (int) regs->regs [base + 5]; + void *buffer = (void *) regs->regs [base + 6]; + int count = (int) regs->regs [base + 7]; + + switch (arg1) { + case SGI_INV_SIZEOF: + retval = sizeof (inventory_t); + break; + case SGI_INV_READ: + retval = dump_inventory_to_user (buffer, count); + break; + default: + retval = -EINVAL; + } + break; + } + + default: + printk("irix_syssgi: Unsupported command %d\n", (int)cmd); + retval = -EINVAL; + break; + }; + +out: + return retval; +} + +asmlinkage int irix_gtime(struct pt_regs *regs) +{ + return get_seconds(); +} + +/* + * IRIX is completely broken... it returns 0 on success, otherwise + * ENOMEM. + */ +asmlinkage int irix_brk(unsigned long brk) +{ + unsigned long rlim; + unsigned long newbrk, oldbrk; + struct mm_struct *mm = current->mm; + int ret; + + down_write(&mm->mmap_sem); + if (brk < mm->end_code) { + ret = -ENOMEM; + goto out; + } + + newbrk = PAGE_ALIGN(brk); + oldbrk = PAGE_ALIGN(mm->brk); + if (oldbrk == newbrk) { + mm->brk = brk; + ret = 0; + goto out; + } + + /* + * Always allow shrinking brk + */ + if (brk <= mm->brk) { + mm->brk = brk; + do_munmap(mm, newbrk, oldbrk-newbrk); + ret = 0; + goto out; + } + /* + * Check against rlimit and stack.. + */ + rlim = current->signal->rlim[RLIMIT_DATA].rlim_cur; + if (rlim >= RLIM_INFINITY) + rlim = ~0; + if (brk - mm->end_code > rlim) { + ret = -ENOMEM; + goto out; + } + + /* + * Check against existing mmap mappings. + */ + if (find_vma_intersection(mm, oldbrk, newbrk+PAGE_SIZE)) { + ret = -ENOMEM; + goto out; + } + + /* + * Check if we have enough memory.. + */ + if (security_vm_enough_memory((newbrk-oldbrk) >> PAGE_SHIFT)) { + ret = -ENOMEM; + goto out; + } + + /* + * Ok, looks good - let it rip. + */ + mm->brk = brk; + do_brk(oldbrk, newbrk-oldbrk); + ret = 0; + +out: + up_write(&mm->mmap_sem); + return ret; +} + +asmlinkage int irix_getpid(struct pt_regs *regs) +{ + regs->regs[3] = current->real_parent->pid; + return current->pid; +} + +asmlinkage int irix_getuid(struct pt_regs *regs) +{ + regs->regs[3] = current->euid; + return current->uid; +} + +asmlinkage int irix_getgid(struct pt_regs *regs) +{ + regs->regs[3] = current->egid; + return current->gid; +} + +asmlinkage int irix_stime(int value) +{ + int err; + struct timespec tv; + + tv.tv_sec = value; + tv.tv_nsec = 0; + err = security_settime(&tv, NULL); + if (err) + return err; + + write_seqlock_irq(&xtime_lock); + xtime.tv_sec = value; + xtime.tv_nsec = 0; + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + write_sequnlock_irq(&xtime_lock); + + return 0; +} + +static inline void jiffiestotv(unsigned long jiffies, struct timeval *value) +{ + value->tv_usec = (jiffies % HZ) * (1000000 / HZ); + value->tv_sec = jiffies / HZ; +} + +static inline void getitimer_real(struct itimerval *value) +{ + register unsigned long val, interval; + + interval = current->it_real_incr; + val = 0; + if (del_timer(¤t->real_timer)) { + unsigned long now = jiffies; + val = current->real_timer.expires; + add_timer(¤t->real_timer); + /* look out for negative/zero itimer.. */ + if (val <= now) + val = now+1; + val -= now; + } + jiffiestotv(val, &value->it_value); + jiffiestotv(interval, &value->it_interval); +} + +asmlinkage unsigned int irix_alarm(unsigned int seconds) +{ + struct itimerval it_new, it_old; + unsigned int oldalarm; + + if (!seconds) { + getitimer_real(&it_old); + del_timer(¤t->real_timer); + } else { + it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0; + it_new.it_value.tv_sec = seconds; + it_new.it_value.tv_usec = 0; + do_setitimer(ITIMER_REAL, &it_new, &it_old); + } + oldalarm = it_old.it_value.tv_sec; + /* + * ehhh.. We can't return 0 if we have an alarm pending ... + * And we'd better return too much than too little anyway + */ + if (it_old.it_value.tv_usec) + oldalarm++; + + return oldalarm; +} + +asmlinkage int irix_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + + return -EINTR; +} + +/* XXX need more than this... */ +asmlinkage int irix_mount(char *dev_name, char *dir_name, unsigned long flags, + char *type, void *data, int datalen) +{ + printk("[%s:%d] irix_mount(%p,%p,%08lx,%p,%p,%d)\n", + current->comm, current->pid, + dev_name, dir_name, flags, type, data, datalen); + + return sys_mount(dev_name, dir_name, type, flags, data); +} + +struct irix_statfs { + short f_type; + long f_bsize, f_frsize, f_blocks, f_bfree, f_files, f_ffree; + char f_fname[6], f_fpack[6]; +}; + +asmlinkage int irix_statfs(const char *path, struct irix_statfs *buf, + int len, int fs_type) +{ + struct nameidata nd; + struct kstatfs kbuf; + int error, i; + + /* We don't support this feature yet. */ + if (fs_type) { + error = -EINVAL; + goto out; + } + if (!access_ok(VERIFY_WRITE, buf, sizeof(struct irix_statfs))) { + error = -EFAULT; + goto out; + } + error = user_path_walk(path, &nd); + if (error) + goto out; + + error = vfs_statfs(nd.dentry->d_inode->i_sb, &kbuf); + if (error) + goto dput_and_out; + + __put_user(kbuf.f_type, &buf->f_type); + __put_user(kbuf.f_bsize, &buf->f_bsize); + __put_user(kbuf.f_frsize, &buf->f_frsize); + __put_user(kbuf.f_blocks, &buf->f_blocks); + __put_user(kbuf.f_bfree, &buf->f_bfree); + __put_user(kbuf.f_files, &buf->f_files); + __put_user(kbuf.f_ffree, &buf->f_ffree); + for (i = 0; i < 6; i++) { + __put_user(0, &buf->f_fname[i]); + __put_user(0, &buf->f_fpack[i]); + } + error = 0; + +dput_and_out: + path_release(&nd); +out: + return error; +} + +asmlinkage int irix_fstatfs(unsigned int fd, struct irix_statfs *buf) +{ + struct kstatfs kbuf; + struct file *file; + int error, i; + + if (!access_ok(VERIFY_WRITE, buf, sizeof(struct irix_statfs))) { + error = -EFAULT; + goto out; + } + if (!(file = fget(fd))) { + error = -EBADF; + goto out; + } + + error = vfs_statfs(file->f_dentry->d_inode->i_sb, &kbuf); + if (error) + goto out_f; + + __put_user(kbuf.f_type, &buf->f_type); + __put_user(kbuf.f_bsize, &buf->f_bsize); + __put_user(kbuf.f_frsize, &buf->f_frsize); + __put_user(kbuf.f_blocks, &buf->f_blocks); + __put_user(kbuf.f_bfree, &buf->f_bfree); + __put_user(kbuf.f_files, &buf->f_files); + __put_user(kbuf.f_ffree, &buf->f_ffree); + for(i = 0; i < 6; i++) { + __put_user(0, &buf->f_fname[i]); + __put_user(0, &buf->f_fpack[i]); + } + +out_f: + fput(file); +out: + return error; +} + +asmlinkage int irix_setpgrp(int flags) +{ + int error; + +#ifdef DEBUG_PROCGRPS + printk("[%s:%d] setpgrp(%d) ", current->comm, current->pid, flags); +#endif + if(!flags) + error = process_group(current); + else + error = sys_setsid(); +#ifdef DEBUG_PROCGRPS + printk("returning %d\n", process_group(current)); +#endif + + return error; +} + +asmlinkage int irix_times(struct tms * tbuf) +{ + int err = 0; + + if (tbuf) { + if (!access_ok(VERIFY_WRITE,tbuf,sizeof *tbuf)) + return -EFAULT; + err |= __put_user(current->utime, &tbuf->tms_utime); + err |= __put_user(current->stime, &tbuf->tms_stime); + err |= __put_user(current->signal->cutime, &tbuf->tms_cutime); + err |= __put_user(current->signal->cstime, &tbuf->tms_cstime); + } + + return err; +} + +asmlinkage int irix_exec(struct pt_regs *regs) +{ + int error, base = 0; + char *filename; + + if(regs->regs[2] == 1000) + base = 1; + filename = getname((char *) (long)regs->regs[base + 4]); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + return error; + + error = do_execve(filename, (char **) (long)regs->regs[base + 5], + (char **) 0, regs); + putname(filename); + + return error; +} + +asmlinkage int irix_exece(struct pt_regs *regs) +{ + int error, base = 0; + char *filename; + + if (regs->regs[2] == 1000) + base = 1; + filename = getname((char *) (long)regs->regs[base + 4]); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + return error; + error = do_execve(filename, (char **) (long)regs->regs[base + 5], + (char **) (long)regs->regs[base + 6], regs); + putname(filename); + + return error; +} + +asmlinkage unsigned long irix_gethostid(void) +{ + printk("[%s:%d]: irix_gethostid() called...\n", + current->comm, current->pid); + + return -EINVAL; +} + +asmlinkage unsigned long irix_sethostid(unsigned long val) +{ + printk("[%s:%d]: irix_sethostid(%08lx) called...\n", + current->comm, current->pid, val); + + return -EINVAL; +} + +asmlinkage int irix_socket(int family, int type, int protocol) +{ + switch(type) { + case 1: + type = SOCK_DGRAM; + break; + + case 2: + type = SOCK_STREAM; + break; + + case 3: + type = 9; /* Invalid... */ + break; + + case 4: + type = SOCK_RAW; + break; + + case 5: + type = SOCK_RDM; + break; + + case 6: + type = SOCK_SEQPACKET; + break; + + default: + break; + } + + return sys_socket(family, type, protocol); +} + +asmlinkage int irix_getdomainname(char *name, int len) +{ + int error; + + if (!access_ok(VERIFY_WRITE, name, len)) + return -EFAULT; + + down_read(&uts_sem); + if (len > __NEW_UTS_LEN) + len = __NEW_UTS_LEN; + error = 0; + if (copy_to_user(name, system_utsname.domainname, len)) + error = -EFAULT; + up_read(&uts_sem); + + return error; +} + +asmlinkage unsigned long irix_getpagesize(void) +{ + return PAGE_SIZE; +} + +asmlinkage int irix_msgsys(int opcode, unsigned long arg0, unsigned long arg1, + unsigned long arg2, unsigned long arg3, + unsigned long arg4) +{ + switch (opcode) { + case 0: + return sys_msgget((key_t) arg0, (int) arg1); + case 1: + return sys_msgctl((int) arg0, (int) arg1, (struct msqid_ds *)arg2); + case 2: + return sys_msgrcv((int) arg0, (struct msgbuf *) arg1, + (size_t) arg2, (long) arg3, (int) arg4); + case 3: + return sys_msgsnd((int) arg0, (struct msgbuf *) arg1, + (size_t) arg2, (int) arg3); + default: + return -EINVAL; + } +} + +asmlinkage int irix_shmsys(int opcode, unsigned long arg0, unsigned long arg1, + unsigned long arg2, unsigned long arg3) +{ + switch (opcode) { + case 0: + return do_shmat((int) arg0, (char *)arg1, (int) arg2, + (unsigned long *) arg3); + case 1: + return sys_shmctl((int)arg0, (int)arg1, (struct shmid_ds *)arg2); + case 2: + return sys_shmdt((char *)arg0); + case 3: + return sys_shmget((key_t) arg0, (int) arg1, (int) arg2); + default: + return -EINVAL; + } +} + +asmlinkage int irix_semsys(int opcode, unsigned long arg0, unsigned long arg1, + unsigned long arg2, int arg3) +{ + switch (opcode) { + case 0: + return sys_semctl((int) arg0, (int) arg1, (int) arg2, + (union semun) arg3); + case 1: + return sys_semget((key_t) arg0, (int) arg1, (int) arg2); + case 2: + return sys_semop((int) arg0, (struct sembuf *)arg1, + (unsigned int) arg2); + default: + return -EINVAL; + } +} + +static inline loff_t llseek(struct file *file, loff_t offset, int origin) +{ + loff_t (*fn)(struct file *, loff_t, int); + loff_t retval; + + fn = default_llseek; + if (file->f_op && file->f_op->llseek) + fn = file->f_op->llseek; + lock_kernel(); + retval = fn(file, offset, origin); + unlock_kernel(); + return retval; +} + +asmlinkage int irix_lseek64(int fd, int _unused, int offhi, int offlow, + int origin) +{ + int retval; + struct file * file; + loff_t offset; + + retval = -EBADF; + file = fget(fd); + if (!file) + goto bad; + retval = -EINVAL; + if (origin > 2) + goto out_putf; + + offset = llseek(file, ((loff_t) offhi << 32) | offlow, origin); + retval = (int) offset; + +out_putf: + fput(file); +bad: + return retval; +} + +asmlinkage int irix_sginap(int ticks) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(ticks); + return 0; +} + +asmlinkage int irix_sgikopt(char *istring, char *ostring, int len) +{ + return -EINVAL; +} + +asmlinkage int irix_gettimeofday(struct timeval *tv) +{ + time_t sec; + long nsec, seq; + int err; + + if (!access_ok(VERIFY_WRITE, tv, sizeof(struct timeval))) + return -EFAULT; + + do { + seq = read_seqbegin(&xtime_lock); + sec = xtime.tv_sec; + nsec = xtime.tv_nsec; + } while (read_seqretry(&xtime_lock, seq)); + + err = __put_user(sec, &tv->tv_sec); + err |= __put_user((nsec / 1000), &tv->tv_usec); + + return err; +} + +#define IRIX_MAP_AUTOGROW 0x40 + +asmlinkage unsigned long irix_mmap32(unsigned long addr, size_t len, int prot, + int flags, int fd, off_t offset) +{ + struct file *file = NULL; + unsigned long retval; + + if (!(flags & MAP_ANONYMOUS)) { + if (!(file = fget(fd))) + return -EBADF; + + /* Ok, bad taste hack follows, try to think in something else + * when reading this. */ + if (flags & IRIX_MAP_AUTOGROW) { + unsigned long old_pos; + long max_size = offset + len; + + if (max_size > file->f_dentry->d_inode->i_size) { + old_pos = sys_lseek (fd, max_size - 1, 0); + sys_write (fd, "", 1); + sys_lseek (fd, old_pos, 0); + } + } + } + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + + down_write(¤t->mm->mmap_sem); + retval = do_mmap(file, addr, len, prot, flags, offset); + up_write(¤t->mm->mmap_sem); + if (file) + fput(file); + + return retval; +} + +asmlinkage int irix_madvise(unsigned long addr, int len, int behavior) +{ + printk("[%s:%d] Wheee.. irix_madvise(%08lx,%d,%d)\n", + current->comm, current->pid, addr, len, behavior); + + return -EINVAL; +} + +asmlinkage int irix_pagelock(char *addr, int len, int op) +{ + printk("[%s:%d] Wheee.. irix_pagelock(%p,%d,%d)\n", + current->comm, current->pid, addr, len, op); + + return -EINVAL; +} + +asmlinkage int irix_quotactl(struct pt_regs *regs) +{ + printk("[%s:%d] Wheee.. irix_quotactl()\n", + current->comm, current->pid); + + return -EINVAL; +} + +asmlinkage int irix_BSDsetpgrp(int pid, int pgrp) +{ + int error; + +#ifdef DEBUG_PROCGRPS + printk("[%s:%d] BSDsetpgrp(%d, %d) ", current->comm, current->pid, + pid, pgrp); +#endif + if(!pid) + pid = current->pid; + + /* Wheee, weird sysv thing... */ + if ((pgrp == 0) && (pid == current->pid)) + error = sys_setsid(); + else + error = sys_setpgid(pid, pgrp); + +#ifdef DEBUG_PROCGRPS + printk("error = %d\n", error); +#endif + + return error; +} + +asmlinkage int irix_systeminfo(int cmd, char *buf, int cnt) +{ + printk("[%s:%d] Wheee.. irix_systeminfo(%d,%p,%d)\n", + current->comm, current->pid, cmd, buf, cnt); + + return -EINVAL; +} + +struct iuname { + char sysname[257], nodename[257], release[257]; + char version[257], machine[257]; + char m_type[257], base_rel[257]; + char _unused0[257], _unused1[257], _unused2[257]; + char _unused3[257], _unused4[257], _unused5[257]; +}; + +asmlinkage int irix_uname(struct iuname *buf) +{ + down_read(&uts_sem); + if (copy_to_user(system_utsname.sysname, buf->sysname, 65) + || copy_to_user(system_utsname.nodename, buf->nodename, 65) + || copy_to_user(system_utsname.release, buf->release, 65) + || copy_to_user(system_utsname.version, buf->version, 65) + || copy_to_user(system_utsname.machine, buf->machine, 65)) { + return -EFAULT; + } + up_read(&uts_sem); + + return 1; +} + +#undef DEBUG_XSTAT + +static int irix_xstat32_xlate(struct kstat *stat, void *ubuf) +{ + struct xstat32 { + u32 st_dev, st_pad1[3], st_ino, st_mode, st_nlink, st_uid, st_gid; + u32 st_rdev, st_pad2[2], st_size, st_pad3; + u32 st_atime0, st_atime1; + u32 st_mtime0, st_mtime1; + u32 st_ctime0, st_ctime1; + u32 st_blksize, st_blocks; + char st_fstype[16]; + u32 st_pad4[8]; + } ub; + + if (!sysv_valid_dev(stat->dev) || !sysv_valid_dev(stat->rdev)) + return -EOVERFLOW; + ub.st_dev = sysv_encode_dev(stat->dev); + ub.st_ino = stat->ino; + ub.st_mode = stat->mode; + ub.st_nlink = stat->nlink; + SET_UID(ub.st_uid, stat->uid); + SET_GID(ub.st_gid, stat->gid); + ub.st_rdev = sysv_encode_dev(stat->rdev); +#if BITS_PER_LONG == 32 + if (stat->size > MAX_NON_LFS) + return -EOVERFLOW; +#endif + ub.st_size = stat->size; + ub.st_atime0 = stat->atime.tv_sec; + ub.st_atime1 = stat->atime.tv_nsec; + ub.st_mtime0 = stat->mtime.tv_sec; + ub.st_mtime1 = stat->atime.tv_nsec; + ub.st_ctime0 = stat->ctime.tv_sec; + ub.st_ctime1 = stat->atime.tv_nsec; + ub.st_blksize = stat->blksize; + ub.st_blocks = stat->blocks; + strcpy (ub.st_fstype, "efs"); + + return copy_to_user(ubuf, &ub, sizeof(ub)) ? -EFAULT : 0; +} + +static int irix_xstat64_xlate(struct kstat *stat, void *ubuf) +{ + struct xstat64 { + u32 st_dev; s32 st_pad1[3]; + unsigned long long st_ino; + u32 st_mode; + u32 st_nlink; s32 st_uid; s32 st_gid; u32 st_rdev; + s32 st_pad2[2]; + long long st_size; + s32 st_pad3; + struct { s32 tv_sec, tv_nsec; } st_atime, st_mtime, st_ctime; + s32 st_blksize; + long long st_blocks; + char st_fstype[16]; + s32 st_pad4[8]; + } ks; + + if (!sysv_valid_dev(stat->dev) || !sysv_valid_dev(stat->rdev)) + return -EOVERFLOW; + + ks.st_dev = sysv_encode_dev(stat->dev); + ks.st_pad1[0] = ks.st_pad1[1] = ks.st_pad1[2] = 0; + ks.st_ino = (unsigned long long) stat->ino; + ks.st_mode = (u32) stat->mode; + ks.st_nlink = (u32) stat->nlink; + ks.st_uid = (s32) stat->uid; + ks.st_gid = (s32) stat->gid; + ks.st_rdev = sysv_encode_dev (stat->rdev); + ks.st_pad2[0] = ks.st_pad2[1] = 0; + ks.st_size = (long long) stat->size; + ks.st_pad3 = 0; + + /* XXX hackety hack... */ + ks.st_atime.tv_sec = (s32) stat->atime.tv_sec; + ks.st_atime.tv_nsec = stat->atime.tv_nsec; + ks.st_mtime.tv_sec = (s32) stat->mtime.tv_sec; + ks.st_mtime.tv_nsec = stat->mtime.tv_nsec; + ks.st_ctime.tv_sec = (s32) stat->ctime.tv_sec; + ks.st_ctime.tv_nsec = stat->ctime.tv_nsec; + + ks.st_blksize = (s32) stat->blksize; + ks.st_blocks = (long long) stat->blocks; + memset(ks.st_fstype, 0, 16); + ks.st_pad4[0] = ks.st_pad4[1] = ks.st_pad4[2] = ks.st_pad4[3] = 0; + ks.st_pad4[4] = ks.st_pad4[5] = ks.st_pad4[6] = ks.st_pad4[7] = 0; + + /* Now write it all back. */ + return copy_to_user(ubuf, &ks, sizeof(ks)) ? -EFAULT : 0; +} + +asmlinkage int irix_xstat(int version, char *filename, struct stat *statbuf) +{ + int retval; + struct kstat stat; + +#ifdef DEBUG_XSTAT + printk("[%s:%d] Wheee.. irix_xstat(%d,%s,%p) ", + current->comm, current->pid, version, filename, statbuf); +#endif + + retval = vfs_stat(filename, &stat); + if (!retval) { + switch(version) { + case 2: + retval = irix_xstat32_xlate(&stat, statbuf); + break; + case 3: + retval = irix_xstat64_xlate(&stat, statbuf); + break; + default: + retval = -EINVAL; + } + } + return retval; +} + +asmlinkage int irix_lxstat(int version, char *filename, struct stat *statbuf) +{ + int error; + struct kstat stat; + +#ifdef DEBUG_XSTAT + printk("[%s:%d] Wheee.. irix_lxstat(%d,%s,%p) ", + current->comm, current->pid, version, filename, statbuf); +#endif + + error = vfs_lstat(filename, &stat); + + if (!error) { + switch (version) { + case 2: + error = irix_xstat32_xlate(&stat, statbuf); + break; + case 3: + error = irix_xstat64_xlate(&stat, statbuf); + break; + default: + error = -EINVAL; + } + } + return error; +} + +asmlinkage int irix_fxstat(int version, int fd, struct stat *statbuf) +{ + int error; + struct kstat stat; + +#ifdef DEBUG_XSTAT + printk("[%s:%d] Wheee.. irix_fxstat(%d,%d,%p) ", + current->comm, current->pid, version, fd, statbuf); +#endif + + error = vfs_fstat(fd, &stat); + if (!error) { + switch (version) { + case 2: + error = irix_xstat32_xlate(&stat, statbuf); + break; + case 3: + error = irix_xstat64_xlate(&stat, statbuf); + break; + default: + error = -EINVAL; + } + } + return error; +} + +asmlinkage int irix_xmknod(int ver, char *filename, int mode, unsigned dev) +{ + int retval; + printk("[%s:%d] Wheee.. irix_xmknod(%d,%s,%x,%x)\n", + current->comm, current->pid, ver, filename, mode, dev); + + switch(ver) { + case 2: + /* shouldn't we convert here as well as on stat()? */ + retval = sys_mknod(filename, mode, dev); + break; + + default: + retval = -EINVAL; + break; + }; + + return retval; +} + +asmlinkage int irix_swapctl(int cmd, char *arg) +{ + printk("[%s:%d] Wheee.. irix_swapctl(%d,%p)\n", + current->comm, current->pid, cmd, arg); + + return -EINVAL; +} + +struct irix_statvfs { + u32 f_bsize; u32 f_frsize; u32 f_blocks; + u32 f_bfree; u32 f_bavail; u32 f_files; u32 f_ffree; u32 f_favail; + u32 f_fsid; char f_basetype[16]; + u32 f_flag; u32 f_namemax; + char f_fstr[32]; u32 f_filler[16]; +}; + +asmlinkage int irix_statvfs(char *fname, struct irix_statvfs *buf) +{ + struct nameidata nd; + struct kstatfs kbuf; + int error, i; + + printk("[%s:%d] Wheee.. irix_statvfs(%s,%p)\n", + current->comm, current->pid, fname, buf); + if (!access_ok(VERIFY_WRITE, buf, sizeof(struct irix_statvfs))) { + error = -EFAULT; + goto out; + } + error = user_path_walk(fname, &nd); + if (error) + goto out; + error = vfs_statfs(nd.dentry->d_inode->i_sb, &kbuf); + if (error) + goto dput_and_out; + + __put_user(kbuf.f_bsize, &buf->f_bsize); + __put_user(kbuf.f_frsize, &buf->f_frsize); + __put_user(kbuf.f_blocks, &buf->f_blocks); + __put_user(kbuf.f_bfree, &buf->f_bfree); + __put_user(kbuf.f_bfree, &buf->f_bavail); /* XXX hackety hack... */ + __put_user(kbuf.f_files, &buf->f_files); + __put_user(kbuf.f_ffree, &buf->f_ffree); + __put_user(kbuf.f_ffree, &buf->f_favail); /* XXX hackety hack... */ +#ifdef __MIPSEB__ + __put_user(kbuf.f_fsid.val[1], &buf->f_fsid); +#else + __put_user(kbuf.f_fsid.val[0], &buf->f_fsid); +#endif + for (i = 0; i < 16; i++) + __put_user(0, &buf->f_basetype[i]); + __put_user(0, &buf->f_flag); + __put_user(kbuf.f_namelen, &buf->f_namemax); + for (i = 0; i < 32; i++) + __put_user(0, &buf->f_fstr[i]); + + error = 0; + +dput_and_out: + path_release(&nd); +out: + return error; +} + +asmlinkage int irix_fstatvfs(int fd, struct irix_statvfs *buf) +{ + struct kstatfs kbuf; + struct file *file; + int error, i; + + printk("[%s:%d] Wheee.. irix_fstatvfs(%d,%p)\n", + current->comm, current->pid, fd, buf); + + if (!access_ok(VERIFY_WRITE, buf, sizeof(struct irix_statvfs))) { + error = -EFAULT; + goto out; + } + if (!(file = fget(fd))) { + error = -EBADF; + goto out; + } + error = vfs_statfs(file->f_dentry->d_inode->i_sb, &kbuf); + if (error) + goto out_f; + + __put_user(kbuf.f_bsize, &buf->f_bsize); + __put_user(kbuf.f_frsize, &buf->f_frsize); + __put_user(kbuf.f_blocks, &buf->f_blocks); + __put_user(kbuf.f_bfree, &buf->f_bfree); + __put_user(kbuf.f_bfree, &buf->f_bavail); /* XXX hackety hack... */ + __put_user(kbuf.f_files, &buf->f_files); + __put_user(kbuf.f_ffree, &buf->f_ffree); + __put_user(kbuf.f_ffree, &buf->f_favail); /* XXX hackety hack... */ +#ifdef __MIPSEB__ + __put_user(kbuf.f_fsid.val[1], &buf->f_fsid); +#else + __put_user(kbuf.f_fsid.val[0], &buf->f_fsid); +#endif + for(i = 0; i < 16; i++) + __put_user(0, &buf->f_basetype[i]); + __put_user(0, &buf->f_flag); + __put_user(kbuf.f_namelen, &buf->f_namemax); + __clear_user(&buf->f_fstr, sizeof(buf->f_fstr)); + +out_f: + fput(file); +out: + return error; +} + +asmlinkage int irix_priocntl(struct pt_regs *regs) +{ + printk("[%s:%d] Wheee.. irix_priocntl()\n", + current->comm, current->pid); + + return -EINVAL; +} + +asmlinkage int irix_sigqueue(int pid, int sig, int code, int val) +{ + printk("[%s:%d] Wheee.. irix_sigqueue(%d,%d,%d,%d)\n", + current->comm, current->pid, pid, sig, code, val); + + return -EINVAL; +} + +asmlinkage int irix_truncate64(char *name, int pad, int size1, int size2) +{ + int retval; + + if (size1) { + retval = -EINVAL; + goto out; + } + retval = sys_truncate(name, size2); + +out: + return retval; +} + +asmlinkage int irix_ftruncate64(int fd, int pad, int size1, int size2) +{ + int retval; + + if (size1) { + retval = -EINVAL; + goto out; + } + retval = sys_ftruncate(fd, size2); + +out: + return retval; +} + +asmlinkage int irix_mmap64(struct pt_regs *regs) +{ + int len, prot, flags, fd, off1, off2, error, base = 0; + unsigned long addr, pgoff, *sp; + struct file *file = NULL; + + if (regs->regs[2] == 1000) + base = 1; + sp = (unsigned long *) (regs->regs[29] + 16); + addr = regs->regs[base + 4]; + len = regs->regs[base + 5]; + prot = regs->regs[base + 6]; + if (!base) { + flags = regs->regs[base + 7]; + if (!access_ok(VERIFY_READ, sp, (4 * sizeof(unsigned long)))) { + error = -EFAULT; + goto out; + } + fd = sp[0]; + __get_user(off1, &sp[1]); + __get_user(off2, &sp[2]); + } else { + if (!access_ok(VERIFY_READ, sp, (5 * sizeof(unsigned long)))) { + error = -EFAULT; + goto out; + } + __get_user(flags, &sp[0]); + __get_user(fd, &sp[1]); + __get_user(off1, &sp[2]); + __get_user(off2, &sp[3]); + } + + if (off1 & PAGE_MASK) { + error = -EOVERFLOW; + goto out; + } + + pgoff = (off1 << (32 - PAGE_SHIFT)) | (off2 >> PAGE_SHIFT); + + if (!(flags & MAP_ANONYMOUS)) { + if (!(file = fget(fd))) { + error = -EBADF; + goto out; + } + + /* Ok, bad taste hack follows, try to think in something else + when reading this */ + if (flags & IRIX_MAP_AUTOGROW) { + unsigned long old_pos; + long max_size = off2 + len; + + if (max_size > file->f_dentry->d_inode->i_size) { + old_pos = sys_lseek (fd, max_size - 1, 0); + sys_write (fd, "", 1); + sys_lseek (fd, old_pos, 0); + } + } + } + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + + down_write(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up_write(¤t->mm->mmap_sem); + + if (file) + fput(file); + +out: + return error; +} + +asmlinkage int irix_dmi(struct pt_regs *regs) +{ + printk("[%s:%d] Wheee.. irix_dmi()\n", + current->comm, current->pid); + + return -EINVAL; +} + +asmlinkage int irix_pread(int fd, char *buf, int cnt, int off64, + int off1, int off2) +{ + printk("[%s:%d] Wheee.. irix_pread(%d,%p,%d,%d,%d,%d)\n", + current->comm, current->pid, fd, buf, cnt, off64, off1, off2); + + return -EINVAL; +} + +asmlinkage int irix_pwrite(int fd, char *buf, int cnt, int off64, + int off1, int off2) +{ + printk("[%s:%d] Wheee.. irix_pwrite(%d,%p,%d,%d,%d,%d)\n", + current->comm, current->pid, fd, buf, cnt, off64, off1, off2); + + return -EINVAL; +} + +asmlinkage int irix_sgifastpath(int cmd, unsigned long arg0, unsigned long arg1, + unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5) +{ + printk("[%s:%d] Wheee.. irix_fastpath(%d,%08lx,%08lx,%08lx,%08lx," + "%08lx,%08lx)\n", + current->comm, current->pid, cmd, arg0, arg1, arg2, + arg3, arg4, arg5); + + return -EINVAL; +} + +struct irix_statvfs64 { + u32 f_bsize; u32 f_frsize; + u64 f_blocks; u64 f_bfree; u64 f_bavail; + u64 f_files; u64 f_ffree; u64 f_favail; + u32 f_fsid; + char f_basetype[16]; + u32 f_flag; u32 f_namemax; + char f_fstr[32]; + u32 f_filler[16]; +}; + +asmlinkage int irix_statvfs64(char *fname, struct irix_statvfs64 *buf) +{ + struct nameidata nd; + struct kstatfs kbuf; + int error, i; + + printk("[%s:%d] Wheee.. irix_statvfs64(%s,%p)\n", + current->comm, current->pid, fname, buf); + if (!access_ok(VERIFY_WRITE, buf, sizeof(struct irix_statvfs64))) { + error = -EFAULT; + goto out; + } + error = user_path_walk(fname, &nd); + if (error) + goto out; + error = vfs_statfs(nd.dentry->d_inode->i_sb, &kbuf); + if (error) + goto dput_and_out; + + __put_user(kbuf.f_bsize, &buf->f_bsize); + __put_user(kbuf.f_frsize, &buf->f_frsize); + __put_user(kbuf.f_blocks, &buf->f_blocks); + __put_user(kbuf.f_bfree, &buf->f_bfree); + __put_user(kbuf.f_bfree, &buf->f_bavail); /* XXX hackety hack... */ + __put_user(kbuf.f_files, &buf->f_files); + __put_user(kbuf.f_ffree, &buf->f_ffree); + __put_user(kbuf.f_ffree, &buf->f_favail); /* XXX hackety hack... */ +#ifdef __MIPSEB__ + __put_user(kbuf.f_fsid.val[1], &buf->f_fsid); +#else + __put_user(kbuf.f_fsid.val[0], &buf->f_fsid); +#endif + for(i = 0; i < 16; i++) + __put_user(0, &buf->f_basetype[i]); + __put_user(0, &buf->f_flag); + __put_user(kbuf.f_namelen, &buf->f_namemax); + for(i = 0; i < 32; i++) + __put_user(0, &buf->f_fstr[i]); + + error = 0; + +dput_and_out: + path_release(&nd); +out: + return error; +} + +asmlinkage int irix_fstatvfs64(int fd, struct irix_statvfs *buf) +{ + struct kstatfs kbuf; + struct file *file; + int error, i; + + printk("[%s:%d] Wheee.. irix_fstatvfs64(%d,%p)\n", + current->comm, current->pid, fd, buf); + + if (!access_ok(VERIFY_WRITE, buf, sizeof(struct irix_statvfs))) { + error = -EFAULT; + goto out; + } + if (!(file = fget(fd))) { + error = -EBADF; + goto out; + } + error = vfs_statfs(file->f_dentry->d_inode->i_sb, &kbuf); + if (error) + goto out_f; + + __put_user(kbuf.f_bsize, &buf->f_bsize); + __put_user(kbuf.f_frsize, &buf->f_frsize); + __put_user(kbuf.f_blocks, &buf->f_blocks); + __put_user(kbuf.f_bfree, &buf->f_bfree); + __put_user(kbuf.f_bfree, &buf->f_bavail); /* XXX hackety hack... */ + __put_user(kbuf.f_files, &buf->f_files); + __put_user(kbuf.f_ffree, &buf->f_ffree); + __put_user(kbuf.f_ffree, &buf->f_favail); /* XXX hackety hack... */ +#ifdef __MIPSEB__ + __put_user(kbuf.f_fsid.val[1], &buf->f_fsid); +#else + __put_user(kbuf.f_fsid.val[0], &buf->f_fsid); +#endif + for(i = 0; i < 16; i++) + __put_user(0, &buf->f_basetype[i]); + __put_user(0, &buf->f_flag); + __put_user(kbuf.f_namelen, &buf->f_namemax); + __clear_user(buf->f_fstr, sizeof(buf->f_fstr[i])); + +out_f: + fput(file); +out: + return error; +} + +asmlinkage int irix_getmountid(char *fname, unsigned long *midbuf) +{ + int err = 0; + + printk("[%s:%d] irix_getmountid(%s, %p)\n", + current->comm, current->pid, fname, midbuf); + if (!access_ok(VERIFY_WRITE, midbuf, (sizeof(unsigned long) * 4))) + return -EFAULT; + + /* + * The idea with this system call is that when trying to determine + * 'pwd' and it's a toss-up for some reason, userland can use the + * fsid of the filesystem to try and make the right decision, but + * we don't have this so for now. XXX + */ + err |= __put_user(0, &midbuf[0]); + err |= __put_user(0, &midbuf[1]); + err |= __put_user(0, &midbuf[2]); + err |= __put_user(0, &midbuf[3]); + + return err; +} + +asmlinkage int irix_nsproc(unsigned long entry, unsigned long mask, + unsigned long arg, unsigned long sp, int slen) +{ + printk("[%s:%d] Wheee.. irix_nsproc(%08lx,%08lx,%08lx,%08lx,%d)\n", + current->comm, current->pid, entry, mask, arg, sp, slen); + + return -EINVAL; +} + +#undef DEBUG_GETDENTS + +struct irix_dirent32 { + u32 d_ino; + u32 d_off; + unsigned short d_reclen; + char d_name[1]; +}; + +struct irix_dirent32_callback { + struct irix_dirent32 *current_dir; + struct irix_dirent32 *previous; + int count; + int error; +}; + +#define NAME_OFFSET32(de) ((int) ((de)->d_name - (char *) (de))) +#define ROUND_UP32(x) (((x)+sizeof(u32)-1) & ~(sizeof(u32)-1)) + +static int irix_filldir32(void *__buf, const char *name, int namlen, + loff_t offset, ino_t ino, unsigned int d_type) +{ + struct irix_dirent32 *dirent; + struct irix_dirent32_callback *buf = + (struct irix_dirent32_callback *)__buf; + unsigned short reclen = ROUND_UP32(NAME_OFFSET32(dirent) + namlen + 1); + +#ifdef DEBUG_GETDENTS + printk("\nirix_filldir32[reclen<%d>namlen<%d>count<%d>]", + reclen, namlen, buf->count); +#endif + buf->error = -EINVAL; /* only used if we fail.. */ + if (reclen > buf->count) + return -EINVAL; + dirent = buf->previous; + if (dirent) + __put_user(offset, &dirent->d_off); + dirent = buf->current_dir; + buf->previous = dirent; + __put_user(ino, &dirent->d_ino); + __put_user(reclen, &dirent->d_reclen); + copy_to_user(dirent->d_name, name, namlen); + __put_user(0, &dirent->d_name[namlen]); + ((char *) dirent) += reclen; + buf->current_dir = dirent; + buf->count -= reclen; + + return 0; +} + +asmlinkage int irix_ngetdents(unsigned int fd, void * dirent, + unsigned int count, int *eob) +{ + struct file *file; + struct irix_dirent32 *lastdirent; + struct irix_dirent32_callback buf; + int error; + +#ifdef DEBUG_GETDENTS + printk("[%s:%d] ngetdents(%d, %p, %d, %p) ", current->comm, + current->pid, fd, dirent, count, eob); +#endif + error = -EBADF; + file = fget(fd); + if (!file) + goto out; + + buf.current_dir = (struct irix_dirent32 *) dirent; + buf.previous = NULL; + buf.count = count; + buf.error = 0; + + error = vfs_readdir(file, irix_filldir32, &buf); + if (error < 0) + goto out_putf; + + error = buf.error; + lastdirent = buf.previous; + if (lastdirent) { + put_user(file->f_pos, &lastdirent->d_off); + error = count - buf.count; + } + + if (put_user(0, eob) < 0) { + error = -EFAULT; + goto out_putf; + } + +#ifdef DEBUG_GETDENTS + printk("eob=%d returning %d\n", *eob, count - buf.count); +#endif + error = count - buf.count; + +out_putf: + fput(file); +out: + return error; +} + +struct irix_dirent64 { + u64 d_ino; + u64 d_off; + unsigned short d_reclen; + char d_name[1]; +}; + +struct irix_dirent64_callback { + struct irix_dirent64 *curr; + struct irix_dirent64 *previous; + int count; + int error; +}; + +#define NAME_OFFSET64(de) ((int) ((de)->d_name - (char *) (de))) +#define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1)) + +static int irix_filldir64(void * __buf, const char * name, int namlen, + loff_t offset, ino_t ino, unsigned int d_type) +{ + struct irix_dirent64 *dirent; + struct irix_dirent64_callback * buf = + (struct irix_dirent64_callback *) __buf; + unsigned short reclen = ROUND_UP64(NAME_OFFSET64(dirent) + namlen + 1); + + buf->error = -EINVAL; /* only used if we fail.. */ + if (reclen > buf->count) + return -EINVAL; + dirent = buf->previous; + if (dirent) + __put_user(offset, &dirent->d_off); + dirent = buf->curr; + buf->previous = dirent; + __put_user(ino, &dirent->d_ino); + __put_user(reclen, &dirent->d_reclen); + __copy_to_user(dirent->d_name, name, namlen); + __put_user(0, &dirent->d_name[namlen]); + ((char *) dirent) += reclen; + buf->curr = dirent; + buf->count -= reclen; + + return 0; +} + +asmlinkage int irix_getdents64(int fd, void *dirent, int cnt) +{ + struct file *file; + struct irix_dirent64 *lastdirent; + struct irix_dirent64_callback buf; + int error; + +#ifdef DEBUG_GETDENTS + printk("[%s:%d] getdents64(%d, %p, %d) ", current->comm, + current->pid, fd, dirent, cnt); +#endif + error = -EBADF; + if (!(file = fget(fd))) + goto out; + + error = -EFAULT; + if (!access_ok(VERIFY_WRITE, dirent, cnt)) + goto out_f; + + error = -EINVAL; + if (cnt < (sizeof(struct irix_dirent64) + 255)) + goto out_f; + + buf.curr = (struct irix_dirent64 *) dirent; + buf.previous = NULL; + buf.count = cnt; + buf.error = 0; + error = vfs_readdir(file, irix_filldir64, &buf); + if (error < 0) + goto out_f; + lastdirent = buf.previous; + if (!lastdirent) { + error = buf.error; + goto out_f; + } + lastdirent->d_off = (u64) file->f_pos; +#ifdef DEBUG_GETDENTS + printk("returning %d\n", cnt - buf.count); +#endif + error = cnt - buf.count; + +out_f: + fput(file); +out: + return error; +} + +asmlinkage int irix_ngetdents64(int fd, void *dirent, int cnt, int *eob) +{ + struct file *file; + struct irix_dirent64 *lastdirent; + struct irix_dirent64_callback buf; + int error; + +#ifdef DEBUG_GETDENTS + printk("[%s:%d] ngetdents64(%d, %p, %d) ", current->comm, + current->pid, fd, dirent, cnt); +#endif + error = -EBADF; + if (!(file = fget(fd))) + goto out; + + error = -EFAULT; + if (!access_ok(VERIFY_WRITE, dirent, cnt) || + !access_ok(VERIFY_WRITE, eob, sizeof(*eob))) + goto out_f; + + error = -EINVAL; + if (cnt < (sizeof(struct irix_dirent64) + 255)) + goto out_f; + + *eob = 0; + buf.curr = (struct irix_dirent64 *) dirent; + buf.previous = NULL; + buf.count = cnt; + buf.error = 0; + error = vfs_readdir(file, irix_filldir64, &buf); + if (error < 0) + goto out_f; + lastdirent = buf.previous; + if (!lastdirent) { + error = buf.error; + goto out_f; + } + lastdirent->d_off = (u64) file->f_pos; +#ifdef DEBUG_GETDENTS + printk("eob=%d returning %d\n", *eob, cnt - buf.count); +#endif + error = cnt - buf.count; + +out_f: + fput(file); +out: + return error; +} + +asmlinkage int irix_uadmin(unsigned long op, unsigned long func, unsigned long arg) +{ + int retval; + + switch (op) { + case 1: + /* Reboot */ + printk("[%s:%d] irix_uadmin: Wants to reboot...\n", + current->comm, current->pid); + retval = -EINVAL; + goto out; + + case 2: + /* Shutdown */ + printk("[%s:%d] irix_uadmin: Wants to shutdown...\n", + current->comm, current->pid); + retval = -EINVAL; + goto out; + + case 4: + /* Remount-root */ + printk("[%s:%d] irix_uadmin: Wants to remount root...\n", + current->comm, current->pid); + retval = -EINVAL; + goto out; + + case 8: + /* Kill all tasks. */ + printk("[%s:%d] irix_uadmin: Wants to kill all tasks...\n", + current->comm, current->pid); + retval = -EINVAL; + goto out; + + case 256: + /* Set magic mushrooms... */ + printk("[%s:%d] irix_uadmin: Wants to set magic mushroom[%d]...\n", + current->comm, current->pid, (int) func); + retval = -EINVAL; + goto out; + + default: + printk("[%s:%d] irix_uadmin: Unknown operation [%d]...\n", + current->comm, current->pid, (int) op); + retval = -EINVAL; + goto out; + }; + +out: + return retval; +} + +asmlinkage int irix_utssys(char *inbuf, int arg, int type, char *outbuf) +{ + int retval; + + switch(type) { + case 0: + /* uname() */ + retval = irix_uname((struct iuname *)inbuf); + goto out; + + case 2: + /* ustat() */ + printk("[%s:%d] irix_utssys: Wants to do ustat()\n", + current->comm, current->pid); + retval = -EINVAL; + goto out; + + case 3: + /* fusers() */ + printk("[%s:%d] irix_utssys: Wants to do fusers()\n", + current->comm, current->pid); + retval = -EINVAL; + goto out; + + default: + printk("[%s:%d] irix_utssys: Wants to do unknown type[%d]\n", + current->comm, current->pid, (int) type); + retval = -EINVAL; + goto out; + } + +out: + return retval; +} + +#undef DEBUG_FCNTL + +#define IRIX_F_ALLOCSP 10 + +asmlinkage int irix_fcntl(int fd, int cmd, int arg) +{ + int retval; + +#ifdef DEBUG_FCNTL + printk("[%s:%d] irix_fcntl(%d, %d, %d) ", current->comm, + current->pid, fd, cmd, arg); +#endif + if (cmd == IRIX_F_ALLOCSP){ + return 0; + } + retval = sys_fcntl(fd, cmd, arg); +#ifdef DEBUG_FCNTL + printk("%d\n", retval); +#endif + return retval; +} + +asmlinkage int irix_ulimit(int cmd, int arg) +{ + int retval; + + switch(cmd) { + case 1: + printk("[%s:%d] irix_ulimit: Wants to get file size limit.\n", + current->comm, current->pid); + retval = -EINVAL; + goto out; + + case 2: + printk("[%s:%d] irix_ulimit: Wants to set file size limit.\n", + current->comm, current->pid); + retval = -EINVAL; + goto out; + + case 3: + printk("[%s:%d] irix_ulimit: Wants to get brk limit.\n", + current->comm, current->pid); + retval = -EINVAL; + goto out; + + case 4: +#if 0 + printk("[%s:%d] irix_ulimit: Wants to get fd limit.\n", + current->comm, current->pid); + retval = -EINVAL; + goto out; +#endif + retval = current->signal->rlim[RLIMIT_NOFILE].rlim_cur; + goto out; + + case 5: + printk("[%s:%d] irix_ulimit: Wants to get txt offset.\n", + current->comm, current->pid); + retval = -EINVAL; + goto out; + + default: + printk("[%s:%d] irix_ulimit: Unknown command [%d].\n", + current->comm, current->pid, cmd); + retval = -EINVAL; + goto out; + } +out: + return retval; +} + +asmlinkage int irix_unimp(struct pt_regs *regs) +{ + printk("irix_unimp [%s:%d] v0=%d v1=%d a0=%08lx a1=%08lx a2=%08lx " + "a3=%08lx\n", current->comm, current->pid, + (int) regs->regs[2], (int) regs->regs[3], + regs->regs[4], regs->regs[5], regs->regs[6], regs->regs[7]); + + return -ENOSYS; +} diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c new file mode 100644 index 000000000000..648c82292ed6 --- /dev/null +++ b/arch/mips/kernel/time.c @@ -0,0 +1,755 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * Copyright (c) 2003, 2004 Maciej W. Rozycki + * + * Common time service routines for MIPS machines. See + * Documentation/mips/time.README. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/param.h> +#include <linux/time.h> +#include <linux/timex.h> +#include <linux/smp.h> +#include <linux/kernel_stat.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/module.h> + +#include <asm/bootinfo.h> +#include <asm/compiler.h> +#include <asm/cpu.h> +#include <asm/cpu-features.h> +#include <asm/div64.h> +#include <asm/sections.h> +#include <asm/time.h> + +/* + * The integer part of the number of usecs per jiffy is taken from tick, + * but the fractional part is not recorded, so we calculate it using the + * initial value of HZ. This aids systems where tick isn't really an + * integer (e.g. for HZ = 128). + */ +#define USECS_PER_JIFFY TICK_SIZE +#define USECS_PER_JIFFY_FRAC ((unsigned long)(u32)((1000000ULL << 32) / HZ)) + +#define TICK_SIZE (tick_nsec / 1000) + +u64 jiffies_64 = INITIAL_JIFFIES; + +EXPORT_SYMBOL(jiffies_64); + +/* + * forward reference + */ +extern volatile unsigned long wall_jiffies; + +DEFINE_SPINLOCK(rtc_lock); + +/* + * By default we provide the null RTC ops + */ +static unsigned long null_rtc_get_time(void) +{ + return mktime(2000, 1, 1, 0, 0, 0); +} + +static int null_rtc_set_time(unsigned long sec) +{ + return 0; +} + +unsigned long (*rtc_get_time)(void) = null_rtc_get_time; +int (*rtc_set_time)(unsigned long) = null_rtc_set_time; +int (*rtc_set_mmss)(unsigned long); + + +/* usecs per counter cycle, shifted to left by 32 bits */ +static unsigned int sll32_usecs_per_cycle; + +/* how many counter cycles in a jiffy */ +static unsigned long cycles_per_jiffy; + +/* Cycle counter value at the previous timer interrupt.. */ +static unsigned int timerhi, timerlo; + +/* expirelo is the count value for next CPU timer interrupt */ +static unsigned int expirelo; + + +/* + * Null timer ack for systems not needing one (e.g. i8254). + */ +static void null_timer_ack(void) { /* nothing */ } + +/* + * Null high precision timer functions for systems lacking one. + */ +static unsigned int null_hpt_read(void) +{ + return 0; +} + +static void null_hpt_init(unsigned int count) { /* nothing */ } + + +/* + * Timer ack for an R4k-compatible timer of a known frequency. + */ +static void c0_timer_ack(void) +{ + unsigned int count; + + /* Ack this timer interrupt and set the next one. */ + expirelo += cycles_per_jiffy; + write_c0_compare(expirelo); + + /* Check to see if we have missed any timer interrupts. */ + count = read_c0_count(); + if ((count - expirelo) < 0x7fffffff) { + /* missed_timer_count++; */ + expirelo = count + cycles_per_jiffy; + write_c0_compare(expirelo); + } +} + +/* + * High precision timer functions for a R4k-compatible timer. + */ +static unsigned int c0_hpt_read(void) +{ + return read_c0_count(); +} + +/* For use solely as a high precision timer. */ +static void c0_hpt_init(unsigned int count) +{ + write_c0_count(read_c0_count() - count); +} + +/* For use both as a high precision timer and an interrupt source. */ +static void c0_hpt_timer_init(unsigned int count) +{ + count = read_c0_count() - count; + expirelo = (count / cycles_per_jiffy + 1) * cycles_per_jiffy; + write_c0_count(expirelo - cycles_per_jiffy); + write_c0_compare(expirelo); + write_c0_count(count); +} + +int (*mips_timer_state)(void); +void (*mips_timer_ack)(void); +unsigned int (*mips_hpt_read)(void); +void (*mips_hpt_init)(unsigned int); + + +/* + * This version of gettimeofday has microsecond resolution and better than + * microsecond precision on fast machines with cycle counter. + */ +void do_gettimeofday(struct timeval *tv) +{ + unsigned long seq; + unsigned long lost; + unsigned long usec, sec; + unsigned long max_ntp_tick = tick_usec - tickadj; + + do { + seq = read_seqbegin(&xtime_lock); + + usec = do_gettimeoffset(); + + lost = jiffies - wall_jiffies; + + /* + * If time_adjust is negative then NTP is slowing the clock + * so make sure not to go into next possible interval. + * Better to lose some accuracy than have time go backwards.. + */ + if (unlikely(time_adjust < 0)) { + usec = min(usec, max_ntp_tick); + + if (lost) + usec += lost * max_ntp_tick; + } else if (unlikely(lost)) + usec += lost * tick_usec; + + sec = xtime.tv_sec; + usec += (xtime.tv_nsec / 1000); + + } while (read_seqretry(&xtime_lock, seq)); + + while (usec >= 1000000) { + usec -= 1000000; + sec++; + } + + tv->tv_sec = sec; + tv->tv_usec = usec; +} + +EXPORT_SYMBOL(do_gettimeofday); + +int do_settimeofday(struct timespec *tv) +{ + time_t wtm_sec, sec = tv->tv_sec; + long wtm_nsec, nsec = tv->tv_nsec; + + if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) + return -EINVAL; + + write_seqlock_irq(&xtime_lock); + + /* + * This is revolting. We need to set "xtime" correctly. However, + * the value in this location is the value at the most recent update + * of wall time. Discover what correction gettimeofday() would have + * made, and then undo it! + */ + nsec -= do_gettimeoffset() * NSEC_PER_USEC; + nsec -= (jiffies - wall_jiffies) * tick_nsec; + + wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); + wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); + + set_normalized_timespec(&xtime, sec, nsec); + set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); + + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + + write_sequnlock_irq(&xtime_lock); + clock_was_set(); + return 0; +} + +EXPORT_SYMBOL(do_settimeofday); + +/* + * Gettimeoffset routines. These routines returns the time duration + * since last timer interrupt in usecs. + * + * If the exact CPU counter frequency is known, use fixed_rate_gettimeoffset. + * Otherwise use calibrate_gettimeoffset() + * + * If the CPU does not have the counter register, you can either supply + * your own gettimeoffset() routine, or use null_gettimeoffset(), which + * gives the same resolution as HZ. + */ + +static unsigned long null_gettimeoffset(void) +{ + return 0; +} + + +/* The function pointer to one of the gettimeoffset funcs. */ +unsigned long (*do_gettimeoffset)(void) = null_gettimeoffset; + + +static unsigned long fixed_rate_gettimeoffset(void) +{ + u32 count; + unsigned long res; + + /* Get last timer tick in absolute kernel time */ + count = mips_hpt_read(); + + /* .. relative to previous jiffy (32 bits is enough) */ + count -= timerlo; + + __asm__("multu %1,%2" + : "=h" (res) + : "r" (count), "r" (sll32_usecs_per_cycle) + : "lo", GCC_REG_ACCUM); + + /* + * Due to possible jiffies inconsistencies, we need to check + * the result so that we'll get a timer that is monotonic. + */ + if (res >= USECS_PER_JIFFY) + res = USECS_PER_JIFFY - 1; + + return res; +} + + +/* + * Cached "1/(clocks per usec) * 2^32" value. + * It has to be recalculated once each jiffy. + */ +static unsigned long cached_quotient; + +/* Last jiffy when calibrate_divXX_gettimeoffset() was called. */ +static unsigned long last_jiffies; + +/* + * This is moved from dec/time.c:do_ioasic_gettimeoffset() by Maciej. + */ +static unsigned long calibrate_div32_gettimeoffset(void) +{ + u32 count; + unsigned long res, tmp; + unsigned long quotient; + + tmp = jiffies; + + quotient = cached_quotient; + + if (last_jiffies != tmp) { + last_jiffies = tmp; + if (last_jiffies != 0) { + unsigned long r0; + do_div64_32(r0, timerhi, timerlo, tmp); + do_div64_32(quotient, USECS_PER_JIFFY, + USECS_PER_JIFFY_FRAC, r0); + cached_quotient = quotient; + } + } + + /* Get last timer tick in absolute kernel time */ + count = mips_hpt_read(); + + /* .. relative to previous jiffy (32 bits is enough) */ + count -= timerlo; + + __asm__("multu %1,%2" + : "=h" (res) + : "r" (count), "r" (quotient) + : "lo", GCC_REG_ACCUM); + + /* + * Due to possible jiffies inconsistencies, we need to check + * the result so that we'll get a timer that is monotonic. + */ + if (res >= USECS_PER_JIFFY) + res = USECS_PER_JIFFY - 1; + + return res; +} + +static unsigned long calibrate_div64_gettimeoffset(void) +{ + u32 count; + unsigned long res, tmp; + unsigned long quotient; + + tmp = jiffies; + + quotient = cached_quotient; + + if (last_jiffies != tmp) { + last_jiffies = tmp; + if (last_jiffies) { + unsigned long r0; + __asm__(".set push\n\t" + ".set mips3\n\t" + "lwu %0,%3\n\t" + "dsll32 %1,%2,0\n\t" + "or %1,%1,%0\n\t" + "ddivu $0,%1,%4\n\t" + "mflo %1\n\t" + "dsll32 %0,%5,0\n\t" + "or %0,%0,%6\n\t" + "ddivu $0,%0,%1\n\t" + "mflo %0\n\t" + ".set pop" + : "=&r" (quotient), "=&r" (r0) + : "r" (timerhi), "m" (timerlo), + "r" (tmp), "r" (USECS_PER_JIFFY), + "r" (USECS_PER_JIFFY_FRAC) + : "hi", "lo", GCC_REG_ACCUM); + cached_quotient = quotient; + } + } + + /* Get last timer tick in absolute kernel time */ + count = mips_hpt_read(); + + /* .. relative to previous jiffy (32 bits is enough) */ + count -= timerlo; + + __asm__("multu %1,%2" + : "=h" (res) + : "r" (count), "r" (quotient) + : "lo", GCC_REG_ACCUM); + + /* + * Due to possible jiffies inconsistencies, we need to check + * the result so that we'll get a timer that is monotonic. + */ + if (res >= USECS_PER_JIFFY) + res = USECS_PER_JIFFY - 1; + + return res; +} + + +/* last time when xtime and rtc are sync'ed up */ +static long last_rtc_update; + +/* + * local_timer_interrupt() does profiling and process accounting + * on a per-CPU basis. + * + * In UP mode, it is invoked from the (global) timer_interrupt. + * + * In SMP mode, it might invoked by per-CPU timer interrupt, or + * a broadcasted inter-processor interrupt which itself is triggered + * by the global timer interrupt. + */ +void local_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + if (current->pid) + profile_tick(CPU_PROFILING, regs); + update_process_times(user_mode(regs)); +} + +/* + * High-level timer interrupt service routines. This function + * is set as irqaction->handler and is invoked through do_IRQ. + */ +irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned long j; + unsigned int count; + + count = mips_hpt_read(); + mips_timer_ack(); + + /* Update timerhi/timerlo for intra-jiffy calibration. */ + timerhi += count < timerlo; /* Wrap around */ + timerlo = count; + + /* + * call the generic timer interrupt handling + */ + do_timer(regs); + + /* + * If we have an externally synchronized Linux clock, then update + * CMOS clock accordingly every ~11 minutes. rtc_set_time() has to be + * called as close as possible to 500 ms before the new second starts. + */ + write_seqlock(&xtime_lock); + if ((time_status & STA_UNSYNC) == 0 && + xtime.tv_sec > last_rtc_update + 660 && + (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 && + (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) { + if (rtc_set_mmss(xtime.tv_sec) == 0) { + last_rtc_update = xtime.tv_sec; + } else { + /* do it again in 60 s */ + last_rtc_update = xtime.tv_sec - 600; + } + } + write_sequnlock(&xtime_lock); + + /* + * If jiffies has overflown in this timer_interrupt, we must + * update the timer[hi]/[lo] to make fast gettimeoffset funcs + * quotient calc still valid. -arca + * + * The first timer interrupt comes late as interrupts are + * enabled long after timers are initialized. Therefore the + * high precision timer is fast, leading to wrong gettimeoffset() + * calculations. We deal with it by setting it based on the + * number of its ticks between the second and the third interrupt. + * That is still somewhat imprecise, but it's a good estimate. + * --macro + */ + j = jiffies; + if (j < 4) { + static unsigned int prev_count; + static int hpt_initialized; + + switch (j) { + case 0: + timerhi = timerlo = 0; + mips_hpt_init(count); + break; + case 2: + prev_count = count; + break; + case 3: + if (!hpt_initialized) { + unsigned int c3 = 3 * (count - prev_count); + + timerhi = 0; + timerlo = c3; + mips_hpt_init(count - c3); + hpt_initialized = 1; + } + break; + default: + break; + } + } + + /* + * In UP mode, we call local_timer_interrupt() to do profiling + * and process accouting. + * + * In SMP mode, local_timer_interrupt() is invoked by appropriate + * low-level local timer interrupt handler. + */ + local_timer_interrupt(irq, dev_id, regs); + + return IRQ_HANDLED; +} + +asmlinkage void ll_timer_interrupt(int irq, struct pt_regs *regs) +{ + irq_enter(); + kstat_this_cpu.irqs[irq]++; + + /* we keep interrupt disabled all the time */ + timer_interrupt(irq, NULL, regs); + + irq_exit(); +} + +asmlinkage void ll_local_timer_interrupt(int irq, struct pt_regs *regs) +{ + irq_enter(); + if (smp_processor_id() != 0) + kstat_this_cpu.irqs[irq]++; + + /* we keep interrupt disabled all the time */ + local_timer_interrupt(irq, NULL, regs); + + irq_exit(); +} + +/* + * time_init() - it does the following things. + * + * 1) board_time_init() - + * a) (optional) set up RTC routines, + * b) (optional) calibrate and set the mips_hpt_frequency + * (only needed if you intended to use fixed_rate_gettimeoffset + * or use cpu counter as timer interrupt source) + * 2) setup xtime based on rtc_get_time(). + * 3) choose a appropriate gettimeoffset routine. + * 4) calculate a couple of cached variables for later usage + * 5) board_timer_setup() - + * a) (optional) over-write any choices made above by time_init(). + * b) machine specific code should setup the timer irqaction. + * c) enable the timer interrupt + */ + +void (*board_time_init)(void); +void (*board_timer_setup)(struct irqaction *irq); + +unsigned int mips_hpt_frequency; + +static struct irqaction timer_irqaction = { + .handler = timer_interrupt, + .flags = SA_INTERRUPT, + .name = "timer", +}; + +static unsigned int __init calibrate_hpt(void) +{ + u64 frequency; + u32 hpt_start, hpt_end, hpt_count, hz; + + const int loops = HZ / 10; + int log_2_loops = 0; + int i; + + /* + * We want to calibrate for 0.1s, but to avoid a 64-bit + * division we round the number of loops up to the nearest + * power of 2. + */ + while (loops > 1 << log_2_loops) + log_2_loops++; + i = 1 << log_2_loops; + + /* + * Wait for a rising edge of the timer interrupt. + */ + while (mips_timer_state()); + while (!mips_timer_state()); + + /* + * Now see how many high precision timer ticks happen + * during the calculated number of periods between timer + * interrupts. + */ + hpt_start = mips_hpt_read(); + do { + while (mips_timer_state()); + while (!mips_timer_state()); + } while (--i); + hpt_end = mips_hpt_read(); + + hpt_count = hpt_end - hpt_start; + hz = HZ; + frequency = (u64)hpt_count * (u64)hz; + + return frequency >> log_2_loops; +} + +void __init time_init(void) +{ + if (board_time_init) + board_time_init(); + + if (!rtc_set_mmss) + rtc_set_mmss = rtc_set_time; + + xtime.tv_sec = rtc_get_time(); + xtime.tv_nsec = 0; + + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); + + /* Choose appropriate high precision timer routines. */ + if (!cpu_has_counter && !mips_hpt_read) { + /* No high precision timer -- sorry. */ + mips_hpt_read = null_hpt_read; + mips_hpt_init = null_hpt_init; + } else if (!mips_hpt_frequency && !mips_timer_state) { + /* A high precision timer of unknown frequency. */ + if (!mips_hpt_read) { + /* No external high precision timer -- use R4k. */ + mips_hpt_read = c0_hpt_read; + mips_hpt_init = c0_hpt_init; + } + + if ((current_cpu_data.isa_level == MIPS_CPU_ISA_M32) || + (current_cpu_data.isa_level == MIPS_CPU_ISA_I) || + (current_cpu_data.isa_level == MIPS_CPU_ISA_II)) + /* + * We need to calibrate the counter but we don't have + * 64-bit division. + */ + do_gettimeoffset = calibrate_div32_gettimeoffset; + else + /* + * We need to calibrate the counter but we *do* have + * 64-bit division. + */ + do_gettimeoffset = calibrate_div64_gettimeoffset; + } else { + /* We know counter frequency. Or we can get it. */ + if (!mips_hpt_read) { + /* No external high precision timer -- use R4k. */ + mips_hpt_read = c0_hpt_read; + + if (mips_timer_state) + mips_hpt_init = c0_hpt_init; + else { + /* No external timer interrupt -- use R4k. */ + mips_hpt_init = c0_hpt_timer_init; + mips_timer_ack = c0_timer_ack; + } + } + if (!mips_hpt_frequency) + mips_hpt_frequency = calibrate_hpt(); + + do_gettimeoffset = fixed_rate_gettimeoffset; + + /* Calculate cache parameters. */ + cycles_per_jiffy = (mips_hpt_frequency + HZ / 2) / HZ; + + /* sll32_usecs_per_cycle = 10^6 * 2^32 / mips_counter_freq */ + do_div64_32(sll32_usecs_per_cycle, + 1000000, mips_hpt_frequency / 2, + mips_hpt_frequency); + + /* Report the high precision timer rate for a reference. */ + printk("Using %u.%03u MHz high precision timer.\n", + ((mips_hpt_frequency + 500) / 1000) / 1000, + ((mips_hpt_frequency + 500) / 1000) % 1000); + } + + if (!mips_timer_ack) + /* No timer interrupt ack (e.g. i8254). */ + mips_timer_ack = null_timer_ack; + + /* This sets up the high precision timer for the first interrupt. */ + mips_hpt_init(mips_hpt_read()); + + /* + * Call board specific timer interrupt setup. + * + * this pointer must be setup in machine setup routine. + * + * Even if a machine chooses to use a low-level timer interrupt, + * it still needs to setup the timer_irqaction. + * In that case, it might be better to set timer_irqaction.handler + * to be NULL function so that we are sure the high-level code + * is not invoked accidentally. + */ + board_timer_setup(&timer_irqaction); +} + +#define FEBRUARY 2 +#define STARTOFTIME 1970 +#define SECDAY 86400L +#define SECYR (SECDAY * 365) +#define leapyear(y) ((!((y) % 4) && ((y) % 100)) || !((y) % 400)) +#define days_in_year(y) (leapyear(y) ? 366 : 365) +#define days_in_month(m) (month_days[(m) - 1]) + +static int month_days[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +void to_tm(unsigned long tim, struct rtc_time *tm) +{ + long hms, day, gday; + int i; + + gday = day = tim / SECDAY; + hms = tim % SECDAY; + + /* Hours, minutes, seconds are easy */ + tm->tm_hour = hms / 3600; + tm->tm_min = (hms % 3600) / 60; + tm->tm_sec = (hms % 3600) % 60; + + /* Number of years in days */ + for (i = STARTOFTIME; day >= days_in_year(i); i++) + day -= days_in_year(i); + tm->tm_year = i; + + /* Number of months in days left */ + if (leapyear(tm->tm_year)) + days_in_month(FEBRUARY) = 29; + for (i = 1; day >= days_in_month(i); i++) + day -= days_in_month(i); + days_in_month(FEBRUARY) = 28; + tm->tm_mon = i - 1; /* tm_mon starts from 0 to 11 */ + + /* Days are what is left over (+1) from all that. */ + tm->tm_mday = day + 1; + + /* + * Determine the day of week + */ + tm->tm_wday = (gday + 4) % 7; /* 1970/1/1 was Thursday */ +} + +EXPORT_SYMBOL(rtc_lock); +EXPORT_SYMBOL(to_tm); +EXPORT_SYMBOL(rtc_set_time); +EXPORT_SYMBOL(rtc_get_time); + +unsigned long long sched_clock(void) +{ + return (unsigned long long)jiffies*(1000000000/HZ); +} diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c new file mode 100644 index 000000000000..56c36e42e0a6 --- /dev/null +++ b/arch/mips/kernel/traps.c @@ -0,0 +1,1062 @@ +/* + * 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 - 1999, 2000, 01 Ralf Baechle + * Copyright (C) 1995, 1996 Paul M. Antoine + * Copyright (C) 1998 Ulf Carlsson + * Copyright (C) 1999 Silicon Graphics, Inc. + * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com + * Copyright (C) 2000, 01 MIPS Technologies, Inc. + * Copyright (C) 2002, 2003, 2004 Maciej W. Rozycki + */ +#include <linux/config.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/spinlock.h> +#include <linux/kallsyms.h> + +#include <asm/bootinfo.h> +#include <asm/branch.h> +#include <asm/break.h> +#include <asm/cpu.h> +#include <asm/fpu.h> +#include <asm/module.h> +#include <asm/pgtable.h> +#include <asm/ptrace.h> +#include <asm/sections.h> +#include <asm/system.h> +#include <asm/tlbdebug.h> +#include <asm/traps.h> +#include <asm/uaccess.h> +#include <asm/mmu_context.h> +#include <asm/watch.h> +#include <asm/types.h> + +extern asmlinkage void handle_tlbm(void); +extern asmlinkage void handle_tlbl(void); +extern asmlinkage void handle_tlbs(void); +extern asmlinkage void handle_adel(void); +extern asmlinkage void handle_ades(void); +extern asmlinkage void handle_ibe(void); +extern asmlinkage void handle_dbe(void); +extern asmlinkage void handle_sys(void); +extern asmlinkage void handle_bp(void); +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_fpe(void); +extern asmlinkage void handle_mdmx(void); +extern asmlinkage void handle_watch(void); +extern asmlinkage void handle_mcheck(void); +extern asmlinkage void handle_reserved(void); + +extern int fpu_emulator_cop1Handler(int xcptno, struct pt_regs *xcp, + struct mips_fpu_soft_struct *ctx); + +void (*board_be_init)(void); +int (*board_be_handler)(struct pt_regs *regs, int is_fixup); + +/* + * These constant is for searching for possible module text segments. + * MODULE_RANGE is a guess of how much space is likely to be vmalloced. + */ +#define MODULE_RANGE (8*1024*1024) + +/* + * This routine abuses get_user()/put_user() to reference pointers + * with at least a bit of error checking ... + */ +void show_stack(struct task_struct *task, unsigned long *sp) +{ + const int field = 2 * sizeof(unsigned long); + long stackdata; + int i; + + if (!sp) { + if (task && task != current) + sp = (unsigned long *) task->thread.reg29; + else + sp = (unsigned long *) &sp; + } + + printk("Stack :"); + i = 0; + while ((unsigned long) sp & (PAGE_SIZE - 1)) { + if (i && ((i % (64 / field)) == 0)) + printk("\n "); + if (i > 39) { + printk(" ..."); + break; + } + + if (__get_user(stackdata, sp++)) { + printk(" (Bad stack address)"); + break; + } + + printk(" %0*lx", field, stackdata); + i++; + } + printk("\n"); +} + +void show_trace(struct task_struct *task, unsigned long *stack) +{ + const int field = 2 * sizeof(unsigned long); + unsigned long addr; + + if (!stack) { + if (task && task != current) + stack = (unsigned long *) task->thread.reg29; + else + stack = (unsigned long *) &stack; + } + + printk("Call Trace:"); +#ifdef CONFIG_KALLSYMS + printk("\n"); +#endif + while (!kstack_end(stack)) { + addr = *stack++; + if (__kernel_text_address(addr)) { + printk(" [<%0*lx>] ", field, addr); + print_symbol("%s\n", addr); + } + } + printk("\n"); +} + +/* + * The architecture-independent dump_stack generator + */ +void dump_stack(void) +{ + unsigned long stack; + + show_trace(current, &stack); +} + +EXPORT_SYMBOL(dump_stack); + +void show_code(unsigned int *pc) +{ + long i; + + printk("\nCode:"); + + for(i = -3 ; i < 6 ; i++) { + unsigned int insn; + if (__get_user(insn, pc + i)) { + printk(" (Bad address in epc)\n"); + break; + } + printk("%c%08x%c", (i?' ':'<'), insn, (i?' ':'>')); + } +} + +void show_regs(struct pt_regs *regs) +{ + const int field = 2 * sizeof(unsigned long); + unsigned int cause = regs->cp0_cause; + int i; + + printk("Cpu %d\n", smp_processor_id()); + + /* + * Saved main processor registers + */ + for (i = 0; i < 32; ) { + if ((i % 4) == 0) + printk("$%2d :", i); + if (i == 0) + printk(" %0*lx", field, 0UL); + else if (i == 26 || i == 27) + printk(" %*s", field, ""); + else + printk(" %0*lx", field, regs->regs[i]); + + i++; + if ((i % 4) == 0) + printk("\n"); + } + + printk("Hi : %0*lx\n", field, regs->hi); + printk("Lo : %0*lx\n", field, regs->lo); + + /* + * Saved cp0 registers + */ + printk("epc : %0*lx ", field, regs->cp0_epc); + print_symbol("%s ", regs->cp0_epc); + printk(" %s\n", print_tainted()); + printk("ra : %0*lx ", field, regs->regs[31]); + print_symbol("%s\n", regs->regs[31]); + + printk("Status: %08x ", (uint32_t) regs->cp0_status); + + if (regs->cp0_status & ST0_KX) + printk("KX "); + if (regs->cp0_status & ST0_SX) + printk("SX "); + if (regs->cp0_status & ST0_UX) + printk("UX "); + switch (regs->cp0_status & ST0_KSU) { + case KSU_USER: + printk("USER "); + break; + case KSU_SUPERVISOR: + printk("SUPERVISOR "); + break; + case KSU_KERNEL: + printk("KERNEL "); + break; + default: + printk("BAD_MODE "); + break; + } + if (regs->cp0_status & ST0_ERL) + printk("ERL "); + if (regs->cp0_status & ST0_EXL) + printk("EXL "); + if (regs->cp0_status & ST0_IE) + printk("IE "); + printk("\n"); + + printk("Cause : %08x\n", cause); + + cause = (cause & CAUSEF_EXCCODE) >> CAUSEB_EXCCODE; + if (1 <= cause && cause <= 5) + printk("BadVA : %0*lx\n", field, regs->cp0_badvaddr); + + printk("PrId : %08x\n", read_c0_prid()); +} + +void show_registers(struct pt_regs *regs) +{ + show_regs(regs); + print_modules(); + printk("Process %s (pid: %d, threadinfo=%p, task=%p)\n", + current->comm, current->pid, current_thread_info(), current); + show_stack(current, (long *) regs->regs[29]); + show_trace(current, (long *) regs->regs[29]); + show_code((unsigned int *) regs->cp0_epc); + printk("\n"); +} + +static DEFINE_SPINLOCK(die_lock); + +NORET_TYPE void __die(const char * str, struct pt_regs * regs, + const char * file, const char * func, unsigned long line) +{ + static int die_counter; + + console_verbose(); + spin_lock_irq(&die_lock); + printk("%s", str); + if (file && func) + printk(" in %s:%s, line %ld", file, func, line); + printk("[#%d]:\n", ++die_counter); + show_registers(regs); + spin_unlock_irq(&die_lock); + do_exit(SIGSEGV); +} + +void __die_if_kernel(const char * str, struct pt_regs * regs, + const char * file, const char * func, unsigned long line) +{ + if (!user_mode(regs)) + __die(str, regs, file, func, line); +} + +extern const struct exception_table_entry __start___dbe_table[]; +extern const struct exception_table_entry __stop___dbe_table[]; + +void __declare_dbe_table(void) +{ + __asm__ __volatile__( + ".section\t__dbe_table,\"a\"\n\t" + ".previous" + ); +} + +/* Given an address, look for it in the exception tables. */ +static const struct exception_table_entry *search_dbe_tables(unsigned long addr) +{ + const struct exception_table_entry *e; + + e = search_extable(__start___dbe_table, __stop___dbe_table - 1, addr); + if (!e) + e = search_module_dbetables(addr); + return e; +} + +asmlinkage void do_be(struct pt_regs *regs) +{ + const int field = 2 * sizeof(unsigned long); + const struct exception_table_entry *fixup = NULL; + int data = regs->cp0_cause & 4; + int action = MIPS_BE_FATAL; + + /* XXX For now. Fixme, this searches the wrong table ... */ + if (data && !user_mode(regs)) + fixup = search_dbe_tables(exception_epc(regs)); + + if (fixup) + action = MIPS_BE_FIXUP; + + if (board_be_handler) + action = board_be_handler(regs, fixup != 0); + + switch (action) { + case MIPS_BE_DISCARD: + return; + case MIPS_BE_FIXUP: + if (fixup) { + regs->cp0_epc = fixup->nextinsn; + return; + } + break; + default: + break; + } + + /* + * Assume it would be too dangerous to continue ... + */ + printk(KERN_ALERT "%s bus error, epc == %0*lx, ra == %0*lx\n", + data ? "Data" : "Instruction", + field, regs->cp0_epc, field, regs->regs[31]); + die_if_kernel("Oops", regs); + force_sig(SIGBUS, current); +} + +static inline int get_insn_opcode(struct pt_regs *regs, unsigned int *opcode) +{ + unsigned int *epc; + + epc = (unsigned int *) regs->cp0_epc + + ((regs->cp0_cause & CAUSEF_BD) != 0); + if (!get_user(*opcode, epc)) + return 0; + + force_sig(SIGSEGV, current); + return 1; +} + +/* + * ll/sc emulation + */ + +#define OPCODE 0xfc000000 +#define BASE 0x03e00000 +#define RT 0x001f0000 +#define OFFSET 0x0000ffff +#define LL 0xc0000000 +#define SC 0xe0000000 + +/* + * The ll_bit is cleared by r*_switch.S + */ + +unsigned long ll_bit; + +static struct task_struct *ll_task = NULL; + +static inline void simulate_ll(struct pt_regs *regs, unsigned int opcode) +{ + unsigned long value, *vaddr; + long offset; + int signal = 0; + + /* + * analyse the ll instruction that just caused a ri exception + * and put the referenced address to addr. + */ + + /* sign extend offset */ + offset = opcode & OFFSET; + offset <<= 16; + offset >>= 16; + + vaddr = (unsigned long *)((long)(regs->regs[(opcode & BASE) >> 21]) + offset); + + if ((unsigned long)vaddr & 3) { + signal = SIGBUS; + goto sig; + } + if (get_user(value, vaddr)) { + signal = SIGSEGV; + goto sig; + } + + preempt_disable(); + + if (ll_task == NULL || ll_task == current) { + ll_bit = 1; + } else { + ll_bit = 0; + } + ll_task = current; + + preempt_enable(); + + regs->regs[(opcode & RT) >> 16] = value; + + compute_return_epc(regs); + return; + +sig: + force_sig(signal, current); +} + +static inline void simulate_sc(struct pt_regs *regs, unsigned int opcode) +{ + unsigned long *vaddr, reg; + long offset; + int signal = 0; + + /* + * analyse the sc instruction that just caused a ri exception + * and put the referenced address to addr. + */ + + /* sign extend offset */ + offset = opcode & OFFSET; + offset <<= 16; + offset >>= 16; + + vaddr = (unsigned long *)((long)(regs->regs[(opcode & BASE) >> 21]) + offset); + reg = (opcode & RT) >> 16; + + if ((unsigned long)vaddr & 3) { + signal = SIGBUS; + goto sig; + } + + preempt_disable(); + + if (ll_bit == 0 || ll_task != current) { + regs->regs[reg] = 0; + preempt_enable(); + compute_return_epc(regs); + return; + } + + preempt_enable(); + + if (put_user(regs->regs[reg], vaddr)) { + signal = SIGSEGV; + goto sig; + } + + regs->regs[reg] = 1; + + compute_return_epc(regs); + return; + +sig: + force_sig(signal, current); +} + +/* + * ll uses the opcode of lwc0 and sc uses the opcode of swc0. That is both + * opcodes are supposed to result in coprocessor unusable exceptions if + * executed on ll/sc-less processors. That's the theory. In practice a + * few processors such as NEC's VR4100 throw reserved instruction exceptions + * instead, so we're doing the emulation thing in both exception handlers. + */ +static inline int simulate_llsc(struct pt_regs *regs) +{ + unsigned int opcode; + + if (unlikely(get_insn_opcode(regs, &opcode))) + return -EFAULT; + + if ((opcode & OPCODE) == LL) { + simulate_ll(regs, opcode); + return 0; + } + if ((opcode & OPCODE) == SC) { + simulate_sc(regs, opcode); + return 0; + } + + return -EFAULT; /* Strange things going on ... */ +} + +asmlinkage void do_ov(struct pt_regs *regs) +{ + siginfo_t info; + + info.si_code = FPE_INTOVF; + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_addr = (void *)regs->cp0_epc; + force_sig_info(SIGFPE, &info, current); +} + +/* + * XXX Delayed fp exceptions when doing a lazy ctx switch XXX + */ +asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31) +{ + if (fcr31 & FPU_CSR_UNI_X) { + int sig; + + preempt_disable(); + + /* + * Unimplemented operation exception. If we've got the full + * software emulator on-board, let's use it... + * + * Force FPU to dump state into task/thread context. We're + * moving a lot of data here for what is probably a single + * instruction, but the alternative is to pre-decode the FP + * register operands before invoking the emulator, which seems + * a bit extreme for what should be an infrequent event. + */ + save_fp(current); + + /* Run the emulator */ + sig = fpu_emulator_cop1Handler (0, regs, + ¤t->thread.fpu.soft); + + /* + * We can't allow the emulated instruction to leave any of + * the cause bit set in $fcr31. + */ + current->thread.fpu.soft.fcr31 &= ~FPU_CSR_ALL_X; + + /* Restore the hardware register state */ + restore_fp(current); + + preempt_enable(); + + /* If something went wrong, signal */ + if (sig) + force_sig(sig, current); + + return; + } + + force_sig(SIGFPE, current); +} + +asmlinkage void do_bp(struct pt_regs *regs) +{ + unsigned int opcode, bcode; + siginfo_t info; + + die_if_kernel("Break instruction in kernel code", regs); + + if (get_insn_opcode(regs, &opcode)) + return; + + /* + * 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, but not always, grrr... + * We handle both cases with a simple heuristics. --macro + */ + bcode = ((opcode >> 6) & ((1 << 20) - 1)); + if (bcode < (1 << 10)) + bcode <<= 10; + + /* + * (A short test says that IRIX 5.3 sends SIGTRAP for all break + * insns, even for break codes that indicate arithmetic failures. + * Weird ...) + * But should we continue the brokenness??? --macro + */ + switch (bcode) { + case BRK_OVERFLOW << 10: + case BRK_DIVZERO << 10: + if (bcode == (BRK_DIVZERO << 10)) + info.si_code = FPE_INTDIV; + else + info.si_code = FPE_INTOVF; + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_addr = (void *)regs->cp0_epc; + force_sig_info(SIGFPE, &info, current); + break; + default: + force_sig(SIGTRAP, current); + } +} + +asmlinkage void do_tr(struct pt_regs *regs) +{ + unsigned int opcode, tcode = 0; + siginfo_t info; + + die_if_kernel("Trap instruction in kernel code", regs); + + if (get_insn_opcode(regs, &opcode)) + return; + + /* Immediate versions don't provide a code. */ + if (!(opcode & OPCODE)) + tcode = ((opcode >> 6) & ((1 << 10) - 1)); + + /* + * (A short test says that IRIX 5.3 sends SIGTRAP for all trap + * insns, even for trap codes that indicate arithmetic failures. + * Weird ...) + * But should we continue the brokenness??? --macro + */ + switch (tcode) { + case BRK_OVERFLOW: + case BRK_DIVZERO: + if (tcode == BRK_DIVZERO) + info.si_code = FPE_INTDIV; + else + info.si_code = FPE_INTOVF; + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_addr = (void *)regs->cp0_epc; + force_sig_info(SIGFPE, &info, current); + break; + default: + force_sig(SIGTRAP, current); + } +} + +asmlinkage void do_ri(struct pt_regs *regs) +{ + die_if_kernel("Reserved instruction in kernel code", regs); + + if (!cpu_has_llsc) + if (!simulate_llsc(regs)) + return; + + force_sig(SIGILL, current); +} + +asmlinkage void do_cpu(struct pt_regs *regs) +{ + unsigned int cpid; + + die_if_kernel("do_cpu invoked from kernel context!", regs); + + cpid = (regs->cp0_cause >> CAUSEB_CE) & 3; + + switch (cpid) { + case 0: + if (cpu_has_llsc) + break; + + if (!simulate_llsc(regs)) + return; + break; + + case 1: + preempt_disable(); + + own_fpu(); + if (used_math()) { /* Using the FPU again. */ + restore_fp(current); + } else { /* First time FPU user. */ + init_fpu(); + set_used_math(); + } + + if (!cpu_has_fpu) { + int sig = fpu_emulator_cop1Handler(0, regs, + ¤t->thread.fpu.soft); + if (sig) + force_sig(sig, current); + } + + preempt_enable(); + + return; + + case 2: + case 3: + break; + } + + force_sig(SIGILL, current); +} + +asmlinkage void do_mdmx(struct pt_regs *regs) +{ + force_sig(SIGILL, current); +} + +asmlinkage void do_watch(struct pt_regs *regs) +{ + /* + * We use the watch exception where available to detect stack + * overflows. + */ + dump_tlb_all(); + show_regs(regs); + panic("Caught WATCH exception - probably caused by stack overflow."); +} + +asmlinkage void do_mcheck(struct pt_regs *regs) +{ + show_regs(regs); + dump_tlb_all(); + /* + * Some chips may have other causes of machine check (e.g. SB1 + * graduation timer) + */ + panic("Caught Machine Check exception - %scaused by multiple " + "matching entries in the TLB.", + (regs->cp0_status & ST0_TS) ? "" : "not "); +} + +asmlinkage void do_reserved(struct pt_regs *regs) +{ + /* + * Game over - no way to handle this if it ever occurs. Most probably + * caused by a new unknown cpu type or after another deadly + * hard/software error. + */ + show_regs(regs); + panic("Caught reserved exception %ld - should not happen.", + (regs->cp0_cause & 0x7f) >> 2); +} + +/* + * Some MIPS CPUs can enable/disable for cache parity detection, but do + * it different ways. + */ +static inline void parity_protection_init(void) +{ + switch (current_cpu_data.cputype) { + case CPU_24K: + /* 24K cache parity not currently implemented in FPGA */ + printk(KERN_INFO "Disable cache parity protection for " + "MIPS 24K CPU.\n"); + write_c0_ecc(read_c0_ecc() & ~0x80000000); + break; + case CPU_5KC: + /* Set the PE bit (bit 31) in the c0_ecc register. */ + printk(KERN_INFO "Enable cache parity protection for " + "MIPS 5KC/24K CPUs.\n"); + write_c0_ecc(read_c0_ecc() | 0x80000000); + break; + case CPU_20KC: + case CPU_25KF: + /* Clear the DE bit (bit 16) in the c0_status register. */ + printk(KERN_INFO "Enable cache parity protection for " + "MIPS 20KC/25KF CPUs.\n"); + clear_c0_status(ST0_DE); + break; + default: + break; + } +} + +asmlinkage void cache_parity_error(void) +{ + const int field = 2 * sizeof(unsigned long); + unsigned int reg_val; + + /* For the moment, report the problem and hang. */ + printk("Cache error exception:\n"); + printk("cp0_errorepc == %0*lx\n", field, read_c0_errorepc()); + reg_val = read_c0_cacheerr(); + printk("c0_cacheerr == %08x\n", reg_val); + + printk("Decoded c0_cacheerr: %s cache fault in %s reference.\n", + reg_val & (1<<30) ? "secondary" : "primary", + reg_val & (1<<31) ? "data" : "insn"); + printk("Error bits: %s%s%s%s%s%s%s\n", + reg_val & (1<<29) ? "ED " : "", + reg_val & (1<<28) ? "ET " : "", + reg_val & (1<<26) ? "EE " : "", + reg_val & (1<<25) ? "EB " : "", + reg_val & (1<<24) ? "EI " : "", + reg_val & (1<<23) ? "E1 " : "", + reg_val & (1<<22) ? "E0 " : ""); + printk("IDX: 0x%08x\n", reg_val & ((1<<22)-1)); + +#if defined(CONFIG_CPU_MIPS32) || defined (CONFIG_CPU_MIPS64) + if (reg_val & (1<<22)) + printk("DErrAddr0: 0x%0*lx\n", field, read_c0_derraddr0()); + + if (reg_val & (1<<23)) + printk("DErrAddr1: 0x%0*lx\n", field, read_c0_derraddr1()); +#endif + + panic("Can't handle the cache error!"); +} + +/* + * SDBBP EJTAG debug exception handler. + * We skip the instruction and return to the next instruction. + */ +void ejtag_exception_handler(struct pt_regs *regs) +{ + const int field = 2 * sizeof(unsigned long); + unsigned long depc, old_epc; + unsigned int debug; + + printk("SDBBP EJTAG debug exception - not handled yet, just ignored!\n"); + depc = read_c0_depc(); + debug = read_c0_debug(); + printk("c0_depc = %0*lx, DEBUG = %08x\n", field, depc, debug); + if (debug & 0x80000000) { + /* + * In branch delay slot. + * We cheat a little bit here and use EPC to calculate the + * debug return address (DEPC). EPC is restored after the + * calculation. + */ + old_epc = regs->cp0_epc; + regs->cp0_epc = depc; + __compute_return_epc(regs); + depc = regs->cp0_epc; + regs->cp0_epc = old_epc; + } else + depc += 4; + write_c0_depc(depc); + +#if 0 + printk("\n\n----- Enable EJTAG single stepping ----\n\n"); + write_c0_debug(debug | 0x100); +#endif +} + +/* + * NMI exception handler. + */ +void nmi_exception_handler(struct pt_regs *regs) +{ + printk("NMI taken!!!!\n"); + die("NMI", regs); + while(1) ; +} + +unsigned long exception_handlers[32]; + +/* + * As a side effect of the way this is implemented we're limited + * to interrupt handlers in the address range from + * KSEG0 <= x < KSEG0 + 256mb on the Nevada. Oh well ... + */ +void *set_except_vector(int n, void *addr) +{ + unsigned long handler = (unsigned long) addr; + unsigned long old_handler = exception_handlers[n]; + + exception_handlers[n] = handler; + if (n == 0 && cpu_has_divec) { + *(volatile u32 *)(CAC_BASE + 0x200) = 0x08000000 | + (0x03ffffff & (handler >> 2)); + flush_icache_range(CAC_BASE + 0x200, CAC_BASE + 0x204); + } + return (void *)old_handler; +} + +/* + * This is used by native signal handling + */ +asmlinkage int (*save_fp_context)(struct sigcontext *sc); +asmlinkage int (*restore_fp_context)(struct sigcontext *sc); + +extern asmlinkage int _save_fp_context(struct sigcontext *sc); +extern asmlinkage int _restore_fp_context(struct sigcontext *sc); + +extern asmlinkage int fpu_emulator_save_context(struct sigcontext *sc); +extern asmlinkage int fpu_emulator_restore_context(struct sigcontext *sc); + +static inline void signal_init(void) +{ + if (cpu_has_fpu) { + save_fp_context = _save_fp_context; + restore_fp_context = _restore_fp_context; + } else { + save_fp_context = fpu_emulator_save_context; + restore_fp_context = fpu_emulator_restore_context; + } +} + +#ifdef CONFIG_MIPS32_COMPAT + +/* + * This is used by 32-bit signal stuff on the 64-bit kernel + */ +asmlinkage int (*save_fp_context32)(struct sigcontext32 *sc); +asmlinkage int (*restore_fp_context32)(struct sigcontext32 *sc); + +extern asmlinkage int _save_fp_context32(struct sigcontext32 *sc); +extern asmlinkage int _restore_fp_context32(struct sigcontext32 *sc); + +extern asmlinkage int fpu_emulator_save_context32(struct sigcontext32 *sc); +extern asmlinkage int fpu_emulator_restore_context32(struct sigcontext32 *sc); + +static inline void signal32_init(void) +{ + if (cpu_has_fpu) { + save_fp_context32 = _save_fp_context32; + restore_fp_context32 = _restore_fp_context32; + } else { + save_fp_context32 = fpu_emulator_save_context32; + restore_fp_context32 = fpu_emulator_restore_context32; + } +} +#endif + +extern void cpu_cache_init(void); +extern void tlb_init(void); + +void __init per_cpu_trap_init(void) +{ + unsigned int cpu = smp_processor_id(); + unsigned int status_set = ST0_CU0; + + /* + * Disable coprocessors and select 32-bit or 64-bit addressing + * and the 16/32 or 32/32 FPR register model. Reset the BEV + * flag that some firmware may have left set and the TS bit (for + * IP27). Set XX for ISA IV code to work. + */ +#ifdef CONFIG_MIPS64 + status_set |= ST0_FR|ST0_KX|ST0_SX|ST0_UX; +#endif + if (current_cpu_data.isa_level == MIPS_CPU_ISA_IV) + status_set |= ST0_XX; + change_c0_status(ST0_CU|ST0_FR|ST0_BEV|ST0_TS|ST0_KX|ST0_SX|ST0_UX, + status_set); + + /* + * Some MIPS CPUs have a dedicated interrupt vector which reduces the + * interrupt processing overhead. Use it where available. + */ + if (cpu_has_divec) + set_c0_cause(CAUSEF_IV); + + cpu_data[cpu].asid_cache = ASID_FIRST_VERSION; + TLBMISS_HANDLER_SETUP(); + + atomic_inc(&init_mm.mm_count); + current->active_mm = &init_mm; + BUG_ON(current->mm); + enter_lazy_tlb(&init_mm, current); + + cpu_cache_init(); + tlb_init(); +} + +void __init trap_init(void) +{ + extern char except_vec3_generic, except_vec3_r4000; + extern char except_vec_ejtag_debug; + extern char except_vec4; + unsigned long i; + + per_cpu_trap_init(); + + /* + * Copy the generic exception handlers to their final destination. + * This will be overriden later as suitable for a particular + * configuration. + */ + memcpy((void *)(CAC_BASE + 0x180), &except_vec3_generic, 0x80); + + /* + * Setup default vectors + */ + for (i = 0; i <= 31; i++) + set_except_vector(i, handle_reserved); + + /* + * Copy the EJTAG debug exception vector handler code to it's final + * destination. + */ + if (cpu_has_ejtag) + memcpy((void *)(CAC_BASE + 0x300), &except_vec_ejtag_debug, 0x80); + + /* + * Only some CPUs have the watch exceptions. + */ + if (cpu_has_watch) + set_except_vector(23, handle_watch); + + /* + * Some MIPS CPUs have a dedicated interrupt vector which reduces the + * interrupt processing overhead. Use it where available. + */ + if (cpu_has_divec) + memcpy((void *)(CAC_BASE + 0x200), &except_vec4, 0x8); + + /* + * Some CPUs can enable/disable for cache parity detection, but does + * it different ways. + */ + parity_protection_init(); + + /* + * The Data Bus Errors / Instruction Bus Errors are signaled + * by external hardware. Therefore these two exceptions + * may have board specific handlers. + */ + if (board_be_init) + board_be_init(); + + set_except_vector(1, handle_tlbm); + set_except_vector(2, handle_tlbl); + set_except_vector(3, handle_tlbs); + + set_except_vector(4, handle_adel); + set_except_vector(5, handle_ades); + + set_except_vector(6, handle_ibe); + set_except_vector(7, handle_dbe); + + 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(22, handle_mdmx); + + if (cpu_has_fpu && !cpu_has_nofpuex) + set_except_vector(15, handle_fpe); + + if (cpu_has_mcheck) + set_except_vector(24, handle_mcheck); + + if (cpu_has_vce) + /* Special exception: R4[04]00 uses also the divec space. */ + memcpy((void *)(CAC_BASE + 0x180), &except_vec3_r4000, 0x100); + else if (cpu_has_4kex) + memcpy((void *)(CAC_BASE + 0x180), &except_vec3_generic, 0x80); + else + memcpy((void *)(CAC_BASE + 0x080), &except_vec3_generic, 0x80); + + if (current_cpu_data.cputype == CPU_R6000 || + current_cpu_data.cputype == CPU_R6000A) { + /* + * 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. + * (Duh, crap, there is someone with a triple R6k machine) + */ + //set_except_vector(14, handle_mc); + //set_except_vector(15, handle_ndc); + } + + signal_init(); +#ifdef CONFIG_MIPS32_COMPAT + signal32_init(); +#endif + + flush_icache_range(CAC_BASE, CAC_BASE + 0x400); +} diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c new file mode 100644 index 000000000000..3f24a1d45865 --- /dev/null +++ b/arch/mips/kernel/unaligned.c @@ -0,0 +1,550 @@ +/* + * 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, 1998, 1999, 2002 by Ralf Baechle + * Copyright (C) 1999 Silicon Graphics, Inc. + * + * 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 decided 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 argument x is 0 for disabling software emulation, enabled otherwise. + * + * Below a little program to play around with this feature. + * + * #include <stdio.h> + * #include <sys/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"); + * } + * + * Coprocessor 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/config.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/signal.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> + +#include <asm/asm.h> +#include <asm/branch.h> +#include <asm/byteorder.h> +#include <asm/inst.h> +#include <asm/uaccess.h> +#include <asm/system.h> + +#define STR(x) __STR(x) +#define __STR(x) #x + +#ifdef CONFIG_PROC_FS +unsigned long unaligned_instructions; +#endif + +static inline int emulate_load_store_insn(struct pt_regs *regs, + void *addr, unsigned long pc, + unsigned long **regptr, unsigned long *newvalue) +{ + union mips_instruction insn; + unsigned long value; + unsigned int res; + + regs->regs[0] = 0; + *regptr=NULL; + + /* + * 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 interest. + */ + case lh_op: + if (!access_ok(VERIFY_READ, addr, 2)) + goto sigbus; + + __asm__ __volatile__ (".set\tnoat\n" +#ifdef __BIG_ENDIAN + "1:\tlb\t%0, 0(%2)\n" + "2:\tlbu\t$1, 1(%2)\n\t" +#endif +#ifdef __LITTLE_ENDIAN + "1:\tlb\t%0, 1(%2)\n" + "2:\tlbu\t$1, 0(%2)\n\t" +#endif + "sll\t%0, 0x8\n\t" + "or\t%0, $1\n\t" + "li\t%1, 0\n" + "3:\t.set\tat\n\t" + ".section\t.fixup,\"ax\"\n\t" + "4:\tli\t%1, %3\n\t" + "j\t3b\n\t" + ".previous\n\t" + ".section\t__ex_table,\"a\"\n\t" + STR(PTR)"\t1b, 4b\n\t" + STR(PTR)"\t2b, 4b\n\t" + ".previous" + : "=&r" (value), "=r" (res) + : "r" (addr), "i" (-EFAULT)); + if (res) + goto fault; + *newvalue = value; + *regptr = ®s->regs[insn.i_format.rt]; + break; + + case lw_op: + if (!access_ok(VERIFY_READ, addr, 4)) + goto sigbus; + + __asm__ __volatile__ ( +#ifdef __BIG_ENDIAN + "1:\tlwl\t%0, (%2)\n" + "2:\tlwr\t%0, 3(%2)\n\t" +#endif +#ifdef __LITTLE_ENDIAN + "1:\tlwl\t%0, 3(%2)\n" + "2:\tlwr\t%0, (%2)\n\t" +#endif + "li\t%1, 0\n" + "3:\t.section\t.fixup,\"ax\"\n\t" + "4:\tli\t%1, %3\n\t" + "j\t3b\n\t" + ".previous\n\t" + ".section\t__ex_table,\"a\"\n\t" + STR(PTR)"\t1b, 4b\n\t" + STR(PTR)"\t2b, 4b\n\t" + ".previous" + : "=&r" (value), "=r" (res) + : "r" (addr), "i" (-EFAULT)); + if (res) + goto fault; + *newvalue = value; + *regptr = ®s->regs[insn.i_format.rt]; + break; + + case lhu_op: + if (!access_ok(VERIFY_READ, addr, 2)) + goto sigbus; + + __asm__ __volatile__ ( + ".set\tnoat\n" +#ifdef __BIG_ENDIAN + "1:\tlbu\t%0, 0(%2)\n" + "2:\tlbu\t$1, 1(%2)\n\t" +#endif +#ifdef __LITTLE_ENDIAN + "1:\tlbu\t%0, 1(%2)\n" + "2:\tlbu\t$1, 0(%2)\n\t" +#endif + "sll\t%0, 0x8\n\t" + "or\t%0, $1\n\t" + "li\t%1, 0\n" + "3:\t.set\tat\n\t" + ".section\t.fixup,\"ax\"\n\t" + "4:\tli\t%1, %3\n\t" + "j\t3b\n\t" + ".previous\n\t" + ".section\t__ex_table,\"a\"\n\t" + STR(PTR)"\t1b, 4b\n\t" + STR(PTR)"\t2b, 4b\n\t" + ".previous" + : "=&r" (value), "=r" (res) + : "r" (addr), "i" (-EFAULT)); + if (res) + goto fault; + *newvalue = value; + *regptr = ®s->regs[insn.i_format.rt]; + break; + + case lwu_op: +#ifdef CONFIG_MIPS64 + /* + * A 32-bit kernel might be running on a 64-bit processor. But + * if we're on a 32-bit processor and an i-cache incoherency + * or race makes us see a 64-bit instruction here the sdl/sdr + * would blow up, so for now we don't handle unaligned 64-bit + * instructions on 32-bit kernels. + */ + if (!access_ok(VERIFY_READ, addr, 4)) + goto sigbus; + + __asm__ __volatile__ ( +#ifdef __BIG_ENDIAN + "1:\tlwl\t%0, (%2)\n" + "2:\tlwr\t%0, 3(%2)\n\t" +#endif +#ifdef __LITTLE_ENDIAN + "1:\tlwl\t%0, 3(%2)\n" + "2:\tlwr\t%0, (%2)\n\t" +#endif + "dsll\t%0, %0, 32\n\t" + "dsrl\t%0, %0, 32\n\t" + "li\t%1, 0\n" + "3:\t.section\t.fixup,\"ax\"\n\t" + "4:\tli\t%1, %3\n\t" + "j\t3b\n\t" + ".previous\n\t" + ".section\t__ex_table,\"a\"\n\t" + STR(PTR)"\t1b, 4b\n\t" + STR(PTR)"\t2b, 4b\n\t" + ".previous" + : "=&r" (value), "=r" (res) + : "r" (addr), "i" (-EFAULT)); + if (res) + goto fault; + *newvalue = value; + *regptr = ®s->regs[insn.i_format.rt]; + break; +#endif /* CONFIG_MIPS64 */ + + /* Cannot handle 64-bit instructions in 32-bit kernel */ + goto sigill; + + case ld_op: +#ifdef CONFIG_MIPS64 + /* + * A 32-bit kernel might be running on a 64-bit processor. But + * if we're on a 32-bit processor and an i-cache incoherency + * or race makes us see a 64-bit instruction here the sdl/sdr + * would blow up, so for now we don't handle unaligned 64-bit + * instructions on 32-bit kernels. + */ + if (!access_ok(VERIFY_READ, addr, 8)) + goto sigbus; + + __asm__ __volatile__ ( +#ifdef __BIG_ENDIAN + "1:\tldl\t%0, (%2)\n" + "2:\tldr\t%0, 7(%2)\n\t" +#endif +#ifdef __LITTLE_ENDIAN + "1:\tldl\t%0, 7(%2)\n" + "2:\tldr\t%0, (%2)\n\t" +#endif + "li\t%1, 0\n" + "3:\t.section\t.fixup,\"ax\"\n\t" + "4:\tli\t%1, %3\n\t" + "j\t3b\n\t" + ".previous\n\t" + ".section\t__ex_table,\"a\"\n\t" + STR(PTR)"\t1b, 4b\n\t" + STR(PTR)"\t2b, 4b\n\t" + ".previous" + : "=&r" (value), "=r" (res) + : "r" (addr), "i" (-EFAULT)); + if (res) + goto fault; + *newvalue = value; + *regptr = ®s->regs[insn.i_format.rt]; + break; +#endif /* CONFIG_MIPS64 */ + + /* Cannot handle 64-bit instructions in 32-bit kernel */ + goto sigill; + + case sh_op: + if (!access_ok(VERIFY_WRITE, addr, 2)) + goto sigbus; + + value = regs->regs[insn.i_format.rt]; + __asm__ __volatile__ ( +#ifdef __BIG_ENDIAN + ".set\tnoat\n" + "1:\tsb\t%1, 1(%2)\n\t" + "srl\t$1, %1, 0x8\n" + "2:\tsb\t$1, 0(%2)\n\t" + ".set\tat\n\t" +#endif +#ifdef __LITTLE_ENDIAN + ".set\tnoat\n" + "1:\tsb\t%1, 0(%2)\n\t" + "srl\t$1,%1, 0x8\n" + "2:\tsb\t$1, 1(%2)\n\t" + ".set\tat\n\t" +#endif + "li\t%0, 0\n" + "3:\n\t" + ".section\t.fixup,\"ax\"\n\t" + "4:\tli\t%0, %3\n\t" + "j\t3b\n\t" + ".previous\n\t" + ".section\t__ex_table,\"a\"\n\t" + STR(PTR)"\t1b, 4b\n\t" + STR(PTR)"\t2b, 4b\n\t" + ".previous" + : "=r" (res) + : "r" (value), "r" (addr), "i" (-EFAULT)); + if (res) + goto fault; + break; + + case sw_op: + if (!access_ok(VERIFY_WRITE, addr, 4)) + goto sigbus; + + value = regs->regs[insn.i_format.rt]; + __asm__ __volatile__ ( +#ifdef __BIG_ENDIAN + "1:\tswl\t%1,(%2)\n" + "2:\tswr\t%1, 3(%2)\n\t" +#endif +#ifdef __LITTLE_ENDIAN + "1:\tswl\t%1, 3(%2)\n" + "2:\tswr\t%1, (%2)\n\t" +#endif + "li\t%0, 0\n" + "3:\n\t" + ".section\t.fixup,\"ax\"\n\t" + "4:\tli\t%0, %3\n\t" + "j\t3b\n\t" + ".previous\n\t" + ".section\t__ex_table,\"a\"\n\t" + STR(PTR)"\t1b, 4b\n\t" + STR(PTR)"\t2b, 4b\n\t" + ".previous" + : "=r" (res) + : "r" (value), "r" (addr), "i" (-EFAULT)); + if (res) + goto fault; + break; + + case sd_op: +#ifdef CONFIG_MIPS64 + /* + * A 32-bit kernel might be running on a 64-bit processor. But + * if we're on a 32-bit processor and an i-cache incoherency + * or race makes us see a 64-bit instruction here the sdl/sdr + * would blow up, so for now we don't handle unaligned 64-bit + * instructions on 32-bit kernels. + */ + if (!access_ok(VERIFY_WRITE, addr, 8)) + goto sigbus; + + value = regs->regs[insn.i_format.rt]; + __asm__ __volatile__ ( +#ifdef __BIG_ENDIAN + "1:\tsdl\t%1,(%2)\n" + "2:\tsdr\t%1, 7(%2)\n\t" +#endif +#ifdef __LITTLE_ENDIAN + "1:\tsdl\t%1, 7(%2)\n" + "2:\tsdr\t%1, (%2)\n\t" +#endif + "li\t%0, 0\n" + "3:\n\t" + ".section\t.fixup,\"ax\"\n\t" + "4:\tli\t%0, %3\n\t" + "j\t3b\n\t" + ".previous\n\t" + ".section\t__ex_table,\"a\"\n\t" + STR(PTR)"\t1b, 4b\n\t" + STR(PTR)"\t2b, 4b\n\t" + ".previous" + : "=r" (res) + : "r" (value), "r" (addr), "i" (-EFAULT)); + if (res) + goto fault; + break; +#endif /* CONFIG_MIPS64 */ + + /* Cannot handle 64-bit instructions in 32-bit kernel */ + goto sigill; + + 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 coprocessor 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 or + * cache coherence problem. Die sucker, die ... + */ + goto sigill; + } + +#ifdef CONFIG_PROC_FS + unaligned_instructions++; +#endif + + return 0; + +fault: + /* Did we have an exception handler installed? */ + if (fixup_exception(regs)) + return 1; + + die_if_kernel ("Unhandled kernel unaligned access", regs); + send_sig(SIGSEGV, current, 1); + + return 0; + +sigbus: + die_if_kernel("Unhandled kernel unaligned access", regs); + send_sig(SIGBUS, current, 1); + + return 0; + +sigill: + die_if_kernel("Unhandled kernel unaligned access or invalid instruction", regs); + send_sig(SIGILL, current, 1); + + return 0; +} + +asmlinkage void do_ade(struct pt_regs *regs) +{ + unsigned long *regptr, newval; + extern int do_dsemulret(struct pt_regs *); + mm_segment_t seg; + unsigned long pc; + + /* + * Address errors may be deliberately induced by the FPU emulator to + * retake control of the CPU after executing the instruction in the + * delay slot of an emulated branch. + */ + /* Terminate if exception was recognized as a delay slot return */ + if (do_dsemulret(regs)) + return; + + /* Otherwise handle as normal */ + + /* + * Did we catch a fault trying to load an instruction? + * Or are we running in MIPS16 mode? + */ + if ((regs->cp0_badvaddr == regs->cp0_epc) || (regs->cp0_epc & 0x1)) + goto sigbus; + + pc = exception_epc(regs); + if ((current->thread.mflags & MF_FIXADE) == 0) + goto sigbus; + + /* + * Do branch emulation only if we didn't forward the exception. + * This is all so but ugly ... + */ + seg = get_fs(); + if (!user_mode(regs)) + set_fs(KERNEL_DS); + if (!emulate_load_store_insn(regs, (void *)regs->cp0_badvaddr, pc, + ®ptr, &newval)) { + compute_return_epc(regs); + /* + * Now that branch is evaluated, update the dest + * register if necessary + */ + if (regptr) + *regptr = newval; + } + set_fs(seg); + + return; + +sigbus: + die_if_kernel("Kernel unaligned instruction access", regs); + force_sig(SIGBUS, current); + + /* + * XXX On return from the signal handler we should advance the epc + */ +} diff --git a/arch/mips/kernel/vmlinux.lds.S b/arch/mips/kernel/vmlinux.lds.S new file mode 100644 index 000000000000..e830d788c106 --- /dev/null +++ b/arch/mips/kernel/vmlinux.lds.S @@ -0,0 +1,183 @@ +#include <linux/config.h> +#include <asm-generic/vmlinux.lds.h> + +#undef mips /* CPP really sucks for this job */ +#define mips mips +OUTPUT_ARCH(mips) +ENTRY(kernel_entry) +jiffies = JIFFIES; +SECTIONS +{ +#ifdef CONFIG_BOOT_ELF64 + /* Read-only sections, merged into text segment: */ + /* . = 0xc000000000000000; */ + + /* This is the value for an Origin kernel, taken from an IRIX kernel. */ + /* . = 0xc00000000001c000; */ + + /* Set the vaddr for the text segment to a value + >= 0xa800 0000 0001 9000 if no symmon is going to configured + >= 0xa800 0000 0030 0000 otherwise */ + + /* . = 0xa800000000300000; */ + /* . = 0xa800000000300000; */ + . = 0xffffffff80300000; +#endif + . = LOADADDR; + /* read-only */ + _text = .; /* Text and read-only data */ + .text : { + *(.text) + SCHED_TEXT + LOCK_TEXT + *(.fixup) + *(.gnu.warning) + } =0 + + _etext = .; /* End of text section */ + + . = ALIGN(16); /* Exception table */ + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + __start___dbe_table = .; /* Exception table for data bus errors */ + __dbe_table : { *(__dbe_table) } + __stop___dbe_table = .; + + RODATA + + /* writeable */ + .data : { /* Data */ + . = . + DATAOFFSET; /* for CONFIG_MAPPED_KERNEL */ + *(.data.init_task) + + *(.data) + + /* Align the initial ramdisk image (INITRD) on page boundaries. */ + . = ALIGN(4096); + __rd_start = .; + *(.initrd) + . = ALIGN(4096); + __rd_end = .; + + CONSTRUCTORS + } + _gp = . + 0x8000; + .lit8 : { *(.lit8) } + .lit4 : { *(.lit4) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : { *(.sdata) } + + . = ALIGN(4096); + __nosave_begin = .; + .data_nosave : { *(.data.nosave) } + . = ALIGN(4096); + __nosave_end = .; + + . = ALIGN(32); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + + _edata = .; /* End of data section */ + + /* will be freed after init */ + . = ALIGN(4096); /* Init code and data */ + __init_begin = .; + .init.text : { + _sinittext = .; + *(.init.text) + _einittext = .; + } + .init.data : { *(.init.data) } + . = ALIGN(16); + __setup_start = .; + .init.setup : { *(.init.setup) } + __setup_end = .; + + .early_initcall.init : { + __earlyinitcall_start = .; + *(.initcall.early1.init) + } + __earlyinitcall_end = .; + + __initcall_start = .; + .initcall.init : { + *(.initcall1.init) + *(.initcall2.init) + *(.initcall3.init) + *(.initcall4.init) + *(.initcall5.init) + *(.initcall6.init) + *(.initcall7.init) + } + __initcall_end = .; + + __con_initcall_start = .; + .con_initcall.init : { *(.con_initcall.init) } + __con_initcall_end = .; + SECURITY_INIT + . = ALIGN(4096); + __initramfs_start = .; + .init.ramfs : { *(.init.ramfs) } + __initramfs_end = .; + . = ALIGN(32); + __per_cpu_start = .; + .data.percpu : { *(.data.percpu) } + __per_cpu_end = .; + . = ALIGN(4096); + __init_end = .; + /* freed after init ends here */ + + __bss_start = .; /* BSS */ + .sbss : { + *(.sbss) + *(.scommon) + } + .bss : { + *(.bss) + *(COMMON) + } + __bss_stop = .; + + _end = . ; + + /* Sections to be discarded */ + /DISCARD/ : { + *(.exit.text) + *(.exit.data) + *(.exitcall.exit) + + /* ABI crap starts here */ + *(.comment) + *(.MIPS.options) + *(.note) + *(.options) + *(.pdr) + *(.reginfo) + *(.mdebug*) + } + + /* This is the MIPS specific mdebug section. */ + .mdebug : { *(.mdebug) } + /* These are needed for ELF backends which have not yet been + converted to the new style linker. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + /* DWARF debug sections. + Symbols in the .debug DWARF section are relative to the beginning of the + section so we begin .debug at 0. It's not clear yet what needs to happen + for the others. */ + .debug 0 : { *(.debug) } + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + .debug_sfnames 0 : { *(.debug_sfnames) } + .line 0 : { *(.line) } + /* These must appear regardless of . */ + .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) } + .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) } + .comment : { *(.comment) } + .note : { *(.note) } +} |