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