Commit 107a0367 authored by Ingo Molnar's avatar Ingo Molnar

x86, mm: fault.c, refactor/simplify the is_prefetch() code

Impact: no functionality changed

Factor out the opcode checker into a helper inline.

The code got a tiny bit smaller:

   text	   data	    bss	    dec	    hex	filename
   4632	     32	     24	   4688	   1250	fault.o.before
   4618	     32	     24	   4674	   1242	fault.o.after

And it got cleaner / easier to review as well.

Cc: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 2d4a7167
...@@ -99,38 +99,12 @@ static inline int notify_page_fault(struct pt_regs *regs) ...@@ -99,38 +99,12 @@ static inline int notify_page_fault(struct pt_regs *regs)
* *
* Opcode checker based on code by Richard Brunner. * Opcode checker based on code by Richard Brunner.
*/ */
static int static inline int
is_prefetch(struct pt_regs *regs, unsigned long error_code, unsigned long addr) check_prefetch_opcode(struct pt_regs *regs, unsigned char *instr,
unsigned char opcode, int *prefetch)
{ {
unsigned char *max_instr; unsigned char instr_hi = opcode & 0xf0;
unsigned char *instr; unsigned char instr_lo = opcode & 0x0f;
int scan_more = 1;
int prefetch = 0;
/*
* If it was a exec (instruction fetch) fault on NX page, then
* do not ignore the fault:
*/
if (error_code & PF_INSTR)
return 0;
instr = (unsigned char *)convert_ip_to_linear(current, regs);
max_instr = instr + 15;
if (user_mode(regs) && instr >= (unsigned char *)TASK_SIZE)
return 0;
while (scan_more && instr < max_instr) {
unsigned char instr_hi;
unsigned char instr_lo;
unsigned char opcode;
if (probe_kernel_address(instr, opcode))
break;
instr_hi = opcode & 0xf0;
instr_lo = opcode & 0x0f;
instr++;
switch (instr_hi) { switch (instr_hi) {
case 0x20: case 0x20:
...@@ -141,8 +115,7 @@ is_prefetch(struct pt_regs *regs, unsigned long error_code, unsigned long addr) ...@@ -141,8 +115,7 @@ is_prefetch(struct pt_regs *regs, unsigned long error_code, unsigned long addr)
* opcode if some of these prefixes are present so * opcode if some of these prefixes are present so
* X86_64 will never get here anyway * X86_64 will never get here anyway
*/ */
scan_more = ((instr_lo & 7) == 0x6); return ((instr_lo & 7) == 0x6);
break;
#ifdef CONFIG_X86_64 #ifdef CONFIG_X86_64
case 0x40: case 0x40:
/* /*
...@@ -152,30 +125,57 @@ is_prefetch(struct pt_regs *regs, unsigned long error_code, unsigned long addr) ...@@ -152,30 +125,57 @@ is_prefetch(struct pt_regs *regs, unsigned long error_code, unsigned long addr)
* but for now it's good enough to assume that long * but for now it's good enough to assume that long
* mode only uses well known segments or kernel. * mode only uses well known segments or kernel.
*/ */
scan_more = (!user_mode(regs)) || (regs->cs == __USER_CS); return (!user_mode(regs)) || (regs->cs == __USER_CS);
break;
#endif #endif
case 0x60: case 0x60:
/* 0x64 thru 0x67 are valid prefixes in all modes. */ /* 0x64 thru 0x67 are valid prefixes in all modes. */
scan_more = (instr_lo & 0xC) == 0x4; return (instr_lo & 0xC) == 0x4;
break;
case 0xF0: case 0xF0:
/* 0xF0, 0xF2, 0xF3 are valid prefixes in all modes. */ /* 0xF0, 0xF2, 0xF3 are valid prefixes in all modes. */
scan_more = !instr_lo || (instr_lo>>1) == 1; return !instr_lo || (instr_lo>>1) == 1;
break;
case 0x00: case 0x00:
/* Prefetch instruction is 0x0F0D or 0x0F18 */ /* Prefetch instruction is 0x0F0D or 0x0F18 */
scan_more = 0;
if (probe_kernel_address(instr, opcode)) if (probe_kernel_address(instr, opcode))
break; return 0;
prefetch = (instr_lo == 0xF) &&
*prefetch = (instr_lo == 0xF) &&
(opcode == 0x0D || opcode == 0x18); (opcode == 0x0D || opcode == 0x18);
break; return 0;
default: default:
scan_more = 0; return 0;
break;
} }
}
static int
is_prefetch(struct pt_regs *regs, unsigned long error_code, unsigned long addr)
{
unsigned char *max_instr;
unsigned char *instr;
int prefetch = 0;
/*
* If it was a exec (instruction fetch) fault on NX page, then
* do not ignore the fault:
*/
if (error_code & PF_INSTR)
return 0;
instr = (void *)convert_ip_to_linear(current, regs);
max_instr = instr + 15;
if (user_mode(regs) && instr >= (unsigned char *)TASK_SIZE)
return 0;
while (instr < max_instr) {
unsigned char opcode;
if (probe_kernel_address(instr, opcode))
break;
instr++;
if (!check_prefetch_opcode(regs, instr, opcode, &prefetch))
break;
} }
return prefetch; return prefetch;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment