| From c2226fc9e87ba3da060e47333657cd6616652b84 Mon Sep 17 00:00:00 2001 |
| From: =?UTF-8?q?Stephan=20B=C3=A4rwolf?= <stephan.baerwolf@tu-ilmenau.de> |
| Date: Thu, 12 Jan 2012 16:43:04 +0100 |
| Subject: KVM: x86: fix missing checks in syscall emulation |
| |
| From: =?UTF-8?q?Stephan=20B=C3=A4rwolf?= <stephan.baerwolf@tu-ilmenau.de> |
| |
| commit c2226fc9e87ba3da060e47333657cd6616652b84 upstream. |
| |
| On hosts without this patch, 32bit guests will crash (and 64bit guests |
| may behave in a wrong way) for example by simply executing following |
| nasm-demo-application: |
| |
| [bits 32] |
| global _start |
| SECTION .text |
| _start: syscall |
| |
| (I tested it with winxp and linux - both always crashed) |
| |
| Disassembly of section .text: |
| |
| 00000000 <_start>: |
| 0: 0f 05 syscall |
| |
| The reason seems a missing "invalid opcode"-trap (int6) for the |
| syscall opcode "0f05", which is not available on Intel CPUs |
| within non-longmodes, as also on some AMD CPUs within legacy-mode. |
| (depending on CPU vendor, MSR_EFER and cpuid) |
| |
| Because previous mentioned OSs may not engage corresponding |
| syscall target-registers (STAR, LSTAR, CSTAR), they remain |
| NULL and (non trapping) syscalls are leading to multiple |
| faults and finally crashs. |
| |
| Depending on the architecture (AMD or Intel) pretended by |
| guests, various checks according to vendor's documentation |
| are implemented to overcome the current issue and behave |
| like the CPUs physical counterparts. |
| |
| [mtosatti: cleanup/beautify code] |
| |
| Signed-off-by: Stephan Baerwolf <stephan.baerwolf@tu-ilmenau.de> |
| Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com> |
| Signed-off-by: Stefan Bader <stefan.bader@canonical.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/x86/include/asm/kvm_emulate.h | 13 +++++++++ |
| arch/x86/kvm/emulate.c | 51 +++++++++++++++++++++++++++++++++++++ |
| 2 files changed, 64 insertions(+) |
| |
| --- a/arch/x86/include/asm/kvm_emulate.h |
| +++ b/arch/x86/include/asm/kvm_emulate.h |
| @@ -300,6 +300,19 @@ struct x86_emulate_ctxt { |
| #define X86EMUL_MODE_PROT (X86EMUL_MODE_PROT16|X86EMUL_MODE_PROT32| \ |
| X86EMUL_MODE_PROT64) |
| |
| +/* CPUID vendors */ |
| +#define X86EMUL_CPUID_VENDOR_AuthenticAMD_ebx 0x68747541 |
| +#define X86EMUL_CPUID_VENDOR_AuthenticAMD_ecx 0x444d4163 |
| +#define X86EMUL_CPUID_VENDOR_AuthenticAMD_edx 0x69746e65 |
| + |
| +#define X86EMUL_CPUID_VENDOR_AMDisbetterI_ebx 0x69444d41 |
| +#define X86EMUL_CPUID_VENDOR_AMDisbetterI_ecx 0x21726574 |
| +#define X86EMUL_CPUID_VENDOR_AMDisbetterI_edx 0x74656273 |
| + |
| +#define X86EMUL_CPUID_VENDOR_GenuineIntel_ebx 0x756e6547 |
| +#define X86EMUL_CPUID_VENDOR_GenuineIntel_ecx 0x6c65746e |
| +#define X86EMUL_CPUID_VENDOR_GenuineIntel_edx 0x49656e69 |
| + |
| enum x86_intercept_stage { |
| X86_ICTP_NONE = 0, /* Allow zero-init to not match anything */ |
| X86_ICPT_PRE_EXCEPT, |
| --- a/arch/x86/kvm/emulate.c |
| +++ b/arch/x86/kvm/emulate.c |
| @@ -1877,6 +1877,51 @@ setup_syscalls_segments(struct x86_emula |
| ss->p = 1; |
| } |
| |
| +static bool em_syscall_is_enabled(struct x86_emulate_ctxt *ctxt) |
| +{ |
| + struct x86_emulate_ops *ops = ctxt->ops; |
| + u32 eax, ebx, ecx, edx; |
| + |
| + /* |
| + * syscall should always be enabled in longmode - so only become |
| + * vendor specific (cpuid) if other modes are active... |
| + */ |
| + if (ctxt->mode == X86EMUL_MODE_PROT64) |
| + return true; |
| + |
| + eax = 0x00000000; |
| + ecx = 0x00000000; |
| + if (ops->get_cpuid(ctxt, &eax, &ebx, &ecx, &edx)) { |
| + /* |
| + * Intel ("GenuineIntel") |
| + * remark: Intel CPUs only support "syscall" in 64bit |
| + * longmode. Also an 64bit guest with a |
| + * 32bit compat-app running will #UD !! While this |
| + * behaviour can be fixed (by emulating) into AMD |
| + * response - CPUs of AMD can't behave like Intel. |
| + */ |
| + if (ebx == X86EMUL_CPUID_VENDOR_GenuineIntel_ebx && |
| + ecx == X86EMUL_CPUID_VENDOR_GenuineIntel_ecx && |
| + edx == X86EMUL_CPUID_VENDOR_GenuineIntel_edx) |
| + return false; |
| + |
| + /* AMD ("AuthenticAMD") */ |
| + if (ebx == X86EMUL_CPUID_VENDOR_AuthenticAMD_ebx && |
| + ecx == X86EMUL_CPUID_VENDOR_AuthenticAMD_ecx && |
| + edx == X86EMUL_CPUID_VENDOR_AuthenticAMD_edx) |
| + return true; |
| + |
| + /* AMD ("AMDisbetter!") */ |
| + if (ebx == X86EMUL_CPUID_VENDOR_AMDisbetterI_ebx && |
| + ecx == X86EMUL_CPUID_VENDOR_AMDisbetterI_ecx && |
| + edx == X86EMUL_CPUID_VENDOR_AMDisbetterI_edx) |
| + return true; |
| + } |
| + |
| + /* default: (not Intel, not AMD), apply Intel's stricter rules... */ |
| + return false; |
| +} |
| + |
| static int em_syscall(struct x86_emulate_ctxt *ctxt) |
| { |
| struct x86_emulate_ops *ops = ctxt->ops; |
| @@ -1890,9 +1935,15 @@ static int em_syscall(struct x86_emulate |
| ctxt->mode == X86EMUL_MODE_VM86) |
| return emulate_ud(ctxt); |
| |
| + if (!(em_syscall_is_enabled(ctxt))) |
| + return emulate_ud(ctxt); |
| + |
| ops->get_msr(ctxt, MSR_EFER, &efer); |
| setup_syscalls_segments(ctxt, &cs, &ss); |
| |
| + if (!(efer & EFER_SCE)) |
| + return emulate_ud(ctxt); |
| + |
| ops->get_msr(ctxt, MSR_STAR, &msr_data); |
| msr_data >>= 32; |
| cs_sel = (u16)(msr_data & 0xfffc); |