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.


Removed spurious print
[palacios.git] / palacios / src / devices / apic.c
index 13ead3c..62fbb4d 100644 (file)
@@ -11,7 +11,8 @@
  * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org> 
  * All rights reserved.
  *
- * Author: Jack Lange <jarusl@cs.northwestern.edu>
+ * Authors: Jack Lange <jarusl@cs.northwestern.edu>
+ *          Peter Dinda <pdinda@northwestern.edu> (SMP)
  *
  * This is free software.  You are permitted to use,
  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
 #include <devices/apic_regs.h>
 #include <palacios/vmm.h>
 #include <palacios/vmm_msr.h>
+#include <palacios/vmm_sprintf.h>
+#include <palacios/vm_guest.h>
+#include <palacios/vmm_types.h>
+
+
+#include <palacios/vmm_queue.h>
+#include <palacios/vmm_lock.h>
+
+/* The locking in this file is nasty.
+ * There are 3 different locking approaches that are taken, depending on the APIC operation
+ * 1. Queue locks. Actual irq insertions are done via queueing irq ops at the dest apic. 
+ *    The destination apic's core is responsible for draining the queue, and actually 
+ *    setting the vector table. 
+ * 2. State lock. This is a standard lock taken when internal apic state is read/written. 
+ *    When an irq's destination is determined this lock is taken to examine the apic's 
+ *    addressability. 
+ * 3. VM barrier lock. This is taken when actual VM core state is changed (via SIPI). 
+ */
+
 
 
-#ifndef DEBUG_APIC
+#ifndef V3_CONFIG_DEBUG_APIC
 #undef PrintDebug
 #define PrintDebug(fmt, args...)
-#endif
+#else
+
+static char * shorthand_str[] = { 
+    "(no shorthand)",
+    "(self)",
+    "(all)",
+    "(all-but-me)",
+};
+
+static char * deliverymode_str[] = { 
+    "(fixed)",
+    "(lowest priority)",
+    "(SMI)",
+    "(reserved)",
+    "(NMI)",
+    "(INIT)",
+    "(Start Up)",
+    "(ExtInt)",
+};
 
+#endif
 
 typedef enum { APIC_TMR_INT, APIC_THERM_INT, APIC_PERF_INT, 
               APIC_LINT0_INT, APIC_LINT1_INT, APIC_ERR_INT } apic_irq_type_t;
 
 #define APIC_FIXED_DELIVERY  0x0
+#define APIC_LOWEST_DELIVERY 0x1
 #define APIC_SMI_DELIVERY    0x2
+#define APIC_RES1_DELIVERY   0x3
 #define APIC_NMI_DELIVERY    0x4
 #define APIC_INIT_DELIVERY   0x5
+#define APIC_SIPI_DELIVERY   0x6
 #define APIC_EXTINT_DELIVERY 0x7
 
+#define APIC_SHORTHAND_NONE        0x0
+#define APIC_SHORTHAND_SELF        0x1
+#define APIC_SHORTHAND_ALL         0x2
+#define APIC_SHORTHAND_ALL_BUT_ME  0x3
+
+#define APIC_DEST_PHYSICAL    0x0
+#define APIC_DEST_LOGICAL     0x1
 
-#define BASE_ADDR_MSR 0x0000001B
+
+#define BASE_ADDR_MSR     0x0000001B
 #define DEFAULT_BASE_ADDR 0xfee00000
 
 #define APIC_ID_OFFSET                    0x020
@@ -113,18 +163,16 @@ typedef enum { APIC_TMR_INT, APIC_THERM_INT, APIC_PERF_INT,
 #define EXT_INT_LOC_VEC_TBL_OFFSET2       0x520   // 0x500 - 0x530
 #define EXT_INT_LOC_VEC_TBL_OFFSET3       0x530   // 0x500 - 0x530
 
-
-
 struct apic_msr {
     union {
-       uint64_t val;
+       uint64_t value;
        struct {
-           uchar_t rsvd;
-           uint_t bootstrap_cpu : 1;
-           uint_t rsvd2         : 2;
-           uint_t apic_enable   : 1;
-           ullong_t base_addr   : 40;
-           uint_t rsvd3         : 12;
+           uint8_t rsvd;
+           uint8_t bootstrap_cpu : 1;
+           uint8_t rsvd2         : 2;
+           uint8_t apic_enable   : 1;
+           uint64_t base_addr    : 40;
+           uint32_t rsvd3        : 12;
        } __attribute__((packed));
     } __attribute__((packed));
 } __attribute__((packed));
@@ -132,11 +180,17 @@ struct apic_msr {
 
 
 
+typedef enum {INIT_ST, 
+             SIPI, 
+             STARTED} ipi_state_t; 
+
+struct apic_dev_state;
+
 struct apic_state {
     addr_t base_addr;
 
     /* MSRs */
-    v3_msr_t base_addr_msr;
+    struct apic_msr base_addr_msr;
 
 
     /* memory map registers */
@@ -166,30 +220,71 @@ struct apic_state {
 
     uint32_t tmr_cur_cnt;
     uint32_t tmr_init_cnt;
+    uint32_t missed_ints;
 
-
+    struct local_vec_tbl_reg ext_intr_vec_tbl[4];
 
     uint32_t rem_rd_data;
 
 
-    uchar_t int_req_reg[32];
-    uchar_t int_svc_reg[32];
-    uchar_t int_en_reg[32];
-    uchar_t trig_mode_reg[32];
-  
+    ipi_state_t ipi_state;
+
+    uint8_t int_req_reg[32];
+    uint8_t int_svc_reg[32];
+    uint8_t int_en_reg[32];
+    uint8_t trig_mode_reg[32];
+
+    struct guest_info * core;
+
+    void * controller_handle;
+
+    struct v3_timer * timer;
+
+
+    struct v3_queue irq_queue;
+
     uint32_t eoi;
 
 
 };
 
 
-static void init_apic_state(struct apic_state * apic) {
+
+
+struct apic_dev_state {
+    int num_apics;
+  
+    v3_lock_t state_lock;
+
+    struct apic_state apics[0];
+} __attribute__((packed));
+
+
+
+
+
+static int apic_read(struct guest_info * core, addr_t guest_addr, void * dst, uint_t length, void * priv_data);
+static int apic_write(struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data);
+
+// No lcoking done
+static void init_apic_state(struct apic_state * apic, uint32_t id) {
     apic->base_addr = DEFAULT_BASE_ADDR;
-    apic->base_addr_msr.value = 0x0000000000000900LL;
+
+    if (id == 0) { 
+       // boot processor, enabled
+       apic->base_addr_msr.value = 0x0000000000000900LL;
+    } else {
+       // ap processor, enabled
+       apic->base_addr_msr.value = 0x0000000000000800LL;
+    }
+
+    // same base address regardless of ap or main
     apic->base_addr_msr.value |= ((uint64_t)DEFAULT_BASE_ADDR); 
 
-    PrintDebug("Sizeof Interrupt Request Register %d, should be 32\n", 
-              (uint_t)sizeof(apic->int_req_reg));
+    PrintDebug("apic %u: (init_apic_state): msr=0x%llx\n",id, apic->base_addr_msr.value);
+
+    PrintDebug("apic %u: (init_apic_state): Sizeof Interrupt Request Register %d, should be 32\n",
+              id, (uint_t)sizeof(apic->int_req_reg));
 
     memset(apic->int_req_reg, 0, sizeof(apic->int_req_reg));
     memset(apic->int_svc_reg, 0, sizeof(apic->int_svc_reg));
@@ -200,10 +295,11 @@ static void init_apic_state(struct apic_state * apic) {
     apic->rem_rd_data = 0x00000000;
     apic->tmr_init_cnt = 0x00000000;
     apic->tmr_cur_cnt = 0x00000000;
+    apic->missed_ints = 0;
 
-    // TODO:
-    // We need to figure out what the APIC ID is....
-    apic->lapic_id.val = 0x00000000;
+    apic->lapic_id.val = id;
+    
+    apic->ipi_state = INIT_ST;
 
     // The P6 has 6 LVT entries, so we set the value to (6-1)...
     apic->apic_ver.val = 0x80050010;
@@ -223,58 +319,122 @@ static void init_apic_state(struct apic_state * apic) {
     apic->lint1_vec_tbl.val = 0x00010000;
     apic->err_vec_tbl.val = 0x00010000;
     apic->tmr_div_cfg.val = 0x00000000;
+    //apic->ext_apic_feature.val = 0x00000007;
     apic->ext_apic_feature.val = 0x00040007;
     apic->ext_apic_ctrl.val = 0x00000000;
     apic->spec_eoi.val = 0x00000000;
+
+
+    v3_init_queue(&(apic->irq_queue));
+
+
 }
 
 
 
 
-static int read_apic_msr(uint_t msr, v3_msr_t * dst, void * priv_data) {
-    struct vm_device * dev = (struct vm_device *)priv_data;
-    struct apic_state * apic = (struct apic_state *)dev->private_data;
-    PrintError("READING APIC BASE ADDR: HI=%x LO=%x\n", apic->base_addr_msr.hi, apic->base_addr_msr.lo);
 
-    return -1;
+static int read_apic_msr(struct guest_info * core, uint_t msr, v3_msr_t * dst, void * priv_data) {
+    struct apic_dev_state * apic_dev = (struct apic_dev_state *)priv_data;
+    struct apic_state * apic = &(apic_dev->apics[core->vcpu_id]);
+
+    PrintDebug("apic %u: core %u: MSR read\n", apic->lapic_id.val, core->vcpu_id);
+
+    dst->value = apic->base_addr;
+
+    return 0;
 }
 
 
-static int write_apic_msr(uint_t msr, v3_msr_t src, void * priv_data) {
-    //  struct vm_device * dev = (struct vm_device *)priv_data;
-    //  struct apic_state * apic = (struct apic_state *)dev->private_data;
+static int write_apic_msr(struct guest_info * core, uint_t msr, v3_msr_t src, void * priv_data) {
+    struct apic_dev_state * apic_dev = (struct apic_dev_state *)priv_data;
+    struct apic_state * apic = &(apic_dev->apics[core->vcpu_id]);
+    struct v3_mem_region * old_reg = v3_get_mem_region(core->vm_info, core->vcpu_id, apic->base_addr);
 
-    PrintError("WRITING APIC BASE ADDR: HI=%x LO=%x\n", src.hi, src.lo);
 
-    return -1;
+    PrintDebug("apic %u: core %u: MSR write\n", apic->lapic_id.val, core->vcpu_id);
+
+    if (old_reg == NULL) {
+       // uh oh...
+       PrintError("apic %u: core %u: APIC Base address region does not exit...\n",
+                  apic->lapic_id.val, core->vcpu_id);
+       return -1;
+    }
+    
+
+
+    v3_delete_mem_region(core->vm_info, old_reg);
+
+    apic->base_addr = src.value;
+
+    if (v3_hook_full_mem(core->vm_info, core->vcpu_id, apic->base_addr, 
+                        apic->base_addr + PAGE_SIZE_4KB, 
+                        apic_read, apic_write, apic_dev) == -1) {
+       PrintError("apic %u: core %u: Could not hook new APIC Base address\n",
+                  apic->lapic_id.val, core->vcpu_id);
+
+       return -1;
+    }
+
+
+    return 0;
 }
 
 
+
+
+
 // irq_num is the bit offset into a 256 bit buffer...
 static int activate_apic_irq(struct apic_state * apic, uint32_t irq_num) {
     int major_offset = (irq_num & ~0x00000007) >> 3;
     int minor_offset = irq_num & 0x00000007;
-    uchar_t * req_location = apic->int_req_reg + major_offset;
-    uchar_t * en_location = apic->int_en_reg + major_offset;
-    uchar_t flag = 0x1 << minor_offset;
+    uint8_t * req_location = apic->int_req_reg + major_offset;
+    uint8_t * en_location = apic->int_en_reg + major_offset;
+    uint8_t flag = 0x1 << minor_offset;
 
-    if (irq_num <= 15) {
-       PrintError("Attempting to raise an invalid interrupt: %d\n", irq_num);
-       return -1;
-    }
 
-    PrintDebug("Raising APIC IRQ %d\n", irq_num);
+    PrintDebug("apic %u: core %d: Raising APIC IRQ %d\n", apic->lapic_id.val, apic->core->vcpu_id, irq_num);
+
+    if (*req_location & flag) {
+       PrintDebug("Interrupt %d  coallescing\n", irq_num);
+       return 0;
+    }
 
     if (*en_location & flag) {
        *req_location |= flag;
+       return 1;
     } else {
-       PrintDebug("Interrupt  not enabled... %.2x\n", *en_location);
-       return 0;
+       PrintDebug("apic %u: core %d: Interrupt  not enabled... %.2x\n", 
+                  apic->lapic_id.val, apic->core->vcpu_id, *en_location);
+    }
+
+    return 0;
+}
+
+
+static int add_apic_irq_entry(struct apic_state * apic, uint8_t irq_num) {
+
+    if (irq_num <= 15) {
+       PrintError("core %d: Attempting to raise an invalid interrupt: %d\n", 
+                   apic->core->vcpu_id, irq_num);
+       return -1;
     }
 
+    v3_enqueue(&(apic->irq_queue), (addr_t)irq_num);
+
     return 0;
 }
 
+static void drain_irq_entries(struct apic_state * apic) {
+    uint32_t irq = 0;
+
+    while ((irq = (uint32_t)v3_dequeue(&(apic->irq_queue))) != 0) {
+       activate_apic_irq(apic, irq);
+    }
+
+}
+
+
 
 
 static int get_highest_isr(struct apic_state * apic) {
@@ -282,11 +442,11 @@ static int get_highest_isr(struct apic_state * apic) {
 
     // We iterate backwards to find the highest priority
     for (i = 31; i >= 0; i--) {
-       uchar_t  * svc_major = apic->int_svc_reg + i;
+       uint8_t  * svc_major = apic->int_svc_reg + i;
     
        if ((*svc_major) & 0xff) {
            for (j = 7; j >= 0; j--) {
-               uchar_t flag = 0x1 << j;
+               uint8_t flag = 0x1 << j;
                if ((*svc_major) & flag) {
                    return ((i * 8) + j);
                }
@@ -304,11 +464,11 @@ static int get_highest_irr(struct apic_state * apic) {
 
     // We iterate backwards to find the highest priority
     for (i = 31; i >= 0; i--) {
-       uchar_t  * req_major = apic->int_req_reg + i;
+       uint8_t  * req_major = apic->int_req_reg + i;
     
        if ((*req_major) & 0xff) {
            for (j = 7; j >= 0; j--) {
-               uchar_t flag = 0x1 << j;
+               uint8_t flag = 0x1 << j;
                if ((*req_major) & flag) {
                    return ((i * 8) + j);
                }
@@ -328,26 +488,26 @@ static int apic_do_eoi(struct apic_state * apic) {
     if (isr_irq != -1) {
        int major_offset = (isr_irq & ~0x00000007) >> 3;
        int minor_offset = isr_irq & 0x00000007;
-       uchar_t flag = 0x1 << minor_offset;
-       uchar_t * svc_location = apic->int_svc_reg + major_offset;
+       uint8_t flag = 0x1 << minor_offset;
+       uint8_t * svc_location = apic->int_svc_reg + major_offset;
        
-       PrintDebug("Received APIC EOI\n");
+       PrintDebug("apic %u: core ?: Received APIC EOI for IRQ %d\n", apic->lapic_id.val,isr_irq);
        
        *svc_location &= ~flag;
 
-#ifdef CRAY_XT
+#ifdef V3_CONFIG_CRAY_XT
        
-       if ((((i * 8) + j) == 238) || 
-           (((i * 8) + j) == 239)) {
-           PrintError("Acking IRQ %d\n", ((i * 8) + j));
+       if ((isr_irq == 238) || 
+           (isr_irq == 239)) {
+           PrintDebug("apic %u: core ?: Acking IRQ %d\n", apic->lapic_id.val,isr_irq);
        }
        
-       if (((i * 8) + j) == 238) {
+       if (isr_irq == 238) {
            V3_ACK_IRQ(238);
        }
 #endif
     } else {
-       PrintError("Spurious EOI...\n");
+       //PrintError("apic %u: core ?: Spurious EOI...\n",apic->lapic_id.val);
     }
        
     return 0;
@@ -392,39 +552,446 @@ static int activate_internal_irq(struct apic_state * apic, apic_irq_type_t int_t
            masked = apic->err_vec_tbl.mask;
            break;
        default:
-           PrintError("Invalid APIC interrupt type\n");
+           PrintError("apic %u: core ?: Invalid APIC interrupt type\n", apic->lapic_id.val);
            return -1;
     }
 
     // interrupt is masked, don't send
     if (masked == 1) {
-       PrintDebug("Inerrupt is masked\n");
+       PrintDebug("apic %u: core ?: Inerrupt is masked\n", apic->lapic_id.val);
        return 0;
     }
 
     if (del_mode == APIC_FIXED_DELIVERY) {
        //PrintDebug("Activating internal APIC IRQ %d\n", vec_num);
-       return activate_apic_irq(apic, vec_num);
+       return add_apic_irq_entry(apic, vec_num);
     } else {
-       PrintError("Unhandled Delivery Mode\n");
+       PrintError("apic %u: core ?: Unhandled Delivery Mode\n", apic->lapic_id.val);
        return -1;
     }
 }
 
 
-static int apic_read(addr_t guest_addr, void * dst, uint_t length, void * priv_data) {
-    struct vm_device * dev = (struct vm_device *)priv_data;
-    struct apic_state * apic = (struct apic_state *)dev->private_data;
+
+static inline int should_deliver_cluster_ipi(struct apic_dev_state * apic_dev,
+                                            struct guest_info * dst_core, 
+                                            struct apic_state * dst_apic, uint8_t mda) {
+
+    int ret = 0;
+
+
+    if         ( ((mda & 0xf0) == (dst_apic->log_dst.dst_log_id & 0xf0)) &&  /* (I am in the cluster and */
+         ((mda & 0x0f) & (dst_apic->log_dst.dst_log_id & 0x0f)) ) {  /*  I am in the set)        */
+       ret = 1;
+    } else {
+       ret = 0;
+    }
+
+
+    if (ret == 1) {
+       PrintDebug("apic %u core %u: accepting clustered IRQ (mda 0x%x == log_dst 0x%x)\n",
+                  dst_apic->lapic_id.val, dst_core->vcpu_id, mda, 
+                  dst_apic->log_dst.dst_log_id);
+    } else {
+       PrintDebug("apic %u core %u: rejecting clustered IRQ (mda 0x%x != log_dst 0x%x)\n",
+                  dst_apic->lapic_id.val, dst_core->vcpu_id, mda, 
+                  dst_apic->log_dst.dst_log_id);
+    }
+
+    return ret;
+
+}
+
+static inline int should_deliver_flat_ipi(struct apic_dev_state * apic_dev,
+                                         struct guest_info * dst_core,
+                                         struct apic_state * dst_apic, uint8_t mda) {
+
+    int ret = 0;
+
+
+    if ((dst_apic->log_dst.dst_log_id & mda) != 0) {  // I am in the set 
+       ret = 1;
+    } else {
+       ret = 0;
+    }
+
+
+    if (ret == 1) {
+       PrintDebug("apic %u core %u: accepting flat IRQ (mda 0x%x == log_dst 0x%x)\n",
+                  dst_apic->lapic_id.val, dst_core->vcpu_id, mda, 
+                  dst_apic->log_dst.dst_log_id);
+    } else {
+       PrintDebug("apic %u core %u: rejecting flat IRQ (mda 0x%x != log_dst 0x%x)\n",
+                  dst_apic->lapic_id.val, dst_core->vcpu_id, mda, 
+                  dst_apic->log_dst.dst_log_id);
+    }
+
+
+    return ret;
+}
+
+
+
+static int should_deliver_ipi(struct apic_dev_state * apic_dev, 
+                             struct guest_info * dst_core, 
+                             struct apic_state * dst_apic, uint8_t mda) {
+    addr_t flags = 0;
+    int ret = 0;
+
+    flags = v3_lock_irqsave(apic_dev->state_lock);
+
+    if (dst_apic->dst_fmt.model == 0xf) {
+
+       if (mda == 0xff) {
+           /* always deliver broadcast */
+           ret = 1;
+       } else {
+           ret = should_deliver_flat_ipi(apic_dev, dst_core, dst_apic, mda);
+       }
+    } else if (dst_apic->dst_fmt.model == 0x0) {
+
+       if (mda == 0xff) {
+           /*  always deliver broadcast */
+           ret = 1;
+       } else {
+           ret = should_deliver_cluster_ipi(apic_dev, dst_core, dst_apic, mda);
+       }
+
+    } else {
+       ret = -1;
+    }
+    
+    v3_unlock_irqrestore(apic_dev->state_lock, flags);
+
+
+    if (ret == -1) {
+       PrintError("apic %u core %u: invalid destination format register value 0x%x for logical mode delivery.\n", 
+                  dst_apic->lapic_id.val, dst_core->vcpu_id, dst_apic->dst_fmt.model);
+    }
+
+    return ret;
+}
+
+
+
+
+// Only the src_apic pointer is used
+static int deliver_ipi(struct apic_state * src_apic, 
+                      struct apic_state * dst_apic, 
+                      uint32_t vector, uint8_t del_mode) {
+
+
+    struct guest_info * dst_core = dst_apic->core;
+
+
+    switch (del_mode) {
+
+       case APIC_FIXED_DELIVERY:  
+       case APIC_LOWEST_DELIVERY: {
+           // lowest priority - 
+           // caller needs to have decided which apic to deliver to!
+
+           PrintDebug("delivering IRQ %d to core %u\n", vector, dst_core->vcpu_id); 
+
+           add_apic_irq_entry(dst_apic, vector);
+           
+#ifdef V3_CONFIG_MULTITHREAD_OS
+           if (dst_apic != src_apic) { 
+               PrintDebug(" non-local core with new interrupt, forcing it to exit now\n"); 
+               v3_interrupt_cpu(dst_core->vm_info, dst_core->pcpu_id, 0);
+           }
+#endif
+
+
+           break;
+       }
+       case APIC_INIT_DELIVERY: { 
+
+           PrintDebug(" INIT delivery to core %u\n", dst_core->vcpu_id);
+
+           // TODO: any APIC reset on dest core (shouldn't be needed, but not sure...)
+
+           // Sanity check
+           if (dst_apic->ipi_state != INIT_ST) { 
+               PrintError(" Warning: core %u is not in INIT state (mode = %d), ignored (assuming this is the deassert)\n",
+                          dst_core->vcpu_id, dst_apic->ipi_state);
+               // Only a warning, since INIT INIT SIPI is common
+               break;
+           }
+
+           // We transition the target core to SIPI state
+           dst_apic->ipi_state = 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(" INIT delivery done\n");
+
+           break;                                                      
+       }
+       case APIC_SIPI_DELIVERY: { 
+
+           // Sanity check
+           if (dst_apic->ipi_state != SIPI) { 
+               PrintError(" core %u is not in SIPI state (mode = %d), ignored!\n",
+                          dst_core->vcpu_id, dst_apic->ipi_state);
+               break;
+           }
+
+           v3_reset_vm_core(dst_core, vector);
+
+           PrintDebug(" SIPI delivery (0x%x -> 0x%x:0x0) to core %u\n",
+                      vector, dst_core->segments.cs.selector, dst_core->vcpu_id);
+           // Maybe need to adjust the APIC?
+           
+           // We transition the target core to SIPI state
+           dst_core->core_run_state = CORE_RUNNING;  // note: locking should not be needed here
+           dst_apic->ipi_state = STARTED;
+           
+           // As with INIT, we should not need to do anything else
+           
+           PrintDebug(" SIPI delivery done\n");
+           
+           break;                                                      
+       }
+
+       case APIC_EXTINT_DELIVERY: // EXTINT
+           /* Two possible things to do here: 
+            * 1. Ignore the IPI and assume the 8259a (PIC) will handle it
+            * 2. Add 32 to the vector and inject it...
+            * We probably just want to do 1 here, and assume the raise_irq() will hit the 8259a.
+            */
+           return 0;
+
+       case APIC_SMI_DELIVERY: 
+       case APIC_RES1_DELIVERY: // reserved                                            
+       case APIC_NMI_DELIVERY:
+       default:
+           PrintError("IPI %d delivery is unsupported\n", del_mode); 
+           return -1;
+    }
+    
+    return 0;
+    
+}
+
+static struct apic_state * find_physical_apic(struct apic_dev_state * apic_dev, uint32_t dst_idx) {
+    struct apic_state * dst_apic = NULL;
+    addr_t flags;
+    int i;
+
+    flags = v3_lock_irqsave(apic_dev->state_lock);
+
+    if ( (dst_idx > 0) && (dst_idx < apic_dev->num_apics) ) { 
+       // see if it simply is the core id
+       if (apic_dev->apics[dst_idx].lapic_id.val == dst_idx) { 
+            dst_apic = &(apic_dev->apics[dst_idx]);
+       }
+    }
+
+    for (i = 0; i < apic_dev->num_apics; i++) { 
+       if (apic_dev->apics[i].lapic_id.val == dst_idx) { 
+           dst_apic =  &(apic_dev->apics[i]);
+       }
+    }
+
+    v3_unlock_irqrestore(apic_dev->state_lock, flags);
+
+    return dst_apic;
+
+}
+
+
+static int route_ipi(struct apic_dev_state * apic_dev,
+                    struct apic_state * src_apic, 
+                    struct int_cmd_reg * icr) {
+    struct apic_state * dest_apic = NULL;
+
+
+    PrintDebug("apic: IPI %s %u from apic %p to %s %s %u (icr=0x%llx)\n",
+              deliverymode_str[icr->del_mode], 
+              icr->vec, 
+              src_apic,               
+              (icr->dst_mode == 0) ? "(physical)" : "(logical)", 
+              shorthand_str[icr->dst_shorthand], 
+              icr->dst,
+              icr->val);
+
+
+    switch (icr->dst_shorthand) {
+
+       case APIC_SHORTHAND_NONE:  // no shorthand
+           if (icr->dst_mode == APIC_DEST_PHYSICAL) { 
+
+               dest_apic = find_physical_apic(apic_dev, icr->dst);
+               
+               if (dest_apic == NULL) { 
+                   PrintError("apic: Attempted send to unregistered apic id=%u\n", icr->dst);
+                   return -1;
+               }
+
+               if (deliver_ipi(src_apic, dest_apic, 
+                               icr->vec, icr->del_mode) == -1) {
+                   PrintError("apic: Could not deliver IPI\n");
+                   return -1;
+               }
+
+
+               PrintDebug("apic: done\n");
+
+           } else if (icr->dst_mode == APIC_DEST_LOGICAL) {
+               
+               if (icr->del_mode != APIC_LOWEST_DELIVERY) { 
+                   int i;
+                   uint8_t mda = icr->dst;
+
+                   // logical, but not lowest priority
+                   // we immediately trigger
+                   // fixed, smi, reserved, nmi, init, sipi, etc
+
+                   
+                   for (i = 0; i < apic_dev->num_apics; i++) { 
+                       int del_flag = 0;
+                       
+                       dest_apic = &(apic_dev->apics[i]);
+                       
+                       del_flag = should_deliver_ipi(apic_dev, dest_apic->core, dest_apic, mda);
+                       
+                       if (del_flag == -1) {
+
+                           PrintError("apic: Error checking delivery mode\n");
+                           return -1;
+                       } else if (del_flag == 1) {
+
+                           if (deliver_ipi(src_apic, dest_apic, 
+                                           icr->vec, icr->del_mode) == -1) {
+                               PrintError("apic: Error: Could not deliver IPI\n");
+                               return -1;
+                           }
+                       }
+                   }
+               } else {  // APIC_LOWEST_DELIVERY
+                   struct apic_state * cur_best_apic = NULL;
+                   uint8_t mda = icr->dst;
+                   int i;
+
+                   // logical, lowest priority
+
+                   for (i = 0; i < apic_dev->num_apics; i++) { 
+                       int del_flag = 0;
+
+                       dest_apic = &(apic_dev->apics[i]);
+                       
+                       del_flag = should_deliver_ipi(apic_dev, dest_apic->core, dest_apic, mda);
+                       
+                       if (del_flag == -1) {
+                           PrintError("apic: Error checking delivery mode\n");
+
+                           return -1;
+                       } else if (del_flag == 1) {
+                           // update priority for lowest priority scan
+                           addr_t flags = 0;
+
+                           flags = v3_lock_irqsave(apic_dev->state_lock);
+
+                           if (cur_best_apic == 0) {
+                               cur_best_apic = dest_apic;  
+                           } else if (dest_apic->task_prio.val < cur_best_apic->task_prio.val) {
+                               cur_best_apic = dest_apic;
+                           } 
+
+                           v3_unlock_irqrestore(apic_dev->state_lock, flags);
+
+                       }                       
+                   }
+
+                   // now we will deliver to the best one if it exists
+                   if (!cur_best_apic) { 
+                       PrintDebug("apic: lowest priority deliver, but no destinations!\n");
+                   } else {
+                       if (deliver_ipi(src_apic, cur_best_apic, 
+                                       icr->vec, icr->del_mode) == -1) {
+                           PrintError("apic: Error: Could not deliver IPI\n");
+                           return -1;
+                       }
+                       //V3_Print("apic: logical, lowest priority delivery to apic %u\n",cur_best_apic->lapic_id.val);
+                   }
+               }
+           }
+
+           break;
+           
+       case APIC_SHORTHAND_SELF:  // self
+
+           if (src_apic == NULL) {    /* this is not an apic, but it's trying to send to itself??? */
+               PrintError("apic: Sending IPI to self from generic IPI sender\n");
+               break;
+           }
+
+
+
+           if (icr->dst_mode == APIC_DEST_PHYSICAL)  {  /* physical delivery */
+               if (deliver_ipi(src_apic, src_apic, icr->vec, icr->del_mode) == -1) {
+                   PrintError("apic: Could not deliver IPI to self (physical)\n");
+                   return -1;
+               }
+           } else if (icr->dst_mode == APIC_DEST_LOGICAL) {  /* logical delivery */
+               PrintError("apic: use of logical delivery in self (untested)\n");
+
+               if (deliver_ipi(src_apic, src_apic, icr->vec, icr->del_mode) == -1) {
+                   PrintError("apic: Could not deliver IPI to self (logical)\n");
+                   return -1;
+               }
+           }
+
+           break;
+           
+       case APIC_SHORTHAND_ALL: 
+       case APIC_SHORTHAND_ALL_BUT_ME: { /* 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 < apic_dev->num_apics; i++) { 
+               dest_apic = &(apic_dev->apics[i]);
+               
+               if ((dest_apic != src_apic) || (icr->dst_shorthand == APIC_SHORTHAND_ALL)) { 
+                   if (deliver_ipi(src_apic, dest_apic, icr->vec, icr->del_mode) == -1) {
+                       PrintError("apic: Error: Could not deliver IPI\n");
+                       return -1;
+                   }
+               }
+           }
+
+           break;
+       }
+       default:
+           PrintError("apic: Error routing IPI, invalid Mode (%d)\n", icr->dst_shorthand);
+           return -1;
+    }
+    return 0;
+}
+
+
+// External function, expected to acquire lock on apic
+static int apic_read(struct guest_info * core, addr_t guest_addr, void * dst, uint_t length, void * priv_data) {
+    struct apic_dev_state * apic_dev = (struct apic_dev_state *)(priv_data);
+    struct apic_state * apic = &(apic_dev->apics[core->vcpu_id]);
     addr_t reg_addr  = guest_addr - apic->base_addr;
     struct apic_msr * msr = (struct apic_msr *)&(apic->base_addr_msr.value);
     uint32_t val = 0;
 
 
-    PrintDebug("Read apic address space (%p)\n", 
-              (void *)guest_addr);
+    PrintDebug("apic %u: core %u: at %p: Read apic address space (%p)\n",
+              apic->lapic_id.val, core->vcpu_id, apic, (void *)guest_addr);
 
     if (msr->apic_enable == 0) {
-       PrintError("Write to APIC address space with disabled APIC\n");
+       PrintError("apic %u: core %u: Read from APIC address space with disabled APIC, apic msr=0x%llx\n",
+                  apic->lapic_id.val, core->vcpu_id, apic->base_addr_msr.value);
        return -1;
     }
 
@@ -618,15 +1185,26 @@ static int apic_read(addr_t guest_addr, void * dst, uint_t length, void * priv_d
 
            // Unhandled Registers
        case EXT_INT_LOC_VEC_TBL_OFFSET0:
+           val = apic->ext_intr_vec_tbl[0].val;
+           break;
        case EXT_INT_LOC_VEC_TBL_OFFSET1:
+           val = apic->ext_intr_vec_tbl[1].val;
+           break;
        case EXT_INT_LOC_VEC_TBL_OFFSET2:
+           val = apic->ext_intr_vec_tbl[2].val;
+           break;
        case EXT_INT_LOC_VEC_TBL_OFFSET3:
+           val = apic->ext_intr_vec_tbl[3].val;
+           break;
+    
+
        case EXT_APIC_FEATURE_OFFSET:
        case EXT_APIC_CMD_OFFSET:
        case SEOI_OFFSET:
 
        default:
-           PrintError("Read from Unhandled APIC Register: %x\n", (uint32_t)reg_addr);
+           PrintError("apic %u: core %u: Read from Unhandled APIC Register: %x (getting zero)\n", 
+                      apic->lapic_id.val, core->vcpu_id, (uint32_t)reg_addr);
            return -1;
     }
 
@@ -638,7 +1216,7 @@ static int apic_read(addr_t guest_addr, void * dst, uint_t length, void * priv_d
        *val_ptr = *(((uint8_t *)&val) + byte_addr);
 
     } else if ((length == 2) && 
-              ((reg_addr & 0x3) == 0x3)) {
+              ((reg_addr & 0x3) != 0x3)) {
        uint_t byte_addr = reg_addr & 0x3;
        uint16_t * val_ptr = (uint16_t *)dst;
        *val_ptr = *(((uint16_t *)&val) + byte_addr);
@@ -648,34 +1226,45 @@ static int apic_read(addr_t guest_addr, void * dst, uint_t length, void * priv_d
        *val_ptr = val;
 
     } else {
-       PrintError("Invalid apic read length (%d)\n", length);
+       PrintError("apic %u: core %u: Invalid apic read length (%d)\n", 
+                  apic->lapic_id.val, core->vcpu_id, length);
        return -1;
     }
 
-    PrintDebug("Read finished (val=%x)\n", *(uint32_t *)dst);
+    PrintDebug("apic %u: core %u: Read finished (val=%x)\n", 
+              apic->lapic_id.val, core->vcpu_id, *(uint32_t *)dst);
 
     return length;
 }
 
 
-static int apic_write(addr_t guest_addr, void * src, uint_t length, void * priv_data) {
-    struct vm_device * dev = (struct vm_device *)priv_data;
-    struct apic_state * apic = (struct apic_state *)dev->private_data;
+/**
+ *
+ */
+static int apic_write(struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data) {
+    struct apic_dev_state * apic_dev = (struct apic_dev_state *)(priv_data);
+    struct apic_state * apic = &(apic_dev->apics[core->vcpu_id]); 
     addr_t reg_addr  = guest_addr - apic->base_addr;
     struct apic_msr * msr = (struct apic_msr *)&(apic->base_addr_msr.value);
     uint32_t op_val = *(uint32_t *)src;
+    addr_t flags = 0;
+
+    PrintDebug("apic %u: core %u: at %p and priv_data is at %p\n",
+              apic->lapic_id.val, core->vcpu_id, apic, priv_data);
 
-    PrintDebug("Write to apic address space (%p) (val=%x)\n", 
-              (void *)guest_addr, *(uint32_t *)src);
+    PrintDebug("apic %u: core %u: write to address space (%p) (val=%x)\n", 
+              apic->lapic_id.val, core->vcpu_id, (void *)guest_addr, *(uint32_t *)src);
 
     if (msr->apic_enable == 0) {
-       PrintError("Write to APIC address space with disabled APIC\n");
+       PrintError("apic %u: core %u: Write to APIC address space with disabled APIC, apic msr=0x%llx\n",
+                  apic->lapic_id.val, core->vcpu_id, apic->base_addr_msr.value);
        return -1;
     }
 
 
     if (length != 4) {
-       PrintError("Invalid apic write length (%d)\n", length);
+       PrintError("apic %u: core %u: Invalid apic write length (%d)\n", 
+                  apic->lapic_id.val, length, core->vcpu_id);
        return -1;
     }
 
@@ -709,26 +1298,33 @@ static int apic_write(addr_t guest_addr, void * src, uint_t length, void * priv_
        case TRIG_OFFSET7:
        case PPR_OFFSET:
        case EXT_APIC_FEATURE_OFFSET:
-#if 1
-           PrintError("Attempting to write to read only register %p (ignored)\n", (void *)reg_addr);
-#else   
-           PrintError("Attempting to write to read only register %p (error)\n", (void *)reg_addr);
-           return -1;
-#endif
+
+           PrintError("apic %u: core %u: Attempting to write to read only register %p (error)\n", 
+                      apic->lapic_id.val, core->vcpu_id, (void *)reg_addr);
+
            break;
 
            // Data registers
        case APIC_ID_OFFSET:
+           //V3_Print("apic %u: core %u: my id is being changed to %u\n", 
+           //       apic->lapic_id.val, core->vcpu_id, op_val);
+
            apic->lapic_id.val = op_val;
            break;
        case TPR_OFFSET:
            apic->task_prio.val = op_val;
            break;
        case LDR_OFFSET:
+           PrintDebug("apic %u: core %u: setting log_dst.val to 0x%x\n",
+                      apic->lapic_id.val, core->vcpu_id, op_val);
+           flags = v3_lock_irqsave(apic_dev->state_lock);
            apic->log_dst.val = op_val;
+           v3_unlock_irqrestore(apic_dev->state_lock, flags);
            break;
        case DFR_OFFSET:
+           flags = v3_lock_irqsave(apic_dev->state_lock);
            apic->dst_fmt.val = op_val;
+           v3_unlock_irqrestore(apic_dev->state_lock, flags);
            break;
        case SPURIOUS_INT_VEC_OFFSET:
            apic->spurious_int.val = op_val;
@@ -762,6 +1358,8 @@ static int apic_write(addr_t guest_addr, void * src, uint_t length, void * priv_
            apic->tmr_cur_cnt = op_val;
            break;
        case TMR_DIV_CFG_OFFSET:
+           PrintDebug("apic %u: core %u: setting tmr_div_cfg to 0x%x\n",
+                      apic->lapic_id.val, core->vcpu_id, op_val);
            apic->tmr_div_cfg.val = op_val;
            break;
 
@@ -791,43 +1389,88 @@ static int apic_write(addr_t guest_addr, void * src, uint_t length, void * priv_
        case IER_OFFSET7:
            *(uint32_t *)(apic->int_en_reg + 28) = op_val;
            break;
-   
+
+       case EXT_INT_LOC_VEC_TBL_OFFSET0:
+           apic->ext_intr_vec_tbl[0].val = op_val;
+           break;
+       case EXT_INT_LOC_VEC_TBL_OFFSET1:
+           apic->ext_intr_vec_tbl[1].val = op_val;
+           break;
+       case EXT_INT_LOC_VEC_TBL_OFFSET2:
+           apic->ext_intr_vec_tbl[2].val = op_val;
+           break;
+       case EXT_INT_LOC_VEC_TBL_OFFSET3:
+           apic->ext_intr_vec_tbl[3].val = op_val;
+           break;
+
 
            // Action Registers
        case EOI_OFFSET:
-           // do eoi
+           // do eoi 
            apic_do_eoi(apic);
            break;
 
-       case INT_CMD_LO_OFFSET:
-       case INT_CMD_HI_OFFSET:
-           // Unhandled Registers
-       case EXT_INT_LOC_VEC_TBL_OFFSET0:
-       case EXT_INT_LOC_VEC_TBL_OFFSET1:
-       case EXT_INT_LOC_VEC_TBL_OFFSET2:
-       case EXT_INT_LOC_VEC_TBL_OFFSET3:
+       case INT_CMD_LO_OFFSET: {
+           // execute command 
+
+           struct int_cmd_reg tmp_icr;
+
+           apic->int_cmd.lo = op_val;
+
+           tmp_icr = apic->int_cmd;
+
+           //      V3_Print("apic %u: core %u: sending cmd 0x%llx to apic %u\n", 
+           //       apic->lapic_id.val, core->vcpu_id,
+           //       apic->int_cmd.val, apic->int_cmd.dst);
+
+           if (route_ipi(apic_dev, apic, &tmp_icr) == -1) { 
+               PrintError("IPI Routing failure\n");
+               return -1;
+           }
+
+           break;
+       }
+       case INT_CMD_HI_OFFSET: {
+           apic->int_cmd.hi = op_val;
+           //V3_Print("apic %u: core %u: writing command high=0x%x\n", apic->lapic_id.val, core->vcpu_id,apic->int_cmd.hi);
+
+           break;
+       }
+       // Unhandled Registers
        case EXT_APIC_CMD_OFFSET:
        case SEOI_OFFSET:
        default:
-           PrintError("Write to Unhandled APIC Register: %x\n", (uint32_t)reg_addr);
+           PrintError("apic %u: core %u: Write to Unhandled APIC Register: %x (ignored)\n", 
+                      apic->lapic_id.val, core->vcpu_id, (uint32_t)reg_addr);
+
            return -1;
     }
 
-    PrintDebug("Write finished\n");
+    PrintDebug("apic %u: core %u: Write finished\n", apic->lapic_id.val, core->vcpu_id);
 
     return length;
+
 }
 
 
 
 /* Interrupt Controller Functions */
 
-// returns 1 if an interrupt is pending, 0 otherwise
-static int apic_intr_pending(void * private_data) {
-    struct vm_device * dev = (struct vm_device *)private_data;
-    struct apic_state * apic = (struct apic_state *)dev->private_data;
-    int req_irq = get_highest_irr(apic);
-    int svc_irq = get_highest_isr(apic);
+
+static int apic_intr_pending(struct guest_info * core, void * private_data) {
+    struct apic_dev_state * apic_dev = (struct apic_dev_state *)(private_data);
+    struct apic_state * apic = &(apic_dev->apics[core->vcpu_id]); 
+    int req_irq = 0;    
+    int svc_irq = 0;
+
+    // Activate all queued IRQ entries
+    drain_irq_entries(apic);
+
+    // Check for newly activated entries
+    req_irq = get_highest_irr(apic);
+    svc_irq = get_highest_isr(apic);
+
+    //    PrintDebug("apic %u: core %u: req_irq=%d, svc_irq=%d\n",apic->lapic_id.val,info->vcpu_id,req_irq,svc_irq);
 
     if ((req_irq >= 0) && 
        (req_irq > svc_irq)) {
@@ -837,9 +1480,11 @@ static int apic_intr_pending(void * private_data) {
     return 0;
 }
 
-static int apic_get_intr_number(void * private_data) {
-    struct vm_device * dev = (struct vm_device *)private_data;
-    struct apic_state * apic = (struct apic_state *)dev->private_data;
+
+
+static int apic_get_intr_number(struct guest_info * core, void * private_data) {
+    struct apic_dev_state * apic_dev = (struct apic_dev_state *)(private_data);
+    struct apic_state * apic = &(apic_dev->apics[core->vcpu_id]); 
     int req_irq = get_highest_irr(apic);
     int svc_irq = get_highest_isr(apic);
 
@@ -852,58 +1497,106 @@ static int apic_get_intr_number(void * private_data) {
     return -1;
 }
 
-static int apic_raise_intr(void * private_data, int irq) {
-#ifdef CRAY_XT
-    // The Seastar is connected directly to the LAPIC via LINT0 on the ICC bus
 
-    if (irq == 238) {
-       struct vm_device * dev = (struct vm_device *)private_data;
-       struct apic_state * apic = (struct apic_state *)dev->private_data;
 
-       return activate_apic_irq(apic, irq);
+int v3_apic_send_ipi(struct v3_vm_info * vm, struct v3_gen_ipi * ipi, void * dev_data) {
+    struct apic_dev_state * apic_dev = (struct apic_dev_state *)
+       (((struct vm_device *)dev_data)->private_data);
+    struct int_cmd_reg tmp_icr;
+
+    // zero out all the fields
+    tmp_icr.val = 0;
+
+    tmp_icr.vec = ipi->vector;
+    tmp_icr.del_mode = ipi->mode;
+    tmp_icr.dst_mode = ipi->logical;
+    tmp_icr.trig_mode = ipi->trigger_mode;
+    tmp_icr.dst_shorthand = ipi->dst_shorthand;
+    tmp_icr.dst = ipi->dst;
+
+
+    return route_ipi(apic_dev, NULL, &tmp_icr);
+}
+
+
+int v3_apic_raise_intr(struct v3_vm_info * vm, uint32_t irq, uint32_t dst, void * dev_data) {
+    struct apic_dev_state * apic_dev = (struct apic_dev_state *)
+       (((struct vm_device*)dev_data)->private_data);
+    struct apic_state * apic = &(apic_dev->apics[dst]); 
+
+    PrintDebug("apic %u core ?: raising interrupt IRQ %u (dst = %u).\n", apic->lapic_id.val, irq, dst); 
+
+    add_apic_irq_entry(apic, irq);
+
+#ifdef V3_CONFIG_MULTITHREAD_OS   
+    if ((V3_Get_CPU() != dst)) {
+       v3_interrupt_cpu(vm, dst, 0);
     }
 #endif
-    return 0;
-}
 
-static int apic_lower_intr(void * private_data, int irq) {
     return 0;
 }
 
-static int apic_begin_irq(void * private_data, int irq) {
-    struct vm_device * dev = (struct vm_device *)private_data;
-    struct apic_state * apic = (struct apic_state *)dev->private_data;
-    int major_offset = (irq & ~0x00000007) >> 3;
-    int minor_offset = irq & 0x00000007;
-    uchar_t * req_location = apic->int_req_reg + major_offset;
-    uchar_t * svc_location = apic->int_svc_reg + major_offset;
-    uchar_t flag = 0x01 << minor_offset;
 
-    *svc_location |= flag;
-    *req_location &= ~flag;
 
-#ifdef CRAY_XT
-    if ((irq == 238) || (irq == 239)) {
-       PrintError("APIC: Begin IRQ %d (ISR=%x), (IRR=%x)\n", irq, *svc_location, *req_location);
+static int apic_begin_irq(struct guest_info * core, void * private_data, int irq) {
+    struct apic_dev_state * apic_dev = (struct apic_dev_state *)(private_data);
+    struct apic_state * apic = &(apic_dev->apics[core->vcpu_id]); 
+    int major_offset = (irq & ~0x00000007) >> 3;
+    int minor_offset = irq & 0x00000007;
+    uint8_t *req_location = apic->int_req_reg + major_offset;
+    uint8_t *svc_location = apic->int_svc_reg + major_offset;
+    uint8_t flag = 0x01 << minor_offset;
+
+    if (*req_location & flag) {
+       // we will only pay attention to a begin irq if we
+       // know that we initiated it!
+       *svc_location |= flag;
+       *req_location &= ~flag;
+    } else {
+       // do nothing... 
+       //PrintDebug("apic %u: core %u: begin irq for %d ignored since I don't own it\n",
+       //         apic->lapic_id.val, core->vcpu_id, irq);
     }
-#endif
 
     return 0;
 }
 
 
+/* Timer Functions */
+
+static void apic_inject_timer_intr(struct guest_info *core,
+                                  void * priv_data) {
+    struct apic_dev_state * apic_dev = (struct apic_dev_state *)(priv_data);
+    struct apic_state * apic = &(apic_dev->apics[core->vcpu_id]); 
+    // raise irq
+    PrintDebug("apic %u: core %u: Raising APIC Timer interrupt (periodic=%d) (icnt=%d)\n",
+              apic->lapic_id.val, core->vcpu_id,
+              apic->tmr_vec_tbl.tmr_mode, apic->tmr_init_cnt);
+
+    if (apic_intr_pending(core, priv_data)) {
+        PrintDebug("apic %u: core %u: Overriding pending IRQ %d\n", 
+                  apic->lapic_id.val, core->vcpu_id, 
+                  apic_get_intr_number(core, priv_data));
+    }
+
+    if (activate_internal_irq(apic, APIC_TMR_INT) == -1) {
+       PrintError("apic %u: core %u: Could not raise Timer interrupt\n",
+                  apic->lapic_id.val, core->vcpu_id);
+    }
 
-int v3_apic_raise_intr(struct vm_device * apic_dev, int intr_num) {
-    struct apic_state * apic = (struct apic_state *)apic_dev->private_data;
-    return activate_apic_irq(apic, intr_num);
+    return;
 }
+       
 
 
 
-/* Timer Functions */
-static void apic_update_time(ullong_t cpu_cycles, ullong_t cpu_freq, void * priv_data) {
-    struct vm_device * dev = (struct vm_device *)priv_data;
-    struct apic_state * apic = (struct apic_state *)dev->private_data;
+static void apic_update_time(struct guest_info * core, 
+                            uint64_t cpu_cycles, uint64_t cpu_freq, 
+                            void * priv_data) {
+    struct apic_dev_state * apic_dev = (struct apic_dev_state *)(priv_data);
+    struct apic_state * apic = &(apic_dev->apics[core->vcpu_id]); 
+
     // The 32 bit GCC runtime is a pile of shit
 #ifdef __V3_64BIT__
     uint64_t tmr_ticks = 0;
@@ -911,7 +1604,7 @@ static void apic_update_time(ullong_t cpu_cycles, ullong_t cpu_freq, void * priv
     uint32_t tmr_ticks = 0;
 #endif
 
-    uchar_t tmr_div = *(uchar_t *)&(apic->tmr_div_cfg.val);
+    uint8_t tmr_div = *(uint8_t *)&(apic->tmr_div_cfg.val);
     uint_t shift_num = 0;
 
 
@@ -921,7 +1614,7 @@ static void apic_update_time(ullong_t cpu_cycles, ullong_t cpu_freq, void * priv
     if ((apic->tmr_init_cnt == 0) || 
        ( (apic->tmr_vec_tbl.tmr_mode == APIC_TMR_ONESHOT) &&
          (apic->tmr_cur_cnt == 0))) {
-       //PrintDebug("APIC timer not yet initialized\n");
+       //PrintDebug("apic %u: core %u: APIC timer not yet initialized\n",apic->lapic_id.val,info->vcpu_id);
        return;
     }
 
@@ -952,92 +1645,249 @@ static void apic_update_time(ullong_t cpu_cycles, ullong_t cpu_freq, void * priv
            shift_num = 7;
            break;
        default:
-           PrintError("Invalid Timer Divider configuration\n");
+           PrintError("apic %u: core %u: Invalid Timer Divider configuration\n",
+                      apic->lapic_id.val, core->vcpu_id);
            return;
     }
 
     tmr_ticks = cpu_cycles >> shift_num;
-    PrintDebug("Timer Ticks: %p\n", (void *)tmr_ticks);
+    //    PrintDebug("Timer Ticks: %p\n", (void *)tmr_ticks);
 
     if (tmr_ticks < apic->tmr_cur_cnt) {
        apic->tmr_cur_cnt -= tmr_ticks;
+#ifdef V3_CONFIG_APIC_ENQUEUE_MISSED_TMR_IRQS
+       if (apic->missed_ints && !apic_intr_pending(core, priv_data)) {
+           PrintDebug("apic %u: core %u: Injecting queued APIC timer interrupt.\n",
+                      apic->lapic_id.val, core->vcpu_id);
+           apic_inject_timer_intr(core, priv_data);
+           apic->missed_ints--;
+       }
+#endif /* CONFIG_APIC_ENQUEUE_MISSED_TMR_IRQS */ 
     } else {
        tmr_ticks -= apic->tmr_cur_cnt;
        apic->tmr_cur_cnt = 0;
 
-       // raise irq
-       PrintDebug("Raising APIC Timer interrupt (periodic=%d) (icnt=%d) (div=%d)\n", 
-                  apic->tmr_vec_tbl.tmr_mode, apic->tmr_init_cnt, shift_num);
+       apic_inject_timer_intr(core, priv_data);
 
-       if (activate_internal_irq(apic, APIC_TMR_INT) == -1) {
-           PrintError("Could not raise Timer interrupt\n");
-       }
-    
        if (apic->tmr_vec_tbl.tmr_mode == APIC_TMR_PERIODIC) {
+           int queued_ints = tmr_ticks / apic->tmr_init_cnt;
            tmr_ticks = tmr_ticks % apic->tmr_init_cnt;
            apic->tmr_cur_cnt = apic->tmr_init_cnt - tmr_ticks;
+           apic->missed_ints += queued_ints;
        }
     }
 
-
+    return;
 }
 
-
-
 static struct intr_ctrl_ops intr_ops = {
     .intr_pending = apic_intr_pending,
     .get_intr_number = apic_get_intr_number,
-    .raise_intr = apic_raise_intr,
     .begin_irq = apic_begin_irq,
-    .lower_intr = apic_lower_intr, 
 };
 
 
-static struct vm_timer_ops timer_ops = {
-    .update_time = apic_update_time,
+static struct v3_timer_ops timer_ops = {
+    .update_timer = apic_update_time,
 };
 
 
-static int apic_init(struct vm_device * dev) {
-    struct guest_info * info = dev->vm;
-    struct apic_state * apic = (struct apic_state *)(dev->private_data);
 
-    v3_register_intr_controller(dev->vm, &intr_ops, dev);
-    v3_add_timer(dev->vm, &timer_ops, dev);
 
-    init_apic_state(apic);
+static int apic_free(struct apic_dev_state * apic_dev) {
+    int i = 0;
+    struct v3_vm_info * vm = NULL;
 
-    v3_hook_msr(info, BASE_ADDR_MSR, read_apic_msr, write_apic_msr, dev);
+    for (i = 0; i < apic_dev->num_apics; i++) {
+       struct apic_state * apic = &(apic_dev->apics[i]);
+       struct guest_info * core = apic->core;
+       
+       vm = core->vm_info;
 
-    v3_hook_full_mem(info, apic->base_addr, apic->base_addr + PAGE_SIZE_4KB, apic_read, apic_write, dev);
+       v3_remove_intr_controller(core, apic->controller_handle);
+
+       if (apic->timer) {
+           v3_remove_timer(core, apic->timer);
+       }
+
+       // unhook memory
+
+    }
+
+    v3_unhook_msr(vm, BASE_ADDR_MSR);
+
+    V3_Free(apic_dev);
+    return 0;
+}
+
+#ifdef V3_CONFIG_CHECKPOINT
+static int apic_save(struct v3_chkpt_ctx * ctx, void * private_data) {
+    struct apic_dev_state * apic_state = (struct apic_dev_state *)private_data;
+    int i = 0;
+
+    V3_CHKPT_STD_SAVE(ctx, apic_state->num_apics);
+
+    //V3_CHKPT_STD_SAVE(ctx,apic_state->state_lock);
+    for (i = 0; i < apic_state->num_apics; i++) {
+
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].base_addr);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].base_addr_msr);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].lapic_id);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].apic_ver);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].ext_apic_ctrl);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].local_vec_tbl);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].tmr_vec_tbl);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].tmr_div_cfg);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].lint0_vec_tbl);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].lint1_vec_tbl);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].perf_ctr_loc_vec_tbl);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].therm_loc_vec_tbl);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].err_vec_tbl);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].err_status);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].spurious_int);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].int_cmd);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].log_dst);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].dst_fmt);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].arb_prio);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].task_prio);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].proc_prio);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].ext_apic_feature);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].spec_eoi);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].tmr_cur_cnt);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].tmr_init_cnt);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].ext_intr_vec_tbl);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].rem_rd_data);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].ipi_state);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].int_req_reg);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].int_svc_reg);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].int_en_reg);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].trig_mode_reg);
+       V3_CHKPT_STD_SAVE(ctx, apic_state->apics[i].eoi);
+
+    }
 
     return 0;
 }
 
-static int apic_deinit(struct vm_device * dev) {
-    struct guest_info * info = dev->vm;
+static int apic_load(struct v3_chkpt_ctx * ctx, void * private_data) {
+    struct apic_dev_state *apic_state = (struct apic_dev_state *)private_data;
+    int i = 0;
+
+    V3_CHKPT_STD_LOAD(ctx,apic_state->num_apics);
+
+    for (i = 0; i < apic_state->num_apics; i++) {
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].base_addr);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].base_addr_msr);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].lapic_id);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].apic_ver);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].ext_apic_ctrl);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].local_vec_tbl);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].tmr_vec_tbl);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].tmr_div_cfg);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].lint0_vec_tbl);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].lint1_vec_tbl);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].perf_ctr_loc_vec_tbl);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].therm_loc_vec_tbl);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].err_vec_tbl);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].err_status);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].spurious_int);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].int_cmd);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].log_dst);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].dst_fmt);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].arb_prio);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].task_prio);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].proc_prio);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].ext_apic_feature);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].spec_eoi);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].tmr_cur_cnt);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].tmr_init_cnt);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].ext_intr_vec_tbl);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].rem_rd_data);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].ipi_state);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].int_req_reg);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].int_svc_reg);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].int_en_reg);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].trig_mode_reg);
+       V3_CHKPT_STD_LOAD(ctx, apic_state->apics[i].eoi);
+    }
 
-    v3_unhook_msr(info, BASE_ADDR_MSR);
 
     return 0;
 }
 
+#endif
 
-static struct vm_device_ops dev_ops = {
-    .init = apic_init,
-    .deinit = apic_deinit,
-    .reset = NULL,
-    .start = NULL,
-    .stop = NULL,
+static struct v3_device_ops dev_ops = {
+    .free = (int (*)(void *))apic_free,
+#ifdef V3_CONFIG_CHECKPOINT
+    .save = apic_save,
+    .load = apic_load
+#endif
 };
 
 
-struct vm_device * v3_create_apic() {
-    PrintDebug("Creating APIC\n");
 
-    struct apic_state * apic = (struct apic_state *)V3_Malloc(sizeof(struct apic_state));
+static int apic_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
+    char * dev_id = v3_cfg_val(cfg, "ID");
+    struct apic_dev_state * apic_dev = NULL;
+    int i = 0;
 
-    struct vm_device * device = v3_create_device("APIC", &dev_ops, apic);
-  
-    return device;
+    PrintDebug("apic: creating an APIC for each core\n");
+
+    apic_dev = (struct apic_dev_state *)V3_Malloc(sizeof(struct apic_dev_state) + 
+                                                 sizeof(struct apic_state) * vm->num_cores);
+
+    apic_dev->num_apics = vm->num_cores;
+    v3_lock_init(&(apic_dev->state_lock));
+
+    struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, apic_dev);
+
+    if (dev == NULL) {
+       PrintError("apic: Could not attach device %s\n", dev_id);
+       V3_Free(apic_dev);
+       return -1;
+    }
+
+    
+    for (i = 0; i < vm->num_cores; i++) {
+       struct apic_state * apic = &(apic_dev->apics[i]);
+       struct guest_info * core = &(vm->cores[i]);
+
+       apic->core = core;
+
+       init_apic_state(apic, i);
+
+       apic->controller_handle = v3_register_intr_controller(core, &intr_ops, apic_dev);
+
+       apic->timer = v3_add_timer(core, &timer_ops, apic_dev);
+
+       if (apic->timer == NULL) {
+           PrintError("APIC: Failed to attach timer to core %d\n", i);
+           v3_remove_device(dev);
+           return -1;
+       }
+
+       v3_hook_full_mem(vm, core->vcpu_id, apic->base_addr, apic->base_addr + PAGE_SIZE_4KB, apic_read, apic_write, apic_dev);
+
+       PrintDebug("apic %u: (setup device): done, my id is %u\n", i, apic->lapic_id.val);
+    }
+
+#ifdef V3_CONFIG_DEBUG_APIC
+    for (i = 0; i < vm->num_cores; i++) {
+       struct apic_state * apic = &(apic_dev->apics[i]);
+       PrintDebug("apic: sanity check: apic %u (at %p) has id %u and msr value %llx and core at %p\n",
+                  i, apic, apic->lapic_id.val, apic->base_addr_msr.value,apic->core);
+    }
+#endif
+
+
+    PrintDebug("apic: priv_data is at %p\n", apic_dev);
+
+    v3_hook_msr(vm, BASE_ADDR_MSR, read_apic_msr, write_apic_msr, apic_dev);
+
+    return 0;
 }
+
+
+
+device_register("LAPIC", apic_init)