summaryrefslogtreecommitdiffstats
path: root/arch/sparc/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sparc/kernel')
-rw-r--r--arch/sparc/kernel/entry.S51
-rw-r--r--arch/sparc/kernel/entry.h7
-rw-r--r--arch/sparc/kernel/etrap_64.S8
-rw-r--r--arch/sparc/kernel/leon_kernel.c6
-rw-r--r--arch/sparc/kernel/perf_event.c37
-rw-r--r--arch/sparc/kernel/process_32.c152
-rw-r--r--arch/sparc/kernel/process_64.c305
-rw-r--r--arch/sparc/kernel/ptrace_64.c4
-rw-r--r--arch/sparc/kernel/setup_64.c21
-rw-r--r--arch/sparc/kernel/signal_64.c4
-rw-r--r--arch/sparc/kernel/smp_64.c11
-rw-r--r--arch/sparc/kernel/sys_sparc32.c36
-rw-r--r--arch/sparc/kernel/sys_sparc_32.c24
-rw-r--r--arch/sparc/kernel/sys_sparc_64.c21
-rw-r--r--arch/sparc/kernel/syscalls.S30
-rw-r--r--arch/sparc/kernel/systbls_32.S1
-rw-r--r--arch/sparc/kernel/systbls_64.S4
-rw-r--r--arch/sparc/kernel/traps_64.c4
-rw-r--r--arch/sparc/kernel/unaligned_64.c36
-rw-r--r--arch/sparc/kernel/visemul.c23
-rw-r--r--arch/sparc/kernel/vmlinux.lds.S5
-rw-r--r--arch/sparc/kernel/winfixup.S2
22 files changed, 364 insertions, 428 deletions
diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S
index dcaa1cf0de4..21fd1a8f47d 100644
--- a/arch/sparc/kernel/entry.S
+++ b/arch/sparc/kernel/entry.S
@@ -806,23 +806,10 @@ sys_nis_syscall:
call c_sys_nis_syscall
mov %l5, %o7
- .align 4
- .globl sys_execve
-sys_execve:
- mov %o7, %l5
- add %sp, STACKFRAME_SZ, %o0 ! pt_regs *regs arg
- call sparc_execve
- mov %l5, %o7
-
- .globl sunos_execv
sunos_execv:
- st %g0, [%sp + STACKFRAME_SZ + PT_I2]
-
- call sparc_execve
- add %sp, STACKFRAME_SZ, %o0
-
- b ret_sys_call
- ld [%sp + STACKFRAME_SZ + PT_I0], %o0
+ .globl sunos_execv
+ b sys_execve
+ clr %i2
.align 4
.globl sys_sparc_pipe
@@ -959,17 +946,9 @@ flush_patch_four:
.align 4
linux_sparc_ni_syscall:
sethi %hi(sys_ni_syscall), %l7
- b syscall_is_too_hard
+ b do_syscall
or %l7, %lo(sys_ni_syscall), %l7
-linux_fast_syscall:
- andn %l7, 3, %l7
- mov %i0, %o0
- mov %i1, %o1
- mov %i2, %o2
- jmpl %l7 + %g0, %g0
- mov %i3, %o3
-
linux_syscall_trace:
add %sp, STACKFRAME_SZ, %o0
call syscall_trace
@@ -991,6 +970,23 @@ ret_from_fork:
b ret_sys_call
ld [%sp + STACKFRAME_SZ + PT_I0], %o0
+ .globl ret_from_kernel_thread
+ret_from_kernel_thread:
+ call schedule_tail
+ ld [%g3 + TI_TASK], %o0
+ ld [%sp + STACKFRAME_SZ + PT_G1], %l0
+ call %l0
+ ld [%sp + STACKFRAME_SZ + PT_G2], %o0
+ rd %psr, %l1
+ ld [%sp + STACKFRAME_SZ + PT_PSR], %l0
+ andn %l0, PSR_CWP, %l0
+ nop
+ and %l1, PSR_CWP, %l1
+ or %l0, %l1, %l0
+ st %l0, [%sp + STACKFRAME_SZ + PT_PSR]
+ b ret_sys_call
+ mov 0, %o0
+
/* Linux native system calls enter here... */
.align 4
.globl linux_sparc_syscall
@@ -1002,11 +998,8 @@ linux_sparc_syscall:
bgeu linux_sparc_ni_syscall
sll %g1, 2, %l4
ld [%l7 + %l4], %l7
- andcc %l7, 1, %g0
- bne linux_fast_syscall
- /* Just do first insn from SAVE_ALL in the delay slot */
-syscall_is_too_hard:
+do_syscall:
SAVE_ALL_HEAD
rd %wim, %l3
diff --git a/arch/sparc/kernel/entry.h b/arch/sparc/kernel/entry.h
index 0c218e4c088..cc3c5cb47cd 100644
--- a/arch/sparc/kernel/entry.h
+++ b/arch/sparc/kernel/entry.h
@@ -59,6 +59,13 @@ struct popc_6insn_patch_entry {
extern struct popc_6insn_patch_entry __popc_6insn_patch,
__popc_6insn_patch_end;
+struct pause_patch_entry {
+ unsigned int addr;
+ unsigned int insns[3];
+};
+extern struct pause_patch_entry __pause_3insn_patch,
+ __pause_3insn_patch_end;
+
extern void __init per_cpu_patch(void);
extern void sun4v_patch_1insn_range(struct sun4v_1insn_patch_entry *,
struct sun4v_1insn_patch_entry *);
diff --git a/arch/sparc/kernel/etrap_64.S b/arch/sparc/kernel/etrap_64.S
index 786b185e6e3..1276ca2567b 100644
--- a/arch/sparc/kernel/etrap_64.S
+++ b/arch/sparc/kernel/etrap_64.S
@@ -92,8 +92,10 @@ etrap_save: save %g2, -STACK_BIAS, %sp
rdpr %wstate, %g2
wrpr %g0, 0, %canrestore
sll %g2, 3, %g2
+
+ /* Set TI_SYS_FPDEPTH to 1 and clear TI_SYS_NOERROR. */
mov 1, %l5
- stb %l5, [%l6 + TI_FPDEPTH]
+ sth %l5, [%l6 + TI_SYS_NOERROR]
wrpr %g3, 0, %otherwin
wrpr %g2, 0, %wstate
@@ -152,7 +154,9 @@ etrap_save: save %g2, -STACK_BIAS, %sp
add %l6, TI_FPSAVED + 1, %l4
srl %l5, 1, %l3
add %l5, 2, %l5
- stb %l5, [%l6 + TI_FPDEPTH]
+
+ /* Set TI_SYS_FPDEPTH to %l5 and clear TI_SYS_NOERROR. */
+ sth %l5, [%l6 + TI_SYS_NOERROR]
ba,pt %xcc, 2b
stb %g0, [%l4 + %l3]
nop
diff --git a/arch/sparc/kernel/leon_kernel.c b/arch/sparc/kernel/leon_kernel.c
index f8b6eee40bd..87f60ee6543 100644
--- a/arch/sparc/kernel/leon_kernel.c
+++ b/arch/sparc/kernel/leon_kernel.c
@@ -56,11 +56,13 @@ static inline unsigned int leon_eirq_get(int cpu)
static void leon_handle_ext_irq(unsigned int irq, struct irq_desc *desc)
{
unsigned int eirq;
+ struct irq_bucket *p;
int cpu = sparc_leon3_cpuid();
eirq = leon_eirq_get(cpu);
- if ((eirq & 0x10) && irq_map[eirq]->irq) /* bit4 tells if IRQ happened */
- generic_handle_irq(irq_map[eirq]->irq);
+ p = irq_map[eirq];
+ if ((eirq & 0x10) && p && p->irq) /* bit4 tells if IRQ happened */
+ generic_handle_irq(p->irq);
}
/* The extended IRQ controller has been found, this function registers it */
diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c
index e48651dace1..b5c38faa4ea 100644
--- a/arch/sparc/kernel/perf_event.c
+++ b/arch/sparc/kernel/perf_event.c
@@ -817,15 +817,17 @@ static u64 nop_for_index(int idx)
static inline void sparc_pmu_enable_event(struct cpu_hw_events *cpuc, struct hw_perf_event *hwc, int idx)
{
- u64 val, mask = mask_for_index(idx);
+ u64 enc, val, mask = mask_for_index(idx);
int pcr_index = 0;
if (sparc_pmu->num_pcrs > 1)
pcr_index = idx;
+ enc = perf_event_get_enc(cpuc->events[idx]);
+
val = cpuc->pcr[pcr_index];
val &= ~mask;
- val |= hwc->config;
+ val |= event_encoding(enc, idx);
cpuc->pcr[pcr_index] = val;
pcr_ops->write_pcr(pcr_index, cpuc->pcr[pcr_index]);
@@ -1738,8 +1740,6 @@ static void perf_callchain_user_64(struct perf_callchain_entry *entry,
{
unsigned long ufp;
- perf_callchain_store(entry, regs->tpc);
-
ufp = regs->u_regs[UREG_I6] + STACK_BIAS;
do {
struct sparc_stackf *usf, sf;
@@ -1760,19 +1760,27 @@ static void perf_callchain_user_32(struct perf_callchain_entry *entry,
{
unsigned long ufp;
- perf_callchain_store(entry, regs->tpc);
-
ufp = regs->u_regs[UREG_I6] & 0xffffffffUL;
do {
- struct sparc_stackf32 *usf, sf;
unsigned long pc;
- usf = (struct sparc_stackf32 *) ufp;
- if (__copy_from_user_inatomic(&sf, usf, sizeof(sf)))
- break;
+ if (thread32_stack_is_64bit(ufp)) {
+ struct sparc_stackf *usf, sf;
- pc = sf.callers_pc;
- ufp = (unsigned long)sf.fp;
+ ufp += STACK_BIAS;
+ usf = (struct sparc_stackf *) ufp;
+ if (__copy_from_user_inatomic(&sf, usf, sizeof(sf)))
+ break;
+ pc = sf.callers_pc & 0xffffffff;
+ ufp = ((unsigned long) sf.fp) & 0xffffffff;
+ } else {
+ struct sparc_stackf32 *usf, sf;
+ usf = (struct sparc_stackf32 *) ufp;
+ if (__copy_from_user_inatomic(&sf, usf, sizeof(sf)))
+ break;
+ pc = sf.callers_pc;
+ ufp = (unsigned long)sf.fp;
+ }
perf_callchain_store(entry, pc);
} while (entry->nr < PERF_MAX_STACK_DEPTH);
}
@@ -1780,6 +1788,11 @@ static void perf_callchain_user_32(struct perf_callchain_entry *entry,
void
perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs)
{
+ perf_callchain_store(entry, regs->tpc);
+
+ if (!current->mm)
+ return;
+
flushw_user();
if (test_thread_flag(TIF_32BIT))
perf_callchain_user_32(entry, regs);
diff --git a/arch/sparc/kernel/process_32.c b/arch/sparc/kernel/process_32.c
index 487bffb36f5..bf4c6addce7 100644
--- a/arch/sparc/kernel/process_32.c
+++ b/arch/sparc/kernel/process_32.c
@@ -316,9 +316,10 @@ asmlinkage int sparc_do_fork(unsigned long clone_flags,
* XXX See comment above sys_vfork in sparc64. todo.
*/
extern void ret_from_fork(void);
+extern void ret_from_kernel_thread(void);
int copy_thread(unsigned long clone_flags, unsigned long sp,
- unsigned long unused,
+ unsigned long arg,
struct task_struct *p, struct pt_regs *regs)
{
struct thread_info *ti = task_thread_info(p);
@@ -336,16 +337,13 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
}
/*
- * p->thread_info new_stack childregs
- * ! ! ! {if(PSR_PS) }
- * V V (stk.fr.) V (pt_regs) { (stk.fr.) }
- * +----- - - - - - ------+===========+============={+==========}+
+ * p->thread_info new_stack childregs stack bottom
+ * ! ! ! !
+ * V V (stk.fr.) V (pt_regs) V
+ * +----- - - - - - ------+===========+=============+
*/
new_stack = task_stack_page(p) + THREAD_SIZE;
- if (regs->psr & PSR_PS)
- new_stack -= STACKFRAME_SZ;
new_stack -= STACKFRAME_SZ + TRACEREG_SZ;
- memcpy(new_stack, (char *)regs - STACKFRAME_SZ, STACKFRAME_SZ + TRACEREG_SZ);
childregs = (struct pt_regs *) (new_stack + STACKFRAME_SZ);
/*
@@ -356,55 +354,58 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
* Thus, kpsr|=PSR_PIL.
*/
ti->ksp = (unsigned long) new_stack;
+ p->thread.kregs = childregs;
+
+ if (unlikely(p->flags & PF_KTHREAD)) {
+ extern int nwindows;
+ unsigned long psr;
+ memset(new_stack, 0, STACKFRAME_SZ + TRACEREG_SZ);
+ p->thread.flags |= SPARC_FLAG_KTHREAD;
+ p->thread.current_ds = KERNEL_DS;
+ ti->kpc = (((unsigned long) ret_from_kernel_thread) - 0x8);
+ childregs->u_regs[UREG_G1] = sp; /* function */
+ childregs->u_regs[UREG_G2] = arg;
+ psr = childregs->psr = get_psr();
+ ti->kpsr = psr | PSR_PIL;
+ ti->kwim = 1 << (((psr & PSR_CWP) + 1) % nwindows);
+ return 0;
+ }
+ memcpy(new_stack, (char *)regs - STACKFRAME_SZ, STACKFRAME_SZ + TRACEREG_SZ);
+ childregs->u_regs[UREG_FP] = sp;
+ p->thread.flags &= ~SPARC_FLAG_KTHREAD;
+ p->thread.current_ds = USER_DS;
ti->kpc = (((unsigned long) ret_from_fork) - 0x8);
ti->kpsr = current->thread.fork_kpsr | PSR_PIL;
ti->kwim = current->thread.fork_kwim;
- if(regs->psr & PSR_PS) {
- extern struct pt_regs fake_swapper_regs;
+ if (sp != regs->u_regs[UREG_FP]) {
+ struct sparc_stackf __user *childstack;
+ struct sparc_stackf __user *parentstack;
- p->thread.kregs = &fake_swapper_regs;
- new_stack += STACKFRAME_SZ + TRACEREG_SZ;
- childregs->u_regs[UREG_FP] = (unsigned long) new_stack;
- p->thread.flags |= SPARC_FLAG_KTHREAD;
- p->thread.current_ds = KERNEL_DS;
- memcpy(new_stack, (void *)regs->u_regs[UREG_FP], STACKFRAME_SZ);
- childregs->u_regs[UREG_G6] = (unsigned long) ti;
- } else {
- p->thread.kregs = childregs;
- childregs->u_regs[UREG_FP] = sp;
- p->thread.flags &= ~SPARC_FLAG_KTHREAD;
- p->thread.current_ds = USER_DS;
-
- if (sp != regs->u_regs[UREG_FP]) {
- struct sparc_stackf __user *childstack;
- struct sparc_stackf __user *parentstack;
-
- /*
- * This is a clone() call with supplied user stack.
- * Set some valid stack frames to give to the child.
- */
- childstack = (struct sparc_stackf __user *)
- (sp & ~0xfUL);
- parentstack = (struct sparc_stackf __user *)
- regs->u_regs[UREG_FP];
+ /*
+ * This is a clone() call with supplied user stack.
+ * Set some valid stack frames to give to the child.
+ */
+ childstack = (struct sparc_stackf __user *)
+ (sp & ~0xfUL);
+ parentstack = (struct sparc_stackf __user *)
+ regs->u_regs[UREG_FP];
#if 0
- printk("clone: parent stack:\n");
- show_stackframe(parentstack);
+ printk("clone: parent stack:\n");
+ show_stackframe(parentstack);
#endif
- childstack = clone_stackframe(childstack, parentstack);
- if (!childstack)
- return -EFAULT;
+ childstack = clone_stackframe(childstack, parentstack);
+ if (!childstack)
+ return -EFAULT;
#if 0
- printk("clone: child stack:\n");
- show_stackframe(childstack);
+ printk("clone: child stack:\n");
+ show_stackframe(childstack);
#endif
- childregs->u_regs[UREG_FP] = (unsigned long)childstack;
- }
+ childregs->u_regs[UREG_FP] = (unsigned long)childstack;
}
#ifdef CONFIG_SMP
@@ -475,69 +476,6 @@ int dump_fpu (struct pt_regs * regs, elf_fpregset_t * fpregs)
return 1;
}
-/*
- * sparc_execve() executes a new program after the asm stub has set
- * things up for us. This should basically do what I want it to.
- */
-asmlinkage int sparc_execve(struct pt_regs *regs)
-{
- int error, base = 0;
- struct filename *filename;
-
- /* Check for indirect call. */
- if(regs->u_regs[UREG_G1] == 0)
- base = 1;
-
- filename = getname((char __user *)regs->u_regs[base + UREG_I0]);
- error = PTR_ERR(filename);
- if(IS_ERR(filename))
- goto out;
- error = do_execve(filename->name,
- (const char __user *const __user *)
- regs->u_regs[base + UREG_I1],
- (const char __user *const __user *)
- regs->u_regs[base + UREG_I2],
- regs);
- putname(filename);
-out:
- return error;
-}
-
-/*
- * This is the mechanism for creating a new kernel thread.
- *
- * NOTE! Only a kernel-only process(ie the swapper or direct descendants
- * who haven't done an "execve()") should use this: it will work within
- * a system call from a "real" process, but the process memory space will
- * not be freed until both the parent and the child have exited.
- */
-pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
-{
- long retval;
-
- __asm__ __volatile__("mov %4, %%g2\n\t" /* Set aside fn ptr... */
- "mov %5, %%g3\n\t" /* and arg. */
- "mov %1, %%g1\n\t"
- "mov %2, %%o0\n\t" /* Clone flags. */
- "mov 0, %%o1\n\t" /* usp arg == 0 */
- "t 0x10\n\t" /* Linux/Sparc clone(). */
- "cmp %%o1, 0\n\t"
- "be 1f\n\t" /* The parent, just return. */
- " nop\n\t" /* Delay slot. */
- "jmpl %%g2, %%o7\n\t" /* Call the function. */
- " mov %%g3, %%o0\n\t" /* Get back the arg in delay. */
- "mov %3, %%g1\n\t"
- "t 0x10\n\t" /* Linux/Sparc exit(). */
- /* Notreached by child. */
- "1: mov %%o0, %0\n\t" :
- "=r" (retval) :
- "i" (__NR_clone), "r" (flags | CLONE_VM | CLONE_UNTRACED),
- "i" (__NR_exit), "r" (fn), "r" (arg) :
- "g1", "g2", "g3", "o0", "o1", "memory", "cc");
- return retval;
-}
-EXPORT_SYMBOL(kernel_thread);
-
unsigned long get_wchan(struct task_struct *task)
{
unsigned long pc, fp, bias = 0;
diff --git a/arch/sparc/kernel/process_64.c b/arch/sparc/kernel/process_64.c
index fcaa5942112..dff54f46728 100644
--- a/arch/sparc/kernel/process_64.c
+++ b/arch/sparc/kernel/process_64.c
@@ -27,6 +27,7 @@
#include <linux/tick.h>
#include <linux/init.h>
#include <linux/cpu.h>
+#include <linux/perf_event.h>
#include <linux/elfcore.h>
#include <linux/sysrq.h>
#include <linux/nmi.h>
@@ -47,6 +48,7 @@
#include <asm/syscalls.h>
#include <asm/irq_regs.h>
#include <asm/smp.h>
+#include <asm/pcr.h>
#include "kstack.h"
@@ -204,18 +206,22 @@ void show_regs(struct pt_regs *regs)
show_stack(current, (unsigned long *) regs->u_regs[UREG_FP]);
}
-struct global_reg_snapshot global_reg_snapshot[NR_CPUS];
-static DEFINE_SPINLOCK(global_reg_snapshot_lock);
+union global_cpu_snapshot global_cpu_snapshot[NR_CPUS];
+static DEFINE_SPINLOCK(global_cpu_snapshot_lock);
static void __global_reg_self(struct thread_info *tp, struct pt_regs *regs,
int this_cpu)
{
+ struct global_reg_snapshot *rp;
+
flushw_all();
- global_reg_snapshot[this_cpu].tstate = regs->tstate;
- global_reg_snapshot[this_cpu].tpc = regs->tpc;
- global_reg_snapshot[this_cpu].tnpc = regs->tnpc;
- global_reg_snapshot[this_cpu].o7 = regs->u_regs[UREG_I7];
+ rp = &global_cpu_snapshot[this_cpu].reg;
+
+ rp->tstate = regs->tstate;
+ rp->tpc = regs->tpc;
+ rp->tnpc = regs->tnpc;
+ rp->o7 = regs->u_regs[UREG_I7];
if (regs->tstate & TSTATE_PRIV) {
struct reg_window *rw;
@@ -223,17 +229,17 @@ static void __global_reg_self(struct thread_info *tp, struct pt_regs *regs,
rw = (struct reg_window *)
(regs->u_regs[UREG_FP] + STACK_BIAS);
if (kstack_valid(tp, (unsigned long) rw)) {
- global_reg_snapshot[this_cpu].i7 = rw->ins[7];
+ rp->i7 = rw->ins[7];
rw = (struct reg_window *)
(rw->ins[6] + STACK_BIAS);
if (kstack_valid(tp, (unsigned long) rw))
- global_reg_snapshot[this_cpu].rpc = rw->ins[7];
+ rp->rpc = rw->ins[7];
}
} else {
- global_reg_snapshot[this_cpu].i7 = 0;
- global_reg_snapshot[this_cpu].rpc = 0;
+ rp->i7 = 0;
+ rp->rpc = 0;
}
- global_reg_snapshot[this_cpu].thread = tp;
+ rp->thread = tp;
}
/* In order to avoid hangs we do not try to synchronize with the
@@ -261,9 +267,9 @@ void arch_trigger_all_cpu_backtrace(void)
if (!regs)
regs = tp->kregs;
- spin_lock_irqsave(&global_reg_snapshot_lock, flags);
+ spin_lock_irqsave(&global_cpu_snapshot_lock, flags);
- memset(global_reg_snapshot, 0, sizeof(global_reg_snapshot));
+ memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot));
this_cpu = raw_smp_processor_id();
@@ -272,7 +278,7 @@ void arch_trigger_all_cpu_backtrace(void)
smp_fetch_global_regs();
for_each_online_cpu(cpu) {
- struct global_reg_snapshot *gp = &global_reg_snapshot[cpu];
+ struct global_reg_snapshot *gp = &global_cpu_snapshot[cpu].reg;
__global_reg_poll(gp);
@@ -295,9 +301,9 @@ void arch_trigger_all_cpu_backtrace(void)
}
}
- memset(global_reg_snapshot, 0, sizeof(global_reg_snapshot));
+ memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot));
- spin_unlock_irqrestore(&global_reg_snapshot_lock, flags);
+ spin_unlock_irqrestore(&global_cpu_snapshot_lock, flags);
}
#ifdef CONFIG_MAGIC_SYSRQ
@@ -309,16 +315,90 @@ static void sysrq_handle_globreg(int key)
static struct sysrq_key_op sparc_globalreg_op = {
.handler = sysrq_handle_globreg,
- .help_msg = "Globalregs",
+ .help_msg = "global-regs(Y)",
.action_msg = "Show Global CPU Regs",
};
-static int __init sparc_globreg_init(void)
+static void __global_pmu_self(int this_cpu)
+{
+ struct global_pmu_snapshot *pp;
+ int i, num;
+
+ pp = &global_cpu_snapshot[this_cpu].pmu;
+
+ num = 1;
+ if (tlb_type == hypervisor &&
+ sun4v_chip_type >= SUN4V_CHIP_NIAGARA4)
+ num = 4;
+
+ for (i = 0; i < num; i++) {
+ pp->pcr[i] = pcr_ops->read_pcr(i);
+ pp->pic[i] = pcr_ops->read_pic(i);
+ }
+}
+
+static void __global_pmu_poll(struct global_pmu_snapshot *pp)
+{
+ int limit = 0;
+
+ while (!pp->pcr[0] && ++limit < 100) {
+ barrier();
+ udelay(1);
+ }
+}
+
+static void pmu_snapshot_all_cpus(void)
+{
+ unsigned long flags;
+ int this_cpu, cpu;
+
+ spin_lock_irqsave(&global_cpu_snapshot_lock, flags);
+
+ memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot));
+
+ this_cpu = raw_smp_processor_id();
+
+ __global_pmu_self(this_cpu);
+
+ smp_fetch_global_pmu();
+
+ for_each_online_cpu(cpu) {
+ struct global_pmu_snapshot *pp = &global_cpu_snapshot[cpu].pmu;
+
+ __global_pmu_poll(pp);
+
+ printk("%c CPU[%3d]: PCR[%08lx:%08lx:%08lx:%08lx] PIC[%08lx:%08lx:%08lx:%08lx]\n",
+ (cpu == this_cpu ? '*' : ' '), cpu,
+ pp->pcr[0], pp->pcr[1], pp->pcr[2], pp->pcr[3],
+ pp->pic[0], pp->pic[1], pp->pic[2], pp->pic[3]);
+ }
+
+ memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot));
+
+ spin_unlock_irqrestore(&global_cpu_snapshot_lock, flags);
+}
+
+static void sysrq_handle_globpmu(int key)
{
- return register_sysrq_key('y', &sparc_globalreg_op);
+ pmu_snapshot_all_cpus();
}
-core_initcall(sparc_globreg_init);
+static struct sysrq_key_op sparc_globalpmu_op = {
+ .handler = sysrq_handle_globpmu,
+ .help_msg = "global-pmu(X)",
+ .action_msg = "Show Global PMU Regs",
+};
+
+static int __init sparc_sysrq_init(void)
+{
+ int ret = register_sysrq_key('y', &sparc_globalreg_op);
+
+ if (!ret)
+ ret = register_sysrq_key('x', &sparc_globalpmu_op);
+ return ret;
+}
+
+core_initcall(sparc_sysrq_init);
#endif
@@ -372,13 +452,16 @@ void flush_thread(void)
/* It's a bit more tricky when 64-bit tasks are involved... */
static unsigned long clone_stackframe(unsigned long csp, unsigned long psp)
{
+ bool stack_64bit = test_thread_64bit_stack(psp);
unsigned long fp, distance, rval;
- if (!(test_thread_flag(TIF_32BIT))) {
+ if (stack_64bit) {
csp += STACK_BIAS;
psp += STACK_BIAS;
__get_user(fp, &(((struct reg_window __user *)psp)->ins[6]));
fp += STACK_BIAS;
+ if (test_thread_flag(TIF_32BIT))
+ fp &= 0xffffffff;
} else
__get_user(fp, &(((struct reg_window32 __user *)psp)->ins[6]));
@@ -392,7 +475,7 @@ static unsigned long clone_stackframe(unsigned long csp, unsigned long psp)
rval = (csp - distance);
if (copy_in_user((void __user *) rval, (void __user *) psp, distance))
rval = 0;
- else if (test_thread_flag(TIF_32BIT)) {
+ else if (!stack_64bit) {
if (put_user(((u32)csp),
&(((struct reg_window32 __user *)rval)->ins[6])))
rval = 0;
@@ -427,18 +510,18 @@ void synchronize_user_stack(void)
flush_user_windows();
if ((window = get_thread_wsaved()) != 0) {
- int winsize = sizeof(struct reg_window);
- int bias = 0;
-
- if (test_thread_flag(TIF_32BIT))
- winsize = sizeof(struct reg_window32);
- else
- bias = STACK_BIAS;
-
window -= 1;
do {
- unsigned long sp = (t->rwbuf_stkptrs[window] + bias);
struct reg_window *rwin = &t->reg_window[window];
+ int winsize = sizeof(struct reg_window);
+ unsigned long sp;
+
+ sp = t->rwbuf_stkptrs[window];
+
+ if (test_thread_64bit_stack(sp))
+ sp += STACK_BIAS;
+ else
+ winsize = sizeof(struct reg_window32);
if (!copy_to_user((char __user *)sp, rwin, winsize)) {
shift_window_buffer(window, get_thread_wsaved() - 1, t);
@@ -464,13 +547,6 @@ void fault_in_user_windows(void)
{
struct thread_info *t = current_thread_info();
unsigned long window;
- int winsize = sizeof(struct reg_window);
- int bias = 0;
-
- if (test_thread_flag(TIF_32BIT))
- winsize = sizeof(struct reg_window32);
- else
- bias = STACK_BIAS;
flush_user_windows();
window = get_thread_wsaved();
@@ -478,8 +554,16 @@ void fault_in_user_windows(void)
if (likely(window != 0)) {
window -= 1;
do {
- unsigned long sp = (t->rwbuf_stkptrs[window] + bias);
struct reg_window *rwin = &t->reg_window[window];
+ int winsize = sizeof(struct reg_window);
+ unsigned long sp;
+
+ sp = t->rwbuf_stkptrs[window];
+
+ if (test_thread_64bit_stack(sp))
+ sp += STACK_BIAS;
+ else
+ winsize = sizeof(struct reg_window32);
if (unlikely(sp & 0x7UL))
stack_unaligned(sp);
@@ -538,64 +622,55 @@ asmlinkage long sparc_do_fork(unsigned long clone_flags,
* Child --> %o0 == parents pid, %o1 == 1
*/
int copy_thread(unsigned long clone_flags, unsigned long sp,
- unsigned long unused,
+ unsigned long arg,
struct task_struct *p, struct pt_regs *regs)
{
struct thread_info *t = task_thread_info(p);
struct sparc_stackf *parent_sf;
unsigned long child_stack_sz;
char *child_trap_frame;
- int kernel_thread;
-
- kernel_thread = (regs->tstate & TSTATE_PRIV) ? 1 : 0;
- parent_sf = ((struct sparc_stackf *) regs) - 1;
/* Calculate offset to stack_frame & pt_regs */
- child_stack_sz = ((STACKFRAME_SZ + TRACEREG_SZ) +
- (kernel_thread ? STACKFRAME_SZ : 0));
+ child_stack_sz = (STACKFRAME_SZ + TRACEREG_SZ);
child_trap_frame = (task_stack_page(p) +
(THREAD_SIZE - child_stack_sz));
- memcpy(child_trap_frame, parent_sf, child_stack_sz);
- t->flags = (t->flags & ~((0xffUL << TI_FLAG_CWP_SHIFT) |
- (0xffUL << TI_FLAG_CURRENT_DS_SHIFT))) |
- (((regs->tstate + 1) & TSTATE_CWP) << TI_FLAG_CWP_SHIFT);
t->new_child = 1;
t->ksp = ((unsigned long) child_trap_frame) - STACK_BIAS;
t->kregs = (struct pt_regs *) (child_trap_frame +
sizeof(struct sparc_stackf));
t->fpsaved[0] = 0;
- if (kernel_thread) {
- struct sparc_stackf *child_sf = (struct sparc_stackf *)
- (child_trap_frame + (STACKFRAME_SZ + TRACEREG_SZ));
-
- /* Zero terminate the stack backtrace. */
- child_sf->fp = NULL;
- t->kregs->u_regs[UREG_FP] =
- ((unsigned long) child_sf) - STACK_BIAS;
+ if (unlikely(p->flags & PF_KTHREAD)) {
+ memset(child_trap_frame, 0, child_stack_sz);
+ __thread_flag_byte_ptr(t)[TI_FLAG_BYTE_CWP] =
+ (current_pt_regs()->tstate + 1) & TSTATE_CWP;
+ t->current_ds = ASI_P;
+ t->kregs->u_regs[UREG_G1] = sp; /* function */
+ t->kregs->u_regs[UREG_G2] = arg;
+ return 0;
+ }
- t->flags |= ((long)ASI_P << TI_FLAG_CURRENT_DS_SHIFT);
- t->kregs->u_regs[UREG_G6] = (unsigned long) t;
- t->kregs->u_regs[UREG_G4] = (unsigned long) t->task;
- } else {
- if (t->flags & _TIF_32BIT) {
- sp &= 0x00000000ffffffffUL;
- regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL;
- }
- t->kregs->u_regs[UREG_FP] = sp;
- t->flags |= ((long)ASI_AIUS << TI_FLAG_CURRENT_DS_SHIFT);
- if (sp != regs->u_regs[UREG_FP]) {
- unsigned long csp;
-
- csp = clone_stackframe(sp, regs->u_regs[UREG_FP]);
- if (!csp)
- return -EFAULT;
- t->kregs->u_regs[UREG_FP] = csp;
- }
- if (t->utraps)
- t->utraps[0]++;
+ parent_sf = ((struct sparc_stackf *) regs) - 1;
+ memcpy(child_trap_frame, parent_sf, child_stack_sz);
+ if (t->flags & _TIF_32BIT) {
+ sp &= 0x00000000ffffffffUL;
+ regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL;
}
+ t->kregs->u_regs[UREG_FP] = sp;
+ __thread_flag_byte_ptr(t)[TI_FLAG_BYTE_CWP] =
+ (regs->tstate + 1) & TSTATE_CWP;
+ t->current_ds = ASI_AIUS;
+ if (sp != regs->u_regs[UREG_FP]) {
+ unsigned long csp;
+
+ csp = clone_stackframe(sp, regs->u_regs[UREG_FP]);
+ if (!csp)
+ return -EFAULT;
+ t->kregs->u_regs[UREG_FP] = csp;
+ }
+ if (t->utraps)
+ t->utraps[0]++;
/* Set the return value for the child. */
t->kregs->u_regs[UREG_I0] = current->pid;
@@ -610,45 +685,6 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
return 0;
}
-/*
- * This is the mechanism for creating a new kernel thread.
- *
- * NOTE! Only a kernel-only process(ie the swapper or direct descendants
- * who haven't done an "execve()") should use this: it will work within
- * a system call from a "real" process, but the process memory space will
- * not be freed until both the parent and the child have exited.
- */
-pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
-{
- long retval;
-
- /* If the parent runs before fn(arg) is called by the child,
- * the input registers of this function can be clobbered.
- * So we stash 'fn' and 'arg' into global registers which
- * will not be modified by the parent.
- */
- __asm__ __volatile__("mov %4, %%g2\n\t" /* Save FN into global */
- "mov %5, %%g3\n\t" /* Save ARG into global */
- "mov %1, %%g1\n\t" /* Clone syscall nr. */
- "mov %2, %%o0\n\t" /* Clone flags. */
- "mov 0, %%o1\n\t" /* usp arg == 0 */
- "t 0x6d\n\t" /* Linux/Sparc clone(). */
- "brz,a,pn %%o1, 1f\n\t" /* Parent, just return. */
- " mov %%o0, %0\n\t"
- "jmpl %%g2, %%o7\n\t" /* Call the function. */
- " mov %%g3, %%o0\n\t" /* Set arg in delay. */
- "mov %3, %%g1\n\t"
- "t 0x6d\n\t" /* Linux/Sparc exit(). */
- /* Notreached by child. */
- "1:" :
- "=r" (retval) :
- "i" (__NR_clone), "r" (flags | CLONE_VM | CLONE_UNTRACED),
- "i" (__NR_exit), "r" (fn), "r" (arg) :
- "g1", "g2", "g3", "o0", "o1", "memory", "cc");
- return retval;
-}
-EXPORT_SYMBOL(kernel_thread);
-
typedef struct {
union {
unsigned int pr_regs[32];
@@ -715,41 +751,6 @@ int dump_fpu (struct pt_regs * regs, elf_fpregset_t * fpregs)
}
EXPORT_SYMBOL(dump_fpu);
-/*
- * sparc_execve() executes a new program after the asm stub has set
- * things up for us. This should basically do what I want it to.
- */
-asmlinkage int sparc_execve(struct pt_regs *regs)
-{
- int error, base = 0;
- struct filename *filename;
-
- /* User register window flush is done by entry.S */
-
- /* Check for indirect call. */
- if (regs->u_regs[UREG_G1] == 0)
- base = 1;
-
- filename = getname((char __user *)regs->u_regs[base + UREG_I0]);
- error = PTR_ERR(filename);
- if (IS_ERR(filename))
- goto out;
- error = do_execve(filename->name,
- (const char __user *const __user *)
- regs->u_regs[base + UREG_I1],
- (const char __user *const __user *)
- regs->u_regs[base + UREG_I2], regs);
- putname(filename);
- if (!error) {
- fprs_write(0);
- current_thread_info()->xfsr[0] = 0;
- current_thread_info()->fpsaved[0] = 0;
- regs->tstate &= ~TSTATE_PEF;
- }
-out:
- return error;
-}
-
unsigned long get_wchan(struct task_struct *task)
{
unsigned long pc, fp, bias = 0;
diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c
index 484dabac704..7ff45e4ba68 100644
--- a/arch/sparc/kernel/ptrace_64.c
+++ b/arch/sparc/kernel/ptrace_64.c
@@ -151,7 +151,7 @@ static int regwindow64_get(struct task_struct *target,
{
unsigned long rw_addr = regs->u_regs[UREG_I6];
- if (test_tsk_thread_flag(current, TIF_32BIT)) {
+ if (!test_thread_64bit_stack(rw_addr)) {
struct reg_window32 win32;
int i;
@@ -176,7 +176,7 @@ static int regwindow64_set(struct task_struct *target,
{
unsigned long rw_addr = regs->u_regs[UREG_I6];
- if (test_tsk_thread_flag(current, TIF_32BIT)) {
+ if (!test_thread_64bit_stack(rw_addr)) {
struct reg_window32 win32;
int i;
diff --git a/arch/sparc/kernel/setup_64.c b/arch/sparc/kernel/setup_64.c
index 0800e71d8a8..0eaf0059aae 100644
--- a/arch/sparc/kernel/setup_64.c
+++ b/arch/sparc/kernel/setup_64.c
@@ -316,6 +316,25 @@ static void __init popc_patch(void)
}
}
+static void __init pause_patch(void)
+{
+ struct pause_patch_entry *p;
+
+ p = &__pause_3insn_patch;
+ while (p < &__pause_3insn_patch_end) {
+ unsigned long i, addr = p->addr;
+
+ for (i = 0; i < 3; i++) {
+ *(unsigned int *) (addr + (i * 4)) = p->insns[i];
+ wmb();
+ __asm__ __volatile__("flush %0"
+ : : "r" (addr + (i * 4)));
+ }
+
+ p++;
+ }
+}
+
#ifdef CONFIG_SMP
void __init boot_cpu_id_too_large(int cpu)
{
@@ -528,6 +547,8 @@ static void __init init_sparc64_elf_hwcap(void)
if (sparc64_elf_hwcap & AV_SPARC_POPC)
popc_patch();
+ if (sparc64_elf_hwcap & AV_SPARC_PAUSE)
+ pause_patch();
}
void __init setup_arch(char **cmdline_p)
diff --git a/arch/sparc/kernel/signal_64.c b/arch/sparc/kernel/signal_64.c
index 867de2f8189..689e1ba6280 100644
--- a/arch/sparc/kernel/signal_64.c
+++ b/arch/sparc/kernel/signal_64.c
@@ -295,9 +295,7 @@ void do_rt_sigreturn(struct pt_regs *regs)
err |= restore_fpu_state(regs, fpu_save);
err |= __copy_from_user(&set, &sf->mask, sizeof(sigset_t));
- err |= do_sigaltstack(&sf->stack, NULL, (unsigned long)sf);
-
- if (err)
+ if (err || do_sigaltstack(&sf->stack, NULL, (unsigned long)sf) == -EFAULT)
goto segv;
err |= __get_user(rwin_save, &sf->rwin_save);
diff --git a/arch/sparc/kernel/smp_64.c b/arch/sparc/kernel/smp_64.c
index 781bcb10b8b..d94b878577b 100644
--- a/arch/sparc/kernel/smp_64.c
+++ b/arch/sparc/kernel/smp_64.c
@@ -852,6 +852,8 @@ extern unsigned long xcall_flush_tlb_mm;
extern unsigned long xcall_flush_tlb_pending;
extern unsigned long xcall_flush_tlb_kernel_range;
extern unsigned long xcall_fetch_glob_regs;
+extern unsigned long xcall_fetch_glob_pmu;
+extern unsigned long xcall_fetch_glob_pmu_n4;
extern unsigned long xcall_receive_signal;
extern unsigned long xcall_new_mmu_context_version;
#ifdef CONFIG_KGDB
@@ -1000,6 +1002,15 @@ void smp_fetch_global_regs(void)
smp_cross_call(&xcall_fetch_glob_regs, 0, 0, 0);
}
+void smp_fetch_global_pmu(void)
+{
+ if (tlb_type == hypervisor &&
+ sun4v_chip_type >= SUN4V_CHIP_NIAGARA4)
+ smp_cross_call(&xcall_fetch_glob_pmu_n4, 0, 0, 0);
+ else
+ smp_cross_call(&xcall_fetch_glob_pmu, 0, 0, 0);
+}
+
/* We know that the window frames of the user have been flushed
* to the stack before we get here because all callers of us
* are flush_tlb_*() routines, and these run after flush_cache_*()
diff --git a/arch/sparc/kernel/sys_sparc32.c b/arch/sparc/kernel/sys_sparc32.c
index c3239811a1b..03c7e929ec3 100644
--- a/arch/sparc/kernel/sys_sparc32.c
+++ b/arch/sparc/kernel/sys_sparc32.c
@@ -396,42 +396,6 @@ asmlinkage long compat_sys_rt_sigaction(int sig,
return ret;
}
-/*
- * sparc32_execve() executes a new program after the asm stub has set
- * things up for us. This should basically do what I want it to.
- */
-asmlinkage long sparc32_execve(struct pt_regs *regs)
-{
- int error, base = 0;
- struct filename *filename;
-
- /* User register window flush is done by entry.S */
-
- /* Check for indirect call. */
- if ((u32)regs->u_regs[UREG_G1] == 0)
- base = 1;
-
- filename = getname(compat_ptr(regs->u_regs[base + UREG_I0]));
- error = PTR_ERR(filename);
- if (IS_ERR(filename))
- goto out;
-
- error = compat_do_execve(filename->name,
- compat_ptr(regs->u_regs[base + UREG_I1]),
- compat_ptr(regs->u_regs[base + UREG_I2]), regs);
-
- putname(filename);
-
- if (!error) {
- fprs_write(0);
- current_thread_info()->xfsr[0] = 0;
- current_thread_info()->fpsaved[0] = 0;
- regs->tstate &= ~TSTATE_PEF;
- }
-out:
- return error;
-}
-
#ifdef CONFIG_MODULES
asmlinkage long sys32_init_module(void __user *umod, u32 len,
diff --git a/arch/sparc/kernel/sys_sparc_32.c b/arch/sparc/kernel/sys_sparc_32.c
index 0c9b31b22e0..a8e6eb0a11d 100644
--- a/arch/sparc/kernel/sys_sparc_32.c
+++ b/arch/sparc/kernel/sys_sparc_32.c
@@ -258,27 +258,3 @@ out:
up_read(&uts_sem);
return err;
}
-
-/*
- * Do a system call from kernel instead of calling sys_execve so we
- * end up with proper pt_regs.
- */
-int kernel_execve(const char *filename,
- const char *const argv[],
- const char *const envp[])
-{
- long __res;
- register long __g1 __asm__ ("g1") = __NR_execve;
- register long __o0 __asm__ ("o0") = (long)(filename);
- register long __o1 __asm__ ("o1") = (long)(argv);
- register long __o2 __asm__ ("o2") = (long)(envp);
- asm volatile ("t 0x10\n\t"
- "bcc 1f\n\t"
- "mov %%o0, %0\n\t"
- "sub %%g0, %%o0, %0\n\t"
- "1:\n\t"
- : "=r" (__res), "=&r" (__o0)
- : "1" (__o0), "r" (__o1), "r" (__o2), "r" (__g1)
- : "cc");
- return __res;
-}
diff --git a/arch/sparc/kernel/sys_sparc_64.c b/arch/sparc/kernel/sys_sparc_64.c
index 11c6c9603e7..51b85feb8b9 100644
--- a/arch/sparc/kernel/sys_sparc_64.c
+++ b/arch/sparc/kernel/sys_sparc_64.c
@@ -730,24 +730,7 @@ SYSCALL_DEFINE5(rt_sigaction, int, sig, const struct sigaction __user *, act,
return ret;
}
-/*
- * Do a system call from kernel instead of calling sys_execve so we
- * end up with proper pt_regs.
- */
-int kernel_execve(const char *filename,
- const char *const argv[],
- const char *const envp[])
+asmlinkage long sys_kern_features(void)
{
- long __res;
- register long __g1 __asm__ ("g1") = __NR_execve;
- register long __o0 __asm__ ("o0") = (long)(filename);
- register long __o1 __asm__ ("o1") = (long)(argv);
- register long __o2 __asm__ ("o2") = (long)(envp);
- asm volatile ("t 0x6d\n\t"
- "sub %%g0, %%o0, %0\n\t"
- "movcc %%xcc, %%o0, %0\n\t"
- : "=r" (__res), "=&r" (__o0)
- : "1" (__o0), "r" (__o1), "r" (__o2), "r" (__g1)
- : "cc");
- return __res;
+ return KERN_FEATURE_MIXED_MODE_STACK;
}
diff --git a/arch/sparc/kernel/syscalls.S b/arch/sparc/kernel/syscalls.S
index 7f5f65d0b3f..2ef41e67f0b 100644
--- a/arch/sparc/kernel/syscalls.S
+++ b/arch/sparc/kernel/syscalls.S
@@ -1,23 +1,19 @@
/* SunOS's execv() call only specifies the argv argument, the
* environment settings are the same as the calling processes.
*/
-sys_execve:
- sethi %hi(sparc_execve), %g1
- ba,pt %xcc, execve_merge
- or %g1, %lo(sparc_execve), %g1
+sys64_execve:
+ set sys_execve, %g1
+ jmpl %g1, %g0
+ flushw
#ifdef CONFIG_COMPAT
sunos_execv:
- stx %g0, [%sp + PTREGS_OFF + PT_V9_I2]
+ mov %g0, %o2
sys32_execve:
- sethi %hi(sparc32_execve), %g1
- or %g1, %lo(sparc32_execve), %g1
-#endif
-
-execve_merge:
- flushw
+ set compat_sys_execve, %g1
jmpl %g1, %g0
- add %sp, PTREGS_OFF, %o0
+ flushw
+#endif
.align 32
sys_sparc_pipe:
@@ -112,11 +108,16 @@ sys_clone:
ret_from_syscall:
/* Clear current_thread_info()->new_child. */
stb %g0, [%g6 + TI_NEW_CHILD]
- ldx [%g6 + TI_FLAGS], %l0
call schedule_tail
mov %g7, %o0
+ ldx [%sp + PTREGS_OFF + PT_V9_I0], %o0
+ brnz,pt %o0, ret_sys_call
+ ldx [%g6 + TI_FLAGS], %l0
+ ldx [%sp + PTREGS_OFF + PT_V9_G1], %l1
+ call %l1
+ ldx [%sp + PTREGS_OFF + PT_V9_G2], %o0
ba,pt %xcc, ret_sys_call
- ldx [%sp + PTREGS_OFF + PT_V9_I0], %o0
+ mov 0, %o0
.globl sparc_exit
.type sparc_exit,#function
@@ -222,7 +223,6 @@ ret_sys_call:
ldx [%sp + PTREGS_OFF + PT_V9_TNPC], %l1 ! pc = npc
2:
- stb %g0, [%g6 + TI_SYS_NOERROR]
/* System call success, clear Carry condition code. */
andn %g3, %g2, %g3
3:
diff --git a/arch/sparc/kernel/systbls_32.S b/arch/sparc/kernel/systbls_32.S
index 63402f9e9f5..5147f574f12 100644
--- a/arch/sparc/kernel/systbls_32.S
+++ b/arch/sparc/kernel/systbls_32.S
@@ -85,3 +85,4 @@ sys_call_table:
/*325*/ .long sys_pwritev, sys_rt_tgsigqueueinfo, sys_perf_event_open, sys_recvmmsg, sys_fanotify_init
/*330*/ .long sys_fanotify_mark, sys_prlimit64, sys_name_to_handle_at, sys_open_by_handle_at, sys_clock_adjtime
/*335*/ .long sys_syncfs, sys_sendmmsg, sys_setns, sys_process_vm_readv, sys_process_vm_writev
+/*340*/ .long sys_ni_syscall, sys_kcmp
diff --git a/arch/sparc/kernel/systbls_64.S b/arch/sparc/kernel/systbls_64.S
index 3a58e0d66f5..ebb7f5fc58f 100644
--- a/arch/sparc/kernel/systbls_64.S
+++ b/arch/sparc/kernel/systbls_64.S
@@ -86,6 +86,7 @@ sys_call_table32:
.word compat_sys_pwritev, compat_sys_rt_tgsigqueueinfo, sys_perf_event_open, compat_sys_recvmmsg, sys_fanotify_init
/*330*/ .word sys32_fanotify_mark, sys_prlimit64, sys_name_to_handle_at, compat_sys_open_by_handle_at, compat_sys_clock_adjtime
.word sys_syncfs, compat_sys_sendmmsg, sys_setns, compat_sys_process_vm_readv, compat_sys_process_vm_writev
+/*340*/ .word sys_kern_features, sys_kcmp
#endif /* CONFIG_COMPAT */
@@ -106,7 +107,7 @@ sys_call_table:
/*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
+ .word sys_reboot, sys_nis_syscall, sys_symlink, sys_readlink, sys64_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, sys_64_munmap, sys_mprotect
@@ -163,3 +164,4 @@ sys_call_table:
.word sys_pwritev, sys_rt_tgsigqueueinfo, sys_perf_event_open, sys_recvmmsg, sys_fanotify_init
/*330*/ .word sys_fanotify_mark, sys_prlimit64, sys_name_to_handle_at, sys_open_by_handle_at, sys_clock_adjtime
.word sys_syncfs, sys_sendmmsg, sys_setns, sys_process_vm_readv, sys_process_vm_writev
+/*340*/ .word sys_kern_features, sys_kcmp
diff --git a/arch/sparc/kernel/traps_64.c b/arch/sparc/kernel/traps_64.c
index b66a77968f3..e7ecf1507d9 100644
--- a/arch/sparc/kernel/traps_64.c
+++ b/arch/sparc/kernel/traps_64.c
@@ -2688,8 +2688,8 @@ void __init trap_init(void)
TI_PRE_COUNT != offsetof(struct thread_info,
preempt_count) ||
TI_NEW_CHILD != offsetof(struct thread_info, new_child) ||
- TI_SYS_NOERROR != offsetof(struct thread_info,
- syscall_noerror) ||
+ TI_CURRENT_DS != offsetof(struct thread_info,
+ current_ds) ||
TI_RESTART_BLOCK != offsetof(struct thread_info,
restart_block) ||
TI_KUNA_REGS != offsetof(struct thread_info,
diff --git a/arch/sparc/kernel/unaligned_64.c b/arch/sparc/kernel/unaligned_64.c
index f81d038f734..8201c25e766 100644
--- a/arch/sparc/kernel/unaligned_64.c
+++ b/arch/sparc/kernel/unaligned_64.c
@@ -113,21 +113,24 @@ static inline long sign_extend_imm13(long imm)
static unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs)
{
- unsigned long value;
+ unsigned long value, fp;
if (reg < 16)
return (!reg ? 0 : regs->u_regs[reg]);
+
+ fp = regs->u_regs[UREG_FP];
+
if (regs->tstate & TSTATE_PRIV) {
struct reg_window *win;
- win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS);
+ win = (struct reg_window *)(fp + STACK_BIAS);
value = win->locals[reg - 16];
- } else if (test_thread_flag(TIF_32BIT)) {
+ } else if (!test_thread_64bit_stack(fp)) {
struct reg_window32 __user *win32;
- win32 = (struct reg_window32 __user *)((unsigned long)((u32)regs->u_regs[UREG_FP]));
+ win32 = (struct reg_window32 __user *)((unsigned long)((u32)fp));
get_user(value, &win32->locals[reg - 16]);
} else {
struct reg_window __user *win;
- win = (struct reg_window __user *)(regs->u_regs[UREG_FP] + STACK_BIAS);
+ win = (struct reg_window __user *)(fp + STACK_BIAS);
get_user(value, &win->locals[reg - 16]);
}
return value;
@@ -135,19 +138,24 @@ static unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs)
static unsigned long *fetch_reg_addr(unsigned int reg, struct pt_regs *regs)
{
+ unsigned long fp;
+
if (reg < 16)
return &regs->u_regs[reg];
+
+ fp = regs->u_regs[UREG_FP];
+
if (regs->tstate & TSTATE_PRIV) {
struct reg_window *win;
- win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS);
+ win = (struct reg_window *)(fp + STACK_BIAS);
return &win->locals[reg - 16];
- } else if (test_thread_flag(TIF_32BIT)) {
+ } else if (!test_thread_64bit_stack(fp)) {
struct reg_window32 *win32;
- win32 = (struct reg_window32 *)((unsigned long)((u32)regs->u_regs[UREG_FP]));
+ win32 = (struct reg_window32 *)((unsigned long)((u32)fp));
return (unsigned long *)&win32->locals[reg - 16];
} else {
struct reg_window *win;
- win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS);
+ win = (struct reg_window *)(fp + STACK_BIAS);
return &win->locals[reg - 16];
}
}
@@ -392,13 +400,15 @@ int handle_popc(u32 insn, struct pt_regs *regs)
if (rd)
regs->u_regs[rd] = ret;
} else {
- if (test_thread_flag(TIF_32BIT)) {
+ unsigned long fp = regs->u_regs[UREG_FP];
+
+ if (!test_thread_64bit_stack(fp)) {
struct reg_window32 __user *win32;
- win32 = (struct reg_window32 __user *)((unsigned long)((u32)regs->u_regs[UREG_FP]));
+ win32 = (struct reg_window32 __user *)((unsigned long)((u32)fp));
put_user(ret, &win32->locals[rd - 16]);
} else {
struct reg_window __user *win;
- win = (struct reg_window __user *)(regs->u_regs[UREG_FP] + STACK_BIAS);
+ win = (struct reg_window __user *)(fp + STACK_BIAS);
put_user(ret, &win->locals[rd - 16]);
}
}
@@ -554,7 +564,7 @@ void handle_ld_nf(u32 insn, struct pt_regs *regs)
reg[0] = 0;
if ((insn & 0x780000) == 0x180000)
reg[1] = 0;
- } else if (test_thread_flag(TIF_32BIT)) {
+ } else if (!test_thread_64bit_stack(regs->u_regs[UREG_FP])) {
put_user(0, (int __user *) reg);
if ((insn & 0x780000) == 0x180000)
put_user(0, ((int __user *) reg) + 1);
diff --git a/arch/sparc/kernel/visemul.c b/arch/sparc/kernel/visemul.c
index 08e074b7eb6..c096c624ac4 100644
--- a/arch/sparc/kernel/visemul.c
+++ b/arch/sparc/kernel/visemul.c
@@ -149,21 +149,24 @@ static inline void maybe_flush_windows(unsigned int rs1, unsigned int rs2,
static unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs)
{
- unsigned long value;
+ unsigned long value, fp;
if (reg < 16)
return (!reg ? 0 : regs->u_regs[reg]);
+
+ fp = regs->u_regs[UREG_FP];
+
if (regs->tstate & TSTATE_PRIV) {
struct reg_window *win;
- win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS);
+ win = (struct reg_window *)(fp + STACK_BIAS);
value = win->locals[reg - 16];
- } else if (test_thread_flag(TIF_32BIT)) {
+ } else if (!test_thread_64bit_stack(fp)) {
struct reg_window32 __user *win32;
- win32 = (struct reg_window32 __user *)((unsigned long)((u32)regs->u_regs[UREG_FP]));
+ win32 = (struct reg_window32 __user *)((unsigned long)((u32)fp));
get_user(value, &win32->locals[reg - 16]);
} else {
struct reg_window __user *win;
- win = (struct reg_window __user *)(regs->u_regs[UREG_FP] + STACK_BIAS);
+ win = (struct reg_window __user *)(fp + STACK_BIAS);
get_user(value, &win->locals[reg - 16]);
}
return value;
@@ -172,16 +175,18 @@ static unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs)
static inline unsigned long __user *__fetch_reg_addr_user(unsigned int reg,
struct pt_regs *regs)
{
+ unsigned long fp = regs->u_regs[UREG_FP];
+
BUG_ON(reg < 16);
BUG_ON(regs->tstate & TSTATE_PRIV);
- if (test_thread_flag(TIF_32BIT)) {
+ if (!test_thread_64bit_stack(fp)) {
struct reg_window32 __user *win32;
- win32 = (struct reg_window32 __user *)((unsigned long)((u32)regs->u_regs[UREG_FP]));
+ win32 = (struct reg_window32 __user *)((unsigned long)((u32)fp));
return (unsigned long __user *)&win32->locals[reg - 16];
} else {
struct reg_window __user *win;
- win = (struct reg_window __user *)(regs->u_regs[UREG_FP] + STACK_BIAS);
+ win = (struct reg_window __user *)(fp + STACK_BIAS);
return &win->locals[reg - 16];
}
}
@@ -204,7 +209,7 @@ static void store_reg(struct pt_regs *regs, unsigned long val, unsigned long rd)
} else {
unsigned long __user *rd_user = __fetch_reg_addr_user(rd, regs);
- if (test_thread_flag(TIF_32BIT))
+ if (!test_thread_64bit_stack(regs->u_regs[UREG_FP]))
__put_user((u32)val, (u32 __user *)rd_user);
else
__put_user(val, rd_user);
diff --git a/arch/sparc/kernel/vmlinux.lds.S b/arch/sparc/kernel/vmlinux.lds.S
index 89c2c29f154..0bacceb1915 100644
--- a/arch/sparc/kernel/vmlinux.lds.S
+++ b/arch/sparc/kernel/vmlinux.lds.S
@@ -132,6 +132,11 @@ SECTIONS
*(.popc_6insn_patch)
__popc_6insn_patch_end = .;
}
+ .pause_3insn_patch : {
+ __pause_3insn_patch = .;
+ *(.pause_3insn_patch)
+ __pause_3insn_patch_end = .;
+ }
PERCPU_SECTION(SMP_CACHE_BYTES)
. = ALIGN(PAGE_SIZE);
diff --git a/arch/sparc/kernel/winfixup.S b/arch/sparc/kernel/winfixup.S
index a6b0863c27d..1e67ce95836 100644
--- a/arch/sparc/kernel/winfixup.S
+++ b/arch/sparc/kernel/winfixup.S
@@ -43,6 +43,8 @@ spill_fixup_mna:
spill_fixup_dax:
TRAP_LOAD_THREAD_REG(%g6, %g1)
ldx [%g6 + TI_FLAGS], %g1
+ andcc %sp, 0x1, %g0
+ movne %icc, 0, %g1
andcc %g1, _TIF_32BIT, %g0
ldub [%g6 + TI_WSAVED], %g1
sll %g1, 3, %g3