xref: /illumos-gate/usr/src/uts/i86xpv/io/privcmd_hcall.c (revision e7cbe64f7a72dae5cb44f100db60ca88f3313c65)
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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/types.h>
29 #include <sys/file.h>
30 #include <sys/errno.h>
31 #include <sys/open.h>
32 #include <sys/cred.h>
33 #include <sys/conf.h>
34 #include <sys/stat.h>
35 #include <sys/modctl.h>
36 #include <sys/ddi.h>
37 #include <sys/sunddi.h>
38 #include <sys/vmsystm.h>
39 #include <sys/hypervisor.h>
40 #include <sys/xen_errno.h>
41 #include <sys/sysmacros.h>
42 #include <sys/sdt.h>
43 
44 #include <xen/sys/privcmd.h>
45 #include <sys/privcmd_impl.h>
46 
47 typedef struct import_export {
48 	void *			ie_uaddr;
49 	void *			ie_kaddr;
50 	size_t			ie_size;
51 	uint32_t		ie_flags;
52 } import_export_t;
53 
54 static import_export_t null_ie = {NULL, NULL, 0, 0};
55 
56 #define	IE_IMPORT	0x0001		/* Data needs to be copied in */
57 #define	IE_EXPORT	0x0002		/* Data needs to be copied out */
58 #define	IE_FREE		0x0004
59 #define	IE_IMPEXP	(IE_IMPORT | IE_EXPORT)
60 
61 /*
62  * Import a buffer from user-space.  If the caller provides a kernel
63  * address, we import to that address.  If not, we kmem_alloc() the space
64  * ourselves.
65  */
66 static int
67 import_buffer(import_export_t *iep, void *uaddr, void *kaddr, size_t size,
68     uint32_t flags)
69 {
70 	iep->ie_uaddr = uaddr;
71 	iep->ie_size = size;
72 	iep->ie_flags = flags & IE_EXPORT;
73 
74 	if (size == 0 || uaddr == NULL) {
75 		*iep = null_ie;
76 		return (0);
77 	}
78 
79 	if (kaddr == NULL) {
80 		iep->ie_kaddr = kmem_alloc(size, KM_SLEEP);
81 		iep->ie_flags |= IE_FREE;
82 	} else {
83 		iep->ie_kaddr = kaddr;
84 		iep->ie_flags &= ~IE_FREE;
85 	}
86 
87 	if ((flags & IE_IMPORT) &&
88 	    (ddi_copyin(uaddr, iep->ie_kaddr, size, 0) != 0)) {
89 		if (iep->ie_flags & IE_FREE) {
90 			kmem_free(iep->ie_kaddr, iep->ie_size);
91 			iep->ie_kaddr = NULL;
92 			iep->ie_flags = 0;
93 		}
94 		return (-X_EFAULT);
95 	}
96 
97 	return (0);
98 }
99 
100 static void
101 export_buffer(import_export_t *iep, int *error)
102 {
103 	int copy_err = 0;
104 
105 	if (iep->ie_size == 0 || iep->ie_uaddr == NULL)
106 		return;
107 
108 	/*
109 	 * If the buffer was marked for export initially, and if the
110 	 * hypercall completed successfully, resync the user-space buffer
111 	 * with our in-kernel buffer.
112 	 */
113 	if ((iep->ie_flags & IE_EXPORT) && (*error >= 0) &&
114 	    (ddi_copyout(iep->ie_kaddr, iep->ie_uaddr, iep->ie_size, 0) != 0))
115 		copy_err = -X_EFAULT;
116 	if (iep->ie_flags & IE_FREE) {
117 		kmem_free(iep->ie_kaddr, iep->ie_size);
118 		iep->ie_kaddr = NULL;
119 		iep->ie_flags = 0;
120 	}
121 
122 	if (copy_err != 0 && *error >= 0)
123 		*error = copy_err;
124 }
125 
126 /*
127  * Xen 'op' structures often include pointers disguised as 'handles', which
128  * refer to addresses in user space.  This routine copies a buffer
129  * associated with an embedded pointer into kernel space, and replaces the
130  * pointer to userspace with a pointer to the new kernel buffer.
131  *
132  * Note: if Xen ever redefines the structure of a 'handle', this routine
133  * (specifically the definition of 'hdl') will need to be updated.
134  */
135 static int
136 import_handle(import_export_t *iep, void *field, size_t size, int flags)
137 {
138 	struct { void *p; } *hdl = field;
139 	void *ptr;
140 	int err;
141 
142 	/*LINTED: constant in conditional context*/
143 	get_xen_guest_handle(ptr, (*hdl));
144 	err = import_buffer(iep, ptr, NULL, size, (flags));
145 	if (err == 0) {
146 		/*LINTED: constant in conditional context*/
147 		set_xen_guest_handle((*hdl), (void *)((iep)->ie_kaddr));
148 	}
149 	return (err);
150 }
151 
152 static int
153 privcmd_HYPERVISOR_mmu_update(mmu_update_t *ureq, int count, int *scount,
154     domid_t domid)
155 {
156 	mmu_update_t *kreq, single_kreq;
157 	import_export_t cnt_ie, req_ie;
158 	int error, kscount, bytes;
159 
160 	bytes = count * sizeof (*kreq);
161 	kreq = (count == 1) ? &single_kreq : kmem_alloc(bytes, KM_SLEEP);
162 
163 	error = import_buffer(&cnt_ie, scount, &kscount, sizeof (kscount),
164 	    IE_IMPEXP);
165 	if (error != 0)
166 		req_ie = null_ie;
167 	else
168 		error = import_buffer(&req_ie, ureq, kreq, bytes, IE_IMPEXP);
169 
170 	DTRACE_XPV3(mmu__update__start, int, domid, int, count, mmu_update_t *,
171 	    ((error == -X_EFAULT) ? ureq : kreq));
172 
173 	if (error == 0)
174 		error = HYPERVISOR_mmu_update(kreq, count, &kscount, domid);
175 	export_buffer(&cnt_ie, &error);
176 	export_buffer(&req_ie, &error);
177 	if (count != 1)
178 		kmem_free(kreq, bytes);
179 
180 	DTRACE_XPV1(mmu__update__end, int, error);
181 	return (error);
182 }
183 
184 static int
185 privcmd_HYPERVISOR_domctl(xen_domctl_t *opp)
186 {
187 	xen_domctl_t op;
188 	import_export_t op_ie, sub_ie;
189 	int error = 0;
190 
191 	if ((error = import_buffer(&op_ie, opp, &op, sizeof (op),
192 	    IE_IMPEXP)) != 0)
193 		return (error);
194 
195 	sub_ie = null_ie;
196 
197 	/*
198 	 * Check this first because our wrapper will forcibly overwrite it.
199 	 */
200 	if (op.interface_version != XEN_DOMCTL_INTERFACE_VERSION) {
201 #ifdef DEBUG
202 		printf("domctl vers mismatch (cmd %d, found 0x%x, need 0x%x\n",
203 		    op.cmd, op.interface_version, XEN_DOMCTL_INTERFACE_VERSION);
204 #endif
205 		error = -X_EACCES;
206 		export_buffer(&op_ie, &error);
207 		return (error);
208 	}
209 
210 	/*
211 	 * Now handle any domctl ops with embedded pointers elsewhere
212 	 * in the user address space that also need to be tacked down
213 	 * while the hypervisor futzes with them.
214 	 */
215 	switch (op.cmd) {
216 	case XEN_DOMCTL_createdomain:
217 		DTRACE_XPV1(dom__create__start, xen_domctl_t *,
218 		    &op.u.createdomain);
219 		break;
220 
221 	case XEN_DOMCTL_destroydomain:
222 		DTRACE_XPV1(dom__destroy__start, domid_t, op.domain);
223 		break;
224 
225 	case XEN_DOMCTL_pausedomain:
226 		DTRACE_XPV1(dom__pause__start, domid_t, op.domain);
227 		break;
228 
229 	case XEN_DOMCTL_unpausedomain:
230 		DTRACE_XPV1(dom__unpause__start, domid_t, op.domain);
231 		break;
232 
233 	case XEN_DOMCTL_getdomaininfo:
234 		break;
235 
236 	case XEN_DOMCTL_getmemlist: {
237 		error = import_handle(&sub_ie, &op.u.getmemlist.buffer,
238 		    op.u.getmemlist.max_pfns * sizeof (xen_pfn_t), IE_EXPORT);
239 		break;
240 	}
241 
242 	case XEN_DOMCTL_getpageframeinfo:
243 		break;
244 
245 	case XEN_DOMCTL_getpageframeinfo2: {
246 		error = import_handle(&sub_ie, &op.u.getpageframeinfo2.array,
247 		    op.u.getpageframeinfo2.num * sizeof (ulong_t), IE_IMPEXP);
248 		break;
249 	}
250 
251 	case XEN_DOMCTL_shadow_op: {
252 		size_t size;
253 
254 		size = roundup(howmany(op.u.shadow_op.pages, NBBY),
255 		    sizeof (ulong_t));
256 		error = import_handle(&sub_ie,
257 		    &op.u.shadow_op.dirty_bitmap, size, IE_IMPEXP);
258 		break;
259 	}
260 
261 	case XEN_DOMCTL_max_mem:
262 		break;
263 
264 	case XEN_DOMCTL_setvcpucontext: {
265 		vcpu_guest_context_t *taddr;
266 		error = import_handle(&sub_ie, &op.u.vcpucontext.ctxt,
267 		    sizeof (vcpu_guest_context_t), IE_IMPORT);
268 		if (error == -X_EFAULT)
269 			/*LINTED: constant in conditional context*/
270 			get_xen_guest_handle_u(taddr, op.u.vcpucontext.ctxt);
271 		else
272 			taddr = sub_ie.ie_kaddr;
273 		DTRACE_XPV2(setvcpucontext__start, domid_t, op.domain,
274 		    vcpu_guest_context_t *, taddr);
275 		break;
276 	}
277 
278 	case XEN_DOMCTL_getvcpucontext: {
279 		error = import_handle(&sub_ie, &op.u.vcpucontext.ctxt,
280 		    sizeof (vcpu_guest_context_t), IE_EXPORT);
281 		break;
282 	}
283 
284 
285 	case XEN_DOMCTL_sethvmcontext: {
286 		error = import_handle(&sub_ie, &op.u.hvmcontext.buffer,
287 		    op.u.hvmcontext.size, IE_IMPORT);
288 		break;
289 	}
290 
291 	case XEN_DOMCTL_gethvmcontext: {
292 #if !defined(__GNUC__) && defined(__i386__)
293 		if (op.u.hvmcontext.buffer.u.p != NULL)
294 #else
295 		if (op.u.hvmcontext.buffer.p != NULL)
296 #endif
297 			error = import_handle(&sub_ie, &op.u.hvmcontext.buffer,
298 			    op.u.hvmcontext.size, IE_EXPORT);
299 		break;
300 	}
301 
302 	case XEN_DOMCTL_resumedomain:
303 	case XEN_DOMCTL_getvcpuinfo:
304 	case XEN_DOMCTL_setvcpuaffinity:
305 	case XEN_DOMCTL_getvcpuaffinity:
306 	case XEN_DOMCTL_max_vcpus:
307 	case XEN_DOMCTL_scheduler_op:
308 	case XEN_DOMCTL_setdomainhandle:
309 	case XEN_DOMCTL_setdebugging:
310 	case XEN_DOMCTL_irq_permission:
311 	case XEN_DOMCTL_iomem_permission:
312 	case XEN_DOMCTL_ioport_permission:
313 	case XEN_DOMCTL_hypercall_init:
314 	case XEN_DOMCTL_arch_setup:
315 	case XEN_DOMCTL_settimeoffset:
316 	case XEN_DOMCTL_real_mode_area:
317 	case XEN_DOMCTL_set_address_size:
318 	case XEN_DOMCTL_sendtrigger:
319 		break;
320 
321 	default:
322 #ifdef DEBUG
323 		printf("unrecognized HYPERVISOR_domctl %d\n", op.cmd);
324 #endif
325 		error = -X_EINVAL;
326 	}
327 
328 	if (error == 0)
329 		error = HYPERVISOR_domctl(&op);
330 
331 	export_buffer(&op_ie, &error);
332 	export_buffer(&sub_ie, &error);
333 
334 	switch (op.cmd) {
335 	case XEN_DOMCTL_createdomain:
336 		DTRACE_XPV1(dom__create__end, int, error);
337 		break;
338 	case XEN_DOMCTL_destroydomain:
339 		DTRACE_XPV1(dom__destroy__end, int, error);
340 		break;
341 	case XEN_DOMCTL_pausedomain:
342 		DTRACE_XPV1(dom__pause__end, int, error);
343 		break;
344 	case XEN_DOMCTL_unpausedomain:
345 		DTRACE_XPV1(dom__unpause__end, int, error);
346 		break;
347 	case XEN_DOMCTL_setvcpucontext:
348 		DTRACE_XPV1(setvcpucontext__end, int, error);
349 		break;
350 	default:
351 		;
352 	}
353 
354 	return (error);
355 }
356 
357 static int
358 privcmd_HYPERVISOR_sysctl(xen_sysctl_t *opp)
359 {
360 	xen_sysctl_t op;
361 	import_export_t op_ie, sub_ie;
362 	int error = 0;
363 
364 	if (import_buffer(&op_ie, opp, &op, sizeof (op), IE_IMPEXP) != 0)
365 		return (-X_EFAULT);
366 
367 	sub_ie = null_ie;
368 
369 	/*
370 	 * Check this first because our wrapper will forcibly overwrite it.
371 	 */
372 	if (op.interface_version != XEN_SYSCTL_INTERFACE_VERSION) {
373 		error = -X_EACCES;
374 		export_buffer(&op_ie, &error);
375 		return (error);
376 	}
377 
378 	switch (op.cmd) {
379 	case XEN_SYSCTL_readconsole: {
380 		error = import_handle(&sub_ie, &op.u.readconsole.buffer,
381 		    op.u.readconsole.count, IE_EXPORT);
382 		break;
383 	}
384 
385 	case XEN_SYSCTL_debug_keys: {
386 		error = import_handle(&sub_ie, &op.u.debug_keys.keys,
387 		    op.u.debug_keys.nr_keys, IE_IMPORT);
388 		break;
389 	}
390 
391 	case XEN_SYSCTL_tbuf_op:
392 	case XEN_SYSCTL_physinfo:
393 	case XEN_SYSCTL_sched_id:
394 		break;
395 
396 	case XEN_SYSCTL_perfc_op: {
397 		xen_sysctl_perfc_desc_t *scdp;
398 		/*
399 		 * If 'desc' is NULL, then the caller is asking for
400 		 * the number of counters.  If 'desc' is non-NULL,
401 		 * then we need to know how many counters there are
402 		 * before wiring down the output buffer appropriately.
403 		 */
404 		/*LINTED: constant in conditional context*/
405 		get_xen_guest_handle_u(scdp, op.u.perfc_op.desc);
406 		if (scdp != NULL) {
407 			static int numcounters = -1;
408 
409 			if (numcounters == -1) {
410 				xen_sysctl_t dop;
411 
412 				dop.cmd = XEN_SYSCTL_perfc_op;
413 				dop.interface_version =
414 				    XEN_SYSCTL_INTERFACE_VERSION;
415 				dop.u.perfc_op.cmd = XEN_SYSCTL_PERFCOP_query;
416 
417 				error = HYPERVISOR_sysctl(&dop);
418 				if (error != 0)
419 					break;
420 				numcounters = dop.u.perfc_op.nr_counters;
421 			}
422 			ASSERT(numcounters != -1);
423 			error = import_handle(&sub_ie, &op.u.perfc_op.desc,
424 			    (sizeof (xen_sysctl_perfc_desc_t) * numcounters),
425 			    IE_EXPORT);
426 		}
427 		break;
428 	}
429 
430 	case XEN_SYSCTL_getdomaininfolist: {
431 		error = import_handle(&sub_ie, &op.u.getdomaininfolist.buffer,
432 		    (op.u.getdomaininfolist.max_domains *
433 		    sizeof (xen_domctl_getdomaininfo_t)), IE_EXPORT);
434 		break;
435 	}
436 
437 	case XEN_SYSCTL_getcpuinfo:
438 		error = import_handle(&sub_ie, &op.u.getcpuinfo.info,
439 		    op.u.getcpuinfo.max_cpus *
440 		    sizeof (xen_sysctl_cpuinfo_t), IE_EXPORT);
441 		break;
442 	default:
443 #ifdef DEBUG
444 		printf("unrecognized HYPERVISOR_sysctl %d\n", op.cmd);
445 #endif
446 		error = -X_EINVAL;
447 	}
448 
449 	if (error == 0)
450 		error = HYPERVISOR_sysctl(&op);
451 
452 	export_buffer(&op_ie, &error);
453 	export_buffer(&sub_ie, &error);
454 
455 	return (error);
456 }
457 
458 static int
459 privcmd_HYPERVISOR_platform_op(xen_platform_op_t *opp)
460 {
461 	import_export_t op_ie, sub_ie;
462 	xen_platform_op_t op;
463 	int error;
464 
465 	if (import_buffer(&op_ie, opp, &op, sizeof (op), IE_IMPEXP) != 0)
466 		return (-X_EFAULT);
467 
468 	sub_ie = null_ie;
469 
470 	/*
471 	 * Check this first because our wrapper will forcibly overwrite it.
472 	 */
473 	if (op.interface_version != XENPF_INTERFACE_VERSION) {
474 		error = -X_EACCES;
475 		export_buffer(&op_ie, &error);
476 		return (error);
477 	}
478 
479 	/*
480 	 * Now handle any platform ops with embedded pointers elsewhere
481 	 * in the user address space that also need to be tacked down
482 	 * while the hypervisor futzes with them.
483 	 */
484 	switch (op.cmd) {
485 	case XENPF_settime:
486 	case XENPF_add_memtype:
487 	case XENPF_del_memtype:
488 	case XENPF_read_memtype:
489 	case XENPF_platform_quirk:
490 		break;
491 
492 	case XENPF_microcode_update:
493 		error = import_handle(&sub_ie, &op.u.microcode.data,
494 		    op.u.microcode.length, IE_IMPORT);
495 		break;
496 
497 	default:
498 #ifdef DEBUG
499 		printf("unrecognized HYPERVISOR_platform_op %d\n", op.cmd);
500 #endif
501 		return (-X_EINVAL);
502 	}
503 
504 	if (error == 0)
505 		error = HYPERVISOR_platform_op(&op);
506 
507 	export_buffer(&op_ie, &error);
508 	export_buffer(&sub_ie, &error);
509 
510 	return (error);
511 }
512 
513 static int
514 privcmd_HYPERVISOR_memory_op(int cmd, void *arg)
515 {
516 	int error = 0;
517 	import_export_t op_ie, sub_ie, gpfn_ie, mfn_ie;
518 	union {
519 		domid_t domid;
520 		struct xen_memory_reservation resv;
521 		struct xen_machphys_mfn_list xmml;
522 		struct xen_add_to_physmap xatp;
523 		struct xen_translate_gpfn_list tgl;
524 		struct xen_memory_map mm;
525 		struct xen_foreign_memory_map fmm;
526 	} op_arg;
527 
528 	op_ie = sub_ie = gpfn_ie = mfn_ie = null_ie;
529 
530 	switch (cmd) {
531 	case XENMEM_increase_reservation:
532 	case XENMEM_decrease_reservation:
533 	case XENMEM_populate_physmap: {
534 		ulong_t *taddr;
535 
536 		if (import_buffer(&op_ie, arg, &op_arg, sizeof (op_arg.resv),
537 		    IE_IMPEXP) != 0)
538 			return (-X_EFAULT);
539 
540 		error = import_handle(&sub_ie, &op_arg.resv.extent_start,
541 		    (op_arg.resv.nr_extents * sizeof (ulong_t)), IE_IMPEXP);
542 
543 		if (error == -X_EFAULT)
544 			/*LINTED: constant in conditional context*/
545 			get_xen_guest_handle(taddr, op_arg.resv.extent_start);
546 		else
547 			taddr = sub_ie.ie_kaddr;
548 
549 		switch (cmd) {
550 		case XENMEM_increase_reservation:
551 			DTRACE_XPV4(increase__reservation__start,
552 			    domid_t, op_arg.resv.domid,
553 			    ulong_t, op_arg.resv.nr_extents,
554 			    uint_t, op_arg.resv.extent_order,
555 			    ulong_t *, taddr);
556 			break;
557 		case XENMEM_decrease_reservation:
558 			DTRACE_XPV4(decrease__reservation__start,
559 			    domid_t, op_arg.resv.domid,
560 			    ulong_t, op_arg.resv.nr_extents,
561 			    uint_t, op_arg.resv.extent_order,
562 			    ulong_t *, taddr);
563 			break;
564 		case XENMEM_populate_physmap:
565 			DTRACE_XPV3(populate__physmap__start,
566 			    domid_t, op_arg.resv.domid,
567 			    ulong_t, op_arg.resv.nr_extents,
568 			    ulong_t *, taddr);
569 			break;
570 		}
571 
572 		break;
573 	}
574 
575 	case XENMEM_maximum_ram_page:
576 		break;
577 
578 	case XENMEM_current_reservation:
579 	case XENMEM_maximum_reservation:
580 	case XENMEM_maximum_gpfn:
581 		if (import_buffer(&op_ie, arg, &op_arg, sizeof (op_arg.domid),
582 		    IE_IMPEXP) != 0)
583 			return (-X_EFAULT);
584 		break;
585 
586 	case XENMEM_machphys_mfn_list: {
587 		if (import_buffer(&op_ie, arg, &op_arg, sizeof (op_arg.xmml),
588 		    IE_IMPEXP) != 0)
589 			return (-X_EFAULT);
590 
591 		error = import_handle(&sub_ie, &op_arg.xmml.extent_start,
592 		    (op_arg.xmml.max_extents * sizeof (ulong_t)), IE_IMPEXP);
593 		break;
594 	}
595 
596 	case XENMEM_add_to_physmap:
597 		if (import_buffer(&op_ie, arg, &op_arg, sizeof (op_arg.xatp),
598 		    IE_IMPEXP) != 0)
599 			return (-X_EFAULT);
600 		DTRACE_XPV4(add__to__physmap__start, domid_t,
601 		    op_arg.xatp.domid, uint_t, op_arg.xatp.space, ulong_t,
602 		    op_arg.xatp.idx, ulong_t, op_arg.xatp.gpfn);
603 		break;
604 
605 	case XENMEM_translate_gpfn_list: {
606 		if (import_buffer(&op_ie, arg, &op_arg, sizeof (op_arg.tgl),
607 		    IE_IMPEXP) != 0)
608 			return (-X_EFAULT);
609 
610 		error = import_handle(&gpfn_ie, &op_arg.tgl.gpfn_list,
611 		    (op_arg.tgl.nr_gpfns * sizeof (long)), IE_IMPORT);
612 		if (error == 0)
613 			error = import_handle(&mfn_ie, &op_arg.tgl.mfn_list,
614 			    (op_arg.tgl.nr_gpfns * sizeof (long)), IE_EXPORT);
615 		break;
616 	}
617 
618 	case XENMEM_memory_map:
619 	case XENMEM_machine_memory_map: {
620 		if (import_buffer(&op_ie, arg, &op_arg, sizeof (op_arg.mm),
621 		    IE_EXPORT) != 0)
622 			return (-X_EFAULT);
623 
624 		/*
625 		 * XXPV: ugh. e820entry is packed, but not in the kernel, since
626 		 * we remove all attributes; seems like this is a nice way to
627 		 * break mysteriously.
628 		 */
629 		error = import_handle(&sub_ie, &op_arg.mm.buffer,
630 		    (op_arg.mm.nr_entries * 20), IE_IMPEXP);
631 		break;
632 	}
633 
634 	case XENMEM_set_memory_map: {
635 		struct xen_memory_map *taddr;
636 		if (import_buffer(&op_ie, arg, &op_arg, sizeof (op_arg.fmm),
637 		    IE_IMPORT) != 0)
638 			return (-X_EFAULT);
639 
640 		/*
641 		 * As above.
642 		 */
643 		error = import_handle(&sub_ie, &op_arg.fmm.map.buffer,
644 		    (op_arg.fmm.map.nr_entries * 20), IE_IMPEXP);
645 
646 		if (error == -X_EFAULT)
647 			/*LINTED: constant in conditional context*/
648 			get_xen_guest_handle(taddr, op_arg.fmm.map.buffer);
649 		else
650 			taddr = sub_ie.ie_kaddr;
651 		DTRACE_XPV3(set__memory__map__start, domid_t,
652 		    op_arg.fmm.domid, int, op_arg.fmm.map.nr_entries,
653 		    struct xen_memory_map *, taddr);
654 		break;
655 	}
656 
657 	default:
658 #ifdef DEBUG
659 		printf("unrecognized HYPERVISOR_memory_op %d\n", cmd);
660 #endif
661 		return (-X_EINVAL);
662 	}
663 
664 	if (error == 0)
665 		error = HYPERVISOR_memory_op(cmd,
666 		    (arg == NULL) ? NULL: &op_arg);
667 
668 	export_buffer(&op_ie, &error);
669 	export_buffer(&sub_ie, &error);
670 	export_buffer(&gpfn_ie, &error);
671 	export_buffer(&mfn_ie, &error);
672 
673 	switch (cmd) {
674 	case XENMEM_increase_reservation:
675 		DTRACE_XPV1(increase__reservation__end, int, error);
676 		break;
677 	case XENMEM_decrease_reservation:
678 		DTRACE_XPV1(decrease__reservation__end, int, error);
679 		break;
680 	case XENMEM_populate_physmap:
681 		DTRACE_XPV1(populate__physmap__end, int, error);
682 		break;
683 	case XENMEM_add_to_physmap:
684 		DTRACE_XPV1(add__to__physmap__end, int, error);
685 		break;
686 	case XENMEM_set_memory_map:
687 		DTRACE_XPV1(set__memory__map__end, int, error);
688 		break;
689 	}
690 	return (error);
691 }
692 
693 static int
694 privcmd_HYPERVISOR_event_channel_op(int cmd, void *arg)
695 {
696 	int error;
697 	size_t size;
698 	import_export_t op_ie;
699 	uint32_t flags;
700 
701 	switch (cmd) {
702 	case EVTCHNOP_alloc_unbound:
703 		size = sizeof (evtchn_alloc_unbound_t);
704 		flags = IE_IMPEXP;
705 		break;
706 	case EVTCHNOP_bind_interdomain:
707 		size = sizeof (evtchn_bind_interdomain_t);
708 		flags = IE_IMPEXP;
709 		break;
710 	case EVTCHNOP_bind_virq:
711 		size = sizeof (evtchn_bind_virq_t);
712 		flags = IE_IMPEXP;
713 		break;
714 	case EVTCHNOP_bind_pirq:
715 		size = sizeof (evtchn_bind_pirq_t);
716 		flags = IE_IMPEXP;
717 		break;
718 	case EVTCHNOP_bind_ipi:
719 		size = sizeof (evtchn_bind_ipi_t);
720 		flags = IE_IMPEXP;
721 		break;
722 	case EVTCHNOP_close:
723 		size = sizeof (evtchn_close_t);
724 		flags = IE_IMPORT;
725 		break;
726 	case EVTCHNOP_send:
727 		size = sizeof (evtchn_send_t);
728 		flags = IE_IMPORT;
729 		break;
730 	case EVTCHNOP_status:
731 		size = sizeof (evtchn_status_t);
732 		flags = IE_IMPEXP;
733 		break;
734 	case EVTCHNOP_bind_vcpu:
735 		size = sizeof (evtchn_bind_vcpu_t);
736 		flags = IE_IMPORT;
737 		break;
738 	case EVTCHNOP_unmask:
739 		size = sizeof (evtchn_unmask_t);
740 		flags = IE_IMPORT;
741 		break;
742 	case EVTCHNOP_reset:
743 		size = sizeof (evtchn_reset_t);
744 		flags = IE_IMPORT;
745 		break;
746 
747 	default:
748 #ifdef DEBUG
749 		printf("unrecognized HYPERVISOR_event_channel op %d\n", cmd);
750 #endif
751 		return (-X_EINVAL);
752 	}
753 
754 	error = import_buffer(&op_ie, arg, NULL, size, flags);
755 
756 	/*
757 	 * If there is sufficient demand, we can replace this void * with
758 	 * the proper op structure pointer.
759 	 */
760 	DTRACE_XPV2(evtchn__op__start, int, cmd, void *,
761 	    ((error == -X_EFAULT) ? arg : op_ie.ie_kaddr));
762 
763 	if (error == 0)
764 		error = HYPERVISOR_event_channel_op(cmd, op_ie.ie_kaddr);
765 	export_buffer(&op_ie, &error);
766 
767 	DTRACE_XPV1(evtchn__op__end, int, error);
768 
769 	return (error);
770 }
771 
772 static int
773 privcmd_HYPERVISOR_xen_version(int cmd, void *arg)
774 {
775 	int error;
776 	int size = 0;
777 	import_export_t op_ie;
778 	uint32_t flags = IE_EXPORT;
779 
780 	switch (cmd) {
781 	case XENVER_version:
782 		break;
783 	case XENVER_extraversion:
784 		size = sizeof (xen_extraversion_t);
785 		break;
786 	case XENVER_compile_info:
787 		size = sizeof (xen_compile_info_t);
788 		break;
789 	case XENVER_capabilities:
790 		size = sizeof (xen_capabilities_info_t);
791 		break;
792 	case XENVER_changeset:
793 		size = sizeof (xen_changeset_info_t);
794 		break;
795 	case XENVER_platform_parameters:
796 		size = sizeof (xen_platform_parameters_t);
797 		break;
798 	case XENVER_get_features:
799 		flags = IE_IMPEXP;
800 		size = sizeof (xen_feature_info_t);
801 		break;
802 	case XENVER_pagesize:
803 		break;
804 	case XENVER_guest_handle:
805 		size = sizeof (xen_domain_handle_t);
806 		break;
807 
808 	default:
809 #ifdef DEBUG
810 		printf("unrecognized HYPERVISOR_xen_version op %d\n", cmd);
811 #endif
812 		return (-X_EINVAL);
813 	}
814 
815 	error = import_buffer(&op_ie, arg, NULL, size, flags);
816 	if (error == 0)
817 		error = HYPERVISOR_xen_version(cmd, op_ie.ie_kaddr);
818 	export_buffer(&op_ie, &error);
819 
820 	return (error);
821 }
822 
823 static int
824 privcmd_HYPERVISOR_acm_op(void *uacmctl)
825 {
826 	int error;
827 	struct xen_acmctl *acmctl;
828 	import_export_t op_ie;
829 
830 	error = import_buffer(&op_ie, uacmctl, NULL, sizeof (*acmctl),
831 	    IE_IMPEXP);
832 	if (error != 0)
833 		return (error);
834 
835 	acmctl = op_ie.ie_kaddr;
836 
837 	if (acmctl->interface_version != ACM_INTERFACE_VERSION) {
838 #ifdef DEBUG
839 		printf("acm vers mismatch (cmd %d, found 0x%x, need 0x%x\n",
840 		    acmctl->cmd, acmctl->interface_version,
841 		    ACM_INTERFACE_VERSION);
842 #endif
843 		error = -X_EACCES;
844 		export_buffer(&op_ie, &error);
845 		return (error);
846 	}
847 
848 	switch (acmctl->cmd) {
849 	case ACMOP_setpolicy:
850 	case ACMOP_getpolicy:
851 	case ACMOP_dumpstats:
852 	case ACMOP_getssid:
853 	case ACMOP_getdecision:
854 	case ACMOP_chgpolicy:
855 	case ACMOP_relabeldoms:
856 		break;
857 	default:
858 #ifdef DEBUG
859 		printf("unrecognized HYPERVISOR_acm_op op %d\n", acmctl->cmd);
860 #endif
861 		return (-X_EINVAL);
862 	}
863 
864 	if (error == 0)
865 		error = HYPERVISOR_acm_op(acmctl);
866 	export_buffer(&op_ie, &error);
867 
868 	return (error);
869 }
870 
871 static int
872 privcmd_HYPERVISOR_mmuext_op(struct mmuext_op *op, int count, uint_t *scount,
873     domid_t domid)
874 {
875 	int error, bytes;
876 	uint_t kscount;
877 	struct mmuext_op *kop, single_kop;
878 	import_export_t op_ie, scnt_ie;
879 
880 	op_ie = scnt_ie = null_ie;
881 	error = 0;
882 
883 	if (count >= 1) {
884 		bytes = count * sizeof (*kop);
885 		kop = (count == 1) ? &single_kop : kmem_alloc(bytes, KM_SLEEP);
886 		error = import_buffer(&op_ie, op, kop, bytes, IE_IMPORT);
887 	}
888 
889 	DTRACE_XPV2(mmu__ext__op__start, int, count, struct mmuext_op *,
890 	    ((error == -X_EFAULT) ? op : kop));
891 
892 	if (scount != NULL && error == 0)
893 		error = import_buffer(&scnt_ie, scount, &kscount,
894 		    sizeof (kscount), IE_EXPORT);
895 
896 	if (error == 0)
897 		error = HYPERVISOR_mmuext_op(kop, count, &kscount, domid);
898 	export_buffer(&op_ie, &error);
899 	export_buffer(&scnt_ie, &error);
900 
901 	DTRACE_XPV1(mmu__ext__op__end, int, error);
902 
903 	if (count > 1)
904 		kmem_free(kop, bytes);
905 	return (error);
906 }
907 
908 static int
909 privcmd_HYPERVISOR_hvm_op(int cmd, void *arg)
910 {
911 	int error;
912 	int size = 0;
913 	import_export_t arg_ie;
914 	uint32_t flags = IE_IMPORT;
915 
916 	switch (cmd) {
917 	case HVMOP_set_param:
918 	case HVMOP_get_param:
919 		size = sizeof (struct xen_hvm_param);
920 		flags = IE_IMPEXP;
921 		break;
922 	case HVMOP_set_pci_intx_level:
923 		size = sizeof (struct xen_hvm_set_pci_intx_level);
924 		break;
925 	case HVMOP_set_isa_irq_level:
926 		size = sizeof (struct xen_hvm_set_isa_irq_level);
927 		break;
928 	case HVMOP_set_pci_link_route:
929 		size = sizeof (struct xen_hvm_set_pci_link_route);
930 		break;
931 
932 	default:
933 #ifdef DEBUG
934 		printf("unrecognized HVM op 0x%x\n", cmd);
935 #endif
936 		return (-X_EINVAL);
937 	}
938 
939 	error = import_buffer(&arg_ie, arg, NULL, size, flags);
940 	if (error == 0)
941 		error = HYPERVISOR_hvm_op(cmd, arg_ie.ie_kaddr);
942 	export_buffer(&arg_ie, &error);
943 
944 	return (error);
945 }
946 
947 static int
948 privcmd_HYPERVISOR_sched_op(int cmd, void *arg)
949 {
950 	int error;
951 	int size = 0;
952 	import_export_t op_ie;
953 	struct sched_remote_shutdown op;
954 
955 	switch (cmd) {
956 	case SCHEDOP_remote_shutdown:
957 		size = sizeof (struct sched_remote_shutdown);
958 		break;
959 	default:
960 #ifdef DEBUG
961 		printf("unrecognized sched op 0x%x\n", cmd);
962 #endif
963 		return (-X_EINVAL);
964 	}
965 
966 	error = import_buffer(&op_ie, arg, &op, size, IE_IMPORT);
967 	if (error == 0)
968 		error = HYPERVISOR_sched_op(cmd, (arg == NULL) ? NULL : &op);
969 	export_buffer(&op_ie, &error);
970 
971 	return (error);
972 }
973 
974 int allow_all_hypercalls = 0;
975 int privcmd_efault_debug = 0;
976 
977 /*ARGSUSED*/
978 int
979 do_privcmd_hypercall(void *uarg, int mode, cred_t *cr, int *rval)
980 {
981 	privcmd_hypercall_t __hc, *hc = &__hc;
982 	int error;
983 
984 	if (ddi_copyin(uarg, hc, sizeof (*hc), mode))
985 		return (EFAULT);
986 
987 	switch (hc->op) {
988 	case __HYPERVISOR_mmu_update:
989 		error = privcmd_HYPERVISOR_mmu_update(
990 		    (mmu_update_t *)hc->arg[0], (int)hc->arg[1],
991 		    (int *)hc->arg[2], (domid_t)hc->arg[3]);
992 		break;
993 	case __HYPERVISOR_domctl:
994 		error = privcmd_HYPERVISOR_domctl(
995 		    (xen_domctl_t *)hc->arg[0]);
996 		break;
997 	case __HYPERVISOR_sysctl:
998 		error = privcmd_HYPERVISOR_sysctl(
999 		    (xen_sysctl_t *)hc->arg[0]);
1000 		break;
1001 	case __HYPERVISOR_platform_op:
1002 		error = privcmd_HYPERVISOR_platform_op(
1003 		    (xen_platform_op_t *)hc->arg[0]);
1004 		break;
1005 	case __HYPERVISOR_memory_op:
1006 		error = privcmd_HYPERVISOR_memory_op(
1007 		    (int)hc->arg[0], (void *)hc->arg[1]);
1008 		break;
1009 	case __HYPERVISOR_event_channel_op:
1010 		error = privcmd_HYPERVISOR_event_channel_op(
1011 		    (int)hc->arg[0], (void *)hc->arg[1]);
1012 		break;
1013 	case __HYPERVISOR_xen_version:
1014 		error = privcmd_HYPERVISOR_xen_version(
1015 		    (int)hc->arg[0], (void *)hc->arg[1]);
1016 		break;
1017 	case __HYPERVISOR_mmuext_op:
1018 		error = privcmd_HYPERVISOR_mmuext_op(
1019 		    (struct mmuext_op *)hc->arg[0], (int)hc->arg[1],
1020 		    (uint_t *)hc->arg[2], (domid_t)hc->arg[3]);
1021 		break;
1022 	case __HYPERVISOR_acm_op:
1023 		error = privcmd_HYPERVISOR_acm_op((void *)hc->arg[0]);
1024 		break;
1025 	case __HYPERVISOR_hvm_op:
1026 		error = privcmd_HYPERVISOR_hvm_op(
1027 		    (int)hc->arg[0], (void *)hc->arg[1]);
1028 		break;
1029 	case __HYPERVISOR_sched_op:
1030 		error = privcmd_HYPERVISOR_sched_op(
1031 		    (int)hc->arg[0], (void *)hc->arg[1]);
1032 		break;
1033 	default:
1034 		if (allow_all_hypercalls)
1035 			error = __hypercall5(hc->op, hc->arg[0], hc->arg[1],
1036 			    hc->arg[2], hc->arg[3], hc->arg[4]);
1037 		else {
1038 #ifdef DEBUG
1039 			printf("unrecognized hypercall %ld\n", hc->op);
1040 #endif
1041 			error = -X_EPERM;
1042 		}
1043 		break;
1044 	}
1045 
1046 	if (error > 0) {
1047 		*rval = error;
1048 		error = 0;
1049 	} else if (error != 0)
1050 		error = xen_xlate_errcode(error);
1051 
1052 	return (error);
1053 }
1054