disk.c revision 225736
1/*- 2 * Copyright (c) 2008 Semihalf, Rafal Jaworowski 3 * Copyright (c) 2009 Semihalf, Piotr Ziecik 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 */ 28 29/* 30 * Block storage I/O routines for U-Boot 31 */ 32 33#include <sys/cdefs.h> 34__FBSDID("$FreeBSD: stable/9/sys/boot/uboot/lib/disk.c 208534 2010-05-25 09:59:53Z raj $"); 35 36#include <sys/param.h> 37#include <sys/endian.h> 38#include <sys/queue.h> 39#include <netinet/in.h> 40#include <machine/stdarg.h> 41#include <stand.h> 42#include <uuid.h> 43 44#define FSTYPENAMES 45#include <sys/disklabel.h> 46#include <sys/diskmbr.h> 47#include <sys/gpt.h> 48 49#include "api_public.h" 50#include "bootstrap.h" 51#include "glue.h" 52#include "libuboot.h" 53 54#define DEBUG 55#undef DEBUG 56 57#define stor_printf(fmt, args...) do { \ 58 printf("%s%d: ", dev->d_dev->dv_name, dev->d_unit); \ 59 printf(fmt, ##args); \ 60} while (0) 61 62#ifdef DEBUG 63#define debugf(fmt, args...) do { printf("%s(): ", __func__); \ 64 printf(fmt,##args); } while (0) 65#else 66#define debugf(fmt, args...) 67#endif 68 69struct gpt_part { 70 int gp_index; 71 uuid_t gp_type; 72 uint64_t gp_start; 73 uint64_t gp_end; 74}; 75 76struct open_dev { 77 int od_bsize; /* block size */ 78 int od_bstart; /* start block offset from beginning of disk */ 79 union { 80 struct { 81 struct disklabel bsdlabel; 82 } _bsd; 83 struct { 84 struct gpt_part *gpt_partitions; 85 int gpt_nparts; 86 } _gpt; 87 } _data; 88}; 89 90#define od_bsdlabel _data._bsd.bsdlabel 91#define od_nparts _data._gpt.gpt_nparts 92#define od_partitions _data._gpt.gpt_partitions 93 94static uuid_t efi = GPT_ENT_TYPE_EFI; 95static uuid_t freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT; 96static uuid_t freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS; 97static uuid_t freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP; 98static uuid_t freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS; 99static uuid_t ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA; 100 101static int stor_info[UB_MAX_DEV]; 102static int stor_info_no = 0; 103static int stor_opendev(struct open_dev **, struct uboot_devdesc *); 104static int stor_closedev(struct uboot_devdesc *); 105static int stor_readdev(struct uboot_devdesc *, daddr_t, size_t, char *); 106static int stor_open_count = 0; 107 108/* devsw I/F */ 109static int stor_init(void); 110static int stor_strategy(void *, int, daddr_t, size_t, char *, size_t *); 111static int stor_open(struct open_file *, ...); 112static int stor_close(struct open_file *); 113static void stor_print(int); 114 115struct devsw uboot_storage = { 116 "disk", 117 DEVT_DISK, 118 stor_init, 119 stor_strategy, 120 stor_open, 121 stor_close, 122 noioctl, 123 stor_print 124}; 125 126static void 127uuid_letoh(uuid_t *uuid) 128{ 129 130 uuid->time_low = le32toh(uuid->time_low); 131 uuid->time_mid = le16toh(uuid->time_mid); 132 uuid->time_hi_and_version = le16toh(uuid->time_hi_and_version); 133} 134 135static int 136stor_init(void) 137{ 138 struct device_info *di; 139 int i, found = 0; 140 141 if (devs_no == 0) { 142 printf("No U-Boot devices! Really enumerated?\n"); 143 return (-1); 144 } 145 146 for (i = 0; i < devs_no; i++) { 147 di = ub_dev_get(i); 148 if ((di != NULL) && (di->type & DEV_TYP_STOR)) { 149 if (stor_info_no >= UB_MAX_DEV) { 150 printf("Too many storage devices: %d\n", 151 stor_info_no); 152 return (-1); 153 } 154 stor_info[stor_info_no++] = i; 155 found = 1; 156 } 157 } 158 159 if (!found) { 160 debugf("No storage devices\n"); 161 return (-1); 162 } 163 164 debugf("storage devices found: %d\n", stor_info_no); 165 return (0); 166} 167 168static int 169stor_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, 170 size_t *rsize) 171{ 172 struct uboot_devdesc *dev = (struct uboot_devdesc *)devdata; 173 struct open_dev *od = (struct open_dev *)dev->d_disk.data; 174 int bcount, err; 175 176 debugf("od=%p, size=%d, bsize=%d\n", od, size, od->od_bsize); 177 178 if (rw != F_READ) { 179 stor_printf("write attempt, operation not supported!\n"); 180 return (EROFS); 181 } 182 183 if (size % od->od_bsize) { 184 stor_printf("size=%d not multiple of device block size=%d\n", 185 size, od->od_bsize); 186 return (EIO); 187 } 188 bcount = size / od->od_bsize; 189 190 if (rsize) 191 *rsize = 0; 192 193 err = stor_readdev(dev, blk + od->od_bstart, bcount, buf); 194 if (!err && rsize) 195 *rsize = size; 196 197 return (err); 198} 199 200static int 201stor_open(struct open_file *f, ...) 202{ 203 va_list ap; 204 struct open_dev *od; 205 struct uboot_devdesc *dev; 206 int err; 207 208 va_start(ap, f); 209 dev = va_arg(ap, struct uboot_devdesc *); 210 va_end(ap); 211 212 if ((err = stor_opendev(&od, dev)) != 0) 213 return (err); 214 215 ((struct uboot_devdesc *)(f->f_devdata))->d_disk.data = od; 216 217 return (0); 218} 219 220static int 221stor_close(struct open_file *f) 222{ 223 struct uboot_devdesc *dev; 224 225 dev = (struct uboot_devdesc *)(f->f_devdata); 226 227 return (stor_closedev(dev)); 228} 229 230static int 231stor_open_gpt(struct open_dev *od, struct uboot_devdesc *dev) 232{ 233 char *buf; 234 struct dos_partition *dp; 235 struct gpt_hdr *hdr; 236 struct gpt_ent *ent; 237 daddr_t slba, lba, elba; 238 int eps, part, i; 239 int err = 0; 240 241 od->od_nparts = 0; 242 od->od_partitions = NULL; 243 244 /* Devices with block size smaller than 512 bytes cannot use GPT */ 245 if (od->od_bsize < 512) 246 return (ENXIO); 247 248 /* Allocate 1 block */ 249 buf = malloc(od->od_bsize); 250 if (!buf) { 251 stor_printf("could not allocate memory for GPT\n"); 252 return (ENOMEM); 253 } 254 255 /* Read MBR */ 256 err = stor_readdev(dev, 0, 1, buf); 257 if (err) { 258 stor_printf("GPT read error=%d\n", err); 259 err = EIO; 260 goto out; 261 } 262 263 /* Check the slice table magic. */ 264 if (le16toh(*((uint16_t *)(buf + DOSMAGICOFFSET))) != DOSMAGIC) { 265 err = ENXIO; 266 goto out; 267 } 268 269 /* Check GPT slice */ 270 dp = (struct dos_partition *)(buf + DOSPARTOFF); 271 part = 0; 272 273 for (i = 0; i < NDOSPART; i++) { 274 if (dp[i].dp_typ == 0xee) 275 part += 1; 276 else if (dp[i].dp_typ != 0x00) { 277 err = EINVAL; 278 goto out; 279 } 280 } 281 282 if (part != 1) { 283 err = EINVAL; 284 goto out; 285 } 286 287 /* Read primary GPT header */ 288 err = stor_readdev(dev, 1, 1, buf); 289 if (err) { 290 stor_printf("GPT read error=%d\n", err); 291 err = EIO; 292 goto out; 293 } 294 295 hdr = (struct gpt_hdr *)buf; 296 297 /* Check GPT header */ 298 if (bcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0 || 299 le64toh(hdr->hdr_lba_self) != 1 || 300 le32toh(hdr->hdr_revision) < 0x00010000 || 301 le32toh(hdr->hdr_entsz) < sizeof(*ent) || 302 od->od_bsize % le32toh(hdr->hdr_entsz) != 0) { 303 debugf("Invalid GPT header!\n"); 304 err = EINVAL; 305 goto out; 306 } 307 308 /* Count number of valid partitions */ 309 part = 0; 310 eps = od->od_bsize / le32toh(hdr->hdr_entsz); 311 slba = le64toh(hdr->hdr_lba_table); 312 elba = slba + le32toh(hdr->hdr_entries) / eps; 313 314 for (lba = slba; lba < elba; lba++) { 315 err = stor_readdev(dev, lba, 1, buf); 316 if (err) { 317 stor_printf("GPT read error=%d\n", err); 318 err = EIO; 319 goto out; 320 } 321 322 ent = (struct gpt_ent *)buf; 323 324 for (i = 0; i < eps; i++) { 325 if (uuid_is_nil(&ent[i].ent_type, NULL) || 326 le64toh(ent[i].ent_lba_start) == 0 || 327 le64toh(ent[i].ent_lba_end) < 328 le64toh(ent[i].ent_lba_start)) 329 continue; 330 331 part += 1; 332 } 333 } 334 335 /* Save information about partitions */ 336 if (part != 0) { 337 od->od_nparts = part; 338 od->od_partitions = malloc(part * sizeof(struct gpt_part)); 339 if (!od->od_partitions) { 340 stor_printf("could not allocate memory for GPT\n"); 341 err = ENOMEM; 342 goto out; 343 } 344 345 part = 0; 346 for (lba = slba; lba < elba; lba++) { 347 err = stor_readdev(dev, lba, 1, buf); 348 if (err) { 349 stor_printf("GPT read error=%d\n", err); 350 err = EIO; 351 goto out; 352 } 353 354 ent = (struct gpt_ent *)buf; 355 356 for (i = 0; i < eps; i++) { 357 if (uuid_is_nil(&ent[i].ent_type, NULL) || 358 le64toh(ent[i].ent_lba_start) == 0 || 359 le64toh(ent[i].ent_lba_end) < 360 le64toh(ent[i].ent_lba_start)) 361 continue; 362 363 od->od_partitions[part].gp_index = (lba - slba) 364 * eps + i + 1; 365 od->od_partitions[part].gp_type = 366 ent[i].ent_type; 367 od->od_partitions[part].gp_start = 368 le64toh(ent[i].ent_lba_start); 369 od->od_partitions[part].gp_end = 370 le64toh(ent[i].ent_lba_end); 371 372 uuid_letoh(&od->od_partitions[part].gp_type); 373 part += 1; 374 } 375 } 376 } 377 378 dev->d_disk.ptype = PTYPE_GPT; 379 /* 380 * If index of partition to open (dev->d_disk.pnum) is not defined 381 * we set it to the index of the first existing partition. This 382 * handles cases when only a disk device is specified (without full 383 * partition information) by the caller. 384 */ 385 if ((od->od_nparts > 0) && (dev->d_disk.pnum == 0)) 386 dev->d_disk.pnum = od->od_partitions[0].gp_index; 387 388 for (i = 0; i < od->od_nparts; i++) 389 if (od->od_partitions[i].gp_index == dev->d_disk.pnum) 390 od->od_bstart = od->od_partitions[i].gp_start; 391 392out: 393 if (err && od->od_partitions) 394 free(od->od_partitions); 395 396 free(buf); 397 return (err); 398} 399 400static int 401stor_open_bsdlabel(struct open_dev *od, struct uboot_devdesc *dev) 402{ 403 char *buf; 404 struct disklabel *dl; 405 int err = 0; 406 407 /* Allocate 1 block */ 408 buf = malloc(od->od_bsize); 409 if (!buf) { 410 stor_printf("could not allocate memory for disklabel\n"); 411 return (ENOMEM); 412 } 413 414 /* Read disklabel */ 415 err = stor_readdev(dev, LABELSECTOR, 1, buf); 416 if (err) { 417 stor_printf("disklabel read error=%d\n", err); 418 err = ERDLAB; 419 goto out; 420 } 421 bcopy(buf + LABELOFFSET, &od->od_bsdlabel, sizeof(struct disklabel)); 422 dl = &od->od_bsdlabel; 423 424 if (dl->d_magic != DISKMAGIC) { 425 stor_printf("no disklabel magic!\n"); 426 err = EUNLAB; 427 goto out; 428 } 429 430 od->od_bstart = dl->d_partitions[dev->d_disk.pnum].p_offset; 431 dev->d_disk.ptype = PTYPE_BSDLABEL; 432 433 debugf("bstart=%d\n", od->od_bstart); 434 435out: 436 free(buf); 437 return (err); 438} 439 440static int 441stor_readdev(struct uboot_devdesc *dev, daddr_t blk, size_t size, char *buf) 442{ 443 lbasize_t real_size; 444 int err, handle; 445 446 debugf("reading size=%d @ 0x%08x\n", size, (uint32_t)buf); 447 448 handle = stor_info[dev->d_unit]; 449 err = ub_dev_read(handle, buf, size, blk, &real_size); 450 if (err != 0) { 451 stor_printf("read failed, error=%d\n", err); 452 return (EIO); 453 } 454 455 if (real_size != size) { 456 stor_printf("real size != size\n"); 457 err = EIO; 458 } 459 460 return (err); 461} 462 463 464static int 465stor_opendev(struct open_dev **odp, struct uboot_devdesc *dev) 466{ 467 struct device_info *di; 468 struct open_dev *od; 469 int err, h; 470 471 h = stor_info[dev->d_unit]; 472 473 debugf("refcount=%d\n", stor_open_count); 474 475 /* 476 * There can be recursive open calls from the infrastructure, but at 477 * U-Boot level open the device only the first time. 478 */ 479 if (stor_open_count > 0) 480 stor_open_count++; 481 else if ((err = ub_dev_open(h)) != 0) { 482 stor_printf("device open failed with error=%d, handle=%d\n", 483 err, h); 484 *odp = NULL; 485 return (ENXIO); 486 } 487 488 if ((di = ub_dev_get(h)) == NULL) 489 panic("could not retrieve U-Boot device_info, handle=%d", h); 490 491 if ((od = malloc(sizeof(struct open_dev))) == NULL) { 492 stor_printf("could not allocate memory for open_dev\n"); 493 return (ENOMEM); 494 } 495 od->od_bsize = di->di_stor.block_size; 496 od->od_bstart = 0; 497 498 if ((err = stor_open_gpt(od, dev)) != 0) 499 err = stor_open_bsdlabel(od, dev); 500 501 if (err != 0) 502 free(od); 503 else { 504 stor_open_count = 1; 505 *odp = od; 506 } 507 508 return (err); 509} 510 511static int 512stor_closedev(struct uboot_devdesc *dev) 513{ 514 struct open_dev *od; 515 int err, h; 516 517 od = (struct open_dev *)dev->d_disk.data; 518 if (dev->d_disk.ptype == PTYPE_GPT && od->od_nparts != 0) 519 free(od->od_partitions); 520 521 free(od); 522 dev->d_disk.data = NULL; 523 524 if (--stor_open_count == 0) { 525 h = stor_info[dev->d_unit]; 526 if ((err = ub_dev_close(h)) != 0) { 527 stor_printf("device close failed with error=%d, " 528 "handle=%d\n", err, h); 529 return (ENXIO); 530 } 531 } 532 533 return (0); 534} 535 536/* Given a size in 512 byte sectors, convert it to a human-readable number. */ 537/* XXX stolen from sys/boot/i386/libi386/biosdisk.c, should really be shared */ 538static char * 539display_size(uint64_t size) 540{ 541 static char buf[80]; 542 char unit; 543 544 size /= 2; 545 unit = 'K'; 546 if (size >= 10485760000LL) { 547 size /= 1073741824; 548 unit = 'T'; 549 } else if (size >= 10240000) { 550 size /= 1048576; 551 unit = 'G'; 552 } else if (size >= 10000) { 553 size /= 1024; 554 unit = 'M'; 555 } 556 sprintf(buf, "%.6ld%cB", (long)size, unit); 557 return (buf); 558} 559 560static void 561stor_print_bsdlabel(struct uboot_devdesc *dev, char *prefix, int verbose) 562{ 563 char buf[512], line[80]; 564 struct disklabel *dl; 565 uint32_t off, size; 566 int err, i, t; 567 568 /* Read disklabel */ 569 err = stor_readdev(dev, LABELSECTOR, 1, buf); 570 if (err) { 571 sprintf(line, "%s%d: disklabel read error=%d\n", 572 dev->d_dev->dv_name, dev->d_unit, err); 573 pager_output(line); 574 return; 575 } 576 dl = (struct disklabel *)buf; 577 578 if (dl->d_magic != DISKMAGIC) { 579 sprintf(line, "%s%d: no disklabel magic!\n", 580 dev->d_dev->dv_name, dev->d_unit); 581 pager_output(line); 582 return; 583 } 584 585 /* Print partitions info */ 586 for (i = 0; i < dl->d_npartitions; i++) { 587 if ((t = dl->d_partitions[i].p_fstype) < FSMAXTYPES) { 588 589 off = dl->d_partitions[i].p_offset; 590 size = dl->d_partitions[i].p_size; 591 if (fstypenames[t] == NULL || size == 0) 592 continue; 593 594 if ((('a' + i) == 'c') && (!verbose)) 595 continue; 596 597 sprintf(line, " %s%c: %s %s (%d - %d)\n", prefix, 598 'a' + i, fstypenames[t], display_size(size), 599 off, off + size); 600 601 pager_output(line); 602 } 603 } 604} 605 606static void 607stor_print_gpt(struct uboot_devdesc *dev, char *prefix, int verbose) 608{ 609 struct open_dev *od = (struct open_dev *)dev->d_disk.data; 610 struct gpt_part *gp; 611 char line[80]; 612 char *fs; 613 int i; 614 615 for (i = 0; i < od->od_nparts; i++) { 616 gp = &od->od_partitions[i]; 617 618 if (uuid_equal(&gp->gp_type, &efi, NULL)) 619 fs = "EFI"; 620 else if (uuid_equal(&gp->gp_type, &ms_basic_data, NULL)) 621 fs = "FAT/NTFS"; 622 else if (uuid_equal(&gp->gp_type, &freebsd_boot, NULL)) 623 fs = "FreeBSD Boot"; 624 else if (uuid_equal(&gp->gp_type, &freebsd_ufs, NULL)) 625 fs = "FreeBSD UFS"; 626 else if (uuid_equal(&gp->gp_type, &freebsd_swap, NULL)) 627 fs = "FreeBSD Swap"; 628 else if (uuid_equal(&gp->gp_type, &freebsd_zfs, NULL)) 629 fs = "FreeBSD ZFS"; 630 else 631 fs = "unknown"; 632 633 sprintf(line, " %sp%u: %s %s (%lld - %lld)\n", prefix, 634 gp->gp_index, fs, 635 display_size(gp->gp_end + 1 - gp->gp_start), gp->gp_start, 636 gp->gp_end); 637 638 pager_output(line); 639 } 640} 641 642static void 643stor_print_one(int i, struct device_info *di, int verbose) 644{ 645 struct uboot_devdesc dev; 646 struct open_dev *od; 647 char line[80]; 648 649 sprintf(line, "\tdisk%d (%s)\n", i, ub_stor_type(di->type)); 650 pager_output(line); 651 652 dev.d_dev = &uboot_storage; 653 dev.d_unit = i; 654 dev.d_disk.pnum = -1; 655 dev.d_disk.data = NULL; 656 657 if (stor_opendev(&od, &dev) == 0) { 658 dev.d_disk.data = od; 659 660 if (dev.d_disk.ptype == PTYPE_GPT) { 661 sprintf(line, "\t\tdisk%d", i); 662 stor_print_gpt(&dev, line, verbose); 663 } else if (dev.d_disk.ptype == PTYPE_BSDLABEL) { 664 sprintf(line, "\t\tdisk%d", i); 665 stor_print_bsdlabel(&dev, line, verbose); 666 } 667 668 stor_closedev(&dev); 669 } 670} 671 672static void 673stor_print(int verbose) 674{ 675 struct device_info *di; 676 int i; 677 678 for (i = 0; i < stor_info_no; i++) { 679 di = ub_dev_get(stor_info[i]); 680 if (di != NULL) 681 stor_print_one(i, di, verbose); 682 } 683} 684