xref: /netbsd-src/sys/arch/macppc/stand/ofwboot/ofdev.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: ofdev.c,v 1.26 2012/02/19 12:02:55 tsutsui 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/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 
66 static int
67 strategy(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 
95 static int
96 devopen_dummy(struct open_file *of, ...) {
97 	return -1;
98 }
99 
100 static int
101 devclose(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 
113 static struct devsw of_devsw[1] = {
114 	{ "OpenFirmware", strategy, devopen_dummy, devclose, noioctl }
115 };
116 int ndevs = sizeof of_devsw / sizeof of_devsw[0];
117 
118 static struct fs_ops file_system_ffsv1 = FS_OPS(ffsv1);
119 static struct fs_ops file_system_ffsv2 = FS_OPS(ffsv2);
120 static struct fs_ops file_system_lfsv1 = FS_OPS(lfsv1);
121 static struct fs_ops file_system_lfsv2 = FS_OPS(lfsv2);
122 static struct fs_ops file_system_hfs = FS_OPS(hfs);
123 static struct fs_ops file_system_ustarfs = FS_OPS(ustarfs);
124 static struct fs_ops file_system_cd9660 = FS_OPS(cd9660);
125 static struct fs_ops file_system_nfs = FS_OPS(nfs);
126 static struct fs_ops file_system_dosfs = FS_OPS(dosfs);
127 
128 struct fs_ops file_system[9];
129 int nfsys;
130 
131 static struct of_dev ofdev = {
132 	-1,
133 };
134 
135 char 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  */
141 static u_int8_t
142 check_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  */
179 static int
180 search_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 
219 static u_long
220 get_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  */
230 static int
231 search_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 
289 bool
290 parsefilepath(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 
376 int
377 devopen(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;
494 bad:
495 	OF_close(handle);
496 	ofdev.handle = -1;
497 	return error;
498 }
499