KVM: PIT support for HPET legacy mode
When kvm is in hpet_legacy_mode, the hpet is providing the timer interrupt and the pit should not be. So in legacy mode, the pit timer is destroyed, but the *state* of the pit is maintained. So if kvm or the guest tries to modify the state of the pit, this modification is accepted, *except* that the timer isn't actually started. When we exit hpet_legacy_mode, the current state of the pit (which is up to date since we've been accepting modifications) is used to restart the pit timer. The saved_mode code in kvm_pit_load_count temporarily changes mode to 0xff in order to destroy the timer, but then restores the actual value, again maintaining "current" state of the pit for possible later reenablement. [avi: add some reserved storage in the ioctl; make SET_PIT2 IOW] [marcelo: fix memory corruption due to reserved storage] Signed-off-by: Beth Kon <eak@us.ibm.com> Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com> Signed-off-by: Avi Kivity <avi@redhat.com>
This commit is contained in:
@ -332,20 +332,33 @@ static void pit_load_count(struct kvm *kvm, int channel, u32 val)
|
||||
case 1:
|
||||
/* FIXME: enhance mode 4 precision */
|
||||
case 4:
|
||||
create_pit_timer(ps, val, 0);
|
||||
if (!(ps->flags & KVM_PIT_FLAGS_HPET_LEGACY)) {
|
||||
create_pit_timer(ps, val, 0);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
create_pit_timer(ps, val, 1);
|
||||
if (!(ps->flags & KVM_PIT_FLAGS_HPET_LEGACY)){
|
||||
create_pit_timer(ps, val, 1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
destroy_pit_timer(&ps->pit_timer);
|
||||
}
|
||||
}
|
||||
|
||||
void kvm_pit_load_count(struct kvm *kvm, int channel, u32 val)
|
||||
void kvm_pit_load_count(struct kvm *kvm, int channel, u32 val, int hpet_legacy_start)
|
||||
{
|
||||
pit_load_count(kvm, channel, val);
|
||||
u8 saved_mode;
|
||||
if (hpet_legacy_start) {
|
||||
/* save existing mode for later reenablement */
|
||||
saved_mode = kvm->arch.vpit->pit_state.channels[0].mode;
|
||||
kvm->arch.vpit->pit_state.channels[0].mode = 0xff; /* disable timer */
|
||||
pit_load_count(kvm, channel, val);
|
||||
kvm->arch.vpit->pit_state.channels[0].mode = saved_mode;
|
||||
} else {
|
||||
pit_load_count(kvm, channel, val);
|
||||
}
|
||||
}
|
||||
|
||||
static inline struct kvm_pit *dev_to_pit(struct kvm_io_device *dev)
|
||||
@ -554,6 +567,7 @@ void kvm_pit_reset(struct kvm_pit *pit)
|
||||
struct kvm_kpit_channel_state *c;
|
||||
|
||||
mutex_lock(&pit->pit_state.lock);
|
||||
pit->pit_state.flags = 0;
|
||||
for (i = 0; i < 3; i++) {
|
||||
c = &pit->pit_state.channels[i];
|
||||
c->mode = 0xff;
|
||||
|
Reference in New Issue
Block a user