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
diff --git a/boot_control/include/libboot_control/libboot_control.h b/boot_control/include/libboot_control/libboot_control.h
index 34a9aff..5468658 100644
--- a/boot_control/include/libboot_control/libboot_control.h
+++ b/boot_control/include/libboot_control/libboot_control.h
@@ -62,5 +62,28 @@
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 ff4eaab..7021839 100644
--- a/boot_control/libboot_control.cpp
+++ b/boot_control/libboot_control.cpp
@@ -232,6 +232,10 @@
UpdateAndSaveBootloaderControl(device.c_str(), &boot_ctrl);
}
+ if (!InitMiscVirtualAbMessageIfNeeded()) {
+ return false;
+ }
+
num_slots_ = boot_ctrl.nb_slot;
return true;
}
@@ -335,18 +339,15 @@
}
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 @@
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<uint8_t>(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<MergeStatus>(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 f838930..4f7085d 100644
--- a/bootloader_message/bootloader_message.cpp
+++ b/bootloader_message/bootloader_message.cpp
@@ -292,6 +292,49 @@
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 e3425fc..3a3b862 100644
--- a/bootloader_message/include/bootloader_message/bootloader_message.h
+++ b/bootloader_message/include/bootloader_message/bootloader_message.h
@@ -185,6 +185,28 @@
"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 <string>
@@ -247,6 +269,10 @@
// 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 <stdbool.h>