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)
*
* Opcode checker based on code by Richard Brunner.
*/
static int
is_prefetch(struct pt_regs *regs, unsigned long error_code, unsigned long addr)
static inline int
check_prefetch_opcode(struct pt_regs *regs, unsigned char *instr,
unsigned char opcode, int *prefetch)
{
unsigned char *max_instr;
unsigned char *instr;
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++;
unsigned char instr_hi = opcode & 0xf0;
unsigned char instr_lo = opcode & 0x0f;
switch (instr_hi) {
case 0x20:
......@@ -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
* X86_64 will never get here anyway
*/
scan_more = ((instr_lo & 7) == 0x6);
break;
return ((instr_lo & 7) == 0x6);
#ifdef CONFIG_X86_64
case 0x40:
/*
......@@ -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
* mode only uses well known segments or kernel.
*/
scan_more = (!user_mode(regs)) || (regs->cs == __USER_CS);
break;
return (!user_mode(regs)) || (regs->cs == __USER_CS);
#endif
case 0x60:
/* 0x64 thru 0x67 are valid prefixes in all modes. */
scan_more = (instr_lo & 0xC) == 0x4;
break;
return (instr_lo & 0xC) == 0x4;
case 0xF0:
/* 0xF0, 0xF2, 0xF3 are valid prefixes in all modes. */
scan_more = !instr_lo || (instr_lo>>1) == 1;
break;
return !instr_lo || (instr_lo>>1) == 1;
case 0x00:
/* Prefetch instruction is 0x0F0D or 0x0F18 */
scan_more = 0;
if (probe_kernel_address(instr, opcode))
break;
prefetch = (instr_lo == 0xF) &&
return 0;
*prefetch = (instr_lo == 0xF) &&
(opcode == 0x0D || opcode == 0x18);
break;
return 0;
default:
scan_more = 0;
break;
return 0;
}
}
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;
}
......
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