public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [ANNOUNCE] CMOV emulation for 2.4.19-rc1
@ 2002-06-30  4:39 Willy TARREAU
  2002-07-01 13:58 ` Denis Vlasenko
  2002-07-01 18:25 ` Denis Vlasenko
  0 siblings, 2 replies; 16+ messages in thread
From: Willy TARREAU @ 2002-06-30  4:39 UTC (permalink / raw)
  To: linux-kernel, Ronald.Wahl

[-- Attachment #1: Type: text/plain, Size: 1937 bytes --]

Hi all,

OK, I know that many people dislike this, but I know others
who occasionally need it anyway. So I don't post it for general
inclusion, but for interested people.

What is it ? it's a software emulator for x86 opcodes not handled
by the processor. Emulations available are :
  - BSWAP, CMPXCHG, XADD on 386 (486 instructions)
  - CMOV on any x86 processor (pentium-pro instructions)

It is not meant to replace a correct compilation, but it may have
happened to all of us to try to rescue a damaged system or with a
boot disk, and copying some programs with a floppy, and then get
an 'Illegal instruction' because these programs have been compiled
with a badly configured compiler. I once had a gcc which did i686
by default, and I had a hard time trying to execute an e2fsck it
had compiled, on my k6 notebook !

Same if you take a disk from a system, and try to boot it on
another one. Well, I won't spend more time finding examples.

I've been using the 486 emulation on my 386 firewall for a few
years now, and have been quite happy with it. I cleaned the code
a bit and added support for cmov. All this will grow your bzImage
with less than 1kB.

As I stated above, it is *NOT* meant to replace a recompilation
for the correct target. Emulation is quite slow because of the
time the CPU spends processing the trap. I measured about 450
cycles for a cmov, which is quite a overhead, but still
acceptable for occasionnal purposes (1us on my k6/450).

I was thinking about adding some statistics informations, such
as the number of traps caught, globally and by process, but
finally realized that this was only bloat for something that
should not be used permanently.

I didn't have the time to try VMWare on top of this. It could
be interesting to be able to provide CMOV or other instructions
to guest systems.

Here is the patch against 2.4.19-rc1.

Comments and criticisms welcome, but flames to /dev/null.

Cheers,
Willy


[-- Attachment #2: patch-2.4.19-rc1-emux86-0.2 --]
[-- Type: text/plain, Size: 21801 bytes --]

diff -ur 2.4.19-rc1/Documentation/Configure.help 2.4.19-rc1-emux86-0.2/Documentation/Configure.help
--- 2.4.19-rc1/Documentation/Configure.help	Sun Jun 30 03:21:56 2002
+++ 2.4.19-rc1-emux86-0.2/Documentation/Configure.help	Sun Jun 30 06:05:58 2002
@@ -3985,6 +3985,66 @@
   Select this for a Pentium Classic processor with the RDTSC (Read
   Time Stamp Counter) instruction for benchmarking.
 
+486 emulation
+CONFIG_CPU_EMU486
+  When used on a 386, Linux can emulate 3 instructions from the 486 set.
+  This allows user space programs compiled for 486 to run on a 386
+  without crashing with a SIGILL. As any emulation, performance will be
+  very low, but since these instruction are not often used, this might
+  not hurt. The emulated instructions are :
+     - bswap (does the same as htonl())
+     - cmpxchg (used in multi-threading, mutex locking)
+     - xadd (rarely used)
+
+  Note that this can also allow Step-A 486's to correctly run multi-thread
+  applications since cmpxchg has a wrong opcode on this early CPU.
+
+  Don't use this to enable multi-threading on an SMP machine, the lock
+  atomicity can't be guaranted !
+
+  Although it's highly preferable that you only execute programs targetted
+  for your CPU, it may happen that, consecutively to a hardware replacement,
+  or during rescue of a damaged system, you have to execute such programs
+  on an inadapted processor. In this case, this option will help you get
+  your programs working, even if they will be slower.
+
+  It is recommended that you say N here in any case, except for the
+  kernels that you will use on your rescue disks.
+  
+  This option should not be left on by default, because it means that
+  you execute a program not targetted for your CPU. You should recompile
+  your applications whenever possible.
+
+  If you are not sure, say N.
+
+Pentium-Pro CMOV emulation
+CONFIG_CPU_EMU686
+  Intel Pentium-Pro processor brought a new set of instructions borrowed
+  from RISC processors, which permit to write many simple conditionnal
+  blocks without a branch instruction, thus being faster. They are supported
+  on all PentiumII, PentiumIII, Pentium4 and Celerons to date. GCC generates
+  these instructions when "-march=i686" is specified. There is an ever
+  increasing number of programs compiled with this option, that will simply
+  crash on 386/486/Pentium/AmdK6 and others when trying to execute the
+  faulty instruction.
+
+  Although it's highly preferable that you only execute programs targetted
+  for your CPU, it may happen that, consecutively to a hardware replacement,
+  or during rescue of a damaged system, you have to execute such programs
+  on an inadapted processor. In this case, this option will help you keep
+  your programs working, even if some may be noticeably slower : an overhead
+  of 1us has been measured on a k6-2/450 (about 450 cycles).
+
+  It is recommended that you say N here in any case, except for the
+  kernels that you will use on your rescue disks. This emulation typically
+  increases a bzImage with 500 bytes.
+  
+  This option should not be left on by default, because it means that
+  you execute a program not targetted for your CPU. You should recompile
+  your applications whenever possible.
+
+  If you are not sure, say N.
+
 32-bit PDC
 CONFIG_PDC_NARROW
   Saying Y here will allow developers with a C180, C200, C240, C360,
diff -ur 2.4.19-rc1/arch/i386/config.in 2.4.19-rc1-emux86-0.2/arch/i386/config.in
--- 2.4.19-rc1/arch/i386/config.in	Sun Jun 30 03:22:07 2002
+++ 2.4.19-rc1-emux86-0.2/arch/i386/config.in	Sun Jun 30 03:22:38 2002
@@ -54,6 +54,8 @@
    define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y
    define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n
    define_bool CONFIG_X86_PPRO_FENCE y
+   bool '486 emulation' CONFIG_CPU_EMU486
+   dep_bool 'Pentium-Pro CMOV emulation' CONFIG_CPU_EMU686 $CONFIG_CPU_EMU486
 else
    define_bool CONFIG_X86_WP_WORKS_OK y
    define_bool CONFIG_X86_INVLPG y
@@ -63,6 +65,7 @@
    define_bool CONFIG_X86_POPAD_OK y
    define_bool CONFIG_RWSEM_GENERIC_SPINLOCK n
    define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y
+   bool 'Pentium-Pro CMOV emulation' CONFIG_CPU_EMU686
 fi
 if [ "$CONFIG_M486" = "y" ]; then
    define_int  CONFIG_X86_L1_CACHE_SHIFT 4
diff -ur 2.4.19-rc1/arch/i386/kernel/traps.c 2.4.19-rc1-emux86-0.2/arch/i386/kernel/traps.c
--- 2.4.19-rc1/arch/i386/kernel/traps.c	Sun Jun 30 03:22:15 2002
+++ 2.4.19-rc1-emux86-0.2/arch/i386/kernel/traps.c	Sun Jun 30 05:33:18 2002
@@ -85,6 +85,24 @@
 asmlinkage void spurious_interrupt_bug(void);
 asmlinkage void machine_check(void);
 
+#if defined(CONFIG_CPU_EMU486) || defined(CONFIG_CPU_EMU686)
+asmlinkage void do_general_protection(struct pt_regs * regs, long error_code);
+
+/* gives the address of any register member in a struct pt_regs */
+static const int reg_ofs[8] = {
+	(int)&((struct pt_regs *)0)->eax,
+	(int)&((struct pt_regs *)0)->ecx,
+	(int)&((struct pt_regs *)0)->edx,
+	(int)&((struct pt_regs *)0)->ebx,
+	(int)&((struct pt_regs *)0)->esp,
+	(int)&((struct pt_regs *)0)->ebp,
+	(int)&((struct pt_regs *)0)->esi,
+	(int)&((struct pt_regs *)0)->edi
+};
+
+#define REG_PTR(regs, reg) ((unsigned long *)(((void *)(regs)) + reg_ofs[reg]))
+#endif
+
 int kstack_depth_to_print = 24;
 
 
@@ -371,11 +389,514 @@
 	do_trap(trapnr, signr, str, 1, regs, error_code, &info); \
 }
 
+#if defined(CONFIG_CPU_EMU486) || defined(CONFIG_CPU_EMU686)
+/* This code can be used to allow old 386's to hopefully correctly execute some
+ * code which was originally compiled for a 486, and to allow CMOV-disabled
+ * processors to emulate CMOV instructions. In user space, only 3 instructions
+ * have been added between the 386 the 486 :
+ *    - BSWAP reg		performs exactly htonl())
+ *    - CMPXCHG reg/mem, reg	used for mutex locking
+ *    - XADD reg/mem, reg	not encountered yet.
+ *
+ * Warning: this will NEVER allow a kernel compiled for a 486 to boot on a 386,
+ * neither will it allow a CMOV-optimized kernel to run on a processor without
+ * CMOV ! It will only help to port programs, or save you on a rescue disk, but
+ * for performance's sake, it's far better to recompile.
+ *
+ * Tests patterns have been submitted to this code on a 386, and it now seems
+ * OK. If you think you've found a bug, please report it to
+ * Willy Tarreau <willy@meta-x.org>.
+ */
+
+/* [modrm_address] returns a pointer to a user-space location by decoding the
+ * mod/rm byte and the bytes at <from>, which point to the mod/reg/rm byte.
+ * This must only be called if modrm indicates memory and not register. The
+ * <from> parameter is updated when bytes are read.
+ */
+static void *modrm_address(struct pt_regs *regs, unsigned char **from,
+                           char w, char bit32, unsigned char modrm)
+{
+	unsigned long int offset=0;
+	unsigned char sib;
+
+	/* we'll behave differently in 16 and 32 bits mode */
+	if (bit32) {  /* 32-bits addressing mode (default) */
+		if ((modrm & 0xC7) == 0x05) { /* 32 bits offset and nothing more */
+			offset = **from + (((int)*(*from+1)) << 8) +
+				(((int)*(*from+2)) << 16) + (((int)*(*from+3)) << 24);
+			*from += 4;
+			return (void *)offset;
+		}
+		
+		if ((modrm & 0x07) != 0x04)
+			offset += *REG_PTR(regs, modrm & 0x07);
+		else {
+			/* SIB byte is present and must be used */
+			sib=*(*from)++;
+
+			/* index * scale */
+			if (((sib >> 3) & 0x07) != 0x04)
+				offset += *REG_PTR(regs, (sib >> 3) & 0x07) << (sib >> 6);
+
+			if (((sib & 0x07) == 5) && ((modrm & 0xC0) == 0)) {
+				/* base off32 + scaled index */
+				offset += **from + (((int)*(*from+1)) << 8) +
+					(((int)*(*from+2)) << 16) + (((int)*(*from+3)) << 24);
+				*from += 4;
+				return (void *)offset;
+			}
+			/* base */
+			offset += *REG_PTR(regs, sib & 0x07);
+		}
+	
+		if ((modrm & 0xC0) == 0x40) {  /* 8 bits signed offset */
+			offset += *(signed char *)((*from)++);
+		} else if ((modrm & 0xC0) == 0x80) { /* 32 bits unsigned offset */
+			offset += **from + (((int)*(*from+1)) << 8) +
+				(((int)*(*from+2)) << 16) + (((int)*(*from+3)) << 24);
+			*from += 4;
+		}
+		return (void *)offset;  /* return the 32 bits offset */
+	} else { /* 16-bits addressing mode */
+		/* handle special case now */
+		if ((modrm & 0xC7) == 0x06) { /* 16 bits offset */
+			offset = **from + (((int)*(*from+1)) << 8);
+			*from += 2;
+			return (void *)offset;
+		}
+
+		if ((modrm & 4) == 0)
+			offset += (modrm & 2) ? regs->ebp : regs->ebx;
+		if ((modrm & 7) < 6)
+			offset += (modrm & 1) ? regs->edi : regs->esi;
+		else if ((modrm & 7) == 6)  /* bp */
+			offset += regs->ebp;
+		else if ((modrm & 7) == 7)  /* bx */
+			offset += regs->ebx;
+
+		/* now, let's include 8/16 bits offset */
+		if ((modrm & 0xC0) == 0x40) {  /* 8 bits signed offset */
+			offset += *(signed char *)((*from)++);
+		} else if ((modrm & 0xC0) == 0x80) { /* 16 bits unsigned offset */
+			offset += **from + (((int)*(*from+1)) << 8);
+			*from += 2;
+		}
+		return (void *)(offset & 0xFFFF);
+	}
+}
+
+
+/*
+ * skip_modrm() computes the EIP value of next instruction from the
+ * pointer <from> which points to the first byte after the mod/rm byte.
+ * Its purpose is to implement a fast alternative to modrm_address()
+ * when offset value is not needed.
+ */
+static void *skip_modrm(unsigned char *from, char bit32, unsigned char modrm)
+{
+	if (bit32) { /* 32-bits addressing mode (default) */
+		if ((modrm & 0xC7) == 0x05)  /* 32 bits offset and nothing more */
+			return from + 4;
+	
+		if ((modrm & 0x07) == 0x04) {
+			if (((*from++ & 0x07) == 5) && ((modrm & 0xC0) == 0)) /* base+sib */
+				return from + 4;
+		}
+	}
+	else if ((modrm & 0xC7) == 0x06) /* 16 bits offset */
+		return from + 2;
+
+	/* now, let's include 8/16/32 bits offset */
+	if ((modrm & 0xC0) == 0x40)		/* 8 bits signed offset */
+		return from + 1;
+	else if ((modrm & 0xC0) == 0x80)	/* 16/32 bits unsigned offset */
+		return from + (2 << bit32);
+	else
+		return from;
+}
+
+
+/* [reg_address] returns a pointer to a register in the regs struct, depending
+ * on <w> (byte/word) and reg. Since the caller knows about <w>, it's
+ * responsible for understanding the result as a byte, word or dword pointer.
+ */
+static inline void *reg_address(struct pt_regs *regs, char w, unsigned char reg)
+{
+	if (w)
+		/* 16/32 bits mode */
+		return REG_PTR(regs, reg & 7);
+	else
+		/* 8 bits mode : al,cl,dl,bl,ah,ch,dh,bh */
+		return ((reg & 4) >> 2) + (unsigned char *)REG_PTR(regs, reg & 3);
+
+	/* this is set just to prevent the compiler from complaining */
+	return NULL;
+}
+
+/* [do_invalid_op] is called by exception 6 after an invalid opcode has been
+ * encountered. It will decode the prefixes and the instruction code, to try
+ * to emulate it, and will send a SIGILL or SIGSEGV to the process if not
+ * possible.
+ */
+asmlinkage void do_invalid_op(struct pt_regs * regs, long error_code)
+{
+	enum {
+		PREFIX_ES=1,
+		PREFIX_CS=2,
+		PREFIX_SS=4,
+		PREFIX_DS=8,
+		PREFIX_FS=16,
+		PREFIX_GS=32,
+		PREFIX_SEG=63,  /* any seg */
+		PREFIX_D32=64,
+		PREFIX_A32=128,
+		PREFIX_LOCK=256,
+		PREFIX_REPN=512,
+		PREFIX_REP=1024
+	};
+
+	unsigned int prefixes;
+	unsigned char *eip = (unsigned char *)regs->eip;
+	unsigned long int *src, *dst;
+
+	/* we'll first read all known opcode prefixes, and discard obviously
+	   invalid combinations.*/
+	prefixes=0;
+	while (1) {
+		if ((*eip & 0xfc) == 0x64) {
+			switch (*eip) {
+			case 0x66: /* Operand switches 16/32 bits */
+				if (prefixes & PREFIX_D32)
+					goto invalid_opcode;
+				prefixes |= PREFIX_D32;
+				eip++;
+				continue;
+			case 0x67: /* Address switches 16/32 bits */
+				if (prefixes & PREFIX_A32)
+					goto invalid_opcode;
+				prefixes |= PREFIX_A32;
+				eip++;
+				continue;
+			case 0x64: /* FS: */
+				if (prefixes & PREFIX_SEG)
+					goto invalid_opcode;
+				prefixes |= PREFIX_FS;
+				eip++;
+				continue;
+			case 0x65: /* GS: */
+				if (prefixes & PREFIX_SEG)
+					goto invalid_opcode;
+				prefixes |= PREFIX_GS;
+				eip++;
+				continue;
+			}
+		}
+		else if ((*eip & 0xfc) == 0xf0) {
+			switch (*eip) {
+			case 0xF0: /* lock */
+				if (prefixes & PREFIX_LOCK)
+					goto invalid_opcode;
+				prefixes |= PREFIX_LOCK;
+#ifdef CONFIG_SMP
+				/* if we're in SMP mode, a missing lock can lead to problems in
+				 * multi-threaded environment. We must send a warning. In UP,
+				 * however, this should have no effect.
+				 */
+				printk(KERN_WARNING "Warning ! LOCK prefix found at EIP=0x%08x in"
+				       "process %d(%s), has no effect before a software-emulated"
+				       "instruction\n", regs->eip, current->pid, current->comm);
+#endif
+				eip++;
+				continue;
+			case 0xF2: /* repne */
+				if (prefixes & (PREFIX_REPN | PREFIX_REP))
+					goto invalid_opcode;
+				prefixes |= PREFIX_REPN;
+				eip++;
+				continue;
+			case 0xF3: /* rep */
+				if (prefixes & (PREFIX_REP | PREFIX_REPN))
+					goto invalid_opcode;
+				prefixes |= PREFIX_REP;
+				eip++;
+				continue;
+			}
+		}
+		else if ((*eip & 0xe7) == 0x26) {
+			switch (*eip) {
+			case 0x26: /* ES: */
+				if (prefixes & PREFIX_SEG)
+					goto invalid_opcode;
+				prefixes |= PREFIX_ES;
+				eip++;
+				continue;
+			case 0x2E: /* CS: */
+				if (prefixes & PREFIX_SEG)
+					goto invalid_opcode;
+				prefixes |= PREFIX_CS;
+				eip++;
+				continue;
+			case 0x36: /* SS: */
+				if (prefixes & PREFIX_SEG)
+					goto invalid_opcode;
+				prefixes |= PREFIX_SS;
+				eip++;
+				continue;
+			case 0x3E: /* DS: */
+				if (prefixes & PREFIX_SEG)
+					goto invalid_opcode;
+				prefixes |= PREFIX_DS;
+				eip++;
+				continue;
+			}
+		}
+		/* if this opcode has not been processed, it's not a prefix. */
+		break;
+	}
+
+	/* now we know about all the prefixes */
+
+#if defined(CONFIG_CPU_EMU686)
+	/* here, we'll try to emulate the CMOV* instructions, which gcc blindly
+	 * generates when specifying -march=i686, even though the processor flags
+	 * must be checked against support for these instructions.
+	 */
+	if ((*eip == 0x0F) && ((*(eip+1) & 0xF0) == 0x40)) {  /* CMOV* */
+		unsigned char cond, ncond, reg, modrm;
+		unsigned long flags;
+
+		/* to optimize processing, we'll associate a flag mask to each opcode.
+		 * If the EFLAGS value ANDed with this mask is not null, then the cond
+		 * is met. One exception is CMOVL which is true if SF != OF. For this
+		 * purpose, we'll make a fake flag 'SFOF' (unused bit 3) which equals
+		 * SF^OF, so that CMOVL is true if SFOF != 0.
+		 */
+		static short unsigned cmov_flags[8] = {
+			0x0800, /* CMOVO	=> OF */
+			0x0001, /* CMOVB	=> CF */
+			0x0040, /* CMOVE	=> ZF */
+			0x0041, /* CMOVBE	=> CF | ZF */
+			0x0080, /* CMOVS	=> SF */
+			0x0004, /* CMOVP	=> PF */
+			0x0008, /* CMOVL	=> SF^OF */
+			0x0048, /* CMOVLE	=> SF^OF | ZF */
+		};
+
+#ifdef INVALID_OP_EVEN_IF_CPU_WOULD_ACCEPT
+		if (prefixes & (PREFIX_REP | PREFIX_REPN))
+			goto invalid_opcode;
+#endif
+
+		flags =	regs->eflags & 0x08C5; /* OF, SF, ZF, PF, CF */
+
+		/* SFOF (flags_3) <= OF(flags_11) ^ SF(flags_7) */
+		flags |= ((flags ^ (flags >> 4)) >> 4) & 0x8;
+
+		cond  = *(eip+1) & 0x0F;
+		ncond = cond & 1;	/* condition is negated */
+		cond >>= 1;
+		ncond ^= !!(flags & cmov_flags[cond]);
+		/* ncond is now true if the cond matches the opcode */
+
+		modrm = *(eip+2);
+		eip += 3; /* skips all the opcodes */
+
+		if (!ncond) {
+			/* condition is not valid, skip the instruction and do nothing */
+			regs->eip = (unsigned long)skip_modrm(eip, !(prefixes & PREFIX_A32), modrm);
+			return;
+		}
+
+		/* we'll have to do the work */
+		reg = (modrm >> 3) & 7;
+		modrm &= 0xC7;
+
+		/* condition is valid */
+		dst = reg_address(regs, 1, reg);
+		if ((modrm & 0xC0) == 0xC0) { /* register to register */
+			src = reg_address(regs, 1, modrm & 0x07);
+		}
+		else {
+			src = modrm_address(regs, &eip, 1, !(prefixes & PREFIX_A32), modrm);
+			/* we must verify that src is valid for this task */
+			if ((prefixes & (PREFIX_FS | PREFIX_GS)) ||
+			    verify_area(VERIFY_WRITE, (void *)src, ((prefixes & PREFIX_D32) ? 2 : 4))) {
+				do_general_protection(regs, error_code);
+				return;
+			}
+		}
+	
+		if (!(prefixes & PREFIX_D32)) /* 32 bits operands */
+			*(unsigned long*)dst = *(unsigned long*)src;
+		else
+			*(unsigned short*)dst = *(unsigned short*)src;
+
+		regs->eip = (unsigned long)eip;
+		return;
+	}
+#endif /* CONFIG_CPU_EMU686 */
+
+#if defined(CONFIG_CPU_EMU486)
+	/* we'll verify if this is a BSWAP opcode, main source of SIGILL on 386's */
+	if ((*eip == 0x0F) && ((*(eip+1) & 0xF8) == 0xC8)) {  /* BSWAP */
+		unsigned char w, reg, modrm;
+
+#ifdef INVALID_OP_EVEN_IF_CPU_WOULD_ACCEPT
+		if (prefixes & (PREFIX_REP | PREFIX_REPN))
+			goto invalid_opcode;
+#endif	
+
+		reg = *(eip+1) & 0x07;
+
+		src=reg_address(regs, 1, reg);
+
+		__asm__ __volatile__ (
+				      "xchgb %%al, %%ah\n\t"
+				      "roll $16, %%eax\n\t"
+				      "xchgb %%al, %%ah\n\t"
+				      : "=a" (*(unsigned long *)src)
+				      : "a" (*(unsigned long *)src));
+		regs->eip = (unsigned long)((char *)eip + 2);
+		return;
+	}
+
+	/* we'll also try to emulate the CMPXCHG instruction (used in mutex locks).
+	   This instruction is often locked, but it's not possible to put a lock
+	   here. Anyway, I don't believe that there are lots of multiprocessors
+	   386 out there ...
+	*/
+	if ((*eip == 0x0F) && ((*(eip+1) & 0xFE) == 0xB0)) {  /* CMPXCHG */
+		unsigned char w, reg, modrm;
+
+#ifdef INVALID_OP_EVEN_IF_CPU_WOULD_ACCEPT
+		if (prefixes & (PREFIX_REP | PREFIX_REPN))
+			goto invalid_opcode;
+#endif
+		w=*(eip+1) & 1;
+		modrm = *(eip+2);
+		reg = (modrm >> 3) & 7;
+		modrm &= 0xC7;
+		eip += 3; /* skips all the opcodes */
+
+		dst = reg_address(regs, w, reg);
+		if ((modrm & 0xC0) == 0xC0) /* register to register */
+			src = reg_address(regs, w, modrm & 0x07);
+		else {
+			src = modrm_address(regs, &eip, w, !(prefixes & PREFIX_A32), modrm);
+			/* we must verify that src is valid for this task */
+			if ((prefixes & (PREFIX_FS | PREFIX_GS)) ||
+			    verify_area(VERIFY_WRITE, (void *)src, (w?((prefixes & PREFIX_D32)?2:4):1))) {
+				do_general_protection(regs, error_code);
+				return;
+			}
+		}
+
+		if (!w) { /* 8 bits operands */
+			if ((unsigned char)regs->eax == *(unsigned char*)src) {
+				*(unsigned char*)src = *(unsigned char*)dst;
+				regs->eflags |= 0x40;  /* set Zero Flag */
+			}
+			else {
+				regs->eflags &= ~0x40;  /* clear Zero Flag */
+				*(unsigned char*)&(regs->eax) = *(unsigned char*)src;
+			}
+		}
+		else if (prefixes & PREFIX_D32) { /* 16 bits operands */
+			if ((unsigned short)regs->eax == *(unsigned short*)src) {
+				*(unsigned short*)src = *(unsigned short*)dst;
+				regs->eflags |= 0x40;  /* set Zero Flag */
+			}
+			else {
+				regs->eflags &= ~0x40;  /* clear Zero Flag */
+				*(unsigned short*)&regs->eax = *(unsigned short*)src;
+			}
+		}
+		else { /* 32 bits operands */
+			if ((unsigned long)regs->eax == *(unsigned long*)src) {
+				*(unsigned long*)src = *(unsigned long*)dst;
+				regs->eflags |= 0x40;  /* set Zero Flag */
+			}
+			else {
+				regs->eflags &= ~0x40;  /* clear Zero Flag */
+				regs->eax = *(unsigned long*)src;
+			}
+		}
+		regs->eip = (unsigned long)eip;
+		return;
+	}
+
+	/* we'll also try to emulate the XADD instruction (not very common) */
+	if ((*eip == 0x0F) && ((*(eip+1) & 0xFE) == 0xC0)) {  /* XADD */
+		unsigned char w, reg, modrm;
+		unsigned long op1, op2;
+
+#ifdef INVALID_OP_EVEN_IF_CPU_WOULD_ACCEPT
+		if (prefixes & (PREFIX_REP | PREFIX_REPN))
+			goto invalid_opcode;
+#endif
+		w = *(eip + 1) & 1;
+		modrm = *(eip + 2);
+		reg = (modrm >> 3) & 7;
+		modrm &= 0xC7;
+		eip += 3; /* skips all the opcodes */
+
+		dst = reg_address(regs, w, reg);
+		if ((modrm & 0xC0) == 0xC0) /* register to register */
+			src = reg_address(regs, w, modrm & 0x07);
+		else {
+			src = modrm_address(regs, &eip, w,! (prefixes & PREFIX_A32), modrm);
+			/* we must verify that src is valid for this task */
+			if ((prefixes & (PREFIX_FS | PREFIX_GS)) ||
+			    verify_area(VERIFY_WRITE, (void *)src, (w?((prefixes & PREFIX_D32)?2:4):1))) {
+				do_general_protection(regs, error_code);
+				return;
+			}
+		}
+
+		if (!w) { /* 8 bits operands */
+			op1=*(unsigned char*)src;
+			op2=*(unsigned char*)dst;
+			*(unsigned char*)src = op1 + op2;
+			*(unsigned char*)dst = op1;
+		}
+		else if (prefixes & PREFIX_D32) { /* 16 bits operands */
+			op1=*(unsigned short*)src;
+			op2=*(unsigned short*)dst;
+			*(unsigned short*)src = op1 + op2;
+			*(unsigned short*)dst = op1;
+		}
+		else { /* 32 bits operands */
+			op1=*(unsigned long*)src;
+			op2=*(unsigned long*)dst;
+			*(unsigned long*)src = op1 + op2;
+			*(unsigned long*)dst = op1;
+		}
+		regs->eip = (unsigned long)eip;
+		return;
+	}
+#endif /* CONFIG_CPU_EMU486 */
+	/* it's a case we can't handle. Unknown opcode or too many prefixes. */
+ invalid_opcode:
+#ifdef CONFIG_CPU_EMU486_DEBUG
+	printk(KERN_DEBUG "do_invalid_op() : invalid opcode detected @%p : %02x %02x ...\n", eip, eip[0], eip[1]);
+#endif
+	current->thread.error_code = error_code;
+	current->thread.trap_no = 6;
+	force_sig(SIGILL, current);
+	die_if_kernel("invalid operand",regs,error_code);
+}
+
+#endif  /* CONFIG_CPU_EMU486 || CONFIG_CPU_EMU686 */
+
 DO_VM86_ERROR_INFO( 0, SIGFPE,  "divide error", divide_error, FPE_INTDIV, regs->eip)
 DO_VM86_ERROR( 3, SIGTRAP, "int3", int3)
 DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow)
 DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds)
+
+#if !defined(CONFIG_CPU_EMU486) && !defined(CONFIG_CPU_EMU686)
 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)

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [ANNOUNCE] CMOV emulation for 2.4.19-rc1
  2002-07-01 13:58 ` Denis Vlasenko
@ 2002-07-01 13:03   ` willy tarreau
  2002-07-01 15:55     ` Bill Davidsen
                       ` (2 more replies)
  0 siblings, 3 replies; 16+ messages in thread
From: willy tarreau @ 2002-07-01 13:03 UTC (permalink / raw)
  To: vda, Willy TARREAU, willy, linux-kernel, Ronald.Wahl

Hello Denis,

> This code is performance critical.
> With this in mind,

Yes and no. In fact, I first wanted to code some
parts in assembler because GCC is sub-optimal
on bit-fields calculations. But then, I realized that
I could save, say 10 cycles, while the trap costs
about 400 cycles.

> +static void *modrm_address(struct pt_regs *regs,
> unsigned char **from, char w, char bit32,
> w seems to be unused

Well, you're right, it's not used anymore. It was
used to check if the instruction applies to a byte
or a word.

> Why? i86 can do unaligned accesses:
> 	offset = *(u32*)(*from); *from += 4;

that's simply because I'm not sure if the kernel
runs with AC flag on or off. I quickly checked
that it's OK from userland.

> +       /* we'll first read all known opcode
> prefixes, and discard obviously
> +          invalid combinations.*/
> Prefixes are rarer than plain opcodes. Maybe:
> 1.check opcodes
> 2.no? check prefixes
> 3.yes? check opcodes again

perhaps a good idea, I don't know. I think the
current code doesn't cost much in case there
is no prefix (only 3 failed IFs). I also wrote a
prefix bitmap to directly map opcodes to prefixes/
known instructions, but thought it was not really
usefull and costed 32 bytes, so I removed it.
 
> +                               if (prefixes &
PREFIX_LOCK)
> +                                       goto
invalid_opcode;
> Cycles burned for nothing.
> What harm can be done if we managed to emulate
> 	lock lock lock xadd a,b

simply avoid that someone filling 16Meg of code
with prefixes spends all his time in the kernel.
When I did this, I had checked and noticed that
an instruction with a repeated prefix is invalid.

> +                       case 0xF3: /* rep */
> These prefixes are invalid for commands we emulate.
> No GCC will ever generate such code, don't check for
> them.

Yes, I agree with you. The only instructions that
support these prefixes are stable and it's not likely
that others will come in the future, so we may
handle them in the general case of invalid
instruction.

> eliminate 'reg = (modrm >> 3) & 7', move calculation
> to <<<1
> eliminate 'modrm &= 0xC7', move to <<<2 or drop it
> (I think modrm_adr() will work fine with unmasked
> modrm)

I know this part can be reworked. I just need a bit
of time to check redundant calculations between
the main function and modrm_addr(), and I think
I can simplify even more.

Like I said above, I didn't insist on optimizations,
I prefered to get a clear code first. If I want to
optimize, I think most of this will be assembler.

Thanks a lot for your feedback,
Willy


___________________________________________________________
Do You Yahoo!? -- Une adresse @yahoo.fr gratuite et en français !
Yahoo! Mail : http://fr.mail.yahoo.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [ANNOUNCE] CMOV emulation for 2.4.19-rc1
  2002-07-01 18:16     ` Denis Vlasenko
@ 2002-07-01 13:25       ` willy tarreau
  2002-07-02 20:00       ` Willy TARREAU
  1 sibling, 0 replies; 16+ messages in thread
From: willy tarreau @ 2002-07-01 13:25 UTC (permalink / raw)
  To: vda, Willy TARREAU, willy, linux-kernel, Ronald.Wahl

> Can you code up a "dummy" emulator (which just
> ignores any invalid opcode by doing eip+=3) and
> compare trap times of your emulator and dummy
> one for, say, CMOVC AL,AL? (with carry flag
> cleared)

I may do this. Don't have the time at the moment,
but perhaps this evening...

cheers,
Willy


___________________________________________________________
Do You Yahoo!? -- Une adresse @yahoo.fr gratuite et en français !
Yahoo! Mail : http://fr.mail.yahoo.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [ANNOUNCE] CMOV emulation for 2.4.19-rc1
  2002-06-30  4:39 [ANNOUNCE] CMOV emulation for 2.4.19-rc1 Willy TARREAU
@ 2002-07-01 13:58 ` Denis Vlasenko
  2002-07-01 13:03   ` willy tarreau
  2002-07-01 18:25 ` Denis Vlasenko
  1 sibling, 1 reply; 16+ messages in thread
From: Denis Vlasenko @ 2002-07-01 13:58 UTC (permalink / raw)
  To: Willy TARREAU, willy, linux-kernel, Ronald.Wahl

On 30 June 2002 02:39, Willy TARREAU wrote:
> Hi all,
>
> OK, I know that many people dislike this, but I know others
> who occasionally need it anyway. So I don't post it for general
> inclusion, but for interested people.

This code is performance critical.
With this in mind,

+static void *modrm_address(struct pt_regs *regs, unsigned char **from,
+                           char w, char bit32, unsigned char modrm)
+{
w seems to be unused

+                       offset = **from + (((int)*(*from+1)) << 8) +
+                               (((int)*(*from+2)) << 16) + (((int)*(*from+3)) << 24);
+                       *from += 4;
Why? i86 can do unaligned accesses:
	offset = *(u32*)(*from); *from += 4;
or even
	offset = *((u32*)(*from))++; //ugly isn't it?

+                               /* base off32 + scaled index */
+                               offset += **from + (((int)*(*from+1)) << 8) +
+                                       (((int)*(*from+2)) << 16) + (((int)*(*from+3)) << 24);
+                               *from += 4;
same

+               } else if ((modrm & 0xC0) == 0x80) { /* 32 bits unsigned offset */
+                       offset += **from + (((int)*(*from+1)) << 8) +
+                               (((int)*(*from+2)) << 16) + (((int)*(*from+3)) << 24);
+                       *from += 4;
same

+               if ((modrm & 0xC7) == 0x06) { /* 16 bits offset */
+                       offset = **from + (((int)*(*from+1)) << 8);
+                       *from += 2;
similar

+               } else if ((modrm & 0xC0) == 0x80) { /* 16 bits unsigned offset */
+                       offset += **from + (((int)*(*from+1)) << 8);
+                       *from += 2;
similar

+asmlinkage void do_invalid_op(struct pt_regs * regs, long error_code)
+{
...
+       /* we'll first read all known opcode prefixes, and discard obviously
+          invalid combinations.*/
Prefixes are rarer than plain opcodes. Maybe:
1.check opcodes
2.no? check prefixes
3.yes? check opcodes again

+                               if (prefixes & PREFIX_LOCK)
+                                       goto invalid_opcode;
Cycles burned for nothing.
What harm can be done if we managed to emulate
	lock lock lock xadd a,b
?
(same for all other prefixes)
(OTOH this way you can be sure while() will end sooner or later)

+                       case 0xF2: /* repne */
...
+                       case 0xF3: /* rep */
These prefixes are invalid for commands we emulate.
No GCC will ever generate such code, don't check for them.
(Comment them out if you like to retain the code)
This will also simplify
+               else if ((*eip & 0xfc) == 0xf0) {
+                       switch (*eip) {
+                       case 0xF0: /* lock */
down to single if().

+               reg = (modrm >> 3) & 7;
+               modrm &= 0xC7;
+
+               /* condition is valid */
+               dst = reg_address(regs, 1, reg);  <<<1
+               if ((modrm & 0xC0) == 0xC0) { /* register to register */
+                       src = reg_address(regs, 1, modrm & 0x07);
+               }
+               else {
+                       src = modrm_address(regs, &eip, 1, !(prefixes & PREFIX_A32), modrm); <<<2
eliminate 'reg = (modrm >> 3) & 7', move calculation to <<<1
eliminate 'modrm &= 0xC7', move to <<<2 or drop it
(I think modrm_adr() will work fine with unmasked modrm)
--
vda

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [ANNOUNCE] CMOV emulation for 2.4.19-rc1
  2002-07-01 13:03   ` willy tarreau
@ 2002-07-01 15:55     ` Bill Davidsen
  2002-07-02 10:46       ` Denis Vlasenko
  2002-07-01 16:25     ` Gabriel Paubert
  2002-07-01 18:16     ` Denis Vlasenko
  2 siblings, 1 reply; 16+ messages in thread
From: Bill Davidsen @ 2002-07-01 15:55 UTC (permalink / raw)
  To: willy tarreau; +Cc: vda, Willy TARREAU, willy, linux-kernel, Ronald.Wahl

On Mon, 1 Jul 2002, [iso-8859-1] willy tarreau wrote:

> Like I said above, I didn't insist on optimizations,
> I prefered to get a clear code first. If I want to
> optimize, I think most of this will be assembler.

This sounds good, the idea is that it should work at all, clarity is good,
I can't imagine anyone running this long term instead of building a
compile with the right machine type.

-- 
bill davidsen <davidsen@tmr.com>
  CTO, TMR Associates, Inc
Doing interesting things with little computers since 1979.


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [ANNOUNCE] CMOV emulation for 2.4.19-rc1
  2002-07-01 13:03   ` willy tarreau
  2002-07-01 15:55     ` Bill Davidsen
@ 2002-07-01 16:25     ` Gabriel Paubert
  2002-07-01 17:08       ` willy tarreau
  2002-07-01 18:16     ` Denis Vlasenko
  2 siblings, 1 reply; 16+ messages in thread
From: Gabriel Paubert @ 2002-07-01 16:25 UTC (permalink / raw)
  To: willy tarreau, linux-kernel

willy tarreau wrote:
> Hello Denis,
> 
> 
>>This code is performance critical.
>>With this in mind,
> 
> 
> Yes and no. In fact, I first wanted to code some
> parts in assembler because GCC is sub-optimal
> on bit-fields calculations. But then, I realized that
> I could save, say 10 cycles, while the trap costs
> about 400 cycles.
> 
> 
>>+static void *modrm_address(struct pt_regs *regs,
>>unsigned char **from, char w, char bit32,
>>w seems to be unused
> 
> 
> Well, you're right, it's not used anymore. It was
> used to check if the instruction applies to a byte
> or a word.
> 
> 
>>Why? i86 can do unaligned accesses:
>>	offset = *(u32*)(*from); *from += 4;
> 
> 
> that's simply because I'm not sure if the kernel
> runs with AC flag on or off. I quickly checked
> that it's OK from userland.

AC is only checked when running at CPL==3, i.e.,
you'll never get an alignment trap in the kernel.

	Gabriel




^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [ANNOUNCE] CMOV emulation for 2.4.19-rc1
  2002-07-01 16:25     ` Gabriel Paubert
@ 2002-07-01 17:08       ` willy tarreau
  0 siblings, 0 replies; 16+ messages in thread
From: willy tarreau @ 2002-07-01 17:08 UTC (permalink / raw)
  To: Gabriel Paubert, linux-kernel

> > that's simply because I'm not sure if the kernel
> > runs with AC flag on or off. I quickly checked
> > that it's OK from userland.
> 
> AC is only checked when running at CPL==3, i.e.,
> you'll never get an alignment trap in the kernel.

thanks for the tip, I think that with all the feedback
I got, I could rewrite it more cleanly ;-)

Cheers,
Willy


___________________________________________________________
Do You Yahoo!? -- Une adresse @yahoo.fr gratuite et en français !
Yahoo! Mail : http://fr.mail.yahoo.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [ANNOUNCE] CMOV emulation for 2.4.19-rc1
  2002-07-01 13:03   ` willy tarreau
  2002-07-01 15:55     ` Bill Davidsen
  2002-07-01 16:25     ` Gabriel Paubert
@ 2002-07-01 18:16     ` Denis Vlasenko
  2002-07-01 13:25       ` willy tarreau
  2002-07-02 20:00       ` Willy TARREAU
  2 siblings, 2 replies; 16+ messages in thread
From: Denis Vlasenko @ 2002-07-01 18:16 UTC (permalink / raw)
  To: willy tarreau, Willy TARREAU, willy, linux-kernel, Ronald.Wahl

On 1 July 2002 11:03, willy tarreau wrote:
> Hello Denis,
>
> > This code is performance critical.
> > With this in mind,
>
> Yes and no. In fact, I first wanted to code some
> parts in assembler because GCC is sub-optimal
> on bit-fields calculations. But then, I realized that
> I could save, say 10 cycles, while the trap costs
> about 400 cycles.

Can you code up a "dummy" emulator (which just ignores
any invalid opcode by doing eip+=3) and compare trap times
of your emulator and dummy one for, say, CMOVC AL,AL?
(with carry flag cleared)
--
vda

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [ANNOUNCE] CMOV emulation for 2.4.19-rc1
  2002-06-30  4:39 [ANNOUNCE] CMOV emulation for 2.4.19-rc1 Willy TARREAU
  2002-07-01 13:58 ` Denis Vlasenko
@ 2002-07-01 18:25 ` Denis Vlasenko
  1 sibling, 0 replies; 16+ messages in thread
From: Denis Vlasenko @ 2002-07-01 18:25 UTC (permalink / raw)
  To: Willy TARREAU, linux-kernel, Ronald.Wahl

On 30 June 2002 02:39, Willy TARREAU wrote:
> Hi all,
>
> OK, I know that many people dislike this, but I know others
> who occasionally need it anyway. So I don't post it for general
> inclusion, but for interested people.

+       if ((*eip == 0x0F) && ((*(eip+1) & 0xF0) == 0x40)) {  /* CMOV* */
...
+       if ((*eip == 0x0F) && ((*(eip+1) & 0xF8) == 0xC8)) {  /* BSWAP */
...
+       if ((*eip == 0x0F) && ((*(eip+1) & 0xFE) == 0xB0)) {  /* CMPXCHG */
...
+       if ((*eip == 0x0F) && ((*(eip+1) & 0xFE) == 0xC0)) {  /* XADD */

You may check for 0x0F only once:

	if(*eip!=0x0f) goto invalid_opcode;
	eip++;
--
vda

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [ANNOUNCE] CMOV emulation for 2.4.19-rc1
  2002-07-02 10:46       ` Denis Vlasenko
@ 2002-07-02  6:31         ` willy tarreau
  2002-07-02 12:03           ` Denis Vlasenko
  0 siblings, 1 reply; 16+ messages in thread
From: willy tarreau @ 2002-07-02  6:31 UTC (permalink / raw)
  To: vda, Bill Davidsen; +Cc: Willy TARREAU, willy, linux-kernel, Ronald.Wahl

> I see a potential problem here: if someone is
> running such kernel all the time, he can take huge
> performance penalty. 'Dunno why but on
> my box mailer does not run. It _crawls_'.
> Ordinary user may perceive it like 'Linux is slow'.
> What can be done to prevent this? Printk can go
> unnoticed in the log, as far as nothing actually
> breaks user won't look into the logs...

As I state in my former mail, I think it would be
good to at least implement statistics on the
number of traps for each instruction set, and
also be able to disable emulation, to check
whether a program correctly runs without
or not.

> 1.big red letters 'CMOV EMULATION' across the
screen? :-)
> 2.Scroll lock LED inverted each time CMOV is
triggered?
> 3.Printk at kernel init time:
> "Emergency rescue kernel with CMOV emulation: can
> be very slow, not for production use!" ?

perhaps not, but we could send an alert message on
the system console the first time an instruction is
emulated, with the program's name. But nothing more,
else we'll have to modify the task struct to include
counters, and I really don't want that.

> Of course (1) is a joke.

so (2) isn't ? and you talk about overhead of 3 IFs
:-)

Cheers,
Willy


___________________________________________________________
Do You Yahoo!? -- Une adresse @yahoo.fr gratuite et en français !
Yahoo! Mail : http://fr.mail.yahoo.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [ANNOUNCE] CMOV emulation for 2.4.19-rc1
  2002-07-01 15:55     ` Bill Davidsen
@ 2002-07-02 10:46       ` Denis Vlasenko
  2002-07-02  6:31         ` willy tarreau
  0 siblings, 1 reply; 16+ messages in thread
From: Denis Vlasenko @ 2002-07-02 10:46 UTC (permalink / raw)
  To: Bill Davidsen, willy tarreau
  Cc: Willy TARREAU, willy, linux-kernel, Ronald.Wahl

On 1 July 2002 13:55, Bill Davidsen wrote:
> On Mon, 1 Jul 2002, [iso-8859-1] willy tarreau wrote:
> > Like I said above, I didn't insist on optimizations,
> > I prefered to get a clear code first. If I want to
> > optimize, I think most of this will be assembler.
>
> This sounds good, the idea is that it should work at all, clarity is good,
> I can't imagine anyone running this long term instead of building a
> compile with the right machine type.

I see a potential problem here: if someone is running such kernel
all the time, he can take huge performance penalty. 'Dunno why but on
my box mailer does not run. It _crawls_'.
Ordinary user may perceive it like 'Linux is slow'.

What can be done to prevent this? Printk can go unnoticed in the log,
as far as nothing actually breaks user won't look into the logs...

1.big red letters 'CMOV EMULATION' across the screen? :-)
2.Scroll lock LED inverted each time CMOV is triggered?
3.Printk at kernel init time:
  "Emergency rescue kernel with CMOV emulation: can be very slow,
	not for production use!" ?

Of course (1) is a joke.
--
vda

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [ANNOUNCE] CMOV emulation for 2.4.19-rc1
  2002-07-02  6:31         ` willy tarreau
@ 2002-07-02 12:03           ` Denis Vlasenko
  0 siblings, 0 replies; 16+ messages in thread
From: Denis Vlasenko @ 2002-07-02 12:03 UTC (permalink / raw)
  To: willy tarreau, Bill Davidsen
  Cc: Willy TARREAU, willy, linux-kernel, Ronald.Wahl

On 2 July 2002 04:31, willy tarreau wrote:
> > 1.big red letters 'CMOV EMULATION' across the screen? :-)
> > 2.Scroll lock LED inverted each time CMOV is triggered?
> > 3.Printk at kernel init time:
> > "Emergency rescue kernel with CMOV emulation: can
> > be very slow, not for production use!" ?
>
> perhaps not, but we could send an alert message on
> the system console the first time an instruction is
> emulated, with the program's name. But nothing more,
> else we'll have to modify the task struct to include
> counters, and I really don't want that.
>
> > Of course (1) is a joke.
>
> so (2) isn't ? and you talk about overhead of 3 IFs

(2) is a half-joke, so to say. It woulda be funny to see
on lkml:

From: JRLuser@host.com
Subj: mailer crawls like on 286 and scroll LED blinks!!!

Everyone will immediately realize what's going on.
This will save us chasing non-existent performance 
problems. But it will cost _many_ cycles each fault.

Seriously, I think (3) is best. Why?

> an alert message on the system console
> the first time an instruction is emulated

is a printk(KERN_EMERG...), it can go unnoticed
too (all logs go to file only or user in X)
_and_ it incurs penalty on each fault.
 
> > 3.Printk at kernel init time:
> > "Emergency rescue kernel with CMOV emulation: can
> > be very slow, not for production use!" ?

is non-suppressable (unless user is stupid enough
to suppress ALL kernel boot messages) and have no penalty.
--
vda

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [ANNOUNCE] CMOV emulation for 2.4.19-rc1
  2002-07-01 18:16     ` Denis Vlasenko
  2002-07-01 13:25       ` willy tarreau
@ 2002-07-02 20:00       ` Willy TARREAU
  2002-07-03  0:36         ` jw schultz
  1 sibling, 1 reply; 16+ messages in thread
From: Willy TARREAU @ 2002-07-02 20:00 UTC (permalink / raw)
  To: Denis Vlasenko
  Cc: willy tarreau, Willy TARREAU, willy, linux-kernel, Ronald.Wahl

> Can you code up a "dummy" emulator (which just ignores
> any invalid opcode by doing eip+=3) and compare trap times
> of your emulator and dummy one for, say, CMOVC AL,AL?
> (with carry flag cleared)

The dummy emulator costs exactly 296 cycles (stable) on my
k6-2/450. It only adds 3 to eip then returns.

To check this, I compared 1 million iteriations of 10
consecutive cmove %eax,%eax with as much lea 0(%eax),%eax
(1 cycle, RAW dependancy, not parallelizable), and the
difference was exactly 660 ns/inst (297 cycles).

That said, I agree with you that it's worth optimizing a
bit, at least to stay closer to 300 cycles than to 450.
But that won't make emulated machines fast anyway.

One interesting note: I tested the prog on a VIA C3/533
Mhz. One native cmove %eax,%eax costs 56 cycles here ! (at
first, I even thought it was emulated). It's a shame to see
how these instructions have been implemented. May be they
flush the pipelines, write-backs, ... before the instruction.
BTW, cmov isn't reported in cpu_flags, perhaps to discourage
progs from using it ;-)

I will recode the stuff, and add two preventive messages:
 - at boot time : "warning: this kernel may emulate unsupported instructions. If you
   find it slow, please do dmesg."
 - at first emulation : "trap caught for instruction XXX, program XXX."

Cheers,
Willy


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [ANNOUNCE] CMOV emulation for 2.4.19-rc1
  2002-07-02 20:00       ` Willy TARREAU
@ 2002-07-03  0:36         ` jw schultz
  2002-07-18 19:15           ` Robert de Bath
  0 siblings, 1 reply; 16+ messages in thread
From: jw schultz @ 2002-07-03  0:36 UTC (permalink / raw)
  To: linux-kernel

On Tue, Jul 02, 2002 at 10:00:05PM +0200, Willy TARREAU wrote:
> > Can you code up a "dummy" emulator (which just ignores
> > any invalid opcode by doing eip+=3) and compare trap times
> > of your emulator and dummy one for, say, CMOVC AL,AL?
> > (with carry flag cleared)
> 
> The dummy emulator costs exactly 296 cycles (stable) on my
> k6-2/450. It only adds 3 to eip then returns.
> 
> To check this, I compared 1 million iteriations of 10
> consecutive cmove %eax,%eax with as much lea 0(%eax),%eax
> (1 cycle, RAW dependancy, not parallelizable), and the
> difference was exactly 660 ns/inst (297 cycles).
> 
> That said, I agree with you that it's worth optimizing a
> bit, at least to stay closer to 300 cycles than to 450.
> But that won't make emulated machines fast anyway.
> 
> One interesting note: I tested the prog on a VIA C3/533
> Mhz. One native cmove %eax,%eax costs 56 cycles here ! (at
> first, I even thought it was emulated). It's a shame to see
> how these instructions have been implemented. May be they
> flush the pipelines, write-backs, ... before the instruction.
> BTW, cmov isn't reported in cpu_flags, perhaps to discourage
> progs from using it ;-)
> 
> I will recode the stuff, and add two preventive messages:
>  - at boot time : "warning: this kernel may emulate unsupported instructions. If you
>    find it slow, please do dmesg."
>  - at first emulation : "trap caught for instruction XXX, program XXX."

Too often the "it seems slow" complaint comes after weeks or
even months of uptime.  How about the message every n times
an emulation is required.

	  if(!(emulation_count++ & 0xHHHH))
		printk(...);

wouldn't add too much more overhead than

	if (!emulation_notice)
	{
		emulation_notice = 1;
		printk(...);
	}

after all this is only supposed to happen under rescue
situations.  That way it will be sure to be in the logs and
maybe even on the console and we won't have to hunt for it.

Also, the message should say you are doing instruction
emulation.  "wrong model cpu, emulating instruction XXX" I
doubt indicating the program is helpful unless the tracking
is done per task or the printk every time you emulate.

-- 
________________________________________________________________
	J.W. Schultz            Pegasystems Technologies
	email address:		jw@pegasys.ws

		Remember Cernan and Schmitt

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [ANNOUNCE] CMOV emulation for 2.4.19-rc1
  2002-07-03  0:36         ` jw schultz
@ 2002-07-18 19:15           ` Robert de Bath
  2002-07-18 20:44             ` jw schultz
  0 siblings, 1 reply; 16+ messages in thread
From: Robert de Bath @ 2002-07-18 19:15 UTC (permalink / raw)
  To: jw schultz; +Cc: linux-kernel

On Tue, 2 Jul 2002, jw schultz wrote:

> wouldn't add too much more overhead than
>
> 	if (!emulation_notice)
> 	{
> 		emulation_notice = 1;
> 		printk(...);
> 	}
>
> after all this is only supposed to happen under rescue
> situations.  That way it will be sure to be in the logs and
> maybe even on the console and we won't have to hunt for it.
>
> Also, the message should say you are doing instruction
> emulation.  "wrong model cpu, emulating instruction XXX" I
> doubt indicating the program is helpful unless the tracking
> is done per task or the printk every time you emulate.

I'd suggest this message could be so frequent that you want to
link it's display to real time. Check the jiffy counter each
time and if it's been less that X seconds since the last message
just up a counter. Plus in the message say how many instructions
have been emulated since the last one ... eg if it's only five
I don't care, but five million would be a problem!

One other thing ... should the FPU emulator also display messages
like these if it's used?

-- 
Rob.                          (Robert de Bath <robert$ @ debath.co.uk>)
                                       <http://www.cix.co.uk/~mayday>



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [ANNOUNCE] CMOV emulation for 2.4.19-rc1
  2002-07-18 19:15           ` Robert de Bath
@ 2002-07-18 20:44             ` jw schultz
  0 siblings, 0 replies; 16+ messages in thread
From: jw schultz @ 2002-07-18 20:44 UTC (permalink / raw)
  To: linux-kernel


On Thu, Jul 18, 2002 at 08:15:05PM +0100, Robert de Bath wrote:
> On Tue, 2 Jul 2002, jw schultz wrote:
[something causing printk every n emulation hits]
> 
> > wouldn't add too much more overhead than
> >
> > 	if (!emulation_notice)
> > 	{
> > 		emulation_notice = 1;
> > 		printk(...);
> > 	}
> >
> > after all this is only supposed to happen under rescue
> > situations.  That way it will be sure to be in the logs and
> > maybe even on the console and we won't have to hunt for it.
> >
> > Also, the message should say you are doing instruction
> > emulation.  "wrong model cpu, emulating instruction XXX" I
> > doubt indicating the program is helpful unless the tracking
> > is done per task or the printk every time you emulate.
> 
> I'd suggest this message could be so frequent that you want to
> link it's display to real time. Check the jiffy counter each
> time and if it's been less that X seconds since the last message
> just up a counter. Plus in the message say how many instructions
> have been emulated since the last one ... eg if it's only five
> I don't care, but five million would be a problem!

If a jiffies check (need only be low order word) isn't too
expensive, fine.  My concern hear is that while i don't want
the printk overhead of emulation to swamp the system i do want
it to pepper the log so if someone is foolish enough to be
miscompiled with this in they will know it.

Emulating advanced instructions via traps is slow, very slow
i would be willing to put up with an extra 5% time overhead
to tell the user he shouldn't be doing it.  This emulation
should only be done long enough to rescue and/or recompile.
period. It occurs to me now that if it comes from user-mode
(can we tell?) we should always printk with ARGV[0], not
PID, to identify the faulty executable.

As such i'm more concerned with codesize than speed.  If it
is too big i wouldn't enable it in *config.

> 
> One other thing ... should the FPU emulator also display messages
> like these if it's used?
Absolutely not.  The kernel never uses FPU instructions and
there are legitimate situations for running on systems
without an FPU where user-level floating point will be used.

The distinction between these two emulations is clear.
FPU emulation allows user-mode code to do floating point
without coding around the (now corner) case of not having a
FPU.  CMOV et al emulation allows you to move a HD from a
dead MB to another with a different CPU type or at least
boot a kernel that was configured for the wrong CPU type
without crashing on an "illegal instruction".  One is
long-term normal operation, the other is short-term crash
avoidance.


-- 
________________________________________________________________
	J.W. Schultz            Pegasystems Technologies
	email address:		jw@pegasys.ws

		Remember Cernan and Schmitt

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2002-07-18 20:41 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2002-06-30  4:39 [ANNOUNCE] CMOV emulation for 2.4.19-rc1 Willy TARREAU
2002-07-01 13:58 ` Denis Vlasenko
2002-07-01 13:03   ` willy tarreau
2002-07-01 15:55     ` Bill Davidsen
2002-07-02 10:46       ` Denis Vlasenko
2002-07-02  6:31         ` willy tarreau
2002-07-02 12:03           ` Denis Vlasenko
2002-07-01 16:25     ` Gabriel Paubert
2002-07-01 17:08       ` willy tarreau
2002-07-01 18:16     ` Denis Vlasenko
2002-07-01 13:25       ` willy tarreau
2002-07-02 20:00       ` Willy TARREAU
2002-07-03  0:36         ` jw schultz
2002-07-18 19:15           ` Robert de Bath
2002-07-18 20:44             ` jw schultz
2002-07-01 18:25 ` Denis Vlasenko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox