📄 traps.c
字号:
__get_user(desc, pdesc) ) return 0; *sel = (desc.a >> 16) & 0x0000fffc; *off = (desc.a & 0x0000ffff) | (desc.b & 0xffff0000); *ar = desc.b & 0x0000ffff; /* * check_descriptor() clears the DPL field and stores the * guest requested DPL in the selector's RPL field. */ if ( *ar & _SEGMENT_DPL ) return 0; *ar |= (desc.a >> (16 - 13)) & _SEGMENT_DPL; if ( !is_pv_32bit_vcpu(v) ) { if ( (*ar & 0x1f00) != 0x0c00 || (gate_sel >= FIRST_RESERVED_GDT_BYTE - 8 && !(gate_sel & 4)) || __get_user(desc, pdesc + 1) || (desc.b & 0x1f00) ) return 0; *off |= (unsigned long)desc.a << 32; return 1; } switch ( *ar & 0x1f00 ) { case 0x0400: *off &= 0xffff; break; case 0x0c00: break; default: return 0; } return 1;}#endif/* Has the guest requested sufficient permission for this I/O access? */static inline int guest_io_okay( unsigned int port, unsigned int bytes, struct vcpu *v, struct cpu_user_regs *regs){#if defined(__x86_64__) /* If in user mode, switch to kernel mode just to read I/O bitmap. */ int user_mode = !(v->arch.flags & TF_kernel_mode);#define TOGGLE_MODE() if ( user_mode ) toggle_guest_mode(v)#elif defined(__i386__)#define TOGGLE_MODE() ((void)0)#endif if ( !vm86_mode(regs) && (v->arch.iopl >= (guest_kernel_mode(v, regs) ? 1 : 3)) ) return 1; if ( v->arch.iobmp_limit > (port + bytes) ) { union { uint8_t bytes[2]; uint16_t mask; } x; /* * Grab permission bytes from guest space. Inaccessible bytes are * read as 0xff (no access allowed). */ TOGGLE_MODE(); switch ( __copy_from_guest_offset(x.bytes, v->arch.iobmp, port>>3, 2) ) { default: x.bytes[0] = ~0; case 1: x.bytes[1] = ~0; case 0: break; } TOGGLE_MODE(); if ( (x.mask & (((1<<bytes)-1) << (port&7))) == 0 ) return 1; } return 0;}/* Has the administrator granted sufficient permission for this I/O access? */static inline int admin_io_okay( unsigned int port, unsigned int bytes, struct vcpu *v, struct cpu_user_regs *regs){ return ioports_access_permitted(v->domain, port, port + bytes - 1);}#define guest_inb_okay(_p, _d, _r) admin_io_okay(_p, 1, _d, _r)#define guest_inw_okay(_p, _d, _r) admin_io_okay(_p, 2, _d, _r)#define guest_inl_okay(_p, _d, _r) admin_io_okay(_p, 4, _d, _r)#define guest_outb_okay(_p, _d, _r) admin_io_okay(_p, 1, _d, _r)#define guest_outw_okay(_p, _d, _r) admin_io_okay(_p, 2, _d, _r)#define guest_outl_okay(_p, _d, _r) admin_io_okay(_p, 4, _d, _r)/* I/O emulation support. Helper routines for, and type of, the stack stub.*/void host_to_guest_gpr_switch(struct cpu_user_regs *) __attribute__((__regparm__(1)));unsigned long guest_to_host_gpr_switch(unsigned long) __attribute__((__regparm__(1)));void (*pv_post_outb_hook)(unsigned int port, u8 value);/* Instruction fetch with error handling. */#define insn_fetch(type, base, eip, limit) \({ unsigned long _rc, _ptr = (base) + (eip); \ type _x; \ if ( ad_default < 8 ) \ _ptr = (unsigned int)_ptr; \ if ( (limit) < sizeof(_x) - 1 || (eip) > (limit) - (sizeof(_x) - 1) ) \ goto fail; \ if ( (_rc = copy_from_user(&_x, (type *)_ptr, sizeof(_x))) != 0 ) \ { \ propagate_page_fault(_ptr + sizeof(_x) - _rc, 0); \ goto skip; \ } \ (eip) += sizeof(_x); _x; })#if defined(CONFIG_X86_32)# define read_sreg(regs, sr) ((regs)->sr)#elif defined(CONFIG_X86_64)# define read_sreg(regs, sr) read_segment_register(sr)#endifstatic int emulate_privileged_op(struct cpu_user_regs *regs){ struct vcpu *v = current; unsigned long *reg, eip = regs->eip, res; u8 opcode, modrm_reg = 0, modrm_rm = 0, rep_prefix = 0, lock = 0, rex = 0; enum { lm_seg_none, lm_seg_fs, lm_seg_gs } lm_ovr = lm_seg_none; unsigned int port, i, data_sel, ar, data, rc, bpmatch = 0; unsigned int op_bytes, op_default, ad_bytes, ad_default;#define rd_ad(reg) (ad_bytes >= sizeof(regs->reg) \ ? regs->reg \ : ad_bytes == 4 \ ? (u32)regs->reg \ : (u16)regs->reg)#define wr_ad(reg, val) (ad_bytes >= sizeof(regs->reg) \ ? regs->reg = (val) \ : ad_bytes == 4 \ ? (*(u32 *)®s->reg = (val)) \ : (*(u16 *)®s->reg = (val))) unsigned long code_base, code_limit; char io_emul_stub[32]; void (*io_emul)(struct cpu_user_regs *) __attribute__((__regparm__(1))); u32 l, h, eax, edx; if ( !read_descriptor(regs->cs, v, regs, &code_base, &code_limit, &ar, _SEGMENT_CODE|_SEGMENT_S|_SEGMENT_DPL|_SEGMENT_P) ) goto fail; op_default = op_bytes = (ar & (_SEGMENT_L|_SEGMENT_DB)) ? 4 : 2; ad_default = ad_bytes = (ar & _SEGMENT_L) ? 8 : op_default; if ( !(ar & _SEGMENT_S) || !(ar & _SEGMENT_P) || !(ar & _SEGMENT_CODE) ) goto fail; /* emulating only opcodes not allowing SS to be default */ data_sel = read_sreg(regs, ds); /* Legacy prefixes. */ for ( i = 0; i < 8; i++, rex == opcode || (rex = 0) ) { switch ( opcode = insn_fetch(u8, code_base, eip, code_limit) ) { case 0x66: /* operand-size override */ op_bytes = op_default ^ 6; /* switch between 2/4 bytes */ continue; case 0x67: /* address-size override */ ad_bytes = ad_default != 4 ? 4 : 2; /* switch to 2/4 bytes */ continue; case 0x2e: /* CS override */ data_sel = regs->cs; continue; case 0x3e: /* DS override */ data_sel = read_sreg(regs, ds); continue; case 0x26: /* ES override */ data_sel = read_sreg(regs, es); continue; case 0x64: /* FS override */ data_sel = read_sreg(regs, fs); lm_ovr = lm_seg_fs; continue; case 0x65: /* GS override */ data_sel = read_sreg(regs, gs); lm_ovr = lm_seg_gs; continue; case 0x36: /* SS override */ data_sel = regs->ss; continue; case 0xf0: /* LOCK */ lock = 1; continue; case 0xf2: /* REPNE/REPNZ */ case 0xf3: /* REP/REPE/REPZ */ rep_prefix = 1; continue; default: if ( (ar & _SEGMENT_L) && (opcode & 0xf0) == 0x40 ) { rex = opcode; continue; } break; } break; } /* REX prefix. */ if ( rex & 8 ) /* REX.W */ op_bytes = 4; /* emulating only opcodes not supporting 64-bit operands */ modrm_reg = (rex & 4) << 1; /* REX.R */ /* REX.X does not need to be decoded. */ modrm_rm = (rex & 1) << 3; /* REX.B */ if ( opcode == 0x0f ) goto twobyte_opcode; if ( lock ) goto fail; /* Input/Output String instructions. */ if ( (opcode >= 0x6c) && (opcode <= 0x6f) ) { unsigned long data_base, data_limit; if ( rep_prefix && (rd_ad(ecx) == 0) ) goto done; if ( !(opcode & 2) ) { data_sel = read_sreg(regs, es); lm_ovr = lm_seg_none; } if ( !(ar & _SEGMENT_L) ) { if ( !read_descriptor(data_sel, v, regs, &data_base, &data_limit, &ar, _SEGMENT_WR|_SEGMENT_S|_SEGMENT_DPL|_SEGMENT_P) ) goto fail; if ( !(ar & _SEGMENT_S) || !(ar & _SEGMENT_P) || (opcode & 2 ? (ar & _SEGMENT_CODE) && !(ar & _SEGMENT_WR) : (ar & _SEGMENT_CODE) || !(ar & _SEGMENT_WR)) ) goto fail; }#ifdef CONFIG_X86_64 else { if ( lm_ovr == lm_seg_none || data_sel < 4 ) { switch ( lm_ovr ) { case lm_seg_none: data_base = 0UL; break; case lm_seg_fs: data_base = v->arch.guest_context.fs_base; break; case lm_seg_gs: if ( guest_kernel_mode(v, regs) ) data_base = v->arch.guest_context.gs_base_kernel; else data_base = v->arch.guest_context.gs_base_user; break; } } else read_descriptor(data_sel, v, regs, &data_base, &data_limit, &ar, 0); data_limit = ~0UL; ar = _SEGMENT_WR|_SEGMENT_S|_SEGMENT_DPL|_SEGMENT_P; }#endif port = (u16)regs->edx; continue_io_string: switch ( opcode ) { case 0x6c: /* INSB */ op_bytes = 1; case 0x6d: /* INSW/INSL */ if ( data_limit < op_bytes - 1 || rd_ad(edi) > data_limit - (op_bytes - 1) || !guest_io_okay(port, op_bytes, v, regs) ) goto fail; switch ( op_bytes ) { case 1: /* emulate PIT counter 2 */ data = (u8)(guest_inb_okay(port, v, regs) ? inb(port) : ((port == 0x42 || port == 0x43 || port == 0x61) ? pv_pit_handler(port, 0, 0) : ~0)); break; case 2: data = (u16)(guest_inw_okay(port, v, regs) ? inw(port) : ~0); break; case 4: data = (u32)(guest_inl_okay(port, v, regs) ? inl(port) : ~0); break; } if ( (rc = copy_to_user((void *)data_base + rd_ad(edi), &data, op_bytes)) != 0 ) { propagate_page_fault(data_base + rd_ad(edi) + op_bytes - rc, PFEC_write_access); return EXCRET_fault_fixed; } wr_ad(edi, regs->edi + (int)((regs->eflags & EF_DF) ? -op_bytes : op_bytes)); break; case 0x6e: /* OUTSB */ op_bytes = 1; case 0x6f: /* OUTSW/OUTSL */ if ( data_limit < op_bytes - 1 || rd_ad(esi) > data_limit - (op_bytes - 1) || !guest_io_okay(port, op_bytes, v, regs) ) goto fail; rc = copy_from_user(&data, (void *)data_base + rd_ad(esi), op_bytes); if ( rc != 0 ) { propagate_page_fault(data_base + rd_ad(esi) + op_bytes - rc, 0); return EXCRET_fault_fixed; } switch ( op_bytes ) { case 1: if ( guest_outb_okay(port, v, regs) ) { outb((u8)data, port); if ( pv_post_outb_hook ) pv_post_outb_hook(port, data); } else if ( port == 0x42 || port == 0x43 || port == 0x61 ) pv_pit_handler(port, data, 1); break; case 2: if ( guest_outw_okay(port, v, regs) ) outw((u16)data, port); break; case 4: if ( guest_outl_okay(port, v, regs) ) outl((u32)data, port); break; } wr_ad(esi, regs->esi + (int)((regs->eflags & EF_DF) ? -op_bytes : op_bytes)); break; } bpmatch = check_guest_io_breakpoint(v, port, op_bytes); if ( rep_prefix && (wr_ad(ecx, regs->ecx - 1) != 0) ) { if ( !bpmatch && !hypercall_preempt_check() ) goto continue_io_string; eip = regs->eip; } goto done; } /* * Very likely to be an I/O instruction (IN/OUT). * Build an on-stack stub to execute the instruction with full guest * GPR context. This is needed for some systems which (ab)use IN/OUT * to communicate with BIOS code in system-management mode. */#ifdef __x86_64__ /* movq $host_to_guest_gpr_switch,%rcx */ io_emul_stub[0] = 0x48; io_emul_stub[1] = 0xb9; *(void **)&io_emul_stub[2] = (void *)host_to_guest_gpr_switch; /* callq *%rcx */ io_emul_stub[10] = 0xff; io_emul_stub[11] = 0xd1;#else /* call host_to_guest_gpr_switch */ io_emul_stub[0] = 0xe8; *(s32 *)&io_emul_stub[1] = (char *)host_to_guest_gpr_switch - &io_emul_stub[5]; /* 7 x nop */ memset(&io_emul_stub[5], 0x90, 7);#endif /* data16 or nop */ io_emul_stub[12] = (op_bytes != 2) ? 0x90 : 0x66; /* <io-access opcode> */ io_emul_stub[13] = opcode; /* imm8 or nop */ io_emul_stub[14] = 0x90; /* ret (jumps to guest_to_host_gpr_switch) */ io_emul_stub[15] = 0xc3; /* Handy function-typed pointer to the stub. */ io_emul = (void *)io_emul_stub; if ( ioemul_handle_quirk ) ioemul_handle_quirk(opcode, &io_emul_stub[12], regs); /* I/O Port and Interrupt Flag instructions. */ switch ( opcode ) { case 0xe4: /* IN imm8,%al */ op_bytes = 1; case 0xe5: /* IN imm8,%eax */ port = insn_fetch(u8, code_base, eip, code_limit); io_emul_stub[14] = port; /* imm8 */ exec_in: if ( !guest_io_okay(port, op_bytes, v, regs) ) goto fail; switch ( op_bytes ) { case 1: if ( guest_inb_okay(port, v, regs) ) io_emul(regs); else if ( port == 0x42 || port == 0x43 || port == 0x61 ) { regs->eax &= ~0xffUL; regs->eax |= pv_pit_handler(port, 0, 0); } else regs->eax |= (u8)~0; break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -