Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


PCI interrupt delivery via ioapic
[palacios.git] / palacios / src / devices / mptable.c
index 3b6a463..cca7a44 100644 (file)
 
   Currently, we set up n identical processors (based on
   number of cores in guest info), with apics 0..n-1, and
-  ioapic as n.
+  ioapic as n.  The ISA interrupt lines map to pins 0..15
+  of the first ioapic.  PCI bus lines map to pins 16..19
+  of the first ioapic.  The system supports virtual wire
+  compability mode and symmetric mode.   PIC mode is not supported. 
 
   The expectation is that the target will have 
   8 bytes (for ___HVMMP signature) followed by 896 bytes of space
@@ -72,6 +75,7 @@
 
 
 #define BUS_ISA "ISA   "
+#define BUS_PCI "PCI   "
 
 #define INT_TYPE_INT 0
 #define INT_TYPE_NMI 1
@@ -214,6 +218,8 @@ struct mp_table_local_interrupt_assignment {
 
 
 
+#define PCI           1
+#define NUM_PCI_SLOTS 8
 
 
 static inline int check_for_cookie(void * target) {
@@ -275,6 +281,11 @@ static int write_pointer(void * target, uint32_t mptable_gpa) {
     p->pointer = mptable_gpa;
     p->length = 1;             // length in 16 byte chunks
     p->spec_rev = SPEC_REV;
+
+    // The remaining zeros indicate that an MP config table is present
+    // and that virtual wire mode is implemented (not PIC mode)
+    // Either virtual wire or PIC must be implemented in addition to
+    // symmetric I/O mode
     
     // checksum calculation
     p->checksum = 0;
@@ -315,8 +326,14 @@ static int write_mptable(void * target, uint32_t numcores) {
     memcpy(header->oem_id, OEM_ID, 8);
     memcpy(header->prod_id, PROD_ID, 12);
 
-    // n processors, 1 ioapic, 1 isa bus, 16 IRQs = 18+n
+#if PCI
+    // n processors, 1 ioapic, 1 pci bus, 1 isa bus, 16 IRQ, 4*NUM_SLOTS = 19 + 4*numslots+ n
+    header->entry_count = numcores + 19 + 4 * NUM_PCI_SLOTS;
+#else
+    // n processors, 1 ioapic, 1 isa bus, 16 IRQ INTs = 18+n
     header->entry_count = numcores + 18;
+#endif
+
     header->lapic_addr = LAPIC_ADDR;
     
     // now we arrange the processors;
@@ -343,15 +360,27 @@ static int write_mptable(void * target, uint32_t numcores) {
        cur += sizeof(struct mp_table_processor);
     }
 
-    // next comes the ISA bas
+#if PCI
+    // PCI bus is always zero
     bus = (struct mp_table_bus *)cur;
     cur += sizeof(struct mp_table_bus);
 
     memset((void *)bus, 0, sizeof(struct mp_table_bus));
     bus->entry_type = ENTRY_BUS;
     bus->bus_id = 0;
+    memcpy(bus->bus_type, BUS_PCI, 6);
+#endif
+
+    // next comes the ISA bus  (bus one)
+    bus = (struct mp_table_bus *)cur;
+    cur += sizeof(struct mp_table_bus);
+
+    memset((void *)bus, 0, sizeof(struct mp_table_bus));
+    bus->entry_type = ENTRY_BUS;
+    bus->bus_id = 1;
     memcpy(bus->bus_type, BUS_ISA, 6);
 
+
     // next comes the IOAPIC
     ioapic = (struct mp_table_ioapic *)cur;
     cur += sizeof(struct mp_table_ioapic);
@@ -364,10 +393,19 @@ static int write_mptable(void * target, uint32_t numcores) {
     ioapic->ioapic_address = IOAPIC_ADDR;
 
 
+
+    // LEGACY ISA IRQ mappings
     // The MPTABLE IRQ mappings are kind of odd. 
     // We don't include a bus IRQ 2, and instead remap Bus IRQ 0 to dest irq 2
-
-
+    // The idea here is that the timer hooks to 2, while the PIC hooks
+    // to zero in ExtInt mode.  This makes it possible to do virtual wire
+    // mode via the ioapic.
+    //
+    // Note that the timer connects to pin 2 of the IOAPIC.  Sadly,
+    // the timer is unaware of this and just raises irq 0.  The ioapic
+    // transforms this to a pin 2 interrupt.   If we want the PIC
+    // to be able to channel interrupts via pin 0, we need a separate
+    // path. 
     for (irq = 0; irq < 16; irq++) { 
        uint8_t dst_irq = irq;
 
@@ -384,7 +422,7 @@ static int write_mptable(void * target, uint32_t numcores) {
        interrupt->interrupt_type = INT_TYPE_INT;
        interrupt->flags.po = INT_POLARITY_DEFAULT;
        interrupt->flags.el = INT_TRIGGER_DEFAULT;
-       interrupt->source_bus_id = 0;
+       interrupt->source_bus_id = 1;
        interrupt->source_bus_irq = irq;
        interrupt->dest_ioapic_id = numcores;
        interrupt->dest_ioapic_intn = dst_irq;
@@ -392,10 +430,55 @@ static int write_mptable(void * target, uint32_t numcores) {
        cur += sizeof(struct mp_table_io_interrupt_assignment);
     }
 
+#if PCI
+    {
+      // Interrupt redirection entries for PCI bus
+      // 
+      // We need an entry for each slot+pci interrupt
+      // There can be 32 slots, each of which can use 4 interrupts
+      // Thus there are 128 entries
+      //
+      // In this simple setup, we map
+      // slot i, intr j (both zero based)  to  pci_irq[(i+j)%4]
+      
+      int slot, intr;
+      static uint8_t pci_irq[4] = {16,17,18,19};
+
+      for (slot=0;slot<NUM_PCI_SLOTS;slot++) { 
+       for (intr=0;intr<4;intr++) { 
+
+         uint8_t dst_irq = pci_irq[(slot+intr)%4];
+
+         interrupt = (struct mp_table_io_interrupt_assignment *)cur;
+         memset((void *)interrupt, 0, sizeof(struct mp_table_io_interrupt_assignment));
+
+         interrupt->entry_type = ENTRY_IOINT;
+         interrupt->interrupt_type = INT_TYPE_INT;
+         interrupt->flags.po = INT_POLARITY_DEFAULT;
+         interrupt->flags.el = INT_TRIGGER_DEFAULT;
+         interrupt->source_bus_id = 0;
+          // Yes, this is how you encode the slot and pin of a PCI device
+          // As we all know, bits are expensive
+          // We can have as many as 32 slots, but to get that large,
+          // we would need to tweak the bios's landing zone for the mptable
+         interrupt->source_bus_irq = (slot<<2) | intr ;
+         interrupt->dest_ioapic_id = numcores;
+         interrupt->dest_ioapic_intn = dst_irq;
+
+         cur += sizeof(struct mp_table_io_interrupt_assignment);
+
+         //V3_Print("PCI0, slot %d, irq %d maps to irq %d\n",slot,intr,dst_irq);
+       }
+      }
+    }
+#endif
+       
     // now we can set the length;
 
     header->base_table_length = (cur - (uint8_t *)header);
 
+    V3_Print("MPtable size: %u\n",header->base_table_length);
+
     // checksum calculation
     header->checksum = 0;
     sum = 0;
@@ -403,8 +486,6 @@ static int write_mptable(void * target, uint32_t numcores) {
        sum += ((uint8_t *)target)[i];
     }
     header->checksum = (255 - sum) + 1;
-
-
        
     return 0;
 }