Browse Source

[pci] Add support for PCI MSI-X interrupts

The Intel 40 Gigabit Ethernet virtual functions support only MSI-X
interrupts, and will write back completed interrupt descriptors only
when the device attempts to raise an interrupt (or when a complete
cacheline of receive descriptors has been completed).

We cannot actually use MSI-X interrupts within iPXE, since we never
have ownership of the APIC.  However, an MSI-X interrupt is
fundamentally just a DMA write of a single dword to an arbitrary
address.  We can therefore configure the device to "raise" an
interrupt by writing a meaningless value to an otherwise unused memory
location: this is sufficient to trigger the receive descriptor
writeback logic.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 5 years ago
parent
commit
afee77d816
4 changed files with 340 additions and 0 deletions
  1. 251
    0
      src/drivers/bus/pcimsix.c
  2. 1
    0
      src/include/ipxe/errfile.h
  3. 11
    0
      src/include/ipxe/pci.h
  4. 77
    0
      src/include/ipxe/pcimsix.h

+ 251
- 0
src/drivers/bus/pcimsix.c View File

@@ -0,0 +1,251 @@
1
+/*
2
+ * Copyright (C) 2019 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or (at your option) any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ * 02110-1301, USA.
18
+ *
19
+ * You can also choose to distribute this program under the terms of
20
+ * the Unmodified Binary Distribution Licence (as given in the file
21
+ * COPYING.UBDL), provided that you have satisfied its requirements.
22
+ */
23
+
24
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
+
26
+#include <stdint.h>
27
+#include <errno.h>
28
+#include <assert.h>
29
+#include <ipxe/pci.h>
30
+#include <ipxe/pcimsix.h>
31
+
32
+/** @file
33
+ *
34
+ * PCI MSI-X interrupts
35
+ *
36
+ */
37
+
38
+/**
39
+ * Get MSI-X descriptor name (for debugging)
40
+ *
41
+ * @v cfg		Configuration space offset
42
+ * @ret name		Descriptor name
43
+ */
44
+static const char * pci_msix_name ( unsigned int cfg ) {
45
+
46
+	switch ( cfg ) {
47
+	case PCI_MSIX_DESC_TABLE:	return "table";
48
+	case PCI_MSIX_DESC_PBA:		return "PBA";
49
+	default:			return "<UNKNOWN>";
50
+	}
51
+}
52
+
53
+/**
54
+ * Map MSI-X BAR portion
55
+ *
56
+ * @v pci		PCI device
57
+ * @v msix		MSI-X capability
58
+ * @v cfg		Configuration space offset
59
+ * @ret io		I/O address
60
+ */
61
+static void * pci_msix_ioremap ( struct pci_device *pci, struct pci_msix *msix,
62
+				 unsigned int cfg ) {
63
+	uint32_t desc;
64
+	unsigned int bar;
65
+	unsigned long start;
66
+	unsigned long offset;
67
+	unsigned long base;
68
+	void *io;
69
+
70
+	/* Read descriptor */
71
+	pci_read_config_dword ( pci, ( msix->cap + cfg ), &desc );
72
+
73
+	/* Get BAR */
74
+	bar = PCI_MSIX_DESC_BIR ( desc );
75
+	offset = PCI_MSIX_DESC_OFFSET ( desc );
76
+	start = pci_bar_start ( pci, PCI_BASE_ADDRESS ( bar ) );
77
+	if ( ! start ) {
78
+		DBGC ( msix, "MSI-X %p %s could not find BAR%d\n",
79
+		       msix, pci_msix_name ( cfg ), bar );
80
+		return NULL;
81
+	}
82
+	base = ( start + offset );
83
+	DBGC ( msix, "MSI-X %p %s at %#08lx (BAR%d+%#lx)\n",
84
+	       msix, pci_msix_name ( cfg ), base, bar, offset );
85
+
86
+	/* Map BAR portion */
87
+	io = ioremap ( ( start + offset ), PCI_MSIX_LEN );
88
+	if ( ! io ) {
89
+		DBGC ( msix, "MSI-X %p %s could not map %#08lx\n",
90
+		       msix, pci_msix_name ( cfg ), base );
91
+		return NULL;
92
+	}
93
+
94
+	return io;
95
+}
96
+
97
+/**
98
+ * Enable MSI-X interrupts
99
+ *
100
+ * @v pci		PCI device
101
+ * @v msix		MSI-X capability
102
+ * @ret rc		Return status code
103
+ */
104
+int pci_msix_enable ( struct pci_device *pci, struct pci_msix *msix ) {
105
+	uint16_t ctrl;
106
+	int rc;
107
+
108
+	/* Locate capability */
109
+	msix->cap = pci_find_capability ( pci, PCI_CAP_ID_MSIX );
110
+	if ( ! msix->cap ) {
111
+		DBGC ( msix, "MSI-X %p found no MSI-X capability in "
112
+		       PCI_FMT "\n", msix, PCI_ARGS ( pci ) );
113
+		rc = -ENOENT;
114
+		goto err_cap;
115
+	}
116
+
117
+	/* Extract interrupt count */
118
+	pci_read_config_word ( pci, ( msix->cap + PCI_MSIX_CTRL ), &ctrl );
119
+	msix->count = ( PCI_MSIX_CTRL_SIZE ( ctrl ) + 1 );
120
+	DBGC ( msix, "MSI-X %p has %d vectors for " PCI_FMT "\n",
121
+	       msix, msix->count, PCI_ARGS ( pci ) );
122
+
123
+	/* Map MSI-X table */
124
+	msix->table = pci_msix_ioremap ( pci, msix, PCI_MSIX_DESC_TABLE );
125
+	if ( ! msix->table ) {
126
+		rc = -ENOENT;
127
+		goto err_table;
128
+	}
129
+
130
+	/* Map pending bit array */
131
+	msix->pba = pci_msix_ioremap ( pci, msix, PCI_MSIX_DESC_PBA );
132
+	if ( ! msix->pba ) {
133
+		rc = -ENOENT;
134
+		goto err_pba;
135
+	}
136
+
137
+	/* Enable MSI-X */
138
+	ctrl &= ~PCI_MSIX_CTRL_MASK;
139
+	ctrl |= PCI_MSIX_CTRL_ENABLE;
140
+	pci_write_config_word ( pci, ( msix->cap + PCI_MSIX_CTRL ), ctrl );
141
+
142
+	return 0;
143
+
144
+	iounmap ( msix->pba );
145
+ err_pba:
146
+	iounmap ( msix->table );
147
+ err_table:
148
+ err_cap:
149
+	return rc;
150
+}
151
+
152
+/**
153
+ * Disable MSI-X interrupts
154
+ *
155
+ * @v pci		PCI device
156
+ * @v msix		MSI-X capability
157
+ */
158
+void pci_msix_disable ( struct pci_device *pci, struct pci_msix *msix ) {
159
+	uint16_t ctrl;
160
+
161
+	/* Disable MSI-X */
162
+	pci_read_config_word ( pci, ( msix->cap + PCI_MSIX_CTRL ), &ctrl );
163
+	ctrl &= ~PCI_MSIX_CTRL_ENABLE;
164
+	pci_write_config_word ( pci, ( msix->cap + PCI_MSIX_CTRL ), ctrl );
165
+
166
+	/* Unmap pending bit array */
167
+	iounmap ( msix->pba );
168
+
169
+	/* Unmap MSI-X table */
170
+	iounmap ( msix->table );
171
+}
172
+
173
+/**
174
+ * Map MSI-X interrupt vector
175
+ *
176
+ * @v msix		MSI-X capability
177
+ * @v vector		MSI-X vector
178
+ * @v address		Message address
179
+ * @v data		Message data
180
+ */
181
+void pci_msix_map ( struct pci_msix *msix, unsigned int vector,
182
+		    physaddr_t address, uint32_t data ) {
183
+	void *base;
184
+
185
+	/* Sanity check */
186
+	assert ( vector < msix->count );
187
+
188
+	/* Map interrupt vector */
189
+	base = ( msix->table + PCI_MSIX_VECTOR ( vector ) );
190
+	writel ( ( address & 0xffffffffUL ), ( base + PCI_MSIX_ADDRESS_LO ) );
191
+	if ( sizeof ( address ) > sizeof ( uint32_t ) ) {
192
+		writel ( ( ( ( uint64_t ) address ) >> 32 ),
193
+			 ( base + PCI_MSIX_ADDRESS_HI ) );
194
+	} else {
195
+		writel ( 0, ( base + PCI_MSIX_ADDRESS_HI ) );
196
+	}
197
+	writel ( data, ( base + PCI_MSIX_DATA ) );
198
+}
199
+
200
+/**
201
+ * Control MSI-X interrupt vector
202
+ *
203
+ * @v msix		MSI-X capability
204
+ * @v vector		MSI-X vector
205
+ * @v mask		Control mask
206
+ */
207
+void pci_msix_control ( struct pci_msix *msix, unsigned int vector,
208
+			uint32_t mask ) {
209
+	void *base;
210
+	uint32_t ctrl;
211
+
212
+	/* Mask/unmask interrupt vector */
213
+	base = ( msix->table + PCI_MSIX_VECTOR ( vector ) );
214
+	ctrl = readl ( base + PCI_MSIX_CONTROL );
215
+	ctrl &= ~PCI_MSIX_CONTROL_MASK;
216
+	ctrl |= mask;
217
+	writel ( ctrl, ( base + PCI_MSIX_CONTROL ) );
218
+}
219
+
220
+/**
221
+ * Dump MSI-X interrupt state (for debugging)
222
+ *
223
+ * @v msix		MSI-X capability
224
+ * @v vector		MSI-X vector
225
+ */
226
+void pci_msix_dump ( struct pci_msix *msix, unsigned int vector ) {
227
+	void *base;
228
+	uint32_t address_hi;
229
+	uint32_t address_lo;
230
+	physaddr_t address;
231
+	uint32_t data;
232
+	uint32_t ctrl;
233
+	uint32_t pba;
234
+
235
+	/* Do nothing in non-debug builds */
236
+	if ( ! DBG_LOG )
237
+		return;
238
+
239
+	/* Mask/unmask interrupt vector */
240
+	base = ( msix->table + PCI_MSIX_VECTOR ( vector ) );
241
+	address_hi = readl ( base + PCI_MSIX_ADDRESS_HI );
242
+	address_lo = readl ( base + PCI_MSIX_ADDRESS_LO );
243
+	data = readl ( base + PCI_MSIX_DATA );
244
+	ctrl = readl ( base + PCI_MSIX_CONTROL );
245
+	pba = readl ( msix->pba );
246
+	address = ( ( ( ( uint64_t ) address_hi ) << 32 ) | address_lo );
247
+	DBGC ( msix, "MSI-X %p vector %d %#08x => %#08lx%s%s\n",
248
+	       msix, vector, data, address,
249
+	       ( ( ctrl & PCI_MSIX_CONTROL_MASK ) ? " (masked)" : "" ),
250
+	       ( ( pba & ( 1 << vector ) ) ? " (pending)" : "" ) );
251
+}

+ 1
- 0
src/include/ipxe/errfile.h View File

@@ -205,6 +205,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
205 205
 #define ERRFILE_ena		     ( ERRFILE_DRIVER | 0x00c90000 )
206 206
 #define ERRFILE_icplus		     ( ERRFILE_DRIVER | 0x00ca0000 )
207 207
 #define ERRFILE_intelxl		     ( ERRFILE_DRIVER | 0x00cb0000 )
208
+#define ERRFILE_pcimsix		     ( ERRFILE_DRIVER | 0x00cc0000 )
208 209
 
209 210
 #define ERRFILE_aoe			( ERRFILE_NET | 0x00000000 )
210 211
 #define ERRFILE_arp			( ERRFILE_NET | 0x00010000 )

+ 11
- 0
src/include/ipxe/pci.h View File

@@ -94,6 +94,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
94 94
 #define PCI_CAP_ID_VPD			0x03	/**< Vital product data */
95 95
 #define PCI_CAP_ID_VNDR			0x09	/**< Vendor-specific */
96 96
 #define PCI_CAP_ID_EXP			0x10	/**< PCI Express */
97
+#define PCI_CAP_ID_MSIX			0x11	/**< MSI-X */
97 98
 #define PCI_CAP_ID_EA			0x14	/**< Enhanced Allocation */
98 99
 
99 100
 /** Next capability */
@@ -109,6 +110,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
109 110
 #define PCI_EXP_DEVCTL		0x08
110 111
 #define PCI_EXP_DEVCTL_FLR		0x8000	/**< Function level reset */
111 112
 
113
+/** MSI-X interrupts */
114
+#define PCI_MSIX_CTRL		0x02
115
+#define PCI_MSIX_CTRL_ENABLE		0x8000	/**< Enable MSI-X */
116
+#define PCI_MSIX_CTRL_MASK		0x4000	/**< Mask all interrupts */
117
+#define PCI_MSIX_CTRL_SIZE(x)	( (x) & 0x07ff ) /**< Table size */
118
+#define PCI_MSIX_DESC_TABLE	0x04
119
+#define PCI_MSIX_DESC_PBA	0x08
120
+#define PCI_MSIX_DESC_BIR(x)	( (x) & 0x00000007 ) /**< BAR index */
121
+#define PCI_MSIX_DESC_OFFSET(x)	( (x) & 0xfffffff8 ) /**< BAR offset */
122
+
112 123
 /** Uncorrectable error status */
113 124
 #define PCI_ERR_UNCOR_STATUS	0x04
114 125
 

+ 77
- 0
src/include/ipxe/pcimsix.h View File

@@ -0,0 +1,77 @@
1
+#ifndef _IPXE_PCIMSIX_H
2
+#define _IPXE_PCIMSIX_H
3
+
4
+/** @file
5
+ *
6
+ * PCI MSI-X interrupts
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
11
+
12
+#include <ipxe/pci.h>
13
+
14
+/** MSI-X BAR mapped length */
15
+#define PCI_MSIX_LEN 0x1000
16
+
17
+/** MSI-X vector offset */
18
+#define PCI_MSIX_VECTOR(n) ( (n) * 0x10 )
19
+
20
+/** MSI-X vector address low 32 bits */
21
+#define PCI_MSIX_ADDRESS_LO 0x0
22
+
23
+/** MSI-X vector address high 32 bits */
24
+#define PCI_MSIX_ADDRESS_HI 0x4
25
+
26
+/** MSI-X vector data */
27
+#define PCI_MSIX_DATA 0x8
28
+
29
+/** MSI-X vector control */
30
+#define PCI_MSIX_CONTROL 0xc
31
+#define PCI_MSIX_CONTROL_MASK 0x00000001	/**< Vector is masked */
32
+
33
+/** PCI MSI-X capability */
34
+struct pci_msix {
35
+	/** Capability offset */
36
+	unsigned int cap;
37
+	/** Number of vectors */
38
+	unsigned int count;
39
+	/** MSI-X table */
40
+	void *table;
41
+	/** Pending bit array */
42
+	void *pba;
43
+};
44
+
45
+extern int pci_msix_enable ( struct pci_device *pci, struct pci_msix *msix );
46
+extern void pci_msix_disable ( struct pci_device *pci, struct pci_msix *msix );
47
+extern void pci_msix_map ( struct pci_msix *msix, unsigned int vector,
48
+			   physaddr_t address, uint32_t data );
49
+extern void pci_msix_control ( struct pci_msix *msix, unsigned int vector,
50
+			       uint32_t mask );
51
+extern void pci_msix_dump ( struct pci_msix *msix, unsigned int vector );
52
+
53
+/**
54
+ * Mask MSI-X interrupt vector
55
+ *
56
+ * @v msix		MSI-X capability
57
+ * @v vector		MSI-X vector
58
+ */
59
+static inline __attribute__ (( always_inline )) void
60
+pci_msix_mask ( struct pci_msix *msix, unsigned int vector ) {
61
+
62
+	pci_msix_control ( msix, vector, PCI_MSIX_CONTROL_MASK );
63
+}
64
+
65
+/**
66
+ * Unmask MSI-X interrupt vector
67
+ *
68
+ * @v msix		MSI-X capability
69
+ * @v vector		MSI-X vector
70
+ */
71
+static inline __attribute__ (( always_inline )) void
72
+pci_msix_unmask ( struct pci_msix *msix, unsigned int vector ) {
73
+
74
+	pci_msix_control ( msix, vector, 0 );
75
+}
76
+
77
+#endif /* _IPXE_PCIMSIX_H */

Loading…
Cancel
Save