mirror of
https://gitee.com/Vancouver2017/luban-lite-t3e-pro.git
synced 2025-12-15 19:08:54 +00:00
1039 lines
29 KiB
C
1039 lines
29 KiB
C
/*
|
|
* Copyright (c) 2024, Artinchip Technology Co., Ltd
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Dehuang Wu <dehuang.wu@artinchip.com>
|
|
*/
|
|
|
|
#include <aic_common.h>
|
|
#include <string.h>
|
|
#include <malloc.h>
|
|
#include <aic_partition.h>
|
|
#include <disk_part.h>
|
|
#include <aic_crc32.h>
|
|
|
|
static const efi_guid_t aic_disk_guid = DISK_DEFAULT_GUID;
|
|
static const efi_guid_t partition_basic_data_guid = PARTITION_BASIC_DATA_GUID;
|
|
static struct disk_blk_ops blk_ops = { 0, 0 };
|
|
|
|
void aic_disk_part_set_ops(struct disk_blk_ops *ops)
|
|
{
|
|
blk_ops.blk_write = ops->blk_write;
|
|
blk_ops.blk_read = ops->blk_read;
|
|
}
|
|
|
|
static unsigned long blk_dwrite(struct blk_desc *dev, u64 start, u64 blkcnt,
|
|
void *buf)
|
|
{
|
|
if (blk_ops.blk_write)
|
|
return blk_ops.blk_write(dev, start, blkcnt, buf);
|
|
return 0;
|
|
}
|
|
|
|
static unsigned long blk_dread(struct blk_desc *dev, u64 start, u64 blkcnt,
|
|
const void *buf)
|
|
{
|
|
if (blk_ops.blk_read)
|
|
return blk_ops.blk_read(dev, start, blkcnt, buf);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* efi_crc32() - EFI version of crc32 function
|
|
* @buf: buffer to calculate crc32 of
|
|
* @len - length of buf
|
|
*
|
|
* Description: Returns EFI-style CRC32 value for @buf
|
|
*/
|
|
static inline u32 efi_crc32(const void *buf, u32 len)
|
|
{
|
|
return crc32(0, buf, len);
|
|
}
|
|
|
|
/*
|
|
* Private function prototypes
|
|
*/
|
|
|
|
int pmbr_part_valid(struct partition *part);
|
|
int is_pmbr_valid(legacy_mbr *mbr);
|
|
int is_gpt_valid(struct blk_desc *dev_desc, u64 lba, gpt_header *pgpt_head,
|
|
gpt_entry **pgpt_pte);
|
|
gpt_entry *alloc_read_gpt_entries(struct blk_desc *dev_desc,
|
|
gpt_header *pgpt_head);
|
|
int is_pte_valid(gpt_entry *pte);
|
|
int find_valid_gpt(struct blk_desc *dev_desc, gpt_header *gpt_head,
|
|
gpt_entry **pgpt_pte);
|
|
|
|
static int validate_gpt_header(gpt_header *gpt_h, u64 lba, u64 lastlba)
|
|
{
|
|
u32 crc32_backup = 0;
|
|
u32 calc_crc32;
|
|
|
|
/* Check the GPT header signature */
|
|
if (gpt_h->signature != GPT_HEADER_SIGNATURE_EFI) {
|
|
pr_debug("%s signature is wrong: 0x%llX != 0x%llX\n",
|
|
"GUID Partition Table Header", gpt_h->signature,
|
|
GPT_HEADER_SIGNATURE_EFI);
|
|
return -1;
|
|
}
|
|
|
|
/* Check the GUID Partition Table CRC */
|
|
memcpy(&crc32_backup, &gpt_h->header_crc32, sizeof(crc32_backup));
|
|
memset(&gpt_h->header_crc32, 0, sizeof(gpt_h->header_crc32));
|
|
|
|
calc_crc32 = efi_crc32((const unsigned char *)gpt_h, (gpt_h->header_size));
|
|
|
|
memcpy(&gpt_h->header_crc32, &crc32_backup, sizeof(crc32_backup));
|
|
|
|
if (calc_crc32 != (crc32_backup)) {
|
|
pr_debug("%s CRC is wrong: 0x%x != 0x%x\n", "GUID Partition Table Header",
|
|
(crc32_backup), calc_crc32);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Check that the my_lba entry points to the LBA that contains the GPT
|
|
*/
|
|
if (gpt_h->my_lba != lba) {
|
|
pr_debug("GPT: my_lba incorrect: %llX != %llX\n", gpt_h->my_lba, lba);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Check that the first_usable_lba and that the last_usable_lba are
|
|
* within the disk.
|
|
*/
|
|
if (gpt_h->first_usable_lba > lastlba) {
|
|
pr_debug("GPT: first_usable_lba incorrect: %llX > %llX\n",
|
|
gpt_h->first_usable_lba, lastlba);
|
|
return -1;
|
|
}
|
|
if (gpt_h->last_usable_lba > lastlba) {
|
|
pr_debug("GPT: last_usable_lba incorrect: %llX > %llX\n",
|
|
gpt_h->last_usable_lba, lastlba);
|
|
return -1;
|
|
}
|
|
|
|
pr_debug(
|
|
"GPT: first_usable_lba: %llX last_usable_lba: %llX last lba: %llX n",
|
|
gpt_h->first_usable_lba, gpt_h->last_usable_lba, lastlba);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int validate_gpt_entries(gpt_header *gpt_h, gpt_entry *gpt_e)
|
|
{
|
|
u32 calc_crc32;
|
|
|
|
/* Check the GUID Partition Table Entry Array CRC */
|
|
calc_crc32 = efi_crc32((const unsigned char *)gpt_e,
|
|
(gpt_h->num_partition_entries) *
|
|
(gpt_h->sizeof_partition_entry));
|
|
|
|
if (calc_crc32 != (gpt_h->partition_entry_array_crc32)) {
|
|
pr_debug("%s: 0x%x != 0x%x\n",
|
|
"GUID Partition Table Entry Array CRC is wrong",
|
|
(gpt_h->partition_entry_array_crc32), calc_crc32);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void prepare_backup_gpt_header(gpt_header *gpt_h)
|
|
{
|
|
u32 calc_crc32;
|
|
u64 val;
|
|
|
|
/* recalculate the values for the Backup GPT Header */
|
|
val = gpt_h->my_lba;
|
|
gpt_h->my_lba = gpt_h->alternate_lba;
|
|
gpt_h->alternate_lba = (val);
|
|
gpt_h->partition_entry_lba = gpt_h->last_usable_lba + 1;
|
|
gpt_h->header_crc32 = 0;
|
|
|
|
calc_crc32 = efi_crc32((const unsigned char *)gpt_h, (gpt_h->header_size));
|
|
gpt_h->header_crc32 = (calc_crc32);
|
|
}
|
|
|
|
#define in_range(c, lo, up) ((u8)c >= lo && (u8)c <= up)
|
|
#define isprint(c) in_range(c, 0x20, 0x7f)
|
|
static char *print_efiname(gpt_entry *pte)
|
|
{
|
|
static char name[PARTNAME_SZ + 1];
|
|
int i;
|
|
for (i = 0; i < PARTNAME_SZ; i++) {
|
|
u8 c;
|
|
c = pte->partition_name[i] & 0xff;
|
|
c = (c && !isprint(c)) ? '.' : c;
|
|
name[i] = c;
|
|
}
|
|
name[PARTNAME_SZ] = 0;
|
|
return name;
|
|
}
|
|
|
|
static void print_uuid_bin(const unsigned char *uuid_bin, int guid_fmt)
|
|
{
|
|
const u8 uuid_char_order[16] = { 0, 1, 2, 3, 4, 5, 6, 7,
|
|
8, 9, 10, 11, 12, 13, 14, 15 };
|
|
const u8 guid_char_order[16] = { 3, 2, 1, 0, 5, 4, 7, 6,
|
|
8, 9, 10, 11, 12, 13, 14, 15 };
|
|
const u8 *char_order;
|
|
int i;
|
|
|
|
/*
|
|
* UUID and GUID bin data - always in big endian:
|
|
* 4B-2B-2B-2B-6B
|
|
* be be be be be
|
|
*/
|
|
if (guid_fmt)
|
|
char_order = guid_char_order;
|
|
else
|
|
char_order = uuid_char_order;
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
printf("%02x", uuid_bin[char_order[i]]);
|
|
switch (i) {
|
|
case 3:
|
|
case 5:
|
|
case 7:
|
|
case 9:
|
|
printf("-");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int aic_disk_dump_gpt_parts(struct blk_desc *dev_desc)
|
|
{
|
|
u8 gpt_buf[512];
|
|
gpt_header *gpt_head = (void *)gpt_buf;
|
|
gpt_entry *gpt_pte = NULL;
|
|
int i = 0;
|
|
unsigned char *uuid_bin;
|
|
|
|
/* This function validates AND fills in the GPT header and PTE */
|
|
if (find_valid_gpt(dev_desc, gpt_head, &gpt_pte) != 1)
|
|
return 0;
|
|
|
|
pr_debug("%s: gpt-entry at %p\n", __func__, gpt_pte);
|
|
|
|
printf("Part\tStart LBA\tEnd LBA\t\tName\n");
|
|
printf("\tAttributes\n");
|
|
printf("\tType GUID\n");
|
|
printf("\tPartition GUID\n");
|
|
|
|
for (i = 0; i < (gpt_head->num_partition_entries); i++) {
|
|
/* Stop at the first non valid PTE */
|
|
if (!is_pte_valid(&gpt_pte[i]))
|
|
break;
|
|
|
|
printf("%3d\t0x%08llx\t0x%08llx\t\"%s\"\n", (i + 1),
|
|
(gpt_pte[i].starting_lba), (gpt_pte[i].ending_lba),
|
|
print_efiname(&gpt_pte[i]));
|
|
printf("\tattrs:\t0x%016llx\n", gpt_pte[i].attributes.raw);
|
|
uuid_bin = (unsigned char *)gpt_pte[i].partition_type_guid.b;
|
|
printf("\ttype: ");
|
|
print_uuid_bin(uuid_bin, 0);
|
|
printf("\n");
|
|
uuid_bin = (unsigned char *)gpt_pte[i].unique_partition_guid.b;
|
|
printf("\tguid: ");
|
|
print_uuid_bin(uuid_bin, 1);
|
|
printf("\n");
|
|
}
|
|
|
|
/* Remember to free pte */
|
|
free(gpt_pte);
|
|
return i;
|
|
}
|
|
|
|
int aic_disk_dump_mbr_parts(struct blk_desc *dev_desc)
|
|
{
|
|
u8 mbr_buf[512];
|
|
legacy_mbr *p_mbr = (void *)mbr_buf;
|
|
struct partition *pp;
|
|
int i;
|
|
|
|
if (blk_dread(dev_desc, 0, 1, p_mbr) != 1) {
|
|
pr_err("** Can't read from device **\n");
|
|
return 0;
|
|
}
|
|
|
|
if (p_mbr->signature != MSDOS_MBR_SIGNATURE) {
|
|
pr_err("** Not MBR sector **\n");
|
|
return 0;
|
|
}
|
|
|
|
if (is_pmbr_valid(p_mbr))
|
|
return 0;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
pp = &p_mbr->partition_record[i];
|
|
if ((pp->boot_ind != 0x00) && (pp->boot_ind != 0x80)) {
|
|
break;
|
|
}
|
|
printf("Partition %d\n", i + 1);
|
|
printf(" boot_ind = 0x%x\n", pp->boot_ind);
|
|
printf(" head = 0x%x\n", pp->head);
|
|
printf(" sector = 0x%x\n", pp->sector);
|
|
printf(" cyl = 0x%x\n", pp->cyl);
|
|
printf(" sys_ind = 0x%x\n", pp->sys_ind);
|
|
printf(" end_head = 0x%x\n", pp->end_head);
|
|
printf(" end_sector = 0x%x\n", pp->end_sector);
|
|
printf(" end_cyl = 0x%x\n", pp->end_cyl);
|
|
printf(" start_sect = 0x%x\n", pp->start_sect);
|
|
printf(" nr_sects = 0x%x\n", pp->nr_sects);
|
|
}
|
|
|
|
return i;
|
|
}
|
|
void aic_disk_dump_parts(struct blk_desc *dev_desc)
|
|
{
|
|
int ret;
|
|
|
|
ret = aic_disk_dump_mbr_parts(dev_desc);
|
|
if (ret)
|
|
return;
|
|
aic_disk_dump_gpt_parts(dev_desc);
|
|
}
|
|
|
|
struct aic_partition *aic_disk_get_parts(struct blk_desc *dev_desc)
|
|
{
|
|
struct aic_partition *ret;
|
|
|
|
ret = aic_disk_get_gpt_parts(dev_desc);
|
|
if (ret)
|
|
return ret;
|
|
return aic_disk_get_mbr_parts(dev_desc);
|
|
}
|
|
|
|
struct aic_partition *aic_disk_get_mbr_parts(struct blk_desc *dev_desc)
|
|
{
|
|
u8 mbr_buf[512];
|
|
legacy_mbr *p_mbr = (void *)mbr_buf;
|
|
struct partition *pp;
|
|
struct aic_partition *parts, *p, *n;
|
|
int i;
|
|
|
|
if (blk_dread(dev_desc, 0, 1, p_mbr) != 1) {
|
|
pr_err("** Can't read from device **\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (p_mbr->signature != MSDOS_MBR_SIGNATURE) {
|
|
pr_err("** Not MBR sector **\n");
|
|
return NULL;
|
|
}
|
|
|
|
parts = p = n = NULL;
|
|
for (i = 0; i < 4; i++) {
|
|
pp = &p_mbr->partition_record[i];
|
|
if ((pp->boot_ind != 0x00) && (pp->boot_ind != 0x80)) {
|
|
break;
|
|
}
|
|
if (pp->start_sect == 0)
|
|
break;
|
|
|
|
n = malloc(sizeof(*n));
|
|
if (!n) {
|
|
pr_err("%s, malloc buffer for partition failed.\n", __func__);
|
|
goto err;
|
|
}
|
|
memset(n, 0, sizeof(*n));
|
|
n->start = pp->start_sect * dev_desc->blksz;
|
|
n->size = (u64)pp->nr_sects * dev_desc->blksz;
|
|
|
|
if (parts == NULL) {
|
|
parts = n;
|
|
}
|
|
if (p) {
|
|
p->next = n;
|
|
p = p->next;
|
|
} else {
|
|
p = n;
|
|
}
|
|
}
|
|
|
|
return parts;
|
|
err:
|
|
if (parts)
|
|
aic_part_free(parts);
|
|
return NULL;
|
|
}
|
|
|
|
struct aic_partition *aic_disk_get_gpt_parts(struct blk_desc *dev_desc)
|
|
{
|
|
struct aic_partition *parts, *p, *n;
|
|
u8 gpt_buf[512];
|
|
gpt_header *gpt_head = (void *)gpt_buf;
|
|
gpt_entry *gpt_pte = NULL;
|
|
int i;
|
|
|
|
/* This function validates AND fills in the GPT header and PTE */
|
|
if (find_valid_gpt(dev_desc, gpt_head, &gpt_pte) != 1)
|
|
return NULL;
|
|
|
|
parts = NULL;
|
|
p = NULL;
|
|
for (i = 0; i < (gpt_head->num_partition_entries); i++) {
|
|
if (!is_pte_valid(&gpt_pte[i])) {
|
|
pr_debug("%s: *** Invalid partition number %d ***\n", __func__, i);
|
|
break;
|
|
}
|
|
n = malloc(sizeof(*n));
|
|
if (!n) {
|
|
pr_err("%s, malloc buffer for partition failed.\n", __func__);
|
|
goto err;
|
|
}
|
|
memset(n, 0, sizeof(*n));
|
|
|
|
/* The 'u64' casting may limit the maximum disk size to 2 TB */
|
|
n->start = gpt_pte[i].starting_lba * dev_desc->blksz;
|
|
|
|
/* The ending LBA is inclusive, to calculate size, add 1 to it */
|
|
n->size = (gpt_pte[i].ending_lba + 1 - gpt_pte[i].starting_lba) *
|
|
dev_desc->blksz;
|
|
|
|
snprintf((char *)n->name, sizeof(n->name), "%s",
|
|
print_efiname(&gpt_pte[i]));
|
|
if (parts == NULL) {
|
|
parts = n;
|
|
}
|
|
if (p) {
|
|
p->next = n;
|
|
p = p->next;
|
|
} else {
|
|
p = n;
|
|
}
|
|
}
|
|
|
|
n = parts;
|
|
/* Remember to free pte */
|
|
free(gpt_pte);
|
|
return parts;
|
|
|
|
err:
|
|
if (parts)
|
|
aic_part_free(parts);
|
|
free(gpt_pte);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* set_protective_mbr(): Set the EFI protective MBR
|
|
* @param dev_desc - block device descriptor
|
|
*
|
|
* @return - zero on success, otherwise error
|
|
*/
|
|
static int set_protective_mbr(struct blk_desc *dev_desc)
|
|
{
|
|
/* Setup the Protective MBR */
|
|
u8 mbr_buf[512];
|
|
legacy_mbr *p_mbr = (void *)mbr_buf;
|
|
|
|
if (p_mbr == NULL) {
|
|
printf("%s: calloc failed!\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
/* Read MBR to backup boot code if it exists */
|
|
if (blk_dread(dev_desc, 0, 1, p_mbr) != 1) {
|
|
pr_err("** Can't read from device **\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Clear all data in MBR except of backed up boot code */
|
|
memset((char *)p_mbr + MSDOS_MBR_BOOT_CODE_SIZE, 0,
|
|
sizeof(*p_mbr) - MSDOS_MBR_BOOT_CODE_SIZE);
|
|
|
|
/* Append signature */
|
|
p_mbr->signature = MSDOS_MBR_SIGNATURE;
|
|
p_mbr->partition_record[0].sys_ind = EFI_PMBR_OSTYPE_EFI_GPT;
|
|
p_mbr->partition_record[0].start_sect = 1;
|
|
p_mbr->partition_record[0].nr_sects = (u32)dev_desc->lba_count - 1;
|
|
|
|
/* Write MBR sector to the MMC device */
|
|
if (blk_dwrite(dev_desc, 0, 1, p_mbr) != 1) {
|
|
printf("** Can't write to device **\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int write_gpt_table(struct blk_desc *dev_desc, gpt_header *gpt_h,
|
|
gpt_entry *gpt_e)
|
|
{
|
|
const int pte_blk_cnt =
|
|
PAD_COUNT((gpt_h->num_partition_entries * sizeof(gpt_entry)), 512);
|
|
u32 calc_crc32;
|
|
|
|
pr_debug("max lba: %x\n", (u32)dev_desc->lba_count);
|
|
/* Setup the Protective MBR */
|
|
if (set_protective_mbr(dev_desc) < 0)
|
|
goto err;
|
|
|
|
/* Generate CRC for the Primary GPT Header */
|
|
calc_crc32 = efi_crc32((const unsigned char *)gpt_e,
|
|
(gpt_h->num_partition_entries) *
|
|
(gpt_h->sizeof_partition_entry));
|
|
gpt_h->partition_entry_array_crc32 = (calc_crc32);
|
|
|
|
calc_crc32 = efi_crc32((const unsigned char *)gpt_h, (gpt_h->header_size));
|
|
gpt_h->header_crc32 = (calc_crc32);
|
|
|
|
/* Write the First GPT to the block right after the Legacy MBR */
|
|
if (blk_dwrite(dev_desc, 1, 1, gpt_h) != 1)
|
|
goto err;
|
|
|
|
if (blk_dwrite(dev_desc, gpt_h->partition_entry_lba, pte_blk_cnt, gpt_e) !=
|
|
pte_blk_cnt)
|
|
goto err;
|
|
|
|
prepare_backup_gpt_header(gpt_h);
|
|
|
|
if (blk_dwrite(dev_desc, (u64)gpt_h->last_usable_lba + 1, pte_blk_cnt,
|
|
gpt_e) != pte_blk_cnt)
|
|
goto err;
|
|
|
|
if (blk_dwrite(dev_desc, (u64)(gpt_h->my_lba), 1, gpt_h) != 1)
|
|
goto err;
|
|
|
|
pr_debug("GPT successfully written to block device!\n");
|
|
return 0;
|
|
|
|
err:
|
|
printf("** Can't write to device **\n");
|
|
return -1;
|
|
}
|
|
|
|
int gpt_fill_pte(struct blk_desc *dev_desc, gpt_header *gpt_h, gpt_entry *gpt_e,
|
|
struct aic_partition *partitions)
|
|
{
|
|
u64 offset = (u64)(gpt_h->first_usable_lba);
|
|
u64 last_usable_lba = (u64)(gpt_h->last_usable_lba);
|
|
int i, k;
|
|
u8 *p;
|
|
struct aic_partition *part;
|
|
size_t efiname_len, dosname_len;
|
|
size_t hdr_start = gpt_h->my_lba;
|
|
size_t hdr_end = hdr_start + 1;
|
|
size_t pte_start = gpt_h->partition_entry_lba;
|
|
size_t pte_end = pte_start + gpt_h->num_partition_entries *
|
|
gpt_h->sizeof_partition_entry /
|
|
dev_desc->blksz;
|
|
|
|
part = partitions;
|
|
i = 0;
|
|
while (part) {
|
|
/* partition starting lba */
|
|
u64 start = part->start / dev_desc->blksz;
|
|
u64 size = part->size / dev_desc->blksz;
|
|
|
|
if (start) {
|
|
offset = start + size;
|
|
} else {
|
|
start = offset;
|
|
offset += size;
|
|
}
|
|
|
|
/*
|
|
* If our partition overlaps with either the GPT
|
|
* header, or the partition entry, reject it.
|
|
*/
|
|
if (((start < hdr_end && hdr_start < (start + size)) ||
|
|
(start < pte_end && pte_start < (start + size)))) {
|
|
printf("Partition overlap\n");
|
|
return -1;
|
|
}
|
|
|
|
gpt_e[i].starting_lba = (start);
|
|
|
|
if (offset > (last_usable_lba + 1)) {
|
|
printf("Partitions layout exceds disk size\n");
|
|
return -1;
|
|
}
|
|
/* partition ending lba */
|
|
if ((part->next == NULL) && (size == 0))
|
|
/* extend the last partition to maximuim */
|
|
gpt_e[i].ending_lba = gpt_h->last_usable_lba;
|
|
else
|
|
gpt_e[i].ending_lba = (offset - 1);
|
|
|
|
/* partition type GUID */
|
|
memcpy(gpt_e[i].partition_type_guid.b, &partition_basic_data_guid, 16);
|
|
memcpy(gpt_e[i].unique_partition_guid.b, &partition_basic_data_guid, 16);
|
|
p = &gpt_e[i].unique_partition_guid.b[15];
|
|
*p += (i + 1);
|
|
|
|
/* partition attributes */
|
|
memset(&gpt_e[i].attributes, 0, sizeof(gpt_entry_attributes));
|
|
|
|
/* partition name */
|
|
efiname_len = sizeof(gpt_e[i].partition_name) / sizeof(u16);
|
|
dosname_len = sizeof(part->name);
|
|
|
|
memset(gpt_e[i].partition_name, 0, sizeof(gpt_e[i].partition_name));
|
|
|
|
for (k = 0; k < min(dosname_len, efiname_len); k++)
|
|
gpt_e[i].partition_name[k] = (u16)(part->name[k]);
|
|
|
|
part = part->next;
|
|
i++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static u32 partition_entries_offset(struct blk_desc *dev_desc)
|
|
{
|
|
u32 offset_blks = 2;
|
|
|
|
/*
|
|
* The earliest LBA this can be at is LBA#2 (i.e. right behind
|
|
* the (protective) MBR and the GPT header.
|
|
*/
|
|
if (offset_blks < 2)
|
|
offset_blks = 2;
|
|
|
|
return offset_blks;
|
|
}
|
|
|
|
int gpt_fill_header(struct blk_desc *dev_desc, gpt_header *gpt_h, int part_cnt)
|
|
{
|
|
gpt_h->signature = (GPT_HEADER_SIGNATURE_EFI);
|
|
gpt_h->revision = (GPT_HEADER_REVISION_V1);
|
|
gpt_h->header_size = (sizeof(gpt_header));
|
|
gpt_h->my_lba = (1);
|
|
gpt_h->alternate_lba = (dev_desc->lba_count - 1);
|
|
gpt_h->last_usable_lba = (dev_desc->lba_count - 34);
|
|
gpt_h->partition_entry_lba = (partition_entries_offset(dev_desc));
|
|
gpt_h->first_usable_lba = ((gpt_h->partition_entry_lba) + 32);
|
|
gpt_h->num_partition_entries = (part_cnt);
|
|
gpt_h->sizeof_partition_entry = (sizeof(gpt_entry));
|
|
gpt_h->header_crc32 = 0;
|
|
gpt_h->partition_entry_array_crc32 = 0;
|
|
|
|
memcpy(gpt_h->disk_guid.b, &aic_disk_guid, 16);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int aic_disk_write_gpt(struct blk_desc *dev_desc, struct aic_partition *parts)
|
|
{
|
|
gpt_header *gpt_h;
|
|
gpt_entry *gpt_e;
|
|
int ret, size, cnt;
|
|
struct aic_partition *p;
|
|
|
|
p = parts;
|
|
cnt = 0;
|
|
while (p) {
|
|
cnt++;
|
|
p = p->next;
|
|
}
|
|
if (cnt > GPT_ENTRY_NUMBERS) {
|
|
pr_err("Too many partition entries.\n");
|
|
return -1;
|
|
}
|
|
|
|
size = PAD_SIZE(sizeof(gpt_header), 512);
|
|
gpt_h = malloc(size);
|
|
if (gpt_h == NULL) {
|
|
printf("%s: calloc failed!\n", __func__);
|
|
return -1;
|
|
}
|
|
memset(gpt_h, 0, size);
|
|
|
|
size = PAD_SIZE(GPT_ENTRY_NUMBERS * sizeof(gpt_entry), 512);
|
|
gpt_e = malloc(size);
|
|
if (gpt_e == NULL) {
|
|
printf("%s: calloc failed!\n", __func__);
|
|
free(gpt_h);
|
|
return -1;
|
|
}
|
|
memset(gpt_e, 0, size);
|
|
|
|
/* Generate Primary GPT header (LBA1) */
|
|
ret = gpt_fill_header(dev_desc, gpt_h, cnt);
|
|
if (ret)
|
|
goto err;
|
|
|
|
/* Generate partition entries */
|
|
ret = gpt_fill_pte(dev_desc, gpt_h, gpt_e, parts);
|
|
if (ret)
|
|
goto err;
|
|
|
|
/* Write GPT partition table */
|
|
ret = write_gpt_table(dev_desc, gpt_h, gpt_e);
|
|
|
|
err:
|
|
free(gpt_e);
|
|
free(gpt_h);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* gpt_convert_efi_name_to_char() - convert u16 string to char string
|
|
*
|
|
* TODO: this conversion only supports ANSI characters
|
|
*
|
|
* @s: target buffer
|
|
* @es: u16 string to be converted
|
|
* @n: size of target buffer
|
|
*/
|
|
static void gpt_convert_efi_name_to_char(char *s, void *es, int n)
|
|
{
|
|
char *ess = es;
|
|
int i, j;
|
|
|
|
memset(s, '\0', n);
|
|
|
|
for (i = 0, j = 0; j < n; i += 2, j++) {
|
|
s[j] = ess[i];
|
|
if (!ess[i])
|
|
return;
|
|
}
|
|
}
|
|
|
|
int gpt_verify_headers(struct blk_desc *dev_desc, gpt_header *gpt_head,
|
|
gpt_entry **gpt_pte)
|
|
{
|
|
/*
|
|
* This function validates AND
|
|
* fills in the GPT header and PTE
|
|
*/
|
|
if (is_gpt_valid(dev_desc, GPT_PRIMARY_PARTITION_TABLE_LBA, gpt_head,
|
|
gpt_pte) != 1) {
|
|
printf("%s: *** ERROR: Invalid GPT ***\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
/* Free pte before allocating again */
|
|
free(*gpt_pte);
|
|
|
|
/*
|
|
* Check that the alternate_lba entry points to the last LBA
|
|
*/
|
|
if ((gpt_head->alternate_lba) != (dev_desc->lba_count - 1)) {
|
|
printf("%s: *** ERROR: Misplaced Backup GPT ***\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
if (is_gpt_valid(dev_desc, (dev_desc->lba_count - 1), gpt_head, gpt_pte) !=
|
|
1) {
|
|
printf("%s: *** ERROR: Invalid Backup GPT ***\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int gpt_verify_partitions(struct blk_desc *dev_desc,
|
|
struct aic_partition *partitions,
|
|
gpt_header *gpt_head, gpt_entry **gpt_pte)
|
|
{
|
|
struct aic_partition *part;
|
|
char efi_str[PARTNAME_SZ + 1];
|
|
u64 gpt_part_size;
|
|
gpt_entry *gpt_e;
|
|
int ret, i;
|
|
|
|
ret = gpt_verify_headers(dev_desc, gpt_head, gpt_pte);
|
|
if (ret)
|
|
return ret;
|
|
|
|
gpt_e = *gpt_pte;
|
|
|
|
part = partitions;
|
|
i = 0;
|
|
while (part) {
|
|
if (i == gpt_head->num_partition_entries) {
|
|
pr_err("More partitions than allowed!\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Check if GPT and ENV partition names match */
|
|
gpt_convert_efi_name_to_char(efi_str, gpt_e[i].partition_name,
|
|
PARTNAME_SZ + 1);
|
|
|
|
pr_debug("%s: part: %2d name - GPT: %16s, ENV: %16s ", __func__, i,
|
|
efi_str, part->name);
|
|
|
|
if (strncmp(efi_str, (char *)part->name, sizeof(part->name))) {
|
|
pr_err("Partition name: %s does not match %s!\n", efi_str,
|
|
(char *)part->name);
|
|
return -1;
|
|
}
|
|
|
|
/* Check if GPT and ENV sizes match */
|
|
gpt_part_size = (gpt_e[i].ending_lba) - (gpt_e[i].starting_lba) + 1;
|
|
pr_debug("size(LBA) - GPT: %8llu, ENV: %8llu ",
|
|
(unsigned long long)gpt_part_size,
|
|
(unsigned long long)part->size);
|
|
|
|
if ((gpt_part_size) != part->size) {
|
|
/* We do not check the extend partition size */
|
|
if ((part->next == NULL) && (part->size == 0))
|
|
continue;
|
|
|
|
pr_err("Partition %s size: %llu does not match %llu!\n", efi_str,
|
|
(unsigned long long)gpt_part_size,
|
|
(unsigned long long)part->size);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Start address is optional - check only if provided
|
|
* in '$partition' variable
|
|
*/
|
|
if (!part->start) {
|
|
pr_debug("\n");
|
|
continue;
|
|
}
|
|
|
|
/* Check if GPT and ENV start LBAs match */
|
|
pr_debug("start LBA - GPT: %8llu, ENV: %8llu\n",
|
|
(gpt_e[i].starting_lba), (unsigned long long)part->start);
|
|
|
|
if ((gpt_e[i].starting_lba) != part->start) {
|
|
pr_err("Partition %s start: %llu does not match %llu!\n", efi_str,
|
|
(gpt_e[i].starting_lba), (unsigned long long)part->start);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int is_valid_gpt_buf(struct blk_desc *dev_desc, void *buf)
|
|
{
|
|
gpt_header *gpt_h;
|
|
gpt_entry *gpt_e;
|
|
|
|
/* determine start of GPT Header in the buffer */
|
|
gpt_h = buf + (GPT_PRIMARY_PARTITION_TABLE_LBA * dev_desc->blksz);
|
|
if (validate_gpt_header(gpt_h, GPT_PRIMARY_PARTITION_TABLE_LBA,
|
|
dev_desc->lba_count))
|
|
return -1;
|
|
|
|
/* determine start of GPT Entries in the buffer */
|
|
gpt_e = buf + ((gpt_h->partition_entry_lba) * 512);
|
|
if (validate_gpt_entries(gpt_h, gpt_e))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Private functions
|
|
*/
|
|
/*
|
|
* pmbr_part_valid(): Check for EFI partition signature
|
|
*
|
|
* Returns: 1 if EFI GPT partition type is found.
|
|
*/
|
|
int pmbr_part_valid(struct partition *part)
|
|
{
|
|
u32 val;
|
|
u8 *p;
|
|
|
|
p = (void *)&part->start_sect;
|
|
val = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
|
|
if ((part->sys_ind == EFI_PMBR_OSTYPE_EFI_GPT) && (val == 1)) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* is_pmbr_valid(): test Protective MBR for validity
|
|
*
|
|
* Returns: 1 if PMBR is valid, 0 otherwise.
|
|
* Validity depends on two things:
|
|
* 1) MSDOS signature is in the last two bytes of the MBR
|
|
* 2) One partition of type 0xEE is found, checked by pmbr_part_valid()
|
|
*/
|
|
int is_pmbr_valid(legacy_mbr *mbr)
|
|
{
|
|
int i = 0;
|
|
|
|
if (!mbr || (mbr->signature) != MSDOS_MBR_SIGNATURE)
|
|
return 0;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
if (pmbr_part_valid(&mbr->partition_record[i])) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* is_gpt_valid() - tests one GPT header and PTEs for validity
|
|
*
|
|
* lba is the logical block address of the GPT header to test
|
|
* gpt is a GPT header ptr, filled on return.
|
|
* ptes is a PTEs ptr, filled on return.
|
|
*
|
|
* Description: returns 1 if valid, 0 on error, 2 if ignored header
|
|
* If valid, returns pointers to PTEs.
|
|
*/
|
|
int is_gpt_valid(struct blk_desc *dev_desc, u64 lba, gpt_header *pgpt_head,
|
|
gpt_entry **pgpt_pte)
|
|
{
|
|
u8 mbr_buf[512];
|
|
legacy_mbr *mbr = (void *)mbr_buf;
|
|
/* Confirm valid arguments prior to allocation. */
|
|
if (!dev_desc || !pgpt_head) {
|
|
printf("%s: Invalid Argument(s)\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
/* Read MBR Header from device */
|
|
if (blk_dread(dev_desc, 0, 1, (ulong *)mbr) != 1) {
|
|
printf("*** ERROR: Can't read MBR header ***\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Read GPT Header from device */
|
|
if (blk_dread(dev_desc, (u64)lba, 1, pgpt_head) != 1) {
|
|
printf("*** ERROR: Can't read GPT header ***\n");
|
|
return 0;
|
|
}
|
|
|
|
if (validate_gpt_header(pgpt_head, (u64)lba, dev_desc->lba_count))
|
|
return 0;
|
|
|
|
/* Read and allocate Partition Table Entries */
|
|
*pgpt_pte = alloc_read_gpt_entries(dev_desc, pgpt_head);
|
|
if (*pgpt_pte == NULL) {
|
|
printf("GPT: Failed to allocate memory for PTE\n");
|
|
return 0;
|
|
}
|
|
|
|
if (validate_gpt_entries(pgpt_head, *pgpt_pte)) {
|
|
free(*pgpt_pte);
|
|
return 0;
|
|
}
|
|
|
|
/* We're done, all's well */
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* find_valid_gpt() - finds a valid GPT header and PTEs
|
|
*
|
|
* gpt is a GPT header ptr, filled on return.
|
|
* ptes is a PTEs ptr, filled on return.
|
|
*
|
|
* Description: returns 1 if found a valid gpt, 0 on error.
|
|
* If valid, returns pointers to PTEs.
|
|
*/
|
|
int find_valid_gpt(struct blk_desc *dev_desc, gpt_header *gpt_head,
|
|
gpt_entry **pgpt_pte)
|
|
{
|
|
int r;
|
|
|
|
r = is_gpt_valid(dev_desc, GPT_PRIMARY_PARTITION_TABLE_LBA, gpt_head,
|
|
pgpt_pte);
|
|
|
|
if (r != 1) {
|
|
if (r != 2)
|
|
pr_debug("%s: *** ERROR: Invalid GPT ***\n", __func__);
|
|
|
|
if (is_gpt_valid(dev_desc, (dev_desc->lba_count - 1), gpt_head,
|
|
pgpt_pte) != 1) {
|
|
pr_debug("%s: *** ERROR: Invalid Backup GPT ***\n", __func__);
|
|
return 0;
|
|
}
|
|
if (r != 2)
|
|
pr_debug("%s: *** Using Backup GPT ***\n", __func__);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* alloc_read_gpt_entries(): reads partition entries from disk
|
|
* @dev_desc
|
|
* @gpt - GPT header
|
|
*
|
|
* Description: Returns ptes on success, NULL on error.
|
|
* Allocates space for PTEs based on information found in @gpt.
|
|
* Notes: remember to free pte when you're done!
|
|
*/
|
|
gpt_entry *alloc_read_gpt_entries(struct blk_desc *dev_desc,
|
|
gpt_header *pgpt_head)
|
|
{
|
|
size_t count = 0, blk_cnt;
|
|
u64 blk;
|
|
gpt_entry *pte = NULL;
|
|
|
|
if (!dev_desc || !pgpt_head) {
|
|
printf("%s: Invalid Argument(s)\n", __func__);
|
|
return NULL;
|
|
}
|
|
|
|
count = (pgpt_head->num_partition_entries) *
|
|
(pgpt_head->sizeof_partition_entry);
|
|
|
|
pr_debug("%s: count = %u * %u = %lu\n", __func__,
|
|
(u32)(pgpt_head->num_partition_entries),
|
|
(u32)(pgpt_head->sizeof_partition_entry), (ulong)count);
|
|
|
|
/* Allocate memory for PTE, remember to FREE */
|
|
if (count != 0) {
|
|
pte = malloc(PAD_SIZE(count, 512));
|
|
}
|
|
|
|
if (count == 0 || pte == NULL) {
|
|
printf("%s: ERROR: Can't allocate %#lX bytes for GPT Entries\n",
|
|
__func__, (ulong)count);
|
|
return NULL;
|
|
}
|
|
|
|
/* Read GPT Entries from device */
|
|
blk = (pgpt_head->partition_entry_lba);
|
|
blk_cnt = PAD_COUNT(count, 512);
|
|
if (blk_dread(dev_desc, blk, (u64)blk_cnt, pte) != blk_cnt) {
|
|
printf("*** ERROR: Can't read GPT Entries ***\n");
|
|
free(pte);
|
|
return NULL;
|
|
}
|
|
return pte;
|
|
}
|
|
|
|
/**
|
|
* is_pte_valid(): validates a single Partition Table Entry
|
|
* @gpt_entry - Pointer to a single Partition Table Entry
|
|
*
|
|
* Description: returns 1 if valid, 0 on error.
|
|
*/
|
|
int is_pte_valid(gpt_entry *pte)
|
|
{
|
|
efi_guid_t unused_guid;
|
|
|
|
if (!pte) {
|
|
printf("%s: Invalid Argument(s)\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
/* Only one validation for now:
|
|
* The GUID Partition Type != Unused Entry (ALL-ZERO)
|
|
*/
|
|
memset(unused_guid.b, 0, sizeof(unused_guid.b));
|
|
|
|
if (memcmp(pte->partition_type_guid.b, unused_guid.b,
|
|
sizeof(unused_guid.b)) == 0) {
|
|
pr_debug("%s: Found an unused PTE GUID at 0x%08X\n", __func__,
|
|
(unsigned int)(uintptr_t)pte);
|
|
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|