| 1 | /* | 
| 2 |  * Copyright (c) 2004-2019 Apple Inc. All rights reserved. | 
| 3 |  * | 
| 4 |  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | 
| 5 |  * | 
| 6 |  * This file contains Original Code and/or Modifications of Original Code | 
| 7 |  * as defined in and that are subject to the Apple Public Source License | 
| 8 |  * Version 2.0 (the 'License'). You may not use this file except in | 
| 9 |  * compliance with the License. The rights granted to you under the License | 
| 10 |  * may not be used to create, or enable the creation or redistribution of, | 
| 11 |  * unlawful or unlicensed copies of an Apple operating system, or to | 
| 12 |  * circumvent, violate, or enable the circumvention or violation of, any | 
| 13 |  * terms of an Apple operating system software license agreement. | 
| 14 |  * | 
| 15 |  * Please obtain a copy of the License at | 
| 16 |  * http://www.opensource.apple.com/apsl/ and read it before using this file. | 
| 17 |  * | 
| 18 |  * The Original Code and all software distributed under the License are | 
| 19 |  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | 
| 20 |  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | 
| 21 |  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | 
| 22 |  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | 
| 23 |  * Please see the License for the specific language governing rights and | 
| 24 |  * limitations under the License. | 
| 25 |  * | 
| 26 |  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | 
| 27 |  */ | 
| 28 | /* | 
| 29 |  * Copyright (c) 1988 University of Utah. | 
| 30 |  * Copyright (c) 1990, 1993 | 
| 31 |  *	The Regents of the University of California.  All rights reserved. | 
| 32 |  * | 
| 33 |  * This code is derived from software contributed to Berkeley by | 
| 34 |  * the Systems Programming Group of the University of Utah Computer | 
| 35 |  * Science Department. | 
| 36 |  * | 
| 37 |  * Redistribution and use in source and binary forms, with or without | 
| 38 |  * modification, are permitted provided that the following conditions | 
| 39 |  * are met: | 
| 40 |  * 1. Redistributions of source code must retain the above copyright | 
| 41 |  *    notice, this list of conditions and the following disclaimer. | 
| 42 |  * 2. Redistributions in binary form must reproduce the above copyright | 
| 43 |  *    notice, this list of conditions and the following disclaimer in the | 
| 44 |  *    documentation and/or other materials provided with the distribution. | 
| 45 |  * 3. All advertising materials mentioning features or use of this software | 
| 46 |  *    must display the following acknowledgement: | 
| 47 |  *	This product includes software developed by the University of | 
| 48 |  *	California, Berkeley and its contributors. | 
| 49 |  * 4. Neither the name of the University nor the names of its contributors | 
| 50 |  *    may be used to endorse or promote products derived from this software | 
| 51 |  *    without specific prior written permission. | 
| 52 |  * | 
| 53 |  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 
| 54 |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
| 55 |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
| 56 |  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 
| 57 |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
| 58 |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 
| 59 |  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 
| 60 |  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
| 61 |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 
| 62 |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
| 63 |  * SUCH DAMAGE. | 
| 64 |  * | 
| 65 |  * from: Utah Hdr: vn.c 1.13 94/04/02 | 
| 66 |  * | 
| 67 |  *	from: @(#)vn.c	8.6 (Berkeley) 4/1/94 | 
| 68 |  * $FreeBSD: src/sys/dev/vn/vn.c,v 1.105.2.4 2001/11/18 07:11:00 dillon Exp $ | 
| 69 |  */ | 
| 70 |  | 
| 71 | /* | 
| 72 |  * RAM disk driver. | 
| 73 |  * | 
| 74 |  * Block interface to a ramdisk. | 
| 75 |  * | 
| 76 |  */ | 
| 77 |  | 
| 78 | #include <sys/param.h> | 
| 79 | #include <sys/kernel.h> | 
| 80 | #include <sys/mount.h> | 
| 81 | #include <sys/namei.h> | 
| 82 | #include <sys/proc.h> | 
| 83 | #include <sys/buf.h> | 
| 84 | #include <sys/malloc.h> | 
| 85 | #include <sys/mount.h> | 
| 86 | #include <sys/fcntl.h> | 
| 87 | #include <sys/conf.h> | 
| 88 | #include <sys/disk.h> | 
| 89 | #include <sys/stat.h> | 
| 90 | #include <sys/vm.h> | 
| 91 | #include <sys/uio_internal.h> | 
| 92 | #include <libkern/libkern.h> | 
| 93 |  | 
| 94 | #include <vm/pmap.h> | 
| 95 | #include <vm/vm_pager.h> | 
| 96 | #include <mach/memory_object_types.h> | 
| 97 | #include <kern/debug.h> | 
| 98 |  | 
| 99 | #include <miscfs/devfs/devfs.h> | 
| 100 |  | 
| 101 |  | 
| 102 | void            mdevinit(int the_cnt); | 
| 103 |  | 
| 104 | static open_close_fcn_t mdevopen; | 
| 105 | static open_close_fcn_t mdevclose; | 
| 106 | static psize_fcn_t              mdevsize; | 
| 107 | static strategy_fcn_t   mdevstrategy; | 
| 108 | static int                              mdevbioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p); | 
| 109 | static int                              mdevcioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p); | 
| 110 | static int                              mdevrw(dev_t dev, struct uio *uio, int ioflag); | 
| 111 |  | 
| 112 | #ifdef CONFIG_MEMDEV_INSECURE | 
| 113 | static char *                   nonspace(char *pos, char *end); | 
| 114 | static char *                   getspace(char *pos, char *end); | 
| 115 | static char *                   cvtnum(char *pos, char *end, uint64_t *num); | 
| 116 | #endif /* CONFIG_MEMDEV_INSECURE */ | 
| 117 |  | 
| 118 | extern void             bcopy_phys(addr64_t from, addr64_t to, vm_size_t bytes); | 
| 119 | extern void             mapping_set_mod(ppnum_t pn); | 
| 120 | extern ppnum_t  pmap_find_phys(pmap_t pmap, addr64_t va); | 
| 121 |  | 
| 122 | /* | 
| 123 |  * Maximal number of memory devices. | 
| 124 |  */ | 
| 125 | #define NB_MAX_MDEVICES (16) | 
| 126 |  | 
| 127 | /* | 
| 128 |  * cdevsw | 
| 129 |  *	D_DISK		we want to look like a disk | 
| 130 |  *	D_CANFREE	We support B_FREEBUF | 
| 131 |  */ | 
| 132 |  | 
| 133 | static const struct bdevsw mdevbdevsw = { | 
| 134 | 	.d_open     = mdevopen, | 
| 135 | 	.d_close    = mdevclose, | 
| 136 | 	.d_strategy = mdevstrategy, | 
| 137 | 	.d_ioctl    = mdevbioctl, | 
| 138 | 	.d_dump     = eno_dump, | 
| 139 | 	.d_psize    = mdevsize, | 
| 140 | 	.d_type     = D_DISK, | 
| 141 | }; | 
| 142 |  | 
| 143 | static const struct cdevsw mdevcdevsw = { | 
| 144 | 	.d_open       = mdevopen, | 
| 145 | 	.d_close      = mdevclose, | 
| 146 | 	.d_read       = mdevrw, | 
| 147 | 	.d_write      = mdevrw, | 
| 148 | 	.d_ioctl      = mdevcioctl, | 
| 149 | 	.d_stop       = eno_stop, | 
| 150 | 	.d_reset      = eno_reset, | 
| 151 | 	.d_ttys       = NULL, | 
| 152 | 	.d_select     = eno_select, | 
| 153 | 	.d_mmap       = eno_mmap, | 
| 154 | 	.d_strategy   = eno_strat, | 
| 155 | 	.d_reserved_1 = eno_getc, | 
| 156 | 	.d_reserved_2 = eno_putc, | 
| 157 | 	.d_type       = D_DISK, | 
| 158 | }; | 
| 159 |  | 
| 160 | struct mdev { | 
| 161 | 	uint64_t        mdBase;         /* base page number (pages are assumed to be 4K). Multiply by 4096 to find actual address */ | 
| 162 | 	uint32_t        mdSize;         /* size in pages (pages are assumed to be 4K). Multiply by 4096 to find actual size. */ | 
| 163 | 	int                     mdFlags;        /* flags */ | 
| 164 | 	int                     mdSecsize;      /* sector size */ | 
| 165 | 	int                     mdBDev;         /* Block device number */ | 
| 166 | 	int                     mdCDev;         /* Character device number */ | 
| 167 | 	void *          mdbdevb; | 
| 168 | 	void *          mdcdevb; | 
| 169 | } mdev[NB_MAX_MDEVICES]; | 
| 170 |  | 
| 171 | /* mdFlags */ | 
| 172 | #define mdInited        0x01    /* This device defined */ | 
| 173 | #define mdRO            0x02    /* This device is read-only */ | 
| 174 | #define mdPhys          0x04    /* This device is in physical memory */ | 
| 175 |  | 
| 176 | int mdevBMajor = -1; | 
| 177 | int mdevCMajor = -1; | 
| 178 |  | 
| 179 | static int      mdevioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p, int is_char); | 
| 180 | dev_t           mdevadd(int devid, uint64_t base, unsigned int size, int phys); | 
| 181 | dev_t           mdevlookup(int devid); | 
| 182 | void            mdevremoveall(void); | 
| 183 | int             mdevgetrange(int devid, uint64_t *base, uint64_t *size); | 
| 184 |  | 
| 185 | static  int | 
| 186 | mdevclose(__unused dev_t dev, __unused int flags, | 
| 187 |     __unused int devtype, __unused struct proc *p) | 
| 188 | { | 
| 189 | 	return 0; | 
| 190 | } | 
| 191 |  | 
| 192 | static  int | 
| 193 | mdevopen(dev_t dev, int flags, __unused int devtype, __unused struct proc *p) | 
| 194 | { | 
| 195 | 	int devid; | 
| 196 |  | 
| 197 | 	devid = minor(dev);                                                                     /* Get minor device number */ | 
| 198 |  | 
| 199 | 	if (devid >= NB_MAX_MDEVICES || devid < 0) { | 
| 200 | 		return ENXIO;                                                                 /* Not valid */ | 
| 201 | 	} | 
| 202 | 	if ((flags & FWRITE) && (mdev[devid].mdFlags & mdRO)) { | 
| 203 | 		return EACCES;                                                /* Currently mounted RO */ | 
| 204 | 	} | 
| 205 | 	return 0; | 
| 206 | } | 
| 207 |  | 
| 208 | static int | 
| 209 | mdevrw(dev_t dev, struct uio *uio, __unused int ioflag) | 
| 210 | { | 
| 211 | 	int                     status; | 
| 212 | 	addr64_t                mdata; | 
| 213 | 	int                     devid; | 
| 214 | 	enum uio_seg            saveflag; | 
| 215 | 	int                     count; | 
| 216 |  | 
| 217 | 	devid = minor(dev);                                                                     /* Get minor device number */ | 
| 218 |  | 
| 219 | 	if (devid >= NB_MAX_MDEVICES || devid < 0) { | 
| 220 | 		return ENXIO;                                                                 /* Not valid */ | 
| 221 | 	} | 
| 222 | 	if (!(mdev[devid].mdFlags & mdInited)) { | 
| 223 | 		return ENXIO;                                 /* Have we actually been defined yet? */ | 
| 224 | 	} | 
| 225 | 	if (uio->uio_offset < 0) { | 
| 226 | 		return EINVAL;  /* invalid offset */ | 
| 227 | 	} | 
| 228 | 	if (uio_resid(a_uio: uio) < 0) { | 
| 229 | 		return EINVAL; | 
| 230 | 	} | 
| 231 | 	mdata = ((addr64_t)mdev[devid].mdBase << 12) + uio->uio_offset; /* Point to the area in "file" */ | 
| 232 |  | 
| 233 | 	saveflag = uio->uio_segflg;                                                     /* Remember what the request is */ | 
| 234 | 	/* Make sure we are moving from physical ram if physical device */ | 
| 235 | 	if (mdev[devid].mdFlags & mdPhys) { | 
| 236 | 		if (uio->uio_segflg == UIO_USERSPACE64) { | 
| 237 | 			uio->uio_segflg = UIO_PHYS_USERSPACE64; | 
| 238 | 		} else if (uio->uio_segflg == UIO_USERSPACE32) { | 
| 239 | 			uio->uio_segflg = UIO_PHYS_USERSPACE32; | 
| 240 | 		} else { | 
| 241 | 			uio->uio_segflg = UIO_PHYS_USERSPACE; | 
| 242 | 		} | 
| 243 | 	} | 
| 244 |  | 
| 245 | 	if (uio->uio_offset > (mdev[devid].mdSize << 12)) { | 
| 246 | 		count = 0; | 
| 247 | 	} else { | 
| 248 | 		count = imin(a: uio_resid(a_uio: uio), b: (mdev[devid].mdSize << 12) - uio->uio_offset); | 
| 249 | 	} | 
| 250 |  | 
| 251 | 	status = uiomove64(cp: mdata, n: count, uio);     /* Move the data */ | 
| 252 | 	uio->uio_segflg = saveflag;                                                     /* Restore the flag */ | 
| 253 |  | 
| 254 | 	return status; | 
| 255 | } | 
| 256 |  | 
| 257 | static void | 
| 258 | mdevstrategy(struct buf *bp) | 
| 259 | { | 
| 260 | 	unsigned int left, lop, csize; | 
| 261 | 	vm_offset_t vaddr, blkoff; | 
| 262 | 	int devid; | 
| 263 | 	addr64_t paddr, fvaddr; | 
| 264 | 	ppnum_t pp; | 
| 265 |  | 
| 266 | 	devid = minor(buf_device(bp));                                                  /* Get minor device number */ | 
| 267 |  | 
| 268 | 	if ((mdev[devid].mdFlags & mdInited) == 0) {            /* Have we actually been defined yet? */ | 
| 269 | 		buf_seterror(bp, ENXIO); | 
| 270 | 		buf_biodone(bp); | 
| 271 | 		return; | 
| 272 | 	} | 
| 273 |  | 
| 274 | 	buf_setresid(bp, resid: buf_count(bp));                                                /* Set byte count */ | 
| 275 |  | 
| 276 | 	blkoff = buf_blkno(bp) * mdev[devid].mdSecsize;         /* Get offset into file */ | 
| 277 |  | 
| 278 | /* | 
| 279 |  *	Note that reading past end is an error, but reading at end is an EOF.  For these | 
| 280 |  *	we just return with resid == count. | 
| 281 |  */ | 
| 282 |  | 
| 283 | 	if (blkoff >= (mdev[devid].mdSize << 12)) {                     /* Are they trying to read/write at/after end? */ | 
| 284 | 		if (blkoff != (mdev[devid].mdSize << 12)) {              /* Are we trying to read after EOF? */ | 
| 285 | 			buf_seterror(bp, EINVAL);                                               /* Yeah, this is an error */ | 
| 286 | 		} | 
| 287 | 		buf_biodone(bp);                                                                /* Return */ | 
| 288 | 		return; | 
| 289 | 	} | 
| 290 |  | 
| 291 | 	if ((blkoff + buf_count(bp)) > (mdev[devid].mdSize << 12)) {            /* Will this read go past end? */ | 
| 292 | 		buf_setcount(bp, bcount: (uint32_t)((mdev[devid].mdSize << 12) - blkoff));  /* Yes, trim to max */ | 
| 293 | 	} | 
| 294 | 	/* | 
| 295 | 	 * make sure the buffer's data area is | 
| 296 | 	 * accessible | 
| 297 | 	 */ | 
| 298 | 	if (buf_map(bp, io_addr: (caddr_t *)&vaddr)) { | 
| 299 | 		panic("ramstrategy: buf_map failed" ); | 
| 300 | 	} | 
| 301 |  | 
| 302 | 	fvaddr = (mdev[devid].mdBase << 12) + blkoff;           /* Point to offset into ram disk */ | 
| 303 |  | 
| 304 | 	if (buf_flags(bp) & B_READ) {                                   /* Is this a read? */ | 
| 305 | 		if (!(mdev[devid].mdFlags & mdPhys)) {                   /* Physical mapped disk? */ | 
| 306 | 			bcopy(src: (void *)((uintptr_t)fvaddr), | 
| 307 | 			    dst: (void *)vaddr, n: (size_t)buf_count(bp));      /* This is virtual, just get the data */ | 
| 308 | 		} else { | 
| 309 | 			left = buf_count(bp);                                           /* Init the amount left to copy */ | 
| 310 | 			while (left) {                                                           /* Go until it is all copied */ | 
| 311 | 				lop = min(a: (4096 - (vaddr & 4095)), b: (4096 - (fvaddr & 4095)));   /* Get smallest amount left on sink and source */ | 
| 312 | 				csize = min(a: lop, b: left);                                 /* Don't move more than we need to */ | 
| 313 |  | 
| 314 | 				pp = pmap_find_phys(pmap: kernel_pmap, va: (addr64_t)((uintptr_t)vaddr)); /* Get the sink physical address */ | 
| 315 | 				if (!pp) {                                                               /* Not found, what gives? */ | 
| 316 | 					panic("mdevstrategy: sink address %016llX not mapped" , (addr64_t)((uintptr_t)vaddr)); | 
| 317 | 				} | 
| 318 | 				paddr = (addr64_t)(((addr64_t)pp << 12) | (addr64_t)(vaddr & 4095));    /* Get actual address */ | 
| 319 | 				bcopy_phys(from: fvaddr, to: paddr, bytes: csize);               /* Copy this on in */ | 
| 320 | 				mapping_set_mod(pn: (ppnum_t)(paddr >> 12));        /* Make sure we know that it is modified */ | 
| 321 |  | 
| 322 | 				left = left - csize;                                    /* Calculate what is left */ | 
| 323 | 				vaddr = vaddr + csize;                                  /* Move to next sink address */ | 
| 324 | 				fvaddr = fvaddr + csize;                                /* Bump to next physical address */ | 
| 325 | 			} | 
| 326 | 		} | 
| 327 | 	} else {                                                                                        /* This is a write */ | 
| 328 | 		if (!(mdev[devid].mdFlags & mdPhys)) {                   /* Physical mapped disk? */ | 
| 329 | 			bcopy(src: (void *)vaddr, dst: (void *)((uintptr_t)fvaddr), | 
| 330 | 			    n: (size_t)buf_count(bp));             /* This is virtual, just put the data */ | 
| 331 | 		} else { | 
| 332 | 			left = buf_count(bp);                                           /* Init the amount left to copy */ | 
| 333 | 			while (left) {                                                           /* Go until it is all copied */ | 
| 334 | 				lop = min(a: (4096 - (vaddr & 4095)), b: (4096 - (fvaddr & 4095)));   /* Get smallest amount left on sink and source */ | 
| 335 | 				csize = min(a: lop, b: left);                                 /* Don't move more than we need to */ | 
| 336 |  | 
| 337 | 				pp = pmap_find_phys(pmap: kernel_pmap, va: (addr64_t)((uintptr_t)vaddr)); /* Get the source physical address */ | 
| 338 | 				if (!pp) {                                                               /* Not found, what gives? */ | 
| 339 | 					panic("mdevstrategy: source address %016llX not mapped" , (addr64_t)((uintptr_t)vaddr)); | 
| 340 | 				} | 
| 341 | 				paddr = (addr64_t)(((addr64_t)pp << 12) | (addr64_t)(vaddr & 4095));    /* Get actual address */ | 
| 342 |  | 
| 343 | 				bcopy_phys(from: paddr, to: fvaddr, bytes: csize);               /* Move this on out */ | 
| 344 |  | 
| 345 | 				left = left - csize;                                    /* Calculate what is left */ | 
| 346 | 				vaddr = vaddr + csize;                                  /* Move to next sink address */ | 
| 347 | 				fvaddr = fvaddr + csize;                                /* Bump to next physical address */ | 
| 348 | 			} | 
| 349 | 		} | 
| 350 | 	} | 
| 351 | 	/* | 
| 352 | 	 * buf_unmap takes care of all the cases | 
| 353 | 	 * it will unmap the buffer from kernel | 
| 354 | 	 * virtual space if that was the state | 
| 355 | 	 * when we mapped it. | 
| 356 | 	 */ | 
| 357 | 	buf_unmap(bp); | 
| 358 |  | 
| 359 | 	buf_setresid(bp, resid: 0);                                                                    /* Nothing more to do */ | 
| 360 | 	buf_biodone(bp);                                                                        /* Say we've finished */ | 
| 361 | } | 
| 362 |  | 
| 363 | static int | 
| 364 | mdevbioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) | 
| 365 | { | 
| 366 | 	return mdevioctl(dev, cmd, data, flag, p, is_char: 0); | 
| 367 | } | 
| 368 |  | 
| 369 | static int | 
| 370 | mdevcioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) | 
| 371 | { | 
| 372 | 	return mdevioctl(dev, cmd, data, flag, p, is_char: 1); | 
| 373 | } | 
| 374 |  | 
| 375 | static int | 
| 376 | mdevioctl(dev_t dev, u_long cmd, caddr_t data, __unused int flag, | 
| 377 |     struct proc *p, int is_char) | 
| 378 | { | 
| 379 | 	int error; | 
| 380 | 	u_int32_t *f; | 
| 381 | 	u_int64_t *o; | 
| 382 | 	int devid; | 
| 383 | 	dk_memdev_info_t * memdev_info; | 
| 384 |  | 
| 385 | 	devid = minor(dev);                                                                     /* Get minor device number */ | 
| 386 |  | 
| 387 | 	if (devid >= NB_MAX_MDEVICES || devid < 0) { | 
| 388 | 		return ENXIO;                                                                 /* Not valid */ | 
| 389 | 	} | 
| 390 | 	error = proc_suser(p);                  /* Are we superman? */ | 
| 391 | 	if (error) { | 
| 392 | 		return error;                                                         /* Nope... */ | 
| 393 | 	} | 
| 394 | 	f = (u_int32_t*)data; | 
| 395 | 	o = (u_int64_t *)data; | 
| 396 | 	memdev_info = (dk_memdev_info_t *) data; | 
| 397 |  | 
| 398 | 	switch (cmd) { | 
| 399 | 	case DKIOCGETMAXBLOCKCOUNTREAD: | 
| 400 | 		*o = 32; | 
| 401 | 		break; | 
| 402 |  | 
| 403 | 	case DKIOCGETMAXBLOCKCOUNTWRITE: | 
| 404 | 		*o = 32; | 
| 405 | 		break; | 
| 406 |  | 
| 407 | 	case DKIOCGETMAXSEGMENTCOUNTREAD: | 
| 408 | 		*o = 32; | 
| 409 | 		break; | 
| 410 |  | 
| 411 | 	case DKIOCGETMAXSEGMENTCOUNTWRITE: | 
| 412 | 		*o = 32; | 
| 413 | 		break; | 
| 414 |  | 
| 415 | 	case DKIOCGETBLOCKSIZE: | 
| 416 | 		*f = mdev[devid].mdSecsize; | 
| 417 | 		break; | 
| 418 |  | 
| 419 | 	case DKIOCSETBLOCKSIZE: | 
| 420 | 		if (is_char) { | 
| 421 | 			return ENODEV;                                        /* We can only do this for a block */ | 
| 422 | 		} | 
| 423 | 		if (*f < DEV_BSIZE) { | 
| 424 | 			return EINVAL;                                /* Too short? */ | 
| 425 | 		} | 
| 426 | 		mdev[devid].mdSecsize = *f;                                             /* set the new block size */ | 
| 427 | 		break; | 
| 428 |  | 
| 429 | 	case DKIOCISWRITABLE: | 
| 430 | 		*f = 1; | 
| 431 | 		break; | 
| 432 |  | 
| 433 | 	case DKIOCGETBLOCKCOUNT: | 
| 434 | 		if (!(mdev[devid].mdFlags & mdInited)) { | 
| 435 | 			return ENXIO; | 
| 436 | 		} | 
| 437 | 		*o = ((mdev[devid].mdSize << 12) + mdev[devid].mdSecsize - 1) / mdev[devid].mdSecsize; | 
| 438 | 		break; | 
| 439 |  | 
| 440 | 	/* | 
| 441 | 	 * We're interested in the following bits of information: | 
| 442 | 	 *   Are you a memory-backed device (always yes, in this case)? | 
| 443 | 	 *   Physical memory (mdPhys)? | 
| 444 | 	 *   What is your base page? | 
| 445 | 	 *   What is your size? | 
| 446 | 	 */ | 
| 447 | 	case DKIOCGETMEMDEVINFO: | 
| 448 | 		if (!(mdev[devid].mdFlags & mdInited)) { | 
| 449 | 			return ENXIO; | 
| 450 | 		} | 
| 451 | 		memdev_info->mi_mdev = TRUE; | 
| 452 | 		memdev_info->mi_phys = (mdev[devid].mdFlags & mdPhys) ? TRUE : FALSE; | 
| 453 | 		memdev_info->mi_base = (uint32_t)mdev[devid].mdBase; | 
| 454 | 		memdev_info->mi_size = mdev[devid].mdSize; | 
| 455 | 		break; | 
| 456 |  | 
| 457 | 	default: | 
| 458 | 		error = ENOTTY; | 
| 459 | 		break; | 
| 460 | 	} | 
| 461 | 	return error; | 
| 462 | } | 
| 463 |  | 
| 464 |  | 
| 465 | static  int | 
| 466 | mdevsize(dev_t dev) | 
| 467 | { | 
| 468 | 	int devid; | 
| 469 |  | 
| 470 | 	devid = minor(dev);                                                                     /* Get minor device number */ | 
| 471 | 	if (devid >= NB_MAX_MDEVICES || devid < 0) { | 
| 472 | 		return ENXIO;                                                                 /* Not valid */ | 
| 473 | 	} | 
| 474 | 	if ((mdev[devid].mdFlags & mdInited) == 0) { | 
| 475 | 		return -1;                                            /* Not inited yet */ | 
| 476 | 	} | 
| 477 | 	return mdev[devid].mdSecsize; | 
| 478 | } | 
| 479 |  | 
| 480 | #include <pexpert/pexpert.h> | 
| 481 |  | 
| 482 | void | 
| 483 | mdevinit(__unused int the_cnt) | 
| 484 | { | 
| 485 | #ifdef CONFIG_MEMDEV_INSECURE | 
| 486 |  | 
| 487 | 	int devid, phys; | 
| 488 | 	uint64_t base; | 
| 489 | 	uint64_t size; | 
| 490 | 	char *ba, *lp; | 
| 491 | 	dev_t dev; | 
| 492 |  | 
| 493 |  | 
| 494 | 	ba = PE_boot_args();                                                            /* Get the boot arguments */ | 
| 495 | 	lp = ba + 256;                                                                          /* Point to the end */ | 
| 496 |  | 
| 497 | 	while (1) {                                                                                      /* Step through, looking for our keywords */ | 
| 498 | 		phys = 0;                                                                               /* Assume virtual memory device */ | 
| 499 | 		ba = nonspace(ba, lp);                                                  /* Find non-space */ | 
| 500 | 		if (ba >= lp) { | 
| 501 | 			return;                                                         /* We are done if no more... */ | 
| 502 | 		} | 
| 503 | 		if (((ba[0] != 'v') && (ba[0] != 'p')) | 
| 504 | 		    || (ba[1] != 'm') || (ba[2] != 'd') || (ba[4] != '=') | 
| 505 | 		    || (ba[3] < '0') || (ba[3] > 'f') | 
| 506 | 		    || ((ba[3] > '9') && (ba[3] < 'a'))) {              /* Is this of form "vmdx=" or "pmdx=" where x is hex digit? */ | 
| 507 | 			ba = getspace(ba, lp);                                          /* Find next white space or end */ | 
| 508 | 			continue;                                                                       /* Start looking for the next one */ | 
| 509 | 		} | 
| 510 |  | 
| 511 | 		if (ba[0] == 'p') { | 
| 512 | 			phys = 1;                                                       /* Set physical memory disk */ | 
| 513 | 		} | 
| 514 | 		devid = ba[3] & 0xF;                                                    /* Assume digit */ | 
| 515 | 		if (ba[3] > '9') { | 
| 516 | 			devid += 9;                                                     /* Adjust for hex digits */ | 
| 517 | 		} | 
| 518 | 		ba = &ba[5];                                                                    /* Step past keyword */ | 
| 519 | 		ba = cvtnum(ba, lp, &base);                                             /* Convert base of memory disk */ | 
| 520 | 		if (ba >= lp) { | 
| 521 | 			return;                                                         /* Malformed one at the end, leave */ | 
| 522 | 		} | 
| 523 | 		if (ba[0] != '.') { | 
| 524 | 			continue;                                                       /* If not length separater, try next... */ | 
| 525 | 		} | 
| 526 | 		if (base & 0xFFF) { | 
| 527 | 			continue;                                                       /* Only allow page aligned stuff */ | 
| 528 | 		} | 
| 529 | 		ba++;                                                                                   /* Step past '.' */ | 
| 530 | 		ba = cvtnum(ba, lp, &size);                                             /* Try to convert it */ | 
| 531 | 		if (!size || (size & 0xFFF)) { | 
| 532 | 			continue;                                       /* Allow only non-zer page size multiples */ | 
| 533 | 		} | 
| 534 | 		if (ba < lp) {                                                                   /* If we are not at end, check end character */ | 
| 535 | 			if ((ba[0] != ' ') && (ba[0] != 0)) { | 
| 536 | 				continue;                               /* End must be null or space */ | 
| 537 | 			} | 
| 538 | 		} | 
| 539 |  | 
| 540 | 		dev = mdevadd(devid, base >> 12, (unsigned)size >> 12, phys);   /* Go add the device */ | 
| 541 | 	} | 
| 542 |  | 
| 543 | #endif /* CONFIG_MEMDEV_INSECURE */ | 
| 544 |  | 
| 545 | 	return; | 
| 546 | } | 
| 547 |  | 
| 548 | #ifdef CONFIG_MEMDEV_INSECURE | 
| 549 |  | 
| 550 | char * | 
| 551 | nonspace(char *pos, char *end)                                          /* Find next non-space in string */ | 
| 552 | { | 
| 553 | 	if (pos >= end) { | 
| 554 | 		return end;                                                             /* Don't go past end */ | 
| 555 | 	} | 
| 556 | 	if (pos[0] == 0) { | 
| 557 | 		return end;                                                             /* If at null, make end */ | 
| 558 | 	} | 
| 559 | 	while (1) {                                                                                      /* Keep going */ | 
| 560 | 		if (pos[0] != ' ') { | 
| 561 | 			return pos;                                             /* Leave if we found one */ | 
| 562 | 		} | 
| 563 | 		pos++;                                                                                  /* Stop */ | 
| 564 | 		if (pos >= end) { | 
| 565 | 			return end;                                                     /* Quit if we run off end */ | 
| 566 | 		} | 
| 567 | 	} | 
| 568 | } | 
| 569 |  | 
| 570 | char * | 
| 571 | getspace(char *pos, char *end)                                          /* Find next non-space in string */ | 
| 572 | { | 
| 573 | 	while (1) {                                                                                      /* Keep going */ | 
| 574 | 		if (pos >= end) { | 
| 575 | 			return end;                                                     /* Don't go past end */ | 
| 576 | 		} | 
| 577 | 		if (pos[0] == 0) { | 
| 578 | 			return end;                                                     /* Leave if we hit null */ | 
| 579 | 		} | 
| 580 | 		if (pos[0] == ' ') { | 
| 581 | 			return pos;                                             /* Leave if we found one */ | 
| 582 | 		} | 
| 583 | 		pos++;                                                                                  /* Stop */ | 
| 584 | 	} | 
| 585 | } | 
| 586 |  | 
| 587 | char * | 
| 588 | cvtnum(char *pos, char *end, uint64_t *num)                     /* Convert to a number */ | 
| 589 | { | 
| 590 | 	int rad, dig; | 
| 591 |  | 
| 592 | 	*num = 0;                                                                                       /* Set answer to 0 to start */ | 
| 593 | 	rad = 10; | 
| 594 |  | 
| 595 | 	if (pos >= end) { | 
| 596 | 		return end;                                                             /* Don't go past end */ | 
| 597 | 	} | 
| 598 | 	if (pos[0] == 0) { | 
| 599 | 		return end;                                                             /* If at null, make end */ | 
| 600 | 	} | 
| 601 | 	if (pos[0] == '0' && ((pos[1] == 'x') || (pos[1] == 'x'))) {     /* A hex constant? */ | 
| 602 | 		rad = 16; | 
| 603 | 		pos += 2;                                                                               /* Point to the number */ | 
| 604 | 	} | 
| 605 |  | 
| 606 | 	while (1) {                                                                                      /* Convert it */ | 
| 607 | 		if (pos >= end) { | 
| 608 | 			return end;                                                     /* Don't go past end */ | 
| 609 | 		} | 
| 610 | 		if (pos[0] == 0) { | 
| 611 | 			return end;                                                     /* If at null, make end */ | 
| 612 | 		} | 
| 613 | 		if (pos[0] < '0') { | 
| 614 | 			return pos;                                             /* Leave if non-digit */ | 
| 615 | 		} | 
| 616 | 		dig = pos[0] & 0xF;                                                             /* Extract digit */ | 
| 617 | 		if (pos[0] > '9') {                                                              /* Is it bigger than 9? */ | 
| 618 | 			if (rad == 10) { | 
| 619 | 				return pos;                                             /* Leave if not base 10 */ | 
| 620 | 			} | 
| 621 | 			if (!(((pos[0] >= 'A') && (pos[0] <= 'F')) | 
| 622 | 			    || ((pos[0] >= 'a') && (pos[0] <= 'f')))) { | 
| 623 | 				return pos;                                     /* Leave if bogus char */ | 
| 624 | 			} | 
| 625 | 			dig = dig + 9;                                                          /* Adjust for character */ | 
| 626 | 		} | 
| 627 | 		*num = (*num * rad) + dig;                                              /* Accumulate the number */ | 
| 628 | 		pos++;                                                                                  /* Step on */ | 
| 629 | 	} | 
| 630 | } | 
| 631 |  | 
| 632 | #endif /* CONFIG_MEMDEV_INSECURE */ | 
| 633 |  | 
| 634 | dev_t | 
| 635 | mdevadd(int devid, uint64_t base, unsigned int size, int phys) | 
| 636 | { | 
| 637 | 	int i; | 
| 638 |  | 
| 639 | 	if (devid < 0) { | 
| 640 | 		devid = -1; | 
| 641 | 		for (i = 0; i < NB_MAX_MDEVICES; i++) {                                          /* Search all known memory devices */ | 
| 642 | 			if (!(mdev[i].mdFlags & mdInited)) {                     /* Is this a free one? */ | 
| 643 | 				if (devid < 0) { | 
| 644 | 					devid = i;                                      /* Remember first free one */ | 
| 645 | 				} | 
| 646 | 				continue;                                                               /* Skip check */ | 
| 647 | 			} | 
| 648 | 			if (!(((base + size - 1) < mdev[i].mdBase) || ((mdev[i].mdBase + mdev[i].mdSize - 1) < base))) { /* Is there any overlap? */ | 
| 649 | 				panic("mdevadd: attempt to add overlapping memory device at %016llX-%016llX" , mdev[i].mdBase, mdev[i].mdBase + mdev[i].mdSize - 1); | 
| 650 | 			} | 
| 651 | 		} | 
| 652 | 		if (devid < 0) {                                                                 /* Do we have free slots? */ | 
| 653 | 			panic("mdevadd: attempt to add more than %d memory devices" , NB_MAX_MDEVICES); | 
| 654 | 		} | 
| 655 | 	} else { | 
| 656 | 		if (devid >= NB_MAX_MDEVICES) {                                                          /* Giving us something bogus? */ | 
| 657 | 			panic("mdevadd: attempt to explicitly add a bogus memory device: %08X" , devid); | 
| 658 | 		} | 
| 659 | 		if (mdev[devid].mdFlags & mdInited) {                    /* Already there? */ | 
| 660 | 			panic("mdevadd: attempt to explicitly add a previously defined memory device: %08X" , devid); | 
| 661 | 		} | 
| 662 | 	} | 
| 663 |  | 
| 664 | 	if (mdevBMajor < 0) {                                                            /* Have we gotten a major number yet? */ | 
| 665 | 		mdevBMajor = bdevsw_add(-1, &mdevbdevsw);               /* Add to the table and figure out a major number */ | 
| 666 | 		if (mdevBMajor < 0) { | 
| 667 | 			printf("mdevadd: error - bdevsw_add() returned %d\n" , mdevBMajor); | 
| 668 | 			return -1; | 
| 669 | 		} | 
| 670 | 	} | 
| 671 |  | 
| 672 | 	if (mdevCMajor < 0) {                                                            /* Have we gotten a major number yet? */ | 
| 673 | 		mdevCMajor = cdevsw_add_with_bdev(index: -1, csw: &mdevcdevsw, bdev: mdevBMajor);         /* Add to the table and figure out a major number */ | 
| 674 | 		if (mdevCMajor < 0) { | 
| 675 | 			printf("ramdevice_init: error - cdevsw_add() returned %d\n" , mdevCMajor); | 
| 676 | 			return -1; | 
| 677 | 		} | 
| 678 | 	} | 
| 679 |  | 
| 680 | 	mdev[devid].mdBDev = makedev(mdevBMajor, devid);        /* Get the device number */ | 
| 681 | 	mdev[devid].mdbdevb = devfs_make_node(dev: mdev[devid].mdBDev, DEVFS_BLOCK,  /* Make the node */ | 
| 682 | 	    UID_ROOT, GID_OPERATOR, | 
| 683 | 	    perms: 0600, fmt: "md%d" , devid); | 
| 684 | 	if (mdev[devid].mdbdevb == NULL) {                                      /* Did we make one? */ | 
| 685 | 		printf("mdevadd: devfs_make_node for block failed!\n" ); | 
| 686 | 		return -1;                                                                              /* Nope... */ | 
| 687 | 	} | 
| 688 |  | 
| 689 | 	mdev[devid].mdCDev = makedev(mdevCMajor, devid);        /* Get the device number */ | 
| 690 | 	mdev[devid].mdcdevb = devfs_make_node(dev: mdev[devid].mdCDev, DEVFS_CHAR,           /* Make the node */ | 
| 691 | 	    UID_ROOT, GID_OPERATOR, | 
| 692 | 	    perms: 0600, fmt: "rmd%d" , devid); | 
| 693 | 	if (mdev[devid].mdcdevb == NULL) {                                      /* Did we make one? */ | 
| 694 | 		printf("mdevadd: devfs_make_node for character failed!\n" ); | 
| 695 | 		return -1;                                                                              /* Nope... */ | 
| 696 | 	} | 
| 697 |  | 
| 698 | 	mdev[devid].mdBase = base;                                                      /* Set the base address of ram disk */ | 
| 699 | 	mdev[devid].mdSize = size;                                                      /* Set the length of the ram disk */ | 
| 700 | 	mdev[devid].mdSecsize = DEV_BSIZE;                                      /* Set starting block size */ | 
| 701 | 	if (phys) { | 
| 702 | 		mdev[devid].mdFlags |= mdPhys;                          /* Show that we are in physical memory */ | 
| 703 | 	} | 
| 704 | 	mdev[devid].mdFlags |= mdInited;                                        /* Show we are all set up */ | 
| 705 | 	printf("Added memory device md%x/rmd%x (%08X/%08X) at %016llX for %016llX\n" , | 
| 706 | 	    devid, devid, mdev[devid].mdBDev, mdev[devid].mdCDev, base << 12, (uint64_t)size << 12); | 
| 707 | 	return mdev[devid].mdBDev; | 
| 708 | } | 
| 709 |  | 
| 710 |  | 
| 711 | dev_t | 
| 712 | mdevlookup(int devid) | 
| 713 | { | 
| 714 | 	if ((devid < 0) || (devid >= NB_MAX_MDEVICES)) { | 
| 715 | 		return -1;                                                              /* Filter any bogus requests */ | 
| 716 | 	} | 
| 717 | 	if (!(mdev[devid].mdFlags & mdInited)) { | 
| 718 | 		return -1;                                      /* This one hasn't been defined */ | 
| 719 | 	} | 
| 720 | 	return mdev[devid].mdBDev;                                                      /* Return the device number */ | 
| 721 | } | 
| 722 |  | 
| 723 | void | 
| 724 | mdevremoveall(void) | 
| 725 | { | 
| 726 | 	int i; | 
| 727 |  | 
| 728 | 	for (i = 0; i < NB_MAX_MDEVICES; i++) { | 
| 729 | 		if (!(mdev[i].mdFlags & mdInited)) { | 
| 730 | 			continue;                               /* Ignore unused mdevs */ | 
| 731 | 		} | 
| 732 | 		devfs_remove(handle: mdev[i].mdbdevb);                  /* Remove the block device */ | 
| 733 | 		devfs_remove(handle: mdev[i].mdcdevb);                  /* Remove the character device */ | 
| 734 |  | 
| 735 | 		mdev[i].mdBase = 0;                             /* Clear the mdev's storage */ | 
| 736 | 		mdev[i].mdSize = 0; | 
| 737 | 		mdev[i].mdSecsize = 0; | 
| 738 | 		mdev[i].mdFlags = 0; | 
| 739 | 		mdev[i].mdBDev = 0; | 
| 740 | 		mdev[i].mdCDev = 0; | 
| 741 | 		mdev[i].mdbdevb = 0; | 
| 742 | 		mdev[i].mdcdevb = 0; | 
| 743 | 	} | 
| 744 | } | 
| 745 |  | 
| 746 | int | 
| 747 | mdevgetrange(int devid, uint64_t *base, uint64_t *size) | 
| 748 | { | 
| 749 | 	assert(base); | 
| 750 | 	assert(size); | 
| 751 |  | 
| 752 | 	/* filter invalid request */ | 
| 753 | 	if ((devid < 0) || (devid >= NB_MAX_MDEVICES)) { | 
| 754 | 		return -1; | 
| 755 | 	} | 
| 756 |  | 
| 757 | 	/* filter non-initialized memory devices */ | 
| 758 | 	if ((mdev[devid].mdFlags & mdInited) == 0) { | 
| 759 | 		return -1; | 
| 760 | 	} | 
| 761 |  | 
| 762 | 	*base = mdev[devid].mdBase << 12; | 
| 763 | 	*size = mdev[devid].mdSize << 12; | 
| 764 |  | 
| 765 | 	/* make sure (base, size) is a valid range and will not overflow */ | 
| 766 | 	assert(*size < (UINT64_MAX - *base)); | 
| 767 |  | 
| 768 | 	return 0; | 
| 769 | } | 
| 770 |  |