msi.c revision 340016
1/*-
2 * Copyright (c) 2006 Yahoo!, Inc.
3 * All rights reserved.
4 * Written by: John Baldwin <jhb@FreeBSD.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the names of any co-contributors
15 *    may be used to endorse or promote products derived from this software
16 *    without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31/*
32 * Support for PCI Message Signalled Interrupts (MSI).  MSI interrupts on
33 * x86 are basically APIC messages that the northbridge delivers directly
34 * to the local APICs as if they had come from an I/O APIC.
35 */
36
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD: stable/11/sys/x86/x86/msi.c 340016 2018-11-01 18:34:26Z jhb $");
39
40#include "opt_acpi.h"
41
42#include <sys/param.h>
43#include <sys/bus.h>
44#include <sys/kernel.h>
45#include <sys/lock.h>
46#include <sys/malloc.h>
47#include <sys/mutex.h>
48#include <sys/sx.h>
49#include <sys/sysctl.h>
50#include <sys/systm.h>
51#include <x86/apicreg.h>
52#include <machine/cputypes.h>
53#include <machine/md_var.h>
54#include <machine/frame.h>
55#include <machine/intr_machdep.h>
56#include <x86/apicvar.h>
57#include <x86/iommu/iommu_intrmap.h>
58#include <machine/specialreg.h>
59#include <dev/pci/pcivar.h>
60
61/* Fields in address for Intel MSI messages. */
62#define	MSI_INTEL_ADDR_DEST		0x000ff000
63#define	MSI_INTEL_ADDR_RH		0x00000008
64# define MSI_INTEL_ADDR_RH_ON		0x00000008
65# define MSI_INTEL_ADDR_RH_OFF		0x00000000
66#define	MSI_INTEL_ADDR_DM		0x00000004
67# define MSI_INTEL_ADDR_DM_PHYSICAL	0x00000000
68# define MSI_INTEL_ADDR_DM_LOGICAL	0x00000004
69
70/* Fields in data for Intel MSI messages. */
71#define	MSI_INTEL_DATA_TRGRMOD		IOART_TRGRMOD	/* Trigger mode. */
72# define MSI_INTEL_DATA_TRGREDG		IOART_TRGREDG
73# define MSI_INTEL_DATA_TRGRLVL		IOART_TRGRLVL
74#define	MSI_INTEL_DATA_LEVEL		0x00004000	/* Polarity. */
75# define MSI_INTEL_DATA_DEASSERT	0x00000000
76# define MSI_INTEL_DATA_ASSERT		0x00004000
77#define	MSI_INTEL_DATA_DELMOD		IOART_DELMOD	/* Delivery mode. */
78# define MSI_INTEL_DATA_DELFIXED	IOART_DELFIXED
79# define MSI_INTEL_DATA_DELLOPRI	IOART_DELLOPRI
80# define MSI_INTEL_DATA_DELSMI		IOART_DELSMI
81# define MSI_INTEL_DATA_DELNMI		IOART_DELNMI
82# define MSI_INTEL_DATA_DELINIT		IOART_DELINIT
83# define MSI_INTEL_DATA_DELEXINT	IOART_DELEXINT
84#define	MSI_INTEL_DATA_INTVEC		IOART_INTVEC	/* Interrupt vector. */
85
86/*
87 * Build Intel MSI message and data values from a source.  AMD64 systems
88 * seem to be compatible, so we use the same function for both.
89 */
90#define	INTEL_ADDR(msi)							\
91	(MSI_INTEL_ADDR_BASE | (msi)->msi_cpu << 12 |			\
92	    MSI_INTEL_ADDR_RH_OFF | MSI_INTEL_ADDR_DM_PHYSICAL)
93#define	INTEL_DATA(msi)							\
94	(MSI_INTEL_DATA_TRGREDG | MSI_INTEL_DATA_DELFIXED | (msi)->msi_vector)
95
96static MALLOC_DEFINE(M_MSI, "msi", "PCI MSI");
97
98/*
99 * MSI sources are bunched into groups.  This is because MSI forces
100 * all of the messages to share the address and data registers and
101 * thus certain properties (such as the local APIC ID target on x86).
102 * Each group has a 'first' source that contains information global to
103 * the group.  These fields are marked with (g) below.
104 *
105 * Note that local APIC ID is kind of special.  Each message will be
106 * assigned an ID by the system; however, a group will use the ID from
107 * the first message.
108 *
109 * For MSI-X, each message is isolated.
110 */
111struct msi_intsrc {
112	struct intsrc msi_intsrc;
113	device_t msi_dev;		/* Owning device. (g) */
114	struct msi_intsrc *msi_first;	/* First source in group. */
115	u_int msi_irq;			/* IRQ cookie. */
116	u_int msi_msix;			/* MSI-X message. */
117	u_int msi_vector:8;		/* IDT vector. */
118	u_int msi_cpu;			/* Local APIC ID. (g) */
119	u_int msi_count:8;		/* Messages in this group. (g) */
120	u_int msi_maxcount:8;		/* Alignment for this group. (g) */
121	u_int *msi_irqs;		/* Group's IRQ list. (g) */
122	u_int msi_remap_cookie;
123};
124
125static void	msi_create_source(void);
126static void	msi_enable_source(struct intsrc *isrc);
127static void	msi_disable_source(struct intsrc *isrc, int eoi);
128static void	msi_eoi_source(struct intsrc *isrc);
129static void	msi_enable_intr(struct intsrc *isrc);
130static void	msi_disable_intr(struct intsrc *isrc);
131static int	msi_vector(struct intsrc *isrc);
132static int	msi_source_pending(struct intsrc *isrc);
133static int	msi_config_intr(struct intsrc *isrc, enum intr_trigger trig,
134		    enum intr_polarity pol);
135static int	msi_assign_cpu(struct intsrc *isrc, u_int apic_id);
136
137struct pic msi_pic = {
138	.pic_enable_source = msi_enable_source,
139	.pic_disable_source = msi_disable_source,
140	.pic_eoi_source = msi_eoi_source,
141	.pic_enable_intr = msi_enable_intr,
142	.pic_disable_intr = msi_disable_intr,
143	.pic_vector = msi_vector,
144	.pic_source_pending = msi_source_pending,
145	.pic_suspend = NULL,
146	.pic_resume = NULL,
147	.pic_config_intr = msi_config_intr,
148	.pic_assign_cpu = msi_assign_cpu,
149	.pic_reprogram_pin = NULL,
150};
151
152u_int first_msi_irq;
153
154#ifdef SMP
155/**
156 * Xen hypervisors prior to 4.6.0 do not properly handle updates to
157 * enabled MSI-X table entries.  Allow migration of MSI-X interrupts
158 * to be disabled via a tunable. Values have the following meaning:
159 *
160 * -1: automatic detection by FreeBSD
161 *  0: enable migration
162 *  1: disable migration
163 */
164int msix_disable_migration = -1;
165SYSCTL_INT(_machdep, OID_AUTO, disable_msix_migration, CTLFLAG_RDTUN,
166    &msix_disable_migration, 0,
167    "Disable migration of MSI-X interrupts between CPUs");
168#endif
169
170static int msi_enabled;
171static u_int msi_last_irq;
172static struct mtx msi_lock;
173
174static void
175msi_enable_source(struct intsrc *isrc)
176{
177}
178
179static void
180msi_disable_source(struct intsrc *isrc, int eoi)
181{
182
183	if (eoi == PIC_EOI)
184		lapic_eoi();
185}
186
187static void
188msi_eoi_source(struct intsrc *isrc)
189{
190
191	lapic_eoi();
192}
193
194static void
195msi_enable_intr(struct intsrc *isrc)
196{
197	struct msi_intsrc *msi = (struct msi_intsrc *)isrc;
198
199	apic_enable_vector(msi->msi_cpu, msi->msi_vector);
200}
201
202static void
203msi_disable_intr(struct intsrc *isrc)
204{
205	struct msi_intsrc *msi = (struct msi_intsrc *)isrc;
206
207	apic_disable_vector(msi->msi_cpu, msi->msi_vector);
208}
209
210static int
211msi_vector(struct intsrc *isrc)
212{
213	struct msi_intsrc *msi = (struct msi_intsrc *)isrc;
214
215	return (msi->msi_irq);
216}
217
218static int
219msi_source_pending(struct intsrc *isrc)
220{
221
222	return (0);
223}
224
225static int
226msi_config_intr(struct intsrc *isrc, enum intr_trigger trig,
227    enum intr_polarity pol)
228{
229
230	return (ENODEV);
231}
232
233static int
234msi_assign_cpu(struct intsrc *isrc, u_int apic_id)
235{
236	struct msi_intsrc *sib, *msi = (struct msi_intsrc *)isrc;
237	int old_vector;
238	u_int old_id;
239	int i, vector;
240
241	/*
242	 * Only allow CPUs to be assigned to the first message for an
243	 * MSI group.
244	 */
245	if (msi->msi_first != msi)
246		return (EINVAL);
247
248#ifdef SMP
249	if (msix_disable_migration && msi->msi_msix)
250		return (EINVAL);
251#endif
252
253	/* Store information to free existing irq. */
254	old_vector = msi->msi_vector;
255	old_id = msi->msi_cpu;
256	if (old_id == apic_id)
257		return (0);
258
259	/* Allocate IDT vectors on this cpu. */
260	if (msi->msi_count > 1) {
261		KASSERT(msi->msi_msix == 0, ("MSI-X message group"));
262		vector = apic_alloc_vectors(apic_id, msi->msi_irqs,
263		    msi->msi_count, msi->msi_maxcount);
264	} else
265		vector = apic_alloc_vector(apic_id, msi->msi_irq);
266	if (vector == 0)
267		return (ENOSPC);
268
269	msi->msi_cpu = apic_id;
270	msi->msi_vector = vector;
271	if (msi->msi_intsrc.is_handlers > 0)
272		apic_enable_vector(msi->msi_cpu, msi->msi_vector);
273	if (bootverbose)
274		printf("msi: Assigning %s IRQ %d to local APIC %u vector %u\n",
275		    msi->msi_msix ? "MSI-X" : "MSI", msi->msi_irq,
276		    msi->msi_cpu, msi->msi_vector);
277	for (i = 1; i < msi->msi_count; i++) {
278		sib = (struct msi_intsrc *)intr_lookup_source(msi->msi_irqs[i]);
279		sib->msi_cpu = apic_id;
280		sib->msi_vector = vector + i;
281		if (sib->msi_intsrc.is_handlers > 0)
282			apic_enable_vector(sib->msi_cpu, sib->msi_vector);
283		if (bootverbose)
284			printf(
285		    "msi: Assigning MSI IRQ %d to local APIC %u vector %u\n",
286			    sib->msi_irq, sib->msi_cpu, sib->msi_vector);
287	}
288	BUS_REMAP_INTR(device_get_parent(msi->msi_dev), msi->msi_dev,
289	    msi->msi_irq);
290
291	/*
292	 * Free the old vector after the new one is established.  This is done
293	 * to prevent races where we could miss an interrupt.
294	 */
295	if (msi->msi_intsrc.is_handlers > 0)
296		apic_disable_vector(old_id, old_vector);
297	apic_free_vector(old_id, old_vector, msi->msi_irq);
298	for (i = 1; i < msi->msi_count; i++) {
299		sib = (struct msi_intsrc *)intr_lookup_source(msi->msi_irqs[i]);
300		if (sib->msi_intsrc.is_handlers > 0)
301			apic_disable_vector(old_id, old_vector + i);
302		apic_free_vector(old_id, old_vector + i, msi->msi_irqs[i]);
303	}
304	return (0);
305}
306
307void
308msi_init(void)
309{
310
311	/* Check if we have a supported CPU. */
312	switch (cpu_vendor_id) {
313	case CPU_VENDOR_INTEL:
314	case CPU_VENDOR_AMD:
315		break;
316	case CPU_VENDOR_CENTAUR:
317		if (CPUID_TO_FAMILY(cpu_id) == 0x6 &&
318		    CPUID_TO_MODEL(cpu_id) >= 0xf)
319			break;
320		/* FALLTHROUGH */
321	default:
322		return;
323	}
324
325#ifdef SMP
326	if (msix_disable_migration == -1) {
327		/* The default is to allow migration of MSI-X interrupts. */
328		msix_disable_migration = 0;
329	}
330#endif
331
332	first_msi_irq = max(MINIMUM_MSI_INT, num_io_irqs);
333	num_io_irqs = first_msi_irq + NUM_MSI_INTS;
334
335	msi_enabled = 1;
336	intr_register_pic(&msi_pic);
337	mtx_init(&msi_lock, "msi", NULL, MTX_DEF);
338}
339
340static void
341msi_create_source(void)
342{
343	struct msi_intsrc *msi;
344	u_int irq;
345
346	mtx_lock(&msi_lock);
347	if (msi_last_irq >= NUM_MSI_INTS) {
348		mtx_unlock(&msi_lock);
349		return;
350	}
351	irq = msi_last_irq + first_msi_irq;
352	msi_last_irq++;
353	mtx_unlock(&msi_lock);
354
355	msi = malloc(sizeof(struct msi_intsrc), M_MSI, M_WAITOK | M_ZERO);
356	msi->msi_intsrc.is_pic = &msi_pic;
357	msi->msi_irq = irq;
358	intr_register_source(&msi->msi_intsrc);
359	nexus_add_irq(irq);
360}
361
362/*
363 * Try to allocate 'count' interrupt sources with contiguous IDT values.
364 */
365int
366msi_alloc(device_t dev, int count, int maxcount, int *irqs)
367{
368	struct msi_intsrc *msi, *fsrc;
369	u_int cpu, *mirqs;
370	int cnt, i, vector;
371#ifdef ACPI_DMAR
372	u_int cookies[count];
373	int error;
374#endif
375
376	if (!msi_enabled)
377		return (ENXIO);
378
379	if (count > 1)
380		mirqs = malloc(count * sizeof(*mirqs), M_MSI, M_WAITOK);
381	else
382		mirqs = NULL;
383again:
384	mtx_lock(&msi_lock);
385
386	/* Try to find 'count' free IRQs. */
387	cnt = 0;
388	for (i = first_msi_irq; i < first_msi_irq + NUM_MSI_INTS; i++) {
389		msi = (struct msi_intsrc *)intr_lookup_source(i);
390
391		/* End of allocated sources, so break. */
392		if (msi == NULL)
393			break;
394
395		/* If this is a free one, save its IRQ in the array. */
396		if (msi->msi_dev == NULL) {
397			irqs[cnt] = i;
398			cnt++;
399			if (cnt == count)
400				break;
401		}
402	}
403
404	/* Do we need to create some new sources? */
405	if (cnt < count) {
406		/* If we would exceed the max, give up. */
407		if (i + (count - cnt) >= first_msi_irq + NUM_MSI_INTS) {
408			mtx_unlock(&msi_lock);
409			free(mirqs, M_MSI);
410			return (ENXIO);
411		}
412		mtx_unlock(&msi_lock);
413
414		/* We need count - cnt more sources. */
415		while (cnt < count) {
416			msi_create_source();
417			cnt++;
418		}
419		goto again;
420	}
421
422	/* Ok, we now have the IRQs allocated. */
423	KASSERT(cnt == count, ("count mismatch"));
424
425	/* Allocate 'count' IDT vectors. */
426	cpu = intr_next_cpu();
427	vector = apic_alloc_vectors(cpu, irqs, count, maxcount);
428	if (vector == 0) {
429		mtx_unlock(&msi_lock);
430		free(mirqs, M_MSI);
431		return (ENOSPC);
432	}
433
434#ifdef ACPI_DMAR
435	mtx_unlock(&msi_lock);
436	error = iommu_alloc_msi_intr(dev, cookies, count);
437	mtx_lock(&msi_lock);
438	if (error == EOPNOTSUPP)
439		error = 0;
440	if (error != 0) {
441		for (i = 0; i < count; i++)
442			apic_free_vector(cpu, vector + i, irqs[i]);
443		free(mirqs, M_MSI);
444		return (error);
445	}
446	for (i = 0; i < count; i++) {
447		msi = (struct msi_intsrc *)intr_lookup_source(irqs[i]);
448		msi->msi_remap_cookie = cookies[i];
449	}
450#endif
451
452	/* Assign IDT vectors and make these messages owned by 'dev'. */
453	fsrc = (struct msi_intsrc *)intr_lookup_source(irqs[0]);
454	for (i = 0; i < count; i++) {
455		msi = (struct msi_intsrc *)intr_lookup_source(irqs[i]);
456		msi->msi_cpu = cpu;
457		msi->msi_dev = dev;
458		msi->msi_vector = vector + i;
459		if (bootverbose)
460			printf(
461		    "msi: routing MSI IRQ %d to local APIC %u vector %u\n",
462			    msi->msi_irq, msi->msi_cpu, msi->msi_vector);
463		msi->msi_first = fsrc;
464		KASSERT(msi->msi_intsrc.is_handlers == 0,
465		    ("dead MSI has handlers"));
466	}
467	fsrc->msi_count = count;
468	fsrc->msi_maxcount = maxcount;
469	if (count > 1)
470		bcopy(irqs, mirqs, count * sizeof(*mirqs));
471	fsrc->msi_irqs = mirqs;
472	mtx_unlock(&msi_lock);
473	return (0);
474}
475
476int
477msi_release(int *irqs, int count)
478{
479	struct msi_intsrc *msi, *first;
480	int i;
481
482	mtx_lock(&msi_lock);
483	first = (struct msi_intsrc *)intr_lookup_source(irqs[0]);
484	if (first == NULL) {
485		mtx_unlock(&msi_lock);
486		return (ENOENT);
487	}
488
489	/* Make sure this isn't an MSI-X message. */
490	if (first->msi_msix) {
491		mtx_unlock(&msi_lock);
492		return (EINVAL);
493	}
494
495	/* Make sure this message is allocated to a group. */
496	if (first->msi_first == NULL) {
497		mtx_unlock(&msi_lock);
498		return (ENXIO);
499	}
500
501	/*
502	 * Make sure this is the start of a group and that we are releasing
503	 * the entire group.
504	 */
505	if (first->msi_first != first || first->msi_count != count) {
506		mtx_unlock(&msi_lock);
507		return (EINVAL);
508	}
509	KASSERT(first->msi_dev != NULL, ("unowned group"));
510
511	/* Clear all the extra messages in the group. */
512	for (i = 1; i < count; i++) {
513		msi = (struct msi_intsrc *)intr_lookup_source(irqs[i]);
514		KASSERT(msi->msi_first == first, ("message not in group"));
515		KASSERT(msi->msi_dev == first->msi_dev, ("owner mismatch"));
516#ifdef ACPI_DMAR
517		iommu_unmap_msi_intr(first->msi_dev, msi->msi_remap_cookie);
518#endif
519		msi->msi_first = NULL;
520		msi->msi_dev = NULL;
521		apic_free_vector(msi->msi_cpu, msi->msi_vector, msi->msi_irq);
522		msi->msi_vector = 0;
523	}
524
525	/* Clear out the first message. */
526#ifdef ACPI_DMAR
527	mtx_unlock(&msi_lock);
528	iommu_unmap_msi_intr(first->msi_dev, first->msi_remap_cookie);
529	mtx_lock(&msi_lock);
530#endif
531	first->msi_first = NULL;
532	first->msi_dev = NULL;
533	apic_free_vector(first->msi_cpu, first->msi_vector, first->msi_irq);
534	first->msi_vector = 0;
535	first->msi_count = 0;
536	first->msi_maxcount = 0;
537	free(first->msi_irqs, M_MSI);
538	first->msi_irqs = NULL;
539
540	mtx_unlock(&msi_lock);
541	return (0);
542}
543
544int
545msi_map(int irq, uint64_t *addr, uint32_t *data)
546{
547	struct msi_intsrc *msi;
548	int error;
549#ifdef ACPI_DMAR
550	struct msi_intsrc *msi1;
551	int i, k;
552#endif
553
554	mtx_lock(&msi_lock);
555	msi = (struct msi_intsrc *)intr_lookup_source(irq);
556	if (msi == NULL) {
557		mtx_unlock(&msi_lock);
558		return (ENOENT);
559	}
560
561	/* Make sure this message is allocated to a device. */
562	if (msi->msi_dev == NULL) {
563		mtx_unlock(&msi_lock);
564		return (ENXIO);
565	}
566
567	/*
568	 * If this message isn't an MSI-X message, make sure it's part
569	 * of a group, and switch to the first message in the
570	 * group.
571	 */
572	if (!msi->msi_msix) {
573		if (msi->msi_first == NULL) {
574			mtx_unlock(&msi_lock);
575			return (ENXIO);
576		}
577		msi = msi->msi_first;
578	}
579
580#ifdef ACPI_DMAR
581	if (!msi->msi_msix) {
582		for (k = msi->msi_count - 1, i = first_msi_irq; k > 0 &&
583		    i < first_msi_irq + NUM_MSI_INTS; i++) {
584			if (i == msi->msi_irq)
585				continue;
586			msi1 = (struct msi_intsrc *)intr_lookup_source(i);
587			if (!msi1->msi_msix && msi1->msi_first == msi) {
588				mtx_unlock(&msi_lock);
589				iommu_map_msi_intr(msi1->msi_dev,
590				    msi1->msi_cpu, msi1->msi_vector,
591				    msi1->msi_remap_cookie, NULL, NULL);
592				k--;
593				mtx_lock(&msi_lock);
594			}
595		}
596	}
597	mtx_unlock(&msi_lock);
598	error = iommu_map_msi_intr(msi->msi_dev, msi->msi_cpu,
599	    msi->msi_vector, msi->msi_remap_cookie, addr, data);
600#else
601	mtx_unlock(&msi_lock);
602	error = EOPNOTSUPP;
603#endif
604	if (error == EOPNOTSUPP) {
605		*addr = INTEL_ADDR(msi);
606		*data = INTEL_DATA(msi);
607		error = 0;
608	}
609	return (error);
610}
611
612int
613msix_alloc(device_t dev, int *irq)
614{
615	struct msi_intsrc *msi;
616	u_int cpu;
617	int i, vector;
618#ifdef ACPI_DMAR
619	u_int cookie;
620	int error;
621#endif
622
623	if (!msi_enabled)
624		return (ENXIO);
625
626again:
627	mtx_lock(&msi_lock);
628
629	/* Find a free IRQ. */
630	for (i = first_msi_irq; i < first_msi_irq + NUM_MSI_INTS; i++) {
631		msi = (struct msi_intsrc *)intr_lookup_source(i);
632
633		/* End of allocated sources, so break. */
634		if (msi == NULL)
635			break;
636
637		/* Stop at the first free source. */
638		if (msi->msi_dev == NULL)
639			break;
640	}
641
642	/* Do we need to create a new source? */
643	if (msi == NULL) {
644		/* If we would exceed the max, give up. */
645		if (i + 1 >= first_msi_irq + NUM_MSI_INTS) {
646			mtx_unlock(&msi_lock);
647			return (ENXIO);
648		}
649		mtx_unlock(&msi_lock);
650
651		/* Create a new source. */
652		msi_create_source();
653		goto again;
654	}
655
656	/* Allocate an IDT vector. */
657	cpu = intr_next_cpu();
658	vector = apic_alloc_vector(cpu, i);
659	if (vector == 0) {
660		mtx_unlock(&msi_lock);
661		return (ENOSPC);
662	}
663
664	msi->msi_dev = dev;
665#ifdef ACPI_DMAR
666	mtx_unlock(&msi_lock);
667	error = iommu_alloc_msi_intr(dev, &cookie, 1);
668	mtx_lock(&msi_lock);
669	if (error == EOPNOTSUPP)
670		error = 0;
671	if (error != 0) {
672		msi->msi_dev = NULL;
673		apic_free_vector(cpu, vector, i);
674		return (error);
675	}
676	msi->msi_remap_cookie = cookie;
677#endif
678
679	if (bootverbose)
680		printf("msi: routing MSI-X IRQ %d to local APIC %u vector %u\n",
681		    msi->msi_irq, cpu, vector);
682
683	/* Setup source. */
684	msi->msi_cpu = cpu;
685	msi->msi_first = msi;
686	msi->msi_vector = vector;
687	msi->msi_msix = 1;
688	msi->msi_count = 1;
689	msi->msi_maxcount = 1;
690	msi->msi_irqs = NULL;
691
692	KASSERT(msi->msi_intsrc.is_handlers == 0, ("dead MSI-X has handlers"));
693	mtx_unlock(&msi_lock);
694
695	*irq = i;
696	return (0);
697}
698
699int
700msix_release(int irq)
701{
702	struct msi_intsrc *msi;
703
704	mtx_lock(&msi_lock);
705	msi = (struct msi_intsrc *)intr_lookup_source(irq);
706	if (msi == NULL) {
707		mtx_unlock(&msi_lock);
708		return (ENOENT);
709	}
710
711	/* Make sure this is an MSI-X message. */
712	if (!msi->msi_msix) {
713		mtx_unlock(&msi_lock);
714		return (EINVAL);
715	}
716
717	KASSERT(msi->msi_dev != NULL, ("unowned message"));
718
719	/* Clear out the message. */
720#ifdef ACPI_DMAR
721	mtx_unlock(&msi_lock);
722	iommu_unmap_msi_intr(msi->msi_dev, msi->msi_remap_cookie);
723	mtx_lock(&msi_lock);
724#endif
725	msi->msi_first = NULL;
726	msi->msi_dev = NULL;
727	apic_free_vector(msi->msi_cpu, msi->msi_vector, msi->msi_irq);
728	msi->msi_vector = 0;
729	msi->msi_msix = 0;
730	msi->msi_count = 0;
731	msi->msi_maxcount = 0;
732
733	mtx_unlock(&msi_lock);
734	return (0);
735}
736