/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ /* * drivers/mmc/aml_emmc_partition.c * * Copyright (C) 2020 Amlogic, Inc. All rights reserved. * */ #include #include #include #include #include #include #include #include #include #include "mmc_private.h" #include #include #include #include #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) /*CONFIG_AML_GPT meas gpt is from ept*/ /*else gpt is individual*/ #ifdef CONFIG_AML_GPT #define GPT_PRIORITY (0) #else #define GPT_PRIORITY (1) #endif /* CONFIG_AML_GPT */ #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 (16) #define MAX_PART_COUNT MAX_MMC_PART_NUM bool dts_to_gpt = true; /* 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) */ u64 alter_gpt_lba; #define GPT_SIZE 0x4400 /* 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 */ unsigned 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; unsigned int count; /* partition count in use */ }; #ifdef CONFIG_AML_NAND unsigned device_boot_flag = (unsigned)_AML_DEVICE_BOOT_FLAG_DEFAULT; #else /*unsigned device_boot_flag = (unsigned)_AML_DEVICE_BOOT_FLAG_DEFAULT;*/ unsigned device_boot_flag = (unsigned)EMMC_BOOT_FLAG; #endif bool is_partition_checked = false; #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*/ /* gpt not support 0 size partition */ #ifndef CONFIG_YOCTO PARTITION_ELEMENT(MMC_CACHE_NAME, 0, 0), #endif 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 */ VIRTUAL_PARTITION_ELEMENT(MMC_TABLE_NAME, MMC_TABLE_OFFSET, MMC_TABLE_SIZE), 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_DTB_NAME, DTB_OFFSET, DTB_SIZE), VIRTUAL_PARTITION_ELEMENT(MMC_FASTBOOT_CONTEXT_NAME, FASTBOOT_CONTEXT_OFFSET, FASTBOOT_CONTEXT_SIZE), VIRTUAL_PARTITION_ELEMENT(MMC_MAGIC_NAME, MAGIC_OFFSET, MAGIC_SIZE), VIRTUAL_PARTITION_ELEMENT(MMC_RANDOM_NAME, RANDOM_OFFSET, RANDOM_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), }; 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); return i; } i++; }; return -1; } 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); return part; } i++; }; return NULL; } /* fixme, must called after offset was calculated. */ 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 */ 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 = mmc->block_dev.block_read(mmc->block_dev.dev, _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 = mmc->block_dev.block_write(mmc->block_dev.dev, _blk, _cnt, buffer); return (ulong)(_cnt * mmc->read_bl_len); } static struct partitions * get_ptbl_from_dtb(struct mmc *mmc) { struct partitions * ptbl = NULL; #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 /* asign partition info to *ptbl */ ptbl = get_partitions(); return ptbl; #ifndef DTB_BIND_KERNEL _err1: if (buffer) free(buffer); _err: free (ptbl); return NULL; #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; part->offset = 0; #if (CONFIG_MPT_DEBUG) _dump_part_tbl(part, itbl->count); #endif if (!strcmp(part->name, MMC_BOOT_NAME)) { gap = MMC_BOOT_PARTITION_RESERVED; if (!is_mainstorage_emmc()) sprintf(part->name, "bootloadere"); } 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 (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 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 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; } } return; } /* 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 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) { setenv("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; } static void _free_iptbl(struct _iptbl *iptbl) { if (iptbl && iptbl->partitions) { free(iptbl->partitions); iptbl->partitions = NULL; } if (iptbl) { free(iptbl); iptbl = NULL; } return; } /* * fixme, need check space size later. */ 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; } if (src->count > MAX_MMC_PART_NUM) { ret = -3; goto _out; } dst->count = src->count; memcpy(dst->partitions, src->partitions, sizeof(struct partitions) * src->count); _out: return ret; } 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 extern_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 = mmc->block_dev.block_read(mmc->block_dev.dev, 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 external 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) { extern_start = logic_start; read_offset = extern_start; }else read_offset = extern_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 partitions is a fixed value of PARTITION_RESERVED ,otherwise the emmc * partitions 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 extern_size = 0; int i; /* the entry is active or not */ p_entry->boot_ind = 0x00; if (part_num == 3) {/* the logic partition 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++) extern_size = p_iptbl->partitions[i].size >> 9; memcpy((unsigned char *)p_entry->start4, &start_offset, 4); memcpy((unsigned char *)p_entry->size4, &extern_size, 4); } else {/* the primary partition 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)); if (mbr == NULL) return -ENOMEM; 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 = mmc->block_dev.block_write(mmc->block_dev.dev, 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; } #ifdef CONFIG_AML_GPT 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; int gpt_part_num; uint64_t offset; uint64_t size; char name[PARTNAME_SZ]; int gpt_changed = 0; block_dev_desc_t *dev_desc = &mmc->block_dev; ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); if (!dev_desc) { printf("%s: Invalid Argument(s)\n", __func__); return 1; } 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__); } return 1; } gpt_part_num = le32_to_cpu(gpt_head->num_partition_entries); for (i = 0; i < gpt_part_num; i++) { if (!is_pte_valid(&gpt_pte[i])) break; offset = le64_to_cpu(gpt_pte[i].starting_lba << 9ULL); /* ept skip bootloader */ if (partitions[i + 1].offset != offset) { printf("Caution! GPT %d offset had been changed\n", i); 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 != gpt_part_num - 1) && partitions[i + 1].size != size) { printf("Caution! GPT %d size had been changed\n", i); 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 + 1].name) != 0) { printf("Caution! GPT name %d had been changed\n", i); gpt_changed = 1; break; } } if ((i != parts_num - 1) && gpt_changed == 0) { gpt_changed = 1; printf("Caution! GPT number had been changed\n"); } free(gpt_pte); return gpt_changed; } int fill_ept_by_gpt(struct mmc *mmc, struct _iptbl *p_iptbl_ept) { block_dev_desc_t *dev_desc = &mmc->block_dev; ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); 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; u64 alternate; if (!dev_desc) { printf("%s: Invalid Argument(s)\n", __func__); return 1; } if (is_gpt_valid(dev_desc, GPT_PRIMARY_PARTITION_TABLE_LBA, gpt_head, &gpt_pte) != 1) { printf("%s: ***ERROR:Invalid GPT ***\n", __func__); alternate = get_gpt_alternate(mmc); if (alternate != 0) { if (is_gpt_valid(dev_desc, alternate, gpt_head, &gpt_pte) != 1) { printf("%s: ***ERROR: Invalid Backup GPT ***\n", __func__); return 1; } } else { printf("%s: *** Using Backup GPT ***\n", __func__); } return 1; } strncpy((char *)partitions, (char *)emmc_partition_table, sizeof(struct partitions)); for (i = 0; i < le32_to_cpu(gpt_head->num_partition_entries); i++) { if (!is_pte_valid(&gpt_pte[i])) break; partitions[i + 1].offset = le64_to_cpu(gpt_pte[i].starting_lba << 9ULL); partitions[i + 1].size = ((le64_to_cpu(gpt_pte[i].ending_lba) + 1) - le64_to_cpu(gpt_pte[i].starting_lba)) << 9ULL; /* partition name */ efiname_len = sizeof(gpt_pte[i].partition_name) / sizeof(efi_char16_t); dosname_len = sizeof(partitions[i].name); memset(partitions[i + 1].name, 0, sizeof(partitions[i].name)); for (k = 0; k < min(dosname_len, efiname_len); k++) partitions[i + 1].name[k] = (char)gpt_pte[i].partition_name[k]; } free(gpt_pte); 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; /* ept keep bootloader but gpt remove bootloader */ for (i = 0; i < count - 1; i++) { disk_part[i].start = part[i + 1].offset >> 9; strcpy((char *)disk_part[i].name, part[i+1].name); gen_rand_uuid_str(disk_part[i].uuid, UUID_STR_FORMAT_STD); disk_part[i].bootable = 0; if (i == (count - 2) && alter_gpt_lba == 0) disk_part[i].size = (part[i + 1].size - 34 * 512) >> 9; else disk_part[i].size = (part[i + 1].size) >> 9; apt_info("%s, 0x%lx, %lx\n", disk_part[i].name, disk_part[i].start, disk_part[i].size); } return; } u64 find_alter_gpt_lba(struct _iptbl *ept) { struct partitions *part = ept->partitions; int mask_flag = 1 << 13; int i; for (i = 0; i < ept->count; i++) { if (part[i].mask_flags & mask_flag) { alter_gpt_lba = (part[i].offset >> 9) - 1; return alter_gpt_lba; } } return 0; } /********************************* * * note: ept shouldn't empty * otherwise this function * shouldn't be called * * ******************************* * * if gpt is empty * construct gpt by ept * elif partition table is changed * if ept has higher priority * use ept update gpt * else * use gpt update ept * ********************************/ int confirm_gpt(struct mmc *mmc) { char *str_disk_guid; int gpt_priority = GPT_PRIORITY; int dcount; int ret = 1; disk_partition_t *disk_partition; block_dev_desc_t *dev_desc = &mmc->block_dev; gpt_entry *gpt_pte = NULL; alter_gpt_lba = 0; ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); /*remove bootloader partition*/ dcount = p_iptbl_ept->count - 1; if (dcount < 1) return 1; alter_gpt_lba = find_alter_gpt_lba(p_iptbl_ept); printf("alter gpt 0x%llx\n", alter_gpt_lba); disk_partition = malloc(PAD_TO_BLOCKSIZE(sizeof(disk_partition_t) * dcount, dev_desc)); if (disk_partition == NULL) return -ENOMEM; memset(disk_partition, 0, sizeof(disk_partition_t) * dcount); trans_ept_to_diskpart(p_iptbl_ept, disk_partition); str_disk_guid = malloc(UUID_STR_LEN + 1); if (str_disk_guid == NULL) { free(disk_partition); return -ENOMEM; } gen_rand_uuid_str(str_disk_guid, UUID_STR_FORMAT_STD); if (is_gpt_valid(dev_desc, GPT_PRIMARY_PARTITION_TABLE_LBA, gpt_head, &gpt_pte) != 1) { /*gpt is empty write gpt*/ ret = gpt_restore(&mmc->block_dev, str_disk_guid, disk_partition, dcount); printf("GPT IS RESTORED %s\n", ret ? "Failed!" : "OK!"); } else if (is_gpt_changed(mmc, p_iptbl_ept)) { if (gpt_priority) { /*gpt have higher priority update ept*/ ret = fill_ept_by_gpt(mmc, p_iptbl_ept); printf("and gpt has higher priority, so ept had been update\n"); } else { /*ept have higher priority update gpt*/ ret = gpt_restore(&mmc->block_dev, str_disk_guid, disk_partition, dcount); printf("but ept has higher priority, so gpt had been recover\n"); } } free(str_disk_guid); free(disk_partition); free(gpt_pte); return ret; } int mmc_gpt_read(void *source) { struct mmc *mmc; block_dev_desc_t *dev_desc; unsigned long offset = 0; size_t size = 34; lbaint_t cnt; mmc = find_mmc_device(1); if (!mmc) return -1; dev_desc = &mmc->block_dev; cnt = dev_desc->block_read(mmc->block_dev.dev, offset, size, source); if (cnt != size) return -1; if (is_valid_gpt_buf(dev_desc, (u_char *)source)) { printf("%s: invalid GPT\n", __func__); return 1; } return 0; } lbaint_t get_gpt_alternate(struct mmc *mmc) { gpt_header *gpt_h; void *buf; int ret; block_dev_desc_t *dev_desc = &mmc->block_dev; lbaint_t alternate; buf = malloc(GPT_SIZE); if (!buf) { printf("not enough space for gpt buffer\n"); return 0; } 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 0; } free(buf); return 0; } u64 calc_alternate_check(struct gpt_alternate *gpt_alt) { int i; u64 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; loff_t offset = (RESERVED_GPT_OFFSET + MMC_GPT_ALT_OFFSET) >> 9; size_t size = MMC_GPT_ALT_SIZE >> 9; lbaint_t cnt; block_dev_desc_t *dev_desc; struct mmc *mmc; mmc = find_mmc_device(1); if (!mmc) return -1; gpt_alt = malloc(MMC_GPT_ALT_SIZE); if (!gpt_alt) return -ENOMEM; dev_desc = &mmc->block_dev; cnt = dev_desc->block_read(mmc->block_dev.dev, offset, size, gpt_alt); if (cnt != size) { 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; int ret = 0; loff_t offset = (RESERVED_GPT_OFFSET + MMC_GPT_ALT_OFFSET) >> 9; size_t size = MMC_GPT_ALT_SIZE >> 9; lbaint_t cnt; struct mmc *mmc; mmc = find_mmc_device(1); if (!mmc) return -1; 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); cnt = mmc->block_dev.block_write(mmc->block_dev.dev, offset, size, gpt_alt); if (cnt != size) ret = 1; 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) { block_dev_desc_t *dev_desc = &mmc->block_dev; gpt_entry *gpt_pte = NULL; lbaint_t alternate_lba; 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); *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 (test_part_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; block_dev_desc_t *dev_desc = &mmc->block_dev; 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) { 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) { block_dev_desc_t *dev_desc = &mmc->block_dev; gpt_header *gpt_h; void *buf; int ret; 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; } #endif //CONFIG_AML_GPT 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; setenv("partition_mode", "dynamic"); } if (strncmp(p[i].name, "vendor_boot", 11) == 0) { vendor_boot_partition = true; setenv("vendor_boot_mode", "true"); } i++; } } /*************************************************** * 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; #if (CONFIG_PTBL_MBR) cpu_id_t cpu_id = get_cpu_id(); #endif /* partition table from dtb/code/emmc rsv */ struct _iptbl iptbl_dtb, iptbl_inh; struct _iptbl *p_iptbl_rsv = NULL; int update = 1; /* 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); #if (CONFIG_MPT_DEBUG) apt_info("inherent partition table\n"); _dump_part_tbl(iptbl_inh.partitions, iptbl_inh.count); #endif /* For re-entry */ if (NULL == 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); } #ifdef CONFIG_AML_GPT static int skip_gpt = -1; ret = check_dts_valid(&mmc->block_dev); if (ret == 0) { skip_gpt = 1; dts_to_gpt = true; } else { skip_gpt = 0; dts_to_gpt = false; } #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"); /* try to get table of partitions 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) { apt_wrn("update rsv with dtb!\n"); ret = update_ptbl_rsv(mmc, p_iptbl_ept); } //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, compatible for mbr&ebr */ ret |= _update_ptbl_mbr(mmc, p_iptbl_ept); apt_wrn("MBR Updated!\n"); } } #endif #ifdef CONFIG_AML_GPT if (skip_gpt == 0) { mmc_check_gpt(mmc); confirm_gpt(mmc); if (alter_gpt_lba != 0) write_gpt_alternate(alter_gpt_lba); if (resize_gpt(mmc)) goto _out; } #endif _update_part_tbl(p_iptbl_ept->partitions, p_iptbl_ept->count); /* init part again */ init_part(&mmc->block_dev); _out: if (p_iptbl_rsv) _free_iptbl(p_iptbl_rsv); return ret; } struct partitions *find_mmc_partition_by_name (char *name) { struct partitions *partition = NULL; if (NULL == p_iptbl_ept) goto _out; partition = p_iptbl_ept->partitions; partition = _find_partition_by_name(partition, p_iptbl_ept->count, name); _out: return partition; } /* find virtual partition in inherent table. */ int find_virtual_partition_by_name (char *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, 3); partition->offset = offset + vpart->offset; partition->size = (vpart->size * DTB_COPIES); } return ret; } int find_dev_num_by_partition_name (char *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; } unsigned get_part_tbl_from_ept(int num, char *name) { struct partitions *part = NULL; if (NULL == p_iptbl_ept) return -1; if (num > (p_iptbl_ept->count-1)) return -1; part = p_iptbl_ept->partitions; strcpy(name, get_part_name(part, num)); return part[num].mask_flags; } int get_part_info_from_tbl(block_dev_desc_t *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 int mmc_boot_size(char *name, uint64_t* size) { return get_boot_size(name, size); } 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(block_dev_desc_t *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 *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; }