xref: /openbsd-src/sys/dev/rd.c (revision 471aeecfc619bc9b69519928152daf993376c2a1)
1*471aeecfSnaddy /*	$OpenBSD: rd.c,v 1.14 2022/04/06 18:59:27 naddy Exp $	*/
2e040ab8cSderaadt 
3e040ab8cSderaadt /*
468d84d28Smatthew  * Copyright (c) 2011 Matthew Dempsky <matthew@dempsky.org>
5e040ab8cSderaadt  *
668d84d28Smatthew  * Permission to use, copy, modify, and distribute this software for any
768d84d28Smatthew  * purpose with or without fee is hereby granted, provided that the above
868d84d28Smatthew  * copyright notice and this permission notice appear in all copies.
9e040ab8cSderaadt  *
1068d84d28Smatthew  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1168d84d28Smatthew  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1268d84d28Smatthew  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1368d84d28Smatthew  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1468d84d28Smatthew  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1568d84d28Smatthew  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1668d84d28Smatthew  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17e040ab8cSderaadt  */
18e040ab8cSderaadt 
19e040ab8cSderaadt #include <sys/param.h>
20e040ab8cSderaadt #include <sys/systm.h>
2168d84d28Smatthew #include <sys/proc.h>
2268d84d28Smatthew #include <sys/errno.h>
2368d84d28Smatthew #include <sys/buf.h>
2468d84d28Smatthew #include <sys/malloc.h>
2568d84d28Smatthew #include <sys/ioctl.h>
2668d84d28Smatthew #include <sys/disklabel.h>
2768d84d28Smatthew #include <sys/device.h>
2868d84d28Smatthew #include <sys/disk.h>
2968d84d28Smatthew #include <sys/stat.h>
30c0cd3489Sguenther #include <sys/fcntl.h>
3168d84d28Smatthew #include <sys/uio.h>
3268d84d28Smatthew #include <sys/conf.h>
3368d84d28Smatthew #include <sys/dkio.h>
3468d84d28Smatthew #include <sys/vnode.h>
35e040ab8cSderaadt 
36e040ab8cSderaadt #ifndef MINIROOTSIZE
37e040ab8cSderaadt #define MINIROOTSIZE 512
38e040ab8cSderaadt #endif
39e040ab8cSderaadt 
40e040ab8cSderaadt #define ROOTBYTES (MINIROOTSIZE << DEV_BSHIFT)
41e040ab8cSderaadt 
42e040ab8cSderaadt /*
43e040ab8cSderaadt  * This array will be patched to contain a file-system image.
44e040ab8cSderaadt  * See the program:  src/distrib/common/rdsetroot.c
45e040ab8cSderaadt  */
46e040ab8cSderaadt u_int32_t rd_root_size = ROOTBYTES;
47e040ab8cSderaadt char rd_root_image[ROOTBYTES] = "|This is the root ramdisk!\n";
48e040ab8cSderaadt 
4968d84d28Smatthew void	rdattach(int);
5068d84d28Smatthew int	rd_match(struct device *, void *, void *);
5168d84d28Smatthew void	rd_attach(struct device *, struct device *, void *);
5268d84d28Smatthew int	rd_detach(struct device *, int);
5368d84d28Smatthew 
5468d84d28Smatthew struct rd_softc {
5568d84d28Smatthew 	struct device	sc_dev;
5668d84d28Smatthew 	struct disk	sc_dk;
5768d84d28Smatthew };
5868d84d28Smatthew 
59*471aeecfSnaddy const struct cfattach rd_ca = {
6068d84d28Smatthew 	sizeof(struct rd_softc),
6168d84d28Smatthew 	rd_match,
6268d84d28Smatthew 	rd_attach,
6368d84d28Smatthew 	rd_detach
6468d84d28Smatthew };
6568d84d28Smatthew 
6668d84d28Smatthew struct cfdriver rd_cd = {
6768d84d28Smatthew 	NULL,
6868d84d28Smatthew 	"rd",
6968d84d28Smatthew 	DV_DISK
7068d84d28Smatthew };
7168d84d28Smatthew 
7268d84d28Smatthew #define rdlookup(unit)	((struct rd_softc *)disk_lookup(&rd_cd, (unit)))
7368d84d28Smatthew 
7468d84d28Smatthew int	rdgetdisklabel(dev_t, struct rd_softc *, struct disklabel *, int);
7568d84d28Smatthew 
76e040ab8cSderaadt void
rdattach(int num)7768d84d28Smatthew rdattach(int num)
78e040ab8cSderaadt {
7968d84d28Smatthew 	static struct cfdata cf; /* Fake cf. */
8068d84d28Smatthew 	struct rd_softc *sc;
8168d84d28Smatthew 	int i;
8268d84d28Smatthew 
8368d84d28Smatthew 	/* There's only one rd_root_image, so only attach one rd. */
8468d84d28Smatthew 	num = 1;
8568d84d28Smatthew 
8668d84d28Smatthew 	/* XXX: Fake up more? */
8768d84d28Smatthew 	cf.cf_attach = &rd_ca;
8868d84d28Smatthew 	cf.cf_driver = &rd_cd;
8968d84d28Smatthew 
9068d84d28Smatthew 	rd_cd.cd_ndevs = num;
919f6fb5c7Sderaadt 	rd_cd.cd_devs = mallocarray(num, sizeof(void *), M_DEVBUF, M_NOWAIT);
9268d84d28Smatthew 	if (rd_cd.cd_devs == NULL)
9368d84d28Smatthew 		panic("rdattach: out of memory");
9468d84d28Smatthew 
9568d84d28Smatthew 	for (i = 0; i < num; ++i) {
9668d84d28Smatthew 		/* Allocate the softc and initialize it. */
9768d84d28Smatthew 		sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT|M_ZERO);
9868d84d28Smatthew 		if (sc == NULL)
9968d84d28Smatthew 			panic("rdattach: out of memory");
10068d84d28Smatthew 		sc->sc_dev.dv_class = DV_DISK;
10168d84d28Smatthew 		sc->sc_dev.dv_cfdata = &cf;
10268d84d28Smatthew 		sc->sc_dev.dv_flags = DVF_ACTIVE;
10368d84d28Smatthew 		sc->sc_dev.dv_unit = i;
10468d84d28Smatthew 	        if (snprintf(sc->sc_dev.dv_xname, sizeof(sc->sc_dev.dv_xname),
10568d84d28Smatthew 		    "rd%d", i) >= sizeof(sc->sc_dev.dv_xname))
10668d84d28Smatthew 			panic("rdattach: device name too long");
107d6568aefSdlg 		sc->sc_dev.dv_ref = 1;
10868d84d28Smatthew 
10968d84d28Smatthew 		/* Attach it to the device tree. */
11068d84d28Smatthew 		rd_cd.cd_devs[i] = sc;
11168d84d28Smatthew 		TAILQ_INSERT_TAIL(&alldevs, &sc->sc_dev, dv_list);
11268d84d28Smatthew 		device_ref(&sc->sc_dev);
11368d84d28Smatthew 
11468d84d28Smatthew 		/* Finish initializing. */
11568d84d28Smatthew 		rd_attach(NULL, &sc->sc_dev, NULL);
116e040ab8cSderaadt 	}
117e040ab8cSderaadt }
118e040ab8cSderaadt 
11968d84d28Smatthew int
rd_match(struct device * parent,void * match,void * aux)12068d84d28Smatthew rd_match(struct device *parent, void *match, void *aux)
121e040ab8cSderaadt {
12268d84d28Smatthew 	return (0);
12368d84d28Smatthew }
12468d84d28Smatthew 
12568d84d28Smatthew void
rd_attach(struct device * parent,struct device * self,void * aux)12668d84d28Smatthew rd_attach(struct device *parent, struct device *self, void *aux)
12768d84d28Smatthew {
12868d84d28Smatthew 	struct rd_softc *sc = (struct rd_softc *)self;
12968d84d28Smatthew 
13068d84d28Smatthew 	/* Attach disk. */
13168d84d28Smatthew 	sc->sc_dk.dk_name = sc->sc_dev.dv_xname;
13268d84d28Smatthew 	disk_attach(&sc->sc_dev, &sc->sc_dk);
13368d84d28Smatthew }
13468d84d28Smatthew 
13568d84d28Smatthew int
rd_detach(struct device * self,int flags)13668d84d28Smatthew rd_detach(struct device *self, int flags)
13768d84d28Smatthew {
13868d84d28Smatthew 	struct rd_softc *sc = (struct rd_softc *)self;
13968d84d28Smatthew 
140e1d64023Smatthew 	disk_gone(rdopen, self->dv_unit);
14168d84d28Smatthew 
14268d84d28Smatthew 	/* Detach disk. */
14368d84d28Smatthew 	disk_detach(&sc->sc_dk);
14468d84d28Smatthew 
14568d84d28Smatthew 	return (0);
14668d84d28Smatthew }
14768d84d28Smatthew 
14868d84d28Smatthew int
rdopen(dev_t dev,int flag,int fmt,struct proc * p)14968d84d28Smatthew rdopen(dev_t dev, int flag, int fmt, struct proc *p)
15068d84d28Smatthew {
15168d84d28Smatthew 	struct rd_softc *sc;
15268d84d28Smatthew 	u_int unit, part;
15368d84d28Smatthew 	int error;
15468d84d28Smatthew 
15568d84d28Smatthew 	unit = DISKUNIT(dev);
15668d84d28Smatthew 	part = DISKPART(dev);
15768d84d28Smatthew 
15868d84d28Smatthew 	sc = rdlookup(unit);
15968d84d28Smatthew 	if (sc == NULL)
16068d84d28Smatthew 		return (ENXIO);
16168d84d28Smatthew 
16268d84d28Smatthew 	if ((error = disk_lock(&sc->sc_dk)) != 0)
16368d84d28Smatthew 		goto unref;
16468d84d28Smatthew 
16568d84d28Smatthew 	if (sc->sc_dk.dk_openmask == 0) {
16668d84d28Smatthew 		/* Load the partition info if not already loaded. */
16768d84d28Smatthew 		if ((error = rdgetdisklabel(dev, sc, sc->sc_dk.dk_label, 0))
16868d84d28Smatthew 		    != 0)
16968d84d28Smatthew 			goto unlock;
17068d84d28Smatthew 	}
17168d84d28Smatthew 
172e1d64023Smatthew 	error = disk_openpart(&sc->sc_dk, part, fmt, 1);
17368d84d28Smatthew 
17468d84d28Smatthew  unlock:
17568d84d28Smatthew 	disk_unlock(&sc->sc_dk);
17668d84d28Smatthew  unref:
17768d84d28Smatthew 	device_unref(&sc->sc_dev);
17868d84d28Smatthew 	return (error);
17968d84d28Smatthew }
18068d84d28Smatthew 
18168d84d28Smatthew int
rdclose(dev_t dev,int flag,int fmt,struct proc * p)18268d84d28Smatthew rdclose(dev_t dev, int flag, int fmt, struct proc *p)
18368d84d28Smatthew {
18468d84d28Smatthew 	struct rd_softc *sc;
18568d84d28Smatthew 	u_int unit, part;
18668d84d28Smatthew 
18768d84d28Smatthew 	unit = DISKUNIT(dev);
18868d84d28Smatthew 	part = DISKPART(dev);
18968d84d28Smatthew 
19068d84d28Smatthew 	sc = rdlookup(unit);
19168d84d28Smatthew 	if (sc == NULL)
19268d84d28Smatthew 		return (ENXIO);
19368d84d28Smatthew 
19468d84d28Smatthew 	disk_lock_nointr(&sc->sc_dk);
19568d84d28Smatthew 
196e1d64023Smatthew 	disk_closepart(&sc->sc_dk, part, fmt);
19768d84d28Smatthew 
19868d84d28Smatthew 	disk_unlock(&sc->sc_dk);
19968d84d28Smatthew 	device_unref(&sc->sc_dev);
20068d84d28Smatthew 	return (0);
20168d84d28Smatthew }
20268d84d28Smatthew 
20368d84d28Smatthew void
rdstrategy(struct buf * bp)20468d84d28Smatthew rdstrategy(struct buf *bp)
20568d84d28Smatthew {
20668d84d28Smatthew 	struct rd_softc *sc;
20768d84d28Smatthew 	struct partition *p;
20868d84d28Smatthew 	size_t off, xfer;
20968d84d28Smatthew 	caddr_t addr;
21068d84d28Smatthew 	int s;
21168d84d28Smatthew 
21268d84d28Smatthew 	sc = rdlookup(DISKUNIT(bp->b_dev));
21368d84d28Smatthew 	if (sc == NULL) {
21468d84d28Smatthew 		bp->b_error = ENXIO;
21568d84d28Smatthew 		goto bad;
21668d84d28Smatthew 	}
21768d84d28Smatthew 
218d1e5b14cSmatthew 	/* Validate the request. */
219d1e5b14cSmatthew 	if (bounds_check_with_label(bp, sc->sc_dk.dk_label) == -1)
22068d84d28Smatthew 		goto done;
22168d84d28Smatthew 
22268d84d28Smatthew 	/* Do the transfer. */
22368d84d28Smatthew 	/* XXX: Worry about overflow when computing off? */
22468d84d28Smatthew 
22568d84d28Smatthew 	p = &sc->sc_dk.dk_label->d_partitions[DISKPART(bp->b_dev)];
22668d84d28Smatthew 	off = DL_GETPOFFSET(p) * sc->sc_dk.dk_label->d_secsize +
22768d84d28Smatthew 	    (u_int64_t)bp->b_blkno * DEV_BSIZE;
22868d84d28Smatthew 	if (off > rd_root_size)
22968d84d28Smatthew 		off = rd_root_size;
23068d84d28Smatthew 	xfer = bp->b_bcount;
23168d84d28Smatthew 	if (xfer > rd_root_size - off)
23268d84d28Smatthew 		xfer = rd_root_size - off;
23368d84d28Smatthew 	addr = rd_root_image + off;
23468d84d28Smatthew 	if (bp->b_flags & B_READ)
23568d84d28Smatthew 		memcpy(bp->b_data, addr, xfer);
23668d84d28Smatthew 	else
23768d84d28Smatthew 		memcpy(addr, bp->b_data, xfer);
23868d84d28Smatthew 	bp->b_resid = bp->b_bcount - xfer;
23968d84d28Smatthew 	goto done;
24068d84d28Smatthew 
24168d84d28Smatthew  bad:
24268d84d28Smatthew 	bp->b_flags |= B_ERROR;
243d1e5b14cSmatthew 	bp->b_resid = bp->b_bcount;
24468d84d28Smatthew  done:
24568d84d28Smatthew 	s = splbio();
24668d84d28Smatthew 	biodone(bp);
24768d84d28Smatthew 	splx(s);
248d1e5b14cSmatthew 	if (sc != NULL)
24968d84d28Smatthew 		device_unref(&sc->sc_dev);
25068d84d28Smatthew }
25168d84d28Smatthew 
25268d84d28Smatthew int
rdioctl(dev_t dev,u_long cmd,caddr_t data,int fflag,struct proc * p)25368d84d28Smatthew rdioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p)
25468d84d28Smatthew {
25568d84d28Smatthew 	struct rd_softc *sc;
25668d84d28Smatthew 	struct disklabel *lp;
25768d84d28Smatthew 	int error = 0;
25868d84d28Smatthew 
25968d84d28Smatthew 	sc = rdlookup(DISKUNIT(dev));
26068d84d28Smatthew 	if (sc == NULL)
26168d84d28Smatthew 		return (ENXIO);
26268d84d28Smatthew 
26368d84d28Smatthew 	switch (cmd) {
26468d84d28Smatthew 	case DIOCRLDINFO:
26568d84d28Smatthew 		lp = malloc(sizeof(*lp), M_TEMP, M_WAITOK);
26668d84d28Smatthew 		rdgetdisklabel(dev, sc, lp, 0);
267073b8838Sderaadt 		memcpy(sc->sc_dk.dk_label, lp, sizeof(*lp));
26801527b18Sderaadt 		free(lp, M_TEMP, sizeof(*lp));
26968d84d28Smatthew 		goto done;
27068d84d28Smatthew 
27168d84d28Smatthew 	case DIOCGPDINFO:
27268d84d28Smatthew 		rdgetdisklabel(dev, sc, (struct disklabel *)data, 1);
27368d84d28Smatthew 		goto done;
27468d84d28Smatthew 
27568d84d28Smatthew 	case DIOCGDINFO:
27668d84d28Smatthew 		*(struct disklabel *)data = *(sc->sc_dk.dk_label);
27768d84d28Smatthew 		goto done;
27868d84d28Smatthew 
27968d84d28Smatthew 	case DIOCGPART:
28068d84d28Smatthew 		((struct partinfo *)data)->disklab = sc->sc_dk.dk_label;
28168d84d28Smatthew 		((struct partinfo *)data)->part =
28268d84d28Smatthew 		    &sc->sc_dk.dk_label->d_partitions[DISKPART(dev)];
28368d84d28Smatthew 		goto done;
28468d84d28Smatthew 
28568d84d28Smatthew 	case DIOCWDINFO:
28668d84d28Smatthew 	case DIOCSDINFO:
28768d84d28Smatthew 		if ((fflag & FWRITE) == 0) {
28868d84d28Smatthew 			error = EBADF;
28968d84d28Smatthew 			goto done;
29068d84d28Smatthew 		}
29168d84d28Smatthew 
29268d84d28Smatthew 		if ((error = disk_lock(&sc->sc_dk)) != 0)
29368d84d28Smatthew 			goto done;
29468d84d28Smatthew 
29568d84d28Smatthew 		error = setdisklabel(sc->sc_dk.dk_label,
29668d84d28Smatthew 		    (struct disklabel *)data, sc->sc_dk.dk_openmask);
29768d84d28Smatthew 		if (error == 0) {
29868d84d28Smatthew 			if (cmd == DIOCWDINFO)
29968d84d28Smatthew 				error = writedisklabel(DISKLABELDEV(dev),
30068d84d28Smatthew 				    rdstrategy, sc->sc_dk.dk_label);
30168d84d28Smatthew 		}
30268d84d28Smatthew 
30368d84d28Smatthew 		disk_unlock(&sc->sc_dk);
30468d84d28Smatthew 		goto done;
30568d84d28Smatthew 	}
30668d84d28Smatthew 
30768d84d28Smatthew  done:
30868d84d28Smatthew 	device_unref(&sc->sc_dev);
30968d84d28Smatthew 	return (error);
31068d84d28Smatthew }
31168d84d28Smatthew 
31268d84d28Smatthew int
rdgetdisklabel(dev_t dev,struct rd_softc * sc,struct disklabel * lp,int spoofonly)31368d84d28Smatthew rdgetdisklabel(dev_t dev, struct rd_softc *sc, struct disklabel *lp,
31468d84d28Smatthew     int spoofonly)
31568d84d28Smatthew {
31668d84d28Smatthew 	bzero(lp, sizeof(struct disklabel));
31768d84d28Smatthew 
31868d84d28Smatthew 	lp->d_secsize = DEV_BSIZE;
31968d84d28Smatthew 	lp->d_ntracks = 1;
32068d84d28Smatthew 	lp->d_nsectors = rd_root_size >> DEV_BSHIFT;
32168d84d28Smatthew 	lp->d_ncylinders = 1;
32268d84d28Smatthew 	lp->d_secpercyl = lp->d_nsectors;
32368d84d28Smatthew 	if (lp->d_secpercyl == 0) {
32468d84d28Smatthew 		lp->d_secpercyl = 100;
32568d84d28Smatthew 		/* as long as it's not 0 - readdisklabel divides by it */
32668d84d28Smatthew 	}
32768d84d28Smatthew 
32868d84d28Smatthew 	strncpy(lp->d_typename, "RAM disk", sizeof(lp->d_typename));
32968d84d28Smatthew 	lp->d_type = DTYPE_SCSI;
33068d84d28Smatthew 	strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
33168d84d28Smatthew 	DL_SETDSIZE(lp, lp->d_nsectors);
33268d84d28Smatthew 	lp->d_version = 1;
33368d84d28Smatthew 
33468d84d28Smatthew 	lp->d_magic = DISKMAGIC;
33568d84d28Smatthew 	lp->d_magic2 = DISKMAGIC;
33668d84d28Smatthew 	lp->d_checksum = dkcksum(lp);
33768d84d28Smatthew 
33868d84d28Smatthew 	/* Call the generic disklabel extraction routine. */
33968d84d28Smatthew 	return (readdisklabel(DISKLABELDEV(dev), rdstrategy, lp, spoofonly));
34068d84d28Smatthew }
34168d84d28Smatthew 
34268d84d28Smatthew int
rdread(dev_t dev,struct uio * uio,int ioflag)34368d84d28Smatthew rdread(dev_t dev, struct uio *uio, int ioflag)
34468d84d28Smatthew {
34568d84d28Smatthew 	return (physio(rdstrategy, dev, B_READ, minphys, uio));
34668d84d28Smatthew }
34768d84d28Smatthew 
34868d84d28Smatthew int
rdwrite(dev_t dev,struct uio * uio,int ioflag)34968d84d28Smatthew rdwrite(dev_t dev, struct uio *uio, int ioflag)
35068d84d28Smatthew {
35168d84d28Smatthew 	return (physio(rdstrategy, dev, B_WRITE, minphys, uio));
35268d84d28Smatthew }
35368d84d28Smatthew 
35468d84d28Smatthew int
rddump(dev_t dev,daddr_t blkno,caddr_t va,size_t size)3551abdbfdeSderaadt rddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
35668d84d28Smatthew {
35768d84d28Smatthew 	return (ENXIO);
35868d84d28Smatthew }
35968d84d28Smatthew 
3601abdbfdeSderaadt daddr_t
rdsize(dev_t dev)36168d84d28Smatthew rdsize(dev_t dev)
36268d84d28Smatthew {
36368d84d28Smatthew 	return (-1);
364e040ab8cSderaadt }
365