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