Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 28 Dec 2013 00:20:55 +0000 (UTC)
From:      Neel Natu <neel@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r259978 - head/sys/amd64/vmm/io
Message-ID:  <201312280020.rBS0Ktlo097572@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201312280020.rBS0Ktlo097572>