cmd_mem.c: Fix warning when CONFIG_CMD_MEMTEST is not set
[kernel/u-boot.git] / common / cmd_gpt.c
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>
30 #include <div64.h>
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;
135         uint64_t size_ll, start_ll;
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;
222                 size_ll = ustrtoull(p, &p, 0);
223                 parts[i].size = lldiv(size_ll, dev_desc->blksz);
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;
231                         start_ll = ustrtoull(p, &p, 0);
232                         parts[i].start = lldiv(start_ll, dev_desc->blksz);
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 );