diff -rupN linux-2.4.33-pre1/arch/i386/config.in linux-2.4.33-pre1.i386-emu-rdtsc/arch/i386/config.in --- linux-2.4.33-pre1/arch/i386/config.in 2004-11-17 18:36:41.000000000 +0100 +++ linux-2.4.33-pre1.i386-emu-rdtsc/arch/i386/config.in 2005-12-29 21:55:00.000000000 +0100 @@ -222,6 +222,10 @@ if [ "$CONFIG_HIGHMEM" = "y" ]; then bool 'HIGHMEM I/O support' CONFIG_HIGHIO fi +if [ "$CONFIG_X86_HAS_TSC" != "y" ]; then + bool "Kernel emulation of user-space RDTSC instructions" CONFIG_X86_EMU_RDTSC +fi + bool 'Math emulation' CONFIG_MATH_EMULATION bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR bool 'Symmetric multi-processing support' CONFIG_SMP diff -rupN linux-2.4.33-pre1/arch/i386/kernel/irq.c linux-2.4.33-pre1.i386-emu-rdtsc/arch/i386/kernel/irq.c --- linux-2.4.33-pre1/arch/i386/kernel/irq.c 2003-11-29 00:28:10.000000000 +0100 +++ linux-2.4.33-pre1.i386-emu-rdtsc/arch/i386/kernel/irq.c 2005-12-29 21:55:00.000000000 +0100 @@ -179,6 +179,12 @@ int show_interrupts(struct seq_file *p, seq_printf(p, "MIS: %10u\n", atomic_read(&irq_mis_count)); #endif #endif +#ifdef CONFIG_X86_EMU_RDTSC + { + extern unsigned int invalid_op_counter; + seq_printf(p, "ILL: %10u\n", invalid_op_counter); + } +#endif return 0; } diff -rupN linux-2.4.33-pre1/arch/i386/kernel/traps.c linux-2.4.33-pre1.i386-emu-rdtsc/arch/i386/kernel/traps.c --- linux-2.4.33-pre1/arch/i386/kernel/traps.c 2005-11-17 13:27:30.000000000 +0100 +++ linux-2.4.33-pre1.i386-emu-rdtsc/arch/i386/kernel/traps.c 2005-12-29 21:55:00.000000000 +0100 @@ -388,7 +388,58 @@ DO_VM86_ERROR_INFO( 0, SIGFPE, "divide DO_VM86_ERROR( 3, SIGTRAP, "int3", int3) DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow) DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds) + +#ifdef CONFIG_X86_EMU_RDTSC +unsigned int invalid_op_counter; +static int handle_invalid_op(struct pt_regs *regs) +{ + unsigned char *eip; + unsigned char op[2]; + + ++invalid_op_counter; + + if (!user_mode(regs)) { + printk(KERN_ERR "%s: illegal opcode while in kernel mode\n", + __FUNCTION__); + return 0; + } + eip = (unsigned char*)regs->eip; + if (get_user(op[0], eip+0) || get_user(op[1], eip+1)) { + printk(KERN_ERR "%s: unable to read opcode\n", + __FUNCTION__); + return 0; + } + /* rpmq in Fedora Core 1 executes RDTSC :-( */ + if (op[0] == 0x0F && op[1] == 0x31) { + static unsigned int fake_tsc_low; + regs->eax = ++fake_tsc_low; + regs->edx = 0; + regs->eip = (unsigned long)eip+2; + //printk(KERN_ERR "%s: RDTSC in pid %d (comm %s)\n", + // __FUNCTION__, current->pid, current->comm); + return 1; + } + printk(KERN_ERR "%s: illegal opcode 0x%02X 0x%02X in pid %d (comm %s)\n", + __FUNCTION__, op[0], op[1], current->pid, current->comm); + return 0; +} + +asmlinkage void do_invalid_op(struct pt_regs *regs, long error_code) +{ + siginfo_t info; + + if (handle_invalid_op(regs)) + return; + info.si_signo = SIGILL; + info.si_errno = 0; + info.si_code = ILL_ILLOPN; + info.si_addr = (void *)regs->eip; + do_trap(6, SIGILL, "invalid operand", 0, regs, error_code, &info); +} +#else DO_ERROR_INFO( 6, SIGILL, "invalid operand", invalid_op, ILL_ILLOPN, regs->eip) +#endif + DO_VM86_ERROR( 7, SIGSEGV, "device not available", device_not_available) DO_ERROR( 8, SIGSEGV, "double fault", double_fault) DO_ERROR( 9, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun)