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
memparse(const char * ptr,char ** retptr)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
env_init_parts(struct blk_desc * dev_desc,struct list_head * parts_head)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 parts_list = ENV_PARTITIONS;
86 #endif
87 if (!parts_list)
88 return -EINVAL;
89
90 next = strchr(parts_list, ':');
91 INIT_LIST_HEAD(parts_head);
92 while (next) {
93 /* Skip ':' and ',' */
94 next++;
95 if (*next == '-') {
96 size = (~0UL);
97 next++;
98 } else {
99 size = (lbaint_t)memparse(next, &next);
100 }
101 if (*next == '@') {
102 next++;
103 start = (lbaint_t)memparse(next, &next);
104 }
105 next++;
106 pend = strchr(next, ')');
107 if (!pend)
108 break;
109
110 len = min_t(int, pend - next, PART_NAME_LEN);
111 part = malloc(sizeof(*part));
112 if (!part)
113 break;
114 part->start = (start + offset) / dev_desc->blksz;
115 start += size;
116 /* Last partition use all remain space */
117 if (size == (~0UL))
118 part->size = dev_desc->lba - part->start;
119 else
120 part->size = size / dev_desc->blksz;
121 strncpy(part->name, next, len);
122 part->name[len] = '\0';
123 list_add_tail(&part->node, parts_head);
124 next = strchr(next, ',');
125 }
126
127 dev_num = DEV_NUM(dev_desc);
128
129 return 0;
130 }
131
part_print_env(struct blk_desc * dev_desc)132 void part_print_env(struct blk_desc *dev_desc)
133 {
134 struct list_head *node;
135 struct env_part *p;
136 int i = 0;
137 int ret;
138
139 if (list_empty(&parts_head) || (dev_num != DEV_NUM(dev_desc))) {
140 ret = env_init_parts(dev_desc, &parts_head);
141 if (ret) {
142 printf("%s: Invalid env parameter\n", __func__);
143 return;
144 }
145 }
146
147 printf("Part\tStart LBA\tSize\t\tName\n");
148 list_for_each(node, &parts_head) {
149 p = list_entry(node, struct env_part, node);
150 printf("%3d\t0x%08lx\t0x%08lx\t%s\n", (i++ + 1),
151 p->start, p->size, p->name);
152 }
153 }
154
part_get_info_env(struct blk_desc * dev_desc,int idx,disk_partition_t * info)155 static int part_get_info_env(struct blk_desc *dev_desc, int idx,
156 disk_partition_t *info)
157 {
158 struct env_part *p = NULL;
159 struct list_head *node;
160 int part_num = 1;
161 int ret = 0;
162
163 if (idx < 1) {
164 printf("%s: Invalid partition no.%d\n", __func__, idx);
165 return -EINVAL;
166 }
167
168 if (list_empty(&parts_head) || (dev_num != DEV_NUM(dev_desc))) {
169 ret = env_init_parts(dev_desc, &parts_head);
170 if (ret) {
171 printf("%s: Invalid env partition\n", __func__);
172 return -1;
173 }
174 }
175
176 list_for_each(node, &parts_head) {
177 p = list_entry(node, struct env_part, node);
178 if (idx == part_num)
179 break;
180 part_num++;
181 }
182
183 if (part_num < idx) {
184 debug("%s: Invalid partition no.%d\n", __func__, idx);
185 return -EINVAL;
186 }
187
188 info->start = p->start;
189 info->size = p->size;
190 info->blksz = dev_desc->blksz;
191 info->bootable = 0;
192
193 sprintf((char *)info->name, "%s", p->name);
194 strcpy((char *)info->type, "U-Boot");
195
196 return 0;
197 }
198
part_test_env(struct blk_desc * dev_desc)199 static int part_test_env(struct blk_desc *dev_desc)
200 {
201 return env_init_parts(dev_desc, &parts_head) ? -1 : 0;
202 }
203
204 /*
205 * Add an 'a_b_' prefix so it comes before 'a_efi'.
206 */
207 U_BOOT_PART_TYPE(a_b_env) = {
208 .name = "ENV",
209 .part_type = PART_TYPE_ENV,
210 .max_entries = ENV_ENTRY_NUMBERS,
211 .get_info = part_get_info_ptr(part_get_info_env),
212 .print = part_print_ptr(part_print_env),
213 .test = part_test_env,
214 };
215 #endif
216
217