1 /******************************************************************************
2  * arch/x86/hypercall.c
3  *
4  * Common x86 hypercall infrastructure.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; If not, see <http://www.gnu.org/licenses/>.
18  *
19  * Copyright (c) 2015,2016 Citrix Systems Ltd.
20  */
21 
22 #include <xen/hypercall.h>
23 
24 #define ARGS(x, n)                              \
25     [ __HYPERVISOR_ ## x ] = { n, n }
26 #define COMP(x, n, c)                           \
27     [ __HYPERVISOR_ ## x ] = { n, c }
28 
29 const hypercall_args_t hypercall_args_table[NR_hypercalls] =
30 {
31     ARGS(set_trap_table, 1),
32     ARGS(mmu_update, 4),
33     ARGS(set_gdt, 2),
34     ARGS(stack_switch, 2),
35     COMP(set_callbacks, 3, 4),
36     ARGS(fpu_taskswitch, 1),
37     ARGS(sched_op_compat, 2),
38     ARGS(platform_op, 1),
39     ARGS(set_debugreg, 2),
40     ARGS(get_debugreg, 1),
41     COMP(update_descriptor, 2, 4),
42     ARGS(memory_op, 2),
43     ARGS(multicall, 2),
44     COMP(update_va_mapping, 3, 4),
45     COMP(set_timer_op, 1, 2),
46     ARGS(event_channel_op_compat, 1),
47     ARGS(xen_version, 2),
48     ARGS(console_io, 3),
49     ARGS(physdev_op_compat, 1),
50     ARGS(grant_table_op, 3),
51     ARGS(vm_assist, 2),
52     COMP(update_va_mapping_otherdomain, 4, 5),
53     ARGS(vcpu_op, 3),
54     COMP(set_segment_base, 2, 0),
55     ARGS(mmuext_op, 4),
56     ARGS(xsm_op, 1),
57     ARGS(nmi_op, 2),
58     ARGS(sched_op, 2),
59     ARGS(callback_op, 2),
60     ARGS(xenoprof_op, 2),
61     ARGS(event_channel_op, 2),
62     ARGS(physdev_op, 2),
63     ARGS(hvm_op, 2),
64     ARGS(sysctl, 1),
65     ARGS(domctl, 1),
66     ARGS(kexec_op, 2),
67     ARGS(tmem_op, 1),
68     ARGS(xenpmu_op, 2),
69     ARGS(dm_op, 3),
70     ARGS(mca, 1),
71     ARGS(arch_1, 1),
72 };
73 
74 #undef COMP
75 #undef ARGS
76 
77 #define next_arg(fmt, args) ({                                              \
78     unsigned long __arg;                                                    \
79     switch ( *(fmt)++ )                                                     \
80     {                                                                       \
81     case 'i': __arg = (unsigned long)va_arg(args, unsigned int);  break;    \
82     case 'l': __arg = (unsigned long)va_arg(args, unsigned long); break;    \
83     case 'h': __arg = (unsigned long)va_arg(args, void *);        break;    \
84     default:  __arg = 0; BUG();                                             \
85     }                                                                       \
86     __arg;                                                                  \
87 })
88 
hypercall_create_continuation(unsigned int op,const char * format,...)89 unsigned long hypercall_create_continuation(
90     unsigned int op, const char *format, ...)
91 {
92     struct vcpu *curr = current;
93     struct mc_state *mcs = &curr->mc_state;
94     const char *p = format;
95     unsigned long arg;
96     unsigned int i;
97     va_list args;
98 
99     curr->hcall_preempted = true;
100 
101     va_start(args, format);
102 
103     if ( mcs->flags & MCSF_in_multicall )
104     {
105         for ( i = 0; *p != '\0'; i++ )
106             mcs->call.args[i] = next_arg(p, args);
107     }
108     else
109     {
110         struct cpu_user_regs *regs = guest_cpu_user_regs();
111 
112         regs->rax = op;
113 
114         if ( !curr->hcall_compat )
115         {
116             for ( i = 0; *p != '\0'; i++ )
117             {
118                 arg = next_arg(p, args);
119                 switch ( i )
120                 {
121                 case 0: regs->rdi = arg; break;
122                 case 1: regs->rsi = arg; break;
123                 case 2: regs->rdx = arg; break;
124                 case 3: regs->r10 = arg; break;
125                 case 4: regs->r8  = arg; break;
126                 case 5: regs->r9  = arg; break;
127                 }
128             }
129         }
130         else
131         {
132             for ( i = 0; *p != '\0'; i++ )
133             {
134                 arg = next_arg(p, args);
135                 switch ( i )
136                 {
137                 case 0: regs->rbx = arg; break;
138                 case 1: regs->rcx = arg; break;
139                 case 2: regs->rdx = arg; break;
140                 case 3: regs->rsi = arg; break;
141                 case 4: regs->rdi = arg; break;
142                 case 5: regs->rbp = arg; break;
143                 }
144             }
145         }
146     }
147 
148     va_end(args);
149 
150     return op;
151 }
152 
hypercall_xlat_continuation(unsigned int * id,unsigned int nr,unsigned int mask,...)153 int hypercall_xlat_continuation(unsigned int *id, unsigned int nr,
154                                 unsigned int mask, ...)
155 {
156     int rc = 0;
157     struct mc_state *mcs = &current->mc_state;
158     struct cpu_user_regs *regs;
159     unsigned int i, cval = 0;
160     unsigned long nval = 0;
161     va_list args;
162 
163     ASSERT(nr <= ARRAY_SIZE(mcs->call.args));
164     ASSERT(!(mask >> nr));
165     ASSERT(!id || *id < nr);
166     ASSERT(!id || !(mask & (1U << *id)));
167 
168     va_start(args, mask);
169 
170     if ( mcs->flags & MCSF_in_multicall )
171     {
172         if ( !current->hcall_preempted )
173         {
174             va_end(args);
175             return 0;
176         }
177 
178         for ( i = 0; i < nr; ++i, mask >>= 1 )
179         {
180             if ( mask & 1 )
181             {
182                 nval = va_arg(args, unsigned long);
183                 cval = va_arg(args, unsigned int);
184                 if ( cval == nval )
185                     mask &= ~1U;
186                 else
187                     BUG_ON(nval == (unsigned int)nval);
188             }
189             else if ( id && *id == i )
190             {
191                 *id = mcs->call.args[i];
192                 id = NULL;
193             }
194             if ( (mask & 1) && mcs->call.args[i] == nval )
195             {
196                 mcs->call.args[i] = cval;
197                 ++rc;
198             }
199             else
200                 BUG_ON(mcs->call.args[i] != (unsigned int)mcs->call.args[i]);
201         }
202     }
203     else
204     {
205         regs = guest_cpu_user_regs();
206         for ( i = 0; i < nr; ++i, mask >>= 1 )
207         {
208             unsigned long *reg;
209 
210             switch ( i )
211             {
212             case 0: reg = &regs->rbx; break;
213             case 1: reg = &regs->rcx; break;
214             case 2: reg = &regs->rdx; break;
215             case 3: reg = &regs->rsi; break;
216             case 4: reg = &regs->rdi; break;
217             case 5: reg = &regs->rbp; break;
218             default: BUG(); reg = NULL; break;
219             }
220             if ( (mask & 1) )
221             {
222                 nval = va_arg(args, unsigned long);
223                 cval = va_arg(args, unsigned int);
224                 if ( cval == nval )
225                     mask &= ~1U;
226                 else
227                     BUG_ON(nval == (unsigned int)nval);
228             }
229             else if ( id && *id == i )
230             {
231                 *id = *reg;
232                 id = NULL;
233             }
234             if ( (mask & 1) && *reg == nval )
235             {
236                 *reg = cval;
237                 ++rc;
238             }
239             else
240                 BUG_ON(*reg != (unsigned int)*reg);
241         }
242     }
243 
244     va_end(args);
245 
246     return rc;
247 }
248 
249 /*
250  * Local variables:
251  * mode: C
252  * c-file-style: "BSD"
253  * c-basic-offset: 4
254  * tab-width: 4
255  * indent-tabs-mode: nil
256  * End:
257  */
258 
259