📄 traps.c
字号:
desc.a = (sel << 20) | 0xffff; desc.b = vm86attr | (sel >> 12); } *ar = desc.b & 0x00f0ff00; if ( !(desc.b & _SEGMENT_L) ) { *base = ((desc.a >> 16) + ((desc.b & 0xff) << 16) + (desc.b & 0xff000000)); *limit = (desc.a & 0xffff) | (desc.b & 0x000f0000); if ( desc.b & _SEGMENT_G ) *limit = ((*limit + 1) << 12) - 1;#ifndef NDEBUG if ( !vm86_mode(regs) && (sel > 3) ) { unsigned int a, l; unsigned char valid; asm volatile ( "larl %2,%0 ; setz %1" : "=r" (a), "=rm" (valid) : "rm" (sel)); BUG_ON(valid && ((a & 0x00f0ff00) != *ar)); asm volatile ( "lsll %2,%0 ; setz %1" : "=r" (l), "=rm" (valid) : "rm" (sel)); BUG_ON(valid && (l != *limit)); }#endif } else { *base = 0UL; *limit = ~0UL; } return 1;}#ifdef __x86_64__static int read_gate_descriptor(unsigned int gate_sel, const struct vcpu *v, unsigned int *sel, unsigned long *off, unsigned int *ar){ struct desc_struct desc; const struct desc_struct *pdesc; pdesc = (const struct desc_struct *) (!(gate_sel & 4) ? GDT_VIRT_START(v) : LDT_VIRT_START(v)) + (gate_sel >> 3); if ( (gate_sel < 4) || ((gate_sel >= FIRST_RESERVED_GDT_BYTE) && !(gate_sel & 4)) || __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 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 int admin_io_okay( unsigned int port, unsigned int bytes, struct vcpu *v, struct cpu_user_regs *regs){ /* * Port 0xcf8 (CONFIG_ADDRESS) is only visible for DWORD accesses. * We never permit direct access to that register. */ if ( (port == 0xcf8) && (bytes == 4) ) return 0; return ioports_access_permitted(v->domain, port, port + bytes - 1);}static uint32_t guest_io_read( unsigned int port, unsigned int bytes, struct vcpu *v, struct cpu_user_regs *regs){ extern uint32_t pci_conf_read( uint32_t cf8, uint8_t offset, uint8_t bytes); uint32_t data = 0; unsigned int shift = 0; if ( admin_io_okay(port, bytes, v, regs) ) { switch ( bytes ) { case 1: return inb(port); case 2: return inw(port); case 4: return inl(port); } } while ( bytes != 0 ) { unsigned int size = 1; uint32_t sub_data = 0xff; if ( (port == 0x42) || (port == 0x43) || (port == 0x61) ) { sub_data = pv_pit_handler(port, 0, 0); } else if ( (port == 0xcf8) && (bytes == 4) ) { size = 4; sub_data = v->domain->arch.pci_cf8; } else if ( ((port & 0xfffc) == 0xcfc) && IS_PRIV(v->domain) ) { size = min(bytes, 4 - (port & 3)); if ( size == 3 ) size = 2; sub_data = pci_conf_read(v->domain->arch.pci_cf8, port & 3, size); } if ( size == 4 ) return sub_data; data |= (sub_data & ((1u << (size * 8)) - 1)) << shift; shift += size * 8; port += size; bytes -= size; } return data;}static void guest_io_write( unsigned int port, unsigned int bytes, uint32_t data, struct vcpu *v, struct cpu_user_regs *regs){ extern void pci_conf_write( uint32_t cf8, uint8_t offset, uint8_t bytes, uint32_t data); if ( admin_io_okay(port, bytes, v, regs) ) { switch ( bytes ) { case 1: outb((uint8_t)data, port); if ( pv_post_outb_hook ) pv_post_outb_hook(port, (uint8_t)data); break; case 2: outw((uint16_t)data, port); break; case 4: outl(data, port); break; } return; } while ( bytes != 0 ) { unsigned int size = 1; if ( (port == 0x42) || (port == 0x43) || (port == 0x61) ) { pv_pit_handler(port, (uint8_t)data, 1); } else if ( (port == 0xcf8) && (bytes == 4) ) { size = 4; v->domain->arch.pci_cf8 = data; } else if ( ((port & 0xfffc) == 0xcfc) && IS_PRIV(v->domain) ) { size = min(bytes, 4 - (port & 3)); if ( size == 3 ) size = 2; pci_conf_write(v->domain->arch.pci_cf8, port & 3, size, data); } if ( size == 4 ) return; port += size; bytes -= size; data >>= size * 8; }}/* 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; /* emulate 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; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -