/* arch/sparc64/kernel/process.c
*
* Copyright (C) 1995, 1996, 2008 David S. Miller (davem@davemloft.net)
* Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
* Copyright (C) 1997, 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
/*
* This file handles the architecture-dependent parts of process handling..
*/
#include <stdarg.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/smp.h>
#include <linux/stddef.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/user.h>
#include <linux/delay.h>
#include <linux/compat.h>
#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>
#include <linux/context_tracking.h>
#include <asm/uaccess.h>
#include <asm/page.h>
#include <asm/pgalloc.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <asm/pstate.h>
#include <asm/elf.h>
#include <asm/fpumacro.h>
#include <asm/head.h>
#include <asm/cpudata.h>
#include <asm/mmu_context.h>
#include <asm/unistd.h>
#include <asm/hypervisor.h>
#include <asm/syscalls.h>
#include <asm/irq_regs.h>
#include <asm/smp.h>
#include <asm/pcr.h>
#include "kstack.h"
/* Idle loop support on sparc64. */
void arch_cpu_idle(void)
{
if (tlb_type != hypervisor) {
touch_nmi_watchdog();
local_irq_enable();
} else {
unsigned long pstate;
local_irq_enable();
/* The sun4v sleeping code requires that we have PSTATE.IE cleared over
* the cpu sleep hypervisor call.
*/
__asm__ __volatile__(
"rdpr %%pstate, %0\n\t"
"andn %0, %1, %0\n\t"
"wrpr %0, %%g0, %%pstate"
: "=&r" (pstate)
: "i" (PSTATE_IE));
if (!need_resched() && !cpu_is_offline(smp_processor_id()))
sun4v_cpu_yield();
/* Re-enable interrupts. */
__asm__ __volatile__(
"rdpr %%pstate, %0\n\t"
"or %0, %1, %0\n\t"
"wrpr %0, %%g0, %%pstate"
: "=&r" (pstate)
: "i" (PSTATE_IE));
}
}
#ifdef CONFIG_HOTPLUG_CPU
void arch_cpu_idle_dead(void)
{
sched_preempt_enable_no_resched();
cpu_play_dead();
}
#endif
#ifdef CONFIG_COMPAT
static void show_regwindow32(struct pt_regs *regs)
{
struct reg_window32 __user *rw;
struct reg_window32 r_w;
mm_segment_t old_fs;
__asm__ __volatile__ ("flushw");
rw = compat_ptr((unsigned)regs->u_regs[14]);
old_fs = get_fs();
set_fs (USER_DS);
if (copy_from_user (&r_w, rw, sizeof(r_w))) {
set_fs (old_fs);
return;
}
set_fs (old_fs);
printk("l0: %08x l1: %08x l2: %08x l3: %08x "
"l4: %08x l5: %08x l6: %08x l7: %08x\n",
r_w.locals[0], r_w.locals[1], r_w.locals[2], r_w.locals[3],
r_w.locals[4], r_w.locals[5], r_w.locals[6], r_w.locals[7]);
printk("i0: %08x i1: %08x i2: %08x i3: %08x "
"i4: %08x i5: %08x i6: %08x i7: %08x\n",
r_w.ins[0], r_w.ins[1], r_w.ins[2], r_w.ins[3],
r_w.ins[4], r_w.ins[5], r_w.ins[6], r_w.ins[7]);
}
#else
#define show_regwindow32(regs) do { } while (0)
#endif
static void show_regwindow(struct pt_regs *regs)
{
struct reg_window __user *rw;
struct reg_window *rwk;
struct reg_window r_w;
mm_segment_t old_fs;
if ((regs->tstate & TSTATE_PRIV) || !(test_thread_flag(TIF_32BIT))) {
__asm__ __volatile__ ("flushw");
rw = (struct reg_window __user *)
(regs->u_regs[14] + STACK_BIAS);
rwk = (struct reg_window *)
(regs->u_regs[14] + STACK_BIAS);
if (!(regs->tstate & TSTATE_PRIV)) {
old_fs = get_fs();
set_fs (USER_DS);
if (copy_from_user (&r_w, rw, sizeof(r_w))) {
set_fs (old_fs);
return;
}
rwk = &r_w;
set_fs (old_fs);
}
} else {
show_regwindow32(regs);
return;
}
printk("l0: %016lx l1: %016lx l2: %016lx l3: %016lx\n",
rwk->locals[0], rwk->locals[1], rwk->locals[2], rwk->locals[3]);
printk("l4: %016lx l5: %016lx l6: %016lx l7: %016lx\n",
rwk->locals[4], rwk->locals[5], rwk->locals[6], rwk->locals[7]);
printk("i0: %016lx i1: %016lx i2: %016lx i3: %016lx\n",
rwk->ins[0], rwk->ins[1], rwk->ins[2], rwk->ins[3]);
printk("i4: %016lx i5: %016lx i6: %016lx i7: %016lx\n",
rwk->ins[4], rwk->ins[5], rwk->ins[6], rwk->ins[7]);
if (regs->tstate & TSTATE_PRIV)
printk("I7: <%pS>\n", (void *) rwk->ins[7]);
}
void show_regs(struct pt_regs *regs)
{
show_regs_print_info(KERN_DEFAULT);
printk("TSTATE: %016lx TPC: %016lx TNPC: %016lx Y: %08x %s\n", regs->tstate,
regs->tpc, regs->tnpc, regs->y, print_tainted());
printk("TPC: <%pS>\n", (void *) regs->tpc);
printk("g0: %016lx g1: %016lx g2: %016lx g3: %016lx\n",
regs->u_regs[0], regs->u_regs[1], regs->u_regs[2],
regs->u_regs[3]);
printk("g4: %016lx g5: %016lx g6: %016lx g7: %016lx\n",
regs->u_regs[4], regs->u_regs[5], regs->u_regs[6],
regs->u_regs[7]);
printk("o0: %016lx o1: %016lx o2: %016lx o3: %016lx\n",
regs->u_regs[8], regs->u_regs[9], regs->u_regs[10],
regs->u_regs[11]);
printk("o4: %016lx o5: %016lx sp: %016lx ret_pc: %016lx\n",
regs->u_regs[12], regs->u_regs[13], regs->u_regs[14],
regs->u_regs[15]);
printk("RPC: <%pS>\n", (void *) regs->u_regs[15]);
show_regwindow(regs);
show_stack(current, (unsigned long *) regs->u_regs[UREG_FP]);
}
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();
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;
rw = (struct reg_window *)
(regs->u_regs[UREG_FP] + STACK_BIAS);
if (kstack_valid(tp, (unsigned long) rw)) {
rp->i7 = rw->ins[7];
rw = (struct reg_window *)
(rw->ins[6] + STACK_BIAS);
if (kstack_valid(tp, (unsigned long) rw))
rp->rpc = rw->ins[7];
}
} else {
rp->i7 = 0;
rp->rpc = 0;
}
rp->thread = tp;
}
/* In order to avoid hangs we do not try to synchronize with the
* global register dump client cpus. The last store they make is to
* the thread pointer, so do a short poll waiting for that to become
* non-NULL.
*/
static void __global_reg_poll(struct global_reg_snapshot *gp)
{
int limit = 0;
while (!gp->thread && ++limit < 100) {
barrier();
udelay(1);
}
}
void arch_trigger_all_cpu_backtrace(bool include_self)
{
struct thread_info *tp = current_thread_info();
struct pt_regs *regs = get_irq_regs();
unsigned long flags;
int this_cpu, cpu;
if (!regs)
regs = tp->kregs;
spin_lock_irqsave(&global_cpu_snapshot_lock, flags);
this_cpu = raw_smp_processor_id();
memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot));
if (include_self)
__global_reg_self(tp, regs, this_cpu);
smp_fetch_global_regs();
for_each_online_cpu(cpu) {
struct global_reg_snapshot *gp;
if (!include_self && cpu == this_cpu)
continue;
gp = &global_cpu_snapshot[cpu].reg;
__global_reg_poll(gp);
tp = gp->thread;
printk("%c CPU[%3d]: TSTATE[%016lx] TPC[%016lx] TNPC[%016lx] TASK[%s:%d]\n",
(cpu == this_cpu ? '*' : ' '), cpu,
gp->tstate, gp->tpc, gp->tnpc,
((tp && tp->task) ? tp->task->comm : "NULL"),
((tp && tp->task) ? tp->task->pid : -1));
if (gp->tstate & TSTATE_PRIV) {
printk(" TPC[%pS] O7[%pS] I7[%pS] RPC[%pS]\n",
(void *) gp->tpc,
(void *) gp->o7,
(void *) gp->i7,
(void *) gp->rpc);
} else {
printk(" TPC[%lx] O7[%lx] I7[%lx] RPC[%lx]\n",
gp->tpc, gp->o7, gp->i7, gp->rpc);
}
touch_nmi_watchdog();
}
memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot));
spin_unlock_irqrestore(&global_cpu_snapshot_lock, flags);
}
#ifdef CONFIG_MAGIC_SYSRQ
static void sysrq_handle_globreg(int key)
{
arch_trigg