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