1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/file.h> 31 #include <sys/errno.h> 32 #include <sys/open.h> 33 #include <sys/cred.h> 34 #include <sys/conf.h> 35 #include <sys/stat.h> 36 #include <sys/modctl.h> 37 #include <sys/ddi.h> 38 #include <sys/sunddi.h> 39 #include <sys/vmsystm.h> 40 #include <sys/sdt.h> 41 #include <sys/hypervisor.h> 42 #include <sys/xen_errno.h> 43 44 #include <vm/hat_i86.h> 45 #include <vm/hat_pte.h> 46 #include <vm/seg_mf.h> 47 48 #include <xen/sys/privcmd.h> 49 #include <sys/privcmd_impl.h> 50 51 static dev_info_t *privcmd_devi; 52 53 /*ARGSUSED*/ 54 static int 55 privcmd_getinfo(dev_info_t *devi, ddi_info_cmd_t cmd, void *arg, void **result) 56 { 57 switch (cmd) { 58 case DDI_INFO_DEVT2DEVINFO: 59 case DDI_INFO_DEVT2INSTANCE: 60 break; 61 default: 62 return (DDI_FAILURE); 63 } 64 65 switch (getminor((dev_t)arg)) { 66 case PRIVCMD_MINOR: 67 break; 68 default: 69 return (DDI_FAILURE); 70 } 71 72 if (cmd == DDI_INFO_DEVT2INSTANCE) 73 *result = 0; 74 else 75 *result = privcmd_devi; 76 return (DDI_SUCCESS); 77 } 78 79 static int 80 privcmd_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 81 { 82 if (cmd != DDI_ATTACH) 83 return (DDI_FAILURE); 84 85 if (ddi_create_minor_node(devi, PRIVCMD_NODE, 86 S_IFCHR, PRIVCMD_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS) 87 return (DDI_FAILURE); 88 89 privcmd_devi = devi; 90 ddi_report_dev(devi); 91 return (DDI_SUCCESS); 92 } 93 94 static int 95 privcmd_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 96 { 97 if (cmd != DDI_DETACH) 98 return (DDI_FAILURE); 99 ddi_remove_minor_node(devi, NULL); 100 privcmd_devi = NULL; 101 return (DDI_SUCCESS); 102 } 103 104 /*ARGSUSED1*/ 105 static int 106 privcmd_open(dev_t *dev, int flag, int otyp, cred_t *cr) 107 { 108 return (getminor(*dev) == PRIVCMD_MINOR ? 0 : ENXIO); 109 } 110 111 /* 112 * Map a contiguous set of machine frames in a foreign domain. 113 * Used in the following way: 114 * 115 * privcmd_mmap_t p; 116 * privcmd_mmap_entry_t e; 117 * 118 * addr = mmap(NULL, size, prot, MAP_SHARED, fd, 0); 119 * p.num = number of privcmd_mmap_entry_t's 120 * p.dom = domid; 121 * p.entry = &e; 122 * e.va = addr; 123 * e.mfn = mfn; 124 * e.npages = btopr(size); 125 * ioctl(fd, IOCTL_PRIVCMD_MMAP, &p); 126 */ 127 /*ARGSUSED2*/ 128 int 129 do_privcmd_mmap(void *uarg, int mode, cred_t *cr) 130 { 131 privcmd_mmap_t __mmapcmd, *mmc = &__mmapcmd; 132 privcmd_mmap_entry_t *umme; 133 struct as *as = curproc->p_as; 134 struct seg *seg; 135 int i, error = 0; 136 137 if (ddi_copyin(uarg, mmc, sizeof (*mmc), mode)) 138 return (EFAULT); 139 140 DTRACE_XPV3(mmap__start, domid_t, mmc->dom, int, mmc->num, 141 privcmd_mmap_entry_t *, mmc->entry); 142 143 if (mmc->dom == DOMID_SELF) { 144 error = ENOTSUP; /* Too paranoid? */ 145 goto done; 146 } 147 148 for (umme = mmc->entry, i = 0; i < mmc->num; i++, umme++) { 149 privcmd_mmap_entry_t __mmapent, *mme = &__mmapent; 150 caddr_t addr; 151 152 if (ddi_copyin(umme, mme, sizeof (*mme), mode)) { 153 error = EFAULT; 154 break; 155 } 156 157 DTRACE_XPV3(mmap__entry, ulong_t, mme->va, ulong_t, mme->mfn, 158 ulong_t, mme->npages); 159 160 if (mme->mfn == MFN_INVALID) { 161 error = EINVAL; 162 break; 163 } 164 165 addr = (caddr_t)mme->va; 166 167 /* 168 * Find the segment we want to mess with, then add 169 * the mfn range to the segment. 170 */ 171 AS_LOCK_ENTER(as, &as->a_lock, RW_READER); 172 if ((seg = as_findseg(as, addr, 0)) == NULL || 173 addr + mmu_ptob(mme->npages) > seg->s_base + seg->s_size) 174 error = EINVAL; 175 else 176 error = segmf_add_mfns(seg, addr, 177 mme->mfn, mme->npages, mmc->dom); 178 AS_LOCK_EXIT(as, &as->a_lock); 179 180 if (error != 0) 181 break; 182 } 183 184 done: 185 DTRACE_XPV1(mmap__end, int, error); 186 187 return (error); 188 } 189 190 /* 191 * Set up the address range to map to an array of mfns in 192 * a foreign domain. Used in the following way: 193 * 194 * privcmd_mmap_batch_t p; 195 * 196 * addr = mmap(NULL, size, prot, MAP_SHARED, fd, 0); 197 * p.num = number of pages 198 * p.dom = domid 199 * p.addr = addr; 200 * p.arr = array of mfns, indexed 0 .. p.num - 1 201 * ioctl(fd, IOCTL_PRIVCMD_MMAPBATCH, &p); 202 */ 203 /*ARGSUSED2*/ 204 static int 205 do_privcmd_mmapbatch(void *uarg, int mode, cred_t *cr) 206 { 207 privcmd_mmapbatch_t __mmapbatch, *mmb = &__mmapbatch; 208 struct as *as = curproc->p_as; 209 struct seg *seg; 210 int i, error = 0; 211 caddr_t addr; 212 ulong_t *ulp; 213 214 if (ddi_copyin(uarg, mmb, sizeof (*mmb), mode)) 215 return (EFAULT); 216 217 DTRACE_XPV3(mmapbatch__start, domid_t, mmb->dom, int, mmb->num, 218 caddr_t, mmb->addr); 219 220 addr = (caddr_t)mmb->addr; 221 AS_LOCK_ENTER(as, &as->a_lock, RW_READER); 222 if ((seg = as_findseg(as, addr, 0)) == NULL || 223 addr + ptob(mmb->num) > seg->s_base + seg->s_size) { 224 error = EINVAL; 225 goto done; 226 } 227 228 for (i = 0, ulp = mmb->arr; 229 i < mmb->num; i++, addr += PAGESIZE, ulp++) { 230 mfn_t mfn; 231 232 if (fulword(ulp, &mfn) != 0) { 233 error = EFAULT; 234 break; 235 } 236 237 if (mfn == MFN_INVALID) { 238 error = EINVAL; 239 break; 240 } 241 242 if (segmf_add_mfns(seg, addr, mfn, 1, mmb->dom) == 0) 243 continue; 244 245 /* 246 * Tell the process that this MFN could not be mapped, so it 247 * won't later try to access it. 248 */ 249 mfn |= 0xf0000000; 250 if (sulword(ulp, mfn) != 0) { 251 error = EFAULT; 252 break; 253 } 254 } 255 256 done: 257 AS_LOCK_EXIT(as, &as->a_lock); 258 259 DTRACE_XPV3(mmapbatch__end, int, error, struct seg *, seg, caddr_t, 260 mmb->addr); 261 262 return (error); 263 } 264 265 /*ARGSUSED*/ 266 static int 267 privcmd_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval) 268 { 269 if ((mode & FMODELS) != FNATIVE) 270 return (EOVERFLOW); 271 272 /* 273 * Everything is a -native- data type. 274 */ 275 276 switch (cmd) { 277 case IOCTL_PRIVCMD_HYPERCALL: 278 return (do_privcmd_hypercall((void *)arg, mode, cr, rval)); 279 case IOCTL_PRIVCMD_MMAP: 280 if (DOMAIN_IS_PRIVILEGED(xen_info)) 281 return (do_privcmd_mmap((void *)arg, mode, cr)); 282 break; 283 case IOCTL_PRIVCMD_MMAPBATCH: 284 if (DOMAIN_IS_PRIVILEGED(xen_info)) 285 return (do_privcmd_mmapbatch((void *)arg, mode, cr)); 286 break; 287 default: 288 break; 289 } 290 return (EINVAL); 291 } 292 293 /* 294 * The real magic happens in the segmf segment driver. 295 */ 296 /*ARGSUSED8*/ 297 static int 298 privcmd_segmap(dev_t dev, off_t off, struct as *as, caddr_t *addrp, 299 off_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr) 300 { 301 struct segmf_crargs a; 302 int error; 303 304 as_rangelock(as); 305 if ((flags & MAP_FIXED) == 0) { 306 map_addr(addrp, len, (offset_t)off, 0, flags); 307 if (*addrp == NULL) { 308 error = ENOMEM; 309 goto rangeunlock; 310 } 311 } else { 312 /* 313 * User specified address 314 */ 315 (void) as_unmap(as, *addrp, len); 316 } 317 318 /* 319 * The mapping *must* be MAP_SHARED at offset 0. 320 * 321 * (Foreign pages are treated like device memory; the 322 * ioctl interface allows the backing objects to be 323 * arbitrarily redefined to point at any machine frame.) 324 */ 325 if ((flags & MAP_TYPE) != MAP_SHARED || off != 0) { 326 error = EINVAL; 327 goto rangeunlock; 328 } 329 330 a.dev = dev; 331 a.prot = (uchar_t)prot; 332 a.maxprot = (uchar_t)maxprot; 333 error = as_map(as, *addrp, len, segmf_create, &a); 334 335 rangeunlock: 336 as_rangeunlock(as); 337 return (error); 338 } 339 340 static struct cb_ops privcmd_cb_ops = { 341 privcmd_open, 342 nulldev, /* close */ 343 nodev, /* strategy */ 344 nodev, /* print */ 345 nodev, /* dump */ 346 nodev, /* read */ 347 nodev, /* write */ 348 privcmd_ioctl, 349 nodev, /* devmap */ 350 nodev, /* mmap */ 351 privcmd_segmap, 352 nochpoll, /* poll */ 353 ddi_prop_op, 354 NULL, 355 D_64BIT | D_NEW | D_MP 356 }; 357 358 static struct dev_ops privcmd_dv_ops = { 359 DEVO_REV, 360 0, 361 privcmd_getinfo, 362 nulldev, /* identify */ 363 nulldev, /* probe */ 364 privcmd_attach, 365 privcmd_detach, 366 nodev, /* reset */ 367 &privcmd_cb_ops, 368 0 /* struct bus_ops */ 369 }; 370 371 static struct modldrv modldrv = { 372 &mod_driverops, 373 "privcmd driver %I%", 374 &privcmd_dv_ops 375 }; 376 377 static struct modlinkage modl = { 378 MODREV_1, 379 &modldrv 380 }; 381 382 int 383 _init(void) 384 { 385 return (mod_install(&modl)); 386 } 387 388 int 389 _fini(void) 390 { 391 return (mod_remove(&modl)); 392 } 393 394 int 395 _info(struct modinfo *modinfo) 396 { 397 return (mod_info(&modl, modinfo)); 398 } 399