blob: c82df99b89232d43c989f21b62ed02414bf214be [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <zircon/types.h>
// clang-format off
#define X86_FLAGS_STATUS ((1u << 11 /* OF */) | \
(1u << 7 /* SF */) | \
(1u << 6 /* ZF */) | \
(1u << 2 /* PF */) | \
(1u << 1 /* Reserved (must be 1) */) | \
(1u << 0 /* CF */))
#define INST_MOV_READ 0u
#define INST_MOV_WRITE 1u
#define INST_TEST 2u
// clang-format on
__BEGIN_CDECLS
typedef struct zx_vcpu_state zx_vcpu_state_t;
/* Stores info from a decoded instruction. */
typedef struct instruction {
uint8_t type;
uint8_t mem;
uint32_t imm;
uint64_t* reg;
uint32_t* flags;
} instruction_t;
zx_status_t inst_decode(const uint8_t* inst_buf, uint32_t inst_len, zx_vcpu_state_t* vcpu_state,
instruction_t* inst);
#define DEFINE_INST_VAL(size) \
static inline uint##size##_t inst_val##size(const instruction_t* inst) { \
return (uint##size##_t)(inst->reg != NULL ? *inst->reg : inst->imm); \
}
DEFINE_INST_VAL(32);
DEFINE_INST_VAL(16);
DEFINE_INST_VAL(8);
#undef DEFINE_INST_VAL
#define DEFINE_INST_READ(size) \
static inline zx_status_t inst_read##size(const instruction_t* inst, uint##size##_t value) { \
if (inst->type != INST_MOV_READ || inst->mem != (size / 8)) \
return ZX_ERR_NOT_SUPPORTED; \
*inst->reg = value; \
return ZX_OK; \
}
DEFINE_INST_READ(32);
DEFINE_INST_READ(16);
DEFINE_INST_READ(8);
#undef DEFINE_INST_READ
#define DEFINE_INST_WRITE(size) \
static inline zx_status_t inst_write##size(const instruction_t* inst, uint##size##_t* value) { \
if (inst->type != INST_MOV_WRITE || inst->mem != (size / 8)) \
return ZX_ERR_NOT_SUPPORTED; \
*value = inst_val##size(inst); \
return ZX_OK; \
}
DEFINE_INST_WRITE(32);
DEFINE_INST_WRITE(16);
#undef DEFINE_INST_WRITE
#define DEFINE_INST_RW(size) \
static inline zx_status_t inst_rw##size(const instruction_t* inst, uint##size##_t* value) { \
if (inst->type == INST_MOV_READ) { \
return inst_read##size(inst, *value); \
} else if (inst->type == INST_MOV_WRITE) { \
return inst_write##size(inst, value); \
} else { \
return ZX_ERR_NOT_SUPPORTED; \
} \
}
DEFINE_INST_RW(32);
DEFINE_INST_RW(16);
#undef DEFINE_INST_RW
#if defined(__x86_64__)
// Returns the flags that are assigned to the x86 flags register by an
// 8-bit TEST instruction for the given two operand values.
static inline uint16_t x86_flags_for_test8(uint8_t value1, uint8_t value2) {
// TEST cannot set the overflow flag (bit 11).
uint16_t ax_reg;
__asm__ volatile(
"testb %[i1], %[i2];"
"lahf" // Copies flags into the %ah register
: "=a"(ax_reg)
: [i1] "r"(value1), [i2] "r"(value2)
: "cc");
// Extract the value of the %ah register from the %ax register.
return (uint16_t)(ax_reg >> 8);
}
#endif
static inline zx_status_t inst_test8(const instruction_t* inst, uint8_t inst_val, uint8_t value) {
if (inst->type != INST_TEST || inst->mem != 1u || inst_val8(inst) != inst_val)
return ZX_ERR_NOT_SUPPORTED;
#if __x86_64__
*inst->flags &= ~X86_FLAGS_STATUS;
*inst->flags |= x86_flags_for_test8(inst_val, value);
return ZX_OK;
#else // __x86_64__
return ZX_ERR_NOT_SUPPORTED;
#endif // __x86_64__
}
__END_CDECLS