[vim3][paver] Add vim3 specific device partitioner
Bug: 177613693
Change-Id: I2c3c087e2f2b0453042f772367dcb7f555b6507e
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/475805
Commit-Queue: Yecheng Zhao <zyecheng@google.com>
Reviewed-by: David Pursell <dpursell@google.com>
Reviewed-by: Suraj Malhotra <surajmalhotra@google.com>
diff --git a/src/devices/board/drivers/vim3/vim3-emmc.cc b/src/devices/board/drivers/vim3/vim3-emmc.cc
index cf44f1f..ecceaab 100644
--- a/src/devices/board/drivers/vim3/vim3-emmc.cc
+++ b/src/devices/board/drivers/vim3/vim3-emmc.cc
@@ -47,23 +47,13 @@
.version_3 = true,
.prefs = SDMMC_HOST_PREFS_DISABLE_HS400,
};
-static const guid_map_t guid_map[] = {
- {"zircon-a", GUID_ZIRCON_A_VALUE},
- {"zircon-b", GUID_ZIRCON_B_VALUE},
- {"zircon-r", GUID_ZIRCON_R_VALUE},
- {"fvm", GUID_FVM_VALUE},
-};
+
static const pbus_metadata_t emmc_metadata[] = {
{
.type = DEVICE_METADATA_PRIVATE,
.data_buffer = &config,
.data_size = sizeof(config),
},
- {
- .type = DEVICE_METADATA_GUID_MAP,
- .data_buffer = guid_map,
- .data_size = sizeof(guid_map),
- },
};
static const pbus_boot_metadata_t emmc_boot_metadata[] = {
diff --git a/src/storage/lib/paver/BUILD.gn b/src/storage/lib/paver/BUILD.gn
index 47bed58..ad47b1c 100644
--- a/src/storage/lib/paver/BUILD.gn
+++ b/src/storage/lib/paver/BUILD.gn
@@ -167,6 +167,18 @@
]
}
+source_set("vim3") {
+ sources = [ "vim3.cc" ]
+ deps = [
+ ":gpt",
+ ":paver-core",
+ ":sysconfig",
+ "//src/devices/lib/amlogic",
+ "//src/lib/uuid",
+ "//zircon/system/ulib/gpt",
+ ]
+}
+
source_set("x64") {
sources = [ "x64.cc" ]
deps = [
@@ -190,6 +202,7 @@
":nelson",
":paver-core",
":sherlock",
+ ":vim3",
":x64",
"//sdk/fidl/fuchsia.paver:fuchsia.paver_llcpp",
"//zircon/system/ulib/fidl-async:fidl-async-cpp",
diff --git a/src/storage/lib/paver/provider.cc b/src/storage/lib/paver/provider.cc
index 94d2278..3065781 100644
--- a/src/storage/lib/paver/provider.cc
+++ b/src/storage/lib/paver/provider.cc
@@ -17,6 +17,7 @@
#include "src/storage/lib/paver/nelson.h"
#include "src/storage/lib/paver/paver.h"
#include "src/storage/lib/paver/sherlock.h"
+#include "src/storage/lib/paver/vim3.h"
#include "src/storage/lib/paver/x64.h"
namespace {
@@ -41,6 +42,7 @@
paver::DevicePartitionerFactory::Register(std::make_unique<paver::NelsonPartitionerFactory>());
paver::DevicePartitionerFactory::Register(std::make_unique<paver::SherlockPartitionerFactory>());
paver::DevicePartitionerFactory::Register(std::make_unique<paver::LuisPartitionerFactory>());
+ paver::DevicePartitionerFactory::Register(std::make_unique<paver::Vim3PartitionerFactory>());
paver::DevicePartitionerFactory::Register(
std::make_unique<paver::ChromebookX64PartitionerFactory>());
paver::DevicePartitionerFactory::Register(std::make_unique<paver::X64PartitionerFactory>());
@@ -49,6 +51,7 @@
abr::ClientFactory::Register(std::make_unique<paver::NelsonAbrClientFactory>());
abr::ClientFactory::Register(std::make_unique<paver::SherlockAbrClientFactory>());
abr::ClientFactory::Register(std::make_unique<paver::LuisAbrClientFactory>());
+ abr::ClientFactory::Register(std::make_unique<paver::Vim3AbrClientFactory>());
abr::ClientFactory::Register(std::make_unique<paver::X64AbrClientFactory>());
return ZX_OK;
}
diff --git a/src/storage/lib/paver/test/device-partitioner-test.cc b/src/storage/lib/paver/test/device-partitioner-test.cc
index 72ac5f6..e2c3206 100644
--- a/src/storage/lib/paver/test/device-partitioner-test.cc
+++ b/src/storage/lib/paver/test/device-partitioner-test.cc
@@ -44,6 +44,7 @@
#include "src/storage/lib/paver/sherlock.h"
#include "src/storage/lib/paver/test/test-utils.h"
#include "src/storage/lib/paver/utils.h"
+#include "src/storage/lib/paver/vim3.h"
#include "src/storage/lib/paver/x64.h"
namespace paver {
@@ -1419,8 +1420,6 @@
EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
}
-
-
TEST_F(SherlockPartitionerTests, FindBootloader) {
std::unique_ptr<BlockDevice> gpt_dev;
ASSERT_NO_FATAL_FAILURES(CreateDisk(&gpt_dev));
@@ -1990,6 +1989,144 @@
ASSERT_NO_FATAL_FAILURES(ValidateBootloaderRead(read_buf.data(), kBL2ImageValue, kTplImageValue));
}
+class Vim3PartitionerTests : public GptDevicePartitionerTests {
+ protected:
+ static constexpr size_t kVim3BlockSize = 512;
+ Vim3PartitionerTests() : GptDevicePartitionerTests("vim3", kVim3BlockSize) {}
+
+ // Create a DevicePartition for a device.
+ zx::status<std::unique_ptr<paver::DevicePartitioner>> CreatePartitioner(
+ const fbl::unique_fd& device) {
+ zx::channel svc_root = GetSvcRoot();
+ return paver::Vim3Partitioner::Initialize(devmgr_.devfs_root().duplicate(), std::move(svc_root),
+ device);
+ }
+};
+
+TEST_F(Vim3PartitionerTests, InitializeWithoutGptFails) {
+ std::unique_ptr<BlockDevice> gpt_dev;
+ ASSERT_NO_FATAL_FAILURES(CreateDisk(&gpt_dev));
+
+ ASSERT_NOT_OK(CreatePartitioner(kDummyDevice));
+}
+
+TEST_F(Vim3PartitionerTests, InitializeWithoutFvmSucceeds) {
+ std::unique_ptr<BlockDevice> gpt_dev;
+ ASSERT_NO_FATAL_FAILURES(CreateDisk(32 * kGibibyte, &gpt_dev));
+
+ {
+ // Pause the block watcher while we write partitions to the disk.
+ // This is to avoid the block watcher seeing an intermediate state of the partition table
+ // and incorrectly treating it as an MBR.
+ // The watcher is automatically resumed when this goes out of scope.
+ auto pauser = paver::BlockWatcherPauser::Create(GetSvcRoot());
+ ASSERT_OK(pauser);
+
+ // Set up a valid GPT.
+ std::unique_ptr<gpt::GptDevice> gpt;
+ ASSERT_NO_FATAL_FAILURES(CreateGptDevice(gpt_dev.get(), &gpt));
+
+ ASSERT_OK(CreatePartitioner(kDummyDevice));
+ }
+}
+
+TEST_F(Vim3PartitionerTests, AddPartitionNotSupported) {
+ std::unique_ptr<BlockDevice> gpt_dev;
+ ASSERT_NO_FATAL_FAILURES(CreateDisk(64 * kMebibyte, &gpt_dev));
+ fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
+
+ auto status = CreatePartitioner(std::move(gpt_fd));
+ ASSERT_OK(status);
+
+ ASSERT_STATUS(status->AddPartition(PartitionSpec(paver::Partition::kZirconB)),
+ ZX_ERR_NOT_SUPPORTED);
+}
+
+TEST_F(Vim3PartitionerTests, FindPartition) {
+ std::unique_ptr<BlockDevice> gpt_dev;
+ constexpr uint64_t kBlockCount = 0x800000;
+ ASSERT_NO_FATAL_FAILURES(CreateDisk(kBlockCount * block_size_, &gpt_dev));
+
+ // The initial gpt partitions are randomly chosen and does not necessarily reflect the
+ // actual gpt partition layout in product.
+ std::vector<PartitionDescription> vim3_partitions = {
+ {GPT_DURABLE_BOOT_NAME, kDummyType, 0, 0x10000}, {GPT_VBMETA_A_NAME, kDummyType, 0, 0x10000},
+ {GPT_VBMETA_B_NAME, kDummyType, 0, 0x10000}, {GPT_VBMETA_R_NAME, kDummyType, 0, 0x10000},
+ {GPT_ZIRCON_A_NAME, kDummyType, 0, 0x10000}, {GPT_ZIRCON_B_NAME, kDummyType, 0, 0x10000},
+ {GPT_ZIRCON_R_NAME, kDummyType, 0, 0x10000}, {GPT_DURABLE_NAME, kDummyType, 0, 0x10000},
+ {GPT_FVM_NAME, kDummyType, 0, 0x10000},
+ };
+ vim3_partitions[0].start = 0x10400;
+ for (size_t i = 1; i < vim3_partitions.size(); i++) {
+ vim3_partitions[i].start = vim3_partitions[i - 1].start + vim3_partitions[i - 1].length;
+ }
+ ASSERT_NO_FATAL_FAILURES(InitializeStartingGPTPartitions(gpt_dev.get(), vim3_partitions));
+
+ fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
+ auto status = CreatePartitioner(std::move(gpt_fd));
+ ASSERT_OK(status);
+ std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
+
+ EXPECT_NOT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA)));
+
+ std::unique_ptr<BlockDevice> boot0_dev, boot1_dev;
+ ASSERT_NO_FATAL_FAILURES(CreateDisk(kBlockCount * kBlockSize, kBoot0Type, &boot0_dev));
+ ASSERT_NO_FATAL_FAILURES(CreateDisk(kBlockCount * kBlockSize, kBoot1Type, &boot1_dev));
+
+ // Make sure we can find the important partitions.
+ EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kBootloaderA)));
+ EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconA)));
+ EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconB)));
+ EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kZirconR)));
+ EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaA)));
+ EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaB)));
+ EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kVbMetaR)));
+ EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kAbrMeta)));
+ EXPECT_OK(partitioner->FindPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
+}
+
+TEST_F(Vim3PartitionerTests, CreateAbrClient) {
+ std::unique_ptr<BlockDevice> gpt_dev;
+ constexpr uint64_t kBlockCount = 0x748034;
+ ASSERT_NO_FATAL_FAILURES(CreateDisk(kBlockCount * block_size_, &gpt_dev));
+
+ const std::vector<PartitionDescription> kStartingPartitions = {
+ {GPT_DURABLE_BOOT_NAME, kDummyType, 0x10400, 0x10000},
+ };
+ ASSERT_NO_FATAL_FAILURES(InitializeStartingGPTPartitions(gpt_dev.get(), kStartingPartitions));
+ zx::channel svc_root = GetSvcRoot();
+ std::shared_ptr<paver::Context> context;
+ EXPECT_OK(paver::Vim3AbrClientFactory().New(devmgr_.devfs_root().duplicate(), std::move(svc_root),
+ context));
+}
+
+TEST_F(Vim3PartitionerTests, SupportsPartition) {
+ std::unique_ptr<BlockDevice> gpt_dev;
+ ASSERT_NO_FATAL_FAILURES(CreateDisk(64 * kMebibyte, &gpt_dev));
+ fbl::unique_fd gpt_fd(dup(gpt_dev->fd()));
+
+ auto status = CreatePartitioner(std::move(gpt_fd));
+ ASSERT_OK(status);
+ std::unique_ptr<paver::DevicePartitioner>& partitioner = status.value();
+
+ EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kBootloaderA)));
+ EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconA)));
+ EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconB)));
+ EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kZirconR)));
+ EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaA)));
+ EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaB)));
+ EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kVbMetaR)));
+ EXPECT_TRUE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kAbrMeta)));
+ EXPECT_TRUE(
+ partitioner->SupportsPartition(PartitionSpec(paver::Partition::kFuchsiaVolumeManager)));
+ // Unsupported partition type.
+ EXPECT_FALSE(partitioner->SupportsPartition(PartitionSpec(paver::Partition::kUnknown)));
+
+ // Unsupported content type.
+ EXPECT_FALSE(
+ partitioner->SupportsPartition(PartitionSpec(paver::Partition::kAbrMeta, "foo_type")));
+}
+
TEST(AstroPartitionerTests, IsFvmWithinFtl) {
std::unique_ptr<SkipBlockDevice> device;
ASSERT_NO_FATAL_FAILURES(SkipBlockDevice::Create(kNandInfo, &device));
diff --git a/src/storage/lib/paver/test/main.cc b/src/storage/lib/paver/test/main.cc
index cb78e09..c0b9201 100644
--- a/src/storage/lib/paver/test/main.cc
+++ b/src/storage/lib/paver/test/main.cc
@@ -12,6 +12,7 @@
#include "src/storage/lib/paver/luis.h"
#include "src/storage/lib/paver/nelson.h"
#include "src/storage/lib/paver/sherlock.h"
+#include "src/storage/lib/paver/vim3.h"
#include "src/storage/lib/paver/x64.h"
int main(int argc, char** argv) {
@@ -20,6 +21,7 @@
paver::DevicePartitionerFactory::Register(std::make_unique<paver::NelsonPartitionerFactory>());
paver::DevicePartitionerFactory::Register(std::make_unique<paver::SherlockPartitionerFactory>());
paver::DevicePartitionerFactory::Register(std::make_unique<paver::LuisPartitionerFactory>());
+ paver::DevicePartitionerFactory::Register(std::make_unique<paver::Vim3PartitionerFactory>());
paver::DevicePartitionerFactory::Register(
std::make_unique<paver::ChromebookX64PartitionerFactory>());
paver::DevicePartitionerFactory::Register(std::make_unique<paver::X64PartitionerFactory>());
@@ -28,6 +30,7 @@
abr::ClientFactory::Register(std::make_unique<paver::NelsonAbrClientFactory>());
abr::ClientFactory::Register(std::make_unique<paver::SherlockAbrClientFactory>());
abr::ClientFactory::Register(std::make_unique<paver::LuisAbrClientFactory>());
+ abr::ClientFactory::Register(std::make_unique<paver::Vim3AbrClientFactory>());
abr::ClientFactory::Register(std::make_unique<paver::X64AbrClientFactory>());
return RUN_ALL_TESTS(argc, argv);
}
diff --git a/src/storage/lib/paver/vim3.cc b/src/storage/lib/paver/vim3.cc
new file mode 100644
index 0000000..d7bea7a
--- /dev/null
+++ b/src/storage/lib/paver/vim3.cc
@@ -0,0 +1,193 @@
+// 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/paver/vim3.h"
+
+#include <lib/fzl/owned-vmo-mapper.h>
+
+#include <fbl/span.h>
+#include <gpt/gpt.h>
+#include <soc/aml-common/aml-guid.h>
+
+#include "src/lib/uuid/uuid.h"
+#include "src/storage/lib/paver/pave-logging.h"
+#include "src/storage/lib/paver/utils.h"
+
+namespace paver {
+namespace {
+
+using uuid::Uuid;
+
+} // namespace
+
+zx::status<std::unique_ptr<DevicePartitioner>> Vim3Partitioner::Initialize(
+ fbl::unique_fd devfs_root, const zx::channel& svc_root, const fbl::unique_fd& block_device) {
+ auto status = IsBoard(devfs_root, "vim3");
+ if (status.is_error()) {
+ return status.take_error();
+ }
+
+ auto status_or_gpt =
+ GptDevicePartitioner::InitializeGpt(std::move(devfs_root), svc_root, block_device);
+ if (status_or_gpt.is_error()) {
+ return status_or_gpt.take_error();
+ }
+
+ auto partitioner = WrapUnique(new Vim3Partitioner(std::move(status_or_gpt->gpt)));
+
+ LOG("Successfully initialized Vim3Partitioner Device Partitioner\n");
+ return zx::ok(std::move(partitioner));
+}
+
+bool Vim3Partitioner::SupportsPartition(const PartitionSpec& spec) const {
+ const PartitionSpec supported_specs[] = {PartitionSpec(paver::Partition::kBootloaderA),
+ PartitionSpec(paver::Partition::kZirconA),
+ PartitionSpec(paver::Partition::kZirconB),
+ PartitionSpec(paver::Partition::kZirconR),
+ PartitionSpec(paver::Partition::kVbMetaA),
+ PartitionSpec(paver::Partition::kVbMetaB),
+ PartitionSpec(paver::Partition::kVbMetaR),
+ PartitionSpec(paver::Partition::kAbrMeta),
+ PartitionSpec(paver::Partition::kFuchsiaVolumeManager)};
+
+ for (const auto& supported : supported_specs) {
+ if (SpecMatches(spec, supported)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+zx::status<std::unique_ptr<PartitionClient>> Vim3Partitioner::AddPartition(
+ const PartitionSpec& spec) const {
+ ERROR("Cannot add partitions to a vim3 device\n");
+ return zx::error(ZX_ERR_NOT_SUPPORTED);
+}
+
+zx::status<std::unique_ptr<PartitionClient>> Vim3Partitioner::GetEmmcBootPartitionClient() const {
+ auto boot0_part =
+ OpenBlockPartition(gpt_->devfs_root(), std::nullopt, Uuid(GUID_EMMC_BOOT1_VALUE), ZX_SEC(5));
+ if (boot0_part.is_error()) {
+ return boot0_part.take_error();
+ }
+ auto boot0 =
+ std::make_unique<FixedOffsetBlockPartitionClient>(std::move(boot0_part.value()), 1, 0);
+
+ auto boot1_part =
+ OpenBlockPartition(gpt_->devfs_root(), std::nullopt, Uuid(GUID_EMMC_BOOT2_VALUE), ZX_SEC(5));
+ if (boot1_part.is_error()) {
+ return boot1_part.take_error();
+ }
+ auto boot1 =
+ std::make_unique<FixedOffsetBlockPartitionClient>(std::move(boot1_part.value()), 1, 0);
+
+ std::vector<std::unique_ptr<PartitionClient>> partitions;
+ partitions.push_back(std::move(boot0));
+ partitions.push_back(std::move(boot1));
+
+ return zx::ok(std::make_unique<PartitionCopyClient>(std::move(partitions)));
+}
+
+zx::status<std::unique_ptr<PartitionClient>> Vim3Partitioner::FindPartition(
+ const PartitionSpec& spec) const {
+ if (!SupportsPartition(spec)) {
+ ERROR("Unsupported partition %s\n", spec.ToString().c_str());
+ return zx::error(ZX_ERR_NOT_SUPPORTED);
+ }
+
+ std::string_view part_name;
+
+ switch (spec.partition) {
+ case Partition::kBootloaderA:
+ return GetEmmcBootPartitionClient();
+ case Partition::kZirconA:
+ part_name = GPT_ZIRCON_A_NAME;
+ break;
+ case Partition::kZirconB:
+ part_name = GPT_ZIRCON_B_NAME;
+ break;
+ case Partition::kZirconR:
+ part_name = GPT_ZIRCON_R_NAME;
+ break;
+ case Partition::kVbMetaA:
+ part_name = GPT_VBMETA_A_NAME;
+ break;
+ case Partition::kVbMetaB:
+ part_name = GPT_VBMETA_B_NAME;
+ break;
+ case Partition::kVbMetaR:
+ part_name = GPT_VBMETA_R_NAME;
+ break;
+ case Partition::kAbrMeta:
+ part_name = GPT_DURABLE_BOOT_NAME;
+ break;
+ case Partition::kFuchsiaVolumeManager:
+ part_name = GPT_FVM_NAME;
+ break;
+ default:
+ ERROR("Partition type is invalid\n");
+ return zx::error(ZX_ERR_INVALID_ARGS);
+ }
+
+ const auto filter_by_name = [part_name](const gpt_partition_t& part) {
+ char cstring_name[GPT_NAME_LEN] = {};
+ utf16_to_cstring(cstring_name, part.name, GPT_NAME_LEN);
+ return part_name == std::string_view(cstring_name);
+ };
+
+ auto status = gpt_->FindPartition(std::move(filter_by_name));
+ if (status.is_error()) {
+ return status.take_error();
+ }
+ return zx::ok(std::move(status->partition));
+}
+
+zx::status<> Vim3Partitioner::WipeFvm() const { return gpt_->WipeFvm(); }
+
+zx::status<> Vim3Partitioner::InitPartitionTables() const {
+ ERROR("Initializing gpt partitions from paver is not supported on vim3\n");
+ return zx::error(ZX_ERR_NOT_SUPPORTED);
+}
+
+zx::status<> Vim3Partitioner::WipePartitionTables() const {
+ return zx::error(ZX_ERR_NOT_SUPPORTED);
+}
+
+zx::status<> Vim3Partitioner::ValidatePayload(const PartitionSpec& spec,
+ fbl::Span<const uint8_t> data) const {
+ if (!SupportsPartition(spec)) {
+ ERROR("Unsupported partition %s\n", spec.ToString().c_str());
+ return zx::error(ZX_ERR_NOT_SUPPORTED);
+ }
+ return zx::ok();
+}
+
+zx::status<std::unique_ptr<DevicePartitioner>> Vim3PartitionerFactory::New(
+ fbl::unique_fd devfs_root, const zx::channel& svc_root, Arch arch,
+ std::shared_ptr<Context> context, const fbl::unique_fd& block_device) {
+ return Vim3Partitioner::Initialize(std::move(devfs_root), svc_root, block_device);
+}
+
+zx::status<std::unique_ptr<abr::Client>> Vim3AbrClientFactory::New(
+ fbl::unique_fd devfs_root, const zx::channel& svc_root,
+ std::shared_ptr<paver::Context> context) {
+ fbl::unique_fd none;
+ auto partitioner = Vim3Partitioner::Initialize(std::move(devfs_root), std::move(svc_root), none);
+
+ if (partitioner.is_error()) {
+ return partitioner.take_error();
+ }
+
+ // ABR metadata has no need of a content type since it's always local rather
+ // than provided in an update package, so just use the default content type.
+ auto partition = partitioner->FindPartition(paver::PartitionSpec(paver::Partition::kAbrMeta));
+ if (partition.is_error()) {
+ return partition.take_error();
+ }
+
+ return abr::AbrPartitionClient::Create(std::move(partition.value()));
+}
+
+} // namespace paver
diff --git a/src/storage/lib/paver/vim3.h b/src/storage/lib/paver/vim3.h
new file mode 100644
index 0000000..bcc1a2a
--- /dev/null
+++ b/src/storage/lib/paver/vim3.h
@@ -0,0 +1,67 @@
+// 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.
+#ifndef SRC_STORAGE_LIB_PAVER_VIM3_H_
+#define SRC_STORAGE_LIB_PAVER_VIM3_H_
+
+#include "src/storage/lib/paver/abr-client.h"
+#include "src/storage/lib/paver/device-partitioner.h"
+#include "src/storage/lib/paver/gpt.h"
+#include "src/storage/lib/paver/partition-client.h"
+
+namespace paver {
+
+class Vim3Partitioner : public DevicePartitioner {
+ public:
+ static zx::status<std::unique_ptr<DevicePartitioner>> Initialize(
+ fbl::unique_fd devfs_root, const zx::channel& svc_root, const fbl::unique_fd& block_device);
+
+ bool IsFvmWithinFtl() const override { return false; }
+
+ bool SupportsPartition(const PartitionSpec& spec) const override;
+
+ zx::status<std::unique_ptr<PartitionClient>> AddPartition(
+ const PartitionSpec& spec) const override;
+
+ zx::status<std::unique_ptr<PartitionClient>> FindPartition(
+ const PartitionSpec& spec) const override;
+
+ zx::status<> FinalizePartition(const PartitionSpec& spec) const override { return zx::ok(); }
+
+ zx::status<> WipeFvm() const override;
+
+ zx::status<> InitPartitionTables() const override;
+
+ zx::status<> WipePartitionTables() const override;
+
+ zx::status<> ValidatePayload(const PartitionSpec& spec,
+ fbl::Span<const uint8_t> data) const override;
+
+ zx::status<> Flush() const override { return zx::ok(); }
+
+ private:
+ explicit Vim3Partitioner(std::unique_ptr<GptDevicePartitioner> gpt) : gpt_(std::move(gpt)) {}
+
+ zx::status<std::unique_ptr<PartitionClient>> GetEmmcBootPartitionClient() const;
+
+ std::unique_ptr<GptDevicePartitioner> gpt_;
+};
+
+class Vim3PartitionerFactory : public DevicePartitionerFactory {
+ public:
+ zx::status<std::unique_ptr<DevicePartitioner>> New(fbl::unique_fd devfs_root,
+ const zx::channel& svc_root, Arch arch,
+ std::shared_ptr<Context> context,
+ const fbl::unique_fd& block_device) final;
+};
+
+class Vim3AbrClientFactory : public abr::ClientFactory {
+ public:
+ zx::status<std::unique_ptr<abr::Client>> New(fbl::unique_fd devfs_root,
+ const zx::channel& svc_root,
+ std::shared_ptr<paver::Context> context) final;
+};
+
+} // namespace paver
+
+#endif // SRC_STORAGE_LIB_PAVER_VIM3_H_