blob: d3c329cb8890fef390641c1a957a66080a5bb62c [file] [log] [blame]
// Copyright 2019 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 <inttypes.h>
#include <ddk/protocol/block.h>
#include <ddk/protocol/block/volume.h>
#include <fbl/unique_ptr.h>
#include <lib/sync/completion.h>
#include <lib/zircon-internal/debug.h>
#include <lib/zx/vmo.h>
#include <zircon/status.h>
#include <zxcrypt/ddk-volume.h>
#include <zxcrypt/volume.h>
#include <memory>
#include <utility>
#define ZXDEBUG 0
namespace zxcrypt {
void SyncComplete(void* cookie, zx_status_t status, block_op_t* block) {
// Use the 32bit command field to shuttle the response back to the callsite that's waiting on
// the completion
block->command = status;
sync_completion_signal(static_cast<sync_completion_t*>(cookie));
}
// Performs synchronous I/O
zx_status_t SyncIO(zx_device_t* dev, uint32_t cmd, void* buf, size_t off, size_t len) {
zx_status_t rc;
if (!dev || !buf || len == 0) {
xprintf("bad parameter(s): dev=%p, buf=%p, len=%zu\n", dev, buf, len);
return ZX_ERR_INVALID_ARGS;
}
block_impl_protocol_t proto;
if ((rc = device_get_protocol(dev, ZX_PROTOCOL_BLOCK, &proto)) != ZX_OK) {
xprintf("block protocol not support\n");
return ZX_ERR_NOT_SUPPORTED;
}
zx::vmo vmo;
if ((rc = zx::vmo::create(len, 0, &vmo)) != ZX_OK) {
xprintf("zx::vmo::create failed: %s\n", zx_status_get_string(rc));
return rc;
}
block_info_t info;
size_t op_size;
block_impl_query(&proto, &info, &op_size);
size_t bsz = info.block_size;
ZX_DEBUG_ASSERT(off / bsz <= UINT32_MAX);
ZX_DEBUG_ASSERT(len / bsz <= UINT32_MAX);
fbl::AllocChecker ac;
std::unique_ptr<char[]> raw;
if constexpr (alignof(block_op_t) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
raw = std::unique_ptr<char[]>(
new (static_cast<std::align_val_t>(alignof(block_op_t)), &ac) char[op_size]);
} else {
raw = std::unique_ptr<char[]>(
new (&ac) char[op_size]);
}
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
block_op_t* block = reinterpret_cast<block_op_t*>(raw.get());
sync_completion_t completion;
sync_completion_reset(&completion);
block->command = cmd;
block->rw.vmo = vmo.get();
block->rw.length = static_cast<uint32_t>(len / bsz);
block->rw.offset_dev = static_cast<uint32_t>(off / bsz);
block->rw.offset_vmo = 0;
if (cmd == BLOCK_OP_WRITE && (rc = vmo.write(buf, 0, len)) != ZX_OK) {
xprintf("zx::vmo::write failed: %s\n", zx_status_get_string(rc));
return rc;
}
block_impl_queue(&proto, block, SyncComplete, &completion);
sync_completion_wait(&completion, ZX_TIME_INFINITE);
rc = block->command;
if (rc != ZX_OK) {
xprintf("Block I/O failed: %s\n", zx_status_get_string(rc));
return rc;
}
if (cmd == BLOCK_OP_READ && (rc = vmo.read(buf, 0, len)) != ZX_OK) {
xprintf("zx::vmo::read failed: %s\n", zx_status_get_string(rc));
return rc;
}
return ZX_OK;
}
DdkVolume::DdkVolume(zx_device_t* dev) : Volume(), dev_(dev) {}
zx_status_t DdkVolume::Bind(crypto::Cipher::Direction direction, crypto::Cipher* cipher) const {
zx_status_t rc;
ZX_DEBUG_ASSERT(dev_); // Cannot bind from library
if (!cipher) {
xprintf("bad parameter(s): cipher=%p\n", cipher);
return ZX_ERR_INVALID_ARGS;
}
if (!block_.get()) {
xprintf("not initialized\n");
return ZX_ERR_BAD_STATE;
}
if ((rc = cipher->Init(cipher_, direction, data_key_, data_iv_, block_.len())) != ZX_OK) {
return rc;
}
return ZX_OK;
}
zx_status_t DdkVolume::Unlock(zx_device_t* dev, const crypto::Secret& key, key_slot_t slot,
fbl::unique_ptr<DdkVolume>* out) {
zx_status_t rc;
if (!dev || !out) {
xprintf("bad parameter(s): dev=%p, out=%p\n", dev, out);
return ZX_ERR_INVALID_ARGS;
}
fbl::AllocChecker ac;
fbl::unique_ptr<DdkVolume> volume(new (&ac) DdkVolume(dev));
if (!ac.check()) {
xprintf("allocation failed: %zu bytes\n", sizeof(DdkVolume));
return ZX_ERR_NO_MEMORY;
}
if ((rc = volume->Init()) != ZX_OK) {
xprintf("volume->Init() failed: %s\n", zx_status_get_string(rc));
return rc;
}
if ((rc = volume->Unlock(key, slot)) != ZX_OK) {
xprintf("volume->Unlock() failed: %s\n", zx_status_get_string(rc));
return rc;
}
*out = std::move(volume);
return ZX_OK;
}
zx_status_t DdkVolume::Init() {
return Volume::Init();
}
zx_status_t DdkVolume::Unlock(const crypto::Secret& key, key_slot_t slot) {
return Volume::Unlock(key, slot);
}
zx_status_t DdkVolume::GetBlockInfo(BlockInfo* out) {
zx_status_t rc;
block_impl_protocol_t proto;
if ((rc = device_get_protocol(dev_, ZX_PROTOCOL_BLOCK, &proto)) != ZX_OK) {
xprintf("block protocol not supported\n");
return ZX_ERR_NOT_SUPPORTED;
}
block_info_t info;
size_t block_op_size;
block_impl_query(&proto, &info, &block_op_size);
out->block_count = info.block_count;
out->block_size = info.block_size;
return ZX_OK;
}
zx_status_t DdkVolume::GetFvmSliceSize(uint64_t* out) {
zx_status_t rc;
block_volume_protocol_t proto;
if ((rc = device_get_protocol(dev_, ZX_PROTOCOL_BLOCK_VOLUME, &proto)) != ZX_OK) {
xprintf("block volume protocol not supported\n");
return ZX_ERR_NOT_SUPPORTED;
}
parent_volume_info_t info;
if ((rc = block_volume_query(&proto, &info)) != ZX_OK) {
return rc;
}
*out = info.slice_size;
return ZX_OK;
}
zx_status_t DdkVolume::DoBlockFvmVsliceQuery(uint64_t vslice_start,
SliceRegion ranges[MAX_SLICE_REGIONS],
uint64_t* slice_count) {
static_assert(MAX_SLICE_QUERY_REQUESTS == Volume::MAX_SLICE_REGIONS,
"block volume slice response count must match");
zx_status_t rc;
block_volume_protocol_t proto;
if ((rc = device_get_protocol(dev_, ZX_PROTOCOL_BLOCK_VOLUME, &proto)) != ZX_OK) {
xprintf("block volume protocol not supported\n");
return ZX_ERR_NOT_SUPPORTED;
}
slice_region_t tmp_ranges[MAX_SLICE_QUERY_REQUESTS];
size_t range_count;
if ((rc = block_volume_query_slices(&proto, &vslice_start, 1, tmp_ranges,
MAX_SLICE_QUERY_REQUESTS, &range_count)) != ZX_OK) {
return rc;
}
if (range_count > Volume::MAX_SLICE_REGIONS) {
// Should be impossible. Trust nothing.
return ZX_ERR_BAD_STATE;
}
*slice_count = range_count;
for (size_t i = 0; i < range_count; i++) {
ranges[i].allocated = tmp_ranges[i].allocated;
ranges[i].count = tmp_ranges[i].count;
}
return ZX_OK;
}
zx_status_t DdkVolume::DoBlockFvmExtend(uint64_t start_slice, uint64_t slice_count) {
zx_status_t rc;
block_volume_protocol_t proto;
if ((rc = device_get_protocol(dev_, ZX_PROTOCOL_BLOCK_VOLUME, &proto)) != ZX_OK) {
xprintf("block volume protocol not supported\n");
return ZX_ERR_NOT_SUPPORTED;
}
slice_extent_t extent;
extent.offset = start_slice;
extent.length = slice_count;
return block_volume_extend(&proto, &extent);
}
zx_status_t DdkVolume::Read() {
return SyncIO(dev_, BLOCK_OP_READ, block_.get(), offset_, block_.len());
}
zx_status_t DdkVolume::Write() {
return SyncIO(dev_, BLOCK_OP_WRITE, block_.get(), offset_, block_.len());
}
} // namespace zxcrypt