debuggers.hg

view xen/arch/x86/physdev.c @ 22863:16c863cb99f2

x86: physdev_[un]map_pirq() use rcu_lock_target_domain_by_id().

More idiomatic, and avoids accidental failure to check caller
privilege. For example, the unmap path was not checking for any
privilege before calling unmap_domain_pirq_emuirq(), which can't be
right.

Signed-off-by: Keir Fraser <keir@xen.org>
author Keir Fraser <keir@xen.org>
date Wed Jan 26 08:56:44 2011 +0000 (2011-01-26)
parents d281061e6ec0
children
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/msi.h>
13 #include <asm/hypercall.h>
14 #include <public/xen.h>
15 #include <public/physdev.h>
16 #include <xsm/xsm.h>
17 #include <asm/p2m.h>
19 #ifndef COMPAT
20 typedef long ret_t;
21 #endif
23 int
24 ioapic_guest_read(
25 unsigned long physbase, unsigned int reg, u32 *pval);
26 int
27 ioapic_guest_write(
28 unsigned long physbase, unsigned int reg, u32 pval);
30 static int physdev_hvm_map_pirq(
31 struct domain *d, struct physdev_map_pirq *map)
32 {
33 int pirq, ret = 0;
35 spin_lock(&d->event_lock);
36 switch ( map->type )
37 {
38 case MAP_PIRQ_TYPE_GSI: {
39 struct hvm_irq_dpci *hvm_irq_dpci;
40 struct hvm_girq_dpci_mapping *girq;
41 uint32_t machine_gsi = 0;
43 /* find the machine gsi corresponding to the
44 * emulated gsi */
45 hvm_irq_dpci = domain_get_irq_dpci(d);
46 if ( hvm_irq_dpci )
47 {
48 list_for_each_entry ( girq,
49 &hvm_irq_dpci->girq[map->index],
50 list )
51 machine_gsi = girq->machine_gsi;
52 }
53 /* found one, this mean we are dealing with a pt device */
54 if ( machine_gsi )
55 {
56 map->index = domain_pirq_to_irq(d, machine_gsi);
57 pirq = machine_gsi;
58 ret = (pirq > 0) ? 0 : pirq;
59 }
60 /* we didn't find any, this means we are dealing
61 * with an emulated device */
62 else
63 {
64 pirq = map->pirq;
65 if ( pirq < 0 )
66 pirq = get_free_pirq(d, map->type, map->index);
67 ret = map_domain_emuirq_pirq(d, pirq, map->index);
68 }
69 map->pirq = pirq;
70 break;
71 }
73 default:
74 ret = -EINVAL;
75 dprintk(XENLOG_G_WARNING, "map type %d not supported yet\n", map->type);
76 break;
77 }
79 spin_unlock(&d->event_lock);
80 return ret;
81 }
83 static int physdev_map_pirq(struct physdev_map_pirq *map)
84 {
85 struct domain *d;
86 int pirq, irq, ret = 0;
87 struct msi_info _msi;
88 void *map_data = NULL;
90 ret = rcu_lock_target_domain_by_id(map->domid, &d);
91 if ( ret )
92 return ret;
94 if ( map->domid == DOMID_SELF && is_hvm_domain(d) )
95 {
96 ret = physdev_hvm_map_pirq(d, map);
97 goto free_domain;
98 }
100 if ( !IS_PRIV_FOR(current->domain, d) )
101 {
102 ret = -EPERM;
103 goto free_domain;
104 }
106 /* Verify or get irq. */
107 switch ( map->type )
108 {
109 case MAP_PIRQ_TYPE_GSI:
110 if ( map->index < 0 || map->index >= nr_irqs_gsi )
111 {
112 dprintk(XENLOG_G_ERR, "dom%d: map invalid irq %d\n",
113 d->domain_id, map->index);
114 ret = -EINVAL;
115 goto free_domain;
116 }
118 irq = domain_pirq_to_irq(current->domain, map->index);
119 if ( irq <= 0 )
120 {
121 if ( IS_PRIV(current->domain) )
122 irq = map->index;
123 else {
124 dprintk(XENLOG_G_ERR, "dom%d: map pirq with incorrect irq!\n",
125 d->domain_id);
126 ret = -EINVAL;
127 goto free_domain;
128 }
129 }
130 break;
132 case MAP_PIRQ_TYPE_MSI:
133 irq = map->index;
134 if ( irq == -1 )
135 irq = create_irq();
137 if ( irq < 0 || irq >= nr_irqs )
138 {
139 dprintk(XENLOG_G_ERR, "dom%d: can't create irq for msi!\n",
140 d->domain_id);
141 ret = -EINVAL;
142 goto free_domain;
143 }
145 _msi.bus = map->bus;
146 _msi.devfn = map->devfn;
147 _msi.entry_nr = map->entry_nr;
148 _msi.table_base = map->table_base;
149 _msi.irq = irq;
150 map_data = &_msi;
151 break;
153 default:
154 dprintk(XENLOG_G_ERR, "dom%d: wrong map_pirq type %x\n",
155 d->domain_id, map->type);
156 ret = -EINVAL;
157 goto free_domain;
158 }
160 spin_lock(&pcidevs_lock);
161 /* Verify or get pirq. */
162 spin_lock(&d->event_lock);
163 pirq = domain_irq_to_pirq(d, irq);
164 if ( map->pirq < 0 )
165 {
166 if ( pirq )
167 {
168 dprintk(XENLOG_G_ERR, "dom%d: %d:%d already mapped to %d\n",
169 d->domain_id, map->index, map->pirq,
170 pirq);
171 if ( pirq < 0 )
172 {
173 ret = -EBUSY;
174 goto done;
175 }
176 }
177 else
178 {
179 pirq = get_free_pirq(d, map->type, map->index);
180 if ( pirq < 0 )
181 {
182 dprintk(XENLOG_G_ERR, "dom%d: no free pirq\n", d->domain_id);
183 ret = pirq;
184 goto done;
185 }
186 }
187 }
188 else
189 {
190 if ( pirq && pirq != map->pirq )
191 {
192 dprintk(XENLOG_G_ERR, "dom%d: pirq %d conflicts with irq %d\n",
193 d->domain_id, map->index, map->pirq);
194 ret = -EEXIST;
195 goto done;
196 }
197 else
198 pirq = map->pirq;
199 }
201 ret = map_domain_pirq(d, pirq, irq, map->type, map_data);
202 if ( ret == 0 )
203 map->pirq = pirq;
205 if ( !ret && is_hvm_domain(d) )
206 map_domain_emuirq_pirq(d, pirq, IRQ_PT);
208 done:
209 spin_unlock(&d->event_lock);
210 spin_unlock(&pcidevs_lock);
211 if ( (ret != 0) && (map->type == MAP_PIRQ_TYPE_MSI) && (map->index == -1) )
212 destroy_irq(irq);
213 free_domain:
214 rcu_unlock_domain(d);
215 return ret;
216 }
218 static int physdev_unmap_pirq(struct physdev_unmap_pirq *unmap)
219 {
220 struct domain *d;
221 int ret;
223 ret = rcu_lock_target_domain_by_id(unmap->domid, &d);
224 if ( ret )
225 return ret;
227 if ( is_hvm_domain(d) )
228 {
229 spin_lock(&d->event_lock);
230 ret = unmap_domain_pirq_emuirq(d, unmap->pirq);
231 spin_unlock(&d->event_lock);
232 if ( unmap->domid == DOMID_SELF || ret )
233 goto free_domain;
234 }
236 ret = -EPERM;
237 if ( !IS_PRIV_FOR(current->domain, d) )
238 goto free_domain;
240 spin_lock(&pcidevs_lock);
241 spin_lock(&d->event_lock);
242 ret = unmap_domain_pirq(d, unmap->pirq);
243 spin_unlock(&d->event_lock);
244 spin_unlock(&pcidevs_lock);
246 free_domain:
247 rcu_unlock_domain(d);
248 return ret;
249 }
251 ret_t do_physdev_op(int cmd, XEN_GUEST_HANDLE(void) arg)
252 {
253 int irq;
254 ret_t ret;
255 struct vcpu *v = current;
257 switch ( cmd )
258 {
259 case PHYSDEVOP_eoi: {
260 struct physdev_eoi eoi;
261 ret = -EFAULT;
262 if ( copy_from_guest(&eoi, arg, 1) != 0 )
263 break;
264 ret = -EINVAL;
265 if ( eoi.irq >= v->domain->nr_pirqs )
266 break;
267 if ( v->domain->arch.pirq_eoi_map )
268 evtchn_unmask(v->domain->pirq_to_evtchn[eoi.irq]);
269 if ( !is_hvm_domain(v->domain) ||
270 domain_pirq_to_emuirq(v->domain, eoi.irq) == IRQ_PT )
271 ret = pirq_guest_eoi(v->domain, eoi.irq);
272 else
273 ret = 0;
274 break;
275 }
277 case PHYSDEVOP_pirq_eoi_gmfn: {
278 struct physdev_pirq_eoi_gmfn info;
279 unsigned long mfn;
281 ret = -EFAULT;
282 if ( copy_from_guest(&info, arg, 1) != 0 )
283 break;
285 ret = -EINVAL;
286 mfn = gmfn_to_mfn(current->domain, info.gmfn);
287 if ( !mfn_valid(mfn) ||
288 !get_page_and_type(mfn_to_page(mfn), v->domain,
289 PGT_writable_page) )
290 break;
292 if ( cmpxchg(&v->domain->arch.pirq_eoi_map_mfn, 0, mfn) != 0 )
293 {
294 put_page_and_type(mfn_to_page(mfn));
295 ret = -EBUSY;
296 break;
297 }
299 v->domain->arch.pirq_eoi_map = map_domain_page_global(mfn);
300 if ( v->domain->arch.pirq_eoi_map == NULL )
301 {
302 v->domain->arch.pirq_eoi_map_mfn = 0;
303 put_page_and_type(mfn_to_page(mfn));
304 ret = -ENOSPC;
305 break;
306 }
308 ret = 0;
309 break;
310 }
312 /* Legacy since 0x00030202. */
313 case PHYSDEVOP_IRQ_UNMASK_NOTIFY: {
314 ret = pirq_guest_unmask(v->domain);
315 break;
316 }
318 case PHYSDEVOP_irq_status_query: {
319 struct physdev_irq_status_query irq_status_query;
320 ret = -EFAULT;
321 if ( copy_from_guest(&irq_status_query, arg, 1) != 0 )
322 break;
323 irq = irq_status_query.irq;
324 ret = -EINVAL;
325 if ( (irq < 0) || (irq >= v->domain->nr_pirqs) )
326 break;
327 irq_status_query.flags = 0;
328 if ( is_hvm_domain(v->domain) &&
329 domain_pirq_to_emuirq(v->domain, irq) != IRQ_PT )
330 {
331 ret = copy_to_guest(arg, &irq_status_query, 1) ? -EFAULT : 0;
332 break;
333 }
335 /*
336 * Even edge-triggered or message-based IRQs can need masking from
337 * time to time. If teh guest is not dynamically checking for this
338 * via the new pirq_eoi_map mechanism, it must conservatively always
339 * execute the EOI hypercall. In practice, this only really makes a
340 * difference for maskable MSI sources, and if those are supported
341 * then dom0 is probably modern anyway.
342 */
343 irq_status_query.flags |= XENIRQSTAT_needs_eoi;
344 if ( pirq_shared(v->domain, irq) )
345 irq_status_query.flags |= XENIRQSTAT_shared;
346 ret = copy_to_guest(arg, &irq_status_query, 1) ? -EFAULT : 0;
347 break;
348 }
350 case PHYSDEVOP_map_pirq: {
351 struct physdev_map_pirq map;
353 ret = -EFAULT;
354 if ( copy_from_guest(&map, arg, 1) != 0 )
355 break;
357 ret = physdev_map_pirq(&map);
359 if ( copy_to_guest(arg, &map, 1) != 0 )
360 ret = -EFAULT;
361 break;
362 }
364 case PHYSDEVOP_unmap_pirq: {
365 struct physdev_unmap_pirq unmap;
367 ret = -EFAULT;
368 if ( copy_from_guest(&unmap, arg, 1) != 0 )
369 break;
371 ret = physdev_unmap_pirq(&unmap);
372 break;
373 }
375 case PHYSDEVOP_apic_read: {
376 struct physdev_apic apic;
377 ret = -EFAULT;
378 if ( copy_from_guest(&apic, arg, 1) != 0 )
379 break;
380 ret = -EPERM;
381 if ( !IS_PRIV(v->domain) )
382 break;
383 ret = xsm_apic(v->domain, cmd);
384 if ( ret )
385 break;
386 ret = ioapic_guest_read(apic.apic_physbase, apic.reg, &apic.value);
387 if ( copy_to_guest(arg, &apic, 1) != 0 )
388 ret = -EFAULT;
389 break;
390 }
392 case PHYSDEVOP_apic_write: {
393 struct physdev_apic apic;
394 ret = -EFAULT;
395 if ( copy_from_guest(&apic, arg, 1) != 0 )
396 break;
397 ret = -EPERM;
398 if ( !IS_PRIV(v->domain) )
399 break;
400 ret = xsm_apic(v->domain, cmd);
401 if ( ret )
402 break;
403 ret = ioapic_guest_write(apic.apic_physbase, apic.reg, apic.value);
404 break;
405 }
407 case PHYSDEVOP_alloc_irq_vector: {
408 struct physdev_irq irq_op;
410 ret = -EFAULT;
411 if ( copy_from_guest(&irq_op, arg, 1) != 0 )
412 break;
414 ret = -EPERM;
415 if ( !IS_PRIV(v->domain) )
416 break;
418 ret = xsm_assign_vector(v->domain, irq_op.irq);
419 if ( ret )
420 break;
422 /* Vector is only used by hypervisor, and dom0 shouldn't
423 touch it in its world, return irq_op.irq as the vecotr,
424 and make this hypercall dummy, and also defer the vector
425 allocation when dom0 tries to programe ioapic entry. */
426 irq_op.vector = irq_op.irq;
427 ret = 0;
429 if ( copy_to_guest(arg, &irq_op, 1) != 0 )
430 ret = -EFAULT;
431 break;
432 }
434 case PHYSDEVOP_set_iopl: {
435 struct physdev_set_iopl set_iopl;
436 ret = -EFAULT;
437 if ( copy_from_guest(&set_iopl, arg, 1) != 0 )
438 break;
439 ret = -EINVAL;
440 if ( set_iopl.iopl > 3 )
441 break;
442 ret = 0;
443 v->arch.iopl = set_iopl.iopl;
444 break;
445 }
447 case PHYSDEVOP_set_iobitmap: {
448 struct physdev_set_iobitmap set_iobitmap;
449 ret = -EFAULT;
450 if ( copy_from_guest(&set_iobitmap, arg, 1) != 0 )
451 break;
452 ret = -EINVAL;
453 if ( !guest_handle_okay(set_iobitmap.bitmap, IOBMP_BYTES) ||
454 (set_iobitmap.nr_ports > 65536) )
455 break;
456 ret = 0;
457 #ifndef COMPAT
458 v->arch.iobmp = set_iobitmap.bitmap;
459 #else
460 guest_from_compat_handle(v->arch.iobmp, set_iobitmap.bitmap);
461 #endif
462 v->arch.iobmp_limit = set_iobitmap.nr_ports;
463 break;
464 }
466 case PHYSDEVOP_manage_pci_add: {
467 struct physdev_manage_pci manage_pci;
468 ret = -EPERM;
469 if ( !IS_PRIV(v->domain) )
470 break;
471 ret = -EFAULT;
472 if ( copy_from_guest(&manage_pci, arg, 1) != 0 )
473 break;
475 ret = pci_add_device(manage_pci.bus, manage_pci.devfn);
476 break;
477 }
479 case PHYSDEVOP_manage_pci_remove: {
480 struct physdev_manage_pci manage_pci;
481 ret = -EPERM;
482 if ( !IS_PRIV(v->domain) )
483 break;
484 ret = -EFAULT;
485 if ( copy_from_guest(&manage_pci, arg, 1) != 0 )
486 break;
488 ret = pci_remove_device(manage_pci.bus, manage_pci.devfn);
489 break;
490 }
492 case PHYSDEVOP_manage_pci_add_ext: {
493 struct physdev_manage_pci_ext manage_pci_ext;
494 struct pci_dev_info pdev_info;
496 ret = -EPERM;
497 if ( !IS_PRIV(current->domain) )
498 break;
500 ret = -EFAULT;
501 if ( copy_from_guest(&manage_pci_ext, arg, 1) != 0 )
502 break;
504 ret = -EINVAL;
505 if ( (manage_pci_ext.is_extfn > 1) || (manage_pci_ext.is_virtfn > 1) )
506 break;
508 pdev_info.is_extfn = manage_pci_ext.is_extfn;
509 pdev_info.is_virtfn = manage_pci_ext.is_virtfn;
510 pdev_info.physfn.bus = manage_pci_ext.physfn.bus;
511 pdev_info.physfn.devfn = manage_pci_ext.physfn.devfn;
512 ret = pci_add_device_ext(manage_pci_ext.bus,
513 manage_pci_ext.devfn,
514 &pdev_info);
515 break;
516 }
518 case PHYSDEVOP_restore_msi: {
519 struct physdev_restore_msi restore_msi;
520 struct pci_dev *pdev;
522 ret = -EPERM;
523 if ( !IS_PRIV(v->domain) )
524 break;
526 ret = -EFAULT;
527 if ( copy_from_guest(&restore_msi, arg, 1) != 0 )
528 break;
530 spin_lock(&pcidevs_lock);
531 pdev = pci_get_pdev(restore_msi.bus, restore_msi.devfn);
532 ret = pdev ? pci_restore_msi_state(pdev) : -ENODEV;
533 spin_unlock(&pcidevs_lock);
534 break;
535 }
536 case PHYSDEVOP_setup_gsi: {
537 struct physdev_setup_gsi setup_gsi;
539 ret = -EPERM;
540 if ( !IS_PRIV(v->domain) )
541 break;
543 ret = -EFAULT;
544 if ( copy_from_guest(&setup_gsi, arg, 1) != 0 )
545 break;
547 ret = -EINVAL;
548 if ( setup_gsi.gsi < 0 || setup_gsi.gsi >= nr_irqs_gsi )
549 break;
550 ret = mp_register_gsi(setup_gsi.gsi, setup_gsi.triggering,
551 setup_gsi.polarity);
552 break;
553 }
554 case PHYSDEVOP_get_free_pirq: {
555 struct physdev_get_free_pirq out;
556 struct domain *d;
558 d = rcu_lock_current_domain();
560 ret = -EFAULT;
561 if ( copy_from_guest(&out, arg, 1) != 0 )
562 break;
564 spin_lock(&d->event_lock);
565 out.pirq = get_free_pirq(d, out.type, 0);
566 d->arch.pirq_irq[out.pirq] = PIRQ_ALLOCATED;
567 spin_unlock(&d->event_lock);
569 ret = copy_to_guest(arg, &out, 1) ? -EFAULT : 0;
571 rcu_unlock_domain(d);
572 break;
573 }
574 default:
575 ret = -ENOSYS;
576 break;
577 }
579 return ret;
580 }
582 /*
583 * Local variables:
584 * mode: C
585 * c-set-style: "BSD"
586 * c-basic-offset: 4
587 * tab-width: 4
588 * indent-tabs-mode: nil
589 * End:
590 */