From patchwork Tue May 5 08:16:20 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jan Beulich X-Patchwork-Id: 11528393 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 3E0081392 for ; Tue, 5 May 2020 08:17:18 +0000 (UTC) Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 2417A2068E for ; Tue, 5 May 2020 08:17:18 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 2417A2068E Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=suse.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=xen-devel-bounces@lists.xenproject.org Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1jVska-0004SC-PC; Tue, 05 May 2020 08:16:24 +0000 Received: from us1-rack-iad1.inumbo.com ([172.99.69.81]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1jVska-0004S2-9N for xen-devel@lists.xenproject.org; Tue, 05 May 2020 08:16:24 +0000 X-Inumbo-ID: b4e8ef72-8ea8-11ea-b9cf-bc764e2007e4 Received: from mx2.suse.de (unknown [195.135.220.15]) by us1-rack-iad1.inumbo.com (Halon) with ESMTPS id b4e8ef72-8ea8-11ea-b9cf-bc764e2007e4; Tue, 05 May 2020 08:16:23 +0000 (UTC) X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id B68C7AFF0; Tue, 5 May 2020 08:16:24 +0000 (UTC) Subject: [PATCH v8 08/12] x86emul: support FLDENV and FRSTOR From: Jan Beulich To: "xen-devel@lists.xenproject.org" References: <60cc730f-2a1c-d7a6-74fe-64f3c9308831@suse.com> Message-ID: <09fe2c18-0037-af71-93be-87261051e2a2@suse.com> Date: Tue, 5 May 2020 10:16:20 +0200 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:68.0) Gecko/20100101 Thunderbird/68.7.0 MIME-Version: 1.0 In-Reply-To: <60cc730f-2a1c-d7a6-74fe-64f3c9308831@suse.com> Content-Language: en-US X-BeenThere: xen-devel@lists.xenproject.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Cc: Andrew Cooper , Wei Liu , Roger Pau Monne Errors-To: xen-devel-bounces@lists.xenproject.org Sender: "Xen-devel" While the Intel SDM claims that FRSTOR itself may raise #MF upon completion, this was confirmed by Intel to be a doc error which will be corrected in due course; behavior is like FLDENV, and like old hard copy manuals describe it. Otherwise we'd have to emulate the insn by filling st(N) in suitable order, followed by FLDENV. Signed-off-by: Jan Beulich --- v7: New. --- a/tools/tests/x86_emulator/test_x86_emulator.c +++ b/tools/tests/x86_emulator/test_x86_emulator.c @@ -2442,6 +2442,27 @@ int main(int argc, char **argv) else printf("skipped\n"); + printf("%-40s", "Testing fldenv 8(%edx)..."); + if ( stack_exec && cpu_has_fpu ) + { + asm volatile ( "fnstenv %0\n\t" + "fninit" + : "=m" (res[2]) :: "memory" ); + zap_fpsel(&res[2], true); + instr[0] = 0xd9; instr[1] = 0x62; instr[2] = 0x08; + regs.eip = (unsigned long)&instr[0]; + regs.edx = (unsigned long)res; + rc = x86_emulate(&ctxt, &emulops); + asm volatile ( "fnstenv %0" : "=m" (res[9]) :: "memory" ); + if ( (rc != X86EMUL_OKAY) || + memcmp(res + 2, res + 9, 28) || + (regs.eip != (unsigned long)&instr[3]) ) + goto fail; + printf("okay\n"); + } + else + printf("skipped\n"); + printf("%-40s", "Testing 16-bit fnsave (%ecx)..."); if ( stack_exec && cpu_has_fpu ) { @@ -2468,6 +2489,31 @@ int main(int argc, char **argv) goto fail; printf("okay\n"); } + else + printf("skipped\n"); + + printf("%-40s", "Testing frstor (%edx)..."); + if ( stack_exec && cpu_has_fpu ) + { + const uint16_t seven = 7; + + asm volatile ( "fninit\n\t" + "fld1\n\t" + "fidivs %1\n\t" + "fnsave %0\n\t" + : "=&m" (res[0]) : "m" (seven) : "memory" ); + zap_fpsel(&res[0], true); + instr[0] = 0xdd; instr[1] = 0x22; + regs.eip = (unsigned long)&instr[0]; + regs.edx = (unsigned long)res; + rc = x86_emulate(&ctxt, &emulops); + asm volatile ( "fnsave %0" : "=m" (res[27]) :: "memory" ); + if ( (rc != X86EMUL_OKAY) || + memcmp(res, res + 27, 108) || + (regs.eip != (unsigned long)&instr[2]) ) + goto fail; + printf("okay\n"); + } else printf("skipped\n"); --- a/xen/arch/x86/x86_emulate/x86_emulate.c +++ b/xen/arch/x86/x86_emulate/x86_emulate.c @@ -857,6 +857,7 @@ struct x86_emulate_state { blk_NONE, blk_enqcmd, #ifndef X86EMUL_NO_FPU + blk_fld, /* FLDENV, FRSTOR */ blk_fst, /* FNSTENV, FNSAVE */ #endif blk_movdir, @@ -4948,21 +4949,14 @@ x86_emulate( dst.bytes = 4; emulate_fpu_insn_memdst(b, modrm_reg & 7, dst.val); break; - case 4: /* fldenv - TODO */ - state->fpu_ctrl = true; - goto unimplemented_insn; - case 5: /* fldcw m2byte */ - state->fpu_ctrl = true; - fpu_memsrc16: - if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val, - 2, ctxt)) != X86EMUL_OKAY ) - goto done; - emulate_fpu_insn_memsrc(b, modrm_reg & 7, src.val); - break; + case 4: /* fldenv */ + /* Raise #MF now if there are pending unmasked exceptions. */ + emulate_fpu_insn_stub(0xd9, 0xd0 /* fnop */); + /* fall through */ case 6: /* fnstenv */ fail_if(!ops->blk); - state->blk = blk_fst; - /* REX is meaningless for this insn by this point. */ + state->blk = modrm_reg & 2 ? blk_fst : blk_fld; + /* REX is meaningless for these insns by this point. */ rex_prefix = in_protmode(ctxt, ops); if ( (rc = ops->blk(ea.mem.seg, ea.mem.off, NULL, op_bytes > 2 ? sizeof(struct x87_env32) @@ -4972,6 +4966,14 @@ x86_emulate( goto done; state->fpu_ctrl = true; break; + case 5: /* fldcw m2byte */ + state->fpu_ctrl = true; + fpu_memsrc16: + if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val, + 2, ctxt)) != X86EMUL_OKAY ) + goto done; + emulate_fpu_insn_memsrc(b, modrm_reg & 7, src.val); + break; case 7: /* fnstcw m2byte */ state->fpu_ctrl = true; fpu_memdst16: @@ -5124,13 +5126,14 @@ x86_emulate( dst.bytes = 8; emulate_fpu_insn_memdst(b, modrm_reg & 7, dst.val); break; - case 4: /* frstor - TODO */ - state->fpu_ctrl = true; - goto unimplemented_insn; + case 4: /* frstor */ + /* Raise #MF now if there are pending unmasked exceptions. */ + emulate_fpu_insn_stub(0xd9, 0xd0 /* fnop */); + /* fall through */ case 6: /* fnsave */ fail_if(!ops->blk); - state->blk = blk_fst; - /* REX is meaningless for this insn by this point. */ + state->blk = modrm_reg & 2 ? blk_fst : blk_fld; + /* REX is meaningless for these insns by this point. */ rex_prefix = in_protmode(ctxt, ops); if ( (rc = ops->blk(ea.mem.seg, ea.mem.off, NULL, op_bytes > 2 ? sizeof(struct x87_env32) + 80 @@ -11648,6 +11651,89 @@ int x86_emul_blk( #ifndef X86EMUL_NO_FPU + case blk_fld: + ASSERT(!data); + + /* state->rex_prefix carries CR0.PE && !EFLAGS.VM setting */ + switch ( bytes ) + { + case sizeof(fpstate.env): + case sizeof(fpstate): + memcpy(&fpstate.env, ptr, sizeof(fpstate.env)); + if ( !state->rex_prefix ) + { + unsigned int fip = fpstate.env.mode.real.fip_lo + + (fpstate.env.mode.real.fip_hi << 16); + unsigned int fdp = fpstate.env.mode.real.fdp_lo + + (fpstate.env.mode.real.fdp_hi << 16); + unsigned int fop = fpstate.env.mode.real.fop; + + fpstate.env.mode.prot.fip = fip & 0xf; + fpstate.env.mode.prot.fcs = fip >> 4; + fpstate.env.mode.prot.fop = fop; + fpstate.env.mode.prot.fdp = fdp & 0xf; + fpstate.env.mode.prot.fds = fdp >> 4; + } + + if ( bytes == sizeof(fpstate.env) ) + ptr = NULL; + else + ptr += sizeof(fpstate.env); + break; + + case sizeof(struct x87_env16): + case sizeof(struct x87_env16) + sizeof(fpstate.freg): + { + const struct x87_env16 *env = ptr; + + fpstate.env.fcw = env->fcw; + fpstate.env.fsw = env->fsw; + fpstate.env.ftw = env->ftw; + + if ( state->rex_prefix ) + { + fpstate.env.mode.prot.fip = env->mode.prot.fip; + fpstate.env.mode.prot.fcs = env->mode.prot.fcs; + fpstate.env.mode.prot.fdp = env->mode.prot.fdp; + fpstate.env.mode.prot.fds = env->mode.prot.fds; + fpstate.env.mode.prot.fop = 0; /* unknown */ + } + else + { + unsigned int fip = env->mode.real.fip_lo + + (env->mode.real.fip_hi << 16); + unsigned int fdp = env->mode.real.fdp_lo + + (env->mode.real.fdp_hi << 16); + unsigned int fop = env->mode.real.fop; + + fpstate.env.mode.prot.fip = fip & 0xf; + fpstate.env.mode.prot.fcs = fip >> 4; + fpstate.env.mode.prot.fop = fop; + fpstate.env.mode.prot.fdp = fdp & 0xf; + fpstate.env.mode.prot.fds = fdp >> 4; + } + + if ( bytes == sizeof(*env) ) + ptr = NULL; + else + ptr += sizeof(*env); + break; + } + + default: + ASSERT_UNREACHABLE(); + return X86EMUL_UNHANDLEABLE; + } + + if ( ptr ) + { + memcpy(fpstate.freg, ptr, sizeof(fpstate.freg)); + asm volatile ( "frstor %0" :: "m" (fpstate) ); + } + else + asm volatile ( "fldenv %0" :: "m" (fpstate.env) ); + break; + case blk_fst: ASSERT(!data);