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