]>
Commit | Line | Data |
---|---|---|
8b096237 PW |
1 | /* |
2 | * cmd_gpt.c -- GPT (GUID Partition Table) handling command | |
3 | * | |
4 | * Copyright (C) 2012 Samsung Electronics | |
5 | * author: Lukasz Majewski <l.majewski@samsung.com> | |
6 | * author: Piotr Wilczek <p.wilczek@samsung.com> | |
7 | * | |
1a459660 | 8 | * SPDX-License-Identifier: GPL-2.0+ |
8b096237 PW |
9 | */ |
10 | ||
11 | #include <common.h> | |
12 | #include <malloc.h> | |
13 | #include <command.h> | |
14 | #include <mmc.h> | |
15 | #include <part_efi.h> | |
16 | #include <exports.h> | |
17 | #include <linux/ctype.h> | |
3e34cf7b | 18 | #include <div64.h> |
8b096237 PW |
19 | |
20 | #ifndef CONFIG_PARTITION_UUIDS | |
21 | #error CONFIG_PARTITION_UUIDS must be enabled for CONFIG_CMD_GPT to be enabled | |
22 | #endif | |
23 | ||
24 | /** | |
25 | * extract_env(): Expand env name from string format '&{env_name}' | |
26 | * and return pointer to the env (if the env is set) | |
27 | * | |
28 | * @param str - pointer to string | |
29 | * @param env - pointer to pointer to extracted env | |
30 | * | |
31 | * @return - zero on successful expand and env is set | |
32 | */ | |
33 | static char extract_env(const char *str, char **env) | |
34 | { | |
35 | char *e, *s; | |
36 | ||
37 | if (!str || strlen(str) < 4) | |
38 | return -1; | |
39 | ||
40 | if ((strncmp(str, "${", 2) == 0) && (str[strlen(str) - 1] == '}')) { | |
41 | s = strdup(str); | |
42 | if (s == NULL) | |
43 | return -1; | |
44 | memset(s + strlen(s) - 1, '\0', 1); | |
45 | memmove(s, s + 2, strlen(s) - 1); | |
46 | e = getenv(s); | |
47 | free(s); | |
48 | if (e == NULL) { | |
49 | printf("Environmental '%s' not set\n", str); | |
50 | return -1; /* env not set */ | |
51 | } | |
52 | *env = e; | |
53 | return 0; | |
54 | } | |
55 | ||
56 | return -1; | |
57 | } | |
58 | ||
59 | /** | |
60 | * extract_val(): Extract value from a key=value pair list (comma separated). | |
61 | * Only value for the given key is returend. | |
62 | * Function allocates memory for the value, remember to free! | |
63 | * | |
64 | * @param str - pointer to string with key=values pairs | |
65 | * @param key - pointer to the key to search for | |
66 | * | |
67 | * @return - pointer to allocated string with the value | |
68 | */ | |
69 | static char *extract_val(const char *str, const char *key) | |
70 | { | |
71 | char *v, *k; | |
72 | char *s, *strcopy; | |
73 | char *new = NULL; | |
74 | ||
75 | strcopy = strdup(str); | |
76 | if (strcopy == NULL) | |
77 | return NULL; | |
78 | ||
79 | s = strcopy; | |
80 | while (s) { | |
81 | v = strsep(&s, ","); | |
82 | if (!v) | |
83 | break; | |
84 | k = strsep(&v, "="); | |
85 | if (!k) | |
86 | break; | |
87 | if (strcmp(k, key) == 0) { | |
88 | new = strdup(v); | |
89 | break; | |
90 | } | |
91 | } | |
92 | ||
93 | free(strcopy); | |
94 | ||
95 | return new; | |
96 | } | |
97 | ||
98 | /** | |
99 | * set_gpt_info(): Fill partition information from string | |
100 | * function allocates memory, remember to free! | |
101 | * | |
102 | * @param dev_desc - pointer block device descriptor | |
103 | * @param str_part - pointer to string with partition information | |
104 | * @param str_disk_guid - pointer to pointer to allocated string with disk guid | |
105 | * @param partitions - pointer to pointer to allocated partitions array | |
106 | * @param parts_count - number of partitions | |
107 | * | |
108 | * @return - zero on success, otherwise error | |
109 | * | |
110 | */ | |
111 | static int set_gpt_info(block_dev_desc_t *dev_desc, | |
112 | const char *str_part, | |
113 | char **str_disk_guid, | |
114 | disk_partition_t **partitions, | |
115 | u8 *parts_count) | |
116 | { | |
117 | char *tok, *str, *s; | |
118 | int i; | |
119 | char *val, *p; | |
120 | int p_count; | |
121 | disk_partition_t *parts; | |
122 | int errno = 0; | |
3e34cf7b | 123 | uint64_t size_ll, start_ll; |
8b096237 PW |
124 | |
125 | debug("%s: MMC lba num: 0x%x %d\n", __func__, | |
126 | (unsigned int)dev_desc->lba, (unsigned int)dev_desc->lba); | |
127 | ||
128 | if (str_part == NULL) | |
129 | return -1; | |
130 | ||
131 | str = strdup(str_part); | |
132 | ||
133 | /* extract disk guid */ | |
134 | s = str; | |
135 | tok = strsep(&s, ";"); | |
136 | val = extract_val(tok, "uuid_disk"); | |
137 | if (!val) { | |
138 | free(str); | |
139 | return -2; | |
140 | } | |
141 | if (extract_env(val, &p)) | |
142 | p = val; | |
143 | *str_disk_guid = strdup(p); | |
144 | free(val); | |
145 | ||
146 | if (strlen(s) == 0) | |
147 | return -3; | |
148 | ||
149 | i = strlen(s) - 1; | |
150 | if (s[i] == ';') | |
151 | s[i] = '\0'; | |
152 | ||
153 | /* calculate expected number of partitions */ | |
154 | p_count = 1; | |
155 | p = s; | |
156 | while (*p) { | |
157 | if (*p++ == ';') | |
158 | p_count++; | |
159 | } | |
160 | ||
161 | /* allocate memory for partitions */ | |
162 | parts = calloc(sizeof(disk_partition_t), p_count); | |
163 | ||
164 | /* retrive partions data from string */ | |
165 | for (i = 0; i < p_count; i++) { | |
166 | tok = strsep(&s, ";"); | |
167 | ||
168 | if (tok == NULL) | |
169 | break; | |
170 | ||
171 | /* uuid */ | |
172 | val = extract_val(tok, "uuid"); | |
173 | if (!val) { /* 'uuid' is mandatory */ | |
174 | errno = -4; | |
175 | goto err; | |
176 | } | |
177 | if (extract_env(val, &p)) | |
178 | p = val; | |
179 | if (strlen(p) >= sizeof(parts[i].uuid)) { | |
180 | printf("Wrong uuid format for partition %d\n", i); | |
181 | errno = -4; | |
182 | goto err; | |
183 | } | |
184 | strcpy((char *)parts[i].uuid, p); | |
185 | free(val); | |
186 | ||
187 | /* name */ | |
188 | val = extract_val(tok, "name"); | |
189 | if (!val) { /* name is mandatory */ | |
190 | errno = -4; | |
191 | goto err; | |
192 | } | |
193 | if (extract_env(val, &p)) | |
194 | p = val; | |
195 | if (strlen(p) >= sizeof(parts[i].name)) { | |
196 | errno = -4; | |
197 | goto err; | |
198 | } | |
199 | strcpy((char *)parts[i].name, p); | |
200 | free(val); | |
201 | ||
202 | /* size */ | |
203 | val = extract_val(tok, "size"); | |
204 | if (!val) { /* 'size' is mandatory */ | |
205 | errno = -4; | |
206 | goto err; | |
207 | } | |
208 | if (extract_env(val, &p)) | |
209 | p = val; | |
3e34cf7b PW |
210 | size_ll = ustrtoull(p, &p, 0); |
211 | parts[i].size = lldiv(size_ll, dev_desc->blksz); | |
8b096237 PW |
212 | free(val); |
213 | ||
214 | /* start address */ | |
215 | val = extract_val(tok, "start"); | |
216 | if (val) { /* start address is optional */ | |
217 | if (extract_env(val, &p)) | |
218 | p = val; | |
3e34cf7b PW |
219 | start_ll = ustrtoull(p, &p, 0); |
220 | parts[i].start = lldiv(start_ll, dev_desc->blksz); | |
8b096237 PW |
221 | free(val); |
222 | } | |
223 | } | |
224 | ||
225 | *parts_count = p_count; | |
226 | *partitions = parts; | |
227 | free(str); | |
228 | ||
229 | return 0; | |
230 | err: | |
231 | free(str); | |
232 | free(*str_disk_guid); | |
233 | free(parts); | |
234 | ||
235 | return errno; | |
236 | } | |
237 | ||
238 | static int gpt_mmc_default(int dev, const char *str_part) | |
239 | { | |
240 | int ret; | |
241 | char *str_disk_guid; | |
242 | u8 part_count = 0; | |
243 | disk_partition_t *partitions = NULL; | |
244 | ||
245 | struct mmc *mmc = find_mmc_device(dev); | |
246 | ||
247 | if (mmc == NULL) { | |
248 | printf("%s: mmc dev %d NOT available\n", __func__, dev); | |
249 | return CMD_RET_FAILURE; | |
250 | } | |
251 | ||
252 | if (!str_part) | |
253 | return -1; | |
254 | ||
255 | /* fill partitions */ | |
256 | ret = set_gpt_info(&mmc->block_dev, str_part, | |
257 | &str_disk_guid, &partitions, &part_count); | |
258 | if (ret) { | |
259 | if (ret == -1) | |
260 | printf("No partition list provided\n"); | |
261 | if (ret == -2) | |
262 | printf("Missing disk guid\n"); | |
263 | if ((ret == -3) || (ret == -4)) | |
264 | printf("Partition list incomplete\n"); | |
265 | return -1; | |
266 | } | |
267 | ||
268 | /* save partitions layout to disk */ | |
269 | gpt_restore(&mmc->block_dev, str_disk_guid, partitions, part_count); | |
270 | free(str_disk_guid); | |
271 | free(partitions); | |
272 | ||
273 | return 0; | |
274 | } | |
275 | ||
276 | /** | |
277 | * do_gpt(): Perform GPT operations | |
278 | * | |
279 | * @param cmdtp - command name | |
280 | * @param flag | |
281 | * @param argc | |
282 | * @param argv | |
283 | * | |
284 | * @return zero on success; otherwise error | |
285 | */ | |
286 | static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | |
287 | { | |
288 | int ret = CMD_RET_SUCCESS; | |
289 | int dev = 0; | |
290 | char *pstr; | |
291 | ||
292 | if (argc < 5) | |
293 | return CMD_RET_USAGE; | |
294 | ||
295 | /* command: 'write' */ | |
296 | if ((strcmp(argv[1], "write") == 0) && (argc == 5)) { | |
297 | /* device: 'mmc' */ | |
298 | if (strcmp(argv[2], "mmc") == 0) { | |
299 | /* check if 'dev' is a number */ | |
300 | for (pstr = argv[3]; *pstr != '\0'; pstr++) | |
301 | if (!isdigit(*pstr)) { | |
302 | printf("'%s' is not a number\n", | |
303 | argv[3]); | |
304 | return CMD_RET_USAGE; | |
305 | } | |
306 | dev = (int)simple_strtoul(argv[3], NULL, 10); | |
307 | /* write to mmc */ | |
308 | if (gpt_mmc_default(dev, argv[4])) | |
309 | return CMD_RET_FAILURE; | |
310 | } | |
311 | } else { | |
312 | return CMD_RET_USAGE; | |
313 | } | |
314 | return ret; | |
315 | } | |
316 | ||
317 | U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, | |
318 | "GUID Partition Table", | |
319 | "<command> <interface> <dev> <partions_list>\n" | |
320 | " - GUID partition table restoration\n" | |
321 | " Restore GPT information on a device connected\n" | |
322 | " to interface\n" | |
323 | ); |