1 /*
2 * pv/callback.c
3 *
4 * hypercall handles and helper functions for guest callback
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms and conditions of the GNU General Public
8 * License, version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public
16 * License along with this program; If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <xen/event.h>
20 #include <xen/hypercall.h>
21 #include <xen/guest_access.h>
22 #include <xen/lib.h>
23 #include <xen/sched.h>
24 #include <compat/callback.h>
25 #include <compat/nmi.h>
26
27 #include <asm/current.h>
28 #include <asm/nmi.h>
29 #include <asm/shared.h>
30 #include <asm/traps.h>
31
32 #include <public/callback.h>
33
34 /* Override macros from asm/page.h to make them work with mfn_t */
35 #undef mfn_to_page
36 #define mfn_to_page(mfn) __mfn_to_page(mfn_x(mfn))
37 #undef page_to_mfn
38 #define page_to_mfn(pg) _mfn(__page_to_mfn(pg))
39
register_guest_nmi_callback(unsigned long address)40 static int register_guest_nmi_callback(unsigned long address)
41 {
42 struct vcpu *curr = current;
43 struct domain *d = curr->domain;
44 struct trap_info *t = &curr->arch.pv_vcpu.trap_ctxt[TRAP_nmi];
45
46 if ( !is_canonical_address(address) )
47 return -EINVAL;
48
49 t->vector = TRAP_nmi;
50 t->flags = 0;
51 t->cs = (is_pv_32bit_domain(d) ?
52 FLAT_COMPAT_KERNEL_CS : FLAT_KERNEL_CS);
53 t->address = address;
54 TI_SET_IF(t, 1);
55
56 /*
57 * If no handler was registered we can 'lose the NMI edge'. Re-assert it
58 * now.
59 */
60 if ( curr->vcpu_id == 0 && arch_get_nmi_reason(d) != 0 )
61 curr->nmi_pending = 1;
62
63 return 0;
64 }
65
unregister_guest_nmi_callback(void)66 static void unregister_guest_nmi_callback(void)
67 {
68 struct vcpu *curr = current;
69 struct trap_info *t = &curr->arch.pv_vcpu.trap_ctxt[TRAP_nmi];
70
71 memset(t, 0, sizeof(*t));
72 }
73
register_guest_callback(struct callback_register * reg)74 static long register_guest_callback(struct callback_register *reg)
75 {
76 long ret = 0;
77 struct vcpu *curr = current;
78
79 if ( !is_canonical_address(reg->address) )
80 return -EINVAL;
81
82 switch ( reg->type )
83 {
84 case CALLBACKTYPE_event:
85 curr->arch.pv_vcpu.event_callback_eip = reg->address;
86 break;
87
88 case CALLBACKTYPE_failsafe:
89 curr->arch.pv_vcpu.failsafe_callback_eip = reg->address;
90 if ( reg->flags & CALLBACKF_mask_events )
91 set_bit(_VGCF_failsafe_disables_events,
92 &curr->arch.vgc_flags);
93 else
94 clear_bit(_VGCF_failsafe_disables_events,
95 &curr->arch.vgc_flags);
96 break;
97
98 case CALLBACKTYPE_syscall:
99 curr->arch.pv_vcpu.syscall_callback_eip = reg->address;
100 if ( reg->flags & CALLBACKF_mask_events )
101 set_bit(_VGCF_syscall_disables_events,
102 &curr->arch.vgc_flags);
103 else
104 clear_bit(_VGCF_syscall_disables_events,
105 &curr->arch.vgc_flags);
106 break;
107
108 case CALLBACKTYPE_syscall32:
109 curr->arch.pv_vcpu.syscall32_callback_eip = reg->address;
110 curr->arch.pv_vcpu.syscall32_disables_events =
111 !!(reg->flags & CALLBACKF_mask_events);
112 break;
113
114 case CALLBACKTYPE_sysenter:
115 curr->arch.pv_vcpu.sysenter_callback_eip = reg->address;
116 curr->arch.pv_vcpu.sysenter_disables_events =
117 !!(reg->flags & CALLBACKF_mask_events);
118 break;
119
120 case CALLBACKTYPE_nmi:
121 ret = register_guest_nmi_callback(reg->address);
122 break;
123
124 default:
125 ret = -ENOSYS;
126 break;
127 }
128
129 return ret;
130 }
131
unregister_guest_callback(struct callback_unregister * unreg)132 static long unregister_guest_callback(struct callback_unregister *unreg)
133 {
134 long ret;
135
136 switch ( unreg->type )
137 {
138 case CALLBACKTYPE_event:
139 case CALLBACKTYPE_failsafe:
140 case CALLBACKTYPE_syscall:
141 case CALLBACKTYPE_syscall32:
142 case CALLBACKTYPE_sysenter:
143 ret = -EINVAL;
144 break;
145
146 case CALLBACKTYPE_nmi:
147 unregister_guest_nmi_callback();
148 ret = 0;
149 break;
150
151 default:
152 ret = -ENOSYS;
153 break;
154 }
155
156 return ret;
157 }
158
do_callback_op(int cmd,XEN_GUEST_HANDLE_PARAM (const_void)arg)159 long do_callback_op(int cmd, XEN_GUEST_HANDLE_PARAM(const_void) arg)
160 {
161 long ret;
162
163 switch ( cmd )
164 {
165 case CALLBACKOP_register:
166 {
167 struct callback_register reg;
168
169 ret = -EFAULT;
170 if ( copy_from_guest(®, arg, 1) )
171 break;
172
173 ret = register_guest_callback(®);
174 }
175 break;
176
177 case CALLBACKOP_unregister:
178 {
179 struct callback_unregister unreg;
180
181 ret = -EFAULT;
182 if ( copy_from_guest(&unreg, arg, 1) )
183 break;
184
185 ret = unregister_guest_callback(&unreg);
186 }
187 break;
188
189 default:
190 ret = -ENOSYS;
191 break;
192 }
193
194 return ret;
195 }
196
do_set_callbacks(unsigned long event_address,unsigned long failsafe_address,unsigned long syscall_address)197 long do_set_callbacks(unsigned long event_address,
198 unsigned long failsafe_address,
199 unsigned long syscall_address)
200 {
201 struct callback_register event = {
202 .type = CALLBACKTYPE_event,
203 .address = event_address,
204 };
205 struct callback_register failsafe = {
206 .type = CALLBACKTYPE_failsafe,
207 .address = failsafe_address,
208 };
209 struct callback_register syscall = {
210 .type = CALLBACKTYPE_syscall,
211 .address = syscall_address,
212 };
213
214 register_guest_callback(&event);
215 register_guest_callback(&failsafe);
216 register_guest_callback(&syscall);
217
218 return 0;
219 }
220
compat_register_guest_callback(struct compat_callback_register * reg)221 static long compat_register_guest_callback(struct compat_callback_register *reg)
222 {
223 long ret = 0;
224 struct vcpu *curr = current;
225
226 fixup_guest_code_selector(curr->domain, reg->address.cs);
227
228 switch ( reg->type )
229 {
230 case CALLBACKTYPE_event:
231 curr->arch.pv_vcpu.event_callback_cs = reg->address.cs;
232 curr->arch.pv_vcpu.event_callback_eip = reg->address.eip;
233 break;
234
235 case CALLBACKTYPE_failsafe:
236 curr->arch.pv_vcpu.failsafe_callback_cs = reg->address.cs;
237 curr->arch.pv_vcpu.failsafe_callback_eip = reg->address.eip;
238 if ( reg->flags & CALLBACKF_mask_events )
239 set_bit(_VGCF_failsafe_disables_events,
240 &curr->arch.vgc_flags);
241 else
242 clear_bit(_VGCF_failsafe_disables_events,
243 &curr->arch.vgc_flags);
244 break;
245
246 case CALLBACKTYPE_syscall32:
247 curr->arch.pv_vcpu.syscall32_callback_cs = reg->address.cs;
248 curr->arch.pv_vcpu.syscall32_callback_eip = reg->address.eip;
249 curr->arch.pv_vcpu.syscall32_disables_events =
250 (reg->flags & CALLBACKF_mask_events) != 0;
251 break;
252
253 case CALLBACKTYPE_sysenter:
254 curr->arch.pv_vcpu.sysenter_callback_cs = reg->address.cs;
255 curr->arch.pv_vcpu.sysenter_callback_eip = reg->address.eip;
256 curr->arch.pv_vcpu.sysenter_disables_events =
257 (reg->flags & CALLBACKF_mask_events) != 0;
258 break;
259
260 case CALLBACKTYPE_nmi:
261 ret = register_guest_nmi_callback(reg->address.eip);
262 break;
263
264 default:
265 ret = -ENOSYS;
266 break;
267 }
268
269 return ret;
270 }
271
compat_unregister_guest_callback(struct compat_callback_unregister * unreg)272 static long compat_unregister_guest_callback(
273 struct compat_callback_unregister *unreg)
274 {
275 long ret;
276
277 switch ( unreg->type )
278 {
279 case CALLBACKTYPE_event:
280 case CALLBACKTYPE_failsafe:
281 case CALLBACKTYPE_syscall32:
282 case CALLBACKTYPE_sysenter:
283 ret = -EINVAL;
284 break;
285
286 case CALLBACKTYPE_nmi:
287 unregister_guest_nmi_callback();
288 ret = 0;
289 break;
290
291 default:
292 ret = -ENOSYS;
293 break;
294 }
295
296 return ret;
297 }
298
compat_callback_op(int cmd,XEN_GUEST_HANDLE (void)arg)299 long compat_callback_op(int cmd, XEN_GUEST_HANDLE(void) arg)
300 {
301 long ret;
302
303 switch ( cmd )
304 {
305 case CALLBACKOP_register:
306 {
307 struct compat_callback_register reg;
308
309 ret = -EFAULT;
310 if ( copy_from_guest(®, arg, 1) )
311 break;
312
313 ret = compat_register_guest_callback(®);
314 }
315 break;
316
317 case CALLBACKOP_unregister:
318 {
319 struct compat_callback_unregister unreg;
320
321 ret = -EFAULT;
322 if ( copy_from_guest(&unreg, arg, 1) )
323 break;
324
325 ret = compat_unregister_guest_callback(&unreg);
326 }
327 break;
328
329 default:
330 ret = -EINVAL;
331 break;
332 }
333
334 return ret;
335 }
336
compat_set_callbacks(unsigned long event_selector,unsigned long event_address,unsigned long failsafe_selector,unsigned long failsafe_address)337 long compat_set_callbacks(unsigned long event_selector,
338 unsigned long event_address,
339 unsigned long failsafe_selector,
340 unsigned long failsafe_address)
341 {
342 struct compat_callback_register event = {
343 .type = CALLBACKTYPE_event,
344 .address = {
345 .cs = event_selector,
346 .eip = event_address
347 }
348 };
349 struct compat_callback_register failsafe = {
350 .type = CALLBACKTYPE_failsafe,
351 .address = {
352 .cs = failsafe_selector,
353 .eip = failsafe_address
354 }
355 };
356
357 compat_register_guest_callback(&event);
358 compat_register_guest_callback(&failsafe);
359
360 return 0;
361 }
362
do_set_trap_table(XEN_GUEST_HANDLE_PARAM (const_trap_info_t)traps)363 long do_set_trap_table(XEN_GUEST_HANDLE_PARAM(const_trap_info_t) traps)
364 {
365 struct trap_info cur;
366 struct vcpu *curr = current;
367 struct trap_info *dst = curr->arch.pv_vcpu.trap_ctxt;
368 long rc = 0;
369
370 /* If no table is presented then clear the entire virtual IDT. */
371 if ( guest_handle_is_null(traps) )
372 {
373 memset(dst, 0, NR_VECTORS * sizeof(*dst));
374 init_int80_direct_trap(curr);
375 return 0;
376 }
377
378 for ( ; ; )
379 {
380 if ( copy_from_guest(&cur, traps, 1) )
381 {
382 rc = -EFAULT;
383 break;
384 }
385
386 if ( cur.address == 0 )
387 break;
388
389 if ( !is_canonical_address(cur.address) )
390 return -EINVAL;
391
392 fixup_guest_code_selector(curr->domain, cur.cs);
393
394 memcpy(&dst[cur.vector], &cur, sizeof(cur));
395
396 if ( cur.vector == 0x80 )
397 init_int80_direct_trap(curr);
398
399 guest_handle_add_offset(traps, 1);
400
401 if ( hypercall_preempt_check() )
402 {
403 rc = hypercall_create_continuation(
404 __HYPERVISOR_set_trap_table, "h", traps);
405 break;
406 }
407 }
408
409 return rc;
410 }
411
compat_set_trap_table(XEN_GUEST_HANDLE (trap_info_compat_t)traps)412 int compat_set_trap_table(XEN_GUEST_HANDLE(trap_info_compat_t) traps)
413 {
414 struct vcpu *curr = current;
415 struct compat_trap_info cur;
416 struct trap_info *dst = curr->arch.pv_vcpu.trap_ctxt;
417 long rc = 0;
418
419 /* If no table is presented then clear the entire virtual IDT. */
420 if ( guest_handle_is_null(traps) )
421 {
422 memset(dst, 0, NR_VECTORS * sizeof(*dst));
423 init_int80_direct_trap(curr);
424 return 0;
425 }
426
427 for ( ; ; )
428 {
429 if ( copy_from_guest(&cur, traps, 1) )
430 {
431 rc = -EFAULT;
432 break;
433 }
434
435 if ( cur.address == 0 )
436 break;
437
438 fixup_guest_code_selector(curr->domain, cur.cs);
439
440 XLAT_trap_info(dst + cur.vector, &cur);
441
442 if ( cur.vector == 0x80 )
443 init_int80_direct_trap(curr);
444
445 guest_handle_add_offset(traps, 1);
446
447 if ( hypercall_preempt_check() )
448 {
449 rc = hypercall_create_continuation(
450 __HYPERVISOR_set_trap_table, "h", traps);
451 break;
452 }
453 }
454
455 return rc;
456 }
457
do_nmi_op(unsigned int cmd,XEN_GUEST_HANDLE_PARAM (void)arg)458 long do_nmi_op(unsigned int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
459 {
460 struct xennmi_callback cb;
461 long rc = 0;
462
463 switch ( cmd )
464 {
465 case XENNMI_register_callback:
466 rc = -EFAULT;
467 if ( copy_from_guest(&cb, arg, 1) )
468 break;
469 rc = register_guest_nmi_callback(cb.handler_address);
470 break;
471 case XENNMI_unregister_callback:
472 unregister_guest_nmi_callback();
473 rc = 0;
474 break;
475 default:
476 rc = -ENOSYS;
477 break;
478 }
479
480 return rc;
481 }
482
compat_nmi_op(unsigned int cmd,XEN_GUEST_HANDLE_PARAM (void)arg)483 int compat_nmi_op(unsigned int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
484 {
485 struct compat_nmi_callback cb;
486 int rc = 0;
487
488 switch ( cmd )
489 {
490 case XENNMI_register_callback:
491 rc = -EFAULT;
492 if ( copy_from_guest(&cb, arg, 1) )
493 break;
494 rc = register_guest_nmi_callback(cb.handler_address);
495 break;
496 case XENNMI_unregister_callback:
497 unregister_guest_nmi_callback();
498 rc = 0;
499 break;
500 default:
501 rc = -ENOSYS;
502 break;
503 }
504
505 return rc;
506 }
507
508 /*
509 * Local variables:
510 * mode: C
511 * c-file-style: "BSD"
512 * c-basic-offset: 4
513 * tab-width: 4
514 * indent-tabs-mode: nil
515 * End:
516 */
517