Improvements:
- Emulate the instructions by executing them directly on the host CPU. This is easier and probably faster than doing it in software manually. - Decode SUB from Primary, CMP from Group1, TEST from Group3, and add associated tests. - Handle correctly the cases where an instruction that always implicitly reads the register operand is executed with the mem operand as source (eg: "orq (%rbx),%rax"). - Fix the MMU handling of 32bit-PAE. Under PAE CR3 is not page-aligned, so there are extra bits that are valid. With these changes in place I can boot Windows XP on Qemu+NVMM.
This commit is contained in:
parent
290f0433dd
commit
83ed0b5e52
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: libnvmm_x86.c,v 1.18 2019/02/01 06:49:58 maxv Exp $ */
|
||||
/* $NetBSD: libnvmm_x86.c,v 1.19 2019/02/07 10:58:45 maxv Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2018 The NetBSD Foundation, Inc.
|
||||
|
@ -111,6 +111,8 @@ nvmm_vcpu_dump(struct nvmm_machine *mach, nvmm_cpuid_t cpuid)
|
|||
#define pte32_l1idx(va) (((va) & PTE32_L1_MASK) >> PTE32_L1_SHIFT)
|
||||
#define pte32_l2idx(va) (((va) & PTE32_L2_MASK) >> PTE32_L2_SHIFT)
|
||||
|
||||
#define CR3_FRAME_32BIT PG_FRAME
|
||||
|
||||
typedef uint32_t pte_32bit_t;
|
||||
|
||||
static int
|
||||
|
@ -125,7 +127,7 @@ x86_gva_to_gpa_32bit(struct nvmm_machine *mach, uint64_t cr3,
|
|||
*prot = NVMM_PROT_ALL;
|
||||
|
||||
/* Parse L2. */
|
||||
L2gpa = (cr3 & PG_FRAME);
|
||||
L2gpa = (cr3 & CR3_FRAME_32BIT);
|
||||
if (nvmm_gpa_to_hva(mach, L2gpa, &L2hva) == -1)
|
||||
return -1;
|
||||
pdir = (pte_32bit_t *)L2hva;
|
||||
|
@ -181,6 +183,8 @@ x86_gva_to_gpa_32bit(struct nvmm_machine *mach, uint64_t cr3,
|
|||
#define pte32_pae_l2idx(va) (((va) & PTE32_PAE_L2_MASK) >> PTE32_PAE_L2_SHIFT)
|
||||
#define pte32_pae_l3idx(va) (((va) & PTE32_PAE_L3_MASK) >> PTE32_PAE_L3_SHIFT)
|
||||
|
||||
#define CR3_FRAME_32BIT_PAE __BITS(31, 5)
|
||||
|
||||
typedef uint64_t pte_32bit_pae_t;
|
||||
|
||||
static int
|
||||
|
@ -195,7 +199,7 @@ x86_gva_to_gpa_32bit_pae(struct nvmm_machine *mach, uint64_t cr3,
|
|||
*prot = NVMM_PROT_ALL;
|
||||
|
||||
/* Parse L3. */
|
||||
L3gpa = (cr3 & PG_FRAME);
|
||||
L3gpa = (cr3 & CR3_FRAME_32BIT_PAE);
|
||||
if (nvmm_gpa_to_hva(mach, L3gpa, &L3hva) == -1)
|
||||
return -1;
|
||||
pdir = (pte_32bit_pae_t *)L3hva;
|
||||
|
@ -272,6 +276,8 @@ x86_gva_to_gpa_32bit_pae(struct nvmm_machine *mach, uint64_t cr3,
|
|||
#define pte64_l3idx(va) (((va) & PTE64_L3_MASK) >> PTE64_L3_SHIFT)
|
||||
#define pte64_l4idx(va) (((va) & PTE64_L4_MASK) >> PTE64_L4_SHIFT)
|
||||
|
||||
#define CR3_FRAME_64BIT PG_FRAME
|
||||
|
||||
typedef uint64_t pte_64bit_t;
|
||||
|
||||
static inline bool
|
||||
|
@ -297,7 +303,7 @@ x86_gva_to_gpa_64bit(struct nvmm_machine *mach, uint64_t cr3,
|
|||
return -1;
|
||||
|
||||
/* Parse L4. */
|
||||
L4gpa = (cr3 & PG_FRAME);
|
||||
L4gpa = (cr3 & CR3_FRAME_64BIT);
|
||||
if (nvmm_gpa_to_hva(mach, L4gpa, &L4hva) == -1)
|
||||
return -1;
|
||||
pdir = (pte_64bit_t *)L4hva;
|
||||
|
@ -820,13 +826,68 @@ out:
|
|||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static void x86_emul_or(struct nvmm_mem *, void (*)(struct nvmm_mem *), uint64_t *);
|
||||
static void x86_emul_and(struct nvmm_mem *, void (*)(struct nvmm_mem *), uint64_t *);
|
||||
static void x86_emul_xor(struct nvmm_mem *, void (*)(struct nvmm_mem *), uint64_t *);
|
||||
static void x86_emul_mov(struct nvmm_mem *, void (*)(struct nvmm_mem *), uint64_t *);
|
||||
static void x86_emul_stos(struct nvmm_mem *, void (*)(struct nvmm_mem *), uint64_t *);
|
||||
static void x86_emul_lods(struct nvmm_mem *, void (*)(struct nvmm_mem *), uint64_t *);
|
||||
static void x86_emul_movs(struct nvmm_mem *, void (*)(struct nvmm_mem *), uint64_t *);
|
||||
struct x86_emul {
|
||||
bool read;
|
||||
bool notouch;
|
||||
void (*func)(struct nvmm_mem *, uint64_t *);
|
||||
};
|
||||
|
||||
static void x86_func_or(struct nvmm_mem *, uint64_t *);
|
||||
static void x86_func_and(struct nvmm_mem *, uint64_t *);
|
||||
static void x86_func_sub(struct nvmm_mem *, uint64_t *);
|
||||
static void x86_func_xor(struct nvmm_mem *, uint64_t *);
|
||||
static void x86_func_cmp(struct nvmm_mem *, uint64_t *);
|
||||
static void x86_func_test(struct nvmm_mem *, uint64_t *);
|
||||
static void x86_func_mov(struct nvmm_mem *, uint64_t *);
|
||||
static void x86_func_stos(struct nvmm_mem *, uint64_t *);
|
||||
static void x86_func_lods(struct nvmm_mem *, uint64_t *);
|
||||
static void x86_func_movs(struct nvmm_mem *, uint64_t *);
|
||||
|
||||
static const struct x86_emul x86_emul_or = {
|
||||
.read = true,
|
||||
.func = x86_func_or
|
||||
};
|
||||
|
||||
static const struct x86_emul x86_emul_and = {
|
||||
.read = true,
|
||||
.func = x86_func_and
|
||||
};
|
||||
|
||||
static const struct x86_emul x86_emul_sub = {
|
||||
.read = true,
|
||||
.func = x86_func_sub
|
||||
};
|
||||
|
||||
static const struct x86_emul x86_emul_xor = {
|
||||
.read = true,
|
||||
.func = x86_func_xor
|
||||
};
|
||||
|
||||
static const struct x86_emul x86_emul_cmp = {
|
||||
.notouch = true,
|
||||
.func = x86_func_cmp
|
||||
};
|
||||
|
||||
static const struct x86_emul x86_emul_test = {
|
||||
.notouch = true,
|
||||
.func = x86_func_test
|
||||
};
|
||||
|
||||
static const struct x86_emul x86_emul_mov = {
|
||||
.func = x86_func_mov
|
||||
};
|
||||
|
||||
static const struct x86_emul x86_emul_stos = {
|
||||
.func = x86_func_stos
|
||||
};
|
||||
|
||||
static const struct x86_emul x86_emul_lods = {
|
||||
.func = x86_func_lods
|
||||
};
|
||||
|
||||
static const struct x86_emul x86_emul_movs = {
|
||||
.func = x86_func_movs
|
||||
};
|
||||
|
||||
/* Legacy prefixes. */
|
||||
#define LEG_LOCK 0xF0
|
||||
|
@ -954,10 +1015,9 @@ struct x86_instr {
|
|||
|
||||
struct x86_store src;
|
||||
struct x86_store dst;
|
||||
|
||||
struct x86_store *strm;
|
||||
|
||||
void (*emul)(struct nvmm_mem *, void (*)(struct nvmm_mem *), uint64_t *);
|
||||
const struct x86_emul *emul;
|
||||
};
|
||||
|
||||
struct x86_decode_fsm {
|
||||
|
@ -985,14 +1045,15 @@ struct x86_opcode {
|
|||
int defsize;
|
||||
int allsize;
|
||||
bool group1;
|
||||
bool group3;
|
||||
bool group11;
|
||||
bool immediate;
|
||||
int flags;
|
||||
void (*emul)(struct nvmm_mem *, void (*)(struct nvmm_mem *), uint64_t *);
|
||||
const struct x86_emul *emul;
|
||||
};
|
||||
|
||||
struct x86_group_entry {
|
||||
void (*emul)(struct nvmm_mem *, void (*)(struct nvmm_mem *), uint64_t *);
|
||||
const struct x86_emul *emul;
|
||||
};
|
||||
|
||||
#define OPSIZE_BYTE 0x01
|
||||
|
@ -1005,19 +1066,37 @@ struct x86_group_entry {
|
|||
#define FLAG_ze 0x04
|
||||
|
||||
static const struct x86_group_entry group1[8] = {
|
||||
[1] = { .emul = x86_emul_or },
|
||||
[4] = { .emul = x86_emul_and },
|
||||
[6] = { .emul = x86_emul_xor }
|
||||
[1] = { .emul = &x86_emul_or },
|
||||
[4] = { .emul = &x86_emul_and },
|
||||
[6] = { .emul = &x86_emul_xor },
|
||||
[7] = { .emul = &x86_emul_cmp }
|
||||
};
|
||||
|
||||
static const struct x86_group_entry group3[8] = {
|
||||
[0] = { .emul = &x86_emul_test },
|
||||
[1] = { .emul = &x86_emul_test }
|
||||
};
|
||||
|
||||
static const struct x86_group_entry group11[8] = {
|
||||
[0] = { .emul = x86_emul_mov }
|
||||
[0] = { .emul = &x86_emul_mov }
|
||||
};
|
||||
|
||||
static const struct x86_opcode primary_opcode_table[] = {
|
||||
/*
|
||||
* Group1
|
||||
*/
|
||||
{
|
||||
/* Eb, Ib */
|
||||
.byte = 0x80,
|
||||
.regmodrm = true,
|
||||
.regtorm = true,
|
||||
.szoverride = false,
|
||||
.defsize = OPSIZE_BYTE,
|
||||
.allsize = -1,
|
||||
.group1 = true,
|
||||
.immediate = true,
|
||||
.emul = NULL /* group1 */
|
||||
},
|
||||
{
|
||||
/* Ev, Iz */
|
||||
.byte = 0x81,
|
||||
|
@ -1045,6 +1124,35 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.emul = NULL /* group1 */
|
||||
},
|
||||
|
||||
/*
|
||||
* Group3
|
||||
*/
|
||||
{
|
||||
/* Eb, Ib */
|
||||
.byte = 0xF6,
|
||||
.regmodrm = true,
|
||||
.regtorm = true,
|
||||
.szoverride = false,
|
||||
.defsize = OPSIZE_BYTE,
|
||||
.allsize = -1,
|
||||
.group3 = true,
|
||||
.immediate = true,
|
||||
.emul = NULL /* group3 */
|
||||
},
|
||||
{
|
||||
/* Ev, Iz */
|
||||
.byte = 0xF7,
|
||||
.regmodrm = true,
|
||||
.regtorm = true,
|
||||
.szoverride = true,
|
||||
.defsize = -1,
|
||||
.allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD,
|
||||
.group3 = true,
|
||||
.immediate = true,
|
||||
.flags = FLAG_immz,
|
||||
.emul = NULL /* group3 */
|
||||
},
|
||||
|
||||
/*
|
||||
* Group11
|
||||
*/
|
||||
|
@ -1085,7 +1193,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = false,
|
||||
.defsize = OPSIZE_BYTE,
|
||||
.allsize = -1,
|
||||
.emul = x86_emul_or
|
||||
.emul = &x86_emul_or
|
||||
},
|
||||
{
|
||||
/* Ev, Gv */
|
||||
|
@ -1095,7 +1203,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = true,
|
||||
.defsize = -1,
|
||||
.allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD,
|
||||
.emul = x86_emul_or
|
||||
.emul = &x86_emul_or
|
||||
},
|
||||
{
|
||||
/* Gb, Eb */
|
||||
|
@ -1105,7 +1213,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = false,
|
||||
.defsize = OPSIZE_BYTE,
|
||||
.allsize = -1,
|
||||
.emul = x86_emul_or
|
||||
.emul = &x86_emul_or
|
||||
},
|
||||
{
|
||||
/* Gv, Ev */
|
||||
|
@ -1115,7 +1223,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = true,
|
||||
.defsize = -1,
|
||||
.allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD,
|
||||
.emul = x86_emul_or
|
||||
.emul = &x86_emul_or
|
||||
},
|
||||
|
||||
/*
|
||||
|
@ -1129,7 +1237,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = false,
|
||||
.defsize = OPSIZE_BYTE,
|
||||
.allsize = -1,
|
||||
.emul = x86_emul_and
|
||||
.emul = &x86_emul_and
|
||||
},
|
||||
{
|
||||
/* Ev, Gv */
|
||||
|
@ -1139,7 +1247,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = true,
|
||||
.defsize = -1,
|
||||
.allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD,
|
||||
.emul = x86_emul_and
|
||||
.emul = &x86_emul_and
|
||||
},
|
||||
{
|
||||
/* Gb, Eb */
|
||||
|
@ -1149,7 +1257,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = false,
|
||||
.defsize = OPSIZE_BYTE,
|
||||
.allsize = -1,
|
||||
.emul = x86_emul_and
|
||||
.emul = &x86_emul_and
|
||||
},
|
||||
{
|
||||
/* Gv, Ev */
|
||||
|
@ -1159,7 +1267,51 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = true,
|
||||
.defsize = -1,
|
||||
.allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD,
|
||||
.emul = x86_emul_and
|
||||
.emul = &x86_emul_and
|
||||
},
|
||||
|
||||
/*
|
||||
* SUB
|
||||
*/
|
||||
{
|
||||
/* Eb, Gb */
|
||||
.byte = 0x28,
|
||||
.regmodrm = true,
|
||||
.regtorm = true,
|
||||
.szoverride = false,
|
||||
.defsize = OPSIZE_BYTE,
|
||||
.allsize = -1,
|
||||
.emul = &x86_emul_sub
|
||||
},
|
||||
{
|
||||
/* Ev, Gv */
|
||||
.byte = 0x29,
|
||||
.regmodrm = true,
|
||||
.regtorm = true,
|
||||
.szoverride = true,
|
||||
.defsize = -1,
|
||||
.allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD,
|
||||
.emul = &x86_emul_sub
|
||||
},
|
||||
{
|
||||
/* Gb, Eb */
|
||||
.byte = 0x2A,
|
||||
.regmodrm = true,
|
||||
.regtorm = false,
|
||||
.szoverride = false,
|
||||
.defsize = OPSIZE_BYTE,
|
||||
.allsize = -1,
|
||||
.emul = &x86_emul_sub
|
||||
},
|
||||
{
|
||||
/* Gv, Ev */
|
||||
.byte = 0x2B,
|
||||
.regmodrm = true,
|
||||
.regtorm = false,
|
||||
.szoverride = true,
|
||||
.defsize = -1,
|
||||
.allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD,
|
||||
.emul = &x86_emul_sub
|
||||
},
|
||||
|
||||
/*
|
||||
|
@ -1173,7 +1325,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = false,
|
||||
.defsize = OPSIZE_BYTE,
|
||||
.allsize = -1,
|
||||
.emul = x86_emul_xor
|
||||
.emul = &x86_emul_xor
|
||||
},
|
||||
{
|
||||
/* Ev, Gv */
|
||||
|
@ -1183,7 +1335,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = true,
|
||||
.defsize = -1,
|
||||
.allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD,
|
||||
.emul = x86_emul_xor
|
||||
.emul = &x86_emul_xor
|
||||
},
|
||||
{
|
||||
/* Gb, Eb */
|
||||
|
@ -1193,7 +1345,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = false,
|
||||
.defsize = OPSIZE_BYTE,
|
||||
.allsize = -1,
|
||||
.emul = x86_emul_xor
|
||||
.emul = &x86_emul_xor
|
||||
},
|
||||
{
|
||||
/* Gv, Ev */
|
||||
|
@ -1203,7 +1355,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = true,
|
||||
.defsize = -1,
|
||||
.allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD,
|
||||
.emul = x86_emul_xor
|
||||
.emul = &x86_emul_xor
|
||||
},
|
||||
|
||||
/*
|
||||
|
@ -1217,7 +1369,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = false,
|
||||
.defsize = OPSIZE_BYTE,
|
||||
.allsize = -1,
|
||||
.emul = x86_emul_mov
|
||||
.emul = &x86_emul_mov
|
||||
},
|
||||
{
|
||||
/* Ev, Gv */
|
||||
|
@ -1227,7 +1379,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = true,
|
||||
.defsize = -1,
|
||||
.allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD,
|
||||
.emul = x86_emul_mov
|
||||
.emul = &x86_emul_mov
|
||||
},
|
||||
{
|
||||
/* Gb, Eb */
|
||||
|
@ -1237,7 +1389,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = false,
|
||||
.defsize = OPSIZE_BYTE,
|
||||
.allsize = -1,
|
||||
.emul = x86_emul_mov
|
||||
.emul = &x86_emul_mov
|
||||
},
|
||||
{
|
||||
/* Gv, Ev */
|
||||
|
@ -1247,7 +1399,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = true,
|
||||
.defsize = -1,
|
||||
.allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD,
|
||||
.emul = x86_emul_mov
|
||||
.emul = &x86_emul_mov
|
||||
},
|
||||
{
|
||||
/* AL, Ob */
|
||||
|
@ -1257,7 +1409,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = false,
|
||||
.defsize = OPSIZE_BYTE,
|
||||
.allsize = -1,
|
||||
.emul = x86_emul_mov
|
||||
.emul = &x86_emul_mov
|
||||
},
|
||||
{
|
||||
/* rAX, Ov */
|
||||
|
@ -1267,7 +1419,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = true,
|
||||
.defsize = -1,
|
||||
.allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD,
|
||||
.emul = x86_emul_mov
|
||||
.emul = &x86_emul_mov
|
||||
},
|
||||
{
|
||||
/* Ob, AL */
|
||||
|
@ -1277,7 +1429,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = false,
|
||||
.defsize = OPSIZE_BYTE,
|
||||
.allsize = -1,
|
||||
.emul = x86_emul_mov
|
||||
.emul = &x86_emul_mov
|
||||
},
|
||||
{
|
||||
/* Ov, rAX */
|
||||
|
@ -1287,7 +1439,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = true,
|
||||
.defsize = -1,
|
||||
.allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD,
|
||||
.emul = x86_emul_mov
|
||||
.emul = &x86_emul_mov
|
||||
},
|
||||
|
||||
/*
|
||||
|
@ -1300,7 +1452,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = false,
|
||||
.defsize = OPSIZE_BYTE,
|
||||
.allsize = -1,
|
||||
.emul = x86_emul_movs
|
||||
.emul = &x86_emul_movs
|
||||
},
|
||||
{
|
||||
/* Yv, Xv */
|
||||
|
@ -1309,7 +1461,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = true,
|
||||
.defsize = -1,
|
||||
.allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD,
|
||||
.emul = x86_emul_movs
|
||||
.emul = &x86_emul_movs
|
||||
},
|
||||
|
||||
/*
|
||||
|
@ -1322,7 +1474,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = false,
|
||||
.defsize = OPSIZE_BYTE,
|
||||
.allsize = -1,
|
||||
.emul = x86_emul_stos
|
||||
.emul = &x86_emul_stos
|
||||
},
|
||||
{
|
||||
/* Yv, rAX */
|
||||
|
@ -1331,7 +1483,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = true,
|
||||
.defsize = -1,
|
||||
.allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD,
|
||||
.emul = x86_emul_stos
|
||||
.emul = &x86_emul_stos
|
||||
},
|
||||
|
||||
/*
|
||||
|
@ -1344,7 +1496,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = false,
|
||||
.defsize = OPSIZE_BYTE,
|
||||
.allsize = -1,
|
||||
.emul = x86_emul_lods
|
||||
.emul = &x86_emul_lods
|
||||
},
|
||||
{
|
||||
/* rAX, Xv */
|
||||
|
@ -1353,7 +1505,7 @@ static const struct x86_opcode primary_opcode_table[] = {
|
|||
.szoverride = true,
|
||||
.defsize = -1,
|
||||
.allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD,
|
||||
.emul = x86_emul_lods
|
||||
.emul = &x86_emul_lods
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1370,7 +1522,7 @@ static const struct x86_opcode secondary_opcode_table[] = {
|
|||
.defsize = OPSIZE_BYTE,
|
||||
.allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD,
|
||||
.flags = FLAG_ze,
|
||||
.emul = x86_emul_mov
|
||||
.emul = &x86_emul_mov
|
||||
},
|
||||
{
|
||||
/* Gv, Ew */
|
||||
|
@ -1381,7 +1533,7 @@ static const struct x86_opcode secondary_opcode_table[] = {
|
|||
.defsize = OPSIZE_WORD,
|
||||
.allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD,
|
||||
.flags = FLAG_ze,
|
||||
.emul = x86_emul_mov
|
||||
.emul = &x86_emul_mov
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -2064,6 +2216,11 @@ node_regmodrm(struct x86_decode_fsm *fsm, struct x86_instr *instr)
|
|||
return -1;
|
||||
}
|
||||
instr->emul = group1[instr->regmodrm.reg].emul;
|
||||
} else if (opcode->group3) {
|
||||
if (group3[instr->regmodrm.reg].emul == NULL) {
|
||||
return -1;
|
||||
}
|
||||
instr->emul = group3[instr->regmodrm.reg].emul;
|
||||
} else if (opcode->group11) {
|
||||
if (group11[instr->regmodrm.reg].emul == NULL) {
|
||||
return -1;
|
||||
|
@ -2425,150 +2582,270 @@ x86_decode(uint8_t *inst_bytes, size_t inst_len, struct x86_instr *instr,
|
|||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static inline uint8_t
|
||||
compute_parity(uint8_t *data)
|
||||
{
|
||||
uint64_t *ptr = (uint64_t *)data;
|
||||
uint64_t val = *ptr;
|
||||
|
||||
val ^= val >> 32;
|
||||
val ^= val >> 16;
|
||||
val ^= val >> 8;
|
||||
val ^= val >> 4;
|
||||
val ^= val >> 2;
|
||||
val ^= val >> 1;
|
||||
return (~val) & 1;
|
||||
#define EXEC_INSTR(sz, instr) \
|
||||
static uint##sz##_t \
|
||||
exec_##instr####sz(uint##sz##_t op1, uint##sz##_t op2, uint64_t *rflags)\
|
||||
{ \
|
||||
uint##sz##_t res; \
|
||||
__asm __volatile ( \
|
||||
#instr " %2, %3;" \
|
||||
"mov %3, %1;" \
|
||||
"pushfq;" \
|
||||
"popq %0" \
|
||||
: "=r" (*rflags), "=r" (res) \
|
||||
: "r" (op1), "r" (op2)); \
|
||||
return res; \
|
||||
}
|
||||
|
||||
#define EXEC_DISPATCHER(instr) \
|
||||
static uint64_t \
|
||||
exec_##instr(uint64_t op1, uint64_t op2, uint64_t *rflags, size_t opsize) \
|
||||
{ \
|
||||
switch (opsize) { \
|
||||
case 1: \
|
||||
return exec_##instr##8(op1, op2, rflags); \
|
||||
case 2: \
|
||||
return exec_##instr##16(op1, op2, rflags); \
|
||||
case 4: \
|
||||
return exec_##instr##32(op1, op2, rflags); \
|
||||
default: \
|
||||
return exec_##instr##64(op1, op2, rflags); \
|
||||
} \
|
||||
}
|
||||
|
||||
/* SUB: ret = op1 - op2 */
|
||||
#define PSL_SUB_MASK (PSL_V|PSL_C|PSL_Z|PSL_N|PSL_PF|PSL_AF)
|
||||
EXEC_INSTR(8, sub)
|
||||
EXEC_INSTR(16, sub)
|
||||
EXEC_INSTR(32, sub)
|
||||
EXEC_INSTR(64, sub)
|
||||
EXEC_DISPATCHER(sub)
|
||||
|
||||
/* OR: ret = op1 | op2 */
|
||||
#define PSL_OR_MASK (PSL_V|PSL_C|PSL_Z|PSL_N|PSL_PF)
|
||||
EXEC_INSTR(8, or)
|
||||
EXEC_INSTR(16, or)
|
||||
EXEC_INSTR(32, or)
|
||||
EXEC_INSTR(64, or)
|
||||
EXEC_DISPATCHER(or)
|
||||
|
||||
/* AND: ret = op1 & op2 */
|
||||
#define PSL_AND_MASK (PSL_V|PSL_C|PSL_Z|PSL_N|PSL_PF)
|
||||
EXEC_INSTR(8, and)
|
||||
EXEC_INSTR(16, and)
|
||||
EXEC_INSTR(32, and)
|
||||
EXEC_INSTR(64, and)
|
||||
EXEC_DISPATCHER(and)
|
||||
|
||||
/* XOR: ret = op1 ^ op2 */
|
||||
#define PSL_XOR_MASK (PSL_V|PSL_C|PSL_Z|PSL_N|PSL_PF)
|
||||
EXEC_INSTR(8, xor)
|
||||
EXEC_INSTR(16, xor)
|
||||
EXEC_INSTR(32, xor)
|
||||
EXEC_INSTR(64, xor)
|
||||
EXEC_DISPATCHER(xor)
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Emulation functions. We don't care about the order of the operands, except
|
||||
* for SUB, CMP and TEST. For these ones we look at mem->write todetermine who
|
||||
* is op1 and who is op2.
|
||||
*/
|
||||
|
||||
static void
|
||||
x86_emul_or(struct nvmm_mem *mem, void (*cb)(struct nvmm_mem *),
|
||||
uint64_t *gprs)
|
||||
x86_func_or(struct nvmm_mem *mem, uint64_t *gprs)
|
||||
{
|
||||
uint64_t *retval = (uint64_t *)mem->data;
|
||||
const bool write = mem->write;
|
||||
uint64_t fl = gprs[NVMM_X64_GPR_RFLAGS];
|
||||
uint8_t data[8];
|
||||
size_t i;
|
||||
uint64_t *op1, op2, fl, ret;
|
||||
|
||||
fl &= ~(PSL_V|PSL_C|PSL_Z|PSL_N|PSL_PF);
|
||||
op1 = (uint64_t *)mem->data;
|
||||
op2 = 0;
|
||||
|
||||
memcpy(data, mem->data, sizeof(data));
|
||||
|
||||
/* Fetch the value to be OR'ed. */
|
||||
/* Fetch the value to be OR'ed (op2). */
|
||||
mem->data = (uint8_t *)&op2;
|
||||
mem->write = false;
|
||||
(*cb)(mem);
|
||||
(*__callbacks.mem)(mem);
|
||||
|
||||
/* Perform the OR. */
|
||||
for (i = 0; i < mem->size; i++) {
|
||||
mem->data[i] |= data[i];
|
||||
if (mem->data[i] != 0)
|
||||
fl |= PSL_Z;
|
||||
}
|
||||
if (mem->data[mem->size-1] & __BIT(7))
|
||||
fl |= PSL_N;
|
||||
if (compute_parity(mem->data))
|
||||
fl |= PSL_PF;
|
||||
ret = exec_or(*op1, op2, &fl, mem->size);
|
||||
|
||||
if (write) {
|
||||
/* Write back the result. */
|
||||
mem->data = (uint8_t *)&ret;
|
||||
mem->write = true;
|
||||
(*cb)(mem);
|
||||
(*__callbacks.mem)(mem);
|
||||
} else {
|
||||
/* Return data to the caller. */
|
||||
*retval = ret;
|
||||
}
|
||||
|
||||
gprs[NVMM_X64_GPR_RFLAGS] = fl;
|
||||
gprs[NVMM_X64_GPR_RFLAGS] &= ~PSL_OR_MASK;
|
||||
gprs[NVMM_X64_GPR_RFLAGS] |= (fl & PSL_OR_MASK);
|
||||
}
|
||||
|
||||
static void
|
||||
x86_emul_and(struct nvmm_mem *mem, void (*cb)(struct nvmm_mem *),
|
||||
uint64_t *gprs)
|
||||
x86_func_and(struct nvmm_mem *mem, uint64_t *gprs)
|
||||
{
|
||||
uint64_t *retval = (uint64_t *)mem->data;
|
||||
const bool write = mem->write;
|
||||
uint64_t fl = gprs[NVMM_X64_GPR_RFLAGS];
|
||||
uint8_t data[8];
|
||||
size_t i;
|
||||
uint64_t *op1, op2, fl, ret;
|
||||
|
||||
fl &= ~(PSL_V|PSL_C|PSL_Z|PSL_N|PSL_PF);
|
||||
op1 = (uint64_t *)mem->data;
|
||||
op2 = 0;
|
||||
|
||||
memcpy(data, mem->data, sizeof(data));
|
||||
|
||||
/* Fetch the value to be AND'ed. */
|
||||
/* Fetch the value to be AND'ed (op2). */
|
||||
mem->data = (uint8_t *)&op2;
|
||||
mem->write = false;
|
||||
(*cb)(mem);
|
||||
(*__callbacks.mem)(mem);
|
||||
|
||||
/* Perform the AND. */
|
||||
for (i = 0; i < mem->size; i++) {
|
||||
mem->data[i] &= data[i];
|
||||
if (mem->data[i] != 0)
|
||||
fl |= PSL_Z;
|
||||
}
|
||||
if (mem->data[mem->size-1] & __BIT(7))
|
||||
fl |= PSL_N;
|
||||
if (compute_parity(mem->data))
|
||||
fl |= PSL_PF;
|
||||
ret = exec_and(*op1, op2, &fl, mem->size);
|
||||
|
||||
if (write) {
|
||||
/* Write back the result. */
|
||||
mem->data = (uint8_t *)&ret;
|
||||
mem->write = true;
|
||||
(*cb)(mem);
|
||||
(*__callbacks.mem)(mem);
|
||||
} else {
|
||||
/* Return data to the caller. */
|
||||
*retval = ret;
|
||||
}
|
||||
|
||||
gprs[NVMM_X64_GPR_RFLAGS] = fl;
|
||||
gprs[NVMM_X64_GPR_RFLAGS] &= ~PSL_AND_MASK;
|
||||
gprs[NVMM_X64_GPR_RFLAGS] |= (fl & PSL_AND_MASK);
|
||||
}
|
||||
|
||||
static void
|
||||
x86_emul_xor(struct nvmm_mem *mem, void (*cb)(struct nvmm_mem *),
|
||||
uint64_t *gprs)
|
||||
x86_func_sub(struct nvmm_mem *mem, uint64_t *gprs)
|
||||
{
|
||||
uint64_t *retval = (uint64_t *)mem->data;
|
||||
const bool write = mem->write;
|
||||
uint64_t fl = gprs[NVMM_X64_GPR_RFLAGS];
|
||||
uint8_t data[8];
|
||||
size_t i;
|
||||
uint64_t *op1, *op2, fl, ret;
|
||||
uint64_t tmp;
|
||||
bool memop1;
|
||||
|
||||
fl &= ~(PSL_V|PSL_C|PSL_Z|PSL_N|PSL_PF);
|
||||
memop1 = !mem->write;
|
||||
op1 = memop1 ? &tmp : (uint64_t *)mem->data;
|
||||
op2 = memop1 ? (uint64_t *)mem->data : &tmp;
|
||||
|
||||
memcpy(data, mem->data, sizeof(data));
|
||||
|
||||
/* Fetch the value to be XOR'ed. */
|
||||
/* Fetch the value to be SUB'ed (op1 or op2). */
|
||||
mem->data = (uint8_t *)&tmp;
|
||||
mem->write = false;
|
||||
(*cb)(mem);
|
||||
(*__callbacks.mem)(mem);
|
||||
|
||||
/* Perform the SUB. */
|
||||
ret = exec_sub(*op1, *op2, &fl, mem->size);
|
||||
|
||||
if (write) {
|
||||
/* Write back the result. */
|
||||
mem->data = (uint8_t *)&ret;
|
||||
mem->write = true;
|
||||
(*__callbacks.mem)(mem);
|
||||
} else {
|
||||
/* Return data to the caller. */
|
||||
*retval = ret;
|
||||
}
|
||||
|
||||
gprs[NVMM_X64_GPR_RFLAGS] &= ~PSL_SUB_MASK;
|
||||
gprs[NVMM_X64_GPR_RFLAGS] |= (fl & PSL_SUB_MASK);
|
||||
}
|
||||
|
||||
static void
|
||||
x86_func_xor(struct nvmm_mem *mem, uint64_t *gprs)
|
||||
{
|
||||
uint64_t *retval = (uint64_t *)mem->data;
|
||||
const bool write = mem->write;
|
||||
uint64_t *op1, op2, fl, ret;
|
||||
|
||||
op1 = (uint64_t *)mem->data;
|
||||
op2 = 0;
|
||||
|
||||
/* Fetch the value to be XOR'ed (op2). */
|
||||
mem->data = (uint8_t *)&op2;
|
||||
mem->write = false;
|
||||
(*__callbacks.mem)(mem);
|
||||
|
||||
/* Perform the XOR. */
|
||||
for (i = 0; i < mem->size; i++) {
|
||||
mem->data[i] ^= data[i];
|
||||
if (mem->data[i] != 0)
|
||||
fl |= PSL_Z;
|
||||
}
|
||||
if (mem->data[mem->size-1] & __BIT(7))
|
||||
fl |= PSL_N;
|
||||
if (compute_parity(mem->data))
|
||||
fl |= PSL_PF;
|
||||
ret = exec_xor(*op1, op2, &fl, mem->size);
|
||||
|
||||
if (write) {
|
||||
/* Write back the result. */
|
||||
mem->data = (uint8_t *)&ret;
|
||||
mem->write = true;
|
||||
(*cb)(mem);
|
||||
(*__callbacks.mem)(mem);
|
||||
} else {
|
||||
/* Return data to the caller. */
|
||||
*retval = ret;
|
||||
}
|
||||
|
||||
gprs[NVMM_X64_GPR_RFLAGS] = fl;
|
||||
gprs[NVMM_X64_GPR_RFLAGS] &= ~PSL_XOR_MASK;
|
||||
gprs[NVMM_X64_GPR_RFLAGS] |= (fl & PSL_XOR_MASK);
|
||||
}
|
||||
|
||||
static void
|
||||
x86_emul_mov(struct nvmm_mem *mem, void (*cb)(struct nvmm_mem *),
|
||||
uint64_t *gprs)
|
||||
x86_func_cmp(struct nvmm_mem *mem, uint64_t *gprs)
|
||||
{
|
||||
uint64_t *op1, *op2, fl;
|
||||
uint64_t tmp;
|
||||
bool memop1;
|
||||
|
||||
memop1 = !mem->write;
|
||||
op1 = memop1 ? &tmp : (uint64_t *)mem->data;
|
||||
op2 = memop1 ? (uint64_t *)mem->data : &tmp;
|
||||
|
||||
/* Fetch the value to be CMP'ed (op1 or op2). */
|
||||
mem->data = (uint8_t *)&tmp;
|
||||
mem->write = false;
|
||||
(*__callbacks.mem)(mem);
|
||||
|
||||
/* Perform the CMP. */
|
||||
exec_sub(*op1, *op2, &fl, mem->size);
|
||||
|
||||
gprs[NVMM_X64_GPR_RFLAGS] &= ~PSL_SUB_MASK;
|
||||
gprs[NVMM_X64_GPR_RFLAGS] |= (fl & PSL_SUB_MASK);
|
||||
}
|
||||
|
||||
static void
|
||||
x86_func_test(struct nvmm_mem *mem, uint64_t *gprs)
|
||||
{
|
||||
uint64_t *op1, *op2, fl;
|
||||
uint64_t tmp;
|
||||
bool memop1;
|
||||
|
||||
memop1 = !mem->write;
|
||||
op1 = memop1 ? &tmp : (uint64_t *)mem->data;
|
||||
op2 = memop1 ? (uint64_t *)mem->data : &tmp;
|
||||
|
||||
/* Fetch the value to be TEST'ed (op1 or op2). */
|
||||
mem->data = (uint8_t *)&tmp;
|
||||
mem->write = false;
|
||||
(*__callbacks.mem)(mem);
|
||||
|
||||
/* Perform the TEST. */
|
||||
exec_and(*op1, *op2, &fl, mem->size);
|
||||
|
||||
gprs[NVMM_X64_GPR_RFLAGS] &= ~PSL_AND_MASK;
|
||||
gprs[NVMM_X64_GPR_RFLAGS] |= (fl & PSL_AND_MASK);
|
||||
}
|
||||
|
||||
static void
|
||||
x86_func_mov(struct nvmm_mem *mem, uint64_t *gprs)
|
||||
{
|
||||
/*
|
||||
* Nothing special, just move without emulation.
|
||||
*/
|
||||
(*cb)(mem);
|
||||
(*__callbacks.mem)(mem);
|
||||
}
|
||||
|
||||
static void
|
||||
x86_emul_stos(struct nvmm_mem *mem, void (*cb)(struct nvmm_mem *),
|
||||
uint64_t *gprs)
|
||||
x86_func_stos(struct nvmm_mem *mem, uint64_t *gprs)
|
||||
{
|
||||
/*
|
||||
* Just move, and update RDI.
|
||||
*/
|
||||
(*cb)(mem);
|
||||
(*__callbacks.mem)(mem);
|
||||
|
||||
if (gprs[NVMM_X64_GPR_RFLAGS] & PSL_D) {
|
||||
gprs[NVMM_X64_GPR_RDI] -= mem->size;
|
||||
|
@ -2578,13 +2855,12 @@ x86_emul_stos(struct nvmm_mem *mem, void (*cb)(struct nvmm_mem *),
|
|||
}
|
||||
|
||||
static void
|
||||
x86_emul_lods(struct nvmm_mem *mem, void (*cb)(struct nvmm_mem *),
|
||||
uint64_t *gprs)
|
||||
x86_func_lods(struct nvmm_mem *mem, uint64_t *gprs)
|
||||
{
|
||||
/*
|
||||
* Just move, and update RSI.
|
||||
*/
|
||||
(*cb)(mem);
|
||||
(*__callbacks.mem)(mem);
|
||||
|
||||
if (gprs[NVMM_X64_GPR_RFLAGS] & PSL_D) {
|
||||
gprs[NVMM_X64_GPR_RSI] -= mem->size;
|
||||
|
@ -2594,8 +2870,7 @@ x86_emul_lods(struct nvmm_mem *mem, void (*cb)(struct nvmm_mem *),
|
|||
}
|
||||
|
||||
static void
|
||||
x86_emul_movs(struct nvmm_mem *mem, void (*cb)(struct nvmm_mem *),
|
||||
uint64_t *gprs)
|
||||
x86_func_movs(struct nvmm_mem *mem, uint64_t *gprs)
|
||||
{
|
||||
/*
|
||||
* Special instruction: double memory operand. Don't call the cb,
|
||||
|
@ -2795,7 +3070,7 @@ assist_mem_double(struct nvmm_machine *mach, struct nvmm_x64_state *state,
|
|||
return -1;
|
||||
|
||||
mem.size = size;
|
||||
(*instr->emul)(&mem, NULL, state->gprs);
|
||||
(*instr->emul->func)(&mem, state->gprs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2860,15 +3135,25 @@ assist_mem_single(struct nvmm_machine *mach, struct nvmm_x64_state *state,
|
|||
default:
|
||||
DISASSEMBLER_BUG();
|
||||
}
|
||||
}
|
||||
|
||||
(*instr->emul)(&mem, __callbacks.mem, state->gprs);
|
||||
|
||||
if (!mem.write) {
|
||||
} else if (instr->emul->read) {
|
||||
if (instr->dst.type != STORE_REG) {
|
||||
DISASSEMBLER_BUG();
|
||||
}
|
||||
memcpy(&val, mem.data, sizeof(uint64_t));
|
||||
if (instr->dst.disp.type != DISP_NONE) {
|
||||
DISASSEMBLER_BUG();
|
||||
}
|
||||
val = state->gprs[instr->dst.u.reg->num];
|
||||
val = __SHIFTOUT(val, instr->dst.u.reg->mask);
|
||||
memcpy(mem.data, &val, mem.size);
|
||||
}
|
||||
|
||||
(*instr->emul->func)(&mem, state->gprs);
|
||||
|
||||
if (!instr->emul->notouch && !mem.write) {
|
||||
if (instr->dst.type != STORE_REG) {
|
||||
DISASSEMBLER_BUG();
|
||||
}
|
||||
memcpy(&val, membuf, sizeof(uint64_t));
|
||||
val = __SHIFTIN(val, instr->dst.u.reg->mask);
|
||||
state->gprs[instr->dst.u.reg->num] &= ~instr->dst.u.reg->mask;
|
||||
state->gprs[instr->dst.u.reg->num] |= val;
|
||||
|
|
|
@ -292,10 +292,13 @@ extern uint8_t test8_begin, test8_end;
|
|||
extern uint8_t test9_begin, test9_end;
|
||||
extern uint8_t test10_begin, test10_end;
|
||||
extern uint8_t test11_begin, test11_end;
|
||||
extern uint8_t test12_begin, test12_end;
|
||||
extern uint8_t test13_begin, test13_end;
|
||||
extern uint8_t test14_begin, test14_end;
|
||||
|
||||
static const struct test tests[] = {
|
||||
{ "test1 - MOV", &test1_begin, &test1_end, 0x3004 },
|
||||
{ "test2 - OR", &test2_begin, &test2_end, 0x14FF },
|
||||
{ "test2 - OR", &test2_begin, &test2_end, 0x16FF },
|
||||
{ "test3 - AND", &test3_begin, &test3_end, 0x1FC0 },
|
||||
{ "test4 - XOR", &test4_begin, &test4_end, 0x10CF },
|
||||
{ "test5 - Address Sizes", &test5_begin, &test5_end, 0x1F00 },
|
||||
|
@ -305,6 +308,9 @@ static const struct test tests[] = {
|
|||
{ "test9 - MOVS", &test9_begin, &test9_end, 0x12345678 },
|
||||
{ "test10 - MOVZXB", &test10_begin, &test10_end, 0x00000078 },
|
||||
{ "test11 - MOVZXW", &test11_begin, &test11_end, 0x00005678 },
|
||||
{ "test12 - CMP", &test12_begin, &test12_end, 0x00000001 },
|
||||
{ "test13 - SUB", &test13_begin, &test13_end, 0x0000000F0000A0FF },
|
||||
{ "test14 - TEST", &test14_begin, &test14_end, 0x00000001 },
|
||||
{ NULL, NULL, NULL, -1 }
|
||||
};
|
||||
|
||||
|
|
|
@ -38,6 +38,9 @@
|
|||
.globl test9_begin, test9_end
|
||||
.globl test10_begin, test10_end
|
||||
.globl test11_begin, test11_end
|
||||
.globl test12_begin, test12_end
|
||||
.globl test13_begin, test13_end
|
||||
.globl test14_begin, test14_end
|
||||
.text
|
||||
.code64
|
||||
|
||||
|
@ -74,6 +77,10 @@ test2_begin:
|
|||
movq $0x0400,%rcx
|
||||
orw %cx,(%rax)
|
||||
|
||||
movq $0x0200,%rcx
|
||||
orq (%rax),%rcx
|
||||
movq %rcx,(%rax)
|
||||
|
||||
TEST_END
|
||||
test2_end:
|
||||
|
||||
|
@ -202,3 +209,85 @@ test11_begin:
|
|||
|
||||
TEST_END
|
||||
test11_end:
|
||||
|
||||
.align 64
|
||||
test12_begin:
|
||||
movq $0x1000,%rax
|
||||
movq $0xFFFFFFFFF2345678,(%rax)
|
||||
|
||||
cmpb $0x78,(%rax)
|
||||
jne .L12_failure
|
||||
cmpb $0x77,(%rax)
|
||||
jl .L12_failure
|
||||
cmpb $0x79,(%rax)
|
||||
jg .L12_failure
|
||||
|
||||
cmpw $0x5678,(%rax)
|
||||
jne .L12_failure
|
||||
cmpw $0x5677,(%rax)
|
||||
jl .L12_failure
|
||||
cmpw $0x5679,(%rax)
|
||||
jg .L12_failure
|
||||
|
||||
cmpl $0xF2345678,(%rax)
|
||||
jne .L12_failure
|
||||
cmpl $0xF2345677,(%rax)
|
||||
jl .L12_failure
|
||||
cmpl $0xF2345679,(%rax)
|
||||
jg .L12_failure
|
||||
|
||||
cmpq $0xFFFFFFFFF2345678,(%rax)
|
||||
jne .L12_failure
|
||||
cmpq $0xFFFFFFFFF2345677,(%rax)
|
||||
jl .L12_failure
|
||||
cmpq $0xFFFFFFFFF2345679,(%rax)
|
||||
jg .L12_failure
|
||||
|
||||
.L12_success:
|
||||
movq $1,(%rax)
|
||||
TEST_END
|
||||
.L12_failure:
|
||||
movq $0,(%rax)
|
||||
TEST_END
|
||||
test12_end:
|
||||
|
||||
.align 64
|
||||
test13_begin:
|
||||
movq $0x1000,%rax
|
||||
movq $0x000000001000A0FF,(%rax)
|
||||
|
||||
movq $0xFFFF,%rcx
|
||||
subb %cl,(%rax)
|
||||
|
||||
movq $0xA000,%rcx
|
||||
subw %cx,(%rax)
|
||||
|
||||
movq $0x0000000F1000A0FF,%rcx
|
||||
subq (%rax),%rcx
|
||||
|
||||
movq %rcx,(%rax)
|
||||
|
||||
TEST_END
|
||||
test13_end:
|
||||
|
||||
.align 64
|
||||
test14_begin:
|
||||
movq $0x1000,%rax
|
||||
movq $0xA0FF,(%rax)
|
||||
|
||||
testb $0x0F,(%rax)
|
||||
jz .L14_failure
|
||||
|
||||
testw $0x0F00,(%rax)
|
||||
jnz .L14_failure
|
||||
|
||||
testl $0xA000,(%rax)
|
||||
jz .L14_failure
|
||||
|
||||
.L14_success:
|
||||
movq $1,(%rax)
|
||||
TEST_END
|
||||
.L14_failure:
|
||||
movq $0,(%rax)
|
||||
TEST_END
|
||||
test14_end:
|
||||
|
|
Loading…
Reference in New Issue