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