xref: /rk3399_rockchip-uboot/common/spl/spl_ab.c (revision 362b6cb356df1d5593f7c68ed58b6d70bf2c6be2)
10cc16201SJason Zhu // SPDX-License-Identifier: GPL-2.0
20cc16201SJason Zhu /*
30cc16201SJason Zhu  * Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
40cc16201SJason Zhu  */
50cc16201SJason Zhu 
60cc16201SJason Zhu #include <common.h>
70cc16201SJason Zhu #include <blk.h>
80cc16201SJason Zhu #include <spl_ab.h>
90cc16201SJason Zhu 
100cc16201SJason Zhu int safe_memcmp(const void *s1, const void *s2, size_t n)
110cc16201SJason Zhu {
120cc16201SJason Zhu 	const unsigned char *us1 = s1;
130cc16201SJason Zhu 	const unsigned char *us2 = s2;
140cc16201SJason Zhu 	int result = 0;
150cc16201SJason Zhu 
160cc16201SJason Zhu 	if (0 == n)
170cc16201SJason Zhu 		return 0;
180cc16201SJason Zhu 
190cc16201SJason Zhu 	/*
200cc16201SJason Zhu 	 * Code snippet without data-dependent branch due to Nate Lawson
210cc16201SJason Zhu 	 * (nate@root.org) of Root Labs.
220cc16201SJason Zhu 	 */
230cc16201SJason Zhu 	while (n--)
240cc16201SJason Zhu 		result |= *us1++ ^ *us2++;
250cc16201SJason Zhu 
260cc16201SJason Zhu 	return result != 0;
270cc16201SJason Zhu }
280cc16201SJason Zhu 
290cc16201SJason Zhu static uint32_t htobe32(uint32_t in)
300cc16201SJason Zhu {
310cc16201SJason Zhu 	union {
320cc16201SJason Zhu 		uint32_t word;
330cc16201SJason Zhu 		uint8_t bytes[4];
340cc16201SJason Zhu 	} ret;
350cc16201SJason Zhu 
360cc16201SJason Zhu 	ret.bytes[0] = (in >> 24) & 0xff;
370cc16201SJason Zhu 	ret.bytes[1] = (in >> 16) & 0xff;
380cc16201SJason Zhu 	ret.bytes[2] = (in >> 8) & 0xff;
390cc16201SJason Zhu 	ret.bytes[3] = in & 0xff;
400cc16201SJason Zhu 
410cc16201SJason Zhu 	return ret.word;
420cc16201SJason Zhu }
430cc16201SJason Zhu 
440cc16201SJason Zhu static uint32_t be32toh(uint32_t in)
450cc16201SJason Zhu {
460cc16201SJason Zhu 	uint8_t *d = (uint8_t *)&in;
470cc16201SJason Zhu 	uint32_t ret;
480cc16201SJason Zhu 
490cc16201SJason Zhu 	ret = ((uint32_t)d[0]) << 24;
500cc16201SJason Zhu 	ret |= ((uint32_t)d[1]) << 16;
510cc16201SJason Zhu 	ret |= ((uint32_t)d[2]) << 8;
520cc16201SJason Zhu 	ret |= ((uint32_t)d[3]);
530cc16201SJason Zhu 
540cc16201SJason Zhu 	return ret;
550cc16201SJason Zhu }
560cc16201SJason Zhu 
570cc16201SJason Zhu static bool spl_ab_data_verify_and_byteswap(const AvbABData *src,
580cc16201SJason Zhu 					    AvbABData *dest)
590cc16201SJason Zhu {
600cc16201SJason Zhu 	/* Ensure magic is correct. */
610cc16201SJason Zhu 	if (safe_memcmp(src->magic, AVB_AB_MAGIC, AVB_AB_MAGIC_LEN) != 0) {
620cc16201SJason Zhu 		printf("Magic is incorrect.\n");
630cc16201SJason Zhu 		return false;
640cc16201SJason Zhu 	}
650cc16201SJason Zhu 
660cc16201SJason Zhu 	memcpy(dest, src, sizeof(AvbABData));
670cc16201SJason Zhu 	dest->crc32 = be32toh(dest->crc32);
680cc16201SJason Zhu 
690cc16201SJason Zhu 	/* Ensure we don't attempt to access any fields if the major version
700cc16201SJason Zhu 	 * is not supported.
710cc16201SJason Zhu 	 */
720cc16201SJason Zhu 	if (dest->version_major > AVB_AB_MAJOR_VERSION) {
730cc16201SJason Zhu 		printf("No support for given major version.\n");
740cc16201SJason Zhu 		return false;
750cc16201SJason Zhu 	}
760cc16201SJason Zhu 
770cc16201SJason Zhu 	/* Bail if CRC32 doesn't match. */
780cc16201SJason Zhu 	if (dest->crc32 !=
790cc16201SJason Zhu 		crc32(0, (const uint8_t *)dest,
800cc16201SJason Zhu 		      sizeof(AvbABData) - sizeof(uint32_t))) {
810cc16201SJason Zhu 		printf("CRC32 does not match.\n");
820cc16201SJason Zhu 		return false;
830cc16201SJason Zhu 	}
840cc16201SJason Zhu 
850cc16201SJason Zhu 	return true;
860cc16201SJason Zhu }
870cc16201SJason Zhu 
880cc16201SJason Zhu static void spl_ab_data_update_crc_and_byteswap(const AvbABData *src,
890cc16201SJason Zhu 						AvbABData *dest)
900cc16201SJason Zhu {
910cc16201SJason Zhu 	memcpy(dest, src, sizeof(AvbABData));
920cc16201SJason Zhu 	dest->crc32 = htobe32(crc32(0, (const uint8_t *)dest,
930cc16201SJason Zhu 			    sizeof(AvbABData) - sizeof(uint32_t)));
940cc16201SJason Zhu }
950cc16201SJason Zhu 
960cc16201SJason Zhu static void spl_ab_data_init(AvbABData *data)
970cc16201SJason Zhu {
980cc16201SJason Zhu 	memset(data, '\0', sizeof(AvbABData));
990cc16201SJason Zhu 	memcpy(data->magic, AVB_AB_MAGIC, AVB_AB_MAGIC_LEN);
1000cc16201SJason Zhu 	data->version_major = AVB_AB_MAJOR_VERSION;
1010cc16201SJason Zhu 	data->version_minor = AVB_AB_MINOR_VERSION;
1020cc16201SJason Zhu 	data->last_boot = 0;
1030cc16201SJason Zhu 	data->slots[0].priority = AVB_AB_MAX_PRIORITY;
1040cc16201SJason Zhu 	data->slots[0].tries_remaining = AVB_AB_MAX_TRIES_REMAINING;
1050cc16201SJason Zhu 	data->slots[0].successful_boot = 0;
1060cc16201SJason Zhu 	data->slots[1].priority = AVB_AB_MAX_PRIORITY - 1;
1070cc16201SJason Zhu 	data->slots[1].tries_remaining = AVB_AB_MAX_TRIES_REMAINING;
1080cc16201SJason Zhu 	data->slots[1].successful_boot = 0;
1090cc16201SJason Zhu }
1100cc16201SJason Zhu 
1110cc16201SJason Zhu static int spl_read_ab_metadata(struct blk_desc *dev_desc, AvbABData *ab_data,
1120cc16201SJason Zhu 				char *partition)
1130cc16201SJason Zhu {
1140cc16201SJason Zhu 	disk_partition_t part_info;
1150cc16201SJason Zhu 	char temp[512];
1160cc16201SJason Zhu 	int ret;
1170cc16201SJason Zhu 
1180cc16201SJason Zhu 	if (!dev_desc || !partition || !ab_data)
1190cc16201SJason Zhu 		return -EFAULT;
1200cc16201SJason Zhu 
1210cc16201SJason Zhu 	if (part_get_info_by_name(dev_desc, partition, &part_info) < 0)
1220cc16201SJason Zhu 		return -ENOENT;
1230cc16201SJason Zhu 
1240cc16201SJason Zhu 	ret = blk_dread(dev_desc, part_info.start + AB_METADATA_OFFSET, 1,
1250cc16201SJason Zhu 			temp);
1260cc16201SJason Zhu 	if (ret != 1)
1270cc16201SJason Zhu 		return -ENODEV;
1280cc16201SJason Zhu 
1290cc16201SJason Zhu 	if (sizeof(AvbABData) > 512)
1300cc16201SJason Zhu 		return -ENOMEM;
1310cc16201SJason Zhu 
1320cc16201SJason Zhu 	memcpy(ab_data, temp, sizeof(AvbABData));
1330cc16201SJason Zhu 
1340cc16201SJason Zhu 	return 0;
1350cc16201SJason Zhu }
1360cc16201SJason Zhu 
1370cc16201SJason Zhu static int spl_write_ab_metadata(struct blk_desc *dev_desc, AvbABData *ab_data,
1380cc16201SJason Zhu 				 char *partition)
1390cc16201SJason Zhu {
1400cc16201SJason Zhu 	disk_partition_t part_info;
1410cc16201SJason Zhu 	char temp[512];
1420cc16201SJason Zhu 	int ret;
1430cc16201SJason Zhu 
1440cc16201SJason Zhu 	if (!dev_desc || !partition || !ab_data)
1450cc16201SJason Zhu 		return -EFAULT;
1460cc16201SJason Zhu 
1470cc16201SJason Zhu 	if (sizeof(AvbABData) > 512)
1480cc16201SJason Zhu 		return -ENOMEM;
1490cc16201SJason Zhu 
1500cc16201SJason Zhu 	memcpy(temp, ab_data, sizeof(AvbABData));
1510cc16201SJason Zhu 	if (part_get_info_by_name(dev_desc, partition, &part_info) < 0)
1520cc16201SJason Zhu 		return -ENOENT;
1530cc16201SJason Zhu 
1540cc16201SJason Zhu 	ret = blk_dwrite(dev_desc, part_info.start + AB_METADATA_OFFSET, 1,
1550cc16201SJason Zhu 			 temp);
1560cc16201SJason Zhu 	if (ret != 1)
1570cc16201SJason Zhu 		return -ENODEV;
1580cc16201SJason Zhu 
1590cc16201SJason Zhu 	return 0;
1600cc16201SJason Zhu }
1610cc16201SJason Zhu 
1620cc16201SJason Zhu static int spl_ab_data_write(struct blk_desc *dev_desc, AvbABData *ab_data,
1630cc16201SJason Zhu 			     char *partition)
1640cc16201SJason Zhu {
1650cc16201SJason Zhu 	AvbABData serialized;
1660cc16201SJason Zhu 
1670cc16201SJason Zhu 	spl_ab_data_update_crc_and_byteswap(ab_data, &serialized);
1680cc16201SJason Zhu 
1690cc16201SJason Zhu 	return spl_write_ab_metadata(dev_desc, &serialized, partition);
1700cc16201SJason Zhu }
1710cc16201SJason Zhu 
1720cc16201SJason Zhu static int spl_ab_data_read(struct blk_desc *dev_desc, AvbABData *ab_data,
1730cc16201SJason Zhu 			    char *partition)
1740cc16201SJason Zhu {
1750cc16201SJason Zhu 	int ret;
1760cc16201SJason Zhu 	AvbABData serialized;
1770cc16201SJason Zhu 
1780cc16201SJason Zhu 	ret = spl_read_ab_metadata(dev_desc, &serialized, partition);
1790cc16201SJason Zhu 	if (ret)
1800cc16201SJason Zhu 		return ret;
1810cc16201SJason Zhu 
1820cc16201SJason Zhu 	if (!spl_ab_data_verify_and_byteswap(&serialized, ab_data)) {
1830cc16201SJason Zhu 		printf("Error validating A/B metadata from disk. "
1840cc16201SJason Zhu 		       "Resetting and writing new A/B metadata to disk.\n");
1850cc16201SJason Zhu 		spl_ab_data_init(ab_data);
1860cc16201SJason Zhu 		spl_ab_data_write(dev_desc, ab_data, partition);
1870cc16201SJason Zhu 	}
1880cc16201SJason Zhu 
1890cc16201SJason Zhu 	return 0;
1900cc16201SJason Zhu }
1910cc16201SJason Zhu 
1920cc16201SJason Zhu static bool spl_slot_is_bootable(AvbABSlotData *slot)
1930cc16201SJason Zhu {
1940cc16201SJason Zhu 	return slot->priority > 0 &&
1950cc16201SJason Zhu 		(slot->successful_boot || (slot->tries_remaining > 0));
1960cc16201SJason Zhu }
1970cc16201SJason Zhu 
1980cc16201SJason Zhu static int spl_get_lastboot(AvbABData *ab_data)
1990cc16201SJason Zhu {
2000cc16201SJason Zhu 	return ab_data->last_boot;
2010cc16201SJason Zhu }
2020cc16201SJason Zhu 
2030cc16201SJason Zhu int spl_get_current_slot(struct blk_desc *dev_desc, char *partition, char *slot)
2040cc16201SJason Zhu {
205c6d7f8e4SJason Zhu 	static int last_slot_index = -1;
2060cc16201SJason Zhu 	size_t slot_index_to_boot;
2070cc16201SJason Zhu 	AvbABData ab_data;
2080cc16201SJason Zhu 	int ret;
2090cc16201SJason Zhu 
210*362b6cb3SXuhui Lin 	/*
211*362b6cb3SXuhui Lin 	 * 1. Call spl_ab_decrease_tries() before load kernel to be compatible with
212*362b6cb3SXuhui Lin 	 * the case when storage is eMMC, because preload image will occupy eMMC.
213*362b6cb3SXuhui Lin 	 *
214*362b6cb3SXuhui Lin 	 * 2. (For solving Boundary problem) Need to record slot_suffix before ab_decrease,
215*362b6cb3SXuhui Lin 	 * otherwise when boot kernel, it will use the result after decrease.
216*362b6cb3SXuhui Lin 	 *
217*362b6cb3SXuhui Lin 	 * 3. For example, current slot_suffix is _a, tries-remaining is 1. Without
218*362b6cb3SXuhui Lin 	 * recording slot_suffix before decrease, after decrease, slot_suffix is _b,
219*362b6cb3SXuhui Lin 	 * tries-remaining is 7. SPL will parse boot_b instead of boot_a that expected.
220*362b6cb3SXuhui Lin 	 */
221*362b6cb3SXuhui Lin #ifdef CONFIG_SPL_KERNEL_BOOT
222*362b6cb3SXuhui Lin 	if (last_slot_index == 0) {
223*362b6cb3SXuhui Lin 		memcpy(slot, "_a", 2);
224*362b6cb3SXuhui Lin 		return 0;
225*362b6cb3SXuhui Lin 	} else if (last_slot_index == 1) {
226*362b6cb3SXuhui Lin 		memcpy(slot, "_b", 2);
227*362b6cb3SXuhui Lin 		return 0;
228*362b6cb3SXuhui Lin 	}
229*362b6cb3SXuhui Lin #endif
230*362b6cb3SXuhui Lin 
2310cc16201SJason Zhu 	ret = spl_ab_data_read(dev_desc, &ab_data, partition);
2320cc16201SJason Zhu 	if (ret)
2330cc16201SJason Zhu 		return ret;
2340cc16201SJason Zhu 
2350cc16201SJason Zhu 	if (spl_slot_is_bootable(&ab_data.slots[0]) &&
2360cc16201SJason Zhu 	    spl_slot_is_bootable(&ab_data.slots[1])) {
2370cc16201SJason Zhu 		if (ab_data.slots[1].priority > ab_data.slots[0].priority)
2380cc16201SJason Zhu 			slot_index_to_boot = 1;
2390cc16201SJason Zhu 		else
2400cc16201SJason Zhu 			slot_index_to_boot = 0;
2410cc16201SJason Zhu 	} else if (spl_slot_is_bootable(&ab_data.slots[0])) {
2420cc16201SJason Zhu 		slot_index_to_boot = 0;
2430cc16201SJason Zhu 	} else if (spl_slot_is_bootable(&ab_data.slots[1])) {
2440cc16201SJason Zhu 		slot_index_to_boot = 1;
2450cc16201SJason Zhu 	} else {
2460cc16201SJason Zhu 		printf("No bootable slots found, use lastboot.\n");
2470cc16201SJason Zhu 		if (spl_get_lastboot(&ab_data) == 0) {
2480cc16201SJason Zhu 			memcpy(slot, "_a", 2);
2490cc16201SJason Zhu 			goto out;
2500cc16201SJason Zhu 		} else if (spl_get_lastboot(&ab_data) == 1) {
2510cc16201SJason Zhu 			memcpy(slot, "_b", 2);
2520cc16201SJason Zhu 			goto out;
2530cc16201SJason Zhu 		} else {
2540cc16201SJason Zhu 			return -ENODEV;
2550cc16201SJason Zhu 		}
2560cc16201SJason Zhu 	}
2570cc16201SJason Zhu 
2580cc16201SJason Zhu 	if (slot_index_to_boot == 0)
2590cc16201SJason Zhu 		memcpy(slot, "_a", 2);
2600cc16201SJason Zhu 	else if (slot_index_to_boot == 1)
2610cc16201SJason Zhu 		memcpy(slot, "_b", 2);
2620cc16201SJason Zhu 
263c6d7f8e4SJason Zhu 	if (last_slot_index != slot_index_to_boot) {
264c6d7f8e4SJason Zhu 		last_slot_index = slot_index_to_boot;
265c6d7f8e4SJason Zhu 		printf("SPL: A/B-slot: %s, successful: %d, tries-remain: %d\n",
266c6d7f8e4SJason Zhu 		       slot,
267c6d7f8e4SJason Zhu 		       ab_data.slots[slot_index_to_boot].successful_boot,
268c6d7f8e4SJason Zhu 		       ab_data.slots[slot_index_to_boot].tries_remaining);
269c6d7f8e4SJason Zhu 	}
270c6d7f8e4SJason Zhu 
2710cc16201SJason Zhu out:
2720cc16201SJason Zhu 	return 0;
2730cc16201SJason Zhu }
2740cc16201SJason Zhu 
2751e33e3cbSJason Zhu int spl_ab_append_part_slot(struct blk_desc *dev_desc,
2761e33e3cbSJason Zhu 			    const char *part_name,
2771e33e3cbSJason Zhu 			    char *new_name)
2780cc16201SJason Zhu {
2791e33e3cbSJason Zhu 	char slot_suffix[3] = {0};
2800cc16201SJason Zhu 
2811e33e3cbSJason Zhu 	if (!strcmp(part_name, "misc")) {
2821e33e3cbSJason Zhu 		strcat(new_name, part_name);
2831e33e3cbSJason Zhu 		return 0;
2841e33e3cbSJason Zhu 	}
2850cc16201SJason Zhu 
2861e33e3cbSJason Zhu 	if (spl_get_current_slot(dev_desc, "misc", slot_suffix)) {
2873eac03e2SJoseph Chen 		printf("No misc partition\n");
2883eac03e2SJoseph Chen 		strcat(new_name, part_name);
2893eac03e2SJoseph Chen 		return 0;
2901e33e3cbSJason Zhu 	}
2910cc16201SJason Zhu 
2921e33e3cbSJason Zhu 	strcpy(new_name, part_name);
2931e33e3cbSJason Zhu 	strcat(new_name, slot_suffix);
2940cc16201SJason Zhu 
2950cc16201SJason Zhu 	return 0;
2960cc16201SJason Zhu }
297093f4d99SJason Zhu 
298093f4d99SJason Zhu static int spl_save_metadata_if_changed(struct blk_desc *dev_desc,
299093f4d99SJason Zhu 					AvbABData *ab_data,
300093f4d99SJason Zhu 					AvbABData *ab_data_orig)
301093f4d99SJason Zhu {
302093f4d99SJason Zhu 	if (safe_memcmp(ab_data, ab_data_orig, sizeof(AvbABData)))
303093f4d99SJason Zhu 		return spl_ab_data_write(dev_desc, ab_data, "misc");
304093f4d99SJason Zhu 
305093f4d99SJason Zhu 	return 0;
306093f4d99SJason Zhu }
307093f4d99SJason Zhu 
308f3803074SXuhui Lin static void spl_slot_set_unbootable(AvbABSlotData* slot)
309f3803074SXuhui Lin {
310f3803074SXuhui Lin 	slot->priority = 0;
311f3803074SXuhui Lin 	slot->tries_remaining = 0;
312f3803074SXuhui Lin 	slot->successful_boot = 0;
313f3803074SXuhui Lin }
314f3803074SXuhui Lin 
315f3803074SXuhui Lin /* Ensure all unbootable and/or illegal states are marked as the
316f3803074SXuhui Lin  * canonical 'unbootable' state, e.g. priority=0, tries_remaining=0,
317f3803074SXuhui Lin  * and successful_boot=0.
318f3803074SXuhui Lin  */
319f3803074SXuhui Lin static void spl_slot_normalize(AvbABSlotData* slot)
320f3803074SXuhui Lin {
321f3803074SXuhui Lin 	if (slot->priority > 0) {
322f3803074SXuhui Lin 		if (slot->tries_remaining == 0 && !slot->successful_boot) {
323f3803074SXuhui Lin 			/* We've exhausted all tries -> unbootable. */
324f3803074SXuhui Lin 			spl_slot_set_unbootable(slot);
325f3803074SXuhui Lin 		}
326f3803074SXuhui Lin 		if (slot->tries_remaining > 0 && slot->successful_boot) {
327f3803074SXuhui Lin 			/* Illegal state - avb_ab_mark_slot_successful() and so on
328f3803074SXuhui Lin 			 * will clear tries_remaining when setting successful_boot.
329f3803074SXuhui Lin 			 */
330f3803074SXuhui Lin 			spl_slot_set_unbootable(slot);
331f3803074SXuhui Lin 		}
332f3803074SXuhui Lin 	} else {
333f3803074SXuhui Lin 		spl_slot_set_unbootable(slot);
334f3803074SXuhui Lin 	}
335f3803074SXuhui Lin }
336f3803074SXuhui Lin 
337093f4d99SJason Zhu /* If verify fail in a/b system, then decrease 1. */
338093f4d99SJason Zhu int spl_ab_decrease_tries(struct blk_desc *dev_desc)
339093f4d99SJason Zhu {
340093f4d99SJason Zhu 	AvbABData ab_data, ab_data_orig;
341093f4d99SJason Zhu 	size_t slot_index = 0;
342093f4d99SJason Zhu 	char slot_suffix[3] = {0};
343093f4d99SJason Zhu 	int ret = -1;
344093f4d99SJason Zhu 
345093f4d99SJason Zhu 	ret = spl_get_current_slot(dev_desc, "misc", slot_suffix);
346093f4d99SJason Zhu 	if (ret)
347093f4d99SJason Zhu 		goto out;
348093f4d99SJason Zhu 
349093f4d99SJason Zhu 	if (!strncmp(slot_suffix, "_a", 2))
350093f4d99SJason Zhu 		slot_index = 0;
351093f4d99SJason Zhu 	else if (!strncmp(slot_suffix, "_b", 2))
352093f4d99SJason Zhu 		slot_index = 1;
353093f4d99SJason Zhu 	else
354093f4d99SJason Zhu 		slot_index = 0;
355093f4d99SJason Zhu 
356093f4d99SJason Zhu 	ret = spl_ab_data_read(dev_desc, &ab_data, "misc");
357093f4d99SJason Zhu 	if (ret)
358093f4d99SJason Zhu 		goto out;
359093f4d99SJason Zhu 
360093f4d99SJason Zhu 	memcpy(&ab_data_orig, &ab_data, sizeof(AvbABData));
361093f4d99SJason Zhu 
362f3803074SXuhui Lin 	/* Ensure data is normalized, e.g. illegal states will be marked as
363f3803074SXuhui Lin 	 * unbootable and all unbootable states are represented with
364f3803074SXuhui Lin 	 * (priority=0, tries_remaining=0, successful_boot=0).
365f3803074SXuhui Lin 	 */
366f3803074SXuhui Lin 	spl_slot_normalize(&ab_data.slots[0]);
367f3803074SXuhui Lin 	spl_slot_normalize(&ab_data.slots[1]);
368f3803074SXuhui Lin 
369093f4d99SJason Zhu 	/* ... and decrement tries remaining, if applicable. */
370093f4d99SJason Zhu 	if (!ab_data.slots[slot_index].successful_boot &&
371093f4d99SJason Zhu 	    ab_data.slots[slot_index].tries_remaining > 0)
372093f4d99SJason Zhu 		ab_data.slots[slot_index].tries_remaining -= 1;
373093f4d99SJason Zhu 
374093f4d99SJason Zhu 	ret = spl_save_metadata_if_changed(dev_desc, &ab_data, &ab_data_orig);
375093f4d99SJason Zhu 
376093f4d99SJason Zhu out:
377093f4d99SJason Zhu 	return ret;
378093f4d99SJason Zhu }
379f34b44cfSXuhui Lin 
380f34b44cfSXuhui Lin /*
381f34b44cfSXuhui Lin  * If boot A/B system fail, tries-remaining decrease 1
382f34b44cfSXuhui Lin  * and do reset automatically if still bootable.
383f34b44cfSXuhui Lin  */
384f34b44cfSXuhui Lin int spl_ab_decrease_reset(struct blk_desc *dev_desc)
385f34b44cfSXuhui Lin {
386f34b44cfSXuhui Lin 	AvbABData ab_data;
387f34b44cfSXuhui Lin 	int ret;
388f34b44cfSXuhui Lin 
389f34b44cfSXuhui Lin 	ret = spl_ab_data_read(dev_desc, &ab_data, "misc");
390f34b44cfSXuhui Lin 	if (ret)
391f34b44cfSXuhui Lin 		return ret;
392f34b44cfSXuhui Lin 
393f34b44cfSXuhui Lin 	/* If current device cannot boot, return and try other devices. */
394f34b44cfSXuhui Lin 	if (!spl_slot_is_bootable(&ab_data.slots[0]) &&
395f34b44cfSXuhui Lin 	    !spl_slot_is_bootable(&ab_data.slots[1])) {
396f34b44cfSXuhui Lin 		printf("A/B: no bootable slot\n");
397f34b44cfSXuhui Lin 		return -ENODEV;
398f34b44cfSXuhui Lin 	}
399f34b44cfSXuhui Lin 
400f34b44cfSXuhui Lin 	/* If current device still can boot, decrease and do reset. */
401f34b44cfSXuhui Lin 	ret = spl_ab_decrease_tries(dev_desc);
402f34b44cfSXuhui Lin 	if (ret)
403f34b44cfSXuhui Lin 		return ret;
404f34b44cfSXuhui Lin 
405f34b44cfSXuhui Lin 	printf("A/B: slot boot fail, do reset\n");
406f34b44cfSXuhui Lin 	do_reset(NULL, 0, 0, NULL);
407f34b44cfSXuhui Lin 
408f34b44cfSXuhui Lin 	/*
409f34b44cfSXuhui Lin 	 * Only do_reset() fail will arrive here, return a
410f34b44cfSXuhui Lin 	 * negative number, then enter maskrom in the caller.
411f34b44cfSXuhui Lin 	 */
412f34b44cfSXuhui Lin 	return -EINVAL;
413f34b44cfSXuhui Lin }
414