// SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2018-2020 * Allwinner Technology Co., Ltd. * wangwei * */ #include #include #include #include #include #include #include #include #ifndef CONFIG_ENABLE_MTD_CMDLINE_PARTS_BY_ENV static sunxi_mbr_t *mbr ; #endif extern struct bootloader_control ab_message; DECLARE_GLOBAL_DATA_PTR; #if defined (CONFIG_ENABLE_MTD_CMDLINE_PARTS_BY_ENV) typedef struct sunxi_env_partition_t { unsigned char name[16]; unsigned int addrlo; unsigned int lenlo; unsigned int ro; }sunxi_env_partition; typedef struct sunxi_env_mtd_t { unsigned char device_name[16]; unsigned int device_id; unsigned int PartCount; sunxi_env_partition array[SUNXI_MBR_MAX_PART_COUNT]; }sunxi_env_mtd; static sunxi_env_mtd env_mtd; int sunxi_partition_parse_get_info(int index, disk_partition_t *info) { int find_flag = 0; char *src; char *des; char tmp[5]; int partcount; int count; int size; char *cmdline = env_get("sunxi_mtdparts"); if(cmdline == NULL) { printf("error:can't find mtdparts in env.\n"); debug("get partition error\n"); return -1; } //name src = cmdline; des = strchr(src, '.'); count = (int)(des-src); strncpy((char *)(env_mtd.device_name), src,count); //printf("the device.name = %s\n",env_mtd.device_name); src = des; //id memset(tmp, 0, 5); des = strchr(src, ':'); count = (int)(des-src); //printf("count %d\n",count); strncpy(tmp, (src+1), (count-1)); env_mtd.device_id = (int)simple_strtoul(tmp, NULL, 10); //printf("env_mtd.device_id = %d\n",env_mtd.device_id); src = des; //partition for(partcount=0; *(des+1)!='\0'; partcount++) { //size memset(tmp, 0, 5); des = strchr(src, '('); count = (int)(des-src); //printf("count %d\n",count); strncpy(tmp,src+1,count-2); size = (int)simple_strtoul(tmp, NULL, 10); //printf("size : %d\n",size); if(*(des-1) == 'K') env_mtd.array[partcount].lenlo = size *1024 / 512; else if(*(des-1) == 'M') env_mtd.array[partcount].lenlo = size *1024 * 1024 / 512; if(partcount == 0) env_mtd.array[partcount].addrlo = 0; else env_mtd.array[partcount].addrlo = env_mtd.array[partcount-1].addrlo + env_mtd.array[partcount-1].lenlo; //partition_name src = des; des = strchr(src, ')'); count = (int)(des-src); strncpy((char *)(env_mtd.array[partcount].name),src+1,count-1); //only-read ? src = des; des = strchr(src, ','); if(des == NULL) { env_mtd.array[partcount].ro = 0; break; } count = (int)(des-src); if(!(count-1) && !strncmp((src+1), "ro",2)) env_mtd.array[partcount].ro = 1; else env_mtd.array[partcount].ro = 0; src = des; if((index-1) == partcount) { find_flag = 1; strncpy((char *)info->name, (const char *)env_mtd.array[partcount].name, PART_NAME_LEN); info->start = env_mtd.array[partcount].addrlo; info->size = env_mtd.array[partcount].lenlo; //printf("name:%s, size:%d, offset:%x, ro:%d\n",env_mtd.array[partcount].name,env_mtd.array[partcount].lenlo,env_mtd.array[partcount].addrlo,env_mtd.array[partcount].ro); break ; //dont check all } } if(!find_flag) { return -1; } /* env_mtd.PartCount = partcount + 1; printf("partition_count:%d\n",env_mtd.PartCount); */ return 0; } #endif /** * Parses a string into a number. The number stored at ptr is * potentially suffixed with K (for kilobytes, or 1024 bytes), * M (for megabytes, or 1048576 bytes), or G (for gigabytes, or * 1073741824). If the number is suffixed with K, M, or G, then * the return value is the number multiplied by one kilobyte, one * megabyte, or one gigabyte, respectively. * * @param ptr where parse begins * @param retptr output pointer to next char after parse completes (output) * @return resulting unsigned int */ static u64 size_parse(const char *const ptr, const char **retptr) { u64 ret = simple_strtoull(ptr, (char **)retptr, 0); switch (**retptr) { case 'G': case 'g': ret <<= 10; case 'M': case 'm': ret <<= 10; case 'K': case 'k': ret <<= 10; (*retptr)++; default: break; } return ret; } int sunxi_partition_parse(const char *name, disk_partition_t *info) { const char *src, *des, *part_name; const char *cmdline = env_get("mtdparts"); int name_len; if (!cmdline) { printf("error:can't find mtdparts in env.\n"); debug("get partition error\n"); return -1; } src = strchr(cmdline, ':'); if (!src) { printf("Invalid mtdparts parameter\n"); return -1; } while (1) { des = strchr(src, '('); if (!des) goto err; part_name = ++des; des = strchr(des, ')'); if (!des) goto err; name_len = des - part_name; if (!strncmp(name, part_name, name_len)) break; src = strchr(des, ','); if (!src) goto err; } src++; if (*src == '-') goto err; strncpy((char *)info->name, part_name, name_len); info->size = size_parse(src, &src); if (*src == '@') { src++; info->start = size_parse(src, &src); } return 0; err: printf("%s not found in mtdparts\n", name); return -1; } int sunxi_partition_init(void) { #ifndef CONFIG_ENABLE_MTD_CMDLINE_PARTS_BY_ENV if (mbr == NULL) { mbr = memalign(ARCH_DMA_MINALIGN, SUNXI_MBR_SIZE); if (mbr == NULL) { printf("unable to allocate TDs\n"); return -1; } if (sunxi_flash_read(0, SUNXI_MBR_SIZE / 512, (void *)mbr) <= 0) { printf("line:%d:sunxi_flash_read fail\n", __LINE__); return -1; } if (!strncmp((const char *)mbr->magic, SUNXI_MBR_MAGIC, 8)) { int crc = 0; crc = crc32(0, (const unsigned char *)&mbr->version, SUNXI_MBR_SIZE - 4); if (crc != mbr->crc32) { return -1; } gd->lockflag = mbr->lockflag; } } #endif return 0; } int sunxi_probe_partition_map(void) { #ifndef CONFIG_ENABLE_MTD_CMDLINE_PARTS_BY_ENV struct blk_desc *desc; desc = blk_get_devnum_by_typename("sunxi_flash", 0); if (desc == NULL) { pr_err("%s: get desc fail\n", __func__); return -1; } if (part_init_info_map(desc) < 0) return -1; else #endif return 0; } int sunxi_replace_android_ab_system(char *intput_part_name, char *output_part_name) { strncpy(output_part_name, intput_part_name, strlen(intput_part_name)); #ifdef CONFIG_ANDROID_AB int i, ret; static int part_cur_len; static char ab_partition[16][16] = {"bootloader", "env", "boot", "vendor_boot", "init_boot", "dtbo", "vbmeta", "vbmeta_system", "vbmeta_vendor"}; char *ab_part_list = env_get("ab_partition_list"); if ((gd->env_has_init) && (ab_part_list != NULL) && (part_cur_len != strlen(ab_part_list))) { memset(ab_partition, 0, sizeof(ab_partition)); sunxi_parsed_specific_string(ab_part_list, ab_partition, ',', ' '); part_cur_len = strlen(ab_part_list); } ret = ab_select_slot_from_partname("misc"); /* tick_printf("output_part_name:%s\t intput_part_name:%s\n", output_part_name, intput_part_name); */ for (i = 0; i < sizeof(ab_partition)/sizeof(ab_partition[0]); i++) { if (!strncmp(intput_part_name, ab_partition[i], max(strlen(intput_part_name), strlen(ab_partition[i])))) { if (ret == 1) { sprintf(output_part_name, "%s_b", intput_part_name); } else { sprintf(output_part_name, "%s_a", intput_part_name); } break; } } #endif /* tick_printf("output_part_name:%s\t intput_part_name:%s\n", output_part_name, intput_part_name); */ return 0; } int sunxi_partition_get_partno_byname(const char *part_name) { int i; struct blk_desc *desc; int ret; disk_partition_t info; char temp_part_name[16] = {0}; desc = blk_get_devnum_by_typename("sunxi_flash", 0); if (desc == NULL) { printf("%s: get desc fail\n", __func__); ret = -ENODEV; } sunxi_replace_android_ab_system((char *)part_name, temp_part_name); for (i = 1;; i++) { ret = part_get_info(desc, i, &info); debug("%s: try part %d, ret = %d\n", __func__, i, ret); if (ret < 0) { printf("partno erro : can't find partition %s\n", part_name); return ret; } if (!strncmp((const char *)info.name, temp_part_name, sizeof(info.name))) { return i; } else if (!strncmp((const char *)info.name, part_name, sizeof(info.name))) { return i; } } return -1; } int sunxi_partition_get_info_byname(const char *part_name, uint *part_offset, uint *part_size) { disk_partition_t info = { 0 }; if (sunxi_partition_get_info(part_name, &info) == 0) { *part_offset = info.start; *part_size = info.size; return 0; } return -1; } uint sunxi_partition_get_offset_byname(const char *part_name) { disk_partition_t info = { 0 }; if (sunxi_partition_get_info(part_name, &info) == 0) { return info.start; } return 0; } /* for blk read/write */ int sunxi_flash_try_partition(struct blk_desc *desc, const char *str, disk_partition_t *info) { int i, ret; char temp_part_name[16] = {0}; #if 0 char *gpt_signature[512] = {0}; printf("\n"); debug("line:%d:gpt_header_size %d\n", __LINE__, sizeof(gpt_header)); if (sunxi_flash_phyread(1, 1, (void *)gpt_signature) <= 0) { printf("line:%d:sunxi_flash_read fail\n", __LINE__); } if (strncmp((const char *)(gpt_signature), GPT_SIGNATURE, sizeof(GPT_SIGNATURE))) { debug("line:%d:get gpt partition fail\n", __LINE__); debug("line:%d:try to mbr partition\n", __LINE__); if (get_boot_storage_type() == STORAGE_SD) { u32 start, size; if (sunxi_partition_get_info_byname(str, &start, &size)) return -1; strcpy((char *)info->name, str); info->start = start; info->size = size; return 0; } } printf("line:%d:get gpt partition success\n", __LINE__); #endif sunxi_replace_android_ab_system((char *)str, temp_part_name); for (i = 1;; i++) { #if defined (CONFIG_ENABLE_MTD_CMDLINE_PARTS_BY_ENV) /*Get partitiones by env*/ ret = sunxi_partition_parse_get_info(i, info); #else /* Get partitiones by GPT */ ret = part_get_info(desc, i, info); debug("%s: try part %d, ret = %d\n", __func__, i, ret); #endif if (ret < 0) return ret; if (!strncmp((const char *)info->name, temp_part_name, sizeof(info->name))) break; else if (!strncmp((const char *)info->name, (char *)str, sizeof(info->name))) break; } return 0; } /* for sunxi_flash_read/write */ int sunxi_partition_get_info(const char *part_name, disk_partition_t *info) { struct blk_desc *desc; int ret; int storage_type; int logic_offset; storage_type = get_boot_storage_type(); if (get_boot_work_mode() == WORK_MODE_CARD_PRODUCT || storage_type == STORAGE_EMMC || storage_type == STORAGE_EMMC3 || storage_type == STORAGE_SD || storage_type == STORAGE_EMMC0) { logic_offset = sunxi_flashmap_logical_offset(FLASHMAP_SDMMC, LINUX_LOGIC_OFFSET); } else { logic_offset = 0; } desc = blk_get_devnum_by_typename("sunxi_flash", 0); if (desc == NULL) { debug("%s: get desc fail\n", __func__); ret = -ENODEV; goto __err; } ret = sunxi_flash_try_partition(desc, part_name, info); if (ret < 0) { debug("%s: get partition info fail\n", __func__); ret = -ENODEV; goto __err; } debug("name:%s start:0x%x, size: 0x%x\n", info->name, (u32)info->start, (u32)info->size); /* conver gpt info to sunxi_part */ /* gpt part use phy address */ /* sunxi part use logic address */ info->start -= logic_offset; return 0; __err: return ret; } /* for card sprite mode*/ lbaint_t sunxi_partition_get_offset(int part_index) { if (get_boot_work_mode() != WORK_MODE_CARD_PRODUCT) { printf("****not support*****\n"); return (lbaint_t)(-1); } #ifdef CONFIG_ENABLE_MTD_CMDLINE_PARTS_BY_ENV disk_partition_t info; int ret; ret = sunxi_partition_parse_get_info(part_index, &info); if (ret == 0) { debug(" mbr->array[%d].name=%s\n", part_index, info.name); debug(" mbr->array[%d].lenlo=0x%x\n", part_index, (u32)info.size); debug("mbr->array[%d].addrlo=0x%x\n", part_index, (u32)info.start); return (lbaint_t)info.start; } #else sunxi_partition_init(); if (mbr->PartCount && part_index <= mbr->PartCount) { debug(" mbr->array[%d].name=%s\n", part_index, mbr->array[part_index].name); debug(" mbr->array[%d].lenlo=%d\n", part_index, mbr->array[part_index].lenlo); debug("mbr->array[%d].addrlo=%d\n", part_index, mbr->array[part_index].addrlo); return (lbaint_t)mbr->array[part_index].addrlo; } #endif return (lbaint_t)(-1); }