Commit | Line | Data |
---|---|---|
f021c8b3 IY |
1 | /* |
2 | * This program is free software; you can redistribute it and/or modify | |
3 | * it under the terms of the GNU General Public License as published by | |
4 | * the Free Software Foundation; either version 2 of the License, or | |
5 | * (at your option) any later version. | |
6 | * | |
7 | * This program is distributed in the hope that it will be useful, | |
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | * GNU General Public License for more details. | |
11 | * | |
12 | * You should have received a copy of the GNU General Public License | |
13 | * along with this program; if not, write to the Free Software | |
14 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
15 | * | |
16 | * Tristan Gingold <tristan.gingold@bull.net> | |
17 | * | |
18 | * Copyright (c) 2007 | |
19 | * Isaku Yamahata <yamahata at valinux co jp> | |
20 | * VA Linux Systems Japan K.K. | |
21 | * consolidate mini and inline version. | |
22 | */ | |
23 | ||
24 | #include <linux/module.h> | |
25 | #include <xen/interface/xen.h> | |
26 | #include <xen/interface/memory.h> | |
27 | #include <xen/interface/grant_table.h> | |
28 | #include <xen/interface/callback.h> | |
29 | #include <xen/interface/vcpu.h> | |
30 | #include <asm/xen/hypervisor.h> | |
31 | #include <asm/xen/xencomm.h> | |
32 | ||
33 | /* Xencomm notes: | |
34 | * This file defines hypercalls to be used by xencomm. The hypercalls simply | |
35 | * create inlines or mini descriptors for pointers and then call the raw arch | |
36 | * hypercall xencomm_arch_hypercall_XXX | |
37 | * | |
38 | * If the arch wants to directly use these hypercalls, simply define macros | |
39 | * in asm/xen/hypercall.h, eg: | |
40 | * #define HYPERVISOR_sched_op xencomm_hypercall_sched_op | |
41 | * | |
42 | * The arch may also define HYPERVISOR_xxx as a function and do more operations | |
43 | * before/after doing the hypercall. | |
44 | * | |
45 | * Note: because only inline or mini descriptors are created these functions | |
46 | * must only be called with in kernel memory parameters. | |
47 | */ | |
48 | ||
49 | int | |
50 | xencomm_hypercall_console_io(int cmd, int count, char *str) | |
51 | { | |
52 | /* xen early printk uses console io hypercall before | |
53 | * xencomm initialization. In that case, we just ignore it. | |
54 | */ | |
55 | if (!xencomm_is_initialized()) | |
56 | return 0; | |
57 | ||
58 | return xencomm_arch_hypercall_console_io | |
59 | (cmd, count, xencomm_map_no_alloc(str, count)); | |
60 | } | |
61 | EXPORT_SYMBOL_GPL(xencomm_hypercall_console_io); | |
62 | ||
63 | int | |
64 | xencomm_hypercall_event_channel_op(int cmd, void *op) | |
65 | { | |
66 | struct xencomm_handle *desc; | |
67 | desc = xencomm_map_no_alloc(op, sizeof(struct evtchn_op)); | |
68 | if (desc == NULL) | |
69 | return -EINVAL; | |
70 | ||
71 | return xencomm_arch_hypercall_event_channel_op(cmd, desc); | |
72 | } | |
73 | EXPORT_SYMBOL_GPL(xencomm_hypercall_event_channel_op); | |
74 | ||
75 | int | |
76 | xencomm_hypercall_xen_version(int cmd, void *arg) | |
77 | { | |
78 | struct xencomm_handle *desc; | |
79 | unsigned int argsize; | |
80 | ||
81 | switch (cmd) { | |
82 | case XENVER_version: | |
83 | /* do not actually pass an argument */ | |
84 | return xencomm_arch_hypercall_xen_version(cmd, 0); | |
85 | case XENVER_extraversion: | |
86 | argsize = sizeof(struct xen_extraversion); | |
87 | break; | |
88 | case XENVER_compile_info: | |
89 | argsize = sizeof(struct xen_compile_info); | |
90 | break; | |
91 | case XENVER_capabilities: | |
92 | argsize = sizeof(struct xen_capabilities_info); | |
93 | break; | |
94 | case XENVER_changeset: | |
95 | argsize = sizeof(struct xen_changeset_info); | |
96 | break; | |
97 | case XENVER_platform_parameters: | |
98 | argsize = sizeof(struct xen_platform_parameters); | |
99 | break; | |
100 | case XENVER_get_features: | |
101 | argsize = (arg == NULL) ? 0 : sizeof(struct xen_feature_info); | |
102 | break; | |
103 | ||
104 | default: | |
105 | printk(KERN_DEBUG | |
106 | "%s: unknown version op %d\n", __func__, cmd); | |
107 | return -ENOSYS; | |
108 | } | |
109 | ||
110 | desc = xencomm_map_no_alloc(arg, argsize); | |
111 | if (desc == NULL) | |
112 | return -EINVAL; | |
113 | ||
114 | return xencomm_arch_hypercall_xen_version(cmd, desc); | |
115 | } | |
116 | EXPORT_SYMBOL_GPL(xencomm_hypercall_xen_version); | |
117 | ||
118 | int | |
119 | xencomm_hypercall_physdev_op(int cmd, void *op) | |
120 | { | |
121 | unsigned int argsize; | |
122 | ||
123 | switch (cmd) { | |
124 | case PHYSDEVOP_apic_read: | |
125 | case PHYSDEVOP_apic_write: | |
126 | argsize = sizeof(struct physdev_apic); | |
127 | break; | |
128 | case PHYSDEVOP_alloc_irq_vector: | |
129 | case PHYSDEVOP_free_irq_vector: | |
130 | argsize = sizeof(struct physdev_irq); | |
131 | break; | |
132 | case PHYSDEVOP_irq_status_query: | |
133 | argsize = sizeof(struct physdev_irq_status_query); | |
134 | break; | |
135 | ||
136 | default: | |
137 | printk(KERN_DEBUG | |
138 | "%s: unknown physdev op %d\n", __func__, cmd); | |
139 | return -ENOSYS; | |
140 | } | |
141 | ||
142 | return xencomm_arch_hypercall_physdev_op | |
143 | (cmd, xencomm_map_no_alloc(op, argsize)); | |
144 | } | |
145 | ||
146 | static int | |
147 | xencommize_grant_table_op(struct xencomm_mini **xc_area, | |
148 | unsigned int cmd, void *op, unsigned int count, | |
149 | struct xencomm_handle **desc) | |
150 | { | |
151 | struct xencomm_handle *desc1; | |
152 | unsigned int argsize; | |
153 | ||
154 | switch (cmd) { | |
155 | case GNTTABOP_map_grant_ref: | |
156 | argsize = sizeof(struct gnttab_map_grant_ref); | |
157 | break; | |
158 | case GNTTABOP_unmap_grant_ref: | |
159 | argsize = sizeof(struct gnttab_unmap_grant_ref); | |
160 | break; | |
161 | case GNTTABOP_setup_table: | |
162 | { | |
163 | struct gnttab_setup_table *setup = op; | |
164 | ||
165 | argsize = sizeof(*setup); | |
166 | ||
167 | if (count != 1) | |
168 | return -EINVAL; | |
169 | desc1 = __xencomm_map_no_alloc | |
170 | (xen_guest_handle(setup->frame_list), | |
171 | setup->nr_frames * | |
172 | sizeof(*xen_guest_handle(setup->frame_list)), | |
173 | *xc_area); | |
174 | if (desc1 == NULL) | |
175 | return -EINVAL; | |
176 | (*xc_area)++; | |
177 | set_xen_guest_handle(setup->frame_list, (void *)desc1); | |
178 | break; | |
179 | } | |
180 | case GNTTABOP_dump_table: | |
181 | argsize = sizeof(struct gnttab_dump_table); | |
182 | break; | |
183 | case GNTTABOP_transfer: | |
184 | argsize = sizeof(struct gnttab_transfer); | |
185 | break; | |
186 | case GNTTABOP_copy: | |
187 | argsize = sizeof(struct gnttab_copy); | |
188 | break; | |
189 | case GNTTABOP_query_size: | |
190 | argsize = sizeof(struct gnttab_query_size); | |
191 | break; | |
192 | default: | |
193 | printk(KERN_DEBUG "%s: unknown hypercall grant table op %d\n", | |
194 | __func__, cmd); | |
195 | BUG(); | |
196 | } | |
197 | ||
198 | *desc = __xencomm_map_no_alloc(op, count * argsize, *xc_area); | |
199 | if (*desc == NULL) | |
200 | return -EINVAL; | |
201 | (*xc_area)++; | |
202 | ||
203 | return 0; | |
204 | } | |
205 | ||
206 | int | |
207 | xencomm_hypercall_grant_table_op(unsigned int cmd, void *op, | |
208 | unsigned int count) | |
209 | { | |
210 | int rc; | |
211 | struct xencomm_handle *desc; | |
212 | XENCOMM_MINI_ALIGNED(xc_area, 2); | |
213 | ||
214 | rc = xencommize_grant_table_op(&xc_area, cmd, op, count, &desc); | |
215 | if (rc) | |
216 | return rc; | |
217 | ||
218 | return xencomm_arch_hypercall_grant_table_op(cmd, desc, count); | |
219 | } | |
220 | EXPORT_SYMBOL_GPL(xencomm_hypercall_grant_table_op); | |
221 | ||
222 | int | |
223 | xencomm_hypercall_sched_op(int cmd, void *arg) | |
224 | { | |
225 | struct xencomm_handle *desc; | |
226 | unsigned int argsize; | |
227 | ||
228 | switch (cmd) { | |
229 | case SCHEDOP_yield: | |
230 | case SCHEDOP_block: | |
231 | argsize = 0; | |
232 | break; | |
233 | case SCHEDOP_shutdown: | |
234 | argsize = sizeof(struct sched_shutdown); | |
235 | break; | |
236 | case SCHEDOP_poll: | |
237 | { | |
238 | struct sched_poll *poll = arg; | |
239 | struct xencomm_handle *ports; | |
240 | ||
241 | argsize = sizeof(struct sched_poll); | |
242 | ports = xencomm_map_no_alloc(xen_guest_handle(poll->ports), | |
243 | sizeof(*xen_guest_handle(poll->ports))); | |
244 | ||
245 | set_xen_guest_handle(poll->ports, (void *)ports); | |
246 | break; | |
247 | } | |
248 | default: | |
249 | printk(KERN_DEBUG "%s: unknown sched op %d\n", __func__, cmd); | |
250 | return -ENOSYS; | |
251 | } | |
252 | ||
253 | desc = xencomm_map_no_alloc(arg, argsize); | |
254 | if (desc == NULL) | |
255 | return -EINVAL; | |
256 | ||
257 | return xencomm_arch_hypercall_sched_op(cmd, desc); | |
258 | } | |
259 | EXPORT_SYMBOL_GPL(xencomm_hypercall_sched_op); | |
260 | ||
261 | int | |
262 | xencomm_hypercall_multicall(void *call_list, int nr_calls) | |
263 | { | |
264 | int rc; | |
265 | int i; | |
266 | struct multicall_entry *mce; | |
267 | struct xencomm_handle *desc; | |
268 | XENCOMM_MINI_ALIGNED(xc_area, nr_calls * 2); | |
269 | ||
270 | for (i = 0; i < nr_calls; i++) { | |
271 | mce = (struct multicall_entry *)call_list + i; | |
272 | ||
273 | switch (mce->op) { | |
274 | case __HYPERVISOR_update_va_mapping: | |
275 | case __HYPERVISOR_mmu_update: | |
276 | /* No-op on ia64. */ | |
277 | break; | |
278 | case __HYPERVISOR_grant_table_op: | |
279 | rc = xencommize_grant_table_op | |
280 | (&xc_area, | |
281 | mce->args[0], (void *)mce->args[1], | |
282 | mce->args[2], &desc); | |
283 | if (rc) | |
284 | return rc; | |
285 | mce->args[1] = (unsigned long)desc; | |
286 | break; | |
287 | case __HYPERVISOR_memory_op: | |
288 | default: | |
289 | printk(KERN_DEBUG | |
290 | "%s: unhandled multicall op entry op %lu\n", | |
291 | __func__, mce->op); | |
292 | return -ENOSYS; | |
293 | } | |
294 | } | |
295 | ||
296 | desc = xencomm_map_no_alloc(call_list, | |
297 | nr_calls * sizeof(struct multicall_entry)); | |
298 | if (desc == NULL) | |
299 | return -EINVAL; | |
300 | ||
301 | return xencomm_arch_hypercall_multicall(desc, nr_calls); | |
302 | } | |
303 | EXPORT_SYMBOL_GPL(xencomm_hypercall_multicall); | |
304 | ||
305 | int | |
306 | xencomm_hypercall_callback_op(int cmd, void *arg) | |
307 | { | |
308 | unsigned int argsize; | |
309 | switch (cmd) { | |
310 | case CALLBACKOP_register: | |
311 | argsize = sizeof(struct callback_register); | |
312 | break; | |
313 | case CALLBACKOP_unregister: | |
314 | argsize = sizeof(struct callback_unregister); | |
315 | break; | |
316 | default: | |
317 | printk(KERN_DEBUG | |
318 | "%s: unknown callback op %d\n", __func__, cmd); | |
319 | return -ENOSYS; | |
320 | } | |
321 | ||
322 | return xencomm_arch_hypercall_callback_op | |
323 | (cmd, xencomm_map_no_alloc(arg, argsize)); | |
324 | } | |
325 | ||
326 | static int | |
327 | xencommize_memory_reservation(struct xencomm_mini *xc_area, | |
328 | struct xen_memory_reservation *mop) | |
329 | { | |
330 | struct xencomm_handle *desc; | |
331 | ||
332 | desc = __xencomm_map_no_alloc(xen_guest_handle(mop->extent_start), | |
333 | mop->nr_extents * | |
334 | sizeof(*xen_guest_handle(mop->extent_start)), | |
335 | xc_area); | |
336 | if (desc == NULL) | |
337 | return -EINVAL; | |
338 | ||
339 | set_xen_guest_handle(mop->extent_start, (void *)desc); | |
340 | return 0; | |
341 | } | |
342 | ||
343 | int | |
344 | xencomm_hypercall_memory_op(unsigned int cmd, void *arg) | |
345 | { | |
346 | GUEST_HANDLE(xen_pfn_t) extent_start_va[2] = { {NULL}, {NULL} }; | |
347 | struct xen_memory_reservation *xmr = NULL; | |
348 | int rc; | |
349 | struct xencomm_handle *desc; | |
350 | unsigned int argsize; | |
351 | XENCOMM_MINI_ALIGNED(xc_area, 2); | |
352 | ||
353 | switch (cmd) { | |
354 | case XENMEM_increase_reservation: | |
355 | case XENMEM_decrease_reservation: | |
356 | case XENMEM_populate_physmap: | |
357 | xmr = (struct xen_memory_reservation *)arg; | |
358 | set_xen_guest_handle(extent_start_va[0], | |
359 | xen_guest_handle(xmr->extent_start)); | |
360 | ||
361 | argsize = sizeof(*xmr); | |
362 | rc = xencommize_memory_reservation(xc_area, xmr); | |
363 | if (rc) | |
364 | return rc; | |
365 | xc_area++; | |
366 | break; | |
367 | ||
368 | case XENMEM_maximum_ram_page: | |
369 | argsize = 0; | |
370 | break; | |
371 | ||
372 | case XENMEM_add_to_physmap: | |
373 | argsize = sizeof(struct xen_add_to_physmap); | |
374 | break; | |
375 | ||
376 | default: | |
377 | printk(KERN_DEBUG "%s: unknown memory op %d\n", __func__, cmd); | |
378 | return -ENOSYS; | |
379 | } | |
380 | ||
381 | desc = xencomm_map_no_alloc(arg, argsize); | |
382 | if (desc == NULL) | |
383 | return -EINVAL; | |
384 | ||
385 | rc = xencomm_arch_hypercall_memory_op(cmd, desc); | |
386 | ||
387 | switch (cmd) { | |
388 | case XENMEM_increase_reservation: | |
389 | case XENMEM_decrease_reservation: | |
390 | case XENMEM_populate_physmap: | |
391 | set_xen_guest_handle(xmr->extent_start, | |
392 | xen_guest_handle(extent_start_va[0])); | |
393 | break; | |
394 | } | |
395 | ||
396 | return rc; | |
397 | } | |
398 | EXPORT_SYMBOL_GPL(xencomm_hypercall_memory_op); | |
399 | ||
400 | int | |
401 | xencomm_hypercall_suspend(unsigned long srec) | |
402 | { | |
403 | struct sched_shutdown arg; | |
404 | ||
405 | arg.reason = SHUTDOWN_suspend; | |
406 | ||
407 | return xencomm_arch_hypercall_sched_op( | |
408 | SCHEDOP_shutdown, xencomm_map_no_alloc(&arg, sizeof(arg))); | |
409 | } | |
410 | ||
411 | long | |
412 | xencomm_hypercall_vcpu_op(int cmd, int cpu, void *arg) | |
413 | { | |
414 | unsigned int argsize; | |
415 | switch (cmd) { | |
416 | case VCPUOP_register_runstate_memory_area: { | |
417 | struct vcpu_register_runstate_memory_area *area = | |
418 | (struct vcpu_register_runstate_memory_area *)arg; | |
419 | argsize = sizeof(*arg); | |
420 | set_xen_guest_handle(area->addr.h, | |
421 | (void *)xencomm_map_no_alloc(area->addr.v, | |
422 | sizeof(area->addr.v))); | |
423 | break; | |
424 | } | |
425 | ||
426 | default: | |
427 | printk(KERN_DEBUG "%s: unknown vcpu op %d\n", __func__, cmd); | |
428 | return -ENOSYS; | |
429 | } | |
430 | ||
431 | return xencomm_arch_hypercall_vcpu_op(cmd, cpu, | |
432 | xencomm_map_no_alloc(arg, argsize)); | |
433 | } | |
434 | ||
435 | long | |
436 | xencomm_hypercall_opt_feature(void *arg) | |
437 | { | |
438 | return xencomm_arch_hypercall_opt_feature( | |
439 | xencomm_map_no_alloc(arg, | |
440 | sizeof(struct xen_ia64_opt_feature))); | |
441 | } |