@@ -46,7 +46,47 @@ static int read(
if ( verbose )
printf("** %s(%u, %p,, %u,)\n", __func__, seg, (void *)offset, bytes);
- bytes_read += bytes;
+ switch ( seg )
+ {
+ uint64_t value;
+
+ case x86_seg_gdtr:
+ /* Fake system segment type matching table index. */
+ if ( (offset & 7) || (bytes > 8) )
+ return X86EMUL_UNHANDLEABLE;
+#ifdef __x86_64__
+ if ( !(offset & 8) )
+ {
+ memset(p_data, 0, bytes);
+ return X86EMUL_OKAY;
+ }
+ value = (offset - 8) >> 4;
+#else
+ value = (offset - 8) >> 3;
+#endif
+ if ( value >= 0x10 )
+ return X86EMUL_UNHANDLEABLE;
+ value |= value << 40;
+ memcpy(p_data, &value, bytes);
+ return X86EMUL_OKAY;
+
+ case x86_seg_ldtr:
+ /* Fake user segment type matching table index. */
+ if ( (offset & 7) || (bytes > 8) )
+ return X86EMUL_UNHANDLEABLE;
+ value = offset >> 3;
+ if ( value >= 0x10 )
+ return X86EMUL_UNHANDLEABLE;
+ value |= (value | 0x10) << 40;
+ memcpy(p_data, &value, bytes);
+ return X86EMUL_OKAY;
+
+ default:
+ if ( !is_x86_user_segment(seg) )
+ return X86EMUL_UNHANDLEABLE;
+ bytes_read += bytes;
+ break;
+ }
memcpy(p_data, (void *)offset, bytes);
return X86EMUL_OKAY;
}
@@ -75,6 +115,8 @@ static int write(
if ( verbose )
printf("** %s(%u, %p,, %u,)\n", __func__, seg, (void *)offset, bytes);
+ if ( !is_x86_user_segment(seg) )
+ return X86EMUL_UNHANDLEABLE;
memcpy((void *)offset, p_data, bytes);
return X86EMUL_OKAY;
}
@@ -90,10 +132,24 @@ static int cmpxchg(
if ( verbose )
printf("** %s(%u, %p,, %u,)\n", __func__, seg, (void *)offset, bytes);
+ if ( !is_x86_user_segment(seg) )
+ return X86EMUL_UNHANDLEABLE;
memcpy((void *)offset, new, bytes);
return X86EMUL_OKAY;
}
+static int read_segment(
+ enum x86_segment seg,
+ struct segment_register *reg,
+ struct x86_emulate_ctxt *ctxt)
+{
+ if ( !is_x86_user_segment(seg) )
+ return X86EMUL_UNHANDLEABLE;
+ memset(reg, 0, sizeof(*reg));
+ reg->attr.fields.p = 1;
+ return X86EMUL_OKAY;
+}
+
static int cpuid(
unsigned int *eax,
unsigned int *ebx,
@@ -193,6 +249,21 @@ static int read_cr(
return X86EMUL_UNHANDLEABLE;
}
+static int read_msr(
+ unsigned int reg,
+ uint64_t *val,
+ struct x86_emulate_ctxt *ctxt)
+{
+ switch ( reg )
+ {
+ case 0xc0000080: /* EFER */
+ *val = ctxt->addr_size > 32 ? 0x500 /* LME|LMA */ : 0;
+ return X86EMUL_OKAY;
+ }
+
+ return X86EMUL_UNHANDLEABLE;
+}
+
int get_fpu(
void (*exception_callback)(void *, struct cpu_user_regs *),
void *exception_callback_arg,
@@ -223,8 +294,10 @@ static struct x86_emulate_ops emulops =
.insn_fetch = fetch,
.write = write,
.cmpxchg = cmpxchg,
+ .read_segment = read_segment,
.cpuid = cpuid,
.read_cr = read_cr,
+ .read_msr = read_msr,
.get_fpu = get_fpu,
};
@@ -724,6 +797,156 @@ int main(int argc, char **argv)
goto fail;
printf("okay\n");
+ printf("%-40s", "Testing lar (null selector)...");
+ instr[0] = 0x0f; instr[1] = 0x02; instr[2] = 0xc1;
+ regs.eflags = 0x240;
+ regs.eip = (unsigned long)&instr[0];
+ regs.ecx = 0;
+ regs.eax = 0x11111111;
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.eax != 0x11111111) ||
+ (regs.eflags != 0x200) ||
+ (regs.eip != (unsigned long)&instr[3]) )
+ goto fail;
+ printf("okay\n");
+
+ printf("%-40s", "Testing lsl (null selector)...");
+ instr[0] = 0x0f; instr[1] = 0x03; instr[2] = 0xca;
+ regs.eflags = 0x240;
+ regs.eip = (unsigned long)&instr[0];
+ regs.edx = 0;
+ regs.ecx = 0x11111111;
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.ecx != 0x11111111) ||
+ (regs.eflags != 0x200) ||
+ (regs.eip != (unsigned long)&instr[3]) )
+ goto fail;
+ printf("okay\n");
+
+ printf("%-40s", "Testing verr (null selector)...");
+ instr[0] = 0x0f; instr[1] = 0x00; instr[2] = 0x21;
+ regs.eflags = 0x240;
+ regs.eip = (unsigned long)&instr[0];
+ regs.ecx = (unsigned long)res;
+ *res = 0;
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.eflags != 0x200) ||
+ (regs.eip != (unsigned long)&instr[3]) )
+ goto fail;
+ printf("okay\n");
+
+ printf("%-40s", "Testing verw (null selector)...");
+ instr[0] = 0x0f; instr[1] = 0x00; instr[2] = 0x2a;
+ regs.eflags = 0x240;
+ regs.eip = (unsigned long)&instr[0];
+ regs.ecx = 0;
+ regs.edx = (unsigned long)res;
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.eflags != 0x200) ||
+ (regs.eip != (unsigned long)&instr[3]) )
+ goto fail;
+ printf("okay\n");
+
+ printf("%-40s", "Testing lar/lsl/verr/verw (all types)...");
+ for ( i = 0; i < 0x20; ++i )
+ {
+ unsigned int sel = i < 0x10 ?
+#ifndef __x86_64__
+ (i << 3) + 8
+#else
+ (i << 4) + 8
+#endif
+ : ((i - 0x10) << 3) | 4;
+ bool failed;
+
+#ifndef __x86_64__
+# define LAR_VALID 0xffff1a3eU
+# define LSL_VALID 0xffff0a0eU
+#else
+# define LAR_VALID 0xffff1a04U
+# define LSL_VALID 0xffff0a04U
+#endif
+#define VERR_VALID 0xccff0000U
+#define VERW_VALID 0x00cc0000U
+
+ instr[0] = 0x0f; instr[1] = 0x02; instr[2] = 0xc2;
+ regs.eflags = (LAR_VALID >> i) & 1 ? 0x200 : 0x240;
+ regs.eip = (unsigned long)&instr[0];
+ regs.edx = sel;
+ regs.eax = 0x11111111;
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.eip != (unsigned long)&instr[3]) )
+ goto fail;
+ if ( (LAR_VALID >> i) & 1 )
+ failed = (regs.eflags != 0x240) ||
+ ((regs.eax & 0xf0ff00) != (i << 8));
+ else
+ failed = (regs.eflags != 0x200) ||
+ (regs.eax != 0x11111111);
+ if ( failed )
+ {
+ printf("LAR %04x (type %02x) ", sel, i);
+ goto fail;
+ }
+
+ instr[0] = 0x0f; instr[1] = 0x03; instr[2] = 0xd1;
+ regs.eflags = (LSL_VALID >> i) & 1 ? 0x200 : 0x240;
+ regs.eip = (unsigned long)&instr[0];
+ regs.ecx = sel;
+ regs.edx = 0x11111111;
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.eip != (unsigned long)&instr[3]) )
+ goto fail;
+ if ( (LSL_VALID >> i) & 1 )
+ failed = (regs.eflags != 0x240) ||
+ (regs.edx != (i & 0xf));
+ else
+ failed = (regs.eflags != 0x200) ||
+ (regs.edx != 0x11111111);
+ if ( failed )
+ {
+ printf("LSL %04x (type %02x) ", sel, i);
+ goto fail;
+ }
+
+ instr[0] = 0x0f; instr[1] = 0x00; instr[2] = 0xe2;
+ regs.eflags = (VERR_VALID >> i) & 1 ? 0x200 : 0x240;
+ regs.eip = (unsigned long)&instr[0];
+ regs.ecx = 0;
+ regs.edx = sel;
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.eip != (unsigned long)&instr[3]) )
+ goto fail;
+ if ( regs.eflags != ((VERR_VALID >> i) & 1 ? 0x240 : 0x200) )
+ {
+ printf("VERR %04x (type %02x) ", sel, i);
+ goto fail;
+ }
+
+ instr[0] = 0x0f; instr[1] = 0x00; instr[2] = 0xe9;
+ regs.eflags = (VERW_VALID >> i) & 1 ? 0x200 : 0x240;
+ regs.eip = (unsigned long)&instr[0];
+ regs.ecx = sel;
+ regs.edx = 0;
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( (rc != X86EMUL_OKAY) ||
+ (regs.eip != (unsigned long)&instr[3]) )
+ goto fail;
+ if ( regs.eflags != ((VERW_VALID >> i) & 1 ? 0x240 : 0x200) )
+ {
+ printf("VERW %04x (type %02x) ", sel, i);
+ goto fail;
+ }
+ }
+ printf("okay\n");
+
#define decl_insn(which) extern const unsigned char which[], which##_len[]
#define put_insn(which, insn) ".pushsection .test, \"ax\", @progbits\n" \
#which ": " insn "\n" \
@@ -182,7 +182,7 @@ static const opcode_desc_t opcode_table[
static const opcode_desc_t twobyte_table[256] = {
/* 0x00 - 0x07 */
- ModRM, ImplicitOps|ModRM, ModRM, ModRM,
+ ModRM, ImplicitOps|ModRM, DstReg|SrcMem16|ModRM, DstReg|SrcMem16|ModRM,
0, ImplicitOps, ImplicitOps, ImplicitOps,
/* 0x08 - 0x0F */
ImplicitOps, ImplicitOps, 0, ImplicitOps,
@@ -1353,6 +1353,11 @@ realmode_load_seg(
return rc;
}
+/*
+ * Passing in x86_seg_none means
+ * - suppress any exceptions other than #PF,
+ * - don't commit any state.
+ */
static int
protmode_load_seg(
enum x86_segment seg,
@@ -1391,7 +1396,7 @@ protmode_load_seg(
}
/* System segment descriptors must reside in the GDT. */
- if ( !is_x86_user_segment(seg) && (sel & 4) )
+ if ( is_x86_system_segment(seg) && (sel & 4) )
goto raise_exn;
switch ( rc = ops->read(sel_seg, sel & 0xfff8, &desc, sizeof(desc), ctxt) )
@@ -1408,14 +1413,11 @@ protmode_load_seg(
return rc;
}
- if ( !is_x86_user_segment(seg) )
- {
- /* System segments must have S flag == 0. */
- if ( desc.b & (1u << 12) )
- goto raise_exn;
- }
+ /* System segments must have S flag == 0. */
+ if ( is_x86_system_segment(seg) && (desc.b & (1u << 12)) )
+ goto raise_exn;
/* User segments must have S flag == 1. */
- else if ( !(desc.b & (1u << 12)) )
+ if ( is_x86_user_segment(seg) && !(desc.b & (1u << 12)) )
goto raise_exn;
dpl = (desc.b >> 13) & 3;
@@ -1477,10 +1479,17 @@ protmode_load_seg(
((dpl < cpl) || (dpl < rpl)) )
goto raise_exn;
break;
+ case x86_seg_none:
+ /* Non-conforming segment: check DPL against RPL and CPL. */
+ if ( ((desc.b & (0x1c << 8)) != (0x1c << 8)) &&
+ ((dpl < cpl) || (dpl < rpl)) )
+ return X86EMUL_EXCEPTION;
+ a_flag = 0;
+ break;
}
/* Segment present in memory? */
- if ( !(desc.b & (1 << 15)) )
+ if ( !(desc.b & (1 << 15)) && seg != x86_seg_none )
{
fault_type = seg != x86_seg_ss ? EXC_NP : EXC_SS;
goto raise_exn;
@@ -1488,7 +1497,7 @@ protmode_load_seg(
if ( !is_x86_user_segment(seg) )
{
- int lm = in_longmode(ctxt, ops);
+ int lm = (desc.b & (1u << 12)) ? 0 : in_longmode(ctxt, ops);
if ( lm < 0 )
return X86EMUL_UNHANDLEABLE;
@@ -1508,7 +1517,8 @@ protmode_load_seg(
return rc;
}
if ( (desc_hi.b & 0x00001f00) ||
- !is_canonical_address((uint64_t)desc_hi.a << 32) )
+ (seg != x86_seg_none &&
+ !is_canonical_address((uint64_t)desc_hi.a << 32)) )
goto raise_exn;
}
}
@@ -1551,7 +1561,8 @@ protmode_load_seg(
return X86EMUL_OKAY;
raise_exn:
- generate_exception(fault_type, sel & 0xfffc);
+ generate_exception_if(seg != x86_seg_none, fault_type, sel & 0xfffc);
+ rc = X86EMUL_EXCEPTION;
done:
return rc;
}
@@ -4399,6 +4410,29 @@ x86_emulate(
if ( (rc = load_seg(seg, src.val, 0, NULL, ctxt, ops)) != 0 )
goto done;
break;
+ case 4: /* verr / verw */
+ _regs.eflags &= ~EFLG_ZF;
+ switch ( rc = protmode_load_seg(x86_seg_none, src.val, false,
+ &sreg, ctxt, ops) )
+ {
+ case X86EMUL_OKAY:
+ if ( sreg.attr.fields.s &&
+ ((modrm_reg & 1) ? ((sreg.attr.fields.type & 0xa) == 0x2)
+ : ((sreg.attr.fields.type & 0xa) != 0x8)) )
+ _regs.eflags |= EFLG_ZF;
+ break;
+ case X86EMUL_EXCEPTION:
+ if ( ctxt->event_pending )
+ {
+ ASSERT(ctxt->event.vector == EXC_PF);
+ default:
+ goto done;
+ }
+ /* Instead of the exception, ZF remains cleared. */
+ rc = X86EMUL_OKAY;
+ break;
+ }
+ break;
default:
generate_exception_if(true, EXC_UD);
break;
@@ -4607,6 +4641,98 @@ x86_emulate(
break;
}
+ case X86EMUL_OPC(0x0f, 0x02): /* lar */
+ generate_exception_if(!in_protmode(ctxt, ops), EXC_UD);
+ _regs.eflags &= ~EFLG_ZF;
+ switch ( rc = protmode_load_seg(x86_seg_none, src.val, false, &sreg,
+ ctxt, ops) )
+ {
+ case X86EMUL_OKAY:
+ if ( !sreg.attr.fields.s )
+ {
+ switch ( sreg.attr.fields.type )
+ {
+ case 0x01: /* available 16-bit TSS */
+ case 0x03: /* busy 16-bit TSS */
+ case 0x04: /* 16-bit call gate */
+ case 0x05: /* 16/32-bit task gate */
+ if ( in_longmode(ctxt, ops) )
+ break;
+ /* fall through */
+ case 0x02: /* LDT */
+ case 0x09: /* available 32/64-bit TSS */
+ case 0x0b: /* busy 32/64-bit TSS */
+ case 0x0c: /* 32/64-bit call gate */
+ _regs.eflags |= EFLG_ZF;
+ break;
+ }
+ }
+ else
+ _regs.eflags |= EFLG_ZF;
+ break;
+ case X86EMUL_EXCEPTION:
+ if ( ctxt->event_pending )
+ {
+ ASSERT(ctxt->event.vector == EXC_PF);
+ default:
+ goto done;
+ }
+ /* Instead of the exception, ZF remains cleared. */
+ rc = X86EMUL_OKAY;
+ break;
+ }
+ if ( _regs.eflags & EFLG_ZF )
+ dst.val = ((sreg.attr.bytes & 0xff) << 8) |
+ ((sreg.limit >> (sreg.attr.fields.g ? 12 : 0)) &
+ 0xf0000) |
+ ((sreg.attr.bytes & 0xf00) << 12);
+ else
+ dst.type = OP_NONE;
+ break;
+
+ case X86EMUL_OPC(0x0f, 0x03): /* lsl */
+ generate_exception_if(!in_protmode(ctxt, ops), EXC_UD);
+ _regs.eflags &= ~EFLG_ZF;
+ switch ( rc = protmode_load_seg(x86_seg_none, src.val, false, &sreg,
+ ctxt, ops) )
+ {
+ case X86EMUL_OKAY:
+ if ( !sreg.attr.fields.s )
+ {
+ switch ( sreg.attr.fields.type )
+ {
+ case 0x01: /* available 16-bit TSS */
+ case 0x03: /* busy 16-bit TSS */
+ if ( in_longmode(ctxt, ops) )
+ break;
+ /* fall through */
+ case 0x02: /* LDT */
+ case 0x09: /* available 32/64-bit TSS */
+ case 0x0b: /* busy 32/64-bit TSS */
+ _regs.eflags |= EFLG_ZF;
+ break;
+ }
+ }
+ else
+ _regs.eflags |= EFLG_ZF;
+ break;
+ case X86EMUL_EXCEPTION:
+ if ( ctxt->event_pending )
+ {
+ ASSERT(ctxt->event.vector == EXC_PF);
+ default:
+ goto done;
+ }
+ /* Instead of the exception, ZF remains cleared. */
+ rc = X86EMUL_OKAY;
+ break;
+ }
+ if ( _regs.eflags & EFLG_ZF )
+ dst.val = sreg.limit;
+ else
+ dst.type = OP_NONE;
+ break;
+
case X86EMUL_OPC(0x0f, 0x05): /* syscall */ {
uint64_t msr_content;