xref: /openbsd/sys/dev/rd.c (revision 73471bf0)
1 /*	$OpenBSD: rd.c,v 1.13 2017/12/30 23:08:29 guenther Exp $	*/
2 
3 /*
4  * Copyright (c) 2011 Matthew Dempsky <matthew@dempsky.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/proc.h>
22 #include <sys/errno.h>
23 #include <sys/buf.h>
24 #include <sys/malloc.h>
25 #include <sys/ioctl.h>
26 #include <sys/disklabel.h>
27 #include <sys/device.h>
28 #include <sys/disk.h>
29 #include <sys/stat.h>
30 #include <sys/fcntl.h>
31 #include <sys/uio.h>
32 #include <sys/conf.h>
33 #include <sys/dkio.h>
34 #include <sys/vnode.h>
35 
36 #ifndef MINIROOTSIZE
37 #define MINIROOTSIZE 512
38 #endif
39 
40 #define ROOTBYTES (MINIROOTSIZE << DEV_BSHIFT)
41 
42 /*
43  * This array will be patched to contain a file-system image.
44  * See the program:  src/distrib/common/rdsetroot.c
45  */
46 u_int32_t rd_root_size = ROOTBYTES;
47 char rd_root_image[ROOTBYTES] = "|This is the root ramdisk!\n";
48 
49 void	rdattach(int);
50 int	rd_match(struct device *, void *, void *);
51 void	rd_attach(struct device *, struct device *, void *);
52 int	rd_detach(struct device *, int);
53 
54 struct rd_softc {
55 	struct device	sc_dev;
56 	struct disk	sc_dk;
57 };
58 
59 struct cfattach rd_ca = {
60 	sizeof(struct rd_softc),
61 	rd_match,
62 	rd_attach,
63 	rd_detach
64 };
65 
66 struct cfdriver rd_cd = {
67 	NULL,
68 	"rd",
69 	DV_DISK
70 };
71 
72 #define rdlookup(unit)	((struct rd_softc *)disk_lookup(&rd_cd, (unit)))
73 
74 int	rdgetdisklabel(dev_t, struct rd_softc *, struct disklabel *, int);
75 
76 void
77 rdattach(int num)
78 {
79 	static struct cfdata cf; /* Fake cf. */
80 	struct rd_softc *sc;
81 	int i;
82 
83 	/* There's only one rd_root_image, so only attach one rd. */
84 	num = 1;
85 
86 	/* XXX: Fake up more? */
87 	cf.cf_attach = &rd_ca;
88 	cf.cf_driver = &rd_cd;
89 
90 	rd_cd.cd_ndevs = num;
91 	rd_cd.cd_devs = mallocarray(num, sizeof(void *), M_DEVBUF, M_NOWAIT);
92 	if (rd_cd.cd_devs == NULL)
93 		panic("rdattach: out of memory");
94 
95 	for (i = 0; i < num; ++i) {
96 		/* Allocate the softc and initialize it. */
97 		sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT|M_ZERO);
98 		if (sc == NULL)
99 			panic("rdattach: out of memory");
100 		sc->sc_dev.dv_class = DV_DISK;
101 		sc->sc_dev.dv_cfdata = &cf;
102 		sc->sc_dev.dv_flags = DVF_ACTIVE;
103 		sc->sc_dev.dv_unit = i;
104 	        if (snprintf(sc->sc_dev.dv_xname, sizeof(sc->sc_dev.dv_xname),
105 		    "rd%d", i) >= sizeof(sc->sc_dev.dv_xname))
106 			panic("rdattach: device name too long");
107 		sc->sc_dev.dv_ref = 1;
108 
109 		/* Attach it to the device tree. */
110 		rd_cd.cd_devs[i] = sc;
111 		TAILQ_INSERT_TAIL(&alldevs, &sc->sc_dev, dv_list);
112 		device_ref(&sc->sc_dev);
113 
114 		/* Finish initializing. */
115 		rd_attach(NULL, &sc->sc_dev, NULL);
116 	}
117 }
118 
119 int
120 rd_match(struct device *parent, void *match, void *aux)
121 {
122 	return (0);
123 }
124 
125 void
126 rd_attach(struct device *parent, struct device *self, void *aux)
127 {
128 	struct rd_softc *sc = (struct rd_softc *)self;
129 
130 	/* Attach disk. */
131 	sc->sc_dk.dk_name = sc->sc_dev.dv_xname;
132 	disk_attach(&sc->sc_dev, &sc->sc_dk);
133 }
134 
135 int
136 rd_detach(struct device *self, int flags)
137 {
138 	struct rd_softc *sc = (struct rd_softc *)self;
139 
140 	disk_gone(rdopen, self->dv_unit);
141 
142 	/* Detach disk. */
143 	disk_detach(&sc->sc_dk);
144 
145 	return (0);
146 }
147 
148 int
149 rdopen(dev_t dev, int flag, int fmt, struct proc *p)
150 {
151 	struct rd_softc *sc;
152 	u_int unit, part;
153 	int error;
154 
155 	unit = DISKUNIT(dev);
156 	part = DISKPART(dev);
157 
158 	sc = rdlookup(unit);
159 	if (sc == NULL)
160 		return (ENXIO);
161 
162 	if ((error = disk_lock(&sc->sc_dk)) != 0)
163 		goto unref;
164 
165 	if (sc->sc_dk.dk_openmask == 0) {
166 		/* Load the partition info if not already loaded. */
167 		if ((error = rdgetdisklabel(dev, sc, sc->sc_dk.dk_label, 0))
168 		    != 0)
169 			goto unlock;
170 	}
171 
172 	error = disk_openpart(&sc->sc_dk, part, fmt, 1);
173 
174  unlock:
175 	disk_unlock(&sc->sc_dk);
176  unref:
177 	device_unref(&sc->sc_dev);
178 	return (error);
179 }
180 
181 int
182 rdclose(dev_t dev, int flag, int fmt, struct proc *p)
183 {
184 	struct rd_softc *sc;
185 	u_int unit, part;
186 
187 	unit = DISKUNIT(dev);
188 	part = DISKPART(dev);
189 
190 	sc = rdlookup(unit);
191 	if (sc == NULL)
192 		return (ENXIO);
193 
194 	disk_lock_nointr(&sc->sc_dk);
195 
196 	disk_closepart(&sc->sc_dk, part, fmt);
197 
198 	disk_unlock(&sc->sc_dk);
199 	device_unref(&sc->sc_dev);
200 	return (0);
201 }
202 
203 void
204 rdstrategy(struct buf *bp)
205 {
206 	struct rd_softc *sc;
207 	struct partition *p;
208 	size_t off, xfer;
209 	caddr_t addr;
210 	int s;
211 
212 	sc = rdlookup(DISKUNIT(bp->b_dev));
213 	if (sc == NULL) {
214 		bp->b_error = ENXIO;
215 		goto bad;
216 	}
217 
218 	/* Validate the request. */
219 	if (bounds_check_with_label(bp, sc->sc_dk.dk_label) == -1)
220 		goto done;
221 
222 	/* Do the transfer. */
223 	/* XXX: Worry about overflow when computing off? */
224 
225 	p = &sc->sc_dk.dk_label->d_partitions[DISKPART(bp->b_dev)];
226 	off = DL_GETPOFFSET(p) * sc->sc_dk.dk_label->d_secsize +
227 	    (u_int64_t)bp->b_blkno * DEV_BSIZE;
228 	if (off > rd_root_size)
229 		off = rd_root_size;
230 	xfer = bp->b_bcount;
231 	if (xfer > rd_root_size - off)
232 		xfer = rd_root_size - off;
233 	addr = rd_root_image + off;
234 	if (bp->b_flags & B_READ)
235 		memcpy(bp->b_data, addr, xfer);
236 	else
237 		memcpy(addr, bp->b_data, xfer);
238 	bp->b_resid = bp->b_bcount - xfer;
239 	goto done;
240 
241  bad:
242 	bp->b_flags |= B_ERROR;
243 	bp->b_resid = bp->b_bcount;
244  done:
245 	s = splbio();
246 	biodone(bp);
247 	splx(s);
248 	if (sc != NULL)
249 		device_unref(&sc->sc_dev);
250 }
251 
252 int
253 rdioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p)
254 {
255 	struct rd_softc *sc;
256 	struct disklabel *lp;
257 	int error = 0;
258 
259 	sc = rdlookup(DISKUNIT(dev));
260 	if (sc == NULL)
261 		return (ENXIO);
262 
263 	switch (cmd) {
264 	case DIOCRLDINFO:
265 		lp = malloc(sizeof(*lp), M_TEMP, M_WAITOK);
266 		rdgetdisklabel(dev, sc, lp, 0);
267 		memcpy(sc->sc_dk.dk_label, lp, sizeof(*lp));
268 		free(lp, M_TEMP, sizeof(*lp));
269 		goto done;
270 
271 	case DIOCGPDINFO:
272 		rdgetdisklabel(dev, sc, (struct disklabel *)data, 1);
273 		goto done;
274 
275 	case DIOCGDINFO:
276 		*(struct disklabel *)data = *(sc->sc_dk.dk_label);
277 		goto done;
278 
279 	case DIOCGPART:
280 		((struct partinfo *)data)->disklab = sc->sc_dk.dk_label;
281 		((struct partinfo *)data)->part =
282 		    &sc->sc_dk.dk_label->d_partitions[DISKPART(dev)];
283 		goto done;
284 
285 	case DIOCWDINFO:
286 	case DIOCSDINFO:
287 		if ((fflag & FWRITE) == 0) {
288 			error = EBADF;
289 			goto done;
290 		}
291 
292 		if ((error = disk_lock(&sc->sc_dk)) != 0)
293 			goto done;
294 
295 		error = setdisklabel(sc->sc_dk.dk_label,
296 		    (struct disklabel *)data, sc->sc_dk.dk_openmask);
297 		if (error == 0) {
298 			if (cmd == DIOCWDINFO)
299 				error = writedisklabel(DISKLABELDEV(dev),
300 				    rdstrategy, sc->sc_dk.dk_label);
301 		}
302 
303 		disk_unlock(&sc->sc_dk);
304 		goto done;
305 	}
306 
307  done:
308 	device_unref(&sc->sc_dev);
309 	return (error);
310 }
311 
312 int
313 rdgetdisklabel(dev_t dev, struct rd_softc *sc, struct disklabel *lp,
314     int spoofonly)
315 {
316 	bzero(lp, sizeof(struct disklabel));
317 
318 	lp->d_secsize = DEV_BSIZE;
319 	lp->d_ntracks = 1;
320 	lp->d_nsectors = rd_root_size >> DEV_BSHIFT;
321 	lp->d_ncylinders = 1;
322 	lp->d_secpercyl = lp->d_nsectors;
323 	if (lp->d_secpercyl == 0) {
324 		lp->d_secpercyl = 100;
325 		/* as long as it's not 0 - readdisklabel divides by it */
326 	}
327 
328 	strncpy(lp->d_typename, "RAM disk", sizeof(lp->d_typename));
329 	lp->d_type = DTYPE_SCSI;
330 	strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
331 	DL_SETDSIZE(lp, lp->d_nsectors);
332 	lp->d_version = 1;
333 
334 	lp->d_magic = DISKMAGIC;
335 	lp->d_magic2 = DISKMAGIC;
336 	lp->d_checksum = dkcksum(lp);
337 
338 	/* Call the generic disklabel extraction routine. */
339 	return (readdisklabel(DISKLABELDEV(dev), rdstrategy, lp, spoofonly));
340 }
341 
342 int
343 rdread(dev_t dev, struct uio *uio, int ioflag)
344 {
345 	return (physio(rdstrategy, dev, B_READ, minphys, uio));
346 }
347 
348 int
349 rdwrite(dev_t dev, struct uio *uio, int ioflag)
350 {
351 	return (physio(rdstrategy, dev, B_WRITE, minphys, uio));
352 }
353 
354 int
355 rddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
356 {
357 	return (ENXIO);
358 }
359 
360 daddr_t
361 rdsize(dev_t dev)
362 {
363 	return (-1);
364 }
365