1 /* $NetBSD: ofdev.c,v 1.23 2010/10/17 15:33:04 phx 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 37 #include "ofdev.h" 38 #include "openfirm.h" 39 40 #include <sys/param.h> 41 #include <sys/disklabel.h> 42 #include <sys/bootblock.h> 43 44 #include <netinet/in.h> 45 46 #include <lib/libkern/libkern.h> 47 48 #include <lib/libsa/stand.h> 49 #include <lib/libsa/byteorder.h> 50 #include <lib/libsa/cd9660.h> 51 #include <lib/libsa/nfs.h> 52 #include <lib/libsa/ufs.h> 53 #include <lib/libsa/lfs.h> 54 #include <lib/libsa/ustarfs.h> 55 56 #include "hfs.h" 57 #include "net.h" 58 59 #ifdef DEBUG 60 # define DPRINTF printf 61 #else 62 # define DPRINTF while (0) printf 63 #endif 64 65 static int 66 strategy(void *devdata, int rw, daddr_t blk, size_t size, void *buf, 67 size_t *rsize) 68 { 69 struct of_dev *dev = devdata; 70 u_quad_t pos; 71 int n; 72 73 if (rw != F_READ) 74 return EPERM; 75 if (dev->type != OFDEV_DISK) 76 panic("strategy"); 77 78 pos = (u_quad_t)(blk + dev->partoff) * dev->bsize; 79 80 for (;;) { 81 if (OF_seek(dev->handle, pos) < 0) 82 break; 83 n = OF_read(dev->handle, buf, size); 84 if (n == -2) 85 continue; 86 if (n < 0) 87 break; 88 *rsize = n; 89 return 0; 90 } 91 return EIO; 92 } 93 94 static int 95 devopen_dummy(struct open_file *of, ...) { 96 return -1; 97 } 98 99 static int 100 devclose(struct open_file *of) 101 { 102 struct of_dev *op = of->f_devdata; 103 104 if (op->type == OFDEV_NET) 105 net_close(op); 106 OF_call_method("dma-free", op->handle, 2, 0, op->dmabuf, MAXPHYS); 107 OF_close(op->handle); 108 op->handle = -1; 109 return 0; 110 } 111 112 static struct devsw of_devsw[1] = { 113 { "OpenFirmware", strategy, devopen_dummy, devclose, noioctl } 114 }; 115 int ndevs = sizeof of_devsw / sizeof of_devsw[0]; 116 117 static struct fs_ops file_system_ffsv1 = FS_OPS(ffsv1); 118 static struct fs_ops file_system_ffsv2 = FS_OPS(ffsv2); 119 static struct fs_ops file_system_lfsv1 = FS_OPS(lfsv1); 120 static struct fs_ops file_system_lfsv2 = FS_OPS(lfsv2); 121 static struct fs_ops file_system_hfs = FS_OPS(hfs); 122 static struct fs_ops file_system_ustarfs = FS_OPS(ustarfs); 123 static struct fs_ops file_system_cd9660 = FS_OPS(cd9660); 124 static struct fs_ops file_system_nfs = FS_OPS(nfs); 125 126 struct fs_ops file_system[8]; 127 int nfsys; 128 129 static struct of_dev ofdev = { 130 -1, 131 }; 132 133 char opened_name[MAXBOOTPATHLEN]; 134 135 /* 136 * Check if this APM partition is a suitable root partition and return 137 * its file system type or zero. 138 */ 139 static u_int8_t 140 check_apm_root(struct part_map_entry *part, int *clust) 141 { 142 struct blockzeroblock *bzb; 143 char typestr[32], *s; 144 u_int8_t fstype; 145 146 *clust = 0; /* may become 1 for A/UX partitions */ 147 fstype = 0; 148 bzb = (struct blockzeroblock *)(&part->pmBootArgs); 149 150 /* convert partition type name to upper case */ 151 strncpy(typestr, (char *)part->pmPartType, sizeof(typestr)); 152 typestr[sizeof(typestr) - 1] = '\0'; 153 for (s = typestr; *s; s++) 154 if ((*s >= 'a') && (*s <= 'z')) 155 *s = (*s - 'a' + 'A'); 156 157 if (strcmp(PART_TYPE_NBSD_PPCBOOT, typestr) == 0) { 158 if ((bzb->bzbMagic == BZB_MAGIC) && 159 (bzb->bzbType < FSMAXTYPES)) 160 fstype = bzb->bzbType; 161 else 162 fstype = FS_BSDFFS; 163 } else if (strcmp(PART_TYPE_UNIX, typestr) == 0 && 164 bzb->bzbMagic == BZB_MAGIC && (bzb->bzbFlags & BZB_ROOTFS)) { 165 *clust = bzb->bzbCluster; 166 fstype = FS_BSDFFS; 167 } 168 169 return fstype; 170 } 171 172 /* 173 * Build a disklabel from APM partitions. 174 * We will just look for a suitable root partition and insert it into 175 * the 'a' slot. Should be sufficient to boot a kernel from it. 176 */ 177 static int 178 search_mac_label(struct of_dev *devp, char *buf, struct disklabel *lp) 179 { 180 struct part_map_entry *pme; 181 struct partition *a_part; 182 size_t nread; 183 int blkno, clust, lastblk, lastclust; 184 u_int8_t fstype; 185 186 pme = (struct part_map_entry *)buf; 187 a_part = &lp->d_partitions[0]; /* disklabel 'a' partition */ 188 lastclust = -1; 189 190 for (blkno = lastblk = 1; blkno <= lastblk; blkno++) { 191 if (strategy(devp, F_READ, blkno, DEV_BSIZE, pme, &nread) 192 || nread != DEV_BSIZE) 193 return ERDLAB; 194 if (pme->pmSig != PART_ENTRY_MAGIC || 195 pme->pmPartType[0] == '\0') 196 break; 197 lastblk = pme->pmMapBlkCnt; 198 199 fstype = check_apm_root(pme, &clust); 200 201 if (fstype && (lastclust == -1 || clust < lastclust)) { 202 a_part->p_size = pme->pmPartBlkCnt; 203 a_part->p_offset = pme->pmPyPartStart; 204 a_part->p_fstype = fstype; 205 if ((lastclust = clust) == 0) 206 break; /* we won't find a better match */ 207 } 208 } 209 if (lastclust < 0) 210 return ERDLAB; /* no root partition found */ 211 212 /* pretend we only have partitions 'a', 'b' and 'c' */ 213 lp->d_npartitions = RAW_PART + 1; 214 return 0; 215 } 216 217 static u_long 218 get_long(const void *p) 219 { 220 const unsigned char *cp = p; 221 222 return cp[0] | (cp[1] << 8) | (cp[2] << 16) | (cp[3] << 24); 223 } 224 225 /* 226 * Find a valid disklabel from MBR partitions. 227 */ 228 static int 229 search_dos_label(struct of_dev *devp, u_long off, char *buf, 230 struct disklabel *lp, u_long off0) 231 { 232 size_t nread; 233 struct mbr_partition *p; 234 int i; 235 u_long poff; 236 static int recursion; 237 238 if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &nread) 239 || nread != DEV_BSIZE) 240 return ERDLAB; 241 242 if (*(u_int16_t *)&buf[MBR_MAGIC_OFFSET] != sa_htole16(MBR_MAGIC)) 243 return ERDLAB; 244 245 if (recursion++ <= 1) 246 off0 += off; 247 for (p = (struct mbr_partition *)(buf + MBR_PART_OFFSET), i = 4; 248 --i >= 0; p++) { 249 if (p->mbrp_type == MBR_PTYPE_NETBSD 250 #ifdef COMPAT_386BSD_MBRPART 251 || (p->mbrp_type == MBR_PTYPE_386BSD && 252 (printf("WARNING: old BSD partition ID!\n"), 1) 253 /* XXX XXX - libsa printf() is void */ ) 254 #endif 255 ) { 256 poff = get_long(&p->mbrp_start) + off0; 257 if (strategy(devp, F_READ, poff + 1, 258 DEV_BSIZE, buf, &nread) == 0 259 && nread == DEV_BSIZE) { 260 if (!getdisklabel(buf, lp)) { 261 recursion--; 262 return 0; 263 } 264 } 265 if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &nread) 266 || nread != DEV_BSIZE) { 267 recursion--; 268 return ERDLAB; 269 } 270 } else if (p->mbrp_type == MBR_PTYPE_EXT) { 271 poff = get_long(&p->mbrp_start); 272 if (!search_dos_label(devp, poff, buf, lp, off0)) { 273 recursion--; 274 return 0; 275 } 276 if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &nread) 277 || nread != DEV_BSIZE) { 278 recursion--; 279 return ERDLAB; 280 } 281 } 282 } 283 recursion--; 284 return ERDLAB; 285 } 286 287 bool 288 parsefilepath(const char *path, char *devname, char *fname, char *ppart) 289 { 290 char *cp, *lp; 291 char savec; 292 int dhandle; 293 char str[MAXBOOTPATHLEN]; 294 char devtype[16]; 295 296 DPRINTF("%s: path = %s\n", __func__, path); 297 298 devtype[0] = '\0'; 299 300 if (devname != NULL) 301 devname[0] = '\0'; 302 if (fname != NULL) 303 fname[0] = '\0'; 304 if (ppart != NULL) 305 *ppart = 0; 306 307 strlcpy(str, path, sizeof(str)); 308 lp = str; 309 for (cp = str; *cp != '\0'; lp = cp) { 310 /* For each component of the path name... */ 311 while (*++cp != '\0' && *cp != '/') 312 continue; 313 savec = *cp; 314 *cp = '\0'; 315 /* ...look whether there is a device with this name */ 316 dhandle = OF_finddevice(str); 317 DPRINTF("%s: Checking %s: dhandle = %d\n", 318 __func__, str, dhandle); 319 *cp = savec; 320 if (dhandle != -1) { 321 /* 322 * If it's a vaild device, lp is a delimiter 323 * in the OF device path. 324 */ 325 if (OF_getprop(dhandle, "device_type", devtype, 326 sizeof devtype) < 0) 327 devtype[0] = '\0'; 328 continue; 329 } 330 331 /* 332 * If not, lp is the delimiter between OF device path 333 * and the specified filename. 334 */ 335 336 /* Check if the last component was a block device... */ 337 if (strcmp(devtype, "block") == 0) { 338 /* search for arguments */ 339 for (cp = lp; 340 --cp >= str && *cp != '/' && *cp != ':';) 341 continue; 342 if (cp >= str && *cp == ':') { 343 /* found arguments */ 344 for (cp = lp; 345 *--cp != ':' && *cp != ',';) 346 continue; 347 if (*++cp >= 'a' && 348 *cp <= 'a' + MAXPARTITIONS && 349 ppart != NULL) 350 *ppart = *cp; 351 } 352 } 353 if (*lp != '\0') { 354 /* set filename */ 355 DPRINTF("%s: filename = %s\n", __func__, lp); 356 if (fname != NULL) 357 strcpy(fname, lp); 358 if (str != lp) { 359 /* set device path */ 360 *lp = '\0'; 361 DPRINTF("%s: device path = %s\n", 362 __func__, str); 363 if (devname != NULL) 364 strcpy(devname, str); 365 } 366 } 367 return true; 368 } 369 370 DPRINTF("%s: invalid path?\n", __func__); 371 return false; 372 } 373 374 int 375 devopen(struct open_file *of, const char *name, char **file) 376 { 377 char *cp; 378 char partition; 379 char devname[MAXBOOTPATHLEN]; 380 char filename[MAXBOOTPATHLEN]; 381 char buf[DEV_BSIZE]; 382 struct disklabel label; 383 int handle, part; 384 size_t nread; 385 int error = 0; 386 387 if (ofdev.handle != -1) 388 panic("devopen"); 389 if (of->f_flags != F_READ) 390 return EPERM; 391 392 if (!parsefilepath(name, devname, filename, &partition)) 393 return ENOENT; 394 395 if (filename[0] == '\0') 396 /* no filename */ 397 return ENOENT; 398 399 if (devname[0] == '\0') 400 /* no device, use default bootdev */ 401 strlcpy(devname, bootdev, sizeof(devname)); 402 403 DPRINTF("%s: devname = %s, filename = %s\n", 404 __func__, devname, filename); 405 406 strlcpy(opened_name, devname, sizeof(opened_name)); 407 if (partition) { 408 cp = opened_name + strlen(opened_name); 409 *cp++ = ':'; 410 *cp++ = partition; 411 *cp = 0; 412 } 413 if (filename[0] != '/') 414 strlcat(opened_name, "/", sizeof(opened_name)); 415 strlcat(opened_name, filename, sizeof(opened_name)); 416 417 DPRINTF("%s: opened_name = %s\n", __func__, opened_name); 418 419 *file = opened_name + strlen(devname) + 1; 420 if ((handle = OF_finddevice(devname)) == -1) 421 return ENOENT; 422 if (OF_getprop(handle, "device_type", buf, sizeof buf) < 0) 423 return ENXIO; 424 if (!strcmp(buf, "block") && strrchr(devname, ':') == NULL) 425 /* indicate raw partition, when missing */ 426 strlcat(devname, ":0", sizeof(devname)); 427 if ((handle = OF_open(devname)) == -1) 428 return ENXIO; 429 memset(&ofdev, 0, sizeof ofdev); 430 ofdev.handle = handle; 431 ofdev.dmabuf = NULL; 432 OF_call_method("dma-alloc", handle, 1, 1, MAXPHYS, &ofdev.dmabuf); 433 if (!strcmp(buf, "block")) { 434 ofdev.type = OFDEV_DISK; 435 ofdev.bsize = DEV_BSIZE; 436 /* First try to find a disklabel without partitions */ 437 if (strategy(&ofdev, F_READ, 438 LABELSECTOR, DEV_BSIZE, buf, &nread) != 0 439 || nread != DEV_BSIZE 440 || getdisklabel(buf, &label)) { 441 /* Else try APM or MBR partitions */ 442 if (((struct drvr_map *)buf)->sbSig == DRIVER_MAP_MAGIC) 443 error = search_mac_label(&ofdev, buf, &label); 444 else 445 error = search_dos_label(&ofdev, 0, buf, 446 &label, 0); 447 if (error && error != ERDLAB) 448 goto bad; 449 } 450 451 if (error == ERDLAB) { 452 if (partition) 453 /* 454 * User specified a parititon, 455 * but there is none 456 */ 457 goto bad; 458 /* No label, just use complete disk */ 459 ofdev.partoff = 0; 460 } else { 461 part = partition ? partition - 'a' : 0; 462 ofdev.partoff = label.d_partitions[part].p_offset; 463 } 464 465 of->f_dev = of_devsw; 466 of->f_devdata = &ofdev; 467 file_system[0] = file_system_ffsv1; 468 file_system[1] = file_system_ffsv2; 469 file_system[2] = file_system_lfsv1; 470 file_system[3] = file_system_lfsv2; 471 file_system[4] = file_system_ustarfs; 472 file_system[5] = file_system_cd9660; 473 file_system[6] = file_system_hfs; 474 nfsys = 7; 475 return 0; 476 } 477 if (!strcmp(buf, "network")) { 478 ofdev.type = OFDEV_NET; 479 of->f_dev = of_devsw; 480 of->f_devdata = &ofdev; 481 file_system[0] = file_system_nfs; 482 nfsys = 1; 483 if ((error = net_open(&ofdev))) 484 goto bad; 485 return 0; 486 } 487 error = EFTYPE; 488 bad: 489 OF_close(handle); 490 ofdev.handle = -1; 491 return error; 492 } 493