mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
alpha: split the prefetch check in do_page_fault() by mode
Before EV6, a load into $31 or $f31 could fault, so do_page_fault() reads the faulting instruction and skips it if it is such a prefetch. The PC can be a user or a kernel address, since the check runs for faults from either mode, and it reads it with __get_user() either way. That only works because alpha's __get_user() does no address check. It also ignores the error, relying on the fault fixup zeroing the instruction, which does not decode as a prefetch. Move the check into is_prefetch(), and read the instruction with get_user() for a user-mode PC and get_kernel_nofault() for a kernel one. A failed read now explicitly means "not a prefetch". This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
5a36c24b0e
commit
7318a05d2f
|
|
@ -81,6 +81,28 @@ __load_new_mm_context(struct mm_struct *next_mm)
|
|||
(((unsigned long *)regs)[(r) <= 8 ? (r) : (r) <= 15 ? (r)-17 : \
|
||||
(r) <= 18 ? (r)+11 : (r)-10])
|
||||
|
||||
/*
|
||||
* As of EV6, a load into $31/$f31 is a prefetch, and never faults
|
||||
* (or is suppressed by the PALcode). Support that for older CPUs
|
||||
* by ignoring such an instruction.
|
||||
*/
|
||||
static bool is_prefetch(struct pt_regs *regs)
|
||||
{
|
||||
unsigned int insn;
|
||||
int err;
|
||||
|
||||
if (user_mode(regs))
|
||||
err = get_user(insn, (unsigned int __user *)regs->pc);
|
||||
else
|
||||
err = get_kernel_nofault(insn, (unsigned int *)regs->pc);
|
||||
if (err)
|
||||
return false;
|
||||
|
||||
/* ldq ldl ldt lds ldg ldf ldwu ldbu */
|
||||
return (insn >> 21 & 0x1f) == 0x1f &&
|
||||
(1ul << (insn >> 26) & 0x30f00001400ul);
|
||||
}
|
||||
|
||||
asmlinkage void
|
||||
do_page_fault(unsigned long address, unsigned long mmcsr,
|
||||
long cause, struct pt_regs *regs)
|
||||
|
|
@ -92,18 +114,9 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
|
|||
vm_fault_t fault;
|
||||
unsigned int flags = FAULT_FLAG_DEFAULT;
|
||||
|
||||
/* As of EV6, a load into $31/$f31 is a prefetch, and never faults
|
||||
(or is suppressed by the PALcode). Support that for older CPUs
|
||||
by ignoring such an instruction. */
|
||||
if (cause == 0) {
|
||||
unsigned int insn;
|
||||
__get_user(insn, (unsigned int __user *)regs->pc);
|
||||
if ((insn >> 21 & 0x1f) == 0x1f &&
|
||||
/* ldq ldl ldt lds ldg ldf ldwu ldbu */
|
||||
(1ul << (insn >> 26) & 0x30f00001400ul)) {
|
||||
regs->pc += 4;
|
||||
return;
|
||||
}
|
||||
if (cause == 0 && is_prefetch(regs)) {
|
||||
regs->pc += 4;
|
||||
return;
|
||||
}
|
||||
|
||||
/* If we're in an interrupt context, or have no user context,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user