ia64/xen-unstable

view xen/arch/x86/physdev.c @ 17541:6ecbb00e58cd

MSI 2/6: change the pirq to be per-domain

Signed-off-by: Jiang Yunhong <yunhong.jiang@intel.com>
Signed-off-by: Shan Haitao <haitao.shan@intel.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu May 01 10:30:22 2008 +0100 (2008-05-01)
parents 00fec8212ae6
children 86c0353f19d0
line source
2 #include <xen/config.h>
3 #include <xen/init.h>
4 #include <xen/lib.h>
5 #include <xen/types.h>
6 #include <xen/sched.h>
7 #include <xen/irq.h>
8 #include <xen/event.h>
9 #include <xen/guest_access.h>
10 #include <xen/iocap.h>
11 #include <asm/current.h>
12 #include <asm/hypercall.h>
13 #include <public/xen.h>
14 #include <public/physdev.h>
15 #include <xsm/xsm.h>
17 #ifndef COMPAT
18 typedef long ret_t;
19 #endif
21 int
22 ioapic_guest_read(
23 unsigned long physbase, unsigned int reg, u32 *pval);
24 int
25 ioapic_guest_write(
26 unsigned long physbase, unsigned int reg, u32 pval);
28 static int get_free_pirq(struct domain *d, int type, int index)
29 {
30 int i;
32 if ( d == NULL )
33 return -EINVAL;
35 ASSERT(spin_is_locked(&d->arch.irq_lock));
37 if ( type == MAP_PIRQ_TYPE_GSI )
38 {
39 for ( i = 16; i < NR_PIRQS; i++ )
40 if ( !d->arch.pirq_vector[i] )
41 break;
42 if ( i == NR_PIRQS )
43 return -ENOSPC;
44 }
45 else
46 {
47 for ( i = NR_PIRQS - 1; i >= 16; i-- )
48 if ( !d->arch.pirq_vector[i] )
49 break;
50 if ( i == 16 )
51 return -ENOSPC;
52 }
54 return i;
55 }
57 /*
58 * Caller hold the irq_lock
59 */
60 static int map_domain_pirq(struct domain *d, int pirq, int vector, int type)
61 {
62 int ret = 0;
63 int old_vector, old_pirq;
65 if ( d == NULL )
66 return -EINVAL;
68 ASSERT(spin_is_locked(&d->arch.irq_lock));
70 if ( !IS_PRIV(current->domain) )
71 return -EPERM;
73 if ( pirq < 0 || pirq >= NR_PIRQS || vector < 0 || vector >= NR_VECTORS )
74 {
75 gdprintk(XENLOG_G_ERR,
76 "invalid pirq %x or vector %x\n", pirq, vector);
77 return -EINVAL;
78 }
80 old_vector = d->arch.pirq_vector[pirq];
81 old_pirq = d->arch.vector_pirq[vector];
83 if ( (old_vector && (old_vector != vector) ) ||
84 (old_pirq && (old_pirq != pirq)) )
85 {
86 gdprintk(XENLOG_G_ERR, "remap pirq %x vector %x while not unmap\n",
87 pirq, vector);
88 ret = -EINVAL;
89 goto done;
90 }
92 ret = irq_permit_access(d, pirq);
93 if ( ret )
94 {
95 gdprintk(XENLOG_G_ERR, "add irq permit access %x failed\n", pirq);
96 ret = -EINVAL;
97 goto done;
98 }
100 d->arch.pirq_vector[pirq] = vector;
101 d->arch.vector_pirq[vector] = pirq;
103 done:
104 return ret;
105 }
107 /*
108 * The pirq should has been unbound before this call
109 */
110 static int unmap_domain_pirq(struct domain *d, int pirq)
111 {
112 int ret = 0;
113 int vector;
115 if ( d == NULL || pirq < 0 || pirq > NR_PIRQS )
116 return -EINVAL;
118 if ( !IS_PRIV(current->domain) )
119 return -EINVAL;
121 ASSERT(spin_is_locked(&d->arch.irq_lock));
123 vector = d->arch.pirq_vector[pirq];
125 if ( !vector )
126 {
127 gdprintk(XENLOG_G_ERR, "domain %X: pirq %x not mapped still\n",
128 d->domain_id, pirq);
129 ret = -EINVAL;
130 }
131 else
132 d->arch.pirq_vector[pirq] = d->arch.vector_pirq[vector] = 0;
133 ret = irq_deny_access(d, pirq);
135 if ( ret )
136 gdprintk(XENLOG_G_ERR, "deny irq %x access failed\n", pirq);
138 return ret;
139 }
141 extern int msi_irq_enable;
142 static int physdev_map_pirq(struct physdev_map_pirq *map)
143 {
144 struct domain *d;
145 int vector, pirq, ret = 0;
146 unsigned long flags;
148 /* if msi_irq_enable is not enabled,map always success */
149 if ( !msi_irq_enable )
150 return 0;
152 if ( !IS_PRIV(current->domain) )
153 return -EPERM;
155 if ( !map )
156 return -EINVAL;
158 if ( map->domid == DOMID_SELF )
159 d = rcu_lock_domain(current->domain);
160 else
161 d = rcu_lock_domain_by_id(map->domid);
163 if ( d == NULL )
164 {
165 ret = -ESRCH;
166 goto free_domain;
167 }
169 switch ( map->type )
170 {
171 case MAP_PIRQ_TYPE_GSI:
172 if ( map->index >= NR_IRQS )
173 {
174 ret = -EINVAL;
175 gdprintk(XENLOG_G_ERR,
176 "map invalid irq %x\n", map->index);
177 goto free_domain;
178 }
179 vector = IO_APIC_VECTOR(map->index);
180 if ( !vector )
181 {
182 ret = -EINVAL;
183 gdprintk(XENLOG_G_ERR,
184 "map irq with no vector %x\n", map->index);
185 goto free_domain;
186 }
187 break;
188 case MAP_PIRQ_TYPE_MSI:
189 vector = map->index;
190 if ( vector < 0 || vector >= NR_VECTORS )
191 {
192 ret = -EINVAL;
193 gdprintk(XENLOG_G_ERR,
194 "map_pirq with wrong vector %x\n", map->index);
195 goto free_domain;
196 }
197 break;
198 default:
199 ret = -EINVAL;
200 gdprintk(XENLOG_G_ERR, "wrong map_pirq type %x\n", map->type);
201 goto free_domain;
202 break;
203 }
205 spin_lock_irqsave(&d->arch.irq_lock, flags);
206 if ( map->pirq == -1 )
207 {
208 if ( d->arch.vector_pirq[vector] )
209 {
210 gdprintk(XENLOG_G_ERR, "%x %x mapped already%x\n",
211 map->index, map->pirq,
212 d->arch.vector_pirq[vector]);
213 pirq = d->arch.vector_pirq[vector];
214 }
215 else
216 {
217 pirq = get_free_pirq(d, map->type, map->index);
218 if ( pirq < 0 )
219 {
220 ret = pirq;
221 gdprintk(XENLOG_G_ERR, "No free pirq\n");
222 goto done;
223 }
224 }
225 }
226 else
227 {
228 if ( d->arch.vector_pirq[vector] &&
229 d->arch.vector_pirq[vector] != map->pirq )
230 {
231 gdprintk(XENLOG_G_ERR, "%x conflict with %x\n",
232 map->index, map->pirq);
233 ret = -EEXIST;
234 goto done;
235 }
236 else
237 pirq = map->pirq;
238 }
240 ret = map_domain_pirq(d, pirq, vector, map->type);
242 if ( !ret )
243 map->pirq = pirq;
244 done:
245 spin_unlock_irqrestore(&d->arch.irq_lock, flags);
246 free_domain:
247 rcu_unlock_domain(d);
248 return ret;
249 }
251 static int physdev_unmap_pirq(struct physdev_unmap_pirq *unmap)
252 {
253 struct domain *d;
254 unsigned long flags;
255 int ret;
257 if ( !msi_irq_enable )
258 return 0;
260 if ( !IS_PRIV(current->domain) )
261 return -EPERM;
263 if ( !unmap )
264 return -EINVAL;
266 if ( unmap->domid == DOMID_SELF )
267 d = rcu_lock_domain(current->domain);
268 else
269 d = rcu_lock_domain_by_id(unmap->domid);
271 if ( d == NULL )
272 {
273 rcu_unlock_domain(d);
274 return -ESRCH;
275 }
277 spin_lock_irqsave(&d->arch.irq_lock, flags);
278 ret = unmap_domain_pirq(d, unmap->pirq);
279 spin_unlock_irqrestore(&d->arch.irq_lock, flags);
280 rcu_unlock_domain(d);
282 return ret;
283 }
285 ret_t do_physdev_op(int cmd, XEN_GUEST_HANDLE(void) arg)
286 {
287 int irq;
288 ret_t ret;
289 struct vcpu *v = current;
291 switch ( cmd )
292 {
293 case PHYSDEVOP_eoi: {
294 struct physdev_eoi eoi;
295 ret = -EFAULT;
296 if ( copy_from_guest(&eoi, arg, 1) != 0 )
297 break;
298 ret = pirq_guest_eoi(v->domain, eoi.irq);
299 break;
300 }
302 /* Legacy since 0x00030202. */
303 case PHYSDEVOP_IRQ_UNMASK_NOTIFY: {
304 ret = pirq_guest_unmask(v->domain);
305 break;
306 }
308 case PHYSDEVOP_irq_status_query: {
309 struct physdev_irq_status_query irq_status_query;
310 ret = -EFAULT;
311 if ( copy_from_guest(&irq_status_query, arg, 1) != 0 )
312 break;
313 irq = irq_status_query.irq;
314 ret = -EINVAL;
315 if ( (irq < 0) || (irq >= NR_IRQS) )
316 break;
317 irq_status_query.flags = 0;
318 if ( pirq_acktype(v->domain, irq) != 0 )
319 irq_status_query.flags |= XENIRQSTAT_needs_eoi;
320 if ( pirq_shared(v->domain, irq) )
321 irq_status_query.flags |= XENIRQSTAT_shared;
322 ret = copy_to_guest(arg, &irq_status_query, 1) ? -EFAULT : 0;
323 break;
324 }
326 case PHYSDEVOP_map_pirq: {
327 struct physdev_map_pirq map;
329 ret = -EFAULT;
330 if ( copy_from_guest(&map, arg, 1) != 0 )
331 break;
333 ret = physdev_map_pirq(&map);
334 if ( copy_to_guest(arg, &map, 1) != 0 )
335 ret = -EFAULT;
336 break;
337 }
339 case PHYSDEVOP_unmap_pirq: {
340 struct physdev_unmap_pirq unmap;
342 ret = -EFAULT;
343 if ( copy_from_guest(&unmap, arg, 1) != 0 )
344 break;
346 ret = physdev_unmap_pirq(&unmap);
347 break;
348 }
350 case PHYSDEVOP_apic_read: {
351 struct physdev_apic apic;
352 ret = -EFAULT;
353 if ( copy_from_guest(&apic, arg, 1) != 0 )
354 break;
355 ret = -EPERM;
356 if ( !IS_PRIV(v->domain) )
357 break;
358 ret = xsm_apic(v->domain, cmd);
359 if ( ret )
360 break;
361 ret = ioapic_guest_read(apic.apic_physbase, apic.reg, &apic.value);
362 if ( copy_to_guest(arg, &apic, 1) != 0 )
363 ret = -EFAULT;
364 break;
365 }
367 case PHYSDEVOP_apic_write: {
368 struct physdev_apic apic;
369 ret = -EFAULT;
370 if ( copy_from_guest(&apic, arg, 1) != 0 )
371 break;
372 ret = -EPERM;
373 if ( !IS_PRIV(v->domain) )
374 break;
375 ret = xsm_apic(v->domain, cmd);
376 if ( ret )
377 break;
378 ret = ioapic_guest_write(apic.apic_physbase, apic.reg, apic.value);
379 break;
380 }
382 case PHYSDEVOP_alloc_irq_vector: {
383 struct physdev_irq irq_op;
384 unsigned long flags;
386 ret = -EFAULT;
387 if ( copy_from_guest(&irq_op, arg, 1) != 0 )
388 break;
390 ret = -EPERM;
391 if ( !IS_PRIV(v->domain) )
392 break;
394 ret = xsm_assign_vector(v->domain, irq_op.irq);
395 if ( ret )
396 break;
398 irq = irq_op.irq;
399 ret = -EINVAL;
400 if ( (irq < 0) || (irq >= NR_IRQS) )
401 break;
403 irq_op.vector = assign_irq_vector(irq);
405 ret = 0;
407 if ( msi_irq_enable )
408 {
409 spin_lock_irqsave(&dom0->arch.irq_lock, flags);
410 if ( irq != AUTO_ASSIGN )
411 ret = map_domain_pirq(dom0, irq_op.irq, irq_op.vector,
412 MAP_PIRQ_TYPE_GSI);
413 spin_unlock_irqrestore(&dom0->arch.irq_lock, flags);
414 }
416 if ( copy_to_guest(arg, &irq_op, 1) != 0 )
417 ret = -EFAULT;
418 break;
419 }
421 case PHYSDEVOP_set_iopl: {
422 struct physdev_set_iopl set_iopl;
423 ret = -EFAULT;
424 if ( copy_from_guest(&set_iopl, arg, 1) != 0 )
425 break;
426 ret = -EINVAL;
427 if ( set_iopl.iopl > 3 )
428 break;
429 ret = 0;
430 v->arch.iopl = set_iopl.iopl;
431 break;
432 }
434 case PHYSDEVOP_set_iobitmap: {
435 struct physdev_set_iobitmap set_iobitmap;
436 ret = -EFAULT;
437 if ( copy_from_guest(&set_iobitmap, arg, 1) != 0 )
438 break;
439 ret = -EINVAL;
440 if ( !guest_handle_okay(set_iobitmap.bitmap, IOBMP_BYTES) ||
441 (set_iobitmap.nr_ports > 65536) )
442 break;
443 ret = 0;
444 #ifndef COMPAT
445 v->arch.iobmp = set_iobitmap.bitmap;
446 #else
447 guest_from_compat_handle(v->arch.iobmp, set_iobitmap.bitmap);
448 #endif
449 v->arch.iobmp_limit = set_iobitmap.nr_ports;
450 break;
451 }
453 default:
454 ret = -ENOSYS;
455 break;
456 }
458 return ret;
459 }
461 /*
462 * Local variables:
463 * mode: C
464 * c-set-style: "BSD"
465 * c-basic-offset: 4
466 * tab-width: 4
467 * indent-tabs-mode: nil
468 * End:
469 */