1 /*        $NetBSD: pci_msi_machdep.c,v 1.19 2023/11/21 23:22:23 gutteridge Exp $          */
2 
3 /*
4  * Copyright (c) 2015 Internet Initiative Japan Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /*
30  * TODO
31  *
32  *     - PBA (Pending Bit Array) support
33  *     - HyperTransport mapping support
34  */
35 
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: pci_msi_machdep.c,v 1.19 2023/11/21 23:22:23 gutteridge Exp $");
38 
39 #include "opt_intrdebug.h"
40 #include "ioapic.h"
41 
42 #include <sys/types.h>
43 #include <sys/param.h>
44 #include <sys/time.h>
45 #include <sys/systm.h>
46 #include <sys/cpu.h>
47 #include <sys/errno.h>
48 #include <sys/device.h>
49 #include <sys/intr.h>
50 #include <sys/kmem.h>
51 
52 #include <dev/pci/pcivar.h>
53 
54 #include <machine/i82093var.h>
55 #include <machine/pic.h>
56 
57 #include <x86/pci/msipic.h>
58 #include <x86/pci/pci_msi_machdep.h>
59 
60 #ifdef INTRDEBUG
61 #define MSIDEBUG
62 #endif
63 
64 #ifdef MSIDEBUG
65 #define DPRINTF(msg) printf msg
66 #else
67 #define DPRINTF(msg)
68 #endif
69 
70 static pci_intr_handle_t
pci_msi_calculate_handle(struct pic * msi_pic,int vector)71 pci_msi_calculate_handle(struct pic *msi_pic, int vector)
72 {
73           pci_intr_handle_t pih;
74 
75           KASSERT(msipic_is_msi_pic(msi_pic));
76 
77           pih = __SHIFTIN((uint64_t)msipic_get_devid(msi_pic), MSI_INT_DEV_MASK)
78               | __SHIFTIN((uint64_t)vector, MSI_INT_VEC_MASK)
79               | APIC_INT_VIA_MSI;
80           if (msi_pic->pic_type == PIC_MSI)
81                     MSI_INT_MAKE_MSI(pih);
82           else if (msi_pic->pic_type == PIC_MSIX)
83                     MSI_INT_MAKE_MSIX(pih);
84           else
85                     panic("%s: Unexpected pic_type: %d\n", __func__,
86                         msi_pic->pic_type);
87 
88           return pih;
89 }
90 
91 #ifdef __HAVE_PCI_MSI_MSIX
92 static pci_intr_handle_t *
pci_msi_alloc_vectors(struct pic * msi_pic,uint * table_indexes,int * count)93 pci_msi_alloc_vectors(struct pic *msi_pic, uint *table_indexes, int *count)
94 {
95           struct intrsource *isp;
96           pci_intr_handle_t *vectors, pih;
97           int i;
98           const char *intrstr;
99           char intrstr_buf[INTRIDBUF];
100 
101           vectors = kmem_zalloc(sizeof(vectors[0]) * (*count), KM_SLEEP);
102           mutex_enter(&cpu_lock);
103           for (i = 0; i < *count; i++) {
104                     u_int table_index;
105 
106                     if (table_indexes == NULL)
107                               table_index = i;
108                     else
109                               table_index = table_indexes[i];
110 
111                     pih = pci_msi_calculate_handle(msi_pic, table_index);
112 
113                     intrstr = x86_pci_msi_string(NULL, pih, intrstr_buf,
114                         sizeof(intrstr_buf));
115                     isp = intr_allocate_io_intrsource(intrstr);
116                     if (isp == NULL) {
117                               mutex_exit(&cpu_lock);
118                               DPRINTF(("can't allocate io_intersource\n"));
119                               kmem_free(vectors, sizeof(vectors[0]) * (*count));
120                               return NULL;
121                     }
122 
123                     vectors[i] = pih;
124           }
125           mutex_exit(&cpu_lock);
126 
127           return vectors;
128 }
129 #endif /* __HAVE_PCI_MSI_MSIX */
130 
131 static void
pci_msi_free_vectors(struct pic * msi_pic,pci_intr_handle_t * pihs,int count)132 pci_msi_free_vectors(struct pic *msi_pic, pci_intr_handle_t *pihs, int count)
133 {
134           pci_intr_handle_t pih;
135           int i;
136           const char *intrstr;
137           char intrstr_buf[INTRIDBUF];
138 
139           mutex_enter(&cpu_lock);
140           for (i = 0; i < count; i++) {
141                     pih = pci_msi_calculate_handle(msi_pic, i);
142                     intrstr = x86_pci_msi_string(NULL, pih, intrstr_buf,
143                         sizeof(intrstr_buf));
144                     intr_free_io_intrsource(intrstr);
145           }
146           mutex_exit(&cpu_lock);
147 
148           kmem_free(pihs, sizeof(pihs[0]) * count);
149 }
150 
151 #ifdef __HAVE_PCI_MSI_MSIX
152 static int
pci_msi_alloc_common(pci_intr_handle_t ** ihps,int * count,const struct pci_attach_args * pa,bool exact)153 pci_msi_alloc_common(pci_intr_handle_t **ihps, int *count,
154     const struct pci_attach_args *pa, bool exact)
155 {
156           struct pic *msi_pic;
157           pci_intr_handle_t *vectors;
158           int error, i;
159 
160 #if NIOAPIC > 0
161           if (nioapics == 0) {
162                     DPRINTF(("no IOAPIC.\n"));
163                     return ENODEV;
164           }
165 #endif
166 
167           if ((pa->pa_flags & PCI_FLAGS_MSI_OKAY) == 0) {
168                     DPRINTF(("PCI host bridge does not support MSI.\n"));
169                     return ENODEV;
170           }
171 
172           msi_pic = msipic_construct_msi_pic(pa);
173           if (msi_pic == NULL) {
174                     DPRINTF(("cannot allocate MSI pic.\n"));
175                     return EINVAL;
176           }
177 
178           vectors = NULL;
179           while (*count > 0) {
180                     vectors = pci_msi_alloc_vectors(msi_pic, NULL, count);
181                     if (vectors != NULL)
182                               break;
183 
184                     if (exact) {
185                               DPRINTF(("cannot allocate MSI vectors.\n"));
186                               msipic_destruct_msi_pic(msi_pic);
187                               return ENOMEM;
188                     } else {
189                               (*count) >>= 1; /* must be power of 2. */
190                               continue;
191                     }
192           }
193           if (vectors == NULL) {
194                     DPRINTF(("cannot allocate MSI vectors.\n"));
195                     msipic_destruct_msi_pic(msi_pic);
196                     return ENOMEM;
197           }
198 
199           for (i = 0; i < *count; i++) {
200                     MSI_INT_MAKE_MSI(vectors[i]);
201           }
202 
203           error = msipic_set_msi_vectors(msi_pic, NULL, *count);
204           if (error) {
205                     pci_msi_free_vectors(msi_pic, vectors, *count);
206                     msipic_destruct_msi_pic(msi_pic);
207                     return error;
208           }
209 
210           *ihps = vectors;
211 #ifdef XENPV
212           if (xen_map_msi_pirq(msi_pic, *count)) {
213                     DPRINTF(("xen_map_msi_pirq() failed\n"));
214                     pci_msi_free_vectors(msi_pic, vectors, *count);
215                     msipic_destruct_msi_pic(msi_pic);
216                     return EINVAL;
217           }
218 #endif
219 
220           return 0;
221 }
222 #endif /* __HAVE_PCI_MSI_MSIX */
223 
224 static void *
pci_msi_common_establish(pci_chipset_tag_t pc,pci_intr_handle_t ih,int level,int (* func)(void *),void * arg,struct pic * pic,const char * xname)225 pci_msi_common_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih,
226     int level, int (*func)(void *), void *arg, struct pic *pic,
227     const char *xname)
228 {
229           int irq, pin;
230           bool mpsafe;
231 
232           KASSERT(INT_VIA_MSI(ih));
233 
234           irq = -1;
235           pin = MSI_INT_VEC(ih);
236           mpsafe = ((ih & MPSAFE_MASK) != 0);
237 
238           return intr_establish_xname(irq, pic, pin, IST_EDGE, level, func, arg,
239               mpsafe, xname);
240 }
241 
242 static void
pci_msi_common_disestablish(pci_chipset_tag_t pc,void * cookie)243 pci_msi_common_disestablish(pci_chipset_tag_t pc, void *cookie)
244 {
245 
246           intr_disestablish(cookie);
247 }
248 
249 #ifdef __HAVE_PCI_MSI_MSIX
250 static int
pci_msix_alloc_common(pci_intr_handle_t ** ihps,u_int * table_indexes,int * count,const struct pci_attach_args * pa,bool exact)251 pci_msix_alloc_common(pci_intr_handle_t **ihps, u_int *table_indexes,
252     int *count, const struct pci_attach_args *pa, bool exact)
253 {
254           struct pic *msix_pic;
255           pci_intr_handle_t *vectors;
256           int error, i;
257 
258 #if NIOAPIC > 0
259           if (nioapics == 0) {
260                     DPRINTF(("no IOAPIC.\n"));
261                     return ENODEV;
262           }
263 #endif
264 
265           if ((pa->pa_flags & PCI_FLAGS_MSIX_OKAY) == 0) {
266                     DPRINTF(("PCI host bridge does not support MSI-X.\n"));
267                     return ENODEV;
268           }
269 
270           msix_pic = msipic_construct_msix_pic(pa);
271           if (msix_pic == NULL)
272                     return EINVAL;
273 
274           vectors = NULL;
275           while (*count > 0) {
276                     vectors = pci_msi_alloc_vectors(msix_pic, table_indexes, count);
277                     if (vectors != NULL)
278                               break;
279 
280                     if (exact) {
281                               DPRINTF(("cannot allocate MSI-X vectors.\n"));
282                               msipic_destruct_msix_pic(msix_pic);
283                               return ENOMEM;
284                     } else {
285                               (*count)--;
286                               continue;
287                     }
288           }
289           if (vectors == NULL) {
290                     DPRINTF(("cannot allocate MSI-X vectors.\n"));
291                     msipic_destruct_msix_pic(msix_pic);
292                     return ENOMEM;
293           }
294 
295           for (i = 0; i < *count; i++) {
296                     MSI_INT_MAKE_MSIX(vectors[i]);
297           }
298 
299           error = msipic_set_msi_vectors(msix_pic, vectors, *count);
300           if (error) {
301                     pci_msi_free_vectors(msix_pic, vectors, *count);
302                     msipic_destruct_msix_pic(msix_pic);
303                     return error;
304           }
305 
306           *ihps = vectors;
307 
308 #ifdef XENPV
309           if (xen_map_msix_pirq(msix_pic, *count)) {
310                     DPRINTF(("xen_map_msi_pirq() failed\n"));
311                     pci_msi_free_vectors(msix_pic, vectors, *count);
312                     msipic_destruct_msix_pic(msix_pic);
313                     return EINVAL;
314           }
315 #endif
316           return 0;
317 }
318 
319 static int
x86_pci_msi_alloc(pci_intr_handle_t ** ihps,int * count,const struct pci_attach_args * pa)320 x86_pci_msi_alloc(pci_intr_handle_t **ihps, int *count,
321     const struct pci_attach_args *pa)
322 {
323 
324           return pci_msi_alloc_common(ihps, count, pa, false);
325 }
326 
327 static int
x86_pci_msi_alloc_exact(pci_intr_handle_t ** ihps,int count,const struct pci_attach_args * pa)328 x86_pci_msi_alloc_exact(pci_intr_handle_t **ihps, int count,
329     const struct pci_attach_args *pa)
330 {
331 
332           return pci_msi_alloc_common(ihps, &count, pa, true);
333 }
334 #endif /* __HAVE_PCI_MSI_MSIX */
335 
336 static void
x86_pci_msi_release_internal(pci_intr_handle_t * pihs,int count)337 x86_pci_msi_release_internal(pci_intr_handle_t *pihs, int count)
338 {
339           struct pic *pic;
340 
341           pic = msipic_find_msi_pic(MSI_INT_DEV(pihs[0]));
342           if (pic == NULL)
343                     return;
344 
345 #ifdef XENPV
346           xen_pci_msi_release(pic, count);
347 #endif
348           pci_msi_free_vectors(pic, pihs, count);
349           msipic_destruct_msi_pic(pic);
350 }
351 
352 #ifdef __HAVE_PCI_MSI_MSIX
353 static int
x86_pci_msix_alloc(pci_intr_handle_t ** ihps,int * count,const struct pci_attach_args * pa)354 x86_pci_msix_alloc(pci_intr_handle_t **ihps, int *count,
355     const struct pci_attach_args *pa)
356 {
357 
358           return pci_msix_alloc_common(ihps, NULL, count, pa, false);
359 }
360 
361 static int
x86_pci_msix_alloc_exact(pci_intr_handle_t ** ihps,int count,const struct pci_attach_args * pa)362 x86_pci_msix_alloc_exact(pci_intr_handle_t **ihps, int count,
363     const struct pci_attach_args *pa)
364 {
365 
366           return pci_msix_alloc_common(ihps, NULL, &count, pa, true);
367 }
368 
369 static int
x86_pci_msix_alloc_map(pci_intr_handle_t ** ihps,u_int * table_indexes,int count,const struct pci_attach_args * pa)370 x86_pci_msix_alloc_map(pci_intr_handle_t **ihps, u_int *table_indexes,
371     int count, const struct pci_attach_args *pa)
372 {
373 
374           return pci_msix_alloc_common(ihps, table_indexes, &count, pa, true);
375 }
376 #endif /* __HAVE_PCI_MSI_MSIX */
377 
378 static void
x86_pci_msix_release_internal(pci_intr_handle_t * pihs,int count)379 x86_pci_msix_release_internal(pci_intr_handle_t *pihs, int count)
380 {
381           struct pic *pic;
382 
383           pic = msipic_find_msi_pic(MSI_INT_DEV(pihs[0]));
384           if (pic == NULL)
385                     return;
386 
387 #ifdef XENPV
388           xen_pci_msi_release(pic, count);
389 #endif
390           pci_msi_free_vectors(pic, pihs, count);
391           msipic_destruct_msix_pic(pic);
392 }
393 
394 /*****************************************************************************/
395 /*
396  * extern for MD code.
397  */
398 
399 /*
400  * Return intrid for an MSI/MSI-X device.
401  * "buf" must be allocated by caller.
402  */
403 const char *
x86_pci_msi_string(pci_chipset_tag_t pc,pci_intr_handle_t ih,char * buf,size_t len)404 x86_pci_msi_string(pci_chipset_tag_t pc, pci_intr_handle_t ih, char *buf,
405     size_t len)
406 {
407           int dev, vec;
408 
409           KASSERT(INT_VIA_MSI(ih));
410 
411           dev = MSI_INT_DEV(ih);
412           vec = MSI_INT_VEC(ih);
413           if (MSI_INT_IS_MSIX(ih))
414                     snprintf(buf, len, "msix%d vec %d", dev, vec);
415           else
416                     snprintf(buf, len, "msi%d vec %d", dev, vec);
417 
418           return buf;
419 }
420 
421 /*
422  * Release MSI handles.
423  */
424 void
x86_pci_msi_release(pci_chipset_tag_t pc,pci_intr_handle_t * pihs,int count)425 x86_pci_msi_release(pci_chipset_tag_t pc, pci_intr_handle_t *pihs, int count)
426 {
427 
428           if (count < 1)
429                     return;
430 
431           return x86_pci_msi_release_internal(pihs, count);
432 }
433 
434 /*
435  * Establish an MSI handle.
436  * If multiple MSI handles are required to be established, a device driver
437  * must call this function for each handle.
438  */
439 void *
x86_pci_msi_establish(pci_chipset_tag_t pc,pci_intr_handle_t ih,int level,int (* func)(void *),void * arg,const char * xname)440 x86_pci_msi_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih,
441     int level, int (*func)(void *), void *arg, const char *xname)
442 {
443           struct pic *pic;
444 
445           pic = msipic_find_msi_pic(MSI_INT_DEV(ih));
446           if (pic == NULL) {
447                     DPRINTF(("pci_intr_handler has no msi_pic\n"));
448                     return NULL;
449           }
450 
451           return pci_msi_common_establish(pc, ih, level, func, arg, pic, xname);
452 }
453 
454 /*
455  * Disestablish an MSI handle.
456  * If multiple MSI handles are required to be disestablished, a device driver
457  * must call this function for each handle.
458  */
459 void
x86_pci_msi_disestablish(pci_chipset_tag_t pc,void * cookie)460 x86_pci_msi_disestablish(pci_chipset_tag_t pc, void *cookie)
461 {
462 
463           pci_msi_common_disestablish(pc, cookie);
464 }
465 
466 /*
467  * Release MSI-X handles.
468  */
469 void
x86_pci_msix_release(pci_chipset_tag_t pc,pci_intr_handle_t * pihs,int count)470 x86_pci_msix_release(pci_chipset_tag_t pc, pci_intr_handle_t *pihs, int count)
471 {
472 
473           if (count < 1)
474                     return;
475 
476           return x86_pci_msix_release_internal(pihs, count);
477 }
478 
479 /*
480  * Establish an MSI-X handle.
481  * If multiple MSI-X handles are required to be established, a device driver
482  * must call this function for each handle.
483  */
484 void *
x86_pci_msix_establish(pci_chipset_tag_t pc,pci_intr_handle_t ih,int level,int (* func)(void *),void * arg,const char * xname)485 x86_pci_msix_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih,
486     int level, int (*func)(void *), void *arg, const char *xname)
487 {
488           struct pic *pic;
489 
490           pic = msipic_find_msi_pic(MSI_INT_DEV(ih));
491           if (pic == NULL) {
492                     DPRINTF(("pci_intr_handler has no msi_pic\n"));
493                     return NULL;
494           }
495 
496           return pci_msi_common_establish(pc, ih, level, func, arg, pic, xname);
497 }
498 
499 /*
500  * Disestablish an MSI-X handle.
501  * If multiple MSI-X handles are required to be disestablished, a device driver
502  * must call this function for each handle.
503  */
504 void
x86_pci_msix_disestablish(pci_chipset_tag_t pc,void * cookie)505 x86_pci_msix_disestablish(pci_chipset_tag_t pc, void *cookie)
506 {
507 
508           pci_msi_common_disestablish(pc, cookie);
509 }
510 
511 #ifdef __HAVE_PCI_MSI_MSIX
512 /*****************************************************************************/
513 /*
514  * extern for MI code.
515  */
516 
517 /*
518  * This function is used by device drivers like pci_intr_map().
519  *
520  * "ihps" is the array of vector numbers which MSI used instead of IRQ number.
521  * "count" must be power of 2.
522  * "count" can decrease if struct intrsource cannot be allocated.
523  * if count == 0, return non-zero value.
524  */
525 int
pci_msi_alloc(const struct pci_attach_args * pa,pci_intr_handle_t ** ihps,int * count)526 pci_msi_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihps,
527     int *count)
528 {
529           int hw_max;
530 
531           /* MSI vector count must be power of 2. */
532           KASSERT(*count > 0);
533           KASSERT(((*count - 1) & *count) == 0);
534 
535           hw_max = pci_msi_count(pa->pa_pc, pa->pa_tag);
536           if (hw_max == 0)
537                     return ENODEV;
538 
539           if (*count > hw_max) {
540                     DPRINTF(("cut off MSI count to %d\n", hw_max));
541                     *count = hw_max; /* cut off hw_max */
542           }
543 
544           return x86_pci_msi_alloc(ihps, count, pa);
545 }
546 
547 /*
548  * This function is used by device drivers like pci_intr_map().
549  *
550  * "ihps" is the array  of vector numbers which MSI used instead of IRQ number.
551  * "count" must be power of 2.
552  * "count" can not decrease.
553  * If "count" struct intrsources cannot be allocated, return non-zero value.
554  */
555 int
pci_msi_alloc_exact(const struct pci_attach_args * pa,pci_intr_handle_t ** ihps,int count)556 pci_msi_alloc_exact(const struct pci_attach_args *pa, pci_intr_handle_t **ihps,
557     int count)
558 {
559           int hw_max;
560 
561           /* MSI vector count must be power of 2. */
562           KASSERT(count > 0);
563           KASSERT(((count - 1) & count) == 0);
564 
565           hw_max = pci_msi_count(pa->pa_pc, pa->pa_tag);
566           if (hw_max == 0)
567                     return ENODEV;
568 
569           if (count > hw_max) {
570                     DPRINTF(("over hardware max MSI count %d\n", hw_max));
571                     return EINVAL;
572           }
573 
574           return x86_pci_msi_alloc_exact(ihps, count, pa);
575 }
576 
577 /*
578  * This function is used by device drivers like pci_intr_map().
579  *
580  * "ihps" is the array of vector numbers which MSI-X used instead of IRQ number.
581  * "count" can decrease if enough struct intrsources cannot be allocated.
582  * if count == 0, return non-zero value.
583  */
584 int
pci_msix_alloc(const struct pci_attach_args * pa,pci_intr_handle_t ** ihps,int * count)585 pci_msix_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihps,
586     int *count)
587 {
588           int hw_max;
589 
590           KASSERT(*count > 0);
591 
592           hw_max = pci_msix_count(pa->pa_pc, pa->pa_tag);
593           if (hw_max == 0)
594                     return ENODEV;
595 
596           if (*count > hw_max) {
597                     DPRINTF(("cut off MSI-X count to %d\n", hw_max));
598                     *count = hw_max; /* cut off hw_max */
599           }
600 
601           return x86_pci_msix_alloc(ihps, count, pa);
602 }
603 
604 /*
605  * This function is used by device drivers like pci_intr_map().
606  *
607  * "ihps" is the array of vector numbers which MSI-X used instead of IRQ number.
608  * "count" can not decrease.
609  * If "count" struct intrsource cannot be allocated, return non-zero value.
610  */
611 int
pci_msix_alloc_exact(const struct pci_attach_args * pa,pci_intr_handle_t ** ihps,int count)612 pci_msix_alloc_exact(const struct pci_attach_args *pa, pci_intr_handle_t **ihps,
613     int count)
614 {
615           int hw_max;
616 
617           KASSERT(count > 0);
618 
619           hw_max = pci_msix_count(pa->pa_pc, pa->pa_tag);
620           if (hw_max == 0)
621                     return ENODEV;
622 
623           if (count > hw_max) {
624                     DPRINTF(("over hardware max MSI-X count %d\n", hw_max));
625                     return EINVAL;
626           }
627 
628           return x86_pci_msix_alloc_exact(ihps, count, pa);
629 }
630 
631 /*
632  * This function is used by device drivers like pci_intr_map().
633  * Furthermore, this function can map each handle to an MSI-X table index.
634  *
635  * "ihps" is the array of vector numbers which MSI-X used instead of IRQ number.
636  * "count" can not decrease.
637  * "map" size must be equal to "count".
638  * If "count" struct intrsource cannot be allocated, return non-zero value.
639  * e.g.
640  *     If "map" = { 1, 4, 0 },
641  *     1st handle is bound to MSI-X index 1
642  *     2nd handle is bound to MSI-X index 4
643  *     3rd handle is bound to MSI-X index 0
644  */
645 int
pci_msix_alloc_map(const struct pci_attach_args * pa,pci_intr_handle_t ** ihps,u_int * table_indexes,int count)646 pci_msix_alloc_map(const struct pci_attach_args *pa, pci_intr_handle_t **ihps,
647     u_int *table_indexes, int count)
648 {
649           int hw_max, i, j;
650 
651           KASSERT(count > 0);
652 
653           hw_max = pci_msix_count(pa->pa_pc, pa->pa_tag);
654           if (hw_max == 0)
655                     return ENODEV;
656 
657           if (count > hw_max) {
658                     DPRINTF(("over hardware max MSI-X count %d\n", hw_max));
659                     return EINVAL;
660           }
661 
662           /* check not to duplicate table_index */
663           for (i = 0; i < count; i++) {
664                     u_int basing = table_indexes[i];
665 
666                     KASSERT(table_indexes[i] < PCI_MSIX_MAX_VECTORS);
667                     if (basing >= hw_max) {
668                               DPRINTF(("table index is over hardware max MSI-X index %d\n",
669                                         hw_max - 1));
670                               return EINVAL;
671                     }
672 
673                     for (j = i + 1; j < count; j++) {
674                               if (basing == table_indexes[j]) {
675                                         DPRINTF(("MSI-X table index duplicated\n"));
676                                         return EINVAL;
677                               }
678                     }
679           }
680 
681           return x86_pci_msix_alloc_map(ihps, table_indexes, count, pa);
682 }
683 #endif /* __HAVE_PCI_MSI_MSIX */
684