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/perfc.h>
13 #include <xen/trace.h>
14 #include <asm/current.h>
15 #include <asm/hardirq.h>
16 
17 #ifndef COMPAT
18 typedef long ret_t;
19 #define xlat_multicall_entry(mcs)
20 
__trace_multicall_call(multicall_entry_t * call)21 static void __trace_multicall_call(multicall_entry_t *call)
22 {
23     __trace_hypercall(TRC_PV_HYPERCALL_SUBCALL, call->op, call->args);
24 }
25 #endif
26 
trace_multicall_call(multicall_entry_t * call)27 static void trace_multicall_call(multicall_entry_t *call)
28 {
29     if ( !tb_init_done )
30         return;
31 
32     __trace_multicall_call(call);
33 }
34 
35 ret_t
do_multicall(XEN_GUEST_HANDLE_PARAM (multicall_entry_t)call_list,uint32_t nr_calls)36 do_multicall(
37     XEN_GUEST_HANDLE_PARAM(multicall_entry_t) call_list, uint32_t nr_calls)
38 {
39     struct vcpu *curr = current;
40     struct mc_state *mcs = &curr->mc_state;
41     uint32_t         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_incr(calls_to_multicall);
117     perfc_add(calls_from_multicall, i);
118     mcs->flags = 0;
119     return rc;
120 
121  preempted:
122     perfc_add(calls_from_multicall, i);
123     mcs->flags = 0;
124     return hypercall_create_continuation(
125         __HYPERVISOR_multicall, "hi", call_list, nr_calls-i);
126 }
127 
128 /*
129  * Local variables:
130  * mode: C
131  * c-file-style: "BSD"
132  * c-basic-offset: 4
133  * tab-width: 4
134  * indent-tabs-mode: nil
135  * End:
136  */
137