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