diff -urNp --exclude CVS --exclude BitKeeper --exclude {arch} --exclude .arch-ids x-ref/arch/i386/mm/fault.c x/arch/i386/mm/fault.c --- x-ref/arch/i386/mm/fault.c 2003-10-02 10:37:44.000000000 +0200 +++ x/arch/i386/mm/fault.c 2003-10-02 10:38:14.000000000 +0200 @@ -106,6 +106,81 @@ out_of_memory: goto bad_area; } +/* Sometimes the CPU reports invalid exceptions on prefetch. + Check that here and ignore. + Opcode checker based on code by Richard Brunner */ +static int is_prefetch(struct pt_regs *regs, unsigned long addr) +{ + unsigned char *instr = (unsigned char *)(regs->eip); + int scan_more = 1; + int prefetch = 0; + unsigned char *max_instr = instr + 15; + + /* Avoid recursive faults. This is just an optimization, + they must be handled correctly too */ + if (regs->eip == addr) + return 0; + + if (((regs->xcs & 0xffff) != __KERNEL_CS) && ((regs->xcs & 0xffff) != __USER_CS)) + return 0; + + while (scan_more && instr < max_instr) { + unsigned char opcode; + unsigned char instr_hi; + unsigned char instr_lo; + + if ((regs->xcs & 3) ? get_user(opcode,instr) : __get_user(opcode, instr)) + break; + + instr_hi = opcode & 0xf0; + instr_lo = opcode & 0x0f; + instr++; + + switch (instr_hi) { + case 0x20: + case 0x30: + /* Values 0x26,0x2E,0x36,0x3E are valid x86 + prefixes. In long mode, the CPU will signal + invalid opcode if some of these prefixes are + present so we will never get here anyway */ + scan_more = ((instr_lo & 7) == 0x6); + break; + + case 0x40: + /* May be valid in long mode (REX prefixes) */ + break; + + case 0x60: + /* 0x64 thru 0x67 are valid prefixes in all modes. */ + scan_more = (instr_lo & 0xC) == 0x4; + break; + case 0xF0: + /* 0xF0, 0xF2, and 0xF3 are valid prefixes in all modes. */ + scan_more = !instr_lo || (instr_lo>>1) == 1; + break; + case 0x00: + /* Prefetch instruction is 0x0F0D or 0x0F18 */ + scan_more = 0; + if ((regs->xcs & 3) ? get_user(opcode,instr) : __get_user(opcode, instr)) + break; + prefetch = (instr_lo == 0xF) && + (opcode == 0x0D || opcode == 0x18); + break; + default: + scan_more = 0; + break; + } + } + +#if 0 + if (prefetch) + printk("%s: prefetch caused page fault at %lx/%lx\n", current->comm, + regs->eip, addr); +#endif + return prefetch; +} + + /* * Unlock any spinlocks which will prevent us from getting the * message out @@ -191,7 +266,7 @@ asmlinkage void do_page_fault(struct pt_ * context, we must not take the fault.. */ if (in_interrupt() || !mm) - goto no_context; + goto bad_area_nosemaphore; #ifdef CONFIG_X86_REMOTE_DEBUG if (kgdb_memerr_expected) { @@ -287,8 +362,12 @@ good_area: bad_area: up_read(&mm->mmap_sem); +bad_area_nosemaphore: /* User mode accesses just cause a SIGSEGV */ if (error_code & 4) { + if (is_prefetch(regs, address)) + return; + tsk->thread.cr2 = address; tsk->thread.error_code = error_code; tsk->thread.trap_no = 14; @@ -334,6 +413,9 @@ no_context: } #endif + if (is_prefetch(regs, address)) + return; + /* * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. @@ -384,10 +466,13 @@ out_of_memory: do_sigbus: up_read(&mm->mmap_sem); - /* - * Send a sigbus, regardless of whether we were in kernel - * or user mode. - */ + /* Kernel mode? Handle exceptions or die */ + if (!(error_code & 4)) + goto no_context; + + if (is_prefetch(regs, address)) + return; + tsk->thread.cr2 = address; tsk->thread.error_code = error_code; tsk->thread.trap_no = 14; @@ -397,9 +482,6 @@ do_sigbus: info.si_addr = (void *)address; force_sig_info(SIGBUS, &info, tsk); - /* Kernel mode? Handle exceptions or die */ - if (!(error_code & 4)) - goto no_context; return; vmalloc_fault: