1 /**	$MirOS: src/sys/dev/ramdisk.c,v 1.4 2008/11/08 23:04:16 tg Exp $ */
2 /*	$OpenBSD: ramdisk.c,v 1.26 2005/06/30 23:49:08 miod Exp $	*/
3 /*	$NetBSD: ramdisk.c,v 1.8 1996/04/12 08:30:09 leo Exp $	*/
4 
5 /*
6  * Copyright (c) 2004 Thorsten "mirabilos" Glaser.
7  * Copyright (c) 1995 Gordon W. Ross, Leo Weppelman.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  * 4. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *      This product includes software developed by
23  *			Gordon W. Ross and Leo Weppelman.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  */
36 
37 /*
38  * This implements a general-purpose RAM-disk.
39  * See ramdisk.h for notes on the config types.
40  *
41  * Note that this driver provides the same functionality
42  * as the MFS filesystem hack, but this is better because
43  * you can use this for any filesystem type you'd like!
44  *
45  * Credit for most of the kmem ramdisk code goes to:
46  *   Leo Weppelman (atari) and Phil Nelson (pc532)
47  * Credit for the ideas behind the "user space RAM" code goes
48  * to the authors of the MFS implementation.
49  */
50 
51 #include <sys/param.h>
52 #include <sys/kernel.h>
53 #include <sys/malloc.h>
54 #include <sys/systm.h>
55 #include <sys/buf.h>
56 #include <sys/device.h>
57 #include <sys/file.h>
58 #include <sys/disk.h>
59 #include <sys/proc.h>
60 #include <sys/conf.h>
61 #include <sys/disklabel.h>
62 #include <sys/dkio.h>
63 
64 #include <uvm/uvm_extern.h>
65 
66 #include <dev/ramdisk.h>
67 
68 /*
69  * By default, include the user-space functionality.
70  * Use:  option RAMDISK_SERVER=0 to turn it off.
71  */
72 #if !defined(RAMDISK_SERVER) && !defined(SMALL_KERNEL)
73 #define	RAMDISK_SERVER 1
74 #endif
75 
76 /*
77  * XXX: the "control" unit is (base unit + 16).
78  * We should just use the cdev as the "control", but
79  * that interferes with the security stuff preventing
80  * simultaneous use of raw and block devices.
81  *
82  * XXX Assumption: 16 RAM-disks are enough!
83  */
84 #define RD_MAX_UNITS	0x10
85 #define RD_IS_CTRL(dev) (DISKPART(dev) == RAW_PART)
86 
87 /* autoconfig stuff... */
88 
89 struct rd_softc {
90 	struct device sc_dev;	/* REQUIRED first entry */
91 	struct disk sc_dkdev;	/* hook for generic disk handling */
92 	struct rd_conf sc_rd;
93 #if RAMDISK_SERVER
94 	struct buf *sc_buflist;
95 #endif
96 };
97 /* shorthand for fields in sc_rd: */
98 #define sc_addr sc_rd.rd_addr
99 #define sc_size sc_rd.rd_size
100 #define sc_type sc_rd.rd_type
101 
102 void rdattach(int);
103 void rd_attach(struct device *, struct device *, void *);
104 void rdgetdisklabel(struct rd_softc *);
105 
106 /*
107  * Some ports (such as i386) use a swapgeneric that wants to
108  * snoop around in this rd_cd structure.  It is preserved
109  * (for now) to remain compatible with such practice.
110  * XXX - that practice is questionable...
111  */
112 struct cfdriver rd_cd = {
113 	NULL, "rd", DV_DULL
114 };
115 
116 void rdstrategy(struct buf *);
117 struct dkdriver rddkdriver = {rdstrategy};
118 
119 int ramdisk_ndevs;
120 void *ramdisk_devs[RD_MAX_UNITS];
121 
122 /*
123  * This is called if we are configured as a pseudo-device
124  */
125 void
rdattach(int n)126 rdattach(int n)
127 {
128 	struct rd_softc *sc;
129 	int i;
130 
131 #ifdef	DIAGNOSTIC
132 	if (ramdisk_ndevs) {
133 		printf("ramdisk: multiple attach calls?\n");
134 		return;
135 	}
136 #endif
137 
138 	/* XXX:  Are we supposed to provide a default? */
139 	if (n <= 1)
140 		n = 1;
141 	if (n > RD_MAX_UNITS)
142 		n = RD_MAX_UNITS;
143 	ramdisk_ndevs = n;
144 
145 	/* XXX: Fake-up rd_cd (see above) */
146 	rd_cd.cd_ndevs = ramdisk_ndevs;
147 	rd_cd.cd_devs = ramdisk_devs;
148 
149 	/* Attach as if by autoconfig. */
150 	for (i = 0; i < n; i++) {
151 		sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK);
152 		bzero((caddr_t)sc, sizeof(*sc));
153 		if (snprintf(sc->sc_dev.dv_xname, sizeof(sc->sc_dev.dv_xname),
154 		    "rd%d", i) >= sizeof(sc->sc_dev.dv_xname)) {
155 			printf("rdattach: device name too long\n");
156 			free(sc, M_DEVBUF);
157 			return;
158 		}
159 		ramdisk_devs[i] = sc;
160 		sc->sc_dev.dv_unit = i;
161 		rd_attach(NULL, &sc->sc_dev, NULL);
162 	}
163 }
164 
165 void
rd_attach(struct device * parent,struct device * self,void * aux)166 rd_attach(struct device *parent, struct device *self, void *aux)
167 {
168 	struct rd_softc *sc = (struct rd_softc *)self;
169 
170 	/* XXX - Could accept aux info here to set the config. */
171 #ifdef	RAMDISK_HOOKS
172 	/*
173 	 * This external function might setup a pre-loaded disk.
174 	 * All it would need to do is setup the rd_conf struct.
175 	 * See sys/arch/sun3/dev/rd_root.c for an example.
176 	 */
177 	rd_attach_hook(sc->sc_dev.dv_unit, &sc->sc_rd);
178 #endif
179 
180 	/*
181 	 * Initialize and attach the disk structure.
182 	 */
183 	sc->sc_dkdev.dk_driver = &rddkdriver;
184 	sc->sc_dkdev.dk_name = sc->sc_dev.dv_xname;
185 	disk_attach(&sc->sc_dkdev);
186 }
187 
188 /*
189  * operational routines:
190  * open, close, read, write, strategy,
191  * ioctl, dump, size
192  */
193 
194 #if RAMDISK_SERVER
195 int rd_server_loop(struct rd_softc *);
196 int
197 rd_ioctl_server(struct rd_softc *,
198     struct rd_conf *, struct proc *);
199 #endif
200 int
201 rd_ioctl_kalloc(struct rd_softc *,
202     struct rd_conf *, struct proc *);
203 
204 dev_type_open(rdopen);
205 dev_type_close(rdclose);
206 dev_type_read(rdread);
207 dev_type_write(rdwrite);
208 dev_type_ioctl(rdioctl);
209 dev_type_size(rdsize);
210 dev_type_dump(rddump);
211 
212 int
rddump(dev_t dev,daddr_t blkno,caddr_t va,size_t size)213 rddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
214 {
215 	return ENODEV;
216 }
217 
218 int
rdsize(dev_t dev)219 rdsize(dev_t dev)
220 {
221 	int part, unit;
222 	struct rd_softc *sc;
223 
224 	/* Disallow control units. */
225 	unit = DISKUNIT(dev);
226 	if (unit >= ramdisk_ndevs)
227 		return 0;
228 	sc = ramdisk_devs[unit];
229 	if (sc == NULL)
230 		return 0;
231 
232 	if (sc->sc_type == RD_UNCONFIGURED)
233 		return 0;
234 
235 	rdgetdisklabel(sc);
236 	part = DISKPART(dev);
237 	if (part >= sc->sc_dkdev.dk_label->d_npartitions)
238 		return 0;
239 	else
240 		return sc->sc_dkdev.dk_label->d_partitions[part].p_size *
241 		    (sc->sc_dkdev.dk_label->d_secsize / DEV_BSIZE);
242 }
243 
244 int
rdopen(dev_t dev,int flag,int fmt,struct proc * proc)245 rdopen(dev_t dev, int flag, int fmt, struct proc *proc)
246 {
247 	int unit;
248 	struct rd_softc *sc;
249 
250 	unit = DISKUNIT(dev);
251 	if (unit >= ramdisk_ndevs)
252 		return ENXIO;
253 	sc = ramdisk_devs[unit];
254 	if (sc == NULL)
255 		return ENXIO;
256 
257 	/*
258 	 * The control device is not exclusive, and can
259 	 * open uninitialized units (so you can setconf).
260 	 */
261 	if (RD_IS_CTRL(dev))
262 		return 0;
263 
264 #ifdef	RAMDISK_HOOKS
265 	/* Call the open hook to allow loading the device. */
266 	rd_open_hook(unit, &sc->sc_rd);
267 #endif
268 
269 	/*
270 	 * This is a normal, "slave" device, so
271 	 * enforce initialized, exclusive open.
272 	 */
273 	if (sc->sc_type == RD_UNCONFIGURED)
274 		return ENXIO;
275 
276 	return 0;
277 }
278 
279 int
rdclose(dev_t dev,int flag,int fmt,struct proc * proc)280 rdclose(dev_t dev, int flag, int fmt, struct proc *proc)
281 {
282 	return 0;
283 }
284 
285 int
rdread(dev_t dev,struct uio * uio,int flags)286 rdread(dev_t dev, struct uio *uio, int flags)
287 {
288 	return (physio(rdstrategy, NULL, dev, B_READ, minphys, uio));
289 }
290 
291 int
rdwrite(dev_t dev,struct uio * uio,int flags)292 rdwrite(dev_t dev, struct uio *uio, int flags)
293 {
294 	return (physio(rdstrategy, NULL, dev, B_WRITE, minphys, uio));
295 }
296 
297 /*
298  * Handle I/O requests, either directly, or
299  * by passing them to the server process.
300  */
301 void
rdstrategy(struct buf * bp)302 rdstrategy(struct buf *bp)
303 {
304 	int unit, part;
305 	struct rd_softc *sc;
306 	caddr_t addr;
307 	size_t off, xfer;
308 	int s;
309 
310 	unit = DISKUNIT(bp->b_dev);
311 	sc = ramdisk_devs[unit];
312 
313 	if (!sc->sc_dkdev.dk_label->d_secsize)
314 		rdgetdisklabel(sc);
315 
316 	/* Sort rogue requests out */
317 	if (sc == NULL || bp->b_blkno < 0 ||
318 	    (bp->b_bcount % sc->sc_dkdev.dk_label->d_secsize) != 0) {
319 		bp->b_error = EINVAL;
320 		goto bad;
321 	}
322 	/* Do not write on "no trespassing" areas... */
323 	part = DISKPART(bp->b_dev);
324 	if (part != RAW_PART &&
325 	    bounds_check_with_label(bp, sc->sc_dkdev.dk_label,
326 	    sc->sc_dkdev.dk_cpulabel, 1) <= 0)
327 		goto bad;
328 
329 	switch (sc->sc_type) {
330 #if RAMDISK_SERVER
331 	case RD_UMEM_SERVER:
332 		/* Just add this job to the server's queue. */
333 		bp->b_actf = sc->sc_buflist;
334 		sc->sc_buflist = bp;
335 		if (bp->b_actf == NULL) {
336 			/* server queue was empty. */
337 			wakeup((caddr_t)sc);
338 			/* see rd_server_loop() */
339 		}
340 		/* no biodone in this case */
341 		return;
342 #endif	/* RAMDISK_SERVER */
343 
344 	case RD_KMEM_FIXED:
345 	case RD_KMEM_ALLOCATED:
346 		/* These are in kernel space.  Access directly. */
347 		bp->b_resid = bp->b_bcount;
348 		off = (bp->b_blkno << DEV_BSHIFT);
349 		xfer = bp->b_bcount;
350 		if (xfer > (sc->sc_size - off))
351 			xfer = (sc->sc_size - off);
352 		addr = sc->sc_addr + off;
353 		if (bp->b_flags & B_READ)
354 			bcopy(addr, bp->b_data, xfer);
355 		else
356 			bcopy(bp->b_data, addr, xfer);
357 		bp->b_resid -= xfer;
358 		break;
359 
360 	default:
361 		bp->b_error = EIO;
362 bad:
363 		bp->b_flags |= B_ERROR;
364 		bp->b_resid = bp->b_bcount;
365 		break;
366 	}
367 
368 	s = splbio();
369 	biodone(bp);
370 	splx(s);
371 }
372 
373 int
rdioctl(dev_t dev,u_long cmd,caddr_t data,int flag,struct proc * proc)374 rdioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *proc)
375 {
376 	int unit;
377 	struct rd_softc *sc;
378 	struct rd_conf *urd;
379 	int error;
380 
381 	unit = DISKUNIT(dev);
382 	sc = ramdisk_devs[unit];
383 
384 	urd = (struct rd_conf *)data;
385 	switch (cmd) {
386 	case DIOCGDINFO:
387 		if (sc->sc_type == RD_UNCONFIGURED) {
388 			break;
389 		}
390 		rdgetdisklabel(sc);
391 		bcopy(sc->sc_dkdev.dk_label, data, sizeof(struct disklabel));
392 		return 0;
393 
394 	case DIOCWDINFO:
395 	case DIOCSDINFO:
396 		if (sc->sc_type == RD_UNCONFIGURED) {
397 			break;
398 		}
399 		if ((flag & FWRITE) == 0)
400 			return EBADF;
401 
402 		error = setdisklabel(sc->sc_dkdev.dk_label,
403 		    (struct disklabel *)data, /* sd->sc_dk.dk_openmask : */ 0,
404 		    sc->sc_dkdev.dk_cpulabel);
405 		if (error == 0) {
406 			if (cmd == DIOCWDINFO)
407 				error = writedisklabel(DISKLABELDEV(dev),
408 				    rdstrategy, sc->sc_dkdev.dk_label,
409 				    sc->sc_dkdev.dk_cpulabel);
410 		}
411 		return error;
412 
413 	case DIOCWLABEL:
414 		if (sc->sc_type == RD_UNCONFIGURED) {
415 			break;
416 		}
417 		if ((flag & FWRITE) == 0)
418 			return EBADF;
419 		return 0;
420 
421 	case RD_GETCONF:
422 		/* If this is not the control device, punt! */
423 		if (RD_IS_CTRL(dev) == 0) {
424 			break;
425 		}
426 		*urd = sc->sc_rd;
427 		return 0;
428 
429 	case RD_SETCONF:
430 		/* If this is not the control device, punt! */
431 		if (RD_IS_CTRL(dev) == 0) {
432 			break;
433 		}
434 		/* Can only set it once. */
435 		if (sc->sc_type != RD_UNCONFIGURED) {
436 			break;
437 		}
438 		switch (urd->rd_type) {
439 		case RD_KMEM_ALLOCATED:
440 			return rd_ioctl_kalloc(sc, urd, proc);
441 #if RAMDISK_SERVER
442 		case RD_UMEM_SERVER:
443 			return rd_ioctl_server(sc, urd, proc);
444 #endif
445 		default:
446 			break;
447 		}
448 		break;
449 	}
450 	return EINVAL;
451 }
452 
453 void
rdgetdisklabel(struct rd_softc * sc)454 rdgetdisklabel(struct rd_softc *sc)
455 {
456 	struct disklabel *lp = sc->sc_dkdev.dk_label;
457 
458 	bzero(sc->sc_dkdev.dk_label, sizeof(struct disklabel));
459 	bzero(sc->sc_dkdev.dk_cpulabel, sizeof(struct cpu_disklabel));
460 
461 	lp->d_secsize = 1 << DEV_BSHIFT;
462 	lp->d_ntracks = 1;
463 	lp->d_nsectors = sc->sc_size >> DEV_BSHIFT;
464 	lp->d_ncylinders = 1;
465 	lp->d_secpercyl = lp->d_nsectors;
466 	if (lp->d_secpercyl == 0) {
467 		lp->d_secpercyl = 100;
468 		/* as long as it's not 0 - readdisklabel divides by it (?) */
469 	}
470 	strncpy(lp->d_typename, "RAM disk", sizeof(lp->d_typename));
471 	lp->d_type = DTYPE_SCSI;
472 	strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
473 	lp->d_secperunit = lp->d_nsectors;
474 	lp->d_rpm = 3600;
475 	lp->d_interleave = 1;
476 	lp->d_flags = D_RAMDISK;
477 
478 	lp->d_partitions[RAW_PART].p_offset = 0;
479 	lp->d_partitions[RAW_PART].p_size =
480 	    lp->d_secperunit * (lp->d_secsize / DEV_BSIZE);
481 	lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
482 	lp->d_npartitions = RAW_PART + 1;
483 
484 	lp->d_magic = DISKMAGIC;
485 	lp->d_magic2 = DISKMAGIC;
486 	lp->d_checksum = dkcksum(lp);
487 
488 	/*
489 	 * Call the generic disklabel extraction routine
490 	 */
491 	readdisklabel(DISKLABELDEV(sc->sc_dev.dv_unit), rdstrategy,
492 	    sc->sc_dkdev.dk_label, sc->sc_dkdev.dk_cpulabel, 0);
493 }
494 
495 /*
496  * Handle ioctl RD_SETCONF for (sc_type == RD_KMEM_ALLOCATED)
497  * Just allocate some kernel memory and return.
498  */
499 int
rd_ioctl_kalloc(struct rd_softc * sc,struct rd_conf * urd,struct proc * proc)500 rd_ioctl_kalloc(struct rd_softc *sc, struct rd_conf *urd, struct proc *proc)
501 {
502 	vaddr_t addr;
503 	vsize_t size;
504 
505 	/* Sanity check the size. */
506 	size = urd->rd_size;
507 	addr = uvm_km_zalloc(kernel_map, size);
508 	if (!addr)
509 		return ENOMEM;
510 
511 	/* This unit is now configured. */
512 	sc->sc_addr = (caddr_t)addr;	/* kernel space */
513 	sc->sc_size = (size_t)size;
514 	sc->sc_type = RD_KMEM_ALLOCATED;
515 	return 0;
516 }
517 
518 #if RAMDISK_SERVER
519 /*
520  * Handle ioctl RD_SETCONF for (sc_type == RD_UMEM_SERVER)
521  * Set config, then become the I/O server for this unit.
522  */
523 int
rd_ioctl_server(struct rd_softc * sc,struct rd_conf * urd,struct proc * proc)524 rd_ioctl_server(struct rd_softc *sc, struct rd_conf *urd, struct proc *proc)
525 {
526 	vaddr_t end;
527 	int error;
528 
529 	/* Sanity check addr, size. */
530 	end = (vaddr_t)(urd->rd_addr + urd->rd_size);
531 
532 	if ((end >= VM_MAXUSER_ADDRESS) || (end < ((vaddr_t)urd->rd_addr)))
533 		return EINVAL;
534 
535 	/* This unit is now configured. */
536 	sc->sc_addr = urd->rd_addr;	/* user space */
537 	sc->sc_size = urd->rd_size;
538 	sc->sc_type = RD_UMEM_SERVER;
539 
540 	/* Become the server daemon */
541 	error = rd_server_loop(sc);
542 
543 	/* This server is now going away! */
544 	sc->sc_type = RD_UNCONFIGURED;
545 	sc->sc_addr = 0;
546 	sc->sc_size = 0;
547 
548 	return (error);
549 }
550 
551 int rd_sleep_pri = PWAIT | PCATCH;
552 
553 int
rd_server_loop(struct rd_softc * sc)554 rd_server_loop(struct rd_softc *sc)
555 {
556 	struct buf *bp;
557 	caddr_t addr;	/* user space address */
558 	size_t off;	/* offset into "device" */
559 	size_t xfer;	/* amount to transfer */
560 	int error;
561 	int s;
562 
563 	for (;;) {
564 		/* Wait for some work to arrive. */
565 		while (sc->sc_buflist == NULL) {
566 			error = tsleep((caddr_t)sc, rd_sleep_pri, "rd_idle", 0);
567 			if (error)
568 				return error;
569 		}
570 
571 		/* Unlink buf from head of list. */
572 		bp = sc->sc_buflist;
573 		sc->sc_buflist = bp->b_actf;
574 		bp->b_actf = NULL;
575 
576 		/* Do the transfer to/from user space. */
577 		error = 0;
578 		bp->b_resid = bp->b_bcount;
579 		off = (bp->b_blkno << DEV_BSHIFT);
580 		if (off >= sc->sc_size) {
581 			if (bp->b_flags & B_READ)
582 				goto done;	/* EOF (not an error) */
583 			error = EIO;
584 			goto done;
585 		}
586 		xfer = bp->b_resid;
587 		if (xfer > (sc->sc_size - off))
588 			xfer = (sc->sc_size - off);
589 		addr = sc->sc_addr + off;
590 		if (bp->b_flags & B_READ)
591 			error = copyin(addr, bp->b_data, xfer);
592 		else
593 			error = copyout(bp->b_data, addr, xfer);
594 		if (!error)
595 			bp->b_resid -= xfer;
596 
597 done:
598 		if (error) {
599 			bp->b_error = error;
600 			bp->b_flags |= B_ERROR;
601 		}
602 		s = splbio();
603 		biodone(bp);
604 		splx(s);
605 	}
606 }
607 #endif	/* RAMDISK_SERVER */
608