1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * (C) Copyright 2001 4 * Raymond Lo, lo@routefree.com 5 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 6 */ 7 8/* 9 * Support for harddisk partitions. 10 * 11 * To be compatible with LinuxPPC and Apple we use the standard Apple 12 * SCSI disk partitioning scheme. For more information see: 13 * http://developer.apple.com/techpubs/mac/Devices/Devices-126.html#MARKER-14-92 14 */ 15 16#include <common.h> 17#include <blk.h> 18#include <command.h> 19#include <ide.h> 20#include <memalign.h> 21#include <asm/unaligned.h> 22#include <linux/compiler.h> 23#include "part_dos.h" 24#include <part.h> 25 26#define DOS_PART_DEFAULT_SECTOR 512 27 28/* should this be configurable? It looks like it's not very common at all 29 * to use large numbers of partitions */ 30#define MAX_EXT_PARTS 256 31 32static inline int is_extended(int part_type) 33{ 34 return (part_type == DOS_PART_TYPE_EXTENDED || 35 part_type == DOS_PART_TYPE_EXTENDED_LBA || 36 part_type == DOS_PART_TYPE_EXTENDED_LINUX); 37} 38 39static int get_bootable(dos_partition_t *p) 40{ 41 int ret = 0; 42 43 if (p->sys_ind == 0xef) 44 ret |= PART_EFI_SYSTEM_PARTITION; 45 if (p->boot_ind == 0x80) 46 ret |= PART_BOOTABLE; 47 return ret; 48} 49 50static void print_one_part(dos_partition_t *p, lbaint_t ext_part_sector, 51 int part_num, unsigned int disksig) 52{ 53 lbaint_t lba_start = ext_part_sector + get_unaligned_le32(p->start4); 54 lbaint_t lba_size = get_unaligned_le32(p->size4); 55 56 printf("%3d\t%-10" LBAFlength "u\t%-10" LBAFlength 57 "u\t%08x-%02x\t%02x%s%s\n", 58 part_num, lba_start, lba_size, disksig, part_num, p->sys_ind, 59 (is_extended(p->sys_ind) ? " Extd" : ""), 60 (get_bootable(p) ? " Boot" : "")); 61} 62 63static int test_block_type(unsigned char *buffer) 64{ 65 int slot; 66 struct dos_partition *p; 67 int part_count = 0; 68 69 if((buffer[DOS_PART_MAGIC_OFFSET + 0] != 0x55) || 70 (buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) ) { 71 return (-1); 72 } /* no DOS Signature at all */ 73 p = (struct dos_partition *)&buffer[DOS_PART_TBL_OFFSET]; 74 75 /* Check that the boot indicators are valid and count the partitions. */ 76 for (slot = 0; slot < 4; ++slot, ++p) { 77 if (p->boot_ind != 0 && p->boot_ind != 0x80) 78 break; 79 if (p->sys_ind) 80 ++part_count; 81 } 82 83 /* 84 * If the partition table is invalid or empty, 85 * check if this is a DOS PBR 86 */ 87 if (slot != 4 || !part_count) { 88 if (!strncmp((char *)&buffer[DOS_PBR_FSTYPE_OFFSET], 89 "FAT", 3) || 90 !strncmp((char *)&buffer[DOS_PBR32_FSTYPE_OFFSET], 91 "FAT32", 5)) 92 return DOS_PBR; /* This is a DOS PBR and not an MBR */ 93 } 94 if (slot == 4) 95 return DOS_MBR; /* This is an DOS MBR */ 96 97 /* This is neither a DOS MBR nor a DOS PBR */ 98 return -1; 99} 100 101static int part_test_dos(struct blk_desc *desc) 102{ 103#ifndef CONFIG_SPL_BUILD 104 ALLOC_CACHE_ALIGN_BUFFER(legacy_mbr, mbr, 105 DIV_ROUND_UP(desc->blksz, sizeof(legacy_mbr))); 106 107 if (blk_dread(desc, 0, 1, (ulong *)mbr) != 1) 108 return -1; 109 110 if (test_block_type((unsigned char *)mbr) != DOS_MBR) 111 return -1; 112 113 if (desc->sig_type == SIG_TYPE_NONE && mbr->unique_mbr_signature) { 114 desc->sig_type = SIG_TYPE_MBR; 115 desc->mbr_sig = mbr->unique_mbr_signature; 116 } 117#else 118 ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, desc->blksz); 119 120 if (blk_dread(desc, 0, 1, (ulong *)buffer) != 1) 121 return -1; 122 123 if (test_block_type(buffer) != DOS_MBR) 124 return -1; 125#endif 126 127 return 0; 128} 129 130/* Print a partition that is relative to its Extended partition table 131 */ 132static void print_partition_extended(struct blk_desc *desc, 133 lbaint_t ext_part_sector, 134 lbaint_t relative, 135 int part_num, unsigned int disksig) 136{ 137 ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, desc->blksz); 138 dos_partition_t *pt; 139 int i; 140 141 /* set a maximum recursion level */ 142 if (part_num > MAX_EXT_PARTS) 143 { 144 printf("** Nested DOS partitions detected, stopping **\n"); 145 return; 146 } 147 148 if (blk_dread(desc, ext_part_sector, 1, (ulong *)buffer) != 1) { 149 printf ("** Can't read partition table on %d:" LBAFU " **\n", 150 desc->devnum, ext_part_sector); 151 return; 152 } 153 i=test_block_type(buffer); 154 if (i != DOS_MBR) { 155 printf ("bad MBR sector signature 0x%02x%02x\n", 156 buffer[DOS_PART_MAGIC_OFFSET], 157 buffer[DOS_PART_MAGIC_OFFSET + 1]); 158 return; 159 } 160 161 if (!ext_part_sector) 162 disksig = get_unaligned_le32(&buffer[DOS_PART_DISKSIG_OFFSET]); 163 164 /* Print all primary/logical partitions */ 165 pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET); 166 for (i = 0; i < 4; i++, pt++) { 167 /* 168 * fdisk does not show the extended partitions that 169 * are not in the MBR 170 */ 171 172 if ((pt->sys_ind != 0) && 173 (ext_part_sector == 0 || !is_extended (pt->sys_ind)) ) { 174 print_one_part(pt, ext_part_sector, part_num, disksig); 175 } 176 177 /* Reverse engr the fdisk part# assignment rule! */ 178 if ((ext_part_sector == 0) || 179 (pt->sys_ind != 0 && !is_extended (pt->sys_ind)) ) { 180 part_num++; 181 } 182 } 183 184 /* Follows the extended partitions */ 185 pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET); 186 for (i = 0; i < 4; i++, pt++) { 187 if (is_extended (pt->sys_ind)) { 188 lbaint_t lba_start 189 = get_unaligned_le32 (pt->start4) + relative; 190 191 print_partition_extended(desc, lba_start, 192 !ext_part_sector ? lba_start : 193 relative, part_num, disksig); 194 } 195 } 196 197 return; 198} 199 200 201/* Print a partition that is relative to its Extended partition table 202 */ 203static int part_get_info_extended(struct blk_desc *desc, 204 lbaint_t ext_part_sector, lbaint_t relative, 205 int part_num, int which_part, 206 struct disk_partition *info, uint disksig) 207{ 208 ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, desc->blksz); 209 struct disk_partition wdinfo = { 0 }; 210 dos_partition_t *pt; 211 int i, ret; 212 int dos_type; 213 214 /* set a maximum recursion level */ 215 if (part_num > MAX_EXT_PARTS) 216 { 217 printf("** Nested DOS partitions detected, stopping **\n"); 218 return -1; 219 } 220 221 if (blk_dread(desc, ext_part_sector, 1, (ulong *)buffer) != 1) { 222 printf ("** Can't read partition table on %d:" LBAFU " **\n", 223 desc->devnum, ext_part_sector); 224 return -1; 225 } 226 if (buffer[DOS_PART_MAGIC_OFFSET] != 0x55 || 227 buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) { 228 printf ("bad MBR sector signature 0x%02x%02x\n", 229 buffer[DOS_PART_MAGIC_OFFSET], 230 buffer[DOS_PART_MAGIC_OFFSET + 1]); 231 return -1; 232 } 233 234 if (CONFIG_IS_ENABLED(PARTITION_UUIDS) && !ext_part_sector) 235 disksig = get_unaligned_le32(&buffer[DOS_PART_DISKSIG_OFFSET]); 236 237 ret = part_get_info_whole_disk(desc, &wdinfo); 238 if (ret) 239 return ret; 240 241 /* Print all primary/logical partitions */ 242 pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET); 243 for (i = 0; i < 4; i++, pt++) { 244 /* 245 * fdisk does not show the extended partitions that 246 * are not in the MBR 247 */ 248 if (((pt->boot_ind & ~0x80) == 0) && 249 (pt->sys_ind != 0) && 250 (part_num == which_part) && 251 (ext_part_sector == 0 || is_extended(pt->sys_ind) == 0)) { 252 if (wdinfo.blksz > DOS_PART_DEFAULT_SECTOR) 253 info->blksz = wdinfo.blksz; 254 else 255 info->blksz = DOS_PART_DEFAULT_SECTOR; 256 info->start = (lbaint_t)(ext_part_sector + 257 get_unaligned_le32(pt->start4)); 258 info->size = (lbaint_t)get_unaligned_le32(pt->size4); 259 part_set_generic_name(desc, part_num, 260 (char *)info->name); 261 /* sprintf(info->type, "%d, pt->sys_ind); */ 262 strcpy((char *)info->type, "U-Boot"); 263 info->bootable = get_bootable(pt); 264 if (CONFIG_IS_ENABLED(PARTITION_UUIDS)) { 265 char str[12]; 266 267 sprintf(str, "%08x-%02x", disksig, part_num); 268 disk_partition_set_uuid(info, str); 269 } 270 info->sys_ind = pt->sys_ind; 271 return 0; 272 } 273 274 /* Reverse engr the fdisk part# assignment rule! */ 275 if ((ext_part_sector == 0) || 276 (pt->sys_ind != 0 && !is_extended (pt->sys_ind)) ) { 277 part_num++; 278 } 279 } 280 281 /* Follows the extended partitions */ 282 pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET); 283 for (i = 0; i < 4; i++, pt++) { 284 if (is_extended (pt->sys_ind)) { 285 lbaint_t lba_start 286 = get_unaligned_le32 (pt->start4) + relative; 287 288 return part_get_info_extended(desc, lba_start, 289 ext_part_sector == 0 ? lba_start : relative, 290 part_num, which_part, info, disksig); 291 } 292 } 293 294 /* Check for DOS PBR if no partition is found */ 295 dos_type = test_block_type(buffer); 296 297 if (dos_type == DOS_PBR) { 298 info->start = 0; 299 info->size = desc->lba; 300 if (wdinfo.blksz > DOS_PART_DEFAULT_SECTOR) 301 info->blksz = wdinfo.blksz; 302 else 303 info->blksz = DOS_PART_DEFAULT_SECTOR; 304 info->bootable = 0; 305 strcpy((char *)info->type, "U-Boot"); 306 disk_partition_clr_uuid(info); 307 return 0; 308 } 309 310 return -1; 311} 312 313static void __maybe_unused part_print_dos(struct blk_desc *desc) 314{ 315 printf("Part\tStart Sector\tNum Sectors\tUUID\t\tType\n"); 316 print_partition_extended(desc, 0, 0, 1, 0); 317} 318 319static int __maybe_unused part_get_info_dos(struct blk_desc *desc, int part, 320 struct disk_partition *info) 321{ 322 return part_get_info_extended(desc, 0, 0, 1, part, info, 0); 323} 324 325int is_valid_dos_buf(void *buf) 326{ 327 return test_block_type(buf) == DOS_MBR ? 0 : -1; 328} 329 330#if IS_ENABLED(CONFIG_CMD_MBR) 331static void lba_to_chs(lbaint_t lba, unsigned char *rc, unsigned char *rh, 332 unsigned char *rs) 333{ 334 unsigned int c, h, s; 335 /* use fixed CHS geometry */ 336 unsigned int sectpertrack = 63; 337 unsigned int heads = 255; 338 339 c = (lba + 1) / sectpertrack / heads; 340 h = (lba + 1) / sectpertrack - c * heads; 341 s = (lba + 1) - (c * heads + h) * sectpertrack; 342 343 if (c > 1023) { 344 c = 1023; 345 h = 254; 346 s = 63; 347 } 348 349 *rc = c & 0xff; 350 *rh = h; 351 *rs = s + ((c & 0x300) >> 2); 352} 353 354static void mbr_fill_pt_entry(dos_partition_t *pt, lbaint_t start, 355 lbaint_t relative, lbaint_t size, uchar sys_ind, bool bootable) 356{ 357 pt->boot_ind = bootable ? 0x80 : 0x00; 358 pt->sys_ind = sys_ind; 359 lba_to_chs(start, &pt->cyl, &pt->head, &pt->sector); 360 lba_to_chs(start + size - 1, &pt->end_cyl, &pt->end_head, &pt->end_sector); 361 put_unaligned_le32(relative, &pt->start4); 362 put_unaligned_le32(size, &pt->size4); 363} 364 365int write_mbr_partitions(struct blk_desc *dev, 366 struct disk_partition *p, int count, unsigned int disksig) 367{ 368 ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev->blksz); 369 lbaint_t ext_part_start = 0, ext_part_size = 0, ext_part_sect = 0; 370 dos_partition_t *pt; 371 int i; 372 373 memset(buffer, 0, dev->blksz); 374 buffer[DOS_PART_MAGIC_OFFSET] = 0x55; 375 buffer[DOS_PART_MAGIC_OFFSET + 1] = 0xaa; 376 put_unaligned_le32(disksig, &buffer[DOS_PART_DISKSIG_OFFSET]); 377 pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET); 378 379 /* create all primary partitions */ 380 for (i = 0; i < 4 && i < count; i++, pt++) { 381 mbr_fill_pt_entry(pt, p[i].start, p[i].start, p[i].size, 382 p[i].sys_ind, p[i].bootable); 383 if (is_extended(p[i].sys_ind)) { 384 ext_part_start = p[i].start; 385 ext_part_size = p[i].size; 386 ext_part_sect = p[i].start; 387 } 388 } 389 390 if (i < count && !ext_part_start) { 391 printf("%s: extended partition is needed for more than 4 partitions\n", 392 __func__); 393 return -1; 394 } 395 396 /* write MBR */ 397 if (blk_dwrite(dev, 0, 1, buffer) != 1) { 398 printf("%s: failed writing 'MBR' (1 blks at 0x0)\n", 399 __func__); 400 return -1; 401 } 402 403 /* create extended volumes */ 404 for (; i < count; i++) { 405 lbaint_t next_ebr = 0; 406 407 memset(buffer, 0, dev->blksz); 408 buffer[DOS_PART_MAGIC_OFFSET] = 0x55; 409 buffer[DOS_PART_MAGIC_OFFSET + 1] = 0xaa; 410 pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET); 411 412 mbr_fill_pt_entry(pt, p[i].start, p[i].start - ext_part_sect, 413 p[i].size, p[i].sys_ind, p[i].bootable); 414 415 if (i + 1 < count) { 416 pt++; 417 next_ebr = p[i].start + p[i].size; 418 mbr_fill_pt_entry(pt, next_ebr, 419 next_ebr - ext_part_start, 420 p[i+1].start + p[i+1].size - next_ebr, 421 DOS_PART_TYPE_EXTENDED, 0); 422 } 423 424 /* write EBR */ 425 if (blk_dwrite(dev, ext_part_sect, 1, buffer) != 1) { 426 printf("%s: failed writing 'EBR' (1 blks at 0x%lx)\n", 427 __func__, ext_part_sect); 428 return -1; 429 } 430 ext_part_sect = next_ebr; 431 } 432 433 /* Update the partition table entries*/ 434 part_init(dev); 435 436 return 0; 437} 438 439int layout_mbr_partitions(struct disk_partition *p, int count, 440 lbaint_t total_sectors) 441{ 442 struct disk_partition *ext = NULL; 443 int i, j; 444 lbaint_t ext_vol_start; 445 446 /* calculate primary partitions start and size if needed */ 447 if (!p[0].start) 448 p[0].start = DOS_PART_DEFAULT_GAP; 449 for (i = 0; i < 4 && i < count; i++) { 450 if (!p[i].start) 451 p[i].start = p[i - 1].start + p[i - 1].size; 452 if (!p[i].size) { 453 lbaint_t end = total_sectors; 454 lbaint_t allocated = 0; 455 456 for (j = i + 1; j < 4 && j < count; j++) { 457 if (p[j].start) { 458 end = p[j].start; 459 break; 460 } 461 allocated += p[j].size; 462 } 463 p[i].size = end - allocated - p[i].start; 464 } 465 if (p[i].sys_ind == 0x05) 466 ext = &p[i]; 467 } 468 469 if (count <= 4) 470 return 0; 471 472 if (!ext) { 473 log_err("extended partition is needed for more than 4 partitions\n"); 474 return -EINVAL; 475 } 476 477 /* calculate extended volumes start and size if needed */ 478 ext_vol_start = ext->start; 479 for (i = 4; i < count; i++) { 480 if (!p[i].start) 481 p[i].start = ext_vol_start + DOS_PART_DEFAULT_GAP; 482 if (!p[i].size) { 483 lbaint_t end = ext->start + ext->size; 484 lbaint_t allocated = 0; 485 486 for (j = i + 1; j < count; j++) { 487 if (p[j].start) { 488 end = p[j].start - DOS_PART_DEFAULT_GAP; 489 break; 490 } 491 allocated += p[j].size + DOS_PART_DEFAULT_GAP; 492 } 493 p[i].size = end - allocated - p[i].start; 494 } 495 ext_vol_start = p[i].start + p[i].size; 496 } 497 498 return 0; 499} 500#endif 501 502int write_mbr_sector(struct blk_desc *desc, void *buf) 503{ 504 if (is_valid_dos_buf(buf)) 505 return -1; 506 507 /* write MBR */ 508 if (blk_dwrite(desc, 0, 1, buf) != 1) { 509 printf("%s: failed writing '%s' (1 blks at 0x0)\n", 510 __func__, "MBR"); 511 return 1; 512 } 513 514 /* Update the partition table entries*/ 515 part_init(desc); 516 517 return 0; 518} 519 520U_BOOT_PART_TYPE(dos) = { 521 .name = "DOS", 522 .part_type = PART_TYPE_DOS, 523 .max_entries = DOS_ENTRY_NUMBERS, 524 .get_info = part_get_info_ptr(part_get_info_dos), 525 .print = part_print_ptr(part_print_dos), 526 .test = part_test_dos, 527}; 528