1/*	$NetBSD: ofdev.c,v 1.7 2005/12/11 12:19:05 christos 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 <sys/param.h>
38#include <sys/disklabel.h>
39#include <sys/bootblock.h>
40
41#include <netinet/in.h>
42
43#include <lib/libkern/libkern.h>
44
45#include <lib/libsa/byteorder.h>
46#include <lib/libsa/ufs.h>
47#include <lib/libsa/cd9660.h>
48#include <lib/libsa/dosfs.h>
49#include <lib/libsa/nfs.h>
50
51#include "extern.h"
52#include "ofdev.h"
53#include "openfirm.h"
54
55extern char bootdev[];
56
57#ifdef DEBUG
58# define DPRINTF printf
59#else
60# define DPRINTF while (0) printf
61#endif
62
63static char *
64filename(char *str, char *ppart)
65{
66	char *cp, *lp;
67	char savec;
68	int dhandle;
69	char devtype[16];
70
71	lp = str;
72	devtype[0] = 0;
73	*ppart = 0;
74	for (cp = str; *cp; lp = cp) {
75		/* For each component of the path name... */
76		while (*++cp && *cp != '/');
77		savec = *cp;
78		*cp = 0;
79		/* ...look whether there is a device with this name */
80		dhandle = OF_finddevice(str);
81		*cp = savec;
82		if (dhandle == -1) {
83			/* if not, lp is the delimiter between device and path */
84			/* if the last component was a block device... */
85			if (!strcmp(devtype, "block")) {
86				/* search for arguments */
87				for (cp = lp;
88				     --cp >= str && *cp != '/' && *cp != ':';);
89				if (cp >= str && *cp == ':') {
90					/* found arguments, make firmware ignore them */
91					*cp = 0;
92					for (cp = lp; *--cp && *cp != ',';);
93					if (*++cp >= 'a' && *cp <= 'a' + MAXPARTITIONS)
94						*ppart = *cp;
95				}
96			}
97			return lp;
98		} else if (OF_getprop(dhandle, "device_type", devtype, sizeof devtype) < 0)
99			devtype[0] = 0;
100	}
101	return 0;
102}
103
104static int
105strategy(void *devdata, int rw, daddr_t blk, size_t size, void *buf,
106    size_t *rsize)
107{
108	struct of_dev *dev = devdata;
109	u_quad_t pos;
110	int n;
111
112	if (rw != F_READ)
113		return EPERM;
114	if (dev->type != OFDEV_DISK)
115		panic("strategy");
116
117	pos = (u_quad_t)(blk + dev->partoff) * dev->bsize;
118
119	for (;;) {
120		if (OF_seek(dev->handle, pos) < 0)
121			break;
122		n = OF_read(dev->handle, buf, size);
123		if (n == -2)
124			continue;
125		if (n < 0)
126			break;
127		*rsize = n;
128		return 0;
129	}
130	return EIO;
131}
132
133static int
134devclose(struct open_file *of)
135{
136	struct of_dev *op = of->f_devdata;
137
138	if (op->type == OFDEV_NET)
139		net_close(op);
140	OF_close(op->handle);
141	op->handle = -1;
142	return 0;
143}
144
145static char ofw[] = "OpenFirmware";
146
147struct devsw devsw[1] = {
148	{
149		ofw,
150		strategy,
151		(int (*)(struct open_file *, ...))nodev,
152		devclose,
153		noioctl
154	}
155};
156int ndevs = sizeof devsw / sizeof devsw[0];
157
158static struct fs_ops file_system_ufs = FS_OPS(ufs);
159static struct fs_ops file_system_cd9660 = FS_OPS(cd9660);
160static struct fs_ops file_system_dosfs = FS_OPS(dosfs);
161static struct fs_ops file_system_nfs = FS_OPS(nfs);
162
163struct fs_ops file_system[3];
164int nfsys;
165
166static struct of_dev ofdev = {
167	-1,
168};
169
170char opened_name[256];
171int floppyboot;
172
173static u_long
174get_long(const void *p)
175{
176	const unsigned char *cp = p;
177
178	return cp[0] | (cp[1] << 8) | (cp[2] << 16) | (cp[3] << 24);
179}
180
181/*
182 * Find a valid disklabel.
183 */
184static int
185search_label(struct of_dev *devp, u_long off, char *buf, struct disklabel *lp,
186    u_long off0)
187{
188	size_t nread;
189	struct mbr_partition *p;
190	int i;
191	u_long poff;
192	static int recursion;
193
194	if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &nread)
195	    || nread != DEV_BSIZE)
196		return ERDLAB;
197
198	if (*(u_int16_t *)&buf[MBR_MAGIC_OFFSET] != sa_htole16(MBR_MAGIC))
199		return ERDLAB;
200
201	if (recursion++ <= 1)
202		off0 += off;
203	for (p = (struct mbr_partition *)(buf + MBR_PART_OFFSET), i = 4;
204	     --i >= 0; p++) {
205		if (p->mbrp_type == MBR_PTYPE_NETBSD
206#ifdef COMPAT_386BSD_MBRPART
207		    || (p->mbrp_type == MBR_PTYPE_386BSD &&
208			(printf("WARNING: old BSD partition ID!\n"), 1)
209			/* XXX XXX - libsa printf() is void */ )
210#endif
211		    ) {
212			poff = get_long(&p->mbrp_start) + off0;
213			if (strategy(devp, F_READ, poff + LABELSECTOR,
214				     DEV_BSIZE, buf, &nread) == 0
215			    && nread == DEV_BSIZE) {
216				if (!getdisklabel(buf, lp)) {
217					recursion--;
218					return 0;
219				}
220			}
221			if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &nread)
222			    || nread != DEV_BSIZE) {
223				recursion--;
224				return ERDLAB;
225			}
226		} else if (p->mbrp_type == MBR_PTYPE_EXT) {
227			poff = get_long(&p->mbrp_start);
228			if (!search_label(devp, poff, buf, lp, off0)) {
229				recursion--;
230				return 0;
231			}
232			if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &nread)
233			    || nread != DEV_BSIZE) {
234				recursion--;
235				return ERDLAB;
236			}
237		}
238	}
239	recursion--;
240	return ERDLAB;
241}
242
243int
244devopen(struct open_file *of, const char *name, char **file)
245{
246	char *cp;
247	char partition;
248	char fname[256];
249	char buf[DEV_BSIZE];
250	struct disklabel label;
251	int handle, part;
252	size_t nread;
253	int error = 0;
254
255	if (ofdev.handle != -1)
256		panic("devopen");
257	if (of->f_flags != F_READ)
258		return EPERM;
259	(void)strcpy(fname, name);
260	cp = filename(fname, &partition);
261	if (cp) {
262		DPRINTF("filename=%s\n", cp);
263		strcpy(buf, cp);
264		*cp = 0;
265	}
266	if (!cp || !*buf)
267		strcpy(buf, DEFAULT_KERNEL);
268	if (!*fname)
269		strcpy(fname, bootdev);
270	DPRINTF("fname=%s\n", fname);
271	strcpy(opened_name, fname);
272	if (partition) {
273		cp = opened_name + strlen(opened_name);
274		*cp++ = ':';
275		*cp++ = partition;
276		*cp = 0;
277	}
278	if (*buf != '/')
279		(void)strcat(opened_name, "/");
280	(void)strcat(opened_name, buf);
281	*file = opened_name + strlen(fname) + 1;
282	if ((handle = OF_finddevice(fname)) == -1) {
283		DPRINTF("OF_finddevice(\"%s\") failed\n", fname);
284		return ENOENT;
285	}
286	if (OF_getprop(handle, "name", buf, sizeof buf) < 0)
287		return ENXIO;
288	floppyboot = !strcmp(buf, "floppy");
289	if (OF_getprop(handle, "device_type", buf, sizeof buf) < 0)
290		return ENXIO;
291	if (!strcmp(buf, "block"))
292		/* For block devices, indicate raw partition (:0 in OpenFirmware) */
293		(void)strcat(fname, ":0");
294	if ((handle = OF_open(fname)) == -1)
295		return ENXIO;
296	(void)memset(&ofdev, 0, sizeof ofdev);
297	ofdev.handle = handle;
298	if (!strcmp(buf, "block")) {
299		ofdev.type = OFDEV_DISK;
300		ofdev.bsize = DEV_BSIZE;
301		/* First try to find a disklabel without MBR partitions */
302		if (strategy(&ofdev, F_READ,
303			     LABELSECTOR, DEV_BSIZE, buf, &nread) != 0
304		    || nread != DEV_BSIZE
305		    || getdisklabel(buf, &label)) {
306			/* Else try MBR partitions */
307			error = search_label(&ofdev, 0, buf, &label, 0);
308			if (error && error != ERDLAB)
309				goto bad;
310		}
311
312		if (error == ERDLAB) {
313			if (partition)
314				/* User specified a parititon, but there is none */
315				goto bad;
316			/* No, label, just use complete disk */
317			ofdev.partoff = 0;
318		} else {
319			part = partition ? partition - 'a' : 0;
320			ofdev.partoff = label.d_partitions[part].p_offset;
321		}
322
323		of->f_dev = devsw;
324		of->f_devdata = &ofdev;
325		file_system[0] = file_system_ufs;
326		file_system[1] = file_system_cd9660;
327		file_system[1] = file_system_dosfs;
328		nfsys = 2;
329		return 0;
330	}
331	if (!strcmp(buf, "network")) {
332		ofdev.type = OFDEV_NET;
333		of->f_dev = devsw;
334		of->f_devdata = &ofdev;
335		file_system[0] = file_system_nfs;
336		nfsys = 1;
337		if ((error = net_open(&ofdev)) != 0)
338			goto bad;
339		return 0;
340	}
341	error = EFTYPE;
342bad:
343	OF_close(handle);
344	ofdev.handle = -1;
345	return error;
346}
347