ia64/xen-unstable

view xen/arch/ia64/xen/hypercall.c @ 9767:46597f27a0f5

[IA64] Hypercall cleanup

Clean up xen_hypercall to query hypercall table instead.

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