Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arm64: MMU fault handling and page table management

This patch adds support for the handling of the MMU faults (exception
entry code introduced by a previous patch) and page table management.

The user translation table is pointed to by TTBR0 and the kernel one
(swapper_pg_dir) by TTBR1. There is no translation information shared or
address space overlapping between user and kernel page tables.

Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Acked-by: Tony Lindgren <tony@atomide.com>
Acked-by: Nicolas Pitre <nico@linaro.org>
Acked-by: Olof Johansson <olof@lixom.net>
Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>

+965
+67
arch/arm64/include/asm/page.h
··· 1 + /* 2 + * Based on arch/arm/include/asm/page.h 3 + * 4 + * Copyright (C) 1995-2003 Russell King 5 + * Copyright (C) 2012 ARM Ltd. 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License version 2 as 9 + * published by the Free Software Foundation. 10 + * 11 + * This program is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + #ifndef __ASM_PAGE_H 20 + #define __ASM_PAGE_H 21 + 22 + /* PAGE_SHIFT determines the page size */ 23 + #ifdef CONFIG_ARM64_64K_PAGES 24 + #define PAGE_SHIFT 16 25 + #else 26 + #define PAGE_SHIFT 12 27 + #endif 28 + #define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT) 29 + #define PAGE_MASK (~(PAGE_SIZE-1)) 30 + 31 + /* We do define AT_SYSINFO_EHDR but don't use the gate mechanism */ 32 + #define __HAVE_ARCH_GATE_AREA 1 33 + 34 + #ifndef __ASSEMBLY__ 35 + 36 + #ifdef CONFIG_ARM64_64K_PAGES 37 + #include <asm/pgtable-2level-types.h> 38 + #else 39 + #include <asm/pgtable-3level-types.h> 40 + #endif 41 + 42 + extern void __cpu_clear_user_page(void *p, unsigned long user); 43 + extern void __cpu_copy_user_page(void *to, const void *from, 44 + unsigned long user); 45 + extern void copy_page(void *to, const void *from); 46 + extern void clear_page(void *to); 47 + 48 + #define clear_user_page(addr,vaddr,pg) __cpu_clear_user_page(addr, vaddr) 49 + #define copy_user_page(to,from,vaddr,pg) __cpu_copy_user_page(to, from, vaddr) 50 + 51 + typedef struct page *pgtable_t; 52 + 53 + #ifdef CONFIG_HAVE_ARCH_PFN_VALID 54 + extern int pfn_valid(unsigned long); 55 + #endif 56 + 57 + #include <asm/memory.h> 58 + 59 + #endif /* !__ASSEMBLY__ */ 60 + 61 + #define VM_DATA_DEFAULT_FLAGS \ 62 + (((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0) | \ 63 + VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) 64 + 65 + #include <asm-generic/getorder.h> 66 + 67 + #endif
+113
arch/arm64/include/asm/pgalloc.h
··· 1 + /* 2 + * Based on arch/arm/include/asm/pgalloc.h 3 + * 4 + * Copyright (C) 2000-2001 Russell King 5 + * Copyright (C) 2012 ARM Ltd. 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License version 2 as 9 + * published by the Free Software Foundation. 10 + * 11 + * This program is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + #ifndef __ASM_PGALLOC_H 20 + #define __ASM_PGALLOC_H 21 + 22 + #include <asm/pgtable-hwdef.h> 23 + #include <asm/processor.h> 24 + #include <asm/cacheflush.h> 25 + #include <asm/tlbflush.h> 26 + 27 + #define check_pgt_cache() do { } while (0) 28 + 29 + #ifndef CONFIG_ARM64_64K_PAGES 30 + 31 + static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr) 32 + { 33 + return (pmd_t *)get_zeroed_page(GFP_KERNEL | __GFP_REPEAT); 34 + } 35 + 36 + static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd) 37 + { 38 + BUG_ON((unsigned long)pmd & (PAGE_SIZE-1)); 39 + free_page((unsigned long)pmd); 40 + } 41 + 42 + static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd) 43 + { 44 + set_pud(pud, __pud(__pa(pmd) | PMD_TYPE_TABLE)); 45 + } 46 + 47 + #endif /* CONFIG_ARM64_64K_PAGES */ 48 + 49 + extern pgd_t *pgd_alloc(struct mm_struct *mm); 50 + extern void pgd_free(struct mm_struct *mm, pgd_t *pgd); 51 + 52 + #define PGALLOC_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT | __GFP_ZERO) 53 + 54 + static inline pte_t * 55 + pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr) 56 + { 57 + return (pte_t *)__get_free_page(PGALLOC_GFP); 58 + } 59 + 60 + static inline pgtable_t 61 + pte_alloc_one(struct mm_struct *mm, unsigned long addr) 62 + { 63 + struct page *pte; 64 + 65 + pte = alloc_pages(PGALLOC_GFP, 0); 66 + if (pte) 67 + pgtable_page_ctor(pte); 68 + 69 + return pte; 70 + } 71 + 72 + /* 73 + * Free a PTE table. 74 + */ 75 + static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte) 76 + { 77 + if (pte) 78 + free_page((unsigned long)pte); 79 + } 80 + 81 + static inline void pte_free(struct mm_struct *mm, pgtable_t pte) 82 + { 83 + pgtable_page_dtor(pte); 84 + __free_page(pte); 85 + } 86 + 87 + static inline void __pmd_populate(pmd_t *pmdp, phys_addr_t pte, 88 + pmdval_t prot) 89 + { 90 + set_pmd(pmdp, __pmd(pte | prot)); 91 + } 92 + 93 + /* 94 + * Populate the pmdp entry with a pointer to the pte. This pmd is part 95 + * of the mm address space. 96 + */ 97 + static inline void 98 + pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmdp, pte_t *ptep) 99 + { 100 + /* 101 + * The pmd must be loaded with the physical address of the PTE table 102 + */ 103 + __pmd_populate(pmdp, __pa(ptep), PMD_TYPE_TABLE); 104 + } 105 + 106 + static inline void 107 + pmd_populate(struct mm_struct *mm, pmd_t *pmdp, pgtable_t ptep) 108 + { 109 + __pmd_populate(pmdp, page_to_phys(ptep), PMD_TYPE_TABLE); 110 + } 111 + #define pmd_pgtable(pmd) pmd_page(pmd) 112 + 113 + #endif
+34
arch/arm64/mm/copypage.c
··· 1 + /* 2 + * Based on arch/arm/mm/copypage.c 3 + * 4 + * Copyright (C) 2002 Deep Blue Solutions Ltd, All Rights Reserved. 5 + * Copyright (C) 2012 ARM Ltd. 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License version 2 as 9 + * published by the Free Software Foundation. 10 + * 11 + * This program is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #include <linux/mm.h> 21 + 22 + #include <asm/page.h> 23 + #include <asm/cacheflush.h> 24 + 25 + void __cpu_copy_user_page(void *kto, const void *kfrom, unsigned long vaddr) 26 + { 27 + copy_page(kto, kfrom); 28 + __flush_dcache_area(kto, PAGE_SIZE); 29 + } 30 + 31 + void __cpu_clear_user_page(void *kaddr, unsigned long vaddr) 32 + { 33 + clear_page(kaddr); 34 + }
+17
arch/arm64/mm/extable.c
··· 1 + /* 2 + * Based on arch/arm/mm/extable.c 3 + */ 4 + 5 + #include <linux/module.h> 6 + #include <linux/uaccess.h> 7 + 8 + int fixup_exception(struct pt_regs *regs) 9 + { 10 + const struct exception_table_entry *fixup; 11 + 12 + fixup = search_exception_tables(instruction_pointer(regs)); 13 + if (fixup) 14 + regs->pc = fixup->fixup; 15 + 16 + return fixup != NULL; 17 + }
+534
arch/arm64/mm/fault.c
··· 1 + /* 2 + * Based on arch/arm/mm/fault.c 3 + * 4 + * Copyright (C) 1995 Linus Torvalds 5 + * Copyright (C) 1995-2004 Russell King 6 + * Copyright (C) 2012 ARM Ltd. 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License version 2 as 10 + * published by the Free Software Foundation. 11 + * 12 + * This program is distributed in the hope that it will be useful, 13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 + * GNU General Public License for more details. 16 + * 17 + * You should have received a copy of the GNU General Public License 18 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 19 + */ 20 + 21 + #include <linux/module.h> 22 + #include <linux/signal.h> 23 + #include <linux/mm.h> 24 + #include <linux/hardirq.h> 25 + #include <linux/init.h> 26 + #include <linux/kprobes.h> 27 + #include <linux/uaccess.h> 28 + #include <linux/page-flags.h> 29 + #include <linux/sched.h> 30 + #include <linux/highmem.h> 31 + #include <linux/perf_event.h> 32 + 33 + #include <asm/exception.h> 34 + #include <asm/debug-monitors.h> 35 + #include <asm/system_misc.h> 36 + #include <asm/pgtable.h> 37 + #include <asm/tlbflush.h> 38 + 39 + /* 40 + * Dump out the page tables associated with 'addr' in mm 'mm'. 41 + */ 42 + void show_pte(struct mm_struct *mm, unsigned long addr) 43 + { 44 + pgd_t *pgd; 45 + 46 + if (!mm) 47 + mm = &init_mm; 48 + 49 + pr_alert("pgd = %p\n", mm->pgd); 50 + pgd = pgd_offset(mm, addr); 51 + pr_alert("[%08lx] *pgd=%016llx", addr, pgd_val(*pgd)); 52 + 53 + do { 54 + pud_t *pud; 55 + pmd_t *pmd; 56 + pte_t *pte; 57 + 58 + if (pgd_none_or_clear_bad(pgd)) 59 + break; 60 + 61 + pud = pud_offset(pgd, addr); 62 + if (pud_none_or_clear_bad(pud)) 63 + break; 64 + 65 + pmd = pmd_offset(pud, addr); 66 + printk(", *pmd=%016llx", pmd_val(*pmd)); 67 + if (pmd_none_or_clear_bad(pmd)) 68 + break; 69 + 70 + pte = pte_offset_map(pmd, addr); 71 + printk(", *pte=%016llx", pte_val(*pte)); 72 + pte_unmap(pte); 73 + } while(0); 74 + 75 + printk("\n"); 76 + } 77 + 78 + /* 79 + * The kernel tried to access some page that wasn't present. 80 + */ 81 + static void __do_kernel_fault(struct mm_struct *mm, unsigned long addr, 82 + unsigned int esr, struct pt_regs *regs) 83 + { 84 + /* 85 + * Are we prepared to handle this kernel fault? 86 + */ 87 + if (fixup_exception(regs)) 88 + return; 89 + 90 + /* 91 + * No handler, we'll have to terminate things with extreme prejudice. 92 + */ 93 + bust_spinlocks(1); 94 + pr_alert("Unable to handle kernel %s at virtual address %08lx\n", 95 + (addr < PAGE_SIZE) ? "NULL pointer dereference" : 96 + "paging request", addr); 97 + 98 + show_pte(mm, addr); 99 + die("Oops", regs, esr); 100 + bust_spinlocks(0); 101 + do_exit(SIGKILL); 102 + } 103 + 104 + /* 105 + * Something tried to access memory that isn't in our memory map. User mode 106 + * accesses just cause a SIGSEGV 107 + */ 108 + static void __do_user_fault(struct task_struct *tsk, unsigned long addr, 109 + unsigned int esr, unsigned int sig, int code, 110 + struct pt_regs *regs) 111 + { 112 + struct siginfo si; 113 + 114 + if (show_unhandled_signals) { 115 + pr_info("%s[%d]: unhandled page fault (%d) at 0x%08lx, code 0x%03x\n", 116 + tsk->comm, task_pid_nr(tsk), sig, addr, esr); 117 + show_pte(tsk->mm, addr); 118 + show_regs(regs); 119 + } 120 + 121 + tsk->thread.fault_address = addr; 122 + si.si_signo = sig; 123 + si.si_errno = 0; 124 + si.si_code = code; 125 + si.si_addr = (void __user *)addr; 126 + force_sig_info(sig, &si, tsk); 127 + } 128 + 129 + void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *regs) 130 + { 131 + struct task_struct *tsk = current; 132 + struct mm_struct *mm = tsk->active_mm; 133 + 134 + /* 135 + * If we are in kernel mode at this point, we have no context to 136 + * handle this fault with. 137 + */ 138 + if (user_mode(regs)) 139 + __do_user_fault(tsk, addr, esr, SIGSEGV, SEGV_MAPERR, regs); 140 + else 141 + __do_kernel_fault(mm, addr, esr, regs); 142 + } 143 + 144 + #define VM_FAULT_BADMAP 0x010000 145 + #define VM_FAULT_BADACCESS 0x020000 146 + 147 + #define ESR_WRITE (1 << 6) 148 + #define ESR_LNX_EXEC (1 << 24) 149 + 150 + /* 151 + * Check that the permissions on the VMA allow for the fault which occurred. 152 + * If we encountered a write fault, we must have write permission, otherwise 153 + * we allow any permission. 154 + */ 155 + static inline bool access_error(unsigned int esr, struct vm_area_struct *vma) 156 + { 157 + unsigned int mask = VM_READ | VM_WRITE | VM_EXEC; 158 + 159 + if (esr & ESR_WRITE) 160 + mask = VM_WRITE; 161 + if (esr & ESR_LNX_EXEC) 162 + mask = VM_EXEC; 163 + 164 + return vma->vm_flags & mask ? false : true; 165 + } 166 + 167 + static int __do_page_fault(struct mm_struct *mm, unsigned long addr, 168 + unsigned int esr, unsigned int flags, 169 + struct task_struct *tsk) 170 + { 171 + struct vm_area_struct *vma; 172 + int fault; 173 + 174 + vma = find_vma(mm, addr); 175 + fault = VM_FAULT_BADMAP; 176 + if (unlikely(!vma)) 177 + goto out; 178 + if (unlikely(vma->vm_start > addr)) 179 + goto check_stack; 180 + 181 + /* 182 + * Ok, we have a good vm_area for this memory access, so we can handle 183 + * it. 184 + */ 185 + good_area: 186 + if (access_error(esr, vma)) { 187 + fault = VM_FAULT_BADACCESS; 188 + goto out; 189 + } 190 + 191 + return handle_mm_fault(mm, vma, addr & PAGE_MASK, flags); 192 + 193 + check_stack: 194 + if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr)) 195 + goto good_area; 196 + out: 197 + return fault; 198 + } 199 + 200 + static int __kprobes do_page_fault(unsigned long addr, unsigned int esr, 201 + struct pt_regs *regs) 202 + { 203 + struct task_struct *tsk; 204 + struct mm_struct *mm; 205 + int fault, sig, code; 206 + int write = esr & ESR_WRITE; 207 + unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE | 208 + (write ? FAULT_FLAG_WRITE : 0); 209 + 210 + tsk = current; 211 + mm = tsk->mm; 212 + 213 + /* Enable interrupts if they were enabled in the parent context. */ 214 + if (interrupts_enabled(regs)) 215 + local_irq_enable(); 216 + 217 + /* 218 + * If we're in an interrupt or have no user context, we must not take 219 + * the fault. 220 + */ 221 + if (in_atomic() || !mm) 222 + goto no_context; 223 + 224 + /* 225 + * As per x86, we may deadlock here. However, since the kernel only 226 + * validly references user space from well defined areas of the code, 227 + * we can bug out early if this is from code which shouldn't. 228 + */ 229 + if (!down_read_trylock(&mm->mmap_sem)) { 230 + if (!user_mode(regs) && !search_exception_tables(regs->pc)) 231 + goto no_context; 232 + retry: 233 + down_read(&mm->mmap_sem); 234 + } else { 235 + /* 236 + * The above down_read_trylock() might have succeeded in which 237 + * case, we'll have missed the might_sleep() from down_read(). 238 + */ 239 + might_sleep(); 240 + #ifdef CONFIG_DEBUG_VM 241 + if (!user_mode(regs) && !search_exception_tables(regs->pc)) 242 + goto no_context; 243 + #endif 244 + } 245 + 246 + fault = __do_page_fault(mm, addr, esr, flags, tsk); 247 + 248 + /* 249 + * If we need to retry but a fatal signal is pending, handle the 250 + * signal first. We do not need to release the mmap_sem because it 251 + * would already be released in __lock_page_or_retry in mm/filemap.c. 252 + */ 253 + if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) 254 + return 0; 255 + 256 + /* 257 + * Major/minor page fault accounting is only done on the initial 258 + * attempt. If we go through a retry, it is extremely likely that the 259 + * page will be found in page cache at that point. 260 + */ 261 + 262 + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr); 263 + if (flags & FAULT_FLAG_ALLOW_RETRY) { 264 + if (fault & VM_FAULT_MAJOR) { 265 + tsk->maj_flt++; 266 + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, regs, 267 + addr); 268 + } else { 269 + tsk->min_flt++; 270 + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, regs, 271 + addr); 272 + } 273 + if (fault & VM_FAULT_RETRY) { 274 + /* 275 + * Clear FAULT_FLAG_ALLOW_RETRY to avoid any risk of 276 + * starvation. 277 + */ 278 + flags &= ~FAULT_FLAG_ALLOW_RETRY; 279 + goto retry; 280 + } 281 + } 282 + 283 + up_read(&mm->mmap_sem); 284 + 285 + /* 286 + * Handle the "normal" case first - VM_FAULT_MAJOR / VM_FAULT_MINOR 287 + */ 288 + if (likely(!(fault & (VM_FAULT_ERROR | VM_FAULT_BADMAP | 289 + VM_FAULT_BADACCESS)))) 290 + return 0; 291 + 292 + if (fault & VM_FAULT_OOM) { 293 + /* 294 + * We ran out of memory, call the OOM killer, and return to 295 + * userspace (which will retry the fault, or kill us if we got 296 + * oom-killed). 297 + */ 298 + pagefault_out_of_memory(); 299 + return 0; 300 + } 301 + 302 + /* 303 + * If we are in kernel mode at this point, we have no context to 304 + * handle this fault with. 305 + */ 306 + if (!user_mode(regs)) 307 + goto no_context; 308 + 309 + if (fault & VM_FAULT_SIGBUS) { 310 + /* 311 + * We had some memory, but were unable to successfully fix up 312 + * this page fault. 313 + */ 314 + sig = SIGBUS; 315 + code = BUS_ADRERR; 316 + } else { 317 + /* 318 + * Something tried to access memory that isn't in our memory 319 + * map. 320 + */ 321 + sig = SIGSEGV; 322 + code = fault == VM_FAULT_BADACCESS ? 323 + SEGV_ACCERR : SEGV_MAPERR; 324 + } 325 + 326 + __do_user_fault(tsk, addr, esr, sig, code, regs); 327 + return 0; 328 + 329 + no_context: 330 + __do_kernel_fault(mm, addr, esr, regs); 331 + return 0; 332 + } 333 + 334 + /* 335 + * First Level Translation Fault Handler 336 + * 337 + * We enter here because the first level page table doesn't contain a valid 338 + * entry for the address. 339 + * 340 + * If the address is in kernel space (>= TASK_SIZE), then we are probably 341 + * faulting in the vmalloc() area. 342 + * 343 + * If the init_task's first level page tables contains the relevant entry, we 344 + * copy the it to this task. If not, we send the process a signal, fixup the 345 + * exception, or oops the kernel. 346 + * 347 + * NOTE! We MUST NOT take any locks for this case. We may be in an interrupt 348 + * or a critical region, and should only copy the information from the master 349 + * page table, nothing more. 350 + */ 351 + static int __kprobes do_translation_fault(unsigned long addr, 352 + unsigned int esr, 353 + struct pt_regs *regs) 354 + { 355 + if (addr < TASK_SIZE) 356 + return do_page_fault(addr, esr, regs); 357 + 358 + do_bad_area(addr, esr, regs); 359 + return 0; 360 + } 361 + 362 + /* 363 + * Some section permission faults need to be handled gracefully. They can 364 + * happen due to a __{get,put}_user during an oops. 365 + */ 366 + static int do_sect_fault(unsigned long addr, unsigned int esr, 367 + struct pt_regs *regs) 368 + { 369 + do_bad_area(addr, esr, regs); 370 + return 0; 371 + } 372 + 373 + /* 374 + * This abort handler always returns "fault". 375 + */ 376 + static int do_bad(unsigned long addr, unsigned int esr, struct pt_regs *regs) 377 + { 378 + return 1; 379 + } 380 + 381 + static struct fault_info { 382 + int (*fn)(unsigned long addr, unsigned int esr, struct pt_regs *regs); 383 + int sig; 384 + int code; 385 + const char *name; 386 + } fault_info[] = { 387 + { do_bad, SIGBUS, 0, "ttbr address size fault" }, 388 + { do_bad, SIGBUS, 0, "level 1 address size fault" }, 389 + { do_bad, SIGBUS, 0, "level 2 address size fault" }, 390 + { do_bad, SIGBUS, 0, "level 3 address size fault" }, 391 + { do_translation_fault, SIGSEGV, SEGV_MAPERR, "input address range fault" }, 392 + { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 1 translation fault" }, 393 + { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 2 translation fault" }, 394 + { do_page_fault, SIGSEGV, SEGV_MAPERR, "level 3 translation fault" }, 395 + { do_bad, SIGBUS, 0, "reserved access flag fault" }, 396 + { do_bad, SIGSEGV, SEGV_ACCERR, "level 1 access flag fault" }, 397 + { do_bad, SIGSEGV, SEGV_ACCERR, "level 2 access flag fault" }, 398 + { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 access flag fault" }, 399 + { do_bad, SIGBUS, 0, "reserved permission fault" }, 400 + { do_bad, SIGSEGV, SEGV_ACCERR, "level 1 permission fault" }, 401 + { do_sect_fault, SIGSEGV, SEGV_ACCERR, "level 2 permission fault" }, 402 + { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 permission fault" }, 403 + { do_bad, SIGBUS, 0, "synchronous external abort" }, 404 + { do_bad, SIGBUS, 0, "asynchronous external abort" }, 405 + { do_bad, SIGBUS, 0, "unknown 18" }, 406 + { do_bad, SIGBUS, 0, "unknown 19" }, 407 + { do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" }, 408 + { do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" }, 409 + { do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" }, 410 + { do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" }, 411 + { do_bad, SIGBUS, 0, "synchronous parity error" }, 412 + { do_bad, SIGBUS, 0, "asynchronous parity error" }, 413 + { do_bad, SIGBUS, 0, "unknown 26" }, 414 + { do_bad, SIGBUS, 0, "unknown 27" }, 415 + { do_bad, SIGBUS, 0, "synchronous parity error (translation table walk" }, 416 + { do_bad, SIGBUS, 0, "synchronous parity error (translation table walk" }, 417 + { do_bad, SIGBUS, 0, "synchronous parity error (translation table walk" }, 418 + { do_bad, SIGBUS, 0, "synchronous parity error (translation table walk" }, 419 + { do_bad, SIGBUS, 0, "unknown 32" }, 420 + { do_bad, SIGBUS, BUS_ADRALN, "alignment fault" }, 421 + { do_bad, SIGBUS, 0, "debug event" }, 422 + { do_bad, SIGBUS, 0, "unknown 35" }, 423 + { do_bad, SIGBUS, 0, "unknown 36" }, 424 + { do_bad, SIGBUS, 0, "unknown 37" }, 425 + { do_bad, SIGBUS, 0, "unknown 38" }, 426 + { do_bad, SIGBUS, 0, "unknown 39" }, 427 + { do_bad, SIGBUS, 0, "unknown 40" }, 428 + { do_bad, SIGBUS, 0, "unknown 41" }, 429 + { do_bad, SIGBUS, 0, "unknown 42" }, 430 + { do_bad, SIGBUS, 0, "unknown 43" }, 431 + { do_bad, SIGBUS, 0, "unknown 44" }, 432 + { do_bad, SIGBUS, 0, "unknown 45" }, 433 + { do_bad, SIGBUS, 0, "unknown 46" }, 434 + { do_bad, SIGBUS, 0, "unknown 47" }, 435 + { do_bad, SIGBUS, 0, "unknown 48" }, 436 + { do_bad, SIGBUS, 0, "unknown 49" }, 437 + { do_bad, SIGBUS, 0, "unknown 50" }, 438 + { do_bad, SIGBUS, 0, "unknown 51" }, 439 + { do_bad, SIGBUS, 0, "implementation fault (lockdown abort)" }, 440 + { do_bad, SIGBUS, 0, "unknown 53" }, 441 + { do_bad, SIGBUS, 0, "unknown 54" }, 442 + { do_bad, SIGBUS, 0, "unknown 55" }, 443 + { do_bad, SIGBUS, 0, "unknown 56" }, 444 + { do_bad, SIGBUS, 0, "unknown 57" }, 445 + { do_bad, SIGBUS, 0, "implementation fault (coprocessor abort)" }, 446 + { do_bad, SIGBUS, 0, "unknown 59" }, 447 + { do_bad, SIGBUS, 0, "unknown 60" }, 448 + { do_bad, SIGBUS, 0, "unknown 61" }, 449 + { do_bad, SIGBUS, 0, "unknown 62" }, 450 + { do_bad, SIGBUS, 0, "unknown 63" }, 451 + }; 452 + 453 + /* 454 + * Dispatch a data abort to the relevant handler. 455 + */ 456 + asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr, 457 + struct pt_regs *regs) 458 + { 459 + const struct fault_info *inf = fault_info + (esr & 63); 460 + struct siginfo info; 461 + 462 + if (!inf->fn(addr, esr, regs)) 463 + return; 464 + 465 + pr_alert("Unhandled fault: %s (0x%08x) at 0x%016lx\n", 466 + inf->name, esr, addr); 467 + 468 + info.si_signo = inf->sig; 469 + info.si_errno = 0; 470 + info.si_code = inf->code; 471 + info.si_addr = (void __user *)addr; 472 + arm64_notify_die("", regs, &info, esr); 473 + } 474 + 475 + /* 476 + * Handle stack alignment exceptions. 477 + */ 478 + asmlinkage void __exception do_sp_pc_abort(unsigned long addr, 479 + unsigned int esr, 480 + struct pt_regs *regs) 481 + { 482 + struct siginfo info; 483 + 484 + info.si_signo = SIGBUS; 485 + info.si_errno = 0; 486 + info.si_code = BUS_ADRALN; 487 + info.si_addr = (void __user *)addr; 488 + arm64_notify_die("", regs, &info, esr); 489 + } 490 + 491 + static struct fault_info debug_fault_info[] = { 492 + { do_bad, SIGTRAP, TRAP_HWBKPT, "hardware breakpoint" }, 493 + { do_bad, SIGTRAP, TRAP_HWBKPT, "hardware single-step" }, 494 + { do_bad, SIGTRAP, TRAP_HWBKPT, "hardware watchpoint" }, 495 + { do_bad, SIGBUS, 0, "unknown 3" }, 496 + { do_bad, SIGTRAP, TRAP_BRKPT, "aarch32 BKPT" }, 497 + { do_bad, SIGTRAP, 0, "aarch32 vector catch" }, 498 + { do_bad, SIGTRAP, TRAP_BRKPT, "aarch64 BRK" }, 499 + { do_bad, SIGBUS, 0, "unknown 7" }, 500 + }; 501 + 502 + void __init hook_debug_fault_code(int nr, 503 + int (*fn)(unsigned long, unsigned int, struct pt_regs *), 504 + int sig, int code, const char *name) 505 + { 506 + BUG_ON(nr < 0 || nr >= ARRAY_SIZE(debug_fault_info)); 507 + 508 + debug_fault_info[nr].fn = fn; 509 + debug_fault_info[nr].sig = sig; 510 + debug_fault_info[nr].code = code; 511 + debug_fault_info[nr].name = name; 512 + } 513 + 514 + asmlinkage int __exception do_debug_exception(unsigned long addr, 515 + unsigned int esr, 516 + struct pt_regs *regs) 517 + { 518 + const struct fault_info *inf = debug_fault_info + DBG_ESR_EVT(esr); 519 + struct siginfo info; 520 + 521 + if (!inf->fn(addr, esr, regs)) 522 + return 1; 523 + 524 + pr_alert("Unhandled debug exception: %s (0x%08x) at 0x%016lx\n", 525 + inf->name, esr, addr); 526 + 527 + info.si_signo = inf->sig; 528 + info.si_errno = 0; 529 + info.si_code = inf->code; 530 + info.si_addr = (void __user *)addr; 531 + arm64_notify_die("", regs, &info, esr); 532 + 533 + return 0; 534 + }
+2
arch/arm64/mm/mm.h
··· 1 + extern void __flush_dcache_page(struct page *page); 2 + extern void __init bootmem_init(void);
+144
arch/arm64/mm/mmap.c
··· 1 + /* 2 + * Based on arch/arm/mm/mmap.c 3 + * 4 + * Copyright (C) 2012 ARM Ltd. 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + * 10 + * This program is distributed in the hope that it will be useful, 11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 + * GNU General Public License for more details. 14 + * 15 + * You should have received a copy of the GNU General Public License 16 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 + */ 18 + 19 + #include <linux/elf.h> 20 + #include <linux/fs.h> 21 + #include <linux/mm.h> 22 + #include <linux/mman.h> 23 + #include <linux/export.h> 24 + #include <linux/shm.h> 25 + #include <linux/sched.h> 26 + #include <linux/io.h> 27 + #include <linux/personality.h> 28 + #include <linux/random.h> 29 + 30 + #include <asm/cputype.h> 31 + 32 + /* 33 + * Leave enough space between the mmap area and the stack to honour ulimit in 34 + * the face of randomisation. 35 + */ 36 + #define MIN_GAP (SZ_128M + ((STACK_RND_MASK << PAGE_SHIFT) + 1)) 37 + #define MAX_GAP (STACK_TOP/6*5) 38 + 39 + static int mmap_is_legacy(void) 40 + { 41 + if (current->personality & ADDR_COMPAT_LAYOUT) 42 + return 1; 43 + 44 + if (rlimit(RLIMIT_STACK) == RLIM_INFINITY) 45 + return 1; 46 + 47 + return sysctl_legacy_va_layout; 48 + } 49 + 50 + /* 51 + * Since get_random_int() returns the same value within a 1 jiffy window, we 52 + * will almost always get the same randomisation for the stack and mmap 53 + * region. This will mean the relative distance between stack and mmap will be 54 + * the same. 55 + * 56 + * To avoid this we can shift the randomness by 1 bit. 57 + */ 58 + static unsigned long mmap_rnd(void) 59 + { 60 + unsigned long rnd = 0; 61 + 62 + if (current->flags & PF_RANDOMIZE) 63 + rnd = (long)get_random_int() & (STACK_RND_MASK >> 1); 64 + 65 + return rnd << (PAGE_SHIFT + 1); 66 + } 67 + 68 + static unsigned long mmap_base(void) 69 + { 70 + unsigned long gap = rlimit(RLIMIT_STACK); 71 + 72 + if (gap < MIN_GAP) 73 + gap = MIN_GAP; 74 + else if (gap > MAX_GAP) 75 + gap = MAX_GAP; 76 + 77 + return PAGE_ALIGN(STACK_TOP - gap - mmap_rnd()); 78 + } 79 + 80 + /* 81 + * This function, called very early during the creation of a new process VM 82 + * image, sets up which VM layout function to use: 83 + */ 84 + void arch_pick_mmap_layout(struct mm_struct *mm) 85 + { 86 + /* 87 + * Fall back to the standard layout if the personality bit is set, or 88 + * if the expected stack growth is unlimited: 89 + */ 90 + if (mmap_is_legacy()) { 91 + mm->mmap_base = TASK_UNMAPPED_BASE; 92 + mm->get_unmapped_area = arch_get_unmapped_area; 93 + mm->unmap_area = arch_unmap_area; 94 + } else { 95 + mm->mmap_base = mmap_base(); 96 + mm->get_unmapped_area = arch_get_unmapped_area_topdown; 97 + mm->unmap_area = arch_unmap_area_topdown; 98 + } 99 + } 100 + EXPORT_SYMBOL_GPL(arch_pick_mmap_layout); 101 + 102 + 103 + /* 104 + * You really shouldn't be using read() or write() on /dev/mem. This might go 105 + * away in the future. 106 + */ 107 + int valid_phys_addr_range(unsigned long addr, size_t size) 108 + { 109 + if (addr < PHYS_OFFSET) 110 + return 0; 111 + if (addr + size > __pa(high_memory - 1) + 1) 112 + return 0; 113 + 114 + return 1; 115 + } 116 + 117 + /* 118 + * Do not allow /dev/mem mappings beyond the supported physical range. 119 + */ 120 + int valid_mmap_phys_addr_range(unsigned long pfn, size_t size) 121 + { 122 + return !(((pfn << PAGE_SHIFT) + size) & ~PHYS_MASK); 123 + } 124 + 125 + #ifdef CONFIG_STRICT_DEVMEM 126 + 127 + #include <linux/ioport.h> 128 + 129 + /* 130 + * devmem_is_allowed() checks to see if /dev/mem access to a certain address 131 + * is valid. The argument is a physical page number. We mimic x86 here by 132 + * disallowing access to system RAM as well as device-exclusive MMIO regions. 133 + * This effectively disable read()/write() on /dev/mem. 134 + */ 135 + int devmem_is_allowed(unsigned long pfn) 136 + { 137 + if (iomem_is_exclusive(pfn << PAGE_SHIFT)) 138 + return 0; 139 + if (!page_is_ram(pfn)) 140 + return 1; 141 + return 0; 142 + } 143 + 144 + #endif
+54
arch/arm64/mm/pgd.c
··· 1 + /* 2 + * PGD allocation/freeing 3 + * 4 + * Copyright (C) 2012 ARM Ltd. 5 + * Author: Catalin Marinas <catalin.marinas@arm.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License version 2 as 9 + * published by the Free Software Foundation. 10 + * 11 + * This program is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #include <linux/mm.h> 21 + #include <linux/gfp.h> 22 + #include <linux/highmem.h> 23 + #include <linux/slab.h> 24 + 25 + #include <asm/pgalloc.h> 26 + #include <asm/page.h> 27 + #include <asm/tlbflush.h> 28 + 29 + #include "mm.h" 30 + 31 + #define PGD_SIZE (PTRS_PER_PGD * sizeof(pgd_t)) 32 + 33 + pgd_t *pgd_alloc(struct mm_struct *mm) 34 + { 35 + pgd_t *new_pgd; 36 + 37 + if (PGD_SIZE == PAGE_SIZE) 38 + new_pgd = (pgd_t *)get_zeroed_page(GFP_KERNEL); 39 + else 40 + new_pgd = kzalloc(PGD_SIZE, GFP_KERNEL); 41 + 42 + if (!new_pgd) 43 + return NULL; 44 + 45 + return new_pgd; 46 + } 47 + 48 + void pgd_free(struct mm_struct *mm, pgd_t *pgd) 49 + { 50 + if (PGD_SIZE == PAGE_SIZE) 51 + free_page((unsigned long)pgd); 52 + else 53 + kfree(pgd); 54 + }