blob: f0a66c8aad766a58c1f6398ee3fc42fb84845706 [file] [log] [blame]
// Copyright 2021 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 "src/virtualization/bin/vmm/vcpu.h"
#include <lib/fit/defer.h>
#include <lib/fit/function.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/trace/event.h>
#include <lib/zx/thread.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <zircon/process.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/hypervisor.h>
#include <zircon/syscalls/port.h>
#include "src/lib/fxl/strings/string_printf.h"
#include "src/virtualization/bin/vmm/arch/x64/decode.h"
#include "src/virtualization/bin/vmm/guest.h"
#include "src/virtualization/bin/vmm/io.h"
namespace {
zx_status_t PerformMemAccess(const zx_packet_guest_mem_t& mem, IoMapping* device_mapping,
const Instruction* inst) {
TRACE_DURATION("machina", "mmio", "addr", mem.addr, "access_size", inst->access_size);
zx_status_t status;
IoValue mmio = {inst->access_size, {.u64 = 0}};
switch (inst->type) {
case INST_MOV_WRITE:
switch (inst->access_size) {
case 1:
status = inst_write8(inst, &mmio.u8);
break;
case 2:
status = inst_write16(inst, &mmio.u16);
break;
case 4:
status = inst_write32(inst, &mmio.u32);
break;
default:
return ZX_ERR_NOT_SUPPORTED;
}
if (status != ZX_OK) {
return status;
}
return device_mapping->Write(mem.addr, mmio);
case INST_MOV_READ:
status = device_mapping->Read(mem.addr, &mmio);
if (status != ZX_OK) {
return status;
}
switch (inst->access_size) {
case 1:
return inst_read8(inst, mmio.u8);
case 2:
return inst_read16(inst, mmio.u16);
case 4:
return inst_read32(inst, mmio.u32);
default:
return ZX_ERR_NOT_SUPPORTED;
}
case INST_TEST:
status = device_mapping->Read(mem.addr, &mmio);
if (status != ZX_OK) {
return status;
}
switch (inst->access_size) {
case 1:
return inst_test8(inst, static_cast<uint8_t>(inst->imm), mmio.u8);
default:
return ZX_ERR_NOT_SUPPORTED;
}
default:
return ZX_ERR_INVALID_ARGS;
}
}
} // namespace
zx_status_t Vcpu::ArchHandleMem(const zx_packet_guest_mem_t& mem, IoMapping* device_mapping) {
// Read guest register state.
zx_vcpu_state_t vcpu_state;
zx_status_t status = vcpu_.read_state(ZX_VCPU_STATE, &vcpu_state, sizeof(vcpu_state));
if (status != ZX_OK) {
return status;
}
// Decode the instruction the guest was attempting to perform.
Instruction inst;
status = inst_decode(mem.inst_buf, mem.inst_len, mem.default_operand_size, &vcpu_state, &inst);
if (status != ZX_OK) {
std::string inst;
for (uint8_t i = 0; i < mem.inst_len; i++) {
fxl::StringAppendf(&inst, " %x", mem.inst_buf[i]);
}
FX_LOGS(ERROR) << "Unsupported instruction:" << inst;
return status;
}
// Perform the access.
status = PerformMemAccess(mem, device_mapping, &inst);
if (status != ZX_OK) {
return status;
}
// If there was an attempt to read or test memory, update the guest's GPRs.
if (inst.type == INST_MOV_READ || inst.type == INST_TEST) {
return vcpu_.write_state(ZX_VCPU_STATE, &vcpu_state, sizeof(vcpu_state));
}
return ZX_OK;
}
zx_status_t Vcpu::ArchHandleInput(const zx_packet_guest_io_t& io, IoMapping* device_mapping) {
TRACE_DURATION("machina", "pio_in", "port", io.port, "access_size", io.access_size);
IoValue value = {};
value.access_size = io.access_size;
zx_status_t status = device_mapping->Read(io.port, &value);
if (status != ZX_OK) {
return status;
}
zx_vcpu_io_t vcpu_io;
memset(&vcpu_io, 0, sizeof(vcpu_io));
vcpu_io.access_size = value.access_size;
vcpu_io.u32 = value.u32;
if (vcpu_io.access_size != io.access_size) {
FX_LOGS(ERROR) << "Unexpected size (" << vcpu_io.access_size << " != " << io.access_size
<< ") for port in 0x" << std::hex << io.port;
return ZX_ERR_IO;
}
return vcpu_.write_state(ZX_VCPU_IO, &vcpu_io, sizeof(vcpu_io));
}
zx_status_t Vcpu::ArchHandleOutput(const zx_packet_guest_io_t& io, IoMapping* device_mapping) {
TRACE_DURATION("machina", "pio_out", "port", io.port, "access_size", io.access_size);
IoValue value;
value.access_size = io.access_size;
value.u32 = io.u32;
return device_mapping->Write(io.port, value);
}
zx_status_t Vcpu::ArchHandleIo(const zx_packet_guest_io_t& io, uint64_t trap_key) {
IoMapping* device_mapping = IoMapping::FromPortKey(trap_key);
zx_status_t status =
io.input ? ArchHandleInput(io, device_mapping) : ArchHandleOutput(io, device_mapping);
// Print a warning for unknown errors.
switch (status) {
case ZX_OK:
case ZX_ERR_CANCELED:
case ZX_ERR_STOP:
break;
default:
FX_LOGS(ERROR) << std::hex << "Device '" << device_mapping->handler()->Name()
<< "' returned status " << zx_status_get_string(status)
<< " while attempting to handle IO port " << (io.input ? "read" : "write")
<< " on port 0x" << io.port << " (mapping offset 0x"
<< (io.port - device_mapping->base()) << ")";
break;
}
return status;
}