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
xencomm_hypercall_console_io(int cmd,int count,char * str)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
xencomm_hypercall_event_channel_op(int cmd,void * op)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
xencomm_hypercall_xen_version(int cmd,void * arg)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
xencomm_hypercall_physdev_op(int cmd,void * op)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
xencommize_grant_table_op(struct xencomm_mini ** xc_area,unsigned int cmd,void * op,unsigned int count,struct xencomm_handle ** desc)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
xencomm_hypercall_grant_table_op(unsigned int cmd,void * op,unsigned int count)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
xencomm_hypercall_sched_op(int cmd,void * arg)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
xencomm_hypercall_multicall(void * call_list,int nr_calls)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
xencomm_hypercall_callback_op(int cmd,void * arg)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
xencommize_memory_reservation(struct xencomm_mini * xc_area,struct xen_memory_reservation * mop)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
xencomm_hypercall_memory_op(unsigned int cmd,void * arg)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
xencomm_hypercall_suspend(unsigned long srec)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
xencomm_hypercall_vcpu_op(int cmd,int cpu,void * arg)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
xencomm_hypercall_opt_feature(void * arg)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 }
442