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