diff options
Diffstat (limited to 'arch/sparc')
52 files changed, 733 insertions, 513 deletions
diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index de58c02633b4..c3ea215334f6 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -36,6 +36,7 @@ config SPARC64 select HAVE_KRETPROBES select HAVE_KPROBES select HAVE_LMB + select HAVE_SYSCALL_WRAPPERS select USE_GENERIC_SMP_HELPERS if SMP select RTC_DRV_CMOS select RTC_DRV_BQ4802 diff --git a/arch/sparc/configs/sparc64_defconfig b/arch/sparc/configs/sparc64_defconfig index cde19ae78f5a..ade49941def2 100644 --- a/arch/sparc/configs/sparc64_defconfig +++ b/arch/sparc/configs/sparc64_defconfig @@ -867,8 +867,8 @@ CONFIG_I2C_ALGOBIT=y # Miscellaneous I2C Chip support # # CONFIG_DS1682 is not set -# CONFIG_AT24 is not set -# CONFIG_SENSORS_EEPROM is not set +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_LEGACY is not set # CONFIG_SENSORS_PCF8574 is not set # CONFIG_PCF8575 is not set # CONFIG_SENSORS_PCA9539 is not set diff --git a/arch/sparc/include/asm/Kbuild b/arch/sparc/include/asm/Kbuild index 95e38a43dff0..deeb0fba8029 100644 --- a/arch/sparc/include/asm/Kbuild +++ b/arch/sparc/include/asm/Kbuild @@ -17,4 +17,3 @@ header-y += traps.h header-y += uctx.h header-y += utrap.h header-y += watchdog.h -header-y += swab.h diff --git a/arch/sparc/include/asm/byteorder.h b/arch/sparc/include/asm/byteorder.h index 48a047cd6fa9..ccc1b6b7de6c 100644 --- a/arch/sparc/include/asm/byteorder.h +++ b/arch/sparc/include/asm/byteorder.h @@ -1,7 +1,6 @@ #ifndef _SPARC_BYTEORDER_H #define _SPARC_BYTEORDER_H -#include <asm/swab.h> #include <linux/byteorder/big_endian.h> #endif /* _SPARC_BYTEORDER_H */ diff --git a/arch/sparc/include/asm/compat.h b/arch/sparc/include/asm/compat.h index f260b58f5ce9..0e706257918f 100644 --- a/arch/sparc/include/asm/compat.h +++ b/arch/sparc/include/asm/compat.h @@ -240,4 +240,9 @@ struct compat_shmid64_ds { unsigned int __unused2; }; +static inline int is_compat_task(void) +{ + return test_thread_flag(TIF_32BIT); +} + #endif /* _ASM_SPARC64_COMPAT_H */ diff --git a/arch/sparc/include/asm/cpudata_64.h b/arch/sparc/include/asm/cpudata_64.h index 7da7c13d23c4..a11b89ee9ef8 100644 --- a/arch/sparc/include/asm/cpudata_64.h +++ b/arch/sparc/include/asm/cpudata_64.h @@ -17,7 +17,7 @@ typedef struct { /* Dcache line 1 */ unsigned int __softirq_pending; /* must be 1st, see rtrap.S */ - unsigned int __pad0; + unsigned int __nmi_count; unsigned long clock_tick; /* %tick's per second */ unsigned long __pad; unsigned int __pad1; diff --git a/arch/sparc/include/asm/irq_64.h b/arch/sparc/include/asm/irq_64.h index d47d4a1955a9..1934f2cbf513 100644 --- a/arch/sparc/include/asm/irq_64.h +++ b/arch/sparc/include/asm/irq_64.h @@ -66,9 +66,6 @@ extern void virt_irq_free(unsigned int virt_irq); extern void __init init_IRQ(void); extern void fixup_irqs(void); -extern int register_perfctr_intr(void (*handler)(struct pt_regs *)); -extern void release_perfctr_intr(void (*handler)(struct pt_regs *)); - static inline void set_softint(unsigned long bits) { __asm__ __volatile__("wr %0, 0x0, %%set_softint" @@ -98,5 +95,6 @@ void __trigger_all_cpu_backtrace(void); extern void *hardirq_stack[NR_CPUS]; extern void *softirq_stack[NR_CPUS]; #define __ARCH_HAS_DO_SOFTIRQ +#define ARCH_HAS_NMI_WATCHDOG #endif diff --git a/arch/sparc/include/asm/kdebug_64.h b/arch/sparc/include/asm/kdebug_64.h index f905b773235a..feb3578e12c4 100644 --- a/arch/sparc/include/asm/kdebug_64.h +++ b/arch/sparc/include/asm/kdebug_64.h @@ -14,6 +14,8 @@ enum die_val { DIE_TRAP, DIE_TRAP_TL1, DIE_CALL, + DIE_NMI, + DIE_NMIWATCHDOG, }; #endif diff --git a/arch/sparc/include/asm/nmi.h b/arch/sparc/include/asm/nmi.h new file mode 100644 index 000000000000..fbd546dd4feb --- /dev/null +++ b/arch/sparc/include/asm/nmi.h @@ -0,0 +1,10 @@ +#ifndef __NMI_H +#define __NMI_H + +extern int __init nmi_init(void); +extern void perfctr_irq(int irq, struct pt_regs *regs); +extern void nmi_adjust_hz(unsigned int new_hz); + +extern int nmi_usable; + +#endif /* __NMI_H */ diff --git a/arch/sparc/include/asm/pcr.h b/arch/sparc/include/asm/pcr.h new file mode 100644 index 000000000000..a2f5c61f924e --- /dev/null +++ b/arch/sparc/include/asm/pcr.h @@ -0,0 +1,46 @@ +#ifndef __PCR_H +#define __PCR_H + +struct pcr_ops { + u64 (*read)(void); + void (*write)(u64); +}; +extern const struct pcr_ops *pcr_ops; + +extern void deferred_pcr_work_irq(int irq, struct pt_regs *regs); +extern void schedule_deferred_pcr_work(void); + +#define PCR_PIC_PRIV 0x00000001 /* PIC access is privileged */ +#define PCR_STRACE 0x00000002 /* Trace supervisor events */ +#define PCR_UTRACE 0x00000004 /* Trace user events */ +#define PCR_N2_HTRACE 0x00000008 /* Trace hypervisor events */ +#define PCR_N2_TOE_OV0 0x00000010 /* Trap if PIC 0 overflows */ +#define PCR_N2_TOE_OV1 0x00000020 /* Trap if PIC 1 overflows */ +#define PCR_N2_MASK0 0x00003fc0 +#define PCR_N2_MASK0_SHIFT 6 +#define PCR_N2_SL0 0x0003c000 +#define PCR_N2_SL0_SHIFT 14 +#define PCR_N2_OV0 0x00040000 +#define PCR_N2_MASK1 0x07f80000 +#define PCR_N2_MASK1_SHIFT 19 +#define PCR_N2_SL1 0x78000000 +#define PCR_N2_SL1_SHIFT 27 +#define PCR_N2_OV1 0x80000000 + +extern unsigned int picl_shift; + +/* In order to commonize as much of the implementation as + * possible, we use PICH as our counter. Mostly this is + * to accomodate Niagara-1 which can only count insn cycles + * in PICH. + */ +static inline u64 picl_value(unsigned int nmi_hz) +{ + u32 delta = local_cpu_data().clock_tick / (nmi_hz << picl_shift); + + return ((u64)((0 - delta) & 0xffffffff)) << 32; +} + +extern u64 pcr_enable; + +#endif /* __PCR_H */ diff --git a/arch/sparc/include/asm/pil.h b/arch/sparc/include/asm/pil.h index d573820c0ff4..266937030546 100644 --- a/arch/sparc/include/asm/pil.h +++ b/arch/sparc/include/asm/pil.h @@ -23,6 +23,8 @@ #define PIL_SMP_CTX_NEW_VERSION 4 #define PIL_DEVICE_IRQ 5 #define PIL_SMP_CALL_FUNC_SNGL 6 +#define PIL_DEFERRED_PCR_WORK 7 +#define PIL_KGDB_CAPTURE 8 #define PIL_NORMAL_MAX 14 #define PIL_NMI 15 diff --git a/arch/sparc/include/asm/seccomp.h b/arch/sparc/include/asm/seccomp.h index 7fcd9968192b..adca1bce41d4 100644 --- a/arch/sparc/include/asm/seccomp.h +++ b/arch/sparc/include/asm/seccomp.h @@ -1,11 +1,5 @@ #ifndef _ASM_SECCOMP_H -#include <linux/thread_info.h> /* already defines TIF_32BIT */ - -#ifndef TIF_32BIT -#error "unexpected TIF_32BIT on sparc64" -#endif - #include <linux/unistd.h> #define __NR_seccomp_read __NR_read diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile index 53adcaa0348b..54742e58831c 100644 --- a/arch/sparc/kernel/Makefile +++ b/arch/sparc/kernel/Makefile @@ -52,6 +52,8 @@ obj-$(CONFIG_SPARC64) += visemul.o obj-$(CONFIG_SPARC64) += hvapi.o obj-$(CONFIG_SPARC64) += sstate.o obj-$(CONFIG_SPARC64) += mdesc.o +obj-$(CONFIG_SPARC64) += pcr.o +obj-$(CONFIG_SPARC64) += nmi.o # sparc32 do not use GENERIC_HARDIRQS but uses the generic devres implementation obj-$(CONFIG_SPARC32) += devres.o diff --git a/arch/sparc/kernel/central.c b/arch/sparc/kernel/central.c index 05f1c916db06..f3b5466c389c 100644 --- a/arch/sparc/kernel/central.c +++ b/arch/sparc/kernel/central.c @@ -103,6 +103,7 @@ static int __devinit clock_board_probe(struct of_device *op, p->leds_resource.name = "leds"; p->leds_pdev.name = "sunfire-clockboard-leds"; + p->leds_pdev.id = -1; p->leds_pdev.resource = &p->leds_resource; p->leds_pdev.num_resources = 1; p->leds_pdev.dev.parent = &op->dev; @@ -197,6 +198,7 @@ static int __devinit fhc_probe(struct of_device *op, p->leds_resource.name = "leds"; p->leds_pdev.name = "sunfire-fhc-leds"; + p->leds_pdev.id = p->board_num; p->leds_pdev.resource = &p->leds_resource; p->leds_pdev.num_resources = 1; p->leds_pdev.dev.parent = &op->dev; diff --git a/arch/sparc/kernel/chmc.c b/arch/sparc/kernel/chmc.c index 3b9f4d6e14a9..e1a9598e2a4d 100644 --- a/arch/sparc/kernel/chmc.c +++ b/arch/sparc/kernel/chmc.c @@ -306,6 +306,7 @@ static int jbusmc_print_dimm(int syndrome_code, buf[1] = '?'; buf[2] = '?'; buf[3] = '\0'; + return 0; } p = dp->controller; prop = &p->layout; diff --git a/arch/sparc/kernel/cpu.c b/arch/sparc/kernel/cpu.c index f0b825505da5..d85c3dc4953a 100644 --- a/arch/sparc/kernel/cpu.c +++ b/arch/sparc/kernel/cpu.c @@ -26,6 +26,7 @@ EXPORT_PER_CPU_SYMBOL(__cpu_data); struct cpu_info { int psr_vers; const char *name; + const char *pmu_name; }; struct fpu_info { @@ -45,6 +46,9 @@ struct manufacturer_info { #define CPU(ver, _name) \ { .psr_vers = ver, .name = _name } +#define CPU_PMU(ver, _name, _pmu_name) \ +{ .psr_vers = ver, .name = _name, .pmu_name = _pmu_name } + #define FPU(ver, _name) \ { .fp_vers = ver, .name = _name } @@ -183,10 +187,10 @@ static const struct manufacturer_info __initconst manufacturer_info[] = { },{ 0x17, .cpu_info = { - CPU(0x10, "TI UltraSparc I (SpitFire)"), - CPU(0x11, "TI UltraSparc II (BlackBird)"), - CPU(0x12, "TI UltraSparc IIi (Sabre)"), - CPU(0x13, "TI UltraSparc IIe (Hummingbird)"), + CPU_PMU(0x10, "TI UltraSparc I (SpitFire)", "ultra12"), + CPU_PMU(0x11, "TI UltraSparc II (BlackBird)", "ultra12"), + CPU_PMU(0x12, "TI UltraSparc IIi (Sabre)", "ultra12"), + CPU_PMU(0x13, "TI UltraSparc IIe (Hummingbird)", "ultra12"), CPU(-1, NULL) }, .fpu_info = { @@ -199,7 +203,7 @@ static const struct manufacturer_info __initconst manufacturer_info[] = { },{ 0x22, .cpu_info = { - CPU(0x10, "TI UltraSparc I (SpitFire)"), + CPU_PMU(0x10, "TI UltraSparc I (SpitFire)", "ultra12"), CPU(-1, NULL) }, .fpu_info = { @@ -209,12 +213,12 @@ static const struct manufacturer_info __initconst manufacturer_info[] = { },{ 0x3e, .cpu_info = { - CPU(0x14, "TI UltraSparc III (Cheetah)"), - CPU(0x15, "TI UltraSparc III+ (Cheetah+)"), - CPU(0x16, "TI UltraSparc IIIi (Jalapeno)"), - CPU(0x18, "TI UltraSparc IV (Jaguar)"), - CPU(0x19, "TI UltraSparc IV+ (Panther)"), - CPU(0x22, "TI UltraSparc IIIi+ (Serrano)"), + CPU_PMU(0x14, "TI UltraSparc III (Cheetah)", "ultra3"), + CPU_PMU(0x15, "TI UltraSparc III+ (Cheetah+)", "ultra3+"), + CPU_PMU(0x16, "TI UltraSparc IIIi (Jalapeno)", "ultra3i"), + CPU_PMU(0x18, "TI UltraSparc IV (Jaguar)", "ultra3+"), + CPU_PMU(0x19, "TI UltraSparc IV+ (Panther)", "ultra4+"), + CPU_PMU(0x22, "TI UltraSparc IIIi+ (Serrano)", "ultra3i"), CPU(-1, NULL) }, .fpu_info = { @@ -234,29 +238,44 @@ static const struct manufacturer_info __initconst manufacturer_info[] = { const char *sparc_cpu_type; const char *sparc_fpu_type; +const char *sparc_pmu_type; unsigned int fsr_storage; static void set_cpu_and_fpu(int psr_impl, int psr_vers, int fpu_vers) { + const struct manufacturer_info *manuf; + int i; + sparc_cpu_type = NULL; sparc_fpu_type = NULL; - if (psr_impl < ARRAY_SIZE(manufacturer_info)) + sparc_pmu_type = NULL; + manuf = NULL; + + for (i = 0; i < ARRAY_SIZE(manufacturer_info); i++) + { + if (psr_impl == manufacturer_info[i].psr_impl) { + manuf = &manufacturer_info[i]; + break; + } + } + if (manuf != NULL) { const struct cpu_info *cpu; const struct fpu_info *fpu; - cpu = &manufacturer_info[psr_impl].cpu_info[0]; + cpu = &manuf->cpu_info[0]; while (cpu->psr_vers != -1) { if (cpu->psr_vers == psr_vers) { sparc_cpu_type = cpu->name; + sparc_pmu_type = cpu->pmu_name; sparc_fpu_type = "No FPU"; break; } cpu++; } - fpu = &manufacturer_info[psr_impl].fpu_info[0]; + fpu = &manuf->fpu_info[0]; while (fpu->fp_vers != -1) { if (fpu->fp_vers == fpu_vers) { @@ -278,6 +297,8 @@ static void set_cpu_and_fpu(int psr_impl, int psr_vers, int fpu_vers) psr_impl, fpu_vers); sparc_fpu_type = "Unknown FPU"; } + if (sparc_pmu_type == NULL) + sparc_pmu_type = "Unknown PMU"; } #ifdef CONFIG_SPARC32 @@ -303,11 +324,13 @@ static void __init sun4v_cpu_probe(void) case SUN4V_CHIP_NIAGARA1: sparc_cpu_type = "UltraSparc T1 (Niagara)"; sparc_fpu_type = "UltraSparc T1 integrated FPU"; + sparc_pmu_type = "niagara"; break; case SUN4V_CHIP_NIAGARA2: sparc_cpu_type = "UltraSparc T2 (Niagara2)"; sparc_fpu_type = "UltraSparc T2 integrated FPU"; + sparc_pmu_type = "niagara2"; break; default: diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S index faf9ccd9ef5d..f41ecc5ac0b4 100644 --- a/arch/sparc/kernel/entry.S +++ b/arch/sparc/kernel/entry.S @@ -1088,8 +1088,8 @@ sunos_execv: ld [%sp + STACKFRAME_SZ + PT_I0], %o0 .align 4 - .globl sys_pipe -sys_pipe: + .globl sys_sparc_pipe +sys_sparc_pipe: mov %o7, %l5 add %sp, STACKFRAME_SZ, %o0 ! pt_regs *regs arg call sparc_pipe diff --git a/arch/sparc/kernel/head_64.S b/arch/sparc/kernel/head_64.S index 8ffee714f932..a46c3a21e26d 100644 --- a/arch/sparc/kernel/head_64.S +++ b/arch/sparc/kernel/head_64.S @@ -891,10 +891,35 @@ prom_tba: .xword 0 tlb_type: .word 0 /* Must NOT end up in BSS */ .section ".fixup",#alloc,#execinstr - .globl __ret_efault, __retl_efault -__ret_efault: + .globl __ret_efault, __retl_efault, __ret_one, __retl_one +ENTRY(__ret_efault) ret restore %g0, -EFAULT, %o0 -__retl_efault: +ENDPROC(__ret_efault) + +ENTRY(__retl_efault) retl mov -EFAULT, %o0 +ENDPROC(__retl_efault) + +ENTRY(__retl_one) + retl + mov 1, %o0 +ENDPROC(__retl_one) + +ENTRY(__ret_one_asi) + wr %g0, ASI_AIUS, %asi + ret + restore %g0, 1, %o0 +ENDPROC(__ret_one_asi) + +ENTRY(__retl_one_asi) + wr %g0, ASI_AIUS, %asi + retl + mov 1, %o0 +ENDPROC(__retl_one_asi) + +ENTRY(__retl_o1) + retl + mov %o1, %o0 +ENDPROC(__retl_o1) diff --git a/arch/sparc/kernel/irq_64.c b/arch/sparc/kernel/irq_64.c index cab8e0286871..1c378d8e90c5 100644 --- a/arch/sparc/kernel/irq_64.c +++ b/arch/sparc/kernel/irq_64.c @@ -196,6 +196,11 @@ int show_interrupts(struct seq_file *p, void *v) seq_putc(p, '\n'); skip: spin_unlock_irqrestore(&irq_desc[i].lock, flags); + } else if (i == NR_IRQS) { + seq_printf(p, "NMI: "); + for_each_online_cpu(j) + seq_printf(p, "%10u ", cpu_data(j).__nmi_count); + seq_printf(p, " Non-maskable interrupts\n"); } return 0; } @@ -318,17 +323,25 @@ static void sun4u_set_affinity(unsigned int virt_irq, sun4u_irq_enable(virt_irq); } +/* Don't do anything. The desc->status check for IRQ_DISABLED in + * handler_irq() will skip the handler call and that will leave the + * interrupt in the sent state. The next ->enable() call will hit the + * ICLR register to reset the state machine. + * + * This scheme is necessary, instead of clearing the Valid bit in the + * IMAP register, to handle the case of IMAP registers being shared by + * multiple INOs (and thus ICLR registers). Since we use a different + * virtual IRQ for each shared IMAP instance, the generic code thinks + * there is only one user so it prematurely calls ->disable() on + * free_irq(). + * + * We have to provide an explicit ->disable() method instead of using + * NULL to get the default. The reason is that if the generic code + * sees that, it also hooks up a default ->shutdown method which + * invokes ->mask() which we do not want. See irq_chip_set_defaults(). + */ static void sun4u_irq_disable(unsigned int virt_irq) { - struct irq_handler_data *data = get_irq_chip_data(virt_irq); - - if (likely(data)) { - unsigned long imap = data->imap; - unsigned long tmp = upa_readq(imap); - - tmp &= ~IMAP_VALID; - upa_writeq(tmp, imap); - } } static void sun4u_irq_eoi(unsigned int virt_irq) @@ -741,7 +754,8 @@ void handler_irq(int irq, struct pt_regs *regs) desc = irq_desc + virt_irq; - desc->handle_irq(virt_irq, desc); + if (!(desc->status & IRQ_DISABLED)) + desc->handle_irq(virt_irq, desc); bucket_pa = next_pa; } @@ -778,69 +792,6 @@ void do_softirq(void) local_irq_restore(flags); } -static void unhandled_perf_irq(struct pt_regs *regs) -{ - unsigned long pcr, pic; - - read_pcr(pcr); - read_pic(pic); - - write_pcr(0); - - printk(KERN_EMERG "CPU %d: Got unexpected perf counter IRQ.\n", - smp_processor_id()); - printk(KERN_EMERG "CPU %d: PCR[%016lx] PIC[%016lx]\n", - smp_processor_id(), pcr, pic); -} - -/* Almost a direct copy of the powerpc PMC code. */ -static DEFINE_SPINLOCK(perf_irq_lock); -static void *perf_irq_owner_caller; /* mostly for debugging */ -static void (*perf_irq)(struct pt_regs *regs) = unhandled_perf_irq; - -/* Invoked from level 15 PIL handler in trap table. */ -void perfctr_irq(int irq, struct pt_regs *regs) -{ - clear_softint(1 << irq); - perf_irq(regs); -} - -int register_perfctr_intr(void (*handler)(struct pt_regs *)) -{ - int ret; - - if (!handler) - return -EINVAL; - - spin_lock(&perf_irq_lock); - if (perf_irq != unhandled_perf_irq) { - printk(KERN_WARNING "register_perfctr_intr: " - "perf IRQ busy (reserved by caller %p)\n", - perf_irq_owner_caller); - ret = -EBUSY; - goto out; - } - - perf_irq_owner_caller = __builtin_return_address(0); - perf_irq = handler; - - ret = 0; -out: - spin_unlock(&perf_irq_lock); - - return ret; -} -EXPORT_SYMBOL_GPL(register_perfctr_intr); - -void release_perfctr_intr(void (*handler)(struct pt_regs *)) -{ - spin_lock(&perf_irq_lock); - perf_irq_owner_caller = NULL; - perf_irq = unhandled_perf_irq; - spin_unlock(&perf_irq_lock); -} -EXPORT_SYMBOL_GPL(release_perfctr_intr); - #ifdef CONFIG_HOTPLUG_CPU void fixup_irqs(void) { diff --git a/arch/sparc/kernel/kernel.h b/arch/sparc/kernel/kernel.h index 81a972e8d8ea..15d8a3f645c9 100644 --- a/arch/sparc/kernel/kernel.h +++ b/arch/sparc/kernel/kernel.h @@ -5,6 +5,7 @@ /* cpu.c */ extern const char *sparc_cpu_type; +extern const char *sparc_pmu_type; extern const char *sparc_fpu_type; extern unsigned int fsr_storage; diff --git a/arch/sparc/kernel/kgdb_64.c b/arch/sparc/kernel/kgdb_64.c index fefbe6dc51be..f5a0fd490b59 100644 --- a/arch/sparc/kernel/kgdb_64.c +++ b/arch/sparc/kernel/kgdb_64.c @@ -108,7 +108,7 @@ void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) } #ifdef CONFIG_SMP -void smp_kgdb_capture_client(struct pt_regs *regs) +void smp_kgdb_capture_client(int irq, struct pt_regs *regs) { unsigned long flags; diff --git a/arch/sparc/kernel/nmi.c b/arch/sparc/kernel/nmi.c new file mode 100644 index 000000000000..f3577223c863 --- /dev/null +++ b/arch/sparc/kernel/nmi.c @@ -0,0 +1,225 @@ +/* Pseudo NMI support on sparc64 systems. + * + * Copyright (C) 2009 David S. Miller <davem@davemloft.net> + * + * The NMI watchdog support and infrastructure is based almost + * entirely upon the x86 NMI support code. + */ +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/init.h> +#include <linux/percpu.h> +#include <linux/nmi.h> +#include <linux/module.h> +#include <linux/kprobes.h> +#include <linux/kernel_stat.h> +#include <linux/slab.h> +#include <linux/kdebug.h> +#include <linux/delay.h> +#include <linux/smp.h> + +#include <asm/ptrace.h> +#include <asm/local.h> +#include <asm/pcr.h> + +/* We don't have a real NMI on sparc64, but we can fake one + * up using profiling counter overflow interrupts and interrupt + * levels. + * + * The profile overflow interrupts at level 15, so we use + * level 14 as our IRQ off level. + */ + +static int nmi_watchdog_active; +static int panic_on_timeout; + +int nmi_usable; +EXPORT_SYMBOL_GPL(nmi_usable); + +static unsigned int nmi_hz = HZ; + +static DEFINE_PER_CPU(unsigned int, last_irq_sum); +static DEFINE_PER_CPU(local_t, alert_counter); +static DEFINE_PER_CPU(int, nmi_touch); + +void touch_nmi_watchdog(void) +{ + if (nmi_watchdog_active) { + int cpu; + + for_each_present_cpu(cpu) { + if (per_cpu(nmi_touch, cpu) != 1) + per_cpu(nmi_touch, cpu) = 1; + } + } + + touch_softlockup_watchdog(); +} +EXPORT_SYMBOL(touch_nmi_watchdog); + +static void die_nmi(const char *str, struct pt_regs *regs, int do_panic) +{ + if (notify_die(DIE_NMIWATCHDOG, str, regs, 0, + pt_regs_trap_type(regs), SIGINT) == NOTIFY_STOP) + return; + + console_verbose(); + bust_spinlocks(1); + + printk(KERN_EMERG "%s", str); + printk(" on CPU%d, ip %08lx, registers:\n", + smp_processor_id(), regs->tpc); + show_regs(regs); + dump_stack(); + + bust_spinlocks(0); + + if (do_panic || panic_on_oops) + panic("Non maskable interrupt"); + + local_irq_enable(); + do_exit(SIGBUS); +} + +notrace __kprobes void perfctr_irq(int irq, struct pt_regs *regs) +{ + unsigned int sum, touched = 0; + int cpu = smp_processor_id(); + + clear_softint(1 << irq); + pcr_ops->write(PCR_PIC_PRIV); + + local_cpu_data().__nmi_count++; + + if (notify_die(DIE_NMI, "nmi", regs, 0, + pt_regs_trap_type(regs), SIGINT) == NOTIFY_STOP) + touched = 1; + + sum = kstat_irqs_cpu(0, cpu); + if (__get_cpu_var(nmi_touch)) { + __get_cpu_var(nmi_touch) = 0; + touched = 1; + } + if (!touched && __get_cpu_var(last_irq_sum) == sum) { + local_inc(&__get_cpu_var(alert_counter)); + if (local_read(&__get_cpu_var(alert_counter)) == 5 * nmi_hz) + die_nmi("BUG: NMI Watchdog detected LOCKUP", + regs, panic_on_timeout); + } else { + __get_cpu_var(last_irq_sum) = sum; + local_set(&__get_cpu_var(alert_counter), 0); + } + if (nmi_usable) { + write_pic(picl_value(nmi_hz)); + pcr_ops->write(pcr_enable); + } +} + +static inline unsigned int get_nmi_count(int cpu) +{ + return cpu_data(cpu).__nmi_count; +} + +static int endflag __initdata; + +static __init void nmi_cpu_busy(void *data) +{ + local_irq_enable_in_hardirq(); + while (endflag == 0) + mb(); +} + +static void report_broken_nmi(int cpu, int *prev_nmi_count) +{ + printk(KERN_CONT "\n"); + + printk(KERN_WARNING + "WARNING: CPU#%d: NMI appears to be stuck (%d->%d)!\n", + cpu, prev_nmi_count[cpu], get_nmi_count(cpu)); + + printk(KERN_WARNING + "Please report this to bugzilla.kernel.org,\n"); + printk(KERN_WARNING + "and attach the output of the 'dmesg' command.\n"); + + nmi_usable = 0; +} + +static void stop_watchdog(void *unused) +{ + pcr_ops->write(PCR_PIC_PRIV); +} + +static int __init check_nmi_watchdog(void) +{ + unsigned int *prev_nmi_count; + int cpu, err; + + prev_nmi_count = kmalloc(nr_cpu_ids * sizeof(unsigned int), GFP_KERNEL); + if (!prev_nmi_count) { + err = -ENOMEM; + goto error; + } + + printk(KERN_INFO "Testing NMI watchdog ... "); + + smp_call_function(nmi_cpu_busy, (void *)&endflag, 0); + + for_each_possible_cpu(cpu) + prev_nmi_count[cpu] = get_nmi_count(cpu); + local_irq_enable(); + mdelay((20 * 1000) / nmi_hz); /* wait 20 ticks */ + + for_each_online_cpu(cpu) { + if (get_nmi_count(cpu) - prev_nmi_count[cpu] <= 5) + report_broken_nmi(cpu, prev_nmi_count); + } + endflag = 1; + if (!nmi_usable) { + kfree(prev_nmi_count); + err = -ENODEV; + goto error; + } + printk("OK.\n"); + + nmi_hz = 1; + + kfree(prev_nmi_count); + return 0; +error: + on_each_cpu(stop_watchdog, NULL, 1); + return err; +} + +static void start_watchdog(void *unused) +{ + pcr_ops->write(PCR_PIC_PRIV); + write_pic(picl_value(nmi_hz)); + + pcr_ops->write(pcr_enable); +} + +void nmi_adjust_hz(unsigned int new_hz) +{ + nmi_hz = new_hz; + on_each_cpu(start_watchdog, NULL, 1); +} +EXPORT_SYMBOL_GPL(nmi_adjust_hz); + +int __init nmi_init(void) +{ + nmi_usable = 1; + + on_each_cpu(start_watchdog, NULL, 1); + + return check_nmi_watchdog(); +} + +static int __init setup_nmi_watchdog(char *str) +{ + if (!strncmp(str, "panic", 5)) + panic_on_timeout = 1; + + return 0; +} +__setup("nmi_watchdog=", setup_nmi_watchdog); diff --git a/arch/sparc/kernel/pci_common.c b/arch/sparc/kernel/pci_common.c index 64e6edf17b9d..b775658a927d 100644 --- a/arch/sparc/kernel/pci_common.c +++ b/arch/sparc/kernel/pci_common.c @@ -368,7 +368,7 @@ static void pci_register_iommu_region(struct pci_pbm_info *pbm) const u32 *vdma = of_get_property(pbm->op->node, "virtual-dma", NULL); if (vdma) { - struct resource *rp = kmalloc(sizeof(*rp), GFP_KERNEL); + struct resource *rp = kzalloc(sizeof(*rp), GFP_KERNEL); if (!rp) { prom_printf("Cannot allocate IOMMU resource.\n"); diff --git a/arch/sparc/kernel/pcr.c b/arch/sparc/kernel/pcr.c new file mode 100644 index 000000000000..1ae8cdd7e703 --- /dev/null +++ b/arch/sparc/kernel/pcr.c @@ -0,0 +1,158 @@ +/* pcr.c: Generic sparc64 performance counter infrastructure. + * + * Copyright (C) 2009 David S. Miller (davem@davemloft.net) + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/irq.h> + +#include <asm/pil.h> +#include <asm/pcr.h> +#include <asm/nmi.h> + +/* This code is shared between various users of the performance + * counters. Users will be oprofile, pseudo-NMI watchdog, and the + * perf_counter support layer. + */ + +#define PCR_SUN4U_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE) +#define PCR_N2_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE | \ + PCR_N2_TOE_OV1 | \ + (2 << PCR_N2_SL1_SHIFT) | \ + (0xff << PCR_N2_MASK1_SHIFT)) + +u64 pcr_enable; +unsigned int picl_shift; + +/* Performance counter interrupts run unmasked at PIL level 15. + * Therefore we can't do things like wakeups and other work + * that expects IRQ disabling to be adhered to in locking etc. + * + * Therefore in such situations we defer the work by signalling + * a lower level cpu IRQ. + */ +void deferred_pcr_work_irq(int irq, struct pt_regs *regs) +{ + clear_softint(1 << PIL_DEFERRED_PCR_WORK); +} + +void schedule_deferred_pcr_work(void) +{ + set_softint(1 << PIL_DEFERRED_PCR_WORK); +} + +const struct pcr_ops *pcr_ops; +EXPORT_SYMBOL_GPL(pcr_ops); + +static u64 direct_pcr_read(void) +{ + u64 val; + + read_pcr(val); + return val; +} + +static void direct_pcr_write(u64 val) +{ + write_pcr(val); +} + +static const struct pcr_ops direct_pcr_ops = { + .read = direct_pcr_read, + .write = direct_pcr_write, +}; + +static void n2_pcr_write(u64 val) +{ + unsigned long ret; + + ret = sun4v_niagara2_setperf(HV_N2_PERF_SPARC_CTL, val); + if (val != HV_EOK) + write_pcr(val); +} + +static const struct pcr_ops n2_pcr_ops = { + .read = direct_pcr_read, + .write = n2_pcr_write, +}; + +static unsigned long perf_hsvc_group; +static unsigned long perf_hsvc_major; +static unsigned long perf_hsvc_minor; + +static int __init register_perf_hsvc(void) +{ + if (tlb_type == hypervisor) { + switch (sun4v_chip_type) { + case SUN4V_CHIP_NIAGARA1: + perf_hsvc_group = HV_GRP_NIAG_PERF; + break; + + case SUN4V_CHIP_NIAGARA2: + perf_hsvc_group = HV_GRP_N2_CPU; + break; + + default: + return -ENODEV; + } + + + perf_hsvc_major = 1; + perf_hsvc_minor = 0; + if (sun4v_hvapi_register(perf_hsvc_group, + perf_hsvc_major, + &perf_hsvc_minor)) { + printk("perfmon: Could not register hvapi.\n"); + return -ENODEV; + } + } + return 0; +} + +static void __init unregister_perf_hsvc(void) +{ + if (tlb_type != hypervisor) + return; + sun4v_hvapi_unregister(perf_hsvc_group); +} + +int __init pcr_arch_init(void) +{ + int err = register_perf_hsvc(); + + if (err) + return err; + + switch (tlb_type) { + case hypervisor: + pcr_ops = &n2_pcr_ops; + pcr_enable = PCR_N2_ENABLE; + picl_shift = 2; + break; + + case cheetah: + case cheetah_plus: + pcr_ops = &direct_pcr_ops; + pcr_enable = PCR_SUN4U_ENABLE; + break; + + case spitfire: + /* UltraSPARC-I/II and derivatives lack a profile + * counter overflow interrupt so we can't make use of + * their hardware currently. + */ + /* fallthrough */ + default: + err = -ENODEV; + goto out_unregister; + } + + return nmi_init(); + +out_unregister: + unregister_perf_hsvc(); + return err; +} + +arch_initcall(pcr_arch_init); diff --git a/arch/sparc/kernel/process_64.c b/arch/sparc/kernel/process_64.c index cc8b5604442c..a73954b87f0a 100644 --- a/arch/sparc/kernel/process_64.c +++ b/arch/sparc/kernel/process_64.c @@ -29,6 +29,7 @@ #include <linux/cpu.h> #include <linux/elfcore.h> #include <linux/sysrq.h> +#include <linux/nmi.h> #include <asm/uaccess.h> #include <asm/system.h> @@ -52,8 +53,10 @@ static void sparc64_yield(int cpu) { - if (tlb_type != hypervisor) + if (tlb_type != hypervisor) { + touch_nmi_watchdog(); return; + } clear_thread_flag(TIF_POLLING_NRFLAG); smp_mb__after_clear_bit(); diff --git a/arch/sparc/kernel/setup_64.c b/arch/sparc/kernel/setup_64.c index 49d061f4ae9d..f2bcfd2967d7 100644 --- a/arch/sparc/kernel/setup_64.c +++ b/arch/sparc/kernel/setup_64.c @@ -354,6 +354,7 @@ static int show_cpuinfo(struct seq_file *m, void *__unused) seq_printf(m, "cpu\t\t: %s\n" "fpu\t\t: %s\n" + "pmu\t\t: %s\n" "prom\t\t: %s\n" "type\t\t: %s\n" "ncpus probed\t: %d\n" @@ -366,6 +367,7 @@ static int show_cpuinfo(struct seq_file *m, void *__unused) , sparc_cpu_type, sparc_fpu_type, + sparc_pmu_type, prom_version, ((tlb_type == hypervisor) ? "sun4v" : diff --git a/arch/sparc/kernel/sparc_ksyms_64.c b/arch/sparc/kernel/sparc_ksyms_64.c index da8f804feb49..0f26066a08d9 100644 --- a/arch/sparc/kernel/sparc_ksyms_64.c +++ b/arch/sparc/kernel/sparc_ksyms_64.c @@ -9,7 +9,6 @@ #include <linux/pci.h> #include <linux/init.h> -#include <asm/spinlock.h> #include <asm/system.h> #include <asm/cpudata.h> #include <asm/uaccess.h> @@ -23,16 +22,6 @@ struct poll { short revents; }; -/* used by various drivers */ -#ifdef CONFIG_SMP -/* Out of line rw-locking implementation. */ -EXPORT_SYMBOL(__read_lock); -EXPORT_SYMBOL(__read_unlock); -EXPORT_SYMBOL(__write_lock); -EXPORT_SYMBOL(__write_unlock); -EXPORT_SYMBOL(__write_trylock); -#endif /* CONFIG_SMP */ - /* from helpers.S */ EXPORT_SYMBOL(__flushw_user); EXPORT_SYMBOL_GPL(real_hard_smp_processor_id); diff --git a/arch/sparc/kernel/sys_sparc_64.c b/arch/sparc/kernel/sys_sparc_64.c index 09058fc39e73..e2d102447a43 100644 --- a/arch/sparc/kernel/sys_sparc_64.c +++ b/arch/sparc/kernel/sys_sparc_64.c @@ -399,7 +399,7 @@ void arch_pick_mmap_layout(struct mm_struct *mm) } } -asmlinkage unsigned long sparc_brk(unsigned long brk) +SYSCALL_DEFINE1(sparc_brk, unsigned long, brk) { /* People could try to be nasty and use ta 0x6d in 32bit programs */ if (test_thread_flag(TIF_32BIT) && brk >= STACK_TOP32) @@ -415,7 +415,7 @@ asmlinkage unsigned long sparc_brk(unsigned long brk) * sys_pipe() is the normal C calling standard for creating * a pipe. It's not the way unix traditionally does this, though. */ -asmlinkage long sparc_pipe(struct pt_regs *regs) +SYSCALL_DEFINE1(sparc_pipe_real, struct pt_regs *, regs) { int fd[2]; int error; @@ -435,8 +435,8 @@ out: * This is really horribly ugly. */ -asmlinkage long sys_ipc(unsigned int call, int first, unsigned long second, - unsigned long third, void __user *ptr, long fifth) +SYSCALL_DEFINE6(ipc, unsigned int, call, int, first, unsigned long, second, + unsigned long, third, void __user *, ptr, long, fifth) { long err; @@ -519,7 +519,7 @@ out: return err; } -asmlinkage long sparc64_newuname(struct new_utsname __user *name) +SYSCALL_DEFINE1(sparc64_newuname, struct new_utsname __user *, name) { int ret = sys_newuname(name); @@ -530,7 +530,7 @@ asmlinkage long sparc64_newuname(struct new_utsname __user *name) return ret; } -asmlinkage long sparc64_personality(unsigned long personality) +SYSCALL_DEFINE1(sparc64_personality, unsigned long, personality) { int ret; @@ -564,9 +564,9 @@ int sparc_mmap_check(unsigned long addr, unsigned long len) } /* Linux version of mmap */ -asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, - unsigned long prot, unsigned long flags, unsigned long fd, - unsigned long off) +SYSCALL_DEFINE6(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 = -EBADF; @@ -589,7 +589,7 @@ out: return retval; } -asmlinkage long sys64_munmap(unsigned long addr, size_t len) +SYSCALL_DEFINE2(64_munmap, unsigned long, addr, size_t, len) { long ret; @@ -606,9 +606,9 @@ 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 sys64_mremap(unsigned long addr, - unsigned long old_len, unsigned long new_len, - unsigned long flags, unsigned long new_addr) +SYSCALL_DEFINE5(64_mremap, unsigned long, addr, unsigned long, old_len, + unsigned long, new_len, unsigned long, flags, + unsigned long, new_addr) { unsigned long ret = -EINVAL; @@ -671,7 +671,7 @@ asmlinkage void sparc_breakpoint(struct pt_regs *regs) extern void check_pending(int signum); -asmlinkage long sys_getdomainname(char __user *name, int len) +SYSCALL_DEFINE2(getdomainname, char __user *, name, int, len) { int nlen, err; @@ -694,11 +694,10 @@ out: return err; } -asmlinkage long sys_utrap_install(utrap_entry_t type, - utrap_handler_t new_p, - utrap_handler_t new_d, - utrap_handler_t __user *old_p, - utrap_handler_t __user *old_d) +SYSCALL_DEFINE5(utrap_install, utrap_entry_t, type, + utrap_handler_t, new_p, utrap_handler_t, new_d, + utrap_handler_t __user *, old_p, + utrap_handler_t __user *, old_d) { if (type < UT_INSTRUCTION_EXCEPTION || type > UT_TRAP_INSTRUCTION_31) return -EINVAL; @@ -764,11 +763,9 @@ asmlinkage long sparc_memory_ordering(unsigned long model, return 0; } -asmlinkage long sys_rt_sigaction(int sig, - const struct sigaction __user *act, - struct sigaction __user *oact, - void __user *restorer, - size_t sigsetsize) +SYSCALL_DEFINE5(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; @@ -808,7 +805,8 @@ asmlinkage void update_perfctrs(void) reset_pic(); } -asmlinkage long sys_perfctr(int opcode, unsigned long arg0, unsigned long arg1, unsigned long arg2) +SYSCALL_DEFINE4(perfctr, int, opcode, unsigned long, arg0, + unsigned long, arg1, unsigned long, arg2) { int err = 0; diff --git a/arch/sparc/kernel/syscalls.S b/arch/sparc/kernel/syscalls.S index 7a6786a71363..d150c2aa98d2 100644 --- a/arch/sparc/kernel/syscalls.S +++ b/arch/sparc/kernel/syscalls.S @@ -20,8 +20,8 @@ execve_merge: add %sp, PTREGS_OFF, %o0 .align 32 -sys_pipe: - ba,pt %xcc, sparc_pipe +sys_sparc_pipe: + ba,pt %xcc, sys_sparc_pipe_real add %sp, PTREGS_OFF, %o0 sys_nis_syscall: ba,pt %xcc, c_sys_nis_syscall diff --git a/arch/sparc/kernel/systbls.h b/arch/sparc/kernel/systbls.h index bc9f5dac4069..15c2d752b2bc 100644 --- a/arch/sparc/kernel/systbls.h +++ b/arch/sparc/kernel/systbls.h @@ -16,9 +16,6 @@ extern asmlinkage long sys_ipc(unsigned int call, int first, void __user *ptr, long fifth); extern asmlinkage long sparc64_newuname(struct new_utsname __user *name); extern asmlinkage long sparc64_personality(unsigned long personality); -extern asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, - unsigned long prot, unsigned long flags, - unsigned long fd, unsigned long off); extern asmlinkage long sys64_munmap(unsigned long addr, size_t len); extern asmlinkage unsigned long sys64_mremap(unsigned long addr, unsigned long old_len, diff --git a/arch/sparc/kernel/systbls_32.S b/arch/sparc/kernel/systbls_32.S index 7d0807586442..dccc95df0c7f 100644 --- a/arch/sparc/kernel/systbls_32.S +++ b/arch/sparc/kernel/systbls_32.S @@ -24,7 +24,7 @@ sys_call_table: /*25*/ .long sys_vmsplice, 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 +/*40*/ .long sys_newlstat, sys_dup, sys_sparc_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 @@ -56,7 +56,7 @@ sys_call_table: /*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_ioprio_set, sys_getppid, sparc_sigaction, sys_sgetmask -/*200*/ .long sys_ssetmask, sys_sigsuspend, sys_newlstat, sys_uselib, old_readdir +/*200*/ .long sys_ssetmask, sys_sigsuspend, sys_newlstat, sys_uselib, sys_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_ioprio_get, sys_adjtimex diff --git a/arch/sparc/kernel/systbls_64.S b/arch/sparc/kernel/systbls_64.S index 9fc78cf354bd..f93c42a2b522 100644 --- a/arch/sparc/kernel/systbls_64.S +++ b/arch/sparc/kernel/systbls_64.S @@ -21,12 +21,12 @@ sys_call_table32: /*0*/ .word sys_restart_syscall, sys32_exit, sys_fork, sys_read, sys_write /*5*/ .word sys32_open, sys_close, sys32_wait4, sys32_creat, sys_link /*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys_chown16, sys32_mknod -/*15*/ .word sys_chmod, sys_lchown16, sparc_brk, sys32_perfctr, sys32_lseek +/*15*/ .word sys_chmod, sys_lchown16, sys_sparc_brk, sys32_perfctr, sys32_lseek /*20*/ .word sys_getpid, sys_capget, sys_capset, sys_setuid16, sys_getuid16 /*25*/ .word sys32_vmsplice, compat_sys_ptrace, sys_alarm, sys32_sigaltstack, sys_pause /*30*/ .word compat_sys_utime, sys_lchown, sys_fchown, sys32_access, sys32_nice .word sys_chown, sys_sync, sys32_kill, compat_sys_newstat, sys32_sendfile -/*40*/ .word compat_sys_newlstat, sys_dup, sys_pipe, compat_sys_times, sys_getuid +/*40*/ .word compat_sys_newlstat, sys_dup, sys_sparc_pipe, compat_sys_times, sys_getuid .word sys32_umount, sys_setgid16, sys_getgid16, sys32_signal, sys_geteuid16 /*50*/ .word sys_getegid16, sys_acct, sys_nis_syscall, sys_getgid, compat_sys_ioctl .word sys32_reboot, sys32_mmap2, sys_symlink, sys32_readlink, sys32_execve @@ -55,8 +55,8 @@ sys_call_table32: /*170*/ .word sys32_lsetxattr, sys32_fsetxattr, sys_getxattr, sys_lgetxattr, compat_sys_getdents .word sys_setsid, sys_fchdir, sys32_fgetxattr, sys_listxattr, sys_llistxattr /*180*/ .word sys32_flistxattr, sys_removexattr, sys_lremovexattr, compat_sys_sigpending, sys_ni_syscall - .word sys32_setpgid, sys32_fremovexattr, sys32_tkill, sys32_exit_group, sparc64_newuname -/*190*/ .word sys32_init_module, sparc64_personality, sys_remap_file_pages, sys32_epoll_create, sys32_epoll_ctl + .word sys32_setpgid, sys32_fremovexattr, sys32_tkill, sys32_exit_group, sys_sparc64_newuname +/*190*/ .word sys32_init_module, sys_sparc64_personality, sys_remap_file_pages, sys32_epoll_create, sys32_epoll_ctl .word sys32_epoll_wait, sys32_ioprio_set, sys_getppid, sys32_sigaction, sys_sgetmask /*200*/ .word sys32_ssetmask, sys_sigsuspend, compat_sys_newlstat, sys_uselib, compat_sys_old_readdir .word sys32_readahead, sys32_socketcall, sys32_syslog, sys32_lookup_dcookie, sys32_fadvise64 @@ -95,18 +95,18 @@ sys_call_table: /*0*/ .word sys_restart_syscall, sparc_exit, sys_fork, sys_read, sys_write /*5*/ .word sys_open, sys_close, sys_wait4, sys_creat, sys_link /*10*/ .word sys_unlink, sys_nis_syscall, sys_chdir, sys_chown, sys_mknod -/*15*/ .word sys_chmod, sys_lchown, sparc_brk, sys_perfctr, sys_lseek +/*15*/ .word sys_chmod, sys_lchown, sys_sparc_brk, sys_perfctr, sys_lseek /*20*/ .word sys_getpid, sys_capget, sys_capset, sys_setuid, sys_getuid /*25*/ .word sys_vmsplice, sys_ptrace, sys_alarm, sys_sigaltstack, sys_nis_syscall /*30*/ .word sys_utime, sys_nis_syscall, sys_nis_syscall, sys_access, sys_nice .word sys_nis_syscall, sys_sync, sys_kill, sys_newstat, sys_sendfile64 -/*40*/ .word sys_newlstat, sys_dup, sys_pipe, sys_times, sys_nis_syscall +/*40*/ .word sys_newlstat, sys_dup, sys_sparc_pipe, sys_times, sys_nis_syscall .word sys_umount, sys_setgid, sys_getgid, sys_signal, sys_geteuid /*50*/ .word sys_getegid, sys_acct, sys_memory_ordering, sys_nis_syscall, sys_ioctl .word sys_reboot, sys_nis_syscall, sys_symlink, sys_readlink, sys_execve /*60*/ .word sys_umask, sys_chroot, sys_newfstat, sys_fstat64, sys_getpagesize .word sys_msync, sys_vfork, sys_pread64, sys_pwrite64, sys_nis_syscall -/*70*/ .word sys_nis_syscall, sys_mmap, sys_nis_syscall, sys64_munmap, sys_mprotect +/*70*/ .word sys_nis_syscall, sys_mmap, sys_nis_syscall, sys_64_munmap, sys_mprotect .word sys_madvise, sys_vhangup, sys_nis_syscall, sys_mincore, sys_getgroups /*80*/ .word sys_setgroups, sys_getpgrp, sys_nis_syscall, sys_setitimer, sys_nis_syscall .word sys_swapon, sys_getitimer, sys_nis_syscall, sys_sethostname, sys_nis_syscall @@ -129,8 +129,8 @@ sys_call_table: /*170*/ .word sys_lsetxattr, sys_fsetxattr, sys_getxattr, sys_lgetxattr, sys_getdents .word sys_setsid, sys_fchdir, sys_fgetxattr, sys_listxattr, sys_llistxattr /*180*/ .word sys_flistxattr, sys_removexattr, sys_lremovexattr, sys_nis_syscall, sys_ni_syscall - .word sys_setpgid, sys_fremovexattr, sys_tkill, sys_exit_group, sparc64_newuname -/*190*/ .word sys_init_module, sparc64_personality, sys_remap_file_pages, sys_epoll_create, sys_epoll_ctl + .word sys_setpgid, sys_fremovexattr, sys_tkill, sys_exit_group, sys_sparc64_newuname +/*190*/ .word sys_init_module, sys_sparc64_personality, sys_remap_file_pages, sys_epoll_create, sys_epoll_ctl .word sys_epoll_wait, sys_ioprio_set, sys_getppid, sys_nis_syscall, sys_sgetmask /*200*/ .word sys_ssetmask, sys_nis_syscall, sys_newlstat, sys_uselib, sys_nis_syscall .word sys_readahead, sys_socketcall, sys_syslog, sys_lookup_dcookie, sys_fadvise64 @@ -142,7 +142,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 sys64_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl +/*250*/ .word sys_64_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl .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/traps_64.c b/arch/sparc/kernel/traps_64.c index c2d153d46586..d809c4ebb48f 100644 --- a/arch/sparc/kernel/traps_64.c +++ b/arch/sparc/kernel/traps_64.c @@ -1,6 +1,6 @@ /* arch/sparc64/kernel/traps.c * - * Copyright (C) 1995,1997,2008 David S. Miller (davem@davemloft.net) + * Copyright (C) 1995,1997,2008,2009 David S. Miller (davem@davemloft.net) * Copyright (C) 1997,1999,2000 Jakub Jelinek (jakub@redhat.com) */ @@ -314,6 +314,21 @@ void sun4v_data_access_exception(struct pt_regs *regs, unsigned long addr, unsig return; if (regs->tstate & TSTATE_PRIV) { + /* Test if this comes from uaccess places. */ + const struct exception_table_entry *entry; + + entry = search_exception_tables(regs->tpc); + if (entry) { + /* Ouch, somebody is trying VM hole tricks on us... */ +#ifdef DEBUG_EXCEPTIONS + printk("Exception: PC<%016lx> faddr<UNKNOWN>\n", regs->tpc); + printk("EX_TABLE: insn<%016lx> fixup<%016lx>\n", + regs->tpc, entry->fixup); +#endif + regs->tpc = entry->fixup; + regs->tnpc = regs->tpc + 4; + return; + } printk("sun4v_data_access_exception: ADDR[%016lx] " "CTX[%04x] TYPE[%04x], going.\n", addr, ctx, type); diff --git a/arch/sparc/kernel/ttable.S b/arch/sparc/kernel/ttable.S index ea925503b42e..76d837fc47d3 100644 --- a/arch/sparc/kernel/ttable.S +++ b/arch/sparc/kernel/ttable.S @@ -63,7 +63,13 @@ tl0_irq6: TRAP_IRQ(smp_call_function_single_client, 6) #else tl0_irq6: BTRAP(0x46) #endif -tl0_irq7: BTRAP(0x47) BTRAP(0x48) BTRAP(0x49) +tl0_irq7: TRAP_IRQ(deferred_pcr_work_irq, 7) +#ifdef CONFIG_KGDB +tl0_irq8: TRAP_IRQ(smp_kgdb_capture_client, 8) +#else +tl0_irq8: BTRAP(0x48) +#endif +tl0_irq9: BTRAP(0x49) tl0_irq10: BTRAP(0x4a) BTRAP(0x4b) BTRAP(0x4c) BTRAP(0x4d) tl0_irq14: TRAP_IRQ(timer_interrupt, 14) tl0_irq15: TRAP_NMI_IRQ(perfctr_irq, 15) diff --git a/arch/sparc/lib/GENbzero.S b/arch/sparc/lib/GENbzero.S index 6a4f956a2f7a..8e7a843ddd88 100644 --- a/arch/sparc/lib/GENbzero.S +++ b/arch/sparc/lib/GENbzero.S @@ -6,13 +6,9 @@ #define EX_ST(x,y) \ 98: x,y; \ - .section .fixup; \ - .align 4; \ -99: retl; \ - mov %o1, %o0; \ .section __ex_table,"a";\ .align 4; \ - .word 98b, 99b; \ + .word 98b, __retl_o1; \ .text; \ .align 4; diff --git a/arch/sparc/lib/GENcopy_from_user.S b/arch/sparc/lib/GENcopy_from_user.S index 2b9df99e87f9..b7d0bd6b1406 100644 --- a/arch/sparc/lib/GENcopy_from_user.S +++ b/arch/sparc/lib/GENcopy_from_user.S @@ -5,13 +5,9 @@ #define EX_LD(x) \ 98: x; \ - .section .fixup; \ - .align 4; \ -99: retl; \ - mov 1, %o0; \ .section __ex_table,"a";\ .align 4; \ - .word 98b, 99b; \ + .word 98b, __retl_one; \ .text; \ .align 4; @@ -27,7 +23,7 @@ #define PREAMBLE \ rd %asi, %g1; \ cmp %g1, ASI_AIUS; \ - bne,pn %icc, memcpy_user_stub; \ + bne,pn %icc, ___copy_in_user; \ nop #endif diff --git a/arch/sparc/lib/GENcopy_to_user.S b/arch/sparc/lib/GENcopy_to_user.S index bb3f7084daf9..780550e1afc7 100644 --- a/arch/sparc/lib/GENcopy_to_user.S +++ b/arch/sparc/lib/GENcopy_to_user.S @@ -5,13 +5,9 @@ #define EX_ST(x) \ 98: x; \ - .section .fixup; \ - .align 4; \ -99: retl; \ - mov 1, %o0; \ .section __ex_table,"a";\ .align 4; \ - .word 98b, 99b; \ + .word 98b, __retl_one; \ .text; \ .align 4; @@ -31,7 +27,7 @@ #define PREAMBLE \ rd %asi, %g1; \ cmp %g1, ASI_AIUS; \ - bne,pn %icc, memcpy_user_stub; \ + bne,pn %icc, ___copy_in_user; \ nop #endif diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile index 273fc85269fc..e75faf0e59ae 100644 --- a/arch/sparc/lib/Makefile +++ b/arch/sparc/lib/Makefile @@ -18,7 +18,7 @@ lib-$(CONFIG_SPARC32) += lshrdi3.o ashldi3.o lib-y += rwsem_$(BITS).o lib-$(CONFIG_SPARC32) += muldi3.o bitext.o cmpdi2.o -lib-$(CONFIG_SPARC64) += PeeCeeI.o copy_page.o clear_page.o bzero.o +lib-$(CONFIG_SPARC64) += copy_page.o clear_page.o bzero.o lib-$(CONFIG_SPARC64) += csum_copy.o csum_copy_from_user.o csum_copy_to_user.o lib-$(CONFIG_SPARC64) += VISsave.o lib-$(CONFIG_SPARC64) += bitops.o @@ -43,3 +43,4 @@ lib-$(CONFIG_SPARC64) += mcount.o ipcsum.o xor.o obj-y += iomap.o obj-$(CONFIG_SPARC32) += atomic32.o obj-y += ksyms.o +obj-$(CONFIG_SPARC64) += PeeCeeI.o diff --git a/arch/sparc/lib/NG2copy_from_user.S b/arch/sparc/lib/NG2copy_from_user.S index c77ef5f22102..119ccb9a54f4 100644 --- a/arch/sparc/lib/NG2copy_from_user.S +++ b/arch/sparc/lib/NG2copy_from_user.S @@ -5,14 +5,9 @@ #define EX_LD(x) \ 98: x; \ - .section .fixup; \ - .align 4; \ -99: wr %g0, ASI_AIUS, %asi;\ - retl; \ - mov 1, %o0; \ .section __ex_table,"a";\ .align 4; \ - .word 98b, 99b; \ + .word 98b, __retl_one_asi;\ .text; \ .align 4; @@ -33,7 +28,7 @@ #define PREAMBLE \ rd %asi, %g1; \ cmp %g1, ASI_AIUS; \ - bne,pn %icc, memcpy_user_stub; \ + bne,pn %icc, ___copy_in_user; \ nop #endif diff --git a/arch/sparc/lib/NG2copy_to_user.S b/arch/sparc/lib/NG2copy_to_user.S index 4bd4093acbbd..7fe1ccefd9d0 100644 --- a/arch/sparc/lib/NG2copy_to_user.S +++ b/arch/sparc/lib/NG2copy_to_user.S @@ -5,14 +5,9 @@ #define EX_ST(x) \ 98: x; \ - .section .fixup; \ - .align 4; \ -99: wr %g0, ASI_AIUS, %asi;\ - retl; \ - mov 1, %o0; \ .section __ex_table,"a";\ .align 4; \ - .word 98b, 99b; \ + .word 98b, __retl_one_asi;\ .text; \ .align 4; @@ -42,7 +37,7 @@ #define PREAMBLE \ rd %asi, %g1; \ cmp %g1, ASI_AIUS; \ - bne,pn %icc, memcpy_user_stub; \ + bne,pn %icc, ___copy_in_user; \ nop #endif diff --git a/arch/sparc/lib/NGbzero.S b/arch/sparc/lib/NGbzero.S index 814d5f7a45e1..beab29bf419b 100644 --- a/arch/sparc/lib/NGbzero.S +++ b/arch/sparc/lib/NGbzero.S @@ -6,13 +6,9 @@ #define EX_ST(x,y) \ 98: x,y; \ - .section .fixup; \ - .align 4; \ -99: retl; \ - mov %o1, %o0; \ .section __ex_table,"a";\ .align 4; \ - .word 98b, 99b; \ + .word 98b, __retl_o1; \ .text; \ .align 4; diff --git a/arch/sparc/lib/NGcopy_from_user.S b/arch/sparc/lib/NGcopy_from_user.S index e7f433f71b42..5d1e4d1ac21e 100644 --- a/arch/sparc/lib/NGcopy_from_user.S +++ b/arch/sparc/lib/NGcopy_from_user.S @@ -5,14 +5,9 @@ #define EX_LD(x) \ 98: x; \ - .section .fixup; \ - .align 4; \ -99: wr %g0, ASI_AIUS, %asi;\ - ret; \ - restore %g0, 1, %o0; \ .section __ex_table,"a";\ .align 4; \ - .word 98b, 99b; \ + .word 98b, __ret_one_asi;\ .text; \ .align 4; @@ -30,7 +25,7 @@ #define PREAMBLE \ rd %asi, %g1; \ cmp %g1, ASI_AIUS; \ - bne,pn %icc, memcpy_user_stub; \ + bne,pn %icc, ___copy_in_user; \ nop #endif diff --git a/arch/sparc/lib/NGcopy_to_user.S b/arch/sparc/lib/NGcopy_to_user.S index 6ea01c5532a0..ff630dcb273c 100644 --- a/arch/sparc/lib/NGcopy_to_user.S +++ b/arch/sparc/lib/NGcopy_to_user.S @@ -5,14 +5,9 @@ #define EX_ST(x) \ 98: x; \ - .section .fixup; \ - .align 4; \ -99: wr %g0, ASI_AIUS, %asi;\ - ret; \ - restore %g0, 1, %o0; \ .section __ex_table,"a";\ .align 4; \ - .word 98b, 99b; \ + .word 98b, __ret_one_asi;\ .text; \ .align 4; @@ -33,7 +28,7 @@ #define PREAMBLE \ rd %asi, %g1; \ cmp %g1, ASI_AIUS; \ - bne,pn %icc, memcpy_user_stub; \ + bne,pn %icc, ___copy_in_user; \ nop #endif diff --git a/arch/sparc/lib/U1copy_from_user.S b/arch/sparc/lib/U1copy_from_user.S index 3192b0bf4fab..a6ae2ea04bf5 100644 --- a/arch/sparc/lib/U1copy_from_user.S +++ b/arch/sparc/lib/U1copy_from_user.S @@ -5,13 +5,9 @@ #define EX_LD(x) \ 98: x; \ - .section .fixup; \ - .align 4; \ -99: retl; \ - mov 1, %o0; \ .section __ex_table,"a";\ .align 4; \ - .word 98b, 99b; \ + .word 98b, __retl_one; \ .text; \ .align 4; @@ -27,7 +23,7 @@ #define PREAMBLE \ rd %asi, %g1; \ cmp %g1, ASI_AIUS; \ - bne,pn %icc, memcpy_user_stub; \ + bne,pn %icc, ___copy_in_user; \ nop; \ #include "U1memcpy.S" diff --git a/arch/sparc/lib/U1copy_to_user.S b/arch/sparc/lib/U1copy_to_user.S index d1210ffb0b82..f4b970eeb485 100644 --- a/arch/sparc/lib/U1copy_to_user.S +++ b/arch/sparc/lib/U1copy_to_user.S @@ -5,13 +5,9 @@ #define EX_ST(x) \ 98: x; \ - .section .fixup; \ - .align 4; \ -99: retl; \ - mov 1, %o0; \ .section __ex_table,"a";\ .align 4; \ - .word 98b, 99b; \ + .word 98b, __retl_one; \ .text; \ .align 4; @@ -27,7 +23,7 @@ #define PREAMBLE \ rd %asi, %g1; \ cmp %g1, ASI_AIUS; \ - bne,pn %icc, memcpy_user_stub; \ + bne,pn %icc, ___copy_in_user; \ nop; \ #include "U1memcpy.S" diff --git a/arch/sparc/lib/U3copy_from_user.S b/arch/sparc/lib/U3copy_from_user.S index f5bfc8d9d216..b1acd1331c33 100644 --- a/arch/sparc/lib/U3copy_from_user.S +++ b/arch/sparc/lib/U3copy_from_user.S @@ -5,13 +5,9 @@ #define EX_LD(x) \ 98: x; \ - .section .fixup; \ - .align 4; \ -99: retl; \ - mov 1, %o0; \ .section __ex_table,"a";\ .align 4; \ - .word 98b, 99b; \ + .word 98b, __retl_one; \ .text; \ .align 4; diff --git a/arch/sparc/lib/U3copy_to_user.S b/arch/sparc/lib/U3copy_to_user.S index 2334f111bb0c..ef1e493afdfa 100644 --- a/arch/sparc/lib/U3copy_to_user.S +++ b/arch/sparc/lib/U3copy_to_user.S @@ -5,13 +5,9 @@ #define EX_ST(x) \ 98: x; \ - .section .fixup; \ - .align 4; \ -99: retl; \ - mov 1, %o0; \ .section __ex_table,"a";\ .align 4; \ - .word 98b, 99b; \ + .word 98b, __retl_one; \ .text; \ .align 4; @@ -27,7 +23,7 @@ #define PREAMBLE \ rd %asi, %g1; \ cmp %g1, ASI_AIUS; \ - bne,pn %icc, memcpy_user_stub; \ + bne,pn %icc, ___copy_in_user; \ nop; \ #include "U3memcpy.S" diff --git a/arch/sparc/lib/bzero.S b/arch/sparc/lib/bzero.S index c7bbae8c590f..b6557297440f 100644 --- a/arch/sparc/lib/bzero.S +++ b/arch/sparc/lib/bzero.S @@ -88,13 +88,9 @@ __bzero_done: #define EX_ST(x,y) \ 98: x,y; \ - .section .fixup; \ - .align 4; \ -99: retl; \ - mov %o1, %o0; \ .section __ex_table,"a";\ .align 4; \ - .word 98b, 99b; \ + .word 98b, __retl_o1; \ .text; \ .align 4; diff --git a/arch/sparc/lib/copy_in_user.S b/arch/sparc/lib/copy_in_user.S index 650af3f21f78..302c0e60dc2c 100644 --- a/arch/sparc/lib/copy_in_user.S +++ b/arch/sparc/lib/copy_in_user.S @@ -3,19 +3,16 @@ * Copyright (C) 1999, 2000, 2004 David S. Miller (davem@redhat.com) */ +#include <linux/linkage.h> #include <asm/asi.h> #define XCC xcc #define EX(x,y) \ 98: x,y; \ - .section .fixup; \ - .align 4; \ -99: retl; \ - mov 1, %o0; \ .section __ex_table,"a";\ .align 4; \ - .word 98b, 99b; \ + .word 98b, __retl_one; \ .text; \ .align 4; @@ -31,18 +28,7 @@ * to copy register windows around during thread cloning. */ - .globl ___copy_in_user - .type ___copy_in_user,#function -___copy_in_user: /* %o0=dst, %o1=src, %o2=len */ - /* Writing to %asi is _expensive_ so we hardcode it. - * Reading %asi to check for KERNEL_DS is comparatively - * cheap. - */ - rd %asi, %g1 - cmp %g1, ASI_AIUS - bne,pn %icc, memcpy_user_stub - nop - +ENTRY(___copy_in_user) /* %o0=dst, %o1=src, %o2=len */ cmp %o2, 0 be,pn %XCC, 85f or %o0, %o1, %o3 @@ -53,22 +39,24 @@ ___copy_in_user: /* %o0=dst, %o1=src, %o2=len */ /* 16 < len <= 64 */ andcc %o3, 0x7, %g0 bne,pn %XCC, 90f - sub %o0, %o1, %o3 + nop andn %o2, 0x7, %o4 and %o2, 0x7, %o2 1: subcc %o4, 0x8, %o4 EX(ldxa [%o1] %asi, %o5) - EX(stxa %o5, [%o1 + %o3] ASI_AIUS) + EX(stxa %o5, [%o0] %asi) + add %o1, 0x8, %o1 bgu,pt %XCC, 1b - add %o1, 0x8, %o1 + add %o0, 0x8, %o0 andcc %o2, 0x4, %g0 be,pt %XCC, 1f nop sub %o2, 0x4, %o2 EX(lduwa [%o1] %asi, %o5) - EX(stwa %o5, [%o1 + %o3] ASI_AIUS) + EX(stwa %o5, [%o0] %asi) add %o1, 0x4, %o1 + add %o0, 0x4, %o0 1: cmp %o2, 0 be,pt %XCC, 85f nop @@ -78,14 +66,15 @@ ___copy_in_user: /* %o0=dst, %o1=src, %o2=len */ 80: /* 0 < len <= 16 */ andcc %o3, 0x3, %g0 bne,pn %XCC, 90f - sub %o0, %o1, %o3 + nop 82: subcc %o2, 4, %o2 EX(lduwa [%o1] %asi, %g1) - EX(stwa %g1, [%o1 + %o3] ASI_AIUS) + EX(stwa %g1, [%o0] %asi) + add %o1, 4, %o1 bgu,pt %XCC, 82b - add %o1, 4, %o1 + add %o0, 4, %o0 85: retl clr %o0 @@ -94,26 +83,10 @@ ___copy_in_user: /* %o0=dst, %o1=src, %o2=len */ 90: subcc %o2, 1, %o2 EX(lduba [%o1] %asi, %g1) - EX(stba %g1, [%o1 + %o3] ASI_AIUS) + EX(stba %g1, [%o0] %asi) + add %o1, 1, %o1 bgu,pt %XCC, 90b - add %o1, 1, %o1 + add %o0, 1, %o0 retl clr %o0 - - .size ___copy_in_user, .-___copy_in_user - - /* Act like copy_{to,in}_user(), ie. return zero instead - * of original destination pointer. This is invoked when - * copy_{to,in}_user() finds that %asi is kernel space. - */ - .globl memcpy_user_stub - .type memcpy_user_stub,#function -memcpy_user_stub: - save %sp, -192, %sp - mov %i0, %o0 - mov %i1, %o1 - call memcpy - mov %i2, %o2 - ret - restore %g0, %g0, %o0 - .size memcpy_user_stub, .-memcpy_user_stub +ENDPROC(___copy_in_user) diff --git a/arch/sparc/mm/fault_64.c b/arch/sparc/mm/fault_64.c index a9e474bf6385..4ab8993b0863 100644 --- a/arch/sparc/mm/fault_64.c +++ b/arch/sparc/mm/fault_64.c @@ -19,6 +19,7 @@ #include <linux/interrupt.h> #include <linux/kprobes.h> #include <linux/kdebug.h> +#include <linux/percpu.h> #include <asm/page.h> #include <asm/pgtable.h> @@ -224,6 +225,30 @@ cannot_handle: unhandled_fault (address, current, regs); } +static void noinline bogus_32bit_fault_tpc(struct pt_regs *regs) +{ + static int times; + + if (times++ < 10) + printk(KERN_ERR "FAULT[%s:%d]: 32-bit process reports " + "64-bit TPC [%lx]\n", + current->comm, current->pid, + regs->tpc); + show_regs(regs); +} + +static void noinline bogus_32bit_fault_address(struct pt_regs *regs, + unsigned long addr) +{ + static int times; + + if (times++ < 10) + printk(KERN_ERR "FAULT[%s:%d]: 32-bit process " + "reports 64-bit fault address [%lx]\n", + current->comm, current->pid, addr); + show_regs(regs); +} + asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs) { struct mm_struct *mm = current->mm; @@ -244,6 +269,19 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs) (fault_code & FAULT_CODE_DTLB)) BUG(); + if (test_thread_flag(TIF_32BIT)) { + if (!(regs->tstate & TSTATE_PRIV)) { + if (unlikely((regs->tpc >> 32) != 0)) { + bogus_32bit_fault_tpc(regs); + goto intr_or_no_mm; + } + } + if (unlikely((address >> 32) != 0)) { + bogus_32bit_fault_address(regs, address); + goto intr_or_no_mm; + } + } + if (regs->tstate & TSTATE_PRIV) { unsigned long tpc = regs->tpc; @@ -264,12 +302,6 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs) if (in_atomic() || !mm) goto intr_or_no_mm; - if (test_thread_flag(TIF_32BIT)) { - if (!(regs->tstate & TSTATE_PRIV)) - regs->tpc &= 0xffffffff; - address &= 0xffffffff; - } - if (!down_read_trylock(&mm->mmap_sem)) { if ((regs->tstate & TSTATE_PRIV) && !search_exception_tables(regs->tpc)) { diff --git a/arch/sparc/mm/ultra.S b/arch/sparc/mm/ultra.S index 80c788ec7c32..b57a5942ba64 100644 --- a/arch/sparc/mm/ultra.S +++ b/arch/sparc/mm/ultra.S @@ -679,28 +679,8 @@ xcall_new_mmu_context_version: #ifdef CONFIG_KGDB .globl xcall_kgdb_capture xcall_kgdb_capture: -661: rdpr %pstate, %g2 - wrpr %g2, PSTATE_IG | PSTATE_AG, %pstate - .section .sun4v_2insn_patch, "ax" - .word 661b - nop - nop - .previous - - rdpr %pil, %g2 - wrpr %g0, PIL_NORMAL_MAX, %pil - sethi %hi(109f), %g7 - ba,pt %xcc, etrap_irq -109: or %g7, %lo(109b), %g7 -#ifdef CONFIG_TRACE_IRQFLAGS - call trace_hardirqs_off - nop -#endif - call smp_kgdb_capture_client - add %sp, PTREGS_OFF, %o0 - /* Has to be a non-v9 branch due to the large distance. */ - ba rtrap_xcall - ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1 + wr %g0, (1 << PIL_KGDB_CAPTURE), %set_softint + retry #endif #endif /* CONFIG_SMP */ diff --git a/arch/sparc/oprofile/init.c b/arch/sparc/oprofile/init.c index d6e170c074fc..d172f86439b1 100644 --- a/arch/sparc/oprofile/init.c +++ b/arch/sparc/oprofile/init.c @@ -13,217 +13,57 @@ #include <linux/init.h> #ifdef CONFIG_SPARC64 -#include <asm/hypervisor.h> -#include <asm/spitfire.h> -#include <asm/cpudata.h> -#include <asm/irq.h> +#include <linux/notifier.h> +#include <linux/rcupdate.h> +#include <linux/kdebug.h> +#include <asm/nmi.h> -static int nmi_enabled; - -struct pcr_ops { - u64 (*read)(void); - void (*write)(u64); -}; -static const struct pcr_ops *pcr_ops; - -static u64 direct_pcr_read(void) -{ - u64 val; - - read_pcr(val); - return val; -} - -static void direct_pcr_write(u64 val) -{ - write_pcr(val); -} - -static const struct pcr_ops direct_pcr_ops = { - .read = direct_pcr_read, - .write = direct_pcr_write, -}; - -static void n2_pcr_write(u64 val) +static int profile_timer_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data) { - unsigned long ret; - - ret = sun4v_niagara2_setperf(HV_N2_PERF_SPARC_CTL, val); - if (val != HV_EOK) - write_pcr(val); -} - -static const struct pcr_ops n2_pcr_ops = { - .read = direct_pcr_read, - .write = n2_pcr_write, -}; - -/* In order to commonize as much of the implementation as - * possible, we use PICH as our counter. Mostly this is - * to accomodate Niagara-1 which can only count insn cycles - * in PICH. - */ -static u64 picl_value(void) -{ - u32 delta = local_cpu_data().clock_tick / HZ; - - return ((u64)((0 - delta) & 0xffffffff)) << 32; -} - -#define PCR_PIC_PRIV 0x00000001 /* PIC access is privileged */ -#define PCR_STRACE 0x00000002 /* Trace supervisor events */ -#define PCR_UTRACE 0x00000004 /* Trace user events */ -#define PCR_N2_HTRACE 0x00000008 /* Trace hypervisor events */ -#define PCR_N2_TOE_OV0 0x00000010 /* Trap if PIC 0 overflows */ -#define PCR_N2_TOE_OV1 0x00000020 /* Trap if PIC 1 overflows */ -#define PCR_N2_MASK0 0x00003fc0 -#define PCR_N2_MASK0_SHIFT 6 -#define PCR_N2_SL0 0x0003c000 -#define PCR_N2_SL0_SHIFT 14 -#define PCR_N2_OV0 0x00040000 -#define PCR_N2_MASK1 0x07f80000 -#define PCR_N2_MASK1_SHIFT 19 -#define PCR_N2_SL1 0x78000000 -#define PCR_N2_SL1_SHIFT 27 -#define PCR_N2_OV1 0x80000000 - -#define PCR_SUN4U_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE) -#define PCR_N2_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE | \ - PCR_N2_TOE_OV1 | \ - (2 << PCR_N2_SL1_SHIFT) | \ - (0xff << PCR_N2_MASK1_SHIFT)) - -static u64 pcr_enable = PCR_SUN4U_ENABLE; - -static void nmi_handler(struct pt_regs *regs) -{ - pcr_ops->write(PCR_PIC_PRIV); - - if (nmi_enabled) { - oprofile_add_sample(regs, 0); - - write_pic(picl_value()); - pcr_ops->write(pcr_enable); - } -} - -/* We count "clock cycle" events in the lower 32-bit PIC. - * Then configure it such that it overflows every HZ, and thus - * generates a level 15 interrupt at that frequency. - */ -static void cpu_nmi_start(void *_unused) -{ - pcr_ops->write(PCR_PIC_PRIV); - write_pic(picl_value()); - - pcr_ops->write(pcr_enable); -} + struct die_args *args = (struct die_args *)data; + int ret = NOTIFY_DONE; -static void cpu_nmi_stop(void *_unused) -{ - pcr_ops->write(PCR_PIC_PRIV); -} - -static int nmi_start(void) -{ - int err = register_perfctr_intr(nmi_handler); - - if (!err) { - nmi_enabled = 1; - wmb(); - err = on_each_cpu(cpu_nmi_start, NULL, 1); - if (err) { - nmi_enabled = 0; - wmb(); - on_each_cpu(cpu_nmi_stop, NULL, 1); - release_perfctr_intr(nmi_handler); - } + switch (val) { + case DIE_NMI: + oprofile_add_sample(args->regs, 0); + ret = NOTIFY_STOP; + break; + default: + break; } - - return err; -} - -static void nmi_stop(void) -{ - nmi_enabled = 0; - wmb(); - - on_each_cpu(cpu_nmi_stop, NULL, 1); - release_perfctr_intr(nmi_handler); - synchronize_sched(); + return ret; } -static unsigned long perf_hsvc_group; -static unsigned long perf_hsvc_major; -static unsigned long perf_hsvc_minor; +static struct notifier_block profile_timer_exceptions_nb = { + .notifier_call = profile_timer_exceptions_notify, +}; -static int __init register_perf_hsvc(void) +static int timer_start(void) { - if (tlb_type == hypervisor) { - switch (sun4v_chip_type) { - case SUN4V_CHIP_NIAGARA1: - perf_hsvc_group = HV_GRP_NIAG_PERF; - break; - - case SUN4V_CHIP_NIAGARA2: - perf_hsvc_group = HV_GRP_N2_CPU; - break; - - default: - return -ENODEV; - } - - - perf_hsvc_major = 1; - perf_hsvc_minor = 0; - if (sun4v_hvapi_register(perf_hsvc_group, - perf_hsvc_major, - &perf_hsvc_minor)) { - printk("perfmon: Could not register N2 hvapi.\n"); - return -ENODEV; - } - } + if (register_die_notifier(&profile_timer_exceptions_nb)) + return 1; + nmi_adjust_hz(HZ); return 0; } -static void unregister_perf_hsvc(void) + +static void timer_stop(void) { - if (tlb_type != hypervisor) - return; - sun4v_hvapi_unregister(perf_hsvc_group); + nmi_adjust_hz(1); + unregister_die_notifier(&profile_timer_exceptions_nb); + synchronize_sched(); /* Allow already-started NMIs to complete. */ } -static int oprofile_nmi_init(struct oprofile_operations *ops) +static int op_nmi_timer_init(struct oprofile_operations *ops) { - int err = register_perf_hsvc(); - - if (err) - return err; - - switch (tlb_type) { - case hypervisor: - pcr_ops = &n2_pcr_ops; - pcr_enable = PCR_N2_ENABLE; - break; - - case cheetah: - case cheetah_plus: - pcr_ops = &direct_pcr_ops; - break; - - default: + if (!nmi_usable) return -ENODEV; - } - ops->create_files = NULL; - ops->setup = NULL; - ops->shutdown = NULL; - ops->start = nmi_start; - ops->stop = nmi_stop; + ops->start = timer_start; + ops->stop = timer_stop; ops->cpu_type = "timer"; - - printk(KERN_INFO "oprofile: Using perfctr based NMI timer interrupt.\n"); - + printk(KERN_INFO "oprofile: Using perfctr NMI timer interrupt.\n"); return 0; } #endif @@ -233,7 +73,7 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) int ret = -ENODEV; #ifdef CONFIG_SPARC64 - ret = oprofile_nmi_init(ops); + ret = op_nmi_timer_init(ops); if (!ret) return ret; #endif @@ -241,10 +81,6 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) return ret; } - void oprofile_arch_exit(void) { -#ifdef CONFIG_SPARC64 - unregister_perf_hsvc(); -#endif } |