File: /usr/src/linux/arch/alpha/mm/fault.c

1     /*
2      *  linux/arch/alpha/mm/fault.c
3      *
4      *  Copyright (C) 1995  Linus Torvalds
5      */
6     
7     #include <linux/config.h>
8     #include <linux/sched.h>
9     #include <linux/kernel.h>
10     #include <linux/mm.h>
11     #include <asm/io.h>
12     
13     #define __EXTERN_INLINE inline
14     #include <asm/mmu_context.h>
15     #include <asm/pgalloc.h>
16     #undef  __EXTERN_INLINE
17     
18     #include <linux/signal.h>
19     #include <linux/errno.h>
20     #include <linux/string.h>
21     #include <linux/types.h>
22     #include <linux/ptrace.h>
23     #include <linux/mman.h>
24     #include <linux/smp.h>
25     #include <linux/smp_lock.h>
26     #include <linux/interrupt.h>
27     
28     #include <asm/system.h>
29     #include <asm/uaccess.h>
30     
31     extern void die_if_kernel(char *,struct pt_regs *,long, unsigned long *);
32     
33     
34     /*
35      * Force a new ASN for a task.
36      */
37     
38     #ifndef CONFIG_SMP
39     unsigned long last_asn = ASN_FIRST_VERSION;
40     #endif
41     
42     extern void
43     __load_new_mm_context(struct mm_struct *next_mm)
44     {
45     	unsigned long mmc;
46     
47     	mmc = __get_new_mm_context(next_mm, smp_processor_id());
48     	next_mm->context[smp_processor_id()] = mmc;
49     	current->thread.asn = mmc & HARDWARE_ASN_MASK;
50             current->thread.ptbr
51     	  = ((unsigned long) next_mm->pgd - IDENT_ADDR) >> PAGE_SHIFT;
52     
53     	__reload_thread(&current->thread);
54     }
55     
56     
57     /*
58      * This routine handles page faults.  It determines the address,
59      * and the problem, and then passes it off to handle_mm_fault().
60      *
61      * mmcsr:
62      *	0 = translation not valid
63      *	1 = access violation
64      *	2 = fault-on-read
65      *	3 = fault-on-execute
66      *	4 = fault-on-write
67      *
68      * cause:
69      *	-1 = instruction fetch
70      *	0 = load
71      *	1 = store
72      *
73      * Registers $9 through $15 are saved in a block just prior to `regs' and
74      * are saved and restored around the call to allow exception code to
75      * modify them.
76      */
77     
78     /* Macro for exception fixup code to access integer registers.  */
79     #define dpf_reg(r)							\
80     	(((unsigned long *)regs)[(r) <= 8 ? (r) : (r) <= 15 ? (r)-16 :	\
81     				 (r) <= 18 ? (r)+8 : (r)-10])
82     
83     asmlinkage void
84     do_page_fault(unsigned long address, unsigned long mmcsr,
85     	      long cause, struct pt_regs *regs)
86     {
87     	struct vm_area_struct * vma;
88     	struct mm_struct *mm = current->mm;
89     	unsigned int fixup;
90     	int fault;
91     
92     	/* As of EV6, a load into $31/$f31 is a prefetch, and never faults
93     	   (or is suppressed by the PALcode).  Support that for older CPUs
94     	   by ignoring such an instruction.  */
95     	if (cause == 0) {
96     		unsigned int insn;
97     		__get_user(insn, (unsigned int *)regs->pc);
98     		if ((insn >> 21 & 0x1f) == 0x1f &&
99     		    /* ldq ldl ldt lds ldg ldf ldwu ldbu */
100     		    (1ul << (insn >> 26) & 0x30f00001400ul)) {
101     			regs->pc += 4;
102     			return;
103     		}
104     	}
105     
106     	/* If we're in an interrupt context, or have no user context,
107     	   we must not take the fault.  */
108     	if (!mm || in_interrupt())
109     		goto no_context;
110     
111     #ifdef CONFIG_ALPHA_LARGE_VMALLOC
112     	if (address >= TASK_SIZE)
113     		goto vmalloc_fault;
114     #endif
115     
116     	down_read(&mm->mmap_sem);
117     	vma = find_vma(mm, address);
118     	if (!vma)
119     		goto bad_area;
120     	if (vma->vm_start <= address)
121     		goto good_area;
122     	if (!(vma->vm_flags & VM_GROWSDOWN))
123     		goto bad_area;
124     	if (expand_stack(vma, address))
125     		goto bad_area;
126     /*
127      * Ok, we have a good vm_area for this memory access, so
128      * we can handle it..
129      */
130     good_area:
131     	if (cause < 0) {
132     		if (!(vma->vm_flags & VM_EXEC))
133     			goto bad_area;
134     	} else if (!cause) {
135     		/* Allow reads even for write-only mappings */
136     		if (!(vma->vm_flags & (VM_READ | VM_WRITE)))
137     			goto bad_area;
138     	} else {
139     		if (!(vma->vm_flags & VM_WRITE))
140     			goto bad_area;
141     	}
142     
143      survive:
144     	/*
145     	 * If for any reason at all we couldn't handle the fault,
146     	 * make sure we exit gracefully rather than endlessly redo
147     	 * the fault.
148     	 */
149     	fault = handle_mm_fault(mm, vma, address, cause > 0);
150     	up_read(&mm->mmap_sem);
151     
152     	if (fault < 0)
153     		goto out_of_memory;
154     	if (fault == 0)
155     		goto do_sigbus;
156     
157     	return;
158     
159     /*
160      * Something tried to access memory that isn't in our memory map..
161      * Fix it, but check if it's kernel or user first..
162      */
163     bad_area:
164     	up_read(&mm->mmap_sem);
165     
166     	if (user_mode(regs)) {
167     		force_sig(SIGSEGV, current);
168     		return;
169     	}
170     
171     no_context:
172     	/* Are we prepared to handle this fault as an exception?  */
173     	if ((fixup = search_exception_table(regs->pc, regs->gp)) != 0) {
174     		unsigned long newpc;
175     		newpc = fixup_exception(dpf_reg, fixup, regs->pc);
176     #if 0
177     		printk("%s: Exception at [<%lx>] (%lx) handled successfully\n",
178     		       current->comm, regs->pc, newpc);
179     #endif
180     		regs->pc = newpc;
181     		return;
182     	}
183     
184     /*
185      * Oops. The kernel tried to access some bad page. We'll have to
186      * terminate things with extreme prejudice.
187      */
188     	printk(KERN_ALERT "Unable to handle kernel paging request at "
189     	       "virtual address %016lx\n", address);
190     	die_if_kernel("Oops", regs, cause, (unsigned long*)regs - 16);
191     	do_exit(SIGKILL);
192     
193     /*
194      * We ran out of memory, or some other thing happened to us that made
195      * us unable to handle the page fault gracefully.
196      */
197     out_of_memory:
198     	if (current->pid == 1) {
199     		current->policy |= SCHED_YIELD;
200     		schedule();
201     		down_read(&mm->mmap_sem);
202     		goto survive;
203     	}
204     	printk(KERN_ALERT "VM: killing process %s(%d)\n",
205     	       current->comm, current->pid);
206     	if (!user_mode(regs))
207     		goto no_context;
208     	do_exit(SIGKILL);
209     
210     do_sigbus:
211     	/*
212     	 * Send a sigbus, regardless of whether we were in kernel
213     	 * or user mode.
214     	 */
215     	force_sig(SIGBUS, current);
216     	if (!user_mode(regs))
217     		goto no_context;
218     	return;
219     
220     #ifdef CONFIG_ALPHA_LARGE_VMALLOC
221     vmalloc_fault:
222     	if (user_mode(regs)) {
223     		force_sig(SIGSEGV, current);
224     		return;
225     	} else {
226     		/* Synchronize this task's top level page-table
227     		   with the "reference" page table from init.  */
228     		long offset = __pgd_offset(address);
229     		pgd_t *pgd, *pgd_k;
230     
231     		pgd = current->active_mm->pgd + offset;
232     		pgd_k = swapper_pg_dir + offset;
233     		if (!pgd_present(*pgd) && pgd_present(*pgd_k)) {
234     			pgd_val(*pgd) = pgd_val(*pgd_k);
235     			return;
236     		}
237     		goto no_context;
238     	}
239     #endif
240     }
241