blob: 9898af5c7ec4a09a99fc3b689684254fad2a091d [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 <errno.h>
#include <fuchsia/hardware/block/c/fidl.h>
#include <fuchsia/hardware/block/volume/c/fidl.h>
#include <lib/devmgr-integration-test/fixture.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <sys/stat.h>
#include <zircon/device/block.h>
#include <zircon/errors.h>
#include <zircon/types.h>
#include <memory>
#include <crypto/bytes.h>
#include <crypto/cipher.h>
#include <fbl/unique_fd.h>
#include <fvm/format.h>
#include <unittest/unittest.h>
#include <zxcrypt/volume.h>
#include "test-device.h"
namespace zxcrypt {
namespace testing {
namespace {
// See test-device.h; the following macros allow reusing tests for each of the supported versions.
#define EACH_PARAM(OP, Test) OP(Test, Volume, AES256_XTS_SHA256)
bool TestBind(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
EXPECT_TRUE(device.Bind(version, fvm));
END_TEST;
}
DEFINE_EACH_DEVICE(TestBind)
// TODO(aarongreen): When ZX-1130 is resolved, add tests that check zxcrypt_rekey and zxcrypt_shred.
// Device::DdkGetSize tests
bool TestDdkGetSize(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, fvm));
fbl::unique_fd parent = device.parent();
fbl::unique_fd zxcrypt = device.zxcrypt();
struct stat parent_buf, zxcrypt_buf;
ASSERT_EQ(fstat(parent.get(), &parent_buf), 0, strerror(errno));
ASSERT_EQ(fstat(zxcrypt.get(), &zxcrypt_buf), 0, strerror(errno));
ASSERT_GT(parent_buf.st_size, zxcrypt_buf.st_size);
EXPECT_EQ((parent_buf.st_size - zxcrypt_buf.st_size) / device.block_size(),
device.reserved_blocks());
END_TEST;
}
DEFINE_EACH_DEVICE(TestDdkGetSize)
// FIDL tests
bool TestBlockGetInfo(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, fvm));
fuchsia_hardware_block_BlockInfo parent_blk, zxcrypt_blk;
zx_status_t status;
EXPECT_EQ(
fuchsia_hardware_block_BlockGetInfo(device.parent_channel()->get(), &status, &parent_blk),
ZX_OK);
EXPECT_EQ(status, ZX_OK);
EXPECT_EQ(
fuchsia_hardware_block_BlockGetInfo(device.zxcrypt_channel()->get(), &status, &zxcrypt_blk),
ZX_OK);
EXPECT_EQ(status, ZX_OK);
EXPECT_EQ(parent_blk.block_size, zxcrypt_blk.block_size);
EXPECT_GE(parent_blk.block_count, zxcrypt_blk.block_count + device.reserved_blocks());
END_TEST;
}
DEFINE_EACH_DEVICE(TestBlockGetInfo)
bool TestBlockFvmQuery(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, fvm));
auto parent = device.parent_channel();
auto zxcrypt = device.zxcrypt_channel();
zx_status_t status;
fuchsia_hardware_block_volume_VolumeInfo parent_fvm, zxcrypt_fvm;
if (!fvm) {
// Send FVM query to non-FVM device.
EXPECT_EQ(fuchsia_hardware_block_volume_VolumeQuery(zxcrypt->get(), &status, &zxcrypt_fvm),
ZX_OK);
EXPECT_EQ(status, ZX_ERR_NOT_SUPPORTED);
} else {
// Get the zxcrypt info.
EXPECT_EQ(fuchsia_hardware_block_volume_VolumeQuery(parent->get(), &status, &parent_fvm),
ZX_OK);
EXPECT_EQ(status, ZX_OK);
EXPECT_EQ(fuchsia_hardware_block_volume_VolumeQuery(zxcrypt->get(), &status, &zxcrypt_fvm),
ZX_OK);
EXPECT_EQ(status, ZX_OK);
EXPECT_EQ(parent_fvm.slice_size, zxcrypt_fvm.slice_size);
EXPECT_EQ(parent_fvm.vslice_count, zxcrypt_fvm.vslice_count + device.reserved_slices());
}
END_TEST;
}
DEFINE_EACH_DEVICE(TestBlockFvmQuery)
bool QueryLeadingFvmSlice(const TestDevice& device, bool fvm) {
BEGIN_HELPER;
auto parent = device.parent_channel();
auto zxcrypt = device.zxcrypt_channel();
uint64_t start_slices[1];
start_slices[0] = 0;
fuchsia_hardware_block_volume_VsliceRange
parent_ranges[fuchsia_hardware_block_volume_MAX_SLICE_REQUESTS];
fuchsia_hardware_block_volume_VsliceRange
zxcrypt_ranges[fuchsia_hardware_block_volume_MAX_SLICE_REQUESTS];
size_t actual_parent_ranges_count, actual_zxcrypt_ranges_count;
zx_status_t parent_io_status, zxcrypt_io_status, parent_status, zxcrypt_status;
parent_io_status = fuchsia_hardware_block_volume_VolumeQuerySlices(
parent->get(), start_slices, fbl::count_of(start_slices), &parent_status, parent_ranges,
&actual_parent_ranges_count);
zxcrypt_io_status = fuchsia_hardware_block_volume_VolumeQuerySlices(
zxcrypt->get(), start_slices, fbl::count_of(start_slices), &zxcrypt_status, zxcrypt_ranges,
&actual_zxcrypt_ranges_count);
if (fvm) {
ASSERT_EQ(parent_io_status, ZX_OK);
ASSERT_EQ(parent_status, ZX_OK);
ASSERT_EQ(zxcrypt_io_status, ZX_OK);
ASSERT_EQ(zxcrypt_status, ZX_OK);
// Query zxcrypt about the slices, which should omit those reserved.
ASSERT_EQ(actual_parent_ranges_count, 1U);
EXPECT_TRUE(parent_ranges[0].allocated);
ASSERT_EQ(actual_zxcrypt_ranges_count, 1U);
EXPECT_TRUE(zxcrypt_ranges[0].allocated);
EXPECT_EQ(parent_ranges[0].count, zxcrypt_ranges[0].count + device.reserved_slices());
} else {
// Non-FVM parent devices will close the connection upon receiving FVM requests.
ASSERT_EQ(parent_io_status, ZX_ERR_PEER_CLOSED);
// zxcrypt always supports the FVM protocol, but returns ERR_NOT_SUPPORTED if not
// sitting atop an FVM driver.
ASSERT_EQ(zxcrypt_io_status, ZX_OK);
ASSERT_EQ(zxcrypt_status, ZX_ERR_NOT_SUPPORTED);
}
END_HELPER;
}
bool TestBlockFvmVSliceQuery(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, fvm));
EXPECT_TRUE(QueryLeadingFvmSlice(device, fvm));
END_TEST;
}
DEFINE_EACH_DEVICE(TestBlockFvmVSliceQuery)
bool TestBlockFvmShrinkAndExtend(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, fvm));
auto zxcrypt = device.zxcrypt_channel();
uint64_t offset = 1;
uint64_t length = 1;
zx_status_t status;
if (!fvm) {
// Send FVM message to non-FVM device.
ASSERT_EQ(fuchsia_hardware_block_volume_VolumeShrink(zxcrypt->get(), offset, length, &status),
ZX_OK);
ASSERT_EQ(status, ZX_ERR_NOT_SUPPORTED);
ASSERT_EQ(fuchsia_hardware_block_volume_VolumeExtend(zxcrypt->get(), offset, length, &status),
ZX_OK);
ASSERT_EQ(status, ZX_ERR_NOT_SUPPORTED);
} else {
// Shrink the FVM partition and make sure the change in size is reflected
ASSERT_EQ(fuchsia_hardware_block_volume_VolumeShrink(zxcrypt->get(), offset, length, &status),
ZX_OK);
ASSERT_EQ(status, ZX_OK);
EXPECT_TRUE(QueryLeadingFvmSlice(device, fvm));
// Extend the FVM partition and make sure the change in size is reflected
ASSERT_EQ(fuchsia_hardware_block_volume_VolumeExtend(zxcrypt->get(), offset, length, &status),
ZX_OK);
ASSERT_EQ(status, ZX_OK);
EXPECT_TRUE(QueryLeadingFvmSlice(device, fvm));
}
END_TEST;
}
DEFINE_EACH_DEVICE(TestBlockFvmShrinkAndExtend)
// Device::DdkIotxnQueue tests
bool TestFdZeroLength(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, fvm));
EXPECT_TRUE(device.WriteFd(0, 0));
EXPECT_TRUE(device.ReadFd(0, 0));
END_TEST;
}
DEFINE_EACH_DEVICE(TestFdZeroLength)
bool TestFdFirstBlock(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, fvm));
size_t one = device.block_size();
EXPECT_TRUE(device.WriteFd(0, one));
EXPECT_TRUE(device.ReadFd(0, one));
END_TEST;
}
DEFINE_EACH_DEVICE(TestFdFirstBlock)
bool TestFdLastBlock(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, fvm));
size_t n = device.size();
size_t one = device.block_size();
EXPECT_TRUE(device.WriteFd(n - one, one));
EXPECT_TRUE(device.ReadFd(n - one, one));
END_TEST;
}
DEFINE_EACH_DEVICE(TestFdLastBlock)
bool TestFdAllBlocks(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, fvm));
size_t n = device.size();
EXPECT_TRUE(device.WriteFd(0, n));
EXPECT_TRUE(device.ReadFd(0, n));
END_TEST;
}
DEFINE_EACH_DEVICE(TestFdAllBlocks)
bool TestFdUnaligned(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, fvm));
size_t one = device.block_size();
ssize_t one_s = static_cast<ssize_t>(one);
ASSERT_TRUE(device.WriteFd(one, one));
ASSERT_TRUE(device.ReadFd(one, one));
EXPECT_EQ(device.lseek(one - 1), one_s - 1);
EXPECT_LT(device.write(one, one), 0);
EXPECT_LT(device.read(one, one), 0);
EXPECT_EQ(device.lseek(one + 1), one_s + 1);
EXPECT_LT(device.write(one, one), 0);
EXPECT_LT(device.read(one, one), 0);
EXPECT_EQ(device.lseek(one), one_s);
EXPECT_LT(device.write(one, one - 1), 0);
EXPECT_LT(device.read(one, one - 1), 0);
EXPECT_EQ(device.lseek(one), one_s);
EXPECT_LT(device.write(one, one + 1), 0);
EXPECT_LT(device.read(one, one + 1), 0);
END_TEST;
}
DEFINE_EACH_DEVICE(TestFdUnaligned)
bool TestFdOutOfBounds(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, fvm));
size_t n = device.size();
ssize_t n_s = static_cast<ssize_t>(n);
size_t one = device.block_size();
ssize_t one_s = static_cast<ssize_t>(one);
size_t two = one + one;
ssize_t two_s = static_cast<ssize_t>(two);
ASSERT_TRUE(device.WriteFd(0, one));
EXPECT_EQ(device.lseek(n), n_s);
EXPECT_NE(device.write(n, one), one_s);
EXPECT_EQ(device.lseek(n - one), n_s - one_s);
EXPECT_NE(device.write(n - one, two), two_s);
EXPECT_EQ(device.lseek(two), two_s);
EXPECT_NE(device.write(two, n - one), n_s - one_s);
EXPECT_EQ(device.lseek(one), one_s);
EXPECT_NE(device.write(one, n), n_s);
ASSERT_TRUE(device.ReadFd(0, one));
EXPECT_EQ(device.lseek(n), n_s);
EXPECT_NE(device.read(n, one), one_s);
EXPECT_EQ(device.lseek(n - one), n_s - one_s);
EXPECT_NE(device.read(n - one, two), two_s);
EXPECT_EQ(device.lseek(two), two_s);
EXPECT_NE(device.read(two, n - one), n_s - one_s);
EXPECT_EQ(device.lseek(one), one_s);
EXPECT_NE(device.read(one, n), n_s);
END_TEST;
}
DEFINE_EACH_DEVICE(TestFdOutOfBounds)
bool TestFdOneToMany(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, fvm));
size_t n = device.size();
size_t one = device.block_size();
ASSERT_TRUE(device.WriteFd(0, n));
ASSERT_TRUE(device.Rebind());
for (size_t off = 0; off < n; off += one) {
EXPECT_TRUE(device.ReadFd(off, one));
}
END_TEST;
}
DEFINE_EACH_DEVICE(TestFdOneToMany)
bool TestFdManyToOne(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, fvm));
size_t n = device.size();
size_t one = device.block_size();
for (size_t off = 0; off < n; off += one) {
EXPECT_TRUE(device.WriteFd(off, one));
}
ASSERT_TRUE(device.Rebind());
EXPECT_TRUE(device.ReadFd(0, n));
END_TEST;
}
DEFINE_EACH_DEVICE(TestFdManyToOne)
// Device::BlockWrite and Device::BlockRead tests
bool TestVmoZeroLength(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, fvm));
// Zero length is illegal for the block fifo
EXPECT_ZX(device.block_fifo_txn(BLOCKIO_WRITE, 0, 0), ZX_ERR_INVALID_ARGS);
EXPECT_ZX(device.block_fifo_txn(BLOCKIO_READ, 0, 0), ZX_ERR_INVALID_ARGS);
END_TEST;
}
DEFINE_EACH_DEVICE(TestVmoZeroLength)
bool TestVmoFirstBlock(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, fvm));
EXPECT_TRUE(device.WriteVmo(0, 1));
EXPECT_TRUE(device.ReadVmo(0, 1));
END_TEST;
}
DEFINE_EACH_DEVICE(TestVmoFirstBlock)
bool TestVmoLastBlock(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, fvm));
size_t n = device.block_count();
EXPECT_TRUE(device.WriteVmo(n - 1, 1));
EXPECT_TRUE(device.ReadVmo(n - 1, 1));
END_TEST;
}
DEFINE_EACH_DEVICE(TestVmoLastBlock)
bool TestVmoAllBlocks(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, fvm));
size_t n = device.block_count();
EXPECT_TRUE(device.WriteVmo(0, n));
EXPECT_TRUE(device.ReadVmo(0, n));
END_TEST;
}
DEFINE_EACH_DEVICE(TestVmoAllBlocks)
bool TestVmoOutOfBounds(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, fvm));
size_t n = device.block_count();
ASSERT_TRUE(device.WriteVmo(0, 1));
EXPECT_ZX(device.block_fifo_txn(BLOCKIO_WRITE, n, 1), ZX_ERR_OUT_OF_RANGE);
EXPECT_ZX(device.block_fifo_txn(BLOCKIO_WRITE, n - 1, 2), ZX_ERR_OUT_OF_RANGE);
EXPECT_ZX(device.block_fifo_txn(BLOCKIO_WRITE, 2, n - 1), ZX_ERR_OUT_OF_RANGE);
EXPECT_ZX(device.block_fifo_txn(BLOCKIO_WRITE, 1, n), ZX_ERR_OUT_OF_RANGE);
ASSERT_TRUE(device.ReadVmo(0, 1));
EXPECT_ZX(device.block_fifo_txn(BLOCKIO_READ, n, 1), ZX_ERR_OUT_OF_RANGE);
EXPECT_ZX(device.block_fifo_txn(BLOCKIO_READ, n - 1, 2), ZX_ERR_OUT_OF_RANGE);
EXPECT_ZX(device.block_fifo_txn(BLOCKIO_READ, 2, n - 1), ZX_ERR_OUT_OF_RANGE);
EXPECT_ZX(device.block_fifo_txn(BLOCKIO_READ, 1, n), ZX_ERR_OUT_OF_RANGE);
END_TEST;
}
DEFINE_EACH_DEVICE(TestVmoOutOfBounds)
bool TestVmoOneToMany(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, fvm));
size_t n = device.block_count();
EXPECT_TRUE(device.WriteVmo(0, n));
ASSERT_TRUE(device.Rebind());
for (size_t off = 0; off < n; ++off) {
EXPECT_TRUE(device.ReadVmo(off, 1));
}
END_TEST;
}
DEFINE_EACH_DEVICE(TestVmoOneToMany)
bool TestVmoManyToOne(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, fvm));
size_t n = device.block_count();
for (size_t off = 0; off < n; ++off) {
EXPECT_TRUE(device.WriteVmo(off, 1));
}
ASSERT_TRUE(device.Rebind());
EXPECT_TRUE(device.ReadVmo(0, n));
END_TEST;
}
DEFINE_EACH_DEVICE(TestVmoManyToOne)
bool TestVmoStall(Volume::Version version, bool fvm) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, fvm));
auto zxcrypt = device.zxcrypt_channel();
// The device can have up to 4 * max_transfer_size bytes in flight before it begins queuing them
// internally.
fuchsia_hardware_block_BlockInfo zxcrypt_blk;
zx_status_t status;
ASSERT_EQ(fuchsia_hardware_block_BlockGetInfo(zxcrypt->get(), &status, &zxcrypt_blk), ZX_OK);
ASSERT_EQ(status, ZX_OK);
size_t blks_per_req = 4;
size_t max = Volume::kBufferSize / (device.block_size() * blks_per_req);
size_t num = max + 1;
fbl::AllocChecker ac;
std::unique_ptr<block_fifo_request_t[]> requests(new (&ac) block_fifo_request_t[num]);
ASSERT_TRUE(ac.check());
for (size_t i = 0; i < num; ++i) {
requests[i].opcode = (i % 2 == 0 ? BLOCKIO_WRITE : BLOCKIO_READ);
requests[i].length = static_cast<uint32_t>(blks_per_req);
requests[i].dev_offset = 0;
requests[i].vmo_offset = 0;
}
EXPECT_TRUE(device.SleepUntil(max, true /* defer transactions */));
EXPECT_EQ(device.block_fifo_txn(requests.get(), num), ZX_OK);
EXPECT_TRUE(device.WakeUp());
END_TEST;
}
DEFINE_EACH_DEVICE(TestVmoStall)
bool TestWriteAfterFvmExtend(Volume::Version version) {
BEGIN_TEST;
TestDevice device;
ASSERT_TRUE(device.SetupDevmgr());
ASSERT_TRUE(device.Bind(version, true));
auto zxcrypt = device.zxcrypt_channel();
size_t n = device.size();
ssize_t n_s = static_cast<ssize_t>(n);
size_t one = device.block_size();
ssize_t one_s = static_cast<ssize_t>(one);
EXPECT_EQ(device.lseek(n), n_s);
EXPECT_NE(device.write(n, one), one_s);
zx_status_t status;
fuchsia_hardware_block_volume_VolumeInfo info;
ASSERT_EQ(fuchsia_hardware_block_volume_VolumeQuery(zxcrypt->get(), &status, &info), ZX_OK);
ASSERT_EQ(status, ZX_OK);
uint64_t offset = device.size() / info.slice_size;
uint64_t length = 1;
ASSERT_EQ(fuchsia_hardware_block_volume_VolumeExtend(zxcrypt->get(), offset, length, &status),
ZX_OK);
EXPECT_EQ(status, ZX_OK);
EXPECT_EQ(device.lseek(n), n_s);
EXPECT_EQ(device.write(n, one), one_s);
END_TEST;
}
DEFINE_EACH(TestWriteAfterFvmExtend)
// TODO(aarongreen): Currently, we're using XTS, which provides no data integrity. When possible,
// we should switch to an AEAD, which would allow us to detect data corruption when doing I/O.
// bool TestBadData(void) {
// BEGIN_TEST;
// END_TEST;
// }
BEGIN_TEST_CASE(ZxcryptTest)
RUN_EACH_DEVICE(TestBind)
RUN_EACH_DEVICE(TestDdkGetSize)
RUN_EACH_DEVICE(TestBlockGetInfo)
RUN_EACH_DEVICE(TestBlockFvmQuery)
RUN_EACH_DEVICE(TestBlockFvmVSliceQuery)
RUN_EACH_DEVICE(TestBlockFvmShrinkAndExtend)
RUN_EACH_DEVICE(TestFdZeroLength)
RUN_EACH_DEVICE(TestFdFirstBlock)
RUN_EACH_DEVICE(TestFdLastBlock)
RUN_EACH_DEVICE(TestFdAllBlocks)
RUN_EACH_DEVICE(TestFdUnaligned)
RUN_EACH_DEVICE(TestFdOutOfBounds)
RUN_EACH_DEVICE(TestFdOneToMany)
RUN_EACH_DEVICE(TestFdManyToOne)
RUN_EACH_DEVICE(TestVmoZeroLength)
RUN_EACH_DEVICE(TestVmoFirstBlock)
RUN_EACH_DEVICE(TestVmoLastBlock)
RUN_EACH_DEVICE(TestVmoAllBlocks)
RUN_EACH_DEVICE(TestVmoOutOfBounds)
RUN_EACH_DEVICE(TestVmoOneToMany)
RUN_EACH_DEVICE(TestVmoManyToOne)
// Disabled (See ZX-2112): RUN_EACH_DEVICE(TestVmoStall)
RUN_EACH(TestWriteAfterFvmExtend)
END_TEST_CASE(ZxcryptTest)
} // namespace
} // namespace testing
} // namespace zxcrypt