From owner-svn-src-head@FreeBSD.ORG Sat Dec 28 00:20:56 2013 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 1873C882; Sat, 28 Dec 2013 00:20:56 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 03FC31D85; Sat, 28 Dec 2013 00:20:56 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.7/8.14.7) with ESMTP id rBS0KthJ097575; Sat, 28 Dec 2013 00:20:55 GMT (envelope-from neel@svn.freebsd.org) Received: (from neel@localhost) by svn.freebsd.org (8.14.7/8.14.7/Submit) id rBS0Ktlo097572; Sat, 28 Dec 2013 00:20:55 GMT (envelope-from neel@svn.freebsd.org) Message-Id: <201312280020.rBS0Ktlo097572@svn.freebsd.org> From: Neel Natu Date: Sat, 28 Dec 2013 00:20:55 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r259978 - head/sys/amd64/vmm/io X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.17 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 28 Dec 2013 00:20:56 -0000 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 + /* * 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);