Subject: Bug#515982: text_poke_early crash and noreplace-paravirt

From: Richard Kettlewell <[email-masked]>
Date: Mon, 07 Sep 2009 20:43:17 +0100

I saw the text_poke_early crash too, on a Soekris net4501, which uses an 
AMD ElanSC520.  The problem may or may not apply to other 486-class 
hardware (some reports of the bug say it does).

The workaround is to specify noreplace-paravirt on the kernel command line.

If the bug does indeed affect all 486-class CPUs then it would be a good 
idea to compile CONFIG_PARAVIRT out of the -486 kernels entirely, at 
least until such time as the bug is fixed.

ttfn/rjk

---

From: Ben Hutchings <[email-masked]>
Date: Tue, 08 Sep 2009 02:36:11 +0100

> least until such time as the bug is fixed.

I'm not sure we can do that because the -486 kernel is used for
installation.  Let's fix this properly.

Comparing the original code with the oops, we have:

void *text_poke_early(void *addr, const void *opcode, size_t len)
{
c010793c:       55                      push   %ebp
c010793d:       89 c5                   mov    %eax,%ebp
c010793f:       57                      push   %edi
c0107940:       56                      push   %esi
c0107941:       89 d6                   mov    %edx,%esi
c0107943:       53                      push   %ebx
c0107944:       89 cb                   mov    %ecx,%ebx

	unsigned long flags;
c0107946:       83 ec 04                sub    $0x4,%esp

	local_irq_save(flags);
[original]
c0107949:       51                      push   %ecx
c010794a:       52                      push   %edx
c010794b:       ff 15 dc 1d 34 c0       call   *0xc0341ddc
c0107951:       5a                      pop    %edx
c0107952:       59                      pop    %ecx
c0107953:       89 c2                   mov    %eax,%edx
[patched code not visible]
[original]
c0107955:       51                      push   %ecx
c0107956:       52                      push   %edx
c0107957:       ff 15 e4 1d 34 c0       call   *0xc0341de4
c010795d:       5a                      pop    %edx
c010795e:       59                      pop    %ecx
[patched]
c0107955:	fa                   	cli    
c0107956:	90                   	nop
c0107958:	8d b4 26 00 00 00 00 	lea    0x0(%esi,%eiz,1),%esi
c010795e:	90                   	nop

	memcpy(addr, opcode, len);
c010795f:       89 c8                   mov    %ecx,%eax
c0107961:       89 ef                   mov    %ebp,%edi
c0107963:       c1 e8 02                shr    $0x2,%eax
c0107966:       89 c1                   mov    %eax,%ecx
c0107968:       f3 a5                   rep movsl %ds:(%esi),%es:(%edi)
c010796a:       89 d9                   mov    %ebx,%ecx
c010796c:       83 e1 03                and    $0x3,%ecx
c010796f:       74 02                   je     0xc0107973
c0107971:       f3 a4                   rep movsb %ds:(%esi),%es:(%edi)
c0107973:       89 d0                   mov    %edx,%eax

	local_irq_restore(flags);
[original]
c0107975:       51                      push   %ecx
c0107976:       52                      push   %edx
c0107977:       ff 15 e0 1d 34 c0       call   *0xc0341de0
c010797d:       5a                      pop    %edx
c010797e:       59                      pop    %ecx
[patched]
c0107975:	50                   	push   %eax
c0107976:	9d                   	popf   
c0107977:	90                   	nop
c0107878:	8d b4 26 00 00 00 00 	lea    0x0(%esi,%eiz,1),%esi

	sync_core();
c010797f:       b8 01 00 00 00          mov    $0x1,%eax
c0107984:       0f a2                   cpuid  

}
c0107986:       5a                      pop    %edx
c0107987:       89 e8                   mov    %ebp,%eax
c0107989:       5b                      pop    %ebx
c010798a:       5e                      pop    %esi
c010798b:       5f                      pop    %edi
c010798c:       5d                      pop    %ebp
c010798d:       c3                      ret    

The crash occurs at c010797d.  Presumably the original code is
prefetched and executed up to and including the call instruction, and
then the patched code is fetched after the local_irq_restore()
implementation returns.

I think we need to flush the prefetch buffer between memcpy() and
local_irq_restore().  According to Intel documentation, we can do this
on the i486 with a jmp instruction, and later processors flush the
prefetch buffer automatically when the corresponding memory is modified.

There is a further problem in that sync_core() uses "cpuid" which isn't
implemented by most 486-class processors, but that is easy to fix.

I don't have any 486-class systems to test this on, so perhaps you could
try this patch:

--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -488,6 +488,9 @@
 	unsigned long flags;
 	local_irq_save(flags);
 	memcpy(addr, opcode, len);
+	/* Force 486-class processors to flush prefetched instructions,
+	   since we may have just patched local_irq_restore(). */
+	asm volatile("jmp 1f\n1:\n" ::: "memory");
 	local_irq_restore(flags);
 	sync_core();
 	/* Could also do a CLFLUSH here to speed up CPU recovery; but
--- a/include/asm-x86/processor.h
+++ b/include/asm-x86/processor.h
@@ -698,6 +698,12 @@
 {
 	int tmp;
 
+#if defined(CONFIG_M386) || defined(CONFIG_M486)
+	/* This is unnecessary on 386- and 486-class processors, most of
+	   which don't even implement CPUID. */
+	if (boot_cpu_data.x86 < 5)
+		return;
+#endif
 	asm volatile("cpuid" : "=a" (tmp) : "0" (1)
 		     : "ebx", "ecx", "edx", "memory");
 }
--- END ---

Ben.

-- 
Ben Hutchings
Life is what happens to you while you're busy making other plans.
                                                               - John Lennon

---

From: Richard Kettlewell <[email-masked]>
Date: Tue, 08 Sep 2009 23:22:07 +0100

> I don't have any 486-class systems to test this on, so perhaps you could

That works for me.

   Booting 'Debian GNU/Linux, kernel 2.6.26-2-486-511703' 
 
                   root  (hd0,0) 
                             Filesystem type is ext2fs, partition type 
0x83                                 kernel 
/boot/vmlinuz-2.6.26-2-486-511703 root=/dev/hda1 ro console=ttyS0,57600 
n8 
            [Linux-bzImage, setup=0x3000, size=0x164590] 
                  initrd  /boot/initrd.img-2.6.26-2-486 
                              [Linux-initrd @ 0x3d80000, 0x26fd3f bytes] 
 
                                            Probing EDD (edd=off to 
disable)... ok

[...]

[    0.000000] Initializing cgroup subsys cpu
[    0.000000] Linux version 2.6.26 (2.6.26) (richard@leucomorph) (gcc 
version 4.3.2 (Debian 4.3.2-1.1) ) #1 Tue Sep 8 11:31:50 BST 2009

[...]

[    0.104006] CPU: AMD Am5x86-WB stepping 04
[    0.112007] Checking 'hlt' instruction... OK.
[    0.156009] Freeing SMP alternatives: 0k freed
[    0.160010] ACPI: Core revision 20080321

[...]

ttfn/rjk