diff options
| author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 | 
| commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
| tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/sparc/kernel | |
| download | olio-linux-3.10-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.xz olio-linux-3.10-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip  | |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/sparc/kernel')
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 *)®s, sizeof(regs)) <= 0) +		return; +	prom_apply_obio_ranges(®s, 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, ®s[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, ®s[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 *)®s[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(¤t->thread.float_regs[0], ¤t->thread.fsr, +		       ¤t->thread.fpqueue[0], ¤t->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(¤t->thread.float_regs[0], ¤t->thread.fsr, +		       ¤t->thread.fpqueue[0], ¤t->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], ®s->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], ¤t->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], ¤t->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(¤t->thread.float_regs[0], ¤t->thread.fsr, +		       ¤t->thread.fpqueue[0], ¤t->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(¤t->thread.float_regs[0], ¤t->thread.fsr, +		       ¤t->thread.fpqueue[0], ¤t->thread.fpqdepth); +		if (regs != NULL) { +			regs->psr &= ~(PSR_EF); +			last_task_used_math = NULL; +		} +	} +#endif +	memcpy(&fpregs->pr_fr.pr_regs[0], +	       ¤t->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], +		       ¤t->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(¤t->sighand->siglock); +	saveset = current->blocked; +	siginitset(¤t->blocked, set); +	recalc_sigpending(); +	spin_unlock_irq(¤t->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(¤t->sighand->siglock); +	oldset = current->blocked; +	current->blocked = set; +	recalc_sigpending(); +	spin_unlock_irq(¤t->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(¤t->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(¤t->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(¤t->sighand->siglock); +	current->blocked = set; +	recalc_sigpending(); +	spin_unlock_irq(¤t->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(¤t->sighand->siglock); +	current->blocked = set; +	recalc_sigpending(); +	spin_unlock_irq(¤t->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(®s->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(¤t->sighand->siglock); +	current->blocked = set; +	recalc_sigpending(); +	spin_unlock_irq(¤t->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(¤t->thread.float_regs[0], ¤t->thread.fsr, +		       ¤t->thread.fpqueue[0], ¤t->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(¤t->thread.float_regs[0], ¤t->thread.fsr, +		       ¤t->thread.fpqueue[0], ¤t->thread.fpqdepth); +		last_task_used_math = NULL; +		regs->psr &= ~(PSR_EF); +	} +#endif +	err |= __copy_to_user(&fpu->si_float_regs[0], +			      ¤t->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], +				      ¤t->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], ®s->u_regs[UREG_G1], +			      sizeof(long) * 7); +	err |= __copy_to_user(&(*gr)[SVR4_O0], ®s->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], ®s->u_regs[UREG_G1], +			      sizeof(uint) * 7); +	err |= __copy_to_user(&(*gr)[SVR4_O0], ®s->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(¤t->sighand->siglock); +	current->blocked = set; +	recalc_sigpending(); +	spin_unlock_irq(¤t->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(®s->u_regs[UREG_G1], &(*gr)[SVR4_G1], +			      sizeof(long) * 7); +	err |= __copy_from_user(®s->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(¤t->sighand->siglock); +		sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); +		sigaddset(¤t->blocked, signr); +		recalc_sigpending(); +		spin_unlock_irq(¤t->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 = ¤t->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 *)®isters[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 *)®isters[NPC], ptr, 4); +	*ptr++ = ';'; + +	*ptr++ = hexchars[O7 >> 4]; +	*ptr++ = hexchars[O7 & 0xf]; +	*ptr++ = ':'; +	ptr = mem2hex((char *)®isters[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 *)®isters[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 *)®isters[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" (¤t_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" (¤t_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(¤t->mm->mmap_sem); +	retval = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); +	up_write(¤t->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(¤t->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(¤t->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(¤t->mm->mmap_sem); +	retval = do_mmap(file, addr, len, prot, flags, off); +	up_write(¤t->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(¤t->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(¤t->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(¤t->sighand->siglock); +	old = current->blocked.sig[0]; +	current->blocked.sig[0] |= (blk_mask & _BLOCKABLE); +	recalc_sigpending(); +	spin_unlock_irq(¤t->sighand->siglock); +	return old; +} + +asmlinkage unsigned long sunos_sigsetmask(unsigned long newmask) +{ +	unsigned long retval; + +	spin_lock_irq(¤t->sighand->siglock); +	retval = current->blocked.sig[0]; +	current->blocked.sig[0] = (newmask & _BLOCKABLE); +	recalc_sigpending(); +	spin_unlock_irq(¤t->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(¤t->thread.float_regs[0], ¤t->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(¤t->thread.float_regs[0], ¤t->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(¤t->thread.float_regs[0], ¤t->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 ®s->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  |