blob: 157962587888d9a868f4873ed4b667e589d03ca8 [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.
#include <hypervisor/decode.h>
#include <string.h>
#include <zircon/syscalls/hypervisor.h>
#include <zircon/syscalls/port.h>
static const uint8_t kRexRMask = 1u << 2;
static const uint8_t kRexWMask = 1u << 3;
static const uint8_t kModRMRegMask = 0b00111000;
// The Operand Size (w) Bit.
static const uint8_t kWMask = 1u;
static bool is_h66_prefix(uint8_t prefix) {
return prefix == 0x66;
}
static bool is_rex_prefix(uint8_t prefix) {
return (prefix >> 4) == 0b0100;
}
static bool has_sib_byte(uint8_t mod_rm) {
return (mod_rm >> 6) != 0b11 && (mod_rm & 0b111) == 0b100;
}
static uint8_t displacement_size(uint8_t mod_rm) {
switch (mod_rm >> 6) {
case 0b01:
return 1;
case 0b10:
return 4;
default:
return (mod_rm & ~kModRMRegMask) == 0b00000101 ? 4 : 0;
}
}
static uint8_t operand_size(bool h66, bool rex_w, bool w) {
if (!w) {
return 1;
} else if (rex_w) {
return 8;
} else if (!h66) {
return 4;
} else {
return 2;
}
}
static uint8_t immediate_size(bool h66, bool w) {
if (!w) {
return 1;
} else if (!h66) {
return 4;
} else {
return 2;
}
}
static uint8_t register_id(uint8_t mod_rm, bool rex_r) {
return static_cast<uint8_t>(((mod_rm >> 3) & 0b111) + (rex_r ? 0b1000 : 0));
}
// From Intel Volume 2, Appendix B.1.4.1
//
// Registers 4-7 (typically referring to SP,BP,SI,DI) instead refer to the
// high byte registers (AH,CH,DH,BH) when using 1 byte registers and no rex
// prefix is provided.
static inline bool is_high_byte(uint8_t size, bool rex) {
return size == 1 && !rex;
}
static uint64_t* select_register(zx_vcpu_state_t* vcpu_state, uint8_t register_id, uint8_t size,
bool rex) {
// From Intel Volume 2, Section 2.1.
switch (register_id) {
// From Intel Volume 2, Section 2.1.5.
case 0:
return &vcpu_state->rax;
case 1:
return &vcpu_state->rcx;
case 2:
return &vcpu_state->rdx;
case 3:
return &vcpu_state->rbx;
case 4:
if (is_high_byte(size, rex))
return nullptr;
return &vcpu_state->rsp;
case 5:
if (is_high_byte(size, rex))
return nullptr;
return &vcpu_state->rbp;
case 6:
if (is_high_byte(size, rex))
return nullptr;
return &vcpu_state->rsi;
case 7:
if (is_high_byte(size, rex))
return nullptr;
return &vcpu_state->rdi;
case 8:
return &vcpu_state->r8;
case 9:
return &vcpu_state->r9;
case 10:
return &vcpu_state->r10;
case 11:
return &vcpu_state->r11;
case 12:
return &vcpu_state->r12;
case 13:
return &vcpu_state->r13;
case 14:
return &vcpu_state->r14;
case 15:
return &vcpu_state->r15;
default:
return NULL;
}
}
zx_status_t deconstruct_instruction(const uint8_t* inst_buf, uint32_t inst_len,
uint16_t* opcode, uint8_t* mod_rm) {
if (inst_len == 0)
return ZX_ERR_NOT_SUPPORTED;
switch (inst_buf[0]) {
case 0x0f:
if (inst_len < 3)
return ZX_ERR_NOT_SUPPORTED;
*opcode = *(uint16_t*)inst_buf;
*mod_rm = inst_buf[2];
break;
default:
if (inst_len < 2)
return ZX_ERR_OUT_OF_RANGE;
*opcode = inst_buf[0];
*mod_rm = inst_buf[1];
break;
}
return ZX_OK;
}
zx_status_t inst_decode(const uint8_t* inst_buf, uint32_t inst_len, zx_vcpu_state_t* vcpu_state,
instruction_t* inst) {
if (inst_len == 0)
return ZX_ERR_BAD_STATE;
if (inst_len > X86_MAX_INST_LEN)
return ZX_ERR_OUT_OF_RANGE;
// Parse 66H prefix.
bool h66 = is_h66_prefix(inst_buf[0]);
if (h66) {
if (inst_len == 1)
return ZX_ERR_BAD_STATE;
inst_buf++;
inst_len--;
}
// Parse REX prefix.
//
// From Intel Volume 2, Appendix 2.2.1: Only one REX prefix is allowed per
// instruction. If used, the REX prefix byte must immediately precede the
// opcode byte or the escape opcode byte (0FH).
bool rex = false;
bool rex_r = false;
bool rex_w = false;
if (is_rex_prefix(inst_buf[0])) {
rex = true;
rex_r = inst_buf[0] & kRexRMask;
rex_w = inst_buf[0] & kRexWMask;
inst_buf++;
inst_len--;
}
// Technically this is valid, but no sane compiler should emit it.
if (h66 && rex_w)
return ZX_ERR_NOT_SUPPORTED;
uint16_t opcode;
uint8_t mod_rm;
zx_status_t status = deconstruct_instruction(inst_buf, inst_len, &opcode, &mod_rm);
if (status != ZX_OK)
return status;
if (has_sib_byte(mod_rm))
return ZX_ERR_NOT_SUPPORTED;
const uint8_t disp_size = displacement_size(mod_rm);
switch (opcode) {
// Move r to r/m.
// 1000 100w : mod reg r/m
case 0x88:
case 0x89: {
if (inst_len != disp_size + 2u)
return ZX_ERR_OUT_OF_RANGE;
const bool w = opcode & kWMask;
inst->type = INST_MOV_WRITE;
inst->mem = operand_size(h66, rex_w, w);
inst->imm = 0;
inst->reg = select_register(vcpu_state, register_id(mod_rm, rex_r), inst->mem, rex);
inst->flags = NULL;
return inst->reg == NULL ? ZX_ERR_NOT_SUPPORTED : ZX_OK;
}
// Move r/m to r.
// 1000 101w : mod reg r/m
case 0x8a:
case 0x8b: {
if (inst_len != disp_size + 2u)
return ZX_ERR_OUT_OF_RANGE;
const bool w = opcode & kWMask;
inst->type = INST_MOV_READ;
inst->mem = operand_size(h66, rex_w, w);
inst->imm = 0;
inst->reg = select_register(vcpu_state, register_id(mod_rm, rex_r), inst->mem, rex);
inst->flags = NULL;
return inst->reg == NULL ? ZX_ERR_NOT_SUPPORTED : ZX_OK;
}
// Move imm to r/m.
// 1100 011w : mod 000 r/m : immediate data
case 0xc6:
case 0xc7: {
const bool w = opcode & kWMask;
const uint8_t imm_size = immediate_size(h66, w);
if (inst_len != disp_size + imm_size + 2u)
return ZX_ERR_OUT_OF_RANGE;
if ((mod_rm & kModRMRegMask) != 0)
return ZX_ERR_INVALID_ARGS;
inst->type = INST_MOV_WRITE;
inst->mem = operand_size(h66, rex_w, w);
inst->imm = 0;
inst->reg = NULL;
inst->flags = NULL;
memcpy(&inst->imm, inst_buf + disp_size + 2, imm_size);
return ZX_OK;
}
// Move (16-bit) with zero-extend r/m to r.
case 0xb70f:
if (h66)
return ZX_ERR_BAD_STATE;
// Move (8-bit) with zero-extend r/m to r.
case 0xb60f: {
if (inst_len != disp_size + 3u)
return ZX_ERR_OUT_OF_RANGE;
const bool w = opcode & (kWMask << 8);
// We'll be operating with different sized operands due to the zero-
// extend. The 'w' bit determines if we're reading 8 or 16 bits out of
// memory while the h66/rex_w bits are used to select the destination
// register size.
const uint8_t mem_size = w ? 2 : 1;
const uint8_t reg_size = operand_size(h66, rex_w, true);
inst->type = INST_MOV_READ;
inst->mem = mem_size;
inst->imm = 0;
inst->reg = select_register(vcpu_state, register_id(mod_rm, rex_r), reg_size, rex);
inst->flags = NULL;
return inst->reg == NULL ? ZX_ERR_NOT_SUPPORTED : ZX_OK;
}
// Logical compare (8-bit) imm with r/m.
case 0xf6:
if (h66)
return ZX_ERR_BAD_STATE;
if (inst_len != disp_size + 3u)
return ZX_ERR_OUT_OF_RANGE;
if ((mod_rm & kModRMRegMask) != 0)
return ZX_ERR_INVALID_ARGS;
inst->type = INST_TEST;
inst->mem = 1;
inst->imm = 0;
inst->reg = NULL;
inst->flags = &vcpu_state->rflags;
memcpy(&inst->imm, inst_buf + disp_size + 2, 1);
return ZX_OK;
default:
return ZX_ERR_NOT_SUPPORTED;
}
}