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(&reg, arg, 1) )
171             break;
172 
173         ret = register_guest_callback(&reg);
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(&reg, arg, 1) )
311             break;
312 
313         ret = compat_register_guest_callback(&reg);
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