1 /* $NetBSD: ofdev.c,v 1.26 2010/02/17 15:50:06 eeh Exp $ */ 2 3 /* 4 * Copyright (C) 1995, 1996 Wolfgang Solfrank. 5 * Copyright (C) 1995, 1996 TooLs GmbH. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by TooLs GmbH. 19 * 4. The name of TooLs GmbH may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 28 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 /* 34 * Device I/O routines using Open Firmware 35 */ 36 #include <sys/param.h> 37 #include <sys/disklabel.h> 38 #ifdef NETBOOT 39 #include <netinet/in.h> 40 #endif 41 42 #include <lib/libsa/stand.h> 43 #include <lib/libsa/ufs.h> 44 #include <lib/libsa/lfs.h> 45 #include <lib/libsa/cd9660.h> 46 #ifdef NETBOOT 47 #include <lib/libsa/nfs.h> 48 #include <lib/libsa/tftp.h> 49 #endif 50 #include <lib/libkern/libkern.h> 51 52 #include <dev/sun/disklabel.h> 53 #include <dev/raidframe/raidframevar.h> 54 55 #include <machine/promlib.h> 56 57 #include "ofdev.h" 58 #include "boot.h" 59 60 extern char bootdev[]; 61 62 /* 63 * This is ugly. A path on a sparc machine is something like this: 64 * 65 * [device] [-<options] [path] [-options] [otherstuff] [-<more options] 66 * 67 */ 68 69 char * 70 filename(char *str, char *ppart) 71 { 72 char *cp, *lp; 73 char savec; 74 int dhandle; 75 char devtype[16]; 76 77 lp = str; 78 devtype[0] = 0; 79 *ppart = '\0'; 80 for (cp = str; *cp; lp = cp) { 81 /* For each component of the path name... */ 82 while (*++cp && *cp != '/'); 83 savec = *cp; 84 *cp = 0; 85 /* ...look whether there is a device with this name */ 86 dhandle = prom_finddevice(str); 87 DPRINTF(("filename: prom_finddevice(%s) returned %x\n", 88 str, dhandle)); 89 *cp = savec; 90 if (dhandle == -1) { 91 /* 92 * if not, lp is the delimiter between device and 93 * path. if the last component was a block device. 94 */ 95 if (!strcmp(devtype, "block")) { 96 /* search for arguments */ 97 DPRINTF(("filename: hunting for arguments " 98 "in %s\n", lp)); 99 for (cp = lp; ; ) { 100 cp--; 101 if (cp < str || 102 cp[0] == '/' || 103 (cp[0] == ' ' && (cp+1) != lp && 104 cp[1] == '-')) 105 break; 106 } 107 if (cp >= str && *cp == '-') 108 /* found arguments, make firmware 109 ignore them */ 110 *cp = 0; 111 for (cp = lp; *--cp && *cp != ',' 112 && *cp != ':';) 113 ; 114 if (cp[0] == ':' && cp[1] >= 'a' && 115 cp[1] <= 'a' + MAXPARTITIONS) { 116 *ppart = cp[1]; 117 cp[0] = '\0'; 118 } 119 } 120 DPRINTF(("filename: found %s\n",lp)); 121 return lp; 122 } else if (_prom_getprop(dhandle, "device_type", devtype, 123 sizeof devtype) < 0) 124 devtype[0] = 0; 125 } 126 DPRINTF(("filename: not found\n",lp)); 127 return 0; 128 } 129 130 static int 131 strategy(void *devdata, int rw, daddr_t blk, size_t size, void *buf, size_t *rsize) 132 { 133 struct of_dev *dev = devdata; 134 u_quad_t pos; 135 int n; 136 137 if (rw != F_READ) 138 return EPERM; 139 if (dev->type != OFDEV_DISK) 140 panic("strategy"); 141 142 #ifdef NON_DEBUG 143 printf("strategy: block %lx, partition offset %lx, blksz %lx\n", 144 (long)blk, (long)dev->partoff, (long)dev->bsize); 145 printf("strategy: seek position should be: %lx\n", 146 (long)((blk + dev->partoff) * dev->bsize)); 147 #endif 148 pos = (u_quad_t)(blk + dev->partoff) * dev->bsize; 149 150 for (;;) { 151 #ifdef NON_DEBUG 152 printf("strategy: seeking to %lx\n", (long)pos); 153 #endif 154 if (prom_seek(dev->handle, pos) < 0) 155 break; 156 #ifdef NON_DEBUG 157 printf("strategy: reading %lx at %p\n", (long)size, buf); 158 #endif 159 n = prom_read(dev->handle, buf, size); 160 if (n == -2) 161 continue; 162 if (n < 0) 163 break; 164 *rsize = n; 165 return 0; 166 } 167 return EIO; 168 } 169 170 static int 171 devclose(struct open_file *of) 172 { 173 struct of_dev *op = of->f_devdata; 174 175 #ifdef NETBOOT 176 if (op->type == OFDEV_NET) 177 net_close(op); 178 #endif 179 prom_close(op->handle); 180 op->handle = -1; 181 } 182 183 static struct devsw ofdevsw[1] = { 184 "OpenFirmware", 185 strategy, 186 (int (*)(struct open_file *, ...))nodev, 187 devclose, 188 noioctl 189 }; 190 int ndevs = sizeof ofdevsw / sizeof ofdevsw[0]; 191 192 193 #ifdef SPARC_BOOT_UFS 194 static struct fs_ops file_system_ufs[] = 195 { FS_OPS(ufs), FS_OPS(ffsv2), FS_OPS(lfsv1), FS_OPS(lfsv2) }; 196 #endif 197 #ifdef SPARC_BOOT_CD9660 198 static struct fs_ops file_system_cd9660 = FS_OPS(cd9660); 199 #endif 200 #ifdef NETBOOT 201 static struct fs_ops file_system_nfs = FS_OPS(nfs); 202 static struct fs_ops file_system_tftp = FS_OPS(tftp); 203 #endif 204 205 struct fs_ops file_system[7]; 206 int nfsys; 207 208 static struct of_dev ofdev = { 209 -1, 210 }; 211 212 char opened_name[256]; 213 int floppyboot; 214 215 static u_long 216 get_long(const void *p) 217 { 218 const unsigned char *cp = p; 219 220 return cp[0] | (cp[1] << 8) | (cp[2] << 16) | (cp[3] << 24); 221 } 222 /************************************************************************ 223 * 224 * The rest of this was taken from arch/sparc64/scsi/sun_disklabel.c 225 * and then substantially rewritten by Gordon W. Ross 226 * 227 ************************************************************************/ 228 229 /* What partition types to assume for Sun disklabels: */ 230 static u_char 231 sun_fstypes[8] = { 232 FS_BSDFFS, /* a */ 233 FS_SWAP, /* b */ 234 FS_OTHER, /* c - whole disk */ 235 FS_BSDFFS, /* d */ 236 FS_BSDFFS, /* e */ 237 FS_BSDFFS, /* f */ 238 FS_BSDFFS, /* g */ 239 FS_BSDFFS, /* h */ 240 }; 241 242 /* 243 * Given a SunOS disk label, set lp to a BSD disk label. 244 * Returns NULL on success, else an error string. 245 * 246 * The BSD label is cleared out before this is called. 247 */ 248 static char * 249 disklabel_sun_to_bsd(char *cp, struct disklabel *lp) 250 { 251 struct sun_disklabel *sl; 252 struct partition *npp; 253 struct sun_dkpart *spp; 254 int i, secpercyl; 255 u_short cksum, *sp1, *sp2; 256 257 sl = (struct sun_disklabel *)cp; 258 259 /* Verify the XOR check. */ 260 sp1 = (u_short *)sl; 261 sp2 = (u_short *)(sl + 1); 262 cksum = 0; 263 while (sp1 < sp2) 264 cksum ^= *sp1++; 265 if (cksum != 0) 266 return("SunOS disk label, bad checksum"); 267 268 /* Format conversion. */ 269 lp->d_magic = DISKMAGIC; 270 lp->d_magic2 = DISKMAGIC; 271 memcpy(lp->d_packname, sl->sl_text, sizeof(lp->d_packname)); 272 273 lp->d_secsize = 512; 274 lp->d_nsectors = sl->sl_nsectors; 275 lp->d_ntracks = sl->sl_ntracks; 276 lp->d_ncylinders = sl->sl_ncylinders; 277 278 secpercyl = sl->sl_nsectors * sl->sl_ntracks; 279 lp->d_secpercyl = secpercyl; 280 lp->d_secperunit = secpercyl * sl->sl_ncylinders; 281 282 lp->d_sparespercyl = sl->sl_sparespercyl; 283 lp->d_acylinders = sl->sl_acylinders; 284 lp->d_rpm = sl->sl_rpm; 285 lp->d_interleave = sl->sl_interleave; 286 287 lp->d_npartitions = 8; 288 /* These are as defined in <ufs/ffs/fs.h> */ 289 lp->d_bbsize = 8192; /* XXX */ 290 lp->d_sbsize = 8192; /* XXX */ 291 292 for (i = 0; i < 8; i++) { 293 spp = &sl->sl_part[i]; 294 npp = &lp->d_partitions[i]; 295 npp->p_offset = spp->sdkp_cyloffset * secpercyl; 296 npp->p_size = spp->sdkp_nsectors; 297 DPRINTF(("partition %d start %x size %x\n", i, (int)npp->p_offset, (int)npp->p_size)); 298 if (npp->p_size == 0) { 299 npp->p_fstype = FS_UNUSED; 300 } else { 301 npp->p_fstype = sun_fstypes[i]; 302 if (npp->p_fstype == FS_BSDFFS) { 303 /* 304 * The sun label does not store the FFS fields, 305 * so just set them with default values here. 306 */ 307 npp->p_fsize = 1024; 308 npp->p_frag = 8; 309 npp->p_cpg = 16; 310 } 311 } 312 } 313 314 lp->d_checksum = 0; 315 lp->d_checksum = dkcksum(lp); 316 DPRINTF(("disklabel_sun_to_bsd: success!\n")); 317 return (NULL); 318 } 319 320 /* 321 * Find a valid disklabel. 322 */ 323 static char * 324 search_label(struct of_dev *devp, u_long off, char *buf, 325 struct disklabel *lp, u_long off0) 326 { 327 size_t read; 328 struct mbr_partition *p; 329 int i; 330 u_long poff; 331 static int recursion; 332 333 struct disklabel *dlp; 334 struct sun_disklabel *slp; 335 int error; 336 337 /* minimal requirements for archtypal disk label */ 338 if (lp->d_secperunit == 0) 339 lp->d_secperunit = 0x1fffffff; 340 lp->d_npartitions = 1; 341 if (lp->d_partitions[0].p_size == 0) 342 lp->d_partitions[0].p_size = 0x1fffffff; 343 lp->d_partitions[0].p_offset = 0; 344 345 if (strategy(devp, F_READ, LABELSECTOR, DEV_BSIZE, buf, &read) 346 || read != DEV_BSIZE) 347 return ("Cannot read label"); 348 /* Check for a NetBSD disk label. */ 349 dlp = (struct disklabel *) (buf + LABELOFFSET); 350 if (dlp->d_magic == DISKMAGIC) { 351 if (dkcksum(dlp)) 352 return ("NetBSD disk label corrupted"); 353 *lp = *dlp; 354 DPRINTF(("search_label: found NetBSD label\n")); 355 return (NULL); 356 } 357 358 /* Check for a Sun disk label (for PROM compatibility). */ 359 slp = (struct sun_disklabel *) buf; 360 if (slp->sl_magic == SUN_DKMAGIC) 361 return (disklabel_sun_to_bsd(buf, lp)); 362 363 364 memset(buf, 0, sizeof(buf)); 365 return ("no disk label"); 366 } 367 368 int 369 devopen(struct open_file *of, const char *name, char **file) 370 { 371 char *cp; 372 char partition; 373 char fname[256], devname[256]; 374 union { 375 char buf[DEV_BSIZE]; 376 struct disklabel label; 377 } b; 378 struct disklabel label; 379 int handle, part, try = 0; 380 size_t read; 381 char *errmsg = NULL, *pp, savedpart = 0; 382 int error = 0; 383 384 if (ofdev.handle != -1) 385 panic("devopen"); 386 if (of->f_flags != F_READ) 387 return EPERM; 388 DPRINTF(("devopen: you want %s\n", name)); 389 strcpy(fname, name); 390 cp = filename(fname, &partition); 391 if (cp) { 392 strcpy(b.buf, cp); 393 *cp = 0; 394 } 395 if (!cp || !b.buf[0]) 396 strcpy(b.buf, DEFAULT_KERNEL); 397 if (!*fname) 398 strcpy(fname, bootdev); 399 strcpy(opened_name, fname); 400 if (partition) { 401 cp = opened_name + strlen(opened_name); 402 *cp++ = ':'; 403 *cp++ = partition; 404 *cp = 0; 405 } 406 *file = opened_name + strlen(opened_name); 407 if (b.buf[0] != '/') 408 strcat(opened_name, "/"); 409 strcat(opened_name, b.buf); 410 DPRINTF(("devopen: trying %s\n", fname)); 411 if ((handle = prom_finddevice(fname)) == -1) 412 return ENOENT; 413 DPRINTF(("devopen: found %s\n", fname)); 414 if (_prom_getprop(handle, "name", b.buf, sizeof b.buf) < 0) 415 return ENXIO; 416 DPRINTF(("devopen: %s is called %s\n", fname, b.buf)); 417 floppyboot = !strcmp(b.buf, "floppy"); 418 if (_prom_getprop(handle, "device_type", b.buf, sizeof b.buf) < 0) 419 return ENXIO; 420 DPRINTF(("devopen: %s is a %s device\n", fname, b.buf)); 421 if (!strcmp(b.buf, "block")) { 422 pp = strrchr(fname, ':'); 423 if (pp && pp[1] >= 'a' && pp[1] <= 'f' && pp[2] == 0) { 424 savedpart = pp[1]; 425 } else { 426 savedpart = 'a'; 427 handle = prom_open(fname); 428 if (handle != -1) { 429 OF_instance_to_path(handle, devname, 430 sizeof(devname)); 431 DPRINTF(("real path: %s\n", devname)); 432 prom_close(handle); 433 pp = devname + strlen(devname); 434 if (pp > devname + 3) pp -= 2; 435 if (pp[0] == ':') 436 savedpart = pp[1]; 437 } 438 pp = fname + strlen(fname); 439 pp[0] = ':'; 440 pp[2] = '\0'; 441 } 442 pp[1] = 'c'; 443 DPRINTF(("devopen: replacing by whole disk device %s\n", 444 fname)); 445 if (savedpart) 446 partition = savedpart; 447 } 448 449 open_again: 450 DPRINTF(("devopen: opening %s\n", fname)); 451 if ((handle = prom_open(fname)) == -1) { 452 DPRINTF(("devopen: open of %s failed\n", fname)); 453 if (pp && savedpart) { 454 if (try == 0) { 455 pp[0] = '\0'; 456 try = 1; 457 } else { 458 pp[0] = ':'; 459 pp[1] = savedpart; 460 pp = NULL; 461 savedpart = '\0'; 462 } 463 goto open_again; 464 } 465 return ENXIO; 466 } 467 DPRINTF(("devopen: %s is now open\n", fname)); 468 memset(&ofdev, 0, sizeof ofdev); 469 ofdev.handle = handle; 470 if (!strcmp(b.buf, "block")) { 471 ofdev.type = OFDEV_DISK; 472 ofdev.bsize = DEV_BSIZE; 473 /* First try to find a disklabel without MBR partitions */ 474 DPRINTF(("devopen: trying to read disklabel\n")); 475 if (strategy(&ofdev, F_READ, 476 LABELSECTOR, DEV_BSIZE, b.buf, &read) != 0 477 || read != DEV_BSIZE 478 || (errmsg = getdisklabel(b.buf, &label))) { 479 if (errmsg) printf("devopen: getdisklabel returned %s\n", errmsg); 480 /* Else try MBR partitions */ 481 errmsg = search_label(&ofdev, 0, b.buf, &label, 0); 482 if (errmsg) { 483 printf("devopen: search_label returned %s\n", errmsg); 484 error = ERDLAB; 485 } 486 if (error && error != ERDLAB) 487 goto bad; 488 } 489 490 if (error == ERDLAB) { 491 /* No, label, just use complete disk */ 492 ofdev.partoff = 0; 493 if (pp && savedpart) { 494 pp[1] = savedpart; 495 prom_close(handle); 496 if ((handle = prom_open(fname)) == -1) { 497 DPRINTF(("devopen: open of %s failed\n", 498 fname)); 499 return ENXIO; 500 } 501 ofdev.handle = handle; 502 DPRINTF(("devopen: back to original device %s\n", 503 fname)); 504 } 505 } else { 506 part = partition ? partition - 'a' : 0; 507 ofdev.partoff = label.d_partitions[part].p_offset; 508 DPRINTF(("devopen: setting partition %d offset %x\n", 509 part, ofdev.partoff)); 510 if (label.d_partitions[part].p_fstype == FS_RAID) { 511 ofdev.partoff += RF_PROTECTED_SECTORS; 512 DPRINTF(("devopen: found RAID partition, " 513 "adjusting offset to %x\n", ofdev.partoff)); 514 } 515 } 516 517 nfsys = 0; 518 of->f_dev = ofdevsw; 519 of->f_devdata = &ofdev; 520 #ifdef SPARC_BOOT_UFS 521 memcpy(&file_system[nfsys++], &file_system_ufs[0], sizeof file_system[0]); 522 memcpy(&file_system[nfsys++], &file_system_ufs[1], sizeof file_system[0]); 523 memcpy(&file_system[nfsys++], &file_system_ufs[2], sizeof file_system[0]); 524 memcpy(&file_system[nfsys++], &file_system_ufs[3], sizeof file_system[0]); 525 #endif 526 #ifdef SPARC_BOOT_CD9660 527 memcpy(&file_system[nfsys++], &file_system_cd9660, sizeof file_system[0]); 528 #endif 529 DPRINTF(("devopen: return 0\n")); 530 return 0; 531 } 532 #ifdef NETBOOT 533 if (!strcmp(b.buf, "network")) { 534 if (error = net_open(&ofdev)) 535 goto bad; 536 537 ofdev.type = OFDEV_NET; 538 of->f_dev = ofdevsw; 539 of->f_devdata = &ofdev; 540 541 if (!strncmp(*file,"/tftp:",6)) { 542 *file += 6; 543 memcpy(&file_system[0], &file_system_tftp, sizeof file_system[0]); 544 if (net_tftp_bootp(&of->f_devdata)) { 545 net_close(&ofdev); 546 goto bad; 547 } 548 } else { 549 memcpy(&file_system[0], &file_system_nfs, sizeof file_system[0]); 550 if (error = net_mountroot()) { 551 net_close(&ofdev); 552 goto bad; 553 } 554 } 555 nfsys = 1; 556 return 0; 557 } 558 #endif 559 error = EFTYPE; 560 bad: 561 DPRINTF(("devopen: error %d, cannot open device\n", error)); 562 prom_close(handle); 563 ofdev.handle = -1; 564 return error; 565 } 566