📄 io.c
字号:
/* * io.c: Handling I/O and interrupts. * * Copyright (c) 2004, Intel Corporation. * Copyright (c) 2005, International Business Machines 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/mm.h>#include <xen/lib.h>#include <xen/errno.h>#include <xen/trace.h>#include <xen/event.h>#include <xen/hypercall.h>#include <asm/current.h>#include <asm/cpufeature.h>#include <asm/processor.h>#include <asm/msr.h>#include <asm/apic.h>#include <asm/paging.h>#include <asm/shadow.h>#include <asm/p2m.h>#include <asm/hvm/hvm.h>#include <asm/hvm/support.h>#include <asm/hvm/vpt.h>#include <asm/hvm/vpic.h>#include <asm/hvm/vlapic.h>#include <asm/hvm/trace.h>#include <public/sched.h>#include <xen/iocap.h>#include <public/hvm/ioreq.h>#if defined (__i386__)static void set_reg_value (int size, int index, int seg, struct cpu_user_regs *regs, long value){ switch (size) { case BYTE: switch (index) { case 0: regs->eax &= 0xFFFFFF00; regs->eax |= (value & 0xFF); break; case 1: regs->ecx &= 0xFFFFFF00; regs->ecx |= (value & 0xFF); break; case 2: regs->edx &= 0xFFFFFF00; regs->edx |= (value & 0xFF); break; case 3: regs->ebx &= 0xFFFFFF00; regs->ebx |= (value & 0xFF); break; case 4: regs->eax &= 0xFFFF00FF; regs->eax |= ((value & 0xFF) << 8); break; case 5: regs->ecx &= 0xFFFF00FF; regs->ecx |= ((value & 0xFF) << 8); break; case 6: regs->edx &= 0xFFFF00FF; regs->edx |= ((value & 0xFF) << 8); break; case 7: regs->ebx &= 0xFFFF00FF; regs->ebx |= ((value & 0xFF) << 8); break; default: goto crash; } break; case WORD: switch (index) { case 0: regs->eax &= 0xFFFF0000; regs->eax |= (value & 0xFFFF); break; case 1: regs->ecx &= 0xFFFF0000; regs->ecx |= (value & 0xFFFF); break; case 2: regs->edx &= 0xFFFF0000; regs->edx |= (value & 0xFFFF); break; case 3: regs->ebx &= 0xFFFF0000; regs->ebx |= (value & 0xFFFF); break; case 4: regs->esp &= 0xFFFF0000; regs->esp |= (value & 0xFFFF); break; case 5: regs->ebp &= 0xFFFF0000; regs->ebp |= (value & 0xFFFF); break; case 6: regs->esi &= 0xFFFF0000; regs->esi |= (value & 0xFFFF); break; case 7: regs->edi &= 0xFFFF0000; regs->edi |= (value & 0xFFFF); break; default: goto crash; } break; case LONG: switch (index) { case 0: regs->eax = value; break; case 1: regs->ecx = value; break; case 2: regs->edx = value; break; case 3: regs->ebx = value; break; case 4: regs->esp = value; break; case 5: regs->ebp = value; break; case 6: regs->esi = value; break; case 7: regs->edi = value; break; default: goto crash; } break; default: crash: gdprintk(XENLOG_ERR, "size:%x, index:%x are invalid!\n", size, index); domain_crash_synchronous(); }}#elsestatic inline void __set_reg_value(unsigned long *reg, int size, long value){ switch (size) { case BYTE_64: *reg &= ~0xFF; *reg |= (value & 0xFF); break; case WORD: *reg &= ~0xFFFF; *reg |= (value & 0xFFFF); break; case LONG: *reg &= ~0xFFFFFFFF; *reg |= (value & 0xFFFFFFFF); break; case QUAD: *reg = value; break; default: gdprintk(XENLOG_ERR, "size:%x is invalid\n", size); domain_crash_synchronous(); }}static void set_reg_value (int size, int index, int seg, struct cpu_user_regs *regs, long value){ if (size == BYTE) { switch (index) { case 0: regs->rax &= ~0xFF; regs->rax |= (value & 0xFF); break; case 1: regs->rcx &= ~0xFF; regs->rcx |= (value & 0xFF); break; case 2: regs->rdx &= ~0xFF; regs->rdx |= (value & 0xFF); break; case 3: regs->rbx &= ~0xFF; regs->rbx |= (value & 0xFF); break; case 4: regs->rax &= 0xFFFFFFFFFFFF00FF; regs->rax |= ((value & 0xFF) << 8); break; case 5: regs->rcx &= 0xFFFFFFFFFFFF00FF; regs->rcx |= ((value & 0xFF) << 8); break; case 6: regs->rdx &= 0xFFFFFFFFFFFF00FF; regs->rdx |= ((value & 0xFF) << 8); break; case 7: regs->rbx &= 0xFFFFFFFFFFFF00FF; regs->rbx |= ((value & 0xFF) << 8); break; default: gdprintk(XENLOG_ERR, "size:%x, index:%x are invalid!\n", size, index); domain_crash_synchronous(); break; } return; } switch (index) { case 0: __set_reg_value(®s->rax, size, value); break; case 1: __set_reg_value(®s->rcx, size, value); break; case 2: __set_reg_value(®s->rdx, size, value); break; case 3: __set_reg_value(®s->rbx, size, value); break; case 4: __set_reg_value(®s->rsp, size, value); break; case 5: __set_reg_value(®s->rbp, size, value); break; case 6: __set_reg_value(®s->rsi, size, value); break; case 7: __set_reg_value(®s->rdi, size, value); break; case 8: __set_reg_value(®s->r8, size, value); break; case 9: __set_reg_value(®s->r9, size, value); break; case 10: __set_reg_value(®s->r10, size, value); break; case 11: __set_reg_value(®s->r11, size, value); break; case 12: __set_reg_value(®s->r12, size, value); break; case 13: __set_reg_value(®s->r13, size, value); break; case 14: __set_reg_value(®s->r14, size, value); break; case 15: __set_reg_value(®s->r15, size, value); break; default: gdprintk(XENLOG_ERR, "Invalid index\n"); domain_crash_synchronous(); } return;}#endiflong get_reg_value(int size, int index, int seg, struct cpu_user_regs *regs);static inline void set_eflags_CF(int size, unsigned int instr, unsigned long result, unsigned long src, unsigned long dst, struct cpu_user_regs *regs){ unsigned long mask; if ( size == BYTE_64 ) size = BYTE; ASSERT((size <= sizeof(mask)) && (size > 0)); mask = ~0UL >> (8 * (sizeof(mask) - size)); if ( instr == INSTR_ADD ) { /* CF=1 <==> result is less than the augend and addend) */ if ( (result & mask) < (dst & mask) ) { ASSERT((result & mask) < (src & mask)); regs->eflags |= X86_EFLAGS_CF; } } else { ASSERT( instr == INSTR_CMP || instr == INSTR_SUB ); if ( (src & mask) > (dst & mask) ) regs->eflags |= X86_EFLAGS_CF; }}static inline void set_eflags_OF(int size, unsigned int instr, unsigned long result, unsigned long src, unsigned long dst, struct cpu_user_regs *regs){ unsigned long mask; if ( size == BYTE_64 ) size = BYTE; ASSERT((size <= sizeof(mask)) && (size > 0)); mask = 1UL << ((8*size) - 1); if ( instr == INSTR_ADD ) { if ((src ^ result) & (dst ^ result) & mask); regs->eflags |= X86_EFLAGS_OF; } else { ASSERT(instr == INSTR_CMP || instr == INSTR_SUB); if ((dst ^ src) & (dst ^ result) & mask) regs->eflags |= X86_EFLAGS_OF; }}static inline void set_eflags_AF(int size, unsigned long result, unsigned long src, unsigned long dst, struct cpu_user_regs *regs){ if ((result ^ src ^ dst) & 0x10) regs->eflags |= X86_EFLAGS_AF;}static inline void set_eflags_ZF(int size, unsigned long result, struct cpu_user_regs *regs){ unsigned long mask; if ( size == BYTE_64 ) size = BYTE; ASSERT((size <= sizeof(mask)) && (size > 0)); mask = ~0UL >> (8 * (sizeof(mask) - size)); if ((result & mask) == 0) regs->eflags |= X86_EFLAGS_ZF;}static inline void set_eflags_SF(int size, unsigned long result, struct cpu_user_regs *regs){ unsigned long mask; if ( size == BYTE_64 ) size = BYTE; ASSERT((size <= sizeof(mask)) && (size > 0)); mask = 1UL << ((8*size) - 1); if (result & mask) regs->eflags |= X86_EFLAGS_SF;}static char parity_table[256] = { 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1};static inline void set_eflags_PF(int size, unsigned long result, struct cpu_user_regs *regs){ if (parity_table[result & 0xFF]) regs->eflags |= X86_EFLAGS_PF;}static void hvm_pio_assist(struct cpu_user_regs *regs, ioreq_t *p, struct hvm_io_op *pio_opp){ if ( p->data_is_ptr || (pio_opp->flags & OVERLAP) ) { int sign = p->df ? -1 : 1; if ( pio_opp->flags & REPZ ) regs->ecx -= p->count; if ( p->dir == IOREQ_READ ) { if ( pio_opp->flags & OVERLAP ) { unsigned long addr = pio_opp->addr; if ( hvm_paging_enabled(current) ) { int rv = hvm_copy_to_guest_virt(addr, &p->data, p->size); if ( rv == HVMCOPY_bad_gva_to_gfn ) return; /* exception already injected */ } else (void)hvm_copy_to_guest_phys(addr, &p->data, p->size); } regs->edi += sign * p->count * p->size; } else /* p->dir == IOREQ_WRITE */ { ASSERT(p->dir == IOREQ_WRITE); regs->esi += sign * p->count * p->size; } } else if ( p->dir == IOREQ_READ ) { unsigned long old_eax = regs->eax; switch ( p->size ) { case 1: regs->eax = (old_eax & ~0xff) | (p->data & 0xff); break; case 2: regs->eax = (old_eax & ~0xffff) | (p->data & 0xffff); break; case 4: regs->eax = (p->data & 0xffffffff); break; default: printk("Error: %s unknown port size\n", __FUNCTION__); domain_crash_synchronous(); } HVMTRACE_1D(IO_ASSIST, current, p->data); }}static void hvm_mmio_assist(struct cpu_user_regs *regs, ioreq_t *p, struct hvm_io_op *mmio_opp){ int sign = p->df ? -1 : 1; int size = -1, index = -1; unsigned long value = 0, result = 0; unsigned long src, dst; src = mmio_opp->operand[0]; dst = mmio_opp->operand[1]; size = operand_size(src); HVMTRACE_1D(MMIO_ASSIST, current, p->data); switch (mmio_opp->instr) { case INSTR_MOV:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -