diff options
Diffstat (limited to 'bootctrl/boot_control.cc')
-rw-r--r-- | bootctrl/boot_control.cc | 351 |
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 +}; |