162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* arch/sparc64/kernel/process.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 1995, 1996, 2008 David S. Miller (davem@davemloft.net) 562306a36Sopenharmony_ci * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) 662306a36Sopenharmony_ci * Copyright (C) 1997, 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci * This file handles the architecture-dependent parts of process handling.. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci#include <linux/errno.h> 1362306a36Sopenharmony_ci#include <linux/export.h> 1462306a36Sopenharmony_ci#include <linux/sched.h> 1562306a36Sopenharmony_ci#include <linux/sched/debug.h> 1662306a36Sopenharmony_ci#include <linux/sched/task.h> 1762306a36Sopenharmony_ci#include <linux/sched/task_stack.h> 1862306a36Sopenharmony_ci#include <linux/kernel.h> 1962306a36Sopenharmony_ci#include <linux/mm.h> 2062306a36Sopenharmony_ci#include <linux/fs.h> 2162306a36Sopenharmony_ci#include <linux/smp.h> 2262306a36Sopenharmony_ci#include <linux/stddef.h> 2362306a36Sopenharmony_ci#include <linux/ptrace.h> 2462306a36Sopenharmony_ci#include <linux/slab.h> 2562306a36Sopenharmony_ci#include <linux/user.h> 2662306a36Sopenharmony_ci#include <linux/delay.h> 2762306a36Sopenharmony_ci#include <linux/compat.h> 2862306a36Sopenharmony_ci#include <linux/tick.h> 2962306a36Sopenharmony_ci#include <linux/init.h> 3062306a36Sopenharmony_ci#include <linux/cpu.h> 3162306a36Sopenharmony_ci#include <linux/perf_event.h> 3262306a36Sopenharmony_ci#include <linux/elfcore.h> 3362306a36Sopenharmony_ci#include <linux/sysrq.h> 3462306a36Sopenharmony_ci#include <linux/nmi.h> 3562306a36Sopenharmony_ci#include <linux/context_tracking.h> 3662306a36Sopenharmony_ci#include <linux/signal.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include <linux/uaccess.h> 3962306a36Sopenharmony_ci#include <asm/page.h> 4062306a36Sopenharmony_ci#include <asm/pgalloc.h> 4162306a36Sopenharmony_ci#include <asm/processor.h> 4262306a36Sopenharmony_ci#include <asm/pstate.h> 4362306a36Sopenharmony_ci#include <asm/elf.h> 4462306a36Sopenharmony_ci#include <asm/fpumacro.h> 4562306a36Sopenharmony_ci#include <asm/head.h> 4662306a36Sopenharmony_ci#include <asm/cpudata.h> 4762306a36Sopenharmony_ci#include <asm/mmu_context.h> 4862306a36Sopenharmony_ci#include <asm/unistd.h> 4962306a36Sopenharmony_ci#include <asm/hypervisor.h> 5062306a36Sopenharmony_ci#include <asm/syscalls.h> 5162306a36Sopenharmony_ci#include <asm/irq_regs.h> 5262306a36Sopenharmony_ci#include <asm/smp.h> 5362306a36Sopenharmony_ci#include <asm/pcr.h> 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#include "kstack.h" 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* Idle loop support on sparc64. */ 5862306a36Sopenharmony_civoid arch_cpu_idle(void) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci if (tlb_type != hypervisor) { 6162306a36Sopenharmony_ci touch_nmi_watchdog(); 6262306a36Sopenharmony_ci } else { 6362306a36Sopenharmony_ci unsigned long pstate; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci raw_local_irq_enable(); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* The sun4v sleeping code requires that we have PSTATE.IE cleared over 6862306a36Sopenharmony_ci * the cpu sleep hypervisor call. 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci __asm__ __volatile__( 7162306a36Sopenharmony_ci "rdpr %%pstate, %0\n\t" 7262306a36Sopenharmony_ci "andn %0, %1, %0\n\t" 7362306a36Sopenharmony_ci "wrpr %0, %%g0, %%pstate" 7462306a36Sopenharmony_ci : "=&r" (pstate) 7562306a36Sopenharmony_ci : "i" (PSTATE_IE)); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (!need_resched() && !cpu_is_offline(smp_processor_id())) { 7862306a36Sopenharmony_ci sun4v_cpu_yield(); 7962306a36Sopenharmony_ci /* If resumed by cpu_poke then we need to explicitly 8062306a36Sopenharmony_ci * call scheduler_ipi(). 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci scheduler_poke(); 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* Re-enable interrupts. */ 8662306a36Sopenharmony_ci __asm__ __volatile__( 8762306a36Sopenharmony_ci "rdpr %%pstate, %0\n\t" 8862306a36Sopenharmony_ci "or %0, %1, %0\n\t" 8962306a36Sopenharmony_ci "wrpr %0, %%g0, %%pstate" 9062306a36Sopenharmony_ci : "=&r" (pstate) 9162306a36Sopenharmony_ci : "i" (PSTATE_IE)); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci raw_local_irq_disable(); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 9862306a36Sopenharmony_civoid __noreturn arch_cpu_idle_dead(void) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci sched_preempt_enable_no_resched(); 10162306a36Sopenharmony_ci cpu_play_dead(); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci#endif 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 10662306a36Sopenharmony_cistatic void show_regwindow32(struct pt_regs *regs) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct reg_window32 __user *rw; 10962306a36Sopenharmony_ci struct reg_window32 r_w; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci __asm__ __volatile__ ("flushw"); 11262306a36Sopenharmony_ci rw = compat_ptr((unsigned int)regs->u_regs[14]); 11362306a36Sopenharmony_ci if (copy_from_user (&r_w, rw, sizeof(r_w))) { 11462306a36Sopenharmony_ci return; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci printk("l0: %08x l1: %08x l2: %08x l3: %08x " 11862306a36Sopenharmony_ci "l4: %08x l5: %08x l6: %08x l7: %08x\n", 11962306a36Sopenharmony_ci r_w.locals[0], r_w.locals[1], r_w.locals[2], r_w.locals[3], 12062306a36Sopenharmony_ci r_w.locals[4], r_w.locals[5], r_w.locals[6], r_w.locals[7]); 12162306a36Sopenharmony_ci printk("i0: %08x i1: %08x i2: %08x i3: %08x " 12262306a36Sopenharmony_ci "i4: %08x i5: %08x i6: %08x i7: %08x\n", 12362306a36Sopenharmony_ci r_w.ins[0], r_w.ins[1], r_w.ins[2], r_w.ins[3], 12462306a36Sopenharmony_ci r_w.ins[4], r_w.ins[5], r_w.ins[6], r_w.ins[7]); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci#else 12762306a36Sopenharmony_ci#define show_regwindow32(regs) do { } while (0) 12862306a36Sopenharmony_ci#endif 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void show_regwindow(struct pt_regs *regs) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct reg_window __user *rw; 13362306a36Sopenharmony_ci struct reg_window *rwk; 13462306a36Sopenharmony_ci struct reg_window r_w; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if ((regs->tstate & TSTATE_PRIV) || !(test_thread_flag(TIF_32BIT))) { 13762306a36Sopenharmony_ci __asm__ __volatile__ ("flushw"); 13862306a36Sopenharmony_ci rw = (struct reg_window __user *) 13962306a36Sopenharmony_ci (regs->u_regs[14] + STACK_BIAS); 14062306a36Sopenharmony_ci rwk = (struct reg_window *) 14162306a36Sopenharmony_ci (regs->u_regs[14] + STACK_BIAS); 14262306a36Sopenharmony_ci if (!(regs->tstate & TSTATE_PRIV)) { 14362306a36Sopenharmony_ci if (copy_from_user (&r_w, rw, sizeof(r_w))) { 14462306a36Sopenharmony_ci return; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci rwk = &r_w; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci } else { 14962306a36Sopenharmony_ci show_regwindow32(regs); 15062306a36Sopenharmony_ci return; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci printk("l0: %016lx l1: %016lx l2: %016lx l3: %016lx\n", 15362306a36Sopenharmony_ci rwk->locals[0], rwk->locals[1], rwk->locals[2], rwk->locals[3]); 15462306a36Sopenharmony_ci printk("l4: %016lx l5: %016lx l6: %016lx l7: %016lx\n", 15562306a36Sopenharmony_ci rwk->locals[4], rwk->locals[5], rwk->locals[6], rwk->locals[7]); 15662306a36Sopenharmony_ci printk("i0: %016lx i1: %016lx i2: %016lx i3: %016lx\n", 15762306a36Sopenharmony_ci rwk->ins[0], rwk->ins[1], rwk->ins[2], rwk->ins[3]); 15862306a36Sopenharmony_ci printk("i4: %016lx i5: %016lx i6: %016lx i7: %016lx\n", 15962306a36Sopenharmony_ci rwk->ins[4], rwk->ins[5], rwk->ins[6], rwk->ins[7]); 16062306a36Sopenharmony_ci if (regs->tstate & TSTATE_PRIV) 16162306a36Sopenharmony_ci printk("I7: <%pS>\n", (void *) rwk->ins[7]); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_civoid show_regs(struct pt_regs *regs) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci show_regs_print_info(KERN_DEFAULT); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci printk("TSTATE: %016lx TPC: %016lx TNPC: %016lx Y: %08x %s\n", regs->tstate, 16962306a36Sopenharmony_ci regs->tpc, regs->tnpc, regs->y, print_tainted()); 17062306a36Sopenharmony_ci printk("TPC: <%pS>\n", (void *) regs->tpc); 17162306a36Sopenharmony_ci printk("g0: %016lx g1: %016lx g2: %016lx g3: %016lx\n", 17262306a36Sopenharmony_ci regs->u_regs[0], regs->u_regs[1], regs->u_regs[2], 17362306a36Sopenharmony_ci regs->u_regs[3]); 17462306a36Sopenharmony_ci printk("g4: %016lx g5: %016lx g6: %016lx g7: %016lx\n", 17562306a36Sopenharmony_ci regs->u_regs[4], regs->u_regs[5], regs->u_regs[6], 17662306a36Sopenharmony_ci regs->u_regs[7]); 17762306a36Sopenharmony_ci printk("o0: %016lx o1: %016lx o2: %016lx o3: %016lx\n", 17862306a36Sopenharmony_ci regs->u_regs[8], regs->u_regs[9], regs->u_regs[10], 17962306a36Sopenharmony_ci regs->u_regs[11]); 18062306a36Sopenharmony_ci printk("o4: %016lx o5: %016lx sp: %016lx ret_pc: %016lx\n", 18162306a36Sopenharmony_ci regs->u_regs[12], regs->u_regs[13], regs->u_regs[14], 18262306a36Sopenharmony_ci regs->u_regs[15]); 18362306a36Sopenharmony_ci printk("RPC: <%pS>\n", (void *) regs->u_regs[15]); 18462306a36Sopenharmony_ci show_regwindow(regs); 18562306a36Sopenharmony_ci show_stack(current, (unsigned long *)regs->u_regs[UREG_FP], KERN_DEFAULT); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ciunion global_cpu_snapshot global_cpu_snapshot[NR_CPUS]; 18962306a36Sopenharmony_cistatic DEFINE_SPINLOCK(global_cpu_snapshot_lock); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic void __global_reg_self(struct thread_info *tp, struct pt_regs *regs, 19262306a36Sopenharmony_ci int this_cpu) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct global_reg_snapshot *rp; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci flushw_all(); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci rp = &global_cpu_snapshot[this_cpu].reg; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci rp->tstate = regs->tstate; 20162306a36Sopenharmony_ci rp->tpc = regs->tpc; 20262306a36Sopenharmony_ci rp->tnpc = regs->tnpc; 20362306a36Sopenharmony_ci rp->o7 = regs->u_regs[UREG_I7]; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (regs->tstate & TSTATE_PRIV) { 20662306a36Sopenharmony_ci struct reg_window *rw; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci rw = (struct reg_window *) 20962306a36Sopenharmony_ci (regs->u_regs[UREG_FP] + STACK_BIAS); 21062306a36Sopenharmony_ci if (kstack_valid(tp, (unsigned long) rw)) { 21162306a36Sopenharmony_ci rp->i7 = rw->ins[7]; 21262306a36Sopenharmony_ci rw = (struct reg_window *) 21362306a36Sopenharmony_ci (rw->ins[6] + STACK_BIAS); 21462306a36Sopenharmony_ci if (kstack_valid(tp, (unsigned long) rw)) 21562306a36Sopenharmony_ci rp->rpc = rw->ins[7]; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci } else { 21862306a36Sopenharmony_ci rp->i7 = 0; 21962306a36Sopenharmony_ci rp->rpc = 0; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci rp->thread = tp; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci/* In order to avoid hangs we do not try to synchronize with the 22562306a36Sopenharmony_ci * global register dump client cpus. The last store they make is to 22662306a36Sopenharmony_ci * the thread pointer, so do a short poll waiting for that to become 22762306a36Sopenharmony_ci * non-NULL. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_cistatic void __global_reg_poll(struct global_reg_snapshot *gp) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci int limit = 0; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci while (!gp->thread && ++limit < 100) { 23462306a36Sopenharmony_ci barrier(); 23562306a36Sopenharmony_ci udelay(1); 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_civoid arch_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct thread_info *tp = current_thread_info(); 24262306a36Sopenharmony_ci struct pt_regs *regs = get_irq_regs(); 24362306a36Sopenharmony_ci unsigned long flags; 24462306a36Sopenharmony_ci int this_cpu, cpu; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (!regs) 24762306a36Sopenharmony_ci regs = tp->kregs; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci spin_lock_irqsave(&global_cpu_snapshot_lock, flags); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci this_cpu = raw_smp_processor_id(); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot)); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (cpumask_test_cpu(this_cpu, mask) && this_cpu != exclude_cpu) 25662306a36Sopenharmony_ci __global_reg_self(tp, regs, this_cpu); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci smp_fetch_global_regs(); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci for_each_cpu(cpu, mask) { 26162306a36Sopenharmony_ci struct global_reg_snapshot *gp; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (cpu == exclude_cpu) 26462306a36Sopenharmony_ci continue; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci gp = &global_cpu_snapshot[cpu].reg; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci __global_reg_poll(gp); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci tp = gp->thread; 27162306a36Sopenharmony_ci printk("%c CPU[%3d]: TSTATE[%016lx] TPC[%016lx] TNPC[%016lx] TASK[%s:%d]\n", 27262306a36Sopenharmony_ci (cpu == this_cpu ? '*' : ' '), cpu, 27362306a36Sopenharmony_ci gp->tstate, gp->tpc, gp->tnpc, 27462306a36Sopenharmony_ci ((tp && tp->task) ? tp->task->comm : "NULL"), 27562306a36Sopenharmony_ci ((tp && tp->task) ? tp->task->pid : -1)); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (gp->tstate & TSTATE_PRIV) { 27862306a36Sopenharmony_ci printk(" TPC[%pS] O7[%pS] I7[%pS] RPC[%pS]\n", 27962306a36Sopenharmony_ci (void *) gp->tpc, 28062306a36Sopenharmony_ci (void *) gp->o7, 28162306a36Sopenharmony_ci (void *) gp->i7, 28262306a36Sopenharmony_ci (void *) gp->rpc); 28362306a36Sopenharmony_ci } else { 28462306a36Sopenharmony_ci printk(" TPC[%lx] O7[%lx] I7[%lx] RPC[%lx]\n", 28562306a36Sopenharmony_ci gp->tpc, gp->o7, gp->i7, gp->rpc); 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci touch_nmi_watchdog(); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot)); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci spin_unlock_irqrestore(&global_cpu_snapshot_lock, flags); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci#ifdef CONFIG_MAGIC_SYSRQ 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic void sysrq_handle_globreg(u8 key) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci trigger_all_cpu_backtrace(); 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic const struct sysrq_key_op sparc_globalreg_op = { 30462306a36Sopenharmony_ci .handler = sysrq_handle_globreg, 30562306a36Sopenharmony_ci .help_msg = "global-regs(y)", 30662306a36Sopenharmony_ci .action_msg = "Show Global CPU Regs", 30762306a36Sopenharmony_ci}; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic void __global_pmu_self(int this_cpu) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct global_pmu_snapshot *pp; 31262306a36Sopenharmony_ci int i, num; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (!pcr_ops) 31562306a36Sopenharmony_ci return; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci pp = &global_cpu_snapshot[this_cpu].pmu; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci num = 1; 32062306a36Sopenharmony_ci if (tlb_type == hypervisor && 32162306a36Sopenharmony_ci sun4v_chip_type >= SUN4V_CHIP_NIAGARA4) 32262306a36Sopenharmony_ci num = 4; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci for (i = 0; i < num; i++) { 32562306a36Sopenharmony_ci pp->pcr[i] = pcr_ops->read_pcr(i); 32662306a36Sopenharmony_ci pp->pic[i] = pcr_ops->read_pic(i); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic void __global_pmu_poll(struct global_pmu_snapshot *pp) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci int limit = 0; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci while (!pp->pcr[0] && ++limit < 100) { 33562306a36Sopenharmony_ci barrier(); 33662306a36Sopenharmony_ci udelay(1); 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic void pmu_snapshot_all_cpus(void) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci unsigned long flags; 34362306a36Sopenharmony_ci int this_cpu, cpu; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci spin_lock_irqsave(&global_cpu_snapshot_lock, flags); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot)); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci this_cpu = raw_smp_processor_id(); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci __global_pmu_self(this_cpu); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci smp_fetch_global_pmu(); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci for_each_online_cpu(cpu) { 35662306a36Sopenharmony_ci struct global_pmu_snapshot *pp = &global_cpu_snapshot[cpu].pmu; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci __global_pmu_poll(pp); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci printk("%c CPU[%3d]: PCR[%08lx:%08lx:%08lx:%08lx] PIC[%08lx:%08lx:%08lx:%08lx]\n", 36162306a36Sopenharmony_ci (cpu == this_cpu ? '*' : ' '), cpu, 36262306a36Sopenharmony_ci pp->pcr[0], pp->pcr[1], pp->pcr[2], pp->pcr[3], 36362306a36Sopenharmony_ci pp->pic[0], pp->pic[1], pp->pic[2], pp->pic[3]); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci touch_nmi_watchdog(); 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot)); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci spin_unlock_irqrestore(&global_cpu_snapshot_lock, flags); 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic void sysrq_handle_globpmu(u8 key) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci pmu_snapshot_all_cpus(); 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic const struct sysrq_key_op sparc_globalpmu_op = { 37962306a36Sopenharmony_ci .handler = sysrq_handle_globpmu, 38062306a36Sopenharmony_ci .help_msg = "global-pmu(x)", 38162306a36Sopenharmony_ci .action_msg = "Show Global PMU Regs", 38262306a36Sopenharmony_ci}; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic int __init sparc_sysrq_init(void) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci int ret = register_sysrq_key('y', &sparc_globalreg_op); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (!ret) 38962306a36Sopenharmony_ci ret = register_sysrq_key('x', &sparc_globalpmu_op); 39062306a36Sopenharmony_ci return ret; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cicore_initcall(sparc_sysrq_init); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci#endif 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci/* Free current thread data structures etc.. */ 39862306a36Sopenharmony_civoid exit_thread(struct task_struct *tsk) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci struct thread_info *t = task_thread_info(tsk); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (t->utraps) { 40362306a36Sopenharmony_ci if (t->utraps[0] < 2) 40462306a36Sopenharmony_ci kfree (t->utraps); 40562306a36Sopenharmony_ci else 40662306a36Sopenharmony_ci t->utraps[0]--; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_civoid flush_thread(void) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct thread_info *t = current_thread_info(); 41362306a36Sopenharmony_ci struct mm_struct *mm; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci mm = t->task->mm; 41662306a36Sopenharmony_ci if (mm) 41762306a36Sopenharmony_ci tsb_context_switch(mm); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci set_thread_wsaved(0); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* Clear FPU register state. */ 42262306a36Sopenharmony_ci t->fpsaved[0] = 0; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci/* It's a bit more tricky when 64-bit tasks are involved... */ 42662306a36Sopenharmony_cistatic unsigned long clone_stackframe(unsigned long csp, unsigned long psp) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci bool stack_64bit = test_thread_64bit_stack(psp); 42962306a36Sopenharmony_ci unsigned long fp, distance, rval; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (stack_64bit) { 43262306a36Sopenharmony_ci csp += STACK_BIAS; 43362306a36Sopenharmony_ci psp += STACK_BIAS; 43462306a36Sopenharmony_ci __get_user(fp, &(((struct reg_window __user *)psp)->ins[6])); 43562306a36Sopenharmony_ci fp += STACK_BIAS; 43662306a36Sopenharmony_ci if (test_thread_flag(TIF_32BIT)) 43762306a36Sopenharmony_ci fp &= 0xffffffff; 43862306a36Sopenharmony_ci } else 43962306a36Sopenharmony_ci __get_user(fp, &(((struct reg_window32 __user *)psp)->ins[6])); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci /* Now align the stack as this is mandatory in the Sparc ABI 44262306a36Sopenharmony_ci * due to how register windows work. This hides the 44362306a36Sopenharmony_ci * restriction from thread libraries etc. 44462306a36Sopenharmony_ci */ 44562306a36Sopenharmony_ci csp &= ~15UL; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci distance = fp - psp; 44862306a36Sopenharmony_ci rval = (csp - distance); 44962306a36Sopenharmony_ci if (raw_copy_in_user((void __user *)rval, (void __user *)psp, distance)) 45062306a36Sopenharmony_ci rval = 0; 45162306a36Sopenharmony_ci else if (!stack_64bit) { 45262306a36Sopenharmony_ci if (put_user(((u32)csp), 45362306a36Sopenharmony_ci &(((struct reg_window32 __user *)rval)->ins[6]))) 45462306a36Sopenharmony_ci rval = 0; 45562306a36Sopenharmony_ci } else { 45662306a36Sopenharmony_ci if (put_user(((u64)csp - STACK_BIAS), 45762306a36Sopenharmony_ci &(((struct reg_window __user *)rval)->ins[6]))) 45862306a36Sopenharmony_ci rval = 0; 45962306a36Sopenharmony_ci else 46062306a36Sopenharmony_ci rval = rval - STACK_BIAS; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return rval; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci/* Standard stuff. */ 46762306a36Sopenharmony_cistatic inline void shift_window_buffer(int first_win, int last_win, 46862306a36Sopenharmony_ci struct thread_info *t) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci int i; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci for (i = first_win; i < last_win; i++) { 47362306a36Sopenharmony_ci t->rwbuf_stkptrs[i] = t->rwbuf_stkptrs[i+1]; 47462306a36Sopenharmony_ci memcpy(&t->reg_window[i], &t->reg_window[i+1], 47562306a36Sopenharmony_ci sizeof(struct reg_window)); 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_civoid synchronize_user_stack(void) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct thread_info *t = current_thread_info(); 48262306a36Sopenharmony_ci unsigned long window; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci flush_user_windows(); 48562306a36Sopenharmony_ci if ((window = get_thread_wsaved()) != 0) { 48662306a36Sopenharmony_ci window -= 1; 48762306a36Sopenharmony_ci do { 48862306a36Sopenharmony_ci struct reg_window *rwin = &t->reg_window[window]; 48962306a36Sopenharmony_ci int winsize = sizeof(struct reg_window); 49062306a36Sopenharmony_ci unsigned long sp; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci sp = t->rwbuf_stkptrs[window]; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (test_thread_64bit_stack(sp)) 49562306a36Sopenharmony_ci sp += STACK_BIAS; 49662306a36Sopenharmony_ci else 49762306a36Sopenharmony_ci winsize = sizeof(struct reg_window32); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (!copy_to_user((char __user *)sp, rwin, winsize)) { 50062306a36Sopenharmony_ci shift_window_buffer(window, get_thread_wsaved() - 1, t); 50162306a36Sopenharmony_ci set_thread_wsaved(get_thread_wsaved() - 1); 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci } while (window--); 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic void stack_unaligned(unsigned long sp) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *) sp); 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cistatic const char uwfault32[] = KERN_INFO \ 51362306a36Sopenharmony_ci "%s[%d]: bad register window fault: SP %08lx (orig_sp %08lx) TPC %08lx O7 %08lx\n"; 51462306a36Sopenharmony_cistatic const char uwfault64[] = KERN_INFO \ 51562306a36Sopenharmony_ci "%s[%d]: bad register window fault: SP %016lx (orig_sp %016lx) TPC %08lx O7 %016lx\n"; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_civoid fault_in_user_windows(struct pt_regs *regs) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci struct thread_info *t = current_thread_info(); 52062306a36Sopenharmony_ci unsigned long window; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci flush_user_windows(); 52362306a36Sopenharmony_ci window = get_thread_wsaved(); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (likely(window != 0)) { 52662306a36Sopenharmony_ci window -= 1; 52762306a36Sopenharmony_ci do { 52862306a36Sopenharmony_ci struct reg_window *rwin = &t->reg_window[window]; 52962306a36Sopenharmony_ci int winsize = sizeof(struct reg_window); 53062306a36Sopenharmony_ci unsigned long sp, orig_sp; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci orig_sp = sp = t->rwbuf_stkptrs[window]; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (test_thread_64bit_stack(sp)) 53562306a36Sopenharmony_ci sp += STACK_BIAS; 53662306a36Sopenharmony_ci else 53762306a36Sopenharmony_ci winsize = sizeof(struct reg_window32); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (unlikely(sp & 0x7UL)) 54062306a36Sopenharmony_ci stack_unaligned(sp); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (unlikely(copy_to_user((char __user *)sp, 54362306a36Sopenharmony_ci rwin, winsize))) { 54462306a36Sopenharmony_ci if (show_unhandled_signals) 54562306a36Sopenharmony_ci printk_ratelimited(is_compat_task() ? 54662306a36Sopenharmony_ci uwfault32 : uwfault64, 54762306a36Sopenharmony_ci current->comm, current->pid, 54862306a36Sopenharmony_ci sp, orig_sp, 54962306a36Sopenharmony_ci regs->tpc, 55062306a36Sopenharmony_ci regs->u_regs[UREG_I7]); 55162306a36Sopenharmony_ci goto barf; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci } while (window--); 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci set_thread_wsaved(0); 55662306a36Sopenharmony_ci return; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cibarf: 55962306a36Sopenharmony_ci set_thread_wsaved(window + 1); 56062306a36Sopenharmony_ci force_sig(SIGSEGV); 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci/* Copy a Sparc thread. The fork() return value conventions 56462306a36Sopenharmony_ci * under SunOS are nothing short of bletcherous: 56562306a36Sopenharmony_ci * Parent --> %o0 == childs pid, %o1 == 0 56662306a36Sopenharmony_ci * Child --> %o0 == parents pid, %o1 == 1 56762306a36Sopenharmony_ci */ 56862306a36Sopenharmony_ciint copy_thread(struct task_struct *p, const struct kernel_clone_args *args) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci unsigned long clone_flags = args->flags; 57162306a36Sopenharmony_ci unsigned long sp = args->stack; 57262306a36Sopenharmony_ci unsigned long tls = args->tls; 57362306a36Sopenharmony_ci struct thread_info *t = task_thread_info(p); 57462306a36Sopenharmony_ci struct pt_regs *regs = current_pt_regs(); 57562306a36Sopenharmony_ci struct sparc_stackf *parent_sf; 57662306a36Sopenharmony_ci unsigned long child_stack_sz; 57762306a36Sopenharmony_ci char *child_trap_frame; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* Calculate offset to stack_frame & pt_regs */ 58062306a36Sopenharmony_ci child_stack_sz = (STACKFRAME_SZ + TRACEREG_SZ); 58162306a36Sopenharmony_ci child_trap_frame = (task_stack_page(p) + 58262306a36Sopenharmony_ci (THREAD_SIZE - child_stack_sz)); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci t->new_child = 1; 58562306a36Sopenharmony_ci t->ksp = ((unsigned long) child_trap_frame) - STACK_BIAS; 58662306a36Sopenharmony_ci t->kregs = (struct pt_regs *) (child_trap_frame + 58762306a36Sopenharmony_ci sizeof(struct sparc_stackf)); 58862306a36Sopenharmony_ci t->fpsaved[0] = 0; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (unlikely(args->fn)) { 59162306a36Sopenharmony_ci memset(child_trap_frame, 0, child_stack_sz); 59262306a36Sopenharmony_ci __thread_flag_byte_ptr(t)[TI_FLAG_BYTE_CWP] = 59362306a36Sopenharmony_ci (current_pt_regs()->tstate + 1) & TSTATE_CWP; 59462306a36Sopenharmony_ci t->kregs->u_regs[UREG_G1] = (unsigned long) args->fn; 59562306a36Sopenharmony_ci t->kregs->u_regs[UREG_G2] = (unsigned long) args->fn_arg; 59662306a36Sopenharmony_ci return 0; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci parent_sf = ((struct sparc_stackf *) regs) - 1; 60062306a36Sopenharmony_ci memcpy(child_trap_frame, parent_sf, child_stack_sz); 60162306a36Sopenharmony_ci if (t->flags & _TIF_32BIT) { 60262306a36Sopenharmony_ci sp &= 0x00000000ffffffffUL; 60362306a36Sopenharmony_ci regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci t->kregs->u_regs[UREG_FP] = sp; 60662306a36Sopenharmony_ci __thread_flag_byte_ptr(t)[TI_FLAG_BYTE_CWP] = 60762306a36Sopenharmony_ci (regs->tstate + 1) & TSTATE_CWP; 60862306a36Sopenharmony_ci if (sp != regs->u_regs[UREG_FP]) { 60962306a36Sopenharmony_ci unsigned long csp; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci csp = clone_stackframe(sp, regs->u_regs[UREG_FP]); 61262306a36Sopenharmony_ci if (!csp) 61362306a36Sopenharmony_ci return -EFAULT; 61462306a36Sopenharmony_ci t->kregs->u_regs[UREG_FP] = csp; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci if (t->utraps) 61762306a36Sopenharmony_ci t->utraps[0]++; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci /* Set the return value for the child. */ 62062306a36Sopenharmony_ci t->kregs->u_regs[UREG_I0] = current->pid; 62162306a36Sopenharmony_ci t->kregs->u_regs[UREG_I1] = 1; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci /* Set the second return value for the parent. */ 62462306a36Sopenharmony_ci regs->u_regs[UREG_I1] = 0; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (clone_flags & CLONE_SETTLS) 62762306a36Sopenharmony_ci t->kregs->u_regs[UREG_G7] = tls; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci return 0; 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci/* TIF_MCDPER in thread info flags for current task is updated lazily upon 63362306a36Sopenharmony_ci * a context switch. Update this flag in current task's thread flags 63462306a36Sopenharmony_ci * before dup so the dup'd task will inherit the current TIF_MCDPER flag. 63562306a36Sopenharmony_ci */ 63662306a36Sopenharmony_ciint arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci if (adi_capable()) { 63962306a36Sopenharmony_ci register unsigned long tmp_mcdper; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci __asm__ __volatile__( 64262306a36Sopenharmony_ci ".word 0x83438000\n\t" /* rd %mcdper, %g1 */ 64362306a36Sopenharmony_ci "mov %%g1, %0\n\t" 64462306a36Sopenharmony_ci : "=r" (tmp_mcdper) 64562306a36Sopenharmony_ci : 64662306a36Sopenharmony_ci : "g1"); 64762306a36Sopenharmony_ci if (tmp_mcdper) 64862306a36Sopenharmony_ci set_thread_flag(TIF_MCDPER); 64962306a36Sopenharmony_ci else 65062306a36Sopenharmony_ci clear_thread_flag(TIF_MCDPER); 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci *dst = *src; 65462306a36Sopenharmony_ci return 0; 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ciunsigned long __get_wchan(struct task_struct *task) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci unsigned long pc, fp, bias = 0; 66062306a36Sopenharmony_ci struct thread_info *tp; 66162306a36Sopenharmony_ci struct reg_window *rw; 66262306a36Sopenharmony_ci unsigned long ret = 0; 66362306a36Sopenharmony_ci int count = 0; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci tp = task_thread_info(task); 66662306a36Sopenharmony_ci bias = STACK_BIAS; 66762306a36Sopenharmony_ci fp = task_thread_info(task)->ksp + bias; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci do { 67062306a36Sopenharmony_ci if (!kstack_valid(tp, fp)) 67162306a36Sopenharmony_ci break; 67262306a36Sopenharmony_ci rw = (struct reg_window *) fp; 67362306a36Sopenharmony_ci pc = rw->ins[7]; 67462306a36Sopenharmony_ci if (!in_sched_functions(pc)) { 67562306a36Sopenharmony_ci ret = pc; 67662306a36Sopenharmony_ci goto out; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci fp = rw->ins[6] + bias; 67962306a36Sopenharmony_ci } while (++count < 16); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ciout: 68262306a36Sopenharmony_ci return ret; 68362306a36Sopenharmony_ci} 684