// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2019 Amlogic, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include "mmc_private.h" #include #include #include #include #include DECLARE_GLOBAL_DATA_PTR; /* using mbr*/ #if (CONFIG_PTBL_MBR) /* cmpare partition name? */ #define CONFIG_CMP_PARTNAME (0) /* cmpare partition mask */ #define CONFIG_CMP_PARTMASK (0) #else #define CONFIG_CMP_PARTNAME (1) #define CONFIG_CMP_PARTMASK (1) #endif /* debug info*/ #define CONFIG_MPT_DEBUG (0) #define GPT_SIZE 0x4400 #define apt_err(fmt, ...) printf( "%s()-%d: " fmt , \ __func__, __LINE__, ##__VA_ARGS__) #define apt_wrn(fmt, ...) printf( "%s()-%d: " fmt , \ __func__, __LINE__, ##__VA_ARGS__) #if (CONFIG_MPT_DEBUG) /* for detail debug info */ #define apt_info(fmt, ...) printf( "%s()-%d: " fmt , \ __func__, __LINE__, ##__VA_ARGS__) #else #define apt_info(fmt, ...) #endif /* creat MBR for emmc */ #define MAX_PNAME_LEN MAX_MMC_PART_NAME_LEN #define MAX_PART_COUNT MAX_MMC_PART_NUM /* Global offset of reserved partition is 36MBytes since MMC_BOOT_PARTITION_RESERVED is 32MBytes and MMC_BOOT_DEVICE_SIZE is 4MBytes. MMC_RESERVED_SIZE is 64MBytes for now. layout detail inside reserved partition. 0x000000 - 0x003fff: partition table 0x004000 - 0x03ffff: storage key area (16k offset & 256k size) 0x400000 - 0x47ffff: dtb area (4M offset & 512k size) 0x480000 - 64MBytes: resv for other usage. ... */ /* #define RSV_DTB_OFFSET_GLB (SZ_1M*40) #define RSV_DTB_SIZE (512*1024UL) #define RSV_PTBL_OFFSET (SZ_1M*0) #define RSV_PTBL_SIZE (16*1024UL) #define RSV_SKEY_OFFSET (16*1024UL) #define RSV_SKEY_SIZE (256*1024UL) #define RSV_DTB_OFFSET (SZ_1M*4) */ /* virtual partitions which are in "reserved" */ #define MAX_MMC_VIRTUAL_PART_CNT (5) /* BinaryLayout of partition table stored in rsv area */ struct ptbl_rsv { char magic[4]; /* MPT */ unsigned char version[12]; /* binary version */ int count; /* partition count in using */ int checksum; struct partitions partitions[MAX_MMC_PART_NUM]; }; /* partition table for innor usage*/ struct _iptbl { struct partitions *partitions; int count; /* partition count in use */ }; unsigned device_boot_flag = 0xff; extern bool is_partition_checked; #ifndef CONFIG_AML_MMC_INHERENT_PART /* fixme, name should be changed as aml_inherent_ptbl */ struct partitions emmc_partition_table[] = { PARTITION_ELEMENT(MMC_BOOT_NAME, MMC_BOOT_DEVICE_SIZE, 0), PARTITION_ELEMENT(MMC_RESERVED_NAME, MMC_RESERVED_SIZE, 0), /* prior partitions, same partition name with dts*/ /* partition size will be override by dts*/ PARTITION_ELEMENT(MMC_CACHE_NAME, 0, 0), PARTITION_ELEMENT(MMC_ENV_NAME, MMC_ENV_SIZE, 0), #ifdef CONFIG_AB_UPDATE PARTITION_ELEMENT(FIP_A_NAME, FIP_SIZE, 0), PARTITION_ELEMENT(FIP_B_NAME, FIP_SIZE, 0), #endif }; struct virtual_partition virtual_partition_table[] = { /* partition for name idx, off & size will not be used! */ #if (CONFIG_PTBL_MBR) VIRTUAL_PARTITION_ELEMENT(MMC_MBR_NAME, MMC_MBR_OFFSET, MMC_MBR_SIZE), #endif VIRTUAL_PARTITION_ELEMENT(MMC_BOOT_NAME0, 0, 0), VIRTUAL_PARTITION_ELEMENT(MMC_BOOT_NAME1, 0, 0), /* virtual partition in reserved partition, take care off and size */ #ifdef CONFIG_AML_PARTITION VIRTUAL_PARTITION_ELEMENT(MMC_TABLE_NAME, MMC_TABLE_OFFSET, MMC_TABLE_SIZE), #endif VIRTUAL_PARTITION_ELEMENT(MMC_KEY_NAME, EMMCKEY_RESERVE_OFFSET, MMC_KEY_SIZE), VIRTUAL_PARTITION_ELEMENT(MMC_PATTERN_NAME, CALI_PATTERN_OFFSET, CALI_PATTERN_SIZE), VIRTUAL_PARTITION_ELEMENT(MMC_MAGIC_NAME, MAGIC_OFFSET, MAGIC_SIZE), VIRTUAL_PARTITION_ELEMENT(MMC_RANDOM_NAME, RANDOM_OFFSET, RANDOM_SIZE), #ifndef DTB_BIND_KERNEL VIRTUAL_PARTITION_ELEMENT(MMC_DTB_NAME, DTB_OFFSET, DTB_SIZE), #endif VIRTUAL_PARTITION_ELEMENT(MMC_FASTBOOT_CONTEXT_NAME, FASTBOOT_CONTEXT_OFFSET, FASTBOOT_CONTEXT_SIZE), VIRTUAL_PARTITION_ELEMENT(MMC_DDR_PARAMETER_NAME, DDR_PARAMETER_OFFSET, DDR_PARAMETER_SIZE), VIRTUAL_PARTITION_ELEMENT(MMC_GPT_ALT_NAME, MMC_GPT_ALT_OFFSET, MMC_GPT_ALT_SIZE), VIRTUAL_PARTITION_ELEMENT(MMC_TUN_PARA_NAME, MMC_TUN_PARA_OFFSET, MMC_TUN_PARA_SIZE), }; int get_emmc_tuning_para_offset(void) { int off; off = RESERVED_GPT_OFFSET + MMC_TUN_PARA_OFFSET; return off; } int get_emmc_partition_arraysize(void) { return ARRAY_SIZE(emmc_partition_table); } int get_emmc_virtual_partition_arraysize(void) { return ARRAY_SIZE(virtual_partition_table); } #endif void __attribute__((unused)) _dump_part_tbl(struct partitions *p, int count) { int i = 0; apt_info("count %d\n", count); while (i < count) { printf("%02d %10s %016llx %016llx\n", i, p[i].name, p[i].offset, p[i].size); i++; } return; } static int _get_part_index_by_name(struct partitions *tbl, int cnt, const char *name) { int i = 0; struct partitions *part = NULL; while (i < cnt) { part = &tbl[i]; if (!strcmp(name, part->name)) { apt_info("find %s @ tbl[%d]\n", name, i); break; } i++; }; if (i == cnt) { i = -1; apt_wrn("do not find match in table %s\n", name); } if (gpt_partition) i += 1; return i; } static struct partitions *_find_partition_by_name(struct partitions *tbl, int cnt, const char *name) { int i = 0; struct partitions *part = NULL; while (i < cnt) { part = &tbl[i]; if (!strcmp(name, part->name)) { apt_info("find %s @ tbl[%d]\n", name, i); break; } i++; }; if (i == cnt) { part = NULL; apt_wrn("do not find match in table %s\n", name); } return part; } /* fixme, must called after offset was calculated. */ static ulong _get_inherent_offset(const char *name) { struct partitions *part; part = _find_partition_by_name(emmc_partition_table, get_emmc_partition_arraysize(), name); if (NULL == part) return -1; else return part->offset; } /* partition table (Emmc Partition Table) */ struct _iptbl *p_iptbl_ept = NULL; /* trans byte into lba manner for rsv area read/write */ #ifdef CONFIG_AML_PARTITION static ulong _mmc_rsv_read(struct mmc *mmc, ulong offset, ulong size, void * buffer) { lbaint_t _blk, _cnt; if (0 == size) return 0; _blk = offset / mmc->read_bl_len; _cnt = size / mmc->read_bl_len; _cnt = blk_dread(mmc_get_blk_desc(mmc), _blk, _cnt, buffer); return (ulong)(_cnt * mmc->read_bl_len); } static ulong _mmc_rsv_write(struct mmc *mmc, ulong offset, ulong size, void * buffer) { lbaint_t _blk, _cnt; if (0 == size) return 0; _blk = offset / mmc->read_bl_len; _cnt = size / mmc->read_bl_len; _cnt = blk_dwrite(mmc_get_blk_desc(mmc), _blk, _cnt, buffer); return (ulong)(_cnt * mmc->read_bl_len); } #endif int fill_ept_by_gpt(struct mmc *mmc, struct _iptbl *p_iptbl_ept) { struct blk_desc *dev_desc = mmc_get_blk_desc(mmc); gpt_entry *gpt_pte = NULL; int i, k; size_t efiname_len, dosname_len; struct _iptbl *ept = p_iptbl_ept; struct partitions *partitions = ept->partitions; uint64_t alternate; ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); if (is_gpt_valid(dev_desc, GPT_PRIMARY_PARTITION_TABLE_LBA, gpt_head, &gpt_pte) != 1) { alternate = get_gpt_alternate(mmc); if (alternate != -1) { if (is_gpt_valid(dev_desc, alternate, gpt_head, &gpt_pte) != 1) { printf("%s: invalid gpt\n", __func__); return 1; } } printf("%s: *** Using Backup GPT ***\n", __func__); } for (i = 0; i < le32_to_cpu(gpt_head->num_partition_entries); i++) { if (!is_pte_valid(&gpt_pte[i])) break; partitions[i].offset = le64_to_cpu(gpt_pte[i].starting_lba << 9ULL); partitions[i].size = ((le64_to_cpu(gpt_pte[i].ending_lba) + 1) - le64_to_cpu(gpt_pte[i].starting_lba)) << 9ULL; /* mask flag */ partitions[i].mask_flags = (uint32_t)le64_to_cpu(gpt_pte[i].attributes.fields.type_guid_specific); /* partition name */ efiname_len = sizeof(gpt_pte[i].partition_name) / sizeof(efi_char16_t); dosname_len = sizeof(partitions[i].name); memset(partitions[i].name, 0, sizeof(partitions[i].name)); for (k = 0; k < min(dosname_len, efiname_len); k++) partitions[i].name[k] = (char)gpt_pte[i].partition_name[k]; if (strcmp(partitions[i].name, "boot_a") == 0) { has_boot_slot = 1; printf("set has_boot_slot = 1\n"); } else if (strcmp(partitions[i].name, "boot") == 0) { has_boot_slot = 0; printf("set has_boot_slot = 0\n"); } if (strcmp(partitions[i].name, "system_a") == 0) has_system_slot = 1; else if (strcmp(partitions[i].name, "system") == 0) has_system_slot = 0; if (strcmp(partitions[i].name, "super") == 0) { dynamic_partition = true; env_set("partition_mode", "dynamic"); printf("enable dynamic_partition\n"); } if (strncmp(partitions[i].name, "vendor_boot", 11) == 0) { vendor_boot_partition = true; env_set("vendor_boot_mode", "true"); printf("enable vendor_boot\n"); } debug("%02d %10s %016llx %016llx\n", i, partitions[i].name, partitions[i].offset, partitions[i].size); } ept->count = i; free(gpt_pte); return 0; } /* * 1. gpt is writed on emmc * parse gpt and compose ept and part_table * */ int get_ept_from_gpt(struct mmc *mmc) { struct partitions *ptbl = p_iptbl_ept->partitions; if (!fill_ept_by_gpt(mmc, p_iptbl_ept)) { printf("get ept from gpt success\n"); gpt_partition = true; return 0; } else if (part_table && part_table[0].offset != 0) { memcpy(ptbl, part_table, sizeof(struct partitions) * parts_total_num); p_iptbl_ept->count = parts_total_num; printf("get ept from part_table success\n"); gpt_partition = true; return 0; } return 1; } static struct partitions * get_ptbl_from_dtb(struct mmc *mmc) { struct partitions * ptbl = NULL; #ifdef CONFIG_AML_PARTITION #ifndef DTB_BIND_KERNEL unsigned char * buffer = NULL; ulong ret, offset; struct virtual_partition *vpart = aml_get_virtual_partition_by_name(MMC_DTB_NAME); /* try get dtb table from ddr, which may exsit while usb burning */ if (NULL == get_partitions()) { /* if failed, try rsv dtb area then. */ buffer = malloc(vpart->size * DTB_COPIES); if (NULL == buffer) { apt_err("Can not alloc enough buffer\n"); goto _err; } offset = _get_inherent_offset(MMC_RESERVED_NAME) + vpart->offset; ret = _mmc_rsv_read(mmc, offset, (vpart->size * DTB_COPIES), buffer); if (ret != (vpart->size * DTB_COPIES)) { apt_err("Can not alloc enough buffer\n"); goto _err1; } /* parse it */ if (get_partition_from_dts(buffer)) { apt_err("get partition table from dts faild\n"); goto _err1; } /* double check part_table(glb) */ if (NULL == get_partitions()) { goto _err1; } apt_info("get partition table from dts successfully\n"); free(buffer); buffer = NULL; } #endif #endif /* asign partition info to *ptbl */ ptbl = get_partitions(); return ptbl; #ifdef CONFIG_AML_PARTITION #ifndef DTB_BIND_KERNEL _err1: if (buffer) free(buffer); _err: free (ptbl); return NULL; #endif #endif } static struct partitions *is_prio_partition(struct _iptbl *list, struct partitions *part) { int i; struct partitions *plist = NULL; if (list->count == 0) goto _out; apt_info("count %d\n", list->count); for (i=0; icount; i++) { plist = &list->partitions[i]; apt_info("%d: %s, %s\n", i, part->name, plist->name); if (!strcmp(plist->name, part->name)) { apt_info("%s is prio in list[%d]\n", part->name, i); break; } } if (i == list->count) plist = NULL; _out: return plist; } /* calculate offset of each partitions. bottom is a flag for considering */ static int _calculate_offset(struct mmc *mmc, struct _iptbl *itbl, u32 bottom) { int i; struct partitions *part; ulong gap = PARTITION_RESERVED; int ret = 0; if (itbl->count <= 0) return -1; part = itbl->partitions; #if (CONFIG_MPT_DEBUG) _dump_part_tbl(part, itbl->count); #endif if (!strcmp(part->name, "bootloader")) { part->offset = 0; gap = MMC_BOOT_PARTITION_RESERVED; } for (i=1; icount; i++) { /**/ part[i].offset = part[i-1].offset + part[i-1].size + gap; /* check capacity overflow ?*/ if (((part[i].offset + part[i].size) > mmc->capacity) || (part[i].size == -1)) { part[i].size = mmc->capacity - part[i].offset; /* reserv space @ the bottom */ if (bottom && (part[i].size > MMC_BOTTOM_RSV_SIZE)) { apt_info("reserv %d bytes at bottom\n", MMC_BOTTOM_RSV_SIZE); part[i].size -= MMC_BOTTOM_RSV_SIZE; } break; } if ((part[i].mask_flags & 0x100) != 0) gap = PARTITION_MIN_RESERVED; else gap = PARTITION_RESERVED; } #if (ADD_LAST_PARTITION) i += 1; if (i == itbl->count - 1 && bottom == 1) { part[i - 1].size -= gap + part[i].size; part[i].offset = part[i - 1].offset + part[i - 1].size + gap; } #endif if (i < (itbl->count - 1)) { apt_err("too large partition table for current emmc, overflow!\n"); ret = -1; } #if (CONFIG_MPT_DEBUG) _dump_part_tbl(part, itbl->count); #endif return ret; } static void compose_ept(struct _iptbl *dtb, struct _iptbl *inh, struct _iptbl *ept) { int i; struct partitions *partition = NULL; struct partitions *dst, *src, *prio; /* override inh info by dts */ apt_info("dtb %p, inh %p, ept %p\n", dtb, inh, ept); apt_info("ept->partitions %p\n", ept->partitions); partition = ept->partitions; apt_info("partition %p\n", partition); for (i=0; icount %d\n", i, ept->count); dst = &partition[ept->count]; src = (i < inh->count) ? &inh->partitions[i]:&dtb->partitions[i-inh->count]; prio = is_prio_partition(ept, src); if (prio) { /* override prio partition by new */ apt_info("override %d: %s\n", ept->count, prio->name); //*prio = *src; dst = prio; } else ept->count ++; *dst = *src; if (-1 == src->size) { apt_info("break! %s\n", src->name); break; } } #if (ADD_LAST_PARTITION) i += 1; dst = &partition[ept->count]; src = &dtb->partitions[i - inh->count]; ept->count += 1; *dst = *src; #endif return; } #ifdef CONFIG_AML_PARTITION static int _get_version(unsigned char * s) { int version = 0; if (!strncmp((char *)s, MMC_MPT_VERSION_2, sizeof(MMC_MPT_VERSION_2))) version = 2; else if (!strncmp((char *)s, MMC_MPT_VERSION_1, sizeof(MMC_MPT_VERSION_1))) version = 1; else version = -1; return version; } /* calc checksum. there's a bug on v1 which did not calculate all the partitions. */ static int _calc_iptbl_check_v2(struct partitions *part, int count) { int ret = 0, i; int size = count * sizeof(struct partitions) >> 2; int *buf = (int *)part; for (i = 0; i < size; i++) ret += buf[i]; return ret; } static int _calc_iptbl_check_v1(struct partitions *part, int count) { int i, j; u32 checksum = 0, *p; for (i = 0; i < count; i++) { p = (u32*)part; /*BUG here, do not fix it!!*/ for (j = sizeof(struct partitions)/sizeof(checksum); j > 0; j--) { checksum += *p; p++; } } return checksum; } static int _calc_iptbl_check(struct partitions * part, int count, int version) { if (1 == version) return _calc_iptbl_check_v1(part, count); else if (2 == version) return _calc_iptbl_check_v2(part, count); else return -1; } /* ept is malloced out side */ static int _cpy_iptbl(struct _iptbl * dst, struct _iptbl * src) { int ret = 0; if (!dst || !src) { apt_err("invalid arg %s\n", !dst ? "dst" : "src"); ret = -1; goto _out; } if (!dst->partitions || !src->partitions) { apt_err("invalid arg %s->partitions\n", !dst ? "dst" : "src"); ret = -2; goto _out; } dst->count = src->count; memcpy(dst->partitions, src->partitions, sizeof(struct partitions) * src->count); _out: return ret; } /* get ptbl from rsv area from emmc */ static int get_ptbl_rsv(struct mmc *mmc, struct _iptbl *rsv) { struct ptbl_rsv * ptbl_rsv = NULL; uchar * buffer = NULL; ulong size, offset; int checksum, version, ret = 0; struct virtual_partition *vpart = aml_get_virtual_partition_by_name(MMC_TABLE_NAME); size = (sizeof(struct ptbl_rsv) + 511) / 512 * 512; if (vpart->size < size) { apt_err("too much partitions\n"); ret = -1; goto _out; } buffer = malloc(size); if (NULL == buffer) { apt_err("no enough memory for ptbl rsv\n"); ret = -2; goto _out; } /* read it from emmc. */ offset = _get_inherent_offset(MMC_RESERVED_NAME) + vpart->offset; if (size != _mmc_rsv_read(mmc, offset, size, buffer)) { apt_err("read ptbl from rsv failed\n"); ret = -3; goto _out; } ptbl_rsv = (struct ptbl_rsv *) buffer; apt_info("magic %3.3s, version %8.8s, checksum %x\n", ptbl_rsv->magic, ptbl_rsv->version, ptbl_rsv->checksum); /* fixme, check magic ?*/ if (strncmp(ptbl_rsv->magic, MMC_PARTITIONS_MAGIC, sizeof(MMC_PARTITIONS_MAGIC))) { apt_err("magic faild %s, %3.3s\n", MMC_PARTITIONS_MAGIC, ptbl_rsv->magic); ret = -4; goto _out; } /* check version*/ version = _get_version(ptbl_rsv->version); if (version < 0) { apt_err("version faild %s, %3.3s\n", MMC_PARTITIONS_MAGIC, ptbl_rsv->magic); ret = -5; goto _out; } if (ptbl_rsv->count > MAX_MMC_PART_NUM) { apt_err("invalid partition count %d\n", ptbl_rsv->count); ret = -1; goto _out; } /* check sum */ checksum = _calc_iptbl_check(ptbl_rsv->partitions, ptbl_rsv->count, version); if (checksum != ptbl_rsv->checksum) { apt_err("checksum faild 0x%x, 0x%x\n", ptbl_rsv->checksum, checksum); ret = -6; goto _out; } rsv->count = ptbl_rsv->count; memcpy(rsv->partitions, ptbl_rsv->partitions, rsv->count * sizeof(struct partitions)); _out: if (buffer) free (buffer); return ret; } /* update partition tables from src if success, return 0; else, return 1 */ static int update_ptbl_rsv(struct mmc *mmc, struct _iptbl *src) { struct ptbl_rsv *ptbl_rsv = NULL; uchar *buffer; ulong size, offset; int ret = 0, version; struct virtual_partition *vpart = aml_get_virtual_partition_by_name(MMC_TABLE_NAME); size = (sizeof(struct ptbl_rsv) + 511) / 512 * 512; buffer = malloc(size); if (NULL == buffer) { apt_err("no enough memory for ptbl rsv\n"); return -1; } memset(buffer, 0 , size); /* version, magic and checksum */ ptbl_rsv = (struct ptbl_rsv *) buffer; strcpy((char *)ptbl_rsv->version, MMC_MPT_VERSION); strcpy(ptbl_rsv->magic, MMC_PARTITIONS_MAGIC); if (src->count > MAX_MMC_PART_NUM) { apt_err("too much partitions\n"); ret = -1; goto _err; } ptbl_rsv->count = src->count; memcpy(ptbl_rsv->partitions, src->partitions, sizeof(struct partitions)*src->count); version = _get_version(ptbl_rsv->version); ptbl_rsv->checksum = _calc_iptbl_check(src->partitions, src->count, version); /* write it to emmc. */ apt_info("magic %3.3s, version %8.8s, checksum %x\n", ptbl_rsv->magic, ptbl_rsv->version, ptbl_rsv->checksum); offset = _get_inherent_offset(MMC_RESERVED_NAME) + vpart->offset; if (_mmc_rsv_write(mmc, offset, size, buffer) != size) { apt_err("write ptbl to rsv failed\n"); ret = -1; goto _err; } _err: free (buffer); return ret; } static void _free_iptbl(struct _iptbl *iptbl) { if (iptbl && iptbl->partitions) { free(iptbl->partitions); iptbl->partitions = NULL; } if (iptbl) { free(iptbl); iptbl = NULL; } return; } #endif static int _cmp_partition(struct partitions *dst, struct partitions *src, int override) { int ret = 0; #if (CONFIG_CMP_PARTNAME) if (strncmp(dst->name, src->name, sizeof(src->name))) ret = -2; #endif if (dst->size != src->size) ret = -3; if (dst->offset != src->offset) ret = -4; #if (CONFIG_CMP_PARTMASK) if (dst->mask_flags != src->mask_flags) ret = -5; #endif if (ret && (!override)) { apt_err("name: %10.10s<->%10.10s\n", dst->name, src->name); apt_err("size: %llx<->%llx\n", dst->size, src->size); apt_err("offset: %llx<->%llx\n", dst->offset, src->offset); apt_err("mask: %08x<->%08x\n", dst->mask_flags, src->mask_flags); } if (override) { *dst = *src; ret = 0; } return ret; } /* compare partition tables if same, do nothing then return 0; else, print the diff ones and return -x -1:count -2:name -3:size -4:offset */ static int _cmp_iptbl(struct _iptbl * dst, struct _iptbl * src) { int ret = 0, i = 0; struct partitions *dstp; struct partitions *srcp; if (dst->count != src->count) { apt_err("partition count is not same %d:%d\n", dst->count, src->count); ret = -1; goto _out; } while (i < dst->count) { dstp = &dst->partitions[i]; srcp = &src->partitions[i]; ret = _cmp_partition(dstp, srcp, 0); if (ret) { env_set("part_changed", "1"); apt_err("partition %d has changed\n", i); break; } i++; } _out: return ret; } /* iptbl buffer opt. */ static int _zalloc_iptbl(struct _iptbl **_iptbl) { int ret = 0; struct _iptbl *iptbl; struct partitions *partition = NULL; partition = malloc(sizeof(struct partitions)*MAX_PART_COUNT); if (NULL == partition) { ret = -1; apt_err("no enough memory for partitions\n"); goto _out; } iptbl = malloc(sizeof(struct _iptbl)); if (NULL == iptbl) { ret = -2; apt_err("no enough memory for ept\n"); free(partition); goto _out; } memset(partition, 0, sizeof(struct partitions)*MAX_PART_COUNT); memset(iptbl, 0, sizeof(struct _iptbl)); iptbl->partitions = partition; apt_info("iptbl %p, partition %p, iptbl->partitions %p\n", iptbl, partition, iptbl->partitions); *_iptbl = iptbl; _out: return ret; } /* * fixme, need check space size later. */ static inline int le32_to_int(unsigned char *le32) { return ((le32[3] << 24) + (le32[2] << 16) + (le32[1] << 8) + le32[0] ); } static int test_block_type(unsigned char *buffer) { int slot; struct dos_partition *p; if ((buffer[DOS_PART_MAGIC_OFFSET + 0] != 0x55) || (buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) ) { return (-1); } /* no DOS Signature at all */ p = (struct dos_partition *)&buffer[DOS_PART_TBL_OFFSET]; for (slot = 0; slot < 3; slot++) { if (p->boot_ind != 0 && p->boot_ind != 0x80) { if (!slot && (strncmp((char *)&buffer[DOS_PBR_FSTYPE_OFFSET], "FAT", 3) == 0 || strncmp((char *)&buffer[DOS_PBR32_FSTYPE_OFFSET], "FAT32", 5) == 0)) { return DOS_PBR; /* is PBR */ } else { return -1; } } } return DOS_MBR; /* Is MBR */ } //DOS_MBR OR DOS_PBR /* * re-constructed iptbl from mbr&ebr infos. * memory for iptbl_mbr must be alloced outside. * */ static void _construct_ptbl_by_mbr(struct mmc *mmc, struct _iptbl *iptbl_mbr) { int ret,i; int flag = 0; lbaint_t read_offset = 0; int part_num = 0; int primary_num = 0; uint64_t logic_start = 0; uint64_t extended_start = 0; struct dos_partition *pt; struct partitions *partitions = iptbl_mbr->partitions; apt_info("aml MBR&EBR debug...\n"); ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, 512); for (;;) { apt_info("**%02d: read_offset %016llx\n", part_num, (uint64_t)read_offset<<9); ret = blk_dread(mmc_get_blk_desc(mmc), read_offset, 1, buffer); if (read_offset == 0) flag = 1; else flag = 0; /* debug code */ // print_buffer(0,buffer,1,512,16); if (ret != 1) { apt_err("ret %d fail to read current ebr&mbr from emmc! \n", ret); break; } ret = test_block_type(buffer); if (ret != 0 && ret != 1) { apt_err("invalid magic value: 0x%02x%02x\n", buffer[DOS_PART_MAGIC_OFFSET], buffer[DOS_PART_MAGIC_OFFSET + 1]); break; } pt = (dos_partition_t *)(&buffer[0] + DOS_PART_TBL_OFFSET); for (i = 0; i < 4; i++, pt++) { if ( (pt->boot_ind == 0x00 || pt->boot_ind == 0x80) && pt->sys_ind == 0x83 ) { //emmc_partition[part_num]->name = NULL; partitions[part_num].offset = ((uint64_t)(le32_to_int(pt->start4)+read_offset) << 9ULL); partitions[part_num].size = (uint64_t)le32_to_int(pt->size4) << 9ULL; partitions[part_num].mask_flags = pt->sys_ind; apt_info("--partition[%d]: %016llx, %016llx, 0x%08x \n", part_num, partitions[part_num].offset, partitions[part_num].size, le32_to_int(pt->size4)); part_num++; if ( flag ) primary_num++; }else{/* get the next extended partition info */ if ( pt->boot_ind == 0x00 && pt->sys_ind == 0x05) { logic_start = (uint64_t)le32_to_int (pt->start4); //logic_size = (uint64_t)le32_to_int (pt->size4); } } } /* mbr & ebr debug infos */ apt_info("******%02d: read_offset=%016llx, logic_start=%016llx\n", part_num,(uint64_t)read_offset*512ULL,logic_start*512ULL); if (part_num == primary_num) { extended_start = logic_start; read_offset = extended_start; }else read_offset = extended_start + logic_start; if (logic_start == 0) break; logic_start = 0; } iptbl_mbr->count = part_num; apt_info("iptbl_mbr->count = %d\n", iptbl_mbr->count); return; } static int __attribute__((unused)) _check_ptbl_mbr(struct mmc *mmc, struct _iptbl *ept) { int ret = 0; /* re-constructed by mbr */ struct _iptbl *iptbl_mbr = NULL; struct partitions *partitions = NULL; iptbl_mbr = malloc(sizeof(struct _iptbl)); if (NULL == iptbl_mbr) { apt_err("no enough memory for iptbl_mbr\n"); return -1; } memset(iptbl_mbr , 0, sizeof(struct _iptbl)); partitions = (struct partitions *)malloc(sizeof(struct partitions) * DOS_PARTITION_COUNT); if (NULL == partitions) { apt_err("no enough memory for partitions\n"); free(iptbl_mbr); return -1; } memset(partitions, 0, sizeof(struct partitions) * DOS_PARTITION_COUNT); iptbl_mbr->partitions = partitions; _construct_ptbl_by_mbr(mmc, iptbl_mbr); ret = _cmp_iptbl(iptbl_mbr, ept); if (partitions) free(partitions); if (iptbl_mbr) free(iptbl_mbr); apt_wrn("MBR is %s\n", ret?"Improper!":"OK!"); return ret; } /* construct a partition table entry of EBR */ static int _construct_ebr_1st_entry(struct _iptbl *p_iptbl,struct dos_partition *p_ebr, int part_num ) { uint64_t start_offset = 0; uint64_t logic_size = 0; p_ebr->boot_ind = 0x00; p_ebr->sys_ind = 0x83; /* Starting = relative offset between this EBR sector and the first sector of the logical partition * the gap between two partition is a fixed value of PARTITION_RESERVED ,otherwise the emmc partiton * is different with reserved */ start_offset = PARTITION_RESERVED >> 9; /* Number of sectors = total count of sectors for this logical partition */ // logic_size = (p_iptbl->partitions[part_num].size) >> 9ULL; logic_size = lldiv(p_iptbl->partitions[part_num].size, 512); apt_info("*** %02d: size 0x%016llx, logic_size 0x%016llx\n", part_num, p_iptbl->partitions[part_num].size, logic_size); memcpy((unsigned char *)(p_ebr->start4), &start_offset, 4); memcpy((unsigned char *)(p_ebr->size4), &logic_size, 4); return 0; } static int _construct_ebr_2nd_entry(struct _iptbl *p_iptbl, struct dos_partition *p_ebr, int part_num) { uint64_t start_offset = 0; uint64_t logic_size = 0; if ((part_num+2) > p_iptbl->count) return 0; p_ebr->boot_ind = 0x00; p_ebr->sys_ind = 0x05; /* Starting sector = LBA address of next EBR minus LBA address of extended partition's first EBR */ start_offset = (p_iptbl->partitions[part_num+1].offset - PARTITION_RESERVED - (p_iptbl->partitions[3].offset - PARTITION_RESERVED)) >> 9; /* total count of sectors for next logical partition, but count starts from the next EBR sector */ logic_size = (p_iptbl->partitions[part_num+1].size + PARTITION_RESERVED) >> 9; memcpy((unsigned char *)(p_ebr->start4), &start_offset, 4); memcpy((unsigned char *)(p_ebr->size4), &logic_size, 4); return 0; } /* construct a partition table entry of MBR OR EBR */ static int _construct_mbr_entry(struct _iptbl *p_iptbl, struct dos_partition *p_entry, int part_num) { uint64_t start_offset = 0; uint64_t primary_size = 0; uint64_t extended_size = 0; int i; /* the entry is active or not */ p_entry->boot_ind = 0x00; if (part_num == 3) {/* the logic partion entry */ /* the entry type */ p_entry->sys_ind = 0x05; start_offset = (p_iptbl->partitions[3].offset - PARTITION_RESERVED) >> 9; for ( i = 3;i< p_iptbl->count;i++) extended_size = p_iptbl->partitions[i].size >> 9; memcpy((unsigned char *)p_entry->start4, &start_offset, 4); memcpy((unsigned char *)p_entry->size4, &extended_size, 4); }else{/* the primary partion entry */ /* the entry type */ p_entry->sys_ind = 0x83; start_offset = (p_iptbl->partitions[part_num].offset) >> 9; primary_size = (p_iptbl->partitions[part_num].size)>>9; memcpy((unsigned char *)p_entry->start4, &start_offset, 4); memcpy((unsigned char *)p_entry->size4, &primary_size, 4); } return 0; } static int _construct_mbr_or_ebr(struct _iptbl *p_iptbl, struct dos_mbr_or_ebr *p_br, int part_num, int type) { int i; if (DOS_MBR == type) { /* construct a integral MBR */ for (i = 0; i<4 ; i++) _construct_mbr_entry(p_iptbl, &p_br->part_entry[i], i); }else{ /* construct a integral EBR */ p_br->bootstart[DOS_PBR32_FSTYPE_OFFSET] = 'F'; p_br->bootstart[DOS_PBR32_FSTYPE_OFFSET + 1] = 'A'; p_br->bootstart[DOS_PBR32_FSTYPE_OFFSET + 2] = 'T'; p_br->bootstart[DOS_PBR32_FSTYPE_OFFSET + 3] = '3'; p_br->bootstart[DOS_PBR32_FSTYPE_OFFSET + 4] = '2'; _construct_ebr_1st_entry(p_iptbl, &p_br->part_entry[0], part_num); _construct_ebr_2nd_entry(p_iptbl, &p_br->part_entry[1], part_num); } p_br->magic[0] = 0x55 ; p_br->magic[1] = 0xAA ; return 0; } static __attribute__((unused)) int _update_ptbl_mbr(struct mmc *mmc, struct _iptbl *p_iptbl) { int ret = 0, start_blk = 0, blk_cnt = 1; unsigned char *src; int i; struct dos_mbr_or_ebr *mbr; struct _iptbl *ptb ; ptb = p_iptbl; mbr = malloc(sizeof(struct dos_mbr_or_ebr)); for (i=0;icount;i++) { apt_info("-update MBR-: partition[%02d]: %016llx - %016llx\n",i, ptb->partitions[i].offset, ptb->partitions[i].size); } for (i = 0;i < ptb->count;) { memset(mbr ,0 ,sizeof(struct dos_mbr_or_ebr)); if (i == 0) { _construct_mbr_or_ebr(ptb, mbr, i, 0); i = i+2; } else _construct_mbr_or_ebr(ptb, mbr, i, 2); src = (unsigned char *)mbr; apt_info("--%s(): %02d(%02d), off %x\n", __func__, i, ptb->count, start_blk); ret = blk_dwrite(mmc_get_blk_desc(mmc), start_blk, blk_cnt, src); i++; if (ret != blk_cnt) { apt_err("write current MBR failed! ret: %d != cnt: %d\n",ret,blk_cnt); break; } start_blk = (ptb->partitions[i].offset - PARTITION_RESERVED) >> 9; } free(mbr); ret = !ret; if (ret) apt_err("write MBR failed!\n"); return ret; } int check_gpt_change(struct blk_desc *dev_desc, void *buf) { int i, k; int m = 0; gpt_header *gpt_h; gpt_entry *gpt_e; u32 calc_crc32; u32 entries_num; size_t efiname_len; int ret = 0; bool alternate_flag = false; int j = 0; int recovery_offset_old = 0, recovery_offset_new = 0; int tee_offset_old = 0, tee_offset_new = 0; int oem_offset_old = 0, oem_offset_new = 0; int data_offset_old = 0, data_offset_new = 0; int data_size_old = 0, data_size_new = 0; int cache_offset_old = 0, cache_offset_new = 0; int cache_size_old = 0, cache_size_new = 0; int tee_size_old = 0, tee_size_new = 0; int oem_size_old = 0, oem_size_new = 0; int metadata_offset_old = 0, metadata_offset_new = 0; int metadata_size_old = 0, metadata_size_new = 0; struct partitions *partitions = p_iptbl_ept->partitions; int parts_num = p_iptbl_ept->count; uint64_t offset; uint64_t size; uint32_t mask_flags; char name[PARTNAME_SZ]; char *update_dts_gpt = NULL; #if (ADD_LAST_PARTITION) ulong gap = GPT_GAP; #endif update_dts_gpt = env_get("update_dts_gpt"); if (is_valid_gpt_buf(dev_desc, buf)) return -1; gpt_h = buf + (GPT_PRIMARY_PARTITION_TABLE_LBA * dev_desc->blksz); /* determine start of GPT Entries in the buffer */ gpt_e = buf + (le64_to_cpu(gpt_h->partition_entry_lba) * dev_desc->blksz); entries_num = le32_to_cpu(gpt_h->num_partition_entries); if (le64_to_cpu(gpt_h->alternate_lba) > dev_desc->lba || le64_to_cpu(gpt_h->alternate_lba) == 0) { printf("GPT: alternate_lba: %llX, " LBAF ", reset it\n", le64_to_cpu(gpt_h->alternate_lba), dev_desc->lba); gpt_h->alternate_lba = cpu_to_le64(dev_desc->lba - 1); alternate_flag = true; } if (le64_to_cpu(gpt_h->last_usable_lba) > dev_desc->lba) { printf("GPT: last_usable_lba incorrect: %llX > " LBAF ", reset it\n", le64_to_cpu(gpt_h->last_usable_lba), dev_desc->lba); if (alternate_flag) gpt_h->last_usable_lba = cpu_to_le64(dev_desc->lba - 34); else gpt_h->last_usable_lba = cpu_to_le64(dev_desc->lba - 1); } for (i = 0; i < entries_num; i++) { #if (ADD_LAST_PARTITION) if (i == entries_num - 1) { gpt_e[i - 1].ending_lba -= gpt_e[i].ending_lba + le64_to_cpu(gap) + 1; gpt_e[i].starting_lba = gpt_e[i - 1].ending_lba + le64_to_cpu(gap) + 1; gpt_e[i].ending_lba = gpt_h->last_usable_lba; } #endif if (le64_to_cpu(gpt_e[i].starting_lba) > gpt_h->last_usable_lba) { printf("gpt_e[%d].starting_lba: %llX > %llX, writing failed\n", i, le64_to_cpu(gpt_e[i].starting_lba), le64_to_cpu(gpt_h->last_usable_lba)); return 1; } if (le64_to_cpu(gpt_e[i].ending_lba) > gpt_h->last_usable_lba) { printf("gpt_e[%d].ending_lba: %llX > %llX, reset it\n", i, le64_to_cpu(gpt_e[i].ending_lba), le64_to_cpu(gpt_h->last_usable_lba)); if (alternate_flag) gpt_e[i].ending_lba = ((gpt_h->last_usable_lba >> 12) << 12) - 1; else gpt_e[i].ending_lba = gpt_h->last_usable_lba; printf("gpt_e[%d].ending_lba: %llX\n", i, gpt_e[i].ending_lba); } } calc_crc32 = crc32(0, (const unsigned char *)gpt_e, entries_num * le32_to_cpu(gpt_h->sizeof_partition_entry)); gpt_h->partition_entry_array_crc32 = calc_crc32; gpt_h->header_crc32 = 0; calc_crc32 = crc32(0, (const unsigned char *)gpt_h, le32_to_cpu(gpt_h->header_size)); gpt_h->header_crc32 = calc_crc32; if (update_dts_gpt) { printf("update_dts_gpt is %s\n", update_dts_gpt); m = 1; ret = 2; } if (parts_num != entries_num) { printf("parts_num changes\n"); ret = 2; } for (j = m; j < parts_num; j++) { if (partitions[j].size != 0 && (strcmp(partitions[j].name, "rsv") != 0)) { if (!strcmp(partitions[j].name, "recovery")) { recovery_offset_old = partitions[j].offset; //printf("recovery_offset_old = %d\n", recovery_offset_old); } else if (!strcmp(partitions[j].name, "cache")) { cache_offset_old = partitions[j].offset; cache_size_old = partitions[j].size; //printf("cache_offset_old = %d\n", cache_offset_old); //printf("cache_size_old = %d\n", cache_size_old); } else if (!strcmp(partitions[j].name, "userdata") || !strcmp(partitions[j].name, "data")) { data_offset_old = partitions[j].offset; data_size_old = partitions[j].size; //printf("data_offset_old = %d\n", data_offset_old); //printf("data_size_old = %d\n", data_size_old); } else if (!strcmp(partitions[j].name, "tee")) { tee_offset_old = partitions[j].offset; tee_size_old = partitions[j].size; //printf("tee_offset_old = %d\n", tee_offset_old); //printf("tee_size_old = %d\n", tee_size_old); } else if (!strcmp(partitions[j].name, "metadata")) { metadata_offset_old = partitions[j].offset; metadata_size_old = partitions[j].size; //printf("metadata_offset_old = %d\n", metadata_offset_old); //printf("metadata_size_old = %d\n", metadata_size_old); } else if (!strcmp(partitions[j].name, "oem")) { oem_offset_old = partitions[j].offset; oem_size_old = partitions[j].size; //printf("oem_offset_old = %d\n", tee_offset_old); //printf("oem_size_old = %d\n", tee_size_old); } } } for (i = 0; i < entries_num; i++) { /* partition name */ efiname_len = sizeof(gpt_e[i].partition_name) / sizeof(efi_char16_t); memset(name, 0, PARTNAME_SZ); for (k = 0; k < efiname_len; k++) name[k] = (char)gpt_e[i].partition_name[k]; if (strcmp(name, partitions[i].name) != 0) { printf("Caution! GPT name [%s] had been changed\n", name); ret = 2; } offset = le64_to_cpu(gpt_e[i].starting_lba << 9ULL); size = ((le64_to_cpu(gpt_e[i].ending_lba) + 1) - le64_to_cpu(gpt_e[i].starting_lba)) << 9ULL; mask_flags = (uint32_t)le64_to_cpu(gpt_e[i].attributes.fields.type_guid_specific); if (!strcmp(name, "recovery")) { recovery_offset_new = offset; //printf("recovery_offset_new = %d\n", recovery_offset_new); } else if (!strcmp(name, "cache")) { cache_offset_new = offset; cache_size_new = size; ///printf("cache_offset_new = %d\n", cache_offset_new); //printf("cache_size_new = %d\n", cache_size_new); } else if (!strcmp(name, "userdata") || !strcmp(name, "data")) { data_offset_new = offset; data_size_new = size; //printf("data_offset_new = %d\n", data_offset_new); //printf("data_size_new = %d\n", data_size_new); } else if (!strcmp(name, "tee")) { tee_offset_new = offset; tee_size_new = size; //printf("tee_offset_new = %d\n", tee_offset_new); //printf("tee_size_new = %d\n", tee_size_new); } else if (!strcmp(name, "metadata")) { metadata_offset_new = offset; metadata_size_new = size; //printf("metadata_offset_new = %d\n", metadata_offset_new); //printf("metadata_size_new = %d\n", metadata_size_new); } else if (!strcmp(name, "oem")) { oem_offset_new = offset; oem_size_new = size; //printf("oem_offset_new = %d\n", oem_offset_new); //printf("oem_size_new = %d\n", oem_size_new); } for (j = m; j < parts_num; j++) { if ((strcmp(partitions[j].name, name) == 0) && (strcmp(partitions[j].name, "rsv") != 0)) { if (partitions[j].offset != offset || partitions[j].size != size) { printf("%s offset/size had been changed\n", name); printf("offset: %016llx --> %016llx\n", partitions[j].offset, offset); printf("size: %016llx --> %016llx\n", partitions[j].size, size); ret = 3; } if (partitions[j].mask_flags != mask_flags) { printf("%s mask_flags had been changed\n", name); printf("%08x<->%08x\n", partitions[j].mask_flags, mask_flags); if (ret == 0) ret = 2; } if (strcmp(name, "userdata") == 0 && partitions[j].offset == offset && partitions[j].size != size) { printf("userdata offset is same, just size changed\n"); if ((partitions[j].size - size) == 0x200000) { printf("new size small 2M, move backgpt to gap\n"); gpt_h->last_usable_lba = cpu_to_le64(dev_desc->lba - 1); gpt_h->alternate_lba = gpt_e[1].starting_lba - 1; gpt_e[i].ending_lba = gpt_h->last_usable_lba; printf("GPT: reset last_usable_lba: %llX\n", le64_to_cpu(gpt_h->last_usable_lba)); printf("GPT: reset alternate_lba: %llX\n", le64_to_cpu(gpt_h->alternate_lba)); printf("reset userdata size: %016llx\n", ((le64_to_cpu(gpt_e[i].ending_lba) + 1) - le64_to_cpu(gpt_e[i].starting_lba)) << 9ULL); calc_crc32 = crc32(0, (const unsigned char *)gpt_e, entries_num * le32_to_cpu(gpt_h->sizeof_partition_entry)); gpt_h->partition_entry_array_crc32 = calc_crc32; gpt_h->header_crc32 = 0; calc_crc32 = crc32(0, (const unsigned char *)gpt_h, le32_to_cpu(gpt_h->header_size)); gpt_h->header_crc32 = calc_crc32; ret = 5; data_size_new = partitions[j].size; } else if ((size - partitions[j].size) == 0x200000) { printf("new size large 2M\n"); gpt_h->alternate_lba = cpu_to_le64(dev_desc->lba - 1); gpt_h->last_usable_lba = cpu_to_le64(dev_desc->lba - 34); gpt_e[i].ending_lba = ((gpt_h->last_usable_lba >> 12) << 12) - 1; printf("GPT: reset last_usable_lba: %llX\n", le64_to_cpu(gpt_h->last_usable_lba)); printf("GPT: reset alternate_lba: %llX\n", le64_to_cpu(gpt_h->alternate_lba)); printf("reset userdata size: %016llx\n", ((le64_to_cpu(gpt_e[i].ending_lba) + 1) - le64_to_cpu(gpt_e[i].starting_lba)) << 9ULL); calc_crc32 = crc32(0, (const unsigned char *)gpt_e, entries_num * le32_to_cpu(gpt_h->sizeof_partition_entry)); gpt_h->partition_entry_array_crc32 = calc_crc32; gpt_h->header_crc32 = 0; calc_crc32 = crc32(0, (const unsigned char *)gpt_h, le32_to_cpu(gpt_h->header_size)); gpt_h->header_crc32 = calc_crc32; ret = 5; data_size_new = partitions[j].size; } else if (size > partitions[j].size) { printf("other new size large case\n"); printf("last_usable_lba: %llX\n", le64_to_cpu(gpt_h->last_usable_lba)); printf("alternate_lba: %llX\n", le64_to_cpu(gpt_h->alternate_lba)); printf("userdata size: %016llx\n", ((le64_to_cpu(gpt_e[i].ending_lba) + 1) - le64_to_cpu(gpt_e[i].starting_lba)) << 9ULL); gpt_e[i].ending_lba = cpu_to_le64((partitions[j].size >> 9ULL) + le64_to_cpu(gpt_e[i].starting_lba) - 1); gpt_h->last_usable_lba = cpu_to_le64(gpt_e[i].starting_lba); printf("GPT: reset last_usable_lba: %llX\n", le64_to_cpu(gpt_h->last_usable_lba)); printf("GPT: reset alternate_lba: %llX\n", le64_to_cpu(gpt_h->alternate_lba)); printf("reset userdata size: %016llx\n", ((le64_to_cpu(gpt_e[i].ending_lba) + 1) - le64_to_cpu(gpt_e[i].starting_lba)) << 9ULL); calc_crc32 = crc32(0, (const unsigned char *)gpt_e, entries_num * le32_to_cpu(gpt_h->sizeof_partition_entry)); gpt_h->partition_entry_array_crc32 = calc_crc32; gpt_h->header_crc32 = 0; calc_crc32 = crc32(0, (const unsigned char *)gpt_h, le32_to_cpu(gpt_h->header_size)); gpt_h->header_crc32 = calc_crc32; ret = 5; data_size_new = partitions[j].size; } } } } } if (data_offset_old != data_offset_new || data_size_old != data_size_new || cache_offset_old != cache_offset_new || cache_size_old != cache_size_new || tee_offset_old != tee_offset_new || tee_size_old != tee_size_new || metadata_offset_old != metadata_offset_new || metadata_size_old != metadata_size_new || oem_offset_old != oem_offset_new || oem_size_old != oem_size_new || recovery_offset_old != recovery_offset_new) { printf("null ab critical partition change\n"); ret = 4; } return ret; } int is_gpt_changed(struct mmc *mmc, struct _iptbl *p_iptbl_ept) { int i, k; gpt_entry *gpt_pte = NULL; size_t efiname_len; struct _iptbl *ept = p_iptbl_ept; struct partitions *partitions = ept->partitions; int parts_num = ept->count; uint64_t offset; uint64_t size; char name[PARTNAME_SZ]; int gpt_changed = 0; struct blk_desc *dev_desc = mmc_get_blk_desc(mmc); ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); if (is_gpt_valid(dev_desc, GPT_PRIMARY_PARTITION_TABLE_LBA, gpt_head, &gpt_pte) != 1) { printf("%s: ***ERROR:Invalid GPT ***\n", __func__); if (is_gpt_valid(dev_desc, (dev_desc->lba - 1), gpt_head, &gpt_pte) != 1) { printf("%s: ***ERROR: Invalid Backup GPT ***\n", __func__); return 1; } else { printf("%s: *** Using Backup GPT ***\n", __func__); } } for (i = 0; i < le32_to_cpu(gpt_head->num_partition_entries); i++) { if (!is_pte_valid(&gpt_pte[i])) break; offset = le64_to_cpu(gpt_pte[i].starting_lba<<9ULL); if (partitions[i].offset != offset) { printf("Caution! GPT offset had been changed\n"); gpt_changed = 1; break; } size = ((le64_to_cpu(gpt_pte[i].ending_lba)+1) - le64_to_cpu(gpt_pte[i].starting_lba)) << 9ULL; if (i == parts_num - 1) { if ((partitions[i].size - GPT_TOTAL_SIZE) != size) { printf("Caution! GPT size had been changed\n"); gpt_changed = 1; break; } } else { if (partitions[i].size != size) { printf("Caution! GPT size had been changed\n"); gpt_changed = 1; break; } } /* partition name */ efiname_len = sizeof(gpt_pte[i].partition_name) / sizeof(efi_char16_t); memset(name, 0, PARTNAME_SZ); for (k = 0; k < efiname_len; k++) name[k] = (char)gpt_pte[i].partition_name[k]; if (strcmp(name, partitions[i].name) != 0) { printf("Caution! GPT name had been changed\n"); gpt_changed = 1; break; } } if ((i != parts_num) && (gpt_changed == 0)) { gpt_changed = 1; printf("Caution! GPT number had been changed\n"); } free(gpt_pte); return gpt_changed; } int is_gpt_broken(struct mmc *mmc) { gpt_entry *gpt_pte = NULL; int broken_status = 0; struct blk_desc *dev_desc = mmc_get_blk_desc(mmc); ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); if (is_gpt_valid(dev_desc, GPT_PRIMARY_PARTITION_TABLE_LBA, gpt_head, &gpt_pte) != 1) { broken_status += 1; printf("%s: ***ERROR:Invalid GPT ***\n", __func__); } if (is_gpt_valid(dev_desc, (dev_desc->lba - 1), gpt_head, &gpt_pte) != 1) { printf("%s: ***ERROR: Invalid Backup GPT ***\n", __func__); broken_status += 2; } if (broken_status != 3) free(gpt_pte); return broken_status; } /* * check is gpt is valid * if valid return 0 * else return 1 */ int aml_gpt_valid(struct mmc *mmc) { struct blk_desc *dev_desc = mmc_get_blk_desc(mmc); if (!dev_desc) { printf("%s: Invalid Argument(s)\n", __func__); return 1; } else { ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); gpt_entry *gpt_pte = NULL; if (is_gpt_valid(dev_desc, GPT_PRIMARY_PARTITION_TABLE_LBA, gpt_head, &gpt_pte) != 1) { if (is_gpt_valid(dev_desc, (dev_desc->lba - 1), gpt_head, &gpt_pte) != 1) { printf("gpt is invalid\n"); return 1; } else { printf("%s: *** Using Backup GPT ***\n", __func__); } } } return 0; } void trans_ept_to_diskpart(struct _iptbl *ept, disk_partition_t *disk_part) { struct partitions *part = ept->partitions; int count = ept->count; int i; for (i = 0; i < count; i++) { disk_part[i].start = part[i].offset >> 9; strcpy((char *)disk_part[i].name, part[i].name); /* store maskflag into type, 8bits ONLY! */ disk_part[i].type[0] = (uchar)part[i].mask_flags; #ifdef CONFIG_PARTITION_TYPE_GUID strcpy((char *)disk_part[i].type_guid, part[i].name); #endif #ifdef CONFIG_RANDOM_UUID gen_rand_uuid_str(disk_part[i].uuid, UUID_STR_FORMAT_STD); #endif disk_part[i].bootable = 0; if ( i == (count - 1)) disk_part[i].size = 0; else disk_part[i].size = (part[i].size) >> 9; } return; } #ifdef CONFIG_AML_PARTITION /* * compare ept and rsv * * if different: * update rsv write back on emmc * */ int enable_rsv_part_table(struct mmc *mmc) { struct _iptbl *p_iptbl_rsv = NULL; int ret = -1; /* try to get partition table from rsv */ ret = _zalloc_iptbl(&p_iptbl_rsv); if (ret) return ret; if (!get_ptbl_rsv(mmc, p_iptbl_rsv)) { if (_cmp_iptbl(p_iptbl_ept, p_iptbl_rsv)) { apt_wrn("update rsv with gpt!\n"); ret = update_ptbl_rsv(mmc, p_iptbl_ept); if (ret) printf("update rsv with gpt failed\n"); } } else { printf("rsv not exist\n"); ret = update_ptbl_rsv(mmc, p_iptbl_ept); if (ret) printf("update rsv with gpt failed\n"); } _free_iptbl(p_iptbl_rsv); return ret; } #endif void __attribute__((unused)) _update_part_tbl(struct partitions *p, int count) { int i = 0; while (i < count) { if (strcmp(p[i].name, "boot_a") == 0) has_boot_slot = 1; else if (strcmp(p[i].name, "boot") == 0) has_boot_slot = 0; if (strcmp(p[i].name, "system_a") == 0) has_system_slot = 1; else if (strcmp(p[i].name, "system") == 0) has_system_slot = 0; if (strcmp(p[i].name, "super") == 0) { dynamic_partition = true; env_set("partition_mode", "dynamic"); } if (strncmp(p[i].name, "vendor_boot", 11) == 0) { vendor_boot_partition = true; env_set("vendor_boot_mode", "true"); } i++; } } lbaint_t get_gpt_alternate(struct mmc *mmc) { gpt_header *gpt_h; void *buf; int ret; struct blk_desc *dev_desc = mmc_get_blk_desc(mmc); lbaint_t alternate; buf = malloc(GPT_SIZE); if (!buf) { printf("not enough space for gpt buffer\n"); return -1; } ret = mmc_gpt_read(buf); if (ret == 0) { /* determine start of GPT Header in the buffer */ gpt_h = buf + (GPT_PRIMARY_PARTITION_TABLE_LBA * dev_desc->blksz); alternate = le64_to_cpu(gpt_h->alternate_lba); free(buf); return alternate; } else if (ret == -1) { printf("%s: read gpt failed\n", __func__); free(buf); return -1; } free(buf); return 0; } static uint64_t calc_alternate_check(struct gpt_alternate *gpt_alt) { int i; uint64_t checksum = 0; int size = sizeof(struct gpt_alternate) - sizeof(uint64_t); char *buf = (char *)gpt_alt; for (i = 0; i < size; i++) checksum += buf[i]; return checksum; } int is_gpt_alter_valid(lbaint_t *alternate_lba) { struct gpt_alternate *gpt_alt; int ret; char *name = NULL; loff_t offset = (RESERVED_GPT_OFFSET + MMC_GPT_ALT_OFFSET) >> 9; size_t size = MMC_GPT_ALT_SIZE >> 9; gpt_alt = malloc(MMC_GPT_ALT_SIZE); if (!gpt_alt) return -ENOMEM; ret = mmc_storage_read(name, offset, size, (void *)gpt_alt); if (ret) { free(gpt_alt); return -1; } if (strcmp(gpt_alt->magic, "GPT")) { printf("gpt_alt->magic %s\n", gpt_alt->magic); free(gpt_alt); return -1; } else if (calc_alternate_check(gpt_alt) != gpt_alt->checksum) { printf("gpt_alt->checksum %llx\n", gpt_alt->checksum); free(gpt_alt); return -1; } *alternate_lba = gpt_alt->alternate_lba; free(gpt_alt); return 1; } int write_gpt_alternate(lbaint_t gpt_alternate) { struct gpt_alternate *gpt_alt; char *name = NULL; int ret; loff_t offset = (RESERVED_GPT_OFFSET + MMC_GPT_ALT_OFFSET) >> 9; size_t size = MMC_GPT_ALT_SIZE >> 9; gpt_alt = malloc(MMC_GPT_ALT_SIZE); if (!gpt_alt) return -ENOMEM; memset(gpt_alt, 0, MMC_GPT_ALT_SIZE); strcpy(gpt_alt->magic, "GPT"); gpt_alt->alternate_lba = gpt_alternate; gpt_alt->checksum = calc_alternate_check(gpt_alt); ret = mmc_storage_write(name, offset, size, (void *)gpt_alt); free(gpt_alt); return ret; } /* * check: mbr, first_gpt, secondary_gpt, and alternate addr * * if first_gpt is valid but one of other part is broken * prepare to repair others * else if alternate addr and secondary_gpt is valid * and first_gpt is broken * prepare to repair first_gpt * otherwise * do nothing */ int _mmc_check_gpt(struct mmc *mmc, lbaint_t *alternate) { struct blk_desc *dev_desc = mmc_get_blk_desc(mmc); gpt_entry *gpt_pte = NULL; lbaint_t alternate_lba; u32 entries_num; int i, k; size_t efiname_len; int flag = 0; char name[PARTNAME_SZ]; ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); /* first_gpt is valid, others is broken, repair */ if (is_gpt_valid(dev_desc, GPT_PRIMARY_PARTITION_TABLE_LBA, gpt_head, &gpt_pte) == 1) { alternate_lba = (lbaint_t)le64_to_cpu(gpt_head->alternate_lba); entries_num = le32_to_cpu(gpt_head->num_partition_entries); for (i = 0; i < entries_num; i++) { /* partition name */ efiname_len = sizeof(gpt_pte[i].partition_name) / sizeof(efi_char16_t); memset(name, 0, PARTNAME_SZ); for (k = 0; k < efiname_len; k++) name[k] = (char)gpt_pte[i].partition_name[k]; if (alternate_lba > le64_to_cpu(gpt_pte[i].starting_lba) && alternate_lba < le64_to_cpu(gpt_pte[i].ending_lba)) { printf("GPT: alternate_lba: %lX during %s, invalid\n", alternate_lba, name); flag = 1; } } if (flag == 1) { printf("GPT: alternate_lba: %llX invalid, reset it\n", le64_to_cpu(gpt_head->alternate_lba)); gpt_head->alternate_lba = gpt_pte[1].starting_lba - 1; alternate_lba = (lbaint_t)le64_to_cpu(gpt_head->alternate_lba); *alternate = alternate_lba; free(gpt_pte); return 1; } *alternate = alternate_lba; free(gpt_pte); if (is_gpt_valid(dev_desc, alternate_lba, gpt_head, &gpt_pte) != 1) return 1; free(gpt_pte); if (part_test_efi(dev_desc) || alternate_lba != *alternate || (is_gpt_alter_valid(&alternate_lba) != 1)) return 1; else return 0; /* first_gpt is broken, secondary_gpt is valid, repair */ } else if ((is_gpt_alter_valid(&alternate_lba) == 1) && (is_gpt_valid(dev_desc, alternate_lba, gpt_head, &gpt_pte)) == 1) { *alternate = alternate_lba; free(gpt_pte); return 1; /* legacy mode, secondary_gpt at last lba of mmc */ } else if (is_gpt_valid(dev_desc, dev_desc->lba - 1, gpt_head, &gpt_pte) == 1) { *alternate = dev_desc->lba - 1; free(gpt_pte); return 1; } printf("%s: gpt is invalid and can't be repair\n", __func__); return 0; } int mmc_repair_gpt(struct mmc *mmc, lbaint_t alternate) { gpt_entry *gpt_pte = NULL; struct blk_desc *dev_desc = mmc_get_blk_desc(mmc); int ret; u64 pri_lba = GPT_PRIMARY_PARTITION_TABLE_LBA; ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); if (is_gpt_valid(dev_desc, pri_lba, gpt_head, &gpt_pte) == 1) { gpt_head->alternate_lba = alternate; ret = write_gpt_table(dev_desc, gpt_head, gpt_pte); free(gpt_pte); ret |= write_gpt_alternate(alternate); return ret; } else if (is_gpt_valid(dev_desc, alternate, gpt_head, &gpt_pte) == 1) { /* change alternate header to first header */ prepare_backup_gpt_header(gpt_head); ret = write_gpt_table(dev_desc, gpt_head, gpt_pte); free(gpt_pte); ret |= write_gpt_alternate(alternate); return ret; } return 1; } void mmc_check_gpt(struct mmc *mmc) { lbaint_t alternate; int ret; if (_mmc_check_gpt(mmc, &alternate)) { printf("GPT is not complete\n"); ret = mmc_repair_gpt(mmc, alternate); printf("GPT is repaired %s\n", ret ? "failed" : "success"); } printf("gpt is complete\n"); } int resize_gpt(struct mmc *mmc) { gpt_header *gpt_h; void *buf; int ret; struct blk_desc *dev_desc = mmc_get_blk_desc(mmc); buf = malloc(GPT_SIZE); if (!buf) { printf("not enough space for gpt buffer\n"); return -1; } ret = mmc_gpt_read(buf); if (ret == 0) { /* determine start of GPT Header in the buffer */ gpt_h = buf + (GPT_PRIMARY_PARTITION_TABLE_LBA * dev_desc->blksz); if (le64_to_cpu(gpt_h->last_usable_lba) > dev_desc->lba) { ret = write_mbr_and_gpt_partitions(dev_desc, (u_char *)buf); if (ret) { printf("%s: writing GPT partitions failed\n", __func__); free(buf); return -1; } printf("resize gpt success\n"); } } else if (ret == -1) { printf("%s: read gpt failed\n", __func__); free(buf); return -1; } free(buf); return 0; } /*************************************************** * init partition table for emmc device. * returns 0 means ok. * other means failure. *************************************************** * work flows: * source of logic partition table(LPT) is from dts * no matter MACRO is on/off * 1. try to get LPT from dtb * 1.1 if dtb exist, compose ept by LPT&inh * 1.2 if not, go ahead * 2. try to get ept from emmc rsv partition * 2.1 if not: * 2.1.1 when dtb exists * 2.1.1.1 check ept with dtb * 2.1.1.2 update rsv if needed * 2.1.1 without dtb, exit * 2.2 if got: * 2.2.1 try to reconstruct ept by MBR * 2.2.2 check it with ept * 2.2.3 update MBR if needed *************************************************** * when normal boot: * without dtb, with rsv, with MBR * when blank emmc: * without dtb, without rsv, without MBR * when burning MBR on a blank emmc: * with dtb, without rsv, without MBR * when burning MBR on a emmc with rsv: * with dtb, with rsv, without MBR * when burning MBR on a emmc with rsv&MBR: * with dtb, with rsv, with MBR ***************************************************/ int mmc_device_init (struct mmc *mmc) { int ret = 1; #ifdef CONFIG_AML_PARTITION int update = 1; struct _iptbl *p_iptbl_rsv = NULL; #endif #if (CONFIG_PTBL_MBR) || (!CONFIG_AML_PARTITION) cpu_id_t cpu_id = get_cpu_id(); #endif /* partition table from dtb/code/emmc rsv */ struct _iptbl iptbl_dtb, iptbl_inh; /* For re-entry */ if (!p_iptbl_ept) { ret = _zalloc_iptbl(&p_iptbl_ept); if (ret) goto _out; } else { p_iptbl_ept->count = 0; memset(p_iptbl_ept->partitions, 0, sizeof(struct partitions) * MAX_PART_COUNT); } //get bootloader index const int bootCpyIndex = store_bootup_bootidx("bootloader"); printf("bootCpyIndex = %d\n", bootCpyIndex); if (bootCpyIndex != 0) { mmc_check_gpt(mmc); if (resize_gpt(mmc)) goto _out; } /* calculate inherent offset */ iptbl_inh.count = get_emmc_partition_arraysize(); if (iptbl_inh.count) { iptbl_inh.partitions = emmc_partition_table; _calculate_offset(mmc, &iptbl_inh, 0); } apt_info("inh count %d\n", iptbl_inh.count); ret = get_ept_from_gpt(mmc); if (!ret) { #ifdef CONFIG_AML_PARTITION /* init part again */ part_init(mmc_get_blk_desc(mmc)); return enable_rsv_part_table(mmc); #else part_init(mmc_get_blk_desc(mmc)); return ret; #endif } #if (CONFIG_MPT_DEBUG) apt_info("inherent partition table\n"); _dump_part_tbl(iptbl_inh.partitions, iptbl_inh.count); #endif /* try to get partition table from dtb(ddr or emmc) */ iptbl_dtb.partitions = get_ptbl_from_dtb(mmc); /* construct ept by dtb if exist */ if (iptbl_dtb.partitions) { iptbl_dtb.count = get_partition_count(); apt_info("dtb %p, count %d\n", iptbl_dtb.partitions, iptbl_dtb.count); /* reserved partition must exist! */ if (iptbl_inh.count) { compose_ept(&iptbl_dtb, &iptbl_inh, p_iptbl_ept); if (0 == p_iptbl_ept->count) { apt_err("compose partition table failed!\n"); goto _out; } /* calculate offset infos. considering GAPs */ if (_calculate_offset(mmc, p_iptbl_ept, 1)) { goto _out; } #if (CONFIG_MPT_DEBUG) apt_info("ept partition table\n"); _dump_part_tbl(p_iptbl_ept->partitions, p_iptbl_ept->count); #endif } else { /* report fail, because there is no reserved partitions */ apt_err("compose partition table failed!\n"); ret = -1; goto _out; } } else apt_wrn("get partition table from dtb failed\n"); #ifndef CONFIG_AML_PARTITION if (cpu_id.family_id < MESON_CPU_MAJOR_ID_G12B) { printf("CONFIG_AML_PARTITION should define before G12B\n"); goto _out; } #endif #ifdef CONFIG_AML_PARTITION /* try to get partiton table from rsv */ ret = _zalloc_iptbl(&p_iptbl_rsv); if (ret) goto _out; ret = get_ptbl_rsv(mmc, p_iptbl_rsv); if (p_iptbl_rsv->count) { /* dtb exist, p_iptbl_ept already inited */ if (iptbl_dtb.partitions) { ret = _cmp_iptbl(p_iptbl_ept, p_iptbl_rsv); if (!ret) { update = 0; } } else { /* without dtb, update ept with rsv */ #if 0 p_iptbl_ept->count = p_iptbl_rsv->count; memcpy(p_iptbl_ept->partitions, p_iptbl_rsv->partitions, p_iptbl_ept->count * sizeof(struct partitions)); #endif _cpy_iptbl(p_iptbl_ept, p_iptbl_rsv); update = 0; } } else { /* without dtb& rsv */ if (!iptbl_dtb.partitions) { apt_err("dtb&rsv are not exist, no LPT source\n"); ret = -9; goto _out; } } if (update && iptbl_dtb.partitions && (aml_gpt_valid(mmc) != 0)) { apt_wrn("update rsv with dtb!\n"); ret = update_ptbl_rsv(mmc, p_iptbl_ept); } #endif //apt_wrn("ept source is %s\n", (ept_source == p_iptbl_ept)?"ept":"rsv"); #if (CONFIG_PTBL_MBR) /* 1st sector was reserved by romboot after gxl */ if (cpu_id.family_id >= MESON_CPU_MAJOR_ID_GXL) { if (_check_ptbl_mbr(mmc, p_iptbl_ept)) { /*fixme, comaptible for mbr&ebr */ ret |= _update_ptbl_mbr(mmc, p_iptbl_ept); apt_wrn("MBR Updated!\n"); } } #endif _update_part_tbl(p_iptbl_ept->partitions, p_iptbl_ept->count); /* init part again */ part_init(mmc_get_blk_desc(mmc)); _out: #ifdef CONFIG_AML_PARTITION if (p_iptbl_rsv) _free_iptbl(p_iptbl_rsv); #endif return ret; } struct partitions *find_mmc_partition_by_name (char const *name) { struct partitions *partition = NULL; apt_info("p_iptbl_ept %p\n", p_iptbl_ept); if (NULL == p_iptbl_ept) { goto _out; } partition = p_iptbl_ept->partitions; partition = _find_partition_by_name(partition, p_iptbl_ept->count, name); apt_info("partition %p\n", partition); if (!partition) { partition = _find_partition_by_name(emmc_partition_table, get_emmc_partition_arraysize(), name); } apt_info("partition %p\n", partition); _out: return partition; } struct partitions *find_ept_partition_by_name(char const *name) { struct partitions *partition = NULL; apt_info("p_iptbl_ept %p\n", p_iptbl_ept); if (!p_iptbl_ept) goto _out; partition = p_iptbl_ept->partitions; partition = _find_partition_by_name(partition, p_iptbl_ept->count, name); apt_info("partition %p\n", partition); _out: return partition; } /* find virtual partition in inherent table. */ int find_virtual_partition_by_name (char const *name, struct partitions *partition) { int ret = 0; ulong offset; struct virtual_partition *vpart = aml_get_virtual_partition_by_name(MMC_DTB_NAME); if (NULL == partition) return -1; offset = _get_inherent_offset(MMC_RESERVED_NAME); if (-1 == offset) { apt_err("can't find %s in inherent\n", MMC_RESERVED_NAME); return -1; } if (!strcmp(name, "dtb")) { strncpy(partition->name, name, sizeof(partition->name) - 1); partition->name[sizeof(partition->name) - 1] = '\0'; partition->offset = offset + vpart->offset; partition->size = (vpart->size * DTB_COPIES); } return ret; } int find_dev_num_by_partition_name (char const *name) { int dev = -1; /* card */ if (!strcmp(name, MMC_CARD_PARTITION_NAME)) { dev = 0; } else { /* eMMC OR TSD */ /* partition name is valid */ if (find_mmc_partition_by_name(name)) { dev = 1; } } return dev; } static inline uint64_t get_part_size(struct partitions *part, int num) { return part[num].size; } static inline uint64_t get_part_offset(struct partitions *part, int num) { return part[num].offset; } static inline char * get_part_name(struct partitions *part, int num) { return (char *)part[num].name; } int get_part_info_from_tbl(struct blk_desc *dev_desc, int num, disk_partition_t *info) { int ret = 0; struct partitions *part; if (NULL == p_iptbl_ept) return -1; if (num > (p_iptbl_ept->count-1)) return -1; part = p_iptbl_ept->partitions; /*get partition info by index! */ info->start = (lbaint_t)(get_part_offset(part, num)/dev_desc->blksz); info->size = (lbaint_t)(get_part_size(part, num)/dev_desc->blksz); info->blksz = dev_desc->blksz; strcpy((char *)info->name, get_part_name(part, num)); return ret; } #if (CONFIG_MPT_DEBUG) void show_partition_info(disk_partition_t *info) { printf("----------%s----------\n", __func__); printf("name %10s\n", info->name); printf("blksz " LBAFU "\n", info->blksz); printf("sart %ld\n", info->start); printf("size %ld\n", info->size); printf("----------end----------\n"); } #endif struct partitions *aml_get_partition_by_name(const char *name) { struct partitions *partition = NULL; partition = _find_partition_by_name(emmc_partition_table, get_emmc_partition_arraysize(), name); if (partition == NULL) apt_wrn("do not find match in inherent table %s\n", name); return partition; } struct virtual_partition *aml_get_virtual_partition_by_name(const char *name) { int i = 0, cnt; struct virtual_partition *part = NULL; cnt = get_emmc_virtual_partition_arraysize(); while (i < cnt) { part = &virtual_partition_table[i]; if (!strcmp(name, part->name)) { apt_info("find %10s @ tbl[%d]\n", name, i); break; } i++; }; if (i == cnt) { part = NULL; apt_wrn("do not find match in table %10s\n", name); } return part; } int get_part_info_by_name(struct blk_desc *dev_desc, const char *name, disk_partition_t *info) { struct partitions *partition = NULL; struct partitions virtual; int ret = 0; cpu_id_t cpu_id = get_cpu_id(); partition = find_mmc_partition_by_name((char *)name); if (partition) { info->start = (lbaint_t)(partition->offset/dev_desc->blksz); info->size = (lbaint_t)(partition->size/dev_desc->blksz); info->blksz = dev_desc->blksz; strcpy((char *)info->name, partition->name); } else if (!find_virtual_partition_by_name((char *)name, &virtual)) { /* try virtual partitions */ apt_wrn("Got %s in virtual table\n", name); info->start = (lbaint_t)(virtual.offset/dev_desc->blksz); info->size = (lbaint_t)(virtual.size/dev_desc->blksz); info->blksz = dev_desc->blksz; strcpy((char *)info->name, virtual.name); } else { /* all partitions were tried, fail */ ret = -1; goto _out; } /* for bootloader */ if ((0 == info->start) && (cpu_id.family_id >= MESON_CPU_MAJOR_ID_GXL)) { info->start = 1; info->size -= 1; } #if (CONFIG_MPT_DEBUG) show_partition_info(info); #endif _out: return ret; } /* * get the partition number by name * return value * < 0 means no partition found * >= 0 means valid partition */ __weak int get_partition_num_by_name(char const *name) { int ret = -1; struct partitions *partition = NULL; if (NULL == p_iptbl_ept) goto _out; partition = p_iptbl_ept->partitions; ret = _get_part_index_by_name(partition, p_iptbl_ept->count, name); _out: return ret; } /* * get the partition info by number * return value * < 0 means no partition found * >= 0 means valid partition */ __weak struct partitions *get_partition_info_by_num(const int num) { struct partitions *partition = NULL; if ((NULL == p_iptbl_ept) || (num >= p_iptbl_ept->count)) goto _out; partition = &p_iptbl_ept->partitions[num]; _out: return partition; } u64 map_last_gap(struct mmc *mmc, const char *name) { u64 lubyte; u64 last_byte; struct blk_desc *dev_desc = mmc_get_blk_desc(mmc); int count = p_iptbl_ept->count; struct partitions *partitions = p_iptbl_ept->partitions; lubyte = partitions[count - 1].offset + partitions[count - 1].size; last_byte = dev_desc->lba << 9ULL; if (lubyte > last_byte) return 0; else if (last_byte - lubyte < SZ_1M) return 0; if (!strcmp(name, MMC_PATTERN_NAME)) return lubyte; else if (!strcmp(name, MMC_MAGIC_NAME)) return lubyte + CALI_PATTERN_SIZE; else if (!strcmp(name, MMC_RANDOM_NAME)) return lubyte + CALI_PATTERN_SIZE + MAGIC_SIZE; else if (!strcmp(name, MMC_DDR_PARAMETER_NAME)) return lubyte + CALI_PATTERN_SIZE + MAGIC_SIZE + RANDOM_SIZE; else return 0; }