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 */
29static 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 */
67static 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
93static 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;
198err:
199	free(str);
200	free(parts);
201
202	return errno;
203}
204
205static 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
232static 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_by_type(dev, i + 1, PART_TYPE_DOS, &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;
256fail:
257	free(partitions);
258	return ret;
259}
260
261static 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)dectoul(argv[3], &ep);
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
305U_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