1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * cmd_mbr.c -- MBR (Master Boot Record) handling command
4 *
5 * Copyright (C) 2020 Samsung Electronics
6 * author: Marek Szyprowski <m.szyprowski@samsung.com>
7 *
8 * based on the gpt command.
9 */
10
11 #include <common.h>
12 #include <blk.h>
13 #include <command.h>
14 #include <malloc.h>
15 #include <part.h>
16
17 /**
18 * extract_val() - Extract a value from the key=value pair list
19 * @str: pointer to string with key=values pairs
20 * @key: pointer to the key to search for
21 *
22 * The list of parameters is come separated, only a value for
23 * the given key is returend.
24 *
25 * Function allocates memory for the value, remember to free!
26 *
27 * Return: Pointer to allocated string with the value.
28 */
extract_val(const char * str,const char * key)29 static char *extract_val(const char *str, const char *key)
30 {
31 char *v, *k;
32 char *s, *strcopy;
33 char *new = NULL;
34
35 strcopy = strdup(str);
36 if (strcopy == NULL)
37 return NULL;
38
39 s = strcopy;
40 while (s) {
41 v = strsep(&s, ",");
42 if (!v)
43 break;
44 k = strsep(&v, "=");
45 if (!k)
46 break;
47 if (strcmp(k, key) == 0) {
48 new = strdup(v);
49 break;
50 }
51 }
52
53 free(strcopy);
54
55 return new;
56 }
57
58 /**
59 * found_key() - Search for a key without a value in the parameter list
60 * @str: pointer to string with key
61 * @key: pointer to the key to search for
62 *
63 * The list of parameters is come separated.
64 *
65 * Return: True if key has been found.
66 */
found_key(const char * str,const char * key)67 static bool found_key(const char *str, const char *key)
68 {
69 char *k;
70 char *s, *strcopy;
71 bool result = false;
72
73 strcopy = strdup(str);
74 if (!strcopy)
75 return NULL;
76
77 s = strcopy;
78 while (s) {
79 k = strsep(&s, ",");
80 if (!k)
81 break;
82 if (strcmp(k, key) == 0) {
83 result = true;
84 break;
85 }
86 }
87
88 free(strcopy);
89
90 return result;
91 }
92
str_to_partitions(const char * str_part,int blksz,unsigned long * disk_uuid,struct disk_partition ** partitions,int * parts_count)93 static int str_to_partitions(const char *str_part, int blksz,
94 unsigned long *disk_uuid, struct disk_partition **partitions,
95 int *parts_count)
96 {
97 char *tok, *str, *s;
98 int i;
99 char *val, *p;
100 int p_count;
101 struct disk_partition *parts;
102 int errno = 0;
103 uint64_t size_ll, start_ll;
104
105 if (str_part == NULL)
106 return -1;
107
108 str = strdup(str_part);
109 if (str == NULL)
110 return -ENOMEM;
111
112 /* extract disk guid */
113 s = str;
114 val = extract_val(str, "uuid_disk");
115 if (val) {
116 val = strsep(&val, ";");
117 p = val;
118 *disk_uuid = ustrtoull(p, &p, 0);
119 free(val);
120 /* Move s to first partition */
121 strsep(&s, ";");
122 }
123 if (s == NULL) {
124 printf("Error: is the partitions string NULL-terminated?\n");
125 return -EINVAL;
126 }
127
128 /* remove the optional semicolon at the end of the string */
129 i = strlen(s) - 1;
130 if (s[i] == ';')
131 s[i] = '\0';
132
133 /* calculate expected number of partitions */
134 p_count = 1;
135 p = s;
136 while (*p) {
137 if (*p++ == ';')
138 p_count++;
139 }
140
141 /* allocate memory for partitions */
142 parts = calloc(sizeof(struct disk_partition), p_count);
143 if (parts == NULL)
144 return -ENOMEM;
145
146 /* retrieve partitions data from string */
147 for (i = 0; i < p_count; i++) {
148 tok = strsep(&s, ";");
149
150 if (tok == NULL)
151 break;
152
153 /* size */
154 val = extract_val(tok, "size");
155 if (!val) { /* 'size' is mandatory */
156 errno = -4;
157 goto err;
158 }
159 p = val;
160 if ((strcmp(p, "-") == 0)) {
161 /* auto extend the size */
162 parts[i].size = 0;
163 } else {
164 size_ll = ustrtoull(p, &p, 0);
165 parts[i].size = size_ll / blksz;
166 }
167 free(val);
168
169 /* start address */
170 val = extract_val(tok, "start");
171 if (val) { /* start address is optional */
172 p = val;
173 start_ll = ustrtoull(p, &p, 0);
174 parts[i].start = start_ll / blksz;
175 free(val);
176 }
177
178 /* system id */
179 val = extract_val(tok, "id");
180 if (!val) { /* '' is mandatory */
181 errno = -4;
182 goto err;
183 }
184 p = val;
185 parts[i].sys_ind = ustrtoul(p, &p, 0);
186 free(val);
187
188 /* bootable */
189 if (found_key(tok, "bootable"))
190 parts[i].bootable = PART_BOOTABLE;
191 }
192
193 *parts_count = p_count;
194 *partitions = parts;
195 free(str);
196
197 return 0;
198 err:
199 free(str);
200 free(parts);
201
202 return errno;
203 }
204
do_write_mbr(struct blk_desc * dev,const char * str)205 static int do_write_mbr(struct blk_desc *dev, const char *str)
206 {
207 unsigned long disk_uuid = 0;
208 struct disk_partition *partitions;
209 int blksz = dev->blksz;
210 int count;
211
212 if (str_to_partitions(str, blksz, &disk_uuid, &partitions, &count)) {
213 printf("MBR: failed to setup partitions from \"%s\"\n", str);
214 return -1;
215 }
216
217 if (layout_mbr_partitions(partitions, count, dev->lba)) {
218 printf("MBR: failed to layout partitions on the device\n");
219 free(partitions);
220 return -1;
221 }
222
223 if (write_mbr_partitions(dev, partitions, count, disk_uuid)) {
224 printf("MBR: failed to write partitions to the device\n");
225 free(partitions);
226 return -1;
227 }
228
229 return 0;
230 }
231
do_verify_mbr(struct blk_desc * dev,const char * str)232 static int do_verify_mbr(struct blk_desc *dev, const char *str)
233 {
234 unsigned long disk_uuid = 0;
235 struct disk_partition *partitions;
236 int blksz = dev->blksz;
237 int count, i, ret = 1;
238
239 if (str_to_partitions(str, blksz, &disk_uuid, &partitions, &count)) {
240 printf("MBR: failed to setup partitions from \"%s\"\n", str);
241 return -1;
242 }
243
244 for (i = 0; i < count; i++) {
245 struct disk_partition p;
246
247 if (part_get_info(dev, i+1, &p))
248 goto fail;
249
250 if ((partitions[i].size && p.size < partitions[i].size) ||
251 (partitions[i].start && p.start < partitions[i].start) ||
252 (p.sys_ind != partitions[i].sys_ind))
253 goto fail;
254 }
255 ret = 0;
256 fail:
257 free(partitions);
258 return ret;
259 }
260
do_mbr(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])261 static int do_mbr(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
262 {
263 const char *parts = NULL;
264 int ret = CMD_RET_SUCCESS;
265 int dev = 0;
266 char *ep;
267 struct blk_desc *blk_dev_desc = NULL;
268
269 if (argc != 4 && argc != 5)
270 return CMD_RET_USAGE;
271
272 dev = (int)simple_strtoul(argv[3], &ep, 10);
273 if (!ep || ep[0] != '\0') {
274 printf("'%s' is not a number\n", argv[3]);
275 return CMD_RET_USAGE;
276 }
277 blk_dev_desc = blk_get_dev(argv[2], dev);
278 if (!blk_dev_desc) {
279 printf("%s: %s dev %d NOT available\n",
280 __func__, argv[2], dev);
281 return CMD_RET_FAILURE;
282 }
283
284 if ((strcmp(argv[1], "write") == 0)) {
285 parts = (argc == 5) ? argv[4] : env_get("mbr_parts");
286 printf("MBR: write ");
287 ret = do_write_mbr(blk_dev_desc, parts);
288 } else if ((strcmp(argv[1], "verify") == 0)) {
289 printf("MBR: verify ");
290 parts = (argc == 5) ? argv[4] : env_get("mbr_parts");
291 ret = do_verify_mbr(blk_dev_desc, parts);
292 } else {
293 return CMD_RET_USAGE;
294 }
295
296 if (ret) {
297 printf("error!\n");
298 return CMD_RET_FAILURE;
299 }
300
301 printf("success!\n");
302 return CMD_RET_SUCCESS;
303 }
304
305 U_BOOT_CMD(mbr, CONFIG_SYS_MAXARGS, 1, do_mbr,
306 "MBR (Master Boot Record)",
307 "<command> <interface> <dev> <partitions_list>\n"
308 " - MBR partition table restoration utility\n"
309 " Restore or check partition information on a device connected\n"
310 " to the given block interface\n"
311 " Example usage:\n"
312 " mbr write mmc 0 [\"${mbr_parts}\"]\n"
313 " mbr verify mmc 0 [\"${partitions}\"]\n"
314 );
315