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