From 12211d163e93c655f56ce8023f9ef05597d777d2 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 24 Jul 2018 15:21:20 -0700 Subject: [PATCH] fastbootd: Enable erase and flash commands for physical partitions. Bug: 78793464 Test: adb reboot fastboot && fastboot flashall Change-Id: Ibe802c36f6efe20111a2315616ef34d3a027950f --- fastboot/Android.bp | 1 + fastboot/device/commands.cpp | 34 +++++++++- fastboot/device/commands.h | 2 + fastboot/device/fastboot_device.cpp | 3 + fastboot/device/fastboot_device.h | 5 +- fastboot/device/flashing.cpp | 102 ++++++++++++++++++++++++++++ fastboot/device/flashing.h | 24 +++++++ fastboot/device/utility.cpp | 37 ++++++++++ fastboot/device/utility.h | 24 +++++++ fastboot/device/variables.cpp | 15 ++++ fastboot/device/variables.h | 1 + 11 files changed, 241 insertions(+), 7 deletions(-) create mode 100644 fastboot/device/flashing.cpp create mode 100644 fastboot/device/flashing.h diff --git a/fastboot/Android.bp b/fastboot/Android.bp index e9bb1d7afca0..19f6390636b9 100644 --- a/fastboot/Android.bp +++ b/fastboot/Android.bp @@ -94,6 +94,7 @@ cc_binary { srcs: [ "device/commands.cpp", "device/fastboot_device.cpp", + "device/flashing.cpp", "device/main.cpp", "device/usb_client.cpp", "device/utility.cpp", diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp index 0e4a68b20445..7eaefe6de6bf 100644 --- a/fastboot/device/commands.cpp +++ b/fastboot/device/commands.cpp @@ -26,9 +26,11 @@ #include #include #include +#include #include "constants.h" #include "fastboot_device.h" +#include "flashing.h" #include "utility.h" using ::android::hardware::hidl_string; @@ -51,7 +53,8 @@ bool GetVarHandler(FastbootDevice* device, const std::vector& args) {FB_VAR_SLOT_COUNT, GetSlotCount}, {FB_VAR_HAS_SLOT, GetHasSlot}, {FB_VAR_SLOT_SUCCESSFUL, GetSlotSuccessful}, - {FB_VAR_SLOT_UNBOOTABLE, GetSlotUnbootable}}; + {FB_VAR_SLOT_UNBOOTABLE, GetSlotUnbootable}, + {FB_VAR_PARTITION_SIZE, GetPartitionSize}}; // args[0] is command name, args[1] is variable. auto found_variable = kVariableMap.find(args[1]); @@ -63,6 +66,20 @@ bool GetVarHandler(FastbootDevice* device, const std::vector& args) return found_variable->second(device, getvar_args); } +bool EraseHandler(FastbootDevice* device, const std::vector& args) { + if (args.size() < 2) { + return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments"); + } + PartitionHandle handle; + if (!OpenPartition(device, args[1], &handle)) { + return device->WriteStatus(FastbootResult::FAIL, "Partition doesn't exist"); + } + if (wipe_block_device(handle.fd(), get_block_device_size(handle.fd())) == 0) { + return device->WriteStatus(FastbootResult::OKAY, "Erasing succeeded"); + } + return device->WriteStatus(FastbootResult::FAIL, "Erasing failed"); +} + bool DownloadHandler(FastbootDevice* device, const std::vector& args) { if (args.size() < 2) { return device->WriteStatus(FastbootResult::FAIL, "size argument unspecified"); @@ -72,12 +89,12 @@ bool DownloadHandler(FastbootDevice* device, const std::vector& arg if (!android::base::ParseUint("0x" + args[1], &size, UINT_MAX)) { return device->WriteStatus(FastbootResult::FAIL, "Invalid size"); } - device->get_download_data().resize(size); + device->download_data().resize(size); if (!device->WriteStatus(FastbootResult::DATA, android::base::StringPrintf("%08x", size))) { return false; } - if (device->HandleData(true, &device->get_download_data())) { + if (device->HandleData(true, &device->download_data())) { return device->WriteStatus(FastbootResult::OKAY, ""); } @@ -85,6 +102,17 @@ bool DownloadHandler(FastbootDevice* device, const std::vector& arg return device->WriteStatus(FastbootResult::FAIL, "Couldn't download data"); } +bool FlashHandler(FastbootDevice* device, const std::vector& args) { + if (args.size() < 2) { + return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments"); + } + int ret = Flash(device, args[1]); + if (ret < 0) { + return device->WriteStatus(FastbootResult::FAIL, strerror(-ret)); + } + return device->WriteStatus(FastbootResult::OKAY, "Flashing succeeded"); +} + bool SetActiveHandler(FastbootDevice* device, const std::vector& args) { if (args.size() < 2) { return device->WriteStatus(FastbootResult::FAIL, "Missing slot argument"); diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h index 8785b917a0dd..830eb554e366 100644 --- a/fastboot/device/commands.h +++ b/fastboot/device/commands.h @@ -39,3 +39,5 @@ bool RebootBootloaderHandler(FastbootDevice* device, const std::vector& args); bool RebootRecoveryHandler(FastbootDevice* device, const std::vector& args); bool GetVarHandler(FastbootDevice* device, const std::vector& args); +bool EraseHandler(FastbootDevice* device, const std::vector& args); +bool FlashHandler(FastbootDevice* device, const std::vector& args); diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp index a225bf80d5bb..b94fbb0b1613 100644 --- a/fastboot/device/fastboot_device.cpp +++ b/fastboot/device/fastboot_device.cpp @@ -23,6 +23,7 @@ #include #include "constants.h" +#include "flashing.h" #include "usb_client.h" using ::android::hardware::hidl_string; @@ -40,6 +41,8 @@ FastbootDevice::FastbootDevice() {FB_CMD_REBOOT_BOOTLOADER, RebootBootloaderHandler}, {FB_CMD_REBOOT_FASTBOOT, RebootFastbootHandler}, {FB_CMD_REBOOT_RECOVERY, RebootRecoveryHandler}, + {FB_CMD_ERASE, EraseHandler}, + {FB_CMD_FLASH, FlashHandler}, }), transport_(std::make_unique()), boot_control_hal_(IBootControl::getService()) {} diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h index 75171206f1af..addc2eff78ce 100644 --- a/fastboot/device/fastboot_device.h +++ b/fastboot/device/fastboot_device.h @@ -43,9 +43,7 @@ class FastbootDevice { bool WriteOkay(const std::string& message); bool WriteFail(const std::string& message); - std::vector& get_download_data() { return download_data_; } - void set_upload_data(const std::vector& data) { upload_data_ = data; } - void set_upload_data(std::vector&& data) { upload_data_ = std::move(data); } + std::vector& download_data() { return download_data_; } Transport* get_transport() { return transport_.get(); } android::sp boot_control_hal() { return boot_control_hal_; @@ -57,5 +55,4 @@ class FastbootDevice { std::unique_ptr transport_; android::sp boot_control_hal_; std::vector download_data_; - std::vector upload_data_; }; diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp new file mode 100644 index 000000000000..d3dd82c92aa3 --- /dev/null +++ b/fastboot/device/flashing.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "flashing.h" + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "fastboot_device.h" +#include "utility.h" + +namespace { + +constexpr uint32_t SPARSE_HEADER_MAGIC = 0xed26ff3a; + +} // namespace + +int FlashRawDataChunk(int fd, const char* data, size_t len) { + size_t ret = 0; + while (ret < len) { + int this_len = std::min(static_cast(1048576UL * 8), len - ret); + int this_ret = write(fd, data, this_len); + if (this_ret < 0) { + PLOG(ERROR) << "Failed to flash data of len " << len; + return -1; + } + data += this_ret; + ret += this_ret; + } + return 0; +} + +int FlashRawData(int fd, const std::vector& downloaded_data) { + int ret = FlashRawDataChunk(fd, downloaded_data.data(), downloaded_data.size()); + if (ret < 0) { + return -errno; + } + return ret; +} + +int WriteCallback(void* priv, const void* data, size_t len) { + int fd = reinterpret_cast(priv); + if (!data) { + return lseek64(fd, len, SEEK_CUR) >= 0 ? 0 : -errno; + } + return FlashRawDataChunk(fd, reinterpret_cast(data), len); +} + +int FlashSparseData(int fd, std::vector& downloaded_data) { + struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(), true, false); + if (!file) { + return -ENOENT; + } + return sparse_file_callback(file, false, false, WriteCallback, reinterpret_cast(fd)); +} + +int FlashBlockDevice(int fd, std::vector& downloaded_data) { + lseek64(fd, 0, SEEK_SET); + if (downloaded_data.size() >= sizeof(SPARSE_HEADER_MAGIC) && + *reinterpret_cast(downloaded_data.data()) == SPARSE_HEADER_MAGIC) { + return FlashSparseData(fd, downloaded_data); + } else { + return FlashRawData(fd, downloaded_data); + } +} + +int Flash(FastbootDevice* device, const std::string& partition_name) { + PartitionHandle handle; + if (!OpenPartition(device, partition_name, &handle)) { + return -ENOENT; + } + + std::vector data = std::move(device->download_data()); + if (data.size() == 0) { + return -EINVAL; + } else if (data.size() > get_block_device_size(handle.fd())) { + return -EOVERFLOW; + } + return FlashBlockDevice(handle.fd(), data); +} diff --git a/fastboot/device/flashing.h b/fastboot/device/flashing.h new file mode 100644 index 000000000000..206a4076e115 --- /dev/null +++ b/fastboot/device/flashing.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +class FastbootDevice; + +int Flash(FastbootDevice* device, const std::string& partition_name); diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp index c8d2b3eb1a61..73cf1bff1693 100644 --- a/fastboot/device/utility.cpp +++ b/fastboot/device/utility.cpp @@ -16,8 +16,45 @@ #include "utility.h" +#include + +#include "fastboot_device.h" + +using android::base::unique_fd; using android::hardware::boot::V1_0::Slot; +static bool OpenPhysicalPartition(const std::string& name, PartitionHandle* handle) { + std::optional path = FindPhysicalPartition(name); + if (!path) { + return false; + } + *handle = PartitionHandle(*path); + return true; +} + +bool OpenPartition(FastbootDevice* /* device */, const std::string& name, PartitionHandle* handle) { + if (!OpenPhysicalPartition(name, handle)) { + LOG(ERROR) << "No such partition: " << name; + return false; + } + + unique_fd fd(TEMP_FAILURE_RETRY(open(handle->path().c_str(), O_WRONLY | O_EXCL))); + if (fd < 0) { + PLOG(ERROR) << "Failed to open block device: " << handle->path(); + return false; + } + handle->set_fd(std::move(fd)); + return true; +} + +std::optional FindPhysicalPartition(const std::string& name) { + std::string path = "/dev/block/by-name/" + name; + if (access(path.c_str(), R_OK | W_OK) < 0) { + return {}; + } + return path; +} + bool GetSlotNumber(const std::string& slot, Slot* number) { if (slot.size() != 1) { return false; diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h index 867d693bac6a..26f486b7bbf3 100644 --- a/fastboot/device/utility.h +++ b/fastboot/device/utility.h @@ -15,8 +15,32 @@ */ #pragma once +#include #include +#include #include +// Logical partitions are only mapped to a block device as needed, and +// immediately unmapped when no longer needed. In order to enforce this we +// require accessing partitions through a Handle abstraction, which may perform +// additional operations after closing its file descriptor. +class PartitionHandle { + public: + PartitionHandle() {} + explicit PartitionHandle(const std::string& path) : path_(path) {} + const std::string& path() const { return path_; } + int fd() const { return fd_.get(); } + void set_fd(android::base::unique_fd&& fd) { fd_ = std::move(fd); } + + private: + std::string path_; + android::base::unique_fd fd_; +}; + +class FastbootDevice; + +std::optional FindPhysicalPartition(const std::string& name); +bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle); + bool GetSlotNumber(const std::string& slot, android::hardware::boot::V1_0::Slot* number); diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp index 33f7f74bd3c0..8f66fea76017 100644 --- a/fastboot/device/variables.cpp +++ b/fastboot/device/variables.cpp @@ -16,6 +16,8 @@ #include "variables.h" +#include + #include #include #include @@ -24,6 +26,7 @@ #include #include "fastboot_device.h" +#include "flashing.h" #include "utility.h" using ::android::hardware::boot::V1_0::BoolResult; @@ -125,3 +128,15 @@ bool GetHasSlot(FastbootDevice* device, const std::vector& args) { std::string result = (args[0] == "userdata" ? "no" : "yes"); return device->WriteOkay(result); } + +bool GetPartitionSize(FastbootDevice* device, const std::vector& args) { + if (args.size() < 1) { + return device->WriteFail("Missing argument"); + } + PartitionHandle handle; + if (!OpenPartition(device, args[0], &handle)) { + return device->WriteFail("Could not open partition"); + } + uint64_t size = get_block_device_size(handle.fd()); + return device->WriteOkay(android::base::StringPrintf("%" PRIX64, size)); +} diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h index 45c6dc9cb496..88947e0cee54 100644 --- a/fastboot/device/variables.h +++ b/fastboot/device/variables.h @@ -34,3 +34,4 @@ bool GetSlotUnbootable(FastbootDevice* device, const std::vector& a bool GetMaxDownloadSize(FastbootDevice* device, const std::vector& args); bool GetUnlocked(FastbootDevice* device, const std::vector& args); bool GetHasSlot(FastbootDevice* device, const std::vector& args); +bool GetPartitionSize(FastbootDevice* device, const std::vector& args);