aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2012-05-14 01:57:28 -0400
committerPaul Mundt <lethal@linux-sh.org>2012-05-14 01:57:28 -0400
commit5a1dc78a38bfb04159a08cd493e5b3d844939e6c (patch)
tree860420d3d52e2666449d1e688c399876a5c16bd3
parentf007688a50cf5724049a4a5f17023fcdb0966b54 (diff)
sh: Support thread fault code encoding.
This provides a simple interface modelled after sparc64/m32r to encode the error code in the upper byte of thread_info for finer-grained handling in the page fault path. Signed-off-by: Paul Mundt <lethal@linux-sh.org>
-rw-r--r--arch/sh/include/asm/thread_info.h46
-rw-r--r--arch/sh/kernel/cpu/sh3/entry.S11
-rw-r--r--arch/sh/mm/fault_32.c68
3 files changed, 78 insertions, 47 deletions
diff --git a/arch/sh/include/asm/thread_info.h b/arch/sh/include/asm/thread_info.h
index 20ee40af16e9..25a13e534ffe 100644
--- a/arch/sh/include/asm/thread_info.h
+++ b/arch/sh/include/asm/thread_info.h
@@ -10,8 +10,18 @@
10 * - Incorporating suggestions made by Linus Torvalds and Dave Miller 10 * - Incorporating suggestions made by Linus Torvalds and Dave Miller
11 */ 11 */
12#ifdef __KERNEL__ 12#ifdef __KERNEL__
13
13#include <asm/page.h> 14#include <asm/page.h>
14 15
16/*
17 * Page fault error code bits
18 */
19#define FAULT_CODE_WRITE (1 << 0) /* write access */
20#define FAULT_CODE_INITIAL (1 << 1) /* initial page write */
21#define FAULT_CODE_ITLB (1 << 2) /* ITLB miss */
22#define FAULT_CODE_PROT (1 << 3) /* protection fault */
23#define FAULT_CODE_USER (1 << 4) /* user-mode access */
24
15#ifndef __ASSEMBLY__ 25#ifndef __ASSEMBLY__
16#include <asm/processor.h> 26#include <asm/processor.h>
17 27
@@ -107,10 +117,13 @@ extern void init_thread_xstate(void);
107#endif /* __ASSEMBLY__ */ 117#endif /* __ASSEMBLY__ */
108 118
109/* 119/*
110 * thread information flags 120 * Thread information flags
111 * - these are process state flags that various assembly files may need to access 121 *
112 * - pending work-to-be-done flags are in LSW 122 * - Limited to 24 bits, upper byte used for fault code encoding.
113 * - other flags in MSW 123 *
124 * - _TIF_ALLWORK_MASK and _TIF_WORK_MASK need to fit within 2 bytes, or
125 * we blow the tst immediate size constraints and need to fix up
126 * arch/sh/kernel/entry-common.S.
114 */ 127 */
115#define TIF_SYSCALL_TRACE 0 /* syscall trace active */ 128#define TIF_SYSCALL_TRACE 0 /* syscall trace active */
116#define TIF_SIGPENDING 1 /* signal pending */ 129#define TIF_SIGPENDING 1 /* signal pending */
@@ -133,12 +146,6 @@ extern void init_thread_xstate(void);
133#define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) 146#define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT)
134#define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) 147#define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG)
135 148
136/*
137 * _TIF_ALLWORK_MASK and _TIF_WORK_MASK need to fit within 2 bytes, or we
138 * blow the tst immediate size constraints and need to fix up
139 * arch/sh/kernel/entry-common.S.
140 */
141
142/* work to do in syscall trace */ 149/* work to do in syscall trace */
143#define _TIF_WORK_SYSCALL_MASK (_TIF_SYSCALL_TRACE | _TIF_SINGLESTEP | \ 150#define _TIF_WORK_SYSCALL_MASK (_TIF_SYSCALL_TRACE | _TIF_SINGLESTEP | \
144 _TIF_SYSCALL_AUDIT | _TIF_SECCOMP | \ 151 _TIF_SYSCALL_AUDIT | _TIF_SECCOMP | \
@@ -165,6 +172,7 @@ extern void init_thread_xstate(void);
165#define TS_USEDFPU 0x0002 /* FPU used by this task this quantum */ 172#define TS_USEDFPU 0x0002 /* FPU used by this task this quantum */
166 173
167#ifndef __ASSEMBLY__ 174#ifndef __ASSEMBLY__
175
168#define HAVE_SET_RESTORE_SIGMASK 1 176#define HAVE_SET_RESTORE_SIGMASK 1
169static inline void set_restore_sigmask(void) 177static inline void set_restore_sigmask(void)
170{ 178{
@@ -172,6 +180,24 @@ static inline void set_restore_sigmask(void)
172 ti->status |= TS_RESTORE_SIGMASK; 180 ti->status |= TS_RESTORE_SIGMASK;
173 set_bit(TIF_SIGPENDING, (unsigned long *)&ti->flags); 181 set_bit(TIF_SIGPENDING, (unsigned long *)&ti->flags);
174} 182}
183
184#define TI_FLAG_FAULT_CODE_SHIFT 24
185
186/*
187 * Additional thread flag encoding
188 */
189static inline void set_thread_fault_code(unsigned int val)
190{
191 struct thread_info *ti = current_thread_info();
192 ti->flags = (ti->flags & (~0 >> (32 - TI_FLAG_FAULT_CODE_SHIFT)))
193 | (val << TI_FLAG_FAULT_CODE_SHIFT);
194}
195
196static inline unsigned int get_thread_fault_code(void)
197{
198 struct thread_info *ti = current_thread_info();
199 return ti->flags >> TI_FLAG_FAULT_CODE_SHIFT;
200}
175#endif /* !__ASSEMBLY__ */ 201#endif /* !__ASSEMBLY__ */
176 202
177#endif /* __KERNEL__ */ 203#endif /* __KERNEL__ */
diff --git a/arch/sh/kernel/cpu/sh3/entry.S b/arch/sh/kernel/cpu/sh3/entry.S
index f6a389c996cb..262db6ec067b 100644
--- a/arch/sh/kernel/cpu/sh3/entry.S
+++ b/arch/sh/kernel/cpu/sh3/entry.S
@@ -2,7 +2,7 @@
2 * arch/sh/kernel/cpu/sh3/entry.S 2 * arch/sh/kernel/cpu/sh3/entry.S
3 * 3 *
4 * Copyright (C) 1999, 2000, 2002 Niibe Yutaka 4 * Copyright (C) 1999, 2000, 2002 Niibe Yutaka
5 * Copyright (C) 2003 - 2006 Paul Mundt 5 * Copyright (C) 2003 - 2012 Paul Mundt
6 * 6 *
7 * This file is subject to the terms and conditions of the GNU General Public 7 * This file is subject to the terms and conditions of the GNU General Public
8 * License. See the file "COPYING" in the main directory of this archive 8 * License. See the file "COPYING" in the main directory of this archive
@@ -17,6 +17,7 @@
17#include <cpu/mmu_context.h> 17#include <cpu/mmu_context.h>
18#include <asm/page.h> 18#include <asm/page.h>
19#include <asm/cache.h> 19#include <asm/cache.h>
20#include <asm/thread_info.h>
20 21
21! NOTE: 22! NOTE:
22! GNU as (as of 2.9.1) changes bf/s into bt/s and bra, when the address 23! GNU as (as of 2.9.1) changes bf/s into bt/s and bra, when the address
@@ -114,22 +115,22 @@ ENTRY(tlb_miss_load)
114 .align 2 115 .align 2
115ENTRY(tlb_miss_store) 116ENTRY(tlb_miss_store)
116 bra call_handle_tlbmiss 117 bra call_handle_tlbmiss
117 mov #1, r5 118 mov #FAULT_CODE_WRITE, r5
118 119
119 .align 2 120 .align 2
120ENTRY(initial_page_write) 121ENTRY(initial_page_write)
121 bra call_handle_tlbmiss 122 bra call_handle_tlbmiss
122 mov #2, r5 123 mov #FAULT_CODE_INITIAL, r5
123 124
124 .align 2 125 .align 2
125ENTRY(tlb_protection_violation_load) 126ENTRY(tlb_protection_violation_load)
126 bra call_do_page_fault 127 bra call_do_page_fault
127 mov #0, r5 128 mov #FAULT_CODE_PROT, r5
128 129
129 .align 2 130 .align 2
130ENTRY(tlb_protection_violation_store) 131ENTRY(tlb_protection_violation_store)
131 bra call_do_page_fault 132 bra call_do_page_fault
132 mov #1, r5 133 mov #(FAULT_CODE_PROT | FAULT_CODE_WRITE), r5
133 134
134call_handle_tlbmiss: 135call_handle_tlbmiss:
135 mov.l 1f, r0 136 mov.l 1f, r0
diff --git a/arch/sh/mm/fault_32.c b/arch/sh/mm/fault_32.c
index 889e83b5ff22..a469b95e88fb 100644
--- a/arch/sh/mm/fault_32.c
+++ b/arch/sh/mm/fault_32.c
@@ -211,7 +211,7 @@ show_fault_oops(struct pt_regs *regs, unsigned long address)
211} 211}
212 212
213static noinline void 213static noinline void
214no_context(struct pt_regs *regs, unsigned long writeaccess, 214no_context(struct pt_regs *regs, unsigned long error_code,
215 unsigned long address) 215 unsigned long address)
216{ 216{
217 /* Are we prepared to handle this kernel fault? */ 217 /* Are we prepared to handle this kernel fault? */
@@ -229,13 +229,13 @@ no_context(struct pt_regs *regs, unsigned long writeaccess,
229 229
230 show_fault_oops(regs, address); 230 show_fault_oops(regs, address);
231 231
232 die("Oops", regs, writeaccess); 232 die("Oops", regs, error_code);
233 bust_spinlocks(0); 233 bust_spinlocks(0);
234 do_exit(SIGKILL); 234 do_exit(SIGKILL);
235} 235}
236 236
237static void 237static void
238__bad_area_nosemaphore(struct pt_regs *regs, unsigned long writeaccess, 238__bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code,
239 unsigned long address, int si_code) 239 unsigned long address, int si_code)
240{ 240{
241 struct task_struct *tsk = current; 241 struct task_struct *tsk = current;
@@ -252,18 +252,18 @@ __bad_area_nosemaphore(struct pt_regs *regs, unsigned long writeaccess,
252 return; 252 return;
253 } 253 }
254 254
255 no_context(regs, writeaccess, address); 255 no_context(regs, error_code, address);
256} 256}
257 257
258static noinline void 258static noinline void
259bad_area_nosemaphore(struct pt_regs *regs, unsigned long writeaccess, 259bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code,
260 unsigned long address) 260 unsigned long address)
261{ 261{
262 __bad_area_nosemaphore(regs, writeaccess, address, SEGV_MAPERR); 262 __bad_area_nosemaphore(regs, error_code, address, SEGV_MAPERR);
263} 263}
264 264
265static void 265static void
266__bad_area(struct pt_regs *regs, unsigned long writeaccess, 266__bad_area(struct pt_regs *regs, unsigned long error_code,
267 unsigned long address, int si_code) 267 unsigned long address, int si_code)
268{ 268{
269 struct mm_struct *mm = current->mm; 269 struct mm_struct *mm = current->mm;
@@ -274,20 +274,20 @@ __bad_area(struct pt_regs *regs, unsigned long writeaccess,
274 */ 274 */
275 up_read(&mm->mmap_sem); 275 up_read(&mm->mmap_sem);
276 276
277 __bad_area_nosemaphore(regs, writeaccess, address, si_code); 277 __bad_area_nosemaphore(regs, error_code, address, si_code);
278} 278}
279 279
280static noinline void 280static noinline void
281bad_area(struct pt_regs *regs, unsigned long writeaccess, unsigned long address) 281bad_area(struct pt_regs *regs, unsigned long error_code, unsigned long address)
282{ 282{
283 __bad_area(regs, writeaccess, address, SEGV_MAPERR); 283 __bad_area(regs, error_code, address, SEGV_MAPERR);
284} 284}
285 285
286static noinline void 286static noinline void
287bad_area_access_error(struct pt_regs *regs, unsigned long writeaccess, 287bad_area_access_error(struct pt_regs *regs, unsigned long error_code,
288 unsigned long address) 288 unsigned long address)
289{ 289{
290 __bad_area(regs, writeaccess, address, SEGV_ACCERR); 290 __bad_area(regs, error_code, address, SEGV_ACCERR);
291} 291}
292 292
293static void out_of_memory(void) 293static void out_of_memory(void)
@@ -302,7 +302,7 @@ static void out_of_memory(void)
302} 302}
303 303
304static void 304static void
305do_sigbus(struct pt_regs *regs, unsigned long writeaccess, unsigned long address) 305do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address)
306{ 306{
307 struct task_struct *tsk = current; 307 struct task_struct *tsk = current;
308 struct mm_struct *mm = tsk->mm; 308 struct mm_struct *mm = tsk->mm;
@@ -311,13 +311,13 @@ do_sigbus(struct pt_regs *regs, unsigned long writeaccess, unsigned long address
311 311
312 /* Kernel mode? Handle exceptions or die: */ 312 /* Kernel mode? Handle exceptions or die: */
313 if (!user_mode(regs)) 313 if (!user_mode(regs))
314 no_context(regs, writeaccess, address); 314 no_context(regs, error_code, address);
315 315
316 force_sig_info_fault(SIGBUS, BUS_ADRERR, address, tsk); 316 force_sig_info_fault(SIGBUS, BUS_ADRERR, address, tsk);
317} 317}
318 318
319static noinline int 319static noinline int
320mm_fault_error(struct pt_regs *regs, unsigned long writeaccess, 320mm_fault_error(struct pt_regs *regs, unsigned long error_code,
321 unsigned long address, unsigned int fault) 321 unsigned long address, unsigned int fault)
322{ 322{
323 /* 323 /*
@@ -328,7 +328,7 @@ mm_fault_error(struct pt_regs *regs, unsigned long writeaccess,
328 if (!(fault & VM_FAULT_RETRY)) 328 if (!(fault & VM_FAULT_RETRY))
329 up_read(&current->mm->mmap_sem); 329 up_read(&current->mm->mmap_sem);
330 if (!user_mode(regs)) 330 if (!user_mode(regs))
331 no_context(regs, writeaccess, address); 331 no_context(regs, error_code, address);
332 return 1; 332 return 1;
333 } 333 }
334 334
@@ -339,14 +339,14 @@ mm_fault_error(struct pt_regs *regs, unsigned long writeaccess,
339 /* Kernel mode? Handle exceptions or die: */ 339 /* Kernel mode? Handle exceptions or die: */
340 if (!user_mode(regs)) { 340 if (!user_mode(regs)) {
341 up_read(&current->mm->mmap_sem); 341 up_read(&current->mm->mmap_sem);
342 no_context(regs, writeaccess, address); 342 no_context(regs, error_code, address);
343 return 1; 343 return 1;
344 } 344 }
345 345
346 out_of_memory(); 346 out_of_memory();
347 } else { 347 } else {
348 if (fault & VM_FAULT_SIGBUS) 348 if (fault & VM_FAULT_SIGBUS)
349 do_sigbus(regs, writeaccess, address); 349 do_sigbus(regs, error_code, address);
350 else 350 else
351 BUG(); 351 BUG();
352 } 352 }
@@ -381,7 +381,7 @@ static int fault_in_kernel_space(unsigned long address)
381 * routines. 381 * routines.
382 */ 382 */
383asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, 383asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
384 unsigned long writeaccess, 384 unsigned long error_code,
385 unsigned long address) 385 unsigned long address)
386{ 386{
387 unsigned long vec; 387 unsigned long vec;
@@ -389,8 +389,9 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
389 struct mm_struct *mm; 389 struct mm_struct *mm;
390 struct vm_area_struct * vma; 390 struct vm_area_struct * vma;
391 int fault; 391 int fault;
392 int write = error_code & FAULT_CODE_WRITE;
392 unsigned int flags = (FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE | 393 unsigned int flags = (FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE |
393 (writeaccess ? FAULT_FLAG_WRITE : 0)); 394 (write ? FAULT_FLAG_WRITE : 0));
394 395
395 tsk = current; 396 tsk = current;
396 mm = tsk->mm; 397 mm = tsk->mm;
@@ -411,7 +412,7 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
411 if (notify_page_fault(regs, vec)) 412 if (notify_page_fault(regs, vec))
412 return; 413 return;
413 414
414 bad_area_nosemaphore(regs, writeaccess, address); 415 bad_area_nosemaphore(regs, error_code, address);
415 return; 416 return;
416 } 417 }
417 418
@@ -429,7 +430,7 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
429 * in an atomic region then we must not take the fault: 430 * in an atomic region then we must not take the fault:
430 */ 431 */
431 if (unlikely(in_atomic() || !mm)) { 432 if (unlikely(in_atomic() || !mm)) {
432 bad_area_nosemaphore(regs, writeaccess, address); 433 bad_area_nosemaphore(regs, error_code, address);
433 return; 434 return;
434 } 435 }
435 436
@@ -438,17 +439,17 @@ retry:
438 439
439 vma = find_vma(mm, address); 440 vma = find_vma(mm, address);
440 if (unlikely(!vma)) { 441 if (unlikely(!vma)) {
441 bad_area(regs, writeaccess, address); 442 bad_area(regs, error_code, address);
442 return; 443 return;
443 } 444 }
444 if (likely(vma->vm_start <= address)) 445 if (likely(vma->vm_start <= address))
445 goto good_area; 446 goto good_area;
446 if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) { 447 if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) {
447 bad_area(regs, writeaccess, address); 448 bad_area(regs, error_code, address);
448 return; 449 return;
449 } 450 }
450 if (unlikely(expand_stack(vma, address))) { 451 if (unlikely(expand_stack(vma, address))) {
451 bad_area(regs, writeaccess, address); 452 bad_area(regs, error_code, address);
452 return; 453 return;
453 } 454 }
454 455
@@ -457,11 +458,13 @@ retry:
457 * we can handle it.. 458 * we can handle it..
458 */ 459 */
459good_area: 460good_area:
460 if (unlikely(access_error(writeaccess, vma))) { 461 if (unlikely(access_error(error_code, vma))) {
461 bad_area_access_error(regs, writeaccess, address); 462 bad_area_access_error(regs, error_code, address);
462 return; 463 return;
463 } 464 }
464 465
466 set_thread_fault_code(error_code);
467
465 /* 468 /*
466 * If for any reason at all we couldn't handle the fault, 469 * If for any reason at all we couldn't handle the fault,
467 * make sure we exit gracefully rather than endlessly redo 470 * make sure we exit gracefully rather than endlessly redo
@@ -470,7 +473,7 @@ good_area:
470 fault = handle_mm_fault(mm, vma, address, flags); 473 fault = handle_mm_fault(mm, vma, address, flags);
471 474
472 if (unlikely(fault & (VM_FAULT_RETRY | VM_FAULT_ERROR))) 475 if (unlikely(fault & (VM_FAULT_RETRY | VM_FAULT_ERROR)))
473 if (mm_fault_error(regs, writeaccess, address, fault)) 476 if (mm_fault_error(regs, error_code, address, fault))
474 return; 477 return;
475 478
476 if (flags & FAULT_FLAG_ALLOW_RETRY) { 479 if (flags & FAULT_FLAG_ALLOW_RETRY) {
@@ -502,7 +505,7 @@ good_area:
502 * Called with interrupts disabled. 505 * Called with interrupts disabled.
503 */ 506 */
504asmlinkage int __kprobes 507asmlinkage int __kprobes
505handle_tlbmiss(struct pt_regs *regs, unsigned long writeaccess, 508handle_tlbmiss(struct pt_regs *regs, unsigned long error_code,
506 unsigned long address) 509 unsigned long address)
507{ 510{
508 pgd_t *pgd; 511 pgd_t *pgd;
@@ -535,10 +538,10 @@ handle_tlbmiss(struct pt_regs *regs, unsigned long writeaccess,
535 entry = *pte; 538 entry = *pte;
536 if (unlikely(pte_none(entry) || pte_not_present(entry))) 539 if (unlikely(pte_none(entry) || pte_not_present(entry)))
537 return 1; 540 return 1;
538 if (unlikely(writeaccess && !pte_write(entry))) 541 if (unlikely(error_code && !pte_write(entry)))
539 return 1; 542 return 1;
540 543
541 if (writeaccess) 544 if (error_code)
542 entry = pte_mkdirty(entry); 545 entry = pte_mkdirty(entry);
543 entry = pte_mkyoung(entry); 546 entry = pte_mkyoung(entry);
544 547
@@ -550,10 +553,11 @@ handle_tlbmiss(struct pt_regs *regs, unsigned long writeaccess,
550 * the case of an initial page write exception, so we need to 553 * the case of an initial page write exception, so we need to
551 * flush it in order to avoid potential TLB entry duplication. 554 * flush it in order to avoid potential TLB entry duplication.
552 */ 555 */
553 if (writeaccess == 2) 556 if (error_code == FAULT_CODE_INITIAL)
554 local_flush_tlb_one(get_asid(), address & PAGE_MASK); 557 local_flush_tlb_one(get_asid(), address & PAGE_MASK);
555#endif 558#endif
556 559
560 set_thread_fault_code(error_code);
557 update_mmu_cache(NULL, address, pte); 561 update_mmu_cache(NULL, address, pte);
558 562
559 return 0; 563 return 0;