ia64/xen-unstable

view xen/arch/ia64/xen/hypercall.c @ 10685:8ad37880564d

[IA64] emulate PAL_HALT_LIGHT on domU

This patch emulates Guest PAL_HALT_LIGHT on domU by using do_block and timer.
It also adds the function of the timer interrupt to domU at the vcpu woke up.

Signed-off-by: Atsushi SAKAI <sakaia@jp.fujitsu.com>
[warning fixes and static inlining]
Signed-off-by: Alex Williamson <alex.williamson@hp.com>
author awilliam@xenbuild.aw
date Mon Jul 10 13:12:41 2006 -0600 (2006-07-10)
parents 8dc4af3f192c
children b2abc70be89e
line source
1 /*
2 * Hypercall implementations
3 *
4 * Copyright (C) 2005 Hewlett-Packard Co.
5 * Dan Magenheimer (dan.magenheimer@hp.com)
6 *
7 */
9 #include <xen/config.h>
10 #include <xen/sched.h>
11 #include <xen/hypercall.h>
12 #include <xen/multicall.h>
13 #include <xen/guest_access.h>
15 #include <linux/efi.h> /* FOR EFI_UNIMPLEMENTED */
16 #include <asm/sal.h> /* FOR struct ia64_sal_retval */
17 #include <asm/fpswa.h> /* FOR struct fpswa_ret_t */
19 #include <asm/vcpu.h>
20 #include <asm/dom_fw.h>
21 #include <public/dom0_ops.h>
22 #include <public/event_channel.h>
23 #include <public/memory.h>
24 #include <public/sched.h>
25 #include <xen/irq.h>
26 #include <asm/hw_irq.h>
27 #include <public/physdev.h>
28 #include <xen/domain.h>
29 #include <public/callback.h>
30 #include <xen/event.h>
31 #include <asm/privop_stat.h>
33 static long do_physdev_op_compat(XEN_GUEST_HANDLE(physdev_op_t) uop);
34 static long do_physdev_op(int cmd, XEN_GUEST_HANDLE(void) arg);
35 static long do_callback_op(int cmd, XEN_GUEST_HANDLE(void) arg);
37 hypercall_t ia64_hypercall_table[] =
38 {
39 (hypercall_t)do_ni_hypercall, /* do_set_trap_table */ /* 0 */
40 (hypercall_t)do_ni_hypercall, /* do_mmu_update */
41 (hypercall_t)do_ni_hypercall, /* do_set_gdt */
42 (hypercall_t)do_ni_hypercall, /* do_stack_switch */
43 (hypercall_t)do_ni_hypercall, /* do_set_callbacks */
44 (hypercall_t)do_ni_hypercall, /* do_fpu_taskswitch */ /* 5 */
45 (hypercall_t)do_sched_op_compat,
46 (hypercall_t)do_dom0_op,
47 (hypercall_t)do_ni_hypercall, /* do_set_debugreg */
48 (hypercall_t)do_ni_hypercall, /* do_get_debugreg */
49 (hypercall_t)do_ni_hypercall, /* do_update_descriptor */ /* 10 */
50 (hypercall_t)do_ni_hypercall, /* do_ni_hypercall */
51 (hypercall_t)do_memory_op,
52 (hypercall_t)do_multicall,
53 (hypercall_t)do_ni_hypercall, /* do_update_va_mapping */
54 (hypercall_t)do_ni_hypercall, /* do_set_timer_op */ /* 15 */
55 (hypercall_t)do_event_channel_op_compat,
56 (hypercall_t)do_xen_version,
57 (hypercall_t)do_console_io,
58 (hypercall_t)do_physdev_op_compat,
59 (hypercall_t)do_grant_table_op, /* 20 */
60 (hypercall_t)do_ni_hypercall, /* do_vm_assist */
61 (hypercall_t)do_ni_hypercall, /* do_update_va_mapping_otherdomain */
62 (hypercall_t)do_ni_hypercall, /* (x86 only) */
63 (hypercall_t)do_ni_hypercall, /* do_vcpu_op */
64 (hypercall_t)do_ni_hypercall, /* (x86_64 only) */ /* 25 */
65 (hypercall_t)do_ni_hypercall, /* do_mmuext_op */
66 (hypercall_t)do_ni_hypercall, /* do_acm_op */
67 (hypercall_t)do_ni_hypercall, /* do_nmi_op */
68 (hypercall_t)do_sched_op,
69 (hypercall_t)do_callback_op, /* */ /* 30 */
70 (hypercall_t)do_ni_hypercall, /* */
71 (hypercall_t)do_event_channel_op,
72 (hypercall_t)do_physdev_op,
73 (hypercall_t)do_ni_hypercall, /* */
74 (hypercall_t)do_ni_hypercall, /* */ /* 35 */
75 (hypercall_t)do_ni_hypercall, /* */
76 (hypercall_t)do_ni_hypercall, /* */
77 (hypercall_t)do_ni_hypercall, /* */
78 (hypercall_t)do_ni_hypercall, /* */
79 (hypercall_t)do_ni_hypercall, /* */ /* 40 */
80 (hypercall_t)do_ni_hypercall, /* */
81 (hypercall_t)do_ni_hypercall, /* */
82 (hypercall_t)do_ni_hypercall, /* */
83 (hypercall_t)do_ni_hypercall, /* */
84 (hypercall_t)do_ni_hypercall, /* */ /* 45 */
85 (hypercall_t)do_ni_hypercall, /* */
86 (hypercall_t)do_ni_hypercall, /* */
87 #ifdef CONFIG_XEN_IA64_DOM0_VP
88 (hypercall_t)do_dom0vp_op, /* dom0vp_op */
89 #else
90 (hypercall_t)do_ni_hypercall, /* arch_0 */
91 #endif
92 (hypercall_t)do_ni_hypercall, /* arch_1 */
93 (hypercall_t)do_ni_hypercall, /* arch_2 */ /* 50 */
94 (hypercall_t)do_ni_hypercall, /* arch_3 */
95 (hypercall_t)do_ni_hypercall, /* arch_4 */
96 (hypercall_t)do_ni_hypercall, /* arch_5 */
97 (hypercall_t)do_ni_hypercall, /* arch_6 */
98 (hypercall_t)do_ni_hypercall /* arch_7 */ /* 55 */
99 };
101 uint32_t nr_hypercalls =
102 sizeof(ia64_hypercall_table) / sizeof(hypercall_t);
104 static IA64FAULT
105 xen_hypercall (struct pt_regs *regs)
106 {
107 uint32_t cmd = (uint32_t)regs->r2;
109 if (cmd < nr_hypercalls)
110 regs->r8 = (*ia64_hypercall_table[cmd])(
111 regs->r14,
112 regs->r15,
113 regs->r16,
114 regs->r17,
115 regs->r18,
116 regs->r19);
117 else
118 regs->r8 = -ENOSYS;
120 return IA64_NO_FAULT;
121 }
124 static void
125 fw_hypercall_ipi (struct pt_regs *regs)
126 {
127 int cpu = regs->r14;
128 int vector = regs->r15;
129 struct vcpu *targ;
130 struct domain *d = current->domain;
132 /* Be sure the target exists. */
133 if (cpu > MAX_VIRT_CPUS)
134 return;
135 targ = d->vcpu[cpu];
136 if (targ == NULL)
137 return;
139 if (vector == XEN_SAL_BOOT_RENDEZ_VEC
140 && (!test_bit(_VCPUF_initialised, &targ->vcpu_flags)
141 || test_bit(_VCPUF_down, &targ->vcpu_flags))) {
143 /* First start: initialize vpcu. */
144 if (!test_bit(_VCPUF_initialised, &targ->vcpu_flags)) {
145 struct vcpu_guest_context c;
147 memset (&c, 0, sizeof (c));
149 if (arch_set_info_guest (targ, &c) != 0) {
150 printf ("arch_boot_vcpu: failure\n");
151 return;
152 }
153 }
155 /* First or next rendez-vous: set registers. */
156 vcpu_init_regs (targ);
157 vcpu_regs (targ)->cr_iip = d->arch.sal_data->boot_rdv_ip;
158 vcpu_regs (targ)->r1 = d->arch.sal_data->boot_rdv_r1;
159 vcpu_regs (targ)->b0 = d->arch.sal_return_addr;
161 if (test_and_clear_bit(_VCPUF_down,
162 &targ->vcpu_flags)) {
163 vcpu_wake(targ);
164 printf ("arch_boot_vcpu: vcpu %d awaken\n",
165 targ->vcpu_id);
166 }
167 else
168 printf ("arch_boot_vcpu: huu, already awaken!\n");
169 }
170 else {
171 int running = test_bit(_VCPUF_running,
172 &targ->vcpu_flags);
174 vcpu_pend_interrupt(targ, vector);
175 vcpu_unblock(targ);
176 if (running)
177 smp_send_event_check_cpu(targ->processor);
178 }
179 return;
180 }
182 static fpswa_ret_t
183 fw_hypercall_fpswa (struct vcpu *v)
184 {
185 return PSCBX(v, fpswa_ret);
186 }
188 static IA64FAULT
189 fw_hypercall (struct pt_regs *regs)
190 {
191 struct vcpu *v = current;
192 struct sal_ret_values x;
193 efi_status_t efi_ret_value;
194 fpswa_ret_t fpswa_ret;
195 IA64FAULT fault;
196 unsigned long index = regs->r2 & FW_HYPERCALL_NUM_MASK_HIGH;
198 switch (index) {
199 case FW_HYPERCALL_PAL_CALL:
200 //printf("*** PAL hypercall: index=%d\n",regs->r28);
201 //FIXME: This should call a C routine
202 #if 0
203 // This is very conservative, but avoids a possible
204 // (and deadly) freeze in paravirtualized domains due
205 // to a yet-to-be-found bug where pending_interruption
206 // is zero when it shouldn't be. Since PAL is called
207 // in the idle loop, this should resolve it
208 VCPU(v,pending_interruption) = 1;
209 #endif
210 if (regs->r28 == PAL_HALT_LIGHT) {
211 if (vcpu_deliverable_interrupts(v) ||
212 event_pending(v)) {
213 idle_when_pending++;
214 vcpu_pend_unspecified_interrupt(v);
215 //printf("idle w/int#%d pending!\n",pi);
216 //this shouldn't happen, but it apparently does quite a bit! so don't
217 //allow it to happen... i.e. if a domain has an interrupt pending and
218 //it tries to halt itself because it thinks it is idle, just return here
219 //as deliver_pending_interrupt is called on the way out and will deliver it
220 }
221 else {
222 pal_halt_light_count++;
223 set_timer(&v->arch.hlt_timer,
224 vcpu_get_next_timer_ns(v));
225 do_sched_op_compat(SCHEDOP_block, 0);
226 }
227 regs->r8 = 0;
228 regs->r9 = 0;
229 regs->r10 = 0;
230 regs->r11 = 0;
231 }
232 else {
233 struct ia64_pal_retval y;
235 if (regs->r28 >= PAL_COPY_PAL)
236 y = xen_pal_emulator
237 (regs->r28, vcpu_get_gr (v, 33),
238 vcpu_get_gr (v, 34),
239 vcpu_get_gr (v, 35));
240 else
241 y = xen_pal_emulator(regs->r28,regs->r29,
242 regs->r30,regs->r31);
243 regs->r8 = y.status; regs->r9 = y.v0;
244 regs->r10 = y.v1; regs->r11 = y.v2;
245 }
246 break;
247 case FW_HYPERCALL_SAL_CALL:
248 x = sal_emulator(vcpu_get_gr(v,32),vcpu_get_gr(v,33),
249 vcpu_get_gr(v,34),vcpu_get_gr(v,35),
250 vcpu_get_gr(v,36),vcpu_get_gr(v,37),
251 vcpu_get_gr(v,38),vcpu_get_gr(v,39));
252 regs->r8 = x.r8; regs->r9 = x.r9;
253 regs->r10 = x.r10; regs->r11 = x.r11;
254 break;
255 case FW_HYPERCALL_SAL_RETURN:
256 if ( !test_and_set_bit(_VCPUF_down, &v->vcpu_flags) )
257 vcpu_sleep_nosync(v);
258 break;
259 case FW_HYPERCALL_EFI_CALL:
260 efi_ret_value = efi_emulator (regs, &fault);
261 if (fault != IA64_NO_FAULT) return fault;
262 regs->r8 = efi_ret_value;
263 break;
264 case FW_HYPERCALL_IPI:
265 fw_hypercall_ipi (regs);
266 break;
267 case FW_HYPERCALL_SET_SHARED_INFO_VA:
268 regs->r8 = domain_set_shared_info_va (regs->r28);
269 break;
270 case FW_HYPERCALL_FPSWA:
271 fpswa_ret = fw_hypercall_fpswa (v);
272 regs->r8 = fpswa_ret.status;
273 regs->r9 = fpswa_ret.err0;
274 regs->r10 = fpswa_ret.err1;
275 regs->r11 = fpswa_ret.err2;
276 break;
277 default:
278 printf("unknown ia64 fw hypercall %lx\n", regs->r2);
279 regs->r8 = do_ni_hypercall();
280 }
281 return IA64_NO_FAULT;
282 }
284 /* opt_unsafe_hypercall: If true, unsafe debugging hypercalls are allowed.
285 These can create security hole. */
286 static int opt_unsafe_hypercall = 0;
287 boolean_param("unsafe_hypercall", opt_unsafe_hypercall);
289 IA64FAULT
290 ia64_hypercall (struct pt_regs *regs)
291 {
292 struct vcpu *v = current;
293 unsigned long index = regs->r2;
294 int privlvl = (regs->cr_ipsr & IA64_PSR_CPL) >> IA64_PSR_CPL0_BIT;
296 if (index >= FW_HYPERCALL_FIRST_USER) {
297 /* Note: user hypercalls are not safe, since Xen doesn't
298 check memory access privilege: Xen does not deny reading
299 or writing to kernel memory. */
300 if (!opt_unsafe_hypercall) {
301 printf("user xen/ia64 hypercalls disabled\n");
302 regs->r8 = -1;
303 }
304 else switch (index) {
305 case 0xffff:
306 regs->r8 = dump_privop_counts_to_user(
307 (char *) vcpu_get_gr(v,32),
308 (int) vcpu_get_gr(v,33));
309 break;
310 case 0xfffe:
311 regs->r8 = zero_privop_counts_to_user(
312 (char *) vcpu_get_gr(v,32),
313 (int) vcpu_get_gr(v,33));
314 break;
315 default:
316 printf("unknown user xen/ia64 hypercall %lx\n", index);
317 regs->r8 = do_ni_hypercall();
318 }
319 return IA64_NO_FAULT;
320 }
322 /* Hypercalls are only allowed by kernel.
323 Kernel checks memory accesses. */
324 if (privlvl != 2) {
325 /* FIXME: Return a better error value ?
326 Reflection ? Illegal operation ? */
327 regs->r8 = -1;
328 return IA64_NO_FAULT;
329 }
331 if (index >= FW_HYPERCALL_FIRST_ARCH)
332 return fw_hypercall (regs);
333 else
334 return xen_hypercall (regs);
335 }
337 unsigned long hypercall_create_continuation(
338 unsigned int op, const char *format, ...)
339 {
340 struct mc_state *mcs = &mc_state[smp_processor_id()];
341 struct vcpu *v = current;
342 const char *p = format;
343 unsigned long arg;
344 unsigned int i;
345 va_list args;
347 va_start(args, format);
348 if ( test_bit(_MCSF_in_multicall, &mcs->flags) ) {
349 panic("PREEMPT happen in multicall\n"); // Not support yet
350 } else {
351 vcpu_set_gr(v, 2, op, 0);
352 for ( i = 0; *p != '\0'; i++) {
353 switch ( *p++ )
354 {
355 case 'i':
356 arg = (unsigned long)va_arg(args, unsigned int);
357 break;
358 case 'l':
359 arg = (unsigned long)va_arg(args, unsigned long);
360 break;
361 case 'h':
362 arg = (unsigned long)va_arg(args, void *);
363 break;
364 default:
365 arg = 0;
366 BUG();
367 }
368 switch (i) {
369 case 0: vcpu_set_gr(v, 14, arg, 0);
370 break;
371 case 1: vcpu_set_gr(v, 15, arg, 0);
372 break;
373 case 2: vcpu_set_gr(v, 16, arg, 0);
374 break;
375 case 3: vcpu_set_gr(v, 17, arg, 0);
376 break;
377 case 4: vcpu_set_gr(v, 18, arg, 0);
378 break;
379 default: panic("Too many args for hypercall continuation\n");
380 break;
381 }
382 }
383 }
384 v->arch.hypercall_continuation = 1;
385 va_end(args);
386 return op;
387 }
389 /* Need make this function common */
390 extern int
391 iosapic_guest_read(
392 unsigned long physbase, unsigned int reg, u32 *pval);
393 extern int
394 iosapic_guest_write(
395 unsigned long physbase, unsigned int reg, u32 pval);
397 static long do_physdev_op(int cmd, XEN_GUEST_HANDLE(void) arg)
398 {
399 int irq;
400 long ret;
402 switch ( cmd )
403 {
404 case PHYSDEVOP_eoi: {
405 struct physdev_eoi eoi;
406 ret = -EFAULT;
407 if ( copy_from_guest(&eoi, arg, 1) != 0 )
408 break;
409 ret = pirq_guest_eoi(current->domain, eoi.irq);
410 break;
411 }
413 /* Legacy since 0x00030202. */
414 case PHYSDEVOP_IRQ_UNMASK_NOTIFY: {
415 ret = pirq_guest_unmask(current->domain);
416 break;
417 }
419 case PHYSDEVOP_irq_status_query: {
420 struct physdev_irq_status_query irq_status_query;
421 ret = -EFAULT;
422 if ( copy_from_guest(&irq_status_query, arg, 1) != 0 )
423 break;
424 irq = irq_status_query.irq;
425 ret = -EINVAL;
426 if ( (irq < 0) || (irq >= NR_IRQS) )
427 break;
428 irq_status_query.flags = 0;
429 /* Edge-triggered interrupts don't need an explicit unmask downcall. */
430 if ( !strstr(irq_desc[irq_to_vector(irq)].handler->typename, "edge") )
431 irq_status_query.flags |= XENIRQSTAT_needs_eoi;
432 ret = copy_to_guest(arg, &irq_status_query, 1) ? -EFAULT : 0;
433 break;
434 }
436 case PHYSDEVOP_apic_read: {
437 struct physdev_apic apic;
438 ret = -EFAULT;
439 if ( copy_from_guest(&apic, arg, 1) != 0 )
440 break;
441 ret = -EPERM;
442 if ( !IS_PRIV(current->domain) )
443 break;
444 ret = iosapic_guest_read(apic.apic_physbase, apic.reg, &apic.value);
445 if ( copy_to_guest(arg, &apic, 1) != 0 )
446 ret = -EFAULT;
447 break;
448 }
450 case PHYSDEVOP_apic_write: {
451 struct physdev_apic apic;
452 ret = -EFAULT;
453 if ( copy_from_guest(&apic, arg, 1) != 0 )
454 break;
455 ret = -EPERM;
456 if ( !IS_PRIV(current->domain) )
457 break;
458 ret = iosapic_guest_write(apic.apic_physbase, apic.reg, apic.value);
459 break;
460 }
462 case PHYSDEVOP_alloc_irq_vector: {
463 struct physdev_irq irq_op;
465 ret = -EFAULT;
466 if ( copy_from_guest(&irq_op, arg, 1) != 0 )
467 break;
469 ret = -EPERM;
470 if ( !IS_PRIV(current->domain) )
471 break;
473 ret = -EINVAL;
474 if ( (irq = irq_op.irq) >= NR_IRQS )
475 break;
477 irq_op.vector = assign_irq_vector(irq);
478 ret = copy_to_guest(arg, &irq_op, 1) ? -EFAULT : 0;
479 break;
480 }
482 default:
483 ret = -EINVAL;
484 break;
485 }
487 return ret;
488 }
490 /* Legacy hypercall (as of 0x00030202). */
491 static long do_physdev_op_compat(XEN_GUEST_HANDLE(physdev_op_t) uop)
492 {
493 struct physdev_op op;
495 if ( unlikely(copy_from_guest(&op, uop, 1) != 0) )
496 return -EFAULT;
498 return do_physdev_op(op.cmd, guest_handle_from_ptr(&uop.p->u, void));
499 }
501 /* Legacy hypercall (as of 0x00030202). */
502 long do_event_channel_op_compat(XEN_GUEST_HANDLE(evtchn_op_t) uop)
503 {
504 struct evtchn_op op;
506 if ( unlikely(copy_from_guest(&op, uop, 1) != 0) )
507 return -EFAULT;
509 return do_event_channel_op(op.cmd, guest_handle_from_ptr(&uop.p->u, void));
510 }
512 static long register_guest_callback(struct callback_register *reg)
513 {
514 long ret = 0;
515 struct vcpu *v = current;
517 if (IS_VMM_ADDRESS(reg->address))
518 return -EINVAL;
520 switch ( reg->type )
521 {
522 case CALLBACKTYPE_event:
523 v->arch.event_callback_ip = reg->address;
524 break;
526 case CALLBACKTYPE_failsafe:
527 v->arch.failsafe_callback_ip = reg->address;
528 break;
530 default:
531 ret = -EINVAL;
532 break;
533 }
535 return ret;
536 }
538 static long unregister_guest_callback(struct callback_unregister *unreg)
539 {
540 return -EINVAL ;
541 }
543 /* First time to add callback to xen/ia64, so let's just stick to
544 * the newer callback interface.
545 */
546 static long do_callback_op(int cmd, XEN_GUEST_HANDLE(void) arg)
547 {
548 long ret;
550 switch ( cmd )
551 {
552 case CALLBACKOP_register:
553 {
554 struct callback_register reg;
556 ret = -EFAULT;
557 if ( copy_from_guest(&reg, arg, 1) )
558 break;
560 ret = register_guest_callback(&reg);
561 }
562 break;
564 case CALLBACKOP_unregister:
565 {
566 struct callback_unregister unreg;
568 ret = -EFAULT;
569 if ( copy_from_guest(&unreg, arg, 1) )
570 break;
572 ret = unregister_guest_callback(&unreg);
573 }
574 break;
576 default:
577 ret = -EINVAL;
578 break;
579 }
581 return ret;
582 }