From patchwork Tue Jul 12 18:45:42 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 9225929 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 22E50604DB for ; Tue, 12 Jul 2016 18:45:56 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 15DDA27165 for ; Tue, 12 Jul 2016 18:45:56 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 0A34D27D5D; Tue, 12 Jul 2016 18:45:56 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4FFD127165 for ; Tue, 12 Jul 2016 18:45:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1750854AbcGLSpv (ORCPT ); Tue, 12 Jul 2016 14:45:51 -0400 Received: from mail-wm0-f68.google.com ([74.125.82.68]:35411 "EHLO mail-wm0-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750831AbcGLSpu (ORCPT ); Tue, 12 Jul 2016 14:45:50 -0400 Received: by mail-wm0-f68.google.com with SMTP id i5so2971552wmg.2 for ; Tue, 12 Jul 2016 11:45:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:subject:date:message-id; bh=FlD7FaE4KJGbB3kYLHymNzyvygIp9lp8/ViUe2u2Xwo=; b=RXJVQhU5WggXeL87VxjmMtP8KP6rE91f+YltSbsTpVoLONKOLg2sR9dFqpPrfRocya aGDzYFqKse50gtMMGbDQt2loL9n65RbN4N4EnkZrFf/4HeMSoeVeXMQ9fTmon/EBw9Wm wI5XXRTxp6KyYtlZM7xehVE33RlrsYQc9W/3wPGvSsItINMDMpQCp5dsZaftlxN4wZ11 otfSv0GGVEJhaNDzgif1x3zg76yG+IXysK0amMMbfWuByXmf2pFGFZW5xA6s7+5VU3rS G1ehYVUKaXz8OUTxCyhqiP4v8vTZ/1DtPFFtYfx9+pLjGT3jcaLDdlVYuLKxuEKbOv3o SWFQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:sender:from:to:subject:date:message-id; bh=FlD7FaE4KJGbB3kYLHymNzyvygIp9lp8/ViUe2u2Xwo=; b=DWYPRxCiyvoonLlmOv3W1djN4SUTXo+6wPND3NGSBlU/SQsZxfOuDYQnyoviJuES16 tnmNrY+B7Pa7RyItD5CBHglOxW+DaoLpYxCNXRrj8Pb/myymH7Ha2MeltWz2O/oaDZAD McYAB7q2ekr1NJWy7MHkoNKOA2WMQcrJDrlKBs+VV7TthSZGyTwVrIFkEmuSNDfVbvwv sUmCATWMmgbh8/Ohad6amH5sWsuS58lX9Ty9aTHmNJFsr5PjaDZogAu33rF0hdeGOWo3 wwGGPo6YWFT9x4f24wtsi8ad9hVP0a3BmJMsPR2R0LuR+2pqlSVsoMjP2u7aXjQbFT8T 10MQ== X-Gm-Message-State: ALyK8tIvTd+9KXpPCWbeea+zqOM6zFUaudimY177Fw0mqvpIGK8ewRXgz3TqIBtK6zvm/Q== X-Received: by 10.28.46.129 with SMTP id u123mr21782678wmu.19.1468349148455; Tue, 12 Jul 2016 11:45:48 -0700 (PDT) Received: from donizetti.lan (94-39-188-118.adsl-ull.clienti.tiscali.it. [94.39.188.118]) by smtp.gmail.com with ESMTPSA id q203sm13948707wmd.24.2016.07.12.11.45.46 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 12 Jul 2016 11:45:47 -0700 (PDT) From: Paolo Bonzini To: kvm@vger.kernel.org Subject: [PATCH kvm-unit-tests] x86: add UMIP test Date: Tue, 12 Jul 2016 20:45:42 +0200 Message-Id: <1468349143-2309-1-git-send-email-pbonzini@redhat.com> X-Mailer: git-send-email 2.7.4 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The UMIP feature can be emulated by KVM, so it's useful to add a test that it works properly. Signed-off-by: Paolo Bonzini --- lib/x86/desc.c | 2 +- lib/x86/desc.h | 1 + lib/x86/processor.h | 9 +++ x86/Makefile.common | 1 + x86/umip.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++++ x86/unittests.cfg | 4 ++ 6 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 x86/umip.c diff --git a/lib/x86/desc.c b/lib/x86/desc.c index 402204d..a2d66af 100644 --- a/lib/x86/desc.c +++ b/lib/x86/desc.c @@ -63,7 +63,7 @@ static const char* exception_mnemonic(int vector) } } -static void unhandled_exception(struct ex_regs *regs, bool cpu) +void unhandled_exception(struct ex_regs *regs, bool cpu) { printf("Unhandled %sexception %ld %s at ip %016lx\n", cpu ? "cpu " : "", regs->vector, diff --git a/lib/x86/desc.h b/lib/x86/desc.h index be52fd4..7646cfa 100644 --- a/lib/x86/desc.h +++ b/lib/x86/desc.h @@ -154,6 +154,7 @@ void set_gdt_entry(int sel, u32 base, u32 limit, u8 access, u8 gran); void set_intr_alt_stack(int e, void *fn); void print_current_tss_info(void); void handle_exception(u8 v, void (*func)(struct ex_regs *regs)); +void unhandled_exception(struct ex_regs *regs, bool cpu); bool test_for_exception(unsigned int ex, void (*trigger_func)(void *data), void *data); diff --git a/lib/x86/processor.h b/lib/x86/processor.h index ee7f180..398ce4c 100644 --- a/lib/x86/processor.h +++ b/lib/x86/processor.h @@ -25,6 +25,7 @@ #define X86_CR4_DE 0x00000008 #define X86_CR4_PSE 0x00000010 #define X86_CR4_PAE 0x00000020 +#define X86_CR4_UMIP 0x00000800 #define X86_CR4_VMXE 0x00002000 #define X86_CR4_PCIDE 0x00020000 #define X86_CR4_SMAP 0x00200000 @@ -36,6 +37,7 @@ #define X86_EFLAGS_ZF 0x00000040 #define X86_EFLAGS_SF 0x00000080 #define X86_EFLAGS_OF 0x00000800 +#define X86_EFLAGS_IOPL 0x00003000 #define X86_EFLAGS_AC 0x00040000 #define X86_IA32_EFER 0xc0000080 @@ -146,6 +148,13 @@ static inline void write_rflags(unsigned long f) asm volatile ("push %0; popf\n\t" : : "rm"(f)); } +static inline void set_iopl(int iopl) +{ + unsigned long flags = read_rflags() & ~X86_EFLAGS_IOPL; + flags |= iopl * (X86_EFLAGS_IOPL / 3); + write_rflags(flags); +} + static inline u64 rdmsr(u32 index) { u32 a, d; diff --git a/x86/Makefile.common b/x86/Makefile.common index 356d879..32e8e51 100644 --- a/x86/Makefile.common +++ b/x86/Makefile.common @@ -45,6 +45,7 @@ tests-common = $(TEST_DIR)/vmexit.flat $(TEST_DIR)/tsc.flat \ $(TEST_DIR)/tsc_adjust.flat $(TEST_DIR)/asyncpf.flat \ $(TEST_DIR)/init.flat $(TEST_DIR)/smap.flat \ $(TEST_DIR)/hyperv_synic.flat $(TEST_DIR)/hyperv_stimer.flat \ + $(TEST_DIR)/umip.flat ifdef API tests-common += api/api-sample diff --git a/x86/umip.c b/x86/umip.c new file mode 100644 index 0000000..c1a40d6 --- /dev/null +++ b/x86/umip.c @@ -0,0 +1,194 @@ + +#include "libcflat.h" +#include "desc.h" +#include "processor.h" + +#define CPUID_7_ECX_UMIP (1 << 2) +static int cpuid_7_ecx; + + +/* GP handler to skip over faulting instructions */ + +static unsigned long expected_rip; +static int skip_count; +static volatile int gp_count; + +void gp_handler(struct ex_regs *regs) +{ + if (regs->rip == expected_rip) { + gp_count++; + regs->rip += skip_count; + } else { + unhandled_exception(regs, false); + } +} + + +#define GP_ASM(stmt, in, clobber) \ + asm ("mov" W " $1f, %[expected_rip]\n\t" \ + "movl $2f-1f, %[skip_count]\n\t" \ + "1: " stmt "\n\t" \ + "2: " \ + : [expected_rip] "=m" (expected_rip), \ + [skip_count] "=m" (skip_count) \ + : in : clobber) + +static void do_smsw(void) +{ + gp_count = 0; + GP_ASM("smsw %%ax", , "eax"); +} + +static void do_sldt(void) +{ + gp_count = 0; + GP_ASM("sldt %%ax", , "eax"); +} + +static void do_str(void) +{ + gp_count = 0; + GP_ASM("str %%ax", , "eax"); +} + +static void do_sgdt(void) +{ + struct descriptor_table_ptr dt; + gp_count = 0; + GP_ASM("sgdt %[dt]", [dt]"m"(dt), ); +} + +static void do_sidt(void) +{ + struct descriptor_table_ptr dt; + gp_count = 0; + GP_ASM("sidt %[dt]", [dt]"m"(dt), ); +} + +static void do_movcr(void) +{ + gp_count = 0; + GP_ASM("mov %%cr0, %%" R "ax", , "eax"); +} + +static void test_umip_nogp(char *msg) +{ + puts(msg); + + do_smsw(); + report("no exception from smsw", gp_count == 0); + do_sgdt(); + report("no exception from sgdt", gp_count == 0); + do_sidt(); + report("no exception from sidt", gp_count == 0); + do_sldt(); + report("no exception from sldt", gp_count == 0); + do_str(); + report("no exception from str", gp_count == 0); + if (read_cs() & 3) { + do_movcr(); + report("exception from mov %%cr0, %%eax", gp_count == 1); + } +} + +static void test_umip_gp(char *msg) +{ + puts(msg); + + do_smsw(); + report("exception from smsw", gp_count == 1); + do_sgdt(); + report("exception from sgdt", gp_count == 1); + do_sidt(); + report("exception from sidt", gp_count == 1); + do_sldt(); + report("exception from sldt", gp_count == 1); + do_str(); + report("exception from str", gp_count == 1); + if (read_cs() & 3) { + do_movcr(); + report("exception from mov %%cr0, %%eax", gp_count == 1); + } +} + +/* The ugly mode switching code */ + +int do_ring3(void (*fn)(char *), char *arg) +{ + static unsigned char user_stack[4096]; + int ret; + + asm volatile ("mov %[user_ds], %%" R "dx\n\t" + "mov %%dx, %%ds\n\t" + "mov %%dx, %%es\n\t" + "mov %%dx, %%fs\n\t" + "mov %%dx, %%gs\n\t" + "mov %%" R "sp, %%" R "cx\n\t" + "push" W " %%" R "dx \n\t" + "lea %[user_stack_top], %%" R "dx \n\t" + "push" W " %%" R "dx \n\t" + "pushf" W "\n\t" + "push" W " %[user_cs] \n\t" + "push" W " $1f \n\t" + "iret" W "\n" + "1: \n\t" + "push %%" R "cx\n\t" /* save kernel SP */ + +#ifndef __x86_64__ + "push %[arg]\n\t" +#endif + "call *%[fn]\n\t" +#ifndef __x86_64__ + "pop %%ecx\n\t" +#endif + + "pop %%" R "cx\n\t" + "mov $1f, %%" R "dx\n\t" + "int %[kernel_entry_vector]\n\t" + ".section .text.entry \n\t" + "kernel_entry: \n\t" + "mov %%" R "cx, %%" R "sp \n\t" + "mov %[kernel_ds], %%cx\n\t" + "mov %%cx, %%ds\n\t" + "mov %%cx, %%es\n\t" + "mov %%cx, %%fs\n\t" + "mov %%cx, %%gs\n\t" + "jmp *%%" R "dx \n\t" + ".section .text\n\t" + "1:\n\t" + : [ret] "=&a" (ret) + : [user_ds] "i" (USER_DS), + [user_cs] "i" (USER_CS), + [user_stack_top]"m"(user_stack[sizeof user_stack]), + [fn]"r"(fn), + [arg]"D"(arg), + [kernel_ds]"i"(KERNEL_DS), + [kernel_entry_vector]"i"(0x20) + : "rcx", "rdx"); + return ret; +} + +int main() +{ + extern unsigned char kernel_entry; + + setup_idt(); + set_idt_entry(0x20, &kernel_entry, 3); + handle_exception(13, gp_handler); + set_iopl(3); + + test_umip_nogp("UMIP=0, CPL=0\n"); + do_ring3(test_umip_nogp, "UMIP=0, CPL=3\n"); + + cpuid_7_ecx = cpuid(7).c; + if (!(cpuid_7_ecx & CPUID_7_ECX_UMIP)) { + printf("UMIP not available\n"); + return report_summary(); + } + write_cr4(read_cr4() | X86_CR4_UMIP); + + test_umip_nogp("UMIP=0, CPL=0\n"); + do_ring3(test_umip_gp, "UMIP=0, CPL=3\n"); + + return report_summary(); +} diff --git a/x86/unittests.cfg b/x86/unittests.cfg index 60747cf..f76f0d4 100644 --- a/x86/unittests.cfg +++ b/x86/unittests.cfg @@ -179,6 +179,10 @@ file = pcid.flat extra_params = -cpu qemu64,+pcid arch = x86_64 +[umip] +file = umip.flat +extra_params = -cpu qemu64,+umip + [vmx] file = vmx.flat extra_params = -cpu host,+vmx