Files
E3/e3_176_ref-补丁/middleware/partition/partition_parser.c
2026-01-09 16:48:24 +08:00

2883 lines
86 KiB
C

/**
* @file partition_parse.c
*
* Copyright (c) 2021 Semidrive Semiconductor.
* All rights reserved.
*
* Description:
*
* Revision History:
* -----------------
*/
#include <ab_partition_parser.h>
#include <assert.h>
#include <compiler.h>
#include <crc32.h>
#include <debug.h>
#include <partition_parser.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <types.h>
#define SKIP_MBR 1
/* Semidrive sub-partition support GUID */
/* type_guid: 7d06a189-1fef-4d89-8940-e8c2a6832ecf */
static const char sub_part_guid[PARTITION_TYPE_GUID_SIZE] = {
0x89, 0xa1, 0x6, 0x7d, 0xef, 0x1f, 0x89, 0x4d,
0x89, 0x40, 0xe8, 0xc2, 0xa6, 0x83, 0x2e, 0xcf};
static uint32_t ptdev_read_gpt(partition_device_t *part_dev,
struct partition_entry *parent_entry,
uint32_t block_size);
static uint32_t ptdev_read_mbr(partition_device_t *part_dev,
uint32_t block_size);
static void mbr_fill_name(struct partition_entry *partition_ent, uint32_t type);
static uint32_t ptdev_verify_mbr_signature(uint32_t size, uint8_t *buffer);
static uint32_t mbr_partition_get_type(uint32_t size, uint8_t *partition,
uint8_t *partition_type);
static uint32_t ptdev_get_type(uint32_t size, uint8_t *partition,
uint32_t *partition_type);
static uint32_t ptdev_parse_gpt_header(
partition_device_t *part_dev, uint8_t *buffer, uint64_t *first_usable_lba,
uint32_t *partition_entry_size, uint32_t *header_size,
uint32_t *max_partition_count, struct partition_entry *parent_entry,
bool secondary_gpt, uint64_t *partition_entries_offset,
uint32_t *crc_entries_array, bool check_lba);
static uint32_t write_mbr(partition_device_t *part_dev, uint32_t,
uint8_t *mbrImage, uint32_t block_size);
static uint32_t write_gpt(partition_device_t *part_dev,
struct partition_entry *parent_entry, uint32_t size,
uint8_t *gptImage, uint32_t block_size,
bool last_part_extend);
static uint32_t parse_gpt(uint8_t *buf, uint32_t buf_len, uint32_t block_size,
GPT_header *gpt_header, bool is_secondary_gpt);
static const char *partition_separator = "$";
static bool ptdev_sub_part_exist(struct partition_entry *partition_entries);
/**
* @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 Get Partition Count
*
* @param part_dev
* @return unsigned Partition Count
*/
unsigned ptdev_get_partition_count(partition_device_t *part_dev)
{
if (!part_dev) {
PT_ERROR("Invalid partition dev\n");
return 0;
}
return part_dev->count;
}
/**
* @brief Get the partition_entry
*
* @param part_dev
* @return struct partition_entry*
*/
struct partition_entry *
ptdev_get_partition_entries(partition_device_t *part_dev)
{
if (!part_dev) {
PT_ERROR("Invalid partition dev\n");
return NULL;
}
return part_dev->partition_entries;
}
/**
* @brief read the partiton table and get the parititon count
*
* @param part_dev
* @return uint32_t parititon count
*/
static uint32_t ptdev_get_entries_count(partition_device_t *part_dev)
{
uint32_t count = 0;
uint32_t block_size = pt_disk_get_blocksize(part_dev->diskdev);
PT_DBG("GET COUNT!\n");
if (!block_size) {
PT_ERROR("get block size = %d error\n", block_size);
return 0;
}
#if SKIP_MBR
part_dev->gpt_partitions_exist = true;
#else
if (ptdev_read_mbr(part_dev, block_size)) {
PT_ERROR("Boot: MBR read failed!\n");
return 0;
}
#endif
/* Read GPT of the card if exist */
if (part_dev->gpt_partitions_exist) {
if (ptdev_read_gpt(part_dev, NULL, block_size)) {
PT_ERROR("read gpt fail!\n");
goto out;
}
}
count = part_dev->count;
out:
part_dev->count = 0;
part_dev->gpt_partitions_exist = false;
return count;
}
/**
* @brief Read the storage device, fill the partition entries array, scan and
* find active slot Invoke after ptdev_set_abslot_decider, or else take for no
* a/b slot support
*
* @param part_dev
* @return int 0 is success other is error
*/
int ptdev_read_table(partition_device_t *part_dev)
{
unsigned int ret;
uint32_t block_size;
uint32_t partition_count = 0;
struct partition_entry *partition_entries;
PT_DBG("READ TABLE!\n");
if (!part_dev) {
PT_ERROR("Invalide partition\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;
}
if (part_dev->partition_entries) {
vPortFree(part_dev->partition_entries);
}
/* Before getting partition count, set count to 0 and set entries pointer to
* NULL */
part_dev->count = 0;
part_dev->partition_entries = NULL;
partition_count = ptdev_get_entries_count(part_dev);
if (!partition_count) {
PT_ERROR("get partition count fail!\n");
return 1;
}
partition_count =
(partition_count > NUM_PARTITIONS) ? NUM_PARTITIONS : partition_count;
part_dev->partition_entries = (struct partition_entry *)pvPortMalloc(
partition_count * sizeof(struct partition_entry));
ASSERT(part_dev->partition_entries);
memset(part_dev->partition_entries, 0,
partition_count * sizeof(struct partition_entry));
#if SKIP_MBR
part_dev->gpt_partitions_exist = true;
#else
/* Read MBR of the card */
ret = ptdev_read_mbr(part_dev, block_size);
if (ret) {
PT_ERROR("Boot: MBR read failed!\n");
return 1;
}
#endif
/* Read GPT of the card if exist */
if (part_dev->gpt_partitions_exist) {
ret = ptdev_read_gpt(part_dev, NULL, block_size);
if (ret) {
PT_ERROR("Boot: GPT read failed!\n");
return 1;
}
}
partition_entries = part_dev->partition_entries;
partition_count = part_dev->count;
for (unsigned i = 0; i < partition_count; i++) {
if (ptdev_sub_part_exist(partition_entries + i)) {
PT_DBG("%s may have sub partitions\n", partition_entries[i].name);
if (ptdev_read_gpt(part_dev, &partition_entries[i], block_size)) {
PT_ERROR("read extral partition table failed\n");
}
}
}
/* Scan of multislot support */
ptdev_scan_for_multislot(part_dev);
return 0;
}
/**
* @brief Read MBR and fill partition table.
* @param part_dev
* @param block_size
* @return unsigned int 0 is success other is error
*/
static uint32_t ptdev_read_mbr(partition_device_t *part_dev,
uint32_t block_size)
{
uint8_t *buffer = NULL;
unsigned int dtype;
unsigned int dfirstsec;
unsigned int EBR_first_sec;
unsigned int EBR_current_sec;
int ret = 0;
int idx, i;
unsigned partition_count = part_dev->count;
struct partition_entry *partition_entries = part_dev->partition_entries;
struct partition_entry *current_entry;
struct partition_entry pt_entry;
buffer = (uint8_t *)pvPortMallocAligned(ROUNDUP(block_size, block_size),
block_size);
if (!buffer) {
PT_ERROR("Error allocating memory while reading partition table\n");
ret = -1;
goto end;
}
/* Print out the MBR first */
ret = pt_disk_read(part_dev->diskdev, part_dev->gpt_offset,
(uint8_t *)buffer, block_size);
if (ret) {
PT_ERROR("Could not read partition from boot device\n");
goto end;
}
/* Check to see if signature exists */
ret = ptdev_verify_mbr_signature(block_size, buffer);
if (ret) {
goto end;
}
/*
* Process each of the four partitions in the MBR by reading the table
* information into our mbr table.
*/
idx = TABLE_ENTRY_0;
for (i = 0; i < 4; i++) {
/* Type 0xEE indicates end of MBR and GPT partitions exist */
dtype = buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_TYPE];
if (dtype == MBR_PROTECTED_TYPE) {
part_dev->gpt_partitions_exist = true;
goto end;
}
current_entry =
partition_entries ? &partition_entries[partition_count] : &pt_entry;
current_entry->dtype = dtype;
current_entry->attribute_flag =
buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_STATUS];
current_entry->first_lba = GET_LWORD_FROM_BYTE(
&buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_FIRST_SEC]);
current_entry->size = GET_LWORD_FROM_BYTE(
&buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_SIZE]);
dfirstsec = current_entry->first_lba;
mbr_fill_name(&partition_entries[partition_count],
current_entry->dtype);
partition_count++;
if (partition_count == NUM_PARTITIONS)
goto end;
}
part_dev->count = partition_count;
/* See if the last partition is EBR, if not, parsing is done */
if (dtype != MBR_EBR_TYPE) {
goto end;
}
EBR_first_sec = dfirstsec;
EBR_current_sec = dfirstsec;
ret = pt_disk_read(part_dev->diskdev,
(EBR_first_sec * block_size) + part_dev->gpt_offset,
(uint8_t *)buffer, block_size);
if (ret)
goto end;
/* Loop to parse the EBR */
for (i = 0;; i++) {
ret = ptdev_verify_mbr_signature(block_size, buffer);
if (ret) {
ret = 0;
break;
}
current_entry =
partition_entries ? &partition_entries[partition_count] : &pt_entry;
current_entry->attribute_flag = buffer[TABLE_ENTRY_0 + OFFSET_STATUS];
current_entry->dtype = buffer[TABLE_ENTRY_0 + OFFSET_TYPE];
current_entry->first_lba =
GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + OFFSET_FIRST_SEC]) +
EBR_current_sec;
current_entry->size =
GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + OFFSET_SIZE]);
mbr_fill_name(&(partition_entries[partition_count]),
current_entry->dtype);
partition_count++;
if (partition_count == NUM_PARTITIONS)
goto end;
dfirstsec =
GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_1 + OFFSET_FIRST_SEC]);
if (dfirstsec == 0) {
/* Getting to the end of the EBR tables */
break;
}
/* More EBR to follow - read in the next EBR sector */
PT_DBG("Reading EBR block from 0x%X\n", EBR_first_sec + dfirstsec);
ret = pt_disk_read(part_dev->diskdev,
((EBR_first_sec + dfirstsec) * block_size) +
part_dev->gpt_offset,
(uint8_t *)buffer, block_size);
if (ret)
goto end;
EBR_current_sec = EBR_first_sec + dfirstsec;
}
end:
if (buffer)
vPortFree(buffer);
return ret;
}
/**
* @brief restore the parition table
* @param part_dev
* @param dir
* @param parent_entry
* @return uint32_t 0 is success other is error
*/
static uint32_t restore_gpt(partition_device_t *part_dev,
enum restore_direction dir,
struct partition_entry *parent_entry)
{
uint32_t ret = 0;
uint64_t device_capacity;
uint8_t *header_buf = NULL;
uint8_t *entries_buf = NULL;
uint8_t *mbr_buf = NULL;
uint32_t block_size, gpt_size, erase_grp_sz, crc32_val, max_partition_count,
crc_entries;
uint32_t entries_cnt_per_block, blocks_to_read, header_size,
partition_entry_size;
uint64_t first_usable_lba, backup_header_lba, current_header_lba,
entries_start_lba, cur_gpt_offset;
uint64_t card_size_sec, ptn_src_header, ptn_dst_header, ptn_erase,
ptn_dst_entries, partition_entries_offset;
if (!part_dev) {
PT_ERROR("partition device error!\n");
goto end;
}
block_size = pt_disk_get_blocksize(part_dev->diskdev);
if (!block_size) {
PT_ERROR("get block size =%d error\n", block_size);
ret = 1;
goto end;
}
erase_grp_sz = pt_disk_get_erasesize(part_dev->diskdev);
if (!erase_grp_sz) {
PT_ERROR("get erase size =%d error\n", erase_grp_sz);
ret = 1;
goto end;
}
if (!parent_entry) {
device_capacity = pt_disk_get_capacity(part_dev->diskdev);
if (device_capacity < part_dev->gpt_offset) {
PT_ERROR("get capacity %lld error\n", device_capacity);
ret = 1;
goto end;
}
device_capacity = device_capacity - part_dev->gpt_offset;
cur_gpt_offset = part_dev->gpt_offset;
} else {
cur_gpt_offset =
parent_entry->first_lba * block_size + part_dev->gpt_offset;
device_capacity =
(parent_entry->last_lba - parent_entry->first_lba + 1) * block_size;
PT_DBG("name %s, first %llu, last %llu\n", parent_entry->name,
parent_entry->first_lba, parent_entry->last_lba);
}
card_size_sec = (device_capacity) / block_size;
ASSERT(card_size_sec > 0);
header_buf = (uint8_t *)pvPortMallocAligned(block_size, block_size);
entries_buf = (uint8_t *)pvPortMallocAligned(block_size, block_size);
if (!header_buf || !entries_buf)
goto end;
if (SEC2PRI == dir) {
mbr_buf = (uint8_t *)pvPortMallocAligned(block_size, block_size);
if (!mbr_buf)
goto end;
ret = pt_disk_read(part_dev->diskdev, cur_gpt_offset, mbr_buf,
block_size);
if (ret) {
PT_ERROR("fail to read mbr block\n");
goto end;
}
ptn_src_header = (card_size_sec - 1) * block_size + cur_gpt_offset;
} else {
ptn_src_header = cur_gpt_offset + block_size;
}
ret =
pt_disk_read(part_dev->diskdev, ptn_src_header, header_buf, block_size);
if (ret) {
PT_ERROR("fail to read src gpt header!\n");
goto end;
}
PT_DBG("src header:%llu dir:%u!\n", ptn_src_header, dir);
ret = ptdev_parse_gpt_header(
part_dev, header_buf, &first_usable_lba, &partition_entry_size,
&header_size, &max_partition_count, parent_entry, (dir == SEC2PRI),
&partition_entries_offset, &crc_entries, true);
if (ret) {
PT_ERROR("fail to parse src gpt header!\n");
goto end;
}
entries_cnt_per_block = block_size / partition_entry_size;
blocks_to_read = round_up(max_partition_count, entries_cnt_per_block) /
entries_cnt_per_block;
backup_header_lba =
GET_LLWORD_FROM_BYTE(&header_buf[PRIMARY_HEADER_OFFSET]);
current_header_lba =
GET_LLWORD_FROM_BYTE(&header_buf[BACKUP_HEADER_OFFSET]);
if (SEC2PRI == dir) {
ptn_dst_header = cur_gpt_offset;
gpt_size = (1 + GPT_HEADER_BLOCKS + blocks_to_read) * block_size;
ptn_erase = ptn_dst_header;
ptn_dst_header += block_size;
current_header_lba = GPT_LBA;
ptn_dst_entries = ptn_dst_header + block_size;
entries_start_lba = 0x2;
} else {
ptn_dst_header = (card_size_sec - 1) * block_size + cur_gpt_offset;
gpt_size = (GPT_HEADER_BLOCKS + blocks_to_read) * block_size;
ptn_erase = ptn_dst_header - blocks_to_read * block_size;
ptn_dst_entries = ptn_erase;
entries_start_lba = card_size_sec - 1 - blocks_to_read;
}
PUT_LONG_LONG(header_buf + PRIMARY_HEADER_OFFSET, current_header_lba);
PUT_LONG_LONG(header_buf + BACKUP_HEADER_OFFSET, backup_header_lba);
PUT_LONG_LONG(header_buf + PARTITION_ENTRIES_OFFSET, entries_start_lba);
if (strstr(part_dev->diskdev->info->disk_name, "flash")) {
if (pt_disk_erase(part_dev->diskdev,
round_down(ptn_erase, erase_grp_sz),
round_up(gpt_size, erase_grp_sz))) {
PT_ERROR("erase gpt header fail!\n");
goto end;
}
}
crc32_val = 0;
partition_entries_offset += cur_gpt_offset;
for (uint32_t i = 0; i < blocks_to_read; i++) {
ret = pt_disk_read(part_dev->diskdev,
partition_entries_offset + (i * block_size),
entries_buf, block_size);
if (ret) {
PT_ERROR("fail to read partition entries\n");
goto end;
}
crc32_val = crc32(crc32_val, entries_buf, block_size);
ret =
pt_disk_write(part_dev->diskdev, ptn_dst_entries + (i * block_size),
entries_buf, block_size);
if (ret) {
PT_ERROR("fail to restore partition entries\n");
goto end;
}
}
if (crc_entries != crc32_val) {
PT_ERROR("fail to check partition entries crc32\n");
ret = 1;
goto end;
}
PUT_LONG(header_buf + PARTITION_CRC_OFFSET, crc32_val);
crc32_val = 0;
PUT_LONG(header_buf + HEADER_CRC_OFFSET, crc32_val);
crc32_val = crc32(0, header_buf, header_size);
PUT_LONG(header_buf + HEADER_CRC_OFFSET, crc32_val);
ret = pt_disk_write(part_dev->diskdev, ptn_dst_header, header_buf,
block_size);
if (ret) {
PT_ERROR("fail to restore partition header\n");
goto end;
}
/* restore_mbr */
if (SEC2PRI == dir) {
ret = pt_disk_write(part_dev->diskdev, cur_gpt_offset, mbr_buf,
block_size);
if (ret) {
PT_ERROR("fail to restore the mbr\n");
goto end;
}
}
ret = 0;
end:
if (header_buf)
vPortFree(header_buf);
if (entries_buf)
vPortFree(entries_buf);
if (mbr_buf)
vPortFree(mbr_buf);
return ret;
}
/**
* @brief check secondary
* @param part_dev
* @param parent_entry
* @return uint32_t 0 is success other is error
*/
static uint32_t check_secondary_gpt(partition_device_t *part_dev,
struct partition_entry *parent_entry)
{
uint32_t ret = 1;
uint64_t device_capacity;
uint8_t *header_buf = NULL;
uint8_t *entries_buf = NULL;
uint32_t block_size, crc32_val, max_partition_count, crc_entries;
uint32_t entries_cnt_per_block, blocks_to_read, header_size,
partition_entry_size;
uint64_t first_usable_lba, cur_gpt_offset;
uint64_t card_size_sec, ptn_src_header, partition_entries_offset;
if (!part_dev) {
PT_ERROR("partition device error!\n");
goto end;
}
block_size = pt_disk_get_blocksize(part_dev->diskdev);
if (!block_size) {
PT_ERROR("get block size =%d error\n", block_size);
ret = 1;
goto end;
}
if (!parent_entry) {
device_capacity = pt_disk_get_capacity(part_dev->diskdev);
if (device_capacity < part_dev->gpt_offset) {
PT_ERROR("get capacity = %lld error\n", device_capacity);
ret = 1;
goto end;
}
device_capacity = device_capacity - part_dev->gpt_offset;
cur_gpt_offset = part_dev->gpt_offset;
} else {
cur_gpt_offset =
parent_entry->first_lba * block_size + part_dev->gpt_offset;
device_capacity =
(parent_entry->last_lba - parent_entry->first_lba + 1) * block_size;
PT_DBG("parent name %s, first %llu, last %llu\n", parent_entry->name,
parent_entry->first_lba, parent_entry->last_lba);
}
card_size_sec = (device_capacity) / block_size;
ASSERT(card_size_sec > 0);
header_buf = (uint8_t *)pvPortMallocAligned(block_size, block_size);
entries_buf = (uint8_t *)pvPortMallocAligned(block_size, block_size);
if (!header_buf || !entries_buf) {
PT_ERROR("fail to allocate memory!\n");
goto end;
}
ptn_src_header = (card_size_sec - 1) * block_size + cur_gpt_offset;
ret =
pt_disk_read(part_dev->diskdev, ptn_src_header, header_buf, block_size);
if (ret) {
PT_ERROR("fail to read src gpt header!\n");
goto end;
}
ret = ptdev_parse_gpt_header(part_dev, header_buf, &first_usable_lba,
&partition_entry_size, &header_size,
&max_partition_count, parent_entry, true,
&partition_entries_offset, &crc_entries, true);
if (ret) {
PT_ERROR("fail to parse src gpt header!\n");
goto end;
}
entries_cnt_per_block = block_size / partition_entry_size;
blocks_to_read = round_up(max_partition_count, entries_cnt_per_block) /
entries_cnt_per_block;
crc32_val = 0;
partition_entries_offset += cur_gpt_offset;
for (uint32_t i = 0; i < blocks_to_read; i++) {
ret = pt_disk_read(part_dev->diskdev,
partition_entries_offset + (i * block_size),
entries_buf, block_size);
if (ret) {
PT_ERROR("fail to read partition entries\n");
goto end;
}
crc32_val = crc32(crc32_val, entries_buf, block_size);
}
if (crc_entries != crc32_val) {
PT_ERROR("fail to check partition entries crc32\n");
ret = 1;
goto end;
}
ret = 0;
end:
if (header_buf)
vPortFree(header_buf);
if (entries_buf)
vPortFree(entries_buf);
return ret;
}
/**
* @brief Read and check GPT from disk
*
* @param part_dev
* @param parent_entry
* @param block_size
* @return unsigned int 0 is success other is error
*/
static uint32_t ptdev_read_gpt(partition_device_t *part_dev,
struct partition_entry *parent_entry,
uint32_t block_size)
{
int ret = 0;
uint32_t crc_val = 0;
uint32_t crc_entries = 0;
unsigned int header_size;
bool valid_entry_finish = false;
unsigned long long first_usable_lba;
unsigned long long backup_header_lba;
unsigned long long card_size_sec;
unsigned int max_partition_count = 0;
unsigned int partition_entry_size;
unsigned int i = 0; /* Counter for each block */
unsigned int j = 0; /* Counter for each entry in a block */
unsigned int n = 0; /* Counter for UTF-16 -> 8 conversion */
unsigned char UTF16_name[MAX_GPT_NAME_SIZE];
/* LBA of first partition -- 1 Block after Protected MBR + 1 for PT */
uint64_t device_capacity;
uint8_t *data = NULL;
uint8_t *gpt_header_ptr = NULL;
uint8_t *entries_buffer = NULL;
uint32_t blocks_for_entries =
(NUM_PARTITIONS * PARTITION_ENTRY_SIZE) / block_size;
uint32_t entries_cnt_per_block;
uint32_t blocks_to_read;
uint64_t lba_offset;
unsigned long long cur_gpt_offset;
uint64_t partition_entries_offset = 0;
struct partition_entry *partition_entries = part_dev->partition_entries;
struct partition_entry *current_entry;
struct partition_entry pt_entry;
unsigned partition_count = part_dev->count;
const unsigned partition_count_reset = part_dev->count;
bool re_parse_gpt = false;
/* Get the density of the boot device */
PT_DBG("ptdev_read_gpt\n");
if (!parent_entry) {
device_capacity = pt_disk_get_capacity(part_dev->diskdev);
if (device_capacity < part_dev->gpt_offset) {
PT_ERROR("get capacity = %lld error\n", device_capacity);
ret = -1;
goto end;
}
device_capacity = device_capacity - part_dev->gpt_offset;
cur_gpt_offset = part_dev->gpt_offset;
lba_offset = 0;
} else {
lba_offset = parent_entry->first_lba;
cur_gpt_offset =
parent_entry->first_lba * block_size + part_dev->gpt_offset;
device_capacity =
(parent_entry->last_lba - parent_entry->first_lba + 1) * block_size;
PT_DBG("parent name %s, first %llu, last %llu\n", parent_entry->name,
parent_entry->first_lba, parent_entry->last_lba);
}
gpt_header_ptr = (uint8_t *)pvPortMallocAligned(
ROUNDUP(block_size, block_size), block_size);
if (!gpt_header_ptr) {
PT_ERROR("Failed to Allocate memory to read partition table\n");
ret = -1;
goto end;
}
data = gpt_header_ptr;
/* Print out the GPT first */
ret = pt_disk_read(part_dev->diskdev,
(disk_addr_t)(block_size + cur_gpt_offset),
(uint8_t *)data, (disk_addr_t)block_size);
if (ret) {
PT_ERROR("GPT: Could not read primary gpt from boot device\n");
goto end;
}
ret = ptdev_parse_gpt_header(part_dev, data, &first_usable_lba,
(uint32_t *)&partition_entry_size, (uint32_t *)&header_size,
(uint32_t *)&max_partition_count, parent_entry, false,
&partition_entries_offset, &crc_entries, true);
if (ret) {
PT_ERROR("GPT: (WARNING) Primary header invalid\n");
re_parse_gpt:
re_parse_gpt = true;
part_dev->count = partition_count_reset;
partition_count = partition_count_reset;
data = gpt_header_ptr;
/* Check the backup gpt */
/* Get size of MMC */
card_size_sec = (device_capacity) / block_size;
ASSERT(card_size_sec > 0);
backup_header_lba = card_size_sec - 1;
ret = pt_disk_read(part_dev->diskdev,
(backup_header_lba * block_size) + cur_gpt_offset,
(uint8_t *)data, block_size);
if (ret) {
PT_ERROR("GPT: Could not read backup gpt from boot_device\n");
goto end;
}
ret = ptdev_parse_gpt_header(
part_dev, data, &first_usable_lba, (uint32_t *)&partition_entry_size,
(uint32_t *)&header_size, (uint32_t *)&max_partition_count, parent_entry, true,
&partition_entries_offset, &crc_entries, true);
if (ret) {
PT_ERROR("GPT: Primary and backup signatures invalid\n");
goto end;
}
}
if (!entries_buffer)
entries_buffer = (uint8_t *)pvPortMallocAligned(block_size, block_size);
if (!entries_buffer) {
PT_ERROR("GPT: Allocate memory fail\n");
ret = 1;
goto end;
}
crc_val = 0;
valid_entry_finish = false;
entries_cnt_per_block = block_size / partition_entry_size;
blocks_to_read = round_up(max_partition_count, entries_cnt_per_block) /
entries_cnt_per_block;
partition_entries_offset += cur_gpt_offset;
/* Read GPT Entries */
for (i = 0; i < blocks_to_read; i++) {
ASSERT(partition_count < NUM_PARTITIONS);
ret = pt_disk_read(part_dev->diskdev,
partition_entries_offset + (i * block_size),
entries_buffer, block_size);
if (ret) {
PT_ERROR("GPT: read partition entries fail\n");
goto end;
}
data = entries_buffer;
crc_val = crc32(crc_val, data, block_size);
for (j = 0; j < entries_cnt_per_block && !valid_entry_finish; j++) {
/*
* If partition_entries is NULL,
* it means that the caller only wants
* to get partition entries count
*/
current_entry = &pt_entry;
memcpy(&(current_entry->type_guid),
&data[(j * partition_entry_size)], PARTITION_TYPE_GUID_SIZE);
if (current_entry->type_guid[0] == 0x00 &&
current_entry->type_guid[1] == 0x00) {
/*
* Here, the last valid partition has got,
* but needs to read remain data for crc32 of partition
* entries array
*/
// i = ROUNDUP(max_partition_count, entries_cnt_per_block);
valid_entry_finish = true;
break;
}
if (partition_entries) {
current_entry = &partition_entries[partition_count];
memcpy(&(current_entry->type_guid), &(pt_entry.type_guid),
PARTITION_TYPE_GUID_SIZE);
}
memcpy(&(current_entry->unique_partition_guid),
&data[(j * partition_entry_size) + UNIQUE_GUID_OFFSET],
UNIQUE_PARTITION_GUID_SIZE);
current_entry->first_lba = GET_LLWORD_FROM_BYTE(
&data[(j * partition_entry_size) + FIRST_LBA_OFFSET]);
current_entry->last_lba = GET_LLWORD_FROM_BYTE(
&data[(j * partition_entry_size) + LAST_LBA_OFFSET]);
/* If partition entry LBA is not valid, skip this entry
and parse next entry */
if ((current_entry->first_lba) < first_usable_lba ||
(current_entry->last_lba) >
(device_capacity / block_size -
(blocks_for_entries + GPT_HEADER_BLOCKS + 1)) ||
current_entry->first_lba > current_entry->last_lba) {
PT_ERROR("Partition entry(%d), lba not valid\n", j);
continue;
}
/* Here, save the actual lba */
current_entry->first_lba += lba_offset;
current_entry->last_lba += lba_offset;
current_entry->size =
current_entry->last_lba - current_entry->first_lba + 1;
current_entry->attribute_flag = GET_LLWORD_FROM_BYTE(
&data[(j * partition_entry_size) + ATTRIBUTE_FLAG_OFFSET]);
memcpy(UTF16_name,
&data[(j * partition_entry_size) + PARTITION_NAME_OFFSET],
MAX_GPT_NAME_SIZE);
/*
* Currently partition names in *.xml are UTF-8 and lowercase
* Only supporting english for now so removing 2nd byte of UTF-16
*/
for (n = 0; n < MAX_GPT_NAME_SIZE / 2; n++) {
current_entry->name[n] = UTF16_name[n * 2];
}
if (parent_entry) {
unsigned char *parent_name = parent_entry->name;
char name[MAX_GPT_NAME_SIZE * 2 + 1] = {0};
sprintf(name, "%s%s%s", parent_name, partition_separator,
current_entry->name);
if (strlen(name) >= MAX_GPT_NAME_SIZE)
PT_ERROR("parent or sub partiton name is too long.\n");
strncpy((char *)current_entry->name, (const char *)&name,
MAX_GPT_NAME_SIZE);
}
partition_count++;
part_dev->count = partition_count;
if (!partition_entries && ptdev_sub_part_exist(current_entry)) {
if (ptdev_read_gpt(part_dev, current_entry, block_size)) {
/* if parse sub partition fail, go on */
PT_ERROR("parse sub partition fail!\n");
} else {
partition_count = part_dev->count;
}
}
}
}
if (crc_val != crc_entries) {
PT_ERROR("Partition entires crc mismatch crc_val= 0x%08x with "
"crc_val_org= 0x%08x\n",
crc_val, crc_entries);
if (!re_parse_gpt) {
/* Here, it means fail to check crc32 of primary gpt entries array!
* We need to re-parse gpt from secondary gpt
* */
PT_ERROR("re-parse gpt from secondary gpt!\n");
goto re_parse_gpt;
} else {
/* Here, the primary and the sencondary
* gpt are all error.*/
ret = 1;
goto end;
}
}
if (re_parse_gpt) {
PT_ERROR("restore primary gpt!\n");
ret = restore_gpt(part_dev, SEC2PRI, parent_entry);
if (ret)
PT_ERROR("fail to restore primary gpt!\n");
} else {
if (check_secondary_gpt(part_dev, parent_entry)) {
PT_ERROR("secnodary gpt error!\n");
if (restore_gpt(part_dev, PRI2SEC, parent_entry)) {
PT_ERROR("fail to restore secondary gpt!\n");
} else {
PT_ERROR("restore secondary gpt successfully!\n");
}
} else {
PT_DBG("check secondary gpt ok!\n");
}
}
ret = 0;
end:
if (gpt_header_ptr)
vPortFree(gpt_header_ptr);
if (entries_buffer) {
vPortFree(entries_buffer);
}
return ret;
}
/**
* @brief Write MBR of the partition
*
* @param part_dev
* @param size
* @param mbrImage
* @param block_size
* @return unsigned int 0 is success other is error
*/
static unsigned int write_mbr_in_blocks(partition_device_t *part_dev,
uint32_t size, uint8_t *mbrImage,
uint32_t block_size)
{
unsigned int dtype;
unsigned int dfirstsec;
unsigned int ebrSectorOffset;
unsigned char *ebrImage;
unsigned char *lastAddress;
int idx, i;
unsigned int ret;
/* Write the first block */
ret = pt_disk_write(part_dev->diskdev, part_dev->gpt_offset,
(uint8_t *)mbrImage, block_size);
if (ret) {
PT_ERROR("Failed to write mbr partition\n");
goto end;
}
PT_DBG("write of first MBR block ok\n");
/*
Loop through the MBR table to see if there is an EBR.
If found, then figure out where to write the first EBR
*/
idx = TABLE_ENTRY_0;
for (i = 0; i < 4; i++) {
dtype = mbrImage[idx + i * TABLE_ENTRY_SIZE + OFFSET_TYPE];
if (MBR_EBR_TYPE == dtype) {
PT_DBG("EBR found.\n");
break;
}
}
if (MBR_EBR_TYPE != dtype) {
PT_DBG("No EBR in this image\n");
goto end;
}
/* EBR exists. Write each EBR block to boot_device */
ebrImage = mbrImage + block_size;
ebrSectorOffset = GET_LWORD_FROM_BYTE(
&mbrImage[idx + i * TABLE_ENTRY_SIZE + OFFSET_FIRST_SEC]);
dfirstsec = 0;
PT_DBG("first EBR to be written at sector 0x%X\n", dfirstsec);
lastAddress = mbrImage + size;
while (ebrImage < lastAddress) {
PT_DBG("writing to 0x%X\n", (ebrSectorOffset + dfirstsec) * block_size);
ret = pt_disk_write(part_dev->diskdev,
(ebrSectorOffset + dfirstsec) * block_size +
part_dev->gpt_offset,
(uint8_t *)ebrImage, block_size);
if (ret) {
PT_ERROR("Failed to write EBR block to sector 0x%X\n", dfirstsec);
goto end;
}
dfirstsec =
GET_LWORD_FROM_BYTE(&ebrImage[TABLE_ENTRY_1 + OFFSET_FIRST_SEC]);
ebrImage += block_size;
}
PT_DBG("MBR written to boot device successfully\n");
end:
return ret;
}
/**
* @brief Write the MBR/EBR
*
* @param part_dev
* @param size
* @param mbrImage
* @param block_size
* @return unsigned int 0 is success other is error
*/
static uint32_t write_mbr(partition_device_t *part_dev, uint32_t size,
uint8_t *mbrImage, uint32_t block_size)
{
unsigned int ret;
uint64_t device_capacity;
/* Verify that passed in block is a valid MBR */
ret = ptdev_verify_mbr_signature(size, mbrImage);
if (ret) {
goto end;
}
device_capacity = pt_disk_get_capacity(part_dev->diskdev);
if (device_capacity < part_dev->gpt_offset) {
PT_ERROR("get capacity = %lld error\n", device_capacity);
ret = -1;
goto end;
}
/* Erasing the device before writing */
ret = pt_disk_erase(part_dev->diskdev, part_dev->gpt_offset,
device_capacity - part_dev->gpt_offset);
if (ret) {
PT_ERROR("Failed to erase the eMMC card\n");
goto end;
}
/* Write the MBR/EBR to boot device */
ret = write_mbr_in_blocks(part_dev, size, mbrImage, block_size);
if (ret) {
PT_ERROR("Failed to write MBR block to boot device.\n");
goto end;
}
/* Re-read the MBR partition into mbr table */
ret = ptdev_read_mbr(part_dev, block_size);
if (ret) {
PT_ERROR("Failed to re-read mbr partition.\n");
goto end;
}
ptdev_dump(part_dev);
end:
return ret;
}
/**
* @brief A8h reflected is 15h, i.e. 10101000 <--> 00010101
*
* @param data
* @param len
* @return int
*/
int reflect(int data, int len)
{
int ref = 0;
for (int i = 0; i < len; i++) {
if (data & 0x1) {
ref |= (1 << ((len - 1) - i));
}
data = (data >> 1);
}
return ref;
}
/**
* @brief Write the GPT Partition Entry Array
*
* @param part_dev
* @param header
* @param partition_array_start
* @param array_size
* @param block_size
* @param parent_lba
* @return unsigned int 0 is success other is error
*/
static unsigned int
write_gpt_partition_array(partition_device_t *part_dev, uint8_t *header,
unsigned char *partition_array_start,
uint32_t array_size, uint32_t block_size,
uint64_t parent_lba)
{
unsigned int ret = 1;
unsigned long long partition_entry_lba;
unsigned long long partition_entry_array_start_location;
partition_entry_lba =
GET_LLWORD_FROM_BYTE(&header[PARTITION_ENTRIES_OFFSET]) + parent_lba;
partition_entry_array_start_location = partition_entry_lba * block_size;
ret = pt_disk_write(part_dev->diskdev,
partition_entry_array_start_location +
part_dev->gpt_offset,
(uint8_t *)partition_array_start, array_size);
if (ret) {
PT_ERROR("GPT: FAILED to write the partition entry array\n");
goto end;
}
end:
return ret;
}
/**
* @brief update partiton according to the real disk information
*
* @param part_dev
* @param gptImage
* @param density
* @param array_size
* @param max_part_count
* @param part_entry_size
* @param block_size
* @param parent_entry
* @param last_part_extend
*/
static void patch_gpt(partition_device_t *part_dev, uint8_t *gptImage,
uint64_t density, uint32_t array_size,
uint32_t max_part_count, uint32_t part_entry_size,
uint32_t block_size, struct partition_entry *parent_entry,
bool last_part_extend)
{
unsigned char *primary_gpt_header;
unsigned char *secondary_gpt_header;
unsigned long long *last_partition_entry;
unsigned int offset;
unsigned char *partition_entry_array_start;
unsigned long long card_size_sec;
int total_part = 0;
uint32_t crc_value = 0;
unsigned int last_part_offset;
unsigned ptn_entries_blocks =
(NUM_PARTITIONS * PARTITION_ENTRY_SIZE) / block_size;
/* Get size of MMC */
card_size_sec = (density) / block_size;
/* Working around cap at 4GB */
if (card_size_sec == 0) {
card_size_sec = 4 * 1024 * 1024 * 2 - 1;
}
/* Patching primary header */
primary_gpt_header = (gptImage + block_size);
PUT_LONG_LONG(primary_gpt_header + BACKUP_HEADER_OFFSET,
((long long)(card_size_sec - 1)));
PUT_LONG_LONG(primary_gpt_header + LAST_USABLE_LBA_OFFSET,
((long long)(card_size_sec -
(ptn_entries_blocks + GPT_HEADER_BLOCKS + 1))));
/* Patching backup GPT */
offset = (2 * array_size);
secondary_gpt_header = offset + block_size + primary_gpt_header;
PUT_LONG_LONG(secondary_gpt_header + PRIMARY_HEADER_OFFSET,
((long long)(card_size_sec - 1)));
PUT_LONG_LONG(secondary_gpt_header + LAST_USABLE_LBA_OFFSET,
((long long)(card_size_sec -
(ptn_entries_blocks + GPT_HEADER_BLOCKS + 1))));
PUT_LONG_LONG(secondary_gpt_header + PARTITION_ENTRIES_OFFSET,
((long long)(card_size_sec -
(ptn_entries_blocks + GPT_HEADER_BLOCKS))));
/* Find last partition */
last_partition_entry =
(unsigned long long *)(primary_gpt_header + block_size +
total_part * PARTITION_ENTRY_SIZE);
// need check 128 bit for GUID
while (*last_partition_entry != 0 || *(last_partition_entry + 1) != 0) {
total_part++;
last_partition_entry =
(unsigned long long *)(primary_gpt_header + block_size +
total_part * PARTITION_ENTRY_SIZE);
}
/* Patching last partition */
if (last_part_extend) {
last_part_offset =
(total_part - 1) * PARTITION_ENTRY_SIZE + PARTITION_ENTRY_LAST_LBA;
PUT_LONG_LONG(primary_gpt_header + block_size + last_part_offset,
(long long)(card_size_sec - (ptn_entries_blocks +
GPT_HEADER_BLOCKS + 1)));
PUT_LONG_LONG(
primary_gpt_header + block_size + last_part_offset + array_size,
(long long)(card_size_sec -
(ptn_entries_blocks + GPT_HEADER_BLOCKS + 1)));
}
/* Updating CRC of the Partition entry array in both headers */
partition_entry_array_start = primary_gpt_header + block_size;
crc_value =
crc32(0, partition_entry_array_start, max_part_count * part_entry_size);
PUT_LONG(primary_gpt_header + PARTITION_CRC_OFFSET, crc_value);
crc_value = crc32(0, partition_entry_array_start + array_size,
max_part_count * part_entry_size);
PUT_LONG(secondary_gpt_header + PARTITION_CRC_OFFSET, crc_value);
/* Clearing CRC fields to calculate */
PUT_LONG(primary_gpt_header + HEADER_CRC_OFFSET, 0);
crc_value = crc32(0, primary_gpt_header, GPT_HEADER_SIZE);
PUT_LONG(primary_gpt_header + HEADER_CRC_OFFSET, crc_value);
PUT_LONG(secondary_gpt_header + HEADER_CRC_OFFSET, 0);
crc_value = crc32(0, secondary_gpt_header, GPT_HEADER_SIZE);
PUT_LONG(secondary_gpt_header + HEADER_CRC_OFFSET, crc_value);
}
/**
* @brief Write the GPT to the device.
*
* @param part_dev
* @param parent_entry
* @param size
* @param gptImage
* @param block_size
* @param last_part_extend
* @return unsigned int 0 is success other is error
*/
static uint32_t write_gpt(partition_device_t *part_dev,
struct partition_entry *parent_entry,
uint32_t size, uint8_t *gptImage,
uint32_t block_size, bool last_part_extend)
{
unsigned int ret = 1;
unsigned int header_size;
unsigned long long first_usable_lba;
unsigned long long backup_header_lba;
unsigned int max_partition_count = 0;
unsigned int partition_entry_size;
unsigned char *partition_entry_array_start;
unsigned char *primary_gpt_header;
unsigned char *secondary_gpt_header;
unsigned int offset;
unsigned int partition_entry_array_size;
unsigned long long primary_header_location; /* address on the boot device */
unsigned long long
secondary_header_location; /* address on the boot device */
uint64_t device_capacity;
uint64_t parent_lba = 0;
uint64_t partition_offset = 0;
/* Verify that passed block has a valid GPT primary header */
primary_gpt_header = (gptImage + block_size);
ret = ptdev_parse_gpt_header(part_dev, primary_gpt_header,
&first_usable_lba, (uint32_t *)&partition_entry_size,
(uint32_t *)&header_size, (uint32_t *)&max_partition_count,
parent_entry, false, NULL, NULL, false);
if (ret) {
PT_ERROR("GPT: Primary signature invalid cannot write GPT\n");
goto end;
}
/* Get the density of the mmc device */
if (parent_entry) {
device_capacity =
(parent_entry->last_lba - parent_entry->first_lba + 1) * block_size;
} else {
device_capacity = pt_disk_get_capacity(part_dev->diskdev);
if (device_capacity < part_dev->gpt_offset) {
PT_ERROR("get capacity = %lld error\n", device_capacity);
ret = 1;
goto end;
}
device_capacity = device_capacity - part_dev->gpt_offset;
}
/* Verify that passed block has a valid backup GPT HEADER */
partition_entry_array_size = partition_entry_size * max_partition_count;
if (partition_entry_array_size < MIN_PARTITION_ARRAY_SIZE) {
partition_entry_array_size = MIN_PARTITION_ARRAY_SIZE;
}
offset = (2 * partition_entry_array_size);
secondary_gpt_header = offset + block_size + primary_gpt_header;
ret = ptdev_parse_gpt_header(part_dev, secondary_gpt_header,
&first_usable_lba, (uint32_t *)&partition_entry_size,
(uint32_t *)&header_size, (uint32_t *)&max_partition_count,
parent_entry, true, NULL, NULL, false);
if (ret) {
PT_ERROR("GPT: Backup signature invalid cannot write GPT\n");
goto end;
}
/* Patching the primary and the backup header of the GPT table */
patch_gpt(part_dev, gptImage, device_capacity, partition_entry_array_size,
max_partition_count, partition_entry_size, block_size,
parent_entry, last_part_extend);
/* Writing protective MBR */
if (parent_entry) {
partition_offset = parent_entry->first_lba * block_size;
parent_lba = parent_entry->first_lba;
}
ret = pt_disk_write(part_dev->diskdev,
partition_offset + part_dev->gpt_offset,
(uint8_t *)gptImage, block_size);
if (ret) {
PT_ERROR("Failed to write Protective MBR\n");
goto end;
}
/* Writing the primary GPT header */
primary_header_location = block_size + partition_offset;
ret = pt_disk_write(part_dev->diskdev,
primary_header_location + part_dev->gpt_offset,
(uint8_t *)primary_gpt_header, block_size);
if (ret) {
PT_ERROR("Failed to write GPT header\n");
goto end;
}
/* Writing the backup GPT header */
backup_header_lba =
GET_LLWORD_FROM_BYTE(&primary_gpt_header[BACKUP_HEADER_OFFSET]);
secondary_header_location = (backup_header_lba + parent_lba) * block_size;
ret = pt_disk_write(part_dev->diskdev,
secondary_header_location + part_dev->gpt_offset,
(uint8_t *)secondary_gpt_header, block_size);
if (ret) {
PT_ERROR("Failed to write GPT backup header\n");
goto end;
}
/* Writing the partition entries array for the primary header */
partition_entry_array_start = primary_gpt_header + block_size;
ret = write_gpt_partition_array(
part_dev, primary_gpt_header, partition_entry_array_start,
partition_entry_array_size, block_size, parent_lba);
if (ret) {
PT_ERROR("GPT: Could not write GPT Partition entries array\n");
goto end;
}
/*Writing the partition entries array for the backup header */
partition_entry_array_start =
primary_gpt_header + block_size + partition_entry_array_size;
ret = write_gpt_partition_array(
part_dev, secondary_gpt_header, partition_entry_array_start,
partition_entry_array_size, block_size, parent_lba);
if (ret) {
PT_ERROR("GPT: Could not write GPT Partition entries array\n");
goto end;
}
/* Re-read the GPT partition table */
PT_DBG("Re-reading the GPT Partition Table\n");
part_dev->count = 0;
ptdev_read_table(part_dev);
ptdev_dump(part_dev);
PT_ALWAYS("GPT: Partition Table written\n");
memset(primary_gpt_header, 0x00, size - block_size);
end:
return ret;
}
/**
* @brief Overwrite the partition table, if name is NULL, overwrite the globe
* partition table, or else overwrite the sub-partition table.
* @param part_dev
* @param name
* @param size
* @param ptable
* @param last_part_extend
* @return int 0 is success other is error
*/
int ptdev_write_table(partition_device_t *part_dev, const char *name,
unsigned size, unsigned char *ptable,
bool last_part_extend)
{
unsigned int ret = 1;
unsigned int partition_type;
uint32_t block_size;
int index = INVALID_PTN;
struct partition_entry *partition_entries;
if (ptable == NULL) {
PT_ERROR("NULL partition table\n");
return 1;
}
if (!part_dev) {
PT_ERROR("Write Invalide partition\n");
return 1;
}
partition_entries = part_dev->partition_entries;
if (name) {
PT_DBG("write partition %s\n", name);
index = ptdev_get_index(part_dev, name);
if (index == INVALID_PTN) {
PT_ERROR(
"Invalide partition name or write global partiton first.\n");
return 1;
}
} else {
PT_DBG("write global parition\n");
}
block_size = pt_disk_get_blocksize(part_dev->diskdev);
if (!block_size) {
PT_ERROR("get block size =%d error\n", block_size);
return 1;
}
ret = ptdev_get_type(size, ptable, (uint32_t *)&partition_type);
if (ret)
return ret;
switch (partition_type) {
case PARTITION_TYPE_MBR:
PT_DBG("Writing MBR partition\n");
ret = write_mbr(part_dev, size, ptable, block_size);
break;
case PARTITION_TYPE_GPT:
PT_ALWAYS("Writing GPT partition\n");
if (index == INVALID_PTN) {
PT_ALWAYS("Re-Flash the global partitions\n");
ret = write_gpt(part_dev, NULL, size, ptable, block_size,
last_part_extend);
} else {
if (!part_dev->partition_entries) {
PT_ERROR("Write Invalide extral partition\n");
ret = 1;
break;
}
PT_ALWAYS("Re-Flash the extral partition table %s\n", name);
ret = write_gpt(part_dev, &partition_entries[index], size, ptable,
block_size, last_part_extend);
}
PT_ALWAYS("Re-Flash all the partitions\n");
break;
default:
PT_ERROR("Invalid partition\n");
ret = 1;
return ret;
}
return ret;
}
/**
* @brief Fill name for android partition found.
*
* @param partition_ent
* @param type
*/
static void mbr_fill_name(struct partition_entry *partition_ent,
uint32_t type)
{
memset(partition_ent->name, 0, MAX_GPT_NAME_SIZE);
switch (type) {
case MBR_RPM_TYPE:
strcpy((char *)partition_ent->name, "rpm");
break;
case MBR_TZ_TYPE:
strcpy((char *)partition_ent->name, "tz");
break;
case MBR_BOOT_TYPE:
strcpy((char *)partition_ent->name, "boot");
break;
case MBR_RECOVERY_TYPE:
strcpy((char *)partition_ent->name, "recovery");
break;
case MBR_MISC_TYPE:
strcpy((char *)partition_ent->name, "msic");
break;
case MBR_SSD_TYPE:
strcpy((char *)partition_ent->name, "ssd");
break;
};
}
/**
* @brief Find index of parition in array of partition entries
*
* @param part_dev
* @param name
* @return unsigned int
*/
unsigned int ptdev_get_index(partition_device_t *part_dev, const char *name)
{
unsigned int input_string_length = strlen(name);
unsigned n;
int curr_slot = INVALID;
const char *suffix_curr_actv_slot = NULL;
char *curr_suffix = NULL;
unsigned partition_count;
struct partition_entry *partition_entries;
if ((!part_dev) || (!part_dev->partition_entries) || (!name)) {
return INVALID_PTN;
}
partition_entries = part_dev->partition_entries;
partition_count = part_dev->count;
if (partition_count > NUM_PARTITIONS) {
return INVALID_PTN;
}
/* We iterate through the parition entries list,
to find the partition with active slot suffix.
*/
for (n = 0; n < partition_count; n++) {
if (!strncmp((const char *)name,
(const char *)partition_entries[n].name,
input_string_length)) {
curr_suffix =
(char *)(partition_entries[n].name + input_string_length);
/* if partition_entries.name is NULL terminated return the index */
if (*curr_suffix == '\0')
return n;
if (ptdev_multislot_is_supported(part_dev)) {
curr_slot = ptdev_find_active_slot(part_dev);
/* If suffix string matches with current active slot suffix
* return index */
if (curr_slot != INVALID) {
suffix_curr_actv_slot = SUFFIX_SLOT(curr_slot);
if (!strncmp((const char *)curr_suffix,
suffix_curr_actv_slot,
strlen(suffix_curr_actv_slot)))
return n;
else
continue;
} else {
/* No valid active slot */
return INVALID_PTN;
}
}
}
}
return INVALID_PTN;
}
/**
* @brief Get size of the partition
*
* @param part_dev
* @param name
* @return unsigned long long
*/
unsigned long long ptdev_get_size(partition_device_t *part_dev,
const char *name)
{
uint32_t block_size;
uint32_t index;
struct partition_entry *partition_entries;
if ((!part_dev) || (!part_dev->partition_entries) || (!name)) {
return 0;
}
index = ptdev_get_index(part_dev, name);
block_size = pt_disk_get_blocksize(part_dev->diskdev);
if (!block_size) {
PT_ERROR("get block size =%d error\n", block_size);
return 0;
}
partition_entries = part_dev->partition_entries;
if (index == (unsigned)INVALID_PTN)
return 0;
else {
return partition_entries[index].size * block_size;
}
}
/**
* @brief Get offset of the partition
*
* @param part_dev
* @param name
* @return unsigned long long
*/
unsigned long long ptdev_get_offset(partition_device_t *part_dev,
const char *name)
{
uint32_t block_size;
uint32_t index;
struct partition_entry *partition_entries;
if ((!part_dev) || (!part_dev->partition_entries) || (!name)) {
return 0;
}
index = ptdev_get_index(part_dev, name);
block_size = pt_disk_get_blocksize(part_dev->diskdev);
if (!block_size) {
PT_ERROR("get block size =%d error\n", block_size);
return 0;
}
partition_entries = part_dev->partition_entries;
if (index == (unsigned)INVALID_PTN)
return 0;
else {
return partition_entries[index].first_lba * block_size +
part_dev->gpt_offset;
}
}
/**
* @brief Get offset and size of the partition
*
* @param part_dev
* @param name
* @return struct partition_info
*/
struct partition_info ptdev_get_info(partition_device_t *part_dev,
const char *name)
{
struct partition_info info = {0};
if (!name) {
PT_ERROR("Invalid partition name passed\n");
return info;
}
if (!part_dev) {
PT_ERROR("Invalid partition dev\n");
return info;
}
info.offset = ptdev_get_offset(part_dev, name);
info.size = ptdev_get_size(part_dev, name);
if (!info.offset || !info.size)
PT_ERROR("Error unable to find partition : [%s]\n", name);
return info;
}
/**
* @brief Debug: Print all parsed partitions
*
* @param part_dev
*/
void ptdev_dump(partition_device_t *part_dev)
{
unsigned i = 0;
unsigned partition_count;
struct partition_entry *partition_entries;
if ((!part_dev) || (!part_dev->partition_entries)) {
PT_ERROR("Invalid partition dev\n");
return;
}
partition_count = part_dev->count;
partition_entries = part_dev->partition_entries;
for (i = 0; i < partition_count; i++) {
PT_ALWAYS(
"ptn[%d]:Name[%s] Size[%llu] Type[%u] First[%llu] Last[%llu]\n", i,
partition_entries[i].name, partition_entries[i].size,
partition_entries[i].dtype, partition_entries[i].first_lba,
partition_entries[i].last_lba);
}
}
/**
* @brief Print all parsed partitions arribut bits
*
* @param part_dev
*/
void ptdev_attr_dump(partition_device_t *part_dev)
{
const char *a, *b, *s;
unsigned long long retry = 0;
unsigned long long priority = 0;
unsigned partition_count;
struct partition_entry *partition_entries;
if ((!part_dev) || (!part_dev->partition_entries)) {
PT_ERROR("Invalid partition dev\n");
return;
}
partition_count = part_dev->count;
partition_entries = part_dev->partition_entries;
PT_ALWAYS("active bootable success retry priority name\n");
for (unsigned i = 0; i < partition_count; i++) {
a = (!!(partition_entries[i].attribute_flag & PART_ATT_ACTIVE_VAL))
? "Y"
: "N";
b = (!!(partition_entries[i].attribute_flag & PART_ATT_UNBOOTABLE_VAL))
? "N"
: "Y";
s = (!!(partition_entries[i].attribute_flag & PART_ATT_SUCCESSFUL_VAL))
? "Y"
: "N";
retry = (partition_entries[i].attribute_flag &
PART_ATT_MAX_RETRY_COUNT_VAL) >>
PART_ATT_MAX_RETRY_CNT_BIT;
priority =
(partition_entries[i].attribute_flag & PART_ATT_PRIORITY_VAL) >>
PART_ATT_PRIORITY_BIT;
PT_ALWAYS(" %s %s %s %llx %llx %s\n", a,
b, s, retry, priority, partition_entries[i].name);
}
}
/**
* @brief verify the mbr signature
*
* @param size
* @param buffer
* @return unsigned int
*/
static uint32_t ptdev_verify_mbr_signature(uint32_t size,
uint8_t *buffer)
{
/* Avoid checking past end of buffer */
if ((TABLE_SIGNATURE + 1) > size) {
return 1;
}
/* Check to see if signature exists */
if ((buffer[TABLE_SIGNATURE] != MMC_MBR_SIGNATURE_BYTE_0) ||
(buffer[TABLE_SIGNATURE + 1] != MMC_MBR_SIGNATURE_BYTE_1)) {
PT_ERROR("MBR signature does not match.\n");
return 1;
}
return 0;
}
/**
* @brief master boot record partition types should be in a byte
*
* @param size
* @param partition
* @param partition_type
* @return unsigned int
*/
static uint32_t mbr_partition_get_type(uint32_t size,
uint8_t *partition,
uint8_t *partition_type)
{
unsigned int type_offset = TABLE_ENTRY_0 + OFFSET_TYPE;
if (size < (type_offset + sizeof(*partition_type))) {
return 1;
}
*partition_type = partition[type_offset];
return 0;
}
/**
* @brief Get the partition type
*
* @param size
* @param partition
* @param partition_type
* @return unsigned int
*/
static uint32_t ptdev_get_type(uint32_t size, uint8_t *partition,
uint32_t *partition_type)
{
unsigned int ret = 0;
/*
* If the block contains the MBR signature, then it's likely either
* MBR or MBR with protective type (GPT). If the MBR signature is
* not there, then it could be the GPT backup.
*/
/* First check the MBR signature */
ret = ptdev_verify_mbr_signature(size, partition);
if (!ret) {
unsigned char mbr_partition_type = PARTITION_TYPE_MBR;
/* MBR signature verified. This could be MBR, MBR + EBR, or GPT */
ret = mbr_partition_get_type(size, partition, &mbr_partition_type);
if (ret) {
PT_ERROR("Cannot get TYPE of partition");
} else if (MBR_PROTECTED_TYPE == mbr_partition_type) {
*partition_type = PARTITION_TYPE_GPT;
} else {
*partition_type = PARTITION_TYPE_MBR;
}
} else {
/*
* This could be the GPT backup. Make that assumption for now.
* Anybody who treats the block as GPT backup should check the
* signature.
*/
*partition_type = PARTITION_TYPE_GPT_BACKUP;
}
return ret;
}
/**
* @brief Parse the gpt header and get the required header fields
* Return 0 on valid signature
* @param part_dev
* @param buffer
* @param first_usable_lba
* @param partition_entry_size
* @param header_size
* @param max_partition_count
* @param parent_entry
* @param secondary_gpt
* @param partition_entries_offset
* @param crc_entries_array
* @param check_lba
* @return unsigned int
*/
uint32_t ptdev_parse_gpt_header(
partition_device_t *part_dev, uint8_t *buffer,
uint64_t *first_usable_lba, uint32_t *partition_entry_size,
uint32_t *header_size, uint32_t *max_partition_count,
struct partition_entry *parent_entry, bool secondary_gpt,
uint64_t *partition_entries_offset, uint32_t *crc_entries_array,
bool check_lba)
{
uint32_t ret = 0;
uint32_t crc_val = 0;
uint32_t crc_val_org = 0;
unsigned long long last_usable_lba = 0;
unsigned long long partition_0 = 0;
unsigned long long current_lba = 0;
uint32_t block_size = 0;
uint32_t blocks_for_entries = 0;
uint64_t device_capacity = 0;
uint8_t *entry_buffer_p = NULL;
block_size = pt_disk_get_blocksize(part_dev->diskdev);
if (!block_size) {
PT_ERROR("get block size =%d error\n", block_size);
return 1;
}
blocks_for_entries = (NUM_PARTITIONS * PARTITION_ENTRY_SIZE) / block_size;
if (!parent_entry) {
device_capacity = pt_disk_get_capacity(part_dev->diskdev);
if (device_capacity < part_dev->gpt_offset) {
PT_ERROR("get capacity =%lld error\n", device_capacity);
return 1;
}
device_capacity = device_capacity - part_dev->gpt_offset;
} else {
device_capacity =
(parent_entry->last_lba - parent_entry->first_lba + 1) * block_size;
}
/* Check GPT Signature */
if (((uint32_t *)buffer)[0] != GPT_SIGNATURE_2 ||
((uint32_t *)buffer)[1] != GPT_SIGNATURE_1) {
PT_ERROR("GPT: (WARNING) signature invalid\n");
return 1;
}
*header_size = GET_LWORD_FROM_BYTE(&buffer[HEADER_SIZE_OFFSET]);
/* check for header size too small */
if (*header_size < GPT_HEADER_SIZE) {
PT_ERROR("GPT Header size is too small\n");
return 1;
}
/* check for header size too large */
if (*header_size > block_size) {
PT_ERROR("GPT Header size is too large\n");
return 1;
}
crc_val_org = GET_LWORD_FROM_BYTE(&buffer[HEADER_CRC_OFFSET]);
/*Write CRC to 0 before we calculate the crc of the GPT header*/
crc_val = 0;
PUT_LONG(&buffer[HEADER_CRC_OFFSET], crc_val);
crc_val = crc32(0, buffer, *header_size);
if (crc_val != crc_val_org) {
PT_ERROR(
"Header crc mismatch crc_val = 0x%08x with crc_val_org = 0x%08x\n",
crc_val, crc_val_org);
return 1;
} else
PUT_LONG(&buffer[HEADER_CRC_OFFSET], crc_val);
current_lba = GET_LLWORD_FROM_BYTE(&buffer[PRIMARY_HEADER_OFFSET]);
*first_usable_lba = GET_LLWORD_FROM_BYTE(&buffer[FIRST_USABLE_LBA_OFFSET]);
*max_partition_count = GET_LWORD_FROM_BYTE(&buffer[PARTITION_COUNT_OFFSET]);
*partition_entry_size = GET_LWORD_FROM_BYTE(&buffer[PENTRY_SIZE_OFFSET]);
last_usable_lba = GET_LLWORD_FROM_BYTE(&buffer[LAST_USABLE_LBA_OFFSET]);
/* current lba and GPT lba should be same */
if (!secondary_gpt) {
if (current_lba != GPT_LBA) {
PT_ERROR("Primary GPT first usable LBA mismatch\n");
return 1;
}
} else {
/*
Check only in case of reading, skip for flashing as this is patched
in patch_gpt() later in flow.
*/
if (check_lba &&
(current_lba != ((device_capacity / block_size) - 1))) {
PT_ERROR("Secondary GPT first usable LBA mismatch\n");
return 1;
}
}
/* check for first lba should be with in the valid range */
if (*first_usable_lba > (device_capacity / block_size)) {
PT_ERROR("Invalid first_usable_lba\n");
return 1;
}
/* check for last lba should be with in the valid range */
if (last_usable_lba > (device_capacity / block_size)) {
PT_ERROR("Invalid last_usable_lba\n");
return 1;
}
/* check for partition entry size */
if (*partition_entry_size != PARTITION_ENTRY_SIZE) {
PT_ERROR("Invalid parition entry size\n");
return 1;
}
if ((*max_partition_count) >
(MIN_PARTITION_ARRAY_SIZE / (*partition_entry_size))) {
PT_ERROR("Invalid maximum partition count\n");
return 1;
}
if (check_lba) {
partition_0 = GET_LLWORD_FROM_BYTE(&buffer[PARTITION_ENTRIES_OFFSET]);
/* start LBA should always be 2 in primary GPT */
if (!secondary_gpt) {
if (partition_0 != 0x2) {
PT_ERROR("PrimaryGPT starting LBA mismatch\n");
return 1;
}
} else {
if (partition_0 != ((device_capacity / block_size) -
(blocks_for_entries + GPT_HEADER_BLOCKS))) {
PT_ERROR("BackupGPT starting LBA mismatch\n");
return 1;
}
}
*partition_entries_offset = partition_0 * block_size;
crc_val_org = GET_LWORD_FROM_BYTE(&buffer[PARTITION_CRC_OFFSET]);
*crc_entries_array = crc_val_org;
}
if (entry_buffer_p) {
vPortFree(entry_buffer_p);
entry_buffer_p = NULL;
}
return ret;
}
/**
* @brief check if partition table exists
*
* @param part_dev
* @return true
* @return false
*/
bool ptdev_gpt_exists(partition_device_t *part_dev)
{
return part_dev->gpt_partitions_exist;
}
/**
* @brief check if partition is_readonly
*
* @param part_dev
* @param name
* @return true
* @return false
*/
bool partition_is_readonly(partition_device_t *part_dev, const char *name)
{
struct partition_entry *partition_entries;
uint32_t index;
if ((!part_dev) || (!part_dev->partition_entries)) {
PT_ERROR("Invalid partition dev\n");
return false;
}
partition_entries = part_dev->partition_entries;
index = ptdev_get_index(part_dev, name);
if (index == (unsigned)INVALID_PTN) {
PT_ERROR("Invalide partition name\n");
return false;
}
return !!(partition_entries[index].attribute_flag & PART_ATT_READONLY_VAL);
}
/**
* @brief check if sub-partition table exists
*
* @param partition_entries
* @return true
* @return false
*/
static bool ptdev_sub_part_exist(struct partition_entry *partition_entries)
{
return !memcmp(sub_part_guid, partition_entries->type_guid,
PARTITION_TYPE_GUID_SIZE);
}
/**
* @brief set up the partiton for a disk
*
* @param diskdev
* @param gpt_offset
* @return partition_device_t*
*/
partition_device_t *ptdev_setup(struct disk_dev *diskdev, uint64_t gpt_offset)
{
partition_device_t *part_dev =
(partition_device_t *)pvPortMalloc(sizeof(partition_device_t));
if (part_dev) {
memset(part_dev, 0, sizeof(*part_dev));
part_dev->diskdev = diskdev;
part_dev->gpt_offset = gpt_offset;
}
return part_dev;
}
/**
* @brief destroy the partiton for a disk
*
* @param ptdev
* @return unsigned int
*/
unsigned int ptdev_destroy(partition_device_t *ptdev)
{
if (!ptdev) {
PT_ERROR("pointer is null\n");
return -1;
}
if (ptdev->partition_entries)
vPortFree(ptdev->partition_entries);
vPortFree(ptdev);
return 0;
}
/**
* @brief check if partition is active
*
* @param entry
* @return true
* @return false
*/
bool part_is_active(const struct partition_entry *entry)
{
return !!(entry->attribute_flag & PART_ATT_ACTIVE_VAL);
}
/**
* @brief Parse the gpt header and get the required header fields
* Return 0 on valid signature
* @param buffer
* @param first_usable_lba
* @param partition_entry_size
* @param header_size
* @param max_partition_count
* @param gpt_header
* @param block_size
* @param is_secondary_gpt
* @return unsigned int 0 is success ,1 is fail
*/
static unsigned int partition_parse_gpt_header(
unsigned char *buffer, unsigned long long *first_usable_lba,
unsigned int *partition_entry_size, unsigned int *header_size,
unsigned int *max_partition_count, GPT_header *gpt_header,
uint32_t block_size, bool is_secondary_gpt)
{
uint32_t crc_val = 0;
uint32_t ret = 0;
uint32_t crc_val_org = 0;
unsigned long long last_usable_lba = 0;
unsigned long long partition_0 = 0;
unsigned long long current_lba = 0;
unsigned long long backup_lba = 0;
/* Check GPT Signature */
if (((uint32_t *)buffer)[0] != GPT_SIGNATURE_2 ||
((uint32_t *)buffer)[1] != GPT_SIGNATURE_1) {
PT_ERROR("GPT signature error:0x%0x 0x%0x\n", ((uint32_t *)buffer)[0],
((uint32_t *)buffer)[1]);
return 1;
}
*header_size = GET_LWORD_FROM_BYTE(&buffer[HEADER_SIZE_OFFSET]);
/*check for header size too small*/
if (*header_size < GPT_HEADER_SIZE) {
PT_ERROR("GPT Header size is too small\n");
return 1;
}
/*check for header size too large*/
if (*header_size > block_size) {
PT_ERROR("GPT Header size is too large\n");
return 1;
}
crc_val_org = GET_LWORD_FROM_BYTE(&buffer[HEADER_CRC_OFFSET]);
/*Write CRC to 0 before we calculate the crc of the GPT header*/
crc_val = 0;
PUT_LONG(&buffer[HEADER_CRC_OFFSET], crc_val);
crc_val = crc32(0, buffer, *header_size);
if (crc_val != crc_val_org) {
PT_ERROR("Header crc mismatch crc_val = %u with crc_val_org = %u\n",
crc_val, crc_val_org);
return 1;
} else
PUT_LONG(&buffer[HEADER_CRC_OFFSET], crc_val);
current_lba = GET_LLWORD_FROM_BYTE(&buffer[PRIMARY_HEADER_OFFSET]);
backup_lba = GET_LLWORD_FROM_BYTE(&buffer[BACKUP_HEADER_OFFSET]);
*first_usable_lba = GET_LLWORD_FROM_BYTE(&buffer[FIRST_USABLE_LBA_OFFSET]);
*max_partition_count = GET_LWORD_FROM_BYTE(&buffer[PARTITION_COUNT_OFFSET]);
*partition_entry_size = GET_LWORD_FROM_BYTE(&buffer[PENTRY_SIZE_OFFSET]);
last_usable_lba = GET_LLWORD_FROM_BYTE(&buffer[LAST_USABLE_LBA_OFFSET]);
/*current lba and GPT lba should be same*/
if (!is_secondary_gpt && current_lba != GPT_LBA) {
PT_ERROR("Primary GPT first usable LBA mismatch\n");
return 1;
}
/*check for partition entry size*/
if (*partition_entry_size != PARTITION_ENTRY_SIZE) {
PT_ERROR("Invalid parition entry size\n");
return 1;
}
if ((*max_partition_count) >
(MIN_PARTITION_ARRAY_SIZE / (*partition_entry_size))) {
PT_ERROR("Invalid maximum partition count\n");
return 1;
}
partition_0 = GET_LLWORD_FROM_BYTE(&buffer[PARTITION_ENTRIES_OFFSET]);
/*start LBA should always be 2 in primary GPT*/
if (!is_secondary_gpt && partition_0 != 0x2) {
PT_ERROR("PrimaryGPT starting LBA mismatch\n");
ret = 1;
return ret;
}
memcpy((void *)(gpt_header->sign), buffer, 8);
memcpy((void *)(gpt_header->version), buffer + 8, 4);
memcpy((void *)(gpt_header->guid), buffer + 56, 16);
gpt_header->header_sz = *header_size;
gpt_header->current_lba = current_lba;
gpt_header->backup_lba = backup_lba;
gpt_header->first_usable_lba = *first_usable_lba;
gpt_header->last_usable_lba = last_usable_lba;
gpt_header->partition_entry_lba = partition_0;
gpt_header->partition_entry_count = *max_partition_count;
gpt_header->partition_entry_sz = *partition_entry_size;
gpt_header->header_crc32 = crc_val;
return ret;
}
/**
* @brief Parse a buffer, check if it has a primary or secondary parition table
* or inside
*
* @param buf
* @param buf_len
* @param gpt_header
* @param block_size
* @param is_secondary_gpt
* @return unsigned int 0 is success, other is fail
*/
unsigned int parse_gpt_table_from_buffer(uint8_t *buf, uint32_t buf_len,
GPT_header *gpt_header,
uint32_t block_size,
bool is_secondary_gpt)
{
unsigned int ret;
struct partition_entry *partition_entries = gpt_header->partition_entries;
/* Allocate partition entries array */
if (!partition_entries) {
partition_entries = (struct partition_entry *)pvPortMalloc(
NUM_PARTITIONS * sizeof(struct partition_entry));
ASSERT(partition_entries);
memset(partition_entries, 0x0,
NUM_PARTITIONS * sizeof(struct partition_entry));
} else {
memset(partition_entries, 0x0,
NUM_PARTITIONS * sizeof(struct partition_entry));
gpt_header->actual_entries_count = 0;
}
gpt_header->partition_entries = partition_entries;
ret = parse_gpt(buf, buf_len, block_size, gpt_header, is_secondary_gpt);
if (ret) {
PT_ERROR("GPT read failed!\n");
return 1;
}
return 0;
}
/**
* @brief Do Parsing a buffer, check if it has a parition table(Primary or
* Secondry) inside
* @param buffer
* @param buf_len
* @param block_size
* @param gpt_header
* @param is_secondary_gpt
* @return unsigned int
*/
static uint32_t parse_gpt(uint8_t *buffer, uint32_t buf_len,
uint32_t block_size, GPT_header *gpt_header,
bool is_secondary_gpt)
{
int ret = 0;
uint32_t crc_val = 0;
uint32_t crc_val_org = 0;
unsigned int header_size;
unsigned long long first_usable_lba;
unsigned int max_partition_count = 0;
unsigned int partition_entry_size;
unsigned int i = 0; /* Counter for each block */
unsigned int j = 0; /* Counter for each entry in a block */
unsigned int n = 0; /* Counter for UTF-16 -> 8 conversion */
unsigned char UTF16_name[MAX_GPT_NAME_SIZE];
/* LBA of first partition -- 1 Block after Protected MBR + 1 for PT */
uint8_t *data = NULL;
uint32_t part_entry_cnt = block_size / PARTITION_ENTRY_SIZE;
struct partition_entry *partition_entries = NULL;
unsigned char *new_buffer = NULL;
uint32_t partition_count = 0;
if (is_secondary_gpt) {
data = buffer + (buf_len - block_size);
} else {
data = buffer;
}
ret = partition_parse_gpt_header(
data, &first_usable_lba, &partition_entry_size, &header_size,
&max_partition_count, gpt_header, block_size, is_secondary_gpt);
if (ret) {
PT_ERROR("ret:%d\n", ret);
return ret;
}
if (is_secondary_gpt) {
new_buffer = buffer;
} else {
new_buffer = buffer + block_size;
}
crc_val_org = GET_LWORD_FROM_BYTE(&data[PARTITION_CRC_OFFSET]);
crc_val = crc32(0, new_buffer, max_partition_count * partition_entry_size);
if (crc_val != crc_val_org) {
PT_ERROR("Partition entires crc mismatch crc_val= 0x%08x with "
"crc_val_org= 0x%08x\n",
crc_val, crc_val_org);
return 1;
}
gpt_header->entry_array_crc32 = crc_val;
partition_entries = gpt_header->partition_entries;
/* Read GPT Entries */
for (i = 0;
i < (ROUNDUP(max_partition_count, part_entry_cnt)) / part_entry_cnt;
i++) {
ASSERT(partition_count < NUM_PARTITIONS);
data = (new_buffer + (i * block_size));
for (j = 0; j < part_entry_cnt; j++) {
memcpy(&(partition_entries[partition_count].type_guid),
&data[(j * partition_entry_size)], PARTITION_TYPE_GUID_SIZE);
if (partition_entries[partition_count].type_guid[0] == 0x00 &&
partition_entries[partition_count].type_guid[1] == 0x00) {
i = ROUNDUP(max_partition_count, part_entry_cnt);
break;
}
memcpy(&(partition_entries[partition_count].unique_partition_guid),
&data[(j * partition_entry_size) + UNIQUE_GUID_OFFSET],
UNIQUE_PARTITION_GUID_SIZE);
partition_entries[partition_count].first_lba = GET_LLWORD_FROM_BYTE(
&data[(j * partition_entry_size) + FIRST_LBA_OFFSET]);
partition_entries[partition_count].last_lba = GET_LLWORD_FROM_BYTE(
&data[(j * partition_entry_size) + LAST_LBA_OFFSET]);
partition_entries[partition_count].size =
partition_entries[partition_count].last_lba -
partition_entries[partition_count].first_lba + 1;
partition_entries[partition_count].attribute_flag =
GET_LLWORD_FROM_BYTE(
&data[(j * partition_entry_size) + ATTRIBUTE_FLAG_OFFSET]);
memset(&UTF16_name, 0x00, MAX_GPT_NAME_SIZE);
memcpy(UTF16_name,
&data[(j * partition_entry_size) + PARTITION_NAME_OFFSET],
MAX_GPT_NAME_SIZE);
/*
* Currently partition names in *.xml are UTF-8 and lowercase
* Only supporting english for now so removing 2nd byte of UTF-16
*/
for (n = 0; n < MAX_GPT_NAME_SIZE / 2; n++) {
partition_entries[partition_count].name[n] = UTF16_name[n * 2];
}
partition_count++;
}
}
gpt_header->actual_entries_count = partition_count;
return ret;
}
/**
* @brief Get the partition index from header object
*
* @param name
* @param gpt_header
* @return int
*/
int get_partition_index_from_header(const char *name, GPT_header *gpt_header)
{
unsigned int input_string_length = strlen(name);
unsigned n;
char *curr_suffix = NULL;
struct partition_entry *partition_entries = gpt_header->partition_entries;
/* We iterate through the parition entries list,
to find the partition with active slot suffix.
*/
for (n = 0; n < gpt_header->partition_entry_count; n++) {
if (!strncmp((const char *)name,
(const char *)partition_entries[n].name,
input_string_length)) {
curr_suffix =
(char *)(partition_entries[n].name + input_string_length);
/* if partition_entries.name is NULL terminated return the index */
if (*curr_suffix == '\0')
return n;
}
}
return INVALID_PTN;
}
/**
* @brief Get the partition size from header object
*
* @param index
* @param gpt_header
* @param block_size
* @return unsigned long long
*/
unsigned long long get_partition_size_from_header(int index,
GPT_header *gpt_header,
uint32_t block_size)
{
struct partition_entry *partition_entries = gpt_header->partition_entries;
if (index == INVALID_PTN)
return 0;
else {
return partition_entries[index].size * block_size;
}
}
/* Get offset of the partition */
/**
* @brief Get the partition offset from header object
*
* @param index
* @param gpt_header
* @param block_size
* @return unsigned long long
*/
unsigned long long get_partition_offset_from_header(int index,
GPT_header *gpt_header,
uint32_t block_size)
{
struct partition_entry *partition_entries = gpt_header->partition_entries;
if (index == INVALID_PTN)
return 0;
else {
return partition_entries[index].first_lba * block_size;
}
}
/**
* @brief Get the partition info from header object
*
* @param name
* @param gpt_header
* @param block_size
* @return struct partition_info
*/
struct partition_info get_partition_info_from_header(const char *name,
GPT_header *gpt_header,
uint32_t block_size)
{
struct partition_info info = {0};
int index = INVALID_PTN;
if (!name) {
PT_ERROR("Invalid partition name passed\n");
goto out;
}
index = get_partition_index_from_header(name, gpt_header);
if (index != INVALID_PTN) {
info.offset =
get_partition_offset_from_header(index, gpt_header, block_size);
info.size =
get_partition_size_from_header(index, gpt_header, block_size);
} else {
PT_ERROR("Error unable to find partition : [%s]\n", name);
}
out:
return info;
}
/**
* @brief Making every parition is aligned to the sector size
*
* @param buffer
* @param buf_len
* @param block_size
* @param sector_sz
* @param capacity
* @return uint32_t
*/
uint32_t gpt_partition_round(uint8_t *buffer, uint32_t buf_len,
uint32_t block_size, uint32_t sector_sz,
uint64_t capacity)
{
uint32_t ret = 0;
uint32_t offset = 0;
uint32_t gpt_sz = 0;
uint32_t crc_val = 0;
uint64_t size_in_lba = 0;
uint64_t patch_size = 0;
uint8_t *primary_header = NULL;
uint8_t *secondary_header = NULL;
GPT_header gpt_header_pri = {0};
GPT_header gpt_header_bak = {0};
uint64_t last_entry_last_lba = 0;
uint64_t partition_entry_size = 0;
uint64_t max_partition_count = 0;
uint32_t blocks_for_entries = 0;
struct partition_entry *entry = NULL;
uint32_t partition_entry_array_size = 0;
uint32_t gpt_in_sector;
blocks_for_entries = (NUM_PARTITIONS * PARTITION_ENTRY_SIZE) / block_size;
gpt_sz = (GPT_HEADER_BLOCKS + blocks_for_entries) * block_size;
if (buf_len < GPT_HEADER_SIZE * 2 + MIN_PARTITION_ARRAY_SIZE * 2 ||
buf_len < gpt_sz * 2) {
PT_ERROR("ptb buffer len:%u error!\n", buf_len);
return 1;
}
if (!capacity || capacity % block_size != 0) {
PT_ERROR("capacity:%llu error, block_size:%u!\n", capacity, block_size);
return 1;
}
if (!sector_sz || sector_sz % block_size != 0) {
PT_ERROR("sector_sz:%u error, block_size:%u!\n", sector_sz, block_size);
return 1;
}
ret = parse_gpt_table_from_buffer(buffer, buf_len - gpt_sz, &gpt_header_pri,
block_size, false);
ret |= parse_gpt_table_from_buffer(buffer + gpt_sz, buf_len - gpt_sz,
&gpt_header_bak, block_size, true);
if (ret) {
PT_ERROR("ptb check fail!\n");
return 2;
}
gpt_in_sector = round_up(gpt_sz, sector_sz);
max_partition_count = GET_LWORD_FROM_BYTE(&buffer[PARTITION_COUNT_OFFSET]);
partition_entry_size = GET_LWORD_FROM_BYTE(&buffer[PENTRY_SIZE_OFFSET]);
partition_entry_array_size = partition_entry_size * max_partition_count;
if (partition_entry_array_size < MIN_PARTITION_ARRAY_SIZE) {
partition_entry_array_size = MIN_PARTITION_ARRAY_SIZE;
}
offset = partition_entry_array_size * 2;
primary_header = buffer;
secondary_header = buffer + block_size + offset;
for (uint32_t i = 0; i < gpt_header_pri.actual_entries_count; i++) {
entry = &gpt_header_pri.partition_entries[i];
PT_ALWAYS("first_lba:%llu last_lba:%llu\n", entry->first_lba,
entry->last_lba);
size_in_lba = entry->last_lba - entry->first_lba + 1;
if (entry->first_lba <= last_entry_last_lba) {
entry->first_lba = last_entry_last_lba + 1;
}
if ((entry->first_lba * block_size) % sector_sz != 0) {
entry->first_lba =
round_up(entry->first_lba * block_size, sector_sz) / block_size;
}
PUT_LONG_LONG(buffer + block_size + i * partition_entry_size +
FIRST_LBA_OFFSET,
entry->first_lba);
PUT_LONG_LONG(buffer + block_size + i * partition_entry_size +
partition_entry_array_size + FIRST_LBA_OFFSET,
entry->first_lba);
entry->last_lba = size_in_lba + entry->first_lba - 1;
patch_size = (size_in_lba * block_size) % sector_sz;
if (patch_size != 0) {
patch_size = sector_sz - patch_size;
entry->last_lba += patch_size / block_size;
}
if (i == gpt_header_pri.actual_entries_count - 1) {
/* If it is the last partition,
* round_down its size,
* because secondary gpt header should not be in the same sector
* with the last partition */
entry->last_lba = (capacity - gpt_in_sector) / block_size - 1;
PUT_LONG_LONG(primary_header + LAST_USABLE_LBA_OFFSET,
entry->last_lba);
PUT_LONG_LONG(secondary_header + LAST_USABLE_LBA_OFFSET,
entry->last_lba);
if ((entry->last_lba >= entry->first_lba) &&
(entry->last_lba - entry->first_lba < size_in_lba - 1)) {
PT_ERROR("shrink the last entry!\n");
}
}
if (entry->last_lba <= entry->first_lba) {
PT_ERROR("partition size error: first lba:%llu last_lba:%llu!\n",
entry->first_lba, entry->last_lba);
return 3;
}
PUT_LONG_LONG(buffer + block_size + i * partition_entry_size +
LAST_LBA_OFFSET,
entry->last_lba);
PUT_LONG_LONG(buffer + block_size + i * partition_entry_size +
partition_entry_array_size + LAST_LBA_OFFSET,
entry->last_lba);
last_entry_last_lba = entry->last_lba;
PT_ALWAYS("after patch first_lba:%llu last_lba:%llu\n",
entry->first_lba, entry->last_lba);
}
crc_val = crc32(0, primary_header + block_size,
partition_entry_size * max_partition_count);
PUT_LONG(primary_header + PARTITION_CRC_OFFSET, crc_val);
PT_DBG("primary partition entries crc:0x%0x!\n", crc_val);
crc_val = crc32(0, primary_header + block_size + partition_entry_array_size,
partition_entry_size * max_partition_count);
PUT_LONG(secondary_header + PARTITION_CRC_OFFSET, crc_val);
PT_DBG("secondary partition entries crc:0x%0x!\n", crc_val);
PUT_LONG(primary_header + HEADER_CRC_OFFSET, 0);
crc_val = crc32(0, primary_header, GPT_HEADER_SIZE);
PUT_LONG(primary_header + HEADER_CRC_OFFSET, crc_val);
PT_DBG("primary header crc:0x%0x!\n", crc_val);
PUT_LONG(secondary_header + HEADER_CRC_OFFSET, 0);
crc_val = crc32(0, secondary_header, GPT_HEADER_SIZE);
PUT_LONG(secondary_header + HEADER_CRC_OFFSET, crc_val);
PT_DBG("secondary header crc:0x%0x!\n", crc_val);
if (gpt_header_pri.partition_entries) {
vPortFree(gpt_header_pri.partition_entries);
}
if (gpt_header_bak.partition_entries) {
vPortFree(gpt_header_bak.partition_entries);
}
return ret;
}