From patchwork Thu Dec 8 11:52:17 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jan Beulich X-Patchwork-Id: 9466201 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 8B9166071E for ; Thu, 8 Dec 2016 11:54:35 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 70A1628577 for ; Thu, 8 Dec 2016 11:54:35 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 654442857B; Thu, 8 Dec 2016 11:54:35 +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=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id EB1FB28577 for ; Thu, 8 Dec 2016 11:54:33 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1cExFS-0004dS-FE; Thu, 08 Dec 2016 11:52:26 +0000 Received: from mail6.bemta6.messagelabs.com ([193.109.254.103]) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1cExFR-0004dC-9m for xen-devel@lists.xenproject.org; Thu, 08 Dec 2016 11:52:25 +0000 Received: from [85.158.143.35] by server-5.bemta-6.messagelabs.com id 27/58-19272-77949485; Thu, 08 Dec 2016 11:52:23 +0000 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrFIsWRWlGSWpSXmKPExsXS6fjDS7fM0zP CYF2bsMX3LZOZHBg9Dn+4whLAGMWamZeUX5HAmvH1QwNrwcmJTBXn5z1lbmC8tZ2xi5GTQ0gg T+LFs1esIDavgJ3ExKkHmEFsCQFDiafvr7OB2CwCqhKvzj5jAbHZBNQl2p5tB6rn4BARMJA4d zQJxGQW0JfYtg6sQljAXKJ31TFWiOl2Eq2PN4LZnAL2Env/PmQHKecVEJT4u0MYJMwMVNL/az LjBEaeWQiZWUgyELaWxMNft1ggbG2JZQtfM88C2ystsfwfB0TYWmL7qYlMqEpAbDeJ1ilPmBY wcqxi1ChOLSpLLdI1NtBLKspMzyjJTczM0TU0MNPLTS0uTkxPzUlMKtZLzs/dxAgMVwYg2MH4 d23gIUZJDiYlUd5dE9wjhPiS8lMqMxKLM+KLSnNSiw8xynBwKEnwqnl4RggJFqWmp1akZeYAI wcmLcHBoyTCy+oOlOYtLkjMLc5Mh0idYtTlmPZs8VMmIZa8/LxUKXHeOyBFAiBFGaV5cCNgUX yJUVZKmJcR6CghnoLUotzMElT5V4ziHIxKwrxbQKbwZOaVwG16BXQEE9AR8264gxxRkoiQkmp glE4+9Pqn3XLv9rd7Tyr1TZ+wxSxftLjh2cvYDas6DVaul+Vaw1m6Xzl87SV2+5nGhU9fNS4u /LFd/X938Z/ea01LggPchV6nCpv9ztdtOrHwz7Elpi8cD948fOh5lAjTu4TZM4WWPe3jDYqTU 7S9v9PaK/imun3M/aTu5yfW1Cid5Lqxi19ulxJLcUaioRZzUXEiAGpS8nHdAgAA X-Env-Sender: JBeulich@suse.com X-Msg-Ref: server-12.tower-21.messagelabs.com!1481197939!47313500!1 X-Originating-IP: [137.65.248.74] X-SpamReason: No, hits=0.0 required=7.0 tests= X-StarScan-Received: X-StarScan-Version: 9.1.1; banners=-,-,- X-VirusChecked: Checked Received: (qmail 42835 invoked from network); 8 Dec 2016 11:52:21 -0000 Received: from prv-mh.provo.novell.com (HELO prv-mh.provo.novell.com) (137.65.248.74) by server-12.tower-21.messagelabs.com with DHE-RSA-AES256-GCM-SHA384 encrypted SMTP; 8 Dec 2016 11:52:21 -0000 Received: from INET-PRV-MTA by prv-mh.provo.novell.com with Novell_GroupWise; Thu, 08 Dec 2016 04:52:16 -0700 Message-Id: <584957810200007800126B28@prv-mh.provo.novell.com> X-Mailer: Novell GroupWise Internet Agent 14.2.1 Date: Thu, 08 Dec 2016 04:52:17 -0700 From: "Jan Beulich" To: "xen-devel" References: <5849551B0200007800126AC6@prv-mh.provo.novell.com> In-Reply-To: <5849551B0200007800126AC6@prv-mh.provo.novell.com> Mime-Version: 1.0 Cc: Andrew Cooper Subject: [Xen-devel] [PATCH 2/2] x86emul: support LAR/LSL/VERR/VERW X-BeenThere: xen-devel@lists.xen.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xen.org Sender: "Xen-devel" X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Jan Beulich x86emul: support LAR/LSL/VERR/VERW Signed-off-by: Jan Beulich --- a/tools/tests/x86_emulator/test_x86_emulator.c +++ b/tools/tests/x86_emulator/test_x86_emulator.c @@ -46,7 +46,47 @@ static int read( if ( verbose ) printf("** %s(%u, %p,, %u,)\n", __func__, seg, (void *)offset, bytes); - bytes_read += bytes; + switch ( seg ) + { + uint64_t value; + + case x86_seg_gdtr: + /* Fake system segment type matching table index. */ + if ( (offset & 7) || (bytes > 8) ) + return X86EMUL_UNHANDLEABLE; +#ifdef __x86_64__ + if ( !(offset & 8) ) + { + memset(p_data, 0, bytes); + return X86EMUL_OKAY; + } + value = (offset - 8) >> 4; +#else + value = (offset - 8) >> 3; +#endif + if ( value >= 0x10 ) + return X86EMUL_UNHANDLEABLE; + value |= value << 40; + memcpy(p_data, &value, bytes); + return X86EMUL_OKAY; + + case x86_seg_ldtr: + /* Fake user segment type matching table index. */ + if ( (offset & 7) || (bytes > 8) ) + return X86EMUL_UNHANDLEABLE; + value = offset >> 3; + if ( value >= 0x10 ) + return X86EMUL_UNHANDLEABLE; + value |= (value | 0x10) << 40; + memcpy(p_data, &value, bytes); + return X86EMUL_OKAY; + + default: + if ( !is_x86_user_segment(seg) ) + return X86EMUL_UNHANDLEABLE; + bytes_read += bytes; + break; + } memcpy(p_data, (void *)offset, bytes); return X86EMUL_OKAY; } @@ -75,6 +115,8 @@ static int write( if ( verbose ) printf("** %s(%u, %p,, %u,)\n", __func__, seg, (void *)offset, bytes); + if ( !is_x86_user_segment(seg) ) + return X86EMUL_UNHANDLEABLE; memcpy((void *)offset, p_data, bytes); return X86EMUL_OKAY; } @@ -90,10 +132,24 @@ static int cmpxchg( if ( verbose ) printf("** %s(%u, %p,, %u,)\n", __func__, seg, (void *)offset, bytes); + if ( !is_x86_user_segment(seg) ) + return X86EMUL_UNHANDLEABLE; memcpy((void *)offset, new, bytes); return X86EMUL_OKAY; } +static int read_segment( + enum x86_segment seg, + struct segment_register *reg, + struct x86_emulate_ctxt *ctxt) +{ + if ( !is_x86_user_segment(seg) ) + return X86EMUL_UNHANDLEABLE; + memset(reg, 0, sizeof(*reg)); + reg->attr.fields.p = 1; + return X86EMUL_OKAY; +} + static int cpuid( unsigned int *eax, unsigned int *ebx, @@ -193,6 +249,21 @@ static int read_cr( return X86EMUL_UNHANDLEABLE; } +static int read_msr( + unsigned int reg, + uint64_t *val, + struct x86_emulate_ctxt *ctxt) +{ + switch ( reg ) + { + case 0xc0000080: /* EFER */ + *val = ctxt->addr_size > 32 ? 0x500 /* LME|LMA */ : 0; + return X86EMUL_OKAY; + } + + return X86EMUL_UNHANDLEABLE; +} + int get_fpu( void (*exception_callback)(void *, struct cpu_user_regs *), void *exception_callback_arg, @@ -223,8 +294,10 @@ static struct x86_emulate_ops emulops = .insn_fetch = fetch, .write = write, .cmpxchg = cmpxchg, + .read_segment = read_segment, .cpuid = cpuid, .read_cr = read_cr, + .read_msr = read_msr, .get_fpu = get_fpu, }; @@ -726,6 +799,156 @@ int main(int argc, char **argv) goto fail; printf("okay\n"); + printf("%-40s", "Testing lar (null selector)..."); + instr[0] = 0x0f; instr[1] = 0x02; instr[2] = 0xc1; + regs.eflags = 0x240; + regs.eip = (unsigned long)&instr[0]; + regs.ecx = 0; + regs.eax = 0x11111111; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || + (regs.eax != 0x11111111) || + (regs.eflags != 0x200) || + (regs.eip != (unsigned long)&instr[3]) ) + goto fail; + printf("okay\n"); + + printf("%-40s", "Testing lsl (null selector)..."); + instr[0] = 0x0f; instr[1] = 0x03; instr[2] = 0xca; + regs.eflags = 0x240; + regs.eip = (unsigned long)&instr[0]; + regs.edx = 0; + regs.ecx = 0x11111111; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || + (regs.ecx != 0x11111111) || + (regs.eflags != 0x200) || + (regs.eip != (unsigned long)&instr[3]) ) + goto fail; + printf("okay\n"); + + printf("%-40s", "Testing verr (null selector)..."); + instr[0] = 0x0f; instr[1] = 0x00; instr[2] = 0x21; + regs.eflags = 0x240; + regs.eip = (unsigned long)&instr[0]; + regs.ecx = (unsigned long)res; + *res = 0; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || + (regs.eflags != 0x200) || + (regs.eip != (unsigned long)&instr[3]) ) + goto fail; + printf("okay\n"); + + printf("%-40s", "Testing verw (null selector)..."); + instr[0] = 0x0f; instr[1] = 0x00; instr[2] = 0x2a; + regs.eflags = 0x240; + regs.eip = (unsigned long)&instr[0]; + regs.ecx = 0; + regs.edx = (unsigned long)res; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || + (regs.eflags != 0x200) || + (regs.eip != (unsigned long)&instr[3]) ) + goto fail; + printf("okay\n"); + + printf("%-40s", "Testing lar/lsl/verr/verw (all types)..."); + for ( i = 0; i < 0x20; ++i ) + { + unsigned int sel = i < 0x10 ? +#ifndef __x86_64__ + (i << 3) + 8 +#else + (i << 4) + 8 +#endif + : ((i - 0x10) << 3) | 4; + bool failed; + +#ifndef __x86_64__ +# define LAR_VALID 0xffff1a3eU +# define LSL_VALID 0xffff0a0eU +#else +# define LAR_VALID 0xffff1a04U +# define LSL_VALID 0xffff0a04U +#endif +#define VERR_VALID 0xccff0000U +#define VERW_VALID 0x00cc0000U + + instr[0] = 0x0f; instr[1] = 0x02; instr[2] = 0xc2; + regs.eflags = (LAR_VALID >> i) & 1 ? 0x200 : 0x240; + regs.eip = (unsigned long)&instr[0]; + regs.edx = sel; + regs.eax = 0x11111111; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || + (regs.eip != (unsigned long)&instr[3]) ) + goto fail; + if ( (LAR_VALID >> i) & 1 ) + failed = (regs.eflags != 0x240) || + ((regs.eax & 0xf0ff00) != (i << 8)); + else + failed = (regs.eflags != 0x200) || + (regs.eax != 0x11111111); + if ( failed ) + { + printf("LAR %04x (type %02x) ", sel, i); + goto fail; + } + + instr[0] = 0x0f; instr[1] = 0x03; instr[2] = 0xd1; + regs.eflags = (LSL_VALID >> i) & 1 ? 0x200 : 0x240; + regs.eip = (unsigned long)&instr[0]; + regs.ecx = sel; + regs.edx = 0x11111111; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || + (regs.eip != (unsigned long)&instr[3]) ) + goto fail; + if ( (LSL_VALID >> i) & 1 ) + failed = (regs.eflags != 0x240) || + (regs.edx != (i & 0xf)); + else + failed = (regs.eflags != 0x200) || + (regs.edx != 0x11111111); + if ( failed ) + { + printf("LSL %04x (type %02x) ", sel, i); + goto fail; + } + + instr[0] = 0x0f; instr[1] = 0x00; instr[2] = 0xe2; + regs.eflags = (VERR_VALID >> i) & 1 ? 0x200 : 0x240; + regs.eip = (unsigned long)&instr[0]; + regs.ecx = 0; + regs.edx = sel; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || + (regs.eip != (unsigned long)&instr[3]) ) + goto fail; + if ( regs.eflags != ((VERR_VALID >> i) & 1 ? 0x240 : 0x200) ) + { + printf("VERR %04x (type %02x) ", sel, i); + goto fail; + } + + instr[0] = 0x0f; instr[1] = 0x00; instr[2] = 0xe9; + regs.eflags = (VERW_VALID >> i) & 1 ? 0x200 : 0x240; + regs.eip = (unsigned long)&instr[0]; + regs.ecx = sel; + regs.edx = 0; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || + (regs.eip != (unsigned long)&instr[3]) ) + goto fail; + if ( regs.eflags != ((VERW_VALID >> i) & 1 ? 0x240 : 0x200) ) + { + printf("VERW %04x (type %02x) ", sel, i); + goto fail; + } + } + printf("okay\n"); + #define decl_insn(which) extern const unsigned char which[], which##_len[] #define put_insn(which, insn) ".pushsection .test, \"ax\", @progbits\n" \ #which ": " insn "\n" \ --- a/xen/arch/x86/x86_emulate/x86_emulate.c +++ b/xen/arch/x86/x86_emulate/x86_emulate.c @@ -182,7 +182,7 @@ static const opcode_desc_t opcode_table[ static const opcode_desc_t twobyte_table[256] = { /* 0x00 - 0x07 */ - ModRM, ImplicitOps|ModRM, ModRM, ModRM, + ModRM, ImplicitOps|ModRM, DstReg|SrcMem16|ModRM, DstReg|SrcMem16|ModRM, 0, ImplicitOps, ImplicitOps, ImplicitOps, /* 0x08 - 0x0F */ ImplicitOps, ImplicitOps, 0, ImplicitOps, @@ -1384,7 +1384,7 @@ protmode_load_seg( } /* System segment descriptors must reside in the GDT. */ - if ( !is_x86_user_segment(seg) && (sel & 4) ) + if ( is_x86_system_segment(seg) && (sel & 4) ) goto raise_exn; switch ( rc = ops->read(sel_seg, sel & 0xfff8, &desc, sizeof(desc), ctxt) ) @@ -1401,14 +1401,11 @@ protmode_load_seg( return rc; } - if ( !is_x86_user_segment(seg) ) - { - /* System segments must have S flag == 0. */ - if ( desc.b & (1u << 12) ) - goto raise_exn; - } + /* System segments must have S flag == 0. */ + if ( is_x86_system_segment(seg) && (desc.b & (1u << 12)) ) + goto raise_exn; /* User segments must have S flag == 1. */ - else if ( !(desc.b & (1u << 12)) ) + if ( is_x86_user_segment(seg) && !(desc.b & (1u << 12)) ) goto raise_exn; dpl = (desc.b >> 13) & 3; @@ -1470,10 +1467,17 @@ protmode_load_seg( ((dpl < cpl) || (dpl < rpl)) ) goto raise_exn; break; + case x86_seg_none: + /* Non-conforming segment: check DPL against RPL and CPL. */ + if ( ((desc.b & (0x1c << 8)) != (0x1c << 8)) && + ((dpl < cpl) || (dpl < rpl)) ) + return X86EMUL_EXCEPTION; + a_flag = 0; + break; } /* Segment present in memory? */ - if ( !(desc.b & (1 << 15)) ) + if ( !(desc.b & (1 << 15)) && seg != x86_seg_none ) { fault_type = seg != x86_seg_ss ? EXC_NP : EXC_SS; goto raise_exn; @@ -1481,7 +1485,7 @@ protmode_load_seg( if ( !is_x86_user_segment(seg) ) { - int lm = in_longmode(ctxt, ops); + int lm = (desc.b & (1u << 12)) ? 0 : in_longmode(ctxt, ops); if ( lm < 0 ) return X86EMUL_UNHANDLEABLE; @@ -1501,7 +1505,8 @@ protmode_load_seg( return rc; } if ( (desc_hi.b & 0x00001f00) || - !is_canonical_address((uint64_t)desc_hi.a << 32) ) + (seg != x86_seg_none && + !is_canonical_address((uint64_t)desc_hi.a << 32)) ) goto raise_exn; } } @@ -1544,7 +1549,8 @@ protmode_load_seg( return X86EMUL_OKAY; raise_exn: - generate_exception(fault_type, sel & 0xfffc); + generate_exception_if(seg != x86_seg_none, fault_type, sel & 0xfffc); + rc = X86EMUL_EXCEPTION; done: return rc; } @@ -4424,6 +4430,28 @@ x86_emulate( if ( (rc = load_seg(seg, src.val, 0, NULL, ctxt, ops)) != 0 ) goto done; break; + case 4: /* verr / verw */ + _regs.eflags &= ~EFLG_ZF; + switch ( rc = protmode_load_seg(x86_seg_none, src.val, false, + &sreg, ctxt, ops) ) + { + case X86EMUL_OKAY: + if ( sreg.attr.fields.s && + ((modrm_reg & 1) ? ((sreg.attr.fields.type & 0xa) == 0x2) + : ((sreg.attr.fields.type & 0xa) != 0x8)) ) + _regs.eflags |= EFLG_ZF; + break; + case X86EMUL_EXCEPTION: + if ( ctxt->event_pending ) + { + default: + goto done; + } + /* Instead of the exception, ZF remains cleared. */ + rc = X86EMUL_OKAY; + break; + } + break; default: generate_exception_if(true, EXC_UD); break; @@ -4621,6 +4649,96 @@ x86_emulate( break; } + case X86EMUL_OPC(0x0f, 0x02): /* lar */ + generate_exception_if(!in_protmode(ctxt, ops), EXC_UD); + _regs.eflags &= ~EFLG_ZF; + switch ( rc = protmode_load_seg(x86_seg_none, src.val, false, &sreg, + ctxt, ops) ) + { + case X86EMUL_OKAY: + if ( !sreg.attr.fields.s ) + { + switch ( sreg.attr.fields.type ) + { + case 0x01: /* available 16-bit TSS */ + case 0x03: /* busy 16-bit TSS */ + case 0x04: /* 16-bit call gate */ + case 0x05: /* 16/32-bit task gate */ + if ( in_longmode(ctxt, ops) ) + break; + /* fall through */ + case 0x02: /* LDT */ + case 0x09: /* available 32/64-bit TSS */ + case 0x0b: /* busy 32/64-bit TSS */ + case 0x0c: /* 32/64-bit call gate */ + _regs.eflags |= EFLG_ZF; + break; + } + } + else + _regs.eflags |= EFLG_ZF; + break; + case X86EMUL_EXCEPTION: + if ( ctxt->event_pending ) + { + default: + goto done; + } + /* Instead of the exception, ZF remains cleared. */ + rc = X86EMUL_OKAY; + break; + } + if ( _regs.eflags & EFLG_ZF ) + dst.val = ((sreg.attr.bytes & 0xff) << 8) | + ((sreg.limit >> (sreg.attr.fields.g ? 12 : 0)) & + 0xf0000) | + ((sreg.attr.bytes & 0xf00) << 12); + else + dst.type = OP_NONE; + break; + + case X86EMUL_OPC(0x0f, 0x03): /* lsl */ + generate_exception_if(!in_protmode(ctxt, ops), EXC_UD); + _regs.eflags &= ~EFLG_ZF; + switch ( rc = protmode_load_seg(x86_seg_none, src.val, false, &sreg, + ctxt, ops) ) + { + case X86EMUL_OKAY: + if ( !sreg.attr.fields.s ) + { + switch ( sreg.attr.fields.type ) + { + case 0x01: /* available 16-bit TSS */ + case 0x03: /* busy 16-bit TSS */ + if ( in_longmode(ctxt, ops) ) + break; + /* fall through */ + case 0x02: /* LDT */ + case 0x09: /* available 32/64-bit TSS */ + case 0x0b: /* busy 32/64-bit TSS */ + _regs.eflags |= EFLG_ZF; + break; + } + } + else + _regs.eflags |= EFLG_ZF; + break; + case X86EMUL_EXCEPTION: + if ( ctxt->event_pending ) + { + default: + goto done; + } + /* Instead of the exception, ZF remains cleared. */ + rc = X86EMUL_OKAY; + break; + } + if ( _regs.eflags & EFLG_ZF ) + dst.val = sreg.limit; + else + dst.type = OP_NONE; + break; + case X86EMUL_OPC(0x0f, 0x05): /* syscall */ { uint64_t msr_content; --- a/tools/tests/x86_emulator/test_x86_emulator.c +++ b/tools/tests/x86_emulator/test_x86_emulator.c @@ -46,7 +46,47 @@ static int read( if ( verbose ) printf("** %s(%u, %p,, %u,)\n", __func__, seg, (void *)offset, bytes); - bytes_read += bytes; + switch ( seg ) + { + uint64_t value; + + case x86_seg_gdtr: + /* Fake system segment type matching table index. */ + if ( (offset & 7) || (bytes > 8) ) + return X86EMUL_UNHANDLEABLE; +#ifdef __x86_64__ + if ( !(offset & 8) ) + { + memset(p_data, 0, bytes); + return X86EMUL_OKAY; + } + value = (offset - 8) >> 4; +#else + value = (offset - 8) >> 3; +#endif + if ( value >= 0x10 ) + return X86EMUL_UNHANDLEABLE; + value |= value << 40; + memcpy(p_data, &value, bytes); + return X86EMUL_OKAY; + + case x86_seg_ldtr: + /* Fake user segment type matching table index. */ + if ( (offset & 7) || (bytes > 8) ) + return X86EMUL_UNHANDLEABLE; + value = offset >> 3; + if ( value >= 0x10 ) + return X86EMUL_UNHANDLEABLE; + value |= (value | 0x10) << 40; + memcpy(p_data, &value, bytes); + return X86EMUL_OKAY; + + default: + if ( !is_x86_user_segment(seg) ) + return X86EMUL_UNHANDLEABLE; + bytes_read += bytes; + break; + } memcpy(p_data, (void *)offset, bytes); return X86EMUL_OKAY; } @@ -75,6 +115,8 @@ static int write( if ( verbose ) printf("** %s(%u, %p,, %u,)\n", __func__, seg, (void *)offset, bytes); + if ( !is_x86_user_segment(seg) ) + return X86EMUL_UNHANDLEABLE; memcpy((void *)offset, p_data, bytes); return X86EMUL_OKAY; } @@ -90,10 +132,24 @@ static int cmpxchg( if ( verbose ) printf("** %s(%u, %p,, %u,)\n", __func__, seg, (void *)offset, bytes); + if ( !is_x86_user_segment(seg) ) + return X86EMUL_UNHANDLEABLE; memcpy((void *)offset, new, bytes); return X86EMUL_OKAY; } +static int read_segment( + enum x86_segment seg, + struct segment_register *reg, + struct x86_emulate_ctxt *ctxt) +{ + if ( !is_x86_user_segment(seg) ) + return X86EMUL_UNHANDLEABLE; + memset(reg, 0, sizeof(*reg)); + reg->attr.fields.p = 1; + return X86EMUL_OKAY; +} + static int cpuid( unsigned int *eax, unsigned int *ebx, @@ -193,6 +249,21 @@ static int read_cr( return X86EMUL_UNHANDLEABLE; } +static int read_msr( + unsigned int reg, + uint64_t *val, + struct x86_emulate_ctxt *ctxt) +{ + switch ( reg ) + { + case 0xc0000080: /* EFER */ + *val = ctxt->addr_size > 32 ? 0x500 /* LME|LMA */ : 0; + return X86EMUL_OKAY; + } + + return X86EMUL_UNHANDLEABLE; +} + int get_fpu( void (*exception_callback)(void *, struct cpu_user_regs *), void *exception_callback_arg, @@ -223,8 +294,10 @@ static struct x86_emulate_ops emulops = .insn_fetch = fetch, .write = write, .cmpxchg = cmpxchg, + .read_segment = read_segment, .cpuid = cpuid, .read_cr = read_cr, + .read_msr = read_msr, .get_fpu = get_fpu, }; @@ -726,6 +799,156 @@ int main(int argc, char **argv) goto fail; printf("okay\n"); + printf("%-40s", "Testing lar (null selector)..."); + instr[0] = 0x0f; instr[1] = 0x02; instr[2] = 0xc1; + regs.eflags = 0x240; + regs.eip = (unsigned long)&instr[0]; + regs.ecx = 0; + regs.eax = 0x11111111; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || + (regs.eax != 0x11111111) || + (regs.eflags != 0x200) || + (regs.eip != (unsigned long)&instr[3]) ) + goto fail; + printf("okay\n"); + + printf("%-40s", "Testing lsl (null selector)..."); + instr[0] = 0x0f; instr[1] = 0x03; instr[2] = 0xca; + regs.eflags = 0x240; + regs.eip = (unsigned long)&instr[0]; + regs.edx = 0; + regs.ecx = 0x11111111; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || + (regs.ecx != 0x11111111) || + (regs.eflags != 0x200) || + (regs.eip != (unsigned long)&instr[3]) ) + goto fail; + printf("okay\n"); + + printf("%-40s", "Testing verr (null selector)..."); + instr[0] = 0x0f; instr[1] = 0x00; instr[2] = 0x21; + regs.eflags = 0x240; + regs.eip = (unsigned long)&instr[0]; + regs.ecx = (unsigned long)res; + *res = 0; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || + (regs.eflags != 0x200) || + (regs.eip != (unsigned long)&instr[3]) ) + goto fail; + printf("okay\n"); + + printf("%-40s", "Testing verw (null selector)..."); + instr[0] = 0x0f; instr[1] = 0x00; instr[2] = 0x2a; + regs.eflags = 0x240; + regs.eip = (unsigned long)&instr[0]; + regs.ecx = 0; + regs.edx = (unsigned long)res; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || + (regs.eflags != 0x200) || + (regs.eip != (unsigned long)&instr[3]) ) + goto fail; + printf("okay\n"); + + printf("%-40s", "Testing lar/lsl/verr/verw (all types)..."); + for ( i = 0; i < 0x20; ++i ) + { + unsigned int sel = i < 0x10 ? +#ifndef __x86_64__ + (i << 3) + 8 +#else + (i << 4) + 8 +#endif + : ((i - 0x10) << 3) | 4; + bool failed; + +#ifndef __x86_64__ +# define LAR_VALID 0xffff1a3eU +# define LSL_VALID 0xffff0a0eU +#else +# define LAR_VALID 0xffff1a04U +# define LSL_VALID 0xffff0a04U +#endif +#define VERR_VALID 0xccff0000U +#define VERW_VALID 0x00cc0000U + + instr[0] = 0x0f; instr[1] = 0x02; instr[2] = 0xc2; + regs.eflags = (LAR_VALID >> i) & 1 ? 0x200 : 0x240; + regs.eip = (unsigned long)&instr[0]; + regs.edx = sel; + regs.eax = 0x11111111; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || + (regs.eip != (unsigned long)&instr[3]) ) + goto fail; + if ( (LAR_VALID >> i) & 1 ) + failed = (regs.eflags != 0x240) || + ((regs.eax & 0xf0ff00) != (i << 8)); + else + failed = (regs.eflags != 0x200) || + (regs.eax != 0x11111111); + if ( failed ) + { + printf("LAR %04x (type %02x) ", sel, i); + goto fail; + } + + instr[0] = 0x0f; instr[1] = 0x03; instr[2] = 0xd1; + regs.eflags = (LSL_VALID >> i) & 1 ? 0x200 : 0x240; + regs.eip = (unsigned long)&instr[0]; + regs.ecx = sel; + regs.edx = 0x11111111; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || + (regs.eip != (unsigned long)&instr[3]) ) + goto fail; + if ( (LSL_VALID >> i) & 1 ) + failed = (regs.eflags != 0x240) || + (regs.edx != (i & 0xf)); + else + failed = (regs.eflags != 0x200) || + (regs.edx != 0x11111111); + if ( failed ) + { + printf("LSL %04x (type %02x) ", sel, i); + goto fail; + } + + instr[0] = 0x0f; instr[1] = 0x00; instr[2] = 0xe2; + regs.eflags = (VERR_VALID >> i) & 1 ? 0x200 : 0x240; + regs.eip = (unsigned long)&instr[0]; + regs.ecx = 0; + regs.edx = sel; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || + (regs.eip != (unsigned long)&instr[3]) ) + goto fail; + if ( regs.eflags != ((VERR_VALID >> i) & 1 ? 0x240 : 0x200) ) + { + printf("VERR %04x (type %02x) ", sel, i); + goto fail; + } + + instr[0] = 0x0f; instr[1] = 0x00; instr[2] = 0xe9; + regs.eflags = (VERW_VALID >> i) & 1 ? 0x200 : 0x240; + regs.eip = (unsigned long)&instr[0]; + regs.ecx = sel; + regs.edx = 0; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || + (regs.eip != (unsigned long)&instr[3]) ) + goto fail; + if ( regs.eflags != ((VERW_VALID >> i) & 1 ? 0x240 : 0x200) ) + { + printf("VERW %04x (type %02x) ", sel, i); + goto fail; + } + } + printf("okay\n"); + #define decl_insn(which) extern const unsigned char which[], which##_len[] #define put_insn(which, insn) ".pushsection .test, \"ax\", @progbits\n" \ #which ": " insn "\n" \ --- a/xen/arch/x86/x86_emulate/x86_emulate.c +++ b/xen/arch/x86/x86_emulate/x86_emulate.c @@ -182,7 +182,7 @@ static const opcode_desc_t opcode_table[ static const opcode_desc_t twobyte_table[256] = { /* 0x00 - 0x07 */ - ModRM, ImplicitOps|ModRM, ModRM, ModRM, + ModRM, ImplicitOps|ModRM, DstReg|SrcMem16|ModRM, DstReg|SrcMem16|ModRM, 0, ImplicitOps, ImplicitOps, ImplicitOps, /* 0x08 - 0x0F */ ImplicitOps, ImplicitOps, 0, ImplicitOps, @@ -1384,7 +1384,7 @@ protmode_load_seg( } /* System segment descriptors must reside in the GDT. */ - if ( !is_x86_user_segment(seg) && (sel & 4) ) + if ( is_x86_system_segment(seg) && (sel & 4) ) goto raise_exn; switch ( rc = ops->read(sel_seg, sel & 0xfff8, &desc, sizeof(desc), ctxt) ) @@ -1401,14 +1401,11 @@ protmode_load_seg( return rc; } - if ( !is_x86_user_segment(seg) ) - { - /* System segments must have S flag == 0. */ - if ( desc.b & (1u << 12) ) - goto raise_exn; - } + /* System segments must have S flag == 0. */ + if ( is_x86_system_segment(seg) && (desc.b & (1u << 12)) ) + goto raise_exn; /* User segments must have S flag == 1. */ - else if ( !(desc.b & (1u << 12)) ) + if ( is_x86_user_segment(seg) && !(desc.b & (1u << 12)) ) goto raise_exn; dpl = (desc.b >> 13) & 3; @@ -1470,10 +1467,17 @@ protmode_load_seg( ((dpl < cpl) || (dpl < rpl)) ) goto raise_exn; break; + case x86_seg_none: + /* Non-conforming segment: check DPL against RPL and CPL. */ + if ( ((desc.b & (0x1c << 8)) != (0x1c << 8)) && + ((dpl < cpl) || (dpl < rpl)) ) + return X86EMUL_EXCEPTION; + a_flag = 0; + break; } /* Segment present in memory? */ - if ( !(desc.b & (1 << 15)) ) + if ( !(desc.b & (1 << 15)) && seg != x86_seg_none ) { fault_type = seg != x86_seg_ss ? EXC_NP : EXC_SS; goto raise_exn; @@ -1481,7 +1485,7 @@ protmode_load_seg( if ( !is_x86_user_segment(seg) ) { - int lm = in_longmode(ctxt, ops); + int lm = (desc.b & (1u << 12)) ? 0 : in_longmode(ctxt, ops); if ( lm < 0 ) return X86EMUL_UNHANDLEABLE; @@ -1501,7 +1505,8 @@ protmode_load_seg( return rc; } if ( (desc_hi.b & 0x00001f00) || - !is_canonical_address((uint64_t)desc_hi.a << 32) ) + (seg != x86_seg_none && + !is_canonical_address((uint64_t)desc_hi.a << 32)) ) goto raise_exn; } } @@ -1544,7 +1549,8 @@ protmode_load_seg( return X86EMUL_OKAY; raise_exn: - generate_exception(fault_type, sel & 0xfffc); + generate_exception_if(seg != x86_seg_none, fault_type, sel & 0xfffc); + rc = X86EMUL_EXCEPTION; done: return rc; } @@ -4424,6 +4430,28 @@ x86_emulate( if ( (rc = load_seg(seg, src.val, 0, NULL, ctxt, ops)) != 0 ) goto done; break; + case 4: /* verr / verw */ + _regs.eflags &= ~EFLG_ZF; + switch ( rc = protmode_load_seg(x86_seg_none, src.val, false, + &sreg, ctxt, ops) ) + { + case X86EMUL_OKAY: + if ( sreg.attr.fields.s && + ((modrm_reg & 1) ? ((sreg.attr.fields.type & 0xa) == 0x2) + : ((sreg.attr.fields.type & 0xa) != 0x8)) ) + _regs.eflags |= EFLG_ZF; + break; + case X86EMUL_EXCEPTION: + if ( ctxt->event_pending ) + { + default: + goto done; + } + /* Instead of the exception, ZF remains cleared. */ + rc = X86EMUL_OKAY; + break; + } + break; default: generate_exception_if(true, EXC_UD); break; @@ -4621,6 +4649,96 @@ x86_emulate( break; } + case X86EMUL_OPC(0x0f, 0x02): /* lar */ + generate_exception_if(!in_protmode(ctxt, ops), EXC_UD); + _regs.eflags &= ~EFLG_ZF; + switch ( rc = protmode_load_seg(x86_seg_none, src.val, false, &sreg, + ctxt, ops) ) + { + case X86EMUL_OKAY: + if ( !sreg.attr.fields.s ) + { + switch ( sreg.attr.fields.type ) + { + case 0x01: /* available 16-bit TSS */ + case 0x03: /* busy 16-bit TSS */ + case 0x04: /* 16-bit call gate */ + case 0x05: /* 16/32-bit task gate */ + if ( in_longmode(ctxt, ops) ) + break; + /* fall through */ + case 0x02: /* LDT */ + case 0x09: /* available 32/64-bit TSS */ + case 0x0b: /* busy 32/64-bit TSS */ + case 0x0c: /* 32/64-bit call gate */ + _regs.eflags |= EFLG_ZF; + break; + } + } + else + _regs.eflags |= EFLG_ZF; + break; + case X86EMUL_EXCEPTION: + if ( ctxt->event_pending ) + { + default: + goto done; + } + /* Instead of the exception, ZF remains cleared. */ + rc = X86EMUL_OKAY; + break; + } + if ( _regs.eflags & EFLG_ZF ) + dst.val = ((sreg.attr.bytes & 0xff) << 8) | + ((sreg.limit >> (sreg.attr.fields.g ? 12 : 0)) & + 0xf0000) | + ((sreg.attr.bytes & 0xf00) << 12); + else + dst.type = OP_NONE; + break; + + case X86EMUL_OPC(0x0f, 0x03): /* lsl */ + generate_exception_if(!in_protmode(ctxt, ops), EXC_UD); + _regs.eflags &= ~EFLG_ZF; + switch ( rc = protmode_load_seg(x86_seg_none, src.val, false, &sreg, + ctxt, ops) ) + { + case X86EMUL_OKAY: + if ( !sreg.attr.fields.s ) + { + switch ( sreg.attr.fields.type ) + { + case 0x01: /* available 16-bit TSS */ + case 0x03: /* busy 16-bit TSS */ + if ( in_longmode(ctxt, ops) ) + break; + /* fall through */ + case 0x02: /* LDT */ + case 0x09: /* available 32/64-bit TSS */ + case 0x0b: /* busy 32/64-bit TSS */ + _regs.eflags |= EFLG_ZF; + break; + } + } + else + _regs.eflags |= EFLG_ZF; + break; + case X86EMUL_EXCEPTION: + if ( ctxt->event_pending ) + { + default: + goto done; + } + /* Instead of the exception, ZF remains cleared. */ + rc = X86EMUL_OKAY; + break; + } + if ( _regs.eflags & EFLG_ZF ) + dst.val = sreg.limit; + else + dst.type = OP_NONE; + break; + case X86EMUL_OPC(0x0f, 0x05): /* syscall */ { uint64_t msr_content;