diff options
Diffstat (limited to 'arch/arm/kernel/process.c')
| -rw-r--r-- | arch/arm/kernel/process.c | 632 |
1 files changed, 335 insertions, 297 deletions
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index ba298277bec..81ef686a91c 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -10,184 +10,250 @@ */ #include <stdarg.h> -#include <linux/config.h> -#include <linux/module.h> +#include <linux/export.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/mm.h> #include <linux/stddef.h> #include <linux/unistd.h> -#include <linux/ptrace.h> -#include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/delay.h> #include <linux/reboot.h> #include <linux/interrupt.h> #include <linux/kallsyms.h> #include <linux/init.h> #include <linux/cpu.h> +#include <linux/elfcore.h> +#include <linux/pm.h> +#include <linux/tick.h> +#include <linux/utsname.h> +#include <linux/uaccess.h> +#include <linux/random.h> +#include <linux/hw_breakpoint.h> +#include <linux/leds.h> +#include <linux/reboot.h> -#include <asm/system.h> -#include <asm/io.h> -#include <asm/leds.h> +#include <asm/cacheflush.h> +#include <asm/idmap.h> #include <asm/processor.h> -#include <asm/uaccess.h> +#include <asm/thread_notify.h> +#include <asm/stacktrace.h> +#include <asm/system_misc.h> #include <asm/mach/time.h> +#include <asm/tls.h> -extern const char *processor_modes[]; -extern void setup_mm_for_reboot(char mode); +#ifdef CONFIG_CC_STACKPROTECTOR +#include <linux/stackprotector.h> +unsigned long __stack_chk_guard __read_mostly; +EXPORT_SYMBOL(__stack_chk_guard); +#endif -static volatile int hlt_counter; +static const char *processor_modes[] __maybe_unused = { + "USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" , "UK4_26" , "UK5_26" , "UK6_26" , "UK7_26" , + "UK8_26" , "UK9_26" , "UK10_26", "UK11_26", "UK12_26", "UK13_26", "UK14_26", "UK15_26", + "USER_32", "FIQ_32" , "IRQ_32" , "SVC_32" , "UK4_32" , "UK5_32" , "UK6_32" , "ABT_32" , + "UK8_32" , "UK9_32" , "UK10_32", "UND_32" , "UK12_32", "UK13_32", "UK14_32", "SYS_32" +}; -#include <asm/arch/system.h> +static const char *isa_modes[] __maybe_unused = { + "ARM" , "Thumb" , "Jazelle", "ThumbEE" +}; -void disable_hlt(void) -{ - hlt_counter++; -} +extern void call_with_stack(void (*fn)(void *), void *arg, void *sp); +typedef void (*phys_reset_t)(unsigned long); -EXPORT_SYMBOL(disable_hlt); +/* + * A temporary stack to use for CPU reset. This is static so that we + * don't clobber it with the identity mapping. When running with this + * stack, any references to the current task *will not work* so you + * should really do as little as possible before jumping to your reset + * code. + */ +static u64 soft_restart_stack[16]; -void enable_hlt(void) +static void __soft_restart(void *addr) { - hlt_counter--; -} + phys_reset_t phys_reset; -EXPORT_SYMBOL(enable_hlt); + /* Take out a flat memory mapping. */ + setup_mm_for_reboot(); -static int __init nohlt_setup(char *__unused) -{ - hlt_counter = 1; - return 1; + /* Clean and invalidate caches */ + flush_cache_all(); + + /* Turn off caching */ + cpu_proc_fin(); + + /* Push out any further dirty data, and ensure cache is empty */ + flush_cache_all(); + + /* Switch to the identity mapping. */ + phys_reset = (phys_reset_t)(unsigned long)virt_to_phys(cpu_reset); + phys_reset((unsigned long)addr); + + /* Should never get here. */ + BUG(); } -static int __init hlt_setup(char *__unused) +void soft_restart(unsigned long addr) { - hlt_counter = 0; - return 1; + u64 *stack = soft_restart_stack + ARRAY_SIZE(soft_restart_stack); + + /* Disable interrupts first */ + raw_local_irq_disable(); + local_fiq_disable(); + + /* Disable the L2 if we're the last man standing. */ + if (num_online_cpus() == 1) + outer_disable(); + + /* Change to the new stack and continue with the reset. */ + call_with_stack(__soft_restart, (void *)addr, (void *)stack); + + /* Should never get here. */ + BUG(); } -__setup("nohlt", nohlt_setup); -__setup("hlt", hlt_setup); +static void null_restart(enum reboot_mode reboot_mode, const char *cmd) +{ +} /* - * The following aren't currently used. + * Function pointers to optional machine specific functions */ -void (*pm_idle)(void); -EXPORT_SYMBOL(pm_idle); - void (*pm_power_off)(void); EXPORT_SYMBOL(pm_power_off); +void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd) = null_restart; +EXPORT_SYMBOL_GPL(arm_pm_restart); + +/* + * This is our default idle handler. + */ + +void (*arm_pm_idle)(void); + /* - * This is our default idle handler. We need to disable - * interrupts here to ensure we don't miss a wakeup call. + * Called from the core idle loop. */ -void default_idle(void) + +void arch_cpu_idle(void) { - local_irq_disable(); - if (!need_resched() && !hlt_counter) { - timer_dyn_reprogram(); - arch_idle(); - } + if (arm_pm_idle) + arm_pm_idle(); + else + cpu_do_idle(); local_irq_enable(); } -/* - * The idle thread. We try to conserve power, while trying to keep - * overall latency low. The architecture specific idle is passed - * a value to indicate the level of "idleness" of the system. - */ -void cpu_idle(void) +void arch_cpu_idle_prepare(void) { local_fiq_enable(); +} - /* endless idle loop with no priority at all */ - while (1) { - void (*idle)(void) = pm_idle; - -#ifdef CONFIG_HOTPLUG_CPU - if (cpu_is_offline(smp_processor_id())) { - leds_event(led_idle_start); - cpu_die(); - } +void arch_cpu_idle_enter(void) +{ + ledtrig_cpu(CPU_LED_IDLE_START); +#ifdef CONFIG_PL310_ERRATA_769419 + wmb(); #endif - - if (!idle) - idle = default_idle; - preempt_disable(); - leds_event(led_idle_start); - while (!need_resched()) - idle(); - leds_event(led_idle_end); - preempt_enable(); - schedule(); - } } -static char reboot_mode = 'h'; +void arch_cpu_idle_exit(void) +{ + ledtrig_cpu(CPU_LED_IDLE_END); +} -int __init reboot_setup(char *str) +#ifdef CONFIG_HOTPLUG_CPU +void arch_cpu_idle_dead(void) { - reboot_mode = str[0]; - return 1; + cpu_die(); } +#endif -__setup("reboot=", reboot_setup); +/* + * Called by kexec, immediately prior to machine_kexec(). + * + * This must completely disable all secondary CPUs; simply causing those CPUs + * to execute e.g. a RAM-based pin loop is not sufficient. This allows the + * kexec'd kernel to use any and all RAM as it sees fit, without having to + * avoid any code or data used by any SW CPU pin loop. The CPU hotplug + * functionality embodied in disable_nonboot_cpus() to achieve this. + */ +void machine_shutdown(void) +{ + disable_nonboot_cpus(); +} +/* + * Halting simply requires that the secondary CPUs stop performing any + * activity (executing tasks, handling interrupts). smp_send_stop() + * achieves this. + */ void machine_halt(void) { -} + local_irq_disable(); + smp_send_stop(); + local_irq_disable(); + while (1); +} +/* + * Power-off simply requires that the secondary CPUs stop performing any + * activity (executing tasks, handling interrupts). smp_send_stop() + * achieves this. When the system power is turned off, it will take all CPUs + * with it. + */ void machine_power_off(void) { + local_irq_disable(); + smp_send_stop(); + if (pm_power_off) pm_power_off(); } - -void machine_restart(char * __unused) +/* + * Restart requires that the secondary CPUs stop performing any activity + * while the primary CPU resets the system. Systems with a single CPU can + * use soft_restart() as their machine descriptor's .restart hook, since that + * will cause the only available CPU to reset. Systems with multiple CPUs must + * provide a HW restart implementation, to ensure that all CPUs reset at once. + * This is required so that any code running after reset on the primary CPU + * doesn't have to co-ordinate with other CPUs to ensure they aren't still + * executing pre-reset code, and using RAM that the primary CPU's code wishes + * to use. Implementing such co-ordination would be essentially impossible. + */ +void machine_restart(char *cmd) { - /* - * Clean and disable cache, and turn off interrupts - */ - cpu_proc_fin(); + local_irq_disable(); + smp_send_stop(); + + arm_pm_restart(reboot_mode, cmd); - /* - * Tell the mm system that we are going to reboot - - * we may need it to insert some 1:1 mappings so that - * soft boot works. - */ - setup_mm_for_reboot(reboot_mode); - - /* - * Now call the architecture specific reboot code. - */ - arch_reset(reboot_mode); - - /* - * Whoops - the architecture was unable to reboot. - * Tell the user! - */ + /* Give a grace period for failure to restart of 1s */ mdelay(1000); + + /* Whoops - the platform was unable to reboot. Tell the user! */ printk("Reboot failed -- System halted\n"); + local_irq_disable(); while (1); } void __show_regs(struct pt_regs *regs) { - unsigned long flags = condition_codes(regs); + unsigned long flags; + char buf[64]; + + show_regs_print_info(KERN_DEFAULT); - printk("CPU: %d\n", smp_processor_id()); print_symbol("PC is at %s\n", instruction_pointer(regs)); print_symbol("LR is at %s\n", regs->ARM_lr); - printk("pc : [<%08lx>] lr : [<%08lx>] %s\n" + printk("pc : [<%08lx>] lr : [<%08lx>] psr: %08lx\n" "sp : %08lx ip : %08lx fp : %08lx\n", - instruction_pointer(regs), - regs->ARM_lr, print_tainted(), regs->ARM_sp, - regs->ARM_ip, regs->ARM_fp); + regs->ARM_pc, regs->ARM_lr, regs->ARM_cpsr, + regs->ARM_sp, regs->ARM_ip, regs->ARM_fp); printk("r10: %08lx r9 : %08lx r8 : %08lx\n", regs->ARM_r10, regs->ARM_r9, regs->ARM_r8); @@ -197,180 +263,130 @@ void __show_regs(struct pt_regs *regs) printk("r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n", regs->ARM_r3, regs->ARM_r2, regs->ARM_r1, regs->ARM_r0); - printk("Flags: %c%c%c%c", - flags & PSR_N_BIT ? 'N' : 'n', - flags & PSR_Z_BIT ? 'Z' : 'z', - flags & PSR_C_BIT ? 'C' : 'c', - flags & PSR_V_BIT ? 'V' : 'v'); - printk(" IRQs o%s FIQs o%s Mode %s%s Segment %s\n", - interrupts_enabled(regs) ? "n" : "ff", + + flags = regs->ARM_cpsr; + buf[0] = flags & PSR_N_BIT ? 'N' : 'n'; + buf[1] = flags & PSR_Z_BIT ? 'Z' : 'z'; + buf[2] = flags & PSR_C_BIT ? 'C' : 'c'; + buf[3] = flags & PSR_V_BIT ? 'V' : 'v'; + buf[4] = '\0'; + +#ifndef CONFIG_CPU_V7M + printk("Flags: %s IRQs o%s FIQs o%s Mode %s ISA %s Segment %s\n", + buf, interrupts_enabled(regs) ? "n" : "ff", fast_interrupts_enabled(regs) ? "n" : "ff", processor_modes[processor_mode(regs)], - thumb_mode(regs) ? " (T)" : "", + isa_modes[isa_mode(regs)], get_fs() == get_ds() ? "kernel" : "user"); +#else + printk("xPSR: %08lx\n", regs->ARM_cpsr); +#endif + +#ifdef CONFIG_CPU_CP15 { - unsigned int ctrl, transbase, dac; - __asm__ ( - " mrc p15, 0, %0, c1, c0\n" - " mrc p15, 0, %1, c2, c0\n" - " mrc p15, 0, %2, c3, c0\n" - : "=r" (ctrl), "=r" (transbase), "=r" (dac)); - printk("Control: %04X Table: %08X DAC: %08X\n", - ctrl, transbase, dac); + unsigned int ctrl; + + buf[0] = '\0'; +#ifdef CONFIG_CPU_CP15_MMU + { + unsigned int transbase, dac; + asm("mrc p15, 0, %0, c2, c0\n\t" + "mrc p15, 0, %1, c3, c0\n" + : "=r" (transbase), "=r" (dac)); + snprintf(buf, sizeof(buf), " Table: %08x DAC: %08x", + transbase, dac); + } +#endif + asm("mrc p15, 0, %0, c1, c0\n" : "=r" (ctrl)); + + printk("Control: %08x%s\n", ctrl, buf); } +#endif } void show_regs(struct pt_regs * regs) { printk("\n"); - printk("Pid: %d, comm: %20s\n", current->pid, current->comm); __show_regs(regs); - __backtrace(); + dump_stack(); } -void show_fpregs(struct user_fp *regs) -{ - int i; - - for (i = 0; i < 8; i++) { - unsigned long *p; - char type; - - p = (unsigned long *)(regs->fpregs + i); +ATOMIC_NOTIFIER_HEAD(thread_notify_head); - switch (regs->ftype[i]) { - case 1: type = 'f'; break; - case 2: type = 'd'; break; - case 3: type = 'e'; break; - default: type = '?'; break; - } - if (regs->init_flag) - type = '?'; - - printk(" f%d(%c): %08lx %08lx %08lx%c", - i, type, p[0], p[1], p[2], i & 1 ? '\n' : ' '); - } - - - printk("FPSR: %08lx FPCR: %08lx\n", - (unsigned long)regs->fpsr, - (unsigned long)regs->fpcr); -} - -/* - * Task structure and kernel stack allocation. - */ -static unsigned long *thread_info_head; -static unsigned int nr_thread_info; - -#define EXTRA_TASK_STRUCT 4 - -struct thread_info *alloc_thread_info(struct task_struct *task) -{ - struct thread_info *thread = NULL; - - if (EXTRA_TASK_STRUCT) { - unsigned long *p = thread_info_head; - - if (p) { - thread_info_head = (unsigned long *)p[0]; - nr_thread_info -= 1; - } - thread = (struct thread_info *)p; - } - - if (!thread) - thread = (struct thread_info *) - __get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER); - -#ifdef CONFIG_DEBUG_STACK_USAGE - /* - * The stack must be cleared if you want SYSRQ-T to - * give sensible stack usage information - */ - if (thread) - memzero(thread, THREAD_SIZE); -#endif - return thread; -} - -void free_thread_info(struct thread_info *thread) -{ - if (EXTRA_TASK_STRUCT && nr_thread_info < EXTRA_TASK_STRUCT) { - unsigned long *p = (unsigned long *)thread; - p[0] = (unsigned long)thread_info_head; - thread_info_head = p; - nr_thread_info += 1; - } else - free_pages((unsigned long)thread, THREAD_SIZE_ORDER); -} +EXPORT_SYMBOL_GPL(thread_notify_head); /* * Free current thread data structures etc.. */ void exit_thread(void) { + thread_notify(THREAD_NOTIFY_EXIT, current_thread_info()); } -static void default_fp_init(union fp_state *fp) -{ - memset(fp, 0, sizeof(union fp_state)); -} - -void (*fp_init)(union fp_state *) = default_fp_init; -EXPORT_SYMBOL(fp_init); - void flush_thread(void) { struct thread_info *thread = current_thread_info(); struct task_struct *tsk = current; + flush_ptrace_hw_breakpoint(tsk); + memset(thread->used_cp, 0, sizeof(thread->used_cp)); memset(&tsk->thread.debug, 0, sizeof(struct debug_info)); -#if defined(CONFIG_IWMMXT) - iwmmxt_task_release(thread); -#endif - fp_init(&thread->fpstate); -#if defined(CONFIG_VFP) - vfp_flush_thread(&thread->vfpstate); -#endif + memset(&thread->fpstate, 0, sizeof(union fp_state)); + + thread_notify(THREAD_NOTIFY_FLUSH, thread); } void release_thread(struct task_struct *dead_task) { -#if defined(CONFIG_VFP) - vfp_release_thread(&dead_task->thread_info->vfpstate); -#endif -#if defined(CONFIG_IWMMXT) - iwmmxt_task_release(dead_task->thread_info); -#endif } asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); int -copy_thread(int nr, unsigned long clone_flags, unsigned long stack_start, - unsigned long stk_sz, struct task_struct *p, struct pt_regs *regs) +copy_thread(unsigned long clone_flags, unsigned long stack_start, + unsigned long stk_sz, struct task_struct *p) { - struct thread_info *thread = p->thread_info; - struct pt_regs *childregs; - - childregs = ((struct pt_regs *)((unsigned long)thread + THREAD_START_SP)) - 1; - *childregs = *regs; - childregs->ARM_r0 = 0; - childregs->ARM_sp = stack_start; + struct thread_info *thread = task_thread_info(p); + struct pt_regs *childregs = task_pt_regs(p); memset(&thread->cpu_context, 0, sizeof(struct cpu_context_save)); - thread->cpu_context.sp = (unsigned long)childregs; + + if (likely(!(p->flags & PF_KTHREAD))) { + *childregs = *current_pt_regs(); + childregs->ARM_r0 = 0; + if (stack_start) + childregs->ARM_sp = stack_start; + } else { + memset(childregs, 0, sizeof(struct pt_regs)); + thread->cpu_context.r4 = stk_sz; + thread->cpu_context.r5 = stack_start; + childregs->ARM_cpsr = SVC_MODE; + } thread->cpu_context.pc = (unsigned long)ret_from_fork; + thread->cpu_context.sp = (unsigned long)childregs; + + clear_ptrace_hw_breakpoint(p); if (clone_flags & CLONE_SETTLS) - thread->tp_value = regs->ARM_r3; + thread->tp_value[0] = childregs->ARM_r3; + thread->tp_value[1] = get_tpuser(); + + thread_notify(THREAD_NOTIFY_COPY, thread); return 0; } /* + * Fill in the task's elfregs structure for a core dump. + */ +int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs) +{ + elf_core_copy_regs(elfregs, task_pt_regs(t)); + return 1; +} + +/* * fill in the fpe structure for a core dump... */ int dump_fpu (struct pt_regs *regs, struct user_fp *fp) @@ -385,90 +401,112 @@ int dump_fpu (struct pt_regs *regs, struct user_fp *fp) } EXPORT_SYMBOL(dump_fpu); -/* - * fill in the user structure for a core dump.. - */ -void dump_thread(struct pt_regs * regs, struct user * dump) +unsigned long get_wchan(struct task_struct *p) { - struct task_struct *tsk = current; - - dump->magic = CMAGIC; - dump->start_code = tsk->mm->start_code; - dump->start_stack = regs->ARM_sp & ~(PAGE_SIZE - 1); - - dump->u_tsize = (tsk->mm->end_code - tsk->mm->start_code) >> PAGE_SHIFT; - dump->u_dsize = (tsk->mm->brk - tsk->mm->start_data + PAGE_SIZE - 1) >> PAGE_SHIFT; - dump->u_ssize = 0; - - dump->u_debugreg[0] = tsk->thread.debug.bp[0].address; - dump->u_debugreg[1] = tsk->thread.debug.bp[1].address; - dump->u_debugreg[2] = tsk->thread.debug.bp[0].insn.arm; - dump->u_debugreg[3] = tsk->thread.debug.bp[1].insn.arm; - dump->u_debugreg[4] = tsk->thread.debug.nsaved; + struct stackframe frame; + unsigned long stack_page; + int count = 0; + if (!p || p == current || p->state == TASK_RUNNING) + return 0; - if (dump->start_stack < 0x04000000) - dump->u_ssize = (0x04000000 - dump->start_stack) >> PAGE_SHIFT; + frame.fp = thread_saved_fp(p); + frame.sp = thread_saved_sp(p); + frame.lr = 0; /* recovered from the stack */ + frame.pc = thread_saved_pc(p); + stack_page = (unsigned long)task_stack_page(p); + do { + if (frame.sp < stack_page || + frame.sp >= stack_page + THREAD_SIZE || + unwind_frame(&frame) < 0) + return 0; + if (!in_sched_functions(frame.pc)) + return frame.pc; + } while (count ++ < 16); + return 0; +} - dump->regs = *regs; - dump->u_fpvalid = dump_fpu (regs, &dump->u_fp); +unsigned long arch_randomize_brk(struct mm_struct *mm) +{ + unsigned long range_end = mm->brk + 0x02000000; + return randomize_range(mm->brk, range_end, 0) ? : mm->brk; } -EXPORT_SYMBOL(dump_thread); +#ifdef CONFIG_MMU +#ifdef CONFIG_KUSER_HELPERS /* - * Shuffle the argument into the correct register before calling the - * thread function. r1 is the thread argument, r2 is the pointer to - * the thread function, and r3 points to the exit function. + * The vectors page is always readable from user space for the + * atomic helpers. Insert it into the gate_vma so that it is visible + * through ptrace and /proc/<pid>/mem. */ -extern void kernel_thread_helper(void); -asm( ".section .text\n" -" .align\n" -" .type kernel_thread_helper, #function\n" -"kernel_thread_helper:\n" -" mov r0, r1\n" -" mov lr, r3\n" -" mov pc, r2\n" -" .size kernel_thread_helper, . - kernel_thread_helper\n" -" .previous"); +static struct vm_area_struct gate_vma = { + .vm_start = 0xffff0000, + .vm_end = 0xffff0000 + PAGE_SIZE, + .vm_flags = VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYEXEC, +}; -/* - * Create a kernel thread. - */ -pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) +static int __init gate_vma_init(void) { - struct pt_regs regs; + gate_vma.vm_page_prot = PAGE_READONLY_EXEC; + return 0; +} +arch_initcall(gate_vma_init); - memset(®s, 0, sizeof(regs)); +struct vm_area_struct *get_gate_vma(struct mm_struct *mm) +{ + return &gate_vma; +} - regs.ARM_r1 = (unsigned long)arg; - regs.ARM_r2 = (unsigned long)fn; - regs.ARM_r3 = (unsigned long)do_exit; - regs.ARM_pc = (unsigned long)kernel_thread_helper; - regs.ARM_cpsr = SVC_MODE; +int in_gate_area(struct mm_struct *mm, unsigned long addr) +{ + return (addr >= gate_vma.vm_start) && (addr < gate_vma.vm_end); +} - return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); +int in_gate_area_no_mm(unsigned long addr) +{ + return in_gate_area(NULL, addr); } -EXPORT_SYMBOL(kernel_thread); +#define is_gate_vma(vma) ((vma) == &gate_vma) +#else +#define is_gate_vma(vma) 0 +#endif -unsigned long get_wchan(struct task_struct *p) +const char *arch_vma_name(struct vm_area_struct *vma) { - unsigned long fp, lr; - unsigned long stack_start, stack_end; - int count = 0; - if (!p || p == current || p->state == TASK_RUNNING) - return 0; + return is_gate_vma(vma) ? "[vectors]" : + (vma->vm_mm && vma->vm_start == vma->vm_mm->context.sigpage) ? + "[sigpage]" : NULL; +} - stack_start = (unsigned long)(p->thread_info + 1); - stack_end = ((unsigned long)p->thread_info) + THREAD_SIZE; +static struct page *signal_page; +extern struct page *get_signal_page(void); - fp = thread_saved_fp(p); - do { - if (fp < stack_start || fp > stack_end) - return 0; - lr = pc_pointer (((unsigned long *)fp)[-1]); - if (!in_sched_functions(lr)) - return lr; - fp = *(unsigned long *) (fp - 12); - } while (count ++ < 16); - return 0; +int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) +{ + struct mm_struct *mm = current->mm; + unsigned long addr; + int ret; + + if (!signal_page) + signal_page = get_signal_page(); + if (!signal_page) + return -ENOMEM; + + down_write(&mm->mmap_sem); + addr = get_unmapped_area(NULL, 0, PAGE_SIZE, 0, 0); + if (IS_ERR_VALUE(addr)) { + ret = addr; + goto up_fail; + } + + ret = install_special_mapping(mm, addr, PAGE_SIZE, + VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC, + &signal_page); + + if (ret == 0) + mm->context.sigpage = addr; + + up_fail: + up_write(&mm->mmap_sem); + return ret; } -EXPORT_SYMBOL(get_wchan); +#endif |
