Files
test/middleware/partition/ab_partition_parser.c
2025-11-07 20:19:23 +08:00

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;
}