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
34extern "C" {
35#endif
36#include <errno.h>
37#define LOG_TAG "bootcontrolhal"
38#include <log/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
67using namespace std;
68const char *slot_suffix_arr[] = {
69	AB_SLOT_A_SUFFIX,
70	AB_SLOT_B_SUFFIX,
71	NULL};
72
73enum part_attr_type {
74	ATTR_SLOT_ACTIVE = 0,
75	ATTR_BOOT_SUCCESSFUL,
76	ATTR_UNBOOTABLE,
77};
78
79void 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.
89static 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;
124error:
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
132static 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;
244error:
245	if (disk)
246		gpt_disk_free(disk);
247	return -1;
248}
249
250unsigned 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		static_assert(AB_SLOT_A_SUFFIX[0] == '_', "Breaking change to slot A suffix");
270		static_assert(AB_SLOT_B_SUFFIX[0] == '_', "Breaking change to slot B suffix");
271		if (!strncmp(de->d_name, BOOT_IMG_PTN_NAME,
272					strlen(BOOT_IMG_PTN_NAME)))
273			slot_count++;
274	}
275	closedir(dir_bootdev);
276	return slot_count;
277error:
278	if (dir_bootdev)
279		closedir(dir_bootdev);
280	return 0;
281}
282
283unsigned get_current_slot(struct boot_control_module *module)
284{
285	uint32_t num_slots = 0;
286	char bootSlotProp[PROPERTY_VALUE_MAX] = {'\0'};
287	unsigned i = 0;
288	if (!module) {
289		ALOGE("%s: Invalid argument", __func__);
290		goto error;
291	}
292	num_slots = get_number_slots(module);
293	if (num_slots <= 1) {
294		//Slot 0 is the only slot around.
295		return 0;
296	}
297	property_get(BOOT_SLOT_PROP, bootSlotProp, "N/A");
298	if (!strncmp(bootSlotProp, "N/A", strlen("N/A"))) {
299		ALOGE("%s: Unable to read boot slot property",
300				__func__);
301		goto error;
302	}
303	//Iterate through a list of partitons named as boot+suffix
304	//and see which one is currently active.
305	for (i = 0; slot_suffix_arr[i] != NULL ; i++) {
306		if (!strncmp(bootSlotProp,
307					slot_suffix_arr[i],
308					strlen(slot_suffix_arr[i])))
309				return i;
310	}
311error:
312	//The HAL spec requires that we return a number between
313	//0 to num_slots - 1. Since something went wrong here we
314	//are just going to return the default slot.
315	return 0;
316}
317
318static int boot_control_check_slot_sanity(struct boot_control_module *module,
319		unsigned slot)
320{
321	if (!module)
322		return -1;
323	uint32_t num_slots = get_number_slots(module);
324	if ((num_slots < 1) || (slot > num_slots - 1)) {
325		ALOGE("Invalid slot number");
326		return -1;
327	}
328	return 0;
329
330}
331
332int mark_boot_successful(struct boot_control_module *module)
333{
334	unsigned cur_slot = 0;
335	if (!module) {
336		ALOGE("%s: Invalid argument", __func__);
337		goto error;
338	}
339	cur_slot = get_current_slot(module);
340	if (update_slot_attribute(slot_suffix_arr[cur_slot],
341				ATTR_BOOT_SUCCESSFUL)) {
342		goto error;
343	}
344	return 0;
345error:
346	ALOGE("%s: Failed to mark boot successful", __func__);
347	return -1;
348}
349
350const char *get_suffix(struct boot_control_module *module, unsigned slot)
351{
352	if (boot_control_check_slot_sanity(module, slot) != 0)
353		return NULL;
354	else
355		return slot_suffix_arr[slot];
356}
357
358
359//Return a gpt disk structure representing the disk that holds
360//partition.
361static struct gpt_disk* boot_ctl_get_disk_info(char *partition)
362{
363	struct gpt_disk *disk = NULL;
364	if (!partition)
365		return NULL;
366	disk = gpt_disk_alloc();
367	if (!disk) {
368		ALOGE("%s: Failed to alloc disk",
369				__func__);
370		goto error;
371	}
372	if (gpt_disk_get_disk_info(partition, disk)) {
373		ALOGE("failed to get disk info for %s",
374				partition);
375		goto error;
376	}
377	return disk;
378error:
379	if (disk)
380		gpt_disk_free(disk);
381	return NULL;
382}
383
384//The argument here is a vector of partition names(including the slot suffix)
385//that lie on a single disk
386static int boot_ctl_set_active_slot_for_partitions(vector<string> part_list,
387		unsigned slot)
388{
389	char buf[PATH_MAX] = {0};
390	struct gpt_disk *disk = NULL;
391	char slotA[MAX_GPT_NAME_SIZE + 1] = {0};
392	char slotB[MAX_GPT_NAME_SIZE + 1] = {0};
393	char active_guid[TYPE_GUID_SIZE + 1] = {0};
394	char inactive_guid[TYPE_GUID_SIZE + 1] = {0};
395	//Pointer to the partition entry of current 'A' partition
396	uint8_t *pentryA = NULL;
397	uint8_t *pentryA_bak = NULL;
398	//Pointer to partition entry of current 'B' partition
399	uint8_t *pentryB = NULL;
400	uint8_t *pentryB_bak = NULL;
401	struct stat st;
402	vector<string>::iterator partition_iterator;
403
404	for (partition_iterator = part_list.begin();
405			partition_iterator != part_list.end();
406			partition_iterator++) {
407		//Chop off the slot suffix from the partition name to
408		//make the string easier to work with.
409		string prefix = *partition_iterator;
410		if (prefix.size() < (strlen(AB_SLOT_A_SUFFIX) + 1)) {
411			ALOGE("Invalid partition name: %s", prefix.c_str());
412			goto error;
413		}
414		prefix.resize(prefix.size() - strlen(AB_SLOT_A_SUFFIX));
415		//Check if A/B versions of this ptn exist
416		snprintf(buf, sizeof(buf) - 1, "%s/%s%s", BOOT_DEV_DIR,
417				prefix.c_str(),
418				AB_SLOT_A_SUFFIX);
419		if (stat(buf, &st))
420			continue;
421		memset(buf, '\0', sizeof(buf));
422		snprintf(buf, sizeof(buf) - 1, "%s/%s%s", BOOT_DEV_DIR,
423				prefix.c_str(),
424				AB_SLOT_B_SUFFIX);
425		if (stat(buf, &st))
426			continue;
427		memset(slotA, 0, sizeof(slotA));
428		memset(slotB, 0, sizeof(slotA));
429		snprintf(slotA, sizeof(slotA) - 1, "%s%s", prefix.c_str(),
430				AB_SLOT_A_SUFFIX);
431		snprintf(slotB, sizeof(slotB) - 1,"%s%s", prefix.c_str(),
432				AB_SLOT_B_SUFFIX);
433		//Get the disk containing the partitions that were passed in.
434		//All partitions passed in must lie on the same disk.
435		if (!disk) {
436			disk = boot_ctl_get_disk_info(slotA);
437			if (!disk)
438				goto error;
439		}
440		//Get partition entry for slot A & B from the primary
441		//and backup tables.
442		pentryA = gpt_disk_get_pentry(disk, slotA, PRIMARY_GPT);
443		pentryA_bak = gpt_disk_get_pentry(disk, slotA, SECONDARY_GPT);
444		pentryB = gpt_disk_get_pentry(disk, slotB, PRIMARY_GPT);
445		pentryB_bak = gpt_disk_get_pentry(disk, slotB, SECONDARY_GPT);
446		if ( !pentryA || !pentryA_bak || !pentryB || !pentryB_bak) {
447			//None of these should be NULL since we have already
448			//checked for A & B versions earlier.
449			ALOGE("Slot pentries for %s not found.",
450					prefix.c_str());
451			goto error;
452		}
453		memset(active_guid, '\0', sizeof(active_guid));
454		memset(inactive_guid, '\0', sizeof(inactive_guid));
455		if (get_partition_attribute(slotA, ATTR_SLOT_ACTIVE) == 1) {
456			//A is the current active slot
457			memcpy((void*)active_guid, (const void*)pentryA,
458					TYPE_GUID_SIZE);
459			memcpy((void*)inactive_guid,(const void*)pentryB,
460					TYPE_GUID_SIZE);
461		} else if (get_partition_attribute(slotB,
462					ATTR_SLOT_ACTIVE) == 1) {
463			//B is the current active slot
464			memcpy((void*)active_guid, (const void*)pentryB,
465					TYPE_GUID_SIZE);
466			memcpy((void*)inactive_guid, (const void*)pentryA,
467					TYPE_GUID_SIZE);
468		} else {
469			ALOGE("Both A & B are inactive..Aborting");
470			goto error;
471		}
472		if (!strncmp(slot_suffix_arr[slot], AB_SLOT_A_SUFFIX,
473					strlen(AB_SLOT_A_SUFFIX))){
474			//Mark A as active in primary table
475			UPDATE_SLOT(pentryA, active_guid, SLOT_ACTIVE);
476			//Mark A as active in backup table
477			UPDATE_SLOT(pentryA_bak, active_guid, SLOT_ACTIVE);
478			//Mark B as inactive in primary table
479			UPDATE_SLOT(pentryB, inactive_guid, SLOT_INACTIVE);
480			//Mark B as inactive in backup table
481			UPDATE_SLOT(pentryB_bak, inactive_guid, SLOT_INACTIVE);
482		} else if (!strncmp(slot_suffix_arr[slot], AB_SLOT_B_SUFFIX,
483					strlen(AB_SLOT_B_SUFFIX))){
484			//Mark B as active in primary table
485			UPDATE_SLOT(pentryB, active_guid, SLOT_ACTIVE);
486			//Mark B as active in backup table
487			UPDATE_SLOT(pentryB_bak, active_guid, SLOT_ACTIVE);
488			//Mark A as inavtive in primary table
489			UPDATE_SLOT(pentryA, inactive_guid, SLOT_INACTIVE);
490			//Mark A as inactive in backup table
491			UPDATE_SLOT(pentryA_bak, inactive_guid, SLOT_INACTIVE);
492		} else {
493			//Something has gone terribly terribly wrong
494			ALOGE("%s: Unknown slot suffix!", __func__);
495			goto error;
496		}
497		if (disk) {
498			if (gpt_disk_update_crc(disk) != 0) {
499				ALOGE("%s: Failed to update gpt_disk crc",
500						__func__);
501				goto error;
502			}
503		}
504	}
505	//write updated content to disk
506	if (disk) {
507		if (gpt_disk_commit(disk)) {
508			ALOGE("Failed to commit disk entry");
509			goto error;
510		}
511		gpt_disk_free(disk);
512	}
513	return 0;
514
515error:
516	if (disk)
517		gpt_disk_free(disk);
518	return -1;
519}
520
521int set_active_boot_slot(struct boot_control_module *module, unsigned slot)
522{
523	map<string, vector<string>> ptn_map;
524	vector<string> ptn_vec;
525	const char ptn_list[][MAX_GPT_NAME_SIZE] = { AB_PTN_LIST };
526	uint32_t i;
527	int rc = -1;
528	int is_ufs = gpt_utils_is_ufs_device();
529	map<string, vector<string>>::iterator map_iter;
530	vector<string>::iterator string_iter;
531
532	if (boot_control_check_slot_sanity(module, slot)) {
533		ALOGE("%s: Bad arguments", __func__);
534		goto error;
535	}
536	//The partition list just contains prefixes(without the _a/_b) of the
537	//partitions that support A/B. In order to get the layout we need the
538	//actual names. To do this we append the slot suffix to every member
539	//in the list.
540	for (i = 0; i < ARRAY_SIZE(ptn_list); i++) {
541		//XBL is handled differrently for ufs devices so ignore it
542		if (is_ufs && !strncmp(ptn_list[i], PTN_XBL, strlen(PTN_XBL)))
543				continue;
544		//The partition list will be the list of _a partitions
545		string cur_ptn = ptn_list[i];
546		cur_ptn.append(AB_SLOT_A_SUFFIX);
547		ptn_vec.push_back(cur_ptn);
548
549	}
550	//The partition map gives us info in the following format:
551	// [path_to_block_device_1]--><partitions on device 1>
552	// [path_to_block_device_2]--><partitions on device 2>
553	// ...
554	// ...
555	// eg:
556	// [/dev/block/sdb]---><system, boot, rpm, tz,....>
557	if (gpt_utils_get_partition_map(ptn_vec, ptn_map)) {
558		ALOGE("%s: Failed to get partition map",
559				__func__);
560		goto error;
561	}
562	for (map_iter = ptn_map.begin(); map_iter != ptn_map.end(); map_iter++){
563		if (map_iter->second.size() < 1)
564			continue;
565		if (boot_ctl_set_active_slot_for_partitions(map_iter->second, slot)) {
566			ALOGE("%s: Failed to set active slot for partitions ", __func__);;
567			goto error;
568		}
569	}
570	if (is_ufs) {
571		if (!strncmp(slot_suffix_arr[slot], AB_SLOT_A_SUFFIX,
572					strlen(AB_SLOT_A_SUFFIX))){
573			//Set xbl_a as the boot lun
574			rc = gpt_utils_set_xbl_boot_partition(NORMAL_BOOT);
575		} else if (!strncmp(slot_suffix_arr[slot], AB_SLOT_B_SUFFIX,
576					strlen(AB_SLOT_B_SUFFIX))){
577			//Set xbl_b as the boot lun
578			rc = gpt_utils_set_xbl_boot_partition(BACKUP_BOOT);
579		} else {
580			//Something has gone terribly terribly wrong
581			ALOGE("%s: Unknown slot suffix!", __func__);
582			goto error;
583		}
584		if (rc) {
585			ALOGE("%s: Failed to switch xbl boot partition",
586					__func__);
587			goto error;
588		}
589	}
590	return 0;
591error:
592	return -1;
593}
594
595int set_slot_as_unbootable(struct boot_control_module *module, unsigned slot)
596{
597	if (boot_control_check_slot_sanity(module, slot) != 0) {
598		ALOGE("%s: Argument check failed", __func__);
599		goto error;
600	}
601	if (update_slot_attribute(slot_suffix_arr[slot],
602				ATTR_UNBOOTABLE)) {
603		goto error;
604	}
605	return 0;
606error:
607	ALOGE("%s: Failed to mark slot unbootable", __func__);
608	return -1;
609}
610
611int is_slot_bootable(struct boot_control_module *module, unsigned slot)
612{
613	int attr = 0;
614	char bootPartition[MAX_GPT_NAME_SIZE + 1] = {0};
615
616	if (boot_control_check_slot_sanity(module, slot) != 0) {
617		ALOGE("%s: Argument check failed", __func__);
618		goto error;
619	}
620	snprintf(bootPartition,
621			sizeof(bootPartition) - 1, "boot%s",
622			slot_suffix_arr[slot]);
623	attr = get_partition_attribute(bootPartition, ATTR_UNBOOTABLE);
624	if (attr >= 0)
625		return !attr;
626error:
627	return -1;
628}
629
630int is_slot_marked_successful(struct boot_control_module *module, unsigned slot)
631{
632	int attr = 0;
633	char bootPartition[MAX_GPT_NAME_SIZE + 1] = {0};
634
635	if (boot_control_check_slot_sanity(module, slot) != 0) {
636		ALOGE("%s: Argument check failed", __func__);
637		goto error;
638	}
639	snprintf(bootPartition,
640			sizeof(bootPartition) - 1,
641			"boot%s", slot_suffix_arr[slot]);
642	attr = get_partition_attribute(bootPartition, ATTR_BOOT_SUCCESSFUL);
643	if (attr >= 0)
644		return attr;
645error:
646	return -1;
647}
648
649static hw_module_methods_t boot_control_module_methods = {
650	.open = NULL,
651};
652
653boot_control_module_t HAL_MODULE_INFO_SYM = {
654	.common = {
655		.tag = HARDWARE_MODULE_TAG,
656		.module_api_version = 1,
657		.hal_api_version = 0,
658		.id = BOOT_CONTROL_HARDWARE_MODULE_ID,
659		.name = "Boot control HAL",
660		.author = "Code Aurora Forum",
661		.methods = &boot_control_module_methods,
662	},
663	.init = boot_control_init,
664	.getNumberSlots = get_number_slots,
665	.getCurrentSlot = get_current_slot,
666	.markBootSuccessful = mark_boot_successful,
667	.setActiveBootSlot = set_active_boot_slot,
668	.setSlotAsUnbootable = set_slot_as_unbootable,
669	.isSlotBootable = is_slot_bootable,
670	.getSuffix = get_suffix,
671	.isSlotMarkedSuccessful = is_slot_marked_successful,
672};
673#ifdef __cplusplus
674}
675#endif
676