blob: 97b03dc52d4637a90330911065111e18cbb0eb56 [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 "src/virtualization/bin/vmm/controller/virtio_block.h"
#include <fuchsia/virtualization/hardware/cpp/fidl.h>
#include <lib/sys/cpp/service_directory.h>
#include <zircon/errors.h>
#include "src/lib/fxl/strings/string_printf.h"
#include "src/virtualization/bin/vmm/device/block.h"
namespace {
constexpr auto kVirtioBlockCollectionName = "virtio_block_devices";
constexpr auto component_url = "#meta/virtio_block.cm";
uint32_t read_only(fuchsia::virtualization::BlockMode mode) {
return mode == fuchsia::virtualization::BlockMode::READ_ONLY ? VIRTIO_BLK_F_RO : 0;
}
uint32_t discardable(fuchsia::virtualization::BlockFormat& format) {
// TODO(https://fxbug.dev/42172095): Enable discard support if BLOCK is the format used.
return 0;
}
} // namespace
VirtioBlock::VirtioBlock(const PhysMem& phys_mem, fuchsia::virtualization::BlockMode mode,
fuchsia::virtualization::BlockFormat format)
: VirtioComponentDevice(
"Virtio Block", phys_mem,
// From Virtio 1.0, Section 5.2.5.2: Devices SHOULD always offer
// VIRTIO_BLK_F_FLUSH.
//
// VIRTIO_BLK_F_BLK_SIZE is required by Zircon guests.
VIRTIO_BLK_F_FLUSH | VIRTIO_BLK_F_BLK_SIZE | read_only(mode) | discardable(format),
fit::bind_member(this, &VirtioBlock::ConfigureQueue),
fit::bind_member(this, &VirtioBlock::Ready)),
mode_(mode),
format_(std::move(format)) {}
zx_status_t VirtioBlock::Start(const zx::guest& guest, const std::string& id,
::sys::ComponentContext* context, async_dispatcher_t* dispatcher,
size_t component_name_suffix) {
std::string component_name = fxl::StringPrintf("virtio_block_%zu", component_name_suffix);
zx_status_t status = CreateDynamicComponent(
context, kVirtioBlockCollectionName, component_name.c_str(), component_url,
[block = block_.NewRequest()](std::shared_ptr<sys::ServiceDirectory> services) mutable {
return services->Connect(std::move(block));
});
if (status != ZX_OK) {
return status;
}
fuchsia::virtualization::hardware::StartInfo start_info;
status = PrepStart(guest, dispatcher, &start_info);
if (status != ZX_OK) {
return status;
}
uint64_t capacity;
uint32_t block_size;
status = block_->Start(std::move(start_info),
{
.id = id,
.mode = mode_,
.format = std::move(format_),
},
&capacity, &block_size);
if (status != ZX_OK) {
return status;
}
// Capacity is expressed in terms fixed size sectors (512 bytes) and not the devices preferred
// block size.
//
// Virtio 1.0, Section 5.2.4: The capacity of the device (expressed in 512-byte sectors) is
// always present.
//
// Virtio 1.0, Section 2.5.2: If the VIRTIO_BLK_F_BLK_SIZE feature is negotiated, blk_size can be
// read to determine the optimal sector size for the driver to use. This does not affect the units
// used in the protocol (always 512 bytes), but awareness of the correct value can affect
// performance.
if (capacity % kBlockSectorSize != 0) {
FX_LOGS(ERROR) << "Virtio block device capacity must be aligned to 512 byte sectors: " << id
<< " has capacity " << capacity << " and block size " << block_size;
return ZX_ERR_INVALID_ARGS;
}
std::lock_guard<std::mutex> lock(device_config_.mutex);
config_.capacity = capacity / kBlockSectorSize;
config_.blk_size = block_size;
return ZX_OK;
}
zx_status_t VirtioBlock::ConfigureQueue(uint16_t queue, uint16_t size, zx_gpaddr_t desc,
zx_gpaddr_t avail, zx_gpaddr_t used) {
return block_->ConfigureQueue(queue, size, desc, avail, used);
}
zx_status_t VirtioBlock::Ready(uint32_t negotiated_features) {
return block_->Ready(negotiated_features);
}