| // 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 |