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>
7*7fe881c9SXuhui Lin #include <android_image.h>
8*7fe881c9SXuhui Lin #include <fdt_support.h>
90cc16201SJason Zhu #include <blk.h>
10afc5def0SXuhui Lin #include <malloc.h>
110cc16201SJason Zhu #include <spl_ab.h>
120cc16201SJason Zhu
safe_memcmp(const void * s1,const void * s2,size_t n)130cc16201SJason Zhu int safe_memcmp(const void *s1, const void *s2, size_t n)
140cc16201SJason Zhu {
150cc16201SJason Zhu const unsigned char *us1 = s1;
160cc16201SJason Zhu const unsigned char *us2 = s2;
170cc16201SJason Zhu int result = 0;
180cc16201SJason Zhu
190cc16201SJason Zhu if (0 == n)
200cc16201SJason Zhu return 0;
210cc16201SJason Zhu
220cc16201SJason Zhu /*
230cc16201SJason Zhu * Code snippet without data-dependent branch due to Nate Lawson
240cc16201SJason Zhu * (nate@root.org) of Root Labs.
250cc16201SJason Zhu */
260cc16201SJason Zhu while (n--)
270cc16201SJason Zhu result |= *us1++ ^ *us2++;
280cc16201SJason Zhu
290cc16201SJason Zhu return result != 0;
300cc16201SJason Zhu }
310cc16201SJason Zhu
htobe32(uint32_t in)320cc16201SJason Zhu static uint32_t htobe32(uint32_t in)
330cc16201SJason Zhu {
340cc16201SJason Zhu union {
350cc16201SJason Zhu uint32_t word;
360cc16201SJason Zhu uint8_t bytes[4];
370cc16201SJason Zhu } ret;
380cc16201SJason Zhu
390cc16201SJason Zhu ret.bytes[0] = (in >> 24) & 0xff;
400cc16201SJason Zhu ret.bytes[1] = (in >> 16) & 0xff;
410cc16201SJason Zhu ret.bytes[2] = (in >> 8) & 0xff;
420cc16201SJason Zhu ret.bytes[3] = in & 0xff;
430cc16201SJason Zhu
440cc16201SJason Zhu return ret.word;
450cc16201SJason Zhu }
460cc16201SJason Zhu
be32toh(uint32_t in)470cc16201SJason Zhu static uint32_t be32toh(uint32_t in)
480cc16201SJason Zhu {
490cc16201SJason Zhu uint8_t *d = (uint8_t *)∈
500cc16201SJason Zhu uint32_t ret;
510cc16201SJason Zhu
520cc16201SJason Zhu ret = ((uint32_t)d[0]) << 24;
530cc16201SJason Zhu ret |= ((uint32_t)d[1]) << 16;
540cc16201SJason Zhu ret |= ((uint32_t)d[2]) << 8;
550cc16201SJason Zhu ret |= ((uint32_t)d[3]);
560cc16201SJason Zhu
570cc16201SJason Zhu return ret;
580cc16201SJason Zhu }
590cc16201SJason Zhu
spl_ab_data_verify_and_byteswap(const AvbABData * src,AvbABData * dest)600cc16201SJason Zhu static bool spl_ab_data_verify_and_byteswap(const AvbABData *src,
610cc16201SJason Zhu AvbABData *dest)
620cc16201SJason Zhu {
630cc16201SJason Zhu /* Ensure magic is correct. */
640cc16201SJason Zhu if (safe_memcmp(src->magic, AVB_AB_MAGIC, AVB_AB_MAGIC_LEN) != 0) {
650cc16201SJason Zhu printf("Magic is incorrect.\n");
660cc16201SJason Zhu return false;
670cc16201SJason Zhu }
680cc16201SJason Zhu
690cc16201SJason Zhu memcpy(dest, src, sizeof(AvbABData));
700cc16201SJason Zhu dest->crc32 = be32toh(dest->crc32);
710cc16201SJason Zhu
720cc16201SJason Zhu /* Ensure we don't attempt to access any fields if the major version
730cc16201SJason Zhu * is not supported.
740cc16201SJason Zhu */
750cc16201SJason Zhu if (dest->version_major > AVB_AB_MAJOR_VERSION) {
760cc16201SJason Zhu printf("No support for given major version.\n");
770cc16201SJason Zhu return false;
780cc16201SJason Zhu }
790cc16201SJason Zhu
800cc16201SJason Zhu /* Bail if CRC32 doesn't match. */
810cc16201SJason Zhu if (dest->crc32 !=
820cc16201SJason Zhu crc32(0, (const uint8_t *)dest,
830cc16201SJason Zhu sizeof(AvbABData) - sizeof(uint32_t))) {
840cc16201SJason Zhu printf("CRC32 does not match.\n");
850cc16201SJason Zhu return false;
860cc16201SJason Zhu }
870cc16201SJason Zhu
880cc16201SJason Zhu return true;
890cc16201SJason Zhu }
900cc16201SJason Zhu
spl_ab_data_update_crc_and_byteswap(const AvbABData * src,AvbABData * dest)910cc16201SJason Zhu static void spl_ab_data_update_crc_and_byteswap(const AvbABData *src,
920cc16201SJason Zhu AvbABData *dest)
930cc16201SJason Zhu {
940cc16201SJason Zhu memcpy(dest, src, sizeof(AvbABData));
950cc16201SJason Zhu dest->crc32 = htobe32(crc32(0, (const uint8_t *)dest,
960cc16201SJason Zhu sizeof(AvbABData) - sizeof(uint32_t)));
970cc16201SJason Zhu }
980cc16201SJason Zhu
spl_ab_data_init(AvbABData * data)990cc16201SJason Zhu static void spl_ab_data_init(AvbABData *data)
1000cc16201SJason Zhu {
1010cc16201SJason Zhu memset(data, '\0', sizeof(AvbABData));
1020cc16201SJason Zhu memcpy(data->magic, AVB_AB_MAGIC, AVB_AB_MAGIC_LEN);
1030cc16201SJason Zhu data->version_major = AVB_AB_MAJOR_VERSION;
1040cc16201SJason Zhu data->version_minor = AVB_AB_MINOR_VERSION;
1050cc16201SJason Zhu data->last_boot = 0;
1060cc16201SJason Zhu data->slots[0].priority = AVB_AB_MAX_PRIORITY;
1070cc16201SJason Zhu data->slots[0].tries_remaining = AVB_AB_MAX_TRIES_REMAINING;
1080cc16201SJason Zhu data->slots[0].successful_boot = 0;
1090cc16201SJason Zhu data->slots[1].priority = AVB_AB_MAX_PRIORITY - 1;
1100cc16201SJason Zhu data->slots[1].tries_remaining = AVB_AB_MAX_TRIES_REMAINING;
1110cc16201SJason Zhu data->slots[1].successful_boot = 0;
1120cc16201SJason Zhu }
1130cc16201SJason Zhu
spl_read_ab_metadata(struct blk_desc * dev_desc,AvbABData * ab_data,char * partition)1140cc16201SJason Zhu static int spl_read_ab_metadata(struct blk_desc *dev_desc, AvbABData *ab_data,
1150cc16201SJason Zhu char *partition)
1160cc16201SJason Zhu {
1170cc16201SJason Zhu disk_partition_t part_info;
1180cc16201SJason Zhu char temp[512];
1190cc16201SJason Zhu int ret;
1200cc16201SJason Zhu
1210cc16201SJason Zhu if (!dev_desc || !partition || !ab_data)
1220cc16201SJason Zhu return -EFAULT;
1230cc16201SJason Zhu
1240cc16201SJason Zhu if (part_get_info_by_name(dev_desc, partition, &part_info) < 0)
1250cc16201SJason Zhu return -ENOENT;
1260cc16201SJason Zhu
1270cc16201SJason Zhu ret = blk_dread(dev_desc, part_info.start + AB_METADATA_OFFSET, 1,
1280cc16201SJason Zhu temp);
1290cc16201SJason Zhu if (ret != 1)
1300cc16201SJason Zhu return -ENODEV;
1310cc16201SJason Zhu
1320cc16201SJason Zhu if (sizeof(AvbABData) > 512)
1330cc16201SJason Zhu return -ENOMEM;
1340cc16201SJason Zhu
1350cc16201SJason Zhu memcpy(ab_data, temp, sizeof(AvbABData));
1360cc16201SJason Zhu
1370cc16201SJason Zhu return 0;
1380cc16201SJason Zhu }
1390cc16201SJason Zhu
spl_write_ab_metadata(struct blk_desc * dev_desc,AvbABData * ab_data,char * partition)1400cc16201SJason Zhu static int spl_write_ab_metadata(struct blk_desc *dev_desc, AvbABData *ab_data,
1410cc16201SJason Zhu char *partition)
1420cc16201SJason Zhu {
1430cc16201SJason Zhu disk_partition_t part_info;
1440cc16201SJason Zhu char temp[512];
1450cc16201SJason Zhu int ret;
1460cc16201SJason Zhu
1470cc16201SJason Zhu if (!dev_desc || !partition || !ab_data)
1480cc16201SJason Zhu return -EFAULT;
1490cc16201SJason Zhu
1500cc16201SJason Zhu if (sizeof(AvbABData) > 512)
1510cc16201SJason Zhu return -ENOMEM;
1520cc16201SJason Zhu
1530cc16201SJason Zhu memcpy(temp, ab_data, sizeof(AvbABData));
1540cc16201SJason Zhu if (part_get_info_by_name(dev_desc, partition, &part_info) < 0)
1550cc16201SJason Zhu return -ENOENT;
1560cc16201SJason Zhu
1570cc16201SJason Zhu ret = blk_dwrite(dev_desc, part_info.start + AB_METADATA_OFFSET, 1,
1580cc16201SJason Zhu temp);
1590cc16201SJason Zhu if (ret != 1)
1600cc16201SJason Zhu return -ENODEV;
1610cc16201SJason Zhu
1620cc16201SJason Zhu return 0;
1630cc16201SJason Zhu }
1640cc16201SJason Zhu
spl_ab_data_write(struct blk_desc * dev_desc,AvbABData * ab_data,char * partition)1650cc16201SJason Zhu static int spl_ab_data_write(struct blk_desc *dev_desc, AvbABData *ab_data,
1660cc16201SJason Zhu char *partition)
1670cc16201SJason Zhu {
1680cc16201SJason Zhu AvbABData serialized;
1690cc16201SJason Zhu
1700cc16201SJason Zhu spl_ab_data_update_crc_and_byteswap(ab_data, &serialized);
1710cc16201SJason Zhu
1720cc16201SJason Zhu return spl_write_ab_metadata(dev_desc, &serialized, partition);
1730cc16201SJason Zhu }
1740cc16201SJason Zhu
spl_ab_data_read(struct blk_desc * dev_desc,AvbABData * ab_data,char * partition)1750cc16201SJason Zhu static int spl_ab_data_read(struct blk_desc *dev_desc, AvbABData *ab_data,
1760cc16201SJason Zhu char *partition)
1770cc16201SJason Zhu {
1780cc16201SJason Zhu int ret;
1790cc16201SJason Zhu AvbABData serialized;
1800cc16201SJason Zhu
1810cc16201SJason Zhu ret = spl_read_ab_metadata(dev_desc, &serialized, partition);
1820cc16201SJason Zhu if (ret)
1830cc16201SJason Zhu return ret;
1840cc16201SJason Zhu
1850cc16201SJason Zhu if (!spl_ab_data_verify_and_byteswap(&serialized, ab_data)) {
1860cc16201SJason Zhu printf("Error validating A/B metadata from disk. "
1870cc16201SJason Zhu "Resetting and writing new A/B metadata to disk.\n");
1880cc16201SJason Zhu spl_ab_data_init(ab_data);
1890cc16201SJason Zhu spl_ab_data_write(dev_desc, ab_data, partition);
1900cc16201SJason Zhu }
1910cc16201SJason Zhu
1920cc16201SJason Zhu return 0;
1930cc16201SJason Zhu }
1940cc16201SJason Zhu
spl_slot_is_bootable(AvbABSlotData * slot)1950cc16201SJason Zhu static bool spl_slot_is_bootable(AvbABSlotData *slot)
1960cc16201SJason Zhu {
1970cc16201SJason Zhu return slot->priority > 0 &&
1980cc16201SJason Zhu (slot->successful_boot || (slot->tries_remaining > 0));
1990cc16201SJason Zhu }
2000cc16201SJason Zhu
spl_get_lastboot(AvbABData * ab_data)2010cc16201SJason Zhu static int spl_get_lastboot(AvbABData *ab_data)
2020cc16201SJason Zhu {
2030cc16201SJason Zhu return ab_data->last_boot;
2040cc16201SJason Zhu }
2050cc16201SJason Zhu
spl_get_current_slot(struct blk_desc * dev_desc,char * partition,char * slot)2060cc16201SJason Zhu int spl_get_current_slot(struct blk_desc *dev_desc, char *partition, char *slot)
2070cc16201SJason Zhu {
208c6d7f8e4SJason Zhu static int last_slot_index = -1;
2090cc16201SJason Zhu size_t slot_index_to_boot;
2100cc16201SJason Zhu AvbABData ab_data;
2110cc16201SJason Zhu int ret;
2120cc16201SJason Zhu
213362b6cb3SXuhui Lin /*
214362b6cb3SXuhui Lin * 1. Call spl_ab_decrease_tries() before load kernel to be compatible with
215362b6cb3SXuhui Lin * the case when storage is eMMC, because preload image will occupy eMMC.
216362b6cb3SXuhui Lin *
217362b6cb3SXuhui Lin * 2. (For solving Boundary problem) Need to record slot_suffix before ab_decrease,
218362b6cb3SXuhui Lin * otherwise when boot kernel, it will use the result after decrease.
219362b6cb3SXuhui Lin *
220362b6cb3SXuhui Lin * 3. For example, current slot_suffix is _a, tries-remaining is 1. Without
221362b6cb3SXuhui Lin * recording slot_suffix before decrease, after decrease, slot_suffix is _b,
222362b6cb3SXuhui Lin * tries-remaining is 7. SPL will parse boot_b instead of boot_a that expected.
223362b6cb3SXuhui Lin */
224362b6cb3SXuhui Lin #ifdef CONFIG_SPL_KERNEL_BOOT
225362b6cb3SXuhui Lin if (last_slot_index == 0) {
226362b6cb3SXuhui Lin memcpy(slot, "_a", 2);
227362b6cb3SXuhui Lin return 0;
228362b6cb3SXuhui Lin } else if (last_slot_index == 1) {
229362b6cb3SXuhui Lin memcpy(slot, "_b", 2);
230362b6cb3SXuhui Lin return 0;
231362b6cb3SXuhui Lin }
232362b6cb3SXuhui Lin #endif
233362b6cb3SXuhui Lin
2340cc16201SJason Zhu ret = spl_ab_data_read(dev_desc, &ab_data, partition);
2350cc16201SJason Zhu if (ret)
2360cc16201SJason Zhu return ret;
2370cc16201SJason Zhu
2380cc16201SJason Zhu if (spl_slot_is_bootable(&ab_data.slots[0]) &&
2390cc16201SJason Zhu spl_slot_is_bootable(&ab_data.slots[1])) {
2400cc16201SJason Zhu if (ab_data.slots[1].priority > ab_data.slots[0].priority)
2410cc16201SJason Zhu slot_index_to_boot = 1;
2420cc16201SJason Zhu else
2430cc16201SJason Zhu slot_index_to_boot = 0;
2440cc16201SJason Zhu } else if (spl_slot_is_bootable(&ab_data.slots[0])) {
2450cc16201SJason Zhu slot_index_to_boot = 0;
2460cc16201SJason Zhu } else if (spl_slot_is_bootable(&ab_data.slots[1])) {
2470cc16201SJason Zhu slot_index_to_boot = 1;
2480cc16201SJason Zhu } else {
2490cc16201SJason Zhu printf("No bootable slots found, use lastboot.\n");
2500cc16201SJason Zhu if (spl_get_lastboot(&ab_data) == 0) {
2510cc16201SJason Zhu memcpy(slot, "_a", 2);
2520cc16201SJason Zhu goto out;
2530cc16201SJason Zhu } else if (spl_get_lastboot(&ab_data) == 1) {
2540cc16201SJason Zhu memcpy(slot, "_b", 2);
2550cc16201SJason Zhu goto out;
2560cc16201SJason Zhu } else {
2570cc16201SJason Zhu return -ENODEV;
2580cc16201SJason Zhu }
2590cc16201SJason Zhu }
2600cc16201SJason Zhu
2610cc16201SJason Zhu if (slot_index_to_boot == 0)
2620cc16201SJason Zhu memcpy(slot, "_a", 2);
2630cc16201SJason Zhu else if (slot_index_to_boot == 1)
2640cc16201SJason Zhu memcpy(slot, "_b", 2);
2650cc16201SJason Zhu
266c6d7f8e4SJason Zhu if (last_slot_index != slot_index_to_boot) {
267c6d7f8e4SJason Zhu last_slot_index = slot_index_to_boot;
268c6d7f8e4SJason Zhu printf("SPL: A/B-slot: %s, successful: %d, tries-remain: %d\n",
269c6d7f8e4SJason Zhu slot,
270c6d7f8e4SJason Zhu ab_data.slots[slot_index_to_boot].successful_boot,
271c6d7f8e4SJason Zhu ab_data.slots[slot_index_to_boot].tries_remaining);
272c6d7f8e4SJason Zhu }
273c6d7f8e4SJason Zhu
2740cc16201SJason Zhu out:
2750cc16201SJason Zhu return 0;
2760cc16201SJason Zhu }
2770cc16201SJason Zhu
spl_ab_append_part_slot(struct blk_desc * dev_desc,const char * part_name,char * new_name)2781e33e3cbSJason Zhu int spl_ab_append_part_slot(struct blk_desc *dev_desc,
2791e33e3cbSJason Zhu const char *part_name,
2801e33e3cbSJason Zhu char *new_name)
2810cc16201SJason Zhu {
2821e33e3cbSJason Zhu char slot_suffix[3] = {0};
2830cc16201SJason Zhu
2841e33e3cbSJason Zhu if (!strcmp(part_name, "misc")) {
2851e33e3cbSJason Zhu strcat(new_name, part_name);
2861e33e3cbSJason Zhu return 0;
2871e33e3cbSJason Zhu }
2880cc16201SJason Zhu
2891e33e3cbSJason Zhu if (spl_get_current_slot(dev_desc, "misc", slot_suffix)) {
2903eac03e2SJoseph Chen printf("No misc partition\n");
2913eac03e2SJoseph Chen strcat(new_name, part_name);
2923eac03e2SJoseph Chen return 0;
2931e33e3cbSJason Zhu }
2940cc16201SJason Zhu
2951e33e3cbSJason Zhu strcpy(new_name, part_name);
2961e33e3cbSJason Zhu strcat(new_name, slot_suffix);
2970cc16201SJason Zhu
2980cc16201SJason Zhu return 0;
2990cc16201SJason Zhu }
300093f4d99SJason Zhu
spl_save_metadata_if_changed(struct blk_desc * dev_desc,AvbABData * ab_data,AvbABData * ab_data_orig)301093f4d99SJason Zhu static int spl_save_metadata_if_changed(struct blk_desc *dev_desc,
302093f4d99SJason Zhu AvbABData *ab_data,
303093f4d99SJason Zhu AvbABData *ab_data_orig)
304093f4d99SJason Zhu {
305093f4d99SJason Zhu if (safe_memcmp(ab_data, ab_data_orig, sizeof(AvbABData)))
306093f4d99SJason Zhu return spl_ab_data_write(dev_desc, ab_data, "misc");
307093f4d99SJason Zhu
308093f4d99SJason Zhu return 0;
309093f4d99SJason Zhu }
310093f4d99SJason Zhu
spl_slot_set_unbootable(AvbABSlotData * slot)311f3803074SXuhui Lin static void spl_slot_set_unbootable(AvbABSlotData* slot)
312f3803074SXuhui Lin {
313f3803074SXuhui Lin slot->priority = 0;
314f3803074SXuhui Lin slot->tries_remaining = 0;
315f3803074SXuhui Lin slot->successful_boot = 0;
316f3803074SXuhui Lin }
317f3803074SXuhui Lin
318f3803074SXuhui Lin /* Ensure all unbootable and/or illegal states are marked as the
319f3803074SXuhui Lin * canonical 'unbootable' state, e.g. priority=0, tries_remaining=0,
320f3803074SXuhui Lin * and successful_boot=0.
321f3803074SXuhui Lin */
spl_slot_normalize(AvbABSlotData * slot)322f3803074SXuhui Lin static void spl_slot_normalize(AvbABSlotData* slot)
323f3803074SXuhui Lin {
324f3803074SXuhui Lin if (slot->priority > 0) {
325f3803074SXuhui Lin if (slot->tries_remaining == 0 && !slot->successful_boot) {
326f3803074SXuhui Lin /* We've exhausted all tries -> unbootable. */
327f3803074SXuhui Lin spl_slot_set_unbootable(slot);
328f3803074SXuhui Lin }
329f3803074SXuhui Lin if (slot->tries_remaining > 0 && slot->successful_boot) {
330f3803074SXuhui Lin /* Illegal state - avb_ab_mark_slot_successful() and so on
331f3803074SXuhui Lin * will clear tries_remaining when setting successful_boot.
332f3803074SXuhui Lin */
333f3803074SXuhui Lin spl_slot_set_unbootable(slot);
334f3803074SXuhui Lin }
335f3803074SXuhui Lin } else {
336f3803074SXuhui Lin spl_slot_set_unbootable(slot);
337f3803074SXuhui Lin }
338f3803074SXuhui Lin }
339f3803074SXuhui Lin
340093f4d99SJason Zhu /* If verify fail in a/b system, then decrease 1. */
spl_ab_decrease_tries(struct blk_desc * dev_desc)341093f4d99SJason Zhu int spl_ab_decrease_tries(struct blk_desc *dev_desc)
342093f4d99SJason Zhu {
343093f4d99SJason Zhu AvbABData ab_data, ab_data_orig;
344093f4d99SJason Zhu size_t slot_index = 0;
345093f4d99SJason Zhu char slot_suffix[3] = {0};
346093f4d99SJason Zhu int ret = -1;
347093f4d99SJason Zhu
348093f4d99SJason Zhu ret = spl_get_current_slot(dev_desc, "misc", slot_suffix);
349093f4d99SJason Zhu if (ret)
350093f4d99SJason Zhu goto out;
351093f4d99SJason Zhu
352093f4d99SJason Zhu if (!strncmp(slot_suffix, "_a", 2))
353093f4d99SJason Zhu slot_index = 0;
354093f4d99SJason Zhu else if (!strncmp(slot_suffix, "_b", 2))
355093f4d99SJason Zhu slot_index = 1;
356093f4d99SJason Zhu else
357093f4d99SJason Zhu slot_index = 0;
358093f4d99SJason Zhu
359093f4d99SJason Zhu ret = spl_ab_data_read(dev_desc, &ab_data, "misc");
360093f4d99SJason Zhu if (ret)
361093f4d99SJason Zhu goto out;
362093f4d99SJason Zhu
363093f4d99SJason Zhu memcpy(&ab_data_orig, &ab_data, sizeof(AvbABData));
364093f4d99SJason Zhu
365f3803074SXuhui Lin /* Ensure data is normalized, e.g. illegal states will be marked as
366f3803074SXuhui Lin * unbootable and all unbootable states are represented with
367f3803074SXuhui Lin * (priority=0, tries_remaining=0, successful_boot=0).
368f3803074SXuhui Lin */
369f3803074SXuhui Lin spl_slot_normalize(&ab_data.slots[0]);
370f3803074SXuhui Lin spl_slot_normalize(&ab_data.slots[1]);
371f3803074SXuhui Lin
372093f4d99SJason Zhu /* ... and decrement tries remaining, if applicable. */
373093f4d99SJason Zhu if (!ab_data.slots[slot_index].successful_boot &&
374093f4d99SJason Zhu ab_data.slots[slot_index].tries_remaining > 0)
375093f4d99SJason Zhu ab_data.slots[slot_index].tries_remaining -= 1;
376093f4d99SJason Zhu
377093f4d99SJason Zhu ret = spl_save_metadata_if_changed(dev_desc, &ab_data, &ab_data_orig);
378093f4d99SJason Zhu
379093f4d99SJason Zhu out:
380093f4d99SJason Zhu return ret;
381093f4d99SJason Zhu }
382f34b44cfSXuhui Lin
383f34b44cfSXuhui Lin /*
384f34b44cfSXuhui Lin * If boot A/B system fail, tries-remaining decrease 1
385f34b44cfSXuhui Lin * and do reset automatically if still bootable.
386f34b44cfSXuhui Lin */
spl_ab_decrease_reset(struct blk_desc * dev_desc)387f34b44cfSXuhui Lin int spl_ab_decrease_reset(struct blk_desc *dev_desc)
388f34b44cfSXuhui Lin {
389f34b44cfSXuhui Lin AvbABData ab_data;
390f34b44cfSXuhui Lin int ret;
391f34b44cfSXuhui Lin
392f34b44cfSXuhui Lin ret = spl_ab_data_read(dev_desc, &ab_data, "misc");
393f34b44cfSXuhui Lin if (ret)
394f34b44cfSXuhui Lin return ret;
395f34b44cfSXuhui Lin
396f34b44cfSXuhui Lin /* If current device cannot boot, return and try other devices. */
397f34b44cfSXuhui Lin if (!spl_slot_is_bootable(&ab_data.slots[0]) &&
398f34b44cfSXuhui Lin !spl_slot_is_bootable(&ab_data.slots[1])) {
399f34b44cfSXuhui Lin printf("A/B: no bootable slot\n");
400f34b44cfSXuhui Lin return -ENODEV;
401f34b44cfSXuhui Lin }
402f34b44cfSXuhui Lin
403f34b44cfSXuhui Lin /* If current device still can boot, decrease and do reset. */
404f34b44cfSXuhui Lin ret = spl_ab_decrease_tries(dev_desc);
405f34b44cfSXuhui Lin if (ret)
406f34b44cfSXuhui Lin return ret;
407f34b44cfSXuhui Lin
408f34b44cfSXuhui Lin printf("A/B: slot boot fail, do reset\n");
409f34b44cfSXuhui Lin do_reset(NULL, 0, 0, NULL);
410f34b44cfSXuhui Lin
411f34b44cfSXuhui Lin /*
412f34b44cfSXuhui Lin * Only do_reset() fail will arrive here, return a
413f34b44cfSXuhui Lin * negative number, then enter maskrom in the caller.
414f34b44cfSXuhui Lin */
415f34b44cfSXuhui Lin return -EINVAL;
416f34b44cfSXuhui Lin }
417afc5def0SXuhui Lin
spl_ab_bootargs_append_slot(void * fdt,char * slot)418afc5def0SXuhui Lin int spl_ab_bootargs_append_slot(void *fdt, char *slot)
419afc5def0SXuhui Lin {
420afc5def0SXuhui Lin char *str;
421afc5def0SXuhui Lin int len, ret = 0;
422afc5def0SXuhui Lin
423afc5def0SXuhui Lin if (!slot)
424afc5def0SXuhui Lin return 0;
425afc5def0SXuhui Lin
426afc5def0SXuhui Lin len = strlen(ANDROID_ARG_SLOT_SUFFIX) + strlen(slot) + 1;
427afc5def0SXuhui Lin str = malloc(len);
428afc5def0SXuhui Lin if (!str)
429afc5def0SXuhui Lin return -ENOMEM;
430afc5def0SXuhui Lin
431afc5def0SXuhui Lin snprintf(str, len, "%s%s", ANDROID_ARG_SLOT_SUFFIX, slot);
432afc5def0SXuhui Lin ret = fdt_bootargs_append(fdt, str);
433afc5def0SXuhui Lin if (ret)
434afc5def0SXuhui Lin printf("Append slot info to bootargs fail");
435afc5def0SXuhui Lin
436afc5def0SXuhui Lin free(str);
437afc5def0SXuhui Lin
438afc5def0SXuhui Lin return ret;
439afc5def0SXuhui Lin }
440