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 2008 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 /* 239 * This mfn is invalid and should not be added to 240 * segmf, as we'd only cause an immediate EFAULT when 241 * we tried to fault it in. 242 */ 243 mfn |= XEN_DOMCTL_PFINFO_XTAB; 244 continue; 245 } 246 247 if (segmf_add_mfns(seg, addr, mfn, 1, mmb->dom) == 0) 248 continue; 249 250 /* 251 * Tell the process that this MFN could not be mapped, so it 252 * won't later try to access it. 253 */ 254 mfn |= XEN_DOMCTL_PFINFO_XTAB; 255 if (sulword(ulp, mfn) != 0) { 256 error = EFAULT; 257 break; 258 } 259 } 260 261 done: 262 AS_LOCK_EXIT(as, &as->a_lock); 263 264 DTRACE_XPV3(mmapbatch__end, int, error, struct seg *, seg, caddr_t, 265 mmb->addr); 266 267 return (error); 268 } 269 270 /*ARGSUSED*/ 271 static int 272 privcmd_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval) 273 { 274 if ((mode & FMODELS) != FNATIVE) 275 return (EOVERFLOW); 276 277 /* 278 * Everything is a -native- data type. 279 */ 280 281 switch (cmd) { 282 case IOCTL_PRIVCMD_HYPERCALL: 283 return (do_privcmd_hypercall((void *)arg, mode, cr, rval)); 284 case IOCTL_PRIVCMD_MMAP: 285 if (DOMAIN_IS_PRIVILEGED(xen_info)) 286 return (do_privcmd_mmap((void *)arg, mode, cr)); 287 break; 288 case IOCTL_PRIVCMD_MMAPBATCH: 289 if (DOMAIN_IS_PRIVILEGED(xen_info)) 290 return (do_privcmd_mmapbatch((void *)arg, mode, cr)); 291 break; 292 default: 293 break; 294 } 295 return (EINVAL); 296 } 297 298 /* 299 * The real magic happens in the segmf segment driver. 300 */ 301 /*ARGSUSED8*/ 302 static int 303 privcmd_segmap(dev_t dev, off_t off, struct as *as, caddr_t *addrp, 304 off_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr) 305 { 306 struct segmf_crargs a; 307 int error; 308 309 as_rangelock(as); 310 if ((flags & MAP_FIXED) == 0) { 311 map_addr(addrp, len, (offset_t)off, 0, flags); 312 if (*addrp == NULL) { 313 error = ENOMEM; 314 goto rangeunlock; 315 } 316 } else { 317 /* 318 * User specified address 319 */ 320 (void) as_unmap(as, *addrp, len); 321 } 322 323 /* 324 * The mapping *must* be MAP_SHARED at offset 0. 325 * 326 * (Foreign pages are treated like device memory; the 327 * ioctl interface allows the backing objects to be 328 * arbitrarily redefined to point at any machine frame.) 329 */ 330 if ((flags & MAP_TYPE) != MAP_SHARED || off != 0) { 331 error = EINVAL; 332 goto rangeunlock; 333 } 334 335 a.dev = dev; 336 a.prot = (uchar_t)prot; 337 a.maxprot = (uchar_t)maxprot; 338 error = as_map(as, *addrp, len, segmf_create, &a); 339 340 rangeunlock: 341 as_rangeunlock(as); 342 return (error); 343 } 344 345 static struct cb_ops privcmd_cb_ops = { 346 privcmd_open, 347 nulldev, /* close */ 348 nodev, /* strategy */ 349 nodev, /* print */ 350 nodev, /* dump */ 351 nodev, /* read */ 352 nodev, /* write */ 353 privcmd_ioctl, 354 nodev, /* devmap */ 355 nodev, /* mmap */ 356 privcmd_segmap, 357 nochpoll, /* poll */ 358 ddi_prop_op, 359 NULL, 360 D_64BIT | D_NEW | D_MP 361 }; 362 363 static struct dev_ops privcmd_dv_ops = { 364 DEVO_REV, 365 0, 366 privcmd_getinfo, 367 nulldev, /* identify */ 368 nulldev, /* probe */ 369 privcmd_attach, 370 privcmd_detach, 371 nodev, /* reset */ 372 &privcmd_cb_ops, 373 0 /* struct bus_ops */ 374 }; 375 376 static struct modldrv modldrv = { 377 &mod_driverops, 378 "privcmd driver %I%", 379 &privcmd_dv_ops 380 }; 381 382 static struct modlinkage modl = { 383 MODREV_1, 384 &modldrv 385 }; 386 387 int 388 _init(void) 389 { 390 return (mod_install(&modl)); 391 } 392 393 int 394 _fini(void) 395 { 396 return (mod_remove(&modl)); 397 } 398 399 int 400 _info(struct modinfo *modinfo) 401 { 402 return (mod_info(&modl, modinfo)); 403 } 404