[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH for 4.9 4/6] x86/svm: Introduce svm_emul_swint_injection()
Software events require emulation in some cases on AMD hardware. Introduce svm_emul_swint_injection() to perform this emulation if necessary in svm_inject_event(), which will cope with any sources of event, rather than just those coming from x86_emulate(). This logic mirrors inject_swint() in the x86 instruction emulator. Signed-off-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx> --- CC: Jan Beulich <JBeulich@xxxxxxxx> CC: Boris Ostrovsky <boris.ostrovsky@xxxxxxxxxx> CC: Suravee Suthikulpanit <suravee.suthikulpanit@xxxxxxx> CC: Julien Grall <julien.grall@xxxxxxx> --- xen/arch/x86/hvm/svm/svm.c | 136 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c index 4d7e49f..6d77c7e 100644 --- a/xen/arch/x86/hvm/svm/svm.c +++ b/xen/arch/x86/hvm/svm/svm.c @@ -1183,6 +1183,124 @@ static void svm_vcpu_destroy(struct vcpu *v) passive_domain_destroy(v); } +/* + * Emulate enough of interrupt injection to cover the DPL check (omitted by + * hardware), and to work out whether it is safe to move %rip fowards for + * architectural trap vs fault semantics in the exception frame (which + * hardware won't cope with). + * + * The event parameter will be modified to a fault if necessary. + */ +static void svm_emul_swint_injection(struct x86_event *event) +{ + struct vcpu *curr = current; + struct vmcb_struct *vmcb = curr->arch.hvm_svm.vmcb; + struct cpu_user_regs *regs = guest_cpu_user_regs(); + + unsigned int trap = event->vector, type = event->type; + unsigned int fault = TRAP_gp_fault, ec = 0; + + if ( vmcb->_cr0 & X86_CR0_PE ) /* TODO: support real-mode injection? */ + { + pagefault_info_t pf; + struct segment_register idtr; + unsigned int idte_size, idte_offset; + unsigned long idte_linear_addr; + struct { uint32_t a, b, c, d; } idte = {}; + bool lm = vmcb->_efer & EFER_LMA; + enum hvm_segmentation_mode seg_mode = + lm ? hvm_seg_mode_long : hvm_seg_mode_prot; + int rc; + + idte_size = lm ? 16 : 8; + idte_offset = trap * idte_size; + + /* ICEBP sets the External Event bit despite being an instruction. */ + ec = (trap << 3) | X86_XEC_IDT | + (type == X86_EVENTTYPE_PRI_SW_EXCEPTION ? X86_XEC_EXT : 0); + + /* + * TODO: This does not cover the v8086 mode with CR4.VME case + * correctly, but falls on the safe side from the point of view of a + * 32bit OS. Someone with many TUITs can see about reading the TSS + * Software Interrupt Redirection bitmap. + */ + if ( (regs->eflags & X86_EFLAGS_VM) && + MASK_EXTR(regs->eflags, X86_EFLAGS_IOPL) != 3 ) + goto raise_exception; + + /* + * Read all 8/16 bytes so the idtr limit check is applied properly to + * this entry, even though don't look all the words read. + */ + hvm_get_segment_register(curr, x86_seg_idtr, &idtr); + if ( !hvm_virtual_to_linear_addr(x86_seg_idtr, &idtr, idte_offset, + idte_size, hvm_access_read, + seg_mode, &idte_linear_addr) ) + goto raise_exception; + + rc = hvm_copy_from_guest_linear(&idte, idte_linear_addr, idte_size, + PFEC_implicit, &pf); + if ( rc ) + { + if ( rc == HVMCOPY_bad_gva_to_gfn ) + { + fault = TRAP_page_fault; + ec = pf.ec; + event->cr2 = pf.linear; + } + + goto raise_exception; + } + + /* This must be an interrupt, trap, or task gate. */ + switch ( (idte.b >> 8) & 0x1f ) + { + case SYS_DESC_irq_gate: + case SYS_DESC_trap_gate: + break; + case SYS_DESC_irq_gate16: + case SYS_DESC_trap_gate16: + case SYS_DESC_task_gate: + if ( !lm ) + break; + /* fall through */ + default: + goto raise_exception; + } + + /* The 64-bit high half's type must be zero. */ + if ( idte.d & 0x1f00 ) + goto raise_exception; + + /* ICEBP counts as a hardware event, and bypasses the dpl check. */ + if ( type != X86_EVENTTYPE_PRI_SW_EXCEPTION && + vmcb->_cpl > ((idte.b >> 13) & 3) ) + goto raise_exception; + + /* Is this entry present? */ + if ( !(idte.b & (1u << 15)) ) + { + fault = TRAP_no_segment; + goto raise_exception; + } + + /* + * Any further fault during injection will cause a double fault. It + * is fine to leave this up to hardware, and software won't be in a + * position to care about the architectural correctness of %rip in the + * exception frame. + */ + return; + } + + raise_exception: + event->vector = fault; + event->type = X86_EVENTTYPE_HW_EXCEPTION; + event->insn_len = 0; + event->error_code = ec; +} + static void svm_inject_event(const struct x86_event *event) { struct vcpu *curr = current; @@ -1191,6 +1309,24 @@ static void svm_inject_event(const struct x86_event *event) struct x86_event _event = *event; struct cpu_user_regs *regs = guest_cpu_user_regs(); + /* + * For hardware lacking NRips support, and always for ICEBP instructions, + * the processor requires extra help to deliver software events. + * + * Xen must emulate enough of the event injection to be sure that a + * further fault shouldn't occur during delivery. This covers the fact + * that hardware doesn't perform DPL checking on injection. + * + * Also, it accounts for proper positioning of %rip for an event with trap + * semantics (where %rip should point after the instruction) which suffers + * a fault during injection (at which point %rip should point at the + * instruction). + */ + if ( event->type == X86_EVENTTYPE_PRI_SW_EXCEPTION || + (!cpu_has_svm_nrips && (event->type == X86_EVENTTYPE_SW_INTERRUPT || + event->type == X86_EVENTTYPE_SW_EXCEPTION)) ) + svm_emul_swint_injection(&_event); + switch ( _event.vector ) { case TRAP_debug: -- 2.1.4 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx https://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |