210 lines
4.8 KiB
C
Executable File
210 lines
4.8 KiB
C
Executable File
/*
|
|
* Copyright (C) 2016 Freescale Semiconductor, Inc.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <stdlib.h>
|
|
#include <linux/string.h>
|
|
#include <fsl_fastboot.h>
|
|
#include <fsl_avb.h>
|
|
|
|
/* as libavb's bootctl doesn't have the get_var support
|
|
* we add the getvar support on our side ...*/
|
|
#ifndef MAX_PTN
|
|
#define MAX_PTN 32
|
|
#endif
|
|
#define SLOT_NUM 2
|
|
static char *slot_suffix[SLOT_NUM] = {"_a", "_b"};
|
|
|
|
static int strcmp_l1(const char *s1, const char *s2) {
|
|
if (!s1 || !s2)
|
|
return -1;
|
|
return strncmp(s1, s2, strlen(s1));
|
|
}
|
|
|
|
static bool slot_is_bootable(AvbABSlotData* slot) {
|
|
#ifdef CONFIG_DUAL_BOOTLOADER
|
|
/* The 'bootloader_verified' will be set when the slot has only one chance
|
|
* left, which means the slot is bootable even tries_remaining is 0.
|
|
*/
|
|
return slot->priority > 0 &&
|
|
(slot->successful_boot || (slot->tries_remaining > 0)
|
|
|| (slot->bootloader_verified == 1));
|
|
#else
|
|
return slot->priority > 0 &&
|
|
(slot->successful_boot || (slot->tries_remaining > 0));
|
|
#endif
|
|
}
|
|
|
|
int slotidx_from_suffix(char *suffix) {
|
|
int slot = -1;
|
|
|
|
if (!strcmp(suffix, "_a") ||
|
|
!strcmp(suffix, "a"))
|
|
slot = 0;
|
|
else if (!strcmp(suffix, "_b") ||
|
|
!strcmp(suffix, "b"))
|
|
slot = 1;
|
|
|
|
return slot;
|
|
}
|
|
|
|
bool is_slotvar_avb(char *cmd) {
|
|
|
|
assert(cmd != NULL);
|
|
if (!strcmp_l1("has-slot:", cmd) ||
|
|
!strcmp_l1("slot-successful:", cmd) ||
|
|
!strcmp_l1("slot-count", cmd) ||
|
|
!strcmp_l1("slot-suffixes", cmd) ||
|
|
!strcmp_l1("current-slot", cmd) ||
|
|
!strcmp_l1("slot-unbootable:", cmd) ||
|
|
!strcmp_l1("slot-retry-count:", cmd))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
int get_curr_slot(AvbABData *ab_data) {
|
|
if (slot_is_bootable(&ab_data->slots[0]) &&
|
|
slot_is_bootable(&ab_data->slots[1])) {
|
|
if (ab_data->slots[1].priority > ab_data->slots[0].priority)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
} else if (slot_is_bootable(&ab_data->slots[0]))
|
|
return 0;
|
|
else if (slot_is_bootable(&ab_data->slots[1]))
|
|
return 1;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
extern struct fastboot_ptentry g_ptable[MAX_PTN];
|
|
extern unsigned int g_pcount;
|
|
|
|
static bool has_slot(char *cmd) {
|
|
unsigned int n;
|
|
char *ptr;
|
|
|
|
for (n = 0; n < g_pcount; n++) {
|
|
ptr = strstr(g_ptable[n].name, cmd);
|
|
if (ptr != NULL) {
|
|
ptr += strlen(cmd);
|
|
if (!strcmp(ptr, "_a") || !strcmp(ptr, "_b"))
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int get_slotvar_avb(AvbABOps *ab_ops, char *cmd, char *buffer, size_t size) {
|
|
|
|
AvbABData ab_data;
|
|
AvbABSlotData *slot_data;
|
|
int slot;
|
|
|
|
if ((ab_ops == NULL) || (cmd == NULL) || (buffer == NULL))
|
|
return -1;
|
|
|
|
char *str = cmd;
|
|
if (!strcmp_l1("has-slot:", cmd)) {
|
|
str += strlen("has-slot:");
|
|
if (has_slot(str))
|
|
strlcpy(buffer, "yes", size);
|
|
else
|
|
strlcpy(buffer, "no", size);
|
|
return 0;
|
|
|
|
} else if (!strcmp_l1("slot-suffixes", cmd)) {
|
|
strlcpy(buffer, "_a,_b", size);
|
|
return 0 ;
|
|
|
|
} else if (!strcmp_l1("slot-count", cmd)) {
|
|
strlcpy(buffer, "2", size);
|
|
return 0 ;
|
|
}
|
|
|
|
/* load ab meta */
|
|
if (ab_ops->read_ab_metadata == NULL ||
|
|
ab_ops->read_ab_metadata(ab_ops, &ab_data) != AVB_IO_RESULT_OK) {
|
|
strlcpy(buffer, "ab data read error", size);
|
|
return -1 ;
|
|
}
|
|
|
|
if (!strcmp_l1("current-slot", cmd)) {
|
|
int curr = get_curr_slot(&ab_data);
|
|
if (curr >= 0 && curr < SLOT_NUM)
|
|
strlcpy(buffer, slot_suffix[curr] + sizeof(unsigned char), size);
|
|
else {
|
|
strlcpy(buffer, "no bootable slot", size);
|
|
return -1;
|
|
}
|
|
|
|
} else if (!strcmp_l1("slot-successful:", cmd)) {
|
|
str += strlen("slot-successful:");
|
|
slot = slotidx_from_suffix(str);
|
|
if (slot < 0) {
|
|
strlcpy(buffer, "no such slot", size);
|
|
return -1;
|
|
} else {
|
|
slot_data = &ab_data.slots[slot];
|
|
bool succ = (slot_data->successful_boot != 0);
|
|
strlcpy(buffer, succ ? "yes" : "no", size);
|
|
}
|
|
|
|
} else if (!strcmp_l1("slot-unbootable:", cmd)) {
|
|
str += strlen("slot-unbootable:");
|
|
slot = slotidx_from_suffix(str);
|
|
if (slot < 0) {
|
|
strlcpy(buffer, "no such slot", size);
|
|
return -1;
|
|
} else {
|
|
slot_data = &ab_data.slots[slot];
|
|
bool bootable = slot_is_bootable(slot_data);
|
|
strlcpy(buffer, bootable ? "no" : "yes", size);
|
|
}
|
|
|
|
} else if (!strcmp_l1("slot-retry-count:", cmd)) {
|
|
str += strlen("slot-retry-count:");
|
|
slot = slotidx_from_suffix(str);
|
|
if (slot < 0) {
|
|
strlcpy(buffer, "no such slot", size);
|
|
return -1;
|
|
}
|
|
else {
|
|
slot_data = &ab_data.slots[slot];
|
|
char var[7];
|
|
sprintf(var, "%d",
|
|
slot_data->tries_remaining);
|
|
strlcpy(buffer, var, size);
|
|
}
|
|
|
|
} else {
|
|
strlcpy(buffer, "no such slot command", size);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
char *select_slot(AvbABOps *ab_ops) {
|
|
AvbABData ab_data;
|
|
int curr;
|
|
|
|
if (ab_ops == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/* load ab meta */
|
|
if (ab_ops->read_ab_metadata == NULL ||
|
|
ab_ops->read_ab_metadata(ab_ops, &ab_data) != AVB_IO_RESULT_OK) {
|
|
return NULL;
|
|
}
|
|
curr = get_curr_slot(&ab_data);
|
|
if (curr >= 0 && curr < SLOT_NUM)
|
|
return slot_suffix[curr];
|
|
else
|
|
return NULL;
|
|
}
|