| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  *  linux/arch/cris/mm/fault.c | 
 | 3 |  * | 
 | 4 |  *  Copyright (C) 2000, 2001  Axis Communications AB | 
 | 5 |  * | 
 | 6 |  *  Authors:  Bjorn Wesen  | 
 | 7 |  *  | 
 | 8 |  *  $Log: fault.c,v $ | 
| Mikael Starvik | 4f18cfb | 2005-07-27 11:44:39 -0700 | [diff] [blame] | 9 |  *  Revision 1.20  2005/03/04 08:16:18  starvik | 
 | 10 |  *  Merge of Linux 2.6.11. | 
 | 11 |  * | 
 | 12 |  *  Revision 1.19  2005/01/14 10:07:59  starvik | 
 | 13 |  *  Fixed warning. | 
 | 14 |  * | 
 | 15 |  *  Revision 1.18  2005/01/12 08:10:14  starvik | 
 | 16 |  *  Readded the change of frametype when handling kernel page fault fixup | 
 | 17 |  *  for v10. This is necessary to avoid that the CPU remakes the faulting | 
 | 18 |  *  access. | 
 | 19 |  * | 
 | 20 |  *  Revision 1.17  2005/01/11 13:53:05  starvik | 
 | 21 |  *  Use raw_printk. | 
 | 22 |  * | 
 | 23 |  *  Revision 1.16  2004/12/17 11:39:41  starvik | 
 | 24 |  *  SMP support. | 
 | 25 |  * | 
 | 26 |  *  Revision 1.15  2004/11/23 18:36:18  starvik | 
 | 27 |  *  Stack is now non-executable. | 
 | 28 |  *  Signal handler trampolines are placed in a reserved page mapped into all | 
 | 29 |  *  processes. | 
 | 30 |  * | 
 | 31 |  *  Revision 1.14  2004/11/23 07:10:21  starvik | 
 | 32 |  *  Moved find_fixup_code to generic code. | 
 | 33 |  * | 
 | 34 |  *  Revision 1.13  2004/11/23 07:00:54  starvik | 
 | 35 |  *  Actually use the execute permission bit in the MMU. This makes it possible | 
 | 36 |  *  to prevent e.g. attacks where executable code is put on the stack. | 
 | 37 |  * | 
 | 38 |  *  Revision 1.12  2004/09/29 06:16:04  starvik | 
 | 39 |  *  Use instruction_pointer | 
 | 40 |  * | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 41 |  *  Revision 1.11  2004/05/14 07:58:05  starvik | 
 | 42 |  *  Merge of changes from 2.4 | 
 | 43 |  * | 
 | 44 |  *  Revision 1.10  2003/10/27 14:51:24  starvik | 
 | 45 |  *  Removed debugcode | 
 | 46 |  * | 
 | 47 |  *  Revision 1.9  2003/10/27 14:50:42  starvik | 
 | 48 |  *  Changed do_page_fault signature | 
 | 49 |  * | 
 | 50 |  *  Revision 1.8  2003/07/04 13:02:48  tobiasa | 
 | 51 |  *  Moved code snippet from arch/cris/mm/fault.c that searches for fixup code | 
 | 52 |  *  to seperate function in arch-specific files. | 
 | 53 |  * | 
 | 54 |  *  Revision 1.7  2003/01/22 06:48:38  starvik | 
 | 55 |  *  Fixed warnings issued by GCC 3.2.1 | 
 | 56 |  * | 
 | 57 |  *  Revision 1.6  2003/01/09 14:42:52  starvik | 
 | 58 |  *  Merge of Linux 2.5.55 | 
 | 59 |  * | 
 | 60 |  *  Revision 1.5  2002/12/11 14:44:48  starvik | 
 | 61 |  *  Extracted v10 (ETRAX 100LX) specific stuff to arch/cris/arch-v10/mm | 
 | 62 |  * | 
 | 63 |  *  Revision 1.4  2002/11/13 15:10:28  starvik | 
 | 64 |  *  pte_offset has been renamed to pte_offset_kernel | 
 | 65 |  * | 
 | 66 |  *  Revision 1.3  2002/11/05 06:45:13  starvik | 
 | 67 |  *  Merge of Linux 2.5.45 | 
 | 68 |  * | 
 | 69 |  *  Revision 1.2  2001/12/18 13:35:22  bjornw | 
 | 70 |  *  Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). | 
 | 71 |  * | 
 | 72 |  *  Revision 1.20  2001/11/22 13:34:06  bjornw | 
 | 73 |  *  * Bug workaround (LX TR89): force a rerun of the whole of an interrupted | 
 | 74 |  *    unaligned write, because the second half of the write will be corrupted | 
 | 75 |  *    otherwise. Affected unaligned writes spanning not-yet mapped pages. | 
 | 76 |  *  * Optimization: use the wr_rd bit in R_MMU_CAUSE to know whether a miss | 
 | 77 |  *    was due to a read or a write (before we didn't know this until the next | 
 | 78 |  *    restart of the interrupted instruction, thus wasting one fault-irq) | 
 | 79 |  * | 
 | 80 |  *  Revision 1.19  2001/11/12 19:02:10  pkj | 
 | 81 |  *  Fixed compiler warnings. | 
 | 82 |  * | 
 | 83 |  *  Revision 1.18  2001/07/18 22:14:32  bjornw | 
 | 84 |  *  Enable interrupts in the bulk of do_page_fault | 
 | 85 |  * | 
 | 86 |  *  Revision 1.17  2001/07/18 13:07:23  bjornw | 
 | 87 |  *  * Detect non-existant PTE's in vmalloc pmd synchronization | 
 | 88 |  *  * Remove comment about fast-paths for VMALLOC_START etc, because all that | 
 | 89 |  *    was totally bogus anyway it turned out :) | 
 | 90 |  *  * Fix detection of vmalloc-area synchronization | 
 | 91 |  *  * Add some comments | 
 | 92 |  * | 
 | 93 |  *  Revision 1.16  2001/06/13 00:06:08  bjornw | 
 | 94 |  *  current_pgd should be volatile | 
 | 95 |  * | 
 | 96 |  *  Revision 1.15  2001/06/13 00:02:23  bjornw | 
 | 97 |  *  Use a separate variable to store the current pgd to avoid races in schedule | 
 | 98 |  * | 
 | 99 |  *  Revision 1.14  2001/05/16 17:41:07  hp | 
 | 100 |  *  Last comment tweak further tweaked. | 
 | 101 |  * | 
 | 102 |  *  Revision 1.13  2001/05/15 00:58:44  hp | 
 | 103 |  *  Expand a bit on the comment why we compare address >= TASK_SIZE rather | 
 | 104 |  *  than >= VMALLOC_START. | 
 | 105 |  * | 
 | 106 |  *  Revision 1.12  2001/04/04 10:51:14  bjornw | 
 | 107 |  *  mmap_sem is grabbed for reading | 
 | 108 |  * | 
 | 109 |  *  Revision 1.11  2001/03/23 07:36:07  starvik | 
 | 110 |  *  Corrected according to review remarks | 
 | 111 |  * | 
 | 112 |  *  Revision 1.10  2001/03/21 16:10:11  bjornw | 
 | 113 |  *  CRIS_FRAME_FIXUP not needed anymore, use FRAME_NORMAL | 
 | 114 |  * | 
 | 115 |  *  Revision 1.9  2001/03/05 13:22:20  bjornw | 
 | 116 |  *  Spell-fix and fix in vmalloc_fault handling | 
 | 117 |  * | 
 | 118 |  *  Revision 1.8  2000/11/22 14:45:31  bjornw | 
 | 119 |  *  * 2.4.0-test10 removed the set_pgdir instantaneous kernel global mapping | 
 | 120 |  *    into all processes. Instead we fill in the missing PTE entries on demand. | 
 | 121 |  * | 
 | 122 |  *  Revision 1.7  2000/11/21 16:39:09  bjornw | 
 | 123 |  *  fixup switches frametype | 
 | 124 |  * | 
 | 125 |  *  Revision 1.6  2000/11/17 16:54:08  bjornw | 
 | 126 |  *  More detailed siginfo reporting | 
 | 127 |  * | 
 | 128 |  * | 
 | 129 |  */ | 
 | 130 |  | 
 | 131 | #include <linux/mm.h> | 
 | 132 | #include <linux/interrupt.h> | 
 | 133 | #include <linux/module.h> | 
 | 134 | #include <asm/uaccess.h> | 
 | 135 |  | 
 | 136 | extern int find_fixup_code(struct pt_regs *); | 
 | 137 | extern void die_if_kernel(const char *, struct pt_regs *, long); | 
| Mikael Starvik | 4f18cfb | 2005-07-27 11:44:39 -0700 | [diff] [blame] | 138 | extern int raw_printk(const char *fmt, ...); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 139 |  | 
 | 140 | /* debug of low-level TLB reload */ | 
 | 141 | #undef DEBUG | 
 | 142 |  | 
 | 143 | #ifdef DEBUG | 
 | 144 | #define D(x) x | 
 | 145 | #else | 
 | 146 | #define D(x) | 
 | 147 | #endif | 
 | 148 |  | 
 | 149 | /* debug of higher-level faults */ | 
 | 150 | #define DPG(x) | 
 | 151 |  | 
 | 152 | /* current active page directory */ | 
 | 153 |  | 
| Mikael Starvik | 4f18cfb | 2005-07-27 11:44:39 -0700 | [diff] [blame] | 154 | volatile DEFINE_PER_CPU(pgd_t *,current_pgd); | 
 | 155 | unsigned long cris_signal_return_page; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 156 |  | 
 | 157 | /* | 
 | 158 |  * This routine handles page faults.  It determines the address, | 
 | 159 |  * and the problem, and then passes it off to one of the appropriate | 
 | 160 |  * routines. | 
 | 161 |  * | 
 | 162 |  * Notice that the address we're given is aligned to the page the fault | 
 | 163 |  * occurred in, since we only get the PFN in R_MMU_CAUSE not the complete | 
 | 164 |  * address. | 
 | 165 |  * | 
 | 166 |  * error_code: | 
 | 167 |  *	bit 0 == 0 means no page found, 1 means protection fault | 
 | 168 |  *	bit 1 == 0 means read, 1 means write | 
 | 169 |  * | 
 | 170 |  * If this routine detects a bad access, it returns 1, otherwise it | 
 | 171 |  * returns 0. | 
 | 172 |  */ | 
 | 173 |  | 
 | 174 | asmlinkage void | 
 | 175 | do_page_fault(unsigned long address, struct pt_regs *regs, | 
 | 176 | 	      int protection, int writeaccess) | 
 | 177 | { | 
 | 178 | 	struct task_struct *tsk; | 
 | 179 | 	struct mm_struct *mm; | 
 | 180 | 	struct vm_area_struct * vma; | 
 | 181 | 	siginfo_t info; | 
| Nick Piggin | 83c5407 | 2007-07-19 01:47:05 -0700 | [diff] [blame] | 182 | 	int fault; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 183 |  | 
| Mikael Starvik | 4f18cfb | 2005-07-27 11:44:39 -0700 | [diff] [blame] | 184 |         D(printk("Page fault for %lX on %X at %lX, prot %d write %d\n", | 
 | 185 |                  address, smp_processor_id(), instruction_pointer(regs), | 
 | 186 |                  protection, writeaccess)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 187 |  | 
 | 188 | 	tsk = current; | 
 | 189 |  | 
 | 190 | 	/* | 
 | 191 | 	 * We fault-in kernel-space virtual memory on-demand. The | 
 | 192 | 	 * 'reference' page table is init_mm.pgd. | 
 | 193 | 	 * | 
 | 194 | 	 * NOTE! We MUST NOT take any locks for this case. We may | 
 | 195 | 	 * be in an interrupt or a critical region, and should | 
 | 196 | 	 * only copy the information from the master page table, | 
 | 197 | 	 * nothing more. | 
 | 198 | 	 * | 
 | 199 | 	 * NOTE2: This is done so that, when updating the vmalloc | 
 | 200 | 	 * mappings we don't have to walk all processes pgdirs and | 
 | 201 | 	 * add the high mappings all at once. Instead we do it as they | 
 | 202 | 	 * are used. However vmalloc'ed page entries have the PAGE_GLOBAL | 
 | 203 | 	 * bit set so sometimes the TLB can use a lingering entry. | 
 | 204 | 	 * | 
 | 205 | 	 * This verifies that the fault happens in kernel space | 
 | 206 | 	 * and that the fault was not a protection error (error_code & 1). | 
 | 207 | 	 */ | 
 | 208 |  | 
 | 209 | 	if (address >= VMALLOC_START && | 
 | 210 | 	    !protection && | 
 | 211 | 	    !user_mode(regs)) | 
 | 212 | 		goto vmalloc_fault; | 
 | 213 |  | 
| Mikael Starvik | 4f18cfb | 2005-07-27 11:44:39 -0700 | [diff] [blame] | 214 | 	/* When stack execution is not allowed we store the signal | 
 | 215 | 	 * trampolines in the reserved cris_signal_return_page. | 
 | 216 | 	 * Handle this in the exact same way as vmalloc (we know | 
 | 217 | 	 * that the mapping is there and is valid so no need to | 
 | 218 | 	 * call handle_mm_fault). | 
 | 219 | 	 */ | 
 | 220 | 	if (cris_signal_return_page && | 
 | 221 | 	    address == cris_signal_return_page && | 
 | 222 | 	    !protection && user_mode(regs)) | 
 | 223 | 		goto vmalloc_fault; | 
 | 224 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 225 | 	/* we can and should enable interrupts at this point */ | 
| Mikael Starvik | 4f18cfb | 2005-07-27 11:44:39 -0700 | [diff] [blame] | 226 | 	local_irq_enable(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 227 |  | 
 | 228 | 	mm = tsk->mm; | 
 | 229 | 	info.si_code = SEGV_MAPERR; | 
 | 230 |  | 
 | 231 | 	/* | 
 | 232 | 	 * If we're in an interrupt or have no user | 
 | 233 | 	 * context, we must not take the fault.. | 
 | 234 | 	 */ | 
 | 235 |  | 
| Peter Zijlstra | 6edaf68 | 2006-12-06 20:32:18 -0800 | [diff] [blame] | 236 | 	if (in_atomic() || !mm) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 237 | 		goto no_context; | 
 | 238 |  | 
 | 239 | 	down_read(&mm->mmap_sem); | 
 | 240 | 	vma = find_vma(mm, address); | 
 | 241 | 	if (!vma) | 
 | 242 | 		goto bad_area; | 
 | 243 | 	if (vma->vm_start <= address) | 
 | 244 | 		goto good_area; | 
 | 245 | 	if (!(vma->vm_flags & VM_GROWSDOWN)) | 
 | 246 | 		goto bad_area; | 
 | 247 | 	if (user_mode(regs)) { | 
 | 248 | 		/* | 
 | 249 | 		 * accessing the stack below usp is always a bug. | 
 | 250 | 		 * we get page-aligned addresses so we can only check | 
 | 251 | 		 * if we're within a page from usp, but that might be | 
 | 252 | 		 * enough to catch brutal errors at least. | 
 | 253 | 		 */ | 
 | 254 | 		if (address + PAGE_SIZE < rdusp()) | 
 | 255 | 			goto bad_area; | 
 | 256 | 	} | 
 | 257 | 	if (expand_stack(vma, address)) | 
 | 258 | 		goto bad_area; | 
 | 259 |  | 
 | 260 | 	/* | 
 | 261 | 	 * Ok, we have a good vm_area for this memory access, so | 
 | 262 | 	 * we can handle it.. | 
 | 263 | 	 */ | 
 | 264 |  | 
 | 265 |  good_area: | 
 | 266 | 	info.si_code = SEGV_ACCERR; | 
 | 267 |  | 
 | 268 | 	/* first do some preliminary protection checks */ | 
 | 269 |  | 
| Mikael Starvik | 4f18cfb | 2005-07-27 11:44:39 -0700 | [diff] [blame] | 270 | 	if (writeaccess == 2){ | 
 | 271 | 		if (!(vma->vm_flags & VM_EXEC)) | 
 | 272 | 			goto bad_area; | 
 | 273 | 	} else if (writeaccess == 1) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 274 | 		if (!(vma->vm_flags & VM_WRITE)) | 
 | 275 | 			goto bad_area; | 
 | 276 | 	} else { | 
 | 277 | 		if (!(vma->vm_flags & (VM_READ | VM_EXEC))) | 
 | 278 | 			goto bad_area; | 
 | 279 | 	} | 
 | 280 |  | 
 | 281 | 	/* | 
 | 282 | 	 * If for any reason at all we couldn't handle the fault, | 
 | 283 | 	 * make sure we exit gracefully rather than endlessly redo | 
 | 284 | 	 * the fault. | 
 | 285 | 	 */ | 
 | 286 |  | 
| Nick Piggin | 83c5407 | 2007-07-19 01:47:05 -0700 | [diff] [blame] | 287 | 	fault = handle_mm_fault(mm, vma, address, writeaccess & 1); | 
 | 288 | 	if (unlikely(fault & VM_FAULT_ERROR)) { | 
 | 289 | 		if (fault & VM_FAULT_OOM) | 
 | 290 | 			goto out_of_memory; | 
 | 291 | 		else if (fault & VM_FAULT_SIGBUS) | 
 | 292 | 			goto do_sigbus; | 
 | 293 | 		BUG(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 294 | 	} | 
| Nick Piggin | 83c5407 | 2007-07-19 01:47:05 -0700 | [diff] [blame] | 295 | 	if (fault & VM_FAULT_MAJOR) | 
 | 296 | 		tsk->maj_flt++; | 
 | 297 | 	else | 
 | 298 | 		tsk->min_flt++; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 299 |  | 
 | 300 | 	up_read(&mm->mmap_sem); | 
 | 301 | 	return; | 
 | 302 |  | 
 | 303 | 	/* | 
 | 304 | 	 * Something tried to access memory that isn't in our memory map.. | 
 | 305 | 	 * Fix it, but check if it's kernel or user first.. | 
 | 306 | 	 */ | 
 | 307 |  | 
 | 308 |  bad_area: | 
 | 309 | 	up_read(&mm->mmap_sem); | 
 | 310 |  | 
 | 311 |  bad_area_nosemaphore: | 
 | 312 | 	DPG(show_registers(regs)); | 
 | 313 |  | 
 | 314 | 	/* User mode accesses just cause a SIGSEGV */ | 
 | 315 |  | 
 | 316 | 	if (user_mode(regs)) { | 
 | 317 | 		info.si_signo = SIGSEGV; | 
 | 318 | 		info.si_errno = 0; | 
 | 319 | 		/* info.si_code has been set above */ | 
 | 320 | 		info.si_addr = (void *)address; | 
 | 321 | 		force_sig_info(SIGSEGV, &info, tsk); | 
 | 322 | 		return; | 
 | 323 | 	} | 
 | 324 |  | 
 | 325 |  no_context: | 
 | 326 |  | 
 | 327 | 	/* Are we prepared to handle this kernel fault? | 
 | 328 | 	 * | 
 | 329 | 	 * (The kernel has valid exception-points in the source  | 
 | 330 | 	 *  when it acesses user-memory. When it fails in one | 
 | 331 | 	 *  of those points, we find it in a table and do a jump | 
 | 332 | 	 *  to some fixup code that loads an appropriate error | 
 | 333 | 	 *  code) | 
 | 334 | 	 */ | 
 | 335 |  | 
 | 336 | 	if (find_fixup_code(regs)) | 
 | 337 | 		return; | 
 | 338 |  | 
 | 339 | 	/* | 
 | 340 | 	 * Oops. The kernel tried to access some bad page. We'll have to | 
 | 341 | 	 * terminate things with extreme prejudice. | 
 | 342 | 	 */ | 
 | 343 |  | 
 | 344 | 	if ((unsigned long) (address) < PAGE_SIZE) | 
| Mikael Starvik | 4f18cfb | 2005-07-27 11:44:39 -0700 | [diff] [blame] | 345 | 		raw_printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 346 | 	else | 
| Mikael Starvik | 4f18cfb | 2005-07-27 11:44:39 -0700 | [diff] [blame] | 347 | 		raw_printk(KERN_ALERT "Unable to handle kernel access"); | 
 | 348 | 	raw_printk(" at virtual address %08lx\n",address); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 349 |  | 
 | 350 | 	die_if_kernel("Oops", regs, (writeaccess << 1) | protection); | 
 | 351 |  | 
 | 352 | 	do_exit(SIGKILL); | 
 | 353 |  | 
 | 354 | 	/* | 
 | 355 | 	 * We ran out of memory, or some other thing happened to us that made | 
 | 356 | 	 * us unable to handle the page fault gracefully. | 
 | 357 | 	 */ | 
 | 358 |  | 
 | 359 |  out_of_memory: | 
 | 360 | 	up_read(&mm->mmap_sem); | 
 | 361 | 	printk("VM: killing process %s\n", tsk->comm); | 
 | 362 | 	if (user_mode(regs)) | 
 | 363 | 		do_exit(SIGKILL); | 
 | 364 | 	goto no_context; | 
 | 365 |  | 
 | 366 |  do_sigbus: | 
 | 367 | 	up_read(&mm->mmap_sem); | 
 | 368 |  | 
 | 369 | 	/* | 
 | 370 | 	 * Send a sigbus, regardless of whether we were in kernel | 
 | 371 | 	 * or user mode. | 
 | 372 | 	 */ | 
 | 373 | 	info.si_signo = SIGBUS; | 
 | 374 | 	info.si_errno = 0; | 
 | 375 | 	info.si_code = BUS_ADRERR; | 
 | 376 | 	info.si_addr = (void *)address; | 
 | 377 | 	force_sig_info(SIGBUS, &info, tsk); | 
 | 378 |  | 
 | 379 | 	/* Kernel mode? Handle exceptions or die */ | 
 | 380 | 	if (!user_mode(regs)) | 
 | 381 | 		goto no_context; | 
 | 382 | 	return; | 
 | 383 |  | 
 | 384 | vmalloc_fault: | 
 | 385 | 	{ | 
 | 386 | 		/* | 
 | 387 | 		 * Synchronize this task's top level page-table | 
 | 388 | 		 * with the 'reference' page table. | 
 | 389 | 		 * | 
 | 390 | 		 * Use current_pgd instead of tsk->active_mm->pgd | 
 | 391 | 		 * since the latter might be unavailable if this | 
 | 392 | 		 * code is executed in a misfortunately run irq | 
 | 393 | 		 * (like inside schedule() between switch_mm and | 
 | 394 | 		 *  switch_to...). | 
 | 395 | 		 */ | 
 | 396 |  | 
 | 397 | 		int offset = pgd_index(address); | 
 | 398 | 		pgd_t *pgd, *pgd_k; | 
| Mikael Starvik | 4f18cfb | 2005-07-27 11:44:39 -0700 | [diff] [blame] | 399 | 		pud_t *pud, *pud_k; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 400 | 		pmd_t *pmd, *pmd_k; | 
 | 401 | 		pte_t *pte_k; | 
 | 402 |  | 
| Mikael Starvik | 4f18cfb | 2005-07-27 11:44:39 -0700 | [diff] [blame] | 403 | 		pgd = (pgd_t *)per_cpu(current_pgd, smp_processor_id()) + offset; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 404 | 		pgd_k = init_mm.pgd + offset; | 
 | 405 |  | 
 | 406 | 		/* Since we're two-level, we don't need to do both | 
 | 407 | 		 * set_pgd and set_pmd (they do the same thing). If | 
 | 408 | 		 * we go three-level at some point, do the right thing | 
 | 409 | 		 * with pgd_present and set_pgd here.  | 
 | 410 | 		 *  | 
 | 411 | 		 * Also, since the vmalloc area is global, we don't | 
 | 412 | 		 * need to copy individual PTE's, it is enough to | 
 | 413 | 		 * copy the pgd pointer into the pte page of the | 
 | 414 | 		 * root task. If that is there, we'll find our pte if | 
 | 415 | 		 * it exists. | 
 | 416 | 		 */ | 
 | 417 |  | 
| Mikael Starvik | 4f18cfb | 2005-07-27 11:44:39 -0700 | [diff] [blame] | 418 | 		pud = pud_offset(pgd, address); | 
 | 419 | 		pud_k = pud_offset(pgd_k, address); | 
 | 420 | 		if (!pud_present(*pud_k)) | 
 | 421 | 			goto no_context; | 
 | 422 |  | 
 | 423 | 		pmd = pmd_offset(pud, address); | 
 | 424 | 		pmd_k = pmd_offset(pud_k, address); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 425 |  | 
 | 426 | 		if (!pmd_present(*pmd_k)) | 
 | 427 | 			goto bad_area_nosemaphore; | 
 | 428 |  | 
 | 429 | 		set_pmd(pmd, *pmd_k); | 
 | 430 |  | 
 | 431 | 		/* Make sure the actual PTE exists as well to | 
 | 432 | 		 * catch kernel vmalloc-area accesses to non-mapped | 
 | 433 | 		 * addresses. If we don't do this, this will just | 
 | 434 | 		 * silently loop forever. | 
 | 435 | 		 */ | 
 | 436 |  | 
 | 437 | 		pte_k = pte_offset_kernel(pmd_k, address); | 
 | 438 | 		if (!pte_present(*pte_k)) | 
 | 439 | 			goto no_context; | 
 | 440 |  | 
 | 441 | 		return; | 
 | 442 | 	} | 
 | 443 | } | 
| Mikael Starvik | 4f18cfb | 2005-07-27 11:44:39 -0700 | [diff] [blame] | 444 |  | 
 | 445 | /* Find fixup code. */ | 
 | 446 | int | 
 | 447 | find_fixup_code(struct pt_regs *regs) | 
 | 448 | { | 
 | 449 | 	const struct exception_table_entry *fixup; | 
 | 450 |  | 
 | 451 | 	if ((fixup = search_exception_tables(instruction_pointer(regs))) != 0) { | 
 | 452 | 		/* Adjust the instruction pointer in the stackframe. */ | 
 | 453 | 		instruction_pointer(regs) = fixup->fixup; | 
 | 454 | 		arch_fixup(regs); | 
 | 455 | 		return 1; | 
 | 456 | 	} | 
 | 457 |  | 
 | 458 | 	return 0; | 
 | 459 | } |