blob: 9164f037b2d4a4042caa8df97ab4d22e1a2437a5 [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/storage/lib/block_client/cpp/reader_writer.h"
#include <safemath/checked_math.h>
namespace block_client {
namespace {
constexpr uint64_t kDefaultBufferSize = 128ul * 1024;
} // namespace
zx_status_t ReaderWriter::Read(uint64_t offset, const size_t count, void* buf) {
if (zx_status_t status = EnsureBufferInitialized(); status != ZX_OK) {
return status;
}
return DoIo(offset, count, vmoid_.get(), 0, false, buf);
}
zx_status_t ReaderWriter::Read(uint64_t offset, const size_t count, const zx::vmo& vmo,
uint64_t vmo_offset) {
storage::OwnedVmoid vmoid;
if (zx_status_t status = device_.BlockAttachVmo(vmo, &vmoid.GetReference(&device_));
status != ZX_OK) {
return status;
}
return DoIo(offset, count, vmoid.get(), vmo_offset, false, std::nullopt);
}
zx_status_t ReaderWriter::Write(uint64_t offset, const size_t count, void* buf) {
if (zx_status_t status = EnsureBufferInitialized(); status != ZX_OK) {
return status;
}
return DoIo(offset, count, vmoid_.get(), 0, true, buf);
}
zx_status_t ReaderWriter::Write(uint64_t offset, const size_t count, const zx::vmo& vmo,
uint64_t vmo_offset) {
storage::OwnedVmoid vmoid;
if (zx_status_t status = device_.BlockAttachVmo(vmo, &vmoid.GetReference(&device_));
status != ZX_OK) {
return status;
}
return DoIo(offset, count, vmoid.get(), vmo_offset, true, std::nullopt);
}
zx_status_t ReaderWriter::EnsureBufferInitialized() {
if (!buffer_.vmo()) {
if (zx_status_t status =
buffer_.CreateAndMap(kDefaultBufferSize, "block_client::ReaderWriteer");
status != ZX_OK) {
return status;
}
if (zx_status_t status = device_.BlockAttachVmo(buffer_.vmo(), &vmoid_.GetReference(&device_));
status != ZX_OK) {
return status;
}
}
return ZX_OK;
}
zx_status_t ReaderWriter::DoIo(uint64_t offset, size_t count, vmoid_t vmoid, uint64_t vmo_offset,
bool write, std::optional<void*> buf) {
if (block_size_ == 0) {
fuchsia_hardware_block::wire::BlockInfo info;
if (zx_status_t status = device_.BlockGetInfo(&info); status != ZX_OK)
return status;
block_size_ = info.block_size;
}
const uint64_t read_size = std::max(kDefaultBufferSize, block_size_);
if (count % block_size_ != 0 || offset % block_size_ != 0) {
return ZX_ERR_INVALID_ARGS;
}
uint64_t remaining = count;
while (remaining > 0) {
size_t amount = std::min(remaining, read_size);
if (buf && write) {
memcpy(buffer_.start(), *buf, amount);
}
uint8_t opcode = write ? BLOCK_OPCODE_WRITE : BLOCK_OPCODE_READ;
block_fifo_request_t request = {
.command = {.opcode = opcode, .flags = 0},
.vmoid = vmoid,
.length = safemath::checked_cast<uint32_t>(amount / block_size_),
.vmo_offset = vmo_offset / block_size_,
.dev_offset = offset / block_size_,
};
if (zx_status_t status = device_.FifoTransaction(&request, 1); status != ZX_OK)
return status;
remaining -= amount;
offset += amount;
if (buf) {
if (!write) {
memcpy(*buf, buffer_.start(), amount);
}
*buf = static_cast<uint8_t*>(*buf) + amount;
} else {
vmo_offset += amount;
}
}
return ZX_OK;
}
} // namespace block_client