📄 emulate.c
字号:
/* * emulate.c: handling SVM emulate instructions help. * Copyright (c) 2005 AMD Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307 USA. * */#include <xen/config.h>#include <xen/init.h>#include <xen/lib.h>#include <xen/trace.h>#include <asm/msr.h>#include <asm/hvm/hvm.h>#include <asm/hvm/support.h>#include <asm/hvm/svm/svm.h>#include <asm/hvm/svm/vmcb.h>#include <asm/hvm/svm/emulate.h>#define REX_PREFIX_BASE 0x40#define REX_X 0x02#define REX_W 0x08#define REX_R 0x04#define REX_B 0x01#define IS_REX_PREFIX(prefix) ((prefix & 0xf0) == REX_PREFIX_BASE)#define DECODE_MODRM_MOD(modrm) ((modrm & 0xC0) >> 6)#define DECODE_MODRM_REG(prefix, modrm) \ ((prefix & REX_R) && IS_REX_PREFIX(prefix)) \ ? (0x08 | ((modrm >> 3) & 0x07)) : ((modrm >> 3) & 0x07)#define DECODE_MODRM_RM(prefix, modrm) \ ((prefix & REX_B) && IS_REX_PREFIX(prefix)) \ ? (0x08 | (modrm & 0x07)) : (modrm & 0x07)#define DECODE_SIB_SCALE(sib) DECODE_MODRM_MOD(sib)#define DECODE_SIB_INDEX(prefix, sib) \ ((prefix & REX_X) && IS_REX_PREFIX(prefix)) \ ? (0x08 | ((sib >> 3) & 0x07)) : ((sib >> 3) & 0x07)#define DECODE_SIB_BASE(prefix, sib) DECODE_MODRM_RM(prefix, sib)static inline unsigned long DECODE_GPR_VALUE( struct cpu_user_regs *regs, u8 gpr_rm){ unsigned long value; switch (gpr_rm) { case 0x0: value = regs->eax; break; case 0x1: value = regs->ecx; break; case 0x2: value = regs->edx; break; case 0x3: value = regs->ebx; break; case 0x4: value = regs->esp; case 0x5: value = regs->ebp; break; case 0x6: value = regs->esi; break; case 0x7: value = regs->edi; break;#if __x86_64__ case 0x8: value = regs->r8; break; case 0x9: value = regs->r9; break; case 0xA: value = regs->r10; break; case 0xB: value = regs->r11; break; case 0xC: value = regs->r12; break; case 0xD: value = regs->r13; break; case 0xE: value = regs->r14; break; case 0xF: value = regs->r15; break;#endif default: printk("Invlaid gpr_rm = %d\n", gpr_rm); ASSERT(0); value = (unsigned long)-1; /* error retrun */ } return value;}#define CHECK_LENGTH64(num) \ if (num > length) \ { \ *size = 0; \ return (unsigned long) -1; \ }#define modrm operand [0]#define sib operand [1]unsigned long get_effective_addr_modrm64(struct cpu_user_regs *regs, const u8 prefix, int inst_len, const u8 *operand, u8 *size){ unsigned long effective_addr = (unsigned long) -1; u8 length, modrm_mod, modrm_rm; u32 disp = 0; struct vcpu *v = current; struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; HVM_DBG_LOG(DBG_LEVEL_1, "prefix = %x, length = %d, operand[0,1] = %x %x", prefix, *size, operand[0], operand[1]); if ((NULL == size) || (NULL == operand) || (1 > *size)) { *size = 0; return effective_addr; } modrm_mod = DECODE_MODRM_MOD(modrm); modrm_rm = DECODE_MODRM_RM(prefix, modrm); length = *size; *size = 1; switch (modrm_rm) { case 0x4:#if __x86_64__ case 0xC:#endif if (modrm_mod < 3) { *size = length; effective_addr = get_effective_addr_sib(vmcb, regs, prefix, operand, size); } else { effective_addr = DECODE_GPR_VALUE(regs, modrm_rm); } break; case 0x5: if (0 < modrm_mod) { effective_addr = regs->ebp; *size = 1; break; }#if __x86_64__ /* FALLTHRU */ case 0xD: if (0 < modrm_mod) { *size = 1; effective_addr = regs->r13; break; }#endif CHECK_LENGTH64(*size + (u8)sizeof(u32)); memcpy (&disp, operand + 1, sizeof (u32)); *size += sizeof (u32);#if __x86_64__ /* 64-bit mode */ if (vmcb->cs.attr.fields.l && hvm_long_mode_enabled(v)) return regs->eip + inst_len + *size + disp;#endif return disp; default: effective_addr = DECODE_GPR_VALUE(regs, modrm_rm); } if (3 > modrm_mod) { if (1 == modrm_mod ) { CHECK_LENGTH64(*size + (u8)sizeof(u8)); disp = sib; *size += sizeof (u8); } else if (2 == modrm_mod ) { CHECK_LENGTH64(*size + sizeof (u32)); memcpy (&disp, operand + 1, sizeof (u32)); *size += sizeof (u32); } effective_addr += disp; } return effective_addr;}unsigned long get_effective_addr_sib(struct vmcb_struct *vmcb, struct cpu_user_regs *regs, const u8 prefix, const u8 *operand, u8 *size){ unsigned long base, effective_addr = (unsigned long)-1; u8 sib_scale, sib_idx, sib_base, length; u32 disp = 0; if (NULL == size || NULL == operand || 2 > *size) { *size = 0; return effective_addr; } sib_scale = DECODE_SIB_SCALE(sib); sib_idx = DECODE_SIB_INDEX(prefix, sib); sib_base = DECODE_SIB_BASE(prefix, sib); base = DECODE_GPR_VALUE(regs, sib_base); if ((unsigned long)-1 == base) { /* * Surely this is wrong. base should be allowed to be -1, even if * it's not the usual case... */ *size = 0; return base; } length = *size; *size = 2; if (0x5 == (sib_base & 0x5)) { switch (DECODE_MODRM_MOD(modrm)) { case 0: CHECK_LENGTH64(*size + (u8)sizeof(u32)); memcpy (&disp, operand + 2, sizeof(u32)); *size += sizeof(u32); base = disp; break; case 1: CHECK_LENGTH64(*size + (u8)sizeof (u8)); *size += sizeof(u8); base += operand [2]; break; case 2: CHECK_LENGTH64(*size + (u8)sizeof (u32)); memcpy(&disp, operand + 2, sizeof(u32)); *size += sizeof(u32); base += disp; } } if (4 == sib_idx) return base; effective_addr = DECODE_GPR_VALUE(regs, sib_idx); effective_addr <<= sib_scale; return (effective_addr + base);}/* Get the register/mode number of src register in ModRM register. */unsigned int decode_dest_reg(u8 prefix, u8 m){ return DECODE_MODRM_REG(prefix, m);}unsigned int decode_src_reg(u8 prefix, u8 m){ return DECODE_MODRM_RM(prefix, m);}unsigned long svm_rip2pointer(struct vcpu *v){ /* * The following is subtle. Intuitively this code would be something like: * * if (16bit) addr = (cs << 4) + rip; else addr = rip; * * However, this code doesn't work for code executing after CR0.PE=0, * but before the %cs has been updated. We don't get signalled when * %cs is update, but fortunately, base contain the valid base address * no matter what kind of addressing is used. */ struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; unsigned long p = vmcb->cs.base + guest_cpu_user_regs()->eip; ASSERT(v == current); if (!(vmcb->cs.attr.fields.l && hvm_long_mode_enabled(v))) return (u32)p; /* mask to 32 bits */ /* NB. Should mask to 16 bits if in real mode or 16-bit protected mode. */ return p;}#define MAKE_INSTR(nm, ...) static const u8 OPCODE_##nm[] = { __VA_ARGS__ }/* * Here's how it works: * First byte: Length. * Following bytes: Opcode bytes. * Special case: Last byte, if zero, doesn't need to match. */MAKE_INSTR(INVD, 2, 0x0f, 0x08);MAKE_INSTR(WBINVD, 2, 0x0f, 0x09);MAKE_INSTR(CPUID, 2, 0x0f, 0xa2);MAKE_INSTR(RDMSR, 2, 0x0f, 0x32);MAKE_INSTR(WRMSR, 2, 0x0f, 0x30);MAKE_INSTR(CLI, 1, 0xfa);MAKE_INSTR(STI, 1, 0xfb);MAKE_INSTR(RDPMC, 2, 0x0f, 0x33);MAKE_INSTR(CLGI, 3, 0x0f, 0x01, 0xdd);MAKE_INSTR(STGI, 3, 0x0f, 0x01, 0xdc);MAKE_INSTR(VMRUN, 3, 0x0f, 0x01, 0xd8);MAKE_INSTR(VMLOAD, 3, 0x0f, 0x01, 0xda);MAKE_INSTR(VMSAVE, 3, 0x0f, 0x01, 0xdb);MAKE_INSTR(VMCALL, 3, 0x0f, 0x01, 0xd9);MAKE_INSTR(PAUSE, 2, 0xf3, 0x90);MAKE_INSTR(SKINIT, 3, 0x0f, 0x01, 0xde);MAKE_INSTR(MOV2CR, 3, 0x0f, 0x22, 0x00);MAKE_INSTR(MOVCR2, 3, 0x0f, 0x20, 0x00);MAKE_INSTR(MOV2DR, 3, 0x0f, 0x23, 0x00);MAKE_INSTR(MOVDR2, 3, 0x0f, 0x21, 0x00);MAKE_INSTR(PUSHF, 1, 0x9c);MAKE_INSTR(POPF, 1, 0x9d);MAKE_INSTR(RSM, 2, 0x0f, 0xaa);MAKE_INSTR(INVLPG, 3, 0x0f, 0x01, 0x00);MAKE_INSTR(INVLPGA,3, 0x0f, 0x01, 0xdf);MAKE_INSTR(HLT, 1, 0xf4);MAKE_INSTR(CLTS, 2, 0x0f, 0x06);MAKE_INSTR(LMSW, 3, 0x0f, 0x01, 0x00);MAKE_INSTR(SMSW, 3, 0x0f, 0x01, 0x00);MAKE_INSTR(INT3, 1, 0xcc);static const u8 *opc_bytes[INSTR_MAX_COUNT] = { [INSTR_INVD] = OPCODE_INVD, [INSTR_WBINVD] = OPCODE_WBINVD, [INSTR_CPUID] = OPCODE_CPUID, [INSTR_RDMSR] = OPCODE_RDMSR, [INSTR_WRMSR] = OPCODE_WRMSR, [INSTR_CLI] = OPCODE_CLI, [INSTR_STI] = OPCODE_STI, [INSTR_RDPMC] = OPCODE_RDPMC, [INSTR_CLGI] = OPCODE_CLGI, [INSTR_STGI] = OPCODE_STGI, [INSTR_VMRUN] = OPCODE_VMRUN, [INSTR_VMLOAD] = OPCODE_VMLOAD, [INSTR_VMSAVE] = OPCODE_VMSAVE, [INSTR_VMCALL] = OPCODE_VMCALL, [INSTR_PAUSE] = OPCODE_PAUSE, [INSTR_SKINIT] = OPCODE_SKINIT, [INSTR_MOV2CR] = OPCODE_MOV2CR, [INSTR_MOVCR2] = OPCODE_MOVCR2, [INSTR_MOV2DR] = OPCODE_MOV2DR, [INSTR_MOVDR2] = OPCODE_MOVDR2, [INSTR_PUSHF] = OPCODE_PUSHF, [INSTR_POPF] = OPCODE_POPF, [INSTR_RSM] = OPCODE_RSM, [INSTR_INVLPG] = OPCODE_INVLPG, [INSTR_INVLPGA]= OPCODE_INVLPGA, [INSTR_CLTS] = OPCODE_CLTS, [INSTR_HLT] = OPCODE_HLT, [INSTR_LMSW] = OPCODE_LMSW, [INSTR_SMSW] = OPCODE_SMSW, [INSTR_INT3] = OPCODE_INT3};/* * Intel has a vmcs entry to give the instruction length. AMD doesn't. So we * have to do a little bit of work to find out... * * The caller may supply a buffer of at least MAX_INST_LEN bytes, which * the instruction will be read into. */int __get_instruction_length_from_list(struct vcpu *v, enum instruction_index *list, unsigned int list_count, u8 *guest_eip_buf, enum instruction_index *match){ struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; unsigned int inst_len = 0; unsigned int i; unsigned int j; int found = 0; enum instruction_index instr = 0; unsigned long fetch_addr; int fetch_len; u8 *buf; const u8 *opcode = NULL; u8 temp_buffer[MAX_INST_LEN]; /* Use the stack if the caller didn't give us a buffer */ buf = ( guest_eip_buf ) ? guest_eip_buf : temp_buffer;#define FETCH(_buf, _addr, _len) do \ { \ switch ( hvm_fetch_from_guest_virt((_buf), (_addr), (_len)) ) \ { \ case HVMCOPY_okay: \ break; \ case HVMCOPY_bad_gva_to_gfn: \ /* OK just to give up; we'll have injected #PF already */ \ return 0; \ case HVMCOPY_bad_gfn_to_mfn: \ /* Not OK: fetches from non-RAM pages are not supportable. */ \ gdprintk(XENLOG_ERR, "Bad instruction fetch at %#lx (%#lx)\n", \ (unsigned long) guest_cpu_user_regs()->eip, fetch_addr); \ hvm_inject_exception(TRAP_gp_fault, 0, 0); \ return 0; \ } \ } while (0) /* Fetch up to the next page break; we'll fetch from the next page * later if we have to. */ fetch_addr = svm_rip2pointer(v); fetch_len = PAGE_SIZE - (fetch_addr & ~PAGE_MASK) ; if ( fetch_len > MAX_INST_LEN ) fetch_len = MAX_INST_LEN; FETCH(buf, fetch_addr, fetch_len); for (j = 0; j < list_count; j++) { instr = list[j]; opcode = opc_bytes[instr]; ASSERT(opcode); while (inst_len < MAX_INST_LEN && is_prefix(buf[inst_len]) && !is_prefix(opcode[1])) { inst_len++; if ( inst_len >= fetch_len ) { FETCH(buf + fetch_len, fetch_addr + fetch_len, MAX_INST_LEN - fetch_len); fetch_len = MAX_INST_LEN; } } ASSERT(opcode[0] <= 15); /* Make sure the table is correct. */ found = 1; for (i = 0; i < opcode[0]; i++) { /* If the last byte is zero, we just accept it without checking */ if (i == opcode[0]-1 && opcode[i+1] == 0) break; if ( inst_len + i >= fetch_len ) { FETCH(buf + fetch_len, fetch_addr + fetch_len, MAX_INST_LEN - fetch_len); fetch_len = MAX_INST_LEN; } if (buf[inst_len+i] != opcode[i+1]) { found = 0; break; } } if (found) break; } /* It's a match */ if (found) { inst_len += opcode[0]; ASSERT(inst_len <= MAX_INST_LEN); if (match) *match = instr; return inst_len; } printk("%s: Mismatch between expected and actual instruction bytes: " "eip = %lx\n", __func__, (unsigned long)vmcb->rip); hvm_inject_exception(TRAP_gp_fault, 0, 0); return 0;#undef FETCH}/* * Local variables: * mode: C * c-set-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -