ia64/xen-unstable

view xen/arch/ia64/xen/hypercall.c @ 9747:de2dc4e7966a

[IA64] Add support to physdev_ops

Add support to physdev ops, and thus give IOSAPIC RTEs
managed by Xen now. Dom0 now issues hypercall to r/w
RTE entry. Another change is the irq vector allocation
which is also owned by xen now.

After this change, the IOSAPIC is almost owned by xen
with only exception as IOSAPIC EOI which is still issued
by dom0 directly. But that's OK since currently dom0
owns all external physical devices. Later full event
channel mechanism will provide necessary support for
driver domain, and at that time, dom0 instead issues
physdev_op (PHYSDEVOP_IRQ_UNMASK_NOTIFY) naturally as
replace of IOSAPIC EOI.

Signed-off-by Kevin Tian <kevin.tian@intel.com>
author awilliam@xenbuild.aw
date Fri Apr 21 09:03:19 2006 -0600 (2006-04-21)
parents 5cc367720223
children de0c04ed4ab7
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>
28 extern unsigned long translate_domain_mpaddr(unsigned long);
29 static long do_physdev_op(GUEST_HANDLE(physdev_op_t) uop);
30 /* FIXME: where these declarations should be there ? */
31 extern int dump_privop_counts_to_user(char *, int);
32 extern int zero_privop_counts_to_user(char *, int);
34 unsigned long idle_when_pending = 0;
35 unsigned long pal_halt_light_count = 0;
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,
56 (hypercall_t)do_xen_version,
57 (hypercall_t)do_console_io,
58 (hypercall_t)do_physdev_op, /* do_physdev_op */
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_ni_hypercall, /* */ /* 30 */
70 (hypercall_t)do_ni_hypercall /* */
71 };
73 static int
74 xen_hypercall (struct pt_regs *regs)
75 {
76 switch (regs->r2) {
77 case __HYPERVISOR_sched_op_compat:
78 regs->r8 = do_sched_op_compat((int) regs->r14,
79 (unsigned long) regs->r15);
80 break;
82 case __HYPERVISOR_dom0_op:
83 regs->r8 = do_dom0_op(guest_handle_from_ptr(regs->r14,
84 dom0_op_t));
85 break;
87 case __HYPERVISOR_memory_op:
88 regs->r8 = do_memory_op(regs->r14,
89 guest_handle_from_ptr(regs->r15, void));
90 break;
92 case __HYPERVISOR_event_channel_op:
93 regs->r8 = do_event_channel_op(guest_handle_from_ptr(regs->r14, evtchn_op_t));
94 break;
96 case __HYPERVISOR_physdev_op:
97 regs->r8 = do_physdev_op(guest_handle_from_ptr(regs->r14,
98 physdev_op_t));
99 break;
101 case __HYPERVISOR_grant_table_op:
102 regs->r8 = do_grant_table_op((unsigned int) regs->r14,
103 guest_handle_from_ptr(regs->r15, void),
104 (unsigned int) regs->r16);
105 break;
107 case __HYPERVISOR_console_io:
108 regs->r8 = do_console_io((int) regs->r14, (int) regs->r15,
109 guest_handle_from_ptr(regs->r16, char));
110 break;
112 case __HYPERVISOR_xen_version:
113 regs->r8 = do_xen_version((int) regs->r14,
114 guest_handle_from_ptr(regs->r15, void));
115 break;
117 case __HYPERVISOR_multicall:
118 regs->r8 = do_multicall(guest_handle_from_ptr(regs->r14,
119 multicall_entry_t), (unsigned int) regs->r15);
120 break;
122 case __HYPERVISOR_sched_op:
123 regs->r8 = do_sched_op((int) regs->r14,
124 guest_handle_from_ptr(regs->r15, void));
125 break;
127 default:
128 printf("unknown xen hypercall %lx\n", regs->r2);
129 regs->r8 = do_ni_hypercall();
130 }
131 return 1;
132 }
135 static int
136 fw_hypercall (struct pt_regs *regs)
137 {
138 struct vcpu *v = current;
139 struct sal_ret_values x;
140 unsigned long *tv, *tc;
142 switch (regs->r2) {
143 case FW_HYPERCALL_PAL_CALL:
144 //printf("*** PAL hypercall: index=%d\n",regs->r28);
145 //FIXME: This should call a C routine
146 #if 0
147 // This is very conservative, but avoids a possible
148 // (and deadly) freeze in paravirtualized domains due
149 // to a yet-to-be-found bug where pending_interruption
150 // is zero when it shouldn't be. Since PAL is called
151 // in the idle loop, this should resolve it
152 VCPU(v,pending_interruption) = 1;
153 #endif
154 if (regs->r28 == PAL_HALT_LIGHT) {
155 int pi;
156 #define SPURIOUS_VECTOR 15
157 pi = vcpu_check_pending_interrupts(v);
158 if (pi != SPURIOUS_VECTOR) {
159 if (!VCPU(v,pending_interruption))
160 idle_when_pending++;
161 vcpu_pend_unspecified_interrupt(v);
162 //printf("idle w/int#%d pending!\n",pi);
163 //this shouldn't happen, but it apparently does quite a bit! so don't
164 //allow it to happen... i.e. if a domain has an interrupt pending and
165 //it tries to halt itself because it thinks it is idle, just return here
166 //as deliver_pending_interrupt is called on the way out and will deliver it
167 }
168 else {
169 pal_halt_light_count++;
170 do_sched_op_compat(SCHEDOP_yield, 0);
171 }
172 regs->r8 = 0;
173 regs->r9 = 0;
174 regs->r10 = 0;
175 regs->r11 = 0;
176 }
177 else {
178 struct ia64_pal_retval y;
180 if (regs->r28 >= PAL_COPY_PAL)
181 y = xen_pal_emulator
182 (regs->r28, vcpu_get_gr (v, 33),
183 vcpu_get_gr (v, 34),
184 vcpu_get_gr (v, 35));
185 else
186 y = xen_pal_emulator(regs->r28,regs->r29,
187 regs->r30,regs->r31);
188 regs->r8 = y.status; regs->r9 = y.v0;
189 regs->r10 = y.v1; regs->r11 = y.v2;
190 }
191 break;
192 case FW_HYPERCALL_SAL_CALL:
193 x = sal_emulator(vcpu_get_gr(v,32),vcpu_get_gr(v,33),
194 vcpu_get_gr(v,34),vcpu_get_gr(v,35),
195 vcpu_get_gr(v,36),vcpu_get_gr(v,37),
196 vcpu_get_gr(v,38),vcpu_get_gr(v,39));
197 regs->r8 = x.r8; regs->r9 = x.r9;
198 regs->r10 = x.r10; regs->r11 = x.r11;
199 break;
200 case FW_HYPERCALL_EFI_RESET_SYSTEM:
201 printf("efi.reset_system called ");
202 if (current->domain == dom0) {
203 printf("(by dom0)\n ");
204 (*efi.reset_system)(EFI_RESET_WARM,0,0,NULL);
205 }
206 else
207 domain_shutdown (current->domain, SHUTDOWN_reboot);
208 regs->r8 = EFI_UNSUPPORTED;
209 break;
210 case FW_HYPERCALL_EFI_GET_TIME:
211 tv = (unsigned long *) vcpu_get_gr(v,32);
212 tc = (unsigned long *) vcpu_get_gr(v,33);
213 //printf("efi_get_time(%p,%p) called...",tv,tc);
214 tv = (unsigned long *) __va(translate_domain_mpaddr((unsigned long) tv));
215 if (tc) tc = (unsigned long *) __va(translate_domain_mpaddr((unsigned long) tc));
216 regs->r8 = (*efi.get_time)((efi_time_t *) tv, (efi_time_cap_t *) tc);
217 //printf("and returns %lx\n",regs->r8);
218 break;
219 case FW_HYPERCALL_EFI_SET_TIME:
220 case FW_HYPERCALL_EFI_GET_WAKEUP_TIME:
221 case FW_HYPERCALL_EFI_SET_WAKEUP_TIME:
222 // FIXME: need fixes in efi.h from 2.6.9
223 case FW_HYPERCALL_EFI_SET_VIRTUAL_ADDRESS_MAP:
224 // FIXME: WARNING!! IF THIS EVER GETS IMPLEMENTED
225 // SOME OF THE OTHER EFI EMULATIONS WILL CHANGE AS
226 // POINTER ARGUMENTS WILL BE VIRTUAL!!
227 case FW_HYPERCALL_EFI_GET_VARIABLE:
228 // FIXME: need fixes in efi.h from 2.6.9
229 case FW_HYPERCALL_EFI_GET_NEXT_VARIABLE:
230 case FW_HYPERCALL_EFI_SET_VARIABLE:
231 case FW_HYPERCALL_EFI_GET_NEXT_HIGH_MONO_COUNT:
232 // FIXME: need fixes in efi.h from 2.6.9
233 regs->r8 = EFI_UNSUPPORTED;
234 break;
235 default:
236 printf("unknown ia64 fw hypercall %lx\n", regs->r2);
237 regs->r8 = do_ni_hypercall();
238 }
239 return 1;
240 }
242 /* opt_unsafe_hypercall: If true, unsafe debugging hypercalls are allowed.
243 These can create security hole. */
244 static int opt_unsafe_hypercall = 0;
245 boolean_param("unsafe_hypercall", opt_unsafe_hypercall);
247 int
248 ia64_hypercall (struct pt_regs *regs)
249 {
250 struct vcpu *v = current;
251 unsigned long index = regs->r2;
252 int privlvl = (regs->cr_ipsr & IA64_PSR_CPL) >> IA64_PSR_CPL0_BIT;
254 if (index >= FW_HYPERCALL_FIRST_USER) {
255 /* Note: user hypercalls are not safe, since Xen doesn't
256 check memory access privilege: Xen does not deny reading
257 or writing to kernel memory. */
258 if (!opt_unsafe_hypercall) {
259 printf("user xen/ia64 hypercalls disabled\n");
260 regs->r8 = -1;
261 }
262 else switch (index) {
263 case 0xffff:
264 regs->r8 = dump_privop_counts_to_user(
265 (char *) vcpu_get_gr(v,32),
266 (int) vcpu_get_gr(v,33));
267 break;
268 case 0xfffe:
269 regs->r8 = zero_privop_counts_to_user(
270 (char *) vcpu_get_gr(v,32),
271 (int) vcpu_get_gr(v,33));
272 break;
273 default:
274 printf("unknown user xen/ia64 hypercall %lx\n", index);
275 regs->r8 = do_ni_hypercall();
276 }
277 return 1;
278 }
280 /* Hypercalls are only allowed by kernel.
281 Kernel checks memory accesses. */
282 if (privlvl != 2) {
283 /* FIXME: Return a better error value ?
284 Reflection ? Illegal operation ? */
285 regs->r8 = -1;
286 return 1;
287 }
289 if (index >= FW_HYPERCALL_FIRST_ARCH)
290 return fw_hypercall (regs);
291 else
292 return xen_hypercall (regs);
293 }
295 /* Need make this function common */
296 extern int
297 iosapic_guest_read(
298 unsigned long physbase, unsigned int reg, u32 *pval);
299 extern int
300 iosapic_guest_write(
301 unsigned long physbase, unsigned int reg, u32 pval);
303 static long do_physdev_op(GUEST_HANDLE(physdev_op_t) uop)
304 {
305 struct physdev_op op;
306 long ret;
307 int irq;
309 if ( unlikely(copy_from_guest(&op, uop, 1) != 0) )
310 return -EFAULT;
312 switch ( op.cmd )
313 {
314 case PHYSDEVOP_IRQ_UNMASK_NOTIFY:
315 ret = pirq_guest_unmask(current->domain);
316 break;
318 case PHYSDEVOP_IRQ_STATUS_QUERY:
319 irq = op.u.irq_status_query.irq;
320 ret = -EINVAL;
321 if ( (irq < 0) || (irq >= NR_IRQS) )
322 break;
323 op.u.irq_status_query.flags = 0;
324 /* Edge-triggered interrupts don't need an explicit unmask downcall. */
325 if ( !strstr(irq_desc[irq_to_vector(irq)].handler->typename, "edge") )
326 op.u.irq_status_query.flags |= PHYSDEVOP_IRQ_NEEDS_UNMASK_NOTIFY;
327 ret = 0;
328 break;
330 case PHYSDEVOP_APIC_READ:
331 ret = -EPERM;
332 if ( !IS_PRIV(current->domain) )
333 break;
334 ret = iosapic_guest_read(
335 op.u.apic_op.apic_physbase,
336 op.u.apic_op.reg,
337 &op.u.apic_op.value);
338 break;
340 case PHYSDEVOP_APIC_WRITE:
341 ret = -EPERM;
342 if ( !IS_PRIV(current->domain) )
343 break;
344 ret = iosapic_guest_write(
345 op.u.apic_op.apic_physbase,
346 op.u.apic_op.reg,
347 op.u.apic_op.value);
348 break;
350 case PHYSDEVOP_ASSIGN_VECTOR:
351 if ( !IS_PRIV(current->domain) )
352 return -EPERM;
354 if ( (irq = op.u.irq_op.irq) >= NR_IRQS )
355 return -EINVAL;
357 op.u.irq_op.vector = assign_irq_vector(irq);
358 ret = 0;
359 break;
361 default:
362 ret = -EINVAL;
363 break;
364 }
366 if ( copy_to_guest(uop, &op, 1) )
367 ret = -EFAULT;
369 return ret;
370 }