xref: /rk3399_rockchip-uboot/disk/part_env.c (revision 7a38549f8980099ed3f77d30d1ba3ee692380280)
1 /*
2  * (C) Copyright 2022 Rockchip 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 struct env_part {
12 	char name[PART_NAME_LEN];
13 	lbaint_t start;
14 	lbaint_t size;
15 	struct list_head node;
16 };
17 
18 #define DEV_NUM(dev_desc)  (((dev_desc)->if_type << 8) + (dev_desc)->devnum)
19 
20 /*
21  * What's this?
22  *
23  * There maybe different storage media will use this partition driver,
24  * e.g. rkand with SD card. So we need a flag info to recognize it
25  * and rebuild partition table.
26  */
27 static int dev_num = -1;
28 static LIST_HEAD(parts_head);
29 
30 #ifdef CONFIG_SPL_BUILD
31 static char *env_parts_list;
32 
33 void env_part_list_init(const char *list)
34 {
35 	env_parts_list = (char *)list;
36 }
37 #endif
38 
39 static unsigned long long memparse(const char *ptr, char **retptr)
40 {
41 	char *endptr;	/* local pointer to end of parsed string */
42 	unsigned long long ret = simple_strtoull(ptr, &endptr, 0);
43 
44 	switch (*endptr) {
45 	case 'E':
46 	case 'e':
47 		ret <<= 10;
48 		/* fall through */
49 	case 'P':
50 	case 'p':
51 		ret <<= 10;
52 		/* fall through */
53 	case 'T':
54 	case 't':
55 		ret <<= 10;
56 		/* fall through */
57 	case 'G':
58 	case 'g':
59 		ret <<= 10;
60 		/* fall through */
61 	case 'M':
62 	case 'm':
63 		ret <<= 10;
64 		/* fall through */
65 	case 'K':
66 	case 'k':
67 		ret <<= 10;
68 		endptr++;
69 	default:
70 		break;
71 	}
72 
73 	if (retptr)
74 		*retptr = endptr;
75 
76 	return ret;
77 }
78 
79 static int env_init_parts(struct blk_desc *dev_desc, struct list_head *parts_head)
80 {
81 	struct env_part *part;
82 	lbaint_t size, start = 0;
83 	int len, offset = 0;
84 	char *next, *pend;
85 	char *parts_list = NULL;
86 #ifndef CONFIG_SPL_BUILD
87 	const char *parts_prefix[] = { "mtdparts", "blkdevparts", };
88 	int i;
89 
90 	for (i = 0; i < ARRAY_SIZE(parts_prefix); i++) {
91 		parts_list = env_get(parts_prefix[i]);
92 		if (parts_list)
93 			break;
94 	}
95 #else
96 	parts_list = env_parts_list;
97 #endif
98 	if (!parts_list)
99 		return -EINVAL;
100 
101 	next = strchr(parts_list, ':');
102 	INIT_LIST_HEAD(parts_head);
103 	while (next) {
104 		/* Skip ':' and ',' */
105 		next++;
106 		if (*next == '-') {
107 			size = (~0UL);
108 			next++;
109 		} else {
110 			size = (lbaint_t)memparse(next, &next);
111 		}
112 		if (*next == '@') {
113 			next++;
114 			start = (lbaint_t)memparse(next, &next);
115 		}
116 		next++;
117 		pend = strchr(next, ')');
118 		if (!pend)
119 			break;
120 
121 		len = min_t(int, pend - next, PART_NAME_LEN);
122 		part = malloc(sizeof(*part));
123 		if (!part)
124 			break;
125 		part->start = (start + offset) / dev_desc->blksz;
126 		start += size;
127 		/* Last partition use all remain space */
128 		if (size == (~0UL))
129 			part->size = dev_desc->lba - part->start;
130 		else
131 			part->size = size / dev_desc->blksz;
132 		strncpy(part->name, next, len);
133 		part->name[len] = '\0';
134 		list_add_tail(&part->node, parts_head);
135 		next = strchr(next, ',');
136 	}
137 
138 	dev_num = DEV_NUM(dev_desc);
139 
140 	/* Add into "bootargs" */
141 #ifndef CONFIG_SPL_BUILD
142 	char *parts_cmdline;
143 
144 	parts_cmdline =
145 		calloc(1, strlen(parts_list) + strlen(parts_prefix[i]) + 2);
146 	if (!parts_cmdline)
147 		return -ENOMEM;
148 	strcat(parts_cmdline, parts_prefix[i]);
149 	strcat(parts_cmdline, "=");
150 	strcat(parts_cmdline, parts_list);
151 	env_update("bootargs", parts_cmdline);
152 	free(parts_cmdline);
153 #endif
154 
155 	printf("PartType: ENV\n");
156 
157 	return 0;
158 }
159 
160 void part_print_env(struct blk_desc *dev_desc)
161 {
162 	struct list_head *node;
163 	struct env_part *p;
164 	int i = 0;
165 	int ret;
166 
167 	if (list_empty(&parts_head) || (dev_num != DEV_NUM(dev_desc))) {
168 		ret = env_init_parts(dev_desc, &parts_head);
169 		if (ret) {
170 			printf("%s: Invalid env parameter\n", __func__);
171 			return;
172 		}
173 	}
174 
175 	printf("Part\tStart LBA\tSize\t\tName\n");
176 	list_for_each(node, &parts_head) {
177 		p = list_entry(node, struct env_part, node);
178 		printf("%3d\t0x%08lx\t0x%08lx\t%s\n", (i++ + 1),
179 		       p->start, p->size, p->name);
180 	}
181 }
182 
183 static int part_get_info_env(struct blk_desc *dev_desc, int idx,
184 			     disk_partition_t *info)
185 {
186 	struct env_part *p = NULL;
187 	struct list_head *node;
188 	int part_num = 1;
189 	int ret = 0;
190 
191 	if (idx < 1) {
192 		printf("%s: Invalid partition no.%d\n", __func__, idx);
193 		return -EINVAL;
194 	}
195 
196 	if (list_empty(&parts_head) || (dev_num != DEV_NUM(dev_desc))) {
197 		ret = env_init_parts(dev_desc, &parts_head);
198 		if (ret) {
199 			printf("%s: Invalid env partition\n", __func__);
200 			return -1;
201 		}
202 	}
203 
204 	list_for_each(node, &parts_head) {
205 		p = list_entry(node, struct env_part, node);
206 		if (idx == part_num)
207 			break;
208 		part_num++;
209 	}
210 
211 	if (part_num < idx) {
212 		debug("%s: Invalid partition no.%d\n", __func__, idx);
213 		return -EINVAL;
214 	}
215 
216 	info->start = p->start;
217 	info->size  = p->size;
218 	info->blksz = dev_desc->blksz;
219 	info->bootable = 0;
220 
221 	sprintf((char *)info->name, "%s", p->name);
222 	strcpy((char *)info->type, "U-Boot");
223 
224 	return 0;
225 }
226 
227 static int part_test_env(struct blk_desc *dev_desc)
228 {
229 	return env_init_parts(dev_desc, &parts_head) ? -1 : 0;
230 }
231 
232 /*
233  * Add an 'a_b_' prefix so it comes before 'a_efi'.
234  */
235 U_BOOT_PART_TYPE(a_b_env) = {
236 	.name		= "ENV",
237 	.part_type	= PART_TYPE_ENV,
238 	.max_entries	= ENV_ENTRY_NUMBERS,
239 	.get_info	= part_get_info_ptr(part_get_info_env),
240 	.print		= part_print_ptr(part_print_env),
241 	.test		= part_test_env,
242 };
243 #endif
244 
245