disk.c revision 193387
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: head/sys/boot/uboot/lib/disk.c 193387 2009-06-03 16:28:29Z 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 printf("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 for (i = 0; i < od->od_nparts; i++) 381 if (od->od_partitions[i].gp_index == dev->d_disk.pnum) 382 od->od_bstart = od->od_partitions[i].gp_start; 383 384out: 385 if (err && od->od_partitions) 386 free(od->od_partitions); 387 388 free(buf); 389 return (err); 390} 391 392static int 393stor_open_bsdlabel(struct open_dev *od, struct uboot_devdesc *dev) 394{ 395 char *buf; 396 struct disklabel *dl; 397 int err = 0; 398 399 /* Allocate 1 block */ 400 buf = malloc(od->od_bsize); 401 if (!buf) { 402 stor_printf("could not allocate memory for disklabel\n"); 403 return (ENOMEM); 404 } 405 406 /* Read disklabel */ 407 err = stor_readdev(dev, LABELSECTOR, 1, buf); 408 if (err) { 409 stor_printf("disklabel read error=%d\n", err); 410 err = ERDLAB; 411 goto out; 412 } 413 bcopy(buf + LABELOFFSET, &od->od_bsdlabel, sizeof(struct disklabel)); 414 dl = &od->od_bsdlabel; 415 416 if (dl->d_magic != DISKMAGIC) { 417 stor_printf("no disklabel magic!\n"); 418 err = EUNLAB; 419 goto out; 420 } 421 422 od->od_bstart = dl->d_partitions[dev->d_disk.pnum].p_offset; 423 dev->d_disk.ptype = PTYPE_BSDLABEL; 424 425 debugf("bstart=%d\n", od->od_bstart); 426 427out: 428 free(buf); 429 return (err); 430} 431 432static int 433stor_readdev(struct uboot_devdesc *dev, daddr_t blk, size_t size, char *buf) 434{ 435 lbasize_t real_size; 436 int err, handle; 437 438 debugf("reading size=%d @ 0x%08x\n", size, (uint32_t)buf); 439 440 handle = stor_info[dev->d_unit]; 441 err = ub_dev_read(handle, buf, size, blk, &real_size); 442 if (err != 0) { 443 stor_printf("read failed, error=%d\n", err); 444 return (EIO); 445 } 446 447 if (real_size != size) { 448 stor_printf("real size != size\n"); 449 err = EIO; 450 } 451 452 return (err); 453} 454 455 456static int 457stor_opendev(struct open_dev **odp, struct uboot_devdesc *dev) 458{ 459 struct device_info *di; 460 struct open_dev *od; 461 int err, h; 462 463 h = stor_info[dev->d_unit]; 464 465 debugf("refcount=%d\n", stor_open_count); 466 467 /* 468 * There can be recursive open calls from the infrastructure, but at 469 * U-Boot level open the device only the first time. 470 */ 471 if (stor_open_count > 0) 472 stor_open_count++; 473 else if ((err = ub_dev_open(h)) != 0) { 474 stor_printf("device open failed with error=%d, handle=%d\n", 475 err, h); 476 *odp = NULL; 477 return (ENXIO); 478 } 479 480 if ((di = ub_dev_get(h)) == NULL) 481 panic("could not retrieve U-Boot device_info, handle=%d", h); 482 483 if ((od = malloc(sizeof(struct open_dev))) == NULL) { 484 stor_printf("could not allocate memory for open_dev\n"); 485 return (ENOMEM); 486 } 487 od->od_bsize = di->di_stor.block_size; 488 od->od_bstart = 0; 489 490 if ((err = stor_open_gpt(od, dev)) != 0) 491 err = stor_open_bsdlabel(od, dev); 492 493 if (err != 0) 494 free(od); 495 else { 496 stor_open_count = 1; 497 *odp = od; 498 } 499 500 return (err); 501} 502 503static int 504stor_closedev(struct uboot_devdesc *dev) 505{ 506 struct open_dev *od; 507 int err, h; 508 509 od = (struct open_dev *)dev->d_disk.data; 510 if (dev->d_disk.ptype == PTYPE_GPT && od->od_nparts != 0) 511 free(od->od_partitions); 512 513 free(od); 514 dev->d_disk.data = NULL; 515 516 if (--stor_open_count == 0) { 517 h = stor_info[dev->d_unit]; 518 if ((err = ub_dev_close(h)) != 0) { 519 stor_printf("device close failed with error=%d, " 520 "handle=%d\n", err, h); 521 return (ENXIO); 522 } 523 } 524 525 return (0); 526} 527 528/* Given a size in 512 byte sectors, convert it to a human-readable number. */ 529/* XXX stolen from sys/boot/i386/libi386/biosdisk.c, should really be shared */ 530static char * 531display_size(uint64_t size) 532{ 533 static char buf[80]; 534 char unit; 535 536 size /= 2; 537 unit = 'K'; 538 if (size >= 10485760000LL) { 539 size /= 1073741824; 540 unit = 'T'; 541 } else if (size >= 10240000) { 542 size /= 1048576; 543 unit = 'G'; 544 } else if (size >= 10000) { 545 size /= 1024; 546 unit = 'M'; 547 } 548 sprintf(buf, "%.6ld%cB", (long)size, unit); 549 return (buf); 550} 551 552static void 553stor_print_bsdlabel(struct uboot_devdesc *dev, char *prefix, int verbose) 554{ 555 char buf[512], line[80]; 556 struct disklabel *dl; 557 uint32_t off, size; 558 int err, i, t; 559 560 /* Read disklabel */ 561 err = stor_readdev(dev, LABELSECTOR, 1, buf); 562 if (err) { 563 sprintf(line, "%s%d: disklabel read error=%d\n", 564 dev->d_dev->dv_name, dev->d_unit, err); 565 pager_output(line); 566 return; 567 } 568 dl = (struct disklabel *)buf; 569 570 if (dl->d_magic != DISKMAGIC) { 571 sprintf(line, "%s%d: no disklabel magic!\n", 572 dev->d_dev->dv_name, dev->d_unit); 573 pager_output(line); 574 return; 575 } 576 577 /* Print partitions info */ 578 for (i = 0; i < dl->d_npartitions; i++) { 579 if ((t = dl->d_partitions[i].p_fstype) < FSMAXTYPES) { 580 581 off = dl->d_partitions[i].p_offset; 582 size = dl->d_partitions[i].p_size; 583 if (fstypenames[t] == NULL || size == 0) 584 continue; 585 586 if ((('a' + i) == 'c') && (!verbose)) 587 continue; 588 589 sprintf(line, " %s%c: %s %s (%d - %d)\n", prefix, 590 'a' + i, fstypenames[t], display_size(size), 591 off, off + size); 592 593 pager_output(line); 594 } 595 } 596} 597 598static void 599stor_print_gpt(struct uboot_devdesc *dev, char *prefix, int verbose) 600{ 601 struct open_dev *od = (struct open_dev *)dev->d_disk.data; 602 struct gpt_part *gp; 603 char line[80]; 604 char *fs; 605 int i; 606 607 for (i = 0; i < od->od_nparts; i++) { 608 gp = &od->od_partitions[i]; 609 610 if (uuid_equal(&gp->gp_type, &efi, NULL)) 611 fs = "EFI"; 612 else if (uuid_equal(&gp->gp_type, &ms_basic_data, NULL)) 613 fs = "FAT/NTFS"; 614 else if (uuid_equal(&gp->gp_type, &freebsd_boot, NULL)) 615 fs = "FreeBSD Boot"; 616 else if (uuid_equal(&gp->gp_type, &freebsd_ufs, NULL)) 617 fs = "FreeBSD UFS"; 618 else if (uuid_equal(&gp->gp_type, &freebsd_swap, NULL)) 619 fs = "FreeBSD Swap"; 620 else if (uuid_equal(&gp->gp_type, &freebsd_zfs, NULL)) 621 fs = "FreeBSD ZFS"; 622 else 623 fs = "unknown"; 624 625 sprintf(line, " %sp%u: %s %s (%lld - %lld)\n", prefix, 626 gp->gp_index, fs, 627 display_size(gp->gp_end + 1 - gp->gp_start), gp->gp_start, 628 gp->gp_end); 629 630 pager_output(line); 631 } 632} 633 634static void 635stor_print_one(int i, struct device_info *di, int verbose) 636{ 637 struct uboot_devdesc dev; 638 struct open_dev *od; 639 char line[80]; 640 641 sprintf(line, "\tdisk%d (%s)\n", i, ub_stor_type(di->type)); 642 pager_output(line); 643 644 dev.d_dev = &uboot_storage; 645 dev.d_unit = i; 646 dev.d_disk.pnum = -1; 647 dev.d_disk.data = NULL; 648 649 if (stor_opendev(&od, &dev) == 0) { 650 dev.d_disk.data = od; 651 652 if (dev.d_disk.ptype == PTYPE_GPT) { 653 sprintf(line, "\t\tdisk%d", i); 654 stor_print_gpt(&dev, line, verbose); 655 } else if (dev.d_disk.ptype == PTYPE_BSDLABEL) { 656 sprintf(line, "\t\tdisk%d", i); 657 stor_print_bsdlabel(&dev, line, verbose); 658 } 659 660 stor_closedev(&dev); 661 } 662} 663 664static void 665stor_print(int verbose) 666{ 667 struct device_info *di; 668 int i; 669 670 for (i = 0; i < stor_info_no; i++) { 671 di = ub_dev_get(stor_info[i]); 672 if (di != NULL) 673 stor_print_one(i, di, verbose); 674 } 675} 676