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.


disabled serial port debugging
[palacios.git] / palacios / src / devices / icc_bus.c
index e75734d..0a74eee 100644 (file)
@@ -23,7 +23,6 @@
 #include <devices/icc_bus.h>
 #include <devices/apic_regs.h>
 
-
 #define MAX_APICS 256
 
 #ifndef CONFIG_DEBUG_ICC_BUS
 #endif
 
 
-void v3_force_exit() {
+void v3_force_exit(void *p) {
+#ifdef CONFIG_DEBUG_ICC_BUS
+    struct guest_info *core=(struct guest_info *)p;
+#endif
+    PrintDebug("core %u: Forced to exit!\n",core->cpu_id);
 }
 
 struct ipi_thunk_data {
@@ -64,7 +67,7 @@ static struct v3_device_ops dev_ops = {
     .stop = NULL,
 };
 
-
+#ifdef CONFIG_DEBUG_ICC_BUS
 static char *shorthand_str[] = { 
     "(no shorthand)",
     "(self)",
@@ -80,21 +83,30 @@ static char *deliverymode_str[] = {
     "(NMI)",
     "(INIT)",
     "(Start Up)",
-    "(reserved)",
+    "(ExtInt)",
 };
+#endif
 
 
-
-static int deliver(uint32_t src_apic, struct apic_data *dest_apic, struct int_cmd_reg *icr, struct icc_bus_state * state) {
+static int deliver(uint32_t src_apic, struct apic_data *dest_apic, struct int_cmd_reg *icr, struct icc_bus_state * state, uint32_t extirq) {
 
     switch (icr->del_mode) {                                           
 
        case 0:  //fixed
        case 1: // lowest priority
-           PrintDebug("icc_bus: delivering to core %u\n",dest_apic->core->cpu_id); 
-           dest_apic->ops->raise_intr(dest_apic->core, icr->vec, dest_apic->priv_data); 
+       case 7: // ExtInt
+           PrintDebug("icc_bus: delivering IRQ to core %u\n",dest_apic->core->cpu_id); 
+           dest_apic->ops->raise_intr(dest_apic->core, 
+                                      icr->del_mode!=7 ? icr->vec : extirq,
+                                      dest_apic->priv_data); 
            if (src_apic!=state->ioapic_id && dest_apic->core->cpu_id != src_apic) { 
+               // Assume core # is same as logical processor for now
+               // TODO FIX THIS FIX THIS
+               // THERE SHOULD BE:  guestapicid->virtualapicid map,
+               //                   cpu_id->logical processor map
+               //     host maitains logical proc->phsysical proc
                PrintDebug("icc_bus: non-local core, forcing it to exit\n"); 
+               V3_Call_On_CPU(dest_apic->core->cpu_id,v3_force_exit,(void*)(dest_apic->core));
                // TODO: do what the print says
            }                                                   
            break;                                                      
@@ -105,7 +117,6 @@ static int deliver(uint32_t src_apic, struct apic_data *dest_apic, struct int_cm
            break;                                                      
            
        case 3:  //reserved                                             
-       case 7:
            PrintError("icc_bus: Reserved delivery mode 3 is unsupported\n"); 
            return -1;                                          
            break;                                                      
@@ -115,14 +126,70 @@ static int deliver(uint32_t src_apic, struct apic_data *dest_apic, struct int_cm
            return -1;                                          
            break;                                                      
 
-       case 5: //INIT
-           PrintError("icc_bus: INIT delivery is unsupported\n"); 
-           return -1;                                          
+       case 5: { //INIT
+           struct guest_info *core = dest_apic->core;
+
+           PrintDebug("icc_bus: INIT delivery to core %u\n",core->cpu_id);
+
+           // TODO: any APIC reset on dest core (shouldn't be needed, but not sure...)
+
+           // Sanity check
+           if (core->cpu_mode != INIT) { 
+               PrintError("icc_bus: Warning: core %u is not in INIT state, ignored\n",core->cpu_id);
+               // Only a warning, since INIT INIT SIPI is common
+               break;
+           }
+
+           // We transition the target core to SIPI state
+           core->cpu_mode = SIPI;  // note: locking should not be needed here
+
+           // That should be it since the target core should be
+           // waiting in host on this transition
+           // either it's on another core or on a different preemptive thread
+           // in both cases, it will quickly notice this transition 
+           // in particular, we should not need to force an exit here
+
+           PrintDebug("icc_bus: INIT delivery done\n");
+
+       }
            break;                                                      
 
-       case 6: //Start Up
-           PrintError("icc_bus: Startup Delivery is unsupported\n"); 
-           return -1;                                          
+       case 6: { //SIPI
+           struct guest_info *core = dest_apic->core;
+
+           // Sanity check
+           if (core->cpu_mode!=SIPI) { 
+               PrintError("icc_bus: core %u is not in SIPI state, ignored!\n",core->cpu_id);
+               break;
+           }
+
+           // Write the RIP, CS, and descriptor
+           // assume the rest is already good to go
+           //
+           // vector VV -> rip at 0
+           //              CS = VV00
+           //  This means we start executing at linear address VV000
+           //
+           // So the selector needs to be VV00
+           // and the base needs to be VV000
+           //
+           core->rip = 0;
+           core->segments.cs.selector = icr->vec << 8;
+           core->segments.cs.limit = 0xffff;
+           core->segments.cs.base = icr->vec << 12;
+
+           PrintDebug("icc_bus: SIPI delivery (0x%x -> 0x%x:0x0) to core %u\n",
+                      icr->vec, core->segments.cs.selector, core->cpu_id);
+           // Maybe need to adjust the APIC?
+           
+           // We transition the target core to SIPI state
+           core->cpu_mode = REAL;  // note: locking should not be needed here
+
+           // As with INIT, we should not need to do anything else
+
+           PrintDebug("icc_bus: SIPI delivery done\n");
+
+       }
            break;                                                      
     }
 
@@ -130,66 +197,179 @@ static int deliver(uint32_t src_apic, struct apic_data *dest_apic, struct int_cm
 } 
 
 
+//
+// icr_data contains interrupt vector *except* for ext_int
+// in which case it is given via irq
+//
 
-int v3_icc_send_ipi(struct vm_device * icc_bus, uint32_t src_apic, uint64_t icr_data) {
+int v3_icc_send_ipi(struct vm_device * icc_bus, uint32_t src_apic, uint64_t icr_data, 
+                   uint32_t dfr_data, uint32_t extirq) {
 
-    PrintDebug("icc_bus: icc_bus=%p, src_apic=%u, icr_data=%llx\n",icc_bus,src_apic,icr_data);
+    PrintDebug("icc_bus: icc_bus=%p, src_apic=%u, icr_data=%llx, extirq=%u\n",icc_bus,src_apic,icr_data,extirq);
 
     struct int_cmd_reg *icr = (struct int_cmd_reg *)&icr_data;
+    struct dst_fmt_reg *dfr = (struct dst_fmt_reg*)&dfr_data;
+
     struct icc_bus_state * state = (struct icc_bus_state *)icc_bus->private_data;
+    struct apic_data * dest_apic = NULL;
+    PrintDebug("icc_bus: icc_bus=%p, src_apic=%u, icr_data=%llx, extirq=%u\n", 
+              icc_bus, src_apic, icr_data, extirq);
 
     // initial sanity checks
-    if (src_apic>=MAX_APICS || (!state->apics[src_apic].present && src_apic!=state->ioapic_id)) { 
+    if ((src_apic >= MAX_APICS) || 
+       ((state->apics[src_apic].present == 0) && 
+        (src_apic != state->ioapic_id))) { 
        PrintError("icc_bus: Apparently sending from unregistered apic id=%u\n",src_apic);
        return -1;
     }
-    if (icr->dst_mode==0  && !state->apics[icr->dst].present) { 
-       PrintError("icc_bus: Attempted send to unregistered apic id=%u\n",icr->dst);
+
+
+    if ((icr->dst_mode == 0) && (state->apics[icr->dst].present == 0)) { 
+       PrintError("icc_bus: Attempted send to unregistered apic id=%u\n", icr->dst);
        return -1;
     }
-    
-    struct apic_data * dest_apic =  &(state->apics[icr->dst]);
+
+    dest_apic =  &(state->apics[icr->dst]);
 
 
-    PrintDebug("icc_bus: IPI %s %u from %s %u to %s %u (icr=0x%llx)\n",
-              deliverymode_str[icr->del_mode], icr->vec, src_apic==state->ioapic_id ? "ioapic" : "apic",
-              src_apic, shorthand_str[icr->dst_shorthand], icr->dst,icr->val);
+    PrintDebug("icc_bus: IPI %s %u from %s %u to %s %s %u (icr=0x%llx, dfr=0x%x) (extirq=%u)\n",
+              deliverymode_str[icr->del_mode], icr->vec, 
+              src_apic==state->ioapic_id ? "ioapic" : "apic",
+              src_apic,               
+              icr->dst_mode==0 ? "(physical)" : "(logical)", 
+              shorthand_str[icr->dst_shorthand], icr->dst,icr->val, dfr->val,
+              extirq);
 
+    /*
+
+    if (icr->dst==state->ioapic_id) { 
+       PrintError("icc_bus: Attempted send to ioapic ignored\n");
+       return -1;
+    }
+    */
 
 
 
     switch (icr->dst_shorthand) {
 
        case 0:  // no shorthand
-           if (deliver(src_apic,dest_apic,icr,state)) { 
-               return -1;
+
+           if (icr->dst_mode==0) { 
+               // physical delivery
+               struct apic_data * dest_apic =  &(state->apics[icr->dst]);
+               if (deliver(src_apic,dest_apic,icr,state,extirq)) { 
+                   return -1;
+               }
+           } else {
+               // logical delivery
+               uint8_t mda = icr->dst; // message destination address, not physical address
+               
+               if (dfr->model==0xf) { 
+                   // flat model
+                   // this means we deliver the IPI each destination APIC where
+                   // mda of sender & ldr of receiver is nonzero
+                   // mda=0xff means broadcast to all
+                   //
+                   int i;
+                   for (i=0;i<MAX_APICS;i++) { 
+                       struct apic_data *dest_apic=&(state->apics[i]);
+                       if (dest_apic->present &&
+                           dest_apic->ops->should_deliver_flat(dest_apic->core,
+                                                               mda,
+                                                               dest_apic->priv_data)) {
+                           if (deliver(src_apic,dest_apic,icr,state,extirq)) { 
+                               return -1;
+                           }
+                       }
+                   }
+               } else if (dfr->model==0x0) {
+                   // cluster model
+                   //
+                   // there are two variants of this
+                   //
+                   // 1. (ancient P5/P6) All apics are on one bus
+                   //    mda[31:28] is the target cluster, 
+                   //    mda[27:24] has one bit for each apic in the cluster
+                   //    mda[31:28] of sending apic == ldr[31:28] of dest apic means
+                   //      the dest apic is part of the cluster
+                   //      then mda[27:24] & ldr[27:24] nonzero means to deliver
+                   //    also, mda=0xff still means broadcast 
+                   //    So, basically, you have 15 clusters of 4 apics each + broadcast
+                   //
+                   // 2. (current) hierarchical cluster model
+                   //    This is some hwat unclearly documented in volume 3, 9-32
+                   //    basically, you have a hierarchy of clusters that where
+                   //    each cluster has 4 agents (APICs?) and a cluster manager.
+                   //    The cluster manager is not an apic, though, and outside of
+                   //    scope of documents.  Again, you have 15 clusters of 4 apics
+                   //    each + broadcast.   My impression is that this is identical 
+                   //    to variant 1 for our purposes. 
+                   //
+                   //
+                   // if we are in lowest priorty mode, we should just pick one
+                   // according to the arbitrarion prioty register
+                   int i;
+                   for (i=0;i<MAX_APICS;i++) { 
+                       struct apic_data *dest_apic=&(state->apics[i]);
+                       if (dest_apic->present &&
+                           dest_apic->ops->should_deliver_cluster(dest_apic->core,
+                                                                  mda,
+                                                                  dest_apic->priv_data)) {
+                           if (deliver(src_apic,dest_apic,icr,state,extirq)) { 
+                               return -1;
+                           }
+                       }
+                   }
+               } else {
+                   PrintError("icc_bus: unknown logical delivery model 0x%x\n", dfr->model);
+                   return -1;
+               }
+
            }
+           
            break;
-
+           
        case 1:  // self
-           if (icr->dst==state->ioapic_id) { 
-               PrintError("icc_bus: ioapic attempting to send to itself\n");
-               return -1;
-           }
-           if (deliver(src_apic,dest_apic,icr,state)) { 
+
+           if (icr->dst_mode==0) { 
+               // physical delivery
+               if (icr->dst==state->ioapic_id) { 
+                   PrintError("icc_bus: ioapic attempting to send to itself\n");
+                   return -1;
+               }
+               struct apic_data *dest_apic=&(state->apics[src_apic]);
+               if (deliver(src_apic,dest_apic,icr,state,extirq)) { 
+                   return -1;
+               }
+           } else {
+               // logical delivery
+               PrintError("icc_bus: use of logical delivery in self is not yet supported.\n");
+
                return -1;
            }
            break;
-
+           
        case 2: 
+
        case 3: { // all and all-but-me
+           // assuming that logical verus physical doesn't matter
+           // although it is odd that both are used
            int i;
            for (i=0;i<MAX_APICS;i++) { 
-               dest_apic=&(state->apics[i]);
+               struct apic_data *dest_apic=&(state->apics[i]);
                if (dest_apic->present && (i!=src_apic || icr->dst_shorthand==2)) { 
-                   if (deliver(src_apic,dest_apic,icr,state)) { 
+                   if (deliver(src_apic,dest_apic,icr,state,extirq)) { 
                        return -1;
                    }
                }
            }
        }
            break;
+
+       default:
+           return -1;
     }
+    
 
     return 0;
 }
@@ -237,19 +417,18 @@ int v3_icc_register_ioapic(struct v3_vm_info *vm, struct vm_device * icc_bus, ui
 }
 
 
-
 static int icc_bus_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
     PrintDebug("icc_bus: Creating ICC_BUS\n");
 
-    char * name = v3_cfg_val(cfg, "name");
+    char * dev_id = v3_cfg_val(cfg, "ID");
 
     struct icc_bus_state * icc_bus = (struct icc_bus_state *)V3_Malloc(sizeof(struct icc_bus_state));
     memset(icc_bus, 0, sizeof(struct icc_bus_state));
 
-    struct vm_device * dev = v3_allocate_device(name, &dev_ops, icc_bus);
+    struct vm_device * dev = v3_allocate_device(dev_id, &dev_ops, icc_bus);
 
     if (v3_attach_device(vm, dev) == -1) {
-        PrintError("icc_bus: Could not attach device %s\n", name);
+        PrintError("icc_bus: Could not attach device %s\n", dev_id);
         return -1;
     }