From cf8427af89f47075953dad10d6ea9b848736527f Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 4 Nov 2019 14:08:11 -0800 Subject: bootloader_message: Add helpers for handling IBootControl MergeStatus. Move merge_status from bootloader_control_ab, which is in vendor space, to a new generic AOSP struct in system space. This will allow more devices to share the same HAL implementation. This patch also changes libboot_control to compensate for merge_status moving out of vendor space. The reference HAL library now also provides separate helper functions for managing the merge status, so devices using a custom boot control HAL can still take advantage of the new misc implementation. Bug: 139156011 Test: manual test Change-Id: I5cd824e25f9d07aad1476301def5cdc3f506b029 --- .../include/libboot_control/libboot_control.h | 23 +++++++ boot_control/libboot_control.cpp | 80 +++++++++++++++++++--- bootloader_message/bootloader_message.cpp | 43 ++++++++++++ .../bootloader_message/bootloader_message.h | 26 +++++++ 4 files changed, 163 insertions(+), 9 deletions(-) diff --git a/boot_control/include/libboot_control/libboot_control.h b/boot_control/include/libboot_control/libboot_control.h index 34a9affe1..546865887 100644 --- a/boot_control/include/libboot_control/libboot_control.h +++ b/boot_control/include/libboot_control/libboot_control.h @@ -62,5 +62,28 @@ class BootControl { unsigned int current_slot_ = 0; }; +// Helper functions to write the Virtual A/B merge status message. These are +// separate because BootControl uses bootloader_control_ab in vendor space, +// whereas the Virtual A/B merge status is in system space. A HAL might not +// use bootloader_control_ab, but may want to use the AOSP method of maintaining +// the merge status. + +// If the Virtual A/B message has not yet been initialized, then initialize it. +// This should be called when the BootControl HAL first loads. +// +// If the Virtual A/B message in misc was already initialized, true is returned. +// If initialization was attempted, but failed, false is returned, and the HAL +// should fail to load. +bool InitMiscVirtualAbMessageIfNeeded(); + +// Save the current merge status as well as the current slot. +bool SetMiscVirtualAbMergeStatus(unsigned int current_slot, + android::hardware::boot::V1_1::MergeStatus status); + +// Return the current merge status. If the saved status is SNAPSHOTTED but the +// slot hasn't changed, the status returned will be NONE. +bool GetMiscVirtualAbMergeStatus(unsigned int current_slot, + android::hardware::boot::V1_1::MergeStatus* status); + } // namespace bootable } // namespace android diff --git a/boot_control/libboot_control.cpp b/boot_control/libboot_control.cpp index ff4eaabfa..702183979 100644 --- a/boot_control/libboot_control.cpp +++ b/boot_control/libboot_control.cpp @@ -232,6 +232,10 @@ bool BootControl::Init() { UpdateAndSaveBootloaderControl(device.c_str(), &boot_ctrl); } + if (!InitMiscVirtualAbMessageIfNeeded()) { + return false; + } + num_slots_ = boot_ctrl.nb_slot; return true; } @@ -335,18 +339,15 @@ bool BootControl::IsValidSlot(unsigned int slot) { } bool BootControl::SetSnapshotMergeStatus(MergeStatus status) { - bootloader_control bootctrl; - if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false; - - bootctrl.merge_status = (unsigned int)status; - return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl); + return SetMiscVirtualAbMergeStatus(current_slot_, status); } MergeStatus BootControl::GetSnapshotMergeStatus() { - bootloader_control bootctrl; - if (!LoadBootloaderControl(misc_device_, &bootctrl)) return MergeStatus::UNKNOWN; - - return (MergeStatus)bootctrl.merge_status; + MergeStatus status; + if (!GetMiscVirtualAbMergeStatus(current_slot_, &status)) { + return MergeStatus::UNKNOWN; + } + return status; } const char* BootControl::GetSuffix(unsigned int slot) { @@ -356,5 +357,66 @@ const char* BootControl::GetSuffix(unsigned int slot) { return kSlotSuffixes[slot]; } +bool InitMiscVirtualAbMessageIfNeeded() { + std::string err; + misc_virtual_ab_message message; + if (!ReadMiscVirtualAbMessage(&message, &err)) { + LOG(ERROR) << "Could not read merge status: " << err; + return false; + } + + if (message.version == MISC_VIRTUAL_AB_MESSAGE_VERSION) { + // Already initialized. + return true; + } + + message = {}; + message.version = MISC_VIRTUAL_AB_MESSAGE_VERSION; + if (!WriteMiscVirtualAbMessage(message, &err)) { + LOG(ERROR) << "Could not write merge status: " << err; + return false; + } + return true; +} + +bool SetMiscVirtualAbMergeStatus(unsigned int current_slot, + android::hardware::boot::V1_1::MergeStatus status) { + std::string err; + misc_virtual_ab_message message; + + if (!ReadMiscVirtualAbMessage(&message, &err)) { + LOG(ERROR) << "Could not read merge status: " << err; + return false; + } + + message.merge_status = static_cast(status); + message.source_slot = current_slot; + if (!WriteMiscVirtualAbMessage(message, &err)) { + LOG(ERROR) << "Could not write merge status: " << err; + return false; + } + return true; +} + +bool GetMiscVirtualAbMergeStatus(unsigned int current_slot, + android::hardware::boot::V1_1::MergeStatus* status) { + std::string err; + misc_virtual_ab_message message; + + if (!ReadMiscVirtualAbMessage(&message, &err)) { + LOG(ERROR) << "Could not read merge status: " << err; + return false; + } + + // If the slot reverted after having created a snapshot, then the snapshot will + // be thrown away at boot. Thus we don't count this as being in a snapshotted + // state. + *status = static_cast(message.merge_status); + if (*status == MergeStatus::SNAPSHOTTED && current_slot == message.source_slot) { + *status = MergeStatus::NONE; + } + return true; +} + } // namespace bootable } // namespace android diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp index f838930fd..4f7085db9 100644 --- a/bootloader_message/bootloader_message.cpp +++ b/bootloader_message/bootloader_message.cpp @@ -292,6 +292,49 @@ bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, err); } +static bool ValidateSystemSpaceRegion(size_t offset, size_t size, std::string* err) { + if (size <= SYSTEM_SPACE_SIZE_IN_MISC && offset <= (SYSTEM_SPACE_SIZE_IN_MISC - size)) { + return true; + } + *err = android::base::StringPrintf("Out of bound access (offset %zu size %zu)", offset, size); + return false; +} + +static bool ReadMiscPartitionSystemSpace(void* data, size_t size, size_t offset, std::string* err) { + if (!ValidateSystemSpaceRegion(offset, size, err)) { + return false; + } + auto misc_blk_device = get_misc_blk_device(err); + if (misc_blk_device.empty()) { + return false; + } + return read_misc_partition(data, size, misc_blk_device, SYSTEM_SPACE_OFFSET_IN_MISC + offset, + err); +} + +static bool WriteMiscPartitionSystemSpace(const void* data, size_t size, size_t offset, + std::string* err) { + if (!ValidateSystemSpaceRegion(offset, size, err)) { + return false; + } + auto misc_blk_device = get_misc_blk_device(err); + if (misc_blk_device.empty()) { + return false; + } + return write_misc_partition(data, size, misc_blk_device, SYSTEM_SPACE_OFFSET_IN_MISC + offset, + err); +} + +bool ReadMiscVirtualAbMessage(misc_virtual_ab_message* message, std::string* err) { + return ReadMiscPartitionSystemSpace(message, sizeof(*message), + offsetof(misc_system_space_layout, virtual_ab_message), err); +} + +bool WriteMiscVirtualAbMessage(const misc_virtual_ab_message& message, std::string* err) { + return WriteMiscPartitionSystemSpace(&message, sizeof(message), + offsetof(misc_system_space_layout, virtual_ab_message), err); +} + extern "C" bool write_reboot_bootloader(void) { std::string err; return write_reboot_bootloader(&err); diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h index e3425fc8b..3a3b862aa 100644 --- a/bootloader_message/include/bootloader_message/bootloader_message.h +++ b/bootloader_message/include/bootloader_message/bootloader_message.h @@ -185,6 +185,28 @@ static_assert(sizeof(struct bootloader_control) == "struct bootloader_control has wrong size"); #endif +// Holds Virtual A/B merge status information. Current version is 1. New fields +// must be added to the end. +struct misc_virtual_ab_message { + uint8_t version; + uint8_t merge_status; // IBootControl 1.1, MergeStatus enum. + uint8_t source_slot; // Slot number when merge_status was written. + uint8_t reserved[61]; +} __attribute__((packed)); + +#define MISC_VIRTUAL_AB_MESSAGE_VERSION 1 + +#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus) +static_assert(sizeof(struct misc_virtual_ab_message) == 64, + "struct misc_virtual_ab_message has wrong size"); +#endif + +// This struct is not meant to be used directly, rather, it is to make +// computation of offsets easier. New fields must be added to the end. +struct misc_system_space_layout { + misc_virtual_ab_message virtual_ab_message; +} __attribute__((packed)); + #ifdef __cplusplus #include @@ -247,6 +269,10 @@ bool ReadMiscPartitionVendorSpace(void* data, size_t size, size_t offset, std::s // offset is in relative to the start of the vendor space. bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, std::string* err); +// Read or write the Virtual A/B message from system space in /misc. +bool ReadMiscVirtualAbMessage(misc_virtual_ab_message* message, std::string* err); +bool WriteMiscVirtualAbMessage(const misc_virtual_ab_message& message, std::string* err); + #else #include -- cgit v1.2.3