summaryrefslogtreecommitdiff
path: root/arch/mips/kernel
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/mips/kernel
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')
-rw-r--r--arch/mips/kernel/Makefile65
-rw-r--r--arch/mips/kernel/binfmt_elfn32.c119
-rw-r--r--arch/mips/kernel/binfmt_elfo32.c139
-rw-r--r--arch/mips/kernel/branch.c199
-rw-r--r--arch/mips/kernel/cpu-bugs64.c321
-rw-r--r--arch/mips/kernel/cpu-probe.c598
-rw-r--r--arch/mips/kernel/entry.S155
-rw-r--r--arch/mips/kernel/gdb-low.S370
-rw-r--r--arch/mips/kernel/gdb-stub.c1091
-rw-r--r--arch/mips/kernel/genex.S302
-rw-r--r--arch/mips/kernel/genrtc.c64
-rw-r--r--arch/mips/kernel/head.S221
-rw-r--r--arch/mips/kernel/i8259.c331
-rw-r--r--arch/mips/kernel/init_task.c42
-rw-r--r--arch/mips/kernel/ioctl32.c58
-rw-r--r--arch/mips/kernel/irix5sys.S1041
-rw-r--r--arch/mips/kernel/irixelf.c1326
-rw-r--r--arch/mips/kernel/irixinv.c77
-rw-r--r--arch/mips/kernel/irixioctl.c261
-rw-r--r--arch/mips/kernel/irixsig.c853
-rw-r--r--arch/mips/kernel/irq-msc01.c189
-rw-r--r--arch/mips/kernel/irq-mv6434x.c161
-rw-r--r--arch/mips/kernel/irq-rm7000.c98
-rw-r--r--arch/mips/kernel/irq-rm9000.c149
-rw-r--r--arch/mips/kernel/irq.c140
-rw-r--r--arch/mips/kernel/irq_cpu.c118
-rw-r--r--arch/mips/kernel/linux32.c1469
-rw-r--r--arch/mips/kernel/mips_ksyms.c67
-rw-r--r--arch/mips/kernel/module-elf32.c250
-rw-r--r--arch/mips/kernel/module-elf64.c274
-rw-r--r--arch/mips/kernel/module.c53
-rw-r--r--arch/mips/kernel/offset.c314
-rw-r--r--arch/mips/kernel/proc.c149
-rw-r--r--arch/mips/kernel/process.c364
-rw-r--r--arch/mips/kernel/ptrace.c338
-rw-r--r--arch/mips/kernel/ptrace32.c285
-rw-r--r--arch/mips/kernel/r2300_fpu.S126
-rw-r--r--arch/mips/kernel/r2300_switch.S174
-rw-r--r--arch/mips/kernel/r4k_fpu.S191
-rw-r--r--arch/mips/kernel/r4k_switch.S221
-rw-r--r--arch/mips/kernel/r6000_fpu.S87
-rw-r--r--arch/mips/kernel/reset.c43
-rw-r--r--arch/mips/kernel/scall32-o32.S641
-rw-r--r--arch/mips/kernel/scall64-64.S451
-rw-r--r--arch/mips/kernel/scall64-n32.S365
-rw-r--r--arch/mips/kernel/scall64-o32.S488
-rw-r--r--arch/mips/kernel/semaphore.c164
-rw-r--r--arch/mips/kernel/setup.c571
-rw-r--r--arch/mips/kernel/signal-common.h137
-rw-r--r--arch/mips/kernel/signal.c517
-rw-r--r--arch/mips/kernel/signal32.c905
-rw-r--r--arch/mips/kernel/signal_n32.c197
-rw-r--r--arch/mips/kernel/smp.c425
-rw-r--r--arch/mips/kernel/syscall.c407
-rw-r--r--arch/mips/kernel/sysirix.c2179
-rw-r--r--arch/mips/kernel/time.c755
-rw-r--r--arch/mips/kernel/traps.c1062
-rw-r--r--arch/mips/kernel/unaligned.c550
-rw-r--r--arch/mips/kernel/vmlinux.lds.S183
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 = &current_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 = &current_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 = &current_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 = *(&regs->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 *)&regs->cp0_epc, ptr, sizeof(long), 0);
+ *ptr++ = ';';
+
+ /*
+ * Send frame pointer
+ */
+ *ptr++ = hexchars[REG_FP >> 4];
+ *ptr++ = hexchars[REG_FP & 0xf];
+ *ptr++ = ':';
+ ptr = mem2hex((char *)&regs->reg30, ptr, sizeof(long), 0);
+ *ptr++ = ';';
+
+ /*
+ * Send stack pointer
+ */
+ *ptr++ = hexchars[REG_SP >> 4];
+ *ptr++ = hexchars[REG_SP & 0xf];
+ *ptr++ = ':';
+ ptr = mem2hex((char *)&regs->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 *)&regs->reg0, ptr, 32*sizeof(long), 0); /* r0...r31 */
+ ptr = mem2hex((char *)&regs->cp0_status, ptr, 6*sizeof(long), 0); /* cp0 */
+ ptr = mem2hex((char *)&regs->fpr0, ptr, 32*sizeof(long), 0); /* f0...31 */
+ ptr = mem2hex((char *)&regs->cp1_fsr, ptr, 2*sizeof(long), 0); /* cp1 */
+ ptr = mem2hex((char *)&regs->frame_ptr, ptr, 2*sizeof(long), 0); /* frp */
+ ptr = mem2hex((char *)&regs->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 *)&regs->reg0, 32*sizeof(long), 0, 0);
+ ptr += 32*(2*sizeof(long));
+ hex2mem(ptr, (char *)&regs->cp0_status, 6*sizeof(long), 0, 0);
+ ptr += 6*(2*sizeof(long));
+ hex2mem(ptr, (char *)&regs->fpr0, 32*sizeof(long), 0, 0);
+ ptr += 32*(2*sizeof(long));
+ hex2mem(ptr, (char *)&regs->cp1_fsr, 2*sizeof(long), 0, 0);
+ ptr += 2*(2*sizeof(long));
+ hex2mem(ptr, (char *)&regs->frame_ptr, 2*sizeof(long), 0, 0);
+ ptr += 2*(2*sizeof(long));
+ hex2mem(ptr, (char *)&regs->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(&current->mm->mmap_sem);
+ do_brk(start, end - start);
+ up_write(&current->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(&current->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(&current->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(&current->mm->mmap_sem);
+ do_brk(len, (last_bss - len));
+ up_write(&current->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(&current->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(&current->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(&current->mm->mmap_sem);
+ v = do_brk (PRDA_ADDRESS, PAGE_SIZE);
+ up_write(&current->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(&current->mm->mmap_sem);
+ (void) do_mmap(NULL, 0, 4096, PROT_READ | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE, 0);
+ up_write(&current->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(&current->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(&current->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(&current->mm->mmap_sem);
+ do_brk(len, bss-len);
+ up_write(&current->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(&current->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(&current->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(&notes[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(&notes[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(&current->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(&current->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(&current->sighand->siglock);
+ sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+ sigaddset(&current->blocked,sig);
+ recalc_sigpending();
+ spin_unlock_irq(&current->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 = &current->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 *) &current->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(&current->sighand->siglock);
+ current->blocked = blocked;
+ recalc_sigpending();
+ spin_unlock_irq(&current->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" (&regs));
+ /* 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(&current->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(&current->sighand->siglock);
+ }
+ if(old) {
+ if (!access_ok(VERIFY_WRITE, old, sizeof(*old)))
+ return -EFAULT;
+ __copy_to_user(old, &current->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(&current->sighand->siglock);
+ saveset = current->blocked;
+ current->blocked = newset;
+ recalc_sigpending();
+ spin_unlock_irq(&current->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 (&current->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(&current->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(&current->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, &current->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(&current->mm->mmap_sem);
+ error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
+ up_write(&current->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]), &regs);
+ 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, &current->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(&regs, 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, &regs, 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(&current->sighand->siglock);
+ saveset = current->blocked;
+ current->blocked = newset;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ regs.regs[2] = EINTR;
+ regs.regs[7] = 1;
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (do_signal(&saveset, &regs))
+ 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(&current->sighand->siglock);
+ saveset = current->blocked;
+ current->blocked = newset;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ regs.regs[2] = EINTR;
+ regs.regs[7] = 1;
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (do_signal(&saveset, &regs))
+ 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(&current->sighand->siglock);
+ current->blocked = blocked;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ if (restore_sigcontext(&regs, &frame->sf_sc))
+ goto badframe;
+
+ /*
+ * 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" (&regs));
+ /* 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(&current->sighand->siglock);
+ current->blocked = set;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ if (restore_sigcontext(&regs, &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" (&regs));
+ /* 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(&current->sighand->siglock);
+ sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+ sigaddset(&current->blocked,sig);
+ recalc_sigpending();
+ spin_unlock_irq(&current->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 = &current->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(&current->sighand->siglock);
+ saveset = current->blocked;
+ current->blocked = newset;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ regs.regs[2] = EINTR;
+ regs.regs[7] = 1;
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (do_signal32(&saveset, &regs))
+ 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(&current->sighand->siglock);
+ saveset = current->blocked;
+ current->blocked = newset;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ regs.regs[2] = EINTR;
+ regs.regs[7] = 1;
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (do_signal32(&saveset, &regs))
+ 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(&current->sighand->siglock);
+ current->blocked = blocked;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ if (restore_sigcontext32(&regs, &frame->sf_sc))
+ goto badframe;
+
+ /*
+ * 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" (&regs));
+ /* 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(&current->sighand->siglock);
+ current->blocked = set;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ if (restore_sigcontext32(&regs, &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" (&regs));
+ /* 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(&current->sighand->siglock);
+ sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+ sigaddset(&current->blocked,sig);
+ recalc_sigpending();
+ spin_unlock_irq(&current->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 = &current->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(&current->sighand->siglock);
+ current->blocked = set;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ if (restore_sigcontext(&regs, &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" (&regs));
+ /* 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 = &current_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(&current->mm->mmap_sem);
+ error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
+ up_write(&current->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], &regs, 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, &regs, 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], &regs);
+ 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(&current->real_timer)) {
+ unsigned long now = jiffies;
+ val = current->real_timer.expires;
+ add_timer(&current->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(&current->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(&current->mm->mmap_sem);
+ retval = do_mmap(file, addr, len, prot, flags, offset);
+ up_write(&current->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(&current->mm->mmap_sem);
+ error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
+ up_write(&current->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,
+ &current->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,
+ &current->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 = &regs->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 = &regs->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 = &regs->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 = &regs->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 = &regs->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,
+ &regptr, &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) }
+}