diff options
| author | Jiri Kosina <jkosina@suse.cz> | 2011-09-15 15:08:05 +0200 | 
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2011-09-15 15:08:18 +0200 | 
| commit | e060c38434b2caa78efe7cedaff4191040b65a15 (patch) | |
| tree | 407361230bf6733f63d8e788e4b5e6566ee04818 /arch/sparc/kernel | |
| parent | 10e4ac572eeffe5317019bd7330b6058a400dfc2 (diff) | |
| parent | cc39c6a9bbdebfcf1a7dee64d83bf302bc38d941 (diff) | |
| download | olio-linux-3.10-e060c38434b2caa78efe7cedaff4191040b65a15.tar.xz olio-linux-3.10-e060c38434b2caa78efe7cedaff4191040b65a15.zip  | |
Merge branch 'master' into for-next
Fast-forward merge with Linus to be able to merge patches
based on more recent version of the tree.
Diffstat (limited to 'arch/sparc/kernel')
35 files changed, 861 insertions, 315 deletions
diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile index b90b4a1d070..cb85458f89d 100644 --- a/arch/sparc/kernel/Makefile +++ b/arch/sparc/kernel/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_SPARC32)   += sun4m_irq.o sun4c_irq.o sun4d_irq.o  obj-y                   += process_$(BITS).o  obj-y                   += signal_$(BITS).o +obj-y                   += sigutil_$(BITS).o  obj-$(CONFIG_SPARC32)   += ioport.o  obj-y                   += setup_$(BITS).o  obj-y                   += idprom.o diff --git a/arch/sparc/kernel/cpu.c b/arch/sparc/kernel/cpu.c index 138dbbc8dc8..9810fd88105 100644 --- a/arch/sparc/kernel/cpu.c +++ b/arch/sparc/kernel/cpu.c @@ -396,6 +396,7 @@ static int show_cpuinfo(struct seq_file *m, void *__unused)  		   , cpu_data(0).clock_tick  #endif  		); +	cpucap_info(m);  #ifdef CONFIG_SMP  	smp_bogo(m);  #endif @@ -474,11 +475,18 @@ static void __init sun4v_cpu_probe(void)  		sparc_pmu_type = "niagara2";  		break; +	case SUN4V_CHIP_NIAGARA3: +		sparc_cpu_type = "UltraSparc T3 (Niagara3)"; +		sparc_fpu_type = "UltraSparc T3 integrated FPU"; +		sparc_pmu_type = "niagara3"; +		break; +  	default:  		printk(KERN_WARNING "CPU: Unknown sun4v cpu type [%s]\n",  		       prom_cpu_compatible);  		sparc_cpu_type = "Unknown SUN4V CPU";  		sparc_fpu_type = "Unknown SUN4V FPU"; +		sparc_pmu_type = "Unknown SUN4V PMU";  		break;  	}  } diff --git a/arch/sparc/kernel/cpumap.c b/arch/sparc/kernel/cpumap.c index d91fd782743..4197e8d62d4 100644 --- a/arch/sparc/kernel/cpumap.c +++ b/arch/sparc/kernel/cpumap.c @@ -324,6 +324,7 @@ static int iterate_cpu(struct cpuinfo_tree *t, unsigned int root_index)  	switch (sun4v_chip_type) {  	case SUN4V_CHIP_NIAGARA1:  	case SUN4V_CHIP_NIAGARA2: +	case SUN4V_CHIP_NIAGARA3:  		rover_inc_table = niagara_iterate_method;  		break;  	default: diff --git a/arch/sparc/kernel/ds.c b/arch/sparc/kernel/ds.c index dd1342c0a3b..7429b47c3ac 100644 --- a/arch/sparc/kernel/ds.c +++ b/arch/sparc/kernel/ds.c @@ -15,12 +15,15 @@  #include <linux/reboot.h>  #include <linux/cpu.h> +#include <asm/hypervisor.h>  #include <asm/ldc.h>  #include <asm/vio.h>  #include <asm/mdesc.h>  #include <asm/head.h>  #include <asm/irq.h> +#include "kernel.h" +  #define DRV_MODULE_NAME		"ds"  #define PFX DRV_MODULE_NAME	": "  #define DRV_MODULE_VERSION	"1.0" @@ -828,18 +831,32 @@ void ldom_set_var(const char *var, const char *value)  	}  } +static char full_boot_str[256] __attribute__((aligned(32))); +static int reboot_data_supported; +  void ldom_reboot(const char *boot_command)  {  	/* Don't bother with any of this if the boot_command  	 * is empty.  	 */  	if (boot_command && strlen(boot_command)) { -		char full_boot_str[256]; +		unsigned long len;  		strcpy(full_boot_str, "boot ");  		strcpy(full_boot_str + strlen("boot "), boot_command); +		len = strlen(full_boot_str); -		ldom_set_var("reboot-command", full_boot_str); +		if (reboot_data_supported) { +			unsigned long ra = kimage_addr_to_ra(full_boot_str); +			unsigned long hv_ret; + +			hv_ret = sun4v_reboot_data_set(ra, len); +			if (hv_ret != HV_EOK) +				pr_err("SUN4V: Unable to set reboot data " +				       "hv_ret=%lu\n", hv_ret); +		} else { +			ldom_set_var("reboot-command", full_boot_str); +		}  	}  	sun4v_mach_sir();  } @@ -1237,6 +1254,16 @@ static struct vio_driver ds_driver = {  static int __init ds_init(void)  { +	unsigned long hv_ret, major, minor; + +	if (tlb_type == hypervisor) { +		hv_ret = sun4v_get_version(HV_GRP_REBOOT_DATA, &major, &minor); +		if (hv_ret == HV_EOK) { +			pr_info("SUN4V: Reboot data supported (maj=%lu,min=%lu).\n", +				major, minor); +			reboot_data_supported = 1; +		} +	}  	kthread_run(ds_thread, NULL, "kldomd");  	return vio_register_driver(&ds_driver); diff --git a/arch/sparc/kernel/entry.h b/arch/sparc/kernel/entry.h index d1f1361c416..e27f8ea8656 100644 --- a/arch/sparc/kernel/entry.h +++ b/arch/sparc/kernel/entry.h @@ -42,6 +42,20 @@ extern void fpsave(unsigned long *fpregs, unsigned long *fsr,  extern void fpload(unsigned long *fpregs, unsigned long *fsr);  #else /* CONFIG_SPARC32 */ +struct popc_3insn_patch_entry { +	unsigned int	addr; +	unsigned int	insns[3]; +}; +extern struct popc_3insn_patch_entry __popc_3insn_patch, +	__popc_3insn_patch_end; + +struct popc_6insn_patch_entry { +	unsigned int	addr; +	unsigned int	insns[6]; +}; +extern struct popc_6insn_patch_entry __popc_6insn_patch, +	__popc_6insn_patch_end; +  extern void __init per_cpu_patch(void);  extern void __init sun4v_patch(void);  extern void __init boot_cpu_id_too_large(int cpu); diff --git a/arch/sparc/kernel/head_64.S b/arch/sparc/kernel/head_64.S index aa594c792d1..0eac1b2fc53 100644 --- a/arch/sparc/kernel/head_64.S +++ b/arch/sparc/kernel/head_64.S @@ -132,6 +132,8 @@ prom_sun4v_name:  	.asciz	"sun4v"  prom_niagara_prefix:  	.asciz	"SUNW,UltraSPARC-T" +prom_sparc_prefix: +	.asciz	"SPARC-T"  	.align	4  prom_root_compatible:  	.skip	64 @@ -382,6 +384,22 @@ sun4v_chip_type:  90:	ldub	[%g7], %g2  	ldub	[%g1], %g4  	cmp	%g2, %g4 +	bne,pn	%icc, 89f +	 add	%g7, 1, %g7 +	subcc	%g3, 1, %g3 +	bne,pt	%xcc, 90b +	 add	%g1, 1, %g1 +	ba,pt	%xcc, 91f +	 nop + +89:	sethi	%hi(prom_cpu_compatible), %g1 +	or	%g1, %lo(prom_cpu_compatible), %g1 +	sethi	%hi(prom_sparc_prefix), %g7 +	or	%g7, %lo(prom_sparc_prefix), %g7 +	mov	7, %g3 +90:	ldub	[%g7], %g2 +	ldub	[%g1], %g4 +	cmp	%g2, %g4  	bne,pn	%icc, 4f  	 add	%g7, 1, %g7  	subcc	%g3, 1, %g3 @@ -390,6 +408,15 @@ sun4v_chip_type:  	sethi	%hi(prom_cpu_compatible), %g1  	or	%g1, %lo(prom_cpu_compatible), %g1 +	ldub	[%g1 + 7], %g2 +	cmp	%g2, '3' +	be,pt	%xcc, 5f +	 mov	SUN4V_CHIP_NIAGARA3, %g4 +	ba,pt	%xcc, 4f +	 nop + +91:	sethi	%hi(prom_cpu_compatible), %g1 +	or	%g1, %lo(prom_cpu_compatible), %g1  	ldub	[%g1 + 17], %g2  	cmp	%g2, '1'  	be,pt	%xcc, 5f @@ -397,6 +424,7 @@ sun4v_chip_type:  	cmp	%g2, '2'  	be,pt	%xcc, 5f  	 mov	SUN4V_CHIP_NIAGARA2, %g4 +	  4:  	mov	SUN4V_CHIP_UNKNOWN, %g4  5:	sethi	%hi(sun4v_chip_type), %g2 @@ -514,6 +542,9 @@ niagara_tlb_fixup:  	 cmp	%g1, SUN4V_CHIP_NIAGARA2  	be,pt	%xcc, niagara2_patch  	 nop +	cmp	%g1, SUN4V_CHIP_NIAGARA3 +	be,pt	%xcc, niagara2_patch +	 nop  	call	generic_patch_copyops  	 nop @@ -528,7 +559,7 @@ niagara2_patch:  	 nop  	call	niagara_patch_bzero  	 nop -	call	niagara2_patch_pageops +	call	niagara_patch_pageops  	 nop  	ba,a,pt	%xcc, 80f diff --git a/arch/sparc/kernel/hvapi.c b/arch/sparc/kernel/hvapi.c index 7c60afb835b..c2d055d8ba9 100644 --- a/arch/sparc/kernel/hvapi.c +++ b/arch/sparc/kernel/hvapi.c @@ -28,16 +28,23 @@ static struct api_info api_table[] = {  	{ .group = HV_GRP_CORE,		.flags = FLAG_PRE_API	},  	{ .group = HV_GRP_INTR,					},  	{ .group = HV_GRP_SOFT_STATE,				}, +	{ .group = HV_GRP_TM,					},  	{ .group = HV_GRP_PCI,		.flags = FLAG_PRE_API	},  	{ .group = HV_GRP_LDOM,					},  	{ .group = HV_GRP_SVC_CHAN,	.flags = FLAG_PRE_API	},  	{ .group = HV_GRP_NCS,		.flags = FLAG_PRE_API	},  	{ .group = HV_GRP_RNG,					}, +	{ .group = HV_GRP_PBOOT,				}, +	{ .group = HV_GRP_TPM,					}, +	{ .group = HV_GRP_SDIO,					}, +	{ .group = HV_GRP_SDIO_ERR,				}, +	{ .group = HV_GRP_REBOOT_DATA,				},  	{ .group = HV_GRP_NIAG_PERF,	.flags = FLAG_PRE_API	},  	{ .group = HV_GRP_FIRE_PERF,				},  	{ .group = HV_GRP_N2_CPU,				},  	{ .group = HV_GRP_NIU,					},  	{ .group = HV_GRP_VF_CPU,				}, +	{ .group = HV_GRP_KT_CPU,				},  	{ .group = HV_GRP_DIAG,		.flags = FLAG_PRE_API	},  }; diff --git a/arch/sparc/kernel/hvcalls.S b/arch/sparc/kernel/hvcalls.S index 8a5f35ffb15..58d60de4d65 100644 --- a/arch/sparc/kernel/hvcalls.S +++ b/arch/sparc/kernel/hvcalls.S @@ -798,3 +798,10 @@ ENTRY(sun4v_niagara2_setperf)  	retl  	 nop  ENDPROC(sun4v_niagara2_setperf) + +ENTRY(sun4v_reboot_data_set) +	mov	HV_FAST_REBOOT_DATA_SET, %o5 +	ta	HV_FAST_TRAP +	retl +	 nop +ENDPROC(sun4v_reboot_data_set) diff --git a/arch/sparc/kernel/ioport.c b/arch/sparc/kernel/ioport.c index 6ffccd6e015..d0479e2163f 100644 --- a/arch/sparc/kernel/ioport.c +++ b/arch/sparc/kernel/ioport.c @@ -65,9 +65,6 @@ static inline void dma_make_coherent(unsigned long pa, unsigned long len)  }  #endif -static 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); @@ -143,7 +140,11 @@ 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) { +	/* +	 * XXX Too slow. Can have 8192 DVMA pages on sun4m in the worst case. +	 * This probably warrants some sort of hashing. +	*/ +	if ((res = lookup_resource(&sparc_iomap, vaddr)) == NULL) {  		printk("free_io/iounmap: cannot free %lx\n", vaddr);  		return;  	} @@ -319,7 +320,7 @@ static void sbus_free_coherent(struct device *dev, size_t n, void *p,  	struct resource *res;  	struct page *pgv; -	if ((res = _sparc_find_resource(&_sparc_dvma, +	if ((res = lookup_resource(&_sparc_dvma,  	    (unsigned long)p)) == NULL) {  		printk("sbus_free_consistent: cannot free %p\n", p);  		return; @@ -492,7 +493,7 @@ static void pci32_free_coherent(struct device *dev, size_t n, void *p,  {  	struct resource *res; -	if ((res = _sparc_find_resource(&_sparc_dvma, +	if ((res = lookup_resource(&_sparc_dvma,  	    (unsigned long)p)) == NULL) {  		printk("pci_free_consistent: cannot free %p\n", p);  		return; @@ -715,25 +716,6 @@ static const struct file_operations sparc_io_proc_fops = {  };  #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. - */ -static 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; -} -  static void register_proc_sparc_ioport(void)  {  #ifdef CONFIG_PROC_FS diff --git a/arch/sparc/kernel/irq.h b/arch/sparc/kernel/irq.h index 100b9c204e7..42851122bbd 100644 --- a/arch/sparc/kernel/irq.h +++ b/arch/sparc/kernel/irq.h @@ -88,7 +88,7 @@ BTFIXUPDEF_CALL(void, set_irq_udt, int)  #define set_irq_udt(cpu) BTFIXUP_CALL(set_irq_udt)(cpu)  /* All SUN4D IPIs are sent on this IRQ, may be shared with hard IRQs */ -#define SUN4D_IPI_IRQ 14 +#define SUN4D_IPI_IRQ 13  extern void sun4d_ipi_interrupt(void); diff --git a/arch/sparc/kernel/irq_64.c b/arch/sparc/kernel/irq_64.c index 4e78862d12f..0dd8422a469 100644 --- a/arch/sparc/kernel/irq_64.c +++ b/arch/sparc/kernel/irq_64.c @@ -26,7 +26,7 @@  #include <asm/ptrace.h>  #include <asm/processor.h> -#include <asm/atomic.h> +#include <linux/atomic.h>  #include <asm/system.h>  #include <asm/irq.h>  #include <asm/io.h> diff --git a/arch/sparc/kernel/kernel.h b/arch/sparc/kernel/kernel.h index 6f6544cfa0e..fd6c36b1df7 100644 --- a/arch/sparc/kernel/kernel.h +++ b/arch/sparc/kernel/kernel.h @@ -4,12 +4,27 @@  #include <linux/interrupt.h>  #include <asm/traps.h> +#include <asm/head.h> +#include <asm/io.h>  /* cpu.c */  extern const char *sparc_pmu_type;  extern unsigned int fsr_storage;  extern int ncpus_probed; +#ifdef CONFIG_SPARC64 +/* setup_64.c */ +struct seq_file; +extern void cpucap_info(struct seq_file *); + +static inline unsigned long kimage_addr_to_ra(const char *p) +{ +	unsigned long val = (unsigned long) p; + +	return kern_base + (val - KERNBASE); +} +#endif +  #ifdef CONFIG_SPARC32  /* cpu.c */  extern void cpu_probe(void); diff --git a/arch/sparc/kernel/ktlb.S b/arch/sparc/kernel/ktlb.S index 1d361477d7d..79f31036484 100644 --- a/arch/sparc/kernel/ktlb.S +++ b/arch/sparc/kernel/ktlb.S @@ -47,16 +47,16 @@ kvmap_itlb_tsb_miss:  kvmap_itlb_vmalloc_addr:  	KERN_PGTABLE_WALK(%g4, %g5, %g2, kvmap_itlb_longpath) -	KTSB_LOCK_TAG(%g1, %g2, %g7) +	TSB_LOCK_TAG(%g1, %g2, %g7)  	/* Load and check PTE.  */  	ldxa		[%g5] ASI_PHYS_USE_EC, %g5  	mov		1, %g7  	sllx		%g7, TSB_TAG_INVALID_BIT, %g7  	brgez,a,pn	%g5, kvmap_itlb_longpath -	 KTSB_STORE(%g1, %g7) +	 TSB_STORE(%g1, %g7) -	KTSB_WRITE(%g1, %g5, %g6) +	TSB_WRITE(%g1, %g5, %g6)  	/* fallthrough to TLB load */ @@ -102,9 +102,9 @@ kvmap_itlb_longpath:  kvmap_itlb_obp:  	OBP_TRANS_LOOKUP(%g4, %g5, %g2, %g3, kvmap_itlb_longpath) -	KTSB_LOCK_TAG(%g1, %g2, %g7) +	TSB_LOCK_TAG(%g1, %g2, %g7) -	KTSB_WRITE(%g1, %g5, %g6) +	TSB_WRITE(%g1, %g5, %g6)  	ba,pt		%xcc, kvmap_itlb_load  	 nop @@ -112,17 +112,17 @@ kvmap_itlb_obp:  kvmap_dtlb_obp:  	OBP_TRANS_LOOKUP(%g4, %g5, %g2, %g3, kvmap_dtlb_longpath) -	KTSB_LOCK_TAG(%g1, %g2, %g7) +	TSB_LOCK_TAG(%g1, %g2, %g7) -	KTSB_WRITE(%g1, %g5, %g6) +	TSB_WRITE(%g1, %g5, %g6)  	ba,pt		%xcc, kvmap_dtlb_load  	 nop  	.align		32  kvmap_dtlb_tsb4m_load: -	KTSB_LOCK_TAG(%g1, %g2, %g7) -	KTSB_WRITE(%g1, %g5, %g6) +	TSB_LOCK_TAG(%g1, %g2, %g7) +	TSB_WRITE(%g1, %g5, %g6)  	ba,pt		%xcc, kvmap_dtlb_load  	 nop @@ -222,16 +222,16 @@ kvmap_linear_patch:  kvmap_dtlb_vmalloc_addr:  	KERN_PGTABLE_WALK(%g4, %g5, %g2, kvmap_dtlb_longpath) -	KTSB_LOCK_TAG(%g1, %g2, %g7) +	TSB_LOCK_TAG(%g1, %g2, %g7)  	/* Load and check PTE.  */  	ldxa		[%g5] ASI_PHYS_USE_EC, %g5  	mov		1, %g7  	sllx		%g7, TSB_TAG_INVALID_BIT, %g7  	brgez,a,pn	%g5, kvmap_dtlb_longpath -	 KTSB_STORE(%g1, %g7) +	 TSB_STORE(%g1, %g7) -	KTSB_WRITE(%g1, %g5, %g6) +	TSB_WRITE(%g1, %g5, %g6)  	/* fallthrough to TLB load */ diff --git a/arch/sparc/kernel/leon_pci_grpci2.c b/arch/sparc/kernel/leon_pci_grpci2.c index 44dc093ee33..fad1bd07cb5 100644 --- a/arch/sparc/kernel/leon_pci_grpci2.c +++ b/arch/sparc/kernel/leon_pci_grpci2.c @@ -215,7 +215,7 @@ struct grpci2_priv {  DEFINE_SPINLOCK(grpci2_dev_lock);  struct grpci2_priv *grpci2priv; -int grpci2_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +int grpci2_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)  {  	struct grpci2_priv *priv = dev->bus->sysdata;  	int irq_group; diff --git a/arch/sparc/kernel/leon_smp.c b/arch/sparc/kernel/leon_smp.c index fe8fb44c609..1210fde1874 100644 --- a/arch/sparc/kernel/leon_smp.c +++ b/arch/sparc/kernel/leon_smp.c @@ -28,7 +28,7 @@  #include <asm/tlbflush.h>  #include <asm/ptrace.h> -#include <asm/atomic.h> +#include <linux/atomic.h>  #include <asm/irq_regs.h>  #include <asm/traps.h> diff --git a/arch/sparc/kernel/mdesc.c b/arch/sparc/kernel/mdesc.c index 42f28c7420e..acaebb63c4f 100644 --- a/arch/sparc/kernel/mdesc.c +++ b/arch/sparc/kernel/mdesc.c @@ -508,6 +508,8 @@ const char *mdesc_node_name(struct mdesc_handle *hp, u64 node)  }  EXPORT_SYMBOL(mdesc_node_name); +static u64 max_cpus = 64; +  static void __init report_platform_properties(void)  {  	struct mdesc_handle *hp = mdesc_grab(); @@ -543,8 +545,10 @@ static void __init report_platform_properties(void)  	if (v)  		printk("PLATFORM: watchdog-max-timeout [%llu ms]\n", *v);  	v = mdesc_get_property(hp, pn, "max-cpus", NULL); -	if (v) -		printk("PLATFORM: max-cpus [%llu]\n", *v); +	if (v) { +		max_cpus = *v; +		printk("PLATFORM: max-cpus [%llu]\n", max_cpus); +	}  #ifdef CONFIG_SMP  	{ @@ -715,7 +719,7 @@ static void __cpuinit set_proc_ids(struct mdesc_handle *hp)  }  static void __cpuinit get_one_mondo_bits(const u64 *p, unsigned int *mask, -					 unsigned char def) +					 unsigned long def, unsigned long max)  {  	u64 val; @@ -726,6 +730,9 @@ static void __cpuinit get_one_mondo_bits(const u64 *p, unsigned int *mask,  	if (!val || val >= 64)  		goto use_default; +	if (val > max) +		val = max; +  	*mask = ((1U << val) * 64U) - 1U;  	return; @@ -736,19 +743,28 @@ use_default:  static void __cpuinit get_mondo_data(struct mdesc_handle *hp, u64 mp,  				     struct trap_per_cpu *tb)  { +	static int printed;  	const u64 *val;  	val = mdesc_get_property(hp, mp, "q-cpu-mondo-#bits", NULL); -	get_one_mondo_bits(val, &tb->cpu_mondo_qmask, 7); +	get_one_mondo_bits(val, &tb->cpu_mondo_qmask, 7, ilog2(max_cpus * 2));  	val = mdesc_get_property(hp, mp, "q-dev-mondo-#bits", NULL); -	get_one_mondo_bits(val, &tb->dev_mondo_qmask, 7); +	get_one_mondo_bits(val, &tb->dev_mondo_qmask, 7, 8);  	val = mdesc_get_property(hp, mp, "q-resumable-#bits", NULL); -	get_one_mondo_bits(val, &tb->resum_qmask, 6); +	get_one_mondo_bits(val, &tb->resum_qmask, 6, 7);  	val = mdesc_get_property(hp, mp, "q-nonresumable-#bits", NULL); -	get_one_mondo_bits(val, &tb->nonresum_qmask, 2); +	get_one_mondo_bits(val, &tb->nonresum_qmask, 2, 2); +	if (!printed++) { +		pr_info("SUN4V: Mondo queue sizes " +			"[cpu(%u) dev(%u) r(%u) nr(%u)]\n", +			tb->cpu_mondo_qmask + 1, +			tb->dev_mondo_qmask + 1, +			tb->resum_qmask + 1, +			tb->nonresum_qmask + 1); +	}  }  static void * __cpuinit mdesc_iterate_over_cpus(void *(*func)(struct mdesc_handle *, u64, int, void *), void *arg, cpumask_t *mask) diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c index a19f0419547..1aaf8c180be 100644 --- a/arch/sparc/kernel/pcic.c +++ b/arch/sparc/kernel/pcic.c @@ -352,8 +352,8 @@ int __init pcic_probe(void)  	strcpy(pbm->prom_name, namebuf);  	{ -		extern volatile int t_nmi[1]; -		extern int pcic_nmi_trap_patch[1]; +		extern volatile int t_nmi[4]; +		extern int pcic_nmi_trap_patch[4];  		t_nmi[0] = pcic_nmi_trap_patch[0];  		t_nmi[1] = pcic_nmi_trap_patch[1]; diff --git a/arch/sparc/kernel/pcr.c b/arch/sparc/kernel/pcr.c index 8ac23e66008..343b0f9e2e7 100644 --- a/arch/sparc/kernel/pcr.c +++ b/arch/sparc/kernel/pcr.c @@ -80,8 +80,11 @@ static void n2_pcr_write(u64 val)  {  	unsigned long ret; -	ret = sun4v_niagara2_setperf(HV_N2_PERF_SPARC_CTL, val); -	if (ret != HV_EOK) +	if (val & PCR_N2_HTRACE) { +		ret = sun4v_niagara2_setperf(HV_N2_PERF_SPARC_CTL, val); +		if (ret != HV_EOK) +			write_pcr(val); +	} else  		write_pcr(val);  } @@ -106,6 +109,10 @@ static int __init register_perf_hsvc(void)  			perf_hsvc_group = HV_GRP_N2_CPU;  			break; +		case SUN4V_CHIP_NIAGARA3: +			perf_hsvc_group = HV_GRP_KT_CPU; +			break; +  		default:  			return -ENODEV;  		} diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c index 62a034318b1..614da624330 100644 --- a/arch/sparc/kernel/perf_event.c +++ b/arch/sparc/kernel/perf_event.c @@ -22,7 +22,7 @@  #include <asm/stacktrace.h>  #include <asm/cpudata.h>  #include <asm/uaccess.h> -#include <asm/atomic.h> +#include <linux/atomic.h>  #include <asm/nmi.h>  #include <asm/pcr.h> @@ -1343,7 +1343,8 @@ static bool __init supported_pmu(void)  		sparc_pmu = &niagara1_pmu;  		return true;  	} -	if (!strcmp(sparc_pmu_type, "niagara2")) { +	if (!strcmp(sparc_pmu_type, "niagara2") || +	    !strcmp(sparc_pmu_type, "niagara3")) {  		sparc_pmu = &niagara2_pmu;  		return true;  	} diff --git a/arch/sparc/kernel/setup_64.c b/arch/sparc/kernel/setup_64.c index c4dd0999da8..3c5bb784214 100644 --- a/arch/sparc/kernel/setup_64.c +++ b/arch/sparc/kernel/setup_64.c @@ -29,6 +29,7 @@  #include <linux/interrupt.h>  #include <linux/cpu.h>  #include <linux/initrd.h> +#include <linux/module.h>  #include <asm/system.h>  #include <asm/io.h> @@ -46,6 +47,8 @@  #include <asm/mmu.h>  #include <asm/ns87303.h>  #include <asm/btext.h> +#include <asm/elf.h> +#include <asm/mdesc.h>  #ifdef CONFIG_IP_PNP  #include <net/ipconfig.h> @@ -269,6 +272,40 @@ void __init sun4v_patch(void)  	sun4v_hvapi_init();  } +static void __init popc_patch(void) +{ +	struct popc_3insn_patch_entry *p3; +	struct popc_6insn_patch_entry *p6; + +	p3 = &__popc_3insn_patch; +	while (p3 < &__popc_3insn_patch_end) { +		unsigned long i, addr = p3->addr; + +		for (i = 0; i < 3; i++) { +			*(unsigned int *) (addr +  (i * 4)) = p3->insns[i]; +			wmb(); +			__asm__ __volatile__("flush	%0" +					     : : "r" (addr +  (i * 4))); +		} + +		p3++; +	} + +	p6 = &__popc_6insn_patch; +	while (p6 < &__popc_6insn_patch_end) { +		unsigned long i, addr = p6->addr; + +		for (i = 0; i < 6; i++) { +			*(unsigned int *) (addr +  (i * 4)) = p6->insns[i]; +			wmb(); +			__asm__ __volatile__("flush	%0" +					     : : "r" (addr +  (i * 4))); +		} + +		p6++; +	} +} +  #ifdef CONFIG_SMP  void __init boot_cpu_id_too_large(int cpu)  { @@ -278,6 +315,160 @@ void __init boot_cpu_id_too_large(int cpu)  }  #endif +/* On Ultra, we support all of the v8 capabilities. */ +unsigned long sparc64_elf_hwcap = (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | +				   HWCAP_SPARC_SWAP | HWCAP_SPARC_MULDIV | +				   HWCAP_SPARC_V9); +EXPORT_SYMBOL(sparc64_elf_hwcap); + +static const char *hwcaps[] = { +	"flush", "stbar", "swap", "muldiv", "v9", +	"ultra3", "blkinit", "n2", + +	/* These strings are as they appear in the machine description +	 * 'hwcap-list' property for cpu nodes. +	 */ +	"mul32", "div32", "fsmuld", "v8plus", "popc", "vis", "vis2", +	"ASIBlkInit", "fmaf", "vis3", "hpc", "random", "trans", "fjfmau", +	"ima", "cspare", +}; + +void cpucap_info(struct seq_file *m) +{ +	unsigned long caps = sparc64_elf_hwcap; +	int i, printed = 0; + +	seq_puts(m, "cpucaps\t\t: "); +	for (i = 0; i < ARRAY_SIZE(hwcaps); i++) { +		unsigned long bit = 1UL << i; +		if (caps & bit) { +			seq_printf(m, "%s%s", +				   printed ? "," : "", hwcaps[i]); +			printed++; +		} +	} +	seq_putc(m, '\n'); +} + +static void __init report_hwcaps(unsigned long caps) +{ +	int i, printed = 0; + +	printk(KERN_INFO "CPU CAPS: ["); +	for (i = 0; i < ARRAY_SIZE(hwcaps); i++) { +		unsigned long bit = 1UL << i; +		if (caps & bit) { +			printk(KERN_CONT "%s%s", +			       printed ? "," : "", hwcaps[i]); +			if (++printed == 8) { +				printk(KERN_CONT "]\n"); +				printk(KERN_INFO "CPU CAPS: ["); +				printed = 0; +			} +		} +	} +	printk(KERN_CONT "]\n"); +} + +static unsigned long __init mdesc_cpu_hwcap_list(void) +{ +	struct mdesc_handle *hp; +	unsigned long caps = 0; +	const char *prop; +	int len; +	u64 pn; + +	hp = mdesc_grab(); +	if (!hp) +		return 0; + +	pn = mdesc_node_by_name(hp, MDESC_NODE_NULL, "cpu"); +	if (pn == MDESC_NODE_NULL) +		goto out; + +	prop = mdesc_get_property(hp, pn, "hwcap-list", &len); +	if (!prop) +		goto out; + +	while (len) { +		int i, plen; + +		for (i = 0; i < ARRAY_SIZE(hwcaps); i++) { +			unsigned long bit = 1UL << i; + +			if (!strcmp(prop, hwcaps[i])) { +				caps |= bit; +				break; +			} +		} + +		plen = strlen(prop) + 1; +		prop += plen; +		len -= plen; +	} + +out: +	mdesc_release(hp); +	return caps; +} + +/* This yields a mask that user programs can use to figure out what + * instruction set this cpu supports. + */ +static void __init init_sparc64_elf_hwcap(void) +{ +	unsigned long cap = sparc64_elf_hwcap; +	unsigned long mdesc_caps; + +	if (tlb_type == cheetah || tlb_type == cheetah_plus) +		cap |= HWCAP_SPARC_ULTRA3; +	else if (tlb_type == hypervisor) { +		if (sun4v_chip_type == SUN4V_CHIP_NIAGARA1 || +		    sun4v_chip_type == SUN4V_CHIP_NIAGARA2 || +		    sun4v_chip_type == SUN4V_CHIP_NIAGARA3) +			cap |= HWCAP_SPARC_BLKINIT; +		if (sun4v_chip_type == SUN4V_CHIP_NIAGARA2 || +		    sun4v_chip_type == SUN4V_CHIP_NIAGARA3) +			cap |= HWCAP_SPARC_N2; +	} + +	cap |= (AV_SPARC_MUL32 | AV_SPARC_DIV32 | AV_SPARC_V8PLUS); + +	mdesc_caps = mdesc_cpu_hwcap_list(); +	if (!mdesc_caps) { +		if (tlb_type == spitfire) +			cap |= AV_SPARC_VIS; +		if (tlb_type == cheetah || tlb_type == cheetah_plus) +			cap |= AV_SPARC_VIS | AV_SPARC_VIS2; +		if (tlb_type == cheetah_plus) { +			unsigned long impl, ver; + +			__asm__ __volatile__("rdpr %%ver, %0" : "=r" (ver)); +			impl = ((ver >> 32) & 0xffff); +			if (impl == PANTHER_IMPL) +				cap |= AV_SPARC_POPC; +		} +		if (tlb_type == hypervisor) { +			if (sun4v_chip_type == SUN4V_CHIP_NIAGARA1) +				cap |= AV_SPARC_ASI_BLK_INIT; +			if (sun4v_chip_type == SUN4V_CHIP_NIAGARA2 || +			    sun4v_chip_type == SUN4V_CHIP_NIAGARA3) +				cap |= (AV_SPARC_VIS | AV_SPARC_VIS2 | +					AV_SPARC_ASI_BLK_INIT | +					AV_SPARC_POPC); +			if (sun4v_chip_type == SUN4V_CHIP_NIAGARA3) +				cap |= (AV_SPARC_VIS3 | AV_SPARC_HPC | +					AV_SPARC_FMAF); +		} +	} +	sparc64_elf_hwcap = cap | mdesc_caps; + +	report_hwcaps(sparc64_elf_hwcap); + +	if (sparc64_elf_hwcap & AV_SPARC_POPC) +		popc_patch(); +} +  void __init setup_arch(char **cmdline_p)  {  	/* Initialize PROM console and command line. */ @@ -337,6 +528,7 @@ void __init setup_arch(char **cmdline_p)  	init_cur_cpu_trap(current_thread_info());  	paging_init(); +	init_sparc64_elf_hwcap();  }  extern int stop_a_enabled; diff --git a/arch/sparc/kernel/signal32.c b/arch/sparc/kernel/signal32.c index 75fad425e24..1ba95aff5d5 100644 --- a/arch/sparc/kernel/signal32.c +++ b/arch/sparc/kernel/signal32.c @@ -29,6 +29,8 @@  #include <asm/visasm.h>  #include <asm/compat_signal.h> +#include "sigutil.h" +  #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))  /* This magic should be in g_upper[0] for all upper parts @@ -44,14 +46,14 @@ typedef struct {  struct signal_frame32 {  	struct sparc_stackf32	ss;  	__siginfo32_t		info; -	/* __siginfo_fpu32_t * */ u32 fpu_save; +	/* __siginfo_fpu_t * */ u32 fpu_save;  	unsigned int		insns[2];  	unsigned int		extramask[_COMPAT_NSIG_WORDS - 1];  	unsigned int		extra_size; /* Should be sizeof(siginfo_extra_v8plus_t) */  	/* Only valid if (info.si_regs.psr & (PSR_VERS|PSR_IMPL)) == PSR_V8PLUS */  	siginfo_extra_v8plus_t	v8plus; -	__siginfo_fpu_t		fpu_state; -}; +	/* __siginfo_rwin_t * */u32 rwin_save; +} __attribute__((aligned(8)));  typedef struct compat_siginfo{  	int si_signo; @@ -110,18 +112,14 @@ struct rt_signal_frame32 {  	compat_siginfo_t	info;  	struct pt_regs32	regs;  	compat_sigset_t		mask; -	/* __siginfo_fpu32_t * */ u32 fpu_save; +	/* __siginfo_fpu_t * */ u32 fpu_save;  	unsigned int		insns[2];  	stack_t32		stack;  	unsigned int		extra_size; /* Should be sizeof(siginfo_extra_v8plus_t) */  	/* Only valid if (regs.psr & (PSR_VERS|PSR_IMPL)) == PSR_V8PLUS */  	siginfo_extra_v8plus_t	v8plus; -	__siginfo_fpu_t		fpu_state; -}; - -/* Align macros */ -#define SF_ALIGNEDSZ  (((sizeof(struct signal_frame32) + 15) & (~15))) -#define RT_ALIGNEDSZ  (((sizeof(struct rt_signal_frame32) + 15) & (~15))) +	/* __siginfo_rwin_t * */u32 rwin_save; +} __attribute__((aligned(8)));  int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)  { @@ -192,30 +190,13 @@ int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)  	return 0;  } -static int restore_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) -{ -	unsigned long *fpregs = current_thread_info()->fpregs; -	unsigned long fprs; -	int err; -	 -	err = __get_user(fprs, &fpu->si_fprs); -	fprs_write(0); -	regs->tstate &= ~TSTATE_PEF; -	if (fprs & FPRS_DL) -		err |= copy_from_user(fpregs, &fpu->si_float_regs[0], (sizeof(unsigned int) * 32)); -	if (fprs & FPRS_DU) -		err |= copy_from_user(fpregs+16, &fpu->si_float_regs[32], (sizeof(unsigned int) * 32)); -	err |= __get_user(current_thread_info()->xfsr[0], &fpu->si_fsr); -	err |= __get_user(current_thread_info()->gsr[0], &fpu->si_gsr); -	current_thread_info()->fpsaved[0] |= fprs; -	return err; -} -  void do_sigreturn32(struct pt_regs *regs)  {  	struct signal_frame32 __user *sf; +	compat_uptr_t fpu_save; +	compat_uptr_t rwin_save;  	unsigned int psr; -	unsigned pc, npc, fpu_save; +	unsigned pc, npc;  	sigset_t set;  	unsigned seta[_COMPAT_NSIG_WORDS];  	int err, i; @@ -273,8 +254,13 @@ void do_sigreturn32(struct pt_regs *regs)  	pt_regs_clear_syscall(regs);  	err |= __get_user(fpu_save, &sf->fpu_save); -	if (fpu_save) -		err |= restore_fpu_state32(regs, &sf->fpu_state); +	if (!err && fpu_save) +		err |= restore_fpu_state(regs, compat_ptr(fpu_save)); +	err |= __get_user(rwin_save, &sf->rwin_save); +	if (!err && rwin_save) { +		if (restore_rwin_state(compat_ptr(rwin_save))) +			goto segv; +	}  	err |= __get_user(seta[0], &sf->info.si_mask);  	err |= copy_from_user(seta+1, &sf->extramask,  			      (_COMPAT_NSIG_WORDS - 1) * sizeof(unsigned int)); @@ -300,7 +286,9 @@ segv:  asmlinkage void do_rt_sigreturn32(struct pt_regs *regs)  {  	struct rt_signal_frame32 __user *sf; -	unsigned int psr, pc, npc, fpu_save, u_ss_sp; +	unsigned int psr, pc, npc, u_ss_sp; +	compat_uptr_t fpu_save; +	compat_uptr_t rwin_save;  	mm_segment_t old_fs;  	sigset_t set;  	compat_sigset_t seta; @@ -359,8 +347,8 @@ asmlinkage void do_rt_sigreturn32(struct pt_regs *regs)  	pt_regs_clear_syscall(regs);  	err |= __get_user(fpu_save, &sf->fpu_save); -	if (fpu_save) -		err |= restore_fpu_state32(regs, &sf->fpu_state); +	if (!err && fpu_save) +		err |= restore_fpu_state(regs, compat_ptr(fpu_save));  	err |= copy_from_user(&seta, &sf->mask, sizeof(compat_sigset_t));  	err |= __get_user(u_ss_sp, &sf->stack.ss_sp);  	st.ss_sp = compat_ptr(u_ss_sp); @@ -376,6 +364,12 @@ asmlinkage void do_rt_sigreturn32(struct pt_regs *regs)  	do_sigaltstack((stack_t __user *) &st, NULL, (unsigned long)sf);  	set_fs(old_fs); +	err |= __get_user(rwin_save, &sf->rwin_save); +	if (!err && rwin_save) { +		if (restore_rwin_state(compat_ptr(rwin_save))) +			goto segv; +	} +  	switch (_NSIG_WORDS) {  		case 4: set.sig[3] = seta.sig[6] + (((long)seta.sig[7]) << 32);  		case 3: set.sig[2] = seta.sig[4] + (((long)seta.sig[5]) << 32); @@ -433,26 +427,6 @@ static void __user *get_sigframe(struct sigaction *sa, struct pt_regs *regs, uns  	return (void __user *) sp;  } -static int save_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) -{ -	unsigned long *fpregs = current_thread_info()->fpregs; -	unsigned long fprs; -	int err = 0; -	 -	fprs = current_thread_info()->fpsaved[0]; -	if (fprs & FPRS_DL) -		err |= copy_to_user(&fpu->si_float_regs[0], fpregs, -				    (sizeof(unsigned int) * 32)); -	if (fprs & FPRS_DU) -		err |= copy_to_user(&fpu->si_float_regs[32], fpregs+16, -				    (sizeof(unsigned int) * 32)); -	err |= __put_user(current_thread_info()->xfsr[0], &fpu->si_fsr); -	err |= __put_user(current_thread_info()->gsr[0], &fpu->si_gsr); -	err |= __put_user(fprs, &fpu->si_fprs); - -	return err; -} -  /* The I-cache flush instruction only works in the primary ASI, which   * right now is the nucleus, aka. kernel space.   * @@ -515,18 +489,23 @@ static int setup_frame32(struct k_sigaction *ka, struct pt_regs *regs,  			 int signo, sigset_t *oldset)  {  	struct signal_frame32 __user *sf; +	int i, err, wsaved; +	void __user *tail;  	int sigframe_size;  	u32 psr; -	int i, err;  	unsigned int seta[_COMPAT_NSIG_WORDS];  	/* 1. Make sure everything is clean */  	synchronize_user_stack();  	save_and_clear_fpu(); -	sigframe_size = SF_ALIGNEDSZ; -	if (!(current_thread_info()->fpsaved[0] & FPRS_FEF)) -		sigframe_size -= sizeof(__siginfo_fpu_t); +	wsaved = get_thread_wsaved(); + +	sigframe_size = sizeof(*sf); +	if (current_thread_info()->fpsaved[0] & FPRS_FEF) +		sigframe_size += sizeof(__siginfo_fpu_t); +	if (wsaved) +		sigframe_size += sizeof(__siginfo_rwin_t);  	sf = (struct signal_frame32 __user *)  		get_sigframe(&ka->sa, regs, sigframe_size); @@ -534,8 +513,7 @@ static int setup_frame32(struct k_sigaction *ka, struct pt_regs *regs,  	if (invalid_frame_pointer(sf, sigframe_size))  		goto sigill; -	if (get_thread_wsaved() != 0) -		goto sigill; +	tail = (sf + 1);  	/* 2. Save the current process state */  	if (test_thread_flag(TIF_32BIT)) { @@ -560,11 +538,22 @@ static int setup_frame32(struct k_sigaction *ka, struct pt_regs *regs,  			  &sf->v8plus.asi);  	if (psr & PSR_EF) { -		err |= save_fpu_state32(regs, &sf->fpu_state); -		err |= __put_user((u64)&sf->fpu_state, &sf->fpu_save); +		__siginfo_fpu_t __user *fp = tail; +		tail += sizeof(*fp); +		err |= save_fpu_state(regs, fp); +		err |= __put_user((u64)fp, &sf->fpu_save);  	} else {  		err |= __put_user(0, &sf->fpu_save);  	} +	if (wsaved) { +		__siginfo_rwin_t __user *rwp = tail; +		tail += sizeof(*rwp); +		err |= save_rwin_state(wsaved, rwp); +		err |= __put_user((u64)rwp, &sf->rwin_save); +		set_thread_wsaved(0); +	} else { +		err |= __put_user(0, &sf->rwin_save); +	}  	switch (_NSIG_WORDS) {  	case 4: seta[7] = (oldset->sig[3] >> 32); @@ -580,10 +569,21 @@ static int setup_frame32(struct k_sigaction *ka, struct pt_regs *regs,  	err |= __copy_to_user(sf->extramask, seta + 1,  			      (_COMPAT_NSIG_WORDS - 1) * sizeof(unsigned int)); -	err |= copy_in_user((u32 __user *)sf, -			    (u32 __user *)(regs->u_regs[UREG_FP]), -			    sizeof(struct reg_window32)); -	 +	if (!wsaved) { +		err |= copy_in_user((u32 __user *)sf, +				    (u32 __user *)(regs->u_regs[UREG_FP]), +				    sizeof(struct reg_window32)); +	} else { +		struct reg_window *rp; + +		rp = ¤t_thread_info()->reg_window[wsaved - 1]; +		for (i = 0; i < 8; i++) +			err |= __put_user(rp->locals[i], &sf->ss.locals[i]); +		for (i = 0; i < 6; i++) +			err |= __put_user(rp->ins[i], &sf->ss.ins[i]); +		err |= __put_user(rp->ins[6], &sf->ss.fp); +		err |= __put_user(rp->ins[7], &sf->ss.callers_pc); +	}	  	if (err)  		goto sigsegv; @@ -613,7 +613,6 @@ static int setup_frame32(struct k_sigaction *ka, struct pt_regs *regs,  		err |= __put_user(0x91d02010, &sf->insns[1]); /*t 0x10*/  		if (err)  			goto sigsegv; -  		flush_signal_insns(address);  	}  	return 0; @@ -632,18 +631,23 @@ static int setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs,  			    siginfo_t *info)  {  	struct rt_signal_frame32 __user *sf; +	int i, err, wsaved; +	void __user *tail;  	int sigframe_size;  	u32 psr; -	int i, err;  	compat_sigset_t seta;  	/* 1. Make sure everything is clean */  	synchronize_user_stack();  	save_and_clear_fpu(); -	sigframe_size = RT_ALIGNEDSZ; -	if (!(current_thread_info()->fpsaved[0] & FPRS_FEF)) -		sigframe_size -= sizeof(__siginfo_fpu_t); +	wsaved = get_thread_wsaved(); + +	sigframe_size = sizeof(*sf); +	if (current_thread_info()->fpsaved[0] & FPRS_FEF) +		sigframe_size += sizeof(__siginfo_fpu_t); +	if (wsaved) +		sigframe_size += sizeof(__siginfo_rwin_t);  	sf = (struct rt_signal_frame32 __user *)  		get_sigframe(&ka->sa, regs, sigframe_size); @@ -651,8 +655,7 @@ static int setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs,  	if (invalid_frame_pointer(sf, sigframe_size))  		goto sigill; -	if (get_thread_wsaved() != 0) -		goto sigill; +	tail = (sf + 1);  	/* 2. Save the current process state */  	if (test_thread_flag(TIF_32BIT)) { @@ -677,11 +680,22 @@ static int setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs,  			  &sf->v8plus.asi);  	if (psr & PSR_EF) { -		err |= save_fpu_state32(regs, &sf->fpu_state); -		err |= __put_user((u64)&sf->fpu_state, &sf->fpu_save); +		__siginfo_fpu_t __user *fp = tail; +		tail += sizeof(*fp); +		err |= save_fpu_state(regs, fp); +		err |= __put_user((u64)fp, &sf->fpu_save);  	} else {  		err |= __put_user(0, &sf->fpu_save);  	} +	if (wsaved) { +		__siginfo_rwin_t __user *rwp = tail; +		tail += sizeof(*rwp); +		err |= save_rwin_state(wsaved, rwp); +		err |= __put_user((u64)rwp, &sf->rwin_save); +		set_thread_wsaved(0); +	} else { +		err |= __put_user(0, &sf->rwin_save); +	}  	/* Update the siginfo structure.  */  	err |= copy_siginfo_to_user32(&sf->info, info); @@ -703,9 +717,21 @@ static int setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs,  	}  	err |= __copy_to_user(&sf->mask, &seta, sizeof(compat_sigset_t)); -	err |= copy_in_user((u32 __user *)sf, -			    (u32 __user *)(regs->u_regs[UREG_FP]), -			    sizeof(struct reg_window32)); +	if (!wsaved) { +		err |= copy_in_user((u32 __user *)sf, +				    (u32 __user *)(regs->u_regs[UREG_FP]), +				    sizeof(struct reg_window32)); +	} else { +		struct reg_window *rp; + +		rp = ¤t_thread_info()->reg_window[wsaved - 1]; +		for (i = 0; i < 8; i++) +			err |= __put_user(rp->locals[i], &sf->ss.locals[i]); +		for (i = 0; i < 6; i++) +			err |= __put_user(rp->ins[i], &sf->ss.ins[i]); +		err |= __put_user(rp->ins[6], &sf->ss.fp); +		err |= __put_user(rp->ins[7], &sf->ss.callers_pc); +	}  	if (err)  		goto sigsegv; diff --git a/arch/sparc/kernel/signal_32.c b/arch/sparc/kernel/signal_32.c index 5e5c5fd0378..04ede8f04ad 100644 --- a/arch/sparc/kernel/signal_32.c +++ b/arch/sparc/kernel/signal_32.c @@ -26,6 +26,8 @@  #include <asm/pgtable.h>  #include <asm/cacheflush.h>	/* flush_sig_insns */ +#include "sigutil.h" +  #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))  extern void fpsave(unsigned long *fpregs, unsigned long *fsr, @@ -39,8 +41,8 @@ struct signal_frame {  	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; -}; +	__siginfo_rwin_t __user	*rwin_save; +} __attribute__((aligned(8)));  struct rt_signal_frame {  	struct sparc_stackf	ss; @@ -51,8 +53,8 @@ struct rt_signal_frame {  	unsigned int		insns[2];  	stack_t			stack;  	unsigned int		extra_size; /* Should be 0 */ -	__siginfo_fpu_t		fpu_state; -}; +	__siginfo_rwin_t __user	*rwin_save; +} __attribute__((aligned(8)));  /* Align macros */  #define SF_ALIGNEDSZ  (((sizeof(struct signal_frame) + 7) & (~7))) @@ -79,43 +81,13 @@ asmlinkage int sys_sigsuspend(old_sigset_t set)  	return _sigpause_common(set);  } -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; -} -  asmlinkage void do_sigreturn(struct pt_regs *regs)  {  	struct signal_frame __user *sf;  	unsigned long up_psr, pc, npc;  	sigset_t set;  	__siginfo_fpu_t __user *fpu_save; +	__siginfo_rwin_t __user *rwin_save;  	int err;  	/* Always make any pending restarted system calls return -EINTR */ @@ -150,9 +122,11 @@ asmlinkage void do_sigreturn(struct pt_regs *regs)  	pt_regs_clear_syscall(regs);  	err |= __get_user(fpu_save, &sf->fpu_save); -  	if (fpu_save)  		err |= restore_fpu_state(regs, fpu_save); +	err |= __get_user(rwin_save, &sf->rwin_save); +	if (rwin_save) +		err |= restore_rwin_state(rwin_save);  	/* This is pretty much atomic, no amount locking would prevent  	 * the races which exist anyways. @@ -180,6 +154,7 @@ 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; +	__siginfo_rwin_t __user *rwin_save;  	mm_segment_t old_fs;  	sigset_t set;  	stack_t st; @@ -207,8 +182,7 @@ asmlinkage void do_rt_sigreturn(struct pt_regs *regs)  	pt_regs_clear_syscall(regs);  	err |= __get_user(fpu_save, &sf->fpu_save); - -	if (fpu_save) +	if (!err && fpu_save)  		err |= restore_fpu_state(regs, fpu_save);  	err |= __copy_from_user(&set, &sf->mask, sizeof(sigset_t)); @@ -228,6 +202,12 @@ asmlinkage void do_rt_sigreturn(struct pt_regs *regs)  	do_sigaltstack((const stack_t __user *) &st, NULL, (unsigned long)sf);  	set_fs(old_fs); +	err |= __get_user(rwin_save, &sf->rwin_save); +	if (!err && rwin_save) { +		if (restore_rwin_state(rwin_save)) +			goto segv; +	} +  	sigdelsetmask(&set, ~_BLOCKABLE);  	spin_lock_irq(¤t->sighand->siglock);  	current->blocked = set; @@ -280,53 +260,23 @@ static inline void __user *get_sigframe(struct sigaction *sa, struct pt_regs *re  	return (void __user *) sp;  } -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 int setup_frame(struct k_sigaction *ka, struct pt_regs *regs,  		       int signo, sigset_t *oldset)  {  	struct signal_frame __user *sf; -	int sigframe_size, err; +	int sigframe_size, err, wsaved; +	void __user *tail;  	/* 1. Make sure everything is clean */  	synchronize_user_stack(); -	sigframe_size = SF_ALIGNEDSZ; -	if (!used_math()) -		sigframe_size -= sizeof(__siginfo_fpu_t); +	wsaved = current_thread_info()->w_saved; + +	sigframe_size = sizeof(*sf); +	if (used_math()) +		sigframe_size += sizeof(__siginfo_fpu_t); +	if (wsaved) +		sigframe_size += sizeof(__siginfo_rwin_t);  	sf = (struct signal_frame __user *)  		get_sigframe(&ka->sa, regs, sigframe_size); @@ -334,8 +284,7 @@ static int setup_frame(struct k_sigaction *ka, struct pt_regs *regs,  	if (invalid_frame_pointer(sf, sigframe_size))  		goto sigill_and_return; -	if (current_thread_info()->w_saved != 0) -		goto sigill_and_return; +	tail = sf + 1;  	/* 2. Save the current process state */  	err = __copy_to_user(&sf->info.si_regs, regs, sizeof(struct pt_regs)); @@ -343,17 +292,34 @@ static int setup_frame(struct k_sigaction *ka, struct pt_regs *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); +		__siginfo_fpu_t __user *fp = tail; +		tail += sizeof(*fp); +		err |= save_fpu_state(regs, fp); +		err |= __put_user(fp, &sf->fpu_save);  	} else {  		err |= __put_user(0, &sf->fpu_save);  	} +	if (wsaved) { +		__siginfo_rwin_t __user *rwp = tail; +		tail += sizeof(*rwp); +		err |= save_rwin_state(wsaved, rwp); +		err |= __put_user(rwp, &sf->rwin_save); +	} else { +		err |= __put_user(0, &sf->rwin_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_window32)); +	if (!wsaved) { +		err |= __copy_to_user(sf, (char *) regs->u_regs[UREG_FP], +				      sizeof(struct reg_window32)); +	} else { +		struct reg_window32 *rp; + +		rp = ¤t_thread_info()->reg_window[wsaved - 1]; +		err |= __copy_to_user(sf, rp, sizeof(struct reg_window32)); +	}  	if (err)  		goto sigsegv; @@ -399,21 +365,24 @@ static int 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; +	int sigframe_size, wsaved; +	void __user *tail;  	unsigned int psr;  	int err;  	synchronize_user_stack(); -	sigframe_size = RT_ALIGNEDSZ; -	if (!used_math()) -		sigframe_size -= sizeof(__siginfo_fpu_t); +	wsaved = current_thread_info()->w_saved; +	sigframe_size = sizeof(*sf); +	if (used_math()) +		sigframe_size += sizeof(__siginfo_fpu_t); +	if (wsaved) +		sigframe_size += sizeof(__siginfo_rwin_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; +	tail = sf + 1;  	err  = __put_user(regs->pc, &sf->regs.pc);  	err |= __put_user(regs->npc, &sf->regs.npc);  	err |= __put_user(regs->y, &sf->regs.y); @@ -425,11 +394,21 @@ static int setup_rt_frame(struct k_sigaction *ka, struct pt_regs *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); +		__siginfo_fpu_t *fp = tail; +		tail += sizeof(*fp); +		err |= save_fpu_state(regs, fp); +		err |= __put_user(fp, &sf->fpu_save);  	} else {  		err |= __put_user(0, &sf->fpu_save);  	} +	if (wsaved) { +		__siginfo_rwin_t *rwp = tail; +		tail += sizeof(*rwp); +		err |= save_rwin_state(wsaved, rwp); +		err |= __put_user(rwp, &sf->rwin_save); +	} else { +		err |= __put_user(0, &sf->rwin_save); +	}  	err |= __copy_to_user(&sf->mask, &oldset->sig[0], sizeof(sigset_t));  	/* Setup sigaltstack */ @@ -437,8 +416,15 @@ static int setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs,  	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_window32)); +	if (!wsaved) { +		err |= __copy_to_user(sf, (char *) regs->u_regs[UREG_FP], +				      sizeof(struct reg_window32)); +	} else { +		struct reg_window32 *rp; + +		rp = ¤t_thread_info()->reg_window[wsaved - 1]; +		err |= __copy_to_user(sf, rp, sizeof(struct reg_window32)); +	}  	err |= copy_siginfo_to_user(&sf->info, info); diff --git a/arch/sparc/kernel/signal_64.c b/arch/sparc/kernel/signal_64.c index 006fe451588..47509df3b89 100644 --- a/arch/sparc/kernel/signal_64.c +++ b/arch/sparc/kernel/signal_64.c @@ -34,6 +34,7 @@  #include "entry.h"  #include "systbls.h" +#include "sigutil.h"  #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) @@ -236,7 +237,7 @@ struct rt_signal_frame {  	__siginfo_fpu_t __user	*fpu_save;  	stack_t			stack;  	sigset_t		mask; -	__siginfo_fpu_t		fpu_state; +	__siginfo_rwin_t	*rwin_save;  };  static long _sigpause_common(old_sigset_t set) @@ -266,33 +267,12 @@ asmlinkage long sys_sigsuspend(old_sigset_t set)  	return _sigpause_common(set);  } -static inline int -restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) -{ -	unsigned long *fpregs = current_thread_info()->fpregs; -	unsigned long fprs; -	int err; - -	err = __get_user(fprs, &fpu->si_fprs); -	fprs_write(0); -	regs->tstate &= ~TSTATE_PEF; -	if (fprs & FPRS_DL) -		err |= copy_from_user(fpregs, &fpu->si_float_regs[0], -		       	       (sizeof(unsigned int) * 32)); -	if (fprs & FPRS_DU) -		err |= copy_from_user(fpregs+16, &fpu->si_float_regs[32], -		       	       (sizeof(unsigned int) * 32)); -	err |= __get_user(current_thread_info()->xfsr[0], &fpu->si_fsr); -	err |= __get_user(current_thread_info()->gsr[0], &fpu->si_gsr); -	current_thread_info()->fpsaved[0] |= fprs; -	return err; -} -  void do_rt_sigreturn(struct pt_regs *regs)  {  	struct rt_signal_frame __user *sf;  	unsigned long tpc, tnpc, tstate;  	__siginfo_fpu_t __user *fpu_save; +	__siginfo_rwin_t __user *rwin_save;  	sigset_t set;  	int err; @@ -325,8 +305,8 @@ void do_rt_sigreturn(struct pt_regs *regs)  	regs->tstate |= (tstate & (TSTATE_ASI | TSTATE_ICC | TSTATE_XCC));  	err |= __get_user(fpu_save, &sf->fpu_save); -	if (fpu_save) -		err |= restore_fpu_state(regs, &sf->fpu_state); +	if (!err && fpu_save) +		err |= restore_fpu_state(regs, fpu_save);  	err |= __copy_from_user(&set, &sf->mask, sizeof(sigset_t));  	err |= do_sigaltstack(&sf->stack, NULL, (unsigned long)sf); @@ -334,6 +314,12 @@ void do_rt_sigreturn(struct pt_regs *regs)  	if (err)  		goto segv; +	err |= __get_user(rwin_save, &sf->rwin_save); +	if (!err && rwin_save) { +		if (restore_rwin_state(rwin_save)) +			goto segv; +	} +  	regs->tpc = tpc;  	regs->tnpc = tnpc; @@ -351,34 +337,13 @@ segv:  }  /* Checks if the fp is valid */ -static int invalid_frame_pointer(void __user *fp, int fplen) +static int invalid_frame_pointer(void __user *fp)  {  	if (((unsigned long) fp) & 15)  		return 1;  	return 0;  } -static inline int -save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) -{ -	unsigned long *fpregs = current_thread_info()->fpregs; -	unsigned long fprs; -	int err = 0; -	 -	fprs = current_thread_info()->fpsaved[0]; -	if (fprs & FPRS_DL) -		err |= copy_to_user(&fpu->si_float_regs[0], fpregs, -				    (sizeof(unsigned int) * 32)); -	if (fprs & FPRS_DU) -		err |= copy_to_user(&fpu->si_float_regs[32], fpregs+16, -				    (sizeof(unsigned int) * 32)); -	err |= __put_user(current_thread_info()->xfsr[0], &fpu->si_fsr); -	err |= __put_user(current_thread_info()->gsr[0], &fpu->si_gsr); -	err |= __put_user(fprs, &fpu->si_fprs); - -	return err; -} -  static inline void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, unsigned long framesize)  {  	unsigned long sp = regs->u_regs[UREG_FP] + STACK_BIAS; @@ -414,34 +379,48 @@ 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, err; +	int wsaved, err, sf_size; +	void __user *tail;  	/* 1. Make sure everything is clean */  	synchronize_user_stack();  	save_and_clear_fpu(); -	sigframe_size = sizeof(struct rt_signal_frame); -	if (!(current_thread_info()->fpsaved[0] & FPRS_FEF)) -		sigframe_size -= sizeof(__siginfo_fpu_t); +	wsaved = get_thread_wsaved(); +	sf_size = sizeof(struct rt_signal_frame); +	if (current_thread_info()->fpsaved[0] & FPRS_FEF) +		sf_size += sizeof(__siginfo_fpu_t); +	if (wsaved) +		sf_size += sizeof(__siginfo_rwin_t);  	sf = (struct rt_signal_frame __user *) -		get_sigframe(ka, regs, sigframe_size); -	 -	if (invalid_frame_pointer (sf, sigframe_size)) -		goto sigill; +		get_sigframe(ka, regs, sf_size); -	if (get_thread_wsaved() != 0) +	if (invalid_frame_pointer (sf))  		goto sigill; +	tail = (sf + 1); +  	/* 2. Save the current process state */  	err = copy_to_user(&sf->regs, regs, sizeof (*regs));  	if (current_thread_info()->fpsaved[0] & FPRS_FEF) { -		err |= save_fpu_state(regs, &sf->fpu_state); -		err |= __put_user((u64)&sf->fpu_state, &sf->fpu_save); +		__siginfo_fpu_t __user *fpu_save = tail; +		tail += sizeof(__siginfo_fpu_t); +		err |= save_fpu_state(regs, fpu_save); +		err |= __put_user((u64)fpu_save, &sf->fpu_save);  	} else {  		err |= __put_user(0, &sf->fpu_save);  	} +	if (wsaved) { +		__siginfo_rwin_t __user *rwin_save = tail; +		tail += sizeof(__siginfo_rwin_t); +		err |= save_rwin_state(wsaved, rwin_save); +		err |= __put_user((u64)rwin_save, &sf->rwin_save); +		set_thread_wsaved(0); +	} else { +		err |= __put_user(0, &sf->rwin_save); +	}  	/* Setup sigaltstack */  	err |= __put_user(current->sas_ss_sp, &sf->stack.ss_sp); @@ -450,10 +429,17 @@ setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs,  	err |= copy_to_user(&sf->mask, oldset, sizeof(sigset_t)); -	err |= copy_in_user((u64 __user *)sf, -			    (u64 __user *)(regs->u_regs[UREG_FP]+STACK_BIAS), -			    sizeof(struct reg_window)); +	if (!wsaved) { +		err |= copy_in_user((u64 __user *)sf, +				    (u64 __user *)(regs->u_regs[UREG_FP] + +						   STACK_BIAS), +				    sizeof(struct reg_window)); +	} else { +		struct reg_window *rp; +		rp = ¤t_thread_info()->reg_window[wsaved - 1]; +		err |= copy_to_user(sf, rp, sizeof(struct reg_window)); +	}  	if (info)  		err |= copy_siginfo_to_user(&sf->info, info);  	else { diff --git a/arch/sparc/kernel/sigutil.h b/arch/sparc/kernel/sigutil.h new file mode 100644 index 00000000000..d223aa432bb --- /dev/null +++ b/arch/sparc/kernel/sigutil.h @@ -0,0 +1,9 @@ +#ifndef _SIGUTIL_H +#define _SIGUTIL_H + +int save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu); +int restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu); +int save_rwin_state(int wsaved, __siginfo_rwin_t __user *rwin); +int restore_rwin_state(__siginfo_rwin_t __user *rp); + +#endif /* _SIGUTIL_H */ diff --git a/arch/sparc/kernel/sigutil_32.c b/arch/sparc/kernel/sigutil_32.c new file mode 100644 index 00000000000..35c7897b009 --- /dev/null +++ b/arch/sparc/kernel/sigutil_32.c @@ -0,0 +1,120 @@ +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/thread_info.h> +#include <linux/uaccess.h> +#include <linux/sched.h> + +#include <asm/sigcontext.h> +#include <asm/fpumacro.h> +#include <asm/ptrace.h> + +#include "sigutil.h" + +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; +} + +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; +} + +int save_rwin_state(int wsaved, __siginfo_rwin_t __user *rwin) +{ +	int i, err = __put_user(wsaved, &rwin->wsaved); + +	for (i = 0; i < wsaved; i++) { +		struct reg_window32 *rp; +		unsigned long fp; + +		rp = ¤t_thread_info()->reg_window[i]; +		fp = current_thread_info()->rwbuf_stkptrs[i]; +		err |= copy_to_user(&rwin->reg_window[i], rp, +				    sizeof(struct reg_window32)); +		err |= __put_user(fp, &rwin->rwbuf_stkptrs[i]); +	} +	return err; +} + +int restore_rwin_state(__siginfo_rwin_t __user *rp) +{ +	struct thread_info *t = current_thread_info(); +	int i, wsaved, err; + +	__get_user(wsaved, &rp->wsaved); +	if (wsaved > NSWINS) +		return -EFAULT; + +	err = 0; +	for (i = 0; i < wsaved; i++) { +		err |= copy_from_user(&t->reg_window[i], +				      &rp->reg_window[i], +				      sizeof(struct reg_window32)); +		err |= __get_user(t->rwbuf_stkptrs[i], +				  &rp->rwbuf_stkptrs[i]); +	} +	if (err) +		return err; + +	t->w_saved = wsaved; +	synchronize_user_stack(); +	if (t->w_saved) +		return -EFAULT; +	return 0; + +} diff --git a/arch/sparc/kernel/sigutil_64.c b/arch/sparc/kernel/sigutil_64.c new file mode 100644 index 00000000000..e7dc508c38e --- /dev/null +++ b/arch/sparc/kernel/sigutil_64.c @@ -0,0 +1,93 @@ +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/thread_info.h> +#include <linux/uaccess.h> + +#include <asm/sigcontext.h> +#include <asm/fpumacro.h> +#include <asm/ptrace.h> + +#include "sigutil.h" + +int save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) +{ +	unsigned long *fpregs = current_thread_info()->fpregs; +	unsigned long fprs; +	int err = 0; +	 +	fprs = current_thread_info()->fpsaved[0]; +	if (fprs & FPRS_DL) +		err |= copy_to_user(&fpu->si_float_regs[0], fpregs, +				    (sizeof(unsigned int) * 32)); +	if (fprs & FPRS_DU) +		err |= copy_to_user(&fpu->si_float_regs[32], fpregs+16, +				    (sizeof(unsigned int) * 32)); +	err |= __put_user(current_thread_info()->xfsr[0], &fpu->si_fsr); +	err |= __put_user(current_thread_info()->gsr[0], &fpu->si_gsr); +	err |= __put_user(fprs, &fpu->si_fprs); + +	return err; +} + +int restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) +{ +	unsigned long *fpregs = current_thread_info()->fpregs; +	unsigned long fprs; +	int err; + +	err = __get_user(fprs, &fpu->si_fprs); +	fprs_write(0); +	regs->tstate &= ~TSTATE_PEF; +	if (fprs & FPRS_DL) +		err |= copy_from_user(fpregs, &fpu->si_float_regs[0], +		       	       (sizeof(unsigned int) * 32)); +	if (fprs & FPRS_DU) +		err |= copy_from_user(fpregs+16, &fpu->si_float_regs[32], +		       	       (sizeof(unsigned int) * 32)); +	err |= __get_user(current_thread_info()->xfsr[0], &fpu->si_fsr); +	err |= __get_user(current_thread_info()->gsr[0], &fpu->si_gsr); +	current_thread_info()->fpsaved[0] |= fprs; +	return err; +} + +int save_rwin_state(int wsaved, __siginfo_rwin_t __user *rwin) +{ +	int i, err = __put_user(wsaved, &rwin->wsaved); + +	for (i = 0; i < wsaved; i++) { +		struct reg_window *rp = ¤t_thread_info()->reg_window[i]; +		unsigned long fp = current_thread_info()->rwbuf_stkptrs[i]; + +		err |= copy_to_user(&rwin->reg_window[i], rp, +				    sizeof(struct reg_window)); +		err |= __put_user(fp, &rwin->rwbuf_stkptrs[i]); +	} +	return err; +} + +int restore_rwin_state(__siginfo_rwin_t __user *rp) +{ +	struct thread_info *t = current_thread_info(); +	int i, wsaved, err; + +	__get_user(wsaved, &rp->wsaved); +	if (wsaved > NSWINS) +		return -EFAULT; + +	err = 0; +	for (i = 0; i < wsaved; i++) { +		err |= copy_from_user(&t->reg_window[i], +				      &rp->reg_window[i], +				      sizeof(struct reg_window)); +		err |= __get_user(t->rwbuf_stkptrs[i], +				  &rp->rwbuf_stkptrs[i]); +	} +	if (err) +		return err; + +	set_thread_wsaved(wsaved); +	synchronize_user_stack(); +	if (get_thread_wsaved()) +		return -EFAULT; +	return 0; +} diff --git a/arch/sparc/kernel/smp_32.c b/arch/sparc/kernel/smp_32.c index 21b125341bf..f671e7fd6dd 100644 --- a/arch/sparc/kernel/smp_32.c +++ b/arch/sparc/kernel/smp_32.c @@ -22,7 +22,7 @@  #include <linux/delay.h>  #include <asm/ptrace.h> -#include <asm/atomic.h> +#include <linux/atomic.h>  #include <asm/irq.h>  #include <asm/page.h> diff --git a/arch/sparc/kernel/smp_64.c b/arch/sparc/kernel/smp_64.c index 99cb17251bb..4a442c32e11 100644 --- a/arch/sparc/kernel/smp_64.c +++ b/arch/sparc/kernel/smp_64.c @@ -28,7 +28,7 @@  #include <asm/head.h>  #include <asm/ptrace.h> -#include <asm/atomic.h> +#include <linux/atomic.h>  #include <asm/tlbflush.h>  #include <asm/mmu_context.h>  #include <asm/cpudata.h> diff --git a/arch/sparc/kernel/sparc_ksyms_64.c b/arch/sparc/kernel/sparc_ksyms_64.c index 372ad59c4cb..83b47ab02d9 100644 --- a/arch/sparc/kernel/sparc_ksyms_64.c +++ b/arch/sparc/kernel/sparc_ksyms_64.c @@ -8,6 +8,7 @@  #include <linux/module.h>  #include <linux/pci.h>  #include <linux/init.h> +#include <linux/bitops.h>  #include <asm/system.h>  #include <asm/cpudata.h> @@ -38,5 +39,15 @@ EXPORT_SYMBOL(sun4v_niagara_setperf);  EXPORT_SYMBOL(sun4v_niagara2_getperf);  EXPORT_SYMBOL(sun4v_niagara2_setperf); +/* from hweight.S */ +EXPORT_SYMBOL(__arch_hweight8); +EXPORT_SYMBOL(__arch_hweight16); +EXPORT_SYMBOL(__arch_hweight32); +EXPORT_SYMBOL(__arch_hweight64); + +/* from ffs_ffz.S */ +EXPORT_SYMBOL(ffs); +EXPORT_SYMBOL(__ffs); +  /* Exporting a symbol from /init/main.c */  EXPORT_SYMBOL(saved_command_line); diff --git a/arch/sparc/kernel/sstate.c b/arch/sparc/kernel/sstate.c index 8cdbe5946b4..c59af546f52 100644 --- a/arch/sparc/kernel/sstate.c +++ b/arch/sparc/kernel/sstate.c @@ -14,14 +14,9 @@  #include <asm/head.h>  #include <asm/io.h> -static int hv_supports_soft_state; - -static unsigned long kimage_addr_to_ra(const char *p) -{ -	unsigned long val = (unsigned long) p; +#include "kernel.h" -	return kern_base + (val - KERNBASE); -} +static int hv_supports_soft_state;  static void do_set_sstate(unsigned long state, const char *msg)  { diff --git a/arch/sparc/kernel/sys32.S b/arch/sparc/kernel/sys32.S index 44e5faf1ad5..d97f3eb72e0 100644 --- a/arch/sparc/kernel/sys32.S +++ b/arch/sparc/kernel/sys32.S @@ -81,7 +81,6 @@ SIGN2(sys32_fadvise64, compat_sys_fadvise64, %o0, %o4)  SIGN2(sys32_fadvise64_64, compat_sys_fadvise64_64, %o0, %o5)  SIGN2(sys32_bdflush, sys_bdflush, %o0, %o1)  SIGN1(sys32_mlockall, sys_mlockall, %o0) -SIGN1(sys32_nfsservctl, compat_sys_nfsservctl, %o0)  SIGN1(sys32_clock_nanosleep, compat_sys_clock_nanosleep, %o1)  SIGN1(sys32_timer_settime, compat_sys_timer_settime, %o1)  SIGN1(sys32_io_submit, compat_sys_io_submit, %o1) diff --git a/arch/sparc/kernel/systbls_32.S b/arch/sparc/kernel/systbls_32.S index 6e492d59f6b..09d8ec45445 100644 --- a/arch/sparc/kernel/systbls_32.S +++ b/arch/sparc/kernel/systbls_32.S @@ -67,7 +67,7 @@ sys_call_table:  /*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 sys_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl +/*250*/	.long sys_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_ni_syscall  /*255*/	.long sys_sync_file_range, 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 diff --git a/arch/sparc/kernel/systbls_64.S b/arch/sparc/kernel/systbls_64.S index f566518483b..edbec45d468 100644 --- a/arch/sparc/kernel/systbls_64.S +++ b/arch/sparc/kernel/systbls_64.S @@ -68,7 +68,7 @@ sys_call_table32:  	.word compat_sys_fstatfs64, sys_llseek, sys_mlock, sys_munlock, sys32_mlockall  /*240*/	.word sys_munlockall, sys32_sched_setparam, sys32_sched_getparam, sys32_sched_setscheduler, sys32_sched_getscheduler  	.word sys_sched_yield, sys32_sched_get_priority_max, sys32_sched_get_priority_min, sys32_sched_rr_get_interval, compat_sys_nanosleep -/*250*/	.word sys_mremap, compat_sys_sysctl, sys32_getsid, sys_fdatasync, sys32_nfsservctl +/*250*/	.word sys_mremap, compat_sys_sysctl, sys32_getsid, sys_fdatasync, sys_nis_syscall  	.word sys32_sync_file_range, compat_sys_clock_settime, compat_sys_clock_gettime, compat_sys_clock_getres, sys32_clock_nanosleep  /*260*/	.word compat_sys_sched_getaffinity, compat_sys_sched_setaffinity, sys32_timer_settime, compat_sys_timer_gettime, sys_timer_getoverrun  	.word sys_timer_delete, compat_sys_timer_create, sys_ni_syscall, compat_sys_io_setup, sys_io_destroy @@ -145,7 +145,7 @@ sys_call_table:  	.word sys_fstatfs64, sys_llseek, sys_mlock, sys_munlock, sys_mlockall  /*240*/	.word sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_sched_setscheduler, sys_sched_getscheduler  	.word sys_sched_yield, sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, sys_nanosleep -/*250*/	.word sys_64_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl +/*250*/	.word sys_64_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nis_syscall  	.word sys_sync_file_range, sys_clock_settime, sys_clock_gettime, sys_clock_getres, sys_clock_nanosleep  /*260*/	.word sys_sched_getaffinity, sys_sched_setaffinity, sys_timer_settime, sys_timer_gettime, sys_timer_getoverrun  	.word sys_timer_delete, sys_timer_create, sys_ni_syscall, sys_io_setup, sys_io_destroy diff --git a/arch/sparc/kernel/unaligned_64.c b/arch/sparc/kernel/unaligned_64.c index 35cff1673aa..76e4ac1a13e 100644 --- a/arch/sparc/kernel/unaligned_64.c +++ b/arch/sparc/kernel/unaligned_64.c @@ -22,6 +22,7 @@  #include <linux/bitops.h>  #include <linux/perf_event.h>  #include <linux/ratelimit.h> +#include <linux/bitops.h>  #include <asm/fpumacro.h>  enum direction { @@ -373,16 +374,11 @@ asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn)  	}  } -static char popc_helper[] = { -0, 1, 1, 2, 1, 2, 2, 3, -1, 2, 2, 3, 2, 3, 3, 4,  -}; -  int handle_popc(u32 insn, struct pt_regs *regs)  { -	u64 value; -	int ret, i, rd = ((insn >> 25) & 0x1f);  	int from_kernel = (regs->tstate & TSTATE_PRIV) != 0; +	int ret, rd = ((insn >> 25) & 0x1f); +	u64 value;  	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0);  	if (insn & 0x2000) { @@ -392,10 +388,7 @@ int handle_popc(u32 insn, struct pt_regs *regs)  		maybe_flush_windows(0, insn & 0x1f, rd, from_kernel);  		value = fetch_reg(insn & 0x1f, regs);  	} -	for (ret = 0, i = 0; i < 16; i++) { -		ret += popc_helper[value & 0xf]; -		value >>= 4; -	} +	ret = hweight64(value);  	if (rd < 16) {  		if (rd)  			regs->u_regs[rd] = ret; diff --git a/arch/sparc/kernel/vmlinux.lds.S b/arch/sparc/kernel/vmlinux.lds.S index c0220759003..0e1605697b4 100644 --- a/arch/sparc/kernel/vmlinux.lds.S +++ b/arch/sparc/kernel/vmlinux.lds.S @@ -107,7 +107,26 @@ SECTIONS  		*(.sun4v_2insn_patch)  		__sun4v_2insn_patch_end = .;  	} - +	.swapper_tsb_phys_patch : { +		__swapper_tsb_phys_patch = .; +		*(.swapper_tsb_phys_patch) +		__swapper_tsb_phys_patch_end = .; +	} +	.swapper_4m_tsb_phys_patch : { +		__swapper_4m_tsb_phys_patch = .; +		*(.swapper_4m_tsb_phys_patch) +		__swapper_4m_tsb_phys_patch_end = .; +	} +	.popc_3insn_patch : { +		__popc_3insn_patch = .; +		*(.popc_3insn_patch) +		__popc_3insn_patch_end = .; +	} +	.popc_6insn_patch : { +		__popc_6insn_patch = .; +		*(.popc_6insn_patch) +		__popc_6insn_patch_end = .; +	}  	PERCPU_SECTION(SMP_CACHE_BYTES)  	. = ALIGN(PAGE_SIZE);  |