1 /* 2 * (C) Copyright 2017 rkparm Electronics Co., Ltd 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <malloc.h> 9 10 #ifdef HAVE_BLOCK_DEVICE 11 #define RK_PARAM_OFFSET 0x2000 12 #define MAX_PARAM_SIZE (1024 * 64) 13 14 struct rkparm_param { 15 u32 tag; 16 u32 length; 17 char params[1]; 18 u32 crc; 19 }; 20 21 struct rkparm_part { 22 char name[PART_NAME_LEN]; 23 unsigned long start; 24 unsigned long size; 25 struct list_head node; 26 }; 27 28 29 static LIST_HEAD(parts_head); 30 static int dev_num = -1; 31 32 static int rkparm_param_parse(char *param, struct list_head *parts_head, 33 struct blk_desc *dev_desc) 34 { 35 struct rkparm_part *part; 36 const char *cmdline = strstr(param, "CMDLINE:"); 37 const char *blkdev_parts; 38 char *cmdline_end, *next, *pend; 39 int len, offset = 0; 40 unsigned long size, start; 41 42 if (!cmdline) { 43 printf("invalid parameter\n"); 44 return -EINVAL; 45 } 46 47 blkdev_parts = strstr(cmdline, "mtdparts"); 48 next = strchr(blkdev_parts, ':'); 49 cmdline_end = strstr(cmdline, "\n"); /* end by '\n' */ 50 *cmdline_end = '\0'; 51 /* 52 * 1. skip "CMDLINE:" 53 * 2. Initrd fixup: remove unused "initrd=0x...,0x...", this for 54 * compatible with legacy parameter.txt 55 */ 56 env_update_filter("bootargs", cmdline + strlen("CMDLINE:"), "initrd="); 57 58 INIT_LIST_HEAD(parts_head); 59 while (next) { 60 /* Skip ':' and ',' */ 61 next++; 62 if (*next == '-') { 63 size = (~0UL); 64 next++; 65 } else { 66 size = simple_strtoul(next, &next, 16); 67 } 68 /* Skip '@' */ 69 next++; 70 start = simple_strtoul(next, &next, 16); 71 next++; 72 pend = strchr(next, ')'); 73 if (!pend) 74 break; 75 len = min_t(int, pend - next, PART_NAME_LEN); 76 part = malloc(sizeof(*part)); 77 if (!part) { 78 printf("out of memory\n"); 79 break; 80 } 81 if (dev_desc->if_type != IF_TYPE_RKNAND) 82 offset = RK_PARAM_OFFSET; 83 part->start = start + offset; 84 /* Last partition use all remain space */ 85 if (size == (~0UL)) 86 size = dev_desc->lba - part->start; 87 part->size = size; 88 strncpy(part->name, next, len); 89 part->name[len] = '\0'; 90 list_add_tail(&part->node, parts_head); 91 next = strchr(next, ','); 92 } 93 94 dev_num = ((dev_desc->if_type << 8) + dev_desc->devnum); 95 96 return 0; 97 } 98 99 static int rkparm_init_param(struct blk_desc *dev_desc, 100 struct list_head *parts_head) 101 { 102 struct rkparm_param *param; 103 int offset = 0; 104 int ret; 105 106 param = memalign(ARCH_DMA_MINALIGN, MAX_PARAM_SIZE); 107 if (!param) { 108 printf("out of memory\n"); 109 return -ENOMEM; 110 } 111 112 if (dev_desc->if_type != IF_TYPE_RKNAND) 113 offset = RK_PARAM_OFFSET; 114 115 ret = blk_dread(dev_desc, offset, MAX_PARAM_SIZE >> 9, (ulong *)param); 116 if (ret != (MAX_PARAM_SIZE >> 9)) { 117 printf("%s param read fail\n", __func__); 118 return -EINVAL; 119 } 120 121 return rkparm_param_parse(param->params, parts_head, dev_desc); 122 123 } 124 125 static void part_print_rkparm(struct blk_desc *dev_desc) 126 { 127 int ret = 0; 128 struct list_head *node; 129 struct rkparm_part *p = NULL; 130 int i = 0; 131 132 if (list_empty(&parts_head) || 133 (dev_num != ((dev_desc->if_type << 8) + dev_desc->devnum))) 134 ret = rkparm_init_param(dev_desc, &parts_head); 135 136 if (ret) { 137 printf("%s Invalid rkparm parameter\n", __func__); 138 return; 139 } 140 141 printf("Part\tStart LBA\tSize\t\tName\n"); 142 list_for_each(node, &parts_head) { 143 p = list_entry(node, struct rkparm_part, node); 144 printf("%3d\t0x%08lx\t0x%08lx\t%s\n", (i++ + 1), 145 p->start, p->size, p->name); 146 } 147 148 149 return; 150 } 151 152 static int part_get_info_rkparm(struct blk_desc *dev_desc, int idx, 153 disk_partition_t *info) 154 { 155 struct list_head *node; 156 struct rkparm_part *p = NULL; 157 int part_num = 1; 158 int ret = 0; 159 160 if (idx < 1) { 161 printf("%s Invalid partition no.%d\n", __func__, idx); 162 return -EINVAL; 163 } 164 165 if (list_empty(&parts_head) || 166 (dev_num != ((dev_desc->if_type << 8) + dev_desc->devnum))) 167 ret = rkparm_init_param(dev_desc, &parts_head); 168 169 if (ret) { 170 printf("%s Invalid rkparm partition\n", __func__); 171 return -1; 172 } 173 174 list_for_each(node, &parts_head) { 175 p = list_entry(node, struct rkparm_part, node); 176 if (idx == part_num) 177 break; 178 part_num ++; 179 } 180 181 if (part_num > idx) { 182 printf("%s Invalid partition no.%d\n", __func__, idx); 183 return -EINVAL; 184 } 185 186 info->start = p->start; 187 info->size = p->size << 9; 188 info->blksz = dev_desc->blksz; 189 190 sprintf((char *)info->name, "%s", p->name); 191 strcpy((char *)info->type, "U-Boot"); 192 info->bootable = 0; 193 194 return 0; 195 } 196 197 static int part_test_rkparm(struct blk_desc *dev_desc) 198 { 199 int ret = 0; 200 201 if (list_empty(&parts_head) || 202 (dev_num != ((dev_desc->if_type << 8) + dev_desc->devnum))) 203 ret = rkparm_init_param(dev_desc, &parts_head); 204 if (ret) 205 ret = -1; 206 207 return ret; 208 } 209 /* 210 * Add an 'b_' prefix so it comes before 'dos' and after 'a_efi' in the linker 211 * list. We need to check EFI first, and then rkparm partition 212 */ 213 U_BOOT_PART_TYPE(b_rkparm) = { 214 .name = "RKPARM", 215 .part_type = PART_TYPE_RKPARM, 216 .max_entries = RKPARM_ENTRY_NUMBERS, 217 .get_info = part_get_info_ptr(part_get_info_rkparm), 218 .print = part_print_ptr(part_print_rkparm), 219 .test = part_test_rkparm, 220 }; 221 #endif 222