hv: vlapic_timer: add vlapic one-shot/periodic timer support

Enable guest LAPIC one-shot/periodic timer support.

Change-Id: I368e28beaa81d6566de2626bbe26c9f8972f0891
Signed-off-by: Li, Fei1 <fei1.li@intel.com>
This commit is contained in:
Li, Fei1 2018-06-08 09:04:42 +08:00 committed by Jack Ren
parent 47116e8c4b
commit 46f64b55b4
2 changed files with 81 additions and 10 deletions

View File

@ -224,6 +224,15 @@ vlapic_id_write_handler(struct vlapic *vlapic)
lapic->id = vlapic_get_id(vlapic); lapic->id = vlapic_get_id(vlapic);
} }
static inline uint32_t
vlapic_timer_divisor_shift(uint32_t dcr)
{
uint32_t val;
val = ((dcr & 0x3) | ((dcr & 0x8) >> 1));
return ((val + 1) & 0x7);
}
static inline bool static inline bool
vlapic_lvtt_oneshot(struct vlapic *vlapic) vlapic_lvtt_oneshot(struct vlapic *vlapic)
{ {
@ -280,6 +289,32 @@ static void vlapic_reset_timer(struct vlapic *vlapic)
timer->period_in_cycle = 0; timer->period_in_cycle = 0;
} }
static bool
set_expiration(struct vlapic *vlapic)
{
uint64_t now = rdtsc();
uint64_t delta;
struct vlapic_timer *vlapic_timer;
struct timer *timer;
uint32_t tmicr, divisor_shift;
vlapic_timer = &vlapic->vlapic_timer;
tmicr = vlapic_timer->tmicr;
divisor_shift = vlapic_timer->divisor_shift;
if (!tmicr || divisor_shift > 8)
return false;
delta = tmicr << divisor_shift;
timer = &vlapic_timer->timer;
if (vlapic_lvtt_period(vlapic))
timer->period_in_cycle = delta;
timer->fire_tsc = now + delta;
return true;
}
static void vlapic_update_lvtt(struct vlapic *vlapic, static void vlapic_update_lvtt(struct vlapic *vlapic,
uint32_t val) uint32_t val)
{ {
@ -303,19 +338,53 @@ static void vlapic_update_lvtt(struct vlapic *vlapic,
} }
} }
static uint32_t vlapic_get_ccr(__unused struct vlapic *vlapic) static uint32_t vlapic_get_ccr(struct vlapic *vlapic)
{ {
return 0; uint64_t now = rdtsc();
uint64_t remain_count = 0;
struct vlapic_timer *vlapic_timer;
vlapic_timer = &vlapic->vlapic_timer;
if (vlapic_timer->tmicr && !vlapic_lvtt_tsc_deadline(vlapic)) {
uint64_t fire_tsc = vlapic_timer->timer.fire_tsc;
uint32_t divisor_shift = vlapic_timer->divisor_shift;
if (now < fire_tsc)
remain_count = (fire_tsc - now) >> divisor_shift;
}
return remain_count;
} }
static void vlapic_dcr_write_handler(__unused struct vlapic *vlapic) static void vlapic_dcr_write_handler(struct vlapic *vlapic)
{ {
uint32_t divisor_shift;
struct vlapic_timer *vlapic_timer;
struct lapic *lapic = vlapic->apic_page;
vlapic_timer = &vlapic->vlapic_timer;
divisor_shift = vlapic_timer_divisor_shift(lapic->dcr_timer);
vlapic_timer->divisor_shift = divisor_shift;
} }
static void vlapic_icrtmr_write_handler(__unused struct vlapic *vlapic) static void vlapic_icrtmr_write_handler(struct vlapic *vlapic)
{ {
} struct lapic *lapic;
struct vlapic_timer *vlapic_timer;
if (vlapic_lvtt_tsc_deadline(vlapic))
return;
lapic = vlapic->apic_page;
vlapic_timer = &vlapic->vlapic_timer;
vlapic_timer->tmicr = lapic->icr_timer;
del_timer(&vlapic_timer->timer);
if (set_expiration(vlapic))
add_timer(&vlapic_timer->timer);
}
static uint64_t vlapic_get_tsc_deadline_msr(struct vlapic *vlapic) static uint64_t vlapic_get_tsc_deadline_msr(struct vlapic *vlapic)
{ {
@ -1165,10 +1234,12 @@ vlapic_svr_write_handler(struct vlapic *vlapic)
if ((changed & APIC_SVR_ENABLE) != 0) { if ((changed & APIC_SVR_ENABLE) != 0) {
if ((new & APIC_SVR_ENABLE) == 0) { if ((new & APIC_SVR_ENABLE) == 0) {
/* /*
* TODO: The apic is now disabled so stop the apic timer * The apic is now disabled so stop the apic timer
* and mask all the LVT entries. * and mask all the LVT entries.
*/ */
dev_dbg(ACRN_DBG_LAPIC, "vlapic is software-disabled"); dev_dbg(ACRN_DBG_LAPIC, "vlapic is software-disabled");
del_timer(&vlapic->vlapic_timer.timer);
vlapic_mask_lvts(vlapic); vlapic_mask_lvts(vlapic);
/* the only one enabled LINT0-ExtINT vlapic disabled */ /* the only one enabled LINT0-ExtINT vlapic disabled */
if (vlapic->vm->vpic_wire_mode == VPIC_WIRE_NULL) { if (vlapic->vm->vpic_wire_mode == VPIC_WIRE_NULL) {
@ -1183,7 +1254,8 @@ vlapic_svr_write_handler(struct vlapic *vlapic)
*/ */
dev_dbg(ACRN_DBG_LAPIC, "vlapic is software-enabled"); dev_dbg(ACRN_DBG_LAPIC, "vlapic is software-enabled");
if (vlapic_lvtt_period(vlapic)) if (vlapic_lvtt_period(vlapic))
vlapic_icrtmr_write_handler(vlapic); if (set_expiration(vlapic))
add_timer(&vlapic->vlapic_timer.timer);
} }
} }
} }
@ -1302,10 +1374,9 @@ vlapic_read(struct vlapic *vlapic, int mmio_access, uint64_t offset,
if (vlapic_lvtt_tsc_deadline(vlapic)) if (vlapic_lvtt_tsc_deadline(vlapic))
*data = 0; *data = 0;
else else
*data = vlapic_get_ccr(vlapic); *data = lapic->icr_timer;
break; break;
case APIC_OFFSET_TIMER_CCR: case APIC_OFFSET_TIMER_CCR:
/* TODO */
*data = vlapic_get_ccr(vlapic); *data = vlapic_get_ccr(vlapic);
break; break;
case APIC_OFFSET_TIMER_DCR: case APIC_OFFSET_TIMER_DCR:

View File

@ -112,7 +112,7 @@ struct vlapic_timer {
struct timer timer; struct timer timer;
uint32_t mode; uint32_t mode;
uint32_t tmicr; uint32_t tmicr;
uint32_t divisor; uint32_t divisor_shift;
}; };
struct vlapic { struct vlapic {