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