blob: 562434fe72ffac0fd9ea4d52f7f8d1dd54bfbf7c [file] [log] [blame]
/*
* 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;
}