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);