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 <map> 30 #include <list> 31 #include <string> 32 #include <vector> 33 #ifdef __cplusplus 34 extern "C" { 35 #endif 36 #include <errno.h> 37 #define LOG_TAG "bootcontrolhal" 38 #include <cutils/log.h> 39 #include <hardware/boot_control.h> 40 #include <stdio.h> 41 #include <string.h> 42 #include <unistd.h> 43 #include <dirent.h> 44 #include <sys/types.h> 45 #include <sys/stat.h> 46 #include <fcntl.h> 47 #include <limits.h> 48 #include <cutils/properties.h> 49 #include "gpt-utils.h" 50 51 #define BOOTDEV_DIR "/dev/block/bootdevice/by-name" 52 #define BOOT_IMG_PTN_NAME "boot" 53 #define LUN_NAME_END_LOC 14 54 #define BOOT_SLOT_PROP "ro.boot.slot_suffix" 55 56 #define SLOT_ACTIVE 1 57 #define SLOT_INACTIVE 2 58 #define UPDATE_SLOT(pentry, guid, slot_state) ({ \ 59 memcpy(pentry, guid, TYPE_GUID_SIZE); \ 60 if (slot_state == SLOT_ACTIVE)\ 61 *(pentry + AB_FLAG_OFFSET) = AB_SLOT_ACTIVE_VAL; \ 62 else if (slot_state == SLOT_INACTIVE) \ 63 *(pentry + AB_FLAG_OFFSET) = (*(pentry + AB_FLAG_OFFSET)& \ 64 ~AB_PARTITION_ATTR_SLOT_ACTIVE); \ 65 }) 66 67 using namespace std; 68 const char *slot_suffix_arr[] = { 69 AB_SLOT_A_SUFFIX, 70 AB_SLOT_B_SUFFIX, 71 NULL}; 72 73 enum part_attr_type { 74 ATTR_SLOT_ACTIVE = 0, 75 ATTR_BOOT_SUCCESSFUL, 76 ATTR_UNBOOTABLE, 77 }; 78 79 void boot_control_init(struct boot_control_module *module) 80 { 81 if (!module) { 82 ALOGE("Invalid argument passed to %s", __func__); 83 return; 84 } 85 return; 86 } 87 88 //Get the value of one of the attribute fields for a partition. 89 static int get_partition_attribute(char *partname, 90 enum part_attr_type part_attr) 91 { 92 struct gpt_disk *disk = NULL; 93 uint8_t *pentry = NULL; 94 int retval = -1; 95 uint8_t *attr = NULL; 96 if (!partname) 97 goto error; 98 disk = gpt_disk_alloc(); 99 if (!disk) { 100 ALOGE("%s: Failed to alloc disk struct", __func__); 101 goto error; 102 } 103 if (gpt_disk_get_disk_info(partname, disk)) { 104 ALOGE("%s: Failed to get disk info", __func__); 105 goto error; 106 } 107 pentry = gpt_disk_get_pentry(disk, partname, PRIMARY_GPT); 108 if (!pentry) { 109 ALOGE("%s: pentry does not exist in disk struct", 110 __func__); 111 goto error; 112 } 113 attr = pentry + AB_FLAG_OFFSET; 114 if (part_attr == ATTR_SLOT_ACTIVE) 115 retval = !!(*attr & AB_PARTITION_ATTR_SLOT_ACTIVE); 116 else if (part_attr == ATTR_BOOT_SUCCESSFUL) 117 retval = !!(*attr & AB_PARTITION_ATTR_BOOT_SUCCESSFUL); 118 else if (part_attr == ATTR_UNBOOTABLE) 119 retval = !!(*attr & AB_PARTITION_ATTR_UNBOOTABLE); 120 else 121 retval = -1; 122 gpt_disk_free(disk); 123 return retval; 124 error: 125 if (disk) 126 gpt_disk_free(disk); 127 return retval; 128 } 129 130 //Set a particular attribute for all the partitions in a 131 //slot 132 static int update_slot_attribute(const char *slot, 133 enum part_attr_type ab_attr) 134 { 135 unsigned int i = 0; 136 char buf[PATH_MAX]; 137 struct stat st; 138 struct gpt_disk *disk = NULL; 139 uint8_t *pentry = NULL; 140 uint8_t *pentry_bak = NULL; 141 int rc = -1; 142 uint8_t *attr = NULL; 143 uint8_t *attr_bak = NULL; 144 char partName[MAX_GPT_NAME_SIZE + 1] = {0}; 145 const char ptn_list[][MAX_GPT_NAME_SIZE] = { AB_PTN_LIST }; 146 int slot_name_valid = 0; 147 if (!slot) { 148 ALOGE("%s: Invalid argument", __func__); 149 goto error; 150 } 151 for (i = 0; slot_suffix_arr[i] != NULL; i++) 152 { 153 if (!strncmp(slot, slot_suffix_arr[i], 154 strlen(slot_suffix_arr[i]))) 155 slot_name_valid = 1; 156 } 157 if (!slot_name_valid) { 158 ALOGE("%s: Invalid slot name", __func__); 159 goto error; 160 } 161 for (i=0; i < ARRAY_SIZE(ptn_list); i++) { 162 memset(buf, '\0', sizeof(buf)); 163 //Check if A/B versions of this ptn exist 164 snprintf(buf, sizeof(buf) - 1, 165 "%s/%s%s", 166 BOOT_DEV_DIR, 167 ptn_list[i], 168 AB_SLOT_A_SUFFIX 169 ); 170 if (stat(buf, &st)) { 171 //partition does not have _a version 172 continue; 173 } 174 memset(buf, '\0', sizeof(buf)); 175 snprintf(buf, sizeof(buf) - 1, 176 "%s/%s%s", 177 BOOT_DEV_DIR, 178 ptn_list[i], 179 AB_SLOT_B_SUFFIX 180 ); 181 if (stat(buf, &st)) { 182 //partition does not have _a version 183 continue; 184 } 185 memset(partName, '\0', sizeof(partName)); 186 snprintf(partName, 187 sizeof(partName) - 1, 188 "%s%s", 189 ptn_list[i], 190 slot); 191 disk = gpt_disk_alloc(); 192 if (!disk) { 193 ALOGE("%s: Failed to alloc disk struct", 194 __func__); 195 goto error; 196 } 197 rc = gpt_disk_get_disk_info(partName, disk); 198 if (rc != 0) { 199 ALOGE("%s: Failed to get disk info for %s", 200 __func__, 201 partName); 202 goto error; 203 } 204 pentry = gpt_disk_get_pentry(disk, partName, PRIMARY_GPT); 205 pentry_bak = gpt_disk_get_pentry(disk, partName, SECONDARY_GPT); 206 if (!pentry || !pentry_bak) { 207 ALOGE("%s: Failed to get pentry/pentry_bak for %s", 208 __func__, 209 partName); 210 goto error; 211 } 212 attr = pentry + AB_FLAG_OFFSET; 213 attr_bak = pentry_bak + AB_FLAG_OFFSET; 214 if (ab_attr == ATTR_BOOT_SUCCESSFUL) { 215 *attr = (*attr) | AB_PARTITION_ATTR_BOOT_SUCCESSFUL; 216 *attr_bak = (*attr_bak) | 217 AB_PARTITION_ATTR_BOOT_SUCCESSFUL; 218 } else if (ab_attr == ATTR_UNBOOTABLE) { 219 *attr = (*attr) | AB_PARTITION_ATTR_UNBOOTABLE; 220 *attr_bak = (*attr_bak) | AB_PARTITION_ATTR_UNBOOTABLE; 221 } else if (ab_attr == ATTR_SLOT_ACTIVE) { 222 *attr = (*attr) | AB_PARTITION_ATTR_SLOT_ACTIVE; 223 *attr_bak = (*attr) | AB_PARTITION_ATTR_SLOT_ACTIVE; 224 } else { 225 ALOGE("%s: Unrecognized attr", __func__); 226 goto error; 227 } 228 if (gpt_disk_update_crc(disk)) { 229 ALOGE("%s: Failed to update crc for %s", 230 __func__, 231 partName); 232 goto error; 233 } 234 if (gpt_disk_commit(disk)) { 235 ALOGE("%s: Failed to write back entry for %s", 236 __func__, 237 partName); 238 goto error; 239 } 240 gpt_disk_free(disk); 241 disk = NULL; 242 } 243 return 0; 244 error: 245 if (disk) 246 gpt_disk_free(disk); 247 return -1; 248 } 249 250 unsigned get_number_slots(struct boot_control_module *module) 251 { 252 struct dirent *de = NULL; 253 DIR *dir_bootdev = NULL; 254 unsigned slot_count = 0; 255 if (!module) { 256 ALOGE("%s: Invalid argument", __func__); 257 goto error; 258 } 259 dir_bootdev = opendir(BOOTDEV_DIR); 260 if (!dir_bootdev) { 261 ALOGE("%s: Failed to open bootdev dir (%s)", 262 __func__, 263 strerror(errno)); 264 goto error; 265 } 266 while ((de = readdir(dir_bootdev))) { 267 if (de->d_name[0] == '.') 268 continue; 269 if (!strncmp(de->d_name, BOOT_IMG_PTN_NAME, 270 strlen(BOOT_IMG_PTN_NAME))) 271 slot_count++; 272 } 273 closedir(dir_bootdev); 274 return slot_count; 275 error: 276 if (dir_bootdev) 277 closedir(dir_bootdev); 278 return 0; 279 } 280 281 unsigned get_current_slot(struct boot_control_module *module) 282 { 283 uint32_t num_slots = 0; 284 char bootSlotProp[PROPERTY_VALUE_MAX] = {'\0'}; 285 unsigned i = 0; 286 if (!module) { 287 ALOGE("%s: Invalid argument", __func__); 288 goto error; 289 } 290 num_slots = get_number_slots(module); 291 if (num_slots <= 1) { 292 //Slot 0 is the only slot around. 293 return 0; 294 } 295 property_get(BOOT_SLOT_PROP, bootSlotProp, "N/A"); 296 if (!strncmp(bootSlotProp, "N/A", strlen("N/A"))) { 297 ALOGE("%s: Unable to read boot slot property", 298 __func__); 299 goto error; 300 } 301 //Iterate through a list of partitons named as boot+suffix 302 //and see which one is currently active. 303 for (i = 0; slot_suffix_arr[i] != NULL ; i++) { 304 if (!strncmp(bootSlotProp, 305 slot_suffix_arr[i], 306 strlen(slot_suffix_arr[i]))) 307 return i; 308 } 309 error: 310 //The HAL spec requires that we return a number between 311 //0 to num_slots - 1. Since something went wrong here we 312 //are just going to return the default slot. 313 return 0; 314 } 315 316 static int boot_control_check_slot_sanity(struct boot_control_module *module, 317 unsigned slot) 318 { 319 if (!module) 320 return -1; 321 uint32_t num_slots = get_number_slots(module); 322 if ((num_slots < 1) || (slot > num_slots - 1)) { 323 ALOGE("Invalid slot number"); 324 return -1; 325 } 326 return 0; 327 328 } 329 330 int mark_boot_successful(struct boot_control_module *module) 331 { 332 unsigned cur_slot = 0; 333 if (!module) { 334 ALOGE("%s: Invalid argument", __func__); 335 goto error; 336 } 337 cur_slot = get_current_slot(module); 338 if (update_slot_attribute(slot_suffix_arr[cur_slot], 339 ATTR_BOOT_SUCCESSFUL)) { 340 goto error; 341 } 342 return 0; 343 error: 344 ALOGE("%s: Failed to mark boot successful", __func__); 345 return -1; 346 } 347 348 const char *get_suffix(struct boot_control_module *module, unsigned slot) 349 { 350 if (boot_control_check_slot_sanity(module, slot) != 0) 351 return NULL; 352 else 353 return slot_suffix_arr[slot]; 354 } 355 356 357 //Return a gpt disk structure representing the disk that holds 358 //partition. 359 static struct gpt_disk* boot_ctl_get_disk_info(char *partition) 360 { 361 struct gpt_disk *disk = NULL; 362 if (!partition) 363 return NULL; 364 disk = gpt_disk_alloc(); 365 if (!disk) { 366 ALOGE("%s: Failed to alloc disk", 367 __func__); 368 goto error; 369 } 370 if (gpt_disk_get_disk_info(partition, disk)) { 371 ALOGE("failed to get disk info for %s", 372 partition); 373 goto error; 374 } 375 return disk; 376 error: 377 if (disk) 378 gpt_disk_free(disk); 379 return NULL; 380 } 381 382 //The argument here is a vector of partition names(including the slot suffix) 383 //that lie on a single disk 384 static int boot_ctl_set_active_slot_for_partitions(vector<string> part_list, 385 unsigned slot) 386 { 387 char buf[PATH_MAX] = {0}; 388 struct gpt_disk *disk = NULL; 389 char slotA[MAX_GPT_NAME_SIZE + 1] = {0}; 390 char slotB[MAX_GPT_NAME_SIZE + 1] = {0}; 391 char active_guid[TYPE_GUID_SIZE + 1] = {0}; 392 char inactive_guid[TYPE_GUID_SIZE + 1] = {0}; 393 //Pointer to the partition entry of current 'A' partition 394 uint8_t *pentryA = NULL; 395 uint8_t *pentryA_bak = NULL; 396 //Pointer to partition entry of current 'B' partition 397 uint8_t *pentryB = NULL; 398 uint8_t *pentryB_bak = NULL; 399 struct stat st; 400 vector<string>::iterator partition_iterator; 401 402 for (partition_iterator = part_list.begin(); 403 partition_iterator != part_list.end(); 404 partition_iterator++) { 405 //Chop off the slot suffix from the partition name to 406 //make the string easier to work with. 407 string prefix = *partition_iterator; 408 if (prefix.size() < (strlen(AB_SLOT_A_SUFFIX) + 1)) { 409 ALOGE("Invalid partition name: %s", prefix.c_str()); 410 goto error; 411 } 412 prefix.resize(prefix.size() - strlen(AB_SLOT_A_SUFFIX)); 413 //Check if A/B versions of this ptn exist 414 snprintf(buf, sizeof(buf) - 1, "%s/%s%s", BOOT_DEV_DIR, 415 prefix.c_str(), 416 AB_SLOT_A_SUFFIX); 417 if (stat(buf, &st)) 418 continue; 419 memset(buf, '\0', sizeof(buf)); 420 snprintf(buf, sizeof(buf) - 1, "%s/%s%s", BOOT_DEV_DIR, 421 prefix.c_str(), 422 AB_SLOT_B_SUFFIX); 423 if (stat(buf, &st)) 424 continue; 425 memset(slotA, 0, sizeof(slotA)); 426 memset(slotB, 0, sizeof(slotA)); 427 snprintf(slotA, sizeof(slotA) - 1, "%s%s", prefix.c_str(), 428 AB_SLOT_A_SUFFIX); 429 snprintf(slotB, sizeof(slotB) - 1,"%s%s", prefix.c_str(), 430 AB_SLOT_B_SUFFIX); 431 //Get the disk containing the partitions that were passed in. 432 //All partitions passed in must lie on the same disk. 433 if (!disk) { 434 disk = boot_ctl_get_disk_info(slotA); 435 if (!disk) 436 goto error; 437 } 438 //Get partition entry for slot A & B from the primary 439 //and backup tables. 440 pentryA = gpt_disk_get_pentry(disk, slotA, PRIMARY_GPT); 441 pentryA_bak = gpt_disk_get_pentry(disk, slotA, SECONDARY_GPT); 442 pentryB = gpt_disk_get_pentry(disk, slotB, PRIMARY_GPT); 443 pentryB_bak = gpt_disk_get_pentry(disk, slotB, SECONDARY_GPT); 444 if ( !pentryA || !pentryA_bak || !pentryB || !pentryB_bak) { 445 //None of these should be NULL since we have already 446 //checked for A & B versions earlier. 447 ALOGE("Slot pentries for %s not found.", 448 prefix.c_str()); 449 goto error; 450 } 451 memset(active_guid, '\0', sizeof(active_guid)); 452 memset(inactive_guid, '\0', sizeof(inactive_guid)); 453 if (get_partition_attribute(slotA, ATTR_SLOT_ACTIVE) == 1) { 454 //A is the current active slot 455 memcpy((void*)active_guid, (const void*)pentryA, 456 TYPE_GUID_SIZE); 457 memcpy((void*)inactive_guid,(const void*)pentryB, 458 TYPE_GUID_SIZE); 459 } else if (get_partition_attribute(slotB, 460 ATTR_SLOT_ACTIVE) == 1) { 461 //B is the current active slot 462 memcpy((void*)active_guid, (const void*)pentryB, 463 TYPE_GUID_SIZE); 464 memcpy((void*)inactive_guid, (const void*)pentryA, 465 TYPE_GUID_SIZE); 466 } else { 467 ALOGE("Both A & B are inactive..Aborting"); 468 goto error; 469 } 470 if (!strncmp(slot_suffix_arr[slot], AB_SLOT_A_SUFFIX, 471 strlen(AB_SLOT_A_SUFFIX))){ 472 //Mark A as active in primary table 473 UPDATE_SLOT(pentryA, active_guid, SLOT_ACTIVE); 474 //Mark A as active in backup table 475 UPDATE_SLOT(pentryA_bak, active_guid, SLOT_ACTIVE); 476 //Mark B as inactive in primary table 477 UPDATE_SLOT(pentryB, inactive_guid, SLOT_INACTIVE); 478 //Mark B as inactive in backup table 479 UPDATE_SLOT(pentryB_bak, inactive_guid, SLOT_INACTIVE); 480 } else if (!strncmp(slot_suffix_arr[slot], AB_SLOT_B_SUFFIX, 481 strlen(AB_SLOT_B_SUFFIX))){ 482 //Mark B as active in primary table 483 UPDATE_SLOT(pentryB, active_guid, SLOT_ACTIVE); 484 //Mark B as active in backup table 485 UPDATE_SLOT(pentryB_bak, active_guid, SLOT_ACTIVE); 486 //Mark A as inavtive in primary table 487 UPDATE_SLOT(pentryA, inactive_guid, SLOT_INACTIVE); 488 //Mark A as inactive in backup table 489 UPDATE_SLOT(pentryA_bak, inactive_guid, SLOT_INACTIVE); 490 } else { 491 //Something has gone terribly terribly wrong 492 ALOGE("%s: Unknown slot suffix!", __func__); 493 goto error; 494 } 495 if (disk) { 496 if (gpt_disk_update_crc(disk) != 0) { 497 ALOGE("%s: Failed to update gpt_disk crc", 498 __func__); 499 goto error; 500 } 501 } 502 } 503 //write updated content to disk 504 if (disk) { 505 if (gpt_disk_commit(disk)) { 506 ALOGE("Failed to commit disk entry"); 507 goto error; 508 } 509 gpt_disk_free(disk); 510 } 511 return 0; 512 513 error: 514 if (disk) 515 gpt_disk_free(disk); 516 return -1; 517 } 518 519 int set_active_boot_slot(struct boot_control_module *module, unsigned slot) 520 { 521 map<string, vector<string>> ptn_map; 522 vector<string> ptn_vec; 523 const char ptn_list[][MAX_GPT_NAME_SIZE] = { AB_PTN_LIST }; 524 uint32_t i; 525 int rc = -1; 526 int is_ufs = gpt_utils_is_ufs_device(); 527 map<string, vector<string>>::iterator map_iter; 528 vector<string>::iterator string_iter; 529 530 if (boot_control_check_slot_sanity(module, slot)) { 531 ALOGE("%s: Bad arguments", __func__); 532 goto error; 533 } 534 //The partition list just contains prefixes(without the _a/_b) of the 535 //partitions that support A/B. In order to get the layout we need the 536 //actual names. To do this we append the slot suffix to every member 537 //in the list. 538 for (i = 0; i < ARRAY_SIZE(ptn_list); i++) { 539 //XBL is handled differrently for ufs devices so ignore it 540 if (is_ufs && !strncmp(ptn_list[i], PTN_XBL, strlen(PTN_XBL))) 541 continue; 542 //The partition list will be the list of _a partitions 543 string cur_ptn = ptn_list[i]; 544 cur_ptn.append(AB_SLOT_A_SUFFIX); 545 ptn_vec.push_back(cur_ptn); 546 547 } 548 //The partition map gives us info in the following format: 549 // [path_to_block_device_1]--><partitions on device 1> 550 // [path_to_block_device_2]--><partitions on device 2> 551 // ... 552 // ... 553 // eg: 554 // [/dev/block/sdb]---><system, boot, rpm, tz,....> 555 if (gpt_utils_get_partition_map(ptn_vec, ptn_map)) { 556 ALOGE("%s: Failed to get partition map", 557 __func__); 558 goto error; 559 } 560 for (map_iter = ptn_map.begin(); map_iter != ptn_map.end(); map_iter++){ 561 if (map_iter->second.size() < 1) 562 continue; 563 boot_ctl_set_active_slot_for_partitions(map_iter->second, slot); 564 } 565 if (is_ufs) { 566 if (!strncmp(slot_suffix_arr[slot], AB_SLOT_A_SUFFIX, 567 strlen(AB_SLOT_A_SUFFIX))){ 568 //Set xbl_a as the boot lun 569 rc = gpt_utils_set_xbl_boot_partition(NORMAL_BOOT); 570 } else if (!strncmp(slot_suffix_arr[slot], AB_SLOT_B_SUFFIX, 571 strlen(AB_SLOT_B_SUFFIX))){ 572 //Set xbl_b as the boot lun 573 rc = gpt_utils_set_xbl_boot_partition(BACKUP_BOOT); 574 } else { 575 //Something has gone terribly terribly wrong 576 ALOGE("%s: Unknown slot suffix!", __func__); 577 goto error; 578 } 579 if (rc) { 580 ALOGE("%s: Failed to switch xbl boot partition", 581 __func__); 582 goto error; 583 } 584 } 585 return 0; 586 error: 587 return -1; 588 } 589 590 int set_slot_as_unbootable(struct boot_control_module *module, unsigned slot) 591 { 592 if (boot_control_check_slot_sanity(module, slot) != 0) { 593 ALOGE("%s: Argument check failed", __func__); 594 goto error; 595 } 596 if (update_slot_attribute(slot_suffix_arr[slot], 597 ATTR_UNBOOTABLE)) { 598 goto error; 599 } 600 return 0; 601 error: 602 ALOGE("%s: Failed to mark slot unbootable", __func__); 603 return -1; 604 } 605 606 int is_slot_bootable(struct boot_control_module *module, unsigned slot) 607 { 608 int attr = 0; 609 char bootPartition[MAX_GPT_NAME_SIZE + 1] = {0}; 610 611 if (boot_control_check_slot_sanity(module, slot) != 0) { 612 ALOGE("%s: Argument check failed", __func__); 613 goto error; 614 } 615 snprintf(bootPartition, 616 sizeof(bootPartition) - 1, "boot%s", 617 slot_suffix_arr[slot]); 618 attr = get_partition_attribute(bootPartition, ATTR_UNBOOTABLE); 619 if (attr >= 0) 620 return !attr; 621 error: 622 return -1; 623 } 624 625 int is_slot_marked_successful(struct boot_control_module *module, unsigned slot) 626 { 627 int attr = 0; 628 char bootPartition[MAX_GPT_NAME_SIZE + 1] = {0}; 629 630 if (boot_control_check_slot_sanity(module, slot) != 0) { 631 ALOGE("%s: Argument check failed", __func__); 632 goto error; 633 } 634 snprintf(bootPartition, 635 sizeof(bootPartition) - 1, 636 "boot%s", slot_suffix_arr[slot]); 637 attr = get_partition_attribute(bootPartition, ATTR_BOOT_SUCCESSFUL); 638 if (attr >= 0) 639 return attr; 640 error: 641 return -1; 642 } 643 644 static hw_module_methods_t boot_control_module_methods = { 645 .open = NULL, 646 }; 647 648 boot_control_module_t HAL_MODULE_INFO_SYM = { 649 .common = { 650 .tag = HARDWARE_MODULE_TAG, 651 .module_api_version = 1, 652 .hal_api_version = 0, 653 .id = BOOT_CONTROL_HARDWARE_MODULE_ID, 654 .name = "Boot control HAL", 655 .author = "Code Aurora Forum", 656 .methods = &boot_control_module_methods, 657 }, 658 .init = boot_control_init, 659 .getNumberSlots = get_number_slots, 660 .getCurrentSlot = get_current_slot, 661 .markBootSuccessful = mark_boot_successful, 662 .setActiveBootSlot = set_active_boot_slot, 663 .setSlotAsUnbootable = set_slot_as_unbootable, 664 .isSlotBootable = is_slot_bootable, 665 .getSuffix = get_suffix, 666 .isSlotMarkedSuccessful = is_slot_marked_successful, 667 }; 668 #ifdef __cplusplus 669 } 670 #endif 671