MCE: Fix vm86 handling for 32bit mce handler
authorAndi Kleen <andi@firstfloor.org>
Fri, 19 Nov 2010 12:16:22 +0000 (13:16 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 2 Oct 2012 16:47:55 +0000 (09:47 -0700)
commit a129a7c84582629741e5fa6f40026efcd7a65bd4 upstream.

When running on 32bit the mce handler could misinterpret
vm86 mode as ring 0. This can affect whether it does recovery
or not; it was possible to panic when recovery was actually
possible.

Fix this by always forcing vm86 to look like ring 3.

[ Backport to 3.0 notes:
Things changed there slightly:
   - move mce_get_rip() up. It fills up m->cs and m->ip values which
     are evaluated in mce_severity(). Therefore move it up right before
     the mce_severity call. This seem to be another bug in 3.0?
   - Place the backport (fix m->cs in V86 case) to where m->cs gets
     filled which is mce_get_rip() in 3.0
]

Signed-off-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
Signed-off-by: Thomas Renninger <trenn@suse.de>
Reviewed-by: Tony Luck <tony.luck@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/x86/kernel/cpu/mcheck/mce.c

index 942bda2fd2f845040132b0d196c4ad1795105a5e..1396edf20b99efdf0fe7048bb6b3fb2bb3406efb 100644 (file)
@@ -451,6 +451,13 @@ static inline void mce_get_rip(struct mce *m, struct pt_regs *regs)
        if (regs && (m->mcgstatus & (MCG_STATUS_RIPV|MCG_STATUS_EIPV))) {
                m->ip = regs->ip;
                m->cs = regs->cs;
+               /*
+                * When in VM86 mode make the cs look like ring 3
+                * always. This is a lie, but it's better than passing
+                * the additional vm86 bit around everywhere.
+                */
+               if (v8086_mode(regs))
+                       m->cs |= 3;
        } else {
                m->ip = 0;
                m->cs = 0;
@@ -988,6 +995,7 @@ void do_machine_check(struct pt_regs *regs, long error_code)
                 */
                add_taint(TAINT_MACHINE_CHECK);
 
+               mce_get_rip(&m, regs);
                severity = mce_severity(&m, tolerant, NULL);
 
                /*
@@ -1026,7 +1034,6 @@ void do_machine_check(struct pt_regs *regs, long error_code)
                if (severity == MCE_AO_SEVERITY && mce_usable_address(&m))
                        mce_ring_add(m.addr >> PAGE_SHIFT);
 
-               mce_get_rip(&m, regs);
                mce_log(&m);
 
                if (severity > worst) {