ia64/xen-unstable

view xen/arch/ia64/xen/hypercall.c @ 9873:4e1b8be54311

Rename *GUEST_HANDLE to *XEN_GUEST_HANDLE.
Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
author kaf24@firebug.cl.cam.ac.uk
date Thu Apr 27 13:38:21 2006 +0100 (2006-04-27)
parents 63af1c14fa18
children 42a8e3101c6c
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 */
18 #include <asm/vcpu.h>
19 #include <asm/dom_fw.h>
20 #include <public/dom0_ops.h>
21 #include <public/event_channel.h>
22 #include <public/memory.h>
23 #include <public/sched.h>
24 #include <xen/irq.h>
25 #include <asm/hw_irq.h>
26 #include <public/physdev.h>
27 #include <xen/domain.h>
29 extern unsigned long translate_domain_mpaddr(unsigned long);
30 static long do_physdev_op(XEN_GUEST_HANDLE(physdev_op_t) uop);
31 /* FIXME: where these declarations should be there ? */
32 extern int dump_privop_counts_to_user(char *, int);
33 extern int zero_privop_counts_to_user(char *, int);
35 unsigned long idle_when_pending = 0;
36 unsigned long pal_halt_light_count = 0;
38 hypercall_t ia64_hypercall_table[] =
39 {
40 (hypercall_t)do_ni_hypercall, /* do_set_trap_table */ /* 0 */
41 (hypercall_t)do_ni_hypercall, /* do_mmu_update */
42 (hypercall_t)do_ni_hypercall, /* do_set_gdt */
43 (hypercall_t)do_ni_hypercall, /* do_stack_switch */
44 (hypercall_t)do_ni_hypercall, /* do_set_callbacks */
45 (hypercall_t)do_ni_hypercall, /* do_fpu_taskswitch */ /* 5 */
46 (hypercall_t)do_sched_op_compat,
47 (hypercall_t)do_dom0_op,
48 (hypercall_t)do_ni_hypercall, /* do_set_debugreg */
49 (hypercall_t)do_ni_hypercall, /* do_get_debugreg */
50 (hypercall_t)do_ni_hypercall, /* do_update_descriptor */ /* 10 */
51 (hypercall_t)do_ni_hypercall, /* do_ni_hypercall */
52 (hypercall_t)do_memory_op,
53 (hypercall_t)do_multicall,
54 (hypercall_t)do_ni_hypercall, /* do_update_va_mapping */
55 (hypercall_t)do_ni_hypercall, /* do_set_timer_op */ /* 15 */
56 (hypercall_t)do_event_channel_op,
57 (hypercall_t)do_xen_version,
58 (hypercall_t)do_console_io,
59 (hypercall_t)do_physdev_op,
60 (hypercall_t)do_grant_table_op, /* 20 */
61 (hypercall_t)do_ni_hypercall, /* do_vm_assist */
62 (hypercall_t)do_ni_hypercall, /* do_update_va_mapping_otherdomain */
63 (hypercall_t)do_ni_hypercall, /* (x86 only) */
64 (hypercall_t)do_ni_hypercall, /* do_vcpu_op */
65 (hypercall_t)do_ni_hypercall, /* (x86_64 only) */ /* 25 */
66 (hypercall_t)do_ni_hypercall, /* do_mmuext_op */
67 (hypercall_t)do_ni_hypercall, /* do_acm_op */
68 (hypercall_t)do_ni_hypercall, /* do_nmi_op */
69 (hypercall_t)do_sched_op,
70 (hypercall_t)do_ni_hypercall, /* */ /* 30 */
71 (hypercall_t)do_ni_hypercall /* */
72 };
74 uint32_t nr_hypercalls =
75 sizeof(ia64_hypercall_table) / sizeof(hypercall_t);
77 static int
78 xen_hypercall (struct pt_regs *regs)
79 {
80 uint32_t cmd = (uint32_t)regs->r2;
82 if (cmd < nr_hypercalls)
83 regs->r8 = (*ia64_hypercall_table[cmd])(
84 regs->r14,
85 regs->r15,
86 regs->r16,
87 regs->r17,
88 regs->r18,
89 regs->r19);
90 else
91 #ifdef CONFIG_XEN_IA64_DOM0_VP
92 if (cmd == __HYPERVISOR_ia64_dom0vp_op)
93 regs->r8 = do_dom0vp_op(regs->r14, regs->r15, regs->r16,
94 regs->r17, regs->r18);
95 else
96 #endif
97 regs->r8 = -ENOSYS;
99 return 1;
100 }
103 static void
104 fw_hypercall_ipi (struct pt_regs *regs)
105 {
106 int cpu = regs->r14;
107 int vector = regs->r15;
108 struct vcpu *targ;
110 if (0 && vector == 254)
111 printf ("send_ipi from %d to %d vector=%d\n",
112 current->vcpu_id, cpu, vector);
114 if (cpu > MAX_VIRT_CPUS)
115 return;
117 targ = current->domain->vcpu[cpu];
118 if (targ == NULL)
119 return;
121 if (vector == XEN_SAL_BOOT_RENDEZ_VEC
122 && !test_bit(_VCPUF_initialised, &targ->vcpu_flags)) {
123 struct pt_regs *targ_regs = vcpu_regs (targ);
124 struct vcpu_guest_context c;
126 printf ("arch_boot_vcpu: %p %p\n",
127 (void *)targ_regs->cr_iip,
128 (void *)targ_regs->r1);
129 memset (&c, 0, sizeof (c));
130 /* Copy regs. */
131 c.regs.cr_iip = targ_regs->cr_iip;
132 c.regs.r1 = targ_regs->r1;
134 /* Copy from vcpu 0. */
135 c.vcpu.evtchn_vector =
136 current->domain->vcpu[0]->vcpu_info->arch.evtchn_vector;
137 if (arch_set_info_guest (targ, &c) != 0) {
138 printf ("arch_boot_vcpu: failure\n");
139 return;
140 }
141 if (test_and_clear_bit(_VCPUF_down,
142 &targ->vcpu_flags)) {
143 vcpu_wake(targ);
144 printf ("arch_boot_vcpu: vcpu %d awaken %016lx!\n",
145 targ->vcpu_id, targ_regs->cr_iip);
146 }
147 else
148 printf ("arch_boot_vcpu: huu, already awaken!");
149 }
150 else {
151 int running = test_bit(_VCPUF_running,
152 &targ->vcpu_flags);
154 vcpu_pend_interrupt(targ, vector);
155 vcpu_unblock(targ);
156 if (running)
157 smp_send_event_check_cpu(targ->processor);
158 }
159 return;
160 }
162 static int
163 fw_hypercall (struct pt_regs *regs)
164 {
165 struct vcpu *v = current;
166 struct sal_ret_values x;
167 unsigned long *tv, *tc;
169 switch (regs->r2) {
170 case FW_HYPERCALL_PAL_CALL:
171 //printf("*** PAL hypercall: index=%d\n",regs->r28);
172 //FIXME: This should call a C routine
173 #if 0
174 // This is very conservative, but avoids a possible
175 // (and deadly) freeze in paravirtualized domains due
176 // to a yet-to-be-found bug where pending_interruption
177 // is zero when it shouldn't be. Since PAL is called
178 // in the idle loop, this should resolve it
179 VCPU(v,pending_interruption) = 1;
180 #endif
181 if (regs->r28 == PAL_HALT_LIGHT) {
182 int pi;
183 #define SPURIOUS_VECTOR 15
184 pi = vcpu_check_pending_interrupts(v);
185 if (pi != SPURIOUS_VECTOR) {
186 if (!VCPU(v,pending_interruption))
187 idle_when_pending++;
188 vcpu_pend_unspecified_interrupt(v);
189 //printf("idle w/int#%d pending!\n",pi);
190 //this shouldn't happen, but it apparently does quite a bit! so don't
191 //allow it to happen... i.e. if a domain has an interrupt pending and
192 //it tries to halt itself because it thinks it is idle, just return here
193 //as deliver_pending_interrupt is called on the way out and will deliver it
194 }
195 else {
196 pal_halt_light_count++;
197 do_sched_op_compat(SCHEDOP_yield, 0);
198 }
199 regs->r8 = 0;
200 regs->r9 = 0;
201 regs->r10 = 0;
202 regs->r11 = 0;
203 }
204 else {
205 struct ia64_pal_retval y;
207 if (regs->r28 >= PAL_COPY_PAL)
208 y = xen_pal_emulator
209 (regs->r28, vcpu_get_gr (v, 33),
210 vcpu_get_gr (v, 34),
211 vcpu_get_gr (v, 35));
212 else
213 y = xen_pal_emulator(regs->r28,regs->r29,
214 regs->r30,regs->r31);
215 regs->r8 = y.status; regs->r9 = y.v0;
216 regs->r10 = y.v1; regs->r11 = y.v2;
217 }
218 break;
219 case FW_HYPERCALL_SAL_CALL:
220 x = sal_emulator(vcpu_get_gr(v,32),vcpu_get_gr(v,33),
221 vcpu_get_gr(v,34),vcpu_get_gr(v,35),
222 vcpu_get_gr(v,36),vcpu_get_gr(v,37),
223 vcpu_get_gr(v,38),vcpu_get_gr(v,39));
224 regs->r8 = x.r8; regs->r9 = x.r9;
225 regs->r10 = x.r10; regs->r11 = x.r11;
226 break;
227 case FW_HYPERCALL_EFI_RESET_SYSTEM:
228 printf("efi.reset_system called ");
229 if (current->domain == dom0) {
230 printf("(by dom0)\n ");
231 (*efi.reset_system)(EFI_RESET_WARM,0,0,NULL);
232 }
233 else
234 domain_shutdown (current->domain, SHUTDOWN_reboot);
235 regs->r8 = EFI_UNSUPPORTED;
236 break;
237 case FW_HYPERCALL_EFI_GET_TIME:
238 tv = (unsigned long *) vcpu_get_gr(v,32);
239 tc = (unsigned long *) vcpu_get_gr(v,33);
240 //printf("efi_get_time(%p,%p) called...",tv,tc);
241 tv = (unsigned long *) __va(translate_domain_mpaddr((unsigned long) tv));
242 if (tc) tc = (unsigned long *) __va(translate_domain_mpaddr((unsigned long) tc));
243 regs->r8 = (*efi.get_time)((efi_time_t *) tv, (efi_time_cap_t *) tc);
244 //printf("and returns %lx\n",regs->r8);
245 break;
246 case FW_HYPERCALL_EFI_SET_TIME:
247 case FW_HYPERCALL_EFI_GET_WAKEUP_TIME:
248 case FW_HYPERCALL_EFI_SET_WAKEUP_TIME:
249 // FIXME: need fixes in efi.h from 2.6.9
250 case FW_HYPERCALL_EFI_SET_VIRTUAL_ADDRESS_MAP:
251 // FIXME: WARNING!! IF THIS EVER GETS IMPLEMENTED
252 // SOME OF THE OTHER EFI EMULATIONS WILL CHANGE AS
253 // POINTER ARGUMENTS WILL BE VIRTUAL!!
254 case FW_HYPERCALL_EFI_GET_VARIABLE:
255 // FIXME: need fixes in efi.h from 2.6.9
256 case FW_HYPERCALL_EFI_GET_NEXT_VARIABLE:
257 case FW_HYPERCALL_EFI_SET_VARIABLE:
258 case FW_HYPERCALL_EFI_GET_NEXT_HIGH_MONO_COUNT:
259 // FIXME: need fixes in efi.h from 2.6.9
260 regs->r8 = EFI_UNSUPPORTED;
261 break;
262 case FW_HYPERCALL_IPI:
263 fw_hypercall_ipi (regs);
264 break;
265 default:
266 printf("unknown ia64 fw hypercall %lx\n", regs->r2);
267 regs->r8 = do_ni_hypercall();
268 }
269 return 1;
270 }
272 /* opt_unsafe_hypercall: If true, unsafe debugging hypercalls are allowed.
273 These can create security hole. */
274 static int opt_unsafe_hypercall = 0;
275 boolean_param("unsafe_hypercall", opt_unsafe_hypercall);
277 int
278 ia64_hypercall (struct pt_regs *regs)
279 {
280 struct vcpu *v = current;
281 unsigned long index = regs->r2;
282 int privlvl = (regs->cr_ipsr & IA64_PSR_CPL) >> IA64_PSR_CPL0_BIT;
284 if (index >= FW_HYPERCALL_FIRST_USER) {
285 /* Note: user hypercalls are not safe, since Xen doesn't
286 check memory access privilege: Xen does not deny reading
287 or writing to kernel memory. */
288 if (!opt_unsafe_hypercall) {
289 printf("user xen/ia64 hypercalls disabled\n");
290 regs->r8 = -1;
291 }
292 else switch (index) {
293 case 0xffff:
294 regs->r8 = dump_privop_counts_to_user(
295 (char *) vcpu_get_gr(v,32),
296 (int) vcpu_get_gr(v,33));
297 break;
298 case 0xfffe:
299 regs->r8 = zero_privop_counts_to_user(
300 (char *) vcpu_get_gr(v,32),
301 (int) vcpu_get_gr(v,33));
302 break;
303 default:
304 printf("unknown user xen/ia64 hypercall %lx\n", index);
305 regs->r8 = do_ni_hypercall();
306 }
307 return 1;
308 }
310 /* Hypercalls are only allowed by kernel.
311 Kernel checks memory accesses. */
312 if (privlvl != 2) {
313 /* FIXME: Return a better error value ?
314 Reflection ? Illegal operation ? */
315 regs->r8 = -1;
316 return 1;
317 }
319 if (index >= FW_HYPERCALL_FIRST_ARCH)
320 return fw_hypercall (regs);
321 else
322 return xen_hypercall (regs);
323 }
325 /* Need make this function common */
326 extern int
327 iosapic_guest_read(
328 unsigned long physbase, unsigned int reg, u32 *pval);
329 extern int
330 iosapic_guest_write(
331 unsigned long physbase, unsigned int reg, u32 pval);
333 static long do_physdev_op(XEN_GUEST_HANDLE(physdev_op_t) uop)
334 {
335 struct physdev_op op;
336 long ret;
337 int irq;
339 if ( unlikely(copy_from_guest(&op, uop, 1) != 0) )
340 return -EFAULT;
342 switch ( op.cmd )
343 {
344 case PHYSDEVOP_IRQ_UNMASK_NOTIFY:
345 ret = pirq_guest_unmask(current->domain);
346 break;
348 case PHYSDEVOP_IRQ_STATUS_QUERY:
349 irq = op.u.irq_status_query.irq;
350 ret = -EINVAL;
351 if ( (irq < 0) || (irq >= NR_IRQS) )
352 break;
353 op.u.irq_status_query.flags = 0;
354 /* Edge-triggered interrupts don't need an explicit unmask downcall. */
355 if ( !strstr(irq_desc[irq_to_vector(irq)].handler->typename, "edge") )
356 op.u.irq_status_query.flags |= PHYSDEVOP_IRQ_NEEDS_UNMASK_NOTIFY;
357 ret = 0;
358 break;
360 case PHYSDEVOP_APIC_READ:
361 ret = -EPERM;
362 if ( !IS_PRIV(current->domain) )
363 break;
364 ret = iosapic_guest_read(
365 op.u.apic_op.apic_physbase,
366 op.u.apic_op.reg,
367 &op.u.apic_op.value);
368 break;
370 case PHYSDEVOP_APIC_WRITE:
371 ret = -EPERM;
372 if ( !IS_PRIV(current->domain) )
373 break;
374 ret = iosapic_guest_write(
375 op.u.apic_op.apic_physbase,
376 op.u.apic_op.reg,
377 op.u.apic_op.value);
378 break;
380 case PHYSDEVOP_ASSIGN_VECTOR:
381 if ( !IS_PRIV(current->domain) )
382 return -EPERM;
384 if ( (irq = op.u.irq_op.irq) >= NR_IRQS )
385 return -EINVAL;
387 op.u.irq_op.vector = assign_irq_vector(irq);
388 ret = 0;
389 break;
391 default:
392 ret = -EINVAL;
393 break;
394 }
396 if ( copy_to_guest(uop, &op, 1) )
397 ret = -EFAULT;
399 return ret;
400 }