summaryrefslogtreecommitdiff
path: root/bootctrl/boot_control.cc
diff options
context:
space:
mode:
Diffstat (limited to 'bootctrl/boot_control.cc')
-rw-r--r--bootctrl/boot_control.cc351
1 files changed, 351 insertions, 0 deletions
diff --git a/bootctrl/boot_control.cc b/bootctrl/boot_control.cc
new file mode 100644
index 0000000..28c30d4
--- /dev/null
+++ b/bootctrl/boot_control.cc
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * 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 <log/log.h>
+#include <cutils/properties.h>
+#include <zlib.h>
+#include <hardware/boot_control.h>
+#include <bootloader_message.h>
+
+#include <string>
+
+#define BOOT_SLOT_PROP "ro.boot.slot_suffix"
+
+struct BootControlPrivate {
+ // The base struct needs to be first in the list.
+ boot_control_module_t base;
+
+ // Whether this struct was initialized with data from the bootloader message
+ // that doesn't change until next reboot.
+ bool initialized;
+
+ // The path to the misc_device as reported in the fstab.
+ const char* misc_device;
+
+ // The number of slots present on the device.
+ unsigned int num_slots;
+
+ // The slot where we are running from.
+ unsigned int current_slot;
+};
+
+constexpr unsigned int kMaxNumSlots =
+ sizeof(bootloader_control::slot_info) /
+ sizeof(bootloader_control::slot_info[0]);
+
+constexpr const char* kSlotSuffixes[kMaxNumSlots] = { "_a", "_b", "_c", "_d" };
+
+// Return the little-endian representation of the CRC-32 of the first fields
+// in |boot_ctrl| up to the crc32_le field.
+static uint32_t GetBootloaderControlCRC(const bootloader_control* boot_ctrl) {
+ return crc32(0, (const uint8_t*)boot_ctrl,
+ offsetof(bootloader_control, crc32_le));
+}
+
+static bool LoadBootloaderControl(const char* misc_device,
+ bootloader_control* boot_ctrl) {
+ std::string str_err;
+ if (read_bootloader_control_from(boot_ctrl, misc_device, &str_err))
+ return true;
+
+ ALOGE("%s", str_err.c_str());
+
+ return false;
+}
+
+static bool SaveBootloaderControl(const char* misc_device,
+ bootloader_control* boot_ctrl) {
+ boot_ctrl->crc32_le = GetBootloaderControlCRC(boot_ctrl);
+
+ std::string str_err;
+ if (write_bootloader_control_to(boot_ctrl, misc_device, &str_err))
+ return true;
+
+ ALOGE("%s", str_err.c_str());
+
+ return false;
+}
+
+// Return the index of the slot suffix passed or -1 if not a valid slot suffix.
+static int SlotSuffixToIndex(const char* suffix) {
+ for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
+ if (!strcmp(kSlotSuffixes[slot], suffix)) return slot;
+ }
+
+ return -1;
+}
+
+static bool IsInitialized(const BootControlPrivate* module) {
+ if (!module->initialized) {
+ ALOGW("Module not initialized");
+ return false;
+ }
+
+ return true;
+}
+
+void BootControlInit(boot_control_module_t* module) {
+ struct BootControlPrivate* bootctrl_module =
+ reinterpret_cast<BootControlPrivate*>(module);
+
+ if (bootctrl_module->initialized) return;
+
+ if (!module) {
+ ALOGE("Invalid argument passed to %s", __func__);
+ return;
+ }
+
+ ALOGI("Init %s", module->common.name);
+
+ // Initialize the current_slot from the read-only property. If the property
+ // was not set (from either the command line or the device tree), we can later
+ // initialize it from the bootloader_control struct.
+ char suffix_prop[PROPERTY_VALUE_MAX] = {0};
+ property_get(BOOT_SLOT_PROP, suffix_prop, "");
+ bootctrl_module->current_slot = SlotSuffixToIndex(suffix_prop);
+
+ std::string err;
+ std::string device = get_bootloader_message_blk_device(&err);
+
+ bootloader_control boot_ctrl;
+ if (!LoadBootloaderControl(device.c_str(), &boot_ctrl))
+ ALOGE("Error loading metadata");
+
+ // Note that since there isn't a module unload function this memory is leaked.
+ bootctrl_module->misc_device = strdup(device.c_str());
+ uint32_t computed_crc32 = GetBootloaderControlCRC(&boot_ctrl);
+ if (boot_ctrl.crc32_le != computed_crc32) {
+ ALOGE("Invalid boot control found, expected CRC-32 0x%04X, "
+ "but found 0x%04X. Should re-initializing A/B metadata.",
+ computed_crc32, boot_ctrl.crc32_le);
+ return;
+ }
+
+ std::string metadata_suffix = "_" + std::string(boot_ctrl.slot_suffix);
+ if (SlotSuffixToIndex(metadata_suffix.c_str()) !=
+ bootctrl_module->current_slot) {
+ ALOGE("Kernel slot argument and A/B metadata do not match, "
+ "%s=%s, slot metadata=%s", BOOT_SLOT_PROP, suffix_prop,
+ boot_ctrl.slot_suffix);
+ return;
+ }
+
+ bootctrl_module->initialized = true;
+ bootctrl_module->num_slots = boot_ctrl.nb_slot;
+
+ ALOGI("Current slot: %s(%d), number of slots: %d", boot_ctrl.slot_suffix,
+ bootctrl_module->current_slot, bootctrl_module->num_slots);
+
+ return;
+}
+
+unsigned int GetNumberSlots(boot_control_module_t* module) {
+ BootControlPrivate* const bootctrl_module =
+ reinterpret_cast<BootControlPrivate*>(module);
+
+ if (!IsInitialized(bootctrl_module)) return -1;
+
+ return bootctrl_module->num_slots;
+}
+
+unsigned int GetCurrentSlot(boot_control_module_t* module) {
+ BootControlPrivate* const bootctrl_module =
+ reinterpret_cast<BootControlPrivate*>(module);
+
+ if (!IsInitialized(bootctrl_module)) return -1;
+
+ return bootctrl_module->current_slot;
+}
+
+int IsSlotMarkedSuccessful(boot_control_module_t* module, unsigned int slot) {
+ BootControlPrivate* const bootctrl_module =
+ reinterpret_cast<BootControlPrivate*>(module);
+
+ if (!IsInitialized(bootctrl_module)) return -1;
+
+ if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
+ // Invalid slot number.
+ return -1;
+ }
+
+ bootloader_control bootctrl;
+ if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl))
+ return -1;
+
+ return (bootctrl.slot_info[slot].successful_boot &&
+ bootctrl.slot_info[slot].tries_remaining);
+}
+
+int MarkBootSuccessful(boot_control_module_t* module) {
+ BootControlPrivate* const bootctrl_module =
+ reinterpret_cast<BootControlPrivate*>(module);
+
+ if (!IsInitialized(bootctrl_module)) return -1;
+
+ bootloader_control bootctrl;
+ if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl))
+ return -1;
+
+ bootctrl.slot_info[bootctrl_module->current_slot].successful_boot = 1;
+ // tries_remaining == 0 means that the slot is not bootable anymore, make
+ // sure we mark the current slot as bootable if it succeeds in the last
+ // attempt.
+ bootctrl.slot_info[bootctrl_module->current_slot].tries_remaining = 1;
+ if (!SaveBootloaderControl(bootctrl_module->misc_device, &bootctrl))
+ return -1;
+
+ ALOGI("Slot %d is marked as successfully booted",
+ bootctrl_module->current_slot);
+
+ return 0;
+}
+
+int SetActiveBootSlot(boot_control_module_t* module, unsigned int slot) {
+ BootControlPrivate* const bootctrl_module =
+ reinterpret_cast<BootControlPrivate*>(module);
+
+ if (!IsInitialized(bootctrl_module))
+ return -1;
+
+ if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
+ // Invalid slot number.
+ return -1;
+ }
+
+ bootloader_control bootctrl;
+ if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl))
+ return -1;
+
+ // Set every other slot with a lower priority than the new "active" slot.
+ const unsigned int kActivePriority = 15;
+ const unsigned int kActiveTries = 6;
+ for (unsigned int i = 0; i < bootctrl_module->num_slots; ++i) {
+ if (i != slot) {
+ if (bootctrl.slot_info[i].priority >= kActivePriority)
+ bootctrl.slot_info[i].priority = kActivePriority - 1;
+ }
+ }
+
+ // Note that setting a slot as active doesn't change the successful bit.
+ // The successful bit will only be changed by setSlotAsUnbootable().
+ bootctrl.slot_info[slot].priority = kActivePriority;
+ bootctrl.slot_info[slot].tries_remaining = kActiveTries;
+
+ // Setting the current slot as active is a way to revert the operation that
+ // set *another* slot as active at the end of an updater. This is commonly
+ // used to cancel the pending update. We should only reset the verity_corrpted
+ // bit when attempting a new slot, otherwise the verity bit on the current
+ // slot would be flip.
+ if (slot != bootctrl_module->current_slot)
+ bootctrl.slot_info[slot].verity_corrupted = 0;
+
+ if (!SaveBootloaderControl(bootctrl_module->misc_device, &bootctrl))
+ return -1;
+
+ ALOGI("Slot %d is set as active", slot);
+
+ return 0;
+}
+
+int SetSlotAsUnbootable(boot_control_module_t* module, unsigned int slot) {
+ BootControlPrivate* const bootctrl_module =
+ reinterpret_cast<BootControlPrivate*>(module);
+
+ if (!IsInitialized(bootctrl_module))
+ return -1;
+
+ if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
+ // Invalid slot number.
+ return -1;
+ }
+
+ bootloader_control bootctrl;
+ if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl))
+ return -1;
+
+ // The only way to mark a slot as unbootable, regardless of the priority is to
+ // set the tries_remaining to 0.
+ bootctrl.slot_info[slot].successful_boot = 0;
+ bootctrl.slot_info[slot].tries_remaining = 0;
+ if (!SaveBootloaderControl(bootctrl_module->misc_device, &bootctrl))
+ return -1;
+
+ ALOGI("Slot %d is marked as unbootable", slot);
+
+ return 0;
+}
+
+int IsSlotBootable(struct boot_control_module* module, unsigned int slot) {
+ BootControlPrivate* const bootctrl_module =
+ reinterpret_cast<BootControlPrivate*>(module);
+
+ if (!IsInitialized(bootctrl_module)) return -1;
+
+ if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
+ // Invalid slot number.
+ return -1;
+ }
+
+ bootloader_control bootctrl;
+ if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl))
+ return -1;
+
+ return bootctrl.slot_info[slot].tries_remaining;
+}
+
+const char* GetSuffix(boot_control_module_t* module, unsigned int slot) {
+ BootControlPrivate* const bootctrl_module =
+ reinterpret_cast<BootControlPrivate*>(module);
+
+ if (!IsInitialized(bootctrl_module)) return NULL;
+
+ if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) return NULL;
+
+ return kSlotSuffixes[slot];
+}
+
+static hw_module_methods_t boot_control_module_methods = {
+ .open = NULL,
+};
+
+BootControlPrivate HAL_MODULE_INFO_SYM = {
+ .base = {
+ .common ={
+ .tag = HARDWARE_MODULE_TAG,
+ .module_api_version = 1,
+ .hal_api_version = 0,
+ .id = BOOT_CONTROL_HARDWARE_MODULE_ID,
+ .name = "AM57xx Boot control HAL",
+ .author = "Texas Instruments",
+ .methods = &boot_control_module_methods
+ },
+
+ .init = BootControlInit,
+ .getNumberSlots = GetNumberSlots,
+ .getCurrentSlot = GetCurrentSlot,
+ .markBootSuccessful = MarkBootSuccessful,
+ .setActiveBootSlot = SetActiveBootSlot,
+ .setSlotAsUnbootable = SetSlotAsUnbootable,
+ .isSlotBootable = IsSlotBootable,
+ .getSuffix = GetSuffix,
+ .isSlotMarkedSuccessful = IsSlotMarkedSuccessful
+ },
+
+ .initialized = false,
+ .misc_device = nullptr,
+ .num_slots = 0,
+ .current_slot = 0
+};