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