9ns: mnt: Don't use a 'bogus' struct
[akaros.git] / kern / arch / x86 / msi.c
1 /*
2  * This file is part of the UCB release of Plan 9. It is subject to the license
3  * terms in the LICENSE file found in the top-level directory of this
4  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
5  * part of the UCB release of Plan 9, including this file, may be copied,
6  * modified, propagated, or distributed except according to the terms contained
7  * in the LICENSE file.
8  */
9
10 #include <slab.h>
11 #include <kmalloc.h>
12 #include <kref.h>
13 #include <string.h>
14 #include <stdio.h>
15 #include <assert.h>
16 #include <error.h>
17 #include <cpio.h>
18 #include <pmap.h>
19 #include <smp.h>
20 #include <net/ip.h>
21
22 enum {
23         Dpcicap         = 1<<0,
24         Dmsicap         = 1<<1,
25         Dvec            = 1<<2,
26         Debug           = 0,
27 };
28
29 enum {
30         /* MSI address format
31          *
32          * +31----------------------20+19----------12+11--------4+--3--+--2--+1---0+
33          * |       0xfee              | Dest APIC ID |  Reserved | RH  | DM  |  XX |
34          * +--------------------------+--------------+-----------+-----+-----+-----+
35          *
36          * RH: Redirection Hint
37          * DM: Destinatio Mode
38          * XX: Probably reserved, set to 0
39          */
40         Msiabase                = 0xfee00000u,
41         Msiadest                = 1<<12,                /* same as 63:56 of apic vector */
42         Msiaedest       = 1<<4,         /* same as 55:48 of apic vector */
43         Msialowpri      = 1<<3,         /* redirection hint */
44         Msialogical     = 1<<2,
45
46         /* MSI data format
47          * +63-------------------------------------------------------------------32+
48          * |                          Reserved                                     |
49          * +-------------------------------+-15-+-14-+--------+10----8+7----------0+
50          * |          Reserved             | TM | Lv | Reserv | Dmode |   Vector   |
51          * +-------------------------------+----+----+--------+-------+------------+
52          *
53          * Dmode: delivery mode (like APIC/LVT messages).  Usually 000 (Fixed).
54          * TM: Trigger mode (0 Edge, 1 Level)
55          * Lv: Level assert (0 Deassert, 1 Assert)
56          *
57          *
58          * for more info, check intel's SDMv3 (grep message signal) */
59         Msidlevel       = 1<<15,
60         Msidassert      = 1<<14,
61         Msidmode        = 1<<8,         /* 3 bits; delivery mode */
62         Msidvector      = 0xff<<0,
63 };
64
65 enum{
66         /* msi capabilities */
67         Vmask           = 1<<8, /* Vectors can be masked. Optional. */
68         Cap64           = 1<<7, /* 64-bit addresses. Optional. */
69         Mmesgmsk        = 7<<4, /* Mask for # of messages allowed. See 6.8.1.3 */
70         Mmcap           = 7<<1, /* # of messages the function can support. */
71         Msienable       = 1<<0, /* Enable. */
72         /* msix capabilities */
73         Msixenable      = 1<<15,
74         Msixmask        = 1<<14,
75         Msixtblsize     = 0x7ff,
76 };
77
78 /* Find the offset in config space of this function of the msi capability.
79  * It is defined in 6.8.1 and is variable-sized.  Returns 0 on failure. */
80 static int msicap(struct pci_device *p)
81 {
82         return p->caps[PCI_CAP_ID_MSI];
83 }
84
85 /* Find the offset in config space of this function of the msi-x capability.
86  * It is defined in 6.8.1 and is variable-sized.
87  */
88 static int msixcap(struct pci_device *p)
89 {
90         return p->caps[PCI_CAP_ID_MSIX];
91 }
92
93 static int msi_blacklist(struct pci_device *p)
94 {
95         switch (p->ven_id << 16 | p->dev_id) {
96                 case 0x11ab << 16 | 0x6485:
97                 case 0x8086 << 16 | 0x100f:
98                         return -1;
99         }
100         return 0;
101 }
102
103 static int msix_blacklist(struct pci_device *p)
104 {
105         switch (p->ven_id << 16 | p->dev_id) {
106 //              case 0x11ab << 16 | 0x6485:     /* placeholder */
107                         return -1;
108         }
109         return 0;
110 }
111
112 static uint32_t msi_make_addr_lo(uint64_t vec)
113 {
114         unsigned int dest, lopri, logical;
115         /* The destination is the traditional 8-bit APIC id is in 63:56 of the
116          * vector.  Later we may need to deal with extra destination bits
117          * (Msiaedest, in this code).  I haven't seen anything in the Intel SDM
118          * about using Msiaedest (the bits are reserved) */
119         dest = vec >> 56;
120         /* lopri is rarely set, and intel doesn't recommend using it.  with msi, the
121          * lopri field is actually a redirection hint, and also must be set when
122          * sending logical messages. */
123         lopri = (vec & 0x700) == MTlp;
124         logical = (vec & Lm) != 0;
125         if (logical)
126                 lopri = 1;
127         return Msiabase | Msiadest * dest | Msialowpri * lopri |
128                Msialogical * logical;
129 }
130
131 static uint32_t msi_make_data(uint64_t vec)
132 {
133         unsigned int deliv_mode;
134         deliv_mode = (vec >> 8) & 7;
135         /* We can only specify the lower 16 bits of the MSI message, the rest gets
136          * forced to 0 by the device.  MSI-X can use the full 32 bits.  We're
137          * assuming edge triggered here. */
138         return Msidmode * deliv_mode | ((unsigned int)vec & 0xff);
139 }
140
141 /* see section 6.8.1 of the pci spec. */
142 /* Set up a single function on a single device.
143  * We need to take the vec, bust it up into bits,
144  * and put parts of it in the msi address and parts
145  * in the msi data.
146  */
147 int pci_msi_enable(struct pci_device *p, uint64_t vec)
148 {
149         unsigned int c, f, datao;
150
151         spin_lock_irqsave(&p->lock);
152         if (p->msix_ready) {
153                 printk("MSI: MSI-X is already enabled, aborting\n");
154                 spin_unlock_irqsave(&p->lock);
155                 return -1;
156         }
157         if (p->msi_ready) {
158                 /* only allowing one enable of MSI per device (not supporting multiple
159                  * vectors) */
160                 printk("MSI: MSI is already enabled, aborting\n");
161                 spin_unlock_irqsave(&p->lock);
162                 return -1;
163         }
164         p->msi_ready = TRUE;
165
166         /* Get the offset of the MSI capability in the function's config space. */
167         c = msicap(p);
168         if (!c) {
169                 spin_unlock_irqsave(&p->lock);
170                 return -1;
171         }
172
173         /* read it, clear out the Mmesgmsk bits.
174          * This means that there will be no multiple
175          * messages enabled.
176          */
177         f = pcidev_read16(p, c + 2) & ~Mmesgmsk;
178
179         if (msi_blacklist(p) != 0) {
180                 spin_unlock_irqsave(&p->lock);
181                 return -1;
182         }
183
184         /* Data begins at 8 bytes in. */
185         datao = 8;
186         p->msi_msg_addr_lo = msi_make_addr_lo(vec);
187         printd("Write to %d %08lx \n",c + 4, p->msi_msg_addr_lo);
188         pcidev_write32(p, c + 4, p->msi_msg_addr_lo);
189
190         /* And even if it's 64-bit capable, we do nothing with
191          * the high order bits. If it is 64-bit we need to offset
192          * datao (data offset) by 4 (i.e. another 32 bits)
193          */
194         if(f & Cap64){
195                 datao += 4;
196                 pcidev_write32(p, c + 8, 0);
197         }
198         p->msi_msg_addr_hi = 0;
199
200         p->msi_msg_data = msi_make_data(vec);
201         printd("Write data %d %04x\n", c + datao, p->msi_msg_data);
202         pcidev_write16(p, c + datao, p->msi_msg_data);
203
204         /* If we have the option of masking the vectors,
205          * blow all the masks to 0. It's a 32-bit mask.
206          */
207         if(f & Vmask)
208                 pcidev_write32(p, c + datao + 4, 0);
209
210         /* Now write the control bits back, with the Mmesg mask (which is a power of
211          * 2) set to 0 (meaning one vector only).  Note we still haven't enabled
212          * MSI.  Will do that when we unmask.  According to the spec, we're not
213          * supposed to use the Msienable bit to mask the IRQ, though I don't see how
214          * we can mask on non-Vmask-supported HW. */
215         printd("write @ %d %04lx\n",c + 2, f);
216         pcidev_write16(p, c + 2, f);
217         spin_unlock_irqsave(&p->lock);
218         return 0;
219 }
220
221 static void __msix_mask_entry(struct msix_entry *entry)
222 {
223         uintptr_t reg = (uintptr_t)&entry->vector;
224         write_mmreg32(reg, read_mmreg32(reg) | 0x1);
225 }
226
227 static void __msix_unmask_entry(struct msix_entry *entry)
228 {
229         uintptr_t reg = (uintptr_t)&entry->vector;
230         write_mmreg32(reg, read_mmreg32(reg) & ~0x1);
231 }
232
233 static uintptr_t msix_get_capbar_paddr(struct pci_device *p, int offset)
234 {
235         uint32_t bir, capbar_off;
236         uintptr_t membar;
237
238         bir = pcidev_read32(p, offset);
239         capbar_off = bir & ~0x7;
240         bir &= 0x7;
241         membar = pci_get_membar(p, bir);
242
243         if (!membar) {
244                 printk("MSI-X: no cap membar, bir %d\n", bir);
245                 return 0;
246         }
247         membar += capbar_off;
248         return membar;
249 }
250
251 /* One time initialization of MSI-X for a PCI device.  -1 on error.  Otherwise,
252  * the device will be ready to assign/route MSI-X entries/vectors.  All vectors
253  * are masked, but the overall MSI-X function is unmasked.
254  *
255  * Hold the pci_device lock. */
256 static int __pci_msix_init(struct pci_device *p)
257 {
258         unsigned int c;
259         uint16_t f;
260         int tbl_bir, tbl_off, pba_bir, pba_off;
261         struct msix_entry *entry;
262
263         if (p->msix_ready)
264                 return 0;
265         if (p->msi_ready) {
266                 printk("MSI-X: MSI is already on, aborting\n");
267                 return -1;
268         }
269         if (msix_blacklist(p) != 0)
270                 return -1;
271         /* Get the offset of the MSI capability in the function's config space. */
272         c = msixcap(p);
273         if (c == 0)
274                 return -1;
275         f = pcidev_read16(p, c + 2);
276         /* enable and mask the entire function/all vectors */
277         f |= Msixenable | Msixmask;
278         pcidev_write16(p, c + 2, f);
279
280         p->msix_tbl_paddr = msix_get_capbar_paddr(p, c + 4);
281         p->msix_pba_paddr = msix_get_capbar_paddr(p, c + 8);
282         if (!p->msix_tbl_paddr || !p->msix_pba_paddr) {
283                 /* disable msix, so we can possibly use msi */
284                 pcidev_write16(p, c + 2, f & ~Msixenable);
285                 printk("MSI-X: Missing a tbl (%p) or PBA (%p) paddr!\n",
286                        p->msix_tbl_paddr, p->msix_pba_paddr);
287                 return -1;
288         }
289         p->msix_nr_vec = (f & Msixtblsize) + 1;
290         p->msix_tbl_vaddr = vmap_pmem_nocache(p->msix_tbl_paddr, p->msix_nr_vec *
291                                               sizeof(struct msix_entry));
292         if (!p->msix_tbl_vaddr) {
293                 pcidev_write16(p, c + 2, f & ~Msixenable);
294                 printk("MSI-X: unable to vmap the Table!\n");
295                 return -1;
296         }
297         p->msix_pba_vaddr = vmap_pmem_nocache(p->msix_pba_paddr,
298                                               ROUNDUP(p->msix_nr_vec, 8) / 8);
299         if (!p->msix_pba_vaddr) {
300                 pcidev_write16(p, c + 2, f & ~Msixenable);
301                 printk("MSI-X: unable to vmap the PBA!\n");
302                 vunmap_vmem(p->msix_tbl_paddr,
303                         p->msix_nr_vec * sizeof(struct msix_entry));
304                 return -1;
305         }
306         /* they should all be masked already, but remasking just in case.  likewise,
307          * we need to 0 out the data, since we'll use the lower byte later when
308          * determining if an msix vector is free or not. */
309         entry = (struct msix_entry*)p->msix_tbl_vaddr;
310         for (int i = 0; i < p->msix_nr_vec; i++, entry++) {
311                 __msix_mask_entry(entry);
312                 write_mmreg32((uintptr_t)&entry->data, 0);
313         }
314         /* unmask the device, now that all the vectors are masked */
315         f &= ~Msixmask;
316         pcidev_write16(p, c + 2, f);
317         p->msix_ready = TRUE;
318         return 0;
319 }
320
321 /* Some parts of msix init need to happen during boot.  Devices can call this
322  * during their reset methods, and then later register their IRQs during attach.
323  * Other OS's also alloc the vector around this time, though we'll hold off on
324  * that for now. */
325 int pci_msix_init(struct pci_device *p)
326 {
327         int ret;
328         spin_lock_irqsave(&p->lock);
329         ret = __pci_msix_init(p);
330         spin_unlock_irqsave(&p->lock);
331         return ret;
332 }
333
334 /* Enables an MSI-X vector for a PCI device.  vec is formatted like an ioapic
335  * route.  This should be able to handle multiple vectors for a device.  Returns
336  * a msix_irq_vector linkage struct on success (the connection btw an irq_h and
337  * the specific {pcidev, entry}), and 0 on failure. */
338 struct msix_irq_vector *pci_msix_enable(struct pci_device *p, uint64_t vec)
339 {
340         int i;
341         struct msix_entry *entry;
342         struct msix_irq_vector *linkage;
343         unsigned int c, datao;
344
345         spin_lock_irqsave(&p->lock);
346         /* Ensure we're init'd.  We could remove this in the future, though not
347          * everyone calls the extern pci_msix_init. */
348         if (__pci_msix_init(p) < 0) {
349                 spin_unlock_irqsave(&p->lock);
350                 return 0;
351         }
352         /* find an unused slot (no apic_vector assigned).  later, we might want to
353          * point back to the irq_hs for each entry.  not a big deal now. */
354         entry = (struct msix_entry*)p->msix_tbl_vaddr;
355         for (i = 0; i < p->msix_nr_vec; i++, entry++)
356                 if (!(read_mmreg32((uintptr_t)&entry->data) & 0xff))
357                         break;
358         if (i == p->msix_nr_vec) {
359                 printk("[kernel] unable to alloc an MSI-X vector (bug?)\n");
360                 spin_unlock_irqsave(&p->lock);
361                 return 0;
362         }
363         linkage = kmalloc(sizeof(struct msix_irq_vector), MEM_WAIT);
364         linkage->pcidev = p;
365         linkage->entry = entry;
366         linkage->addr_lo = msi_make_addr_lo(vec);
367         linkage->addr_hi = 0;
368         linkage->data = msi_make_data(vec);
369         write_mmreg32((uintptr_t)&entry->data, linkage->data);
370         write_mmreg32((uintptr_t)&entry->addr_lo, linkage->addr_lo);
371         write_mmreg32((uintptr_t)&entry->addr_hi, linkage->addr_hi);
372         spin_unlock_irqsave(&p->lock);
373         return linkage;
374 }
375
376 void pci_dump_msix_table(struct pci_device *p)
377 {
378         struct msix_entry *entry;
379         void *tbl = (void*)p->msix_tbl_vaddr;
380
381         hexdump(tbl, p->msix_nr_vec * sizeof(struct msix_entry));
382         entry = (struct msix_entry*)p->msix_tbl_vaddr;
383         for (int i = 0; i < p->msix_nr_vec; i++, entry++)
384                 printk("Entry %d, addr hi:lo 0x%08x:%08x data 0x%08x\n", i,
385                        entry->addr_hi, entry->addr_lo, entry->data);
386 }
387
388 void pci_msi_mask(struct pci_device *p)
389 {
390         unsigned int c, f;
391         c = msicap(p);
392         assert(c);
393
394         spin_lock_irqsave(&p->lock);
395         f = pcidev_read16(p, c + 2);
396         pcidev_write16(p, c + 2, f & ~Msienable);
397         spin_unlock_irqsave(&p->lock);
398 }
399
400 void pci_msi_unmask(struct pci_device *p)
401 {
402         unsigned int c, f;
403         c = msicap(p);
404         assert(c);
405
406         spin_lock_irqsave(&p->lock);
407         f = pcidev_read16(p, c + 2);
408         pcidev_write16(p, c + 2, f | Msienable);
409         spin_unlock_irqsave(&p->lock);
410 }
411
412 void pci_msi_route(struct pci_device *p, int dest)
413 {
414         unsigned int c, f;
415         c = msicap(p);
416         assert(c);
417
418         spin_lock_irqsave(&p->lock);
419         /* mask out the old destination, replace with new */
420         p->msi_msg_addr_lo &= ~(((1 << 8) - 1) << 12);
421         p->msi_msg_addr_lo |= (dest & 0xff) << 12;
422         pcidev_write32(p, c + 4, p->msi_msg_addr_lo);
423         spin_unlock_irqsave(&p->lock);
424 }
425
426 void pci_msix_mask_vector(struct msix_irq_vector *linkage)
427 {
428         spin_lock_irqsave(&linkage->pcidev->lock);
429         __msix_mask_entry(linkage->entry);
430         spin_unlock_irqsave(&linkage->pcidev->lock);
431 }
432
433 void pci_msix_unmask_vector(struct msix_irq_vector *linkage)
434 {
435         spin_lock_irqsave(&linkage->pcidev->lock);
436         __msix_unmask_entry(linkage->entry);
437         spin_unlock_irqsave(&linkage->pcidev->lock);
438 }
439
440 void pci_msix_route_vector(struct msix_irq_vector *linkage, int dest)
441 {
442         spin_lock_irqsave(&linkage->pcidev->lock);
443         /* mask out the old destination, replace with new */
444         linkage->addr_lo &= ~(((1 << 8) - 1) << 12);
445         linkage->addr_lo |= (dest & 0xff) << 12;
446         write_mmreg32((uintptr_t)&linkage->entry->addr_lo, linkage->addr_lo);
447         spin_unlock_irqsave(&linkage->pcidev->lock);
448 }