From 38b3c7a122154e270d4c4adb69822783bc47c181 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 15 Aug 2018 16:27:42 -0700 Subject: [PATCH] fastbootd: Add an update-super command to sync the super partition. This change introduces an "update-super" command to the fastboot protocol. Unlike the "flash" command, which copies raw or sparse data to a partition, the "update-super" command requires the data to be a super image generated by lpmake. If the super partition is not yet formatted (or is corrupt), then it will be formatted using the given image. Otherwise, "update-super" will preserve the existing partition layout, and only ensure that logical partition entries exist for all the new partitions in the given image. All new partitions added this way will have a zero size, and it is the host's responsibility to size them as needed afterwards with the "resize-logical-partition" command. In addition, the "update-super" command supports a "wipe" argument, which will force the super partition to be reformatted with the given image, overwriting any existing partition tables. Bug: 78793464 Test: fastboot flashall with a super partition Change-Id: If37d839a03e396e11b6c08a9c32984106613d1dc --- fastboot/README.md | 35 ++++++++++++++ fastboot/constants.h | 1 + fastboot/device/commands.cpp | 8 +++ fastboot/device/commands.h | 1 + fastboot/device/fastboot_device.cpp | 1 + fastboot/device/flashing.cpp | 75 +++++++++++++++++++++++++++++ fastboot/device/flashing.h | 1 + 7 files changed, 122 insertions(+) diff --git a/fastboot/README.md b/fastboot/README.md index ec7dcb4bc..15b5965ec 100644 --- a/fastboot/README.md +++ b/fastboot/README.md @@ -186,10 +186,45 @@ The various currently defined names are: bootloader requiring a signature before it will install or boot images. + is-userspace If the value is "yes", the device is running + fastbootd. Otherwise, it is running fastboot + in the bootloader. + Names starting with a lowercase character are reserved by this specification. OEM-specific names should not start with lowercase characters. +## Logical Partitions + +There are a number of commands to interact with logical partitions: + + update-super:%s:%s Write the previously downloaded image to a super + partition. Unlike the "flash" command, this has + special rules. The image must have been created by + the lpmake command, and must not be a sparse image. + If the last argument is "wipe", then all existing + logical partitions are deleted. If no final argument + is specified, the partition tables are merged. Any + partition in the new image that does not exist in the + old image is created with a zero size. + + In all cases, this will cause the temporary "scratch" + partition to be deleted if it exists. + + create-logical-partition:%s:%d + Create a logical partition with the given name and + size, in the super partition. + + delete-logical-partition:%s + Delete a logical partition with the given name. + + resize-logical-partition:%s:%d + Change the size of the named logical partition. + +In addition, there is a variable to test whether a partition is logical: + + is-logical:%s If the value is "yes", the partition is logical. + Otherwise the partition is physical. ## TCP Protocol v1 diff --git a/fastboot/constants.h b/fastboot/constants.h index 43daab03b..6542b83d2 100644 --- a/fastboot/constants.h +++ b/fastboot/constants.h @@ -33,6 +33,7 @@ #define FB_CMD_CREATE_PARTITION "create-logical-partition" #define FB_CMD_DELETE_PARTITION "delete-logical-partition" #define FB_CMD_RESIZE_PARTITION "resize-logical-partition" +#define FB_CMD_UPDATE_SUPER "update-super" #define RESPONSE_OKAY "OKAY" #define RESPONSE_FAIL "FAIL" diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp index 690bfa8b3..1e853bfa0 100644 --- a/fastboot/device/commands.cpp +++ b/fastboot/device/commands.cpp @@ -336,3 +336,11 @@ bool ResizePartitionHandler(FastbootDevice* device, const std::vectorWriteOkay("Partition resized"); } + +bool UpdateSuperHandler(FastbootDevice* device, const std::vector& args) { + if (args.size() < 2) { + return device->WriteFail("Invalid arguments"); + } + bool wipe = (args.size() >= 3 && args[2] == "wipe"); + return UpdateSuper(device, args[1], wipe); +} diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h index f67df9140..4778d2350 100644 --- a/fastboot/device/commands.h +++ b/fastboot/device/commands.h @@ -44,3 +44,4 @@ bool FlashHandler(FastbootDevice* device, const std::vector& args); bool CreatePartitionHandler(FastbootDevice* device, const std::vector& args); bool DeletePartitionHandler(FastbootDevice* device, const std::vector& args); bool ResizePartitionHandler(FastbootDevice* device, const std::vector& args); +bool UpdateSuperHandler(FastbootDevice* device, const std::vector& args); diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp index c306e675a..6ed6d3290 100644 --- a/fastboot/device/fastboot_device.cpp +++ b/fastboot/device/fastboot_device.cpp @@ -46,6 +46,7 @@ FastbootDevice::FastbootDevice() {FB_CMD_CREATE_PARTITION, CreatePartitionHandler}, {FB_CMD_DELETE_PARTITION, DeletePartitionHandler}, {FB_CMD_RESIZE_PARTITION, ResizePartitionHandler}, + {FB_CMD_UPDATE_SUPER, UpdateSuperHandler}, }), transport_(std::make_unique()), boot_control_hal_(IBootControl::getService()) {} diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp index b5ed1702c..e3efbcba2 100644 --- a/fastboot/device/flashing.cpp +++ b/fastboot/device/flashing.cpp @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include "fastboot_device.h" @@ -36,6 +38,8 @@ constexpr uint32_t SPARSE_HEADER_MAGIC = 0xed26ff3a; } // namespace +using namespace android::fs_mgr; + int FlashRawDataChunk(int fd, const char* data, size_t len) { size_t ret = 0; while (ret < len) { @@ -99,3 +103,74 @@ int Flash(FastbootDevice* device, const std::string& partition_name) { } return FlashBlockDevice(handle.fd(), data); } + +bool UpdateSuper(FastbootDevice* device, const std::string& partition_name, bool wipe) { + std::optional super = FindPhysicalPartition(partition_name); + if (!super) { + return device->WriteFail("Could not find partition: " + partition_name); + } + + std::vector data = std::move(device->download_data()); + if (data.empty()) { + return device->WriteFail("No data available"); + } + + std::unique_ptr new_metadata = ReadFromImageBlob(data.data(), data.size()); + if (!new_metadata) { + return device->WriteFail("Data is not a valid logical partition metadata image"); + } + + // If we are unable to read the existing metadata, then the super partition + // is corrupt. In this case we reflash the whole thing using the provided + // image. + std::string slot_suffix = device->GetCurrentSlot(); + uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix); + std::unique_ptr metadata = ReadMetadata(super->c_str(), slot_number); + if (!metadata || wipe) { + if (!FlashPartitionTable(super.value(), *new_metadata.get())) { + return device->WriteFail("Unable to flash new partition table"); + } + return device->WriteOkay("Successfully flashed partition table"); + } + + // There's a working super partition, and we don't want to wipe it - it may + // may contain partitions created for the user. Instead, we create a zero- + // sized partition for each entry in the new partition table. It is then + // the host's responsibility to size it correctly via resize-logical-partition. + std::unique_ptr builder = MetadataBuilder::New(*metadata.get()); + if (!builder) { + return device->WriteFail("Unable to create a metadata builder"); + } + for (const auto& partition : new_metadata->partitions) { + std::string name = GetPartitionName(partition); + if (builder->FindPartition(name)) { + continue; + } + std::string guid = GetPartitionGuid(partition); + if (!builder->AddPartition(name, guid, partition.attributes)) { + return device->WriteFail("Unable to add partition: " + name); + } + } + + // The scratch partition may exist as temporary storage, created for + // use by adb remount for overlayfs. If we're performing a flashall + // operation then we want to start over with a clean slate, so we + // remove the scratch partition until it is requested again. + builder->RemovePartition("scratch"); + + new_metadata = builder->Export(); + if (!new_metadata) { + return device->WriteFail("Unable to export new partition table"); + } + + // Write the new table to every metadata slot. + bool ok = true; + for (size_t i = 0; i < new_metadata->geometry.metadata_slot_count; i++) { + ok &= UpdatePartitionTable(super.value(), *new_metadata.get(), i); + } + + if (!ok) { + return device->WriteFail("Unable to write new partition table"); + } + return device->WriteOkay("Successfully updated partition table"); +} diff --git a/fastboot/device/flashing.h b/fastboot/device/flashing.h index 206a4076e..89e20fce2 100644 --- a/fastboot/device/flashing.h +++ b/fastboot/device/flashing.h @@ -22,3 +22,4 @@ class FastbootDevice; int Flash(FastbootDevice* device, const std::string& partition_name); +bool UpdateSuper(FastbootDevice* device, const std::string& partition_name, bool wipe);