aboutsummaryrefslogtreecommitdiff
path: root/arch/sparc/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sparc/kernel')
-rw-r--r--arch/sparc/kernel/Makefile27
-rw-r--r--arch/sparc/kernel/apc.c186
-rw-r--r--arch/sparc/kernel/asm-offsets.c45
-rw-r--r--arch/sparc/kernel/auxio.c138
-rw-r--r--arch/sparc/kernel/cpu.c168
-rw-r--r--arch/sparc/kernel/devices.c160
-rw-r--r--arch/sparc/kernel/ebus.c361
-rw-r--r--arch/sparc/kernel/entry.S1956
-rw-r--r--arch/sparc/kernel/errtbls.c276
-rw-r--r--arch/sparc/kernel/etrap.S321
-rw-r--r--arch/sparc/kernel/head.S1326
-rw-r--r--arch/sparc/kernel/idprom.c108
-rw-r--r--arch/sparc/kernel/init_task.c28
-rw-r--r--arch/sparc/kernel/ioport.c731
-rw-r--r--arch/sparc/kernel/irq.c614
-rw-r--r--arch/sparc/kernel/module.c159
-rw-r--r--arch/sparc/kernel/muldiv.c240
-rw-r--r--arch/sparc/kernel/pcic.c1041
-rw-r--r--arch/sparc/kernel/pmc.c99
-rw-r--r--arch/sparc/kernel/process.c746
-rw-r--r--arch/sparc/kernel/ptrace.c632
-rw-r--r--arch/sparc/kernel/rtrap.S319
-rw-r--r--arch/sparc/kernel/sclow.S86
-rw-r--r--arch/sparc/kernel/semaphore.c155
-rw-r--r--arch/sparc/kernel/setup.c476
-rw-r--r--arch/sparc/kernel/signal.c1181
-rw-r--r--arch/sparc/kernel/smp.c295
-rw-r--r--arch/sparc/kernel/sparc-stub.c724
-rw-r--r--arch/sparc/kernel/sparc_ksyms.c334
-rw-r--r--arch/sparc/kernel/sun4c_irq.c250
-rw-r--r--arch/sparc/kernel/sun4d_irq.c594
-rw-r--r--arch/sparc/kernel/sun4d_smp.c486
-rw-r--r--arch/sparc/kernel/sun4m_irq.c399
-rw-r--r--arch/sparc/kernel/sun4m_smp.c451
-rw-r--r--arch/sparc/kernel/sun4setup.c75
-rw-r--r--arch/sparc/kernel/sunos_asm.S67
-rw-r--r--arch/sparc/kernel/sunos_ioctl.c231
-rw-r--r--arch/sparc/kernel/sys_solaris.c37
-rw-r--r--arch/sparc/kernel/sys_sparc.c485
-rw-r--r--arch/sparc/kernel/sys_sunos.c1194
-rw-r--r--arch/sparc/kernel/systbls.S186
-rw-r--r--arch/sparc/kernel/tadpole.c126
-rw-r--r--arch/sparc/kernel/tick14.c85
-rw-r--r--arch/sparc/kernel/time.c641
-rw-r--r--arch/sparc/kernel/trampoline.S162
-rw-r--r--arch/sparc/kernel/traps.c515
-rw-r--r--arch/sparc/kernel/unaligned.c548
-rw-r--r--arch/sparc/kernel/vmlinux.lds.S103
-rw-r--r--arch/sparc/kernel/windows.c127
-rw-r--r--arch/sparc/kernel/wof.S428
-rw-r--r--arch/sparc/kernel/wuf.S360
51 files changed, 20482 insertions, 0 deletions
diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile
new file mode 100644
index 00000000000..3d22ba2af01
--- /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 00000000000..406dd94afb4
--- /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 00000000000..1f55231f07d
--- /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 00000000000..d3b3648362c
--- /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 00000000000..6a4ebc62193
--- /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 00000000000..fcb0c049c3f
--- /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 00000000000..1754192c69d
--- /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 00000000000..b448166f5da
--- /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 00000000000..bb36f6eadfe
--- /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 00000000000..a8b35bed12a
--- /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 00000000000..42d3de59d19
--- /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 00000000000..2e1b0f6e99d
--- /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 00000000000..fc31de66b1c
--- /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 00000000000..d0f2bd227c4
--- /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 00000000000..410b9a72aba
--- /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 00000000000..7931d6f9281
--- /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 00000000000..37b9a494223
--- /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 00000000000..597d3ff6ad6
--- /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 00000000000..7eca8871ff4
--- /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 00000000000..143fe2f3c1c
--- /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 00000000000..fc4ad69357b
--- /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 00000000000..f7460d897e7
--- /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 00000000000..3a867fc1992
--- /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 00000000000..0c37c1a7cd7
--- /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 00000000000..55352ed85e8
--- /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 00000000000..011ff35057a
--- /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 00000000000..c6e721d8f47
--- /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 00000000000..e84f815e690
--- /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 00000000000..f91b0e8d0dc
--- /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 00000000000..3d6a99073c4
--- /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 00000000000..52621348a56
--- /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 00000000000..cc1fc898495
--- /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 00000000000..39d712c3c80
--- /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 00000000000..f113422a372
--- /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 00000000000..229a52f55f1
--- /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 00000000000..07fe86014fb
--- /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 00000000000..df1c0b31a93
--- /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 00000000000..fb7578554c7
--- /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 00000000000..0cdfc9d294b
--- /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 00000000000..81c894acd0d
--- /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 00000000000..928ffeb0fab
--- /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 00000000000..f476a5f4af6
--- /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 00000000000..fd8005a3e6b
--- /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 00000000000..6486cbf2efe
--- /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 00000000000..2dcdaa1fd8c
--- /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 00000000000..3f451ae6648
--- /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 00000000000..a6330fbc9dd
--- /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 00000000000..38938d2e63a
--- /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 00000000000..9cc93eaa4ab
--- /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 00000000000..083b1215d51
--- /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 00000000000..d1a266bf103
--- /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