Edit

IABSD.fr/src/sys/dev/rd.c

Branch :

  • Show log

    Commit

  • Author : guenther
    Date : 2017-12-30 23:08:29
    Hash : c0cd3489
    Message : Don't pull in <sys/file.h> just to get fcntl.h ok deraadt@ krw@

  • sys/dev/rd.c
  • /*	$OpenBSD: rd.c,v 1.13 2017/12/30 23:08:29 guenther Exp $	*/
    
    /*
     * Copyright (c) 2011 Matthew Dempsky <matthew@dempsky.org>
     *
     * Permission to use, copy, modify, and distribute this software for any
     * purpose with or without fee is hereby granted, provided that the above
     * copyright notice and this permission notice appear in all copies.
     *
     * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     */
    
    #include <sys/param.h>
    #include <sys/systm.h>
    #include <sys/proc.h>
    #include <sys/errno.h>
    #include <sys/buf.h>
    #include <sys/malloc.h>
    #include <sys/ioctl.h>
    #include <sys/disklabel.h>
    #include <sys/device.h>
    #include <sys/disk.h>
    #include <sys/stat.h>
    #include <sys/fcntl.h>
    #include <sys/uio.h>
    #include <sys/conf.h>
    #include <sys/dkio.h>
    #include <sys/vnode.h>
    
    #ifndef MINIROOTSIZE
    #define MINIROOTSIZE 512
    #endif
    
    #define ROOTBYTES (MINIROOTSIZE << DEV_BSHIFT)
    
    /*
     * This array will be patched to contain a file-system image.
     * See the program:  src/distrib/common/rdsetroot.c
     */
    u_int32_t rd_root_size = ROOTBYTES;
    char rd_root_image[ROOTBYTES] = "|This is the root ramdisk!\n";
    
    void	rdattach(int);
    int	rd_match(struct device *, void *, void *);
    void	rd_attach(struct device *, struct device *, void *);
    int	rd_detach(struct device *, int);
    
    struct rd_softc {
    	struct device	sc_dev;
    	struct disk	sc_dk;
    };
    
    struct cfattach rd_ca = {
    	sizeof(struct rd_softc),
    	rd_match,
    	rd_attach,
    	rd_detach
    };
    
    struct cfdriver rd_cd = {
    	NULL,
    	"rd",
    	DV_DISK
    };
    
    #define rdlookup(unit)	((struct rd_softc *)disk_lookup(&rd_cd, (unit)))
    
    int	rdgetdisklabel(dev_t, struct rd_softc *, struct disklabel *, int);
    
    void
    rdattach(int num)
    {
    	static struct cfdata cf; /* Fake cf. */
    	struct rd_softc *sc;
    	int i;
    
    	/* There's only one rd_root_image, so only attach one rd. */
    	num = 1;
    
    	/* XXX: Fake up more? */
    	cf.cf_attach = &rd_ca;
    	cf.cf_driver = &rd_cd;
    
    	rd_cd.cd_ndevs = num;
    	rd_cd.cd_devs = mallocarray(num, sizeof(void *), M_DEVBUF, M_NOWAIT);
    	if (rd_cd.cd_devs == NULL)
    		panic("rdattach: out of memory");
    
    	for (i = 0; i < num; ++i) {
    		/* Allocate the softc and initialize it. */
    		sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT|M_ZERO);
    		if (sc == NULL)
    			panic("rdattach: out of memory");
    		sc->sc_dev.dv_class = DV_DISK;
    		sc->sc_dev.dv_cfdata = &cf;
    		sc->sc_dev.dv_flags = DVF_ACTIVE;
    		sc->sc_dev.dv_unit = i;
    	        if (snprintf(sc->sc_dev.dv_xname, sizeof(sc->sc_dev.dv_xname),
    		    "rd%d", i) >= sizeof(sc->sc_dev.dv_xname))
    			panic("rdattach: device name too long");
    		sc->sc_dev.dv_ref = 1;
    
    		/* Attach it to the device tree. */
    		rd_cd.cd_devs[i] = sc;
    		TAILQ_INSERT_TAIL(&alldevs, &sc->sc_dev, dv_list);
    		device_ref(&sc->sc_dev);
    
    		/* Finish initializing. */
    		rd_attach(NULL, &sc->sc_dev, NULL);
    	}
    }
    
    int
    rd_match(struct device *parent, void *match, void *aux)
    {
    	return (0);
    }
    
    void
    rd_attach(struct device *parent, struct device *self, void *aux)
    {
    	struct rd_softc *sc = (struct rd_softc *)self;
    
    	/* Attach disk. */
    	sc->sc_dk.dk_name = sc->sc_dev.dv_xname;
    	disk_attach(&sc->sc_dev, &sc->sc_dk);
    }
    
    int
    rd_detach(struct device *self, int flags)
    {
    	struct rd_softc *sc = (struct rd_softc *)self;
    
    	disk_gone(rdopen, self->dv_unit);
    
    	/* Detach disk. */
    	disk_detach(&sc->sc_dk);
    
    	return (0);
    }
    
    int
    rdopen(dev_t dev, int flag, int fmt, struct proc *p)
    {
    	struct rd_softc *sc;
    	u_int unit, part;
    	int error;
    
    	unit = DISKUNIT(dev);
    	part = DISKPART(dev);
    
    	sc = rdlookup(unit);
    	if (sc == NULL)
    		return (ENXIO);
    
    	if ((error = disk_lock(&sc->sc_dk)) != 0)
    		goto unref;
    
    	if (sc->sc_dk.dk_openmask == 0) {
    		/* Load the partition info if not already loaded. */
    		if ((error = rdgetdisklabel(dev, sc, sc->sc_dk.dk_label, 0))
    		    != 0)
    			goto unlock;
    	}
    
    	error = disk_openpart(&sc->sc_dk, part, fmt, 1);
    
     unlock:
    	disk_unlock(&sc->sc_dk);
     unref:
    	device_unref(&sc->sc_dev);
    	return (error);
    }
    
    int
    rdclose(dev_t dev, int flag, int fmt, struct proc *p)
    {
    	struct rd_softc *sc;
    	u_int unit, part;
    
    	unit = DISKUNIT(dev);
    	part = DISKPART(dev);
    
    	sc = rdlookup(unit);
    	if (sc == NULL)
    		return (ENXIO);
    
    	disk_lock_nointr(&sc->sc_dk);
    
    	disk_closepart(&sc->sc_dk, part, fmt);
    
    	disk_unlock(&sc->sc_dk);
    	device_unref(&sc->sc_dev);
    	return (0);
    }
    
    void
    rdstrategy(struct buf *bp)
    {
    	struct rd_softc *sc;
    	struct partition *p;
    	size_t off, xfer;
    	caddr_t addr;
    	int s;
    
    	sc = rdlookup(DISKUNIT(bp->b_dev));
    	if (sc == NULL) {
    		bp->b_error = ENXIO;
    		goto bad;
    	}
    
    	/* Validate the request. */
    	if (bounds_check_with_label(bp, sc->sc_dk.dk_label) == -1)
    		goto done;
    
    	/* Do the transfer. */
    	/* XXX: Worry about overflow when computing off? */
    
    	p = &sc->sc_dk.dk_label->d_partitions[DISKPART(bp->b_dev)];
    	off = DL_GETPOFFSET(p) * sc->sc_dk.dk_label->d_secsize +
    	    (u_int64_t)bp->b_blkno * DEV_BSIZE;
    	if (off > rd_root_size)
    		off = rd_root_size;
    	xfer = bp->b_bcount;
    	if (xfer > rd_root_size - off)
    		xfer = rd_root_size - off;
    	addr = rd_root_image + off;
    	if (bp->b_flags & B_READ)
    		memcpy(bp->b_data, addr, xfer);
    	else
    		memcpy(addr, bp->b_data, xfer);
    	bp->b_resid = bp->b_bcount - xfer;
    	goto done;
    
     bad:
    	bp->b_flags |= B_ERROR;
    	bp->b_resid = bp->b_bcount;
     done:
    	s = splbio();
    	biodone(bp);
    	splx(s);
    	if (sc != NULL)
    		device_unref(&sc->sc_dev);
    }
    
    int
    rdioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p)
    {
    	struct rd_softc *sc;
    	struct disklabel *lp;
    	int error = 0;
    
    	sc = rdlookup(DISKUNIT(dev));
    	if (sc == NULL)
    		return (ENXIO);
    
    	switch (cmd) {
    	case DIOCRLDINFO:
    		lp = malloc(sizeof(*lp), M_TEMP, M_WAITOK);
    		rdgetdisklabel(dev, sc, lp, 0);
    		memcpy(sc->sc_dk.dk_label, lp, sizeof(*lp));
    		free(lp, M_TEMP, sizeof(*lp));
    		goto done;
    
    	case DIOCGPDINFO:
    		rdgetdisklabel(dev, sc, (struct disklabel *)data, 1);
    		goto done;
    
    	case DIOCGDINFO:
    		*(struct disklabel *)data = *(sc->sc_dk.dk_label);
    		goto done;
    
    	case DIOCGPART:
    		((struct partinfo *)data)->disklab = sc->sc_dk.dk_label;
    		((struct partinfo *)data)->part =
    		    &sc->sc_dk.dk_label->d_partitions[DISKPART(dev)];
    		goto done;
    
    	case DIOCWDINFO:
    	case DIOCSDINFO:
    		if ((fflag & FWRITE) == 0) {
    			error = EBADF;
    			goto done;
    		}
    
    		if ((error = disk_lock(&sc->sc_dk)) != 0)
    			goto done;
    
    		error = setdisklabel(sc->sc_dk.dk_label,
    		    (struct disklabel *)data, sc->sc_dk.dk_openmask);
    		if (error == 0) {
    			if (cmd == DIOCWDINFO)
    				error = writedisklabel(DISKLABELDEV(dev),
    				    rdstrategy, sc->sc_dk.dk_label);
    		}
    
    		disk_unlock(&sc->sc_dk);
    		goto done;
    	}
    
     done:
    	device_unref(&sc->sc_dev);
    	return (error);
    }
    
    int
    rdgetdisklabel(dev_t dev, struct rd_softc *sc, struct disklabel *lp,
        int spoofonly)
    {
    	bzero(lp, sizeof(struct disklabel));
    
    	lp->d_secsize = DEV_BSIZE;
    	lp->d_ntracks = 1;
    	lp->d_nsectors = rd_root_size >> DEV_BSHIFT;
    	lp->d_ncylinders = 1;
    	lp->d_secpercyl = lp->d_nsectors;
    	if (lp->d_secpercyl == 0) {
    		lp->d_secpercyl = 100;
    		/* as long as it's not 0 - readdisklabel divides by it */
    	}
    
    	strncpy(lp->d_typename, "RAM disk", sizeof(lp->d_typename));
    	lp->d_type = DTYPE_SCSI;
    	strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
    	DL_SETDSIZE(lp, lp->d_nsectors);
    	lp->d_version = 1;
    
    	lp->d_magic = DISKMAGIC;
    	lp->d_magic2 = DISKMAGIC;
    	lp->d_checksum = dkcksum(lp);
    
    	/* Call the generic disklabel extraction routine. */
    	return (readdisklabel(DISKLABELDEV(dev), rdstrategy, lp, spoofonly));
    }
    
    int
    rdread(dev_t dev, struct uio *uio, int ioflag)
    {
    	return (physio(rdstrategy, dev, B_READ, minphys, uio));
    }
    
    int
    rdwrite(dev_t dev, struct uio *uio, int ioflag)
    {
    	return (physio(rdstrategy, dev, B_WRITE, minphys, uio));
    }
    
    int
    rddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
    {
    	return (ENXIO);
    }
    
    daddr_t
    rdsize(dev_t dev)
    {
    	return (-1);
    }