xref: /freebsd/sys/dev/ofw/ofw_disk.c (revision 6b3455a7665208c366849f0b2b3bc916fb97516e)
1 /*
2  * Copyright (C) 2002 Benno Rice <benno@FreeBSD.org>
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  *
26  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/bio.h>
34 #include <sys/module.h>
35 #include <sys/bus.h>
36 #include <sys/conf.h>
37 #include <sys/kernel.h>
38 #include <sys/limits.h>
39 #include <geom/geom_disk.h>
40 
41 #include <dev/ofw/openfirm.h>
42 
43 #include <machine/bus.h>
44 #include <machine/md_var.h>
45 #include <machine/nexusvar.h>
46 
47 #define	OFWD_BLOCKSIZE	512
48 
49 struct ofwd_softc
50 {
51 	device_t	ofwd_dev;
52 	struct		disk *ofwd_disk;
53 	phandle_t	ofwd_package;
54 	ihandle_t	ofwd_instance;
55 };
56 
57 /*
58  * Disk device bus interface.
59  */
60 static void	ofwd_identify(driver_t *, device_t);
61 static int	ofwd_probe(device_t);
62 static int	ofwd_attach(device_t);
63 
64 static device_method_t	ofwd_methods[] = {
65 	DEVMETHOD(device_identify,	ofwd_identify),
66 	DEVMETHOD(device_probe, 	ofwd_probe),
67 	DEVMETHOD(device_attach,	ofwd_attach),
68 	{ 0, 0 }
69 };
70 
71 static driver_t ofwd_driver = {
72 	"ofwd",
73 	ofwd_methods,
74 	sizeof(struct ofwd_softc)
75 };
76 
77 static devclass_t	ofwd_devclass;
78 
79 DRIVER_MODULE(ofwd, nexus, ofwd_driver, ofwd_devclass, 0, 0);
80 
81 /*
82  * Disk device control interface.
83  */
84 static disk_strategy_t	ofwd_strategy;
85 
86 /*
87  * Handle an I/O request.
88  */
89 static void
90 ofwd_strategy(struct bio *bp)
91 {
92 	struct	ofwd_softc *sc;
93 	long	r;
94 
95 	sc = (struct ofwd_softc *)bp->bio_disk->d_drv1;
96 
97 	if (sc == NULL) {
98 		printf("ofwd: bio for invalid disk!\n");
99 		biofinish(bp, NULL, EINVAL);
100 		return;
101 	}
102 
103 	bp->bio_resid = bp->bio_bcount;
104 
105 	r = OF_seek(sc->ofwd_instance, bp->bio_offset);
106 	if (r == -1) {
107 		device_printf(sc->ofwd_dev, "seek failed\n");
108 		biofinish(bp, NULL, EIO);
109 		return;
110 	}
111 
112 	if (bp->bio_cmd == BIO_READ) {
113 		r = OF_read(sc->ofwd_instance, (void *)bp->bio_data,
114 		    bp->bio_bcount);
115 	} else {
116 		r = OF_write(sc->ofwd_instance, (void *)bp->bio_data,
117 			    bp->bio_bcount);
118 	}
119 
120 	if (r > bp->bio_bcount)
121 		panic("ofwd: more bytes read/written than requested");
122 	if (r == -1) {
123 		device_printf(sc->ofwd_dev, "r (%ld) < bp->bio_bcount (%ld)\n",
124 		    r, bp->bio_bcount);
125 		biofinish(bp, NULL, EIO);
126 		return;
127 	}
128 
129 	bp->bio_resid -= r;
130 
131 	if (r < bp->bio_bcount) {
132 		device_printf(sc->ofwd_dev, "r (%ld) < bp->bio_bcount (%ld)\n",
133 		    r, bp->bio_bcount);
134 		biofinish(bp, NULL, EIO);	/* XXX: probably not an error */
135 		return;
136 	}
137 	biodone(bp);
138 	return;
139 }
140 
141 /*
142  * Attach the OpenFirmware disk to nexus if present
143  */
144 static void
145 ofwd_identify(driver_t *driver, device_t parent)
146 {
147 	device_t child;
148 	phandle_t ofd;
149 	static char type[8];
150 
151 	ofd = OF_finddevice("ofwdisk");
152 	if (ofd == -1)
153 		return;
154 
155 	OF_getprop(ofd, "device_type", type, sizeof(type));
156 
157 	child = BUS_ADD_CHILD(parent, INT_MAX, "ofwd", 0);
158 	if (child != NULL) {
159 		nexus_set_device_type(child, type);
160 		nexus_set_node(child, ofd);
161 	}
162 }
163 
164 /*
165  * Probe for an OpenFirmware disk.
166  */
167 static int
168 ofwd_probe(device_t dev)
169 {
170 	char		*type;
171 	char		fname[32];
172 	phandle_t	node;
173 
174 	type = nexus_get_device_type(dev);
175 	node = nexus_get_node(dev);
176 
177 	if (type == NULL ||
178 	    (strcmp(type, "disk") != 0 && strcmp(type, "block") != 0))
179 		return (ENXIO);
180 
181 	if (OF_getprop(node, "file", fname, sizeof(fname)) == -1)
182 		return (ENXIO);
183 
184 	device_set_desc(dev, "OpenFirmware disk");
185 	return (0);
186 }
187 
188 static int
189 ofwd_attach(device_t dev)
190 {
191 	struct	ofwd_softc *sc;
192 	char	path[128];
193 	char	fname[32];
194 
195 	sc = device_get_softc(dev);
196 	sc->ofwd_dev = dev;
197 
198 	bzero(path, 128);
199 	OF_package_to_path(nexus_get_node(dev), path, 128);
200 	OF_getprop(nexus_get_node(dev), "file", fname, sizeof(fname));
201 	device_printf(dev, "located at %s, file %s\n", path, fname);
202 	sc->ofwd_instance = OF_open(path);
203 	if (sc->ofwd_instance == -1) {
204 		device_printf(dev, "could not create instance\n");
205 		return (ENXIO);
206 	}
207 
208 	sc->ofwd_disk = disk_alloc();
209 	sc->ofwd_disk->d_strategy = ofwd_strategy;
210 	sc->ofwd_disk->d_name = "ofwd";
211 	sc->ofwd_disk->d_sectorsize = OFWD_BLOCKSIZE;
212 	sc->ofwd_disk->d_mediasize = (off_t)33554432 * OFWD_BLOCKSIZE;
213 	sc->ofwd_disk->d_fwsectors = 0;
214 	sc->ofwd_disk->d_fwheads = 0;
215 	sc->ofwd_disk->d_drv1 = sc;
216 	sc->ofwd_disk->d_maxsize = PAGE_SIZE;
217 	sc->ofwd_disk->d_unit = device_get_unit(dev);
218 	sc->ofwd_disk->d_flags = DISKFLAG_NEEDSGIANT;
219 	disk_create(sc->ofwd_disk, DISK_VERSION);
220 
221 	return (0);
222 }
223