xref: /openbmc/linux/arch/arm/kernel/process.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  linux/arch/arm/kernel/process.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  Copyright (C) 1996-2000 Russell King - Converted to ARM.
61da177e4SLinus Torvalds  *  Original Copyright (C) 1995  Linus Torvalds
71da177e4SLinus Torvalds  */
8ecea4ab6SPaul Gortmaker #include <linux/export.h>
91da177e4SLinus Torvalds #include <linux/sched.h>
10b17b0153SIngo Molnar #include <linux/sched/debug.h>
1129930025SIngo Molnar #include <linux/sched/task.h>
1268db0cf1SIngo Molnar #include <linux/sched/task_stack.h>
131da177e4SLinus Torvalds #include <linux/kernel.h>
141da177e4SLinus Torvalds #include <linux/mm.h>
151da177e4SLinus Torvalds #include <linux/stddef.h>
161da177e4SLinus Torvalds #include <linux/unistd.h>
171da177e4SLinus Torvalds #include <linux/user.h>
181da177e4SLinus Torvalds #include <linux/interrupt.h>
191da177e4SLinus Torvalds #include <linux/init.h>
2084dff1a7SBen Dooks #include <linux/elfcore.h>
2174617fb6SRichard Purdie #include <linux/pm.h>
229e4559ddSKevin Hilman #include <linux/tick.h>
23154c772eSRussell King #include <linux/utsname.h>
2433fa9b13SRussell King #include <linux/uaccess.h>
25990cb8acSNicolas Pitre #include <linux/random.h>
26864232faSWill Deacon #include <linux/hw_breakpoint.h>
27fa8bbb13SBryan Wu #include <linux/leds.h>
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds #include <asm/processor.h>
30d6551e88SRussell King #include <asm/thread_notify.h>
312d7c11bfSCatalin Marinas #include <asm/stacktrace.h>
32779dd959SRussell King #include <asm/system_misc.h>
332ea83398SRussell King #include <asm/mach/time.h>
34a4780adeSAndré Hentschel #include <asm/tls.h>
35045ab94eSRussell King #include <asm/vdso.h>
361da177e4SLinus Torvalds 
3783dc1d99SBen Dooks (Codethink) #include "signal.h"
3883dc1d99SBen Dooks (Codethink) 
399c46929eSArd Biesheuvel #if defined(CONFIG_CURRENT_POINTER_IN_TPIDRURO) || defined(CONFIG_SMP)
4050596b75SArd Biesheuvel DEFINE_PER_CPU(struct task_struct *, __entry_task);
4150596b75SArd Biesheuvel #endif
4250596b75SArd Biesheuvel 
43189af465SArd Biesheuvel #if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_STACKPROTECTOR_PER_TASK)
44c743f380SNicolas Pitre #include <linux/stackprotector.h>
45c743f380SNicolas Pitre unsigned long __stack_chk_guard __read_mostly;
46c743f380SNicolas Pitre EXPORT_SYMBOL(__stack_chk_guard);
47c743f380SNicolas Pitre #endif
48c743f380SNicolas Pitre 
499c46929eSArd Biesheuvel #ifndef CONFIG_CURRENT_POINTER_IN_TPIDRURO
509c46929eSArd Biesheuvel asmlinkage struct task_struct *__current;
519c46929eSArd Biesheuvel EXPORT_SYMBOL(__current);
529c46929eSArd Biesheuvel #endif
539c46929eSArd Biesheuvel 
54e2e55fdeSUwe Kleine-König static const char *processor_modes[] __maybe_unused = {
55ae0a846eSRussell King   "USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" , "UK4_26" , "UK5_26" , "UK6_26" , "UK7_26" ,
56ae0a846eSRussell King   "UK8_26" , "UK9_26" , "UK10_26", "UK11_26", "UK12_26", "UK13_26", "UK14_26", "UK15_26",
57f3a04202SStephen Boyd   "USER_32", "FIQ_32" , "IRQ_32" , "SVC_32" , "UK4_32" , "UK5_32" , "MON_32" , "ABT_32" ,
58f3a04202SStephen Boyd   "UK8_32" , "UK9_32" , "HYP_32", "UND_32" , "UK12_32", "UK13_32", "UK14_32", "SYS_32"
59ae0a846eSRussell King };
60ae0a846eSRussell King 
61e2e55fdeSUwe Kleine-König static const char *isa_modes[] __maybe_unused = {
62909d6c6cSGeorge G. Davis   "ARM" , "Thumb" , "Jazelle", "ThumbEE"
63909d6c6cSGeorge G. Davis };
64909d6c6cSGeorge G. Davis 
651da177e4SLinus Torvalds /*
664fa20439SNicolas Pitre  * This is our default idle handler.
671da177e4SLinus Torvalds  */
684fa20439SNicolas Pitre 
694fa20439SNicolas Pitre void (*arm_pm_idle)(void);
704fa20439SNicolas Pitre 
71ad68cc7aSNicolas Pitre /*
72ad68cc7aSNicolas Pitre  * Called from the core idle loop.
73ad68cc7aSNicolas Pitre  */
74ad68cc7aSNicolas Pitre 
arch_cpu_idle(void)75ad68cc7aSNicolas Pitre void arch_cpu_idle(void)
761da177e4SLinus Torvalds {
774fa20439SNicolas Pitre 	if (arm_pm_idle)
784fa20439SNicolas Pitre 		arm_pm_idle();
794fa20439SNicolas Pitre 	else
80ae940913SNicolas Pitre 		cpu_do_idle();
811da177e4SLinus Torvalds }
829ccdac36SRussell King 
arch_cpu_idle_prepare(void)83f7b861b7SThomas Gleixner void arch_cpu_idle_prepare(void)
841da177e4SLinus Torvalds {
851da177e4SLinus Torvalds 	local_fiq_enable();
86f7b861b7SThomas Gleixner }
871da177e4SLinus Torvalds 
arch_cpu_idle_enter(void)88f7b861b7SThomas Gleixner void arch_cpu_idle_enter(void)
89f7b861b7SThomas Gleixner {
90fa8bbb13SBryan Wu 	ledtrig_cpu(CPU_LED_IDLE_START);
9111ed0ba1SWill Deacon #ifdef CONFIG_PL310_ERRATA_769419
9211ed0ba1SWill Deacon 	wmb();
9311ed0ba1SWill Deacon #endif
94f7b861b7SThomas Gleixner }
95f7b861b7SThomas Gleixner 
arch_cpu_idle_exit(void)96f7b861b7SThomas Gleixner void arch_cpu_idle_exit(void)
97f7b861b7SThomas Gleixner {
98f7b861b7SThomas Gleixner 	ledtrig_cpu(CPU_LED_IDLE_END);
99f7b861b7SThomas Gleixner }
100f7b861b7SThomas Gleixner 
__show_regs_alloc_free(struct pt_regs * regs)1015aa6b70eSManinder Singh void __show_regs_alloc_free(struct pt_regs *regs)
1025aa6b70eSManinder Singh {
1035aa6b70eSManinder Singh 	int i;
1045aa6b70eSManinder Singh 
1055aa6b70eSManinder Singh 	/* check for r0 - r12 only */
1065aa6b70eSManinder Singh 	for (i = 0; i < 13; i++) {
1075aa6b70eSManinder Singh 		pr_alert("Register r%d information:", i);
1085aa6b70eSManinder Singh 		mem_dump_obj((void *)regs->uregs[i]);
1095aa6b70eSManinder Singh 	}
1105aa6b70eSManinder Singh }
1115aa6b70eSManinder Singh 
__show_regs(struct pt_regs * regs)112652a12efSRussell King void __show_regs(struct pt_regs *regs)
1131da177e4SLinus Torvalds {
114154c772eSRussell King 	unsigned long flags;
115154c772eSRussell King 	char buf[64];
11677f1b959SRussell King #ifndef CONFIG_CPU_V7M
1178ac6f5d7SArnd Bergmann 	unsigned int domain;
11877f1b959SRussell King #ifdef CONFIG_CPU_SW_DOMAIN_PAN
11977f1b959SRussell King 	/*
12077f1b959SRussell King 	 * Get the domain register for the parent context. In user
12177f1b959SRussell King 	 * mode, we don't save the DACR, so lets use what it should
12277f1b959SRussell King 	 * be. For other modes, we place it after the pt_regs struct.
12377f1b959SRussell King 	 */
124e6978e4bSRussell King 	if (user_mode(regs)) {
12577f1b959SRussell King 		domain = DACR_UACCESS_ENABLE;
126e6978e4bSRussell King 	} else {
1275fa9da50SRussell King 		domain = to_svc_pt_regs(regs)->dacr;
128e6978e4bSRussell King 	}
12977f1b959SRussell King #else
13077f1b959SRussell King 	domain = get_domain();
13177f1b959SRussell King #endif
13277f1b959SRussell King #endif
1331da177e4SLinus Torvalds 
134a43cb95dSTejun Heo 	show_regs_print_info(KERN_DEFAULT);
135a43cb95dSTejun Heo 
1363ea70d7dSSergey Senozhatsky 	printk("PC is at %pS\n", (void *)instruction_pointer(regs));
1373ea70d7dSSergey Senozhatsky 	printk("LR is at %pS\n", (void *)regs->ARM_lr);
138801f19b9SJoe Perches 	printk("pc : [<%08lx>]    lr : [<%08lx>]    psr: %08lx\n",
139801f19b9SJoe Perches 	       regs->ARM_pc, regs->ARM_lr, regs->ARM_cpsr);
140801f19b9SJoe Perches 	printk("sp : %08lx  ip : %08lx  fp : %08lx\n",
141154c772eSRussell King 	       regs->ARM_sp, regs->ARM_ip, regs->ARM_fp);
1421da177e4SLinus Torvalds 	printk("r10: %08lx  r9 : %08lx  r8 : %08lx\n",
1431da177e4SLinus Torvalds 		regs->ARM_r10, regs->ARM_r9,
1441da177e4SLinus Torvalds 		regs->ARM_r8);
1451da177e4SLinus Torvalds 	printk("r7 : %08lx  r6 : %08lx  r5 : %08lx  r4 : %08lx\n",
1461da177e4SLinus Torvalds 		regs->ARM_r7, regs->ARM_r6,
1471da177e4SLinus Torvalds 		regs->ARM_r5, regs->ARM_r4);
1481da177e4SLinus Torvalds 	printk("r3 : %08lx  r2 : %08lx  r1 : %08lx  r0 : %08lx\n",
1491da177e4SLinus Torvalds 		regs->ARM_r3, regs->ARM_r2,
1501da177e4SLinus Torvalds 		regs->ARM_r1, regs->ARM_r0);
151154c772eSRussell King 
152154c772eSRussell King 	flags = regs->ARM_cpsr;
153154c772eSRussell King 	buf[0] = flags & PSR_N_BIT ? 'N' : 'n';
154154c772eSRussell King 	buf[1] = flags & PSR_Z_BIT ? 'Z' : 'z';
155154c772eSRussell King 	buf[2] = flags & PSR_C_BIT ? 'C' : 'c';
156154c772eSRussell King 	buf[3] = flags & PSR_V_BIT ? 'V' : 'v';
157154c772eSRussell King 	buf[4] = '\0';
158154c772eSRussell King 
159e2e55fdeSUwe Kleine-König #ifndef CONFIG_CPU_V7M
160a5e090acSRussell King 	{
161a5e090acSRussell King 		const char *segment;
162a5e090acSRussell King 
163a5e090acSRussell King 		if ((domain & domain_mask(DOMAIN_USER)) ==
164a5e090acSRussell King 		    domain_val(DOMAIN_USER, DOMAIN_NOACCESS))
165a5e090acSRussell King 			segment = "none";
166a5e090acSRussell King 		else
167a5e090acSRussell King 			segment = "user";
168a5e090acSRussell King 
169909d6c6cSGeorge G. Davis 		printk("Flags: %s  IRQs o%s  FIQs o%s  Mode %s  ISA %s  Segment %s\n",
170154c772eSRussell King 			buf, interrupts_enabled(regs) ? "n" : "ff",
1711da177e4SLinus Torvalds 			fast_interrupts_enabled(regs) ? "n" : "ff",
1721da177e4SLinus Torvalds 			processor_modes[processor_mode(regs)],
173a5e090acSRussell King 			isa_modes[isa_mode(regs)], segment);
174a5e090acSRussell King 	}
175e2e55fdeSUwe Kleine-König #else
176e2e55fdeSUwe Kleine-König 	printk("xPSR: %08lx\n", regs->ARM_cpsr);
177e2e55fdeSUwe Kleine-König #endif
178e2e55fdeSUwe Kleine-König 
179154c772eSRussell King #ifdef CONFIG_CPU_CP15
1801da177e4SLinus Torvalds 	{
181f12d0d7cSHyok S. Choi 		unsigned int ctrl;
182154c772eSRussell King 
183154c772eSRussell King 		buf[0] = '\0';
184f12d0d7cSHyok S. Choi #ifdef CONFIG_CPU_CP15_MMU
185f12d0d7cSHyok S. Choi 		{
18677f1b959SRussell King 			unsigned int transbase;
187154c772eSRussell King 			asm("mrc p15, 0, %0, c2, c0\n\t"
1881eef5d2fSRussell King 			    : "=r" (transbase));
189154c772eSRussell King 			snprintf(buf, sizeof(buf), "  Table: %08x  DAC: %08x",
19077f1b959SRussell King 				transbase, domain);
191f12d0d7cSHyok S. Choi 		}
192f12d0d7cSHyok S. Choi #endif
193154c772eSRussell King 		asm("mrc p15, 0, %0, c1, c0\n" : "=r" (ctrl));
194154c772eSRussell King 
195154c772eSRussell King 		printk("Control: %08x%s\n", ctrl, buf);
196154c772eSRussell King 	}
197f12d0d7cSHyok S. Choi #endif
1981da177e4SLinus Torvalds }
1991da177e4SLinus Torvalds 
show_regs(struct pt_regs * regs)200652a12efSRussell King void show_regs(struct pt_regs * regs)
201652a12efSRussell King {
202652a12efSRussell King 	__show_regs(regs);
20309cffecaSZhen Lei 	dump_backtrace(regs, NULL, KERN_DEFAULT);
204652a12efSRussell King }
205652a12efSRussell King 
206797245f5SRussell King ATOMIC_NOTIFIER_HEAD(thread_notify_head);
207797245f5SRussell King 
208797245f5SRussell King EXPORT_SYMBOL_GPL(thread_notify_head);
209797245f5SRussell King 
2101da177e4SLinus Torvalds /*
2111da177e4SLinus Torvalds  * Free current thread data structures etc..
2121da177e4SLinus Torvalds  */
exit_thread(struct task_struct * tsk)213e6464694SJiri Slaby void exit_thread(struct task_struct *tsk)
2141da177e4SLinus Torvalds {
215e6464694SJiri Slaby 	thread_notify(THREAD_NOTIFY_EXIT, task_thread_info(tsk));
2161da177e4SLinus Torvalds }
2171da177e4SLinus Torvalds 
flush_thread(void)2181da177e4SLinus Torvalds void flush_thread(void)
2191da177e4SLinus Torvalds {
2201da177e4SLinus Torvalds 	struct thread_info *thread = current_thread_info();
2211da177e4SLinus Torvalds 	struct task_struct *tsk = current;
2221da177e4SLinus Torvalds 
223864232faSWill Deacon 	flush_ptrace_hw_breakpoint(tsk);
224864232faSWill Deacon 
2251da177e4SLinus Torvalds 	memset(&tsk->thread.debug, 0, sizeof(struct debug_info));
226d6551e88SRussell King 	memset(&thread->fpstate, 0, sizeof(union fp_state));
227d6551e88SRussell King 
228fbfb872fSNathan Lynch 	flush_tls();
229fbfb872fSNathan Lynch 
230d6551e88SRussell King 	thread_notify(THREAD_NOTIFY_FLUSH, thread);
2311da177e4SLinus Torvalds }
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
2341da177e4SLinus Torvalds 
copy_thread(struct task_struct * p,const struct kernel_clone_args * args)235c5febea0SEric W. Biederman int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
2361da177e4SLinus Torvalds {
237c5febea0SEric W. Biederman 	unsigned long clone_flags = args->flags;
238c5febea0SEric W. Biederman 	unsigned long stack_start = args->stack;
239c5febea0SEric W. Biederman 	unsigned long tls = args->tls;
240815d5ec8SAl Viro 	struct thread_info *thread = task_thread_info(p);
241815d5ec8SAl Viro 	struct pt_regs *childregs = task_pt_regs(p);
2421da177e4SLinus Torvalds 
2439e14f828SAl Viro 	memset(&thread->cpu_context, 0, sizeof(struct cpu_context_save));
2449e14f828SAl Viro 
245af4cb25dSRussell King #ifdef CONFIG_CPU_USE_DOMAINS
2461eef5d2fSRussell King 	/*
2471eef5d2fSRussell King 	 * Copy the initial value of the domain access control register
2481eef5d2fSRussell King 	 * from the current thread: thread->addr_limit will have been
2491eef5d2fSRussell King 	 * copied from the current thread via setup_thread_stack() in
2501eef5d2fSRussell King 	 * kernel/fork.c
2511eef5d2fSRussell King 	 */
2521eef5d2fSRussell King 	thread->cpu_domain = get_domain();
253af4cb25dSRussell King #endif
2541eef5d2fSRussell King 
2555bd2e97cSEric W. Biederman 	if (likely(!args->fn)) {
25638a61b6bSAl Viro 		*childregs = *current_pt_regs();
2571da177e4SLinus Torvalds 		childregs->ARM_r0 = 0;
25838a61b6bSAl Viro 		if (stack_start)
2591da177e4SLinus Torvalds 			childregs->ARM_sp = stack_start;
2609e14f828SAl Viro 	} else {
2619fff2fa0SAl Viro 		memset(childregs, 0, sizeof(struct pt_regs));
2625bd2e97cSEric W. Biederman 		thread->cpu_context.r4 = (unsigned long)args->fn_arg;
2635bd2e97cSEric W. Biederman 		thread->cpu_context.r5 = (unsigned long)args->fn;
2649e14f828SAl Viro 		childregs->ARM_cpsr = SVC_MODE;
2659e14f828SAl Viro 	}
2669fff2fa0SAl Viro 	thread->cpu_context.pc = (unsigned long)ret_from_fork;
2679e14f828SAl Viro 	thread->cpu_context.sp = (unsigned long)childregs;
2681da177e4SLinus Torvalds 
269864232faSWill Deacon 	clear_ptrace_hw_breakpoint(p);
270864232faSWill Deacon 
2711da177e4SLinus Torvalds 	if (clone_flags & CLONE_SETTLS)
272167ee0b8SAmanieu d'Antras 		thread->tp_value[0] = tls;
273a4780adeSAndré Hentschel 	thread->tp_value[1] = get_tpuser();
2741da177e4SLinus Torvalds 
2752e82669aSCatalin Marinas 	thread_notify(THREAD_NOTIFY_COPY, thread);
2762e82669aSCatalin Marinas 
2771da177e4SLinus Torvalds 	return 0;
2781da177e4SLinus Torvalds }
2791da177e4SLinus Torvalds 
__get_wchan(struct task_struct * p)28042a20f86SKees Cook unsigned long __get_wchan(struct task_struct *p)
2811da177e4SLinus Torvalds {
2822d7c11bfSCatalin Marinas 	struct stackframe frame;
2831b15ec7aSKonstantin Khlebnikov 	unsigned long stack_page;
2841da177e4SLinus Torvalds 	int count = 0;
2851da177e4SLinus Torvalds 
2862d7c11bfSCatalin Marinas 	frame.fp = thread_saved_fp(p);
2872d7c11bfSCatalin Marinas 	frame.sp = thread_saved_sp(p);
2882d7c11bfSCatalin Marinas 	frame.lr = 0;			/* recovered from the stack */
2892d7c11bfSCatalin Marinas 	frame.pc = thread_saved_pc(p);
2901b15ec7aSKonstantin Khlebnikov 	stack_page = (unsigned long)task_stack_page(p);
2911da177e4SLinus Torvalds 	do {
2921b15ec7aSKonstantin Khlebnikov 		if (frame.sp < stack_page ||
2931b15ec7aSKonstantin Khlebnikov 		    frame.sp >= stack_page + THREAD_SIZE ||
2941b15ec7aSKonstantin Khlebnikov 		    unwind_frame(&frame) < 0)
2951da177e4SLinus Torvalds 			return 0;
2962d7c11bfSCatalin Marinas 		if (!in_sched_functions(frame.pc))
2972d7c11bfSCatalin Marinas 			return frame.pc;
2981da177e4SLinus Torvalds 	} while (count ++ < 16);
2991da177e4SLinus Torvalds 	return 0;
3001da177e4SLinus Torvalds }
301990cb8acSNicolas Pitre 
3026cde6d42SWill Deacon #ifdef CONFIG_MMU
303a5463cd3SRussell King #ifdef CONFIG_KUSER_HELPERS
304ec706dabSNicolas Pitre /*
305ec706dabSNicolas Pitre  * The vectors page is always readable from user space for the
30648be69a0SRussell King  * atomic helpers. Insert it into the gate_vma so that it is visible
30748be69a0SRussell King  * through ptrace and /proc/<pid>/mem.
308ec706dabSNicolas Pitre  */
309a670468fSAndrew Morton static struct vm_area_struct gate_vma;
310ec706dabSNicolas Pitre 
gate_vma_init(void)311f9d4861fSWill Deacon static int __init gate_vma_init(void)
312ec706dabSNicolas Pitre {
3132c4541e2SKirill A. Shutemov 	vma_init(&gate_vma, NULL);
314f9d4861fSWill Deacon 	gate_vma.vm_page_prot = PAGE_READONLY_EXEC;
315a670468fSAndrew Morton 	gate_vma.vm_start = 0xffff0000;
316a670468fSAndrew Morton 	gate_vma.vm_end	= 0xffff0000 + PAGE_SIZE;
317*1c71222eSSuren Baghdasaryan 	vm_flags_init(&gate_vma, VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYEXEC);
318f9d4861fSWill Deacon 	return 0;
319f9d4861fSWill Deacon }
320f9d4861fSWill Deacon arch_initcall(gate_vma_init);
321f9d4861fSWill Deacon 
get_gate_vma(struct mm_struct * mm)322f9d4861fSWill Deacon struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
323f9d4861fSWill Deacon {
324f9d4861fSWill Deacon 	return &gate_vma;
325f9d4861fSWill Deacon }
326f9d4861fSWill Deacon 
in_gate_area(struct mm_struct * mm,unsigned long addr)327f9d4861fSWill Deacon int in_gate_area(struct mm_struct *mm, unsigned long addr)
328f9d4861fSWill Deacon {
329f9d4861fSWill Deacon 	return (addr >= gate_vma.vm_start) && (addr < gate_vma.vm_end);
330f9d4861fSWill Deacon }
331f9d4861fSWill Deacon 
in_gate_area_no_mm(unsigned long addr)332f9d4861fSWill Deacon int in_gate_area_no_mm(unsigned long addr)
333f9d4861fSWill Deacon {
334f9d4861fSWill Deacon 	return in_gate_area(NULL, addr);
335ec706dabSNicolas Pitre }
3361d0bbf42SRussell King #define is_gate_vma(vma)	((vma) == &gate_vma)
337a5463cd3SRussell King #else
338a5463cd3SRussell King #define is_gate_vma(vma)	0
339a5463cd3SRussell King #endif
340ec706dabSNicolas Pitre 
arch_vma_name(struct vm_area_struct * vma)341ec706dabSNicolas Pitre const char *arch_vma_name(struct vm_area_struct *vma)
342ec706dabSNicolas Pitre {
34302e0409aSNathan Lynch 	return is_gate_vma(vma) ? "[vectors]" : NULL;
34448be69a0SRussell King }
34548be69a0SRussell King 
346389522b0SNathan Lynch /* If possible, provide a placement hint at a random offset from the
347ecf99a43SNathan Lynch  * stack for the sigpage and vdso pages.
348389522b0SNathan Lynch  */
sigpage_addr(const struct mm_struct * mm,unsigned int npages)349389522b0SNathan Lynch static unsigned long sigpage_addr(const struct mm_struct *mm,
350389522b0SNathan Lynch 				  unsigned int npages)
351389522b0SNathan Lynch {
352389522b0SNathan Lynch 	unsigned long offset;
353389522b0SNathan Lynch 	unsigned long first;
354389522b0SNathan Lynch 	unsigned long last;
355389522b0SNathan Lynch 	unsigned long addr;
356389522b0SNathan Lynch 	unsigned int slots;
357389522b0SNathan Lynch 
358389522b0SNathan Lynch 	first = PAGE_ALIGN(mm->start_stack);
359389522b0SNathan Lynch 
360389522b0SNathan Lynch 	last = TASK_SIZE - (npages << PAGE_SHIFT);
361389522b0SNathan Lynch 
362389522b0SNathan Lynch 	/* No room after stack? */
363389522b0SNathan Lynch 	if (first > last)
364389522b0SNathan Lynch 		return 0;
365389522b0SNathan Lynch 
366389522b0SNathan Lynch 	/* Just enough room? */
367389522b0SNathan Lynch 	if (first == last)
368389522b0SNathan Lynch 		return first;
369389522b0SNathan Lynch 
370389522b0SNathan Lynch 	slots = ((last - first) >> PAGE_SHIFT) + 1;
371389522b0SNathan Lynch 
3728032bf12SJason A. Donenfeld 	offset = get_random_u32_below(slots);
373389522b0SNathan Lynch 
374389522b0SNathan Lynch 	addr = first + (offset << PAGE_SHIFT);
375389522b0SNathan Lynch 
376389522b0SNathan Lynch 	return addr;
37748be69a0SRussell King }
37848be69a0SRussell King 
379e0d40756SRussell King static struct page *signal_page;
38048be69a0SRussell King extern struct page *get_signal_page(void);
38148be69a0SRussell King 
sigpage_mremap(const struct vm_special_mapping * sm,struct vm_area_struct * new_vma)382280e87e9SDmitry Safonov static int sigpage_mremap(const struct vm_special_mapping *sm,
383280e87e9SDmitry Safonov 		struct vm_area_struct *new_vma)
384280e87e9SDmitry Safonov {
385280e87e9SDmitry Safonov 	current->mm->context.sigpage = new_vma->vm_start;
386280e87e9SDmitry Safonov 	return 0;
387280e87e9SDmitry Safonov }
388280e87e9SDmitry Safonov 
38902e0409aSNathan Lynch static const struct vm_special_mapping sigpage_mapping = {
39002e0409aSNathan Lynch 	.name = "[sigpage]",
39102e0409aSNathan Lynch 	.pages = &signal_page,
392280e87e9SDmitry Safonov 	.mremap = sigpage_mremap,
39302e0409aSNathan Lynch };
39402e0409aSNathan Lynch 
arch_setup_additional_pages(struct linux_binprm * bprm,int uses_interp)39548be69a0SRussell King int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
39648be69a0SRussell King {
39748be69a0SRussell King 	struct mm_struct *mm = current->mm;
39802e0409aSNathan Lynch 	struct vm_area_struct *vma;
399ecf99a43SNathan Lynch 	unsigned long npages;
40048be69a0SRussell King 	unsigned long addr;
401389522b0SNathan Lynch 	unsigned long hint;
40202e0409aSNathan Lynch 	int ret = 0;
40348be69a0SRussell King 
404e0d40756SRussell King 	if (!signal_page)
405e0d40756SRussell King 		signal_page = get_signal_page();
406e0d40756SRussell King 	if (!signal_page)
40748be69a0SRussell King 		return -ENOMEM;
40848be69a0SRussell King 
409ecf99a43SNathan Lynch 	npages = 1; /* for sigpage */
410ecf99a43SNathan Lynch 	npages += vdso_total_pages;
411ecf99a43SNathan Lynch 
412d8ed45c5SMichel Lespinasse 	if (mmap_write_lock_killable(mm))
41369048176SMichal Hocko 		return -EINTR;
414ecf99a43SNathan Lynch 	hint = sigpage_addr(mm, npages);
415ecf99a43SNathan Lynch 	addr = get_unmapped_area(NULL, hint, npages << PAGE_SHIFT, 0, 0);
41648be69a0SRussell King 	if (IS_ERR_VALUE(addr)) {
41748be69a0SRussell King 		ret = addr;
41848be69a0SRussell King 		goto up_fail;
41948be69a0SRussell King 	}
42048be69a0SRussell King 
42102e0409aSNathan Lynch 	vma = _install_special_mapping(mm, addr, PAGE_SIZE,
42248be69a0SRussell King 		VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC,
42302e0409aSNathan Lynch 		&sigpage_mapping);
42448be69a0SRussell King 
42502e0409aSNathan Lynch 	if (IS_ERR(vma)) {
42602e0409aSNathan Lynch 		ret = PTR_ERR(vma);
42702e0409aSNathan Lynch 		goto up_fail;
42802e0409aSNathan Lynch 	}
42902e0409aSNathan Lynch 
43048be69a0SRussell King 	mm->context.sigpage = addr;
43148be69a0SRussell King 
432ecf99a43SNathan Lynch 	/* Unlike the sigpage, failure to install the vdso is unlikely
433ecf99a43SNathan Lynch 	 * to be fatal to the process, so no error check needed
434ecf99a43SNathan Lynch 	 * here.
435ecf99a43SNathan Lynch 	 */
436ecf99a43SNathan Lynch 	arm_install_vdso(mm, addr + PAGE_SIZE);
437ecf99a43SNathan Lynch 
43848be69a0SRussell King  up_fail:
439d8ed45c5SMichel Lespinasse 	mmap_write_unlock(mm);
44048be69a0SRussell King 	return ret;
441ec706dabSNicolas Pitre }
4426cde6d42SWill Deacon #endif
443