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