Home | History | Annotate | Line # | Download | only in ofwboot
      1 /*	$NetBSD: ofdev.c,v 1.37 2017/09/15 13:25:34 martin 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 #include "net.h"
     60 
     61 extern char bootdev[];
     62 extern bool root_fs_quickseekable;
     63 
     64 struct btinfo_bootdev_unit bi_unit;
     65 bool bootinfo_pass_bootunit = false;
     66 
     67 /*
     68  * This is ugly.  A path on a sparc machine is something like this:
     69  *
     70  *	[device] [-<options] [path] [-options] [otherstuff] [-<more options]
     71  *
     72  */
     73 
     74 char *
     75 filename(char *str, char *ppart)
     76 {
     77 	char *cp, *lp;
     78 	char savec;
     79 	int dhandle;
     80 	char devtype[16];
     81 
     82 	lp = str;
     83 	devtype[0] = 0;
     84 	*ppart = '\0';
     85 	for (cp = str; *cp; lp = cp) {
     86 		/* For each component of the path name... */
     87 		while (*++cp && *cp != '/');
     88 		savec = *cp;
     89 		*cp = 0;
     90 		/* ...look whether there is a device with this name */
     91 		dhandle = prom_finddevice(str);
     92 		DPRINTF(("filename: prom_finddevice(%s) returned %x\n",
     93 		       str, dhandle));
     94 		*cp = savec;
     95 		if (dhandle == -1) {
     96 			/*
     97 			 * if not, lp is the delimiter between device and
     98 			 * path.  if the last component was a block device.
     99 			 */
    100 			if (strcmp(devtype, "block") == 0
    101 			    || strcmp(devtype, "scsi") == 0) {
    102 				/* search for arguments */
    103 				DPRINTF(("filename: hunting for arguments "
    104 				       "in %s\n", lp));
    105 				for (cp = lp; ; ) {
    106 					cp--;
    107 					if (cp < str ||
    108 					    cp[0] == '/' ||
    109 					    (cp[0] == ' ' && (cp+1) != lp &&
    110 					     cp[1] == '-'))
    111 						break;
    112 				}
    113 				if (cp >= str && *cp == '-')
    114 					/* found arguments, make firmware
    115 					   ignore them */
    116 					*cp = 0;
    117 				for (cp = lp; *--cp && *cp != ','
    118 					&& *cp != ':';)
    119 						;
    120 				if (cp[0] == ':' && cp[1] >= 'a' &&
    121 				    cp[1] <= 'a' + MAXPARTITIONS) {
    122 					*ppart = cp[1];
    123 					cp[0] = '\0';
    124 				}
    125 			}
    126 			DPRINTF(("filename: found %s\n",lp));
    127 			return lp;
    128 		} else if (_prom_getprop(dhandle, "device_type", devtype,
    129 				sizeof devtype) < 0)
    130 			devtype[0] = 0;
    131 	}
    132 	DPRINTF(("filename: not found\n"));
    133 	return 0;
    134 }
    135 
    136 static int
    137 strategy(void *devdata, int rw, daddr_t blk, size_t size, void *buf, size_t *rsize)
    138 {
    139 	struct of_dev *dev = devdata;
    140 	u_quad_t pos;
    141 	int n;
    142 
    143 	if (rw != F_READ)
    144 		return EPERM;
    145 	if (dev->type != OFDEV_DISK)
    146 		panic("strategy");
    147 
    148 #ifdef NON_DEBUG
    149 	printf("strategy: block %lx, partition offset %lx, blksz %lx\n",
    150 	       (long)blk, (long)dev->partoff, (long)dev->bsize);
    151 	printf("strategy: seek position should be: %lx\n",
    152 	       (long)((blk + dev->partoff) * dev->bsize));
    153 #endif
    154 	pos = (u_quad_t)(blk + dev->partoff) * dev->bsize;
    155 
    156 	for (;;) {
    157 #ifdef NON_DEBUG
    158 		printf("strategy: seeking to %lx\n", (long)pos);
    159 #endif
    160 		if (prom_seek(dev->handle, pos) < 0)
    161 			break;
    162 #ifdef NON_DEBUG
    163 		printf("strategy: reading %lx at %p\n", (long)size, buf);
    164 #endif
    165 		n = prom_read(dev->handle, buf, size);
    166 		if (n == -2)
    167 			continue;
    168 		if (n < 0)
    169 			break;
    170 		*rsize = n;
    171 		return 0;
    172 	}
    173 	return EIO;
    174 }
    175 
    176 static int
    177 devclose(struct open_file *of)
    178 {
    179 	struct of_dev *op = of->f_devdata;
    180 
    181 #ifdef NETBOOT
    182 	if (op->type == OFDEV_NET)
    183 		net_close(op);
    184 #endif
    185 	prom_close(op->handle);
    186 	op->handle = -1;
    187 	return 0;
    188 }
    189 
    190 static struct devsw ofdevsw[1] = {
    191 	{
    192 		"OpenFirmware",
    193 		strategy,
    194 		(int (*)(struct open_file *, ...))nodev,
    195 		devclose,
    196 		noioctl
    197 	}
    198 };
    199 int ndevs = sizeof ofdevsw / sizeof ofdevsw[0];
    200 
    201 
    202 #ifdef SPARC_BOOT_UFS
    203 static struct fs_ops file_system_ufs[] =
    204 { FS_OPS(ufs), FS_OPS(ffsv2), FS_OPS(lfsv1), FS_OPS(lfsv2) };
    205 #endif
    206 #ifdef SPARC_BOOT_CD9660
    207 static struct fs_ops file_system_cd9660 = FS_OPS(cd9660);
    208 #endif
    209 #ifdef NETBOOT
    210 static struct fs_ops file_system_nfs = FS_OPS(nfs);
    211 static struct fs_ops file_system_tftp = FS_OPS(tftp);
    212 #endif
    213 
    214 struct fs_ops file_system[7];
    215 int nfsys;
    216 
    217 static struct of_dev ofdev = {
    218 	-1,
    219 };
    220 
    221 char opened_name[256];
    222 int floppyboot;
    223 
    224 /************************************************************************
    225  *
    226  * The rest of this was taken from arch/sparc64/scsi/sun_disklabel.c
    227  * and then substantially rewritten by Gordon W. Ross
    228  *
    229  ************************************************************************/
    230 
    231 /* What partition types to assume for Sun disklabels: */
    232 static u_char
    233 sun_fstypes[8] = {
    234 	FS_BSDFFS,	/* a */
    235 	FS_SWAP,	/* b */
    236 	FS_OTHER,	/* c - whole disk */
    237 	FS_BSDFFS,	/* d */
    238 	FS_BSDFFS,	/* e */
    239 	FS_BSDFFS,	/* f */
    240 	FS_BSDFFS,	/* g */
    241 	FS_BSDFFS,	/* h */
    242 };
    243 
    244 /*
    245  * Given a SunOS disk label, set lp to a BSD disk label.
    246  * Returns NULL on success, else an error string.
    247  *
    248  * The BSD label is cleared out before this is called.
    249  */
    250 static char *
    251 disklabel_sun_to_bsd(char *cp, struct disklabel *lp)
    252 {
    253 	struct sun_disklabel *sl;
    254 	struct partition *npp;
    255 	struct sun_dkpart *spp;
    256 	int i, secpercyl;
    257 	u_short cksum, *sp1, *sp2;
    258 
    259 	sl = (struct sun_disklabel *)cp;
    260 
    261 	/* Verify the XOR check. */
    262 	sp1 = (u_short *)sl;
    263 	sp2 = (u_short *)(sl + 1);
    264 	cksum = 0;
    265 	while (sp1 < sp2)
    266 		cksum ^= *sp1++;
    267 	if (cksum != 0)
    268 		return("SunOS disk label, bad checksum");
    269 
    270 	/* Format conversion. */
    271 	lp->d_magic = DISKMAGIC;
    272 	lp->d_magic2 = DISKMAGIC;
    273 	memcpy(lp->d_packname, sl->sl_text, sizeof(lp->d_packname));
    274 
    275 	lp->d_secsize = 512;
    276 	lp->d_nsectors   = sl->sl_nsectors;
    277 	lp->d_ntracks    = sl->sl_ntracks;
    278 	lp->d_ncylinders = sl->sl_ncylinders;
    279 
    280 	secpercyl = sl->sl_nsectors * sl->sl_ntracks;
    281 	lp->d_secpercyl  = secpercyl;
    282 	lp->d_secperunit = secpercyl * sl->sl_ncylinders;
    283 
    284 	lp->d_sparespercyl = sl->sl_sparespercyl;
    285 	lp->d_acylinders   = sl->sl_acylinders;
    286 	lp->d_rpm          = sl->sl_rpm;
    287 	lp->d_interleave   = sl->sl_interleave;
    288 
    289 	lp->d_npartitions = 8;
    290 	/* These are as defined in <ufs/ffs/fs.h> */
    291 	lp->d_bbsize = 8192;	/* XXX */
    292 	lp->d_sbsize = 8192;	/* XXX */
    293 
    294 	for (i = 0; i < 8; i++) {
    295 		spp = &sl->sl_part[i];
    296 		npp = &lp->d_partitions[i];
    297 		npp->p_offset = spp->sdkp_cyloffset * secpercyl;
    298 		npp->p_size = spp->sdkp_nsectors;
    299 		DPRINTF(("partition %d start %x size %x\n", i, (int)npp->p_offset, (int)npp->p_size));
    300 		if (npp->p_size == 0) {
    301 			npp->p_fstype = FS_UNUSED;
    302 		} else {
    303 			npp->p_fstype = sun_fstypes[i];
    304 			if (npp->p_fstype == FS_BSDFFS) {
    305 				/*
    306 				 * The sun label does not store the FFS fields,
    307 				 * so just set them with default values here.
    308 				 */
    309 				npp->p_fsize = 1024;
    310 				npp->p_frag = 8;
    311 				npp->p_cpg = 16;
    312 			}
    313 		}
    314 	}
    315 
    316 	lp->d_checksum = 0;
    317 	lp->d_checksum = dkcksum(lp);
    318 	DPRINTF(("disklabel_sun_to_bsd: success!\n"));
    319 	return (NULL);
    320 }
    321 
    322 /*
    323  * Find a valid disklabel.
    324  */
    325 static char *
    326 search_label(struct of_dev *devp, u_long off, char *buf,
    327 	     struct disklabel *lp, u_long off0)
    328 {
    329 	size_t readsize;
    330 	struct disklabel *dlp;
    331 	struct sun_disklabel *slp;
    332 
    333 	/* minimal requirements for archtypal disk label */
    334 	if (lp->d_secperunit == 0)
    335 		lp->d_secperunit = 0x1fffffff;
    336 	lp->d_npartitions = 1;
    337 	if (lp->d_partitions[0].p_size == 0)
    338 		lp->d_partitions[0].p_size = 0x1fffffff;
    339 	lp->d_partitions[0].p_offset = 0;
    340 
    341 	if (strategy(devp, F_READ, LABELSECTOR, DEV_BSIZE, buf, &readsize)
    342 	    || readsize != DEV_BSIZE)
    343 		return ("Cannot read label");
    344 	/* Check for a NetBSD disk label. */
    345 	dlp = (struct disklabel *) (buf + LABELOFFSET);
    346 	if (dlp->d_magic == DISKMAGIC) {
    347 		if (dkcksum(dlp))
    348 			return ("NetBSD disk label corrupted");
    349 		*lp = *dlp;
    350 		DPRINTF(("search_label: found NetBSD label\n"));
    351 		return (NULL);
    352 	}
    353 
    354 	/* Check for a Sun disk label (for PROM compatibility). */
    355 	slp = (struct sun_disklabel *) buf;
    356 	if (slp->sl_magic == SUN_DKMAGIC)
    357 		return (disklabel_sun_to_bsd(buf, lp));
    358 
    359 
    360 	memset(buf, 0, DEV_BSIZE);
    361 	return ("no disk label");
    362 }
    363 
    364 static void
    365 device_target_unit(const char *dev, int ihandle)
    366 {
    367 	cell_t units[4], phandle, myself, depth = 0, odepth = 0, cnt;
    368 	char buf[256];
    369 
    370 	/* init the data passed to the kernel */
    371 	bootinfo_pass_bootunit = false;
    372 	memset(&bi_unit, 0, sizeof(bi_unit));
    373 
    374 	/* save old my-self value */
    375 	OF_interpret("my-self", 0, 1, &myself);
    376 	/* set our device as my-self */
    377 	OF_interpret("to my-self", 1, 0, HDL2CELL(ihandle));
    378 
    379 	/*
    380 	 * my-unit delivers a variable number of cells, we could
    381 	 * walk up the path and find a #address-cells value that
    382 	 * describes it, but it seems to just work this simple
    383 	 * way.
    384 	 */
    385 	OF_interpret("depth", 0, 1, &odepth);
    386 	OF_interpret("my-unit depth", 0, 5, &depth,
    387 	    &units[0], &units[1], &units[2], &units[3]);
    388 	cnt = depth-odepth;
    389 
    390 	/*
    391 	 * Old versions of QEMU's OpenBIOS have a bug in the
    392 	 * CIF implementation for instance_to_package, test
    393 	 * for that explicitly here and work around it if needed.
    394 	 */
    395 	phandle = OF_instance_to_package(ihandle);
    396 	OF_package_to_path(phandle, buf, sizeof(buf));
    397 	buf[sizeof(buf)-1] = 0;
    398 	if (strlen(buf) > 2 && strlen(dev) > 2 &&
    399 	    strncmp(buf, dev, strlen(buf)) != 0) {
    400 		DPRINTF(("OpenBIOS workaround: phandle %" PRIx32 "is %s, "
    401 		    "does not match %s\n", (uint32_t)phandle, buf, dev));
    402 		OF_interpret("my-self ihandle>non-interposed-phandle",
    403 		     0, 1, &phandle);
    404 		OF_package_to_path(phandle, buf, sizeof(buf));
    405 		DPRINTF(("new phandle %" PRIx32 " is %s\n",
    406 		    (uint32_t)phandle, buf));
    407 	}
    408 
    409 	bi_unit.phandle = phandle;
    410 	bi_unit.parent = OF_parent(phandle);
    411 	bi_unit.lun = units[cnt > 2 ? 3 : 1];
    412 	bi_unit.target = units[cnt > 2 ? 2 : 0];
    413 	if (cnt >= 4)
    414 		bi_unit.wwn = (uint64_t)units[0] << 32 | (uint32_t)units[1];
    415 	DPRINTF(("boot device package: %" PRIx32 ", parent: %" PRIx32
    416 	    ", lun: %" PRIu32 ", target: %" PRIu32 ", wwn: %" PRIx64 "\n",
    417 	    bi_unit.phandle, bi_unit.parent, bi_unit.lun, bi_unit.target,
    418 	    bi_unit.wwn));
    419 
    420 	/* restore my-self */
    421 	OF_interpret("to my-self", 1, 0, myself);
    422 
    423 	/* now that we have gatherd all the details, pass them to the kernel */
    424 	bootinfo_pass_bootunit = true;
    425 }
    426 
    427 int
    428 devopen(struct open_file *of, const char *name, char **file)
    429 {
    430 	char *cp;
    431 	char partition;
    432 	char fname[256], devname[256];
    433 	union {
    434 		char buf[DEV_BSIZE];
    435 		struct disklabel label;
    436 	} b;
    437 	struct disklabel label;
    438 	int handle, part, try = 0;
    439 	size_t readsize;
    440 	char *errmsg = NULL, *pp = NULL, savedpart = 0;
    441 	int error = 0;
    442 	bool get_target_unit = false;
    443 
    444 	if (ofdev.handle != -1)
    445 		panic("devopen: ofdev already in use");
    446 	if (of->f_flags != F_READ)
    447 		return EPERM;
    448 	DPRINTF(("devopen: you want %s\n", name));
    449 	strcpy(fname, name);
    450 	cp = filename(fname, &partition);
    451 	if (cp) {
    452 		strcpy(b.buf, cp);
    453 		*cp = 0;
    454 	}
    455 	if (!cp || !b.buf[0])
    456 		strcpy(b.buf, DEFAULT_KERNEL);
    457 	if (!*fname)
    458 		strcpy(fname, bootdev);
    459 	strcpy(opened_name, fname);
    460 	if (partition) {
    461 		cp = opened_name + strlen(opened_name);
    462 		*cp++ = ':';
    463 		*cp++ = partition;
    464 		*cp = 0;
    465 	}
    466 	*file = opened_name + strlen(opened_name);
    467 	if (b.buf[0] != '/')
    468 		strcat(opened_name, "/");
    469 	strcat(opened_name, b.buf);
    470 	DPRINTF(("devopen: trying %s\n", fname));
    471 	if ((handle = prom_finddevice(fname)) == -1)
    472 		return ENOENT;
    473 	DPRINTF(("devopen: found %s\n", fname));
    474 	if (_prom_getprop(handle, "name", b.buf, sizeof b.buf) < 0)
    475 		return ENXIO;
    476 	DPRINTF(("devopen: %s is called %s\n", fname, b.buf));
    477 	floppyboot = !strcmp(b.buf, "floppy");
    478 	if (_prom_getprop(handle, "device_type", b.buf, sizeof b.buf) < 0)
    479 		return ENXIO;
    480 	DPRINTF(("devopen: %s is a %s device\n", fname, b.buf));
    481 	if (strcmp(b.buf, "block") == 0 || strcmp(b.buf, "scsi") == 0) {
    482 
    483 		get_target_unit = true;
    484 
    485 		pp = strrchr(fname, ':');
    486 		if (pp && pp[1] >= 'a' && pp[1] <= 'f' && pp[2] == 0) {
    487 			savedpart = pp[1];
    488 		} else {
    489 			savedpart = 'a';
    490 			handle = prom_open(fname);
    491 			if (handle != -1) {
    492 				OF_instance_to_path(handle, devname,
    493 				    sizeof(devname));
    494 				DPRINTF(("real path: %s\n", devname));
    495 				prom_close(handle);
    496 				pp = devname + strlen(devname);
    497 				if (pp > devname + 3) pp -= 2;
    498 				if (pp[0] == ':')
    499 					savedpart = pp[1];
    500 			}
    501 			pp = fname + strlen(fname);
    502 			pp[0] = ':';
    503 			pp[2] = '\0';
    504 		}
    505 		pp[1] = 'c';
    506 		DPRINTF(("devopen: replacing by whole disk device %s\n",
    507 		    fname));
    508 		if (savedpart)
    509 			partition = savedpart;
    510 	}
    511 
    512 open_again:
    513 	DPRINTF(("devopen: opening %s\n", fname));
    514 	if ((handle = prom_open(fname)) == -1) {
    515 		DPRINTF(("devopen: open of %s failed\n", fname));
    516 		if (pp && savedpart) {
    517 			if (try == 0) {
    518 				pp[0] = '\0';
    519 				try = 1;
    520 			} else {
    521 				pp[0] = ':';
    522 				pp[1] = savedpart;
    523 				pp = NULL;
    524 				savedpart = '\0';
    525 			}
    526 			goto open_again;
    527 		}
    528 		return ENXIO;
    529 	}
    530 	DPRINTF(("devopen: %s is now open\n", fname));
    531 
    532 	if (get_target_unit)
    533 		device_target_unit(fname, handle);
    534 
    535 	memset(&ofdev, 0, sizeof ofdev);
    536 	ofdev.handle = handle;
    537 	if (strcmp(b.buf, "block") == 0 || strcmp(b.buf, "scsi") == 0) {
    538 		ofdev.type = OFDEV_DISK;
    539 		ofdev.bsize = DEV_BSIZE;
    540 		/* First try to find a disklabel without MBR partitions */
    541 		DPRINTF(("devopen: trying to read disklabel\n"));
    542 		if (strategy(&ofdev, F_READ,
    543 			     LABELSECTOR, DEV_BSIZE, b.buf, &readsize) != 0
    544 		    || readsize != DEV_BSIZE
    545 		    || (errmsg = getdisklabel(b.buf, &label))) {
    546 			if (errmsg) {
    547 				DPRINTF(("devopen: getdisklabel returned %s\n",
    548 					errmsg));
    549 			}
    550 			/* Else try MBR partitions */
    551 			errmsg = search_label(&ofdev, 0, b.buf, &label, 0);
    552 			if (errmsg) {
    553 				printf("devopen: search_label returned %s\n", errmsg);
    554 				error = ERDLAB;
    555 			}
    556 			if (error && error != ERDLAB)
    557 				goto bad;
    558 		}
    559 
    560 		if (error == ERDLAB) {
    561 			/* No, label, just use complete disk */
    562 			ofdev.partoff = 0;
    563 			if (pp && savedpart) {
    564 				pp[1] = savedpart;
    565 				prom_close(handle);
    566 				if ((handle = prom_open(fname)) == -1) {
    567 					DPRINTF(("devopen: open of %s failed\n",
    568 						fname));
    569 					return ENXIO;
    570 				}
    571 				ofdev.handle = handle;
    572 				DPRINTF(("devopen: back to original device %s\n",
    573 					fname));
    574 			}
    575 		} else {
    576 			part = partition ? partition - 'a' : 0;
    577 			ofdev.partoff = label.d_partitions[part].p_offset;
    578 			DPRINTF(("devopen: setting partition %d offset %lx\n",
    579 			       part, ofdev.partoff));
    580 			if (label.d_partitions[part].p_fstype == FS_RAID) {
    581 				ofdev.partoff += RF_PROTECTED_SECTORS;
    582 				DPRINTF(("devopen: found RAID partition, "
    583 				    "adjusting offset to %lx\n", ofdev.partoff));
    584 			}
    585 		}
    586 
    587 		nfsys = 0;
    588 		of->f_dev = ofdevsw;
    589 		of->f_devdata = &ofdev;
    590 #ifdef SPARC_BOOT_UFS
    591 		memcpy(&file_system[nfsys++], &file_system_ufs[0], sizeof file_system[0]);
    592 		memcpy(&file_system[nfsys++], &file_system_ufs[1], sizeof file_system[0]);
    593 		memcpy(&file_system[nfsys++], &file_system_ufs[2], sizeof file_system[0]);
    594 		memcpy(&file_system[nfsys++], &file_system_ufs[3], sizeof file_system[0]);
    595 #endif
    596 #ifdef SPARC_BOOT_CD9660
    597 		memcpy(&file_system[nfsys++], &file_system_cd9660, sizeof file_system[0]);
    598 #endif
    599 		DPRINTF(("devopen: return 0\n"));
    600 		return 0;
    601 	}
    602 #ifdef NETBOOT
    603 	if (!strcmp(b.buf, "network")) {
    604 		if ((error = net_open(&ofdev)) != 0)
    605 			goto bad;
    606 
    607 		ofdev.type = OFDEV_NET;
    608 		of->f_dev = ofdevsw;
    609 		of->f_devdata = &ofdev;
    610 
    611 		if (!strncmp(*file,"/tftp:",6)) {
    612 			*file += 6;
    613 			memcpy(&file_system[0], &file_system_tftp, sizeof file_system[0]);
    614 			if (net_tftp_bootp((int **)&of->f_devdata)) {
    615 				net_close(&ofdev);
    616 				goto bad;
    617 			}
    618 			root_fs_quickseekable = false;
    619 		} else {
    620 			memcpy(&file_system[0], &file_system_nfs, sizeof file_system[0]);
    621 			if ((error = net_mountroot()) != 0) {
    622 				net_close(&ofdev);
    623 				goto bad;
    624 			}
    625 		}
    626 		nfsys = 1;
    627 		return 0;
    628 	}
    629 #endif
    630 	error = EFTYPE;
    631 bad:
    632 	DPRINTF(("devopen: error %d, cannot open device\n", error));
    633 	prom_close(handle);
    634 	ofdev.handle = -1;
    635 	return error;
    636 }
    637