aboutsummaryrefslogtreecommitdiff
path: root/arch/sparc
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sparc')
-rw-r--r--arch/sparc/Kconfig1
-rw-r--r--arch/sparc/configs/sparc64_defconfig4
-rw-r--r--arch/sparc/include/asm/cpudata_64.h2
-rw-r--r--arch/sparc/include/asm/irq_64.h4
-rw-r--r--arch/sparc/include/asm/kdebug_64.h2
-rw-r--r--arch/sparc/include/asm/nmi.h10
-rw-r--r--arch/sparc/include/asm/pcr.h46
-rw-r--r--arch/sparc/include/asm/pil.h1
-rw-r--r--arch/sparc/kernel/Makefile2
-rw-r--r--arch/sparc/kernel/central.c2
-rw-r--r--arch/sparc/kernel/cpu.c51
-rw-r--r--arch/sparc/kernel/irq_64.c68
-rw-r--r--arch/sparc/kernel/kernel.h1
-rw-r--r--arch/sparc/kernel/nmi.c224
-rw-r--r--arch/sparc/kernel/pcr.c153
-rw-r--r--arch/sparc/kernel/process_64.c5
-rw-r--r--arch/sparc/kernel/setup_64.c2
-rw-r--r--arch/sparc/kernel/sys_sparc_64.c48
-rw-r--r--arch/sparc/kernel/syscalls.S2
-rw-r--r--arch/sparc/kernel/systbls.h3
-rw-r--r--arch/sparc/kernel/systbls_64.S16
-rw-r--r--arch/sparc/kernel/traps_64.c17
-rw-r--r--arch/sparc/kernel/ttable.S3
-rw-r--r--arch/sparc/lib/Makefile3
-rw-r--r--arch/sparc/mm/fault_64.c44
-rw-r--r--arch/sparc/oprofile/init.c232
26 files changed, 618 insertions, 328 deletions
diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
index de58c02633b..c3ea215334f 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 cde19ae78f5..ade49941def 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/cpudata_64.h b/arch/sparc/include/asm/cpudata_64.h
index 7da7c13d23c..a11b89ee9ef 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 d47d4a1955a..1934f2cbf51 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 f905b773235..feb3578e12c 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 00000000000..fbd546dd4fe
--- /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 00000000000..a2f5c61f924
--- /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 d573820c0ff..32a7efe76d0 100644
--- a/arch/sparc/include/asm/pil.h
+++ b/arch/sparc/include/asm/pil.h
@@ -23,6 +23,7 @@
#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_NORMAL_MAX 14
#define PIL_NMI 15
diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile
index 53adcaa0348..54742e58831 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 05f1c916db0..f3b5466c389 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/cpu.c b/arch/sparc/kernel/cpu.c
index f0b825505da..d85c3dc4953 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/irq_64.c b/arch/sparc/kernel/irq_64.c
index cab8e028687..e289376198e 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;
}
@@ -778,69 +783,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 81a972e8d8e..15d8a3f645c 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/nmi.c b/arch/sparc/kernel/nmi.c
new file mode 100644
index 00000000000..09f088ed4a6
--- /dev/null
+++ b/arch/sparc/kernel/nmi.c
@@ -0,0 +1,224 @@
+/* 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);
+
+ 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/pcr.c b/arch/sparc/kernel/pcr.c
new file mode 100644
index 00000000000..92e0dda141a
--- /dev/null
+++ b/arch/sparc/kernel/pcr.c
@@ -0,0 +1,153 @@
+/* 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:
+ case spitfire:
+ pcr_ops = &direct_pcr_ops;
+ pcr_enable = PCR_SUN4U_ENABLE;
+ break;
+
+ 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 cc8b5604442..a73954b87f0 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 49d061f4ae9..f2bcfd2967d 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/sys_sparc_64.c b/arch/sparc/kernel/sys_sparc_64.c
index 09058fc39e7..e2d102447a4 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 87f5a3b8a25..d150c2aa98d 100644
--- a/arch/sparc/kernel/syscalls.S
+++ b/arch/sparc/kernel/syscalls.S
@@ -21,7 +21,7 @@ execve_merge:
.align 32
sys_sparc_pipe:
- ba,pt %xcc, 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 bc9f5dac406..15c2d752b2b 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_64.S b/arch/sparc/kernel/systbls_64.S
index e6007bb3704..f93c42a2b52 100644
--- a/arch/sparc/kernel/systbls_64.S
+++ b/arch/sparc/kernel/systbls_64.S
@@ -21,7 +21,7 @@ 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
@@ -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,7 +95,7 @@ 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
@@ -106,7 +106,7 @@ sys_call_table:
.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 c2d153d4658..d809c4ebb48 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 ea925503b42..d9bdfb9d5c1 100644
--- a/arch/sparc/kernel/ttable.S
+++ b/arch/sparc/kernel/ttable.S
@@ -63,7 +63,8 @@ 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)
+tl0_irq8: BTRAP(0x48) 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/Makefile b/arch/sparc/lib/Makefile
index 273fc85269f..e75faf0e59a 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/mm/fault_64.c b/arch/sparc/mm/fault_64.c
index a9e474bf638..4ab8993b086 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/oprofile/init.c b/arch/sparc/oprofile/init.c
index d6e170c074f..d172f86439b 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
}