]>
Commit | Line | Data |
---|---|---|
750c543c MS |
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 <[email protected]> | |
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 | */ | |
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 | */ | |
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 | ||
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 | ||
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 | ||
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 | ||
55a42443 | 247 | if (part_get_info_by_type(dev, i + 1, PART_TYPE_DOS, &p)) |
750c543c MS |
248 | goto fail; |
249 | ||
362a79f3 SG |
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) | |
750c543c MS |
253 | goto fail; |
254 | } | |
255 | ret = 0; | |
256 | fail: | |
257 | free(partitions); | |
258 | return ret; | |
259 | } | |
260 | ||
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 | ||
0b1284eb | 272 | dev = (int)dectoul(argv[3], &ep); |
750c543c MS |
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 | ); |