/****************************************************************************** * arch/x86/pv/emul-inv-op.c * * Emulate invalid op for PV guests * * Modifications to Linux original are copyright (c) 2002-2004, K A Fraser * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "emulate.h" static int emulate_invalid_rdtscp(struct cpu_user_regs *regs) { char opcode[3]; unsigned long eip, rc; struct vcpu *v = current; eip = regs->rip; if ( (rc = copy_from_user(opcode, (char *)eip, sizeof(opcode))) != 0 ) { pv_inject_page_fault(0, eip + sizeof(opcode) - rc); return EXCRET_fault_fixed; } if ( memcmp(opcode, "\xf\x1\xf9", sizeof(opcode)) ) return 0; eip += sizeof(opcode); pv_soft_rdtsc(v, regs, 1); pv_emul_instruction_done(regs, eip); return EXCRET_fault_fixed; } static int emulate_forced_invalid_op(struct cpu_user_regs *regs) { char sig[5], instr[2]; unsigned long eip, rc; struct cpuid_leaf res; const struct msr_vcpu_policy *vp = current->arch.msr; eip = regs->rip; /* Check for forced emulation signature: ud2 ; .ascii "xen". */ if ( (rc = copy_from_user(sig, (char *)eip, sizeof(sig))) != 0 ) { pv_inject_page_fault(0, eip + sizeof(sig) - rc); return EXCRET_fault_fixed; } if ( memcmp(sig, "\xf\xbxen", sizeof(sig)) ) return 0; eip += sizeof(sig); /* We only emulate CPUID. */ if ( ( rc = copy_from_user(instr, (char *)eip, sizeof(instr))) != 0 ) { pv_inject_page_fault(0, eip + sizeof(instr) - rc); return EXCRET_fault_fixed; } if ( memcmp(instr, "\xf\xa2", sizeof(instr)) ) return 0; /* If cpuid faulting is enabled and CPL>0 inject a #GP in place of #UD. */ if ( vp->misc_features_enables.cpuid_faulting && !guest_kernel_mode(current, regs) ) { regs->rip = eip; pv_inject_hw_exception(TRAP_gp_fault, regs->error_code); return EXCRET_fault_fixed; } eip += sizeof(instr); guest_cpuid(current, regs->eax, regs->ecx, &res); regs->rax = res.a; regs->rbx = res.b; regs->rcx = res.c; regs->rdx = res.d; pv_emul_instruction_done(regs, eip); trace_trap_one_addr(TRC_PV_FORCED_INVALID_OP, regs->rip); return EXCRET_fault_fixed; } bool pv_emulate_invalid_op(struct cpu_user_regs *regs) { return !emulate_invalid_rdtscp(regs) && !emulate_forced_invalid_op(regs); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */