首页 > 技术文章 > bootctl hal

pyjetson 2021-06-30 20:15 原文

概述

用于A/B系统切换A/B的hal

源码解析

源码位置:android/hardware/interfaces/boot/1.1/default/boot_control/libboot_control.cpp文件

1. bootctl模块

1.1 IsSlotMarkedSuccessful-正常情况为true,OTA情况为false

bool BootControl::IsSlotMarkedSuccessful(unsigned int slot) {
  if (slot >= kMaxNumSlots || slot >= num_slots_) {
    // Invalid slot number.
    return false;
  }

  bootloader_control bootctrl;	// slot为0
  if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false;
// 当该slot成功启动的时候,该位置1;// 该slot能重启的次数
    // 表明此时处于OTA升级阶段或者是第一次刷固件的时候,需要进行checkpoint,这里为false;其他情况为true
  return bootctrl.slot_info[slot].successful_boot && bootctrl.slot_info[slot].tries_remaining;
}

1.2 Init-初始化操作

// Initialize the boot_control_private struct with the information from
// the bootloader_message buffer stored in |boot_ctrl|. Returns whether the
// initialization succeeded.
bool BootControl::Init() {
    // 只初始化一次
  if (initialized_) return true;

  // 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.
    // 这个是内核cmdline上传上来的,uboot设的
  std::string suffix_prop = android::base::GetProperty("ro.boot.slot_suffix", "");
  if (suffix_prop.empty()) {
    LOG(ERROR) << "Slot suffix property is not set";
    return false;
  }// _a为0
  current_slot_ = SlotSuffixToIndex(suffix_prop.c_str());

  std::string err;// fstab文件中配置的/misc,/dev/block/by-name/misc
  std::string device = get_bootloader_message_blk_device(&err);
  if (device.empty()) {
    LOG(ERROR) << "Could not find bootloader message block device: " << err;
    return false;
  }

  bootloader_control boot_ctrl;
    // 读misc分区
  if (!LoadBootloaderControl(device.c_str(), &boot_ctrl)) {
    LOG(ERROR) << "Failed to load bootloader control block";
    return false;
  }

  // Note that since there isn't a module unload function this memory is leaked.
  // We use `device` below sometimes, so it's not moved out of here.
  misc_device_ = device;
  initialized_ = true;

  // Validate the loaded data, otherwise we will destroy it and re-initialize it
  // with the current information.
    // crc32错误检测
  uint32_t computed_crc32 = BootloaderControlLECRC(&boot_ctrl);
    // 我们事先生成了一个misc.img,里面都是计算好的,所以是一致的
  if (boot_ctrl.crc32_le != computed_crc32) {
    LOG(WARNING) << "Invalid boot control found, expected CRC-32 0x" << std::hex << computed_crc32
                 << " but found 0x" << std::hex << boot_ctrl.crc32_le << ". Re-initializing.";
    InitDefaultBootloaderControl(this, &boot_ctrl);
    UpdateAndSaveBootloaderControl(device.c_str(), &boot_ctrl);
  }
// 事先生成的misc.img,不需要生成
  if (!InitMiscVirtualAbMessageIfNeeded()) {
    return false;
  }

  num_slots_ = boot_ctrl.nb_slot;	// 这个为2
  return true;
}

1.3 SlotSuffixToIndex

constexpr unsigned int kMaxNumSlots =
    sizeof(bootloader_control::slot_info) / sizeof(bootloader_control::slot_info[0]);	// 为4个
constexpr const char* kSlotSuffixes[kMaxNumSlots] = { "_a", "_b", "_c", "_d" };
int SlotSuffixToIndex(const char* suffix) {
  for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
    if (!strcmp(kSlotSuffixes[slot], suffix)) return slot;	// _a为0
  }
  return -1;
}

1.4 LoadBootloaderControl-读misc分区中的bootloader_message_ab数据结构

bool LoadBootloaderControl(const std::string& misc_device, bootloader_control* buffer) {
  android::base::unique_fd fd(open(misc_device.c_str(), O_RDONLY));
  if (fd.get() == -1) {
    PLOG(ERROR) << "failed to open " << misc_device;
    return false;
  }// constexpr off_t kBootloaderControlOffset = offsetof(bootloader_message_ab, slot_suffix);
    // 读bootloader_control信息
  if (lseek(fd, kBootloaderControlOffset, SEEK_SET) != kBootloaderControlOffset) {
    PLOG(ERROR) << "failed to lseek " << misc_device;
    return false;
  }
  if (!android::base::ReadFully(fd.get(), buffer, sizeof(bootloader_control))) {
    PLOG(ERROR) << "failed to read " << misc_device;
    return false;
  }
  return true;
}

1.5 get_bootloader_message_blk_device-返回fsatb中配置的misc分区的路径

android/bootable/recovery/bootloader_message/bootloader_message.cpp文件

std::string get_bootloader_message_blk_device(std::string* err) {
  std::string misc_blk_device = get_misc_blk_device(err);
  if (misc_blk_device.empty()) return "";
  if (!wait_for_device(misc_blk_device, err)) return "";
  return misc_blk_device;
}
std::string get_misc_blk_device(std::string* err) {
  if (g_misc_device_for_test.has_value() && !g_misc_device_for_test->empty()) {
    return *g_misc_device_for_test;
  }
  Fstab fstab;
  if (!ReadDefaultFstab(&fstab)) {
    *err = "failed to read default fstab";
    return "";
  }
  for (const auto& entry : fstab) {
      // fstab中配置的
    if (entry.mount_point == "/misc") {
      return entry.blk_device;
    }
  }

  *err = "failed to find /misc partition";
  return "";
}

1.6 BootloaderControlLECRC-计算CRC32

static uint32_t CRC32(const uint8_t* buf, size_t size) {
  static uint32_t crc_table[256];	// static类型的,只生成一次crc_table

  // Compute the CRC-32 table only once.
  if (!crc_table[1]) {
    for (uint32_t i = 0; i < 256; ++i) {
      uint32_t crc = i;
      for (uint32_t j = 0; j < 8; ++j) {
        uint32_t mask = -(crc & 1);
        crc = (crc >> 1) ^ (0xEDB88320 & mask);
      }
      crc_table[i] = crc;
    }
  }

  uint32_t ret = -1;
  for (size_t i = 0; i < size; ++i) {
    ret = (ret >> 8) ^ crc_table[(ret ^ buf[i]) & 0xFF];	// 计算CRC32
  }

  return ~ret;
}

// Return the little-endian representation of the CRC-32 of the first fields
// in |boot_ctrl| up to the crc32_le field.
uint32_t BootloaderControlLECRC(const bootloader_control* boot_ctrl) {
  return htole32(
      CRC32(reinterpret_cast<const uint8_t*>(boot_ctrl), offsetof(bootloader_control, crc32_le)));
}
bool InitMiscVirtualAbMessageIfNeeded() {
  std::string err;
  misc_virtual_ab_message message;
    // 读misc分区
  if (!ReadMiscVirtualAbMessage(&message, &err)) {
    LOG(ERROR) << "Could not read merge status: " << err;
    return false;
  }
// 这里直接返回
  if (message.version == MISC_VIRTUAL_AB_MESSAGE_VERSION &&
      message.magic == MISC_VIRTUAL_AB_MAGIC_HEADER) {
    // Already initialized.
    return true;
  }

  message = {};
  message.version = MISC_VIRTUAL_AB_MESSAGE_VERSION;
  message.magic = MISC_VIRTUAL_AB_MAGIC_HEADER;
  if (!WriteMiscVirtualAbMessage(message, &err)) {
    LOG(ERROR) << "Could not write merge status: " << err;
    return false;
  }
  return true;
}

1.7 GetCurrentSlot

unsigned int BootControl::GetCurrentSlot() {
  return current_slot_;	// 默认为0
}

2. Misc分区中相关的数据结构

源码位置:android/hardware/interfaces/boot/1.1/default/boot_control/include/private/boot_control_definition.h文件

2.1 bootloader_message_ab数据结构

struct bootloader_message_ab {
    struct bootloader_message message;	// 2048字节
    char slot_suffix[32];  // 为bootloader_control数据结构,相关的信息都存放于此
    char update_channel[128];
    char reserved[1888];
};

2.2 bootloader_control 数据结构

struct bootloader_control {
    // NUL terminated active slot suffix.
    char slot_suffix[4];  // 存放_a或者_b
    // Bootloader Control AB magic number (see BOOT_CTRL_MAGIC).
    uint32_t magic;  // 为0x42414342
    // Version of struct being used (see BOOT_CTRL_VERSION).
    uint8_t version;  // 为1
    // Number of slots being managed.
    uint8_t nb_slot : 3;  // slot的数目,一般为2(AB)
    // Number of times left attempting to boot recovery.
    uint8_t recovery_tries_remaining : 3;
    // Status of any pending snapshot merge of dynamic partitions.
    uint8_t merge_status : 3;
    // Ensure 4-bytes alignment for slot_info field.
    uint8_t reserved0[1];
    // Per-slot information.  Up to 4 slots.
    struct slot_metadata slot_info[4];  // 最大支持4个slot
    // Reserved for further use.
    uint8_t reserved1[8];
    // CRC32 of all 28 bytes preceding this field (little endian
    // format).
    uint32_t crc32_le;
} __attribute__((packed));

2.3 slot_metadata数据结构

struct slot_metadata {
    // Slot priority with 15 meaning highest priority, 1 lowest
    // priority and 0 the slot is unbootable.
    uint8_t priority : 4; // slot的优先级,优先级高的先启动,最高为15,最低为1。为0时不给启动
    // Number of times left attempting to boot this slot.
    uint8_t tries_remaining : 3; // 该slot能重启的次数
    // 1 if this slot has booted successfully, 0 otherwise.
    uint8_t successful_boot : 1; // 当该slot成功启动的时候,该位置1
    // 1 if this slot is corrupted from a dm-verity corruption, 0
    // otherwise.
    uint8_t verity_corrupted : 1; // 目前无人设此值1
    // Reserved for further use.
    uint8_t reserved : 7;
} __attribute__((packed));

2.4 misc分区数据

00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|	// bootloader_message数据结构
*
00000800  61 00 00 00 42 43 41 42  01 02 00 00 9f 00 7f 00  |a...BCAB........|	// slot_suffix bootloader_control
00000810  00 00 00 00 00 00 00 00  00 00 00 00 7c fd 96 02  |............|...|
00000820  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00008000  02 b0 0a 74 56 00 00 00  00 00 00 00 00 00 00 00  |...tV...........|
00008010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
01000000

问题

补充

参考

推荐阅读