1704 lines
49 KiB
C
1704 lines
49 KiB
C
/**
|
|
* @file ab_partition_parse.c
|
|
*
|
|
* Copyright (c) 2021 Semidrive Semiconductor.
|
|
* All rights reserved.
|
|
*
|
|
* Description:
|
|
*
|
|
* Revision History:
|
|
* -----------------
|
|
*/
|
|
#include <ab_partition_parser.h>
|
|
#include <compiler.h>
|
|
#include <crc32.h>
|
|
#include <debug.h>
|
|
#include <disk.h>
|
|
#include <partition_parser.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <types.h>
|
|
|
|
//#define AB_DEBUG
|
|
|
|
/* Slot suffix */
|
|
#define SUFFIX_LEN 2
|
|
|
|
const char *suffix_slot[] = {"_a", "_b"};
|
|
const char *suffix_delimiter = "_";
|
|
|
|
unsigned long long att_val_array[ATTR_NUM] = {
|
|
[ATTR_UNBOOTABLE] = PART_ATT_UNBOOTABLE_VAL,
|
|
[ATTR_ACTIVE] = PART_ATT_ACTIVE_VAL | PART_ATT_PRIORITY_VAL,
|
|
[ATTR_SUCCESSFUL] = PART_ATT_SUCCESSFUL_VAL,
|
|
[ATTR_RETRY] = PART_ATT_MAX_RETRY_COUNT_VAL,
|
|
};
|
|
|
|
/* local functions. */
|
|
static int boot_slot_index[AB_SUPPORTED_SLOTS];
|
|
static void mark_all_partitions_active(partition_device_t *part_dev,
|
|
unsigned slot);
|
|
void ptdev_mark_active_slot(partition_device_t *part_dev, int slot);
|
|
static void swap_guid(partition_device_t *part_dev, int new_slot);
|
|
static char *ptdev_scan_for_multislot_byname(partition_device_t *part_dev,
|
|
char *name);
|
|
|
|
/**
|
|
* @brief get the lowest common multiple for "size"
|
|
*
|
|
* @param size iput number
|
|
* @param aligned aligned number
|
|
* @return uint64_t lowest common multiple
|
|
*/
|
|
static uint64_t round_up(uint64_t size, uint64_t aligned)
|
|
{
|
|
uint64_t mod = 0;
|
|
|
|
if (aligned == 0 || size < aligned)
|
|
return aligned;
|
|
|
|
/* Sometimes, 'aligned' is not equal to power of 2 */
|
|
mod = size % aligned;
|
|
|
|
size += mod ? aligned - mod : 0;
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
* @brief get the greatest common divisor for "size"
|
|
*
|
|
* @param size iput number
|
|
* @param aligned aligned number
|
|
* @return uint64_t the greatest common divisor
|
|
*/
|
|
static uint64_t round_down(uint64_t size, uint64_t aligned)
|
|
{
|
|
uint64_t mod = 0;
|
|
|
|
if (aligned == 0 || size < aligned)
|
|
return 0;
|
|
|
|
/* Sometimes, 'aligned' is not equal to power of 2 */
|
|
mod = size % aligned;
|
|
|
|
size -= mod;
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
* @brief print reboot reason
|
|
*
|
|
* @param reason
|
|
*/
|
|
__UNUSED static void reboot_device(int reason) { printf("reboot reason:%d\n", reason); }
|
|
|
|
/**
|
|
* @brief memcpy by size
|
|
*
|
|
* @param dest
|
|
* @param dst_size
|
|
* @param src
|
|
* @param src_size
|
|
* @return size_t
|
|
*/
|
|
static size_t memscpy(void *dest, size_t dst_size, const void *src,
|
|
size_t src_size)
|
|
{
|
|
size_t copy_size = MIN(dst_size, src_size);
|
|
memcpy(dest, src, copy_size);
|
|
return copy_size;
|
|
}
|
|
|
|
/**
|
|
* @brief strrev
|
|
*
|
|
* @param str
|
|
*/
|
|
static void strrev(unsigned char *str)
|
|
{
|
|
int i;
|
|
int j;
|
|
unsigned char a;
|
|
unsigned len = strlen((const char *)str);
|
|
|
|
for (i = 0, j = len - 1; i < j; i++, j--) {
|
|
a = str[i];
|
|
str[i] = str[j];
|
|
str[j] = a;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief int to asic
|
|
*
|
|
* @param num
|
|
* @param str
|
|
* @param len
|
|
* @param base
|
|
* @return int
|
|
*/
|
|
static int itoa_s(int num, unsigned char *str, int len, int base)
|
|
{
|
|
int sum = num;
|
|
int i = 0;
|
|
int digit;
|
|
|
|
if (len == 0)
|
|
return -1;
|
|
|
|
do {
|
|
digit = sum % base;
|
|
|
|
if (digit < 0xA)
|
|
str[i++] = '0' + digit;
|
|
else
|
|
str[i++] = 'A' + digit - 0xA;
|
|
|
|
sum /= base;
|
|
} while (sum && (i < (len - 1)));
|
|
|
|
if (i == (len - 1) && sum)
|
|
return -1;
|
|
|
|
str[i] = '\0';
|
|
strrev(str);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Read slot attribute of of the partition_entry
|
|
*
|
|
* @param partition_entries
|
|
* @param index
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static inline bool slot_is_active(struct partition_entry *partition_entries,
|
|
unsigned index)
|
|
{
|
|
return !!(partition_entries[index].attribute_flag & PART_ATT_ACTIVE_VAL);
|
|
}
|
|
|
|
/**
|
|
* @brief Get a slot's successful bit
|
|
*
|
|
* @param partition_entries
|
|
* @param index
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static inline bool slot_is_sucessful(struct partition_entry *partition_entries,
|
|
unsigned index)
|
|
{
|
|
return !!(partition_entries[index].attribute_flag &
|
|
PART_ATT_SUCCESSFUL_VAL);
|
|
}
|
|
|
|
/**
|
|
* @brief Get a slot's retry count bit
|
|
*
|
|
* @param partition_entries
|
|
* @param index
|
|
* @return unsigned
|
|
*/
|
|
static inline unsigned
|
|
slot_retry_count(struct partition_entry *partition_entries, unsigned index)
|
|
{
|
|
return ((partition_entries[index].attribute_flag &
|
|
PART_ATT_MAX_RETRY_COUNT_VAL) >>
|
|
PART_ATT_MAX_RETRY_CNT_BIT);
|
|
}
|
|
|
|
/**
|
|
* @brief Get a slot's priority bit
|
|
*
|
|
* @param partition_entries
|
|
* @param index
|
|
* @return unsigned
|
|
*/
|
|
static inline unsigned slot_priority(struct partition_entry *partition_entries,
|
|
unsigned index)
|
|
{
|
|
return ((partition_entries[index].attribute_flag & PART_ATT_PRIORITY_VAL) >>
|
|
PART_ATT_PRIORITY_BIT);
|
|
}
|
|
|
|
/**
|
|
* @brief Get a slot's bootalbe bit
|
|
* @param partition_entries
|
|
* @param index
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static inline bool slot_is_bootable(struct partition_entry *partition_entries,
|
|
unsigned index)
|
|
{
|
|
return !(
|
|
(partition_entries[index].attribute_flag & PART_ATT_UNBOOTABLE_VAL) >>
|
|
PART_ATT_UNBOOTABLE_BIT);
|
|
}
|
|
|
|
/**
|
|
* @brief Get the slot attribute object
|
|
* @param partition_entries
|
|
* @param index
|
|
* @return uint8_t
|
|
*/
|
|
__UNUSED static inline uint8_t
|
|
get_slot_attribute(struct partition_entry *partition_entries, unsigned index)
|
|
{
|
|
return ((partition_entries[index].attribute_flag >> PART_ATT_PRIORITY_BIT) &
|
|
0xFF);
|
|
}
|
|
|
|
/**
|
|
* @brief Get the inverse slot object
|
|
* @param part_dev
|
|
* @param slot
|
|
* @return int
|
|
*/
|
|
int get_inverse_slot(partition_device_t *part_dev, int slot)
|
|
{
|
|
if (part_dev->multislot_support)
|
|
return (slot == SLOT_A) ? SLOT_B : SLOT_A;
|
|
|
|
return INVALID;
|
|
}
|
|
|
|
/**
|
|
* @brief Decrease a slot's retrycnt
|
|
*
|
|
* @param part_dev
|
|
* @param slot
|
|
*/
|
|
static void decrease_slot_retrycnt(partition_device_t *part_dev, unsigned slot)
|
|
{
|
|
int i;
|
|
char *pname = NULL;
|
|
char *suffix_str = NULL;
|
|
uint64_t boot_retry_count;
|
|
struct partition_entry *partition_entries;
|
|
int partition_count = 0;
|
|
|
|
if (NULL == part_dev) {
|
|
PT_ERROR("Invalide partition dev\n");
|
|
return;
|
|
}
|
|
|
|
if ((slot != SLOT_A) && (slot != SLOT_B)) {
|
|
PT_ERROR("mark_slot_attr:slot =%d unknown\n", slot);
|
|
return;
|
|
}
|
|
|
|
partition_entries = ptdev_get_partition_entries(part_dev);
|
|
partition_count = ptdev_get_partition_count(part_dev);
|
|
|
|
for (i = 0; i < partition_count; i++) {
|
|
pname = (char *)partition_entries[i].name;
|
|
#ifdef AB_DEBUG
|
|
PT_ERROR("Transversing partition %s\n", pname);
|
|
#endif
|
|
|
|
/* Find partition, if it is A/B enabled */
|
|
suffix_str = ptdev_scan_for_multislot_byname(part_dev, pname);
|
|
|
|
if (suffix_str) {
|
|
if (!strcmp(suffix_str, SUFFIX_SLOT(slot))) {
|
|
boot_retry_count = slot_retry_count(partition_entries, i);
|
|
|
|
if ((boot_retry_count > 0) &&
|
|
(boot_retry_count < MAX_RETRY_COUNT)) {
|
|
partition_entries[i].attribute_flag &=
|
|
~PART_ATT_MAX_RETRY_COUNT_VAL;
|
|
partition_entries[i].attribute_flag |=
|
|
((boot_retry_count - 1) << PART_ATT_MAX_RETRY_CNT_BIT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
part_dev->attributes_updated = true;
|
|
}
|
|
|
|
/**
|
|
* @brief Inactive a new slot.
|
|
* @param part_dev
|
|
* @param slot
|
|
*/
|
|
void ptdev_deactivate_slot(partition_device_t *part_dev, int slot)
|
|
{
|
|
struct partition_entry *partition_entries;
|
|
int count;
|
|
char *pname;
|
|
char *suffix_str = NULL;
|
|
|
|
if ((!part_dev) || (!part_dev->partition_entries)) {
|
|
PT_ERROR("Invalide partition dev\n");
|
|
return;
|
|
}
|
|
|
|
if ((slot != SLOT_A) && (slot != SLOT_B)) {
|
|
PT_ERROR("ERROR: slot= %d wrong slot number\n", slot);
|
|
return;
|
|
}
|
|
|
|
count = part_dev->count;
|
|
partition_entries = ptdev_get_partition_entries(part_dev);
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
pname = (char *)partition_entries[i].name;
|
|
|
|
/* Find partition, if it is A/B enabled */
|
|
suffix_str = ptdev_scan_for_multislot_byname(part_dev, pname);
|
|
|
|
if (suffix_str) {
|
|
if (!strcmp(suffix_str, SUFFIX_SLOT(slot))) {
|
|
/* Set Unbootable bit */
|
|
SET_BIT(partition_entries[i].attribute_flag,
|
|
PART_ATT_UNBOOTABLE_BIT);
|
|
|
|
/* Clear Sucess bit and Active bits */
|
|
CLR_BIT(partition_entries[i].attribute_flag,
|
|
PART_ATT_SUCCESS_BIT);
|
|
CLR_BIT(partition_entries[i].attribute_flag,
|
|
PART_ATT_ACTIVE_BIT);
|
|
|
|
/* Clear Max retry count and priority value */
|
|
partition_entries[i].attribute_flag &=
|
|
(~PART_ATT_PRIORITY_VAL & ~PART_ATT_MAX_RETRY_COUNT_VAL);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief Active a new slot.
|
|
* use after fisrt start up or after OTA update
|
|
* @param part_dev
|
|
* @param slot
|
|
*/
|
|
void ptdev_activate_slot(partition_device_t *part_dev, int slot)
|
|
{
|
|
struct partition_entry *partition_entries;
|
|
int count;
|
|
char *pname;
|
|
char *suffix_str = NULL;
|
|
|
|
if ((!part_dev) || (!part_dev->partition_entries)) {
|
|
PT_ERROR("Invalide partition dev\n");
|
|
return;
|
|
}
|
|
|
|
if ((slot != SLOT_A) && (slot != SLOT_B)) {
|
|
PT_ERROR("ERROR: slot= %d wrong slot number\n", slot);
|
|
return;
|
|
}
|
|
|
|
count = part_dev->count;
|
|
partition_entries = ptdev_get_partition_entries(part_dev);
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
pname = (char *)partition_entries[i].name;
|
|
|
|
/* Find partition, if it is A/B enabled */
|
|
suffix_str = ptdev_scan_for_multislot_byname(part_dev, pname);
|
|
|
|
if (suffix_str) {
|
|
if (!strcmp(suffix_str, SUFFIX_SLOT(slot))) {
|
|
/* CLR Unbootable bit and Sucess bit*/
|
|
CLR_BIT(partition_entries[i].attribute_flag,
|
|
PART_ATT_UNBOOTABLE_BIT);
|
|
CLR_BIT(partition_entries[i].attribute_flag,
|
|
PART_ATT_SUCCESS_BIT);
|
|
|
|
/* Set Active bits */
|
|
SET_BIT(partition_entries[i].attribute_flag,
|
|
PART_ATT_ACTIVE_BIT);
|
|
|
|
/* Set Max retry count and priority value */
|
|
partition_entries[i].attribute_flag |=
|
|
(PART_ATT_PRIORITY_VAL | PART_ATT_MAX_RETRY_COUNT_VAL);
|
|
}
|
|
} else {
|
|
CLR_BIT(partition_entries[i].attribute_flag,
|
|
PART_ATT_UNBOOTABLE_BIT);
|
|
CLR_BIT(partition_entries[i].attribute_flag, PART_ATT_SUCCESS_BIT);
|
|
SET_BIT(partition_entries[i].attribute_flag, PART_ATT_ACTIVE_BIT);
|
|
partition_entries[i].attribute_flag |=
|
|
(PART_ATT_PRIORITY_VAL | PART_ATT_MAX_RETRY_COUNT_VAL);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief Scan boot partition to find SLOT_A/SLOT_B suffix.
|
|
If found than make multislot_boot flag true and
|
|
scans another partition.
|
|
* @param part_dev
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
bool ptdev_scan_for_multislot(partition_device_t *part_dev)
|
|
{
|
|
int j, count;
|
|
char *tmp1, *tmp2;
|
|
int partition_count;
|
|
struct partition_entry *partition_entries;
|
|
unsigned int len;
|
|
|
|
if ((!part_dev) || (!part_dev->partition_entries)) {
|
|
PT_ERROR("Invalide partition dev\n");
|
|
return 0;
|
|
}
|
|
|
|
partition_count = ptdev_get_partition_count(part_dev);
|
|
partition_entries = ptdev_get_partition_entries(part_dev);
|
|
|
|
/* Intialize all slot specific variables */
|
|
part_dev->multislot_support = false;
|
|
part_dev->active_slot = INVALID;
|
|
part_dev->attributes_updated = false;
|
|
|
|
if (partition_count > NUM_PARTITIONS) {
|
|
PT_ERROR("ERROR: partition_count more than supported.\n");
|
|
return part_dev->multislot_support;
|
|
}
|
|
|
|
int scan_nr = partition_count > MAX_NR_SCAN_FOR_SLOT ? MAX_NR_SCAN_FOR_SLOT
|
|
: partition_count;
|
|
|
|
for (int m = 0; m < scan_nr; m++) {
|
|
tmp1 = (char *)partition_entries[m].name;
|
|
len = strlen(tmp1);
|
|
|
|
if (len < 3)
|
|
continue; /* too few, ignore */
|
|
|
|
for (int x = m + 1; x < scan_nr; x++) {
|
|
if (!strncmp((const char *)tmp1, (char *)partition_entries[x].name,
|
|
len - SUFFIX_LEN) &&
|
|
(len == strlen((char *)partition_entries[x].name))) {
|
|
tmp1 = tmp1 + len - SUFFIX_LEN;
|
|
tmp2 = (char *)(partition_entries[x].name + len - SUFFIX_LEN);
|
|
count = 0;
|
|
|
|
for (j = 0; j < AB_SUPPORTED_SLOTS; j++) {
|
|
if (!strcmp(tmp1, suffix_slot[j]) ||
|
|
!strcmp(tmp2, suffix_slot[j]))
|
|
count++;
|
|
}
|
|
|
|
/* Break out of loop if all slot index are found */
|
|
if (count == AB_SUPPORTED_SLOTS) {
|
|
part_dev->multislot_support = true;
|
|
boot_slot_index[0] = m;
|
|
boot_slot_index[1] = x;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (part_dev->multislot_support)
|
|
break;
|
|
}
|
|
|
|
return part_dev->multislot_support;
|
|
}
|
|
|
|
/**
|
|
* @brief Scan a partition name to find SLOT_A/SLOT_B suffix.
|
|
*
|
|
* @param part_dev
|
|
* @param name
|
|
* @return char*
|
|
*/
|
|
char *ptdev_scan_for_multislot_byname(partition_device_t *part_dev, char *name)
|
|
{
|
|
int x, j, count;
|
|
char *tmp1, *tmp2;
|
|
int partition_count;
|
|
struct partition_entry *partition_entries;
|
|
unsigned int len;
|
|
|
|
if ((!part_dev) || (!part_dev->partition_entries) || (!name)) {
|
|
PT_ERROR("Invalide partition dev\n");
|
|
return NULL;
|
|
}
|
|
|
|
partition_count = ptdev_get_partition_count(part_dev);
|
|
partition_entries = ptdev_get_partition_entries(part_dev);
|
|
|
|
len = strlen(name);
|
|
|
|
if (len < 3)
|
|
return NULL;
|
|
|
|
for (x = 0; x < partition_count; x++) {
|
|
tmp1 = name;
|
|
|
|
if (!strncmp((const char *)tmp1, (char *)partition_entries[x].name,
|
|
len - SUFFIX_LEN) &&
|
|
(len == strlen((char *)partition_entries[x].name))) {
|
|
tmp1 = tmp1 + len - SUFFIX_LEN;
|
|
tmp2 = (char *)(partition_entries[x].name + len - SUFFIX_LEN);
|
|
count = 0;
|
|
|
|
for (j = 0; j < AB_SUPPORTED_SLOTS; j++) {
|
|
if (!strcmp(tmp1, suffix_slot[j]) ||
|
|
!strcmp(tmp2, suffix_slot[j]))
|
|
count++;
|
|
}
|
|
|
|
if (count == AB_SUPPORTED_SLOTS) {
|
|
return tmp1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
Function: To reset partition attributes
|
|
This function reset partition_priority, retry_count
|
|
and clear successful and bootable bits.
|
|
*/
|
|
void ptdev_reset_attributes(partition_device_t *part_dev, unsigned index)
|
|
{
|
|
int partition_count = 0;
|
|
struct partition_entry *partition_entries;
|
|
|
|
if (NULL == part_dev) {
|
|
PT_ERROR("part_dev is NULL\n");
|
|
return;
|
|
}
|
|
|
|
partition_count = ptdev_get_partition_count(part_dev);
|
|
|
|
if (index >= partition_count) {
|
|
PT_ERROR("index is error\n");
|
|
return;
|
|
}
|
|
|
|
partition_entries = ptdev_get_partition_entries(part_dev);
|
|
partition_entries[index].attribute_flag = 0;
|
|
ptdev_attributes_update(part_dev);
|
|
}
|
|
|
|
void ptdev_reset_all_attributes(partition_device_t *part_dev)
|
|
{
|
|
int i;
|
|
int partition_count;
|
|
struct partition_entry *partition_entries;
|
|
|
|
if (NULL == part_dev) {
|
|
PT_ERROR("part_dev is NULL\n");
|
|
return;
|
|
}
|
|
|
|
partition_entries = ptdev_get_partition_entries(part_dev);
|
|
partition_count = ptdev_get_partition_count(part_dev);
|
|
|
|
for (i = 0; i < partition_count; i++) {
|
|
partition_entries[i].attribute_flag = 0;
|
|
}
|
|
|
|
part_dev->active_slot = INVALID;
|
|
ptdev_attributes_update(part_dev);
|
|
}
|
|
|
|
/**
|
|
* @brief Function: Switch active partitions.
|
|
* @param part_dev
|
|
* @param old_slot
|
|
* @param new_slot
|
|
*/
|
|
void ptdev_switch_slots(partition_device_t *part_dev, int old_slot,
|
|
int new_slot)
|
|
{
|
|
struct partition_entry *partition_entries;
|
|
int count;
|
|
char *pname;
|
|
char *suffix_str = NULL;
|
|
|
|
if ((!part_dev) || (!part_dev->partition_entries)) {
|
|
PT_ERROR("Invalide partition dev\n");
|
|
return;
|
|
}
|
|
|
|
if ((old_slot != SLOT_A) && (old_slot != SLOT_B)) {
|
|
PT_ERROR("ERROR: old_slot= %d wrong slot number\n", old_slot);
|
|
return;
|
|
}
|
|
|
|
if ((new_slot != SLOT_A) && (new_slot != SLOT_B)) {
|
|
PT_ERROR("ERROR: new_slot= %d wrong slot number\n", new_slot);
|
|
return;
|
|
}
|
|
|
|
if (new_slot == old_slot) {
|
|
return;
|
|
}
|
|
|
|
count = ptdev_get_partition_count(part_dev);
|
|
partition_entries = ptdev_get_partition_entries(part_dev);
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
pname = (char *)partition_entries[i].name;
|
|
|
|
/* Find partition, if it is A/B enabled */
|
|
suffix_str = ptdev_scan_for_multislot_byname(part_dev, pname);
|
|
|
|
if (suffix_str) {
|
|
/*old active slot */
|
|
if (!strcmp(suffix_str, SUFFIX_SLOT(old_slot))) {
|
|
/* Set Unbootable bit */
|
|
SET_BIT(partition_entries[i].attribute_flag,
|
|
PART_ATT_UNBOOTABLE_BIT);
|
|
/* Clear Sucess bit and Active bits */
|
|
CLR_BIT(partition_entries[i].attribute_flag,
|
|
PART_ATT_SUCCESS_BIT);
|
|
CLR_BIT(partition_entries[i].attribute_flag,
|
|
PART_ATT_ACTIVE_BIT);
|
|
|
|
/* Clear Max retry count and priority value */
|
|
partition_entries[i].attribute_flag &=
|
|
(~PART_ATT_PRIORITY_VAL & ~PART_ATT_MAX_RETRY_COUNT_VAL);
|
|
}
|
|
/*new active slot */
|
|
else if (!strcmp(suffix_str, SUFFIX_SLOT(new_slot))) {
|
|
/* CLR Unbootable bit*/
|
|
CLR_BIT(partition_entries[i].attribute_flag,
|
|
PART_ATT_UNBOOTABLE_BIT);
|
|
// CLR_BIT(partition_entries[i].attribute_flag,
|
|
// PART_ATT_SUCCESS_BIT);
|
|
|
|
/* Set Active bits */
|
|
SET_BIT(partition_entries[i].attribute_flag,
|
|
PART_ATT_ACTIVE_BIT);
|
|
|
|
/* Set Max retry count and priority value */
|
|
partition_entries[i].attribute_flag |=
|
|
(PART_ATT_PRIORITY_VAL | PART_ATT_MAX_RETRY_COUNT_VAL);
|
|
}
|
|
} else {
|
|
CLR_BIT(partition_entries[i].attribute_flag,
|
|
PART_ATT_UNBOOTABLE_BIT);
|
|
// CLR_BIT(partition_entries[i].attribute_flag,
|
|
// PART_ATT_SUCCESS_BIT);
|
|
SET_BIT(partition_entries[i].attribute_flag, PART_ATT_ACTIVE_BIT);
|
|
partition_entries[i].attribute_flag |=
|
|
(PART_ATT_PRIORITY_VAL | PART_ATT_MAX_RETRY_COUNT_VAL);
|
|
}
|
|
}
|
|
|
|
swap_guid(part_dev, new_slot);
|
|
part_dev->active_slot = new_slot;
|
|
part_dev->attributes_updated = true;
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief This function returns the most priority and active slot,
|
|
* also you need to update the global state seperately.
|
|
* @param part_dev
|
|
* @return int
|
|
*/
|
|
int ptdev_find_active_slot(partition_device_t *part_dev)
|
|
{
|
|
unsigned current_priority;
|
|
int i, count = 0;
|
|
bool current_active_bit;
|
|
int boot_priority;
|
|
struct partition_entry *partition_entries;
|
|
|
|
if ((!part_dev) || (!part_dev->partition_entries)) {
|
|
PT_ERROR("Invalide partition dev\n");
|
|
goto out;
|
|
}
|
|
|
|
partition_entries = ptdev_get_partition_entries(part_dev);
|
|
#ifdef AB_DEBUG
|
|
PT_DBG("ptdev_find_active_slot() called\n");
|
|
#endif
|
|
|
|
/* Return current active slot if already found */
|
|
if (part_dev->active_slot != INVALID)
|
|
goto out;
|
|
|
|
for (boot_priority = (MAX_PRIORITY - 1); boot_priority >= 0;
|
|
boot_priority--) {
|
|
/* Search valid boot slot with highest priority */
|
|
for (i = 0; i < AB_SUPPORTED_SLOTS; i++) {
|
|
current_priority =
|
|
slot_priority(partition_entries, boot_slot_index[i]);
|
|
current_active_bit =
|
|
slot_is_active(partition_entries, boot_slot_index[i]);
|
|
|
|
/* Count number of slots with all attributes as zero */
|
|
if (!current_priority && !current_active_bit) {
|
|
count++;
|
|
continue;
|
|
}
|
|
|
|
#ifdef AB_DEBUG
|
|
PT_DBG("Slot:Priority:Active:Bootable %s:%d:%d:%d \n",
|
|
partition_entries[boot_slot_index[i]].name, current_priority,
|
|
current_active_bit);
|
|
#endif
|
|
|
|
if (boot_priority == current_priority) {
|
|
if (current_active_bit) {
|
|
#ifdef AB_DEBUG
|
|
PT_DBG("Slot (%s) is Valid High Priority Slot\n",
|
|
SUFFIX_SLOT(i));
|
|
#endif
|
|
part_dev->active_slot = i;
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* All slots are zeroed, this is first bootup */
|
|
/* Marking and trying SLOT 0 as default */
|
|
if (count == AB_SUPPORTED_SLOTS) {
|
|
/* Update the priority of the boot slot */
|
|
ptdev_activate_slot(part_dev, SLOT_A);
|
|
|
|
part_dev->active_slot = SLOT_A;
|
|
|
|
/* This is required to mark all bits as active,
|
|
for fresh boot post fresh flash */
|
|
part_dev->attributes_updated = true;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
return part_dev->active_slot;
|
|
}
|
|
|
|
/**
|
|
* @brief ptdev_roll_back_check
|
|
*
|
|
* @param part_dev
|
|
*/
|
|
void ptdev_roll_back_check(partition_device_t *part_dev)
|
|
{
|
|
int active_slot = INVALID;
|
|
int other_slot;
|
|
uint64_t boot_retry_count = 0;
|
|
struct partition_entry *partition_entries;
|
|
|
|
if ((!part_dev) || (!part_dev->partition_entries)) {
|
|
PT_ERROR("Invalide partition dev!\n");
|
|
goto out;
|
|
}
|
|
|
|
active_slot = ptdev_find_active_slot(part_dev);
|
|
other_slot = get_inverse_slot(part_dev, active_slot);
|
|
|
|
if ((active_slot != SLOT_A) && (active_slot != SLOT_B)) {
|
|
PT_ERROR("an INVALID active_slot slot!\n");
|
|
goto check_update;
|
|
}
|
|
|
|
if ((other_slot != SLOT_A) && (other_slot != SLOT_B)) {
|
|
PT_ERROR("an INVALID inactive_slot slot!\n");
|
|
goto check_update;
|
|
}
|
|
|
|
partition_entries = ptdev_get_partition_entries(part_dev);
|
|
|
|
/*Do not need fallback, if a active slot's partition flag is set to
|
|
* sucessful and bootable*/
|
|
if (slot_is_bootable(partition_entries, boot_slot_index[active_slot]) &&
|
|
slot_is_sucessful(partition_entries, boot_slot_index[active_slot])) {
|
|
PT_ERROR("Rollback skip! Active slot%s is Successful and Bootable\n",
|
|
SUFFIX_SLOT(active_slot));
|
|
goto check_update;
|
|
}
|
|
|
|
/*Do not need fallback, if a inactive slot's partition flag is not set to
|
|
* sucessful and bootable */
|
|
if ((!slot_is_bootable(partition_entries, boot_slot_index[other_slot])) ||
|
|
(!slot_is_sucessful(partition_entries, boot_slot_index[other_slot]))) {
|
|
PT_ERROR("Rollback skip! Inactive slot%s is not Successful or not "
|
|
"Bootable\n",
|
|
SUFFIX_SLOT(other_slot));
|
|
goto check_update;
|
|
}
|
|
|
|
boot_retry_count =
|
|
slot_retry_count(partition_entries, boot_slot_index[active_slot]);
|
|
|
|
if ((boot_retry_count != 0) && (boot_retry_count < MAX_RETRY_COUNT)) {
|
|
/* Decrement retry count, boot from active slot */
|
|
PT_ERROR("Rollback decrement Retrycount from %llu to %llu, boot from "
|
|
"slot%s\n",
|
|
boot_retry_count, (boot_retry_count - 1),
|
|
SUFFIX_SLOT(active_slot));
|
|
decrease_slot_retrycnt(part_dev, active_slot);
|
|
} else {
|
|
PT_ERROR("Rollback from slot%s to slot%s!\n", SUFFIX_SLOT(active_slot),
|
|
SUFFIX_SLOT(other_slot));
|
|
ptdev_switch_slots(part_dev, active_slot, other_slot);
|
|
}
|
|
|
|
check_update:
|
|
|
|
if (part_dev->attributes_updated) {
|
|
ptdev_attributes_update(part_dev);
|
|
part_dev->attributes_updated = false;
|
|
}
|
|
|
|
out:
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief ptdev_find_boot_slot
|
|
Active + Bootable + Successful
|
|
Active + Bootable ---------------->check Retry_count>0
|
|
Bootable + Successful
|
|
Bootable
|
|
* @param part_dev
|
|
* @return int
|
|
*/
|
|
int ptdev_find_boot_slot(partition_device_t *part_dev)
|
|
{
|
|
int boot_slot = INVALID;
|
|
|
|
if (part_dev == NULL) {
|
|
PT_ERROR("Invalide partition dev!\n");
|
|
return INVALID;
|
|
}
|
|
|
|
boot_slot = ptdev_find_active_slot(part_dev);
|
|
|
|
if (boot_slot == INVALID) {
|
|
PT_ERROR("can't find a bootable slot!\n");
|
|
return INVALID;
|
|
}
|
|
|
|
return boot_slot;
|
|
}
|
|
|
|
/**
|
|
* @brief find the successful slot
|
|
* @param part_dev
|
|
* @return int
|
|
*/
|
|
int ptdev_find_successfull_slot(partition_device_t *part_dev)
|
|
{
|
|
int valid = INVALID, invalid = INVALID;
|
|
int slt_index;
|
|
struct partition_entry *partition_entries;
|
|
|
|
if (NULL == part_dev) {
|
|
PT_ERROR("part_dev is NULL\n");
|
|
return INVALID;
|
|
}
|
|
|
|
partition_entries = ptdev_get_partition_entries(part_dev);
|
|
|
|
if (!part_dev->multislot_support) {
|
|
PT_ERROR("can't find boot slot, multislot not support.\n");
|
|
return INVALID;
|
|
}
|
|
|
|
for (int i = 0; i < AB_SUPPORTED_SLOTS; i++) {
|
|
slt_index = boot_slot_index[i];
|
|
|
|
if (partition_entries[slt_index].attribute_flag &
|
|
PART_ATT_SUCCESSFUL_VAL) {
|
|
valid = i;
|
|
continue;
|
|
}
|
|
|
|
invalid = i;
|
|
}
|
|
|
|
if (invalid == INVALID) {
|
|
PT_DBG("both slots marked as successfull\n");
|
|
return AB_SUPPORTED_SLOTS;
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
/**
|
|
* @brief update guid for parition
|
|
*
|
|
* @param partition_entries
|
|
* @param old_index
|
|
* @param new_index
|
|
*/
|
|
static void guid_update(struct partition_entry *partition_entries,
|
|
unsigned old_index, unsigned new_index)
|
|
{
|
|
unsigned char tmp_guid[PARTITION_TYPE_GUID_SIZE];
|
|
|
|
#ifdef AB_DEBUG
|
|
PT_DBG("Swapping GUID (%s) --> (%s) \n", partition_entries[old_index].name,
|
|
partition_entries[new_index].name);
|
|
#endif
|
|
memcpy(tmp_guid, partition_entries[old_index].type_guid,
|
|
PARTITION_TYPE_GUID_SIZE);
|
|
memcpy(partition_entries[old_index].type_guid,
|
|
partition_entries[new_index].type_guid, PARTITION_TYPE_GUID_SIZE);
|
|
memcpy(partition_entries[new_index].type_guid, tmp_guid,
|
|
PARTITION_TYPE_GUID_SIZE);
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief Function to swap guids of slots
|
|
*
|
|
* @param part_dev
|
|
* @param new_slot
|
|
*/
|
|
static void swap_guid(partition_device_t *part_dev, int new_slot)
|
|
{
|
|
unsigned i, j, tmp_strlen;
|
|
unsigned partition_cnt;
|
|
struct partition_entry *partition_entries;
|
|
const char *ptr_pname, *ptr_suffix;
|
|
int old_slot;
|
|
|
|
if (NULL == part_dev) {
|
|
PT_ERROR("Invalide partition dev\n");
|
|
return;
|
|
}
|
|
|
|
if ((new_slot != SLOT_A) && (new_slot != SLOT_B)) {
|
|
PT_ERROR("ERROR: slot= %d wrong slot number\n", new_slot);
|
|
return;
|
|
}
|
|
|
|
if ((part_dev->active_slot != SLOT_B) &&
|
|
(part_dev->active_slot != SLOT_A)) {
|
|
PT_ERROR("first set Active, do not need swap guid\n");
|
|
return;
|
|
}
|
|
|
|
partition_cnt = ptdev_get_partition_count(part_dev);
|
|
partition_entries = ptdev_get_partition_entries(part_dev);
|
|
old_slot = part_dev->active_slot;
|
|
|
|
if (old_slot == new_slot)
|
|
return;
|
|
|
|
for (i = 0; i < partition_cnt; i++) {
|
|
ptr_pname = (const char *)partition_entries[i].name;
|
|
ptr_suffix =
|
|
ptdev_scan_for_multislot_byname(part_dev, (char *)ptr_pname);
|
|
|
|
if (ptr_suffix) {
|
|
/* Search for suffix in partition name */
|
|
if (!strcmp(ptr_suffix, SUFFIX_SLOT(new_slot))) {
|
|
for (j = i + 1; j < partition_cnt; j++) {
|
|
tmp_strlen =
|
|
strlen(ptr_pname) - strlen(SUFFIX_SLOT(new_slot));
|
|
|
|
if (!strncmp((const char *)partition_entries[j].name,
|
|
ptr_pname, tmp_strlen) &&
|
|
strstr((const char *)partition_entries[j].name,
|
|
SUFFIX_SLOT(old_slot)) &&
|
|
strlen(ptr_pname) ==
|
|
strlen((char *)partition_entries[j].name))
|
|
guid_update(partition_entries, j, i);
|
|
}
|
|
} else if (!strcmp(ptr_suffix, SUFFIX_SLOT(old_slot))) {
|
|
for (j = i + 1; j < partition_cnt; j++) {
|
|
tmp_strlen =
|
|
strlen(ptr_pname) - strlen(SUFFIX_SLOT(old_slot));
|
|
|
|
if (!strncmp((const char *)partition_entries[j].name,
|
|
ptr_pname, tmp_strlen) &&
|
|
strstr((const char *)partition_entries[j].name,
|
|
SUFFIX_SLOT(new_slot)) &&
|
|
strlen(ptr_pname) ==
|
|
strlen((char *)partition_entries[j].name))
|
|
guid_update(partition_entries, i, j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief To set active bit of all partitions of actve slot.
|
|
* also, unset active bits of all other slot
|
|
* @param part_dev
|
|
* @param slot
|
|
*/
|
|
void mark_all_partitions_active(partition_device_t *part_dev, unsigned slot)
|
|
{
|
|
int i;
|
|
char *pname = NULL;
|
|
char *suffix_str = NULL;
|
|
struct partition_entry *partition_entries =
|
|
ptdev_get_partition_entries(part_dev);
|
|
int partition_count = ptdev_get_partition_count(part_dev);
|
|
|
|
for (i = 0; i < partition_count; i++) {
|
|
pname = (char *)partition_entries[i].name;
|
|
#ifdef AB_DEBUG
|
|
PT_DBG("Transversing partition %s\n", pname);
|
|
#endif
|
|
|
|
/* Find partition, if it is A/B enabled */
|
|
suffix_str = ptdev_scan_for_multislot_byname(part_dev, pname);
|
|
|
|
if (suffix_str) {
|
|
if (!strcmp(suffix_str, SUFFIX_SLOT(slot)))
|
|
/* 2a. Mark matching partition as active. */
|
|
partition_entries[i].attribute_flag |= PART_ATT_ACTIVE_VAL;
|
|
else
|
|
/* 2b. Unset active bit for all other partitions. */
|
|
partition_entries[i].attribute_flag &= ~PART_ATT_ACTIVE_VAL;
|
|
} else {
|
|
/* 3. If it isn't A/B enabled, Mark partition as active as well */
|
|
partition_entries[i].attribute_flag |= PART_ATT_ACTIVE_VAL;
|
|
}
|
|
}
|
|
|
|
part_dev->attributes_updated = true;
|
|
}
|
|
|
|
/**
|
|
* @brief Mark the slot to be active and also conditionally
|
|
* update the slot parameters if there is a change.
|
|
* @param part_dev
|
|
* @param slot
|
|
*/
|
|
void ptdev_mark_active_slot(partition_device_t *part_dev, int slot)
|
|
{
|
|
if (!part_dev) {
|
|
PT_ERROR("Invalide partition dev\n");
|
|
goto out;
|
|
}
|
|
|
|
if (part_dev->active_slot == slot)
|
|
goto out;
|
|
|
|
if (slot != INVALID) {
|
|
PT_DBG("Marking (%s) as active\n", SUFFIX_SLOT(slot));
|
|
|
|
/* 1. Swap GUID's to new slot */
|
|
swap_guid(part_dev, slot);
|
|
|
|
/* 2. Set Active bit for all partitions of active slot */
|
|
mark_all_partitions_active(part_dev, slot);
|
|
}
|
|
|
|
part_dev->active_slot = slot;
|
|
out:
|
|
|
|
if (part_dev->attributes_updated) {
|
|
ptdev_attributes_update(part_dev);
|
|
part_dev->attributes_updated = false;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void mark_slot_attr(partition_device_t *part_dev, unsigned slot,
|
|
int attr)
|
|
{
|
|
int i;
|
|
char *pname = NULL;
|
|
char *suffix_str = NULL;
|
|
unsigned long long part_att_val;
|
|
struct partition_entry *partition_entries;
|
|
int partition_count;
|
|
|
|
if (NULL == part_dev) {
|
|
PT_ERROR("part_dev is NULL\n");
|
|
return;
|
|
}
|
|
|
|
if (attr >= ATTR_NUM) {
|
|
PT_ERROR("mark_slot_attr:bad part attribute\n");
|
|
return;
|
|
}
|
|
|
|
if ((slot != SLOT_A) && (slot != SLOT_B)) {
|
|
PT_ERROR("mark_slot_attr:slot =%d unknown\n", slot);
|
|
return;
|
|
}
|
|
|
|
partition_entries = ptdev_get_partition_entries(part_dev);
|
|
partition_count = ptdev_get_partition_count(part_dev);
|
|
|
|
part_att_val = att_val_array[attr];
|
|
|
|
for (i = 0; i < partition_count; i++) {
|
|
pname = (char *)partition_entries[i].name;
|
|
#ifdef AB_DEBUG
|
|
PT_DBG("Transversing partition %s\n", pname);
|
|
#endif
|
|
|
|
/* Find partition, if it is A/B enabled */
|
|
suffix_str = ptdev_scan_for_multislot_byname(part_dev, pname);
|
|
|
|
if (suffix_str) {
|
|
if (!strcmp(suffix_str, SUFFIX_SLOT(slot))) {
|
|
/* 2. Mark matching partition as active. */
|
|
partition_entries[i].attribute_flag |= part_att_val;
|
|
}
|
|
} else if (attr == ATTR_SUCCESSFUL) {
|
|
/* 3. Allow marking none A/B partition ATTR_SUCCESSFUL attr*/
|
|
partition_entries[i].attribute_flag |= part_att_val;
|
|
}
|
|
}
|
|
|
|
part_dev->attributes_updated = true;
|
|
}
|
|
|
|
/*
|
|
Function: To mark bit of all partitions of specific slot.
|
|
*/
|
|
static void clean_slot_attr(partition_device_t *part_dev, unsigned slot,
|
|
int attr)
|
|
{
|
|
int i;
|
|
char *pname = NULL;
|
|
char *suffix_str = NULL;
|
|
unsigned long long part_att_val;
|
|
struct partition_entry *partition_entries;
|
|
int partition_count;
|
|
|
|
if (NULL == part_dev) {
|
|
PT_ERROR("part_dev is NULL\n");
|
|
return;
|
|
}
|
|
|
|
if (attr >= ATTR_NUM) {
|
|
PT_ERROR("clean_slot_attr:bad part attribute\n");
|
|
return;
|
|
}
|
|
|
|
if ((slot != SLOT_A) && (slot != SLOT_B)) {
|
|
PT_ERROR("clean_slot_attr:slot =%d unknown\n", slot);
|
|
return;
|
|
}
|
|
|
|
partition_entries = ptdev_get_partition_entries(part_dev);
|
|
partition_count = ptdev_get_partition_count(part_dev);
|
|
part_att_val = att_val_array[attr];
|
|
|
|
for (i = 0; i < partition_count; i++) {
|
|
pname = (char *)partition_entries[i].name;
|
|
#ifdef AB_DEBUG
|
|
PT_DBG("Transversing partition %s\n", pname);
|
|
#endif
|
|
|
|
/* Find partition, if it is A/B enabled */
|
|
suffix_str = ptdev_scan_for_multislot_byname(part_dev, pname);
|
|
|
|
if (suffix_str) {
|
|
if (!strcmp(suffix_str, SUFFIX_SLOT(slot))) {
|
|
/* Clean matching partition. */
|
|
partition_entries[i].attribute_flag &= ~part_att_val;
|
|
}
|
|
}
|
|
}
|
|
|
|
part_dev->attributes_updated = true;
|
|
}
|
|
void ptdev_mark_slot_attr_noupdate(partition_device_t *part_dev, unsigned slot,
|
|
int attr)
|
|
{
|
|
unsigned inverse_slot;
|
|
|
|
if (NULL == part_dev) {
|
|
PT_ERROR("part_dev is NULL\n");
|
|
return;
|
|
}
|
|
|
|
if ((slot != SLOT_A) && (slot != SLOT_B)) {
|
|
return;
|
|
}
|
|
|
|
if (attr >= ATTR_NUM) {
|
|
PT_ERROR("bad part attribute\n");
|
|
return;
|
|
}
|
|
|
|
if (attr == ATTR_ACTIVE) {
|
|
/* do not need mark */
|
|
if (part_dev->active_slot == slot) {
|
|
return;
|
|
}
|
|
|
|
inverse_slot = get_inverse_slot(part_dev, slot);
|
|
/* 1. Swap GUID's to new slot */
|
|
swap_guid(part_dev, slot);
|
|
/* 2. Set slot as Active */
|
|
mark_slot_attr(part_dev, slot, attr);
|
|
/* 3. Set inverse_slot as Inactive */
|
|
clean_slot_attr(part_dev, inverse_slot, attr);
|
|
part_dev->active_slot = slot;
|
|
} else {
|
|
/* Set attribute for all partitions */
|
|
mark_slot_attr(part_dev, slot, attr);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Function: To mark bit of all partitions of specific slot.
|
|
*/
|
|
void ptdev_clean_slot_attr_noupdate(partition_device_t *part_dev, unsigned slot,
|
|
int attr)
|
|
{
|
|
unsigned inverse_slot;
|
|
|
|
if (NULL == part_dev) {
|
|
PT_ERROR("part_dev is NULL\n");
|
|
return;
|
|
}
|
|
|
|
if ((slot != SLOT_A) && (slot != SLOT_B)) {
|
|
return;
|
|
}
|
|
|
|
if (attr >= ATTR_NUM) {
|
|
PT_ERROR("bad part attribute\n");
|
|
return;
|
|
}
|
|
|
|
if (attr == ATTR_ACTIVE) {
|
|
inverse_slot = get_inverse_slot(part_dev, slot);
|
|
|
|
/* do not need mark */
|
|
if ((part_dev->active_slot != slot) &&
|
|
(part_dev->active_slot == inverse_slot)) {
|
|
return;
|
|
}
|
|
|
|
/* 1. Swap GUID's to new slot */
|
|
swap_guid(part_dev, inverse_slot);
|
|
/* 2. slot mark Active */
|
|
clean_slot_attr(part_dev, slot, attr);
|
|
/* 3. inverse_slot mark Inactive */
|
|
mark_slot_attr(part_dev, inverse_slot, attr);
|
|
part_dev->active_slot = inverse_slot;
|
|
} else {
|
|
/* Set attribute for all partitions */
|
|
clean_slot_attr(part_dev, slot, attr);
|
|
}
|
|
}
|
|
/*
|
|
Function: Mark the slot attribute and update to the
|
|
storage device if there is a change.
|
|
*/
|
|
void ptdev_mark_slot_attr(partition_device_t *part_dev, int slot, int attr)
|
|
{
|
|
if (NULL == part_dev) {
|
|
PT_ERROR("part_dev is NULL\n");
|
|
return;
|
|
}
|
|
|
|
if ((slot != SLOT_A) && (slot != SLOT_B)) {
|
|
return;
|
|
}
|
|
|
|
if (attr > ATTR_NUM) {
|
|
PT_ERROR("bad part attribute\n");
|
|
return;
|
|
}
|
|
|
|
ptdev_mark_slot_attr_noupdate(part_dev, slot, attr);
|
|
|
|
if (part_dev->attributes_updated) {
|
|
ptdev_attributes_update(part_dev);
|
|
part_dev->attributes_updated = false;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
Function: Clean the slot attribute and update to the
|
|
storage device if there is a change.
|
|
*/
|
|
void ptdev_clean_slot_attr(partition_device_t *part_dev, int slot, int attr)
|
|
{
|
|
if (NULL == part_dev) {
|
|
PT_ERROR("part_dev is NULL\n");
|
|
return;
|
|
}
|
|
|
|
if ((slot != SLOT_A) && (slot != SLOT_B)) {
|
|
return;
|
|
}
|
|
|
|
if (attr > ATTR_NUM) {
|
|
PT_ERROR("bad part attribute\n");
|
|
return;
|
|
}
|
|
|
|
ptdev_clean_slot_attr_noupdate(part_dev, slot, attr);
|
|
|
|
if (part_dev->attributes_updated) {
|
|
ptdev_attributes_update(part_dev);
|
|
part_dev->attributes_updated = false;
|
|
}
|
|
|
|
return;
|
|
}
|
|
/**
|
|
* @brief Function to find if multislot is supported
|
|
* @param part_dev
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
bool ptdev_multislot_is_supported(partition_device_t *part_dev)
|
|
{
|
|
if (!part_dev) {
|
|
PT_ERROR("Invalide partition dev\n");
|
|
return false;
|
|
}
|
|
|
|
return part_dev->multislot_support;
|
|
}
|
|
|
|
/**
|
|
* @brief Fill partition meta used for fastboot get var info publication.
|
|
Input partition_entries, partition_count and
|
|
buffer to fill information.
|
|
* @param part_dev
|
|
* @param has_slot_pname
|
|
* @param has_slot_reply
|
|
* @param array_size
|
|
* @return int
|
|
*/
|
|
int ptdev_fill_partition_meta(partition_device_t *part_dev,
|
|
char has_slot_pname[][MAX_GET_VAR_NAME_SIZE],
|
|
char has_slot_reply[][MAX_RSP_SIZE],
|
|
int array_size)
|
|
{
|
|
int i, tmp;
|
|
int count = 0;
|
|
char *pname = NULL;
|
|
int pname_size;
|
|
struct partition_entry *partition_entries;
|
|
int partition_count;
|
|
char *suffix_str;
|
|
|
|
if ((!part_dev) || (!part_dev->partition_entries)) {
|
|
PT_ERROR("Invalide partition dev\n");
|
|
return 0;
|
|
}
|
|
|
|
partition_entries = ptdev_get_partition_entries(part_dev);
|
|
partition_count = ptdev_get_partition_count(part_dev);
|
|
|
|
for (i = 0; i < partition_count; i++) {
|
|
pname = (char *)partition_entries[i].name;
|
|
pname_size = strlen(pname);
|
|
suffix_str = NULL;
|
|
#ifdef AB_DEBUG
|
|
PT_DBG("Transversing partition %s\n", pname);
|
|
#endif
|
|
|
|
/* Find partition, if it is A/B enabled */
|
|
suffix_str = ptdev_scan_for_multislot_byname(part_dev, pname);
|
|
|
|
if (suffix_str) {
|
|
if (!strcmp(suffix_str, SUFFIX_SLOT(SLOT_A))) {
|
|
/* 2. put the partition name in array */
|
|
tmp = pname_size - strlen(suffix_str);
|
|
strncpy(has_slot_pname[count], pname, tmp + 1);
|
|
strncpy(has_slot_reply[count], " Yes", MAX_RSP_SIZE);
|
|
count++;
|
|
}
|
|
} else {
|
|
strncpy(has_slot_pname[count], pname, MAX_GET_VAR_NAME_SIZE);
|
|
strncpy(has_slot_reply[count], " No", MAX_RSP_SIZE);
|
|
count++;
|
|
}
|
|
|
|
/* Avoid over population of array provided */
|
|
if (count >= array_size) {
|
|
PT_ERROR("ERROR: Not able to parse all partitions\n");
|
|
return count;
|
|
}
|
|
}
|
|
|
|
#ifdef AB_DEBUG
|
|
|
|
for (i = 0; i < count; i++)
|
|
PT_DBG("has-slot:%s:%s\n", has_slot_pname[i], has_slot_reply[i]);
|
|
|
|
#endif
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* @brief Fill the slot meta used for fastboot get var info publication.
|
|
* @param part_dev
|
|
* @param slot_info
|
|
*/
|
|
void ptdev_fill_slot_meta(partition_device_t *part_dev,
|
|
struct ab_slot_info *slot_info)
|
|
{
|
|
int i, current_slot_index;
|
|
struct partition_entry *ptn_entries;
|
|
char buff[3];
|
|
|
|
if ((!part_dev) || (!part_dev->partition_entries)) {
|
|
PT_ERROR("Invalide partition dev\n");
|
|
return;
|
|
}
|
|
|
|
if (NULL == slot_info) {
|
|
PT_ERROR("slot_info is NULL\n");
|
|
return;
|
|
}
|
|
|
|
ptn_entries = ptdev_get_partition_entries(part_dev);
|
|
|
|
/* Update slot info */
|
|
for (i = 0; i < AB_SUPPORTED_SLOTS; i++) {
|
|
current_slot_index = boot_slot_index[i];
|
|
strncpy(slot_info[i].slot_is_unbootable_rsp,
|
|
slot_is_bootable(ptn_entries, current_slot_index) ? "No"
|
|
: "Yes",
|
|
MAX_RSP_SIZE);
|
|
strncpy(slot_info[i].slot_is_active_rsp,
|
|
slot_is_active(ptn_entries, current_slot_index) ? "Yes" : "No",
|
|
MAX_RSP_SIZE);
|
|
strncpy(slot_info[i].slot_is_succesful_rsp,
|
|
slot_is_sucessful(ptn_entries, current_slot_index) ? "Yes"
|
|
: "No",
|
|
MAX_RSP_SIZE);
|
|
itoa_s(slot_retry_count(ptn_entries, current_slot_index),
|
|
(unsigned char *)buff, 2, 10);
|
|
strncpy(slot_info[i].slot_retry_count_rsp, buff, MAX_RSP_SIZE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Read and update the attributes of GPT
|
|
*
|
|
* @param part_dev
|
|
* @param gpt_start_addr
|
|
* @param gpt_hdr_offset
|
|
* @param gpt_entries_offset
|
|
* @return int
|
|
*/
|
|
static int update_gpt(partition_device_t *part_dev, uint64_t gpt_start_addr,
|
|
uint64_t gpt_hdr_offset, uint64_t gpt_entries_offset)
|
|
{
|
|
uint8_t *buffer = NULL;
|
|
uint8_t *gpt_entries_ptr, *gpt_hdr_ptr, *tmp = NULL;
|
|
struct partition_entry *partition_entries;
|
|
uint32_t partition_count;
|
|
unsigned max_partition_count = 0;
|
|
unsigned partition_entry_size = 0;
|
|
uint32_t block_size;
|
|
uint32_t erase_grp_sz;
|
|
uint32_t crc_val = 0;
|
|
int ret = 0;
|
|
uint64_t offset;
|
|
uint64_t max_gpt_size_bytes;
|
|
uint64_t mbr_size_bytes;
|
|
|
|
if (NULL == part_dev) {
|
|
PT_ERROR("Invalide partition dev\n");
|
|
goto out;
|
|
}
|
|
|
|
partition_entries = ptdev_get_partition_entries(part_dev);
|
|
partition_count = ptdev_get_partition_count(part_dev);
|
|
|
|
block_size = pt_disk_get_blocksize(part_dev->diskdev);
|
|
erase_grp_sz = pt_disk_get_erasesize(part_dev->diskdev);
|
|
|
|
if (!block_size || !erase_grp_sz) {
|
|
PT_ERROR("get block size %d or rase_grp_sz %derror\n", block_size,
|
|
erase_grp_sz);
|
|
goto out;
|
|
}
|
|
|
|
offset = part_dev->gpt_offset;
|
|
max_gpt_size_bytes = (PARTITION_ENTRY_SIZE * NUM_PARTITIONS +
|
|
GPT_HEADER_BLOCKS * block_size);
|
|
mbr_size_bytes = MBR_BLOCKS * block_size;
|
|
|
|
if (gpt_hdr_offset) {
|
|
/* Primary GPT shall with offset, no necessery for Secondary GPT */
|
|
offset = 0;
|
|
mbr_size_bytes = 0;
|
|
}
|
|
|
|
/* buffer size = mbr size + max_gpt_size_bytes*/
|
|
buffer = (uint8_t *)pvPortMallocAligned(
|
|
ROUNDUP(max_gpt_size_bytes + mbr_size_bytes, block_size), block_size);
|
|
|
|
if (!buffer) {
|
|
PT_ERROR("update_gpt: Failed at memory allocation\n");
|
|
goto out;
|
|
}
|
|
|
|
ret = pt_disk_read(part_dev->diskdev,
|
|
gpt_start_addr + offset - mbr_size_bytes,
|
|
(uint8_t *)buffer, max_gpt_size_bytes + mbr_size_bytes);
|
|
|
|
if (ret) {
|
|
PT_ERROR("Failed to read GPT\n");
|
|
goto out;
|
|
}
|
|
|
|
/* 0. Intialise ptrs for header and entries */
|
|
gpt_entries_ptr = buffer + mbr_size_bytes + gpt_entries_offset * block_size;
|
|
gpt_hdr_ptr = buffer + mbr_size_bytes + gpt_hdr_offset * block_size;
|
|
|
|
/* Update attributes_flag of partition entry */
|
|
tmp = gpt_entries_ptr;
|
|
|
|
for (unsigned i = 0; i < partition_count; i++) {
|
|
/* Update the partition attributes */
|
|
PUT_LONG_LONG(&tmp[ATTRIBUTE_FLAG_OFFSET],
|
|
partition_entries[i].attribute_flag);
|
|
memscpy(tmp, PARTITION_TYPE_GUID_SIZE, partition_entries[i].type_guid,
|
|
PARTITION_TYPE_GUID_SIZE);
|
|
|
|
/* point to the next partition entry */
|
|
tmp += PARTITION_ENTRY_SIZE;
|
|
}
|
|
|
|
/* Calculate and update CRC of partition entries array */
|
|
max_partition_count =
|
|
GET_LWORD_FROM_BYTE(&gpt_hdr_ptr[PARTITION_COUNT_OFFSET]);
|
|
partition_entry_size =
|
|
GET_LWORD_FROM_BYTE(&gpt_hdr_ptr[PENTRY_SIZE_OFFSET]);
|
|
|
|
/* Check for partition entry size */
|
|
if (partition_entry_size != PARTITION_ENTRY_SIZE) {
|
|
PT_ERROR("Invalid parition entry size\n");
|
|
goto out;
|
|
}
|
|
|
|
/* Check for maximum partition size */
|
|
if ((max_partition_count) >
|
|
(MIN_PARTITION_ARRAY_SIZE / (partition_entry_size))) {
|
|
PT_ERROR("Invalid maximum partition count\n");
|
|
goto out;
|
|
}
|
|
|
|
crc_val = crc32(0U, gpt_entries_ptr,
|
|
((max_partition_count) * (partition_entry_size)));
|
|
PUT_LONG(&gpt_hdr_ptr[PARTITION_CRC_OFFSET], crc_val);
|
|
|
|
/* Write CRC to 0 before we calculate the crc of the GPT header */
|
|
crc_val = 0;
|
|
PUT_LONG(&gpt_hdr_ptr[HEADER_CRC_OFFSET], crc_val);
|
|
crc_val = crc32(0U, gpt_hdr_ptr, GPT_HEADER_SIZE);
|
|
PUT_LONG(&gpt_hdr_ptr[HEADER_CRC_OFFSET], crc_val);
|
|
|
|
if (strstr(part_dev->diskdev->info->disk_name, "flash")) {
|
|
if (pt_disk_erase(
|
|
part_dev->diskdev,
|
|
round_down(gpt_start_addr + offset - mbr_size_bytes,
|
|
erase_grp_sz),
|
|
round_up(max_gpt_size_bytes + mbr_size_bytes, erase_grp_sz))) {
|
|
PT_ERROR("Failed to erase gpt\n");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
ret = pt_disk_write(part_dev->diskdev,
|
|
gpt_start_addr + offset - mbr_size_bytes,
|
|
(uint8_t *)buffer, max_gpt_size_bytes + mbr_size_bytes);
|
|
|
|
if (ret) {
|
|
PT_ERROR("Failed to write gpt\n");
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
|
|
if (buffer)
|
|
vPortFree(buffer);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Update the backup and primary guid parition table
|
|
* @param part_dev
|
|
*/
|
|
int ptdev_attributes_update(partition_device_t *part_dev)
|
|
{
|
|
uint64_t offset;
|
|
uint64_t gpt_entries_offset, gpt_hdr_offset;
|
|
uint64_t gpt_start_addr;
|
|
int ret;
|
|
uint32_t block_size;
|
|
unsigned max_entries_size_bytes;
|
|
unsigned max_entries_blocks;
|
|
unsigned max_gpt_blocks;
|
|
uint64_t device_capacity;
|
|
|
|
if (NULL == part_dev) {
|
|
PT_ERROR("Invalide partition dev\n");
|
|
return -1;
|
|
}
|
|
|
|
block_size = pt_disk_get_blocksize(part_dev->diskdev);
|
|
|
|
if (!block_size) {
|
|
PT_ERROR("get block size = %d error\n", block_size);
|
|
return -1;
|
|
}
|
|
|
|
device_capacity = pt_disk_get_capacity(part_dev->diskdev);
|
|
|
|
if (!device_capacity) {
|
|
PT_ERROR("get capacity %lld error\n", device_capacity);
|
|
return -1;
|
|
}
|
|
|
|
max_entries_size_bytes = PARTITION_ENTRY_SIZE * NUM_PARTITIONS;
|
|
max_entries_blocks = max_entries_size_bytes / block_size;
|
|
max_gpt_blocks = GPT_HEADER_BLOCKS + max_entries_blocks;
|
|
|
|
/* Update Primary GPT */
|
|
offset = MBR_BLOCKS; /* offset is 0x1 for primary GPT */
|
|
gpt_start_addr = offset * block_size;
|
|
/* Take gpt_start_addr as start and calculate offset from that in block sz*/
|
|
gpt_hdr_offset = 0; /* For primary partition offset is zero */
|
|
gpt_entries_offset = GPT_HEADER_BLOCKS;
|
|
|
|
ret = update_gpt(part_dev, gpt_start_addr, gpt_hdr_offset,
|
|
gpt_entries_offset);
|
|
|
|
if (ret) {
|
|
PT_ERROR("Failed to update Primary GPT\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Update Secondary GPT */
|
|
offset = ((device_capacity / block_size) - max_gpt_blocks);
|
|
gpt_start_addr = offset * block_size;
|
|
gpt_hdr_offset = max_entries_blocks;
|
|
gpt_entries_offset = 0; /* For secondary GPT entries offset is zero */
|
|
|
|
ret = update_gpt(part_dev, gpt_start_addr, gpt_hdr_offset,
|
|
gpt_entries_offset);
|
|
|
|
if (ret) {
|
|
PT_ERROR("Failed to update Secondary GPT\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|