Home | History | Annotate | Download | only in bootctrl
      1 /*
      2  * Copyright (c) 2016, The Linux Foundation. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *     * Redistributions of source code must retain the above copyright
      8  *       notice, this list of conditions and the following disclaimer.
      9  *     * Redistributions in binary form must reproduce the above
     10  *       copyright notice, this list of conditions and the following
     11  *       disclaimer in the documentation and/or other materials provided
     12  *       with the distribution.
     13  *     * Neither the name of The Linux Foundation nor the names of its
     14  *       contributors may be used to endorse or promote products derived
     15  *       from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
     18  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
     20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
     21  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
     24  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     25  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
     26  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
     27  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28  */
     29 #include <errno.h>
     30 #define LOG_TAG "bootcontrolhal"
     31 #include <cutils/log.h>
     32 #include <hardware/boot_control.h>
     33 #include <stdio.h>
     34 #include <string.h>
     35 #include <unistd.h>
     36 #include <dirent.h>
     37 #include <sys/types.h>
     38 #include <sys/stat.h>
     39 #include <fcntl.h>
     40 #include <limits.h>
     41 #include "gpt-utils.h"
     42 
     43 #define BOOTDEV_DIR "/dev/block/bootdevice/by-name"
     44 #define BOOT_IMG_PTN_NAME "boot"
     45 #define LUN_NAME_END_LOC 14
     46 
     47 const char *slot_suffix_arr[] = {
     48 	AB_SLOT_A_SUFFIX,
     49 	AB_SLOT_B_SUFFIX,
     50 	NULL};
     51 
     52 enum part_attr_type {
     53 	ATTR_SLOT_ACTIVE = 0,
     54 	ATTR_BOOT_SUCCESSFUL,
     55 	ATTR_UNBOOTABLE,
     56 };
     57 
     58 void boot_control_init(struct boot_control_module *module)
     59 {
     60 	if (!module) {
     61 		ALOGE("Invalid argument passed to %s", __func__);
     62 		return;
     63 	}
     64 	return;
     65 }
     66 
     67 //Get the value of one of the attribute fields for a partition.
     68 static int get_partition_attribute(char *partname,
     69 		enum part_attr_type part_attr)
     70 {
     71 	struct gpt_disk *disk = NULL;
     72 	uint8_t *pentry = NULL;
     73 	int retval = -1;
     74 	uint8_t *attr = NULL;
     75 	if (!partname)
     76 		goto error;
     77 	disk = gpt_disk_alloc();
     78 	if (!disk) {
     79 		ALOGE("%s: Failed to alloc disk struct", __func__);
     80 		goto error;
     81 	}
     82 	if (gpt_disk_get_disk_info(partname, disk)) {
     83 		ALOGE("%s: Failed to get disk info", __func__);
     84 		goto error;
     85 	}
     86 	pentry = gpt_disk_get_pentry(disk, partname, PRIMARY_GPT);
     87 	if (!pentry) {
     88 		ALOGE("%s: pentry does not exist in disk struct",
     89 				__func__);
     90 		goto error;
     91 	}
     92 	attr = pentry + AB_FLAG_OFFSET;
     93 	if (part_attr == ATTR_SLOT_ACTIVE)
     94 		retval = !!(*attr & AB_PARTITION_ATTR_SLOT_ACTIVE);
     95 	else if (part_attr == ATTR_BOOT_SUCCESSFUL)
     96 		retval = !!(*attr & AB_PARTITION_ATTR_BOOT_SUCCESSFUL);
     97 	else if (part_attr == ATTR_UNBOOTABLE)
     98 		retval = !!(*attr & AB_PARTITION_ATTR_UNBOOTABLE);
     99 	else
    100 		retval = -1;
    101 	gpt_disk_free(disk);
    102 	return retval;
    103 error:
    104 	if (disk)
    105 		gpt_disk_free(disk);
    106 	return retval;
    107 }
    108 
    109 //Set a particular attribute for all the partitions in a
    110 //slot
    111 static int update_slot_attribute(const char *slot,
    112 		enum part_attr_type ab_attr)
    113 {
    114 	unsigned int i = 0;
    115 	char buf[PATH_MAX];
    116 	struct stat st;
    117 	struct gpt_disk *disk = NULL;
    118 	uint8_t *pentry = NULL;
    119 	uint8_t *pentry_bak = NULL;
    120 	int rc = -1;
    121 	uint8_t *attr = NULL;
    122 	uint8_t *attr_bak = NULL;
    123 	char partName[MAX_GPT_NAME_SIZE + 1] = {0};
    124 	const char ptn_list[][MAX_GPT_NAME_SIZE] = { AB_PTN_LIST };
    125 	int slot_name_valid = 0;
    126 	if (!slot) {
    127 		ALOGE("%s: Invalid argument", __func__);
    128 		goto error;
    129 	}
    130 	for (i = 0; slot_suffix_arr[i] != NULL; i++)
    131 	{
    132 		if (!strncmp(slot, slot_suffix_arr[i],
    133 					strlen(slot_suffix_arr[i])))
    134 				slot_name_valid = 1;
    135 	}
    136 	if (!slot_name_valid) {
    137 		ALOGE("%s: Invalid slot name", __func__);
    138 		goto error;
    139 	}
    140 	for (i=0; i < ARRAY_SIZE(ptn_list); i++) {
    141 		memset(buf, '\0', sizeof(buf));
    142 		//Check if A/B versions of this ptn exist
    143 		snprintf(buf, sizeof(buf) - 1,
    144                                         "%s/%s%s",
    145                                         BOOT_DEV_DIR,
    146                                         ptn_list[i],
    147 					AB_SLOT_A_SUFFIX
    148 					);
    149 		if (stat(buf, &st)) {
    150 			//partition does not have _a version
    151 			continue;
    152 		}
    153 		memset(buf, '\0', sizeof(buf));
    154 		snprintf(buf, sizeof(buf) - 1,
    155                                         "%s/%s%s",
    156                                         BOOT_DEV_DIR,
    157                                         ptn_list[i],
    158 					AB_SLOT_B_SUFFIX
    159 					);
    160 		if (stat(buf, &st)) {
    161 			//partition does not have _a version
    162 			continue;
    163 		}
    164 		memset(partName, '\0', sizeof(partName));
    165 		snprintf(partName,
    166 				sizeof(partName) - 1,
    167 				"%s%s",
    168 				ptn_list[i],
    169 				slot);
    170 		disk = gpt_disk_alloc(disk);
    171 		if (!disk) {
    172 			ALOGE("%s: Failed to alloc disk struct",
    173 					__func__);
    174 			goto error;
    175 		}
    176 		rc = gpt_disk_get_disk_info(partName, disk);
    177 		if (rc != 0) {
    178 			ALOGE("%s: Failed to get disk info for %s",
    179 					__func__,
    180 					partName);
    181 			goto error;
    182 		}
    183 		pentry = gpt_disk_get_pentry(disk, partName, PRIMARY_GPT);
    184 		pentry_bak = gpt_disk_get_pentry(disk, partName, SECONDARY_GPT);
    185 		if (!pentry || !pentry_bak) {
    186 			ALOGE("%s: Failed to get pentry/pentry_bak for %s",
    187 					__func__,
    188 					partName);
    189 			goto error;
    190 		}
    191 		attr = pentry + AB_FLAG_OFFSET;
    192 		attr_bak = pentry_bak + AB_FLAG_OFFSET;
    193 		if (ab_attr == ATTR_BOOT_SUCCESSFUL) {
    194 			*attr = (*attr) | AB_PARTITION_ATTR_BOOT_SUCCESSFUL;
    195 			*attr_bak = (*attr_bak) |
    196 				AB_PARTITION_ATTR_BOOT_SUCCESSFUL;
    197 		} else if (ab_attr == ATTR_UNBOOTABLE) {
    198 			*attr = (*attr) | AB_PARTITION_ATTR_UNBOOTABLE;
    199 			*attr_bak = (*attr_bak) | AB_PARTITION_ATTR_UNBOOTABLE;
    200 		} else if (ab_attr == ATTR_SLOT_ACTIVE) {
    201 			*attr = (*attr) | AB_PARTITION_ATTR_SLOT_ACTIVE;
    202 			*attr_bak = (*attr) | AB_PARTITION_ATTR_SLOT_ACTIVE;
    203 		} else {
    204 			ALOGE("%s: Unrecognized attr", __func__);
    205 			goto error;
    206 		}
    207 		if (gpt_disk_update_crc(disk)) {
    208 			ALOGE("%s: Failed to update crc for %s",
    209 					__func__,
    210 					partName);
    211 			goto error;
    212 		}
    213 		if (gpt_disk_commit(disk)) {
    214 			ALOGE("%s: Failed to write back entry for %s",
    215 					__func__,
    216 					partName);
    217 			goto error;
    218 		}
    219 		gpt_disk_free(disk);
    220 		disk = NULL;
    221 	}
    222 	return 0;
    223 error:
    224 	if (disk)
    225 		gpt_disk_free(disk);
    226 	return -1;
    227 }
    228 
    229 unsigned get_number_slots(struct boot_control_module *module)
    230 {
    231 	struct dirent *de = NULL;
    232 	DIR *dir_bootdev = NULL;
    233 	unsigned slot_count = 0;
    234 	if (!module) {
    235 		ALOGE("%s: Invalid argument", __func__);
    236 		goto error;
    237 	}
    238 	dir_bootdev = opendir(BOOTDEV_DIR);
    239 	if (!dir_bootdev) {
    240 		ALOGE("%s: Failed to open bootdev dir (%s)",
    241 				__func__,
    242 				strerror(errno));
    243 		goto error;
    244 	}
    245 	while ((de = readdir(dir_bootdev))) {
    246 		if (de->d_name[0] == '.')
    247 			continue;
    248 		if (!strncmp(de->d_name, BOOT_IMG_PTN_NAME,
    249 					strlen(BOOT_IMG_PTN_NAME)))
    250 			slot_count++;
    251 	}
    252 	closedir(dir_bootdev);
    253 	return slot_count;
    254 error:
    255 	if (dir_bootdev)
    256 		closedir(dir_bootdev);
    257 	return 0;
    258 }
    259 
    260 unsigned get_current_slot(struct boot_control_module *module)
    261 {
    262 	uint32_t num_slots = 0;
    263 	char bootPartition[MAX_GPT_NAME_SIZE + 1];
    264 	unsigned i = 0;
    265 	if (!module) {
    266 		ALOGE("%s: Invalid argument", __func__);
    267 		goto error;
    268 	}
    269 	num_slots = get_number_slots(module);
    270 	if (num_slots <= 1) {
    271 		//Slot 0 is the only slot around.
    272 		return 0;
    273 	}
    274 	//Iterate through a list of partitons named as boot+suffix
    275 	//and see which one is currently active.
    276 	for (i = 0; slot_suffix_arr[i] != NULL ; i++) {
    277 		memset(bootPartition, '\0', sizeof(bootPartition));
    278 		snprintf(bootPartition, sizeof(bootPartition) - 1,
    279 				"boot%s",
    280 				slot_suffix_arr[i]);
    281 		if (get_partition_attribute(bootPartition,
    282 					ATTR_SLOT_ACTIVE) == 1)
    283 			return i;
    284 	}
    285 error:
    286 	//The HAL spec requires that we return a number between
    287 	//0 to num_slots - 1. Since something went wrong here we
    288 	//are just going to return the default slot.
    289 	return 0;
    290 }
    291 
    292 int mark_boot_successful(struct boot_control_module *module)
    293 {
    294 	unsigned cur_slot = 0;
    295 	if (!module) {
    296 		ALOGE("%s: Invalid argument", __func__);
    297 		goto error;
    298 	}
    299 	cur_slot = get_current_slot(module);
    300 	if (update_slot_attribute(slot_suffix_arr[cur_slot],
    301 				ATTR_BOOT_SUCCESSFUL)) {
    302 		goto error;
    303 	}
    304 	return 0;
    305 error:
    306 	ALOGE("%s: Failed to mark boot successful", __func__);
    307 	return -1;
    308 }
    309 
    310 const char *get_suffix(struct boot_control_module *module, unsigned slot)
    311 {
    312 	unsigned num_slots = 0;
    313 	if (!module) {
    314 		ALOGE("%s: Invalid arg", __func__);
    315 	}
    316 	num_slots = get_number_slots(module);
    317 	if (num_slots < 1 || slot > num_slots - 1)
    318 		return NULL;
    319 	else
    320 		return slot_suffix_arr[slot];
    321 }
    322 
    323 int set_active_boot_slot(struct boot_control_module *module, unsigned slot)
    324 {
    325 	const char ptn_list[][MAX_GPT_NAME_SIZE] = { AB_PTN_LIST };
    326 	char slotA[MAX_GPT_NAME_SIZE + 1] = {0};
    327 	char slotB[MAX_GPT_NAME_SIZE + 1] = {0};
    328 	char active_guid[TYPE_GUID_SIZE + 1] = {0};
    329 	char inactive_guid[TYPE_GUID_SIZE + 1] = {0};
    330 	struct gpt_disk *disk = NULL;
    331 	//Pointer to partition entry of current 'A' partition
    332 	uint8_t *pentryA = NULL;
    333 	uint8_t *pentryA_bak = NULL;
    334 	//Pointer to partition entry of current 'B' partition
    335 	uint8_t *pentryB = NULL;
    336 	uint8_t *pentryB_bak = NULL;
    337 	uint8_t *slot_info = NULL;
    338 	uint32_t i;
    339 	int rc = -1;
    340 	char buf[PATH_MAX] = {0};
    341 	struct stat st;
    342 	unsigned num_slots = 0;
    343 	unsigned current_slot = 0;
    344 	int is_ufs = gpt_utils_is_ufs_device();
    345 
    346 	if (!module) {
    347 		ALOGE("%s: Invalid arg", __func__);
    348 		goto error;
    349 	}
    350 	num_slots = get_number_slots(module);
    351 	if ((num_slots < 1) || (slot > num_slots - 1)) {
    352 		ALOGE("%s: Unable to get num slots/Invalid slot value",
    353 				__func__);
    354 		goto error;
    355 	}
    356         current_slot = get_current_slot(module);
    357 	if (current_slot == slot) {
    358 		//Nothing to do here. Just return
    359 		return 0;
    360 	}
    361 	for (i=0; i < ARRAY_SIZE(ptn_list); i++) {
    362 		//XBL is handled differrently for ufs devices
    363 		if (is_ufs && !strncmp(ptn_list[i], PTN_XBL, strlen(PTN_XBL)))
    364 				continue;
    365 		memset(buf, '\0', sizeof(buf));
    366 		//Check if A/B versions of this ptn exist
    367 		snprintf(buf, sizeof(buf) - 1,
    368                                         "%s/%s%s",
    369                                         BOOT_DEV_DIR,
    370                                         ptn_list[i],
    371 					AB_SLOT_A_SUFFIX
    372 					);
    373 		if (stat(buf, &st)) {
    374 			//partition does not have _a version
    375 			continue;
    376 		}
    377 		memset(buf, '\0', sizeof(buf));
    378 		snprintf(buf, sizeof(buf) - 1,
    379                                         "%s/%s%s",
    380                                         BOOT_DEV_DIR,
    381                                         ptn_list[i],
    382 					AB_SLOT_B_SUFFIX
    383 					);
    384 		if (stat(buf, &st)) {
    385 			//partition does not have _a version
    386 			continue;
    387 		}
    388 		disk = gpt_disk_alloc();
    389 		if (!disk)
    390 			goto error;
    391 		memset(slotA, 0, sizeof(slotA));
    392 		memset(slotB, 0, sizeof(slotB));
    393 		snprintf(slotA, sizeof(slotA) - 1, "%s%s",
    394 				ptn_list[i],
    395 				AB_SLOT_A_SUFFIX);
    396 		snprintf(slotB, sizeof(slotB) - 1,"%s%s",
    397 				ptn_list[i],
    398 				AB_SLOT_B_SUFFIX);
    399 		//It is assumed that both the A and B slots reside on the
    400 		//same physical disk
    401 		if (gpt_disk_get_disk_info(slotA, disk))
    402 			goto error;
    403 		//Get partition entry for slot A from primary table
    404 		pentryA = gpt_disk_get_pentry(disk, slotA, PRIMARY_GPT);
    405 		//Get partition entry for slot A from backup table
    406 		pentryA_bak = gpt_disk_get_pentry(disk, slotA, SECONDARY_GPT);
    407 		//Get partition entry for slot B from primary table
    408 		pentryB = gpt_disk_get_pentry(disk, slotB, PRIMARY_GPT);
    409 		//Get partition entry for slot B from backup table
    410 		pentryB_bak = gpt_disk_get_pentry(disk, slotB, SECONDARY_GPT);
    411 		if ( !pentryA || !pentryA_bak || !pentryB || !pentryB_bak) {
    412 			//Something has gone wrong here.We know that we have
    413 			//_a and _b versions of this partition due to the
    414 			//check at the start of the loop so none of these
    415 			//should be NULL.
    416 			ALOGE("Slot pentries for %s not found.",
    417 					ptn_list[i]);
    418 			goto error;
    419 		}
    420 		memset(active_guid, '\0', sizeof(active_guid));
    421 		memset(inactive_guid, '\0', sizeof(inactive_guid));
    422 		if (get_partition_attribute(slotA, ATTR_SLOT_ACTIVE) == 1) {
    423 			//A is the current active slot
    424 			memcpy((void*)active_guid,
    425 					(const void*)pentryA,
    426 					TYPE_GUID_SIZE);
    427 			memcpy((void*)inactive_guid,
    428 					(const void*)pentryB,
    429 					TYPE_GUID_SIZE);
    430 
    431 		} else if (get_partition_attribute(slotB,
    432 					ATTR_SLOT_ACTIVE) == 1) {
    433 			//B is the current active slot
    434 			memcpy((void*)active_guid,
    435 					(const void*)pentryB,
    436 					TYPE_GUID_SIZE);
    437 			memcpy((void*)inactive_guid,
    438 					(const void*)pentryA,
    439 					TYPE_GUID_SIZE);
    440 		} else {
    441 			ALOGE("Both A & B are inactive..Aborting");
    442 			goto error;
    443 		}
    444 		if (!strncmp(slot_suffix_arr[slot], AB_SLOT_A_SUFFIX,
    445 					strlen(AB_SLOT_A_SUFFIX))){
    446 			//Mark A as active in primary table
    447 			memcpy(pentryA, active_guid, TYPE_GUID_SIZE);
    448 			slot_info = pentryA + AB_FLAG_OFFSET;
    449 			*slot_info = AB_SLOT_ACTIVE_VAL;
    450 
    451 			//Mark A as active in backup table
    452 			memcpy(pentryA_bak, active_guid, TYPE_GUID_SIZE);
    453 			slot_info = pentryA_bak + AB_FLAG_OFFSET;
    454 			*slot_info = AB_SLOT_ACTIVE_VAL;
    455 
    456 			//Mark B as inactive in primary table
    457 			memcpy(pentryB, inactive_guid, TYPE_GUID_SIZE);
    458 			slot_info = pentryB + AB_FLAG_OFFSET;
    459 			*slot_info = *(slot_info) &
    460 				~AB_PARTITION_ATTR_SLOT_ACTIVE;
    461 
    462 			//Mark B as inactive in backup table
    463 			memcpy(pentryB_bak, inactive_guid, TYPE_GUID_SIZE);
    464 			slot_info = pentryB_bak + AB_FLAG_OFFSET;
    465 			*slot_info = *(slot_info) &
    466 				~AB_PARTITION_ATTR_SLOT_ACTIVE;
    467 		} else if (!strncmp(slot_suffix_arr[slot], AB_SLOT_B_SUFFIX,
    468 					strlen(AB_SLOT_B_SUFFIX))){
    469 			//Mark B as active in primary table
    470 			memcpy(pentryB, active_guid, TYPE_GUID_SIZE);
    471 			slot_info = pentryB + AB_FLAG_OFFSET;
    472 			*slot_info = AB_SLOT_ACTIVE_VAL;
    473 
    474 			//Mark B as active in backup table
    475 			memcpy(pentryB_bak, active_guid, TYPE_GUID_SIZE);
    476 			slot_info = pentryB_bak + AB_FLAG_OFFSET;
    477 			*slot_info = AB_SLOT_ACTIVE_VAL;
    478 
    479 			//Mark A as inavtive in primary table
    480 			memcpy(pentryA, inactive_guid, TYPE_GUID_SIZE);
    481 			slot_info = pentryA + AB_FLAG_OFFSET;
    482 			*slot_info = *(slot_info) &
    483 				~AB_PARTITION_ATTR_SLOT_ACTIVE;
    484 
    485 			//Mark A as inactive in backup table
    486 			memcpy(pentryA_bak, inactive_guid, TYPE_GUID_SIZE);
    487 			slot_info = pentryA_bak + AB_FLAG_OFFSET;
    488 			*slot_info = *(slot_info) &
    489 				~AB_PARTITION_ATTR_SLOT_ACTIVE;
    490 		} else {
    491 			//Something has gone terribly terribly wrong
    492 			ALOGE("%s: Unknown slot suffix!", __func__);
    493 			goto error;
    494 		}
    495 		if (gpt_disk_update_crc(disk) != 0) {
    496 			ALOGE("%s: Failed to update gpt_disk crc", __func__);
    497 			goto error;
    498 		}
    499 		if (gpt_disk_commit(disk) != 0) {
    500 			ALOGE("%s: Failed to commit disk info", __func__);
    501 			goto error;
    502 		}
    503 		gpt_disk_free(disk);
    504 		disk = NULL;
    505 	}
    506 	if (is_ufs) {
    507 		if (!strncmp(slot_suffix_arr[slot], AB_SLOT_A_SUFFIX,
    508 					strlen(AB_SLOT_A_SUFFIX))){
    509 			//Set xbl_a as the boot lun
    510 			rc = gpt_utils_set_xbl_boot_partition(NORMAL_BOOT);
    511 		} else if (!strncmp(slot_suffix_arr[slot], AB_SLOT_B_SUFFIX,
    512 					strlen(AB_SLOT_B_SUFFIX))){
    513 			//Set xbl_b as the boot lun
    514 			rc = gpt_utils_set_xbl_boot_partition(BACKUP_BOOT);
    515 		} else {
    516 			//Something has gone terribly terribly wrong
    517 			ALOGE("%s: Unknown slot suffix!", __func__);
    518 			goto error;
    519 		}
    520 		if (rc) {
    521 			ALOGE("%s: Failed to switch xbl boot partition",
    522 					__func__);
    523 			goto error;
    524 		}
    525 	}
    526 	return 0;
    527 error:
    528 	if (disk)
    529 		gpt_disk_free(disk);
    530 	return -1;
    531 }
    532 
    533 int set_slot_as_unbootable(struct boot_control_module *module, unsigned slot)
    534 {
    535 	unsigned num_slots = 0;
    536 	if (!module) {
    537 		ALOGE("%s: Invalid argument", __func__);
    538 		goto error;
    539 	}
    540 	num_slots = get_number_slots(module);
    541 	if (num_slots < 1 || slot > num_slots - 1) {
    542 		ALOGE("%s: Unable to get num_slots/Invalid slot value",
    543 				__func__);
    544 		goto error;
    545 	}
    546 	if (update_slot_attribute(slot_suffix_arr[slot],
    547 				ATTR_UNBOOTABLE)) {
    548 		goto error;
    549 	}
    550 	return 0;
    551 error:
    552 	ALOGE("%s: Failed to mark slot unbootable", __func__);
    553 	return -1;
    554 }
    555 
    556 int is_slot_bootable(struct boot_control_module *module, unsigned slot)
    557 {
    558 	unsigned num_slots = 0;
    559 	int attr = 0;
    560 	char bootPartition[MAX_GPT_NAME_SIZE + 1] = {0};
    561 	if (!module) {
    562 		ALOGE("%s: Invalid argument", __func__);
    563 		goto error;
    564 	}
    565 	num_slots = get_number_slots(module);
    566 	if (num_slots < 1 || slot > num_slots - 1) {
    567 		ALOGE("%s: Unable to get num_slots/Invalid slot value",
    568 				__func__);
    569 		goto error;
    570 	}
    571 	snprintf(bootPartition,
    572 			sizeof(bootPartition) - 1, "boot%s",
    573 			slot_suffix_arr[slot]);
    574 	attr = get_partition_attribute(bootPartition, ATTR_UNBOOTABLE);
    575 	if (attr >= 0)
    576 		return !attr;
    577 error:
    578 	return -1;
    579 }
    580 
    581 int is_slot_marked_successful(struct boot_control_module *module, unsigned slot)
    582 {
    583 	unsigned num_slots = 0;
    584 	int attr = 0;
    585 	char bootPartition[MAX_GPT_NAME_SIZE + 1] = {0};
    586 	if (!module) {
    587 		ALOGE("%s: Invalid argument", __func__);
    588 		goto error;
    589 	}
    590 	num_slots = get_number_slots(module);
    591 	if (num_slots < 1 || slot > num_slots - 1) {
    592 		ALOGE("%s: Unable to get num_slots/Invalid slot value",
    593 				__func__);
    594 		goto error;
    595 	}
    596 	snprintf(bootPartition,
    597 			sizeof(bootPartition) - 1,
    598 			"boot%s", slot_suffix_arr[slot]);
    599 	attr = get_partition_attribute(bootPartition, ATTR_BOOT_SUCCESSFUL);
    600 	if (attr >= 0)
    601 		return attr;
    602 error:
    603 	return -1;
    604 }
    605 
    606 static hw_module_methods_t boot_control_module_methods = {
    607 	.open = NULL,
    608 };
    609 
    610 boot_control_module_t HAL_MODULE_INFO_SYM = {
    611 	.common = {
    612 		.tag = HARDWARE_MODULE_TAG,
    613 		.module_api_version = 1,
    614 		.hal_api_version = 0,
    615 		.id = BOOT_CONTROL_HARDWARE_MODULE_ID,
    616 		.name = "Boot control HAL",
    617 		.author = "Code Aurora Forum",
    618 		.methods = &boot_control_module_methods,
    619 	},
    620 	.init = boot_control_init,
    621 	.getNumberSlots = get_number_slots,
    622 	.getCurrentSlot = get_current_slot,
    623 	.markBootSuccessful = mark_boot_successful,
    624 	.setActiveBootSlot = set_active_boot_slot,
    625 	.setSlotAsUnbootable = set_slot_as_unbootable,
    626 	.isSlotBootable = is_slot_bootable,
    627 	.getSuffix = get_suffix,
    628 	.isSlotMarkedSuccessful = is_slot_marked_successful,
    629 };
    630