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, sub2_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 	sub2_ie = null_ie;
369 
370 	/*
371 	 * Check this first because our wrapper will forcibly overwrite it.
372 	 */
373 	if (op.interface_version != XEN_SYSCTL_INTERFACE_VERSION) {
374 		error = -X_EACCES;
375 		export_buffer(&op_ie, &error);
376 		return (error);
377 	}
378 
379 	switch (op.cmd) {
380 	case XEN_SYSCTL_readconsole: {
381 		error = import_handle(&sub_ie, &op.u.readconsole.buffer,
382 		    op.u.readconsole.count, IE_EXPORT);
383 		break;
384 	}
385 
386 	case XEN_SYSCTL_debug_keys: {
387 		error = import_handle(&sub_ie, &op.u.debug_keys.keys,
388 		    op.u.debug_keys.nr_keys, IE_IMPORT);
389 		break;
390 	}
391 
392 	case XEN_SYSCTL_tbuf_op:
393 	case XEN_SYSCTL_physinfo:
394 	case XEN_SYSCTL_sched_id:
395 		break;
396 
397 	case XEN_SYSCTL_perfc_op: {
398 		xen_sysctl_perfc_desc_t *scdp;
399 		/*
400 		 * If 'desc' is NULL, then the caller is asking for
401 		 * the number of counters.  If 'desc' is non-NULL,
402 		 * then we need to know how many counters there are
403 		 * before wiring down the output buffer appropriately.
404 		 */
405 		/*LINTED: constant in conditional context*/
406 		get_xen_guest_handle_u(scdp, op.u.perfc_op.desc);
407 		if (scdp != NULL) {
408 			static int numcounters = -1;
409 			static int numvals = -1;
410 
411 			if (numcounters == -1) {
412 				xen_sysctl_t dop;
413 
414 				dop.cmd = XEN_SYSCTL_perfc_op;
415 				dop.interface_version =
416 				    XEN_SYSCTL_INTERFACE_VERSION;
417 				dop.u.perfc_op.cmd = XEN_SYSCTL_PERFCOP_query;
418 				/*LINTED: constant in conditional context*/
419 				set_xen_guest_handle_u(dop.u.perfc_op.desc,
420 				    NULL);
421 				/*LINTED: constant in conditional context*/
422 				set_xen_guest_handle_u(dop.u.perfc_op.val,
423 				    NULL);
424 
425 				error = HYPERVISOR_sysctl(&dop);
426 				if (error != 0)
427 					break;
428 				numcounters = dop.u.perfc_op.nr_counters;
429 				numvals = dop.u.perfc_op.nr_vals;
430 			}
431 			ASSERT(numcounters != -1);
432 			ASSERT(numvals != -1);
433 			error = import_handle(&sub_ie, &op.u.perfc_op.desc,
434 			    (sizeof (xen_sysctl_perfc_desc_t) * numcounters),
435 			    IE_EXPORT);
436 			error = import_handle(&sub2_ie, &op.u.perfc_op.val,
437 			    (sizeof (xen_sysctl_perfc_val_t) * numvals),
438 			    IE_EXPORT);
439 		}
440 		break;
441 	}
442 
443 	case XEN_SYSCTL_getdomaininfolist: {
444 		error = import_handle(&sub_ie, &op.u.getdomaininfolist.buffer,
445 		    (op.u.getdomaininfolist.max_domains *
446 		    sizeof (xen_domctl_getdomaininfo_t)), IE_EXPORT);
447 		break;
448 	}
449 
450 	case XEN_SYSCTL_getcpuinfo:
451 		error = import_handle(&sub_ie, &op.u.getcpuinfo.info,
452 		    op.u.getcpuinfo.max_cpus *
453 		    sizeof (xen_sysctl_cpuinfo_t), IE_EXPORT);
454 		break;
455 	default:
456 #ifdef DEBUG
457 		printf("unrecognized HYPERVISOR_sysctl %d\n", op.cmd);
458 #endif
459 		error = -X_EINVAL;
460 	}
461 
462 	if (error == 0)
463 		error = HYPERVISOR_sysctl(&op);
464 
465 	export_buffer(&op_ie, &error);
466 	export_buffer(&sub_ie, &error);
467 	export_buffer(&sub2_ie, &error);
468 
469 	return (error);
470 }
471 
472 static int
473 privcmd_HYPERVISOR_platform_op(xen_platform_op_t *opp)
474 {
475 	import_export_t op_ie, sub_ie;
476 	xen_platform_op_t op;
477 	int error;
478 
479 	if (import_buffer(&op_ie, opp, &op, sizeof (op), IE_IMPEXP) != 0)
480 		return (-X_EFAULT);
481 
482 	sub_ie = null_ie;
483 
484 	/*
485 	 * Check this first because our wrapper will forcibly overwrite it.
486 	 */
487 	if (op.interface_version != XENPF_INTERFACE_VERSION) {
488 		error = -X_EACCES;
489 		export_buffer(&op_ie, &error);
490 		return (error);
491 	}
492 
493 	/*
494 	 * Now handle any platform ops with embedded pointers elsewhere
495 	 * in the user address space that also need to be tacked down
496 	 * while the hypervisor futzes with them.
497 	 */
498 	switch (op.cmd) {
499 	case XENPF_settime:
500 	case XENPF_add_memtype:
501 	case XENPF_del_memtype:
502 	case XENPF_read_memtype:
503 	case XENPF_platform_quirk:
504 		break;
505 
506 	case XENPF_microcode_update:
507 		error = import_handle(&sub_ie, &op.u.microcode.data,
508 		    op.u.microcode.length, IE_IMPORT);
509 		break;
510 
511 	default:
512 #ifdef DEBUG
513 		printf("unrecognized HYPERVISOR_platform_op %d\n", op.cmd);
514 #endif
515 		return (-X_EINVAL);
516 	}
517 
518 	if (error == 0)
519 		error = HYPERVISOR_platform_op(&op);
520 
521 	export_buffer(&op_ie, &error);
522 	export_buffer(&sub_ie, &error);
523 
524 	return (error);
525 }
526 
527 static int
528 privcmd_HYPERVISOR_memory_op(int cmd, void *arg)
529 {
530 	int error = 0;
531 	import_export_t op_ie, sub_ie, gpfn_ie, mfn_ie;
532 	union {
533 		domid_t domid;
534 		struct xen_memory_reservation resv;
535 		struct xen_machphys_mfn_list xmml;
536 		struct xen_add_to_physmap xatp;
537 		struct xen_translate_gpfn_list tgl;
538 		struct xen_memory_map mm;
539 		struct xen_foreign_memory_map fmm;
540 	} op_arg;
541 
542 	op_ie = sub_ie = gpfn_ie = mfn_ie = null_ie;
543 
544 	switch (cmd) {
545 	case XENMEM_increase_reservation:
546 	case XENMEM_decrease_reservation:
547 	case XENMEM_populate_physmap: {
548 		ulong_t *taddr;
549 
550 		if (import_buffer(&op_ie, arg, &op_arg, sizeof (op_arg.resv),
551 		    IE_IMPEXP) != 0)
552 			return (-X_EFAULT);
553 
554 		error = import_handle(&sub_ie, &op_arg.resv.extent_start,
555 		    (op_arg.resv.nr_extents * sizeof (ulong_t)), IE_IMPEXP);
556 
557 		if (error == -X_EFAULT)
558 			/*LINTED: constant in conditional context*/
559 			get_xen_guest_handle(taddr, op_arg.resv.extent_start);
560 		else
561 			taddr = sub_ie.ie_kaddr;
562 
563 		switch (cmd) {
564 		case XENMEM_increase_reservation:
565 			DTRACE_XPV4(increase__reservation__start,
566 			    domid_t, op_arg.resv.domid,
567 			    ulong_t, op_arg.resv.nr_extents,
568 			    uint_t, op_arg.resv.extent_order,
569 			    ulong_t *, taddr);
570 			break;
571 		case XENMEM_decrease_reservation:
572 			DTRACE_XPV4(decrease__reservation__start,
573 			    domid_t, op_arg.resv.domid,
574 			    ulong_t, op_arg.resv.nr_extents,
575 			    uint_t, op_arg.resv.extent_order,
576 			    ulong_t *, taddr);
577 			break;
578 		case XENMEM_populate_physmap:
579 			DTRACE_XPV3(populate__physmap__start,
580 			    domid_t, op_arg.resv.domid,
581 			    ulong_t, op_arg.resv.nr_extents,
582 			    ulong_t *, taddr);
583 			break;
584 		}
585 
586 		break;
587 	}
588 
589 	case XENMEM_maximum_ram_page:
590 		break;
591 
592 	case XENMEM_current_reservation:
593 	case XENMEM_maximum_reservation:
594 	case XENMEM_maximum_gpfn:
595 		if (import_buffer(&op_ie, arg, &op_arg, sizeof (op_arg.domid),
596 		    IE_IMPEXP) != 0)
597 			return (-X_EFAULT);
598 		break;
599 
600 	case XENMEM_machphys_mfn_list: {
601 		if (import_buffer(&op_ie, arg, &op_arg, sizeof (op_arg.xmml),
602 		    IE_IMPEXP) != 0)
603 			return (-X_EFAULT);
604 
605 		error = import_handle(&sub_ie, &op_arg.xmml.extent_start,
606 		    (op_arg.xmml.max_extents * sizeof (ulong_t)), IE_IMPEXP);
607 		break;
608 	}
609 
610 	case XENMEM_add_to_physmap:
611 		if (import_buffer(&op_ie, arg, &op_arg, sizeof (op_arg.xatp),
612 		    IE_IMPEXP) != 0)
613 			return (-X_EFAULT);
614 		DTRACE_XPV4(add__to__physmap__start, domid_t,
615 		    op_arg.xatp.domid, uint_t, op_arg.xatp.space, ulong_t,
616 		    op_arg.xatp.idx, ulong_t, op_arg.xatp.gpfn);
617 		break;
618 
619 	case XENMEM_translate_gpfn_list: {
620 		if (import_buffer(&op_ie, arg, &op_arg, sizeof (op_arg.tgl),
621 		    IE_IMPEXP) != 0)
622 			return (-X_EFAULT);
623 
624 		error = import_handle(&gpfn_ie, &op_arg.tgl.gpfn_list,
625 		    (op_arg.tgl.nr_gpfns * sizeof (long)), IE_IMPORT);
626 		if (error == 0)
627 			error = import_handle(&mfn_ie, &op_arg.tgl.mfn_list,
628 			    (op_arg.tgl.nr_gpfns * sizeof (long)), IE_EXPORT);
629 		break;
630 	}
631 
632 	case XENMEM_memory_map:
633 	case XENMEM_machine_memory_map: {
634 		if (import_buffer(&op_ie, arg, &op_arg, sizeof (op_arg.mm),
635 		    IE_EXPORT) != 0)
636 			return (-X_EFAULT);
637 
638 		/*
639 		 * XXPV: ugh. e820entry is packed, but not in the kernel, since
640 		 * we remove all attributes; seems like this is a nice way to
641 		 * break mysteriously.
642 		 */
643 		error = import_handle(&sub_ie, &op_arg.mm.buffer,
644 		    (op_arg.mm.nr_entries * 20), IE_IMPEXP);
645 		break;
646 	}
647 
648 	case XENMEM_set_memory_map: {
649 		struct xen_memory_map *taddr;
650 		if (import_buffer(&op_ie, arg, &op_arg, sizeof (op_arg.fmm),
651 		    IE_IMPORT) != 0)
652 			return (-X_EFAULT);
653 
654 		/*
655 		 * As above.
656 		 */
657 		error = import_handle(&sub_ie, &op_arg.fmm.map.buffer,
658 		    (op_arg.fmm.map.nr_entries * 20), IE_IMPEXP);
659 
660 		if (error == -X_EFAULT)
661 			/*LINTED: constant in conditional context*/
662 			get_xen_guest_handle(taddr, op_arg.fmm.map.buffer);
663 		else
664 			taddr = sub_ie.ie_kaddr;
665 		DTRACE_XPV3(set__memory__map__start, domid_t,
666 		    op_arg.fmm.domid, int, op_arg.fmm.map.nr_entries,
667 		    struct xen_memory_map *, taddr);
668 		break;
669 	}
670 
671 	default:
672 #ifdef DEBUG
673 		printf("unrecognized HYPERVISOR_memory_op %d\n", cmd);
674 #endif
675 		return (-X_EINVAL);
676 	}
677 
678 	if (error == 0)
679 		error = HYPERVISOR_memory_op(cmd,
680 		    (arg == NULL) ? NULL: &op_arg);
681 
682 	export_buffer(&op_ie, &error);
683 	export_buffer(&sub_ie, &error);
684 	export_buffer(&gpfn_ie, &error);
685 	export_buffer(&mfn_ie, &error);
686 
687 	switch (cmd) {
688 	case XENMEM_increase_reservation:
689 		DTRACE_XPV1(increase__reservation__end, int, error);
690 		break;
691 	case XENMEM_decrease_reservation:
692 		DTRACE_XPV1(decrease__reservation__end, int, error);
693 		break;
694 	case XENMEM_populate_physmap:
695 		DTRACE_XPV1(populate__physmap__end, int, error);
696 		break;
697 	case XENMEM_add_to_physmap:
698 		DTRACE_XPV1(add__to__physmap__end, int, error);
699 		break;
700 	case XENMEM_set_memory_map:
701 		DTRACE_XPV1(set__memory__map__end, int, error);
702 		break;
703 	}
704 	return (error);
705 }
706 
707 static int
708 privcmd_HYPERVISOR_event_channel_op(int cmd, void *arg)
709 {
710 	int error;
711 	size_t size;
712 	import_export_t op_ie;
713 	uint32_t flags;
714 
715 	switch (cmd) {
716 	case EVTCHNOP_alloc_unbound:
717 		size = sizeof (evtchn_alloc_unbound_t);
718 		flags = IE_IMPEXP;
719 		break;
720 	case EVTCHNOP_bind_interdomain:
721 		size = sizeof (evtchn_bind_interdomain_t);
722 		flags = IE_IMPEXP;
723 		break;
724 	case EVTCHNOP_bind_virq:
725 		size = sizeof (evtchn_bind_virq_t);
726 		flags = IE_IMPEXP;
727 		break;
728 	case EVTCHNOP_bind_pirq:
729 		size = sizeof (evtchn_bind_pirq_t);
730 		flags = IE_IMPEXP;
731 		break;
732 	case EVTCHNOP_bind_ipi:
733 		size = sizeof (evtchn_bind_ipi_t);
734 		flags = IE_IMPEXP;
735 		break;
736 	case EVTCHNOP_close:
737 		size = sizeof (evtchn_close_t);
738 		flags = IE_IMPORT;
739 		break;
740 	case EVTCHNOP_send:
741 		size = sizeof (evtchn_send_t);
742 		flags = IE_IMPORT;
743 		break;
744 	case EVTCHNOP_status:
745 		size = sizeof (evtchn_status_t);
746 		flags = IE_IMPEXP;
747 		break;
748 	case EVTCHNOP_bind_vcpu:
749 		size = sizeof (evtchn_bind_vcpu_t);
750 		flags = IE_IMPORT;
751 		break;
752 	case EVTCHNOP_unmask:
753 		size = sizeof (evtchn_unmask_t);
754 		flags = IE_IMPORT;
755 		break;
756 	case EVTCHNOP_reset:
757 		size = sizeof (evtchn_reset_t);
758 		flags = IE_IMPORT;
759 		break;
760 
761 	default:
762 #ifdef DEBUG
763 		printf("unrecognized HYPERVISOR_event_channel op %d\n", cmd);
764 #endif
765 		return (-X_EINVAL);
766 	}
767 
768 	error = import_buffer(&op_ie, arg, NULL, size, flags);
769 
770 	/*
771 	 * If there is sufficient demand, we can replace this void * with
772 	 * the proper op structure pointer.
773 	 */
774 	DTRACE_XPV2(evtchn__op__start, int, cmd, void *,
775 	    ((error == -X_EFAULT) ? arg : op_ie.ie_kaddr));
776 
777 	if (error == 0)
778 		error = HYPERVISOR_event_channel_op(cmd, op_ie.ie_kaddr);
779 	export_buffer(&op_ie, &error);
780 
781 	DTRACE_XPV1(evtchn__op__end, int, error);
782 
783 	return (error);
784 }
785 
786 static int
787 privcmd_HYPERVISOR_xen_version(int cmd, void *arg)
788 {
789 	int error;
790 	int size = 0;
791 	import_export_t op_ie;
792 	uint32_t flags = IE_EXPORT;
793 
794 	switch (cmd) {
795 	case XENVER_version:
796 		break;
797 	case XENVER_extraversion:
798 		size = sizeof (xen_extraversion_t);
799 		break;
800 	case XENVER_compile_info:
801 		size = sizeof (xen_compile_info_t);
802 		break;
803 	case XENVER_capabilities:
804 		size = sizeof (xen_capabilities_info_t);
805 		break;
806 	case XENVER_changeset:
807 		size = sizeof (xen_changeset_info_t);
808 		break;
809 	case XENVER_platform_parameters:
810 		size = sizeof (xen_platform_parameters_t);
811 		break;
812 	case XENVER_get_features:
813 		flags = IE_IMPEXP;
814 		size = sizeof (xen_feature_info_t);
815 		break;
816 	case XENVER_pagesize:
817 		break;
818 	case XENVER_guest_handle:
819 		size = sizeof (xen_domain_handle_t);
820 		break;
821 
822 	default:
823 #ifdef DEBUG
824 		printf("unrecognized HYPERVISOR_xen_version op %d\n", cmd);
825 #endif
826 		return (-X_EINVAL);
827 	}
828 
829 	error = import_buffer(&op_ie, arg, NULL, size, flags);
830 	if (error == 0)
831 		error = HYPERVISOR_xen_version(cmd, op_ie.ie_kaddr);
832 	export_buffer(&op_ie, &error);
833 
834 	return (error);
835 }
836 
837 static int
838 privcmd_HYPERVISOR_acm_op(void *uacmctl)
839 {
840 	int error;
841 	struct xen_acmctl *acmctl;
842 	import_export_t op_ie;
843 
844 	error = import_buffer(&op_ie, uacmctl, NULL, sizeof (*acmctl),
845 	    IE_IMPEXP);
846 	if (error != 0)
847 		return (error);
848 
849 	acmctl = op_ie.ie_kaddr;
850 
851 	if (acmctl->interface_version != ACM_INTERFACE_VERSION) {
852 #ifdef DEBUG
853 		printf("acm vers mismatch (cmd %d, found 0x%x, need 0x%x\n",
854 		    acmctl->cmd, acmctl->interface_version,
855 		    ACM_INTERFACE_VERSION);
856 #endif
857 		error = -X_EACCES;
858 		export_buffer(&op_ie, &error);
859 		return (error);
860 	}
861 
862 	switch (acmctl->cmd) {
863 	case ACMOP_setpolicy:
864 	case ACMOP_getpolicy:
865 	case ACMOP_dumpstats:
866 	case ACMOP_getssid:
867 	case ACMOP_getdecision:
868 	case ACMOP_chgpolicy:
869 	case ACMOP_relabeldoms:
870 		break;
871 	default:
872 #ifdef DEBUG
873 		printf("unrecognized HYPERVISOR_acm_op op %d\n", acmctl->cmd);
874 #endif
875 		return (-X_EINVAL);
876 	}
877 
878 	if (error == 0)
879 		error = HYPERVISOR_acm_op(acmctl);
880 	export_buffer(&op_ie, &error);
881 
882 	return (error);
883 }
884 
885 static int
886 privcmd_HYPERVISOR_mmuext_op(struct mmuext_op *op, int count, uint_t *scount,
887     domid_t domid)
888 {
889 	int error, bytes;
890 	uint_t kscount;
891 	struct mmuext_op *kop, single_kop;
892 	import_export_t op_ie, scnt_ie;
893 
894 	op_ie = scnt_ie = null_ie;
895 	error = 0;
896 
897 	if (count >= 1) {
898 		bytes = count * sizeof (*kop);
899 		kop = (count == 1) ? &single_kop : kmem_alloc(bytes, KM_SLEEP);
900 		error = import_buffer(&op_ie, op, kop, bytes, IE_IMPORT);
901 	}
902 
903 	DTRACE_XPV2(mmu__ext__op__start, int, count, struct mmuext_op *,
904 	    ((error == -X_EFAULT) ? op : kop));
905 
906 	if (scount != NULL && error == 0)
907 		error = import_buffer(&scnt_ie, scount, &kscount,
908 		    sizeof (kscount), IE_EXPORT);
909 
910 	if (error == 0)
911 		error = HYPERVISOR_mmuext_op(kop, count, &kscount, domid);
912 	export_buffer(&op_ie, &error);
913 	export_buffer(&scnt_ie, &error);
914 
915 	DTRACE_XPV1(mmu__ext__op__end, int, error);
916 
917 	if (count > 1)
918 		kmem_free(kop, bytes);
919 	return (error);
920 }
921 
922 static int
923 privcmd_HYPERVISOR_hvm_op(int cmd, void *arg)
924 {
925 	int error;
926 	int size = 0;
927 	import_export_t arg_ie;
928 	uint32_t flags = IE_IMPORT;
929 
930 	switch (cmd) {
931 	case HVMOP_set_param:
932 	case HVMOP_get_param:
933 		size = sizeof (struct xen_hvm_param);
934 		flags = IE_IMPEXP;
935 		break;
936 	case HVMOP_set_pci_intx_level:
937 		size = sizeof (struct xen_hvm_set_pci_intx_level);
938 		break;
939 	case HVMOP_set_isa_irq_level:
940 		size = sizeof (struct xen_hvm_set_isa_irq_level);
941 		break;
942 	case HVMOP_set_pci_link_route:
943 		size = sizeof (struct xen_hvm_set_pci_link_route);
944 		break;
945 
946 	default:
947 #ifdef DEBUG
948 		printf("unrecognized HVM op 0x%x\n", cmd);
949 #endif
950 		return (-X_EINVAL);
951 	}
952 
953 	error = import_buffer(&arg_ie, arg, NULL, size, flags);
954 	if (error == 0)
955 		error = HYPERVISOR_hvm_op(cmd, arg_ie.ie_kaddr);
956 	export_buffer(&arg_ie, &error);
957 
958 	return (error);
959 }
960 
961 static int
962 privcmd_HYPERVISOR_sched_op(int cmd, void *arg)
963 {
964 	int error;
965 	int size = 0;
966 	import_export_t op_ie;
967 	struct sched_remote_shutdown op;
968 
969 	switch (cmd) {
970 	case SCHEDOP_remote_shutdown:
971 		size = sizeof (struct sched_remote_shutdown);
972 		break;
973 	default:
974 #ifdef DEBUG
975 		printf("unrecognized sched op 0x%x\n", cmd);
976 #endif
977 		return (-X_EINVAL);
978 	}
979 
980 	error = import_buffer(&op_ie, arg, &op, size, IE_IMPORT);
981 	if (error == 0)
982 		error = HYPERVISOR_sched_op(cmd, (arg == NULL) ? NULL : &op);
983 	export_buffer(&op_ie, &error);
984 
985 	return (error);
986 }
987 
988 int allow_all_hypercalls = 0;
989 int privcmd_efault_debug = 0;
990 
991 /*ARGSUSED*/
992 int
993 do_privcmd_hypercall(void *uarg, int mode, cred_t *cr, int *rval)
994 {
995 	privcmd_hypercall_t __hc, *hc = &__hc;
996 	int error;
997 
998 	if (ddi_copyin(uarg, hc, sizeof (*hc), mode))
999 		return (EFAULT);
1000 
1001 	switch (hc->op) {
1002 	case __HYPERVISOR_mmu_update:
1003 		error = privcmd_HYPERVISOR_mmu_update(
1004 		    (mmu_update_t *)hc->arg[0], (int)hc->arg[1],
1005 		    (int *)hc->arg[2], (domid_t)hc->arg[3]);
1006 		break;
1007 	case __HYPERVISOR_domctl:
1008 		error = privcmd_HYPERVISOR_domctl(
1009 		    (xen_domctl_t *)hc->arg[0]);
1010 		break;
1011 	case __HYPERVISOR_sysctl:
1012 		error = privcmd_HYPERVISOR_sysctl(
1013 		    (xen_sysctl_t *)hc->arg[0]);
1014 		break;
1015 	case __HYPERVISOR_platform_op:
1016 		error = privcmd_HYPERVISOR_platform_op(
1017 		    (xen_platform_op_t *)hc->arg[0]);
1018 		break;
1019 	case __HYPERVISOR_memory_op:
1020 		error = privcmd_HYPERVISOR_memory_op(
1021 		    (int)hc->arg[0], (void *)hc->arg[1]);
1022 		break;
1023 	case __HYPERVISOR_event_channel_op:
1024 		error = privcmd_HYPERVISOR_event_channel_op(
1025 		    (int)hc->arg[0], (void *)hc->arg[1]);
1026 		break;
1027 	case __HYPERVISOR_xen_version:
1028 		error = privcmd_HYPERVISOR_xen_version(
1029 		    (int)hc->arg[0], (void *)hc->arg[1]);
1030 		break;
1031 	case __HYPERVISOR_mmuext_op:
1032 		error = privcmd_HYPERVISOR_mmuext_op(
1033 		    (struct mmuext_op *)hc->arg[0], (int)hc->arg[1],
1034 		    (uint_t *)hc->arg[2], (domid_t)hc->arg[3]);
1035 		break;
1036 	case __HYPERVISOR_acm_op:
1037 		error = privcmd_HYPERVISOR_acm_op((void *)hc->arg[0]);
1038 		break;
1039 	case __HYPERVISOR_hvm_op:
1040 		error = privcmd_HYPERVISOR_hvm_op(
1041 		    (int)hc->arg[0], (void *)hc->arg[1]);
1042 		break;
1043 	case __HYPERVISOR_sched_op:
1044 		error = privcmd_HYPERVISOR_sched_op(
1045 		    (int)hc->arg[0], (void *)hc->arg[1]);
1046 		break;
1047 	default:
1048 		if (allow_all_hypercalls)
1049 			error = __hypercall5(hc->op, hc->arg[0], hc->arg[1],
1050 			    hc->arg[2], hc->arg[3], hc->arg[4]);
1051 		else {
1052 #ifdef DEBUG
1053 			printf("unrecognized hypercall %ld\n", hc->op);
1054 #endif
1055 			error = -X_EPERM;
1056 		}
1057 		break;
1058 	}
1059 
1060 	if (error > 0) {
1061 		*rval = error;
1062 		error = 0;
1063 	} else if (error != 0)
1064 		error = xen_xlate_errcode(error);
1065 
1066 	return (error);
1067 }
1068