| // 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 "block.h" |
| |
| #include <lib/fake-bti/bti.h> |
| #include <lib/fake_ddk/fake_ddk.h> |
| #include <lib/sync/completion.h> |
| #include <lib/virtio/backends/fake.h> |
| |
| #include <memory> |
| |
| #include <zxtest/zxtest.h> |
| |
| namespace { |
| |
| constexpr uint64_t kCapacity = 1024; |
| constexpr uint64_t kSizeMax = 4000; |
| constexpr uint64_t kSegMax = 1024; |
| constexpr uint64_t kBlkSize = 1024; |
| |
| // Fake virtio 'backend' for a virtio-scsi device. |
| class FakeBackendForBlock : public virtio::FakeBackend { |
| public: |
| FakeBackendForBlock() : virtio::FakeBackend({{0, 1024}}) { |
| // Fill out a block config: |
| virtio_blk_config config; |
| memset(&config, 0, sizeof(config)); |
| config.capacity = kCapacity; |
| config.size_max = kSizeMax; |
| config.seg_max = kSegMax; |
| config.blk_size = kBlkSize; |
| |
| for (uint16_t i = 0; i < sizeof(config); ++i) { |
| AddClassRegister(i, reinterpret_cast<uint8_t*>(&config)[i]); |
| } |
| } |
| }; |
| |
| TEST(BlockTest, InitSuccess) { |
| std::unique_ptr<virtio::Backend> backend = std::make_unique<FakeBackendForBlock>(); |
| zx::bti bti(ZX_HANDLE_INVALID); |
| fake_bti_create(bti.reset_and_get_address()); |
| fake_ddk::Bind ddk; |
| virtio::BlockDevice block(fake_ddk::FakeParent(), std::move(bti), std::move(backend)); |
| ASSERT_EQ(block.Init(), ZX_OK); |
| block.DdkAsyncRemove(); |
| EXPECT_TRUE(ddk.Ok()); |
| block.DdkRelease(); |
| } |
| |
| // Provides control primitives for tests that issue IO requests to the device. |
| class BlockDeviceTest : public zxtest::Test { |
| public: |
| ~BlockDeviceTest() {} |
| |
| void InitDevice() { |
| std::unique_ptr<virtio::Backend> backend = std::make_unique<FakeBackendForBlock>(); |
| zx::bti bti(ZX_HANDLE_INVALID); |
| fake_bti_create(bti.reset_and_get_address()); |
| ddk_ = std::make_unique<fake_ddk::Bind>(); |
| device_ = std::make_unique<virtio::BlockDevice>(fake_ddk::FakeParent(), std::move(bti), |
| std::move(backend)); |
| ASSERT_EQ(device_->Init(), ZX_OK); |
| device_->BlockImplQuery(&info_, &operation_size_); |
| } |
| |
| void RemoveDevice() { |
| device_->DdkAsyncRemove(); |
| EXPECT_TRUE(ddk_->Ok()); |
| device_->DdkRelease(); |
| } |
| |
| static void CompletionCb(void* cookie, zx_status_t status, block_op_t* op) { |
| BlockDeviceTest* operation = reinterpret_cast<BlockDeviceTest*>(cookie); |
| operation->operation_status_ = status; |
| sync_completion_signal(&operation->event_); |
| } |
| |
| bool Wait() { |
| zx_status_t status = sync_completion_wait(&event_, ZX_SEC(5)); |
| sync_completion_reset(&event_); |
| return status == ZX_OK; |
| } |
| |
| zx_status_t OperationStatus() { return operation_status_; } |
| |
| protected: |
| std::unique_ptr<virtio::BlockDevice> device_; |
| block_info_t info_; |
| size_t operation_size_; |
| |
| private: |
| sync_completion_t event_; |
| std::unique_ptr<fake_ddk::Bind> ddk_; |
| zx_status_t operation_status_; |
| }; |
| |
| // Tests trivial attempts to queue one operation. |
| TEST_F(BlockDeviceTest, QueueOne) { |
| InitDevice(); |
| |
| virtio::block_txn_t txn; |
| memset(&txn, 0, sizeof(txn)); |
| txn.op.rw.command = BLOCK_OP_READ; |
| txn.op.rw.length = 0; |
| // TODO(fxbug.dev/43065): This should not return ZX_OK when length == 0. |
| device_->BlockImplQueue(reinterpret_cast<block_op_t*>(&txn), &BlockDeviceTest::CompletionCb, |
| this); |
| ASSERT_TRUE(Wait()); |
| ASSERT_EQ(ZX_OK, OperationStatus()); |
| |
| txn.op.rw.length = kCapacity * 10; |
| device_->BlockImplQueue(reinterpret_cast<block_op_t*>(&txn), &BlockDeviceTest::CompletionCb, |
| this); |
| ASSERT_TRUE(Wait()); |
| ASSERT_EQ(ZX_ERR_OUT_OF_RANGE, OperationStatus()); |
| |
| RemoveDevice(); |
| } |
| |
| TEST_F(BlockDeviceTest, CheckQuery) { |
| InitDevice(); |
| ASSERT_EQ(info_.block_size, kBlkSize); |
| ASSERT_EQ(info_.block_count, kCapacity); |
| ASSERT_GE(info_.max_transfer_size, PAGE_SIZE); |
| ASSERT_GT(operation_size_, sizeof(block_op_t)); |
| RemoveDevice(); |
| } |
| } // anonymous namespace |