unsigned int bytes,
struct x86_emulate_ctxt *ctxt)
{
- unsigned long addr = offset;
- switch ( bytes )
- {
- case 1: *val = *(uint8_t *)addr; break;
- case 2: *val = *(uint16_t *)addr; break;
- case 4: *val = *(uint32_t *)addr; break;
- case 8: *val = *(unsigned long *)addr; break;
- }
+ *val = 0;
+ memcpy(val, (void *)offset, bytes);
return X86EMUL_OKAY;
}
unsigned int bytes,
struct x86_emulate_ctxt *ctxt)
{
- unsigned long addr = offset;
- switch ( bytes )
- {
- case 1: *(uint8_t *)addr = (uint8_t)val; break;
- case 2: *(uint16_t *)addr = (uint16_t)val; break;
- case 4: *(uint32_t *)addr = (uint32_t)val; break;
- case 8: *(unsigned long *)addr = val; break;
- }
+ memcpy((void *)offset, &val, bytes);
return X86EMUL_OKAY;
}
static int cmpxchg(
unsigned int seg,
unsigned long offset,
- unsigned long old,
- unsigned long new,
+ void *old,
+ void *new,
unsigned int bytes,
struct x86_emulate_ctxt *ctxt)
{
- unsigned long addr = offset;
- switch ( bytes )
- {
- case 1: *(uint8_t *)addr = (uint8_t)new; break;
- case 2: *(uint16_t *)addr = (uint16_t)new; break;
- case 4: *(uint32_t *)addr = (uint32_t)new; break;
- case 8: *(unsigned long *)addr = new; break;
- }
- return X86EMUL_OKAY;
-}
-
-static int cmpxchg8b(
- unsigned int seg,
- unsigned long offset,
- unsigned long old_lo,
- unsigned long old_hi,
- unsigned long new_lo,
- unsigned long new_hi,
- struct x86_emulate_ctxt *ctxt)
-{
- unsigned long addr = offset;
- ((unsigned long *)addr)[0] = new_lo;
- ((unsigned long *)addr)[1] = new_hi;
+ memcpy((void *)offset, new, bytes);
return X86EMUL_OKAY;
}
.insn_fetch = read,
.write = write,
.cmpxchg = cmpxchg,
- .cmpxchg8b = cmpxchg8b
};
int main(int argc, char **argv)
static int hvmemul_cmpxchg(
enum x86_segment seg,
unsigned long offset,
- unsigned long old,
- unsigned long new,
+ void *p_old,
+ void *p_new,
unsigned int bytes,
struct x86_emulate_ctxt *ctxt)
{
+ unsigned long new = 0;
+ if ( bytes > sizeof(new) )
+ return X86EMUL_UNHANDLEABLE;
+ memcpy(&new, p_new, bytes);
/* Fix this in case the guest is really relying on r-m-w atomicity. */
return hvmemul_write(seg, offset, new, bytes, ctxt);
}
static int ptwr_emulated_cmpxchg(
enum x86_segment seg,
unsigned long offset,
- unsigned long old,
- unsigned long new,
+ void *p_old,
+ void *p_new,
unsigned int bytes,
struct x86_emulate_ctxt *ctxt)
{
- return ptwr_emulated_update(
- offset, old, new, bytes, 1,
- container_of(ctxt, struct ptwr_emulate_ctxt, ctxt));
-}
-
-static int ptwr_emulated_cmpxchg8b(
- enum x86_segment seg,
- unsigned long offset,
- unsigned long old,
- unsigned long old_hi,
- unsigned long new,
- unsigned long new_hi,
- struct x86_emulate_ctxt *ctxt)
-{
- if ( CONFIG_PAGING_LEVELS == 2 )
+ paddr_t old = 0, new = 0;
+ if ( bytes > sizeof(paddr_t) )
return X86EMUL_UNHANDLEABLE;
+ memcpy(&old, p_old, bytes);
+ memcpy(&new, p_new, bytes);
return ptwr_emulated_update(
- offset, ((u64)old_hi << 32) | old, ((u64)new_hi << 32) | new, 8, 1,
+ offset, old, new, bytes, 1,
container_of(ctxt, struct ptwr_emulate_ctxt, ctxt));
}
.insn_fetch = ptwr_emulated_read,
.write = ptwr_emulated_write,
.cmpxchg = ptwr_emulated_cmpxchg,
- .cmpxchg8b = ptwr_emulated_cmpxchg8b
};
/* Write page fault handler: check if guest is trying to modify a PTE. */
static int
hvm_emulate_cmpxchg(enum x86_segment seg,
unsigned long offset,
- unsigned long old,
- unsigned long new,
+ void *p_old,
+ void *p_new,
unsigned int bytes,
struct x86_emulate_ctxt *ctxt)
{
struct sh_emulate_ctxt *sh_ctxt =
container_of(ctxt, struct sh_emulate_ctxt, ctxt);
struct vcpu *v = current;
- unsigned long addr;
+ unsigned long addr, old[2], new[2];
int rc;
if ( !is_x86_user_segment(seg) )
if ( rc )
return rc;
- return v->arch.paging.mode->shadow.x86_emulate_cmpxchg(
- v, addr, old, new, bytes, sh_ctxt);
-}
-
-static int
-hvm_emulate_cmpxchg8b(enum x86_segment seg,
- unsigned long offset,
- unsigned long old_lo,
- unsigned long old_hi,
- unsigned long new_lo,
- unsigned long new_hi,
- struct x86_emulate_ctxt *ctxt)
-{
- struct sh_emulate_ctxt *sh_ctxt =
- container_of(ctxt, struct sh_emulate_ctxt, ctxt);
- struct vcpu *v = current;
- unsigned long addr;
- int rc;
+ old[0] = new[0] = 0;
+ memcpy(old, p_old, bytes);
+ memcpy(new, p_new, bytes);
- if ( !is_x86_user_segment(seg) )
- return X86EMUL_UNHANDLEABLE;
+ if ( bytes <= sizeof(long) )
+ return v->arch.paging.mode->shadow.x86_emulate_cmpxchg(
+ v, addr, old[0], new[0], bytes, sh_ctxt);
- rc = hvm_translate_linear_addr(
- seg, offset, 8, hvm_access_write, sh_ctxt, &addr);
- if ( rc )
- return rc;
+#ifdef __i386__
+ if ( bytes == 8 )
+ return v->arch.paging.mode->shadow.x86_emulate_cmpxchg8b(
+ v, addr, old[0], old[1], new[0], new[1], sh_ctxt);
+#endif
- return v->arch.paging.mode->shadow.x86_emulate_cmpxchg8b(
- v, addr, old_lo, old_hi, new_lo, new_hi, sh_ctxt);
+ return X86EMUL_UNHANDLEABLE;
}
static struct x86_emulate_ops hvm_shadow_emulator_ops = {
.insn_fetch = hvm_emulate_insn_fetch,
.write = hvm_emulate_write,
.cmpxchg = hvm_emulate_cmpxchg,
- .cmpxchg8b = hvm_emulate_cmpxchg8b,
};
static int
static int
pv_emulate_cmpxchg(enum x86_segment seg,
unsigned long offset,
- unsigned long old,
- unsigned long new,
+ void *p_old,
+ void *p_new,
unsigned int bytes,
struct x86_emulate_ctxt *ctxt)
{
struct sh_emulate_ctxt *sh_ctxt =
container_of(ctxt, struct sh_emulate_ctxt, ctxt);
+ unsigned long old[2], new[2];
struct vcpu *v = current;
- if ( !is_x86_user_segment(seg) )
- return X86EMUL_UNHANDLEABLE;
- return v->arch.paging.mode->shadow.x86_emulate_cmpxchg(
- v, offset, old, new, bytes, sh_ctxt);
-}
-static int
-pv_emulate_cmpxchg8b(enum x86_segment seg,
- unsigned long offset,
- unsigned long old_lo,
- unsigned long old_hi,
- unsigned long new_lo,
- unsigned long new_hi,
- struct x86_emulate_ctxt *ctxt)
-{
- struct sh_emulate_ctxt *sh_ctxt =
- container_of(ctxt, struct sh_emulate_ctxt, ctxt);
- struct vcpu *v = current;
if ( !is_x86_user_segment(seg) )
return X86EMUL_UNHANDLEABLE;
- return v->arch.paging.mode->shadow.x86_emulate_cmpxchg8b(
- v, offset, old_lo, old_hi, new_lo, new_hi, sh_ctxt);
+
+ old[0] = new[0] = 0;
+ memcpy(old, p_old, bytes);
+ memcpy(new, p_new, bytes);
+
+ if ( bytes <= sizeof(long) )
+ return v->arch.paging.mode->shadow.x86_emulate_cmpxchg(
+ v, offset, old[0], new[0], bytes, sh_ctxt);
+
+#ifdef __i386__
+ if ( bytes == 8 )
+ return v->arch.paging.mode->shadow.x86_emulate_cmpxchg8b(
+ v, offset, old[0], old[1], new[0], new[1], sh_ctxt);
+#endif
+
+ return X86EMUL_UNHANDLEABLE;
}
static struct x86_emulate_ops pv_shadow_emulator_ops = {
.insn_fetch = pv_emulate_read,
.write = pv_emulate_write,
.cmpxchg = pv_emulate_cmpxchg,
- .cmpxchg8b = pv_emulate_cmpxchg8b,
};
struct x86_emulate_ops *shadow_init_emulation(
atomic_inc(&v->domain->arch.paging.shadow.gtable_dirty_version);
}
-int
+static int
sh_x86_emulate_write(struct vcpu *v, unsigned long vaddr, void *src,
u32 bytes, struct sh_emulate_ctxt *sh_ctxt)
{
return X86EMUL_OKAY;
}
-int
+static int
sh_x86_emulate_cmpxchg(struct vcpu *v, unsigned long vaddr,
unsigned long old, unsigned long new,
unsigned int bytes, struct sh_emulate_ctxt *sh_ctxt)
return rv;
}
-int
+#ifdef __i386__
+static int
sh_x86_emulate_cmpxchg8b(struct vcpu *v, unsigned long vaddr,
unsigned long old_lo, unsigned long old_hi,
unsigned long new_lo, unsigned long new_hi,
shadow_unlock(v->domain);
return rv;
}
-
+#endif
/**************************************************************************/
/* Audit tools */
.shadow.detach_old_tables = sh_detach_old_tables,
.shadow.x86_emulate_write = sh_x86_emulate_write,
.shadow.x86_emulate_cmpxchg = sh_x86_emulate_cmpxchg,
+#ifdef __i386__
.shadow.x86_emulate_cmpxchg8b = sh_x86_emulate_cmpxchg8b,
+#endif
.shadow.make_monitor_table = sh_make_monitor_table,
.shadow.destroy_monitor_table = sh_destroy_monitor_table,
#if SHADOW_OPTIMIZATIONS & SHOPT_WRITABLE_HEURISTIC
struct { uint32_t a, b; } desc;
unsigned long val;
uint8_t dpl, rpl, cpl;
+ uint32_t new_desc_b;
int rc, fault_type = EXC_TS;
/* NULL selector? */
}
/* Ensure Accessed flag is set. */
+ new_desc_b = desc.b | 0x100;
rc = ((desc.b & 0x100) ? X86EMUL_OKAY :
ops->cmpxchg(
- x86_seg_none, desctab.base + (sel & 0xfff8) + 4, desc.b,
- desc.b | 0x100, 4, ctxt));
+ x86_seg_none, desctab.base + (sel & 0xfff8) + 4,
+ &desc.b, &new_desc_b, 4, ctxt));
} while ( rc == X86EMUL_CMPXCHG_FAILED );
if ( rc )
/* nothing to do */;
else if ( lock_prefix )
rc = ops->cmpxchg(
- dst.mem.seg, dst.mem.off, dst.orig_val,
- dst.val, dst.bytes, ctxt);
+ dst.mem.seg, dst.mem.off, &dst.orig_val,
+ &dst.val, dst.bytes, ctxt);
else
rc = ops->write(
dst.mem.seg, dst.mem.off, dst.val, dst.bytes, ctxt);
src.val = x86_seg_gs;
goto pop_seg;
- case 0xc7: /* Grp9 (cmpxchg8b) */
-#if defined(__i386__)
- {
- unsigned long old_lo, old_hi;
+ case 0xc7: /* Grp9 (cmpxchg8b/cmpxchg16b) */ {
+ unsigned long old[2], exp[2], new[2];
+ unsigned int i;
+
generate_exception_if((modrm_reg & 7) != 1, EXC_UD, -1);
generate_exception_if(ea.type != OP_MEM, EXC_UD, -1);
- if ( (rc = ops->read(ea.mem.seg, ea.mem.off+0, &old_lo, 4, ctxt)) ||
- (rc = ops->read(ea.mem.seg, ea.mem.off+4, &old_hi, 4, ctxt)) )
- goto done;
- if ( (old_lo != _regs.eax) || (old_hi != _regs.edx) )
- {
- _regs.eax = old_lo;
- _regs.edx = old_hi;
- _regs.eflags &= ~EFLG_ZF;
- }
- else if ( ops->cmpxchg8b == NULL )
+ op_bytes *= 2;
+
+ /* Get actual old value. */
+ for ( i = 0; i < (op_bytes/sizeof(long)); i++ )
+ if ( (rc = ops->read(ea.mem.seg, ea.mem.off + i*sizeof(long),
+ &old[i], sizeof(long), ctxt)) != 0 )
+ goto done;
+
+ /* Get expected and proposed values. */
+ if ( op_bytes == 8 )
{
- rc = X86EMUL_UNHANDLEABLE;
- goto done;
+ ((uint32_t *)exp)[0] = _regs.eax; ((uint32_t *)exp)[1] = _regs.edx;
+ ((uint32_t *)new)[0] = _regs.ebx; ((uint32_t *)new)[1] = _regs.ecx;
}
else
{
- if ( (rc = ops->cmpxchg8b(ea.mem.seg, ea.mem.off, old_lo, old_hi,
- _regs.ebx, _regs.ecx, ctxt)) != 0 )
- goto done;
- _regs.eflags |= EFLG_ZF;
+ exp[0] = _regs.eax; exp[1] = _regs.edx;
+ new[0] = _regs.ebx; new[1] = _regs.ecx;
}
- break;
- }
-#elif defined(__x86_64__)
- {
- unsigned long old, new;
- generate_exception_if((modrm_reg & 7) != 1, EXC_UD, -1);
- generate_exception_if(ea.type != OP_MEM, EXC_UD, -1);
- if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &old, 8, ctxt)) != 0 )
- goto done;
- if ( ((uint32_t)(old>>0) != (uint32_t)_regs.eax) ||
- ((uint32_t)(old>>32) != (uint32_t)_regs.edx) )
+
+ if ( memcmp(old, exp, op_bytes) )
{
- _regs.eax = (uint32_t)(old>>0);
- _regs.edx = (uint32_t)(old>>32);
+ /* Expected != actual: store actual to rDX:rAX and clear ZF. */
+ _regs.eax = (op_bytes == 8) ? ((uint32_t *)old)[0] : old[0];
+ _regs.edx = (op_bytes == 8) ? ((uint32_t *)old)[1] : old[1];
_regs.eflags &= ~EFLG_ZF;
}
else
{
- new = (_regs.ecx<<32)|(uint32_t)_regs.ebx;
+ /* Expected == actual: attempt atomic cmpxchg and set ZF. */
if ( (rc = ops->cmpxchg(ea.mem.seg, ea.mem.off, old,
- new, 8, ctxt)) != 0 )
+ new, op_bytes, ctxt)) != 0 )
goto done;
_regs.eflags |= EFLG_ZF;
}
break;
}
-#endif
case 0xc8 ... 0xcf: /* bswap */
dst.type = OP_REG;
* some out-of-band mechanism, unknown to the emulator. The memop signals
* failure by returning X86EMUL_EXCEPTION to the emulator, which will
* then immediately bail.
- * 2. Valid access sizes are 1, 2, 4 and 8 bytes. On x86/32 systems only
- * cmpxchg8b_emulated need support 8-byte accesses.
+ * 2. Valid access sizes are 1, 2, 4 and 8 (x86/64 only) bytes.
* 3. The emulator cannot handle 64-bit mode emulation on an x86/32 system.
*/
struct x86_emulate_ops
/*
* cmpxchg: Emulate an atomic (LOCKed) CMPXCHG operation.
- * @old: [IN ] Value expected to be current at @addr.
- * @new: [IN ] Value to write to @addr.
+ * @p_old: [IN ] Pointer to value expected to be current at @addr.
+ * @p_new: [IN ] Pointer to value to write to @addr.
+ * @bytes: [IN ] Operation size (up to 8 (x86/32) or 16 (x86/64) bytes).
*/
int (*cmpxchg)(
enum x86_segment seg,
unsigned long offset,
- unsigned long old,
- unsigned long new,
+ void *p_old,
+ void *p_new,
unsigned int bytes,
struct x86_emulate_ctxt *ctxt);
- /*
- * cmpxchg8b: Emulate an atomic (LOCKed) CMPXCHG8B operation.
- * @old: [IN ] Value expected to be current at @addr.
- * @new: [IN ] Value to write to @addr.
- * NOTES:
- * 1. This function is only ever called when emulating a real CMPXCHG8B.
- * 2. This function is *never* called on x86/64 systems.
- * 2. Not defining this function (i.e., specifying NULL) is equivalent
- * to defining a function that always returns X86EMUL_UNHANDLEABLE.
- */
- int (*cmpxchg8b)(
- enum x86_segment seg,
- unsigned long offset,
- unsigned long old_lo,
- unsigned long old_hi,
- unsigned long new_lo,
- unsigned long new_hi,
- struct x86_emulate_ctxt *ctxt);
-
/*
* rep_ins: Emulate INS: <src_port> -> <dst_seg:dst_offset>.
* @bytes_per_rep: [IN ] Bytes transferred per repetition.
unsigned long new,
unsigned int bytes,
struct sh_emulate_ctxt *sh_ctxt);
+#ifdef __i386__
int (*x86_emulate_cmpxchg8b )(struct vcpu *v, unsigned long va,
unsigned long old_lo,
unsigned long old_hi,
unsigned long new_lo,
unsigned long new_hi,
struct sh_emulate_ctxt *sh_ctxt);
+#endif
mfn_t (*make_monitor_table )(struct vcpu *v);
void (*destroy_monitor_table )(struct vcpu *v, mfn_t mmfn);
int (*guess_wrmap )(struct vcpu *v,