diff options
author | Ameya Thakur <ameyat@codeaurora.org> | 2016-02-24 11:25:43 -0800 |
---|---|---|
committer | Ameya Thakur <ameyat@codeaurora.org> | 2016-04-09 20:00:46 -0700 |
commit | c14fff0a37a3b14e051444337980968e99ed1826 (patch) | |
tree | 9fa827a9df9b6f7a2daef122a5c3ff892a6bf4c6 | |
parent | 11d0a1a92eef04f51806efc2a2d6198f82bd3db3 (diff) |
bootctrl: Initial commit for the boot control HAL
This HAL is needed to enable recovery on devices that support A/B
partitions.
Change-Id: Id7600573b6da579ed90aa2a26bed2409d631039d
-rw-r--r-- | Android.mk | 10 | ||||
-rw-r--r-- | NOTICE | 26 | ||||
-rw-r--r-- | boot_control.c | 629 |
3 files changed, 665 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..cae83a0 --- /dev/null +++ b/Android.mk @@ -0,0 +1,10 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_C_INCLUDES += hardware/libhardware/include +LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/gpt-utils/inc +LOCAL_CFLAGS += -Wall -Werror +LOCAL_SHARED_LIBRARIES += liblog librecovery_updater_msm +LOCAL_SRC_FILES := boot_control.c +LOCAL_MODULE_RELATIVE_PATH := hw +LOCAL_MODULE := bootctrl.$(TARGET_BOARD_PLATFORM) +include $(BUILD_SHARED_LIBRARY) @@ -0,0 +1,26 @@ +Copyright (c) 2016, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/boot_control.c b/boot_control.c new file mode 100644 index 0000000..58edf5e --- /dev/null +++ b/boot_control.c @@ -0,0 +1,629 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <errno.h> +#define LOG_TAG "bootcontrolhal" +#include <cutils/log.h> +#include <hardware/boot_control.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <dirent.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <limits.h> +#include "gpt-utils.h" + +#define BOOTDEV_DIR "/dev/block/bootdevice/by-name" +#define BOOT_IMG_PTN_NAME "boot" +#define LUN_NAME_END_LOC 14 + +const char *slot_suffix_arr[] = { + AB_SLOT_A_SUFFIX, + AB_SLOT_B_SUFFIX, + NULL}; + +enum part_attr_type { + ATTR_SLOT_ACTIVE = 0, + ATTR_BOOT_SUCCESSFUL, + ATTR_UNBOOTABLE, +}; + +void boot_control_init(struct boot_control_module *module) +{ + if (!module) { + ALOGE("Invalid argument passed to %s", __func__); + return; + } + return; +} + +//Get the value of one of the attribute fields for a partition. +static int get_partition_attribute(char *partname, + enum part_attr_type part_attr) +{ + struct gpt_disk *disk = NULL; + uint8_t *pentry = NULL; + int retval = -1; + uint8_t *attr = NULL; + if (!partname) + goto error; + disk = gpt_disk_alloc(); + if (!disk) { + ALOGE("%s: Failed to alloc disk struct", __func__); + goto error; + } + if (gpt_disk_get_disk_info(partname, disk)) { + ALOGE("%s: Failed to get disk info", __func__); + goto error; + } + pentry = gpt_disk_get_pentry(disk, partname, PRIMARY_GPT); + if (!pentry) { + ALOGE("%s: pentry does not exist in disk struct", + __func__); + goto error; + } + attr = pentry + AB_FLAG_OFFSET; + if (part_attr == ATTR_SLOT_ACTIVE) + retval = !!(*attr & AB_PARTITION_ATTR_SLOT_ACTIVE); + else if (part_attr == ATTR_BOOT_SUCCESSFUL) + retval = !!(*attr & AB_PARTITION_ATTR_BOOT_SUCCESSFUL); + else if (part_attr == ATTR_UNBOOTABLE) + retval = !!(*attr & AB_PARTITION_ATTR_UNBOOTABLE); + else + retval = -1; + gpt_disk_free(disk); + return retval; +error: + if (disk) + gpt_disk_free(disk); + return retval; +} + +//Set a particular attribute for all the partitions in a +//slot +static int update_slot_attribute(const char *slot, + enum part_attr_type ab_attr) +{ + unsigned int i = 0; + char buf[PATH_MAX]; + struct stat st; + struct gpt_disk *disk = NULL; + uint8_t *pentry = NULL; + uint8_t *pentry_bak = NULL; + int rc = -1; + uint8_t *attr = NULL; + uint8_t *attr_bak = NULL; + char partName[MAX_GPT_NAME_SIZE + 1] = {0}; + const char ptn_list[][MAX_GPT_NAME_SIZE] = { AB_PTN_LIST }; + int slot_name_valid = 0; + if (!slot) { + ALOGE("%s: Invalid argument", __func__); + goto error; + } + 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; + } + if (!slot_name_valid) { + ALOGE("%s: Invalid slot name", __func__); + goto error; + } + for (i=0; i < ARRAY_SIZE(ptn_list); 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], + AB_SLOT_A_SUFFIX + ); + if (stat(buf, &st)) { + //partition does not have _a version + continue; + } + memset(buf, '\0', sizeof(buf)); + snprintf(buf, sizeof(buf) - 1, + "%s/%s%s", + BOOT_DEV_DIR, + ptn_list[i], + AB_SLOT_B_SUFFIX + ); + if (stat(buf, &st)) { + //partition does not have _a version + continue; + } + memset(partName, '\0', sizeof(partName)); + snprintf(partName, + sizeof(partName) - 1, + "%s%s", + ptn_list[i], + slot); + disk = gpt_disk_alloc(disk); + if (!disk) { + ALOGE("%s: Failed to alloc disk struct", + __func__); + goto error; + } + rc = gpt_disk_get_disk_info(partName, disk); + if (rc != 0) { + ALOGE("%s: Failed to get disk info for %s", + __func__, + partName); + goto error; + } + pentry = gpt_disk_get_pentry(disk, partName, PRIMARY_GPT); + pentry_bak = gpt_disk_get_pentry(disk, partName, SECONDARY_GPT); + if (!pentry || !pentry_bak) { + ALOGE("%s: Failed to get pentry/pentry_bak for %s", + __func__, + partName); + goto error; + } + attr = pentry + AB_FLAG_OFFSET; + attr_bak = pentry_bak + AB_FLAG_OFFSET; + if (ab_attr == ATTR_BOOT_SUCCESSFUL) { + *attr = (*attr) | AB_PARTITION_ATTR_BOOT_SUCCESSFUL; + *attr_bak = (*attr_bak) | + AB_PARTITION_ATTR_BOOT_SUCCESSFUL; + } else if (ab_attr == ATTR_UNBOOTABLE) { + *attr = (*attr) | AB_PARTITION_ATTR_UNBOOTABLE; + *attr_bak = (*attr_bak) | AB_PARTITION_ATTR_UNBOOTABLE; + } else if (ab_attr == ATTR_SLOT_ACTIVE) { + *attr = (*attr) | AB_PARTITION_ATTR_SLOT_ACTIVE; + *attr_bak = (*attr) | AB_PARTITION_ATTR_SLOT_ACTIVE; + } else { + ALOGE("%s: Unrecognized attr", __func__); + goto error; + } + if (gpt_disk_update_crc(disk)) { + ALOGE("%s: Failed to update crc for %s", + __func__, + partName); + goto error; + } + if (gpt_disk_commit(disk)) { + ALOGE("%s: Failed to write back entry for %s", + __func__, + partName); + goto error; + } + gpt_disk_free(disk); + disk = NULL; + } + return 0; +error: + if (disk) + gpt_disk_free(disk); + return -1; +} + +unsigned get_number_slots(struct boot_control_module *module) +{ + struct dirent *de = NULL; + DIR *dir_bootdev = NULL; + unsigned slot_count = 0; + if (!module) { + ALOGE("%s: Invalid argument", __func__); + goto error; + } + dir_bootdev = opendir(BOOTDEV_DIR); + if (!dir_bootdev) { + ALOGE("%s: Failed to open bootdev dir (%s)", + __func__, + strerror(errno)); + goto error; + } + while ((de = readdir(dir_bootdev))) { + if (de->d_name[0] == '.') + continue; + if (!strncmp(de->d_name, BOOT_IMG_PTN_NAME, + strlen(BOOT_IMG_PTN_NAME))) + slot_count++; + } + closedir(dir_bootdev); + return slot_count; +error: + if (dir_bootdev) + closedir(dir_bootdev); + return 0; +} + +unsigned get_current_slot(struct boot_control_module *module) +{ + uint32_t num_slots = 0; + char bootPartition[MAX_GPT_NAME_SIZE + 1]; + unsigned i = 0; + if (!module) { + ALOGE("%s: Invalid argument", __func__); + goto error; + } + num_slots = get_number_slots(module); + if (num_slots <= 1) { + //Slot 0 is the only slot around. + return 0; + } + //Iterate through a list of partitons named as boot+suffix + //and see which one is currently active. + for (i = 0; slot_suffix_arr[i] != NULL ; i++) { + memset(bootPartition, '\0', sizeof(bootPartition)); + snprintf(bootPartition, sizeof(bootPartition) - 1, + "boot%s", + slot_suffix_arr[i]); + if (get_partition_attribute(bootPartition, + ATTR_SLOT_ACTIVE) == 1) + return i; + } +error: + //The HAL spec requires that we return a number between + //0 to num_slots - 1. Since something went wrong here we + //are just going to return the default slot. + return 0; +} + +int mark_boot_successful(struct boot_control_module *module) +{ + unsigned cur_slot = 0; + if (!module) { + ALOGE("%s: Invalid argument", __func__); + goto error; + } + cur_slot = get_current_slot(module); + if (update_slot_attribute(slot_suffix_arr[cur_slot], + ATTR_BOOT_SUCCESSFUL)) { + goto error; + } + return 0; +error: + ALOGE("%s: Failed to mark boot successful", __func__); + return -1; +} + +const char *get_suffix(struct boot_control_module *module, unsigned slot) +{ + unsigned num_slots = 0; + if (!module) { + ALOGE("%s: Invalid arg", __func__); + } + num_slots = get_number_slots(module); + if (num_slots < 1 || slot > num_slots - 1) + return NULL; + else + return slot_suffix_arr[slot]; +} + +int set_active_boot_slot(struct boot_control_module *module, unsigned slot) +{ + const char ptn_list[][MAX_GPT_NAME_SIZE] = { AB_PTN_LIST }; + char slotA[MAX_GPT_NAME_SIZE + 1] = {0}; + char slotB[MAX_GPT_NAME_SIZE + 1] = {0}; + char active_guid[TYPE_GUID_SIZE + 1] = {0}; + char inactive_guid[TYPE_GUID_SIZE + 1] = {0}; + struct gpt_disk *disk = NULL; + //Pointer to partition entry of current 'A' partition + uint8_t *pentryA = NULL; + uint8_t *pentryA_bak = NULL; + //Pointer to partition entry of current 'B' partition + uint8_t *pentryB = NULL; + uint8_t *pentryB_bak = NULL; + uint8_t *slot_info = NULL; + uint32_t i; + int rc = -1; + char buf[PATH_MAX] = {0}; + struct stat st; + unsigned num_slots = 0; + unsigned current_slot = 0; + int is_ufs = gpt_utils_is_ufs_device(); + + if (!module) { + ALOGE("%s: Invalid arg", __func__); + goto error; + } + num_slots = get_number_slots(module); + if ((num_slots < 1) || (slot > num_slots - 1)) { + ALOGE("%s: Unable to get num slots/Invalid slot value", + __func__); + goto error; + } + current_slot = get_current_slot(module); + if (current_slot == slot) { + //Nothing to do here. Just return + return 0; + } + for (i=0; i < ARRAY_SIZE(ptn_list); i++) { + //XBL is handled differrently for ufs devices + if (is_ufs && !strncmp(ptn_list[i], PTN_XBL, strlen(PTN_XBL))) + continue; + 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], + AB_SLOT_A_SUFFIX + ); + if (stat(buf, &st)) { + //partition does not have _a version + continue; + } + memset(buf, '\0', sizeof(buf)); + snprintf(buf, sizeof(buf) - 1, + "%s/%s%s", + BOOT_DEV_DIR, + ptn_list[i], + AB_SLOT_B_SUFFIX + ); + if (stat(buf, &st)) { + //partition does not have _a version + continue; + } + disk = gpt_disk_alloc(); + if (!disk) + goto error; + memset(slotA, 0, sizeof(slotA)); + memset(slotB, 0, sizeof(slotB)); + snprintf(slotA, sizeof(slotA) - 1, "%s%s", + ptn_list[i], + AB_SLOT_A_SUFFIX); + snprintf(slotB, sizeof(slotB) - 1,"%s%s", + ptn_list[i], + AB_SLOT_B_SUFFIX); + //It is assumed that both the A and B slots reside on the + //same physical disk + if (gpt_disk_get_disk_info(slotA, disk)) + goto error; + //Get partition entry for slot A from primary table + pentryA = gpt_disk_get_pentry(disk, slotA, PRIMARY_GPT); + //Get partition entry for slot A from backup table + pentryA_bak = gpt_disk_get_pentry(disk, slotA, SECONDARY_GPT); + //Get partition entry for slot B from primary table + pentryB = gpt_disk_get_pentry(disk, slotB, PRIMARY_GPT); + //Get partition entry for slot B from backup table + pentryB_bak = gpt_disk_get_pentry(disk, slotB, SECONDARY_GPT); + if ( !pentryA || !pentryA_bak || !pentryB || !pentryB_bak) { + //Something has gone wrong here.We know that we have + //_a and _b versions of this partition due to the + //check at the start of the loop so none of these + //should be NULL. + ALOGE("Slot pentries for %s not found.", + ptn_list[i]); + goto error; + } + memset(active_guid, '\0', sizeof(active_guid)); + memset(inactive_guid, '\0', sizeof(inactive_guid)); + if (get_partition_attribute(slotA, ATTR_SLOT_ACTIVE) == 1) { + //A is the current active slot + memcpy((void*)active_guid, + (const void*)pentryA, + TYPE_GUID_SIZE); + memcpy((void*)inactive_guid, + (const void*)pentryB, + TYPE_GUID_SIZE); + + } else if (get_partition_attribute(slotB, + ATTR_SLOT_ACTIVE) == 1) { + //B is the current active slot + memcpy((void*)active_guid, + (const void*)pentryB, + TYPE_GUID_SIZE); + memcpy((void*)inactive_guid, + (const void*)pentryA, + TYPE_GUID_SIZE); + } else { + ALOGE("Both A & B are inactive..Aborting"); + goto error; + } + if (!strncmp(slot_suffix_arr[slot], AB_SLOT_A_SUFFIX, + strlen(AB_SLOT_A_SUFFIX))){ + //Mark A as active in primary table + memcpy(pentryA, active_guid, TYPE_GUID_SIZE); + slot_info = pentryA + AB_FLAG_OFFSET; + *slot_info = AB_SLOT_ACTIVE_VAL; + + //Mark A as active in backup table + memcpy(pentryA_bak, active_guid, TYPE_GUID_SIZE); + slot_info = pentryA_bak + AB_FLAG_OFFSET; + *slot_info = AB_SLOT_ACTIVE_VAL; + + //Mark B as inactive in primary table + memcpy(pentryB, inactive_guid, TYPE_GUID_SIZE); + slot_info = pentryB + AB_FLAG_OFFSET; + *slot_info = *(slot_info) & + ~AB_PARTITION_ATTR_SLOT_ACTIVE; + + //Mark B as inactive in backup table + memcpy(pentryB_bak, inactive_guid, TYPE_GUID_SIZE); + slot_info = pentryB_bak + AB_FLAG_OFFSET; + *slot_info = *(slot_info) & + ~AB_PARTITION_ATTR_SLOT_ACTIVE; + } else if (!strncmp(slot_suffix_arr[slot], AB_SLOT_B_SUFFIX, + strlen(AB_SLOT_B_SUFFIX))){ + //Mark B as active in primary table + memcpy(pentryB, active_guid, TYPE_GUID_SIZE); + slot_info = pentryB + AB_FLAG_OFFSET; + *slot_info = AB_SLOT_ACTIVE_VAL; + + //Mark B as active in backup table + memcpy(pentryB_bak, active_guid, TYPE_GUID_SIZE); + slot_info = pentryB_bak + AB_FLAG_OFFSET; + *slot_info = AB_SLOT_ACTIVE_VAL; + + //Mark A as inavtive in primary table + memcpy(pentryA, inactive_guid, TYPE_GUID_SIZE); + slot_info = pentryA + AB_FLAG_OFFSET; + *slot_info = *(slot_info) & + ~AB_PARTITION_ATTR_SLOT_ACTIVE; + + //Mark A as inactive in backup table + memcpy(pentryA_bak, inactive_guid, TYPE_GUID_SIZE); + slot_info = pentryA_bak + AB_FLAG_OFFSET; + *slot_info = *(slot_info) & + ~AB_PARTITION_ATTR_SLOT_ACTIVE; + } else { + //Something has gone terribly terribly wrong + ALOGE("%s: Unknown slot suffix!", __func__); + goto error; + } + if (gpt_disk_update_crc(disk) != 0) { + ALOGE("%s: Failed to update gpt_disk crc", __func__); + goto error; + } + if (gpt_disk_commit(disk) != 0) { + ALOGE("%s: Failed to commit disk info", __func__); + goto error; + } + gpt_disk_free(disk); + disk = NULL; + } + if (is_ufs) { + if (!strncmp(slot_suffix_arr[slot], AB_SLOT_A_SUFFIX, + strlen(AB_SLOT_A_SUFFIX))){ + //Set xbl_a as the boot lun + rc = gpt_utils_set_xbl_boot_partition(NORMAL_BOOT); + } else if (!strncmp(slot_suffix_arr[slot], AB_SLOT_B_SUFFIX, + strlen(AB_SLOT_B_SUFFIX))){ + //Set xbl_b as the boot lun + rc = gpt_utils_set_xbl_boot_partition(BACKUP_BOOT); + } else { + //Something has gone terribly terribly wrong + ALOGE("%s: Unknown slot suffix!", __func__); + goto error; + } + if (rc) { + ALOGE("%s: Failed to switch xbl boot partition", + __func__); + goto error; + } + } + return 0; +error: + if (disk) + gpt_disk_free(disk); + return -1; +} + +int set_slot_as_unbootable(struct boot_control_module *module, unsigned slot) +{ + unsigned num_slots = 0; + if (!module) { + ALOGE("%s: Invalid argument", __func__); + goto error; + } + num_slots = get_number_slots(module); + if (num_slots < 1 || slot > num_slots - 1) { + ALOGE("%s: Unable to get num_slots/Invalid slot value", + __func__); + goto error; + } + if (update_slot_attribute(slot_suffix_arr[slot], + ATTR_UNBOOTABLE)) { + goto error; + } + return 0; +error: + ALOGE("%s: Failed to mark slot unbootable", __func__); + return -1; +} + +int is_slot_bootable(struct boot_control_module *module, unsigned slot) +{ + unsigned num_slots = 0; + int attr = 0; + char bootPartition[MAX_GPT_NAME_SIZE + 1] = {0}; + if (!module) { + ALOGE("%s: Invalid argument", __func__); + goto error; + } + num_slots = get_number_slots(module); + if (num_slots < 1 || slot > num_slots - 1) { + ALOGE("%s: Unable to get num_slots/Invalid slot value", + __func__); + goto error; + } + snprintf(bootPartition, + sizeof(bootPartition) - 1, "boot%s", + slot_suffix_arr[slot]); + attr = get_partition_attribute(bootPartition, ATTR_UNBOOTABLE); + if (attr >= 0) + return !attr; +error: + return -1; +} + +int is_slot_marked_successful(struct boot_control_module *module, unsigned slot) +{ + unsigned num_slots = 0; + int attr = 0; + char bootPartition[MAX_GPT_NAME_SIZE + 1] = {0}; + if (!module) { + ALOGE("%s: Invalid argument", __func__); + goto error; + } + num_slots = get_number_slots(module); + if (num_slots < 1 || slot > num_slots - 1) { + ALOGE("%s: Unable to get num_slots/Invalid slot value", + __func__); + goto error; + } + snprintf(bootPartition, + sizeof(bootPartition) - 1, + "boot%s", slot_suffix_arr[slot]); + attr = get_partition_attribute(bootPartition, ATTR_BOOT_SUCCESSFUL); + if (attr >= 0) + return attr; +error: + return -1; +} + +static hw_module_methods_t boot_control_module_methods = { + .open = NULL, +}; + +boot_control_module_t HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = 1, + .hal_api_version = 0, + .id = BOOT_CONTROL_HARDWARE_MODULE_ID, + .name = "Boot control HAL", + .author = "Code Aurora Forum", + .methods = &boot_control_module_methods, + }, + .init = boot_control_init, + .getNumberSlots = get_number_slots, + .getCurrentSlot = get_current_slot, + .markBootSuccessful = mark_boot_successful, + .setActiveBootSlot = set_active_boot_slot, + .setSlotAsUnbootable = set_slot_as_unbootable, + .isSlotBootable = is_slot_bootable, + .getSuffix = get_suffix, + .isSlotMarkedSuccessful = is_slot_marked_successful, +}; |