1 /******************************************************************************
2  * multicall.c
3  */
4 
5 #include <xen/types.h>
6 #include <xen/lib.h>
7 #include <xen/mm.h>
8 #include <xen/sched.h>
9 #include <xen/event.h>
10 #include <xen/multicall.h>
11 #include <xen/guest_access.h>
12 #include <xen/hypercall.h>
13 #include <xen/perfc.h>
14 #include <xen/trace.h>
15 #include <asm/current.h>
16 #include <asm/hardirq.h>
17 
18 #ifndef COMPAT
19 typedef long ret_t;
20 #define xlat_multicall_entry(mcs)
21 
__trace_multicall_call(multicall_entry_t * call)22 static void __trace_multicall_call(multicall_entry_t *call)
23 {
24     __trace_hypercall(TRC_PV_HYPERCALL_SUBCALL, call->op, call->args);
25 }
26 #endif
27 
trace_multicall_call(multicall_entry_t * call)28 static void trace_multicall_call(multicall_entry_t *call)
29 {
30     if ( !tb_init_done )
31         return;
32 
33     __trace_multicall_call(call);
34 }
35 
do_multicall(XEN_GUEST_HANDLE_PARAM (multicall_entry_t)call_list,unsigned long nr_calls)36 ret_t do_multicall(
37     XEN_GUEST_HANDLE_PARAM(multicall_entry_t) call_list, unsigned long nr_calls)
38 {
39     struct vcpu *curr = current;
40     struct mc_state *mcs = &curr->mc_state;
41     unsigned long    i;
42     int              rc = 0;
43     enum mc_disposition disp = mc_continue;
44 
45     if ( unlikely(__test_and_set_bit(_MCSF_in_multicall, &mcs->flags)) )
46     {
47         gdprintk(XENLOG_INFO, "Multicall reentry is disallowed.\n");
48         return -EINVAL;
49     }
50 
51     if ( unlikely(!guest_handle_okay(call_list, nr_calls)) )
52         rc = -EFAULT;
53 
54     for ( i = 0; !rc && disp == mc_continue && i < nr_calls; i++ )
55     {
56         if ( i && hypercall_preempt_check() )
57             goto preempted;
58 
59         if ( unlikely(__copy_from_guest(&mcs->call, call_list, 1)) )
60         {
61             rc = -EFAULT;
62             break;
63         }
64 
65         trace_multicall_call(&mcs->call);
66 
67         disp = arch_do_multicall_call(mcs);
68 
69         /*
70          * In the unlikely event that a hypercall has left interrupts,
71          * spinlocks, or other things in a bad way, continuing the multicall
72          * will typically lead to far more subtle issues to debug.
73          */
74         ASSERT_NOT_IN_ATOMIC();
75 
76 #ifndef NDEBUG
77         {
78             /*
79              * Deliberately corrupt the contents of the multicall structure.
80              * The caller must depend only on the 'result' field on return.
81              */
82             struct multicall_entry corrupt;
83             memset(&corrupt, 0xAA, sizeof(corrupt));
84             (void)__copy_to_guest(call_list, &corrupt, 1);
85         }
86 #endif
87 
88         if ( unlikely(disp == mc_exit) )
89         {
90             if ( __copy_field_to_guest(call_list, &mcs->call, result) )
91                 /* nothing, best effort only */;
92             rc = mcs->call.result;
93         }
94         else if ( unlikely(__copy_field_to_guest(call_list, &mcs->call,
95                                                  result)) )
96             rc = -EFAULT;
97         else if ( curr->hcall_preempted )
98         {
99             /* Translate sub-call continuation to guest layout */
100             xlat_multicall_entry(mcs);
101 
102             /* Copy the sub-call continuation. */
103             if ( likely(!__copy_to_guest(call_list, &mcs->call, 1)) )
104                 goto preempted;
105             else
106                 hypercall_cancel_continuation(curr);
107             rc = -EFAULT;
108         }
109         else
110             guest_handle_add_offset(call_list, 1);
111     }
112 
113     if ( unlikely(disp == mc_preempt) && i < nr_calls )
114         goto preempted;
115 
116     perfc_add(calls_from_multicall, i);
117     mcs->flags = 0;
118     return rc;
119 
120  preempted:
121     perfc_add(calls_from_multicall, i);
122     mcs->flags = 0;
123     return hypercall_create_continuation(
124         __HYPERVISOR_multicall, "hi", call_list, nr_calls-i);
125 }
126 
127 /*
128  * Local variables:
129  * mode: C
130  * c-file-style: "BSD"
131  * c-basic-offset: 4
132  * tab-width: 4
133  * indent-tabs-mode: nil
134  * End:
135  */
136