📄 seg_fixup.c
字号:
/****************************************************************************** * arch/x86/x86_32/seg_fixup.c * * Support for -ve accesses to pseudo-4GB segments. * * Copyright (c) 2004, K A Fraser * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that 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/sched.h>#include <xen/lib.h>#include <xen/errno.h>#include <xen/mm.h>#include <xen/perfc.h>#include <asm/current.h>#include <asm/processor.h>#include <asm/regs.h>#include <asm/x86_emulate.h>/* General instruction properties. */#define INSN_SUFFIX_BYTES (7)#define OPCODE_BYTE (1<<4) #define HAS_MODRM (1<<5)/* Short forms for the table. */#define X 0 /* invalid for some random reason */#define O OPCODE_BYTE#define M HAS_MODRMstatic const unsigned char insn_decode[256] = { /* 0x00 - 0x0F */ O|M, O|M, O|M, O|M, X, X, X, X, O|M, O|M, O|M, O|M, X, X, X, X, /* 0x10 - 0x1F */ O|M, O|M, O|M, O|M, X, X, X, X, O|M, O|M, O|M, O|M, X, X, X, X, /* 0x20 - 0x2F */ O|M, O|M, O|M, O|M, X, X, X, X, O|M, O|M, O|M, O|M, X, X, X, X, /* 0x30 - 0x3F */ O|M, O|M, O|M, O|M, X, X, X, X, O|M, O|M, O|M, O|M, X, X, X, X, /* 0x40 - 0x4F */ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0x50 - 0x5F */ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0x60 - 0x6F */ X, X, X, X, X, X, X, X, X, O|M|4, X, O|M|1, X, X, X, X, /* 0x70 - 0x7F */ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0x80 - 0x8F */ O|M|1, O|M|4, O|M|1, O|M|1, O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M, X|M, O|M, O|M, /* 0x90 - 0x9F */ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0xA0 - 0xAF */ O|4, O|4, O|4, O|4, X, X, X, X, X, X, X, X, X, X, X, X, /* 0xB0 - 0xBF */ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0xC0 - 0xCF */ O|M|1, O|M|1, X, X, X, X, O|M|1, O|M|4, X, X, X, X, X, X, X, X, /* 0xD0 - 0xDF */ O|M, O|M, O|M, O|M, X, X, X, X, X, X, X, X, X, X, X, X, /* 0xE0 - 0xEF */ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0xF0 - 0xFF */ X, X, X, X, X, X, O|M, O|M, X, X, X, X, X, X, O|M, O|M};static const unsigned char twobyte_decode[256] = { /* 0x00 - 0x0F */ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0x10 - 0x1F */ X, X, X, X, X, X, X, X, O|M, X, X, X, X, X, X, X, /* 0x20 - 0x2F */ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0x30 - 0x3F */ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0x40 - 0x4F */ O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M, /* 0x50 - 0x5F */ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0x60 - 0x6F */ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0x70 - 0x7F */ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0x80 - 0x8F */ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0x90 - 0x9F */ O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M, /* 0xA0 - 0xAF */ X, X, X, O|M, O|M|1, O|M, O|M, X, X, X, X, O|M, O|M|1, O|M, X, O|M, /* 0xB0 - 0xBF */ X, X, X, O|M, X, X, O|M, O|M, X, X, O|M|1, O|M, O|M, O|M, O|M, O|M, /* 0xC0 - 0xCF */ O|M, O|M, X, O|M, X, X, X, O|M, X, X, X, X, X, X, X, X, /* 0xD0 - 0xDF */ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0xE0 - 0xEF */ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0xF0 - 0xFF */ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X};/* * Obtain the base and limit associated with the given segment selector. * The selector must identify a 32-bit code or data segment. Any segment that * appears to be truncated to not overlap with Xen is assumed to be a truncated * 4GB segment, and the returned limit reflects this. * @seg (IN) : Segment selector to decode. * @base (OUT): Decoded linear base address. * @limit (OUT): Decoded segment limit, in bytes. 0 == unlimited (4GB). */static int get_baselimit(u16 seg, unsigned long *base, unsigned long *limit){ struct vcpu *curr = current; uint32_t *table, a, b; int ldt = !!(seg & 4); int idx = (seg >> 3) & 8191; /* Get base and check limit. */ if ( ldt ) { table = (uint32_t *)LDT_VIRT_START(curr); if ( idx >= curr->arch.guest_context.ldt_ents ) goto fail; } else /* gdt */ { table = (uint32_t *)GDT_VIRT_START(curr); if ( idx >= curr->arch.guest_context.gdt_ents ) goto fail; } /* Grab the segment descriptor. */ if ( __get_user(a, &table[2*idx+0]) || __get_user(b, &table[2*idx+1]) ) goto fail; /* Barking up the wrong tree. Decode needs a page fault.*/ /* We only parse 32-bit code and data segments. */ if ( (b & (_SEGMENT_P|_SEGMENT_S|_SEGMENT_DB)) != (_SEGMENT_P|_SEGMENT_S|_SEGMENT_DB) ) goto fail; /* Decode base and limit. */ *base = (b&(0xff<<24)) | ((b&0xff)<<16) | (a>>16); *limit = ((b & 0xf0000) | (a & 0x0ffff)) + 1; if ( (b & _SEGMENT_G) ) *limit <<= 12; /* * Anything that looks like a truncated segment we assume ought really * to be a 4GB segment. DANGER! */ if ( (GUEST_SEGMENT_MAX_ADDR - (*base + *limit)) < PAGE_SIZE ) *limit = 0; return 1; fail: return 0;}/* Turn a segment+offset into a linear address. */static int linearise_address(u16 seg, unsigned long off, unsigned long *linear){ unsigned long base, limit; if ( !get_baselimit(seg, &base, &limit) ) return 0; if ( off > (limit-1) ) return 0; *linear = base + off; return 1;}static int fixup_seg(u16 seg, unsigned long offset){ struct vcpu *curr = current; uint32_t *table, a, b, base, limit; int ldt = !!(seg & 4); int idx = (seg >> 3) & 8191; /* Get base and check limit. */ if ( ldt ) { table = (uint32_t *)LDT_VIRT_START(curr); if ( idx >= curr->arch.guest_context.ldt_ents ) { dprintk(XENLOG_DEBUG, "Segment %04x out of LDT range (%ld)\n", seg, curr->arch.guest_context.ldt_ents); goto fail; } } else /* gdt */ { table = (uint32_t *)GDT_VIRT_START(curr); if ( idx >= curr->arch.guest_context.gdt_ents ) { dprintk(XENLOG_DEBUG, "Segment %04x out of GDT range (%ld)\n", seg, curr->arch.guest_context.gdt_ents); goto fail; } } /* Grab the segment descriptor. */ if ( __get_user(a, &table[2*idx+0]) || __get_user(b, &table[2*idx+1]) ) { dprintk(XENLOG_DEBUG, "Fault while reading segment %04x\n", seg); goto fail; /* Barking up the wrong tree. Decode needs a page fault.*/ } /* We only parse 32-bit page-granularity non-privileged data segments. */ if ( (b & (_SEGMENT_P|_SEGMENT_S|_SEGMENT_DB| _SEGMENT_G|_SEGMENT_CODE|_SEGMENT_DPL)) != (_SEGMENT_P|_SEGMENT_S|_SEGMENT_DB|_SEGMENT_G|_SEGMENT_DPL) ) { dprintk(XENLOG_DEBUG, "Bad segment %08x:%08x\n", a, b); goto fail; } /* Decode base and limit. */ base = (b&(0xff<<24)) | ((b&0xff)<<16) | (a>>16); limit = (((b & 0xf0000) | (a & 0x0ffff)) + 1) << 12; if ( b & _SEGMENT_EC ) { /* Expands-down: All the way to zero? Assume 4GB if so. */ if ( ((base + limit) < PAGE_SIZE) && (offset <= limit) ) { /* Flip to expands-up. */ limit = GUEST_SEGMENT_MAX_ADDR - base; goto flip; } } else { /* Expands-up: All the way to Xen space? Assume 4GB if so. */ if ( ((GUEST_SEGMENT_MAX_ADDR - (base + limit)) < PAGE_SIZE) && (offset > limit) ) { /* Flip to expands-down. */ limit = -(base & PAGE_MASK); goto flip; } } dprintk(XENLOG_DEBUG, "None of the above! (%08x:%08x, %08x, %08x, %08x)\n", a, b, base, limit, base+limit); fail: return 0; flip: limit = (limit >> 12) - 1; a &= ~0x0ffff; a |= limit & 0x0ffff; b &= ~0xf0000; b |= limit & 0xf0000; b ^= _SEGMENT_EC; /* grows-up <-> grows-down */ /* NB. This can't fault. Checked readable above; must also be writable. */ atomic_write64((uint64_t *)&table[2*idx], ((uint64_t)b<<32) | a); return 1;}/* * Called from the general-protection fault handler to attempt to decode * and emulate an instruction that depends on 4GB segments. */int gpf_emulate_4gb(struct cpu_user_regs *regs){ struct vcpu *curr = current; u8 modrm, mod, rm, decode; const u32 *base, *index = NULL; unsigned long offset; s8 disp8; s32 disp32 = 0; u8 *eip; /* ptr to instruction start */ u8 *pb, b; /* ptr into instr. / current instr. byte */ int gs_override = 0, scale = 0, twobyte = 0; /* WARNING: We only work for ring-3 segments. */ if ( unlikely(vm86_mode(regs)) || unlikely(!ring_3(regs)) ) goto fail; if ( !linearise_address((u16)regs->cs, regs->eip, (unsigned long *)&eip) ) { dprintk(XENLOG_DEBUG, "Cannot linearise %04x:%08x\n", regs->cs, regs->eip); goto fail; } /* Parse prefix bytes. We're basically looking for segment override. */ for ( pb = eip; ; pb++ ) { if ( get_user(b, pb) ) { dprintk(XENLOG_DEBUG, "Fault while accessing byte %ld of instruction\n", (long)(pb-eip)); goto page_fault; } if ( (pb - eip) >= 15 ) { dprintk(XENLOG_DEBUG, "Too many instruction prefixes for a " "legal instruction\n"); goto fail; } if ( twobyte ) break; switch ( b ) { case 0x67: /* Address-size override */ case 0x2e: /* CS override */ case 0x3e: /* DS override */ case 0x26: /* ES override */ case 0x64: /* FS override */ case 0x36: /* SS override */ dprintk(XENLOG_DEBUG, "Unhandled prefix %02x\n", b); goto fail; case 0x66: /* Operand-size override */ case 0xf0: /* LOCK */ case 0xf2: /* REPNE/REPNZ */ case 0xf3: /* REP/REPE/REPZ */ break; case 0x65: /* GS override */ gs_override = 1; break; case 0x0f: /* Not really a prefix byte */ twobyte = 1; break; default: /* Not a prefix byte */ goto done_prefix; } } done_prefix: if ( !gs_override ) { dprintk(XENLOG_DEBUG, "Only instructions with GS override\n"); goto fail; } decode = (!twobyte ? insn_decode : twobyte_decode)[b]; pb++; if ( !(decode & OPCODE_BYTE) ) { dprintk(XENLOG_DEBUG, "Unsupported %sopcode %02x\n", twobyte ? "two byte " : "", b); goto fail; } if ( !(decode & HAS_MODRM) ) { /* Must be a <disp32>, or bail. */ if ( (decode & INSN_SUFFIX_BYTES) != 4 ) goto fail; if ( get_user(offset, (u32 *)pb) ) { dprintk(XENLOG_DEBUG, "Fault while extracting <moffs32>.\n"); goto page_fault; } pb += 4; goto skip_modrm; } /* * Mod/RM processing. */ if ( get_user(modrm, pb) ) { dprintk(XENLOG_DEBUG, "Fault while extracting modrm byte\n"); goto page_fault; } pb++; mod = (modrm >> 6) & 3; rm = (modrm >> 0) & 7; if ( rm == 4 ) { u8 sib; if ( get_user(sib, pb) ) { dprintk(XENLOG_DEBUG, "Fault while extracting sib byte\n"); goto page_fault; } pb++; rm = sib & 7; if ( (sib & 0x38) != 0x20 ) index = decode_register((sib >> 3) & 7, regs, 0); scale = sib >> 6; } /* Decode R/M field. */ base = decode_register(rm, regs, 0); /* Decode Mod field. */ switch ( mod ) { case 0: if ( rm == 5 ) /* disp32 rather than (EBP) */ { base = NULL; if ( get_user(disp32, (u32 *)pb) ) { dprintk(XENLOG_DEBUG, "Fault while extracting <base32>.\n"); goto page_fault; } pb += 4; } break; case 1: if ( get_user(disp8, pb) ) { dprintk(XENLOG_DEBUG, "Fault while extracting <disp8>.\n"); goto page_fault; } pb++; disp32 = disp8; break; case 2: if ( get_user(disp32, (u32 *)pb) ) { dprintk(XENLOG_DEBUG, "Fault while extracting <disp32>.\n"); goto page_fault; } pb += 4; break; case 3: dprintk(XENLOG_DEBUG, "Not a memory operand!\n"); goto fail; } offset = disp32; if ( base != NULL ) offset += *base; if ( index != NULL ) offset += *index << scale; skip_modrm: if ( !fixup_seg((u16)regs->gs, offset) ) goto fail; /* Success! */ perfc_incr(seg_fixups); /* If requested, give a callback on otherwise unused vector 15. */ if ( VM_ASSIST(curr->domain, VMASST_TYPE_4gb_segments_notify) ) { struct trap_info *ti = &curr->arch.guest_context.trap_ctxt[15]; struct trap_bounce *tb = &curr->arch.trap_bounce; tb->flags = TBF_EXCEPTION | TBF_EXCEPTION_ERRCODE; tb->error_code = pb - eip; tb->cs = ti->cs; tb->eip = ti->address; if ( TI_GET_IF(ti) ) tb->flags |= TBF_INTERRUPT; } return EXCRET_fault_fixed; fail: return 0; page_fault: propagate_page_fault((unsigned long)pb, 0); /* read fault */ return EXCRET_fault_fixed;}/* * Local variables: * mode: C * c-set-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -