Changeset View
Changeset View
Standalone View
Standalone View
sys/amd64/vmm/io/vhpet.c
Show First 20 Lines • Show All 98 Lines • ▼ Show 20 Lines | static void vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, | ||||
sbintime_t now); | sbintime_t now); | ||||
static uint64_t | static uint64_t | ||||
vhpet_capabilities(void) | vhpet_capabilities(void) | ||||
{ | { | ||||
uint64_t cap = 0; | uint64_t cap = 0; | ||||
cap |= 0x8086 << 16; /* vendor id */ | cap |= 0x8086 << 16; /* vendor id */ | ||||
cap |= HPET_CAP_LEG_RT; /* legacy routing capable */ | |||||
cap |= (VHPET_NUM_TIMERS - 1) << 8; /* number of timers */ | cap |= (VHPET_NUM_TIMERS - 1) << 8; /* number of timers */ | ||||
cap |= 1; /* revision */ | cap |= 1; /* revision */ | ||||
cap &= ~HPET_CAP_COUNT_SIZE; /* 32-bit timer */ | cap &= ~HPET_CAP_COUNT_SIZE; /* 32-bit timer */ | ||||
cap &= 0xffffffff; | cap &= 0xffffffff; | ||||
cap |= (FS_PER_S / HPET_FREQ) << 32; /* tick period in fs */ | cap |= (FS_PER_S / HPET_FREQ) << 32; /* tick period in fs */ | ||||
return (cap); | return (cap); | ||||
} | } | ||||
static __inline bool | static __inline bool | ||||
vhpet_counter_enabled(struct vhpet *vhpet) | vhpet_counter_enabled(struct vhpet *vhpet) | ||||
{ | { | ||||
return ((vhpet->config & HPET_CNF_ENABLE) ? true : false); | return ((vhpet->config & HPET_CNF_ENABLE) ? true : false); | ||||
} | } | ||||
static __inline bool | static __inline bool | ||||
vhpet_timer_msi_enabled(struct vhpet *vhpet, int n) | vhpet_timer_msi_enabled(struct vhpet *vhpet, int n) | ||||
{ | { | ||||
const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN; | const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN; | ||||
/* | |||||
* LegacyReplacement Route configuration takes precedence over MSI | |||||
* for timers 0 and 1. | |||||
*/ | |||||
if (n == 0 || n == 1) { | |||||
if (vhpet->config & HPET_CNF_LEG_RT) | |||||
return (false); | |||||
} | |||||
if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable) | if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable) | ||||
return (true); | return (true); | ||||
else | else | ||||
return (false); | return (false); | ||||
} | } | ||||
static __inline int | static __inline int | ||||
vhpet_timer_ioapic_pin(struct vhpet *vhpet, int n) | vhpet_timer_ioapic_pin(struct vhpet *vhpet, int n) | ||||
{ | { | ||||
/* | /* | ||||
* If the timer is configured to use MSI then treat it as if the | * If the timer is configured to use MSI then treat it as if the | ||||
* timer is not connected to the ioapic. | * timer is not connected to the ioapic. | ||||
*/ | */ | ||||
if (vhpet_timer_msi_enabled(vhpet, n)) | if (vhpet_timer_msi_enabled(vhpet, n)) | ||||
return (0); | return (0); | ||||
if (vhpet->config & HPET_CNF_LEG_RT) { | |||||
/* | |||||
* In "legacy routing" timers 0 and 1 are connected to | |||||
* ioapic pins 2 and 8 respectively. | |||||
*/ | |||||
switch (n) { | |||||
case 0: | |||||
return (2); | |||||
case 1: | |||||
return (8); | |||||
} | |||||
} | |||||
return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9); | return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9); | ||||
} | } | ||||
static __inline int | |||||
vhpet_timer_atpic_pin(struct vhpet *vhpet, int n) | |||||
{ | |||||
if (vhpet->config & HPET_CNF_LEG_RT) { | |||||
/* | |||||
* In "legacy routing" timers 0 and 1 are connected to | |||||
* 8259 master pin 0 and slave pin 0 respectively. | |||||
*/ | |||||
switch (n) { | |||||
case 0: | |||||
return (0); | |||||
case 1: | |||||
return (8); | |||||
} | |||||
} | |||||
return (-1); | |||||
} | |||||
static uint32_t | static uint32_t | ||||
vhpet_counter(struct vhpet *vhpet, sbintime_t *nowptr) | vhpet_counter(struct vhpet *vhpet, sbintime_t *nowptr) | ||||
{ | { | ||||
uint32_t val; | uint32_t val; | ||||
sbintime_t now, delta; | sbintime_t now, delta; | ||||
val = vhpet->countbase; | val = vhpet->countbase; | ||||
if (vhpet_counter_enabled(vhpet)) { | if (vhpet_counter_enabled(vhpet)) { | ||||
Show All 13 Lines | if (vhpet_counter_enabled(vhpet)) { | ||||
KASSERT(nowptr == NULL, ("vhpet_counter: nowptr must be NULL")); | KASSERT(nowptr == NULL, ("vhpet_counter: nowptr must be NULL")); | ||||
} | } | ||||
return (val); | return (val); | ||||
} | } | ||||
static void | static void | ||||
vhpet_timer_clear_isr(struct vhpet *vhpet, int n) | vhpet_timer_clear_isr(struct vhpet *vhpet, int n) | ||||
{ | { | ||||
int pin, legacy_pin; | int pin; | ||||
if (vhpet->isr & (1 << n)) { | if (vhpet->isr & (1 << n)) { | ||||
pin = vhpet_timer_ioapic_pin(vhpet, n); | pin = vhpet_timer_ioapic_pin(vhpet, n); | ||||
KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n)); | KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n)); | ||||
vioapic_deassert_irq(vhpet->vm, pin); | vioapic_deassert_irq(vhpet->vm, pin); | ||||
legacy_pin = vhpet_timer_atpic_pin(vhpet, n); | |||||
if (legacy_pin != -1) | |||||
vatpic_deassert_irq(vhpet->vm, legacy_pin); | |||||
vhpet->isr &= ~(1 << n); | vhpet->isr &= ~(1 << n); | ||||
} | } | ||||
} | } | ||||
static __inline bool | static __inline bool | ||||
vhpet_periodic_timer(struct vhpet *vhpet, int n) | vhpet_periodic_timer(struct vhpet *vhpet, int n) | ||||
{ | { | ||||
Show All 9 Lines | |||||
static __inline bool | static __inline bool | ||||
vhpet_timer_edge_trig(struct vhpet *vhpet, int n) | vhpet_timer_edge_trig(struct vhpet *vhpet, int n) | ||||
{ | { | ||||
KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: " | KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: " | ||||
"timer %d is using MSI", n)); | "timer %d is using MSI", n)); | ||||
/* The legacy replacement interrupts are always edge triggered */ | |||||
if (vhpet->config & HPET_CNF_LEG_RT) { | |||||
if (n == 0 || n == 1) | |||||
return (true); | |||||
} | |||||
if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0) | if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0) | ||||
return (true); | return (true); | ||||
else | else | ||||
return (false); | return (false); | ||||
} | } | ||||
static void | static void | ||||
vhpet_timer_interrupt(struct vhpet *vhpet, int n) | vhpet_timer_interrupt(struct vhpet *vhpet, int n) | ||||
{ | { | ||||
int pin, legacy_pin; | int pin; | ||||
/* If interrupts are not enabled for this timer then just return. */ | /* If interrupts are not enabled for this timer then just return. */ | ||||
if (!vhpet_timer_interrupt_enabled(vhpet, n)) | if (!vhpet_timer_interrupt_enabled(vhpet, n)) | ||||
return; | return; | ||||
/* | /* | ||||
* If a level triggered interrupt is already asserted then just return. | * If a level triggered interrupt is already asserted then just return. | ||||
*/ | */ | ||||
Show All 9 Lines | vhpet_timer_interrupt(struct vhpet *vhpet, int n) | ||||
} | } | ||||
pin = vhpet_timer_ioapic_pin(vhpet, n); | pin = vhpet_timer_ioapic_pin(vhpet, n); | ||||
if (pin == 0) { | if (pin == 0) { | ||||
VM_CTR1(vhpet->vm, "hpet t%d intr is not routed to ioapic", n); | VM_CTR1(vhpet->vm, "hpet t%d intr is not routed to ioapic", n); | ||||
return; | return; | ||||
} | } | ||||
legacy_pin = vhpet_timer_atpic_pin(vhpet, n); | |||||
if (vhpet_timer_edge_trig(vhpet, n)) { | if (vhpet_timer_edge_trig(vhpet, n)) { | ||||
vioapic_pulse_irq(vhpet->vm, pin); | vioapic_pulse_irq(vhpet->vm, pin); | ||||
if (legacy_pin != -1) | |||||
vatpic_pulse_irq(vhpet->vm, legacy_pin); | |||||
} else { | } else { | ||||
vhpet->isr |= 1 << n; | vhpet->isr |= 1 << n; | ||||
vioapic_assert_irq(vhpet->vm, pin); | vioapic_assert_irq(vhpet->vm, pin); | ||||
if (legacy_pin != -1) | |||||
vatpic_assert_irq(vhpet->vm, legacy_pin); | |||||
} | } | ||||
} | } | ||||
static void | static void | ||||
vhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter) | vhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter) | ||||
{ | { | ||||
uint32_t compval, comprate, compnext; | uint32_t compval, comprate, compnext; | ||||
▲ Show 20 Lines • Show All 259 Lines • ▼ Show 20 Lines | if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) { | ||||
* the 'config' register. If the HPET is going to be disabled | * the 'config' register. If the HPET is going to be disabled | ||||
* then we need to update 'countbase' with the value right | * then we need to update 'countbase' with the value right | ||||
* before it is disabled. | * before it is disabled. | ||||
*/ | */ | ||||
nowptr = vhpet_counter_enabled(vhpet) ? &now : NULL; | nowptr = vhpet_counter_enabled(vhpet) ? &now : NULL; | ||||
counter = vhpet_counter(vhpet, nowptr); | counter = vhpet_counter(vhpet, nowptr); | ||||
oldval = vhpet->config; | oldval = vhpet->config; | ||||
update_register(&vhpet->config, data, mask); | update_register(&vhpet->config, data, mask); | ||||
/* | |||||
* LegacyReplacement Routing is not supported so clear the | |||||
* bit explicitly. | |||||
*/ | |||||
vhpet->config &= ~HPET_CNF_LEG_RT; | |||||
if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) { | if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) { | ||||
if (vhpet_counter_enabled(vhpet)) { | if (vhpet_counter_enabled(vhpet)) { | ||||
vhpet_start_counting(vhpet); | vhpet_start_counting(vhpet); | ||||
VM_CTR0(vhpet->vm, "hpet enabled"); | VM_CTR0(vhpet->vm, "hpet enabled"); | ||||
} else { | } else { | ||||
vhpet_stop_counting(vhpet, counter, now); | vhpet_stop_counting(vhpet, counter, now); | ||||
VM_CTR0(vhpet->vm, "hpet disabled"); | VM_CTR0(vhpet->vm, "hpet disabled"); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 223 Lines • Show Last 20 Lines |