disk.c revision 185099
1/*- 2 * Copyright (c) 2008 Semihalf, Rafal Jaworowski 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28/* 29 * Block storage I/O routines for U-Boot 30 */ 31 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD: head/sys/boot/uboot/lib/disk.c 185099 2008-11-19 17:34:28Z raj $"); 34 35#include <sys/param.h> 36#include <sys/queue.h> 37#include <netinet/in.h> 38#include <machine/stdarg.h> 39#include <stand.h> 40#include <uuid.h> 41 42#define FSTYPENAMES 43#include <sys/disklabel.h> 44 45#include "api_public.h" 46#include "bootstrap.h" 47#include "glue.h" 48#include "libuboot.h" 49 50#define DEBUG 51#undef DEBUG 52 53#define stor_printf(fmt, args...) do { \ 54 printf("%s%d: ", dev->d_dev->dv_name, dev->d_unit); \ 55 printf(fmt, ##args); \ 56} while (0) 57 58#ifdef DEBUG 59#define debugf(fmt, args...) do { printf("%s(): ", __func__); \ 60 printf(fmt,##args); } while (0) 61#else 62#define debugf(fmt, args...) 63#endif 64 65struct gpt_part { 66 int gp_index; 67 uuid_t gp_type; 68 uint64_t gp_start; 69 uint64_t gp_end; 70}; 71 72struct open_dev { 73 int od_bsize; /* block size */ 74 int od_bstart; /* start block offset from beginning of disk */ 75 int od_type; 76#define OD_BSDLABEL 0x0001 77#define OD_GPT 0x0002 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 int stor_info[UB_MAX_DEV]; 94static int stor_info_no = 0; 95static int stor_opendev(struct open_dev **, struct uboot_devdesc *); 96static int stor_closedev(struct uboot_devdesc *); 97static int stor_readdev(struct uboot_devdesc *, daddr_t, size_t, char *); 98static int stor_open_count = 0; 99 100/* devsw I/F */ 101static int stor_init(void); 102static int stor_strategy(void *, int, daddr_t, size_t, char *, size_t *); 103static int stor_open(struct open_file *, ...); 104static int stor_close(struct open_file *); 105static void stor_print(int); 106 107struct devsw uboot_storage = { 108 "disk", 109 DEVT_DISK, 110 stor_init, 111 stor_strategy, 112 stor_open, 113 stor_close, 114 noioctl, 115 stor_print 116}; 117 118static int 119stor_init(void) 120{ 121 struct device_info *di; 122 int i, found = 0; 123 124 if (devs_no == 0) { 125 printf("No U-Boot devices! Really enumerated?\n"); 126 return (-1); 127 } 128 129 for (i = 0; i < devs_no; i++) { 130 di = ub_dev_get(i); 131 if ((di != NULL) && (di->type & DEV_TYP_STOR)) { 132 if (stor_info_no >= UB_MAX_DEV) { 133 printf("Too many storage devices: %d\n", 134 stor_info_no); 135 return (-1); 136 } 137 stor_info[stor_info_no++] = i; 138 found = 1; 139 } 140 } 141 142 if (!found) { 143 printf("No storage devices\n"); 144 return (-1); 145 } 146 147 debugf("storage devices found: %d\n", stor_info_no); 148 return (0); 149} 150 151static int 152stor_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, 153 size_t *rsize) 154{ 155 struct uboot_devdesc *dev = (struct uboot_devdesc *)devdata; 156 struct open_dev *od = (struct open_dev *)dev->d_disk.data; 157 int bcount, err; 158 159 debugf("od=%p, size=%d, bsize=%d\n", od, size, od->od_bsize); 160 161 if (rw != F_READ) { 162 stor_printf("write attempt, operation not supported!\n"); 163 return (EROFS); 164 } 165 166 if (size % od->od_bsize) { 167 stor_printf("size=%d not multiple of device block size=%d\n", 168 size, od->od_bsize); 169 return (EIO); 170 } 171 bcount = size / od->od_bsize; 172 173 if (rsize) 174 *rsize = 0; 175 176 err = stor_readdev(dev, blk + od->od_bstart, bcount, buf); 177 if (!err && rsize) 178 *rsize = size; 179 180 return (err); 181} 182 183static int 184stor_open(struct open_file *f, ...) 185{ 186 va_list ap; 187 struct open_dev *od; 188 struct uboot_devdesc *dev; 189 int err; 190 191 va_start(ap, f); 192 dev = va_arg(ap, struct uboot_devdesc *); 193 va_end(ap); 194 195 if ((err = stor_opendev(&od, dev)) != 0) 196 return (err); 197 198 ((struct uboot_devdesc *)(f->f_devdata))->d_disk.data = od; 199 200 return (0); 201} 202 203static int 204stor_close(struct open_file *f) 205{ 206 struct uboot_devdesc *dev; 207 208 dev = (struct uboot_devdesc *)(f->f_devdata); 209 210 return (stor_closedev(dev)); 211} 212 213static int 214stor_open_gpt(struct open_dev *od, struct uboot_devdesc *dev) 215{ 216 217 /* TODO */ 218 return (ENXIO); 219} 220 221static int 222stor_open_bsdlabel(struct open_dev *od, struct uboot_devdesc *dev) 223{ 224 char *buf; 225 struct disklabel *dl; 226 int err = 0; 227 228 /* Allocate 1 block */ 229 buf = malloc(od->od_bsize); 230 if (!buf) { 231 stor_printf("could not allocate memory for disklabel\n"); 232 return (ENOMEM); 233 } 234 235 /* Read disklabel */ 236 err = stor_readdev(dev, LABELSECTOR, 1, buf); 237 if (err) { 238 stor_printf("disklabel read error=%d\n", err); 239 err = ERDLAB; 240 goto out; 241 } 242 bcopy(buf + LABELOFFSET, &od->od_bsdlabel, sizeof(struct disklabel)); 243 dl = &od->od_bsdlabel; 244 245 if (dl->d_magic != DISKMAGIC) { 246 stor_printf("no disklabel magic!\n"); 247 err = EUNLAB; 248 goto out; 249 } 250 od->od_type = OD_BSDLABEL; 251 od->od_bstart = dl->d_partitions[dev->d_disk.partition].p_offset; 252 253 debugf("bstart=%d\n", od->od_bstart); 254 255out: 256 free(buf); 257 return (err); 258} 259 260static int 261stor_readdev(struct uboot_devdesc *dev, daddr_t blk, size_t size, char *buf) 262{ 263 lbasize_t real_size; 264 int err, handle; 265 266 debugf("reading size=%d @ 0x%08x\n", size, (uint32_t)buf); 267 268 handle = stor_info[dev->d_unit]; 269 err = ub_dev_read(handle, buf, size, blk, &real_size); 270 if (err != 0) { 271 stor_printf("read failed, error=%d\n", err); 272 return (EIO); 273 } 274 275 if (real_size != size) { 276 stor_printf("real size != size\n"); 277 err = EIO; 278 } 279 280 return (err); 281} 282 283 284static int 285stor_opendev(struct open_dev **odp, struct uboot_devdesc *dev) 286{ 287 struct device_info *di; 288 struct open_dev *od; 289 int err, h; 290 291 h = stor_info[dev->d_unit]; 292 293 debugf("refcount=%d\n", stor_open_count); 294 295 /* 296 * There can be recursive open calls from the infrastructure, but at 297 * U-Boot level open the device only the first time. 298 */ 299 if (stor_open_count > 0) 300 stor_open_count++; 301 else if ((err = ub_dev_open(h)) != 0) { 302 stor_printf("device open failed with error=%d, handle=%d\n", 303 err, h); 304 *odp = NULL; 305 return (ENXIO); 306 } 307 308 if ((di = ub_dev_get(h)) == NULL) 309 panic("could not retrieve U-Boot device_info, handle=%d", h); 310 311 if ((od = malloc(sizeof(struct open_dev))) == NULL) { 312 stor_printf("could not allocate memory for open_dev\n"); 313 return (ENOMEM); 314 } 315 od->od_bsize = di->di_stor.block_size; 316 od->od_bstart = 0; 317 od->od_type = 0; 318 319 if ((err = stor_open_gpt(od, dev)) != 0) 320 err = stor_open_bsdlabel(od, dev); 321 322 if (err != 0) 323 free(od); 324 else { 325 stor_open_count = 1; 326 *odp = od; 327 } 328 329 return (err); 330} 331 332static int 333stor_closedev(struct uboot_devdesc *dev) 334{ 335 int err, h; 336 337 free((struct open_dev *)dev->d_disk.data); 338 dev->d_disk.data = NULL; 339 340 if (--stor_open_count == 0) { 341 h = stor_info[dev->d_unit]; 342 if ((err = ub_dev_close(h)) != 0) { 343 stor_printf("device close failed with error=%d, " 344 "handle=%d\n", err, h); 345 return (ENXIO); 346 } 347 } 348 349 return (0); 350} 351 352/* Given a size in 512 byte sectors, convert it to a human-readable number. */ 353/* XXX stolen from sys/boot/i386/libi386/biosdisk.c, should really be shared */ 354static char * 355display_size(uint64_t size) 356{ 357 static char buf[80]; 358 char unit; 359 360 size /= 2; 361 unit = 'K'; 362 if (size >= 10485760000LL) { 363 size /= 1073741824; 364 unit = 'T'; 365 } else if (size >= 10240000) { 366 size /= 1048576; 367 unit = 'G'; 368 } else if (size >= 10000) { 369 size /= 1024; 370 unit = 'M'; 371 } 372 sprintf(buf, "%.6ld%cB", (long)size, unit); 373 return (buf); 374} 375 376static void 377stor_print_bsdlabel(struct uboot_devdesc *dev, char *prefix, int verbose) 378{ 379 char buf[512], line[80]; 380 struct disklabel *dl; 381 uint32_t off, size; 382 int err, i, t; 383 384 /* Read disklabel */ 385 err = stor_readdev(dev, LABELSECTOR, 1, buf); 386 if (err) { 387 sprintf(line, "%s%d: disklabel read error=%d\n", 388 dev->d_dev->dv_name, dev->d_unit, err); 389 pager_output(line); 390 return; 391 } 392 dl = (struct disklabel *)buf; 393 394 if (dl->d_magic != DISKMAGIC) { 395 sprintf(line, "%s%d: no disklabel magic!\n", 396 dev->d_dev->dv_name, dev->d_unit); 397 pager_output(line); 398 return; 399 } 400 401 /* Print partitions info */ 402 for (i = 0; i < dl->d_npartitions; i++) { 403 if ((t = dl->d_partitions[i].p_fstype) < FSMAXTYPES) { 404 405 off = dl->d_partitions[i].p_offset; 406 size = dl->d_partitions[i].p_size; 407 if (fstypenames[t] == NULL || size == 0) 408 continue; 409 410 if ((('a' + i) == 'c') && (!verbose)) 411 continue; 412 413 sprintf(line, " %s%c: %s %s (%d - %d)\n", prefix, 414 'a' + i, fstypenames[t], display_size(size), 415 off, off + size); 416 417 pager_output(line); 418 } 419 } 420} 421 422static void 423stor_print_one(int i, struct device_info *di, int verbose) 424{ 425 struct uboot_devdesc dev; 426 struct open_dev *od; 427 char line[80]; 428 429 sprintf(line, "\tdisk%d (%s)\n", i, ub_stor_type(di->type)); 430 pager_output(line); 431 432 dev.d_dev = &uboot_storage; 433 dev.d_unit = i; 434 dev.d_disk.partition = -1; 435 dev.d_disk.data = NULL; 436 437 if (stor_opendev(&od, &dev) == 0) { 438 dev.d_disk.data = od; 439 440 if (od->od_type == OD_GPT) { 441 /* TODO */ 442 443 } else if (od->od_type == OD_BSDLABEL) { 444 sprintf(line, "\t\tdisk%d", i); 445 stor_print_bsdlabel(&dev, line, verbose); 446 } 447 448 stor_closedev(&dev); 449 } 450} 451 452static void 453stor_print(int verbose) 454{ 455 struct device_info *di; 456 int i; 457 458 for (i = 0; i < stor_info_no; i++) { 459 di = ub_dev_get(stor_info[i]); 460 if (di != NULL) 461 stor_print_one(i, di, verbose); 462 } 463} 464