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