|  | // 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 <lib/sync/completion.h> | 
|  | #include <lib/zircon-internal/debug.h> | 
|  | #include <lib/zx/vmo.h> | 
|  | #include <zircon/assert.h> | 
|  | #include <zircon/status.h> | 
|  |  | 
|  | #include <memory> | 
|  | #include <utility> | 
|  |  | 
|  | #include <ddk/protocol/block.h> | 
|  | #include <ddk/protocol/block/volume.h> | 
|  | #include <fbl/alloc_checker.h> | 
|  | #include <zxcrypt/ddk-volume.h> | 
|  | #include <zxcrypt/volume.h> | 
|  |  | 
|  | #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, | 
|  | std::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; | 
|  | std::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 |