From patchwork Tue Apr 26 13:19:39 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jan Kiszka X-Patchwork-Id: 732692 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p3QDKDpD030201 for ; Tue, 26 Apr 2011 13:20:13 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755494Ab1DZNUA (ORCPT ); Tue, 26 Apr 2011 09:20:00 -0400 Received: from david.siemens.de ([192.35.17.14]:34540 "EHLO david.siemens.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755445Ab1DZNTp (ORCPT ); Tue, 26 Apr 2011 09:19:45 -0400 Received: from mail1.siemens.de (localhost [127.0.0.1]) by david.siemens.de (8.13.6/8.13.6) with ESMTP id p3QDJfm4026393; Tue, 26 Apr 2011 15:19:41 +0200 Received: from mchn199C.mchp.siemens.de ([139.25.109.49]) by mail1.siemens.de (8.13.6/8.13.6) with ESMTP id p3QDJepK031993; Tue, 26 Apr 2011 15:19:41 +0200 From: Jan Kiszka To: Avi Kivity , Marcelo Tosatti Cc: kvm@vger.kernel.org, "Michael S. Tsirkin" Subject: [PATCH v2 9/9] qemu-kvm: hpet: Add MSI support for in-kernel irqchip mode Date: Tue, 26 Apr 2011 15:19:39 +0200 Message-Id: <3d089633897515a214af032ed633342a7001945f.1303823975.git.jan.kiszka@siemens.com> X-Mailer: git-send-email 1.7.1 In-Reply-To: References: In-Reply-To: References: Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Tue, 26 Apr 2011 13:20:13 +0000 (UTC) Just like PCI devices, the HPET requires special care to route MSI events to the in-kernel irqchip if enabled. Signed-off-by: Jan Kiszka --- hw/hpet.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 70 insertions(+), 1 deletions(-) diff --git a/hw/hpet.c b/hw/hpet.c index 6ce07bc..e773ed8 100644 --- a/hw/hpet.c +++ b/hw/hpet.c @@ -31,6 +31,7 @@ #include "hpet_emul.h" #include "sysbus.h" #include "mc146818rtc.h" +#include "kvm.h" //#define HPET_DEBUG #ifdef HPET_DEBUG @@ -55,6 +56,7 @@ typedef struct HPETTimer { /* timers */ uint8_t wrap_flag; /* timer pop will indicate wrap for one-shot 32-bit * mode. Next pop will be actual timer expiration. */ + KVMMsiMessage kmm; } HPETTimer; typedef struct HPETState { @@ -141,11 +143,59 @@ static int deactivating_bit(uint64_t old, uint64_t new, uint64_t mask) return ((old & mask) && !(new & mask)); } +static int modifying_bit(uint64_t old, uint64_t new, uint64_t mask) +{ + return (old ^ new) & mask; +} + static uint64_t hpet_get_ticks(HPETState *s) { return ns_to_ticks(qemu_get_clock_ns(vm_clock) + s->hpet_offset); } +static void kvm_hpet_msi_update(HPETTimer *t) +{ + KVMMsiMessage new_entry; + int ret = 0; + + if (!kvm_enabled() || !kvm_irqchip_in_kernel()) { + return; + } + + if (timer_fsb_route(t)) { + new_entry.addr_hi = 0; + new_entry.addr_lo = t->fsb >> 32; + new_entry.data = t->fsb & 0xffffffff; + if (t->kmm.gsi == -1) { + kvm_msi_message_add(&new_entry); + ret = 1; + } else { + ret = kvm_msi_message_update(&t->kmm, &new_entry); + } + if (ret > 0) { + t->kmm = new_entry; + kvm_commit_irq_routes(); + } + } else if (t->kmm.gsi != -1) { + kvm_msi_message_del(&t->kmm); + t->kmm.gsi = -1; + } +} + +static void kvm_hpet_msi_free(HPETState *s) +{ + int i; + + for (i = 0; i < s->num_timers; i++) { + HPETTimer *timer = &s->timer[i]; + + if (timer->kmm.gsi != -1) { + kvm_msi_message_del(&timer->kmm); + timer->kmm.gsi = -1; + } + } +} + /* * calculate diff between comparator value and current ticks */ @@ -192,7 +242,11 @@ static void update_irq(struct HPETTimer *timer, int set) qemu_irq_lower(s->irqs[route]); } } else if (timer_fsb_route(timer)) { - stl_phys(timer->fsb >> 32, timer->fsb & 0xffffffff); + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + kvm_set_irq(timer->kmm.gsi, 1, NULL); + } else { + stl_phys(timer->fsb >> 32, timer->fsb & 0xffffffff); + } } else if (timer->config & HPET_TN_TYPE_LEVEL) { s->isr |= mask; qemu_irq_raise(s->irqs[route]); @@ -214,6 +268,8 @@ static int hpet_pre_load(void *opaque) { HPETState *s = opaque; + kvm_hpet_msi_free(s); + /* version 1 only supports 3, later versions will load the actual value */ s->num_timers = HPET_MIN_TIMERS; return 0; @@ -222,6 +278,7 @@ static int hpet_pre_load(void *opaque) static int hpet_post_load(void *opaque, int version_id) { HPETState *s = opaque; + int i; /* Recalculate the offset between the main counter and guest time */ s->hpet_offset = ticks_to_ns(s->hpet_counter) - qemu_get_clock_ns(vm_clock); @@ -241,6 +298,10 @@ static int hpet_post_load(void *opaque, int version_id) hpet_pit_disable(); } + for (i = 0; i < s->num_timers; i++) { + kvm_hpet_msi_update(&s->timer[i]); + } + return 0; } @@ -485,6 +546,9 @@ static void hpet_ram_writel(void *opaque, target_phys_addr_t addr, } else if (deactivating_bit(old_val, new_val, HPET_TN_ENABLE)) { hpet_del_timer(timer); } + if (modifying_bit(old_val, new_val, HPET_TN_FSB_ENABLE)) { + kvm_hpet_msi_update(timer); + } break; case HPET_TN_CFG + 4: // Interrupt capabilities DPRINTF("qemu: invalid HPET_TN_CFG+4 write\n"); @@ -533,9 +597,11 @@ static void hpet_ram_writel(void *opaque, target_phys_addr_t addr, break; case HPET_TN_ROUTE: timer->fsb = (timer->fsb & 0xffffffff00000000ULL) | new_val; + kvm_hpet_msi_update(timer); break; case HPET_TN_ROUTE + 4: timer->fsb = (new_val << 32) | (timer->fsb & 0xffffffff); + kvm_hpet_msi_update(timer); break; default: DPRINTF("qemu: invalid hpet_ram_writel\n"); @@ -667,6 +733,8 @@ static void hpet_reset(DeviceState *d) hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; hpet_cfg.hpet[s->hpet_id].address = sysbus_from_qdev(d)->mmio[0].addr; count = 1; + + kvm_hpet_msi_free(s); } static void hpet_handle_rtc_irq(void *opaque, int n, int level) @@ -711,6 +779,7 @@ static int hpet_init(SysBusDevice *dev) timer->qemu_timer = qemu_new_timer_ns(vm_clock, hpet_timer, timer); timer->tn = i; timer->state = s; + timer->kmm.gsi = -1; } /* 64-bit main counter; LegacyReplacementRoute. */