| /* |
| * NDA AND NEED-TO-KNOW REQUIRED |
| * |
| * Copyright © 2013-2018 Synaptics Incorporated. All rights reserved. |
| * |
| * This file contains information that is proprietary to Synaptics |
| * Incorporated ("Synaptics"). The holder of this file shall treat all |
| * information contained herein as confidential, shall use the |
| * information only for its intended purpose, and shall not duplicate, |
| * disclose, or disseminate any of this information in any manner |
| * unless Synaptics has otherwise provided express, written |
| * permission. |
| * |
| * Use of the materials may require a license of intellectual property |
| * from a third party or from Synaptics. This file conveys no express |
| * or implied licenses to any intellectual property rights belonging |
| * to Synaptics. |
| * |
| * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND |
| * SYNAPTICS EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, |
| * INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE, AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY |
| * INTELLECTUAL PROPERTY RIGHTS. IN NO EVENT SHALL SYNAPTICS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, PUNITIVE, OR |
| * CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION WITH THE USE |
| * OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED AND |
| * BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF |
| * COMPETENT JURISDICTION DOES NOT PERMIT THE DISCLAIMER OF DIRECT |
| * DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' TOTAL CUMULATIVE LIABILITY |
| * TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. DOLLARS. |
| */ |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| //! \file boot_mode.c |
| //! \brief checking current boot mode for miniloader to select fastboot/normal/recovery. |
| /////////////////////////////////////////////////////////////////////////////// |
| #include "com_type.h" |
| #include "io.h" |
| #include "string.h" |
| |
| #include "debug.h" |
| #include "flash_adaptor.h" |
| |
| #if defined(EMMC_BOOT) || defined(ENABLE_EMMC) |
| #include "load_gpt.h" |
| #elif defined(NAND_BOOT) || defined(ENABLE_NAND) |
| #include "load_vt.h" |
| #endif |
| |
| #include "boot_mode.h" |
| |
| /*********************************************** |
| * A/B Mode check * |
| * |
| ***********************************************/ |
| #include <stdint.h> |
| |
| #define AVB_ATTR_PACKED __attribute__((packed)) |
| |
| /* Magic for the A/B struct when serialized. */ |
| #define AVB_AB_MAGIC 0x30424100 //"\0AB0" |
| #define AVB_AB_MAGIC_LEN 4 |
| |
| /* Versioning for the on-disk A/B metadata - keep in sync with avbtool. */ |
| #define AVB_AB_MAJOR_VERSION 1 |
| #define AVB_AB_MINOR_VERSION 0 |
| |
| /* Size of AvbABData struct. */ |
| #define AVB_AB_DATA_SIZE 32 |
| |
| /* Maximum values for slot data */ |
| #define AVB_AB_MAX_PRIORITY 15 |
| #define AVB_AB_MAX_TRIES_REMAINING 7 |
| |
| |
| /* Struct used for recording per-slot metadata. |
| * |
| * When serialized, data is stored in network byte-order. |
| */ |
| typedef struct AvbABSlotData { |
| /* Slot priority. Valid values range from 0 to AVB_AB_MAX_PRIORITY, |
| * both inclusive with 1 being the lowest and AVB_AB_MAX_PRIORITY |
| * being the highest. The special value 0 is used to indicate the |
| * slot is unbootable. |
| */ |
| uint8_t priority; |
| |
| /* Number of times left attempting to boot this slot ranging from 0 |
| * to AVB_AB_MAX_TRIES_REMAINING. |
| */ |
| uint8_t tries_remaining; |
| |
| /* Non-zero if this slot has booted successfully, 0 otherwise. */ |
| uint8_t successful_boot; |
| |
| /* Reserved for future use. */ |
| uint8_t reserved[1]; |
| } AVB_ATTR_PACKED AvbABSlotData; |
| |
| /* Struct used for recording A/B metadata. |
| * |
| * When serialized, data is stored in network byte-order. |
| */ |
| typedef struct AvbABData { |
| /* Magic number used for identification - see AVB_AB_MAGIC. */ |
| uint32_t magic; |
| |
| /* Version of on-disk struct - see AVB_AB_{MAJOR,MINOR}_VERSION. */ |
| uint8_t version_major; |
| uint8_t version_minor; |
| |
| /* Padding to ensure |slots| field start eight bytes in. */ |
| uint8_t reserved1[2]; |
| |
| /* Per-slot metadata. */ |
| AvbABSlotData slots[2]; |
| |
| /* Reserved for future use. */ |
| uint8_t reserved2[12]; |
| |
| /* CRC32 of all 28 bytes preceding this field. */ |
| uint32_t crc32; |
| } AVB_ATTR_PACKED AvbABData; |
| |
| static AvbABData bctrl_miscdata; |
| static int BootAB_sel = BOOTSEL_A; |
| |
| static int read_bootctrl_miscdata(AvbABData *pbootctrl) |
| { |
| unsigned int read_size = 0; |
| long long ret = 0; |
| |
| if(NULL == pbootctrl) { |
| ERR("ERROR: unvalid bootctrl AvbABData for write !\n"); |
| return -1; |
| } |
| |
| #if defined(NAND_BOOT) || defined(ENABLE_NAND) |
| //get misc partition informaiton from NAND by ver_table |
| /* Currently we do not have plan to support A/B with NAND */ |
| |
| #elif defined(EMMC_BOOT) || defined(ENABLE_EMMC) |
| unsigned char buff[512 + 64] = {0}; |
| unsigned char * miscdata_buff = (unsigned char *)((((uintptr_t)buff + 63) >> 5) << 5); |
| |
| //get misc partition informaiton from EMMC by GPT |
| struct gpt_ent gpt_misc; |
| int num = find_partition(MISC_NAME); |
| if(num >= 0) { |
| fetch_partition_info(num, &gpt_misc); |
| } else { |
| // if no misc, return normal |
| ERR("find misc partition error!\n"); |
| return -1; |
| } |
| |
| //read block size of misc partition to get AvbABData |
| read_size = (sizeof(AvbABData) > 512) ? sizeof(AvbABData) : 512; |
| if(read_size <= 512) { |
| ret = read_image(&gpt_misc, 512, miscdata_buff); |
| if(ret){ |
| ERR("read misc area data failed.\n"); |
| return -1; |
| } |
| |
| memcpy(pbootctrl, miscdata_buff, sizeof(AvbABData)); |
| } |
| else { |
| unsigned int spare_size = sizeof(AvbABData) % 512; |
| unsigned int aligned_size = (((sizeof(AvbABData) + 511) >> 9) << 9); |
| |
| for(read_size = 0; read_size <= aligned_size; read_size += 512) { |
| memset(miscdata_buff, 0x0, 512); |
| ret = read_image_from_offset(&gpt_misc, read_size, 512, miscdata_buff); |
| if(ret){ |
| ERR("read misc area data failed with offset 0x%x.\n", read_size); |
| return -1; |
| } |
| |
| if(spare_size && (read_size > sizeof(AvbABData))) |
| memcpy((void *)((uintptr_t)pbootctrl + read_size), miscdata_buff, spare_size); |
| else |
| memcpy((void *)((uintptr_t)pbootctrl + read_size), miscdata_buff, 512); |
| |
| } |
| |
| if(read_size < sizeof(AvbABData)) { |
| ERR("Not enough AvbABData read from misc area ! \n"); |
| return 1; |
| } |
| } |
| |
| return 0; |
| #endif |
| |
| return -1; |
| } |
| |
| static int write_bootctrl_miscdata(AvbABData *pbootctrl) |
| { |
| unsigned int write_size = 0; |
| long long ret = 0; |
| |
| if(NULL == pbootctrl) { |
| ERR("ERROR: unvalid bootctrl AvbABData for write !\n"); |
| return -1; |
| } |
| |
| #if defined(NAND_BOOT) || defined(ENABLE_NAND) |
| //get misc partition informaiton from NAND by ver_table |
| /* Currently we do not have plan to support A/B with NAND */ |
| |
| #elif defined(EMMC_BOOT) || defined(ENABLE_EMMC) |
| unsigned char buff[512 + 64] = {0}; |
| unsigned char * miscdata_buff = (unsigned char *)((((uintptr_t)buff + 63) >> 5) << 5); |
| |
| //get misc partition informaiton from EMMC by GPT |
| struct gpt_ent gpt_misc; |
| int num = find_partition(MISC_NAME); |
| if(num >= 0) { |
| fetch_partition_info(num, &gpt_misc); |
| } else { |
| // if no misc, return normal |
| ERR("find misc partition error!\n"); |
| return -1; |
| } |
| |
| //write block size of misc partition to set AvbABData |
| write_size = (sizeof(AvbABData) > 512) ? sizeof(AvbABData) : 512; |
| if(write_size <= 512) { |
| memset(miscdata_buff, 0x0, 512); |
| memcpy(miscdata_buff, pbootctrl, sizeof(AvbABData)); |
| ret = write_flash(gpt_misc.ent_lba_start * 512, 512, miscdata_buff); |
| if(ret){ |
| ERR("set misc area data failed.\n"); |
| return -1; |
| } |
| } |
| else { |
| unsigned int spare_size = sizeof(AvbABData) % 512; |
| unsigned int aligned_size = (((sizeof(AvbABData) + 511) >> 9) << 9); |
| |
| for(write_size = 0; write_size <= aligned_size; write_size += 512) { |
| memset(miscdata_buff, 0x0, 512); |
| if(spare_size && (write_size >= (sizeof(AvbABData) - spare_size))) |
| memcpy(miscdata_buff, (const void *)((uintptr_t)pbootctrl + write_size), spare_size); |
| else |
| memcpy(miscdata_buff, (const void *)((uintptr_t)pbootctrl + write_size), 512); |
| |
| ret = write_flash(gpt_misc.ent_lba_start * 512 + write_size, 512, miscdata_buff); |
| if(ret){ |
| ERR("write misc area data failed with offset 0x%x.\n", write_size); |
| return -1; |
| } |
| } |
| |
| if(write_size < sizeof(AvbABData)) { |
| ERR("Not enough AvbABData written to misc area ! \n"); |
| return 1; |
| } |
| } |
| |
| return 0; |
| #endif |
| |
| return -1; |
| } |
| |
| static int reflect(int data, int len) |
| { |
| int ref = 0; |
| int i; |
| |
| for (i = 0; i < len; i++) { |
| if (data & 0x1) { |
| ref |= (1 << ((len - 1) - i)); |
| } |
| data = (data >> 1); |
| } |
| |
| return ref; |
| } |
| |
| /* user need to make sure the buf is valid */ |
| static uint32_t calculate_crc32(void* buf, uint32_t len) |
| { |
| uint32_t i, j; |
| uint32_t byte_length = 8; /*length of unit (i.e. byte) */ |
| int msb = 0; |
| int polynomial = 0x04C11DB7; /* IEEE 32bit polynomial */ |
| unsigned int regs = 0xFFFFFFFF; /* init to all ones */ |
| int regs_mask = 0xFFFFFFFF; /* ensure only 32 bit answer */ |
| int regs_msb = 0; |
| unsigned int reflected_regs; |
| |
| for (i = 0; i < len; i++) { |
| int data_byte = *((uint8_t*)buf + i); |
| data_byte = reflect(data_byte, 8); |
| |
| for (j = 0; j < byte_length; j++) { |
| msb = data_byte >> (byte_length - 1); /* get MSB */ |
| msb &= 1; /* ensure just 1 bit */ |
| regs_msb = (regs >> 31) & 1; /* MSB of regs */ |
| regs = regs << 1; /* shift regs for CRC-CCITT */ |
| if (regs_msb ^ msb) { /* MSB is a 1 */ |
| regs = regs ^ polynomial; /* XOR with generator poly */ |
| } |
| regs = regs & regs_mask; /* Mask off excess upper bits */ |
| data_byte <<= 1; /* get to next bit */ |
| } |
| } |
| |
| regs = regs & regs_mask; |
| reflected_regs = reflect(regs, 32) ^ 0xFFFFFFFF; |
| |
| return reflected_regs; |
| } |
| |
| /* users need to make sure AvbABData *pbootctrl is valid pointer */ |
| static int is_valid_bootctrl(AvbABData *pbootctrl) |
| { |
| uint32_t crc32 = calculate_crc32(pbootctrl, sizeof(struct AvbABData) - sizeof(uint32_t)); |
| |
| return ((AVB_AB_MAGIC == pbootctrl->magic) && |
| (AVB_AB_MAJOR_VERSION == pbootctrl->version_major) && |
| (AVB_AB_MINOR_VERSION == pbootctrl->version_minor) && |
| (crc32 == pbootctrl->crc32)); |
| } |
| |
| static int slot_is_bootable(AvbABSlotData *pABSlot) |
| { |
| return (pABSlot->successful_boot) || (pABSlot->priority && pABSlot->tries_remaining); |
| } |
| |
| int isSlotBootable(AvbABData *pbootctrl, unsigned int slot) |
| { |
| if(slot >= MAX_SLOTS) { |
| ERR("invalid default slot number for bootctrl !\n"); |
| return 0; |
| } |
| |
| if(NULL == pbootctrl) { |
| ERR("ERROR: unvalid bootctrl AvbABData !\n"); |
| return 0; |
| } |
| |
| return ((pbootctrl->slots[slot].successful_boot) || |
| (pbootctrl->slots[slot].priority && pbootctrl->slots[slot].tries_remaining)); |
| } |
| |
| int isSlotMarkedSuccessful(AvbABData *pbootctrl, unsigned int slot) |
| { |
| if(slot >= MAX_SLOTS) { |
| ERR("invalid default slot number for bootctrl !\n"); |
| return -1; |
| } |
| |
| if(NULL == pbootctrl) { |
| ERR("ERROR: unvalid bootctrl AvbABData !\n"); |
| return -1; |
| } |
| |
| return (pbootctrl->slots[slot].successful_boot == 1); |
| } |
| |
| int setActiveBootSlot(AvbABData *pbootctrl, unsigned int default_slot) |
| { |
| int ret = 0; |
| |
| if(default_slot >= MAX_SLOTS) { |
| ERR("invalid default slot number for bootctrl !\n"); |
| return -1; |
| } |
| |
| if(NULL == pbootctrl) { |
| ERR("ERROR: unvalid bootctrl AvbABData !\n"); |
| return -1; |
| } |
| |
| pbootctrl->slots[default_slot].priority = AVB_AB_MAX_PRIORITY; |
| pbootctrl->slots[default_slot].tries_remaining = AVB_AB_MAX_TRIES_REMAINING; |
| |
| if(pbootctrl->slots[1 - default_slot].priority) |
| pbootctrl->slots[1 - default_slot].priority = AVB_AB_MAX_PRIORITY - 1; |
| |
| pbootctrl->crc32 = calculate_crc32(pbootctrl, sizeof(struct AvbABData) - sizeof(uint32_t)); |
| |
| ret = write_bootctrl_miscdata(pbootctrl); |
| if(ret) { |
| ERR("%s: Error writing misc data !\n", __func__); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| int setSlotAsUnbootable(AvbABData *pbootctrl, unsigned int default_slot) |
| { |
| int ret = 0; |
| |
| if(default_slot >= MAX_SLOTS) { |
| ERR("invalid default slot number for bootctrl !\n"); |
| return -1; |
| } |
| |
| if(NULL == pbootctrl) { |
| ERR("ERROR: unvalid bootctrl AvbABData !\n"); |
| return -1; |
| } |
| |
| // Set priority, tries_remaining, and successful_boot to 0 |
| pbootctrl->slots[default_slot].priority = 0; |
| pbootctrl->slots[default_slot].tries_remaining = 0; |
| pbootctrl->slots[default_slot].successful_boot = 0; |
| |
| pbootctrl->crc32 = calculate_crc32(pbootctrl, sizeof(struct AvbABData) - sizeof(uint32_t)); |
| |
| ret = write_bootctrl_miscdata(pbootctrl); |
| if(ret) { |
| ERR("%s: Error writing misc data !\n", __func__); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static int check_misc_abcommand(void) |
| { |
| unsigned int bootab_sel = BOOTSEL_DEFAULT; |
| AvbABData *pbootctrl = &bctrl_miscdata; |
| int ret = 0; |
| |
| memset((void *)pbootctrl, 0, sizeof(AvbABData)); |
| ret = read_bootctrl_miscdata(pbootctrl); |
| if (ret) { |
| ERR("Error reading misc data !\n"); |
| return ret; |
| } |
| |
| if (slot_is_bootable(&pbootctrl->slots[0]) && |
| slot_is_bootable(&pbootctrl->slots[1])) { |
| if (pbootctrl->slots[0].priority >= pbootctrl->slots[1].priority) { |
| bootab_sel = BOOTSEL_A; |
| } else { |
| bootab_sel = BOOTSEL_B; |
| } |
| } else if (slot_is_bootable(&pbootctrl->slots[0])) { |
| bootab_sel = BOOTSEL_A; |
| } else if (slot_is_bootable(&pbootctrl->slots[1])) { |
| bootab_sel = BOOTSEL_B; |
| } else { |
| /* No bootable slots! */ |
| ERR("No bootable slots found !!!\n"); |
| bootab_sel = BOOTSEL_INVALID; |
| goto out; |
| } |
| |
| INFO("Slot index=%d got !\n", bootab_sel); |
| |
| out: |
| return bootab_sel; |
| } |
| |
| void init_abmode(void) |
| { |
| /* if no MISC, always in BOOTSEL_A state */ |
| BootAB_sel = BOOTSEL_A; |
| } |
| |
| int check_abmode(void) |
| { |
| BootAB_sel = check_misc_abcommand(); |
| return BootAB_sel; |
| } |
| |
| int get_abmode(void) |
| { |
| return BootAB_sel; |
| } |
| |
| int set_abmode(unsigned int abmode_sel) |
| { |
| /* check the expected slot bootable */ |
| if((abmode_sel != BOOTSEL_A) && (abmode_sel != BOOTSEL_B)) { |
| ERR("Invalid slot index to be set !\n"); |
| BootAB_sel = BOOTSEL_INVALID; |
| goto out; |
| } |
| |
| if(!isSlotBootable(&bctrl_miscdata, abmode_sel)) { |
| ERR("Unbootable slot index to be set !\n"); |
| BootAB_sel = BOOTSEL_INVALID; |
| goto out; |
| } |
| |
| BootAB_sel = abmode_sel; |
| /* NOTICE: after running check_android_abcommand, the bootctrl would be initialized |
| * if we still find the invalid bootctrl, only misc/fts init failure scenario would cause |
| * this, so using the other FTS bootloader.abmode command is still unreasonable ! |
| */ |
| |
| out: |
| return BootAB_sel; |
| } |
| |
| |
| int try_abmode(unsigned int abmode_sel) |
| { |
| /* ... and decrement tries remaining, if applicable. */ |
| if (bctrl_miscdata.slots[abmode_sel].successful_boot) { |
| // the slot has booted successfully, no need to decrease the count |
| return 0; |
| } else { |
| if (is_valid_bootctrl(&bctrl_miscdata) && |
| (bctrl_miscdata.slots[abmode_sel].tries_remaining > 0)) { |
| bctrl_miscdata.slots[abmode_sel].tries_remaining -= 1; |
| bctrl_miscdata.crc32 = calculate_crc32(&bctrl_miscdata, |
| sizeof(struct AvbABData) - sizeof(uint32_t)); |
| write_bootctrl_miscdata(&bctrl_miscdata); |
| NOTICE("Decrease retry: Slot=%d retries left=%d\n", |
| abmode_sel, bctrl_miscdata.slots[abmode_sel].tries_remaining); |
| return 0; |
| } else if (is_valid_bootctrl(&bctrl_miscdata) && |
| (bctrl_miscdata.slots[(abmode_sel + 1) % MAX_SLOTS].tries_remaining > 0)) { |
| // has the other valid slot |
| return 1; |
| } |
| } |
| /* NOTICE: No need to handle this case as slot would be recognized as unbootable |
| * |
| else if(is_valid_bootctrl()) { |
| slot_set_unbootable(&bctrl.slot_info[abmode_sel]); |
| return -1; |
| } |
| else { |
| // invalid bootctrl metadata |
| return 1; |
| } |
| */ |
| return -1; |
| } |