xref: /openbmc/linux/arch/alpha/kernel/process.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  linux/arch/alpha/kernel/process.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  Copyright (C) 1995  Linus Torvalds
61da177e4SLinus Torvalds  */
71da177e4SLinus Torvalds 
81da177e4SLinus Torvalds /*
91da177e4SLinus Torvalds  * This file handles the architecture-dependent parts of process handling.
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121e8a0be5SJosh Poimboeuf #include <linux/cpu.h>
131da177e4SLinus Torvalds #include <linux/errno.h>
141da177e4SLinus Torvalds #include <linux/module.h>
151da177e4SLinus Torvalds #include <linux/sched.h>
16b17b0153SIngo Molnar #include <linux/sched/debug.h>
1729930025SIngo Molnar #include <linux/sched/task.h>
1868db0cf1SIngo Molnar #include <linux/sched/task_stack.h>
191da177e4SLinus Torvalds #include <linux/kernel.h>
201da177e4SLinus Torvalds #include <linux/mm.h>
211da177e4SLinus Torvalds #include <linux/smp.h>
221da177e4SLinus Torvalds #include <linux/stddef.h>
231da177e4SLinus Torvalds #include <linux/unistd.h>
241da177e4SLinus Torvalds #include <linux/ptrace.h>
251da177e4SLinus Torvalds #include <linux/user.h>
261da177e4SLinus Torvalds #include <linux/time.h>
271da177e4SLinus Torvalds #include <linux/major.h>
281da177e4SLinus Torvalds #include <linux/stat.h>
29a8f340e3SJon Smirl #include <linux/vt.h>
301da177e4SLinus Torvalds #include <linux/mman.h>
311da177e4SLinus Torvalds #include <linux/elfcore.h>
321da177e4SLinus Torvalds #include <linux/reboot.h>
331da177e4SLinus Torvalds #include <linux/tty.h>
341da177e4SLinus Torvalds #include <linux/console.h>
355a0e3ad6STejun Heo #include <linux/slab.h>
364c94cadaSFrederic Weisbecker #include <linux/rcupdate.h>
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds #include <asm/reg.h>
397c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
401da177e4SLinus Torvalds #include <asm/io.h>
411da177e4SLinus Torvalds #include <asm/hwrpb.h>
421da177e4SLinus Torvalds #include <asm/fpu.h>
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds #include "proto.h"
451da177e4SLinus Torvalds #include "pci_impl.h"
461da177e4SLinus Torvalds 
475e38291dSEric W. Biederman /*
485e38291dSEric W. Biederman  * Power off function, if any
495e38291dSEric W. Biederman  */
505e38291dSEric W. Biederman void (*pm_power_off)(void) = machine_power_off;
5189eb1693SAl Viro EXPORT_SYMBOL(pm_power_off);
525e38291dSEric W. Biederman 
537f3bbb82SRichard Henderson #ifdef CONFIG_ALPHA_WTINT
547f3bbb82SRichard Henderson /*
557f3bbb82SRichard Henderson  * Sleep the CPU.
567f3bbb82SRichard Henderson  * EV6, LCA45 and QEMU know how to power down, skipping N timer interrupts.
577f3bbb82SRichard Henderson  */
arch_cpu_idle(void)587f3bbb82SRichard Henderson void arch_cpu_idle(void)
597f3bbb82SRichard Henderson {
607f3bbb82SRichard Henderson 	wtint(0);
617f3bbb82SRichard Henderson }
627f3bbb82SRichard Henderson 
arch_cpu_idle_dead(void)63*071c44e4SJosh Poimboeuf void __noreturn arch_cpu_idle_dead(void)
647f3bbb82SRichard Henderson {
657f3bbb82SRichard Henderson 	wtint(INT_MAX);
66550db679SJosh Poimboeuf 	BUG();
677f3bbb82SRichard Henderson }
687f3bbb82SRichard Henderson #endif /* ALPHA_WTINT */
697f3bbb82SRichard Henderson 
701da177e4SLinus Torvalds struct halt_info {
711da177e4SLinus Torvalds 	int mode;
721da177e4SLinus Torvalds 	char *restart_cmd;
731da177e4SLinus Torvalds };
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds static void
common_shutdown_1(void * generic_ptr)761da177e4SLinus Torvalds common_shutdown_1(void *generic_ptr)
771da177e4SLinus Torvalds {
7889b25c0eSLi zeming 	struct halt_info *how = generic_ptr;
791da177e4SLinus Torvalds 	struct percpu_struct *cpup;
801da177e4SLinus Torvalds 	unsigned long *pflags, flags;
811da177e4SLinus Torvalds 	int cpuid = smp_processor_id();
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds 	/* No point in taking interrupts anymore. */
841da177e4SLinus Torvalds 	local_irq_disable();
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds 	cpup = (struct percpu_struct *)
871da177e4SLinus Torvalds 			((unsigned long)hwrpb + hwrpb->processor_offset
881da177e4SLinus Torvalds 			 + hwrpb->processor_size * cpuid);
891da177e4SLinus Torvalds 	pflags = &cpup->flags;
901da177e4SLinus Torvalds 	flags = *pflags;
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds 	/* Clear reason to "default"; clear "bootstrap in progress". */
931da177e4SLinus Torvalds 	flags &= ~0x00ff0001UL;
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds #ifdef CONFIG_SMP
961da177e4SLinus Torvalds 	/* Secondaries halt here. */
971da177e4SLinus Torvalds 	if (cpuid != boot_cpuid) {
981da177e4SLinus Torvalds 		flags |= 0x00040000UL; /* "remain halted" */
991da177e4SLinus Torvalds 		*pflags = flags;
1001371be0fSRusty Russell 		set_cpu_present(cpuid, false);
1011371be0fSRusty Russell 		set_cpu_possible(cpuid, false);
1021da177e4SLinus Torvalds 		halt();
1031da177e4SLinus Torvalds 	}
1041da177e4SLinus Torvalds #endif
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds 	if (how->mode == LINUX_REBOOT_CMD_RESTART) {
1071da177e4SLinus Torvalds 		if (!how->restart_cmd) {
1081da177e4SLinus Torvalds 			flags |= 0x00020000UL; /* "cold bootstrap" */
1091da177e4SLinus Torvalds 		} else {
1101da177e4SLinus Torvalds 			/* For SRM, we could probably set environment
1111da177e4SLinus Torvalds 			   variables to get this to work.  We'd have to
1121da177e4SLinus Torvalds 			   delay this until after srm_paging_stop unless
1131da177e4SLinus Torvalds 			   we ever got srm_fixup working.
1141da177e4SLinus Torvalds 
1151da177e4SLinus Torvalds 			   At the moment, SRM will use the last boot device,
1161da177e4SLinus Torvalds 			   but the file and flags will be the defaults, when
1171da177e4SLinus Torvalds 			   doing a "warm" bootstrap.  */
1181da177e4SLinus Torvalds 			flags |= 0x00030000UL; /* "warm bootstrap" */
1191da177e4SLinus Torvalds 		}
1201da177e4SLinus Torvalds 	} else {
1211da177e4SLinus Torvalds 		flags |= 0x00040000UL; /* "remain halted" */
1221da177e4SLinus Torvalds 	}
1231da177e4SLinus Torvalds 	*pflags = flags;
1241da177e4SLinus Torvalds 
1251da177e4SLinus Torvalds #ifdef CONFIG_SMP
1261da177e4SLinus Torvalds 	/* Wait for the secondaries to halt. */
1271371be0fSRusty Russell 	set_cpu_present(boot_cpuid, false);
1281371be0fSRusty Russell 	set_cpu_possible(boot_cpuid, false);
12971c1a517SYury Norov 	while (!cpumask_empty(cpu_present_mask))
1301da177e4SLinus Torvalds 		barrier();
1311da177e4SLinus Torvalds #endif
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds 	/* If booted from SRM, reset some of the original environment. */
1341da177e4SLinus Torvalds 	if (alpha_using_srm) {
1351da177e4SLinus Torvalds #ifdef CONFIG_DUMMY_CONSOLE
1364b3c86a7SIvan Kokshaysky 		/* If we've gotten here after SysRq-b, leave interrupt
1374b3c86a7SIvan Kokshaysky 		   context before taking over the console. */
138290ec1d5SChangbin Du 		if (in_hardirq())
1394b3c86a7SIvan Kokshaysky 			irq_exit();
1401da177e4SLinus Torvalds 		/* This has the effect of resetting the VGA video origin.  */
141155957f5SWang YanQing 		console_lock();
142155957f5SWang YanQing 		do_take_over_console(&dummy_con, 0, MAX_NR_CONSOLES-1, 1);
143155957f5SWang YanQing 		console_unlock();
1441da177e4SLinus Torvalds #endif
1451da177e4SLinus Torvalds 		pci_restore_srm_config();
1461da177e4SLinus Torvalds 		set_hae(srm_hae);
1471da177e4SLinus Torvalds 	}
1481da177e4SLinus Torvalds 
1491da177e4SLinus Torvalds 	if (alpha_mv.kill_arch)
1501da177e4SLinus Torvalds 		alpha_mv.kill_arch(how->mode);
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds 	if (! alpha_using_srm && how->mode != LINUX_REBOOT_CMD_RESTART) {
1531da177e4SLinus Torvalds 		/* Unfortunately, since MILO doesn't currently understand
1541da177e4SLinus Torvalds 		   the hwrpb bits above, we can't reliably halt the
1551da177e4SLinus Torvalds 		   processor and keep it halted.  So just loop.  */
1561da177e4SLinus Torvalds 		return;
1571da177e4SLinus Torvalds 	}
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 	if (alpha_using_srm)
1601da177e4SLinus Torvalds 		srm_paging_stop();
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds 	halt();
1631da177e4SLinus Torvalds }
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds static void
common_shutdown(int mode,char * restart_cmd)1661da177e4SLinus Torvalds common_shutdown(int mode, char *restart_cmd)
1671da177e4SLinus Torvalds {
1681da177e4SLinus Torvalds 	struct halt_info args;
1691da177e4SLinus Torvalds 	args.mode = mode;
1701da177e4SLinus Torvalds 	args.restart_cmd = restart_cmd;
17115c8b6c1SJens Axboe 	on_each_cpu(common_shutdown_1, &args, 0);
1721da177e4SLinus Torvalds }
1731da177e4SLinus Torvalds 
1741da177e4SLinus Torvalds void
machine_restart(char * restart_cmd)1751da177e4SLinus Torvalds machine_restart(char *restart_cmd)
1761da177e4SLinus Torvalds {
1771da177e4SLinus Torvalds 	common_shutdown(LINUX_REBOOT_CMD_RESTART, restart_cmd);
1781da177e4SLinus Torvalds }
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds 
1811da177e4SLinus Torvalds void
machine_halt(void)1821da177e4SLinus Torvalds machine_halt(void)
1831da177e4SLinus Torvalds {
1841da177e4SLinus Torvalds 	common_shutdown(LINUX_REBOOT_CMD_HALT, NULL);
1851da177e4SLinus Torvalds }
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds 
1881da177e4SLinus Torvalds void
machine_power_off(void)1891da177e4SLinus Torvalds machine_power_off(void)
1901da177e4SLinus Torvalds {
1911da177e4SLinus Torvalds 	common_shutdown(LINUX_REBOOT_CMD_POWER_OFF, NULL);
1921da177e4SLinus Torvalds }
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds /* Used by sysrq-p, among others.  I don't believe r9-r15 are ever
1961da177e4SLinus Torvalds    saved in the context it's used.  */
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds void
show_regs(struct pt_regs * regs)1991da177e4SLinus Torvalds show_regs(struct pt_regs *regs)
2001da177e4SLinus Torvalds {
201a43cb95dSTejun Heo 	show_regs_print_info(KERN_DEFAULT);
2021da177e4SLinus Torvalds 	dik_show_regs(regs, NULL);
2031da177e4SLinus Torvalds }
2041da177e4SLinus Torvalds 
2051da177e4SLinus Torvalds /*
2061da177e4SLinus Torvalds  * Re-start a thread when doing execve()
2071da177e4SLinus Torvalds  */
2081da177e4SLinus Torvalds void
start_thread(struct pt_regs * regs,unsigned long pc,unsigned long sp)2091da177e4SLinus Torvalds start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp)
2101da177e4SLinus Torvalds {
2111da177e4SLinus Torvalds 	regs->pc = pc;
2121da177e4SLinus Torvalds 	regs->ps = 8;
2131da177e4SLinus Torvalds 	wrusp(sp);
2141da177e4SLinus Torvalds }
215cff52dafSAl Viro EXPORT_SYMBOL(start_thread);
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds void
flush_thread(void)2181da177e4SLinus Torvalds flush_thread(void)
2191da177e4SLinus Torvalds {
2201da177e4SLinus Torvalds 	/* Arrange for each exec'ed process to start off with a clean slate
2211da177e4SLinus Torvalds 	   with respect to the FPU.  This is all exceptions disabled.  */
2221da177e4SLinus Torvalds 	current_thread_info()->ieee_state = 0;
2231da177e4SLinus Torvalds 	wrfpcr(FPCR_DYN_NORMAL | ieee_swcr_to_fpcr(0));
2241da177e4SLinus Torvalds 
2251da177e4SLinus Torvalds 	/* Clean slate for TLS.  */
2261da177e4SLinus Torvalds 	current_thread_info()->pcb.unique = 0;
2271da177e4SLinus Torvalds }
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds /*
2309f7b2d1fSAlex Dowad  * Copy architecture-specific thread state
2311da177e4SLinus Torvalds  */
copy_thread(struct task_struct * p,const struct kernel_clone_args * args)232c5febea0SEric W. Biederman int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
2331da177e4SLinus Torvalds {
234c5febea0SEric W. Biederman 	unsigned long clone_flags = args->flags;
235c5febea0SEric W. Biederman 	unsigned long usp = args->stack;
236c5febea0SEric W. Biederman 	unsigned long tls = args->tls;
2371da177e4SLinus Torvalds 	extern void ret_from_fork(void);
238cba1ec7eSAl Viro 	extern void ret_from_kernel_thread(void);
2391da177e4SLinus Torvalds 
24027f45130SAl Viro 	struct thread_info *childti = task_thread_info(p);
241cba1ec7eSAl Viro 	struct pt_regs *childregs = task_pt_regs(p);
24225906730SAl Viro 	struct pt_regs *regs = current_pt_regs();
2431da177e4SLinus Torvalds 	struct switch_stack *childstack, *stack;
2441da177e4SLinus Torvalds 
245cba1ec7eSAl Viro 	childstack = ((struct switch_stack *) childregs) - 1;
24625906730SAl Viro 	childti->pcb.ksp = (unsigned long) childstack;
24725906730SAl Viro 	childti->pcb.flags = 1;	/* set FEN, clear everything else */
24805096666SAl Viro 	childti->status |= TS_SAVED_FP | TS_RESTORE_FP;
24925906730SAl Viro 
2505bd2e97cSEric W. Biederman 	if (unlikely(args->fn)) {
251cba1ec7eSAl Viro 		/* kernel thread */
252cba1ec7eSAl Viro 		memset(childstack, 0,
253cba1ec7eSAl Viro 			sizeof(struct switch_stack) + sizeof(struct pt_regs));
254cba1ec7eSAl Viro 		childstack->r26 = (unsigned long) ret_from_kernel_thread;
2555bd2e97cSEric W. Biederman 		childstack->r9 = (unsigned long) args->fn;
2565bd2e97cSEric W. Biederman 		childstack->r10 = (unsigned long) args->fn_arg;
257f0443da1SZheng Yongjun 		childregs->hae = alpha_mv.hae_cache;
25805096666SAl Viro 		memset(childti->fp, '\0', sizeof(childti->fp));
259cba1ec7eSAl Viro 		childti->pcb.usp = 0;
260cba1ec7eSAl Viro 		return 0;
261cba1ec7eSAl Viro 	}
26225906730SAl Viro 	/* Note: if CLONE_SETTLS is not set, then we must inherit the
26325906730SAl Viro 	   value from the parent, which will have been set by the block
26425906730SAl Viro 	   copy in dup_task_struct.  This is non-intuitive, but is
26525906730SAl Viro 	   required for proper operation in the case of a threaded
26625906730SAl Viro 	   application calling fork.  */
26725906730SAl Viro 	if (clone_flags & CLONE_SETTLS)
2680fdfc53fSChristian Brauner 		childti->pcb.unique = tls;
26921ffcedaSMikulas Patocka 	else
27021ffcedaSMikulas Patocka 		regs->r20 = 0;	/* OSF/1 has some strange fork() semantics.  */
27125906730SAl Viro 	childti->pcb.usp = usp ?: rdusp();
2721da177e4SLinus Torvalds 	*childregs = *regs;
2731da177e4SLinus Torvalds 	childregs->r0 = 0;
2741da177e4SLinus Torvalds 	childregs->r19 = 0;
2751da177e4SLinus Torvalds 	childregs->r20 = 1;	/* OSF/1 has some strange fork() semantics.  */
2761da177e4SLinus Torvalds 	stack = ((struct switch_stack *) regs) - 1;
2771da177e4SLinus Torvalds 	*childstack = *stack;
2781da177e4SLinus Torvalds 	childstack->r26 = (unsigned long) ret_from_fork;
2791da177e4SLinus Torvalds 	return 0;
2801da177e4SLinus Torvalds }
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds /*
2831da177e4SLinus Torvalds  * Fill in the user structure for a ELF core dump.
2841da177e4SLinus Torvalds  */
2851da177e4SLinus Torvalds void
dump_elf_thread(elf_greg_t * dest,struct pt_regs * pt,struct thread_info * ti)2861da177e4SLinus Torvalds dump_elf_thread(elf_greg_t *dest, struct pt_regs *pt, struct thread_info *ti)
2871da177e4SLinus Torvalds {
2881da177e4SLinus Torvalds 	/* switch stack follows right below pt_regs: */
2891da177e4SLinus Torvalds 	struct switch_stack * sw = ((struct switch_stack *) pt) - 1;
2901da177e4SLinus Torvalds 
2911da177e4SLinus Torvalds 	dest[ 0] = pt->r0;
2921da177e4SLinus Torvalds 	dest[ 1] = pt->r1;
2931da177e4SLinus Torvalds 	dest[ 2] = pt->r2;
2941da177e4SLinus Torvalds 	dest[ 3] = pt->r3;
2951da177e4SLinus Torvalds 	dest[ 4] = pt->r4;
2961da177e4SLinus Torvalds 	dest[ 5] = pt->r5;
2971da177e4SLinus Torvalds 	dest[ 6] = pt->r6;
2981da177e4SLinus Torvalds 	dest[ 7] = pt->r7;
2991da177e4SLinus Torvalds 	dest[ 8] = pt->r8;
3001da177e4SLinus Torvalds 	dest[ 9] = sw->r9;
3011da177e4SLinus Torvalds 	dest[10] = sw->r10;
3021da177e4SLinus Torvalds 	dest[11] = sw->r11;
3031da177e4SLinus Torvalds 	dest[12] = sw->r12;
3041da177e4SLinus Torvalds 	dest[13] = sw->r13;
3051da177e4SLinus Torvalds 	dest[14] = sw->r14;
3061da177e4SLinus Torvalds 	dest[15] = sw->r15;
3071da177e4SLinus Torvalds 	dest[16] = pt->r16;
3081da177e4SLinus Torvalds 	dest[17] = pt->r17;
3091da177e4SLinus Torvalds 	dest[18] = pt->r18;
3101da177e4SLinus Torvalds 	dest[19] = pt->r19;
3111da177e4SLinus Torvalds 	dest[20] = pt->r20;
3121da177e4SLinus Torvalds 	dest[21] = pt->r21;
3131da177e4SLinus Torvalds 	dest[22] = pt->r22;
3141da177e4SLinus Torvalds 	dest[23] = pt->r23;
3151da177e4SLinus Torvalds 	dest[24] = pt->r24;
3161da177e4SLinus Torvalds 	dest[25] = pt->r25;
3171da177e4SLinus Torvalds 	dest[26] = pt->r26;
3181da177e4SLinus Torvalds 	dest[27] = pt->r27;
3191da177e4SLinus Torvalds 	dest[28] = pt->r28;
3201da177e4SLinus Torvalds 	dest[29] = pt->gp;
32132163f4bSAl Viro 	dest[30] = ti == current_thread_info() ? rdusp() : ti->pcb.usp;
3221da177e4SLinus Torvalds 	dest[31] = pt->pc;
3231da177e4SLinus Torvalds 
3241da177e4SLinus Torvalds 	/* Once upon a time this was the PS value.  Which is stupid
3251da177e4SLinus Torvalds 	   since that is always 8 for usermode.  Usurped for the more
3261da177e4SLinus Torvalds 	   useful value of the thread's UNIQUE field.  */
3271da177e4SLinus Torvalds 	dest[32] = ti->pcb.unique;
3281da177e4SLinus Torvalds }
329cff52dafSAl Viro EXPORT_SYMBOL(dump_elf_thread);
3301da177e4SLinus Torvalds 
3311da177e4SLinus Torvalds int
dump_elf_task(elf_greg_t * dest,struct task_struct * task)3321da177e4SLinus Torvalds dump_elf_task(elf_greg_t *dest, struct task_struct *task)
3331da177e4SLinus Torvalds {
334e52f4ca2Sakpm@osdl.org 	dump_elf_thread(dest, task_pt_regs(task), task_thread_info(task));
3351da177e4SLinus Torvalds 	return 1;
3361da177e4SLinus Torvalds }
337cff52dafSAl Viro EXPORT_SYMBOL(dump_elf_task);
3381da177e4SLinus Torvalds 
elf_core_copy_task_fpregs(struct task_struct * t,elf_fpregset_t * fpu)339bdbadfccSAl Viro int elf_core_copy_task_fpregs(struct task_struct *t, elf_fpregset_t *fpu)
3401da177e4SLinus Torvalds {
34105096666SAl Viro 	memcpy(fpu, task_thread_info(t)->fp, 32 * 8);
3421da177e4SLinus Torvalds 	return 1;
3431da177e4SLinus Torvalds }
3441da177e4SLinus Torvalds 
3451da177e4SLinus Torvalds /*
3461da177e4SLinus Torvalds  * Return saved PC of a blocked thread.  This assumes the frame
3471da177e4SLinus Torvalds  * pointer is the 6th saved long on the kernel stack and that the
3481da177e4SLinus Torvalds  * saved return address is the first long in the frame.  This all
3491da177e4SLinus Torvalds  * holds provided the thread blocked through a call to schedule() ($15
3501da177e4SLinus Torvalds  * is the frame pointer in schedule() and $15 is saved at offset 48 by
3511da177e4SLinus Torvalds  * entry.S:do_switch_stack).
3521da177e4SLinus Torvalds  *
3531da177e4SLinus Torvalds  * Under heavy swap load I've seen this lose in an ugly way.  So do
3541da177e4SLinus Torvalds  * some extra sanity checking on the ranges we expect these pointers
3551da177e4SLinus Torvalds  * to be in so that we can fail gracefully.  This is just for ps after
3561da177e4SLinus Torvalds  * all.  -- r~
3571da177e4SLinus Torvalds  */
3581da177e4SLinus Torvalds 
35916dc17eeSTobias Klauser static unsigned long
thread_saved_pc(struct task_struct * t)36036c8b586SIngo Molnar thread_saved_pc(struct task_struct *t)
3611da177e4SLinus Torvalds {
36227f45130SAl Viro 	unsigned long base = (unsigned long)task_stack_page(t);
36337bfbaf9SAl Viro 	unsigned long fp, sp = task_thread_info(t)->pcb.ksp;
3641da177e4SLinus Torvalds 
3651da177e4SLinus Torvalds 	if (sp > base && sp+6*8 < base + 16*1024) {
3661da177e4SLinus Torvalds 		fp = ((unsigned long*)sp)[6];
3671da177e4SLinus Torvalds 		if (fp > sp && fp < base + 16*1024)
3681da177e4SLinus Torvalds 			return *(unsigned long *)fp;
3691da177e4SLinus Torvalds 	}
3701da177e4SLinus Torvalds 
3711da177e4SLinus Torvalds 	return 0;
3721da177e4SLinus Torvalds }
3731da177e4SLinus Torvalds 
3741da177e4SLinus Torvalds unsigned long
__get_wchan(struct task_struct * p)37542a20f86SKees Cook __get_wchan(struct task_struct *p)
3761da177e4SLinus Torvalds {
3771da177e4SLinus Torvalds 	unsigned long schedule_frame;
3781da177e4SLinus Torvalds 	unsigned long pc;
37942a20f86SKees Cook 
3801da177e4SLinus Torvalds 	/*
3811da177e4SLinus Torvalds 	 * This one depends on the frame size of schedule().  Do a
3821da177e4SLinus Torvalds 	 * "disass schedule" in gdb to find the frame size.  Also, the
3831da177e4SLinus Torvalds 	 * code assumes that sleep_on() follows immediately after
3841da177e4SLinus Torvalds 	 * interruptible_sleep_on() and that add_timer() follows
3851da177e4SLinus Torvalds 	 * immediately after interruptible_sleep().  Ugly, isn't it?
3861da177e4SLinus Torvalds 	 * Maybe adding a wchan field to task_struct would be better,
3871da177e4SLinus Torvalds 	 * after all...
3881da177e4SLinus Torvalds 	 */
3891da177e4SLinus Torvalds 
3901da177e4SLinus Torvalds 	pc = thread_saved_pc(p);
3911da177e4SLinus Torvalds 	if (in_sched_functions(pc)) {
39237bfbaf9SAl Viro 		schedule_frame = ((unsigned long *)task_thread_info(p)->pcb.ksp)[6];
3931da177e4SLinus Torvalds 		return ((unsigned long *)schedule_frame)[12];
3941da177e4SLinus Torvalds 	}
3951da177e4SLinus Torvalds 	return pc;
3961da177e4SLinus Torvalds }
397