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