@@ -206,6 +206,9 @@ static int hpet_load(QEMUFile *f, void *opaque, int version_id)
qemu_get_timer(f, s->timer[i].qemu_timer);
}
}
+ if (hpet_in_legacy_mode()) {
+ hpet_disable_pit();
+ }
return 0;
}
@@ -475,9 +478,11 @@ static void hpet_ram_writel(void *opaque, target_phys_addr_t addr,
}
/* i8254 and RTC are disabled when HPET is in legacy mode */
if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
- hpet_pit_disable();
+ hpet_disable_pit();
+ dprintf("qemu: hpet disabled pit\n");
} else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
- hpet_pit_enable();
+ hpet_enable_pit();
+ dprintf("qemu: hpet enabled pit\n");
}
break;
case HPET_CFG + 4:
@@ -554,13 +559,15 @@ static void hpet_reset(void *opaque) {
/* 64-bit main counter; 3 timers supported; LegacyReplacementRoute. */
s->capability = 0x8086a201ULL;
s->capability |= ((HPET_CLK_PERIOD) << 32);
- if (count > 0)
+ if (count > 0) {
/* we don't enable pit when hpet_reset is first called (by hpet_init)
* because hpet is taking over for pit here. On subsequent invocations,
* hpet_reset is called due to system reset. At this point control must
* be returned to pit until SW reenables hpet.
*/
- hpet_pit_enable();
+ hpet_enable_pit();
+ dprintf("qemu: hpet enabled pit\n");
+ }
count = 1;
}
@@ -25,6 +25,7 @@
#include "pc.h"
#include "isa.h"
#include "qemu-timer.h"
+#include "qemu-kvm.h"
#include "i8254.h"
//#define DEBUG_PIT
@@ -198,6 +199,9 @@ int pit_get_mode(PITState *pit, int channel)
static inline void pit_load_count(PITChannelState *s, int val)
{
+ if (s->channel == 0 && pit_state.hpet_legacy_mode) {
+ return;
+ }
if (val == 0)
val = 0x10000;
s->count_load_time = qemu_get_clock(vm_clock);
@@ -371,10 +375,11 @@ static void pit_irq_timer_update(PITChannelState *s, int64_t current_time)
(double)(expire_time - current_time) / ticks_per_sec);
#endif
s->next_transition_time = expire_time;
- if (expire_time != -1)
+ if (expire_time != -1) {
qemu_mod_timer(s->irq_timer, expire_time);
- else
+ } else {
qemu_del_timer(s->irq_timer);
+ }
}
static void pit_irq_timer(void *opaque)
@@ -451,6 +456,7 @@ void pit_reset(void *opaque)
PITChannelState *s;
int i;
+ pit->hpet_legacy_mode = 0;
for(i = 0;i < 3; i++) {
s = &pit->channels[i];
s->mode = 3;
@@ -460,32 +466,43 @@ void pit_reset(void *opaque)
}
/* When HPET is operating in legacy mode, i8254 timer0 is disabled */
-void hpet_pit_disable(void) {
- PITChannelState *s;
- s = &pit_state.channels[0];
- if (s->irq_timer)
- qemu_del_timer(s->irq_timer);
+
+void hpet_disable_pit(void)
+{
+ PITChannelState *s = &pit_state.channels[0];
+ if (qemu_kvm_pit_in_kernel()) {
+ kvm_hpet_disable_kpit();
+ } else {
+ if (s->irq_timer) {
+ qemu_del_timer(s->irq_timer);
+ }
+ }
}
/* When HPET is reset or leaving legacy mode, it must reenable i8254
* timer 0
*/
-void hpet_pit_enable(void)
+void hpet_enable_pit(void)
{
PITState *pit = &pit_state;
- PITChannelState *s;
- s = &pit->channels[0];
- s->mode = 3;
- s->gate = 1;
- pit_load_count(s, 0);
+ PITChannelState *s = &pit->channels[0];
+ if (qemu_kvm_pit_in_kernel()) {
+ kvm_hpet_enable_kpit();
+ } else {
+ pit_load_count(s, s->count);
+ }
}
PITState *pit_init(int base, qemu_irq irq)
{
PITState *pit = &pit_state;
PITChannelState *s;
+ int i;
+ for (i = 0; i <3 ; i++) {
+ pit->channels[i].channel = i;
+ }
s = &pit->channels[0];
/* the timer 0 is connected to an IRQ */
s->irq_timer = qemu_new_timer(vm_clock, pit_irq_timer, s);
@@ -35,6 +35,7 @@
typedef struct PITChannelState {
int count; /* can be 65536 */
+ uint8_t channel;
uint16_t latched_count;
uint8_t count_latched;
uint8_t status_latched;
@@ -55,6 +56,7 @@ typedef struct PITChannelState {
struct PITState {
PITChannelState channels[3];
+ uint8_t hpet_legacy_mode;
};
void pit_save(QEMUFile *f, void *opaque);
@@ -74,8 +74,8 @@ int pit_get_out(PITState *pit, int channel, int64_t current_time);
PITState *kvm_pit_init(int base, qemu_irq irq);
-void hpet_pit_disable(void);
-void hpet_pit_enable(void);
+void hpet_disable_pit(void);
+void hpet_enable_pit(void);
/* vmport.c */
void vmport_init(void);
@@ -274,6 +274,7 @@ struct kvm_guest_debug_arch {
struct kvm_pit_state {
struct kvm_pit_channel_state channels[3];
+ __u8 hpet_legacy_mode;
};
struct kvm_reinject_control {
@@ -467,6 +467,26 @@ int kvm_vcpu_inited(CPUState *env)
return env->kvm_cpu_state.created;
}
+void kvm_hpet_disable_kpit(void)
+{
+ struct kvm_pit_state ps;
+ if (qemu_kvm_pit_in_kernel()) {
+ kvm_get_pit(kvm_context, &ps);
+ ps.hpet_legacy_mode = 1;
+ kvm_set_pit(kvm_context, &ps);
+ }
+}
+
+void kvm_hpet_enable_kpit(void)
+{
+ struct kvm_pit_state ps;
+ if (qemu_kvm_pit_in_kernel()) {
+ kvm_get_pit(kvm_context, &ps);
+ ps.hpet_legacy_mode = 0;
+ kvm_set_pit(kvm_context, &ps);
+ }
+}
+
int kvm_init_ap(void)
{
#ifdef TARGET_I386
@@ -34,7 +34,8 @@ int kvm_qemu_check_extension(int ext);
void kvm_apic_init(CPUState *env);
/* called from vcpu initialization */
void qemu_kvm_load_lapic(CPUState *env);
-
+void kvm_hpet_enable_kpit(void);
+void kvm_hpet_disable_kpit(void);
int kvm_set_irq(int irq, int level, int *status);
int kvm_physical_memory_set_dirty_tracking(int enable);
@@ -6056,10 +6056,15 @@ int main(int argc, char **argv, char **envp)
module_call_init(MODULE_INIT_DEVICE);
if (kvm_enabled()) {
- kvm_init_ap();
+ kvm_init_ap();
#ifdef USE_KVM
if (kvm_irqchip && !qemu_kvm_has_gsi_routing()) {
irq0override = 0;
+ /* if kernel can't do irq routing, interrupt source
+ * override 0->2 can not be set up as required by hpet,
+ * so disable hpet.
+ */
+ no_hpet=1;
}
#endif
}
The big change here is handling of enabling/disabling of hpet legacy mode. When hpet enters legacy mode, the spec says that the pit stops generating interrupts. In practice, we want to stop the pit periodic timer from running because it is wasteful in a virtual environment. We also have to worry about the hpet leaving legacy mode (which, at least in linux, happens only during a shutdown or crash). At this point, according to the hpet spec, PIT interrupts need to be reenabled. For us, it means the PIT timer needs to be restarted. This patch handles this situation better than the previous version by coming closer to just disabling PIT interrupts. It allows the PIT state to change if the OS modifies it, even while PIT is disabled, but does not allow a pit timer to start. Then if HPET legacy mode is disabled, whatever the PIT state is at that point, the PIT timer is restarted accordingly. Signed-off-by: Beth Kon <eak@us.ibm.com> --- hw/hpet.c | 15 +++++++++++---- hw/i8254.c | 43 ++++++++++++++++++++++++++++++------------- hw/i8254.h | 2 ++ hw/pc.h | 4 ++-- kvm/include/x86/asm/kvm.h | 1 + qemu-kvm.c | 20 ++++++++++++++++++++ qemu-kvm.h | 3 ++- vl.c | 7 ++++++- 8 files changed, 74 insertions(+), 21 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html