From bca9aa5dd7aabf5c6b206a46ff73cbbfb35e151c Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Fri, 23 Jun 2023 23:21:24 +0100 Subject: [PATCH] more rework, cleanup, port to pure C The only C++ was for handling discovering and iterating over the partitions PER block device, this was implemented in a really overcomplicated way. Simplify things, port everything over to c11 and drop the libstdc++ requirement entirely. --- bootctrl.h | 129 +++++++++--------- bootctrl_impl.cpp => bootctrl_impl.c | 197 +++++++++++---------------- bootctrl_test.cpp => bootctrl_test.c | 1 + gpt-utils.cpp => gpt-utils.c | 97 ++++--------- gpt-utils.h | 101 +++++++------- meson.build | 14 +- qbootctl.cpp => qbootctl.c | 1 + ufs-bsg.cpp => ufs-bsg.c | 22 ++- 8 files changed, 238 insertions(+), 324 deletions(-) rename bootctrl_impl.cpp => bootctrl_impl.c (76%) rename bootctrl_test.cpp => bootctrl_test.c (98%) rename gpt-utils.cpp => gpt-utils.c (91%) rename qbootctl.cpp => qbootctl.c (99%) rename ufs-bsg.cpp => ufs-bsg.c (93%) diff --git a/bootctrl.h b/bootctrl.h index 03da69b..61700b5 100644 --- a/bootctrl.h +++ b/bootctrl.h @@ -19,85 +19,86 @@ #ifndef __BOOTCTRL_H__ #define __BOOTCTRL_H__ +#include + struct slot_info { bool active; bool bootable; - bool successful; + bool successful; }; struct boot_control_module { + /* + * (*getCurrentSlot)() returns the value letting the system know + * whether the current slot is A or B. The meaning of A and B is + * left up to the implementer. It is assumed that if the current slot + * is A, then the block devices underlying B can be accessed directly + * without any risk of corruption. + * The returned value is always guaranteed to be strictly less than the + * value returned by getNumberSlots. Slots start at 0 and + * finish at getNumberSlots() - 1 + */ + unsigned (*getCurrentSlot)(); - /* - * (*getCurrentSlot)() returns the value letting the system know - * whether the current slot is A or B. The meaning of A and B is - * left up to the implementer. It is assumed that if the current slot - * is A, then the block devices underlying B can be accessed directly - * without any risk of corruption. - * The returned value is always guaranteed to be strictly less than the - * value returned by getNumberSlots. Slots start at 0 and - * finish at getNumberSlots() - 1 - */ - unsigned (*getCurrentSlot)(); - - /* - * (*markBootSuccessful)() marks the specified slot - * as boot successful - * - * Returns 0 on success, -errno on error. - */ - int (*markBootSuccessful)(unsigned slot); + /* + * (*markBootSuccessful)() marks the specified slot + * as boot successful + * + * Returns 0 on success, -errno on error. + */ + int (*markBootSuccessful)(unsigned slot); - /* - * (*setActiveBootSlot)() marks the slot passed in parameter as - * the active boot slot (see getCurrentSlot for an explanation - * of the "slot" parameter). This overrides any previous call to - * setSlotAsUnbootable. - * Returns 0 on success, -errno on error. - */ - int (*setActiveBootSlot)(unsigned slot); + /* + * (*setActiveBootSlot)() marks the slot passed in parameter as + * the active boot slot (see getCurrentSlot for an explanation + * of the "slot" parameter). This overrides any previous call to + * setSlotAsUnbootable. + * Returns 0 on success, -errno on error. + */ + int (*setActiveBootSlot)(unsigned slot); - /* - * (*setSlotAsUnbootable)() marks the slot passed in parameter as - * an unbootable. This can be used while updating the contents of the slot's - * partitions, so that the system will not attempt to boot a known bad set up. - * Returns 0 on success, -errno on error. - */ - int (*setSlotAsUnbootable)(unsigned slot); + /* + * (*setSlotAsUnbootable)() marks the slot passed in parameter as + * an unbootable. This can be used while updating the contents of the slot's + * partitions, so that the system will not attempt to boot a known bad set up. + * Returns 0 on success, -errno on error. + */ + int (*setSlotAsUnbootable)(unsigned slot); - /* - * (*isSlotBootable)() returns if the slot passed in parameter is - * bootable. Note that slots can be made unbootable by both the - * bootloader and by the OS using setSlotAsUnbootable. - * Returns 1 if the slot is bootable, 0 if it's not, and -errno on - * error. - */ - int (*isSlotBootable)(unsigned slot); + /* + * (*isSlotBootable)() returns if the slot passed in parameter is + * bootable. Note that slots can be made unbootable by both the + * bootloader and by the OS using setSlotAsUnbootable. + * Returns 1 if the slot is bootable, 0 if it's not, and -errno on + * error. + */ + int (*isSlotBootable)(unsigned slot); - /* - * (*getSuffix)() returns the string suffix used by partitions that - * correspond to the slot number passed in parameter. The returned string - * is expected to be statically allocated and not need to be freed. - * Returns NULL if slot does not match an existing slot. - */ - const char* (*getSuffix)(unsigned slot); + /* + * (*getSuffix)() returns the string suffix used by partitions that + * correspond to the slot number passed in parameter. The returned string + * is expected to be statically allocated and not need to be freed. + * Returns NULL if slot does not match an existing slot. + */ + const char *(*getSuffix)(unsigned slot); - /* - * (*isSlotMarkedSucessful)() returns if the slot passed in parameter has - * been marked as successful using markBootSuccessful. - * Returns 1 if the slot has been marked as successful, 0 if it's - * not the case, and -errno on error. - */ - int (*isSlotMarkedSuccessful)(unsigned slot); + /* + * (*isSlotMarkedSucessful)() returns if the slot passed in parameter has + * been marked as successful using markBootSuccessful. + * Returns 1 if the slot has been marked as successful, 0 if it's + * not the case, and -errno on error. + */ + int (*isSlotMarkedSuccessful)(unsigned slot); - /** - * Returns the active slot to boot into on the next boot. If - * setActiveBootSlot() has been called, the getter function should return - * the same slot as the one provided in the last setActiveBootSlot() call. - */ - unsigned (*getActiveBootSlot)(); + /** + * Returns the active slot to boot into on the next boot. If + * setActiveBootSlot() has been called, the getter function should return + * the same slot as the one provided in the last setActiveBootSlot() call. + */ + unsigned (*getActiveBootSlot)(); }; extern const struct boot_control_module bootctl; extern const struct boot_control_module bootctl_test; -#endif // __BOOTCTRL_H__ +#endif // __BOOTCTRL_H__ diff --git a/bootctrl_impl.cpp b/bootctrl_impl.c similarity index 76% rename from bootctrl_impl.cpp rename to bootctrl_impl.c index 0ff0af9..d6bd9d0 100644 --- a/bootctrl_impl.cpp +++ b/bootctrl_impl.c @@ -16,21 +16,18 @@ * along with this program. If not, see . */ +#include #include #include #include #include -#include -#include -#include +#include #include #include #include -#include #include #include #include -#include #include "gpt-utils.h" #include "ufs-bsg.h" @@ -57,7 +54,6 @@ (*(pentry + AB_FLAG_OFFSET) & ~AB_PARTITION_ATTR_SLOT_ACTIVE); \ }) -using namespace std; const char *slot_suffix_arr[] = { AB_SLOT_A_SUFFIX, AB_SLOT_B_SUFFIX, NULL }; enum part_attr_type { @@ -98,12 +94,12 @@ error: } // Get the value of one of the attribute fields for a partition. -static int get_partition_attribute(struct gpt_disk *disk, char *partname, +static int get_partition_attribute(struct gpt_disk *disk, const char *partname, enum part_attr_type part_attr) { - uint8_t *pentry = nullptr; + uint8_t *pentry = NULL; int retval = -1; - uint8_t *attr = nullptr; + uint8_t *attr = NULL; if (!partname) return -1; @@ -151,13 +147,12 @@ static int update_slot_attribute(struct gpt_disk *disk, const char *slot, unsigned int i = 0; char buf[PATH_MAX]; struct stat st; - uint8_t *pentry = nullptr; - uint8_t *pentry_bak = nullptr; + uint8_t *pentry = NULL; + uint8_t *pentry_bak = NULL; int rc = -1; - uint8_t *attr = nullptr; - uint8_t *attr_bak = nullptr; + uint8_t *attr = NULL; + uint8_t *attr_bak = NULL; char partName[MAX_GPT_NAME_SIZE + 1] = { 0 }; - static const char ptn_list[][MAX_GPT_NAME_SIZE - 1] = { AB_PTN_LIST }; int slot_name_valid = 0; char devpath[PATH_MAX] = { 0 }; @@ -166,7 +161,7 @@ static int update_slot_attribute(struct gpt_disk *disk, const char *slot, return -1; } - for (i = 0; slot_suffix_arr[i] != nullptr; i++) { + for (i = 0; slot_suffix_arr[i] != NULL; i++) { if (!strncmp(slot, slot_suffix_arr[i], strlen(slot_suffix_arr[i]))) slot_name_valid = 1; } @@ -176,10 +171,10 @@ static int update_slot_attribute(struct gpt_disk *disk, const char *slot, return -1; } - for (i = 0; i < ARRAY_SIZE(ptn_list); i++) { + for (i = 0; i < ARRAY_SIZE(g_all_ptns); i++) { memset(buf, '\0', sizeof(buf)); // Check if A/B versions of this ptn exist - snprintf(buf, sizeof(buf) - 1, "%s/%s%s", BOOT_DEV_DIR, ptn_list[i], + snprintf(buf, sizeof(buf) - 1, "%s/%s%s", BOOT_DEV_DIR, g_all_ptns[i], AB_SLOT_A_SUFFIX); if (stat(buf, &st) < 0) { // partition does not have _a version @@ -187,7 +182,7 @@ static int update_slot_attribute(struct gpt_disk *disk, const char *slot, } memset(buf, '\0', sizeof(buf)); - snprintf(buf, sizeof(buf) - 1, "%s/%s%s", BOOT_DEV_DIR, ptn_list[i], + snprintf(buf, sizeof(buf) - 1, "%s/%s%s", BOOT_DEV_DIR, g_all_ptns[i], AB_SLOT_B_SUFFIX); if (stat(buf, &st) < 0) { // partition does not have _b version @@ -195,11 +190,11 @@ static int update_slot_attribute(struct gpt_disk *disk, const char *slot, } memset(partName, '\0', sizeof(partName)); - snprintf(partName, sizeof(partName) - 1, "%s%s", ptn_list[i], slot); + snprintf(partName, sizeof(partName) - 1, "%s%s", g_all_ptns[i], slot); // If the current partition is for a different disk (e.g. /dev/sde when the current disk is /dev/sda) // Then commit the current disk - if (!partition_is_for_disk(partName, disk, devpath, sizeof(devpath))) { + if (partition_is_for_disk(disk, partName, devpath, sizeof(devpath)) != 0) { if (!gpt_disk_commit(disk)) { fprintf(stderr, "%s: Failed to commit disk\n", __func__); return -1; @@ -265,8 +260,8 @@ static int update_slot_attribute(struct gpt_disk *disk, const char *slot, */ unsigned get_number_slots() { - struct dirent *de = nullptr; - DIR *dir_bootdev = nullptr; + struct dirent *de = NULL; + DIR *dir_bootdev = NULL; static int slot_count = 0; // If we've already counted the slots, return the cached value. @@ -274,8 +269,8 @@ unsigned get_number_slots() if (slot_count > 0) return slot_count; - static_assert(AB_SLOT_A_SUFFIX[0] == '_', "Breaking change to slot A suffix"); - static_assert(AB_SLOT_B_SUFFIX[0] == '_', "Breaking change to slot B suffix"); + assert(AB_SLOT_A_SUFFIX[0] == '_'); + assert(AB_SLOT_B_SUFFIX[0] == '_'); dir_bootdev = opendir(BOOTDEV_DIR); // Shouldn't this be an assert? @@ -344,7 +339,7 @@ static unsigned int get_current_slot_from_kernel_cmdline() // Iterate through a list of partitons named as boot+suffix // and see which one is currently active. - for (i = 0; slot_suffix_arr[i] != nullptr; i++) { + for (i = 0; slot_suffix_arr[i] != NULL; i++) { if (!strncmp(bootSlotProp, slot_suffix_arr[i], strlen(slot_suffix_arr[i]))) { // printf("%s current_slot = %d\n", __func__, i); return i; @@ -413,68 +408,66 @@ const char *get_suffix(unsigned slot) return slot_suffix_arr[slot]; } + // The argument here is a vector of partition names(including the slot suffix) // that lie on a single disk -static int boot_ctl_set_active_slot_for_partitions(struct gpt_disk *disk, vector part_list, +static int boot_ctl_set_active_slot_for_partitions(struct gpt_disk *disk, const char ptn_list[][MAX_GPT_NAME_SIZE], int len, unsigned slot) { char buf[PATH_MAX] = { 0 }; - char slotA[MAX_GPT_NAME_SIZE + 1] = { 0 }; - char slotB[MAX_GPT_NAME_SIZE + 1] = { 0 }; + const char *slotA; + char slotB[MAX_GPT_NAME_SIZE] = { 0 }; char active_guid[TYPE_GUID_SIZE + 1] = { 0 }; char inactive_guid[TYPE_GUID_SIZE + 1] = { 0 }; - int rc; + int rc, i; // Pointer to the partition entry of current 'A' partition - uint8_t *pentryA = nullptr; - uint8_t *pentryA_bak = nullptr; + uint8_t *pentryA = NULL; + uint8_t *pentryA_bak = NULL; // Pointer to partition entry of current 'B' partition - uint8_t *pentryB = nullptr; - uint8_t *pentryB_bak = nullptr; + uint8_t *pentryB = NULL; + uint8_t *pentryB_bak = NULL; struct stat st; - vector::iterator partition_iterator; LOGD("Marking slot %s as active:\n", slot_suffix_arr[slot]); - for (partition_iterator = part_list.begin(); partition_iterator != part_list.end(); - partition_iterator++) { + for (i = 0, slotA = ptn_list[0]; i < len; slotA = ptn_list[++i]) { // Chop off the slot suffix from the partition name to // make the string easier to work with. - string prefix = *partition_iterator; - LOGD("Part: %s\n", prefix.c_str()); - if (prefix.size() < (strlen(AB_SLOT_A_SUFFIX) + 1)) { - fprintf(stderr, "Invalid partition name: %s\n", prefix.c_str()); + LOGD("Part: %s\n", slotA); + int n = strlen(slotA) - strlen(AB_SLOT_A_SUFFIX); + if (n + 1 < 3 || n + 1 > MAX_GPT_NAME_SIZE) { + fprintf(stderr, "Invalid partition name: %s\n", slotA); return -1; } - prefix.resize(prefix.size() - strlen(AB_SLOT_A_SUFFIX)); - // Check if A/B versions of this ptn exist - snprintf(buf, sizeof(buf) - 1, "%s/%s%s", BOOT_DEV_DIR, prefix.c_str(), - AB_SLOT_A_SUFFIX); - LOGD("\t_a Path: '%s'\n", buf); - rc = stat(buf, &st); - if (rc < 0) { - fprintf(stderr, "Failed to stat() path: %d: %s\n", rc, strerror(errno)); - continue; - } - memset(buf, '\0', sizeof(buf)); - snprintf(buf, sizeof(buf) - 1, "%s/%s%s", BOOT_DEV_DIR, prefix.c_str(), - AB_SLOT_B_SUFFIX); - // LOGD("\t_b Path: '%s'\n", buf); - rc = stat(buf, &st); - if (rc < 0) { - fprintf(stderr, "Failed to stat() path: %d: %s\n", rc, strerror(errno)); + + memset(slotB, 0, sizeof(slotB)); + strncat(slotB, slotA, n); + strncat(slotB + n, AB_SLOT_B_SUFFIX, 3); + + rc = snprintf(buf, sizeof(buf) - 1, "%s", BOOT_DEV_DIR); + snprintf(buf + rc, PATH_MAX - rc, "/%s", slotA); + LOGD("Checking for partition %s\n", buf); + if (stat(buf, &st)) { + if (!strcmp(slotA, "boot_a") || !strcmp(slotA, "dtbo_a")) { + fprintf(stderr, "Couldn't find required partition %s\n", slotA); + return -1; + } + // Not every device has every partition continue; } - memset(slotA, 0, sizeof(slotA)); - memset(slotB, 0, sizeof(slotA)); - snprintf(slotA, sizeof(slotA) - 1, "%s%s", prefix.c_str(), AB_SLOT_A_SUFFIX); - snprintf(slotB, sizeof(slotB) - 1, "%s%s", prefix.c_str(), AB_SLOT_B_SUFFIX); - - // Get the disk containing the partitions that were passed in. - // All partitions passed in must lie on the same disk. - if (!gpt_disk_is_valid(disk)) { - if (gpt_disk_get_disk_info(slotA, disk) < 0) - return -1; + + snprintf(buf + rc, PATH_MAX - rc, "/%s", slotB); + if (stat(buf, &st)) { + fprintf(stderr, "Partition %s does not exist\n", slotB); + return -1; } + + // Get the disk containing this partition. This only + // actually re-initialises disk if this partition refers + // to a different block device than the last one. + if (gpt_disk_get_disk_info(slotA, disk) < 0) + return -1; + // Get partition entry for slot A & B from the primary // and backup tables. pentryA = gpt_disk_get_pentry(disk, slotA, PRIMARY_GPT); @@ -484,7 +477,7 @@ static int boot_ctl_set_active_slot_for_partitions(struct gpt_disk *disk, vector if (!pentryA || !pentryA_bak || !pentryB || !pentryB_bak) { // None of these should be NULL since we have already // checked for A & B versions earlier. - fprintf(stderr, "Slot pentries for %s not found.\n", prefix.c_str()); + fprintf(stderr, "Slot pentries for %s not found.\n", slotA); return -1; } LOGD("\tAB attr (A): 0x%x (backup: 0x%x)\n", *(uint16_t *)(pentryA + AB_FLAG_OFFSET), @@ -558,86 +551,50 @@ unsigned get_active_boot_slot() int set_active_boot_slot(unsigned slot) { - map> ptn_map; - vector ptn_vec; - const char ptn_list[][MAX_GPT_NAME_SIZE] = { AB_PTN_LIST }; + enum boot_chain chain = (enum boot_chain)slot; struct gpt_disk disk = { 0 }; - uint32_t i; - int rc = -1; - map>::iterator map_iter; + int rc; bool ismmc; if (boot_control_check_slot_sanity(slot)) { fprintf(stderr, "%s: Bad arguments\n", __func__); - goto out; + return -1; } ismmc = gpt_utils_is_partition_backed_by_emmc(PTN_XBL AB_SLOT_A_SUFFIX); + // Do this *before* updating all the slot attributes + // to make sure we can if (!ismmc && ufs_bsg_dev_open() < 0) { - goto out; + return -1; } - // The partition list just contains prefixes(without the _a/_b) of the - // partitions that support A/B. In order to get the layout we need the - // actual names. To do this we append the slot suffix to every member - // in the list. - for (i = 0; i < ARRAY_SIZE(ptn_list); i++) { - // XBL is handled differrently for ufs devices so ignore it - if (!ismmc && !strncmp(ptn_list[i], PTN_XBL, strlen(PTN_XBL))) - continue; - // The partition list will be the list of _a partitions - string cur_ptn = ptn_list[i]; - cur_ptn.append(AB_SLOT_A_SUFFIX); - ptn_vec.push_back(cur_ptn); - } + rc = boot_ctl_set_active_slot_for_partitions(&disk, g_all_ptns, ARRAY_SIZE(g_all_ptns), slot); - // The partition map gives us info in the following format: - // [path_to_block_device_1]--> - // [path_to_block_device_2]--> - // ... - // ... - // eg: - // [/dev/block/sdb]---> - if (gpt_utils_get_partition_map(ptn_vec, ptn_map)) { - fprintf(stderr, "%s: Failed to get partition map\n", __func__); + if (rc) { + fprintf(stderr, "%s: Failed to set active slot for partitions \n", __func__); goto out; } - for (map_iter = ptn_map.begin(); map_iter != ptn_map.end(); map_iter++) { - if (map_iter->second.size() < 1) - continue; - if (boot_ctl_set_active_slot_for_partitions(&disk, map_iter->second, slot)) { - fprintf(stderr, "%s: Failed to set active slot for partitions \n", __func__); - goto out; - } - } // EMMC doesn't need attributes to be set. if (ismmc) - return 0; + goto out; - if (slot == 0) { - // Set xbl_a as the boot lun - rc = gpt_utils_set_xbl_boot_partition(NORMAL_BOOT); - } else if (slot == 1) { - // Set xbl_b as the boot lun - rc = gpt_utils_set_xbl_boot_partition(BACKUP_BOOT); - } else { - // Something has gone terribly terribly wrong - fprintf(stderr, "%s: Unknown slot suffix!\n", __func__); + if (chain > BACKUP_BOOT) { + fprintf(stderr, "%s: Unknown slot %d!\n", __func__, slot); + rc = -1; goto out; } + + rc = gpt_utils_set_xbl_boot_partition(chain); if (rc) { fprintf(stderr, "%s: Failed to switch xbl boot partition\n", __func__); goto out; } - gpt_disk_free(&disk); - return 0; - out: gpt_disk_free(&disk); - return -1; + return rc; } int set_slot_as_unbootable(unsigned slot) diff --git a/bootctrl_test.cpp b/bootctrl_test.c similarity index 98% rename from bootctrl_test.cpp rename to bootctrl_test.c index 284f5cf..bd3c6bb 100644 --- a/bootctrl_test.cpp +++ b/bootctrl_test.c @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +#include #include "bootctrl.h" struct test_state { diff --git a/gpt-utils.cpp b/gpt-utils.c similarity index 91% rename from gpt-utils.cpp rename to gpt-utils.c index 59ce60f..38acc39 100644 --- a/gpt-utils.cpp +++ b/gpt-utils.c @@ -30,8 +30,10 @@ #define _LARGEFILE64_SOURCE /* enable lseek64() */ -#include "assert.h" +#include #include +#include +#include #include #include #include @@ -40,14 +42,11 @@ #include #include #include -#include #include #include -#include #include #include #include -#include #include #include "gpt-utils.h" @@ -75,9 +74,6 @@ #define LUN_NAME_START_LOC (sizeof("/dev/") - 1) #define BOOT_LUN_A_ID 1 #define BOOT_LUN_B_ID 2 -/****************************************************************************** - * MACROS - ******************************************************************************/ #define GET_4_BYTES(ptr) \ ((uint32_t) * ((uint8_t *)(ptr)) | ((uint32_t) * ((uint8_t *)(ptr) + 1) << 8) | \ @@ -97,10 +93,6 @@ *((uint8_t *)(ptr) + 2) = ((y) >> 16) & 0xff; \ *((uint8_t *)(ptr) + 3) = ((y) >> 24) & 0xff; -/****************************************************************************** - * TYPES - ******************************************************************************/ -using namespace std; enum gpt_state { GPT_OK = 0, GPT_BAD_SIGNATURE, GPT_BAD_CRC }; // List of LUN's containing boot critical images. // Required in the case of UFS devices @@ -109,9 +101,6 @@ struct update_data { uint32_t num_valid_entries; }; -/****************************************************************************** - * FUNCTIONS - ******************************************************************************/ void DumpHex(const void *data, size_t size) { char ascii[17]; @@ -222,7 +211,7 @@ static uint8_t *gpt_pentry_seek(const char *ptn_name, const uint8_t *pentries_st return (uint8_t *)(pentry_name - PARTITION_NAME_OFFSET); } - return nullptr; + return NULL; } // Defined in ufs-bsg.cpp @@ -249,7 +238,7 @@ int gpt_utils_set_xbl_boot_partition(enum boot_chain chain) { struct stat st; uint8_t boot_lun_id = 0; - const char *boot_dev = nullptr; + const char *boot_dev = NULL; (void)st; (void)boot_dev; @@ -332,42 +321,6 @@ static int get_dev_path_from_partition_name(const char *partname, char *buf, siz return 0; } -int gpt_utils_get_partition_map(vector &ptn_list, map> &partition_map) -{ - char devpath[PATH_MAX] = { '\0' }; - map>::iterator it; - - if (ptn_list.size() < 1) { - fprintf(stderr, "%s: Invalid ptn list\n", __func__); - return -1; - } - - // Go through the passed in list - for (uint32_t i = 0; i < ptn_list.size(); i++) { - // Key in the map is the path to the device that holds the - // partition - if (get_dev_path_from_partition_name(ptn_list[i].c_str(), devpath, sizeof(devpath))) { - // Not necessarily an error. The partition may just - // not be present. - continue; - } - - string path = devpath; - it = partition_map.find(path); - if (it != partition_map.end()) { - it->second.push_back(ptn_list[i]); - } else { - vector str_vec; - str_vec.push_back(ptn_list[i]); - partition_map.insert(pair>(path, str_vec)); - } - - memset(devpath, '\0', sizeof(devpath)); - } - - return 0; -} - // Get the block size of the disk represented by decsriptor fd static uint32_t gpt_get_block_size(int fd) { @@ -432,7 +385,7 @@ error: // Read out the GPT headers for the disk that contains the partition partname static int gpt_get_headers(const char *partname, uint8_t **primary, uint8_t **backup) { - uint8_t *hdr = nullptr; + uint8_t *hdr = NULL; char devpath[PATH_MAX] = { 0 }; off_t hdr_offset = 0; uint32_t block_size = 0; @@ -505,7 +458,7 @@ static uint8_t *gpt_get_pentry_arr(uint8_t *hdr, int fd) uint32_t pentry_size = 0; uint32_t block_size = 0; uint32_t pentries_arr_size = 0; - uint8_t *pentry_arr = nullptr; + uint8_t *pentry_arr = NULL; int rc = 0; if (!hdr) { fprintf(stderr, "%s: Invalid header\n", __func__); @@ -537,7 +490,7 @@ static uint8_t *gpt_get_pentry_arr(uint8_t *hdr, int fd) error: if (pentry_arr) free(pentry_arr); - return nullptr; + return NULL; } static int gpt_set_pentry_arr(uint8_t *hdr, int fd, uint8_t *arr) @@ -586,19 +539,19 @@ void gpt_disk_free(struct gpt_disk *disk) if (disk->hdr) { free(disk->hdr); - disk->hdr = nullptr; + disk->hdr = NULL; } if (disk->hdr_bak) { free(disk->hdr_bak); - disk->hdr_bak = nullptr; + disk->hdr_bak = NULL; } if (disk->pentry_arr) { free(disk->pentry_arr); - disk->pentry_arr = nullptr; + disk->pentry_arr = NULL; } if (disk->pentry_arr_bak) { free(disk->pentry_arr_bak); - disk->pentry_arr_bak = nullptr; + disk->pentry_arr_bak = NULL; } disk->is_initialized = 0; @@ -616,14 +569,14 @@ bool gpt_disk_is_valid(struct gpt_disk *disk) * and populate the blockdev path. * e.g. for /dev/disk/by-partlabel/system_a blockdev would be /dev/sda */ -bool partition_is_for_disk(const char *part, struct gpt_disk *disk, char *blockdev, int blockdev_len) +int partition_is_for_disk(const struct gpt_disk *disk, const char *part, char *blockdev, int blockdev_len) { int ret; ret = get_dev_path_from_partition_name(part, blockdev, blockdev_len); if (ret) { fprintf(stderr, "%s: Failed to resolve path for %s\n", __func__, part); - return false; + return -1; } if (!strcmp(blockdev, disk->devpath)) { @@ -639,7 +592,7 @@ bool partition_is_for_disk(const char *part, struct gpt_disk *disk, char *blockd */ int gpt_disk_get_disk_info(const char *dev, struct gpt_disk *disk) { - int fd = -1; + int fd = -1, rc; uint32_t gpt_header_size = 0; char devpath[PATH_MAX] = { 0 }; @@ -648,8 +601,14 @@ int gpt_disk_get_disk_info(const char *dev, struct gpt_disk *disk) goto error; } - if (partition_is_for_disk(dev, disk, devpath, sizeof(devpath))) { + rc = partition_is_for_disk(disk, dev, devpath, sizeof(devpath)); + + if (rc > 0) return 0; + + if (rc < 0) { + fprintf(stderr, "%s: Failed to resolve path for %s\n", __func__, dev); + return -1; } if (disk->is_initialized == GPT_DISK_INIT_MAGIC) { @@ -666,8 +625,8 @@ int gpt_disk_get_disk_info(const char *dev, struct gpt_disk *disk) goto error; } - assert(disk->hdr != nullptr); - assert(disk->hdr_bak != nullptr); + assert(disk->hdr != NULL); + assert(disk->hdr_bak != NULL); gpt_header_size = GET_4_BYTES(disk->hdr + HEADER_SIZE_OFFSET); @@ -683,14 +642,14 @@ int gpt_disk_get_disk_info(const char *dev, struct gpt_disk *disk) goto error; } - assert(disk->pentry_arr == nullptr); + assert(disk->pentry_arr == NULL); disk->pentry_arr = gpt_get_pentry_arr(disk->hdr, fd); if (!disk->pentry_arr) { fprintf(stderr, "%s: Failed to obtain partition entry array\n", __func__); goto error; } - assert(disk->pentry_arr_bak == nullptr); + assert(disk->pentry_arr_bak == NULL); disk->pentry_arr_bak = gpt_get_pentry_arr(disk->hdr_bak, fd); if (!disk->pentry_arr_bak) { fprintf(stderr, "%s: Failed to obtain backup partition entry array\n", __func__); @@ -714,10 +673,10 @@ error: // Get pointer to partition entry from a allocated gpt_disk structure uint8_t *gpt_disk_get_pentry(struct gpt_disk *disk, const char *partname, enum gpt_instance instance) { - uint8_t *ptn_arr = nullptr; + uint8_t *ptn_arr = NULL; if (!disk || !partname || disk->is_initialized != GPT_DISK_INIT_MAGIC) { fprintf(stderr, "%s: disk handle not initialised\n", __func__); - return nullptr; + return NULL; } ptn_arr = (instance == PRIMARY_GPT) ? disk->pentry_arr : disk->pentry_arr_bak; return (gpt_pentry_seek(partname, ptn_arr, ptn_arr + disk->pentry_arr_size, diff --git a/gpt-utils.h b/gpt-utils.h index a4eb9e4..4379189 100644 --- a/gpt-utils.h +++ b/gpt-utils.h @@ -29,13 +29,11 @@ #ifndef __GPT_UTILS_H__ #define __GPT_UTILS_H__ -#include -#include -#include #ifdef __cplusplus extern "C" { #endif #include +#include #include #include #include @@ -62,8 +60,8 @@ extern "C" { #define PARTITION_NAME_OFFSET 56 #define MAX_GPT_NAME_SIZE 72 -//Bit 48 onwords in the attribute field are the ones where we are allowed to -//store our AB attributes. +// Bit 48 onwords in the attribute field are the ones where we are allowed to +// store our AB attributes. #define AB_FLAG_OFFSET (ATTRIBUTE_FLAG_OFFSET + 6) #define GPT_DISK_INIT_MAGIC 0xABCD #define AB_PARTITION_ATTR_SLOT_ACTIVE (0x1 << 2) @@ -76,14 +74,22 @@ extern "C" { #define AB_SLOT_A_SUFFIX "_a" #define AB_SLOT_B_SUFFIX "_b" #define PTN_XBL "xbl" -#define PTN_SWAP_LIST \ - PTN_XBL, "abl", "aop", "apdp", "cmnlib", "cmnlib64", "devcfg", "dtbo", \ - "hyp", "keymaster", "msadp", "qupfw", "storsec", "tz", \ - "vbmeta", "vbmeta_system", "xbl_config" - -#define AB_PTN_LIST \ - PTN_SWAP_LIST, "boot", "system", "vendor", "modem", "system_ext", \ - "product" +// XBL is not included because the slot attributes are meaningless there +// *which* XBL partition is active is determined via the UFS bBootLunEn field +// as it needs to be handled by PBL +#define PTN_SWAP_LIST \ + "abl_a", "aop_a", "apdp_a", "cmnlib_a", "cmnlib64_a", "devcfg_a", "dtbo_a", \ + "hyp_a", "keymaster_a", "msadp_a", "qupfw_a", "storsec_a", "tz_a", \ + "vbmeta_a", "vbmeta_system_a" + +static const char g_all_ptns[][MAX_GPT_NAME_SIZE] = { + PTN_SWAP_LIST, "boot_a", "system", + "vendor_a", "modem_a", "system_ext_a", "product_a" +}; + +// No more than /dev/sdk +#define MAX_BLOCK_DEVICES 10 + #define BOOT_DEV_DIR "/dev/disk/by-partlabel" #define EMMC_DEVICE "/dev/mmcblk0" @@ -95,78 +101,69 @@ enum gpt_instance { PRIMARY_GPT = 0, SECONDARY_GPT }; enum boot_chain { NORMAL_BOOT = 0, BACKUP_BOOT }; struct gpt_disk { - //GPT primary header + // GPT primary header uint8_t *hdr; - //primary header crc + // primary header crc uint32_t hdr_crc; - //GPT backup header + // GPT backup header uint8_t *hdr_bak; - //backup header crc + // backup header crc uint32_t hdr_bak_crc; - //Partition entries array + // Partition entries array uint8_t *pentry_arr; - //Partition entries array for backup table + // Partition entries array for backup table uint8_t *pentry_arr_bak; - //Size of the pentry array + // Size of the pentry array uint32_t pentry_arr_size; - //Size of each element in the pentry array + // Size of each element in the pentry array uint32_t pentry_size; - //CRC of the partition entry array + // CRC of the partition entry array uint32_t pentry_arr_crc; - //CRC of the backup partition entry array + // CRC of the backup partition entry array uint32_t pentry_arr_bak_crc; - //Path to block dev representing the disk + // Path to block dev representing the disk char devpath[PATH_MAX]; - //Block size of disk + // Block size of disk uint32_t block_size; uint32_t is_initialized; }; -//GPT disk methods +// GPT disk methods bool gpt_disk_is_valid(struct gpt_disk *disk); -//Free previously allocated gpt_disk struct +// Free previously allocated gpt_disk struct void gpt_disk_free(struct gpt_disk *disk); -//Get the details of the disk holding the partition whose name -//is passed in via dev +// Get the details of the disk holding the partition whose name +// is passed in via dev int gpt_disk_get_disk_info(const char *dev, struct gpt_disk *disk); -bool partition_is_for_disk(const char *part, struct gpt_disk *disk, char *blockdev, int blockdev_len); +int partition_is_for_disk(const struct gpt_disk *disk, const char *part, char *blockdev, int blockdev_len); -//Get pointer to partition entry from a allocated gpt_disk structure +// Get pointer to partition entry from a allocated gpt_disk structure uint8_t *gpt_disk_get_pentry(struct gpt_disk *disk, const char *partname, enum gpt_instance instance); -//Write the contents of struct gpt_disk back to the actual disk +// Write the contents of struct gpt_disk back to the actual disk int gpt_disk_commit(struct gpt_disk *disk); -//Swtich betwieen using either the primary or the backup -//boot LUN for boot. This is required since UFS boot partitions -//cannot have a backup GPT which is what we use for failsafe -//updates of the other 'critical' partitions. This function will -//not be invoked for emmc targets and on UFS targets is only required -//to be invoked for XBL. +// Swtich betwieen using either the primary or the backup +// boot LUN for boot. This is required since UFS boot partitions +// cannot have a backup GPT which is what we use for failsafe +// updates of the other 'critical' partitions. This function will +// not be invoked for emmc targets and on UFS targets is only required +// to be invoked for XBL. // -//The algorithm to do this is as follows: -//- Find the real block device(eg: /dev/block/sdb) that corresponds +// The algorithm to do this is as follows: +// - Find the real block device(eg: /dev/block/sdb) that corresponds // to the /dev/block/bootdevice/by-name/xbl(bak) symlink // -//- Once we have the block device 'node' name(sdb in the above example) +// - Once we have the block device 'node' name(sdb in the above example) // use this node to to locate the scsi generic device that represents // it by checking the file /sys/block/sdb/device/scsi_generic/sgY // -//- Once we locate sgY we call the query ioctl on /dev/sgy to switch -//the boot lun to either LUNA or LUNB +// - Once we locate sgY we call the query ioctl on /dev/sgy to switch +// the boot lun to either LUNA or LUNB int gpt_utils_set_xbl_boot_partition(enum boot_chain chain); -//Given a vector of partition names as a input and a reference to a map, -//populate the map to indicate which physical disk each of the partitions -//sits on. The key in the map is the path to the block device where the -//partition lies and the value is a vector of strings indicating which of -//the passed in partition names sits on that device. -int gpt_utils_get_partition_map( - std::vector &partition_list, - std::map > &partition_map); - bool gpt_utils_is_partition_backed_by_emmc(const char *part); #ifdef __cplusplus } diff --git a/meson.build b/meson.build index afe7d3c..d112c64 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ -project('qbootctl', 'cpp', default_options : ['c_std=c11', 'cpp_std=c++17']) +project('qbootctl', 'c', default_options : ['c_std=c11']) -cc = meson.get_compiler('cpp') +cc = meson.get_compiler('c') deps = [ dependency('zlib'), @@ -11,11 +11,11 @@ if not cc.has_header('linux/bsg.h') endif src = [ - 'qbootctl.cpp', - 'bootctrl_impl.cpp', - 'bootctrl_test.cpp', - 'gpt-utils.cpp', - 'ufs-bsg.cpp', + 'qbootctl.c', + 'bootctrl_impl.c', + 'bootctrl_test.c', + 'gpt-utils.c', + 'ufs-bsg.c', ] inc = [ diff --git a/qbootctl.cpp b/qbootctl.c similarity index 99% rename from qbootctl.cpp rename to qbootctl.c index a666b82..4936b9e 100644 --- a/qbootctl.cpp +++ b/qbootctl.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "bootctrl.h" diff --git a/ufs-bsg.cpp b/ufs-bsg.c similarity index 93% rename from ufs-bsg.cpp rename to ufs-bsg.c index 8b66342..05c8885 100644 --- a/ufs-bsg.cpp +++ b/ufs-bsg.c @@ -81,16 +81,16 @@ static int ufs_bsg_ioctl(int fd, struct ufs_bsg_request *req, enum bsg_ioctl_dir dir) { int ret; - struct sg_io_v4 sg_io { + struct sg_io_v4 sg_io = { + .guard = 'Q', + .protocol = BSG_PROTOCOL_SCSI, + .subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT, + .request_len = sizeof(*req), + .request = (__u64)req, + .response = (__u64)rsp, + .max_response_len = sizeof(*rsp), }; - sg_io.guard = 'Q'; - sg_io.protocol = BSG_PROTOCOL_SCSI; - sg_io.subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT; - sg_io.request_len = sizeof(*req); - sg_io.request = (__u64)req; - sg_io.response = (__u64)rsp; - sg_io.max_response_len = sizeof(*rsp); if (dir == BSG_IOCTL_DIR_FROM_DEV) { sg_io.din_xfer_len = buf_len; sg_io.din_xferp = (__u64)(buf); @@ -137,10 +137,8 @@ static void compose_ufs_bsg_query_req(struct ufs_bsg_request *req, __u8 func, static int ufs_query_attr(int fd, __u32 value, __u8 func, __u8 opcode, __u8 idn, __u8 index, __u8 sel) { - struct ufs_bsg_request req { - }; - struct ufs_bsg_reply rsp { - }; + struct ufs_bsg_request req = { 0 }; + struct ufs_bsg_reply rsp = { 0 }; enum bsg_ioctl_dir dir = BSG_IOCTL_DIR_FROM_DEV; int ret = 0;