352 lines
11 KiB
C++
352 lines
11 KiB
C++
/*
|
|
* 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
|
|
};
|