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