📄 svm.c
字号:
*addr - (*count - 1) * size <= seg->limit) *count = (*addr - seg->limit - 1) / size + 1; } } ASSERT(*count); } *addr += seg->base; }#ifdef __x86_64__ else { if (seg == &vmcb->fs || seg == &vmcb->gs) *addr += seg->base; if (!is_canonical_address(*addr) || !is_canonical_address(*addr + size - 1)) { svm_inject_exception(TRAP_gp_fault, 0, 0); return 0; } if (*count > (1UL << 48) / size) *count = (1UL << 48) / size; if (!(regs->eflags & EF_DF)) { if (*addr + *count * size - 1 < *addr || !is_canonical_address(*addr + *count * size - 1)) *count = (*addr & ~((1UL << 48) - 1)) / size; } else { if ((*count - 1) * size > *addr || !is_canonical_address(*addr + (*count - 1) * size)) *count = (*addr & ~((1UL << 48) - 1)) / size + 1; } ASSERT(*count); }#endif return 1;}static void svm_io_instruction(struct vcpu *v){ struct cpu_user_regs *regs; struct hvm_io_op *pio_opp; unsigned int port; unsigned int size, dir, df; ioio_info_t info; struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; pio_opp = ¤t->arch.hvm_vcpu.io_op; pio_opp->instr = INSTR_PIO; pio_opp->flags = 0; regs = &pio_opp->io_context; /* Copy current guest state into io instruction state structure. */ memcpy(regs, guest_cpu_user_regs(), HVM_CONTEXT_STACK_BYTES); info.bytes = vmcb->exitinfo1; port = info.fields.port; /* port used to be addr */ dir = info.fields.type; /* direction */ df = regs->eflags & X86_EFLAGS_DF ? 1 : 0; if (info.fields.sz32) size = 4; else if (info.fields.sz16) size = 2; else size = 1; if (dir==IOREQ_READ) HVMTRACE_2D(IO_READ, v, port, size); else HVMTRACE_3D(IO_WRITE, v, port, size, regs->eax); HVM_DBG_LOG(DBG_LEVEL_IO, "svm_io_instruction: port 0x%x eip=%x:%"PRIx64", " "exit_qualification = %"PRIx64, port, vmcb->cs.sel, (uint64_t)regs->eip, info.bytes); /* string instruction */ if (info.fields.str) { unsigned long addr, count; paddr_t paddr; unsigned long gfn; uint32_t pfec; int sign = regs->eflags & X86_EFLAGS_DF ? -1 : 1; if (!svm_get_io_address(v, regs, size, info, &count, &addr)) { /* We failed to get a valid address, so don't do the IO operation - * it would just get worse if we do! Hopefully the guest is handing * gp-faults... */ return; } /* "rep" prefix */ if (info.fields.rep) { pio_opp->flags |= REPZ; } /* Translate the address to a physical address */ pfec = PFEC_page_present; if ( dir == IOREQ_READ ) /* Read from PIO --> write to RAM */ pfec |= PFEC_write_access; if ( vmcb->cpl == 3 ) pfec |= PFEC_user_mode; gfn = paging_gva_to_gfn(v, addr, &pfec); if ( gfn == INVALID_GFN ) { /* The guest does not have the RAM address mapped. * Need to send in a page fault */ svm_inject_exception(TRAP_page_fault, pfec, addr); return; } paddr = (paddr_t)gfn << PAGE_SHIFT | (addr & ~PAGE_MASK); /* * Handle string pio instructions that cross pages or that * are unaligned. See the comments in hvm_platform.c/handle_mmio() */ if ((addr & PAGE_MASK) != ((addr + size - 1) & PAGE_MASK)) { unsigned long value = 0; pio_opp->flags |= OVERLAP; pio_opp->addr = addr; if (dir == IOREQ_WRITE) /* OUTS */ { if ( hvm_paging_enabled(current) ) { int rv = hvm_copy_from_guest_virt(&value, addr, size); if ( rv == HVMCOPY_bad_gva_to_gfn ) return; /* exception already injected */ } else (void)hvm_copy_from_guest_phys(&value, addr, size); } else /* dir != IOREQ_WRITE */ /* Remember where to write the result, as a *VA*. * Must be a VA so we can handle the page overlap * correctly in hvm_pio_assist() */ pio_opp->addr = addr; if (count == 1) regs->eip = vmcb->exitinfo2; send_pio_req(port, 1, size, value, dir, df, 0); } else { unsigned long last_addr = sign > 0 ? addr + count * size - 1 : addr - (count - 1) * size; if ((addr & PAGE_MASK) != (last_addr & PAGE_MASK)) { if (sign > 0) count = (PAGE_SIZE - (addr & ~PAGE_MASK)) / size; else count = (addr & ~PAGE_MASK) / size + 1; } else regs->eip = vmcb->exitinfo2; send_pio_req(port, count, size, paddr, dir, df, 1); } } else { /* * On SVM, the RIP of the intruction following the IN/OUT is saved in * ExitInfo2 */ regs->eip = vmcb->exitinfo2; if (port == 0xe9 && dir == IOREQ_WRITE && size == 1) hvm_print_line(v, regs->eax); /* guest debug output */ send_pio_req(port, 1, size, regs->eax, dir, df, 0); }}static void mov_from_cr(int cr, int gp, struct cpu_user_regs *regs){ unsigned long value = 0; struct vcpu *v = current; struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; switch ( cr ) { case 0: value = v->arch.hvm_vcpu.guest_cr[0]; break; case 3: value = (unsigned long)v->arch.hvm_vcpu.guest_cr[3]; break; case 4: value = (unsigned long)v->arch.hvm_vcpu.guest_cr[4]; break; default: gdprintk(XENLOG_ERR, "invalid cr: %d\n", cr); domain_crash(v->domain); return; } HVMTRACE_2D(CR_READ, v, cr, value); set_reg(gp, value, regs, vmcb); HVM_DBG_LOG(DBG_LEVEL_VMMU, "mov_from_cr: CR%d, value = %lx", cr, value);}static int mov_to_cr(int gpreg, int cr, struct cpu_user_regs *regs){ unsigned long value; struct vcpu *v = current; struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; value = get_reg(gpreg, regs, vmcb); HVMTRACE_2D(CR_WRITE, v, cr, value); HVM_DBG_LOG(DBG_LEVEL_1, "mov_to_cr: CR%d, value = %lx, current = %p", cr, value, v); switch ( cr ) { case 0: return hvm_set_cr0(value); case 3: return hvm_set_cr3(value); case 4: return hvm_set_cr4(value); default: gdprintk(XENLOG_ERR, "invalid cr: %d\n", cr); domain_crash(v->domain); return 0; } return 1;}static void svm_cr_access( struct vcpu *v, unsigned int cr, unsigned int type, struct cpu_user_regs *regs){ struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; int inst_len = 0; int index,addr_size,i; unsigned int gpreg,offset; unsigned long value,addr; u8 buffer[MAX_INST_LEN]; u8 prefix = 0; u8 modrm; enum x86_segment seg; int result = 1; enum instruction_index list_a[] = {INSTR_MOV2CR, INSTR_CLTS, INSTR_LMSW}; enum instruction_index list_b[] = {INSTR_MOVCR2, INSTR_SMSW}; enum instruction_index match; if ( type == TYPE_MOV_TO_CR ) { inst_len = __get_instruction_length_from_list( v, list_a, ARRAY_SIZE(list_a), buffer, &match); } else /* type == TYPE_MOV_FROM_CR */ { inst_len = __get_instruction_length_from_list( v, list_b, ARRAY_SIZE(list_b), buffer, &match); } if ( inst_len == 0 ) return; /* get index to first actual instruction byte - as we will need to know where the prefix lives later on */ index = skip_prefix_bytes(buffer, sizeof(buffer)); /* Check for REX prefix - it's ALWAYS the last byte of any prefix bytes */ if (index > 0 && (buffer[index-1] & 0xF0) == 0x40) prefix = buffer[index-1]; HVM_DBG_LOG(DBG_LEVEL_1, "eip = %lx", (unsigned long)regs->eip); switch ( match ) { case INSTR_MOV2CR: gpreg = decode_src_reg(prefix, buffer[index+2]); result = mov_to_cr(gpreg, cr, regs); break; case INSTR_MOVCR2: gpreg = decode_src_reg(prefix, buffer[index+2]); mov_from_cr(cr, gpreg, regs); break; case INSTR_CLTS: v->arch.hvm_vcpu.guest_cr[0] &= ~X86_CR0_TS; svm_update_guest_cr(v, 0); HVMTRACE_0D(CLTS, current); break; case INSTR_LMSW: gpreg = decode_src_reg(prefix, buffer[index+2]); value = get_reg(gpreg, regs, vmcb) & 0xF; value = (v->arch.hvm_vcpu.guest_cr[0] & ~0xF) | value; result = hvm_set_cr0(value); HVMTRACE_1D(LMSW, current, value); break; case INSTR_SMSW: value = v->arch.hvm_vcpu.guest_cr[0] & 0xFFFF; modrm = buffer[index+2]; addr_size = svm_guest_x86_mode(v); if ( addr_size < 2 ) addr_size = 2; if ( likely((modrm & 0xC0) >> 6 == 3) ) { gpreg = decode_src_reg(prefix, modrm); set_reg(gpreg, value, regs, vmcb); } /* * For now, only implement decode of the offset mode, since that's the * only mode observed in a real-world OS. This code is also making the * assumption that we'll never hit this code in long mode. */ else if ( (modrm == 0x26) || (modrm == 0x25) ) { seg = x86_seg_ds; i = index; /* Segment or address size overrides? */ while ( i-- ) { switch ( buffer[i] ) { case 0x26: seg = x86_seg_es; break; case 0x2e: seg = x86_seg_cs; break; case 0x36: seg = x86_seg_ss; break; case 0x64: seg = x86_seg_fs; break; case 0x65: seg = x86_seg_gs; break; case 0x67: addr_size ^= 6; break; } } /* Bail unless this really is a seg_base + offset case */ if ( ((modrm == 0x26) && (addr_size == 4)) || ((modrm == 0x25) && (addr_size == 2)) ) { gdprintk(XENLOG_ERR, "SMSW emulation at guest address: " "%lx failed due to unhandled addressing mode." "ModRM byte was: %x \n", svm_rip2pointer(v), modrm); domain_crash(v->domain); } inst_len += addr_size; offset = *(( unsigned int *) ( void *) &buffer[index + 3]); offset = ( addr_size == 4 ) ? offset : ( offset & 0xFFFF ); addr = hvm_get_segment_base(v, seg); addr += offset; result = (hvm_copy_to_guest_virt(addr, &value, 2) != HVMCOPY_bad_gva_to_gfn); } else { gdprintk(XENLOG_ERR, "SMSW emulation at guest address: %lx " "failed due to unhandled addressing mode!" "ModRM byte was: %x \n", svm_rip2pointer(v), modrm); domain_crash(v->domain); } break; default: BUG(); } if ( result ) __update_guest_eip(regs, inst_len);}static void svm_do_msr_access( struct vcpu *v, struct cpu_user_regs *regs){ struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; int inst_len; u64 msr_content=0; u32 ecx = regs->ecx, eax, edx; HVM_DBG_LOG(DBG_LEVEL_1, "ecx=%x, eax=%x, edx=%x, exitinfo = %lx", ecx, (u32)regs->eax, (u32)regs->edx, (unsigned long)vmcb->exitinfo1); /* is it a read? */ if (vmcb->exitinfo1 == 0) { switch (ecx) { case MSR_IA32_TSC: msr_content = hvm_get_guest_time(v); break; case MSR_IA32_APICBASE: msr_content = vcpu_vlapic(v)->hw.apic_base_msr; break; case MSR_EFER: msr_content = v->arch.hvm_vcpu.guest_efer; break; case MSR_IA32_MC4_MISC: /* Threshold register */ case MSR_F10_MC4_MISC1 ... MSR_F10_MC4_MISC3: /* * MCA/MCE: We report that the threshold register is unavailable * for OS use (locked by the BIOS). */ msr_content = 1ULL << 61; /* MC4_MISC.Locked */ break; case MSR_IA32_EBC_FREQUENCY_ID: /* * This Intel-only register may be accessed if this HVM guest * has been migrated from an Intel host. The value zero is not * particularly meaningful, but at least avoids the guest crashing! */ msr_content = 0; break; case MSR_K8_VM_HSAVE_PA: svm_inject_exception(TRAP_gp_fault, 0, 0); break; case MSR_IA32_MCG_CAP: case MSR_IA32_MCG_STATUS: case MSR_IA32_MC0_STATUS: case MSR_IA32_MC1_STATUS: case MSR_IA32_MC2_STATUS: case MSR_IA32_MC3_STATUS: case MSR_IA32_MC4_STATUS: case MSR_IA32_MC5_STATUS: /* No point in letting the guest see real MCEs */ msr_content = 0; break; case MSR_IA32_DEBUGCTLMSR:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -