[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#198158: architecture i386 isn't i386 anymore



On Wed, 25 Jun 2003, Branden Robinson wrote:
> > Hmm... I'm not sure about this as the last time I used assembler was 
> > in the times of real mode DOS, but there is a yet another option:
> > we can patch the kernel so when an invalid opcode occurs, whatever 
> > instruction was at CS:EIP gets emulated in software, similar to the
> > way i387 emulation is done.
> > (arch/i386/kernel/entry.S)
> > Of course, this would further slow down the speed demon known as 80386,
> > but since (AFAIK) the 486-specific opcodes get used pretty rarely in 
> > non-kernel code, the performance hit wouldn't be crippling.  And, there
> > is no performance hit at all for >386 machines, as no legitimate process
> > ever triggers the invalid opcode fault.
> 
> If this indeed feasible, then this is the solution that appeals most to
> me personally.
> 
> * It seems the least intrusive.  80386 users are probably going to want
>   and use an 80386-specific kernel, if they don't already *have* to.
> * Our hand is forced by the fact that the rest of the Linux distributors
>   in the world, and apparently the GCC folks, don't care about C++ ABI
>   portability to 80386 processors.
> * This doesn't require recompiling anything except the kernel.
> 
> The drawbacks:
> * Someone actually has to write this kernel patch.
As the person who originally submitted this idea, I'm probably the one
who is morally obliged to write it, even though:
* I'm not a kernel hacker,
* I was once good at _8086_ assembly, but the times of real mode are
  long gone, and I have no recent assembler experience,
* I'm moving home later this week, and won't be able to write this
  while my desktop machine is in transit (all the other boxes I got
  root access to are production servers),
* my skills are probably no match for most of you.

The pros and cons for the idea:
Pro:
  this kernel patch would make the 486 ABI transition flawless for all
  80386 users who have it applied, and (optionally) it can be used to
  make other software built for i486, i586 and i686 work on lesser CPUs.
Con:
  those on 80386 who don't have this patch applied will be screwed


I've made some proof-of-concept patches, and I'm ready for actually
emulating the opcodes.  However, you would need to answer the following
questions:
* what opcodes need to be emulated?
    * just those needed for C++ ABI
    * all 386->486 opcodes (there's just a few of them, right?)
    * most 386->586 opcodes (it's impossible to emulate at least RDTSC,
      but the majority of code doesn't use it)
    * 386->686?
* do you need SMP on 80386?  Is there even such thing as 80386 SMP
  machines?  Not requiring SMP support would make the ABI change 
  trivial...
* would you want some other emulation, like 486->586, 586->686?  MMX?
  Once the infrastructure for emulation is done, adding new opcodes
  is a simple task.


And, some patches for you to play with:
1. just reporting the invalid opcodes encountered
--- arch/i386/kernel/traps.c.0	2003-06-25 11:19:53.000000000 +0200
+++ arch/i386/kernel/traps.c	2003-06-25 12:09:16.000000000 +0200
@@ -388,7 +388,6 @@
 DO_VM86_ERROR( 3, SIGTRAP, "int3", int3)
 DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow)
 DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds)
-DO_ERROR_INFO( 6, SIGILL,  "invalid operand", invalid_op, ILL_ILLOPN, regs->eip)
 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)
@@ -397,6 +396,32 @@
 DO_ERROR(12, SIGBUS,  "stack segment", stack_segment)
 DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, get_cr2())
 
+
+asmlinkage void do_invalid_op(struct pt_regs * regs, long error_code)
+{
+	siginfo_t info;
+	int i;
+	
+	printk("Invalid opcode: ");
+	for(i=0;i<20;i++)
+	{
+		unsigned char c;
+		if(__get_user(c, &((unsigned char*)regs->eip)[i]))
+		{
+			printk(" Bad EIP value.");
+			break;
+		}
+		printk("%02x ", c);
+	}
+	printk("\n");
+	
+	info.si_signo = SIGILL;
+	info.si_errno = 0;
+	info.si_code = ILL_ILLOPN;
+	info.si_addr = regs->eip;
+	do_trap(6, SIGILL, "invalid operand", 1, regs, error_code, &info);
+}
+
 asmlinkage void do_general_protection(struct pt_regs * regs, long error_code)
 {
 	if (regs->eflags & VM_MASK)





2. an ugly hack that proves that the emulation can be done.  I took some
   random opcode that just happened to be not present on my machine (you
   would probably need to choose something else), and "emulated" it by
   a token operation: swapping eax and ebx.  I didn't even bother with
   making it nice, readable or anything -- it's just a test code.
--- arch/i386/kernel/traps.c.0	2003-06-25 11:19:53.000000000 +0200
+++ arch/i386/kernel/traps.c	2003-06-25 13:09:50.000000000 +0200
@@ -388,7 +388,6 @@
 DO_VM86_ERROR( 3, SIGTRAP, "int3", int3)
 DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow)
 DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds)
-DO_ERROR_INFO( 6, SIGILL,  "invalid operand", invalid_op, ILL_ILLOPN, regs->eip)
 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)
@@ -397,6 +396,27 @@
 DO_ERROR(12, SIGBUS,  "stack segment", stack_segment)
 DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, get_cr2())
 
+
+asmlinkage void do_invalid_op(struct pt_regs * regs, long error_code)
+{
+	siginfo_t info;
+	int c;
+
+    // Catch "movntpd %xmm6,(%ebx)" = 66 0f 2b 33 (some random SIMD opcode
+    // not present on Celeron/Mendocino), and make it swap eax and ebx.
+    if(__get_user(c, (int*)regs->eip) || (c!=0x332b0f66))
+        goto bad;
+    c=regs->eax, regs->eax=regs->ebx, regs->ebx=c;
+    regs->eip+=4;
+	return;
+bad:
+	info.si_signo = SIGILL;
+	info.si_errno = 0;
+	info.si_code = ILL_ILLOPN;
+	info.si_addr = regs->eip;
+	do_trap(6, SIGILL, "invalid operand", 1, regs, error_code, &info);
+}
+
 asmlinkage void do_general_protection(struct pt_regs * regs, long error_code)
 {
 	if (regs->eflags & VM_MASK)


Regards,
 1KB

/-----------------------\ Shh, be vewy, vewy quiet,
| kilobyte@mimuw.edu.pl | I'm hunting wuntime ewwows!
\-----------------------/
Segmentation fault (core dumped)



Reply to: