MSI cleanup and IRQ routing
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 2 Apr 2014 23:50:02 +0000 (16:50 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 2 Apr 2014 23:52:34 +0000 (16:52 -0700)
Not sure what the deal is with the Msiaedest.  Perhaps those bits are
used for extended APIC id bits (like with an x2APIC?).  Though I heard
the x2APICs use IO redirection/virtualization hardware, or something
similarly ridiculous/painful.

kern/arch/x86/ioapic.c
kern/arch/x86/msi.c
kern/arch/x86/pci.h

index 2509619..eef4a57 100644 (file)
@@ -387,8 +387,6 @@ static int msi_irq_enable(struct irq_handler *irq_h, struct pci_device *p)
                /* TODO: should free vno here */
                return -1;
        }
-       p->msi_hi = hi;
-       p->msi_lo = lo;
        irq_h->check_spurious = lapic_check_spurious;
        irq_h->eoi = lapic_send_eoi;
        irq_h->mask = msi_mask_irq;
index 588e8c5..2603508 100644 (file)
@@ -29,17 +29,37 @@ enum {
 };
 
 enum {
-       /* address */
+       /* MSI address format
+        *
+        * +31----------------------20+19----------12+11--------4+--3--+--2--+1---0+
+        * |       0xfee              | Dest APIC ID |  Reserved | RH  | DM  |  XX |
+        * +--------------------------+--------------+-----------+-----+-----+-----+
+        *
+        * RH: Redirection Hint
+        * DM: Destinatio Mode
+        * XX: Probably reserved, set to 0
+        */
        Msiabase                = 0xfee00000u,
        Msiadest                = 1<<12,                /* same as 63:56 of apic vector */
        Msiaedest       = 1<<4,         /* same as 55:48 of apic vector */
        Msialowpri      = 1<<3,         /* redirection hint */
        Msialogical     = 1<<2,
 
-       /* data */
+       /* MSI data format
+        * +63-------------------------------------------------------------------32+
+        * |                          Reserved                                     |
+        * +-------------------------------+-15-+-14-+--------+10----8+7----------0+
+        * |          Reserved             | TM | Lv | Reserv | Dmode |   Vector   |
+        * +-------------------------------+----+----+--------+-------+------------+
+        *
+        * Dmode: delivery mode (like APIC/LVT messages).  Usually 000 (Fixed).
+        * TM: Trigger mode (0 Edge, 1 Level)
+        * Lv: Level assert (0 Deassert, 1 Assert)
+        *
+        *
+        * for more info, check intel's SDMv3 (grep message signal) */
        Msidlevel       = 1<<15,
        Msidassert      = 1<<14,
-       Msidlogical     = 1<<11,
        Msidmode        = 1<<8,         /* 3 bits; delivery mode */
        Msidvector      = 0xff<<0,
 };
@@ -94,7 +114,7 @@ blacklist(struct pci_device *p)
 int pci_msi_enable(struct pci_device *p, uint64_t vec)
 {
        char *s;
-       unsigned int c, f, d, datao, lopri, dmode, logical;
+       unsigned int c, f, dest, datao, lopri, dmode, logical;
 
        /* Get the offset of the MSI capability
         * in the function's config space.
@@ -110,32 +130,34 @@ int pci_msi_enable(struct pci_device *p, uint64_t vec)
         */
        f = pcidev_read16(p, c + 2) & ~Mmesgmsk;
 
-       /* See if it's a broken device, currently
-        * there's only Marvell there.
-        */
-       if(blacklist(p) != 0)
+       if (blacklist(p) != 0)
                return -1;
 
        /* Data begins at 8 bytes in. */
        datao = 8;
 
-       /* The data we write is 16 bits, scarfed
-        * in the upper 16 bits of d.
-        */
-       d = vec>>48;
+       /* The destination is the traditional 8-bit APIC id is in 63:56 of the
+        * vector.  Later we may need to deal with extra destination bits
+        * (Msiaedest, in this code).  I haven't seen anything in the Intel SDM
+        * about using Msiaedest (the bits are reserved) */
+       dest = vec >> 56;
 
-       /* Hard to see it being anything but lopri but ... */
+       /* lopri is rarely set, and intel doesn't recommend using it.  with msi, the
+        * lopri field is actually a redirection hint, and also must be set when
+        * sending logical messages. */
        lopri = (vec & 0x700) == MTlp;
 
        logical = (vec & Lm) != 0;
+       if (logical)
+               lopri = 1;
 
        /* OK, Msiabase is fee00000, and we offset with the
         * dest from above, lowpri, and logical.
         */
-       printd("Write to %d %08lx \n",c + 4, Msiabase | Msiaedest * d
-               | Msialowpri * lopri | Msialogical * logical);
-       pcidev_write32(p, c + 4, Msiabase | Msiaedest * d
-               | Msialowpri * lopri | Msialogical * logical);
+       p->msi_msg_addr_lo = Msiabase | Msiadest * dest | Msialowpri * lopri |
+                            Msialogical * logical;
+       printd("Write to %d %08lx \n",c + 4, p->msi_msg_addr_lo);
+       pcidev_write32(p, c + 4, p->msi_msg_addr_lo);
 
        /* And even if it's 64-bit capable, we do nothing with
         * the high order bits. If it is 64-bit we need to offset
@@ -145,18 +167,16 @@ int pci_msi_enable(struct pci_device *p, uint64_t vec)
                datao += 4;
                pcidev_write32(p, c + 8, 0);
        }
+       p->msi_msg_addr_hi = 0;
 
        /* pick up the delivery mode from the vector */
        dmode = (vec >> 8) & 7;
 
-       /* the data we write to that location is a combination
-        * of things. It's not yet clear if this is a plan 9 chosen
-        * thing or a PCI spec chosen thing.
-        */
-       printd("Write data %d %04x\n", c + datao, Msidassert | Msidlogical * logical
-                      | Msidmode * dmode | ((unsigned int)vec & 0xff));
-       pcidev_write16(p, c + datao, Msidassert | Msidlogical * logical
-                      | Msidmode * dmode | ((unsigned int)vec & 0xff));
+       /* We can only specify the lower 16 bits of the MSI message, the rest gets
+        * forced to 0 by the device.  We're assuming edge triggered here. */
+       p->msi_msg_data = Msidmode * dmode | ((unsigned int)vec & 0xff);
+       printd("Write data %d %04x\n", c + datao, p->msi_msg_data);
+       pcidev_write16(p, c + datao, p->msi_msg_data);
 
        /* If we have the option of masking the vectors,
         * blow all the masks to 0. It's a 32-bit mask.
@@ -164,10 +184,9 @@ int pci_msi_enable(struct pci_device *p, uint64_t vec)
        if(f & Vmask)
                pcidev_write32(p, c + datao + 4, 0);
 
-       /* Now write the control bits back, with the
-        * Mmesg mask (which is a power of 2) set to 0
-        * (meaning one message only).
-        */
+       /* Now write the control bits back, with the Mmesg mask (which is a power of
+        * 2) set to 0 (meaning one vector only).  Note we still haven't enabled
+        * MSI.  Will do that when we unmask. */
        printd("write @ %d %04lx\n",c + 2, f);
        pcidev_write16(p, c + 2, f);
        return 0;
@@ -295,10 +314,9 @@ int pci_msix_enable(struct pci_device *p, uint64_t vec)
         * of things. It's not yet clear if this is a plan 9 chosen
         * thing or a PCI spec chosen thing.
         */
-       printd("Write data @%p %04x\n", &entry->data, Msidassert | Msidlogical * logical
-                      | Msidmode * dmode | ((unsigned int)vec & 0xff));
-       entry->data = Msidassert | Msidlogical * logical
-               | Msidmode * dmode | ((unsigned int)vec & 0xff);
+       printd("Write data @%p %04x\n", &entry->data, Msidassert |
+              Msidmode * dmode | ((unsigned int)vec & 0xff));
+       entry->data = Msidassert | Msidmode * dmode | ((unsigned int)vec & 0xff);
        return 0;
 }
 /* Mask the msi function. Since 'masking' means disable it,
@@ -322,6 +340,7 @@ pcimsimask(struct pci_device *p, int mask)
        return 0;
 }
 
+/* TODO: should lock in all of these PCI/MSI functions */
 void msi_mask_irq(struct irq_handler *irq_h, int apic_vector)
 {
        struct pci_device *p = (struct pci_device*)irq_h->dev_private;
@@ -352,7 +371,9 @@ int msi_route_irq(struct irq_handler *irq_h, int apic_vector, int dest)
        c = msicap(p);
        assert(c);
 
-       /* TODO */
-       printk("Not routing MSI yet, fix me!\n");
-       return -1;
+       /* mask out the old destination, replace with new */
+       p->msi_msg_addr_lo &= ~(((1 << 8) - 1) << 12);
+       p->msi_msg_addr_lo |= dest << 12;
+       pcidev_write32(p, c + 4, p->msi_msg_addr_lo);
+       return 0;
 }
index c121c5c..72106c0 100644 (file)
@@ -355,8 +355,9 @@ struct pci_device {
        uint8_t                                         class;
        uint8_t                                         subclass;
        uint8_t                                         progif;
-       uint32_t                                        msi_hi;
-       uint32_t                                        msi_lo;
+       uint32_t                                        msi_msg_addr_hi;
+       uint32_t                                        msi_msg_addr_lo;
+       uint32_t                                        msi_msg_data;
        uint8_t                                         nr_bars;
        struct pci_bar                          bar[MAX_PCI_BAR];
        uint32_t                                        caps[PCI_CAP_ID_MAX + 1];