Modify handling of writes to the vlapic ID, LDR and DFR registers.

The handlers are 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.

Additionally, we need to ensure that the value of these registers is always
correctly reflected in the virtual APIC page, because there is no VM exit
when the guest reads these registers with APIC register virtualization.
This commit is contained in:
Neel Natu 2013-12-26 19:58:30 +00:00
parent 4ca1cd1d63
commit 3f0ddc7c5c
2 changed files with 81 additions and 51 deletions

View File

@ -125,72 +125,71 @@ vlapic_get_id(struct vlapic *vlapic)
return (vlapic->vcpuid << 24);
}
static __inline uint32_t
vlapic_get_ldr(struct vlapic *vlapic)
static uint32_t
x2apic_ldr(struct vlapic *vlapic)
{
struct LAPIC *lapic;
int apicid;
uint32_t ldr;
lapic = vlapic->apic_page;
if (x2apic(vlapic)) {
apicid = vlapic_get_id(vlapic);
ldr = 1 << (apicid & 0xf);
ldr |= (apicid & 0xffff0) << 12;
return (ldr);
} else
return (lapic->ldr);
apicid = vlapic_get_id(vlapic);
ldr = 1 << (apicid & 0xf);
ldr |= (apicid & 0xffff0) << 12;
return (ldr);
}
static __inline uint32_t
vlapic_get_dfr(struct vlapic *vlapic)
void
vlapic_dfr_write_handler(struct vlapic *vlapic)
{
struct LAPIC *lapic;
lapic = vlapic->apic_page;
if (x2apic(vlapic))
return (0);
else
return (lapic->dfr);
}
static void
vlapic_set_dfr(struct vlapic *vlapic, uint32_t data)
{
uint32_t dfr;
struct LAPIC *lapic;
if (x2apic(vlapic)) {
VM_CTR1(vlapic->vm, "write to DFR in x2apic mode: %#x", data);
VM_CTR1(vlapic->vm, "ignoring write to DFR in x2apic mode: %#x",
lapic->dfr);
lapic->dfr = 0;
return;
}
lapic = vlapic->apic_page;
dfr = (lapic->dfr & APIC_DFR_RESERVED) | (data & APIC_DFR_MODEL_MASK);
if ((dfr & APIC_DFR_MODEL_MASK) == APIC_DFR_MODEL_FLAT)
lapic->dfr &= APIC_DFR_MODEL_MASK;
lapic->dfr |= APIC_DFR_RESERVED;
if ((lapic->dfr & APIC_DFR_MODEL_MASK) == APIC_DFR_MODEL_FLAT)
VLAPIC_CTR0(vlapic, "vlapic DFR in Flat Model");
else if ((dfr & APIC_DFR_MODEL_MASK) == APIC_DFR_MODEL_CLUSTER)
else if ((lapic->dfr & APIC_DFR_MODEL_MASK) == APIC_DFR_MODEL_CLUSTER)
VLAPIC_CTR0(vlapic, "vlapic DFR in Cluster Model");
else
VLAPIC_CTR1(vlapic, "vlapic DFR in Unknown Model %#x", dfr);
lapic->dfr = dfr;
VLAPIC_CTR1(vlapic, "DFR in Unknown Model %#x", lapic->dfr);
}
static void
vlapic_set_ldr(struct vlapic *vlapic, uint32_t data)
void
vlapic_ldr_write_handler(struct vlapic *vlapic)
{
struct LAPIC *lapic;
lapic = vlapic->apic_page;
/* LDR is read-only in x2apic mode */
if (x2apic(vlapic)) {
VLAPIC_CTR1(vlapic, "write to LDR in x2apic mode: %#x", data);
return;
VLAPIC_CTR1(vlapic, "ignoring write to LDR in x2apic mode: %#x",
lapic->ldr);
lapic->ldr = x2apic_ldr(vlapic);
} else {
lapic->ldr &= ~APIC_LDR_RESERVED;
VLAPIC_CTR1(vlapic, "vlapic LDR set to %#x", lapic->ldr);
}
}
void
vlapic_id_write_handler(struct vlapic *vlapic)
{
struct LAPIC *lapic;
/*
* We don't allow the ID register to be modified so reset it back to
* its default value.
*/
lapic = vlapic->apic_page;
lapic->ldr = data & ~APIC_LDR_RESERVED;
VLAPIC_CTR1(vlapic, "vlapic LDR set to %#x", lapic->ldr);
lapic->id = vlapic_get_id(vlapic);
}
static int
@ -314,6 +313,7 @@ vlapic_reset(struct vlapic *vlapic)
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;
@ -843,8 +843,8 @@ vlapic_calcdest(struct vm *vm, cpuset_t *dmask, uint32_t dest, bool phys,
CPU_CLR(vcpuid, &amask);
vlapic = vm_lapic(vm, vcpuid);
dfr = vlapic_get_dfr(vlapic);
ldr = vlapic_get_ldr(vlapic);
dfr = vlapic->apic_page->dfr;
ldr = vlapic->apic_page->ldr;
if ((dfr & APIC_DFR_MODEL_MASK) ==
APIC_DFR_MODEL_FLAT) {
@ -1099,7 +1099,7 @@ vlapic_read(struct vlapic *vlapic, uint64_t offset, uint64_t *data, bool *retu)
switch(offset)
{
case APIC_OFFSET_ID:
*data = vlapic_get_id(vlapic);
*data = lapic->id;
break;
case APIC_OFFSET_VER:
*data = lapic->version;
@ -1117,10 +1117,10 @@ vlapic_read(struct vlapic *vlapic, uint64_t offset, uint64_t *data, bool *retu)
*data = lapic->eoi;
break;
case APIC_OFFSET_LDR:
*data = vlapic_get_ldr(vlapic);
*data = lapic->ldr;
break;
case APIC_OFFSET_DFR:
*data = vlapic_get_dfr(vlapic);
*data = lapic->dfr;
break;
case APIC_OFFSET_SVR:
*data = lapic->svr;
@ -1178,6 +1178,9 @@ vlapic_write(struct vlapic *vlapic, uint64_t offset, uint64_t data, bool *retu)
struct LAPIC *lapic = vlapic->apic_page;
int retval;
KASSERT((offset & 0xf) == 0 && offset < PAGE_SIZE,
("vlapic_write: invalid offset %#lx", offset));
VLAPIC_CTR2(vlapic, "vlapic write offset %#x, data %#lx", offset, data);
if (offset > sizeof(*lapic)) {
@ -1185,10 +1188,11 @@ vlapic_write(struct vlapic *vlapic, uint64_t offset, uint64_t data, bool *retu)
}
retval = 0;
offset &= ~3;
switch(offset)
{
case APIC_OFFSET_ID:
lapic->id = data;
vlapic_id_write_handler(vlapic);
break;
case APIC_OFFSET_TPR:
lapic->tpr = data & 0xff;
@ -1198,10 +1202,12 @@ vlapic_write(struct vlapic *vlapic, uint64_t offset, uint64_t data, bool *retu)
vlapic_process_eoi(vlapic);
break;
case APIC_OFFSET_LDR:
vlapic_set_ldr(vlapic, data);
lapic->ldr = data;
vlapic_ldr_write_handler(vlapic);
break;
case APIC_OFFSET_DFR:
vlapic_set_dfr(vlapic, data);
lapic->dfr = data;
vlapic_dfr_write_handler(vlapic);
break;
case APIC_OFFSET_SVR:
lapic_set_svr(vlapic, data);
@ -1292,19 +1298,38 @@ vlapic_get_apicbase(struct vlapic *vlapic)
}
void
vlapic_set_apicbase(struct vlapic *vlapic, uint64_t val)
vlapic_set_apicbase(struct vlapic *vlapic, uint64_t new)
{
int err;
struct LAPIC *lapic;
enum x2apic_state state;
uint64_t old;
int err;
err = vm_get_x2apic_state(vlapic->vm, vlapic->vcpuid, &state);
if (err)
panic("vlapic_set_apicbase: err %d fetching x2apic state", err);
if (state == X2APIC_DISABLED)
val &= ~APICBASE_X2APIC;
new &= ~APICBASE_X2APIC;
vlapic->msr_apicbase = val;
old = vlapic->msr_apicbase;
vlapic->msr_apicbase = new;
/*
* If the vlapic is switching between xAPIC and x2APIC modes then
* reset the mode-dependent registers.
*/
if ((old ^ new) & APICBASE_X2APIC) {
lapic = vlapic->apic_page;
lapic->id = vlapic_get_id(vlapic);
if (x2apic(vlapic)) {
lapic->ldr = x2apic_ldr(vlapic);
lapic->dfr = 0;
} else {
lapic->ldr = 0;
lapic->dfr = 0xffffffff;
}
}
}
void

View File

@ -70,4 +70,9 @@ bool vlapic_enabled(struct vlapic *vlapic);
void vlapic_deliver_intr(struct vm *vm, bool level, uint32_t dest, bool phys,
int delmode, int vec);
void vlapic_post_intr(struct vlapic *vlapic, int hostcpu);
/* APIC write handlers */
void vlapic_id_write_handler(struct vlapic *vlapic);
void vlapic_ldr_write_handler(struct vlapic *vlapic);
void vlapic_dfr_write_handler(struct vlapic *vlapic);
#endif /* _VLAPIC_H_ */