2883 lines
86 KiB
C
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;
|
|
}
|