Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+50 −50
Original line number Original line Diff line number Diff line
@@ -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)
		 * 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)
		 * 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;
}
}