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