Linux-2.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!
diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
new file mode 100644
index 0000000..237f922
--- /dev/null
+++ b/arch/sparc/Kconfig
@@ -0,0 +1,393 @@
+# $Id: config.in,v 1.113 2002/01/24 22:14:44 davem Exp $
+# For a description of the syntax of this configuration file,
+# see Documentation/kbuild/kconfig-language.txt.
+#
+
+mainmenu "Linux/SPARC Kernel Configuration"
+
+config MMU
+	bool
+	default y
+
+config UID16
+	bool
+	default y
+
+config HIGHMEM
+	bool
+	default y
+
+config GENERIC_ISA_DMA
+	bool
+	default y
+
+source "init/Kconfig"
+
+menu "General machine setup"
+
+config VT
+	bool
+	select INPUT
+	default y
+	---help---
+	  If you say Y here, you will get support for terminal devices with
+	  display and keyboard devices. These are called "virtual" because you
+	  can run several virtual terminals (also called virtual consoles) on
+	  one physical terminal. This is rather useful, for example one
+	  virtual terminal can collect system messages and warnings, another
+	  one can be used for a text-mode user session, and a third could run
+	  an X session, all in parallel. Switching between virtual terminals
+	  is done with certain key combinations, usually Alt-<function key>.
+
+	  The setterm command ("man setterm") can be used to change the
+	  properties (such as colors or beeping) of a virtual terminal. The
+	  man page console_codes(4) ("man console_codes") contains the special
+	  character sequences that can be used to change those properties
+	  directly. The fonts used on virtual terminals can be changed with
+	  the setfont ("man setfont") command and the key bindings are defined
+	  with the loadkeys ("man loadkeys") command.
+
+	  You need at least one virtual terminal device in order to make use
+	  of your keyboard and monitor. Therefore, only people configuring an
+	  embedded system would want to say N here in order to save some
+	  memory; the only way to log into such a system is then via a serial
+	  or network connection.
+
+	  If unsure, say Y, or else you won't be able to do much with your new
+	  shiny Linux system :-)
+
+config VT_CONSOLE
+	bool
+	default y
+	---help---
+	  The system console is the device which receives all kernel messages
+	  and warnings and which allows logins in single user mode. If you
+	  answer Y here, a virtual terminal (the device used to interact with
+	  a physical terminal) can be used as system console. This is the most
+	  common mode of operations, so you should say Y here unless you want
+	  the kernel messages be output only to a serial port (in which case
+	  you should say Y to "Console on serial port", below).
+
+	  If you do say Y here, by default the currently visible virtual
+	  terminal (/dev/tty0) will be used as system console. You can change
+	  that with a kernel command line option such as "console=tty3" which
+	  would use the third virtual terminal as system console. (Try "man
+	  bootparam" or see the documentation of your boot loader (lilo or
+	  loadlin) about how to pass options to the kernel at boot time.)
+
+	  If unsure, say Y.
+
+config HW_CONSOLE
+	bool
+	default y
+
+config SMP
+	bool "Symmetric multi-processing support (does not work on sun4/sun4c)"
+	depends on BROKEN
+	---help---
+	  This enables support for systems with more than one CPU. If you have
+	  a system with only one CPU, say N. If you have a system with more
+	  than one CPU, say Y.
+
+	  If you say N here, the kernel will run on single and multiprocessor
+	  machines, but will use only one CPU of a multiprocessor machine. If
+	  you say Y here, the kernel will run on many, but not all,
+	  singleprocessor machines. On a singleprocessor machine, the kernel
+	  will run faster if you say N here.
+
+	  People using multiprocessor machines who say Y here should also say
+	  Y to "Enhanced Real Time Clock Support", below. The "Advanced Power
+	  Management" code will be disabled if you say Y here.
+
+	  See also the <file:Documentation/smp.txt>,
+	  <file:Documentation/nmi_watchdog.txt> and the SMP-HOWTO available at
+	  <http://www.tldp.org/docs.html#howto>.
+
+	  If you don't know what to do here, say N.
+
+config NR_CPUS
+	int "Maximum number of CPUs (2-32)"
+	range 2 32
+	depends on SMP
+	default "32"
+
+# Identify this as a Sparc32 build
+config SPARC32
+	bool
+	default y
+	help
+	  SPARC is a family of RISC microprocessors designed and marketed by
+	  Sun Microsystems, incorporated.  They are very widely found in Sun
+	  workstations and clones. This port covers the original 32-bit SPARC;
+	  it is old and stable and usually considered one of the "big three"
+	  along with the Intel and Alpha ports.  The UltraLinux project
+	  maintains both the SPARC32 and SPARC64 ports; its web page is
+	  available at <http://www.ultralinux.org/>.
+
+# Global things across all Sun machines.
+config ISA
+	bool
+	help
+	  ISA is found on Espresso only and is not supported currently.
+	  Say N
+
+config EISA
+	bool
+	help
+	  EISA is not supported.
+	  Say N
+
+config MCA
+	bool
+	help
+	  MCA is not supported.
+	  Say N
+
+config PCMCIA
+	tristate
+	---help---
+	  Say Y here if you want to attach PCMCIA- or PC-cards to your Linux
+	  computer.  These are credit-card size devices such as network cards,
+	  modems or hard drives often used with laptops computers.  There are
+	  actually two varieties of these cards: the older 16 bit PCMCIA cards
+	  and the newer 32 bit CardBus cards.  If you want to use CardBus
+	  cards, you need to say Y here and also to "CardBus support" below.
+
+	  To use your PC-cards, you will need supporting software from David
+	  Hinds' pcmcia-cs package (see the file <file:Documentation/Changes>
+	  for location).  Please also read the PCMCIA-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.
+
+	  To compile this driver as modules, choose M here: the
+	  modules will be called pcmcia_core and ds.
+
+config SBUS
+	bool
+	default y
+
+config SBUSCHAR
+	bool
+	default y
+
+config SERIAL_CONSOLE
+	bool
+	default y
+	---help---
+	  If you say Y here, it will be possible to use a serial port as the
+	  system console (the system console is the device which receives all
+	  kernel messages and warnings and which allows logins in single user
+	  mode). This could be useful if some terminal or printer is connected
+	  to that serial port.
+
+	  Even if you say Y here, the currently visible virtual console
+	  (/dev/tty0) will still be used as the system console by default, but
+	  you can alter that using a kernel command line option such as
+	  "console=ttyS1". (Try "man bootparam" or see the documentation of
+	  your boot loader (silo) about how to pass options to the kernel at
+	  boot time.)
+
+	  If you don't have a graphics card installed and you say Y here, the
+	  kernel will automatically use the first serial line, /dev/ttyS0, as
+	  system console.
+
+	  If unsure, say N.
+
+config SUN_AUXIO
+	bool
+	default y
+
+config SUN_IO
+	bool
+	default y
+
+config RWSEM_GENERIC_SPINLOCK
+	bool
+	default y
+
+config RWSEM_XCHGADD_ALGORITHM
+	bool
+
+config GENERIC_CALIBRATE_DELAY
+	bool
+	default y
+
+config SUN_PM
+	bool
+	default y
+	help
+	  Enable power management and CPU standby features on supported
+	  SPARC platforms.
+
+config SUN4
+	bool "Support for SUN4 machines (disables SUN4[CDM] support)"
+	depends on !SMP
+	default n
+	help
+	  Say Y here if, and only if, your machine is a sun4. Note that
+	  a kernel compiled with this option will run only on sun4.
+	  (And the current version will probably work only on sun4/330.)
+
+if !SUN4
+
+config PCI
+	bool "Support for PCI and PS/2 keyboard/mouse"
+	help
+	  CONFIG_PCI is needed for all JavaStation's (including MrCoffee),
+	  CP-1200, JavaEngine-1, Corona, Red October, and Serengeti SGSC.
+	  All of these platforms are extremely obscure, so say N if unsure.
+
+source "drivers/pci/Kconfig"
+
+endif
+
+config SUN_OPENPROMFS
+	tristate "Openprom tree appears in /proc/openprom"
+	help
+	  If you say Y, the OpenPROM device tree will be available as a
+	  virtual file system, which you can mount to /proc/openprom by "mount
+	  -t openpromfs none /proc/openprom".
+
+	  To compile the /proc/openprom support as a module, choose M here: the
+	  module will be called openpromfs.
+
+	  Only choose N if you know in advance that you will not need to modify
+	  OpenPROM settings on the running system.
+
+source "fs/Kconfig.binfmt"
+
+config SUNOS_EMUL
+	bool "SunOS binary emulation"
+	help
+	  This allows you to run most SunOS binaries.  If you want to do this,
+	  say Y here and place appropriate files in /usr/gnemul/sunos. See
+	  <http://www.ultralinux.org/faq.html> for more information.  If you
+	  want to run SunOS binaries on an Ultra you must also say Y to
+	  "Kernel support for 32-bit a.out binaries" above.
+
+source "drivers/parport/Kconfig"
+
+config PRINTER
+	tristate "Parallel printer support"
+	depends on PARPORT
+	---help---
+	  If you intend to attach a printer to the parallel port of your Linux
+	  box (as opposed to using a serial printer; if the connector at the
+	  printer has 9 or 25 holes ["female"], then it's serial), say Y.
+	  Also read the Printing-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.
+
+	  It is possible to share one parallel port among several devices
+	  (e.g. printer and ZIP drive) and it is safe to compile the
+	  corresponding drivers into the kernel.  If you want to compile this
+	  driver as a module however, choose M here and read
+	  <file:Documentation/parport.txt>.  The module will be called lp.
+
+	  If you have several parallel ports, you can specify which ports to
+	  use with the "lp" kernel command line option.  (Try "man bootparam"
+	  or see the documentation of your boot loader (silo) about how to pass
+	  options to the kernel at boot time.)  The syntax of the "lp" command
+	  line option can be found in <file:drivers/char/lp.c>.
+
+	  If you have more than 8 printers, you need to increase the LP_NO
+	  macro in lp.c and the PARPORT_MAX macro in parport.h.
+
+endmenu
+
+source "drivers/base/Kconfig"
+
+source "drivers/video/Kconfig"
+
+source "drivers/mtd/Kconfig"
+
+source "drivers/serial/Kconfig"
+
+if !SUN4
+source "drivers/sbus/char/Kconfig"
+endif
+
+source "drivers/block/Kconfig"
+
+# Don't frighten a common SBus user
+if PCI
+
+source "drivers/ide/Kconfig"
+
+endif
+
+source "drivers/isdn/Kconfig"
+
+source "drivers/scsi/Kconfig"
+
+source "drivers/fc4/Kconfig"
+
+source "drivers/md/Kconfig"
+
+source "net/Kconfig"
+
+# This one must be before the filesystem configs. -DaveM
+
+menu "Unix98 PTY support"
+
+config UNIX98_PTYS
+	bool "Unix98 PTY support"
+	---help---
+	  A pseudo terminal (PTY) is a software device consisting of two
+	  halves: a master and a slave. The slave device behaves identical to
+	  a physical terminal; the master device is used by a process to
+	  read data from and write data to the slave, thereby emulating a
+	  terminal. Typical programs for the master side are telnet servers
+	  and xterms.
+
+	  Linux has traditionally used the BSD-like names /dev/ptyxx for
+	  masters and /dev/ttyxx for slaves of pseudo terminals. This scheme
+	  has a number of problems. The GNU C library glibc 2.1 and later,
+	  however, supports the Unix98 naming standard: in order to acquire a
+	  pseudo terminal, a process opens /dev/ptmx; the number of the pseudo
+	  terminal is then made available to the process and the pseudo
+	  terminal slave can be accessed as /dev/pts/<number>. What was
+	  traditionally /dev/ttyp2 will then be /dev/pts/2, for example.
+
+	  The entries in /dev/pts/ are created on the fly by a virtual
+	  file system; therefore, if you say Y here you should say Y to
+	  "/dev/pts file system for Unix98 PTYs" as well.
+
+	  If you want to say Y here, you need to have the C library glibc 2.1
+	  or later (equal to libc-6.1, check with "ls -l /lib/libc.so.*").
+	  Read the instructions in <file:Documentation/Changes> pertaining to
+	  pseudo terminals. It's safe to say N.
+
+config UNIX98_PTY_COUNT
+	int "Maximum number of Unix98 PTYs in use (0-2048)"
+	depends on UNIX98_PTYS
+	default "256"
+	help
+	  The maximum number of Unix98 PTYs that can be used at any one time.
+	  The default is 256, and should be enough for desktop systems. Server
+	  machines which support incoming telnet/rlogin/ssh connections and/or
+	  serve several X terminals may want to increase this: every incoming
+	  connection and every xterm uses up one PTY.
+
+	  When not in use, each additional set of 256 PTYs occupy
+	  approximately 8 KB of kernel memory on 32-bit architectures.
+
+endmenu
+
+source "drivers/input/Kconfig"
+
+source "fs/Kconfig"
+
+source "sound/Kconfig"
+
+source "drivers/usb/Kconfig"
+
+source "drivers/infiniband/Kconfig"
+
+source "drivers/char/watchdog/Kconfig"
+
+source "arch/sparc/Kconfig.debug"
+
+source "security/Kconfig"
+
+source "crypto/Kconfig"
+
+source "lib/Kconfig"
diff --git a/arch/sparc/Kconfig.debug b/arch/sparc/Kconfig.debug
new file mode 100644
index 0000000..120f6b5
--- /dev/null
+++ b/arch/sparc/Kconfig.debug
@@ -0,0 +1,14 @@
+menu "Kernel hacking"
+
+source "lib/Kconfig.debug"
+
+config DEBUG_STACK_USAGE
+	bool "Enable stack utilization instrumentation"
+	depends on DEBUG_KERNEL
+	help
+	  Enables the display of the minimum amount of free stack which each
+	  task has ever had available in the sysrq-T and sysrq-P debug output.
+
+	  This option will slow down process creation somewhat.
+
+endmenu
diff --git a/arch/sparc/Makefile b/arch/sparc/Makefile
new file mode 100644
index 0000000..7b3bbaf0
--- /dev/null
+++ b/arch/sparc/Makefile
@@ -0,0 +1,78 @@
+#
+# sparc/Makefile
+#
+# Makefile for the architecture dependent flags and dependencies on the
+# Sparc.
+#
+# Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
+#
+
+#
+# Uncomment the first CFLAGS if you are doing kgdb source level
+# debugging of the kernel to get the proper debugging information.
+
+AS              := $(AS) -32
+LDFLAGS		:= -m elf32_sparc
+CHECKFLAGS	+= -D__sparc__
+
+#CFLAGS := $(CFLAGS) -g -pipe -fcall-used-g5 -fcall-used-g7
+CFLAGS := $(CFLAGS) -m32 -pipe -mno-fpu -fcall-used-g5 -fcall-used-g7
+AFLAGS := $(AFLAGS) -m32
+
+#LDFLAGS_vmlinux = -N -Ttext 0xf0004000
+#  Since 2.5.40, the first stage is left not btfix-ed.
+#  Actual linking is done with "make image".
+LDFLAGS_vmlinux = -r
+
+head-y := arch/sparc/kernel/head.o arch/sparc/kernel/init_task.o
+HEAD_Y := $(head-y)
+
+core-y += arch/sparc/kernel/ arch/sparc/mm/ arch/sparc/math-emu/
+libs-y += arch/sparc/prom/ arch/sparc/lib/
+
+# Export what is needed by arch/sparc/boot/Makefile
+# Renaming is done to avoid confusing pattern matching rules in 2.5.45 (multy-)
+INIT_Y		:= $(patsubst %/, %/built-in.o, $(init-y))
+CORE_Y		:= $(core-y)
+CORE_Y		+= kernel/ mm/ fs/ ipc/ security/ crypto/
+CORE_Y		:= $(patsubst %/, %/built-in.o, $(CORE_Y))
+DRIVERS_Y	:= $(patsubst %/, %/built-in.o, $(drivers-y))
+NET_Y		:= $(patsubst %/, %/built-in.o, $(net-y))
+LIBS_Y1		:= $(patsubst %/, %/lib.a, $(libs-y))
+LIBS_Y2		:= $(patsubst %/, %/built-in.o, $(libs-y))
+LIBS_Y		:= $(LIBS_Y1) $(LIBS_Y2)
+
+ifdef CONFIG_KALLSYMS
+kallsyms.o := .tmp_kallsyms2.o
+endif
+
+export INIT_Y CORE_Y DRIVERS_Y NET_Y LIBS_Y HEAD_Y kallsyms.o
+
+# Default target
+all: image
+
+boot := arch/sparc/boot
+
+image tftpboot.img: vmlinux
+	$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
+
+archclean:
+	$(Q)$(MAKE) $(clean)=$(boot)
+
+prepare: include/asm-$(ARCH)/asm_offsets.h
+
+arch/$(ARCH)/kernel/asm-offsets.s: include/asm include/linux/version.h \
+				   include/config/MARKER
+
+include/asm-$(ARCH)/asm_offsets.h: arch/$(ARCH)/kernel/asm-offsets.s
+	$(call filechk,gen-asm-offsets)
+
+CLEAN_FILES +=	include/asm-$(ARCH)/asm_offsets.h	\
+		arch/$(ARCH)/kernel/asm-offsets.s	\
+		arch/$(ARCH)/boot/System.map
+
+# Don't use tabs in echo arguments.
+define archhelp
+  echo  '* image        - kernel image ($(boot)/image)'
+  echo  '  tftpboot.img - image prepared for tftp'
+endef
diff --git a/arch/sparc/boot/Makefile b/arch/sparc/boot/Makefile
new file mode 100644
index 0000000..b365084
--- /dev/null
+++ b/arch/sparc/boot/Makefile
@@ -0,0 +1,58 @@
+# $Id: Makefile,v 1.10 2000/02/23 08:17:46 jj Exp $
+# Makefile for the Sparc boot stuff.
+#
+# Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+# Copyright (C) 1997,1998 Jakub Jelinek (jj@ultra.linux.cz)
+
+ROOT_IMG	:= /usr/src/root.img
+ELFTOAOUT	:= elftoaout
+
+hostprogs-y	:= piggyback btfixupprep
+targets		:= tftpboot.img btfix.o btfix.S image
+
+quiet_cmd_elftoaout	= ELFTOAOUT $@
+      cmd_elftoaout	= $(ELFTOAOUT) $(obj)/image -o $@
+quiet_cmd_piggy		= PIGGY   $@
+      cmd_piggy		= $(obj)/piggyback $@ $(obj)/System.map $(ROOT_IMG)
+quiet_cmd_btfix		= BTFIX   $@
+      cmd_btfix		= $(OBJDUMP) -x vmlinux | $(obj)/btfixupprep > $@
+quiet_cmd_sysmap        = SYSMAP  $(obj)/System.map
+      cmd_sysmap        = $(CONFIG_SHELL) $(srctree)/scripts/mksysmap
+quiet_cmd_image = LD      $@
+      cmd_image = $(LD) $(LDFLAGS) $(EXTRA_LDFLAGS) $(LDFLAGS_$(@F)) -o $@
+
+define rule_image
+	$(if $($(quiet)cmd_image),               \
+	  echo '  $($(quiet)cmd_image)' &&)      \
+	  $(cmd_image);                          \
+	$(if $($(quiet)cmd_sysmap),              \
+	  echo '  $($(quiet)cmd_sysmap)' &&)  \
+	$(cmd_sysmap) $@ $(obj)/System.map;      \
+	if [ $$? -ne 0 ]; then                   \
+		rm -f $@;                        \
+		/bin/false;                      \
+	fi;                                      \
+	echo 'cmd_$@ := $(cmd_image)' > $(@D)/.$(@F).cmd
+endef
+
+BTOBJS := $(HEAD_Y) $(INIT_Y)
+BTLIBS := $(CORE_Y) $(LIBS_Y) $(DRIVERS_Y) $(NET_Y)
+LDFLAGS_image := -T arch/sparc/kernel/vmlinux.lds $(BTOBJS) \
+                  --start-group $(BTLIBS) --end-group \
+                  $(kallsyms.o) $(obj)/btfix.o
+
+# Link the final image including btfixup'ed symbols.
+# This is a replacement for the link done in the top-level Makefile.
+# Note: No dependency on the prerequisite files since that would require
+# make to try check if they are updated - and due to changes
+# in gcc options (path for example) this would result in
+# these files being recompiled for each build.
+$(obj)/image: $(obj)/btfix.o FORCE
+	$(call if_changed_rule,image)
+
+$(obj)/tftpboot.img: $(obj)/piggyback $(obj)/System.map $(obj)/image FORCE
+	$(call if_changed,elftoaout)
+	$(call if_changed,piggy)
+
+$(obj)/btfix.S: $(obj)/btfixupprep vmlinux FORCE
+	$(call if_changed,btfix)
diff --git a/arch/sparc/boot/btfixupprep.c b/arch/sparc/boot/btfixupprep.c
new file mode 100644
index 0000000..dc7b054
--- /dev/null
+++ b/arch/sparc/boot/btfixupprep.c
@@ -0,0 +1,386 @@
+/* $Id: btfixupprep.c,v 1.6 2001/08/22 15:27:47 davem Exp $
+   Simple utility to prepare vmlinux image for sparc.
+   Resolves all BTFIXUP uses and settings and creates
+   a special .s object to link to the image.
+   
+   Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+   
+   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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+   
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <malloc.h>
+
+#define MAXSYMS 1024
+
+static char *symtab = "SYMBOL TABLE:";
+static char *relrec = "RELOCATION RECORDS FOR [";
+static int rellen;
+static int symlen;
+int mode;
+
+struct _btfixup;
+
+typedef struct _btfixuprel {
+	char *sect;
+	unsigned long offset;
+	struct _btfixup *f;
+	int frel;
+	struct _btfixuprel *next;
+} btfixuprel;
+
+typedef struct _btfixup {
+	int type;
+	int setinitval;
+	unsigned int initval;
+	char *initvalstr;
+	char *name;
+	btfixuprel *rel;
+} btfixup;
+
+btfixup array[MAXSYMS];
+int last = 0;
+char buffer[1024];
+unsigned long lastfoffset = -1;
+unsigned long lastfrelno;
+btfixup *lastf;
+
+void fatal(void) __attribute__((noreturn));
+void fatal(void)
+{
+	fprintf(stderr, "Malformed output from objdump\n%s\n", buffer);
+	exit(1);
+}
+
+btfixup *find(int type, char *name)
+{
+	int i;
+	for (i = 0; i < last; i++) {
+		if (array[i].type == type && !strcmp(array[i].name, name))
+			return array + i;
+	}
+	array[last].type = type;
+	array[last].name = strdup(name);
+	array[last].setinitval = 0;
+	if (!array[last].name) fatal();
+	array[last].rel = NULL;
+	last++;
+	if (last >= MAXSYMS) {
+		fprintf(stderr, "Ugh. Something strange. More than %d different BTFIXUP symbols\n", MAXSYMS);
+		exit(1);
+	}
+	return array + last - 1;
+}
+
+void set_mode (char *buffer)
+{
+  	for (mode = 0;; mode++)
+		if (buffer[mode] < '0' || buffer[mode] > '9')
+			break;
+	if (mode != 8 && mode != 16)
+		fatal();
+}
+
+
+int main(int argc,char **argv)
+{
+	char *p, *q;
+	char *sect;
+	int i, j, k;
+	unsigned int initval;
+	int shift;
+	btfixup *f;
+	btfixuprel *r, **rr;
+	unsigned long offset;
+	char *initvalstr;
+
+	symlen = strlen(symtab);
+	while (fgets (buffer, 1024, stdin) != NULL)
+		if (!strncmp (buffer, symtab, symlen))
+			goto main0;
+	fatal();
+main0:
+	rellen = strlen(relrec);
+	while (fgets (buffer, 1024, stdin) != NULL)
+		if (!strncmp (buffer, relrec, rellen))
+			goto main1;
+	fatal();
+main1:
+	sect = malloc(strlen (buffer + rellen) + 1);
+	if (!sect) fatal();
+	strcpy (sect, buffer + rellen);
+	p = strchr (sect, ']');
+	if (!p) fatal();
+	*p = 0;
+	if (fgets (buffer, 1024, stdin) == NULL)
+		fatal();
+	while (fgets (buffer, 1024, stdin) != NULL) {
+		int nbase;
+		if (!strncmp (buffer, relrec, rellen))
+			goto main1;
+		if (mode == 0)
+			set_mode (buffer);
+		p = strchr (buffer, '\n');
+		if (p) *p = 0;
+		if (strlen (buffer) < 22+mode)
+			continue;
+		if (strncmp (buffer + mode, " R_SPARC_", 9))
+			continue;
+		nbase = 27 - 8 + mode;
+		if (buffer[nbase] != '_' || buffer[nbase+1] != '_' || buffer[nbase+2] != '_')
+			continue;
+		switch (buffer[nbase+3]) {
+			case 'f':	/* CALL */
+			case 'b':	/* BLACKBOX */
+			case 's':	/* SIMM13 */
+			case 'a':	/* HALF */
+			case 'h':	/* SETHI */
+			case 'i':	/* INT */
+				break;
+			default:
+				continue;
+		}
+		p = strchr (buffer + nbase+5, '+');
+		if (p) *p = 0;
+		shift = nbase + 5;
+		if (buffer[nbase+4] == 's' && buffer[nbase+5] == '_') {
+			shift = nbase + 6;
+			if (strcmp (sect, ".init.text")) {
+				fprintf(stderr,
+				    "Wrong use of '%s' BTFIXUPSET in '%s' section.\n"
+				    "BTFIXUPSET_CALL can be used only in"
+				    " __init sections\n",
+				    buffer + shift, sect);
+				exit(1);
+			}
+		} else if (buffer[nbase+4] != '_')
+			continue;
+		if (!strcmp (sect, ".text.exit"))
+			continue;
+		if (strcmp (sect, ".text") &&
+		    strcmp (sect, ".init.text") &&
+		    strcmp (sect, ".fixup") &&
+		    (strcmp (sect, "__ksymtab") || buffer[nbase+3] != 'f')) {
+			if (buffer[nbase+3] == 'f')
+				fprintf(stderr,
+				    "Wrong use of '%s' in '%s' section.\n"
+				    " It can be used only in .text, .init.text,"
+				    " .fixup and __ksymtab\n",
+				    buffer + shift, sect);
+			else
+				fprintf(stderr,
+				    "Wrong use of '%s' in '%s' section.\n"
+				    " It can be only used in .text, .init.text,"
+				    " and .fixup\n", buffer + shift, sect);
+			exit(1);
+		}
+		p = strstr (buffer + shift, "__btset_");
+		if (p && buffer[nbase+4] == 's') {
+			fprintf(stderr, "__btset_ in BTFIXUP name can only be used when defining the variable, not for setting\n%s\n", buffer);
+			exit(1);
+		}
+		initval = 0;
+		initvalstr = NULL;
+		if (p) {
+			if (p[8] != '0' || p[9] != 'x') {
+				fprintf(stderr, "Pre-initialized values can be only initialized with hexadecimal constants starting 0x\n%s\n", buffer);
+				exit(1);
+			}
+			initval = strtoul(p + 10, &q, 16);
+			if (*q || !initval) {
+				fprintf(stderr, "Pre-initialized values can be only in the form name__btset_0xXXXXXXXX where X are hex digits.\nThey cannot be name__btset_0x00000000 though. Use BTFIXUPDEF_XX instead of BTFIXUPDEF_XX_INIT then.\n%s\n", buffer);
+				exit(1);
+			}
+			initvalstr = p + 10;
+			*p = 0;
+		}
+		f = find(buffer[nbase+3], buffer + shift);
+		if (buffer[nbase+4] == 's')
+			continue;
+		switch (buffer[nbase+3]) {
+		case 'f':
+			if (initval) {
+				fprintf(stderr, "Cannot use pre-initalized fixups for calls\n%s\n", buffer);
+				exit(1);
+			}
+			if (!strcmp (sect, "__ksymtab")) {
+				if (strncmp (buffer + mode+9, "32        ", 10)) {
+					fprintf(stderr, "BTFIXUP_CALL in EXPORT_SYMBOL results in relocation other than R_SPARC_32\n\%s\n", buffer);
+					exit(1);
+				}
+			} else if (strncmp (buffer + mode+9, "WDISP30   ", 10) &&
+				   strncmp (buffer + mode+9, "HI22      ", 10) &&
+				   strncmp (buffer + mode+9, "LO10      ", 10)) {
+				fprintf(stderr, "BTFIXUP_CALL results in relocation other than R_SPARC_WDISP30, R_SPARC_HI22 or R_SPARC_LO10\n%s\n", buffer);
+				exit(1);
+			}
+			break;
+		case 'b':
+			if (initval) {
+				fprintf(stderr, "Cannot use pre-initialized fixups for blackboxes\n%s\n", buffer);
+				exit(1);
+			}
+			if (strncmp (buffer + mode+9, "HI22      ", 10)) {
+				fprintf(stderr, "BTFIXUP_BLACKBOX results in relocation other than R_SPARC_HI22\n%s\n", buffer);
+				exit(1);
+			}
+			break;
+		case 's':
+			if (initval + 0x1000 >= 0x2000) {
+				fprintf(stderr, "Wrong initializer for SIMM13. Has to be from $fffff000 to $00000fff\n%s\n", buffer);
+				exit(1);
+			}
+			if (strncmp (buffer + mode+9, "13        ", 10)) {
+				fprintf(stderr, "BTFIXUP_SIMM13 results in relocation other than R_SPARC_13\n%s\n", buffer);
+				exit(1);
+			}
+			break;
+		case 'a':
+			if (initval + 0x1000 >= 0x2000 && (initval & 0x3ff)) {
+				fprintf(stderr, "Wrong initializer for HALF.\n%s\n", buffer);
+				exit(1);
+			}
+			if (strncmp (buffer + mode+9, "13        ", 10)) {
+				fprintf(stderr, "BTFIXUP_HALF results in relocation other than R_SPARC_13\n%s\n", buffer);
+				exit(1);
+			}
+			break;
+		case 'h':
+			if (initval & 0x3ff) {
+				fprintf(stderr, "Wrong initializer for SETHI. Cannot have set low 10 bits\n%s\n", buffer);
+				exit(1);
+			}
+			if (strncmp (buffer + mode+9, "HI22      ", 10)) {
+				fprintf(stderr, "BTFIXUP_SETHI results in relocation other than R_SPARC_HI22\n%s\n", buffer);
+				exit(1);
+			}
+			break;
+		case 'i':
+			if (initval) {
+				fprintf(stderr, "Cannot use pre-initalized fixups for INT\n%s\n", buffer);
+				exit(1);
+			}
+			if (strncmp (buffer + mode+9, "HI22      ", 10) && strncmp (buffer + mode+9, "LO10      ", 10)) {
+				fprintf(stderr, "BTFIXUP_INT results in relocation other than R_SPARC_HI22 and R_SPARC_LO10\n%s\n", buffer);
+				exit(1);
+			}
+			break;
+		}
+		if (!f->setinitval) {
+			f->initval = initval;
+			if (initvalstr) {
+				f->initvalstr = strdup(initvalstr);
+				if (!f->initvalstr) fatal();
+			}
+			f->setinitval = 1;
+		} else if (f->initval != initval) {
+			fprintf(stderr, "Btfixup %s previously used with initializer %s which doesn't match with current initializer\n%s\n",
+					f->name, f->initvalstr ? : "0x00000000", buffer);
+			exit(1);
+		} else if (initval && strcmp(f->initvalstr, initvalstr)) {
+			fprintf(stderr, "Btfixup %s previously used with initializer %s which doesn't match with current initializer.\n"
+					"Initializers have to match literally as well.\n%s\n",
+					f->name, f->initvalstr, buffer);
+			exit(1);
+		}
+		offset = strtoul(buffer, &q, 16);
+		if (q != buffer + mode || (!offset && (mode == 8 ? strncmp (buffer, "00000000 ", 9) : strncmp (buffer, "0000000000000000 ", 17)))) {
+			fprintf(stderr, "Malformed relocation address in\n%s\n", buffer);
+			exit(1);
+		}
+		for (k = 0, r = f->rel, rr = &f->rel; r; rr = &r->next, r = r->next, k++)
+			if (r->offset == offset && !strcmp(r->sect, sect)) {
+				fprintf(stderr, "Ugh. One address has two relocation records\n");
+				exit(1);
+			}
+		*rr = malloc(sizeof(btfixuprel));
+		if (!*rr) fatal();
+		(*rr)->offset = offset;
+		(*rr)->f = NULL;
+		if (buffer[nbase+3] == 'f') {
+			lastf = f;
+			lastfoffset = offset;
+			lastfrelno = k;
+		} else if (lastfoffset + 4 == offset) {
+			(*rr)->f = lastf;
+			(*rr)->frel = lastfrelno;
+		}
+		(*rr)->sect = sect;
+		(*rr)->next = NULL;
+	}
+	printf("! Generated by btfixupprep. Do not edit.\n\n");
+	printf("\t.section\t\".data.init\",#alloc,#write\n\t.align\t4\n\n");
+	printf("\t.global\t___btfixup_start\n___btfixup_start:\n\n");
+	for (i = 0; i < last; i++) {
+		f = array + i;
+		printf("\t.global\t___%cs_%s\n", f->type, f->name);
+		if (f->type == 'f')
+			printf("___%cs_%s:\n\t.word 0x%08x,0,0,", f->type, f->name, f->type << 24);
+		else
+			printf("___%cs_%s:\n\t.word 0x%08x,0,", f->type, f->name, f->type << 24);
+		for (j = 0, r = f->rel; r != NULL; j++, r = r->next);
+		if (j)
+			printf("%d\n\t.word\t", j * 2);
+		else
+			printf("0\n");
+		for (r = f->rel, j--; r != NULL; j--, r = r->next) {
+			if (!strcmp (r->sect, ".text"))
+				printf ("_stext+0x%08lx", r->offset);
+			else if (!strcmp (r->sect, ".init.text"))
+				printf ("__init_begin+0x%08lx", r->offset);
+			else if (!strcmp (r->sect, "__ksymtab"))
+				printf ("__start___ksymtab+0x%08lx", r->offset);
+			else if (!strcmp (r->sect, ".fixup"))
+				printf ("__start___fixup+0x%08lx", r->offset);
+			else
+				fatal();
+			if (f->type == 'f' || !r->f)
+				printf (",0");
+			else
+				printf (",___fs_%s+0x%08x", r->f->name, (4 + r->frel*2)*4 + 4);
+			if (j) printf (",");
+			else printf ("\n");
+		}
+		printf("\n");
+	}
+	printf("\n\t.global\t___btfixup_end\n___btfixup_end:\n");
+	printf("\n\n! Define undefined references\n\n");
+	for (i = 0; i < last; i++) {
+		f = array + i;
+		if (f->type == 'f') {
+			printf("\t.global\t___f_%s\n", f->name);
+			printf("___f_%s:\n", f->name);
+		}
+	}
+	printf("\tretl\n\t nop\n\n");
+	for (i = 0; i < last; i++) {
+		f = array + i;
+		if (f->type != 'f') {
+			if (!f->initval) {
+				printf("\t.global\t___%c_%s\n", f->type, f->name);
+				printf("___%c_%s = 0\n", f->type, f->name);
+			} else {
+				printf("\t.global\t___%c_%s__btset_0x%s\n", f->type, f->name, f->initvalstr);
+				printf("___%c_%s__btset_0x%s = 0x%08x\n", f->type, f->name, f->initvalstr, f->initval);
+			}
+		}
+	}
+	printf("\n\n");
+    	exit(0);
+}
diff --git a/arch/sparc/boot/piggyback.c b/arch/sparc/boot/piggyback.c
new file mode 100644
index 0000000..6962cc6
--- /dev/null
+++ b/arch/sparc/boot/piggyback.c
@@ -0,0 +1,137 @@
+/* $Id: piggyback.c,v 1.4 2000/12/05 00:48:57 anton Exp $
+   Simple utility to make a single-image install kernel with initial ramdisk
+   for Sparc tftpbooting without need to set up nfs.
+
+   Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+   Pete Zaitcev <zaitcev@yahoo.com> endian fixes for cross-compiles, 2000.
+
+   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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+   
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/*
+ * Note: run this on an a.out kernel (use elftoaout for it),
+ * as PROM looks for a.out image only.
+ */
+
+unsigned short ld2(char *p)
+{
+	return (p[0] << 8) | p[1];
+}
+
+unsigned int ld4(char *p)
+{
+	return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+}
+
+void st4(char *p, unsigned int x)
+{
+	p[0] = x >> 24;
+	p[1] = x >> 16;
+	p[2] = x >> 8;
+	p[3] = x;
+}
+
+void usage(void)
+{
+	/* fs_img.gz is an image of initial ramdisk. */
+	fprintf(stderr, "Usage: piggyback vmlinux.aout System.map fs_img.gz\n");
+	fprintf(stderr, "\tKernel image will be modified in place.\n");
+	exit(1);
+}
+
+void die(char *str)
+{
+	perror (str);
+	exit(1);
+}
+
+int main(int argc,char **argv)
+{
+	static char aout_magic[] = { 0x01, 0x03, 0x01, 0x07 };
+	unsigned char buffer[1024], *q, *r;
+	unsigned int i, j, k, start, end, offset;
+	FILE *map;
+	struct stat s;
+	int image, tail;
+
+	if (argc != 4) usage();
+	start = end = 0;
+	if (stat (argv[3], &s) < 0) die (argv[3]);
+	map = fopen (argv[2], "r");
+	if (!map) die(argv[2]);
+	while (fgets (buffer, 1024, map)) {
+		if (!strcmp (buffer + 8, " T start\n") || !strcmp (buffer + 16, " T start\n"))
+			start = strtoul (buffer, NULL, 16);
+		else if (!strcmp (buffer + 8, " A end\n") || !strcmp (buffer + 16, " A end\n"))
+			end = strtoul (buffer, NULL, 16);
+	}
+	fclose (map);
+	if (!start || !end) {
+		fprintf (stderr, "Could not determine start and end from System.map\n");
+		exit(1);
+	}
+	if ((image = open(argv[1],O_RDWR)) < 0) die(argv[1]);
+	if (read(image,buffer,512) != 512) die(argv[1]);
+	if (memcmp (buffer, "\177ELF", 4) == 0) {
+		q = buffer + ld4(buffer + 28);
+		i = ld4(q + 4) + ld4(buffer + 24) - ld4(q + 8);
+		if (lseek(image,i,0) < 0) die("lseek");
+		if (read(image,buffer,512) != 512) die(argv[1]);
+		j = 0;
+	} else if (memcmp(buffer, aout_magic, 4) == 0) {
+		i = j = 32;
+	} else {
+		fprintf (stderr, "Not ELF nor a.out. Don't blame me.\n");
+		exit(1);
+	}
+	k = i;
+	i += (ld2(buffer + j + 2)<<2) - 512;
+	if (lseek(image,i,0) < 0) die("lseek");
+	if (read(image,buffer,1024) != 1024) die(argv[1]);
+	for (q = buffer, r = q + 512; q < r; q += 4) {
+		if (*q == 'H' && q[1] == 'd' && q[2] == 'r' && q[3] == 'S')
+			break;
+	}
+	if (q == r) {
+		fprintf (stderr, "Couldn't find headers signature in the kernel.\n");
+		exit(1);
+	}
+	offset = i + (q - buffer) + 10;
+	if (lseek(image, offset, 0) < 0) die ("lseek");
+
+	st4(buffer, 0);
+	st4(buffer + 4, 0x01000000);
+	st4(buffer + 8, (end + 32 + 4095) & ~4095);
+	st4(buffer + 12, s.st_size);
+
+	if (write(image,buffer+2,14) != 14) die (argv[1]);
+	if (lseek(image, k - start + ((end + 32 + 4095) & ~4095), 0) < 0) die ("lseek");
+	if ((tail = open(argv[3],O_RDONLY)) < 0) die(argv[3]);
+	while ((i = read (tail,buffer,1024)) > 0)
+		if (write(image,buffer,i) != i) die (argv[1]);
+	if (close(image) < 0) die("close");
+	if (close(tail) < 0) die("close");
+    	return 0;
+}
diff --git a/arch/sparc/defconfig b/arch/sparc/defconfig
new file mode 100644
index 0000000..a698562
--- /dev/null
+++ b/arch/sparc/defconfig
@@ -0,0 +1,644 @@
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_MMU=y
+CONFIG_UID16=y
+CONFIG_HIGHMEM=y
+CONFIG_GENERIC_ISA_DMA=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_STANDALONE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+# CONFIG_AUDIT is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_HOTPLUG is not set
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# General setup
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_SMP is not set
+CONFIG_SPARC32=y
+CONFIG_SBUS=y
+CONFIG_SBUSCHAR=y
+CONFIG_SERIAL_CONSOLE=y
+CONFIG_SUN_AUXIO=y
+CONFIG_SUN_IO=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_SUN_PM=y
+# CONFIG_SUN4 is not set
+CONFIG_PCI=y
+# CONFIG_PCI_LEGACY_PROC is not set
+# CONFIG_PCI_NAMES is not set
+CONFIG_SUN_OPENPROMFS=m
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=y
+CONFIG_BINFMT_MISC=m
+CONFIG_SUNOS_EMUL=y
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Generic Driver Options
+#
+# CONFIG_DEBUG_DRIVER is not set
+
+#
+# Graphics support
+#
+# CONFIG_FB is not set
+
+#
+# Console display driver support
+#
+# CONFIG_MDA_CONSOLE is not set
+# CONFIG_PROM_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_SUNCORE=y
+CONFIG_SERIAL_SUNZILOG=y
+CONFIG_SERIAL_SUNZILOG_CONSOLE=y
+CONFIG_SERIAL_SUNSU=y
+CONFIG_SERIAL_SUNSU_CONSOLE=y
+# CONFIG_SERIAL_SUNSAB is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+
+#
+# Misc Linux/SPARC drivers
+#
+CONFIG_SUN_OPENPROMIO=m
+CONFIG_SUN_MOSTEK_RTC=m
+# CONFIG_SUN_BPP is not set
+# CONFIG_SUN_VIDEOPIX is not set
+# CONFIG_SUN_AURORA is not set
+# CONFIG_TADPOLE_TS102_UCTRL is not set
+# CONFIG_SUN_JSFLASH is not set
+CONFIG_APM_RTC_IS_GMT=y
+CONFIG_RTC=m
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_UMEM is not set
+CONFIG_BLK_DEV_LOOP=m
+CONFIG_BLK_DEV_CRYPTOLOOP=m
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_CARMEL is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# SCSI device support
+#
+CONFIG_SCSI=y
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=m
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_CHR_DEV_SG=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI Transport Attributes
+#
+CONFIG_SCSI_SPI_ATTRS=m
+# CONFIG_SCSI_FC_ATTRS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AACRAID is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_DPT_I2O is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_MEGARAID is not set
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_IPS is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_SYM53C8XX_2 is not set
+# CONFIG_SCSI_IPR is not set
+# CONFIG_SCSI_QLOGIC_ISP is not set
+# CONFIG_SCSI_QLOGIC_FC is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
+CONFIG_SCSI_QLOGICPTI=m
+CONFIG_SCSI_QLA2XXX=y
+# CONFIG_SCSI_QLA21XX is not set
+# CONFIG_SCSI_QLA22XX is not set
+# CONFIG_SCSI_QLA2300 is not set
+# CONFIG_SCSI_QLA2322 is not set
+# CONFIG_SCSI_QLA6312 is not set
+# CONFIG_SCSI_QLA6322 is not set
+# CONFIG_SCSI_DC395x is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_NSP32 is not set
+# CONFIG_SCSI_DEBUG is not set
+CONFIG_SCSI_SUNESP=y
+
+#
+# Fibre Channel support
+#
+# CONFIG_FC4 is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_NETLINK_DEV=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=m
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+CONFIG_INET_AH=y
+CONFIG_INET_ESP=y
+CONFIG_INET_IPCOMP=y
+CONFIG_IPV6=m
+CONFIG_IPV6_PRIVACY=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_IPV6_TUNNEL=m
+# CONFIG_NETFILTER is not set
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=m
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+CONFIG_IP_SCTP=m
+# CONFIG_SCTP_DBG_MSG is not set
+CONFIG_SCTP_DBG_OBJCNT=y
+# CONFIG_SCTP_HMAC_NONE is not set
+# CONFIG_SCTP_HMAC_SHA1 is not set
+CONFIG_SCTP_HMAC_MD5=y
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_NET_DIVERT is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+CONFIG_NET_PKTGEN=m
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=m
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_TUN=m
+# CONFIG_ETHERTAP is not set
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=m
+CONFIG_SUNLANCE=y
+CONFIG_HAPPYMEAL=m
+CONFIG_SUNBMAC=m
+CONFIG_SUNQE=m
+# CONFIG_SUNGEM is not set
+# CONFIG_NET_VENDOR_3COM is not set
+
+#
+# Tulip family network device support
+#
+# CONFIG_NET_TULIP is not set
+# CONFIG_HP100 is not set
+# CONFIG_NET_PCI is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_ACENIC is not set
+# CONFIG_DL2K is not set
+# CONFIG_E1000 is not set
+# CONFIG_MYRI_SBUS is not set
+# CONFIG_NS83820 is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
+# CONFIG_SK98LIN is not set
+# CONFIG_TIGON3 is not set
+
+#
+# Ethernet (10000 Mbit)
+#
+# CONFIG_IXGB is not set
+# CONFIG_S2IO is not set
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NET_FC is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
+
+#
+# Unix98 PTY support
+#
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=256
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_PSAUX=y
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+CONFIG_INPUT_JOYDEV=m
+# CONFIG_INPUT_TSDEV is not set
+CONFIG_INPUT_EVDEV=m
+CONFIG_INPUT_EVBUG=m
+
+#
+# Input I/O drivers
+#
+# CONFIG_GAMEPORT is not set
+CONFIG_SOUND_GAMEPORT=y
+CONFIG_SERIO=m
+# CONFIG_SERIO_I8042 is not set
+CONFIG_SERIO_SERPORT=m
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_PCIPS2 is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+CONFIG_KEYBOARD_ATKBD=m
+CONFIG_KEYBOARD_SUNKBD=m
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+CONFIG_INPUT_MOUSE=y
+CONFIG_MOUSE_PS2=m
+CONFIG_MOUSE_SERIAL=m
+# CONFIG_MOUSE_VSXXXAA is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+# CONFIG_EXT3_FS is not set
+# CONFIG_JBD is not set
+CONFIG_FS_MBCACHE=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+CONFIG_XFS_FS=m
+CONFIG_XFS_RT=y
+CONFIG_XFS_QUOTA=y
+CONFIG_XFS_SECURITY=y
+CONFIG_XFS_POSIX_ACL=y
+# CONFIG_MINIX_FS is not set
+CONFIG_ROMFS_FS=m
+# CONFIG_QUOTA is not set
+CONFIG_QUOTACTL=y
+CONFIG_AUTOFS_FS=m
+CONFIG_AUTOFS4_FS=m
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=m
+# CONFIG_JOLIET is not set
+# CONFIG_ZISOFS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_FAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_SYSFS=y
+# CONFIG_DEVFS_FS is not set
+CONFIG_DEVPTS_FS_XATTR=y
+# CONFIG_DEVPTS_FS_SECURITY is not set
+# CONFIG_TMPFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+CONFIG_BEFS_FS=m
+# CONFIG_BEFS_DEBUG is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=y
+# CONFIG_NFS_V3 is not set
+# CONFIG_NFS_V4 is not set
+# CONFIG_NFS_DIRECTIO is not set
+# CONFIG_NFSD is not set
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+# CONFIG_EXPORTFS is not set
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=m
+CONFIG_RPCSEC_GSS_KRB5=m
+# CONFIG_SMB_FS is not set
+CONFIG_CIFS=m
+# CONFIG_CIFS_STATS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+CONFIG_AFS_FS=m
+CONFIG_RXRPC=m
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+CONFIG_SUN_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_DEBUG_SLAB is not set
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_HIGHMEM is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+
+#
+# Security options
+#
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=y
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=y
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_DEFLATE=y
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Library routines
+#
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile
new file mode 100644
index 0000000..3d22ba2
--- /dev/null
+++ b/arch/sparc/kernel/Makefile
@@ -0,0 +1,27 @@
+# $Id: Makefile,v 1.62 2000/12/15 00:41:17 davem Exp $
+# Makefile for the linux kernel.
+#
+
+extra-y		:= head.o init_task.o vmlinux.lds
+
+EXTRA_AFLAGS	:= -ansi
+
+IRQ_OBJS := irq.o sun4m_irq.o sun4c_irq.o sun4d_irq.o
+obj-y    := entry.o wof.o wuf.o etrap.o rtrap.o traps.o $(IRQ_OBJS) \
+	    process.o signal.o ioport.o setup.o idprom.o \
+	    sys_sparc.o sunos_asm.o systbls.o \
+	    time.o windows.o cpu.o devices.o sclow.o \
+	    tadpole.o tick14.o ptrace.o sys_solaris.o \
+	    unaligned.o muldiv.o semaphore.o
+
+obj-$(CONFIG_PCI) += pcic.o
+obj-$(CONFIG_SUN4) += sun4setup.o
+obj-$(CONFIG_SMP) += trampoline.o smp.o sun4m_smp.o sun4d_smp.o
+obj-$(CONFIG_SUN_AUXIO) += auxio.o
+obj-$(CONFIG_PCI) += ebus.o
+obj-$(CONFIG_SUN_PM) += apc.o pmc.o
+obj-$(CONFIG_MODULES) += module.o sparc_ksyms.o
+
+ifdef CONFIG_SUNOS_EMUL
+obj-y += sys_sunos.o sunos_ioctl.o
+endif
diff --git a/arch/sparc/kernel/apc.c b/arch/sparc/kernel/apc.c
new file mode 100644
index 0000000..406dd94
--- /dev/null
+++ b/arch/sparc/kernel/apc.c
@@ -0,0 +1,186 @@
+/* apc - Driver implementation for power management functions
+ * of Aurora Personality Chip (APC) on SPARCstation-4/5 and
+ * derivatives.
+ *
+ * Copyright (c) 2002 Eric Brower (ebrower@usa.net)
+ */
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/pm.h>
+
+#include <asm/io.h>
+#include <asm/sbus.h>
+#include <asm/oplib.h>
+#include <asm/uaccess.h>
+#include <asm/auxio.h>
+#include <asm/apc.h>
+
+/* Debugging
+ * 
+ * #define APC_DEBUG_LED
+ */
+
+#define APC_MINOR	MISC_DYNAMIC_MINOR
+#define APC_OBPNAME	"power-management"
+#define APC_DEVNAME "apc"
+
+volatile static u8 __iomem *regs; 
+static int apc_regsize;
+static int apc_no_idle __initdata = 0;
+
+#define apc_readb(offs)			(sbus_readb(regs+offs))
+#define apc_writeb(val, offs) 	(sbus_writeb(val, regs+offs))
+
+/* Specify "apc=noidle" on the kernel command line to 
+ * disable APC CPU standby support.  Certain prototype
+ * systems (SPARCstation-Fox) do not play well with APC
+ * CPU idle, so disable this if your system has APC and 
+ * crashes randomly.
+ */
+static int __init apc_setup(char *str) 
+{
+	if(!strncmp(str, "noidle", strlen("noidle"))) {
+		apc_no_idle = 1;
+		return 1;
+	}
+	return 0;
+}
+__setup("apc=", apc_setup);
+
+/* 
+ * CPU idle callback function
+ * See .../arch/sparc/kernel/process.c
+ */
+void apc_swift_idle(void)
+{
+#ifdef APC_DEBUG_LED
+	set_auxio(0x00, AUXIO_LED); 
+#endif
+
+	apc_writeb(apc_readb(APC_IDLE_REG) | APC_IDLE_ON, APC_IDLE_REG);
+
+#ifdef APC_DEBUG_LED
+	set_auxio(AUXIO_LED, 0x00); 
+#endif
+} 
+
+static inline void apc_free(void)
+{
+	sbus_iounmap(regs, apc_regsize);
+}
+
+static int apc_open(struct inode *inode, struct file *f)
+{
+	return 0;
+}
+
+static int apc_release(struct inode *inode, struct file *f)
+{
+	return 0;
+}
+
+static int apc_ioctl(struct inode *inode, struct file *f, 
+		     unsigned int cmd, unsigned long __arg)
+{
+	__u8 inarg, __user *arg;
+
+	arg = (__u8 __user *) __arg;
+	switch (cmd) {
+	case APCIOCGFANCTL:
+		if (put_user(apc_readb(APC_FANCTL_REG) & APC_REGMASK, arg))
+				return -EFAULT;
+		break;
+
+	case APCIOCGCPWR:
+		if (put_user(apc_readb(APC_CPOWER_REG) & APC_REGMASK, arg))
+			return -EFAULT;
+		break;
+
+	case APCIOCGBPORT:
+		if (put_user(apc_readb(APC_BPORT_REG) & APC_BPMASK, arg))
+			return -EFAULT;
+		break;
+
+	case APCIOCSFANCTL:
+		if (get_user(inarg, arg))
+			return -EFAULT;
+		apc_writeb(inarg & APC_REGMASK, APC_FANCTL_REG);
+		break;
+	case APCIOCSCPWR:
+		if (get_user(inarg, arg))
+			return -EFAULT;
+		apc_writeb(inarg & APC_REGMASK, APC_CPOWER_REG);
+		break;
+	case APCIOCSBPORT:
+		if (get_user(inarg, arg))
+			return -EFAULT;
+		apc_writeb(inarg & APC_BPMASK, APC_BPORT_REG);
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	return 0;
+}
+
+static struct file_operations apc_fops = {
+	.ioctl =	apc_ioctl,
+	.open =		apc_open,
+	.release =	apc_release,
+};
+
+static struct miscdevice apc_miscdev = { APC_MINOR, APC_DEVNAME, &apc_fops };
+
+static int __init apc_probe(void)
+{
+	struct sbus_bus *sbus = NULL;
+	struct sbus_dev *sdev = NULL;
+	int iTmp = 0;
+
+	for_each_sbus(sbus) {
+		for_each_sbusdev(sdev, sbus) {
+			if (!strcmp(sdev->prom_name, APC_OBPNAME)) {
+				goto sbus_done;
+			}
+		}
+	}
+
+sbus_done:
+	if (!sdev) {
+		return -ENODEV;
+	}
+
+	apc_regsize = sdev->reg_addrs[0].reg_size;
+	regs = sbus_ioremap(&sdev->resource[0], 0, 
+				   apc_regsize, APC_OBPNAME);
+	if(!regs) {
+		printk(KERN_ERR "%s: unable to map registers\n", APC_DEVNAME);
+		return -ENODEV;
+	}
+
+	iTmp = misc_register(&apc_miscdev);
+	if (iTmp != 0) {
+		printk(KERN_ERR "%s: unable to register device\n", APC_DEVNAME);
+		apc_free();
+		return -ENODEV;
+	}
+
+	/* Assign power management IDLE handler */
+	if(!apc_no_idle)
+		pm_idle = apc_swift_idle;	
+
+	printk(KERN_INFO "%s: power management initialized%s\n", 
+		APC_DEVNAME, apc_no_idle ? " (CPU idle disabled)" : "");
+	return 0;
+}
+
+/* This driver is not critical to the boot process
+ * and is easiest to ioremap when SBus is already
+ * initialized, so we install ourselves thusly:
+ */
+__initcall(apc_probe);
+
diff --git a/arch/sparc/kernel/asm-offsets.c b/arch/sparc/kernel/asm-offsets.c
new file mode 100644
index 0000000..1f55231
--- /dev/null
+++ b/arch/sparc/kernel/asm-offsets.c
@@ -0,0 +1,45 @@
+/*
+ * This program is used to generate definitions needed by
+ * assembly language modules.
+ *
+ * We use the technique used in the OSF Mach kernel code:
+ * generate asm statements containing #defines,
+ * compile this file to assembler, and then extract the
+ * #defines from the assembly-language output.
+ *
+ * On sparc, thread_info data is static and TI_XXX offsets are computed by hand.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+// #include <linux/mm.h>
+
+#define DEFINE(sym, val) \
+	asm volatile("\n->" #sym " %0 " #val : : "i" (val))
+
+#define BLANK() asm volatile("\n->" : : )
+
+int foo(void)
+{
+	DEFINE(AOFF_task_thread, offsetof(struct task_struct, thread));
+	BLANK();
+	/* XXX This is the stuff for sclow.S, kill it. */
+	DEFINE(AOFF_task_pid, offsetof(struct task_struct, pid));
+	DEFINE(AOFF_task_uid, offsetof(struct task_struct, uid));
+	DEFINE(AOFF_task_gid, offsetof(struct task_struct, gid));
+	DEFINE(AOFF_task_euid, offsetof(struct task_struct, euid));
+	DEFINE(AOFF_task_egid, offsetof(struct task_struct, egid));
+	/* DEFINE(THREAD_INFO, offsetof(struct task_struct, thread_info)); */
+	DEFINE(ASIZ_task_uid,	sizeof(current->uid));
+	DEFINE(ASIZ_task_gid,	sizeof(current->gid));
+	DEFINE(ASIZ_task_euid,	sizeof(current->euid));
+	DEFINE(ASIZ_task_egid,	sizeof(current->egid));
+	BLANK();
+	DEFINE(AOFF_thread_fork_kpsr,
+			offsetof(struct thread_struct, fork_kpsr));
+	BLANK();
+	DEFINE(AOFF_mm_context, offsetof(struct mm_struct, context));
+
+	/* DEFINE(NUM_USER_SEGMENTS, TASK_SIZE>>28); */
+	return 0;
+}
diff --git a/arch/sparc/kernel/auxio.c b/arch/sparc/kernel/auxio.c
new file mode 100644
index 0000000..d3b3648
--- /dev/null
+++ b/arch/sparc/kernel/auxio.c
@@ -0,0 +1,138 @@
+/* auxio.c: Probing for the Sparc AUXIO register at boot time.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/spinlock.h>
+#include <asm/oplib.h>
+#include <asm/io.h>
+#include <asm/auxio.h>
+#include <asm/string.h>		/* memset(), Linux has no bzero() */
+
+/* Probe and map in the Auxiliary I/O register */
+
+/* auxio_register is not static because it is referenced 
+ * in entry.S::floppy_tdone
+ */
+void __iomem *auxio_register = NULL;
+static DEFINE_SPINLOCK(auxio_lock);
+
+void __init auxio_probe(void)
+{
+	int node, auxio_nd;
+	struct linux_prom_registers auxregs[1];
+	struct resource r;
+
+	switch (sparc_cpu_model) {
+	case sun4d:
+	case sun4:
+		return;
+	default:
+		break;
+	}
+	node = prom_getchild(prom_root_node);
+	auxio_nd = prom_searchsiblings(node, "auxiliary-io");
+	if(!auxio_nd) {
+		node = prom_searchsiblings(node, "obio");
+		node = prom_getchild(node);
+		auxio_nd = prom_searchsiblings(node, "auxio");
+		if(!auxio_nd) {
+#ifdef CONFIG_PCI
+			/* There may be auxio on Ebus */
+			return;
+#else
+			if(prom_searchsiblings(node, "leds")) {
+				/* VME chassis sun4m machine, no auxio exists. */
+				return;
+			}
+			prom_printf("Cannot find auxio node, cannot continue...\n");
+			prom_halt();
+#endif
+		}
+	}
+	if(prom_getproperty(auxio_nd, "reg", (char *) auxregs, sizeof(auxregs)) <= 0)
+		return;
+	prom_apply_obio_ranges(auxregs, 0x1);
+	/* Map the register both read and write */
+	r.flags = auxregs[0].which_io & 0xF;
+	r.start = auxregs[0].phys_addr;
+	r.end = auxregs[0].phys_addr + auxregs[0].reg_size - 1;
+	auxio_register = sbus_ioremap(&r, 0, auxregs[0].reg_size, "auxio");
+	/* Fix the address on sun4m and sun4c. */
+	if((((unsigned long) auxregs[0].phys_addr) & 3) == 3 ||
+	   sparc_cpu_model == sun4c)
+		auxio_register += (3 - ((unsigned long)auxio_register & 3));
+
+	set_auxio(AUXIO_LED, 0);
+}
+
+unsigned char get_auxio(void)
+{
+	if(auxio_register) 
+		return sbus_readb(auxio_register);
+	return 0;
+}
+
+void set_auxio(unsigned char bits_on, unsigned char bits_off)
+{
+	unsigned char regval;
+	unsigned long flags;
+	spin_lock_irqsave(&auxio_lock, flags);
+	switch(sparc_cpu_model) {
+	case sun4c:
+		regval = sbus_readb(auxio_register);
+		sbus_writeb(((regval | bits_on) & ~bits_off) | AUXIO_ORMEIN,
+			auxio_register);
+		break;
+	case sun4m:
+		if(!auxio_register)
+			break;     /* VME chassic sun4m, no auxio. */
+		regval = sbus_readb(auxio_register);
+		sbus_writeb(((regval | bits_on) & ~bits_off) | AUXIO_ORMEIN4M,
+			auxio_register);
+		break;
+	case sun4d:
+		break;
+	default:
+		panic("Can't set AUXIO register on this machine.");
+	};
+	spin_unlock_irqrestore(&auxio_lock, flags);
+}
+
+
+/* sun4m power control register (AUXIO2) */
+
+volatile unsigned char * auxio_power_register = NULL;
+
+void __init auxio_power_probe(void)
+{
+	struct linux_prom_registers regs;
+	int node;
+	struct resource r;
+
+	/* Attempt to find the sun4m power control node. */
+	node = prom_getchild(prom_root_node);
+	node = prom_searchsiblings(node, "obio");
+	node = prom_getchild(node);
+	node = prom_searchsiblings(node, "power");
+	if (node == 0 || node == -1)
+		return;
+
+	/* Map the power control register. */
+	if (prom_getproperty(node, "reg", (char *)&regs, sizeof(regs)) <= 0)
+		return;
+	prom_apply_obio_ranges(&regs, 1);
+	memset(&r, 0, sizeof(r));
+	r.flags = regs.which_io & 0xF;
+	r.start = regs.phys_addr;
+	r.end = regs.phys_addr + regs.reg_size - 1;
+	auxio_power_register = (unsigned char *) sbus_ioremap(&r, 0,
+	    regs.reg_size, "auxpower");
+
+	/* Display a quick message on the console. */
+	if (auxio_power_register)
+		printk(KERN_INFO "Power off control detected.\n");
+}
diff --git a/arch/sparc/kernel/cpu.c b/arch/sparc/kernel/cpu.c
new file mode 100644
index 0000000..6a4ebc6
--- /dev/null
+++ b/arch/sparc/kernel/cpu.c
@@ -0,0 +1,168 @@
+/* cpu.c: Dinky routines to look for the kind of Sparc cpu
+ *        we are on.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/threads.h>
+#include <asm/oplib.h>
+#include <asm/page.h>
+#include <asm/head.h>
+#include <asm/psr.h>
+#include <asm/mbus.h>
+#include <asm/cpudata.h>
+
+DEFINE_PER_CPU(cpuinfo_sparc, __cpu_data) = { 0 };
+
+struct cpu_iu_info {
+  int psr_impl;
+  int psr_vers;
+  char* cpu_name;   /* should be enough I hope... */
+};
+
+struct cpu_fp_info {
+  int psr_impl;
+  int fp_vers;
+  char* fp_name;
+};
+
+/* In order to get the fpu type correct, you need to take the IDPROM's
+ * machine type value into consideration too.  I will fix this.
+ */
+struct cpu_fp_info linux_sparc_fpu[] = {
+  { 0, 0, "Fujitsu MB86910 or Weitek WTL1164/5"},
+  { 0, 1, "Fujitsu MB86911 or Weitek WTL1164/5 or LSI L64831"},
+  { 0, 2, "LSI Logic L64802 or Texas Instruments ACT8847"},
+  /* SparcStation SLC, SparcStation1 */
+  { 0, 3, "Weitek WTL3170/2"},
+  /* SPARCstation-5 */
+  { 0, 4, "Lsi Logic/Meiko L64804 or compatible"},
+  { 0, 5, "reserved"},
+  { 0, 6, "reserved"},
+  { 0, 7, "No FPU"},
+  { 1, 0, "ROSS HyperSparc combined IU/FPU"},
+  { 1, 1, "Lsi Logic L64814"},
+  { 1, 2, "Texas Instruments TMS390-C602A"},
+  { 1, 3, "Cypress CY7C602 FPU"},
+  { 1, 4, "reserved"},
+  { 1, 5, "reserved"},
+  { 1, 6, "reserved"},
+  { 1, 7, "No FPU"},
+  { 2, 0, "BIT B5010 or B5110/20 or B5210"},
+  { 2, 1, "reserved"},
+  { 2, 2, "reserved"},
+  { 2, 3, "reserved"},
+  { 2, 4, "reserved"},
+  { 2, 5, "reserved"},
+  { 2, 6, "reserved"},
+  { 2, 7, "No FPU"},
+  /* SuperSparc 50 module */
+  { 4, 0, "SuperSparc on-chip FPU"},
+  /* SparcClassic */
+  { 4, 4, "TI MicroSparc on chip FPU"},
+  { 5, 0, "Matsushita MN10501"},
+  { 5, 1, "reserved"},
+  { 5, 2, "reserved"},
+  { 5, 3, "reserved"},
+  { 5, 4, "reserved"},
+  { 5, 5, "reserved"},
+  { 5, 6, "reserved"},
+  { 5, 7, "No FPU"},
+  { 9, 3, "Fujitsu or Weitek on-chip FPU"},
+};
+
+#define NSPARCFPU  (sizeof(linux_sparc_fpu)/sizeof(struct cpu_fp_info))
+
+struct cpu_iu_info linux_sparc_chips[] = {
+  /* Sun4/100, 4/200, SLC */
+  { 0, 0, "Fujitsu  MB86900/1A or LSI L64831 SparcKIT-40"},
+  /* borned STP1012PGA */
+  { 0, 4, "Fujitsu  MB86904"},
+  { 0, 5, "Fujitsu TurboSparc MB86907"},
+  /* SparcStation2, SparcServer 490 & 690 */
+  { 1, 0, "LSI Logic Corporation - L64811"},
+  /* SparcStation2 */
+  { 1, 1, "Cypress/ROSS CY7C601"},
+  /* Embedded controller */
+  { 1, 3, "Cypress/ROSS CY7C611"},
+  /* Ross Technologies HyperSparc */
+  { 1, 0xf, "ROSS HyperSparc RT620"},
+  { 1, 0xe, "ROSS HyperSparc RT625 or RT626"},
+  /* ECL Implementation, CRAY S-MP Supercomputer... AIEEE! */
+  /* Someone please write the code to support this beast! ;) */
+  { 2, 0, "Bipolar Integrated Technology - B5010"},
+  { 3, 0, "LSI Logic Corporation - unknown-type"},
+  { 4, 0, "Texas Instruments, Inc. - SuperSparc-(II)"},
+  /* SparcClassic  --  borned STP1010TAB-50*/
+  { 4, 1, "Texas Instruments, Inc. - MicroSparc"},
+  { 4, 2, "Texas Instruments, Inc. - MicroSparc II"},
+  { 4, 3, "Texas Instruments, Inc. - SuperSparc 51"},
+  { 4, 4, "Texas Instruments, Inc. - SuperSparc 61"},
+  { 4, 5, "Texas Instruments, Inc. - unknown"},
+  { 5, 0, "Matsushita - MN10501"},
+  { 6, 0, "Philips Corporation - unknown"},
+  { 7, 0, "Harvest VLSI Design Center, Inc. - unknown"},
+  /* Gallium arsenide 200MHz, BOOOOGOOOOMIPS!!! */
+  { 8, 0, "Systems and Processes Engineering Corporation (SPEC)"},
+  { 9, 0, "Fujitsu or Weitek Power-UP"},
+  { 9, 1, "Fujitsu or Weitek Power-UP"},
+  { 9, 2, "Fujitsu or Weitek Power-UP"},
+  { 9, 3, "Fujitsu or Weitek Power-UP"},
+  { 0xa, 0, "UNKNOWN CPU-VENDOR/TYPE"},
+  { 0xb, 0, "UNKNOWN CPU-VENDOR/TYPE"},
+  { 0xc, 0, "UNKNOWN CPU-VENDOR/TYPE"},
+  { 0xd, 0, "UNKNOWN CPU-VENDOR/TYPE"},
+  { 0xe, 0, "UNKNOWN CPU-VENDOR/TYPE"},
+  { 0xf, 0, "UNKNOWN CPU-VENDOR/TYPE"},
+};
+
+#define NSPARCCHIPS  (sizeof(linux_sparc_chips)/sizeof(struct cpu_iu_info))
+
+char *sparc_cpu_type;
+char *sparc_fpu_type;
+
+unsigned int fsr_storage;
+
+void __init cpu_probe(void)
+{
+	int psr_impl, psr_vers, fpu_vers;
+	int i, psr;
+
+	psr_impl = ((get_psr()>>28)&0xf);
+	psr_vers = ((get_psr()>>24)&0xf);
+
+	psr = get_psr();
+	put_psr(psr | PSR_EF);
+	fpu_vers = ((get_fsr()>>17)&0x7);
+	put_psr(psr);
+
+	for(i = 0; i<NSPARCCHIPS; i++) {
+		if(linux_sparc_chips[i].psr_impl == psr_impl)
+			if(linux_sparc_chips[i].psr_vers == psr_vers) {
+				sparc_cpu_type = linux_sparc_chips[i].cpu_name;
+				break;
+			}
+	}
+
+	if(i==NSPARCCHIPS)
+		printk("DEBUG: psr.impl = 0x%x   psr.vers = 0x%x\n", psr_impl, 
+			    psr_vers);
+
+	for(i = 0; i<NSPARCFPU; i++) {
+		if(linux_sparc_fpu[i].psr_impl == psr_impl)
+			if(linux_sparc_fpu[i].fp_vers == fpu_vers) {
+				sparc_fpu_type = linux_sparc_fpu[i].fp_name;
+				break;
+			}
+	}
+
+	if(i == NSPARCFPU) {
+		printk("DEBUG: psr.impl = 0x%x  fsr.vers = 0x%x\n", psr_impl,
+			    fpu_vers);
+		sparc_fpu_type = linux_sparc_fpu[31].fp_name;
+	}
+}
diff --git a/arch/sparc/kernel/devices.c b/arch/sparc/kernel/devices.c
new file mode 100644
index 0000000..fcb0c04
--- /dev/null
+++ b/arch/sparc/kernel/devices.c
@@ -0,0 +1,160 @@
+/* devices.c: Initial scan of the prom device tree for important
+ *	      Sparc device nodes which we need to find.
+ *
+ * This is based on the sparc64 version, but sun4m doesn't always use
+ * the hardware MIDs, so be careful.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/threads.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+
+#include <asm/page.h>
+#include <asm/oplib.h>
+#include <asm/smp.h>
+#include <asm/system.h>
+#include <asm/cpudata.h>
+
+extern void cpu_probe(void);
+extern void clock_stop_probe(void); /* tadpole.c */
+extern void sun4c_probe_memerr_reg(void);
+
+static char *cpu_mid_prop(void)
+{
+	if (sparc_cpu_model == sun4d)
+		return "cpu-id";
+	return "mid";
+}
+
+static int check_cpu_node(int nd, int *cur_inst,
+			  int (*compare)(int, int, void *), void *compare_arg,
+			  int *prom_node, int *mid)
+{
+	char node_str[128];
+
+	prom_getstring(nd, "device_type", node_str, sizeof(node_str));
+	if (strcmp(node_str, "cpu"))
+		return -ENODEV;
+	
+	if (!compare(nd, *cur_inst, compare_arg)) {
+		if (prom_node)
+			*prom_node = nd;
+		if (mid) {
+			*mid = prom_getintdefault(nd, cpu_mid_prop(), 0);
+			if (sparc_cpu_model == sun4m)
+				*mid &= 3;
+		}
+		return 0;
+	}
+
+	(*cur_inst)++;
+
+	return -ENODEV;
+}
+
+static int __cpu_find_by(int (*compare)(int, int, void *), void *compare_arg,
+			 int *prom_node, int *mid)
+{
+	int nd, cur_inst, err;
+
+	nd = prom_root_node;
+	cur_inst = 0;
+
+	err = check_cpu_node(nd, &cur_inst, compare, compare_arg,
+			     prom_node, mid);
+	if (!err)
+		return 0;
+
+	nd = prom_getchild(nd);
+	while ((nd = prom_getsibling(nd)) != 0) {
+		err = check_cpu_node(nd, &cur_inst, compare, compare_arg,
+				     prom_node, mid);
+		if (!err)
+			return 0;
+	}
+
+	return -ENODEV;
+}
+
+static int cpu_instance_compare(int nd, int instance, void *_arg)
+{
+	int desired_instance = (int) _arg;
+
+	if (instance == desired_instance)
+		return 0;
+	return -ENODEV;
+}
+
+int cpu_find_by_instance(int instance, int *prom_node, int *mid)
+{
+	return __cpu_find_by(cpu_instance_compare, (void *)instance,
+			     prom_node, mid);
+}
+
+static int cpu_mid_compare(int nd, int instance, void *_arg)
+{
+	int desired_mid = (int) _arg;
+	int this_mid;
+
+	this_mid = prom_getintdefault(nd, cpu_mid_prop(), 0);
+	if (this_mid == desired_mid
+	    || (sparc_cpu_model == sun4m && (this_mid & 3) == desired_mid))
+		return 0;
+	return -ENODEV;
+}
+
+int cpu_find_by_mid(int mid, int *prom_node)
+{
+	return __cpu_find_by(cpu_mid_compare, (void *)mid,
+			     prom_node, NULL);
+}
+
+/* sun4m uses truncated mids since we base the cpuid on the ttable/irqset
+ * address (0-3).  This gives us the true hardware mid, which might have
+ * some other bits set.  On 4d hardware and software mids are the same.
+ */
+int cpu_get_hwmid(int prom_node)
+{
+	return prom_getintdefault(prom_node, cpu_mid_prop(), -ENODEV);
+}
+
+void __init device_scan(void)
+{
+	prom_printf("Booting Linux...\n");
+
+#ifndef CONFIG_SMP
+	{
+		int err, cpu_node;
+		err = cpu_find_by_instance(0, &cpu_node, NULL);
+		if (err) {
+			/* Probably a sun4e, Sun is trying to trick us ;-) */
+			prom_printf("No cpu nodes, cannot continue\n");
+			prom_halt();
+		}
+		cpu_data(0).clock_tick = prom_getintdefault(cpu_node,
+							    "clock-frequency",
+							    0);
+	}
+#endif /* !CONFIG_SMP */
+
+	cpu_probe();
+#ifdef CONFIG_SUN_AUXIO
+	{
+		extern void auxio_probe(void);
+		extern void auxio_power_probe(void);
+		auxio_probe();
+		auxio_power_probe();
+	}
+#endif
+	clock_stop_probe();
+
+	if (ARCH_SUN4C_SUN4)
+		sun4c_probe_memerr_reg();
+
+	return;
+}
diff --git a/arch/sparc/kernel/ebus.c b/arch/sparc/kernel/ebus.c
new file mode 100644
index 0000000..1754192
--- /dev/null
+++ b/arch/sparc/kernel/ebus.c
@@ -0,0 +1,361 @@
+/* $Id: ebus.c,v 1.20 2002/01/05 01:13:43 davem Exp $
+ * ebus.c: PCI to EBus bridge device.
+ *
+ * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
+ *
+ * Adopted for sparc by V. Roganov and G. Raiko.
+ * Fixes for different platforms by Pete Zaitcev.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <asm/system.h>
+#include <asm/page.h>
+#include <asm/pbm.h>
+#include <asm/ebus.h>
+#include <asm/io.h>
+#include <asm/oplib.h>
+#include <asm/bpp.h>
+
+struct linux_ebus *ebus_chain = 0;
+
+/* We are together with pcic.c under CONFIG_PCI. */
+extern unsigned int pcic_pin_to_irq(unsigned int, char *name);
+
+/*
+ * IRQ Blacklist
+ * Here we list PROMs and systems that are known to supply crap as IRQ numbers.
+ */
+struct ebus_device_irq {
+	char *name;
+	unsigned int pin;
+};
+
+struct ebus_system_entry {
+	char *esname;
+	struct ebus_device_irq *ipt;
+};
+
+static struct ebus_device_irq je1_1[] = {
+	{ "8042",		 3 },
+	{ "SUNW,CS4231",	 0 },
+	{ "parallel",		 0 },
+	{ "se",			 2 },
+	{ 0, 0 }
+};
+
+/*
+ * Gleb's JE1 supplied reasonable pin numbers, but mine did not (OBP 2.32).
+ * Blacklist the sucker... Note that Gleb's system will work.
+ */
+static struct ebus_system_entry ebus_blacklist[] = {
+	{ "SUNW,JavaEngine1", je1_1 },
+	{ 0, 0 }
+};
+
+static struct ebus_device_irq *ebus_blackp = NULL;
+
+/*
+ */
+static inline unsigned long ebus_alloc(size_t size)
+{
+	return (unsigned long)kmalloc(size, GFP_ATOMIC);
+}
+
+/*
+ */
+int __init ebus_blacklist_irq(char *name)
+{
+	struct ebus_device_irq *dp;
+
+	if ((dp = ebus_blackp) != NULL) {
+		for (; dp->name != NULL; dp++) {
+			if (strcmp(name, dp->name) == 0) {
+				return pcic_pin_to_irq(dp->pin, name);
+			}
+		}
+	}
+	return 0;
+}
+
+void __init fill_ebus_child(int node, struct linux_prom_registers *preg,
+				struct linux_ebus_child *dev)
+{
+	int regs[PROMREG_MAX];
+	int irqs[PROMREG_MAX];
+	char lbuf[128];
+	int i, len;
+
+	dev->prom_node = node;
+	prom_getstring(node, "name", lbuf, sizeof(lbuf));
+	strcpy(dev->prom_name, lbuf);
+
+	len = prom_getproperty(node, "reg", (void *)regs, sizeof(regs));
+	if (len == -1) len = 0;
+	dev->num_addrs = len / sizeof(regs[0]);
+
+	for (i = 0; i < dev->num_addrs; i++) {
+		if (regs[i] >= dev->parent->num_addrs) {
+			prom_printf("UGH: property for %s was %d, need < %d\n",
+				    dev->prom_name, len, dev->parent->num_addrs);
+			panic(__FUNCTION__);
+		}
+		dev->resource[i].start = dev->parent->resource[regs[i]].start; /* XXX resource */
+	}
+
+	for (i = 0; i < PROMINTR_MAX; i++)
+		dev->irqs[i] = PCI_IRQ_NONE;
+
+	if ((dev->irqs[0] = ebus_blacklist_irq(dev->prom_name)) != 0) {
+		dev->num_irqs = 1;
+	} else if ((len = prom_getproperty(node, "interrupts",
+	    (char *)&irqs, sizeof(irqs))) == -1 || len == 0) {
+		dev->num_irqs = 0;
+		dev->irqs[0] = 0;
+		if (dev->parent->num_irqs != 0) {
+			dev->num_irqs = 1;
+			dev->irqs[0] = dev->parent->irqs[0];
+/* P3 */ /* printk("EBUS: dev %s irq %d from parent\n", dev->prom_name, dev->irqs[0]); */
+		}
+	} else {
+		dev->num_irqs = len / sizeof(irqs[0]);
+		if (irqs[0] == 0 || irqs[0] >= 8) {
+			/*
+			 * XXX Zero is a valid pin number...
+			 * This works as long as Ebus is not wired to INTA#.
+			 */
+			printk("EBUS: %s got bad irq %d from PROM\n",
+			    dev->prom_name, irqs[0]);
+			dev->num_irqs = 0;
+			dev->irqs[0] = 0;
+		} else {
+			dev->irqs[0] = pcic_pin_to_irq(irqs[0], dev->prom_name);
+		}
+	}
+}
+
+void __init fill_ebus_device(int node, struct linux_ebus_device *dev)
+{
+	struct linux_prom_registers regs[PROMREG_MAX];
+	struct linux_ebus_child *child;
+	int irqs[PROMINTR_MAX];
+	char lbuf[128];
+	int i, n, len;
+	unsigned long baseaddr;
+
+	dev->prom_node = node;
+	prom_getstring(node, "name", lbuf, sizeof(lbuf));
+	strcpy(dev->prom_name, lbuf);
+
+	len = prom_getproperty(node, "reg", (void *)regs, sizeof(regs));
+	if (len % sizeof(struct linux_prom_registers)) {
+		prom_printf("UGH: proplen for %s was %d, need multiple of %d\n",
+			    dev->prom_name, len,
+			    (int)sizeof(struct linux_prom_registers));
+		panic(__FUNCTION__);
+	}
+	dev->num_addrs = len / sizeof(struct linux_prom_registers);
+
+	for (i = 0; i < dev->num_addrs; i++) {
+		/*
+		 * XXX Collect JE-1 PROM
+		 * 
+		 * Example - JS-E with 3.11:
+		 *  /ebus
+		 *      regs 
+		 *        0x00000000, 0x0, 0x00000000, 0x0, 0x00000000,
+		 *        0x82000010, 0x0, 0xf0000000, 0x0, 0x01000000,
+		 *        0x82000014, 0x0, 0x38800000, 0x0, 0x00800000,
+		 *      ranges
+		 *        0x00, 0x00000000, 0x02000010, 0x0, 0x0, 0x01000000,
+		 *        0x01, 0x01000000, 0x02000014, 0x0, 0x0, 0x00800000,
+		 *  /ebus/8042
+		 *      regs
+		 *        0x00000001, 0x00300060, 0x00000008,
+		 *        0x00000001, 0x00300060, 0x00000008,
+		 */
+		n = regs[i].which_io;
+		if (n >= 4) {
+			/* XXX This is copied from old JE-1 by Gleb. */
+			n = (regs[i].which_io - 0x10) >> 2;
+		} else {
+			;
+		}
+
+/*
+ * XXX Now as we have regions, why don't we make an on-demand allocation...
+ */
+		dev->resource[i].start = 0;
+		if ((baseaddr = dev->bus->self->resource[n].start +
+		    regs[i].phys_addr) != 0) {
+			/* dev->resource[i].name = dev->prom_name; */
+			if ((baseaddr = (unsigned long) ioremap(baseaddr,
+			    regs[i].reg_size)) == 0) {
+				panic("ebus: unable to remap dev %s",
+				    dev->prom_name);
+			}
+		}
+		dev->resource[i].start = baseaddr;	/* XXX Unaligned */
+	}
+
+	for (i = 0; i < PROMINTR_MAX; i++)
+		dev->irqs[i] = PCI_IRQ_NONE;
+
+	if ((dev->irqs[0] = ebus_blacklist_irq(dev->prom_name)) != 0) {
+		dev->num_irqs = 1;
+	} else if ((len = prom_getproperty(node, "interrupts",
+	    (char *)&irqs, sizeof(irqs))) == -1 || len == 0) {
+		dev->num_irqs = 0;
+		if ((dev->irqs[0] = dev->bus->self->irq) != 0) {
+			 dev->num_irqs = 1;
+/* P3 */ /* printk("EBUS: child %s irq %d from parent\n", dev->prom_name, dev->irqs[0]); */
+		}
+	} else {
+		dev->num_irqs = 1;  /* dev->num_irqs = len / sizeof(irqs[0]); */
+		if (irqs[0] == 0 || irqs[0] >= 8) {
+			/* See above for the parent. XXX */
+			printk("EBUS: %s got bad irq %d from PROM\n",
+			    dev->prom_name, irqs[0]);
+			dev->num_irqs = 0;
+			dev->irqs[0] = 0;
+		} else {
+			dev->irqs[0] = pcic_pin_to_irq(irqs[0], dev->prom_name);
+		}
+	}
+
+	if ((node = prom_getchild(node))) {
+		dev->children = (struct linux_ebus_child *)
+			ebus_alloc(sizeof(struct linux_ebus_child));
+
+		child = dev->children;
+		child->next = 0;
+		child->parent = dev;
+		child->bus = dev->bus;
+		fill_ebus_child(node, &regs[0], child);
+
+		while ((node = prom_getsibling(node)) != 0) {
+			child->next = (struct linux_ebus_child *)
+				ebus_alloc(sizeof(struct linux_ebus_child));
+
+			child = child->next;
+			child->next = 0;
+			child->parent = dev;
+			child->bus = dev->bus;
+			fill_ebus_child(node, &regs[0], child);
+		}
+	}
+}
+
+void __init ebus_init(void)
+{
+	struct linux_prom_pci_registers regs[PROMREG_MAX];
+	struct linux_pbm_info *pbm;
+	struct linux_ebus_device *dev;
+	struct linux_ebus *ebus;
+	struct ebus_system_entry *sp;
+	struct pci_dev *pdev;
+	struct pcidev_cookie *cookie;
+	char lbuf[128];
+	unsigned long addr, *base;
+	unsigned short pci_command;
+	int nd, len, ebusnd;
+	int reg, nreg;
+	int num_ebus = 0;
+
+	prom_getstring(prom_root_node, "name", lbuf, sizeof(lbuf));
+	for (sp = ebus_blacklist; sp->esname != NULL; sp++) {
+		if (strcmp(lbuf, sp->esname) == 0) {
+			ebus_blackp = sp->ipt;
+			break;
+		}
+	}
+
+	pdev = pci_get_device(PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_EBUS, 0);
+	if (!pdev) {
+		return;
+	}
+	cookie = pdev->sysdata;
+	ebusnd = cookie->prom_node;
+
+	ebus_chain = ebus = (struct linux_ebus *)
+			ebus_alloc(sizeof(struct linux_ebus));
+	ebus->next = 0;
+
+	while (ebusnd) {
+
+		prom_getstring(ebusnd, "name", lbuf, sizeof(lbuf));
+		ebus->prom_node = ebusnd;
+		strcpy(ebus->prom_name, lbuf);
+		ebus->self = pdev;
+		ebus->parent = pbm = cookie->pbm;
+
+		/* Enable BUS Master. */
+		pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
+		pci_command |= PCI_COMMAND_MASTER;
+		pci_write_config_word(pdev, PCI_COMMAND, pci_command);
+
+		len = prom_getproperty(ebusnd, "reg", (void *)regs,
+				       sizeof(regs));
+		if (len == 0 || len == -1) {
+			prom_printf("%s: can't find reg property\n",
+				    __FUNCTION__);
+			prom_halt();
+		}
+		nreg = len / sizeof(struct linux_prom_pci_registers);
+
+		base = &ebus->self->resource[0].start;
+		for (reg = 0; reg < nreg; reg++) {
+			if (!(regs[reg].which_io & 0x03000000))
+				continue;
+
+			addr = regs[reg].phys_lo;
+			*base++ = addr;
+		}
+
+		nd = prom_getchild(ebusnd);
+		if (!nd)
+			goto next_ebus;
+
+		ebus->devices = (struct linux_ebus_device *)
+				ebus_alloc(sizeof(struct linux_ebus_device));
+
+		dev = ebus->devices;
+		dev->next = 0;
+		dev->children = 0;
+		dev->bus = ebus;
+		fill_ebus_device(nd, dev);
+
+		while ((nd = prom_getsibling(nd)) != 0) {
+			dev->next = (struct linux_ebus_device *)
+				ebus_alloc(sizeof(struct linux_ebus_device));
+
+			dev = dev->next;
+			dev->next = 0;
+			dev->children = 0;
+			dev->bus = ebus;
+			fill_ebus_device(nd, dev);
+		}
+
+	next_ebus:
+		pdev = pci_get_device(PCI_VENDOR_ID_SUN,
+				       PCI_DEVICE_ID_SUN_EBUS, pdev);
+		if (!pdev)
+			break;
+
+		cookie = pdev->sysdata;
+		ebusnd = cookie->prom_node;
+
+		ebus->next = (struct linux_ebus *)
+			ebus_alloc(sizeof(struct linux_ebus));
+		ebus = ebus->next;
+		ebus->next = 0;
+		++num_ebus;
+	}
+	if (pdev)
+		pci_dev_put(pdev);
+}
diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S
new file mode 100644
index 0000000..b448166
--- /dev/null
+++ b/arch/sparc/kernel/entry.S
@@ -0,0 +1,1956 @@
+/* $Id: entry.S,v 1.170 2001/11/13 00:57:05 davem Exp $
+ * arch/sparc/kernel/entry.S:  Sparc trap low-level entry points.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost   (ecd@skynet.be)
+ * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Copyright (C) 1996-1999 Jakub Jelinek   (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997 Anton Blanchard (anton@progsoc.uts.edu.au)
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+
+#include <asm/head.h>
+#include <asm/asi.h>
+#include <asm/smp.h>
+#include <asm/kgdb.h>
+#include <asm/contregs.h>
+#include <asm/ptrace.h>
+#include <asm/asm_offsets.h>
+#include <asm/psr.h>
+#include <asm/vaddrs.h>
+#include <asm/memreg.h>
+#include <asm/page.h>
+#ifdef CONFIG_SUN4
+#include <asm/pgtsun4.h>
+#else
+#include <asm/pgtsun4c.h>
+#endif
+#include <asm/winmacro.h>
+#include <asm/signal.h>
+#include <asm/obio.h>
+#include <asm/mxcc.h>
+#include <asm/thread_info.h>
+#include <asm/param.h>
+
+#include <asm/asmmacro.h>
+
+#define curptr      g6
+
+#define NR_SYSCALLS 284      /* Each OS is different... */
+
+/* These are just handy. */
+#define _SV	save	%sp, -STACKFRAME_SZ, %sp
+#define _RS     restore 
+
+#define FLUSH_ALL_KERNEL_WINDOWS \
+	_SV; _SV; _SV; _SV; _SV; _SV; _SV; \
+	_RS; _RS; _RS; _RS; _RS; _RS; _RS;
+
+/* First, KGDB low level things.  This is a rewrite
+ * of the routines found in the sparc-stub.c asm() statement
+ * from the gdb distribution.  This is also dual-purpose
+ * as a software trap for userlevel programs.
+ */
+	.data
+	.align	4
+
+in_trap_handler:
+	.word	0
+
+	.text
+	.align	4
+
+#if 0 /* kgdb is dropped from 2.5.33 */
+! This function is called when any SPARC trap (except window overflow or
+! underflow) occurs.  It makes sure that the invalid register window is still
+! available before jumping into C code.  It will also restore the world if you
+! return from handle_exception.
+
+	.globl	trap_low
+trap_low:
+	rd	%wim, %l3
+	SAVE_ALL
+
+	sethi	%hi(in_trap_handler), %l4
+	ld	[%lo(in_trap_handler) + %l4], %l5
+	inc	%l5
+	st	%l5, [%lo(in_trap_handler) + %l4]
+
+	/* Make sure kgdb sees the same state we just saved. */
+	LOAD_PT_GLOBALS(sp)
+	LOAD_PT_INS(sp)
+	ld	[%sp + STACKFRAME_SZ + PT_Y], %l4
+	ld	[%sp + STACKFRAME_SZ + PT_WIM], %l3
+	ld	[%sp + STACKFRAME_SZ + PT_PSR], %l0
+	ld	[%sp + STACKFRAME_SZ + PT_PC], %l1
+	ld	[%sp + STACKFRAME_SZ + PT_NPC], %l2
+	rd	%tbr, %l5	/* Never changes... */
+
+	/* Make kgdb exception frame. */	
+	sub	%sp,(16+1+6+1+72)*4,%sp	! Make room for input & locals
+ 					! + hidden arg + arg spill
+					! + doubleword alignment
+					! + registers[72] local var
+	SAVE_KGDB_GLOBALS(sp)
+	SAVE_KGDB_INS(sp)
+	SAVE_KGDB_SREGS(sp, l4, l0, l3, l5, l1, l2)
+
+	/* We are increasing PIL, so two writes. */
+	or	%l0, PSR_PIL, %l0
+	wr	%l0, 0, %psr
+	WRITE_PAUSE
+	wr	%l0, PSR_ET, %psr
+	WRITE_PAUSE
+
+	call	handle_exception
+	 add	%sp, STACKFRAME_SZ, %o0	! Pass address of registers
+
+	/* Load new kgdb register set. */
+	LOAD_KGDB_GLOBALS(sp)
+	LOAD_KGDB_INS(sp)
+	LOAD_KGDB_SREGS(sp, l4, l0, l3, l5, l1, l2)
+	wr      %l4, 0x0, %y
+
+	sethi	%hi(in_trap_handler), %l4
+	ld	[%lo(in_trap_handler) + %l4], %l5
+	dec	%l5
+	st	%l5, [%lo(in_trap_handler) + %l4]
+
+	add	%sp,(16+1+6+1+72)*4,%sp	! Undo the kgdb trap frame.
+
+	/* Now take what kgdb did and place it into the pt_regs
+	 * frame which SparcLinux RESTORE_ALL understands.,
+	 */
+	STORE_PT_INS(sp)
+	STORE_PT_GLOBALS(sp)
+	STORE_PT_YREG(sp, g2)
+	STORE_PT_PRIV(sp, l0, l1, l2)
+
+	RESTORE_ALL
+#endif
+
+#ifdef CONFIG_BLK_DEV_FD
+	.text
+	.align	4
+	.globl	floppy_hardint
+floppy_hardint:
+	/*
+	 * This code cannot touch registers %l0 %l1 and %l2
+	 * because SAVE_ALL depends on their values. It depends
+	 * on %l3 also, but we regenerate it before a call.
+	 * Other registers are:
+	 * %l3 -- base address of fdc registers
+	 * %l4 -- pdma_vaddr
+	 * %l5 -- scratch for ld/st address
+	 * %l6 -- pdma_size
+	 * %l7 -- scratch [floppy byte, ld/st address, aux. data]
+	 */
+
+	/* Do we have work to do? */
+	sethi	%hi(doing_pdma), %l7
+	ld	[%l7 + %lo(doing_pdma)], %l7
+	cmp	%l7, 0
+	be	floppy_dosoftint
+	 nop
+
+	/* Load fdc register base */
+	sethi	%hi(fdc_status), %l3
+	ld	[%l3 + %lo(fdc_status)], %l3
+
+	/* Setup register addresses */
+	sethi	%hi(pdma_vaddr), %l5	! transfer buffer
+	ld	[%l5 + %lo(pdma_vaddr)], %l4
+	sethi	%hi(pdma_size), %l5	! bytes to go
+	ld	[%l5 + %lo(pdma_size)], %l6
+next_byte:
+  	ldub	[%l3], %l7
+
+	andcc	%l7, 0x80, %g0		! Does fifo still have data
+	bz	floppy_fifo_emptied	! fifo has been emptied...
+	 andcc	%l7, 0x20, %g0		! in non-dma mode still?
+	bz	floppy_overrun		! nope, overrun
+	 andcc	%l7, 0x40, %g0		! 0=write 1=read
+	bz	floppy_write
+	 sub	%l6, 0x1, %l6
+
+	/* Ok, actually read this byte */
+	ldub	[%l3 + 1], %l7
+	orcc	%g0, %l6, %g0
+	stb	%l7, [%l4]
+	bne	next_byte
+	 add	%l4, 0x1, %l4
+
+	b	floppy_tdone
+	 nop
+
+floppy_write:
+	/* Ok, actually write this byte */
+	ldub	[%l4], %l7
+	orcc	%g0, %l6, %g0
+	stb	%l7, [%l3 + 1]
+	bne	next_byte
+	 add	%l4, 0x1, %l4
+
+	/* fall through... */
+floppy_tdone:
+	sethi	%hi(pdma_vaddr), %l5
+	st	%l4, [%l5 + %lo(pdma_vaddr)]
+	sethi	%hi(pdma_size), %l5
+	st	%l6, [%l5 + %lo(pdma_size)]
+	/* Flip terminal count pin */
+	set	auxio_register, %l7
+	ld	[%l7], %l7
+
+	set	sparc_cpu_model, %l5
+	ld	[%l5], %l5
+	subcc   %l5, 1, %g0		/* enum { sun4c = 1 }; */
+	be	1f
+	 ldub	[%l7], %l5
+
+	or	%l5, 0xc2, %l5
+	stb	%l5, [%l7]
+	andn    %l5, 0x02, %l5
+	b	2f
+	 nop
+
+1:
+	or      %l5, 0xf4, %l5
+	stb     %l5, [%l7]
+	andn    %l5, 0x04, %l5
+
+2:
+	/* Kill some time so the bits set */
+	WRITE_PAUSE
+	WRITE_PAUSE
+
+	stb     %l5, [%l7]
+
+	/* Prevent recursion */
+	sethi	%hi(doing_pdma), %l7
+	b	floppy_dosoftint
+	 st	%g0, [%l7 + %lo(doing_pdma)]
+
+	/* We emptied the FIFO, but we haven't read everything
+	 * as of yet.  Store the current transfer address and
+	 * bytes left to read so we can continue when the next
+	 * fast IRQ comes in.
+	 */
+floppy_fifo_emptied:
+	sethi	%hi(pdma_vaddr), %l5
+	st	%l4, [%l5 + %lo(pdma_vaddr)]
+	sethi	%hi(pdma_size), %l7
+	st	%l6, [%l7 + %lo(pdma_size)]
+
+	/* Restore condition codes */
+	wr	%l0, 0x0, %psr
+	WRITE_PAUSE
+
+	jmp	%l1
+	rett	%l2
+
+floppy_overrun:
+	sethi	%hi(pdma_vaddr), %l5
+	st	%l4, [%l5 + %lo(pdma_vaddr)]
+	sethi	%hi(pdma_size), %l5
+	st	%l6, [%l5 + %lo(pdma_size)]
+	/* Prevent recursion */
+	sethi	%hi(doing_pdma), %l7
+	st	%g0, [%l7 + %lo(doing_pdma)]
+
+	/* fall through... */
+floppy_dosoftint:
+	rd	%wim, %l3
+	SAVE_ALL
+
+	/* Set all IRQs off. */
+	or	%l0, PSR_PIL, %l4
+	wr	%l4, 0x0, %psr
+	WRITE_PAUSE
+	wr	%l4, PSR_ET, %psr
+	WRITE_PAUSE
+
+	mov	11, %o0			! floppy irq level (unused anyway)
+	mov	%g0, %o1		! devid is not used in fast interrupts
+	call	sparc_floppy_irq
+	 add	%sp, STACKFRAME_SZ, %o2	! struct pt_regs *regs
+
+	RESTORE_ALL
+	
+#endif /* (CONFIG_BLK_DEV_FD) */
+
+	/* Bad trap handler */
+	.globl	bad_trap_handler
+bad_trap_handler:
+	SAVE_ALL
+
+	wr	%l0, PSR_ET, %psr
+	WRITE_PAUSE
+
+	add	%sp, STACKFRAME_SZ, %o0	! pt_regs
+	call	do_hw_interrupt
+	 mov	%l7, %o1		! trap number
+
+	RESTORE_ALL
+	
+/* For now all IRQ's not registered get sent here. handler_irq() will
+ * see if a routine is registered to handle this interrupt and if not
+ * it will say so on the console.
+ */
+
+	.align	4
+	.globl	real_irq_entry, patch_handler_irq
+real_irq_entry:
+	SAVE_ALL
+
+#ifdef CONFIG_SMP
+	.globl	patchme_maybe_smp_msg
+
+	cmp	%l7, 12
+patchme_maybe_smp_msg:
+	bgu	maybe_smp4m_msg
+	 nop
+#endif
+
+real_irq_continue:
+	or	%l0, PSR_PIL, %g2
+	wr	%g2, 0x0, %psr
+	WRITE_PAUSE
+	wr	%g2, PSR_ET, %psr
+	WRITE_PAUSE
+	mov	%l7, %o0		! irq level
+patch_handler_irq:
+	call	handler_irq
+	 add	%sp, STACKFRAME_SZ, %o1	! pt_regs ptr
+	or	%l0, PSR_PIL, %g2	! restore PIL after handler_irq
+	wr	%g2, PSR_ET, %psr	! keep ET up
+	WRITE_PAUSE
+
+	RESTORE_ALL
+
+#ifdef CONFIG_SMP
+	/* SMP per-cpu ticker interrupts are handled specially. */
+smp4m_ticker:
+	bne	real_irq_continue+4
+	 or	%l0, PSR_PIL, %g2
+	wr	%g2, 0x0, %psr
+	WRITE_PAUSE
+	wr	%g2, PSR_ET, %psr
+	WRITE_PAUSE
+	call	smp4m_percpu_timer_interrupt
+	 add	%sp, STACKFRAME_SZ, %o0
+	wr	%l0, PSR_ET, %psr
+	WRITE_PAUSE
+	RESTORE_ALL
+
+	/* Here is where we check for possible SMP IPI passed to us
+	 * on some level other than 15 which is the NMI and only used
+	 * for cross calls.  That has a separate entry point below.
+	 */
+maybe_smp4m_msg:
+	GET_PROCESSOR4M_ID(o3)
+	set	sun4m_interrupts, %l5
+	ld	[%l5], %o5
+	sethi	%hi(0x40000000), %o2
+	sll	%o3, 12, %o3
+	ld	[%o5 + %o3], %o1
+	andcc	%o1, %o2, %g0
+	be,a	smp4m_ticker
+	 cmp	%l7, 14
+	st	%o2, [%o5 + 0x4]
+	WRITE_PAUSE
+	ld	[%o5], %g0
+	WRITE_PAUSE
+	or	%l0, PSR_PIL, %l4
+	wr	%l4, 0x0, %psr
+	WRITE_PAUSE
+	wr	%l4, PSR_ET, %psr
+	WRITE_PAUSE
+	call	smp_reschedule_irq
+	 nop
+
+	RESTORE_ALL
+
+	.align	4
+	.globl	linux_trap_ipi15_sun4m
+linux_trap_ipi15_sun4m:
+	SAVE_ALL
+	sethi	%hi(0x80000000), %o2
+	GET_PROCESSOR4M_ID(o0)
+	set	sun4m_interrupts, %l5
+	ld	[%l5], %o5
+	sll	%o0, 12, %o0
+	add	%o5, %o0, %o5
+	ld	[%o5], %o3
+	andcc	%o3, %o2, %g0
+	be	1f			! Must be an NMI async memory error
+	 st	%o2, [%o5 + 4]
+	WRITE_PAUSE
+	ld	[%o5], %g0
+	WRITE_PAUSE
+	or	%l0, PSR_PIL, %l4
+	wr	%l4, 0x0, %psr
+	WRITE_PAUSE
+	wr	%l4, PSR_ET, %psr
+	WRITE_PAUSE
+	call	smp4m_cross_call_irq
+	 nop
+	b	ret_trap_lockless_ipi
+	 clr	%l6
+1:
+	/* NMI async memory error handling. */
+	sethi	%hi(0x80000000), %l4
+	sethi	%hi(0x4000), %o3
+	sub	%o5, %o0, %o5
+	add	%o5, %o3, %l5
+	st	%l4, [%l5 + 0xc]
+	WRITE_PAUSE
+	ld	[%l5], %g0
+	WRITE_PAUSE
+	or	%l0, PSR_PIL, %l4
+	wr	%l4, 0x0, %psr
+	WRITE_PAUSE
+	wr	%l4, PSR_ET, %psr
+	WRITE_PAUSE
+	call	sun4m_nmi
+	 nop
+	st	%l4, [%l5 + 0x8]
+	WRITE_PAUSE
+	ld	[%l5], %g0
+	WRITE_PAUSE
+	RESTORE_ALL
+
+	.globl	smp4d_ticker
+	/* SMP per-cpu ticker interrupts are handled specially. */
+smp4d_ticker:
+	SAVE_ALL
+	or	%l0, PSR_PIL, %g2
+	sethi	%hi(CC_ICLR), %o0
+	sethi	%hi(1 << 14), %o1
+	or	%o0, %lo(CC_ICLR), %o0
+	stha	%o1, [%o0] ASI_M_MXCC	/* Clear PIL 14 in MXCC's ICLR */
+	wr	%g2, 0x0, %psr
+	WRITE_PAUSE
+	wr	%g2, PSR_ET, %psr
+	WRITE_PAUSE
+	call	smp4d_percpu_timer_interrupt
+	 add	%sp, STACKFRAME_SZ, %o0
+	wr	%l0, PSR_ET, %psr
+	WRITE_PAUSE
+	RESTORE_ALL
+
+	.align	4
+	.globl	linux_trap_ipi15_sun4d
+linux_trap_ipi15_sun4d:
+	SAVE_ALL
+	sethi	%hi(CC_BASE), %o4
+	sethi	%hi(MXCC_ERR_ME|MXCC_ERR_PEW|MXCC_ERR_ASE|MXCC_ERR_PEE), %o2
+	or	%o4, (CC_EREG - CC_BASE), %o0
+	ldda	[%o0] ASI_M_MXCC, %o0
+	andcc	%o0, %o2, %g0
+	bne	1f
+	 sethi	%hi(BB_STAT2), %o2
+	lduba	[%o2] ASI_M_CTL, %o2
+	andcc	%o2, BB_STAT2_MASK, %g0
+	bne	2f
+	 or	%o4, (CC_ICLR - CC_BASE), %o0
+	sethi	%hi(1 << 15), %o1
+	stha	%o1, [%o0] ASI_M_MXCC	/* Clear PIL 15 in MXCC's ICLR */
+	or	%l0, PSR_PIL, %l4
+	wr	%l4, 0x0, %psr
+	WRITE_PAUSE
+	wr	%l4, PSR_ET, %psr
+	WRITE_PAUSE
+	call	smp4d_cross_call_irq
+	 nop
+	b	ret_trap_lockless_ipi
+	 clr	%l6
+
+1:	/* MXCC error */
+2:	/* BB error */
+	/* Disable PIL 15 */
+	set	CC_IMSK, %l4
+	lduha	[%l4] ASI_M_MXCC, %l5
+	sethi	%hi(1 << 15), %l7
+	or	%l5, %l7, %l5
+	stha	%l5, [%l4] ASI_M_MXCC
+	/* FIXME */
+1:	b,a	1b
+
+#endif /* CONFIG_SMP */
+
+	/* This routine handles illegal instructions and privileged
+	 * instruction attempts from user code.
+	 */
+	.align	4
+	.globl	bad_instruction
+bad_instruction:
+	sethi	%hi(0xc1f80000), %l4
+	ld	[%l1], %l5
+	sethi	%hi(0x81d80000), %l7
+	and	%l5, %l4, %l5
+	cmp	%l5, %l7
+	be	1f
+	SAVE_ALL
+
+	wr	%l0, PSR_ET, %psr		! re-enable traps
+	WRITE_PAUSE
+
+	add	%sp, STACKFRAME_SZ, %o0
+	mov	%l1, %o1
+	mov	%l2, %o2
+	call	do_illegal_instruction
+	 mov	%l0, %o3
+
+	RESTORE_ALL
+
+1:	/* unimplemented flush - just skip */
+	jmpl	%l2, %g0
+	 rett	%l2 + 4
+
+	.align	4
+	.globl	priv_instruction
+priv_instruction:
+	SAVE_ALL
+
+	wr	%l0, PSR_ET, %psr
+	WRITE_PAUSE
+
+	add	%sp, STACKFRAME_SZ, %o0
+	mov	%l1, %o1
+	mov	%l2, %o2
+	call	do_priv_instruction
+	 mov	%l0, %o3
+
+	RESTORE_ALL
+
+	/* This routine handles unaligned data accesses. */
+	.align	4
+	.globl	mna_handler
+mna_handler:
+	andcc	%l0, PSR_PS, %g0
+	be	mna_fromuser
+	 nop
+
+	SAVE_ALL
+
+	wr	%l0, PSR_ET, %psr
+	WRITE_PAUSE
+
+	ld	[%l1], %o1
+	call	kernel_unaligned_trap
+	 add	%sp, STACKFRAME_SZ, %o0
+
+	RESTORE_ALL
+
+mna_fromuser:
+	SAVE_ALL
+
+	wr	%l0, PSR_ET, %psr		! re-enable traps
+	WRITE_PAUSE
+
+	ld	[%l1], %o1
+	call	user_unaligned_trap
+	 add	%sp, STACKFRAME_SZ, %o0
+
+	RESTORE_ALL
+
+	/* This routine handles floating point disabled traps. */
+	.align	4
+	.globl	fpd_trap_handler
+fpd_trap_handler:
+	SAVE_ALL
+
+	wr	%l0, PSR_ET, %psr		! re-enable traps
+	WRITE_PAUSE
+
+	add	%sp, STACKFRAME_SZ, %o0
+	mov	%l1, %o1
+	mov	%l2, %o2
+	call	do_fpd_trap
+	 mov	%l0, %o3
+
+	RESTORE_ALL
+
+	/* This routine handles Floating Point Exceptions. */
+	.align	4
+	.globl	fpe_trap_handler
+fpe_trap_handler:
+	set	fpsave_magic, %l5
+	cmp	%l1, %l5
+	be	1f
+	 sethi	%hi(fpsave), %l5
+	or	%l5, %lo(fpsave), %l5
+	cmp	%l1, %l5
+	bne	2f
+	 sethi	%hi(fpsave_catch2), %l5
+	or	%l5, %lo(fpsave_catch2), %l5
+	wr	%l0, 0x0, %psr
+	WRITE_PAUSE
+	jmp	%l5
+	 rett	%l5 + 4
+1:	
+	sethi	%hi(fpsave_catch), %l5
+	or	%l5, %lo(fpsave_catch), %l5
+	wr	%l0, 0x0, %psr
+	WRITE_PAUSE
+	jmp	%l5
+	 rett	%l5 + 4
+
+2:
+	SAVE_ALL
+
+	wr	%l0, PSR_ET, %psr		! re-enable traps
+	WRITE_PAUSE
+
+	add	%sp, STACKFRAME_SZ, %o0
+	mov	%l1, %o1
+	mov	%l2, %o2
+	call	do_fpe_trap
+	 mov	%l0, %o3
+
+	RESTORE_ALL
+
+	/* This routine handles Tag Overflow Exceptions. */
+	.align	4
+	.globl	do_tag_overflow
+do_tag_overflow:
+	SAVE_ALL
+
+	wr	%l0, PSR_ET, %psr		! re-enable traps
+	WRITE_PAUSE
+
+	add	%sp, STACKFRAME_SZ, %o0
+	mov	%l1, %o1
+	mov	%l2, %o2
+	call	handle_tag_overflow
+	 mov	%l0, %o3
+
+	RESTORE_ALL
+
+	/* This routine handles Watchpoint Exceptions. */
+	.align	4
+	.globl	do_watchpoint
+do_watchpoint:
+	SAVE_ALL
+
+	wr	%l0, PSR_ET, %psr		! re-enable traps
+	WRITE_PAUSE
+
+	add	%sp, STACKFRAME_SZ, %o0
+	mov	%l1, %o1
+	mov	%l2, %o2
+	call	handle_watchpoint
+	 mov	%l0, %o3
+
+	RESTORE_ALL
+
+	/* This routine handles Register Access Exceptions. */
+	.align	4
+	.globl	do_reg_access
+do_reg_access:
+	SAVE_ALL
+
+	wr	%l0, PSR_ET, %psr		! re-enable traps
+	WRITE_PAUSE
+
+	add	%sp, STACKFRAME_SZ, %o0
+	mov	%l1, %o1
+	mov	%l2, %o2
+	call	handle_reg_access
+	 mov	%l0, %o3
+
+	RESTORE_ALL
+
+	/* This routine handles Co-Processor Disabled Exceptions. */
+	.align	4
+	.globl	do_cp_disabled
+do_cp_disabled:
+	SAVE_ALL
+
+	wr	%l0, PSR_ET, %psr		! re-enable traps
+	WRITE_PAUSE
+
+	add	%sp, STACKFRAME_SZ, %o0
+	mov	%l1, %o1
+	mov	%l2, %o2
+	call	handle_cp_disabled
+	 mov	%l0, %o3
+
+	RESTORE_ALL
+
+	/* This routine handles Co-Processor Exceptions. */
+	.align	4
+	.globl	do_cp_exception
+do_cp_exception:
+	SAVE_ALL
+
+	wr	%l0, PSR_ET, %psr		! re-enable traps
+	WRITE_PAUSE
+
+	add	%sp, STACKFRAME_SZ, %o0
+	mov	%l1, %o1
+	mov	%l2, %o2
+	call	handle_cp_exception
+	 mov	%l0, %o3
+
+	RESTORE_ALL
+
+	/* This routine handles Hardware Divide By Zero Exceptions. */
+	.align	4
+	.globl	do_hw_divzero
+do_hw_divzero:
+	SAVE_ALL
+
+	wr	%l0, PSR_ET, %psr		! re-enable traps
+	WRITE_PAUSE
+
+	add	%sp, STACKFRAME_SZ, %o0
+	mov	%l1, %o1
+	mov	%l2, %o2
+	call	handle_hw_divzero
+	 mov	%l0, %o3
+
+	RESTORE_ALL
+
+	.align	4
+	.globl	do_flush_windows
+do_flush_windows:
+	SAVE_ALL
+
+	wr	%l0, PSR_ET, %psr
+	WRITE_PAUSE
+
+	andcc	%l0, PSR_PS, %g0
+	bne	dfw_kernel
+	 nop
+
+	call	flush_user_windows
+	 nop
+
+	/* Advance over the trap instruction. */
+	ld	[%sp + STACKFRAME_SZ + PT_NPC], %l1
+	add	%l1, 0x4, %l2
+	st	%l1, [%sp + STACKFRAME_SZ + PT_PC]
+	st	%l2, [%sp + STACKFRAME_SZ + PT_NPC]
+
+	RESTORE_ALL
+
+	.globl	flush_patch_one
+
+	/* We get these for debugging routines using __builtin_return_address() */
+dfw_kernel:
+flush_patch_one:
+	FLUSH_ALL_KERNEL_WINDOWS
+
+	/* Advance over the trap instruction. */
+	ld	[%sp + STACKFRAME_SZ + PT_NPC], %l1
+	add	%l1, 0x4, %l2
+	st	%l1, [%sp + STACKFRAME_SZ + PT_PC]
+	st	%l2, [%sp + STACKFRAME_SZ + PT_NPC]
+
+	RESTORE_ALL
+
+	/* The getcc software trap.  The user wants the condition codes from
+	 * the %psr in register %g1.
+	 */
+
+	.align	4
+	.globl	getcc_trap_handler
+getcc_trap_handler:
+	srl	%l0, 20, %g1	! give user
+	and	%g1, 0xf, %g1	! only ICC bits in %psr
+	jmp	%l2		! advance over trap instruction
+	rett	%l2 + 0x4	! like this...
+
+	/* The setcc software trap.  The user has condition codes in %g1
+	 * that it would like placed in the %psr.  Be careful not to flip
+	 * any unintentional bits!
+	 */
+
+	.align	4
+	.globl	setcc_trap_handler
+setcc_trap_handler:
+	sll	%g1, 0x14, %l4
+	set	PSR_ICC, %l5
+	andn	%l0, %l5, %l0	! clear ICC bits in %psr
+	and	%l4, %l5, %l4	! clear non-ICC bits in user value
+	or	%l4, %l0, %l4	! or them in... mix mix mix
+
+	wr	%l4, 0x0, %psr	! set new %psr
+	WRITE_PAUSE		! TI scumbags...
+
+	jmp	%l2		! advance over trap instruction
+	rett	%l2 + 0x4	! like this...
+
+	.align	4
+	.globl	linux_trap_nmi_sun4c
+linux_trap_nmi_sun4c:
+	SAVE_ALL
+
+	/* Ugh, we need to clear the IRQ line.  This is now
+	 * a very sun4c specific trap handler...
+	 */
+	sethi	%hi(interrupt_enable), %l5
+	ld	[%l5 + %lo(interrupt_enable)], %l5
+	ldub	[%l5], %l6
+	andn	%l6, INTS_ENAB, %l6
+	stb	%l6, [%l5]
+
+	/* Now it is safe to re-enable traps without recursion. */
+	or	%l0, PSR_PIL, %l0
+	wr	%l0, PSR_ET, %psr
+	WRITE_PAUSE
+
+	/* Now call the c-code with the pt_regs frame ptr and the
+	 * memory error registers as arguments.  The ordering chosen
+	 * here is due to unlatching semantics.
+	 */
+	sethi	%hi(AC_SYNC_ERR), %o0
+	add	%o0, 0x4, %o0
+	lda	[%o0] ASI_CONTROL, %o2	! sync vaddr
+	sub	%o0, 0x4, %o0
+	lda	[%o0] ASI_CONTROL, %o1	! sync error
+	add	%o0, 0xc, %o0
+	lda	[%o0] ASI_CONTROL, %o4	! async vaddr
+	sub	%o0, 0x4, %o0
+	lda	[%o0] ASI_CONTROL, %o3	! async error
+	call	sparc_lvl15_nmi
+	 add	%sp, STACKFRAME_SZ, %o0
+
+	RESTORE_ALL
+
+	.align	4
+	.globl	invalid_segment_patch1_ff
+	.globl	invalid_segment_patch2_ff
+invalid_segment_patch1_ff:	cmp	%l4, 0xff
+invalid_segment_patch2_ff:	mov	0xff, %l3
+
+	.align	4
+	.globl	invalid_segment_patch1_1ff
+	.globl	invalid_segment_patch2_1ff
+invalid_segment_patch1_1ff:	cmp	%l4, 0x1ff
+invalid_segment_patch2_1ff:	mov	0x1ff, %l3
+
+	.align	4
+	.globl	num_context_patch1_16, num_context_patch2_16
+num_context_patch1_16:		mov	0x10, %l7
+num_context_patch2_16:		mov	0x10, %l7
+
+	.align	4
+	.globl	vac_linesize_patch_32
+vac_linesize_patch_32:		subcc	%l7, 32, %l7
+
+	.align	4
+	.globl	vac_hwflush_patch1_on, vac_hwflush_patch2_on
+
+/*
+ * Ugly, but we cant use hardware flushing on the sun4 and we'd require
+ * two instructions (Anton)
+ */
+#ifdef CONFIG_SUN4
+vac_hwflush_patch1_on:		nop
+#else
+vac_hwflush_patch1_on:		addcc	%l7, -PAGE_SIZE, %l7
+#endif
+
+vac_hwflush_patch2_on:		sta	%g0, [%l3 + %l7] ASI_HWFLUSHSEG
+
+	.globl	invalid_segment_patch1, invalid_segment_patch2
+	.globl	num_context_patch1
+	.globl	vac_linesize_patch, vac_hwflush_patch1
+	.globl	vac_hwflush_patch2
+
+	.align	4
+	.globl	sun4c_fault
+
+! %l0 = %psr
+! %l1 = %pc
+! %l2 = %npc
+! %l3 = %wim
+! %l7 = 1 for textfault
+! We want error in %l5, vaddr in %l6
+sun4c_fault:
+#ifdef CONFIG_SUN4
+	sethi	%hi(sun4c_memerr_reg), %l4
+	ld	[%l4+%lo(sun4c_memerr_reg)], %l4  ! memerr ctrl reg addr
+	ld	[%l4], %l6		! memerr ctrl reg
+	ld	[%l4 + 4], %l5		! memerr vaddr reg
+	andcc	%l6, 0x80, %g0		! check for error type
+	st	%g0, [%l4 + 4]		! clear the error
+	be	0f			! normal error
+	 sethi	%hi(AC_BUS_ERROR), %l4	! bus err reg addr
+
+	call	prom_halt	! something weird happened
+					! what exactly did happen?
+					! what should we do here?
+
+0:	or	%l4, %lo(AC_BUS_ERROR), %l4	! bus err reg addr
+	lduba	[%l4] ASI_CONTROL, %l6	! bus err reg
+
+	cmp    %l7, 1			! text fault?
+	be	1f			! yes
+	 nop
+
+	ld     [%l1], %l4		! load instruction that caused fault
+	srl	%l4, 21, %l4
+	andcc	%l4, 1, %g0		! store instruction?
+
+	be	1f			! no
+	 sethi	%hi(SUN4C_SYNC_BADWRITE), %l4 ! yep
+					! %lo(SUN4C_SYNC_BADWRITE) = 0
+	or	%l4, %l6, %l6		! set write bit to emulate sun4c
+1:
+#else
+	sethi	%hi(AC_SYNC_ERR), %l4
+	add	%l4, 0x4, %l6			! AC_SYNC_VA in %l6
+	lda	[%l6] ASI_CONTROL, %l5		! Address
+	lda	[%l4] ASI_CONTROL, %l6		! Error, retained for a bit
+#endif
+
+	andn	%l5, 0xfff, %l5			! Encode all info into l7
+	srl	%l6, 14, %l4
+
+	and	%l4, 2, %l4
+	or	%l5, %l4, %l4
+
+	or	%l4, %l7, %l7			! l7 = [addr,write,txtfault]
+
+	andcc	%l0, PSR_PS, %g0
+	be	sun4c_fault_fromuser
+	 andcc	%l7, 1, %g0			! Text fault?
+
+	be	1f
+	 sethi	%hi(KERNBASE), %l4
+
+	mov	%l1, %l5			! PC
+
+1:
+	cmp	%l5, %l4
+	blu	sun4c_fault_fromuser
+	 sethi	%hi(~((1 << SUN4C_REAL_PGDIR_SHIFT) - 1)), %l4
+
+	/* If the kernel references a bum kernel pointer, or a pte which
+	 * points to a non existant page in ram, we will run this code
+	 * _forever_ and lock up the machine!!!!! So we must check for
+	 * this condition, the AC_SYNC_ERR bits are what we must examine.
+	 * Also a parity error would make this happen as well.  So we just
+	 * check that we are in fact servicing a tlb miss and not some
+	 * other type of fault for the kernel.
+	 */
+	andcc	%l6, 0x80, %g0
+	be	sun4c_fault_fromuser
+	 and	%l5, %l4, %l5
+
+	/* Test for NULL pte_t * in vmalloc area. */
+	sethi   %hi(VMALLOC_START), %l4
+	cmp     %l5, %l4
+	blu,a   invalid_segment_patch1
+	 lduXa	[%l5] ASI_SEGMAP, %l4
+
+	sethi   %hi(swapper_pg_dir), %l4
+	srl     %l5, SUN4C_PGDIR_SHIFT, %l6
+	or      %l4, %lo(swapper_pg_dir), %l4
+	sll     %l6, 2, %l6
+	ld      [%l4 + %l6], %l4
+#ifdef CONFIG_SUN4
+	sethi	%hi(PAGE_MASK), %l6
+	andcc	%l4, %l6, %g0
+#else
+	andcc   %l4, PAGE_MASK, %g0
+#endif
+	be      sun4c_fault_fromuser
+	 lduXa  [%l5] ASI_SEGMAP, %l4
+
+invalid_segment_patch1:
+	cmp	%l4, 0x7f
+	bne	1f
+	 sethi	%hi(sun4c_kfree_ring), %l4
+	or	%l4, %lo(sun4c_kfree_ring), %l4
+	ld	[%l4 + 0x18], %l3
+	deccc	%l3			! do we have a free entry?
+	bcs,a	2f			! no, unmap one.
+	 sethi	%hi(sun4c_kernel_ring), %l4
+
+	st	%l3, [%l4 + 0x18]	! sun4c_kfree_ring.num_entries--
+
+	ld	[%l4 + 0x00], %l6	! entry = sun4c_kfree_ring.ringhd.next
+	st	%l5, [%l6 + 0x08]	! entry->vaddr = address
+
+	ld	[%l6 + 0x00], %l3	! next = entry->next
+	ld	[%l6 + 0x04], %l7	! entry->prev
+
+	st	%l7, [%l3 + 0x04]	! next->prev = entry->prev
+	st	%l3, [%l7 + 0x00]	! entry->prev->next = next
+
+	sethi	%hi(sun4c_kernel_ring), %l4
+	or	%l4, %lo(sun4c_kernel_ring), %l4
+					! head = &sun4c_kernel_ring.ringhd
+
+	ld	[%l4 + 0x00], %l7	! head->next
+
+	st	%l4, [%l6 + 0x04]	! entry->prev = head
+	st	%l7, [%l6 + 0x00]	! entry->next = head->next
+	st	%l6, [%l7 + 0x04]	! head->next->prev = entry
+
+	st	%l6, [%l4 + 0x00]	! head->next = entry
+
+	ld	[%l4 + 0x18], %l3
+	inc	%l3			! sun4c_kernel_ring.num_entries++
+	st	%l3, [%l4 + 0x18]
+	b	4f
+	 ld	[%l6 + 0x08], %l5
+
+2:
+	or	%l4, %lo(sun4c_kernel_ring), %l4
+					! head = &sun4c_kernel_ring.ringhd
+
+	ld	[%l4 + 0x04], %l6	! entry = head->prev
+
+	ld	[%l6 + 0x08], %l3	! tmp = entry->vaddr
+
+	! Flush segment from the cache.
+#ifdef CONFIG_SUN4
+	sethi	%hi((128 * 1024)), %l7
+#else
+	sethi	%hi((64 * 1024)), %l7
+#endif
+9:
+vac_hwflush_patch1:
+vac_linesize_patch:
+	subcc	%l7, 16, %l7
+	bne	9b
+vac_hwflush_patch2:
+	 sta	%g0, [%l3 + %l7] ASI_FLUSHSEG
+
+	st	%l5, [%l6 + 0x08]	! entry->vaddr = address
+
+	ld	[%l6 + 0x00], %l5	! next = entry->next
+	ld	[%l6 + 0x04], %l7	! entry->prev
+
+	st	%l7, [%l5 + 0x04]	! next->prev = entry->prev
+	st	%l5, [%l7 + 0x00]	! entry->prev->next = next
+	st	%l4, [%l6 + 0x04]	! entry->prev = head
+
+	ld	[%l4 + 0x00], %l7	! head->next
+
+	st	%l7, [%l6 + 0x00]	! entry->next = head->next
+	st	%l6, [%l7 + 0x04]	! head->next->prev = entry
+	st	%l6, [%l4 + 0x00]	! head->next = entry
+
+	mov	%l3, %l5		! address = tmp
+
+4:
+num_context_patch1:
+	mov	0x08, %l7
+
+	ld	[%l6 + 0x08], %l4
+	ldub	[%l6 + 0x0c], %l3
+	or	%l4, %l3, %l4		! encode new vaddr/pseg into l4
+
+	sethi	%hi(AC_CONTEXT), %l3
+	lduba	[%l3] ASI_CONTROL, %l6
+
+	/* Invalidate old mapping, instantiate new mapping,
+	 * for each context.  Registers l6/l7 are live across
+	 * this loop.
+	 */
+3:	deccc	%l7
+	sethi	%hi(AC_CONTEXT), %l3
+	stba	%l7, [%l3] ASI_CONTROL
+invalid_segment_patch2:
+	mov	0x7f, %l3
+	stXa	%l3, [%l5] ASI_SEGMAP
+	andn	%l4, 0x1ff, %l3
+	bne	3b
+	 stXa	%l4, [%l3] ASI_SEGMAP
+
+	sethi	%hi(AC_CONTEXT), %l3
+	stba	%l6, [%l3] ASI_CONTROL
+
+	andn	%l4, 0x1ff, %l5
+
+1:
+	sethi	%hi(VMALLOC_START), %l4
+	cmp	%l5, %l4
+
+	bgeu	1f
+	 mov	1 << (SUN4C_REAL_PGDIR_SHIFT - PAGE_SHIFT), %l7
+
+	sethi	%hi(KERNBASE), %l6
+
+	sub	%l5, %l6, %l4
+	srl	%l4, PAGE_SHIFT, %l4
+	sethi	%hi((SUN4C_PAGE_KERNEL & 0xf4000000)), %l3
+	or	%l3, %l4, %l3
+
+	sethi	%hi(PAGE_SIZE), %l4
+
+2:
+	sta	%l3, [%l5] ASI_PTE
+	deccc	%l7
+	inc	%l3
+	bne	2b
+	 add	%l5, %l4, %l5
+
+	b	7f
+	 sethi	%hi(sun4c_kernel_faults), %l4
+
+1:
+	srl	%l5, SUN4C_PGDIR_SHIFT, %l3
+	sethi	%hi(swapper_pg_dir), %l4
+	or	%l4, %lo(swapper_pg_dir), %l4
+	sll	%l3, 2, %l3
+	ld	[%l4 + %l3], %l4
+#ifndef CONFIG_SUN4
+	and	%l4, PAGE_MASK, %l4
+#else
+	sethi	%hi(PAGE_MASK), %l6
+	and	%l4, %l6, %l4
+#endif
+
+	srl	%l5, (PAGE_SHIFT - 2), %l6
+	and	%l6, ((SUN4C_PTRS_PER_PTE - 1) << 2), %l6
+	add	%l6, %l4, %l6
+
+	sethi	%hi(PAGE_SIZE), %l4
+
+2:
+	ld	[%l6], %l3
+	deccc	%l7
+	sta	%l3, [%l5] ASI_PTE
+	add	%l6, 0x4, %l6
+	bne	2b
+	 add	%l5, %l4, %l5
+
+	sethi	%hi(sun4c_kernel_faults), %l4
+7:
+	ld	[%l4 + %lo(sun4c_kernel_faults)], %l3
+	inc	%l3
+	st	%l3, [%l4 + %lo(sun4c_kernel_faults)]
+
+	/* Restore condition codes */
+	wr	%l0, 0x0, %psr
+	WRITE_PAUSE
+	jmp	%l1
+	 rett	%l2
+
+sun4c_fault_fromuser:
+	SAVE_ALL
+	 nop
+	
+	mov	%l7, %o1		! Decode the info from %l7
+	mov	%l7, %o2
+	and	%o1, 1, %o1		! arg2 = text_faultp
+	mov	%l7, %o3
+	and	%o2, 2, %o2		! arg3 = writep
+	andn	%o3, 0xfff, %o3		! arg4 = faulting address
+
+	wr	%l0, PSR_ET, %psr
+	WRITE_PAUSE
+
+	call	do_sun4c_fault
+	 add	%sp, STACKFRAME_SZ, %o0	! arg1 = pt_regs ptr
+
+	RESTORE_ALL
+
+	.align	4
+	.globl	srmmu_fault
+srmmu_fault:
+	mov	0x400, %l5
+	mov	0x300, %l4
+
+	lda	[%l5] ASI_M_MMUREGS, %l6	! read sfar first
+	lda	[%l4] ASI_M_MMUREGS, %l5	! read sfsr last
+
+	andn	%l6, 0xfff, %l6
+	srl	%l5, 6, %l5			! and encode all info into l7
+
+	and	%l5, 2, %l5
+	or	%l5, %l6, %l6
+
+	or	%l6, %l7, %l7			! l7 = [addr,write,txtfault]
+
+	SAVE_ALL
+
+	mov	%l7, %o1
+	mov	%l7, %o2
+	and	%o1, 1, %o1		! arg2 = text_faultp
+	mov	%l7, %o3
+	and	%o2, 2, %o2		! arg3 = writep
+	andn	%o3, 0xfff, %o3		! arg4 = faulting address
+
+	wr	%l0, PSR_ET, %psr
+	WRITE_PAUSE
+
+	call	do_sparc_fault
+	 add	%sp, STACKFRAME_SZ, %o0	! arg1 = pt_regs ptr
+
+	RESTORE_ALL
+
+#ifdef CONFIG_SUNOS_EMUL
+	/* SunOS uses syscall zero as the 'indirect syscall' it looks
+	 * like indir_syscall(scall_num, arg0, arg1, arg2...);  etc.
+	 * This is complete brain damage.
+	 */
+	.globl	sunos_indir
+sunos_indir:
+	mov	%o7, %l4
+	cmp	%o0, NR_SYSCALLS
+	blu,a	1f
+	 sll	%o0, 0x2, %o0
+
+	sethi	%hi(sunos_nosys), %l6
+	b	2f
+	 or	%l6, %lo(sunos_nosys), %l6
+
+1:
+	set	sunos_sys_table, %l7
+	ld	[%l7 + %o0], %l6
+
+2:	
+	mov	%o1, %o0
+	mov	%o2, %o1
+	mov	%o3, %o2
+	mov	%o4, %o3
+	mov	%o5, %o4
+	call	%l6
+	 mov	%l4, %o7
+#endif
+
+	.align	4
+	.globl	sys_nis_syscall
+sys_nis_syscall:
+	mov	%o7, %l5
+	add	%sp, STACKFRAME_SZ, %o0		! pt_regs *regs arg
+	call	c_sys_nis_syscall
+	 mov	%l5, %o7
+
+	.align 4
+	.globl	sys_ptrace
+sys_ptrace:
+	call	do_ptrace
+	 add	%sp, STACKFRAME_SZ, %o0
+
+	ld	[%curptr + TI_FLAGS], %l5
+	andcc	%l5, _TIF_SYSCALL_TRACE, %g0
+	be	1f
+	 nop
+
+	call	syscall_trace
+	 nop
+
+1:
+	RESTORE_ALL
+
+	.align	4
+	.globl	sys_execve
+sys_execve:
+	mov	%o7, %l5
+	add	%sp, STACKFRAME_SZ, %o0		! pt_regs *regs arg
+	call	sparc_execve
+	 mov	%l5, %o7
+
+	.align	4
+	.globl	sys_pipe
+sys_pipe:
+	mov	%o7, %l5
+	add	%sp, STACKFRAME_SZ, %o0		! pt_regs *regs arg
+	call	sparc_pipe
+	 mov	%l5, %o7
+
+	.align	4
+	.globl	sys_sigaltstack
+sys_sigaltstack:
+	mov	%o7, %l5
+	mov	%fp, %o2
+	call	do_sigaltstack
+	 mov	%l5, %o7
+
+	.align	4
+	.globl	sys_sigstack
+sys_sigstack:
+	mov	%o7, %l5
+	mov	%fp, %o2
+	call	do_sys_sigstack
+	 mov	%l5, %o7
+
+	.align	4
+	.globl	sys_sigpause
+sys_sigpause:
+	/* Note: %o0 already has correct value... */
+	call	do_sigpause
+	 add	%sp, STACKFRAME_SZ, %o1
+
+	ld	[%curptr + TI_FLAGS], %l5
+	andcc	%l5, _TIF_SYSCALL_TRACE, %g0
+	be	1f
+	 nop
+
+	call	syscall_trace
+	 nop
+
+1:
+	/* We are returning to a signal handler. */
+	RESTORE_ALL
+
+	.align	4
+	.globl	sys_sigsuspend
+sys_sigsuspend:
+	call	do_sigsuspend
+	 add	%sp, STACKFRAME_SZ, %o0
+
+	ld	[%curptr + TI_FLAGS], %l5
+	andcc	%l5, _TIF_SYSCALL_TRACE, %g0
+	be	1f
+	 nop
+
+	call	syscall_trace
+	 nop
+
+1:
+	/* We are returning to a signal handler. */
+	RESTORE_ALL
+
+	.align	4
+	.globl	sys_rt_sigsuspend
+sys_rt_sigsuspend:
+	/* Note: %o0, %o1 already have correct value... */
+	call	do_rt_sigsuspend
+	 add	%sp, STACKFRAME_SZ, %o2
+
+	ld	[%curptr + TI_FLAGS], %l5
+	andcc	%l5, _TIF_SYSCALL_TRACE, %g0
+	be	1f
+	 nop
+
+	call	syscall_trace
+	 nop
+
+1:
+	/* We are returning to a signal handler. */
+	RESTORE_ALL
+
+	.align	4
+	.globl	sys_sigreturn
+sys_sigreturn:
+	call	do_sigreturn
+	 add	%sp, STACKFRAME_SZ, %o0
+
+	ld	[%curptr + TI_FLAGS], %l5
+	andcc	%l5, _TIF_SYSCALL_TRACE, %g0
+	be	1f
+	 nop
+
+	call	syscall_trace
+	 nop
+
+1:
+	/* We don't want to muck with user registers like a
+	 * normal syscall, just return.
+	 */
+	RESTORE_ALL
+
+	.align	4
+	.globl	sys_rt_sigreturn
+sys_rt_sigreturn:
+	call	do_rt_sigreturn
+	 add	%sp, STACKFRAME_SZ, %o0
+
+	ld	[%curptr + TI_FLAGS], %l5
+	andcc	%l5, _TIF_SYSCALL_TRACE, %g0
+	be	1f
+	 nop
+
+	call	syscall_trace
+	 nop
+
+1:
+	/* We are returning to a signal handler. */
+	RESTORE_ALL
+
+	/* Now that we have a real sys_clone, sys_fork() is
+	 * implemented in terms of it.  Our _real_ implementation
+	 * of SunOS vfork() will use sys_vfork().
+	 *
+	 * XXX These three should be consolidated into mostly shared
+	 * XXX code just like on sparc64... -DaveM
+	 */
+	.align	4
+	.globl	sys_fork, flush_patch_two
+sys_fork:
+	mov	%o7, %l5
+flush_patch_two:
+	FLUSH_ALL_KERNEL_WINDOWS;
+	ld	[%curptr + TI_TASK], %o4
+	rd	%psr, %g4
+	WRITE_PAUSE
+	mov	SIGCHLD, %o0			! arg0:	clone flags
+	rd	%wim, %g5
+	WRITE_PAUSE
+	mov	%fp, %o1			! arg1:	usp
+	std	%g4, [%o4 + AOFF_task_thread + AOFF_thread_fork_kpsr]
+	add	%sp, STACKFRAME_SZ, %o2		! arg2:	pt_regs ptr
+	mov	0, %o3
+	call	sparc_do_fork
+	 mov	%l5, %o7
+
+	/* Whee, kernel threads! */
+	.globl	sys_clone, flush_patch_three
+sys_clone:
+	mov	%o7, %l5
+flush_patch_three:
+	FLUSH_ALL_KERNEL_WINDOWS;
+	ld	[%curptr + TI_TASK], %o4
+	rd	%psr, %g4
+	WRITE_PAUSE
+
+	/* arg0,1: flags,usp  -- loaded already */
+	cmp	%o1, 0x0			! Is new_usp NULL?
+	rd	%wim, %g5
+	WRITE_PAUSE
+	be,a	1f
+	 mov	%fp, %o1			! yes, use callers usp
+	andn	%o1, 7, %o1			! no, align to 8 bytes
+1:
+	std	%g4, [%o4 + AOFF_task_thread + AOFF_thread_fork_kpsr]
+	add	%sp, STACKFRAME_SZ, %o2		! arg2:	pt_regs ptr
+	mov	0, %o3
+	call	sparc_do_fork
+	 mov	%l5, %o7
+
+	/* Whee, real vfork! */
+	.globl	sys_vfork, flush_patch_four
+sys_vfork:
+flush_patch_four:
+	FLUSH_ALL_KERNEL_WINDOWS;
+	ld	[%curptr + TI_TASK], %o4
+	rd	%psr, %g4
+	WRITE_PAUSE
+	rd	%wim, %g5
+	WRITE_PAUSE
+	std	%g4, [%o4 + AOFF_task_thread + AOFF_thread_fork_kpsr]
+	sethi	%hi(0x4000 | 0x0100 | SIGCHLD), %o0
+	mov	%fp, %o1
+	or	%o0, %lo(0x4000 | 0x0100 | SIGCHLD), %o0
+	sethi	%hi(sparc_do_fork), %l1
+	mov	0, %o3
+	jmpl	%l1 + %lo(sparc_do_fork), %g0
+	 add	%sp, STACKFRAME_SZ, %o2
+
+        .align  4
+linux_sparc_ni_syscall:
+	sethi   %hi(sys_ni_syscall), %l7
+	b       syscall_is_too_hard
+	 or     %l7, %lo(sys_ni_syscall), %l7
+
+linux_fast_syscall:
+	andn	%l7, 3, %l7
+	mov	%i0, %o0
+	mov	%i1, %o1
+	mov 	%i2, %o2
+	jmpl	%l7 + %g0, %g0
+	 mov	%i3, %o3
+
+linux_syscall_trace:
+	call	syscall_trace
+	 nop
+	mov	%i0, %o0
+	mov	%i1, %o1
+	mov	%i2, %o2
+	mov	%i3, %o3
+	b	2f
+	 mov	%i4, %o4
+
+	.globl	ret_from_fork
+ret_from_fork:
+	call	schedule_tail
+	 mov	%g3, %o0
+	b	ret_sys_call
+	 ld	[%sp + STACKFRAME_SZ + PT_I0], %o0
+
+	/* Linux native and SunOS system calls enter here... */
+	.align	4
+	.globl	linux_sparc_syscall
+linux_sparc_syscall:
+	/* Direct access to user regs, must faster. */
+	cmp	%g1, NR_SYSCALLS
+	bgeu	linux_sparc_ni_syscall
+	 sll	%g1, 2, %l4
+	ld	[%l7 + %l4], %l7
+	andcc	%l7, 1, %g0
+	bne	linux_fast_syscall
+	 /* Just do first insn from SAVE_ALL in the delay slot */
+
+	.globl	syscall_is_too_hard
+syscall_is_too_hard:
+	SAVE_ALL_HEAD
+	 rd	%wim, %l3
+
+	wr	%l0, PSR_ET, %psr
+	mov	%i0, %o0
+	mov	%i1, %o1
+	mov	%i2, %o2
+
+	ld	[%curptr + TI_FLAGS], %l5
+	mov	%i3, %o3
+	andcc	%l5, _TIF_SYSCALL_TRACE, %g0
+	mov	%i4, %o4
+	bne	linux_syscall_trace
+	 mov	%i0, %l5
+2:
+	call	%l7
+	 mov	%i5, %o5
+
+	st	%o0, [%sp + STACKFRAME_SZ + PT_I0]
+
+	.globl	ret_sys_call
+ret_sys_call:
+	ld	[%curptr + TI_FLAGS], %l6
+	cmp	%o0, -ERESTART_RESTARTBLOCK
+	ld	[%sp + STACKFRAME_SZ + PT_PSR], %g3
+	set	PSR_C, %g2
+	bgeu	1f
+	 andcc	%l6, _TIF_SYSCALL_TRACE, %g0
+
+	/* System call success, clear Carry condition code. */
+	andn	%g3, %g2, %g3
+	clr	%l6
+	st	%g3, [%sp + STACKFRAME_SZ + PT_PSR]	
+	bne	linux_syscall_trace2
+	 ld	[%sp + STACKFRAME_SZ + PT_NPC], %l1 /* pc = npc */
+	add	%l1, 0x4, %l2			/* npc = npc+4 */
+	st	%l1, [%sp + STACKFRAME_SZ + PT_PC]
+	b	ret_trap_entry
+	 st	%l2, [%sp + STACKFRAME_SZ + PT_NPC]
+1:
+	/* System call failure, set Carry condition code.
+	 * Also, get abs(errno) to return to the process.
+	 */
+	sub	%g0, %o0, %o0
+	or	%g3, %g2, %g3
+	st	%o0, [%sp + STACKFRAME_SZ + PT_I0]
+	mov	1, %l6
+	st	%g3, [%sp + STACKFRAME_SZ + PT_PSR]
+	bne	linux_syscall_trace2
+	 ld	[%sp + STACKFRAME_SZ + PT_NPC], %l1 /* pc = npc */
+	add	%l1, 0x4, %l2			/* npc = npc+4 */
+	st	%l1, [%sp + STACKFRAME_SZ + PT_PC]
+	b	ret_trap_entry
+	 st	%l2, [%sp + STACKFRAME_SZ + PT_NPC]
+
+linux_syscall_trace2:
+	call	syscall_trace
+	 add	%l1, 0x4, %l2			/* npc = npc+4 */
+	st	%l1, [%sp + STACKFRAME_SZ + PT_PC]
+	b	ret_trap_entry
+	 st	%l2, [%sp + STACKFRAME_SZ + PT_NPC]
+
+
+	/*
+	 * Solaris system calls and indirect system calls enter here.
+         *
+	 * I have named the solaris indirect syscalls like that because
+	 * it seems like Solaris has some fast path syscalls that can
+	 * be handled as indirect system calls. - mig
+	 */
+
+linux_syscall_for_solaris:
+	sethi	%hi(sys_call_table), %l7
+	b	linux_sparc_syscall
+	 or	%l7, %lo(sys_call_table), %l7
+	
+	.align	4
+	.globl	solaris_syscall
+solaris_syscall:
+	cmp	%g1,59
+	be	linux_syscall_for_solaris
+	 cmp	%g1,2
+	be	linux_syscall_for_solaris
+	 cmp    %g1,42
+	be      linux_syscall_for_solaris
+	 cmp	%g1,119
+	be,a	linux_syscall_for_solaris
+	 mov	2, %g1
+1:	
+	SAVE_ALL_HEAD
+	 rd	%wim, %l3
+
+	wr	%l0, PSR_ET, %psr
+	nop
+	nop
+	mov	%i0, %l5
+
+	call	do_solaris_syscall
+	 add	%sp, STACKFRAME_SZ, %o0
+
+	st	%o0, [%sp + STACKFRAME_SZ + PT_I0]
+	set	PSR_C, %g2
+	cmp	%o0, -ERESTART_RESTARTBLOCK
+	bgeu	1f
+	 ld	[%sp + STACKFRAME_SZ + PT_PSR], %g3
+
+	/* System call success, clear Carry condition code. */		
+	andn	%g3, %g2, %g3
+	clr	%l6
+	b	2f
+	 st	%g3, [%sp + STACKFRAME_SZ + PT_PSR]	
+
+1:
+	/* System call failure, set Carry condition code.
+	 * Also, get abs(errno) to return to the process.
+	 */
+	sub	%g0, %o0, %o0
+	mov	1, %l6
+	st	%o0, [%sp + STACKFRAME_SZ + PT_I0]
+	or	%g3, %g2, %g3
+	st	%g3, [%sp + STACKFRAME_SZ + PT_PSR]
+
+	/* Advance the pc and npc over the trap instruction.
+	 * If the npc is unaligned (has a 1 in the lower byte), it means
+	 * the kernel does not want us to play magic (ie, skipping over
+	 * traps).  Mainly when the Solaris code wants to set some PC and
+	 * nPC (setcontext).
+	 */
+2:
+	ld	[%sp + STACKFRAME_SZ + PT_NPC], %l1	/* pc  = npc   */
+	andcc	%l1, 1, %g0
+	bne	1f
+	 add	%l1, 0x4, %l2			/* npc = npc+4 */
+	st	%l1, [%sp + STACKFRAME_SZ + PT_PC]
+	b	ret_trap_entry
+	 st	%l2, [%sp + STACKFRAME_SZ + PT_NPC]
+
+	/* kernel knows what it is doing, fixup npc and continue */
+1:
+	sub	%l1, 1, %l1
+ 	b	ret_trap_entry	
+	 st	%l1, [%sp + STACKFRAME_SZ + PT_NPC]
+
+#ifndef CONFIG_SUNOS_EMUL
+	.align	4
+	.globl	sunos_syscall
+sunos_syscall:
+	SAVE_ALL_HEAD
+	 rd	%wim, %l3
+	wr	%l0, PSR_ET, %psr
+	nop
+	nop
+	mov	%i0, %l5
+	call	do_sunos_syscall
+	 add	%sp, STACKFRAME_SZ, %o0
+#endif
+
+	/* {net, open}bsd system calls enter here... */
+	.align	4
+	.globl	bsd_syscall
+bsd_syscall:
+	/* Direct access to user regs, must faster. */
+	cmp	%g1, NR_SYSCALLS
+	blu,a	1f
+	 sll	%g1, 2, %l4
+
+	set	sys_ni_syscall, %l7
+	b	bsd_is_too_hard
+	 nop
+
+1:
+	ld	[%l7 + %l4], %l7
+
+	.globl	bsd_is_too_hard
+bsd_is_too_hard:
+	rd	%wim, %l3
+	SAVE_ALL
+
+	wr	%l0, PSR_ET, %psr
+	WRITE_PAUSE
+
+2:
+	mov	%i0, %o0
+	mov	%i1, %o1
+	mov	%i2, %o2
+	mov	%i0, %l5
+	mov	%i3, %o3
+	mov	%i4, %o4
+	call	%l7
+	 mov	%i5, %o5
+
+	st	%o0, [%sp + STACKFRAME_SZ + PT_I0]
+	set	PSR_C, %g2
+	cmp	%o0, -ERESTART_RESTARTBLOCK
+	bgeu	1f
+	 ld	[%sp + STACKFRAME_SZ + PT_PSR], %g3
+
+	/* System call success, clear Carry condition code. */		
+	andn	%g3, %g2, %g3
+	clr	%l6
+	b	2f
+	 st	%g3, [%sp + STACKFRAME_SZ + PT_PSR]	
+
+1:
+	/* System call failure, set Carry condition code.
+	 * Also, get abs(errno) to return to the process.
+	 */
+	sub	%g0, %o0, %o0
+#if 0 /* XXX todo XXX */
+	sethi	%hi(bsd_xlatb_rorl), %o3
+	or	%o3, %lo(bsd_xlatb_rorl), %o3
+	sll	%o0, 2, %o0
+	ld	[%o3 + %o0], %o0
+#endif
+	mov	1, %l6
+	st	%o0, [%sp + STACKFRAME_SZ + PT_I0]
+	or	%g3, %g2, %g3
+	st	%g3, [%sp + STACKFRAME_SZ + PT_PSR]
+
+	/* Advance the pc and npc over the trap instruction. */
+2:
+	ld	[%sp + STACKFRAME_SZ + PT_NPC], %l1	/* pc  = npc   */
+	add	%l1, 0x4, %l2			/* npc = npc+4 */
+	st	%l1, [%sp + STACKFRAME_SZ + PT_PC]
+	b	ret_trap_entry
+	 st	%l2, [%sp + STACKFRAME_SZ + PT_NPC]
+
+/* Saving and restoring the FPU state is best done from lowlevel code.
+ *
+ * void fpsave(unsigned long *fpregs, unsigned long *fsr,
+ *             void *fpqueue, unsigned long *fpqdepth)
+ */
+
+	.globl	fpsave
+fpsave:
+	st	%fsr, [%o1]	! this can trap on us if fpu is in bogon state
+	ld	[%o1], %g1
+	set	0x2000, %g4
+	andcc	%g1, %g4, %g0
+	be	2f
+	 mov	0, %g2
+
+	/* We have an fpqueue to save. */
+1:
+	std	%fq, [%o2]
+fpsave_magic:
+	st	%fsr, [%o1]
+	ld	[%o1], %g3
+	andcc	%g3, %g4, %g0
+	add	%g2, 1, %g2
+	bne	1b
+	 add	%o2, 8, %o2
+
+2:
+	st	%g2, [%o3]
+
+	std	%f0, [%o0 + 0x00]
+	std	%f2, [%o0 + 0x08]
+	std	%f4, [%o0 + 0x10]
+	std	%f6, [%o0 + 0x18]
+	std	%f8, [%o0 + 0x20]
+	std	%f10, [%o0 + 0x28]
+	std	%f12, [%o0 + 0x30]
+	std	%f14, [%o0 + 0x38]
+	std	%f16, [%o0 + 0x40]
+	std	%f18, [%o0 + 0x48]
+	std	%f20, [%o0 + 0x50]
+	std	%f22, [%o0 + 0x58]
+	std	%f24, [%o0 + 0x60]
+	std	%f26, [%o0 + 0x68]
+	std	%f28, [%o0 + 0x70]
+	retl
+	 std	%f30, [%o0 + 0x78]
+
+	/* Thanks for Theo Deraadt and the authors of the Sprite/netbsd/openbsd
+	 * code for pointing out this possible deadlock, while we save state
+	 * above we could trap on the fsr store so our low level fpu trap
+	 * code has to know how to deal with this.
+	 */
+fpsave_catch:
+	b	fpsave_magic + 4
+	 st	%fsr, [%o1]
+
+fpsave_catch2:
+	b	fpsave + 4
+	 st	%fsr, [%o1]
+
+	/* void fpload(unsigned long *fpregs, unsigned long *fsr); */
+
+	.globl	fpload
+fpload:
+	ldd	[%o0 + 0x00], %f0
+	ldd	[%o0 + 0x08], %f2
+	ldd	[%o0 + 0x10], %f4
+	ldd	[%o0 + 0x18], %f6
+	ldd	[%o0 + 0x20], %f8
+	ldd	[%o0 + 0x28], %f10
+	ldd	[%o0 + 0x30], %f12
+	ldd	[%o0 + 0x38], %f14
+	ldd	[%o0 + 0x40], %f16
+	ldd	[%o0 + 0x48], %f18
+	ldd	[%o0 + 0x50], %f20
+	ldd	[%o0 + 0x58], %f22
+	ldd	[%o0 + 0x60], %f24
+	ldd	[%o0 + 0x68], %f26
+	ldd	[%o0 + 0x70], %f28
+	ldd	[%o0 + 0x78], %f30
+	ld	[%o1], %fsr
+	retl
+	 nop
+
+	/* __ndelay and __udelay take two arguments:
+	 * 0 - nsecs or usecs to delay
+	 * 1 - per_cpu udelay_val (loops per jiffy)
+	 *
+	 * Note that ndelay gives HZ times higher resolution but has a 10ms
+	 * limit.  udelay can handle up to 1s.
+	 */
+	.globl	__ndelay
+__ndelay:
+	save	%sp, -STACKFRAME_SZ, %sp
+	mov	%i0, %o0
+	call	.umul
+	 mov	0x1ad, %o1		! 2**32 / (1 000 000 000 / HZ)
+	call	.umul
+	 mov	%i1, %o1		! udelay_val
+	ba	delay_continue
+	 mov	%o1, %o0		! >>32 later for better resolution
+
+	.globl	__udelay
+__udelay:
+	save	%sp, -STACKFRAME_SZ, %sp
+	mov	%i0, %o0
+	sethi	%hi(0x10c6), %o1
+	call	.umul
+	 or	%o1, %lo(0x10c6), %o1	! 2**32 / 1 000 000
+	call	.umul
+	 mov	%i1, %o1		! udelay_val
+	call	.umul
+	 mov	HZ, %o0			! >>32 earlier for wider range
+
+delay_continue:
+	cmp	%o0, 0x0
+1:
+	bne	1b
+	 subcc	%o0, 1, %o0
+	
+	ret
+	restore
+
+	/* Handle a software breakpoint */
+	/* We have to inform parent that child has stopped */
+	.align 4
+	.globl breakpoint_trap
+breakpoint_trap:
+	rd	%wim,%l3
+	SAVE_ALL
+	wr 	%l0, PSR_ET, %psr
+	WRITE_PAUSE
+
+	st	%i0, [%sp + STACKFRAME_SZ + PT_G0] ! for restarting syscalls
+	call	sparc_breakpoint
+	 add	%sp, STACKFRAME_SZ, %o0
+
+	RESTORE_ALL
+
+	.align	4
+	.globl	__handle_exception, flush_patch_exception
+__handle_exception:
+flush_patch_exception:
+	FLUSH_ALL_KERNEL_WINDOWS;
+	ldd	[%o0], %o6
+	jmpl	%o7 + 0xc, %g0			! see asm-sparc/processor.h
+	 mov	1, %g1				! signal EFAULT condition
+
+	.align	4
+	.globl	kill_user_windows, kuw_patch1_7win
+	.globl	kuw_patch1
+kuw_patch1_7win:	sll	%o3, 6, %o3
+
+	/* No matter how much overhead this routine has in the worst
+	 * case scenerio, it is several times better than taking the
+	 * traps with the old method of just doing flush_user_windows().
+	 */
+kill_user_windows:
+	ld	[%g6 + TI_UWINMASK], %o0	! get current umask
+	orcc	%g0, %o0, %g0			! if no bits set, we are done
+	be	3f				! nothing to do
+	 rd	%psr, %o5			! must clear interrupts
+	or	%o5, PSR_PIL, %o4		! or else that could change
+	wr	%o4, 0x0, %psr			! the uwinmask state
+	WRITE_PAUSE				! burn them cycles
+1:
+	ld	[%g6 + TI_UWINMASK], %o0	! get consistent state
+	orcc	%g0, %o0, %g0			! did an interrupt come in?
+	be	4f				! yep, we are done
+	 rd	%wim, %o3			! get current wim
+	srl	%o3, 1, %o4			! simulate a save
+kuw_patch1:
+	sll	%o3, 7, %o3			! compute next wim
+	or	%o4, %o3, %o3			! result
+	andncc	%o0, %o3, %o0			! clean this bit in umask
+	bne	kuw_patch1			! not done yet
+	 srl	%o3, 1, %o4			! begin another save simulation
+	wr	%o3, 0x0, %wim			! set the new wim
+	st	%g0, [%g6 + TI_UWINMASK]	! clear uwinmask
+4:
+	wr	%o5, 0x0, %psr			! re-enable interrupts
+	WRITE_PAUSE				! burn baby burn
+3:
+	retl					! return
+	 st	%g0, [%g6 + TI_W_SAVED]		! no windows saved
+
+	.align	4
+	.globl	restore_current
+restore_current:
+	LOAD_CURRENT(g6, o0)
+	retl
+	 nop
+
+#ifdef CONFIG_PCI
+#include <asm/pcic.h>
+
+	.align	4
+	.globl	linux_trap_ipi15_pcic
+linux_trap_ipi15_pcic:
+	rd	%wim, %l3
+	SAVE_ALL
+
+	/*
+	 * First deactivate NMI
+	 * or we cannot drop ET, cannot get window spill traps.
+	 * The busy loop is necessary because the PIO error
+	 * sometimes does not go away quickly and we trap again.
+	 */
+	sethi	%hi(pcic_regs), %o1
+	ld	[%o1 + %lo(pcic_regs)], %o2
+
+	! Get pending status for printouts later.
+	ld	[%o2 + PCI_SYS_INT_PENDING], %o0
+
+	mov	PCI_SYS_INT_PENDING_CLEAR_ALL, %o1
+	stb	%o1, [%o2 + PCI_SYS_INT_PENDING_CLEAR]
+1:
+	ld	[%o2 + PCI_SYS_INT_PENDING], %o1
+	andcc	%o1, ((PCI_SYS_INT_PENDING_PIO|PCI_SYS_INT_PENDING_PCI)>>24), %g0
+	bne	1b
+	 nop
+
+	or	%l0, PSR_PIL, %l4
+	wr	%l4, 0x0, %psr
+	WRITE_PAUSE
+	wr	%l4, PSR_ET, %psr
+	WRITE_PAUSE
+
+	call	pcic_nmi
+	 add	%sp, STACKFRAME_SZ, %o1	! struct pt_regs *regs
+	RESTORE_ALL
+
+	.globl	pcic_nmi_trap_patch
+pcic_nmi_trap_patch:
+	sethi	%hi(linux_trap_ipi15_pcic), %l3
+	jmpl	%l3 + %lo(linux_trap_ipi15_pcic), %g0
+	 rd	%psr, %l0
+	.word	0
+
+#endif /* CONFIG_PCI */
+
+/* End of entry.S */
diff --git a/arch/sparc/kernel/errtbls.c b/arch/sparc/kernel/errtbls.c
new file mode 100644
index 0000000..bb36f6e
--- /dev/null
+++ b/arch/sparc/kernel/errtbls.c
@@ -0,0 +1,276 @@
+/* $Id: errtbls.c,v 1.2 1995/11/25 00:57:55 davem Exp $
+ * errtbls.c: Error number conversion tables between various syscall
+ *            OS semantics.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * Based upon preliminary work which is:
+ *
+ * Copyright (C) 1995 Adrian M. Rodriguez (adrian@remus.rutgers.edu)
+ */
+
+#include <asm/bsderrno.h>        /* NetBSD (bsd4.4) errnos */
+#include <asm/solerrno.h>        /* Solaris errnos */
+
+/* Here are tables which convert between Linux/SunOS error number
+ * values to the equivalent in other OSs.  Note that since the Linux
+ * ones have been set up to match exactly those of SunOS, no
+ * translation table is needed for that OS.
+ */
+
+int solaris_errno[] = {
+	0,
+	SOL_EPERM,
+	SOL_ENOENT,
+	SOL_ESRCH,
+	SOL_EINTR,
+	SOL_EIO,
+	SOL_ENXIO,
+	SOL_E2BIG,
+	SOL_ENOEXEC,
+	SOL_EBADF,
+	SOL_ECHILD,
+	SOL_EAGAIN,
+	SOL_ENOMEM,
+	SOL_EACCES,
+	SOL_EFAULT,
+	SOL_NOTBLK,
+	SOL_EBUSY,
+	SOL_EEXIST,
+	SOL_EXDEV,
+	SOL_ENODEV,
+	SOL_ENOTDIR,
+	SOL_EISDIR,
+	SOL_EINVAL,
+	SOL_ENFILE,
+	SOL_EMFILE,
+	SOL_ENOTTY,
+	SOL_ETXTBSY,
+	SOL_EFBIG,
+	SOL_ENOSPC,
+	SOL_ESPIPE,
+	SOL_EROFS,
+	SOL_EMLINK,
+	SOL_EPIPE,
+	SOL_EDOM,
+	SOL_ERANGE,
+	SOL_EWOULDBLOCK,
+	SOL_EINPROGRESS,
+	SOL_EALREADY,
+	SOL_ENOTSOCK,
+	SOL_EDESTADDRREQ,
+	SOL_EMSGSIZE,
+	SOL_EPROTOTYPE,
+	SOL_ENOPROTOOPT,
+	SOL_EPROTONOSUPPORT,
+	SOL_ESOCKTNOSUPPORT,
+	SOL_EOPNOTSUPP,
+	SOL_EPFNOSUPPORT,
+	SOL_EAFNOSUPPORT,
+	SOL_EADDRINUSE,
+	SOL_EADDRNOTAVAIL,
+	SOL_ENETDOWN,
+	SOL_ENETUNREACH,
+	SOL_ENETRESET,
+	SOL_ECONNABORTED,
+	SOL_ECONNRESET,
+	SOL_ENOBUFS,
+	SOL_EISCONN,
+	SOL_ENOTONN,
+	SOL_ESHUTDOWN,
+	SOL_ETOOMANYREFS,
+	SOL_ETIMEDOUT,
+	SOL_ECONNREFUSED,
+	SOL_ELOOP,
+	SOL_ENAMETOOLONG,
+	SOL_EHOSTDOWN,
+	SOL_EHOSTUNREACH,
+	SOL_ENOTEMPTY,
+	SOL_EPROCLIM,
+	SOL_EUSERS,
+	SOL_EDQUOT,
+	SOL_ESTALE,
+	SOL_EREMOTE,
+	SOL_ENOSTR,
+	SOL_ETIME,
+	SOL_ENOSR,
+	SOL_ENOMSG,
+	SOL_EBADMSG,
+	SOL_IDRM,
+	SOL_EDEADLK,
+	SOL_ENOLCK,
+	SOL_ENONET,
+	SOL_ERREMOTE,
+	SOL_ENOLINK,
+	SOL_EADV,
+	SOL_ESRMNT,
+	SOL_ECOMM,
+	SOL_EPROTO,
+	SOL_EMULTIHOP,
+	SOL_EINVAL,    /* EDOTDOT XXX??? */
+	SOL_REMCHG,
+	SOL_NOSYS,
+	SOL_STRPIPE,
+	SOL_EOVERFLOW,
+	SOL_EBADFD,
+	SOL_ECHRNG,
+	SOL_EL2NSYNC,
+	SOL_EL3HLT,
+	SOL_EL3RST,
+	SOL_NRNG,
+	SOL_EUNATCH,
+	SOL_ENOCSI,
+	SOL_EL2HLT,
+	SOL_EBADE,
+	SOL_EBADR,
+	SOL_EXFULL,
+	SOL_ENOANO,
+	SOL_EBADRQC,
+	SOL_EBADSLT,
+	SOL_EDEADLOCK,
+	SOL_EBFONT,
+	SOL_ELIBEXEC,
+	SOL_ENODATA,
+	SOL_ELIBBAD,
+	SOL_ENOPKG,
+	SOL_ELIBACC,
+	SOL_ENOTUNIQ,
+	SOL_ERESTART,
+	SOL_EUCLEAN,
+	SOL_ENOTNAM,
+	SOL_ENAVAIL,
+	SOL_EISNAM,
+	SOL_EREMOTEIO,
+	SOL_EILSEQ,
+	SOL_ELIBMAX,
+	SOL_ELIBSCN,
+};
+
+int netbsd_errno[] = {
+	0,
+	BSD_EPERM,
+	BSD_ENOENT,
+	BSD_ESRCH,
+	BSD_EINTR,
+	BSD_EIO,
+	BSD_ENXIO,
+	BSD_E2BIG,
+	BSD_ENOEXEC,
+	BSD_EBADF,
+	BSD_ECHILD,
+	BSD_EAGAIN,
+	BSD_ENOMEM,
+	BSD_EACCES,
+	BSD_EFAULT,
+	BSD_NOTBLK,
+	BSD_EBUSY,
+	BSD_EEXIST,
+	BSD_EXDEV,
+	BSD_ENODEV,
+	BSD_ENOTDIR,
+	BSD_EISDIR,
+	BSD_EINVAL,
+	BSD_ENFILE,
+	BSD_EMFILE,
+	BSD_ENOTTY,
+	BSD_ETXTBSY,
+	BSD_EFBIG,
+	BSD_ENOSPC,
+	BSD_ESPIPE,
+	BSD_EROFS,
+	BSD_EMLINK,
+	BSD_EPIPE,
+	BSD_EDOM,
+	BSD_ERANGE,
+	BSD_EWOULDBLOCK,
+	BSD_EINPROGRESS,
+	BSD_EALREADY,
+	BSD_ENOTSOCK,
+	BSD_EDESTADDRREQ,
+	BSD_EMSGSIZE,
+	BSD_EPROTOTYPE,
+	BSD_ENOPROTOOPT,
+	BSD_EPROTONOSUPPORT,
+	BSD_ESOCKTNOSUPPORT,
+	BSD_EOPNOTSUPP,
+	BSD_EPFNOSUPPORT,
+	BSD_EAFNOSUPPORT,
+	BSD_EADDRINUSE,
+	BSD_EADDRNOTAVAIL,
+	BSD_ENETDOWN,
+	BSD_ENETUNREACH,
+	BSD_ENETRESET,
+	BSD_ECONNABORTED,
+	BSD_ECONNRESET,
+	BSD_ENOBUFS,
+	BSD_EISCONN,
+	BSD_ENOTONN,
+	BSD_ESHUTDOWN,
+	BSD_ETOOMANYREFS,
+	BSD_ETIMEDOUT,
+	BSD_ECONNREFUSED,
+	BSD_ELOOP,
+	BSD_ENAMETOOLONG,
+	BSD_EHOSTDOWN,
+	BSD_EHOSTUNREACH,
+	BSD_ENOTEMPTY,
+	BSD_EPROCLIM,
+	BSD_EUSERS,
+	BSD_EDQUOT,
+	BSD_ESTALE,
+	BSD_EREMOTE,
+	BSD_ENOSTR,
+	BSD_ETIME,
+	BSD_ENOSR,
+	BSD_ENOMSG,
+	BSD_EBADMSG,
+	BSD_IDRM,
+	BSD_EDEADLK,
+	BSD_ENOLCK,
+	BSD_ENONET,
+	BSD_ERREMOTE,
+	BSD_ENOLINK,
+	BSD_EADV,
+	BSD_ESRMNT,
+	BSD_ECOMM,
+	BSD_EPROTO,
+	BSD_EMULTIHOP,
+	BSD_EINVAL,    /* EDOTDOT XXX??? */
+	BSD_REMCHG,
+	BSD_NOSYS,
+	BSD_STRPIPE,
+	BSD_EOVERFLOW,
+	BSD_EBADFD,
+	BSD_ECHRNG,
+	BSD_EL2NSYNC,
+	BSD_EL3HLT,
+	BSD_EL3RST,
+	BSD_NRNG,
+	BSD_EUNATCH,
+	BSD_ENOCSI,
+	BSD_EL2HLT,
+	BSD_EBADE,
+	BSD_EBADR,
+	BSD_EXFULL,
+	BSD_ENOANO,
+	BSD_EBADRQC,
+	BSD_EBADSLT,
+	BSD_EDEADLOCK,
+	BSD_EBFONT,
+	BSD_ELIBEXEC,
+	BSD_ENODATA,
+	BSD_ELIBBAD,
+	BSD_ENOPKG,
+	BSD_ELIBACC,
+	BSD_ENOTUNIQ,
+	BSD_ERESTART,
+	BSD_EUCLEAN,
+	BSD_ENOTNAM,
+	BSD_ENAVAIL,
+	BSD_EISNAM,
+	BSD_EREMOTEIO,
+	BSD_EILSEQ,
+	BSD_ELIBMAX,
+	BSD_ELIBSCN,
+};
+
diff --git a/arch/sparc/kernel/etrap.S b/arch/sparc/kernel/etrap.S
new file mode 100644
index 0000000..a8b35be
--- /dev/null
+++ b/arch/sparc/kernel/etrap.S
@@ -0,0 +1,321 @@
+/* $Id: etrap.S,v 1.31 2000/01/08 16:38:18 anton Exp $
+ * etrap.S: Sparc trap window preparation for entry into the
+ *          Linux kernel.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/head.h>
+#include <asm/asi.h>
+#include <asm/contregs.h>
+#include <asm/page.h>
+#include <asm/psr.h>
+#include <asm/ptrace.h>
+#include <asm/winmacro.h>
+#include <asm/asmmacro.h>
+#include <asm/thread_info.h>
+
+/* Registers to not touch at all. */
+#define t_psr        l0 /* Set by caller */
+#define t_pc         l1 /* Set by caller */
+#define t_npc        l2 /* Set by caller */
+#define t_wim        l3 /* Set by caller */
+#define t_twinmask   l4 /* Set at beginning of this entry routine. */
+#define t_kstack     l5 /* Set right before pt_regs frame is built */
+#define t_retpc      l6 /* If you change this, change winmacro.h header file */
+#define t_systable   l7 /* Never touch this, could be the syscall table ptr. */
+#define curptr       g6 /* Set after pt_regs frame is built */
+
+	.text
+	.align 4
+
+	/* SEVEN WINDOW PATCH INSTRUCTIONS */
+	.globl	tsetup_7win_patch1, tsetup_7win_patch2
+	.globl	tsetup_7win_patch3, tsetup_7win_patch4
+	.globl	tsetup_7win_patch5, tsetup_7win_patch6
+tsetup_7win_patch1:	sll	%t_wim, 0x6, %t_wim
+tsetup_7win_patch2:	and	%g2, 0x7f, %g2
+tsetup_7win_patch3:	and	%g2, 0x7f, %g2
+tsetup_7win_patch4:	and	%g1, 0x7f, %g1
+tsetup_7win_patch5:	sll	%t_wim, 0x6, %t_wim
+tsetup_7win_patch6:	and	%g2, 0x7f, %g2
+	/* END OF PATCH INSTRUCTIONS */
+
+	/* At trap time, interrupts and all generic traps do the
+	 * following:
+	 *
+	 * rd	%psr, %l0
+	 * b	some_handler
+	 * rd	%wim, %l3
+	 * nop
+	 *
+	 * Then 'some_handler' if it needs a trap frame (ie. it has
+	 * to call c-code and the trap cannot be handled in-window)
+	 * then it does the SAVE_ALL macro in entry.S which does
+	 *
+	 * sethi	%hi(trap_setup), %l4
+	 * jmpl		%l4 + %lo(trap_setup), %l6
+	 * nop
+	 */
+
+	/* 2 3 4  window number
+	 * -----
+	 * O T S  mnemonic
+	 *
+	 * O == Current window before trap
+	 * T == Window entered when trap occurred
+	 * S == Window we will need to save if (1<<T) == %wim
+	 *
+	 * Before execution gets here, it must be guaranteed that
+	 * %l0 contains trap time %psr, %l1 and %l2 contain the
+	 * trap pc and npc, and %l3 contains the trap time %wim.
+	 */
+
+	.globl	trap_setup, tsetup_patch1, tsetup_patch2
+	.globl	tsetup_patch3, tsetup_patch4
+	.globl	tsetup_patch5, tsetup_patch6
+trap_setup:
+	/* Calculate mask of trap window.  See if from user
+	 * or kernel and branch conditionally.
+	 */
+	mov	1, %t_twinmask
+	andcc	%t_psr, PSR_PS, %g0		 ! fromsupv_p = (psr & PSR_PS)
+	be	trap_setup_from_user		 ! nope, from user mode
+	 sll	%t_twinmask, %t_psr, %t_twinmask ! t_twinmask = (1 << psr)
+
+	/* From kernel, allocate more kernel stack and
+	 * build a pt_regs trap frame.
+	 */
+	sub	%fp, (STACKFRAME_SZ + TRACEREG_SZ), %t_kstack
+	STORE_PT_ALL(t_kstack, t_psr, t_pc, t_npc, g2)
+
+	/* See if we are in the trap window. */
+	andcc	%t_twinmask, %t_wim, %g0
+	bne	trap_setup_kernel_spill		! in trap window, clean up
+	 nop
+
+	/* Trap from kernel with a window available.
+	 * Just do it...
+	 */
+	jmpl	%t_retpc + 0x8, %g0	! return to caller
+	 mov	%t_kstack, %sp		! jump onto new stack
+
+trap_setup_kernel_spill:
+	ld	[%curptr + TI_UWINMASK], %g1
+	orcc	%g0, %g1, %g0
+	bne	trap_setup_user_spill	! there are some user windows, yuck
+	/* Spill from kernel, but only kernel windows, adjust
+	 * %wim and go.
+	 */
+	 srl	%t_wim, 0x1, %g2	! begin computation of new %wim
+tsetup_patch1:
+	sll	%t_wim, 0x7, %t_wim	! patched on 7 window Sparcs
+	or	%t_wim, %g2, %g2
+tsetup_patch2:
+	and	%g2, 0xff, %g2		! patched on 7 window Sparcs
+
+	save	%g0, %g0, %g0
+
+	/* Set new %wim value */
+	wr	%g2, 0x0, %wim
+
+	/* Save the kernel window onto the corresponding stack. */
+	STORE_WINDOW(sp)
+
+	restore	%g0, %g0, %g0
+
+	jmpl	%t_retpc + 0x8, %g0	! return to caller
+	 mov	%t_kstack, %sp		! and onto new kernel stack
+
+#define STACK_OFFSET (THREAD_SIZE - TRACEREG_SZ - STACKFRAME_SZ)
+
+trap_setup_from_user:
+	/* We can't use %curptr yet. */
+	LOAD_CURRENT(t_kstack, t_twinmask)
+
+	sethi	%hi(STACK_OFFSET), %t_twinmask
+	or	%t_twinmask, %lo(STACK_OFFSET), %t_twinmask
+	add	%t_kstack, %t_twinmask, %t_kstack
+
+	mov	1, %t_twinmask
+	sll	%t_twinmask, %t_psr, %t_twinmask ! t_twinmask = (1 << psr)
+
+	/* Build pt_regs frame. */
+	STORE_PT_ALL(t_kstack, t_psr, t_pc, t_npc, g2)
+
+#if 0
+	/* If we're sure every task_struct is THREAD_SIZE aligned,
+	   we can speed this up. */
+	sethi	%hi(STACK_OFFSET), %curptr
+	or	%curptr, %lo(STACK_OFFSET), %curptr
+	sub	%t_kstack, %curptr, %curptr
+#else
+	sethi	%hi(~(THREAD_SIZE - 1)), %curptr
+	and	%t_kstack, %curptr, %curptr
+#endif
+
+	/* Clear current_thread_info->w_saved */
+	st	%g0, [%curptr + TI_W_SAVED]
+
+	/* See if we are in the trap window. */
+	andcc	%t_twinmask, %t_wim, %g0
+	bne	trap_setup_user_spill		! yep we are
+	 orn	%g0, %t_twinmask, %g1		! negate trap win mask into %g1
+
+	/* Trap from user, but not into the invalid window.
+	 * Calculate new umask.  The way this works is,
+	 * any window from the %wim at trap time until
+	 * the window right before the one we are in now,
+	 * is a user window.  A diagram:
+	 *
+	 *      7 6 5 4 3 2 1 0    window number
+	 *      ---------------
+	 *        I     L T        mnemonic
+	 *
+	 * Window 'I' is the invalid window in our example,
+	 * window 'L' is the window the user was in when
+	 * the trap occurred, window T is the trap window
+	 * we are in now.  So therefore, windows 5, 4 and
+	 * 3 are user windows.  The following sequence
+	 * computes the user winmask to represent this.
+	 */
+	subcc	%t_wim, %t_twinmask, %g2
+	bneg,a	1f
+	 sub	%g2, 0x1, %g2
+1:
+	andn	%g2, %t_twinmask, %g2
+tsetup_patch3:
+	and	%g2, 0xff, %g2			! patched on 7win Sparcs
+	st	%g2, [%curptr + TI_UWINMASK]	! store new umask
+
+	jmpl	%t_retpc + 0x8, %g0		! return to caller
+	 mov	%t_kstack, %sp			! and onto kernel stack
+
+trap_setup_user_spill:
+	/* A spill occurred from either kernel or user mode
+	 * and there exist some user windows to deal with.
+	 * A mask of the currently valid user windows
+	 * is in %g1 upon entry to here.
+	 */
+
+tsetup_patch4:
+	and	%g1, 0xff, %g1		! patched on 7win Sparcs, mask
+	srl	%t_wim, 0x1, %g2	! compute new %wim
+tsetup_patch5:
+	sll	%t_wim, 0x7, %t_wim	! patched on 7win Sparcs
+	or	%t_wim, %g2, %g2	! %g2 is new %wim
+tsetup_patch6:
+	and	%g2, 0xff, %g2		! patched on 7win Sparcs
+	andn	%g1, %g2, %g1		! clear this bit in %g1
+	st	%g1, [%curptr + TI_UWINMASK]
+
+	save	%g0, %g0, %g0
+
+	wr	%g2, 0x0, %wim
+
+	/* Call MMU-architecture dependent stack checking
+	 * routine.
+	 */
+	.globl	tsetup_mmu_patchme
+tsetup_mmu_patchme:
+	b	tsetup_sun4c_stackchk
+	 andcc	%sp, 0x7, %g0
+
+	/* Architecture specific stack checking routines.  When either
+	 * of these routines are called, the globals are free to use
+	 * as they have been safely stashed on the new kernel stack
+	 * pointer.  Thus the definition below for simplicity.
+	 */
+#define glob_tmp     g1
+
+	.globl	tsetup_sun4c_stackchk
+tsetup_sun4c_stackchk:
+	/* Done by caller: andcc %sp, 0x7, %g0 */
+	bne	trap_setup_user_stack_is_bolixed
+	 sra	%sp, 29, %glob_tmp
+
+	add	%glob_tmp, 0x1, %glob_tmp
+	andncc	%glob_tmp, 0x1, %g0
+	bne	trap_setup_user_stack_is_bolixed
+	 and	%sp, 0xfff, %glob_tmp		! delay slot
+
+	/* See if our dump area will be on more than one
+	 * page.
+	 */
+	add	%glob_tmp, 0x38, %glob_tmp
+	andncc	%glob_tmp, 0xff8, %g0
+	be	tsetup_sun4c_onepage		! only one page to check
+	 lda	[%sp] ASI_PTE, %glob_tmp	! have to check first page anyways
+
+tsetup_sun4c_twopages:
+	/* Is first page ok permission wise? */
+	srl	%glob_tmp, 29, %glob_tmp
+	cmp	%glob_tmp, 0x6
+	bne	trap_setup_user_stack_is_bolixed
+	 add	%sp, 0x38, %glob_tmp		/* Is second page in vma hole? */
+
+	sra	%glob_tmp, 29, %glob_tmp
+	add	%glob_tmp, 0x1, %glob_tmp
+	andncc	%glob_tmp, 0x1, %g0
+	bne	trap_setup_user_stack_is_bolixed
+	 add	%sp, 0x38, %glob_tmp
+
+	lda	[%glob_tmp] ASI_PTE, %glob_tmp
+
+tsetup_sun4c_onepage:
+	srl	%glob_tmp, 29, %glob_tmp
+	cmp	%glob_tmp, 0x6				! can user write to it?
+	bne	trap_setup_user_stack_is_bolixed	! failure
+	 nop
+
+	STORE_WINDOW(sp)
+
+	restore %g0, %g0, %g0
+
+	jmpl	%t_retpc + 0x8, %g0
+	 mov	%t_kstack, %sp
+
+	.globl	tsetup_srmmu_stackchk
+tsetup_srmmu_stackchk:
+	/* Check results of callers andcc %sp, 0x7, %g0 */
+	bne	trap_setup_user_stack_is_bolixed
+	 sethi   %hi(PAGE_OFFSET), %glob_tmp
+
+	cmp	%glob_tmp, %sp
+	bleu,a	1f
+	 lda	[%g0] ASI_M_MMUREGS, %glob_tmp		! read MMU control
+
+trap_setup_user_stack_is_bolixed:
+	/* From user/kernel into invalid window w/bad user
+	 * stack. Save bad user stack, and return to caller.
+	 */
+	SAVE_BOLIXED_USER_STACK(curptr, g3)
+	restore	%g0, %g0, %g0
+
+	jmpl	%t_retpc + 0x8, %g0
+	 mov	%t_kstack, %sp
+
+1:
+	/* Clear the fault status and turn on the no_fault bit. */
+	or	%glob_tmp, 0x2, %glob_tmp		! or in no_fault bit
+	sta	%glob_tmp, [%g0] ASI_M_MMUREGS		! set it
+
+	/* Dump the registers and cross fingers. */
+	STORE_WINDOW(sp)
+
+	/* Clear the no_fault bit and check the status. */
+	andn	%glob_tmp, 0x2, %glob_tmp
+	sta	%glob_tmp, [%g0] ASI_M_MMUREGS
+	mov	AC_M_SFAR, %glob_tmp
+	lda	[%glob_tmp] ASI_M_MMUREGS, %g0
+	mov	AC_M_SFSR, %glob_tmp
+	lda	[%glob_tmp] ASI_M_MMUREGS, %glob_tmp	! save away status of winstore
+	andcc	%glob_tmp, 0x2, %g0			! did we fault?
+	bne	trap_setup_user_stack_is_bolixed	! failure
+	 nop
+
+	restore %g0, %g0, %g0
+
+	jmpl	%t_retpc + 0x8, %g0
+	 mov	%t_kstack, %sp
+
diff --git a/arch/sparc/kernel/head.S b/arch/sparc/kernel/head.S
new file mode 100644
index 0000000..42d3de5
--- /dev/null
+++ b/arch/sparc/kernel/head.S
@@ -0,0 +1,1326 @@
+/* $Id: head.S,v 1.105 2001/08/12 09:08:56 davem Exp $
+ * head.S: The initial boot code for the Sparc port of Linux.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995,1999 Pete Zaitcev   (zaitcev@yahoo.com)
+ * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Copyright (C) 1997 Jakub Jelinek   (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997 Michael A. Griffith (grif@acm.org)
+ *
+ * CompactPCI platform by Eric Brower, 1999.
+ */
+
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/init.h>
+
+#include <asm/head.h>
+#include <asm/asi.h>
+#include <asm/contregs.h>
+#include <asm/ptrace.h>
+#include <asm/psr.h>
+#include <asm/page.h>
+#include <asm/kdebug.h>
+#include <asm/winmacro.h>
+#include <asm/thread_info.h>	/* TI_UWINMASK */
+#include <asm/errno.h>
+#include <asm/pgtsrmmu.h>	/* SRMMU_PGDIR_SHIFT */
+
+	.data
+/* 
+ * The following are used with the prom_vector node-ops to figure out
+ * the cpu-type 
+ */
+
+	.align 4
+        .globl  cputyp
+cputyp:
+        .word   1
+
+	.align 4
+	.globl cputypval
+cputypval:
+	.asciz "sun4c"
+	.ascii "     "
+
+cputypvalend:
+cputypvallen = cputypvar - cputypval
+
+	.align 4
+/*
+ * Sun people can't spell worth damn. "compatability" indeed.
+ * At least we *know* we can't spell, and use a spell-checker.
+ */
+
+/* Uh, actually Linus it is I who cannot spell. Too much murky
+ * Sparc assembly will do this to ya.
+ */
+cputypvar:
+	.asciz "compatability"
+
+/* Tested on SS-5, SS-10. Probably someone at Sun applied a spell-checker. */
+	.align 4
+cputypvar_sun4m:
+	.asciz "compatible"
+
+	.align 4
+
+#ifndef CONFIG_SUN4
+sun4_notsup:
+	.asciz	"Sparc-Linux sun4 needs a specially compiled kernel, turn CONFIG_SUN4 on.\n\n"
+	.align 4
+#else
+sun4cdm_notsup:
+	.asciz	"Kernel compiled with CONFIG_SUN4 cannot run on SUN4C/SUN4M/SUN4D\nTurn CONFIG_SUN4 off.\n\n"
+	.align 4
+#endif
+
+sun4e_notsup:
+        .asciz  "Sparc-Linux sun4e support does not exist\n\n"
+	.align 4
+
+#ifndef CONFIG_SUNOS_EMUL
+#undef SUNOS_SYSCALL_TRAP
+#define SUNOS_SYSCALL_TRAP SUNOS_NO_SYSCALL_TRAP
+#endif
+
+	/* The Sparc trap table, bootloader gives us control at _start. */
+	.text
+	.globl	start, _stext, _start, __stext
+	.globl  trapbase
+_start:   /* danger danger */
+__stext:
+_stext:
+start:
+trapbase:
+#ifdef CONFIG_SMP
+trapbase_cpu0:
+#endif
+/* We get control passed to us here at t_zero. */
+t_zero:	b gokernel; nop; nop; nop;
+t_tflt:	SPARC_TFAULT                        /* Inst. Access Exception        */
+t_bins:	TRAP_ENTRY(0x2, bad_instruction)    /* Illegal Instruction           */
+t_pins:	TRAP_ENTRY(0x3, priv_instruction)   /* Privileged Instruction        */
+t_fpd:	TRAP_ENTRY(0x4, fpd_trap_handler)   /* Floating Point Disabled       */
+t_wovf:	WINDOW_SPILL                        /* Window Overflow               */
+t_wunf:	WINDOW_FILL                         /* Window Underflow              */
+t_mna:	TRAP_ENTRY(0x7, mna_handler)        /* Memory Address Not Aligned    */
+t_fpe:	TRAP_ENTRY(0x8, fpe_trap_handler)   /* Floating Point Exception      */
+t_dflt:	SPARC_DFAULT                        /* Data Miss Exception           */
+t_tio:	TRAP_ENTRY(0xa, do_tag_overflow)    /* Tagged Instruction Ovrflw     */
+t_wpt:	TRAP_ENTRY(0xb, do_watchpoint)      /* Watchpoint Detected           */
+t_badc:	BAD_TRAP(0xc) BAD_TRAP(0xd) BAD_TRAP(0xe) BAD_TRAP(0xf) BAD_TRAP(0x10)
+t_irq1:	TRAP_ENTRY_INTERRUPT(1)             /* IRQ Software/SBUS Level 1     */
+t_irq2:	TRAP_ENTRY_INTERRUPT(2)             /* IRQ SBUS Level 2              */
+t_irq3:	TRAP_ENTRY_INTERRUPT(3)             /* IRQ SCSI/DMA/SBUS Level 3     */
+t_irq4:	TRAP_ENTRY_INTERRUPT(4)             /* IRQ Software Level 4          */
+t_irq5:	TRAP_ENTRY_INTERRUPT(5)             /* IRQ SBUS/Ethernet Level 5     */
+t_irq6:	TRAP_ENTRY_INTERRUPT(6)             /* IRQ Software Level 6          */
+t_irq7:	TRAP_ENTRY_INTERRUPT(7)             /* IRQ Video/SBUS Level 5        */
+t_irq8:	TRAP_ENTRY_INTERRUPT(8)             /* IRQ SBUS Level 6              */
+t_irq9:	TRAP_ENTRY_INTERRUPT(9)             /* IRQ SBUS Level 7              */
+t_irq10:TRAP_ENTRY_INTERRUPT(10)            /* IRQ Timer #1 (one we use)     */
+t_irq11:TRAP_ENTRY_INTERRUPT(11)            /* IRQ Floppy Intr.              */
+t_irq12:TRAP_ENTRY_INTERRUPT(12)            /* IRQ Zilog serial chip         */
+t_irq13:TRAP_ENTRY_INTERRUPT(13)            /* IRQ Audio Intr.               */
+t_irq14:TRAP_ENTRY_INTERRUPT(14)            /* IRQ Timer #2                  */
+	.globl	t_nmi
+#ifndef CONFIG_SMP
+t_nmi:	NMI_TRAP                            /* Level 15 (NMI)                */
+#else
+t_nmi:	TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
+#endif
+t_racc:	TRAP_ENTRY(0x20, do_reg_access)     /* General Register Access Error */
+t_iacce:BAD_TRAP(0x21)                      /* Instr Access Error            */
+t_bad22:BAD_TRAP(0x22) BAD_TRAP(0x23)
+t_cpdis:TRAP_ENTRY(0x24, do_cp_disabled)    /* Co-Processor Disabled         */
+t_uflsh:SKIP_TRAP(0x25, unimp_flush)        /* Unimplemented FLUSH inst.     */
+t_bad26:BAD_TRAP(0x26) BAD_TRAP(0x27)
+t_cpexc:TRAP_ENTRY(0x28, do_cp_exception)   /* Co-Processor Exception        */
+t_dacce:SPARC_DFAULT                        /* Data Access Error             */
+t_hwdz:	TRAP_ENTRY(0x2a, do_hw_divzero)     /* Division by zero, you lose... */
+t_dserr:BAD_TRAP(0x2b)                      /* Data Store Error              */
+t_daccm:BAD_TRAP(0x2c)                      /* Data Access MMU-Miss          */
+t_bad2d:BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
+t_bad32:BAD_TRAP(0x32) BAD_TRAP(0x33) BAD_TRAP(0x34) BAD_TRAP(0x35) BAD_TRAP(0x36)
+t_bad37:BAD_TRAP(0x37) BAD_TRAP(0x38) BAD_TRAP(0x39) BAD_TRAP(0x3a) BAD_TRAP(0x3b)
+t_iaccm:BAD_TRAP(0x3c)                      /* Instr Access MMU-Miss         */
+t_bad3d:BAD_TRAP(0x3d) BAD_TRAP(0x3e) BAD_TRAP(0x3f) BAD_TRAP(0x40) BAD_TRAP(0x41)
+t_bad42:BAD_TRAP(0x42) BAD_TRAP(0x43) BAD_TRAP(0x44) BAD_TRAP(0x45) BAD_TRAP(0x46)
+t_bad47:BAD_TRAP(0x47) BAD_TRAP(0x48) BAD_TRAP(0x49) BAD_TRAP(0x4a) BAD_TRAP(0x4b)
+t_bad4c:BAD_TRAP(0x4c) BAD_TRAP(0x4d) BAD_TRAP(0x4e) BAD_TRAP(0x4f) BAD_TRAP(0x50)
+t_bad51:BAD_TRAP(0x51) BAD_TRAP(0x52) BAD_TRAP(0x53) BAD_TRAP(0x54) BAD_TRAP(0x55)
+t_bad56:BAD_TRAP(0x56) BAD_TRAP(0x57) BAD_TRAP(0x58) BAD_TRAP(0x59) BAD_TRAP(0x5a)
+t_bad5b:BAD_TRAP(0x5b) BAD_TRAP(0x5c) BAD_TRAP(0x5d) BAD_TRAP(0x5e) BAD_TRAP(0x5f)
+t_bad60:BAD_TRAP(0x60) BAD_TRAP(0x61) BAD_TRAP(0x62) BAD_TRAP(0x63) BAD_TRAP(0x64)
+t_bad65:BAD_TRAP(0x65) BAD_TRAP(0x66) BAD_TRAP(0x67) BAD_TRAP(0x68) BAD_TRAP(0x69)
+t_bad6a:BAD_TRAP(0x6a) BAD_TRAP(0x6b) BAD_TRAP(0x6c) BAD_TRAP(0x6d) BAD_TRAP(0x6e)
+t_bad6f:BAD_TRAP(0x6f) BAD_TRAP(0x70) BAD_TRAP(0x71) BAD_TRAP(0x72) BAD_TRAP(0x73)
+t_bad74:BAD_TRAP(0x74) BAD_TRAP(0x75) BAD_TRAP(0x76) BAD_TRAP(0x77) BAD_TRAP(0x78)
+t_bad79:BAD_TRAP(0x79) BAD_TRAP(0x7a) BAD_TRAP(0x7b) BAD_TRAP(0x7c) BAD_TRAP(0x7d)
+t_bad7e:BAD_TRAP(0x7e) BAD_TRAP(0x7f)
+t_sunos:SUNOS_SYSCALL_TRAP                  /* SunOS System Call             */
+t_sbkpt:BREAKPOINT_TRAP                     /* Software Breakpoint/KGDB      */
+t_divz:	TRAP_ENTRY(0x82, do_hw_divzero)     /* Divide by zero trap           */
+t_flwin:TRAP_ENTRY(0x83, do_flush_windows)  /* Flush Windows Trap            */
+t_clwin:BAD_TRAP(0x84)                      /* Clean Windows Trap            */
+t_rchk:	BAD_TRAP(0x85)                      /* Range Check                   */
+t_funal:BAD_TRAP(0x86)                      /* Fix Unaligned Access Trap     */
+t_iovf:	BAD_TRAP(0x87)                      /* Integer Overflow Trap         */
+t_slowl:SOLARIS_SYSCALL_TRAP                /* Slowaris System Call          */
+t_netbs:NETBSD_SYSCALL_TRAP                 /* Net-B.S. System Call          */
+t_bad8a:BAD_TRAP(0x8a) BAD_TRAP(0x8b) BAD_TRAP(0x8c) BAD_TRAP(0x8d) BAD_TRAP(0x8e)
+t_bad8f:BAD_TRAP(0x8f)
+t_linux:LINUX_SYSCALL_TRAP                  /* Linux System Call             */
+t_bad91:BAD_TRAP(0x91) BAD_TRAP(0x92) BAD_TRAP(0x93) BAD_TRAP(0x94) BAD_TRAP(0x95)
+t_bad96:BAD_TRAP(0x96) BAD_TRAP(0x97) BAD_TRAP(0x98) BAD_TRAP(0x99) BAD_TRAP(0x9a)
+t_bad9b:BAD_TRAP(0x9b) BAD_TRAP(0x9c) BAD_TRAP(0x9d) BAD_TRAP(0x9e) BAD_TRAP(0x9f)
+t_getcc:GETCC_TRAP                          /* Get Condition Codes           */
+t_setcc:SETCC_TRAP                          /* Set Condition Codes           */
+t_getpsr:GETPSR_TRAP                        /* Get PSR Register              */
+t_bada3:BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6)
+t_slowi:INDIRECT_SOLARIS_SYSCALL(156)
+t_bada8:BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab)
+t_badac:BAD_TRAP(0xac) BAD_TRAP(0xad) BAD_TRAP(0xae) BAD_TRAP(0xaf) BAD_TRAP(0xb0)
+t_badb1:BAD_TRAP(0xb1) BAD_TRAP(0xb2) BAD_TRAP(0xb3) BAD_TRAP(0xb4) BAD_TRAP(0xb5)
+t_badb6:BAD_TRAP(0xb6) BAD_TRAP(0xb7) BAD_TRAP(0xb8) BAD_TRAP(0xb9) BAD_TRAP(0xba)
+t_badbb:BAD_TRAP(0xbb) BAD_TRAP(0xbc) BAD_TRAP(0xbd) BAD_TRAP(0xbe) BAD_TRAP(0xbf)
+t_badc0:BAD_TRAP(0xc0) BAD_TRAP(0xc1) BAD_TRAP(0xc2) BAD_TRAP(0xc3) BAD_TRAP(0xc4)
+t_badc5:BAD_TRAP(0xc5) BAD_TRAP(0xc6) BAD_TRAP(0xc7) BAD_TRAP(0xc8) BAD_TRAP(0xc9)
+t_badca:BAD_TRAP(0xca) BAD_TRAP(0xcb) BAD_TRAP(0xcc) BAD_TRAP(0xcd) BAD_TRAP(0xce)
+t_badcf:BAD_TRAP(0xcf) BAD_TRAP(0xd0) BAD_TRAP(0xd1) BAD_TRAP(0xd2) BAD_TRAP(0xd3)
+t_badd4:BAD_TRAP(0xd4) BAD_TRAP(0xd5) BAD_TRAP(0xd6) BAD_TRAP(0xd7) BAD_TRAP(0xd8)
+t_badd9:BAD_TRAP(0xd9) BAD_TRAP(0xda) BAD_TRAP(0xdb) BAD_TRAP(0xdc) BAD_TRAP(0xdd)
+t_badde:BAD_TRAP(0xde) BAD_TRAP(0xdf) BAD_TRAP(0xe0) BAD_TRAP(0xe1) BAD_TRAP(0xe2)
+t_bade3:BAD_TRAP(0xe3) BAD_TRAP(0xe4) BAD_TRAP(0xe5) BAD_TRAP(0xe6) BAD_TRAP(0xe7)
+t_bade8:BAD_TRAP(0xe8) BAD_TRAP(0xe9) BAD_TRAP(0xea) BAD_TRAP(0xeb) BAD_TRAP(0xec)
+t_baded:BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1)
+t_badf2:BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6)
+t_badf7:BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb)
+t_badfc:BAD_TRAP(0xfc) BAD_TRAP(0xfd)
+dbtrap:	BAD_TRAP(0xfe)                      /* Debugger/PROM breakpoint #1   */
+dbtrap2:BAD_TRAP(0xff)                      /* Debugger/PROM breakpoint #2   */	
+
+	.globl	end_traptable
+end_traptable:
+
+#ifdef CONFIG_SMP
+	/* Trap tables for the other cpus. */
+	.globl	trapbase_cpu1, trapbase_cpu2, trapbase_cpu3
+trapbase_cpu1:
+	BAD_TRAP(0x0) SRMMU_TFAULT TRAP_ENTRY(0x2, bad_instruction)
+	TRAP_ENTRY(0x3, priv_instruction) TRAP_ENTRY(0x4, fpd_trap_handler)
+	WINDOW_SPILL WINDOW_FILL TRAP_ENTRY(0x7, mna_handler)
+	TRAP_ENTRY(0x8, fpe_trap_handler) SRMMU_DFAULT
+	TRAP_ENTRY(0xa, do_tag_overflow) TRAP_ENTRY(0xb, do_watchpoint)
+	BAD_TRAP(0xc) BAD_TRAP(0xd) BAD_TRAP(0xe) BAD_TRAP(0xf) BAD_TRAP(0x10)
+	TRAP_ENTRY_INTERRUPT(1) TRAP_ENTRY_INTERRUPT(2)
+	TRAP_ENTRY_INTERRUPT(3) TRAP_ENTRY_INTERRUPT(4)
+	TRAP_ENTRY_INTERRUPT(5) TRAP_ENTRY_INTERRUPT(6)
+	TRAP_ENTRY_INTERRUPT(7)	TRAP_ENTRY_INTERRUPT(8)
+	TRAP_ENTRY_INTERRUPT(9) TRAP_ENTRY_INTERRUPT(10)
+	TRAP_ENTRY_INTERRUPT(11) TRAP_ENTRY_INTERRUPT(12)
+	TRAP_ENTRY_INTERRUPT(13) TRAP_ENTRY_INTERRUPT(14)
+	TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
+	TRAP_ENTRY(0x20, do_reg_access) BAD_TRAP(0x21) BAD_TRAP(0x22)
+	BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) SKIP_TRAP(0x25, unimp_flush)
+	BAD_TRAP(0x26) BAD_TRAP(0x27) TRAP_ENTRY(0x28, do_cp_exception)
+	SRMMU_DFAULT TRAP_ENTRY(0x2a, do_hw_divzero) BAD_TRAP(0x2b) BAD_TRAP(0x2c)
+	BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
+	BAD_TRAP(0x32) BAD_TRAP(0x33) BAD_TRAP(0x34) BAD_TRAP(0x35) BAD_TRAP(0x36)
+	BAD_TRAP(0x37) BAD_TRAP(0x38) BAD_TRAP(0x39) BAD_TRAP(0x3a) BAD_TRAP(0x3b)
+	BAD_TRAP(0x3c) BAD_TRAP(0x3d) BAD_TRAP(0x3e) BAD_TRAP(0x3f) BAD_TRAP(0x40)
+	BAD_TRAP(0x41) BAD_TRAP(0x42) BAD_TRAP(0x43) BAD_TRAP(0x44) BAD_TRAP(0x45)
+	BAD_TRAP(0x46) BAD_TRAP(0x47) BAD_TRAP(0x48) BAD_TRAP(0x49) BAD_TRAP(0x4a)
+	BAD_TRAP(0x4b) BAD_TRAP(0x4c) BAD_TRAP(0x4d) BAD_TRAP(0x4e) BAD_TRAP(0x4f)
+	BAD_TRAP(0x50)
+	BAD_TRAP(0x51) BAD_TRAP(0x52) BAD_TRAP(0x53) BAD_TRAP(0x54) BAD_TRAP(0x55)
+	BAD_TRAP(0x56) BAD_TRAP(0x57) BAD_TRAP(0x58) BAD_TRAP(0x59) BAD_TRAP(0x5a)
+	BAD_TRAP(0x5b) BAD_TRAP(0x5c) BAD_TRAP(0x5d) BAD_TRAP(0x5e) BAD_TRAP(0x5f)
+	BAD_TRAP(0x60) BAD_TRAP(0x61) BAD_TRAP(0x62) BAD_TRAP(0x63) BAD_TRAP(0x64)
+	BAD_TRAP(0x65) BAD_TRAP(0x66) BAD_TRAP(0x67) BAD_TRAP(0x68) BAD_TRAP(0x69)
+	BAD_TRAP(0x6a) BAD_TRAP(0x6b) BAD_TRAP(0x6c) BAD_TRAP(0x6d) BAD_TRAP(0x6e)
+	BAD_TRAP(0x6f) BAD_TRAP(0x70) BAD_TRAP(0x71) BAD_TRAP(0x72) BAD_TRAP(0x73)
+	BAD_TRAP(0x74) BAD_TRAP(0x75) BAD_TRAP(0x76) BAD_TRAP(0x77) BAD_TRAP(0x78)
+	BAD_TRAP(0x79) BAD_TRAP(0x7a) BAD_TRAP(0x7b) BAD_TRAP(0x7c) BAD_TRAP(0x7d)
+	BAD_TRAP(0x7e) BAD_TRAP(0x7f)
+	SUNOS_SYSCALL_TRAP 
+	BREAKPOINT_TRAP
+	TRAP_ENTRY(0x82, do_hw_divzero)
+	TRAP_ENTRY(0x83, do_flush_windows) BAD_TRAP(0x84) BAD_TRAP(0x85)
+	BAD_TRAP(0x86) BAD_TRAP(0x87) SOLARIS_SYSCALL_TRAP
+	NETBSD_SYSCALL_TRAP BAD_TRAP(0x8a) BAD_TRAP(0x8b) BAD_TRAP(0x8c)
+	BAD_TRAP(0x8d) BAD_TRAP(0x8e) BAD_TRAP(0x8f)
+	LINUX_SYSCALL_TRAP BAD_TRAP(0x91) BAD_TRAP(0x92) BAD_TRAP(0x93) BAD_TRAP(0x94)
+	BAD_TRAP(0x95) BAD_TRAP(0x96) BAD_TRAP(0x97) BAD_TRAP(0x98) BAD_TRAP(0x99)
+	BAD_TRAP(0x9a) BAD_TRAP(0x9b) BAD_TRAP(0x9c) BAD_TRAP(0x9d) BAD_TRAP(0x9e)
+	BAD_TRAP(0x9f) GETCC_TRAP SETCC_TRAP GETPSR_TRAP
+	BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6)
+	INDIRECT_SOLARIS_SYSCALL(156) BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab)
+	BAD_TRAP(0xac) BAD_TRAP(0xad) BAD_TRAP(0xae) BAD_TRAP(0xaf) BAD_TRAP(0xb0)
+	BAD_TRAP(0xb1) BAD_TRAP(0xb2) BAD_TRAP(0xb3) BAD_TRAP(0xb4) BAD_TRAP(0xb5)
+	BAD_TRAP(0xb6) BAD_TRAP(0xb7) BAD_TRAP(0xb8) BAD_TRAP(0xb9) BAD_TRAP(0xba)
+	BAD_TRAP(0xbb) BAD_TRAP(0xbc) BAD_TRAP(0xbd) BAD_TRAP(0xbe) BAD_TRAP(0xbf)
+	BAD_TRAP(0xc0) BAD_TRAP(0xc1) BAD_TRAP(0xc2) BAD_TRAP(0xc3) BAD_TRAP(0xc4)
+	BAD_TRAP(0xc5) BAD_TRAP(0xc6) BAD_TRAP(0xc7) BAD_TRAP(0xc8) BAD_TRAP(0xc9)
+	BAD_TRAP(0xca) BAD_TRAP(0xcb) BAD_TRAP(0xcc) BAD_TRAP(0xcd) BAD_TRAP(0xce)
+	BAD_TRAP(0xcf) BAD_TRAP(0xd0) BAD_TRAP(0xd1) BAD_TRAP(0xd2) BAD_TRAP(0xd3)
+	BAD_TRAP(0xd4) BAD_TRAP(0xd5) BAD_TRAP(0xd6) BAD_TRAP(0xd7) BAD_TRAP(0xd8)
+	BAD_TRAP(0xd9) BAD_TRAP(0xda) BAD_TRAP(0xdb) BAD_TRAP(0xdc) BAD_TRAP(0xdd)
+	BAD_TRAP(0xde) BAD_TRAP(0xdf) BAD_TRAP(0xe0) BAD_TRAP(0xe1) BAD_TRAP(0xe2)
+	BAD_TRAP(0xe3) BAD_TRAP(0xe4) BAD_TRAP(0xe5) BAD_TRAP(0xe6) BAD_TRAP(0xe7)
+	BAD_TRAP(0xe8) BAD_TRAP(0xe9) BAD_TRAP(0xea) BAD_TRAP(0xeb) BAD_TRAP(0xec)
+	BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1)
+	BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6)
+	BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb)
+	BAD_TRAP(0xfc) BAD_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff)
+
+trapbase_cpu2:
+	BAD_TRAP(0x0) SRMMU_TFAULT TRAP_ENTRY(0x2, bad_instruction)
+	TRAP_ENTRY(0x3, priv_instruction) TRAP_ENTRY(0x4, fpd_trap_handler)
+	WINDOW_SPILL WINDOW_FILL TRAP_ENTRY(0x7, mna_handler)
+	TRAP_ENTRY(0x8, fpe_trap_handler) SRMMU_DFAULT
+	TRAP_ENTRY(0xa, do_tag_overflow) TRAP_ENTRY(0xb, do_watchpoint)
+	BAD_TRAP(0xc) BAD_TRAP(0xd) BAD_TRAP(0xe) BAD_TRAP(0xf) BAD_TRAP(0x10)
+	TRAP_ENTRY_INTERRUPT(1) TRAP_ENTRY_INTERRUPT(2)
+	TRAP_ENTRY_INTERRUPT(3) TRAP_ENTRY_INTERRUPT(4)
+	TRAP_ENTRY_INTERRUPT(5) TRAP_ENTRY_INTERRUPT(6)
+	TRAP_ENTRY_INTERRUPT(7)	TRAP_ENTRY_INTERRUPT(8)
+	TRAP_ENTRY_INTERRUPT(9) TRAP_ENTRY_INTERRUPT(10)
+	TRAP_ENTRY_INTERRUPT(11) TRAP_ENTRY_INTERRUPT(12)
+	TRAP_ENTRY_INTERRUPT(13) TRAP_ENTRY_INTERRUPT(14)
+	TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
+	TRAP_ENTRY(0x20, do_reg_access) BAD_TRAP(0x21) BAD_TRAP(0x22)
+	BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) SKIP_TRAP(0x25, unimp_flush)
+	BAD_TRAP(0x26) BAD_TRAP(0x27) TRAP_ENTRY(0x28, do_cp_exception)
+	SRMMU_DFAULT TRAP_ENTRY(0x2a, do_hw_divzero) BAD_TRAP(0x2b) BAD_TRAP(0x2c)
+	BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
+	BAD_TRAP(0x32) BAD_TRAP(0x33) BAD_TRAP(0x34) BAD_TRAP(0x35) BAD_TRAP(0x36)
+	BAD_TRAP(0x37) BAD_TRAP(0x38) BAD_TRAP(0x39) BAD_TRAP(0x3a) BAD_TRAP(0x3b)
+	BAD_TRAP(0x3c) BAD_TRAP(0x3d) BAD_TRAP(0x3e) BAD_TRAP(0x3f) BAD_TRAP(0x40)
+	BAD_TRAP(0x41) BAD_TRAP(0x42) BAD_TRAP(0x43) BAD_TRAP(0x44) BAD_TRAP(0x45)
+	BAD_TRAP(0x46) BAD_TRAP(0x47) BAD_TRAP(0x48) BAD_TRAP(0x49) BAD_TRAP(0x4a)
+	BAD_TRAP(0x4b) BAD_TRAP(0x4c) BAD_TRAP(0x4d) BAD_TRAP(0x4e) BAD_TRAP(0x4f)
+	BAD_TRAP(0x50)
+	BAD_TRAP(0x51) BAD_TRAP(0x52) BAD_TRAP(0x53) BAD_TRAP(0x54) BAD_TRAP(0x55)
+	BAD_TRAP(0x56) BAD_TRAP(0x57) BAD_TRAP(0x58) BAD_TRAP(0x59) BAD_TRAP(0x5a)
+	BAD_TRAP(0x5b) BAD_TRAP(0x5c) BAD_TRAP(0x5d) BAD_TRAP(0x5e) BAD_TRAP(0x5f)
+	BAD_TRAP(0x60) BAD_TRAP(0x61) BAD_TRAP(0x62) BAD_TRAP(0x63) BAD_TRAP(0x64)
+	BAD_TRAP(0x65) BAD_TRAP(0x66) BAD_TRAP(0x67) BAD_TRAP(0x68) BAD_TRAP(0x69)
+	BAD_TRAP(0x6a) BAD_TRAP(0x6b) BAD_TRAP(0x6c) BAD_TRAP(0x6d) BAD_TRAP(0x6e)
+	BAD_TRAP(0x6f) BAD_TRAP(0x70) BAD_TRAP(0x71) BAD_TRAP(0x72) BAD_TRAP(0x73)
+	BAD_TRAP(0x74) BAD_TRAP(0x75) BAD_TRAP(0x76) BAD_TRAP(0x77) BAD_TRAP(0x78)
+	BAD_TRAP(0x79) BAD_TRAP(0x7a) BAD_TRAP(0x7b) BAD_TRAP(0x7c) BAD_TRAP(0x7d)
+	BAD_TRAP(0x7e) BAD_TRAP(0x7f)
+	SUNOS_SYSCALL_TRAP 
+	BREAKPOINT_TRAP
+	TRAP_ENTRY(0x82, do_hw_divzero)
+	TRAP_ENTRY(0x83, do_flush_windows) BAD_TRAP(0x84) BAD_TRAP(0x85)
+	BAD_TRAP(0x86) BAD_TRAP(0x87) SOLARIS_SYSCALL_TRAP
+	NETBSD_SYSCALL_TRAP BAD_TRAP(0x8a) BAD_TRAP(0x8b) BAD_TRAP(0x8c)
+	BAD_TRAP(0x8d) BAD_TRAP(0x8e) BAD_TRAP(0x8f)
+	LINUX_SYSCALL_TRAP BAD_TRAP(0x91) BAD_TRAP(0x92) BAD_TRAP(0x93) BAD_TRAP(0x94)
+	BAD_TRAP(0x95) BAD_TRAP(0x96) BAD_TRAP(0x97) BAD_TRAP(0x98) BAD_TRAP(0x99)
+	BAD_TRAP(0x9a) BAD_TRAP(0x9b) BAD_TRAP(0x9c) BAD_TRAP(0x9d) BAD_TRAP(0x9e)
+	BAD_TRAP(0x9f) GETCC_TRAP SETCC_TRAP GETPSR_TRAP
+	BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6)
+	INDIRECT_SOLARIS_SYSCALL(156) BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab)
+	BAD_TRAP(0xac) BAD_TRAP(0xad) BAD_TRAP(0xae) BAD_TRAP(0xaf) BAD_TRAP(0xb0)
+	BAD_TRAP(0xb1) BAD_TRAP(0xb2) BAD_TRAP(0xb3) BAD_TRAP(0xb4) BAD_TRAP(0xb5)
+	BAD_TRAP(0xb6) BAD_TRAP(0xb7) BAD_TRAP(0xb8) BAD_TRAP(0xb9) BAD_TRAP(0xba)
+	BAD_TRAP(0xbb) BAD_TRAP(0xbc) BAD_TRAP(0xbd) BAD_TRAP(0xbe) BAD_TRAP(0xbf)
+	BAD_TRAP(0xc0) BAD_TRAP(0xc1) BAD_TRAP(0xc2) BAD_TRAP(0xc3) BAD_TRAP(0xc4)
+	BAD_TRAP(0xc5) BAD_TRAP(0xc6) BAD_TRAP(0xc7) BAD_TRAP(0xc8) BAD_TRAP(0xc9)
+	BAD_TRAP(0xca) BAD_TRAP(0xcb) BAD_TRAP(0xcc) BAD_TRAP(0xcd) BAD_TRAP(0xce)
+	BAD_TRAP(0xcf) BAD_TRAP(0xd0) BAD_TRAP(0xd1) BAD_TRAP(0xd2) BAD_TRAP(0xd3)
+	BAD_TRAP(0xd4) BAD_TRAP(0xd5) BAD_TRAP(0xd6) BAD_TRAP(0xd7) BAD_TRAP(0xd8)
+	BAD_TRAP(0xd9) BAD_TRAP(0xda) BAD_TRAP(0xdb) BAD_TRAP(0xdc) BAD_TRAP(0xdd)
+	BAD_TRAP(0xde) BAD_TRAP(0xdf) BAD_TRAP(0xe0) BAD_TRAP(0xe1) BAD_TRAP(0xe2)
+	BAD_TRAP(0xe3) BAD_TRAP(0xe4) BAD_TRAP(0xe5) BAD_TRAP(0xe6) BAD_TRAP(0xe7)
+	BAD_TRAP(0xe8) BAD_TRAP(0xe9) BAD_TRAP(0xea) BAD_TRAP(0xeb) BAD_TRAP(0xec)
+	BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1)
+	BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6)
+	BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb)
+	BAD_TRAP(0xfc) BAD_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff)
+
+trapbase_cpu3:
+	BAD_TRAP(0x0) SRMMU_TFAULT TRAP_ENTRY(0x2, bad_instruction)
+	TRAP_ENTRY(0x3, priv_instruction) TRAP_ENTRY(0x4, fpd_trap_handler)
+	WINDOW_SPILL WINDOW_FILL TRAP_ENTRY(0x7, mna_handler)
+	TRAP_ENTRY(0x8, fpe_trap_handler) SRMMU_DFAULT
+	TRAP_ENTRY(0xa, do_tag_overflow) TRAP_ENTRY(0xb, do_watchpoint)
+	BAD_TRAP(0xc) BAD_TRAP(0xd) BAD_TRAP(0xe) BAD_TRAP(0xf) BAD_TRAP(0x10)
+	TRAP_ENTRY_INTERRUPT(1) TRAP_ENTRY_INTERRUPT(2)
+	TRAP_ENTRY_INTERRUPT(3) TRAP_ENTRY_INTERRUPT(4)
+	TRAP_ENTRY_INTERRUPT(5) TRAP_ENTRY_INTERRUPT(6)
+	TRAP_ENTRY_INTERRUPT(7)	TRAP_ENTRY_INTERRUPT(8)
+	TRAP_ENTRY_INTERRUPT(9) TRAP_ENTRY_INTERRUPT(10)
+	TRAP_ENTRY_INTERRUPT(11) TRAP_ENTRY_INTERRUPT(12)
+	TRAP_ENTRY_INTERRUPT(13) TRAP_ENTRY_INTERRUPT(14)
+	TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
+	TRAP_ENTRY(0x20, do_reg_access) BAD_TRAP(0x21) BAD_TRAP(0x22)
+	BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) SKIP_TRAP(0x25, unimp_flush)
+	BAD_TRAP(0x26) BAD_TRAP(0x27) TRAP_ENTRY(0x28, do_cp_exception)
+	SRMMU_DFAULT TRAP_ENTRY(0x2a, do_hw_divzero) BAD_TRAP(0x2b) BAD_TRAP(0x2c)
+	BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
+	BAD_TRAP(0x32) BAD_TRAP(0x33) BAD_TRAP(0x34) BAD_TRAP(0x35) BAD_TRAP(0x36)
+	BAD_TRAP(0x37) BAD_TRAP(0x38) BAD_TRAP(0x39) BAD_TRAP(0x3a) BAD_TRAP(0x3b)
+	BAD_TRAP(0x3c) BAD_TRAP(0x3d) BAD_TRAP(0x3e) BAD_TRAP(0x3f) BAD_TRAP(0x40)
+	BAD_TRAP(0x41) BAD_TRAP(0x42) BAD_TRAP(0x43) BAD_TRAP(0x44) BAD_TRAP(0x45)
+	BAD_TRAP(0x46) BAD_TRAP(0x47) BAD_TRAP(0x48) BAD_TRAP(0x49) BAD_TRAP(0x4a)
+	BAD_TRAP(0x4b) BAD_TRAP(0x4c) BAD_TRAP(0x4d) BAD_TRAP(0x4e) BAD_TRAP(0x4f)
+	BAD_TRAP(0x50)
+	BAD_TRAP(0x51) BAD_TRAP(0x52) BAD_TRAP(0x53) BAD_TRAP(0x54) BAD_TRAP(0x55)
+	BAD_TRAP(0x56) BAD_TRAP(0x57) BAD_TRAP(0x58) BAD_TRAP(0x59) BAD_TRAP(0x5a)
+	BAD_TRAP(0x5b) BAD_TRAP(0x5c) BAD_TRAP(0x5d) BAD_TRAP(0x5e) BAD_TRAP(0x5f)
+	BAD_TRAP(0x60) BAD_TRAP(0x61) BAD_TRAP(0x62) BAD_TRAP(0x63) BAD_TRAP(0x64)
+	BAD_TRAP(0x65) BAD_TRAP(0x66) BAD_TRAP(0x67) BAD_TRAP(0x68) BAD_TRAP(0x69)
+	BAD_TRAP(0x6a) BAD_TRAP(0x6b) BAD_TRAP(0x6c) BAD_TRAP(0x6d) BAD_TRAP(0x6e)
+	BAD_TRAP(0x6f) BAD_TRAP(0x70) BAD_TRAP(0x71) BAD_TRAP(0x72) BAD_TRAP(0x73)
+	BAD_TRAP(0x74) BAD_TRAP(0x75) BAD_TRAP(0x76) BAD_TRAP(0x77) BAD_TRAP(0x78)
+	BAD_TRAP(0x79) BAD_TRAP(0x7a) BAD_TRAP(0x7b) BAD_TRAP(0x7c) BAD_TRAP(0x7d)
+	BAD_TRAP(0x7e) BAD_TRAP(0x7f)
+	SUNOS_SYSCALL_TRAP  
+	BREAKPOINT_TRAP
+	TRAP_ENTRY(0x82, do_hw_divzero)
+	TRAP_ENTRY(0x83, do_flush_windows) BAD_TRAP(0x84) BAD_TRAP(0x85)
+	BAD_TRAP(0x86) BAD_TRAP(0x87) SOLARIS_SYSCALL_TRAP
+	NETBSD_SYSCALL_TRAP BAD_TRAP(0x8a) BAD_TRAP(0x8b) BAD_TRAP(0x8c)
+	BAD_TRAP(0x8d) BAD_TRAP(0x8e) BAD_TRAP(0x8f)
+	LINUX_SYSCALL_TRAP BAD_TRAP(0x91) BAD_TRAP(0x92) BAD_TRAP(0x93) BAD_TRAP(0x94)
+	BAD_TRAP(0x95) BAD_TRAP(0x96) BAD_TRAP(0x97) BAD_TRAP(0x98) BAD_TRAP(0x99)
+	BAD_TRAP(0x9a) BAD_TRAP(0x9b) BAD_TRAP(0x9c) BAD_TRAP(0x9d) BAD_TRAP(0x9e)
+	BAD_TRAP(0x9f) GETCC_TRAP SETCC_TRAP GETPSR_TRAP
+	BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6)
+	INDIRECT_SOLARIS_SYSCALL(156) BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab)
+	BAD_TRAP(0xac) BAD_TRAP(0xad) BAD_TRAP(0xae) BAD_TRAP(0xaf) BAD_TRAP(0xb0)
+	BAD_TRAP(0xb1) BAD_TRAP(0xb2) BAD_TRAP(0xb3) BAD_TRAP(0xb4) BAD_TRAP(0xb5)
+	BAD_TRAP(0xb6) BAD_TRAP(0xb7) BAD_TRAP(0xb8) BAD_TRAP(0xb9) BAD_TRAP(0xba)
+	BAD_TRAP(0xbb) BAD_TRAP(0xbc) BAD_TRAP(0xbd) BAD_TRAP(0xbe) BAD_TRAP(0xbf)
+	BAD_TRAP(0xc0) BAD_TRAP(0xc1) BAD_TRAP(0xc2) BAD_TRAP(0xc3) BAD_TRAP(0xc4)
+	BAD_TRAP(0xc5) BAD_TRAP(0xc6) BAD_TRAP(0xc7) BAD_TRAP(0xc8) BAD_TRAP(0xc9)
+	BAD_TRAP(0xca) BAD_TRAP(0xcb) BAD_TRAP(0xcc) BAD_TRAP(0xcd) BAD_TRAP(0xce)
+	BAD_TRAP(0xcf) BAD_TRAP(0xd0) BAD_TRAP(0xd1) BAD_TRAP(0xd2) BAD_TRAP(0xd3)
+	BAD_TRAP(0xd4) BAD_TRAP(0xd5) BAD_TRAP(0xd6) BAD_TRAP(0xd7) BAD_TRAP(0xd8)
+	BAD_TRAP(0xd9) BAD_TRAP(0xda) BAD_TRAP(0xdb) BAD_TRAP(0xdc) BAD_TRAP(0xdd)
+	BAD_TRAP(0xde) BAD_TRAP(0xdf) BAD_TRAP(0xe0) BAD_TRAP(0xe1) BAD_TRAP(0xe2)
+	BAD_TRAP(0xe3) BAD_TRAP(0xe4) BAD_TRAP(0xe5) BAD_TRAP(0xe6) BAD_TRAP(0xe7)
+	BAD_TRAP(0xe8) BAD_TRAP(0xe9) BAD_TRAP(0xea) BAD_TRAP(0xeb) BAD_TRAP(0xec)
+	BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1)
+	BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6)
+	BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb)
+	BAD_TRAP(0xfc) BAD_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff)
+
+#endif
+	.align PAGE_SIZE
+
+/* This was the only reasonable way I could think of to properly align
+ * these page-table data structures.
+ */
+	.globl pg0, pg1, pg2, pg3
+	.globl empty_bad_page
+	.globl empty_bad_page_table
+	.globl empty_zero_page
+	.globl swapper_pg_dir
+swapper_pg_dir:		.skip PAGE_SIZE
+pg0:			.skip PAGE_SIZE
+pg1:			.skip PAGE_SIZE
+pg2:			.skip PAGE_SIZE
+pg3:			.skip PAGE_SIZE
+empty_bad_page:		.skip PAGE_SIZE
+empty_bad_page_table:	.skip PAGE_SIZE
+empty_zero_page:	.skip PAGE_SIZE
+
+	.global root_flags
+	.global ram_flags
+	.global root_dev
+	.global sparc_ramdisk_image
+	.global sparc_ramdisk_size
+
+/* This stuff has to be in sync with SILO and other potential boot loaders
+ * Fields should be kept upward compatible and whenever any change is made,
+ * HdrS version should be incremented.
+ */
+	.ascii	"HdrS"
+	.word	LINUX_VERSION_CODE
+	.half	0x0203		/* HdrS version */
+root_flags:
+	.half	1
+root_dev:
+	.half	0
+ram_flags:
+	.half	0
+sparc_ramdisk_image:
+	.word	0
+sparc_ramdisk_size:
+	.word	0
+	.word	reboot_command
+	.word	0, 0, 0
+	.word	_end
+
+/* Cool, here we go. Pick up the romvec pointer in %o0 and stash it in
+ * %g7 and at prom_vector_p. And also quickly check whether we are on
+ * a v0, v2, or v3 prom.
+ */
+gokernel:
+		/* Ok, it's nice to know, as early as possible, if we
+		 * are already mapped where we expect to be in virtual
+		 * memory.  The Solaris /boot elf format bootloader
+		 * will peek into our elf header and load us where
+		 * we want to be, otherwise we have to re-map.
+		 *
+		 * Some boot loaders don't place the jmp'rs address
+		 * in %o7, so we do a pc-relative call to a local
+		 * label, then see what %o7 has.
+		 */
+
+		mov	%o7, %g4		! Save %o7
+
+		/* Jump to it, and pray... */
+current_pc:
+		call	1f
+		 nop
+
+1:
+		mov	%o7, %g3
+
+		tst	%o0
+		be	no_sun4u_here
+		 mov	%g4, %o7		/* Previous %o7. */
+	
+		mov	%o0, %l0		! stash away romvec
+		mov	%o0, %g7		! put it here too
+		mov	%o1, %l1		! stash away debug_vec too
+
+		/* Ok, let's check out our run time program counter. */
+		set	current_pc, %g5
+		cmp	%g3, %g5
+		be	already_mapped
+		 nop 
+
+		/* %l6 will hold the offset we have to subtract
+		 * from absolute symbols in order to access areas
+		 * in our own image.  If already mapped this is
+		 * just plain zero, else it is KERNBASE.
+		 */
+		set	KERNBASE, %l6
+		b	copy_prom_lvl14
+		 nop
+
+already_mapped:
+		mov	0, %l6
+
+		/* Copy over the Prom's level 14 clock handler. */
+copy_prom_lvl14:
+#if 1
+		/* DJHR
+		 * preserve our linked/calculated instructions
+		 */
+		set	lvl14_save, %g1
+		set	t_irq14, %g3
+		sub	%g1, %l6, %g1		! translate to physical
+		sub	%g3, %l6, %g3		! translate to physical
+		ldd	[%g3], %g4
+		std	%g4, [%g1]
+		ldd	[%g3+8], %g4
+		std	%g4, [%g1+8]
+#endif
+		rd	%tbr, %g1
+		andn	%g1, 0xfff, %g1		! proms trap table base
+		or	%g0, (0x1e<<4), %g2	! offset to lvl14 intr
+		or	%g1, %g2, %g2
+		set	t_irq14, %g3
+		sub	%g3, %l6, %g3
+		ldd	[%g2], %g4
+		std	%g4, [%g3]
+		ldd	[%g2 + 0x8], %g4
+		std	%g4, [%g3 + 0x8]	! Copy proms handler
+
+/* Must determine whether we are on a sun4c MMU, SRMMU, or SUN4/400 MUTANT
+ * MMU so we can remap ourselves properly.  DON'T TOUCH %l0 thru %l5 in these
+ * remapping routines, we need their values afterwards!
+ */
+		/* Now check whether we are already mapped, if we
+		 * are we can skip all this garbage coming up.
+		 */
+copy_prom_done:
+		cmp	%l6, 0
+		be	go_to_highmem		! this will be a nop then
+		 nop
+
+		set	LOAD_ADDR, %g6
+		cmp	%g7, %g6
+		bne	remap_not_a_sun4	! This is not a Sun4
+		 nop
+
+		or	%g0, 0x1, %g1
+		lduba	[%g1] ASI_CONTROL, %g1	! Only safe to try on Sun4.
+		subcc	%g1, 0x24, %g0		! Is this a mutant Sun4/400???
+		be	sun4_mutant_remap	! Ugh, it is...
+		 nop
+
+		b	sun4_normal_remap	! regular sun4, 2 level mmu
+		 nop
+
+remap_not_a_sun4:
+		lda	[%g0] ASI_M_MMUREGS, %g1 ! same as ASI_PTE on sun4c
+		and	%g1, 0x1, %g1		! Test SRMMU Enable bit ;-)
+		cmp	%g1, 0x0
+		be	sun4c_remap		! A sun4c MMU or normal Sun4
+		 nop
+srmmu_remap:
+		/* First, check for a viking (TI) module. */
+		set	0x40000000, %g2
+		rd	%psr, %g3
+		and	%g2, %g3, %g3
+		subcc	%g3, 0x0, %g0
+		bz	srmmu_nviking
+		 nop
+
+		/* Figure out what kind of viking we are on.
+		 * We need to know if we have to play with the
+		 * AC bit and disable traps or not.
+		 */
+
+		/* I've only seen MicroSparc's on SparcClassics with this
+		 * bit set.
+		 */
+		set	0x800, %g2
+		lda	[%g0] ASI_M_MMUREGS, %g3	! peek in the control reg
+		and	%g2, %g3, %g3
+		subcc	%g3, 0x0, %g0
+		bnz	srmmu_nviking			! is in mbus mode
+		 nop
+		
+		rd	%psr, %g3			! DO NOT TOUCH %g3
+		andn	%g3, PSR_ET, %g2
+		wr	%g2, 0x0, %psr
+		WRITE_PAUSE
+		
+		/* Get context table pointer, then convert to
+		 * a physical address, which is 36 bits.
+		 */
+		set	AC_M_CTPR, %g4
+		lda	[%g4] ASI_M_MMUREGS, %g4
+		sll	%g4, 0x4, %g4			! We use this below
+							! DO NOT TOUCH %g4
+
+		/* Set the AC bit in the Viking's MMU control reg. */
+		lda	[%g0] ASI_M_MMUREGS, %g5	! DO NOT TOUCH %g5
+		set	0x8000, %g6			! AC bit mask
+		or	%g5, %g6, %g6			! Or it in...
+		sta	%g6, [%g0] ASI_M_MMUREGS	! Close your eyes...
+
+		/* Grrr, why does it seem like every other load/store
+		 * on the sun4m is in some ASI space...
+		 * Fine with me, let's get the pointer to the level 1
+		 * page table directory and fetch its entry.
+		 */
+		lda	[%g4] ASI_M_BYPASS, %o1		! This is a level 1 ptr
+		srl	%o1, 0x4, %o1			! Clear low 4 bits
+		sll	%o1, 0x8, %o1			! Make physical
+		
+		/* Ok, pull in the PTD. */
+		lda	[%o1] ASI_M_BYPASS, %o2		! This is the 0x0 16MB pgd
+
+		/* Calculate to KERNBASE entry. */
+		add	%o1, KERNBASE >> (SRMMU_PGDIR_SHIFT - 2), %o3		
+
+		/* Poke the entry into the calculated address. */
+		sta	%o2, [%o3] ASI_M_BYPASS
+
+		/* I don't get it Sun, if you engineered all these
+		 * boot loaders and the PROM (thank you for the debugging
+		 * features btw) why did you not have them load kernel
+		 * images up in high address space, since this is necessary
+		 * for ABI compliance anyways?  Does this low-mapping provide
+		 * enhanced interoperability?
+		 *
+		 * "The PROM is the computer."
+		 */
+
+		/* Ok, restore the MMU control register we saved in %g5 */
+		sta	%g5, [%g0] ASI_M_MMUREGS	! POW... ouch
+
+		/* Turn traps back on.  We saved it in %g3 earlier. */
+		wr	%g3, 0x0, %psr			! tick tock, tick tock
+
+		/* Now we burn precious CPU cycles due to bad engineering. */
+		WRITE_PAUSE
+
+		/* Wow, all that just to move a 32-bit value from one
+		 * place to another...  Jump to high memory.
+		 */
+		b	go_to_highmem
+		 nop
+
+		/* This works on viking's in Mbus mode and all
+		 * other MBUS modules.  It is virtually the same as
+		 * the above madness sans turning traps off and flipping
+		 * the AC bit.
+		 */
+srmmu_nviking:
+		set	AC_M_CTPR, %g1
+		lda	[%g1] ASI_M_MMUREGS, %g1	! get ctx table ptr
+		sll	%g1, 0x4, %g1			! make physical addr
+		lda	[%g1] ASI_M_BYPASS, %g1		! ptr to level 1 pg_table
+		srl	%g1, 0x4, %g1
+		sll	%g1, 0x8, %g1			! make phys addr for l1 tbl
+
+		lda	[%g1] ASI_M_BYPASS, %g2		! get level1 entry for 0x0
+		add	%g1, KERNBASE >> (SRMMU_PGDIR_SHIFT - 2), %g3
+		sta	%g2, [%g3] ASI_M_BYPASS		! place at KERNBASE entry
+		b	go_to_highmem
+		 nop					! wheee....
+
+		/* This remaps the kernel on Sun4/4xx machines
+		 * that have the Sun Mutant Three Level MMU.
+		 * It's like a platypus, Sun didn't have the
+		 * SRMMU in conception so they kludged the three
+		 * level logic in the regular Sun4 MMU probably.
+		 *
+		 * Basically, you take each entry in the top level
+		 * directory that maps the low 3MB starting at
+		 * address zero and put the mapping in the KERNBASE
+		 * slots.  These top level pgd's are called regmaps.
+		 */
+sun4_mutant_remap:
+		or	%g0, %g0, %g3		! source base
+		sethi	%hi(KERNBASE), %g4	! destination base
+		or	%g4, %lo(KERNBASE), %g4
+		sethi	%hi(0x300000), %g5
+		or	%g5, %lo(0x300000), %g5	! upper bound 3MB
+		or	%g0, 0x1, %l6
+		sll	%l6, 24, %l6		! Regmap mapping size
+		add	%g3, 0x2, %g3		! Base magic
+		add	%g4, 0x2, %g4		! Base magic
+
+		/* Main remapping loop on Sun4-Mutant-MMU.
+		 * "I am not an animal..." -Famous Mutant Person
+		 */
+sun4_mutant_loop:
+		lduha	[%g3] ASI_REGMAP, %g2	! Get lower entry
+		stha	%g2, [%g4] ASI_REGMAP	! Store in high entry
+		add	%g4, %l6, %g4		! Move up high memory ptr
+		subcc	%g3, %g5, %g0		! Reached our limit?
+		blu	sun4_mutant_loop	! Nope, loop again
+		 add	%g3, %l6, %g3		! delay, Move up low ptr
+		b	go_to_highmem		! Jump to high memory.
+		 nop
+
+		/* The following is for non-4/4xx sun4 MMU's. */
+sun4_normal_remap:
+		mov	0, %g3			! source base
+		set	KERNBASE, %g4		! destination base
+		set	0x300000, %g5		! upper bound 3MB
+		mov	1, %l6
+		sll	%l6, 18, %l6		! sun4 mmu segmap size
+sun4_normal_loop:
+		lduha	[%g3] ASI_SEGMAP, %g6	! load phys_seg
+		stha	%g6, [%g4] ASI_SEGMAP	! stort new virt mapping
+		add	%g3, %l6, %g3		! increment source pointer
+		subcc	%g3, %g5, %g0		! reached limit?
+		blu	sun4_normal_loop	! nope, loop again
+		 add	%g4, %l6, %g4		! delay, increment dest ptr
+		b	go_to_highmem
+		 nop
+
+		/* The following works for Sun4c MMU's */
+sun4c_remap:
+		mov	0, %g3			! source base
+		set	KERNBASE, %g4		! destination base
+		set	0x300000, %g5		! upper bound 3MB
+		mov	1, %l6
+		sll	%l6, 18, %l6		! sun4c mmu segmap size
+sun4c_remap_loop:
+		lda	[%g3] ASI_SEGMAP, %g6	! load phys_seg
+		sta	%g6, [%g4] ASI_SEGMAP   ! store new virt mapping
+		add	%g3, %l6, %g3		! Increment source ptr
+		subcc	%g3, %g5, %g0		! Reached limit?
+		bl	sun4c_remap_loop	! Nope, loop again
+		 add	%g4, %l6, %g4		! delay, Increment dest ptr
+
+/* Now do a non-relative jump so that PC is in high-memory */
+go_to_highmem:
+		set	execute_in_high_mem, %g1
+		jmpl	%g1, %g0
+		 nop
+
+/* The code above should be at beginning and we have to take care about
+ * short jumps, as branching to .text.init section from .text is usually
+ * impossible */
+		__INIT
+/* Acquire boot time privileged register values, this will help debugging.
+ * I figure out and store nwindows and nwindowsm1 later on.
+ */
+execute_in_high_mem:
+		mov	%l0, %o0		! put back romvec
+		mov	%l1, %o1		! and debug_vec
+
+		sethi	%hi(prom_vector_p), %g1
+		st	%o0, [%g1 + %lo(prom_vector_p)]
+
+		sethi	%hi(linux_dbvec), %g1
+		st	%o1, [%g1 + %lo(linux_dbvec)]
+
+		ld	[%o0 + 0x4], %o3
+		and	%o3, 0x3, %o5			! get the version
+
+		cmp	%o3, 0x2			! a v2 prom?
+		be	found_version
+		 nop
+
+		/* paul@sfe.com.au */
+		cmp	%o3, 0x3			! a v3 prom?
+		be	found_version
+		 nop
+
+/* Old sun4's pass our load address into %o0 instead of the prom
+ * pointer. On sun4's you have to hard code the romvec pointer into
+ * your code. Sun probably still does that because they don't even
+ * trust their own "OpenBoot" specifications.
+ */
+		set	LOAD_ADDR, %g6
+		cmp	%o0, %g6		! an old sun4?
+		be	sun4_init
+		 nop
+
+found_version:
+#ifdef CONFIG_SUN4
+/* For people who try sun4 kernels, even if Configure.help advises them. */
+		ld	[%g7 + 0x68], %o1
+		set	sun4cdm_notsup, %o0
+		call	%o1
+		 nop
+		b	halt_me
+		 nop
+#endif
+/* Get the machine type via the mysterious romvec node operations. */
+
+		add	%g7, 0x1c, %l1		
+		ld	[%l1], %l0
+		ld	[%l0], %l0
+		call 	%l0
+		 or	%g0, %g0, %o0		! next_node(0) = first_node
+		or	%o0, %g0, %g6
+
+		sethi	%hi(cputypvar), %o1	! First node has cpu-arch
+		or	%o1, %lo(cputypvar), %o1
+		sethi	%hi(cputypval), %o2	! information, the string
+		or	%o2, %lo(cputypval), %o2
+		ld	[%l1], %l0		! 'compatibility' tells
+		ld	[%l0 + 0xc], %l0	! that we want 'sun4x' where
+		call	%l0			! x is one of '', 'c', 'm',
+		 nop				! 'd' or 'e'. %o2 holds pointer
+						! to a buf where above string
+						! will get stored by the prom.
+
+		subcc	%o0, %g0, %g0
+		bpos	got_prop		! Got the property
+		 nop
+
+		or	%g6, %g0, %o0
+		sethi	%hi(cputypvar_sun4m), %o1
+		or	%o1, %lo(cputypvar_sun4m), %o1
+		sethi	%hi(cputypval), %o2
+		or	%o2, %lo(cputypval), %o2
+		ld	[%l1], %l0
+		ld	[%l0 + 0xc], %l0
+		call	%l0
+		 nop
+
+got_prop:
+		set	cputypval, %o2
+		ldub	[%o2 + 0x4], %l1
+
+		cmp	%l1, ' '
+		be	1f
+		 cmp	%l1, 'c'
+		be	1f
+		 cmp	%l1, 'm'
+		be	1f
+		 cmp	%l1, 's'
+		be	1f
+		 cmp	%l1, 'd'
+		be	1f
+		 cmp	%l1, 'e'
+		be	no_sun4e_here		! Could be a sun4e.
+		 nop
+		b	no_sun4u_here		! AIEEE, a V9 sun4u... Get our BIG BROTHER kernel :))
+		 nop
+
+1:		set	cputypval, %l1
+		ldub	[%l1 + 0x4], %l1
+		cmp	%l1, 'm'		! Test for sun4d, sun4e ?
+		be	sun4m_init
+		 cmp	%l1, 's'		! Treat sun4s as sun4m
+		be	sun4m_init
+		 cmp	%l1, 'd'		! Let us see how the beast will die
+		be	sun4d_init
+		 nop
+
+		/* Jump into mmu context zero. */
+		set	AC_CONTEXT, %g1
+		stba	%g0, [%g1] ASI_CONTROL
+
+		b	sun4c_continue_boot
+		 nop
+
+/* CPUID in bootbus can be found at PA 0xff0140000 */
+#define SUN4D_BOOTBUS_CPUID     0xf0140000
+
+sun4d_init:
+	/* Need to patch call to handler_irq */
+	set	patch_handler_irq, %g4
+	set	sun4d_handler_irq, %g5
+	sethi	%hi(0x40000000), %g3		! call
+	sub	%g5, %g4, %g5
+	srl	%g5, 2, %g5
+	or	%g5, %g3, %g5
+	st	%g5, [%g4]
+
+#ifdef CONFIG_SMP
+	/* Get our CPU id out of bootbus */
+	set     SUN4D_BOOTBUS_CPUID, %g3
+	lduba   [%g3] ASI_M_CTL, %g3
+	and     %g3, 0xf8, %g3
+	srl     %g3, 3, %g4
+	sta     %g4, [%g0] ASI_M_VIKING_TMP1
+	sethi	%hi(boot_cpu_id), %g5
+	stb	%g4, [%g5 + %lo(boot_cpu_id)]
+	sll	%g4, 2, %g4
+	sethi	%hi(boot_cpu_id4), %g5
+	stb	%g4, [%g5 + %lo(boot_cpu_id4)]
+#endif
+
+	/* Fall through to sun4m_init */
+
+sun4m_init:
+	/* XXX Fucking Cypress... */
+	lda	[%g0] ASI_M_MMUREGS, %g5
+	srl	%g5, 28, %g4
+
+	cmp	%g4, 1
+	bne	1f
+	 srl	%g5, 24, %g4
+
+	and	%g4, 0xf, %g4
+	cmp	%g4, 7		/* This would be a HyperSparc. */
+
+	bne	2f
+	 nop
+
+1:
+
+#define PATCH_IT(dst, src)	\
+	set	(dst), %g5;	\
+	set	(src), %g4;	\
+	ld	[%g4], %g3;	\
+	st	%g3, [%g5];	\
+	ld	[%g4+0x4], %g3;	\
+	st	%g3, [%g5+0x4];
+
+	/* Signed multiply. */
+	PATCH_IT(.mul, .mul_patch)
+	PATCH_IT(.mul+0x08, .mul_patch+0x08)
+
+	/* Signed remainder. */
+	PATCH_IT(.rem, .rem_patch)
+	PATCH_IT(.rem+0x08, .rem_patch+0x08)
+	PATCH_IT(.rem+0x10, .rem_patch+0x10)
+	PATCH_IT(.rem+0x18, .rem_patch+0x18)
+	PATCH_IT(.rem+0x20, .rem_patch+0x20)
+	PATCH_IT(.rem+0x28, .rem_patch+0x28)
+
+	/* Signed division. */
+	PATCH_IT(.div, .div_patch)
+	PATCH_IT(.div+0x08, .div_patch+0x08)
+	PATCH_IT(.div+0x10, .div_patch+0x10)
+	PATCH_IT(.div+0x18, .div_patch+0x18)
+	PATCH_IT(.div+0x20, .div_patch+0x20)
+
+	/* Unsigned multiply. */
+	PATCH_IT(.umul, .umul_patch)
+	PATCH_IT(.umul+0x08, .umul_patch+0x08)
+
+	/* Unsigned remainder. */
+	PATCH_IT(.urem, .urem_patch)
+	PATCH_IT(.urem+0x08, .urem_patch+0x08)
+	PATCH_IT(.urem+0x10, .urem_patch+0x10)
+	PATCH_IT(.urem+0x18, .urem_patch+0x18)
+
+	/* Unsigned division. */
+	PATCH_IT(.udiv, .udiv_patch)
+	PATCH_IT(.udiv+0x08, .udiv_patch+0x08)
+	PATCH_IT(.udiv+0x10, .udiv_patch+0x10)
+
+#undef PATCH_IT
+
+/* Ok, the PROM could have done funny things and apple cider could still
+ * be sitting in the fault status/address registers.  Read them all to
+ * clear them so we don't get magic faults later on.
+ */
+/* This sucks, apparently this makes Vikings call prom panic, will fix later */
+2:
+		rd	%psr, %o1
+		srl	%o1, 28, %o1		! Get a type of the CPU
+
+		subcc	%o1, 4, %g0		! TI: Viking or MicroSPARC
+		be	sun4c_continue_boot
+		 nop
+
+		set	AC_M_SFSR, %o0
+		lda	[%o0] ASI_M_MMUREGS, %g0
+		set	AC_M_SFAR, %o0
+		lda	[%o0] ASI_M_MMUREGS, %g0
+
+		/* Fujitsu MicroSPARC-II has no asynchronous flavors of FARs */
+		subcc	%o1, 0, %g0
+		be	sun4c_continue_boot
+		 nop
+
+		set	AC_M_AFSR, %o0
+		lda	[%o0] ASI_M_MMUREGS, %g0
+		set	AC_M_AFAR, %o0
+		lda	[%o0] ASI_M_MMUREGS, %g0
+		 nop
+
+
+sun4c_continue_boot:
+
+
+/* Aieee, now set PC and nPC, enable traps, give ourselves a stack and it's
+ * show-time!
+ */
+
+		sethi	%hi(cputyp), %o0
+		st	%g4, [%o0 + %lo(cputyp)]
+
+		/* Turn on Supervisor, EnableFloating, and all the PIL bits.
+		 * Also puts us in register window zero with traps off.
+		 */
+		set	(PSR_PS | PSR_S | PSR_PIL | PSR_EF), %g2
+		wr	%g2, 0x0, %psr
+		WRITE_PAUSE
+
+		/* I want a kernel stack NOW! */
+		set	init_thread_union, %g1
+		set	(THREAD_SIZE - STACKFRAME_SZ), %g2
+		add	%g1, %g2, %sp
+		mov	0, %fp			/* And for good luck */
+
+		/* Zero out our BSS section. */
+		set	__bss_start , %o0	! First address of BSS
+		set	end , %o1		! Last address of BSS
+		add	%o0, 0x1, %o0
+1:	
+		stb	%g0, [%o0]
+		subcc	%o0, %o1, %g0
+		bl	1b
+		 add	%o0, 0x1, %o0
+
+		/* Initialize the uwinmask value for init task just in case.
+		 * But first make current_set[boot_cpu_id] point to something useful.
+		 */
+		set	init_thread_union, %g6
+		set	current_set, %g2
+#ifdef CONFIG_SMP
+		sethi	%hi(boot_cpu_id4), %g3
+		ldub	[%g3 + %lo(boot_cpu_id4)], %g3
+		st	%g6, [%g2]
+		add	%g2, %g3, %g2
+#endif
+		st	%g6, [%g2]
+
+		st	%g0, [%g6 + TI_UWINMASK]
+
+/* Compute NWINDOWS and stash it away. Now uses %wim trick explained
+ * in the V8 manual. Ok, this method seems to work, Sparc is cool...
+ * No, it doesn't work, have to play the save/readCWP/restore trick.
+ */
+
+		wr	%g0, 0x0, %wim			! so we do not get a trap
+		WRITE_PAUSE
+
+		save
+
+		rd	%psr, %g3
+
+		restore
+
+		and	%g3, 0x1f, %g3
+		add	%g3, 0x1, %g3
+
+		mov	2, %g1
+		wr	%g1, 0x0, %wim			! make window 1 invalid
+		WRITE_PAUSE
+
+		cmp	%g3, 0x7
+		bne	2f
+		 nop
+
+		/* Adjust our window handling routines to
+		 * do things correctly on 7 window Sparcs.
+		 */
+
+#define		PATCH_INSN(src, dest) \
+		set	src, %g5; \
+		set	dest, %g2; \
+		ld	[%g5], %g4; \
+		st	%g4, [%g2];
+	
+		/* Patch for window spills... */
+		PATCH_INSN(spnwin_patch1_7win, spnwin_patch1)
+		PATCH_INSN(spnwin_patch2_7win, spnwin_patch2)
+		PATCH_INSN(spnwin_patch3_7win, spnwin_patch3)
+
+		/* Patch for window fills... */
+		PATCH_INSN(fnwin_patch1_7win, fnwin_patch1)
+		PATCH_INSN(fnwin_patch2_7win, fnwin_patch2)
+
+		/* Patch for trap entry setup... */
+		PATCH_INSN(tsetup_7win_patch1, tsetup_patch1)
+		PATCH_INSN(tsetup_7win_patch2, tsetup_patch2)
+		PATCH_INSN(tsetup_7win_patch3, tsetup_patch3)
+		PATCH_INSN(tsetup_7win_patch4, tsetup_patch4)
+		PATCH_INSN(tsetup_7win_patch5, tsetup_patch5)
+		PATCH_INSN(tsetup_7win_patch6, tsetup_patch6)
+
+		/* Patch for returning from traps... */
+		PATCH_INSN(rtrap_7win_patch1, rtrap_patch1)
+		PATCH_INSN(rtrap_7win_patch2, rtrap_patch2)
+		PATCH_INSN(rtrap_7win_patch3, rtrap_patch3)
+		PATCH_INSN(rtrap_7win_patch4, rtrap_patch4)
+		PATCH_INSN(rtrap_7win_patch5, rtrap_patch5)
+
+		/* Patch for killing user windows from the register file. */
+		PATCH_INSN(kuw_patch1_7win, kuw_patch1)
+
+		/* Now patch the kernel window flush sequences.
+		 * This saves 2 traps on every switch and fork.
+		 */
+		set	0x01000000, %g4
+		set	flush_patch_one, %g5
+		st	%g4, [%g5 + 0x18]
+		st	%g4, [%g5 + 0x1c]
+		set	flush_patch_two, %g5
+		st	%g4, [%g5 + 0x18]
+		st	%g4, [%g5 + 0x1c]
+		set	flush_patch_three, %g5
+		st	%g4, [%g5 + 0x18]
+		st	%g4, [%g5 + 0x1c]
+		set	flush_patch_four, %g5
+		st	%g4, [%g5 + 0x18]
+		st	%g4, [%g5 + 0x1c]
+		set	flush_patch_exception, %g5
+		st	%g4, [%g5 + 0x18]
+		st	%g4, [%g5 + 0x1c]
+		set	flush_patch_switch, %g5
+		st	%g4, [%g5 + 0x18]
+		st	%g4, [%g5 + 0x1c]
+
+2:		
+		sethi	%hi(nwindows), %g4
+		st	%g3, [%g4 + %lo(nwindows)]	! store final value
+		sub	%g3, 0x1, %g3
+		sethi	%hi(nwindowsm1), %g4
+		st	%g3, [%g4 + %lo(nwindowsm1)]
+
+		/* Here we go, start using Linux's trap table... */
+		set	trapbase, %g3
+		wr	%g3, 0x0, %tbr
+		WRITE_PAUSE
+
+		/* Finally, turn on traps so that we can call c-code. */
+		rd	%psr, %g3
+		wr	%g3, 0x0, %psr
+		WRITE_PAUSE
+
+		wr	%g3, PSR_ET, %psr
+		WRITE_PAUSE
+
+		/* First we call prom_init() to set up PROMLIB, then
+		 * off to start_kernel().
+		 */
+
+		sethi	%hi(prom_vector_p), %g5
+		ld	[%g5 + %lo(prom_vector_p)], %o0
+		call	prom_init
+		 nop
+
+		call 	start_kernel
+		 nop
+	
+		/* We should not get here. */
+		call	halt_me
+		 nop
+
+sun4_init:
+#ifdef CONFIG_SUN4
+/* There, happy now Adrian? */
+		set	cputypval, %o2		! Let everyone know we
+		set	' ', %o0			! are a "sun4 " architecture
+		stb	%o0, [%o2 + 0x4]		
+
+		b got_prop 
+		 nop
+#else
+		sethi   %hi(SUN4_PROM_VECTOR+0x84), %o1
+		ld      [%o1 + %lo(SUN4_PROM_VECTOR+0x84)], %o1
+		set     sun4_notsup, %o0
+		call    %o1	/* printf */
+		 nop
+		sethi   %hi(SUN4_PROM_VECTOR+0xc4), %o1
+		ld      [%o1 + %lo(SUN4_PROM_VECTOR+0xc4)], %o1
+		call    %o1	/* exittomon */
+		 nop
+1:		ba      1b                      ! Cannot exit into KMON
+		 nop
+#endif
+no_sun4e_here:
+		ld	[%g7 + 0x68], %o1
+		set	sun4e_notsup, %o0
+		call	%o1
+		 nop
+		b	halt_me
+		 nop
+
+		__INITDATA
+
+sun4u_1:
+		.asciz "finddevice"
+		.align	4
+sun4u_2:
+		.asciz "/chosen"
+		.align	4
+sun4u_3:
+		.asciz "getprop"
+		.align	4
+sun4u_4:
+		.asciz "stdout"
+		.align	4
+sun4u_5:
+		.asciz "write"
+		.align	4
+sun4u_6:
+        	.asciz  "\n\rOn sun4u you have to use UltraLinux (64bit) kernel\n\rand not a 32bit sun4[cdem] version\n\r\n\r"
+sun4u_6e:
+		.align	4
+sun4u_7:
+		.asciz "exit"
+		.align	8
+sun4u_a1:
+		.word	0, sun4u_1, 0, 1, 0, 1, 0, sun4u_2, 0
+sun4u_r1:
+		.word	0
+sun4u_a2:
+		.word	0, sun4u_3, 0, 4, 0, 1, 0
+sun4u_i2:
+		.word	0, 0, sun4u_4, 0, sun4u_1, 0, 8, 0
+sun4u_r2:
+		.word	0
+sun4u_a3:
+		.word	0, sun4u_5, 0, 3, 0, 1, 0
+sun4u_i3:
+		.word	0, 0, sun4u_6, 0, sun4u_6e - sun4u_6 - 1, 0
+sun4u_r3:
+		.word	0
+sun4u_a4:
+		.word	0, sun4u_7, 0, 0, 0, 0
+sun4u_r4:
+
+		__INIT
+no_sun4u_here:
+		set	sun4u_a1, %o0
+		set	current_pc, %l2
+		cmp	%l2, %g3
+		be	1f
+		 mov	%o4, %l0
+		sub	%g3, %l2, %l6
+		add	%o0, %l6, %o0
+		mov	%o0, %l4
+		mov	sun4u_r4 - sun4u_a1, %l3
+		ld	[%l4], %l5
+2:
+		add	%l4, 4, %l4
+		cmp	%l5, %l2
+		add	%l5, %l6, %l5
+		bgeu,a	3f
+		 st	%l5, [%l4 - 4]
+3:
+		subcc	%l3, 4, %l3
+		bne	2b
+		 ld	[%l4], %l5
+1:
+		call	%l0
+		 mov	%o0, %l1
+
+		ld	[%l1 + (sun4u_r1 - sun4u_a1)], %o1
+		add	%l1, (sun4u_a2 - sun4u_a1), %o0
+		call	%l0
+		 st	%o1, [%o0 + (sun4u_i2 - sun4u_a2)]
+
+		ld	[%l1 + (sun4u_1 - sun4u_a1)], %o1
+		add	%l1, (sun4u_a3 - sun4u_a1), %o0
+		call	%l0
+		st	%o1, [%o0 + (sun4u_i3 - sun4u_a3)]
+
+		call	%l0
+		 add	%l1, (sun4u_a4 - sun4u_a1), %o0
+
+		/* Not reached */
+halt_me:
+		ld	[%g7 + 0x74], %o0
+		call	%o0			! Get us out of here...
+		 nop				! Apparently Solaris is better.
+
+/* Ok, now we continue in the .data/.text sections */
+
+	.data
+	.align 4
+
+/*
+ * Fill up the prom vector, note in particular the kind first element,
+ * no joke. I don't need all of them in here as the entire prom vector
+ * gets initialized in c-code so all routines can use it.
+ */
+
+	.globl	prom_vector_p
+prom_vector_p:
+		.word 0
+
+/* We calculate the following at boot time, window fills/spills and trap entry
+ * code uses these to keep track of the register windows.
+ */
+
+	.align 4
+	.globl	nwindows
+	.globl	nwindowsm1
+nwindows:
+	.word	8
+nwindowsm1:
+	.word	7
+
+/* Boot time debugger vector value.  We need this later on. */
+
+	.align 4
+	.globl	linux_dbvec
+linux_dbvec:
+	.word	0
+	.word	0
+
+	.align 8
+
+	.globl	lvl14_save
+lvl14_save:
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	t_irq14
+
+        .section        ".fixup",#alloc,#execinstr
+        .globl  __ret_efault
+__ret_efault:
+        ret
+         restore %g0, -EFAULT, %o0
diff --git a/arch/sparc/kernel/idprom.c b/arch/sparc/kernel/idprom.c
new file mode 100644
index 0000000..2e1b0f6
--- /dev/null
+++ b/arch/sparc/kernel/idprom.c
@@ -0,0 +1,108 @@
+/* $Id: idprom.c,v 1.24 1999/08/31 06:54:20 davem Exp $
+ * idprom.c: Routines to load the idprom into kernel addresses and
+ *           interpret the data contained within.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+
+#include <asm/oplib.h>
+#include <asm/idprom.h>
+#include <asm/machines.h>  /* Fun with Sun released architectures. */
+#ifdef CONFIG_SUN4
+#include <asm/sun4paddr.h>
+extern void sun4setup(void);
+#endif
+
+struct idprom *idprom;
+static struct idprom idprom_buffer;
+
+/* Here is the master table of Sun machines which use some implementation
+ * of the Sparc CPU and have a meaningful IDPROM machtype value that we
+ * know about.  See asm-sparc/machines.h for empirical constants.
+ */
+struct Sun_Machine_Models Sun_Machines[NUM_SUN_MACHINES] = {
+/* First, Sun4's */
+{ "Sun 4/100 Series", (SM_SUN4 | SM_4_110) },
+{ "Sun 4/200 Series", (SM_SUN4 | SM_4_260) },
+{ "Sun 4/300 Series", (SM_SUN4 | SM_4_330) },
+{ "Sun 4/400 Series", (SM_SUN4 | SM_4_470) },
+/* Now, Sun4c's */
+{ "Sun4c SparcStation 1", (SM_SUN4C | SM_4C_SS1) },
+{ "Sun4c SparcStation IPC", (SM_SUN4C | SM_4C_IPC) },
+{ "Sun4c SparcStation 1+", (SM_SUN4C | SM_4C_SS1PLUS) },
+{ "Sun4c SparcStation SLC", (SM_SUN4C | SM_4C_SLC) },
+{ "Sun4c SparcStation 2", (SM_SUN4C | SM_4C_SS2) },
+{ "Sun4c SparcStation ELC", (SM_SUN4C | SM_4C_ELC) },
+{ "Sun4c SparcStation IPX", (SM_SUN4C | SM_4C_IPX) },
+/* Finally, early Sun4m's */
+{ "Sun4m SparcSystem600", (SM_SUN4M | SM_4M_SS60) },
+{ "Sun4m SparcStation10/20", (SM_SUN4M | SM_4M_SS50) },
+{ "Sun4m SparcStation5", (SM_SUN4M | SM_4M_SS40) },
+/* One entry for the OBP arch's which are sun4d, sun4e, and newer sun4m's */
+{ "Sun4M OBP based system", (SM_SUN4M_OBP | 0x0) } };
+
+static void __init display_system_type(unsigned char machtype)
+{
+	char sysname[128];
+	register int i;
+
+	for (i = 0; i < NUM_SUN_MACHINES; i++) {
+		if(Sun_Machines[i].id_machtype == machtype) {
+			if (machtype != (SM_SUN4M_OBP | 0x00) ||
+			    prom_getproperty(prom_root_node, "banner-name",
+					     sysname, sizeof(sysname)) <= 0)
+				printk("TYPE: %s\n", Sun_Machines[i].name);
+			else
+				printk("TYPE: %s\n", sysname);
+			return;
+		}
+	}
+
+	prom_printf("IDPROM: Bogus id_machtype value, 0x%x\n", machtype);
+	prom_halt();
+}
+
+/* Calculate the IDPROM checksum (xor of the data bytes). */
+static unsigned char __init calc_idprom_cksum(struct idprom *idprom)
+{
+	unsigned char cksum, i, *ptr = (unsigned char *)idprom;
+
+	for (i = cksum = 0; i <= 0x0E; i++)
+		cksum ^= *ptr++;
+
+	return cksum;
+}
+
+/* Create a local IDPROM copy, verify integrity, and display information. */
+void __init idprom_init(void)
+{
+	prom_get_idprom((char *) &idprom_buffer, sizeof(idprom_buffer));
+
+	idprom = &idprom_buffer;
+
+	if (idprom->id_format != 0x01)  {
+		prom_printf("IDPROM: Unknown format type!\n");
+		prom_halt();
+	}
+
+	if (idprom->id_cksum != calc_idprom_cksum(idprom)) {
+		prom_printf("IDPROM: Checksum failure (nvram=%x, calc=%x)!\n",
+			    idprom->id_cksum, calc_idprom_cksum(idprom));
+		prom_halt();
+	}
+
+	display_system_type(idprom->id_machtype);
+
+	printk("Ethernet address: %x:%x:%x:%x:%x:%x\n",
+		    idprom->id_ethaddr[0], idprom->id_ethaddr[1],
+		    idprom->id_ethaddr[2], idprom->id_ethaddr[3],
+		    idprom->id_ethaddr[4], idprom->id_ethaddr[5]);
+#ifdef CONFIG_SUN4
+	sun4setup();
+#endif
+}
diff --git a/arch/sparc/kernel/init_task.c b/arch/sparc/kernel/init_task.c
new file mode 100644
index 0000000..fc31de6
--- /dev/null
+++ b/arch/sparc/kernel/init_task.c
@@ -0,0 +1,28 @@
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init_task.h>
+#include <linux/mqueue.h>
+
+#include <asm/pgtable.h>
+#include <asm/uaccess.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);
+struct task_struct init_task = INIT_TASK(init_task);
+
+EXPORT_SYMBOL(init_mm);
+EXPORT_SYMBOL(init_task);
+
+/* .text section in head.S is aligned at 8k boundary and this gets linked
+ * right after that so that the init_thread_union is aligned properly as well.
+ * If this is not aligned on a 8k boundry, then you should change code
+ * in etrap.S which assumes it.
+ */
+union thread_union init_thread_union
+	__attribute__((section (".text\"\n\t#")))
+	__attribute__((aligned (THREAD_SIZE)))
+	= { INIT_THREAD_INFO(init_task) };
diff --git a/arch/sparc/kernel/ioport.c b/arch/sparc/kernel/ioport.c
new file mode 100644
index 0000000..d0f2bd2
--- /dev/null
+++ b/arch/sparc/kernel/ioport.c
@@ -0,0 +1,731 @@
+/* $Id: ioport.c,v 1.45 2001/10/30 04:54:21 davem Exp $
+ * ioport.c:  Simple io mapping allocator.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ *
+ * 1996: sparc_free_io, 1999: ioremap()/iounmap() by Pete Zaitcev.
+ *
+ * 2000/01/29
+ * <rth> zait: as long as pci_alloc_consistent produces something addressable, 
+ *	things are ok.
+ * <zaitcev> rth: no, it is relevant, because get_free_pages returns you a
+ *	pointer into the big page mapping
+ * <rth> zait: so what?
+ * <rth> zait: remap_it_my_way(virt_to_phys(get_free_page()))
+ * <zaitcev> Hmm
+ * <zaitcev> Suppose I did this remap_it_my_way(virt_to_phys(get_free_page())).
+ *	So far so good.
+ * <zaitcev> Now, driver calls pci_free_consistent(with result of
+ *	remap_it_my_way()).
+ * <zaitcev> How do you find the address to pass to free_pages()?
+ * <rth> zait: walk the page tables?  It's only two or three level after all.
+ * <rth> zait: you have to walk them anyway to remove the mapping.
+ * <zaitcev> Hmm
+ * <zaitcev> Sounds reasonable
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/pci.h>		/* struct pci_dev */
+#include <linux/proc_fs.h>
+
+#include <asm/io.h>
+#include <asm/vaddrs.h>
+#include <asm/oplib.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#include <asm/dma.h>
+
+#define mmu_inval_dma_area(p, l)	/* Anton pulled it out for 2.4.0-xx */
+
+struct resource *_sparc_find_resource(struct resource *r, unsigned long);
+
+static void __iomem *_sparc_ioremap(struct resource *res, u32 bus, u32 pa, int sz);
+static void __iomem *_sparc_alloc_io(unsigned int busno, unsigned long phys,
+    unsigned long size, char *name);
+static void _sparc_free_io(struct resource *res);
+
+/* This points to the next to use virtual memory for DVMA mappings */
+static struct resource _sparc_dvma = {
+	.name = "sparc_dvma", .start = DVMA_VADDR, .end = DVMA_END - 1
+};
+/* This points to the start of I/O mappings, cluable from outside. */
+/*ext*/ struct resource sparc_iomap = {
+	.name = "sparc_iomap", .start = IOBASE_VADDR, .end = IOBASE_END - 1
+};
+
+/*
+ * Our mini-allocator...
+ * Boy this is gross! We need it because we must map I/O for
+ * timers and interrupt controller before the kmalloc is available.
+ */
+
+#define XNMLN  15
+#define XNRES  10	/* SS-10 uses 8 */
+
+struct xresource {
+	struct resource xres;	/* Must be first */
+	int xflag;		/* 1 == used */
+	char xname[XNMLN+1];
+};
+
+static struct xresource xresv[XNRES];
+
+static struct xresource *xres_alloc(void) {
+	struct xresource *xrp;
+	int n;
+
+	xrp = xresv;
+	for (n = 0; n < XNRES; n++) {
+		if (xrp->xflag == 0) {
+			xrp->xflag = 1;
+			return xrp;
+		}
+		xrp++;
+	}
+	return NULL;
+}
+
+static void xres_free(struct xresource *xrp) {
+	xrp->xflag = 0;
+}
+
+/*
+ * These are typically used in PCI drivers
+ * which are trying to be cross-platform.
+ *
+ * Bus type is always zero on IIep.
+ */
+void __iomem *ioremap(unsigned long offset, unsigned long size)
+{
+	char name[14];
+
+	sprintf(name, "phys_%08x", (u32)offset);
+	return _sparc_alloc_io(0, offset, size, name);
+}
+
+/*
+ * Comlimentary to ioremap().
+ */
+void iounmap(volatile void __iomem *virtual)
+{
+	unsigned long vaddr = (unsigned long) virtual & PAGE_MASK;
+	struct resource *res;
+
+	if ((res = _sparc_find_resource(&sparc_iomap, vaddr)) == NULL) {
+		printk("free_io/iounmap: cannot free %lx\n", vaddr);
+		return;
+	}
+	_sparc_free_io(res);
+
+	if ((char *)res >= (char*)xresv && (char *)res < (char *)&xresv[XNRES]) {
+		xres_free((struct xresource *)res);
+	} else {
+		kfree(res);
+	}
+}
+
+/*
+ */
+void __iomem *sbus_ioremap(struct resource *phyres, unsigned long offset,
+    unsigned long size, char *name)
+{
+	return _sparc_alloc_io(phyres->flags & 0xF,
+	    phyres->start + offset, size, name);
+}
+
+/*
+ */
+void sbus_iounmap(volatile void __iomem *addr, unsigned long size)
+{
+	iounmap(addr);
+}
+
+/*
+ * Meat of mapping
+ */
+static void __iomem *_sparc_alloc_io(unsigned int busno, unsigned long phys,
+    unsigned long size, char *name)
+{
+	static int printed_full;
+	struct xresource *xres;
+	struct resource *res;
+	char *tack;
+	int tlen;
+	void __iomem *va;	/* P3 diag */
+
+	if (name == NULL) name = "???";
+
+	if ((xres = xres_alloc()) != 0) {
+		tack = xres->xname;
+		res = &xres->xres;
+	} else {
+		if (!printed_full) {
+			printk("ioremap: done with statics, switching to malloc\n");
+			printed_full = 1;
+		}
+		tlen = strlen(name);
+		tack = kmalloc(sizeof (struct resource) + tlen + 1, GFP_KERNEL);
+		if (tack == NULL) return NULL;
+		memset(tack, 0, sizeof(struct resource));
+		res = (struct resource *) tack;
+		tack += sizeof (struct resource);
+	}
+
+	strlcpy(tack, name, XNMLN+1);
+	res->name = tack;
+
+	va = _sparc_ioremap(res, busno, phys, size);
+	/* printk("ioremap(0x%x:%08lx[0x%lx])=%p\n", busno, phys, size, va); */ /* P3 diag */
+	return va;
+}
+
+/*
+ */
+static void __iomem *
+_sparc_ioremap(struct resource *res, u32 bus, u32 pa, int sz)
+{
+	unsigned long offset = ((unsigned long) pa) & (~PAGE_MASK);
+
+	if (allocate_resource(&sparc_iomap, res,
+	    (offset + sz + PAGE_SIZE-1) & PAGE_MASK,
+	    sparc_iomap.start, sparc_iomap.end, PAGE_SIZE, NULL, NULL) != 0) {
+		/* Usually we cannot see printks in this case. */
+		prom_printf("alloc_io_res(%s): cannot occupy\n",
+		    (res->name != NULL)? res->name: "???");
+		prom_halt();
+	}
+
+	pa &= PAGE_MASK;
+	sparc_mapiorange(bus, pa, res->start, res->end - res->start + 1);
+
+	return (void __iomem *) (res->start + offset);
+}
+
+/*
+ * Comlimentary to _sparc_ioremap().
+ */
+static void _sparc_free_io(struct resource *res)
+{
+	unsigned long plen;
+
+	plen = res->end - res->start + 1;
+	if ((plen & (PAGE_SIZE-1)) != 0) BUG();
+	sparc_unmapiorange(res->start, plen);
+	release_resource(res);
+}
+
+#ifdef CONFIG_SBUS
+
+void sbus_set_sbus64(struct sbus_dev *sdev, int x) {
+	printk("sbus_set_sbus64: unsupported\n");
+}
+
+/*
+ * Allocate a chunk of memory suitable for DMA.
+ * Typically devices use them for control blocks.
+ * CPU may access them without any explicit flushing.
+ *
+ * XXX Some clever people know that sdev is not used and supply NULL. Watch.
+ */
+void *sbus_alloc_consistent(struct sbus_dev *sdev, long len, u32 *dma_addrp)
+{
+	unsigned long len_total = (len + PAGE_SIZE-1) & PAGE_MASK;
+	unsigned long va;
+	struct resource *res;
+	int order;
+
+	/* XXX why are some lenghts signed, others unsigned? */
+	if (len <= 0) {
+		return NULL;
+	}
+	/* XXX So what is maxphys for us and how do drivers know it? */
+	if (len > 256*1024) {			/* __get_free_pages() limit */
+		return NULL;
+	}
+
+	order = get_order(len_total);
+	if ((va = __get_free_pages(GFP_KERNEL, order)) == 0)
+		goto err_nopages;
+
+	if ((res = kmalloc(sizeof(struct resource), GFP_KERNEL)) == NULL)
+		goto err_nomem;
+	memset((char*)res, 0, sizeof(struct resource));
+
+	if (allocate_resource(&_sparc_dvma, res, len_total,
+	    _sparc_dvma.start, _sparc_dvma.end, PAGE_SIZE, NULL, NULL) != 0) {
+		printk("sbus_alloc_consistent: cannot occupy 0x%lx", len_total);
+		goto err_nova;
+	}
+	mmu_inval_dma_area(va, len_total);
+	// XXX The mmu_map_dma_area does this for us below, see comments.
+	// sparc_mapiorange(0, virt_to_phys(va), res->start, len_total);
+	/*
+	 * XXX That's where sdev would be used. Currently we load
+	 * all iommu tables with the same translations.
+	 */
+	if (mmu_map_dma_area(dma_addrp, va, res->start, len_total) != 0)
+		goto err_noiommu;
+
+	return (void *)res->start;
+
+err_noiommu:
+	release_resource(res);
+err_nova:
+	free_pages(va, order);
+err_nomem:
+	kfree(res);
+err_nopages:
+	return NULL;
+}
+
+void sbus_free_consistent(struct sbus_dev *sdev, long n, void *p, u32 ba)
+{
+	struct resource *res;
+	struct page *pgv;
+
+	if ((res = _sparc_find_resource(&_sparc_dvma,
+	    (unsigned long)p)) == NULL) {
+		printk("sbus_free_consistent: cannot free %p\n", p);
+		return;
+	}
+
+	if (((unsigned long)p & (PAGE_SIZE-1)) != 0) {
+		printk("sbus_free_consistent: unaligned va %p\n", p);
+		return;
+	}
+
+	n = (n + PAGE_SIZE-1) & PAGE_MASK;
+	if ((res->end-res->start)+1 != n) {
+		printk("sbus_free_consistent: region 0x%lx asked 0x%lx\n",
+		    (long)((res->end-res->start)+1), n);
+		return;
+	}
+
+	release_resource(res);
+	kfree(res);
+
+	/* mmu_inval_dma_area(va, n); */ /* it's consistent, isn't it */
+	pgv = mmu_translate_dvma(ba);
+	mmu_unmap_dma_area(ba, n);
+
+	__free_pages(pgv, get_order(n));
+}
+
+/*
+ * Map a chunk of memory so that devices can see it.
+ * CPU view of this memory may be inconsistent with
+ * a device view and explicit flushing is necessary.
+ */
+dma_addr_t sbus_map_single(struct sbus_dev *sdev, void *va, size_t len, int direction)
+{
+	/* XXX why are some lenghts signed, others unsigned? */
+	if (len <= 0) {
+		return 0;
+	}
+	/* XXX So what is maxphys for us and how do drivers know it? */
+	if (len > 256*1024) {			/* __get_free_pages() limit */
+		return 0;
+	}
+	return mmu_get_scsi_one(va, len, sdev->bus);
+}
+
+void sbus_unmap_single(struct sbus_dev *sdev, dma_addr_t ba, size_t n, int direction)
+{
+	mmu_release_scsi_one(ba, n, sdev->bus);
+}
+
+int sbus_map_sg(struct sbus_dev *sdev, struct scatterlist *sg, int n, int direction)
+{
+	mmu_get_scsi_sgl(sg, n, sdev->bus);
+
+	/*
+	 * XXX sparc64 can return a partial length here. sun4c should do this
+	 * but it currently panics if it can't fulfill the request - Anton
+	 */
+	return n;
+}
+
+void sbus_unmap_sg(struct sbus_dev *sdev, struct scatterlist *sg, int n, int direction)
+{
+	mmu_release_scsi_sgl(sg, n, sdev->bus);
+}
+
+/*
+ */
+void sbus_dma_sync_single_for_cpu(struct sbus_dev *sdev, dma_addr_t ba, size_t size, int direction)
+{
+#if 0
+	unsigned long va;
+	struct resource *res;
+
+	/* We do not need the resource, just print a message if invalid. */
+	res = _sparc_find_resource(&_sparc_dvma, ba);
+	if (res == NULL)
+		panic("sbus_dma_sync_single: 0x%x\n", ba);
+
+	va = page_address(mmu_translate_dvma(ba)); /* XXX higmem */
+	/*
+	 * XXX This bogosity will be fixed with the iommu rewrite coming soon
+	 * to a kernel near you. - Anton
+	 */
+	/* mmu_inval_dma_area(va, (size + PAGE_SIZE-1) & PAGE_MASK); */
+#endif
+}
+
+void sbus_dma_sync_single_for_device(struct sbus_dev *sdev, dma_addr_t ba, size_t size, int direction)
+{
+#if 0
+	unsigned long va;
+	struct resource *res;
+
+	/* We do not need the resource, just print a message if invalid. */
+	res = _sparc_find_resource(&_sparc_dvma, ba);
+	if (res == NULL)
+		panic("sbus_dma_sync_single: 0x%x\n", ba);
+
+	va = page_address(mmu_translate_dvma(ba)); /* XXX higmem */
+	/*
+	 * XXX This bogosity will be fixed with the iommu rewrite coming soon
+	 * to a kernel near you. - Anton
+	 */
+	/* mmu_inval_dma_area(va, (size + PAGE_SIZE-1) & PAGE_MASK); */
+#endif
+}
+
+void sbus_dma_sync_sg_for_cpu(struct sbus_dev *sdev, struct scatterlist *sg, int n, int direction)
+{
+	printk("sbus_dma_sync_sg_for_cpu: not implemented yet\n");
+}
+
+void sbus_dma_sync_sg_for_device(struct sbus_dev *sdev, struct scatterlist *sg, int n, int direction)
+{
+	printk("sbus_dma_sync_sg_for_device: not implemented yet\n");
+}
+#endif /* CONFIG_SBUS */
+
+#ifdef CONFIG_PCI
+
+/* Allocate and map kernel buffer using consistent mode DMA for a device.
+ * hwdev should be valid struct pci_dev pointer for PCI devices.
+ */
+void *pci_alloc_consistent(struct pci_dev *pdev, size_t len, dma_addr_t *pba)
+{
+	unsigned long len_total = (len + PAGE_SIZE-1) & PAGE_MASK;
+	unsigned long va;
+	struct resource *res;
+	int order;
+
+	if (len == 0) {
+		return NULL;
+	}
+	if (len > 256*1024) {			/* __get_free_pages() limit */
+		return NULL;
+	}
+
+	order = get_order(len_total);
+	va = __get_free_pages(GFP_KERNEL, order);
+	if (va == 0) {
+		printk("pci_alloc_consistent: no %ld pages\n", len_total>>PAGE_SHIFT);
+		return NULL;
+	}
+
+	if ((res = kmalloc(sizeof(struct resource), GFP_KERNEL)) == NULL) {
+		free_pages(va, order);
+		printk("pci_alloc_consistent: no core\n");
+		return NULL;
+	}
+	memset((char*)res, 0, sizeof(struct resource));
+
+	if (allocate_resource(&_sparc_dvma, res, len_total,
+	    _sparc_dvma.start, _sparc_dvma.end, PAGE_SIZE, NULL, NULL) != 0) {
+		printk("pci_alloc_consistent: cannot occupy 0x%lx", len_total);
+		free_pages(va, order);
+		kfree(res);
+		return NULL;
+	}
+	mmu_inval_dma_area(va, len_total);
+#if 0
+/* P3 */ printk("pci_alloc_consistent: kva %lx uncva %lx phys %lx size %lx\n",
+  (long)va, (long)res->start, (long)virt_to_phys(va), len_total);
+#endif
+	sparc_mapiorange(0, virt_to_phys(va), res->start, len_total);
+
+	*pba = virt_to_phys(va); /* equals virt_to_bus (R.I.P.) for us. */
+	return (void *) res->start;
+}
+
+/* Free and unmap a consistent DMA buffer.
+ * cpu_addr is what was returned from pci_alloc_consistent,
+ * size must be the same as what as passed into pci_alloc_consistent,
+ * and likewise dma_addr must be the same as what *dma_addrp was set to.
+ *
+ * References to the memory and mappings assosciated with cpu_addr/dma_addr
+ * past this call are illegal.
+ */
+void pci_free_consistent(struct pci_dev *pdev, size_t n, void *p, dma_addr_t ba)
+{
+	struct resource *res;
+	unsigned long pgp;
+
+	if ((res = _sparc_find_resource(&_sparc_dvma,
+	    (unsigned long)p)) == NULL) {
+		printk("pci_free_consistent: cannot free %p\n", p);
+		return;
+	}
+
+	if (((unsigned long)p & (PAGE_SIZE-1)) != 0) {
+		printk("pci_free_consistent: unaligned va %p\n", p);
+		return;
+	}
+
+	n = (n + PAGE_SIZE-1) & PAGE_MASK;
+	if ((res->end-res->start)+1 != n) {
+		printk("pci_free_consistent: region 0x%lx asked 0x%lx\n",
+		    (long)((res->end-res->start)+1), (long)n);
+		return;
+	}
+
+	pgp = (unsigned long) phys_to_virt(ba);	/* bus_to_virt actually */
+	mmu_inval_dma_area(pgp, n);
+	sparc_unmapiorange((unsigned long)p, n);
+
+	release_resource(res);
+	kfree(res);
+
+	free_pages(pgp, get_order(n));
+}
+
+/* Map a single buffer of the indicated size for DMA in streaming mode.
+ * The 32-bit bus address to use is returned.
+ *
+ * Once the device is given the dma address, the device owns this memory
+ * until either pci_unmap_single or pci_dma_sync_single_* is performed.
+ */
+dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size,
+    int direction)
+{
+	if (direction == PCI_DMA_NONE)
+		BUG();
+	/* IIep is write-through, not flushing. */
+	return virt_to_phys(ptr);
+}
+
+/* Unmap a single streaming mode DMA translation.  The dma_addr and size
+ * must match what was provided for in a previous pci_map_single call.  All
+ * other usages are undefined.
+ *
+ * After this call, reads by the cpu to the buffer are guaranteed to see
+ * whatever the device wrote there.
+ */
+void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t ba, size_t size,
+    int direction)
+{
+	if (direction == PCI_DMA_NONE)
+		BUG();
+	if (direction != PCI_DMA_TODEVICE) {
+		mmu_inval_dma_area((unsigned long)phys_to_virt(ba),
+		    (size + PAGE_SIZE-1) & PAGE_MASK);
+	}
+}
+
+/*
+ * Same as pci_map_single, but with pages.
+ */
+dma_addr_t pci_map_page(struct pci_dev *hwdev, struct page *page,
+			unsigned long offset, size_t size, int direction)
+{
+	if (direction == PCI_DMA_NONE)
+		BUG();
+	/* IIep is write-through, not flushing. */
+	return page_to_phys(page) + offset;
+}
+
+void pci_unmap_page(struct pci_dev *hwdev,
+			dma_addr_t dma_address, size_t size, int direction)
+{
+	if (direction == PCI_DMA_NONE)
+		BUG();
+	/* mmu_inval_dma_area XXX */
+}
+
+/* Map a set of buffers described by scatterlist in streaming
+ * mode for DMA.  This is the scather-gather version of the
+ * above pci_map_single interface.  Here the scatter gather list
+ * elements are each tagged with the appropriate dma address
+ * and length.  They are obtained via sg_dma_{address,length}(SG).
+ *
+ * NOTE: An implementation may be able to use a smaller number of
+ *       DMA address/length pairs than there are SG table elements.
+ *       (for example via virtual mapping capabilities)
+ *       The routine returns the number of addr/length pairs actually
+ *       used, at most nents.
+ *
+ * Device ownership issues as mentioned above for pci_map_single are
+ * the same here.
+ */
+int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents,
+    int direction)
+{
+	int n;
+
+	if (direction == PCI_DMA_NONE)
+		BUG();
+	/* IIep is write-through, not flushing. */
+	for (n = 0; n < nents; n++) {
+		if (page_address(sg->page) == NULL) BUG();
+		sg->dvma_address = virt_to_phys(page_address(sg->page));
+		sg->dvma_length = sg->length;
+		sg++;
+	}
+	return nents;
+}
+
+/* Unmap a set of streaming mode DMA translations.
+ * Again, cpu read rules concerning calls here are the same as for
+ * pci_unmap_single() above.
+ */
+void pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents,
+    int direction)
+{
+	int n;
+
+	if (direction == PCI_DMA_NONE)
+		BUG();
+	if (direction != PCI_DMA_TODEVICE) {
+		for (n = 0; n < nents; n++) {
+			if (page_address(sg->page) == NULL) BUG();
+			mmu_inval_dma_area(
+			    (unsigned long) page_address(sg->page),
+			    (sg->length + PAGE_SIZE-1) & PAGE_MASK);
+			sg++;
+		}
+	}
+}
+
+/* Make physical memory consistent for a single
+ * streaming mode DMA translation before or after a transfer.
+ *
+ * If you perform a pci_map_single() but wish to interrogate the
+ * buffer using the cpu, yet do not wish to teardown the PCI dma
+ * mapping, you must call this function before doing so.  At the
+ * next point you give the PCI dma address back to the card, you
+ * must first perform a pci_dma_sync_for_device, and then the
+ * device again owns the buffer.
+ */
+void pci_dma_sync_single_for_cpu(struct pci_dev *hwdev, dma_addr_t ba, size_t size, int direction)
+{
+	if (direction == PCI_DMA_NONE)
+		BUG();
+	if (direction != PCI_DMA_TODEVICE) {
+		mmu_inval_dma_area((unsigned long)phys_to_virt(ba),
+		    (size + PAGE_SIZE-1) & PAGE_MASK);
+	}
+}
+
+void pci_dma_sync_single_for_device(struct pci_dev *hwdev, dma_addr_t ba, size_t size, int direction)
+{
+	if (direction == PCI_DMA_NONE)
+		BUG();
+	if (direction != PCI_DMA_TODEVICE) {
+		mmu_inval_dma_area((unsigned long)phys_to_virt(ba),
+		    (size + PAGE_SIZE-1) & PAGE_MASK);
+	}
+}
+
+/* Make physical memory consistent for a set of streaming
+ * mode DMA translations after a transfer.
+ *
+ * The same as pci_dma_sync_single_* but for a scatter-gather list,
+ * same rules and usage.
+ */
+void pci_dma_sync_sg_for_cpu(struct pci_dev *hwdev, struct scatterlist *sg, int nents, int direction)
+{
+	int n;
+
+	if (direction == PCI_DMA_NONE)
+		BUG();
+	if (direction != PCI_DMA_TODEVICE) {
+		for (n = 0; n < nents; n++) {
+			if (page_address(sg->page) == NULL) BUG();
+			mmu_inval_dma_area(
+			    (unsigned long) page_address(sg->page),
+			    (sg->length + PAGE_SIZE-1) & PAGE_MASK);
+			sg++;
+		}
+	}
+}
+
+void pci_dma_sync_sg_for_device(struct pci_dev *hwdev, struct scatterlist *sg, int nents, int direction)
+{
+	int n;
+
+	if (direction == PCI_DMA_NONE)
+		BUG();
+	if (direction != PCI_DMA_TODEVICE) {
+		for (n = 0; n < nents; n++) {
+			if (page_address(sg->page) == NULL) BUG();
+			mmu_inval_dma_area(
+			    (unsigned long) page_address(sg->page),
+			    (sg->length + PAGE_SIZE-1) & PAGE_MASK);
+			sg++;
+		}
+	}
+}
+#endif /* CONFIG_PCI */
+
+#ifdef CONFIG_PROC_FS
+
+static int
+_sparc_io_get_info(char *buf, char **start, off_t fpos, int length, int *eof,
+    void *data)
+{
+	char *p = buf, *e = buf + length;
+	struct resource *r;
+	const char *nm;
+
+	for (r = ((struct resource *)data)->child; r != NULL; r = r->sibling) {
+		if (p + 32 >= e)	/* Better than nothing */
+			break;
+		if ((nm = r->name) == 0) nm = "???";
+		p += sprintf(p, "%08lx-%08lx: %s\n", r->start, r->end, nm);
+	}
+
+	return p-buf;
+}
+
+#endif /* CONFIG_PROC_FS */
+
+/*
+ * This is a version of find_resource and it belongs to kernel/resource.c.
+ * Until we have agreement with Linus and Martin, it lingers here.
+ *
+ * XXX Too slow. Can have 8192 DVMA pages on sun4m in the worst case.
+ * This probably warrants some sort of hashing.
+ */
+struct resource *
+_sparc_find_resource(struct resource *root, unsigned long hit)
+{
+        struct resource *tmp;
+
+	for (tmp = root->child; tmp != 0; tmp = tmp->sibling) {
+		if (tmp->start <= hit && tmp->end >= hit)
+			return tmp;
+	}
+	return NULL;
+}
+
+void register_proc_sparc_ioport(void)
+{
+#ifdef CONFIG_PROC_FS
+	create_proc_read_entry("io_map",0,NULL,_sparc_io_get_info,&sparc_iomap);
+	create_proc_read_entry("dvma_map",0,NULL,_sparc_io_get_info,&_sparc_dvma);
+#endif
+}
diff --git a/arch/sparc/kernel/irq.c b/arch/sparc/kernel/irq.c
new file mode 100644
index 0000000..410b9a7
--- /dev/null
+++ b/arch/sparc/kernel/irq.c
@@ -0,0 +1,614 @@
+/*  $Id: irq.c,v 1.114 2001/12/11 04:55:51 davem Exp $
+ *  arch/sparc/kernel/irq.c:  Interrupt request handling routines. On the
+ *                            Sparc the IRQ's are basically 'cast in stone'
+ *                            and you are supposed to probe the prom's device
+ *                            node trees to find out who's got which IRQ.
+ *
+ *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *  Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ *  Copyright (C) 1995,2002 Pete A. Zaitcev (zaitcev@yahoo.com)
+ *  Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
+ *  Copyright (C) 1998-2000 Anton Blanchard (anton@samba.org)
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/linkage.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/delay.h>
+#include <linux/threads.h>
+#include <linux/spinlock.h>
+#include <linux/seq_file.h>
+
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/psr.h>
+#include <asm/smp.h>
+#include <asm/vaddrs.h>
+#include <asm/timer.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/traps.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/pcic.h>
+#include <asm/cacheflush.h>
+
+#ifdef CONFIG_SMP
+#define SMP_NOP2 "nop; nop;\n\t"
+#define SMP_NOP3 "nop; nop; nop;\n\t"
+#else
+#define SMP_NOP2
+#define SMP_NOP3
+#endif /* SMP */
+unsigned long __local_irq_save(void)
+{
+	unsigned long retval;
+	unsigned long tmp;
+
+	__asm__ __volatile__(
+		"rd	%%psr, %0\n\t"
+		SMP_NOP3	/* Sun4m + Cypress + SMP bug */
+		"or	%0, %2, %1\n\t"
+		"wr	%1, 0, %%psr\n\t"
+		"nop; nop; nop\n"
+		: "=&r" (retval), "=r" (tmp)
+		: "i" (PSR_PIL)
+		: "memory");
+
+	return retval;
+}
+
+void local_irq_enable(void)
+{
+	unsigned long tmp;
+
+	__asm__ __volatile__(
+		"rd	%%psr, %0\n\t"
+		SMP_NOP3	/* Sun4m + Cypress + SMP bug */
+		"andn	%0, %1, %0\n\t"
+		"wr	%0, 0, %%psr\n\t"
+		"nop; nop; nop\n"
+		: "=&r" (tmp)
+		: "i" (PSR_PIL)
+		: "memory");
+}
+
+void local_irq_restore(unsigned long old_psr)
+{
+	unsigned long tmp;
+
+	__asm__ __volatile__(
+		"rd	%%psr, %0\n\t"
+		"and	%2, %1, %2\n\t"
+		SMP_NOP2	/* Sun4m + Cypress + SMP bug */
+		"andn	%0, %1, %0\n\t"
+		"wr	%0, %2, %%psr\n\t"
+		"nop; nop; nop\n"
+		: "=&r" (tmp)
+		: "i" (PSR_PIL), "r" (old_psr)
+		: "memory");
+}
+
+EXPORT_SYMBOL(__local_irq_save);
+EXPORT_SYMBOL(local_irq_enable);
+EXPORT_SYMBOL(local_irq_restore);
+
+/*
+ * Dave Redman (djhr@tadpole.co.uk)
+ *
+ * IRQ numbers.. These are no longer restricted to 15..
+ *
+ * this is done to enable SBUS cards and onboard IO to be masked
+ * correctly. using the interrupt level isn't good enough.
+ *
+ * For example:
+ *   A device interrupting at sbus level6 and the Floppy both come in
+ *   at IRQ11, but enabling and disabling them requires writing to
+ *   different bits in the SLAVIO/SEC.
+ *
+ * As a result of these changes sun4m machines could now support
+ * directed CPU interrupts using the existing enable/disable irq code
+ * with tweaks.
+ *
+ */
+
+static void irq_panic(void)
+{
+    extern char *cputypval;
+    prom_printf("machine: %s doesn't have irq handlers defined!\n",cputypval);
+    prom_halt();
+}
+
+void (*sparc_init_timers)(irqreturn_t (*)(int, void *,struct pt_regs *)) =
+    (void (*)(irqreturn_t (*)(int, void *,struct pt_regs *))) irq_panic;
+
+/*
+ * Dave Redman (djhr@tadpole.co.uk)
+ *
+ * There used to be extern calls and hard coded values here.. very sucky!
+ * instead, because some of the devices attach very early, I do something
+ * equally sucky but at least we'll never try to free statically allocated
+ * space or call kmalloc before kmalloc_init :(.
+ * 
+ * In fact it's the timer10 that attaches first.. then timer14
+ * then kmalloc_init is called.. then the tty interrupts attach.
+ * hmmm....
+ *
+ */
+#define MAX_STATIC_ALLOC	4
+struct irqaction static_irqaction[MAX_STATIC_ALLOC];
+int static_irq_count;
+
+struct irqaction *irq_action[NR_IRQS] = {
+	[0 ... (NR_IRQS-1)] = NULL
+};
+
+/* Used to protect the IRQ action lists */
+DEFINE_SPINLOCK(irq_action_lock);
+
+int show_interrupts(struct seq_file *p, void *v)
+{
+	int i = *(loff_t *) v;
+	struct irqaction * action;
+	unsigned long flags;
+#ifdef CONFIG_SMP
+	int j;
+#endif
+
+	if (sparc_cpu_model == sun4d) {
+		extern int show_sun4d_interrupts(struct seq_file *, void *);
+		
+		return show_sun4d_interrupts(p, v);
+	}
+	spin_lock_irqsave(&irq_action_lock, flags);
+	if (i < NR_IRQS) {
+	        action = *(i + irq_action);
+		if (!action) 
+			goto out_unlock;
+		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(cpu_logical_map(j)).irqs[i]);
+		}
+#endif
+		seq_printf(p, " %c %s",
+			(action->flags & SA_INTERRUPT) ? '+' : ' ',
+			action->name);
+		for (action=action->next; action; action = action->next) {
+			seq_printf(p, ",%s %s",
+				(action->flags & SA_INTERRUPT) ? " +" : "",
+				action->name);
+		}
+		seq_putc(p, '\n');
+	}
+out_unlock:
+	spin_unlock_irqrestore(&irq_action_lock, flags);
+	return 0;
+}
+
+void free_irq(unsigned int irq, void *dev_id)
+{
+	struct irqaction * action;
+	struct irqaction * tmp = NULL;
+        unsigned long flags;
+	unsigned int cpu_irq;
+	
+	if (sparc_cpu_model == sun4d) {
+		extern void sun4d_free_irq(unsigned int, void *);
+		
+		sun4d_free_irq(irq, dev_id);
+		return;
+	}
+	cpu_irq = irq & (NR_IRQS - 1);
+        if (cpu_irq > 14) {  /* 14 irq levels on the sparc */
+                printk("Trying to free bogus IRQ %d\n", irq);
+                return;
+        }
+
+	spin_lock_irqsave(&irq_action_lock, flags);
+
+	action = *(cpu_irq + irq_action);
+
+	if (!action->handler) {
+		printk("Trying to free free IRQ%d\n",irq);
+		goto out_unlock;
+	}
+	if (dev_id) {
+		for (; action; action = action->next) {
+			if (action->dev_id == dev_id)
+				break;
+			tmp = action;
+		}
+		if (!action) {
+			printk("Trying to free free shared IRQ%d\n",irq);
+			goto out_unlock;
+		}
+	} else if (action->flags & SA_SHIRQ) {
+		printk("Trying to free shared IRQ%d with NULL device ID\n", irq);
+		goto out_unlock;
+	}
+	if (action->flags & SA_STATIC_ALLOC)
+	{
+		/* This interrupt is marked as specially allocated
+		 * so it is a bad idea to free it.
+		 */
+		printk("Attempt to free statically allocated IRQ%d (%s)\n",
+		       irq, action->name);
+		goto out_unlock;
+	}
+	
+	if (action && tmp)
+		tmp->next = action->next;
+	else
+		*(cpu_irq + irq_action) = action->next;
+
+	spin_unlock_irqrestore(&irq_action_lock, flags);
+
+	synchronize_irq(irq);
+
+	spin_lock_irqsave(&irq_action_lock, flags);
+
+	kfree(action);
+
+	if (!(*(cpu_irq + irq_action)))
+		disable_irq(irq);
+
+out_unlock:
+	spin_unlock_irqrestore(&irq_action_lock, flags);
+}
+
+EXPORT_SYMBOL(free_irq);
+
+/*
+ * This is called when we want to synchronize with
+ * interrupts. We may for example tell a device to
+ * stop sending interrupts: but to make sure there
+ * are no interrupts that are executing on another
+ * CPU we need to call this function.
+ */
+#ifdef CONFIG_SMP
+void synchronize_irq(unsigned int irq)
+{
+	printk("synchronize_irq says: implement me!\n");
+	BUG();
+}
+#endif /* SMP */
+
+void unexpected_irq(int irq, void *dev_id, struct pt_regs * regs)
+{
+        int i;
+	struct irqaction * action;
+	unsigned int cpu_irq;
+	
+	cpu_irq = irq & (NR_IRQS - 1);
+	action = *(cpu_irq + irq_action);
+
+        printk("IO device interrupt, irq = %d\n", irq);
+        printk("PC = %08lx NPC = %08lx FP=%08lx\n", regs->pc, 
+		    regs->npc, regs->u_regs[14]);
+	if (action) {
+		printk("Expecting: ");
+        	for (i = 0; i < 16; i++)
+                	if (action->handler)
+                        	printk("[%s:%d:0x%x] ", action->name,
+				       (int) i, (unsigned int) action->handler);
+	}
+        printk("AIEEE\n");
+	panic("bogus interrupt received");
+}
+
+void handler_irq(int irq, struct pt_regs * regs)
+{
+	struct irqaction * action;
+	int cpu = smp_processor_id();
+#ifdef CONFIG_SMP
+	extern void smp4m_irq_rotate(int cpu);
+#endif
+
+	irq_enter();
+	disable_pil_irq(irq);
+#ifdef CONFIG_SMP
+	/* Only rotate on lower priority IRQ's (scsi, ethernet, etc.). */
+	if(irq < 10)
+		smp4m_irq_rotate(cpu);
+#endif
+	action = *(irq + irq_action);
+	kstat_cpu(cpu).irqs[irq]++;
+	do {
+		if (!action || !action->handler)
+			unexpected_irq(irq, NULL, regs);
+		action->handler(irq, action->dev_id, regs);
+		action = action->next;
+	} while (action);
+	enable_pil_irq(irq);
+	irq_exit();
+}
+
+#ifdef CONFIG_BLK_DEV_FD
+extern void floppy_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+void sparc_floppy_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int cpu = smp_processor_id();
+
+	disable_pil_irq(irq);
+	irq_enter();
+	kstat_cpu(cpu).irqs[irq]++;
+	floppy_interrupt(irq, dev_id, regs);
+	irq_exit();
+	enable_pil_irq(irq);
+	// XXX Eek, it's totally changed with preempt_count() and such
+	// if (softirq_pending(cpu))
+	//	do_softirq();
+}
+#endif
+
+/* Fast IRQ's on the Sparc can only have one routine attached to them,
+ * thus no sharing possible.
+ */
+int request_fast_irq(unsigned int irq,
+		     irqreturn_t (*handler)(int, void *, struct pt_regs *),
+		     unsigned long irqflags, const char *devname)
+{
+	struct irqaction *action;
+	unsigned long flags;
+	unsigned int cpu_irq;
+	int ret;
+#ifdef CONFIG_SMP
+	struct tt_entry *trap_table;
+	extern struct tt_entry trapbase_cpu1, trapbase_cpu2, trapbase_cpu3;
+#endif
+	
+	cpu_irq = irq & (NR_IRQS - 1);
+	if(cpu_irq > 14) {
+		ret = -EINVAL;
+		goto out;
+	}
+	if(!handler) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	spin_lock_irqsave(&irq_action_lock, flags);
+
+	action = *(cpu_irq + irq_action);
+	if(action) {
+		if(action->flags & SA_SHIRQ)
+			panic("Trying to register fast irq when already shared.\n");
+		if(irqflags & SA_SHIRQ)
+			panic("Trying to register fast irq as shared.\n");
+
+		/* Anyway, someone already owns it so cannot be made fast. */
+		printk("request_fast_irq: Trying to register yet already owned.\n");
+		ret = -EBUSY;
+		goto out_unlock;
+	}
+
+	/* If this is flagged as statically allocated then we use our
+	 * private struct which is never freed.
+	 */
+	if (irqflags & SA_STATIC_ALLOC) {
+	    if (static_irq_count < MAX_STATIC_ALLOC)
+		action = &static_irqaction[static_irq_count++];
+	    else
+		printk("Fast IRQ%d (%s) SA_STATIC_ALLOC failed using kmalloc\n",
+		       irq, devname);
+	}
+	
+	if (action == NULL)
+	    action = (struct irqaction *)kmalloc(sizeof(struct irqaction),
+						 GFP_ATOMIC);
+	
+	if (!action) { 
+		ret = -ENOMEM;
+		goto out_unlock;
+	}
+
+	/* Dork with trap table if we get this far. */
+#define INSTANTIATE(table) \
+	table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_one = SPARC_RD_PSR_L0; \
+	table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_two = \
+		SPARC_BRANCH((unsigned long) handler, \
+			     (unsigned long) &table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_two);\
+	table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_three = SPARC_RD_WIM_L3; \
+	table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_four = SPARC_NOP;
+
+	INSTANTIATE(sparc_ttable)
+#ifdef CONFIG_SMP
+	trap_table = &trapbase_cpu1; INSTANTIATE(trap_table)
+	trap_table = &trapbase_cpu2; INSTANTIATE(trap_table)
+	trap_table = &trapbase_cpu3; INSTANTIATE(trap_table)
+#endif
+#undef INSTANTIATE
+	/*
+	 * XXX Correct thing whould be to flush only I- and D-cache lines
+	 * which contain the handler in question. But as of time of the
+	 * writing we have no CPU-neutral interface to fine-grained flushes.
+	 */
+	flush_cache_all();
+
+	action->handler = handler;
+	action->flags = irqflags;
+	cpus_clear(action->mask);
+	action->name = devname;
+	action->dev_id = NULL;
+	action->next = NULL;
+
+	*(cpu_irq + irq_action) = action;
+
+	enable_irq(irq);
+
+	ret = 0;
+out_unlock:
+	spin_unlock_irqrestore(&irq_action_lock, flags);
+out:
+	return ret;
+}
+
+int request_irq(unsigned int irq,
+		irqreturn_t (*handler)(int, void *, struct pt_regs *),
+		unsigned long irqflags, const char * devname, void *dev_id)
+{
+	struct irqaction * action, *tmp = NULL;
+	unsigned long flags;
+	unsigned int cpu_irq;
+	int ret;
+	
+	if (sparc_cpu_model == sun4d) {
+		extern int sun4d_request_irq(unsigned int, 
+					     irqreturn_t (*)(int, void *, struct pt_regs *),
+					     unsigned long, const char *, void *);
+		return sun4d_request_irq(irq, handler, irqflags, devname, dev_id);
+	}
+	cpu_irq = irq & (NR_IRQS - 1);
+	if(cpu_irq > 14) {
+		ret = -EINVAL;
+		goto out;
+	}
+	if (!handler) {
+		ret = -EINVAL;
+		goto out;
+	}
+	    
+	spin_lock_irqsave(&irq_action_lock, flags);
+
+	action = *(cpu_irq + irq_action);
+	if (action) {
+		if ((action->flags & SA_SHIRQ) && (irqflags & SA_SHIRQ)) {
+			for (tmp = action; tmp->next; tmp = tmp->next);
+		} else {
+			ret = -EBUSY;
+			goto out_unlock;
+		}
+		if ((action->flags & SA_INTERRUPT) ^ (irqflags & SA_INTERRUPT)) {
+			printk("Attempt to mix fast and slow interrupts on IRQ%d denied\n", irq);
+			ret = -EBUSY;
+			goto out_unlock;
+		}   
+		action = NULL;		/* Or else! */
+	}
+
+	/* If this is flagged as statically allocated then we use our
+	 * private struct which is never freed.
+	 */
+	if (irqflags & SA_STATIC_ALLOC) {
+		if (static_irq_count < MAX_STATIC_ALLOC)
+			action = &static_irqaction[static_irq_count++];
+		else
+			printk("Request for IRQ%d (%s) SA_STATIC_ALLOC failed using kmalloc\n", irq, devname);
+	}
+	
+	if (action == NULL)
+		action = (struct irqaction *)kmalloc(sizeof(struct irqaction),
+						     GFP_ATOMIC);
+	
+	if (!action) { 
+		ret = -ENOMEM;
+		goto out_unlock;
+	}
+
+	action->handler = handler;
+	action->flags = irqflags;
+	cpus_clear(action->mask);
+	action->name = devname;
+	action->next = NULL;
+	action->dev_id = dev_id;
+
+	if (tmp)
+		tmp->next = action;
+	else
+		*(cpu_irq + irq_action) = action;
+
+	enable_irq(irq);
+
+	ret = 0;
+out_unlock:
+	spin_unlock_irqrestore(&irq_action_lock, flags);
+out:
+	return ret;
+}
+
+EXPORT_SYMBOL(request_irq);
+
+/* We really don't need these at all on the Sparc.  We only have
+ * stubs here because they are exported to modules.
+ */
+unsigned long probe_irq_on(void)
+{
+	return 0;
+}
+
+EXPORT_SYMBOL(probe_irq_on);
+
+int probe_irq_off(unsigned long mask)
+{
+	return 0;
+}
+
+EXPORT_SYMBOL(probe_irq_off);
+
+/* djhr
+ * This could probably be made indirect too and assigned in the CPU
+ * bits of the code. That would be much nicer I think and would also
+ * fit in with the idea of being able to tune your kernel for your machine
+ * by removing unrequired machine and device support.
+ *
+ */
+
+void __init init_IRQ(void)
+{
+	extern void sun4c_init_IRQ( void );
+	extern void sun4m_init_IRQ( void );
+	extern void sun4d_init_IRQ( void );
+
+	switch(sparc_cpu_model) {
+	case sun4c:
+	case sun4:
+		sun4c_init_IRQ();
+		break;
+
+	case sun4m:
+#ifdef CONFIG_PCI
+		pcic_probe();
+		if (pcic_present()) {
+			sun4m_pci_init_IRQ();
+			break;
+		}
+#endif
+		sun4m_init_IRQ();
+		break;
+		
+	case sun4d:
+		sun4d_init_IRQ();
+		break;
+
+	default:
+		prom_printf("Cannot initialize IRQ's on this Sun machine...");
+		break;
+	}
+	btfixup();
+}
+
+void init_irq_proc(void)
+{
+	/* For now, nothing... */
+}
diff --git a/arch/sparc/kernel/module.c b/arch/sparc/kernel/module.c
new file mode 100644
index 0000000..7931d6f
--- /dev/null
+++ b/arch/sparc/kernel/module.c
@@ -0,0 +1,159 @@
+/* Kernel module help for sparc32.
+ *
+ * Copyright (C) 2001 Rusty Russell.
+ * Copyright (C) 2002 David S. Miller.
+ */
+
+#include <linux/moduleloader.h>
+#include <linux/kernel.h>
+#include <linux/elf.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+
+void *module_alloc(unsigned long size)
+{
+	void *ret;
+
+	/* We handle the zero case fine, unlike vmalloc */
+	if (size == 0)
+		return NULL;
+
+	ret = vmalloc(size);
+	if (!ret)
+		ret = ERR_PTR(-ENOMEM);
+	else
+		memset(ret, 0, size);
+
+	return ret;
+}
+
+/* Free memory returned from module_core_alloc/module_init_alloc */
+void module_free(struct module *mod, void *module_region)
+{
+	vfree(module_region);
+	/* FIXME: If module_region == mod->init_region, trim exception
+           table entries. */
+}
+
+/* Make generic code ignore STT_REGISTER dummy undefined symbols,
+ * and replace references to .func with func as in ppc64's dedotify.
+ */
+int module_frob_arch_sections(Elf_Ehdr *hdr,
+			      Elf_Shdr *sechdrs,
+			      char *secstrings,
+			      struct module *mod)
+{
+	unsigned int symidx;
+	Elf32_Sym *sym;
+	char *strtab;
+	int i;
+
+	for (symidx = 0; sechdrs[symidx].sh_type != SHT_SYMTAB; symidx++) {
+		if (symidx == hdr->e_shnum-1) {
+			printk("%s: no symtab found.\n", mod->name);
+			return -ENOEXEC;
+		}
+	}
+	sym = (Elf32_Sym *)sechdrs[symidx].sh_addr;
+	strtab = (char *)sechdrs[sechdrs[symidx].sh_link].sh_addr;
+
+	for (i = 1; i < sechdrs[symidx].sh_size / sizeof(Elf_Sym); i++) {
+		if (sym[i].st_shndx == SHN_UNDEF) {
+			if (ELF32_ST_TYPE(sym[i].st_info) == STT_REGISTER)
+				sym[i].st_shndx = SHN_ABS;
+			else {
+				char *name = strtab + sym[i].st_name;
+				if (name[0] == '.')
+					memmove(name, name+1, strlen(name));
+			}
+		}
+	}
+	return 0;
+}
+
+int apply_relocate(Elf32_Shdr *sechdrs,
+		   const char *strtab,
+		   unsigned int symindex,
+		   unsigned int relsec,
+		   struct module *me)
+{
+	printk(KERN_ERR "module %s: non-ADD RELOCATION unsupported\n",
+	       me->name);
+	return -ENOEXEC;
+}
+
+int apply_relocate_add(Elf32_Shdr *sechdrs,
+		       const char *strtab,
+		       unsigned int symindex,
+		       unsigned int relsec,
+		       struct module *me)
+{
+	unsigned int i;
+	Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr;
+	Elf32_Sym *sym;
+	u8 *location;
+	u32 *loc32;
+
+	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
+		Elf32_Addr v;
+
+		/* This is where to make the change */
+		location = (u8 *)sechdrs[sechdrs[relsec].sh_info].sh_addr
+			+ rel[i].r_offset;
+		loc32 = (u32 *) location;
+		/* This is the symbol it is referring to.  Note that all
+		   undefined symbols have been resolved.  */
+		sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
+			+ ELF32_R_SYM(rel[i].r_info);
+		v = sym->st_value + rel[i].r_addend;
+
+		switch (ELF32_R_TYPE(rel[i].r_info)) {
+		case R_SPARC_32:
+			location[0] = v >> 24;
+			location[1] = v >> 16;
+			location[2] = v >>  8;
+			location[3] = v >>  0;
+			break;
+
+		case R_SPARC_WDISP30:
+			v -= (Elf32_Addr) location;
+			*loc32 = (*loc32 & ~0x3fffffff) |
+				((v >> 2) & 0x3fffffff);
+			break;
+
+		case R_SPARC_WDISP22:
+			v -= (Elf32_Addr) location;
+			*loc32 = (*loc32 & ~0x3fffff) |
+				((v >> 2) & 0x3fffff);
+			break;
+
+		case R_SPARC_LO10:
+			*loc32 = (*loc32 & ~0x3ff) | (v & 0x3ff);
+			break;
+
+		case R_SPARC_HI22:
+			*loc32 = (*loc32 & ~0x3fffff) |
+				((v >> 10) & 0x3fffff);
+			break;
+
+		default:
+			printk(KERN_ERR "module %s: Unknown relocation: %x\n",
+			       me->name,
+			       (int) (ELF32_R_TYPE(rel[i].r_info) & 0xff));
+			return -ENOEXEC;
+		};
+	}
+	return 0;
+}
+
+int module_finalize(const Elf_Ehdr *hdr,
+		    const Elf_Shdr *sechdrs,
+		    struct module *me)
+{
+	return 0;
+}
+
+void module_arch_cleanup(struct module *mod)
+{
+}
diff --git a/arch/sparc/kernel/muldiv.c b/arch/sparc/kernel/muldiv.c
new file mode 100644
index 0000000..37b9a49
--- /dev/null
+++ b/arch/sparc/kernel/muldiv.c
@@ -0,0 +1,240 @@
+/* $Id: muldiv.c,v 1.5 1997/12/15 20:07:20 ecd Exp $
+ * muldiv.c: Hardware multiply/division illegal instruction trap
+ *		for sun4c/sun4 (which do not have those instructions)
+ *
+ * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * 2004-12-25	Krzysztof Helt (krzysztof.h1@wp.pl) 
+ *		- fixed registers constrains in inline assembly declarations
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+/* #define DEBUG_MULDIV */
+
+static inline int has_imm13(int insn)
+{
+	return (insn & 0x2000);
+}
+
+static inline int is_foocc(int insn)
+{
+	return (insn & 0x800000);
+}
+
+static inline int sign_extend_imm13(int imm)
+{
+	return imm << 19 >> 19;
+}
+
+static inline void advance(struct pt_regs *regs)
+{
+	regs->pc   = regs->npc;
+	regs->npc += 4;
+}
+
+static inline void maybe_flush_windows(unsigned int rs1, unsigned int rs2,
+				       unsigned int rd)
+{
+	if(rs2 >= 16 || rs1 >= 16 || rd >= 16) {
+		/* Wheee... */
+		__asm__ __volatile__("save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "restore; restore; restore; restore;\n\t"
+				     "restore; restore; restore;\n\t");
+	}
+}
+
+#define fetch_reg(reg, regs) ({						\
+	struct reg_window __user *win;					\
+	register unsigned long ret;					\
+									\
+	if (!(reg)) ret = 0;						\
+	else if ((reg) < 16) {						\
+		ret = regs->u_regs[(reg)];				\
+	} else {							\
+		/* Ho hum, the slightly complicated case. */		\
+		win = (struct reg_window __user *)regs->u_regs[UREG_FP];\
+		if (get_user (ret, &win->locals[(reg) - 16])) return -1;\
+	}								\
+	ret;								\
+})
+
+static inline int
+store_reg(unsigned int result, unsigned int reg, struct pt_regs *regs)
+{
+	struct reg_window __user *win;
+
+	if (!reg)
+		return 0;
+	if (reg < 16) {
+		regs->u_regs[reg] = result;
+		return 0;
+	} else {
+		/* need to use put_user() in this case: */
+		win = (struct reg_window __user *) regs->u_regs[UREG_FP];
+		return (put_user(result, &win->locals[reg - 16]));
+	}
+}
+		
+extern void handle_hw_divzero (struct pt_regs *regs, unsigned long pc,
+			       unsigned long npc, unsigned long psr);
+
+/* Should return 0 if mul/div emulation succeeded and SIGILL should
+ * not be issued.
+ */
+int do_user_muldiv(struct pt_regs *regs, unsigned long pc)
+{
+	unsigned int insn;
+	int inst;
+	unsigned int rs1, rs2, rdv;
+
+	if (!pc)
+		return -1; /* This happens to often, I think */
+	if (get_user (insn, (unsigned int __user *)pc))
+		return -1;
+	if ((insn & 0xc1400000) != 0x80400000)
+		return -1;
+	inst = ((insn >> 19) & 0xf);
+	if ((inst & 0xe) != 10 && (inst & 0xe) != 14)
+		return -1;
+
+	/* Now we know we have to do something with umul, smul, udiv or sdiv */
+	rs1 = (insn >> 14) & 0x1f;
+	rs2 = insn & 0x1f;
+	rdv = (insn >> 25) & 0x1f;
+	if (has_imm13(insn)) {
+		maybe_flush_windows(rs1, 0, rdv);
+		rs2 = sign_extend_imm13(insn);
+	} else {
+		maybe_flush_windows(rs1, rs2, rdv);
+		rs2 = fetch_reg(rs2, regs);
+	}
+	rs1 = fetch_reg(rs1, regs);
+	switch (inst) {
+	case 10: /* umul */
+#ifdef DEBUG_MULDIV	
+		printk ("unsigned muldiv: 0x%x * 0x%x = ", rs1, rs2);
+#endif		
+		__asm__ __volatile__ ("\n\t"
+			"mov	%0, %%o0\n\t"
+			"call	.umul\n\t"
+			" mov	%1, %%o1\n\t"
+			"mov	%%o0, %0\n\t"
+			"mov	%%o1, %1\n\t"
+			: "=r" (rs1), "=r" (rs2)
+		        : "0" (rs1), "1" (rs2)
+			: "o0", "o1", "o2", "o3", "o4", "o5", "o7", "cc");
+#ifdef DEBUG_MULDIV
+		printk ("0x%x%08x\n", rs2, rs1);
+#endif
+		if (store_reg(rs1, rdv, regs))
+			return -1;
+		regs->y = rs2;
+		break;
+	case 11: /* smul */
+#ifdef DEBUG_MULDIV
+		printk ("signed muldiv: 0x%x * 0x%x = ", rs1, rs2);
+#endif
+		__asm__ __volatile__ ("\n\t"
+			"mov	%0, %%o0\n\t"
+			"call	.mul\n\t"
+			" mov	%1, %%o1\n\t"
+			"mov	%%o0, %0\n\t"
+			"mov	%%o1, %1\n\t"
+			: "=r" (rs1), "=r" (rs2)
+		        : "0" (rs1), "1" (rs2)
+			: "o0", "o1", "o2", "o3", "o4", "o5", "o7", "cc");
+#ifdef DEBUG_MULDIV
+		printk ("0x%x%08x\n", rs2, rs1);
+#endif
+		if (store_reg(rs1, rdv, regs))
+			return -1;
+		regs->y = rs2;
+		break;
+	case 14: /* udiv */
+#ifdef DEBUG_MULDIV
+		printk ("unsigned muldiv: 0x%x%08x / 0x%x = ", regs->y, rs1, rs2);
+#endif
+		if (!rs2) {
+#ifdef DEBUG_MULDIV
+			printk ("DIVISION BY ZERO\n");
+#endif
+			handle_hw_divzero (regs, pc, regs->npc, regs->psr);
+			return 0;
+		}
+		__asm__ __volatile__ ("\n\t"
+			"mov	%2, %%o0\n\t"
+			"mov	%0, %%o1\n\t"
+			"mov	%%g0, %%o2\n\t"
+			"call	__udivdi3\n\t"
+			" mov	%1, %%o3\n\t"
+			"mov	%%o1, %0\n\t"
+			"mov	%%o0, %1\n\t"
+			: "=r" (rs1), "=r" (rs2)
+			: "r" (regs->y), "0" (rs1), "1" (rs2)
+			: "o0", "o1", "o2", "o3", "o4", "o5", "o7",
+			  "g1", "g2", "g3", "cc");
+#ifdef DEBUG_MULDIV
+		printk ("0x%x\n", rs1);
+#endif
+		if (store_reg(rs1, rdv, regs))
+			return -1;
+		break;
+	case 15: /* sdiv */
+#ifdef DEBUG_MULDIV
+		printk ("signed muldiv: 0x%x%08x / 0x%x = ", regs->y, rs1, rs2);
+#endif
+		if (!rs2) {
+#ifdef DEBUG_MULDIV
+			printk ("DIVISION BY ZERO\n");
+#endif
+			handle_hw_divzero (regs, pc, regs->npc, regs->psr);
+			return 0;
+		}
+		__asm__ __volatile__ ("\n\t"
+			"mov	%2, %%o0\n\t"
+			"mov	%0, %%o1\n\t"
+			"mov	%%g0, %%o2\n\t"
+			"call	__divdi3\n\t"
+			" mov	%1, %%o3\n\t"
+			"mov	%%o1, %0\n\t"
+			"mov	%%o0, %1\n\t"
+			: "=r" (rs1), "=r" (rs2)
+			: "r" (regs->y), "0" (rs1), "1" (rs2)
+			: "o0", "o1", "o2", "o3", "o4", "o5", "o7",
+			  "g1", "g2", "g3", "cc");
+#ifdef DEBUG_MULDIV
+		printk ("0x%x\n", rs1);
+#endif
+		if (store_reg(rs1, rdv, regs))
+			return -1;
+		break;
+	}
+	if (is_foocc (insn)) {
+		regs->psr &= ~PSR_ICC;
+		if ((inst & 0xe) == 14) {
+			/* ?div */
+			if (rs2) regs->psr |= PSR_V;
+		}
+		if (!rs1) regs->psr |= PSR_Z;
+		if (((int)rs1) < 0) regs->psr |= PSR_N;
+#ifdef DEBUG_MULDIV
+		printk ("psr muldiv: %08x\n", regs->psr);
+#endif
+	}
+	advance(regs);
+	return 0;
+}
diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c
new file mode 100644
index 0000000..597d3ff
--- /dev/null
+++ b/arch/sparc/kernel/pcic.c
@@ -0,0 +1,1041 @@
+/*
+ * pcic.c: MicroSPARC-IIep PCI controller support
+ *
+ * Copyright (C) 1998 V. Roganov and G. Raiko
+ *
+ * Code is derived from Ultra/PCI PSYCHO controller support, see that
+ * for author info.
+ *
+ * Support for diverse IIep based platforms by Pete Zaitcev.
+ * CP-1200 by Eric Brower.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+
+#include <asm/ebus.h>
+#include <asm/sbus.h> /* for sanity check... */
+#include <asm/swift.h> /* for cache flushing. */
+#include <asm/io.h>
+
+#include <linux/ctype.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/interrupt.h>
+
+#include <asm/irq.h>
+#include <asm/oplib.h>
+#include <asm/pcic.h>
+#include <asm/timer.h>
+#include <asm/uaccess.h>
+
+
+unsigned int pcic_pin_to_irq(unsigned int pin, char *name);
+
+/*
+ * I studied different documents and many live PROMs both from 2.30
+ * family and 3.xx versions. I came to the amazing conclusion: there is
+ * absolutely no way to route interrupts in IIep systems relying on
+ * information which PROM presents. We must hardcode interrupt routing
+ * schematics. And this actually sucks.   -- zaitcev 1999/05/12
+ *
+ * To find irq for a device we determine which routing map
+ * is in effect or, in other words, on which machine we are running.
+ * We use PROM name for this although other techniques may be used
+ * in special cases (Gleb reports a PROMless IIep based system).
+ * Once we know the map we take device configuration address and
+ * find PCIC pin number where INT line goes. Then we may either program
+ * preferred irq into the PCIC or supply the preexisting irq to the device.
+ */
+struct pcic_ca2irq {
+	unsigned char busno;		/* PCI bus number */
+	unsigned char devfn;		/* Configuration address */
+	unsigned char pin;		/* PCIC external interrupt pin */
+	unsigned char irq;		/* Preferred IRQ (mappable in PCIC) */
+	unsigned int force;		/* Enforce preferred IRQ */
+};
+
+struct pcic_sn2list {
+	char *sysname;
+	struct pcic_ca2irq *intmap;
+	int mapdim;
+};
+
+/*
+ * JavaEngine-1 apparently has different versions.
+ *
+ * According to communications with Sun folks, for P2 build 501-4628-03:
+ * pin 0 - parallel, audio;
+ * pin 1 - Ethernet;
+ * pin 2 - su;
+ * pin 3 - PS/2 kbd and mouse.
+ *
+ * OEM manual (805-1486):
+ * pin 0: Ethernet
+ * pin 1: All EBus
+ * pin 2: IGA (unused)
+ * pin 3: Not connected
+ * OEM manual says that 501-4628 & 501-4811 are the same thing,
+ * only the latter has NAND flash in place.
+ *
+ * So far unofficial Sun wins over the OEM manual. Poor OEMs...
+ */
+static struct pcic_ca2irq pcic_i_je1a[] = {	/* 501-4811-03 */
+	{ 0, 0x00, 2, 12, 0 },		/* EBus: hogs all */
+	{ 0, 0x01, 1,  6, 1 },		/* Happy Meal */
+	{ 0, 0x80, 0,  7, 0 },		/* IGA (unused) */
+};
+
+/* XXX JS-E entry is incomplete - PCI Slot 2 address (pin 7)? */
+static struct pcic_ca2irq pcic_i_jse[] = {
+	{ 0, 0x00, 0, 13, 0 },		/* Ebus - serial and keyboard */
+	{ 0, 0x01, 1,  6, 0 },		/* hme */
+	{ 0, 0x08, 2,  9, 0 },		/* VGA - we hope not used :) */
+	{ 0, 0x10, 6,  8, 0 },		/* PCI INTA# in Slot 1 */
+	{ 0, 0x18, 7, 12, 0 },		/* PCI INTA# in Slot 2, shared w. RTC */
+	{ 0, 0x38, 4,  9, 0 },		/* All ISA devices. Read 8259. */
+	{ 0, 0x80, 5, 11, 0 },		/* EIDE */
+	/* {0,0x88, 0,0,0} - unknown device... PMU? Probably no interrupt. */
+	{ 0, 0xA0, 4,  9, 0 },		/* USB */
+	/*
+	 * Some pins belong to non-PCI devices, we hardcode them in drivers.
+	 * sun4m timers - irq 10, 14
+	 * PC style RTC - pin 7, irq 4 ?
+	 * Smart card, Parallel - pin 4 shared with USB, ISA
+	 * audio - pin 3, irq 5 ?
+	 */
+};
+
+/* SPARCengine-6 was the original release name of CP1200.
+ * The documentation differs between the two versions
+ */
+static struct pcic_ca2irq pcic_i_se6[] = {
+	{ 0, 0x08, 0,  2, 0 },		/* SCSI	*/
+	{ 0, 0x01, 1,  6, 0 },		/* HME	*/
+	{ 0, 0x00, 3, 13, 0 },		/* EBus	*/
+};
+
+/*
+ * Krups (courtesy of Varol Kaptan)
+ * No documentation available, but it was easy to guess
+ * because it was very similar to Espresso.
+ *  
+ * pin 0 - kbd, mouse, serial;
+ * pin 1 - Ethernet;
+ * pin 2 - igs (we do not use it);
+ * pin 3 - audio;
+ * pin 4,5,6 - unused;
+ * pin 7 - RTC (from P2 onwards as David B. says).
+ */
+static struct pcic_ca2irq pcic_i_jk[] = {
+	{ 0, 0x00, 0, 13, 0 },		/* Ebus - serial and keyboard */
+	{ 0, 0x01, 1,  6, 0 },		/* hme */
+};
+
+/*
+ * Several entries in this list may point to the same routing map
+ * as several PROMs may be installed on the same physical board.
+ */
+#define SN2L_INIT(name, map)	\
+  { name, map, sizeof(map)/sizeof(struct pcic_ca2irq) }
+
+static struct pcic_sn2list pcic_known_sysnames[] = {
+	SN2L_INIT("SUNW,JavaEngine1", pcic_i_je1a),	/* JE1, PROM 2.32 */
+	SN2L_INIT("SUNW,JS-E", pcic_i_jse),	/* PROLL JavaStation-E */
+	SN2L_INIT("SUNW,SPARCengine-6", pcic_i_se6), /* SPARCengine-6/CP-1200 */
+	SN2L_INIT("SUNW,JS-NC", pcic_i_jk),	/* PROLL JavaStation-NC */
+	SN2L_INIT("SUNW,JSIIep", pcic_i_jk),	/* OBP JavaStation-NC */
+	{ NULL, NULL, 0 }
+};
+
+/*
+ * Only one PCIC per IIep,
+ * and since we have no SMP IIep, only one per system.
+ */
+static int pcic0_up;
+static struct linux_pcic pcic0;
+
+void * __iomem pcic_regs;
+volatile int pcic_speculative;
+volatile int pcic_trapped;
+
+static void pci_do_gettimeofday(struct timeval *tv);
+static int pci_do_settimeofday(struct timespec *tv);
+
+#define CONFIG_CMD(bus, device_fn, where) (0x80000000 | (((unsigned int)bus) << 16) | (((unsigned int)device_fn) << 8) | (where & ~3))
+
+static int pcic_read_config_dword(unsigned int busno, unsigned int devfn,
+    int where, u32 *value)
+{
+	struct linux_pcic *pcic;
+	unsigned long flags;
+
+	pcic = &pcic0;
+
+	local_irq_save(flags);
+#if 0 /* does not fail here */
+	pcic_speculative = 1;
+	pcic_trapped = 0;
+#endif
+	writel(CONFIG_CMD(busno, devfn, where), pcic->pcic_config_space_addr);
+#if 0 /* does not fail here */
+	nop();
+	if (pcic_trapped) {
+		local_irq_restore(flags);
+		*value = ~0;
+		return 0;
+	}
+#endif
+	pcic_speculative = 2;
+	pcic_trapped = 0;
+	*value = readl(pcic->pcic_config_space_data + (where&4));
+	nop();
+	if (pcic_trapped) {
+		pcic_speculative = 0;
+		local_irq_restore(flags);
+		*value = ~0;
+		return 0;
+	}
+	pcic_speculative = 0;
+	local_irq_restore(flags);
+	return 0;
+}
+
+static int pcic_read_config(struct pci_bus *bus, unsigned int devfn,
+   int where, int size, u32 *val)
+{
+	unsigned int v;
+
+	if (bus->number != 0) return -EINVAL;
+	switch (size) {
+	case 1:
+		pcic_read_config_dword(bus->number, devfn, where&~3, &v);
+		*val = 0xff & (v >> (8*(where & 3)));
+		return 0;
+	case 2:
+		if (where&1) return -EINVAL;
+		pcic_read_config_dword(bus->number, devfn, where&~3, &v);
+		*val = 0xffff & (v >> (8*(where & 3)));
+		return 0;
+	case 4:
+		if (where&3) return -EINVAL;
+		pcic_read_config_dword(bus->number, devfn, where&~3, val);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int pcic_write_config_dword(unsigned int busno, unsigned int devfn,
+    int where, u32 value)
+{
+	struct linux_pcic *pcic;
+	unsigned long flags;
+
+	pcic = &pcic0;
+
+	local_irq_save(flags);
+	writel(CONFIG_CMD(busno, devfn, where), pcic->pcic_config_space_addr);
+	writel(value, pcic->pcic_config_space_data + (where&4));
+	local_irq_restore(flags);
+	return 0;
+}
+
+static int pcic_write_config(struct pci_bus *bus, unsigned int devfn,
+   int where, int size, u32 val)
+{
+	unsigned int v;
+
+	if (bus->number != 0) return -EINVAL;
+	switch (size) {
+	case 1:
+		pcic_read_config_dword(bus->number, devfn, where&~3, &v);
+		v = (v & ~(0xff << (8*(where&3)))) |
+		    ((0xff&val) << (8*(where&3)));
+		return pcic_write_config_dword(bus->number, devfn, where&~3, v);
+	case 2:
+		if (where&1) return -EINVAL;
+		pcic_read_config_dword(bus->number, devfn, where&~3, &v);
+		v = (v & ~(0xffff << (8*(where&3)))) |
+		    ((0xffff&val) << (8*(where&3)));
+		return pcic_write_config_dword(bus->number, devfn, where&~3, v);
+	case 4:
+		if (where&3) return -EINVAL;
+		return pcic_write_config_dword(bus->number, devfn, where, val);
+	}
+	return -EINVAL;
+}
+
+static struct pci_ops pcic_ops = {
+	.read =		pcic_read_config,
+	.write =	pcic_write_config,
+};
+
+/*
+ * On sparc64 pcibios_init() calls pci_controller_probe().
+ * We want PCIC probed little ahead so that interrupt controller
+ * would be operational.
+ */
+int __init pcic_probe(void)
+{
+	struct linux_pcic *pcic;
+	struct linux_prom_registers regs[PROMREG_MAX];
+	struct linux_pbm_info* pbm;
+	char namebuf[64];
+	int node;
+	int err;
+
+	if (pcic0_up) {
+		prom_printf("PCIC: called twice!\n");
+		prom_halt();
+	}
+	pcic = &pcic0;
+
+	node = prom_getchild (prom_root_node);
+	node = prom_searchsiblings (node, "pci");
+	if (node == 0)
+		return -ENODEV;
+	/*
+	 * Map in PCIC register set, config space, and IO base
+	 */
+	err = prom_getproperty(node, "reg", (char*)regs, sizeof(regs));
+	if (err == 0 || err == -1) {
+		prom_printf("PCIC: Error, cannot get PCIC registers "
+			    "from PROM.\n");
+		prom_halt();
+	}
+
+	pcic0_up = 1;
+
+	pcic->pcic_res_regs.name = "pcic_registers";
+	pcic->pcic_regs = ioremap(regs[0].phys_addr, regs[0].reg_size);
+	if (!pcic->pcic_regs) {
+		prom_printf("PCIC: Error, cannot map PCIC registers.\n");
+		prom_halt();
+	}
+
+	pcic->pcic_res_io.name = "pcic_io";
+	if ((pcic->pcic_io = (unsigned long)
+	    ioremap(regs[1].phys_addr, 0x10000)) == 0) {
+		prom_printf("PCIC: Error, cannot map PCIC IO Base.\n");
+		prom_halt();
+	}
+
+	pcic->pcic_res_cfg_addr.name = "pcic_cfg_addr";
+	if ((pcic->pcic_config_space_addr =
+	    ioremap(regs[2].phys_addr, regs[2].reg_size * 2)) == 0) {
+		prom_printf("PCIC: Error, cannot map" 
+			    "PCI Configuration Space Address.\n");
+		prom_halt();
+	}
+
+	/*
+	 * Docs say three least significant bits in address and data
+	 * must be the same. Thus, we need adjust size of data.
+	 */
+	pcic->pcic_res_cfg_data.name = "pcic_cfg_data";
+	if ((pcic->pcic_config_space_data =
+	    ioremap(regs[3].phys_addr, regs[3].reg_size * 2)) == 0) {
+		prom_printf("PCIC: Error, cannot map" 
+			    "PCI Configuration Space Data.\n");
+		prom_halt();
+	}
+
+	pbm = &pcic->pbm;
+	pbm->prom_node = node;
+	prom_getstring(node, "name", namebuf, 63);  namebuf[63] = 0;
+	strcpy(pbm->prom_name, namebuf);
+
+	{
+		extern volatile int t_nmi[1];
+		extern int pcic_nmi_trap_patch[1];
+
+		t_nmi[0] = pcic_nmi_trap_patch[0];
+		t_nmi[1] = pcic_nmi_trap_patch[1];
+		t_nmi[2] = pcic_nmi_trap_patch[2];
+		t_nmi[3] = pcic_nmi_trap_patch[3];
+		swift_flush_dcache();
+		pcic_regs = pcic->pcic_regs;
+	}
+
+	prom_getstring(prom_root_node, "name", namebuf, 63);  namebuf[63] = 0;
+	{
+		struct pcic_sn2list *p;
+
+		for (p = pcic_known_sysnames; p->sysname != NULL; p++) {
+			if (strcmp(namebuf, p->sysname) == 0)
+				break;
+		}
+		pcic->pcic_imap = p->intmap;
+		pcic->pcic_imdim = p->mapdim;
+	}
+	if (pcic->pcic_imap == NULL) {
+		/*
+		 * We do not panic here for the sake of embedded systems.
+		 */
+		printk("PCIC: System %s is unknown, cannot route interrupts\n",
+		    namebuf);
+	}
+
+	return 0;
+}
+
+static void __init pcic_pbm_scan_bus(struct linux_pcic *pcic)
+{
+	struct linux_pbm_info *pbm = &pcic->pbm;
+
+	pbm->pci_bus = pci_scan_bus(pbm->pci_first_busno, &pcic_ops, pbm);
+#if 0 /* deadwood transplanted from sparc64 */
+	pci_fill_in_pbm_cookies(pbm->pci_bus, pbm, pbm->prom_node);
+	pci_record_assignments(pbm, pbm->pci_bus);
+	pci_assign_unassigned(pbm, pbm->pci_bus);
+	pci_fixup_irq(pbm, pbm->pci_bus);
+#endif
+}
+
+/*
+ * Main entry point from the PCI subsystem.
+ */
+static int __init pcic_init(void)
+{
+	struct linux_pcic *pcic;
+
+	/*
+	 * PCIC should be initialized at start of the timer.
+	 * So, here we report the presence of PCIC and do some magic passes.
+	 */
+	if(!pcic0_up)
+		return 0;
+	pcic = &pcic0;
+
+	/*
+	 *      Switch off IOTLB translation.
+	 */
+	writeb(PCI_DVMA_CONTROL_IOTLB_DISABLE, 
+	       pcic->pcic_regs+PCI_DVMA_CONTROL);
+
+	/*
+	 *      Increase mapped size for PCI memory space (DMA access).
+	 *      Should be done in that order (size first, address second).
+	 *      Why we couldn't set up 4GB and forget about it? XXX
+	 */
+	writel(0xF0000000UL, pcic->pcic_regs+PCI_SIZE_0);
+	writel(0+PCI_BASE_ADDRESS_SPACE_MEMORY, 
+	       pcic->pcic_regs+PCI_BASE_ADDRESS_0);
+
+	pcic_pbm_scan_bus(pcic);
+
+	ebus_init();
+	return 0;
+}
+
+int pcic_present(void)
+{
+	return pcic0_up;
+}
+
+static int __init pdev_to_pnode(struct linux_pbm_info *pbm, 
+				    struct pci_dev *pdev)
+{
+	struct linux_prom_pci_registers regs[PROMREG_MAX];
+	int err;
+	int node = prom_getchild(pbm->prom_node);
+
+	while(node) {
+		err = prom_getproperty(node, "reg", 
+				       (char *)&regs[0], sizeof(regs));
+		if(err != 0 && err != -1) {
+			unsigned long devfn = (regs[0].which_io >> 8) & 0xff;
+			if(devfn == pdev->devfn)
+				return node;
+		}
+		node = prom_getsibling(node);
+	}
+	return 0;
+}
+
+static inline struct pcidev_cookie *pci_devcookie_alloc(void)
+{
+	return kmalloc(sizeof(struct pcidev_cookie), GFP_ATOMIC);
+}
+
+static void pcic_map_pci_device(struct linux_pcic *pcic,
+    struct pci_dev *dev, int node)
+{
+	char namebuf[64];
+	unsigned long address;
+	unsigned long flags;
+	int j;
+
+	if (node == 0 || node == -1) {
+		strcpy(namebuf, "???");
+	} else {
+		prom_getstring(node, "name", namebuf, 63); namebuf[63] = 0;
+	}
+
+	for (j = 0; j < 6; j++) {
+		address = dev->resource[j].start;
+		if (address == 0) break;	/* are sequential */
+		flags = dev->resource[j].flags;
+		if ((flags & IORESOURCE_IO) != 0) {
+			if (address < 0x10000) {
+				/*
+				 * A device responds to I/O cycles on PCI.
+				 * We generate these cycles with memory
+				 * access into the fixed map (phys 0x30000000).
+				 *
+				 * Since a device driver does not want to
+				 * do ioremap() before accessing PC-style I/O,
+				 * we supply virtual, ready to access address.
+				 *
+				 * Ebus devices do not come here even if
+				 * CheerIO makes a similar conversion.
+				 * See ebus.c for details.
+				 *
+				 * Note that check_region()/request_region()
+				 * work for these devices.
+				 *
+				 * XXX Neat trick, but it's a *bad* idea
+				 * to shit into regions like that.
+				 * What if we want to allocate one more
+				 * PCI base address...
+				 */
+				dev->resource[j].start =
+				    pcic->pcic_io + address;
+				dev->resource[j].end = 1;  /* XXX */
+				dev->resource[j].flags =
+				    (flags & ~IORESOURCE_IO) | IORESOURCE_MEM;
+			} else {
+				/*
+				 * OOPS... PCI Spec allows this. Sun does
+				 * not have any devices getting above 64K
+				 * so it must be user with a weird I/O
+				 * board in a PCI slot. We must remap it
+				 * under 64K but it is not done yet. XXX
+				 */
+				printk("PCIC: Skipping I/O space at 0x%lx,"
+				    "this will Oops if a driver attaches;"
+				    "device '%s' at %02x:%02x)\n", address,
+				    namebuf, dev->bus->number, dev->devfn);
+			}
+		}
+	}
+}
+
+static void
+pcic_fill_irq(struct linux_pcic *pcic, struct pci_dev *dev, int node)
+{
+	struct pcic_ca2irq *p;
+	int i, ivec;
+	char namebuf[64];
+
+	if (node == 0 || node == -1) {
+		strcpy(namebuf, "???");
+	} else {
+		prom_getstring(node, "name", namebuf, sizeof(namebuf));
+	}
+
+	if ((p = pcic->pcic_imap) == 0) {
+		dev->irq = 0;
+		return;
+	}
+	for (i = 0; i < pcic->pcic_imdim; i++) {
+		if (p->busno == dev->bus->number && p->devfn == dev->devfn)
+			break;
+		p++;
+	}
+	if (i >= pcic->pcic_imdim) {
+		printk("PCIC: device %s devfn %02x:%02x not found in %d\n",
+		    namebuf, dev->bus->number, dev->devfn, pcic->pcic_imdim);
+		dev->irq = 0;
+		return;
+	}
+
+	i = p->pin;
+	if (i >= 0 && i < 4) {
+		ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_LO);
+		dev->irq = ivec >> (i << 2) & 0xF;
+	} else if (i >= 4 && i < 8) {
+		ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_HI);
+		dev->irq = ivec >> ((i-4) << 2) & 0xF;
+	} else {					/* Corrupted map */
+		printk("PCIC: BAD PIN %d\n", i); for (;;) {}
+	}
+/* P3 */ /* printk("PCIC: device %s pin %d ivec 0x%x irq %x\n", namebuf, i, ivec, dev->irq); */
+
+	/*
+	 * dev->irq=0 means PROM did not bother to program the upper
+	 * half of PCIC. This happens on JS-E with PROM 3.11, for instance.
+	 */
+	if (dev->irq == 0 || p->force) {
+		if (p->irq == 0 || p->irq >= 15) {	/* Corrupted map */
+			printk("PCIC: BAD IRQ %d\n", p->irq); for (;;) {}
+		}
+		printk("PCIC: setting irq %d at pin %d for device %02x:%02x\n",
+		    p->irq, p->pin, dev->bus->number, dev->devfn);
+		dev->irq = p->irq;
+
+		i = p->pin;
+		if (i >= 4) {
+			ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_HI);
+			ivec &= ~(0xF << ((i - 4) << 2));
+			ivec |= p->irq << ((i - 4) << 2);
+			writew(ivec, pcic->pcic_regs+PCI_INT_SELECT_HI);
+		} else {
+			ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_LO);
+			ivec &= ~(0xF << (i << 2));
+			ivec |= p->irq << (i << 2);
+			writew(ivec, pcic->pcic_regs+PCI_INT_SELECT_LO);
+		}
+ 	}
+
+	return;
+}
+
+/*
+ * Normally called from {do_}pci_scan_bus...
+ */
+void __init pcibios_fixup_bus(struct pci_bus *bus)
+{
+	struct pci_dev *dev;
+	int i, has_io, has_mem;
+	unsigned int cmd;
+	struct linux_pcic *pcic;
+	/* struct linux_pbm_info* pbm = &pcic->pbm; */
+	int node;
+	struct pcidev_cookie *pcp;
+
+	if (!pcic0_up) {
+		printk("pcibios_fixup_bus: no PCIC\n");
+		return;
+	}
+	pcic = &pcic0;
+
+	/*
+	 * Next crud is an equivalent of pbm = pcic_bus_to_pbm(bus);
+	 */
+	if (bus->number != 0) {
+		printk("pcibios_fixup_bus: nonzero bus 0x%x\n", bus->number);
+		return;
+	}
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+
+		/*
+		 * Comment from i386 branch:
+		 *     There are buggy BIOSes that forget to enable I/O and memory
+		 *     access to PCI devices. We try to fix this, but we need to
+		 *     be sure that the BIOS didn't forget to assign an address
+		 *     to the device. [mj]
+		 * OBP is a case of such BIOS :-)
+		 */
+		has_io = has_mem = 0;
+		for(i=0; i<6; i++) {
+			unsigned long f = dev->resource[i].flags;
+			if (f & IORESOURCE_IO) {
+				has_io = 1;
+			} else if (f & IORESOURCE_MEM)
+				has_mem = 1;
+		}
+		pcic_read_config(dev->bus, dev->devfn, PCI_COMMAND, 2, &cmd);
+		if (has_io && !(cmd & PCI_COMMAND_IO)) {
+			printk("PCIC: Enabling I/O for device %02x:%02x\n",
+				dev->bus->number, dev->devfn);
+			cmd |= PCI_COMMAND_IO;
+			pcic_write_config(dev->bus, dev->devfn,
+			    PCI_COMMAND, 2, cmd);
+		}
+		if (has_mem && !(cmd & PCI_COMMAND_MEMORY)) {
+			printk("PCIC: Enabling memory for device %02x:%02x\n",
+				dev->bus->number, dev->devfn);
+			cmd |= PCI_COMMAND_MEMORY;
+			pcic_write_config(dev->bus, dev->devfn,
+			    PCI_COMMAND, 2, cmd);
+		}
+
+		node = pdev_to_pnode(&pcic->pbm, dev);
+		if(node == 0)
+			node = -1;
+
+		/* cookies */
+		pcp = pci_devcookie_alloc();
+		pcp->pbm = &pcic->pbm;
+		pcp->prom_node = node;
+		dev->sysdata = pcp;
+
+		/* fixing I/O to look like memory */
+		if ((dev->class>>16) != PCI_BASE_CLASS_BRIDGE)
+			pcic_map_pci_device(pcic, dev, node);
+
+		pcic_fill_irq(pcic, dev, node);
+	}
+}
+
+/*
+ * pcic_pin_to_irq() is exported to ebus.c.
+ */
+unsigned int
+pcic_pin_to_irq(unsigned int pin, char *name)
+{
+	struct linux_pcic *pcic = &pcic0;
+	unsigned int irq;
+	unsigned int ivec;
+
+	if (pin < 4) {
+		ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_LO);
+		irq = ivec >> (pin << 2) & 0xF;
+	} else if (pin < 8) {
+		ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_HI);
+		irq = ivec >> ((pin-4) << 2) & 0xF;
+	} else {					/* Corrupted map */
+		printk("PCIC: BAD PIN %d FOR %s\n", pin, name);
+		for (;;) {}	/* XXX Cannot panic properly in case of PROLL */
+	}
+/* P3 */ /* printk("PCIC: dev %s pin %d ivec 0x%x irq %x\n", name, pin, ivec, irq); */
+	return irq;
+}
+
+/* Makes compiler happy */
+static volatile int pcic_timer_dummy;
+
+static void pcic_clear_clock_irq(void)
+{
+	pcic_timer_dummy = readl(pcic0.pcic_regs+PCI_SYS_LIMIT);
+}
+
+static irqreturn_t pcic_timer_handler (int irq, void *h, struct pt_regs *regs)
+{
+	write_seqlock(&xtime_lock);	/* Dummy, to show that we remember */
+	pcic_clear_clock_irq();
+	do_timer(regs);
+#ifndef CONFIG_SMP
+	update_process_times(user_mode(regs));
+#endif
+	write_sequnlock(&xtime_lock);
+	return IRQ_HANDLED;
+}
+
+#define USECS_PER_JIFFY  10000  /* We have 100HZ "standard" timer for sparc */
+#define TICK_TIMER_LIMIT ((100*1000000/4)/100)
+
+void __init pci_time_init(void)
+{
+	struct linux_pcic *pcic = &pcic0;
+	unsigned long v;
+	int timer_irq, irq;
+
+	/* A hack until do_gettimeofday prototype is moved to arch specific headers
+	   and btfixupped. Patch do_gettimeofday with ba pci_do_gettimeofday; nop */
+	((unsigned int *)do_gettimeofday)[0] = 
+	    0x10800000 | ((((unsigned long)pci_do_gettimeofday -
+	     (unsigned long)do_gettimeofday) >> 2) & 0x003fffff);
+	((unsigned int *)do_gettimeofday)[1] = 0x01000000;
+	BTFIXUPSET_CALL(bus_do_settimeofday, pci_do_settimeofday, BTFIXUPCALL_NORM);
+	btfixup();
+
+	writel (TICK_TIMER_LIMIT, pcic->pcic_regs+PCI_SYS_LIMIT);
+	/* PROM should set appropriate irq */
+	v = readb(pcic->pcic_regs+PCI_COUNTER_IRQ);
+	timer_irq = PCI_COUNTER_IRQ_SYS(v);
+	writel (PCI_COUNTER_IRQ_SET(timer_irq, 0),
+		pcic->pcic_regs+PCI_COUNTER_IRQ);
+	irq = request_irq(timer_irq, pcic_timer_handler,
+			  (SA_INTERRUPT | SA_STATIC_ALLOC), "timer", NULL);
+	if (irq) {
+		prom_printf("time_init: unable to attach IRQ%d\n", timer_irq);
+		prom_halt();
+	}
+	local_irq_enable();
+}
+
+static __inline__ unsigned long do_gettimeoffset(void)
+{
+	/*
+	 * We devide all to 100
+	 * to have microsecond resolution and to avoid overflow
+	 */
+	unsigned long count =
+	    readl(pcic0.pcic_regs+PCI_SYS_COUNTER) & ~PCI_SYS_COUNTER_OVERFLOW;
+	count = ((count/100)*USECS_PER_JIFFY) / (TICK_TIMER_LIMIT/100);
+	return count;
+}
+
+extern unsigned long wall_jiffies;
+
+static void pci_do_gettimeofday(struct timeval *tv)
+{
+	unsigned long flags;
+	unsigned long seq;
+	unsigned long usec, sec;
+	unsigned long max_ntp_tick = tick_usec - tickadj;
+
+	do {
+		unsigned long lost;
+
+		seq = read_seqbegin_irqsave(&xtime_lock, flags);
+		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_irqrestore(&xtime_lock, seq, flags));
+
+	while (usec >= 1000000) {
+		usec -= 1000000;
+		sec++;
+	}
+
+	tv->tv_sec = sec;
+	tv->tv_usec = usec;
+}
+
+static int pci_do_settimeofday(struct timespec *tv)
+{
+	if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
+		return -EINVAL;
+
+	/*
+	 * 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!
+	 */
+	tv->tv_nsec -= 1000 * (do_gettimeoffset() + 
+				(jiffies - wall_jiffies) * (USEC_PER_SEC / HZ));
+	while (tv->tv_nsec < 0) {
+		tv->tv_nsec += NSEC_PER_SEC;
+		tv->tv_sec--;
+	}
+
+	wall_to_monotonic.tv_sec += xtime.tv_sec - tv->tv_sec;
+	wall_to_monotonic.tv_nsec += xtime.tv_nsec - tv->tv_nsec;
+
+	if (wall_to_monotonic.tv_nsec > NSEC_PER_SEC) {
+		wall_to_monotonic.tv_nsec -= NSEC_PER_SEC;
+		wall_to_monotonic.tv_sec++;
+	}
+	if (wall_to_monotonic.tv_nsec < 0) {
+		wall_to_monotonic.tv_nsec += NSEC_PER_SEC;
+		wall_to_monotonic.tv_sec--;
+	}
+
+	xtime.tv_sec = tv->tv_sec;
+	xtime.tv_nsec = tv->tv_nsec;
+	time_adjust = 0;		/* stop active adjtime() */
+	time_status |= STA_UNSYNC;
+	time_maxerror = NTP_PHASE_LIMIT;
+	time_esterror = NTP_PHASE_LIMIT;
+	return 0;
+}
+
+#if 0
+static void watchdog_reset() {
+	writeb(0, pcic->pcic_regs+PCI_SYS_STATUS);
+}
+#endif
+
+/*
+ * Other archs parse arguments here.
+ */
+char * __init pcibios_setup(char *str)
+{
+	return str;
+}
+
+void pcibios_align_resource(void *data, struct resource *res,
+			    unsigned long size, unsigned long align)
+{
+}
+
+int pcibios_enable_device(struct pci_dev *pdev, int mask)
+{
+	return 0;
+}
+
+/*
+ * NMI
+ */
+void pcic_nmi(unsigned int pend, struct pt_regs *regs)
+{
+
+	pend = flip_dword(pend);
+
+	if (!pcic_speculative || (pend & PCI_SYS_INT_PENDING_PIO) == 0) {
+		/*
+		 * XXX On CP-1200 PCI #SERR may happen, we do not know
+		 * what to do about it yet.
+		 */
+		printk("Aiee, NMI pend 0x%x pc 0x%x spec %d, hanging\n",
+		    pend, (int)regs->pc, pcic_speculative);
+		for (;;) { }
+	}
+	pcic_speculative = 0;
+	pcic_trapped = 1;
+	regs->pc = regs->npc;
+	regs->npc += 4;
+}
+
+static inline unsigned long get_irqmask(int irq_nr)
+{
+	return 1 << irq_nr;
+}
+
+static inline char *pcic_irq_itoa(unsigned int irq)
+{
+	static char buff[16];
+	sprintf(buff, "%d", irq);
+	return buff;
+}
+
+static void pcic_disable_irq(unsigned int irq_nr)
+{
+	unsigned long mask, flags;
+
+	mask = get_irqmask(irq_nr);
+	local_irq_save(flags);
+	writel(mask, pcic0.pcic_regs+PCI_SYS_INT_TARGET_MASK_SET);
+	local_irq_restore(flags);
+}
+
+static void pcic_enable_irq(unsigned int irq_nr)
+{
+	unsigned long mask, flags;
+
+	mask = get_irqmask(irq_nr);
+	local_irq_save(flags);
+	writel(mask, pcic0.pcic_regs+PCI_SYS_INT_TARGET_MASK_CLEAR);
+	local_irq_restore(flags);
+}
+
+static void pcic_clear_profile_irq(int cpu)
+{
+	printk("PCIC: unimplemented code: FILE=%s LINE=%d", __FILE__, __LINE__);
+}
+
+static void pcic_load_profile_irq(int cpu, unsigned int limit)
+{
+	printk("PCIC: unimplemented code: FILE=%s LINE=%d", __FILE__, __LINE__);
+}
+
+/* We assume the caller has disabled local interrupts when these are called,
+ * or else very bizarre behavior will result.
+ */
+static void pcic_disable_pil_irq(unsigned int pil)
+{
+	writel(get_irqmask(pil), pcic0.pcic_regs+PCI_SYS_INT_TARGET_MASK_SET);
+}
+
+static void pcic_enable_pil_irq(unsigned int pil)
+{
+	writel(get_irqmask(pil), pcic0.pcic_regs+PCI_SYS_INT_TARGET_MASK_CLEAR);
+}
+
+void __init sun4m_pci_init_IRQ(void)
+{
+	BTFIXUPSET_CALL(enable_irq, pcic_enable_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(disable_irq, pcic_disable_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(enable_pil_irq, pcic_enable_pil_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(disable_pil_irq, pcic_disable_pil_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(clear_clock_irq, pcic_clear_clock_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(clear_profile_irq, pcic_clear_profile_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(load_profile_irq, pcic_load_profile_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(__irq_itoa, pcic_irq_itoa, BTFIXUPCALL_NORM);
+}
+
+int pcibios_assign_resource(struct pci_dev *pdev, int resource)
+{
+	return -ENXIO;
+}
+
+/*
+ * This probably belongs here rather than ioport.c because
+ * we do not want this crud linked into SBus kernels.
+ * Also, think for a moment about likes of floppy.c that
+ * include architecture specific parts. They may want to redefine ins/outs.
+ *
+ * We do not use horroble macroses here because we want to
+ * advance pointer by sizeof(size).
+ */
+void outsb(unsigned long addr, const void *src, unsigned long count)
+{
+	while (count) {
+		count -= 1;
+		outb(*(const char *)src, addr);
+		src += 1;
+		/* addr += 1; */
+	}
+}
+
+void outsw(unsigned long addr, const void *src, unsigned long count)
+{
+	while (count) {
+		count -= 2;
+		outw(*(const short *)src, addr);
+		src += 2;
+		/* addr += 2; */
+	}
+}
+
+void outsl(unsigned long addr, const void *src, unsigned long count)
+{
+	while (count) {
+		count -= 4;
+		outl(*(const long *)src, addr);
+		src += 4;
+		/* addr += 4; */
+	}
+}
+
+void insb(unsigned long addr, void *dst, unsigned long count)
+{
+	while (count) {
+		count -= 1;
+		*(unsigned char *)dst = inb(addr);
+		dst += 1;
+		/* addr += 1; */
+	}
+}
+
+void insw(unsigned long addr, void *dst, unsigned long count)
+{
+	while (count) {
+		count -= 2;
+		*(unsigned short *)dst = inw(addr);
+		dst += 2;
+		/* addr += 2; */
+	}
+}
+
+void insl(unsigned long addr, void *dst, unsigned long count)
+{
+	while (count) {
+		count -= 4;
+		/*
+		 * XXX I am sure we are in for an unaligned trap here.
+		 */
+		*(unsigned long *)dst = inl(addr);
+		dst += 4;
+		/* addr += 4; */
+	}
+}
+
+subsys_initcall(pcic_init);
diff --git a/arch/sparc/kernel/pmc.c b/arch/sparc/kernel/pmc.c
new file mode 100644
index 0000000..7eca887
--- /dev/null
+++ b/arch/sparc/kernel/pmc.c
@@ -0,0 +1,99 @@
+/* pmc - Driver implementation for power management functions
+ * of Power Management Controller (PMC) on SPARCstation-Voyager.
+ *
+ * Copyright (c) 2002 Eric Brower (ebrower@usa.net)
+ */
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/pm.h>
+
+#include <asm/io.h>
+#include <asm/sbus.h>
+#include <asm/oplib.h>
+#include <asm/uaccess.h>
+#include <asm/auxio.h>
+
+/* Debug
+ *
+ * #define PMC_DEBUG_LED
+ * #define PMC_NO_IDLE
+ */
+
+#define PMC_MINOR	MISC_DYNAMIC_MINOR
+#define PMC_OBPNAME	"SUNW,pmc"
+#define PMC_DEVNAME "pmc"
+
+#define PMC_IDLE_REG	0x00
+#define PMC_IDLE_ON		0x01
+
+volatile static u8 __iomem *regs; 
+static int pmc_regsize;
+
+#define pmc_readb(offs)			(sbus_readb(regs+offs))
+#define pmc_writeb(val, offs) 	(sbus_writeb(val, regs+offs))
+
+/* 
+ * CPU idle callback function
+ * See .../arch/sparc/kernel/process.c
+ */
+void pmc_swift_idle(void)
+{
+#ifdef PMC_DEBUG_LED
+	set_auxio(0x00, AUXIO_LED); 
+#endif
+
+	pmc_writeb(pmc_readb(PMC_IDLE_REG) | PMC_IDLE_ON, PMC_IDLE_REG);
+
+#ifdef PMC_DEBUG_LED
+	set_auxio(AUXIO_LED, 0x00); 
+#endif
+} 
+
+static inline void pmc_free(void)
+{
+	sbus_iounmap(regs, pmc_regsize);
+}
+
+static int __init pmc_probe(void)
+{
+	struct sbus_bus *sbus = NULL;
+	struct sbus_dev *sdev = NULL;
+	for_each_sbus(sbus) {
+		for_each_sbusdev(sdev, sbus) {
+			if (!strcmp(sdev->prom_name, PMC_OBPNAME)) {
+				goto sbus_done;
+			}
+		}
+	}
+
+sbus_done:
+	if (!sdev) {
+		return -ENODEV;
+	}
+
+	pmc_regsize = sdev->reg_addrs[0].reg_size;
+	regs = sbus_ioremap(&sdev->resource[0], 0, 
+				   pmc_regsize, PMC_OBPNAME);
+	if (!regs) {
+		printk(KERN_ERR "%s: unable to map registers\n", PMC_DEVNAME);
+		return -ENODEV;
+	}
+
+#ifndef PMC_NO_IDLE
+	/* Assign power management IDLE handler */
+	pm_idle = pmc_swift_idle;	
+#endif
+
+	printk(KERN_INFO "%s: power management initialized\n", PMC_DEVNAME);
+	return 0;
+}
+
+/* This driver is not critical to the boot process
+ * and is easiest to ioremap when SBus is already
+ * initialized, so we install ourselves thusly:
+ */
+__initcall(pmc_probe);
diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c
new file mode 100644
index 0000000..143fe2f
--- /dev/null
+++ b/arch/sparc/kernel/process.c
@@ -0,0 +1,746 @@
+/*  $Id: process.c,v 1.161 2002/01/23 11:27:32 davem Exp $
+ *  linux/arch/sparc/kernel/process.c
+ *
+ *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *  Copyright (C) 1996 Eddie C. Dost   (ecd@skynet.be)
+ */
+
+/*
+ * This file handles the architecture-dependent parts of process handling..
+ */
+
+#include <stdarg.h>
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/kallsyms.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/config.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/reboot.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/init.h>
+
+#include <asm/auxio.h>
+#include <asm/oplib.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/delay.h>
+#include <asm/processor.h>
+#include <asm/psr.h>
+#include <asm/elf.h>
+#include <asm/unistd.h>
+
+/* 
+ * Power management idle function 
+ * Set in pm platform drivers (apc.c and pmc.c)
+ */
+void (*pm_idle)(void);
+
+/* 
+ * Power-off handler instantiation for pm.h compliance
+ * This is done via auxio, but could be used as a fallback
+ * handler when auxio is not present-- unused for now...
+ */
+void (*pm_power_off)(void);
+
+/*
+ * sysctl - toggle power-off restriction for serial console 
+ * systems in machine_power_off()
+ */
+int scons_pwroff = 1;
+
+extern void fpsave(unsigned long *, unsigned long *, void *, unsigned long *);
+
+struct task_struct *last_task_used_math = NULL;
+struct thread_info *current_set[NR_CPUS];
+
+/*
+ * default_idle is new in 2.5. XXX Review, currently stolen from sparc64.
+ */
+void default_idle(void)
+{
+}
+
+#ifndef CONFIG_SMP
+
+#define SUN4C_FAULT_HIGH 100
+
+/*
+ * the idle loop on a Sparc... ;)
+ */
+void cpu_idle(void)
+{
+	if (current->pid != 0)
+		goto out;
+
+	/* endless idle loop with no priority at all */
+	for (;;) {
+		if (ARCH_SUN4C_SUN4) {
+			static int count = HZ;
+			static unsigned long last_jiffies;
+			static unsigned long last_faults;
+			static unsigned long fps;
+			unsigned long now;
+			unsigned long faults;
+			unsigned long flags;
+
+			extern unsigned long sun4c_kernel_faults;
+			extern void sun4c_grow_kernel_ring(void);
+
+			local_irq_save(flags);
+			now = jiffies;
+			count -= (now - last_jiffies);
+			last_jiffies = now;
+			if (count < 0) {
+				count += HZ;
+				faults = sun4c_kernel_faults;
+				fps = (fps + (faults - last_faults)) >> 1;
+				last_faults = faults;
+#if 0
+				printk("kernel faults / second = %ld\n", fps);
+#endif
+				if (fps >= SUN4C_FAULT_HIGH) {
+					sun4c_grow_kernel_ring();
+				}
+			}
+			local_irq_restore(flags);
+		}
+
+		while((!need_resched()) && pm_idle) {
+			(*pm_idle)();
+		}
+
+		schedule();
+		check_pgt_cache();
+	}
+out:
+	return;
+}
+
+#else
+
+/* This is being executed in task 0 'user space'. */
+void cpu_idle(void)
+{
+	/* endless idle loop with no priority at all */
+	while(1) {
+		if(need_resched()) {
+			schedule();
+			check_pgt_cache();
+		}
+		barrier(); /* or else gcc optimizes... */
+	}
+}
+
+#endif
+
+extern char reboot_command [];
+
+extern void (*prom_palette)(int);
+
+/* XXX cli/sti -> local_irq_xxx here, check this works once SMP is fixed. */
+void machine_halt(void)
+{
+	local_irq_enable();
+	mdelay(8);
+	local_irq_disable();
+	if (!serial_console && prom_palette)
+		prom_palette (1);
+	prom_halt();
+	panic("Halt failed!");
+}
+
+EXPORT_SYMBOL(machine_halt);
+
+void machine_restart(char * cmd)
+{
+	char *p;
+	
+	local_irq_enable();
+	mdelay(8);
+	local_irq_disable();
+
+	p = strchr (reboot_command, '\n');
+	if (p) *p = 0;
+	if (!serial_console && prom_palette)
+		prom_palette (1);
+	if (cmd)
+		prom_reboot(cmd);
+	if (*reboot_command)
+		prom_reboot(reboot_command);
+	prom_feval ("reset");
+	panic("Reboot failed!");
+}
+
+EXPORT_SYMBOL(machine_restart);
+
+void machine_power_off(void)
+{
+#ifdef CONFIG_SUN_AUXIO
+	if (auxio_power_register && (!serial_console || scons_pwroff))
+		*auxio_power_register |= AUXIO_POWER_OFF;
+#endif
+	machine_halt();
+}
+
+EXPORT_SYMBOL(machine_power_off);
+
+static DEFINE_SPINLOCK(sparc_backtrace_lock);
+
+void __show_backtrace(unsigned long fp)
+{
+	struct reg_window *rw;
+	unsigned long flags;
+	int cpu = smp_processor_id();
+
+	spin_lock_irqsave(&sparc_backtrace_lock, flags);
+
+	rw = (struct reg_window *)fp;
+        while(rw && (((unsigned long) rw) >= PAGE_OFFSET) &&
+            !(((unsigned long) rw) & 0x7)) {
+		printk("CPU[%d]: ARGS[%08lx,%08lx,%08lx,%08lx,%08lx,%08lx] "
+		       "FP[%08lx] CALLER[%08lx]: ", cpu,
+		       rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3],
+		       rw->ins[4], rw->ins[5],
+		       rw->ins[6],
+		       rw->ins[7]);
+		print_symbol("%s\n", rw->ins[7]);
+		rw = (struct reg_window *) rw->ins[6];
+	}
+	spin_unlock_irqrestore(&sparc_backtrace_lock, flags);
+}
+
+#define __SAVE __asm__ __volatile__("save %sp, -0x40, %sp\n\t")
+#define __RESTORE __asm__ __volatile__("restore %g0, %g0, %g0\n\t")
+#define __GET_FP(fp) __asm__ __volatile__("mov %%i6, %0" : "=r" (fp))
+
+void show_backtrace(void)
+{
+	unsigned long fp;
+
+	__SAVE; __SAVE; __SAVE; __SAVE;
+	__SAVE; __SAVE; __SAVE; __SAVE;
+	__RESTORE; __RESTORE; __RESTORE; __RESTORE;
+	__RESTORE; __RESTORE; __RESTORE; __RESTORE;
+
+	__GET_FP(fp);
+
+	__show_backtrace(fp);
+}
+
+#ifdef CONFIG_SMP
+void smp_show_backtrace_all_cpus(void)
+{
+	xc0((smpfunc_t) show_backtrace);
+	show_backtrace();
+}
+#endif
+
+#if 0
+void show_stackframe(struct sparc_stackf *sf)
+{
+	unsigned long size;
+	unsigned long *stk;
+	int i;
+
+	printk("l0: %08lx l1: %08lx l2: %08lx l3: %08lx "
+	       "l4: %08lx l5: %08lx l6: %08lx l7: %08lx\n",
+	       sf->locals[0], sf->locals[1], sf->locals[2], sf->locals[3],
+	       sf->locals[4], sf->locals[5], sf->locals[6], sf->locals[7]);
+	printk("i0: %08lx i1: %08lx i2: %08lx i3: %08lx "
+	       "i4: %08lx i5: %08lx fp: %08lx i7: %08lx\n",
+	       sf->ins[0], sf->ins[1], sf->ins[2], sf->ins[3],
+	       sf->ins[4], sf->ins[5], (unsigned long)sf->fp, sf->callers_pc);
+	printk("sp: %08lx x0: %08lx x1: %08lx x2: %08lx "
+	       "x3: %08lx x4: %08lx x5: %08lx xx: %08lx\n",
+	       (unsigned long)sf->structptr, sf->xargs[0], sf->xargs[1],
+	       sf->xargs[2], sf->xargs[3], sf->xargs[4], sf->xargs[5],
+	       sf->xxargs[0]);
+	size = ((unsigned long)sf->fp) - ((unsigned long)sf);
+	size -= STACKFRAME_SZ;
+	stk = (unsigned long *)((unsigned long)sf + STACKFRAME_SZ);
+	i = 0;
+	do {
+		printk("s%d: %08lx\n", i++, *stk++);
+	} while ((size -= sizeof(unsigned long)));
+}
+#endif
+
+void show_regs(struct pt_regs *r)
+{
+	struct reg_window *rw = (struct reg_window *) r->u_regs[14];
+
+        printk("PSR: %08lx PC: %08lx NPC: %08lx Y: %08lx    %s\n",
+	       r->psr, r->pc, r->npc, r->y, print_tainted());
+	print_symbol("PC: <%s>\n", r->pc);
+	printk("%%G: %08lx %08lx  %08lx %08lx  %08lx %08lx  %08lx %08lx\n",
+	       r->u_regs[0], r->u_regs[1], r->u_regs[2], r->u_regs[3],
+	       r->u_regs[4], r->u_regs[5], r->u_regs[6], r->u_regs[7]);
+	printk("%%O: %08lx %08lx  %08lx %08lx  %08lx %08lx  %08lx %08lx\n",
+	       r->u_regs[8], r->u_regs[9], r->u_regs[10], r->u_regs[11],
+	       r->u_regs[12], r->u_regs[13], r->u_regs[14], r->u_regs[15]);
+	print_symbol("RPC: <%s>\n", r->u_regs[15]);
+
+	printk("%%L: %08lx %08lx  %08lx %08lx  %08lx %08lx  %08lx %08lx\n",
+	       rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3],
+	       rw->locals[4], rw->locals[5], rw->locals[6], rw->locals[7]);
+	printk("%%I: %08lx %08lx  %08lx %08lx  %08lx %08lx  %08lx %08lx\n",
+	       rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3],
+	       rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]);
+}
+
+/*
+ * The show_stack is an external API which we do not use ourselves.
+ * The oops is printed in die_if_kernel.
+ */
+void show_stack(struct task_struct *tsk, unsigned long *_ksp)
+{
+	unsigned long pc, fp;
+	unsigned long task_base;
+	struct reg_window *rw;
+	int count = 0;
+
+	if (tsk != NULL)
+		task_base = (unsigned long) tsk->thread_info;
+	else
+		task_base = (unsigned long) current_thread_info();
+
+	fp = (unsigned long) _ksp;
+	do {
+		/* Bogus frame pointer? */
+		if (fp < (task_base + sizeof(struct thread_info)) ||
+		    fp >= (task_base + (PAGE_SIZE << 1)))
+			break;
+		rw = (struct reg_window *) fp;
+		pc = rw->ins[7];
+		printk("[%08lx : ", pc);
+		print_symbol("%s ] ", pc);
+		fp = rw->ins[6];
+	} while (++count < 16);
+	printk("\n");
+}
+
+/*
+ * Note: sparc64 has a pretty intricated thread_saved_pc, check it out.
+ */
+unsigned long thread_saved_pc(struct task_struct *tsk)
+{
+	return tsk->thread_info->kpc;
+}
+
+/*
+ * Free current thread data structures etc..
+ */
+void exit_thread(void)
+{
+#ifndef CONFIG_SMP
+	if(last_task_used_math == current) {
+#else
+	if(current_thread_info()->flags & _TIF_USEDFPU) {
+#endif
+		/* Keep process from leaving FPU in a bogon state. */
+		put_psr(get_psr() | PSR_EF);
+		fpsave(&current->thread.float_regs[0], &current->thread.fsr,
+		       &current->thread.fpqueue[0], &current->thread.fpqdepth);
+#ifndef CONFIG_SMP
+		last_task_used_math = NULL;
+#else
+		current_thread_info()->flags &= ~_TIF_USEDFPU;
+#endif
+	}
+}
+
+void flush_thread(void)
+{
+	current_thread_info()->w_saved = 0;
+
+	/* No new signal delivery by default */
+	current->thread.new_signal = 0;
+#ifndef CONFIG_SMP
+	if(last_task_used_math == current) {
+#else
+	if(current_thread_info()->flags & _TIF_USEDFPU) {
+#endif
+		/* Clean the fpu. */
+		put_psr(get_psr() | PSR_EF);
+		fpsave(&current->thread.float_regs[0], &current->thread.fsr,
+		       &current->thread.fpqueue[0], &current->thread.fpqdepth);
+#ifndef CONFIG_SMP
+		last_task_used_math = NULL;
+#else
+		current_thread_info()->flags &= ~_TIF_USEDFPU;
+#endif
+	}
+
+	/* Now, this task is no longer a kernel thread. */
+	current->thread.current_ds = USER_DS;
+	if (current->thread.flags & SPARC_FLAG_KTHREAD) {
+		current->thread.flags &= ~SPARC_FLAG_KTHREAD;
+
+		/* We must fixup kregs as well. */
+		/* XXX This was not fixed for ti for a while, worked. Unused? */
+		current->thread.kregs = (struct pt_regs *)
+		    ((char *)current->thread_info + (THREAD_SIZE - TRACEREG_SZ));
+	}
+}
+
+static __inline__ struct sparc_stackf __user *
+clone_stackframe(struct sparc_stackf __user *dst,
+		 struct sparc_stackf __user *src)
+{
+	unsigned long size, fp;
+	struct sparc_stackf *tmp;
+	struct sparc_stackf __user *sp;
+
+	if (get_user(tmp, &src->fp))
+		return NULL;
+
+	fp = (unsigned long) tmp;
+	size = (fp - ((unsigned long) src));
+	fp = (unsigned long) dst;
+	sp = (struct sparc_stackf __user *)(fp - size); 
+
+	/* do_fork() grabs the parent semaphore, we must release it
+	 * temporarily so we can build the child clone stack frame
+	 * without deadlocking.
+	 */
+	if (__copy_user(sp, src, size))
+		sp = NULL;
+	else if (put_user(fp, &sp->fp))
+		sp = NULL;
+
+	return sp;
+}
+
+asmlinkage int sparc_do_fork(unsigned long clone_flags,
+                             unsigned long stack_start,
+                             struct pt_regs *regs,
+                             unsigned long stack_size)
+{
+	unsigned long parent_tid_ptr, child_tid_ptr;
+
+	parent_tid_ptr = regs->u_regs[UREG_I2];
+	child_tid_ptr = regs->u_regs[UREG_I4];
+
+	return do_fork(clone_flags, stack_start,
+		       regs, stack_size,
+		       (int __user *) parent_tid_ptr,
+		       (int __user *) child_tid_ptr);
+}
+
+/* Copy a Sparc thread.  The fork() return value conventions
+ * under SunOS are nothing short of bletcherous:
+ * Parent -->  %o0 == childs  pid, %o1 == 0
+ * Child  -->  %o0 == parents pid, %o1 == 1
+ *
+ * NOTE: We have a separate fork kpsr/kwim because
+ *       the parent could change these values between
+ *       sys_fork invocation and when we reach here
+ *       if the parent should sleep while trying to
+ *       allocate the task_struct and kernel stack in
+ *       do_fork().
+ * XXX See comment above sys_vfork in sparc64. todo.
+ */
+extern void ret_from_fork(void);
+
+int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
+		unsigned long unused,
+		struct task_struct *p, struct pt_regs *regs)
+{
+	struct thread_info *ti = p->thread_info;
+	struct pt_regs *childregs;
+	char *new_stack;
+
+#ifndef CONFIG_SMP
+	if(last_task_used_math == current) {
+#else
+	if(current_thread_info()->flags & _TIF_USEDFPU) {
+#endif
+		put_psr(get_psr() | PSR_EF);
+		fpsave(&p->thread.float_regs[0], &p->thread.fsr,
+		       &p->thread.fpqueue[0], &p->thread.fpqdepth);
+#ifdef CONFIG_SMP
+		current_thread_info()->flags &= ~_TIF_USEDFPU;
+#endif
+	}
+
+	/*
+	 *  p->thread_info         new_stack   childregs
+	 *  !                      !           !             {if(PSR_PS) }
+	 *  V                      V (stk.fr.) V  (pt_regs)  { (stk.fr.) }
+	 *  +----- - - - - - ------+===========+============={+==========}+
+	 */
+	new_stack = (char*)ti + THREAD_SIZE;
+	if (regs->psr & PSR_PS)
+		new_stack -= STACKFRAME_SZ;
+	new_stack -= STACKFRAME_SZ + TRACEREG_SZ;
+	memcpy(new_stack, (char *)regs - STACKFRAME_SZ, STACKFRAME_SZ + TRACEREG_SZ);
+	childregs = (struct pt_regs *) (new_stack + STACKFRAME_SZ);
+
+	/*
+	 * A new process must start with interrupts closed in 2.5,
+	 * because this is how Mingo's scheduler works (see schedule_tail
+	 * and finish_arch_switch). If we do not do it, a timer interrupt hits
+	 * before we unlock, attempts to re-take the rq->lock, and then we die.
+	 * Thus, kpsr|=PSR_PIL.
+	 */
+	ti->ksp = (unsigned long) new_stack;
+	ti->kpc = (((unsigned long) ret_from_fork) - 0x8);
+	ti->kpsr = current->thread.fork_kpsr | PSR_PIL;
+	ti->kwim = current->thread.fork_kwim;
+
+	if(regs->psr & PSR_PS) {
+		extern struct pt_regs fake_swapper_regs;
+
+		p->thread.kregs = &fake_swapper_regs;
+		new_stack += STACKFRAME_SZ + TRACEREG_SZ;
+		childregs->u_regs[UREG_FP] = (unsigned long) new_stack;
+		p->thread.flags |= SPARC_FLAG_KTHREAD;
+		p->thread.current_ds = KERNEL_DS;
+		memcpy(new_stack, (void *)regs->u_regs[UREG_FP], STACKFRAME_SZ);
+		childregs->u_regs[UREG_G6] = (unsigned long) ti;
+	} else {
+		p->thread.kregs = childregs;
+		childregs->u_regs[UREG_FP] = sp;
+		p->thread.flags &= ~SPARC_FLAG_KTHREAD;
+		p->thread.current_ds = USER_DS;
+
+		if (sp != regs->u_regs[UREG_FP]) {
+			struct sparc_stackf __user *childstack;
+			struct sparc_stackf __user *parentstack;
+
+			/*
+			 * This is a clone() call with supplied user stack.
+			 * Set some valid stack frames to give to the child.
+			 */
+			childstack = (struct sparc_stackf __user *)
+				(sp & ~0x7UL);
+			parentstack = (struct sparc_stackf __user *)
+				regs->u_regs[UREG_FP];
+
+#if 0
+			printk("clone: parent stack:\n");
+			show_stackframe(parentstack);
+#endif
+
+			childstack = clone_stackframe(childstack, parentstack);
+			if (!childstack)
+				return -EFAULT;
+
+#if 0
+			printk("clone: child stack:\n");
+			show_stackframe(childstack);
+#endif
+
+			childregs->u_regs[UREG_FP] = (unsigned long)childstack;
+		}
+	}
+
+#ifdef CONFIG_SMP
+	/* FPU must be disabled on SMP. */
+	childregs->psr &= ~PSR_EF;
+#endif
+
+	/* Set the return value for the child. */
+	childregs->u_regs[UREG_I0] = current->pid;
+	childregs->u_regs[UREG_I1] = 1;
+
+	/* Set the return value for the parent. */
+	regs->u_regs[UREG_I1] = 0;
+
+	if (clone_flags & CLONE_SETTLS)
+		childregs->u_regs[UREG_G7] = regs->u_regs[UREG_I3];
+
+	return 0;
+}
+
+/*
+ * fill in the user structure for a core dump..
+ */
+void dump_thread(struct pt_regs * regs, struct user * dump)
+{
+	unsigned long first_stack_page;
+
+	dump->magic = SUNOS_CORE_MAGIC;
+	dump->len = sizeof(struct user);
+	dump->regs.psr = regs->psr;
+	dump->regs.pc = regs->pc;
+	dump->regs.npc = regs->npc;
+	dump->regs.y = regs->y;
+	/* fuck me plenty */
+	memcpy(&dump->regs.regs[0], &regs->u_regs[1], (sizeof(unsigned long) * 15));
+	dump->uexec = current->thread.core_exec;
+	dump->u_tsize = (((unsigned long) current->mm->end_code) -
+		((unsigned long) current->mm->start_code)) & ~(PAGE_SIZE - 1);
+	dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1)));
+	dump->u_dsize -= dump->u_tsize;
+	dump->u_dsize &= ~(PAGE_SIZE - 1);
+	first_stack_page = (regs->u_regs[UREG_FP] & ~(PAGE_SIZE - 1));
+	dump->u_ssize = (TASK_SIZE - first_stack_page) & ~(PAGE_SIZE - 1);
+	memcpy(&dump->fpu.fpstatus.fregs.regs[0], &current->thread.float_regs[0], (sizeof(unsigned long) * 32));
+	dump->fpu.fpstatus.fsr = current->thread.fsr;
+	dump->fpu.fpstatus.flags = dump->fpu.fpstatus.extra = 0;
+	dump->fpu.fpstatus.fpq_count = current->thread.fpqdepth;
+	memcpy(&dump->fpu.fpstatus.fpq[0], &current->thread.fpqueue[0],
+	       ((sizeof(unsigned long) * 2) * 16));
+	dump->sigcode = 0;
+}
+
+/*
+ * fill in the fpu structure for a core dump.
+ */
+int dump_fpu (struct pt_regs * regs, elf_fpregset_t * fpregs)
+{
+	if (used_math()) {
+		memset(fpregs, 0, sizeof(*fpregs));
+		fpregs->pr_q_entrysize = 8;
+		return 1;
+	}
+#ifdef CONFIG_SMP
+	if (current_thread_info()->flags & _TIF_USEDFPU) {
+		put_psr(get_psr() | PSR_EF);
+		fpsave(&current->thread.float_regs[0], &current->thread.fsr,
+		       &current->thread.fpqueue[0], &current->thread.fpqdepth);
+		if (regs != NULL) {
+			regs->psr &= ~(PSR_EF);
+			current_thread_info()->flags &= ~(_TIF_USEDFPU);
+		}
+	}
+#else
+	if (current == last_task_used_math) {
+		put_psr(get_psr() | PSR_EF);
+		fpsave(&current->thread.float_regs[0], &current->thread.fsr,
+		       &current->thread.fpqueue[0], &current->thread.fpqdepth);
+		if (regs != NULL) {
+			regs->psr &= ~(PSR_EF);
+			last_task_used_math = NULL;
+		}
+	}
+#endif
+	memcpy(&fpregs->pr_fr.pr_regs[0],
+	       &current->thread.float_regs[0],
+	       (sizeof(unsigned long) * 32));
+	fpregs->pr_fsr = current->thread.fsr;
+	fpregs->pr_qcnt = current->thread.fpqdepth;
+	fpregs->pr_q_entrysize = 8;
+	fpregs->pr_en = 1;
+	if(fpregs->pr_qcnt != 0) {
+		memcpy(&fpregs->pr_q[0],
+		       &current->thread.fpqueue[0],
+		       sizeof(struct fpq) * fpregs->pr_qcnt);
+	}
+	/* Zero out the rest. */
+	memset(&fpregs->pr_q[fpregs->pr_qcnt], 0,
+	       sizeof(struct fpq) * (32 - fpregs->pr_qcnt));
+	return 1;
+}
+
+/*
+ * sparc_execve() executes a new program after the asm stub has set
+ * things up for us.  This should basically do what I want it to.
+ */
+asmlinkage int sparc_execve(struct pt_regs *regs)
+{
+	int error, base = 0;
+	char *filename;
+
+	/* Check for indirect call. */
+	if(regs->u_regs[UREG_G1] == 0)
+		base = 1;
+
+	filename = getname((char __user *)regs->u_regs[base + UREG_I0]);
+	error = PTR_ERR(filename);
+	if(IS_ERR(filename))
+		goto out;
+	error = do_execve(filename,
+			  (char __user * __user *)regs->u_regs[base + UREG_I1],
+			  (char __user * __user *)regs->u_regs[base + UREG_I2],
+			  regs);
+	putname(filename);
+	if (error == 0) {
+		task_lock(current);
+		current->ptrace &= ~PT_DTRACE;
+		task_unlock(current);
+	}
+out:
+	return error;
+}
+
+/*
+ * This is the mechanism for creating a new kernel thread.
+ *
+ * NOTE! Only a kernel-only process(ie the swapper or direct descendants
+ * who haven't done an "execve()") should use this: it will work within
+ * a system call from a "real" process, but the process memory space will
+ * not be free'd until both the parent and the child have exited.
+ */
+pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+{
+	long retval;
+
+	__asm__ __volatile__("mov %4, %%g2\n\t"    /* Set aside fn ptr... */
+			     "mov %5, %%g3\n\t"    /* and arg. */
+			     "mov %1, %%g1\n\t"
+			     "mov %2, %%o0\n\t"    /* Clone flags. */
+			     "mov 0, %%o1\n\t"     /* usp arg == 0 */
+			     "t 0x10\n\t"          /* Linux/Sparc clone(). */
+			     "cmp %%o1, 0\n\t"
+			     "be 1f\n\t"           /* The parent, just return. */
+			     " nop\n\t"            /* Delay slot. */
+			     "jmpl %%g2, %%o7\n\t" /* Call the function. */
+			     " mov %%g3, %%o0\n\t" /* Get back the arg in delay. */
+			     "mov %3, %%g1\n\t"
+			     "t 0x10\n\t"          /* Linux/Sparc exit(). */
+			     /* Notreached by child. */
+			     "1: mov %%o0, %0\n\t" :
+			     "=r" (retval) :
+			     "i" (__NR_clone), "r" (flags | CLONE_VM | CLONE_UNTRACED),
+			     "i" (__NR_exit),  "r" (fn), "r" (arg) :
+			     "g1", "g2", "g3", "o0", "o1", "memory", "cc");
+	return retval;
+}
+
+unsigned long get_wchan(struct task_struct *task)
+{
+	unsigned long pc, fp, bias = 0;
+	unsigned long task_base = (unsigned long) task;
+        unsigned long ret = 0;
+	struct reg_window *rw;
+	int count = 0;
+
+	if (!task || task == current ||
+            task->state == TASK_RUNNING)
+		goto out;
+
+	fp = task->thread_info->ksp + bias;
+	do {
+		/* Bogus frame pointer? */
+		if (fp < (task_base + sizeof(struct thread_info)) ||
+		    fp >= (task_base + (2 * PAGE_SIZE)))
+			break;
+		rw = (struct reg_window *) fp;
+		pc = rw->ins[7];
+		if (!in_sched_functions(pc)) {
+			ret = pc;
+			goto out;
+		}
+		fp = rw->ins[6] + bias;
+	} while (++count < 16);
+
+out:
+	return ret;
+}
+
diff --git a/arch/sparc/kernel/ptrace.c b/arch/sparc/kernel/ptrace.c
new file mode 100644
index 0000000..fc4ad69
--- /dev/null
+++ b/arch/sparc/kernel/ptrace.c
@@ -0,0 +1,632 @@
+/* ptrace.c: Sparc process tracing support.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu)
+ *
+ * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson,
+ * and David Mosberger.
+ *
+ * Added Linux support -miguel (weird, eh?, the orignal code was meant
+ * to emulate SunOS).
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/security.h>
+
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#define MAGIC_CONSTANT 0x80000000
+
+
+/* Returning from ptrace is a bit tricky because the syscall return
+ * low level code assumes any value returned which is negative and
+ * is a valid errno will mean setting the condition codes to indicate
+ * an error return.  This doesn't work, so we have this hook.
+ */
+static inline void pt_error_return(struct pt_regs *regs, unsigned long error)
+{
+	regs->u_regs[UREG_I0] = error;
+	regs->psr |= PSR_C;
+	regs->pc = regs->npc;
+	regs->npc += 4;
+}
+
+static inline void pt_succ_return(struct pt_regs *regs, unsigned long value)
+{
+	regs->u_regs[UREG_I0] = value;
+	regs->psr &= ~PSR_C;
+	regs->pc = regs->npc;
+	regs->npc += 4;
+}
+
+static void
+pt_succ_return_linux(struct pt_regs *regs, unsigned long value, long __user *addr)
+{
+	if (put_user(value, addr)) {
+		pt_error_return(regs, EFAULT);
+		return;
+	}
+	regs->u_regs[UREG_I0] = 0;
+	regs->psr &= ~PSR_C;
+	regs->pc = regs->npc;
+	regs->npc += 4;
+}
+
+static void
+pt_os_succ_return (struct pt_regs *regs, unsigned long val, long __user *addr)
+{
+	if (current->personality == PER_SUNOS)
+		pt_succ_return (regs, val);
+	else
+		pt_succ_return_linux (regs, val, addr);
+}
+
+/* Fuck me gently with a chainsaw... */
+static inline void read_sunos_user(struct pt_regs *regs, unsigned long offset,
+				   struct task_struct *tsk, long __user *addr)
+{
+	struct pt_regs *cregs = tsk->thread.kregs;
+	struct thread_info *t = tsk->thread_info;
+	int v;
+	
+	if(offset >= 1024)
+		offset -= 1024; /* whee... */
+	if(offset & ((sizeof(unsigned long) - 1))) {
+		pt_error_return(regs, EIO);
+		return;
+	}
+	if(offset >= 16 && offset < 784) {
+		offset -= 16; offset >>= 2;
+		pt_os_succ_return(regs, *(((unsigned long *)(&t->reg_window[0]))+offset), addr);
+		return;
+	}
+	if(offset >= 784 && offset < 832) {
+		offset -= 784; offset >>= 2;
+		pt_os_succ_return(regs, *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset), addr);
+		return;
+	}
+	switch(offset) {
+	case 0:
+		v = t->ksp;
+		break;
+	case 4:
+		v = t->kpc;
+		break;
+	case 8:
+		v = t->kpsr;
+		break;
+	case 12:
+		v = t->uwinmask;
+		break;
+	case 832:
+		v = t->w_saved;
+		break;
+	case 896:
+		v = cregs->u_regs[UREG_I0];
+		break;
+	case 900:
+		v = cregs->u_regs[UREG_I1];
+		break;
+	case 904:
+		v = cregs->u_regs[UREG_I2];
+		break;
+	case 908:
+		v = cregs->u_regs[UREG_I3];
+		break;
+	case 912:
+		v = cregs->u_regs[UREG_I4];
+		break;
+	case 916:
+		v = cregs->u_regs[UREG_I5];
+		break;
+	case 920:
+		v = cregs->u_regs[UREG_I6];
+		break;
+	case 924:
+		if(tsk->thread.flags & MAGIC_CONSTANT)
+			v = cregs->u_regs[UREG_G1];
+		else
+			v = 0;
+		break;
+	case 940:
+		v = cregs->u_regs[UREG_I0];
+		break;
+	case 944:
+		v = cregs->u_regs[UREG_I1];
+		break;
+
+	case 948:
+		/* Isn't binary compatibility _fun_??? */
+		if(cregs->psr & PSR_C)
+			v = cregs->u_regs[UREG_I0] << 24;
+		else
+			v = 0;
+		break;
+
+		/* Rest of them are completely unsupported. */
+	default:
+		printk("%s [%d]: Wants to read user offset %ld\n",
+		       current->comm, current->pid, offset);
+		pt_error_return(regs, EIO);
+		return;
+	}
+	if (current->personality == PER_SUNOS)
+		pt_succ_return (regs, v);
+	else
+		pt_succ_return_linux (regs, v, addr);
+	return;
+}
+
+static inline void write_sunos_user(struct pt_regs *regs, unsigned long offset,
+				    struct task_struct *tsk)
+{
+	struct pt_regs *cregs = tsk->thread.kregs;
+	struct thread_info *t = tsk->thread_info;
+	unsigned long value = regs->u_regs[UREG_I3];
+
+	if(offset >= 1024)
+		offset -= 1024; /* whee... */
+	if(offset & ((sizeof(unsigned long) - 1)))
+		goto failure;
+	if(offset >= 16 && offset < 784) {
+		offset -= 16; offset >>= 2;
+		*(((unsigned long *)(&t->reg_window[0]))+offset) = value;
+		goto success;
+	}
+	if(offset >= 784 && offset < 832) {
+		offset -= 784; offset >>= 2;
+		*(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset) = value;
+		goto success;
+	}
+	switch(offset) {
+	case 896:
+		cregs->u_regs[UREG_I0] = value;
+		break;
+	case 900:
+		cregs->u_regs[UREG_I1] = value;
+		break;
+	case 904:
+		cregs->u_regs[UREG_I2] = value;
+		break;
+	case 908:
+		cregs->u_regs[UREG_I3] = value;
+		break;
+	case 912:
+		cregs->u_regs[UREG_I4] = value;
+		break;
+	case 916:
+		cregs->u_regs[UREG_I5] = value;
+		break;
+	case 920:
+		cregs->u_regs[UREG_I6] = value;
+		break;
+	case 924:
+		cregs->u_regs[UREG_I7] = value;
+		break;
+	case 940:
+		cregs->u_regs[UREG_I0] = value;
+		break;
+	case 944:
+		cregs->u_regs[UREG_I1] = value;
+		break;
+
+		/* Rest of them are completely unsupported or "no-touch". */
+	default:
+		printk("%s [%d]: Wants to write user offset %ld\n",
+		       current->comm, current->pid, offset);
+		goto failure;
+	}
+success:
+	pt_succ_return(regs, 0);
+	return;
+failure:
+	pt_error_return(regs, EIO);
+	return;
+}
+
+/* #define ALLOW_INIT_TRACING */
+/* #define DEBUG_PTRACE */
+
+#ifdef DEBUG_PTRACE
+char *pt_rq [] = {
+	/* 0  */ "TRACEME", "PEEKTEXT", "PEEKDATA", "PEEKUSR",
+	/* 4  */ "POKETEXT", "POKEDATA", "POKEUSR", "CONT",
+	/* 8  */ "KILL", "SINGLESTEP", "SUNATTACH", "SUNDETACH",
+	/* 12 */ "GETREGS", "SETREGS", "GETFPREGS", "SETFPREGS",
+	/* 16 */ "READDATA", "WRITEDATA", "READTEXT", "WRITETEXT",
+	/* 20 */ "GETFPAREGS", "SETFPAREGS", "unknown", "unknown",
+	/* 24 */ "SYSCALL", ""
+};
+#endif
+
+/*
+ * 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 void do_ptrace(struct pt_regs *regs)
+{
+	unsigned long request = regs->u_regs[UREG_I0];
+	unsigned long pid = regs->u_regs[UREG_I1];
+	unsigned long addr = regs->u_regs[UREG_I2];
+	unsigned long data = regs->u_regs[UREG_I3];
+	unsigned long addr2 = regs->u_regs[UREG_I4];
+	struct task_struct *child;
+	int ret;
+
+	lock_kernel();
+#ifdef DEBUG_PTRACE
+	{
+		char *s;
+
+		if ((request >= 0) && (request <= 24))
+			s = pt_rq [request];
+		else
+			s = "unknown";
+
+		if (request == PTRACE_POKEDATA && data == 0x91d02001){
+			printk ("do_ptrace: breakpoint pid=%d, addr=%08lx addr2=%08lx\n",
+				pid, addr, addr2);
+		} else 
+			printk("do_ptrace: rq=%s(%d) pid=%d addr=%08lx data=%08lx addr2=%08lx\n",
+			       s, (int) request, (int) pid, addr, data, addr2);
+	}
+#endif
+	if (request == PTRACE_TRACEME) {
+		int my_ret;
+
+		/* are we already being traced? */
+		if (current->ptrace & PT_PTRACED) {
+			pt_error_return(regs, EPERM);
+			goto out;
+		}
+		my_ret = security_ptrace(current->parent, current);
+		if (my_ret) {
+			pt_error_return(regs, -my_ret);
+			goto out;
+		}
+
+		/* set the ptrace bit in the process flags. */
+		current->ptrace |= PT_PTRACED;
+		pt_succ_return(regs, 0);
+		goto out;
+	}
+#ifndef ALLOW_INIT_TRACING
+	if (pid == 1) {
+		/* Can't dork with init. */
+		pt_error_return(regs, EPERM);
+		goto out;
+	}
+#endif
+	read_lock(&tasklist_lock);
+	child = find_task_by_pid(pid);
+	if (child)
+		get_task_struct(child);
+	read_unlock(&tasklist_lock);
+
+	if (!child) {
+		pt_error_return(regs, ESRCH);
+		goto out;
+	}
+
+	if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH)
+	    || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) {
+		if (ptrace_attach(child)) {
+			pt_error_return(regs, EPERM);
+			goto out_tsk;
+		}
+		pt_succ_return(regs, 0);
+		goto out_tsk;
+	}
+
+	ret = ptrace_check_attach(child, request == PTRACE_KILL);
+	if (ret < 0) {
+		pt_error_return(regs, -ret);
+		goto out_tsk;
+	}
+
+	switch(request) {
+	case PTRACE_PEEKTEXT: /* read word at location addr. */ 
+	case PTRACE_PEEKDATA: {
+		unsigned long tmp;
+
+		if (access_process_vm(child, addr,
+				      &tmp, sizeof(tmp), 0) == sizeof(tmp))
+			pt_os_succ_return(regs, tmp, (long __user *)data);
+		else
+			pt_error_return(regs, EIO);
+		goto out_tsk;
+	}
+
+	case PTRACE_PEEKUSR:
+		read_sunos_user(regs, addr, child, (long __user *) data);
+		goto out_tsk;
+
+	case PTRACE_POKEUSR:
+		write_sunos_user(regs, addr, child);
+		goto out_tsk;
+
+	case PTRACE_POKETEXT: /* write the word at location addr. */
+	case PTRACE_POKEDATA: {
+		if (access_process_vm(child, addr,
+				      &data, sizeof(data), 1) == sizeof(data))
+			pt_succ_return(regs, 0);
+		else
+			pt_error_return(regs, EIO);
+		goto out_tsk;
+	}
+
+	case PTRACE_GETREGS: {
+		struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
+		struct pt_regs *cregs = child->thread.kregs;
+		int rval;
+
+		if (!access_ok(VERIFY_WRITE, pregs, sizeof(struct pt_regs))) {
+			rval = -EFAULT;
+			pt_error_return(regs, -rval);
+			goto out_tsk;
+		}
+		__put_user(cregs->psr, (&pregs->psr));
+		__put_user(cregs->pc, (&pregs->pc));
+		__put_user(cregs->npc, (&pregs->npc));
+		__put_user(cregs->y, (&pregs->y));
+		for(rval = 1; rval < 16; rval++)
+			__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]));
+		pt_succ_return(regs, 0);
+#ifdef DEBUG_PTRACE
+		printk ("PC=%x nPC=%x o7=%x\n", cregs->pc, cregs->npc, cregs->u_regs [15]);
+#endif
+		goto out_tsk;
+	}
+
+	case PTRACE_SETREGS: {
+		struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
+		struct pt_regs *cregs = child->thread.kregs;
+		unsigned long psr, pc, npc, y;
+		int i;
+
+		/* Must be careful, tracing process can only set certain
+		 * bits in the psr.
+		 */
+		if (!access_ok(VERIFY_READ, pregs, sizeof(struct pt_regs))) {
+			pt_error_return(regs, EFAULT);
+			goto out_tsk;
+		}
+		__get_user(psr, (&pregs->psr));
+		__get_user(pc, (&pregs->pc));
+		__get_user(npc, (&pregs->npc));
+		__get_user(y, (&pregs->y));
+		psr &= PSR_ICC;
+		cregs->psr &= ~PSR_ICC;
+		cregs->psr |= psr;
+		if (!((pc | npc) & 3)) {
+			cregs->pc = pc;
+			cregs->npc =npc;
+		}
+		cregs->y = y;
+		for(i = 1; i < 16; i++)
+			__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]));
+		pt_succ_return(regs, 0);
+		goto out_tsk;
+	}
+
+	case PTRACE_GETFPREGS: {
+		struct fps {
+			unsigned long regs[32];
+			unsigned long fsr;
+			unsigned long flags;
+			unsigned long extra;
+			unsigned long fpqd;
+			struct fq {
+				unsigned long *insnaddr;
+				unsigned long insn;
+			} fpq[16];
+		};
+		struct fps __user *fps = (struct fps __user *) addr;
+		int i;
+
+		if (!access_ok(VERIFY_WRITE, fps, sizeof(struct fps))) {
+			i = -EFAULT;
+			pt_error_return(regs, -i);
+			goto out_tsk;
+		}
+		for(i = 0; i < 32; i++)
+			__put_user(child->thread.float_regs[i], (&fps->regs[i]));
+		__put_user(child->thread.fsr, (&fps->fsr));
+		__put_user(child->thread.fpqdepth, (&fps->fpqd));
+		__put_user(0, (&fps->flags));
+		__put_user(0, (&fps->extra));
+		for(i = 0; i < 16; i++) {
+			__put_user(child->thread.fpqueue[i].insn_addr,
+				   (&fps->fpq[i].insnaddr));
+			__put_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn));
+		}
+		pt_succ_return(regs, 0);
+		goto out_tsk;
+	}
+
+	case PTRACE_SETFPREGS: {
+		struct fps {
+			unsigned long regs[32];
+			unsigned long fsr;
+			unsigned long flags;
+			unsigned long extra;
+			unsigned long fpqd;
+			struct fq {
+				unsigned long *insnaddr;
+				unsigned long insn;
+			} fpq[16];
+		};
+		struct fps __user *fps = (struct fps __user *) addr;
+		int i;
+
+		if (!access_ok(VERIFY_READ, fps, sizeof(struct fps))) {
+			i = -EFAULT;
+			pt_error_return(regs, -i);
+			goto out_tsk;
+		}
+		copy_from_user(&child->thread.float_regs[0], &fps->regs[0], (32 * sizeof(unsigned long)));
+		__get_user(child->thread.fsr, (&fps->fsr));
+		__get_user(child->thread.fpqdepth, (&fps->fpqd));
+		for(i = 0; i < 16; i++) {
+			__get_user(child->thread.fpqueue[i].insn_addr,
+				   (&fps->fpq[i].insnaddr));
+			__get_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn));
+		}
+		pt_succ_return(regs, 0);
+		goto out_tsk;
+	}
+
+	case PTRACE_READTEXT:
+	case PTRACE_READDATA: {
+		int res = ptrace_readdata(child, addr,
+					  (void __user *) addr2, data);
+
+		if (res == data) {
+			pt_succ_return(regs, 0);
+			goto out_tsk;
+		}
+		/* Partial read is an IO failure */
+		if (res >= 0)
+			res = -EIO;
+		pt_error_return(regs, -res);
+		goto out_tsk;
+	}
+
+	case PTRACE_WRITETEXT:
+	case PTRACE_WRITEDATA: {
+		int res = ptrace_writedata(child, (void __user *) addr2,
+					   addr, data);
+
+		if (res == data) {
+			pt_succ_return(regs, 0);
+			goto out_tsk;
+		}
+		/* Partial write is an IO failure */
+		if (res >= 0)
+			res = -EIO;
+		pt_error_return(regs, -res);
+		goto out_tsk;
+	}
+
+	case PTRACE_SYSCALL: /* continue and stop at (return from) syscall */
+		addr = 1;
+
+	case PTRACE_CONT: { /* restart after signal. */
+		if (data > _NSIG) {
+			pt_error_return(regs, EIO);
+			goto out_tsk;
+		}
+		if (addr != 1) {
+			if (addr & 3) {
+				pt_error_return(regs, EINVAL);
+				goto out_tsk;
+			}
+#ifdef DEBUG_PTRACE
+			printk ("Original: %08lx %08lx\n", child->thread.kregs->pc, child->thread.kregs->npc);
+			printk ("Continuing with %08lx %08lx\n", addr, addr+4);
+#endif
+			child->thread.kregs->pc = addr;
+			child->thread.kregs->npc = addr + 4;
+		}
+
+		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;
+#ifdef DEBUG_PTRACE
+		printk("CONT: %s [%d]: set exit_code = %x %lx %lx\n",
+			child->comm, child->pid, child->exit_code,
+			child->thread.kregs->pc,
+			child->thread.kregs->npc);
+#endif
+		wake_up_process(child);
+		pt_succ_return(regs, 0);
+		goto out_tsk;
+	}
+
+/*
+ * 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: {
+		if (child->exit_state == EXIT_ZOMBIE) {	/* already dead */
+			pt_succ_return(regs, 0);
+			goto out_tsk;
+		}
+		wake_up_process(child);
+		child->exit_code = SIGKILL;
+		pt_succ_return(regs, 0);
+		goto out_tsk;
+	}
+
+	case PTRACE_SUNDETACH: { /* detach a process that was attached. */
+		int err = ptrace_detach(child, data);
+		if (err) {
+			pt_error_return(regs, EIO);
+			goto out_tsk;
+		}
+		pt_succ_return(regs, 0);
+		goto out_tsk;
+	}
+
+	/* PTRACE_DUMPCORE unsupported... */
+
+	default: {
+		int err = ptrace_request(child, request, addr, data);
+		if (err)
+			pt_error_return(regs, -err);
+		else
+			pt_succ_return(regs, 0);
+		goto out_tsk;
+	}
+	}
+out_tsk:
+	if (child)
+		put_task_struct(child);
+out:
+	unlock_kernel();
+}
+
+asmlinkage void syscall_trace(void)
+{
+#ifdef DEBUG_PTRACE
+	printk("%s [%d]: syscall_trace\n", current->comm, current->pid);
+#endif
+	if (!test_thread_flag(TIF_SYSCALL_TRACE))
+		return;
+	if (!(current->ptrace & PT_PTRACED))
+		return;
+	current->thread.flags ^= MAGIC_CONSTANT;
+	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
+	 */
+#ifdef DEBUG_PTRACE
+	printk("%s [%d]: syscall_trace exit= %x\n", current->comm,
+		current->pid, current->exit_code);
+#endif
+	if (current->exit_code) {
+		send_sig (current->exit_code, current, 1);
+		current->exit_code = 0;
+	}
+}
diff --git a/arch/sparc/kernel/rtrap.S b/arch/sparc/kernel/rtrap.S
new file mode 100644
index 0000000..f7460d8
--- /dev/null
+++ b/arch/sparc/kernel/rtrap.S
@@ -0,0 +1,319 @@
+/* $Id: rtrap.S,v 1.58 2002/01/31 03:30:05 davem Exp $
+ * rtrap.S: Return from Sparc trap low-level code.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/page.h>
+#include <asm/ptrace.h>
+#include <asm/psr.h>
+#include <asm/asi.h>
+#include <asm/smp.h>
+#include <asm/contregs.h>
+#include <asm/winmacro.h>
+#include <asm/asmmacro.h>
+#include <asm/thread_info.h>
+
+#define t_psr     l0
+#define t_pc      l1
+#define t_npc     l2
+#define t_wim     l3
+#define twin_tmp1 l4
+#define glob_tmp  g4
+#define curptr    g6
+
+	/* 7 WINDOW SPARC PATCH INSTRUCTIONS */
+	.globl	rtrap_7win_patch1, rtrap_7win_patch2, rtrap_7win_patch3
+	.globl	rtrap_7win_patch4, rtrap_7win_patch5
+rtrap_7win_patch1:	srl	%t_wim, 0x6, %glob_tmp
+rtrap_7win_patch2:	and	%glob_tmp, 0x7f, %glob_tmp
+rtrap_7win_patch3:	srl	%g1, 7, %g2
+rtrap_7win_patch4:	srl	%g2, 6, %g2
+rtrap_7win_patch5:	and	%g1, 0x7f, %g1
+	/* END OF PATCH INSTRUCTIONS */
+
+	/* We need to check for a few things which are:
+	 * 1) The need to call schedule() because this
+	 *    processes quantum is up.
+	 * 2) Pending signals for this process, if any
+	 *    exist we need to call do_signal() to do
+	 *    the needy.
+	 *
+	 * Else we just check if the rett would land us
+	 * in an invalid window, if so we need to grab
+	 * it off the user/kernel stack first.
+	 */
+
+	.globl	ret_trap_entry, rtrap_patch1, rtrap_patch2
+	.globl	rtrap_patch3, rtrap_patch4, rtrap_patch5
+	.globl	ret_trap_lockless_ipi
+ret_trap_entry:
+ret_trap_lockless_ipi:
+	andcc	%t_psr, PSR_PS, %g0
+	be	1f
+	 nop
+
+	wr	%t_psr, 0x0, %psr
+	b	ret_trap_kernel
+	 nop
+
+1:
+	ld	[%curptr + TI_FLAGS], %g2
+	andcc	%g2, (_TIF_NEED_RESCHED), %g0
+	be	signal_p
+	 nop
+
+	call	schedule
+	 nop
+
+	ld	[%curptr + TI_FLAGS], %g2
+signal_p:
+	andcc	%g2, (_TIF_NOTIFY_RESUME|_TIF_SIGPENDING), %g0
+	bz,a	ret_trap_continue
+	 ld	[%sp + STACKFRAME_SZ + PT_PSR], %t_psr
+
+	clr	%o0
+	mov	%l5, %o2
+	mov	%l6, %o3
+	call	do_signal
+	 add	%sp, STACKFRAME_SZ, %o1	! pt_regs ptr
+
+	/* Fall through. */
+	ld	[%sp + STACKFRAME_SZ + PT_PSR], %t_psr
+	clr	%l6
+ret_trap_continue:
+	wr	%t_psr, 0x0, %psr
+	WRITE_PAUSE
+
+	ld	[%curptr + TI_W_SAVED], %twin_tmp1
+	orcc	%g0, %twin_tmp1, %g0
+	be	ret_trap_nobufwins
+	 nop
+
+	wr	%t_psr, PSR_ET, %psr
+	WRITE_PAUSE
+
+	mov	1, %o1
+	call	try_to_clear_window_buffer
+	 add	%sp, STACKFRAME_SZ, %o0
+
+	b	signal_p
+	 ld	[%curptr + TI_FLAGS], %g2
+
+ret_trap_nobufwins:
+	/* Load up the user's out registers so we can pull
+	 * a window from the stack, if necessary.
+	 */
+	LOAD_PT_INS(sp)
+
+	/* If there are already live user windows in the
+	 * set we can return from trap safely.
+	 */
+	ld	[%curptr + TI_UWINMASK], %twin_tmp1
+	orcc	%g0, %twin_tmp1, %g0
+	bne	ret_trap_userwins_ok
+	 nop
+
+		/* Calculate new %wim, we have to pull a register
+		 * window from the users stack.
+		 */
+ret_trap_pull_one_window:
+		rd	%wim, %t_wim
+		sll	%t_wim, 0x1, %twin_tmp1
+rtrap_patch1:	srl	%t_wim, 0x7, %glob_tmp
+		or	%glob_tmp, %twin_tmp1, %glob_tmp
+rtrap_patch2:	and	%glob_tmp, 0xff, %glob_tmp
+
+		wr	%glob_tmp, 0x0, %wim
+
+				/* Here comes the architecture specific 
+				 * branch to the user stack checking routine
+				 * for return from traps.
+				 */
+				.globl	rtrap_mmu_patchme
+rtrap_mmu_patchme:	b	sun4c_rett_stackchk
+				 andcc	%fp, 0x7, %g0	
+
+ret_trap_userwins_ok:
+	LOAD_PT_PRIV(sp, t_psr, t_pc, t_npc)
+	or	%t_pc, %t_npc, %g2
+	andcc	%g2, 0x3, %g0
+	be	1f
+	 nop
+
+	b	ret_trap_unaligned_pc
+	 add	%sp, STACKFRAME_SZ, %o0
+
+1:
+	LOAD_PT_YREG(sp, g1)
+	LOAD_PT_GLOBALS(sp)
+
+	wr	%t_psr, 0x0, %psr
+	WRITE_PAUSE
+
+	jmp	%t_pc
+	rett	%t_npc
+	
+ret_trap_unaligned_pc:
+	ld	[%sp + STACKFRAME_SZ + PT_PC], %o1
+	ld	[%sp + STACKFRAME_SZ + PT_NPC], %o2
+	ld	[%sp + STACKFRAME_SZ + PT_PSR], %o3
+
+	wr	%t_wim, 0x0, %wim		! or else...
+
+	wr	%t_psr, PSR_ET, %psr
+	WRITE_PAUSE
+
+	call	do_memaccess_unaligned
+	 nop
+
+	b	signal_p
+	 ld	[%curptr + TI_FLAGS], %g2
+
+ret_trap_kernel:
+		/* Will the rett land us in the invalid window? */
+		mov	2, %g1
+		sll	%g1, %t_psr, %g1
+rtrap_patch3:	srl	%g1, 8, %g2
+		or	%g1, %g2, %g1
+		rd	%wim, %g2
+		andcc	%g2, %g1, %g0
+		be	1f		! Nope, just return from the trap
+		 sll	%g2, 0x1, %g1
+
+		/* We have to grab a window before returning. */
+rtrap_patch4:	srl	%g2, 7,  %g2
+		or	%g1, %g2, %g1
+rtrap_patch5:	and	%g1, 0xff, %g1
+
+	wr	%g1, 0x0, %wim
+
+	/* Grrr, make sure we load from the right %sp... */
+	LOAD_PT_ALL(sp, t_psr, t_pc, t_npc, g1)
+
+	restore	%g0, %g0, %g0
+	LOAD_WINDOW(sp)
+	b	2f
+	 save	%g0, %g0, %g0
+
+	/* Reload the entire frame in case this is from a
+	 * kernel system call or whatever...
+	 */
+1:
+	LOAD_PT_ALL(sp, t_psr, t_pc, t_npc, g1)
+2:
+	wr	%t_psr, 0x0, %psr
+	WRITE_PAUSE
+
+	jmp	%t_pc
+	rett	%t_npc
+
+ret_trap_user_stack_is_bolixed:
+	wr	%t_wim, 0x0, %wim
+
+	wr	%t_psr, PSR_ET, %psr
+	WRITE_PAUSE
+
+	call	window_ret_fault
+	 add	%sp, STACKFRAME_SZ, %o0
+
+	b	signal_p
+	 ld	[%curptr + TI_FLAGS], %g2
+
+
+	.globl	sun4c_rett_stackchk
+sun4c_rett_stackchk:
+	be	1f
+	 and	%fp, 0xfff, %g1		! delay slot
+
+	b	ret_trap_user_stack_is_bolixed + 0x4
+	 wr	%t_wim, 0x0, %wim
+
+	/* See if we have to check the sanity of one page or two */
+1:
+	add	%g1, 0x38, %g1
+	sra	%fp, 29, %g2
+	add	%g2, 0x1, %g2
+	andncc	%g2, 0x1, %g0
+	be	1f
+	 andncc	%g1, 0xff8, %g0
+
+	/* %sp is in vma hole, yuck */
+	b	ret_trap_user_stack_is_bolixed + 0x4
+	 wr	%t_wim, 0x0, %wim
+
+1:
+	be	sun4c_rett_onepage	/* Only one page to check */
+	 lda	[%fp] ASI_PTE, %g2
+
+sun4c_rett_twopages:
+	add	%fp, 0x38, %g1
+	sra	%g1, 29, %g2
+	add	%g2, 0x1, %g2
+	andncc	%g2, 0x1, %g0
+	be	1f
+	 lda	[%g1] ASI_PTE, %g2
+
+	/* Second page is in vma hole */
+	b	ret_trap_user_stack_is_bolixed + 0x4
+	 wr	%t_wim, 0x0, %wim
+
+1:
+	srl	%g2, 29, %g2
+	andcc	%g2, 0x4, %g0
+	bne	sun4c_rett_onepage
+	 lda	[%fp] ASI_PTE, %g2
+
+	/* Second page has bad perms */
+	b	ret_trap_user_stack_is_bolixed + 0x4
+	 wr	%t_wim, 0x0, %wim
+
+sun4c_rett_onepage:
+	srl	%g2, 29, %g2
+	andcc	%g2, 0x4, %g0
+	bne,a	1f
+	 restore %g0, %g0, %g0
+
+	/* A page had bad page permissions, losing... */
+	b	ret_trap_user_stack_is_bolixed + 0x4
+	 wr	%t_wim, 0x0, %wim
+
+	/* Whee, things are ok, load the window and continue. */
+1:
+	LOAD_WINDOW(sp)
+
+	b	ret_trap_userwins_ok
+	 save	%g0, %g0, %g0
+
+	.globl	srmmu_rett_stackchk
+srmmu_rett_stackchk:
+	bne	ret_trap_user_stack_is_bolixed
+	 sethi   %hi(PAGE_OFFSET), %g1
+	cmp	%g1, %fp
+	bleu	ret_trap_user_stack_is_bolixed
+	 mov	AC_M_SFSR, %g1
+	lda	[%g1] ASI_M_MMUREGS, %g0
+
+	lda	[%g0] ASI_M_MMUREGS, %g1
+	or	%g1, 0x2, %g1
+	sta	%g1, [%g0] ASI_M_MMUREGS
+
+	restore	%g0, %g0, %g0
+
+	LOAD_WINDOW(sp)
+
+	save	%g0, %g0, %g0
+
+	andn	%g1, 0x2, %g1
+	sta	%g1, [%g0] ASI_M_MMUREGS
+
+	mov	AC_M_SFAR, %g2
+	lda	[%g2] ASI_M_MMUREGS, %g2
+
+	mov	AC_M_SFSR, %g1
+	lda	[%g1] ASI_M_MMUREGS, %g1
+	andcc	%g1, 0x2, %g0
+	be	ret_trap_userwins_ok
+	 nop
+
+	b,a	ret_trap_user_stack_is_bolixed
diff --git a/arch/sparc/kernel/sclow.S b/arch/sparc/kernel/sclow.S
new file mode 100644
index 0000000..3a867fc
--- /dev/null
+++ b/arch/sparc/kernel/sclow.S
@@ -0,0 +1,86 @@
+/* sclow.S: Low level special syscall handling.
+ *          Basically these are cases where we can completely
+ *          handle the system call without saving any state
+ *          because we know that the process will not sleep.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/ptrace.h>
+#include <asm/asm_offsets.h>
+#include <asm/errno.h>
+#include <asm/winmacro.h>
+#include <asm/thread_info.h>
+#include <asm/psr.h>
+#include <asm/page.h>
+
+#define CC_AND_RETT  \
+	set	PSR_C, %l4; \
+	andn	%l0, %l4, %l4; \
+	wr	%l4, 0x0, %psr; \
+	nop; nop; nop; \
+	jmp	%l2; \
+	rett	%l2 + 4;
+
+#define SC_AND_RETT  \
+	set	PSR_C, %l4; \
+	or	%l0, %l4, %l4; \
+	wr	%l4, 0x0, %psr; \
+	nop; nop; nop; \
+	jmp	%l2; \
+	rett	%l2 + 4;
+
+#define LABEL(func)  func##_low
+
+	.globl	LABEL(sunosnop)
+LABEL(sunosnop):
+	CC_AND_RETT
+
+#if (ASIZ_task_uid == 2 && ASIZ_task_euid == 2)
+	.globl	LABEL(sunosgetuid)
+LABEL(sunosgetuid):
+	LOAD_CURRENT(l4, l5)
+	ld	[%l4 + TI_TASK], %l4
+	lduh	[%l4 + AOFF_task_uid], %i0
+	lduh	[%l4 + AOFF_task_euid], %i1
+	CC_AND_RETT
+#endif
+
+#if (ASIZ_task_gid == 2 && ASIZ_task_egid == 2)
+	.globl	LABEL(sunosgetgid)
+LABEL(sunosgetgid):
+	LOAD_CURRENT(l4, l5)
+	ld	[%l4 + TI_TASK], %l4
+	lduh	[%l4 + AOFF_task_gid], %i0
+	lduh	[%l4 + AOFF_task_egid], %i1
+	CC_AND_RETT
+#endif
+
+	.globl	LABEL(sunosmctl)
+LABEL(sunosmctl):
+	mov	0, %i0
+	CC_AND_RETT
+
+	.globl	LABEL(sunosgdtsize)
+LABEL(sunosgdtsize):	
+	mov	256, %i0
+	CC_AND_RETT
+
+	.globl	LABEL(getpagesize)
+LABEL(getpagesize):
+	set	PAGE_SIZE, %i0
+	CC_AND_RETT
+
+	/* XXX sys_nice() XXX */
+	/* XXX sys_setpriority() XXX */
+	/* XXX sys_getpriority() XXX */
+	/* XXX sys_setregid() XXX */
+	/* XXX sys_setgid() XXX */
+	/* XXX sys_setreuid() XXX */
+	/* XXX sys_setuid() XXX */
+	/* XXX sys_setfsuid() XXX */
+	/* XXX sys_setfsgid() XXX */
+	/* XXX sys_setpgid() XXX */
+	/* XXX sys_getpgid() XXX */
+	/* XXX sys_setsid() XXX */
+	/* XXX sys_getsid() XXX */
diff --git a/arch/sparc/kernel/semaphore.c b/arch/sparc/kernel/semaphore.c
new file mode 100644
index 0000000..0c37c1a
--- /dev/null
+++ b/arch/sparc/kernel/semaphore.c
@@ -0,0 +1,155 @@
+/* $Id: semaphore.c,v 1.7 2001/04/18 21:06:05 davem Exp $ */
+
+/* sparc32 semaphore implementation, based on i386 version */
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <asm/semaphore.h>
+
+/*
+ * Semaphores are implemented using a two-way counter:
+ * The "count" variable is decremented for each process
+ * that tries to acquire the semaphore, while the "sleeping"
+ * variable is a count of such acquires.
+ *
+ * Notably, the inline "up()" and "down()" functions can
+ * efficiently test if they need to do any extra work (up
+ * needs to do something only if count was negative before
+ * the increment operation.
+ *
+ * "sleeping" and the contention routine ordering is
+ * protected by the semaphore spinlock.
+ *
+ * Note that these functions are only called when there is
+ * contention on the lock, and as such all this is the
+ * "non-critical" part of the whole semaphore business. The
+ * critical part is the inline stuff in <asm/semaphore.h>
+ * where we want to avoid any extra jumps and calls.
+ */
+
+/*
+ * Logic:
+ *  - only on a boundary condition do we need to care. When we go
+ *    from a negative count to a non-negative, we wake people up.
+ *  - when we go from a non-negative count to a negative do we
+ *    (a) synchronize with the "sleeper" count and (b) make sure
+ *    that we're on the wakeup list before we synchronize so that
+ *    we cannot lose wakeup events.
+ */
+
+void __up(struct semaphore *sem)
+{
+	wake_up(&sem->wait);
+}
+
+static DEFINE_SPINLOCK(semaphore_lock);
+
+void __sched __down(struct semaphore * sem)
+{
+	struct task_struct *tsk = current;
+	DECLARE_WAITQUEUE(wait, tsk);
+	tsk->state = TASK_UNINTERRUPTIBLE;
+	add_wait_queue_exclusive(&sem->wait, &wait);
+
+	spin_lock_irq(&semaphore_lock);
+	sem->sleepers++;
+	for (;;) {
+		int sleepers = sem->sleepers;
+
+		/*
+		 * Add "everybody else" into it. They aren't
+		 * playing, because we own the spinlock.
+		 */
+		if (!atomic24_add_negative(sleepers - 1, &sem->count)) {
+			sem->sleepers = 0;
+			break;
+		}
+		sem->sleepers = 1;	/* us - see -1 above */
+		spin_unlock_irq(&semaphore_lock);
+
+		schedule();
+		tsk->state = TASK_UNINTERRUPTIBLE;
+		spin_lock_irq(&semaphore_lock);
+	}
+	spin_unlock_irq(&semaphore_lock);
+	remove_wait_queue(&sem->wait, &wait);
+	tsk->state = TASK_RUNNING;
+	wake_up(&sem->wait);
+}
+
+int __sched __down_interruptible(struct semaphore * sem)
+{
+	int retval = 0;
+	struct task_struct *tsk = current;
+	DECLARE_WAITQUEUE(wait, tsk);
+	tsk->state = TASK_INTERRUPTIBLE;
+	add_wait_queue_exclusive(&sem->wait, &wait);
+
+	spin_lock_irq(&semaphore_lock);
+	sem->sleepers ++;
+	for (;;) {
+		int sleepers = sem->sleepers;
+
+		/*
+		 * With signals pending, this turns into
+		 * the trylock failure case - we won't be
+		 * sleeping, and we* can't get the lock as
+		 * it has contention. Just correct the count
+		 * and exit.
+		 */
+		if (signal_pending(current)) {
+			retval = -EINTR;
+			sem->sleepers = 0;
+			atomic24_add(sleepers, &sem->count);
+			break;
+		}
+
+		/*
+		 * Add "everybody else" into it. They aren't
+		 * playing, because we own the spinlock. The
+		 * "-1" is because we're still hoping to get
+		 * the lock.
+		 */
+		if (!atomic24_add_negative(sleepers - 1, &sem->count)) {
+			sem->sleepers = 0;
+			break;
+		}
+		sem->sleepers = 1;	/* us - see -1 above */
+		spin_unlock_irq(&semaphore_lock);
+
+		schedule();
+		tsk->state = TASK_INTERRUPTIBLE;
+		spin_lock_irq(&semaphore_lock);
+	}
+	spin_unlock_irq(&semaphore_lock);
+	tsk->state = TASK_RUNNING;
+	remove_wait_queue(&sem->wait, &wait);
+	wake_up(&sem->wait);
+	return retval;
+}
+
+/*
+ * Trylock failed - make sure we correct for
+ * having decremented the count.
+ */
+int __down_trylock(struct semaphore * sem)
+{
+	int sleepers;
+	unsigned long flags;
+
+	spin_lock_irqsave(&semaphore_lock, flags);
+	sleepers = sem->sleepers + 1;
+	sem->sleepers = 0;
+
+	/*
+	 * Add "everybody else" and us into it. They aren't
+	 * playing, because we own the spinlock.
+	 */
+	if (!atomic24_add_negative(sleepers, &sem->count))
+		wake_up(&sem->wait);
+
+	spin_unlock_irqrestore(&semaphore_lock, flags);
+	return 1;
+}
diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c
new file mode 100644
index 0000000..55352ed
--- /dev/null
+++ b/arch/sparc/kernel/setup.c
@@ -0,0 +1,476 @@
+/*  $Id: setup.c,v 1.126 2001/11/13 00:49:27 davem Exp $
+ *  linux/arch/sparc/kernel/setup.c
+ *
+ *  Copyright (C) 1995  David S. Miller (davem@caip.rutgers.edu)
+ *  Copyright (C) 2000  Anton Blanchard (anton@samba.org)
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/initrd.h>
+#include <asm/smp.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/tty.h>
+#include <linux/delay.h>
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/syscalls.h>
+#include <linux/kdev_t.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/console.h>
+#include <linux/spinlock.h>
+#include <linux/root_dev.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/oplib.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/traps.h>
+#include <asm/vaddrs.h>
+#include <asm/kdebug.h>
+#include <asm/mbus.h>
+#include <asm/idprom.h>
+#include <asm/machines.h>
+#include <asm/cpudata.h>
+#include <asm/setup.h>
+
+struct screen_info screen_info = {
+	0, 0,			/* orig-x, orig-y */
+	0,			/* unused */
+	0,			/* orig-video-page */
+	0,			/* orig-video-mode */
+	128,			/* orig-video-cols */
+	0,0,0,			/* ega_ax, ega_bx, ega_cx */
+	54,			/* orig-video-lines */
+	0,                      /* orig-video-isVGA */
+	16                      /* orig-video-points */
+};
+
+/* Typing sync at the prom prompt calls the function pointed to by
+ * romvec->pv_synchook which I set to the following function.
+ * This should sync all filesystems and return, for now it just
+ * prints out pretty messages and returns.
+ */
+
+extern unsigned long trapbase;
+void (*prom_palette)(int);
+
+/* Pretty sick eh? */
+void prom_sync_me(void)
+{
+	unsigned long prom_tbr, flags;
+
+	/* XXX Badly broken. FIX! - Anton */
+	local_irq_save(flags);
+	__asm__ __volatile__("rd %%tbr, %0\n\t" : "=r" (prom_tbr));
+	__asm__ __volatile__("wr %0, 0x0, %%tbr\n\t"
+			     "nop\n\t"
+			     "nop\n\t"
+			     "nop\n\t" : : "r" (&trapbase));
+
+	if (prom_palette)
+		prom_palette(1);
+	prom_printf("PROM SYNC COMMAND...\n");
+	show_free_areas();
+	if(current->pid != 0) {
+		local_irq_enable();
+		sys_sync();
+		local_irq_disable();
+	}
+	prom_printf("Returning to prom\n");
+
+	__asm__ __volatile__("wr %0, 0x0, %%tbr\n\t"
+			     "nop\n\t"
+			     "nop\n\t"
+			     "nop\n\t" : : "r" (prom_tbr));
+	local_irq_restore(flags);
+
+	return;
+}
+
+unsigned int boot_flags __initdata = 0;
+#define BOOTME_DEBUG  0x1
+#define BOOTME_SINGLE 0x2
+
+/* Exported for mm/init.c:paging_init. */
+unsigned long cmdline_memory_size __initdata = 0;
+
+static void
+prom_console_write(struct console *con, const char *s, unsigned n)
+{
+	prom_write(s, n);
+}
+
+static struct console prom_debug_console = {
+	.name =		"debug",
+	.write =	prom_console_write,
+	.flags =	CON_PRINTBUFFER,
+	.index =	-1,
+};
+
+int obp_system_intr(void)
+{
+	if (boot_flags & BOOTME_DEBUG) {
+		printk("OBP: system interrupted\n");
+		prom_halt();
+		return 1;
+	}
+	return 0;
+}
+
+/* 
+ * Process kernel command line switches that are specific to the
+ * SPARC or that require special low-level processing.
+ */
+static void __init process_switch(char c)
+{
+	switch (c) {
+	case 'd':
+		boot_flags |= BOOTME_DEBUG;
+		break;
+	case 's':
+		boot_flags |= BOOTME_SINGLE;
+		break;
+	case 'h':
+		prom_printf("boot_flags_init: Halt!\n");
+		prom_halt();
+		break;
+	case 'p':
+		/* Use PROM debug console. */
+		register_console(&prom_debug_console);
+		break;
+	default:
+		printk("Unknown boot switch (-%c)\n", c);
+		break;
+	}
+}
+
+static void __init process_console(char *commands)
+{
+	serial_console = 0;
+	commands += 8;
+	/* Linux-style serial */
+	if (!strncmp(commands, "ttyS", 4))
+		serial_console = simple_strtoul(commands + 4, NULL, 10) + 1;
+	else if (!strncmp(commands, "tty", 3)) {
+		char c = *(commands + 3);
+		/* Solaris-style serial */
+		if (c == 'a' || c == 'b')
+			serial_console = c - 'a' + 1;
+		/* else Linux-style fbcon, not serial */
+	}
+#if defined(CONFIG_PROM_CONSOLE)
+	if (!strncmp(commands, "prom", 4)) {
+		char *p;
+
+		for (p = commands - 8; *p && *p != ' '; p++)
+			*p = ' ';
+		conswitchp = &prom_con;
+	}
+#endif
+}
+
+static void __init boot_flags_init(char *commands)
+{
+	while (*commands) {
+		/* Move to the start of the next "argument". */
+		while (*commands && *commands == ' ')
+			commands++;
+
+		/* Process any command switches, otherwise skip it. */
+		if (*commands == '\0')
+			break;
+		if (*commands == '-') {
+			commands++;
+			while (*commands && *commands != ' ')
+				process_switch(*commands++);
+			continue;
+		}
+		if (!strncmp(commands, "console=", 8)) {
+			process_console(commands);
+		} else if (!strncmp(commands, "mem=", 4)) {
+			/*
+			 * "mem=XXX[kKmM] overrides the PROM-reported
+			 * memory size.
+			 */
+			cmdline_memory_size = simple_strtoul(commands + 4,
+						     &commands, 0);
+			if (*commands == 'K' || *commands == 'k') {
+				cmdline_memory_size <<= 10;
+				commands++;
+			} else if (*commands=='M' || *commands=='m') {
+				cmdline_memory_size <<= 20;
+				commands++;
+			}
+		}
+		while (*commands && *commands != ' ')
+			commands++;
+	}
+}
+
+/* This routine will in the future do all the nasty prom stuff
+ * to probe for the mmu type and its parameters, etc. This will
+ * also be where SMP things happen plus the Sparc specific memory
+ * physical memory probe as on the alpha.
+ */
+
+extern int prom_probe_memory(void);
+extern void sun4c_probe_vac(void);
+extern char cputypval;
+extern unsigned long start, end;
+extern void panic_setup(char *, int *);
+
+extern unsigned short root_flags;
+extern unsigned short root_dev;
+extern unsigned short ram_flags;
+#define RAMDISK_IMAGE_START_MASK	0x07FF
+#define RAMDISK_PROMPT_FLAG		0x8000
+#define RAMDISK_LOAD_FLAG		0x4000
+
+extern int root_mountflags;
+
+char reboot_command[COMMAND_LINE_SIZE];
+enum sparc_cpu sparc_cpu_model;
+
+struct tt_entry *sparc_ttable;
+
+struct pt_regs fake_swapper_regs;
+
+extern void paging_init(void);
+
+void __init setup_arch(char **cmdline_p)
+{
+	int i;
+	unsigned long highest_paddr;
+
+	sparc_ttable = (struct tt_entry *) &start;
+
+	/* Initialize PROM console and command line. */
+	*cmdline_p = prom_getbootargs();
+	strcpy(saved_command_line, *cmdline_p);
+
+	/* Set sparc_cpu_model */
+	sparc_cpu_model = sun_unknown;
+	if(!strcmp(&cputypval,"sun4 ")) { sparc_cpu_model=sun4; }
+	if(!strcmp(&cputypval,"sun4c")) { sparc_cpu_model=sun4c; }
+	if(!strcmp(&cputypval,"sun4m")) { sparc_cpu_model=sun4m; }
+	if(!strcmp(&cputypval,"sun4s")) { sparc_cpu_model=sun4m; }  /* CP-1200 with PROM 2.30 -E */
+	if(!strcmp(&cputypval,"sun4d")) { sparc_cpu_model=sun4d; }
+	if(!strcmp(&cputypval,"sun4e")) { sparc_cpu_model=sun4e; }
+	if(!strcmp(&cputypval,"sun4u")) { sparc_cpu_model=sun4u; }
+
+#ifdef CONFIG_SUN4
+	if (sparc_cpu_model != sun4) {
+		prom_printf("This kernel is for Sun4 architecture only.\n");
+		prom_halt();
+	}
+#endif
+	printk("ARCH: ");
+	switch(sparc_cpu_model) {
+	case sun4:
+		printk("SUN4\n");
+		break;
+	case sun4c:
+		printk("SUN4C\n");
+		break;
+	case sun4m:
+		printk("SUN4M\n");
+		break;
+	case sun4d:
+		printk("SUN4D\n");
+		break;
+	case sun4e:
+		printk("SUN4E\n");
+		break;
+	case sun4u:
+		printk("SUN4U\n");
+		break;
+	default:
+		printk("UNKNOWN!\n");
+		break;
+	};
+
+#ifdef CONFIG_DUMMY_CONSOLE
+	conswitchp = &dummy_con;
+#elif defined(CONFIG_PROM_CONSOLE)
+	conswitchp = &prom_con;
+#endif
+	boot_flags_init(*cmdline_p);
+
+	idprom_init();
+	if (ARCH_SUN4C_SUN4)
+		sun4c_probe_vac();
+	load_mmu();
+	(void) prom_probe_memory();
+
+	phys_base = 0xffffffffUL;
+	highest_paddr = 0UL;
+	for (i = 0; sp_banks[i].num_bytes != 0; i++) {
+		unsigned long top;
+
+		if (sp_banks[i].base_addr < phys_base)
+			phys_base = sp_banks[i].base_addr;
+		top = sp_banks[i].base_addr +
+			sp_banks[i].num_bytes;
+		if (highest_paddr < top)
+			highest_paddr = top;
+	}
+	pfn_base = phys_base >> PAGE_SHIFT;
+
+	if (!root_flags)
+		root_mountflags &= ~MS_RDONLY;
+	ROOT_DEV = old_decode_dev(root_dev);
+#ifdef CONFIG_BLK_DEV_INITRD
+	rd_image_start = ram_flags & RAMDISK_IMAGE_START_MASK;
+	rd_prompt = ((ram_flags & RAMDISK_PROMPT_FLAG) != 0);
+	rd_doload = ((ram_flags & RAMDISK_LOAD_FLAG) != 0);	
+#endif
+
+	prom_setsync(prom_sync_me);
+
+	if((boot_flags&BOOTME_DEBUG) && (linux_dbvec!=0) && 
+	   ((*(short *)linux_dbvec) != -1)) {
+		printk("Booted under KADB. Syncing trap table.\n");
+		(*(linux_dbvec->teach_debugger))();
+	}
+
+	init_mm.context = (unsigned long) NO_CONTEXT;
+	init_task.thread.kregs = &fake_swapper_regs;
+
+	paging_init();
+}
+
+static int __init set_preferred_console(void)
+{
+	int idev, odev;
+
+	/* The user has requested a console so this is already set up. */
+	if (serial_console >= 0)
+		return -EBUSY;
+
+	idev = prom_query_input_device();
+	odev = prom_query_output_device();
+	if (idev == PROMDEV_IKBD && odev == PROMDEV_OSCREEN) {
+		serial_console = 0;
+	} else if (idev == PROMDEV_ITTYA && odev == PROMDEV_OTTYA) {
+		serial_console = 1;
+	} else if (idev == PROMDEV_ITTYB && odev == PROMDEV_OTTYB) {
+		serial_console = 2;
+	} else if (idev == PROMDEV_I_UNK && odev == PROMDEV_OTTYA) {
+		prom_printf("MrCoffee ttya\n");
+		serial_console = 1;
+	} else if (idev == PROMDEV_I_UNK && odev == PROMDEV_OSCREEN) {
+		serial_console = 0;
+		prom_printf("MrCoffee keyboard\n");
+	} else {
+		prom_printf("Confusing console (idev %d, odev %d)\n",
+		    idev, odev);
+		serial_console = 1;
+	}
+
+	if (serial_console)
+		return add_preferred_console("ttyS", serial_console - 1, NULL);
+
+	return -ENODEV;
+}
+console_initcall(set_preferred_console);
+
+extern char *sparc_cpu_type;
+extern char *sparc_fpu_type;
+
+static int show_cpuinfo(struct seq_file *m, void *__unused)
+{
+	seq_printf(m,
+		   "cpu\t\t: %s\n"
+		   "fpu\t\t: %s\n"
+		   "promlib\t\t: Version %d Revision %d\n"
+		   "prom\t\t: %d.%d\n"
+		   "type\t\t: %s\n"
+		   "ncpus probed\t: %d\n"
+		   "ncpus active\t: %d\n"
+#ifndef CONFIG_SMP
+		   "CPU0Bogo\t: %lu.%02lu\n"
+		   "CPU0ClkTck\t: %ld\n"
+#endif
+		   ,
+		   sparc_cpu_type ? sparc_cpu_type : "undetermined",
+		   sparc_fpu_type ? sparc_fpu_type : "undetermined",
+		   romvec->pv_romvers,
+		   prom_rev,
+		   romvec->pv_printrev >> 16,
+		   romvec->pv_printrev & 0xffff,
+		   &cputypval,
+		   num_possible_cpus(),
+		   num_online_cpus()
+#ifndef CONFIG_SMP
+		   , cpu_data(0).udelay_val/(500000/HZ),
+		   (cpu_data(0).udelay_val/(5000/HZ)) % 100,
+		   cpu_data(0).clock_tick
+#endif
+		);
+
+#ifdef CONFIG_SMP
+	smp_bogo(m);
+#endif
+	mmu_info(m);
+#ifdef CONFIG_SMP
+	smp_info(m);
+#endif
+	return 0;
+}
+
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+	/* The pointer we are returning is arbitrary,
+	 * it just has to be non-NULL and not IS_ERR
+	 * in the success case.
+	 */
+	return *pos == 0 ? &c_start : 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,
+};
+
+extern int stop_a_enabled;
+
+void sun_do_break(void)
+{
+	if (!stop_a_enabled)
+		return;
+
+	printk("\n");
+	flush_user_windows();
+
+	prom_cmdline();
+}
+
+int serial_console = -1;
+int stop_a_enabled = 1;
diff --git a/arch/sparc/kernel/signal.c b/arch/sparc/kernel/signal.c
new file mode 100644
index 0000000..011ff35
--- /dev/null
+++ b/arch/sparc/kernel/signal.c
@@ -0,0 +1,1181 @@
+/*  $Id: signal.c,v 1.110 2002/02/08 03:57:14 davem Exp $
+ *  linux/arch/sparc/kernel/signal.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *  Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ *  Copyright (C) 1997 Eddie C. Dost   (ecd@skynet.be)
+ */
+
+#include <linux/config.h>
+#include <linux/sched.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/mm.h>
+#include <linux/tty.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/binfmts.h>	/* do_coredum */
+#include <linux/bitops.h>
+
+#include <asm/uaccess.h>
+#include <asm/ptrace.h>
+#include <asm/svr4.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/cacheflush.h>	/* flush_sig_insns */
+
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+
+extern void fpsave(unsigned long *fpregs, unsigned long *fsr,
+		   void *fpqueue, unsigned long *fpqdepth);
+extern void fpload(unsigned long *fpregs, unsigned long *fsr);
+
+asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs,
+			 unsigned long orig_o0, int restart_syscall);
+
+/* Signal frames: the original one (compatible with SunOS):
+ *
+ * Set up a signal frame... Make the stack look the way SunOS
+ * expects it to look which is basically:
+ *
+ * ---------------------------------- <-- %sp at signal time
+ * Struct sigcontext
+ * Signal address
+ * Ptr to sigcontext area above
+ * Signal code
+ * The signal number itself
+ * One register window
+ * ---------------------------------- <-- New %sp
+ */
+struct signal_sframe {
+	struct reg_window	sig_window;
+	int			sig_num;
+	int			sig_code;
+	struct sigcontext __user *sig_scptr;
+	int			sig_address;
+	struct sigcontext	sig_context;
+	unsigned int		extramask[_NSIG_WORDS - 1];
+};
+
+/* 
+ * And the new one, intended to be used for Linux applications only
+ * (we have enough in there to work with clone).
+ * All the interesting bits are in the info field.
+ */
+
+struct new_signal_frame {
+	struct sparc_stackf	ss;
+	__siginfo_t		info;
+	__siginfo_fpu_t __user	*fpu_save;
+	unsigned long		insns[2] __attribute__ ((aligned (8)));
+	unsigned int		extramask[_NSIG_WORDS - 1];
+	unsigned int		extra_size; /* Should be 0 */
+	__siginfo_fpu_t		fpu_state;
+};
+
+struct rt_signal_frame {
+	struct sparc_stackf	ss;
+	siginfo_t		info;
+	struct pt_regs		regs;
+	sigset_t		mask;
+	__siginfo_fpu_t __user	*fpu_save;
+	unsigned int		insns[2];
+	stack_t			stack;
+	unsigned int		extra_size; /* Should be 0 */
+	__siginfo_fpu_t		fpu_state;
+};
+
+/* Align macros */
+#define SF_ALIGNEDSZ  (((sizeof(struct signal_sframe) + 7) & (~7)))
+#define NF_ALIGNEDSZ  (((sizeof(struct new_signal_frame) + 7) & (~7)))
+#define RT_ALIGNEDSZ  (((sizeof(struct rt_signal_frame) + 7) & (~7)))
+
+/*
+ * atomically swap in the new signal mask, and wait for a signal.
+ * This is really tricky on the Sparc, watch out...
+ */
+asmlinkage void _sigpause_common(old_sigset_t set, struct pt_regs *regs)
+{
+	sigset_t saveset;
+
+	set &= _BLOCKABLE;
+	spin_lock_irq(&current->sighand->siglock);
+	saveset = current->blocked;
+	siginitset(&current->blocked, set);
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+
+	regs->pc = regs->npc;
+	regs->npc += 4;
+
+	/* Condition codes and return value where set here for sigpause,
+	 * and so got used by setup_frame, which again causes sigreturn()
+	 * to return -EINTR.
+	 */
+	while (1) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule();
+		/*
+		 * Return -EINTR and set condition code here,
+		 * so the interrupted system call actually returns
+		 * these.
+		 */
+		regs->psr |= PSR_C;
+		regs->u_regs[UREG_I0] = EINTR;
+		if (do_signal(&saveset, regs, 0, 0))
+			return;
+	}
+}
+
+asmlinkage void do_sigpause(unsigned int set, struct pt_regs *regs)
+{
+	_sigpause_common(set, regs);
+}
+
+asmlinkage void do_sigsuspend (struct pt_regs *regs)
+{
+	_sigpause_common(regs->u_regs[UREG_I0], regs);
+}
+
+asmlinkage void do_rt_sigsuspend(sigset_t __user *uset, size_t sigsetsize,
+				 struct pt_regs *regs)
+{
+	sigset_t oldset, set;
+
+	/* XXX: Don't preclude handling different sized sigset_t's.  */
+	if (sigsetsize != sizeof(sigset_t)) {
+		regs->psr |= PSR_C;
+		regs->u_regs[UREG_I0] = EINVAL;
+		return;
+	}
+
+	if (copy_from_user(&set, uset, sizeof(set))) {
+		regs->psr |= PSR_C;
+		regs->u_regs[UREG_I0] = EFAULT;
+		return;
+	}
+
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sighand->siglock);
+	oldset = current->blocked;
+	current->blocked = set;
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+
+	regs->pc = regs->npc;
+	regs->npc += 4;
+
+	/* Condition codes and return value where set here for sigpause,
+	 * and so got used by setup_frame, which again causes sigreturn()
+	 * to return -EINTR.
+	 */
+	while (1) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule();
+		/*
+		 * Return -EINTR and set condition code here,
+		 * so the interrupted system call actually returns
+		 * these.
+		 */
+		regs->psr |= PSR_C;
+		regs->u_regs[UREG_I0] = EINTR;
+		if (do_signal(&oldset, regs, 0, 0))
+			return;
+	}
+}
+
+static inline int
+restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu)
+{
+	int err;
+#ifdef CONFIG_SMP
+	if (test_tsk_thread_flag(current, TIF_USEDFPU))
+		regs->psr &= ~PSR_EF;
+#else
+	if (current == last_task_used_math) {
+		last_task_used_math = NULL;
+		regs->psr &= ~PSR_EF;
+	}
+#endif
+	set_used_math();
+	clear_tsk_thread_flag(current, TIF_USEDFPU);
+
+	if (!access_ok(VERIFY_READ, fpu, sizeof(*fpu)))
+		return -EFAULT;
+
+	err = __copy_from_user(&current->thread.float_regs[0], &fpu->si_float_regs[0],
+			       (sizeof(unsigned long) * 32));
+	err |= __get_user(current->thread.fsr, &fpu->si_fsr);
+	err |= __get_user(current->thread.fpqdepth, &fpu->si_fpqdepth);
+	if (current->thread.fpqdepth != 0)
+		err |= __copy_from_user(&current->thread.fpqueue[0],
+					&fpu->si_fpqueue[0],
+					((sizeof(unsigned long) +
+					(sizeof(unsigned long *)))*16));
+	return err;
+}
+
+static inline void do_new_sigreturn (struct pt_regs *regs)
+{
+	struct new_signal_frame __user *sf;
+	unsigned long up_psr, pc, npc;
+	sigset_t set;
+	__siginfo_fpu_t __user *fpu_save;
+	int err;
+
+	sf = (struct new_signal_frame __user *) regs->u_regs[UREG_FP];
+
+	/* 1. Make sure we are not getting garbage from the user */
+	if (!access_ok(VERIFY_READ, sf, sizeof(*sf)))
+		goto segv_and_exit;
+
+	if (((unsigned long) sf) & 3)
+		goto segv_and_exit;
+
+	err = __get_user(pc,  &sf->info.si_regs.pc);
+	err |= __get_user(npc, &sf->info.si_regs.npc);
+
+	if ((pc | npc) & 3)
+		goto segv_and_exit;
+
+	/* 2. Restore the state */
+	up_psr = regs->psr;
+	err |= __copy_from_user(regs, &sf->info.si_regs, sizeof(struct pt_regs));
+
+	/* User can only change condition codes and FPU enabling in %psr. */
+	regs->psr = (up_psr & ~(PSR_ICC | PSR_EF))
+		  | (regs->psr & (PSR_ICC | PSR_EF));
+
+	err |= __get_user(fpu_save, &sf->fpu_save);
+
+	if (fpu_save)
+		err |= restore_fpu_state(regs, fpu_save);
+
+	/* This is pretty much atomic, no amount locking would prevent
+	 * the races which exist anyways.
+	 */
+	err |= __get_user(set.sig[0], &sf->info.si_mask);
+	err |= __copy_from_user(&set.sig[1], &sf->extramask,
+			        (_NSIG_WORDS-1) * sizeof(unsigned int));
+			   
+	if (err)
+		goto segv_and_exit;
+
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sighand->siglock);
+	current->blocked = set;
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+	return;
+
+segv_and_exit:
+	force_sig(SIGSEGV, current);
+}
+
+asmlinkage void do_sigreturn(struct pt_regs *regs)
+{
+	struct sigcontext __user *scptr;
+	unsigned long pc, npc, psr;
+	sigset_t set;
+	int err;
+
+	/* Always make any pending restarted system calls return -EINTR */
+	current_thread_info()->restart_block.fn = do_no_restart_syscall;
+
+	synchronize_user_stack();
+
+	if (current->thread.new_signal) {
+		do_new_sigreturn(regs);
+		return;
+	}
+
+	scptr = (struct sigcontext __user *) regs->u_regs[UREG_I0];
+
+	/* Check sanity of the user arg. */
+	if (!access_ok(VERIFY_READ, scptr, sizeof(struct sigcontext)) ||
+	    (((unsigned long) scptr) & 3))
+		goto segv_and_exit;
+
+	err = __get_user(pc, &scptr->sigc_pc);
+	err |= __get_user(npc, &scptr->sigc_npc);
+
+	if ((pc | npc) & 3)
+		goto segv_and_exit;
+
+	/* This is pretty much atomic, no amount locking would prevent
+	 * the races which exist anyways.
+	 */
+	err |= __get_user(set.sig[0], &scptr->sigc_mask);
+	/* Note that scptr + 1 points to extramask */
+	err |= __copy_from_user(&set.sig[1], scptr + 1,
+				(_NSIG_WORDS - 1) * sizeof(unsigned int));
+	
+	if (err)
+		goto segv_and_exit;
+
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sighand->siglock);
+	current->blocked = set;
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+
+	regs->pc = pc;
+	regs->npc = npc;
+
+	err = __get_user(regs->u_regs[UREG_FP], &scptr->sigc_sp);
+	err |= __get_user(regs->u_regs[UREG_I0], &scptr->sigc_o0);
+	err |= __get_user(regs->u_regs[UREG_G1], &scptr->sigc_g1);
+
+	/* User can only change condition codes in %psr. */
+	err |= __get_user(psr, &scptr->sigc_psr);
+	if (err)
+		goto segv_and_exit;
+		
+	regs->psr &= ~(PSR_ICC);
+	regs->psr |= (psr & PSR_ICC);
+	return;
+
+segv_and_exit:
+	force_sig(SIGSEGV, current);
+}
+
+asmlinkage void do_rt_sigreturn(struct pt_regs *regs)
+{
+	struct rt_signal_frame __user *sf;
+	unsigned int psr, pc, npc;
+	__siginfo_fpu_t __user *fpu_save;
+	mm_segment_t old_fs;
+	sigset_t set;
+	stack_t st;
+	int err;
+
+	synchronize_user_stack();
+	sf = (struct rt_signal_frame __user *) regs->u_regs[UREG_FP];
+	if (!access_ok(VERIFY_READ, sf, sizeof(*sf)) ||
+	    (((unsigned long) sf) & 0x03))
+		goto segv;
+
+	err = __get_user(pc, &sf->regs.pc);
+	err |= __get_user(npc, &sf->regs.npc);
+	err |= ((pc | npc) & 0x03);
+
+	err |= __get_user(regs->y, &sf->regs.y);
+	err |= __get_user(psr, &sf->regs.psr);
+
+	err |= __copy_from_user(&regs->u_regs[UREG_G1],
+				&sf->regs.u_regs[UREG_G1], 15 * sizeof(u32));
+
+	regs->psr = (regs->psr & ~PSR_ICC) | (psr & PSR_ICC);
+
+	err |= __get_user(fpu_save, &sf->fpu_save);
+
+	if (fpu_save)
+		err |= restore_fpu_state(regs, fpu_save);
+	err |= __copy_from_user(&set, &sf->mask, sizeof(sigset_t));
+	
+	err |= __copy_from_user(&st, &sf->stack, sizeof(stack_t));
+	
+	if (err)
+		goto segv;
+		
+	regs->pc = pc;
+	regs->npc = npc;
+	
+	/* It is more difficult to avoid calling this function than to
+	 * call it and ignore errors.
+	 */
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	do_sigaltstack((const stack_t __user *) &st, NULL, (unsigned long)sf);
+	set_fs(old_fs);
+
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sighand->siglock);
+	current->blocked = set;
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+	return;
+segv:
+	force_sig(SIGSEGV, current);
+}
+
+/* Checks if the fp is valid */
+static inline int invalid_frame_pointer(void __user *fp, int fplen)
+{
+	if ((((unsigned long) fp) & 7) ||
+	    !__access_ok((unsigned long)fp, fplen) ||
+	    ((sparc_cpu_model == sun4 || sparc_cpu_model == sun4c) &&
+	     ((unsigned long) fp < 0xe0000000 && (unsigned long) fp >= 0x20000000)))
+		return 1;
+	
+	return 0;
+}
+
+static inline void __user *get_sigframe(struct sigaction *sa, struct pt_regs *regs, unsigned long framesize)
+{
+	unsigned long sp;
+
+	sp = regs->u_regs[UREG_FP];
+
+	/* This is the X/Open sanctioned signal stack switching.  */
+	if (sa->sa_flags & SA_ONSTACK) {
+		if (!on_sig_stack(sp) && !((current->sas_ss_sp + current->sas_ss_size) & 7))
+			sp = current->sas_ss_sp + current->sas_ss_size;
+	}
+	return (void __user *)(sp - framesize);
+}
+
+static inline void
+setup_frame(struct sigaction *sa, struct pt_regs *regs, int signr, sigset_t *oldset, siginfo_t *info)
+{
+	struct signal_sframe __user *sframep;
+	struct sigcontext __user *sc;
+	int window = 0, err;
+	unsigned long pc = regs->pc;
+	unsigned long npc = regs->npc;
+	struct thread_info *tp = current_thread_info();
+	void __user *sig_address;
+	int sig_code;
+
+	synchronize_user_stack();
+	sframep = (struct signal_sframe __user *)
+		get_sigframe(sa, regs, SF_ALIGNEDSZ);
+	if (invalid_frame_pointer(sframep, sizeof(*sframep))){
+		/* Don't change signal code and address, so that
+		 * post mortem debuggers can have a look.
+		 */
+		goto sigill_and_return;
+	}
+
+	sc = &sframep->sig_context;
+
+	/* We've already made sure frame pointer isn't in kernel space... */
+	err  = __put_user((sas_ss_flags(regs->u_regs[UREG_FP]) == SS_ONSTACK),
+			 &sc->sigc_onstack);
+	err |= __put_user(oldset->sig[0], &sc->sigc_mask);
+	err |= __copy_to_user(sframep->extramask, &oldset->sig[1],
+			      (_NSIG_WORDS - 1) * sizeof(unsigned int));
+	err |= __put_user(regs->u_regs[UREG_FP], &sc->sigc_sp);
+	err |= __put_user(pc, &sc->sigc_pc);
+	err |= __put_user(npc, &sc->sigc_npc);
+	err |= __put_user(regs->psr, &sc->sigc_psr);
+	err |= __put_user(regs->u_regs[UREG_G1], &sc->sigc_g1);
+	err |= __put_user(regs->u_regs[UREG_I0], &sc->sigc_o0);
+	err |= __put_user(tp->w_saved, &sc->sigc_oswins);
+	if (tp->w_saved)
+		for (window = 0; window < tp->w_saved; window++) {
+			put_user((char *)tp->rwbuf_stkptrs[window],
+				 &sc->sigc_spbuf[window]);
+			err |= __copy_to_user(&sc->sigc_wbuf[window],
+					      &tp->reg_window[window],
+					      sizeof(struct reg_window));
+		}
+	else
+		err |= __copy_to_user(sframep, (char *) regs->u_regs[UREG_FP],
+				      sizeof(struct reg_window));
+
+	tp->w_saved = 0; /* So process is allowed to execute. */
+
+	err |= __put_user(signr, &sframep->sig_num);
+	sig_address = NULL;
+	sig_code = 0;
+	if (SI_FROMKERNEL (info) && (info->si_code & __SI_MASK) == __SI_FAULT) {
+		sig_address = info->si_addr;
+		switch (signr) {
+		case SIGSEGV:
+			switch (info->si_code) {
+			case SEGV_MAPERR: sig_code = SUBSIG_NOMAPPING; break;
+			default: sig_code = SUBSIG_PROTECTION; break;
+			}
+			break;
+		case SIGILL:
+			switch (info->si_code) {
+			case ILL_ILLOPC: sig_code = SUBSIG_ILLINST; break;
+			case ILL_PRVOPC: sig_code = SUBSIG_PRIVINST; break;
+			case ILL_ILLTRP: sig_code = SUBSIG_BADTRAP(info->si_trapno); break;
+			default: sig_code = SUBSIG_STACK; break;
+			}
+			break;
+		case SIGFPE:
+			switch (info->si_code) {
+			case FPE_INTDIV: sig_code = SUBSIG_IDIVZERO; break;
+			case FPE_INTOVF: sig_code = SUBSIG_FPINTOVFL; break;
+			case FPE_FLTDIV: sig_code = SUBSIG_FPDIVZERO; break;
+			case FPE_FLTOVF: sig_code = SUBSIG_FPOVFLOW; break;
+			case FPE_FLTUND: sig_code = SUBSIG_FPUNFLOW; break;
+			case FPE_FLTRES: sig_code = SUBSIG_FPINEXACT; break;
+			case FPE_FLTINV: sig_code = SUBSIG_FPOPERROR; break;
+			default: sig_code = SUBSIG_FPERROR; break;
+			}
+			break;
+		case SIGBUS:
+			switch (info->si_code) {
+			case BUS_ADRALN: sig_code = SUBSIG_ALIGNMENT; break;
+			case BUS_ADRERR: sig_code = SUBSIG_MISCERROR; break;
+			default: sig_code = SUBSIG_BUSTIMEOUT; break;
+			}
+			break;
+		case SIGEMT:
+			switch (info->si_code) {
+			case EMT_TAGOVF: sig_code = SUBSIG_TAG; break;
+			}
+			break;
+		case SIGSYS:
+			if (info->si_code == (__SI_FAULT|0x100)) {
+				/* See sys_sunos.c */
+				sig_code = info->si_trapno;
+				break;
+			}
+		default:
+			sig_address = NULL;
+		}
+	}
+	err |= __put_user((unsigned long)sig_address, &sframep->sig_address);
+	err |= __put_user(sig_code, &sframep->sig_code);
+	err |= __put_user(sc, &sframep->sig_scptr);
+	if (err)
+		goto sigsegv;
+
+	regs->u_regs[UREG_FP] = (unsigned long) sframep;
+	regs->pc = (unsigned long) sa->sa_handler;
+	regs->npc = (regs->pc + 4);
+	return;
+
+sigill_and_return:
+	do_exit(SIGILL);
+sigsegv:
+	force_sigsegv(signr, current);
+}
+
+
+static inline int
+save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu)
+{
+	int err = 0;
+#ifdef CONFIG_SMP
+	if (test_tsk_thread_flag(current, TIF_USEDFPU)) {
+		put_psr(get_psr() | PSR_EF);
+		fpsave(&current->thread.float_regs[0], &current->thread.fsr,
+		       &current->thread.fpqueue[0], &current->thread.fpqdepth);
+		regs->psr &= ~(PSR_EF);
+		clear_tsk_thread_flag(current, TIF_USEDFPU);
+	}
+#else
+	if (current == last_task_used_math) {
+		put_psr(get_psr() | PSR_EF);
+		fpsave(&current->thread.float_regs[0], &current->thread.fsr,
+		       &current->thread.fpqueue[0], &current->thread.fpqdepth);
+		last_task_used_math = NULL;
+		regs->psr &= ~(PSR_EF);
+	}
+#endif
+	err |= __copy_to_user(&fpu->si_float_regs[0],
+			      &current->thread.float_regs[0],
+			      (sizeof(unsigned long) * 32));
+	err |= __put_user(current->thread.fsr, &fpu->si_fsr);
+	err |= __put_user(current->thread.fpqdepth, &fpu->si_fpqdepth);
+	if (current->thread.fpqdepth != 0)
+		err |= __copy_to_user(&fpu->si_fpqueue[0],
+				      &current->thread.fpqueue[0],
+				      ((sizeof(unsigned long) +
+				      (sizeof(unsigned long *)))*16));
+	clear_used_math();
+	return err;
+}
+
+static inline void
+new_setup_frame(struct k_sigaction *ka, struct pt_regs *regs,
+		int signo, sigset_t *oldset)
+{
+	struct new_signal_frame __user *sf;
+	int sigframe_size, err;
+
+	/* 1. Make sure everything is clean */
+	synchronize_user_stack();
+
+	sigframe_size = NF_ALIGNEDSZ;
+	if (!used_math())
+		sigframe_size -= sizeof(__siginfo_fpu_t);
+
+	sf = (struct new_signal_frame __user *)
+		get_sigframe(&ka->sa, regs, sigframe_size);
+
+	if (invalid_frame_pointer(sf, sigframe_size))
+		goto sigill_and_return;
+
+	if (current_thread_info()->w_saved != 0)
+		goto sigill_and_return;
+
+	/* 2. Save the current process state */
+	err = __copy_to_user(&sf->info.si_regs, regs, sizeof(struct pt_regs));
+	
+	err |= __put_user(0, &sf->extra_size);
+
+	if (used_math()) {
+		err |= save_fpu_state(regs, &sf->fpu_state);
+		err |= __put_user(&sf->fpu_state, &sf->fpu_save);
+	} else {
+		err |= __put_user(0, &sf->fpu_save);
+	}
+
+	err |= __put_user(oldset->sig[0], &sf->info.si_mask);
+	err |= __copy_to_user(sf->extramask, &oldset->sig[1],
+			      (_NSIG_WORDS - 1) * sizeof(unsigned int));
+	err |= __copy_to_user(sf, (char *) regs->u_regs[UREG_FP],
+			      sizeof(struct reg_window));
+	if (err)
+		goto sigsegv;
+	
+	/* 3. signal handler back-trampoline and parameters */
+	regs->u_regs[UREG_FP] = (unsigned long) sf;
+	regs->u_regs[UREG_I0] = signo;
+	regs->u_regs[UREG_I1] = (unsigned long) &sf->info;
+	regs->u_regs[UREG_I2] = (unsigned long) &sf->info;
+
+	/* 4. signal handler */
+	regs->pc = (unsigned long) ka->sa.sa_handler;
+	regs->npc = (regs->pc + 4);
+
+	/* 5. return to kernel instructions */
+	if (ka->ka_restorer)
+		regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer;
+	else {
+		regs->u_regs[UREG_I7] = (unsigned long)(&(sf->insns[0]) - 2);
+
+		/* mov __NR_sigreturn, %g1 */
+		err |= __put_user(0x821020d8, &sf->insns[0]);
+
+		/* t 0x10 */
+		err |= __put_user(0x91d02010, &sf->insns[1]);
+		if (err)
+			goto sigsegv;
+
+		/* Flush instruction space. */
+		flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0]));
+	}
+	return;
+
+sigill_and_return:
+	do_exit(SIGILL);
+sigsegv:
+	force_sigsegv(signo, current);
+}
+
+static inline void
+new_setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs,
+		   int signo, sigset_t *oldset, siginfo_t *info)
+{
+	struct rt_signal_frame __user *sf;
+	int sigframe_size;
+	unsigned int psr;
+	int err;
+
+	synchronize_user_stack();
+	sigframe_size = RT_ALIGNEDSZ;
+	if (!used_math())
+		sigframe_size -= sizeof(__siginfo_fpu_t);
+	sf = (struct rt_signal_frame __user *)
+		get_sigframe(&ka->sa, regs, sigframe_size);
+	if (invalid_frame_pointer(sf, sigframe_size))
+		goto sigill;
+	if (current_thread_info()->w_saved != 0)
+		goto sigill;
+
+	err  = __put_user(regs->pc, &sf->regs.pc);
+	err |= __put_user(regs->npc, &sf->regs.npc);
+	err |= __put_user(regs->y, &sf->regs.y);
+	psr = regs->psr;
+	if (used_math())
+		psr |= PSR_EF;
+	err |= __put_user(psr, &sf->regs.psr);
+	err |= __copy_to_user(&sf->regs.u_regs, regs->u_regs, sizeof(regs->u_regs));
+	err |= __put_user(0, &sf->extra_size);
+
+	if (psr & PSR_EF) {
+		err |= save_fpu_state(regs, &sf->fpu_state);
+		err |= __put_user(&sf->fpu_state, &sf->fpu_save);
+	} else {
+		err |= __put_user(0, &sf->fpu_save);
+	}
+	err |= __copy_to_user(&sf->mask, &oldset->sig[0], sizeof(sigset_t));
+	
+	/* Setup sigaltstack */
+	err |= __put_user(current->sas_ss_sp, &sf->stack.ss_sp);
+	err |= __put_user(sas_ss_flags(regs->u_regs[UREG_FP]), &sf->stack.ss_flags);
+	err |= __put_user(current->sas_ss_size, &sf->stack.ss_size);
+	
+	err |= __copy_to_user(sf, (char *) regs->u_regs[UREG_FP],
+			      sizeof(struct reg_window));	
+
+	err |= copy_siginfo_to_user(&sf->info, info);
+
+	if (err)
+		goto sigsegv;
+
+	regs->u_regs[UREG_FP] = (unsigned long) sf;
+	regs->u_regs[UREG_I0] = signo;
+	regs->u_regs[UREG_I1] = (unsigned long) &sf->info;
+	regs->u_regs[UREG_I2] = (unsigned long) &sf->regs;
+
+	regs->pc = (unsigned long) ka->sa.sa_handler;
+	regs->npc = (regs->pc + 4);
+
+	if (ka->ka_restorer)
+		regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer;
+	else {
+		regs->u_regs[UREG_I7] = (unsigned long)(&(sf->insns[0]) - 2);
+
+		/* mov __NR_sigreturn, %g1 */
+		err |= __put_user(0x821020d8, &sf->insns[0]);
+
+		/* t 0x10 */
+		err |= __put_user(0x91d02010, &sf->insns[1]);
+		if (err)
+			goto sigsegv;
+
+		/* Flush instruction space. */
+		flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0]));
+	}
+	return;
+
+sigill:
+	do_exit(SIGILL);
+sigsegv:
+	force_sigsegv(signo, current);
+}
+
+/* Setup a Solaris stack frame */
+static inline void
+setup_svr4_frame(struct sigaction *sa, unsigned long pc, unsigned long npc,
+		 struct pt_regs *regs, int signr, sigset_t *oldset)
+{
+	svr4_signal_frame_t __user *sfp;
+	svr4_gregset_t  __user *gr;
+	svr4_siginfo_t  __user *si;
+	svr4_mcontext_t __user *mc;
+	svr4_gwindows_t __user *gw;
+	svr4_ucontext_t __user *uc;
+	svr4_sigset_t	setv;
+	struct thread_info *tp = current_thread_info();
+	int window = 0, err;
+
+	synchronize_user_stack();
+	sfp = (svr4_signal_frame_t __user *)
+		get_sigframe(sa, regs, SVR4_SF_ALIGNED + sizeof(struct reg_window));
+
+	if (invalid_frame_pointer(sfp, sizeof(*sfp)))
+		goto sigill_and_return;
+
+	/* Start with a clean frame pointer and fill it */
+	err = __clear_user(sfp, sizeof(*sfp));
+
+	/* Setup convenience variables */
+	si = &sfp->si;
+	uc = &sfp->uc;
+	gw = &sfp->gw;
+	mc = &uc->mcontext;
+	gr = &mc->greg;
+	
+	/* FIXME: where am I supposed to put this?
+	 * sc->sigc_onstack = old_status;
+	 * anyways, it does not look like it is used for anything at all.
+	 */
+	setv.sigbits[0] = oldset->sig[0];
+	setv.sigbits[1] = oldset->sig[1];
+	if (_NSIG_WORDS >= 4) {
+		setv.sigbits[2] = oldset->sig[2];
+		setv.sigbits[3] = oldset->sig[3];
+		err |= __copy_to_user(&uc->sigmask, &setv, sizeof(svr4_sigset_t));
+	} else
+		err |= __copy_to_user(&uc->sigmask, &setv,
+				      2 * sizeof(unsigned int));
+
+	/* Store registers */
+	err |= __put_user(regs->pc, &((*gr)[SVR4_PC]));
+	err |= __put_user(regs->npc, &((*gr)[SVR4_NPC]));
+	err |= __put_user(regs->psr, &((*gr)[SVR4_PSR]));
+	err |= __put_user(regs->y, &((*gr)[SVR4_Y]));
+	
+	/* Copy g[1..7] and o[0..7] registers */
+	err |= __copy_to_user(&(*gr)[SVR4_G1], &regs->u_regs[UREG_G1],
+			      sizeof(long) * 7);
+	err |= __copy_to_user(&(*gr)[SVR4_O0], &regs->u_regs[UREG_I0],
+			      sizeof(long) * 8);
+
+	/* Setup sigaltstack */
+	err |= __put_user(current->sas_ss_sp, &uc->stack.sp);
+	err |= __put_user(sas_ss_flags(regs->u_regs[UREG_FP]), &uc->stack.flags);
+	err |= __put_user(current->sas_ss_size, &uc->stack.size);
+
+	/* Save the currently window file: */
+
+	/* 1. Link sfp->uc->gwins to our windows */
+	err |= __put_user(gw, &mc->gwin);
+	    
+	/* 2. Number of windows to restore at setcontext(): */
+	err |= __put_user(tp->w_saved, &gw->count);
+
+	/* 3. Save each valid window
+	 *    Currently, it makes a copy of the windows from the kernel copy.
+	 *    David's code for SunOS, makes the copy but keeps the pointer to
+	 *    the kernel.  My version makes the pointer point to a userland 
+	 *    copy of those.  Mhm, I wonder if I shouldn't just ignore those
+	 *    on setcontext and use those that are on the kernel, the signal
+	 *    handler should not be modyfing those, mhm.
+	 *
+	 *    These windows are just used in case synchronize_user_stack failed
+	 *    to flush the user windows.
+	 */
+	for (window = 0; window < tp->w_saved; window++) {
+		err |= __put_user((int __user *) &(gw->win[window]), &gw->winptr[window]);
+		err |= __copy_to_user(&gw->win[window],
+				      &tp->reg_window[window],
+				      sizeof(svr4_rwindow_t));
+		err |= __put_user(0, gw->winptr[window]);
+	}
+
+	/* 4. We just pay attention to the gw->count field on setcontext */
+	tp->w_saved = 0; /* So process is allowed to execute. */
+
+	/* Setup the signal information.  Solaris expects a bunch of
+	 * information to be passed to the signal handler, we don't provide
+	 * that much currently, should use siginfo.
+	 */
+	err |= __put_user(signr, &si->siginfo.signo);
+	err |= __put_user(SVR4_SINOINFO, &si->siginfo.code);
+	if (err)
+		goto sigsegv;
+
+	regs->u_regs[UREG_FP] = (unsigned long) sfp;
+	regs->pc = (unsigned long) sa->sa_handler;
+	regs->npc = (regs->pc + 4);
+
+	/* Arguments passed to signal handler */
+	if (regs->u_regs[14]){
+		struct reg_window __user *rw = (struct reg_window __user *)
+			regs->u_regs[14];
+
+		err |= __put_user(signr, &rw->ins[0]);
+		err |= __put_user(si, &rw->ins[1]);
+		err |= __put_user(uc, &rw->ins[2]);
+		err |= __put_user(sfp, &rw->ins[6]);	/* frame pointer */
+		if (err)
+			goto sigsegv;
+
+		regs->u_regs[UREG_I0] = signr;
+		regs->u_regs[UREG_I1] = (unsigned long) si;
+		regs->u_regs[UREG_I2] = (unsigned long) uc;
+	}
+	return;
+
+sigill_and_return:
+	do_exit(SIGILL);
+sigsegv:
+	force_sigsegv(signr, current);
+}
+
+asmlinkage int svr4_getcontext(svr4_ucontext_t __user *uc, struct pt_regs *regs)
+{
+	svr4_gregset_t  __user *gr;
+	svr4_mcontext_t __user *mc;
+	svr4_sigset_t	setv;
+	int err = 0;
+
+	synchronize_user_stack();
+
+	if (current_thread_info()->w_saved)
+		return -EFAULT;
+
+	err = clear_user(uc, sizeof(*uc));
+	if (err)
+		return -EFAULT;
+
+	/* Setup convenience variables */
+	mc = &uc->mcontext;
+	gr = &mc->greg;
+
+	setv.sigbits[0] = current->blocked.sig[0];
+	setv.sigbits[1] = current->blocked.sig[1];
+	if (_NSIG_WORDS >= 4) {
+		setv.sigbits[2] = current->blocked.sig[2];
+		setv.sigbits[3] = current->blocked.sig[3];
+		err |= __copy_to_user(&uc->sigmask, &setv, sizeof(svr4_sigset_t));
+	} else
+		err |= __copy_to_user(&uc->sigmask, &setv,
+				      2 * sizeof(unsigned int));
+
+	/* Store registers */
+	err |= __put_user(regs->pc, &uc->mcontext.greg[SVR4_PC]);
+	err |= __put_user(regs->npc, &uc->mcontext.greg[SVR4_NPC]);
+	err |= __put_user(regs->psr, &uc->mcontext.greg[SVR4_PSR]);
+	err |= __put_user(regs->y, &uc->mcontext.greg[SVR4_Y]);
+	
+	/* Copy g[1..7] and o[0..7] registers */
+	err |= __copy_to_user(&(*gr)[SVR4_G1], &regs->u_regs[UREG_G1],
+			      sizeof(uint) * 7);
+	err |= __copy_to_user(&(*gr)[SVR4_O0], &regs->u_regs[UREG_I0],
+			      sizeof(uint) * 8);
+
+	/* Setup sigaltstack */
+	err |= __put_user(current->sas_ss_sp, &uc->stack.sp);
+	err |= __put_user(sas_ss_flags(regs->u_regs[UREG_FP]), &uc->stack.flags);
+	err |= __put_user(current->sas_ss_size, &uc->stack.size);
+
+	/* The register file is not saved
+	 * we have already stuffed all of it with sync_user_stack
+	 */
+	return (err ? -EFAULT : 0);
+}
+
+/* Set the context for a svr4 application, this is Solaris way to sigreturn */
+asmlinkage int svr4_setcontext(svr4_ucontext_t __user *c, struct pt_regs *regs)
+{
+	svr4_gregset_t  __user *gr;
+	unsigned long pc, npc, psr;
+	mm_segment_t old_fs;
+	sigset_t set;
+	svr4_sigset_t setv;
+	int err;
+	stack_t st;
+	
+	/* Fixme: restore windows, or is this already taken care of in
+	 * svr4_setup_frame when sync_user_windows is done?
+	 */
+	flush_user_windows();
+
+	if (current_thread_info()->w_saved)
+		goto sigsegv_and_return;
+
+	if (((unsigned long) c) & 3)
+		goto sigsegv_and_return;
+
+	if (!__access_ok((unsigned long)c, sizeof(*c)))
+		goto sigsegv_and_return;
+
+	/* Check for valid PC and nPC */
+	gr = &c->mcontext.greg;
+	err = __get_user(pc, &((*gr)[SVR4_PC]));
+	err |= __get_user(npc, &((*gr)[SVR4_NPC]));
+
+	if ((pc | npc) & 3)
+		goto sigsegv_and_return;
+
+	/* Retrieve information from passed ucontext */
+	/* note that nPC is ored a 1, this is used to inform entry.S */
+	/* that we don't want it to mess with our PC and nPC */
+
+	/* This is pretty much atomic, no amount locking would prevent
+	 * the races which exist anyways.
+	 */
+	err |= __copy_from_user(&setv, &c->sigmask, sizeof(svr4_sigset_t));
+	
+	err |= __get_user(st.ss_sp, &c->stack.sp);
+	err |= __get_user(st.ss_flags, &c->stack.flags);
+	err |= __get_user(st.ss_size, &c->stack.size);
+	
+	if (err)
+		goto sigsegv_and_return;
+		
+	/* It is more difficult to avoid calling this function than to
+	   call it and ignore errors.  */
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	do_sigaltstack((const stack_t __user *) &st, NULL,
+		       regs->u_regs[UREG_I6]);
+	set_fs(old_fs);
+	
+	set.sig[0] = setv.sigbits[0];
+	set.sig[1] = setv.sigbits[1];
+	if (_NSIG_WORDS >= 4) {
+		set.sig[2] = setv.sigbits[2];
+		set.sig[3] = setv.sigbits[3];
+	}
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sighand->siglock);
+	current->blocked = set;
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+	regs->pc = pc;
+	regs->npc = npc | 1;
+	err |= __get_user(regs->y, &((*gr)[SVR4_Y]));
+	err |= __get_user(psr, &((*gr)[SVR4_PSR]));
+	regs->psr &= ~(PSR_ICC);
+	regs->psr |= (psr & PSR_ICC);
+
+	/* Restore g[1..7] and o[0..7] registers */
+	err |= __copy_from_user(&regs->u_regs[UREG_G1], &(*gr)[SVR4_G1],
+			      sizeof(long) * 7);
+	err |= __copy_from_user(&regs->u_regs[UREG_I0], &(*gr)[SVR4_O0],
+			      sizeof(long) * 8);
+	return (err ? -EFAULT : 0);
+
+sigsegv_and_return:
+	force_sig(SIGSEGV, current);
+	return -EFAULT;
+}
+
+static inline void
+handle_signal(unsigned long signr, struct k_sigaction *ka,
+	      siginfo_t *info, sigset_t *oldset, struct pt_regs *regs,
+	      int svr4_signal)
+{
+	if (svr4_signal)
+		setup_svr4_frame(&ka->sa, regs->pc, regs->npc, regs, signr, oldset);
+	else {
+		if (ka->sa.sa_flags & SA_SIGINFO)
+			new_setup_rt_frame(ka, regs, signr, oldset, info);
+		else if (current->thread.new_signal)
+			new_setup_frame(ka, regs, signr, oldset);
+		else
+			setup_frame(&ka->sa, regs, signr, oldset, info);
+	}
+	if (!(ka->sa.sa_flags & SA_NOMASK)) {
+		spin_lock_irq(&current->sighand->siglock);
+		sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+		sigaddset(&current->blocked, signr);
+		recalc_sigpending();
+		spin_unlock_irq(&current->sighand->siglock);
+	}
+}
+
+static inline void syscall_restart(unsigned long orig_i0, struct pt_regs *regs,
+				   struct sigaction *sa)
+{
+	switch(regs->u_regs[UREG_I0]) {
+	case ERESTART_RESTARTBLOCK:
+	case ERESTARTNOHAND:
+	no_system_call_restart:
+		regs->u_regs[UREG_I0] = EINTR;
+		regs->psr |= PSR_C;
+		break;
+	case ERESTARTSYS:
+		if (!(sa->sa_flags & SA_RESTART))
+			goto no_system_call_restart;
+		/* fallthrough */
+	case ERESTARTNOINTR:
+		regs->u_regs[UREG_I0] = orig_i0;
+		regs->pc -= 4;
+		regs->npc -= 4;
+	}
+}
+
+/* Note that 'init' is a special process: it doesn't get signals it doesn't
+ * want to handle. Thus you cannot kill init even with a SIGKILL even by
+ * mistake.
+ */
+asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs,
+			 unsigned long orig_i0, int restart_syscall)
+{
+	siginfo_t info;
+	struct sparc_deliver_cookie cookie;
+	struct k_sigaction ka;
+	int signr;
+
+	/*
+	 * XXX Disable svr4 signal handling until solaris emulation works.
+	 * It is buggy - Anton
+	 */
+#define SVR4_SIGNAL_BROKEN 1
+#ifdef SVR4_SIGNAL_BROKEN
+	int svr4_signal = 0;
+#else
+	int svr4_signal = current->personality == PER_SVR4;
+#endif
+
+	cookie.restart_syscall = restart_syscall;
+	cookie.orig_i0 = orig_i0;
+
+	if (!oldset)
+		oldset = &current->blocked;
+
+	signr = get_signal_to_deliver(&info, &ka, regs, &cookie);
+	if (signr > 0) {
+		if (cookie.restart_syscall)
+			syscall_restart(cookie.orig_i0, regs, &ka.sa);
+		handle_signal(signr, &ka, &info, oldset,
+			      regs, svr4_signal);
+		return 1;
+	}
+	if (cookie.restart_syscall &&
+	    (regs->u_regs[UREG_I0] == ERESTARTNOHAND ||
+	     regs->u_regs[UREG_I0] == ERESTARTSYS ||
+	     regs->u_regs[UREG_I0] == ERESTARTNOINTR)) {
+		/* replay the system call when we are done */
+		regs->u_regs[UREG_I0] = cookie.orig_i0;
+		regs->pc -= 4;
+		regs->npc -= 4;
+	}
+	if (cookie.restart_syscall &&
+	    regs->u_regs[UREG_I0] == ERESTART_RESTARTBLOCK) {
+		regs->u_regs[UREG_G1] = __NR_restart_syscall;
+		regs->pc -= 4;
+		regs->npc -= 4;
+	}
+	return 0;
+}
+
+asmlinkage int
+do_sys_sigstack(struct sigstack __user *ssptr, struct sigstack __user *ossptr,
+		unsigned long sp)
+{
+	int ret = -EFAULT;
+
+	/* First see if old state is wanted. */
+	if (ossptr) {
+		if (put_user(current->sas_ss_sp + current->sas_ss_size,
+			     &ossptr->the_stack) ||
+		    __put_user(on_sig_stack(sp), &ossptr->cur_status))
+			goto out;
+	}
+
+	/* Now see if we want to update the new state. */
+	if (ssptr) {
+		char *ss_sp;
+
+		if (get_user(ss_sp, &ssptr->the_stack))
+			goto out;
+		/* If the current stack was set with sigaltstack, don't
+		   swap stacks while we are on it.  */
+		ret = -EPERM;
+		if (current->sas_ss_sp && on_sig_stack(sp))
+			goto out;
+
+		/* Since we don't know the extent of the stack, and we don't
+		   track onstack-ness, but rather calculate it, we must
+		   presume a size.  Ho hum this interface is lossy.  */
+		current->sas_ss_sp = (unsigned long)ss_sp - SIGSTKSZ;
+		current->sas_ss_size = SIGSTKSZ;
+	}
+	ret = 0;
+out:
+	return ret;
+}
+
+void ptrace_signal_deliver(struct pt_regs *regs, void *cookie)
+{
+	struct sparc_deliver_cookie *cp = cookie;
+
+	if (cp->restart_syscall &&
+	    (regs->u_regs[UREG_I0] == ERESTARTNOHAND ||
+	     regs->u_regs[UREG_I0] == ERESTARTSYS ||
+	     regs->u_regs[UREG_I0] == ERESTARTNOINTR)) {
+		/* replay the system call when we are done */
+		regs->u_regs[UREG_I0] = cp->orig_i0;
+		regs->pc -= 4;
+		regs->npc -= 4;
+		cp->restart_syscall = 0;
+	}
+
+	if (cp->restart_syscall &&
+	    regs->u_regs[UREG_I0] == ERESTART_RESTARTBLOCK) {
+		regs->u_regs[UREG_G1] = __NR_restart_syscall;
+		regs->pc -= 4;
+		regs->npc -= 4;
+		cp->restart_syscall = 0;
+	}
+}
diff --git a/arch/sparc/kernel/smp.c b/arch/sparc/kernel/smp.c
new file mode 100644
index 0000000..c6e721d
--- /dev/null
+++ b/arch/sparc/kernel/smp.c
@@ -0,0 +1,295 @@
+/* smp.c: Sparc SMP support.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 2004 Keith M Wesolowski (wesolows@foobazco.org)
+ */
+
+#include <asm/head.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/threads.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/cache.h>
+#include <linux/delay.h>
+
+#include <asm/ptrace.h>
+#include <asm/atomic.h>
+
+#include <asm/irq.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/oplib.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+#include <asm/cpudata.h>
+
+volatile int smp_processors_ready = 0;
+int smp_num_cpus = 1;
+volatile unsigned long cpu_callin_map[NR_CPUS] __initdata = {0,};
+unsigned char boot_cpu_id = 0;
+unsigned char boot_cpu_id4 = 0; /* boot_cpu_id << 2 */
+int smp_activated = 0;
+volatile int __cpu_number_map[NR_CPUS];
+volatile int __cpu_logical_map[NR_CPUS];
+
+cpumask_t cpu_online_map = CPU_MASK_NONE;
+cpumask_t phys_cpu_present_map = CPU_MASK_NONE;
+
+/* The only guaranteed locking primitive available on all Sparc
+ * processors is 'ldstub [%reg + immediate], %dest_reg' which atomically
+ * places the current byte at the effective address into dest_reg and
+ * places 0xff there afterwards.  Pretty lame locking primitive
+ * compared to the Alpha and the Intel no?  Most Sparcs have 'swap'
+ * instruction which is much better...
+ */
+
+/* Used to make bitops atomic */
+unsigned char bitops_spinlock = 0;
+
+volatile unsigned long ipi_count;
+
+volatile int smp_process_available=0;
+volatile int smp_commenced = 0;
+
+void __init smp_store_cpu_info(int id)
+{
+	int cpu_node;
+
+	cpu_data(id).udelay_val = loops_per_jiffy;
+
+	cpu_find_by_mid(id, &cpu_node);
+	cpu_data(id).clock_tick = prom_getintdefault(cpu_node,
+						     "clock-frequency", 0);
+	cpu_data(id).prom_node = cpu_node;
+	cpu_data(id).mid = cpu_get_hwmid(cpu_node);
+	if (cpu_data(id).mid < 0)
+		panic("No MID found for CPU%d at node 0x%08d", id, cpu_node);
+}
+
+void __init smp_cpus_done(unsigned int max_cpus)
+{
+}
+
+void cpu_panic(void)
+{
+	printk("CPU[%d]: Returns from cpu_idle!\n", smp_processor_id());
+	panic("SMP bolixed\n");
+}
+
+struct linux_prom_registers smp_penguin_ctable __initdata = { 0 };
+
+void __init smp_boot_cpus(void)
+{
+	extern void smp4m_boot_cpus(void);
+	extern void smp4d_boot_cpus(void);
+	
+	if (sparc_cpu_model == sun4m)
+		smp4m_boot_cpus();
+	else
+		smp4d_boot_cpus();
+}
+
+void smp_send_reschedule(int cpu)
+{
+	/* See sparc64 */
+}
+
+void smp_send_stop(void)
+{
+}
+
+void smp_flush_cache_all(void)
+{
+	xc0((smpfunc_t) BTFIXUP_CALL(local_flush_cache_all));
+	local_flush_cache_all();
+}
+
+void smp_flush_tlb_all(void)
+{
+	xc0((smpfunc_t) BTFIXUP_CALL(local_flush_tlb_all));
+	local_flush_tlb_all();
+}
+
+void smp_flush_cache_mm(struct mm_struct *mm)
+{
+	if(mm->context != NO_CONTEXT) {
+		cpumask_t cpu_mask = mm->cpu_vm_mask;
+		cpu_clear(smp_processor_id(), cpu_mask);
+		if (!cpus_empty(cpu_mask))
+			xc1((smpfunc_t) BTFIXUP_CALL(local_flush_cache_mm), (unsigned long) mm);
+		local_flush_cache_mm(mm);
+	}
+}
+
+void smp_flush_tlb_mm(struct mm_struct *mm)
+{
+	if(mm->context != NO_CONTEXT) {
+		cpumask_t cpu_mask = mm->cpu_vm_mask;
+		cpu_clear(smp_processor_id(), cpu_mask);
+		if (!cpus_empty(cpu_mask)) {
+			xc1((smpfunc_t) BTFIXUP_CALL(local_flush_tlb_mm), (unsigned long) mm);
+			if(atomic_read(&mm->mm_users) == 1 && current->active_mm == mm)
+				mm->cpu_vm_mask = cpumask_of_cpu(smp_processor_id());
+		}
+		local_flush_tlb_mm(mm);
+	}
+}
+
+void smp_flush_cache_range(struct vm_area_struct *vma, unsigned long start,
+			   unsigned long end)
+{
+	struct mm_struct *mm = vma->vm_mm;
+
+	if (mm->context != NO_CONTEXT) {
+		cpumask_t cpu_mask = mm->cpu_vm_mask;
+		cpu_clear(smp_processor_id(), cpu_mask);
+		if (!cpus_empty(cpu_mask))
+			xc3((smpfunc_t) BTFIXUP_CALL(local_flush_cache_range), (unsigned long) vma, start, end);
+		local_flush_cache_range(vma, start, end);
+	}
+}
+
+void smp_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
+			 unsigned long end)
+{
+	struct mm_struct *mm = vma->vm_mm;
+
+	if (mm->context != NO_CONTEXT) {
+		cpumask_t cpu_mask = mm->cpu_vm_mask;
+		cpu_clear(smp_processor_id(), cpu_mask);
+		if (!cpus_empty(cpu_mask))
+			xc3((smpfunc_t) BTFIXUP_CALL(local_flush_tlb_range), (unsigned long) vma, start, end);
+		local_flush_tlb_range(vma, start, end);
+	}
+}
+
+void smp_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
+{
+	struct mm_struct *mm = vma->vm_mm;
+
+	if(mm->context != NO_CONTEXT) {
+		cpumask_t cpu_mask = mm->cpu_vm_mask;
+		cpu_clear(smp_processor_id(), cpu_mask);
+		if (!cpus_empty(cpu_mask))
+			xc2((smpfunc_t) BTFIXUP_CALL(local_flush_cache_page), (unsigned long) vma, page);
+		local_flush_cache_page(vma, page);
+	}
+}
+
+void smp_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+{
+	struct mm_struct *mm = vma->vm_mm;
+
+	if(mm->context != NO_CONTEXT) {
+		cpumask_t cpu_mask = mm->cpu_vm_mask;
+		cpu_clear(smp_processor_id(), cpu_mask);
+		if (!cpus_empty(cpu_mask))
+			xc2((smpfunc_t) BTFIXUP_CALL(local_flush_tlb_page), (unsigned long) vma, page);
+		local_flush_tlb_page(vma, page);
+	}
+}
+
+void smp_reschedule_irq(void)
+{
+	set_need_resched();
+}
+
+void smp_flush_page_to_ram(unsigned long page)
+{
+	/* Current theory is that those who call this are the one's
+	 * who have just dirtied their cache with the pages contents
+	 * in kernel space, therefore we only run this on local cpu.
+	 *
+	 * XXX This experiment failed, research further... -DaveM
+	 */
+#if 1
+	xc1((smpfunc_t) BTFIXUP_CALL(local_flush_page_to_ram), page);
+#endif
+	local_flush_page_to_ram(page);
+}
+
+void smp_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr)
+{
+	cpumask_t cpu_mask = mm->cpu_vm_mask;
+	cpu_clear(smp_processor_id(), cpu_mask);
+	if (!cpus_empty(cpu_mask))
+		xc2((smpfunc_t) BTFIXUP_CALL(local_flush_sig_insns), (unsigned long) mm, insn_addr);
+	local_flush_sig_insns(mm, insn_addr);
+}
+
+extern unsigned int lvl14_resolution;
+
+/* /proc/profile writes can call this, don't __init it please. */
+static DEFINE_SPINLOCK(prof_setup_lock);
+
+int setup_profiling_timer(unsigned int multiplier)
+{
+	int i;
+	unsigned long flags;
+
+	/* Prevent level14 ticker IRQ flooding. */
+	if((!multiplier) || (lvl14_resolution / multiplier) < 500)
+		return -EINVAL;
+
+	spin_lock_irqsave(&prof_setup_lock, flags);
+	for(i = 0; i < NR_CPUS; i++) {
+		if (cpu_possible(i))
+			load_profile_irq(i, lvl14_resolution / multiplier);
+		prof_multiplier(i) = multiplier;
+	}
+	spin_unlock_irqrestore(&prof_setup_lock, flags);
+
+	return 0;
+}
+
+void __init smp_prepare_cpus(unsigned int maxcpus)
+{
+}
+
+void __devinit smp_prepare_boot_cpu(void)
+{
+	current_thread_info()->cpu = hard_smp_processor_id();
+	cpu_set(smp_processor_id(), cpu_online_map);
+	cpu_set(smp_processor_id(), phys_cpu_present_map);
+}
+
+int __devinit __cpu_up(unsigned int cpu)
+{
+	panic("smp doesn't work\n");
+}
+
+void smp_bogo(struct seq_file *m)
+{
+	int i;
+	
+	for (i = 0; i < NR_CPUS; i++) {
+		if (cpu_online(i))
+			seq_printf(m,
+				   "Cpu%dBogo\t: %lu.%02lu\n", 
+				   i,
+				   cpu_data(i).udelay_val/(500000/HZ),
+				   (cpu_data(i).udelay_val/(5000/HZ))%100);
+	}
+}
+
+void smp_info(struct seq_file *m)
+{
+	int i;
+
+	seq_printf(m, "State:\n");
+	for (i = 0; i < NR_CPUS; i++) {
+		if (cpu_online(i))
+			seq_printf(m, "CPU%d\t\t: online\n", i);
+	}
+}
diff --git a/arch/sparc/kernel/sparc-stub.c b/arch/sparc/kernel/sparc-stub.c
new file mode 100644
index 0000000..e84f815
--- /dev/null
+++ b/arch/sparc/kernel/sparc-stub.c
@@ -0,0 +1,724 @@
+/* $Id: sparc-stub.c,v 1.28 2001/10/30 04:54:21 davem Exp $
+ * sparc-stub.c:  KGDB support for the Linux kernel.
+ *
+ * Modifications to run under Linux
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * This file originally came from the gdb sources, and the
+ * copyright notices have been retained below.
+ */
+
+/****************************************************************************
+
+		THIS SOFTWARE IS NOT COPYRIGHTED
+
+   HP offers the following for use in the public domain.  HP makes no
+   warranty with regard to the software or its performance and the
+   user accepts the software "AS IS" with all faults.
+
+   HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
+   TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+****************************************************************************/
+
+/****************************************************************************
+ *  Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
+ *
+ *  Module name: remcom.c $
+ *  Revision: 1.34 $
+ *  Date: 91/03/09 12:29:49 $
+ *  Contributor:     Lake Stevens Instrument Division$
+ *
+ *  Description:     low level support for gdb debugger. $
+ *
+ *  Considerations:  only works on target hardware $
+ *
+ *  Written by:      Glenn Engel $
+ *  ModuleState:     Experimental $
+ *
+ *  NOTES:           See Below $
+ *
+ *  Modified for SPARC by Stu Grossman, Cygnus Support.
+ *
+ *  This code has been extensively tested on the Fujitsu SPARClite demo board.
+ *
+ *  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 trap #1.
+ *
+ *************
+ *
+ *    The following gdb commands are supported:
+ *
+ * command          function                               Return value
+ *
+ *    g             return the value of the CPU registers  hex data or ENN
+ *    G             set the value of the CPU registers     OK or ENN
+ *
+ *    mAA..AA,LLLL  Read LLLL bytes at address AA..AA      hex data or ENN
+ *    MAA..AA,LLLL: Write LLLL bytes at address AA.AA      OK or ENN
+ *
+ *    c             Resume at current address              SNN   ( signal NN)
+ *    cAA..AA       Continue at address AA..AA             SNN
+ *
+ *    s             Step one instruction                   SNN
+ *    sAA..AA       Step one instruction from AA..AA       SNN
+ *
+ *    k             kill
+ *
+ *    ?             What was the last sigval ?             SNN   (signal NN)
+ *
+ *    bBB..BB	    Set baud rate to BB..BB		   OK or BNN, then sets
+ *							   baud rate
+ *
+ * All commands and responses are sent with a packet which includes a
+ * checksum.  A packet consists of
+ *
+ * $<packet info>#<checksum>.
+ *
+ * where
+ * <packet info> :: <characters representing the command or response>
+ * <checksum>    :: < two hex digits computed as modulo 256 sum of <packetinfo>>
+ *
+ * When a packet is received, it is first acknowledged with either '+' or '-'.
+ * '+' indicates a successful transfer.  '-' indicates a failed transfer.
+ *
+ * Example:
+ *
+ * Host:                  Reply:
+ * $m0,10#2a               +$00010203040506070809101112131415#42
+ *
+ ****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+#include <asm/system.h>
+#include <asm/signal.h>
+#include <asm/oplib.h>
+#include <asm/head.h>
+#include <asm/traps.h>
+#include <asm/vac-ops.h>
+#include <asm/kgdb.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/cacheflush.h>
+
+/*
+ *
+ * external low-level support routines
+ */
+
+extern void putDebugChar(char);   /* write a single character      */
+extern char getDebugChar(void);   /* read and return a single char */
+
+/*
+ * BUFMAX defines the maximum number of characters in inbound/outbound buffers
+ * at least NUMREGBYTES*2 are needed for register packets
+ */
+#define BUFMAX 2048
+
+static int initialized;	/* !0 means we've been initialized */
+
+static const char hexchars[]="0123456789abcdef";
+
+#define NUMREGS 72
+
+/* Number of bytes of registers.  */
+#define NUMREGBYTES (NUMREGS * 4)
+enum regnames {G0, G1, G2, G3, G4, G5, G6, G7,
+		 O0, O1, O2, O3, O4, O5, SP, O7,
+		 L0, L1, L2, L3, L4, L5, L6, L7,
+		 I0, I1, I2, I3, I4, I5, FP, I7,
+
+		 F0, F1, F2, F3, F4, F5, F6, F7,
+		 F8, F9, F10, F11, F12, F13, F14, F15,
+		 F16, F17, F18, F19, F20, F21, F22, F23,
+		 F24, F25, F26, F27, F28, F29, F30, F31,
+		 Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR };
+
+
+extern void trap_low(void);  /* In arch/sparc/kernel/entry.S */
+
+unsigned long get_sun4cpte(unsigned long addr)
+{
+	unsigned long entry;
+
+	__asm__ __volatile__("\n\tlda [%1] %2, %0\n\t" : 
+			     "=r" (entry) :
+			     "r" (addr), "i" (ASI_PTE));
+	return entry;
+}
+
+unsigned long get_sun4csegmap(unsigned long addr)
+{
+	unsigned long entry;
+
+	__asm__ __volatile__("\n\tlduba [%1] %2, %0\n\t" : 
+			     "=r" (entry) :
+			     "r" (addr), "i" (ASI_SEGMAP));
+	return entry;
+}
+
+#if 0
+/* Have to sort this out. This cannot be done after initialization. */
+static void flush_cache_all_nop(void) {}
+#endif
+
+/* Place where we save old trap entries for restoration */
+struct tt_entry kgdb_savettable[256];
+typedef void (*trapfunc_t)(void);
+
+/* Helper routine for manipulation of kgdb_savettable */
+static inline void copy_ttentry(struct tt_entry *src, struct tt_entry *dest)
+{
+	dest->inst_one = src->inst_one;
+	dest->inst_two = src->inst_two;
+	dest->inst_three = src->inst_three;
+	dest->inst_four = src->inst_four;
+}
+
+/* Initialize the kgdb_savettable so that debugging can commence */
+static void eh_init(void)
+{
+	int i;
+
+	for(i=0; i < 256; i++)
+		copy_ttentry(&sparc_ttable[i], &kgdb_savettable[i]);
+}
+
+/* Install an exception handler for kgdb */
+static void exceptionHandler(int tnum, trapfunc_t trap_entry)
+{
+	unsigned long te_addr = (unsigned long) trap_entry;
+
+	/* Make new vector */
+	sparc_ttable[tnum].inst_one =
+		SPARC_BRANCH((unsigned long) te_addr,
+			     (unsigned long) &sparc_ttable[tnum].inst_one);
+	sparc_ttable[tnum].inst_two = SPARC_RD_PSR_L0;
+	sparc_ttable[tnum].inst_three = SPARC_NOP;
+	sparc_ttable[tnum].inst_four = SPARC_NOP;
+}
+
+/* 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() & 0x7f;
+			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 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(unsigned char *buffer)
+{
+	unsigned char checksum;
+	int count;
+	unsigned char ch, recv;
+
+	/*  $<packet info>#<checksum>. */
+	do {
+		putDebugChar('$');
+		checksum = 0;
+		count = 0;
+
+		while ((ch = buffer[count])) {
+			putDebugChar(ch);
+			checksum += ch;
+			count += 1;
+		}
+
+		putDebugChar('#');
+		putDebugChar(hexchars[checksum >> 4]);
+		putDebugChar(hexchars[checksum & 0xf]);
+		recv = getDebugChar();
+	} while ((recv & 0x7f) != '+');
+}
+
+static char remcomInBuffer[BUFMAX];
+static char remcomOutBuffer[BUFMAX];
+
+/* 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.
+ */
+
+static unsigned char *
+mem2hex(char *mem, char *buf, int count)
+{
+	unsigned char ch;
+
+	while (count-- > 0) {
+		/* This assembler code is basically:  ch = *mem++;
+		 * except that we use the SPARC/Linux exception table
+		 * mechanism (see how "fixup" works in kernel_mna_trap_fault)
+		 * to arrange for a "return 0" upon a memory fault
+		 */
+		__asm__(
+			"\n1:\n\t"
+			"ldub [%0], %1\n\t"
+			"inc %0\n\t"
+			".section .fixup,#alloc,#execinstr\n\t"
+			".align 4\n"
+			"2:\n\t"
+			"retl\n\t"
+			" mov 0, %%o0\n\t"
+			".section __ex_table, #alloc\n\t"
+			".align 4\n\t"
+			".word 1b, 2b\n\t"
+			".text\n"
+			: "=r" (mem), "=r" (ch) : "0" (mem));
+		*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.
+*/
+static char *
+hex2mem(char *buf, char *mem, int count)
+{
+	int i;
+	unsigned char ch;
+
+	for (i=0; i<count; i++) {
+
+		ch = hex(*buf++) << 4;
+		ch |= hex(*buf++);
+		/* Assembler code is   *mem++ = ch;   with return 0 on fault */
+		__asm__(
+			"\n1:\n\t"
+			"stb %1, [%0]\n\t"
+			"inc %0\n\t"
+			".section .fixup,#alloc,#execinstr\n\t"
+			".align 4\n"
+			"2:\n\t"
+			"retl\n\t"
+			" mov 0, %%o0\n\t"
+			".section __ex_table, #alloc\n\t"
+			".align 4\n\t"
+			".word 1b, 2b\n\t"
+			".text\n"
+			: "=r" (mem) : "r" (ch) , "0" (mem));
+	}
+	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 SPARC */
+  unsigned char signo;		/* Signal that we map this trap into */
+} hard_trap_info[] = {
+  {SP_TRAP_SBPT, SIGTRAP},      /* ta 1 - Linux/KGDB software breakpoint */
+  {0, 0}			/* Must be last */
+};
+
+/* Set up exception handlers for tracing and breakpoints */
+
+void
+set_debug_traps(void)
+{
+	struct hard_trap_info *ht;
+	unsigned long flags;
+
+	local_irq_save(flags);
+#if 0	
+/* Have to sort this out. This cannot be done after initialization. */
+	BTFIXUPSET_CALL(flush_cache_all, flush_cache_all_nop, BTFIXUPCALL_NOP);
+#endif
+
+	/* Initialize our copy of the Linux Sparc trap table */
+	eh_init();
+
+	for (ht = hard_trap_info; ht->tt && ht->signo; ht++) {
+		/* Only if it doesn't destroy our fault handlers */
+		if((ht->tt != SP_TRAP_TFLT) && 
+		   (ht->tt != SP_TRAP_DFLT))
+			exceptionHandler(ht->tt, trap_low);
+	}
+
+	/* In case GDB is started before us, ack any packets (presumably
+	 * "$?#xx") sitting there.
+	 *
+	 * I've found this code causes more problems than it solves,
+	 * so that's why it's commented out.  GDB seems to work fine
+	 * now starting either before or after the kernel   -bwb
+	 */
+#if 0
+	while((c = getDebugChar()) != '$');
+	while((c = getDebugChar()) != '#');
+	c = getDebugChar(); /* eat first csum byte */
+	c = getDebugChar(); /* eat second csum byte */
+	putDebugChar('+'); /* ack it */
+#endif
+
+	initialized = 1; /* connect! */
+	local_irq_restore(flags);
+}
+
+/* Convert the SPARC 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);
+}
+
+/*
+ * 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.
+ */
+
+extern void breakinst(void);
+
+void
+handle_exception (unsigned long *registers)
+{
+	int tt;       /* Trap type */
+	int sigval;
+	int addr;
+	int length;
+	char *ptr;
+	unsigned long *sp;
+
+	/* First, we must force all of the windows to be spilled out */
+
+	asm("save %sp, -64, %sp\n\t"
+	    "save %sp, -64, %sp\n\t"
+	    "save %sp, -64, %sp\n\t"
+	    "save %sp, -64, %sp\n\t"
+	    "save %sp, -64, %sp\n\t"
+	    "save %sp, -64, %sp\n\t"
+	    "save %sp, -64, %sp\n\t"
+	    "save %sp, -64, %sp\n\t"
+	    "restore\n\t"
+	    "restore\n\t"
+	    "restore\n\t"
+	    "restore\n\t"
+	    "restore\n\t"
+	    "restore\n\t"
+	    "restore\n\t"
+	    "restore\n\t");
+
+	lock_kernel();
+	if (registers[PC] == (unsigned long)breakinst) {
+		/* Skip over breakpoint trap insn */
+		registers[PC] = registers[NPC];
+		registers[NPC] += 4;
+	}
+
+	sp = (unsigned long *)registers[SP];
+
+	tt = (registers[TBR] >> 4) & 0xff;
+
+	/* reply to host that an exception has occurred */
+	sigval = computeSignal(tt);
+	ptr = remcomOutBuffer;
+
+	*ptr++ = 'T';
+	*ptr++ = hexchars[sigval >> 4];
+	*ptr++ = hexchars[sigval & 0xf];
+
+	*ptr++ = hexchars[PC >> 4];
+	*ptr++ = hexchars[PC & 0xf];
+	*ptr++ = ':';
+	ptr = mem2hex((char *)&registers[PC], ptr, 4);
+	*ptr++ = ';';
+
+	*ptr++ = hexchars[FP >> 4];
+	*ptr++ = hexchars[FP & 0xf];
+	*ptr++ = ':';
+	ptr = mem2hex((char *) (sp + 8 + 6), ptr, 4); /* FP */
+	*ptr++ = ';';
+
+	*ptr++ = hexchars[SP >> 4];
+	*ptr++ = hexchars[SP & 0xf];
+	*ptr++ = ':';
+	ptr = mem2hex((char *)&sp, ptr, 4);
+	*ptr++ = ';';
+
+	*ptr++ = hexchars[NPC >> 4];
+	*ptr++ = hexchars[NPC & 0xf];
+	*ptr++ = ':';
+	ptr = mem2hex((char *)&registers[NPC], ptr, 4);
+	*ptr++ = ';';
+
+	*ptr++ = hexchars[O7 >> 4];
+	*ptr++ = hexchars[O7 & 0xf];
+	*ptr++ = ':';
+	ptr = mem2hex((char *)&registers[O7], ptr, 4);
+	*ptr++ = ';';
+
+	*ptr++ = 0;
+
+	putpacket(remcomOutBuffer);
+
+	/* XXX We may want to add some features dealing with poking the
+	 * XXX page tables, the real ones on the srmmu, and what is currently
+	 * XXX loaded in the sun4/sun4c tlb at this point in time.  But this
+	 * XXX also required hacking to the gdb sources directly...
+	 */
+
+	while (1) {
+		remcomOutBuffer[0] = 0;
+
+		getpacket(remcomInBuffer);
+		switch (remcomInBuffer[0]) {
+		case '?':
+			remcomOutBuffer[0] = 'S';
+			remcomOutBuffer[1] = hexchars[sigval >> 4];
+			remcomOutBuffer[2] = hexchars[sigval & 0xf];
+			remcomOutBuffer[3] = 0;
+			break;
+
+		case 'd':
+			/* toggle debug flag */
+			break;
+
+		case 'g':		/* return the value of the CPU registers */
+		{
+			ptr = remcomOutBuffer;
+			/* G & O regs */
+			ptr = mem2hex((char *)registers, ptr, 16 * 4);
+			/* L & I regs */
+			ptr = mem2hex((char *) (sp + 0), ptr, 16 * 4);
+			/* Floating point */
+			memset(ptr, '0', 32 * 8);
+			/* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
+			mem2hex((char *)&registers[Y], (ptr + 32 * 4 * 2), (8 * 4));
+		}
+			break;
+
+		case 'G':	   /* set the value of the CPU registers - return OK */
+		{
+			unsigned long *newsp, psr;
+
+			psr = registers[PSR];
+
+			ptr = &remcomInBuffer[1];
+			/* G & O regs */
+			hex2mem(ptr, (char *)registers, 16 * 4);
+			/* L & I regs */
+			hex2mem(ptr + 16 * 4 * 2, (char *) (sp + 0), 16 * 4);
+			/* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
+			hex2mem(ptr + 64 * 4 * 2, (char *)&registers[Y], 8 * 4);
+
+			/* See if the stack pointer has moved.  If so,
+			 * then copy the saved locals and ins to the
+			 * new location.  This keeps the window
+			 * overflow and underflow routines happy.
+			 */
+
+			newsp = (unsigned long *)registers[SP];
+			if (sp != newsp)
+				sp = memcpy(newsp, sp, 16 * 4);
+
+			/* Don't allow CWP to be modified. */
+
+			if (psr != registers[PSR])
+				registers[PSR] = (psr & 0x1f) | (registers[PSR] & ~0x1f);
+
+			strcpy(remcomOutBuffer,"OK");
+		}
+			break;
+
+		case 'm':	  /* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */
+			/* Try to read %x,%x.  */
+
+			ptr = &remcomInBuffer[1];
+
+			if (hexToInt(&ptr, &addr)
+			    && *ptr++ == ','
+			    && hexToInt(&ptr, &length))	{
+				if (mem2hex((char *)addr, remcomOutBuffer, length))
+					break;
+
+				strcpy (remcomOutBuffer, "E03");
+			} else {
+				strcpy(remcomOutBuffer,"E01");
+			}
+			break;
+
+		case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
+			/* Try to read '%x,%x:'.  */
+
+			ptr = &remcomInBuffer[1];
+
+			if (hexToInt(&ptr, &addr)
+			    && *ptr++ == ','
+			    && hexToInt(&ptr, &length)
+			    && *ptr++ == ':') {
+				if (hex2mem(ptr, (char *)addr, length)) {
+					strcpy(remcomOutBuffer, "OK");
+				} else {
+					strcpy(remcomOutBuffer, "E03");
+				}
+			} else {
+				strcpy(remcomOutBuffer, "E02");
+			}
+			break;
+
+		case 'c':    /* cAA..AA    Continue at address AA..AA(optional) */
+			/* try to read optional parameter, pc unchanged if no parm */
+
+			ptr = &remcomInBuffer[1];
+			if (hexToInt(&ptr, &addr)) {
+				registers[PC] = addr;
+				registers[NPC] = addr + 4;
+			}
+
+/* Need to flush the instruction cache here, as we may have deposited a
+ * breakpoint, and the icache probably has no way of knowing that a data ref to
+ * some location may have changed something that is in the instruction cache.
+ */
+			flush_cache_all();
+			unlock_kernel();
+			return;
+
+			/* kill the program */
+		case 'k' :		/* do nothing */
+			break;
+		case 'r':		/* Reset */
+			asm ("call 0\n\t"
+			     "nop\n\t");
+			break;
+		}			/* switch */
+
+		/* reply to the request */
+		putpacket(remcomOutBuffer);
+	} /* while(1) */
+}
+
+/* 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;
+
+	/* Again, watch those c-prefixes for ELF kernels */
+#if defined(__svr4__) || defined(__ELF__)
+	asm(".globl breakinst\n"
+	    "breakinst:\n\t"
+	    "ta 1\n");
+#else
+	asm(".globl _breakinst\n"
+	    "_breakinst:\n\t"
+	    "ta 1\n");
+#endif
+}
diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c
new file mode 100644
index 0000000..f91b0e8
--- /dev/null
+++ b/arch/sparc/kernel/sparc_ksyms.c
@@ -0,0 +1,334 @@
+/* $Id: sparc_ksyms.c,v 1.107 2001/07/17 16:17:33 anton Exp $
+ * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ */
+
+/* Tell string.h we don't want memcpy etc. as cpp defines */
+#define EXPORT_SYMTAB_STROPS
+#define PROMLIB_INTERNAL
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/in6.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#endif
+#include <linux/pm.h>
+#ifdef CONFIG_HIGHMEM
+#include <linux/highmem.h>
+#endif
+
+#include <asm/oplib.h>
+#include <asm/delay.h>
+#include <asm/system.h>
+#include <asm/auxio.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/idprom.h>
+#include <asm/svr4.h>
+#include <asm/head.h>
+#include <asm/smp.h>
+#include <asm/mostek.h>
+#include <asm/ptrace.h>
+#include <asm/user.h>
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+#ifdef CONFIG_SBUS
+#include <asm/sbus.h>
+#include <asm/dma.h>
+#endif
+#ifdef CONFIG_PCI
+#include <asm/ebus.h>
+#endif
+#include <asm/a.out.h>
+#include <asm/io-unit.h>
+#include <asm/bug.h>
+
+extern spinlock_t rtc_lock;
+
+struct poll {
+	int fd;
+	short events;
+	short revents;
+};
+
+extern int svr4_getcontext (svr4_ucontext_t *, struct pt_regs *);
+extern int svr4_setcontext (svr4_ucontext_t *, struct pt_regs *);
+void _sigpause_common (unsigned int set, struct pt_regs *);
+extern void (*__copy_1page)(void *, const void *);
+extern void __memmove(void *, const void *, __kernel_size_t);
+extern void (*bzero_1page)(void *);
+extern void *__bzero(void *, size_t);
+extern void *__memscan_zero(void *, size_t);
+extern void *__memscan_generic(void *, int, size_t);
+extern int __memcmp(const void *, const void *, __kernel_size_t);
+extern int __strncmp(const char *, const char *, __kernel_size_t);
+
+extern int __ashrdi3(int, int);
+extern int __ashldi3(int, int);
+extern int __lshrdi3(int, int);
+extern int __muldi3(int, int);
+extern int __divdi3(int, int);
+
+extern void dump_thread(struct pt_regs *, struct user *);
+
+/* Private functions with odd calling conventions. */
+extern void ___atomic24_add(void);
+extern void ___atomic24_sub(void);
+extern void ___set_bit(void);
+extern void ___clear_bit(void);
+extern void ___change_bit(void);
+
+/* Alias functions whose names begin with "." and export the aliases.
+ * The module references will be fixed up by module_frob_arch_sections.
+ */
+#define DOT_ALIAS2(__ret, __x, __arg1, __arg2) \
+	extern __ret __x(__arg1, __arg2) \
+	             __attribute__((weak, alias("." # __x)));
+
+DOT_ALIAS2(int, div, int, int)
+DOT_ALIAS2(int, mul, int, int)
+DOT_ALIAS2(int, rem, int, int)
+DOT_ALIAS2(unsigned, udiv, unsigned, unsigned)
+DOT_ALIAS2(unsigned, umul, unsigned, unsigned)
+DOT_ALIAS2(unsigned, urem, unsigned, unsigned)
+
+#undef DOT_ALIAS2
+
+/* used by various drivers */
+EXPORT_SYMBOL(sparc_cpu_model);
+EXPORT_SYMBOL(kernel_thread);
+#ifdef CONFIG_DEBUG_SPINLOCK
+#ifdef CONFIG_SMP
+EXPORT_SYMBOL(_do_spin_lock);
+EXPORT_SYMBOL(_do_spin_unlock);
+EXPORT_SYMBOL(_spin_trylock);
+EXPORT_SYMBOL(_do_read_lock);
+EXPORT_SYMBOL(_do_read_unlock);
+EXPORT_SYMBOL(_do_write_lock);
+EXPORT_SYMBOL(_do_write_unlock);
+#endif
+#else
+// XXX find what uses (or used) these.
+// EXPORT_SYMBOL_PRIVATE(_rw_read_enter);
+// EXPORT_SYMBOL_PRIVATE(_rw_read_exit);
+// EXPORT_SYMBOL_PRIVATE(_rw_write_enter);
+#endif
+/* semaphores */
+EXPORT_SYMBOL(__up);
+EXPORT_SYMBOL(__down);
+EXPORT_SYMBOL(__down_trylock);
+EXPORT_SYMBOL(__down_interruptible);
+
+EXPORT_SYMBOL(sparc_valid_addr_bitmap);
+EXPORT_SYMBOL(phys_base);
+EXPORT_SYMBOL(pfn_base);
+
+/* Atomic operations. */
+EXPORT_SYMBOL(___atomic24_add);
+EXPORT_SYMBOL(___atomic24_sub);
+
+/* Bit operations. */
+EXPORT_SYMBOL(___set_bit);
+EXPORT_SYMBOL(___clear_bit);
+EXPORT_SYMBOL(___change_bit);
+
+#ifdef CONFIG_SMP
+/* IRQ implementation. */
+EXPORT_SYMBOL(synchronize_irq);
+
+/* Misc SMP information */
+EXPORT_SYMBOL(__cpu_number_map);
+EXPORT_SYMBOL(__cpu_logical_map);
+#endif
+
+EXPORT_SYMBOL(__udelay);
+EXPORT_SYMBOL(__ndelay);
+EXPORT_SYMBOL(rtc_lock);
+EXPORT_SYMBOL(mostek_lock);
+EXPORT_SYMBOL(mstk48t02_regs);
+#ifdef CONFIG_SUN_AUXIO
+EXPORT_SYMBOL(set_auxio);
+EXPORT_SYMBOL(get_auxio);
+#endif
+EXPORT_SYMBOL(request_fast_irq);
+EXPORT_SYMBOL(io_remap_page_range);
+EXPORT_SYMBOL(io_remap_pfn_range);
+  /* P3: iounit_xxx may be needed, sun4d users */
+/* EXPORT_SYMBOL(iounit_map_dma_init); */
+/* EXPORT_SYMBOL(iounit_map_dma_page); */
+
+#ifndef CONFIG_SMP
+EXPORT_SYMBOL(BTFIXUP_CALL(___xchg32));
+#else
+EXPORT_SYMBOL(BTFIXUP_CALL(__hard_smp_processor_id));
+#endif
+EXPORT_SYMBOL(BTFIXUP_CALL(enable_irq));
+EXPORT_SYMBOL(BTFIXUP_CALL(disable_irq));
+EXPORT_SYMBOL(BTFIXUP_CALL(__irq_itoa));
+EXPORT_SYMBOL(BTFIXUP_CALL(mmu_unlockarea));
+EXPORT_SYMBOL(BTFIXUP_CALL(mmu_lockarea));
+EXPORT_SYMBOL(BTFIXUP_CALL(mmu_get_scsi_sgl));
+EXPORT_SYMBOL(BTFIXUP_CALL(mmu_get_scsi_one));
+EXPORT_SYMBOL(BTFIXUP_CALL(mmu_release_scsi_sgl));
+EXPORT_SYMBOL(BTFIXUP_CALL(mmu_release_scsi_one));
+
+#ifdef CONFIG_SBUS
+EXPORT_SYMBOL(sbus_root);
+EXPORT_SYMBOL(dma_chain);
+EXPORT_SYMBOL(sbus_set_sbus64);
+EXPORT_SYMBOL(sbus_alloc_consistent);
+EXPORT_SYMBOL(sbus_free_consistent);
+EXPORT_SYMBOL(sbus_map_single);
+EXPORT_SYMBOL(sbus_unmap_single);
+EXPORT_SYMBOL(sbus_map_sg);
+EXPORT_SYMBOL(sbus_unmap_sg);
+EXPORT_SYMBOL(sbus_dma_sync_single_for_cpu);
+EXPORT_SYMBOL(sbus_dma_sync_single_for_device);
+EXPORT_SYMBOL(sbus_dma_sync_sg_for_cpu);
+EXPORT_SYMBOL(sbus_dma_sync_sg_for_device);
+EXPORT_SYMBOL(sbus_iounmap);
+EXPORT_SYMBOL(sbus_ioremap);
+#endif
+#ifdef CONFIG_PCI
+EXPORT_SYMBOL(ebus_chain);
+EXPORT_SYMBOL(insb);
+EXPORT_SYMBOL(outsb);
+EXPORT_SYMBOL(insw);
+EXPORT_SYMBOL(outsw);
+EXPORT_SYMBOL(insl);
+EXPORT_SYMBOL(outsl);
+EXPORT_SYMBOL(pci_alloc_consistent);
+EXPORT_SYMBOL(pci_free_consistent);
+EXPORT_SYMBOL(pci_map_single);
+EXPORT_SYMBOL(pci_unmap_single);
+EXPORT_SYMBOL(pci_dma_sync_single_for_cpu);
+EXPORT_SYMBOL(pci_dma_sync_single_for_device);
+EXPORT_SYMBOL(pci_dma_sync_sg_for_cpu);
+EXPORT_SYMBOL(pci_dma_sync_sg_for_device);
+EXPORT_SYMBOL(pci_map_sg);
+EXPORT_SYMBOL(pci_unmap_sg);
+EXPORT_SYMBOL(pci_map_page);
+EXPORT_SYMBOL(pci_unmap_page);
+/* Actually, ioremap/iounmap are not PCI specific. But it is ok for drivers. */
+EXPORT_SYMBOL(ioremap);
+EXPORT_SYMBOL(iounmap);
+#endif
+
+/* in arch/sparc/mm/highmem.c */
+#ifdef CONFIG_HIGHMEM
+EXPORT_SYMBOL(kmap_atomic);
+EXPORT_SYMBOL(kunmap_atomic);
+#endif
+
+/* Solaris/SunOS binary compatibility */
+EXPORT_SYMBOL(svr4_setcontext);
+EXPORT_SYMBOL(svr4_getcontext);
+EXPORT_SYMBOL(_sigpause_common);
+
+EXPORT_SYMBOL(dump_thread);
+
+/* prom symbols */
+EXPORT_SYMBOL(idprom);
+EXPORT_SYMBOL(prom_root_node);
+EXPORT_SYMBOL(prom_getchild);
+EXPORT_SYMBOL(prom_getsibling);
+EXPORT_SYMBOL(prom_searchsiblings);
+EXPORT_SYMBOL(prom_firstprop);
+EXPORT_SYMBOL(prom_nextprop);
+EXPORT_SYMBOL(prom_getproplen);
+EXPORT_SYMBOL(prom_getproperty);
+EXPORT_SYMBOL(prom_node_has_property);
+EXPORT_SYMBOL(prom_setprop);
+EXPORT_SYMBOL(saved_command_line);
+EXPORT_SYMBOL(prom_apply_obio_ranges);
+EXPORT_SYMBOL(prom_getname);
+EXPORT_SYMBOL(prom_feval);
+EXPORT_SYMBOL(prom_getbool);
+EXPORT_SYMBOL(prom_getstring);
+EXPORT_SYMBOL(prom_getint);
+EXPORT_SYMBOL(prom_getintdefault);
+EXPORT_SYMBOL(prom_finddevice);
+EXPORT_SYMBOL(romvec);
+EXPORT_SYMBOL(__prom_getchild);
+EXPORT_SYMBOL(__prom_getsibling);
+
+/* sparc library symbols */
+EXPORT_SYMBOL(memchr);
+EXPORT_SYMBOL(memscan);
+EXPORT_SYMBOL(strlen);
+EXPORT_SYMBOL(strnlen);
+EXPORT_SYMBOL(strcpy);
+EXPORT_SYMBOL(strncpy);
+EXPORT_SYMBOL(strcat);
+EXPORT_SYMBOL(strncat);
+EXPORT_SYMBOL(strcmp);
+EXPORT_SYMBOL(strncmp);
+EXPORT_SYMBOL(strchr);
+EXPORT_SYMBOL(strrchr);
+EXPORT_SYMBOL(strpbrk);
+EXPORT_SYMBOL(strstr);
+EXPORT_SYMBOL(page_kernel);
+
+/* Special internal versions of library functions. */
+EXPORT_SYMBOL(__copy_1page);
+EXPORT_SYMBOL(__memcpy);
+EXPORT_SYMBOL(__memset);
+EXPORT_SYMBOL(bzero_1page);
+EXPORT_SYMBOL(__bzero);
+EXPORT_SYMBOL(__memscan_zero);
+EXPORT_SYMBOL(__memscan_generic);
+EXPORT_SYMBOL(__memcmp);
+EXPORT_SYMBOL(__strncmp);
+EXPORT_SYMBOL(__memmove);
+
+/* Moving data to/from userspace. */
+EXPORT_SYMBOL(__copy_user);
+EXPORT_SYMBOL(__strncpy_from_user);
+
+/* Networking helper routines. */
+EXPORT_SYMBOL(__csum_partial_copy_sparc_generic);
+EXPORT_SYMBOL(csum_partial);
+
+/* Cache flushing.  */
+EXPORT_SYMBOL(sparc_flush_page_to_ram);
+
+/* For when serial stuff is built as modules. */
+EXPORT_SYMBOL(sun_do_break);
+
+EXPORT_SYMBOL(__ret_efault);
+
+EXPORT_SYMBOL(memcmp);
+EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(memset);
+EXPORT_SYMBOL(memmove);
+EXPORT_SYMBOL(__ashrdi3);
+EXPORT_SYMBOL(__ashldi3);
+EXPORT_SYMBOL(__lshrdi3);
+EXPORT_SYMBOL(__muldi3);
+EXPORT_SYMBOL(__divdi3);
+
+EXPORT_SYMBOL(rem);
+EXPORT_SYMBOL(urem);
+EXPORT_SYMBOL(mul);
+EXPORT_SYMBOL(umul);
+EXPORT_SYMBOL(div);
+EXPORT_SYMBOL(udiv);
+
+#ifdef CONFIG_DEBUG_BUGVERBOSE
+EXPORT_SYMBOL(do_BUG);
+#endif
+
+/* Sun Power Management Idle Handler */
+EXPORT_SYMBOL(pm_idle);
diff --git a/arch/sparc/kernel/sun4c_irq.c b/arch/sparc/kernel/sun4c_irq.c
new file mode 100644
index 0000000..3d6a990
--- /dev/null
+++ b/arch/sparc/kernel/sun4c_irq.c
@@ -0,0 +1,250 @@
+/*  sun4c_irq.c
+ *  arch/sparc/kernel/sun4c_irq.c:
+ *
+ *  djhr: Hacked out of irq.c into a CPU dependent version.
+ *
+ *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *  Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ *  Copyright (C) 1995 Pete A. Zaitcev (zaitcev@yahoo.com)
+ *  Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/linkage.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/psr.h>
+#include <asm/vaddrs.h>
+#include <asm/timer.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/traps.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/sun4paddr.h>
+#include <asm/idprom.h>
+#include <asm/machines.h>
+#include <asm/sbus.h>
+
+#if 0
+static struct resource sun4c_timer_eb = { "sun4c_timer" };
+static struct resource sun4c_intr_eb = { "sun4c_intr" };
+#endif
+
+/* Pointer to the interrupt enable byte
+ *
+ * Dave Redman (djhr@tadpole.co.uk)
+ * What you may not be aware of is that entry.S requires this variable.
+ *
+ *  --- linux_trap_nmi_sun4c --
+ *
+ * so don't go making it static, like I tried. sigh.
+ */
+unsigned char *interrupt_enable = NULL;
+
+static int sun4c_pil_map[] = { 0, 1, 2, 3, 5, 7, 8, 9 };
+
+unsigned int sun4c_sbint_to_irq(struct sbus_dev *sdev, unsigned int sbint)
+{
+	if (sbint >= sizeof(sun4c_pil_map)) {
+		printk(KERN_ERR "%s: bogus SBINT %d\n", sdev->prom_name, sbint);
+		BUG();
+	}
+	return sun4c_pil_map[sbint];
+}
+
+static void sun4c_disable_irq(unsigned int irq_nr)
+{
+	unsigned long flags;
+	unsigned char current_mask, new_mask;
+    
+	local_irq_save(flags);
+	irq_nr &= (NR_IRQS - 1);
+	current_mask = *interrupt_enable;
+	switch(irq_nr) {
+	case 1:
+		new_mask = ((current_mask) & (~(SUN4C_INT_E1)));
+		break;
+	case 8:
+		new_mask = ((current_mask) & (~(SUN4C_INT_E8)));
+		break;
+	case 10:
+		new_mask = ((current_mask) & (~(SUN4C_INT_E10)));
+		break;
+	case 14:
+		new_mask = ((current_mask) & (~(SUN4C_INT_E14)));
+		break;
+	default:
+		local_irq_restore(flags);
+		return;
+	}
+	*interrupt_enable = new_mask;
+	local_irq_restore(flags);
+}
+
+static void sun4c_enable_irq(unsigned int irq_nr)
+{
+	unsigned long flags;
+	unsigned char current_mask, new_mask;
+    
+	local_irq_save(flags);
+	irq_nr &= (NR_IRQS - 1);
+	current_mask = *interrupt_enable;
+	switch(irq_nr) {
+	case 1:
+		new_mask = ((current_mask) | SUN4C_INT_E1);
+		break;
+	case 8:
+		new_mask = ((current_mask) | SUN4C_INT_E8);
+		break;
+	case 10:
+		new_mask = ((current_mask) | SUN4C_INT_E10);
+		break;
+	case 14:
+		new_mask = ((current_mask) | SUN4C_INT_E14);
+		break;
+	default:
+		local_irq_restore(flags);
+		return;
+	}
+	*interrupt_enable = new_mask;
+	local_irq_restore(flags);
+}
+
+#define TIMER_IRQ  	10    /* Also at level 14, but we ignore that one. */
+#define PROFILE_IRQ	14    /* Level14 ticker.. used by OBP for polling */
+
+volatile struct sun4c_timer_info *sun4c_timers;
+
+#ifdef CONFIG_SUN4
+/* This is an ugly hack to work around the
+   current timer code, and make it work with 
+   the sun4/260 intersil 
+   */
+volatile struct sun4c_timer_info sun4_timer;
+#endif
+
+static void sun4c_clear_clock_irq(void)
+{
+	volatile unsigned int clear_intr;
+#ifdef CONFIG_SUN4
+	if (idprom->id_machtype == (SM_SUN4 | SM_4_260)) 
+	  clear_intr = sun4_timer.timer_limit10;
+	else
+#endif
+	clear_intr = sun4c_timers->timer_limit10;
+}
+
+static void sun4c_clear_profile_irq(int cpu)
+{
+	/* Errm.. not sure how to do this.. */
+}
+
+static void sun4c_load_profile_irq(int cpu, unsigned int limit)
+{
+	/* Errm.. not sure how to do this.. */
+}
+
+static void __init sun4c_init_timers(irqreturn_t (*counter_fn)(int, void *, struct pt_regs *))
+{
+	int irq;
+
+	/* Map the Timer chip, this is implemented in hardware inside
+	 * the cache chip on the sun4c.
+	 */
+#ifdef CONFIG_SUN4
+	if (idprom->id_machtype == (SM_SUN4 | SM_4_260))
+		sun4c_timers = &sun4_timer;
+	else
+#endif
+	sun4c_timers = ioremap(SUN_TIMER_PHYSADDR,
+	    sizeof(struct sun4c_timer_info));
+
+	/* Have the level 10 timer tick at 100HZ.  We don't touch the
+	 * level 14 timer limit since we are letting the prom handle
+	 * them until we have a real console driver so L1-A works.
+	 */
+	sun4c_timers->timer_limit10 = (((1000000/HZ) + 1) << 10);
+	master_l10_counter = &sun4c_timers->cur_count10;
+	master_l10_limit = &sun4c_timers->timer_limit10;
+
+	irq = request_irq(TIMER_IRQ,
+			  counter_fn,
+			  (SA_INTERRUPT | SA_STATIC_ALLOC),
+			  "timer", NULL);
+	if (irq) {
+		prom_printf("time_init: unable to attach IRQ%d\n",TIMER_IRQ);
+		prom_halt();
+	}
+    
+#if 0
+	/* This does not work on 4/330 */
+	sun4c_enable_irq(10);
+#endif
+	claim_ticker14(NULL, PROFILE_IRQ, 0);
+}
+
+#ifdef CONFIG_SMP
+static void sun4c_nop(void) {}
+#endif
+
+extern char *sun4m_irq_itoa(unsigned int irq);
+
+void __init sun4c_init_IRQ(void)
+{
+	struct linux_prom_registers int_regs[2];
+	int ie_node;
+
+	if (ARCH_SUN4) {
+		interrupt_enable = (char *)
+		    ioremap(sun4_ie_physaddr, PAGE_SIZE);
+	} else {
+		struct resource phyres;
+
+		ie_node = prom_searchsiblings (prom_getchild(prom_root_node),
+				       	"interrupt-enable");
+		if(ie_node == 0)
+			panic("Cannot find /interrupt-enable node");
+
+		/* Depending on the "address" property is bad news... */
+		interrupt_enable = NULL;
+		if (prom_getproperty(ie_node, "reg", (char *) int_regs,
+				     sizeof(int_regs)) != -1) {
+			memset(&phyres, 0, sizeof(struct resource));
+			phyres.flags = int_regs[0].which_io;
+			phyres.start = int_regs[0].phys_addr;
+			interrupt_enable = (char *) sbus_ioremap(&phyres, 0,
+			    int_regs[0].reg_size, "sun4c_intr");
+		}
+	}
+	if (!interrupt_enable)
+		panic("Cannot map interrupt_enable");
+
+	BTFIXUPSET_CALL(sbint_to_irq, sun4c_sbint_to_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(enable_irq, sun4c_enable_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(disable_irq, sun4c_disable_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(enable_pil_irq, sun4c_enable_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(disable_pil_irq, sun4c_disable_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(clear_clock_irq, sun4c_clear_clock_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(clear_profile_irq, sun4c_clear_profile_irq, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(load_profile_irq, sun4c_load_profile_irq, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(__irq_itoa, sun4m_irq_itoa, BTFIXUPCALL_NORM);
+	sparc_init_timers = sun4c_init_timers;
+#ifdef CONFIG_SMP
+	BTFIXUPSET_CALL(set_cpu_int, sun4c_nop, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(clear_cpu_int, sun4c_nop, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(set_irq_udt, sun4c_nop, BTFIXUPCALL_NOP);
+#endif
+	*interrupt_enable = (SUN4C_INT_ENABLE);
+	/* Cannot enable interrupts until OBP ticker is disabled. */
+}
diff --git a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c
new file mode 100644
index 0000000..5262134
--- /dev/null
+++ b/arch/sparc/kernel/sun4d_irq.c
@@ -0,0 +1,594 @@
+/*  $Id: sun4d_irq.c,v 1.29 2001/12/11 04:55:51 davem Exp $
+ *  arch/sparc/kernel/sun4d_irq.c:
+ *			SS1000/SC2000 interrupt handling.
+ *
+ *  Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *  Heavily based on arch/sparc/kernel/irq.c.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/linkage.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <linux/seq_file.h>
+
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/psr.h>
+#include <asm/smp.h>
+#include <asm/vaddrs.h>
+#include <asm/timer.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/traps.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/sbus.h>
+#include <asm/sbi.h>
+#include <asm/cacheflush.h>
+
+/* If you trust current SCSI layer to handle different SCSI IRQs, enable this. I don't trust it... -jj */
+/* #define DISTRIBUTE_IRQS */
+
+struct sun4d_timer_regs *sun4d_timers;
+#define TIMER_IRQ	10
+
+#define MAX_STATIC_ALLOC	4
+extern struct irqaction static_irqaction[MAX_STATIC_ALLOC];
+extern int static_irq_count;
+unsigned char cpu_leds[32];
+#ifdef CONFIG_SMP
+unsigned char sbus_tid[32];
+#endif
+
+extern struct irqaction *irq_action[];
+extern spinlock_t irq_action_lock;
+
+struct sbus_action {
+	struct irqaction *action;
+	/* For SMP this needs to be extended */
+} *sbus_actions;
+
+static int pil_to_sbus[] = {
+	0, 0, 1, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 0,
+};
+
+static int sbus_to_pil[] = {
+	0, 2, 3, 5, 7, 9, 11, 13,
+};
+
+static int nsbi;
+#ifdef CONFIG_SMP
+DEFINE_SPINLOCK(sun4d_imsk_lock);
+#endif
+
+int show_sun4d_interrupts(struct seq_file *p, void *v)
+{
+	int i = *(loff_t *) v, j = 0, k = 0, sbusl;
+	struct irqaction * action;
+	unsigned long flags;
+#ifdef CONFIG_SMP
+	int x;
+#endif
+
+	spin_lock_irqsave(&irq_action_lock, flags);
+	if (i < NR_IRQS) {
+		sbusl = pil_to_sbus[i];
+		if (!sbusl) {
+	 		action = *(i + irq_action);
+			if (!action) 
+		        	goto out_unlock;
+		} else {
+			for (j = 0; j < nsbi; j++) {
+				for (k = 0; k < 4; k++)
+					if ((action = sbus_actions [(j << 5) + (sbusl << 2) + k].action))
+						goto found_it;
+			}
+			goto out_unlock;
+		}
+found_it:	seq_printf(p, "%3d: ", i);
+#ifndef CONFIG_SMP
+		seq_printf(p, "%10u ", kstat_irqs(i));
+#else
+		for (x = 0; x < NR_CPUS; x++) {
+			if (cpu_online(x))
+				seq_printf(p, "%10u ",
+				       kstat_cpu(cpu_logical_map(x)).irqs[i]);
+		}
+#endif
+		seq_printf(p, "%c %s",
+			(action->flags & SA_INTERRUPT) ? '+' : ' ',
+			action->name);
+		action = action->next;
+		for (;;) {
+			for (; action; action = action->next) {
+				seq_printf(p, ",%s %s",
+					(action->flags & SA_INTERRUPT) ? " +" : "",
+					action->name);
+			}
+			if (!sbusl) break;
+			k++;
+			if (k < 4)
+				action = sbus_actions [(j << 5) + (sbusl << 2) + k].action;
+			else {
+				j++;
+				if (j == nsbi) break;
+				k = 0;
+				action = sbus_actions [(j << 5) + (sbusl << 2)].action;
+			}
+		}
+		seq_putc(p, '\n');
+	}
+out_unlock:
+	spin_unlock_irqrestore(&irq_action_lock, flags);
+	return 0;
+}
+
+void sun4d_free_irq(unsigned int irq, void *dev_id)
+{
+	struct irqaction *action, **actionp;
+	struct irqaction *tmp = NULL;
+        unsigned long flags;
+
+	spin_lock_irqsave(&irq_action_lock, flags);
+	if (irq < 15)
+		actionp = irq + irq_action;
+	else
+		actionp = &(sbus_actions[irq - (1 << 5)].action);
+	action = *actionp;
+	if (!action) {
+		printk("Trying to free free IRQ%d\n",irq);
+		goto out_unlock;
+	}
+	if (dev_id) {
+		for (; action; action = action->next) {
+			if (action->dev_id == dev_id)
+				break;
+			tmp = action;
+		}
+		if (!action) {
+			printk("Trying to free free shared IRQ%d\n",irq);
+			goto out_unlock;
+		}
+	} else if (action->flags & SA_SHIRQ) {
+		printk("Trying to free shared IRQ%d with NULL device ID\n", irq);
+		goto out_unlock;
+	}
+	if (action->flags & SA_STATIC_ALLOC)
+	{
+		/* This interrupt is marked as specially allocated
+		 * so it is a bad idea to free it.
+		 */
+		printk("Attempt to free statically allocated IRQ%d (%s)\n",
+		       irq, action->name);
+		goto out_unlock;
+	}
+	
+	if (action && tmp)
+		tmp->next = action->next;
+	else
+		*actionp = action->next;
+
+	spin_unlock_irqrestore(&irq_action_lock, flags);
+
+	synchronize_irq(irq);
+
+	spin_lock_irqsave(&irq_action_lock, flags);
+
+	kfree(action);
+
+	if (!(*actionp))
+		disable_irq(irq);
+
+out_unlock:
+	spin_unlock_irqrestore(&irq_action_lock, flags);
+}
+
+extern void unexpected_irq(int, void *, struct pt_regs *);
+
+void sun4d_handler_irq(int irq, struct pt_regs * regs)
+{
+	struct irqaction * action;
+	int cpu = smp_processor_id();
+	/* SBUS IRQ level (1 - 7) */
+	int sbusl = pil_to_sbus[irq];
+	
+	/* FIXME: Is this necessary?? */
+	cc_get_ipen();
+	
+	cc_set_iclr(1 << irq);
+	
+	irq_enter();
+	kstat_cpu(cpu).irqs[irq]++;
+	if (!sbusl) {
+		action = *(irq + irq_action);
+		if (!action)
+			unexpected_irq(irq, NULL, regs);
+		do {
+			action->handler(irq, action->dev_id, regs);
+			action = action->next;
+		} while (action);
+	} else {
+		int bus_mask = bw_get_intr_mask(sbusl) & 0x3ffff;
+		int sbino;
+		struct sbus_action *actionp;
+		unsigned mask, slot;
+		int sbil = (sbusl << 2);
+		
+		bw_clear_intr_mask(sbusl, bus_mask);
+		
+		/* Loop for each pending SBI */
+		for (sbino = 0; bus_mask; sbino++, bus_mask >>= 1)
+			if (bus_mask & 1) {
+				mask = acquire_sbi(SBI2DEVID(sbino), 0xf << sbil);
+				mask &= (0xf << sbil);
+				actionp = sbus_actions + (sbino << 5) + (sbil);
+				/* Loop for each pending SBI slot */
+				for (slot = (1 << sbil); mask; slot <<= 1, actionp++)
+					if (mask & slot) {
+						mask &= ~slot;
+						action = actionp->action;
+						
+						if (!action)
+							unexpected_irq(irq, NULL, regs);
+						do {
+							action->handler(irq, action->dev_id, regs);
+							action = action->next;
+						} while (action);
+						release_sbi(SBI2DEVID(sbino), slot);
+					}
+			}
+	}
+	irq_exit();
+}
+
+unsigned int sun4d_build_irq(struct sbus_dev *sdev, int irq)
+{
+	int sbusl = pil_to_sbus[irq];
+
+	if (sbusl)
+		return ((sdev->bus->board + 1) << 5) + (sbusl << 2) + sdev->slot;
+	else
+		return irq;
+}
+
+unsigned int sun4d_sbint_to_irq(struct sbus_dev *sdev, unsigned int sbint)
+{
+	if (sbint >= sizeof(sbus_to_pil)) {
+		printk(KERN_ERR "%s: bogus SBINT %d\n", sdev->prom_name, sbint);
+		BUG();
+	}
+	return sun4d_build_irq(sdev, sbus_to_pil[sbint]);
+}
+
+int sun4d_request_irq(unsigned int irq,
+		irqreturn_t (*handler)(int, void *, struct pt_regs *),
+		unsigned long irqflags, const char * devname, void *dev_id)
+{
+	struct irqaction *action, *tmp = NULL, **actionp;
+	unsigned long flags;
+	int ret;
+	
+	if(irq > 14 && irq < (1 << 5)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (!handler) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	spin_lock_irqsave(&irq_action_lock, flags);
+
+	if (irq >= (1 << 5))
+		actionp = &(sbus_actions[irq - (1 << 5)].action);
+	else
+		actionp = irq + irq_action;
+	action = *actionp;
+	
+	if (action) {
+		if ((action->flags & SA_SHIRQ) && (irqflags & SA_SHIRQ)) {
+			for (tmp = action; tmp->next; tmp = tmp->next);
+		} else {
+			ret = -EBUSY;
+			goto out_unlock;
+		}
+		if ((action->flags & SA_INTERRUPT) ^ (irqflags & SA_INTERRUPT)) {
+			printk("Attempt to mix fast and slow interrupts on IRQ%d denied\n", irq);
+			ret = -EBUSY;
+			goto out_unlock;
+		}
+		action = NULL;		/* Or else! */
+	}
+
+	/* If this is flagged as statically allocated then we use our
+	 * private struct which is never freed.
+	 */
+	if (irqflags & SA_STATIC_ALLOC) {
+		if (static_irq_count < MAX_STATIC_ALLOC)
+			action = &static_irqaction[static_irq_count++];
+		else
+			printk("Request for IRQ%d (%s) SA_STATIC_ALLOC failed using kmalloc\n", irq, devname);
+	}
+	
+	if (action == NULL)
+		action = (struct irqaction *)kmalloc(sizeof(struct irqaction),
+						     GFP_ATOMIC);
+	
+	if (!action) { 
+		ret = -ENOMEM;
+		goto out_unlock;
+	}
+
+	action->handler = handler;
+	action->flags = irqflags;
+	cpus_clear(action->mask);
+	action->name = devname;
+	action->next = NULL;
+	action->dev_id = dev_id;
+
+	if (tmp)
+		tmp->next = action;
+	else
+		*actionp = action;
+		
+	enable_irq(irq);
+
+	ret = 0;
+out_unlock:
+	spin_unlock_irqrestore(&irq_action_lock, flags);
+out:
+	return ret;
+}
+
+static void sun4d_disable_irq(unsigned int irq)
+{
+#ifdef CONFIG_SMP
+	int tid = sbus_tid[(irq >> 5) - 1];
+	unsigned long flags;
+#endif	
+	
+	if (irq < NR_IRQS) return;
+#ifdef CONFIG_SMP
+	spin_lock_irqsave(&sun4d_imsk_lock, flags);
+	cc_set_imsk_other(tid, cc_get_imsk_other(tid) | (1 << sbus_to_pil[(irq >> 2) & 7]));
+	spin_unlock_irqrestore(&sun4d_imsk_lock, flags);
+#else		
+	cc_set_imsk(cc_get_imsk() | (1 << sbus_to_pil[(irq >> 2) & 7]));
+#endif
+}
+
+static void sun4d_enable_irq(unsigned int irq)
+{
+#ifdef CONFIG_SMP
+	int tid = sbus_tid[(irq >> 5) - 1];
+	unsigned long flags;
+#endif	
+	
+	if (irq < NR_IRQS) return;
+#ifdef CONFIG_SMP
+	spin_lock_irqsave(&sun4d_imsk_lock, flags);
+	cc_set_imsk_other(tid, cc_get_imsk_other(tid) & ~(1 << sbus_to_pil[(irq >> 2) & 7]));
+	spin_unlock_irqrestore(&sun4d_imsk_lock, flags);
+#else		
+	cc_set_imsk(cc_get_imsk() & ~(1 << sbus_to_pil[(irq >> 2) & 7]));
+#endif
+}
+
+#ifdef CONFIG_SMP
+static void sun4d_set_cpu_int(int cpu, int level)
+{
+	sun4d_send_ipi(cpu, level);
+}
+
+static void sun4d_clear_ipi(int cpu, int level)
+{
+}
+
+static void sun4d_set_udt(int cpu)
+{
+}
+
+/* Setup IRQ distribution scheme. */
+void __init sun4d_distribute_irqs(void)
+{
+#ifdef DISTRIBUTE_IRQS
+	struct sbus_bus *sbus;
+	unsigned long sbus_serving_map;
+
+	sbus_serving_map = cpu_present_map;
+	for_each_sbus(sbus) {
+		if ((sbus->board * 2) == boot_cpu_id && (cpu_present_map & (1 << (sbus->board * 2 + 1))))
+			sbus_tid[sbus->board] = (sbus->board * 2 + 1);
+		else if (cpu_present_map & (1 << (sbus->board * 2)))
+			sbus_tid[sbus->board] = (sbus->board * 2);
+		else if (cpu_present_map & (1 << (sbus->board * 2 + 1)))
+			sbus_tid[sbus->board] = (sbus->board * 2 + 1);
+		else
+			sbus_tid[sbus->board] = 0xff;
+		if (sbus_tid[sbus->board] != 0xff)
+			sbus_serving_map &= ~(1 << sbus_tid[sbus->board]);
+	}
+	for_each_sbus(sbus)
+		if (sbus_tid[sbus->board] == 0xff) {
+			int i = 31;
+				
+			if (!sbus_serving_map)
+				sbus_serving_map = cpu_present_map;
+			while (!(sbus_serving_map & (1 << i)))
+				i--;
+			sbus_tid[sbus->board] = i;
+			sbus_serving_map &= ~(1 << i);
+		}
+	for_each_sbus(sbus) {
+		printk("sbus%d IRQs directed to CPU%d\n", sbus->board, sbus_tid[sbus->board]);
+		set_sbi_tid(sbus->devid, sbus_tid[sbus->board] << 3);
+	}
+#else
+	struct sbus_bus *sbus;
+	int cpuid = cpu_logical_map(1);
+
+	if (cpuid == -1)
+		cpuid = cpu_logical_map(0);
+	for_each_sbus(sbus) {
+		sbus_tid[sbus->board] = cpuid;
+		set_sbi_tid(sbus->devid, cpuid << 3);
+	}
+	printk("All sbus IRQs directed to CPU%d\n", cpuid);
+#endif
+}
+#endif
+ 
+static void sun4d_clear_clock_irq(void)
+{
+	volatile unsigned int clear_intr;
+	clear_intr = sun4d_timers->l10_timer_limit;
+}
+
+static void sun4d_clear_profile_irq(int cpu)
+{
+	bw_get_prof_limit(cpu);
+}
+
+static void sun4d_load_profile_irq(int cpu, unsigned int limit)
+{
+	bw_set_prof_limit(cpu, limit);
+}
+
+static void __init sun4d_init_timers(irqreturn_t (*counter_fn)(int, void *, struct pt_regs *))
+{
+	int irq;
+	int cpu;
+	struct resource r;
+	int mid;
+
+	/* Map the User Timer registers. */
+	memset(&r, 0, sizeof(r));
+#ifdef CONFIG_SMP
+	r.start = CSR_BASE(boot_cpu_id)+BW_TIMER_LIMIT;
+#else
+	r.start = CSR_BASE(0)+BW_TIMER_LIMIT;
+#endif
+	r.flags = 0xf;
+	sun4d_timers = (struct sun4d_timer_regs *) sbus_ioremap(&r, 0,
+	    PAGE_SIZE, "user timer");
+
+	sun4d_timers->l10_timer_limit =  (((1000000/HZ) + 1) << 10);
+	master_l10_counter = &sun4d_timers->l10_cur_count;
+	master_l10_limit = &sun4d_timers->l10_timer_limit;
+
+	irq = request_irq(TIMER_IRQ,
+			  counter_fn,
+			  (SA_INTERRUPT | SA_STATIC_ALLOC),
+			  "timer", NULL);
+	if (irq) {
+		prom_printf("time_init: unable to attach IRQ%d\n",TIMER_IRQ);
+		prom_halt();
+	}
+	
+	/* Enable user timer free run for CPU 0 in BW */
+	/* bw_set_ctrl(0, bw_get_ctrl(0) | BW_CTRL_USER_TIMER); */
+
+	cpu = 0;
+	while (!cpu_find_by_instance(cpu, NULL, &mid)) {
+		sun4d_load_profile_irq(mid >> 3, 0);
+		cpu++;
+	}
+		
+#ifdef CONFIG_SMP
+	{
+		unsigned long flags;
+		extern unsigned long lvl14_save[4];
+		struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (14 - 1)];
+		extern unsigned int real_irq_entry[], smp4d_ticker[];
+		extern unsigned int patchme_maybe_smp_msg[];
+
+		/* Adjust so that we jump directly to smp4d_ticker */
+		lvl14_save[2] += smp4d_ticker - real_irq_entry;
+
+		/* For SMP we use the level 14 ticker, however the bootup code
+		 * has copied the firmwares level 14 vector into boot cpu's
+		 * trap table, we must fix this now or we get squashed.
+		 */
+		local_irq_save(flags);
+		patchme_maybe_smp_msg[0] = 0x01000000; /* NOP out the branch */
+		trap_table->inst_one = lvl14_save[0];
+		trap_table->inst_two = lvl14_save[1];
+		trap_table->inst_three = lvl14_save[2];
+		trap_table->inst_four = lvl14_save[3];
+		local_flush_cache_all();
+		local_irq_restore(flags);
+	}
+#endif
+}
+
+void __init sun4d_init_sbi_irq(void)
+{
+	struct sbus_bus *sbus;
+	unsigned mask;
+
+	nsbi = 0;
+	for_each_sbus(sbus)
+		nsbi++;
+	sbus_actions = (struct sbus_action *)kmalloc (nsbi * 8 * 4 * sizeof(struct sbus_action), GFP_ATOMIC);
+	memset (sbus_actions, 0, (nsbi * 8 * 4 * sizeof(struct sbus_action)));
+	for_each_sbus(sbus) {
+#ifdef CONFIG_SMP	
+		extern unsigned char boot_cpu_id;
+		
+		set_sbi_tid(sbus->devid, boot_cpu_id << 3);
+		sbus_tid[sbus->board] = boot_cpu_id;
+#endif
+		/* Get rid of pending irqs from PROM */
+		mask = acquire_sbi(sbus->devid, 0xffffffff);
+		if (mask) {
+			printk ("Clearing pending IRQs %08x on SBI %d\n", mask, sbus->board);
+			release_sbi(sbus->devid, mask);
+		}
+	}
+}
+
+static char *sun4d_irq_itoa(unsigned int irq)
+{
+	static char buff[16];
+	
+	if (irq < (1 << 5))
+		sprintf(buff, "%d", irq);
+	else
+		sprintf(buff, "%d,%x", sbus_to_pil[(irq >> 2) & 7], irq);
+	return buff;
+}
+
+void __init sun4d_init_IRQ(void)
+{
+	local_irq_disable();
+
+	BTFIXUPSET_CALL(sbint_to_irq, sun4d_sbint_to_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(enable_irq, sun4d_enable_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(disable_irq, sun4d_disable_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(clear_clock_irq, sun4d_clear_clock_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(clear_profile_irq, sun4d_clear_profile_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(load_profile_irq, sun4d_load_profile_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(__irq_itoa, sun4d_irq_itoa, BTFIXUPCALL_NORM);
+	sparc_init_timers = sun4d_init_timers;
+#ifdef CONFIG_SMP
+	BTFIXUPSET_CALL(set_cpu_int, sun4d_set_cpu_int, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(clear_cpu_int, sun4d_clear_ipi, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(set_irq_udt, sun4d_set_udt, BTFIXUPCALL_NOP);
+#endif
+	/* Cannot enable interrupts until OBP ticker is disabled. */
+}
diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c
new file mode 100644
index 0000000..cc1fc89
--- /dev/null
+++ b/arch/sparc/kernel/sun4d_smp.c
@@ -0,0 +1,486 @@
+/* sun4d_smp.c: Sparc SS1000/SC2000 SMP support.
+ *
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *
+ * Based on sun4m's smp.c, which is:
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/head.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/threads.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/profile.h>
+
+#include <asm/ptrace.h>
+#include <asm/atomic.h>
+
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/oplib.h>
+#include <asm/sbus.h>
+#include <asm/sbi.h>
+#include <asm/tlbflush.h>
+#include <asm/cacheflush.h>
+#include <asm/cpudata.h>
+
+#define IRQ_CROSS_CALL		15
+
+extern ctxd_t *srmmu_ctx_table_phys;
+
+extern void calibrate_delay(void);
+
+extern volatile int smp_processors_ready;
+extern int smp_num_cpus;
+static int smp_highest_cpu;
+extern volatile unsigned long cpu_callin_map[NR_CPUS];
+extern struct cpuinfo_sparc cpu_data[NR_CPUS];
+extern unsigned char boot_cpu_id;
+extern int smp_activated;
+extern volatile int __cpu_number_map[NR_CPUS];
+extern volatile int __cpu_logical_map[NR_CPUS];
+extern volatile unsigned long ipi_count;
+extern volatile int smp_process_available;
+extern volatile int smp_commenced;
+extern int __smp4d_processor_id(void);
+
+/* #define SMP_DEBUG */
+
+#ifdef SMP_DEBUG
+#define SMP_PRINTK(x)	printk x
+#else
+#define SMP_PRINTK(x)
+#endif
+
+static inline unsigned long swap(volatile unsigned long *ptr, unsigned long val)
+{
+	__asm__ __volatile__("swap [%1], %0\n\t" :
+			     "=&r" (val), "=&r" (ptr) :
+			     "0" (val), "1" (ptr));
+	return val;
+}
+
+static void smp_setup_percpu_timer(void);
+extern void cpu_probe(void);
+extern void sun4d_distribute_irqs(void);
+
+void __init smp4d_callin(void)
+{
+	int cpuid = hard_smp4d_processor_id();
+	extern spinlock_t sun4d_imsk_lock;
+	unsigned long flags;
+	
+	/* Show we are alive */
+	cpu_leds[cpuid] = 0x6;
+	show_leds(cpuid);
+
+	/* Enable level15 interrupt, disable level14 interrupt for now */
+	cc_set_imsk((cc_get_imsk() & ~0x8000) | 0x4000);
+
+	local_flush_cache_all();
+	local_flush_tlb_all();
+
+	/*
+	 * Unblock the master CPU _only_ when the scheduler state
+	 * of all secondary CPUs will be up-to-date, so after
+	 * the SMP initialization the master will be just allowed
+	 * to call the scheduler code.
+	 */
+	/* Get our local ticker going. */
+	smp_setup_percpu_timer();
+
+	calibrate_delay();
+	smp_store_cpu_info(cpuid);
+	local_flush_cache_all();
+	local_flush_tlb_all();
+
+	/* Allow master to continue. */
+	swap((unsigned long *)&cpu_callin_map[cpuid], 1);
+	local_flush_cache_all();
+	local_flush_tlb_all();
+	
+	cpu_probe();
+
+	while((unsigned long)current_set[cpuid] < PAGE_OFFSET)
+		barrier();
+		
+	while(current_set[cpuid]->cpu != cpuid)
+		barrier();
+		
+	/* Fix idle thread fields. */
+	__asm__ __volatile__("ld [%0], %%g6\n\t"
+			     : : "r" (&current_set[cpuid])
+			     : "memory" /* paranoid */);
+
+	cpu_leds[cpuid] = 0x9;
+	show_leds(cpuid);
+	
+	/* Attach to the address space of init_task. */
+	atomic_inc(&init_mm.mm_count);
+	current->active_mm = &init_mm;
+
+	local_flush_cache_all();
+	local_flush_tlb_all();
+	
+	local_irq_enable();	/* We don't allow PIL 14 yet */
+	
+	while(!smp_commenced)
+		barrier();
+
+	spin_lock_irqsave(&sun4d_imsk_lock, flags);
+	cc_set_imsk(cc_get_imsk() & ~0x4000); /* Allow PIL 14 as well */
+	spin_unlock_irqrestore(&sun4d_imsk_lock, flags);
+}
+
+extern void init_IRQ(void);
+extern void cpu_panic(void);
+
+/*
+ *	Cycle through the processors asking the PROM to start each one.
+ */
+ 
+extern struct linux_prom_registers smp_penguin_ctable;
+extern unsigned long trapbase_cpu1[];
+extern unsigned long trapbase_cpu2[];
+extern unsigned long trapbase_cpu3[];
+
+void __init smp4d_boot_cpus(void)
+{
+	int cpucount = 0;
+	int i, mid;
+
+	printk("Entering SMP Mode...\n");
+	
+	if (boot_cpu_id)
+		current_set[0] = NULL;
+
+	local_irq_enable();
+	cpus_clear(cpu_present_map);
+
+	/* XXX This whole thing has to go.  See sparc64. */
+	for (i = 0; !cpu_find_by_instance(i, NULL, &mid); i++)
+		cpu_set(mid, cpu_present_map);
+	SMP_PRINTK(("cpu_present_map %08lx\n", cpus_addr(cpu_present_map)[0]));
+	for(i=0; i < NR_CPUS; i++)
+		__cpu_number_map[i] = -1;
+	for(i=0; i < NR_CPUS; i++)
+		__cpu_logical_map[i] = -1;
+	__cpu_number_map[boot_cpu_id] = 0;
+	__cpu_logical_map[0] = boot_cpu_id;
+	current_thread_info()->cpu = boot_cpu_id;
+	smp_store_cpu_info(boot_cpu_id);
+	smp_setup_percpu_timer();
+	local_flush_cache_all();
+	if (cpu_find_by_instance(1, NULL, NULL))
+		return;  /* Not an MP box. */
+	SMP_PRINTK(("Iterating over CPUs\n"));
+	for(i = 0; i < NR_CPUS; i++) {
+		if(i == boot_cpu_id)
+			continue;
+
+		if (cpu_isset(i, cpu_present_map)) {
+			extern unsigned long sun4d_cpu_startup;
+			unsigned long *entry = &sun4d_cpu_startup;
+			struct task_struct *p;
+			int timeout;
+			int no;
+
+			/* Cook up an idler for this guy. */
+			p = fork_idle(i);
+			cpucount++;
+			current_set[i] = p->thread_info;
+			for (no = 0; !cpu_find_by_instance(no, NULL, &mid)
+				     && mid != i; no++) ;
+
+			/*
+			 * Initialize the contexts table
+			 * Since the call to prom_startcpu() trashes the structure,
+			 * we need to re-initialize it for each cpu
+			 */
+			smp_penguin_ctable.which_io = 0;
+			smp_penguin_ctable.phys_addr = (unsigned int) srmmu_ctx_table_phys;
+			smp_penguin_ctable.reg_size = 0;
+
+			/* whirrr, whirrr, whirrrrrrrrr... */
+			SMP_PRINTK(("Starting CPU %d at %p task %d node %08x\n", i, entry, cpucount, cpu_data(no).prom_node));
+			local_flush_cache_all();
+			prom_startcpu(cpu_data(no).prom_node,
+				      &smp_penguin_ctable, 0, (char *)entry);
+				      
+			SMP_PRINTK(("prom_startcpu returned :)\n"));
+
+			/* wheee... it's going... */
+			for(timeout = 0; timeout < 10000; timeout++) {
+				if(cpu_callin_map[i])
+					break;
+				udelay(200);
+			}
+			
+			if(cpu_callin_map[i]) {
+				/* Another "Red Snapper". */
+				__cpu_number_map[i] = cpucount;
+				__cpu_logical_map[cpucount] = i;
+			} else {
+				cpucount--;
+				printk("Processor %d is stuck.\n", i);
+			}
+		}
+		if(!(cpu_callin_map[i])) {
+			cpu_clear(i, cpu_present_map);
+			__cpu_number_map[i] = -1;
+		}
+	}
+	local_flush_cache_all();
+	if(cpucount == 0) {
+		printk("Error: only one Processor found.\n");
+		cpu_present_map = cpumask_of_cpu(hard_smp4d_processor_id());
+	} else {
+		unsigned long bogosum = 0;
+		
+		for(i = 0; i < NR_CPUS; i++) {
+			if (cpu_isset(i, cpu_present_map)) {
+				bogosum += cpu_data(i).udelay_val;
+				smp_highest_cpu = i;
+			}
+		}
+		SMP_PRINTK(("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n", cpucount + 1, bogosum/(500000/HZ), (bogosum/(5000/HZ))%100));
+		printk("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n",
+		       cpucount + 1,
+		       bogosum/(500000/HZ),
+		       (bogosum/(5000/HZ))%100);
+		smp_activated = 1;
+		smp_num_cpus = cpucount + 1;
+	}
+
+	/* Free unneeded trap tables */
+	ClearPageReserved(virt_to_page(trapbase_cpu1));
+	set_page_count(virt_to_page(trapbase_cpu1), 1);
+	free_page((unsigned long)trapbase_cpu1);
+	totalram_pages++;
+	num_physpages++;
+
+	ClearPageReserved(virt_to_page(trapbase_cpu2));
+	set_page_count(virt_to_page(trapbase_cpu2), 1);
+	free_page((unsigned long)trapbase_cpu2);
+	totalram_pages++;
+	num_physpages++;
+
+	ClearPageReserved(virt_to_page(trapbase_cpu3));
+	set_page_count(virt_to_page(trapbase_cpu3), 1);
+	free_page((unsigned long)trapbase_cpu3);
+	totalram_pages++;
+	num_physpages++;
+
+	/* Ok, they are spinning and ready to go. */
+	smp_processors_ready = 1;
+	sun4d_distribute_irqs();
+}
+
+static struct smp_funcall {
+	smpfunc_t func;
+	unsigned long arg1;
+	unsigned long arg2;
+	unsigned long arg3;
+	unsigned long arg4;
+	unsigned long arg5;
+	unsigned char processors_in[NR_CPUS];  /* Set when ipi entered. */
+	unsigned char processors_out[NR_CPUS]; /* Set when ipi exited. */
+} ccall_info __attribute__((aligned(8)));
+
+static DEFINE_SPINLOCK(cross_call_lock);
+
+/* Cross calls must be serialized, at least currently. */
+void smp4d_cross_call(smpfunc_t func, unsigned long arg1, unsigned long arg2,
+		    unsigned long arg3, unsigned long arg4, unsigned long arg5)
+{
+	if(smp_processors_ready) {
+		register int high = smp_highest_cpu;
+		unsigned long flags;
+
+		spin_lock_irqsave(&cross_call_lock, flags);
+
+		{
+			/* If you make changes here, make sure gcc generates proper code... */
+			register smpfunc_t f asm("i0") = func;
+			register unsigned long a1 asm("i1") = arg1;
+			register unsigned long a2 asm("i2") = arg2;
+			register unsigned long a3 asm("i3") = arg3;
+			register unsigned long a4 asm("i4") = arg4;
+			register unsigned long a5 asm("i5") = arg5;
+
+			__asm__ __volatile__(
+				"std %0, [%6]\n\t"
+				"std %2, [%6 + 8]\n\t"
+				"std %4, [%6 + 16]\n\t" : :
+				"r"(f), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5),
+				"r" (&ccall_info.func));
+		}
+
+		/* Init receive/complete mapping, plus fire the IPI's off. */
+		{
+			cpumask_t mask;
+			register int i;
+
+			mask = cpumask_of_cpu(hard_smp4d_processor_id());
+			cpus_andnot(mask, cpu_present_map, mask);
+			for(i = 0; i <= high; i++) {
+				if (cpu_isset(i, mask)) {
+					ccall_info.processors_in[i] = 0;
+					ccall_info.processors_out[i] = 0;
+					sun4d_send_ipi(i, IRQ_CROSS_CALL);
+				}
+			}
+		}
+
+		{
+			register int i;
+
+			i = 0;
+			do {
+				while(!ccall_info.processors_in[i])
+					barrier();
+			} while(++i <= high);
+
+			i = 0;
+			do {
+				while(!ccall_info.processors_out[i])
+					barrier();
+			} while(++i <= high);
+		}
+
+		spin_unlock_irqrestore(&cross_call_lock, flags);
+	}
+}
+
+/* Running cross calls. */
+void smp4d_cross_call_irq(void)
+{
+	int i = hard_smp4d_processor_id();
+
+	ccall_info.processors_in[i] = 1;
+	ccall_info.func(ccall_info.arg1, ccall_info.arg2, ccall_info.arg3,
+			ccall_info.arg4, ccall_info.arg5);
+	ccall_info.processors_out[i] = 1;
+}
+
+static int smp4d_stop_cpu_sender;
+
+static void smp4d_stop_cpu(void)
+{
+	int me = hard_smp4d_processor_id();
+	
+	if (me != smp4d_stop_cpu_sender)
+		while(1) barrier();
+}
+
+/* Cross calls, in order to work efficiently and atomically do all
+ * the message passing work themselves, only stopcpu and reschedule
+ * messages come through here.
+ */
+void smp4d_message_pass(int target, int msg, unsigned long data, int wait)
+{
+	int me = hard_smp4d_processor_id();
+
+	SMP_PRINTK(("smp4d_message_pass %d %d %08lx %d\n", target, msg, data, wait));
+	if (msg == MSG_STOP_CPU && target == MSG_ALL_BUT_SELF) {
+		unsigned long flags;
+		static DEFINE_SPINLOCK(stop_cpu_lock);
+		spin_lock_irqsave(&stop_cpu_lock, flags);
+		smp4d_stop_cpu_sender = me;
+		smp4d_cross_call((smpfunc_t)smp4d_stop_cpu, 0, 0, 0, 0, 0);
+		spin_unlock_irqrestore(&stop_cpu_lock, flags);
+	}
+	printk("Yeeee, trying to send SMP msg(%d) to %d on cpu %d\n", msg, target, me);
+	panic("Bogon SMP message pass.");
+}
+
+void smp4d_percpu_timer_interrupt(struct pt_regs *regs)
+{
+	int cpu = hard_smp4d_processor_id();
+	static int cpu_tick[NR_CPUS];
+	static char led_mask[] = { 0xe, 0xd, 0xb, 0x7, 0xb, 0xd };
+
+	bw_get_prof_limit(cpu);	
+	bw_clear_intr_mask(0, 1);	/* INTR_TABLE[0] & 1 is Profile IRQ */
+
+	cpu_tick[cpu]++;
+	if (!(cpu_tick[cpu] & 15)) {
+		if (cpu_tick[cpu] == 0x60)
+			cpu_tick[cpu] = 0;
+		cpu_leds[cpu] = led_mask[cpu_tick[cpu] >> 4];
+		show_leds(cpu);
+	}
+
+	profile_tick(CPU_PROFILING, regs);
+
+	if(!--prof_counter(cpu)) {
+		int user = user_mode(regs);
+
+		irq_enter();
+		update_process_times(user);
+		irq_exit();
+
+		prof_counter(cpu) = prof_multiplier(cpu);
+	}
+}
+
+extern unsigned int lvl14_resolution;
+
+static void __init smp_setup_percpu_timer(void)
+{
+	int cpu = hard_smp4d_processor_id();
+
+	prof_counter(cpu) = prof_multiplier(cpu) = 1;
+	load_profile_irq(cpu, lvl14_resolution);
+}
+
+void __init smp4d_blackbox_id(unsigned *addr)
+{
+	int rd = *addr & 0x3e000000;
+	
+	addr[0] = 0xc0800800 | rd;		/* lda [%g0] ASI_M_VIKING_TMP1, reg */
+	addr[1] = 0x01000000;    		/* nop */
+	addr[2] = 0x01000000;    		/* nop */
+}
+
+void __init smp4d_blackbox_current(unsigned *addr)
+{
+	int rd = *addr & 0x3e000000;
+	
+	addr[0] = 0xc0800800 | rd;		/* lda [%g0] ASI_M_VIKING_TMP1, reg */
+	addr[2] = 0x81282002 | rd | (rd >> 11);	/* sll reg, 2, reg */
+	addr[4] = 0x01000000;			/* nop */
+}
+
+void __init sun4d_init_smp(void)
+{
+	int i;
+	extern unsigned int t_nmi[], linux_trap_ipi15_sun4d[], linux_trap_ipi15_sun4m[];
+
+	/* Patch ipi15 trap table */
+	t_nmi[1] = t_nmi[1] + (linux_trap_ipi15_sun4d - linux_trap_ipi15_sun4m);
+	
+	/* And set btfixup... */
+	BTFIXUPSET_BLACKBOX(hard_smp_processor_id, smp4d_blackbox_id);
+	BTFIXUPSET_BLACKBOX(load_current, smp4d_blackbox_current);
+	BTFIXUPSET_CALL(smp_cross_call, smp4d_cross_call, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(smp_message_pass, smp4d_message_pass, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(__hard_smp_processor_id, __smp4d_processor_id, BTFIXUPCALL_NORM);
+	
+	for (i = 0; i < NR_CPUS; i++) {
+		ccall_info.processors_in[i] = 1;
+		ccall_info.processors_out[i] = 1;
+	}
+}
diff --git a/arch/sparc/kernel/sun4m_irq.c b/arch/sparc/kernel/sun4m_irq.c
new file mode 100644
index 0000000..39d712c
--- /dev/null
+++ b/arch/sparc/kernel/sun4m_irq.c
@@ -0,0 +1,399 @@
+/*  sun4m_irq.c
+ *  arch/sparc/kernel/sun4m_irq.c:
+ *
+ *  djhr: Hacked out of irq.c into a CPU dependent version.
+ *
+ *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *  Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ *  Copyright (C) 1995 Pete A. Zaitcev (zaitcev@yahoo.com)
+ *  Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/linkage.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/psr.h>
+#include <asm/vaddrs.h>
+#include <asm/timer.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/traps.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/smp.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/sbus.h>
+#include <asm/cacheflush.h>
+
+static unsigned long dummy;
+
+struct sun4m_intregs *sun4m_interrupts;
+unsigned long *irq_rcvreg = &dummy;
+
+/* These tables only apply for interrupts greater than 15..
+ * 
+ * any intr value below 0x10 is considered to be a soft-int
+ * this may be useful or it may not.. but that's how I've done it.
+ * and it won't clash with what OBP is telling us about devices.
+ *
+ * take an encoded intr value and lookup if it's valid
+ * then get the mask bits that match from irq_mask
+ *
+ * P3: Translation from irq 0x0d to mask 0x2000 is for MrCoffee.
+ */
+static unsigned char irq_xlate[32] = {
+    /*  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  a,  b,  c,  d,  e,  f */
+	0,  0,  0,  0,  1,  0,  2,  0,  3,  0,  4,  5,  6, 14,  0,  7,
+	0,  0,  8,  9,  0, 10,  0, 11,  0, 12,  0, 13,  0, 14,  0,  0
+};
+
+static unsigned long irq_mask[] = {
+	0,						  /* illegal index */
+	SUN4M_INT_SCSI,				  	  /*  1 irq 4 */
+	SUN4M_INT_ETHERNET,				  /*  2 irq 6 */
+	SUN4M_INT_VIDEO,				  /*  3 irq 8 */
+	SUN4M_INT_REALTIME,				  /*  4 irq 10 */
+	SUN4M_INT_FLOPPY,				  /*  5 irq 11 */
+	(SUN4M_INT_SERIAL | SUN4M_INT_KBDMS),	  	  /*  6 irq 12 */
+	SUN4M_INT_MODULE_ERR,			  	  /*  7 irq 15 */
+	SUN4M_INT_SBUS(0),				  /*  8 irq 2 */
+	SUN4M_INT_SBUS(1),				  /*  9 irq 3 */
+	SUN4M_INT_SBUS(2),				  /* 10 irq 5 */
+	SUN4M_INT_SBUS(3),				  /* 11 irq 7 */
+	SUN4M_INT_SBUS(4),				  /* 12 irq 9 */
+	SUN4M_INT_SBUS(5),				  /* 13 irq 11 */
+	SUN4M_INT_SBUS(6)				  /* 14 irq 13 */
+};
+
+static int sun4m_pil_map[] = { 0, 2, 3, 5, 7, 9, 11, 13 };
+
+unsigned int sun4m_sbint_to_irq(struct sbus_dev *sdev, unsigned int sbint) 
+{
+	if (sbint >= sizeof(sun4m_pil_map)) {
+		printk(KERN_ERR "%s: bogus SBINT %d\n", sdev->prom_name, sbint);
+		BUG();
+	}
+	return sun4m_pil_map[sbint] | 0x30;
+}
+
+inline unsigned long sun4m_get_irqmask(unsigned int irq)
+{
+	unsigned long mask;
+    
+	if (irq > 0x20) {
+		/* OBIO/SBUS interrupts */
+		irq &= 0x1f;
+		mask = irq_mask[irq_xlate[irq]];
+		if (!mask)
+			printk("sun4m_get_irqmask: IRQ%d has no valid mask!\n",irq);
+	} else {
+		/* Soft Interrupts will come here.
+		 * Currently there is no way to trigger them but I'm sure
+		 * something could be cooked up.
+		 */
+		irq &= 0xf;
+		mask = SUN4M_SOFT_INT(irq);
+	}
+	return mask;
+}
+
+static void sun4m_disable_irq(unsigned int irq_nr)
+{
+	unsigned long mask, flags;
+	int cpu = smp_processor_id();
+
+	mask = sun4m_get_irqmask(irq_nr);
+	local_irq_save(flags);
+	if (irq_nr > 15)
+		sun4m_interrupts->set = mask;
+	else
+		sun4m_interrupts->cpu_intregs[cpu].set = mask;
+	local_irq_restore(flags);    
+}
+
+static void sun4m_enable_irq(unsigned int irq_nr)
+{
+	unsigned long mask, flags;
+	int cpu = smp_processor_id();
+
+	/* Dreadful floppy hack. When we use 0x2b instead of
+         * 0x0b the system blows (it starts to whistle!).
+         * So we continue to use 0x0b. Fixme ASAP. --P3
+         */
+        if (irq_nr != 0x0b) {
+		mask = sun4m_get_irqmask(irq_nr);
+		local_irq_save(flags);
+		if (irq_nr > 15)
+			sun4m_interrupts->clear = mask;
+		else
+			sun4m_interrupts->cpu_intregs[cpu].clear = mask;
+		local_irq_restore(flags);    
+	} else {
+		local_irq_save(flags);
+		sun4m_interrupts->clear = SUN4M_INT_FLOPPY;
+		local_irq_restore(flags);
+	}
+}
+
+static unsigned long cpu_pil_to_imask[16] = {
+/*0*/	0x00000000,
+/*1*/	0x00000000,
+/*2*/	SUN4M_INT_SBUS(0) | SUN4M_INT_VME(0),
+/*3*/	SUN4M_INT_SBUS(1) | SUN4M_INT_VME(1),
+/*4*/	SUN4M_INT_SCSI,
+/*5*/	SUN4M_INT_SBUS(2) | SUN4M_INT_VME(2),
+/*6*/	SUN4M_INT_ETHERNET,
+/*7*/	SUN4M_INT_SBUS(3) | SUN4M_INT_VME(3),
+/*8*/	SUN4M_INT_VIDEO,
+/*9*/	SUN4M_INT_SBUS(4) | SUN4M_INT_VME(4) | SUN4M_INT_MODULE_ERR,
+/*10*/	SUN4M_INT_REALTIME,
+/*11*/	SUN4M_INT_SBUS(5) | SUN4M_INT_VME(5) | SUN4M_INT_FLOPPY,
+/*12*/	SUN4M_INT_SERIAL | SUN4M_INT_KBDMS,
+/*13*/	SUN4M_INT_AUDIO,
+/*14*/	SUN4M_INT_E14,
+/*15*/	0x00000000
+};
+
+/* We assume the caller has disabled local interrupts when these are called,
+ * or else very bizarre behavior will result.
+ */
+static void sun4m_disable_pil_irq(unsigned int pil)
+{
+	sun4m_interrupts->set = cpu_pil_to_imask[pil];
+}
+
+static void sun4m_enable_pil_irq(unsigned int pil)
+{
+	sun4m_interrupts->clear = cpu_pil_to_imask[pil];
+}
+
+#ifdef CONFIG_SMP
+static void sun4m_send_ipi(int cpu, int level)
+{
+	unsigned long mask;
+
+	mask = sun4m_get_irqmask(level);
+	sun4m_interrupts->cpu_intregs[cpu].set = mask;
+}
+
+static void sun4m_clear_ipi(int cpu, int level)
+{
+	unsigned long mask;
+
+	mask = sun4m_get_irqmask(level);
+	sun4m_interrupts->cpu_intregs[cpu].clear = mask;
+}
+
+static void sun4m_set_udt(int cpu)
+{
+	sun4m_interrupts->undirected_target = cpu;
+}
+#endif
+
+#define OBIO_INTR	0x20
+#define TIMER_IRQ  	(OBIO_INTR | 10)
+#define PROFILE_IRQ	(OBIO_INTR | 14)
+
+struct sun4m_timer_regs *sun4m_timers;
+unsigned int lvl14_resolution = (((1000000/HZ) + 1) << 10);
+
+static void sun4m_clear_clock_irq(void)
+{
+	volatile unsigned int clear_intr;
+	clear_intr = sun4m_timers->l10_timer_limit;
+}
+
+static void sun4m_clear_profile_irq(int cpu)
+{
+	volatile unsigned int clear;
+    
+	clear = sun4m_timers->cpu_timers[cpu].l14_timer_limit;
+}
+
+static void sun4m_load_profile_irq(int cpu, unsigned int limit)
+{
+	sun4m_timers->cpu_timers[cpu].l14_timer_limit = limit;
+}
+
+char *sun4m_irq_itoa(unsigned int irq)
+{
+	static char buff[16];
+	sprintf(buff, "%d", irq);
+	return buff;
+}
+
+static void __init sun4m_init_timers(irqreturn_t (*counter_fn)(int, void *, struct pt_regs *))
+{
+	int reg_count, irq, cpu;
+	struct linux_prom_registers cnt_regs[PROMREG_MAX];
+	int obio_node, cnt_node;
+	struct resource r;
+
+	cnt_node = 0;
+	if((obio_node =
+	    prom_searchsiblings (prom_getchild(prom_root_node), "obio")) == 0 ||
+	   (obio_node = prom_getchild (obio_node)) == 0 ||
+	   (cnt_node = prom_searchsiblings (obio_node, "counter")) == 0) {
+		prom_printf("Cannot find /obio/counter node\n");
+		prom_halt();
+	}
+	reg_count = prom_getproperty(cnt_node, "reg",
+				     (void *) cnt_regs, sizeof(cnt_regs));
+	reg_count = (reg_count/sizeof(struct linux_prom_registers));
+    
+	/* Apply the obio ranges to the timer registers. */
+	prom_apply_obio_ranges(cnt_regs, reg_count);
+    
+	cnt_regs[4].phys_addr = cnt_regs[reg_count-1].phys_addr;
+	cnt_regs[4].reg_size = cnt_regs[reg_count-1].reg_size;
+	cnt_regs[4].which_io = cnt_regs[reg_count-1].which_io;
+	for(obio_node = 1; obio_node < 4; obio_node++) {
+		cnt_regs[obio_node].phys_addr =
+			cnt_regs[obio_node-1].phys_addr + PAGE_SIZE;
+		cnt_regs[obio_node].reg_size = cnt_regs[obio_node-1].reg_size;
+		cnt_regs[obio_node].which_io = cnt_regs[obio_node-1].which_io;
+	}
+
+	memset((char*)&r, 0, sizeof(struct resource));
+	/* Map the per-cpu Counter registers. */
+	r.flags = cnt_regs[0].which_io;
+	r.start = cnt_regs[0].phys_addr;
+	sun4m_timers = (struct sun4m_timer_regs *) sbus_ioremap(&r, 0,
+	    PAGE_SIZE*SUN4M_NCPUS, "sun4m_cpu_cnt");
+	/* Map the system Counter register. */
+	/* XXX Here we expect consequent calls to yeld adjusent maps. */
+	r.flags = cnt_regs[4].which_io;
+	r.start = cnt_regs[4].phys_addr;
+	sbus_ioremap(&r, 0, cnt_regs[4].reg_size, "sun4m_sys_cnt");
+
+	sun4m_timers->l10_timer_limit =  (((1000000/HZ) + 1) << 10);
+	master_l10_counter = &sun4m_timers->l10_cur_count;
+	master_l10_limit = &sun4m_timers->l10_timer_limit;
+
+	irq = request_irq(TIMER_IRQ,
+			  counter_fn,
+			  (SA_INTERRUPT | SA_STATIC_ALLOC),
+			  "timer", NULL);
+	if (irq) {
+		prom_printf("time_init: unable to attach IRQ%d\n",TIMER_IRQ);
+		prom_halt();
+	}
+   
+	if (!cpu_find_by_instance(1, NULL, NULL)) {
+		for(cpu = 0; cpu < 4; cpu++)
+			sun4m_timers->cpu_timers[cpu].l14_timer_limit = 0;
+		sun4m_interrupts->set = SUN4M_INT_E14;
+	} else {
+		sun4m_timers->cpu_timers[0].l14_timer_limit = 0;
+	}
+#ifdef CONFIG_SMP
+	{
+		unsigned long flags;
+		extern unsigned long lvl14_save[4];
+		struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (14 - 1)];
+
+		/* For SMP we use the level 14 ticker, however the bootup code
+		 * has copied the firmwares level 14 vector into boot cpu's
+		 * trap table, we must fix this now or we get squashed.
+		 */
+		local_irq_save(flags);
+		trap_table->inst_one = lvl14_save[0];
+		trap_table->inst_two = lvl14_save[1];
+		trap_table->inst_three = lvl14_save[2];
+		trap_table->inst_four = lvl14_save[3];
+		local_flush_cache_all();
+		local_irq_restore(flags);
+	}
+#endif
+}
+
+void __init sun4m_init_IRQ(void)
+{
+	int ie_node,i;
+	struct linux_prom_registers int_regs[PROMREG_MAX];
+	int num_regs;
+	struct resource r;
+	int mid;
+    
+	local_irq_disable();
+	if((ie_node = prom_searchsiblings(prom_getchild(prom_root_node), "obio")) == 0 ||
+	   (ie_node = prom_getchild (ie_node)) == 0 ||
+	   (ie_node = prom_searchsiblings (ie_node, "interrupt")) == 0) {
+		prom_printf("Cannot find /obio/interrupt node\n");
+		prom_halt();
+	}
+	num_regs = prom_getproperty(ie_node, "reg", (char *) int_regs,
+				    sizeof(int_regs));
+	num_regs = (num_regs/sizeof(struct linux_prom_registers));
+    
+	/* Apply the obio ranges to these registers. */
+	prom_apply_obio_ranges(int_regs, num_regs);
+    
+	int_regs[4].phys_addr = int_regs[num_regs-1].phys_addr;
+	int_regs[4].reg_size = int_regs[num_regs-1].reg_size;
+	int_regs[4].which_io = int_regs[num_regs-1].which_io;
+	for(ie_node = 1; ie_node < 4; ie_node++) {
+		int_regs[ie_node].phys_addr = int_regs[ie_node-1].phys_addr + PAGE_SIZE;
+		int_regs[ie_node].reg_size = int_regs[ie_node-1].reg_size;
+		int_regs[ie_node].which_io = int_regs[ie_node-1].which_io;
+	}
+
+	memset((char *)&r, 0, sizeof(struct resource));
+	/* Map the interrupt registers for all possible cpus. */
+	r.flags = int_regs[0].which_io;
+	r.start = int_regs[0].phys_addr;
+	sun4m_interrupts = (struct sun4m_intregs *) sbus_ioremap(&r, 0,
+	    PAGE_SIZE*SUN4M_NCPUS, "interrupts_percpu");
+
+	/* Map the system interrupt control registers. */
+	r.flags = int_regs[4].which_io;
+	r.start = int_regs[4].phys_addr;
+	sbus_ioremap(&r, 0, int_regs[4].reg_size, "interrupts_system");
+
+	sun4m_interrupts->set = ~SUN4M_INT_MASKALL;
+	for (i = 0; !cpu_find_by_instance(i, NULL, &mid); i++)
+		sun4m_interrupts->cpu_intregs[mid].clear = ~0x17fff;
+
+	if (!cpu_find_by_instance(1, NULL, NULL)) {
+		/* system wide interrupts go to cpu 0, this should always
+		 * be safe because it is guaranteed to be fitted or OBP doesn't
+		 * come up
+		 *
+		 * Not sure, but writing here on SLAVIO systems may puke
+		 * so I don't do it unless there is more than 1 cpu.
+		 */
+		irq_rcvreg = (unsigned long *)
+				&sun4m_interrupts->undirected_target;
+		sun4m_interrupts->undirected_target = 0;
+	}
+	BTFIXUPSET_CALL(sbint_to_irq, sun4m_sbint_to_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(enable_irq, sun4m_enable_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(disable_irq, sun4m_disable_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(enable_pil_irq, sun4m_enable_pil_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(disable_pil_irq, sun4m_disable_pil_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(clear_clock_irq, sun4m_clear_clock_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(clear_profile_irq, sun4m_clear_profile_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(load_profile_irq, sun4m_load_profile_irq, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(__irq_itoa, sun4m_irq_itoa, BTFIXUPCALL_NORM);
+	sparc_init_timers = sun4m_init_timers;
+#ifdef CONFIG_SMP
+	BTFIXUPSET_CALL(set_cpu_int, sun4m_send_ipi, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(clear_cpu_int, sun4m_clear_ipi, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(set_irq_udt, sun4m_set_udt, BTFIXUPCALL_NORM);
+#endif
+	/* Cannot enable interrupts until OBP ticker is disabled. */
+}
diff --git a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c
new file mode 100644
index 0000000..f113422
--- /dev/null
+++ b/arch/sparc/kernel/sun4m_smp.c
@@ -0,0 +1,451 @@
+/* sun4m_smp.c: Sparc SUN4M SMP support.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/head.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/threads.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/profile.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+
+#include <asm/ptrace.h>
+#include <asm/atomic.h>
+
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/oplib.h>
+#include <asm/cpudata.h>
+
+#define IRQ_RESCHEDULE		13
+#define IRQ_STOP_CPU		14
+#define IRQ_CROSS_CALL		15
+
+extern ctxd_t *srmmu_ctx_table_phys;
+
+extern void calibrate_delay(void);
+
+extern volatile int smp_processors_ready;
+extern int smp_num_cpus;
+extern volatile unsigned long cpu_callin_map[NR_CPUS];
+extern unsigned char boot_cpu_id;
+extern int smp_activated;
+extern volatile int __cpu_number_map[NR_CPUS];
+extern volatile int __cpu_logical_map[NR_CPUS];
+extern volatile unsigned long ipi_count;
+extern volatile int smp_process_available;
+extern volatile int smp_commenced;
+extern int __smp4m_processor_id(void);
+
+/*#define SMP_DEBUG*/
+
+#ifdef SMP_DEBUG
+#define SMP_PRINTK(x)	printk x
+#else
+#define SMP_PRINTK(x)
+#endif
+
+static inline unsigned long swap(volatile unsigned long *ptr, unsigned long val)
+{
+	__asm__ __volatile__("swap [%1], %0\n\t" :
+			     "=&r" (val), "=&r" (ptr) :
+			     "0" (val), "1" (ptr));
+	return val;
+}
+
+static void smp_setup_percpu_timer(void);
+extern void cpu_probe(void);
+
+void __init smp4m_callin(void)
+{
+	int cpuid = hard_smp_processor_id();
+
+	local_flush_cache_all();
+	local_flush_tlb_all();
+
+	set_irq_udt(boot_cpu_id);
+
+	/* Get our local ticker going. */
+	smp_setup_percpu_timer();
+
+	calibrate_delay();
+	smp_store_cpu_info(cpuid);
+
+	local_flush_cache_all();
+	local_flush_tlb_all();
+
+	/*
+	 * Unblock the master CPU _only_ when the scheduler state
+	 * of all secondary CPUs will be up-to-date, so after
+	 * the SMP initialization the master will be just allowed
+	 * to call the scheduler code.
+	 */
+	/* Allow master to continue. */
+	swap((unsigned long *)&cpu_callin_map[cpuid], 1);
+
+	local_flush_cache_all();
+	local_flush_tlb_all();
+	
+	cpu_probe();
+
+	/* Fix idle thread fields. */
+	__asm__ __volatile__("ld [%0], %%g6\n\t"
+			     : : "r" (&current_set[cpuid])
+			     : "memory" /* paranoid */);
+
+	/* Attach to the address space of init_task. */
+	atomic_inc(&init_mm.mm_count);
+	current->active_mm = &init_mm;
+
+	while(!smp_commenced)
+		barrier();
+
+	local_flush_cache_all();
+	local_flush_tlb_all();
+
+	local_irq_enable();
+}
+
+extern void init_IRQ(void);
+extern void cpu_panic(void);
+
+/*
+ *	Cycle through the processors asking the PROM to start each one.
+ */
+ 
+extern struct linux_prom_registers smp_penguin_ctable;
+extern unsigned long trapbase_cpu1[];
+extern unsigned long trapbase_cpu2[];
+extern unsigned long trapbase_cpu3[];
+
+void __init smp4m_boot_cpus(void)
+{
+	int cpucount = 0;
+	int i, mid;
+
+	printk("Entering SMP Mode...\n");
+
+	local_irq_enable();
+	cpus_clear(cpu_present_map);
+
+	for (i = 0; !cpu_find_by_instance(i, NULL, &mid); i++)
+		cpu_set(mid, cpu_present_map);
+
+	for(i=0; i < NR_CPUS; i++) {
+		__cpu_number_map[i] = -1;
+		__cpu_logical_map[i] = -1;
+	}
+
+	__cpu_number_map[boot_cpu_id] = 0;
+	__cpu_logical_map[0] = boot_cpu_id;
+	current_thread_info()->cpu = boot_cpu_id;
+
+	smp_store_cpu_info(boot_cpu_id);
+	set_irq_udt(boot_cpu_id);
+	smp_setup_percpu_timer();
+	local_flush_cache_all();
+	if(cpu_find_by_instance(1, NULL, NULL))
+		return;  /* Not an MP box. */
+	for(i = 0; i < NR_CPUS; i++) {
+		if(i == boot_cpu_id)
+			continue;
+
+		if (cpu_isset(i, cpu_present_map)) {
+			extern unsigned long sun4m_cpu_startup;
+			unsigned long *entry = &sun4m_cpu_startup;
+			struct task_struct *p;
+			int timeout;
+
+			/* Cook up an idler for this guy. */
+			p = fork_idle(i);
+			cpucount++;
+			current_set[i] = p->thread_info;
+			/* See trampoline.S for details... */
+			entry += ((i-1) * 3);
+
+			/*
+			 * Initialize the contexts table
+			 * Since the call to prom_startcpu() trashes the structure,
+			 * we need to re-initialize it for each cpu
+			 */
+			smp_penguin_ctable.which_io = 0;
+			smp_penguin_ctable.phys_addr = (unsigned int) srmmu_ctx_table_phys;
+			smp_penguin_ctable.reg_size = 0;
+
+			/* whirrr, whirrr, whirrrrrrrrr... */
+			printk("Starting CPU %d at %p\n", i, entry);
+			local_flush_cache_all();
+			prom_startcpu(cpu_data(i).prom_node,
+				      &smp_penguin_ctable, 0, (char *)entry);
+
+			/* wheee... it's going... */
+			for(timeout = 0; timeout < 10000; timeout++) {
+				if(cpu_callin_map[i])
+					break;
+				udelay(200);
+			}
+			if(cpu_callin_map[i]) {
+				/* Another "Red Snapper". */
+				__cpu_number_map[i] = i;
+				__cpu_logical_map[i] = i;
+			} else {
+				cpucount--;
+				printk("Processor %d is stuck.\n", i);
+			}
+		}
+		if(!(cpu_callin_map[i])) {
+			cpu_clear(i, cpu_present_map);
+			__cpu_number_map[i] = -1;
+		}
+	}
+	local_flush_cache_all();
+	if(cpucount == 0) {
+		printk("Error: only one Processor found.\n");
+		cpu_present_map = cpumask_of_cpu(smp_processor_id());
+	} else {
+		unsigned long bogosum = 0;
+		for(i = 0; i < NR_CPUS; i++) {
+			if (cpu_isset(i, cpu_present_map))
+				bogosum += cpu_data(i).udelay_val;
+		}
+		printk("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n",
+		       cpucount + 1,
+		       bogosum/(500000/HZ),
+		       (bogosum/(5000/HZ))%100);
+		smp_activated = 1;
+		smp_num_cpus = cpucount + 1;
+	}
+
+	/* Free unneeded trap tables */
+	if (!cpu_isset(i, cpu_present_map)) {
+		ClearPageReserved(virt_to_page(trapbase_cpu1));
+		set_page_count(virt_to_page(trapbase_cpu1), 1);
+		free_page((unsigned long)trapbase_cpu1);
+		totalram_pages++;
+		num_physpages++;
+	}
+	if (!cpu_isset(2, cpu_present_map)) {
+		ClearPageReserved(virt_to_page(trapbase_cpu2));
+		set_page_count(virt_to_page(trapbase_cpu2), 1);
+		free_page((unsigned long)trapbase_cpu2);
+		totalram_pages++;
+		num_physpages++;
+	}
+	if (!cpu_isset(3, cpu_present_map)) {
+		ClearPageReserved(virt_to_page(trapbase_cpu3));
+		set_page_count(virt_to_page(trapbase_cpu3), 1);
+		free_page((unsigned long)trapbase_cpu3);
+		totalram_pages++;
+		num_physpages++;
+	}
+
+	/* Ok, they are spinning and ready to go. */
+	smp_processors_ready = 1;
+}
+
+/* At each hardware IRQ, we get this called to forward IRQ reception
+ * to the next processor.  The caller must disable the IRQ level being
+ * serviced globally so that there are no double interrupts received.
+ *
+ * XXX See sparc64 irq.c.
+ */
+void smp4m_irq_rotate(int cpu)
+{
+}
+
+/* Cross calls, in order to work efficiently and atomically do all
+ * the message passing work themselves, only stopcpu and reschedule
+ * messages come through here.
+ */
+void smp4m_message_pass(int target, int msg, unsigned long data, int wait)
+{
+	static unsigned long smp_cpu_in_msg[NR_CPUS];
+	cpumask_t mask;
+	int me = smp_processor_id();
+	int irq, i;
+
+	if(msg == MSG_RESCHEDULE) {
+		irq = IRQ_RESCHEDULE;
+
+		if(smp_cpu_in_msg[me])
+			return;
+	} else if(msg == MSG_STOP_CPU) {
+		irq = IRQ_STOP_CPU;
+	} else {
+		goto barf;
+	}
+
+	smp_cpu_in_msg[me]++;
+	if(target == MSG_ALL_BUT_SELF || target == MSG_ALL) {
+		mask = cpu_present_map;
+		if(target == MSG_ALL_BUT_SELF)
+			cpu_clear(me, mask);
+		for(i = 0; i < 4; i++) {
+			if (cpu_isset(i, mask))
+				set_cpu_int(i, irq);
+		}
+	} else {
+		set_cpu_int(target, irq);
+	}
+	smp_cpu_in_msg[me]--;
+
+	return;
+barf:
+	printk("Yeeee, trying to send SMP msg(%d) on cpu %d\n", msg, me);
+	panic("Bogon SMP message pass.");
+}
+
+static struct smp_funcall {
+	smpfunc_t func;
+	unsigned long arg1;
+	unsigned long arg2;
+	unsigned long arg3;
+	unsigned long arg4;
+	unsigned long arg5;
+	unsigned long processors_in[NR_CPUS];  /* Set when ipi entered. */
+	unsigned long processors_out[NR_CPUS]; /* Set when ipi exited. */
+} ccall_info;
+
+static DEFINE_SPINLOCK(cross_call_lock);
+
+/* Cross calls must be serialized, at least currently. */
+void smp4m_cross_call(smpfunc_t func, unsigned long arg1, unsigned long arg2,
+		    unsigned long arg3, unsigned long arg4, unsigned long arg5)
+{
+	if(smp_processors_ready) {
+		register int ncpus = smp_num_cpus;
+		unsigned long flags;
+
+		spin_lock_irqsave(&cross_call_lock, flags);
+
+		/* Init function glue. */
+		ccall_info.func = func;
+		ccall_info.arg1 = arg1;
+		ccall_info.arg2 = arg2;
+		ccall_info.arg3 = arg3;
+		ccall_info.arg4 = arg4;
+		ccall_info.arg5 = arg5;
+
+		/* Init receive/complete mapping, plus fire the IPI's off. */
+		{
+			cpumask_t mask = cpu_present_map;
+			register int i;
+
+			cpu_clear(smp_processor_id(), mask);
+			for(i = 0; i < ncpus; i++) {
+				if (cpu_isset(i, mask)) {
+					ccall_info.processors_in[i] = 0;
+					ccall_info.processors_out[i] = 0;
+					set_cpu_int(i, IRQ_CROSS_CALL);
+				} else {
+					ccall_info.processors_in[i] = 1;
+					ccall_info.processors_out[i] = 1;
+				}
+			}
+		}
+
+		{
+			register int i;
+
+			i = 0;
+			do {
+				while(!ccall_info.processors_in[i])
+					barrier();
+			} while(++i < ncpus);
+
+			i = 0;
+			do {
+				while(!ccall_info.processors_out[i])
+					barrier();
+			} while(++i < ncpus);
+		}
+
+		spin_unlock_irqrestore(&cross_call_lock, flags);
+	}
+}
+
+/* Running cross calls. */
+void smp4m_cross_call_irq(void)
+{
+	int i = smp_processor_id();
+
+	ccall_info.processors_in[i] = 1;
+	ccall_info.func(ccall_info.arg1, ccall_info.arg2, ccall_info.arg3,
+			ccall_info.arg4, ccall_info.arg5);
+	ccall_info.processors_out[i] = 1;
+}
+
+void smp4m_percpu_timer_interrupt(struct pt_regs *regs)
+{
+	int cpu = smp_processor_id();
+
+	clear_profile_irq(cpu);
+
+	profile_tick(CPU_PROFILING, regs);
+
+	if(!--prof_counter(cpu)) {
+		int user = user_mode(regs);
+
+		irq_enter();
+		update_process_times(user);
+		irq_exit();
+
+		prof_counter(cpu) = prof_multiplier(cpu);
+	}
+}
+
+extern unsigned int lvl14_resolution;
+
+static void __init smp_setup_percpu_timer(void)
+{
+	int cpu = smp_processor_id();
+
+	prof_counter(cpu) = prof_multiplier(cpu) = 1;
+	load_profile_irq(cpu, lvl14_resolution);
+
+	if(cpu == boot_cpu_id)
+		enable_pil_irq(14);
+}
+
+void __init smp4m_blackbox_id(unsigned *addr)
+{
+	int rd = *addr & 0x3e000000;
+	int rs1 = rd >> 11;
+	
+	addr[0] = 0x81580000 | rd;		/* rd %tbr, reg */
+	addr[1] = 0x8130200c | rd | rs1;    	/* srl reg, 0xc, reg */
+	addr[2] = 0x80082003 | rd | rs1;	/* and reg, 3, reg */
+}
+
+void __init smp4m_blackbox_current(unsigned *addr)
+{
+	int rd = *addr & 0x3e000000;
+	int rs1 = rd >> 11;
+	
+	addr[0] = 0x81580000 | rd;		/* rd %tbr, reg */
+	addr[2] = 0x8130200a | rd | rs1;    	/* srl reg, 0xa, reg */
+	addr[4] = 0x8008200c | rd | rs1;	/* and reg, 3, reg */
+}
+
+void __init sun4m_init_smp(void)
+{
+	BTFIXUPSET_BLACKBOX(hard_smp_processor_id, smp4m_blackbox_id);
+	BTFIXUPSET_BLACKBOX(load_current, smp4m_blackbox_current);
+	BTFIXUPSET_CALL(smp_cross_call, smp4m_cross_call, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(smp_message_pass, smp4m_message_pass, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(__hard_smp_processor_id, __smp4m_processor_id, BTFIXUPCALL_NORM);
+}
diff --git a/arch/sparc/kernel/sun4setup.c b/arch/sparc/kernel/sun4setup.c
new file mode 100644
index 0000000..229a52f
--- /dev/null
+++ b/arch/sparc/kernel/sun4setup.c
@@ -0,0 +1,75 @@
+/* sun4setup.c: Setup the hardware address of various items in the sun4
+ * 		architecture. Called from idprom_init
+ *
+ * Copyright (C) 1998 Chris G. Davis (cdavis@cois.on.ca)
+ */
+
+#include <asm/page.h>
+#include <asm/oplib.h>
+#include <asm/idprom.h>
+#include <asm/sun4paddr.h>
+#include <asm/machines.h>
+
+int sun4_memreg_physaddr;
+int sun4_ie_physaddr;
+int sun4_clock_physaddr;
+int sun4_timer_physaddr;
+int sun4_eth_physaddr;
+int sun4_si_physaddr;
+int sun4_bwtwo_physaddr;
+int sun4_zs0_physaddr;
+int sun4_zs1_physaddr;
+int sun4_dma_physaddr;
+int sun4_esp_physaddr;
+int sun4_ie_physaddr; 
+
+void __init sun4setup(void)
+{
+	printk("Sun4 Hardware Setup v1.0 18/May/98 Chris Davis (cdavis@cois.on.ca). ");
+	/*
+	  setup standard sun4 info
+	  */
+	sun4_ie_physaddr=SUN4_IE_PHYSADDR;
+
+	/*
+	  setup model specific info
+	  */
+	switch(idprom->id_machtype) {
+		case (SM_SUN4 | SM_4_260 ):
+			printk("Setup for a SUN4/260\n");
+			sun4_memreg_physaddr=SUN4_200_MEMREG_PHYSADDR;
+			sun4_clock_physaddr=SUN4_200_CLOCK_PHYSADDR;
+			sun4_timer_physaddr=SUN4_UNUSED_PHYSADDR;
+			sun4_eth_physaddr=SUN4_200_ETH_PHYSADDR;
+			sun4_si_physaddr=SUN4_200_SI_PHYSADDR;
+			sun4_bwtwo_physaddr=SUN4_200_BWTWO_PHYSADDR;
+			sun4_dma_physaddr=SUN4_UNUSED_PHYSADDR;
+			sun4_esp_physaddr=SUN4_UNUSED_PHYSADDR;
+			break;
+		case (SM_SUN4 | SM_4_330 ):
+			printk("Setup for a SUN4/330\n");
+			sun4_memreg_physaddr=SUN4_300_MEMREG_PHYSADDR;
+			sun4_clock_physaddr=SUN4_300_CLOCK_PHYSADDR;
+			sun4_timer_physaddr=SUN4_300_TIMER_PHYSADDR;
+			sun4_eth_physaddr=SUN4_300_ETH_PHYSADDR;
+			sun4_si_physaddr=SUN4_UNUSED_PHYSADDR;
+			sun4_bwtwo_physaddr=SUN4_300_BWTWO_PHYSADDR;
+			sun4_dma_physaddr=SUN4_300_DMA_PHYSADDR;
+			sun4_esp_physaddr=SUN4_300_ESP_PHYSADDR;
+			break;
+		case (SM_SUN4 | SM_4_470 ):
+			printk("Setup for a SUN4/470\n");
+			sun4_memreg_physaddr=SUN4_400_MEMREG_PHYSADDR;
+			sun4_clock_physaddr=SUN4_400_CLOCK_PHYSADDR;
+			sun4_timer_physaddr=SUN4_400_TIMER_PHYSADDR;
+			sun4_eth_physaddr=SUN4_400_ETH_PHYSADDR;
+			sun4_si_physaddr=SUN4_UNUSED_PHYSADDR;
+			sun4_bwtwo_physaddr=SUN4_400_BWTWO_PHYSADDR;
+			sun4_dma_physaddr=SUN4_400_DMA_PHYSADDR;
+			sun4_esp_physaddr=SUN4_400_ESP_PHYSADDR;
+			break;
+		default:
+			;
+	}
+}
+
diff --git a/arch/sparc/kernel/sunos_asm.S b/arch/sparc/kernel/sunos_asm.S
new file mode 100644
index 0000000..07fe860
--- /dev/null
+++ b/arch/sparc/kernel/sunos_asm.S
@@ -0,0 +1,67 @@
+/* $Id: sunos_asm.S,v 1.15 2000/01/11 17:33:21 jj Exp $
+ * sunos_asm.S: SunOS system calls which must have a low-level
+ *              entry point to operate correctly.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * Based upon preliminary work which is:
+ *
+ * Copyright (C) 1995 Adrian M. Rodriguez (adrian@remus.rutgers.edu)
+ */
+
+#include <asm/ptrace.h>
+
+	.text
+	.align 4
+
+	/* When calling ret_sys_call, %o0 should contain the same
+	 * value as in [%sp + STACKFRAME_SZ + PT_I0] */
+
+	/* SunOS getpid() returns pid in %o0 and ppid in %o1 */
+	.globl	sunos_getpid
+sunos_getpid:
+	call	sys_getppid
+	 nop
+
+	call	sys_getpid
+	 st	%o0, [%sp + STACKFRAME_SZ + PT_I1]
+
+	b	ret_sys_call
+	 st	%o0, [%sp + STACKFRAME_SZ + PT_I0]
+
+	/* SunOS getuid() returns uid in %o0 and euid in %o1 */
+	.globl	sunos_getuid
+sunos_getuid:
+	call	sys_geteuid16
+	 nop
+
+	call	sys_getuid16
+	 st	%o0, [%sp + STACKFRAME_SZ + PT_I1]
+
+	b	ret_sys_call
+	 st	%o0, [%sp + STACKFRAME_SZ + PT_I0]
+
+	/* SunOS getgid() returns gid in %o0 and egid in %o1 */
+	.globl	sunos_getgid
+sunos_getgid:
+	call	sys_getegid16
+	 nop
+
+	call	sys_getgid16
+	 st	%o0, [%sp + STACKFRAME_SZ + PT_I1]
+
+	b	ret_sys_call
+	 st	%o0, [%sp + STACKFRAME_SZ + PT_I0]
+
+	/* SunOS's execv() call only specifies the argv argument, the
+	 * environment settings are the same as the calling processes.
+	 */
+	.globl	sunos_execv
+sunos_execv:
+	st	%g0, [%sp + STACKFRAME_SZ + PT_I2]
+
+	call	sparc_execve
+	 add	%sp, STACKFRAME_SZ, %o0
+
+	b	ret_sys_call
+	 ld	[%sp + STACKFRAME_SZ + PT_I0], %o0
diff --git a/arch/sparc/kernel/sunos_ioctl.c b/arch/sparc/kernel/sunos_ioctl.c
new file mode 100644
index 0000000..df1c0b3
--- /dev/null
+++ b/arch/sparc/kernel/sunos_ioctl.c
@@ -0,0 +1,231 @@
+/* $Id: sunos_ioctl.c,v 1.34 2000/09/03 14:10:56 anton Exp $
+ * sunos_ioctl.c: The Linux Operating system: SunOS ioctl compatibility.
+ * 
+ * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/uaccess.h>
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/termios.h>
+#include <linux/ioctl.h>
+#include <linux/route.h>
+#include <linux/sockios.h>
+#include <linux/if.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/syscalls.h>
+#include <linux/file.h>
+#include <asm/kbio.h>
+
+#if 0
+extern char sunkbd_type;
+extern char sunkbd_layout;
+#endif
+
+/* NR_OPEN is now larger and dynamic in recent kernels. */
+#define SUNOS_NR_OPEN	256
+
+asmlinkage int sunos_ioctl (int fd, unsigned long cmd, unsigned long arg)
+{
+	int ret = -EBADF;
+
+	if (fd >= SUNOS_NR_OPEN || !fcheck(fd))
+		goto out;
+
+	/* First handle an easy compat. case for tty ldisc. */
+	if (cmd == TIOCSETD) {
+		int __user *p;
+		int ntty = N_TTY, tmp;
+		mm_segment_t oldfs;
+
+		p = (int __user *) arg;
+		ret = -EFAULT;
+		if (get_user(tmp, p))
+			goto out;
+		if (tmp == 2) {
+			oldfs = get_fs();
+			set_fs(KERNEL_DS);
+			ret = sys_ioctl(fd, cmd, (unsigned long) &ntty);
+			set_fs(oldfs);
+			ret = (ret == -EINVAL ? -EOPNOTSUPP : ret);
+			goto out;
+		}
+	}
+
+	/* Binary compatibility is good American knowhow fuckin' up. */
+	if (cmd == TIOCNOTTY) {
+		ret = sys_setsid();
+		goto out;
+	}
+
+	/* SunOS networking ioctls. */
+	switch (cmd) {
+	case _IOW('r', 10, struct rtentry):
+		ret = sys_ioctl(fd, SIOCADDRT, arg);
+		goto out;
+	case _IOW('r', 11, struct rtentry):
+		ret = sys_ioctl(fd, SIOCDELRT, arg);
+		goto out;
+	case _IOW('i', 12, struct ifreq):
+		ret = sys_ioctl(fd, SIOCSIFADDR, arg);
+		goto out;
+	case _IOWR('i', 13, struct ifreq):
+		ret = sys_ioctl(fd, SIOCGIFADDR, arg);
+		goto out;
+	case _IOW('i', 14, struct ifreq):
+		ret = sys_ioctl(fd, SIOCSIFDSTADDR, arg);
+		goto out;
+	case _IOWR('i', 15, struct ifreq):
+		ret = sys_ioctl(fd, SIOCGIFDSTADDR, arg);
+		goto out;
+	case _IOW('i', 16, struct ifreq):
+		ret = sys_ioctl(fd, SIOCSIFFLAGS, arg);
+		goto out;
+	case _IOWR('i', 17, struct ifreq):
+		ret = sys_ioctl(fd, SIOCGIFFLAGS, arg);
+		goto out;
+	case _IOW('i', 18, struct ifreq):
+		ret = sys_ioctl(fd, SIOCSIFMEM, arg);
+		goto out;
+	case _IOWR('i', 19, struct ifreq):
+		ret = sys_ioctl(fd, SIOCGIFMEM, arg);
+		goto out;
+	case _IOWR('i', 20, struct ifconf):
+		ret = sys_ioctl(fd, SIOCGIFCONF, arg);
+		goto out;
+	case _IOW('i', 21, struct ifreq): /* SIOCSIFMTU */
+		ret = sys_ioctl(fd, SIOCSIFMTU, arg);
+		goto out;
+	case _IOWR('i', 22, struct ifreq): /* SIOCGIFMTU */
+		ret = sys_ioctl(fd, SIOCGIFMTU, arg);
+		goto out;
+
+	case _IOWR('i', 23, struct ifreq):
+		ret = sys_ioctl(fd, SIOCGIFBRDADDR, arg);
+		goto out;
+	case _IOW('i', 24, struct ifreq):
+		ret = sys_ioctl(fd, SIOCSIFBRDADDR, arg);
+		goto out;
+	case _IOWR('i', 25, struct ifreq):
+		ret = sys_ioctl(fd, SIOCGIFNETMASK, arg);
+		goto out;
+	case _IOW('i', 26, struct ifreq):
+		ret = sys_ioctl(fd, SIOCSIFNETMASK, arg);
+		goto out;
+	case _IOWR('i', 27, struct ifreq):
+		ret = sys_ioctl(fd, SIOCGIFMETRIC, arg);
+		goto out;
+	case _IOW('i', 28, struct ifreq):
+		ret = sys_ioctl(fd, SIOCSIFMETRIC, arg);
+		goto out;
+
+	case _IOW('i', 30, struct arpreq):
+		ret = sys_ioctl(fd, SIOCSARP, arg);
+		goto out;
+	case _IOWR('i', 31, struct arpreq):
+		ret = sys_ioctl(fd, SIOCGARP, arg);
+		goto out;
+	case _IOW('i', 32, struct arpreq):
+		ret = sys_ioctl(fd, SIOCDARP, arg);
+		goto out;
+
+	case _IOW('i', 40, struct ifreq): /* SIOCUPPER */
+	case _IOW('i', 41, struct ifreq): /* SIOCLOWER */
+	case _IOW('i', 44, struct ifreq): /* SIOCSETSYNC */
+	case _IOW('i', 45, struct ifreq): /* SIOCGETSYNC */
+	case _IOW('i', 46, struct ifreq): /* SIOCSSDSTATS */
+	case _IOW('i', 47, struct ifreq): /* SIOCSSESTATS */
+	case _IOW('i', 48, struct ifreq): /* SIOCSPROMISC */
+		ret = -EOPNOTSUPP;
+		goto out;
+
+	case _IOW('i', 49, struct ifreq):
+		ret = sys_ioctl(fd, SIOCADDMULTI, arg);
+		goto out;
+	case _IOW('i', 50, struct ifreq):
+		ret = sys_ioctl(fd, SIOCDELMULTI, arg);
+		goto out;
+
+	/* FDDI interface ioctls, unsupported. */
+		
+	case _IOW('i', 51, struct ifreq): /* SIOCFDRESET */
+	case _IOW('i', 52, struct ifreq): /* SIOCFDSLEEP */
+	case _IOW('i', 53, struct ifreq): /* SIOCSTRTFMWAR */
+	case _IOW('i', 54, struct ifreq): /* SIOCLDNSTRTFW */
+	case _IOW('i', 55, struct ifreq): /* SIOCGETFDSTAT */
+	case _IOW('i', 56, struct ifreq): /* SIOCFDNMIINT */
+	case _IOW('i', 57, struct ifreq): /* SIOCFDEXUSER */
+	case _IOW('i', 58, struct ifreq): /* SIOCFDGNETMAP */
+	case _IOW('i', 59, struct ifreq): /* SIOCFDGIOCTL */
+		printk("FDDI ioctl, returning EOPNOTSUPP\n");
+		ret = -EOPNOTSUPP;
+		goto out;
+
+	case _IOW('t', 125, int):
+		/* More stupid tty sunos ioctls, just
+		 * say it worked.
+		 */
+		ret = 0;
+		goto out;
+	/* Non posix grp */
+	case _IOW('t', 118, int): {
+		int oldval, newval, __user *ptr;
+
+		cmd = TIOCSPGRP;
+		ptr = (int __user *) arg;
+		ret = -EFAULT;
+		if (get_user(oldval, ptr))
+			goto out;
+		ret = sys_ioctl(fd, cmd, arg);
+		__get_user(newval, ptr);
+		if (newval == -1) {
+			__put_user(oldval, ptr);
+			ret = -EIO;
+		}
+		if (ret == -ENOTTY)
+			ret = -EIO;
+		goto out;
+	}
+
+	case _IOR('t', 119, int): {
+		int oldval, newval, __user *ptr;
+
+		cmd = TIOCGPGRP;
+		ptr = (int __user *) arg;
+		ret = -EFAULT;
+		if (get_user(oldval, ptr))
+			goto out;
+		ret = sys_ioctl(fd, cmd, arg);
+		__get_user(newval, ptr);
+		if (newval == -1) {
+			__put_user(oldval, ptr);
+			ret = -EIO;
+		}
+		if (ret == -ENOTTY)
+			ret = -EIO;
+		goto out;
+	}
+	}
+
+#if 0
+	if ((cmd & 0xff00) == ('k' << 8)) {
+		printk ("[[KBIO: %8.8x\n", (unsigned int) cmd);
+	}
+#endif
+
+	ret = sys_ioctl(fd, cmd, arg);
+	/* so stupid... */
+	ret = (ret == -EINVAL ? -EOPNOTSUPP : ret);
+out:
+	return ret;
+}
+
+
diff --git a/arch/sparc/kernel/sys_solaris.c b/arch/sparc/kernel/sys_solaris.c
new file mode 100644
index 0000000..fb75785
--- /dev/null
+++ b/arch/sparc/kernel/sys_solaris.c
@@ -0,0 +1,37 @@
+/*
+ * linux/arch/sparc/sys_solaris.c
+ *
+ * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/personality.h>
+#include <linux/ptrace.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/module.h>
+
+asmlinkage int
+do_solaris_syscall (struct pt_regs *regs)
+{
+	static int cnt = 0;
+	if (++cnt < 10) printk ("No solaris handler\n");
+	force_sig(SIGSEGV, current);
+	return 0;
+}
+
+#ifndef CONFIG_SUNOS_EMUL
+asmlinkage int
+do_sunos_syscall (struct pt_regs *regs)
+{
+	static int cnt = 0;
+	if (++cnt < 10) printk ("SunOS binary emulation not compiled in\n");
+	force_sig (SIGSEGV, current);
+	return 0;
+}
+#endif
diff --git a/arch/sparc/kernel/sys_sparc.c b/arch/sparc/kernel/sys_sparc.c
new file mode 100644
index 0000000..0cdfc9d
--- /dev/null
+++ b/arch/sparc/kernel/sys_sparc.c
@@ -0,0 +1,485 @@
+/* $Id: sys_sparc.c,v 1.70 2001/04/14 01:12:02 davem Exp $
+ * linux/arch/sparc/kernel/sys_sparc.c
+ *
+ * This file contains various random system calls that
+ * have a non-standard calling sequence on the Linux/sparc
+ * platform.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/sem.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+#include <linux/stat.h>
+#include <linux/syscalls.h>
+#include <linux/mman.h>
+#include <linux/utsname.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+#include <asm/uaccess.h>
+#include <asm/ipc.h>
+
+/* #define DEBUG_UNIMP_SYSCALL */
+
+/* XXX Make this per-binary type, this way we can detect the type of
+ * XXX a binary.  Every Sparc executable calls this very early on.
+ */
+asmlinkage unsigned long sys_getpagesize(void)
+{
+	return PAGE_SIZE; /* Possibly older binaries want 8192 on sun4's? */
+}
+
+#define COLOUR_ALIGN(addr)      (((addr)+SHMLBA-1)&~(SHMLBA-1))
+
+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;
+
+	if (flags & MAP_FIXED) {
+		/* We do not accept a shared mapping if it would violate
+		 * cache aliasing constraints.
+		 */
+		if ((flags & MAP_SHARED) && (addr & (SHMLBA - 1)))
+			return -EINVAL;
+		return addr;
+	}
+
+	/* See asm-sparc/uaccess.h */
+	if (len > TASK_SIZE - PAGE_SIZE)
+		return -ENOMEM;
+	if (ARCH_SUN4C_SUN4 && len > 0x20000000)
+		return -ENOMEM;
+	if (!addr)
+		addr = TASK_UNMAPPED_BASE;
+
+	if (flags & MAP_SHARED)
+		addr = COLOUR_ALIGN(addr);
+	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 (ARCH_SUN4C_SUN4 && addr < 0xe0000000 && 0x20000000 - len < addr) {
+			addr = PAGE_OFFSET;
+			vmm = find_vma(current->mm, PAGE_OFFSET);
+		}
+		if (TASK_SIZE - PAGE_SIZE - len < addr)
+			return -ENOMEM;
+		if (!vmm || addr + len <= vmm->vm_start)
+			return addr;
+		addr = vmm->vm_end;
+		if (flags & MAP_SHARED)
+			addr = COLOUR_ALIGN(addr);
+	}
+}
+
+asmlinkage unsigned long sparc_brk(unsigned long brk)
+{
+	if(ARCH_SUN4C_SUN4) {
+		if ((brk & 0xe0000000) != (current->mm->brk & 0xe0000000))
+			return current->mm->brk;
+	}
+	return sys_brk(brk);
+}
+
+/*
+ * sys_pipe() is the normal C calling standard for creating
+ * a pipe. It's not the way unix traditionally does this, though.
+ */
+asmlinkage int sparc_pipe(struct pt_regs *regs)
+{
+	int fd[2];
+	int error;
+
+	error = do_pipe(fd);
+	if (error)
+		goto out;
+	regs->u_regs[UREG_I1] = fd[1];
+	error = fd[0];
+out:
+	return error;
+}
+
+/*
+ * 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, int third, void __user *ptr, long fifth)
+{
+	int version, err;
+
+	version = call >> 16; /* hack for backward compatibility */
+	call &= 0xffff;
+
+	if (call <= SEMCTL)
+		switch (call) {
+		case SEMOP:
+			err = sys_semtimedop (first, (struct sembuf __user *)ptr, second, NULL);
+			goto out;
+		case SEMTIMEDOP:
+			err = sys_semtimedop (first, (struct sembuf __user *)ptr, second, (const struct timespec __user *) fifth);
+			goto out;
+		case SEMGET:
+			err = sys_semget (first, second, third);
+			goto out;
+		case SEMCTL: {
+			union semun fourth;
+			err = -EINVAL;
+			if (!ptr)
+				goto out;
+			err = -EFAULT;
+			if (get_user(fourth.__pad,
+				     (void __user * __user *)ptr))
+				goto out;
+			err = sys_semctl (first, second, third, fourth);
+			goto out;
+			}
+		default:
+			err = -ENOSYS;
+			goto out;
+		}
+	if (call <= MSGCTL) 
+		switch (call) {
+		case MSGSND:
+			err = sys_msgsnd (first, (struct msgbuf __user *) ptr, 
+					  second, third);
+			goto out;
+		case MSGRCV:
+			switch (version) {
+			case 0: {
+				struct ipc_kludge tmp;
+				err = -EINVAL;
+				if (!ptr)
+					goto out;
+				err = -EFAULT;
+				if (copy_from_user(&tmp, (struct ipc_kludge __user *) ptr, sizeof (tmp)))
+					goto out;
+				err = sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, third);
+				goto out;
+				}
+			case 1: default:
+				err = sys_msgrcv (first,
+						  (struct msgbuf __user *) ptr,
+						  second, fifth, third);
+				goto out;
+			}
+		case MSGGET:
+			err = sys_msgget ((key_t) first, second);
+			goto out;
+		case MSGCTL:
+			err = sys_msgctl (first, second, (struct msqid_ds __user *) ptr);
+			goto out;
+		default:
+			err = -ENOSYS;
+			goto out;
+		}
+	if (call <= SHMCTL) 
+		switch (call) {
+		case SHMAT:
+			switch (version) {
+			case 0: default: {
+				ulong raddr;
+				err = do_shmat (first, (char __user *) ptr, second, &raddr);
+				if (err)
+					goto out;
+				err = -EFAULT;
+				if (put_user (raddr, (ulong __user *) third))
+					goto out;
+				err = 0;
+				goto out;
+				}
+			case 1:	/* iBCS2 emulator entry point */
+				err = -EINVAL;
+				goto out;
+			}
+		case SHMDT: 
+			err = sys_shmdt ((char __user *)ptr);
+			goto out;
+		case SHMGET:
+			err = sys_shmget (first, second, third);
+			goto out;
+		case SHMCTL:
+			err = sys_shmctl (first, second, (struct shmid_ds __user *) ptr);
+			goto out;
+		default:
+			err = -ENOSYS;
+			goto out;
+		}
+	else
+		err = -ENOSYS;
+out:
+	return err;
+}
+
+/* Linux version of mmap */
+static unsigned long do_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 retval = -EBADF;
+
+	if (!(flags & MAP_ANONYMOUS)) {
+		file = fget(fd);
+		if (!file)
+			goto out;
+	}
+
+	retval = -EINVAL;
+	len = PAGE_ALIGN(len);
+	if (ARCH_SUN4C_SUN4 &&
+	    (len > 0x20000000 ||
+	     ((flags & MAP_FIXED) &&
+	      addr < 0xe0000000 && addr + len > 0x20000000)))
+		goto out_putf;
+
+	/* See asm-sparc/uaccess.h */
+	if (len > TASK_SIZE - PAGE_SIZE || addr + len > TASK_SIZE - PAGE_SIZE)
+		goto out_putf;
+
+	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
+
+	down_write(&current->mm->mmap_sem);
+	retval = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
+	up_write(&current->mm->mmap_sem);
+
+out_putf:
+	if (file)
+		fput(file);
+out:
+	return retval;
+}
+
+asmlinkage unsigned long sys_mmap2(unsigned long addr, unsigned long len,
+	unsigned long prot, unsigned long flags, unsigned long fd,
+	unsigned long pgoff)
+{
+	/* Make sure the shift for mmap2 is constant (12), no matter what PAGE_SIZE
+	   we have. */
+	return do_mmap2(addr, len, prot, flags, fd, pgoff >> (PAGE_SHIFT - 12));
+}
+
+asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len,
+	unsigned long prot, unsigned long flags, unsigned long fd,
+	unsigned long off)
+{
+	return do_mmap2(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
+}
+
+long sparc_remap_file_pages(unsigned long start, unsigned long size,
+			   unsigned long prot, unsigned long pgoff,
+			   unsigned long flags)
+{
+	/* This works on an existing mmap so we don't need to validate
+	 * the range as that was done at the original mmap call.
+	 */
+	return sys_remap_file_pages(start, size, prot,
+				    (pgoff >> (PAGE_SHIFT - 12)), flags);
+}
+
+extern unsigned long do_mremap(unsigned long addr,
+	unsigned long old_len, unsigned long new_len,
+	unsigned long flags, unsigned long new_addr);
+                
+asmlinkage unsigned long sparc_mremap(unsigned long addr,
+	unsigned long old_len, unsigned long new_len,
+	unsigned long flags, unsigned long new_addr)
+{
+	struct vm_area_struct *vma;
+	unsigned long ret = -EINVAL;
+	if (ARCH_SUN4C_SUN4) {
+		if (old_len > 0x20000000 || new_len > 0x20000000)
+			goto out;
+		if (addr < 0xe0000000 && addr + old_len > 0x20000000)
+			goto out;
+	}
+	if (old_len > TASK_SIZE - PAGE_SIZE ||
+	    new_len > TASK_SIZE - PAGE_SIZE)
+		goto out;
+	down_write(&current->mm->mmap_sem);
+	if (flags & MREMAP_FIXED) {
+		if (ARCH_SUN4C_SUN4 &&
+		    new_addr < 0xe0000000 &&
+		    new_addr + new_len > 0x20000000)
+			goto out_sem;
+		if (new_addr + new_len > TASK_SIZE - PAGE_SIZE)
+			goto out_sem;
+	} else if ((ARCH_SUN4C_SUN4 && addr < 0xe0000000 &&
+		    addr + new_len > 0x20000000) ||
+		   addr + new_len > TASK_SIZE - PAGE_SIZE) {
+		unsigned long map_flags = 0;
+		struct file *file = NULL;
+
+		ret = -ENOMEM;
+		if (!(flags & MREMAP_MAYMOVE))
+			goto out_sem;
+
+		vma = find_vma(current->mm, addr);
+		if (vma) {
+			if (vma->vm_flags & VM_SHARED)
+				map_flags |= MAP_SHARED;
+			file = vma->vm_file;
+		}
+
+		new_addr = get_unmapped_area(file, addr, new_len,
+				     vma ? vma->vm_pgoff : 0,
+				     map_flags);
+		ret = new_addr;
+		if (new_addr & ~PAGE_MASK)
+			goto out_sem;
+		flags |= MREMAP_FIXED;
+	}
+	ret = do_mremap(addr, old_len, new_len, flags, new_addr);
+out_sem:
+	up_write(&current->mm->mmap_sem);
+out:
+	return ret;       
+}
+
+/* we come to here via sys_nis_syscall so it can setup the regs argument */
+asmlinkage unsigned long
+c_sys_nis_syscall (struct pt_regs *regs)
+{
+	static int count = 0;
+
+	if (count++ > 5)
+		return -ENOSYS;
+	printk ("%s[%d]: Unimplemented SPARC system call %d\n",
+		current->comm, current->pid, (int)regs->u_regs[1]);
+#ifdef DEBUG_UNIMP_SYSCALL	
+	show_regs (regs);
+#endif
+	return -ENOSYS;
+}
+
+/* #define DEBUG_SPARC_BREAKPOINT */
+
+asmlinkage void
+sparc_breakpoint (struct pt_regs *regs)
+{
+	siginfo_t info;
+
+	lock_kernel();
+#ifdef DEBUG_SPARC_BREAKPOINT
+        printk ("TRAP: Entering kernel PC=%x, nPC=%x\n", regs->pc, regs->npc);
+#endif
+	info.si_signo = SIGTRAP;
+	info.si_errno = 0;
+	info.si_code = TRAP_BRKPT;
+	info.si_addr = (void __user *)regs->pc;
+	info.si_trapno = 0;
+	force_sig_info(SIGTRAP, &info, current);
+
+#ifdef DEBUG_SPARC_BREAKPOINT
+	printk ("TRAP: Returning to space: PC=%x nPC=%x\n", regs->pc, regs->npc);
+#endif
+	unlock_kernel();
+}
+
+asmlinkage int
+sparc_sigaction (int sig, const struct old_sigaction __user *act,
+		 struct old_sigaction __user *oact)
+{
+	struct k_sigaction new_ka, old_ka;
+	int ret;
+
+	if (sig < 0) {
+		current->thread.new_signal = 1;
+		sig = -sig;
+	}
+
+	if (act) {
+		unsigned long 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_restorer, &act->sa_restorer))
+			return -EFAULT;
+		__get_user(new_ka.sa.sa_flags, &act->sa_flags);
+		__get_user(mask, &act->sa_mask);
+		siginitset(&new_ka.sa.sa_mask, mask);
+		new_ka.ka_restorer = NULL;
+	}
+
+	ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+	if (!ret && oact) {
+		/* In the clone() case we could copy half consistent
+		 * state to the user, however this could sleep and
+		 * deadlock us if we held the signal lock on SMP.  So for
+		 * now I take the easy way out and do no locking.
+		 */
+		if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) ||
+		    __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
+		    __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
+			return -EFAULT;
+		__put_user(old_ka.sa.sa_flags, &oact->sa_flags);
+		__put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
+	}
+
+	return ret;
+}
+
+asmlinkage long
+sys_rt_sigaction(int sig,
+		 const struct sigaction __user *act,
+		 struct sigaction __user *oact,
+		 void __user *restorer,
+		 size_t sigsetsize)
+{
+	struct k_sigaction new_ka, old_ka;
+	int ret;
+
+	/* XXX: Don't preclude handling different sized sigset_t's.  */
+	if (sigsetsize != sizeof(sigset_t))
+		return -EINVAL;
+
+	/* All tasks which use RT signals (effectively) use
+	 * new style signals.
+	 */
+	current->thread.new_signal = 1;
+
+	if (act) {
+		new_ka.ka_restorer = restorer;
+		if (copy_from_user(&new_ka.sa, act, sizeof(*act)))
+			return -EFAULT;
+	}
+
+	ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+	if (!ret && oact) {
+		if (copy_to_user(oact, &old_ka.sa, sizeof(*oact)))
+			return -EFAULT;
+	}
+
+	return ret;
+}
+
+asmlinkage int sys_getdomainname(char __user *name, int len)
+{
+ 	int nlen;
+ 	int err = -EFAULT;
+ 	
+ 	down_read(&uts_sem);
+ 	
+	nlen = strlen(system_utsname.domainname) + 1;
+
+	if (nlen < len)
+		len = nlen;
+	if (len > __NEW_UTS_LEN)
+		goto done;
+	if (copy_to_user(name, system_utsname.domainname, len))
+		goto done;
+	err = 0;
+done:
+	up_read(&uts_sem);
+	return err;
+}
diff --git a/arch/sparc/kernel/sys_sunos.c b/arch/sparc/kernel/sys_sunos.c
new file mode 100644
index 0000000..81c894a
--- /dev/null
+++ b/arch/sparc/kernel/sys_sunos.c
@@ -0,0 +1,1194 @@
+/* $Id: sys_sunos.c,v 1.137 2002/02/08 03:57:14 davem Exp $
+ * sys_sunos.c: SunOS specific syscall compatibility support.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ *
+ * Based upon preliminary work which is:
+ *
+ * Copyright (C) 1995 Adrian M. Rodriguez (adrian@remus.rutgers.edu)
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/resource.h>
+#include <linux/ipc.h>
+#include <linux/shm.h>
+#include <linux/msg.h>
+#include <linux/sem.h>
+#include <linux/signal.h>
+#include <linux/uio.h>
+#include <linux/utsname.h>
+#include <linux/major.h>
+#include <linux/stat.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/errno.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/syscalls.h>
+
+#include <net/sock.h>
+
+#include <asm/uaccess.h>
+#ifndef KERNEL_DS
+#include <linux/segment.h>
+#endif
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/pconf.h>
+#include <asm/idprom.h> /* for gethostid() */
+#include <asm/unistd.h>
+#include <asm/system.h>
+
+/* For the nfs mount emulation */
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
+#include <linux/nfs_mount.h>
+
+/* for sunos_select */
+#include <linux/time.h>
+#include <linux/personality.h>
+
+/* NR_OPEN is now larger and dynamic in recent kernels. */
+#define SUNOS_NR_OPEN	256
+
+/* We use the SunOS mmap() semantics. */
+asmlinkage unsigned long sunos_mmap(unsigned long addr, unsigned long len,
+				    unsigned long prot, unsigned long flags,
+				    unsigned long fd, unsigned long off)
+{
+	struct file * file = NULL;
+	unsigned long retval, ret_type;
+
+	if (flags & MAP_NORESERVE) {
+		static int cnt;
+		if (cnt++ < 10)
+			printk("%s: unimplemented SunOS MAP_NORESERVE mmap() flag\n",
+			       current->comm);
+		flags &= ~MAP_NORESERVE;
+	}
+	retval = -EBADF;
+	if (!(flags & MAP_ANONYMOUS)) {
+		if (fd >= SUNOS_NR_OPEN)
+			goto out;
+		file = fget(fd);
+		if (!file)
+			goto out;
+	}
+
+	retval = -EINVAL;
+	/* If this is ld.so or a shared library doing an mmap
+	 * of /dev/zero, transform it into an anonymous mapping.
+	 * SunOS is so stupid some times... hmph!
+	 */
+	if (file) {
+		if (imajor(file->f_dentry->d_inode) == MEM_MAJOR &&
+		    iminor(file->f_dentry->d_inode) == 5) {
+			flags |= MAP_ANONYMOUS;
+			fput(file);
+			file = NULL;
+		}
+	}
+	ret_type = flags & _MAP_NEW;
+	flags &= ~_MAP_NEW;
+
+	if (!(flags & MAP_FIXED))
+		addr = 0;
+	else {
+		if (ARCH_SUN4C_SUN4 &&
+		    (len > 0x20000000 ||
+		     ((flags & MAP_FIXED) &&
+		      addr < 0xe0000000 && addr + len > 0x20000000)))
+			goto out_putf;
+
+		/* See asm-sparc/uaccess.h */
+		if (len > TASK_SIZE - PAGE_SIZE ||
+		    addr + len > TASK_SIZE - PAGE_SIZE)
+			goto out_putf;
+	}
+
+	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
+	down_write(&current->mm->mmap_sem);
+	retval = do_mmap(file, addr, len, prot, flags, off);
+	up_write(&current->mm->mmap_sem);
+	if (!ret_type)
+		retval = ((retval < PAGE_OFFSET) ? 0 : retval);
+
+out_putf:
+	if (file)
+		fput(file);
+out:
+	return retval;
+}
+
+/* lmbench calls this, just say "yeah, ok" */
+asmlinkage int sunos_mctl(unsigned long addr, unsigned long len, int function, char *arg)
+{
+	return 0;
+}
+
+/* SunOS is completely broken... it returns 0 on success, otherwise
+ * ENOMEM.  For sys_sbrk() it wants the old brk value as a return
+ * on success and ENOMEM as before on failure.
+ */
+asmlinkage int sunos_brk(unsigned long brk)
+{
+	int freepages, retval = -ENOMEM;
+	unsigned long rlim;
+	unsigned long newbrk, oldbrk;
+
+	down_write(&current->mm->mmap_sem);
+	if (ARCH_SUN4C_SUN4) {
+		if (brk >= 0x20000000 && brk < 0xe0000000) {
+			goto out;
+		}
+	}
+
+	if (brk < current->mm->end_code)
+		goto out;
+
+	newbrk = PAGE_ALIGN(brk);
+	oldbrk = PAGE_ALIGN(current->mm->brk);
+	retval = 0;
+	if (oldbrk == newbrk) {
+		current->mm->brk = brk;
+		goto out;
+	}
+
+	/*
+	 * Always allow shrinking brk
+	 */
+	if (brk <= current->mm->brk) {
+		current->mm->brk = brk;
+		do_munmap(current->mm, newbrk, oldbrk-newbrk);
+		goto out;
+	}
+	/*
+	 * Check against rlimit and stack..
+	 */
+	retval = -ENOMEM;
+	rlim = current->signal->rlim[RLIMIT_DATA].rlim_cur;
+	if (rlim >= RLIM_INFINITY)
+		rlim = ~0;
+	if (brk - current->mm->end_code > rlim)
+		goto out;
+
+	/*
+	 * Check against existing mmap mappings.
+	 */
+	if (find_vma_intersection(current->mm, oldbrk, newbrk+PAGE_SIZE))
+		goto out;
+
+	/*
+	 * stupid algorithm to decide if we have enough memory: while
+	 * simple, it hopefully works in most obvious cases.. Easy to
+	 * fool it, but this should catch most mistakes.
+	 */
+	freepages = get_page_cache_size();
+	freepages >>= 1;
+	freepages += nr_free_pages();
+	freepages += nr_swap_pages;
+	freepages -= num_physpages >> 4;
+	freepages -= (newbrk-oldbrk) >> PAGE_SHIFT;
+	if (freepages < 0)
+		goto out;
+	/*
+	 * Ok, we have probably got enough memory - let it rip.
+	 */
+	current->mm->brk = brk;
+	do_brk(oldbrk, newbrk-oldbrk);
+	retval = 0;
+out:
+	up_write(&current->mm->mmap_sem);
+	return retval;
+}
+
+asmlinkage unsigned long sunos_sbrk(int increment)
+{
+	int error;
+	unsigned long oldbrk;
+
+	/* This should do it hopefully... */
+	lock_kernel();
+	oldbrk = current->mm->brk;
+	error = sunos_brk(((int) current->mm->brk) + increment);
+	if (!error)
+		error = oldbrk;
+	unlock_kernel();
+	return error;
+}
+
+/* XXX Completely undocumented, and completely magic...
+ * XXX I believe it is to increase the size of the stack by
+ * XXX argument 'increment' and return the new end of stack
+ * XXX area.  Wheee...
+ */
+asmlinkage unsigned long sunos_sstk(int increment)
+{
+	lock_kernel();
+	printk("%s: Call to sunos_sstk(increment<%d>) is unsupported\n",
+	       current->comm, increment);
+	unlock_kernel();
+	return -1;
+}
+
+/* Give hints to the kernel as to what paging strategy to use...
+ * Completely bogus, don't remind me.
+ */
+#define VA_NORMAL     0 /* Normal vm usage expected */
+#define VA_ABNORMAL   1 /* Abnormal/random vm usage probable */
+#define VA_SEQUENTIAL 2 /* Accesses will be of a sequential nature */
+#define VA_INVALIDATE 3 /* Page table entries should be flushed ??? */
+static char *vstrings[] = {
+	"VA_NORMAL",
+	"VA_ABNORMAL",
+	"VA_SEQUENTIAL",
+	"VA_INVALIDATE",
+};
+
+asmlinkage void sunos_vadvise(unsigned long strategy)
+{
+	/* I wanna see who uses this... */
+	lock_kernel();
+	printk("%s: Advises us to use %s paging strategy\n",
+	       current->comm,
+	       strategy <= 3 ? vstrings[strategy] : "BOGUS");
+	unlock_kernel();
+}
+
+/* This just wants the soft limit (ie. rlim_cur element) of the RLIMIT_NOFILE
+ * resource limit and is for backwards compatibility with older sunos
+ * revs.
+ */
+asmlinkage long sunos_getdtablesize(void)
+{
+	return SUNOS_NR_OPEN;
+}
+
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+
+asmlinkage unsigned long sunos_sigblock(unsigned long blk_mask)
+{
+	unsigned long old;
+
+	spin_lock_irq(&current->sighand->siglock);
+	old = current->blocked.sig[0];
+	current->blocked.sig[0] |= (blk_mask & _BLOCKABLE);
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+	return old;
+}
+
+asmlinkage unsigned long sunos_sigsetmask(unsigned long newmask)
+{
+	unsigned long retval;
+
+	spin_lock_irq(&current->sighand->siglock);
+	retval = current->blocked.sig[0];
+	current->blocked.sig[0] = (newmask & _BLOCKABLE);
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+	return retval;
+}
+
+/* SunOS getdents is very similar to the newer Linux (iBCS2 compliant)    */
+/* getdents system call, the format of the structure just has a different */
+/* layout (d_off+d_ino instead of d_ino+d_off) */
+struct sunos_dirent {
+    long           d_off;
+    unsigned long  d_ino;
+    unsigned short d_reclen;
+    unsigned short d_namlen;
+    char           d_name[1];
+};
+
+struct sunos_dirent_callback {
+    struct sunos_dirent __user *curr;
+    struct sunos_dirent __user *previous;
+    int count;
+    int error;
+};
+
+#define NAME_OFFSET(de) ((int) ((de)->d_name - (char __user *) (de)))
+#define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1))
+
+static int sunos_filldir(void * __buf, const char * name, int namlen,
+			 loff_t offset, ino_t ino, unsigned int d_type)
+{
+	struct sunos_dirent __user *dirent;
+	struct sunos_dirent_callback * buf = __buf;
+	int reclen = ROUND_UP(NAME_OFFSET(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(namlen, &dirent->d_namlen);
+	put_user(reclen, &dirent->d_reclen);
+	copy_to_user(dirent->d_name, name, namlen);
+	put_user(0, dirent->d_name + namlen);
+	dirent = (void __user *) dirent + reclen;
+	buf->curr = dirent;
+	buf->count -= reclen;
+	return 0;
+}
+
+asmlinkage int sunos_getdents(unsigned int fd, void __user *dirent, int cnt)
+{
+	struct file * file;
+	struct sunos_dirent __user *lastdirent;
+	struct sunos_dirent_callback buf;
+	int error = -EBADF;
+
+	if (fd >= SUNOS_NR_OPEN)
+		goto out;
+
+	file = fget(fd);
+	if (!file)
+		goto out;
+
+	error = -EINVAL;
+	if (cnt < (sizeof(struct sunos_dirent) + 255))
+		goto out_putf;
+
+	buf.curr = (struct sunos_dirent __user *) dirent;
+	buf.previous = NULL;
+	buf.count = cnt;
+	buf.error = 0;
+
+	error = vfs_readdir(file, sunos_filldir, &buf);
+	if (error < 0)
+		goto out_putf;
+
+	lastdirent = buf.previous;
+	error = buf.error;
+	if (lastdirent) {
+		put_user(file->f_pos, &lastdirent->d_off);
+		error = cnt - buf.count;
+	}
+
+out_putf:
+	fput(file);
+out:
+	return error;
+}
+
+/* Old sunos getdirentries, severely broken compatibility stuff here. */
+struct sunos_direntry {
+    unsigned long  d_ino;
+    unsigned short d_reclen;
+    unsigned short d_namlen;
+    char           d_name[1];
+};
+
+struct sunos_direntry_callback {
+    struct sunos_direntry __user *curr;
+    struct sunos_direntry __user *previous;
+    int count;
+    int error;
+};
+
+static int sunos_filldirentry(void * __buf, const char * name, int namlen,
+			      loff_t offset, ino_t ino, unsigned int d_type)
+{
+	struct sunos_direntry __user *dirent;
+	struct sunos_direntry_callback *buf = __buf;
+	int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
+
+	buf->error = -EINVAL;	/* only used if we fail.. */
+	if (reclen > buf->count)
+		return -EINVAL;
+	dirent = buf->previous;
+	dirent = buf->curr;
+	buf->previous = dirent;
+	put_user(ino, &dirent->d_ino);
+	put_user(namlen, &dirent->d_namlen);
+	put_user(reclen, &dirent->d_reclen);
+	copy_to_user(dirent->d_name, name, namlen);
+	put_user(0, dirent->d_name + namlen);
+	dirent = (void __user *) dirent + reclen;
+	buf->curr = dirent;
+	buf->count -= reclen;
+	return 0;
+}
+
+asmlinkage int sunos_getdirentries(unsigned int fd, void __user *dirent,
+				   int cnt, unsigned int __user *basep)
+{
+	struct file * file;
+	struct sunos_direntry __user *lastdirent;
+	struct sunos_direntry_callback buf;
+	int error = -EBADF;
+
+	if (fd >= SUNOS_NR_OPEN)
+		goto out;
+
+	file = fget(fd);
+	if (!file)
+		goto out;
+
+	error = -EINVAL;
+	if (cnt < (sizeof(struct sunos_direntry) + 255))
+		goto out_putf;
+
+	buf.curr = (struct sunos_direntry __user *) dirent;
+	buf.previous = NULL;
+	buf.count = cnt;
+	buf.error = 0;
+
+	error = vfs_readdir(file, sunos_filldirentry, &buf);
+	if (error < 0)
+		goto out_putf;
+
+	lastdirent = buf.previous;
+	error = buf.error;
+	if (lastdirent) {
+		put_user(file->f_pos, basep);
+		error = cnt - buf.count;
+	}
+
+out_putf:
+	fput(file);
+out:
+	return error;
+}
+
+struct sunos_utsname {
+	char sname[9];
+	char nname[9];
+	char nnext[56];
+	char rel[9];
+	char ver[9];
+	char mach[9];
+};
+
+asmlinkage int sunos_uname(struct sunos_utsname __user *name)
+{
+	int ret;
+	down_read(&uts_sem);
+	ret = copy_to_user(&name->sname[0], &system_utsname.sysname[0], sizeof(name->sname) - 1);
+	if (!ret) {
+		ret |= __copy_to_user(&name->nname[0], &system_utsname.nodename[0], sizeof(name->nname) - 1);
+		ret |= __put_user('\0', &name->nname[8]);
+		ret |= __copy_to_user(&name->rel[0], &system_utsname.release[0], sizeof(name->rel) - 1);
+		ret |= __copy_to_user(&name->ver[0], &system_utsname.version[0], sizeof(name->ver) - 1);
+		ret |= __copy_to_user(&name->mach[0], &system_utsname.machine[0], sizeof(name->mach) - 1);
+	}
+	up_read(&uts_sem);
+	return ret ? -EFAULT : 0;
+}
+
+asmlinkage int sunos_nosys(void)
+{
+	struct pt_regs *regs;
+	siginfo_t info;
+	static int cnt;
+
+	lock_kernel();
+	regs = current->thread.kregs;
+	info.si_signo = SIGSYS;
+	info.si_errno = 0;
+	info.si_code = __SI_FAULT|0x100;
+	info.si_addr = (void __user *)regs->pc;
+	info.si_trapno = regs->u_regs[UREG_G1];
+	send_sig_info(SIGSYS, &info, current);
+	if (cnt++ < 4) {
+		printk("Process makes ni_syscall number %d, register dump:\n",
+		       (int) regs->u_regs[UREG_G1]);
+		show_regs(regs);
+	}
+	unlock_kernel();
+	return -ENOSYS;
+}
+
+/* This is not a real and complete implementation yet, just to keep
+ * the easy SunOS binaries happy.
+ */
+asmlinkage int sunos_fpathconf(int fd, int name)
+{
+	int ret;
+
+	switch(name) {
+	case _PCONF_LINK:
+		ret = LINK_MAX;
+		break;
+	case _PCONF_CANON:
+		ret = MAX_CANON;
+		break;
+	case _PCONF_INPUT:
+		ret = MAX_INPUT;
+		break;
+	case _PCONF_NAME:
+		ret = NAME_MAX;
+		break;
+	case _PCONF_PATH:
+		ret = PATH_MAX;
+		break;
+	case _PCONF_PIPE:
+		ret = PIPE_BUF;
+		break;
+	case _PCONF_CHRESTRICT:		/* XXX Investigate XXX */
+		ret = 1;
+		break;
+	case _PCONF_NOTRUNC:		/* XXX Investigate XXX */
+	case _PCONF_VDISABLE:
+		ret = 0;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+asmlinkage int sunos_pathconf(char __user *path, int name)
+{
+	int ret;
+
+	ret = sunos_fpathconf(0, name); /* XXX cheese XXX */
+	return ret;
+}
+
+/* SunOS mount system call emulation */
+
+asmlinkage int sunos_select(int width, fd_set __user *inp, fd_set __user *outp,
+			    fd_set __user *exp, struct timeval __user *tvp)
+{
+	int ret;
+
+	/* SunOS binaries expect that select won't change the tvp contents */
+	ret = sys_select (width, inp, outp, exp, tvp);
+	if (ret == -EINTR && tvp) {
+		time_t sec, usec;
+
+		__get_user(sec, &tvp->tv_sec);
+		__get_user(usec, &tvp->tv_usec);
+
+		if (sec == 0 && usec == 0)
+			ret = 0;
+	}
+	return ret;
+}
+
+asmlinkage void sunos_nop(void)
+{
+	return;
+}
+
+/* SunOS mount/umount. */
+#define SMNT_RDONLY       1
+#define SMNT_NOSUID       2
+#define SMNT_NEWTYPE      4
+#define SMNT_GRPID        8
+#define SMNT_REMOUNT      16
+#define SMNT_NOSUB        32
+#define SMNT_MULTI        64
+#define SMNT_SYS5         128
+
+struct sunos_fh_t {
+	char fh_data [NFS_FHSIZE];
+};
+
+struct sunos_nfs_mount_args {
+	struct sockaddr_in  __user *addr; /* file server address */
+	struct nfs_fh __user *fh;     /* File handle to be mounted */
+	int        flags;      /* flags */
+	int        wsize;      /* write size in bytes */
+	int        rsize;      /* read size in bytes */
+	int        timeo;      /* initial timeout in .1 secs */
+	int        retrans;    /* times to retry send */
+	char       __user *hostname;  /* server's hostname */
+	int        acregmin;   /* attr cache file min secs */
+	int        acregmax;   /* attr cache file max secs */
+	int        acdirmin;   /* attr cache dir min secs */
+	int        acdirmax;   /* attr cache dir max secs */
+	char       __user *netname;   /* server's netname */
+};
+
+
+/* Bind the socket on a local reserved port and connect it to the
+ * remote server.  This on Linux/i386 is done by the mount program,
+ * not by the kernel.
+ */
+static int
+sunos_nfs_get_server_fd (int fd, struct sockaddr_in *addr)
+{
+	struct sockaddr_in local;
+	struct sockaddr_in server;
+	int    try_port;
+	struct socket *socket;
+	struct inode  *inode;
+	struct file   *file;
+	int    ret, result = 0;
+
+	file = fget(fd);
+	if (!file)
+		goto out;
+
+	inode = file->f_dentry->d_inode;
+
+	socket = SOCKET_I(inode);
+	local.sin_family = AF_INET;
+	local.sin_addr.s_addr = INADDR_ANY;
+
+	/* IPPORT_RESERVED = 1024, can't find the definition in the kernel */
+	try_port = 1024;
+	do {
+		local.sin_port = htons (--try_port);
+		ret = socket->ops->bind(socket, (struct sockaddr*)&local,
+					sizeof(local));
+	} while (ret && try_port > (1024 / 2));
+
+	if (ret)
+		goto out_putf;
+
+	server.sin_family = AF_INET;
+	server.sin_addr = addr->sin_addr;
+	server.sin_port = NFS_PORT;
+
+	/* Call sys_connect */
+	ret = socket->ops->connect (socket, (struct sockaddr *) &server,
+				    sizeof (server), file->f_flags);
+	if (ret >= 0)
+		result = 1;
+
+out_putf:
+	fput(file);
+out:
+	return result;
+}
+
+static int get_default (int value, int def_value)
+{
+    if (value)
+	return value;
+    else
+	return def_value;
+}
+
+static int sunos_nfs_mount(char *dir_name, int linux_flags, void __user *data)
+{
+	int  server_fd, err;
+	char *the_name, *mount_page;
+	struct nfs_mount_data linux_nfs_mount;
+	struct sunos_nfs_mount_args sunos_mount;
+
+	/* Ok, here comes the fun part: Linux's nfs mount needs a
+	 * socket connection to the server, but SunOS mount does not
+	 * require this, so we use the information on the destination
+	 * address to create a socket and bind it to a reserved
+	 * port on this system
+	 */
+	if (copy_from_user(&sunos_mount, data, sizeof(sunos_mount)))
+		return -EFAULT;
+
+	server_fd = sys_socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	if (server_fd < 0)
+		return -ENXIO;
+
+	if (copy_from_user(&linux_nfs_mount.addr,sunos_mount.addr,
+				sizeof(*sunos_mount.addr)) ||
+	    copy_from_user(&linux_nfs_mount.root,sunos_mount.fh,
+				sizeof(*sunos_mount.fh))) {
+		sys_close (server_fd);
+		return -EFAULT;
+	}
+
+	if (!sunos_nfs_get_server_fd (server_fd, &linux_nfs_mount.addr)){
+		sys_close (server_fd);
+		return -ENXIO;
+	}
+
+	/* Now, bind it to a locally reserved port */
+	linux_nfs_mount.version  = NFS_MOUNT_VERSION;
+	linux_nfs_mount.flags    = sunos_mount.flags;
+	linux_nfs_mount.fd       = server_fd;
+	
+	linux_nfs_mount.rsize    = get_default (sunos_mount.rsize, 8192);
+	linux_nfs_mount.wsize    = get_default (sunos_mount.wsize, 8192);
+	linux_nfs_mount.timeo    = get_default (sunos_mount.timeo, 10);
+	linux_nfs_mount.retrans  = sunos_mount.retrans;
+	
+	linux_nfs_mount.acregmin = sunos_mount.acregmin;
+	linux_nfs_mount.acregmax = sunos_mount.acregmax;
+	linux_nfs_mount.acdirmin = sunos_mount.acdirmin;
+	linux_nfs_mount.acdirmax = sunos_mount.acdirmax;
+
+	the_name = getname(sunos_mount.hostname);
+	if (IS_ERR(the_name))
+		return PTR_ERR(the_name);
+
+	strlcpy(linux_nfs_mount.hostname, the_name,
+		sizeof(linux_nfs_mount.hostname));
+	putname (the_name);
+	
+	mount_page = (char *) get_zeroed_page(GFP_KERNEL);
+	if (!mount_page)
+		return -ENOMEM;
+
+	memcpy(mount_page, &linux_nfs_mount, sizeof(linux_nfs_mount));
+
+	err = do_mount("", dir_name, "nfs", linux_flags, mount_page);
+
+	free_page((unsigned long) mount_page);
+	return err;
+}
+
+asmlinkage int
+sunos_mount(char __user *type, char __user *dir, int flags, void __user *data)
+{
+	int linux_flags = 0;
+	int ret = -EINVAL;
+	char *dev_fname = NULL;
+	char *dir_page, *type_page;
+
+	if (!capable (CAP_SYS_ADMIN))
+		return -EPERM;
+		
+	lock_kernel();
+	/* We don't handle the integer fs type */
+	if ((flags & SMNT_NEWTYPE) == 0)
+		goto out;
+
+	/* Do not allow for those flags we don't support */
+	if (flags & (SMNT_GRPID|SMNT_NOSUB|SMNT_MULTI|SMNT_SYS5))
+		goto out;
+
+	if (flags & SMNT_REMOUNT)
+		linux_flags |= MS_REMOUNT;
+	if (flags & SMNT_RDONLY)
+		linux_flags |= MS_RDONLY;
+	if (flags & SMNT_NOSUID)
+		linux_flags |= MS_NOSUID;
+
+	dir_page = getname(dir);
+	ret = PTR_ERR(dir_page);
+	if (IS_ERR(dir_page))
+		goto out;
+
+	type_page = getname(type);
+	ret = PTR_ERR(type_page);
+	if (IS_ERR(type_page))
+		goto out1;
+
+	if (strcmp(type_page, "ext2") == 0) {
+		dev_fname = getname(data);
+	} else if (strcmp(type_page, "iso9660") == 0) {
+		dev_fname = getname(data);
+	} else if (strcmp(type_page, "minix") == 0) {
+		dev_fname = getname(data);
+	} else if (strcmp(type_page, "nfs") == 0) {
+		ret = sunos_nfs_mount (dir_page, flags, data);
+		goto out2;
+        } else if (strcmp(type_page, "ufs") == 0) {
+		printk("Warning: UFS filesystem mounts unsupported.\n");
+		ret = -ENODEV;
+		goto out2;
+	} else if (strcmp(type_page, "proc")) {
+		ret = -ENODEV;
+		goto out2;
+	}
+	ret = PTR_ERR(dev_fname);
+	if (IS_ERR(dev_fname))
+		goto out2;
+	ret = do_mount(dev_fname, dir_page, type_page, linux_flags, NULL);
+	if (dev_fname)
+		putname(dev_fname);
+out2:
+	putname(type_page);
+out1:
+	putname(dir_page);
+out:
+	unlock_kernel();
+	return ret;
+}
+
+
+asmlinkage int sunos_setpgrp(pid_t pid, pid_t pgid)
+{
+	int ret;
+
+	/* So stupid... */
+	if ((!pid || pid == current->pid) &&
+	    !pgid) {
+		sys_setsid();
+		ret = 0;
+	} else {
+		ret = sys_setpgid(pid, pgid);
+	}
+	return ret;
+}
+
+/* So stupid... */
+asmlinkage int sunos_wait4(pid_t pid, unsigned int __user *stat_addr,
+			   int options, struct rusage __user*ru)
+{
+	int ret;
+
+	ret = sys_wait4((pid ? pid : -1), stat_addr, options, ru);
+	return ret;
+}
+
+extern int kill_pg(int, int, int);
+asmlinkage int sunos_killpg(int pgrp, int sig)
+{
+	int ret;
+
+	lock_kernel();
+	ret = kill_pg(pgrp, sig, 0);
+	unlock_kernel();
+	return ret;
+}
+
+asmlinkage int sunos_audit(void)
+{
+	lock_kernel();
+	printk ("sys_audit\n");
+	unlock_kernel();
+	return -1;
+}
+
+asmlinkage unsigned long sunos_gethostid(void)
+{
+	unsigned long ret;
+
+	lock_kernel();
+	ret = ((unsigned long)idprom->id_machtype << 24) |
+		(unsigned long)idprom->id_sernum;
+	unlock_kernel();
+	return ret;
+}
+
+/* sysconf options, for SunOS compatibility */
+#define   _SC_ARG_MAX             1
+#define   _SC_CHILD_MAX           2
+#define   _SC_CLK_TCK             3
+#define   _SC_NGROUPS_MAX         4
+#define   _SC_OPEN_MAX            5
+#define   _SC_JOB_CONTROL         6
+#define   _SC_SAVED_IDS           7
+#define   _SC_VERSION             8
+
+asmlinkage long sunos_sysconf (int name)
+{
+	long ret;
+
+	switch (name){
+	case _SC_ARG_MAX:
+		ret = ARG_MAX;
+		break;
+	case _SC_CHILD_MAX:
+		ret = CHILD_MAX;
+		break;
+	case _SC_CLK_TCK:
+		ret = HZ;
+		break;
+	case _SC_NGROUPS_MAX:
+		ret = NGROUPS_MAX;
+		break;
+	case _SC_OPEN_MAX:
+		ret = OPEN_MAX;
+		break;
+	case _SC_JOB_CONTROL:
+		ret = 1;	/* yes, we do support job control */
+		break;
+	case _SC_SAVED_IDS:
+		ret = 1;	/* yes, we do support saved uids  */
+		break;
+	case _SC_VERSION:
+		/* mhm, POSIX_VERSION is in /usr/include/unistd.h
+		 * should it go on /usr/include/linux?
+		 */
+		ret = 199009L; 
+		break;
+	default:
+		ret = -1;
+		break;
+	};
+	return ret;
+}
+
+asmlinkage int sunos_semsys(int op, unsigned long arg1, unsigned long arg2,
+			    unsigned long arg3, void *ptr)
+{
+	union semun arg4;
+	int ret;
+
+	switch (op) {
+	case 0:
+		/* Most arguments match on a 1:1 basis but cmd doesn't */
+		switch(arg3) {
+		case 4:
+			arg3=GETPID; break;
+		case 5:
+			arg3=GETVAL; break;
+		case 6:
+			arg3=GETALL; break;
+		case 3:
+			arg3=GETNCNT; break;
+		case 7:
+			arg3=GETZCNT; break;
+		case 8:
+			arg3=SETVAL; break;
+		case 9:
+			arg3=SETALL; break;
+		}
+		/* sys_semctl(): */
+		/* value to modify semaphore to */
+		arg4.__pad = (void __user *) ptr;
+		ret = sys_semctl((int)arg1, (int)arg2, (int)arg3, arg4 );
+		break;
+	case 1:
+		/* sys_semget(): */
+		ret = sys_semget((key_t)arg1, (int)arg2, (int)arg3);
+		break;
+	case 2:
+		/* sys_semop(): */
+		ret = sys_semop((int)arg1, (struct sembuf __user *)arg2, (unsigned)arg3);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	};
+	return ret;
+}
+
+asmlinkage int sunos_msgsys(int op, unsigned long arg1, unsigned long arg2,
+			    unsigned long arg3, unsigned long arg4)
+{
+	struct sparc_stackf *sp;
+	unsigned long arg5;
+	int rval;
+
+	switch(op) {
+	case 0:
+		rval = sys_msgget((key_t)arg1, (int)arg2);
+		break;
+	case 1:
+		rval = sys_msgctl((int)arg1, (int)arg2,
+				  (struct msqid_ds __user *)arg3);
+		break;
+	case 2:
+		lock_kernel();
+		sp = (struct sparc_stackf *)current->thread.kregs->u_regs[UREG_FP];
+		arg5 = sp->xxargs[0];
+		unlock_kernel();
+		rval = sys_msgrcv((int)arg1, (struct msgbuf __user *)arg2,
+				  (size_t)arg3, (long)arg4, (int)arg5);
+		break;
+	case 3:
+		rval = sys_msgsnd((int)arg1, (struct msgbuf __user *)arg2,
+				  (size_t)arg3, (int)arg4);
+		break;
+	default:
+		rval = -EINVAL;
+		break;
+	}
+	return rval;
+}
+
+asmlinkage int sunos_shmsys(int op, unsigned long arg1, unsigned long arg2,
+			    unsigned long arg3)
+{
+	unsigned long raddr;
+	int rval;
+
+	switch(op) {
+	case 0:
+		/* do_shmat(): attach a shared memory area */
+		rval = do_shmat((int)arg1,(char __user *)arg2,(int)arg3,&raddr);
+		if (!rval)
+			rval = (int) raddr;
+		break;
+	case 1:
+		/* sys_shmctl(): modify shared memory area attr. */
+		rval = sys_shmctl((int)arg1,(int)arg2,(struct shmid_ds __user *)arg3);
+		break;
+	case 2:
+		/* sys_shmdt(): detach a shared memory area */
+		rval = sys_shmdt((char __user *)arg1);
+		break;
+	case 3:
+		/* sys_shmget(): get a shared memory area */
+		rval = sys_shmget((key_t)arg1,(int)arg2,(int)arg3);
+		break;
+	default:
+		rval = -EINVAL;
+		break;
+	};
+	return rval;
+}
+
+#define SUNOS_EWOULDBLOCK 35
+
+/* see the sunos man page read(2v) for an explanation
+   of this garbage. We use O_NDELAY to mark
+   file descriptors that have been set non-blocking 
+   using 4.2BSD style calls. (tridge) */
+
+static inline int check_nonblock(int ret, int fd)
+{
+	if (ret == -EAGAIN) {
+		struct file * file = fget(fd);
+		if (file) {
+			if (file->f_flags & O_NDELAY)
+				ret = -SUNOS_EWOULDBLOCK;
+			fput(file);
+		}
+	}
+	return ret;
+}
+
+asmlinkage int sunos_read(unsigned int fd, char __user *buf, int count)
+{
+	int ret;
+
+	ret = check_nonblock(sys_read(fd,buf,count),fd);
+	return ret;
+}
+
+asmlinkage int sunos_readv(unsigned long fd, const struct iovec __user *vector,
+			   long count)
+{
+	int ret;
+
+	ret = check_nonblock(sys_readv(fd,vector,count),fd);
+	return ret;
+}
+
+asmlinkage int sunos_write(unsigned int fd, char __user *buf, int count)
+{
+	int ret;
+
+	ret = check_nonblock(sys_write(fd,buf,count),fd);
+	return ret;
+}
+
+asmlinkage int sunos_writev(unsigned long fd,
+			    const struct iovec __user *vector, long count)
+{
+	int ret;
+
+	ret = check_nonblock(sys_writev(fd,vector,count),fd);
+	return ret;
+}
+
+asmlinkage int sunos_recv(int fd, void __user *ubuf, int size, unsigned flags)
+{
+	int ret;
+
+	ret = check_nonblock(sys_recv(fd,ubuf,size,flags),fd);
+	return ret;
+}
+
+asmlinkage int sunos_send(int fd, void __user *buff, int len, unsigned flags)
+{
+	int ret;
+
+	ret = check_nonblock(sys_send(fd,buff,len,flags),fd);
+	return ret;
+}
+
+asmlinkage int sunos_accept(int fd, struct sockaddr __user *sa,
+			    int __user *addrlen)
+{
+	int ret;
+
+	while (1) {
+		ret = check_nonblock(sys_accept(fd,sa,addrlen),fd);	
+		if (ret != -ENETUNREACH && ret != -EHOSTUNREACH)
+			break;
+	}
+
+	return ret;
+}
+
+#define SUNOS_SV_INTERRUPT 2
+
+asmlinkage int
+sunos_sigaction(int sig, const struct old_sigaction __user *act,
+		struct old_sigaction __user *oact)
+{
+	struct k_sigaction new_ka, old_ka;
+	int ret;
+
+	if (act) {
+		old_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;
+		__get_user(mask, &act->sa_mask);
+		new_ka.sa.sa_restorer = NULL;
+		new_ka.ka_restorer = NULL;
+		siginitset(&new_ka.sa.sa_mask, mask);
+		new_ka.sa.sa_flags ^= SUNOS_SV_INTERRUPT;
+	}
+
+	ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+	if (!ret && oact) {
+		/* In the clone() case we could copy half consistent
+		 * state to the user, however this could sleep and
+		 * deadlock us if we held the signal lock on SMP.  So for
+		 * now I take the easy way out and do no locking.
+		 * But then again we don't support SunOS lwp's anyways ;-)
+		 */
+		old_ka.sa.sa_flags ^= SUNOS_SV_INTERRUPT;
+		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;
+		__put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
+	}
+
+	return ret;
+}
+
+
+asmlinkage int sunos_setsockopt(int fd, int level, int optname,
+				char __user *optval, int optlen)
+{
+	int tr_opt = optname;
+	int ret;
+
+	if (level == SOL_IP) {
+		/* Multicast socketopts (ttl, membership) */
+		if (tr_opt >=2 && tr_opt <= 6)
+			tr_opt += 30;
+	}
+	ret = sys_setsockopt(fd, level, tr_opt, optval, optlen);
+	return ret;
+}
+
+asmlinkage int sunos_getsockopt(int fd, int level, int optname,
+				char __user *optval, int __user *optlen)
+{
+	int tr_opt = optname;
+	int ret;
+
+	if (level == SOL_IP) {
+		/* Multicast socketopts (ttl, membership) */
+		if (tr_opt >=2 && tr_opt <= 6)
+			tr_opt += 30;
+	}
+	ret = sys_getsockopt(fd, level, tr_opt, optval, optlen);
+	return ret;
+}
diff --git a/arch/sparc/kernel/systbls.S b/arch/sparc/kernel/systbls.S
new file mode 100644
index 0000000..928ffeb
--- /dev/null
+++ b/arch/sparc/kernel/systbls.S
@@ -0,0 +1,186 @@
+/* $Id: systbls.S,v 1.103 2002/02/08 03:57:14 davem Exp $
+ * systbls.S: System call entry point tables for OS compatibility.
+ *            The native Linux system call table lives here also.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * Based upon preliminary work which is:
+ *
+ * Copyright (C) 1995 Adrian M. Rodriguez (adrian@remus.rutgers.edu)
+ */
+
+#include <linux/config.h>
+
+	.data
+	.align 4
+
+	/* First, the Linux native syscall table. */
+
+	.globl sys_call_table
+sys_call_table:
+/*0*/	.long sys_restart_syscall, sys_exit, sys_fork, sys_read, sys_write
+/*5*/	.long sys_open, sys_close, sys_wait4, sys_creat, sys_link
+/*10*/  .long sys_unlink, sunos_execv, sys_chdir, sys_chown16, sys_mknod
+/*15*/	.long sys_chmod, sys_lchown16, sparc_brk, sys_nis_syscall, sys_lseek
+/*20*/	.long sys_getpid, sys_capget, sys_capset, sys_setuid16, sys_getuid16
+/*25*/	.long sys_time, sys_ptrace, sys_alarm, sys_sigaltstack, sys_pause
+/*30*/	.long sys_utime, sys_lchown, sys_fchown, sys_access, sys_nice
+/*35*/	.long sys_chown, sys_sync, sys_kill, sys_newstat, sys_sendfile
+/*40*/	.long sys_newlstat, sys_dup, sys_pipe, sys_times, sys_getuid
+/*45*/	.long sys_umount, sys_setgid16, sys_getgid16, sys_signal, sys_geteuid16
+/*50*/	.long sys_getegid16, sys_acct, sys_nis_syscall, sys_getgid, sys_ioctl
+/*55*/	.long sys_reboot, sys_mmap2, sys_symlink, sys_readlink, sys_execve
+/*60*/	.long sys_umask, sys_chroot, sys_newfstat, sys_fstat64, sys_getpagesize
+/*65*/	.long sys_msync, sys_vfork, sys_pread64, sys_pwrite64, sys_geteuid
+/*70*/	.long sys_getegid, sys_mmap, sys_setreuid, sys_munmap, sys_mprotect
+/*75*/	.long sys_madvise, sys_vhangup, sys_truncate64, sys_mincore, sys_getgroups16
+/*80*/	.long sys_setgroups16, sys_getpgrp, sys_setgroups, sys_setitimer, sys_ftruncate64
+/*85*/	.long sys_swapon, sys_getitimer, sys_setuid, sys_sethostname, sys_setgid
+/*90*/	.long sys_dup2, sys_setfsuid, sys_fcntl, sys_select, sys_setfsgid
+/*95*/	.long sys_fsync, sys_setpriority, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+/*100*/	.long sys_getpriority, sys_rt_sigreturn, sys_rt_sigaction, sys_rt_sigprocmask, sys_rt_sigpending
+/*105*/	.long sys_rt_sigtimedwait, sys_rt_sigqueueinfo, sys_rt_sigsuspend, sys_setresuid, sys_getresuid
+/*110*/	.long sys_setresgid, sys_getresgid, sys_setregid, sys_nis_syscall, sys_nis_syscall
+/*115*/	.long sys_getgroups, sys_gettimeofday, sys_getrusage, sys_nis_syscall, sys_getcwd
+/*120*/	.long sys_readv, sys_writev, sys_settimeofday, sys_fchown16, sys_fchmod
+/*125*/	.long sys_nis_syscall, sys_setreuid16, sys_setregid16, sys_rename, sys_truncate
+/*130*/	.long sys_ftruncate, sys_flock, sys_lstat64, sys_nis_syscall, sys_nis_syscall
+/*135*/	.long sys_nis_syscall, sys_mkdir, sys_rmdir, sys_utimes, sys_stat64
+/*140*/	.long sys_sendfile64, sys_nis_syscall, sys_futex, sys_gettid, sys_getrlimit
+/*145*/	.long sys_setrlimit, sys_pivot_root, sys_prctl, sys_pciconfig_read, sys_pciconfig_write
+/*150*/	.long sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_poll, sys_getdents64
+/*155*/	.long sys_fcntl64, sys_ni_syscall, sys_statfs, sys_fstatfs, sys_oldumount
+/*160*/	.long sys_sched_setaffinity, sys_sched_getaffinity, sys_getdomainname, sys_setdomainname, sys_nis_syscall
+/*165*/	.long sys_quotactl, sys_set_tid_address, sys_mount, sys_ustat, sys_setxattr
+/*170*/	.long sys_lsetxattr, sys_fsetxattr, sys_getxattr, sys_lgetxattr, sys_getdents
+/*175*/	.long sys_setsid, sys_fchdir, sys_fgetxattr, sys_listxattr, sys_llistxattr
+/*180*/	.long sys_flistxattr, sys_removexattr, sys_lremovexattr, sys_sigpending, sys_ni_syscall
+/*185*/	.long sys_setpgid, sys_fremovexattr, sys_tkill, sys_exit_group, sys_newuname
+/*190*/	.long sys_init_module, sys_personality, sparc_remap_file_pages, sys_epoll_create, sys_epoll_ctl
+/*195*/	.long sys_epoll_wait, sys_nis_syscall, sys_getppid, sparc_sigaction, sys_sgetmask
+/*200*/	.long sys_ssetmask, sys_sigsuspend, sys_newlstat, sys_uselib, old_readdir
+/*205*/	.long sys_readahead, sys_socketcall, sys_syslog, sys_lookup_dcookie, sys_fadvise64
+/*210*/	.long sys_fadvise64_64, sys_tgkill, sys_waitpid, sys_swapoff, sys_sysinfo
+/*215*/	.long sys_ipc, sys_sigreturn, sys_clone, sys_nis_syscall, sys_adjtimex
+/*220*/	.long sys_sigprocmask, sys_ni_syscall, sys_delete_module, sys_ni_syscall, sys_getpgid
+/*225*/	.long sys_bdflush, sys_sysfs, sys_nis_syscall, sys_setfsuid16, sys_setfsgid16
+/*230*/	.long sys_select, sys_time, sys_nis_syscall, sys_stime, sys_statfs64
+					  /* "We are the Knights of the Forest of Ni!!" */
+/*235*/	.long sys_fstatfs64, sys_llseek, sys_mlock, sys_munlock, sys_mlockall
+/*240*/	.long sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_sched_setscheduler, sys_sched_getscheduler
+/*245*/	.long sys_sched_yield, sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, sys_nanosleep
+/*250*/	.long sparc_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl
+/*255*/	.long sys_nis_syscall, sys_clock_settime, sys_clock_gettime, sys_clock_getres, sys_clock_nanosleep
+/*260*/	.long sys_sched_getaffinity, sys_sched_setaffinity, sys_timer_settime, sys_timer_gettime, sys_timer_getoverrun
+/*265*/	.long sys_timer_delete, sys_timer_create, sys_nis_syscall, sys_io_setup, sys_io_destroy
+/*270*/	.long sys_io_submit, sys_io_cancel, sys_io_getevents, sys_mq_open, sys_mq_unlink
+/*275*/	.long sys_mq_timedsend, sys_mq_timedreceive, sys_mq_notify, sys_mq_getsetattr, sys_waitid
+/*280*/	.long sys_ni_syscall, sys_add_key, sys_request_key, sys_keyctl
+
+#ifdef CONFIG_SUNOS_EMUL
+	/* Now the SunOS syscall table. */
+
+	.align 4
+	.globl sunos_sys_table
+sunos_sys_table:
+/*0*/	.long sunos_indir, sys_exit, sys_fork
+	.long sunos_read, sunos_write, sys_open
+	.long sys_close, sunos_wait4, sys_creat
+	.long sys_link, sys_unlink, sunos_execv
+	.long sys_chdir, sunos_nosys, sys_mknod
+	.long sys_chmod, sys_lchown16, sunos_brk
+	.long sunos_nosys, sys_lseek, sunos_getpid
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_getuid, sunos_nosys, sys_ptrace
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sys_access, sunos_nosys, sunos_nosys
+	.long sys_sync, sys_kill, sys_newstat
+	.long sunos_nosys, sys_newlstat, sys_dup
+	.long sys_pipe, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_getgid
+	.long sunos_nosys, sunos_nosys
+/*50*/	.long sunos_nosys, sys_acct, sunos_nosys
+	.long sunos_mctl, sunos_ioctl, sys_reboot
+	.long sunos_nosys, sys_symlink, sys_readlink
+	.long sys_execve, sys_umask, sys_chroot
+	.long sys_newfstat, sunos_nosys, sys_getpagesize
+	.long sys_msync, sys_vfork, sunos_nosys
+	.long sunos_nosys, sunos_sbrk, sunos_sstk
+	.long sunos_mmap, sunos_vadvise, sys_munmap
+	.long sys_mprotect, sys_madvise, sys_vhangup
+	.long sunos_nosys, sys_mincore, sys_getgroups16
+	.long sys_setgroups16, sys_getpgrp, sunos_setpgrp
+	.long sys_setitimer, sunos_nosys, sys_swapon
+	.long sys_getitimer, sys_gethostname, sys_sethostname
+	.long sunos_getdtablesize, sys_dup2, sunos_nop
+	.long sys_fcntl, sunos_select, sunos_nop
+	.long sys_fsync, sys_setpriority, sys_socket
+	.long sys_connect, sunos_accept
+/*100*/	.long sys_getpriority, sunos_send, sunos_recv
+	.long sunos_nosys, sys_bind, sunos_setsockopt
+	.long sys_listen, sunos_nosys, sunos_sigaction
+	.long sunos_sigblock, sunos_sigsetmask, sys_sigpause
+	.long sys_sigstack, sys_recvmsg, sys_sendmsg
+	.long sunos_nosys, sys_gettimeofday, sys_getrusage
+	.long sunos_getsockopt, sunos_nosys, sunos_readv
+	.long sunos_writev, sys_settimeofday, sys_fchown16
+	.long sys_fchmod, sys_recvfrom, sys_setreuid16
+	.long sys_setregid16, sys_rename, sys_truncate
+	.long sys_ftruncate, sys_flock, sunos_nosys
+	.long sys_sendto, sys_shutdown, sys_socketpair
+	.long sys_mkdir, sys_rmdir, sys_utimes
+	.long sys_sigreturn, sunos_nosys, sys_getpeername
+	.long sunos_gethostid, sunos_nosys, sys_getrlimit
+	.long sys_setrlimit, sunos_killpg, sunos_nosys
+	.long sunos_nosys, sunos_nosys
+/*150*/	.long sys_getsockname, sunos_nosys, sunos_nosys
+	.long sys_poll, sunos_nosys, sunos_nosys
+	.long sunos_getdirentries, sys_statfs, sys_fstatfs
+	.long sys_oldumount, sunos_nosys, sunos_nosys
+	.long sys_getdomainname, sys_setdomainname
+	.long sunos_nosys, sys_quotactl, sunos_nosys
+	.long sunos_mount, sys_ustat, sunos_semsys
+	.long sunos_msgsys, sunos_shmsys, sunos_audit
+	.long sunos_nosys, sunos_getdents, sys_setsid
+	.long sys_fchdir, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sys_sigpending, sunos_nosys
+	.long sys_setpgid, sunos_pathconf, sunos_fpathconf
+	.long sunos_sysconf, sunos_uname, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+/*200*/	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys
+/*250*/	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys
+/*260*/	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys
+/*270*/	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys
+/*280*/	.long sunos_nosys, sunos_nosys, sunos_nosys
+	.long sunos_nosys
+
+#endif
diff --git a/arch/sparc/kernel/tadpole.c b/arch/sparc/kernel/tadpole.c
new file mode 100644
index 0000000..f476a5f
--- /dev/null
+++ b/arch/sparc/kernel/tadpole.c
@@ -0,0 +1,126 @@
+/* tadpole.c: Probing for the tadpole clock stopping h/w at boot time.
+ *
+ * Copyright (C) 1996 David Redman (djhr@tadpole.co.uk)
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+
+#include <asm/asi.h>
+#include <asm/oplib.h>
+#include <asm/io.h>
+
+#define MACIO_SCSI_CSR_ADDR	0x78400000
+#define MACIO_EN_DMA		0x00000200
+#define CLOCK_INIT_DONE		1
+
+static int clk_state;
+static volatile unsigned char *clk_ctrl;
+void (*cpu_pwr_save)(void);
+
+static inline unsigned int ldphys(unsigned int addr)
+{
+	unsigned long data;
+    
+	__asm__ __volatile__("\n\tlda [%1] %2, %0\n\t" : 
+			     "=r" (data) :
+			     "r" (addr), "i" (ASI_M_BYPASS));
+	return data;
+}
+
+static void clk_init(void)
+{
+	__asm__ __volatile__("mov 0x6c, %%g1\n\t"
+			     "mov 0x4c, %%g2\n\t"
+			     "mov 0xdf, %%g3\n\t"
+			     "stb %%g1, [%0+3]\n\t"
+			     "stb %%g2, [%0+3]\n\t"
+			     "stb %%g3, [%0+3]\n\t" : :
+			     "r" (clk_ctrl) :
+			     "g1", "g2", "g3");
+}
+
+static void clk_slow(void)
+{
+	__asm__ __volatile__("mov 0xcc, %%g2\n\t"
+			     "mov 0x4c, %%g3\n\t"
+			     "mov 0xcf, %%g4\n\t"
+			     "mov 0xdf, %%g5\n\t"
+			     "stb %%g2, [%0+3]\n\t"
+			     "stb %%g3, [%0+3]\n\t"
+			     "stb %%g4, [%0+3]\n\t"
+			     "stb %%g5, [%0+3]\n\t" : :
+			     "r" (clk_ctrl) :
+			     "g2", "g3", "g4", "g5");
+}
+
+/*
+ * Tadpole is guaranteed to be UP, using local_irq_save.
+ */
+static void tsu_clockstop(void)
+{
+	unsigned int mcsr;
+	unsigned long flags;
+
+	if (!clk_ctrl)
+		return;
+	if (!(clk_state & CLOCK_INIT_DONE)) {
+		local_irq_save(flags);
+		clk_init();
+		clk_state |= CLOCK_INIT_DONE;       /* all done */
+		local_irq_restore(flags);
+		return;
+	}
+	if (!(clk_ctrl[2] & 1))
+		return;               /* no speed up yet */
+
+	local_irq_save(flags);
+
+	/* if SCSI DMA in progress, don't slow clock */
+	mcsr = ldphys(MACIO_SCSI_CSR_ADDR);
+	if ((mcsr&MACIO_EN_DMA) != 0) {
+		local_irq_restore(flags);
+		return;
+	}
+	/* TODO... the minimum clock setting ought to increase the
+	 * memory refresh interval..
+	 */
+	clk_slow();
+	local_irq_restore(flags);
+}
+
+static void swift_clockstop(void)
+{
+	if (!clk_ctrl)
+		return;
+	clk_ctrl[0] = 0;
+}
+
+void __init clock_stop_probe(void)
+{
+	unsigned int node, clk_nd;
+	char name[20];
+    
+	prom_getstring(prom_root_node, "name", name, sizeof(name));
+	if (strncmp(name, "Tadpole", 7))
+		return;
+	node = prom_getchild(prom_root_node);
+	node = prom_searchsiblings(node, "obio");
+	node = prom_getchild(node);
+	clk_nd = prom_searchsiblings(node, "clk-ctrl");
+	if (!clk_nd)
+		return;
+	printk("Clock Stopping h/w detected... ");
+	clk_ctrl = (char *) prom_getint(clk_nd, "address");
+	clk_state = 0;
+	if (name[10] == '\0') {
+		cpu_pwr_save = tsu_clockstop;
+		printk("enabled (S3)\n");
+	} else if ((name[10] == 'X') || (name[10] == 'G')) {
+		cpu_pwr_save = swift_clockstop;
+		printk("enabled (%s)\n",name+7);
+	} else
+		printk("disabled %s\n",name+7);
+}
diff --git a/arch/sparc/kernel/tick14.c b/arch/sparc/kernel/tick14.c
new file mode 100644
index 0000000..fd8005a
--- /dev/null
+++ b/arch/sparc/kernel/tick14.c
@@ -0,0 +1,85 @@
+/* tick14.c
+ * linux/arch/sparc/kernel/tick14.c
+ *
+ * Copyright (C) 1996 David Redman (djhr@tadpole.co.uk)
+ *
+ * This file handles the Sparc specific level14 ticker
+ * This is really useful for profiling OBP uses it for keyboard
+ * aborts and other stuff.
+ *
+ *
+ */
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/timex.h>
+#include <linux/interrupt.h>
+
+#include <asm/oplib.h>
+#include <asm/segment.h>
+#include <asm/timer.h>
+#include <asm/mostek.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+extern unsigned long lvl14_save[5];
+static unsigned long *linux_lvl14 = NULL;
+static unsigned long obp_lvl14[4];
+ 
+/*
+ * Call with timer IRQ closed.
+ * First time we do it with disable_irq, later prom code uses spin_lock_irq().
+ */
+void install_linux_ticker(void)
+{
+
+	if (!linux_lvl14)
+		return;
+	linux_lvl14[0] =  lvl14_save[0];
+	linux_lvl14[1] =  lvl14_save[1];
+	linux_lvl14[2] =  lvl14_save[2];
+	linux_lvl14[3] =  lvl14_save[3];
+}
+
+void install_obp_ticker(void)
+{
+
+	if (!linux_lvl14)
+		return;
+	linux_lvl14[0] =  obp_lvl14[0];
+	linux_lvl14[1] =  obp_lvl14[1];
+	linux_lvl14[2] =  obp_lvl14[2];
+	linux_lvl14[3] =  obp_lvl14[3]; 
+}
+
+void claim_ticker14(irqreturn_t (*handler)(int, void *, struct pt_regs *),
+		    int irq_nr, unsigned int timeout )
+{
+	int cpu = smp_processor_id();
+
+	/* first we copy the obp handler instructions
+	 */
+	disable_irq(irq_nr);
+	if (!handler)
+		return;
+    
+	linux_lvl14 = (unsigned long *)lvl14_save[4];
+	obp_lvl14[0] = linux_lvl14[0];
+	obp_lvl14[1] = linux_lvl14[1];
+	obp_lvl14[2] = linux_lvl14[2];
+	obp_lvl14[3] = linux_lvl14[3];
+
+	if (!request_irq(irq_nr,
+			 handler,
+			 (SA_INTERRUPT | SA_STATIC_ALLOC),
+			 "counter14",
+			 NULL)) {
+		install_linux_ticker();
+		load_profile_irq(cpu, timeout);
+		enable_irq(irq_nr);
+	}
+}
diff --git a/arch/sparc/kernel/time.c b/arch/sparc/kernel/time.c
new file mode 100644
index 0000000..6486cbf
--- /dev/null
+++ b/arch/sparc/kernel/time.c
@@ -0,0 +1,641 @@
+/* $Id: time.c,v 1.60 2002/01/23 14:33:55 davem Exp $
+ * linux/arch/sparc/kernel/time.c
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu)
+ *
+ * Chris Davis (cdavis@cois.on.ca) 03/27/1998
+ * Added support for the intersil on the sun4/4200
+ *
+ * Gleb Raiko (rajko@mech.math.msu.su) 08/18/1998
+ * Support for MicroSPARC-IIep, PCI CPU.
+ *
+ * This file handles the Sparc specific time handling details.
+ *
+ * 1997-09-10	Updated NTP code according to technical memorandum Jan '96
+ *		"A Kernel Model for Precision Timekeeping" by Dave Mills
+ */
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/profile.h>
+
+#include <asm/oplib.h>
+#include <asm/segment.h>
+#include <asm/timer.h>
+#include <asm/mostek.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/idprom.h>
+#include <asm/machines.h>
+#include <asm/sun4paddr.h>
+#include <asm/page.h>
+#include <asm/pcic.h>
+
+extern unsigned long wall_jiffies;
+
+u64 jiffies_64 = INITIAL_JIFFIES;
+
+EXPORT_SYMBOL(jiffies_64);
+
+DEFINE_SPINLOCK(rtc_lock);
+enum sparc_clock_type sp_clock_typ;
+DEFINE_SPINLOCK(mostek_lock);
+void __iomem *mstk48t02_regs = NULL;
+static struct mostek48t08 *mstk48t08_regs = NULL;
+static int set_rtc_mmss(unsigned long);
+static int sbus_do_settimeofday(struct timespec *tv);
+
+#ifdef CONFIG_SUN4
+struct intersil *intersil_clock;
+#define intersil_cmd(intersil_reg, intsil_cmd) intersil_reg->int_cmd_reg = \
+	(intsil_cmd)
+
+#define intersil_intr(intersil_reg, intsil_cmd) intersil_reg->int_intr_reg = \
+	(intsil_cmd)
+
+#define intersil_start(intersil_reg) intersil_cmd(intersil_reg, \
+	( INTERSIL_START | INTERSIL_32K | INTERSIL_NORMAL | INTERSIL_24H |\
+	  INTERSIL_INTR_ENABLE))
+
+#define intersil_stop(intersil_reg) intersil_cmd(intersil_reg, \
+	( INTERSIL_STOP | INTERSIL_32K | INTERSIL_NORMAL | INTERSIL_24H |\
+	  INTERSIL_INTR_ENABLE))
+
+#define intersil_read_intr(intersil_reg, towhere) towhere = \
+	intersil_reg->int_intr_reg
+
+#endif
+
+unsigned long profile_pc(struct pt_regs *regs)
+{
+	extern char __copy_user_begin[], __copy_user_end[];
+	extern char __atomic_begin[], __atomic_end[];
+	extern char __bzero_begin[], __bzero_end[];
+	extern char __bitops_begin[], __bitops_end[];
+
+	unsigned long pc = regs->pc;
+
+	if (in_lock_functions(pc) ||
+	    (pc >= (unsigned long) __copy_user_begin &&
+	     pc < (unsigned long) __copy_user_end) ||
+	    (pc >= (unsigned long) __atomic_begin &&
+	     pc < (unsigned long) __atomic_end) ||
+	    (pc >= (unsigned long) __bzero_begin &&
+	     pc < (unsigned long) __bzero_end) ||
+	    (pc >= (unsigned long) __bitops_begin &&
+	     pc < (unsigned long) __bitops_end))
+		pc = regs->u_regs[UREG_RETPC];
+	return pc;
+}
+
+__volatile__ unsigned int *master_l10_counter;
+__volatile__ unsigned int *master_l10_limit;
+
+/*
+ * timer_interrupt() needs to keep up the real-time clock,
+ * as well as call the "do_timer()" routine every clocktick
+ */
+
+#define TICK_SIZE (tick_nsec / 1000)
+
+irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	/* last time the cmos clock got updated */
+	static long last_rtc_update;
+
+#ifndef CONFIG_SMP
+	profile_tick(CPU_PROFILING, regs);
+#endif
+
+	/* Protect counter clear so that do_gettimeoffset works */
+	write_seqlock(&xtime_lock);
+#ifdef CONFIG_SUN4
+	if((idprom->id_machtype == (SM_SUN4 | SM_4_260)) ||
+	   (idprom->id_machtype == (SM_SUN4 | SM_4_110))) {
+		int temp;
+        	intersil_read_intr(intersil_clock, temp);
+		/* re-enable the irq */
+		enable_pil_irq(10);
+	}
+#endif
+	clear_clock_irq();
+
+	do_timer(regs);
+#ifndef CONFIG_SMP
+	update_process_times(user_mode(regs));
+#endif
+
+
+	/* Determine when to update the Mostek clock. */
+	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 (set_rtc_mmss(xtime.tv_sec) == 0)
+	    last_rtc_update = xtime.tv_sec;
+	  else
+	    last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+	}
+	write_sequnlock(&xtime_lock);
+
+	return IRQ_HANDLED;
+}
+
+/* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */
+static void __init kick_start_clock(void)
+{
+	struct mostek48t02 *regs = (struct mostek48t02 *)mstk48t02_regs;
+	unsigned char sec;
+	int i, count;
+
+	prom_printf("CLOCK: Clock was stopped. Kick start ");
+
+	spin_lock_irq(&mostek_lock);
+
+	/* Turn on the kick start bit to start the oscillator. */
+	regs->creg |= MSTK_CREG_WRITE;
+	regs->sec &= ~MSTK_STOP;
+	regs->hour |= MSTK_KICK_START;
+	regs->creg &= ~MSTK_CREG_WRITE;
+
+	spin_unlock_irq(&mostek_lock);
+
+	/* Delay to allow the clock oscillator to start. */
+	sec = MSTK_REG_SEC(regs);
+	for (i = 0; i < 3; i++) {
+		while (sec == MSTK_REG_SEC(regs))
+			for (count = 0; count < 100000; count++)
+				/* nothing */ ;
+		prom_printf(".");
+		sec = regs->sec;
+	}
+	prom_printf("\n");
+
+	spin_lock_irq(&mostek_lock);
+
+	/* Turn off kick start and set a "valid" time and date. */
+	regs->creg |= MSTK_CREG_WRITE;
+	regs->hour &= ~MSTK_KICK_START;
+	MSTK_SET_REG_SEC(regs,0);
+	MSTK_SET_REG_MIN(regs,0);
+	MSTK_SET_REG_HOUR(regs,0);
+	MSTK_SET_REG_DOW(regs,5);
+	MSTK_SET_REG_DOM(regs,1);
+	MSTK_SET_REG_MONTH(regs,8);
+	MSTK_SET_REG_YEAR(regs,1996 - MSTK_YEAR_ZERO);
+	regs->creg &= ~MSTK_CREG_WRITE;
+
+	spin_unlock_irq(&mostek_lock);
+
+	/* Ensure the kick start bit is off. If it isn't, turn it off. */
+	while (regs->hour & MSTK_KICK_START) {
+		prom_printf("CLOCK: Kick start still on!\n");
+
+		spin_lock_irq(&mostek_lock);
+		regs->creg |= MSTK_CREG_WRITE;
+		regs->hour &= ~MSTK_KICK_START;
+		regs->creg &= ~MSTK_CREG_WRITE;
+		spin_unlock_irq(&mostek_lock);
+	}
+
+	prom_printf("CLOCK: Kick start procedure successful.\n");
+}
+
+/* Return nonzero if the clock chip battery is low. */
+static __inline__ int has_low_battery(void)
+{
+	struct mostek48t02 *regs = (struct mostek48t02 *)mstk48t02_regs;
+	unsigned char data1, data2;
+
+	spin_lock_irq(&mostek_lock);
+	data1 = regs->eeprom[0];	/* Read some data. */
+	regs->eeprom[0] = ~data1;	/* Write back the complement. */
+	data2 = regs->eeprom[0];	/* Read back the complement. */
+	regs->eeprom[0] = data1;	/* Restore the original value. */
+	spin_unlock_irq(&mostek_lock);
+
+	return (data1 == data2);	/* Was the write blocked? */
+}
+
+/* Probe for the real time clock chip on Sun4 */
+static __inline__ void sun4_clock_probe(void)
+{
+#ifdef CONFIG_SUN4
+	int temp;
+	struct resource r;
+
+	memset(&r, 0, sizeof(r));
+	if( idprom->id_machtype == (SM_SUN4 | SM_4_330) ) {
+		sp_clock_typ = MSTK48T02;
+		r.start = sun4_clock_physaddr;
+		mstk48t02_regs = sbus_ioremap(&r, 0,
+				       sizeof(struct mostek48t02), NULL);
+		mstk48t08_regs = NULL;  /* To catch weirdness */
+		intersil_clock = NULL;  /* just in case */
+
+		/* Kick start the clock if it is completely stopped. */
+		if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP)
+			kick_start_clock();
+	} else if( idprom->id_machtype == (SM_SUN4 | SM_4_260)) {
+		/* intersil setup code */
+		printk("Clock: INTERSIL at %8x ",sun4_clock_physaddr);
+		sp_clock_typ = INTERSIL;
+		r.start = sun4_clock_physaddr;
+		intersil_clock = (struct intersil *) 
+		    sbus_ioremap(&r, 0, sizeof(*intersil_clock), "intersil");
+		mstk48t02_regs = 0;  /* just be sure */
+		mstk48t08_regs = NULL;  /* ditto */
+		/* initialise the clock */
+
+		intersil_intr(intersil_clock,INTERSIL_INT_100HZ);
+
+		intersil_start(intersil_clock);
+
+		intersil_read_intr(intersil_clock, temp);
+                while (!(temp & 0x80))
+                        intersil_read_intr(intersil_clock, temp);
+
+                intersil_read_intr(intersil_clock, temp);
+                while (!(temp & 0x80))
+                        intersil_read_intr(intersil_clock, temp);
+
+		intersil_stop(intersil_clock);
+
+	}
+#endif
+}
+
+/* Probe for the mostek real time clock chip. */
+static __inline__ void clock_probe(void)
+{
+	struct linux_prom_registers clk_reg[2];
+	char model[128];
+	register int node, cpuunit, bootbus;
+	struct resource r;
+
+	cpuunit = bootbus = 0;
+	memset(&r, 0, sizeof(r));
+
+	/* Determine the correct starting PROM node for the probe. */
+	node = prom_getchild(prom_root_node);
+	switch (sparc_cpu_model) {
+	case sun4c:
+		break;
+	case sun4m:
+		node = prom_getchild(prom_searchsiblings(node, "obio"));
+		break;
+	case sun4d:
+		node = prom_getchild(bootbus = prom_searchsiblings(prom_getchild(cpuunit = prom_searchsiblings(node, "cpu-unit")), "bootbus"));
+		break;
+	default:
+		prom_printf("CLOCK: Unsupported architecture!\n");
+		prom_halt();
+	}
+
+	/* Find the PROM node describing the real time clock. */
+	sp_clock_typ = MSTK_INVALID;
+	node = prom_searchsiblings(node,"eeprom");
+	if (!node) {
+		prom_printf("CLOCK: No clock found!\n");
+		prom_halt();
+	}
+
+	/* Get the model name and setup everything up. */
+	model[0] = '\0';
+	prom_getstring(node, "model", model, sizeof(model));
+	if (strcmp(model, "mk48t02") == 0) {
+		sp_clock_typ = MSTK48T02;
+		if (prom_getproperty(node, "reg", (char *) clk_reg, sizeof(clk_reg)) == -1) {
+			prom_printf("clock_probe: FAILED!\n");
+			prom_halt();
+		}
+		if (sparc_cpu_model == sun4d)
+			prom_apply_generic_ranges (bootbus, cpuunit, clk_reg, 1);
+		else
+			prom_apply_obio_ranges(clk_reg, 1);
+		/* Map the clock register io area read-only */
+		r.flags = clk_reg[0].which_io;
+		r.start = clk_reg[0].phys_addr;
+		mstk48t02_regs = sbus_ioremap(&r, 0,
+		    sizeof(struct mostek48t02), "mk48t02");
+		mstk48t08_regs = NULL;  /* To catch weirdness */
+	} else if (strcmp(model, "mk48t08") == 0) {
+		sp_clock_typ = MSTK48T08;
+		if(prom_getproperty(node, "reg", (char *) clk_reg,
+				    sizeof(clk_reg)) == -1) {
+			prom_printf("clock_probe: FAILED!\n");
+			prom_halt();
+		}
+		if (sparc_cpu_model == sun4d)
+			prom_apply_generic_ranges (bootbus, cpuunit, clk_reg, 1);
+		else
+			prom_apply_obio_ranges(clk_reg, 1);
+		/* Map the clock register io area read-only */
+		/* XXX r/o attribute is somewhere in r.flags */
+		r.flags = clk_reg[0].which_io;
+		r.start = clk_reg[0].phys_addr;
+		mstk48t08_regs = (struct mostek48t08 *) sbus_ioremap(&r, 0,
+		    sizeof(struct mostek48t08), "mk48t08");
+
+		mstk48t02_regs = &mstk48t08_regs->regs;
+	} else {
+		prom_printf("CLOCK: Unknown model name '%s'\n",model);
+		prom_halt();
+	}
+
+	/* Report a low battery voltage condition. */
+	if (has_low_battery())
+		printk(KERN_CRIT "NVRAM: Low battery voltage!\n");
+
+	/* Kick start the clock if it is completely stopped. */
+	if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP)
+		kick_start_clock();
+}
+
+void __init sbus_time_init(void)
+{
+	unsigned int year, mon, day, hour, min, sec;
+	struct mostek48t02 *mregs;
+
+#ifdef CONFIG_SUN4
+	int temp;
+	struct intersil *iregs;
+#endif
+
+	BTFIXUPSET_CALL(bus_do_settimeofday, sbus_do_settimeofday, BTFIXUPCALL_NORM);
+	btfixup();
+
+	if (ARCH_SUN4)
+		sun4_clock_probe();
+	else
+		clock_probe();
+
+	sparc_init_timers(timer_interrupt);
+	
+#ifdef CONFIG_SUN4
+	if(idprom->id_machtype == (SM_SUN4 | SM_4_330)) {
+#endif
+	mregs = (struct mostek48t02 *)mstk48t02_regs;
+	if(!mregs) {
+		prom_printf("Something wrong, clock regs not mapped yet.\n");
+		prom_halt();
+	}		
+	spin_lock_irq(&mostek_lock);
+	mregs->creg |= MSTK_CREG_READ;
+	sec = MSTK_REG_SEC(mregs);
+	min = MSTK_REG_MIN(mregs);
+	hour = MSTK_REG_HOUR(mregs);
+	day = MSTK_REG_DOM(mregs);
+	mon = MSTK_REG_MONTH(mregs);
+	year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) );
+	xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
+	xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
+        set_normalized_timespec(&wall_to_monotonic,
+                                -xtime.tv_sec, -xtime.tv_nsec);
+	mregs->creg &= ~MSTK_CREG_READ;
+	spin_unlock_irq(&mostek_lock);
+#ifdef CONFIG_SUN4
+	} else if(idprom->id_machtype == (SM_SUN4 | SM_4_260) ) {
+		/* initialise the intersil on sun4 */
+
+		iregs=intersil_clock;
+		if(!iregs) {
+			prom_printf("Something wrong, clock regs not mapped yet.\n");
+			prom_halt();
+		}
+
+		intersil_intr(intersil_clock,INTERSIL_INT_100HZ);
+		disable_pil_irq(10);
+		intersil_stop(iregs);
+		intersil_read_intr(intersil_clock, temp);
+
+		temp = iregs->clk.int_csec;
+
+		sec = iregs->clk.int_sec;
+		min = iregs->clk.int_min;
+		hour = iregs->clk.int_hour;
+		day = iregs->clk.int_day;
+		mon = iregs->clk.int_month;
+		year = MSTK_CVT_YEAR(iregs->clk.int_year);
+
+		enable_pil_irq(10);
+		intersil_start(iregs);
+
+		xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
+		xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
+	        set_normalized_timespec(&wall_to_monotonic,
+ 	                               -xtime.tv_sec, -xtime.tv_nsec);
+		printk("%u/%u/%u %u:%u:%u\n",day,mon,year,hour,min,sec);
+	}
+#endif
+
+	/* Now that OBP ticker has been silenced, it is safe to enable IRQ. */
+	local_irq_enable();
+}
+
+void __init time_init(void)
+{
+#ifdef CONFIG_PCI
+	extern void pci_time_init(void);
+	if (pcic_present()) {
+		pci_time_init();
+		return;
+	}
+#endif
+	sbus_time_init();
+}
+
+extern __inline__ unsigned long do_gettimeoffset(void)
+{
+	return (*master_l10_counter >> 10) & 0x1fffff;
+}
+
+/*
+ * Returns nanoseconds
+ * XXX This is a suboptimal implementation.
+ */
+unsigned long long sched_clock(void)
+{
+	return (unsigned long long)jiffies * (1000000000 / HZ);
+}
+
+/* Ok, my cute asm atomicity trick doesn't work anymore.
+ * There are just too many variables that need to be protected
+ * now (both members of xtime, wall_jiffies, et al.)
+ */
+void do_gettimeofday(struct timeval *tv)
+{
+	unsigned long flags;
+	unsigned long seq;
+	unsigned long usec, sec;
+	unsigned long max_ntp_tick = tick_usec - tickadj;
+
+	do {
+		unsigned long lost;
+
+		seq = read_seqbegin_irqsave(&xtime_lock, flags);
+		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_irqrestore(&xtime_lock, seq, flags));
+
+	while (usec >= 1000000) {
+		usec -= 1000000;
+		sec++;
+	}
+
+	tv->tv_sec = sec;
+	tv->tv_usec = usec;
+}
+
+EXPORT_SYMBOL(do_gettimeofday);
+
+int do_settimeofday(struct timespec *tv)
+{
+	int ret;
+
+	write_seqlock_irq(&xtime_lock);
+	ret = bus_do_settimeofday(tv);
+	write_sequnlock_irq(&xtime_lock);
+	clock_was_set();
+	return ret;
+}
+
+EXPORT_SYMBOL(do_settimeofday);
+
+static int sbus_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;
+
+	/*
+	 * 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 -= 1000 * (do_gettimeoffset() +
+			(jiffies - wall_jiffies) * (USEC_PER_SEC / HZ));
+
+	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;
+	return 0;
+}
+
+/*
+ * BUG: This routine does not handle hour overflow properly; it just
+ *      sets the minutes. Usually you won't notice until after reboot!
+ */
+static int set_rtc_mmss(unsigned long nowtime)
+{
+	int real_seconds, real_minutes, mostek_minutes;
+	struct mostek48t02 *regs = (struct mostek48t02 *)mstk48t02_regs;
+	unsigned long flags;
+#ifdef CONFIG_SUN4
+	struct intersil *iregs = intersil_clock;
+	int temp;
+#endif
+
+	/* Not having a register set can lead to trouble. */
+	if (!regs) {
+#ifdef CONFIG_SUN4
+		if(!iregs)
+		return -1;
+	 	else {
+			temp = iregs->clk.int_csec;
+
+			mostek_minutes = iregs->clk.int_min;
+
+			real_seconds = nowtime % 60;
+			real_minutes = nowtime / 60;
+			if (((abs(real_minutes - mostek_minutes) + 15)/30) & 1)
+				real_minutes += 30;	/* correct for half hour time zone */
+			real_minutes %= 60;
+
+			if (abs(real_minutes - mostek_minutes) < 30) {
+				intersil_stop(iregs);
+				iregs->clk.int_sec=real_seconds;
+				iregs->clk.int_min=real_minutes;
+				intersil_start(iregs);
+			} else {
+				printk(KERN_WARNING
+			       "set_rtc_mmss: can't update from %d to %d\n",
+				       mostek_minutes, real_minutes);
+				return -1;
+			}
+			
+			return 0;
+		}
+#endif
+	}
+
+	spin_lock_irqsave(&mostek_lock, flags);
+	/* Read the current RTC minutes. */
+	regs->creg |= MSTK_CREG_READ;
+	mostek_minutes = MSTK_REG_MIN(regs);
+	regs->creg &= ~MSTK_CREG_READ;
+
+	/*
+	 * since we're only adjusting minutes and seconds,
+	 * don't interfere with hour overflow. This avoids
+	 * messing with unknown time zones but requires your
+	 * RTC not to be off by more than 15 minutes
+	 */
+	real_seconds = nowtime % 60;
+	real_minutes = nowtime / 60;
+	if (((abs(real_minutes - mostek_minutes) + 15)/30) & 1)
+		real_minutes += 30;	/* correct for half hour time zone */
+	real_minutes %= 60;
+
+	if (abs(real_minutes - mostek_minutes) < 30) {
+		regs->creg |= MSTK_CREG_WRITE;
+		MSTK_SET_REG_SEC(regs,real_seconds);
+		MSTK_SET_REG_MIN(regs,real_minutes);
+		regs->creg &= ~MSTK_CREG_WRITE;
+		spin_unlock_irqrestore(&mostek_lock, flags);
+		return 0;
+	} else {
+		spin_unlock_irqrestore(&mostek_lock, flags);
+		return -1;
+	}
+}
diff --git a/arch/sparc/kernel/trampoline.S b/arch/sparc/kernel/trampoline.S
new file mode 100644
index 0000000..2dcdaa1
--- /dev/null
+++ b/arch/sparc/kernel/trampoline.S
@@ -0,0 +1,162 @@
+/* $Id: trampoline.S,v 1.14 2002/01/11 08:45:38 davem Exp $
+ * trampoline.S: SMP cpu boot-up trampoline code.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/init.h>
+#include <asm/head.h>
+#include <asm/psr.h>
+#include <asm/page.h>
+#include <asm/asi.h>
+#include <asm/ptrace.h>
+#include <asm/vaddrs.h>
+#include <asm/contregs.h>
+#include <asm/thread_info.h>
+
+	.globl sun4m_cpu_startup, __smp4m_processor_id
+	.globl sun4d_cpu_startup, __smp4d_processor_id
+
+	__INIT
+	.align 4
+
+/* When we start up a cpu for the first time it enters this routine.
+ * This initializes the chip from whatever state the prom left it
+ * in and sets PIL in %psr to 15, no irqs.
+ */
+
+sun4m_cpu_startup:
+cpu1_startup:
+	sethi	%hi(trapbase_cpu1), %g3
+	b	1f
+	 or	%g3, %lo(trapbase_cpu1), %g3
+
+cpu2_startup:
+	sethi	%hi(trapbase_cpu2), %g3
+	b	1f
+	 or	%g3, %lo(trapbase_cpu2), %g3
+
+cpu3_startup:
+	sethi	%hi(trapbase_cpu3), %g3
+	b	1f
+	 or	%g3, %lo(trapbase_cpu3), %g3
+
+1:
+	/* Set up a sane %psr -- PIL<0xf> S<0x1> PS<0x1> CWP<0x0> */
+	set	(PSR_PIL | PSR_S | PSR_PS), %g1
+	wr	%g1, 0x0, %psr		! traps off though
+	WRITE_PAUSE
+
+	/* Our %wim is one behind CWP */
+	mov	2, %g1
+	wr	%g1, 0x0, %wim
+	WRITE_PAUSE
+
+	/* This identifies "this cpu". */
+	wr	%g3, 0x0, %tbr
+	WRITE_PAUSE
+
+	/* Give ourselves a stack and curptr. */
+	set	current_set, %g5
+	srl	%g3, 10, %g4
+	and	%g4, 0xc, %g4
+	ld	[%g5 + %g4], %g6
+
+	sethi	%hi(THREAD_SIZE - STACKFRAME_SZ), %sp
+	or	%sp, %lo(THREAD_SIZE - STACKFRAME_SZ), %sp
+	add	%g6, %sp, %sp
+
+	/* Turn on traps (PSR_ET). */
+	rd	%psr, %g1
+	wr	%g1, PSR_ET, %psr	! traps on
+	WRITE_PAUSE
+
+	/* Init our caches, etc. */
+	set	poke_srmmu, %g5
+	ld	[%g5], %g5
+	call	%g5
+	 nop
+
+	/* Start this processor. */
+	call	smp4m_callin
+	 nop
+
+	b,a	smp_do_cpu_idle
+
+	.text
+	.align	4
+
+smp_do_cpu_idle:
+	call	cpu_idle
+	 mov	0, %o0
+
+	call	cpu_panic
+	 nop
+
+__smp4m_processor_id:
+	rd	%tbr, %g2
+	srl	%g2, 12, %g2
+	and	%g2, 3, %g2
+	retl
+	 mov	%g1, %o7
+
+__smp4d_processor_id:
+	lda	[%g0] ASI_M_VIKING_TMP1, %g2
+	retl
+	 mov	%g1, %o7
+
+/* CPUID in bootbus can be found at PA 0xff0140000 */
+#define SUN4D_BOOTBUS_CPUID	0xf0140000
+
+	__INIT
+	.align	4
+
+sun4d_cpu_startup:
+	/* Set up a sane %psr -- PIL<0xf> S<0x1> PS<0x1> CWP<0x0> */
+	set	(PSR_PIL | PSR_S | PSR_PS), %g1
+	wr	%g1, 0x0, %psr		! traps off though
+	WRITE_PAUSE
+
+	/* Our %wim is one behind CWP */
+	mov	2, %g1
+	wr	%g1, 0x0, %wim
+	WRITE_PAUSE
+
+	/* Set tbr - we use just one trap table. */
+	set	trapbase, %g1
+	wr	%g1, 0x0, %tbr
+	WRITE_PAUSE
+
+	/* Get our CPU id out of bootbus */
+	set	SUN4D_BOOTBUS_CPUID, %g3
+	lduba	[%g3] ASI_M_CTL, %g3
+	and	%g3, 0xf8, %g3
+	srl	%g3, 3, %g1
+	sta	%g1, [%g0] ASI_M_VIKING_TMP1
+
+	/* Give ourselves a stack and curptr. */
+	set	current_set, %g5
+	srl	%g3, 1, %g4
+	ld	[%g5 + %g4], %g6
+
+	sethi	%hi(THREAD_SIZE - STACKFRAME_SZ), %sp
+	or	%sp, %lo(THREAD_SIZE - STACKFRAME_SZ), %sp
+	add	%g6, %sp, %sp
+
+	/* Turn on traps (PSR_ET). */
+	rd	%psr, %g1
+	wr	%g1, PSR_ET, %psr	! traps on
+	WRITE_PAUSE
+
+	/* Init our caches, etc. */
+	set	poke_srmmu, %g5
+	ld	[%g5], %g5
+	call	%g5
+	 nop
+
+	/* Start this processor. */
+	call	smp4d_callin
+	 nop
+
+	b,a	smp_do_cpu_idle
diff --git a/arch/sparc/kernel/traps.c b/arch/sparc/kernel/traps.c
new file mode 100644
index 0000000..3f451ae
--- /dev/null
+++ b/arch/sparc/kernel/traps.c
@@ -0,0 +1,515 @@
+/* $Id: traps.c,v 1.64 2000/09/03 15:00:49 anton Exp $
+ * arch/sparc/kernel/traps.c
+ *
+ * Copyright 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright 2000 Jakub Jelinek (jakub@redhat.com)
+ */
+
+/*
+ * I hate traps on the sparc, grrr...
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>  /* for jiffies */
+#include <linux/kernel.h>
+#include <linux/kallsyms.h>
+#include <linux/signal.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+#include <asm/delay.h>
+#include <asm/system.h>
+#include <asm/ptrace.h>
+#include <asm/oplib.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/kdebug.h>
+#include <asm/unistd.h>
+#include <asm/traps.h>
+
+/* #define TRAP_DEBUG */
+
+struct trap_trace_entry {
+	unsigned long pc;
+	unsigned long type;
+};
+
+int trap_curbuf = 0;
+struct trap_trace_entry trapbuf[1024];
+
+void syscall_trace_entry(struct pt_regs *regs)
+{
+	printk("%s[%d]: ", current->comm, current->pid);
+	printk("scall<%d> (could be %d)\n", (int) regs->u_regs[UREG_G1],
+	       (int) regs->u_regs[UREG_I0]);
+}
+
+void syscall_trace_exit(struct pt_regs *regs)
+{
+}
+
+void sun4m_nmi(struct pt_regs *regs)
+{
+	unsigned long afsr, afar;
+
+	printk("Aieee: sun4m NMI received!\n");
+	/* XXX HyperSparc hack XXX */
+	__asm__ __volatile__("mov 0x500, %%g1\n\t"
+			     "lda [%%g1] 0x4, %0\n\t"
+			     "mov 0x600, %%g1\n\t"
+			     "lda [%%g1] 0x4, %1\n\t" :
+			     "=r" (afsr), "=r" (afar));
+	printk("afsr=%08lx afar=%08lx\n", afsr, afar);
+	printk("you lose buddy boy...\n");
+	show_regs(regs);
+	prom_halt();
+}
+
+void sun4d_nmi(struct pt_regs *regs)
+{
+	printk("Aieee: sun4d NMI received!\n");
+	printk("you lose buddy boy...\n");
+	show_regs(regs);
+	prom_halt();
+}
+
+void instruction_dump (unsigned long *pc)
+{
+	int i;
+	
+	if((((unsigned long) pc) & 3))
+                return;
+
+	for(i = -3; i < 6; i++)
+		printk("%c%08lx%c",i?' ':'<',pc[i],i?' ':'>');
+	printk("\n");
+}
+
+#define __SAVE __asm__ __volatile__("save %sp, -0x40, %sp\n\t")
+#define __RESTORE __asm__ __volatile__("restore %g0, %g0, %g0\n\t")
+
+void die_if_kernel(char *str, struct pt_regs *regs)
+{
+	static int die_counter;
+	int count = 0;
+
+	/* Amuse the user. */
+	printk(
+"              \\|/ ____ \\|/\n"
+"              \"@'/ ,. \\`@\"\n"
+"              /_| \\__/ |_\\\n"
+"                 \\__U_/\n");
+
+	printk("%s(%d): %s [#%d]\n", current->comm, current->pid, str, ++die_counter);
+	show_regs(regs);
+
+	__SAVE; __SAVE; __SAVE; __SAVE;
+	__SAVE; __SAVE; __SAVE; __SAVE;
+	__RESTORE; __RESTORE; __RESTORE; __RESTORE;
+	__RESTORE; __RESTORE; __RESTORE; __RESTORE;
+
+	{
+		struct reg_window *rw = (struct reg_window *)regs->u_regs[UREG_FP];
+
+		/* Stop the back trace when we hit userland or we
+		 * find some badly aligned kernel stack. Set an upper
+		 * bound in case our stack is trashed and we loop.
+		 */
+		while(rw					&&
+		      count++ < 30				&&
+                      (((unsigned long) rw) >= PAGE_OFFSET)	&&
+		      !(((unsigned long) rw) & 0x7)) {
+			printk("Caller[%08lx]", rw->ins[7]);
+			print_symbol(": %s\n", rw->ins[7]);
+			rw = (struct reg_window *)rw->ins[6];
+		}
+	}
+	printk("Instruction DUMP:");
+	instruction_dump ((unsigned long *) regs->pc);
+	if(regs->psr & PSR_PS)
+		do_exit(SIGKILL);
+	do_exit(SIGSEGV);
+}
+
+void do_hw_interrupt(struct pt_regs *regs, unsigned long type)
+{
+	siginfo_t info;
+
+	if(type < 0x80) {
+		/* Sun OS's puke from bad traps, Linux survives! */
+		printk("Unimplemented Sparc TRAP, type = %02lx\n", type);
+		die_if_kernel("Whee... Hello Mr. Penguin", regs);
+	}	
+
+	if(regs->psr & PSR_PS)
+		die_if_kernel("Kernel bad trap", regs);
+
+	info.si_signo = SIGILL;
+	info.si_errno = 0;
+	info.si_code = ILL_ILLTRP;
+	info.si_addr = (void __user *)regs->pc;
+	info.si_trapno = type - 0x80;
+	force_sig_info(SIGILL, &info, current);
+}
+
+void do_illegal_instruction(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+			    unsigned long psr)
+{
+	extern int do_user_muldiv (struct pt_regs *, unsigned long);
+	siginfo_t info;
+
+	if(psr & PSR_PS)
+		die_if_kernel("Kernel illegal instruction", regs);
+#ifdef TRAP_DEBUG
+	printk("Ill instr. at pc=%08lx instruction is %08lx\n",
+	       regs->pc, *(unsigned long *)regs->pc);
+#endif
+	if (!do_user_muldiv (regs, pc))
+		return;
+
+	info.si_signo = SIGILL;
+	info.si_errno = 0;
+	info.si_code = ILL_ILLOPC;
+	info.si_addr = (void __user *)pc;
+	info.si_trapno = 0;
+	send_sig_info(SIGILL, &info, current);
+}
+
+void do_priv_instruction(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+			 unsigned long psr)
+{
+	siginfo_t info;
+
+	if(psr & PSR_PS)
+		die_if_kernel("Penguin instruction from Penguin mode??!?!", regs);
+	info.si_signo = SIGILL;
+	info.si_errno = 0;
+	info.si_code = ILL_PRVOPC;
+	info.si_addr = (void __user *)pc;
+	info.si_trapno = 0;
+	send_sig_info(SIGILL, &info, current);
+}
+
+/* XXX User may want to be allowed to do this. XXX */
+
+void do_memaccess_unaligned(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+			    unsigned long psr)
+{
+	siginfo_t info;
+
+	if(regs->psr & PSR_PS) {
+		printk("KERNEL MNA at pc %08lx npc %08lx called by %08lx\n", pc, npc,
+		       regs->u_regs[UREG_RETPC]);
+		die_if_kernel("BOGUS", regs);
+		/* die_if_kernel("Kernel MNA access", regs); */
+	}
+#if 0
+	show_regs (regs);
+	instruction_dump ((unsigned long *) regs->pc);
+	printk ("do_MNA!\n");
+#endif
+	info.si_signo = SIGBUS;
+	info.si_errno = 0;
+	info.si_code = BUS_ADRALN;
+	info.si_addr = /* FIXME: Should dig out mna address */ (void *)0;
+	info.si_trapno = 0;
+	send_sig_info(SIGBUS, &info, current);
+}
+
+extern void fpsave(unsigned long *fpregs, unsigned long *fsr,
+		   void *fpqueue, unsigned long *fpqdepth);
+extern void fpload(unsigned long *fpregs, unsigned long *fsr);
+
+static unsigned long init_fsr = 0x0UL;
+static unsigned long init_fregs[32] __attribute__ ((aligned (8))) =
+                { ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL,
+		  ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL,
+		  ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL,
+		  ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL };
+
+void do_fpd_trap(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+		 unsigned long psr)
+{
+	/* Sanity check... */
+	if(psr & PSR_PS)
+		die_if_kernel("Kernel gets FloatingPenguinUnit disabled trap", regs);
+
+	put_psr(get_psr() | PSR_EF);    /* Allow FPU ops. */
+	regs->psr |= PSR_EF;
+#ifndef CONFIG_SMP
+	if(last_task_used_math == current)
+		return;
+	if(last_task_used_math) {
+		/* Other processes fpu state, save away */
+		struct task_struct *fptask = last_task_used_math;
+		fpsave(&fptask->thread.float_regs[0], &fptask->thread.fsr,
+		       &fptask->thread.fpqueue[0], &fptask->thread.fpqdepth);
+	}
+	last_task_used_math = current;
+	if(used_math()) {
+		fpload(&current->thread.float_regs[0], &current->thread.fsr);
+	} else {
+		/* Set initial sane state. */
+		fpload(&init_fregs[0], &init_fsr);
+		set_used_math();
+	}
+#else
+	if(!used_math()) {
+		fpload(&init_fregs[0], &init_fsr);
+		set_used_math();
+	} else {
+		fpload(&current->thread.float_regs[0], &current->thread.fsr);
+	}
+	current_thread_info()->flags |= _TIF_USEDFPU;
+#endif
+}
+
+static unsigned long fake_regs[32] __attribute__ ((aligned (8)));
+static unsigned long fake_fsr;
+static unsigned long fake_queue[32] __attribute__ ((aligned (8)));
+static unsigned long fake_depth;
+
+extern int do_mathemu(struct pt_regs *, struct task_struct *);
+
+void do_fpe_trap(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+		 unsigned long psr)
+{
+	static int calls;
+	siginfo_t info;
+	unsigned long fsr;
+	int ret = 0;
+#ifndef CONFIG_SMP
+	struct task_struct *fpt = last_task_used_math;
+#else
+	struct task_struct *fpt = current;
+#endif
+	put_psr(get_psr() | PSR_EF);
+	/* If nobody owns the fpu right now, just clear the
+	 * error into our fake static buffer and hope it don't
+	 * happen again.  Thank you crashme...
+	 */
+#ifndef CONFIG_SMP
+	if(!fpt) {
+#else
+        if(!(fpt->thread_info->flags & _TIF_USEDFPU)) {
+#endif
+		fpsave(&fake_regs[0], &fake_fsr, &fake_queue[0], &fake_depth);
+		regs->psr &= ~PSR_EF;
+		return;
+	}
+	fpsave(&fpt->thread.float_regs[0], &fpt->thread.fsr,
+	       &fpt->thread.fpqueue[0], &fpt->thread.fpqdepth);
+#ifdef DEBUG_FPU
+	printk("Hmm, FP exception, fsr was %016lx\n", fpt->thread.fsr);
+#endif
+
+	switch ((fpt->thread.fsr & 0x1c000)) {
+	/* switch on the contents of the ftt [floating point trap type] field */
+#ifdef DEBUG_FPU
+	case (1 << 14):
+		printk("IEEE_754_exception\n");
+		break;
+#endif
+	case (2 << 14):  /* unfinished_FPop (underflow & co) */
+	case (3 << 14):  /* unimplemented_FPop (quad stuff, maybe sqrt) */
+		ret = do_mathemu(regs, fpt);
+		break;
+#ifdef DEBUG_FPU
+	case (4 << 14):
+		printk("sequence_error (OS bug...)\n");
+		break;
+	case (5 << 14):
+		printk("hardware_error (uhoh!)\n");
+		break;
+	case (6 << 14):
+		printk("invalid_fp_register (user error)\n");
+		break;
+#endif /* DEBUG_FPU */
+	}
+	/* If we successfully emulated the FPop, we pretend the trap never happened :-> */
+	if (ret) {
+		fpload(&current->thread.float_regs[0], &current->thread.fsr);
+		return;
+	}
+	/* nope, better SIGFPE the offending process... */
+	       
+#ifdef CONFIG_SMP
+	fpt->thread_info->flags &= ~_TIF_USEDFPU;
+#endif
+	if(psr & PSR_PS) {
+		/* The first fsr store/load we tried trapped,
+		 * the second one will not (we hope).
+		 */
+		printk("WARNING: FPU exception from kernel mode. at pc=%08lx\n",
+		       regs->pc);
+		regs->pc = regs->npc;
+		regs->npc += 4;
+		calls++;
+		if(calls > 2)
+			die_if_kernel("Too many Penguin-FPU traps from kernel mode",
+				      regs);
+		return;
+	}
+
+	fsr = fpt->thread.fsr;
+	info.si_signo = SIGFPE;
+	info.si_errno = 0;
+	info.si_addr = (void __user *)pc;
+	info.si_trapno = 0;
+	info.si_code = __SI_FAULT;
+	if ((fsr & 0x1c000) == (1 << 14)) {
+		if (fsr & 0x10)
+			info.si_code = FPE_FLTINV;
+		else if (fsr & 0x08)
+			info.si_code = FPE_FLTOVF;
+		else if (fsr & 0x04)
+			info.si_code = FPE_FLTUND;
+		else if (fsr & 0x02)
+			info.si_code = FPE_FLTDIV;
+		else if (fsr & 0x01)
+			info.si_code = FPE_FLTRES;
+	}
+	send_sig_info(SIGFPE, &info, fpt);
+#ifndef CONFIG_SMP
+	last_task_used_math = NULL;
+#endif
+	regs->psr &= ~PSR_EF;
+	if(calls > 0)
+		calls=0;
+}
+
+void handle_tag_overflow(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+			 unsigned long psr)
+{
+	siginfo_t info;
+
+	if(psr & PSR_PS)
+		die_if_kernel("Penguin overflow trap from kernel mode", regs);
+	info.si_signo = SIGEMT;
+	info.si_errno = 0;
+	info.si_code = EMT_TAGOVF;
+	info.si_addr = (void __user *)pc;
+	info.si_trapno = 0;
+	send_sig_info(SIGEMT, &info, current);
+}
+
+void handle_watchpoint(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+		       unsigned long psr)
+{
+#ifdef TRAP_DEBUG
+	printk("Watchpoint detected at PC %08lx NPC %08lx PSR %08lx\n",
+	       pc, npc, psr);
+#endif
+	if(psr & PSR_PS)
+		panic("Tell me what a watchpoint trap is, and I'll then deal "
+		      "with such a beast...");
+}
+
+void handle_reg_access(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+		       unsigned long psr)
+{
+	siginfo_t info;
+
+#ifdef TRAP_DEBUG
+	printk("Register Access Exception at PC %08lx NPC %08lx PSR %08lx\n",
+	       pc, npc, psr);
+#endif
+	info.si_signo = SIGBUS;
+	info.si_errno = 0;
+	info.si_code = BUS_OBJERR;
+	info.si_addr = (void __user *)pc;
+	info.si_trapno = 0;
+	force_sig_info(SIGBUS, &info, current);
+}
+
+void handle_cp_disabled(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+			unsigned long psr)
+{
+	siginfo_t info;
+
+	info.si_signo = SIGILL;
+	info.si_errno = 0;
+	info.si_code = ILL_COPROC;
+	info.si_addr = (void __user *)pc;
+	info.si_trapno = 0;
+	send_sig_info(SIGILL, &info, current);
+}
+
+void handle_cp_exception(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+			 unsigned long psr)
+{
+	siginfo_t info;
+
+#ifdef TRAP_DEBUG
+	printk("Co-Processor Exception at PC %08lx NPC %08lx PSR %08lx\n",
+	       pc, npc, psr);
+#endif
+	info.si_signo = SIGILL;
+	info.si_errno = 0;
+	info.si_code = ILL_COPROC;
+	info.si_addr = (void __user *)pc;
+	info.si_trapno = 0;
+	send_sig_info(SIGILL, &info, current);
+}
+
+void handle_hw_divzero(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+		       unsigned long psr)
+{
+	siginfo_t info;
+
+	info.si_signo = SIGFPE;
+	info.si_errno = 0;
+	info.si_code = FPE_INTDIV;
+	info.si_addr = (void __user *)pc;
+	info.si_trapno = 0;
+	send_sig_info(SIGFPE, &info, current);
+}
+
+#ifdef CONFIG_DEBUG_BUGVERBOSE
+void do_BUG(const char *file, int line)
+{
+        // bust_spinlocks(1);   XXX Not in our original BUG()
+        printk("kernel BUG at %s:%d!\n", file, line);
+}
+#endif
+
+/* Since we have our mappings set up, on multiprocessors we can spin them
+ * up here so that timer interrupts work during initialization.
+ */
+
+extern void sparc_cpu_startup(void);
+
+int linux_smp_still_initting;
+unsigned int thiscpus_tbr;
+int thiscpus_mid;
+
+void trap_init(void)
+{
+	extern void thread_info_offsets_are_bolixed_pete(void);
+
+	/* Force linker to barf if mismatched */
+	if (TI_UWINMASK    != offsetof(struct thread_info, uwinmask) ||
+	    TI_TASK        != offsetof(struct thread_info, task) ||
+	    TI_EXECDOMAIN  != offsetof(struct thread_info, exec_domain) ||
+	    TI_FLAGS       != offsetof(struct thread_info, flags) ||
+	    TI_CPU         != offsetof(struct thread_info, cpu) ||
+	    TI_PREEMPT     != offsetof(struct thread_info, preempt_count) ||
+	    TI_SOFTIRQ     != offsetof(struct thread_info, softirq_count) ||
+	    TI_HARDIRQ     != offsetof(struct thread_info, hardirq_count) ||
+	    TI_KSP         != offsetof(struct thread_info, ksp) ||
+	    TI_KPC         != offsetof(struct thread_info, kpc) ||
+	    TI_KPSR        != offsetof(struct thread_info, kpsr) ||
+	    TI_KWIM        != offsetof(struct thread_info, kwim) ||
+	    TI_REG_WINDOW  != offsetof(struct thread_info, reg_window) ||
+	    TI_RWIN_SPTRS  != offsetof(struct thread_info, rwbuf_stkptrs) ||
+	    TI_W_SAVED     != offsetof(struct thread_info, w_saved))
+		thread_info_offsets_are_bolixed_pete();
+
+	/* Attach to the address space of init_task. */
+	atomic_inc(&init_mm.mm_count);
+	current->active_mm = &init_mm;
+
+	/* NOTE: Other cpus have this done as they are started
+	 *       up on SMP.
+	 */
+}
diff --git a/arch/sparc/kernel/unaligned.c b/arch/sparc/kernel/unaligned.c
new file mode 100644
index 0000000..a6330fb
--- /dev/null
+++ b/arch/sparc/kernel/unaligned.c
@@ -0,0 +1,548 @@
+/* $Id: unaligned.c,v 1.23 2001/12/21 00:54:31 davem Exp $
+ * unaligned.c: Unaligned load/store trap handling with special
+ *              cases for the kernel to do them more quickly.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+/* #define DEBUG_MNA */
+
+enum direction {
+	load,    /* ld, ldd, ldh, ldsh */
+	store,   /* st, std, sth, stsh */
+	both,    /* Swap, ldstub, etc. */
+	fpload,
+	fpstore,
+	invalid,
+};
+
+#ifdef DEBUG_MNA
+static char *dirstrings[] = {
+  "load", "store", "both", "fpload", "fpstore", "invalid"
+};
+#endif
+
+static inline enum direction decode_direction(unsigned int insn)
+{
+	unsigned long tmp = (insn >> 21) & 1;
+
+	if(!tmp)
+		return load;
+	else {
+		if(((insn>>19)&0x3f) == 15)
+			return both;
+		else
+			return store;
+	}
+}
+
+/* 8 = double-word, 4 = word, 2 = half-word */
+static inline int decode_access_size(unsigned int insn)
+{
+	insn = (insn >> 19) & 3;
+
+	if(!insn)
+		return 4;
+	else if(insn == 3)
+		return 8;
+	else if(insn == 2)
+		return 2;
+	else {
+		printk("Impossible unaligned trap. insn=%08x\n", insn);
+		die_if_kernel("Byte sized unaligned access?!?!", current->thread.kregs);
+		return 4; /* just to keep gcc happy. */
+	}
+}
+
+/* 0x400000 = signed, 0 = unsigned */
+static inline int decode_signedness(unsigned int insn)
+{
+	return (insn & 0x400000);
+}
+
+static inline void maybe_flush_windows(unsigned int rs1, unsigned int rs2,
+				       unsigned int rd)
+{
+	if(rs2 >= 16 || rs1 >= 16 || rd >= 16) {
+		/* Wheee... */
+		__asm__ __volatile__("save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "restore; restore; restore; restore;\n\t"
+				     "restore; restore; restore;\n\t");
+	}
+}
+
+static inline int sign_extend_imm13(int imm)
+{
+	return imm << 19 >> 19;
+}
+
+static inline unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs)
+{
+	struct reg_window *win;
+
+	if(reg < 16)
+		return (!reg ? 0 : regs->u_regs[reg]);
+
+	/* Ho hum, the slightly complicated case. */
+	win = (struct reg_window *) regs->u_regs[UREG_FP];
+	return win->locals[reg - 16]; /* yes, I know what this does... */
+}
+
+static inline unsigned long safe_fetch_reg(unsigned int reg, struct pt_regs *regs)
+{
+	struct reg_window __user *win;
+	unsigned long ret;
+
+	if (reg < 16)
+		return (!reg ? 0 : regs->u_regs[reg]);
+
+	/* Ho hum, the slightly complicated case. */
+	win = (struct reg_window __user *) regs->u_regs[UREG_FP];
+
+	if ((unsigned long)win & 3)
+		return -1;
+
+	if (get_user(ret, &win->locals[reg - 16]))
+		return -1;
+
+	return ret;
+}
+
+static inline unsigned long *fetch_reg_addr(unsigned int reg, struct pt_regs *regs)
+{
+	struct reg_window *win;
+
+	if(reg < 16)
+		return &regs->u_regs[reg];
+	win = (struct reg_window *) regs->u_regs[UREG_FP];
+	return &win->locals[reg - 16];
+}
+
+static unsigned long compute_effective_address(struct pt_regs *regs,
+					       unsigned int insn)
+{
+	unsigned int rs1 = (insn >> 14) & 0x1f;
+	unsigned int rs2 = insn & 0x1f;
+	unsigned int rd = (insn >> 25) & 0x1f;
+
+	if(insn & 0x2000) {
+		maybe_flush_windows(rs1, 0, rd);
+		return (fetch_reg(rs1, regs) + sign_extend_imm13(insn));
+	} else {
+		maybe_flush_windows(rs1, rs2, rd);
+		return (fetch_reg(rs1, regs) + fetch_reg(rs2, regs));
+	}
+}
+
+unsigned long safe_compute_effective_address(struct pt_regs *regs,
+					     unsigned int insn)
+{
+	unsigned int rs1 = (insn >> 14) & 0x1f;
+	unsigned int rs2 = insn & 0x1f;
+	unsigned int rd = (insn >> 25) & 0x1f;
+
+	if(insn & 0x2000) {
+		maybe_flush_windows(rs1, 0, rd);
+		return (safe_fetch_reg(rs1, regs) + sign_extend_imm13(insn));
+	} else {
+		maybe_flush_windows(rs1, rs2, rd);
+		return (safe_fetch_reg(rs1, regs) + safe_fetch_reg(rs2, regs));
+	}
+}
+
+/* This is just to make gcc think panic does return... */
+static void unaligned_panic(char *str)
+{
+	panic(str);
+}
+
+#define do_integer_load(dest_reg, size, saddr, is_signed, errh) ({		\
+__asm__ __volatile__ (								\
+	"cmp	%1, 8\n\t"							\
+	"be	9f\n\t"								\
+	" cmp	%1, 4\n\t"							\
+	"be	6f\n"								\
+"4:\t"	" ldub	[%2], %%l1\n"							\
+"5:\t"	"ldub	[%2 + 1], %%l2\n\t"						\
+	"sll	%%l1, 8, %%l1\n\t"						\
+	"tst	%3\n\t"								\
+	"be	3f\n\t"								\
+	" add	%%l1, %%l2, %%l1\n\t"						\
+	"sll	%%l1, 16, %%l1\n\t"						\
+	"sra	%%l1, 16, %%l1\n"						\
+"3:\t"	"b	0f\n\t"								\
+	" st	%%l1, [%0]\n"							\
+"6:\t"	"ldub	[%2 + 1], %%l2\n\t"						\
+	"sll	%%l1, 24, %%l1\n"						\
+"7:\t"	"ldub	[%2 + 2], %%g7\n\t"						\
+	"sll	%%l2, 16, %%l2\n"						\
+"8:\t"	"ldub	[%2 + 3], %%g1\n\t"						\
+	"sll	%%g7, 8, %%g7\n\t"						\
+	"or	%%l1, %%l2, %%l1\n\t"						\
+	"or	%%g7, %%g1, %%g7\n\t"						\
+	"or	%%l1, %%g7, %%l1\n\t"						\
+	"b	0f\n\t"								\
+	" st	%%l1, [%0]\n"							\
+"9:\t"	"ldub	[%2], %%l1\n"							\
+"10:\t"	"ldub	[%2 + 1], %%l2\n\t"						\
+	"sll	%%l1, 24, %%l1\n"						\
+"11:\t"	"ldub	[%2 + 2], %%g7\n\t"						\
+	"sll	%%l2, 16, %%l2\n"						\
+"12:\t"	"ldub	[%2 + 3], %%g1\n\t"						\
+	"sll	%%g7, 8, %%g7\n\t"						\
+	"or	%%l1, %%l2, %%l1\n\t"						\
+	"or	%%g7, %%g1, %%g7\n\t"						\
+	"or	%%l1, %%g7, %%g7\n"						\
+"13:\t"	"ldub	[%2 + 4], %%l1\n\t"						\
+	"st	%%g7, [%0]\n"							\
+"14:\t"	"ldub	[%2 + 5], %%l2\n\t"						\
+	"sll	%%l1, 24, %%l1\n"						\
+"15:\t"	"ldub	[%2 + 6], %%g7\n\t"						\
+	"sll	%%l2, 16, %%l2\n"						\
+"16:\t"	"ldub	[%2 + 7], %%g1\n\t"						\
+	"sll	%%g7, 8, %%g7\n\t"						\
+	"or	%%l1, %%l2, %%l1\n\t"						\
+	"or	%%g7, %%g1, %%g7\n\t"						\
+	"or	%%l1, %%g7, %%g7\n\t"						\
+	"st	%%g7, [%0 + 4]\n"						\
+"0:\n\n\t"									\
+	".section __ex_table,#alloc\n\t"					\
+	".word	4b, " #errh "\n\t"						\
+	".word	5b, " #errh "\n\t"						\
+	".word	6b, " #errh "\n\t"						\
+	".word	7b, " #errh "\n\t"						\
+	".word	8b, " #errh "\n\t"						\
+	".word	9b, " #errh "\n\t"						\
+	".word	10b, " #errh "\n\t"						\
+	".word	11b, " #errh "\n\t"						\
+	".word	12b, " #errh "\n\t"						\
+	".word	13b, " #errh "\n\t"						\
+	".word	14b, " #errh "\n\t"						\
+	".word	15b, " #errh "\n\t"						\
+	".word	16b, " #errh "\n\n\t"						\
+	".previous\n\t"								\
+	: : "r" (dest_reg), "r" (size), "r" (saddr), "r" (is_signed)		\
+	: "l1", "l2", "g7", "g1", "cc");					\
+})
+	
+#define store_common(dst_addr, size, src_val, errh) ({				\
+__asm__ __volatile__ (								\
+	"ld	[%2], %%l1\n"							\
+	"cmp	%1, 2\n\t"							\
+	"be	2f\n\t"								\
+	" cmp	%1, 4\n\t"							\
+	"be	1f\n\t"								\
+	" srl	%%l1, 24, %%l2\n\t"						\
+	"srl	%%l1, 16, %%g7\n"						\
+"4:\t"	"stb	%%l2, [%0]\n\t"							\
+	"srl	%%l1, 8, %%l2\n"						\
+"5:\t"	"stb	%%g7, [%0 + 1]\n\t"						\
+	"ld	[%2 + 4], %%g7\n"						\
+"6:\t"	"stb	%%l2, [%0 + 2]\n\t"						\
+	"srl	%%g7, 24, %%l2\n"						\
+"7:\t"	"stb	%%l1, [%0 + 3]\n\t"						\
+	"srl	%%g7, 16, %%l1\n"						\
+"8:\t"	"stb	%%l2, [%0 + 4]\n\t"						\
+	"srl	%%g7, 8, %%l2\n"						\
+"9:\t"	"stb	%%l1, [%0 + 5]\n"						\
+"10:\t"	"stb	%%l2, [%0 + 6]\n\t"						\
+	"b	0f\n"								\
+"11:\t"	" stb	%%g7, [%0 + 7]\n"						\
+"1:\t"	"srl	%%l1, 16, %%g7\n"						\
+"12:\t"	"stb	%%l2, [%0]\n\t"							\
+	"srl	%%l1, 8, %%l2\n"						\
+"13:\t"	"stb	%%g7, [%0 + 1]\n"						\
+"14:\t"	"stb	%%l2, [%0 + 2]\n\t"						\
+	"b	0f\n"								\
+"15:\t"	" stb	%%l1, [%0 + 3]\n"						\
+"2:\t"	"srl	%%l1, 8, %%l2\n"						\
+"16:\t"	"stb	%%l2, [%0]\n"							\
+"17:\t"	"stb	%%l1, [%0 + 1]\n"						\
+"0:\n\n\t"									\
+	".section __ex_table,#alloc\n\t"					\
+	".word	4b, " #errh "\n\t"						\
+	".word	5b, " #errh "\n\t"						\
+	".word	6b, " #errh "\n\t"						\
+	".word	7b, " #errh "\n\t"						\
+	".word	8b, " #errh "\n\t"						\
+	".word	9b, " #errh "\n\t"						\
+	".word	10b, " #errh "\n\t"						\
+	".word	11b, " #errh "\n\t"						\
+	".word	12b, " #errh "\n\t"						\
+	".word	13b, " #errh "\n\t"						\
+	".word	14b, " #errh "\n\t"						\
+	".word	15b, " #errh "\n\t"						\
+	".word	16b, " #errh "\n\t"						\
+	".word	17b, " #errh "\n\n\t"						\
+	".previous\n\t"								\
+	: : "r" (dst_addr), "r" (size), "r" (src_val)				\
+	: "l1", "l2", "g7", "g1", "cc");					\
+})
+
+#define do_integer_store(reg_num, size, dst_addr, regs, errh) ({		\
+	unsigned long *src_val;							\
+	static unsigned long zero[2] = { 0, };					\
+										\
+	if (reg_num) src_val = fetch_reg_addr(reg_num, regs);			\
+	else {									\
+		src_val = &zero[0];						\
+		if (size == 8)							\
+			zero[1] = fetch_reg(1, regs);				\
+	}									\
+	store_common(dst_addr, size, src_val, errh);				\
+})
+
+extern void smp_capture(void);
+extern void smp_release(void);
+
+#define do_atomic(srcdest_reg, mem, errh) ({					\
+	unsigned long flags, tmp;						\
+										\
+	smp_capture();								\
+	local_irq_save(flags);							\
+	tmp = *srcdest_reg;							\
+	do_integer_load(srcdest_reg, 4, mem, 0, errh);				\
+	store_common(mem, 4, &tmp, errh);					\
+	local_irq_restore(flags);						\
+	smp_release();								\
+})
+
+static inline void advance(struct pt_regs *regs)
+{
+	regs->pc   = regs->npc;
+	regs->npc += 4;
+}
+
+static inline int floating_point_load_or_store_p(unsigned int insn)
+{
+	return (insn >> 24) & 1;
+}
+
+static inline int ok_for_kernel(unsigned int insn)
+{
+	return !floating_point_load_or_store_p(insn);
+}
+
+void kernel_mna_trap_fault(struct pt_regs *regs, unsigned int insn) __asm__ ("kernel_mna_trap_fault");
+
+void kernel_mna_trap_fault(struct pt_regs *regs, unsigned int insn)
+{
+	unsigned long g2 = regs->u_regs [UREG_G2];
+	unsigned long fixup = search_extables_range(regs->pc, &g2);
+
+	if (!fixup) {
+		unsigned long address = compute_effective_address(regs, insn);
+        	if(address < PAGE_SIZE) {
+                	printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference in mna handler");
+        	} else
+                	printk(KERN_ALERT "Unable to handle kernel paging request in mna handler");
+	        printk(KERN_ALERT " at virtual address %08lx\n",address);
+		printk(KERN_ALERT "current->{mm,active_mm}->context = %08lx\n",
+			(current->mm ? current->mm->context :
+			current->active_mm->context));
+		printk(KERN_ALERT "current->{mm,active_mm}->pgd = %08lx\n",
+			(current->mm ? (unsigned long) current->mm->pgd :
+			(unsigned long) current->active_mm->pgd));
+	        die_if_kernel("Oops", regs);
+		/* Not reached */
+	}
+	regs->pc = fixup;
+	regs->npc = regs->pc + 4;
+	regs->u_regs [UREG_G2] = g2;
+}
+
+asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn)
+{
+	enum direction dir = decode_direction(insn);
+	int size = decode_access_size(insn);
+
+	if(!ok_for_kernel(insn) || dir == both) {
+		printk("Unsupported unaligned load/store trap for kernel at <%08lx>.\n",
+		       regs->pc);
+		unaligned_panic("Wheee. Kernel does fpu/atomic unaligned load/store.");
+
+		__asm__ __volatile__ ("\n"
+"kernel_unaligned_trap_fault:\n\t"
+		"mov	%0, %%o0\n\t"
+		"call	kernel_mna_trap_fault\n\t"
+		" mov	%1, %%o1\n\t"
+		:
+		: "r" (regs), "r" (insn)
+		: "o0", "o1", "o2", "o3", "o4", "o5", "o7",
+		  "g1", "g2", "g3", "g4", "g5", "g7", "cc");
+	} else {
+		unsigned long addr = compute_effective_address(regs, insn);
+
+#ifdef DEBUG_MNA
+		printk("KMNA: pc=%08lx [dir=%s addr=%08lx size=%d] retpc[%08lx]\n",
+		       regs->pc, dirstrings[dir], addr, size, regs->u_regs[UREG_RETPC]);
+#endif
+		switch(dir) {
+		case load:
+			do_integer_load(fetch_reg_addr(((insn>>25)&0x1f), regs),
+					size, (unsigned long *) addr,
+					decode_signedness(insn),
+					kernel_unaligned_trap_fault);
+			break;
+
+		case store:
+			do_integer_store(((insn>>25)&0x1f), size,
+					 (unsigned long *) addr, regs,
+					 kernel_unaligned_trap_fault);
+			break;
+#if 0 /* unsupported */
+		case both:
+			do_atomic(fetch_reg_addr(((insn>>25)&0x1f), regs),
+				  (unsigned long *) addr,
+				  kernel_unaligned_trap_fault);
+			break;
+#endif
+		default:
+			panic("Impossible kernel unaligned trap.");
+			/* Not reached... */
+		}
+		advance(regs);
+	}
+}
+
+static inline int ok_for_user(struct pt_regs *regs, unsigned int insn,
+			      enum direction dir)
+{
+	unsigned int reg;
+	int check = (dir == load) ? VERIFY_READ : VERIFY_WRITE;
+	int size = ((insn >> 19) & 3) == 3 ? 8 : 4;
+
+	if ((regs->pc | regs->npc) & 3)
+		return 0;
+
+	/* Must access_ok() in all the necessary places. */
+#define WINREG_ADDR(regnum) \
+	((void __user *)(((unsigned long *)regs->u_regs[UREG_FP])+(regnum)))
+
+	reg = (insn >> 25) & 0x1f;
+	if (reg >= 16) {
+		if (!access_ok(check, WINREG_ADDR(reg - 16), size))
+			return -EFAULT;
+	}
+	reg = (insn >> 14) & 0x1f;
+	if (reg >= 16) {
+		if (!access_ok(check, WINREG_ADDR(reg - 16), size))
+			return -EFAULT;
+	}
+	if (!(insn & 0x2000)) {
+		reg = (insn & 0x1f);
+		if (reg >= 16) {
+			if (!access_ok(check, WINREG_ADDR(reg - 16), size))
+				return -EFAULT;
+		}
+	}
+#undef WINREG_ADDR
+	return 0;
+}
+
+void user_mna_trap_fault(struct pt_regs *regs, unsigned int insn) __asm__ ("user_mna_trap_fault");
+
+void user_mna_trap_fault(struct pt_regs *regs, unsigned int insn)
+{
+	siginfo_t info;
+
+	info.si_signo = SIGBUS;
+	info.si_errno = 0;
+	info.si_code = BUS_ADRALN;
+	info.si_addr = (void __user *)safe_compute_effective_address(regs, insn);
+	info.si_trapno = 0;
+	send_sig_info(SIGBUS, &info, current);
+}
+
+asmlinkage void user_unaligned_trap(struct pt_regs *regs, unsigned int insn)
+{
+	enum direction dir;
+
+	lock_kernel();
+	if(!(current->thread.flags & SPARC_FLAG_UNALIGNED) ||
+	   (((insn >> 30) & 3) != 3))
+		goto kill_user;
+	dir = decode_direction(insn);
+	if(!ok_for_user(regs, insn, dir)) {
+		goto kill_user;
+	} else {
+		int size = decode_access_size(insn);
+		unsigned long addr;
+
+		if(floating_point_load_or_store_p(insn)) {
+			printk("User FPU load/store unaligned unsupported.\n");
+			goto kill_user;
+		}
+
+		addr = compute_effective_address(regs, insn);
+		switch(dir) {
+		case load:
+			do_integer_load(fetch_reg_addr(((insn>>25)&0x1f), regs),
+					size, (unsigned long *) addr,
+					decode_signedness(insn),
+					user_unaligned_trap_fault);
+			break;
+
+		case store:
+			do_integer_store(((insn>>25)&0x1f), size,
+					 (unsigned long *) addr, regs,
+					 user_unaligned_trap_fault);
+			break;
+
+		case both:
+#if 0 /* unsupported */
+			do_atomic(fetch_reg_addr(((insn>>25)&0x1f), regs),
+				  (unsigned long *) addr,
+				  user_unaligned_trap_fault);
+#else
+			/*
+			 * This was supported in 2.4. However, we question
+			 * the value of SWAP instruction across word boundaries.
+			 */
+			printk("Unaligned SWAP unsupported.\n");
+			goto kill_user;
+#endif
+			break;
+
+		default:
+			unaligned_panic("Impossible user unaligned trap.");
+
+			__asm__ __volatile__ ("\n"
+"user_unaligned_trap_fault:\n\t"
+			"mov	%0, %%o0\n\t"
+			"call	user_mna_trap_fault\n\t"
+			" mov	%1, %%o1\n\t"
+			:
+			: "r" (regs), "r" (insn)
+			: "o0", "o1", "o2", "o3", "o4", "o5", "o7",
+			  "g1", "g2", "g3", "g4", "g5", "g7", "cc");
+			goto out;
+		}
+		advance(regs);
+		goto out;
+	}
+
+kill_user:
+	user_mna_trap_fault(regs, insn);
+out:
+	unlock_kernel();
+}
diff --git a/arch/sparc/kernel/vmlinux.lds.S b/arch/sparc/kernel/vmlinux.lds.S
new file mode 100644
index 0000000..38938d2
--- /dev/null
+++ b/arch/sparc/kernel/vmlinux.lds.S
@@ -0,0 +1,103 @@
+/* ld script to make SparcLinux kernel */
+
+#include <asm-generic/vmlinux.lds.h>
+
+OUTPUT_FORMAT("elf32-sparc", "elf32-sparc", "elf32-sparc")
+OUTPUT_ARCH(sparc)
+ENTRY(_start)
+jiffies = jiffies_64 + 4;
+SECTIONS
+{
+  . = 0x10000 + SIZEOF_HEADERS;
+  .text 0xf0004000 :
+  {
+    *(.text)
+    SCHED_TEXT
+    LOCK_TEXT
+    *(.gnu.warning)
+  } =0
+  _etext = .;
+  PROVIDE (etext = .);
+  RODATA
+  .data    :
+  {
+    *(.data)
+    CONSTRUCTORS
+  }
+  .data1   : { *(.data1) }
+  _edata  =  .;
+  PROVIDE (edata = .);
+  __start___fixup = .;
+  .fixup   : { *(.fixup) }
+  __stop___fixup = .;
+  __start___ex_table = .;
+  __ex_table : { *(__ex_table) }
+  __stop___ex_table = .;
+
+  . = ALIGN(4096);
+  __init_begin = .;
+  .init.text : { 
+	_sinittext = .;
+	*(.init.text)
+	_einittext = .;
+  }
+  __init_text_end = .;
+  .init.data : { *(.init.data) }
+  . = ALIGN(16);
+  __setup_start = .;
+  .init.setup : { *(.init.setup) }
+  __setup_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 = .;
+  . = ALIGN(32);
+  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
+
+  __bss_start = .;
+  .sbss      : { *(.sbss) *(.scommon) }
+  .bss       :
+  {
+   *(.dynbss)
+   *(.bss)
+   *(COMMON)
+  }
+  _end = . ;
+  PROVIDE (end = .);
+  /* Stabs debugging sections.  */
+  .stab 0 : { *(.stab) }
+  .stabstr 0 : { *(.stabstr) }
+  .stab.excl 0 : { *(.stab.excl) }
+  .stab.exclstr 0 : { *(.stab.exclstr) }
+  .stab.index 0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment 0 : { *(.comment) }
+  .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) }
+  /DISCARD/ : { *(.exit.text) *(.exit.data) *(.exitcall.exit) }
+}
diff --git a/arch/sparc/kernel/windows.c b/arch/sparc/kernel/windows.c
new file mode 100644
index 0000000..9cc93ea
--- /dev/null
+++ b/arch/sparc/kernel/windows.c
@@ -0,0 +1,127 @@
+/* windows.c: Routines to deal with register window management
+ *            at the C-code level.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+#include <asm/uaccess.h>
+
+/* Do save's until all user register windows are out of the cpu. */
+void flush_user_windows(void)
+{
+	register int ctr asm("g5");
+
+	ctr = 0;
+	__asm__ __volatile__(
+		"\n1:\n\t"
+		"ld	[%%g6 + %2], %%g4\n\t"
+		"orcc	%%g0, %%g4, %%g0\n\t"
+		"add	%0, 1, %0\n\t"
+		"bne	1b\n\t"
+		" save	%%sp, -64, %%sp\n"
+		"2:\n\t"
+		"subcc	%0, 1, %0\n\t"
+		"bne	2b\n\t"
+		" restore %%g0, %%g0, %%g0\n"
+	: "=&r" (ctr)
+	: "0" (ctr),
+	  "i" ((const unsigned long)TI_UWINMASK)
+	: "g4", "cc");
+}
+
+static inline void shift_window_buffer(int first_win, int last_win, struct thread_info *tp)
+{
+	int i;
+
+	for(i = first_win; i < last_win; i++) {
+		tp->rwbuf_stkptrs[i] = tp->rwbuf_stkptrs[i+1];
+		memcpy(&tp->reg_window[i], &tp->reg_window[i+1], sizeof(struct reg_window));
+	}
+}
+
+/* Place as many of the user's current register windows 
+ * on the stack that we can.  Even if the %sp is unaligned
+ * we still copy the window there, the only case that we don't
+ * succeed is if the %sp points to a bum mapping altogether.
+ * setup_frame() and do_sigreturn() use this before shifting
+ * the user stack around.  Future instruction and hardware
+ * bug workaround routines will need this functionality as
+ * well.
+ */
+void synchronize_user_stack(void)
+{
+	struct thread_info *tp = current_thread_info();
+	int window;
+
+	flush_user_windows();
+	if(!tp->w_saved)
+		return;
+
+	/* Ok, there is some dirty work to do. */
+	for(window = tp->w_saved - 1; window >= 0; window--) {
+		unsigned long sp = tp->rwbuf_stkptrs[window];
+
+		/* Ok, let it rip. */
+		if (copy_to_user((char __user *) sp, &tp->reg_window[window],
+				 sizeof(struct reg_window)))
+			continue;
+
+		shift_window_buffer(window, tp->w_saved - 1, tp);
+		tp->w_saved--;
+	}
+}
+
+#if 0
+/* An optimization. */
+static inline void copy_aligned_window(void *dest, const void *src)
+{
+	__asm__ __volatile__("ldd [%1], %%g2\n\t"
+			     "ldd [%1 + 0x8], %%g4\n\t"
+			     "std %%g2, [%0]\n\t"
+			     "std %%g4, [%0 + 0x8]\n\t"
+			     "ldd [%1 + 0x10], %%g2\n\t"
+			     "ldd [%1 + 0x18], %%g4\n\t"
+			     "std %%g2, [%0 + 0x10]\n\t"
+			     "std %%g4, [%0 + 0x18]\n\t"
+			     "ldd [%1 + 0x20], %%g2\n\t"
+			     "ldd [%1 + 0x28], %%g4\n\t"
+			     "std %%g2, [%0 + 0x20]\n\t"
+			     "std %%g4, [%0 + 0x28]\n\t"
+			     "ldd [%1 + 0x30], %%g2\n\t"
+			     "ldd [%1 + 0x38], %%g4\n\t"
+			     "std %%g2, [%0 + 0x30]\n\t"
+			     "std %%g4, [%0 + 0x38]\n\t" : :
+			     "r" (dest), "r" (src) :
+			     "g2", "g3", "g4", "g5");
+}
+#endif
+
+/* Try to push the windows in a threads window buffer to the
+ * user stack.  Unaligned %sp's are not allowed here.
+ */
+
+void try_to_clear_window_buffer(struct pt_regs *regs, int who)
+{
+	struct thread_info *tp = current_thread_info();
+	int window;
+
+	lock_kernel();
+	flush_user_windows();
+	for(window = 0; window < tp->w_saved; window++) {
+		unsigned long sp = tp->rwbuf_stkptrs[window];
+
+		if ((sp & 7) ||
+		    copy_to_user((char __user *) sp, &tp->reg_window[window],
+				 sizeof(struct reg_window)))
+			do_exit(SIGILL);
+	}
+	tp->w_saved = 0;
+	unlock_kernel();
+}
diff --git a/arch/sparc/kernel/wof.S b/arch/sparc/kernel/wof.S
new file mode 100644
index 0000000..083b121
--- /dev/null
+++ b/arch/sparc/kernel/wof.S
@@ -0,0 +1,428 @@
+/* $Id: wof.S,v 1.40 2000/01/08 16:38:18 anton Exp $
+ * wof.S: Sparc window overflow handler.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/contregs.h>
+#include <asm/page.h>
+#include <asm/ptrace.h>
+#include <asm/psr.h>
+#include <asm/smp.h>
+#include <asm/asi.h>
+#include <asm/winmacro.h>
+#include <asm/asmmacro.h>
+#include <asm/thread_info.h>
+
+/* WARNING: This routine is hairy and _very_ complicated, but it
+ *          must be as fast as possible as it handles the allocation
+ *          of register windows to the user and kernel.  If you touch
+ *          this code be _very_ careful as many other pieces of the
+ *          kernel depend upon how this code behaves.  You have been
+ *          duly warned...
+ */
+
+/* We define macro's for registers which have a fixed
+ * meaning throughout this entire routine.  The 'T' in
+ * the comments mean that the register can only be
+ * accessed when in the 'trap' window, 'G' means
+ * accessible in any window.  Do not change these registers
+ * after they have been set, until you are ready to return
+ * from the trap.
+ */
+#define t_psr       l0 /* %psr at trap time                     T */
+#define t_pc        l1 /* PC for trap return                    T */
+#define t_npc       l2 /* NPC for trap return                   T */
+#define t_wim       l3 /* %wim at trap time                     T */
+#define saved_g5    l5 /* Global save register                  T */
+#define saved_g6    l6 /* Global save register                  T */
+#define curptr      g6 /* Gets set to 'current' then stays      G */
+
+/* Now registers whose values can change within the handler.      */
+#define twin_tmp    l4 /* Temp reg, only usable in trap window  T */
+#define glob_tmp    g5 /* Global temporary reg, usable anywhere G */
+
+	.text
+	.align	4
+	/* BEGINNING OF PATCH INSTRUCTIONS */
+	/* On a 7-window Sparc the boot code patches spnwin_*
+	 * instructions with the following ones.
+	 */
+	.globl	spnwin_patch1_7win, spnwin_patch2_7win, spnwin_patch3_7win
+spnwin_patch1_7win:	sll	%t_wim, 6, %glob_tmp
+spnwin_patch2_7win:	and	%glob_tmp, 0x7f, %glob_tmp
+spnwin_patch3_7win:	and	%twin_tmp, 0x7f, %twin_tmp
+	/* END OF PATCH INSTRUCTIONS */
+
+	/* The trap entry point has done the following:
+	 *
+	 * rd    %psr, %l0
+	 * rd    %wim, %l3
+	 * b     spill_window_entry
+	 * andcc %l0, PSR_PS, %g0
+	 */
+
+	/* Datum current_thread_info->uwinmask contains at all times a bitmask
+	 * where if any user windows are active, at least one bit will
+	 * be set in to mask.  If no user windows are active, the bitmask
+	 * will be all zeroes.
+	 */
+	.globl	spill_window_entry 
+	.globl	spnwin_patch1, spnwin_patch2, spnwin_patch3
+spill_window_entry:
+	/* LOCATION: Trap Window */
+
+	mov	%g5, %saved_g5		! save away global temp register
+	mov	%g6, %saved_g6		! save away 'current' ptr register
+
+	/* Compute what the new %wim will be if we save the
+	 * window properly in this trap handler.
+	 *
+	 * newwim = ((%wim>>1) | (%wim<<(nwindows - 1)));
+	 */
+		srl	%t_wim, 0x1, %twin_tmp
+spnwin_patch1:	sll	%t_wim, 7, %glob_tmp
+		or	%glob_tmp, %twin_tmp, %glob_tmp
+spnwin_patch2:	and	%glob_tmp, 0xff, %glob_tmp
+
+	/* The trap entry point has set the condition codes
+	 * up for us to see if this is from user or kernel.
+	 * Get the load of 'curptr' out of the way.
+	 */
+	LOAD_CURRENT(curptr, twin_tmp)
+
+	andcc	%t_psr, PSR_PS, %g0
+	be,a	spwin_fromuser				! all user wins, branch
+	 save	%g0, %g0, %g0				! Go where saving will occur
+	
+	/* See if any user windows are active in the set. */
+	ld	[%curptr + TI_UWINMASK], %twin_tmp	! grab win mask
+	orcc	%g0, %twin_tmp, %g0			! check for set bits
+	bne	spwin_exist_uwins			! yep, there are some
+	 andn	%twin_tmp, %glob_tmp, %twin_tmp		! compute new uwinmask
+
+	/* Save into the window which must be saved and do it.
+	 * Basically if we are here, this means that we trapped
+	 * from kernel mode with only kernel windows in the register
+	 * file.
+	 */
+	save	%g0, %g0, %g0		! save into the window to stash away
+	wr	%glob_tmp, 0x0, %wim	! set new %wim, this is safe now
+
+spwin_no_userwins_from_kernel:
+	/* LOCATION: Window to be saved */
+
+	STORE_WINDOW(sp)		! stash the window
+	restore	%g0, %g0, %g0		! go back into trap window
+
+	/* LOCATION: Trap window */
+	mov	%saved_g5, %g5		! restore %glob_tmp
+	mov	%saved_g6, %g6		! restore %curptr
+	wr	%t_psr, 0x0, %psr	! restore condition codes in %psr
+	WRITE_PAUSE			! waste some time
+	jmp	%t_pc			! Return from trap
+	rett	%t_npc			! we are done
+
+spwin_exist_uwins:
+	/* LOCATION: Trap window */
+
+	/* Wow, user windows have to be dealt with, this is dirty
+	 * and messy as all hell.  And difficult to follow if you
+	 * are approaching the infamous register window trap handling
+	 * problem for the first time. DON'T LOOK!
+	 *
+	 * Note that how the execution path works out, the new %wim
+	 * will be left for us in the global temporary register,
+	 * %glob_tmp.  We cannot set the new %wim first because we
+	 * need to save into the appropriate window without inducing
+	 * a trap (traps are off, we'd get a watchdog wheee)...
+	 * But first, store the new user window mask calculated
+	 * above.
+	 */
+	st	%twin_tmp, [%curptr + TI_UWINMASK]
+	save	%g0, %g0, %g0		! Go to where the saving will occur
+
+spwin_fromuser:
+	/* LOCATION: Window to be saved */
+	wr	%glob_tmp, 0x0, %wim	! Now it is safe to set new %wim
+
+	/* LOCATION: Window to be saved */
+
+	/* This instruction branches to a routine which will check
+	 * to validity of the users stack pointer by whatever means
+	 * are necessary.  This means that this is architecture
+	 * specific and thus this branch instruction will need to
+	 * be patched at boot time once the machine type is known.
+	 * This routine _shall not_ touch %curptr under any
+	 * circumstances whatsoever!  It will branch back to the
+	 * label 'spwin_good_ustack' if the stack is ok but still
+	 * needs to be dumped (SRMMU for instance will not need to
+	 * do this) or 'spwin_finish_up' if the stack is ok and the
+	 * registers have already been saved.  If the stack is found
+	 * to be bogus for some reason the routine shall branch to
+	 * the label 'spwin_user_stack_is_bolixed' which will take
+	 * care of things at that point.
+	 */
+	.globl	spwin_mmu_patchme
+spwin_mmu_patchme:	b	spwin_sun4c_stackchk
+				 andcc	%sp, 0x7, %g0
+
+spwin_good_ustack:
+	/* LOCATION: Window to be saved */
+
+	/* The users stack is ok and we can safely save it at
+	 * %sp.
+	 */
+	STORE_WINDOW(sp)
+
+spwin_finish_up:
+	restore	%g0, %g0, %g0		/* Back to trap window. */
+
+	/* LOCATION: Trap window */
+
+	/* We have spilled successfully, and we have properly stored
+	 * the appropriate window onto the stack.
+	 */
+
+	/* Restore saved globals */
+	mov	%saved_g5, %g5
+	mov	%saved_g6, %g6
+
+	wr	%t_psr, 0x0, %psr
+	WRITE_PAUSE
+	jmp	%t_pc
+	rett	%t_npc
+
+spwin_user_stack_is_bolixed:
+	/* LOCATION: Window to be saved */
+
+	/* Wheee, user has trashed his/her stack.  We have to decide
+	 * how to proceed based upon whether we came from kernel mode
+	 * or not.  If we came from kernel mode, toss the window into
+	 * a special buffer and proceed, the kernel _needs_ a window
+	 * and we could be in an interrupt handler so timing is crucial.
+	 * If we came from user land we build a full stack frame and call
+	 * c-code to gun down the process.
+	 */
+	rd	%psr, %glob_tmp
+	andcc	%glob_tmp, PSR_PS, %g0
+	bne	spwin_bad_ustack_from_kernel
+	 nop
+
+	/* Oh well, throw this one window into the per-task window
+	 * buffer, the first one.
+	 */
+	st	%sp, [%curptr + TI_RWIN_SPTRS]
+	STORE_WINDOW(curptr + TI_REG_WINDOW)
+	restore	%g0, %g0, %g0
+
+	/* LOCATION: Trap Window */
+
+	/* Back in the trap window, update winbuffer save count. */
+	mov	1, %twin_tmp
+	st	%twin_tmp, [%curptr + TI_W_SAVED]
+
+		/* Compute new user window mask.  What we are basically
+		 * doing is taking two windows, the invalid one at trap
+		 * time and the one we attempted to throw onto the users
+		 * stack, and saying that everything else is an ok user
+		 * window.  umask = ((~(%t_wim | %wim)) & valid_wim_bits)
+		 */
+		rd	%wim, %twin_tmp
+		or	%twin_tmp, %t_wim, %twin_tmp
+		not	%twin_tmp
+spnwin_patch3:	and	%twin_tmp, 0xff, %twin_tmp	! patched on 7win Sparcs
+		st	%twin_tmp, [%curptr + TI_UWINMASK]
+
+#define STACK_OFFSET (THREAD_SIZE - TRACEREG_SZ - STACKFRAME_SZ)
+
+	sethi	%hi(STACK_OFFSET), %sp
+	or	%sp, %lo(STACK_OFFSET), %sp
+	add	%curptr, %sp, %sp
+
+	/* Restore the saved globals and build a pt_regs frame. */
+	mov	%saved_g5, %g5
+	mov	%saved_g6, %g6
+	STORE_PT_ALL(sp, t_psr, t_pc, t_npc, g1)
+
+	sethi	%hi(STACK_OFFSET), %g6
+	or	%g6, %lo(STACK_OFFSET), %g6
+	sub	%sp, %g6, %g6		! curptr
+
+	/* Turn on traps and call c-code to deal with it. */
+	wr	%t_psr, PSR_ET, %psr
+	nop
+	call	window_overflow_fault
+	 nop
+
+	/* Return from trap if C-code actually fixes things, if it
+	 * doesn't then we never get this far as the process will
+	 * be given the look of death from Commander Peanut.
+	 */
+	b	ret_trap_entry
+	 clr	%l6
+
+spwin_bad_ustack_from_kernel:
+	/* LOCATION: Window to be saved */
+
+	/* The kernel provoked a spill window trap, but the window we
+	 * need to save is a user one and the process has trashed its
+	 * stack pointer.  We need to be quick, so we throw it into
+	 * a per-process window buffer until we can properly handle
+	 * this later on.
+	 */
+	SAVE_BOLIXED_USER_STACK(curptr, glob_tmp)
+	restore	%g0, %g0, %g0
+
+	/* LOCATION: Trap window */
+
+	/* Restore globals, condition codes in the %psr and
+	 * return from trap.  Note, restoring %g6 when returning
+	 * to kernel mode is not necessarily these days. ;-)
+	 */
+	mov	%saved_g5, %g5
+	mov	%saved_g6, %g6
+
+	wr	%t_psr, 0x0, %psr
+	WRITE_PAUSE
+
+	jmp	%t_pc
+	rett	%t_npc
+
+/* Undefine the register macros which would only cause trouble
+ * if used below.  This helps find 'stupid' coding errors that
+ * produce 'odd' behavior.  The routines below are allowed to
+ * make usage of glob_tmp and t_psr so we leave them defined.
+ */
+#undef twin_tmp
+#undef curptr
+#undef t_pc
+#undef t_npc
+#undef t_wim
+#undef saved_g5
+#undef saved_g6
+
+/* Now come the per-architecture window overflow stack checking routines.
+ * As noted above %curptr cannot be touched by this routine at all.
+ */
+
+	.globl	spwin_sun4c_stackchk
+spwin_sun4c_stackchk:
+	/* LOCATION: Window to be saved on the stack */
+
+	/* See if the stack is in the address space hole but first,
+	 * check results of callers andcc %sp, 0x7, %g0
+	 */
+	be	1f
+	 sra	%sp, 29, %glob_tmp
+
+	rd	%psr, %glob_tmp
+	b	spwin_user_stack_is_bolixed + 0x4
+	 nop
+
+1:
+	add	%glob_tmp, 0x1, %glob_tmp
+	andncc	%glob_tmp, 0x1, %g0
+	be	1f
+	 and	%sp, 0xfff, %glob_tmp		! delay slot
+
+	rd	%psr, %glob_tmp
+	b	spwin_user_stack_is_bolixed + 0x4
+	 nop
+
+	/* See if our dump area will be on more than one
+	 * page.
+	 */
+1:
+	add	%glob_tmp, 0x38, %glob_tmp
+	andncc	%glob_tmp, 0xff8, %g0
+	be	spwin_sun4c_onepage		! only one page to check
+	 lda	[%sp] ASI_PTE, %glob_tmp	! have to check first page anyways
+
+spwin_sun4c_twopages:
+	/* Is first page ok permission wise? */
+	srl	%glob_tmp, 29, %glob_tmp
+	cmp	%glob_tmp, 0x6
+	be	1f
+	 add	%sp, 0x38, %glob_tmp	/* Is second page in vma hole? */
+
+	rd	%psr, %glob_tmp
+	b	spwin_user_stack_is_bolixed + 0x4
+	 nop
+
+1:
+	sra	%glob_tmp, 29, %glob_tmp
+	add	%glob_tmp, 0x1, %glob_tmp
+	andncc	%glob_tmp, 0x1, %g0
+	be	1f
+	 add	%sp, 0x38, %glob_tmp
+
+	rd	%psr, %glob_tmp
+	b	spwin_user_stack_is_bolixed + 0x4
+	 nop
+
+1:
+	lda	[%glob_tmp] ASI_PTE, %glob_tmp
+
+spwin_sun4c_onepage:
+	srl	%glob_tmp, 29, %glob_tmp
+	cmp	%glob_tmp, 0x6				! can user write to it?
+	be	spwin_good_ustack			! success
+	 nop
+
+	rd	%psr, %glob_tmp
+	b	spwin_user_stack_is_bolixed + 0x4
+	 nop
+
+	/* This is a generic SRMMU routine.  As far as I know this
+	 * works for all current v8/srmmu implementations, we'll
+	 * see...
+	 */
+	.globl	spwin_srmmu_stackchk
+spwin_srmmu_stackchk:
+	/* LOCATION: Window to be saved on the stack */
+
+	/* Because of SMP concerns and speed we play a trick.
+	 * We disable fault traps in the MMU control register,
+	 * Execute the stores, then check the fault registers
+	 * to see what happens.  I can hear Linus now
+	 * "disgusting... broken hardware...".
+	 *
+	 * But first, check to see if the users stack has ended
+	 * up in kernel vma, then we would succeed for the 'wrong'
+	 * reason... ;(  Note that the 'sethi' below assumes the
+	 * kernel is page aligned, which should always be the case.
+	 */
+	/* Check results of callers andcc %sp, 0x7, %g0 */
+	bne	spwin_user_stack_is_bolixed
+	 sethi   %hi(PAGE_OFFSET), %glob_tmp
+	cmp	%glob_tmp, %sp
+	bleu	spwin_user_stack_is_bolixed
+	 mov	AC_M_SFSR, %glob_tmp
+
+	/* Clear the fault status and turn on the no_fault bit. */
+	lda	[%glob_tmp] ASI_M_MMUREGS, %g0		! eat SFSR
+
+	lda	[%g0] ASI_M_MMUREGS, %glob_tmp		! read MMU control
+	or	%glob_tmp, 0x2, %glob_tmp		! or in no_fault bit
+	sta	%glob_tmp, [%g0] ASI_M_MMUREGS		! set it
+
+	/* Dump the registers and cross fingers. */
+	STORE_WINDOW(sp)
+
+	/* Clear the no_fault bit and check the status. */
+	andn	%glob_tmp, 0x2, %glob_tmp
+	sta	%glob_tmp, [%g0] ASI_M_MMUREGS
+
+	mov	AC_M_SFAR, %glob_tmp
+	lda	[%glob_tmp] ASI_M_MMUREGS, %g0
+
+	mov	AC_M_SFSR, %glob_tmp
+	lda	[%glob_tmp] ASI_M_MMUREGS, %glob_tmp
+	andcc	%glob_tmp, 0x2, %g0			! did we fault?
+	be,a	spwin_finish_up + 0x4			! cool beans, success
+	 restore %g0, %g0, %g0
+
+	rd	%psr, %glob_tmp
+	b	spwin_user_stack_is_bolixed + 0x4	! we faulted, ugh
+	 nop
diff --git a/arch/sparc/kernel/wuf.S b/arch/sparc/kernel/wuf.S
new file mode 100644
index 0000000..d1a266b
--- /dev/null
+++ b/arch/sparc/kernel/wuf.S
@@ -0,0 +1,360 @@
+/* $Id: wuf.S,v 1.39 2000/01/08 16:38:18 anton Exp $
+ * wuf.S: Window underflow trap handler for the Sparc.
+ *
+ * Copyright (C) 1995 David S. Miller
+ */
+
+#include <asm/contregs.h>
+#include <asm/page.h>
+#include <asm/ptrace.h>
+#include <asm/psr.h>
+#include <asm/smp.h>
+#include <asm/asi.h>
+#include <asm/winmacro.h>
+#include <asm/asmmacro.h>
+#include <asm/thread_info.h>
+
+/* Just like the overflow handler we define macros for registers
+ * with fixed meanings in this routine.
+ */
+#define t_psr       l0
+#define t_pc        l1
+#define t_npc       l2
+#define t_wim       l3
+/* Don't touch the above registers or else you die horribly... */
+
+/* Now macros for the available scratch registers in this routine. */
+#define twin_tmp1    l4
+#define twin_tmp2    l5
+
+#define curptr       g6
+
+	.text
+	.align	4
+
+	/* The trap entry point has executed the following:
+	 *
+	 * rd    %psr, %l0
+	 * rd    %wim, %l3
+	 * b     fill_window_entry
+	 * andcc %l0, PSR_PS, %g0
+	 */
+
+	/* Datum current_thread_info->uwinmask contains at all times a bitmask
+	 * where if any user windows are active, at least one bit will
+	 * be set in to mask.  If no user windows are active, the bitmask
+	 * will be all zeroes.
+	 */
+
+	/* To get an idea of what has just happened to cause this
+	 * trap take a look at this diagram:
+	 *
+	 *      1  2  3  4     <--  Window number
+	 *      ----------
+	 *      T  O  W  I     <--  Symbolic name
+	 *
+	 *      O == the window that execution was in when
+	 *           the restore was attempted
+	 *
+	 *      T == the trap itself has save'd us into this
+	 *           window
+	 *
+	 *      W == this window is the one which is now invalid
+	 *           and must be made valid plus loaded from the
+	 *           stack
+	 *
+	 *      I == this window will be the invalid one when we
+	 *           are done and return from trap if successful
+	 */
+
+	/* BEGINNING OF PATCH INSTRUCTIONS */
+
+	/* On 7-window Sparc the boot code patches fnwin_patch1
+	 * with the following instruction.
+	 */
+	.globl	fnwin_patch1_7win, fnwin_patch2_7win
+fnwin_patch1_7win:	srl	%t_wim, 6, %twin_tmp2
+fnwin_patch2_7win:	and	%twin_tmp1, 0x7f, %twin_tmp1
+	/* END OF PATCH INSTRUCTIONS */
+
+	.globl	fill_window_entry, fnwin_patch1, fnwin_patch2
+fill_window_entry:
+	/* LOCATION: Window 'T' */
+
+	/* Compute what the new %wim is going to be if we retrieve
+	 * the proper window off of the stack.
+	 */
+		sll	%t_wim, 1, %twin_tmp1
+fnwin_patch1:	srl	%t_wim, 7, %twin_tmp2
+		or	%twin_tmp1, %twin_tmp2, %twin_tmp1
+fnwin_patch2:	and	%twin_tmp1, 0xff, %twin_tmp1
+
+	wr	%twin_tmp1, 0x0, %wim	/* Make window 'I' invalid */
+
+	andcc	%t_psr, PSR_PS, %g0
+	be	fwin_from_user
+	 restore	%g0, %g0, %g0		/* Restore to window 'O' */
+
+	/* Trapped from kernel, we trust that the kernel does not
+	 * 'over restore' sorta speak and just grab the window
+	 * from the stack and return.  Easy enough.
+	 */
+fwin_from_kernel:
+	/* LOCATION: Window 'O' */
+
+	restore %g0, %g0, %g0
+
+	/* LOCATION: Window 'W' */
+
+	LOAD_WINDOW(sp)	                /* Load it up */
+
+	/* Spin the wheel... */
+	save	%g0, %g0, %g0
+	save	%g0, %g0, %g0
+	/* I'd like to buy a vowel please... */
+
+	/* LOCATION: Window 'T' */
+
+	/* Now preserve the condition codes in %psr, pause, and
+	 * return from trap.  This is the simplest case of all.
+	 */
+	wr	%t_psr, 0x0, %psr
+	WRITE_PAUSE
+
+	jmp	%t_pc
+	rett	%t_npc
+
+fwin_from_user:
+	/* LOCATION: Window 'O' */
+
+	restore	%g0, %g0, %g0		/* Restore to window 'W' */
+
+	/* LOCATION: Window 'W' */
+
+	/* Branch to the architecture specific stack validation
+	 * routine.  They can be found below...
+	 */
+	.globl	fwin_mmu_patchme
+fwin_mmu_patchme:	b	sun4c_fwin_stackchk
+				 andcc	%sp, 0x7, %g0
+
+#define STACK_OFFSET (THREAD_SIZE - TRACEREG_SZ - STACKFRAME_SZ)
+
+fwin_user_stack_is_bolixed:
+	/* LOCATION: Window 'W' */
+
+	/* Place a pt_regs frame on the kernel stack, save back
+	 * to the trap window and call c-code to deal with this.
+	 */
+	LOAD_CURRENT(l4, l5)
+
+	sethi	%hi(STACK_OFFSET), %l5
+	or	%l5, %lo(STACK_OFFSET), %l5
+	add	%l4, %l5, %l5
+
+	/* Store globals into pt_regs frame. */
+	STORE_PT_GLOBALS(l5)
+	STORE_PT_YREG(l5, g3)
+
+	/* Save current in a global while we change windows. */
+	mov	%l4, %curptr
+
+	save	%g0, %g0, %g0
+
+	/* LOCATION: Window 'O' */
+
+	rd	%psr, %g3		/* Read %psr in live user window */
+	mov	%fp, %g4		/* Save bogus frame pointer. */
+
+	save	%g0, %g0, %g0
+
+	/* LOCATION: Window 'T' */
+
+	sethi	%hi(STACK_OFFSET), %l5
+	or	%l5, %lo(STACK_OFFSET), %l5
+	add	%curptr, %l5, %sp
+
+	/* Build rest of pt_regs. */
+	STORE_PT_INS(sp)
+	STORE_PT_PRIV(sp, t_psr, t_pc, t_npc)
+
+	/* re-set trap time %wim value */
+	wr	%t_wim, 0x0, %wim
+
+	/* Fix users window mask and buffer save count. */
+	mov	0x1, %g5
+	sll	%g5, %g3, %g5
+	st	%g5, [%curptr + TI_UWINMASK]		! one live user window still
+	st	%g0, [%curptr + TI_W_SAVED]		! no windows in the buffer
+
+	wr	%t_psr, PSR_ET, %psr			! enable traps
+	nop
+	call	window_underflow_fault
+	 mov	%g4, %o0
+
+	b	ret_trap_entry
+	 clr	%l6
+
+fwin_user_stack_is_ok:
+	/* LOCATION: Window 'W' */
+
+	/* The users stack area is kosher and mapped, load the
+	 * window and fall through to the finish up routine.
+	 */
+	LOAD_WINDOW(sp)
+
+	/* Round and round she goes... */
+	save	%g0, %g0, %g0		/* Save to window 'O' */
+	save	%g0, %g0, %g0		/* Save to window 'T' */
+	/* Where she'll trap nobody knows... */
+
+	/* LOCATION: Window 'T' */
+
+fwin_user_finish_up:
+	/* LOCATION: Window 'T' */
+
+	wr	%t_psr, 0x0, %psr
+	WRITE_PAUSE	
+
+	jmp	%t_pc
+	rett	%t_npc
+
+	/* Here come the architecture specific checks for stack.
+	 * mappings.  Note that unlike the window overflow handler
+	 * we only need to check whether the user can read from
+	 * the appropriate addresses.  Also note that we are in
+	 * an invalid window which will be loaded, and this means
+	 * that until we actually load the window up we are free
+	 * to use any of the local registers contained within.
+	 *
+	 * On success these routine branch to fwin_user_stack_is_ok
+	 * if the area at %sp is user readable and the window still
+	 * needs to be loaded, else fwin_user_finish_up if the
+	 * routine has done the loading itself.  On failure (bogus
+	 * user stack) the routine shall branch to the label called
+	 * fwin_user_stack_is_bolixed.
+	 *
+	 * Contrary to the arch-specific window overflow stack
+	 * check routines in wof.S, these routines are free to use
+	 * any of the local registers they want to as this window
+	 * does not belong to anyone at this point, however the
+	 * outs and ins are still verboten as they are part of
+	 * 'someone elses' window possibly.
+	 */
+
+	.align	4
+	.globl	sun4c_fwin_stackchk
+sun4c_fwin_stackchk:
+	/* LOCATION: Window 'W' */
+
+	/* Caller did 'andcc %sp, 0x7, %g0' */
+	be	1f
+	 and	%sp, 0xfff, %l0		! delay slot
+
+	b,a	fwin_user_stack_is_bolixed
+
+	/* See if we have to check the sanity of one page or two */
+1:
+	add	%l0, 0x38, %l0
+	sra	%sp, 29, %l5
+	add	%l5, 0x1, %l5
+	andncc	%l5, 0x1, %g0
+	be	1f
+	 andncc	%l0, 0xff8, %g0
+
+	b,a	fwin_user_stack_is_bolixed	/* %sp is in vma hole, yuck */
+
+1:
+	be	sun4c_fwin_onepage	/* Only one page to check */
+	 lda	[%sp] ASI_PTE, %l1
+sun4c_fwin_twopages:
+	add	%sp, 0x38, %l0
+	sra	%l0, 29, %l5
+	add	%l5, 0x1, %l5
+	andncc	%l5, 0x1, %g0
+	be	1f
+	 lda	[%l0] ASI_PTE, %l1
+
+	b,a	fwin_user_stack_is_bolixed	/* Second page in vma hole */
+
+1:
+	srl	%l1, 29, %l1
+	andcc	%l1, 0x4, %g0
+	bne	sun4c_fwin_onepage
+	 lda	[%sp] ASI_PTE, %l1	
+
+	b,a	fwin_user_stack_is_bolixed	/* Second page has bad perms */
+
+sun4c_fwin_onepage:
+	srl	%l1, 29, %l1
+	andcc	%l1, 0x4, %g0
+	bne	fwin_user_stack_is_ok
+	 nop
+
+	/* A page had bad page permissions, losing... */
+	b,a	fwin_user_stack_is_bolixed
+
+	.globl	srmmu_fwin_stackchk
+srmmu_fwin_stackchk:
+	/* LOCATION: Window 'W' */
+
+	/* Caller did 'andcc %sp, 0x7, %g0' */
+	bne	fwin_user_stack_is_bolixed
+	 sethi   %hi(PAGE_OFFSET), %l5
+
+	/* Check if the users stack is in kernel vma, then our
+	 * trial and error technique below would succeed for
+	 * the 'wrong' reason.
+	 */
+	mov	AC_M_SFSR, %l4
+	cmp	%l5, %sp
+	bleu	fwin_user_stack_is_bolixed
+	 lda	[%l4] ASI_M_MMUREGS, %g0	! clear fault status
+
+	/* The technique is, turn off faults on this processor,
+	 * just let the load rip, then check the sfsr to see if
+	 * a fault did occur.  Then we turn on fault traps again
+	 * and branch conditionally based upon what happened.
+	 */
+	lda	[%g0] ASI_M_MMUREGS, %l5	! read mmu-ctrl reg
+	or	%l5, 0x2, %l5			! turn on no-fault bit
+	sta	%l5, [%g0] ASI_M_MMUREGS	! store it
+
+	/* Cross fingers and go for it. */
+	LOAD_WINDOW(sp)
+
+	/* A penny 'saved'... */
+	save	%g0, %g0, %g0
+	save	%g0, %g0, %g0
+	/* Is a BADTRAP earned... */
+
+	/* LOCATION: Window 'T' */
+
+	lda	[%g0] ASI_M_MMUREGS, %twin_tmp1	! load mmu-ctrl again
+	andn	%twin_tmp1, 0x2, %twin_tmp1	! clear no-fault bit
+	sta	%twin_tmp1, [%g0] ASI_M_MMUREGS	! store it
+
+	mov	AC_M_SFAR, %twin_tmp2
+	lda	[%twin_tmp2] ASI_M_MMUREGS, %g0	! read fault address
+
+	mov	AC_M_SFSR, %twin_tmp2
+	lda	[%twin_tmp2] ASI_M_MMUREGS, %twin_tmp2	! read fault status
+	andcc	%twin_tmp2, 0x2, %g0			! did fault occur?
+
+	bne	1f					! yep, cleanup
+	 nop
+
+	wr	%t_psr, 0x0, %psr
+	nop
+	b	fwin_user_finish_up + 0x4
+	 nop
+
+	/* Did I ever tell you about my window lobotomy?
+	 * anyways... fwin_user_stack_is_bolixed expects
+	 * to be in window 'W' so make it happy or else
+	 * we watchdog badly.
+	 */
+1:
+	restore	%g0, %g0, %g0
+	b	fwin_user_stack_is_bolixed	! oh well
+	 restore	%g0, %g0, %g0
diff --git a/arch/sparc/lib/COPYING.LIB b/arch/sparc/lib/COPYING.LIB
new file mode 100644
index 0000000..eb685a5
--- /dev/null
+++ b/arch/sparc/lib/COPYING.LIB
@@ -0,0 +1,481 @@
+		  GNU LIBRARY GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+                    675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+		  GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+     Appendix: How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile
new file mode 100644
index 0000000..2296ff9
--- /dev/null
+++ b/arch/sparc/lib/Makefile
@@ -0,0 +1,13 @@
+# $Id: Makefile,v 1.35 2000/12/15 00:41:18 davem Exp $
+# Makefile for Sparc library files..
+#
+
+EXTRA_AFLAGS := -ansi -DST_DIV0=0x02
+
+lib-y := mul.o rem.o sdiv.o udiv.o umul.o urem.o ashrdi3.o memcpy.o memset.o \
+         strlen.o checksum.o blockops.o memscan.o memcmp.o strncmp.o \
+	 strncpy_from_user.o divdi3.o udivdi3.o strlen_user.o \
+	 copy_user.o locks.o atomic.o atomic32.o bitops.o \
+	 lshrdi3.o ashldi3.o rwsem.o muldi3.o bitext.o
+
+lib-$(CONFIG_DEBUG_SPINLOCK) +=	debuglocks.o
diff --git a/arch/sparc/lib/ashldi3.S b/arch/sparc/lib/ashldi3.S
new file mode 100644
index 0000000..52418a0
--- /dev/null
+++ b/arch/sparc/lib/ashldi3.S
@@ -0,0 +1,34 @@
+/* $Id: ashldi3.S,v 1.2 1999/11/19 04:11:46 davem Exp $
+ * ashldi3.S:	GCC emits these for certain drivers playing
+ *		with long longs.
+ *
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ */
+
+	.text
+	.align	4
+	.globl	__ashldi3
+__ashldi3:
+	cmp	%o2, 0
+	be	9f
+	 mov	0x20, %g2
+
+	sub	%g2, %o2, %g2
+	cmp	%g2, 0
+	bg	7f
+	 sll	%o0, %o2, %g3
+
+	neg	%g2
+	clr	%o5
+	b	8f
+	 sll	%o1, %g2, %o4
+7:
+	srl	%o1, %g2, %g2
+	sll	%o1, %o2, %o5
+	or	%g3, %g2, %o4
+8:
+	mov	%o4, %o0
+	mov	%o5, %o1
+9:
+	retl
+	 nop
diff --git a/arch/sparc/lib/ashrdi3.S b/arch/sparc/lib/ashrdi3.S
new file mode 100644
index 0000000..2848237
--- /dev/null
+++ b/arch/sparc/lib/ashrdi3.S
@@ -0,0 +1,36 @@
+/* $Id: ashrdi3.S,v 1.4 1999/11/19 04:11:49 davem Exp $
+ * ashrdi3.S:	The filesystem code creates all kinds of references to
+ *              this little routine on the sparc with gcc.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+	.text
+	.align	4
+	.globl __ashrdi3
+__ashrdi3:
+	tst	%o2
+	be	3f
+	 or	%g0, 32, %g2
+
+	sub	%g2, %o2, %g2
+
+	tst	%g2
+	bg	1f
+	 sra	%o0, %o2, %o4
+
+	sra	%o0, 31, %o4
+	sub	%g0, %g2, %g2
+	ba	2f
+	 sra	%o0, %g2, %o5
+
+1:
+	sll	%o0, %g2, %g3
+	srl	%o1, %o2, %g2
+	or	%g2, %g3, %o5
+2:
+	or	%g0, %o4, %o0
+	or	%g0, %o5, %o1
+3:
+	jmpl	%o7 + 8, %g0
+	 nop
diff --git a/arch/sparc/lib/atomic.S b/arch/sparc/lib/atomic.S
new file mode 100644
index 0000000..f48ad0c
--- /dev/null
+++ b/arch/sparc/lib/atomic.S
@@ -0,0 +1,100 @@
+/* atomic.S: Move this stuff here for better ICACHE hit rates.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu)
+ */
+
+#include <linux/config.h>
+#include <asm/ptrace.h>
+#include <asm/psr.h>
+
+	.text
+	.align	4
+
+	.globl  __atomic_begin
+__atomic_begin:
+
+#ifndef CONFIG_SMP
+	.globl	___xchg32_sun4c
+___xchg32_sun4c:
+	rd	%psr, %g3
+	andcc	%g3, PSR_PIL, %g0
+	bne	1f
+	 nop
+	wr	%g3, PSR_PIL, %psr
+	nop; nop; nop
+1:
+	andcc	%g3, PSR_PIL, %g0
+	ld	[%g1], %g7
+	bne	1f
+	 st	%g2, [%g1]
+	wr	%g3, 0x0, %psr
+	nop; nop; nop
+1:
+	mov	%g7, %g2
+	jmpl	%o7 + 8, %g0
+	 mov	%g4, %o7
+
+	.globl	___xchg32_sun4md
+___xchg32_sun4md:
+	swap	[%g1], %g2
+	jmpl	%o7 + 8, %g0
+	 mov	%g4, %o7
+#endif
+
+	/* Read asm-sparc/atomic.h carefully to understand how this works for SMP.
+	 * Really, some things here for SMP are overly clever, go read the header.
+	 */
+	.globl	___atomic24_add
+___atomic24_add:
+	rd	%psr, %g3		! Keep the code small, old way was stupid
+	nop; nop; nop;			! Let the bits set
+	or	%g3, PSR_PIL, %g7	! Disable interrupts
+	wr	%g7, 0x0, %psr		! Set %psr
+	nop; nop; nop;			! Let the bits set
+#ifdef CONFIG_SMP
+1:	ldstub	[%g1 + 3], %g7		! Spin on the byte lock for SMP.
+	orcc	%g7, 0x0, %g0		! Did we get it?
+	bne	1b			! Nope...
+	 ld	[%g1], %g7		! Load locked atomic24_t
+	sra	%g7, 8, %g7		! Get signed 24-bit integer
+	add	%g7, %g2, %g2		! Add in argument
+	sll	%g2, 8, %g7		! Transpose back to atomic24_t
+	st	%g7, [%g1]		! Clever: This releases the lock as well.
+#else
+	ld	[%g1], %g7		! Load locked atomic24_t
+	add	%g7, %g2, %g2		! Add in argument
+	st	%g2, [%g1]		! Store it back
+#endif
+	wr	%g3, 0x0, %psr		! Restore original PSR_PIL
+	nop; nop; nop;			! Let the bits set
+	jmpl	%o7, %g0		! NOTE: not + 8, see callers in atomic.h
+	 mov	%g4, %o7		! Restore %o7
+
+	.globl	___atomic24_sub
+___atomic24_sub:
+	rd	%psr, %g3		! Keep the code small, old way was stupid
+	nop; nop; nop;			! Let the bits set
+	or	%g3, PSR_PIL, %g7	! Disable interrupts
+	wr	%g7, 0x0, %psr		! Set %psr
+	nop; nop; nop;			! Let the bits set
+#ifdef CONFIG_SMP
+1:	ldstub	[%g1 + 3], %g7		! Spin on the byte lock for SMP.
+	orcc	%g7, 0x0, %g0		! Did we get it?
+	bne	1b			! Nope...
+	 ld	[%g1], %g7		! Load locked atomic24_t
+	sra	%g7, 8, %g7		! Get signed 24-bit integer
+	sub	%g7, %g2, %g2		! Subtract argument
+	sll	%g2, 8, %g7		! Transpose back to atomic24_t
+	st	%g7, [%g1]		! Clever: This releases the lock as well
+#else
+	ld	[%g1], %g7		! Load locked atomic24_t
+	sub	%g7, %g2, %g2		! Subtract argument
+	st	%g2, [%g1]		! Store it back
+#endif
+	wr	%g3, 0x0, %psr		! Restore original PSR_PIL
+	nop; nop; nop;			! Let the bits set
+	jmpl	%o7, %g0		! NOTE: not + 8, see callers in atomic.h
+	 mov	%g4, %o7		! Restore %o7
+
+	.globl  __atomic_end
+__atomic_end:
diff --git a/arch/sparc/lib/atomic32.c b/arch/sparc/lib/atomic32.c
new file mode 100644
index 0000000..19724c5
--- /dev/null
+++ b/arch/sparc/lib/atomic32.c
@@ -0,0 +1,53 @@
+/*
+ * atomic32.c: 32-bit atomic_t implementation
+ *
+ * Copyright (C) 2004 Keith M Wesolowski
+ * 
+ * Based on asm-parisc/atomic.h Copyright (C) 2000 Philipp Rumpf
+ */
+
+#include <asm/atomic.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+
+#ifdef CONFIG_SMP
+#define ATOMIC_HASH_SIZE	4
+#define ATOMIC_HASH(a)	(&__atomic_hash[(((unsigned long)a)>>8) & (ATOMIC_HASH_SIZE-1)])
+
+spinlock_t __atomic_hash[ATOMIC_HASH_SIZE] = {
+	[0 ... (ATOMIC_HASH_SIZE-1)] = SPIN_LOCK_UNLOCKED
+};
+
+#else /* SMP */
+
+static spinlock_t dummy = SPIN_LOCK_UNLOCKED;
+#define ATOMIC_HASH_SIZE	1
+#define ATOMIC_HASH(a)		(&dummy)
+
+#endif /* SMP */
+
+int __atomic_add_return(int i, atomic_t *v)
+{
+	int ret;
+	unsigned long flags;
+	spin_lock_irqsave(ATOMIC_HASH(v), flags);
+
+	ret = (v->counter += i);
+
+	spin_unlock_irqrestore(ATOMIC_HASH(v), flags);
+	return ret;
+}
+
+void atomic_set(atomic_t *v, int i)
+{
+	unsigned long flags;
+	spin_lock_irqsave(ATOMIC_HASH(v), flags);
+
+	v->counter = i;
+
+	spin_unlock_irqrestore(ATOMIC_HASH(v), flags);
+}
+
+EXPORT_SYMBOL(__atomic_add_return);
+EXPORT_SYMBOL(atomic_set);
+
diff --git a/arch/sparc/lib/bitext.c b/arch/sparc/lib/bitext.c
new file mode 100644
index 0000000..94b05e8
--- /dev/null
+++ b/arch/sparc/lib/bitext.c
@@ -0,0 +1,132 @@
+/*
+ * bitext.c: kernel little helper (of bit shuffling variety).
+ *
+ * Copyright (C) 2002 Pete Zaitcev <zaitcev@yahoo.com>
+ *
+ * The algorithm to search a zero bit string is geared towards its application.
+ * We expect a couple of fixed sizes of requests, so a rotating counter, reset
+ * by align size, should provide fast enough search while maintaining low
+ * fragmentation.
+ */
+
+#include <linux/smp_lock.h>
+#include <linux/bitops.h>
+
+#include <asm/bitext.h>
+
+/**
+ * bit_map_string_get - find and set a bit string in bit map.
+ * @t: the bit map.
+ * @len: requested string length
+ * @align: requested alignment
+ *
+ * Returns offset in the map or -1 if out of space.
+ *
+ * Not safe to call from an interrupt (uses spin_lock).
+ */
+int bit_map_string_get(struct bit_map *t, int len, int align)
+{
+	int offset, count;	/* siamese twins */
+	int off_new;
+	int align1;
+	int i, color;
+
+	if (t->num_colors) {
+		/* align is overloaded to be the page color */
+		color = align;
+		align = t->num_colors;
+	} else {
+		color = 0;
+		if (align == 0)
+			align = 1;
+	}
+	align1 = align - 1;
+	if ((align & align1) != 0)
+		BUG();
+	if (align < 0 || align >= t->size)
+		BUG();
+	if (len <= 0 || len > t->size)
+		BUG();
+	color &= align1;
+
+	spin_lock(&t->lock);
+	if (len < t->last_size)
+		offset = t->first_free;
+	else
+		offset = t->last_off & ~align1;
+	count = 0;
+	for (;;) {
+		off_new = find_next_zero_bit(t->map, t->size, offset);
+		off_new = ((off_new + align1) & ~align1) + color;
+		count += off_new - offset;
+		offset = off_new;
+		if (offset >= t->size)
+			offset = 0;
+		if (count + len > t->size) {
+			spin_unlock(&t->lock);
+/* P3 */ printk(KERN_ERR
+  "bitmap out: size %d used %d off %d len %d align %d count %d\n",
+  t->size, t->used, offset, len, align, count);
+			return -1;
+		}
+
+		if (offset + len > t->size) {
+			count += t->size - offset;
+			offset = 0;
+			continue;
+		}
+
+		i = 0;
+		while (test_bit(offset + i, t->map) == 0) {
+			i++;
+			if (i == len) {
+				for (i = 0; i < len; i++)
+					__set_bit(offset + i, t->map);
+				if (offset == t->first_free)
+					t->first_free = find_next_zero_bit
+							(t->map, t->size,
+							 t->first_free + len);
+				if ((t->last_off = offset + len) >= t->size)
+					t->last_off = 0;
+				t->used += len;
+				t->last_size = len;
+				spin_unlock(&t->lock);
+				return offset;
+			}
+		}
+		count += i + 1;
+		if ((offset += i + 1) >= t->size)
+			offset = 0;
+	}
+}
+
+void bit_map_clear(struct bit_map *t, int offset, int len)
+{
+	int i;
+
+	if (t->used < len)
+		BUG();		/* Much too late to do any good, but alas... */
+	spin_lock(&t->lock);
+	for (i = 0; i < len; i++) {
+		if (test_bit(offset + i, t->map) == 0)
+			BUG();
+		__clear_bit(offset + i, t->map);
+	}
+	if (offset < t->first_free)
+		t->first_free = offset;
+	t->used -= len;
+	spin_unlock(&t->lock);
+}
+
+void bit_map_init(struct bit_map *t, unsigned long *map, int size)
+{
+
+	if ((size & 07) != 0)
+		BUG();
+	memset(map, 0, size>>3);
+
+	memset(t, 0, sizeof *t);
+	spin_lock_init(&t->lock);
+	t->map = map;
+	t->size = size;
+}
diff --git a/arch/sparc/lib/bitops.S b/arch/sparc/lib/bitops.S
new file mode 100644
index 0000000..3e93997
--- /dev/null
+++ b/arch/sparc/lib/bitops.S
@@ -0,0 +1,110 @@
+/* bitops.S: Low level assembler bit operations.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/config.h>
+#include <asm/ptrace.h>
+#include <asm/psr.h>
+
+	.text
+	.align	4
+
+	.globl  __bitops_begin
+__bitops_begin:
+
+	/* Take bits in %g2 and set them in word at %g1,
+	 * return whether bits were set in original value
+	 * in %g2.  %g4 holds value to restore into %o7
+	 * in delay slot of jmpl return, %g3 + %g5 + %g7 can be
+	 * used as temporaries and thus is considered clobbered
+	 * by all callers.
+	 */
+	.globl	___set_bit
+___set_bit:
+	rd	%psr, %g3
+	nop; nop; nop;
+	or	%g3, PSR_PIL, %g5
+	wr	%g5, 0x0, %psr
+	nop; nop; nop
+#ifdef CONFIG_SMP
+	set	bitops_spinlock, %g5
+2:	ldstub	[%g5], %g7		! Spin on the byte lock for SMP.
+	orcc	%g7, 0x0, %g0		! Did we get it?
+	bne	2b			! Nope...
+#endif
+	 ld	[%g1], %g7
+	or	%g7, %g2, %g5
+	and	%g7, %g2, %g2
+#ifdef CONFIG_SMP
+	st	%g5, [%g1]
+	set	bitops_spinlock, %g5
+	stb	%g0, [%g5]
+#else
+	st	%g5, [%g1]
+#endif
+	wr	%g3, 0x0, %psr
+	nop; nop; nop
+	jmpl	%o7, %g0
+	 mov	%g4, %o7
+
+	/* Same as above, but clears the bits from %g2 instead. */
+	.globl	___clear_bit
+___clear_bit:
+	rd	%psr, %g3
+	nop; nop; nop
+	or	%g3, PSR_PIL, %g5
+	wr	%g5, 0x0, %psr
+	nop; nop; nop
+#ifdef CONFIG_SMP
+	set	bitops_spinlock, %g5
+2:	ldstub	[%g5], %g7		! Spin on the byte lock for SMP.
+	orcc	%g7, 0x0, %g0		! Did we get it?
+	bne	2b			! Nope...
+#endif
+	 ld	[%g1], %g7
+	andn	%g7, %g2, %g5
+	and	%g7, %g2, %g2
+#ifdef CONFIG_SMP
+	st	%g5, [%g1]
+	set	bitops_spinlock, %g5
+	stb	%g0, [%g5]
+#else
+	st	%g5, [%g1]
+#endif
+	wr	%g3, 0x0, %psr
+	nop; nop; nop
+	jmpl	%o7, %g0
+	 mov	%g4, %o7
+
+	/* Same thing again, but this time toggles the bits from %g2. */
+	.globl	___change_bit
+___change_bit:
+	rd	%psr, %g3
+	nop; nop; nop
+	or	%g3, PSR_PIL, %g5
+	wr	%g5, 0x0, %psr
+	nop; nop; nop
+#ifdef CONFIG_SMP
+	set	bitops_spinlock, %g5
+2:	ldstub	[%g5], %g7		! Spin on the byte lock for SMP.
+	orcc	%g7, 0x0, %g0		! Did we get it?
+	bne	2b			! Nope...
+#endif
+	 ld	[%g1], %g7
+	xor	%g7, %g2, %g5
+	and	%g7, %g2, %g2
+#ifdef CONFIG_SMP
+	st	%g5, [%g1]
+	set	bitops_spinlock, %g5
+	stb	%g0, [%g5]
+#else
+	st	%g5, [%g1]
+#endif
+	wr	%g3, 0x0, %psr
+	nop; nop; nop
+	jmpl	%o7, %g0
+	 mov	%g4, %o7
+
+	.globl  __bitops_end
+__bitops_end:
diff --git a/arch/sparc/lib/blockops.S b/arch/sparc/lib/blockops.S
new file mode 100644
index 0000000..a7c7ffa
--- /dev/null
+++ b/arch/sparc/lib/blockops.S
@@ -0,0 +1,89 @@
+/* $Id: blockops.S,v 1.8 1998/01/30 10:58:44 jj Exp $
+ * blockops.S: Common block zero optimized routines.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/page.h>
+
+	/* Zero out 64 bytes of memory at (buf + offset).
+	 * Assumes %g1 contains zero.
+	 */
+#define BLAST_BLOCK(buf, offset) \
+	std	%g0, [buf + offset + 0x38]; \
+	std	%g0, [buf + offset + 0x30]; \
+	std	%g0, [buf + offset + 0x28]; \
+	std	%g0, [buf + offset + 0x20]; \
+	std	%g0, [buf + offset + 0x18]; \
+	std	%g0, [buf + offset + 0x10]; \
+	std	%g0, [buf + offset + 0x08]; \
+	std	%g0, [buf + offset + 0x00];
+
+	/* Copy 32 bytes of memory at (src + offset) to
+	 * (dst + offset).
+	 */
+#define MIRROR_BLOCK(dst, src, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
+	ldd	[src + offset + 0x18], t0; \
+	ldd	[src + offset + 0x10], t2; \
+	ldd	[src + offset + 0x08], t4; \
+	ldd	[src + offset + 0x00], t6; \
+	std	t0, [dst + offset + 0x18]; \
+	std	t2, [dst + offset + 0x10]; \
+	std	t4, [dst + offset + 0x08]; \
+	std	t6, [dst + offset + 0x00];
+
+	/* Profiling evidence indicates that memset() is
+	 * commonly called for blocks of size PAGE_SIZE,
+	 * and (2 * PAGE_SIZE) (for kernel stacks)
+	 * and with a second arg of zero.  We assume in
+	 * all of these cases that the buffer is aligned
+	 * on at least an 8 byte boundary.
+	 *
+	 * Therefore we special case them to make them
+	 * as fast as possible.
+	 */
+
+	.text
+	.align	4
+	.globl	bzero_1page, __copy_1page
+
+bzero_1page:
+/* NOTE: If you change the number of insns of this routine, please check
+ * arch/sparc/mm/hypersparc.S */
+	/* %o0 = buf */
+	or	%g0, %g0, %g1
+	or	%o0, %g0, %o1
+	or	%g0, (PAGE_SIZE >> 8), %g2
+1:
+	BLAST_BLOCK(%o0, 0x00)
+	BLAST_BLOCK(%o0, 0x40)
+	BLAST_BLOCK(%o0, 0x80)
+	BLAST_BLOCK(%o0, 0xc0)
+	subcc	%g2, 1, %g2
+	bne	1b
+	 add	%o0, 0x100, %o0
+
+	retl
+	 nop
+
+__copy_1page:
+/* NOTE: If you change the number of insns of this routine, please check
+ * arch/sparc/mm/hypersparc.S */
+	/* %o0 = dst, %o1 = src */
+	or	%g0, (PAGE_SIZE >> 8), %g1
+1:
+	MIRROR_BLOCK(%o0, %o1, 0x00, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5)
+	MIRROR_BLOCK(%o0, %o1, 0x20, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5)
+	MIRROR_BLOCK(%o0, %o1, 0x40, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5)
+	MIRROR_BLOCK(%o0, %o1, 0x60, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5)
+	MIRROR_BLOCK(%o0, %o1, 0x80, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5)
+	MIRROR_BLOCK(%o0, %o1, 0xa0, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5)
+	MIRROR_BLOCK(%o0, %o1, 0xc0, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5)
+	MIRROR_BLOCK(%o0, %o1, 0xe0, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5)
+	subcc	%g1, 1, %g1
+	add	%o0, 0x100, %o0
+	bne	1b
+	 add	%o1, 0x100, %o1
+
+	retl
+	 nop
diff --git a/arch/sparc/lib/checksum.S b/arch/sparc/lib/checksum.S
new file mode 100644
index 0000000..77f2285
--- /dev/null
+++ b/arch/sparc/lib/checksum.S
@@ -0,0 +1,583 @@
+/* checksum.S: Sparc optimized checksum code.
+ *
+ *  Copyright(C) 1995 Linus Torvalds
+ *  Copyright(C) 1995 Miguel de Icaza
+ *  Copyright(C) 1996 David S. Miller
+ *  Copyright(C) 1997 Jakub Jelinek
+ *
+ * derived from:
+ *	Linux/Alpha checksum c-code
+ *      Linux/ix86 inline checksum assembly
+ *      RFC1071 Computing the Internet Checksum (esp. Jacobsons m68k code)
+ *	David Mosberger-Tang for optimized reference c-code
+ *	BSD4.4 portable checksum routine
+ */
+
+#include <asm/errno.h>
+
+#define CSUM_BIGCHUNK(buf, offset, sum, t0, t1, t2, t3, t4, t5)	\
+	ldd	[buf + offset + 0x00], t0;			\
+	ldd	[buf + offset + 0x08], t2;			\
+	addxcc	t0, sum, sum;					\
+	addxcc	t1, sum, sum;					\
+	ldd	[buf + offset + 0x10], t4;			\
+	addxcc	t2, sum, sum;					\
+	addxcc	t3, sum, sum;					\
+	ldd	[buf + offset + 0x18], t0;			\
+	addxcc	t4, sum, sum;					\
+	addxcc	t5, sum, sum;					\
+	addxcc	t0, sum, sum;					\
+	addxcc	t1, sum, sum;
+
+#define CSUM_LASTCHUNK(buf, offset, sum, t0, t1, t2, t3)	\
+	ldd	[buf - offset - 0x08], t0;			\
+	ldd	[buf - offset - 0x00], t2;			\
+	addxcc	t0, sum, sum;					\
+	addxcc	t1, sum, sum;					\
+	addxcc	t2, sum, sum;					\
+	addxcc	t3, sum, sum;
+
+	/* Do end cruft out of band to get better cache patterns. */
+csum_partial_end_cruft:
+	be	1f				! caller asks %o1 & 0x8
+	 andcc	%o1, 4, %g0			! nope, check for word remaining
+	ldd	[%o0], %g2			! load two
+	addcc	%g2, %o2, %o2			! add first word to sum
+	addxcc	%g3, %o2, %o2			! add second word as well
+	add	%o0, 8, %o0			! advance buf ptr
+	addx	%g0, %o2, %o2			! add in final carry
+	andcc	%o1, 4, %g0			! check again for word remaining
+1:	be	1f				! nope, skip this code
+	 andcc	%o1, 3, %o1			! check for trailing bytes
+	ld	[%o0], %g2			! load it
+	addcc	%g2, %o2, %o2			! add to sum
+	add	%o0, 4, %o0			! advance buf ptr
+	addx	%g0, %o2, %o2			! add in final carry
+	andcc	%o1, 3, %g0			! check again for trailing bytes
+1:	be	1f				! no trailing bytes, return
+	 addcc	%o1, -1, %g0			! only one byte remains?
+	bne	2f				! at least two bytes more
+	 subcc	%o1, 2, %o1			! only two bytes more?
+	b	4f				! only one byte remains
+	 or	%g0, %g0, %o4			! clear fake hword value
+2:	lduh	[%o0], %o4			! get hword
+	be	6f				! jmp if only hword remains
+	 add	%o0, 2, %o0			! advance buf ptr either way
+	sll	%o4, 16, %o4			! create upper hword
+4:	ldub	[%o0], %o5			! get final byte
+	sll	%o5, 8, %o5			! put into place
+	or	%o5, %o4, %o4			! coalese with hword (if any)
+6:	addcc	%o4, %o2, %o2			! add to sum
+1:	retl					! get outta here
+	 addx	%g0, %o2, %o0			! add final carry into retval
+
+	/* Also do alignment out of band to get better cache patterns. */
+csum_partial_fix_alignment:
+	cmp	%o1, 6
+	bl	cpte - 0x4
+	 andcc	%o0, 0x2, %g0
+	be	1f
+	 andcc	%o0, 0x4, %g0
+	lduh	[%o0 + 0x00], %g2
+	sub	%o1, 2, %o1
+	add	%o0, 2, %o0
+	sll	%g2, 16, %g2
+	addcc	%g2, %o2, %o2
+	srl	%o2, 16, %g3
+	addx	%g0, %g3, %g2
+	sll	%o2, 16, %o2
+	sll	%g2, 16, %g3
+	srl	%o2, 16, %o2
+	andcc	%o0, 0x4, %g0
+	or	%g3, %o2, %o2
+1:	be	cpa
+	 andcc	%o1, 0xffffff80, %o3
+	ld	[%o0 + 0x00], %g2
+	sub	%o1, 4, %o1
+	addcc	%g2, %o2, %o2
+	add	%o0, 4, %o0
+	addx	%g0, %o2, %o2
+	b	cpa
+	 andcc	%o1, 0xffffff80, %o3
+
+	/* The common case is to get called with a nicely aligned
+	 * buffer of size 0x20.  Follow the code path for that case.
+	 */
+	.globl	csum_partial
+csum_partial:			/* %o0=buf, %o1=len, %o2=sum */
+	andcc	%o0, 0x7, %g0				! alignment problems?
+	bne	csum_partial_fix_alignment		! yep, handle it
+	 sethi	%hi(cpte - 8), %g7			! prepare table jmp ptr
+	andcc	%o1, 0xffffff80, %o3			! num loop iterations
+cpa:	be	3f					! none to do
+	 andcc	%o1, 0x70, %g1				! clears carry flag too
+5:	CSUM_BIGCHUNK(%o0, 0x00, %o2, %o4, %o5, %g2, %g3, %g4, %g5)
+	CSUM_BIGCHUNK(%o0, 0x20, %o2, %o4, %o5, %g2, %g3, %g4, %g5)
+	CSUM_BIGCHUNK(%o0, 0x40, %o2, %o4, %o5, %g2, %g3, %g4, %g5)
+	CSUM_BIGCHUNK(%o0, 0x60, %o2, %o4, %o5, %g2, %g3, %g4, %g5)
+	addx	%g0, %o2, %o2				! sink in final carry
+	subcc	%o3, 128, %o3				! detract from loop iters
+	bne	5b					! more to do
+	 add	%o0, 128, %o0				! advance buf ptr
+	andcc	%o1, 0x70, %g1				! clears carry flag too
+3:	be	cpte					! nope
+	 andcc	%o1, 0xf, %g0				! anything left at all?
+	srl	%g1, 1, %o4				! compute offset
+	sub	%g7, %g1, %g7				! adjust jmp ptr
+	sub	%g7, %o4, %g7				! final jmp ptr adjust
+	jmp	%g7 + %lo(cpte - 8)			! enter the table
+	 add	%o0, %g1, %o0				! advance buf ptr
+cptbl:	CSUM_LASTCHUNK(%o0, 0x68, %o2, %g2, %g3, %g4, %g5)
+	CSUM_LASTCHUNK(%o0, 0x58, %o2, %g2, %g3, %g4, %g5)
+	CSUM_LASTCHUNK(%o0, 0x48, %o2, %g2, %g3, %g4, %g5)
+	CSUM_LASTCHUNK(%o0, 0x38, %o2, %g2, %g3, %g4, %g5)
+	CSUM_LASTCHUNK(%o0, 0x28, %o2, %g2, %g3, %g4, %g5)
+	CSUM_LASTCHUNK(%o0, 0x18, %o2, %g2, %g3, %g4, %g5)
+	CSUM_LASTCHUNK(%o0, 0x08, %o2, %g2, %g3, %g4, %g5)
+	addx	%g0, %o2, %o2				! fetch final carry
+	andcc	%o1, 0xf, %g0				! anything left at all?
+cpte:	bne	csum_partial_end_cruft			! yep, handle it
+	 andcc	%o1, 8, %g0				! check how much
+cpout:	retl						! get outta here
+	 mov	%o2, %o0				! return computed csum
+
+	.globl __csum_partial_copy_start, __csum_partial_copy_end
+__csum_partial_copy_start:
+
+/* Work around cpp -rob */
+#define ALLOC #alloc
+#define EXECINSTR #execinstr
+#define EX(x,y,a,b)				\
+98:     x,y;                                    \
+        .section .fixup,ALLOC,EXECINSTR;	\
+        .align  4;                              \
+99:     ba 30f;                                 \
+         a, b, %o3;                             \
+        .section __ex_table,ALLOC;		\
+        .align  4;                              \
+        .word   98b, 99b;                       \
+        .text;                                  \
+        .align  4
+
+#define EX2(x,y)				\
+98:     x,y;                                    \
+        .section __ex_table,ALLOC;		\
+        .align  4;                              \
+        .word   98b, 30f;                       \
+        .text;                                  \
+        .align  4
+
+#define EX3(x,y)				\
+98:     x,y;                                    \
+        .section __ex_table,ALLOC;		\
+        .align  4;                              \
+        .word   98b, 96f;                       \
+        .text;                                  \
+        .align  4
+
+#define EXT(start,end,handler)			\
+        .section __ex_table,ALLOC;		\
+        .align  4;                              \
+        .word   start, 0, end, handler;         \
+        .text;                                  \
+        .align  4
+
+	/* This aligned version executes typically in 8.5 superscalar cycles, this
+	 * is the best I can do.  I say 8.5 because the final add will pair with
+	 * the next ldd in the main unrolled loop.  Thus the pipe is always full.
+	 * If you change these macros (including order of instructions),
+	 * please check the fixup code below as well.
+	 */
+#define CSUMCOPY_BIGCHUNK_ALIGNED(src, dst, sum, off, t0, t1, t2, t3, t4, t5, t6, t7)	\
+	ldd	[src + off + 0x00], t0;							\
+	ldd	[src + off + 0x08], t2;							\
+	addxcc	t0, sum, sum;								\
+	ldd	[src + off + 0x10], t4;							\
+	addxcc	t1, sum, sum;								\
+	ldd	[src + off + 0x18], t6;							\
+	addxcc	t2, sum, sum;								\
+	std	t0, [dst + off + 0x00];							\
+	addxcc	t3, sum, sum;								\
+	std	t2, [dst + off + 0x08];							\
+	addxcc	t4, sum, sum;								\
+	std	t4, [dst + off + 0x10];							\
+	addxcc	t5, sum, sum;								\
+	std	t6, [dst + off + 0x18];							\
+	addxcc	t6, sum, sum;								\
+	addxcc	t7, sum, sum;
+
+	/* 12 superscalar cycles seems to be the limit for this case,
+	 * because of this we thus do all the ldd's together to get
+	 * Viking MXCC into streaming mode.  Ho hum...
+	 */
+#define CSUMCOPY_BIGCHUNK(src, dst, sum, off, t0, t1, t2, t3, t4, t5, t6, t7)	\
+	ldd	[src + off + 0x00], t0;						\
+	ldd	[src + off + 0x08], t2;						\
+	ldd	[src + off + 0x10], t4;						\
+	ldd	[src + off + 0x18], t6;						\
+	st	t0, [dst + off + 0x00];						\
+	addxcc	t0, sum, sum;							\
+	st	t1, [dst + off + 0x04];						\
+	addxcc	t1, sum, sum;							\
+	st	t2, [dst + off + 0x08];						\
+	addxcc	t2, sum, sum;							\
+	st	t3, [dst + off + 0x0c];						\
+	addxcc	t3, sum, sum;							\
+	st	t4, [dst + off + 0x10];						\
+	addxcc	t4, sum, sum;							\
+	st	t5, [dst + off + 0x14];						\
+	addxcc	t5, sum, sum;							\
+	st	t6, [dst + off + 0x18];						\
+	addxcc	t6, sum, sum;							\
+	st	t7, [dst + off + 0x1c];						\
+	addxcc	t7, sum, sum;
+
+	/* Yuck, 6 superscalar cycles... */
+#define CSUMCOPY_LASTCHUNK(src, dst, sum, off, t0, t1, t2, t3)	\
+	ldd	[src - off - 0x08], t0;				\
+	ldd	[src - off - 0x00], t2;				\
+	addxcc	t0, sum, sum;					\
+	st	t0, [dst - off - 0x08];				\
+	addxcc	t1, sum, sum;					\
+	st	t1, [dst - off - 0x04];				\
+	addxcc	t2, sum, sum;					\
+	st	t2, [dst - off - 0x00];				\
+	addxcc	t3, sum, sum;					\
+	st	t3, [dst - off + 0x04];
+
+	/* Handle the end cruft code out of band for better cache patterns. */
+cc_end_cruft:
+	be	1f
+	 andcc	%o3, 4, %g0
+	EX(ldd	[%o0 + 0x00], %g2, and %o3, 0xf)
+	add	%o1, 8, %o1
+	addcc	%g2, %g7, %g7
+	add	%o0, 8, %o0
+	addxcc	%g3, %g7, %g7
+	EX2(st	%g2, [%o1 - 0x08])
+	addx	%g0, %g7, %g7
+	andcc	%o3, 4, %g0
+	EX2(st	%g3, [%o1 - 0x04])
+1:	be	1f
+	 andcc	%o3, 3, %o3
+	EX(ld	[%o0 + 0x00], %g2, add %o3, 4)
+	add	%o1, 4, %o1
+	addcc	%g2, %g7, %g7
+	EX2(st	%g2, [%o1 - 0x04])
+	addx	%g0, %g7, %g7
+	andcc	%o3, 3, %g0
+	add	%o0, 4, %o0
+1:	be	1f
+	 addcc	%o3, -1, %g0
+	bne	2f
+	 subcc	%o3, 2, %o3
+	b	4f
+	 or	%g0, %g0, %o4
+2:	EX(lduh	[%o0 + 0x00], %o4, add %o3, 2)
+	add	%o0, 2, %o0
+	EX2(sth	%o4, [%o1 + 0x00])
+	be	6f
+	 add	%o1, 2, %o1
+	sll	%o4, 16, %o4
+4:	EX(ldub	[%o0 + 0x00], %o5, add %g0, 1)
+	EX2(stb	%o5, [%o1 + 0x00])
+	sll	%o5, 8, %o5
+	or	%o5, %o4, %o4
+6:	addcc	%o4, %g7, %g7
+1:	retl
+	 addx	%g0, %g7, %o0
+
+	/* Also, handle the alignment code out of band. */
+cc_dword_align:
+	cmp	%g1, 6
+	bl,a	ccte
+	 andcc	%g1, 0xf, %o3
+	andcc	%o0, 0x1, %g0
+	bne	ccslow
+	 andcc	%o0, 0x2, %g0
+	be	1f
+	 andcc	%o0, 0x4, %g0
+	EX(lduh	[%o0 + 0x00], %g4, add %g1, 0)
+	sub	%g1, 2, %g1
+	EX2(sth	%g4, [%o1 + 0x00])
+	add	%o0, 2, %o0
+	sll	%g4, 16, %g4
+	addcc	%g4, %g7, %g7
+	add	%o1, 2, %o1
+	srl	%g7, 16, %g3
+	addx	%g0, %g3, %g4
+	sll	%g7, 16, %g7
+	sll	%g4, 16, %g3
+	srl	%g7, 16, %g7
+	andcc	%o0, 0x4, %g0
+	or	%g3, %g7, %g7
+1:	be	3f
+	 andcc	%g1, 0xffffff80, %g0
+	EX(ld	[%o0 + 0x00], %g4, add %g1, 0)
+	sub	%g1, 4, %g1
+	EX2(st	%g4, [%o1 + 0x00])
+	add	%o0, 4, %o0
+	addcc	%g4, %g7, %g7
+	add	%o1, 4, %o1
+	addx	%g0, %g7, %g7
+	b	3f
+	 andcc	%g1, 0xffffff80, %g0
+
+	/* Sun, you just can't beat me, you just can't.  Stop trying,
+	 * give up.  I'm serious, I am going to kick the living shit
+	 * out of you, game over, lights out.
+	 */
+	.align	8
+	.globl	__csum_partial_copy_sparc_generic
+__csum_partial_copy_sparc_generic:
+					/* %o0=src, %o1=dest, %g1=len, %g7=sum */
+	xor	%o0, %o1, %o4		! get changing bits
+	andcc	%o4, 3, %g0		! check for mismatched alignment
+	bne	ccslow			! better this than unaligned/fixups
+	 andcc	%o0, 7, %g0		! need to align things?
+	bne	cc_dword_align		! yes, we check for short lengths there
+	 andcc	%g1, 0xffffff80, %g0	! can we use unrolled loop?
+3:	be	3f			! nope, less than one loop remains
+	 andcc	%o1, 4, %g0		! dest aligned on 4 or 8 byte boundary?
+	be	ccdbl + 4		! 8 byte aligned, kick ass
+5:	CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x00,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+	CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x20,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+	CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x40,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+	CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x60,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+10:	EXT(5b, 10b, 20f)		! note for exception handling
+	sub	%g1, 128, %g1		! detract from length
+	addx	%g0, %g7, %g7		! add in last carry bit
+	andcc	%g1, 0xffffff80, %g0	! more to csum?
+	add	%o0, 128, %o0		! advance src ptr
+	bne	5b			! we did not go negative, continue looping
+	 add	%o1, 128, %o1		! advance dest ptr
+3:	andcc	%g1, 0x70, %o2		! can use table?
+ccmerge:be	ccte			! nope, go and check for end cruft
+	 andcc	%g1, 0xf, %o3		! get low bits of length (clears carry btw)
+	srl	%o2, 1, %o4		! begin negative offset computation
+	sethi	%hi(12f), %o5		! set up table ptr end
+	add	%o0, %o2, %o0		! advance src ptr
+	sub	%o5, %o4, %o5		! continue table calculation
+	sll	%o2, 1, %g2		! constant multiplies are fun...
+	sub	%o5, %g2, %o5		! some more adjustments
+	jmp	%o5 + %lo(12f)		! jump into it, duff style, wheee...
+	 add	%o1, %o2, %o1		! advance dest ptr (carry is clear btw)
+cctbl:	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x68,%g2,%g3,%g4,%g5)
+	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x58,%g2,%g3,%g4,%g5)
+	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x48,%g2,%g3,%g4,%g5)
+	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x38,%g2,%g3,%g4,%g5)
+	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x28,%g2,%g3,%g4,%g5)
+	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x18,%g2,%g3,%g4,%g5)
+	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x08,%g2,%g3,%g4,%g5)
+12:	EXT(cctbl, 12b, 22f)		! note for exception table handling
+	addx	%g0, %g7, %g7
+	andcc	%o3, 0xf, %g0		! check for low bits set
+ccte:	bne	cc_end_cruft		! something left, handle it out of band
+	 andcc	%o3, 8, %g0		! begin checks for that code
+	retl				! return
+	 mov	%g7, %o0		! give em the computed checksum
+ccdbl:	CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x00,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+	CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x20,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+	CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x40,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+	CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x60,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+11:	EXT(ccdbl, 11b, 21f)		! note for exception table handling
+	sub	%g1, 128, %g1		! detract from length
+	addx	%g0, %g7, %g7		! add in last carry bit
+	andcc	%g1, 0xffffff80, %g0	! more to csum?
+	add	%o0, 128, %o0		! advance src ptr
+	bne	ccdbl			! we did not go negative, continue looping
+	 add	%o1, 128, %o1		! advance dest ptr
+	b	ccmerge			! finish it off, above
+	 andcc	%g1, 0x70, %o2		! can use table? (clears carry btw)
+
+ccslow:	cmp	%g1, 0
+	mov	0, %g5
+	bleu	4f
+	 andcc	%o0, 1, %o5		
+	be,a	1f
+	 srl	%g1, 1, %g4		
+	sub	%g1, 1, %g1	
+	EX(ldub	[%o0], %g5, add %g1, 1)
+	add	%o0, 1, %o0	
+	EX2(stb	%g5, [%o1])
+	srl	%g1, 1, %g4
+	add	%o1, 1, %o1
+1:	cmp	%g4, 0		
+	be,a	3f
+	 andcc	%g1, 1, %g0
+	andcc	%o0, 2, %g0	
+	be,a	1f
+	 srl	%g4, 1, %g4
+	EX(lduh	[%o0], %o4, add %g1, 0)
+	sub	%g1, 2, %g1	
+	srl	%o4, 8, %g2
+	sub	%g4, 1, %g4	
+	EX2(stb	%g2, [%o1])
+	add	%o4, %g5, %g5
+	EX2(stb	%o4, [%o1 + 1])
+	add	%o0, 2, %o0	
+	srl	%g4, 1, %g4
+	add	%o1, 2, %o1
+1:	cmp	%g4, 0		
+	be,a	2f
+	 andcc	%g1, 2, %g0
+	EX3(ld	[%o0], %o4)
+5:	srl	%o4, 24, %g2
+	srl	%o4, 16, %g3
+	EX2(stb	%g2, [%o1])
+	srl	%o4, 8, %g2
+	EX2(stb	%g3, [%o1 + 1])
+	add	%o0, 4, %o0
+	EX2(stb	%g2, [%o1 + 2])
+	addcc	%o4, %g5, %g5
+	EX2(stb	%o4, [%o1 + 3])
+	addx	%g5, %g0, %g5	! I am now to lazy to optimize this (question it
+	add	%o1, 4, %o1	! is worthy). Maybe some day - with the sll/srl
+	subcc	%g4, 1, %g4	! tricks
+	bne,a	5b
+	 EX3(ld	[%o0], %o4)
+	sll	%g5, 16, %g2
+	srl	%g5, 16, %g5
+	srl	%g2, 16, %g2
+	andcc	%g1, 2, %g0
+	add	%g2, %g5, %g5 
+2:	be,a	3f		
+	 andcc	%g1, 1, %g0
+	EX(lduh	[%o0], %o4, and %g1, 3)
+	andcc	%g1, 1, %g0
+	srl	%o4, 8, %g2
+	add	%o0, 2, %o0	
+	EX2(stb	%g2, [%o1])
+	add	%g5, %o4, %g5
+	EX2(stb	%o4, [%o1 + 1])
+	add	%o1, 2, %o1
+3:	be,a	1f		
+	 sll	%g5, 16, %o4
+	EX(ldub	[%o0], %g2, add %g0, 1)
+	sll	%g2, 8, %o4	
+	EX2(stb	%g2, [%o1])
+	add	%g5, %o4, %g5
+	sll	%g5, 16, %o4
+1:	addcc	%o4, %g5, %g5
+	srl	%g5, 16, %o4
+	addx	%g0, %o4, %g5
+	orcc	%o5, %g0, %g0
+	be	4f
+	 srl	%g5, 8, %o4
+	and	%g5, 0xff, %g2
+	and	%o4, 0xff, %o4
+	sll	%g2, 8, %g2
+	or	%g2, %o4, %g5
+4:	addcc	%g7, %g5, %g7
+	retl	
+	 addx	%g0, %g7, %o0
+__csum_partial_copy_end:
+
+/* We do these strange calculations for the csum_*_from_user case only, ie.
+ * we only bother with faults on loads... */
+
+/* o2 = ((g2%20)&3)*8
+ * o3 = g1 - (g2/20)*32 - o2 */
+20:
+	cmp	%g2, 20
+	blu,a	1f
+	 and	%g2, 3, %o2
+	sub	%g1, 32, %g1
+	b	20b
+	 sub	%g2, 20, %g2
+1:
+	sll	%o2, 3, %o2
+	b	31f
+	 sub	%g1, %o2, %o3
+
+/* o2 = (!(g2 & 15) ? 0 : (((g2 & 15) + 1) & ~1)*8)
+ * o3 = g1 - (g2/16)*32 - o2 */
+21:
+	andcc	%g2, 15, %o3
+	srl	%g2, 4, %g2
+	be,a	1f
+	 clr	%o2
+	add	%o3, 1, %o3
+	and	%o3, 14, %o3
+	sll	%o3, 3, %o2
+1:
+	sll	%g2, 5, %g2
+	sub	%g1, %g2, %o3
+	b	31f
+	 sub	%o3, %o2, %o3
+
+/* o0 += (g2/10)*16 - 0x70
+ * 01 += (g2/10)*16 - 0x70
+ * o2 = (g2 % 10) ? 8 : 0
+ * o3 += 0x70 - (g2/10)*16 - o2 */
+22:
+	cmp	%g2, 10
+	blu,a	1f
+	 sub	%o0, 0x70, %o0
+	add	%o0, 16, %o0
+	add	%o1, 16, %o1
+	sub	%o3, 16, %o3
+	b	22b
+	 sub	%g2, 10, %g2
+1:
+	sub	%o1, 0x70, %o1
+	add	%o3, 0x70, %o3
+	clr	%o2
+	tst	%g2
+	bne,a	1f
+	 mov	8, %o2
+1:
+	b	31f
+	 sub	%o3, %o2, %o3
+96:
+	and	%g1, 3, %g1
+	sll	%g4, 2, %g4
+	add	%g1, %g4, %o3
+30:
+/* %o1 is dst
+ * %o3 is # bytes to zero out
+ * %o4 is faulting address
+ * %o5 is %pc where fault occurred */
+	clr	%o2
+31:
+/* %o0 is src
+ * %o1 is dst
+ * %o2 is # of bytes to copy from src to dst
+ * %o3 is # bytes to zero out
+ * %o4 is faulting address
+ * %o5 is %pc where fault occurred */
+	save	%sp, -104, %sp
+        mov     %i5, %o0
+        mov     %i7, %o1
+        mov	%i4, %o2
+        call    lookup_fault
+	 mov	%g7, %i4
+	cmp	%o0, 2
+	bne	1f	
+	 add	%g0, -EFAULT, %i5
+	tst	%i2
+	be	2f
+	 mov	%i0, %o1
+	mov	%i1, %o0
+5:
+	call	__memcpy
+	 mov	%i2, %o2
+	tst	%o0
+	bne,a	2f
+	 add	%i3, %i2, %i3
+	add	%i1, %i2, %i1
+2:
+	mov	%i1, %o0
+6:
+	call	__bzero
+	 mov	%i3, %o1
+1:
+	ld	[%sp + 168], %o2		! struct_ptr of parent
+	st	%i5, [%o2]
+	ret
+	 restore
+
+        .section __ex_table,#alloc
+        .align 4
+        .word 5b,2
+	.word 6b,2
diff --git a/arch/sparc/lib/copy_user.S b/arch/sparc/lib/copy_user.S
new file mode 100644
index 0000000..577505b
--- /dev/null
+++ b/arch/sparc/lib/copy_user.S
@@ -0,0 +1,492 @@
+/* copy_user.S: Sparc optimized copy_from_user and copy_to_user code.
+ *
+ *  Copyright(C) 1995 Linus Torvalds
+ *  Copyright(C) 1996 David S. Miller
+ *  Copyright(C) 1996 Eddie C. Dost
+ *  Copyright(C) 1996,1998 Jakub Jelinek
+ *
+ * derived from:
+ *	e-mail between David and Eddie.
+ *
+ * Returns 0 if successful, otherwise count of bytes not copied yet
+ */
+
+#include <asm/ptrace.h>
+#include <asm/asmmacro.h>
+#include <asm/page.h>
+
+/* Work around cpp -rob */
+#define ALLOC #alloc
+#define EXECINSTR #execinstr
+#define EX(x,y,a,b) 				\
+98: 	x,y;					\
+	.section .fixup,ALLOC,EXECINSTR;	\
+	.align	4;				\
+99:	ba fixupretl;				\
+	 a, b, %g3;				\
+	.section __ex_table,ALLOC;		\
+	.align	4;				\
+	.word	98b, 99b;			\
+	.text;					\
+	.align	4
+
+#define EX2(x,y,c,d,e,a,b) 			\
+98: 	x,y;					\
+	.section .fixup,ALLOC,EXECINSTR;	\
+	.align	4;				\
+99:	c, d, e;				\
+	ba fixupretl;				\
+	 a, b, %g3;				\
+	.section __ex_table,ALLOC;		\
+	.align	4;				\
+	.word	98b, 99b;			\
+	.text;					\
+	.align	4
+
+#define EXO2(x,y) 				\
+98: 	x, y;					\
+	.section __ex_table,ALLOC;		\
+	.align	4;				\
+	.word	98b, 97f;			\
+	.text;					\
+	.align	4
+
+#define EXT(start,end,handler)			\
+	.section __ex_table,ALLOC;		\
+	.align	4;				\
+	.word	start, 0, end, handler;		\
+	.text;					\
+	.align	4
+
+/* Please do not change following macros unless you change logic used
+ * in .fixup at the end of this file as well
+ */
+
+/* Both these macros have to start with exactly the same insn */
+#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
+	ldd	[%src + (offset) + 0x00], %t0; \
+	ldd	[%src + (offset) + 0x08], %t2; \
+	ldd	[%src + (offset) + 0x10], %t4; \
+	ldd	[%src + (offset) + 0x18], %t6; \
+	st	%t0, [%dst + (offset) + 0x00]; \
+	st	%t1, [%dst + (offset) + 0x04]; \
+	st	%t2, [%dst + (offset) + 0x08]; \
+	st	%t3, [%dst + (offset) + 0x0c]; \
+	st	%t4, [%dst + (offset) + 0x10]; \
+	st	%t5, [%dst + (offset) + 0x14]; \
+	st	%t6, [%dst + (offset) + 0x18]; \
+	st	%t7, [%dst + (offset) + 0x1c];
+
+#define MOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
+	ldd	[%src + (offset) + 0x00], %t0; \
+	ldd	[%src + (offset) + 0x08], %t2; \
+	ldd	[%src + (offset) + 0x10], %t4; \
+	ldd	[%src + (offset) + 0x18], %t6; \
+	std	%t0, [%dst + (offset) + 0x00]; \
+	std	%t2, [%dst + (offset) + 0x08]; \
+	std	%t4, [%dst + (offset) + 0x10]; \
+	std	%t6, [%dst + (offset) + 0x18];
+
+#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \
+	ldd	[%src - (offset) - 0x10], %t0; \
+	ldd	[%src - (offset) - 0x08], %t2; \
+	st	%t0, [%dst - (offset) - 0x10]; \
+	st	%t1, [%dst - (offset) - 0x0c]; \
+	st	%t2, [%dst - (offset) - 0x08]; \
+	st	%t3, [%dst - (offset) - 0x04];
+
+#define MOVE_HALFCHUNK(src, dst, offset, t0, t1, t2, t3) \
+	lduh	[%src + (offset) + 0x00], %t0; \
+	lduh	[%src + (offset) + 0x02], %t1; \
+	lduh	[%src + (offset) + 0x04], %t2; \
+	lduh	[%src + (offset) + 0x06], %t3; \
+	sth	%t0, [%dst + (offset) + 0x00]; \
+	sth	%t1, [%dst + (offset) + 0x02]; \
+	sth	%t2, [%dst + (offset) + 0x04]; \
+	sth	%t3, [%dst + (offset) + 0x06];
+
+#define MOVE_SHORTCHUNK(src, dst, offset, t0, t1) \
+	ldub	[%src - (offset) - 0x02], %t0; \
+	ldub	[%src - (offset) - 0x01], %t1; \
+	stb	%t0, [%dst - (offset) - 0x02]; \
+	stb	%t1, [%dst - (offset) - 0x01];
+
+	.text
+	.align	4
+
+	.globl  __copy_user_begin
+__copy_user_begin:
+
+	.globl	__copy_user
+dword_align:
+	andcc	%o1, 1, %g0
+	be	4f
+	 andcc	%o1, 2, %g0
+
+	EXO2(ldub [%o1], %g2)
+	add	%o1, 1, %o1
+	EXO2(stb %g2, [%o0])
+	sub	%o2, 1, %o2
+	bne	3f
+	 add	%o0, 1, %o0
+
+	EXO2(lduh [%o1], %g2)
+	add	%o1, 2, %o1
+	EXO2(sth %g2, [%o0])
+	sub	%o2, 2, %o2
+	b	3f
+	 add	%o0, 2, %o0
+4:
+	EXO2(lduh [%o1], %g2)
+	add	%o1, 2, %o1
+	EXO2(sth %g2, [%o0])
+	sub	%o2, 2, %o2
+	b	3f
+	 add	%o0, 2, %o0
+
+__copy_user:	/* %o0=dst %o1=src %o2=len */
+	xor	%o0, %o1, %o4
+1:
+	andcc	%o4, 3, %o5
+2:
+	bne	cannot_optimize
+	 cmp	%o2, 15
+
+	bleu	short_aligned_end
+	 andcc	%o1, 3, %g0
+
+	bne	dword_align
+3:
+	 andcc	%o1, 4, %g0
+
+	be	2f
+	 mov	%o2, %g1
+
+	EXO2(ld [%o1], %o4)
+	sub	%g1, 4, %g1
+	EXO2(st %o4, [%o0])
+	add	%o1, 4, %o1
+	add	%o0, 4, %o0
+2:
+	andcc	%g1, 0xffffff80, %g7
+	be	3f
+	 andcc	%o0, 4, %g0
+
+	be	ldd_std + 4
+5:
+	MOVE_BIGCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5)
+80:
+	EXT(5b, 80b, 50f)
+	subcc	%g7, 128, %g7
+	add	%o1, 128, %o1
+	bne	5b
+	 add	%o0, 128, %o0
+3:
+	andcc	%g1, 0x70, %g7
+	be	copy_user_table_end
+	 andcc	%g1, 8, %g0
+
+	sethi	%hi(copy_user_table_end), %o5
+	srl	%g7, 1, %o4
+	add	%g7, %o4, %o4
+	add	%o1, %g7, %o1
+	sub	%o5, %o4, %o5
+	jmpl	%o5 + %lo(copy_user_table_end), %g0
+	 add	%o0, %g7, %o0
+
+copy_user_table:
+	MOVE_LASTCHUNK(o1, o0, 0x60, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x50, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x40, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x30, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x20, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x10, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x00, g2, g3, g4, g5)
+copy_user_table_end:
+	EXT(copy_user_table, copy_user_table_end, 51f)
+	be	copy_user_last7
+	 andcc	%g1, 4, %g0
+
+	EX(ldd	[%o1], %g2, and %g1, 0xf)
+	add	%o0, 8, %o0
+	add	%o1, 8, %o1
+	EX(st	%g2, [%o0 - 0x08], and %g1, 0xf)
+	EX2(st	%g3, [%o0 - 0x04], and %g1, 0xf, %g1, sub %g1, 4)
+copy_user_last7:
+	be	1f
+	 andcc	%g1, 2, %g0
+
+	EX(ld	[%o1], %g2, and %g1, 7)
+	add	%o1, 4, %o1
+	EX(st	%g2, [%o0], and %g1, 7)
+	add	%o0, 4, %o0
+1:
+	be	1f
+	 andcc	%g1, 1, %g0
+
+	EX(lduh	[%o1], %g2, and %g1, 3)
+	add	%o1, 2, %o1
+	EX(sth	%g2, [%o0], and %g1, 3)
+	add	%o0, 2, %o0
+1:
+	be	1f
+	 nop
+
+	EX(ldub	[%o1], %g2, add %g0, 1)
+	EX(stb	%g2, [%o0], add %g0, 1)
+1:
+	retl
+ 	 clr	%o0
+
+ldd_std:
+	MOVE_BIGALIGNCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGALIGNCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGALIGNCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGALIGNCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5)
+81:
+	EXT(ldd_std, 81b, 52f)
+	subcc	%g7, 128, %g7
+	add	%o1, 128, %o1
+	bne	ldd_std
+	 add	%o0, 128, %o0
+
+	andcc	%g1, 0x70, %g7
+	be	copy_user_table_end
+	 andcc	%g1, 8, %g0
+
+	sethi	%hi(copy_user_table_end), %o5
+	srl	%g7, 1, %o4
+	add	%g7, %o4, %o4
+	add	%o1, %g7, %o1
+	sub	%o5, %o4, %o5
+	jmpl	%o5 + %lo(copy_user_table_end), %g0
+	 add	%o0, %g7, %o0
+
+cannot_optimize:
+	bleu	short_end
+	 cmp	%o5, 2
+
+	bne	byte_chunk
+	 and	%o2, 0xfffffff0, %o3
+	 
+	andcc	%o1, 1, %g0
+	be	10f
+	 nop
+
+	EXO2(ldub [%o1], %g2)
+	add	%o1, 1, %o1
+	EXO2(stb %g2, [%o0])
+	sub	%o2, 1, %o2
+	andcc	%o2, 0xfffffff0, %o3
+	be	short_end
+	 add	%o0, 1, %o0
+10:
+	MOVE_HALFCHUNK(o1, o0, 0x00, g2, g3, g4, g5)
+	MOVE_HALFCHUNK(o1, o0, 0x08, g2, g3, g4, g5)
+82:
+	EXT(10b, 82b, 53f)
+	subcc	%o3, 0x10, %o3
+	add	%o1, 0x10, %o1
+	bne	10b
+	 add	%o0, 0x10, %o0
+	b	2f
+	 and	%o2, 0xe, %o3
+	
+byte_chunk:
+	MOVE_SHORTCHUNK(o1, o0, -0x02, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, -0x04, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, -0x06, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, -0x08, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, -0x0a, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, -0x0c, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, -0x0e, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, -0x10, g2, g3)
+83:
+	EXT(byte_chunk, 83b, 54f)
+	subcc	%o3, 0x10, %o3
+	add	%o1, 0x10, %o1
+	bne	byte_chunk
+	 add	%o0, 0x10, %o0
+
+short_end:
+	and	%o2, 0xe, %o3
+2:
+	sethi	%hi(short_table_end), %o5
+	sll	%o3, 3, %o4
+	add	%o0, %o3, %o0
+	sub	%o5, %o4, %o5
+	add	%o1, %o3, %o1
+	jmpl	%o5 + %lo(short_table_end), %g0
+	 andcc	%o2, 1, %g0
+84:
+	MOVE_SHORTCHUNK(o1, o0, 0x0c, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x0a, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x08, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x06, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x04, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x02, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x00, g2, g3)
+short_table_end:
+	EXT(84b, short_table_end, 55f)
+	be	1f
+	 nop
+	EX(ldub	[%o1], %g2, add %g0, 1)
+	EX(stb	%g2, [%o0], add %g0, 1)
+1:
+	retl
+ 	 clr	%o0
+
+short_aligned_end:
+	bne	short_end
+	 andcc	%o2, 8, %g0
+
+	be	1f
+	 andcc	%o2, 4, %g0
+
+	EXO2(ld	[%o1 + 0x00], %g2)
+	EXO2(ld	[%o1 + 0x04], %g3)
+	add	%o1, 8, %o1
+	EXO2(st	%g2, [%o0 + 0x00])
+	EX(st	%g3, [%o0 + 0x04], sub %o2, 4)
+	add	%o0, 8, %o0
+1:
+	b	copy_user_last7
+	 mov	%o2, %g1
+
+	.section .fixup,#alloc,#execinstr
+	.align	4
+97:
+	mov	%o2, %g3
+fixupretl:
+	sethi   %hi(PAGE_OFFSET), %g1
+	cmp	%o0, %g1
+	blu	1f
+	 cmp	%o1, %g1
+	bgeu	1f
+	 nop
+	save	%sp, -64, %sp
+	mov	%i0, %o0
+	call	__bzero
+	 mov	%g3, %o1
+	restore
+1:	retl
+	 mov	%g3, %o0
+
+/* exception routine sets %g2 to (broken_insn - first_insn)>>2 */
+50:
+/* This magic counts how many bytes are left when crash in MOVE_BIGCHUNK
+ * happens. This is derived from the amount ldd reads, st stores, etc.
+ * x = g2 % 12;
+ * g3 = g1 + g7 - ((g2 / 12) * 32 + (x < 4) ? 0 : (x - 4) * 4);
+ * o0 += (g2 / 12) * 32;
+ */
+	cmp	%g2, 12
+	add	%o0, %g7, %o0
+	bcs	1f
+	 cmp	%g2, 24
+	bcs	2f
+	 cmp	%g2, 36
+	bcs	3f
+	 nop
+	sub	%g2, 12, %g2
+	sub	%g7, 32, %g7
+3:	sub	%g2, 12, %g2
+	sub	%g7, 32, %g7
+2:	sub	%g2, 12, %g2
+	sub	%g7, 32, %g7
+1:	cmp	%g2, 4
+	bcs,a	60f
+	 clr	%g2
+	sub	%g2, 4, %g2
+	sll	%g2, 2, %g2
+60:	and	%g1, 0x7f, %g3
+	sub	%o0, %g7, %o0
+	add	%g3, %g7, %g3
+	ba	fixupretl
+	 sub	%g3, %g2, %g3
+51:
+/* i = 41 - g2; j = i % 6;
+ * g3 = (g1 & 15) + (i / 6) * 16 + (j < 4) ? (j + 1) * 4 : 16;
+ * o0 -= (i / 6) * 16 + 16;
+ */
+	neg	%g2
+	and	%g1, 0xf, %g1
+	add	%g2, 41, %g2
+	add	%o0, %g1, %o0
+1:	cmp	%g2, 6
+	bcs,a	2f
+	 cmp	%g2, 4
+	add	%g1, 16, %g1
+	b	1b
+	 sub	%g2, 6, %g2
+2:	bcc,a	2f
+	 mov	16, %g2
+	inc	%g2
+	sll	%g2, 2, %g2
+2:	add	%g1, %g2, %g3
+	ba	fixupretl
+	 sub	%o0, %g3, %o0
+52:
+/* g3 = g1 + g7 - (g2 / 8) * 32 + (g2 & 4) ? (g2 & 3) * 8 : 0;
+   o0 += (g2 / 8) * 32 */
+	andn	%g2, 7, %g4
+	add	%o0, %g7, %o0
+	andcc	%g2, 4, %g0
+	and	%g2, 3, %g2
+	sll	%g4, 2, %g4
+	sll	%g2, 3, %g2
+	bne	60b
+	 sub	%g7, %g4, %g7
+	ba	60b
+	 clr	%g2
+53:
+/* g3 = o3 + (o2 & 15) - (g2 & 8) - (g2 & 4) ? (g2 & 3) * 2 : 0;
+   o0 += (g2 & 8) */
+	and	%g2, 3, %g4
+	andcc	%g2, 4, %g0
+	and	%g2, 8, %g2
+	sll	%g4, 1, %g4
+	be	1f
+	 add	%o0, %g2, %o0
+	add	%g2, %g4, %g2
+1:	and	%o2, 0xf, %g3
+	add	%g3, %o3, %g3
+	ba	fixupretl
+	 sub	%g3, %g2, %g3
+54:
+/* g3 = o3 + (o2 & 15) - (g2 / 4) * 2 - (g2 & 2) ? (g2 & 1) : 0;
+   o0 += (g2 / 4) * 2 */
+	srl	%g2, 2, %o4
+	and	%g2, 1, %o5
+	srl	%g2, 1, %g2
+	add	%o4, %o4, %o4
+	and	%o5, %g2, %o5
+	and	%o2, 0xf, %o2
+	add	%o0, %o4, %o0
+	sub	%o3, %o5, %o3
+	sub	%o2, %o4, %o2
+	ba	fixupretl
+	 add	%o2, %o3, %g3
+55:
+/* i = 27 - g2;
+   g3 = (o2 & 1) + i / 4 * 2 + !(i & 3);
+   o0 -= i / 4 * 2 + 1 */
+	neg	%g2
+	and	%o2, 1, %o2
+	add	%g2, 27, %g2
+	srl	%g2, 2, %o5
+	andcc	%g2, 3, %g0
+	mov	1, %g2
+	add	%o5, %o5, %o5
+	be,a	1f
+	 clr	%g2
+1:	add	%g2, %o5, %g3
+	sub	%o0, %g3, %o0
+	ba	fixupretl
+	 add	%g3, %o2, %g3
+
+	.globl  __copy_user_end
+__copy_user_end:
diff --git a/arch/sparc/lib/debuglocks.c b/arch/sparc/lib/debuglocks.c
new file mode 100644
index 0000000..fb18235
--- /dev/null
+++ b/arch/sparc/lib/debuglocks.c
@@ -0,0 +1,202 @@
+/* $Id: debuglocks.c,v 1.11 2001/09/20 00:35:31 davem Exp $
+ * debuglocks.c: Debugging versions of SMP locking primitives.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998-99 Anton Blanchard (anton@progsoc.uts.edu.au)
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/threads.h>	/* For NR_CPUS */
+#include <linux/spinlock.h>
+#include <asm/psr.h>
+#include <asm/system.h>
+
+#ifdef CONFIG_SMP
+
+/* Some notes on how these debugging routines work.  When a lock is acquired
+ * an extra debugging member lock->owner_pc is set to the caller of the lock
+ * acquisition routine.  Right before releasing a lock, the debugging program
+ * counter is cleared to zero.
+ *
+ * Furthermore, since PC's are 4 byte aligned on Sparc, we stuff the CPU
+ * number of the owner in the lowest two bits.
+ */
+
+#define STORE_CALLER(A) __asm__ __volatile__("mov %%i7, %0" : "=r" (A));
+
+static inline void show(char *str, spinlock_t *lock, unsigned long caller)
+{
+	int cpu = smp_processor_id();
+
+	printk("%s(%p) CPU#%d stuck at %08lx, owner PC(%08lx):CPU(%lx)\n",str,
+		lock, cpu, caller, lock->owner_pc & ~3, lock->owner_pc & 3);
+}
+
+static inline void show_read(char *str, rwlock_t *lock, unsigned long caller)
+{
+	int cpu = smp_processor_id();
+
+	printk("%s(%p) CPU#%d stuck at %08lx, owner PC(%08lx):CPU(%lx)\n", str,
+		lock, cpu, caller, lock->owner_pc & ~3, lock->owner_pc & 3);
+}
+
+static inline void show_write(char *str, rwlock_t *lock, unsigned long caller)
+{
+	int cpu = smp_processor_id();
+	int i;
+
+	printk("%s(%p) CPU#%d stuck at %08lx, owner PC(%08lx):CPU(%lx)", str,
+		lock, cpu, caller, lock->owner_pc & ~3, lock->owner_pc & 3);
+
+	for(i = 0; i < NR_CPUS; i++)
+		printk(" reader[%d]=%08lx", i, lock->reader_pc[i]);
+
+	printk("\n");
+}
+
+#undef INIT_STUCK
+#define INIT_STUCK 100000000
+
+void _do_spin_lock(spinlock_t *lock, char *str)
+{
+	unsigned long caller;
+	unsigned long val;
+	int cpu = smp_processor_id();
+	int stuck = INIT_STUCK;
+
+	STORE_CALLER(caller);
+
+again:
+	__asm__ __volatile__("ldstub [%1], %0" : "=r" (val) : "r" (&(lock->lock)));
+	if(val) {
+		while(lock->lock) {
+			if (!--stuck) {
+				show(str, lock, caller);
+				stuck = INIT_STUCK;
+			}
+			barrier();
+		}
+		goto again;
+	}
+	lock->owner_pc = (cpu & 3) | (caller & ~3);
+}
+
+int _spin_trylock(spinlock_t *lock)
+{
+	unsigned long val;
+	unsigned long caller;
+	int cpu = smp_processor_id();
+
+	STORE_CALLER(caller);
+
+	__asm__ __volatile__("ldstub [%1], %0" : "=r" (val) : "r" (&(lock->lock)));
+	if(!val) {
+		/* We got it, record our identity for debugging. */
+		lock->owner_pc = (cpu & 3) | (caller & ~3);
+	}
+	return val == 0;
+}
+
+void _do_spin_unlock(spinlock_t *lock)
+{
+	lock->owner_pc = 0;
+	barrier();
+	lock->lock = 0;
+}
+
+void _do_read_lock(rwlock_t *rw, char *str)
+{
+	unsigned long caller;
+	unsigned long val;
+	int cpu = smp_processor_id();
+	int stuck = INIT_STUCK;
+
+	STORE_CALLER(caller);
+
+wlock_again:
+	__asm__ __volatile__("ldstub [%1 + 3], %0" : "=r" (val) : "r" (&(rw->lock)));
+	if(val) {
+		while(rw->lock & 0xff) {
+			if (!--stuck) {
+				show_read(str, rw, caller);
+				stuck = INIT_STUCK;
+			}
+			barrier();
+		}
+		goto wlock_again;
+	}
+
+	rw->reader_pc[cpu] = caller;
+	barrier();
+	rw->lock++;
+}
+
+void _do_read_unlock(rwlock_t *rw, char *str)
+{
+	unsigned long caller;
+	unsigned long val;
+	int cpu = smp_processor_id();
+	int stuck = INIT_STUCK;
+
+	STORE_CALLER(caller);
+
+wlock_again:
+	__asm__ __volatile__("ldstub [%1 + 3], %0" : "=r" (val) : "r" (&(rw->lock)));
+	if(val) {
+		while(rw->lock & 0xff) {
+			if (!--stuck) {
+				show_read(str, rw, caller);
+				stuck = INIT_STUCK;
+			}
+			barrier();
+		}
+		goto wlock_again;
+	}
+
+	rw->reader_pc[cpu] = 0;
+	barrier();
+	rw->lock -= 0x1ff;
+}
+
+void _do_write_lock(rwlock_t *rw, char *str)
+{
+	unsigned long caller;
+	unsigned long val;
+	int cpu = smp_processor_id();
+	int stuck = INIT_STUCK;
+
+	STORE_CALLER(caller);
+
+wlock_again:
+	__asm__ __volatile__("ldstub [%1 + 3], %0" : "=r" (val) : "r" (&(rw->lock)));
+	if(val) {
+wlock_wait:
+		while(rw->lock) {
+			if (!--stuck) {
+				show_write(str, rw, caller);
+				stuck = INIT_STUCK;
+			}
+			barrier();
+		}
+		goto wlock_again;
+	}
+
+	if (rw->lock & ~0xff) {
+		*(((unsigned char *)&rw->lock)+3) = 0;
+		barrier();
+		goto wlock_wait;
+	}
+
+	barrier();
+	rw->owner_pc = (cpu & 3) | (caller & ~3);
+}
+
+void _do_write_unlock(rwlock_t *rw)
+{
+	rw->owner_pc = 0;
+	barrier();
+	rw->lock = 0;
+}
+
+#endif /* SMP */
diff --git a/arch/sparc/lib/divdi3.S b/arch/sparc/lib/divdi3.S
new file mode 100644
index 0000000..681b368
--- /dev/null
+++ b/arch/sparc/lib/divdi3.S
@@ -0,0 +1,295 @@
+/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC 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, or (at your option)
+any later version.
+
+GNU CC 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 GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+	.data
+	.align 8
+	.globl	__clz_tab
+__clz_tab:
+	.byte	0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5
+	.byte	6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6
+	.byte	7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
+	.byte	7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
+	.byte	8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
+	.byte	8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
+	.byte	8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
+	.byte	8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
+	.size	 __clz_tab,256
+	.global .udiv
+
+	.text
+	.align 4
+	.globl __divdi3
+__divdi3:
+	save %sp,-104,%sp
+	cmp %i0,0
+	bge .LL40
+	mov 0,%l4
+	mov -1,%l4
+	sub %g0,%i1,%o0
+	mov %o0,%o5
+	subcc %g0,%o0,%g0
+	sub %g0,%i0,%o0
+	subx %o0,0,%o4
+	mov %o4,%i0
+	mov %o5,%i1
+.LL40:
+	cmp %i2,0
+	bge .LL84
+	mov %i3,%o4
+	xnor %g0,%l4,%l4
+	sub %g0,%i3,%o0
+	mov %o0,%o3
+	subcc %g0,%o0,%g0
+	sub %g0,%i2,%o0
+	subx %o0,0,%o2
+	mov %o2,%i2
+	mov %o3,%i3
+	mov %i3,%o4
+.LL84:
+	cmp %i2,0
+	bne .LL45
+	mov %i1,%i3
+	cmp %o4,%i0
+	bleu .LL46
+	mov %i3,%o1
+	mov	32,%g1
+	subcc	%i0,%o4,%g0
+1:	bcs	5f
+	 addxcc %o1,%o1,%o1	! shift n1n0 and a q-bit in lsb
+	sub	%i0,%o4,%i0	! this kills msb of n
+	addx	%i0,%i0,%i0	! so this cannot give carry
+	subcc	%g1,1,%g1
+2:	bne	1b
+	 subcc	%i0,%o4,%g0
+	bcs	3f
+	 addxcc %o1,%o1,%o1	! shift n1n0 and a q-bit in lsb
+	b	3f
+	 sub	%i0,%o4,%i0	! this kills msb of n
+4:	sub	%i0,%o4,%i0
+5:	addxcc	%i0,%i0,%i0
+	bcc	2b
+	 subcc	%g1,1,%g1
+! Got carry from n.  Subtract next step to cancel this carry.
+	bne	4b
+	 addcc	%o1,%o1,%o1	! shift n1n0 and a 0-bit in lsb
+	sub	%i0,%o4,%i0
+3:	xnor	%o1,0,%o1
+	b .LL50
+	mov 0,%o2
+.LL46:
+	cmp %o4,0
+	bne .LL85
+	mov %i0,%o2
+	mov 1,%o0
+	call .udiv,0
+	mov 0,%o1
+	mov %o0,%o4
+	mov %i0,%o2
+.LL85:
+	mov 0,%g3
+	mov	32,%g1
+	subcc	%g3,%o4,%g0
+1:	bcs	5f
+	 addxcc %o2,%o2,%o2	! shift n1n0 and a q-bit in lsb
+	sub	%g3,%o4,%g3	! this kills msb of n
+	addx	%g3,%g3,%g3	! so this cannot give carry
+	subcc	%g1,1,%g1
+2:	bne	1b
+	 subcc	%g3,%o4,%g0
+	bcs	3f
+	 addxcc %o2,%o2,%o2	! shift n1n0 and a q-bit in lsb
+	b	3f
+	 sub	%g3,%o4,%g3	! this kills msb of n
+4:	sub	%g3,%o4,%g3
+5:	addxcc	%g3,%g3,%g3
+	bcc	2b
+	 subcc	%g1,1,%g1
+! Got carry from n.  Subtract next step to cancel this carry.
+	bne	4b
+	 addcc	%o2,%o2,%o2	! shift n1n0 and a 0-bit in lsb
+	sub	%g3,%o4,%g3
+3:	xnor	%o2,0,%o2
+	mov %g3,%i0
+	mov %i3,%o1
+	mov	32,%g1
+	subcc	%i0,%o4,%g0
+1:	bcs	5f
+	 addxcc %o1,%o1,%o1	! shift n1n0 and a q-bit in lsb
+	sub	%i0,%o4,%i0	! this kills msb of n
+	addx	%i0,%i0,%i0	! so this cannot give carry
+	subcc	%g1,1,%g1
+2:	bne	1b
+	 subcc	%i0,%o4,%g0
+	bcs	3f
+	 addxcc %o1,%o1,%o1	! shift n1n0 and a q-bit in lsb
+	b	3f
+	 sub	%i0,%o4,%i0	! this kills msb of n
+4:	sub	%i0,%o4,%i0
+5:	addxcc	%i0,%i0,%i0
+	bcc	2b
+	 subcc	%g1,1,%g1
+! Got carry from n.  Subtract next step to cancel this carry.
+	bne	4b
+	 addcc	%o1,%o1,%o1	! shift n1n0 and a 0-bit in lsb
+	sub	%i0,%o4,%i0
+3:	xnor	%o1,0,%o1
+	b .LL86
+	mov %o1,%l1
+.LL45:
+	cmp %i2,%i0
+	bleu .LL51
+	sethi %hi(65535),%o0
+	b .LL78
+	mov 0,%o1
+.LL51:
+	or %o0,%lo(65535),%o0
+	cmp %i2,%o0
+	bgu .LL58
+	mov %i2,%o1
+	cmp %i2,256
+	addx %g0,-1,%o0
+	b .LL64
+	and %o0,8,%o2
+.LL58:
+	sethi %hi(16777215),%o0
+	or %o0,%lo(16777215),%o0
+	cmp %i2,%o0
+	bgu .LL64
+	mov 24,%o2
+	mov 16,%o2
+.LL64:
+	srl %o1,%o2,%o0
+	sethi %hi(__clz_tab),%o1
+	or %o1,%lo(__clz_tab),%o1
+	ldub [%o0+%o1],%o0
+	add %o0,%o2,%o0
+	mov 32,%o1
+	subcc %o1,%o0,%o3
+	bne,a .LL72
+	sub %o1,%o3,%o1
+	cmp %i0,%i2
+	bgu .LL74
+	cmp %i3,%o4
+	blu .LL78
+	mov 0,%o1
+.LL74:
+	b .LL78
+	mov 1,%o1
+.LL72:
+	sll %i2,%o3,%o2
+	srl %o4,%o1,%o0
+	or %o2,%o0,%i2
+	sll %o4,%o3,%o4
+	srl %i0,%o1,%o2
+	sll %i0,%o3,%o0
+	srl %i3,%o1,%o1
+	or %o0,%o1,%i0
+	sll %i3,%o3,%i3
+	mov %i0,%o1
+	mov	32,%g1
+	subcc	%o2,%i2,%g0
+1:	bcs	5f
+	 addxcc %o1,%o1,%o1	! shift n1n0 and a q-bit in lsb
+	sub	%o2,%i2,%o2	! this kills msb of n
+	addx	%o2,%o2,%o2	! so this cannot give carry
+	subcc	%g1,1,%g1
+2:	bne	1b
+	 subcc	%o2,%i2,%g0
+	bcs	3f
+	 addxcc %o1,%o1,%o1	! shift n1n0 and a q-bit in lsb
+	b	3f
+	 sub	%o2,%i2,%o2	! this kills msb of n
+4:	sub	%o2,%i2,%o2
+5:	addxcc	%o2,%o2,%o2
+	bcc	2b
+	 subcc	%g1,1,%g1
+! Got carry from n.  Subtract next step to cancel this carry.
+	bne	4b
+	 addcc	%o1,%o1,%o1	! shift n1n0 and a 0-bit in lsb
+	sub	%o2,%i2,%o2
+3:	xnor	%o1,0,%o1
+	mov %o2,%i0
+	wr	%g0,%o1,%y	! SPARC has 0-3 delay insn after a wr
+	sra	%o4,31,%g2	! Do not move this insn
+	and	%o1,%g2,%g2	! Do not move this insn
+	andcc	%g0,0,%g1	! Do not move this insn
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,%o4,%g1
+	mulscc	%g1,0,%g1
+	add	%g1,%g2,%o0
+	rd	%y,%o2
+	cmp %o0,%i0
+	bgu,a .LL78
+	add %o1,-1,%o1
+	bne,a .LL50
+	mov 0,%o2
+	cmp %o2,%i3
+	bleu .LL50
+	mov 0,%o2
+	add %o1,-1,%o1
+.LL78:
+	mov 0,%o2
+.LL50:
+	mov %o1,%l1
+.LL86:
+	mov %o2,%l0
+	mov %l0,%i0
+	mov %l1,%i1
+	cmp %l4,0
+	be .LL81
+	sub %g0,%i1,%o0
+	mov %o0,%l3
+	subcc %g0,%o0,%g0
+	sub %g0,%i0,%o0
+	subx %o0,0,%l2
+	mov %l2,%i0
+	mov %l3,%i1
+.LL81:
+	ret
+	restore
diff --git a/arch/sparc/lib/locks.S b/arch/sparc/lib/locks.S
new file mode 100644
index 0000000..95fa484
--- /dev/null
+++ b/arch/sparc/lib/locks.S
@@ -0,0 +1,72 @@
+/* $Id: locks.S,v 1.16 2000/02/26 11:02:47 anton Exp $
+ * locks.S: SMP low-level lock primitives on Sparc.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998 Anton Blanchard (anton@progsoc.uts.edu.au)
+ * Copyright (C) 1998 Jakub Jelinek   (jj@ultra.linux.cz)
+ */
+
+#include <asm/ptrace.h>
+#include <asm/psr.h>
+#include <asm/smp.h>
+#include <asm/spinlock.h>
+
+	.text
+	.align	4
+
+	/* Read/writer locks, as usual this is overly clever to make it
+	 * as fast as possible.
+	 */
+
+	/* caches... */
+___rw_read_enter_spin_on_wlock:
+	orcc	%g2, 0x0, %g0
+	be,a	___rw_read_enter
+	 ldstub	[%g1 + 3], %g2
+	b	___rw_read_enter_spin_on_wlock
+	 ldub	[%g1 + 3], %g2
+___rw_read_exit_spin_on_wlock:
+	orcc	%g2, 0x0, %g0
+	be,a	___rw_read_exit
+	 ldstub	[%g1 + 3], %g2
+	b	___rw_read_exit_spin_on_wlock
+	 ldub	[%g1 + 3], %g2
+___rw_write_enter_spin_on_wlock:
+	orcc	%g2, 0x0, %g0
+	be,a	___rw_write_enter
+	 ldstub	[%g1 + 3], %g2
+	b	___rw_write_enter_spin_on_wlock
+	 ld	[%g1], %g2
+
+	.globl	___rw_read_enter
+___rw_read_enter:
+	orcc	%g2, 0x0, %g0
+	bne,a	___rw_read_enter_spin_on_wlock
+	 ldub	[%g1 + 3], %g2
+	ld	[%g1], %g2
+	add	%g2, 1, %g2
+	st	%g2, [%g1]
+	retl
+	 mov	%g4, %o7
+
+	.globl	___rw_read_exit
+___rw_read_exit:
+	orcc	%g2, 0x0, %g0
+	bne,a	___rw_read_exit_spin_on_wlock
+	 ldub	[%g1 + 3], %g2
+	ld	[%g1], %g2
+	sub	%g2, 0x1ff, %g2
+	st	%g2, [%g1]
+	retl
+	 mov	%g4, %o7
+
+	.globl	___rw_write_enter
+___rw_write_enter:
+	orcc	%g2, 0x0, %g0
+	bne	___rw_write_enter_spin_on_wlock
+	 ld	[%g1], %g2
+	andncc	%g2, 0xff, %g0
+	bne,a	___rw_write_enter_spin_on_wlock
+	 stb	%g0, [%g1 + 3]
+	retl
+	 mov	%g4, %o7
diff --git a/arch/sparc/lib/lshrdi3.S b/arch/sparc/lib/lshrdi3.S
new file mode 100644
index 0000000..35abf5b
--- /dev/null
+++ b/arch/sparc/lib/lshrdi3.S
@@ -0,0 +1,27 @@
+/* $Id: lshrdi3.S,v 1.1 1999/03/21 06:37:45 davem Exp $ */
+
+	.globl	__lshrdi3
+__lshrdi3:
+	cmp	%o2, 0
+	be	3f
+	 mov	0x20, %g2
+
+	sub	%g2, %o2, %g2
+	cmp	%g2, 0
+	bg	1f
+	 srl	%o0, %o2, %o4
+
+	clr	%o4
+	neg	%g2
+	b	2f
+	 srl	%o0, %g2, %o5
+1:
+	sll  %o0, %g2, %g3
+	srl  %o1, %o2, %g2
+	or  %g2, %g3, %o5
+2:
+	mov  %o4, %o0
+	mov  %o5, %o1
+3:
+	retl 
+	 nop 
diff --git a/arch/sparc/lib/memcmp.S b/arch/sparc/lib/memcmp.S
new file mode 100644
index 0000000..cb4bdb0
--- /dev/null
+++ b/arch/sparc/lib/memcmp.S
@@ -0,0 +1,312 @@
+	.text
+	.align 4
+	.global __memcmp, memcmp
+__memcmp:
+memcmp:
+#if 1
+	cmp	%o2, 0
+	ble	L3
+	 mov	0, %g3
+L5:
+	ldub	[%o0], %g2
+	ldub	[%o1], %g3
+	sub	%g2, %g3, %g2
+	mov	%g2, %g3
+	sll	%g2, 24, %g2
+
+	cmp	%g2, 0
+	bne	L3
+	 add	%o0, 1, %o0
+
+	add	%o2, -1, %o2
+
+	cmp	%o2, 0
+	bg	L5
+	 add	%o1, 1, %o1
+L3:
+	sll	%g3, 24, %o0
+	sra	%o0, 24, %o0
+
+	retl
+	 nop
+#else
+	save	%sp, -104, %sp
+	mov	%i2, %o4
+	mov	%i0, %o0
+
+	cmp	%o4, 15
+	ble	L72
+	 mov	%i1, %i2
+
+	andcc	%i2, 3, %g0
+	be	L161
+	 andcc	%o0, 3, %g2
+L75:
+	ldub	[%o0], %g3
+	ldub	[%i2], %g2
+	add	%o0,1, %o0
+
+	subcc	%g3, %g2, %i0
+	bne	L156
+	 add	%i2, 1, %i2
+
+	andcc	%i2, 3, %g0
+	bne	L75
+	 add	%o4, -1, %o4
+
+	andcc	%o0, 3, %g2
+L161:
+	bne,a	L78
+	 mov	%i2, %i1
+
+	mov	%o0, %i5
+	mov	%i2, %i3
+	srl	%o4, 2, %i4
+
+	cmp	%i4, 0
+	bge	L93
+	 mov	%i4, %g2
+
+	add %i4, 3, %g2
+L93:
+	sra	%g2, 2, %g2
+	sll	%g2, 2, %g2
+	sub	%i4, %g2, %g2
+
+	cmp	%g2, 1
+	be,a	L88
+	 add	%o0, 4, %i5
+
+	bg	L94
+	 cmp	%g2, 2
+
+	cmp	%g2, 0
+	be,a	L86
+	 ld	[%o0], %g3
+
+	b	L162
+	 ld	[%i5], %g3
+L94:
+	be	L81
+	 cmp	%g2, 3
+
+	be,a	L83
+	 add	%o0, -4, %i5
+
+	b	L162
+	 ld	[%i5], %g3
+L81:
+	add	%o0, -8, %i5
+	ld	[%o0], %g3
+	add	%i2, -8, %i3
+	ld	[%i2], %g2
+
+	b	L82
+	 add	%i4, 2, %i4
+L83:
+	ld	[%o0], %g4
+	add	%i2, -4, %i3
+	ld	[%i2], %g1
+
+	b	L84
+	 add	%i4, 1, %i4
+L86:
+	b	L87
+	 ld	[%i2], %g2
+L88:
+	add	%i2, 4, %i3
+	ld	[%o0], %g4
+	add	%i4, -1, %i4
+	ld	[%i2], %g1
+L95:
+	ld	[%i5], %g3
+L162:
+	cmp	%g4, %g1
+	be	L87
+	 ld	[%i3], %g2
+
+	cmp	%g4, %g1
+L163:
+	bleu	L114
+	 mov	-1, %i0
+
+	b	L114
+	 mov	1, %i0
+L87:
+	ld	[%i5 + 4], %g4
+	cmp	%g3, %g2
+	bne	L163
+	 ld	[%i3 + 4], %g1
+L84:
+	ld	[%i5 + 8], %g3
+
+	cmp	%g4, %g1
+	bne	L163
+	 ld	[%i3 + 8], %g2
+L82:
+	ld	[%i5 + 12], %g4
+	cmp	%g3, %g2
+	bne	L163
+	 ld	[%i3 + 12], %g1
+
+	add	%i5, 16, %i5
+
+	addcc	%i4, -4, %i4
+	bne	L95
+	 add	%i3, 16, %i3
+
+	cmp	%g4, %g1
+	bne	L163
+	 nop
+
+	b	L114
+	 mov	0, %i0
+L78:
+	srl	%o4, 2, %i0
+	and	%o0, -4, %i3
+	orcc	%i0, %g0, %g3
+	sll	%g2, 3, %o7
+	mov	32, %g2
+
+	bge	L129
+	 sub	%g2, %o7, %o1
+
+	add	%i0, 3, %g3
+L129:
+	sra	%g3, 2, %g2
+	sll	%g2, 2, %g2
+	sub	%i0, %g2, %g2
+
+	cmp	%g2, 1
+	be,a	L124
+	 ld	[%i3], %o3
+
+	bg	L130
+	 cmp	%g2, 2
+
+	cmp	%g2, 0
+	be,a	L122
+	 ld	[%i3], %o2
+
+	b	L164
+	sll	%o3, %o7, %g3
+L130:
+	be	L117
+	 cmp	%g2, 3
+
+	be,a	L119
+	 ld	[%i3], %g1
+
+	b	L164
+	 sll	%o3, %o7, %g3
+L117:
+	ld	[%i3], %g4
+	add	%i2, -8, %i1
+	ld	[%i3 + 4], %o3
+	add	%i0, 2, %i0
+	ld	[%i2], %i4
+
+	b	L118
+	 add	%i3, -4, %i3
+L119:
+	ld	[%i3 + 4], %g4
+	add	%i2, -4, %i1
+	ld	[%i2], %i5
+
+	b	L120
+	 add	%i0, 1, %i0
+L122:
+	ld	[%i3 + 4], %g1
+	ld	[%i2], %i4
+
+	b	L123
+	 add	%i3, 4, %i3
+L124:
+	add	%i2, 4, %i1
+	ld	[%i3 + 4], %o2
+	add	%i0, -1, %i0
+	ld	[%i2], %i5
+	add	%i3, 8, %i3
+L131:
+	sll	%o3, %o7, %g3
+L164:
+	srl	%o2, %o1, %g2
+	ld	[%i3], %g1
+	or	%g3, %g2, %g3
+
+	cmp	%g3, %i5
+	bne	L163
+	 ld	[%i1], %i4
+L123:
+	sll	%o2, %o7, %g3
+	srl	%g1, %o1, %g2
+	ld	[%i3 + 4], %g4
+	or	%g3, %g2, %g3
+
+	cmp	%g3, %i4
+	bne	L163
+	 ld	[%i1 + 4], %i5
+L120:
+	sll	%g1, %o7, %g3
+	srl	%g4, %o1, %g2
+	ld	[%i3 + 8], %o3
+	or	%g3, %g2, %g3
+
+	cmp	%g3, %i5
+	bne	L163
+	 ld	[%i1 + 8], %i4
+L118:
+	sll	%g4, %o7, %g3
+	srl	%o3, %o1, %g2
+	ld	[%i3 + 12], %o2
+	or	%g3, %g2, %g3
+
+	cmp	%g3, %i4
+	bne	L163
+	 ld	[%i1 + 12], %i5
+
+	add	%i3, 16, %i3
+	addcc	%i0, -4, %i0
+	bne	L131
+	 add	%i1, 16, %i1
+
+	sll	%o3, %o7, %g3
+	srl	%o2, %o1, %g2
+	or	%g3, %g2, %g3
+
+	cmp	%g3, %i5
+	be,a	L114
+	 mov	0, %i0
+
+	b,a L163
+L114:
+	cmp	%i0, 0
+	bne	L156
+	 and	%o4, -4, %g2
+
+	add	%o0, %g2, %o0
+	add	%i2, %g2, %i2
+	and	%o4, 3, %o4
+L72:
+	cmp	%o4, 0
+	be	L156
+	 mov	0, %i0
+
+	ldub	[%o0], %g3
+L165:
+	ldub	[%i2], %g2
+	add	%o0, 1, %o0
+
+	subcc	%g3, %g2, %i0
+	bne	L156
+	 add	%i2, 1, %i2
+
+	addcc	%o4, -1, %o4
+	bne,a	L165
+	 ldub	[%o0], %g3
+
+	mov	0, %i0
+L156:
+	ret
+	restore
+#endif
diff --git a/arch/sparc/lib/memcpy.S b/arch/sparc/lib/memcpy.S
new file mode 100644
index 0000000..ce10bc8
--- /dev/null
+++ b/arch/sparc/lib/memcpy.S
@@ -0,0 +1,1150 @@
+/* memcpy.S: Sparc optimized memcpy and memmove code
+ * Hand optimized from GNU libc's memcpy and memmove
+ * Copyright (C) 1991,1996 Free Software Foundation
+ * Copyright (C) 1995 Linus Torvalds (Linus.Torvalds@helsinki.fi)
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#ifdef __KERNEL__
+
+#define FUNC(x) 											\
+	.globl	x;		\
+	.type	x,@function;	\
+	.align	4;											\
+x:
+
+#undef FASTER_REVERSE
+#undef FASTER_NONALIGNED
+#define FASTER_ALIGNED
+
+/* In kernel these functions don't return a value.
+ * One should use macros in asm/string.h for that purpose.
+ * We return 0, so that bugs are more apparent.
+ */
+#define SETUP_RETL
+#define RETL_INSN	clr	%o0
+
+#else
+
+/* libc */
+
+#include "DEFS.h"
+
+#define FASTER_REVERSE
+#define FASTER_NONALIGNED
+#define FASTER_ALIGNED
+
+#define SETUP_RETL	mov	%o0, %g6
+#define RETL_INSN	mov	%g6, %o0
+
+#endif
+
+/* Both these macros have to start with exactly the same insn */
+#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
+	ldd	[%src + (offset) + 0x00], %t0; \
+	ldd	[%src + (offset) + 0x08], %t2; \
+	ldd	[%src + (offset) + 0x10], %t4; \
+	ldd	[%src + (offset) + 0x18], %t6; \
+	st	%t0, [%dst + (offset) + 0x00]; \
+	st	%t1, [%dst + (offset) + 0x04]; \
+	st	%t2, [%dst + (offset) + 0x08]; \
+	st	%t3, [%dst + (offset) + 0x0c]; \
+	st	%t4, [%dst + (offset) + 0x10]; \
+	st	%t5, [%dst + (offset) + 0x14]; \
+	st	%t6, [%dst + (offset) + 0x18]; \
+	st	%t7, [%dst + (offset) + 0x1c];
+
+#define MOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
+	ldd	[%src + (offset) + 0x00], %t0; \
+	ldd	[%src + (offset) + 0x08], %t2; \
+	ldd	[%src + (offset) + 0x10], %t4; \
+	ldd	[%src + (offset) + 0x18], %t6; \
+	std	%t0, [%dst + (offset) + 0x00]; \
+	std	%t2, [%dst + (offset) + 0x08]; \
+	std	%t4, [%dst + (offset) + 0x10]; \
+	std	%t6, [%dst + (offset) + 0x18];
+
+#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \
+	ldd	[%src - (offset) - 0x10], %t0; \
+	ldd	[%src - (offset) - 0x08], %t2; \
+	st	%t0, [%dst - (offset) - 0x10]; \
+	st	%t1, [%dst - (offset) - 0x0c]; \
+	st	%t2, [%dst - (offset) - 0x08]; \
+	st	%t3, [%dst - (offset) - 0x04];
+
+#define MOVE_LASTALIGNCHUNK(src, dst, offset, t0, t1, t2, t3) \
+	ldd	[%src - (offset) - 0x10], %t0; \
+	ldd	[%src - (offset) - 0x08], %t2; \
+	std	%t0, [%dst - (offset) - 0x10]; \
+	std	%t2, [%dst - (offset) - 0x08];
+
+#define MOVE_SHORTCHUNK(src, dst, offset, t0, t1) \
+	ldub	[%src - (offset) - 0x02], %t0; \
+	ldub	[%src - (offset) - 0x01], %t1; \
+	stb	%t0, [%dst - (offset) - 0x02]; \
+	stb	%t1, [%dst - (offset) - 0x01];
+
+/* Both these macros have to start with exactly the same insn */
+#define RMOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
+	ldd	[%src - (offset) - 0x20], %t0; \
+	ldd	[%src - (offset) - 0x18], %t2; \
+	ldd	[%src - (offset) - 0x10], %t4; \
+	ldd	[%src - (offset) - 0x08], %t6; \
+	st	%t0, [%dst - (offset) - 0x20]; \
+	st	%t1, [%dst - (offset) - 0x1c]; \
+	st	%t2, [%dst - (offset) - 0x18]; \
+	st	%t3, [%dst - (offset) - 0x14]; \
+	st	%t4, [%dst - (offset) - 0x10]; \
+	st	%t5, [%dst - (offset) - 0x0c]; \
+	st	%t6, [%dst - (offset) - 0x08]; \
+	st	%t7, [%dst - (offset) - 0x04];
+
+#define RMOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
+	ldd	[%src - (offset) - 0x20], %t0; \
+	ldd	[%src - (offset) - 0x18], %t2; \
+	ldd	[%src - (offset) - 0x10], %t4; \
+	ldd	[%src - (offset) - 0x08], %t6; \
+	std	%t0, [%dst - (offset) - 0x20]; \
+	std	%t2, [%dst - (offset) - 0x18]; \
+	std	%t4, [%dst - (offset) - 0x10]; \
+	std	%t6, [%dst - (offset) - 0x08];
+
+#define RMOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \
+	ldd	[%src + (offset) + 0x00], %t0; \
+	ldd	[%src + (offset) + 0x08], %t2; \
+	st	%t0, [%dst + (offset) + 0x00]; \
+	st	%t1, [%dst + (offset) + 0x04]; \
+	st	%t2, [%dst + (offset) + 0x08]; \
+	st	%t3, [%dst + (offset) + 0x0c];
+
+#define RMOVE_SHORTCHUNK(src, dst, offset, t0, t1) \
+	ldub	[%src + (offset) + 0x00], %t0; \
+	ldub	[%src + (offset) + 0x01], %t1; \
+	stb	%t0, [%dst + (offset) + 0x00]; \
+	stb	%t1, [%dst + (offset) + 0x01];
+
+#define SMOVE_CHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, prev, shil, shir, offset2) \
+	ldd	[%src + (offset) + 0x00], %t0; \
+	ldd	[%src + (offset) + 0x08], %t2; \
+	srl	%t0, shir, %t5; \
+	srl	%t1, shir, %t6; \
+	sll	%t0, shil, %t0; \
+	or	%t5, %prev, %t5; \
+	sll	%t1, shil, %prev; \
+	or	%t6, %t0, %t0; \
+	srl	%t2, shir, %t1; \
+	srl	%t3, shir, %t6; \
+	sll	%t2, shil, %t2; \
+	or	%t1, %prev, %t1; \
+	std	%t4, [%dst + (offset) + (offset2) - 0x04]; \
+	std	%t0, [%dst + (offset) + (offset2) + 0x04]; \
+	sll	%t3, shil, %prev; \
+	or	%t6, %t2, %t4;
+
+#define SMOVE_ALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, prev, shil, shir, offset2) \
+	ldd	[%src + (offset) + 0x00], %t0; \
+	ldd	[%src + (offset) + 0x08], %t2; \
+	srl	%t0, shir, %t4;	\
+	srl	%t1, shir, %t5;	\
+	sll	%t0, shil, %t6;	\
+	or	%t4, %prev, %t0; \
+	sll	%t1, shil, %prev; \
+	or	%t5, %t6, %t1; \
+	srl	%t2, shir, %t4;	\
+	srl	%t3, shir, %t5;	\
+	sll	%t2, shil, %t6; \
+	or	%t4, %prev, %t2; \
+	sll	%t3, shil, %prev; \
+	or	%t5, %t6, %t3; \
+	std	%t0, [%dst + (offset) + (offset2) + 0x00]; \
+	std	%t2, [%dst + (offset) + (offset2) + 0x08];
+
+	.text
+	.align	4
+
+#ifdef FASTER_REVERSE
+
+70:	/* rdword_align */
+
+	andcc		%o1, 1, %g0
+	be		4f
+	 andcc		%o1, 2, %g0
+
+	ldub		[%o1 - 1], %g2
+	sub		%o1, 1, %o1
+	stb		%g2, [%o0 - 1]
+	sub		%o2, 1, %o2
+	be		3f
+	 sub		%o0, 1, %o0
+4:
+	lduh		[%o1 - 2], %g2
+	sub		%o1, 2, %o1
+	sth		%g2, [%o0 - 2]
+	sub		%o2, 2, %o2
+	b		3f
+	 sub		%o0, 2, %o0
+
+#endif /* FASTER_REVERSE */
+
+0:
+	retl
+	 nop		! Only bcopy returns here and it retuns void...
+
+#ifdef __KERNEL__
+FUNC(amemmove)
+FUNC(__memmove)
+#endif
+FUNC(memmove)
+	cmp		%o0, %o1
+	SETUP_RETL
+	bleu		9f
+	 sub		%o0, %o1, %o4
+
+	add		%o1, %o2, %o3
+	cmp		%o3, %o0
+	bleu		0f
+	 andcc		%o4, 3, %o5
+
+#ifndef FASTER_REVERSE
+
+	add		%o1, %o2, %o1
+	add		%o0, %o2, %o0
+	sub		%o1, 1, %o1
+	sub		%o0, 1, %o0
+	
+1:	/* reverse_bytes */
+
+	ldub		[%o1], %o4
+	subcc		%o2, 1, %o2
+	stb		%o4, [%o0]
+	sub		%o1, 1, %o1
+	bne		1b
+	 sub		%o0, 1, %o0
+
+	retl
+	 RETL_INSN
+
+#else /* FASTER_REVERSE */
+
+	add		%o1, %o2, %o1
+	add		%o0, %o2, %o0
+	bne		77f
+	 cmp		%o2, 15
+	bleu		91f
+	 andcc		%o1, 3, %g0
+	bne		70b
+3:
+	 andcc		%o1, 4, %g0
+
+	be		2f
+	 mov		%o2, %g1
+
+	ld		[%o1 - 4], %o4
+	sub		%g1, 4, %g1
+	st		%o4, [%o0 - 4]
+	sub		%o1, 4, %o1
+	sub		%o0, 4, %o0
+2:
+	andcc		%g1, 0xffffff80, %g7
+	be		3f
+	 andcc		%o0, 4, %g0
+
+	be		74f + 4
+5:
+	RMOVE_BIGCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5)
+	RMOVE_BIGCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5)
+	RMOVE_BIGCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5)
+	RMOVE_BIGCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5)
+	subcc		%g7, 128, %g7
+	sub		%o1, 128, %o1
+	bne		5b
+	 sub		%o0, 128, %o0
+3:
+	andcc		%g1, 0x70, %g7
+	be		72f
+	 andcc		%g1, 8, %g0
+
+	sethi		%hi(72f), %o5
+	srl		%g7, 1, %o4
+	add		%g7, %o4, %o4
+	sub		%o1, %g7, %o1
+	sub		%o5, %o4, %o5
+	jmpl		%o5 + %lo(72f), %g0
+	 sub		%o0, %g7, %o0
+
+71:	/* rmemcpy_table */
+	RMOVE_LASTCHUNK(o1, o0, 0x60, g2, g3, g4, g5)
+	RMOVE_LASTCHUNK(o1, o0, 0x50, g2, g3, g4, g5)
+	RMOVE_LASTCHUNK(o1, o0, 0x40, g2, g3, g4, g5)
+	RMOVE_LASTCHUNK(o1, o0, 0x30, g2, g3, g4, g5)
+	RMOVE_LASTCHUNK(o1, o0, 0x20, g2, g3, g4, g5)
+	RMOVE_LASTCHUNK(o1, o0, 0x10, g2, g3, g4, g5)
+	RMOVE_LASTCHUNK(o1, o0, 0x00, g2, g3, g4, g5)
+
+72:	/* rmemcpy_table_end */
+
+	be		73f
+	 andcc		%g1, 4, %g0
+
+	ldd		[%o1 - 0x08], %g2
+	sub		%o0, 8, %o0
+	sub		%o1, 8, %o1
+	st		%g2, [%o0]
+	st		%g3, [%o0 + 0x04]
+
+73:	/* rmemcpy_last7 */
+
+	be		1f
+	 andcc		%g1, 2, %g0
+
+	ld		[%o1 - 4], %g2
+	sub		%o1, 4, %o1
+	st		%g2, [%o0 - 4]
+	sub		%o0, 4, %o0
+1:
+	be		1f
+	 andcc		%g1, 1, %g0
+
+	lduh		[%o1 - 2], %g2
+	sub		%o1, 2, %o1
+	sth		%g2, [%o0 - 2]
+	sub		%o0, 2, %o0
+1:
+	be		1f
+	 nop
+
+	ldub		[%o1 - 1], %g2
+	stb		%g2, [%o0 - 1]
+1:
+	retl
+ 	 RETL_INSN
+
+74:	/* rldd_std */
+	RMOVE_BIGALIGNCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5)
+	RMOVE_BIGALIGNCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5)
+	RMOVE_BIGALIGNCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5)
+	RMOVE_BIGALIGNCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5)
+	subcc		%g7, 128, %g7
+	sub		%o1, 128, %o1
+	bne		74b
+	 sub		%o0, 128, %o0
+
+	andcc		%g1, 0x70, %g7
+	be		72b
+	 andcc		%g1, 8, %g0
+
+	sethi		%hi(72b), %o5
+	srl		%g7, 1, %o4
+	add		%g7, %o4, %o4
+	sub		%o1, %g7, %o1
+	sub		%o5, %o4, %o5
+	jmpl		%o5 + %lo(72b), %g0
+	 sub		%o0, %g7, %o0
+
+75:	/* rshort_end */
+
+	and		%o2, 0xe, %o3
+2:
+	sethi		%hi(76f), %o5
+	sll		%o3, 3, %o4
+	sub		%o0, %o3, %o0
+	sub		%o5, %o4, %o5
+	sub		%o1, %o3, %o1
+	jmpl		%o5 + %lo(76f), %g0
+	 andcc		%o2, 1, %g0
+
+	RMOVE_SHORTCHUNK(o1, o0, 0x0c, g2, g3)
+	RMOVE_SHORTCHUNK(o1, o0, 0x0a, g2, g3)
+	RMOVE_SHORTCHUNK(o1, o0, 0x08, g2, g3)
+	RMOVE_SHORTCHUNK(o1, o0, 0x06, g2, g3)
+	RMOVE_SHORTCHUNK(o1, o0, 0x04, g2, g3)
+	RMOVE_SHORTCHUNK(o1, o0, 0x02, g2, g3)
+	RMOVE_SHORTCHUNK(o1, o0, 0x00, g2, g3)
+
+76:	/* rshort_table_end */
+
+	be		1f
+	 nop
+	ldub		[%o1 - 1], %g2
+	stb		%g2, [%o0 - 1]
+1:
+	retl
+ 	 RETL_INSN
+
+91:	/* rshort_aligned_end */
+
+	bne		75b
+	 andcc		%o2, 8, %g0
+
+	be		1f
+	 andcc		%o2, 4, %g0
+
+	ld		[%o1 - 0x08], %g2
+	ld		[%o1 - 0x04], %g3
+	sub		%o1, 8, %o1
+	st		%g2, [%o0 - 0x08]
+	st		%g3, [%o0 - 0x04]
+	sub		%o0, 8, %o0
+1:
+	b		73b
+	 mov		%o2, %g1
+
+77:	/* rnon_aligned */
+	cmp		%o2, 15
+	bleu		75b
+	 andcc		%o0, 3, %g0
+	be		64f
+	 andcc		%o0, 1, %g0
+	be		63f
+	 andcc		%o0, 2, %g0
+	ldub		[%o1 - 1], %g5
+	sub		%o1, 1, %o1
+	stb		%g5, [%o0 - 1]
+	sub		%o0, 1, %o0
+	be		64f
+	 sub		%o2, 1, %o2
+63:
+	ldub		[%o1 - 1], %g5
+	sub		%o1, 2, %o1
+	stb		%g5, [%o0 - 1]
+	sub		%o0, 2, %o0
+	ldub		[%o1], %g5
+	sub		%o2, 2, %o2
+	stb		%g5, [%o0]
+64:	
+	and		%o1, 3, %g2
+	and		%o1, -4, %o1
+	and		%o2, 0xc, %g3
+	add		%o1, 4, %o1
+	cmp		%g3, 4
+	sll		%g2, 3, %g4
+	mov		32, %g2
+	be		4f
+	 sub		%g2, %g4, %g7
+
+	blu		3f
+	 cmp		%g3, 8
+
+	be		2f
+	 srl		%o2, 2, %g3
+
+	ld		[%o1 - 4], %o3
+	add		%o0, -8, %o0
+	ld		[%o1 - 8], %o4
+	add		%o1, -16, %o1
+	b		7f
+	 add		%g3, 1, %g3
+2:
+	ld		[%o1 - 4], %o4
+	add		%o0, -4, %o0
+	ld		[%o1 - 8], %g1
+	add		%o1, -12, %o1
+	b		8f
+	 add		%g3, 2, %g3
+3:
+	ld		[%o1 - 4], %o5
+	add		%o0, -12, %o0
+	ld		[%o1 - 8], %o3
+	add		%o1, -20, %o1
+	b		6f
+	 srl		%o2, 2, %g3
+4:
+	ld		[%o1 - 4], %g1
+	srl		%o2, 2, %g3
+	ld		[%o1 - 8], %o5
+	add		%o1, -24, %o1
+	add		%o0, -16, %o0
+	add		%g3, -1, %g3
+
+	ld		[%o1 + 12], %o3
+5:
+	sll		%o5, %g4, %g2
+	srl		%g1, %g7, %g5
+	or		%g2, %g5, %g2
+	st		%g2, [%o0 + 12]
+6:
+	ld		[%o1 + 8], %o4
+	sll		%o3, %g4, %g2
+	srl		%o5, %g7, %g5
+	or		%g2, %g5, %g2
+	st		%g2, [%o0 + 8]
+7:
+	ld		[%o1 + 4], %g1
+	sll		%o4, %g4, %g2
+	srl		%o3, %g7, %g5
+	or		%g2, %g5, %g2
+	st		%g2, [%o0 + 4]
+8:
+	ld		[%o1], %o5
+	sll		%g1, %g4, %g2
+	srl		%o4, %g7, %g5
+	addcc		%g3, -4, %g3
+	or		%g2, %g5, %g2
+	add		%o1, -16, %o1
+	st		%g2, [%o0]
+	add		%o0, -16, %o0
+	bne,a		5b	
+	 ld		[%o1 + 12], %o3
+	sll		%o5, %g4, %g2
+	srl		%g1, %g7, %g5
+	srl		%g4, 3, %g3
+	or		%g2, %g5, %g2
+	add		%o1, %g3, %o1
+	andcc		%o2, 2, %g0
+	st		%g2, [%o0 + 12]
+	be		1f
+	 andcc		%o2, 1, %g0
+	
+	ldub		[%o1 + 15], %g5
+	add		%o1, -2, %o1
+	stb		%g5, [%o0 + 11]
+	add		%o0, -2, %o0
+	ldub		[%o1 + 16], %g5
+	stb		%g5, [%o0 + 12]
+1:
+	be		1f
+	 nop
+	ldub		[%o1 + 15], %g5
+	stb		%g5, [%o0 + 11]
+1:
+	retl
+	 RETL_INSN
+
+#endif /* FASTER_REVERSE */
+
+/* NOTE: This code is executed just for the cases,
+         where %src (=%o1) & 3 is != 0.
+	 We need to align it to 4. So, for (%src & 3)
+	 1 we need to do ldub,lduh
+	 2 lduh
+	 3 just ldub
+         so even if it looks weird, the branches
+         are correct here. -jj
+ */
+78:	/* dword_align */
+
+	andcc		%o1, 1, %g0
+	be		4f
+	 andcc		%o1, 2, %g0
+
+	ldub		[%o1], %g2
+	add		%o1, 1, %o1
+	stb		%g2, [%o0]
+	sub		%o2, 1, %o2
+	bne		3f
+	 add		%o0, 1, %o0
+4:
+	lduh		[%o1], %g2
+	add		%o1, 2, %o1
+	sth		%g2, [%o0]
+	sub		%o2, 2, %o2
+	b		3f
+	 add		%o0, 2, %o0
+
+#ifdef __KERNEL__
+FUNC(__memcpy)
+#endif
+FUNC(memcpy)	/* %o0=dst %o1=src %o2=len */
+
+	sub		%o0, %o1, %o4
+	SETUP_RETL
+9:
+	andcc		%o4, 3, %o5
+0:
+	bne		86f
+	 cmp		%o2, 15
+
+	bleu		90f
+	 andcc		%o1, 3, %g0
+
+	bne		78b
+3:
+	 andcc		%o1, 4, %g0
+
+	be		2f
+	 mov		%o2, %g1
+
+	ld		[%o1], %o4
+	sub		%g1, 4, %g1
+	st		%o4, [%o0]
+	add		%o1, 4, %o1
+	add		%o0, 4, %o0
+2:
+	andcc		%g1, 0xffffff80, %g7
+	be		3f
+	 andcc		%o0, 4, %g0
+
+	be		82f + 4
+5:
+	MOVE_BIGCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5)
+	subcc		%g7, 128, %g7
+	add		%o1, 128, %o1
+	bne		5b
+	 add		%o0, 128, %o0
+3:
+	andcc		%g1, 0x70, %g7
+	be		80f
+	 andcc		%g1, 8, %g0
+
+	sethi		%hi(80f), %o5
+	srl		%g7, 1, %o4
+	add		%g7, %o4, %o4
+	add		%o1, %g7, %o1
+	sub		%o5, %o4, %o5
+	jmpl		%o5 + %lo(80f), %g0
+	 add		%o0, %g7, %o0
+
+79:	/* memcpy_table */
+
+	MOVE_LASTCHUNK(o1, o0, 0x60, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x50, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x40, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x30, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x20, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x10, g2, g3, g4, g5)
+	MOVE_LASTCHUNK(o1, o0, 0x00, g2, g3, g4, g5)
+
+80:	/* memcpy_table_end */
+	be		81f
+	 andcc		%g1, 4, %g0
+
+	ldd		[%o1], %g2
+	add		%o0, 8, %o0
+	st		%g2, [%o0 - 0x08]
+	add		%o1, 8, %o1
+	st		%g3, [%o0 - 0x04]
+
+81:	/* memcpy_last7 */
+
+	be		1f
+	 andcc		%g1, 2, %g0
+
+	ld		[%o1], %g2
+	add		%o1, 4, %o1
+	st		%g2, [%o0]
+	add		%o0, 4, %o0
+1:
+	be		1f
+	 andcc		%g1, 1, %g0
+
+	lduh		[%o1], %g2
+	add		%o1, 2, %o1
+	sth		%g2, [%o0]
+	add		%o0, 2, %o0
+1:
+	be		1f
+	 nop
+
+	ldub		[%o1], %g2
+	stb		%g2, [%o0]
+1:
+	retl
+ 	 RETL_INSN
+
+82:	/* ldd_std */
+	MOVE_BIGALIGNCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGALIGNCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGALIGNCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5)
+	MOVE_BIGALIGNCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5)
+	subcc		%g7, 128, %g7
+	add		%o1, 128, %o1
+	bne		82b
+	 add		%o0, 128, %o0
+
+#ifndef FASTER_ALIGNED
+
+	andcc		%g1, 0x70, %g7
+	be		80b
+	 andcc		%g1, 8, %g0
+
+	sethi		%hi(80b), %o5
+	srl		%g7, 1, %o4
+	add		%g7, %o4, %o4
+	add		%o1, %g7, %o1
+	sub		%o5, %o4, %o5
+	jmpl		%o5 + %lo(80b), %g0
+	 add		%o0, %g7, %o0
+
+#else /* FASTER_ALIGNED */
+
+	andcc		%g1, 0x70, %g7
+	be		84f
+	 andcc		%g1, 8, %g0
+
+	sethi		%hi(84f), %o5
+	add		%o1, %g7, %o1
+	sub		%o5, %g7, %o5
+	jmpl		%o5 + %lo(84f), %g0
+	 add		%o0, %g7, %o0
+
+83:	/* amemcpy_table */
+
+	MOVE_LASTALIGNCHUNK(o1, o0, 0x60, g2, g3, g4, g5)
+	MOVE_LASTALIGNCHUNK(o1, o0, 0x50, g2, g3, g4, g5)
+	MOVE_LASTALIGNCHUNK(o1, o0, 0x40, g2, g3, g4, g5)
+	MOVE_LASTALIGNCHUNK(o1, o0, 0x30, g2, g3, g4, g5)
+	MOVE_LASTALIGNCHUNK(o1, o0, 0x20, g2, g3, g4, g5)
+	MOVE_LASTALIGNCHUNK(o1, o0, 0x10, g2, g3, g4, g5)
+	MOVE_LASTALIGNCHUNK(o1, o0, 0x00, g2, g3, g4, g5)
+
+84:	/* amemcpy_table_end */
+	be		85f
+	 andcc		%g1, 4, %g0
+
+	ldd		[%o1], %g2
+	add		%o0, 8, %o0
+	std		%g2, [%o0 - 0x08]
+	add		%o1, 8, %o1
+85:	/* amemcpy_last7 */
+	be		1f
+	 andcc		%g1, 2, %g0
+
+	ld		[%o1], %g2
+	add		%o1, 4, %o1
+	st		%g2, [%o0]
+	add		%o0, 4, %o0
+1:
+	be		1f
+	 andcc		%g1, 1, %g0
+
+	lduh		[%o1], %g2
+	add		%o1, 2, %o1
+	sth		%g2, [%o0]
+	add		%o0, 2, %o0
+1:
+	be		1f
+	 nop
+
+	ldub		[%o1], %g2
+	stb		%g2, [%o0]
+1:
+	retl
+ 	 RETL_INSN
+
+#endif /* FASTER_ALIGNED */
+
+86:	/* non_aligned */
+	cmp		%o2, 6
+	bleu		88f
+
+#ifdef FASTER_NONALIGNED
+
+	 cmp		%o2, 256
+	bcc		87f
+
+#endif /* FASTER_NONALIGNED */
+
+	 andcc		%o0, 3, %g0
+	be		61f
+	 andcc		%o0, 1, %g0
+	be		60f
+	 andcc		%o0, 2, %g0
+
+	ldub		[%o1], %g5
+	add		%o1, 1, %o1
+	stb		%g5, [%o0]
+	sub		%o2, 1, %o2
+	bne		61f
+	 add		%o0, 1, %o0
+60:
+	ldub		[%o1], %g3
+	add		%o1, 2, %o1
+	stb		%g3, [%o0]
+	sub		%o2, 2, %o2
+	ldub		[%o1 - 1], %g3
+	add		%o0, 2, %o0
+	stb		%g3, [%o0 - 1]
+61:
+	and		%o1, 3, %g2
+	and		%o2, 0xc, %g3
+	and		%o1, -4, %o1
+	cmp		%g3, 4
+	sll		%g2, 3, %g4
+	mov		32, %g2
+	be		4f
+	 sub		%g2, %g4, %g7
+	
+	blu		3f
+	 cmp		%g3, 0x8
+
+	be		2f
+	 srl		%o2, 2, %g3
+
+	ld		[%o1], %o3
+	add		%o0, -8, %o0
+	ld		[%o1 + 4], %o4
+	b		8f
+	 add		%g3, 1, %g3
+2:
+	ld		[%o1], %o4
+	add		%o0, -12, %o0
+	ld		[%o1 + 4], %o5
+	add		%g3, 2, %g3
+	b		9f
+	 add		%o1, -4, %o1
+3:
+	ld		[%o1], %g1
+	add		%o0, -4, %o0
+	ld		[%o1 + 4], %o3
+	srl		%o2, 2, %g3
+	b		7f
+	 add		%o1, 4, %o1
+4:
+	ld		[%o1], %o5
+	cmp		%o2, 7
+	ld		[%o1 + 4], %g1
+	srl		%o2, 2, %g3
+	bleu		10f
+	 add		%o1, 8, %o1
+
+	ld		[%o1], %o3
+	add		%g3, -1, %g3
+5:
+	sll		%o5, %g4, %g2
+	srl		%g1, %g7, %g5
+	or		%g2, %g5, %g2
+	st		%g2, [%o0]
+7:
+	ld		[%o1 + 4], %o4
+	sll		%g1, %g4, %g2
+	srl		%o3, %g7, %g5
+	or		%g2, %g5, %g2
+	st		%g2, [%o0 + 4]
+8:
+	ld		[%o1 + 8], %o5
+	sll		%o3, %g4, %g2
+	srl		%o4, %g7, %g5
+	or		%g2, %g5, %g2
+	st		%g2, [%o0 + 8]
+9:
+	ld		[%o1 + 12], %g1
+	sll		%o4, %g4, %g2
+	srl		%o5, %g7, %g5
+	addcc		%g3, -4, %g3
+	or		%g2, %g5, %g2
+	add		%o1, 16, %o1
+	st		%g2, [%o0 + 12]
+	add		%o0, 16, %o0
+	bne,a		5b
+	 ld		[%o1], %o3
+10:
+	sll		%o5, %g4, %g2
+	srl		%g1, %g7, %g5
+	srl		%g7, 3, %g3
+	or		%g2, %g5, %g2
+	sub		%o1, %g3, %o1
+	andcc		%o2, 2, %g0
+	st		%g2, [%o0]
+	be		1f
+	 andcc		%o2, 1, %g0
+
+	ldub		[%o1], %g2
+	add		%o1, 2, %o1
+	stb		%g2, [%o0 + 4]
+	add		%o0, 2, %o0
+	ldub		[%o1 - 1], %g2
+	stb		%g2, [%o0 + 3]
+1:
+	be		1f
+	 nop
+	ldub		[%o1], %g2
+	stb		%g2, [%o0 + 4]
+1:
+	retl
+	 RETL_INSN
+
+#ifdef FASTER_NONALIGNED
+
+87:	/* faster_nonaligned */
+
+	andcc		%o1, 3, %g0
+	be		3f
+	 andcc		%o1, 1, %g0
+
+	be		4f
+	 andcc		%o1, 2, %g0
+
+	ldub		[%o1], %g2
+	add		%o1, 1, %o1
+	stb		%g2, [%o0]
+	sub		%o2, 1, %o2
+	bne		3f
+	 add		%o0, 1, %o0
+4:
+	lduh		[%o1], %g2
+	add		%o1, 2, %o1
+	srl		%g2, 8, %g3
+	sub		%o2, 2, %o2
+	stb		%g3, [%o0]
+	add		%o0, 2, %o0
+	stb		%g2, [%o0 - 1]
+3:
+	 andcc		%o1, 4, %g0
+
+	bne		2f
+	 cmp		%o5, 1
+
+	ld		[%o1], %o4
+	srl		%o4, 24, %g2
+	stb		%g2, [%o0]
+	srl		%o4, 16, %g3
+	stb		%g3, [%o0 + 1]
+	srl		%o4, 8, %g2
+	stb		%g2, [%o0 + 2]
+	sub		%o2, 4, %o2
+	stb		%o4, [%o0 + 3]
+	add		%o1, 4, %o1
+	add		%o0, 4, %o0
+2:
+	be		33f
+	 cmp		%o5, 2
+	be		32f
+	 sub		%o2, 4, %o2
+31:
+	ld		[%o1], %g2
+	add		%o1, 4, %o1
+	srl		%g2, 24, %g3
+	and		%o0, 7, %g5
+	stb		%g3, [%o0]
+	cmp		%g5, 7
+	sll		%g2, 8, %g1
+	add		%o0, 4, %o0
+	be		41f
+	 and		%o2, 0xffffffc0, %o3
+	ld		[%o0 - 7], %o4
+4:
+	SMOVE_CHUNK(o1, o0, 0x00, g2, g3, g4, g5, o4, o5, g7, g1, 8, 24, -3)
+	SMOVE_CHUNK(o1, o0, 0x10, g2, g3, g4, g5, o4, o5, g7, g1, 8, 24, -3)
+	SMOVE_CHUNK(o1, o0, 0x20, g2, g3, g4, g5, o4, o5, g7, g1, 8, 24, -3)
+	SMOVE_CHUNK(o1, o0, 0x30, g2, g3, g4, g5, o4, o5, g7, g1, 8, 24, -3)
+	subcc		%o3, 64, %o3
+	add		%o1, 64, %o1
+	bne		4b
+	 add		%o0, 64, %o0
+
+	andcc		%o2, 0x30, %o3
+	be,a		1f
+	 srl		%g1, 16, %g2
+4:
+	SMOVE_CHUNK(o1, o0, 0x00, g2, g3, g4, g5, o4, o5, g7, g1, 8, 24, -3)
+	subcc		%o3, 16, %o3
+	add		%o1, 16, %o1
+	bne		4b
+	 add		%o0, 16, %o0
+
+	srl		%g1, 16, %g2
+1:
+	st		%o4, [%o0 - 7]
+	sth		%g2, [%o0 - 3]
+	srl		%g1, 8, %g4
+	b		88f
+	 stb		%g4, [%o0 - 1]
+32:
+	ld		[%o1], %g2
+	add		%o1, 4, %o1
+	srl		%g2, 16, %g3
+	and		%o0, 7, %g5
+	sth		%g3, [%o0]
+	cmp		%g5, 6
+	sll		%g2, 16, %g1
+	add		%o0, 4, %o0
+	be		42f
+	 and		%o2, 0xffffffc0, %o3
+	ld		[%o0 - 6], %o4
+4:
+	SMOVE_CHUNK(o1, o0, 0x00, g2, g3, g4, g5, o4, o5, g7, g1, 16, 16, -2)
+	SMOVE_CHUNK(o1, o0, 0x10, g2, g3, g4, g5, o4, o5, g7, g1, 16, 16, -2)
+	SMOVE_CHUNK(o1, o0, 0x20, g2, g3, g4, g5, o4, o5, g7, g1, 16, 16, -2)
+	SMOVE_CHUNK(o1, o0, 0x30, g2, g3, g4, g5, o4, o5, g7, g1, 16, 16, -2)
+	subcc		%o3, 64, %o3
+	add		%o1, 64, %o1
+	bne		4b
+	 add		%o0, 64, %o0
+
+	andcc		%o2, 0x30, %o3
+	be,a		1f
+	 srl		%g1, 16, %g2
+4:
+	SMOVE_CHUNK(o1, o0, 0x00, g2, g3, g4, g5, o4, o5, g7, g1, 16, 16, -2)
+	subcc		%o3, 16, %o3
+	add		%o1, 16, %o1
+	bne		4b
+	 add		%o0, 16, %o0
+
+	srl		%g1, 16, %g2
+1:
+	st		%o4, [%o0 - 6]
+	b		88f
+	 sth		%g2, [%o0 - 2]
+33:
+	ld		[%o1], %g2
+	sub		%o2, 4, %o2
+	srl		%g2, 24, %g3
+	and		%o0, 7, %g5
+	stb		%g3, [%o0]
+	cmp		%g5, 5
+	srl		%g2, 8, %g4
+	sll		%g2, 24, %g1
+	sth		%g4, [%o0 + 1]
+	add		%o1, 4, %o1
+	be		43f
+	 and		%o2, 0xffffffc0, %o3
+
+	ld		[%o0 - 1], %o4
+	add		%o0, 4, %o0
+4:
+	SMOVE_CHUNK(o1, o0, 0x00, g2, g3, g4, g5, o4, o5, g7, g1, 24, 8, -1)
+	SMOVE_CHUNK(o1, o0, 0x10, g2, g3, g4, g5, o4, o5, g7, g1, 24, 8, -1)
+	SMOVE_CHUNK(o1, o0, 0x20, g2, g3, g4, g5, o4, o5, g7, g1, 24, 8, -1)
+	SMOVE_CHUNK(o1, o0, 0x30, g2, g3, g4, g5, o4, o5, g7, g1, 24, 8, -1)
+	subcc		%o3, 64, %o3
+	add		%o1, 64, %o1
+	bne		4b
+	 add		%o0, 64, %o0
+
+	andcc		%o2, 0x30, %o3
+	be,a		1f
+	 srl		%g1, 24, %g2
+4:
+	SMOVE_CHUNK(o1, o0, 0x00, g2, g3, g4, g5, o4, o5, g7, g1, 24, 8, -1)
+	subcc		%o3, 16, %o3
+	add		%o1, 16, %o1
+	bne		4b
+	 add		%o0, 16, %o0
+
+	srl		%g1, 24, %g2
+1:
+	st		%o4, [%o0 - 5]
+	b		88f
+	 stb		%g2, [%o0 - 1]
+41:
+	SMOVE_ALIGNCHUNK(o1, o0, 0x00, g2, g3, g4, g5, o4, o5, g7, g1, 8, 24, -3)
+	SMOVE_ALIGNCHUNK(o1, o0, 0x10, g2, g3, g4, g5, o4, o5, g7, g1, 8, 24, -3)
+	SMOVE_ALIGNCHUNK(o1, o0, 0x20, g2, g3, g4, g5, o4, o5, g7, g1, 8, 24, -3)
+	SMOVE_ALIGNCHUNK(o1, o0, 0x30, g2, g3, g4, g5, o4, o5, g7, g1, 8, 24, -3)
+	subcc		%o3, 64, %o3
+	add		%o1, 64, %o1
+	bne		41b
+	 add		%o0, 64, %o0
+	 
+	andcc		%o2, 0x30, %o3
+	be,a		1f
+	 srl		%g1, 16, %g2
+4:
+	SMOVE_ALIGNCHUNK(o1, o0, 0x00, g2, g3, g4, g5, o4, o5, g7, g1, 8, 24, -3)
+	subcc		%o3, 16, %o3
+	add		%o1, 16, %o1
+	bne		4b
+	 add		%o0, 16, %o0
+
+	srl		%g1, 16, %g2
+1:
+	sth		%g2, [%o0 - 3]
+	srl		%g1, 8, %g4
+	b		88f
+	 stb		%g4, [%o0 - 1]
+43:
+	SMOVE_ALIGNCHUNK(o1, o0, 0x00, g2, g3, g4, g5, o4, o5, g7, g1, 24, 8, 3)
+	SMOVE_ALIGNCHUNK(o1, o0, 0x10, g2, g3, g4, g5, o4, o5, g7, g1, 24, 8, 3)
+	SMOVE_ALIGNCHUNK(o1, o0, 0x20, g2, g3, g4, g5, o4, o5, g7, g1, 24, 8, 3)
+	SMOVE_ALIGNCHUNK(o1, o0, 0x30, g2, g3, g4, g5, o4, o5, g7, g1, 24, 8, 3)
+	subcc		%o3, 64, %o3
+	add		%o1, 64, %o1
+	bne		43b
+	 add		%o0, 64, %o0
+
+	andcc		%o2, 0x30, %o3
+	be,a		1f
+	 srl		%g1, 24, %g2
+4:
+	SMOVE_ALIGNCHUNK(o1, o0, 0x00, g2, g3, g4, g5, o4, o5, g7, g1, 24, 8, 3)
+	subcc		%o3, 16, %o3
+	add		%o1, 16, %o1
+	bne		4b
+	 add		%o0, 16, %o0
+
+	srl		%g1, 24, %g2
+1:
+	stb		%g2, [%o0 + 3]
+	b		88f
+	 add		%o0, 4, %o0
+42:
+	SMOVE_ALIGNCHUNK(o1, o0, 0x00, g2, g3, g4, g5, o4, o5, g7, g1, 16, 16, -2)
+	SMOVE_ALIGNCHUNK(o1, o0, 0x10, g2, g3, g4, g5, o4, o5, g7, g1, 16, 16, -2)
+	SMOVE_ALIGNCHUNK(o1, o0, 0x20, g2, g3, g4, g5, o4, o5, g7, g1, 16, 16, -2)
+	SMOVE_ALIGNCHUNK(o1, o0, 0x30, g2, g3, g4, g5, o4, o5, g7, g1, 16, 16, -2)
+	subcc		%o3, 64, %o3
+	add		%o1, 64, %o1
+	bne		42b
+	 add		%o0, 64, %o0
+	 
+	andcc		%o2, 0x30, %o3
+	be,a		1f
+	 srl		%g1, 16, %g2
+4:
+	SMOVE_ALIGNCHUNK(o1, o0, 0x00, g2, g3, g4, g5, o4, o5, g7, g1, 16, 16, -2)
+	subcc		%o3, 16, %o3
+	add		%o1, 16, %o1
+	bne		4b
+	 add		%o0, 16, %o0
+
+	srl		%g1, 16, %g2
+1:
+	sth		%g2, [%o0 - 2]
+
+	/* Fall through */
+	 
+#endif /* FASTER_NONALIGNED */
+
+88:	/* short_end */
+
+	and		%o2, 0xe, %o3
+20:
+	sethi		%hi(89f), %o5
+	sll		%o3, 3, %o4
+	add		%o0, %o3, %o0
+	sub		%o5, %o4, %o5
+	add		%o1, %o3, %o1
+	jmpl		%o5 + %lo(89f), %g0
+	 andcc		%o2, 1, %g0
+
+	MOVE_SHORTCHUNK(o1, o0, 0x0c, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x0a, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x08, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x06, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x04, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x02, g2, g3)
+	MOVE_SHORTCHUNK(o1, o0, 0x00, g2, g3)
+
+89:	/* short_table_end */
+
+	be		1f
+	 nop
+
+	ldub		[%o1], %g2
+	stb		%g2, [%o0]
+1:
+	retl
+ 	 RETL_INSN
+
+90:	/* short_aligned_end */
+	bne		88b
+	 andcc		%o2, 8, %g0
+
+	be		1f
+	 andcc		%o2, 4, %g0
+
+	ld		[%o1 + 0x00], %g2
+	ld		[%o1 + 0x04], %g3
+	add		%o1, 8, %o1
+	st		%g2, [%o0 + 0x00]
+	st		%g3, [%o0 + 0x04]
+	add		%o0, 8, %o0
+1:
+	b		81b
+	 mov		%o2, %g1
diff --git a/arch/sparc/lib/memscan.S b/arch/sparc/lib/memscan.S
new file mode 100644
index 0000000..28e78ff
--- /dev/null
+++ b/arch/sparc/lib/memscan.S
@@ -0,0 +1,133 @@
+/* $Id: memscan.S,v 1.4 1996/09/08 02:01:20 davem Exp $
+ * memscan.S: Optimized memscan for the Sparc.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+/* In essence, this is just a fancy strlen. */
+
+#define LO_MAGIC 0x01010101
+#define HI_MAGIC 0x80808080
+
+	.text
+	.align	4
+	.globl	__memscan_zero, __memscan_generic
+	.globl	memscan
+__memscan_zero:
+	/* %o0 = addr, %o1 = size */
+	cmp	%o1, 0
+	bne,a	1f
+	 andcc	%o0, 3, %g0
+
+	retl
+	 nop
+
+1:
+	be	mzero_scan_word
+	 sethi	%hi(HI_MAGIC), %g2
+
+	ldsb	[%o0], %g3
+mzero_still_not_word_aligned:
+	cmp	%g3, 0
+	bne	1f
+	 add	%o0, 1, %o0
+
+	retl
+	 sub	%o0, 1, %o0
+
+1:
+	subcc	%o1, 1, %o1
+	bne,a	1f
+	 andcc	%o0, 3, %g0
+
+	retl
+	 nop
+
+1:
+	bne,a	mzero_still_not_word_aligned
+	 ldsb	[%o0], %g3
+
+	sethi	%hi(HI_MAGIC), %g2
+mzero_scan_word:
+	or	%g2, %lo(HI_MAGIC), %o3
+	sethi	%hi(LO_MAGIC), %g3
+	or	%g3, %lo(LO_MAGIC), %o2
+mzero_next_word:
+	ld	[%o0], %g2
+mzero_next_word_preloaded:
+	sub	%g2, %o2, %g2
+mzero_next_word_preloaded_next:
+	andcc	%g2, %o3, %g0
+	bne	mzero_byte_zero
+	 add	%o0, 4, %o0
+
+mzero_check_out_of_fuel:
+	subcc	%o1, 4, %o1
+	bg,a	1f
+	 ld	[%o0], %g2
+
+	retl
+	 nop
+
+1:
+	b	mzero_next_word_preloaded_next
+	 sub	%g2, %o2, %g2
+
+	/* Check every byte. */
+mzero_byte_zero:
+	ldsb	[%o0 - 4], %g2
+	cmp	%g2, 0
+	bne	mzero_byte_one
+	 sub	%o0, 4, %g3
+
+	retl
+	 mov	%g3, %o0
+
+mzero_byte_one:
+	ldsb	[%o0 - 3], %g2
+	cmp	%g2, 0
+	bne,a	mzero_byte_two_and_three
+	 ldsb	[%o0 - 2], %g2
+
+	retl
+	 sub	%o0, 3, %o0
+
+mzero_byte_two_and_three:
+	cmp	%g2, 0
+	bne,a	1f
+	 ldsb	[%o0 - 1], %g2
+
+	retl
+	 sub	%o0, 2, %o0
+
+1:
+	cmp	%g2, 0
+	bne,a	mzero_next_word_preloaded
+	 ld	[%o0], %g2
+
+	retl
+	 sub	%o0, 1, %o0
+
+mzero_found_it:
+	retl
+	 sub	%o0, 2, %o0
+
+memscan:
+__memscan_generic:
+	/* %o0 = addr, %o1 = c, %o2 = size */
+	cmp	%o2, 0
+	bne,a	0f
+	 ldub	[%o0], %g2
+
+	b,a	2f
+1:
+	ldub	[%o0], %g2
+0:
+	cmp	%g2, %o1
+	be	2f
+	 addcc	%o2, -1, %o2
+	bne	1b
+	 add	%o0, 1, %o0
+2:
+	retl
+	 nop
diff --git a/arch/sparc/lib/memset.S b/arch/sparc/lib/memset.S
new file mode 100644
index 0000000..a65eba4
--- /dev/null
+++ b/arch/sparc/lib/memset.S
@@ -0,0 +1,203 @@
+/* linux/arch/sparc/lib/memset.S: Sparc optimized memset, bzero and clear_user code
+ * Copyright (C) 1991,1996 Free Software Foundation
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * Returns 0, if ok, and number of bytes not yet set if exception
+ * occurs and we were called as clear_user.
+ */
+
+#include <asm/ptrace.h>
+
+/* Work around cpp -rob */
+#define ALLOC #alloc
+#define EXECINSTR #execinstr
+#define EX(x,y,a,b) 				\
+98: 	x,y;					\
+	.section .fixup,ALLOC,EXECINSTR;	\
+	.align	4;				\
+99:	ba 30f;					\
+	 a, b, %o0;				\
+	.section __ex_table,ALLOC;		\
+	.align	4;				\
+	.word	98b, 99b;			\
+	.text;					\
+	.align	4
+
+#define EXT(start,end,handler) 			\
+	.section __ex_table,ALLOC;		\
+	.align	4;				\
+	.word	start, 0, end, handler;		\
+	.text;					\
+	.align	4
+
+/* Please don't change these macros, unless you change the logic
+ * in the .fixup section below as well.
+ * Store 64 bytes at (BASE + OFFSET) using value SOURCE. */
+#define ZERO_BIG_BLOCK(base, offset, source)    \
+	std	source, [base + offset + 0x00]; \
+	std	source, [base + offset + 0x08]; \
+	std	source, [base + offset + 0x10]; \
+	std	source, [base + offset + 0x18]; \
+	std	source, [base + offset + 0x20]; \
+	std	source, [base + offset + 0x28]; \
+	std	source, [base + offset + 0x30]; \
+	std	source, [base + offset + 0x38];
+
+#define ZERO_LAST_BLOCKS(base, offset, source)	\
+	std	source, [base - offset - 0x38]; \
+	std	source, [base - offset - 0x30]; \
+	std	source, [base - offset - 0x28]; \
+	std	source, [base - offset - 0x20]; \
+	std	source, [base - offset - 0x18]; \
+	std	source, [base - offset - 0x10]; \
+	std	source, [base - offset - 0x08]; \
+	std	source, [base - offset - 0x00];
+
+	.text
+	.align 4
+
+        .globl  __bzero_begin
+__bzero_begin:
+
+	.globl	__bzero, __memset, 
+	.globl	memset
+	.globl	__memset_start, __memset_end
+__memset_start:
+__memset:
+memset:
+	and	%o1, 0xff, %g3
+	sll	%g3, 8, %g2
+	or	%g3, %g2, %g3
+	sll	%g3, 16, %g2
+	or	%g3, %g2, %g3
+	b	1f
+	 mov	%o2, %o1
+3:
+	cmp	%o2, 3
+	be	2f
+	 EX(stb	%g3, [%o0], sub %o1, 0)
+
+	cmp	%o2, 2
+	be	2f
+	 EX(stb	%g3, [%o0 + 0x01], sub %o1, 1)
+
+	EX(stb	%g3, [%o0 + 0x02], sub %o1, 2)
+2:
+	sub	%o2, 4, %o2
+	add	%o1, %o2, %o1
+	b	4f
+	 sub	%o0, %o2, %o0
+
+__bzero:
+	mov	%g0, %g3
+1:
+	cmp	%o1, 7
+	bleu	7f
+	 andcc	%o0, 3, %o2
+
+	bne	3b
+4:
+	 andcc	%o0, 4, %g0
+
+	be	2f
+	 mov	%g3, %g2
+
+	EX(st	%g3, [%o0], sub %o1, 0)
+	sub	%o1, 4, %o1
+	add	%o0, 4, %o0
+2:
+	andcc	%o1, 0xffffff80, %o3	! Now everything is 8 aligned and o1 is len to run
+	be	9f
+	 andcc	%o1, 0x78, %o2
+10:
+	ZERO_BIG_BLOCK(%o0, 0x00, %g2)
+	subcc	%o3, 128, %o3
+	ZERO_BIG_BLOCK(%o0, 0x40, %g2)
+11:
+	EXT(10b, 11b, 20f)
+	bne	10b
+	 add	%o0, 128, %o0
+
+	orcc	%o2, %g0, %g0
+9:
+	be	13f
+	 andcc	%o1, 7, %o1
+
+	srl	%o2, 1, %o3
+	set	13f, %o4
+	sub	%o4, %o3, %o4
+	jmp	%o4
+	 add	%o0, %o2, %o0
+
+12:
+	ZERO_LAST_BLOCKS(%o0, 0x48, %g2)
+	ZERO_LAST_BLOCKS(%o0, 0x08, %g2)
+13:
+	be	8f
+	 andcc	%o1, 4, %g0
+
+	be	1f
+	 andcc	%o1, 2, %g0
+
+	EX(st	%g3, [%o0], and %o1, 7)
+	add	%o0, 4, %o0
+1:
+	be	1f
+	 andcc	%o1, 1, %g0
+
+	EX(sth	%g3, [%o0], and %o1, 3)
+	add	%o0, 2, %o0
+1:
+	bne,a	8f
+	 EX(stb	%g3, [%o0], and %o1, 1)
+8:
+	retl
+	 clr	%o0
+7:
+	be	13b
+	 orcc	%o1, 0, %g0
+
+	be	0f
+8:
+	 add	%o0, 1, %o0
+	subcc	%o1, 1, %o1
+	bne,a	8b
+	 EX(stb	%g3, [%o0 - 1], add %o1, 1)
+0:
+	retl
+	 clr	%o0
+__memset_end:
+
+	.section .fixup,#alloc,#execinstr
+	.align	4
+20:
+	cmp	%g2, 8
+	bleu	1f
+	 and	%o1, 0x7f, %o1
+	sub	%g2, 9, %g2
+	add	%o3, 64, %o3
+1:
+	sll	%g2, 3, %g2
+	add	%o3, %o1, %o0
+	b 30f
+	 sub	%o0, %g2, %o0
+21:
+	mov	8, %o0
+	and	%o1, 7, %o1
+	sub	%o0, %g2, %o0
+	sll	%o0, 3, %o0
+	b 30f
+	 add	%o0, %o1, %o0
+30:
+/* %o4 is faulting address, %o5 is %pc where fault occurred */
+	save	%sp, -104, %sp
+	mov	%i5, %o0
+	mov	%i7, %o1
+	call	lookup_fault
+	 mov	%i4, %o2
+	ret
+	 restore
+
+	.globl __bzero_end
+__bzero_end:
diff --git a/arch/sparc/lib/mul.S b/arch/sparc/lib/mul.S
new file mode 100644
index 0000000..83dffbc
--- /dev/null
+++ b/arch/sparc/lib/mul.S
@@ -0,0 +1,135 @@
+/* $Id: mul.S,v 1.4 1996/09/30 02:22:32 davem Exp $
+ * mul.S:       This routine was taken from glibc-1.09 and is covered
+ *              by the GNU Library General Public License Version 2.
+ */
+
+/*
+ * Signed multiply, from Appendix E of the Sparc Version 8
+ * Architecture Manual.
+ */
+
+/*
+ * Returns %o0 * %o1 in %o1%o0 (i.e., %o1 holds the upper 32 bits of
+ * the 64-bit product).
+ *
+ * This code optimizes short (less than 13-bit) multiplies.
+ */
+
+	.globl .mul
+.mul:
+	mov	%o0, %y		! multiplier -> Y
+	andncc	%o0, 0xfff, %g0	! test bits 12..31
+	be	Lmul_shortway	! if zero, can do it the short way
+	 andcc	%g0, %g0, %o4	! zero the partial product and clear N and V
+
+	/*
+	 * Long multiply.  32 steps, followed by a final shift step.
+	 */
+	mulscc	%o4, %o1, %o4	! 1
+	mulscc	%o4, %o1, %o4	! 2
+	mulscc	%o4, %o1, %o4	! 3
+	mulscc	%o4, %o1, %o4	! 4
+	mulscc	%o4, %o1, %o4	! 5
+	mulscc	%o4, %o1, %o4	! 6
+	mulscc	%o4, %o1, %o4	! 7
+	mulscc	%o4, %o1, %o4	! 8
+	mulscc	%o4, %o1, %o4	! 9
+	mulscc	%o4, %o1, %o4	! 10
+	mulscc	%o4, %o1, %o4	! 11
+	mulscc	%o4, %o1, %o4	! 12
+	mulscc	%o4, %o1, %o4	! 13
+	mulscc	%o4, %o1, %o4	! 14
+	mulscc	%o4, %o1, %o4	! 15
+	mulscc	%o4, %o1, %o4	! 16
+	mulscc	%o4, %o1, %o4	! 17
+	mulscc	%o4, %o1, %o4	! 18
+	mulscc	%o4, %o1, %o4	! 19
+	mulscc	%o4, %o1, %o4	! 20
+	mulscc	%o4, %o1, %o4	! 21
+	mulscc	%o4, %o1, %o4	! 22
+	mulscc	%o4, %o1, %o4	! 23
+	mulscc	%o4, %o1, %o4	! 24
+	mulscc	%o4, %o1, %o4	! 25
+	mulscc	%o4, %o1, %o4	! 26
+	mulscc	%o4, %o1, %o4	! 27
+	mulscc	%o4, %o1, %o4	! 28
+	mulscc	%o4, %o1, %o4	! 29
+	mulscc	%o4, %o1, %o4	! 30
+	mulscc	%o4, %o1, %o4	! 31
+	mulscc	%o4, %o1, %o4	! 32
+	mulscc	%o4, %g0, %o4	! final shift
+
+	! If %o0 was negative, the result is
+	!	(%o0 * %o1) + (%o1 << 32))
+	! We fix that here.
+
+#if 0
+	tst	%o0
+	bge	1f
+	 rd	%y, %o0
+
+	! %o0 was indeed negative; fix upper 32 bits of result by subtracting 
+	! %o1 (i.e., return %o4 - %o1 in %o1).
+	retl
+	 sub	%o4, %o1, %o1
+
+1:
+	retl
+	 mov	%o4, %o1
+#else
+	/* Faster code adapted from tege@sics.se's code for umul.S.  */
+	sra	%o0, 31, %o2	! make mask from sign bit
+	and	%o1, %o2, %o2	! %o2 = 0 or %o1, depending on sign of %o0
+	rd	%y, %o0		! get lower half of product
+	retl
+	 sub	%o4, %o2, %o1	! subtract compensation 
+				!  and put upper half in place
+#endif
+
+Lmul_shortway:
+	/*
+	 * Short multiply.  12 steps, followed by a final shift step.
+	 * The resulting bits are off by 12 and (32-12) = 20 bit positions,
+	 * but there is no problem with %o0 being negative (unlike above).
+	 */
+	mulscc	%o4, %o1, %o4	! 1
+	mulscc	%o4, %o1, %o4	! 2
+	mulscc	%o4, %o1, %o4	! 3
+	mulscc	%o4, %o1, %o4	! 4
+	mulscc	%o4, %o1, %o4	! 5
+	mulscc	%o4, %o1, %o4	! 6
+	mulscc	%o4, %o1, %o4	! 7
+	mulscc	%o4, %o1, %o4	! 8
+	mulscc	%o4, %o1, %o4	! 9
+	mulscc	%o4, %o1, %o4	! 10
+	mulscc	%o4, %o1, %o4	! 11
+	mulscc	%o4, %o1, %o4	! 12
+	mulscc	%o4, %g0, %o4	! final shift
+
+	/*
+	 *  %o4 has 20 of the bits that should be in the low part of the
+	 * result; %y has the bottom 12 (as %y's top 12).  That is:
+	 *
+	 *	  %o4		    %y
+	 * +----------------+----------------+
+	 * | -12- |   -20-  | -12- |   -20-  |
+	 * +------(---------+------)---------+
+	 *  --hi-- ----low-part----
+	 *
+	 * The upper 12 bits of %o4 should be sign-extended to form the
+	 * high part of the product (i.e., highpart = %o4 >> 20).
+	 */
+
+	rd	%y, %o5
+	sll	%o4, 12, %o0	! shift middle bits left 12
+	srl	%o5, 20, %o5	! shift low bits right 20, zero fill at left
+	or	%o5, %o0, %o0	! construct low part of result
+	retl
+	 sra	%o4, 20, %o1	! ... and extract high part of result
+
+	.globl	.mul_patch
+.mul_patch:
+	smul	%o0, %o1, %o0
+	retl
+	 rd	%y, %o1
+	nop
diff --git a/arch/sparc/lib/muldi3.S b/arch/sparc/lib/muldi3.S
new file mode 100644
index 0000000..7f17872
--- /dev/null
+++ b/arch/sparc/lib/muldi3.S
@@ -0,0 +1,76 @@
+/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC 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, or (at your option)
+any later version.
+
+GNU CC 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 GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+	.text
+	.align 4
+	.globl __muldi3
+__muldi3:
+	save  %sp, -104, %sp
+	wr  %g0, %i1, %y
+	sra  %i3, 0x1f, %g2
+	and  %i1, %g2, %g2
+	andcc  %g0, 0, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, %i3, %g1
+	mulscc  %g1, 0, %g1
+	add  %g1, %g2, %l2
+	rd  %y, %o1
+	mov  %o1, %l3
+	mov  %i1, %o0
+	call  .umul
+	mov  %i2, %o1
+	mov  %o0, %l0
+	mov  %i0, %o0
+	call  .umul
+	mov  %i3, %o1
+	add  %l0, %o0, %l0
+	mov  %l2, %i0
+	add  %l2, %l0, %i0
+	ret 
+	restore  %g0, %l3, %o1
diff --git a/arch/sparc/lib/rem.S b/arch/sparc/lib/rem.S
new file mode 100644
index 0000000..4450814
--- /dev/null
+++ b/arch/sparc/lib/rem.S
@@ -0,0 +1,382 @@
+/* $Id: rem.S,v 1.7 1996/09/30 02:22:34 davem Exp $
+ * rem.S:       This routine was taken from glibc-1.09 and is covered
+ *              by the GNU Library General Public License Version 2.
+ */
+
+
+/* This file is generated from divrem.m4; DO NOT EDIT! */
+/*
+ * Division and remainder, from Appendix E of the Sparc Version 8
+ * Architecture Manual, with fixes from Gordon Irlam.
+ */
+
+/*
+ * Input: dividend and divisor in %o0 and %o1 respectively.
+ *
+ * m4 parameters:
+ *  .rem	name of function to generate
+ *  rem		rem=div => %o0 / %o1; rem=rem => %o0 % %o1
+ *  true		true=true => signed; true=false => unsigned
+ *
+ * Algorithm parameters:
+ *  N		how many bits per iteration we try to get (4)
+ *  WORDSIZE	total number of bits (32)
+ *
+ * Derived constants:
+ *  TOPBITS	number of bits in the top decade of a number
+ *
+ * Important variables:
+ *  Q		the partial quotient under development (initially 0)
+ *  R		the remainder so far, initially the dividend
+ *  ITER	number of main division loop iterations required;
+ *		equal to ceil(log2(quotient) / N).  Note that this
+ *		is the log base (2^N) of the quotient.
+ *  V		the current comparand, initially divisor*2^(ITER*N-1)
+ *
+ * Cost:
+ *  Current estimate for non-large dividend is
+ *	ceil(log2(quotient) / N) * (10 + 7N/2) + C
+ *  A large dividend is one greater than 2^(31-TOPBITS) and takes a
+ *  different path, as the upper bits of the quotient must be developed
+ *  one bit at a time.
+ */
+
+
+	.globl .rem
+.rem:
+	! compute sign of result; if neither is negative, no problem
+	orcc	%o1, %o0, %g0	! either negative?
+	bge	2f			! no, go do the divide
+	 mov	%o0, %g2	! compute sign in any case
+
+	tst	%o1
+	bge	1f
+	 tst	%o0
+	! %o1 is definitely negative; %o0 might also be negative
+	bge	2f			! if %o0 not negative...
+	 sub	%g0, %o1, %o1	! in any case, make %o1 nonneg
+1:	! %o0 is negative, %o1 is nonnegative
+	sub	%g0, %o0, %o0	! make %o0 nonnegative
+2:
+
+	! Ready to divide.  Compute size of quotient; scale comparand.
+	orcc	%o1, %g0, %o5
+	bne	1f
+	 mov	%o0, %o3
+
+		! Divide by zero trap.  If it returns, return 0 (about as
+		! wrong as possible, but that is what SunOS does...).
+		ta	ST_DIV0
+		retl
+		 clr	%o0
+
+1:
+	cmp	%o3, %o5			! if %o1 exceeds %o0, done
+	blu	Lgot_result		! (and algorithm fails otherwise)
+	 clr	%o2
+
+	sethi	%hi(1 << (32 - 4 - 1)), %g1
+
+	cmp	%o3, %g1
+	blu	Lnot_really_big
+	 clr	%o4
+
+	! Here the dividend is >= 2**(31-N) or so.  We must be careful here,
+	! as our usual N-at-a-shot divide step will cause overflow and havoc.
+	! The number of bits in the result here is N*ITER+SC, where SC <= N.
+	! Compute ITER in an unorthodox manner: know we need to shift V into
+	! the top decade: so do not even bother to compare to R.
+	1:
+		cmp	%o5, %g1
+		bgeu	3f
+		 mov	1, %g7
+
+		sll	%o5, 4, %o5
+
+		b	1b
+		 add	%o4, 1, %o4
+
+	! Now compute %g7.
+	2:
+		addcc	%o5, %o5, %o5
+
+		bcc	Lnot_too_big
+		 add	%g7, 1, %g7
+
+		! We get here if the %o1 overflowed while shifting.
+		! This means that %o3 has the high-order bit set.
+		! Restore %o5 and subtract from %o3.
+		sll	%g1, 4, %g1	! high order bit
+		srl	%o5, 1, %o5		! rest of %o5
+		add	%o5, %g1, %o5
+
+		b	Ldo_single_div
+		 sub	%g7, 1, %g7
+
+	Lnot_too_big:
+	3:
+		cmp	%o5, %o3
+		blu	2b
+		 nop
+
+		be	Ldo_single_div
+		 nop
+	/* NB: these are commented out in the V8-Sparc manual as well */
+	/* (I do not understand this) */
+	! %o5 > %o3: went too far: back up 1 step
+	!	srl	%o5, 1, %o5
+	!	dec	%g7
+	! do single-bit divide steps
+	!
+	! We have to be careful here.  We know that %o3 >= %o5, so we can do the
+	! first divide step without thinking.  BUT, the others are conditional,
+	! and are only done if %o3 >= 0.  Because both %o3 and %o5 may have the high-
+	! order bit set in the first step, just falling into the regular
+	! division loop will mess up the first time around.
+	! So we unroll slightly...
+	Ldo_single_div:
+		subcc	%g7, 1, %g7
+		bl	Lend_regular_divide
+		 nop
+
+		sub	%o3, %o5, %o3
+		mov	1, %o2
+
+		b	Lend_single_divloop
+		 nop
+	Lsingle_divloop:
+		sll	%o2, 1, %o2
+
+		bl	1f
+		 srl	%o5, 1, %o5
+		! %o3 >= 0
+		sub	%o3, %o5, %o3
+
+		b	2f
+		 add	%o2, 1, %o2
+	1:	! %o3 < 0
+		add	%o3, %o5, %o3
+		sub	%o2, 1, %o2
+	2:
+	Lend_single_divloop:
+		subcc	%g7, 1, %g7
+		bge	Lsingle_divloop
+		 tst	%o3
+
+		b,a	Lend_regular_divide
+
+Lnot_really_big:
+1:
+	sll	%o5, 4, %o5
+	cmp	%o5, %o3
+	bleu	1b
+	 addcc	%o4, 1, %o4
+	be	Lgot_result
+	 sub	%o4, 1, %o4
+
+	tst	%o3	! set up for initial iteration
+Ldivloop:
+	sll	%o2, 4, %o2
+		! depth 1, accumulated bits 0
+	bl	L.1.16
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 2, accumulated bits 1
+	bl	L.2.17
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 3, accumulated bits 3
+	bl	L.3.19
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 4, accumulated bits 7
+	bl	L.4.23
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+
+	b	9f
+	 add	%o2, (7*2+1), %o2
+	
+L.4.23:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (7*2-1), %o2
+	
+L.3.19:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 4, accumulated bits 5
+	bl	L.4.21
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (5*2+1), %o2
+	
+L.4.21:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (5*2-1), %o2
+	
+L.2.17:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 3, accumulated bits 1
+	bl	L.3.17
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 4, accumulated bits 3
+	bl	L.4.19
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (3*2+1), %o2
+
+L.4.19:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (3*2-1), %o2
+
+L.3.17:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 4, accumulated bits 1
+	bl	L.4.17
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (1*2+1), %o2
+
+L.4.17:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (1*2-1), %o2
+
+L.1.16:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 2, accumulated bits -1
+	bl	L.2.15
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 3, accumulated bits -1
+	bl	L.3.15
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 4, accumulated bits -1
+	bl	L.4.15
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-1*2+1), %o2
+
+L.4.15:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-1*2-1), %o2
+
+L.3.15:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 4, accumulated bits -3
+	bl	L.4.13
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-3*2+1), %o2
+
+L.4.13:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-3*2-1), %o2
+
+L.2.15:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 3, accumulated bits -3
+	bl	L.3.13
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 4, accumulated bits -5
+	bl	L.4.11
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-5*2+1), %o2
+
+L.4.11:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-5*2-1), %o2
+
+
+L.3.13:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 4, accumulated bits -7
+	bl	L.4.9
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-7*2+1), %o2
+
+L.4.9:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-7*2-1), %o2
+
+	9:
+Lend_regular_divide:
+	subcc	%o4, 1, %o4
+	bge	Ldivloop
+	 tst	%o3
+
+	bl,a	Lgot_result
+	! non-restoring fixup here (one instruction only!)
+	add	%o3, %o1, %o3
+
+Lgot_result:
+	! check to see if answer should be < 0
+	tst	%g2
+	bl,a	1f
+	 sub %g0, %o3, %o3
+1:
+	retl
+	 mov %o3, %o0
+
+	.globl	.rem_patch
+.rem_patch:
+	sra	%o0, 0x1f, %o4
+	wr	%o4, 0x0, %y
+	nop
+	nop
+	nop
+	sdivcc	%o0, %o1, %o2
+	bvs,a	1f
+	 xnor	%o2, %g0, %o2
+1:	smul	%o2, %o1, %o2
+	retl
+	 sub	%o0, %o2, %o0
+	nop
diff --git a/arch/sparc/lib/rwsem.S b/arch/sparc/lib/rwsem.S
new file mode 100644
index 0000000..e7578dc
--- /dev/null
+++ b/arch/sparc/lib/rwsem.S
@@ -0,0 +1,205 @@
+/* $Id: rwsem.S,v 1.5 2000/05/09 17:40:13 davem Exp $
+ * Assembly part of rw semaphores.
+ *
+ * Copyright (C) 1999 Jakub Jelinek (jakub@redhat.com)
+ */
+
+#include <linux/config.h>
+#include <asm/ptrace.h>
+#include <asm/psr.h>
+
+	.section .sched.text
+	.align	4
+
+	.globl		___down_read
+___down_read:
+	rd		%psr, %g3
+	nop
+	nop
+	nop
+	or		%g3, PSR_PIL, %g7
+	wr		%g7, 0, %psr
+	nop
+	nop
+	nop
+#ifdef CONFIG_SMP
+1:	ldstub		[%g1 + 4], %g7
+	tst		%g7
+	bne		1b
+	 ld		[%g1], %g7
+	sub		%g7, 1, %g7
+	st		%g7, [%g1]
+	stb		%g0, [%g1 + 4]
+#else
+	ld		[%g1], %g7
+	sub		%g7, 1, %g7
+	st		%g7, [%g1]
+#endif
+	wr		%g3, 0, %psr
+	add		%g7, 1, %g7
+	nop
+	nop
+	subcc		%g7, 1, %g7
+	bneg		3f
+	 nop
+2:	jmpl		%o7, %g0
+	 mov		%g4, %o7
+3:	save		%sp, -64, %sp
+	mov		%g1, %l1
+	mov		%g4, %l4
+	bcs		4f
+	 mov		%g5, %l5
+	call		down_read_failed
+	 mov		%l1, %o0
+	mov		%l1, %g1
+	mov		%l4, %g4
+	ba		___down_read
+	 restore	%l5, %g0, %g5
+4:	call		down_read_failed_biased
+	 mov		%l1, %o0
+	mov		%l1, %g1
+	mov		%l4, %g4
+	ba		2b
+	 restore	%l5, %g0, %g5
+
+	.globl		___down_write
+___down_write:
+	rd		%psr, %g3
+	nop
+	nop
+	nop
+	or		%g3, PSR_PIL, %g7
+	wr		%g7, 0, %psr
+	sethi		%hi(0x01000000), %g2
+	nop
+	nop
+#ifdef CONFIG_SMP
+1:	ldstub		[%g1 + 4], %g7
+	tst		%g7
+	bne		1b
+	 ld		[%g1], %g7
+	sub		%g7, %g2, %g7
+	st		%g7, [%g1]
+	stb		%g0, [%g1 + 4]
+#else
+	ld		[%g1], %g7
+	sub		%g7, %g2, %g7
+	st		%g7, [%g1]
+#endif
+	wr		%g3, 0, %psr
+	add		%g7, %g2, %g7
+	nop
+	nop
+	subcc		%g7, %g2, %g7
+	bne		3f
+	 nop
+2:	jmpl		%o7, %g0
+	 mov		%g4, %o7
+3:	save		%sp, -64, %sp
+	mov		%g1, %l1
+	mov		%g4, %l4
+	bcs		4f
+	 mov		%g5, %l5
+	call		down_write_failed
+	 mov		%l1, %o0
+	mov		%l1, %g1
+	mov		%l4, %g4
+	ba		___down_write
+	 restore	%l5, %g0, %g5
+4:	call		down_write_failed_biased
+	 mov		%l1, %o0
+	mov		%l1, %g1
+	mov		%l4, %g4
+	ba		2b
+	 restore	%l5, %g0, %g5
+
+	.text
+	.globl		___up_read
+___up_read:
+	rd		%psr, %g3
+	nop
+	nop
+	nop
+	or		%g3, PSR_PIL, %g7
+	wr		%g7, 0, %psr
+	nop
+	nop
+	nop
+#ifdef CONFIG_SMP
+1:	ldstub		[%g1 + 4], %g7
+	tst		%g7
+	bne		1b
+	 ld		[%g1], %g7
+	add		%g7, 1, %g7
+	st		%g7, [%g1]
+	stb		%g0, [%g1 + 4]
+#else
+	ld		[%g1], %g7
+	add		%g7, 1, %g7
+	st		%g7, [%g1]
+#endif
+	wr		%g3, 0, %psr
+	nop
+	nop
+	nop
+	cmp		%g7, 0
+	be		3f
+	 nop
+2:	jmpl		%o7, %g0
+	 mov		%g4, %o7
+3:	save		%sp, -64, %sp
+	mov		%g1, %l1
+	mov		%g4, %l4
+	mov		%g5, %l5
+	clr		%o1
+	call		__rwsem_wake
+	 mov		%l1, %o0
+	mov		%l1, %g1
+	mov		%l4, %g4
+	ba		2b
+	 restore	%l5, %g0, %g5
+
+	.globl		___up_write
+___up_write:
+	rd		%psr, %g3
+	nop
+	nop
+	nop
+	or		%g3, PSR_PIL, %g7
+	wr		%g7, 0, %psr
+	sethi		%hi(0x01000000), %g2
+	nop
+	nop
+#ifdef CONFIG_SMP
+1:	ldstub		[%g1 + 4], %g7
+	tst		%g7
+	bne		1b
+	 ld		[%g1], %g7
+	add		%g7, %g2, %g7
+	st		%g7, [%g1]
+	stb		%g0, [%g1 + 4]
+#else
+	ld		[%g1], %g7
+	add		%g7, %g2, %g7
+	st		%g7, [%g1]
+#endif
+	wr		%g3, 0, %psr
+	sub		%g7, %g2, %g7
+	nop
+	nop
+	addcc		%g7, %g2, %g7
+	bcs		3f
+	 nop
+2:	jmpl		%o7, %g0
+	 mov		%g4, %o7
+3:	save		%sp, -64, %sp
+	mov		%g1, %l1
+	mov		%g4, %l4
+	mov		%g5, %l5
+	mov		%g7, %o1
+	call		__rwsem_wake
+	 mov		%l1, %o0
+	mov		%l1, %g1
+	mov		%l4, %g4
+	ba		2b
+	 restore	%l5, %g0, %g5
diff --git a/arch/sparc/lib/sdiv.S b/arch/sparc/lib/sdiv.S
new file mode 100644
index 0000000..e0ad80b
--- /dev/null
+++ b/arch/sparc/lib/sdiv.S
@@ -0,0 +1,379 @@
+/* $Id: sdiv.S,v 1.6 1996/10/02 17:37:00 davem Exp $
+ * sdiv.S:      This routine was taken from glibc-1.09 and is covered
+ *              by the GNU Library General Public License Version 2.
+ */
+
+
+/* This file is generated from divrem.m4; DO NOT EDIT! */
+/*
+ * Division and remainder, from Appendix E of the Sparc Version 8
+ * Architecture Manual, with fixes from Gordon Irlam.
+ */
+
+/*
+ * Input: dividend and divisor in %o0 and %o1 respectively.
+ *
+ * m4 parameters:
+ *  .div	name of function to generate
+ *  div		div=div => %o0 / %o1; div=rem => %o0 % %o1
+ *  true		true=true => signed; true=false => unsigned
+ *
+ * Algorithm parameters:
+ *  N		how many bits per iteration we try to get (4)
+ *  WORDSIZE	total number of bits (32)
+ *
+ * Derived constants:
+ *  TOPBITS	number of bits in the top decade of a number
+ *
+ * Important variables:
+ *  Q		the partial quotient under development (initially 0)
+ *  R		the remainder so far, initially the dividend
+ *  ITER	number of main division loop iterations required;
+ *		equal to ceil(log2(quotient) / N).  Note that this
+ *		is the log base (2^N) of the quotient.
+ *  V		the current comparand, initially divisor*2^(ITER*N-1)
+ *
+ * Cost:
+ *  Current estimate for non-large dividend is
+ *	ceil(log2(quotient) / N) * (10 + 7N/2) + C
+ *  A large dividend is one greater than 2^(31-TOPBITS) and takes a
+ *  different path, as the upper bits of the quotient must be developed
+ *  one bit at a time.
+ */
+
+
+	.globl .div
+.div:
+	! compute sign of result; if neither is negative, no problem
+	orcc	%o1, %o0, %g0	! either negative?
+	bge	2f			! no, go do the divide
+	 xor	%o1, %o0, %g2	! compute sign in any case
+
+	tst	%o1
+	bge	1f
+	 tst	%o0
+	! %o1 is definitely negative; %o0 might also be negative
+	bge	2f			! if %o0 not negative...
+	 sub	%g0, %o1, %o1	! in any case, make %o1 nonneg
+1:	! %o0 is negative, %o1 is nonnegative
+	sub	%g0, %o0, %o0	! make %o0 nonnegative
+2:
+
+	! Ready to divide.  Compute size of quotient; scale comparand.
+	orcc	%o1, %g0, %o5
+	bne	1f
+	 mov	%o0, %o3
+
+		! Divide by zero trap.  If it returns, return 0 (about as
+		! wrong as possible, but that is what SunOS does...).
+		ta	ST_DIV0
+		retl
+		 clr	%o0
+
+1:
+	cmp	%o3, %o5			! if %o1 exceeds %o0, done
+	blu	Lgot_result		! (and algorithm fails otherwise)
+	 clr	%o2
+
+	sethi	%hi(1 << (32 - 4 - 1)), %g1
+
+	cmp	%o3, %g1
+	blu	Lnot_really_big
+	 clr	%o4
+
+	! Here the dividend is >= 2**(31-N) or so.  We must be careful here,
+	! as our usual N-at-a-shot divide step will cause overflow and havoc.
+	! The number of bits in the result here is N*ITER+SC, where SC <= N.
+	! Compute ITER in an unorthodox manner: know we need to shift V into
+	! the top decade: so do not even bother to compare to R.
+	1:
+		cmp	%o5, %g1
+		bgeu	3f
+		 mov	1, %g7
+
+		sll	%o5, 4, %o5
+
+		b	1b
+		 add	%o4, 1, %o4
+
+	! Now compute %g7.
+	2:
+		addcc	%o5, %o5, %o5
+		bcc	Lnot_too_big
+		 add	%g7, 1, %g7
+
+		! We get here if the %o1 overflowed while shifting.
+		! This means that %o3 has the high-order bit set.
+		! Restore %o5 and subtract from %o3.
+		sll	%g1, 4, %g1	! high order bit
+		srl	%o5, 1, %o5		! rest of %o5
+		add	%o5, %g1, %o5
+
+		b	Ldo_single_div
+		 sub	%g7, 1, %g7
+
+	Lnot_too_big:
+	3:
+		cmp	%o5, %o3
+		blu	2b
+		 nop
+
+		be	Ldo_single_div
+		 nop
+	/* NB: these are commented out in the V8-Sparc manual as well */
+	/* (I do not understand this) */
+	! %o5 > %o3: went too far: back up 1 step
+	!	srl	%o5, 1, %o5
+	!	dec	%g7
+	! do single-bit divide steps
+	!
+	! We have to be careful here.  We know that %o3 >= %o5, so we can do the
+	! first divide step without thinking.  BUT, the others are conditional,
+	! and are only done if %o3 >= 0.  Because both %o3 and %o5 may have the high-
+	! order bit set in the first step, just falling into the regular
+	! division loop will mess up the first time around.
+	! So we unroll slightly...
+	Ldo_single_div:
+		subcc	%g7, 1, %g7
+		bl	Lend_regular_divide
+		 nop
+
+		sub	%o3, %o5, %o3
+		mov	1, %o2
+
+		b	Lend_single_divloop
+		 nop
+	Lsingle_divloop:
+		sll	%o2, 1, %o2
+
+		bl	1f
+		 srl	%o5, 1, %o5
+		! %o3 >= 0
+		sub	%o3, %o5, %o3
+
+		b	2f
+		 add	%o2, 1, %o2
+	1:	! %o3 < 0
+		add	%o3, %o5, %o3
+		sub	%o2, 1, %o2
+	2:
+	Lend_single_divloop:
+		subcc	%g7, 1, %g7
+		bge	Lsingle_divloop
+		 tst	%o3
+
+		b,a	Lend_regular_divide
+
+Lnot_really_big:
+1:
+	sll	%o5, 4, %o5
+	cmp	%o5, %o3
+	bleu	1b
+	 addcc	%o4, 1, %o4
+
+	be	Lgot_result
+	 sub	%o4, 1, %o4
+
+	tst	%o3	! set up for initial iteration
+Ldivloop:
+	sll	%o2, 4, %o2
+		! depth 1, accumulated bits 0
+	bl	L.1.16
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 2, accumulated bits 1
+	bl	L.2.17
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 3, accumulated bits 3
+	bl	L.3.19
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 4, accumulated bits 7
+	bl	L.4.23
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (7*2+1), %o2
+
+L.4.23:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (7*2-1), %o2
+
+L.3.19:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 4, accumulated bits 5
+	bl	L.4.21
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (5*2+1), %o2
+
+L.4.21:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (5*2-1), %o2
+
+L.2.17:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 3, accumulated bits 1
+	bl	L.3.17
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 4, accumulated bits 3
+	bl	L.4.19
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (3*2+1), %o2
+
+L.4.19:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (3*2-1), %o2
+	
+	
+L.3.17:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 4, accumulated bits 1
+	bl	L.4.17
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (1*2+1), %o2
+
+L.4.17:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (1*2-1), %o2
+
+L.1.16:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 2, accumulated bits -1
+	bl	L.2.15
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 3, accumulated bits -1
+	bl	L.3.15
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 4, accumulated bits -1
+	bl	L.4.15
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-1*2+1), %o2
+
+L.4.15:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-1*2-1), %o2
+
+L.3.15:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 4, accumulated bits -3
+	bl	L.4.13
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-3*2+1), %o2
+
+L.4.13:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-3*2-1), %o2
+
+L.2.15:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 3, accumulated bits -3
+	bl	L.3.13
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 4, accumulated bits -5
+	bl	L.4.11
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-5*2+1), %o2
+
+L.4.11:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-5*2-1), %o2
+
+L.3.13:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 4, accumulated bits -7
+	bl	L.4.9
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-7*2+1), %o2
+
+L.4.9:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-7*2-1), %o2
+
+	9:
+Lend_regular_divide:
+	subcc	%o4, 1, %o4
+	bge	Ldivloop
+	 tst	%o3
+
+	bl,a	Lgot_result
+	! non-restoring fixup here (one instruction only!)
+	sub	%o2, 1, %o2
+
+Lgot_result:
+	! check to see if answer should be < 0
+	tst	%g2
+	bl,a	1f
+	 sub %g0, %o2, %o2
+1:
+	retl
+	 mov %o2, %o0
+
+	.globl	.div_patch
+.div_patch:
+	sra	%o0, 0x1f, %o2
+	wr	%o2, 0x0, %y
+	nop
+	nop
+	nop
+	sdivcc	%o0, %o1, %o0
+	bvs,a	1f
+	 xnor	%o0, %g0, %o0
+1:	retl
+	 nop
diff --git a/arch/sparc/lib/strlen.S b/arch/sparc/lib/strlen.S
new file mode 100644
index 0000000..ed9a763
--- /dev/null
+++ b/arch/sparc/lib/strlen.S
@@ -0,0 +1,81 @@
+/* strlen.S: Sparc optimized strlen code
+ * Hand optimized from GNU libc's strlen
+ * Copyright (C) 1991,1996 Free Software Foundation
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#define LO_MAGIC 0x01010101
+#define HI_MAGIC 0x80808080
+
+0:
+	ldub	[%o0], %o5
+	cmp	%o5, 0
+	be	1f
+	 add	%o0, 1, %o0
+	andcc	%o0, 3, %g0
+	be	4f
+	 or	%o4, %lo(HI_MAGIC), %o3
+	ldub	[%o0], %o5
+	cmp	%o5, 0
+	be	2f
+	 add	%o0, 1, %o0
+	andcc	%o0, 3, %g0
+	be	5f
+	 sethi	%hi(LO_MAGIC), %o4
+	ldub	[%o0], %o5
+	cmp	%o5, 0
+	be	3f
+	 add	%o0, 1, %o0
+	b	8f
+	 or	%o4, %lo(LO_MAGIC), %o2
+1:
+	retl
+	 mov	0, %o0
+2:
+	retl
+	 mov	1, %o0
+3:
+	retl
+	 mov	2, %o0
+
+	.align 4
+	.global strlen
+strlen:
+	mov	%o0, %o1
+	andcc	%o0, 3, %g0
+	bne	0b
+	 sethi	%hi(HI_MAGIC), %o4
+	or	%o4, %lo(HI_MAGIC), %o3
+4:
+	sethi	%hi(LO_MAGIC), %o4
+5:
+	or	%o4, %lo(LO_MAGIC), %o2
+8:
+	ld	[%o0], %o5
+2:
+	sub	%o5, %o2, %o4
+	andcc	%o4, %o3, %g0
+	be	8b
+	 add	%o0, 4, %o0
+
+	/* Check every byte. */
+	srl	%o5, 24, %g5
+	andcc	%g5, 0xff, %g0
+	be	1f
+	 add	%o0, -4, %o4
+	srl	%o5, 16, %g5
+	andcc	%g5, 0xff, %g0
+	be	1f
+	 add	%o4, 1, %o4
+	srl	%o5, 8, %g5
+	andcc	%g5, 0xff, %g0
+	be	1f
+	 add	%o4, 1, %o4
+	andcc	%o5, 0xff, %g0
+	bne,a	2b
+	 ld	[%o0], %o5
+	add	%o4, 1, %o4
+1:
+	retl
+	 sub	%o4, %o1, %o0
diff --git a/arch/sparc/lib/strlen_user.S b/arch/sparc/lib/strlen_user.S
new file mode 100644
index 0000000..8c8a371
--- /dev/null
+++ b/arch/sparc/lib/strlen_user.S
@@ -0,0 +1,109 @@
+/* strlen_user.S: Sparc optimized strlen_user code
+ *
+ * Return length of string in userspace including terminating 0
+ * or 0 for error
+ *
+ * Copyright (C) 1991,1996 Free Software Foundation
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#define LO_MAGIC 0x01010101
+#define HI_MAGIC 0x80808080
+
+10:
+	ldub	[%o0], %o5
+	cmp	%o5, 0
+	be	1f
+	 add	%o0, 1, %o0
+	andcc	%o0, 3, %g0
+	be	4f
+	 or	%o4, %lo(HI_MAGIC), %o3
+11:
+	ldub	[%o0], %o5
+	cmp	%o5, 0
+	be	2f
+	 add	%o0, 1, %o0
+	andcc	%o0, 3, %g0
+	be	5f
+	 sethi	%hi(LO_MAGIC), %o4
+12:
+	ldub	[%o0], %o5
+	cmp	%o5, 0
+	be	3f
+	 add	%o0, 1, %o0
+	b	13f
+	 or	%o4, %lo(LO_MAGIC), %o2
+1:
+	retl
+	 mov	1, %o0
+2:
+	retl
+	 mov	2, %o0
+3:
+	retl
+	 mov	3, %o0
+
+	.align 4
+	.global __strlen_user, __strnlen_user
+__strlen_user:
+	sethi	%hi(32768), %o1
+__strnlen_user:
+	mov	%o1, %g1
+	mov	%o0, %o1
+	andcc	%o0, 3, %g0
+	bne	10b
+	 sethi	%hi(HI_MAGIC), %o4
+	or	%o4, %lo(HI_MAGIC), %o3
+4:
+	sethi	%hi(LO_MAGIC), %o4
+5:
+	or	%o4, %lo(LO_MAGIC), %o2
+13:
+	ld	[%o0], %o5
+2:
+	sub	%o5, %o2, %o4
+	andcc	%o4, %o3, %g0
+	bne	82f
+	 add	%o0, 4, %o0
+	sub	%o0, %o1, %g2
+81:	cmp	%g2, %g1
+	blu	13b
+	 mov	%o0, %o4
+	ba,a	1f
+
+	/* Check every byte. */
+82:	srl	%o5, 24, %g5
+	andcc	%g5, 0xff, %g0
+	be	1f
+	 add	%o0, -3, %o4
+	srl	%o5, 16, %g5
+	andcc	%g5, 0xff, %g0
+	be	1f
+	 add	%o4, 1, %o4
+	srl	%o5, 8, %g5
+	andcc	%g5, 0xff, %g0
+	be	1f
+	 add	%o4, 1, %o4
+	andcc	%o5, 0xff, %g0
+	bne	81b
+	 sub	%o0, %o1, %g2
+
+	add	%o4, 1, %o4
+1:
+	retl
+	 sub	%o4, %o1, %o0
+
+	.section .fixup,#alloc,#execinstr
+	.align	4
+9:
+	retl
+	 clr	%o0
+
+	.section __ex_table,#alloc
+	.align	4
+
+	.word	10b, 9b
+	.word	11b, 9b
+	.word	12b, 9b
+	.word	13b, 9b
diff --git a/arch/sparc/lib/strncmp.S b/arch/sparc/lib/strncmp.S
new file mode 100644
index 0000000..6156268
--- /dev/null
+++ b/arch/sparc/lib/strncmp.S
@@ -0,0 +1,118 @@
+/* $Id: strncmp.S,v 1.2 1996/09/09 02:47:20 davem Exp $
+ * strncmp.S: Hand optimized Sparc assembly of GCC output from GNU libc
+ *            generic strncmp routine.
+ */
+
+	.text
+	.align 4
+	.global __strncmp, strncmp
+__strncmp:
+strncmp:
+	mov	%o0, %g3
+	mov	0, %o3
+
+	cmp	%o2, 3
+	ble	7f
+	 mov	0, %g2
+
+	sra	%o2, 2, %o4
+	ldub	[%g3], %o3
+
+0:
+	ldub	[%o1], %g2
+	add	%g3, 1, %g3
+	and	%o3, 0xff, %o0
+
+	cmp	%o0, 0
+	be	8f
+	 add	%o1, 1, %o1
+
+	cmp	%o0, %g2
+	be,a	1f
+	 ldub	[%g3], %o3
+
+	retl
+	 sub	%o0, %g2, %o0
+
+1:
+	ldub	[%o1], %g2
+	add	%g3,1, %g3
+	and	%o3, 0xff, %o0
+
+	cmp	%o0, 0
+	be	8f
+	 add	%o1, 1, %o1
+
+	cmp	%o0, %g2
+	be,a	1f
+	 ldub	[%g3], %o3
+
+	retl
+	 sub	%o0, %g2, %o0
+
+1:
+	ldub	[%o1], %g2
+	add	%g3, 1, %g3
+	and	%o3, 0xff, %o0
+
+	cmp	%o0, 0
+	be	8f
+	 add	%o1, 1, %o1
+
+	cmp	%o0, %g2
+	be,a	1f
+	 ldub	[%g3], %o3
+
+	retl
+	 sub	%o0, %g2, %o0
+
+1:
+	ldub	[%o1], %g2
+	add	%g3, 1, %g3
+	and	%o3, 0xff, %o0
+
+	cmp	%o0, 0
+	be	8f
+	 add	%o1, 1, %o1
+
+	cmp	%o0, %g2
+	be	1f
+	 add	%o4, -1, %o4
+
+	retl
+	 sub	%o0, %g2, %o0
+
+1:
+
+	cmp	%o4, 0
+	bg,a	0b
+	 ldub	[%g3], %o3
+
+	b	7f
+	 and	%o2, 3, %o2
+
+9:
+	ldub	[%o1], %g2
+	add	%g3, 1, %g3
+	and	%o3, 0xff, %o0
+
+	cmp	%o0, 0
+	be	8f
+	 add	%o1, 1, %o1
+
+	cmp	%o0, %g2
+	be	7f
+	 add	%o2, -1, %o2
+
+8:
+	retl
+	 sub	%o0, %g2, %o0
+
+7:
+	cmp	%o2, 0
+	bg,a	9b
+	 ldub	[%g3], %o3
+
+	and	%g2, 0xff, %o0
+	retl
+	 sub	%o3, %o0, %o0
diff --git a/arch/sparc/lib/strncpy_from_user.S b/arch/sparc/lib/strncpy_from_user.S
new file mode 100644
index 0000000..d771989
--- /dev/null
+++ b/arch/sparc/lib/strncpy_from_user.S
@@ -0,0 +1,47 @@
+/* strncpy_from_user.S: Sparc strncpy from userspace.
+ *
+ *  Copyright(C) 1996 David S. Miller
+ */
+
+#include <asm/ptrace.h>
+#include <asm/errno.h>
+
+	.text
+	.align	4
+
+	/* Must return:
+	 *
+	 * -EFAULT		for an exception
+	 * count		if we hit the buffer limit
+	 * bytes copied		if we hit a null byte
+	 */
+
+	.globl	__strncpy_from_user
+__strncpy_from_user:
+	/* %o0=dest, %o1=src, %o2=count */
+	mov	%o2, %o3
+1:
+	subcc	%o2, 1, %o2
+	bneg	2f
+	 nop
+10:
+	ldub	[%o1], %o4
+	add	%o0, 1, %o0
+	cmp	%o4, 0
+	add	%o1, 1, %o1
+	bne	1b
+	 stb	%o4, [%o0 - 1]
+2:
+	add	%o2, 1, %o0
+	retl
+	 sub	%o3, %o0, %o0
+
+	.section .fixup,#alloc,#execinstr
+	.align	4
+4:
+	retl
+	 mov	-EFAULT, %o0
+
+	.section __ex_table,#alloc
+	.align	4
+	.word	10b, 4b
diff --git a/arch/sparc/lib/udiv.S b/arch/sparc/lib/udiv.S
new file mode 100644
index 0000000..2abfc6b
--- /dev/null
+++ b/arch/sparc/lib/udiv.S
@@ -0,0 +1,355 @@
+/* $Id: udiv.S,v 1.4 1996/09/30 02:22:38 davem Exp $
+ * udiv.S:      This routine was taken from glibc-1.09 and is covered
+ *              by the GNU Library General Public License Version 2.
+ */
+
+
+/* This file is generated from divrem.m4; DO NOT EDIT! */
+/*
+ * Division and remainder, from Appendix E of the Sparc Version 8
+ * Architecture Manual, with fixes from Gordon Irlam.
+ */
+
+/*
+ * Input: dividend and divisor in %o0 and %o1 respectively.
+ *
+ * m4 parameters:
+ *  .udiv	name of function to generate
+ *  div		div=div => %o0 / %o1; div=rem => %o0 % %o1
+ *  false		false=true => signed; false=false => unsigned
+ *
+ * Algorithm parameters:
+ *  N		how many bits per iteration we try to get (4)
+ *  WORDSIZE	total number of bits (32)
+ *
+ * Derived constants:
+ *  TOPBITS	number of bits in the top decade of a number
+ *
+ * Important variables:
+ *  Q		the partial quotient under development (initially 0)
+ *  R		the remainder so far, initially the dividend
+ *  ITER	number of main division loop iterations required;
+ *		equal to ceil(log2(quotient) / N).  Note that this
+ *		is the log base (2^N) of the quotient.
+ *  V		the current comparand, initially divisor*2^(ITER*N-1)
+ *
+ * Cost:
+ *  Current estimate for non-large dividend is
+ *	ceil(log2(quotient) / N) * (10 + 7N/2) + C
+ *  A large dividend is one greater than 2^(31-TOPBITS) and takes a
+ *  different path, as the upper bits of the quotient must be developed
+ *  one bit at a time.
+ */
+
+
+	.globl .udiv
+.udiv:
+
+	! Ready to divide.  Compute size of quotient; scale comparand.
+	orcc	%o1, %g0, %o5
+	bne	1f
+	 mov	%o0, %o3
+
+		! Divide by zero trap.  If it returns, return 0 (about as
+		! wrong as possible, but that is what SunOS does...).
+		ta	ST_DIV0
+		retl
+		 clr	%o0
+
+1:
+	cmp	%o3, %o5			! if %o1 exceeds %o0, done
+	blu	Lgot_result		! (and algorithm fails otherwise)
+	 clr	%o2
+
+	sethi	%hi(1 << (32 - 4 - 1)), %g1
+
+	cmp	%o3, %g1
+	blu	Lnot_really_big
+	 clr	%o4
+
+	! Here the dividend is >= 2**(31-N) or so.  We must be careful here,
+	! as our usual N-at-a-shot divide step will cause overflow and havoc.
+	! The number of bits in the result here is N*ITER+SC, where SC <= N.
+	! Compute ITER in an unorthodox manner: know we need to shift V into
+	! the top decade: so do not even bother to compare to R.
+	1:
+		cmp	%o5, %g1
+		bgeu	3f
+		 mov	1, %g7
+
+		sll	%o5, 4, %o5
+
+		b	1b
+		 add	%o4, 1, %o4
+
+	! Now compute %g7.
+	2:
+		addcc	%o5, %o5, %o5
+		bcc	Lnot_too_big
+		 add	%g7, 1, %g7
+
+		! We get here if the %o1 overflowed while shifting.
+		! This means that %o3 has the high-order bit set.
+		! Restore %o5 and subtract from %o3.
+		sll	%g1, 4, %g1	! high order bit
+		srl	%o5, 1, %o5		! rest of %o5
+		add	%o5, %g1, %o5
+
+		b	Ldo_single_div
+		 sub	%g7, 1, %g7
+
+	Lnot_too_big:
+	3:
+		cmp	%o5, %o3
+		blu	2b
+		 nop
+
+		be	Ldo_single_div
+		 nop
+	/* NB: these are commented out in the V8-Sparc manual as well */
+	/* (I do not understand this) */
+	! %o5 > %o3: went too far: back up 1 step
+	!	srl	%o5, 1, %o5
+	!	dec	%g7
+	! do single-bit divide steps
+	!
+	! We have to be careful here.  We know that %o3 >= %o5, so we can do the
+	! first divide step without thinking.  BUT, the others are conditional,
+	! and are only done if %o3 >= 0.  Because both %o3 and %o5 may have the high-
+	! order bit set in the first step, just falling into the regular
+	! division loop will mess up the first time around.
+	! So we unroll slightly...
+	Ldo_single_div:
+		subcc	%g7, 1, %g7
+		bl	Lend_regular_divide
+		 nop
+
+		sub	%o3, %o5, %o3
+		mov	1, %o2
+
+		b	Lend_single_divloop
+		 nop
+	Lsingle_divloop:
+		sll	%o2, 1, %o2
+		bl	1f
+		 srl	%o5, 1, %o5
+		! %o3 >= 0
+		sub	%o3, %o5, %o3
+		b	2f
+		 add	%o2, 1, %o2
+	1:	! %o3 < 0
+		add	%o3, %o5, %o3
+		sub	%o2, 1, %o2
+	2:
+	Lend_single_divloop:
+		subcc	%g7, 1, %g7
+		bge	Lsingle_divloop
+		 tst	%o3
+
+		b,a	Lend_regular_divide
+
+Lnot_really_big:
+1:
+	sll	%o5, 4, %o5
+
+	cmp	%o5, %o3
+	bleu	1b
+	 addcc	%o4, 1, %o4
+
+	be	Lgot_result
+	 sub	%o4, 1, %o4
+
+	tst	%o3	! set up for initial iteration
+Ldivloop:
+	sll	%o2, 4, %o2
+		! depth 1, accumulated bits 0
+	bl	L.1.16
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 2, accumulated bits 1
+	bl	L.2.17
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 3, accumulated bits 3
+	bl	L.3.19
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 4, accumulated bits 7
+	bl	L.4.23
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (7*2+1), %o2
+
+L.4.23:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (7*2-1), %o2
+
+L.3.19:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 4, accumulated bits 5
+	bl	L.4.21
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (5*2+1), %o2
+
+L.4.21:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (5*2-1), %o2
+
+L.2.17:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 3, accumulated bits 1
+	bl	L.3.17
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 4, accumulated bits 3
+	bl	L.4.19
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (3*2+1), %o2
+
+L.4.19:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (3*2-1), %o2
+
+L.3.17:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 4, accumulated bits 1
+	bl	L.4.17
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (1*2+1), %o2
+
+L.4.17:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (1*2-1), %o2
+
+L.1.16:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 2, accumulated bits -1
+	bl	L.2.15
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 3, accumulated bits -1
+	bl	L.3.15
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 4, accumulated bits -1
+	bl	L.4.15
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-1*2+1), %o2
+
+L.4.15:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-1*2-1), %o2
+
+L.3.15:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 4, accumulated bits -3
+	bl	L.4.13
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-3*2+1), %o2
+
+L.4.13:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-3*2-1), %o2
+
+L.2.15:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 3, accumulated bits -3
+	bl	L.3.13
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 4, accumulated bits -5
+	bl	L.4.11
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-5*2+1), %o2
+
+L.4.11:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-5*2-1), %o2
+
+L.3.13:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 4, accumulated bits -7
+	bl	L.4.9
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-7*2+1), %o2
+
+L.4.9:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-7*2-1), %o2
+
+	9:
+Lend_regular_divide:
+	subcc	%o4, 1, %o4
+	bge	Ldivloop
+	 tst	%o3
+
+	bl,a	Lgot_result
+	! non-restoring fixup here (one instruction only!)
+	sub	%o2, 1, %o2
+
+Lgot_result:
+
+	retl
+	 mov %o2, %o0
+
+	.globl	.udiv_patch
+.udiv_patch:
+	wr	%g0, 0x0, %y
+	nop
+	nop
+	retl
+	 udiv	%o0, %o1, %o0
+	nop
diff --git a/arch/sparc/lib/udivdi3.S b/arch/sparc/lib/udivdi3.S
new file mode 100644
index 0000000..b430f1f
--- /dev/null
+++ b/arch/sparc/lib/udivdi3.S
@@ -0,0 +1,258 @@
+/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC 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, or (at your option)
+any later version.
+
+GNU CC 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 GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+	.text
+	.align 4
+	.globl __udivdi3
+__udivdi3:
+	save %sp,-104,%sp
+	mov %i3,%o3
+	cmp %i2,0
+	bne .LL40
+	mov %i1,%i3
+	cmp %o3,%i0
+	bleu .LL41
+	mov %i3,%o1
+	! Inlined udiv_qrnnd
+	mov	32,%g1
+	subcc	%i0,%o3,%g0
+1:	bcs	5f
+	 addxcc %o1,%o1,%o1	! shift n1n0 and a q-bit in lsb
+	sub	%i0,%o3,%i0	! this kills msb of n
+	addx	%i0,%i0,%i0	! so this cannot give carry
+	subcc	%g1,1,%g1
+2:	bne	1b
+	 subcc	%i0,%o3,%g0
+	bcs	3f
+	 addxcc %o1,%o1,%o1	! shift n1n0 and a q-bit in lsb
+	b	3f
+	 sub	%i0,%o3,%i0	! this kills msb of n
+4:	sub	%i0,%o3,%i0
+5:	addxcc	%i0,%i0,%i0
+	bcc	2b
+	 subcc	%g1,1,%g1
+! Got carry from n.  Subtract next step to cancel this carry.
+	bne	4b
+	 addcc	%o1,%o1,%o1	! shift n1n0 and a 0-bit in lsb
+	sub	%i0,%o3,%i0
+3:	xnor	%o1,0,%o1
+	! End of inline udiv_qrnnd
+	b .LL45
+	mov 0,%o2
+.LL41:
+	cmp %o3,0
+	bne .LL77
+	mov %i0,%o2
+	mov 1,%o0
+	call .udiv,0
+	mov 0,%o1
+	mov %o0,%o3
+	mov %i0,%o2
+.LL77:
+	mov 0,%o4
+	! Inlined udiv_qrnnd
+	mov	32,%g1
+	subcc	%o4,%o3,%g0
+1:	bcs	5f
+	 addxcc %o2,%o2,%o2	! shift n1n0 and a q-bit in lsb
+	sub	%o4,%o3,%o4	! this kills msb of n
+	addx	%o4,%o4,%o4	! so this cannot give carry
+	subcc	%g1,1,%g1
+2:	bne	1b
+	 subcc	%o4,%o3,%g0
+	bcs	3f
+	 addxcc %o2,%o2,%o2	! shift n1n0 and a q-bit in lsb
+	b	3f
+	 sub	%o4,%o3,%o4	! this kills msb of n
+4:	sub	%o4,%o3,%o4
+5:	addxcc	%o4,%o4,%o4
+	bcc	2b
+	 subcc	%g1,1,%g1
+! Got carry from n.  Subtract next step to cancel this carry.
+	bne	4b
+	 addcc	%o2,%o2,%o2	! shift n1n0 and a 0-bit in lsb
+	sub	%o4,%o3,%o4
+3:	xnor	%o2,0,%o2
+	! End of inline udiv_qrnnd
+	mov %o4,%i0
+	mov %i3,%o1
+	! Inlined udiv_qrnnd
+	mov	32,%g1
+	subcc	%i0,%o3,%g0
+1:	bcs	5f
+	 addxcc %o1,%o1,%o1	! shift n1n0 and a q-bit in lsb
+	sub	%i0,%o3,%i0	! this kills msb of n
+	addx	%i0,%i0,%i0	! so this cannot give carry
+	subcc	%g1,1,%g1
+2:	bne	1b
+	 subcc	%i0,%o3,%g0
+	bcs	3f
+	 addxcc %o1,%o1,%o1	! shift n1n0 and a q-bit in lsb
+	b	3f
+	 sub	%i0,%o3,%i0	! this kills msb of n
+4:	sub	%i0,%o3,%i0
+5:	addxcc	%i0,%i0,%i0
+	bcc	2b
+	 subcc	%g1,1,%g1
+! Got carry from n.  Subtract next step to cancel this carry.
+	bne	4b
+	 addcc	%o1,%o1,%o1	! shift n1n0 and a 0-bit in lsb
+	sub	%i0,%o3,%i0
+3:	xnor	%o1,0,%o1
+	! End of inline udiv_qrnnd
+	b .LL78
+	mov %o1,%l1
+.LL40:
+	cmp %i2,%i0
+	bleu .LL46
+	sethi %hi(65535),%o0
+	b .LL73
+	mov 0,%o1
+.LL46:
+	or %o0,%lo(65535),%o0
+	cmp %i2,%o0
+	bgu .LL53
+	mov %i2,%o1
+	cmp %i2,256
+	addx %g0,-1,%o0
+	b .LL59
+	and %o0,8,%o2
+.LL53:
+	sethi %hi(16777215),%o0
+	or %o0,%lo(16777215),%o0
+	cmp %o1,%o0
+	bgu .LL59
+	mov 24,%o2
+	mov 16,%o2
+.LL59:
+	srl %o1,%o2,%o1
+	sethi %hi(__clz_tab),%o0
+	or %o0,%lo(__clz_tab),%o0
+	ldub [%o1+%o0],%o0
+	add %o0,%o2,%o0
+	mov 32,%o1
+	subcc %o1,%o0,%o2
+	bne,a .LL67
+	mov 32,%o0
+	cmp %i0,%i2
+	bgu .LL69
+	cmp %i3,%o3
+	blu .LL73
+	mov 0,%o1
+.LL69:
+	b .LL73
+	mov 1,%o1
+.LL67:
+	sub %o0,%o2,%o0
+	sll %i2,%o2,%i2
+	srl %o3,%o0,%o1
+	or %i2,%o1,%i2
+	sll %o3,%o2,%o3
+	srl %i0,%o0,%o1
+	sll %i0,%o2,%i0
+	srl %i3,%o0,%o0
+	or %i0,%o0,%i0
+	sll %i3,%o2,%i3
+	mov %i0,%o5
+	mov %o1,%o4
+	! Inlined udiv_qrnnd
+	mov	32,%g1
+	subcc	%o4,%i2,%g0
+1:	bcs	5f
+	 addxcc %o5,%o5,%o5	! shift n1n0 and a q-bit in lsb
+	sub	%o4,%i2,%o4	! this kills msb of n
+	addx	%o4,%o4,%o4	! so this cannot give carry
+	subcc	%g1,1,%g1
+2:	bne	1b
+	 subcc	%o4,%i2,%g0
+	bcs	3f
+	 addxcc %o5,%o5,%o5	! shift n1n0 and a q-bit in lsb
+	b	3f
+	 sub	%o4,%i2,%o4	! this kills msb of n
+4:	sub	%o4,%i2,%o4
+5:	addxcc	%o4,%o4,%o4
+	bcc	2b
+	 subcc	%g1,1,%g1
+! Got carry from n.  Subtract next step to cancel this carry.
+	bne	4b
+	 addcc	%o5,%o5,%o5	! shift n1n0 and a 0-bit in lsb
+	sub	%o4,%i2,%o4
+3:	xnor	%o5,0,%o5
+	! End of inline udiv_qrnnd
+	mov %o4,%i0
+	mov %o5,%o1
+	! Inlined umul_ppmm
+	wr	%g0,%o1,%y	! SPARC has 0-3 delay insn after a wr
+	sra	%o3,31,%g2	! Do not move this insn
+	and	%o1,%g2,%g2	! Do not move this insn
+	andcc	%g0,0,%g1	! Do not move this insn
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,%o3,%g1
+	mulscc	%g1,0,%g1
+	add	%g1,%g2,%o0
+	rd	%y,%o2
+	cmp %o0,%i0
+	bgu,a .LL73
+	add %o1,-1,%o1
+	bne,a .LL45
+	mov 0,%o2
+	cmp %o2,%i3
+	bleu .LL45
+	mov 0,%o2
+	add %o1,-1,%o1
+.LL73:
+	mov 0,%o2
+.LL45:
+	mov %o1,%l1
+.LL78:
+	mov %o2,%l0
+	mov %l0,%i0
+	mov %l1,%i1
+	ret
+	restore
diff --git a/arch/sparc/lib/umul.S b/arch/sparc/lib/umul.S
new file mode 100644
index 0000000..a784720
--- /dev/null
+++ b/arch/sparc/lib/umul.S
@@ -0,0 +1,169 @@
+/* $Id: umul.S,v 1.4 1996/09/30 02:22:39 davem Exp $
+ * umul.S:      This routine was taken from glibc-1.09 and is covered
+ *              by the GNU Library General Public License Version 2.
+ */
+
+
+/*
+ * Unsigned multiply.  Returns %o0 * %o1 in %o1%o0 (i.e., %o1 holds the
+ * upper 32 bits of the 64-bit product).
+ *
+ * This code optimizes short (less than 13-bit) multiplies.  Short
+ * multiplies require 25 instruction cycles, and long ones require
+ * 45 instruction cycles.
+ *
+ * On return, overflow has occurred (%o1 is not zero) if and only if
+ * the Z condition code is clear, allowing, e.g., the following:
+ *
+ *	call	.umul
+ *	nop
+ *	bnz	overflow	(or tnz)
+ */
+
+	.globl .umul
+.umul:
+	or	%o0, %o1, %o4
+	mov	%o0, %y		! multiplier -> Y
+
+	andncc	%o4, 0xfff, %g0	! test bits 12..31 of *both* args
+	be	Lmul_shortway	! if zero, can do it the short way
+	 andcc	%g0, %g0, %o4	! zero the partial product and clear N and V
+
+	/*
+	 * Long multiply.  32 steps, followed by a final shift step.
+	 */
+	mulscc	%o4, %o1, %o4	! 1
+	mulscc	%o4, %o1, %o4	! 2
+	mulscc	%o4, %o1, %o4	! 3
+	mulscc	%o4, %o1, %o4	! 4
+	mulscc	%o4, %o1, %o4	! 5
+	mulscc	%o4, %o1, %o4	! 6
+	mulscc	%o4, %o1, %o4	! 7
+	mulscc	%o4, %o1, %o4	! 8
+	mulscc	%o4, %o1, %o4	! 9
+	mulscc	%o4, %o1, %o4	! 10
+	mulscc	%o4, %o1, %o4	! 11
+	mulscc	%o4, %o1, %o4	! 12
+	mulscc	%o4, %o1, %o4	! 13
+	mulscc	%o4, %o1, %o4	! 14
+	mulscc	%o4, %o1, %o4	! 15
+	mulscc	%o4, %o1, %o4	! 16
+	mulscc	%o4, %o1, %o4	! 17
+	mulscc	%o4, %o1, %o4	! 18
+	mulscc	%o4, %o1, %o4	! 19
+	mulscc	%o4, %o1, %o4	! 20
+	mulscc	%o4, %o1, %o4	! 21
+	mulscc	%o4, %o1, %o4	! 22
+	mulscc	%o4, %o1, %o4	! 23
+	mulscc	%o4, %o1, %o4	! 24
+	mulscc	%o4, %o1, %o4	! 25
+	mulscc	%o4, %o1, %o4	! 26
+	mulscc	%o4, %o1, %o4	! 27
+	mulscc	%o4, %o1, %o4	! 28
+	mulscc	%o4, %o1, %o4	! 29
+	mulscc	%o4, %o1, %o4	! 30
+	mulscc	%o4, %o1, %o4	! 31
+	mulscc	%o4, %o1, %o4	! 32
+	mulscc	%o4, %g0, %o4	! final shift
+
+
+	/*
+	 * Normally, with the shift-and-add approach, if both numbers are
+	 * positive you get the correct result.  With 32-bit two's-complement
+	 * numbers, -x is represented as
+	 *
+	 *		  x		    32
+	 *	( 2  -  ------ ) mod 2  *  2
+	 *		   32
+	 *		  2
+	 *
+	 * (the `mod 2' subtracts 1 from 1.bbbb).  To avoid lots of 2^32s,
+	 * we can treat this as if the radix point were just to the left
+	 * of the sign bit (multiply by 2^32), and get
+	 *
+	 *	-x  =  (2 - x) mod 2
+	 *
+	 * Then, ignoring the `mod 2's for convenience:
+	 *
+	 *   x *  y	= xy
+	 *  -x *  y	= 2y - xy
+	 *   x * -y	= 2x - xy
+	 *  -x * -y	= 4 - 2x - 2y + xy
+	 *
+	 * For signed multiplies, we subtract (x << 32) from the partial
+	 * product to fix this problem for negative multipliers (see mul.s).
+	 * Because of the way the shift into the partial product is calculated
+	 * (N xor V), this term is automatically removed for the multiplicand,
+	 * so we don't have to adjust.
+	 *
+	 * But for unsigned multiplies, the high order bit wasn't a sign bit,
+	 * and the correction is wrong.  So for unsigned multiplies where the
+	 * high order bit is one, we end up with xy - (y << 32).  To fix it
+	 * we add y << 32.
+	 */
+#if 0
+	tst	%o1
+	bl,a	1f		! if %o1 < 0 (high order bit = 1),
+	 add	%o4, %o0, %o4	! %o4 += %o0 (add y to upper half)
+
+1:
+	rd	%y, %o0		! get lower half of product
+	retl
+	 addcc	%o4, %g0, %o1	! put upper half in place and set Z for %o1==0
+#else
+	/* Faster code from tege@sics.se.  */
+	sra	%o1, 31, %o2	! make mask from sign bit
+	and	%o0, %o2, %o2	! %o2 = 0 or %o0, depending on sign of %o1
+	rd	%y, %o0		! get lower half of product
+	retl
+	 addcc	%o4, %o2, %o1	! add compensation and put upper half in place
+#endif
+
+Lmul_shortway:
+	/*
+	 * Short multiply.  12 steps, followed by a final shift step.
+	 * The resulting bits are off by 12 and (32-12) = 20 bit positions,
+	 * but there is no problem with %o0 being negative (unlike above),
+	 * and overflow is impossible (the answer is at most 24 bits long).
+	 */
+	mulscc	%o4, %o1, %o4	! 1
+	mulscc	%o4, %o1, %o4	! 2
+	mulscc	%o4, %o1, %o4	! 3
+	mulscc	%o4, %o1, %o4	! 4
+	mulscc	%o4, %o1, %o4	! 5
+	mulscc	%o4, %o1, %o4	! 6
+	mulscc	%o4, %o1, %o4	! 7
+	mulscc	%o4, %o1, %o4	! 8
+	mulscc	%o4, %o1, %o4	! 9
+	mulscc	%o4, %o1, %o4	! 10
+	mulscc	%o4, %o1, %o4	! 11
+	mulscc	%o4, %o1, %o4	! 12
+	mulscc	%o4, %g0, %o4	! final shift
+
+	/*
+	 * %o4 has 20 of the bits that should be in the result; %y has
+	 * the bottom 12 (as %y's top 12).  That is:
+	 *
+	 *	  %o4		    %y
+	 * +----------------+----------------+
+	 * | -12- |   -20-  | -12- |   -20-  |
+	 * +------(---------+------)---------+
+	 *	   -----result-----
+	 *
+	 * The 12 bits of %o4 left of the `result' area are all zero;
+	 * in fact, all top 20 bits of %o4 are zero.
+	 */
+
+	rd	%y, %o5
+	sll	%o4, 12, %o0	! shift middle bits left 12
+	srl	%o5, 20, %o5	! shift low bits right 20
+	or	%o5, %o0, %o0
+	retl
+	 addcc	%g0, %g0, %o1	! %o1 = zero, and set Z
+
+	.globl	.umul_patch
+.umul_patch:
+	umul	%o0, %o1, %o0
+	retl
+	 rd	%y, %o1
+	nop
diff --git a/arch/sparc/lib/urem.S b/arch/sparc/lib/urem.S
new file mode 100644
index 0000000..ec7f0c5
--- /dev/null
+++ b/arch/sparc/lib/urem.S
@@ -0,0 +1,355 @@
+/* $Id: urem.S,v 1.4 1996/09/30 02:22:42 davem Exp $
+ * urem.S:      This routine was taken from glibc-1.09 and is covered
+ *              by the GNU Library General Public License Version 2.
+ */
+
+/* This file is generated from divrem.m4; DO NOT EDIT! */
+/*
+ * Division and remainder, from Appendix E of the Sparc Version 8
+ * Architecture Manual, with fixes from Gordon Irlam.
+ */
+
+/*
+ * Input: dividend and divisor in %o0 and %o1 respectively.
+ *
+ * m4 parameters:
+ *  .urem	name of function to generate
+ *  rem		rem=div => %o0 / %o1; rem=rem => %o0 % %o1
+ *  false		false=true => signed; false=false => unsigned
+ *
+ * Algorithm parameters:
+ *  N		how many bits per iteration we try to get (4)
+ *  WORDSIZE	total number of bits (32)
+ *
+ * Derived constants:
+ *  TOPBITS	number of bits in the top decade of a number
+ *
+ * Important variables:
+ *  Q		the partial quotient under development (initially 0)
+ *  R		the remainder so far, initially the dividend
+ *  ITER	number of main division loop iterations required;
+ *		equal to ceil(log2(quotient) / N).  Note that this
+ *		is the log base (2^N) of the quotient.
+ *  V		the current comparand, initially divisor*2^(ITER*N-1)
+ *
+ * Cost:
+ *  Current estimate for non-large dividend is
+ *	ceil(log2(quotient) / N) * (10 + 7N/2) + C
+ *  A large dividend is one greater than 2^(31-TOPBITS) and takes a
+ *  different path, as the upper bits of the quotient must be developed
+ *  one bit at a time.
+ */
+
+	.globl .urem
+.urem:
+
+	! Ready to divide.  Compute size of quotient; scale comparand.
+	orcc	%o1, %g0, %o5
+	bne	1f
+	 mov	%o0, %o3
+
+		! Divide by zero trap.  If it returns, return 0 (about as
+		! wrong as possible, but that is what SunOS does...).
+		ta	ST_DIV0
+		retl
+		 clr	%o0
+
+1:
+	cmp	%o3, %o5			! if %o1 exceeds %o0, done
+	blu	Lgot_result		! (and algorithm fails otherwise)
+	 clr	%o2
+
+	sethi	%hi(1 << (32 - 4 - 1)), %g1
+
+	cmp	%o3, %g1
+	blu	Lnot_really_big
+	 clr	%o4
+
+	! Here the dividend is >= 2**(31-N) or so.  We must be careful here,
+	! as our usual N-at-a-shot divide step will cause overflow and havoc.
+	! The number of bits in the result here is N*ITER+SC, where SC <= N.
+	! Compute ITER in an unorthodox manner: know we need to shift V into
+	! the top decade: so do not even bother to compare to R.
+	1:
+		cmp	%o5, %g1
+		bgeu	3f
+		 mov	1, %g7
+
+		sll	%o5, 4, %o5
+
+		b	1b
+		 add	%o4, 1, %o4
+
+	! Now compute %g7.
+	2:
+		addcc	%o5, %o5, %o5
+		bcc	Lnot_too_big
+		 add	%g7, 1, %g7
+
+		! We get here if the %o1 overflowed while shifting.
+		! This means that %o3 has the high-order bit set.
+		! Restore %o5 and subtract from %o3.
+		sll	%g1, 4, %g1	! high order bit
+		srl	%o5, 1, %o5		! rest of %o5
+		add	%o5, %g1, %o5
+
+		b	Ldo_single_div
+		 sub	%g7, 1, %g7
+
+	Lnot_too_big:
+	3:
+		cmp	%o5, %o3
+		blu	2b
+		 nop
+
+		be	Ldo_single_div
+		 nop
+	/* NB: these are commented out in the V8-Sparc manual as well */
+	/* (I do not understand this) */
+	! %o5 > %o3: went too far: back up 1 step
+	!	srl	%o5, 1, %o5
+	!	dec	%g7
+	! do single-bit divide steps
+	!
+	! We have to be careful here.  We know that %o3 >= %o5, so we can do the
+	! first divide step without thinking.  BUT, the others are conditional,
+	! and are only done if %o3 >= 0.  Because both %o3 and %o5 may have the high-
+	! order bit set in the first step, just falling into the regular
+	! division loop will mess up the first time around.
+	! So we unroll slightly...
+	Ldo_single_div:
+		subcc	%g7, 1, %g7
+		bl	Lend_regular_divide
+		 nop
+
+		sub	%o3, %o5, %o3
+		mov	1, %o2
+
+		b	Lend_single_divloop
+		 nop
+	Lsingle_divloop:
+		sll	%o2, 1, %o2
+		bl	1f
+		 srl	%o5, 1, %o5
+		! %o3 >= 0
+		sub	%o3, %o5, %o3
+		b	2f
+		 add	%o2, 1, %o2
+	1:	! %o3 < 0
+		add	%o3, %o5, %o3
+		sub	%o2, 1, %o2
+	2:
+	Lend_single_divloop:
+		subcc	%g7, 1, %g7
+		bge	Lsingle_divloop
+		 tst	%o3
+
+		b,a	Lend_regular_divide
+
+Lnot_really_big:
+1:
+	sll	%o5, 4, %o5
+
+	cmp	%o5, %o3
+	bleu	1b
+	 addcc	%o4, 1, %o4
+
+	be	Lgot_result
+	 sub	%o4, 1, %o4
+
+	tst	%o3	! set up for initial iteration
+Ldivloop:
+	sll	%o2, 4, %o2
+		! depth 1, accumulated bits 0
+	bl	L.1.16
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 2, accumulated bits 1
+	bl	L.2.17
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 3, accumulated bits 3
+	bl	L.3.19
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 4, accumulated bits 7
+	bl	L.4.23
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (7*2+1), %o2
+
+L.4.23:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (7*2-1), %o2
+
+L.3.19:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 4, accumulated bits 5
+	bl	L.4.21
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (5*2+1), %o2
+
+L.4.21:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (5*2-1), %o2
+
+L.2.17:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 3, accumulated bits 1
+	bl	L.3.17
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 4, accumulated bits 3
+	bl	L.4.19
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (3*2+1), %o2
+
+L.4.19:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (3*2-1), %o2
+
+L.3.17:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 4, accumulated bits 1
+	bl	L.4.17
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (1*2+1), %o2
+	
+L.4.17:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (1*2-1), %o2
+
+L.1.16:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 2, accumulated bits -1
+	bl	L.2.15
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 3, accumulated bits -1
+	bl	L.3.15
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 4, accumulated bits -1
+	bl	L.4.15
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-1*2+1), %o2
+
+L.4.15:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-1*2-1), %o2
+
+L.3.15:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 4, accumulated bits -3
+	bl	L.4.13
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-3*2+1), %o2
+
+L.4.13:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-3*2-1), %o2
+
+L.2.15:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 3, accumulated bits -3
+	bl	L.3.13
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+			! depth 4, accumulated bits -5
+	bl	L.4.11
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-5*2+1), %o2
+	
+L.4.11:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-5*2-1), %o2
+
+L.3.13:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+			! depth 4, accumulated bits -7
+	bl	L.4.9
+	 srl	%o5,1,%o5
+	! remainder is positive
+	subcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-7*2+1), %o2
+
+L.4.9:
+	! remainder is negative
+	addcc	%o3,%o5,%o3
+	b	9f
+	 add	%o2, (-7*2-1), %o2
+
+	9:
+Lend_regular_divide:
+	subcc	%o4, 1, %o4
+	bge	Ldivloop
+	 tst	%o3
+
+	bl,a	Lgot_result
+	! non-restoring fixup here (one instruction only!)
+	add	%o3, %o1, %o3
+
+Lgot_result:
+
+	retl
+	 mov %o3, %o0
+
+	.globl	.urem_patch
+.urem_patch:
+	wr	%g0, 0x0, %y
+	nop
+	nop
+	nop
+	udiv	%o0, %o1, %o2
+	umul	%o2, %o1, %o2
+	retl
+	 sub	%o0, %o2, %o0
diff --git a/arch/sparc/math-emu/Makefile b/arch/sparc/math-emu/Makefile
new file mode 100644
index 0000000..f84a9a6
--- /dev/null
+++ b/arch/sparc/math-emu/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the FPU instruction emulation.
+#
+
+obj-y    := math.o
+
+EXTRA_AFLAGS := -ansi
+EXTRA_CFLAGS = -I. -I$(TOPDIR)/include/math-emu -w
diff --git a/arch/sparc/math-emu/ashldi3.S b/arch/sparc/math-emu/ashldi3.S
new file mode 100644
index 0000000..eab1d09
--- /dev/null
+++ b/arch/sparc/math-emu/ashldi3.S
@@ -0,0 +1,36 @@
+/* $Id: ashldi3.S,v 1.1 1998/04/06 16:09:28 jj Exp $
+ * ashldi3.S:	Math-emu code creates all kinds of references to
+ *              this little routine on the sparc with gcc.
+ *
+ * Copyright (C) 1998 Jakub Jelinek(jj@ultra.linux.cz)
+ */
+
+#include <asm/cprefix.h>
+
+	.globl C_LABEL(__ashldi3)
+C_LABEL(__ashldi3):
+	tst	%o2
+	be	3f
+	 mov	32, %g2
+
+	sub	%g2, %o2, %g2
+
+	tst	%g2
+	bg	1f
+	 srl	%o1, %g2, %g3
+
+	clr	%o5
+	neg	%g2
+	ba	2f
+	 sll	%o1, %g2, %o4
+
+1:
+	sll	%o1, %o2, %o5
+	srl	%o0, %o2, %g2
+	or	%g2, %g3, %o4
+2:
+	mov	%o4, %o0
+	mov	%o5, %o1
+3:
+	jmpl	%o7 + 8, %g0
+	 nop
diff --git a/arch/sparc/math-emu/math.c b/arch/sparc/math-emu/math.c
new file mode 100644
index 0000000..be2c809
--- /dev/null
+++ b/arch/sparc/math-emu/math.c
@@ -0,0 +1,521 @@
+/*
+ * arch/sparc/math-emu/math.c
+ *
+ * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ * This is a good place to start if you're trying to understand the
+ * emulation code, because it's pretty simple. What we do is
+ * essentially analyse the instruction to work out what the operation
+ * is and which registers are involved. We then execute the appropriate
+ * FXXXX function. [The floating point queue introduces a minor wrinkle;
+ * see below...]
+ * The fxxxxx.c files each emulate a single insn. They look relatively
+ * simple because the complexity is hidden away in an unholy tangle
+ * of preprocessor macros.
+ *
+ * The first layer of macros is single.h, double.h, quad.h. Generally
+ * these files define macros for working with floating point numbers
+ * of the three IEEE formats. FP_ADD_D(R,A,B) is for adding doubles,
+ * for instance. These macros are usually defined as calls to more
+ * generic macros (in this case _FP_ADD(D,2,R,X,Y) where the number
+ * of machine words required to store the given IEEE format is passed
+ * as a parameter. [double.h and co check the number of bits in a word
+ * and define FP_ADD_D & co appropriately].
+ * The generic macros are defined in op-common.h. This is where all
+ * the grotty stuff like handling NaNs is coded. To handle the possible
+ * word sizes macros in op-common.h use macros like _FP_FRAC_SLL_##wc()
+ * where wc is the 'number of machine words' parameter (here 2).
+ * These are defined in the third layer of macros: op-1.h, op-2.h
+ * and op-4.h. These handle operations on floating point numbers composed
+ * of 1,2 and 4 machine words respectively. [For example, on sparc64
+ * doubles are one machine word so macros in double.h eventually use
+ * constructs in op-1.h, but on sparc32 they use op-2.h definitions.]
+ * soft-fp.h is on the same level as op-common.h, and defines some
+ * macros which are independent of both word size and FP format.
+ * Finally, sfp-machine.h is the machine dependent part of the
+ * code: it defines the word size and what type a word is. It also
+ * defines how _FP_MUL_MEAT_t() maps to _FP_MUL_MEAT_n_* : op-n.h
+ * provide several possible flavours of multiply algorithm, most
+ * of which require that you supply some form of asm or C primitive to
+ * do the actual multiply. (such asm primitives should be defined
+ * in sfp-machine.h too). udivmodti4.c is the same sort of thing.
+ *
+ * There may be some errors here because I'm working from a
+ * SPARC architecture manual V9, and what I really want is V8...
+ * Also, the insns which can generate exceptions seem to be a
+ * greater subset of the FPops than for V9 (for example, FCMPED
+ * has to be emulated on V8). So I think I'm going to have
+ * to emulate them all just to be on the safe side...
+ *
+ * Emulation routines originate from soft-fp package, which is
+ * part of glibc and has appropriate copyrights in it (allegedly).
+ *
+ * NB: on sparc int == long == 4 bytes, long long == 8 bytes.
+ * Most bits of the kernel seem to go for long rather than int,
+ * so we follow that practice...
+ */
+
+/* TODO:
+ * fpsave() saves the FP queue but fpload() doesn't reload it.
+ * Therefore when we context switch or change FPU ownership
+ * we have to check to see if the queue had anything in it and
+ * emulate it if it did. This is going to be a pain.
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <asm/uaccess.h>
+
+#include "sfp-util.h"
+#include <math-emu/soft-fp.h>
+#include <math-emu/single.h>
+#include <math-emu/double.h>
+#include <math-emu/quad.h>
+
+#define FLOATFUNC(x) extern int x(void *,void *,void *)
+
+/* The Vn labels indicate what version of the SPARC architecture gas thinks
+ * each insn is. This is from the binutils source :->
+ */
+/* quadword instructions */
+#define FSQRTQ	0x02b		/* v8 */
+#define FADDQ	0x043		/* v8 */
+#define FSUBQ	0x047		/* v8 */
+#define FMULQ	0x04b		/* v8 */
+#define FDIVQ	0x04f		/* v8 */
+#define FDMULQ	0x06e		/* v8 */
+#define FQTOS	0x0c7		/* v8 */
+#define FQTOD	0x0cb		/* v8 */
+#define FITOQ	0x0cc		/* v8 */
+#define FSTOQ	0x0cd		/* v8 */
+#define FDTOQ	0x0ce		/* v8 */
+#define FQTOI	0x0d3		/* v8 */
+#define FCMPQ	0x053		/* v8 */
+#define FCMPEQ	0x057		/* v8 */
+/* single/double instructions (subnormal): should all work */
+#define FSQRTS	0x029		/* v7 */
+#define FSQRTD	0x02a		/* v7 */
+#define FADDS	0x041		/* v6 */
+#define FADDD	0x042		/* v6 */
+#define FSUBS	0x045		/* v6 */
+#define FSUBD	0x046		/* v6 */
+#define FMULS	0x049		/* v6 */
+#define FMULD	0x04a		/* v6 */
+#define FDIVS	0x04d		/* v6 */
+#define FDIVD	0x04e		/* v6 */
+#define FSMULD	0x069		/* v6 */
+#define FDTOS	0x0c6		/* v6 */
+#define FSTOD	0x0c9		/* v6 */
+#define FSTOI	0x0d1		/* v6 */
+#define FDTOI	0x0d2		/* v6 */
+#define FABSS	0x009		/* v6 */
+#define FCMPS	0x051		/* v6 */
+#define FCMPES	0x055		/* v6 */
+#define FCMPD	0x052		/* v6 */
+#define FCMPED	0x056		/* v6 */
+#define FMOVS	0x001		/* v6 */
+#define FNEGS	0x005		/* v6 */
+#define FITOS	0x0c4		/* v6 */
+#define FITOD	0x0c8		/* v6 */
+
+#define FSR_TEM_SHIFT	23UL
+#define FSR_TEM_MASK	(0x1fUL << FSR_TEM_SHIFT)
+#define FSR_AEXC_SHIFT	5UL
+#define FSR_AEXC_MASK	(0x1fUL << FSR_AEXC_SHIFT)
+#define FSR_CEXC_SHIFT	0UL
+#define FSR_CEXC_MASK	(0x1fUL << FSR_CEXC_SHIFT)
+
+static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs);
+
+/* Unlike the Sparc64 version (which has a struct fpustate), we
+ * pass the taskstruct corresponding to the task which currently owns the
+ * FPU. This is partly because we don't have the fpustate struct and
+ * partly because the task owning the FPU isn't always current (as is
+ * the case for the Sparc64 port). This is probably SMP-related...
+ * This function returns 1 if all queued insns were emulated successfully.
+ * The test for unimplemented FPop in kernel mode has been moved into
+ * kernel/traps.c for simplicity.
+ */
+int do_mathemu(struct pt_regs *regs, struct task_struct *fpt)
+{
+	/* regs->pc isn't necessarily the PC at which the offending insn is sitting.
+	 * The FPU maintains a queue of FPops which cause traps.
+	 * When it hits an instruction that requires that the trapped op succeeded
+	 * (usually because it reads a reg. that the trapped op wrote) then it
+	 * causes this exception. We need to emulate all the insns on the queue
+	 * and then allow the op to proceed.
+	 * This code should also handle the case where the trap was precise,
+	 * in which case the queue length is zero and regs->pc points at the
+	 * single FPop to be emulated. (this case is untested, though :->)
+	 * You'll need this case if you want to be able to emulate all FPops
+	 * because the FPU either doesn't exist or has been software-disabled.
+	 * [The UltraSPARC makes FP a precise trap; this isn't as stupid as it
+	 * might sound because the Ultra does funky things with a superscalar
+	 * architecture.]
+	 */
+
+	/* You wouldn't believe how often I typed 'ftp' when I meant 'fpt' :-> */
+
+	int i;
+	int retcode = 0;                               /* assume all succeed */
+	unsigned long insn;
+
+#ifdef DEBUG_MATHEMU
+	printk("In do_mathemu()... pc is %08lx\n", regs->pc);
+	printk("fpqdepth is %ld\n", fpt->thread.fpqdepth);
+	for (i = 0; i < fpt->thread.fpqdepth; i++)
+		printk("%d: %08lx at %08lx\n", i, fpt->thread.fpqueue[i].insn,
+		       (unsigned long)fpt->thread.fpqueue[i].insn_addr);
+#endif
+
+	if (fpt->thread.fpqdepth == 0) {                   /* no queue, guilty insn is at regs->pc */
+#ifdef DEBUG_MATHEMU
+		printk("precise trap at %08lx\n", regs->pc);
+#endif
+		if (!get_user(insn, (u32 __user *) regs->pc)) {
+			retcode = do_one_mathemu(insn, &fpt->thread.fsr, fpt->thread.float_regs);
+			if (retcode) {
+				/* in this case we need to fix up PC & nPC */
+				regs->pc = regs->npc;
+				regs->npc += 4;
+			}
+		}
+		return retcode;
+	}
+
+	/* Normal case: need to empty the queue... */
+	for (i = 0; i < fpt->thread.fpqdepth; i++) {
+		retcode = do_one_mathemu(fpt->thread.fpqueue[i].insn, &(fpt->thread.fsr), fpt->thread.float_regs);
+		if (!retcode)                               /* insn failed, no point doing any more */
+			break;
+	}
+	/* Now empty the queue and clear the queue_not_empty flag */
+	if (retcode)
+		fpt->thread.fsr &= ~(0x3000 | FSR_CEXC_MASK);
+	else
+		fpt->thread.fsr &= ~0x3000;
+	fpt->thread.fpqdepth = 0;
+
+	return retcode;
+}
+
+/* All routines returning an exception to raise should detect
+ * such exceptions _before_ rounding to be consistent with
+ * the behavior of the hardware in the implemented cases
+ * (and thus with the recommendations in the V9 architecture
+ * manual).
+ *
+ * We return 0 if a SIGFPE should be sent, 1 otherwise.
+ */
+static inline int record_exception(unsigned long *pfsr, int eflag)
+{
+	unsigned long fsr = *pfsr;
+	int would_trap;
+
+	/* Determine if this exception would have generated a trap. */
+	would_trap = (fsr & ((long)eflag << FSR_TEM_SHIFT)) != 0UL;
+
+	/* If trapping, we only want to signal one bit. */
+	if (would_trap != 0) {
+		eflag &= ((fsr & FSR_TEM_MASK) >> FSR_TEM_SHIFT);
+		if ((eflag & (eflag - 1)) != 0) {
+			if (eflag & FP_EX_INVALID)
+				eflag = FP_EX_INVALID;
+			else if (eflag & FP_EX_OVERFLOW)
+				eflag = FP_EX_OVERFLOW;
+			else if (eflag & FP_EX_UNDERFLOW)
+				eflag = FP_EX_UNDERFLOW;
+			else if (eflag & FP_EX_DIVZERO)
+				eflag = FP_EX_DIVZERO;
+			else if (eflag & FP_EX_INEXACT)
+				eflag = FP_EX_INEXACT;
+		}
+	}
+
+	/* Set CEXC, here is the rule:
+	 *
+	 *    In general all FPU ops will set one and only one
+	 *    bit in the CEXC field, this is always the case
+	 *    when the IEEE exception trap is enabled in TEM.
+	 */
+	fsr &= ~(FSR_CEXC_MASK);
+	fsr |= ((long)eflag << FSR_CEXC_SHIFT);
+
+	/* Set the AEXC field, rule is:
+	 *
+	 *    If a trap would not be generated, the
+	 *    CEXC just generated is OR'd into the
+	 *    existing value of AEXC.
+	 */
+	if (would_trap == 0)
+		fsr |= ((long)eflag << FSR_AEXC_SHIFT);
+
+	/* If trapping, indicate fault trap type IEEE. */
+	if (would_trap != 0)
+		fsr |= (1UL << 14);
+
+	*pfsr = fsr;
+
+	return (would_trap ? 0 : 1);
+}
+
+typedef union {
+	u32 s;
+	u64 d;
+	u64 q[2];
+} *argp;
+
+static int do_one_mathemu(u32 insn, unsigned long *pfsr, unsigned long *fregs)
+{
+	/* Emulate the given insn, updating fsr and fregs appropriately. */
+	int type = 0;
+	/* r is rd, b is rs2 and a is rs1. The *u arg tells
+	   whether the argument should be packed/unpacked (0 - do not unpack/pack, 1 - unpack/pack)
+	   non-u args tells the size of the argument (0 - no argument, 1 - single, 2 - double, 3 - quad */
+#define TYPE(dummy, r, ru, b, bu, a, au) type = (au << 2) | (a << 0) | (bu << 5) | (b << 3) | (ru << 8) | (r << 6)
+	int freg;
+	argp rs1 = NULL, rs2 = NULL, rd = NULL;
+	FP_DECL_EX;
+	FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
+	FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
+	FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR);
+	int IR;
+	long fsr;
+
+#ifdef DEBUG_MATHEMU
+	printk("In do_mathemu(), emulating %08lx\n", insn);
+#endif
+
+	if ((insn & 0xc1f80000) == 0x81a00000)	/* FPOP1 */ {
+		switch ((insn >> 5) & 0x1ff) {
+		case FSQRTQ: TYPE(3,3,1,3,1,0,0); break;
+		case FADDQ:
+		case FSUBQ:
+		case FMULQ:
+		case FDIVQ: TYPE(3,3,1,3,1,3,1); break;
+		case FDMULQ: TYPE(3,3,1,2,1,2,1); break;
+		case FQTOS: TYPE(3,1,1,3,1,0,0); break;
+		case FQTOD: TYPE(3,2,1,3,1,0,0); break;
+		case FITOQ: TYPE(3,3,1,1,0,0,0); break;
+		case FSTOQ: TYPE(3,3,1,1,1,0,0); break;
+		case FDTOQ: TYPE(3,3,1,2,1,0,0); break;
+		case FQTOI: TYPE(3,1,0,3,1,0,0); break;
+		case FSQRTS: TYPE(2,1,1,1,1,0,0); break;
+		case FSQRTD: TYPE(2,2,1,2,1,0,0); break;
+		case FADDD:
+		case FSUBD:
+		case FMULD:
+		case FDIVD: TYPE(2,2,1,2,1,2,1); break;
+		case FADDS:
+		case FSUBS:
+		case FMULS:
+		case FDIVS: TYPE(2,1,1,1,1,1,1); break;
+		case FSMULD: TYPE(2,2,1,1,1,1,1); break;
+		case FDTOS: TYPE(2,1,1,2,1,0,0); break;
+		case FSTOD: TYPE(2,2,1,1,1,0,0); break;
+		case FSTOI: TYPE(2,1,0,1,1,0,0); break;
+		case FDTOI: TYPE(2,1,0,2,1,0,0); break;
+		case FITOS: TYPE(2,1,1,1,0,0,0); break;
+		case FITOD: TYPE(2,2,1,1,0,0,0); break;
+		case FMOVS:
+		case FABSS:
+		case FNEGS: TYPE(2,1,0,1,0,0,0); break;
+		default:
+#ifdef DEBUG_MATHEMU
+			printk("unknown FPop1: %03lx\n",(insn>>5)&0x1ff);
+#endif
+			break;
+		}
+	} else if ((insn & 0xc1f80000) == 0x81a80000)	/* FPOP2 */ {
+		switch ((insn >> 5) & 0x1ff) {
+		case FCMPS: TYPE(3,0,0,1,1,1,1); break;
+		case FCMPES: TYPE(3,0,0,1,1,1,1); break;
+		case FCMPD: TYPE(3,0,0,2,1,2,1); break;
+		case FCMPED: TYPE(3,0,0,2,1,2,1); break;
+		case FCMPQ: TYPE(3,0,0,3,1,3,1); break;
+		case FCMPEQ: TYPE(3,0,0,3,1,3,1); break;
+		default:
+#ifdef DEBUG_MATHEMU
+			printk("unknown FPop2: %03lx\n",(insn>>5)&0x1ff);
+#endif
+			break;
+		}
+	}
+
+	if (!type) {	/* oops, didn't recognise that FPop */
+#ifdef DEBUG_MATHEMU
+		printk("attempt to emulate unrecognised FPop!\n");
+#endif
+		return 0;
+	}
+
+	/* Decode the registers to be used */
+	freg = (*pfsr >> 14) & 0xf;
+
+	*pfsr &= ~0x1c000;				/* clear the traptype bits */
+	
+	freg = ((insn >> 14) & 0x1f);
+	switch (type & 0x3) {				/* is rs1 single, double or quad? */
+	case 3:
+		if (freg & 3) {				/* quadwords must have bits 4&5 of the */
+							/* encoded reg. number set to zero. */
+			*pfsr |= (6 << 14);
+			return 0;			/* simulate invalid_fp_register exception */
+		}
+	/* fall through */
+	case 2:
+		if (freg & 1) {				/* doublewords must have bit 5 zeroed */
+			*pfsr |= (6 << 14);
+			return 0;
+		}
+	}
+	rs1 = (argp)&fregs[freg];
+	switch (type & 0x7) {
+	case 7: FP_UNPACK_QP (QA, rs1); break;
+	case 6: FP_UNPACK_DP (DA, rs1); break;
+	case 5: FP_UNPACK_SP (SA, rs1); break;
+	}
+	freg = (insn & 0x1f);
+	switch ((type >> 3) & 0x3) {			/* same again for rs2 */
+	case 3:
+		if (freg & 3) {				/* quadwords must have bits 4&5 of the */
+							/* encoded reg. number set to zero. */
+			*pfsr |= (6 << 14);
+			return 0;			/* simulate invalid_fp_register exception */
+		}
+	/* fall through */
+	case 2:
+		if (freg & 1) {				/* doublewords must have bit 5 zeroed */
+			*pfsr |= (6 << 14);
+			return 0;
+		}
+	}
+	rs2 = (argp)&fregs[freg];
+	switch ((type >> 3) & 0x7) {
+	case 7: FP_UNPACK_QP (QB, rs2); break;
+	case 6: FP_UNPACK_DP (DB, rs2); break;
+	case 5: FP_UNPACK_SP (SB, rs2); break;
+	}
+	freg = ((insn >> 25) & 0x1f);
+	switch ((type >> 6) & 0x3) {			/* and finally rd. This one's a bit different */
+	case 0:						/* dest is fcc. (this must be FCMPQ or FCMPEQ) */
+		if (freg) {				/* V8 has only one set of condition codes, so */
+							/* anything but 0 in the rd field is an error */
+			*pfsr |= (6 << 14);		/* (should probably flag as invalid opcode */
+			return 0;			/* but SIGFPE will do :-> ) */
+		}
+		break;
+	case 3:
+		if (freg & 3) {				/* quadwords must have bits 4&5 of the */
+							/* encoded reg. number set to zero. */
+			*pfsr |= (6 << 14);
+			return 0;			/* simulate invalid_fp_register exception */
+		}
+	/* fall through */
+	case 2:
+		if (freg & 1) {				/* doublewords must have bit 5 zeroed */
+			*pfsr |= (6 << 14);
+			return 0;
+		}
+	/* fall through */
+	case 1:
+		rd = (void *)&fregs[freg];
+		break;
+	}
+#ifdef DEBUG_MATHEMU
+	printk("executing insn...\n");
+#endif
+	/* do the Right Thing */
+	switch ((insn >> 5) & 0x1ff) {
+	/* + */
+	case FADDS: FP_ADD_S (SR, SA, SB); break;
+	case FADDD: FP_ADD_D (DR, DA, DB); break;
+	case FADDQ: FP_ADD_Q (QR, QA, QB); break;
+	/* - */
+	case FSUBS: FP_SUB_S (SR, SA, SB); break;
+	case FSUBD: FP_SUB_D (DR, DA, DB); break;
+	case FSUBQ: FP_SUB_Q (QR, QA, QB); break;
+	/* * */
+	case FMULS: FP_MUL_S (SR, SA, SB); break;
+	case FSMULD: FP_CONV (D, S, 2, 1, DA, SA);
+		     FP_CONV (D, S, 2, 1, DB, SB);
+	case FMULD: FP_MUL_D (DR, DA, DB); break;
+	case FDMULQ: FP_CONV (Q, D, 4, 2, QA, DA);
+		     FP_CONV (Q, D, 4, 2, QB, DB);
+	case FMULQ: FP_MUL_Q (QR, QA, QB); break;
+	/* / */
+	case FDIVS: FP_DIV_S (SR, SA, SB); break;
+	case FDIVD: FP_DIV_D (DR, DA, DB); break;
+	case FDIVQ: FP_DIV_Q (QR, QA, QB); break;
+	/* sqrt */
+	case FSQRTS: FP_SQRT_S (SR, SB); break;
+	case FSQRTD: FP_SQRT_D (DR, DB); break;
+	case FSQRTQ: FP_SQRT_Q (QR, QB); break;
+	/* mov */
+	case FMOVS: rd->s = rs2->s; break;
+	case FABSS: rd->s = rs2->s & 0x7fffffff; break;
+	case FNEGS: rd->s = rs2->s ^ 0x80000000; break;
+	/* float to int */
+	case FSTOI: FP_TO_INT_S (IR, SB, 32, 1); break;
+	case FDTOI: FP_TO_INT_D (IR, DB, 32, 1); break;
+	case FQTOI: FP_TO_INT_Q (IR, QB, 32, 1); break;
+	/* int to float */
+	case FITOS: IR = rs2->s; FP_FROM_INT_S (SR, IR, 32, int); break;
+	case FITOD: IR = rs2->s; FP_FROM_INT_D (DR, IR, 32, int); break;
+	case FITOQ: IR = rs2->s; FP_FROM_INT_Q (QR, IR, 32, int); break;
+	/* float to float */
+	case FSTOD: FP_CONV (D, S, 2, 1, DR, SB); break;
+	case FSTOQ: FP_CONV (Q, S, 4, 1, QR, SB); break;
+	case FDTOQ: FP_CONV (Q, D, 4, 2, QR, DB); break;
+	case FDTOS: FP_CONV (S, D, 1, 2, SR, DB); break;
+	case FQTOS: FP_CONV (S, Q, 1, 4, SR, QB); break;
+	case FQTOD: FP_CONV (D, Q, 2, 4, DR, QB); break;
+	/* comparison */
+	case FCMPS:
+	case FCMPES:
+		FP_CMP_S(IR, SB, SA, 3);
+		if (IR == 3 &&
+		    (((insn >> 5) & 0x1ff) == FCMPES ||
+		     FP_ISSIGNAN_S(SA) ||
+		     FP_ISSIGNAN_S(SB)))
+			FP_SET_EXCEPTION (FP_EX_INVALID);
+		break;
+	case FCMPD:
+	case FCMPED:
+		FP_CMP_D(IR, DB, DA, 3);
+		if (IR == 3 &&
+		    (((insn >> 5) & 0x1ff) == FCMPED ||
+		     FP_ISSIGNAN_D(DA) ||
+		     FP_ISSIGNAN_D(DB)))
+			FP_SET_EXCEPTION (FP_EX_INVALID);
+		break;
+	case FCMPQ:
+	case FCMPEQ:
+		FP_CMP_Q(IR, QB, QA, 3);
+		if (IR == 3 &&
+		    (((insn >> 5) & 0x1ff) == FCMPEQ ||
+		     FP_ISSIGNAN_Q(QA) ||
+		     FP_ISSIGNAN_Q(QB)))
+			FP_SET_EXCEPTION (FP_EX_INVALID);
+	}
+	if (!FP_INHIBIT_RESULTS) {
+		switch ((type >> 6) & 0x7) {
+		case 0: fsr = *pfsr;
+			if (IR == -1) IR = 2;
+			/* fcc is always fcc0 */
+			fsr &= ~0xc00; fsr |= (IR << 10); break;
+			*pfsr = fsr;
+			break;
+		case 1: rd->s = IR; break;
+		case 5: FP_PACK_SP (rd, SR); break;
+		case 6: FP_PACK_DP (rd, DR); break;
+		case 7: FP_PACK_QP (rd, QR); break;
+		}
+	}
+	if (_fex == 0)
+		return 1;				/* success! */
+	return record_exception(pfsr, _fex);
+}
diff --git a/arch/sparc/math-emu/sfp-util.h b/arch/sparc/math-emu/sfp-util.h
new file mode 100644
index 0000000..d1b2aff
--- /dev/null
+++ b/arch/sparc/math-emu/sfp-util.h
@@ -0,0 +1,115 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) 				\
+  __asm__ ("addcc %r4,%5,%1\n\t"						\
+	   "addx %r2,%3,%0\n"						\
+	   : "=r" ((USItype)(sh)),					\
+	     "=&r" ((USItype)(sl))					\
+	   : "%rJ" ((USItype)(ah)),					\
+	     "rI" ((USItype)(bh)),					\
+	     "%rJ" ((USItype)(al)),					\
+	     "rI" ((USItype)(bl))					\
+	   : "cc")
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) 				\
+  __asm__ ("subcc %r4,%5,%1\n\t"						\
+	   "subx %r2,%3,%0\n"						\
+	   : "=r" ((USItype)(sh)),					\
+	     "=&r" ((USItype)(sl))					\
+	   : "rJ" ((USItype)(ah)),					\
+	     "rI" ((USItype)(bh)),					\
+	     "rJ" ((USItype)(al)),					\
+	     "rI" ((USItype)(bl))					\
+	   : "cc")
+
+#define umul_ppmm(w1, w0, u, v) \
+  __asm__ ("! Inlined umul_ppmm\n\t"					\
+	"wr	%%g0,%2,%%y	! SPARC has 0-3 delay insn after a wr\n\t" \
+	"sra	%3,31,%%g2	! Don't move this insn\n\t"		\
+	"and	%2,%%g2,%%g2	! Don't move this insn\n\t"		\
+	"andcc	%%g0,0,%%g1	! Don't move this insn\n\t"		\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,%3,%%g1\n\t"					\
+	"mulscc	%%g1,0,%%g1\n\t" 					\
+	"add	%%g1,%%g2,%0\n\t" 					\
+	"rd	%%y,%1\n"						\
+	   : "=r" ((USItype)(w1)),					\
+	     "=r" ((USItype)(w0))					\
+	   : "%rI" ((USItype)(u)),					\
+	     "r" ((USItype)(v))						\
+	   : "%g1", "%g2", "cc")
+
+/* It's quite necessary to add this much assembler for the sparc.
+   The default udiv_qrnnd (in C) is more than 10 times slower!  */
+#define udiv_qrnnd(q, r, n1, n0, d) \
+  __asm__ ("! Inlined udiv_qrnnd\n\t"					\
+	   "mov	32,%%g1\n\t"						\
+	   "subcc	%1,%2,%%g0\n\t"					\
+	   "1:	bcs	5f\n\t"						\
+	   "addxcc %0,%0,%0	! shift n1n0 and a q-bit in lsb\n\t"	\
+	   "sub	%1,%2,%1	! this kills msb of n\n\t"		\
+	   "addx	%1,%1,%1	! so this can't give carry\n\t"	\
+	   "subcc	%%g1,1,%%g1\n\t"				\
+	   "2:	bne	1b\n\t"						\
+	   "subcc	%1,%2,%%g0\n\t"					\
+	   "bcs	3f\n\t"							\
+	   "addxcc %0,%0,%0	! shift n1n0 and a q-bit in lsb\n\t"	\
+	   "b		3f\n\t"						\
+	   "sub	%1,%2,%1	! this kills msb of n\n\t"		\
+	   "4:	sub	%1,%2,%1\n\t"					\
+	   "5:	addxcc	%1,%1,%1\n\t"					\
+	   "bcc	2b\n\t"							\
+	   "subcc	%%g1,1,%%g1\n\t"				\
+	   "! Got carry from n.  Subtract next step to cancel this carry.\n\t" \
+	   "bne	4b\n\t"							\
+	   "addcc	%0,%0,%0	! shift n1n0 and a 0-bit in lsb\n\t" \
+	   "sub	%1,%2,%1\n\t"						\
+	   "3:	xnor	%0,0,%0\n\t"					\
+	   "! End of inline udiv_qrnnd\n"				\
+	   : "=&r" ((USItype)(q)),					\
+	     "=&r" ((USItype)(r))					\
+	   : "r" ((USItype)(d)),					\
+	     "1" ((USItype)(n1)),					\
+	     "0" ((USItype)(n0)) : "%g1", "cc")
+#define UDIV_NEEDS_NORMALIZATION 0
+
+#define abort()								\
+	return 0
+
+#ifdef __BIG_ENDIAN
+#define __BYTE_ORDER __BIG_ENDIAN
+#else
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#endif
diff --git a/arch/sparc/mm/Makefile b/arch/sparc/mm/Makefile
new file mode 100644
index 0000000..16eeba4b
--- /dev/null
+++ b/arch/sparc/mm/Makefile
@@ -0,0 +1,23 @@
+# $Id: Makefile,v 1.38 2000/12/15 00:41:22 davem Exp $
+# Makefile for the linux Sparc-specific parts of the memory manager.
+#
+
+EXTRA_AFLAGS := -ansi
+
+obj-y    := fault.o init.o loadmmu.o generic.o extable.o btfixup.o
+
+ifeq ($(CONFIG_SUN4),y)
+obj-y	 += nosrmmu.o
+else
+obj-y	 += srmmu.o iommu.o io-unit.o hypersparc.o viking.o tsunami.o swift.o
+endif
+
+ifdef CONFIG_HIGHMEM
+obj-y	+= highmem.o
+endif
+
+ifdef CONFIG_SMP
+obj-y   += nosun4c.o
+else
+obj-y   += sun4c.o
+endif
diff --git a/arch/sparc/mm/btfixup.c b/arch/sparc/mm/btfixup.c
new file mode 100644
index 0000000..f147a44
--- /dev/null
+++ b/arch/sparc/mm/btfixup.c
@@ -0,0 +1,336 @@
+/* $Id: btfixup.c,v 1.10 2000/05/09 17:40:13 davem Exp $
+ * btfixup.c: Boot time code fixup and relocator, so that
+ * we can get rid of most indirect calls to achieve single
+ * image sun4c and srmmu kernel.
+ *
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <asm/btfixup.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/oplib.h>
+#include <asm/system.h>
+#include <asm/cacheflush.h>
+
+#define BTFIXUP_OPTIMIZE_NOP
+#define BTFIXUP_OPTIMIZE_OTHER
+
+extern char *srmmu_name;
+static char version[] __initdata = "Boot time fixup v1.6. 4/Mar/98 Jakub Jelinek (jj@ultra.linux.cz). Patching kernel for ";
+#ifdef CONFIG_SUN4
+static char str_sun4c[] __initdata = "sun4\n";
+#else
+static char str_sun4c[] __initdata = "sun4c\n";
+#endif
+static char str_srmmu[] __initdata = "srmmu[%s]/";
+static char str_iommu[] __initdata = "iommu\n";
+static char str_iounit[] __initdata = "io-unit\n";
+
+static int visited __initdata = 0;
+extern unsigned int ___btfixup_start[], ___btfixup_end[], __init_begin[], __init_end[], __init_text_end[];
+extern unsigned int _stext[], _end[], __start___ksymtab[], __stop___ksymtab[];
+static char wrong_f[] __initdata = "Trying to set f fixup %p to invalid function %08x\n";
+static char wrong_b[] __initdata = "Trying to set b fixup %p to invalid function %08x\n";
+static char wrong_s[] __initdata = "Trying to set s fixup %p to invalid value %08x\n";
+static char wrong_h[] __initdata = "Trying to set h fixup %p to invalid value %08x\n";
+static char wrong_a[] __initdata = "Trying to set a fixup %p to invalid value %08x\n";
+static char wrong[] __initdata = "Wrong address for %c fixup %p\n";
+static char insn_f[] __initdata = "Fixup f %p refers to weird instructions at %p[%08x,%08x]\n";
+static char insn_b[] __initdata = "Fixup b %p doesn't refer to a SETHI at %p[%08x]\n";
+static char insn_s[] __initdata = "Fixup s %p doesn't refer to an OR at %p[%08x]\n";
+static char insn_h[] __initdata = "Fixup h %p doesn't refer to a SETHI at %p[%08x]\n";
+static char insn_a[] __initdata = "Fixup a %p doesn't refer to a SETHI nor OR at %p[%08x]\n";
+static char insn_i[] __initdata = "Fixup i %p doesn't refer to a valid instruction at %p[%08x]\n";
+static char fca_und[] __initdata = "flush_cache_all undefined in btfixup()\n";
+static char wrong_setaddr[] __initdata = "Garbled CALL/INT patch at %p[%08x,%08x,%08x]=%08x\n";
+
+#ifdef BTFIXUP_OPTIMIZE_OTHER
+static void __init set_addr(unsigned int *addr, unsigned int q1, int fmangled, unsigned int value)
+{
+	if (!fmangled)
+		*addr = value;
+	else {
+		unsigned int *q = (unsigned int *)q1;
+		if (*addr == 0x01000000) {
+			/* Noped */
+			*q = value;
+		} else if (addr[-1] == *q) {
+			/* Moved */
+			addr[-1] = value;
+			*q = value;
+		} else {
+			prom_printf(wrong_setaddr, addr-1, addr[-1], *addr, *q, value);
+			prom_halt();
+		}
+	}
+}
+#else
+static __inline__ void set_addr(unsigned int *addr, unsigned int q1, int fmangled, unsigned int value)
+{
+	*addr = value;
+}
+#endif
+
+void __init btfixup(void)
+{
+	unsigned int *p, *q;
+	int type, count;
+	unsigned insn;
+	unsigned *addr;
+	int fmangled = 0;
+	void (*flush_cacheall)(void);
+	
+	if (!visited) {
+		visited++;
+		printk(version);
+		if (ARCH_SUN4C_SUN4)
+			printk(str_sun4c);
+		else {
+			printk(str_srmmu, srmmu_name);
+			if (sparc_cpu_model == sun4d)
+				printk(str_iounit);
+			else
+				printk(str_iommu);
+		}
+	}
+	for (p = ___btfixup_start; p < ___btfixup_end; ) {
+		count = p[2];
+		q = p + 3;
+		switch (type = *(unsigned char *)p) {
+		case 'f': 
+			count = p[3];
+			q = p + 4;
+			if (((p[0] & 1) || p[1]) 
+			    && ((p[1] & 3) || (unsigned *)(p[1]) < _stext || (unsigned *)(p[1]) >= _end)) {
+				prom_printf(wrong_f, p, p[1]);
+				prom_halt();
+			}
+			break;
+		case 'b':
+			if (p[1] < (unsigned long)__init_begin || p[1] >= (unsigned long)__init_text_end || (p[1] & 3)) {
+				prom_printf(wrong_b, p, p[1]);
+				prom_halt();
+			}
+			break;
+		case 's':
+			if (p[1] + 0x1000 >= 0x2000) {
+				prom_printf(wrong_s, p, p[1]);
+				prom_halt();
+			}
+			break;
+		case 'h':
+			if (p[1] & 0x3ff) {
+				prom_printf(wrong_h, p, p[1]);
+				prom_halt();
+			}
+			break;
+		case 'a':
+			if (p[1] + 0x1000 >= 0x2000 && (p[1] & 0x3ff)) {
+				prom_printf(wrong_a, p, p[1]);
+				prom_halt();
+			}
+			break;
+		}
+		if (p[0] & 1) {
+			p[0] &= ~1;
+			while (count) {
+				fmangled = 0;
+				addr = (unsigned *)*q;
+				if (addr < _stext || addr >= _end) {
+					prom_printf(wrong, type, p);
+					prom_halt();
+				}
+				insn = *addr;
+#ifdef BTFIXUP_OPTIMIZE_OTHER				
+				if (type != 'f' && q[1]) {
+					insn = *(unsigned int *)q[1];
+					if (!insn || insn == 1)
+						insn = *addr;
+					else
+						fmangled = 1;
+				}
+#endif
+				switch (type) {
+				case 'f':	/* CALL */
+					if (addr >= __start___ksymtab && addr < __stop___ksymtab) {
+						*addr = p[1];
+						break;
+					} else if (!q[1]) {
+						if ((insn & 0xc1c00000) == 0x01000000) { /* SETHI */
+							*addr = (insn & 0xffc00000) | (p[1] >> 10); break;
+						} else if ((insn & 0xc1f82000) == 0x80102000) { /* OR X, %LO(i), Y */
+							*addr = (insn & 0xffffe000) | (p[1] & 0x3ff); break;
+						} else if ((insn & 0xc0000000) != 0x40000000) { /* !CALL */
+				bad_f:
+							prom_printf(insn_f, p, addr, insn, addr[1]);
+							prom_halt();
+						}
+					} else if (q[1] != 1)
+						addr[1] = q[1];
+					if (p[2] == BTFIXUPCALL_NORM) {
+				norm_f:	
+						*addr = 0x40000000 | ((p[1] - (unsigned)addr) >> 2);
+						q[1] = 0;
+						break;
+					}
+#ifndef BTFIXUP_OPTIMIZE_NOP
+					goto norm_f;
+#else
+					if (!(addr[1] & 0x80000000)) {
+						if ((addr[1] & 0xc1c00000) != 0x01000000)	/* !SETHI */
+							goto bad_f; /* CALL, Bicc, FBfcc, CBccc are weird in delay slot, aren't they? */
+					} else {
+						if ((addr[1] & 0x01800000) == 0x01800000) {
+							if ((addr[1] & 0x01f80000) == 0x01e80000) {
+								/* RESTORE */
+								goto norm_f; /* It is dangerous to patch that */
+							}
+							goto bad_f;
+						}
+						if ((addr[1] & 0xffffe003) == 0x9e03e000) {
+							/* ADD %O7, XX, %o7 */
+							int displac = (addr[1] << 19);
+							
+							displac = (displac >> 21) + 2;
+							*addr = (0x10800000) + (displac & 0x3fffff);
+							q[1] = addr[1];
+							addr[1] = p[2];
+							break;
+						}
+						if ((addr[1] & 0x201f) == 0x200f || (addr[1] & 0x7c000) == 0x3c000)
+							goto norm_f; /* Someone is playing bad tricks with us: rs1 or rs2 is o7 */
+						if ((addr[1] & 0x3e000000) == 0x1e000000)
+							goto norm_f; /* rd is %o7. We'd better take care. */
+					}
+					if (p[2] == BTFIXUPCALL_NOP) {
+						*addr = 0x01000000;
+						q[1] = 1;
+						break;
+					}
+#ifndef BTFIXUP_OPTIMIZE_OTHER
+					goto norm_f;
+#else
+					if (addr[1] == 0x01000000) {	/* NOP in the delay slot */
+						q[1] = addr[1];
+						*addr = p[2];
+						break;
+					}
+					if ((addr[1] & 0xc0000000) != 0xc0000000) {
+						/* Not a memory operation */
+						if ((addr[1] & 0x30000000) == 0x10000000) {
+							/* Ok, non-memory op with rd %oX */
+							if ((addr[1] & 0x3e000000) == 0x1c000000)
+								goto bad_f; /* Aiee. Someone is playing strange %sp tricks */
+							if ((addr[1] & 0x3e000000) > 0x12000000 ||
+							    ((addr[1] & 0x3e000000) == 0x12000000 &&
+							     p[2] != BTFIXUPCALL_STO1O0 && p[2] != BTFIXUPCALL_SWAPO0O1) ||
+							    ((p[2] & 0xffffe000) == BTFIXUPCALL_RETINT(0))) {
+								/* Nobody uses the result. We can nop it out. */
+								*addr = p[2];
+								q[1] = addr[1];
+								addr[1] = 0x01000000;
+								break;
+							}
+							if ((addr[1] & 0xf1ffffe0) == 0x90100000) {
+								/* MOV %reg, %Ox */
+								if ((addr[1] & 0x3e000000) == 0x10000000 &&
+								    (p[2] & 0x7c000) == 0x20000) {
+								    	/* Ok, it is call xx; mov reg, %o0 and call optimizes
+								    	   to doing something on %o0. Patch the patch. */
+									*addr = (p[2] & ~0x7c000) | ((addr[1] & 0x1f) << 14);
+									q[1] = addr[1];
+									addr[1] = 0x01000000;
+									break;
+								}
+								if ((addr[1] & 0x3e000000) == 0x12000000 &&
+								    p[2] == BTFIXUPCALL_STO1O0) {
+								    	*addr = (p[2] & ~0x3e000000) | ((addr[1] & 0x1f) << 25);
+								    	q[1] = addr[1];
+								    	addr[1] = 0x01000000;
+								    	break;
+								}
+							}
+						}
+					}
+					*addr = addr[1];
+					q[1] = addr[1];
+					addr[1] = p[2];
+					break;
+#endif /* BTFIXUP_OPTIMIZE_OTHER */
+#endif /* BTFIXUP_OPTIMIZE_NOP */
+				case 'b':	/* BLACKBOX */
+					/* Has to be sethi i, xx */
+					if ((insn & 0xc1c00000) != 0x01000000) {
+						prom_printf(insn_b, p, addr, insn);
+						prom_halt();
+					} else {
+						void (*do_fixup)(unsigned *);
+						
+						do_fixup = (void (*)(unsigned *))p[1];
+						do_fixup(addr);
+					}
+					break;
+				case 's':	/* SIMM13 */
+					/* Has to be or %g0, i, xx */
+					if ((insn & 0xc1ffe000) != 0x80102000) {
+						prom_printf(insn_s, p, addr, insn);
+						prom_halt();
+					}
+					set_addr(addr, q[1], fmangled, (insn & 0xffffe000) | (p[1] & 0x1fff));
+					break;
+				case 'h':	/* SETHI */
+					/* Has to be sethi i, xx */
+					if ((insn & 0xc1c00000) != 0x01000000) {
+						prom_printf(insn_h, p, addr, insn);
+						prom_halt();
+					}
+					set_addr(addr, q[1], fmangled, (insn & 0xffc00000) | (p[1] >> 10));
+					break;
+				case 'a':	/* HALF */
+					/* Has to be sethi i, xx or or %g0, i, xx */
+					if ((insn & 0xc1c00000) != 0x01000000 &&
+					    (insn & 0xc1ffe000) != 0x80102000) {
+						prom_printf(insn_a, p, addr, insn);
+						prom_halt();
+					}
+					if (p[1] & 0x3ff)
+						set_addr(addr, q[1], fmangled, 
+							(insn & 0x3e000000) | 0x80102000 | (p[1] & 0x1fff));
+					else
+						set_addr(addr, q[1], fmangled, 
+							(insn & 0x3e000000) | 0x01000000 | (p[1] >> 10));
+					break;
+				case 'i':	/* INT */
+					if ((insn & 0xc1c00000) == 0x01000000) /* %HI */
+						set_addr(addr, q[1], fmangled, (insn & 0xffc00000) | (p[1] >> 10));
+					else if ((insn & 0x80002000) == 0x80002000 &&
+					         (insn & 0x01800000) != 0x01800000) /* %LO */
+						set_addr(addr, q[1], fmangled, (insn & 0xffffe000) | (p[1] & 0x3ff));
+					else {
+						prom_printf(insn_i, p, addr, insn);
+						prom_halt();
+					}
+					break;
+				}
+				count -= 2;
+				q += 2;
+			}
+		} else
+			p = q + count;
+	}
+#ifdef CONFIG_SMP
+	flush_cacheall = (void (*)(void))BTFIXUPVAL_CALL(local_flush_cache_all);
+#else
+	flush_cacheall = (void (*)(void))BTFIXUPVAL_CALL(flush_cache_all);
+#endif
+	if (!flush_cacheall) {
+		prom_printf(fca_und);
+		prom_halt();
+	}
+	(*flush_cacheall)();
+}
diff --git a/arch/sparc/mm/extable.c b/arch/sparc/mm/extable.c
new file mode 100644
index 0000000..c9845c7
--- /dev/null
+++ b/arch/sparc/mm/extable.c
@@ -0,0 +1,77 @@
+/*
+ * linux/arch/sparc/mm/extable.c
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+
+void sort_extable(struct exception_table_entry *start,
+		  struct exception_table_entry *finish)
+{
+}
+
+/* Caller knows they are in a range if ret->fixup == 0 */
+const struct exception_table_entry *
+search_extable(const struct exception_table_entry *start,
+	       const struct exception_table_entry *last,
+	       unsigned long value)
+{
+	const struct exception_table_entry *walk;
+
+	/* Single insn entries are encoded as:
+	 *	word 1:	insn address
+	 *	word 2:	fixup code address
+	 *
+	 * Range entries are encoded as:
+	 *	word 1: first insn address
+	 *	word 2: 0
+	 *	word 3: last insn address + 4 bytes
+	 *	word 4: fixup code address
+	 *
+	 * See asm/uaccess.h for more details.
+	 */
+
+	/* 1. Try to find an exact match. */
+	for (walk = start; walk <= last; walk++) {
+		if (walk->fixup == 0) {
+			/* A range entry, skip both parts. */
+			walk++;
+			continue;
+		}
+
+		if (walk->insn == value)
+			return walk;
+	}
+
+	/* 2. Try to find a range match. */
+	for (walk = start; walk <= (last - 1); walk++) {
+		if (walk->fixup)
+			continue;
+
+		if (walk[0].insn <= value && walk[1].insn > value)
+			return walk;
+
+		walk++;
+	}
+
+        return NULL;
+}
+
+/* Special extable search, which handles ranges.  Returns fixup */
+unsigned long search_extables_range(unsigned long addr, unsigned long *g2)
+{
+	const struct exception_table_entry *entry;
+
+	entry = search_exception_tables(addr);
+	if (!entry)
+		return 0;
+
+	/* Inside range?  Fix g2 and return correct fixup */
+	if (!entry->fixup) {
+		*g2 = (addr - entry->insn) / 4;
+		return (entry + 1)->fixup;
+	}
+
+	return entry->fixup;
+}
diff --git a/arch/sparc/mm/fault.c b/arch/sparc/mm/fault.c
new file mode 100644
index 0000000..37f4107
--- /dev/null
+++ b/arch/sparc/mm/fault.c
@@ -0,0 +1,596 @@
+/* $Id: fault.c,v 1.122 2001/11/17 07:19:26 davem Exp $
+ * fault.c:  Page fault handlers for the Sparc.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <asm/head.h>
+
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+#include <linux/threads.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/memreg.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/smp.h>
+#include <asm/traps.h>
+#include <asm/kdebug.h>
+#include <asm/uaccess.h>
+
+#define ELEMENTS(arr) (sizeof (arr)/sizeof (arr[0]))
+
+extern int prom_node_root;
+
+/* At boot time we determine these two values necessary for setting
+ * up the segment maps and page table entries (pte's).
+ */
+
+int num_segmaps, num_contexts;
+int invalid_segment;
+
+/* various Virtual Address Cache parameters we find at boot time... */
+
+int vac_size, vac_linesize, vac_do_hw_vac_flushes;
+int vac_entries_per_context, vac_entries_per_segment;
+int vac_entries_per_page;
+
+/* Nice, simple, prom library does all the sweating for us. ;) */
+int prom_probe_memory (void)
+{
+	register struct linux_mlist_v0 *mlist;
+	register unsigned long bytes, base_paddr, tally;
+	register int i;
+
+	i = 0;
+	mlist= *prom_meminfo()->v0_available;
+	bytes = tally = mlist->num_bytes;
+	base_paddr = (unsigned long) mlist->start_adr;
+  
+	sp_banks[0].base_addr = base_paddr;
+	sp_banks[0].num_bytes = bytes;
+
+	while (mlist->theres_more != (void *) 0){
+		i++;
+		mlist = mlist->theres_more;
+		bytes = mlist->num_bytes;
+		tally += bytes;
+		if (i > SPARC_PHYS_BANKS-1) {
+			printk ("The machine has more banks than "
+				"this kernel can support\n"
+				"Increase the SPARC_PHYS_BANKS "
+				"setting (currently %d)\n",
+				SPARC_PHYS_BANKS);
+			i = SPARC_PHYS_BANKS-1;
+			break;
+		}
+    
+		sp_banks[i].base_addr = (unsigned long) mlist->start_adr;
+		sp_banks[i].num_bytes = mlist->num_bytes;
+	}
+
+	i++;
+	sp_banks[i].base_addr = 0xdeadbeef;
+	sp_banks[i].num_bytes = 0;
+
+	/* Now mask all bank sizes on a page boundary, it is all we can
+	 * use anyways.
+	 */
+	for(i=0; sp_banks[i].num_bytes != 0; i++)
+		sp_banks[i].num_bytes &= PAGE_MASK;
+
+	return tally;
+}
+
+/* Traverse the memory lists in the prom to see how much physical we
+ * have.
+ */
+unsigned long
+probe_memory(void)
+{
+	int total;
+
+	total = prom_probe_memory();
+
+	/* Oh man, much nicer, keep the dirt in promlib. */
+	return total;
+}
+
+extern void sun4c_complete_all_stores(void);
+
+/* Whee, a level 15 NMI interrupt memory error.  Let's have fun... */
+asmlinkage void sparc_lvl15_nmi(struct pt_regs *regs, unsigned long serr,
+				unsigned long svaddr, unsigned long aerr,
+				unsigned long avaddr)
+{
+	sun4c_complete_all_stores();
+	printk("FAULT: NMI received\n");
+	printk("SREGS: Synchronous Error %08lx\n", serr);
+	printk("       Synchronous Vaddr %08lx\n", svaddr);
+	printk("      Asynchronous Error %08lx\n", aerr);
+	printk("      Asynchronous Vaddr %08lx\n", avaddr);
+	if (sun4c_memerr_reg)
+		printk("     Memory Parity Error %08lx\n", *sun4c_memerr_reg);
+	printk("REGISTER DUMP:\n");
+	show_regs(regs);
+	prom_halt();
+}
+
+static void unhandled_fault(unsigned long, struct task_struct *,
+		struct pt_regs *) __attribute__ ((noreturn));
+
+static void unhandled_fault(unsigned long address, struct task_struct *tsk,
+                     struct pt_regs *regs)
+{
+	if((unsigned long) address < PAGE_SIZE) {
+		printk(KERN_ALERT
+		    "Unable to handle kernel NULL pointer dereference\n");
+	} else {
+		printk(KERN_ALERT "Unable to handle kernel paging request "
+		       "at virtual address %08lx\n", address);
+	}
+	printk(KERN_ALERT "tsk->{mm,active_mm}->context = %08lx\n",
+		(tsk->mm ? tsk->mm->context : tsk->active_mm->context));
+	printk(KERN_ALERT "tsk->{mm,active_mm}->pgd = %08lx\n",
+		(tsk->mm ? (unsigned long) tsk->mm->pgd :
+		 	(unsigned long) tsk->active_mm->pgd));
+	die_if_kernel("Oops", regs);
+}
+
+asmlinkage int lookup_fault(unsigned long pc, unsigned long ret_pc, 
+			    unsigned long address)
+{
+	struct pt_regs regs;
+	unsigned long g2;
+	unsigned int insn;
+	int i;
+	
+	i = search_extables_range(ret_pc, &g2);
+	switch (i) {
+	case 3:
+		/* load & store will be handled by fixup */
+		return 3;
+
+	case 1:
+		/* store will be handled by fixup, load will bump out */
+		/* for _to_ macros */
+		insn = *((unsigned int *) pc);
+		if ((insn >> 21) & 1)
+			return 1;
+		break;
+
+	case 2:
+		/* load will be handled by fixup, store will bump out */
+		/* for _from_ macros */
+		insn = *((unsigned int *) pc);
+		if (!((insn >> 21) & 1) || ((insn>>19)&0x3f) == 15)
+			return 2; 
+		break; 
+
+	default:
+		break;
+	};
+
+	memset(&regs, 0, sizeof (regs));
+	regs.pc = pc;
+	regs.npc = pc + 4;
+	__asm__ __volatile__(
+		"rd %%psr, %0\n\t"
+		"nop\n\t"
+		"nop\n\t"
+		"nop\n" : "=r" (regs.psr));
+	unhandled_fault(address, current, &regs);
+
+	/* Not reached */
+	return 0;
+}
+
+extern unsigned long safe_compute_effective_address(struct pt_regs *,
+						    unsigned int);
+
+static unsigned long compute_si_addr(struct pt_regs *regs, int text_fault)
+{
+	unsigned int insn;
+
+	if (text_fault)
+		return regs->pc;
+
+	if (regs->psr & PSR_PS) {
+		insn = *(unsigned int *) regs->pc;
+	} else {
+		__get_user(insn, (unsigned int *) regs->pc);
+	}
+
+	return safe_compute_effective_address(regs, insn);
+}
+
+asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write,
+			       unsigned long address)
+{
+	struct vm_area_struct *vma;
+	struct task_struct *tsk = current;
+	struct mm_struct *mm = tsk->mm;
+	unsigned int fixup;
+	unsigned long g2;
+	siginfo_t info;
+	int from_user = !(regs->psr & PSR_PS);
+
+	if(text_fault)
+		address = regs->pc;
+
+	/*
+	 * We fault-in kernel-space virtual memory on-demand. The
+	 * 'reference' page table is init_mm.pgd.
+	 *
+	 * NOTE! We MUST NOT take any locks for this case. We may
+	 * be in an interrupt or a critical region, and should
+	 * only copy the information from the master page table,
+	 * nothing more.
+	 */
+	if (!ARCH_SUN4C_SUN4 && address >= TASK_SIZE)
+		goto vmalloc_fault;
+
+	info.si_code = SEGV_MAPERR;
+
+	/*
+	 * If we're in an interrupt or have no user
+	 * context, we must not take the fault..
+	 */
+        if (in_atomic() || !mm)
+                goto no_context;
+
+	down_read(&mm->mmap_sem);
+
+	/*
+	 * The kernel referencing a bad kernel pointer can lock up
+	 * a sun4c machine completely, so we must attempt recovery.
+	 */
+	if(!from_user && address >= PAGE_OFFSET)
+		goto bad_area;
+
+	vma = find_vma(mm, address);
+	if(!vma)
+		goto bad_area;
+	if(vma->vm_start <= address)
+		goto good_area;
+	if(!(vma->vm_flags & VM_GROWSDOWN))
+		goto bad_area;
+	if(expand_stack(vma, address))
+		goto bad_area;
+	/*
+	 * Ok, we have a good vm_area for this memory access, so
+	 * we can handle it..
+	 */
+good_area:
+	info.si_code = SEGV_ACCERR;
+	if(write) {
+		if(!(vma->vm_flags & VM_WRITE))
+			goto bad_area;
+	} else {
+		/* Allow reads even for write-only mappings */
+		if(!(vma->vm_flags & (VM_READ | VM_EXEC)))
+			goto bad_area;
+	}
+
+	/*
+	 * If for any reason at all we couldn't handle the fault,
+	 * make sure we exit gracefully rather than endlessly redo
+	 * the fault.
+	 */
+	switch (handle_mm_fault(mm, vma, address, write)) {
+	case VM_FAULT_SIGBUS:
+		goto do_sigbus;
+	case VM_FAULT_OOM:
+		goto out_of_memory;
+	case VM_FAULT_MAJOR:
+		current->maj_flt++;
+		break;
+	case VM_FAULT_MINOR:
+	default:
+		current->min_flt++;
+		break;
+	}
+	up_read(&mm->mmap_sem);
+	return;
+
+	/*
+	 * Something tried to access memory that isn't in our memory map..
+	 * Fix it, but check if it's kernel or user first..
+	 */
+bad_area:
+	up_read(&mm->mmap_sem);
+
+bad_area_nosemaphore:
+	/* User mode accesses just cause a SIGSEGV */
+	if(from_user) {
+#if 0
+		printk("Fault whee %s [%d]: segfaults at %08lx pc=%08lx\n",
+		       tsk->comm, tsk->pid, address, regs->pc);
+#endif
+		info.si_signo = SIGSEGV;
+		info.si_errno = 0;
+		/* info.si_code set above to make clear whether
+		   this was a SEGV_MAPERR or SEGV_ACCERR fault.  */
+		info.si_addr = (void __user *)compute_si_addr(regs, text_fault);
+		info.si_trapno = 0;
+		force_sig_info (SIGSEGV, &info, tsk);
+		return;
+	}
+
+	/* Is this in ex_table? */
+no_context:
+	g2 = regs->u_regs[UREG_G2];
+	if (!from_user && (fixup = search_extables_range(regs->pc, &g2))) {
+		if (fixup > 10) { /* Values below are reserved for other things */
+			extern const unsigned __memset_start[];
+			extern const unsigned __memset_end[];
+			extern const unsigned __csum_partial_copy_start[];
+			extern const unsigned __csum_partial_copy_end[];
+
+#ifdef DEBUG_EXCEPTIONS
+			printk("Exception: PC<%08lx> faddr<%08lx>\n", regs->pc, address);
+			printk("EX_TABLE: insn<%08lx> fixup<%08x> g2<%08lx>\n",
+				regs->pc, fixup, g2);
+#endif
+			if ((regs->pc >= (unsigned long)__memset_start &&
+			     regs->pc < (unsigned long)__memset_end) ||
+			    (regs->pc >= (unsigned long)__csum_partial_copy_start &&
+			     regs->pc < (unsigned long)__csum_partial_copy_end)) {
+			        regs->u_regs[UREG_I4] = address;
+				regs->u_regs[UREG_I5] = regs->pc;
+			}
+			regs->u_regs[UREG_G2] = g2;
+			regs->pc = fixup;
+			regs->npc = regs->pc + 4;
+			return;
+		}
+	}
+	
+	unhandled_fault (address, tsk, regs);
+	do_exit(SIGKILL);
+
+/*
+ * We ran out of memory, or some other thing happened to us that made
+ * us unable to handle the page fault gracefully.
+ */
+out_of_memory:
+	up_read(&mm->mmap_sem);
+	printk("VM: killing process %s\n", tsk->comm);
+	if (from_user)
+		do_exit(SIGKILL);
+	goto no_context;
+
+do_sigbus:
+	up_read(&mm->mmap_sem);
+	info.si_signo = SIGBUS;
+	info.si_errno = 0;
+	info.si_code = BUS_ADRERR;
+	info.si_addr = (void __user *) compute_si_addr(regs, text_fault);
+	info.si_trapno = 0;
+	force_sig_info (SIGBUS, &info, tsk);
+	if (!from_user)
+		goto no_context;
+
+vmalloc_fault:
+	{
+		/*
+		 * Synchronize this task's top level page-table
+		 * with the 'reference' page table.
+		 */
+		int offset = pgd_index(address);
+		pgd_t *pgd, *pgd_k;
+		pmd_t *pmd, *pmd_k;
+
+		pgd = tsk->active_mm->pgd + offset;
+		pgd_k = init_mm.pgd + offset;
+
+		if (!pgd_present(*pgd)) {
+			if (!pgd_present(*pgd_k))
+				goto bad_area_nosemaphore;
+			pgd_val(*pgd) = pgd_val(*pgd_k);
+			return;
+		}
+
+		pmd = pmd_offset(pgd, address);
+		pmd_k = pmd_offset(pgd_k, address);
+
+		if (pmd_present(*pmd) || !pmd_present(*pmd_k))
+			goto bad_area_nosemaphore;
+		*pmd = *pmd_k;
+		return;
+	}
+}
+
+asmlinkage void do_sun4c_fault(struct pt_regs *regs, int text_fault, int write,
+			       unsigned long address)
+{
+	extern void sun4c_update_mmu_cache(struct vm_area_struct *,
+					   unsigned long,pte_t);
+	extern pte_t *sun4c_pte_offset_kernel(pmd_t *,unsigned long);
+	struct task_struct *tsk = current;
+	struct mm_struct *mm = tsk->mm;
+	pgd_t *pgdp;
+	pte_t *ptep;
+
+	if (text_fault) {
+		address = regs->pc;
+	} else if (!write &&
+		   !(regs->psr & PSR_PS)) {
+		unsigned int insn, __user *ip;
+
+		ip = (unsigned int __user *)regs->pc;
+		if (!get_user(insn, ip)) {
+			if ((insn & 0xc1680000) == 0xc0680000)
+				write = 1;
+		}
+	}
+
+	if (!mm) {
+		/* We are oopsing. */
+		do_sparc_fault(regs, text_fault, write, address);
+		BUG();	/* P3 Oops already, you bitch */
+	}
+
+	pgdp = pgd_offset(mm, address);
+	ptep = sun4c_pte_offset_kernel((pmd_t *) pgdp, address);
+
+	if (pgd_val(*pgdp)) {
+	    if (write) {
+		if ((pte_val(*ptep) & (_SUN4C_PAGE_WRITE|_SUN4C_PAGE_PRESENT))
+				   == (_SUN4C_PAGE_WRITE|_SUN4C_PAGE_PRESENT)) {
+			unsigned long flags;
+
+			*ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_ACCESSED |
+				      _SUN4C_PAGE_MODIFIED |
+				      _SUN4C_PAGE_VALID |
+				      _SUN4C_PAGE_DIRTY);
+
+			local_irq_save(flags);
+			if (sun4c_get_segmap(address) != invalid_segment) {
+				sun4c_put_pte(address, pte_val(*ptep));
+				local_irq_restore(flags);
+				return;
+			}
+			local_irq_restore(flags);
+		}
+	    } else {
+		if ((pte_val(*ptep) & (_SUN4C_PAGE_READ|_SUN4C_PAGE_PRESENT))
+				   == (_SUN4C_PAGE_READ|_SUN4C_PAGE_PRESENT)) {
+			unsigned long flags;
+
+			*ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_ACCESSED |
+				      _SUN4C_PAGE_VALID);
+
+			local_irq_save(flags);
+			if (sun4c_get_segmap(address) != invalid_segment) {
+				sun4c_put_pte(address, pte_val(*ptep));
+				local_irq_restore(flags);
+				return;
+			}
+			local_irq_restore(flags);
+		}
+	    }
+	}
+
+	/* This conditional is 'interesting'. */
+	if (pgd_val(*pgdp) && !(write && !(pte_val(*ptep) & _SUN4C_PAGE_WRITE))
+	    && (pte_val(*ptep) & _SUN4C_PAGE_VALID))
+		/* Note: It is safe to not grab the MMAP semaphore here because
+		 *       we know that update_mmu_cache() will not sleep for
+		 *       any reason (at least not in the current implementation)
+		 *       and therefore there is no danger of another thread getting
+		 *       on the CPU and doing a shrink_mmap() on this vma.
+		 */
+		sun4c_update_mmu_cache (find_vma(current->mm, address), address,
+					*ptep);
+	else
+		do_sparc_fault(regs, text_fault, write, address);
+}
+
+/* This always deals with user addresses. */
+inline void force_user_fault(unsigned long address, int write)
+{
+	struct vm_area_struct *vma;
+	struct task_struct *tsk = current;
+	struct mm_struct *mm = tsk->mm;
+	siginfo_t info;
+
+	info.si_code = SEGV_MAPERR;
+
+#if 0
+	printk("wf<pid=%d,wr=%d,addr=%08lx>\n",
+	       tsk->pid, write, address);
+#endif
+	down_read(&mm->mmap_sem);
+	vma = find_vma(mm, address);
+	if(!vma)
+		goto bad_area;
+	if(vma->vm_start <= address)
+		goto good_area;
+	if(!(vma->vm_flags & VM_GROWSDOWN))
+		goto bad_area;
+	if(expand_stack(vma, address))
+		goto bad_area;
+good_area:
+	info.si_code = SEGV_ACCERR;
+	if(write) {
+		if(!(vma->vm_flags & VM_WRITE))
+			goto bad_area;
+	} else {
+		if(!(vma->vm_flags & (VM_READ | VM_EXEC)))
+			goto bad_area;
+	}
+	switch (handle_mm_fault(mm, vma, address, write)) {
+	case VM_FAULT_SIGBUS:
+	case VM_FAULT_OOM:
+		goto do_sigbus;
+	}
+	up_read(&mm->mmap_sem);
+	return;
+bad_area:
+	up_read(&mm->mmap_sem);
+#if 0
+	printk("Window whee %s [%d]: segfaults at %08lx\n",
+	       tsk->comm, tsk->pid, address);
+#endif
+	info.si_signo = SIGSEGV;
+	info.si_errno = 0;
+	/* info.si_code set above to make clear whether
+	   this was a SEGV_MAPERR or SEGV_ACCERR fault.  */
+	info.si_addr = (void __user *) address;
+	info.si_trapno = 0;
+	force_sig_info (SIGSEGV, &info, tsk);
+	return;
+
+do_sigbus:
+	up_read(&mm->mmap_sem);
+	info.si_signo = SIGBUS;
+	info.si_errno = 0;
+	info.si_code = BUS_ADRERR;
+	info.si_addr = (void __user *) address;
+	info.si_trapno = 0;
+	force_sig_info (SIGBUS, &info, tsk);
+}
+
+void window_overflow_fault(void)
+{
+	unsigned long sp;
+
+	sp = current_thread_info()->rwbuf_stkptrs[0];
+	if(((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK))
+		force_user_fault(sp + 0x38, 1);
+	force_user_fault(sp, 1);
+}
+
+void window_underflow_fault(unsigned long sp)
+{
+	if(((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK))
+		force_user_fault(sp + 0x38, 0);
+	force_user_fault(sp, 0);
+}
+
+void window_ret_fault(struct pt_regs *regs)
+{
+	unsigned long sp;
+
+	sp = regs->u_regs[UREG_FP];
+	if(((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK))
+		force_user_fault(sp + 0x38, 0);
+	force_user_fault(sp, 0);
+}
diff --git a/arch/sparc/mm/generic.c b/arch/sparc/mm/generic.c
new file mode 100644
index 0000000..db27eee
--- /dev/null
+++ b/arch/sparc/mm/generic.c
@@ -0,0 +1,154 @@
+/* $Id: generic.c,v 1.14 2001/12/21 04:56:15 davem Exp $
+ * generic.c: Generic Sparc mm routines that are not dependent upon
+ *            MMU type but are Sparc specific.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/pagemap.h>
+
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+
+static inline void forget_pte(pte_t page)
+{
+#if 0 /* old 2.4 code */
+	if (pte_none(page))
+		return;
+	if (pte_present(page)) {
+		unsigned long pfn = pte_pfn(page);
+		struct page *ptpage;
+		if (!pfn_valid(pfn))
+			return;
+		ptpage = pfn_to_page(pfn);
+		if (PageReserved(ptpage))
+			return;
+		page_cache_release(ptpage);
+		return;
+	}
+	swap_free(pte_to_swp_entry(page));
+#else
+	if (!pte_none(page)) {
+		printk("forget_pte: old mapping existed!\n");
+		BUG();
+	}
+#endif
+}
+
+/* Remap IO memory, the same way as remap_pfn_range(), but use
+ * the obio memory space.
+ *
+ * They use a pgprot that sets PAGE_IO and does not check the
+ * mem_map table as this is independent of normal memory.
+ */
+static inline void io_remap_pte_range(struct mm_struct *mm, pte_t * pte, unsigned long address, unsigned long size,
+	unsigned long offset, pgprot_t prot, int space)
+{
+	unsigned long end;
+
+	address &= ~PMD_MASK;
+	end = address + size;
+	if (end > PMD_SIZE)
+		end = PMD_SIZE;
+	do {
+		pte_t oldpage = *pte;
+		pte_clear(mm, address, pte);
+		set_pte(pte, mk_pte_io(offset, prot, space));
+		forget_pte(oldpage);
+		address += PAGE_SIZE;
+		offset += PAGE_SIZE;
+		pte++;
+	} while (address < end);
+}
+
+static inline int io_remap_pmd_range(struct mm_struct *mm, pmd_t * pmd, unsigned long address, unsigned long size,
+	unsigned long offset, pgprot_t prot, int space)
+{
+	unsigned long end;
+
+	address &= ~PGDIR_MASK;
+	end = address + size;
+	if (end > PGDIR_SIZE)
+		end = PGDIR_SIZE;
+	offset -= address;
+	do {
+		pte_t * pte = pte_alloc_map(mm, pmd, address);
+		if (!pte)
+			return -ENOMEM;
+		io_remap_pte_range(mm, pte, address, end - address, address + offset, prot, space);
+		address = (address + PMD_SIZE) & PMD_MASK;
+		pmd++;
+	} while (address < end);
+	return 0;
+}
+
+int io_remap_page_range(struct vm_area_struct *vma, unsigned long from, unsigned long offset, unsigned long size, pgprot_t prot, int space)
+{
+	int error = 0;
+	pgd_t * dir;
+	unsigned long beg = from;
+	unsigned long end = from + size;
+	struct mm_struct *mm = vma->vm_mm;
+
+	prot = __pgprot(pg_iobits);
+	offset -= from;
+	dir = pgd_offset(mm, from);
+	flush_cache_range(vma, beg, end);
+
+	spin_lock(&mm->page_table_lock);
+	while (from < end) {
+		pmd_t *pmd = pmd_alloc(current->mm, dir, from);
+		error = -ENOMEM;
+		if (!pmd)
+			break;
+		error = io_remap_pmd_range(mm, pmd, from, end - from, offset + from, prot, space);
+		if (error)
+			break;
+		from = (from + PGDIR_SIZE) & PGDIR_MASK;
+		dir++;
+	}
+	spin_unlock(&mm->page_table_lock);
+
+	flush_tlb_range(vma, beg, end);
+	return error;
+}
+
+int io_remap_pfn_range(struct vm_area_struct *vma, unsigned long from,
+			unsigned long pfn, unsigned long size, pgprot_t prot)
+{
+	int error = 0;
+	pgd_t * dir;
+	unsigned long beg = from;
+	unsigned long end = from + size;
+	struct mm_struct *mm = vma->vm_mm;
+	int space = GET_IOSPACE(pfn);
+	unsigned long offset = GET_PFN(pfn) << PAGE_SHIFT;
+
+	prot = __pgprot(pg_iobits);
+	offset -= from;
+	dir = pgd_offset(mm, from);
+	flush_cache_range(vma, beg, end);
+
+	spin_lock(&mm->page_table_lock);
+	while (from < end) {
+		pmd_t *pmd = pmd_alloc(current->mm, dir, from);
+		error = -ENOMEM;
+		if (!pmd)
+			break;
+		error = io_remap_pmd_range(mm, pmd, from, end - from, offset + from, prot, space);
+		if (error)
+			break;
+		from = (from + PGDIR_SIZE) & PGDIR_MASK;
+		dir++;
+	}
+	spin_unlock(&mm->page_table_lock);
+
+	flush_tlb_range(vma, beg, end);
+	return error;
+}
diff --git a/arch/sparc/mm/highmem.c b/arch/sparc/mm/highmem.c
new file mode 100644
index 0000000..4d8ed9c
--- /dev/null
+++ b/arch/sparc/mm/highmem.c
@@ -0,0 +1,120 @@
+/*
+ *  highmem.c: virtual kernel memory mappings for high memory
+ *
+ *  Provides kernel-static versions of atomic kmap functions originally
+ *  found as inlines in include/asm-sparc/highmem.h.  These became
+ *  needed as kmap_atomic() and kunmap_atomic() started getting
+ *  called from within modules.
+ *  -- Tomas Szepe <szepe@pinerecords.com>, September 2002
+ *
+ *  But kmap_atomic() and kunmap_atomic() cannot be inlined in
+ *  modules because they are loaded with btfixup-ped functions.
+ */
+
+/*
+ * The use of kmap_atomic/kunmap_atomic is discouraged - kmap/kunmap
+ * gives a more generic (and caching) interface. But kmap_atomic can
+ * be used in IRQ contexts, so in some (very limited) cases we need it.
+ *
+ * XXX This is an old text. Actually, it's good to use atomic kmaps,
+ * provided you remember that they are atomic and not try to sleep
+ * with a kmap taken, much like a spinlock. Non-atomic kmaps are
+ * shared by CPUs, and so precious, and establishing them requires IPI.
+ * Atomic kmaps are lightweight and we may have NCPUS more of them.
+ */
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <asm/pgalloc.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+#include <asm/fixmap.h>
+
+void *kmap_atomic(struct page *page, enum km_type type)
+{
+	unsigned long idx;
+	unsigned long vaddr;
+
+	/* even !CONFIG_PREEMPT needs this, for in_atomic in do_page_fault */
+	inc_preempt_count();
+	if (!PageHighMem(page))
+		return page_address(page);
+
+	idx = type + KM_TYPE_NR*smp_processor_id();
+	vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
+
+/* XXX Fix - Anton */
+#if 0
+	__flush_cache_one(vaddr);
+#else
+	flush_cache_all();
+#endif
+
+#ifdef CONFIG_DEBUG_HIGHMEM
+	BUG_ON(!pte_none(*(kmap_pte-idx)));
+#endif
+	set_pte(kmap_pte-idx, mk_pte(page, kmap_prot));
+/* XXX Fix - Anton */
+#if 0
+	__flush_tlb_one(vaddr);
+#else
+	flush_tlb_all();
+#endif
+
+	return (void*) vaddr;
+}
+
+void kunmap_atomic(void *kvaddr, enum km_type type)
+{
+#ifdef CONFIG_DEBUG_HIGHMEM
+	unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
+	unsigned long idx = type + KM_TYPE_NR*smp_processor_id();
+
+	if (vaddr < FIXADDR_START) { // FIXME
+		dec_preempt_count();
+		preempt_check_resched();
+		return;
+	}
+
+	BUG_ON(vaddr != __fix_to_virt(FIX_KMAP_BEGIN+idx));
+
+/* XXX Fix - Anton */
+#if 0
+	__flush_cache_one(vaddr);
+#else
+	flush_cache_all();
+#endif
+
+	/*
+	 * force other mappings to Oops if they'll try to access
+	 * this pte without first remap it
+	 */
+	pte_clear(&init_mm, vaddr, kmap_pte-idx);
+/* XXX Fix - Anton */
+#if 0
+	__flush_tlb_one(vaddr);
+#else
+	flush_tlb_all();
+#endif
+#endif
+
+	dec_preempt_count();
+	preempt_check_resched();
+}
+
+/* We may be fed a pagetable here by ptep_to_xxx and others. */
+struct page *kmap_atomic_to_page(void *ptr)
+{
+	unsigned long idx, vaddr = (unsigned long)ptr;
+	pte_t *pte;
+
+	if (vaddr < SRMMU_NOCACHE_VADDR)
+		return virt_to_page(ptr);
+	if (vaddr < PKMAP_BASE)
+		return pfn_to_page(__nocache_pa(vaddr) >> PAGE_SHIFT);
+	BUG_ON(vaddr < FIXADDR_START);
+	BUG_ON(vaddr > FIXADDR_TOP);
+
+	idx = virt_to_fix(vaddr);
+	pte = kmap_pte - (idx - FIX_KMAP_BEGIN);
+	return pte_page(*pte);
+}
diff --git a/arch/sparc/mm/hypersparc.S b/arch/sparc/mm/hypersparc.S
new file mode 100644
index 0000000..54b8e76
--- /dev/null
+++ b/arch/sparc/mm/hypersparc.S
@@ -0,0 +1,413 @@
+/* $Id: hypersparc.S,v 1.18 2001/12/21 04:56:15 davem Exp $
+ * hypersparc.S: High speed Hypersparc mmu/cache operations.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/ptrace.h>
+#include <asm/psr.h>
+#include <asm/asm_offsets.h>
+#include <asm/asi.h>
+#include <asm/page.h>
+#include <asm/pgtsrmmu.h>
+#include <linux/config.h>
+#include <linux/init.h>
+
+	.text
+	.align	4
+
+	.globl	hypersparc_flush_cache_all, hypersparc_flush_cache_mm
+	.globl	hypersparc_flush_cache_range, hypersparc_flush_cache_page
+	.globl	hypersparc_flush_page_to_ram
+	.globl	hypersparc_flush_page_for_dma, hypersparc_flush_sig_insns
+	.globl	hypersparc_flush_tlb_all, hypersparc_flush_tlb_mm
+	.globl	hypersparc_flush_tlb_range, hypersparc_flush_tlb_page
+
+hypersparc_flush_cache_all:
+	WINDOW_FLUSH(%g4, %g5)
+	sethi	%hi(vac_cache_size), %g4
+	ld	[%g4 + %lo(vac_cache_size)], %g5
+	sethi	%hi(vac_line_size), %g1
+	ld	[%g1 + %lo(vac_line_size)], %g2
+1:	
+	subcc	%g5, %g2, %g5			! hyper_flush_unconditional_combined
+	bne	1b
+	 sta	%g0, [%g5] ASI_M_FLUSH_CTX
+	retl
+	 sta	%g0, [%g0] ASI_M_FLUSH_IWHOLE	! hyper_flush_whole_icache
+
+	/* We expand the window flush to get maximum performance. */
+hypersparc_flush_cache_mm:
+#ifndef CONFIG_SMP
+	ld	[%o0 + AOFF_mm_context], %g1
+	cmp	%g1, -1
+	be	hypersparc_flush_cache_mm_out
+#endif
+	WINDOW_FLUSH(%g4, %g5)
+
+	sethi	%hi(vac_line_size), %g1
+	ld	[%g1 + %lo(vac_line_size)], %o1
+	sethi	%hi(vac_cache_size), %g2
+	ld	[%g2 + %lo(vac_cache_size)], %o0
+	add	%o1, %o1, %g1
+	add	%o1, %g1, %g2
+	add	%o1, %g2, %g3
+	add	%o1, %g3, %g4
+	add	%o1, %g4, %g5
+	add	%o1, %g5, %o4
+	add	%o1, %o4, %o5
+
+	/* BLAMMO! */
+1:
+	subcc	%o0, %o5, %o0				! hyper_flush_cache_user
+	sta	%g0, [%o0 + %g0] ASI_M_FLUSH_USER
+	sta	%g0, [%o0 + %o1] ASI_M_FLUSH_USER
+	sta	%g0, [%o0 + %g1] ASI_M_FLUSH_USER
+	sta	%g0, [%o0 + %g2] ASI_M_FLUSH_USER
+	sta	%g0, [%o0 + %g3] ASI_M_FLUSH_USER
+	sta	%g0, [%o0 + %g4] ASI_M_FLUSH_USER
+	sta	%g0, [%o0 + %g5] ASI_M_FLUSH_USER
+	bne	1b
+	 sta	%g0, [%o0 + %o4] ASI_M_FLUSH_USER
+hypersparc_flush_cache_mm_out:
+	retl
+	 nop
+
+	/* The things we do for performance... */
+hypersparc_flush_cache_range:
+	ld	[%o0 + 0x0], %o0		/* XXX vma->vm_mm, GROSS XXX */
+#ifndef CONFIG_SMP
+	ld	[%o0 + AOFF_mm_context], %g1
+	cmp	%g1, -1
+	be	hypersparc_flush_cache_range_out
+#endif
+	WINDOW_FLUSH(%g4, %g5)
+
+	sethi	%hi(vac_line_size), %g1
+	ld	[%g1 + %lo(vac_line_size)], %o4
+	sethi	%hi(vac_cache_size), %g2
+	ld	[%g2 + %lo(vac_cache_size)], %o3
+
+	/* Here comes the fun part... */
+	add	%o2, (PAGE_SIZE - 1), %o2
+	andn	%o1, (PAGE_SIZE - 1), %o1
+	add	%o4, %o4, %o5
+	andn	%o2, (PAGE_SIZE - 1), %o2
+	add	%o4, %o5, %g1
+	sub	%o2, %o1, %g4
+	add	%o4, %g1, %g2
+	sll	%o3, 2, %g5
+	add	%o4, %g2, %g3
+	cmp	%g4, %g5
+	add	%o4, %g3, %g4
+	blu	0f
+	 add	%o4, %g4, %g5
+	add	%o4, %g5, %g7
+
+	/* Flush entire user space, believe it or not this is quicker
+	 * than page at a time flushings for range > (cache_size<<2).
+	 */
+1:
+	subcc	%o3, %g7, %o3
+	sta	%g0, [%o3 + %g0] ASI_M_FLUSH_USER
+	sta	%g0, [%o3 + %o4] ASI_M_FLUSH_USER
+	sta	%g0, [%o3 + %o5] ASI_M_FLUSH_USER
+	sta	%g0, [%o3 + %g1] ASI_M_FLUSH_USER
+	sta	%g0, [%o3 + %g2] ASI_M_FLUSH_USER
+	sta	%g0, [%o3 + %g3] ASI_M_FLUSH_USER
+	sta	%g0, [%o3 + %g4] ASI_M_FLUSH_USER
+	bne	1b
+	 sta	%g0, [%o3 + %g5] ASI_M_FLUSH_USER
+	retl
+	 nop
+
+	/* Below our threshold, flush one page at a time. */
+0:
+	ld	[%o0 + AOFF_mm_context], %o0
+	mov	SRMMU_CTX_REG, %g7
+	lda	[%g7] ASI_M_MMUREGS, %o3
+	sta	%o0, [%g7] ASI_M_MMUREGS
+	add	%o2, -PAGE_SIZE, %o0
+1:
+	or	%o0, 0x400, %g7
+	lda	[%g7] ASI_M_FLUSH_PROBE, %g7
+	orcc	%g7, 0, %g0
+	be,a	3f
+	 mov	%o0, %o2
+	add	%o4, %g5, %g7
+2:
+	sub	%o2, %g7, %o2
+	sta	%g0, [%o2 + %g0] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o2 + %o4] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o2 + %o5] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o2 + %g1] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o2 + %g2] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o2 + %g3] ASI_M_FLUSH_PAGE
+	andcc	%o2, 0xffc, %g0
+	sta	%g0, [%o2 + %g4] ASI_M_FLUSH_PAGE
+	bne	2b
+	 sta	%g0, [%o2 + %g5] ASI_M_FLUSH_PAGE
+3:
+	cmp	%o2, %o1
+	bne	1b
+	 add	%o2, -PAGE_SIZE, %o0
+	mov	SRMMU_FAULT_STATUS, %g5
+	lda	[%g5] ASI_M_MMUREGS, %g0
+	mov	SRMMU_CTX_REG, %g7
+	sta	%o3, [%g7] ASI_M_MMUREGS
+hypersparc_flush_cache_range_out:
+	retl
+	 nop
+
+	/* HyperSparc requires a valid mapping where we are about to flush
+	 * in order to check for a physical tag match during the flush.
+	 */
+	/* Verified, my ass... */
+hypersparc_flush_cache_page:
+	ld	[%o0 + 0x0], %o0		/* XXX vma->vm_mm, GROSS XXX */
+	ld	[%o0 + AOFF_mm_context], %g2
+#ifndef CONFIG_SMP
+	cmp	%g2, -1
+	be	hypersparc_flush_cache_page_out
+#endif
+	WINDOW_FLUSH(%g4, %g5)
+
+	sethi	%hi(vac_line_size), %g1
+	ld	[%g1 + %lo(vac_line_size)], %o4
+	mov	SRMMU_CTX_REG, %o3
+	andn	%o1, (PAGE_SIZE - 1), %o1
+	lda	[%o3] ASI_M_MMUREGS, %o2
+	sta	%g2, [%o3] ASI_M_MMUREGS
+	or	%o1, 0x400, %o5
+	lda	[%o5] ASI_M_FLUSH_PROBE, %g1
+	orcc	%g0, %g1, %g0
+	be	2f
+	 add	%o4, %o4, %o5
+	sub	%o1, -PAGE_SIZE, %o1
+	add	%o4, %o5, %g1
+	add	%o4, %g1, %g2
+	add	%o4, %g2, %g3
+	add	%o4, %g3, %g4
+	add	%o4, %g4, %g5
+	add	%o4, %g5, %g7
+
+	/* BLAMMO! */
+1:
+	sub	%o1, %g7, %o1
+	sta	%g0, [%o1 + %g0] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o4] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o5] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %g1] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %g2] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %g3] ASI_M_FLUSH_PAGE
+	andcc	%o1, 0xffc, %g0
+	sta	%g0, [%o1 + %g4] ASI_M_FLUSH_PAGE
+	bne	1b
+	 sta	%g0, [%o1 + %g5] ASI_M_FLUSH_PAGE
+2:
+	mov	SRMMU_FAULT_STATUS, %g7
+	mov	SRMMU_CTX_REG, %g4
+	lda	[%g7] ASI_M_MMUREGS, %g0
+	sta	%o2, [%g4] ASI_M_MMUREGS
+hypersparc_flush_cache_page_out:
+	retl
+	 nop
+
+hypersparc_flush_sig_insns:
+	flush	%o1
+	retl
+	 flush	%o1 + 4
+
+	/* HyperSparc is copy-back. */
+hypersparc_flush_page_to_ram:
+	sethi	%hi(vac_line_size), %g1
+	ld	[%g1 + %lo(vac_line_size)], %o4
+	andn	%o0, (PAGE_SIZE - 1), %o0
+	add	%o4, %o4, %o5
+	or	%o0, 0x400, %g7
+	lda	[%g7] ASI_M_FLUSH_PROBE, %g5
+	add	%o4, %o5, %g1
+	orcc	%g5, 0, %g0
+	be	2f
+	 add	%o4, %g1, %g2
+	add	%o4, %g2, %g3
+	sub	%o0, -PAGE_SIZE, %o0
+	add	%o4, %g3, %g4
+	add	%o4, %g4, %g5
+	add	%o4, %g5, %g7
+
+	/* BLAMMO! */
+1:
+	sub	%o0, %g7, %o0
+	sta	%g0, [%o0 + %g0] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o0 + %o4] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o0 + %o5] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o0 + %g1] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o0 + %g2] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o0 + %g3] ASI_M_FLUSH_PAGE
+	andcc	%o0, 0xffc, %g0
+	sta	%g0, [%o0 + %g4] ASI_M_FLUSH_PAGE
+	bne	1b
+	 sta	%g0, [%o0 + %g5] ASI_M_FLUSH_PAGE
+2:
+	mov	SRMMU_FAULT_STATUS, %g1
+	retl
+	 lda	[%g1] ASI_M_MMUREGS, %g0
+
+	/* HyperSparc is IO cache coherent. */
+hypersparc_flush_page_for_dma:
+	retl
+	 nop
+
+	/* It was noted that at boot time a TLB flush all in a delay slot
+	 * can deliver an illegal instruction to the processor if the timing
+	 * is just right...
+	 */
+hypersparc_flush_tlb_all:
+	mov	0x400, %g1
+	sta	%g0, [%g1] ASI_M_FLUSH_PROBE
+	retl
+	 nop
+
+hypersparc_flush_tlb_mm:
+	mov	SRMMU_CTX_REG, %g1
+	ld	[%o0 + AOFF_mm_context], %o1
+	lda	[%g1] ASI_M_MMUREGS, %g5
+#ifndef CONFIG_SMP
+	cmp	%o1, -1
+	be	hypersparc_flush_tlb_mm_out
+#endif
+	 mov	0x300, %g2
+	sta	%o1, [%g1] ASI_M_MMUREGS
+	sta	%g0, [%g2] ASI_M_FLUSH_PROBE
+hypersparc_flush_tlb_mm_out:
+	retl
+	 sta	%g5, [%g1] ASI_M_MMUREGS
+
+hypersparc_flush_tlb_range:
+	ld	[%o0 + 0x00], %o0	/* XXX vma->vm_mm GROSS XXX */
+	mov	SRMMU_CTX_REG, %g1
+	ld	[%o0 + AOFF_mm_context], %o3
+	lda	[%g1] ASI_M_MMUREGS, %g5
+#ifndef CONFIG_SMP
+	cmp	%o3, -1
+	be	hypersparc_flush_tlb_range_out
+#endif
+	 sethi	%hi(~((1 << SRMMU_PGDIR_SHIFT) - 1)), %o4
+	sta	%o3, [%g1] ASI_M_MMUREGS
+	and	%o1, %o4, %o1
+	add	%o1, 0x200, %o1
+	sta	%g0, [%o1] ASI_M_FLUSH_PROBE
+1:
+	sub	%o1, %o4, %o1
+	cmp	%o1, %o2
+	blu,a	1b
+	 sta	%g0, [%o1] ASI_M_FLUSH_PROBE
+hypersparc_flush_tlb_range_out:
+	retl
+	 sta	%g5, [%g1] ASI_M_MMUREGS
+
+hypersparc_flush_tlb_page:
+	ld	[%o0 + 0x00], %o0	/* XXX vma->vm_mm GROSS XXX */
+	mov	SRMMU_CTX_REG, %g1
+	ld	[%o0 + AOFF_mm_context], %o3
+	andn	%o1, (PAGE_SIZE - 1), %o1
+#ifndef CONFIG_SMP
+	cmp	%o3, -1
+	be	hypersparc_flush_tlb_page_out
+#endif
+	 lda	[%g1] ASI_M_MMUREGS, %g5
+	sta	%o3, [%g1] ASI_M_MMUREGS
+	sta	%g0, [%o1] ASI_M_FLUSH_PROBE
+hypersparc_flush_tlb_page_out:
+	retl
+	 sta	%g5, [%g1] ASI_M_MMUREGS
+
+	__INIT
+	
+	/* High speed page clear/copy. */
+hypersparc_bzero_1page:
+/* NOTE: This routine has to be shorter than 40insns --jj */
+	clr	%g1
+	mov	32, %g2
+	mov	64, %g3
+	mov	96, %g4
+	mov	128, %g5
+	mov	160, %g7
+	mov	192, %o2
+	mov	224, %o3
+	mov	16, %o1
+1:
+	stda	%g0, [%o0 + %g0] ASI_M_BFILL
+	stda	%g0, [%o0 + %g2] ASI_M_BFILL
+	stda	%g0, [%o0 + %g3] ASI_M_BFILL
+	stda	%g0, [%o0 + %g4] ASI_M_BFILL
+	stda	%g0, [%o0 + %g5] ASI_M_BFILL
+	stda	%g0, [%o0 + %g7] ASI_M_BFILL
+	stda	%g0, [%o0 + %o2] ASI_M_BFILL
+	stda	%g0, [%o0 + %o3] ASI_M_BFILL
+	subcc	%o1, 1, %o1
+	bne	1b
+	 add	%o0, 256, %o0
+
+	retl
+	 nop
+
+hypersparc_copy_1page:
+/* NOTE: This routine has to be shorter than 70insns --jj */
+	sub	%o1, %o0, %o2		! difference
+	mov	16, %g1
+1:
+	sta	%o0, [%o0 + %o2] ASI_M_BCOPY
+	add	%o0, 32, %o0
+	sta	%o0, [%o0 + %o2] ASI_M_BCOPY
+	add	%o0, 32, %o0
+	sta	%o0, [%o0 + %o2] ASI_M_BCOPY
+	add	%o0, 32, %o0
+	sta	%o0, [%o0 + %o2] ASI_M_BCOPY
+	add	%o0, 32, %o0
+	sta	%o0, [%o0 + %o2] ASI_M_BCOPY
+	add	%o0, 32, %o0
+	sta	%o0, [%o0 + %o2] ASI_M_BCOPY
+	add	%o0, 32, %o0
+	sta	%o0, [%o0 + %o2] ASI_M_BCOPY
+	add	%o0, 32, %o0
+	sta	%o0, [%o0 + %o2] ASI_M_BCOPY
+	subcc	%g1, 1, %g1
+	bne	1b
+	 add	%o0, 32, %o0
+
+	retl
+	 nop
+
+	.globl	hypersparc_setup_blockops
+hypersparc_setup_blockops:
+	sethi	%hi(bzero_1page), %o0
+	or	%o0, %lo(bzero_1page), %o0
+	sethi	%hi(hypersparc_bzero_1page), %o1
+	or	%o1, %lo(hypersparc_bzero_1page), %o1
+	sethi	%hi(hypersparc_copy_1page), %o2
+	or	%o2, %lo(hypersparc_copy_1page), %o2
+	ld	[%o1], %o4
+1:
+	add	%o1, 4, %o1
+	st	%o4, [%o0]
+	add	%o0, 4, %o0
+	cmp	%o1, %o2
+	bne	1b
+	 ld	[%o1], %o4
+	sethi	%hi(__copy_1page), %o0
+	or	%o0, %lo(__copy_1page), %o0
+	sethi	%hi(hypersparc_setup_blockops), %o2
+	or	%o2, %lo(hypersparc_setup_blockops), %o2
+	ld	[%o1], %o4
+1:
+	add	%o1, 4, %o1
+	st	%o4, [%o0]
+	add	%o0, 4, %o0
+	cmp	%o1, %o2
+	bne	1b
+	 ld	[%o1], %o4
+	sta	%g0, [%g0] ASI_M_FLUSH_IWHOLE
+	retl
+	 nop
diff --git a/arch/sparc/mm/init.c b/arch/sparc/mm/init.c
new file mode 100644
index 0000000..a2dea69
--- /dev/null
+++ b/arch/sparc/mm/init.c
@@ -0,0 +1,515 @@
+/*  $Id: init.c,v 1.103 2001/11/19 19:03:08 davem Exp $
+ *  linux/arch/sparc/mm/init.c
+ *
+ *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *  Copyright (C) 1995 Eddie C. Dost (ecd@skynet.be)
+ *  Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *  Copyright (C) 2000 Anton Blanchard (anton@samba.org)
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/initrd.h>
+#include <linux/init.h>
+#include <linux/highmem.h>
+#include <linux/bootmem.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/vac-ops.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/vaddrs.h>
+#include <asm/pgalloc.h>	/* bug in asm-generic/tlb.h: check_pgt_cache */
+#include <asm/tlb.h>
+
+DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
+
+unsigned long *sparc_valid_addr_bitmap;
+
+unsigned long phys_base;
+unsigned long pfn_base;
+
+unsigned long page_kernel;
+
+struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS+1];
+unsigned long sparc_unmapped_base;
+
+struct pgtable_cache_struct pgt_quicklists;
+
+/* References to section boundaries */
+extern char __init_begin, __init_end, _start, _end, etext , edata;
+
+/* Initial ramdisk setup */
+extern unsigned int sparc_ramdisk_image;
+extern unsigned int sparc_ramdisk_size;
+
+unsigned long highstart_pfn, highend_pfn;
+
+pte_t *kmap_pte;
+pgprot_t kmap_prot;
+
+#define kmap_get_fixmap_pte(vaddr) \
+	pte_offset_kernel(pmd_offset(pgd_offset_k(vaddr), (vaddr)), (vaddr))
+
+void __init kmap_init(void)
+{
+	/* cache the first kmap pte */
+	kmap_pte = kmap_get_fixmap_pte(__fix_to_virt(FIX_KMAP_BEGIN));
+	kmap_prot = __pgprot(SRMMU_ET_PTE | SRMMU_PRIV | SRMMU_CACHE);
+}
+
+void show_mem(void)
+{
+	printk("Mem-info:\n");
+	show_free_areas();
+	printk("Free swap:       %6ldkB\n",
+	       nr_swap_pages << (PAGE_SHIFT-10));
+	printk("%ld pages of RAM\n", totalram_pages);
+	printk("%d free pages\n", nr_free_pages());
+#if 0 /* undefined pgtable_cache_size, pgd_cache_size */
+	printk("%ld pages in page table cache\n",pgtable_cache_size);
+#ifndef CONFIG_SMP
+	if (sparc_cpu_model == sun4m || sparc_cpu_model == sun4d)
+		printk("%ld entries in page dir cache\n",pgd_cache_size);
+#endif	
+#endif
+}
+
+void __init sparc_context_init(int numctx)
+{
+	int ctx;
+
+	ctx_list_pool = __alloc_bootmem(numctx * sizeof(struct ctx_list), SMP_CACHE_BYTES, 0UL);
+
+	for(ctx = 0; ctx < numctx; ctx++) {
+		struct ctx_list *clist;
+
+		clist = (ctx_list_pool + ctx);
+		clist->ctx_number = ctx;
+		clist->ctx_mm = NULL;
+	}
+	ctx_free.next = ctx_free.prev = &ctx_free;
+	ctx_used.next = ctx_used.prev = &ctx_used;
+	for(ctx = 0; ctx < numctx; ctx++)
+		add_to_free_ctxlist(ctx_list_pool + ctx);
+}
+
+extern unsigned long cmdline_memory_size;
+unsigned long last_valid_pfn;
+
+unsigned long calc_highpages(void)
+{
+	int i;
+	int nr = 0;
+
+	for (i = 0; sp_banks[i].num_bytes != 0; i++) {
+		unsigned long start_pfn = sp_banks[i].base_addr >> PAGE_SHIFT;
+		unsigned long end_pfn = (sp_banks[i].base_addr + sp_banks[i].num_bytes) >> PAGE_SHIFT;
+
+		if (end_pfn <= max_low_pfn)
+			continue;
+
+		if (start_pfn < max_low_pfn)
+			start_pfn = max_low_pfn;
+
+		nr += end_pfn - start_pfn;
+	}
+
+	return nr;
+}
+
+unsigned long calc_max_low_pfn(void)
+{
+	int i;
+	unsigned long tmp = pfn_base + (SRMMU_MAXMEM >> PAGE_SHIFT);
+	unsigned long curr_pfn, last_pfn;
+
+	last_pfn = (sp_banks[0].base_addr + sp_banks[0].num_bytes) >> PAGE_SHIFT;
+	for (i = 1; sp_banks[i].num_bytes != 0; i++) {
+		curr_pfn = sp_banks[i].base_addr >> PAGE_SHIFT;
+
+		if (curr_pfn >= tmp) {
+			if (last_pfn < tmp)
+				tmp = last_pfn;
+			break;
+		}
+
+		last_pfn = (sp_banks[i].base_addr + sp_banks[i].num_bytes) >> PAGE_SHIFT;
+	}
+
+	return tmp;
+}
+
+unsigned long __init bootmem_init(unsigned long *pages_avail)
+{
+	unsigned long bootmap_size, start_pfn;
+	unsigned long end_of_phys_memory = 0UL;
+	unsigned long bootmap_pfn, bytes_avail, size;
+	int i;
+
+	bytes_avail = 0UL;
+	for (i = 0; sp_banks[i].num_bytes != 0; i++) {
+		end_of_phys_memory = sp_banks[i].base_addr +
+			sp_banks[i].num_bytes;
+		bytes_avail += sp_banks[i].num_bytes;
+		if (cmdline_memory_size) {
+			if (bytes_avail > cmdline_memory_size) {
+				unsigned long slack = bytes_avail - cmdline_memory_size;
+
+				bytes_avail -= slack;
+				end_of_phys_memory -= slack;
+
+				sp_banks[i].num_bytes -= slack;
+				if (sp_banks[i].num_bytes == 0) {
+					sp_banks[i].base_addr = 0xdeadbeef;
+				} else {
+					sp_banks[i+1].num_bytes = 0;
+					sp_banks[i+1].base_addr = 0xdeadbeef;
+				}
+				break;
+			}
+		}
+	}
+
+	/* Start with page aligned address of last symbol in kernel
+	 * image.  
+	 */
+	start_pfn  = (unsigned long)__pa(PAGE_ALIGN((unsigned long) &_end));
+
+	/* Now shift down to get the real physical page frame number. */
+	start_pfn >>= PAGE_SHIFT;
+
+	bootmap_pfn = start_pfn;
+
+	max_pfn = end_of_phys_memory >> PAGE_SHIFT;
+
+	max_low_pfn = max_pfn;
+	highstart_pfn = highend_pfn = max_pfn;
+
+	if (max_low_pfn > pfn_base + (SRMMU_MAXMEM >> PAGE_SHIFT)) {
+		highstart_pfn = pfn_base + (SRMMU_MAXMEM >> PAGE_SHIFT);
+		max_low_pfn = calc_max_low_pfn();
+		printk(KERN_NOTICE "%ldMB HIGHMEM available.\n",
+		    calc_highpages() >> (20 - PAGE_SHIFT));
+	}
+
+#ifdef CONFIG_BLK_DEV_INITRD
+	/* Now have to check initial ramdisk, so that bootmap does not overwrite it */
+	if (sparc_ramdisk_image) {
+		if (sparc_ramdisk_image >= (unsigned long)&_end - 2 * PAGE_SIZE)
+			sparc_ramdisk_image -= KERNBASE;
+		initrd_start = sparc_ramdisk_image + phys_base;
+		initrd_end = initrd_start + sparc_ramdisk_size;
+		if (initrd_end > end_of_phys_memory) {
+			printk(KERN_CRIT "initrd extends beyond end of memory "
+		                 	 "(0x%016lx > 0x%016lx)\ndisabling initrd\n",
+			       initrd_end, end_of_phys_memory);
+			initrd_start = 0;
+		}
+		if (initrd_start) {
+			if (initrd_start >= (start_pfn << PAGE_SHIFT) &&
+			    initrd_start < (start_pfn << PAGE_SHIFT) + 2 * PAGE_SIZE)
+				bootmap_pfn = PAGE_ALIGN (initrd_end) >> PAGE_SHIFT;
+		}
+	}
+#endif	
+	/* Initialize the boot-time allocator. */
+	bootmap_size = init_bootmem_node(NODE_DATA(0), bootmap_pfn, pfn_base,
+					 max_low_pfn);
+
+	/* Now register the available physical memory with the
+	 * allocator.
+	 */
+	*pages_avail = 0;
+	for (i = 0; sp_banks[i].num_bytes != 0; i++) {
+		unsigned long curr_pfn, last_pfn;
+
+		curr_pfn = sp_banks[i].base_addr >> PAGE_SHIFT;
+		if (curr_pfn >= max_low_pfn)
+			break;
+
+		last_pfn = (sp_banks[i].base_addr + sp_banks[i].num_bytes) >> PAGE_SHIFT;
+		if (last_pfn > max_low_pfn)
+			last_pfn = max_low_pfn;
+
+		/*
+		 * .. finally, did all the rounding and playing
+		 * around just make the area go away?
+		 */
+		if (last_pfn <= curr_pfn)
+			continue;
+
+		size = (last_pfn - curr_pfn) << PAGE_SHIFT;
+		*pages_avail += last_pfn - curr_pfn;
+
+		free_bootmem(sp_banks[i].base_addr, size);
+	}
+
+#ifdef CONFIG_BLK_DEV_INITRD
+	if (initrd_start) {
+		/* Reserve the initrd image area. */
+		size = initrd_end - initrd_start;
+		reserve_bootmem(initrd_start, size);
+		*pages_avail -= PAGE_ALIGN(size) >> PAGE_SHIFT;
+
+		initrd_start = (initrd_start - phys_base) + PAGE_OFFSET;
+		initrd_end = (initrd_end - phys_base) + PAGE_OFFSET;		
+	}
+#endif
+	/* Reserve the kernel text/data/bss. */
+	size = (start_pfn << PAGE_SHIFT) - phys_base;
+	reserve_bootmem(phys_base, size);
+	*pages_avail -= PAGE_ALIGN(size) >> PAGE_SHIFT;
+
+	/* Reserve the bootmem map.   We do not account for it
+	 * in pages_avail because we will release that memory
+	 * in free_all_bootmem.
+	 */
+	size = bootmap_size;
+	reserve_bootmem((bootmap_pfn << PAGE_SHIFT), size);
+	*pages_avail -= PAGE_ALIGN(size) >> PAGE_SHIFT;
+
+	return max_pfn;
+}
+
+/*
+ * check_pgt_cache
+ *
+ * This is called at the end of unmapping of VMA (zap_page_range),
+ * to rescan the page cache for architecture specific things,
+ * presumably something like sun4/sun4c PMEGs. Most architectures
+ * define check_pgt_cache empty.
+ *
+ * We simply copy the 2.4 implementation for now.
+ */
+int pgt_cache_water[2] = { 25, 50 };
+
+void check_pgt_cache(void)
+{
+	do_check_pgt_cache(pgt_cache_water[0], pgt_cache_water[1]);
+}
+
+/*
+ * paging_init() sets up the page tables: We call the MMU specific
+ * init routine based upon the Sun model type on the Sparc.
+ *
+ */
+extern void sun4c_paging_init(void);
+extern void srmmu_paging_init(void);
+extern void device_scan(void);
+
+void __init paging_init(void)
+{
+	switch(sparc_cpu_model) {
+	case sun4c:
+	case sun4e:
+	case sun4:
+		sun4c_paging_init();
+		sparc_unmapped_base = 0xe0000000;
+		BTFIXUPSET_SETHI(sparc_unmapped_base, 0xe0000000);
+		break;
+	case sun4m:
+	case sun4d:
+		srmmu_paging_init();
+		sparc_unmapped_base = 0x50000000;
+		BTFIXUPSET_SETHI(sparc_unmapped_base, 0x50000000);
+		break;
+	default:
+		prom_printf("paging_init: Cannot init paging on this Sparc\n");
+		prom_printf("paging_init: sparc_cpu_model = %d\n", sparc_cpu_model);
+		prom_printf("paging_init: Halting...\n");
+		prom_halt();
+	};
+
+	/* Initialize the protection map with non-constant, MMU dependent values. */
+	protection_map[0] = PAGE_NONE;
+	protection_map[1] = PAGE_READONLY;
+	protection_map[2] = PAGE_COPY;
+	protection_map[3] = PAGE_COPY;
+	protection_map[4] = PAGE_READONLY;
+	protection_map[5] = PAGE_READONLY;
+	protection_map[6] = PAGE_COPY;
+	protection_map[7] = PAGE_COPY;
+	protection_map[8] = PAGE_NONE;
+	protection_map[9] = PAGE_READONLY;
+	protection_map[10] = PAGE_SHARED;
+	protection_map[11] = PAGE_SHARED;
+	protection_map[12] = PAGE_READONLY;
+	protection_map[13] = PAGE_READONLY;
+	protection_map[14] = PAGE_SHARED;
+	protection_map[15] = PAGE_SHARED;
+	btfixup();
+	device_scan();
+}
+
+struct cache_palias *sparc_aliases;
+
+static void __init taint_real_pages(void)
+{
+	int i;
+
+	for (i = 0; sp_banks[i].num_bytes; i++) {
+		unsigned long start, end;
+
+		start = sp_banks[i].base_addr;
+		end = start + sp_banks[i].num_bytes;
+
+		while (start < end) {
+			set_bit(start >> 20, sparc_valid_addr_bitmap);
+			start += PAGE_SIZE;
+		}
+	}
+}
+
+void map_high_region(unsigned long start_pfn, unsigned long end_pfn)
+{
+	unsigned long tmp;
+
+#ifdef CONFIG_DEBUG_HIGHMEM
+	printk("mapping high region %08lx - %08lx\n", start_pfn, end_pfn);
+#endif
+
+	for (tmp = start_pfn; tmp < end_pfn; tmp++) {
+		struct page *page = pfn_to_page(tmp);
+
+		ClearPageReserved(page);
+		set_bit(PG_highmem, &page->flags);
+		set_page_count(page, 1);
+		__free_page(page);
+		totalhigh_pages++;
+	}
+}
+
+void __init mem_init(void)
+{
+	int codepages = 0;
+	int datapages = 0;
+	int initpages = 0; 
+	int reservedpages = 0;
+	int i;
+
+	if (PKMAP_BASE+LAST_PKMAP*PAGE_SIZE >= FIXADDR_START) {
+		prom_printf("BUG: fixmap and pkmap areas overlap\n");
+		prom_printf("pkbase: 0x%lx pkend: 0x%lx fixstart 0x%lx\n",
+		       PKMAP_BASE,
+		       (unsigned long)PKMAP_BASE+LAST_PKMAP*PAGE_SIZE,
+		       FIXADDR_START);
+		prom_printf("Please mail sparclinux@vger.kernel.org.\n");
+		prom_halt();
+	}
+
+
+	/* Saves us work later. */
+	memset((void *)&empty_zero_page, 0, PAGE_SIZE);
+
+	i = last_valid_pfn >> ((20 - PAGE_SHIFT) + 5);
+	i += 1;
+	sparc_valid_addr_bitmap = (unsigned long *)
+		__alloc_bootmem(i << 2, SMP_CACHE_BYTES, 0UL);
+
+	if (sparc_valid_addr_bitmap == NULL) {
+		prom_printf("mem_init: Cannot alloc valid_addr_bitmap.\n");
+		prom_halt();
+	}
+	memset(sparc_valid_addr_bitmap, 0, i << 2);
+
+	taint_real_pages();
+
+	max_mapnr = last_valid_pfn - pfn_base;
+	high_memory = __va(max_low_pfn << PAGE_SHIFT);
+
+	totalram_pages = free_all_bootmem();
+
+	for (i = 0; sp_banks[i].num_bytes != 0; i++) {
+		unsigned long start_pfn = sp_banks[i].base_addr >> PAGE_SHIFT;
+		unsigned long end_pfn = (sp_banks[i].base_addr + sp_banks[i].num_bytes) >> PAGE_SHIFT;
+
+		num_physpages += sp_banks[i].num_bytes >> PAGE_SHIFT;
+
+		if (end_pfn <= highstart_pfn)
+			continue;
+
+		if (start_pfn < highstart_pfn)
+			start_pfn = highstart_pfn;
+
+		map_high_region(start_pfn, end_pfn);
+	}
+	
+	totalram_pages += totalhigh_pages;
+
+	codepages = (((unsigned long) &etext) - ((unsigned long)&_start));
+	codepages = PAGE_ALIGN(codepages) >> PAGE_SHIFT;
+	datapages = (((unsigned long) &edata) - ((unsigned long)&etext));
+	datapages = PAGE_ALIGN(datapages) >> PAGE_SHIFT;
+	initpages = (((unsigned long) &__init_end) - ((unsigned long) &__init_begin));
+	initpages = PAGE_ALIGN(initpages) >> PAGE_SHIFT;
+
+	/* Ignore memory holes for the purpose of counting reserved pages */
+	for (i=0; i < max_low_pfn; i++)
+		if (test_bit(i >> (20 - PAGE_SHIFT), sparc_valid_addr_bitmap)
+		    && PageReserved(pfn_to_page(i)))
+			reservedpages++;
+
+	printk(KERN_INFO "Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init, %ldk highmem)\n",
+	       (unsigned long) nr_free_pages() << (PAGE_SHIFT-10),
+	       num_physpages << (PAGE_SHIFT - 10),
+	       codepages << (PAGE_SHIFT-10),
+	       reservedpages << (PAGE_SHIFT - 10),
+	       datapages << (PAGE_SHIFT-10), 
+	       initpages << (PAGE_SHIFT-10),
+	       totalhigh_pages << (PAGE_SHIFT-10));
+}
+
+void free_initmem (void)
+{
+	unsigned long addr;
+
+	addr = (unsigned long)(&__init_begin);
+	for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) {
+		struct page *p;
+
+		p = virt_to_page(addr);
+
+		ClearPageReserved(p);
+		set_page_count(p, 1);
+		__free_page(p);
+		totalram_pages++;
+		num_physpages++;
+	}
+	printk (KERN_INFO "Freeing unused kernel memory: %dk freed\n", (&__init_end - &__init_begin) >> 10);
+}
+
+#ifdef CONFIG_BLK_DEV_INITRD
+void free_initrd_mem(unsigned long start, unsigned long end)
+{
+	if (start < end)
+		printk (KERN_INFO "Freeing initrd memory: %ldk freed\n", (end - start) >> 10);
+	for (; start < end; start += PAGE_SIZE) {
+		struct page *p = virt_to_page(start);
+
+		ClearPageReserved(p);
+		set_page_count(p, 1);
+		__free_page(p);
+		num_physpages++;
+	}
+}
+#endif
+
+void sparc_flush_page_to_ram(struct page *page)
+{
+	unsigned long vaddr = (unsigned long)page_address(page);
+
+	if (vaddr)
+		__flush_page_to_ram(vaddr);
+}
diff --git a/arch/sparc/mm/io-unit.c b/arch/sparc/mm/io-unit.c
new file mode 100644
index 0000000..eefffa1
--- /dev/null
+++ b/arch/sparc/mm/io-unit.c
@@ -0,0 +1,318 @@
+/* $Id: io-unit.c,v 1.24 2001/12/17 07:05:09 davem Exp $
+ * io-unit.c:  IO-UNIT specific routines for memory management.
+ *
+ * Copyright (C) 1997,1998 Jakub Jelinek    (jj@sunsite.mff.cuni.cz)
+ */
+ 
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>	/* pte_offset_map => kmap_atomic */
+#include <linux/bitops.h>
+
+#include <asm/scatterlist.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/sbus.h>
+#include <asm/io.h>
+#include <asm/io-unit.h>
+#include <asm/mxcc.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+#include <asm/dma.h>
+
+/* #define IOUNIT_DEBUG */
+#ifdef IOUNIT_DEBUG
+#define IOD(x) printk(x)
+#else
+#define IOD(x) do { } while (0)
+#endif
+
+#define IOPERM        (IOUPTE_CACHE | IOUPTE_WRITE | IOUPTE_VALID)
+#define MKIOPTE(phys) __iopte((((phys)>>4) & IOUPTE_PAGE) | IOPERM)
+
+void __init
+iounit_init(int sbi_node, int io_node, struct sbus_bus *sbus)
+{
+	iopte_t *xpt, *xptend;
+	struct iounit_struct *iounit;
+	struct linux_prom_registers iommu_promregs[PROMREG_MAX];
+	struct resource r;
+
+	iounit = kmalloc(sizeof(struct iounit_struct), GFP_ATOMIC);
+
+	memset(iounit, 0, sizeof(*iounit));
+	iounit->limit[0] = IOUNIT_BMAP1_START;
+	iounit->limit[1] = IOUNIT_BMAP2_START;
+	iounit->limit[2] = IOUNIT_BMAPM_START;
+	iounit->limit[3] = IOUNIT_BMAPM_END;
+	iounit->rotor[1] = IOUNIT_BMAP2_START;
+	iounit->rotor[2] = IOUNIT_BMAPM_START;
+
+	xpt = NULL;
+	if(prom_getproperty(sbi_node, "reg", (void *) iommu_promregs,
+			    sizeof(iommu_promregs)) != -1) {
+		prom_apply_generic_ranges(io_node, 0, iommu_promregs, 3);
+		memset(&r, 0, sizeof(r));
+		r.flags = iommu_promregs[2].which_io;
+		r.start = iommu_promregs[2].phys_addr;
+		xpt = (iopte_t *) sbus_ioremap(&r, 0, PAGE_SIZE * 16, "XPT");
+	}
+	if(!xpt) panic("Cannot map External Page Table.");
+	
+	sbus->iommu = (struct iommu_struct *)iounit;
+	iounit->page_table = xpt;
+	
+	for (xptend = iounit->page_table + (16 * PAGE_SIZE) / sizeof(iopte_t);
+	     xpt < xptend;)
+	     	iopte_val(*xpt++) = 0;
+}
+
+/* One has to hold iounit->lock to call this */
+static unsigned long iounit_get_area(struct iounit_struct *iounit, unsigned long vaddr, int size)
+{
+	int i, j, k, npages;
+	unsigned long rotor, scan, limit;
+	iopte_t iopte;
+
+        npages = ((vaddr & ~PAGE_MASK) + size + (PAGE_SIZE-1)) >> PAGE_SHIFT;
+
+	/* A tiny bit of magic ingredience :) */
+	switch (npages) {
+	case 1: i = 0x0231; break;
+	case 2: i = 0x0132; break;
+	default: i = 0x0213; break;
+	}
+	
+	IOD(("iounit_get_area(%08lx,%d[%d])=", vaddr, size, npages));
+	
+next:	j = (i & 15);
+	rotor = iounit->rotor[j - 1];
+	limit = iounit->limit[j];
+	scan = rotor;
+nexti:	scan = find_next_zero_bit(iounit->bmap, limit, scan);
+	if (scan + npages > limit) {
+		if (limit != rotor) {
+			limit = rotor;
+			scan = iounit->limit[j - 1];
+			goto nexti;
+		}
+		i >>= 4;
+		if (!(i & 15))
+			panic("iounit_get_area: Couldn't find free iopte slots for (%08lx,%d)\n", vaddr, size);
+		goto next;
+	}
+	for (k = 1, scan++; k < npages; k++)
+		if (test_bit(scan++, iounit->bmap))
+			goto nexti;
+	iounit->rotor[j - 1] = (scan < limit) ? scan : iounit->limit[j - 1];
+	scan -= npages;
+	iopte = MKIOPTE(__pa(vaddr & PAGE_MASK));
+	vaddr = IOUNIT_DMA_BASE + (scan << PAGE_SHIFT) + (vaddr & ~PAGE_MASK);
+	for (k = 0; k < npages; k++, iopte = __iopte(iopte_val(iopte) + 0x100), scan++) {
+		set_bit(scan, iounit->bmap);
+		iounit->page_table[scan] = iopte;
+	}
+	IOD(("%08lx\n", vaddr));
+	return vaddr;
+}
+
+static __u32 iounit_get_scsi_one(char *vaddr, unsigned long len, struct sbus_bus *sbus)
+{
+	unsigned long ret, flags;
+	struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu;
+	
+	spin_lock_irqsave(&iounit->lock, flags);
+	ret = iounit_get_area(iounit, (unsigned long)vaddr, len);
+	spin_unlock_irqrestore(&iounit->lock, flags);
+	return ret;
+}
+
+static void iounit_get_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus *sbus)
+{
+	unsigned long flags;
+	struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu;
+
+	/* FIXME: Cache some resolved pages - often several sg entries are to the same page */
+	spin_lock_irqsave(&iounit->lock, flags);
+	while (sz != 0) {
+		--sz;
+		sg[sz].dvma_address = iounit_get_area(iounit, (unsigned long)page_address(sg[sz].page) + sg[sz].offset, sg[sz].length);
+		sg[sz].dvma_length = sg[sz].length;
+	}
+	spin_unlock_irqrestore(&iounit->lock, flags);
+}
+
+static void iounit_release_scsi_one(__u32 vaddr, unsigned long len, struct sbus_bus *sbus)
+{
+	unsigned long flags;
+	struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu;
+	
+	spin_lock_irqsave(&iounit->lock, flags);
+	len = ((vaddr & ~PAGE_MASK) + len + (PAGE_SIZE-1)) >> PAGE_SHIFT;
+	vaddr = (vaddr - IOUNIT_DMA_BASE) >> PAGE_SHIFT;
+	IOD(("iounit_release %08lx-%08lx\n", (long)vaddr, (long)len+vaddr));
+	for (len += vaddr; vaddr < len; vaddr++)
+		clear_bit(vaddr, iounit->bmap);
+	spin_unlock_irqrestore(&iounit->lock, flags);
+}
+
+static void iounit_release_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus *sbus)
+{
+	unsigned long flags;
+	unsigned long vaddr, len;
+	struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu;
+
+	spin_lock_irqsave(&iounit->lock, flags);
+	while (sz != 0) {
+		--sz;
+		len = ((sg[sz].dvma_address & ~PAGE_MASK) + sg[sz].length + (PAGE_SIZE-1)) >> PAGE_SHIFT;
+		vaddr = (sg[sz].dvma_address - IOUNIT_DMA_BASE) >> PAGE_SHIFT;
+		IOD(("iounit_release %08lx-%08lx\n", (long)vaddr, (long)len+vaddr));
+		for (len += vaddr; vaddr < len; vaddr++)
+			clear_bit(vaddr, iounit->bmap);
+	}
+	spin_unlock_irqrestore(&iounit->lock, flags);
+}
+
+#ifdef CONFIG_SBUS
+static int iounit_map_dma_area(dma_addr_t *pba, unsigned long va, __u32 addr, int len)
+{
+	unsigned long page, end;
+	pgprot_t dvma_prot;
+	iopte_t *iopte;
+	struct sbus_bus *sbus;
+
+	*pba = addr;
+
+	dvma_prot = __pgprot(SRMMU_CACHE | SRMMU_ET_PTE | SRMMU_PRIV);
+	end = PAGE_ALIGN((addr + len));
+	while(addr < end) {
+		page = va;
+		{
+			pgd_t *pgdp;
+			pmd_t *pmdp;
+			pte_t *ptep;
+			long i;
+
+			pgdp = pgd_offset(&init_mm, addr);
+			pmdp = pmd_offset(pgdp, addr);
+			ptep = pte_offset_map(pmdp, addr);
+
+			set_pte(ptep, mk_pte(virt_to_page(page), dvma_prot));
+			
+			i = ((addr - IOUNIT_DMA_BASE) >> PAGE_SHIFT);
+
+			for_each_sbus(sbus) {
+				struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu;
+
+				iopte = (iopte_t *)(iounit->page_table + i);
+				*iopte = MKIOPTE(__pa(page));
+			}
+		}
+		addr += PAGE_SIZE;
+		va += PAGE_SIZE;
+	}
+	flush_cache_all();
+	flush_tlb_all();
+
+	return 0;
+}
+
+static void iounit_unmap_dma_area(unsigned long addr, int len)
+{
+	/* XXX Somebody please fill this in */
+}
+
+/* XXX We do not pass sbus device here, bad. */
+static struct page *iounit_translate_dvma(unsigned long addr)
+{
+	struct sbus_bus *sbus = sbus_root;	/* They are all the same */
+	struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu;
+	int i;
+	iopte_t *iopte;
+
+	i = ((addr - IOUNIT_DMA_BASE) >> PAGE_SHIFT);
+	iopte = (iopte_t *)(iounit->page_table + i);
+	return pfn_to_page(iopte_val(*iopte) >> (PAGE_SHIFT-4)); /* XXX sun4d guru, help */
+}
+#endif
+
+static char *iounit_lockarea(char *vaddr, unsigned long len)
+{
+/* FIXME: Write this */
+	return vaddr;
+}
+
+static void iounit_unlockarea(char *vaddr, unsigned long len)
+{
+/* FIXME: Write this */
+}
+
+void __init ld_mmu_iounit(void)
+{
+	BTFIXUPSET_CALL(mmu_lockarea, iounit_lockarea, BTFIXUPCALL_RETO0);
+	BTFIXUPSET_CALL(mmu_unlockarea, iounit_unlockarea, BTFIXUPCALL_NOP);
+
+	BTFIXUPSET_CALL(mmu_get_scsi_one, iounit_get_scsi_one, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_get_scsi_sgl, iounit_get_scsi_sgl, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_release_scsi_one, iounit_release_scsi_one, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_release_scsi_sgl, iounit_release_scsi_sgl, BTFIXUPCALL_NORM);
+
+#ifdef CONFIG_SBUS
+	BTFIXUPSET_CALL(mmu_map_dma_area, iounit_map_dma_area, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_unmap_dma_area, iounit_unmap_dma_area, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_translate_dvma, iounit_translate_dvma, BTFIXUPCALL_NORM);
+#endif
+}
+
+__u32 iounit_map_dma_init(struct sbus_bus *sbus, int size)
+{
+	int i, j, k, npages;
+	unsigned long rotor, scan, limit;
+	unsigned long flags;
+	__u32 ret;
+	struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu;
+
+        npages = (size + (PAGE_SIZE-1)) >> PAGE_SHIFT;
+	i = 0x0213;
+	spin_lock_irqsave(&iounit->lock, flags);
+next:	j = (i & 15);
+	rotor = iounit->rotor[j - 1];
+	limit = iounit->limit[j];
+	scan = rotor;
+nexti:	scan = find_next_zero_bit(iounit->bmap, limit, scan);
+	if (scan + npages > limit) {
+		if (limit != rotor) {
+			limit = rotor;
+			scan = iounit->limit[j - 1];
+			goto nexti;
+		}
+		i >>= 4;
+		if (!(i & 15))
+			panic("iounit_map_dma_init: Couldn't find free iopte slots for %d bytes\n", size);
+		goto next;
+	}
+	for (k = 1, scan++; k < npages; k++)
+		if (test_bit(scan++, iounit->bmap))
+			goto nexti;
+	iounit->rotor[j - 1] = (scan < limit) ? scan : iounit->limit[j - 1];
+	scan -= npages;
+	ret = IOUNIT_DMA_BASE + (scan << PAGE_SHIFT);
+	for (k = 0; k < npages; k++, scan++)
+		set_bit(scan, iounit->bmap);
+	spin_unlock_irqrestore(&iounit->lock, flags);
+	return ret;
+}
+
+__u32 iounit_map_dma_page(__u32 vaddr, void *addr, struct sbus_bus *sbus)
+{
+	int scan = (vaddr - IOUNIT_DMA_BASE) >> PAGE_SHIFT;
+	struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu;
+	
+	iounit->page_table[scan] = MKIOPTE(__pa(((unsigned long)addr) & PAGE_MASK));
+	return vaddr + (((unsigned long)addr) & ~PAGE_MASK);
+}
diff --git a/arch/sparc/mm/iommu.c b/arch/sparc/mm/iommu.c
new file mode 100644
index 0000000..489bf68
--- /dev/null
+++ b/arch/sparc/mm/iommu.c
@@ -0,0 +1,475 @@
+/*
+ * iommu.c:  IOMMU specific routines for memory management.
+ *
+ * Copyright (C) 1995 David S. Miller  (davem@caip.rutgers.edu)
+ * Copyright (C) 1995,2002 Pete Zaitcev     (zaitcev@yahoo.com)
+ * Copyright (C) 1996 Eddie C. Dost    (ecd@skynet.be)
+ * Copyright (C) 1997,1998 Jakub Jelinek    (jj@sunsite.mff.cuni.cz)
+ */
+ 
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/highmem.h>	/* pte_offset_map => kmap_atomic */
+
+#include <asm/scatterlist.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/sbus.h>
+#include <asm/io.h>
+#include <asm/mxcc.h>
+#include <asm/mbus.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+#include <asm/bitext.h>
+#include <asm/iommu.h>
+#include <asm/dma.h>
+
+/*
+ * This can be sized dynamically, but we will do this
+ * only when we have a guidance about actual I/O pressures.
+ */
+#define IOMMU_RNGE	IOMMU_RNGE_256MB
+#define IOMMU_START	0xF0000000
+#define IOMMU_WINSIZE	(256*1024*1024U)
+#define IOMMU_NPTES	(IOMMU_WINSIZE/PAGE_SIZE)	/* 64K PTEs, 265KB */
+#define IOMMU_ORDER	6				/* 4096 * (1<<6) */
+
+/* srmmu.c */
+extern int viking_mxcc_present;
+BTFIXUPDEF_CALL(void, flush_page_for_dma, unsigned long)
+#define flush_page_for_dma(page) BTFIXUP_CALL(flush_page_for_dma)(page)
+extern int flush_page_for_dma_global;
+static int viking_flush;
+/* viking.S */
+extern void viking_flush_page(unsigned long page);
+extern void viking_mxcc_flush_page(unsigned long page);
+
+/*
+ * Values precomputed according to CPU type.
+ */
+static unsigned int ioperm_noc;		/* Consistent mapping iopte flags */
+static pgprot_t dvma_prot;		/* Consistent mapping pte flags */
+
+#define IOPERM        (IOPTE_CACHE | IOPTE_WRITE | IOPTE_VALID)
+#define MKIOPTE(pfn, perm) (((((pfn)<<8) & IOPTE_PAGE) | (perm)) & ~IOPTE_WAZ)
+
+void __init
+iommu_init(int iommund, struct sbus_bus *sbus)
+{
+	unsigned int impl, vers;
+	unsigned long tmp;
+	struct iommu_struct *iommu;
+	struct linux_prom_registers iommu_promregs[PROMREG_MAX];
+	struct resource r;
+	unsigned long *bitmap;
+
+	iommu = kmalloc(sizeof(struct iommu_struct), GFP_ATOMIC);
+	if (!iommu) {
+		prom_printf("Unable to allocate iommu structure\n");
+		prom_halt();
+	}
+	iommu->regs = NULL;
+	if (prom_getproperty(iommund, "reg", (void *) iommu_promregs,
+			 sizeof(iommu_promregs)) != -1) {
+		memset(&r, 0, sizeof(r));
+		r.flags = iommu_promregs[0].which_io;
+		r.start = iommu_promregs[0].phys_addr;
+		iommu->regs = (struct iommu_regs *)
+			sbus_ioremap(&r, 0, PAGE_SIZE * 3, "iommu_regs");
+	}
+	if (!iommu->regs) {
+		prom_printf("Cannot map IOMMU registers\n");
+		prom_halt();
+	}
+	impl = (iommu->regs->control & IOMMU_CTRL_IMPL) >> 28;
+	vers = (iommu->regs->control & IOMMU_CTRL_VERS) >> 24;
+	tmp = iommu->regs->control;
+	tmp &= ~(IOMMU_CTRL_RNGE);
+	tmp |= (IOMMU_RNGE_256MB | IOMMU_CTRL_ENAB);
+	iommu->regs->control = tmp;
+	iommu_invalidate(iommu->regs);
+	iommu->start = IOMMU_START;
+	iommu->end = 0xffffffff;
+
+	/* Allocate IOMMU page table */
+	/* Stupid alignment constraints give me a headache. 
+	   We need 256K or 512K or 1M or 2M area aligned to
+           its size and current gfp will fortunately give
+           it to us. */
+        tmp = __get_free_pages(GFP_KERNEL, IOMMU_ORDER);
+	if (!tmp) {
+		prom_printf("Unable to allocate iommu table [0x%08x]\n",
+			    IOMMU_NPTES*sizeof(iopte_t));
+		prom_halt();
+	}
+	iommu->page_table = (iopte_t *)tmp;
+
+	/* Initialize new table. */
+	memset(iommu->page_table, 0, IOMMU_NPTES*sizeof(iopte_t));
+	flush_cache_all();
+	flush_tlb_all();
+	iommu->regs->base = __pa((unsigned long) iommu->page_table) >> 4;
+	iommu_invalidate(iommu->regs);
+
+	bitmap = kmalloc(IOMMU_NPTES>>3, GFP_KERNEL);
+	if (!bitmap) {
+		prom_printf("Unable to allocate iommu bitmap [%d]\n",
+			    (int)(IOMMU_NPTES>>3));
+		prom_halt();
+	}
+	bit_map_init(&iommu->usemap, bitmap, IOMMU_NPTES);
+	/* To be coherent on HyperSparc, the page color of DVMA
+	 * and physical addresses must match.
+	 */
+	if (srmmu_modtype == HyperSparc)
+		iommu->usemap.num_colors = vac_cache_size >> PAGE_SHIFT;
+	else
+		iommu->usemap.num_colors = 1;
+
+	printk("IOMMU: impl %d vers %d table 0x%p[%d B] map [%d b]\n",
+	    impl, vers, iommu->page_table,
+	    (int)(IOMMU_NPTES*sizeof(iopte_t)), (int)IOMMU_NPTES);
+
+	sbus->iommu = iommu;
+}
+
+/* This begs to be btfixup-ed by srmmu. */
+/* Flush the iotlb entries to ram. */
+/* This could be better if we didn't have to flush whole pages. */
+static void iommu_flush_iotlb(iopte_t *iopte, unsigned int niopte)
+{
+	unsigned long start;
+	unsigned long end;
+
+	start = (unsigned long)iopte & PAGE_MASK;
+	end = PAGE_ALIGN(start + niopte*sizeof(iopte_t));
+	if (viking_mxcc_present) {
+		while(start < end) {
+			viking_mxcc_flush_page(start);
+			start += PAGE_SIZE;
+		}
+	} else if (viking_flush) {
+		while(start < end) {
+			viking_flush_page(start);
+			start += PAGE_SIZE;
+		}
+	} else {
+		while(start < end) {
+			__flush_page_to_ram(start);
+			start += PAGE_SIZE;
+		}
+	}
+}
+
+static u32 iommu_get_one(struct page *page, int npages, struct sbus_bus *sbus)
+{
+	struct iommu_struct *iommu = sbus->iommu;
+	int ioptex;
+	iopte_t *iopte, *iopte0;
+	unsigned int busa, busa0;
+	int i;
+
+	/* page color = pfn of page */
+	ioptex = bit_map_string_get(&iommu->usemap, npages, page_to_pfn(page));
+	if (ioptex < 0)
+		panic("iommu out");
+	busa0 = iommu->start + (ioptex << PAGE_SHIFT);
+	iopte0 = &iommu->page_table[ioptex];
+
+	busa = busa0;
+	iopte = iopte0;
+	for (i = 0; i < npages; i++) {
+		iopte_val(*iopte) = MKIOPTE(page_to_pfn(page), IOPERM);
+		iommu_invalidate_page(iommu->regs, busa);
+		busa += PAGE_SIZE;
+		iopte++;
+		page++;
+	}
+
+	iommu_flush_iotlb(iopte0, npages);
+
+	return busa0;
+}
+
+static u32 iommu_get_scsi_one(char *vaddr, unsigned int len,
+    struct sbus_bus *sbus)
+{
+	unsigned long off;
+	int npages;
+	struct page *page;
+	u32 busa;
+
+	off = (unsigned long)vaddr & ~PAGE_MASK;
+	npages = (off + len + PAGE_SIZE-1) >> PAGE_SHIFT;
+	page = virt_to_page((unsigned long)vaddr & PAGE_MASK);
+	busa = iommu_get_one(page, npages, sbus);
+	return busa + off;
+}
+
+static __u32 iommu_get_scsi_one_noflush(char *vaddr, unsigned long len, struct sbus_bus *sbus)
+{
+	return iommu_get_scsi_one(vaddr, len, sbus);
+}
+
+static __u32 iommu_get_scsi_one_gflush(char *vaddr, unsigned long len, struct sbus_bus *sbus)
+{
+	flush_page_for_dma(0);
+	return iommu_get_scsi_one(vaddr, len, sbus);
+}
+
+static __u32 iommu_get_scsi_one_pflush(char *vaddr, unsigned long len, struct sbus_bus *sbus)
+{
+	unsigned long page = ((unsigned long) vaddr) & PAGE_MASK;
+
+	while(page < ((unsigned long)(vaddr + len))) {
+		flush_page_for_dma(page);
+		page += PAGE_SIZE;
+	}
+	return iommu_get_scsi_one(vaddr, len, sbus);
+}
+
+static void iommu_get_scsi_sgl_noflush(struct scatterlist *sg, int sz, struct sbus_bus *sbus)
+{
+	int n;
+
+	while (sz != 0) {
+		--sz;
+		n = (sg->length + sg->offset + PAGE_SIZE-1) >> PAGE_SHIFT;
+		sg->dvma_address = iommu_get_one(sg->page, n, sbus) + sg->offset;
+		sg->dvma_length = (__u32) sg->length;
+		sg++;
+	}
+}
+
+static void iommu_get_scsi_sgl_gflush(struct scatterlist *sg, int sz, struct sbus_bus *sbus)
+{
+	int n;
+
+	flush_page_for_dma(0);
+	while (sz != 0) {
+		--sz;
+		n = (sg->length + sg->offset + PAGE_SIZE-1) >> PAGE_SHIFT;
+		sg->dvma_address = iommu_get_one(sg->page, n, sbus) + sg->offset;
+		sg->dvma_length = (__u32) sg->length;
+		sg++;
+	}
+}
+
+static void iommu_get_scsi_sgl_pflush(struct scatterlist *sg, int sz, struct sbus_bus *sbus)
+{
+	unsigned long page, oldpage = 0;
+	int n, i;
+
+	while(sz != 0) {
+		--sz;
+
+		n = (sg->length + sg->offset + PAGE_SIZE-1) >> PAGE_SHIFT;
+
+		/*
+		 * We expect unmapped highmem pages to be not in the cache.
+		 * XXX Is this a good assumption?
+		 * XXX What if someone else unmaps it here and races us?
+		 */
+		if ((page = (unsigned long) page_address(sg->page)) != 0) {
+			for (i = 0; i < n; i++) {
+				if (page != oldpage) {	/* Already flushed? */
+					flush_page_for_dma(page);
+					oldpage = page;
+				}
+				page += PAGE_SIZE;
+			}
+		}
+
+		sg->dvma_address = iommu_get_one(sg->page, n, sbus) + sg->offset;
+		sg->dvma_length = (__u32) sg->length;
+		sg++;
+	}
+}
+
+static void iommu_release_one(u32 busa, int npages, struct sbus_bus *sbus)
+{
+	struct iommu_struct *iommu = sbus->iommu;
+	int ioptex;
+	int i;
+
+	if (busa < iommu->start)
+		BUG();
+	ioptex = (busa - iommu->start) >> PAGE_SHIFT;
+	for (i = 0; i < npages; i++) {
+		iopte_val(iommu->page_table[ioptex + i]) = 0;
+		iommu_invalidate_page(iommu->regs, busa);
+		busa += PAGE_SIZE;
+	}
+	bit_map_clear(&iommu->usemap, ioptex, npages);
+}
+
+static void iommu_release_scsi_one(__u32 vaddr, unsigned long len, struct sbus_bus *sbus)
+{
+	unsigned long off;
+	int npages;
+
+	off = vaddr & ~PAGE_MASK;
+	npages = (off + len + PAGE_SIZE-1) >> PAGE_SHIFT;
+	iommu_release_one(vaddr & PAGE_MASK, npages, sbus);
+}
+
+static void iommu_release_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus *sbus)
+{
+	int n;
+
+	while(sz != 0) {
+		--sz;
+
+		n = (sg->length + sg->offset + PAGE_SIZE-1) >> PAGE_SHIFT;
+		iommu_release_one(sg->dvma_address & PAGE_MASK, n, sbus);
+		sg->dvma_address = 0x21212121;
+		sg++;
+	}
+}
+
+#ifdef CONFIG_SBUS
+static int iommu_map_dma_area(dma_addr_t *pba, unsigned long va,
+    unsigned long addr, int len)
+{
+	unsigned long page, end;
+	struct iommu_struct *iommu = sbus_root->iommu;
+	iopte_t *iopte = iommu->page_table;
+	iopte_t *first;
+	int ioptex;
+
+	if ((va & ~PAGE_MASK) != 0) BUG();
+	if ((addr & ~PAGE_MASK) != 0) BUG();
+	if ((len & ~PAGE_MASK) != 0) BUG();
+
+	/* page color = physical address */
+	ioptex = bit_map_string_get(&iommu->usemap, len >> PAGE_SHIFT,
+		addr >> PAGE_SHIFT);
+	if (ioptex < 0)
+		panic("iommu out");
+
+	iopte += ioptex;
+	first = iopte;
+	end = addr + len;
+	while(addr < end) {
+		page = va;
+		{
+			pgd_t *pgdp;
+			pmd_t *pmdp;
+			pte_t *ptep;
+
+			if (viking_mxcc_present)
+				viking_mxcc_flush_page(page);
+			else if (viking_flush)
+				viking_flush_page(page);
+			else
+				__flush_page_to_ram(page);
+
+			pgdp = pgd_offset(&init_mm, addr);
+			pmdp = pmd_offset(pgdp, addr);
+			ptep = pte_offset_map(pmdp, addr);
+
+			set_pte(ptep, mk_pte(virt_to_page(page), dvma_prot));
+		}
+		iopte_val(*iopte++) =
+		    MKIOPTE(page_to_pfn(virt_to_page(page)), ioperm_noc);
+		addr += PAGE_SIZE;
+		va += PAGE_SIZE;
+	}
+	/* P3: why do we need this?
+	 *
+	 * DAVEM: Because there are several aspects, none of which
+	 *        are handled by a single interface.  Some cpus are
+	 *        completely not I/O DMA coherent, and some have
+	 *        virtually indexed caches.  The driver DMA flushing
+	 *        methods handle the former case, but here during
+	 *        IOMMU page table modifications, and usage of non-cacheable
+	 *        cpu mappings of pages potentially in the cpu caches, we have
+	 *        to handle the latter case as well.
+	 */
+	flush_cache_all();
+	iommu_flush_iotlb(first, len >> PAGE_SHIFT);
+	flush_tlb_all();
+	iommu_invalidate(iommu->regs);
+
+	*pba = iommu->start + (ioptex << PAGE_SHIFT);
+	return 0;
+}
+
+static void iommu_unmap_dma_area(unsigned long busa, int len)
+{
+	struct iommu_struct *iommu = sbus_root->iommu;
+	iopte_t *iopte = iommu->page_table;
+	unsigned long end;
+	int ioptex = (busa - iommu->start) >> PAGE_SHIFT;
+
+	if ((busa & ~PAGE_MASK) != 0) BUG();
+	if ((len & ~PAGE_MASK) != 0) BUG();
+
+	iopte += ioptex;
+	end = busa + len;
+	while (busa < end) {
+		iopte_val(*iopte++) = 0;
+		busa += PAGE_SIZE;
+	}
+	flush_tlb_all();
+	iommu_invalidate(iommu->regs);
+	bit_map_clear(&iommu->usemap, ioptex, len >> PAGE_SHIFT);
+}
+
+static struct page *iommu_translate_dvma(unsigned long busa)
+{
+	struct iommu_struct *iommu = sbus_root->iommu;
+	iopte_t *iopte = iommu->page_table;
+
+	iopte += ((busa - iommu->start) >> PAGE_SHIFT);
+	return pfn_to_page((iopte_val(*iopte) & IOPTE_PAGE) >> (PAGE_SHIFT-4));
+}
+#endif
+
+static char *iommu_lockarea(char *vaddr, unsigned long len)
+{
+	return vaddr;
+}
+
+static void iommu_unlockarea(char *vaddr, unsigned long len)
+{
+}
+
+void __init ld_mmu_iommu(void)
+{
+	viking_flush = (BTFIXUPVAL_CALL(flush_page_for_dma) == (unsigned long)viking_flush_page);
+	BTFIXUPSET_CALL(mmu_lockarea, iommu_lockarea, BTFIXUPCALL_RETO0);
+	BTFIXUPSET_CALL(mmu_unlockarea, iommu_unlockarea, BTFIXUPCALL_NOP);
+
+	if (!BTFIXUPVAL_CALL(flush_page_for_dma)) {
+		/* IO coherent chip */
+		BTFIXUPSET_CALL(mmu_get_scsi_one, iommu_get_scsi_one_noflush, BTFIXUPCALL_RETO0);
+		BTFIXUPSET_CALL(mmu_get_scsi_sgl, iommu_get_scsi_sgl_noflush, BTFIXUPCALL_NORM);
+	} else if (flush_page_for_dma_global) {
+		/* flush_page_for_dma flushes everything, no matter of what page is it */
+		BTFIXUPSET_CALL(mmu_get_scsi_one, iommu_get_scsi_one_gflush, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(mmu_get_scsi_sgl, iommu_get_scsi_sgl_gflush, BTFIXUPCALL_NORM);
+	} else {
+		BTFIXUPSET_CALL(mmu_get_scsi_one, iommu_get_scsi_one_pflush, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(mmu_get_scsi_sgl, iommu_get_scsi_sgl_pflush, BTFIXUPCALL_NORM);
+	}
+	BTFIXUPSET_CALL(mmu_release_scsi_one, iommu_release_scsi_one, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_release_scsi_sgl, iommu_release_scsi_sgl, BTFIXUPCALL_NORM);
+
+#ifdef CONFIG_SBUS
+	BTFIXUPSET_CALL(mmu_map_dma_area, iommu_map_dma_area, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_unmap_dma_area, iommu_unmap_dma_area, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_translate_dvma, iommu_translate_dvma, BTFIXUPCALL_NORM);
+#endif
+
+	if (viking_mxcc_present || srmmu_modtype == HyperSparc) {
+		dvma_prot = __pgprot(SRMMU_CACHE | SRMMU_ET_PTE | SRMMU_PRIV);
+		ioperm_noc = IOPTE_CACHE | IOPTE_WRITE | IOPTE_VALID;
+	} else {
+		dvma_prot = __pgprot(SRMMU_ET_PTE | SRMMU_PRIV);
+		ioperm_noc = IOPTE_WRITE | IOPTE_VALID;
+	}
+}
diff --git a/arch/sparc/mm/loadmmu.c b/arch/sparc/mm/loadmmu.c
new file mode 100644
index 0000000..e9f9571
--- /dev/null
+++ b/arch/sparc/mm/loadmmu.c
@@ -0,0 +1,46 @@
+/* $Id: loadmmu.c,v 1.56 2000/02/08 20:24:21 davem Exp $
+ * loadmmu.c:  This code loads up all the mm function pointers once the
+ *             machine type has been determined.  It also sets the static
+ *             mmu values such as PAGE_NONE, etc.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+
+#include <asm/system.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/a.out.h>
+#include <asm/mmu_context.h>
+#include <asm/oplib.h>
+
+struct ctx_list *ctx_list_pool;
+struct ctx_list ctx_free;
+struct ctx_list ctx_used;
+
+unsigned int pg_iobits;
+
+extern void ld_mmu_sun4c(void);
+extern void ld_mmu_srmmu(void);
+
+void __init load_mmu(void)
+{
+	switch(sparc_cpu_model) {
+	case sun4c:
+	case sun4:
+		ld_mmu_sun4c();
+		break;
+	case sun4m:
+	case sun4d:
+		ld_mmu_srmmu();
+		break;
+	default:
+		prom_printf("load_mmu: %d unsupported\n", (int)sparc_cpu_model);
+		prom_halt();
+	}
+	btfixup();
+}
diff --git a/arch/sparc/mm/nosrmmu.c b/arch/sparc/mm/nosrmmu.c
new file mode 100644
index 0000000..9e21565
--- /dev/null
+++ b/arch/sparc/mm/nosrmmu.c
@@ -0,0 +1,59 @@
+/* $Id: nosrmmu.c,v 1.5 1999/11/19 04:11:54 davem Exp $
+ * nosrmmu.c: This file is a bunch of dummies for sun4 compiles, 
+ *         so that it does not need srmmu and avoid ifdefs.
+ *
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <asm/mbus.h>
+#include <asm/sbus.h>
+
+static char shouldnothappen[] __initdata = "SUN4 kernel can only run on SUN4\n";
+
+enum mbus_module srmmu_modtype;
+void *srmmu_nocache_pool;
+
+int vac_cache_size = 0;
+
+static void __init should_not_happen(void)
+{
+	prom_printf(shouldnothappen);
+	prom_halt();
+}
+
+void __init srmmu_frob_mem_map(unsigned long start_mem)
+{
+	should_not_happen();
+}
+
+unsigned long __init srmmu_paging_init(unsigned long start_mem, unsigned long end_mem)
+{
+	should_not_happen();
+	return 0;
+}
+
+void __init ld_mmu_srmmu(void)
+{
+	should_not_happen();
+}
+
+void srmmu_mapioaddr(unsigned long physaddr, unsigned long virt_addr, int bus_type, int rdonly)
+{
+}
+
+void srmmu_unmapioaddr(unsigned long virt_addr)
+{
+}
+
+__u32 iounit_map_dma_init(struct sbus_bus *sbus, int size)
+{
+	return 0;
+}
+
+__u32 iounit_map_dma_page(__u32 vaddr, void *addr, struct sbus_bus *sbus)
+{
+	return 0;
+}
diff --git a/arch/sparc/mm/nosun4c.c b/arch/sparc/mm/nosun4c.c
new file mode 100644
index 0000000..ea2e210
--- /dev/null
+++ b/arch/sparc/mm/nosun4c.c
@@ -0,0 +1,77 @@
+/* $Id: nosun4c.c,v 1.3 2000/02/14 04:52:36 jj Exp $
+ * nosun4c.c: This file is a bunch of dummies for SMP compiles, 
+ *         so that it does not need sun4c and avoid ifdefs.
+ *
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <asm/pgtable.h>
+
+static char shouldnothappen[] __initdata = "32bit SMP kernel only supports sun4m and sun4d\n";
+
+/* Dummies */
+struct sun4c_mmu_ring {
+	unsigned long xxx1[3];
+	unsigned char xxx2[2];
+	int xxx3;
+};
+struct sun4c_mmu_ring sun4c_kernel_ring;
+struct sun4c_mmu_ring sun4c_kfree_ring;
+unsigned long sun4c_kernel_faults;
+unsigned long *sun4c_memerr_reg;
+
+static void __init should_not_happen(void)
+{
+	prom_printf(shouldnothappen);
+	prom_halt();
+}
+
+unsigned long __init sun4c_paging_init(unsigned long start_mem, unsigned long end_mem)
+{
+	should_not_happen();
+	return 0;
+}
+
+void __init ld_mmu_sun4c(void)
+{
+	should_not_happen();
+}
+
+void sun4c_mapioaddr(unsigned long physaddr, unsigned long virt_addr, int bus_type, int rdonly)
+{
+}
+
+void sun4c_unmapioaddr(unsigned long virt_addr)
+{
+}
+
+void sun4c_complete_all_stores(void)
+{
+}
+
+pte_t *sun4c_pte_offset(pmd_t * dir, unsigned long address)
+{
+	return NULL;
+}
+
+pte_t *sun4c_pte_offset_kernel(pmd_t *dir, unsigned long address)
+{
+	return NULL;
+}
+
+void sun4c_update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte)
+{
+}
+
+void __init sun4c_probe_vac(void)
+{
+	should_not_happen();
+}
+
+void __init sun4c_probe_memerr_reg(void)
+{
+	should_not_happen();
+}
diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c
new file mode 100644
index 0000000..c89a803
--- /dev/null
+++ b/arch/sparc/mm/srmmu.c
@@ -0,0 +1,2274 @@
+/*
+ * srmmu.c:  SRMMU specific routines for memory management.
+ *
+ * Copyright (C) 1995 David S. Miller  (davem@caip.rutgers.edu)
+ * Copyright (C) 1995,2002 Pete Zaitcev (zaitcev@yahoo.com)
+ * Copyright (C) 1996 Eddie C. Dost    (ecd@skynet.be)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1999,2000 Anton Blanchard (anton@samba.org)
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/pagemap.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/bootmem.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+
+#include <asm/bitext.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/kdebug.h>
+#include <asm/vaddrs.h>
+#include <asm/traps.h>
+#include <asm/smp.h>
+#include <asm/mbus.h>
+#include <asm/cache.h>
+#include <asm/oplib.h>
+#include <asm/sbus.h>
+#include <asm/asi.h>
+#include <asm/msi.h>
+#include <asm/a.out.h>
+#include <asm/mmu_context.h>
+#include <asm/io-unit.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+
+/* Now the cpu specific definitions. */
+#include <asm/viking.h>
+#include <asm/mxcc.h>
+#include <asm/ross.h>
+#include <asm/tsunami.h>
+#include <asm/swift.h>
+#include <asm/turbosparc.h>
+
+#include <asm/btfixup.h>
+
+enum mbus_module srmmu_modtype;
+unsigned int hwbug_bitmask;
+int vac_cache_size;
+int vac_line_size;
+
+extern struct resource sparc_iomap;
+
+extern unsigned long last_valid_pfn;
+
+extern unsigned long page_kernel;
+
+pgd_t *srmmu_swapper_pg_dir;
+
+#ifdef CONFIG_SMP
+#define FLUSH_BEGIN(mm)
+#define FLUSH_END
+#else
+#define FLUSH_BEGIN(mm) if((mm)->context != NO_CONTEXT) {
+#define FLUSH_END	}
+#endif
+
+BTFIXUPDEF_CALL(void, flush_page_for_dma, unsigned long)
+#define flush_page_for_dma(page) BTFIXUP_CALL(flush_page_for_dma)(page)
+
+int flush_page_for_dma_global = 1;
+
+#ifdef CONFIG_SMP
+BTFIXUPDEF_CALL(void, local_flush_page_for_dma, unsigned long)
+#define local_flush_page_for_dma(page) BTFIXUP_CALL(local_flush_page_for_dma)(page)
+#endif
+
+char *srmmu_name;
+
+ctxd_t *srmmu_ctx_table_phys;
+ctxd_t *srmmu_context_table;
+
+int viking_mxcc_present;
+static DEFINE_SPINLOCK(srmmu_context_spinlock);
+
+int is_hypersparc;
+
+/*
+ * In general all page table modifications should use the V8 atomic
+ * swap instruction.  This insures the mmu and the cpu are in sync
+ * with respect to ref/mod bits in the page tables.
+ */
+static inline unsigned long srmmu_swap(unsigned long *addr, unsigned long value)
+{
+	__asm__ __volatile__("swap [%2], %0" : "=&r" (value) : "0" (value), "r" (addr));
+	return value;
+}
+
+static inline void srmmu_set_pte(pte_t *ptep, pte_t pteval)
+{
+	srmmu_swap((unsigned long *)ptep, pte_val(pteval));
+}
+
+/* The very generic SRMMU page table operations. */
+static inline int srmmu_device_memory(unsigned long x)
+{
+	return ((x & 0xF0000000) != 0);
+}
+
+int srmmu_cache_pagetables;
+
+/* these will be initialized in srmmu_nocache_calcsize() */
+unsigned long srmmu_nocache_size;
+unsigned long srmmu_nocache_end;
+
+/* 1 bit <=> 256 bytes of nocache <=> 64 PTEs */
+#define SRMMU_NOCACHE_BITMAP_SHIFT (PAGE_SHIFT - 4)
+
+/* The context table is a nocache user with the biggest alignment needs. */
+#define SRMMU_NOCACHE_ALIGN_MAX (sizeof(ctxd_t)*SRMMU_MAX_CONTEXTS)
+
+void *srmmu_nocache_pool;
+void *srmmu_nocache_bitmap;
+static struct bit_map srmmu_nocache_map;
+
+static unsigned long srmmu_pte_pfn(pte_t pte)
+{
+	if (srmmu_device_memory(pte_val(pte))) {
+		/* Just return something that will cause
+		 * pfn_valid() to return false.  This makes
+		 * copy_one_pte() to just directly copy to
+		 * PTE over.
+		 */
+		return ~0UL;
+	}
+	return (pte_val(pte) & SRMMU_PTE_PMASK) >> (PAGE_SHIFT-4);
+}
+
+static struct page *srmmu_pmd_page(pmd_t pmd)
+{
+
+	if (srmmu_device_memory(pmd_val(pmd)))
+		BUG();
+	return pfn_to_page((pmd_val(pmd) & SRMMU_PTD_PMASK) >> (PAGE_SHIFT-4));
+}
+
+static inline unsigned long srmmu_pgd_page(pgd_t pgd)
+{ return srmmu_device_memory(pgd_val(pgd))?~0:(unsigned long)__nocache_va((pgd_val(pgd) & SRMMU_PTD_PMASK) << 4); }
+
+
+static inline int srmmu_pte_none(pte_t pte)
+{ return !(pte_val(pte) & 0xFFFFFFF); }
+
+static inline int srmmu_pte_present(pte_t pte)
+{ return ((pte_val(pte) & SRMMU_ET_MASK) == SRMMU_ET_PTE); }
+
+static inline int srmmu_pte_read(pte_t pte)
+{ return !(pte_val(pte) & SRMMU_NOREAD); }
+
+static inline void srmmu_pte_clear(pte_t *ptep)
+{ srmmu_set_pte(ptep, __pte(0)); }
+
+static inline int srmmu_pmd_none(pmd_t pmd)
+{ return !(pmd_val(pmd) & 0xFFFFFFF); }
+
+static inline int srmmu_pmd_bad(pmd_t pmd)
+{ return (pmd_val(pmd) & SRMMU_ET_MASK) != SRMMU_ET_PTD; }
+
+static inline int srmmu_pmd_present(pmd_t pmd)
+{ return ((pmd_val(pmd) & SRMMU_ET_MASK) == SRMMU_ET_PTD); }
+
+static inline void srmmu_pmd_clear(pmd_t *pmdp) {
+	int i;
+	for (i = 0; i < PTRS_PER_PTE/SRMMU_REAL_PTRS_PER_PTE; i++)
+		srmmu_set_pte((pte_t *)&pmdp->pmdv[i], __pte(0));
+}
+
+static inline int srmmu_pgd_none(pgd_t pgd)          
+{ return !(pgd_val(pgd) & 0xFFFFFFF); }
+
+static inline int srmmu_pgd_bad(pgd_t pgd)
+{ return (pgd_val(pgd) & SRMMU_ET_MASK) != SRMMU_ET_PTD; }
+
+static inline int srmmu_pgd_present(pgd_t pgd)
+{ return ((pgd_val(pgd) & SRMMU_ET_MASK) == SRMMU_ET_PTD); }
+
+static inline void srmmu_pgd_clear(pgd_t * pgdp)
+{ srmmu_set_pte((pte_t *)pgdp, __pte(0)); }
+
+static inline pte_t srmmu_pte_wrprotect(pte_t pte)
+{ return __pte(pte_val(pte) & ~SRMMU_WRITE);}
+
+static inline pte_t srmmu_pte_mkclean(pte_t pte)
+{ return __pte(pte_val(pte) & ~SRMMU_DIRTY);}
+
+static inline pte_t srmmu_pte_mkold(pte_t pte)
+{ return __pte(pte_val(pte) & ~SRMMU_REF);}
+
+static inline pte_t srmmu_pte_mkwrite(pte_t pte)
+{ return __pte(pte_val(pte) | SRMMU_WRITE);}
+
+static inline pte_t srmmu_pte_mkdirty(pte_t pte)
+{ return __pte(pte_val(pte) | SRMMU_DIRTY);}
+
+static inline pte_t srmmu_pte_mkyoung(pte_t pte)
+{ return __pte(pte_val(pte) | SRMMU_REF);}
+
+/*
+ * Conversion functions: convert a page and protection to a page entry,
+ * and a page entry and page directory to the page they refer to.
+ */
+static pte_t srmmu_mk_pte(struct page *page, pgprot_t pgprot)
+{ return __pte((page_to_pfn(page) << (PAGE_SHIFT-4)) | pgprot_val(pgprot)); }
+
+static pte_t srmmu_mk_pte_phys(unsigned long page, pgprot_t pgprot)
+{ return __pte(((page) >> 4) | pgprot_val(pgprot)); }
+
+static pte_t srmmu_mk_pte_io(unsigned long page, pgprot_t pgprot, int space)
+{ return __pte(((page) >> 4) | (space << 28) | pgprot_val(pgprot)); }
+
+/* XXX should we hyper_flush_whole_icache here - Anton */
+static inline void srmmu_ctxd_set(ctxd_t *ctxp, pgd_t *pgdp)
+{ srmmu_set_pte((pte_t *)ctxp, (SRMMU_ET_PTD | (__nocache_pa((unsigned long) pgdp) >> 4))); }
+
+static inline void srmmu_pgd_set(pgd_t * pgdp, pmd_t * pmdp)
+{ srmmu_set_pte((pte_t *)pgdp, (SRMMU_ET_PTD | (__nocache_pa((unsigned long) pmdp) >> 4))); }
+
+static void srmmu_pmd_set(pmd_t *pmdp, pte_t *ptep)
+{
+	unsigned long ptp;	/* Physical address, shifted right by 4 */
+	int i;
+
+	ptp = __nocache_pa((unsigned long) ptep) >> 4;
+	for (i = 0; i < PTRS_PER_PTE/SRMMU_REAL_PTRS_PER_PTE; i++) {
+		srmmu_set_pte((pte_t *)&pmdp->pmdv[i], SRMMU_ET_PTD | ptp);
+		ptp += (SRMMU_REAL_PTRS_PER_PTE*sizeof(pte_t) >> 4);
+	}
+}
+
+static void srmmu_pmd_populate(pmd_t *pmdp, struct page *ptep)
+{
+	unsigned long ptp;	/* Physical address, shifted right by 4 */
+	int i;
+
+	ptp = page_to_pfn(ptep) << (PAGE_SHIFT-4);	/* watch for overflow */
+	for (i = 0; i < PTRS_PER_PTE/SRMMU_REAL_PTRS_PER_PTE; i++) {
+		srmmu_set_pte((pte_t *)&pmdp->pmdv[i], SRMMU_ET_PTD | ptp);
+		ptp += (SRMMU_REAL_PTRS_PER_PTE*sizeof(pte_t) >> 4);
+	}
+}
+
+static inline pte_t srmmu_pte_modify(pte_t pte, pgprot_t newprot)
+{ return __pte((pte_val(pte) & SRMMU_CHG_MASK) | pgprot_val(newprot)); }
+
+/* to find an entry in a top-level page table... */
+extern inline pgd_t *srmmu_pgd_offset(struct mm_struct * mm, unsigned long address)
+{ return mm->pgd + (address >> SRMMU_PGDIR_SHIFT); }
+
+/* Find an entry in the second-level page table.. */
+static inline pmd_t *srmmu_pmd_offset(pgd_t * dir, unsigned long address)
+{
+	return (pmd_t *) srmmu_pgd_page(*dir) +
+	    ((address >> PMD_SHIFT) & (PTRS_PER_PMD - 1));
+}
+
+/* Find an entry in the third-level page table.. */ 
+static inline pte_t *srmmu_pte_offset(pmd_t * dir, unsigned long address)
+{
+	void *pte;
+
+	pte = __nocache_va((dir->pmdv[0] & SRMMU_PTD_PMASK) << 4);
+	return (pte_t *) pte +
+	    ((address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1));
+}
+
+static unsigned long srmmu_swp_type(swp_entry_t entry)
+{
+	return (entry.val >> SRMMU_SWP_TYPE_SHIFT) & SRMMU_SWP_TYPE_MASK;
+}
+
+static unsigned long srmmu_swp_offset(swp_entry_t entry)
+{
+	return (entry.val >> SRMMU_SWP_OFF_SHIFT) & SRMMU_SWP_OFF_MASK;
+}
+
+static swp_entry_t srmmu_swp_entry(unsigned long type, unsigned long offset)
+{
+	return (swp_entry_t) {
+		  (type & SRMMU_SWP_TYPE_MASK) << SRMMU_SWP_TYPE_SHIFT
+		| (offset & SRMMU_SWP_OFF_MASK) << SRMMU_SWP_OFF_SHIFT };
+}
+
+/*
+ * size: bytes to allocate in the nocache area.
+ * align: bytes, number to align at.
+ * Returns the virtual address of the allocated area.
+ */
+static unsigned long __srmmu_get_nocache(int size, int align)
+{
+	int offset;
+
+	if (size < SRMMU_NOCACHE_BITMAP_SHIFT) {
+		printk("Size 0x%x too small for nocache request\n", size);
+		size = SRMMU_NOCACHE_BITMAP_SHIFT;
+	}
+	if (size & (SRMMU_NOCACHE_BITMAP_SHIFT-1)) {
+		printk("Size 0x%x unaligned int nocache request\n", size);
+		size += SRMMU_NOCACHE_BITMAP_SHIFT-1;
+	}
+	BUG_ON(align > SRMMU_NOCACHE_ALIGN_MAX);
+
+	offset = bit_map_string_get(&srmmu_nocache_map,
+		       			size >> SRMMU_NOCACHE_BITMAP_SHIFT,
+					align >> SRMMU_NOCACHE_BITMAP_SHIFT);
+	if (offset == -1) {
+		printk("srmmu: out of nocache %d: %d/%d\n",
+		    size, (int) srmmu_nocache_size,
+		    srmmu_nocache_map.used << SRMMU_NOCACHE_BITMAP_SHIFT);
+		return 0;
+	}
+
+	return (SRMMU_NOCACHE_VADDR + (offset << SRMMU_NOCACHE_BITMAP_SHIFT));
+}
+
+unsigned inline long srmmu_get_nocache(int size, int align)
+{
+	unsigned long tmp;
+
+	tmp = __srmmu_get_nocache(size, align);
+
+	if (tmp)
+		memset((void *)tmp, 0, size);
+
+	return tmp;
+}
+
+void srmmu_free_nocache(unsigned long vaddr, int size)
+{
+	int offset;
+
+	if (vaddr < SRMMU_NOCACHE_VADDR) {
+		printk("Vaddr %lx is smaller than nocache base 0x%lx\n",
+		    vaddr, (unsigned long)SRMMU_NOCACHE_VADDR);
+		BUG();
+	}
+	if (vaddr+size > srmmu_nocache_end) {
+		printk("Vaddr %lx is bigger than nocache end 0x%lx\n",
+		    vaddr, srmmu_nocache_end);
+		BUG();
+	}
+	if (size & (size-1)) {
+		printk("Size 0x%x is not a power of 2\n", size);
+		BUG();
+	}
+	if (size < SRMMU_NOCACHE_BITMAP_SHIFT) {
+		printk("Size 0x%x is too small\n", size);
+		BUG();
+	}
+	if (vaddr & (size-1)) {
+		printk("Vaddr %lx is not aligned to size 0x%x\n", vaddr, size);
+		BUG();
+	}
+
+	offset = (vaddr - SRMMU_NOCACHE_VADDR) >> SRMMU_NOCACHE_BITMAP_SHIFT;
+	size = size >> SRMMU_NOCACHE_BITMAP_SHIFT;
+
+	bit_map_clear(&srmmu_nocache_map, offset, size);
+}
+
+void srmmu_early_allocate_ptable_skeleton(unsigned long start, unsigned long end);
+
+extern unsigned long probe_memory(void);	/* in fault.c */
+
+/*
+ * Reserve nocache dynamically proportionally to the amount of
+ * system RAM. -- Tomas Szepe <szepe@pinerecords.com>, June 2002
+ */
+void srmmu_nocache_calcsize(void)
+{
+	unsigned long sysmemavail = probe_memory() / 1024;
+	int srmmu_nocache_npages;
+
+	srmmu_nocache_npages =
+		sysmemavail / SRMMU_NOCACHE_ALCRATIO / 1024 * 256;
+
+ /* P3 XXX The 4x overuse: corroborated by /proc/meminfo. */
+	// if (srmmu_nocache_npages < 256) srmmu_nocache_npages = 256;
+	if (srmmu_nocache_npages < SRMMU_MIN_NOCACHE_PAGES)
+		srmmu_nocache_npages = SRMMU_MIN_NOCACHE_PAGES;
+
+	/* anything above 1280 blows up */
+	if (srmmu_nocache_npages > SRMMU_MAX_NOCACHE_PAGES)
+		srmmu_nocache_npages = SRMMU_MAX_NOCACHE_PAGES;
+
+	srmmu_nocache_size = srmmu_nocache_npages * PAGE_SIZE;
+	srmmu_nocache_end = SRMMU_NOCACHE_VADDR + srmmu_nocache_size;
+}
+
+void srmmu_nocache_init(void)
+{
+	unsigned int bitmap_bits;
+	pgd_t *pgd;
+	pmd_t *pmd;
+	pte_t *pte;
+	unsigned long paddr, vaddr;
+	unsigned long pteval;
+
+	bitmap_bits = srmmu_nocache_size >> SRMMU_NOCACHE_BITMAP_SHIFT;
+
+	srmmu_nocache_pool = __alloc_bootmem(srmmu_nocache_size,
+		SRMMU_NOCACHE_ALIGN_MAX, 0UL);
+	memset(srmmu_nocache_pool, 0, srmmu_nocache_size);
+
+	srmmu_nocache_bitmap = __alloc_bootmem(bitmap_bits >> 3, SMP_CACHE_BYTES, 0UL);
+	bit_map_init(&srmmu_nocache_map, srmmu_nocache_bitmap, bitmap_bits);
+
+	srmmu_swapper_pg_dir = (pgd_t *)__srmmu_get_nocache(SRMMU_PGD_TABLE_SIZE, SRMMU_PGD_TABLE_SIZE);
+	memset(__nocache_fix(srmmu_swapper_pg_dir), 0, SRMMU_PGD_TABLE_SIZE);
+	init_mm.pgd = srmmu_swapper_pg_dir;
+
+	srmmu_early_allocate_ptable_skeleton(SRMMU_NOCACHE_VADDR, srmmu_nocache_end);
+
+	paddr = __pa((unsigned long)srmmu_nocache_pool);
+	vaddr = SRMMU_NOCACHE_VADDR;
+
+	while (vaddr < srmmu_nocache_end) {
+		pgd = pgd_offset_k(vaddr);
+		pmd = srmmu_pmd_offset(__nocache_fix(pgd), vaddr);
+		pte = srmmu_pte_offset(__nocache_fix(pmd), vaddr);
+
+		pteval = ((paddr >> 4) | SRMMU_ET_PTE | SRMMU_PRIV);
+
+		if (srmmu_cache_pagetables)
+			pteval |= SRMMU_CACHE;
+
+		srmmu_set_pte(__nocache_fix(pte), __pte(pteval));
+
+		vaddr += PAGE_SIZE;
+		paddr += PAGE_SIZE;
+	}
+
+	flush_cache_all();
+	flush_tlb_all();
+}
+
+static inline pgd_t *srmmu_get_pgd_fast(void)
+{
+	pgd_t *pgd = NULL;
+
+	pgd = (pgd_t *)__srmmu_get_nocache(SRMMU_PGD_TABLE_SIZE, SRMMU_PGD_TABLE_SIZE);
+	if (pgd) {
+		pgd_t *init = pgd_offset_k(0);
+		memset(pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
+		memcpy(pgd + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
+						(PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
+	}
+
+	return pgd;
+}
+
+static void srmmu_free_pgd_fast(pgd_t *pgd)
+{
+	srmmu_free_nocache((unsigned long)pgd, SRMMU_PGD_TABLE_SIZE);
+}
+
+static pmd_t *srmmu_pmd_alloc_one(struct mm_struct *mm, unsigned long address)
+{
+	return (pmd_t *)srmmu_get_nocache(SRMMU_PMD_TABLE_SIZE, SRMMU_PMD_TABLE_SIZE);
+}
+
+static void srmmu_pmd_free(pmd_t * pmd)
+{
+	srmmu_free_nocache((unsigned long)pmd, SRMMU_PMD_TABLE_SIZE);
+}
+
+/*
+ * Hardware needs alignment to 256 only, but we align to whole page size
+ * to reduce fragmentation problems due to the buddy principle.
+ * XXX Provide actual fragmentation statistics in /proc.
+ *
+ * Alignments up to the page size are the same for physical and virtual
+ * addresses of the nocache area.
+ */
+static pte_t *
+srmmu_pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address)
+{
+	return (pte_t *)srmmu_get_nocache(PTE_SIZE, PTE_SIZE);
+}
+
+static struct page *
+srmmu_pte_alloc_one(struct mm_struct *mm, unsigned long address)
+{
+	unsigned long pte;
+
+	if ((pte = (unsigned long)srmmu_pte_alloc_one_kernel(mm, address)) == 0)
+		return NULL;
+	return pfn_to_page( __nocache_pa(pte) >> PAGE_SHIFT );
+}
+
+static void srmmu_free_pte_fast(pte_t *pte)
+{
+	srmmu_free_nocache((unsigned long)pte, PTE_SIZE);
+}
+
+static void srmmu_pte_free(struct page *pte)
+{
+	unsigned long p;
+
+	p = (unsigned long)page_address(pte);	/* Cached address (for test) */
+	if (p == 0)
+		BUG();
+	p = page_to_pfn(pte) << PAGE_SHIFT;	/* Physical address */
+	p = (unsigned long) __nocache_va(p);	/* Nocached virtual */
+	srmmu_free_nocache(p, PTE_SIZE);
+}
+
+/*
+ */
+static inline void alloc_context(struct mm_struct *old_mm, struct mm_struct *mm)
+{
+	struct ctx_list *ctxp;
+
+	ctxp = ctx_free.next;
+	if(ctxp != &ctx_free) {
+		remove_from_ctx_list(ctxp);
+		add_to_used_ctxlist(ctxp);
+		mm->context = ctxp->ctx_number;
+		ctxp->ctx_mm = mm;
+		return;
+	}
+	ctxp = ctx_used.next;
+	if(ctxp->ctx_mm == old_mm)
+		ctxp = ctxp->next;
+	if(ctxp == &ctx_used)
+		panic("out of mmu contexts");
+	flush_cache_mm(ctxp->ctx_mm);
+	flush_tlb_mm(ctxp->ctx_mm);
+	remove_from_ctx_list(ctxp);
+	add_to_used_ctxlist(ctxp);
+	ctxp->ctx_mm->context = NO_CONTEXT;
+	ctxp->ctx_mm = mm;
+	mm->context = ctxp->ctx_number;
+}
+
+static inline void free_context(int context)
+{
+	struct ctx_list *ctx_old;
+
+	ctx_old = ctx_list_pool + context;
+	remove_from_ctx_list(ctx_old);
+	add_to_free_ctxlist(ctx_old);
+}
+
+
+static void srmmu_switch_mm(struct mm_struct *old_mm, struct mm_struct *mm,
+    struct task_struct *tsk, int cpu)
+{
+	if(mm->context == NO_CONTEXT) {
+		spin_lock(&srmmu_context_spinlock);
+		alloc_context(old_mm, mm);
+		spin_unlock(&srmmu_context_spinlock);
+		srmmu_ctxd_set(&srmmu_context_table[mm->context], mm->pgd);
+	}
+
+	if (is_hypersparc)
+		hyper_flush_whole_icache();
+
+	srmmu_set_context(mm->context);
+}
+
+/* Low level IO area allocation on the SRMMU. */
+static inline void srmmu_mapioaddr(unsigned long physaddr,
+    unsigned long virt_addr, int bus_type)
+{
+	pgd_t *pgdp;
+	pmd_t *pmdp;
+	pte_t *ptep;
+	unsigned long tmp;
+
+	physaddr &= PAGE_MASK;
+	pgdp = pgd_offset_k(virt_addr);
+	pmdp = srmmu_pmd_offset(pgdp, virt_addr);
+	ptep = srmmu_pte_offset(pmdp, virt_addr);
+	tmp = (physaddr >> 4) | SRMMU_ET_PTE;
+
+	/*
+	 * I need to test whether this is consistent over all
+	 * sun4m's.  The bus_type represents the upper 4 bits of
+	 * 36-bit physical address on the I/O space lines...
+	 */
+	tmp |= (bus_type << 28);
+	tmp |= SRMMU_PRIV;
+	__flush_page_to_ram(virt_addr);
+	srmmu_set_pte(ptep, __pte(tmp));
+}
+
+static void srmmu_mapiorange(unsigned int bus, unsigned long xpa,
+    unsigned long xva, unsigned int len)
+{
+	while (len != 0) {
+		len -= PAGE_SIZE;
+		srmmu_mapioaddr(xpa, xva, bus);
+		xva += PAGE_SIZE;
+		xpa += PAGE_SIZE;
+	}
+	flush_tlb_all();
+}
+
+static inline void srmmu_unmapioaddr(unsigned long virt_addr)
+{
+	pgd_t *pgdp;
+	pmd_t *pmdp;
+	pte_t *ptep;
+
+	pgdp = pgd_offset_k(virt_addr);
+	pmdp = srmmu_pmd_offset(pgdp, virt_addr);
+	ptep = srmmu_pte_offset(pmdp, virt_addr);
+
+	/* No need to flush uncacheable page. */
+	srmmu_pte_clear(ptep);
+}
+
+static void srmmu_unmapiorange(unsigned long virt_addr, unsigned int len)
+{
+	while (len != 0) {
+		len -= PAGE_SIZE;
+		srmmu_unmapioaddr(virt_addr);
+		virt_addr += PAGE_SIZE;
+	}
+	flush_tlb_all();
+}
+
+/*
+ * On the SRMMU we do not have the problems with limited tlb entries
+ * for mapping kernel pages, so we just take things from the free page
+ * pool.  As a side effect we are putting a little too much pressure
+ * on the gfp() subsystem.  This setup also makes the logic of the
+ * iommu mapping code a lot easier as we can transparently handle
+ * mappings on the kernel stack without any special code as we did
+ * need on the sun4c.
+ */
+struct thread_info *srmmu_alloc_thread_info(void)
+{
+	struct thread_info *ret;
+
+	ret = (struct thread_info *)__get_free_pages(GFP_KERNEL,
+						     THREAD_INFO_ORDER);
+#ifdef CONFIG_DEBUG_STACK_USAGE
+	if (ret)
+		memset(ret, 0, PAGE_SIZE << THREAD_INFO_ORDER);
+#endif /* DEBUG_STACK_USAGE */
+
+	return ret;
+}
+
+static void srmmu_free_thread_info(struct thread_info *ti)
+{
+	free_pages((unsigned long)ti, THREAD_INFO_ORDER);
+}
+
+/* tsunami.S */
+extern void tsunami_flush_cache_all(void);
+extern void tsunami_flush_cache_mm(struct mm_struct *mm);
+extern void tsunami_flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end);
+extern void tsunami_flush_cache_page(struct vm_area_struct *vma, unsigned long page);
+extern void tsunami_flush_page_to_ram(unsigned long page);
+extern void tsunami_flush_page_for_dma(unsigned long page);
+extern void tsunami_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr);
+extern void tsunami_flush_tlb_all(void);
+extern void tsunami_flush_tlb_mm(struct mm_struct *mm);
+extern void tsunami_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end);
+extern void tsunami_flush_tlb_page(struct vm_area_struct *vma, unsigned long page);
+extern void tsunami_setup_blockops(void);
+
+/*
+ * Workaround, until we find what's going on with Swift. When low on memory,
+ * it sometimes loops in fault/handle_mm_fault incl. flush_tlb_page to find
+ * out it is already in page tables/ fault again on the same instruction.
+ * I really don't understand it, have checked it and contexts
+ * are right, flush_tlb_all is done as well, and it faults again...
+ * Strange. -jj
+ *
+ * The following code is a deadwood that may be necessary when
+ * we start to make precise page flushes again. --zaitcev
+ */
+static void swift_update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte)
+{
+#if 0
+	static unsigned long last;
+	unsigned int val;
+	/* unsigned int n; */
+
+	if (address == last) {
+		val = srmmu_hwprobe(address);
+		if (val != 0 && pte_val(pte) != val) {
+			printk("swift_update_mmu_cache: "
+			    "addr %lx put %08x probed %08x from %p\n",
+			    address, pte_val(pte), val,
+			    __builtin_return_address(0));
+			srmmu_flush_whole_tlb();
+		}
+	}
+	last = address;
+#endif
+}
+
+/* swift.S */
+extern void swift_flush_cache_all(void);
+extern void swift_flush_cache_mm(struct mm_struct *mm);
+extern void swift_flush_cache_range(struct vm_area_struct *vma,
+				    unsigned long start, unsigned long end);
+extern void swift_flush_cache_page(struct vm_area_struct *vma, unsigned long page);
+extern void swift_flush_page_to_ram(unsigned long page);
+extern void swift_flush_page_for_dma(unsigned long page);
+extern void swift_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr);
+extern void swift_flush_tlb_all(void);
+extern void swift_flush_tlb_mm(struct mm_struct *mm);
+extern void swift_flush_tlb_range(struct vm_area_struct *vma,
+				  unsigned long start, unsigned long end);
+extern void swift_flush_tlb_page(struct vm_area_struct *vma, unsigned long page);
+
+#if 0  /* P3: deadwood to debug precise flushes on Swift. */
+void swift_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+{
+	int cctx, ctx1;
+
+	page &= PAGE_MASK;
+	if ((ctx1 = vma->vm_mm->context) != -1) {
+		cctx = srmmu_get_context();
+/* Is context # ever different from current context? P3 */
+		if (cctx != ctx1) {
+			printk("flush ctx %02x curr %02x\n", ctx1, cctx);
+			srmmu_set_context(ctx1);
+			swift_flush_page(page);
+			__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
+					"r" (page), "i" (ASI_M_FLUSH_PROBE));
+			srmmu_set_context(cctx);
+		} else {
+			 /* Rm. prot. bits from virt. c. */
+			/* swift_flush_cache_all(); */
+			/* swift_flush_cache_page(vma, page); */
+			swift_flush_page(page);
+
+			__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
+				"r" (page), "i" (ASI_M_FLUSH_PROBE));
+			/* same as above: srmmu_flush_tlb_page() */
+		}
+	}
+}
+#endif
+
+/*
+ * The following are all MBUS based SRMMU modules, and therefore could
+ * be found in a multiprocessor configuration.  On the whole, these
+ * chips seems to be much more touchy about DVMA and page tables
+ * with respect to cache coherency.
+ */
+
+/* Cypress flushes. */
+static void cypress_flush_cache_all(void)
+{
+	volatile unsigned long cypress_sucks;
+	unsigned long faddr, tagval;
+
+	flush_user_windows();
+	for(faddr = 0; faddr < 0x10000; faddr += 0x20) {
+		__asm__ __volatile__("lda [%1 + %2] %3, %0\n\t" :
+				     "=r" (tagval) :
+				     "r" (faddr), "r" (0x40000),
+				     "i" (ASI_M_DATAC_TAG));
+
+		/* If modified and valid, kick it. */
+		if((tagval & 0x60) == 0x60)
+			cypress_sucks = *(unsigned long *)(0xf0020000 + faddr);
+	}
+}
+
+static void cypress_flush_cache_mm(struct mm_struct *mm)
+{
+	register unsigned long a, b, c, d, e, f, g;
+	unsigned long flags, faddr;
+	int octx;
+
+	FLUSH_BEGIN(mm)
+	flush_user_windows();
+	local_irq_save(flags);
+	octx = srmmu_get_context();
+	srmmu_set_context(mm->context);
+	a = 0x20; b = 0x40; c = 0x60;
+	d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0;
+
+	faddr = (0x10000 - 0x100);
+	goto inside;
+	do {
+		faddr -= 0x100;
+	inside:
+		__asm__ __volatile__("sta %%g0, [%0] %1\n\t"
+				     "sta %%g0, [%0 + %2] %1\n\t"
+				     "sta %%g0, [%0 + %3] %1\n\t"
+				     "sta %%g0, [%0 + %4] %1\n\t"
+				     "sta %%g0, [%0 + %5] %1\n\t"
+				     "sta %%g0, [%0 + %6] %1\n\t"
+				     "sta %%g0, [%0 + %7] %1\n\t"
+				     "sta %%g0, [%0 + %8] %1\n\t" : :
+				     "r" (faddr), "i" (ASI_M_FLUSH_CTX),
+				     "r" (a), "r" (b), "r" (c), "r" (d),
+				     "r" (e), "r" (f), "r" (g));
+	} while(faddr);
+	srmmu_set_context(octx);
+	local_irq_restore(flags);
+	FLUSH_END
+}
+
+static void cypress_flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
+{
+	struct mm_struct *mm = vma->vm_mm;
+	register unsigned long a, b, c, d, e, f, g;
+	unsigned long flags, faddr;
+	int octx;
+
+	FLUSH_BEGIN(mm)
+	flush_user_windows();
+	local_irq_save(flags);
+	octx = srmmu_get_context();
+	srmmu_set_context(mm->context);
+	a = 0x20; b = 0x40; c = 0x60;
+	d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0;
+
+	start &= SRMMU_REAL_PMD_MASK;
+	while(start < end) {
+		faddr = (start + (0x10000 - 0x100));
+		goto inside;
+		do {
+			faddr -= 0x100;
+		inside:
+			__asm__ __volatile__("sta %%g0, [%0] %1\n\t"
+					     "sta %%g0, [%0 + %2] %1\n\t"
+					     "sta %%g0, [%0 + %3] %1\n\t"
+					     "sta %%g0, [%0 + %4] %1\n\t"
+					     "sta %%g0, [%0 + %5] %1\n\t"
+					     "sta %%g0, [%0 + %6] %1\n\t"
+					     "sta %%g0, [%0 + %7] %1\n\t"
+					     "sta %%g0, [%0 + %8] %1\n\t" : :
+					     "r" (faddr),
+					     "i" (ASI_M_FLUSH_SEG),
+					     "r" (a), "r" (b), "r" (c), "r" (d),
+					     "r" (e), "r" (f), "r" (g));
+		} while (faddr != start);
+		start += SRMMU_REAL_PMD_SIZE;
+	}
+	srmmu_set_context(octx);
+	local_irq_restore(flags);
+	FLUSH_END
+}
+
+static void cypress_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
+{
+	register unsigned long a, b, c, d, e, f, g;
+	struct mm_struct *mm = vma->vm_mm;
+	unsigned long flags, line;
+	int octx;
+
+	FLUSH_BEGIN(mm)
+	flush_user_windows();
+	local_irq_save(flags);
+	octx = srmmu_get_context();
+	srmmu_set_context(mm->context);
+	a = 0x20; b = 0x40; c = 0x60;
+	d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0;
+
+	page &= PAGE_MASK;
+	line = (page + PAGE_SIZE) - 0x100;
+	goto inside;
+	do {
+		line -= 0x100;
+	inside:
+			__asm__ __volatile__("sta %%g0, [%0] %1\n\t"
+					     "sta %%g0, [%0 + %2] %1\n\t"
+					     "sta %%g0, [%0 + %3] %1\n\t"
+					     "sta %%g0, [%0 + %4] %1\n\t"
+					     "sta %%g0, [%0 + %5] %1\n\t"
+					     "sta %%g0, [%0 + %6] %1\n\t"
+					     "sta %%g0, [%0 + %7] %1\n\t"
+					     "sta %%g0, [%0 + %8] %1\n\t" : :
+					     "r" (line),
+					     "i" (ASI_M_FLUSH_PAGE),
+					     "r" (a), "r" (b), "r" (c), "r" (d),
+					     "r" (e), "r" (f), "r" (g));
+	} while(line != page);
+	srmmu_set_context(octx);
+	local_irq_restore(flags);
+	FLUSH_END
+}
+
+/* Cypress is copy-back, at least that is how we configure it. */
+static void cypress_flush_page_to_ram(unsigned long page)
+{
+	register unsigned long a, b, c, d, e, f, g;
+	unsigned long line;
+
+	a = 0x20; b = 0x40; c = 0x60; d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0;
+	page &= PAGE_MASK;
+	line = (page + PAGE_SIZE) - 0x100;
+	goto inside;
+	do {
+		line -= 0x100;
+	inside:
+		__asm__ __volatile__("sta %%g0, [%0] %1\n\t"
+				     "sta %%g0, [%0 + %2] %1\n\t"
+				     "sta %%g0, [%0 + %3] %1\n\t"
+				     "sta %%g0, [%0 + %4] %1\n\t"
+				     "sta %%g0, [%0 + %5] %1\n\t"
+				     "sta %%g0, [%0 + %6] %1\n\t"
+				     "sta %%g0, [%0 + %7] %1\n\t"
+				     "sta %%g0, [%0 + %8] %1\n\t" : :
+				     "r" (line),
+				     "i" (ASI_M_FLUSH_PAGE),
+				     "r" (a), "r" (b), "r" (c), "r" (d),
+				     "r" (e), "r" (f), "r" (g));
+	} while(line != page);
+}
+
+/* Cypress is also IO cache coherent. */
+static void cypress_flush_page_for_dma(unsigned long page)
+{
+}
+
+/* Cypress has unified L2 VIPT, from which both instructions and data
+ * are stored.  It does not have an onboard icache of any sort, therefore
+ * no flush is necessary.
+ */
+static void cypress_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr)
+{
+}
+
+static void cypress_flush_tlb_all(void)
+{
+	srmmu_flush_whole_tlb();
+}
+
+static void cypress_flush_tlb_mm(struct mm_struct *mm)
+{
+	FLUSH_BEGIN(mm)
+	__asm__ __volatile__(
+	"lda	[%0] %3, %%g5\n\t"
+	"sta	%2, [%0] %3\n\t"
+	"sta	%%g0, [%1] %4\n\t"
+	"sta	%%g5, [%0] %3\n"
+	: /* no outputs */
+	: "r" (SRMMU_CTX_REG), "r" (0x300), "r" (mm->context),
+	  "i" (ASI_M_MMUREGS), "i" (ASI_M_FLUSH_PROBE)
+	: "g5");
+	FLUSH_END
+}
+
+static void cypress_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
+{
+	struct mm_struct *mm = vma->vm_mm;
+	unsigned long size;
+
+	FLUSH_BEGIN(mm)
+	start &= SRMMU_PGDIR_MASK;
+	size = SRMMU_PGDIR_ALIGN(end) - start;
+	__asm__ __volatile__(
+		"lda	[%0] %5, %%g5\n\t"
+		"sta	%1, [%0] %5\n"
+		"1:\n\t"
+		"subcc	%3, %4, %3\n\t"
+		"bne	1b\n\t"
+		" sta	%%g0, [%2 + %3] %6\n\t"
+		"sta	%%g5, [%0] %5\n"
+	: /* no outputs */
+	: "r" (SRMMU_CTX_REG), "r" (mm->context), "r" (start | 0x200),
+	  "r" (size), "r" (SRMMU_PGDIR_SIZE), "i" (ASI_M_MMUREGS),
+	  "i" (ASI_M_FLUSH_PROBE)
+	: "g5", "cc");
+	FLUSH_END
+}
+
+static void cypress_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+{
+	struct mm_struct *mm = vma->vm_mm;
+
+	FLUSH_BEGIN(mm)
+	__asm__ __volatile__(
+	"lda	[%0] %3, %%g5\n\t"
+	"sta	%1, [%0] %3\n\t"
+	"sta	%%g0, [%2] %4\n\t"
+	"sta	%%g5, [%0] %3\n"
+	: /* no outputs */
+	: "r" (SRMMU_CTX_REG), "r" (mm->context), "r" (page & PAGE_MASK),
+	  "i" (ASI_M_MMUREGS), "i" (ASI_M_FLUSH_PROBE)
+	: "g5");
+	FLUSH_END
+}
+
+/* viking.S */
+extern void viking_flush_cache_all(void);
+extern void viking_flush_cache_mm(struct mm_struct *mm);
+extern void viking_flush_cache_range(struct vm_area_struct *vma, unsigned long start,
+				     unsigned long end);
+extern void viking_flush_cache_page(struct vm_area_struct *vma, unsigned long page);
+extern void viking_flush_page_to_ram(unsigned long page);
+extern void viking_flush_page_for_dma(unsigned long page);
+extern void viking_flush_sig_insns(struct mm_struct *mm, unsigned long addr);
+extern void viking_flush_page(unsigned long page);
+extern void viking_mxcc_flush_page(unsigned long page);
+extern void viking_flush_tlb_all(void);
+extern void viking_flush_tlb_mm(struct mm_struct *mm);
+extern void viking_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
+				   unsigned long end);
+extern void viking_flush_tlb_page(struct vm_area_struct *vma,
+				  unsigned long page);
+extern void sun4dsmp_flush_tlb_all(void);
+extern void sun4dsmp_flush_tlb_mm(struct mm_struct *mm);
+extern void sun4dsmp_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
+				   unsigned long end);
+extern void sun4dsmp_flush_tlb_page(struct vm_area_struct *vma,
+				  unsigned long page);
+
+/* hypersparc.S */
+extern void hypersparc_flush_cache_all(void);
+extern void hypersparc_flush_cache_mm(struct mm_struct *mm);
+extern void hypersparc_flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end);
+extern void hypersparc_flush_cache_page(struct vm_area_struct *vma, unsigned long page);
+extern void hypersparc_flush_page_to_ram(unsigned long page);
+extern void hypersparc_flush_page_for_dma(unsigned long page);
+extern void hypersparc_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr);
+extern void hypersparc_flush_tlb_all(void);
+extern void hypersparc_flush_tlb_mm(struct mm_struct *mm);
+extern void hypersparc_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end);
+extern void hypersparc_flush_tlb_page(struct vm_area_struct *vma, unsigned long page);
+extern void hypersparc_setup_blockops(void);
+
+/*
+ * NOTE: All of this startup code assumes the low 16mb (approx.) of
+ *       kernel mappings are done with one single contiguous chunk of
+ *       ram.  On small ram machines (classics mainly) we only get
+ *       around 8mb mapped for us.
+ */
+
+void __init early_pgtable_allocfail(char *type)
+{
+	prom_printf("inherit_prom_mappings: Cannot alloc kernel %s.\n", type);
+	prom_halt();
+}
+
+void __init srmmu_early_allocate_ptable_skeleton(unsigned long start, unsigned long end)
+{
+	pgd_t *pgdp;
+	pmd_t *pmdp;
+	pte_t *ptep;
+
+	while(start < end) {
+		pgdp = pgd_offset_k(start);
+		if(srmmu_pgd_none(*(pgd_t *)__nocache_fix(pgdp))) {
+			pmdp = (pmd_t *) __srmmu_get_nocache(
+			    SRMMU_PMD_TABLE_SIZE, SRMMU_PMD_TABLE_SIZE);
+			if (pmdp == NULL)
+				early_pgtable_allocfail("pmd");
+			memset(__nocache_fix(pmdp), 0, SRMMU_PMD_TABLE_SIZE);
+			srmmu_pgd_set(__nocache_fix(pgdp), pmdp);
+		}
+		pmdp = srmmu_pmd_offset(__nocache_fix(pgdp), start);
+		if(srmmu_pmd_none(*(pmd_t *)__nocache_fix(pmdp))) {
+			ptep = (pte_t *)__srmmu_get_nocache(PTE_SIZE, PTE_SIZE);
+			if (ptep == NULL)
+				early_pgtable_allocfail("pte");
+			memset(__nocache_fix(ptep), 0, PTE_SIZE);
+			srmmu_pmd_set(__nocache_fix(pmdp), ptep);
+		}
+		if (start > (0xffffffffUL - PMD_SIZE))
+			break;
+		start = (start + PMD_SIZE) & PMD_MASK;
+	}
+}
+
+void __init srmmu_allocate_ptable_skeleton(unsigned long start, unsigned long end)
+{
+	pgd_t *pgdp;
+	pmd_t *pmdp;
+	pte_t *ptep;
+
+	while(start < end) {
+		pgdp = pgd_offset_k(start);
+		if(srmmu_pgd_none(*pgdp)) {
+			pmdp = (pmd_t *)__srmmu_get_nocache(SRMMU_PMD_TABLE_SIZE, SRMMU_PMD_TABLE_SIZE);
+			if (pmdp == NULL)
+				early_pgtable_allocfail("pmd");
+			memset(pmdp, 0, SRMMU_PMD_TABLE_SIZE);
+			srmmu_pgd_set(pgdp, pmdp);
+		}
+		pmdp = srmmu_pmd_offset(pgdp, start);
+		if(srmmu_pmd_none(*pmdp)) {
+			ptep = (pte_t *) __srmmu_get_nocache(PTE_SIZE,
+							     PTE_SIZE);
+			if (ptep == NULL)
+				early_pgtable_allocfail("pte");
+			memset(ptep, 0, PTE_SIZE);
+			srmmu_pmd_set(pmdp, ptep);
+		}
+		if (start > (0xffffffffUL - PMD_SIZE))
+			break;
+		start = (start + PMD_SIZE) & PMD_MASK;
+	}
+}
+
+/*
+ * This is much cleaner than poking around physical address space
+ * looking at the prom's page table directly which is what most
+ * other OS's do.  Yuck... this is much better.
+ */
+void __init srmmu_inherit_prom_mappings(unsigned long start,unsigned long end)
+{
+	pgd_t *pgdp;
+	pmd_t *pmdp;
+	pte_t *ptep;
+	int what = 0; /* 0 = normal-pte, 1 = pmd-level pte, 2 = pgd-level pte */
+	unsigned long prompte;
+
+	while(start <= end) {
+		if (start == 0)
+			break; /* probably wrap around */
+		if(start == 0xfef00000)
+			start = KADB_DEBUGGER_BEGVM;
+		if(!(prompte = srmmu_hwprobe(start))) {
+			start += PAGE_SIZE;
+			continue;
+		}
+    
+		/* A red snapper, see what it really is. */
+		what = 0;
+    
+		if(!(start & ~(SRMMU_REAL_PMD_MASK))) {
+			if(srmmu_hwprobe((start-PAGE_SIZE) + SRMMU_REAL_PMD_SIZE) == prompte)
+				what = 1;
+		}
+    
+		if(!(start & ~(SRMMU_PGDIR_MASK))) {
+			if(srmmu_hwprobe((start-PAGE_SIZE) + SRMMU_PGDIR_SIZE) ==
+			   prompte)
+				what = 2;
+		}
+    
+		pgdp = pgd_offset_k(start);
+		if(what == 2) {
+			*(pgd_t *)__nocache_fix(pgdp) = __pgd(prompte);
+			start += SRMMU_PGDIR_SIZE;
+			continue;
+		}
+		if(srmmu_pgd_none(*(pgd_t *)__nocache_fix(pgdp))) {
+			pmdp = (pmd_t *)__srmmu_get_nocache(SRMMU_PMD_TABLE_SIZE, SRMMU_PMD_TABLE_SIZE);
+			if (pmdp == NULL)
+				early_pgtable_allocfail("pmd");
+			memset(__nocache_fix(pmdp), 0, SRMMU_PMD_TABLE_SIZE);
+			srmmu_pgd_set(__nocache_fix(pgdp), pmdp);
+		}
+		pmdp = srmmu_pmd_offset(__nocache_fix(pgdp), start);
+		if(srmmu_pmd_none(*(pmd_t *)__nocache_fix(pmdp))) {
+			ptep = (pte_t *) __srmmu_get_nocache(PTE_SIZE,
+							     PTE_SIZE);
+			if (ptep == NULL)
+				early_pgtable_allocfail("pte");
+			memset(__nocache_fix(ptep), 0, PTE_SIZE);
+			srmmu_pmd_set(__nocache_fix(pmdp), ptep);
+		}
+		if(what == 1) {
+			/*
+			 * We bend the rule where all 16 PTPs in a pmd_t point
+			 * inside the same PTE page, and we leak a perfectly
+			 * good hardware PTE piece. Alternatives seem worse.
+			 */
+			unsigned int x;	/* Index of HW PMD in soft cluster */
+			x = (start >> PMD_SHIFT) & 15;
+			*(unsigned long *)__nocache_fix(&pmdp->pmdv[x]) = prompte;
+			start += SRMMU_REAL_PMD_SIZE;
+			continue;
+		}
+		ptep = srmmu_pte_offset(__nocache_fix(pmdp), start);
+		*(pte_t *)__nocache_fix(ptep) = __pte(prompte);
+		start += PAGE_SIZE;
+	}
+}
+
+#define KERNEL_PTE(page_shifted) ((page_shifted)|SRMMU_CACHE|SRMMU_PRIV|SRMMU_VALID)
+
+/* Create a third-level SRMMU 16MB page mapping. */
+static void __init do_large_mapping(unsigned long vaddr, unsigned long phys_base)
+{
+	pgd_t *pgdp = pgd_offset_k(vaddr);
+	unsigned long big_pte;
+
+	big_pte = KERNEL_PTE(phys_base >> 4);
+	*(pgd_t *)__nocache_fix(pgdp) = __pgd(big_pte);
+}
+
+/* Map sp_bank entry SP_ENTRY, starting at virtual address VBASE. */
+static unsigned long __init map_spbank(unsigned long vbase, int sp_entry)
+{
+	unsigned long pstart = (sp_banks[sp_entry].base_addr & SRMMU_PGDIR_MASK);
+	unsigned long vstart = (vbase & SRMMU_PGDIR_MASK);
+	unsigned long vend = SRMMU_PGDIR_ALIGN(vbase + sp_banks[sp_entry].num_bytes);
+	/* Map "low" memory only */
+	const unsigned long min_vaddr = PAGE_OFFSET;
+	const unsigned long max_vaddr = PAGE_OFFSET + SRMMU_MAXMEM;
+
+	if (vstart < min_vaddr || vstart >= max_vaddr)
+		return vstart;
+	
+	if (vend > max_vaddr || vend < min_vaddr)
+		vend = max_vaddr;
+
+	while(vstart < vend) {
+		do_large_mapping(vstart, pstart);
+		vstart += SRMMU_PGDIR_SIZE; pstart += SRMMU_PGDIR_SIZE;
+	}
+	return vstart;
+}
+
+static inline void memprobe_error(char *msg)
+{
+	prom_printf(msg);
+	prom_printf("Halting now...\n");
+	prom_halt();
+}
+
+static inline void map_kernel(void)
+{
+	int i;
+
+	if (phys_base > 0) {
+		do_large_mapping(PAGE_OFFSET, phys_base);
+	}
+
+	for (i = 0; sp_banks[i].num_bytes != 0; i++) {
+		map_spbank((unsigned long)__va(sp_banks[i].base_addr), i);
+	}
+
+	BTFIXUPSET_SIMM13(user_ptrs_per_pgd, PAGE_OFFSET / SRMMU_PGDIR_SIZE);
+}
+
+/* Paging initialization on the Sparc Reference MMU. */
+extern void sparc_context_init(int);
+
+void (*poke_srmmu)(void) __initdata = NULL;
+
+extern unsigned long bootmem_init(unsigned long *pages_avail);
+
+void __init srmmu_paging_init(void)
+{
+	int i, cpunode;
+	char node_str[128];
+	pgd_t *pgd;
+	pmd_t *pmd;
+	pte_t *pte;
+	unsigned long pages_avail;
+
+	sparc_iomap.start = SUN4M_IOBASE_VADDR;	/* 16MB of IOSPACE on all sun4m's. */
+
+	if (sparc_cpu_model == sun4d)
+		num_contexts = 65536; /* We know it is Viking */
+	else {
+		/* Find the number of contexts on the srmmu. */
+		cpunode = prom_getchild(prom_root_node);
+		num_contexts = 0;
+		while(cpunode != 0) {
+			prom_getstring(cpunode, "device_type", node_str, sizeof(node_str));
+			if(!strcmp(node_str, "cpu")) {
+				num_contexts = prom_getintdefault(cpunode, "mmu-nctx", 0x8);
+				break;
+			}
+			cpunode = prom_getsibling(cpunode);
+		}
+	}
+
+	if(!num_contexts) {
+		prom_printf("Something wrong, can't find cpu node in paging_init.\n");
+		prom_halt();
+	}
+
+	pages_avail = 0;
+	last_valid_pfn = bootmem_init(&pages_avail);
+
+	srmmu_nocache_calcsize();
+	srmmu_nocache_init();
+        srmmu_inherit_prom_mappings(0xfe400000,(LINUX_OPPROM_ENDVM-PAGE_SIZE));
+	map_kernel();
+
+	/* ctx table has to be physically aligned to its size */
+	srmmu_context_table = (ctxd_t *)__srmmu_get_nocache(num_contexts*sizeof(ctxd_t), num_contexts*sizeof(ctxd_t));
+	srmmu_ctx_table_phys = (ctxd_t *)__nocache_pa((unsigned long)srmmu_context_table);
+
+	for(i = 0; i < num_contexts; i++)
+		srmmu_ctxd_set((ctxd_t *)__nocache_fix(&srmmu_context_table[i]), srmmu_swapper_pg_dir);
+
+	flush_cache_all();
+	srmmu_set_ctable_ptr((unsigned long)srmmu_ctx_table_phys);
+	flush_tlb_all();
+	poke_srmmu();
+
+#ifdef CONFIG_SUN_IO
+	srmmu_allocate_ptable_skeleton(sparc_iomap.start, IOBASE_END);
+	srmmu_allocate_ptable_skeleton(DVMA_VADDR, DVMA_END);
+#endif
+
+	srmmu_allocate_ptable_skeleton(
+		__fix_to_virt(__end_of_fixed_addresses - 1), FIXADDR_TOP);
+	srmmu_allocate_ptable_skeleton(PKMAP_BASE, PKMAP_END);
+
+	pgd = pgd_offset_k(PKMAP_BASE);
+	pmd = srmmu_pmd_offset(pgd, PKMAP_BASE);
+	pte = srmmu_pte_offset(pmd, PKMAP_BASE);
+	pkmap_page_table = pte;
+
+	flush_cache_all();
+	flush_tlb_all();
+
+	sparc_context_init(num_contexts);
+
+	kmap_init();
+
+	{
+		unsigned long zones_size[MAX_NR_ZONES];
+		unsigned long zholes_size[MAX_NR_ZONES];
+		unsigned long npages;
+		int znum;
+
+		for (znum = 0; znum < MAX_NR_ZONES; znum++)
+			zones_size[znum] = zholes_size[znum] = 0;
+
+		npages = max_low_pfn - pfn_base;
+
+		zones_size[ZONE_DMA] = npages;
+		zholes_size[ZONE_DMA] = npages - pages_avail;
+
+		npages = highend_pfn - max_low_pfn;
+		zones_size[ZONE_HIGHMEM] = npages;
+		zholes_size[ZONE_HIGHMEM] = npages - calc_highpages();
+
+		free_area_init_node(0, &contig_page_data, zones_size,
+				    pfn_base, zholes_size);
+	}
+}
+
+static void srmmu_mmu_info(struct seq_file *m)
+{
+	seq_printf(m, 
+		   "MMU type\t: %s\n"
+		   "contexts\t: %d\n"
+		   "nocache total\t: %ld\n"
+		   "nocache used\t: %d\n",
+		   srmmu_name,
+		   num_contexts,
+		   srmmu_nocache_size,
+		   srmmu_nocache_map.used << SRMMU_NOCACHE_BITMAP_SHIFT);
+}
+
+static void srmmu_update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte)
+{
+}
+
+static void srmmu_destroy_context(struct mm_struct *mm)
+{
+
+	if(mm->context != NO_CONTEXT) {
+		flush_cache_mm(mm);
+		srmmu_ctxd_set(&srmmu_context_table[mm->context], srmmu_swapper_pg_dir);
+		flush_tlb_mm(mm);
+		spin_lock(&srmmu_context_spinlock);
+		free_context(mm->context);
+		spin_unlock(&srmmu_context_spinlock);
+		mm->context = NO_CONTEXT;
+	}
+}
+
+/* Init various srmmu chip types. */
+static void __init srmmu_is_bad(void)
+{
+	prom_printf("Could not determine SRMMU chip type.\n");
+	prom_halt();
+}
+
+static void __init init_vac_layout(void)
+{
+	int nd, cache_lines;
+	char node_str[128];
+#ifdef CONFIG_SMP
+	int cpu = 0;
+	unsigned long max_size = 0;
+	unsigned long min_line_size = 0x10000000;
+#endif
+
+	nd = prom_getchild(prom_root_node);
+	while((nd = prom_getsibling(nd)) != 0) {
+		prom_getstring(nd, "device_type", node_str, sizeof(node_str));
+		if(!strcmp(node_str, "cpu")) {
+			vac_line_size = prom_getint(nd, "cache-line-size");
+			if (vac_line_size == -1) {
+				prom_printf("can't determine cache-line-size, "
+					    "halting.\n");
+				prom_halt();
+			}
+			cache_lines = prom_getint(nd, "cache-nlines");
+			if (cache_lines == -1) {
+				prom_printf("can't determine cache-nlines, halting.\n");
+				prom_halt();
+			}
+
+			vac_cache_size = cache_lines * vac_line_size;
+#ifdef CONFIG_SMP
+			if(vac_cache_size > max_size)
+				max_size = vac_cache_size;
+			if(vac_line_size < min_line_size)
+				min_line_size = vac_line_size;
+			cpu++;
+			if (cpu >= NR_CPUS || !cpu_online(cpu))
+				break;
+#else
+			break;
+#endif
+		}
+	}
+	if(nd == 0) {
+		prom_printf("No CPU nodes found, halting.\n");
+		prom_halt();
+	}
+#ifdef CONFIG_SMP
+	vac_cache_size = max_size;
+	vac_line_size = min_line_size;
+#endif
+	printk("SRMMU: Using VAC size of %d bytes, line size %d bytes.\n",
+	       (int)vac_cache_size, (int)vac_line_size);
+}
+
+static void __init poke_hypersparc(void)
+{
+	volatile unsigned long clear;
+	unsigned long mreg = srmmu_get_mmureg();
+
+	hyper_flush_unconditional_combined();
+
+	mreg &= ~(HYPERSPARC_CWENABLE);
+	mreg |= (HYPERSPARC_CENABLE | HYPERSPARC_WBENABLE);
+	mreg |= (HYPERSPARC_CMODE);
+
+	srmmu_set_mmureg(mreg);
+
+#if 0 /* XXX I think this is bad news... -DaveM */
+	hyper_clear_all_tags();
+#endif
+
+	put_ross_icr(HYPERSPARC_ICCR_FTD | HYPERSPARC_ICCR_ICE);
+	hyper_flush_whole_icache();
+	clear = srmmu_get_faddr();
+	clear = srmmu_get_fstatus();
+}
+
+static void __init init_hypersparc(void)
+{
+	srmmu_name = "ROSS HyperSparc";
+	srmmu_modtype = HyperSparc;
+
+	init_vac_layout();
+
+	is_hypersparc = 1;
+
+	BTFIXUPSET_CALL(pte_clear, srmmu_pte_clear, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_clear, srmmu_pmd_clear, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_clear, srmmu_pgd_clear, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_all, hypersparc_flush_cache_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_mm, hypersparc_flush_cache_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_range, hypersparc_flush_cache_range, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_page, hypersparc_flush_cache_page, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(flush_tlb_all, hypersparc_flush_tlb_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_mm, hypersparc_flush_tlb_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_range, hypersparc_flush_tlb_range, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_page, hypersparc_flush_tlb_page, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(__flush_page_to_ram, hypersparc_flush_page_to_ram, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_sig_insns, hypersparc_flush_sig_insns, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_page_for_dma, hypersparc_flush_page_for_dma, BTFIXUPCALL_NOP);
+
+
+	poke_srmmu = poke_hypersparc;
+
+	hypersparc_setup_blockops();
+}
+
+static void __init poke_cypress(void)
+{
+	unsigned long mreg = srmmu_get_mmureg();
+	unsigned long faddr, tagval;
+	volatile unsigned long cypress_sucks;
+	volatile unsigned long clear;
+
+	clear = srmmu_get_faddr();
+	clear = srmmu_get_fstatus();
+
+	if (!(mreg & CYPRESS_CENABLE)) {
+		for(faddr = 0x0; faddr < 0x10000; faddr += 20) {
+			__asm__ __volatile__("sta %%g0, [%0 + %1] %2\n\t"
+					     "sta %%g0, [%0] %2\n\t" : :
+					     "r" (faddr), "r" (0x40000),
+					     "i" (ASI_M_DATAC_TAG));
+		}
+	} else {
+		for(faddr = 0; faddr < 0x10000; faddr += 0x20) {
+			__asm__ __volatile__("lda [%1 + %2] %3, %0\n\t" :
+					     "=r" (tagval) :
+					     "r" (faddr), "r" (0x40000),
+					     "i" (ASI_M_DATAC_TAG));
+
+			/* If modified and valid, kick it. */
+			if((tagval & 0x60) == 0x60)
+				cypress_sucks = *(unsigned long *)
+							(0xf0020000 + faddr);
+		}
+	}
+
+	/* And one more, for our good neighbor, Mr. Broken Cypress. */
+	clear = srmmu_get_faddr();
+	clear = srmmu_get_fstatus();
+
+	mreg |= (CYPRESS_CENABLE | CYPRESS_CMODE);
+	srmmu_set_mmureg(mreg);
+}
+
+static void __init init_cypress_common(void)
+{
+	init_vac_layout();
+
+	BTFIXUPSET_CALL(pte_clear, srmmu_pte_clear, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_clear, srmmu_pmd_clear, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_clear, srmmu_pgd_clear, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_all, cypress_flush_cache_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_mm, cypress_flush_cache_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_range, cypress_flush_cache_range, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_page, cypress_flush_cache_page, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(flush_tlb_all, cypress_flush_tlb_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_mm, cypress_flush_tlb_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_page, cypress_flush_tlb_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_range, cypress_flush_tlb_range, BTFIXUPCALL_NORM);
+
+
+	BTFIXUPSET_CALL(__flush_page_to_ram, cypress_flush_page_to_ram, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_sig_insns, cypress_flush_sig_insns, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(flush_page_for_dma, cypress_flush_page_for_dma, BTFIXUPCALL_NOP);
+
+	poke_srmmu = poke_cypress;
+}
+
+static void __init init_cypress_604(void)
+{
+	srmmu_name = "ROSS Cypress-604(UP)";
+	srmmu_modtype = Cypress;
+	init_cypress_common();
+}
+
+static void __init init_cypress_605(unsigned long mrev)
+{
+	srmmu_name = "ROSS Cypress-605(MP)";
+	if(mrev == 0xe) {
+		srmmu_modtype = Cypress_vE;
+		hwbug_bitmask |= HWBUG_COPYBACK_BROKEN;
+	} else {
+		if(mrev == 0xd) {
+			srmmu_modtype = Cypress_vD;
+			hwbug_bitmask |= HWBUG_ASIFLUSH_BROKEN;
+		} else {
+			srmmu_modtype = Cypress;
+		}
+	}
+	init_cypress_common();
+}
+
+static void __init poke_swift(void)
+{
+	unsigned long mreg;
+
+	/* Clear any crap from the cache or else... */
+	swift_flush_cache_all();
+
+	/* Enable I & D caches */
+	mreg = srmmu_get_mmureg();
+	mreg |= (SWIFT_IE | SWIFT_DE);
+	/*
+	 * The Swift branch folding logic is completely broken.  At
+	 * trap time, if things are just right, if can mistakenly
+	 * think that a trap is coming from kernel mode when in fact
+	 * it is coming from user mode (it mis-executes the branch in
+	 * the trap code).  So you see things like crashme completely
+	 * hosing your machine which is completely unacceptable.  Turn
+	 * this shit off... nice job Fujitsu.
+	 */
+	mreg &= ~(SWIFT_BF);
+	srmmu_set_mmureg(mreg);
+}
+
+#define SWIFT_MASKID_ADDR  0x10003018
+static void __init init_swift(void)
+{
+	unsigned long swift_rev;
+
+	__asm__ __volatile__("lda [%1] %2, %0\n\t"
+			     "srl %0, 0x18, %0\n\t" :
+			     "=r" (swift_rev) :
+			     "r" (SWIFT_MASKID_ADDR), "i" (ASI_M_BYPASS));
+	srmmu_name = "Fujitsu Swift";
+	switch(swift_rev) {
+	case 0x11:
+	case 0x20:
+	case 0x23:
+	case 0x30:
+		srmmu_modtype = Swift_lots_o_bugs;
+		hwbug_bitmask |= (HWBUG_KERN_ACCBROKEN | HWBUG_KERN_CBITBROKEN);
+		/*
+		 * Gee george, I wonder why Sun is so hush hush about
+		 * this hardware bug... really braindamage stuff going
+		 * on here.  However I think we can find a way to avoid
+		 * all of the workaround overhead under Linux.  Basically,
+		 * any page fault can cause kernel pages to become user
+		 * accessible (the mmu gets confused and clears some of
+		 * the ACC bits in kernel ptes).  Aha, sounds pretty
+		 * horrible eh?  But wait, after extensive testing it appears
+		 * that if you use pgd_t level large kernel pte's (like the
+		 * 4MB pages on the Pentium) the bug does not get tripped
+		 * at all.  This avoids almost all of the major overhead.
+		 * Welcome to a world where your vendor tells you to,
+		 * "apply this kernel patch" instead of "sorry for the
+		 * broken hardware, send it back and we'll give you
+		 * properly functioning parts"
+		 */
+		break;
+	case 0x25:
+	case 0x31:
+		srmmu_modtype = Swift_bad_c;
+		hwbug_bitmask |= HWBUG_KERN_CBITBROKEN;
+		/*
+		 * You see Sun allude to this hardware bug but never
+		 * admit things directly, they'll say things like,
+		 * "the Swift chip cache problems" or similar.
+		 */
+		break;
+	default:
+		srmmu_modtype = Swift_ok;
+		break;
+	};
+
+	BTFIXUPSET_CALL(flush_cache_all, swift_flush_cache_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_mm, swift_flush_cache_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_page, swift_flush_cache_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_range, swift_flush_cache_range, BTFIXUPCALL_NORM);
+
+
+	BTFIXUPSET_CALL(flush_tlb_all, swift_flush_tlb_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_mm, swift_flush_tlb_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_page, swift_flush_tlb_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_range, swift_flush_tlb_range, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(__flush_page_to_ram, swift_flush_page_to_ram, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_sig_insns, swift_flush_sig_insns, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_page_for_dma, swift_flush_page_for_dma, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(update_mmu_cache, swift_update_mmu_cache, BTFIXUPCALL_NORM);
+
+	flush_page_for_dma_global = 0;
+
+	/*
+	 * Are you now convinced that the Swift is one of the
+	 * biggest VLSI abortions of all time?  Bravo Fujitsu!
+	 * Fujitsu, the !#?!%$'d up processor people.  I bet if
+	 * you examined the microcode of the Swift you'd find
+	 * XXX's all over the place.
+	 */
+	poke_srmmu = poke_swift;
+}
+
+static void turbosparc_flush_cache_all(void)
+{
+	flush_user_windows();
+	turbosparc_idflash_clear();
+}
+
+static void turbosparc_flush_cache_mm(struct mm_struct *mm)
+{
+	FLUSH_BEGIN(mm)
+	flush_user_windows();
+	turbosparc_idflash_clear();
+	FLUSH_END
+}
+
+static void turbosparc_flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
+{
+	FLUSH_BEGIN(vma->vm_mm)
+	flush_user_windows();
+	turbosparc_idflash_clear();
+	FLUSH_END
+}
+
+static void turbosparc_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
+{
+	FLUSH_BEGIN(vma->vm_mm)
+	flush_user_windows();
+	if (vma->vm_flags & VM_EXEC)
+		turbosparc_flush_icache();
+	turbosparc_flush_dcache();
+	FLUSH_END
+}
+
+/* TurboSparc is copy-back, if we turn it on, but this does not work. */
+static void turbosparc_flush_page_to_ram(unsigned long page)
+{
+#ifdef TURBOSPARC_WRITEBACK
+	volatile unsigned long clear;
+
+	if (srmmu_hwprobe(page))
+		turbosparc_flush_page_cache(page);
+	clear = srmmu_get_fstatus();
+#endif
+}
+
+static void turbosparc_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr)
+{
+}
+
+static void turbosparc_flush_page_for_dma(unsigned long page)
+{
+	turbosparc_flush_dcache();
+}
+
+static void turbosparc_flush_tlb_all(void)
+{
+	srmmu_flush_whole_tlb();
+}
+
+static void turbosparc_flush_tlb_mm(struct mm_struct *mm)
+{
+	FLUSH_BEGIN(mm)
+	srmmu_flush_whole_tlb();
+	FLUSH_END
+}
+
+static void turbosparc_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
+{
+	FLUSH_BEGIN(vma->vm_mm)
+	srmmu_flush_whole_tlb();
+	FLUSH_END
+}
+
+static void turbosparc_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+{
+	FLUSH_BEGIN(vma->vm_mm)
+	srmmu_flush_whole_tlb();
+	FLUSH_END
+}
+
+
+static void __init poke_turbosparc(void)
+{
+	unsigned long mreg = srmmu_get_mmureg();
+	unsigned long ccreg;
+
+	/* Clear any crap from the cache or else... */
+	turbosparc_flush_cache_all();
+	mreg &= ~(TURBOSPARC_ICENABLE | TURBOSPARC_DCENABLE); /* Temporarily disable I & D caches */
+	mreg &= ~(TURBOSPARC_PCENABLE);		/* Don't check parity */
+	srmmu_set_mmureg(mreg);
+	
+	ccreg = turbosparc_get_ccreg();
+
+#ifdef TURBOSPARC_WRITEBACK
+	ccreg |= (TURBOSPARC_SNENABLE);		/* Do DVMA snooping in Dcache */
+	ccreg &= ~(TURBOSPARC_uS2 | TURBOSPARC_WTENABLE);
+			/* Write-back D-cache, emulate VLSI
+			 * abortion number three, not number one */
+#else
+	/* For now let's play safe, optimize later */
+	ccreg |= (TURBOSPARC_SNENABLE | TURBOSPARC_WTENABLE);
+			/* Do DVMA snooping in Dcache, Write-thru D-cache */
+	ccreg &= ~(TURBOSPARC_uS2);
+			/* Emulate VLSI abortion number three, not number one */
+#endif
+
+	switch (ccreg & 7) {
+	case 0: /* No SE cache */
+	case 7: /* Test mode */
+		break;
+	default:
+		ccreg |= (TURBOSPARC_SCENABLE);
+	}
+	turbosparc_set_ccreg (ccreg);
+
+	mreg |= (TURBOSPARC_ICENABLE | TURBOSPARC_DCENABLE); /* I & D caches on */
+	mreg |= (TURBOSPARC_ICSNOOP);		/* Icache snooping on */
+	srmmu_set_mmureg(mreg);
+}
+
+static void __init init_turbosparc(void)
+{
+	srmmu_name = "Fujitsu TurboSparc";
+	srmmu_modtype = TurboSparc;
+
+	BTFIXUPSET_CALL(flush_cache_all, turbosparc_flush_cache_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_mm, turbosparc_flush_cache_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_page, turbosparc_flush_cache_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_range, turbosparc_flush_cache_range, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(flush_tlb_all, turbosparc_flush_tlb_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_mm, turbosparc_flush_tlb_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_page, turbosparc_flush_tlb_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_range, turbosparc_flush_tlb_range, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(__flush_page_to_ram, turbosparc_flush_page_to_ram, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(flush_sig_insns, turbosparc_flush_sig_insns, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(flush_page_for_dma, turbosparc_flush_page_for_dma, BTFIXUPCALL_NORM);
+
+	poke_srmmu = poke_turbosparc;
+}
+
+static void __init poke_tsunami(void)
+{
+	unsigned long mreg = srmmu_get_mmureg();
+
+	tsunami_flush_icache();
+	tsunami_flush_dcache();
+	mreg &= ~TSUNAMI_ITD;
+	mreg |= (TSUNAMI_IENAB | TSUNAMI_DENAB);
+	srmmu_set_mmureg(mreg);
+}
+
+static void __init init_tsunami(void)
+{
+	/*
+	 * Tsunami's pretty sane, Sun and TI actually got it
+	 * somewhat right this time.  Fujitsu should have
+	 * taken some lessons from them.
+	 */
+
+	srmmu_name = "TI Tsunami";
+	srmmu_modtype = Tsunami;
+
+	BTFIXUPSET_CALL(flush_cache_all, tsunami_flush_cache_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_mm, tsunami_flush_cache_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_page, tsunami_flush_cache_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_range, tsunami_flush_cache_range, BTFIXUPCALL_NORM);
+
+
+	BTFIXUPSET_CALL(flush_tlb_all, tsunami_flush_tlb_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_mm, tsunami_flush_tlb_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_page, tsunami_flush_tlb_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_range, tsunami_flush_tlb_range, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(__flush_page_to_ram, tsunami_flush_page_to_ram, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(flush_sig_insns, tsunami_flush_sig_insns, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_page_for_dma, tsunami_flush_page_for_dma, BTFIXUPCALL_NORM);
+
+	poke_srmmu = poke_tsunami;
+
+	tsunami_setup_blockops();
+}
+
+static void __init poke_viking(void)
+{
+	unsigned long mreg = srmmu_get_mmureg();
+	static int smp_catch;
+
+	if(viking_mxcc_present) {
+		unsigned long mxcc_control = mxcc_get_creg();
+
+		mxcc_control |= (MXCC_CTL_ECE | MXCC_CTL_PRE | MXCC_CTL_MCE);
+		mxcc_control &= ~(MXCC_CTL_RRC);
+		mxcc_set_creg(mxcc_control);
+
+		/*
+		 * We don't need memory parity checks.
+		 * XXX This is a mess, have to dig out later. ecd.
+		viking_mxcc_turn_off_parity(&mreg, &mxcc_control);
+		 */
+
+		/* We do cache ptables on MXCC. */
+		mreg |= VIKING_TCENABLE;
+	} else {
+		unsigned long bpreg;
+
+		mreg &= ~(VIKING_TCENABLE);
+		if(smp_catch++) {
+			/* Must disable mixed-cmd mode here for other cpu's. */
+			bpreg = viking_get_bpreg();
+			bpreg &= ~(VIKING_ACTION_MIX);
+			viking_set_bpreg(bpreg);
+
+			/* Just in case PROM does something funny. */
+			msi_set_sync();
+		}
+	}
+
+	mreg |= VIKING_SPENABLE;
+	mreg |= (VIKING_ICENABLE | VIKING_DCENABLE);
+	mreg |= VIKING_SBENABLE;
+	mreg &= ~(VIKING_ACENABLE);
+	srmmu_set_mmureg(mreg);
+
+#ifdef CONFIG_SMP
+	/* Avoid unnecessary cross calls. */
+	BTFIXUPCOPY_CALL(flush_cache_all, local_flush_cache_all);
+	BTFIXUPCOPY_CALL(flush_cache_mm, local_flush_cache_mm);
+	BTFIXUPCOPY_CALL(flush_cache_range, local_flush_cache_range);
+	BTFIXUPCOPY_CALL(flush_cache_page, local_flush_cache_page);
+	BTFIXUPCOPY_CALL(__flush_page_to_ram, local_flush_page_to_ram);
+	BTFIXUPCOPY_CALL(flush_sig_insns, local_flush_sig_insns);
+	BTFIXUPCOPY_CALL(flush_page_for_dma, local_flush_page_for_dma);
+	btfixup();
+#endif
+}
+
+static void __init init_viking(void)
+{
+	unsigned long mreg = srmmu_get_mmureg();
+
+	/* Ahhh, the viking.  SRMMU VLSI abortion number two... */
+	if(mreg & VIKING_MMODE) {
+		srmmu_name = "TI Viking";
+		viking_mxcc_present = 0;
+		msi_set_sync();
+
+		BTFIXUPSET_CALL(pte_clear, srmmu_pte_clear, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(pmd_clear, srmmu_pmd_clear, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(pgd_clear, srmmu_pgd_clear, BTFIXUPCALL_NORM);
+
+		/*
+		 * We need this to make sure old viking takes no hits
+		 * on it's cache for dma snoops to workaround the
+		 * "load from non-cacheable memory" interrupt bug.
+		 * This is only necessary because of the new way in
+		 * which we use the IOMMU.
+		 */
+		BTFIXUPSET_CALL(flush_page_for_dma, viking_flush_page, BTFIXUPCALL_NORM);
+
+		flush_page_for_dma_global = 0;
+	} else {
+		srmmu_name = "TI Viking/MXCC";
+		viking_mxcc_present = 1;
+
+		srmmu_cache_pagetables = 1;
+
+		/* MXCC vikings lack the DMA snooping bug. */
+		BTFIXUPSET_CALL(flush_page_for_dma, viking_flush_page_for_dma, BTFIXUPCALL_NOP);
+	}
+
+	BTFIXUPSET_CALL(flush_cache_all, viking_flush_cache_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_mm, viking_flush_cache_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_page, viking_flush_cache_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_range, viking_flush_cache_range, BTFIXUPCALL_NORM);
+
+#ifdef CONFIG_SMP
+	if (sparc_cpu_model == sun4d) {
+		BTFIXUPSET_CALL(flush_tlb_all, sun4dsmp_flush_tlb_all, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_mm, sun4dsmp_flush_tlb_mm, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_page, sun4dsmp_flush_tlb_page, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_range, sun4dsmp_flush_tlb_range, BTFIXUPCALL_NORM);
+	} else
+#endif
+	{
+		BTFIXUPSET_CALL(flush_tlb_all, viking_flush_tlb_all, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_mm, viking_flush_tlb_mm, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_page, viking_flush_tlb_page, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_range, viking_flush_tlb_range, BTFIXUPCALL_NORM);
+	}
+
+	BTFIXUPSET_CALL(__flush_page_to_ram, viking_flush_page_to_ram, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(flush_sig_insns, viking_flush_sig_insns, BTFIXUPCALL_NOP);
+
+	poke_srmmu = poke_viking;
+}
+
+/* Probe for the srmmu chip version. */
+static void __init get_srmmu_type(void)
+{
+	unsigned long mreg, psr;
+	unsigned long mod_typ, mod_rev, psr_typ, psr_vers;
+
+	srmmu_modtype = SRMMU_INVAL_MOD;
+	hwbug_bitmask = 0;
+
+	mreg = srmmu_get_mmureg(); psr = get_psr();
+	mod_typ = (mreg & 0xf0000000) >> 28;
+	mod_rev = (mreg & 0x0f000000) >> 24;
+	psr_typ = (psr >> 28) & 0xf;
+	psr_vers = (psr >> 24) & 0xf;
+
+	/* First, check for HyperSparc or Cypress. */
+	if(mod_typ == 1) {
+		switch(mod_rev) {
+		case 7:
+			/* UP or MP Hypersparc */
+			init_hypersparc();
+			break;
+		case 0:
+		case 2:
+			/* Uniprocessor Cypress */
+			init_cypress_604();
+			break;
+		case 10:
+		case 11:
+		case 12:
+			/* _REALLY OLD_ Cypress MP chips... */
+		case 13:
+		case 14:
+		case 15:
+			/* MP Cypress mmu/cache-controller */
+			init_cypress_605(mod_rev);
+			break;
+		default:
+			/* Some other Cypress revision, assume a 605. */
+			init_cypress_605(mod_rev);
+			break;
+		};
+		return;
+	}
+	
+	/*
+	 * Now Fujitsu TurboSparc. It might happen that it is
+	 * in Swift emulation mode, so we will check later...
+	 */
+	if (psr_typ == 0 && psr_vers == 5) {
+		init_turbosparc();
+		return;
+	}
+
+	/* Next check for Fujitsu Swift. */
+	if(psr_typ == 0 && psr_vers == 4) {
+		int cpunode;
+		char node_str[128];
+
+		/* Look if it is not a TurboSparc emulating Swift... */
+		cpunode = prom_getchild(prom_root_node);
+		while((cpunode = prom_getsibling(cpunode)) != 0) {
+			prom_getstring(cpunode, "device_type", node_str, sizeof(node_str));
+			if(!strcmp(node_str, "cpu")) {
+				if (!prom_getintdefault(cpunode, "psr-implementation", 1) &&
+				    prom_getintdefault(cpunode, "psr-version", 1) == 5) {
+					init_turbosparc();
+					return;
+				}
+				break;
+			}
+		}
+		
+		init_swift();
+		return;
+	}
+
+	/* Now the Viking family of srmmu. */
+	if(psr_typ == 4 &&
+	   ((psr_vers == 0) ||
+	    ((psr_vers == 1) && (mod_typ == 0) && (mod_rev == 0)))) {
+		init_viking();
+		return;
+	}
+
+	/* Finally the Tsunami. */
+	if(psr_typ == 4 && psr_vers == 1 && (mod_typ || mod_rev)) {
+		init_tsunami();
+		return;
+	}
+
+	/* Oh well */
+	srmmu_is_bad();
+}
+
+/* don't laugh, static pagetables */
+static void srmmu_check_pgt_cache(int low, int high)
+{
+}
+
+extern unsigned long spwin_mmu_patchme, fwin_mmu_patchme,
+	tsetup_mmu_patchme, rtrap_mmu_patchme;
+
+extern unsigned long spwin_srmmu_stackchk, srmmu_fwin_stackchk,
+	tsetup_srmmu_stackchk, srmmu_rett_stackchk;
+
+extern unsigned long srmmu_fault;
+
+#define PATCH_BRANCH(insn, dest) do { \
+		iaddr = &(insn); \
+		daddr = &(dest); \
+		*iaddr = SPARC_BRANCH((unsigned long) daddr, (unsigned long) iaddr); \
+	} while(0)
+
+static void __init patch_window_trap_handlers(void)
+{
+	unsigned long *iaddr, *daddr;
+	
+	PATCH_BRANCH(spwin_mmu_patchme, spwin_srmmu_stackchk);
+	PATCH_BRANCH(fwin_mmu_patchme, srmmu_fwin_stackchk);
+	PATCH_BRANCH(tsetup_mmu_patchme, tsetup_srmmu_stackchk);
+	PATCH_BRANCH(rtrap_mmu_patchme, srmmu_rett_stackchk);
+	PATCH_BRANCH(sparc_ttable[SP_TRAP_TFLT].inst_three, srmmu_fault);
+	PATCH_BRANCH(sparc_ttable[SP_TRAP_DFLT].inst_three, srmmu_fault);
+	PATCH_BRANCH(sparc_ttable[SP_TRAP_DACC].inst_three, srmmu_fault);
+}
+
+#ifdef CONFIG_SMP
+/* Local cross-calls. */
+static void smp_flush_page_for_dma(unsigned long page)
+{
+	xc1((smpfunc_t) BTFIXUP_CALL(local_flush_page_for_dma), page);
+	local_flush_page_for_dma(page);
+}
+
+#endif
+
+static pte_t srmmu_pgoff_to_pte(unsigned long pgoff)
+{
+	return __pte((pgoff << SRMMU_PTE_FILE_SHIFT) | SRMMU_FILE);
+}
+
+static unsigned long srmmu_pte_to_pgoff(pte_t pte)
+{
+	return pte_val(pte) >> SRMMU_PTE_FILE_SHIFT;
+}
+
+/* Load up routines and constants for sun4m and sun4d mmu */
+void __init ld_mmu_srmmu(void)
+{
+	extern void ld_mmu_iommu(void);
+	extern void ld_mmu_iounit(void);
+	extern void ___xchg32_sun4md(void);
+
+	BTFIXUPSET_SIMM13(pgdir_shift, SRMMU_PGDIR_SHIFT);
+	BTFIXUPSET_SETHI(pgdir_size, SRMMU_PGDIR_SIZE);
+	BTFIXUPSET_SETHI(pgdir_mask, SRMMU_PGDIR_MASK);
+
+	BTFIXUPSET_SIMM13(ptrs_per_pmd, SRMMU_PTRS_PER_PMD);
+	BTFIXUPSET_SIMM13(ptrs_per_pgd, SRMMU_PTRS_PER_PGD);
+
+	BTFIXUPSET_INT(page_none, pgprot_val(SRMMU_PAGE_NONE));
+	BTFIXUPSET_INT(page_shared, pgprot_val(SRMMU_PAGE_SHARED));
+	BTFIXUPSET_INT(page_copy, pgprot_val(SRMMU_PAGE_COPY));
+	BTFIXUPSET_INT(page_readonly, pgprot_val(SRMMU_PAGE_RDONLY));
+	BTFIXUPSET_INT(page_kernel, pgprot_val(SRMMU_PAGE_KERNEL));
+	page_kernel = pgprot_val(SRMMU_PAGE_KERNEL);
+	pg_iobits = SRMMU_VALID | SRMMU_WRITE | SRMMU_REF;
+
+	/* Functions */
+#ifndef CONFIG_SMP	
+	BTFIXUPSET_CALL(___xchg32, ___xchg32_sun4md, BTFIXUPCALL_SWAPG1G2);
+#endif
+	BTFIXUPSET_CALL(do_check_pgt_cache, srmmu_check_pgt_cache, BTFIXUPCALL_NOP);
+
+	BTFIXUPSET_CALL(set_pte, srmmu_set_pte, BTFIXUPCALL_SWAPO0O1);
+	BTFIXUPSET_CALL(switch_mm, srmmu_switch_mm, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(pte_pfn, srmmu_pte_pfn, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_page, srmmu_pmd_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_page, srmmu_pgd_page, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_SETHI(none_mask, 0xF0000000);
+
+	BTFIXUPSET_CALL(pte_present, srmmu_pte_present, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_clear, srmmu_pte_clear, BTFIXUPCALL_SWAPO0G0);
+	BTFIXUPSET_CALL(pte_read, srmmu_pte_read, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(pmd_bad, srmmu_pmd_bad, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_present, srmmu_pmd_present, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_clear, srmmu_pmd_clear, BTFIXUPCALL_SWAPO0G0);
+
+	BTFIXUPSET_CALL(pgd_none, srmmu_pgd_none, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_bad, srmmu_pgd_bad, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_present, srmmu_pgd_present, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_clear, srmmu_pgd_clear, BTFIXUPCALL_SWAPO0G0);
+
+	BTFIXUPSET_CALL(mk_pte, srmmu_mk_pte, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mk_pte_phys, srmmu_mk_pte_phys, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mk_pte_io, srmmu_mk_pte_io, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgd_set, srmmu_pgd_set, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_set, srmmu_pmd_set, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_populate, srmmu_pmd_populate, BTFIXUPCALL_NORM);
+	
+	BTFIXUPSET_INT(pte_modify_mask, SRMMU_CHG_MASK);
+	BTFIXUPSET_CALL(pmd_offset, srmmu_pmd_offset, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_offset_kernel, srmmu_pte_offset, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(free_pte_fast, srmmu_free_pte_fast, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_free, srmmu_pte_free, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_alloc_one_kernel, srmmu_pte_alloc_one_kernel, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_alloc_one, srmmu_pte_alloc_one, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(free_pmd_fast, srmmu_pmd_free, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_alloc_one, srmmu_pmd_alloc_one, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(free_pgd_fast, srmmu_free_pgd_fast, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(get_pgd_fast, srmmu_get_pgd_fast, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_HALF(pte_writei, SRMMU_WRITE);
+	BTFIXUPSET_HALF(pte_dirtyi, SRMMU_DIRTY);
+	BTFIXUPSET_HALF(pte_youngi, SRMMU_REF);
+	BTFIXUPSET_HALF(pte_filei, SRMMU_FILE);
+	BTFIXUPSET_HALF(pte_wrprotecti, SRMMU_WRITE);
+	BTFIXUPSET_HALF(pte_mkcleani, SRMMU_DIRTY);
+	BTFIXUPSET_HALF(pte_mkoldi, SRMMU_REF);
+	BTFIXUPSET_CALL(pte_mkwrite, srmmu_pte_mkwrite, BTFIXUPCALL_ORINT(SRMMU_WRITE));
+	BTFIXUPSET_CALL(pte_mkdirty, srmmu_pte_mkdirty, BTFIXUPCALL_ORINT(SRMMU_DIRTY));
+	BTFIXUPSET_CALL(pte_mkyoung, srmmu_pte_mkyoung, BTFIXUPCALL_ORINT(SRMMU_REF));
+	BTFIXUPSET_CALL(update_mmu_cache, srmmu_update_mmu_cache, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(destroy_context, srmmu_destroy_context, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(sparc_mapiorange, srmmu_mapiorange, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(sparc_unmapiorange, srmmu_unmapiorange, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(__swp_type, srmmu_swp_type, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(__swp_offset, srmmu_swp_offset, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(__swp_entry, srmmu_swp_entry, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(mmu_info, srmmu_mmu_info, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(alloc_thread_info, srmmu_alloc_thread_info, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(free_thread_info, srmmu_free_thread_info, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(pte_to_pgoff, srmmu_pte_to_pgoff, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgoff_to_pte, srmmu_pgoff_to_pte, BTFIXUPCALL_NORM);
+
+	get_srmmu_type();
+	patch_window_trap_handlers();
+
+#ifdef CONFIG_SMP
+	/* El switcheroo... */
+
+	BTFIXUPCOPY_CALL(local_flush_cache_all, flush_cache_all);
+	BTFIXUPCOPY_CALL(local_flush_cache_mm, flush_cache_mm);
+	BTFIXUPCOPY_CALL(local_flush_cache_range, flush_cache_range);
+	BTFIXUPCOPY_CALL(local_flush_cache_page, flush_cache_page);
+	BTFIXUPCOPY_CALL(local_flush_tlb_all, flush_tlb_all);
+	BTFIXUPCOPY_CALL(local_flush_tlb_mm, flush_tlb_mm);
+	BTFIXUPCOPY_CALL(local_flush_tlb_range, flush_tlb_range);
+	BTFIXUPCOPY_CALL(local_flush_tlb_page, flush_tlb_page);
+	BTFIXUPCOPY_CALL(local_flush_page_to_ram, __flush_page_to_ram);
+	BTFIXUPCOPY_CALL(local_flush_sig_insns, flush_sig_insns);
+	BTFIXUPCOPY_CALL(local_flush_page_for_dma, flush_page_for_dma);
+
+	BTFIXUPSET_CALL(flush_cache_all, smp_flush_cache_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_mm, smp_flush_cache_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_range, smp_flush_cache_range, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_page, smp_flush_cache_page, BTFIXUPCALL_NORM);
+	if (sparc_cpu_model != sun4d) {
+		BTFIXUPSET_CALL(flush_tlb_all, smp_flush_tlb_all, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_mm, smp_flush_tlb_mm, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_range, smp_flush_tlb_range, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_page, smp_flush_tlb_page, BTFIXUPCALL_NORM);
+	}
+	BTFIXUPSET_CALL(__flush_page_to_ram, smp_flush_page_to_ram, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_sig_insns, smp_flush_sig_insns, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_page_for_dma, smp_flush_page_for_dma, BTFIXUPCALL_NORM);
+#endif
+
+	if (sparc_cpu_model == sun4d)
+		ld_mmu_iounit();
+	else
+		ld_mmu_iommu();
+#ifdef CONFIG_SMP
+	if (sparc_cpu_model == sun4d)
+		sun4d_init_smp();
+	else
+		sun4m_init_smp();
+#endif
+}
diff --git a/arch/sparc/mm/sun4c.c b/arch/sparc/mm/sun4c.c
new file mode 100644
index 0000000..1d56039
--- /dev/null
+++ b/arch/sparc/mm/sun4c.c
@@ -0,0 +1,2276 @@
+/* $Id: sun4c.c,v 1.212 2001/12/21 04:56:15 davem Exp $
+ * sun4c.c: Doing in software what should be done in hardware.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1996 Andrew Tridgell (Andrew.Tridgell@anu.edu.au)
+ * Copyright (C) 1997-2000 Anton Blanchard (anton@samba.org)
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#define NR_TASK_BUCKETS 512
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/highmem.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+
+#include <asm/scatterlist.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/vaddrs.h>
+#include <asm/idprom.h>
+#include <asm/machines.h>
+#include <asm/memreg.h>
+#include <asm/processor.h>
+#include <asm/auxio.h>
+#include <asm/io.h>
+#include <asm/oplib.h>
+#include <asm/openprom.h>
+#include <asm/mmu_context.h>
+#include <asm/sun4paddr.h>
+#include <asm/highmem.h>
+#include <asm/btfixup.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+
+/* Because of our dynamic kernel TLB miss strategy, and how
+ * our DVMA mapping allocation works, you _MUST_:
+ *
+ * 1) Disable interrupts _and_ not touch any dynamic kernel
+ *    memory while messing with kernel MMU state.  By
+ *    dynamic memory I mean any object which is not in
+ *    the kernel image itself or a thread_union (both of
+ *    which are locked into the MMU).
+ * 2) Disable interrupts while messing with user MMU state.
+ */
+
+extern int num_segmaps, num_contexts;
+
+extern unsigned long page_kernel;
+
+#ifdef CONFIG_SUN4
+#define SUN4C_VAC_SIZE sun4c_vacinfo.num_bytes
+#else
+/* That's it, we prom_halt() on sun4c if the cache size is something other than 65536.
+ * So let's save some cycles and just use that everywhere except for that bootup
+ * sanity check.
+ */
+#define SUN4C_VAC_SIZE 65536
+#endif
+
+#define SUN4C_KERNEL_BUCKETS 32
+
+/* Flushing the cache. */
+struct sun4c_vac_props sun4c_vacinfo;
+unsigned long sun4c_kernel_faults;
+
+/* Invalidate every sun4c cache line tag. */
+static void __init sun4c_flush_all(void)
+{
+	unsigned long begin, end;
+
+	if (sun4c_vacinfo.on)
+		panic("SUN4C: AIEEE, trying to invalidate vac while it is on.");
+
+	/* Clear 'valid' bit in all cache line tags */
+	begin = AC_CACHETAGS;
+	end = (AC_CACHETAGS + SUN4C_VAC_SIZE);
+	while (begin < end) {
+		__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
+				     "r" (begin), "i" (ASI_CONTROL));
+		begin += sun4c_vacinfo.linesize;
+	}
+}
+
+static void sun4c_flush_context_hw(void)
+{
+	unsigned long end = SUN4C_VAC_SIZE;
+
+	__asm__ __volatile__(
+		"1:	addcc	%0, -4096, %0\n\t"
+		"	bne	1b\n\t"
+		"	 sta	%%g0, [%0] %2"
+	: "=&r" (end)
+	: "0" (end), "i" (ASI_HWFLUSHCONTEXT)
+	: "cc");
+}
+
+/* Must be called minimally with IRQs disabled. */
+static void sun4c_flush_segment_hw(unsigned long addr)
+{
+	if (sun4c_get_segmap(addr) != invalid_segment) {
+		unsigned long vac_size = SUN4C_VAC_SIZE;
+
+		__asm__ __volatile__(
+			"1:	addcc	%0, -4096, %0\n\t"
+			"	bne	1b\n\t"
+			"	 sta	%%g0, [%2 + %0] %3"
+			: "=&r" (vac_size)
+			: "0" (vac_size), "r" (addr), "i" (ASI_HWFLUSHSEG)
+			: "cc");
+	}
+}
+
+/* File local boot time fixups. */
+BTFIXUPDEF_CALL(void, sun4c_flush_page, unsigned long)
+BTFIXUPDEF_CALL(void, sun4c_flush_segment, unsigned long)
+BTFIXUPDEF_CALL(void, sun4c_flush_context, void)
+
+#define sun4c_flush_page(addr) BTFIXUP_CALL(sun4c_flush_page)(addr)
+#define sun4c_flush_segment(addr) BTFIXUP_CALL(sun4c_flush_segment)(addr)
+#define sun4c_flush_context() BTFIXUP_CALL(sun4c_flush_context)()
+
+/* Must be called minimally with interrupts disabled. */
+static void sun4c_flush_page_hw(unsigned long addr)
+{
+	addr &= PAGE_MASK;
+	if ((int)sun4c_get_pte(addr) < 0)
+		__asm__ __volatile__("sta %%g0, [%0] %1"
+				     : : "r" (addr), "i" (ASI_HWFLUSHPAGE));
+}
+
+/* Don't inline the software version as it eats too many cache lines if expanded. */
+static void sun4c_flush_context_sw(void)
+{
+	unsigned long nbytes = SUN4C_VAC_SIZE;
+	unsigned long lsize = sun4c_vacinfo.linesize;
+
+	__asm__ __volatile__(
+	"add	%2, %2, %%g1\n\t"
+	"add	%2, %%g1, %%g2\n\t"
+	"add	%2, %%g2, %%g3\n\t"
+	"add	%2, %%g3, %%g4\n\t"
+	"add	%2, %%g4, %%g5\n\t"
+	"add	%2, %%g5, %%o4\n\t"
+	"add	%2, %%o4, %%o5\n"
+	"1:\n\t"
+	"subcc	%0, %%o5, %0\n\t"
+	"sta	%%g0, [%0] %3\n\t"
+	"sta	%%g0, [%0 + %2] %3\n\t"
+	"sta	%%g0, [%0 + %%g1] %3\n\t"
+	"sta	%%g0, [%0 + %%g2] %3\n\t"
+	"sta	%%g0, [%0 + %%g3] %3\n\t"
+	"sta	%%g0, [%0 + %%g4] %3\n\t"
+	"sta	%%g0, [%0 + %%g5] %3\n\t"
+	"bg	1b\n\t"
+	" sta	%%g0, [%1 + %%o4] %3\n"
+	: "=&r" (nbytes)
+	: "0" (nbytes), "r" (lsize), "i" (ASI_FLUSHCTX)
+	: "g1", "g2", "g3", "g4", "g5", "o4", "o5", "cc");
+}
+
+/* Don't inline the software version as it eats too many cache lines if expanded. */
+static void sun4c_flush_segment_sw(unsigned long addr)
+{
+	if (sun4c_get_segmap(addr) != invalid_segment) {
+		unsigned long nbytes = SUN4C_VAC_SIZE;
+		unsigned long lsize = sun4c_vacinfo.linesize;
+
+		__asm__ __volatile__(
+		"add	%2, %2, %%g1\n\t"
+		"add	%2, %%g1, %%g2\n\t"
+		"add	%2, %%g2, %%g3\n\t"
+		"add	%2, %%g3, %%g4\n\t"
+		"add	%2, %%g4, %%g5\n\t"
+		"add	%2, %%g5, %%o4\n\t"
+		"add	%2, %%o4, %%o5\n"
+		"1:\n\t"
+		"subcc	%1, %%o5, %1\n\t"
+		"sta	%%g0, [%0] %6\n\t"
+		"sta	%%g0, [%0 + %2] %6\n\t"
+		"sta	%%g0, [%0 + %%g1] %6\n\t"
+		"sta	%%g0, [%0 + %%g2] %6\n\t"
+		"sta	%%g0, [%0 + %%g3] %6\n\t"
+		"sta	%%g0, [%0 + %%g4] %6\n\t"
+		"sta	%%g0, [%0 + %%g5] %6\n\t"
+		"sta	%%g0, [%0 + %%o4] %6\n\t"
+		"bg	1b\n\t"
+		" add	%0, %%o5, %0\n"
+		: "=&r" (addr), "=&r" (nbytes), "=&r" (lsize)
+		: "0" (addr), "1" (nbytes), "2" (lsize),
+		  "i" (ASI_FLUSHSEG)
+		: "g1", "g2", "g3", "g4", "g5", "o4", "o5", "cc");
+	}
+}
+
+/* Don't inline the software version as it eats too many cache lines if expanded. */
+static void sun4c_flush_page_sw(unsigned long addr)
+{
+	addr &= PAGE_MASK;
+	if ((sun4c_get_pte(addr) & (_SUN4C_PAGE_NOCACHE | _SUN4C_PAGE_VALID)) ==
+	    _SUN4C_PAGE_VALID) {
+		unsigned long left = PAGE_SIZE;
+		unsigned long lsize = sun4c_vacinfo.linesize;
+
+		__asm__ __volatile__(
+		"add	%2, %2, %%g1\n\t"
+		"add	%2, %%g1, %%g2\n\t"
+		"add	%2, %%g2, %%g3\n\t"
+		"add	%2, %%g3, %%g4\n\t"
+		"add	%2, %%g4, %%g5\n\t"
+		"add	%2, %%g5, %%o4\n\t"
+		"add	%2, %%o4, %%o5\n"
+		"1:\n\t"
+		"subcc	%1, %%o5, %1\n\t"
+		"sta	%%g0, [%0] %6\n\t"
+		"sta	%%g0, [%0 + %2] %6\n\t"
+		"sta	%%g0, [%0 + %%g1] %6\n\t"
+		"sta	%%g0, [%0 + %%g2] %6\n\t"
+		"sta	%%g0, [%0 + %%g3] %6\n\t"
+		"sta	%%g0, [%0 + %%g4] %6\n\t"
+		"sta	%%g0, [%0 + %%g5] %6\n\t"
+		"sta	%%g0, [%0 + %%o4] %6\n\t"
+		"bg	1b\n\t"
+		" add	%0, %%o5, %0\n"
+		: "=&r" (addr), "=&r" (left), "=&r" (lsize)
+		: "0" (addr), "1" (left), "2" (lsize),
+		  "i" (ASI_FLUSHPG)
+		: "g1", "g2", "g3", "g4", "g5", "o4", "o5", "cc");
+	}
+}
+
+/* The sun4c's do have an on chip store buffer.  And the way you
+ * clear them out isn't so obvious.  The only way I can think of
+ * to accomplish this is to read the current context register,
+ * store the same value there, then read an external hardware
+ * register.
+ */
+void sun4c_complete_all_stores(void)
+{
+	volatile int _unused;
+
+	_unused = sun4c_get_context();
+	sun4c_set_context(_unused);
+#ifdef CONFIG_SUN_AUXIO
+	_unused = get_auxio();
+#endif
+}
+
+/* Bootup utility functions. */
+static inline void sun4c_init_clean_segmap(unsigned char pseg)
+{
+	unsigned long vaddr;
+
+	sun4c_put_segmap(0, pseg);
+	for (vaddr = 0; vaddr < SUN4C_REAL_PGDIR_SIZE; vaddr += PAGE_SIZE)
+		sun4c_put_pte(vaddr, 0);
+	sun4c_put_segmap(0, invalid_segment);
+}
+
+static inline void sun4c_init_clean_mmu(unsigned long kernel_end)
+{
+	unsigned long vaddr;
+	unsigned char savectx, ctx;
+
+	savectx = sun4c_get_context();
+	kernel_end = SUN4C_REAL_PGDIR_ALIGN(kernel_end);
+	for (ctx = 0; ctx < num_contexts; ctx++) {
+		sun4c_set_context(ctx);
+		for (vaddr = 0; vaddr < 0x20000000; vaddr += SUN4C_REAL_PGDIR_SIZE)
+			sun4c_put_segmap(vaddr, invalid_segment);
+		for (vaddr = 0xe0000000; vaddr < KERNBASE; vaddr += SUN4C_REAL_PGDIR_SIZE)
+			sun4c_put_segmap(vaddr, invalid_segment);
+		for (vaddr = kernel_end; vaddr < KADB_DEBUGGER_BEGVM; vaddr += SUN4C_REAL_PGDIR_SIZE)
+			sun4c_put_segmap(vaddr, invalid_segment);
+		for (vaddr = LINUX_OPPROM_ENDVM; vaddr; vaddr += SUN4C_REAL_PGDIR_SIZE)
+			sun4c_put_segmap(vaddr, invalid_segment);
+	}
+	sun4c_set_context(savectx);
+}
+
+void __init sun4c_probe_vac(void)
+{
+	sun4c_disable_vac();
+
+	if (ARCH_SUN4) {
+		switch (idprom->id_machtype) {
+
+		case (SM_SUN4|SM_4_110):
+			sun4c_vacinfo.type = VAC_NONE;
+			sun4c_vacinfo.num_bytes = 0;
+			sun4c_vacinfo.linesize = 0;
+			sun4c_vacinfo.do_hwflushes = 0;
+			prom_printf("No VAC. Get some bucks and buy a real computer.");
+			prom_halt();
+			break;
+
+		case (SM_SUN4|SM_4_260):
+			sun4c_vacinfo.type = VAC_WRITE_BACK;
+			sun4c_vacinfo.num_bytes = 128 * 1024;
+			sun4c_vacinfo.linesize = 16;
+			sun4c_vacinfo.do_hwflushes = 0;
+			break;
+
+		case (SM_SUN4|SM_4_330):
+			sun4c_vacinfo.type = VAC_WRITE_THROUGH;
+			sun4c_vacinfo.num_bytes = 128 * 1024;
+			sun4c_vacinfo.linesize = 16;
+			sun4c_vacinfo.do_hwflushes = 0;
+			break;
+
+		case (SM_SUN4|SM_4_470):
+			sun4c_vacinfo.type = VAC_WRITE_BACK;
+			sun4c_vacinfo.num_bytes = 128 * 1024;
+			sun4c_vacinfo.linesize = 32;
+			sun4c_vacinfo.do_hwflushes = 0;
+			break;
+
+		default:
+			prom_printf("Cannot initialize VAC - weird sun4 model idprom->id_machtype = %d", idprom->id_machtype);
+			prom_halt();
+		};
+	} else {
+		sun4c_vacinfo.type = VAC_WRITE_THROUGH;
+
+		if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
+		    (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
+			/* PROM on SS1 lacks this info, to be super safe we
+			 * hard code it here since this arch is cast in stone.
+			 */
+			sun4c_vacinfo.num_bytes = 65536;
+			sun4c_vacinfo.linesize = 16;
+		} else {
+			sun4c_vacinfo.num_bytes =
+			 prom_getintdefault(prom_root_node, "vac-size", 65536);
+			sun4c_vacinfo.linesize =
+			 prom_getintdefault(prom_root_node, "vac-linesize", 16);
+		}
+		sun4c_vacinfo.do_hwflushes =
+		 prom_getintdefault(prom_root_node, "vac-hwflush", 0);
+
+		if (sun4c_vacinfo.do_hwflushes == 0)
+			sun4c_vacinfo.do_hwflushes =
+			 prom_getintdefault(prom_root_node, "vac_hwflush", 0);
+
+		if (sun4c_vacinfo.num_bytes != 65536) {
+			prom_printf("WEIRD Sun4C VAC cache size, "
+				    "tell sparclinux@vger.kernel.org");
+			prom_halt();
+		}
+	}
+
+	sun4c_vacinfo.num_lines =
+		(sun4c_vacinfo.num_bytes / sun4c_vacinfo.linesize);
+	switch (sun4c_vacinfo.linesize) {
+	case 16:
+		sun4c_vacinfo.log2lsize = 4;
+		break;
+	case 32:
+		sun4c_vacinfo.log2lsize = 5;
+		break;
+	default:
+		prom_printf("probe_vac: Didn't expect vac-linesize of %d, halting\n",
+			    sun4c_vacinfo.linesize);
+		prom_halt();
+	};
+
+	sun4c_flush_all();
+	sun4c_enable_vac();
+}
+
+/* Patch instructions for the low level kernel fault handler. */
+extern unsigned long invalid_segment_patch1, invalid_segment_patch1_ff;
+extern unsigned long invalid_segment_patch2, invalid_segment_patch2_ff;
+extern unsigned long invalid_segment_patch1_1ff, invalid_segment_patch2_1ff;
+extern unsigned long num_context_patch1, num_context_patch1_16;
+extern unsigned long num_context_patch2_16;
+extern unsigned long vac_linesize_patch, vac_linesize_patch_32;
+extern unsigned long vac_hwflush_patch1, vac_hwflush_patch1_on;
+extern unsigned long vac_hwflush_patch2, vac_hwflush_patch2_on;
+
+#define PATCH_INSN(src, dst) do {	\
+		daddr = &(dst);		\
+		iaddr = &(src);		\
+		*daddr = *iaddr;	\
+	} while (0)
+
+static void __init patch_kernel_fault_handler(void)
+{
+	unsigned long *iaddr, *daddr;
+
+	switch (num_segmaps) {
+		case 128:
+			/* Default, nothing to do. */
+			break;
+		case 256:
+			PATCH_INSN(invalid_segment_patch1_ff,
+				   invalid_segment_patch1);
+			PATCH_INSN(invalid_segment_patch2_ff,
+				   invalid_segment_patch2);
+			break;
+		case 512:
+			PATCH_INSN(invalid_segment_patch1_1ff,
+				   invalid_segment_patch1);
+			PATCH_INSN(invalid_segment_patch2_1ff,
+				   invalid_segment_patch2);
+			break;
+		default:
+			prom_printf("Unhandled number of segmaps: %d\n",
+				    num_segmaps);
+			prom_halt();
+	};
+	switch (num_contexts) {
+		case 8:
+			/* Default, nothing to do. */
+			break;
+		case 16:
+			PATCH_INSN(num_context_patch1_16,
+				   num_context_patch1);
+			break;
+		default:
+			prom_printf("Unhandled number of contexts: %d\n",
+				    num_contexts);
+			prom_halt();
+	};
+
+	if (sun4c_vacinfo.do_hwflushes != 0) {
+		PATCH_INSN(vac_hwflush_patch1_on, vac_hwflush_patch1);
+		PATCH_INSN(vac_hwflush_patch2_on, vac_hwflush_patch2);
+	} else {
+		switch (sun4c_vacinfo.linesize) {
+		case 16:
+			/* Default, nothing to do. */
+			break;
+		case 32:
+			PATCH_INSN(vac_linesize_patch_32, vac_linesize_patch);
+			break;
+		default:
+			prom_printf("Impossible VAC linesize %d, halting...\n",
+				    sun4c_vacinfo.linesize);
+			prom_halt();
+		};
+	}
+}
+
+static void __init sun4c_probe_mmu(void)
+{
+	if (ARCH_SUN4) {
+		switch (idprom->id_machtype) {
+		case (SM_SUN4|SM_4_110):
+			prom_printf("No support for 4100 yet\n");
+			prom_halt();
+			num_segmaps = 256;
+			num_contexts = 8;
+			break;
+
+		case (SM_SUN4|SM_4_260):
+			/* should be 512 segmaps. when it get fixed */
+			num_segmaps = 256;
+			num_contexts = 16;
+			break;
+
+		case (SM_SUN4|SM_4_330):
+			num_segmaps = 256;
+			num_contexts = 16;
+			break;
+
+		case (SM_SUN4|SM_4_470):
+			/* should be 1024 segmaps. when it get fixed */
+			num_segmaps = 256;
+			num_contexts = 64;
+			break;
+		default:
+			prom_printf("Invalid SUN4 model\n");
+			prom_halt();
+		};
+	} else {
+		if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
+		    (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
+			/* Hardcode these just to be safe, PROM on SS1 does
+		 	* not have this info available in the root node.
+		 	*/
+			num_segmaps = 128;
+			num_contexts = 8;
+		} else {
+			num_segmaps =
+			    prom_getintdefault(prom_root_node, "mmu-npmg", 128);
+			num_contexts =
+			    prom_getintdefault(prom_root_node, "mmu-nctx", 0x8);
+		}
+	}
+	patch_kernel_fault_handler();
+}
+
+volatile unsigned long *sun4c_memerr_reg = NULL;
+
+void __init sun4c_probe_memerr_reg(void)
+{
+	int node;
+	struct linux_prom_registers regs[1];
+
+	if (ARCH_SUN4) {
+		sun4c_memerr_reg = ioremap(sun4_memreg_physaddr, PAGE_SIZE);
+	} else {
+		node = prom_getchild(prom_root_node);
+		node = prom_searchsiblings(prom_root_node, "memory-error");
+		if (!node)
+			return;
+		if (prom_getproperty(node, "reg", (char *)regs, sizeof(regs)) <= 0)
+			return;
+		/* hmm I think regs[0].which_io is zero here anyways */
+		sun4c_memerr_reg = ioremap(regs[0].phys_addr, regs[0].reg_size);
+	}
+}
+
+static inline void sun4c_init_ss2_cache_bug(void)
+{
+	extern unsigned long start;
+
+	if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS2)) ||
+	    (idprom->id_machtype == (SM_SUN4C | SM_4C_IPX)) ||
+	    (idprom->id_machtype == (SM_SUN4 | SM_4_330)) ||
+	    (idprom->id_machtype == (SM_SUN4C | SM_4C_ELC))) {
+		/* Whee.. */
+		printk("SS2 cache bug detected, uncaching trap table page\n");
+		sun4c_flush_page((unsigned int) &start);
+		sun4c_put_pte(((unsigned long) &start),
+			(sun4c_get_pte((unsigned long) &start) | _SUN4C_PAGE_NOCACHE));
+	}
+}
+
+/* Addr is always aligned on a page boundary for us already. */
+static int sun4c_map_dma_area(dma_addr_t *pba, unsigned long va,
+    unsigned long addr, int len)
+{
+	unsigned long page, end;
+
+	*pba = addr;
+
+	end = PAGE_ALIGN((addr + len));
+	while (addr < end) {
+		page = va;
+		sun4c_flush_page(page);
+		page -= PAGE_OFFSET;
+		page >>= PAGE_SHIFT;
+		page |= (_SUN4C_PAGE_VALID | _SUN4C_PAGE_DIRTY |
+			 _SUN4C_PAGE_NOCACHE | _SUN4C_PAGE_PRIV);
+		sun4c_put_pte(addr, page);
+		addr += PAGE_SIZE;
+		va += PAGE_SIZE;
+	}
+
+	return 0;
+}
+
+static struct page *sun4c_translate_dvma(unsigned long busa)
+{
+	/* Fortunately for us, bus_addr == uncached_virt in sun4c. */
+	unsigned long pte = sun4c_get_pte(busa);
+	return pfn_to_page(pte & SUN4C_PFN_MASK);
+}
+
+static void sun4c_unmap_dma_area(unsigned long busa, int len)
+{
+	/* Fortunately for us, bus_addr == uncached_virt in sun4c. */
+	/* XXX Implement this */
+}
+
+/* TLB management. */
+
+/* Don't change this struct without changing entry.S. This is used
+ * in the in-window kernel fault handler, and you don't want to mess
+ * with that. (See sun4c_fault in entry.S).
+ */
+struct sun4c_mmu_entry {
+	struct sun4c_mmu_entry *next;
+	struct sun4c_mmu_entry *prev;
+	unsigned long vaddr;
+	unsigned char pseg;
+	unsigned char locked;
+
+	/* For user mappings only, and completely hidden from kernel
+	 * TLB miss code.
+	 */
+	unsigned char ctx;
+	struct sun4c_mmu_entry *lru_next;
+	struct sun4c_mmu_entry *lru_prev;
+};
+
+static struct sun4c_mmu_entry mmu_entry_pool[SUN4C_MAX_SEGMAPS];
+
+static void __init sun4c_init_mmu_entry_pool(void)
+{
+	int i;
+
+	for (i=0; i < SUN4C_MAX_SEGMAPS; i++) {
+		mmu_entry_pool[i].pseg = i;
+		mmu_entry_pool[i].next = NULL;
+		mmu_entry_pool[i].prev = NULL;
+		mmu_entry_pool[i].vaddr = 0;
+		mmu_entry_pool[i].locked = 0;
+		mmu_entry_pool[i].ctx = 0;
+		mmu_entry_pool[i].lru_next = NULL;
+		mmu_entry_pool[i].lru_prev = NULL;
+	}
+	mmu_entry_pool[invalid_segment].locked = 1;
+}
+
+static inline void fix_permissions(unsigned long vaddr, unsigned long bits_on,
+				   unsigned long bits_off)
+{
+	unsigned long start, end;
+
+	end = vaddr + SUN4C_REAL_PGDIR_SIZE;
+	for (start = vaddr; start < end; start += PAGE_SIZE)
+		if (sun4c_get_pte(start) & _SUN4C_PAGE_VALID)
+			sun4c_put_pte(start, (sun4c_get_pte(start) | bits_on) &
+				      ~bits_off);
+}
+
+static inline void sun4c_init_map_kernelprom(unsigned long kernel_end)
+{
+	unsigned long vaddr;
+	unsigned char pseg, ctx;
+#ifdef CONFIG_SUN4
+	/* sun4/110 and 260 have no kadb. */
+	if ((idprom->id_machtype != (SM_SUN4 | SM_4_260)) && 
+	    (idprom->id_machtype != (SM_SUN4 | SM_4_110))) {
+#endif
+	for (vaddr = KADB_DEBUGGER_BEGVM;
+	     vaddr < LINUX_OPPROM_ENDVM;
+	     vaddr += SUN4C_REAL_PGDIR_SIZE) {
+		pseg = sun4c_get_segmap(vaddr);
+		if (pseg != invalid_segment) {
+			mmu_entry_pool[pseg].locked = 1;
+			for (ctx = 0; ctx < num_contexts; ctx++)
+				prom_putsegment(ctx, vaddr, pseg);
+			fix_permissions(vaddr, _SUN4C_PAGE_PRIV, 0);
+		}
+	}
+#ifdef CONFIG_SUN4
+	}
+#endif
+	for (vaddr = KERNBASE; vaddr < kernel_end; vaddr += SUN4C_REAL_PGDIR_SIZE) {
+		pseg = sun4c_get_segmap(vaddr);
+		mmu_entry_pool[pseg].locked = 1;
+		for (ctx = 0; ctx < num_contexts; ctx++)
+			prom_putsegment(ctx, vaddr, pseg);
+		fix_permissions(vaddr, _SUN4C_PAGE_PRIV, _SUN4C_PAGE_NOCACHE);
+	}
+}
+
+static void __init sun4c_init_lock_area(unsigned long start, unsigned long end)
+{
+	int i, ctx;
+
+	while (start < end) {
+		for (i = 0; i < invalid_segment; i++)
+			if (!mmu_entry_pool[i].locked)
+				break;
+		mmu_entry_pool[i].locked = 1;
+		sun4c_init_clean_segmap(i);
+		for (ctx = 0; ctx < num_contexts; ctx++)
+			prom_putsegment(ctx, start, mmu_entry_pool[i].pseg);
+		start += SUN4C_REAL_PGDIR_SIZE;
+	}
+}
+
+/* Don't change this struct without changing entry.S. This is used
+ * in the in-window kernel fault handler, and you don't want to mess
+ * with that. (See sun4c_fault in entry.S).
+ */
+struct sun4c_mmu_ring {
+	struct sun4c_mmu_entry ringhd;
+	int num_entries;
+};
+
+static struct sun4c_mmu_ring sun4c_context_ring[SUN4C_MAX_CONTEXTS]; /* used user entries */
+static struct sun4c_mmu_ring sun4c_ufree_ring;       /* free user entries */
+static struct sun4c_mmu_ring sun4c_ulru_ring;	     /* LRU user entries */
+struct sun4c_mmu_ring sun4c_kernel_ring;      /* used kernel entries */
+struct sun4c_mmu_ring sun4c_kfree_ring;       /* free kernel entries */
+
+static inline void sun4c_init_rings(void)
+{
+	int i;
+
+	for (i = 0; i < SUN4C_MAX_CONTEXTS; i++) {
+		sun4c_context_ring[i].ringhd.next =
+			sun4c_context_ring[i].ringhd.prev =
+			&sun4c_context_ring[i].ringhd;
+		sun4c_context_ring[i].num_entries = 0;
+	}
+	sun4c_ufree_ring.ringhd.next = sun4c_ufree_ring.ringhd.prev =
+		&sun4c_ufree_ring.ringhd;
+	sun4c_ufree_ring.num_entries = 0;
+	sun4c_ulru_ring.ringhd.lru_next = sun4c_ulru_ring.ringhd.lru_prev =
+		&sun4c_ulru_ring.ringhd;
+	sun4c_ulru_ring.num_entries = 0;
+	sun4c_kernel_ring.ringhd.next = sun4c_kernel_ring.ringhd.prev =
+		&sun4c_kernel_ring.ringhd;
+	sun4c_kernel_ring.num_entries = 0;
+	sun4c_kfree_ring.ringhd.next = sun4c_kfree_ring.ringhd.prev =
+		&sun4c_kfree_ring.ringhd;
+	sun4c_kfree_ring.num_entries = 0;
+}
+
+static void add_ring(struct sun4c_mmu_ring *ring,
+		     struct sun4c_mmu_entry *entry)
+{
+	struct sun4c_mmu_entry *head = &ring->ringhd;
+
+	entry->prev = head;
+	(entry->next = head->next)->prev = entry;
+	head->next = entry;
+	ring->num_entries++;
+}
+
+static __inline__ void add_lru(struct sun4c_mmu_entry *entry)
+{
+	struct sun4c_mmu_ring *ring = &sun4c_ulru_ring;
+	struct sun4c_mmu_entry *head = &ring->ringhd;
+
+	entry->lru_next = head;
+	(entry->lru_prev = head->lru_prev)->lru_next = entry;
+	head->lru_prev = entry;
+}
+
+static void add_ring_ordered(struct sun4c_mmu_ring *ring,
+			     struct sun4c_mmu_entry *entry)
+{
+	struct sun4c_mmu_entry *head = &ring->ringhd;
+	unsigned long addr = entry->vaddr;
+
+	while ((head->next != &ring->ringhd) && (head->next->vaddr < addr))
+		head = head->next;
+
+	entry->prev = head;
+	(entry->next = head->next)->prev = entry;
+	head->next = entry;
+	ring->num_entries++;
+
+	add_lru(entry);
+}
+
+static __inline__ void remove_ring(struct sun4c_mmu_ring *ring,
+				   struct sun4c_mmu_entry *entry)
+{
+	struct sun4c_mmu_entry *next = entry->next;
+
+	(next->prev = entry->prev)->next = next;
+	ring->num_entries--;
+}
+
+static void remove_lru(struct sun4c_mmu_entry *entry)
+{
+	struct sun4c_mmu_entry *next = entry->lru_next;
+
+	(next->lru_prev = entry->lru_prev)->lru_next = next;
+}
+
+static void free_user_entry(int ctx, struct sun4c_mmu_entry *entry)
+{
+        remove_ring(sun4c_context_ring+ctx, entry);
+	remove_lru(entry);
+        add_ring(&sun4c_ufree_ring, entry);
+}
+
+static void free_kernel_entry(struct sun4c_mmu_entry *entry,
+			      struct sun4c_mmu_ring *ring)
+{
+        remove_ring(ring, entry);
+        add_ring(&sun4c_kfree_ring, entry);
+}
+
+static void __init sun4c_init_fill_kernel_ring(int howmany)
+{
+	int i;
+
+	while (howmany) {
+		for (i = 0; i < invalid_segment; i++)
+			if (!mmu_entry_pool[i].locked)
+				break;
+		mmu_entry_pool[i].locked = 1;
+		sun4c_init_clean_segmap(i);
+		add_ring(&sun4c_kfree_ring, &mmu_entry_pool[i]);
+		howmany--;
+	}
+}
+
+static void __init sun4c_init_fill_user_ring(void)
+{
+	int i;
+
+	for (i = 0; i < invalid_segment; i++) {
+		if (mmu_entry_pool[i].locked)
+			continue;
+		sun4c_init_clean_segmap(i);
+		add_ring(&sun4c_ufree_ring, &mmu_entry_pool[i]);
+	}
+}
+
+static void sun4c_kernel_unmap(struct sun4c_mmu_entry *kentry)
+{
+	int savectx, ctx;
+
+	savectx = sun4c_get_context();
+	for (ctx = 0; ctx < num_contexts; ctx++) {
+		sun4c_set_context(ctx);
+		sun4c_put_segmap(kentry->vaddr, invalid_segment);
+	}
+	sun4c_set_context(savectx);
+}
+
+static void sun4c_kernel_map(struct sun4c_mmu_entry *kentry)
+{
+	int savectx, ctx;
+
+	savectx = sun4c_get_context();
+	for (ctx = 0; ctx < num_contexts; ctx++) {
+		sun4c_set_context(ctx);
+		sun4c_put_segmap(kentry->vaddr, kentry->pseg);
+	}
+	sun4c_set_context(savectx);
+}
+
+#define sun4c_user_unmap(__entry) \
+	sun4c_put_segmap((__entry)->vaddr, invalid_segment)
+
+static void sun4c_demap_context(struct sun4c_mmu_ring *crp, unsigned char ctx)
+{
+	struct sun4c_mmu_entry *head = &crp->ringhd;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (head->next != head) {
+		struct sun4c_mmu_entry *entry = head->next;
+		int savectx = sun4c_get_context();
+
+		flush_user_windows();
+		sun4c_set_context(ctx);
+		sun4c_flush_context();
+		do {
+			struct sun4c_mmu_entry *next = entry->next;
+
+			sun4c_user_unmap(entry);
+			free_user_entry(ctx, entry);
+
+			entry = next;
+		} while (entry != head);
+		sun4c_set_context(savectx);
+	}
+	local_irq_restore(flags);
+}
+
+static int sun4c_user_taken_entries;  /* This is how much we have.             */
+static int max_user_taken_entries;    /* This limits us and prevents deadlock. */
+
+static struct sun4c_mmu_entry *sun4c_kernel_strategy(void)
+{
+	struct sun4c_mmu_entry *this_entry;
+
+	/* If some are free, return first one. */
+	if (sun4c_kfree_ring.num_entries) {
+		this_entry = sun4c_kfree_ring.ringhd.next;
+		return this_entry;
+	}
+
+	/* Else free one up. */
+	this_entry = sun4c_kernel_ring.ringhd.prev;
+	sun4c_flush_segment(this_entry->vaddr);
+	sun4c_kernel_unmap(this_entry);
+	free_kernel_entry(this_entry, &sun4c_kernel_ring);
+	this_entry = sun4c_kfree_ring.ringhd.next;
+
+	return this_entry;
+}
+
+/* Using this method to free up mmu entries eliminates a lot of
+ * potential races since we have a kernel that incurs tlb
+ * replacement faults.  There may be performance penalties.
+ *
+ * NOTE: Must be called with interrupts disabled.
+ */
+static struct sun4c_mmu_entry *sun4c_user_strategy(void)
+{
+	struct sun4c_mmu_entry *entry;
+	unsigned char ctx;
+	int savectx;
+
+	/* If some are free, return first one. */
+	if (sun4c_ufree_ring.num_entries) {
+		entry = sun4c_ufree_ring.ringhd.next;
+		goto unlink_out;
+	}
+
+	if (sun4c_user_taken_entries) {
+		entry = sun4c_kernel_strategy();
+		sun4c_user_taken_entries--;
+		goto kunlink_out;
+	}
+
+	/* Grab from the beginning of the LRU list. */
+	entry = sun4c_ulru_ring.ringhd.lru_next;
+	ctx = entry->ctx;
+
+	savectx = sun4c_get_context();
+	flush_user_windows();
+	sun4c_set_context(ctx);
+	sun4c_flush_segment(entry->vaddr);
+	sun4c_user_unmap(entry);
+	remove_ring(sun4c_context_ring + ctx, entry);
+	remove_lru(entry);
+	sun4c_set_context(savectx);
+
+	return entry;
+
+unlink_out:
+	remove_ring(&sun4c_ufree_ring, entry);
+	return entry;
+kunlink_out:
+	remove_ring(&sun4c_kfree_ring, entry);
+	return entry;
+}
+
+/* NOTE: Must be called with interrupts disabled. */
+void sun4c_grow_kernel_ring(void)
+{
+	struct sun4c_mmu_entry *entry;
+
+	/* Prevent deadlock condition. */
+	if (sun4c_user_taken_entries >= max_user_taken_entries)
+		return;
+
+	if (sun4c_ufree_ring.num_entries) {
+		entry = sun4c_ufree_ring.ringhd.next;
+        	remove_ring(&sun4c_ufree_ring, entry);
+		add_ring(&sun4c_kfree_ring, entry);
+		sun4c_user_taken_entries++;
+	}
+}
+
+/* 2 page buckets for task struct and kernel stack allocation.
+ *
+ * TASK_STACK_BEGIN
+ * bucket[0]
+ * bucket[1]
+ *   [ ... ]
+ * bucket[NR_TASK_BUCKETS-1]
+ * TASK_STACK_BEGIN + (sizeof(struct task_bucket) * NR_TASK_BUCKETS)
+ *
+ * Each slot looks like:
+ *
+ *  page 1 --  task struct + beginning of kernel stack
+ *  page 2 --  rest of kernel stack
+ */
+
+union task_union *sun4c_bucket[NR_TASK_BUCKETS];
+
+static int sun4c_lowbucket_avail;
+
+#define BUCKET_EMPTY     ((union task_union *) 0)
+#define BUCKET_SHIFT     (PAGE_SHIFT + 1)        /* log2(sizeof(struct task_bucket)) */
+#define BUCKET_SIZE      (1 << BUCKET_SHIFT)
+#define BUCKET_NUM(addr) ((((addr) - SUN4C_LOCK_VADDR) >> BUCKET_SHIFT))
+#define BUCKET_ADDR(num) (((num) << BUCKET_SHIFT) + SUN4C_LOCK_VADDR)
+#define BUCKET_PTE(page)       \
+        ((((page) - PAGE_OFFSET) >> PAGE_SHIFT) | pgprot_val(SUN4C_PAGE_KERNEL))
+#define BUCKET_PTE_PAGE(pte)   \
+        (PAGE_OFFSET + (((pte) & SUN4C_PFN_MASK) << PAGE_SHIFT))
+
+static void get_locked_segment(unsigned long addr)
+{
+	struct sun4c_mmu_entry *stolen;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	addr &= SUN4C_REAL_PGDIR_MASK;
+	stolen = sun4c_user_strategy();
+	max_user_taken_entries--;
+	stolen->vaddr = addr;
+	flush_user_windows();
+	sun4c_kernel_map(stolen);
+	local_irq_restore(flags);
+}
+
+static void free_locked_segment(unsigned long addr)
+{
+	struct sun4c_mmu_entry *entry;
+	unsigned long flags;
+	unsigned char pseg;
+
+	local_irq_save(flags);
+	addr &= SUN4C_REAL_PGDIR_MASK;
+	pseg = sun4c_get_segmap(addr);
+	entry = &mmu_entry_pool[pseg];
+
+	flush_user_windows();
+	sun4c_flush_segment(addr);
+	sun4c_kernel_unmap(entry);
+	add_ring(&sun4c_ufree_ring, entry);
+	max_user_taken_entries++;
+	local_irq_restore(flags);
+}
+
+static inline void garbage_collect(int entry)
+{
+	int start, end;
+
+	/* 32 buckets per segment... */
+	entry &= ~31;
+	start = entry;
+	for (end = (start + 32); start < end; start++)
+		if (sun4c_bucket[start] != BUCKET_EMPTY)
+			return;
+
+	/* Entire segment empty, release it. */
+	free_locked_segment(BUCKET_ADDR(entry));
+}
+
+static struct thread_info *sun4c_alloc_thread_info(void)
+{
+	unsigned long addr, pages;
+	int entry;
+
+	pages = __get_free_pages(GFP_KERNEL, THREAD_INFO_ORDER);
+	if (!pages)
+		return NULL;
+
+	for (entry = sun4c_lowbucket_avail; entry < NR_TASK_BUCKETS; entry++)
+		if (sun4c_bucket[entry] == BUCKET_EMPTY)
+			break;
+	if (entry == NR_TASK_BUCKETS) {
+		free_pages(pages, THREAD_INFO_ORDER);
+		return NULL;
+	}
+	if (entry >= sun4c_lowbucket_avail)
+		sun4c_lowbucket_avail = entry + 1;
+
+	addr = BUCKET_ADDR(entry);
+	sun4c_bucket[entry] = (union task_union *) addr;
+	if(sun4c_get_segmap(addr) == invalid_segment)
+		get_locked_segment(addr);
+
+	/* We are changing the virtual color of the page(s)
+	 * so we must flush the cache to guarantee consistency.
+	 */
+	sun4c_flush_page(pages);
+#ifndef CONFIG_SUN4	
+	sun4c_flush_page(pages + PAGE_SIZE);
+#endif
+
+	sun4c_put_pte(addr, BUCKET_PTE(pages));
+#ifndef CONFIG_SUN4	
+	sun4c_put_pte(addr + PAGE_SIZE, BUCKET_PTE(pages + PAGE_SIZE));
+#endif
+
+#ifdef CONFIG_DEBUG_STACK_USAGE
+	memset((void *)addr, 0, PAGE_SIZE << THREAD_INFO_ORDER);
+#endif /* DEBUG_STACK_USAGE */
+
+	return (struct thread_info *) addr;
+}
+
+static void sun4c_free_thread_info(struct thread_info *ti)
+{
+	unsigned long tiaddr = (unsigned long) ti;
+	unsigned long pages = BUCKET_PTE_PAGE(sun4c_get_pte(tiaddr));
+	int entry = BUCKET_NUM(tiaddr);
+
+	/* We are deleting a mapping, so the flush here is mandatory. */
+	sun4c_flush_page(tiaddr);
+#ifndef CONFIG_SUN4	
+	sun4c_flush_page(tiaddr + PAGE_SIZE);
+#endif
+	sun4c_put_pte(tiaddr, 0);
+#ifndef CONFIG_SUN4	
+	sun4c_put_pte(tiaddr + PAGE_SIZE, 0);
+#endif
+	sun4c_bucket[entry] = BUCKET_EMPTY;
+	if (entry < sun4c_lowbucket_avail)
+		sun4c_lowbucket_avail = entry;
+
+	free_pages(pages, THREAD_INFO_ORDER);
+	garbage_collect(entry);
+}
+
+static void __init sun4c_init_buckets(void)
+{
+	int entry;
+
+	if (sizeof(union thread_union) != (PAGE_SIZE << THREAD_INFO_ORDER)) {
+		extern void thread_info_size_is_bolixed_pete(void);
+		thread_info_size_is_bolixed_pete();
+	}
+
+	for (entry = 0; entry < NR_TASK_BUCKETS; entry++)
+		sun4c_bucket[entry] = BUCKET_EMPTY;
+	sun4c_lowbucket_avail = 0;
+}
+
+static unsigned long sun4c_iobuffer_start;
+static unsigned long sun4c_iobuffer_end;
+static unsigned long sun4c_iobuffer_high;
+static unsigned long *sun4c_iobuffer_map;
+static int iobuffer_map_size;
+
+/*
+ * Alias our pages so they do not cause a trap.
+ * Also one page may be aliased into several I/O areas and we may
+ * finish these I/O separately.
+ */
+static char *sun4c_lockarea(char *vaddr, unsigned long size)
+{
+	unsigned long base, scan;
+	unsigned long npages;
+	unsigned long vpage;
+	unsigned long pte;
+	unsigned long apage;
+	unsigned long high;
+	unsigned long flags;
+
+	npages = (((unsigned long)vaddr & ~PAGE_MASK) +
+		  size + (PAGE_SIZE-1)) >> PAGE_SHIFT;
+
+	scan = 0;
+	local_irq_save(flags);
+	for (;;) {
+		scan = find_next_zero_bit(sun4c_iobuffer_map,
+					  iobuffer_map_size, scan);
+		if ((base = scan) + npages > iobuffer_map_size) goto abend;
+		for (;;) {
+			if (scan >= base + npages) goto found;
+			if (test_bit(scan, sun4c_iobuffer_map)) break;
+			scan++;
+		}
+	}
+
+found:
+	high = ((base + npages) << PAGE_SHIFT) + sun4c_iobuffer_start;
+	high = SUN4C_REAL_PGDIR_ALIGN(high);
+	while (high > sun4c_iobuffer_high) {
+		get_locked_segment(sun4c_iobuffer_high);
+		sun4c_iobuffer_high += SUN4C_REAL_PGDIR_SIZE;
+	}
+
+	vpage = ((unsigned long) vaddr) & PAGE_MASK;
+	for (scan = base; scan < base+npages; scan++) {
+		pte = ((vpage-PAGE_OFFSET) >> PAGE_SHIFT);
+ 		pte |= pgprot_val(SUN4C_PAGE_KERNEL);
+		pte |= _SUN4C_PAGE_NOCACHE;
+		set_bit(scan, sun4c_iobuffer_map);
+		apage = (scan << PAGE_SHIFT) + sun4c_iobuffer_start;
+
+		/* Flush original mapping so we see the right things later. */
+		sun4c_flush_page(vpage);
+
+		sun4c_put_pte(apage, pte);
+		vpage += PAGE_SIZE;
+	}
+	local_irq_restore(flags);
+	return (char *) ((base << PAGE_SHIFT) + sun4c_iobuffer_start +
+			 (((unsigned long) vaddr) & ~PAGE_MASK));
+
+abend:
+	local_irq_restore(flags);
+	printk("DMA vaddr=0x%p size=%08lx\n", vaddr, size);
+	panic("Out of iobuffer table");
+	return NULL;
+}
+
+static void sun4c_unlockarea(char *vaddr, unsigned long size)
+{
+	unsigned long vpage, npages;
+	unsigned long flags;
+	int scan, high;
+
+	vpage = (unsigned long)vaddr & PAGE_MASK;
+	npages = (((unsigned long)vaddr & ~PAGE_MASK) +
+		  size + (PAGE_SIZE-1)) >> PAGE_SHIFT;
+
+	local_irq_save(flags);
+	while (npages != 0) {
+		--npages;
+
+		/* This mapping is marked non-cachable, no flush necessary. */
+		sun4c_put_pte(vpage, 0);
+		clear_bit((vpage - sun4c_iobuffer_start) >> PAGE_SHIFT,
+			  sun4c_iobuffer_map);
+		vpage += PAGE_SIZE;
+	}
+
+	/* garbage collect */
+	scan = (sun4c_iobuffer_high - sun4c_iobuffer_start) >> PAGE_SHIFT;
+	while (scan >= 0 && !sun4c_iobuffer_map[scan >> 5])
+		scan -= 32;
+	scan += 32;
+	high = sun4c_iobuffer_start + (scan << PAGE_SHIFT);
+	high = SUN4C_REAL_PGDIR_ALIGN(high) + SUN4C_REAL_PGDIR_SIZE;
+	while (high < sun4c_iobuffer_high) {
+		sun4c_iobuffer_high -= SUN4C_REAL_PGDIR_SIZE;
+		free_locked_segment(sun4c_iobuffer_high);
+	}
+	local_irq_restore(flags);
+}
+
+/* Note the scsi code at init time passes to here buffers
+ * which sit on the kernel stack, those are already locked
+ * by implication and fool the page locking code above
+ * if passed to by mistake.
+ */
+static __u32 sun4c_get_scsi_one(char *bufptr, unsigned long len, struct sbus_bus *sbus)
+{
+	unsigned long page;
+
+	page = ((unsigned long)bufptr) & PAGE_MASK;
+	if (!virt_addr_valid(page)) {
+		sun4c_flush_page(page);
+		return (__u32)bufptr; /* already locked */
+	}
+	return (__u32)sun4c_lockarea(bufptr, len);
+}
+
+static void sun4c_get_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus *sbus)
+{
+	while (sz != 0) {
+		--sz;
+		sg[sz].dvma_address = (__u32)sun4c_lockarea(page_address(sg[sz].page) + sg[sz].offset, sg[sz].length);
+		sg[sz].dvma_length = sg[sz].length;
+	}
+}
+
+static void sun4c_release_scsi_one(__u32 bufptr, unsigned long len, struct sbus_bus *sbus)
+{
+	if (bufptr < sun4c_iobuffer_start)
+		return; /* On kernel stack or similar, see above */
+	sun4c_unlockarea((char *)bufptr, len);
+}
+
+static void sun4c_release_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus *sbus)
+{
+	while (sz != 0) {
+		--sz;
+		sun4c_unlockarea((char *)sg[sz].dvma_address, sg[sz].length);
+	}
+}
+
+#define TASK_ENTRY_SIZE    BUCKET_SIZE /* see above */
+#define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1))
+
+struct vm_area_struct sun4c_kstack_vma;
+
+static void __init sun4c_init_lock_areas(void)
+{
+	unsigned long sun4c_taskstack_start;
+	unsigned long sun4c_taskstack_end;
+	int bitmap_size;
+
+	sun4c_init_buckets();
+	sun4c_taskstack_start = SUN4C_LOCK_VADDR;
+	sun4c_taskstack_end = (sun4c_taskstack_start +
+			       (TASK_ENTRY_SIZE * NR_TASK_BUCKETS));
+	if (sun4c_taskstack_end >= SUN4C_LOCK_END) {
+		prom_printf("Too many tasks, decrease NR_TASK_BUCKETS please.\n");
+		prom_halt();
+	}
+
+	sun4c_iobuffer_start = sun4c_iobuffer_high =
+				SUN4C_REAL_PGDIR_ALIGN(sun4c_taskstack_end);
+	sun4c_iobuffer_end = SUN4C_LOCK_END;
+	bitmap_size = (sun4c_iobuffer_end - sun4c_iobuffer_start) >> PAGE_SHIFT;
+	bitmap_size = (bitmap_size + 7) >> 3;
+	bitmap_size = LONG_ALIGN(bitmap_size);
+	iobuffer_map_size = bitmap_size << 3;
+	sun4c_iobuffer_map = __alloc_bootmem(bitmap_size, SMP_CACHE_BYTES, 0UL);
+	memset((void *) sun4c_iobuffer_map, 0, bitmap_size);
+
+	sun4c_kstack_vma.vm_mm = &init_mm;
+	sun4c_kstack_vma.vm_start = sun4c_taskstack_start;
+	sun4c_kstack_vma.vm_end = sun4c_taskstack_end;
+	sun4c_kstack_vma.vm_page_prot = PAGE_SHARED;
+	sun4c_kstack_vma.vm_flags = VM_READ | VM_WRITE | VM_EXEC;
+	insert_vm_struct(&init_mm, &sun4c_kstack_vma);
+}
+
+/* Cache flushing on the sun4c. */
+static void sun4c_flush_cache_all(void)
+{
+	unsigned long begin, end;
+
+	flush_user_windows();
+	begin = (KERNBASE + SUN4C_REAL_PGDIR_SIZE);
+	end = (begin + SUN4C_VAC_SIZE);
+
+	if (sun4c_vacinfo.linesize == 32) {
+		while (begin < end) {
+			__asm__ __volatile__(
+			"ld	[%0 + 0x00], %%g0\n\t"
+			"ld	[%0 + 0x20], %%g0\n\t"
+			"ld	[%0 + 0x40], %%g0\n\t"
+			"ld	[%0 + 0x60], %%g0\n\t"
+			"ld	[%0 + 0x80], %%g0\n\t"
+			"ld	[%0 + 0xa0], %%g0\n\t"
+			"ld	[%0 + 0xc0], %%g0\n\t"
+			"ld	[%0 + 0xe0], %%g0\n\t"
+			"ld	[%0 + 0x100], %%g0\n\t"
+			"ld	[%0 + 0x120], %%g0\n\t"
+			"ld	[%0 + 0x140], %%g0\n\t"
+			"ld	[%0 + 0x160], %%g0\n\t"
+			"ld	[%0 + 0x180], %%g0\n\t"
+			"ld	[%0 + 0x1a0], %%g0\n\t"
+			"ld	[%0 + 0x1c0], %%g0\n\t"
+			"ld	[%0 + 0x1e0], %%g0\n"
+			: : "r" (begin));
+			begin += 512;
+		}
+	} else {
+		while (begin < end) {
+			__asm__ __volatile__(
+			"ld	[%0 + 0x00], %%g0\n\t"
+			"ld	[%0 + 0x10], %%g0\n\t"
+			"ld	[%0 + 0x20], %%g0\n\t"
+			"ld	[%0 + 0x30], %%g0\n\t"
+			"ld	[%0 + 0x40], %%g0\n\t"
+			"ld	[%0 + 0x50], %%g0\n\t"
+			"ld	[%0 + 0x60], %%g0\n\t"
+			"ld	[%0 + 0x70], %%g0\n\t"
+			"ld	[%0 + 0x80], %%g0\n\t"
+			"ld	[%0 + 0x90], %%g0\n\t"
+			"ld	[%0 + 0xa0], %%g0\n\t"
+			"ld	[%0 + 0xb0], %%g0\n\t"
+			"ld	[%0 + 0xc0], %%g0\n\t"
+			"ld	[%0 + 0xd0], %%g0\n\t"
+			"ld	[%0 + 0xe0], %%g0\n\t"
+			"ld	[%0 + 0xf0], %%g0\n"
+			: : "r" (begin));
+			begin += 256;
+		}
+	}
+}
+
+static void sun4c_flush_cache_mm(struct mm_struct *mm)
+{
+	int new_ctx = mm->context;
+
+	if (new_ctx != NO_CONTEXT) {
+		flush_user_windows();
+
+		if (sun4c_context_ring[new_ctx].num_entries) {
+			struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd;
+			unsigned long flags;
+
+			local_irq_save(flags);
+			if (head->next != head) {
+				struct sun4c_mmu_entry *entry = head->next;
+				int savectx = sun4c_get_context();
+
+				sun4c_set_context(new_ctx);
+				sun4c_flush_context();
+				do {
+					struct sun4c_mmu_entry *next = entry->next;
+
+					sun4c_user_unmap(entry);
+					free_user_entry(new_ctx, entry);
+
+					entry = next;
+				} while (entry != head);
+				sun4c_set_context(savectx);
+			}
+			local_irq_restore(flags);
+		}
+	}
+}
+
+static void sun4c_flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
+{
+	struct mm_struct *mm = vma->vm_mm;
+	int new_ctx = mm->context;
+
+	if (new_ctx != NO_CONTEXT) {
+		struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd;
+		struct sun4c_mmu_entry *entry;
+		unsigned long flags;
+
+		flush_user_windows();
+
+		local_irq_save(flags);
+		/* All user segmap chains are ordered on entry->vaddr. */
+		for (entry = head->next;
+		     (entry != head) && ((entry->vaddr+SUN4C_REAL_PGDIR_SIZE) < start);
+		     entry = entry->next)
+			;
+
+		/* Tracing various job mixtures showed that this conditional
+		 * only passes ~35% of the time for most worse case situations,
+		 * therefore we avoid all of this gross overhead ~65% of the time.
+		 */
+		if ((entry != head) && (entry->vaddr < end)) {
+			int octx = sun4c_get_context();
+			sun4c_set_context(new_ctx);
+
+			/* At this point, always, (start >= entry->vaddr) and
+			 * (entry->vaddr < end), once the latter condition
+			 * ceases to hold, or we hit the end of the list, we
+			 * exit the loop.  The ordering of all user allocated
+			 * segmaps makes this all work out so beautifully.
+			 */
+			do {
+				struct sun4c_mmu_entry *next = entry->next;
+				unsigned long realend;
+
+				/* "realstart" is always >= entry->vaddr */
+				realend = entry->vaddr + SUN4C_REAL_PGDIR_SIZE;
+				if (end < realend)
+					realend = end;
+				if ((realend - entry->vaddr) <= (PAGE_SIZE << 3)) {
+					unsigned long page = entry->vaddr;
+					while (page < realend) {
+						sun4c_flush_page(page);
+						page += PAGE_SIZE;
+					}
+				} else {
+					sun4c_flush_segment(entry->vaddr);
+					sun4c_user_unmap(entry);
+					free_user_entry(new_ctx, entry);
+				}
+				entry = next;
+			} while ((entry != head) && (entry->vaddr < end));
+			sun4c_set_context(octx);
+		}
+		local_irq_restore(flags);
+	}
+}
+
+static void sun4c_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
+{
+	struct mm_struct *mm = vma->vm_mm;
+	int new_ctx = mm->context;
+
+	/* Sun4c has no separate I/D caches so cannot optimize for non
+	 * text page flushes.
+	 */
+	if (new_ctx != NO_CONTEXT) {
+		int octx = sun4c_get_context();
+		unsigned long flags;
+
+		flush_user_windows();
+		local_irq_save(flags);
+		sun4c_set_context(new_ctx);
+		sun4c_flush_page(page);
+		sun4c_set_context(octx);
+		local_irq_restore(flags);
+	}
+}
+
+static void sun4c_flush_page_to_ram(unsigned long page)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	sun4c_flush_page(page);
+	local_irq_restore(flags);
+}
+
+/* Sun4c cache is unified, both instructions and data live there, so
+ * no need to flush the on-stack instructions for new signal handlers.
+ */
+static void sun4c_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr)
+{
+}
+
+/* TLB flushing on the sun4c.  These routines count on the cache
+ * flushing code to flush the user register windows so that we need
+ * not do so when we get here.
+ */
+
+static void sun4c_flush_tlb_all(void)
+{
+	struct sun4c_mmu_entry *this_entry, *next_entry;
+	unsigned long flags;
+	int savectx, ctx;
+
+	local_irq_save(flags);
+	this_entry = sun4c_kernel_ring.ringhd.next;
+	savectx = sun4c_get_context();
+	flush_user_windows();
+	while (sun4c_kernel_ring.num_entries) {
+		next_entry = this_entry->next;
+		sun4c_flush_segment(this_entry->vaddr);
+		for (ctx = 0; ctx < num_contexts; ctx++) {
+			sun4c_set_context(ctx);
+			sun4c_put_segmap(this_entry->vaddr, invalid_segment);
+		}
+		free_kernel_entry(this_entry, &sun4c_kernel_ring);
+		this_entry = next_entry;
+	}
+	sun4c_set_context(savectx);
+	local_irq_restore(flags);
+}
+
+static void sun4c_flush_tlb_mm(struct mm_struct *mm)
+{
+	int new_ctx = mm->context;
+
+	if (new_ctx != NO_CONTEXT) {
+		struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd;
+		unsigned long flags;
+
+		local_irq_save(flags);
+		if (head->next != head) {
+			struct sun4c_mmu_entry *entry = head->next;
+			int savectx = sun4c_get_context();
+
+			sun4c_set_context(new_ctx);
+			sun4c_flush_context();
+			do {
+				struct sun4c_mmu_entry *next = entry->next;
+
+				sun4c_user_unmap(entry);
+				free_user_entry(new_ctx, entry);
+
+				entry = next;
+			} while (entry != head);
+			sun4c_set_context(savectx);
+		}
+		local_irq_restore(flags);
+	}
+}
+
+static void sun4c_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
+{
+	struct mm_struct *mm = vma->vm_mm;
+	int new_ctx = mm->context;
+
+	if (new_ctx != NO_CONTEXT) {
+		struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd;
+		struct sun4c_mmu_entry *entry;
+		unsigned long flags;
+
+		local_irq_save(flags);
+		/* See commentary in sun4c_flush_cache_range(). */
+		for (entry = head->next;
+		     (entry != head) && ((entry->vaddr+SUN4C_REAL_PGDIR_SIZE) < start);
+		     entry = entry->next)
+			;
+
+		if ((entry != head) && (entry->vaddr < end)) {
+			int octx = sun4c_get_context();
+
+			sun4c_set_context(new_ctx);
+			do {
+				struct sun4c_mmu_entry *next = entry->next;
+
+				sun4c_flush_segment(entry->vaddr);
+				sun4c_user_unmap(entry);
+				free_user_entry(new_ctx, entry);
+
+				entry = next;
+			} while ((entry != head) && (entry->vaddr < end));
+			sun4c_set_context(octx);
+		}
+		local_irq_restore(flags);
+	}
+}
+
+static void sun4c_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+{
+	struct mm_struct *mm = vma->vm_mm;
+	int new_ctx = mm->context;
+
+	if (new_ctx != NO_CONTEXT) {
+		int savectx = sun4c_get_context();
+		unsigned long flags;
+
+		local_irq_save(flags);
+		sun4c_set_context(new_ctx);
+		page &= PAGE_MASK;
+		sun4c_flush_page(page);
+		sun4c_put_pte(page, 0);
+		sun4c_set_context(savectx);
+		local_irq_restore(flags);
+	}
+}
+
+static inline void sun4c_mapioaddr(unsigned long physaddr, unsigned long virt_addr)
+{
+	unsigned long page_entry;
+
+	page_entry = ((physaddr >> PAGE_SHIFT) & SUN4C_PFN_MASK);
+	page_entry |= ((pg_iobits | _SUN4C_PAGE_PRIV) & ~(_SUN4C_PAGE_PRESENT));
+	sun4c_put_pte(virt_addr, page_entry);
+}
+
+static void sun4c_mapiorange(unsigned int bus, unsigned long xpa,
+    unsigned long xva, unsigned int len)
+{
+	while (len != 0) {
+		len -= PAGE_SIZE;
+		sun4c_mapioaddr(xpa, xva);
+		xva += PAGE_SIZE;
+		xpa += PAGE_SIZE;
+	}
+}
+
+static void sun4c_unmapiorange(unsigned long virt_addr, unsigned int len)
+{
+	while (len != 0) {
+		len -= PAGE_SIZE;
+		sun4c_put_pte(virt_addr, 0);
+		virt_addr += PAGE_SIZE;
+	}
+}
+
+static void sun4c_alloc_context(struct mm_struct *old_mm, struct mm_struct *mm)
+{
+	struct ctx_list *ctxp;
+
+	ctxp = ctx_free.next;
+	if (ctxp != &ctx_free) {
+		remove_from_ctx_list(ctxp);
+		add_to_used_ctxlist(ctxp);
+		mm->context = ctxp->ctx_number;
+		ctxp->ctx_mm = mm;
+		return;
+	}
+	ctxp = ctx_used.next;
+	if (ctxp->ctx_mm == old_mm)
+		ctxp = ctxp->next;
+	remove_from_ctx_list(ctxp);
+	add_to_used_ctxlist(ctxp);
+	ctxp->ctx_mm->context = NO_CONTEXT;
+	ctxp->ctx_mm = mm;
+	mm->context = ctxp->ctx_number;
+	sun4c_demap_context(&sun4c_context_ring[ctxp->ctx_number],
+			       ctxp->ctx_number);
+}
+
+/* Switch the current MM context. */
+static void sun4c_switch_mm(struct mm_struct *old_mm, struct mm_struct *mm, struct task_struct *tsk, int cpu)
+{
+	struct ctx_list *ctx;
+	int dirty = 0;
+
+	if (mm->context == NO_CONTEXT) {
+		dirty = 1;
+		sun4c_alloc_context(old_mm, mm);
+	} else {
+		/* Update the LRU ring of contexts. */
+		ctx = ctx_list_pool + mm->context;
+		remove_from_ctx_list(ctx);
+		add_to_used_ctxlist(ctx);
+	}
+	if (dirty || old_mm != mm)
+		sun4c_set_context(mm->context);
+}
+
+static void sun4c_destroy_context(struct mm_struct *mm)
+{
+	struct ctx_list *ctx_old;
+
+	if (mm->context != NO_CONTEXT) {
+		sun4c_demap_context(&sun4c_context_ring[mm->context], mm->context);
+		ctx_old = ctx_list_pool + mm->context;
+		remove_from_ctx_list(ctx_old);
+		add_to_free_ctxlist(ctx_old);
+		mm->context = NO_CONTEXT;
+	}
+}
+
+static void sun4c_mmu_info(struct seq_file *m)
+{
+	int used_user_entries, i;
+
+	used_user_entries = 0;
+	for (i = 0; i < num_contexts; i++)
+		used_user_entries += sun4c_context_ring[i].num_entries;
+
+	seq_printf(m, 
+		   "vacsize\t\t: %d bytes\n"
+		   "vachwflush\t: %s\n"
+		   "vaclinesize\t: %d bytes\n"
+		   "mmuctxs\t\t: %d\n"
+		   "mmupsegs\t: %d\n"
+		   "kernelpsegs\t: %d\n"
+		   "kfreepsegs\t: %d\n"
+		   "usedpsegs\t: %d\n"
+		   "ufreepsegs\t: %d\n"
+		   "user_taken\t: %d\n"
+		   "max_taken\t: %d\n",
+		   sun4c_vacinfo.num_bytes,
+		   (sun4c_vacinfo.do_hwflushes ? "yes" : "no"),
+		   sun4c_vacinfo.linesize,
+		   num_contexts,
+		   (invalid_segment + 1),
+		   sun4c_kernel_ring.num_entries,
+		   sun4c_kfree_ring.num_entries,
+		   used_user_entries,
+		   sun4c_ufree_ring.num_entries,
+		   sun4c_user_taken_entries,
+		   max_user_taken_entries);
+}
+
+/* Nothing below here should touch the mmu hardware nor the mmu_entry
+ * data structures.
+ */
+
+/* First the functions which the mid-level code uses to directly
+ * manipulate the software page tables.  Some defines since we are
+ * emulating the i386 page directory layout.
+ */
+#define PGD_PRESENT  0x001
+#define PGD_RW       0x002
+#define PGD_USER     0x004
+#define PGD_ACCESSED 0x020
+#define PGD_DIRTY    0x040
+#define PGD_TABLE    (PGD_PRESENT | PGD_RW | PGD_USER | PGD_ACCESSED | PGD_DIRTY)
+
+static void sun4c_set_pte(pte_t *ptep, pte_t pte)
+{
+	*ptep = pte;
+}
+
+static void sun4c_pgd_set(pgd_t * pgdp, pmd_t * pmdp)
+{
+}
+
+static void sun4c_pmd_set(pmd_t * pmdp, pte_t * ptep)
+{
+	pmdp->pmdv[0] = PGD_TABLE | (unsigned long) ptep;
+}
+
+static void sun4c_pmd_populate(pmd_t * pmdp, struct page * ptep)
+{
+	if (page_address(ptep) == NULL) BUG();	/* No highmem on sun4c */
+	pmdp->pmdv[0] = PGD_TABLE | (unsigned long) page_address(ptep);
+}
+
+static int sun4c_pte_present(pte_t pte)
+{
+	return ((pte_val(pte) & (_SUN4C_PAGE_PRESENT | _SUN4C_PAGE_PRIV)) != 0);
+}
+static void sun4c_pte_clear(pte_t *ptep)	{ *ptep = __pte(0); }
+
+static int sun4c_pte_read(pte_t pte)
+{
+	return (pte_val(pte) & _SUN4C_PAGE_READ);
+}
+
+static int sun4c_pmd_bad(pmd_t pmd)
+{
+	return (((pmd_val(pmd) & ~PAGE_MASK) != PGD_TABLE) ||
+		(!virt_addr_valid(pmd_val(pmd))));
+}
+
+static int sun4c_pmd_present(pmd_t pmd)
+{
+	return ((pmd_val(pmd) & PGD_PRESENT) != 0);
+}
+
+#if 0 /* if PMD takes one word */
+static void sun4c_pmd_clear(pmd_t *pmdp)	{ *pmdp = __pmd(0); }
+#else /* if pmd_t is a longish aggregate */
+static void sun4c_pmd_clear(pmd_t *pmdp) {
+	memset((void *)pmdp, 0, sizeof(pmd_t));
+}
+#endif
+
+static int sun4c_pgd_none(pgd_t pgd)		{ return 0; }
+static int sun4c_pgd_bad(pgd_t pgd)		{ return 0; }
+static int sun4c_pgd_present(pgd_t pgd)	        { return 1; }
+static void sun4c_pgd_clear(pgd_t * pgdp)	{ }
+
+/*
+ * The following only work if pte_present() is true.
+ * Undefined behaviour if not..
+ */
+static pte_t sun4c_pte_mkwrite(pte_t pte)
+{
+	pte = __pte(pte_val(pte) | _SUN4C_PAGE_WRITE);
+	if (pte_val(pte) & _SUN4C_PAGE_MODIFIED)
+		pte = __pte(pte_val(pte) | _SUN4C_PAGE_SILENT_WRITE);
+	return pte;
+}
+
+static pte_t sun4c_pte_mkdirty(pte_t pte)
+{
+	pte = __pte(pte_val(pte) | _SUN4C_PAGE_MODIFIED);
+	if (pte_val(pte) & _SUN4C_PAGE_WRITE)
+		pte = __pte(pte_val(pte) | _SUN4C_PAGE_SILENT_WRITE);
+	return pte;
+}
+
+static pte_t sun4c_pte_mkyoung(pte_t pte)
+{
+	pte = __pte(pte_val(pte) | _SUN4C_PAGE_ACCESSED);
+	if (pte_val(pte) & _SUN4C_PAGE_READ)
+		pte = __pte(pte_val(pte) | _SUN4C_PAGE_SILENT_READ);
+	return pte;
+}
+
+/*
+ * Conversion functions: convert a page and protection to a page entry,
+ * and a page entry and page directory to the page they refer to.
+ */
+static pte_t sun4c_mk_pte(struct page *page, pgprot_t pgprot)
+{
+	return __pte(page_to_pfn(page) | pgprot_val(pgprot));
+}
+
+static pte_t sun4c_mk_pte_phys(unsigned long phys_page, pgprot_t pgprot)
+{
+	return __pte((phys_page >> PAGE_SHIFT) | pgprot_val(pgprot));
+}
+
+static pte_t sun4c_mk_pte_io(unsigned long page, pgprot_t pgprot, int space)
+{
+	return __pte(((page - PAGE_OFFSET) >> PAGE_SHIFT) | pgprot_val(pgprot));
+}
+
+static unsigned long sun4c_pte_pfn(pte_t pte)
+{
+	return pte_val(pte) & SUN4C_PFN_MASK;
+}
+
+static pte_t sun4c_pgoff_to_pte(unsigned long pgoff)
+{
+	return __pte(pgoff | _SUN4C_PAGE_FILE);
+}
+
+static unsigned long sun4c_pte_to_pgoff(pte_t pte)
+{
+	return pte_val(pte) & ((1UL << PTE_FILE_MAX_BITS) - 1);
+}
+
+
+static __inline__ unsigned long sun4c_pmd_page_v(pmd_t pmd)
+{
+	return (pmd_val(pmd) & PAGE_MASK);
+}
+
+static struct page *sun4c_pmd_page(pmd_t pmd)
+{
+	return virt_to_page(sun4c_pmd_page_v(pmd));
+}
+
+static unsigned long sun4c_pgd_page(pgd_t pgd) { return 0; }
+
+/* to find an entry in a page-table-directory */
+static inline pgd_t *sun4c_pgd_offset(struct mm_struct * mm, unsigned long address)
+{
+	return mm->pgd + (address >> SUN4C_PGDIR_SHIFT);
+}
+
+/* Find an entry in the second-level page table.. */
+static pmd_t *sun4c_pmd_offset(pgd_t * dir, unsigned long address)
+{
+	return (pmd_t *) dir;
+}
+
+/* Find an entry in the third-level page table.. */ 
+pte_t *sun4c_pte_offset_kernel(pmd_t * dir, unsigned long address)
+{
+	return (pte_t *) sun4c_pmd_page_v(*dir) +
+			((address >> PAGE_SHIFT) & (SUN4C_PTRS_PER_PTE - 1));
+}
+
+static unsigned long sun4c_swp_type(swp_entry_t entry)
+{
+	return (entry.val & SUN4C_SWP_TYPE_MASK);
+}
+
+static unsigned long sun4c_swp_offset(swp_entry_t entry)
+{
+	return (entry.val >> SUN4C_SWP_OFF_SHIFT) & SUN4C_SWP_OFF_MASK;
+}
+
+static swp_entry_t sun4c_swp_entry(unsigned long type, unsigned long offset)
+{
+	return (swp_entry_t) {
+		  (offset & SUN4C_SWP_OFF_MASK) << SUN4C_SWP_OFF_SHIFT
+		| (type & SUN4C_SWP_TYPE_MASK) };
+}
+
+static void sun4c_free_pte_slow(pte_t *pte)
+{
+	free_page((unsigned long)pte);
+}
+
+static void sun4c_free_pgd_slow(pgd_t *pgd)
+{
+	free_page((unsigned long)pgd);
+}
+
+static pgd_t *sun4c_get_pgd_fast(void)
+{
+	unsigned long *ret;
+
+	if ((ret = pgd_quicklist) != NULL) {
+		pgd_quicklist = (unsigned long *)(*ret);
+		ret[0] = ret[1];
+		pgtable_cache_size--;
+	} else {
+		pgd_t *init;
+		
+		ret = (unsigned long *)__get_free_page(GFP_KERNEL);
+		memset (ret, 0, (KERNBASE / SUN4C_PGDIR_SIZE) * sizeof(pgd_t));
+		init = sun4c_pgd_offset(&init_mm, 0);
+		memcpy (((pgd_t *)ret) + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
+			(PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
+	}
+	return (pgd_t *)ret;
+}
+
+static void sun4c_free_pgd_fast(pgd_t *pgd)
+{
+	*(unsigned long *)pgd = (unsigned long) pgd_quicklist;
+	pgd_quicklist = (unsigned long *) pgd;
+	pgtable_cache_size++;
+}
+
+
+static __inline__ pte_t *
+sun4c_pte_alloc_one_fast(struct mm_struct *mm, unsigned long address)
+{
+	unsigned long *ret;
+
+	if ((ret = (unsigned long *)pte_quicklist) != NULL) {
+		pte_quicklist = (unsigned long *)(*ret);
+		ret[0] = ret[1];
+		pgtable_cache_size--;
+	}
+	return (pte_t *)ret;
+}
+
+static pte_t *sun4c_pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address)
+{
+	pte_t *pte;
+
+	if ((pte = sun4c_pte_alloc_one_fast(mm, address)) != NULL)
+		return pte;
+
+	pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT);
+	if (pte)
+		memset(pte, 0, PAGE_SIZE);
+	return pte;
+}
+
+static struct page *sun4c_pte_alloc_one(struct mm_struct *mm, unsigned long address)
+{
+	pte_t *pte = sun4c_pte_alloc_one_kernel(mm, address);
+	if (pte == NULL)
+		return NULL;
+	return virt_to_page(pte);
+}
+
+static __inline__ void sun4c_free_pte_fast(pte_t *pte)
+{
+	*(unsigned long *)pte = (unsigned long) pte_quicklist;
+	pte_quicklist = (unsigned long *) pte;
+	pgtable_cache_size++;
+}
+
+static void sun4c_pte_free(struct page *pte)
+{
+	sun4c_free_pte_fast(page_address(pte));
+}
+
+/*
+ * allocating and freeing a pmd is trivial: the 1-entry pmd is
+ * inside the pgd, so has no extra memory associated with it.
+ */
+static pmd_t *sun4c_pmd_alloc_one(struct mm_struct *mm, unsigned long address)
+{
+	BUG();
+	return NULL;
+}
+
+static void sun4c_free_pmd_fast(pmd_t * pmd) { }
+
+static void sun4c_check_pgt_cache(int low, int high)
+{
+	if (pgtable_cache_size > high) {
+		do {
+			if (pgd_quicklist)
+				sun4c_free_pgd_slow(sun4c_get_pgd_fast());
+			if (pte_quicklist)
+				sun4c_free_pte_slow(sun4c_pte_alloc_one_fast(NULL, 0));
+		} while (pgtable_cache_size > low);
+	}
+}
+
+/* An experiment, turn off by default for now... -DaveM */
+#define SUN4C_PRELOAD_PSEG
+
+void sun4c_update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte)
+{
+	unsigned long flags;
+	int pseg;
+
+	local_irq_save(flags);
+	address &= PAGE_MASK;
+	if ((pseg = sun4c_get_segmap(address)) == invalid_segment) {
+		struct sun4c_mmu_entry *entry = sun4c_user_strategy();
+		struct mm_struct *mm = vma->vm_mm;
+		unsigned long start, end;
+
+		entry->vaddr = start = (address & SUN4C_REAL_PGDIR_MASK);
+		entry->ctx = mm->context;
+		add_ring_ordered(sun4c_context_ring + mm->context, entry);
+		sun4c_put_segmap(entry->vaddr, entry->pseg);
+		end = start + SUN4C_REAL_PGDIR_SIZE;
+		while (start < end) {
+#ifdef SUN4C_PRELOAD_PSEG
+			pgd_t *pgdp = sun4c_pgd_offset(mm, start);
+			pte_t *ptep;
+
+			if (!pgdp)
+				goto no_mapping;
+			ptep = sun4c_pte_offset_kernel((pmd_t *) pgdp, start);
+			if (!ptep || !(pte_val(*ptep) & _SUN4C_PAGE_PRESENT))
+				goto no_mapping;
+			sun4c_put_pte(start, pte_val(*ptep));
+			goto next;
+
+		no_mapping:
+#endif
+			sun4c_put_pte(start, 0);
+#ifdef SUN4C_PRELOAD_PSEG
+		next:
+#endif
+			start += PAGE_SIZE;
+		}
+#ifndef SUN4C_PRELOAD_PSEG
+		sun4c_put_pte(address, pte_val(pte));
+#endif
+		local_irq_restore(flags);
+		return;
+	} else {
+		struct sun4c_mmu_entry *entry = &mmu_entry_pool[pseg];
+
+		remove_lru(entry);
+		add_lru(entry);
+	}
+
+	sun4c_put_pte(address, pte_val(pte));
+	local_irq_restore(flags);
+}
+
+extern void sparc_context_init(int);
+extern unsigned long end;
+extern unsigned long bootmem_init(unsigned long *pages_avail);
+extern unsigned long last_valid_pfn;
+
+void __init sun4c_paging_init(void)
+{
+	int i, cnt;
+	unsigned long kernel_end, vaddr;
+	extern struct resource sparc_iomap;
+	unsigned long end_pfn, pages_avail;
+
+	kernel_end = (unsigned long) &end;
+	kernel_end += (SUN4C_REAL_PGDIR_SIZE * 4);
+	kernel_end = SUN4C_REAL_PGDIR_ALIGN(kernel_end);
+
+	pages_avail = 0;
+	last_valid_pfn = bootmem_init(&pages_avail);
+	end_pfn = last_valid_pfn;
+
+	sun4c_probe_mmu();
+	invalid_segment = (num_segmaps - 1);
+	sun4c_init_mmu_entry_pool();
+	sun4c_init_rings();
+	sun4c_init_map_kernelprom(kernel_end);
+	sun4c_init_clean_mmu(kernel_end);
+	sun4c_init_fill_kernel_ring(SUN4C_KERNEL_BUCKETS);
+	sun4c_init_lock_area(sparc_iomap.start, IOBASE_END);
+	sun4c_init_lock_area(DVMA_VADDR, DVMA_END);
+	sun4c_init_lock_areas();
+	sun4c_init_fill_user_ring();
+
+	sun4c_set_context(0);
+	memset(swapper_pg_dir, 0, PAGE_SIZE);
+	memset(pg0, 0, PAGE_SIZE);
+	memset(pg1, 0, PAGE_SIZE);
+	memset(pg2, 0, PAGE_SIZE);
+	memset(pg3, 0, PAGE_SIZE);
+
+	/* Save work later. */
+	vaddr = VMALLOC_START;
+	swapper_pg_dir[vaddr>>SUN4C_PGDIR_SHIFT] = __pgd(PGD_TABLE | (unsigned long) pg0);
+	vaddr += SUN4C_PGDIR_SIZE;
+	swapper_pg_dir[vaddr>>SUN4C_PGDIR_SHIFT] = __pgd(PGD_TABLE | (unsigned long) pg1);
+	vaddr += SUN4C_PGDIR_SIZE;
+	swapper_pg_dir[vaddr>>SUN4C_PGDIR_SHIFT] = __pgd(PGD_TABLE | (unsigned long) pg2);
+	vaddr += SUN4C_PGDIR_SIZE;
+	swapper_pg_dir[vaddr>>SUN4C_PGDIR_SHIFT] = __pgd(PGD_TABLE | (unsigned long) pg3);
+	sun4c_init_ss2_cache_bug();
+	sparc_context_init(num_contexts);
+
+	{
+		unsigned long zones_size[MAX_NR_ZONES];
+		unsigned long zholes_size[MAX_NR_ZONES];
+		unsigned long npages;
+		int znum;
+
+		for (znum = 0; znum < MAX_NR_ZONES; znum++)
+			zones_size[znum] = zholes_size[znum] = 0;
+
+		npages = max_low_pfn - pfn_base;
+
+		zones_size[ZONE_DMA] = npages;
+		zholes_size[ZONE_DMA] = npages - pages_avail;
+
+		npages = highend_pfn - max_low_pfn;
+		zones_size[ZONE_HIGHMEM] = npages;
+		zholes_size[ZONE_HIGHMEM] = npages - calc_highpages();
+
+		free_area_init_node(0, &contig_page_data, zones_size,
+				    pfn_base, zholes_size);
+	}
+
+	cnt = 0;
+	for (i = 0; i < num_segmaps; i++)
+		if (mmu_entry_pool[i].locked)
+			cnt++;
+
+	max_user_taken_entries = num_segmaps - cnt - 40 - 1;
+
+	printk("SUN4C: %d mmu entries for the kernel\n", cnt);
+}
+
+/* Load up routines and constants for sun4c mmu */
+void __init ld_mmu_sun4c(void)
+{
+	extern void ___xchg32_sun4c(void);
+	
+	printk("Loading sun4c MMU routines\n");
+
+	/* First the constants */
+	BTFIXUPSET_SIMM13(pgdir_shift, SUN4C_PGDIR_SHIFT);
+	BTFIXUPSET_SETHI(pgdir_size, SUN4C_PGDIR_SIZE);
+	BTFIXUPSET_SETHI(pgdir_mask, SUN4C_PGDIR_MASK);
+
+	BTFIXUPSET_SIMM13(ptrs_per_pmd, SUN4C_PTRS_PER_PMD);
+	BTFIXUPSET_SIMM13(ptrs_per_pgd, SUN4C_PTRS_PER_PGD);
+	BTFIXUPSET_SIMM13(user_ptrs_per_pgd, KERNBASE / SUN4C_PGDIR_SIZE);
+
+	BTFIXUPSET_INT(page_none, pgprot_val(SUN4C_PAGE_NONE));
+	BTFIXUPSET_INT(page_shared, pgprot_val(SUN4C_PAGE_SHARED));
+	BTFIXUPSET_INT(page_copy, pgprot_val(SUN4C_PAGE_COPY));
+	BTFIXUPSET_INT(page_readonly, pgprot_val(SUN4C_PAGE_READONLY));
+	BTFIXUPSET_INT(page_kernel, pgprot_val(SUN4C_PAGE_KERNEL));
+	page_kernel = pgprot_val(SUN4C_PAGE_KERNEL);
+	pg_iobits = _SUN4C_PAGE_PRESENT | _SUN4C_READABLE | _SUN4C_WRITEABLE |
+		    _SUN4C_PAGE_IO | _SUN4C_PAGE_NOCACHE;
+
+	/* Functions */
+	BTFIXUPSET_CALL(___xchg32, ___xchg32_sun4c, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(do_check_pgt_cache, sun4c_check_pgt_cache, BTFIXUPCALL_NORM);
+	
+	BTFIXUPSET_CALL(flush_cache_all, sun4c_flush_cache_all, BTFIXUPCALL_NORM);
+
+	if (sun4c_vacinfo.do_hwflushes) {
+		BTFIXUPSET_CALL(sun4c_flush_page, sun4c_flush_page_hw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(sun4c_flush_segment, sun4c_flush_segment_hw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(sun4c_flush_context, sun4c_flush_context_hw, BTFIXUPCALL_NORM);
+	} else {
+		BTFIXUPSET_CALL(sun4c_flush_page, sun4c_flush_page_sw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(sun4c_flush_segment, sun4c_flush_segment_sw, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(sun4c_flush_context, sun4c_flush_context_sw, BTFIXUPCALL_NORM);
+	}
+
+	BTFIXUPSET_CALL(flush_tlb_mm, sun4c_flush_tlb_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_mm, sun4c_flush_cache_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(destroy_context, sun4c_destroy_context, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(switch_mm, sun4c_switch_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_page, sun4c_flush_cache_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_page, sun4c_flush_tlb_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_range, sun4c_flush_tlb_range, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_range, sun4c_flush_cache_range, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(__flush_page_to_ram, sun4c_flush_page_to_ram, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_tlb_all, sun4c_flush_tlb_all, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(flush_sig_insns, sun4c_flush_sig_insns, BTFIXUPCALL_NOP);
+
+	BTFIXUPSET_CALL(set_pte, sun4c_set_pte, BTFIXUPCALL_STO1O0);
+
+	/* The 2.4.18 code does not set this on sun4c, how does it work? XXX */
+	/* BTFIXUPSET_SETHI(none_mask, 0x00000000); */	/* Defaults to zero? */
+
+	BTFIXUPSET_CALL(pte_pfn, sun4c_pte_pfn, BTFIXUPCALL_NORM);
+#if 0 /* PAGE_SHIFT <= 12 */ /* Eek. Investigate. XXX */
+	BTFIXUPSET_CALL(pmd_page, sun4c_pmd_page, BTFIXUPCALL_ANDNINT(PAGE_SIZE - 1));
+#else
+	BTFIXUPSET_CALL(pmd_page, sun4c_pmd_page, BTFIXUPCALL_NORM);
+#endif
+	BTFIXUPSET_CALL(pmd_set, sun4c_pmd_set, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_populate, sun4c_pmd_populate, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(pte_present, sun4c_pte_present, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_clear, sun4c_pte_clear, BTFIXUPCALL_STG0O0);
+	BTFIXUPSET_CALL(pte_read, sun4c_pte_read, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(pmd_bad, sun4c_pmd_bad, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_present, sun4c_pmd_present, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pmd_clear, sun4c_pmd_clear, BTFIXUPCALL_STG0O0);
+
+	BTFIXUPSET_CALL(pgd_none, sun4c_pgd_none, BTFIXUPCALL_RETINT(0));
+	BTFIXUPSET_CALL(pgd_bad, sun4c_pgd_bad, BTFIXUPCALL_RETINT(0));
+	BTFIXUPSET_CALL(pgd_present, sun4c_pgd_present, BTFIXUPCALL_RETINT(1));
+	BTFIXUPSET_CALL(pgd_clear, sun4c_pgd_clear, BTFIXUPCALL_NOP);
+
+	BTFIXUPSET_CALL(mk_pte, sun4c_mk_pte, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mk_pte_phys, sun4c_mk_pte_phys, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mk_pte_io, sun4c_mk_pte_io, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_INT(pte_modify_mask, _SUN4C_PAGE_CHG_MASK);
+	BTFIXUPSET_CALL(pmd_offset, sun4c_pmd_offset, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_offset_kernel, sun4c_pte_offset_kernel, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(free_pte_fast, sun4c_free_pte_fast, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_free, sun4c_pte_free, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_alloc_one_kernel, sun4c_pte_alloc_one_kernel, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_alloc_one, sun4c_pte_alloc_one, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(free_pmd_fast, sun4c_free_pmd_fast, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(pmd_alloc_one, sun4c_pmd_alloc_one, BTFIXUPCALL_RETO0);
+	BTFIXUPSET_CALL(free_pgd_fast, sun4c_free_pgd_fast, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(get_pgd_fast, sun4c_get_pgd_fast, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_HALF(pte_writei, _SUN4C_PAGE_WRITE);
+	BTFIXUPSET_HALF(pte_dirtyi, _SUN4C_PAGE_MODIFIED);
+	BTFIXUPSET_HALF(pte_youngi, _SUN4C_PAGE_ACCESSED);
+	BTFIXUPSET_HALF(pte_filei, _SUN4C_PAGE_FILE);
+	BTFIXUPSET_HALF(pte_wrprotecti, _SUN4C_PAGE_WRITE|_SUN4C_PAGE_SILENT_WRITE);
+	BTFIXUPSET_HALF(pte_mkcleani, _SUN4C_PAGE_MODIFIED|_SUN4C_PAGE_SILENT_WRITE);
+	BTFIXUPSET_HALF(pte_mkoldi, _SUN4C_PAGE_ACCESSED|_SUN4C_PAGE_SILENT_READ);
+	BTFIXUPSET_CALL(pte_mkwrite, sun4c_pte_mkwrite, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_mkdirty, sun4c_pte_mkdirty, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pte_mkyoung, sun4c_pte_mkyoung, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(update_mmu_cache, sun4c_update_mmu_cache, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(pte_to_pgoff, sun4c_pte_to_pgoff, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pgoff_to_pte, sun4c_pgoff_to_pte, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(mmu_lockarea, sun4c_lockarea, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_unlockarea, sun4c_unlockarea, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(mmu_get_scsi_one, sun4c_get_scsi_one, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_get_scsi_sgl, sun4c_get_scsi_sgl, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_release_scsi_one, sun4c_release_scsi_one, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_release_scsi_sgl, sun4c_release_scsi_sgl, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(mmu_map_dma_area, sun4c_map_dma_area, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_unmap_dma_area, sun4c_unmap_dma_area, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(mmu_translate_dvma, sun4c_translate_dvma, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(sparc_mapiorange, sun4c_mapiorange, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(sparc_unmapiorange, sun4c_unmapiorange, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(__swp_type, sun4c_swp_type, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(__swp_offset, sun4c_swp_offset, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(__swp_entry, sun4c_swp_entry, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(alloc_thread_info, sun4c_alloc_thread_info, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(free_thread_info, sun4c_free_thread_info, BTFIXUPCALL_NORM);
+
+	BTFIXUPSET_CALL(mmu_info, sun4c_mmu_info, BTFIXUPCALL_NORM);
+
+	/* These should _never_ get called with two level tables. */
+	BTFIXUPSET_CALL(pgd_set, sun4c_pgd_set, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(pgd_page, sun4c_pgd_page, BTFIXUPCALL_RETO0);
+}
diff --git a/arch/sparc/mm/swift.S b/arch/sparc/mm/swift.S
new file mode 100644
index 0000000..2dcaa5a
--- /dev/null
+++ b/arch/sparc/mm/swift.S
@@ -0,0 +1,256 @@
+/* $Id: swift.S,v 1.9 2002/01/08 11:11:59 davem Exp $
+ * swift.S: MicroSparc-II mmu/cache operations.
+ *
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ */
+
+#include <linux/config.h>
+#include <asm/psr.h>
+#include <asm/asi.h>
+#include <asm/page.h>
+#include <asm/pgtsrmmu.h>
+#include <asm/asm_offsets.h>
+
+	.text
+	.align	4
+
+#if 1	/* XXX screw this, I can't get the VAC flushes working
+	 * XXX reliably... -DaveM
+	 */
+	.globl	swift_flush_cache_all, swift_flush_cache_mm
+	.globl	swift_flush_cache_range, swift_flush_cache_page
+	.globl	swift_flush_page_for_dma
+	.globl	swift_flush_page_to_ram
+
+swift_flush_cache_all:
+swift_flush_cache_mm:
+swift_flush_cache_range:
+swift_flush_cache_page:
+swift_flush_page_for_dma:
+swift_flush_page_to_ram:
+	sethi	%hi(0x2000), %o0
+1:	subcc	%o0, 0x10, %o0
+	add	%o0, %o0, %o1
+	sta	%g0, [%o0] ASI_M_DATAC_TAG
+	bne	1b
+	 sta	%g0, [%o1] ASI_M_TXTC_TAG
+	retl
+	 nop
+#else
+
+	.globl	swift_flush_cache_all
+swift_flush_cache_all:
+	WINDOW_FLUSH(%g4, %g5)
+
+	/* Just clear out all the tags. */
+	sethi	%hi(16 * 1024), %o0
+1:	subcc	%o0, 16, %o0
+	sta	%g0, [%o0] ASI_M_TXTC_TAG
+	bne	1b
+	 sta	%g0, [%o0] ASI_M_DATAC_TAG
+	retl
+	 nop
+
+	.globl	swift_flush_cache_mm
+swift_flush_cache_mm:
+	ld	[%o0 + AOFF_mm_context], %g2
+	cmp	%g2, -1
+	be	swift_flush_cache_mm_out
+	WINDOW_FLUSH(%g4, %g5)
+	rd	%psr, %g1
+	andn	%g1, PSR_ET, %g3
+	wr	%g3, 0x0, %psr
+	nop
+	nop
+	mov	SRMMU_CTX_REG, %g7
+	lda	[%g7] ASI_M_MMUREGS, %g5
+	sta	%g2, [%g7] ASI_M_MMUREGS
+
+#if 1
+	sethi	%hi(0x2000), %o0
+1:	subcc	%o0, 0x10, %o0
+	sta	%g0, [%o0] ASI_M_FLUSH_CTX
+	bne	1b
+	 nop
+#else
+	clr	%o0
+	or	%g0, 2048, %g7
+	or	%g0, 2048, %o1
+	add	%o1, 2048, %o2
+	add	%o2, 2048, %o3
+	mov	16, %o4
+	add	%o4, 2048, %o5
+	add	%o5, 2048, %g2
+	add	%g2, 2048, %g3
+1:	sta	%g0, [%o0      ] ASI_M_FLUSH_CTX
+	sta	%g0, [%o0 + %o1] ASI_M_FLUSH_CTX
+	sta	%g0, [%o0 + %o2] ASI_M_FLUSH_CTX
+	sta	%g0, [%o0 + %o3] ASI_M_FLUSH_CTX
+	sta	%g0, [%o0 + %o4] ASI_M_FLUSH_CTX
+	sta	%g0, [%o0 + %o5] ASI_M_FLUSH_CTX
+	sta	%g0, [%o0 + %g2] ASI_M_FLUSH_CTX
+	sta	%g0, [%o0 + %g3] ASI_M_FLUSH_CTX
+	subcc	%g7, 32, %g7
+	bne	1b
+	 add	%o0, 32, %o0
+#endif
+
+	mov	SRMMU_CTX_REG, %g7
+	sta	%g5, [%g7] ASI_M_MMUREGS
+	wr	%g1, 0x0, %psr
+	nop
+	nop
+swift_flush_cache_mm_out:
+	retl
+	 nop
+
+	.globl	swift_flush_cache_range
+swift_flush_cache_range:
+	ld	[%o0 + 0x0], %o0		/* XXX vma->vm_mm, GROSS XXX */
+	sub	%o2, %o1, %o2
+	sethi	%hi(4096), %o3
+	cmp	%o2, %o3
+	bgu	swift_flush_cache_mm
+	 nop
+	b	70f
+	 nop
+
+	.globl	swift_flush_cache_page
+swift_flush_cache_page:
+	ld	[%o0 + 0x0], %o0		/* XXX vma->vm_mm, GROSS XXX */
+70:
+	ld	[%o0 + AOFF_mm_context], %g2
+	cmp	%g2, -1
+	be	swift_flush_cache_page_out
+	WINDOW_FLUSH(%g4, %g5)
+	rd	%psr, %g1
+	andn	%g1, PSR_ET, %g3
+	wr	%g3, 0x0, %psr
+	nop
+	nop
+	mov	SRMMU_CTX_REG, %g7
+	lda	[%g7] ASI_M_MMUREGS, %g5
+	sta	%g2, [%g7] ASI_M_MMUREGS
+
+	andn	%o1, (PAGE_SIZE - 1), %o1
+#if 1
+	sethi	%hi(0x1000), %o0
+1:	subcc	%o0, 0x10, %o0
+	sta	%g0, [%o1 + %o0] ASI_M_FLUSH_PAGE
+	bne	1b
+	 nop
+#else
+	or	%g0, 512, %g7
+	or	%g0, 512, %o0
+	add	%o0, 512, %o2
+	add	%o2, 512, %o3
+	add	%o3, 512, %o4
+	add	%o4, 512, %o5
+	add	%o5, 512, %g3
+	add	%g3, 512, %g4
+1:	sta	%g0, [%o1      ] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o0] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o2] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o3] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o4] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o5] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %g3] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %g4] ASI_M_FLUSH_PAGE
+	subcc	%g7, 16, %g7
+	bne	1b
+	 add	%o1, 16, %o1
+#endif
+
+	mov	SRMMU_CTX_REG, %g7
+	sta	%g5, [%g7] ASI_M_MMUREGS
+	wr	%g1, 0x0, %psr
+	nop
+	nop
+swift_flush_cache_page_out:
+	retl
+	 nop
+
+	/* Swift is write-thru, however it is not
+	 * I/O nor TLB-walk coherent.  Also it has
+	 * caches which are virtually indexed and tagged.
+	 */
+	.globl	swift_flush_page_for_dma
+	.globl	swift_flush_page_to_ram
+swift_flush_page_for_dma:
+swift_flush_page_to_ram:
+	andn	%o0, (PAGE_SIZE - 1), %o1
+#if 1
+	sethi	%hi(0x1000), %o0
+1:	subcc	%o0, 0x10, %o0
+	sta	%g0, [%o1 + %o0] ASI_M_FLUSH_PAGE
+	bne	1b
+	 nop
+#else
+	or	%g0, 512, %g7
+	or	%g0, 512, %o0
+	add	%o0, 512, %o2
+	add	%o2, 512, %o3
+	add	%o3, 512, %o4
+	add	%o4, 512, %o5
+	add	%o5, 512, %g3
+	add	%g3, 512, %g4
+1:	sta	%g0, [%o1      ] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o0] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o2] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o3] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o4] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %o5] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %g3] ASI_M_FLUSH_PAGE
+	sta	%g0, [%o1 + %g4] ASI_M_FLUSH_PAGE
+	subcc	%g7, 16, %g7
+	bne	1b
+	 add	%o1, 16, %o1
+#endif
+	retl
+	 nop
+#endif
+
+	.globl	swift_flush_sig_insns
+swift_flush_sig_insns:
+	flush	%o1
+	retl
+	 flush	%o1 + 4
+
+	.globl	swift_flush_tlb_mm
+	.globl	swift_flush_tlb_range
+	.globl	swift_flush_tlb_all
+swift_flush_tlb_range:
+	ld	[%o0 + 0x00], %o0	/* XXX vma->vm_mm GROSS XXX */
+swift_flush_tlb_mm:
+	ld	[%o0 + AOFF_mm_context], %g2
+	cmp	%g2, -1
+	be	swift_flush_tlb_all_out
+swift_flush_tlb_all:
+	mov	0x400, %o1
+	sta	%g0, [%o1] ASI_M_FLUSH_PROBE
+swift_flush_tlb_all_out:
+	retl
+	 nop
+
+	.globl	swift_flush_tlb_page
+swift_flush_tlb_page:
+	ld	[%o0 + 0x00], %o0	/* XXX vma->vm_mm GROSS XXX */
+	mov	SRMMU_CTX_REG, %g1
+	ld	[%o0 + AOFF_mm_context], %o3
+	andn	%o1, (PAGE_SIZE - 1), %o1
+	cmp	%o3, -1
+	be	swift_flush_tlb_page_out
+	 nop
+#if 1
+	mov	0x400, %o1
+	sta	%g0, [%o1] ASI_M_FLUSH_PROBE	
+#else
+	lda	[%g1] ASI_M_MMUREGS, %g5
+	sta	%o3, [%g1] ASI_M_MMUREGS
+	sta	%g0, [%o1] ASI_M_FLUSH_PAGE	/* rem. virt. cache. prot. */
+	sta	%g0, [%o1] ASI_M_FLUSH_PROBE
+	sta	%g5, [%g1] ASI_M_MMUREGS
+#endif
+swift_flush_tlb_page_out:
+	retl
+	 nop
diff --git a/arch/sparc/mm/tsunami.S b/arch/sparc/mm/tsunami.S
new file mode 100644
index 0000000..8acd178
--- /dev/null
+++ b/arch/sparc/mm/tsunami.S
@@ -0,0 +1,133 @@
+/* $Id: tsunami.S,v 1.7 2001/12/21 04:56:15 davem Exp $
+ * tsunami.S: High speed MicroSparc-I mmu/cache operations.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/config.h>
+#include <asm/ptrace.h>
+#include <asm/asm_offsets.h>
+#include <asm/psr.h>
+#include <asm/asi.h>
+#include <asm/page.h>
+#include <asm/pgtsrmmu.h>
+
+	.text
+	.align	4
+
+	.globl	tsunami_flush_cache_all, tsunami_flush_cache_mm
+	.globl	tsunami_flush_cache_range, tsunami_flush_cache_page
+	.globl	tsunami_flush_page_to_ram, tsunami_flush_page_for_dma
+	.globl	tsunami_flush_sig_insns
+	.globl	tsunami_flush_tlb_all, tsunami_flush_tlb_mm
+	.globl	tsunami_flush_tlb_range, tsunami_flush_tlb_page
+
+	/* Sliiick... */
+tsunami_flush_cache_page:
+tsunami_flush_cache_range:
+	ld	[%o0 + 0x0], %o0	/* XXX vma->vm_mm, GROSS XXX */
+tsunami_flush_cache_mm:
+	ld	[%o0 + AOFF_mm_context], %g2
+	cmp	%g2, -1
+	be	tsunami_flush_cache_out
+tsunami_flush_cache_all:
+	WINDOW_FLUSH(%g4, %g5)
+tsunami_flush_page_for_dma:
+	sta	%g0, [%g0] ASI_M_IC_FLCLEAR
+	sta	%g0, [%g0] ASI_M_DC_FLCLEAR
+tsunami_flush_cache_out:
+tsunami_flush_page_to_ram:
+	retl
+	 nop
+
+tsunami_flush_sig_insns:
+	flush	%o1
+	retl
+	 flush	%o1 + 4
+
+	/* More slick stuff... */
+tsunami_flush_tlb_range:
+	ld	[%o0 + 0x00], %o0	/* XXX vma->vm_mm GROSS XXX */
+tsunami_flush_tlb_mm:
+	ld	[%o0 + AOFF_mm_context], %g2
+	cmp	%g2, -1
+	be	tsunami_flush_tlb_out
+tsunami_flush_tlb_all:
+	 mov	0x400, %o1
+	sta	%g0, [%o1] ASI_M_FLUSH_PROBE
+	nop
+	nop
+	nop
+	nop
+	nop
+tsunami_flush_tlb_out:
+	retl
+	 nop
+
+	/* This one can be done in a fine grained manner... */
+tsunami_flush_tlb_page:
+	ld	[%o0 + 0x00], %o0	/* XXX vma->vm_mm GROSS XXX */
+	mov	SRMMU_CTX_REG, %g1
+	ld	[%o0 + AOFF_mm_context], %o3
+	andn	%o1, (PAGE_SIZE - 1), %o1
+	cmp	%o3, -1
+	be	tsunami_flush_tlb_page_out
+	 lda	[%g1] ASI_M_MMUREGS, %g5
+	sta	%o3, [%g1] ASI_M_MMUREGS
+	sta	%g0, [%o1] ASI_M_FLUSH_PROBE
+	nop
+	nop
+	nop
+	nop
+	nop
+tsunami_flush_tlb_page_out:
+	retl
+	 sta	%g5, [%g1] ASI_M_MMUREGS
+
+#define MIRROR_BLOCK(dst, src, offset, t0, t1, t2, t3) \
+	ldd	[src + offset + 0x18], t0; \
+	std	t0, [dst + offset + 0x18]; \
+	ldd	[src + offset + 0x10], t2; \
+	std	t2, [dst + offset + 0x10]; \
+	ldd	[src + offset + 0x08], t0; \
+	std	t0, [dst + offset + 0x08]; \
+	ldd	[src + offset + 0x00], t2; \
+	std	t2, [dst + offset + 0x00];
+
+	.globl	tsunami_copy_1page
+tsunami_copy_1page:
+/* NOTE: This routine has to be shorter than 70insns --jj */
+	or	%g0, (PAGE_SIZE >> 8), %g1
+1:
+	MIRROR_BLOCK(%o0, %o1, 0x00, %o2, %o3, %o4, %o5)
+	MIRROR_BLOCK(%o0, %o1, 0x20, %o2, %o3, %o4, %o5)
+	MIRROR_BLOCK(%o0, %o1, 0x40, %o2, %o3, %o4, %o5)
+	MIRROR_BLOCK(%o0, %o1, 0x60, %o2, %o3, %o4, %o5)
+	MIRROR_BLOCK(%o0, %o1, 0x80, %o2, %o3, %o4, %o5)
+	MIRROR_BLOCK(%o0, %o1, 0xa0, %o2, %o3, %o4, %o5)
+	MIRROR_BLOCK(%o0, %o1, 0xc0, %o2, %o3, %o4, %o5)
+	MIRROR_BLOCK(%o0, %o1, 0xe0, %o2, %o3, %o4, %o5)
+	subcc	%g1, 1, %g1
+	add	%o0, 0x100, %o0
+	bne	1b
+	 add	%o1, 0x100, %o1
+
+	.globl	tsunami_setup_blockops
+tsunami_setup_blockops:
+	sethi	%hi(__copy_1page), %o0
+	or	%o0, %lo(__copy_1page), %o0
+	sethi	%hi(tsunami_copy_1page), %o1
+	or	%o1, %lo(tsunami_copy_1page), %o1
+	sethi	%hi(tsunami_setup_blockops), %o2
+	or	%o2, %lo(tsunami_setup_blockops), %o2
+	ld	[%o1], %o4
+1:	add	%o1, 4, %o1
+	st	%o4, [%o0]
+	add	%o0, 4, %o0
+	cmp	%o1, %o2
+	bne	1b
+	ld	[%o1], %o4
+	sta	%g0, [%g0] ASI_M_IC_FLCLEAR
+	sta	%g0, [%g0] ASI_M_DC_FLCLEAR
+	retl
+	 nop
diff --git a/arch/sparc/mm/viking.S b/arch/sparc/mm/viking.S
new file mode 100644
index 0000000..f58712d
--- /dev/null
+++ b/arch/sparc/mm/viking.S
@@ -0,0 +1,284 @@
+/* $Id: viking.S,v 1.19 2001/12/21 04:56:15 davem Exp $
+ * viking.S: High speed Viking cache/mmu operations
+ *
+ * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
+ * Copyright (C) 1997,1998,1999  Jakub Jelinek  (jj@ultra.linux.cz)
+ * Copyright (C) 1999  Pavel Semerad  (semerad@ss1000.ms.mff.cuni.cz)
+ */
+
+#include <linux/config.h>
+#include <asm/ptrace.h>
+#include <asm/psr.h>
+#include <asm/asm_offsets.h>
+#include <asm/asi.h>
+#include <asm/mxcc.h>
+#include <asm/page.h>
+#include <asm/pgtsrmmu.h>
+#include <asm/viking.h>
+#include <asm/btfixup.h>
+
+#ifdef CONFIG_SMP
+	.data
+	.align	4
+sun4dsmp_flush_tlb_spin:
+	.word	0
+#endif
+
+	.text
+	.align	4
+
+	.globl	viking_flush_cache_all, viking_flush_cache_mm
+	.globl	viking_flush_cache_range, viking_flush_cache_page
+	.globl	viking_flush_page, viking_mxcc_flush_page
+	.globl	viking_flush_page_for_dma, viking_flush_page_to_ram
+	.globl	viking_flush_sig_insns
+	.globl	viking_flush_tlb_all, viking_flush_tlb_mm
+	.globl	viking_flush_tlb_range, viking_flush_tlb_page
+
+viking_flush_page:
+	sethi	%hi(PAGE_OFFSET), %g2
+	sub	%o0, %g2, %g3
+	srl	%g3, 12, %g1		! ppage >> 12
+
+	clr	%o1			! set counter, 0 - 127
+	sethi	%hi(PAGE_OFFSET + PAGE_SIZE - 0x80000000), %o3
+	sethi	%hi(0x80000000), %o4
+	sethi	%hi(VIKING_PTAG_VALID), %o5
+	sethi	%hi(2*PAGE_SIZE), %o0
+	sethi	%hi(PAGE_SIZE), %g7
+	clr	%o2			! block counter, 0 - 3
+5:
+	sll	%o1, 5, %g4
+	or	%g4, %o4, %g4		! 0x80000000 | (set << 5)
+
+	sll	%o2, 26, %g5		! block << 26
+6:
+	or	%g5, %g4, %g5
+	ldda	[%g5] ASI_M_DATAC_TAG, %g2
+	cmp	%g3, %g1		! ptag == ppage?
+	bne	7f
+	 inc	%o2
+
+	andcc	%g2, %o5, %g0		! ptag VALID?
+	be	7f
+	 add	%g4, %o3, %g2		! (PAGE_OFFSET + PAGE_SIZE) | (set << 5)
+	ld	[%g2], %g3
+	ld	[%g2 + %g7], %g3
+	add	%g2, %o0, %g2
+	ld	[%g2], %g3
+	ld	[%g2 + %g7], %g3
+	add	%g2, %o0, %g2
+	ld	[%g2], %g3
+	ld	[%g2 + %g7], %g3
+	add	%g2, %o0, %g2
+	ld	[%g2], %g3
+	b	8f
+	 ld	[%g2 + %g7], %g3
+
+7:
+	cmp	%o2, 3
+	ble	6b
+	 sll	%o2, 26, %g5			! block << 26
+
+8:	inc	%o1
+	cmp	%o1, 0x7f
+	ble	5b
+	 clr	%o2
+
+9:	retl
+	 nop
+
+viking_mxcc_flush_page:
+	sethi	%hi(PAGE_OFFSET), %g2
+	sub	%o0, %g2, %g3
+	sub	%g3, -PAGE_SIZE, %g3		! ppage + PAGE_SIZE
+	sethi	%hi(MXCC_SRCSTREAM), %o3	! assume %hi(MXCC_SRCSTREAM) == %hi(MXCC_DESTSTREAM)
+	mov	0x10, %g2			! set cacheable bit
+	or	%o3, %lo(MXCC_SRCSTREAM), %o2
+	or	%o3, %lo(MXCC_DESSTREAM), %o3
+	sub	%g3, MXCC_STREAM_SIZE, %g3
+6:
+	stda	%g2, [%o2] ASI_M_MXCC
+	stda	%g2, [%o3] ASI_M_MXCC
+	andncc	%g3, PAGE_MASK, %g0
+	bne	6b
+	 sub	%g3, MXCC_STREAM_SIZE, %g3
+
+9:	retl
+	 nop
+
+viking_flush_cache_page:
+viking_flush_cache_range:
+#ifndef CONFIG_SMP
+	ld	[%o0 + 0x0], %o0		/* XXX vma->vm_mm, GROSS XXX */
+#endif
+viking_flush_cache_mm:
+#ifndef CONFIG_SMP
+	ld	[%o0 + AOFF_mm_context], %g1
+	cmp	%g1, -1
+	bne	viking_flush_cache_all
+	 nop
+	b,a	viking_flush_cache_out
+#endif
+viking_flush_cache_all:
+	WINDOW_FLUSH(%g4, %g5)
+viking_flush_cache_out:
+	retl
+	 nop
+
+viking_flush_tlb_all:
+	mov	0x400, %g1
+	retl
+	 sta	%g0, [%g1] ASI_M_FLUSH_PROBE
+
+viking_flush_tlb_mm:
+	mov	SRMMU_CTX_REG, %g1
+	ld	[%o0 + AOFF_mm_context], %o1
+	lda	[%g1] ASI_M_MMUREGS, %g5
+#ifndef CONFIG_SMP
+	cmp	%o1, -1
+	be	1f
+#endif
+	mov	0x300, %g2
+	sta	%o1, [%g1] ASI_M_MMUREGS
+	sta	%g0, [%g2] ASI_M_FLUSH_PROBE
+	retl
+	 sta	%g5, [%g1] ASI_M_MMUREGS
+#ifndef CONFIG_SMP
+1:	retl
+	 nop
+#endif
+
+viking_flush_tlb_range:
+	ld	[%o0 + 0x00], %o0	/* XXX vma->vm_mm GROSS XXX */
+	mov	SRMMU_CTX_REG, %g1
+	ld	[%o0 + AOFF_mm_context], %o3
+	lda	[%g1] ASI_M_MMUREGS, %g5
+#ifndef CONFIG_SMP
+	cmp	%o3, -1
+	be	2f
+#endif
+	sethi	%hi(~((1 << SRMMU_PGDIR_SHIFT) - 1)), %o4
+	sta	%o3, [%g1] ASI_M_MMUREGS
+	and	%o1, %o4, %o1
+	add	%o1, 0x200, %o1
+	sta	%g0, [%o1] ASI_M_FLUSH_PROBE
+1:	sub	%o1, %o4, %o1
+	cmp	%o1, %o2
+	blu,a	1b
+	 sta	%g0, [%o1] ASI_M_FLUSH_PROBE
+	retl
+	 sta	%g5, [%g1] ASI_M_MMUREGS
+#ifndef CONFIG_SMP
+2:	retl
+	 nop
+#endif
+
+viking_flush_tlb_page:
+	ld	[%o0 + 0x00], %o0	/* XXX vma->vm_mm GROSS XXX */
+	mov	SRMMU_CTX_REG, %g1
+	ld	[%o0 + AOFF_mm_context], %o3
+	lda	[%g1] ASI_M_MMUREGS, %g5
+#ifndef CONFIG_SMP
+	cmp	%o3, -1
+	be	1f
+#endif
+	and	%o1, PAGE_MASK, %o1
+	sta	%o3, [%g1] ASI_M_MMUREGS
+	sta	%g0, [%o1] ASI_M_FLUSH_PROBE
+	retl
+	 sta	%g5, [%g1] ASI_M_MMUREGS
+#ifndef CONFIG_SMP
+1:	retl
+	 nop
+#endif
+
+viking_flush_page_to_ram:
+viking_flush_page_for_dma:
+viking_flush_sig_insns:
+	retl
+	 nop
+
+#ifdef CONFIG_SMP
+	.globl	sun4dsmp_flush_tlb_all, sun4dsmp_flush_tlb_mm
+	.globl	sun4dsmp_flush_tlb_range, sun4dsmp_flush_tlb_page
+sun4dsmp_flush_tlb_all:
+	sethi	%hi(sun4dsmp_flush_tlb_spin), %g3
+1:	ldstub	[%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5
+	tst	%g5
+	bne	2f
+	 mov	0x400, %g1
+	sta	%g0, [%g1] ASI_M_FLUSH_PROBE
+	retl
+	 stb	%g0, [%g3 + %lo(sun4dsmp_flush_tlb_spin)]
+2:	tst	%g5
+	bne,a	2b
+	 ldub	[%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5
+	b,a	1b
+
+sun4dsmp_flush_tlb_mm:
+	sethi	%hi(sun4dsmp_flush_tlb_spin), %g3
+1:	ldstub	[%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5
+	tst	%g5
+	bne	2f
+	 mov	SRMMU_CTX_REG, %g1
+	ld	[%o0 + AOFF_mm_context], %o1
+	lda	[%g1] ASI_M_MMUREGS, %g5
+	mov	0x300, %g2
+	sta	%o1, [%g1] ASI_M_MMUREGS
+	sta	%g0, [%g2] ASI_M_FLUSH_PROBE
+	sta	%g5, [%g1] ASI_M_MMUREGS
+	retl
+	 stb	%g0, [%g3 + %lo(sun4dsmp_flush_tlb_spin)]
+2:	tst	%g5
+	bne,a	2b
+	 ldub	[%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5
+	b,a	1b
+
+sun4dsmp_flush_tlb_range:
+	sethi	%hi(sun4dsmp_flush_tlb_spin), %g3
+1:	ldstub	[%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5
+	tst	%g5
+	bne	3f
+	 mov	SRMMU_CTX_REG, %g1
+	ld	[%o0 + 0x00], %o0	/* XXX vma->vm_mm GROSS XXX */
+	ld	[%o0 + AOFF_mm_context], %o3
+	lda	[%g1] ASI_M_MMUREGS, %g5
+	sethi	%hi(~((1 << SRMMU_PGDIR_SHIFT) - 1)), %o4
+	sta	%o3, [%g1] ASI_M_MMUREGS
+	and	%o1, %o4, %o1
+	add	%o1, 0x200, %o1
+	sta	%g0, [%o1] ASI_M_FLUSH_PROBE
+2:	sub	%o1, %o4, %o1
+	cmp	%o1, %o2
+	blu,a	2b
+	 sta	%g0, [%o1] ASI_M_FLUSH_PROBE
+	sta	%g5, [%g1] ASI_M_MMUREGS
+	retl
+	 stb	%g0, [%g3 + %lo(sun4dsmp_flush_tlb_spin)]
+3:	tst	%g5
+	bne,a	3b
+	 ldub	[%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5
+	b,a	1b
+
+sun4dsmp_flush_tlb_page:
+	sethi	%hi(sun4dsmp_flush_tlb_spin), %g3
+1:	ldstub	[%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5
+	tst	%g5
+	bne	2f
+	 mov	SRMMU_CTX_REG, %g1
+	ld	[%o0 + 0x00], %o0	/* XXX vma->vm_mm GROSS XXX */
+	ld	[%o0 + AOFF_mm_context], %o3
+	lda	[%g1] ASI_M_MMUREGS, %g5
+	and	%o1, PAGE_MASK, %o1
+	sta	%o3, [%g1] ASI_M_MMUREGS
+	sta	%g0, [%o1] ASI_M_FLUSH_PROBE
+	sta	%g5, [%g1] ASI_M_MMUREGS
+	retl
+	 stb	%g0, [%g3 + %lo(sun4dsmp_flush_tlb_spin)]
+2:	tst	%g5
+	bne,a	2b
+	 ldub	[%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5
+	b,a	1b
+	 nop
+#endif
diff --git a/arch/sparc/prom/Makefile b/arch/sparc/prom/Makefile
new file mode 100644
index 0000000..2b217ee
--- /dev/null
+++ b/arch/sparc/prom/Makefile
@@ -0,0 +1,9 @@
+# $Id: Makefile,v 1.8 2000/12/15 00:41:22 davem Exp $
+# Makefile for the Sun Boot PROM interface library under
+# Linux.
+#
+
+lib-y := bootstr.o devmap.o devops.o init.o memory.o misc.o mp.o \
+	 palloc.o ranges.o segment.o console.o printf.o tree.o
+
+lib-$(CONFIG_SUN4) += sun4prom.o
diff --git a/arch/sparc/prom/bootstr.c b/arch/sparc/prom/bootstr.c
new file mode 100644
index 0000000..cfdeac2
--- /dev/null
+++ b/arch/sparc/prom/bootstr.c
@@ -0,0 +1,63 @@
+/* $Id: bootstr.c,v 1.20 2000/02/08 20:24:23 davem Exp $
+ * bootstr.c:  Boot string/argument acquisition from the PROM.
+ *
+ * Copyright(C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/string.h>
+#include <asm/oplib.h>
+#include <asm/sun4prom.h>
+#include <linux/init.h>
+
+#define BARG_LEN  256
+static char barg_buf[BARG_LEN] = { 0 };
+static char fetched __initdata = 0;
+
+extern linux_sun4_romvec *sun4_romvec;
+
+char * __init
+prom_getbootargs(void)
+{
+	int iter;
+	char *cp, *arg;
+
+	/* This check saves us from a panic when bootfd patches args. */
+	if (fetched) {
+		return barg_buf;
+	}
+
+	switch(prom_vers) {
+	case PROM_V0:
+	case PROM_SUN4:
+		cp = barg_buf;
+		/* Start from 1 and go over fd(0,0,0)kernel */
+		for(iter = 1; iter < 8; iter++) {
+			arg = (*(romvec->pv_v0bootargs))->argv[iter];
+			if(arg == 0) break;
+			while(*arg != 0) {
+				/* Leave place for space and null. */
+				if(cp >= barg_buf + BARG_LEN-2){
+					/* We might issue a warning here. */
+					break;
+				}
+				*cp++ = *arg++;
+			}
+			*cp++ = ' ';
+		}
+		*cp = 0;
+		break;
+	case PROM_V2:
+	case PROM_V3:
+		/*
+		 * V3 PROM cannot supply as with more than 128 bytes
+		 * of an argument. But a smart bootstrap loader can.
+		 */
+		strlcpy(barg_buf, *romvec->pv_v2bootargs.bootargs, sizeof(barg_buf));
+		break;
+	default:
+		break;
+	}
+
+	fetched = 1;
+	return barg_buf;
+}
diff --git a/arch/sparc/prom/console.c b/arch/sparc/prom/console.c
new file mode 100644
index 0000000..4e6e41d
--- /dev/null
+++ b/arch/sparc/prom/console.c
@@ -0,0 +1,220 @@
+/* $Id: console.c,v 1.25 2001/10/30 04:54:22 davem Exp $
+ * console.c: Routines that deal with sending and receiving IO
+ *            to/from the current console device using the PROM.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998 Pete Zaitcev <zaitcev@yahoo.com>
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/openprom.h>
+#include <asm/sun4prom.h>
+#include <asm/oplib.h>
+#include <asm/system.h>
+#include <linux/string.h>
+
+extern void restore_current(void);
+
+static char con_name_jmc[] = "/obio/su@"; /* "/obio/su@0,3002f8"; */
+#define CON_SIZE_JMC	(sizeof(con_name_jmc))
+
+/* Non blocking get character from console input device, returns -1
+ * if no input was taken.  This can be used for polling.
+ */
+int
+prom_nbgetchar(void)
+{
+	static char inc;
+	int i = -1;
+	unsigned long flags;
+
+	spin_lock_irqsave(&prom_lock, flags);
+	switch(prom_vers) {
+	case PROM_V0:
+	case PROM_SUN4:
+		i = (*(romvec->pv_nbgetchar))();
+		break;
+	case PROM_V2:
+	case PROM_V3:
+		if( (*(romvec->pv_v2devops).v2_dev_read)(*romvec->pv_v2bootargs.fd_stdin , &inc, 0x1) == 1) {
+			i = inc;
+		} else {
+			i = -1;
+		}
+		break;
+	default:
+		i = -1;
+		break;
+	};
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+	return i; /* Ugh, we could spin forever on unsupported proms ;( */
+}
+
+/* Non blocking put character to console device, returns -1 if
+ * unsuccessful.
+ */
+int
+prom_nbputchar(char c)
+{
+	static char outc;
+	unsigned long flags;
+	int i = -1;
+
+	spin_lock_irqsave(&prom_lock, flags);
+	switch(prom_vers) {
+	case PROM_V0:
+	case PROM_SUN4:
+		i = (*(romvec->pv_nbputchar))(c);
+		break;
+	case PROM_V2:
+	case PROM_V3:
+		outc = c;
+		if( (*(romvec->pv_v2devops).v2_dev_write)(*romvec->pv_v2bootargs.fd_stdout, &outc, 0x1) == 1)
+			i = 0;
+		else
+			i = -1;
+		break;
+	default:
+		i = -1;
+		break;
+	};
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+	return i; /* Ugh, we could spin forever on unsupported proms ;( */
+}
+
+/* Blocking version of get character routine above. */
+char
+prom_getchar(void)
+{
+	int character;
+	while((character = prom_nbgetchar()) == -1) ;
+	return (char) character;
+}
+
+/* Blocking version of put character routine above. */
+void
+prom_putchar(char c)
+{
+	while(prom_nbputchar(c) == -1) ;
+	return;
+}
+
+/* Query for input device type */
+enum prom_input_device
+prom_query_input_device(void)
+{
+	unsigned long flags;
+	int st_p;
+	char propb[64];
+	char *p;
+	int propl;
+
+	switch(prom_vers) {
+	case PROM_V0:
+	case PROM_V2:
+	case PROM_SUN4:
+	default:
+		switch(*romvec->pv_stdin) {
+		case PROMDEV_KBD:	return PROMDEV_IKBD;
+		case PROMDEV_TTYA:	return PROMDEV_ITTYA;
+		case PROMDEV_TTYB:	return PROMDEV_ITTYB;
+		default:
+			return PROMDEV_I_UNK;
+		};
+	case PROM_V3:
+		spin_lock_irqsave(&prom_lock, flags);
+		st_p = (*romvec->pv_v2devops.v2_inst2pkg)(*romvec->pv_v2bootargs.fd_stdin);
+		restore_current();
+		spin_unlock_irqrestore(&prom_lock, flags);
+		if(prom_node_has_property(st_p, "keyboard"))
+			return PROMDEV_IKBD;
+		if (prom_getproperty(st_p, "name", propb, sizeof(propb)) != -1) {
+			if(strncmp(propb, "keyboard", sizeof("serial")) == 0)
+				return PROMDEV_IKBD;
+		}
+		if (prom_getproperty(st_p, "device_type", propb, sizeof(propb)) != -1) {
+		if(strncmp(propb, "serial", sizeof("serial")))
+			return PROMDEV_I_UNK;
+		}
+		propl = prom_getproperty(prom_root_node, "stdin-path", propb, sizeof(propb));
+		if(propl > 2) {
+			p = propb;
+			while(*p) p++; p -= 2;
+			if(p[0] == ':') {
+				if(p[1] == 'a')
+					return PROMDEV_ITTYA;
+				else if(p[1] == 'b')
+					return PROMDEV_ITTYB;
+			}
+		}
+		return PROMDEV_I_UNK;
+	}
+}
+
+/* Query for output device type */
+
+enum prom_output_device
+prom_query_output_device(void)
+{
+	unsigned long flags;
+	int st_p;
+	char propb[64];
+	char *p;
+	int propl;
+
+	switch(prom_vers) {
+	case PROM_V0:
+	case PROM_SUN4:
+		switch(*romvec->pv_stdin) {
+		case PROMDEV_SCREEN:	return PROMDEV_OSCREEN;
+		case PROMDEV_TTYA:	return PROMDEV_OTTYA;
+		case PROMDEV_TTYB:	return PROMDEV_OTTYB;
+		};
+		break;
+	case PROM_V2:
+	case PROM_V3:
+		spin_lock_irqsave(&prom_lock, flags);
+		st_p = (*romvec->pv_v2devops.v2_inst2pkg)(*romvec->pv_v2bootargs.fd_stdout);
+		restore_current();
+		spin_unlock_irqrestore(&prom_lock, flags);
+		propl = prom_getproperty(st_p, "device_type", propb, sizeof(propb));
+		if (propl == sizeof("display") &&
+			strncmp("display", propb, sizeof("display")) == 0)
+		{
+			return PROMDEV_OSCREEN;
+		}
+		if(prom_vers == PROM_V3) {
+			if(propl >= 0 &&
+			    strncmp("serial", propb, sizeof("serial")) != 0)
+				return PROMDEV_O_UNK;
+			propl = prom_getproperty(prom_root_node, "stdout-path",
+						 propb, sizeof(propb));
+			if(propl == CON_SIZE_JMC &&
+			    strncmp(propb, con_name_jmc, CON_SIZE_JMC) == 0)
+				return PROMDEV_OTTYA;
+			if(propl > 2) {
+				p = propb;
+				while(*p) p++; p-= 2;
+				if(p[0]==':') {
+					if(p[1] == 'a')
+						return PROMDEV_OTTYA;
+					else if(p[1] == 'b')
+						return PROMDEV_OTTYB;
+				}
+			}
+		} else {
+			switch(*romvec->pv_stdin) {
+			case PROMDEV_TTYA:	return PROMDEV_OTTYA;
+			case PROMDEV_TTYB:	return PROMDEV_OTTYB;
+			};
+		}
+		break;
+	default:
+		;
+	};
+	return PROMDEV_O_UNK;
+}
diff --git a/arch/sparc/prom/devmap.c b/arch/sparc/prom/devmap.c
new file mode 100644
index 0000000..eb12073
--- /dev/null
+++ b/arch/sparc/prom/devmap.c
@@ -0,0 +1,54 @@
+/* $Id: devmap.c,v 1.7 2000/08/26 02:38:03 anton Exp $
+ * promdevmap.c:  Map device/IO areas to virtual addresses.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+extern void restore_current(void);
+
+/* Just like the routines in palloc.c, these should not be used
+ * by the kernel at all.  Bootloader facility mainly.  And again,
+ * this is only available on V2 proms and above.
+ */
+
+/* Map physical device address 'paddr' in IO space 'ios' of size
+ * 'num_bytes' to a virtual address, with 'vhint' being a hint to
+ * the prom as to where you would prefer the mapping.  We return
+ * where the prom actually mapped it.
+ */
+char *
+prom_mapio(char *vhint, int ios, unsigned int paddr, unsigned int num_bytes)
+{
+	unsigned long flags;
+	char *ret;
+
+	spin_lock_irqsave(&prom_lock, flags);
+	if((num_bytes == 0) || (paddr == 0)) ret = (char *) 0x0;
+	else
+	ret = (*(romvec->pv_v2devops.v2_dumb_mmap))(vhint, ios, paddr,
+						    num_bytes);
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+	return ret;
+}
+
+/* Unmap an IO/device area that was mapped using the above routine. */
+void
+prom_unmapio(char *vaddr, unsigned int num_bytes)
+{
+	unsigned long flags;
+
+	if(num_bytes == 0x0) return;
+	spin_lock_irqsave(&prom_lock, flags);
+	(*(romvec->pv_v2devops.v2_dumb_munmap))(vaddr, num_bytes);
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+	return;
+}
diff --git a/arch/sparc/prom/devops.c b/arch/sparc/prom/devops.c
new file mode 100644
index 0000000..61919b5
--- /dev/null
+++ b/arch/sparc/prom/devops.c
@@ -0,0 +1,89 @@
+/* $Id: devops.c,v 1.13 2000/08/26 02:38:03 anton Exp $
+ * devops.c:  Device operations using the PROM.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+extern void restore_current(void);
+
+/* Open the device described by the string 'dstr'.  Returns the handle
+ * to that device used for subsequent operations on that device.
+ * Returns -1 on failure.
+ */
+int
+prom_devopen(char *dstr)
+{
+	int handle;
+	unsigned long flags;
+	spin_lock_irqsave(&prom_lock, flags);
+	switch(prom_vers) {
+	case PROM_V0:
+		handle = (*(romvec->pv_v0devops.v0_devopen))(dstr);
+		if(handle == 0) handle = -1;
+		break;
+	case PROM_V2:
+	case PROM_V3:
+		handle = (*(romvec->pv_v2devops.v2_dev_open))(dstr);
+		break;
+	default:
+		handle = -1;
+		break;
+	};
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+
+	return handle;
+}
+
+/* Close the device described by device handle 'dhandle'. */
+int
+prom_devclose(int dhandle)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&prom_lock, flags);
+	switch(prom_vers) {
+	case PROM_V0:
+		(*(romvec->pv_v0devops.v0_devclose))(dhandle);
+		break;
+	case PROM_V2:
+	case PROM_V3:
+		(*(romvec->pv_v2devops.v2_dev_close))(dhandle);
+		break;
+	default:
+		break;
+	};
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+	return 0;
+}
+
+/* Seek to specified location described by 'seekhi' and 'seeklo'
+ * for device 'dhandle'.
+ */
+void
+prom_seek(int dhandle, unsigned int seekhi, unsigned int seeklo)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&prom_lock, flags);
+	switch(prom_vers) {
+	case PROM_V0:
+		(*(romvec->pv_v0devops.v0_seekdev))(dhandle, seekhi, seeklo);
+		break;
+	case PROM_V2:
+	case PROM_V3:
+		(*(romvec->pv_v2devops.v2_dev_seek))(dhandle, seekhi, seeklo);
+		break;
+	default:
+		break;
+	};
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+
+	return;
+}
diff --git a/arch/sparc/prom/init.c b/arch/sparc/prom/init.c
new file mode 100644
index 0000000..b83409c
--- /dev/null
+++ b/arch/sparc/prom/init.c
@@ -0,0 +1,95 @@
+/* $Id: init.c,v 1.14 2000/01/29 01:09:12 anton Exp $
+ * init.c:  Initialize internal variables used by the PROM
+ *          library functions.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/sun4prom.h>
+
+struct linux_romvec *romvec;
+enum prom_major_version prom_vers;
+unsigned int prom_rev, prom_prev;
+linux_sun4_romvec *sun4_romvec;
+
+/* The root node of the prom device tree. */
+int prom_root_node;
+
+int prom_stdin, prom_stdout;
+
+/* Pointer to the device tree operations structure. */
+struct linux_nodeops *prom_nodeops;
+
+/* You must call prom_init() before you attempt to use any of the
+ * routines in the prom library.  It returns 0 on success, 1 on
+ * failure.  It gets passed the pointer to the PROM vector.
+ */
+
+extern void prom_meminit(void);
+extern void prom_ranges_init(void);
+
+void __init prom_init(struct linux_romvec *rp)
+{
+#ifdef CONFIG_SUN4
+	extern struct linux_romvec *sun4_prom_init(void);
+	rp = sun4_prom_init();
+#endif
+	romvec = rp;
+
+	switch(romvec->pv_romvers) {
+	case 0:
+		prom_vers = PROM_V0;
+		break;
+	case 2:
+		prom_vers = PROM_V2;
+		break;
+	case 3:
+		prom_vers = PROM_V3;
+		break;
+	case 40:
+		prom_vers = PROM_SUN4;
+		break;
+	default:
+		prom_printf("PROMLIB: Bad PROM version %d\n",
+			    romvec->pv_romvers);
+		prom_halt();
+		break;
+	};
+
+	prom_rev = romvec->pv_plugin_revision;
+	prom_prev = romvec->pv_printrev;
+	prom_nodeops = romvec->pv_nodeops;
+
+	prom_root_node = prom_getsibling(0);
+	if((prom_root_node == 0) || (prom_root_node == -1))
+		prom_halt();
+
+	if((((unsigned long) prom_nodeops) == 0) || 
+	   (((unsigned long) prom_nodeops) == -1))
+		prom_halt();
+
+	if(prom_vers == PROM_V2 || prom_vers == PROM_V3) {
+		prom_stdout = *romvec->pv_v2bootargs.fd_stdout;
+		prom_stdin  = *romvec->pv_v2bootargs.fd_stdin;
+	}
+	
+	prom_meminit();
+
+	prom_ranges_init();
+
+#ifndef CONFIG_SUN4
+	/* SUN4 prints this in sun4_prom_init */
+	printk("PROMLIB: Sun Boot Prom Version %d Revision %d\n",
+	       romvec->pv_romvers, prom_rev);
+#endif
+
+	/* Initialization successful. */
+	return;
+}
diff --git a/arch/sparc/prom/memory.c b/arch/sparc/prom/memory.c
new file mode 100644
index 0000000..46aa51a
--- /dev/null
+++ b/arch/sparc/prom/memory.c
@@ -0,0 +1,216 @@
+/* $Id: memory.c,v 1.15 2000/01/29 01:09:12 anton Exp $
+ * memory.c: Prom routine for acquiring various bits of information
+ *           about RAM on the machine, both virtual and physical.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Michael A. Griffith (grif@acm.org)
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include <asm/openprom.h>
+#include <asm/sun4prom.h>
+#include <asm/oplib.h>
+
+/* This routine, for consistency, returns the ram parameters in the
+ * V0 prom memory descriptor format.  I choose this format because I
+ * think it was the easiest to work with.  I feel the religious
+ * arguments now... ;)  Also, I return the linked lists sorted to
+ * prevent paging_init() upset stomach as I have not yet written
+ * the pepto-bismol kernel module yet.
+ */
+
+struct linux_prom_registers prom_reg_memlist[64];
+struct linux_prom_registers prom_reg_tmp[64];
+
+struct linux_mlist_v0 prom_phys_total[64];
+struct linux_mlist_v0 prom_prom_taken[64];
+struct linux_mlist_v0 prom_phys_avail[64];
+
+struct linux_mlist_v0 *prom_ptot_ptr = prom_phys_total;
+struct linux_mlist_v0 *prom_ptak_ptr = prom_prom_taken;
+struct linux_mlist_v0 *prom_pavl_ptr = prom_phys_avail;
+
+struct linux_mem_v0 prom_memlist;
+
+
+/* Internal Prom library routine to sort a linux_mlist_v0 memory
+ * list.  Used below in initialization.
+ */
+static void __init
+prom_sortmemlist(struct linux_mlist_v0 *thislist)
+{
+	int swapi = 0;
+	int i, mitr, tmpsize;
+	char *tmpaddr;
+	char *lowest;
+
+	for(i=0; thislist[i].theres_more != 0; i++) {
+		lowest = thislist[i].start_adr;
+		for(mitr = i+1; thislist[mitr-1].theres_more != 0; mitr++)
+			if(thislist[mitr].start_adr < lowest) {
+				lowest = thislist[mitr].start_adr;
+				swapi = mitr;
+			}
+		if(lowest == thislist[i].start_adr) continue;
+		tmpaddr = thislist[swapi].start_adr;
+		tmpsize = thislist[swapi].num_bytes;
+		for(mitr = swapi; mitr > i; mitr--) {
+			thislist[mitr].start_adr = thislist[mitr-1].start_adr;
+			thislist[mitr].num_bytes = thislist[mitr-1].num_bytes;
+		}
+		thislist[i].start_adr = tmpaddr;
+		thislist[i].num_bytes = tmpsize;
+	}
+
+	return;
+}
+
+/* Initialize the memory lists based upon the prom version. */
+void __init prom_meminit(void)
+{
+	int node = 0;
+	unsigned int iter, num_regs;
+	struct linux_mlist_v0 *mptr;  /* ptr for traversal */
+
+	switch(prom_vers) {
+	case PROM_V0:
+		/* Nice, kind of easier to do in this case. */
+		/* First, the total physical descriptors. */
+		for(mptr = (*(romvec->pv_v0mem.v0_totphys)), iter=0;
+		    mptr; mptr=mptr->theres_more, iter++) {
+			prom_phys_total[iter].start_adr = mptr->start_adr;
+			prom_phys_total[iter].num_bytes = mptr->num_bytes;
+			prom_phys_total[iter].theres_more = &prom_phys_total[iter+1];
+		}
+		prom_phys_total[iter-1].theres_more = 0x0;
+		/* Second, the total prom taken descriptors. */
+		for(mptr = (*(romvec->pv_v0mem.v0_prommap)), iter=0;
+		    mptr; mptr=mptr->theres_more, iter++) {
+			prom_prom_taken[iter].start_adr = mptr->start_adr;
+			prom_prom_taken[iter].num_bytes = mptr->num_bytes;
+			prom_prom_taken[iter].theres_more = &prom_prom_taken[iter+1];
+		}
+		prom_prom_taken[iter-1].theres_more = 0x0;
+		/* Last, the available physical descriptors. */
+		for(mptr = (*(romvec->pv_v0mem.v0_available)), iter=0;
+		    mptr; mptr=mptr->theres_more, iter++) {
+			prom_phys_avail[iter].start_adr = mptr->start_adr;
+			prom_phys_avail[iter].num_bytes = mptr->num_bytes;
+			prom_phys_avail[iter].theres_more = &prom_phys_avail[iter+1];
+		}
+		prom_phys_avail[iter-1].theres_more = 0x0;
+		/* Sort all the lists. */
+		prom_sortmemlist(prom_phys_total);
+		prom_sortmemlist(prom_prom_taken);
+		prom_sortmemlist(prom_phys_avail);
+		break;
+	case PROM_V2:
+	case PROM_V3:
+		/* Grrr, have to traverse the prom device tree ;( */
+		node = prom_getchild(prom_root_node);
+		node = prom_searchsiblings(node, "memory");
+		num_regs = prom_getproperty(node, "available",
+					    (char *) prom_reg_memlist,
+					    sizeof(prom_reg_memlist));
+		num_regs = (num_regs/sizeof(struct linux_prom_registers));
+		for(iter=0; iter<num_regs; iter++) {
+			prom_phys_avail[iter].start_adr =
+				(char *) prom_reg_memlist[iter].phys_addr;
+			prom_phys_avail[iter].num_bytes =
+				(unsigned long) prom_reg_memlist[iter].reg_size;
+			prom_phys_avail[iter].theres_more =
+				&prom_phys_avail[iter+1];
+		}
+		prom_phys_avail[iter-1].theres_more = 0x0;
+
+		num_regs = prom_getproperty(node, "reg",
+					    (char *) prom_reg_memlist,
+					    sizeof(prom_reg_memlist));
+		num_regs = (num_regs/sizeof(struct linux_prom_registers));
+		for(iter=0; iter<num_regs; iter++) {
+			prom_phys_total[iter].start_adr =
+				(char *) prom_reg_memlist[iter].phys_addr;
+			prom_phys_total[iter].num_bytes =
+				(unsigned long) prom_reg_memlist[iter].reg_size;
+			prom_phys_total[iter].theres_more =
+				&prom_phys_total[iter+1];
+		}
+		prom_phys_total[iter-1].theres_more = 0x0;
+
+		node = prom_getchild(prom_root_node);
+		node = prom_searchsiblings(node, "virtual-memory");
+		num_regs = prom_getproperty(node, "available",
+					    (char *) prom_reg_memlist,
+					    sizeof(prom_reg_memlist));
+		num_regs = (num_regs/sizeof(struct linux_prom_registers));
+
+		/* Convert available virtual areas to taken virtual
+		 * areas.  First sort, then convert.
+		 */
+		for(iter=0; iter<num_regs; iter++) {
+			prom_prom_taken[iter].start_adr =
+				(char *) prom_reg_memlist[iter].phys_addr;
+			prom_prom_taken[iter].num_bytes =
+				(unsigned long) prom_reg_memlist[iter].reg_size;
+			prom_prom_taken[iter].theres_more =
+				&prom_prom_taken[iter+1];
+		}
+		prom_prom_taken[iter-1].theres_more = 0x0;
+
+		prom_sortmemlist(prom_prom_taken);
+
+		/* Finally, convert. */
+		for(iter=0; iter<num_regs; iter++) {
+			prom_prom_taken[iter].start_adr =
+				prom_prom_taken[iter].start_adr +
+					prom_prom_taken[iter].num_bytes;
+			prom_prom_taken[iter].num_bytes =
+				prom_prom_taken[iter+1].start_adr -
+					prom_prom_taken[iter].start_adr;
+		}
+		prom_prom_taken[iter-1].num_bytes =
+			0xffffffff - (unsigned long) prom_prom_taken[iter-1].start_adr;
+
+		/* Sort the other two lists. */
+		prom_sortmemlist(prom_phys_total);
+		prom_sortmemlist(prom_phys_avail);
+		break;
+
+	case PROM_SUN4:
+#ifdef CONFIG_SUN4	
+		/* how simple :) */
+		prom_phys_total[0].start_adr = 0x0;
+		prom_phys_total[0].num_bytes = *(sun4_romvec->memorysize);
+		prom_phys_total[0].theres_more = 0x0;
+		prom_prom_taken[0].start_adr = 0x0; 
+		prom_prom_taken[0].num_bytes = 0x0;
+		prom_prom_taken[0].theres_more = 0x0;
+		prom_phys_avail[0].start_adr = 0x0;
+		prom_phys_avail[0].num_bytes = *(sun4_romvec->memoryavail);
+		prom_phys_avail[0].theres_more = 0x0;
+#endif
+		break;
+
+	default:
+		break;
+	};
+
+	/* Link all the lists into the top-level descriptor. */
+	prom_memlist.v0_totphys=&prom_ptot_ptr;
+	prom_memlist.v0_prommap=&prom_ptak_ptr;
+	prom_memlist.v0_available=&prom_pavl_ptr;
+
+	return;
+}
+
+/* This returns a pointer to our libraries internal v0 format
+ * memory descriptor.
+ */
+struct linux_mem_v0 *
+prom_meminfo(void)
+{
+	return &prom_memlist;
+}
diff --git a/arch/sparc/prom/misc.c b/arch/sparc/prom/misc.c
new file mode 100644
index 0000000..c840c20
--- /dev/null
+++ b/arch/sparc/prom/misc.c
@@ -0,0 +1,139 @@
+/* $Id: misc.c,v 1.18 2000/08/26 02:38:03 anton Exp $
+ * misc.c:  Miscellaneous prom functions that don't belong
+ *          anywhere else.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/auxio.h>
+#include <asm/system.h>
+
+extern void restore_current(void);
+
+DEFINE_SPINLOCK(prom_lock);
+
+/* Reset and reboot the machine with the command 'bcommand'. */
+void
+prom_reboot(char *bcommand)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&prom_lock, flags);
+	(*(romvec->pv_reboot))(bcommand);
+	/* Never get here. */
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+}
+
+/* Forth evaluate the expression contained in 'fstring'. */
+void
+prom_feval(char *fstring)
+{
+	unsigned long flags;
+	if(!fstring || fstring[0] == 0)
+		return;
+	spin_lock_irqsave(&prom_lock, flags);
+	if(prom_vers == PROM_V0)
+		(*(romvec->pv_fortheval.v0_eval))(strlen(fstring), fstring);
+	else
+		(*(romvec->pv_fortheval.v2_eval))(fstring);
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+}
+
+/* We want to do this more nicely some day. */
+extern void (*prom_palette)(int);
+
+/* Drop into the prom, with the chance to continue with the 'go'
+ * prom command.
+ */
+void
+prom_cmdline(void)
+{
+	extern void install_obp_ticker(void);
+	extern void install_linux_ticker(void);
+	unsigned long flags;
+
+	if(!serial_console && prom_palette)
+		prom_palette (1);
+	spin_lock_irqsave(&prom_lock, flags);
+	install_obp_ticker();
+	(*(romvec->pv_abort))();
+	restore_current();
+	install_linux_ticker();
+	spin_unlock_irqrestore(&prom_lock, flags);
+#ifdef CONFIG_SUN_AUXIO
+	set_auxio(AUXIO_LED, 0);
+#endif
+	if(!serial_console && prom_palette)
+		prom_palette (0);
+}
+
+/* Drop into the prom, but completely terminate the program.
+ * No chance of continuing.
+ */
+void
+prom_halt(void)
+{
+	unsigned long flags;
+again:
+	spin_lock_irqsave(&prom_lock, flags);
+	(*(romvec->pv_halt))();
+	/* Never get here. */
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+	goto again; /* PROM is out to get me -DaveM */
+}
+
+typedef void (*sfunc_t)(void);
+
+/* Set prom sync handler to call function 'funcp'. */
+void
+prom_setsync(sfunc_t funcp)
+{
+	if(!funcp) return;
+	*romvec->pv_synchook = funcp;
+}
+
+/* Get the idprom and stuff it into buffer 'idbuf'.  Returns the
+ * format type.  'num_bytes' is the number of bytes that your idbuf
+ * has space for.  Returns 0xff on error.
+ */
+unsigned char
+prom_get_idprom(char *idbuf, int num_bytes)
+{
+	int len;
+
+	len = prom_getproplen(prom_root_node, "idprom");
+	if((len>num_bytes) || (len==-1)) return 0xff;
+	if(!prom_getproperty(prom_root_node, "idprom", idbuf, num_bytes))
+		return idbuf[0];
+
+	return 0xff;
+}
+
+/* Get the major prom version number. */
+int
+prom_version(void)
+{
+	return romvec->pv_romvers;
+}
+
+/* Get the prom plugin-revision. */
+int
+prom_getrev(void)
+{
+	return prom_rev;
+}
+
+/* Get the prom firmware print revision. */
+int
+prom_getprev(void)
+{
+	return prom_prev;
+}
diff --git a/arch/sparc/prom/mp.c b/arch/sparc/prom/mp.c
new file mode 100644
index 0000000..92fe373
--- /dev/null
+++ b/arch/sparc/prom/mp.c
@@ -0,0 +1,121 @@
+/* $Id: mp.c,v 1.12 2000/08/26 02:38:03 anton Exp $
+ * mp.c:  OpenBoot Prom Multiprocessor support routines.  Don't call
+ *        these on a UP or else you will halt and catch fire. ;)
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+extern void restore_current(void);
+
+/* Start cpu with prom-tree node 'cpunode' using context described
+ * by 'ctable_reg' in context 'ctx' at program counter 'pc'.
+ *
+ * XXX Have to look into what the return values mean. XXX
+ */
+int
+prom_startcpu(int cpunode, struct linux_prom_registers *ctable_reg, int ctx, char *pc)
+{
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&prom_lock, flags);
+	switch(prom_vers) {
+	case PROM_V0:
+	case PROM_V2:
+	default:
+		ret = -1;
+		break;
+	case PROM_V3:
+		ret = (*(romvec->v3_cpustart))(cpunode, (int) ctable_reg, ctx, pc);
+		break;
+	};
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+
+	return ret;
+}
+
+/* Stop CPU with device prom-tree node 'cpunode'.
+ * XXX Again, what does the return value really mean? XXX
+ */
+int
+prom_stopcpu(int cpunode)
+{
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&prom_lock, flags);
+	switch(prom_vers) {
+	case PROM_V0:
+	case PROM_V2:
+	default:
+		ret = -1;
+		break;
+	case PROM_V3:
+		ret = (*(romvec->v3_cpustop))(cpunode);
+		break;
+	};
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+
+	return ret;
+}
+
+/* Make CPU with device prom-tree node 'cpunode' idle.
+ * XXX Return value, anyone? XXX
+ */
+int
+prom_idlecpu(int cpunode)
+{
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&prom_lock, flags);
+	switch(prom_vers) {
+	case PROM_V0:
+	case PROM_V2:
+	default:
+		ret = -1;
+		break;
+	case PROM_V3:
+		ret = (*(romvec->v3_cpuidle))(cpunode);
+		break;
+	};
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+
+	return ret;
+}
+
+/* Resume the execution of CPU with nodeid 'cpunode'.
+ * XXX Come on, somebody has to know... XXX
+ */
+int
+prom_restartcpu(int cpunode)
+{
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&prom_lock, flags);
+	switch(prom_vers) {
+	case PROM_V0:
+	case PROM_V2:
+	default:
+		ret = -1;
+		break;
+	case PROM_V3:
+		ret = (*(romvec->v3_cpuresume))(cpunode);
+		break;
+	};
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+
+	return ret;
+}
diff --git a/arch/sparc/prom/palloc.c b/arch/sparc/prom/palloc.c
new file mode 100644
index 0000000..84ce8bc
--- /dev/null
+++ b/arch/sparc/prom/palloc.c
@@ -0,0 +1,44 @@
+/* $Id: palloc.c,v 1.4 1996/04/25 06:09:48 davem Exp $
+ * palloc.c:  Memory allocation from the Sun PROM.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+/* You should not call these routines after memory management
+ * has been initialized in the kernel, if fact you should not
+ * use these if at all possible in the kernel.  They are mainly
+ * to be used for a bootloader for temporary allocations which
+ * it will free before jumping into the kernel it has loaded.
+ *
+ * Also, these routines don't work on V0 proms, only V2 and later.
+ */
+
+/* Allocate a chunk of memory of size 'num_bytes' giving a suggestion
+ * of virtual_hint as the preferred virtual base address of this chunk.
+ * There are no guarantees that you will get the allocation, or that
+ * the prom will abide by your "hint".  So check your return value.
+ */
+char *
+prom_alloc(char *virtual_hint, unsigned int num_bytes)
+{
+	if(prom_vers == PROM_V0) return (char *) 0x0;
+	if(num_bytes == 0x0) return (char *) 0x0;
+	return (*(romvec->pv_v2devops.v2_dumb_mem_alloc))(virtual_hint, num_bytes);
+}
+
+/* Free a previously allocated chunk back to the prom at virtual address
+ * 'vaddr' of size 'num_bytes'.  NOTE: This vaddr is not the hint you
+ * used for the allocation, but the virtual address the prom actually
+ * returned to you.  They may be have been the same, they may have not,
+ * doesn't matter.
+ */
+void
+prom_free(char *vaddr, unsigned int num_bytes)
+{
+	if((prom_vers == PROM_V0) || (num_bytes == 0x0)) return;
+	(*(romvec->pv_v2devops.v2_dumb_mem_free))(vaddr, num_bytes);
+	return;
+}
diff --git a/arch/sparc/prom/printf.c b/arch/sparc/prom/printf.c
new file mode 100644
index 0000000..dc8b598
--- /dev/null
+++ b/arch/sparc/prom/printf.c
@@ -0,0 +1,46 @@
+/*
+ * printf.c:  Internal prom library printf facility.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (c) 2002 Pete Zaitcev (zaitcev@yahoo.com)
+ *
+ * We used to warn all over the code: DO NOT USE prom_printf(),
+ * and yet people do. Anton's banking code was outputing banks
+ * with prom_printf for most of the 2.4 lifetime. Since an effective
+ * stick is not available, we deployed a carrot: an early printk
+ * through PROM by means of -p boot option. This ought to fix it.
+ * USE printk; if you need, deploy -p.
+ */
+
+#include <linux/kernel.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+static char ppbuf[1024];
+
+void
+prom_write(const char *buf, unsigned int n)
+{
+	char ch;
+
+	while (n != 0) {
+		--n;
+		if ((ch = *buf++) == '\n')
+			prom_putchar('\r');
+		prom_putchar(ch);
+	}
+}
+
+void
+prom_printf(char *fmt, ...)
+{
+	va_list args;
+	int i;
+
+	va_start(args, fmt);
+	i = vscnprintf(ppbuf, sizeof(ppbuf), fmt, args);
+	va_end(args);
+
+	prom_write(ppbuf, i);
+}
diff --git a/arch/sparc/prom/ranges.c b/arch/sparc/prom/ranges.c
new file mode 100644
index 0000000..a292032
--- /dev/null
+++ b/arch/sparc/prom/ranges.c
@@ -0,0 +1,118 @@
+/* $Id: ranges.c,v 1.15 2001/12/19 00:29:51 davem Exp $
+ * ranges.c: Handle ranges in newer proms for obio/sbus.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/init.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/types.h>
+#include <asm/sbus.h>
+#include <asm/system.h>
+
+struct linux_prom_ranges promlib_obio_ranges[PROMREG_MAX];
+int num_obio_ranges;
+
+/* Adjust register values based upon the ranges parameters. */
+static void
+prom_adjust_regs(struct linux_prom_registers *regp, int nregs,
+		 struct linux_prom_ranges *rangep, int nranges)
+{
+	int regc, rngc;
+
+	for (regc = 0; regc < nregs; regc++) {
+		for (rngc = 0; rngc < nranges; rngc++)
+			if (regp[regc].which_io == rangep[rngc].ot_child_space)
+				break; /* Fount it */
+		if (rngc == nranges) /* oops */
+			prom_printf("adjust_regs: Could not find range with matching bus type...\n");
+		regp[regc].which_io = rangep[rngc].ot_parent_space;
+		regp[regc].phys_addr -= rangep[rngc].ot_child_base;
+		regp[regc].phys_addr += rangep[rngc].ot_parent_base;
+	}
+}
+
+void
+prom_adjust_ranges(struct linux_prom_ranges *ranges1, int nranges1,
+		   struct linux_prom_ranges *ranges2, int nranges2)
+{
+	int rng1c, rng2c;
+
+	for(rng1c=0; rng1c < nranges1; rng1c++) {
+		for(rng2c=0; rng2c < nranges2; rng2c++)
+			if(ranges1[rng1c].ot_parent_space == ranges2[rng2c].ot_child_space &&
+			   ranges1[rng1c].ot_parent_base >= ranges2[rng2c].ot_child_base &&
+			   ranges2[rng2c].ot_child_base + ranges2[rng2c].or_size - ranges1[rng1c].ot_parent_base > 0U)
+			break;
+		if(rng2c == nranges2) /* oops */
+			prom_printf("adjust_ranges: Could not find matching bus type...\n");
+		else if (ranges1[rng1c].ot_parent_base + ranges1[rng1c].or_size > ranges2[rng2c].ot_child_base + ranges2[rng2c].or_size)
+			ranges1[rng1c].or_size =
+				ranges2[rng2c].ot_child_base + ranges2[rng2c].or_size - ranges1[rng1c].ot_parent_base;
+		ranges1[rng1c].ot_parent_space = ranges2[rng2c].ot_parent_space;
+		ranges1[rng1c].ot_parent_base += ranges2[rng2c].ot_parent_base;
+	}
+}
+
+/* Apply probed obio ranges to registers passed, if no ranges return. */
+void
+prom_apply_obio_ranges(struct linux_prom_registers *regs, int nregs)
+{
+	if(num_obio_ranges)
+		prom_adjust_regs(regs, nregs, promlib_obio_ranges, num_obio_ranges);
+}
+
+void __init prom_ranges_init(void)
+{
+	int node, obio_node;
+	int success;
+
+	num_obio_ranges = 0;
+
+	/* Check for obio and sbus ranges. */
+	node = prom_getchild(prom_root_node);
+	obio_node = prom_searchsiblings(node, "obio");
+
+	if(obio_node) {
+		success = prom_getproperty(obio_node, "ranges",
+					   (char *) promlib_obio_ranges,
+					   sizeof(promlib_obio_ranges));
+		if(success != -1)
+			num_obio_ranges = (success/sizeof(struct linux_prom_ranges));
+	}
+
+	if(num_obio_ranges)
+		prom_printf("PROMLIB: obio_ranges %d\n", num_obio_ranges);
+
+	return;
+}
+
+void
+prom_apply_generic_ranges (int node, int parent, struct linux_prom_registers *regs, int nregs)
+{
+	int success;
+	int num_ranges;
+	struct linux_prom_ranges ranges[PROMREG_MAX];
+	
+	success = prom_getproperty(node, "ranges",
+				   (char *) ranges,
+				   sizeof (ranges));
+	if (success != -1) {
+		num_ranges = (success/sizeof(struct linux_prom_ranges));
+		if (parent) {
+			struct linux_prom_ranges parent_ranges[PROMREG_MAX];
+			int num_parent_ranges;
+		
+			success = prom_getproperty(parent, "ranges",
+				   		   (char *) parent_ranges,
+				   		   sizeof (parent_ranges));
+			if (success != -1) {
+				num_parent_ranges = (success/sizeof(struct linux_prom_ranges));
+				prom_adjust_ranges (ranges, num_ranges, parent_ranges, num_parent_ranges);
+			}
+		}
+		prom_adjust_regs(regs, nregs, ranges, num_ranges);
+	}
+}
diff --git a/arch/sparc/prom/segment.c b/arch/sparc/prom/segment.c
new file mode 100644
index 0000000..09d6460
--- /dev/null
+++ b/arch/sparc/prom/segment.c
@@ -0,0 +1,29 @@
+/* $Id: segment.c,v 1.7 2000/08/26 02:38:03 anton Exp $
+ * segment.c:  Prom routine to map segments in other contexts before
+ *             a standalone is completely mapped.  This is for sun4 and
+ *             sun4c architectures only.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+extern void restore_current(void);
+
+/* Set physical segment 'segment' at virtual address 'vaddr' in
+ * context 'ctx'.
+ */
+void
+prom_putsegment(int ctx, unsigned long vaddr, int segment)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&prom_lock, flags);
+	(*(romvec->pv_setctxt))(ctx, (char *) vaddr, segment);
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+	return;
+}
diff --git a/arch/sparc/prom/sun4prom.c b/arch/sparc/prom/sun4prom.c
new file mode 100644
index 0000000..69ca735
--- /dev/null
+++ b/arch/sparc/prom/sun4prom.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 1996 The Australian National University.
+ * Copyright (C) 1996 Fujitsu Laboratories Limited
+ * Copyright (C) 1997 Michael A. Griffith (grif@acm.org)
+ * Copyright (C) 1997 Sun Weenie (ko@ko.reno.nv.us)
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * 
+ * This software may be distributed under the terms of the Gnu
+ * Public License version 2 or later
+ *
+ * fake a really simple Sun prom for the SUN4
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <asm/oplib.h>
+#include <asm/idprom.h> 
+#include <asm/machines.h> 
+#include <asm/sun4prom.h>
+#include <asm/asi.h>
+#include <asm/contregs.h>
+#include <linux/init.h>
+
+static struct linux_romvec sun4romvec;
+static struct idprom sun4_idprom;
+
+struct property {
+	char *name;
+	char *value;
+	int length;
+};
+
+struct node {
+	int level;
+	struct property *properties;
+};
+
+struct property null_properties = { NULL, NULL, -1 };
+
+struct property root_properties[] = {
+	{"device_type", "cpu", 4},
+	{"idprom", (char *)&sun4_idprom, sizeof(struct idprom)},
+	{NULL, NULL, -1}
+};
+
+struct node nodes[] = {
+	{ 0, &null_properties }, 
+	{ 0, root_properties },
+	{ -1,&null_properties }
+};
+
+
+static int no_nextnode(int node)
+{
+	if (nodes[node].level == nodes[node+1].level)
+		return node+1;
+	return -1;
+}
+
+static int no_child(int node)
+{
+	if (nodes[node].level == nodes[node+1].level-1)
+		return node+1;
+	return -1;
+}
+
+static struct property *find_property(int node,char *name)
+{
+	struct property *prop = &nodes[node].properties[0];
+	while (prop && prop->name) {
+		if (strcmp(prop->name,name) == 0) return prop;
+		prop++;
+	}
+	return NULL;
+}
+
+static int no_proplen(int node,char *name)
+{
+	struct property *prop = find_property(node,name);
+	if (prop) return prop->length;
+	return -1;
+}
+
+static int no_getprop(int node,char *name,char *value)
+{
+	struct property *prop = find_property(node,name);
+	if (prop) {
+		memcpy(value,prop->value,prop->length);
+		return 1;
+	}
+	return -1;
+}
+
+static int no_setprop(int node,char *name,char *value,int len)
+{
+	return -1;
+}
+
+static char *no_nextprop(int node,char *name)
+{
+	struct property *prop = find_property(node,name);
+	if (prop) return prop[1].name;
+	return NULL;
+}
+
+static struct linux_nodeops sun4_nodeops = {
+	no_nextnode,
+	no_child,
+	no_proplen,
+	no_getprop,
+	no_setprop,
+	no_nextprop
+};
+	
+static int synch_hook;
+
+struct linux_romvec * __init sun4_prom_init(void)
+{
+	int i;
+	unsigned char x;
+	char *p;
+                                
+	p = (char *)&sun4_idprom;
+	for (i = 0; i < sizeof(sun4_idprom); i++) {
+		__asm__ __volatile__ ("lduba [%1] %2, %0" : "=r" (x) :
+				      "r" (AC_IDPROM + i), "i" (ASI_CONTROL));
+		*p++ = x;
+	}
+
+	memset(&sun4romvec,0,sizeof(sun4romvec));
+
+	sun4_romvec = (linux_sun4_romvec *) SUN4_PROM_VECTOR;
+
+	sun4romvec.pv_romvers = 40;
+	sun4romvec.pv_nodeops = &sun4_nodeops;
+	sun4romvec.pv_reboot = sun4_romvec->reboot;
+	sun4romvec.pv_abort = sun4_romvec->abortentry;
+	sun4romvec.pv_halt = sun4_romvec->exittomon;
+	sun4romvec.pv_synchook = (void (**)(void))&synch_hook;
+	sun4romvec.pv_setctxt = sun4_romvec->setcxsegmap;
+	sun4romvec.pv_v0bootargs = sun4_romvec->bootParam;
+	sun4romvec.pv_nbgetchar = sun4_romvec->mayget;
+	sun4romvec.pv_nbputchar = sun4_romvec->mayput;
+	sun4romvec.pv_stdin = sun4_romvec->insource;
+	sun4romvec.pv_stdout = sun4_romvec->outsink;
+	
+	/*
+	 * We turn on the LEDs to let folks without monitors or
+	 * terminals know we booted.   Nothing too fancy now.  They
+	 * are all on, except for LED 5, which blinks.   When we
+	 * have more time, we can teach the penguin to say "By your
+	 * command" or "Activating turbo boost, Michael". :-)
+	 */
+	sun4_romvec->setLEDs(0x0);
+	
+	printk("PROMLIB: Old Sun4 boot PROM monitor %s, romvec version %d\n",
+		sun4_romvec->monid,
+		sun4_romvec->romvecversion);
+
+	return &sun4romvec;
+}
diff --git a/arch/sparc/prom/tree.c b/arch/sparc/prom/tree.c
new file mode 100644
index 0000000..2bf03ee
--- /dev/null
+++ b/arch/sparc/prom/tree.c
@@ -0,0 +1,364 @@
+/* $Id: tree.c,v 1.26 2000/08/26 02:38:03 anton Exp $
+ * tree.c: Basic device tree traversal/scanning for the Linux
+ *         prom library.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#define PROMLIB_INTERNAL
+
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ctype.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+extern void restore_current(void);
+
+static char promlib_buf[128];
+
+/* Internal version of prom_getchild that does not alter return values. */
+int __prom_getchild(int node)
+{
+	unsigned long flags;
+	int cnode;
+
+	spin_lock_irqsave(&prom_lock, flags);
+	cnode = prom_nodeops->no_child(node);
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+
+	return cnode;
+}
+
+/* Return the child of node 'node' or zero if no this node has no
+ * direct descendent.
+ */
+int prom_getchild(int node)
+{
+	int cnode;
+
+	if (node == -1)
+		return 0;
+
+	cnode = __prom_getchild(node);
+	if (cnode == 0 || cnode == -1)
+		return 0;
+
+	return cnode;
+}
+
+/* Internal version of prom_getsibling that does not alter return values. */
+int __prom_getsibling(int node)
+{
+	unsigned long flags;
+	int cnode;
+
+	spin_lock_irqsave(&prom_lock, flags);
+	cnode = prom_nodeops->no_nextnode(node);
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+
+	return cnode;
+}
+
+/* Return the next sibling of node 'node' or zero if no more siblings
+ * at this level of depth in the tree.
+ */
+int prom_getsibling(int node)
+{
+	int sibnode;
+
+	if (node == -1)
+		return 0;
+
+	sibnode = __prom_getsibling(node);
+	if (sibnode == 0 || sibnode == -1)
+		return 0;
+
+	return sibnode;
+}
+
+/* Return the length in bytes of property 'prop' at node 'node'.
+ * Return -1 on error.
+ */
+int prom_getproplen(int node, char *prop)
+{
+	int ret;
+	unsigned long flags;
+
+	if((!node) || (!prop))
+		return -1;
+		
+	spin_lock_irqsave(&prom_lock, flags);
+	ret = prom_nodeops->no_proplen(node, prop);
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+	return ret;
+}
+
+/* Acquire a property 'prop' at node 'node' and place it in
+ * 'buffer' which has a size of 'bufsize'.  If the acquisition
+ * was successful the length will be returned, else -1 is returned.
+ */
+int prom_getproperty(int node, char *prop, char *buffer, int bufsize)
+{
+	int plen, ret;
+	unsigned long flags;
+
+	plen = prom_getproplen(node, prop);
+	if((plen > bufsize) || (plen == 0) || (plen == -1))
+		return -1;
+	/* Ok, things seem all right. */
+	spin_lock_irqsave(&prom_lock, flags);
+	ret = prom_nodeops->no_getprop(node, prop, buffer);
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+	return ret;
+}
+
+/* Acquire an integer property and return its value.  Returns -1
+ * on failure.
+ */
+int prom_getint(int node, char *prop)
+{
+	static int intprop;
+
+	if(prom_getproperty(node, prop, (char *) &intprop, sizeof(int)) != -1)
+		return intprop;
+
+	return -1;
+}
+
+/* Acquire an integer property, upon error return the passed default
+ * integer.
+ */
+int prom_getintdefault(int node, char *property, int deflt)
+{
+	int retval;
+
+	retval = prom_getint(node, property);
+	if(retval == -1) return deflt;
+
+	return retval;
+}
+
+/* Acquire a boolean property, 1=TRUE 0=FALSE. */
+int prom_getbool(int node, char *prop)
+{
+	int retval;
+
+	retval = prom_getproplen(node, prop);
+	if(retval == -1) return 0;
+	return 1;
+}
+
+/* Acquire a property whose value is a string, returns a null
+ * string on error.  The char pointer is the user supplied string
+ * buffer.
+ */
+void prom_getstring(int node, char *prop, char *user_buf, int ubuf_size)
+{
+	int len;
+
+	len = prom_getproperty(node, prop, user_buf, ubuf_size);
+	if(len != -1) return;
+	user_buf[0] = 0;
+	return;
+}
+
+
+/* Does the device at node 'node' have name 'name'?
+ * YES = 1   NO = 0
+ */
+int prom_nodematch(int node, char *name)
+{
+	int error;
+
+	static char namebuf[128];
+	error = prom_getproperty(node, "name", namebuf, sizeof(namebuf));
+	if (error == -1) return 0;
+	if(strcmp(namebuf, name) == 0) return 1;
+	return 0;
+}
+
+/* Search siblings at 'node_start' for a node with name
+ * 'nodename'.  Return node if successful, zero if not.
+ */
+int prom_searchsiblings(int node_start, char *nodename)
+{
+
+	int thisnode, error;
+
+	for(thisnode = node_start; thisnode;
+	    thisnode=prom_getsibling(thisnode)) {
+		error = prom_getproperty(thisnode, "name", promlib_buf,
+					 sizeof(promlib_buf));
+		/* Should this ever happen? */
+		if(error == -1) continue;
+		if(strcmp(nodename, promlib_buf)==0) return thisnode;
+	}
+
+	return 0;
+}
+
+/* Gets name in the form prom v2+ uses it (name@x,yyyyy or name (if no reg)) */
+int prom_getname (int node, char *buffer, int len)
+{
+	int i;
+	struct linux_prom_registers reg[PROMREG_MAX];
+	
+	i = prom_getproperty (node, "name", buffer, len);
+	if (i <= 0) return -1;
+	buffer [i] = 0;
+	len -= i;
+	i = prom_getproperty (node, "reg", (char *)reg, sizeof (reg));
+	if (i <= 0) return 0;
+	if (len < 11) return -1;
+	buffer = strchr (buffer, 0);
+	sprintf (buffer, "@%x,%x", reg[0].which_io, (uint)reg[0].phys_addr);
+	return 0;
+}
+
+/* Interal version of nextprop that does not alter return values. */
+char * __prom_nextprop(int node, char * oprop)
+{
+	unsigned long flags;
+	char *prop;
+
+	spin_lock_irqsave(&prom_lock, flags);
+	prop = prom_nodeops->no_nextprop(node, oprop);
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+
+	return prop;
+}
+
+/* Return the first property name for node 'node'. */
+/* buffer is unused argument, but as v9 uses it, we need to have the same interface */
+char * prom_firstprop(int node, char *bufer)
+{
+	if (node == 0 || node == -1)
+		return "";
+
+	return __prom_nextprop(node, "");
+}
+
+/* Return the property type string after property type 'oprop'
+ * at node 'node' .  Returns empty string if no more
+ * property types for this node.
+ */
+char * prom_nextprop(int node, char *oprop, char *buffer)
+{
+	if (node == 0 || node == -1)
+		return "";
+
+	return __prom_nextprop(node, oprop);
+}
+
+int prom_finddevice(char *name)
+{
+	char nbuf[128];
+	char *s = name, *d;
+	int node = prom_root_node, node2;
+	unsigned int which_io, phys_addr;
+	struct linux_prom_registers reg[PROMREG_MAX];
+
+	while (*s++) {
+		if (!*s) return node; /* path '.../' is legal */
+		node = prom_getchild(node);
+
+		for (d = nbuf; *s != 0 && *s != '@' && *s != '/';)
+			*d++ = *s++;
+		*d = 0;
+		
+		node = prom_searchsiblings(node, nbuf);
+		if (!node)
+			return 0;
+
+		if (*s == '@') {
+			if (isxdigit(s[1]) && s[2] == ',') {
+				which_io = simple_strtoul(s+1, NULL, 16);
+				phys_addr = simple_strtoul(s+3, &d, 16);
+				if (d != s + 3 && (!*d || *d == '/')
+				    && d <= s + 3 + 8) {
+					node2 = node;
+					while (node2 && node2 != -1) {
+						if (prom_getproperty (node2, "reg", (char *)reg, sizeof (reg)) > 0) {
+							if (which_io == reg[0].which_io && phys_addr == reg[0].phys_addr) {
+								node = node2;
+								break;
+							}
+						}
+						node2 = prom_getsibling(node2);
+						if (!node2 || node2 == -1)
+							break;
+						node2 = prom_searchsiblings(prom_getsibling(node2), nbuf);
+					}
+				}
+			}
+			while (*s != 0 && *s != '/') s++;
+		}
+	}
+	return node;
+}
+
+int prom_node_has_property(int node, char *prop)
+{
+	char *current_property = "";
+
+	do {
+		current_property = prom_nextprop(node, current_property, NULL);
+		if(!strcmp(current_property, prop))
+		   return 1;
+	} while (*current_property);
+	return 0;
+}
+
+/* Set property 'pname' at node 'node' to value 'value' which has a length
+ * of 'size' bytes.  Return the number of bytes the prom accepted.
+ */
+int prom_setprop(int node, char *pname, char *value, int size)
+{
+	unsigned long flags;
+	int ret;
+
+	if(size == 0) return 0;
+	if((pname == 0) || (value == 0)) return 0;
+	spin_lock_irqsave(&prom_lock, flags);
+	ret = prom_nodeops->no_setprop(node, pname, value, size);
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+	return ret;
+}
+
+int prom_inst2pkg(int inst)
+{
+	int node;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&prom_lock, flags);
+	node = (*romvec->pv_v2devops.v2_inst2pkg)(inst);
+	restore_current();
+	spin_unlock_irqrestore(&prom_lock, flags);
+	if (node == -1) return 0;
+	return node;
+}
+
+/* Return 'node' assigned to a particular prom 'path'
+ * FIXME: Should work for v0 as well
+ */
+int prom_pathtoinode(char *path)
+{
+	int node, inst;
+	
+	inst = prom_devopen (path);
+	if (inst == -1) return 0;
+	node = prom_inst2pkg (inst);
+	prom_devclose (inst);
+	if (node == -1) return 0;
+	return node;
+}