blob: 3129b1a21a63abbb58e9cd624a9e573e1d1cda92 [file] [log] [blame]
/*
* Copyright (c) 2019 MediaTek Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <assert.h>
#include <endian.h>
#include <errno.h>
#include <lib/bio.h>
#include <lib/bootctrl.h>
#include <lib/bootctrl_plat.h>
#include <lib/cksum.h>
#include <lib/mempool.h>
#include <libavb_ab/libavb_ab.h>
#include <platform.h>
#include <string.h>
#include <trace.h>
#define BOOTCTR_PARTITION "misc"
#define MOD "bootctrl"
#define SLOT_COUNT 2
#define LOCAL_TRACE 0
/******************************************************************************
* DEBUG
******************************************************************************/
static AvbABData metadata_saved;
static int metadata_read = 0;
const char *get_suffix(void)
{
const char *suffix[] = {BOOTCTRL_SUFFIX_A, BOOTCTRL_SUFFIX_B};
int slot = get_current_slot();
if (slot < 0 || slot >= SLOT_COUNT)
return NULL;
return suffix[slot];
}
static int read_write_partition_info(AvbABData *bctrl ,int mode)
{
int ret = -1;
bdev_t *bdev;
size_t bdev_erase_size;
size_t total_erase_size;
ssize_t read_bytes;
ssize_t write_bytes;
void *metadata_info_buf = NULL;
uint32_t crc32val;
bdev = bio_open_by_label(BOOTCTR_PARTITION);
if (!bdev) {
LTRACEF("Partition [%s] is not exist.\n", BOOTCTR_PARTITION);
return ret;
}
ASSERT(bdev->geometry != NULL);
bdev_erase_size = (size_t)bdev->geometry->erase_size;
LTRACEF("bdev_erase_size is %d. \n", bdev_erase_size);
switch (mode) {
case READ_PARTITION:
if ((metadata_read) && (!memcmp(metadata_saved.magic ,AVB_AB_MAGIC,
AVB_AB_MAGIC_LEN))) {
memcpy(bctrl, &metadata_saved, sizeof(AvbABData));
} else {
read_bytes = bio_read(bdev, (void *)bctrl,
OFFSETOF_METADATA_INFO, sizeof(AvbABData));
if (-1 == read_bytes) {
LTRACEF("bio_read error %ld\n", read_bytes);
goto out;
}
memcpy(&metadata_saved,bctrl,sizeof(AvbABData));
metadata_read = 1;
}
break;
case WRITE_PARTITION:
crc32val = (uint32_t)crc32(0UL, (const uint8_t *)bctrl,
sizeof(AvbABData) - sizeof(uint32_t));
bctrl->crc32 = ntohl(crc32val);
total_erase_size = bdev_erase_size *
((OFFSETOF_METADATA_INFO + sizeof(AvbABData)) / bdev_erase_size + 1);
// allocate buffer for the partition
metadata_info_buf = mempool_alloc(total_erase_size, MEMPOOL_ANY);
if (!metadata_info_buf) {
LTRACEF("mempool alloc error. \n");
goto out;
}
// read the partition data
read_bytes = bio_read(bdev, (void *)metadata_info_buf,
0, total_erase_size);
if (-1 == read_bytes) {
LTRACEF("bio_read error: %zd\n", read_bytes);
goto out;
}
// update the content in partition_buf
memcpy(metadata_info_buf + OFFSETOF_METADATA_INFO, bctrl, sizeof(AvbABData));
// earse the partition
write_bytes = bio_erase(bdev, 0, total_erase_size);
if (write_bytes < 0) {
LTRACEF("bio_erase error: %zd\n", write_bytes);
goto out;
}
// write the partition
write_bytes = bio_write(bdev, (void *)metadata_info_buf,
0, total_erase_size);
if ((size_t)write_bytes != total_erase_size) {
LTRACEF("bio_write error: %zd\n", write_bytes);
goto out;
}
metadata_read = 0; //force to read from partition after successful blkdev_write()E
break;
default:
break;
}
ret = 0;
out:
bio_close(bdev);
if (metadata_info_buf != NULL) {
mempool_free(metadata_info_buf);
}
return ret;
}
int ab_metadata_init(int slot)
{
int slot1 = 0;
int ret = -1 ;
AvbABSlotData *slotp;
AvbABData metadata;
if (slot < 0 || slot >= SLOT_COUNT) {
return -1;
}
ret = read_write_partition_info(&metadata, READ_PARTITION);
if (ret < 0) {
return -1;
}
memcpy(metadata.magic ,AVB_AB_MAGIC, AVB_AB_MAGIC_LEN);
/* Set highest priority and reset retry count */
slotp = &metadata.slots[slot];
slotp->successful_boot = 0;
slotp->priority = AVB_AB_MAX_PRIORITY;
slotp->tries_remaining = AVB_AB_MAX_TRIES_REMAINING;
slotp->efuse_write = 0;
slotp->bl_ver = 0;
/* Re-set arg to another slot */
slot1 = (slot == 0) ? 1 : 0;
slotp = &metadata.slots[slot1];
slotp->successful_boot = 0;
slotp->priority = AVB_AB_MAX_PRIORITY - 1;
slotp->tries_remaining = AVB_AB_MAX_TRIES_REMAINING;
slotp->efuse_write = 0;
slotp->bl_ver = 0;
ret = read_write_partition_info(&metadata, WRITE_PARTITION);
if (ret < 0) {
return -1;
}
return 0;
}
int get_current_slot(void)
{
int slot = 0, ret = -1;
AvbABData metadata;
ret = read_write_partition_info(&metadata, READ_PARTITION);
if (ret < 0) {
return -1;
}
if (memcmp(metadata.magic , AVB_AB_MAGIC, AVB_AB_MAGIC_LEN) != 0) {
LTRACEF("booctrl magic not match init default value\n");
ab_metadata_init(0);
plat_ab_set_active_bootdev(0);
return slot;
}
slot = (metadata.slots[0].priority >= metadata.slots[1].priority) ? 0 : 1;
return slot;
}
int rollback_slot(int slot)
{
int slot1,ret = -1;
AvbABSlotData *slotp;
AvbABData metadata;
ret = read_write_partition_info(&metadata, READ_PARTITION);
if (ret < 0) {
return -1;
}
/* Set highest priority and reset retry count */
slotp = &metadata.slots[slot];
slotp->priority = AVB_AB_MAX_PRIORITY;
/* Ensure other slot doesn't have as high a priority. */
slot1 = (slot == 0) ? 1 : 0;
slotp = &metadata.slots[slot1];
if (slotp->priority == AVB_AB_MAX_PRIORITY)
slotp->priority = AVB_AB_MAX_PRIORITY - 1;
ret = read_write_partition_info(&metadata, WRITE_PARTITION);
if (ret < 0) {
return -1;
}
return 0;
}
uint8_t get_retry_count(int slot)
{
int ret = -1;
AvbABSlotData *slotp;
AvbABData metadata;
if (slot < 0 || slot >= SLOT_COUNT) {
return ret;
}
ret = read_write_partition_info(&metadata, READ_PARTITION);
if (ret < 0) {
return -1;
}
slotp = &metadata.slots[slot];
return slotp->tries_remaining;
}
int reduce_retry_count(int slot)
{
int ret = -1;
AvbABSlotData *slotp;
AvbABData metadata;
if (slot < 0 || slot >= SLOT_COUNT) {
return -1;
}
ret = read_write_partition_info(&metadata, READ_PARTITION);
if (ret < 0) {
return -1;
}
slotp = &metadata.slots[slot];
if (slotp->tries_remaining > 0)
slotp->tries_remaining--;
ret = read_write_partition_info(&metadata, WRITE_PARTITION);
if (ret < 0) {
return -1;
}
return 0;
}
int check_valid_slot(void)
{
int ret = -1;
AvbABData metadata;
ret = read_write_partition_info(&metadata, READ_PARTITION);
if (ret < 0) {
return -1;
}
if (memcmp(metadata.magic , AVB_AB_MAGIC, AVB_AB_MAGIC_LEN) != 0) {
LTRACEF("booctrl magic not match init default value\n");
ab_metadata_init(0);
return 0;
}
if ((metadata.slots[0].priority > 0) || (metadata.slots[1].priority > 0))
return 0;
return -1;
}
int mark_slot_invalid(int slot)
{
int ret = -1;
AvbABSlotData *slotp;
AvbABData metadata;
if (slot < 0 || slot >= SLOT_COUNT) {
return -1;
}
ret = read_write_partition_info(&metadata, READ_PARTITION);
if (ret < 0) {
return -1;
}
slotp = &metadata.slots[slot];
slotp->successful_boot = 0;
slotp->priority = 0;
ret = read_write_partition_info(&metadata, WRITE_PARTITION);
if (ret < 0) {
return -1;
}
return 0;
}
int get_bootup_status(int slot)
{
int ret = -1;
AvbABSlotData *slotp;
AvbABData metadata;
if (slot < 0 || slot >= SLOT_COUNT) {
return -1;
}
ret = read_write_partition_info(&metadata, READ_PARTITION);
if (ret < 0) {
return -1;
}
slotp = &metadata.slots[slot];
return slotp->successful_boot;
}
static void check_update_anti_rollback_bl_ver(int slot)
{
int ret = -1;
AvbABSlotData *slotp;
AvbABData metadata;
ret = read_write_partition_info(&metadata, READ_PARTITION);
if (ret < 0) {
return;
}
slotp = &metadata.slots[slot];
if (slotp->efuse_write) {
ret = plat_write_lk_antirollback_version(slotp->bl_ver);
if (ret) {
LTRACEF("efuse write bl ver %d fail: %d\n",slotp->bl_ver,ret);
}
slotp->efuse_write = 0;
read_write_partition_info(&metadata, WRITE_PARTITION);
}
}
int check_ab_boot(void)
{
int ret, slot,next_slot;
if (check_valid_slot() != 0){
ab_metadata_init(0);
plat_ab_set_active_bootdev(0);
}
slot = get_current_slot();
if (get_bootup_status(slot) == 1) {
check_update_anti_rollback_bl_ver(slot);
return 0;
}
if (get_retry_count(slot) > 0) {
reduce_retry_count(slot);
return 0;
}
mark_slot_invalid(slot);
if (check_valid_slot() == -1)
return -EIO;
next_slot = (slot == 0) ? 1 : 0;
ret = plat_ab_set_active_bootdev(next_slot);
/* report the bad slot error code when set new active bootdev ok */
if (ret == 0)
return -EBADSLT;
/* otherwise, report the error code we encounter */
return ret;
}