ofw_disk.c revision 96423
1/*
2 * Copyright (C) 2000 Benno Rice.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *
25 * $FreeBSD: head/sys/boot/ofw/libofw/ofw_disk.c 96423 2002-05-11 21:30:46Z jake $
26 */
27
28/*
29 * Disk I/O routines using Open Firmware
30 */
31
32#include <sys/param.h>
33#include <sys/disklabel.h>
34
35#include <netinet/in.h>
36
37#include <machine/stdarg.h>
38
39#include <stand.h>
40
41#include "bootstrap.h"
42#include "libofw.h"
43
44#define	DISKSECSZ	512
45
46static int	ofwd_init(void);
47static int	ofwd_strategy(void *devdata, int flag, daddr_t dblk,
48				size_t size, char *buf, size_t *rsize);
49static int	ofwd_open(struct open_file *f, ...);
50static int	ofwd_close(struct open_file *f);
51static void	ofwd_print(int verbose);
52static char *	ofwd_getdevpath(int unit);
53int readdisklabel(struct ofw_devdesc *);
54
55struct devsw ofwdisk = {
56	"disk",
57	DEVT_DISK,
58	ofwd_init,
59	ofwd_strategy,
60	ofwd_open,
61	ofwd_close,
62	noioctl,
63	ofwd_print
64};
65
66static struct ofwdinfo {
67	int	ofwd_unit;
68	char	ofwd_path[255];
69} ofwdinfo[MAXDEV];
70static int nofwdinfo = 0;
71static int probed;
72
73#define	OFDP_FOUND	0
74#define	OFDP_NOTFOUND	1
75#define	OFDP_TERMINATE	2
76
77#define	MAXDEV_IDE	4
78#define	MAXDEV_DEFAULT	16	/* SCSI etc. */
79
80void
81ofwd_enter_dev(const char *devpath)
82{
83	char *p;
84	int n;
85
86	if (ofwd_getunit(devpath) != -1)
87		return;
88	if ((p = strrchr(devpath, ',')) != NULL)
89		n = p - devpath;
90	else
91		n = strlen(devpath);
92	ofwdinfo[nofwdinfo].ofwd_unit = nofwdinfo;
93	strncpy(ofwdinfo[nofwdinfo].ofwd_path, devpath, n);
94	ofwdinfo[nofwdinfo].ofwd_path[n] = '\0';
95	printf("disk%d is %s\n", nofwdinfo, ofwdinfo[nofwdinfo].ofwd_path);
96	nofwdinfo++;
97}
98
99static int
100ofwd_probe_dev(char *devpath)
101{
102	ihandle_t instance;
103	int rv;
104
105	/* Is the device already in the list? */
106	if (ofwd_getunit(devpath) != -1)
107		return OFDP_FOUND;
108	instance = OF_open(devpath);
109	if (instance != -1) {
110		ofwd_enter_dev(devpath);
111		OF_close(instance);
112	} else
113		return OFDP_NOTFOUND;
114	if (nofwdinfo > MAXDEV) {
115		printf("Hit MAXDEV probing disks.\n");
116		return OFDP_TERMINATE;
117	}
118	return OFDP_FOUND;
119}
120
121static int
122ofwd_probe_devs(void)
123{
124	int ret;
125	char devpath[255];
126#ifdef __sparc64__
127	int i, n;
128	char cdevpath[255];
129#endif
130
131	probed = 1;
132	ofw_devsearch_init();
133	while ((ret = ofw_devsearch("block", devpath)) != 0) {
134		devpath[sizeof devpath - 1] = 0;
135		if (ret == -1)
136			return 1;
137#ifdef DEBUG
138		printf("devpath=\"%s\" ret=%d\n", devpath, ret);
139#endif
140
141		if (strstr(devpath, "cdrom") != 0)
142			continue;
143
144#ifdef __sparc64__
145		/*
146		 * sparc64 machines usually only have a single disk node as
147		 * child of the controller (in the ATA case, there may exist
148		 * an additional cdrom node, which we ignore above, since
149		 * booting from it is special, and it can also be used as a
150		 * disk node).
151		 * Devices are accessed by using disk@unit; when no unit
152		 * number is given, 0 is assumed.
153		 * There is no way we can enumerate the existing disks except
154		 * trying to open them, which unfortunately creates some deleays
155		 * and spurioius warnings printed by the prom, which we can't
156		 * do much about. The search may not stop on the first
157		 * unsuccessful attempt, because that would cause disks that
158		 * follow one with an invalid label (like CD-ROMS) would not
159		 * be detected this way.
160		 * Try to at least be a bit smart and only probe 4 devices in
161		 * the IDE case.
162		 */
163		if (strstr(devpath, "/ide@") != NULL)
164			n = MAXDEV_IDE;
165		else
166			n = MAXDEV_DEFAULT;
167		for (i = 0; i < n; i++) {
168			sprintf(cdevpath, "%s@%d", devpath, i);
169			if (ofwd_probe_dev(cdevpath) == OFDP_TERMINATE)
170				return 1;
171		}
172#else
173		if (ofwd_probe_dev(devpath) == OFDP_TERMINATE)
174			return 1;
175#endif
176	}
177
178	return 0;
179}
180
181static int
182ofwd_init(void)
183{
184#ifdef __sparc64__
185	/* Short-circuit the device probing, since it takes too long. */
186	return 0;
187#else
188	return ofwd_init_devs();
189#endif
190}
191
192static int
193ofwd_strategy(void *devdata, int flag, daddr_t dblk, size_t size, char *buf,
194    size_t *rsize)
195{
196	struct ofw_devdesc *dp = (struct ofw_devdesc *)devdata;
197	unsigned long pos;
198	int n;
199	int i, j;
200
201	pos = (dp->d_kind.ofwdisk.partoff + dblk) * dp->d_kind.ofwdisk.bsize;
202
203	do {
204		if (OF_seek(dp->d_kind.ofwdisk.handle, pos) < 0) {
205			return EIO;
206		}
207		n = OF_read(dp->d_kind.ofwdisk.handle, buf, size);
208		if (n < 0 && n != -2) {
209			return EIO;
210		}
211	} while (n == -2);
212
213	*rsize = size;
214	return 0;
215}
216
217static int
218ofwd_open(struct open_file *f, ...)
219{
220	va_list vl;
221	struct ofw_devdesc *dp;
222	char *devpath;
223	phandle_t diskh;
224	char buf[256];
225	int i, j;
226
227	va_start(vl, f);
228	dp = va_arg(vl, struct ofw_devdesc *);
229	va_end(vl);
230
231	/*
232	 * The unit number is really an index into our device array.
233	 * If it is not in the list, we may need to probe now.
234	 */
235	if (!probed && dp->d_kind.ofwdisk.unit >= nofwdinfo)
236		ofwd_probe_devs();
237	if (dp->d_kind.ofwdisk.unit >= nofwdinfo)
238		return 1;
239	devpath = ofwdinfo[dp->d_kind.ofwdisk.unit].ofwd_path;
240	sprintf(buf, "%s,%d:%c", devpath, dp->d_kind.ofwdisk.slice,
241	    'a' + dp->d_kind.ofwdisk.partition);
242	if ((diskh = OF_open(buf)) == -1) {
243		printf("ofwd_open: Could not open %s\n", buf);
244		return 1;
245	}
246	dp->d_kind.ofwdisk.bsize = DISKSECSZ;
247	dp->d_kind.ofwdisk.handle = diskh;
248	readdisklabel(dp);
249
250	return 0;
251}
252
253int
254readdisklabel(struct ofw_devdesc *dp)
255{
256	char buf[DISKSECSZ];
257	struct disklabel *lp;
258	size_t size;
259	int i;
260
261	dp->d_kind.ofwdisk.partoff = 0;
262	dp->d_dev->dv_strategy(dp, 0, LABELSECTOR, sizeof(buf), buf, &size);
263	i = dp->d_kind.ofwdisk.partition;
264	if (i >= MAXPARTITIONS)
265		return 1;
266
267	lp = (struct disklabel *)(buf + LABELOFFSET);
268	dp->d_kind.ofwdisk.partoff = lp->d_partitions[i].p_offset;
269	return 0;
270}
271
272static int
273ofwd_close(struct open_file *f)
274{
275	struct ofw_devdesc *dev = f->f_devdata;
276	OF_close(dev->d_kind.ofwdisk.handle);
277
278	return 0;
279}
280
281static void
282ofwd_print(int verbose)
283{
284	int	i;
285	char	line[80];
286
287	if (!probed)
288		ofwd_probe_devs();
289	for (i = 0; i < nofwdinfo; i++) {
290		sprintf(line, "    disk%d:   %s", i, ofwdinfo[i].ofwd_path);
291		pager_output(line);
292		pager_output("\n");
293	}
294	return;
295}
296
297int
298ofwd_getunit(const char *path)
299{
300	char *p;
301	int i, n;
302
303	if ((p = strrchr(path, ',')) != NULL)
304		n = p - path;
305	else
306		n = strlen(path);
307	for (i = 0; i < nofwdinfo; i++) {
308		if (strncmp(path, ofwdinfo[i].ofwd_path, n) == 0)
309			return i;
310	}
311
312	return -1;
313}
314