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 *)∈ 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