blob: 8396d8c6db69300be0648675016ef65cf58e0f0d [file] [log] [blame] [edit]
/*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
* *
This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* *
This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
* *
You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* *
Description:
*/
#include <common.h>
#include <command.h>
#include <environment.h>
#include <malloc.h>
#include <asm/byteorder.h>
#include <config.h>
#include <asm/arch/io.h>
#include <partition_table.h>
#include <libavb.h>
#include <version.h>
#ifdef CONFIG_BOOTLOADER_CONTROL_BLOCK
extern int store_read_ops(
unsigned char *partition_name,
unsigned char * buf, uint64_t off, uint64_t size);
extern int store_write_ops(
unsigned char *partition_name,
unsigned char * buf, uint64_t off, uint64_t size);
#define AB_METADATA_MISC_PARTITION_OFFSET 2048
#define MISCBUF_SIZE 2080
/* Magic for the A/B struct when serialized. */
#define AVB_AB_MAGIC "\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];
} 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. */
uint8_t magic[AVB_AB_MAGIC_LEN];
/* 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;
}AvbABData;
/*static int clear_misc_partition(char *clearbuf, int size)
{
char *partition = "misc";
memset(clearbuf, 0, size);
if (store_write_ops((unsigned char *)partition,
(unsigned char *)clearbuf, 0, size) < 0) {
printf("failed to clear %s.\n", partition);
return -1;
}
return 0;
}*/
bool boot_info_validate(AvbABData* info)
{
if (memcmp(info->magic, AVB_AB_MAGIC, AVB_AB_MAGIC_LEN) != 0) {
printf("Magic %s is incorrect.\n", info->magic);
return false;
}
if (info->version_major > AVB_AB_MAJOR_VERSION) {
printf("No support for given major version.\n");
return false;
}
return true;
}
void boot_info_reset(AvbABData* info)
{
memset(info, '\0', sizeof(AvbABData));
memcpy(info->magic, AVB_AB_MAGIC, AVB_AB_MAGIC_LEN);
info->version_major = AVB_AB_MAJOR_VERSION;
info->version_minor = AVB_AB_MINOR_VERSION;
info->slots[0].priority = AVB_AB_MAX_PRIORITY;
info->slots[0].tries_remaining = AVB_AB_MAX_TRIES_REMAINING;
info->slots[0].successful_boot = 0;
info->slots[1].priority = AVB_AB_MAX_PRIORITY - 1;
info->slots[1].tries_remaining = AVB_AB_MAX_TRIES_REMAINING;
info->slots[1].successful_boot = 0;
}
void dump_boot_info(AvbABData* info)
{
printf("info->magic = %s\n", info->magic);
printf("info->version_major = %d\n", info->version_major);
printf("info->version_minor = %d\n", info->version_minor);
printf("info->slots[0].priority = %d\n", info->slots[0].priority);
printf("info->slots[0].tries_remaining = %d\n", info->slots[0].tries_remaining);
printf("info->slots[0].successful_boot = %d\n", info->slots[0].successful_boot);
printf("info->slots[1].priority = %d\n", info->slots[1].priority);
printf("info->slots[1].tries_remaining = %d\n", info->slots[1].tries_remaining);
printf("info->slots[1].successful_boot = %d\n", info->slots[1].successful_boot);
printf("info->crc32 = %d\n", info->crc32);
}
static bool slot_is_bootable(AvbABSlotData* slot) {
return slot->priority > 0 &&
(slot->successful_boot || (slot->tries_remaining > 0));
}
int get_active_slot(AvbABData* info) {
if (info->slots[0].priority > info->slots[1].priority)
return 0;
else
return 1;
}
int boot_info_set_active_slot(AvbABData* info, int slot)
{
unsigned int other_slot_number;
/* Make requested slot top priority, unsuccessful, and with max tries. */
info->slots[slot].priority = AVB_AB_MAX_PRIORITY;
info->slots[slot].tries_remaining = AVB_AB_MAX_TRIES_REMAINING;
info->slots[slot].successful_boot = 0;
/* Ensure other slot doesn't have as high a priority. */
other_slot_number = 1 - slot;
if (info->slots[other_slot_number].priority == AVB_AB_MAX_PRIORITY) {
info->slots[other_slot_number].priority = AVB_AB_MAX_PRIORITY - 1;
}
dump_boot_info(info);
return 0;
}
int boot_info_open_partition(char *miscbuf)
{
#ifdef CONFIG_ZIRCON_BOOT_IMAGE
char *partition = "durable_boot";
#else
char *partition = "misc";
#endif
//int i;
printf("Start read %s partition datas!\n", partition);
if (store_read_ops((unsigned char *)partition,
(unsigned char *)miscbuf, 0, MISCBUF_SIZE) < 0) {
printf("failed to store read %s.\n", partition);
return -1;
}
/*for (i = AB_METADATA_MISC_PARTITION_OFFSET;i < (AB_METADATA_MISC_PARTITION_OFFSET+AVB_AB_DATA_SIZE);i++)
printf("buf: %c \n", miscbuf[i]);*/
return 0;
}
bool boot_info_load(AvbABData *out_info, char *miscbuf)
{
memcpy(out_info, miscbuf+AB_METADATA_MISC_PARTITION_OFFSET, AVB_AB_DATA_SIZE);
dump_boot_info(out_info);
return true;
}
bool boot_info_save(AvbABData *info, char *miscbuf)
{
#ifdef CONFIG_ZIRCON_BOOT_IMAGE
char *partition = "durable_boot";
#else
char *partition = "misc";
#endif
printf("save boot-info \n");
info->crc32 = avb_htobe32(
avb_crc32((const uint8_t*)info, sizeof(AvbABData) - sizeof(uint32_t)));
memcpy(miscbuf+AB_METADATA_MISC_PARTITION_OFFSET, info, AVB_AB_DATA_SIZE);
dump_boot_info(info);
store_write_ops((unsigned char *)partition, (unsigned char *)miscbuf, 0, MISCBUF_SIZE);
return true;
}
static int do_GetValidSlot(
cmd_tbl_t * cmdtp,
int flag,
int argc,
char * const argv[])
{
char miscbuf[MISCBUF_SIZE] = {0};
AvbABData info;
int slot;
bool bootable_a, bootable_b;
if (argc != 1) {
return cmd_usage(cmdtp);
}
boot_info_open_partition(miscbuf);
boot_info_load(&info, miscbuf);
if (!boot_info_validate(&info)) {
printf("boot-info is invalid. Resetting.\n");
boot_info_reset(&info);
boot_info_save(&info, miscbuf);
}
slot = get_active_slot(&info);
printf("active slot = %d\n", slot);
bootable_a = slot_is_bootable(&(info.slots[0]));
bootable_b = slot_is_bootable(&(info.slots[1]));
if ((slot == 0) && (bootable_a)) {
if (has_boot_slot == 1) {
setenv("active_slot","_a");
setenv("boot_part","boot_a");
setenv("slot-suffixes","0");
}
else {
setenv("active_slot","normal");
setenv("boot_part","boot");
}
return 0;
}
if ((slot == 1) && (bootable_b)) {
if (has_boot_slot == 1) {
setenv("active_slot","_b");
setenv("boot_part","boot_b");
setenv("slot-suffixes","1");
}
else {
setenv("active_slot","normal");
setenv("boot_part","boot");
}
return 0;
}
return 0;
}
static int do_SetActiveSlot(
cmd_tbl_t * cmdtp,
int flag,
int argc,
char * const argv[])
{
char miscbuf[MISCBUF_SIZE] = {0};
AvbABData info;
if (argc != 2) {
return cmd_usage(cmdtp);
}
if (has_boot_slot == 0) {
printf("device is not ab mode\n");
return -1;
}
boot_info_open_partition(miscbuf);
boot_info_load(&info, miscbuf);
if (!boot_info_validate(&info)) {
printf("boot-info is invalid. Resetting.\n");
boot_info_reset(&info);
boot_info_save(&info, miscbuf);
}
if (strcmp(argv[1], "a") == 0) {
setenv("active_slot","_a");
setenv("slot-suffixes","0");
setenv("boot_part","boot_a");
printf("set active slot a \n");
boot_info_set_active_slot(&info, 0);
} else if (strcmp(argv[1], "b") == 0) {
setenv("active_slot","_b");
setenv("slot-suffixes","1");
setenv("boot_part","boot_b");
printf("set active slot b \n");
boot_info_set_active_slot(&info, 1);
} else {
printf("error input slot\n");
return -1;
}
boot_info_save(&info, miscbuf);
return 0;
}
int do_GetSystemMode (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
char* system;
#ifdef CONFIG_SYSTEM_AS_ROOT
system = CONFIG_SYSTEM_AS_ROOT;
strcpy(system, CONFIG_SYSTEM_AS_ROOT);
printf("CONFIG_SYSTEM_AS_ROOT: %s \n", CONFIG_SYSTEM_AS_ROOT);
if (strcmp(system, "systemroot") == 0) {
setenv("system_mode","1");
}
else {
setenv("system_mode","0");
}
#else
setenv("system_mode","0");
#endif
return 0;
}
int do_GetAvbMode (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
#ifdef CONFIG_AVB2
char* avbmode;
avbmode = CONFIG_AVB2;
strcpy(avbmode, CONFIG_AVB2);
printf("CONFIG_AVB2: %s \n", CONFIG_AVB2);
if (strcmp(avbmode, "avb2") == 0) {
setenv("avb2","1");
}
else {
setenv("avb2","0");
}
#else
setenv("avb2","0");
#endif
return 0;
}
#endif /* CONFIG_BOOTLOADER_CONTROL_BLOCK */
U_BOOT_CMD(
get_valid_slot, 2, 0, do_GetValidSlot,
"get_valid_slot",
"\nThis command will choose valid slot to boot up which saved in misc\n"
"partition by mark to decide whether execute command!\n"
"So you can execute command: get_valid_slot"
);
U_BOOT_CMD(
set_active_slot, 2, 1, do_SetActiveSlot,
"set_active_slot",
"\nThis command will set active slot\n"
"So you can execute command: set_active_slot a"
);
U_BOOT_CMD(
get_system_as_root_mode, 1, 0, do_GetSystemMode,
"get_system_as_root_mode",
"\nThis command will get system_as_root_mode\n"
"So you can execute command: get_system_as_root_mode"
);
U_BOOT_CMD(
get_avb_mode, 1, 0, do_GetAvbMode,
"get_avb_mode",
"\nThis command will get avb mode\n"
"So you can execute command: get_avb_mode"
);