blob: 1f12e6deb63f0d34bf5083a1df744ae55d74bcbd [file] [log] [blame]
Piotr Wilczek8b096232012-12-11 11:09:47 +01001/*
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 *
Wolfgang Denk1a459662013-07-08 09:37:19 +02008 * SPDX-License-Identifier: GPL-2.0+
Piotr Wilczek8b096232012-12-11 11:09:47 +01009 */
10
11#include <common.h>
12#include <malloc.h>
13#include <command.h>
Piotr Wilczek8b096232012-12-11 11:09:47 +010014#include <part_efi.h>
15#include <exports.h>
16#include <linux/ctype.h>
Piotr Wilczek3e34cf72013-01-27 22:59:25 +000017#include <div64.h>
Piotr Wilczek8b096232012-12-11 11:09:47 +010018
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 */
32static 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 */
68static 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 */
110static 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;
Piotr Wilczek3e34cf72013-01-27 22:59:25 +0000122 uint64_t size_ll, start_ll;
Piotr Wilczek8b096232012-12-11 11:09:47 +0100123
Egbert Eich619f0fd2013-10-04 18:53:04 +0200124 debug("%s: lba num: 0x%x %d\n", __func__,
Piotr Wilczek8b096232012-12-11 11:09:47 +0100125 (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
Robert P. J. Day1f8b5462013-08-21 11:39:19 -0400163 /* retrieve partitions data from string */
Piotr Wilczek8b096232012-12-11 11:09:47 +0100164 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;
Piotr Wilczek3e34cf72013-01-27 22:59:25 +0000209 size_ll = ustrtoull(p, &p, 0);
210 parts[i].size = lldiv(size_ll, dev_desc->blksz);
Piotr Wilczek8b096232012-12-11 11:09:47 +0100211 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;
Piotr Wilczek3e34cf72013-01-27 22:59:25 +0000218 start_ll = ustrtoull(p, &p, 0);
219 parts[i].start = lldiv(start_ll, dev_desc->blksz);
Piotr Wilczek8b096232012-12-11 11:09:47 +0100220 free(val);
221 }
222 }
223
224 *parts_count = p_count;
225 *partitions = parts;
226 free(str);
227
228 return 0;
229err:
230 free(str);
231 free(*str_disk_guid);
232 free(parts);
233
234 return errno;
235}
236
Egbert Eich619f0fd2013-10-04 18:53:04 +0200237static int gpt_default(block_dev_desc_t *blk_dev_desc, const char *str_part)
Piotr Wilczek8b096232012-12-11 11:09:47 +0100238{
239 int ret;
240 char *str_disk_guid;
241 u8 part_count = 0;
242 disk_partition_t *partitions = NULL;
243
Piotr Wilczek8b096232012-12-11 11:09:47 +0100244 if (!str_part)
245 return -1;
246
247 /* fill partitions */
Egbert Eich619f0fd2013-10-04 18:53:04 +0200248 ret = set_gpt_info(blk_dev_desc, str_part,
Piotr Wilczek8b096232012-12-11 11:09:47 +0100249 &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 */
Egbert Eich619f0fd2013-10-04 18:53:04 +0200261 gpt_restore(blk_dev_desc, str_disk_guid, partitions, part_count);
Piotr Wilczek8b096232012-12-11 11:09:47 +0100262 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 */
278static 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;
Egbert Eich619f0fd2013-10-04 18:53:04 +0200282 char *ep;
283 block_dev_desc_t *blk_dev_desc;
Piotr Wilczek8b096232012-12-11 11:09:47 +0100284
285 if (argc < 5)
286 return CMD_RET_USAGE;
287
288 /* command: 'write' */
289 if ((strcmp(argv[1], "write") == 0) && (argc == 5)) {
Egbert Eich619f0fd2013-10-04 18:53:04 +0200290 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;
Piotr Wilczek8b096232012-12-11 11:09:47 +0100294 }
Egbert Eich619f0fd2013-10-04 18:53:04 +0200295 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;
Piotr Wilczek8b096232012-12-11 11:09:47 +0100304 } else {
305 return CMD_RET_USAGE;
306 }
307 return ret;
308}
309
310U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt,
311 "GUID Partition Table",
Robert P. J. Day1f8b5462013-08-21 11:39:19 -0400312 "<command> <interface> <dev> <partitions_list>\n"
Piotr Wilczek8b096232012-12-11 11:09:47 +0100313 " - GUID partition table restoration\n"
314 " Restore GPT information on a device connected\n"
315 " to interface\n"
316);