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