普通文本  |  352行  |  10.76 KB

/*
 * 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
};