Modify handling of writes to the vlapic ICR_TIMER, DCR_TIMER, ICRLO and ESR

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.

We can no longer rely on the value of 'icr_timer' on the APIC page
in the callout handler. With APIC register virtualization the value of
'icr_timer' will be updated by the processor in guest-context before an
APIC-write VM-exit.

Clear the 'delivery status' bit in the ICRLO register in the write handler.
With APIC register virtualization the write happens in guest-context and
we cannot prevent a (buggy) guest from setting this bit.
This commit is contained in:
Neel Natu 2013-12-27 20:18:19 +00:00
parent dfc586a324
commit fafe884473
2 changed files with 42 additions and 36 deletions

View File

@ -100,14 +100,9 @@ do { \
/*
* The 'vlapic->timer_mtx' is used to provide mutual exclusion between the
* vlapic_callout_handler() and vcpu accesses to the following registers:
* - initial count register aka icr_timer
* - current count register aka ccr_timer
* - divide config register aka dcr_timer
* vlapic_callout_handler() and vcpu accesses to:
* - timer_freq_bt, timer_period_bt, timer_fire_bt
* - timer LVT register
*
* Note that the vlapic_callout_handler() does not write to any of these
* registers so they can be safely read from the vcpu context without locking.
*/
#define VLAPIC_TIMER_LOCK(vlapic) mtx_lock_spin(&((vlapic)->timer_mtx))
#define VLAPIC_TIMER_UNLOCK(vlapic) mtx_unlock_spin(&((vlapic)->timer_mtx))
@ -273,8 +268,8 @@ vlapic_get_ccr(struct vlapic *vlapic)
return (ccr);
}
static void
vlapic_set_dcr(struct vlapic *vlapic, uint32_t dcr)
void
vlapic_dcr_write_handler(struct vlapic *vlapic)
{
struct LAPIC *lapic;
int divisor;
@ -282,9 +277,9 @@ vlapic_set_dcr(struct vlapic *vlapic, uint32_t dcr)
lapic = vlapic->apic_page;
VLAPIC_TIMER_LOCK(vlapic);
lapic->dcr_timer = dcr;
divisor = vlapic_timer_divisor(dcr);
VLAPIC_CTR2(vlapic, "vlapic dcr_timer=%#x, divisor=%d", dcr, divisor);
divisor = vlapic_timer_divisor(lapic->dcr_timer);
VLAPIC_CTR2(vlapic, "vlapic dcr_timer=%#x, divisor=%d",
lapic->dcr_timer, divisor);
/*
* Update the timer frequency and the timer period.
@ -299,8 +294,8 @@ vlapic_set_dcr(struct vlapic *vlapic, uint32_t dcr)
VLAPIC_TIMER_UNLOCK(vlapic);
}
static void
vlapic_update_errors(struct vlapic *vlapic)
void
vlapic_esr_write_handler(struct vlapic *vlapic)
{
struct LAPIC *lapic;
@ -323,7 +318,9 @@ vlapic_reset(struct vlapic *vlapic)
lapic->dfr = 0xffffffff;
lapic->svr = APIC_SVR_VECTOR;
vlapic_mask_lvts(vlapic);
vlapic_set_dcr(vlapic, 0);
lapic->dcr_timer = 0;
vlapic_dcr_write_handler(vlapic);
if (vlapic->vcpuid == 0)
vlapic->boot_state = BS_RUNNING; /* BSP */
@ -711,8 +708,6 @@ vlapic_callout_handler(void *arg)
callout_deactivate(&vlapic->callout);
KASSERT(vlapic->apic_page->icr_timer != 0, ("timer is disabled"));
vlapic_fire_timer(vlapic);
if (vlapic_periodic_timer(vlapic)) {
@ -757,16 +752,17 @@ vlapic_callout_handler(void *arg)
VLAPIC_TIMER_UNLOCK(vlapic);
}
static void
vlapic_set_icr_timer(struct vlapic *vlapic, uint32_t icr_timer)
void
vlapic_icrtmr_write_handler(struct vlapic *vlapic)
{
struct LAPIC *lapic;
sbintime_t sbt;
uint32_t icr_timer;
VLAPIC_TIMER_LOCK(vlapic);
lapic = vlapic->apic_page;
lapic->icr_timer = icr_timer;
icr_timer = lapic->icr_timer;
vlapic->timer_period_bt = vlapic->timer_freq_bt;
bintime_mul(&vlapic->timer_period_bt, icr_timer);
@ -888,16 +884,22 @@ vlapic_calcdest(struct vm *vm, cpuset_t *dmask, uint32_t dest, bool phys,
static VMM_STAT_ARRAY(IPIS_SENT, VM_MAXCPU, "ipis sent to vcpu");
static int
lapic_process_icr(struct vlapic *vlapic, uint64_t icrval, bool *retu)
int
vlapic_icrlo_write_handler(struct vlapic *vlapic, bool *retu)
{
int i;
bool phys;
cpuset_t dmask;
uint64_t icrval;
uint32_t dest, vec, mode;
struct vlapic *vlapic2;
struct vm_exit *vmexit;
struct LAPIC *lapic;
lapic = vlapic->apic_page;
lapic->icr_lo &= ~APIC_DELSTAT_PEND;
icrval = ((uint64_t)lapic->icr_hi << 32) | lapic->icr_lo;
if (x2apic(vlapic))
dest = icrval >> 32;
else
@ -1088,7 +1090,7 @@ vlapic_svr_write_handler(struct vlapic *vlapic)
*/
VLAPIC_CTR0(vlapic, "vlapic is software-enabled");
if (vlapic_periodic_timer(vlapic))
vlapic_set_icr_timer(vlapic, lapic->icr_timer);
vlapic_icrtmr_write_handler(vlapic);
}
}
}
@ -1155,6 +1157,8 @@ vlapic_read(struct vlapic *vlapic, uint64_t offset, uint64_t *data, bool *retu)
break;
case APIC_OFFSET_ICR_LOW:
*data = lapic->icr_lo;
if (x2apic(vlapic))
*data |= (uint64_t)lapic->icr_hi << 32;
break;
case APIC_OFFSET_ICR_HI:
*data = lapic->icr_hi;
@ -1224,32 +1228,30 @@ vlapic_write(struct vlapic *vlapic, uint64_t offset, uint64_t data, bool *retu)
vlapic_svr_write_handler(vlapic);
break;
case APIC_OFFSET_ICR_LOW:
if (!x2apic(vlapic)) {
data &= 0xffffffff;
data |= (uint64_t)lapic->icr_hi << 32;
}
retval = lapic_process_icr(vlapic, data, retu);
lapic->icr_lo = data;
if (x2apic(vlapic))
lapic->icr_hi = data >> 32;
retval = vlapic_icrlo_write_handler(vlapic, retu);
break;
case APIC_OFFSET_ICR_HI:
if (!x2apic(vlapic)) {
retval = 0;
lapic->icr_hi = data;
}
lapic->icr_hi = data;
break;
case APIC_OFFSET_CMCI_LVT:
case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT:
vlapic_set_lvt(vlapic, offset, data);
break;
case APIC_OFFSET_TIMER_ICR:
vlapic_set_icr_timer(vlapic, data);
lapic->icr_timer = data;
vlapic_icrtmr_write_handler(vlapic);
break;
case APIC_OFFSET_TIMER_DCR:
vlapic_set_dcr(vlapic, data);
lapic->dcr_timer = data;
vlapic_dcr_write_handler(vlapic);
break;
case APIC_OFFSET_ESR:
vlapic_update_errors(vlapic);
vlapic_esr_write_handler(vlapic);
break;
case APIC_OFFSET_VER:
case APIC_OFFSET_APR:

View File

@ -76,4 +76,8 @@ void vlapic_id_write_handler(struct vlapic *vlapic);
void vlapic_ldr_write_handler(struct vlapic *vlapic);
void vlapic_dfr_write_handler(struct vlapic *vlapic);
void vlapic_svr_write_handler(struct vlapic *vlapic);
void vlapic_esr_write_handler(struct vlapic *vlapic);
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);
#endif /* _VLAPIC_H_ */