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