Author: neel
Date: Sat Dec 28 00:20:55 2013
New Revision: 259978
URL: http://svnweb.freebsd.org/changeset/base/259978

Log:
  Modify handling of writes to the vlapic LVT registers.
  
  The handler is now called after the register value is updated in the virtual
  APIC page. This will make it easier to handle APIC-write VM-exits with APIC
  register virtualization turned on.
  
  This also implies that we need to keep a snapshot of the last value written
  to a LVT register. We can no longer rely on the LVT registers in the APIC
  page to be "clean" because the guest can write anything to it before the
  hypervisor has had a chance to sanitize it.

Modified:
  head/sys/amd64/vmm/io/vlapic.c
  head/sys/amd64/vmm/io/vlapic.h
  head/sys/amd64/vmm/io/vlapic_priv.h

Modified: head/sys/amd64/vmm/io/vlapic.c
==============================================================================
--- head/sys/amd64/vmm/io/vlapic.c      Sat Dec 28 00:16:58 2013        
(r259977)
+++ head/sys/amd64/vmm/io/vlapic.c      Sat Dec 28 00:20:55 2013        
(r259978)
@@ -94,7 +94,6 @@ do {                                                          
        \
 #define        PRIO(x)                 ((x) >> 4)
 
 #define VLAPIC_VERSION         (16)
-#define VLAPIC_MAXLVT_ENTRIES  (APIC_LVT_CMCI)
 
 #define        x2apic(vlapic)  (((vlapic)->msr_apicbase & APICBASE_X2APIC) ? 1 
: 0)
 
@@ -212,20 +211,6 @@ vlapic_timer_divisor(uint32_t dcr)
        }
 }
 
-static void
-vlapic_mask_lvts(struct vlapic *vlapic)
-{
-       struct LAPIC *lapic = vlapic->apic_page;
-
-       lapic->lvt_cmci |= APIC_LVT_M;
-       lapic->lvt_timer |= APIC_LVT_M;
-       lapic->lvt_thermal |= APIC_LVT_M;
-       lapic->lvt_pcint |= APIC_LVT_M;
-       lapic->lvt_lint0 |= APIC_LVT_M;
-       lapic->lvt_lint1 |= APIC_LVT_M;
-       lapic->lvt_error |= APIC_LVT_M;
-}
-
 #if 0
 static inline void
 vlapic_dump_lvt(uint32_t offset, uint32_t *lvt)
@@ -304,32 +289,6 @@ vlapic_esr_write_handler(struct vlapic *
        vlapic->esr_pending = 0;
 }
 
-static void
-vlapic_reset(struct vlapic *vlapic)
-{
-       struct LAPIC *lapic;
-       
-       lapic = vlapic->apic_page;
-       bzero(lapic, sizeof(struct LAPIC));
-
-       lapic->id = vlapic_get_id(vlapic);
-       lapic->version = VLAPIC_VERSION;
-       lapic->version |= (VLAPIC_MAXLVT_ENTRIES << MAXLVTSHIFT);
-       lapic->dfr = 0xffffffff;
-       lapic->svr = APIC_SVR_VECTOR;
-       vlapic_mask_lvts(vlapic);
-
-       lapic->dcr_timer = 0;
-       vlapic_dcr_write_handler(vlapic);
-
-       if (vlapic->vcpuid == 0)
-               vlapic->boot_state = BS_RUNNING;        /* BSP */
-       else
-               vlapic->boot_state = BS_INIT;           /* AP */
-
-       vlapic->svr_last = lapic->svr;
-}
-
 void
 vlapic_set_intr_ready(struct vlapic *vlapic, int vector, bool level)
 {
@@ -388,24 +347,65 @@ vlapic_get_lvtptr(struct vlapic *vlapic,
        }
 }
 
+static __inline int
+lvt_off_to_idx(uint32_t offset)
+{
+       int index;
+
+       switch (offset) {
+       case APIC_OFFSET_CMCI_LVT:
+               index = APIC_LVT_CMCI;
+               break;
+       case APIC_OFFSET_TIMER_LVT:
+               index = APIC_LVT_TIMER;
+               break;
+       case APIC_OFFSET_THERM_LVT:
+               index = APIC_LVT_THERMAL;
+               break;
+       case APIC_OFFSET_PERF_LVT:
+               index = APIC_LVT_PMC;
+               break;
+       case APIC_OFFSET_LINT0_LVT:
+               index = APIC_LVT_LINT0;
+               break;
+       case APIC_OFFSET_LINT1_LVT:
+               index = APIC_LVT_LINT1;
+               break;
+       case APIC_OFFSET_ERROR_LVT:
+               index = APIC_LVT_ERROR;
+               break;
+       default:
+               index = -1;
+               break;
+       }
+       KASSERT(index >= 0 && index <= VLAPIC_MAXLVT_INDEX, ("lvt_off_to_idx: "
+           "invalid lvt index %d for offset %#x", index, offset));
+
+       return (index);
+}
+
 static __inline uint32_t
 vlapic_get_lvt(struct vlapic *vlapic, uint32_t offset)
 {
+       int idx;
+       uint32_t val;
 
-       return (*vlapic_get_lvtptr(vlapic, offset));
+       idx = lvt_off_to_idx(offset);
+       val = atomic_load_acq_32(&vlapic->lvt_last[idx]);
+       return (val);
 }
 
-static void
-vlapic_set_lvt(struct vlapic *vlapic, uint32_t offset, uint32_t val)
+void
+vlapic_lvt_write_handler(struct vlapic *vlapic, uint32_t offset)
 {
-       uint32_t *lvtptr, mask;
+       uint32_t *lvtptr, mask, val;
        struct LAPIC *lapic;
+       int idx;
        
        lapic = vlapic->apic_page;
        lvtptr = vlapic_get_lvtptr(vlapic, offset);     
-
-       if (offset == APIC_OFFSET_TIMER_LVT)
-               VLAPIC_TIMER_LOCK(vlapic);
+       val = *lvtptr;
+       idx = lvt_off_to_idx(offset);
 
        if (!(lapic->svr & APIC_SVR_ENABLE))
                val |= APIC_LVT_M;
@@ -424,10 +424,36 @@ vlapic_set_lvt(struct vlapic *vlapic, ui
                mask |= APIC_LVT_DM;
                break;
        }
-       *lvtptr = val & mask;
+       val &= mask;
+       *lvtptr = val;
+       atomic_store_rel_32(&vlapic->lvt_last[idx], val);
+}
+
+static void
+vlapic_mask_lvts(struct vlapic *vlapic)
+{
+       struct LAPIC *lapic = vlapic->apic_page;
+
+       lapic->lvt_cmci |= APIC_LVT_M;
+       vlapic_lvt_write_handler(vlapic, APIC_OFFSET_CMCI_LVT);
+
+       lapic->lvt_timer |= APIC_LVT_M;
+       vlapic_lvt_write_handler(vlapic, APIC_OFFSET_TIMER_LVT);
+
+       lapic->lvt_thermal |= APIC_LVT_M;
+       vlapic_lvt_write_handler(vlapic, APIC_OFFSET_THERM_LVT);
+
+       lapic->lvt_pcint |= APIC_LVT_M;
+       vlapic_lvt_write_handler(vlapic, APIC_OFFSET_PERF_LVT);
+
+       lapic->lvt_lint0 |= APIC_LVT_M;
+       vlapic_lvt_write_handler(vlapic, APIC_OFFSET_LINT0_LVT);
+
+       lapic->lvt_lint1 |= APIC_LVT_M;
+       vlapic_lvt_write_handler(vlapic, APIC_OFFSET_LINT1_LVT);
 
-       if (offset == APIC_OFFSET_TIMER_LVT)
-               VLAPIC_TIMER_UNLOCK(vlapic);
+       lapic->lvt_error |= APIC_LVT_M;
+       vlapic_lvt_write_handler(vlapic, APIC_OFFSET_ERROR_LVT);
 }
 
 static int
@@ -648,7 +674,7 @@ vlapic_fire_cmci(struct vlapic *vlapic)
        }
 }
 
-static VMM_STAT_ARRAY(LVTS_TRIGGERRED, VLAPIC_MAXLVT_ENTRIES,
+static VMM_STAT_ARRAY(LVTS_TRIGGERRED, VLAPIC_MAXLVT_INDEX + 1,
     "lvts triggered");
 
 int
@@ -1166,6 +1192,11 @@ vlapic_read(struct vlapic *vlapic, uint6
                case APIC_OFFSET_CMCI_LVT:
                case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT:
                        *data = vlapic_get_lvt(vlapic, offset); 
+#ifdef INVARIANTS
+                       reg = vlapic_get_lvtptr(vlapic, offset);
+                       KASSERT(*data == *reg, ("inconsistent lvt value at "
+                           "offset %#lx: %#lx/%#x", offset, *data, *reg));
+#endif
                        break;
                case APIC_OFFSET_TIMER_ICR:
                        *data = lapic->icr_timer;
@@ -1190,6 +1221,7 @@ int
 vlapic_write(struct vlapic *vlapic, uint64_t offset, uint64_t data, bool *retu)
 {
        struct LAPIC    *lapic = vlapic->apic_page;
+       uint32_t        *regptr;
        int             retval;
 
        KASSERT((offset & 0xf) == 0 && offset < PAGE_SIZE,
@@ -1238,7 +1270,9 @@ vlapic_write(struct vlapic *vlapic, uint
                        break;
                case APIC_OFFSET_CMCI_LVT:
                case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT:
-                       vlapic_set_lvt(vlapic, offset, data);
+                       regptr = vlapic_get_lvtptr(vlapic, offset);
+                       *regptr = data;
+                       vlapic_lvt_write_handler(vlapic, offset);
                        break;
                case APIC_OFFSET_TIMER_ICR:
                        lapic->icr_timer = data;
@@ -1269,6 +1303,32 @@ vlapic_write(struct vlapic *vlapic, uint
        return (retval);
 }
 
+static void
+vlapic_reset(struct vlapic *vlapic)
+{
+       struct LAPIC *lapic;
+       
+       lapic = vlapic->apic_page;
+       bzero(lapic, sizeof(struct LAPIC));
+
+       lapic->id = vlapic_get_id(vlapic);
+       lapic->version = VLAPIC_VERSION;
+       lapic->version |= (VLAPIC_MAXLVT_INDEX << MAXLVTSHIFT);
+       lapic->dfr = 0xffffffff;
+       lapic->svr = APIC_SVR_VECTOR;
+       vlapic_mask_lvts(vlapic);
+
+       lapic->dcr_timer = 0;
+       vlapic_dcr_write_handler(vlapic);
+
+       if (vlapic->vcpuid == 0)
+               vlapic->boot_state = BS_RUNNING;        /* BSP */
+       else
+               vlapic->boot_state = BS_INIT;           /* AP */
+
+       vlapic->svr_last = lapic->svr;
+}
+
 void
 vlapic_init(struct vlapic *vlapic)
 {

Modified: head/sys/amd64/vmm/io/vlapic.h
==============================================================================
--- head/sys/amd64/vmm/io/vlapic.h      Sat Dec 28 00:16:58 2013        
(r259977)
+++ head/sys/amd64/vmm/io/vlapic.h      Sat Dec 28 00:20:55 2013        
(r259978)
@@ -80,4 +80,5 @@ void vlapic_esr_write_handler(struct vla
 int vlapic_icrlo_write_handler(struct vlapic *vlapic, bool *retu);
 void vlapic_icrtmr_write_handler(struct vlapic *vlapic);
 void vlapic_dcr_write_handler(struct vlapic *vlapic);
+void vlapic_lvt_write_handler(struct vlapic *vlapic, uint32_t offset);
 #endif /* _VLAPIC_H_ */

Modified: head/sys/amd64/vmm/io/vlapic_priv.h
==============================================================================
--- head/sys/amd64/vmm/io/vlapic_priv.h Sat Dec 28 00:16:58 2013        
(r259977)
+++ head/sys/amd64/vmm/io/vlapic_priv.h Sat Dec 28 00:20:55 2013        
(r259978)
@@ -29,6 +29,8 @@
 #ifndef _VLAPIC_PRIV_H_
 #define        _VLAPIC_PRIV_H_
 
+#include <x86/apicreg.h>
+
 /*
  * APIC Register:              Offset     Description
  */
@@ -91,6 +93,8 @@ enum boot_state {
  */
 #define        ISRVEC_STK_SIZE         (16 + 1)
 
+#define VLAPIC_MAXLVT_INDEX    APIC_LVT_CMCI
+
 struct vlapic {
        struct vm               *vm;
        int                     vcpuid;
@@ -111,12 +115,20 @@ struct vlapic {
         * The vector on the top of the stack is used to compute the
         * Processor Priority in conjunction with the TPR.
         */
-       uint8_t                  isrvec_stk[ISRVEC_STK_SIZE];
-       int                      isrvec_stk_top;
+       uint8_t         isrvec_stk[ISRVEC_STK_SIZE];
+       int             isrvec_stk_top;
+
+       uint64_t        msr_apicbase;
+       enum boot_state boot_state;
 
-       uint64_t                msr_apicbase;
-       enum boot_state         boot_state;
-       uint32_t                svr_last;
+       /*
+        * Copies of some registers in the virtual APIC page. We do this for
+        * a couple of different reasons:
+        * - to be able to detect what changed (e.g. svr_last)
+        * - to maintain a coherent snapshot of the register (e.g. lvt_last)
+        */
+       uint32_t        svr_last;
+       uint32_t        lvt_last[VLAPIC_MAXLVT_INDEX + 1];
 };
 
 void vlapic_init(struct vlapic *vlapic);
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to