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