From patchwork Mon Nov 1 15:01:28 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Graf X-Patchwork-Id: 69809 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 5BD1CB70CC for ; Tue, 2 Nov 2010 03:20:33 +1100 (EST) Received: from localhost ([127.0.0.1]:58342 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PCwVX-0007op-8z for incoming@patchwork.ozlabs.org; Mon, 01 Nov 2010 11:41:15 -0400 Received: from [140.186.70.92] (port=52815 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PCvtx-0003h3-Fy for qemu-devel@nongnu.org; Mon, 01 Nov 2010 11:02:37 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PCvtV-0000dv-4n for qemu-devel@nongnu.org; Mon, 01 Nov 2010 11:01:59 -0400 Received: from cantor2.suse.de ([195.135.220.15]:39324 helo=mx2.suse.de) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PCvtU-0000cw-KV for qemu-devel@nongnu.org; Mon, 01 Nov 2010 11:01:57 -0400 Received: from relay1.suse.de (charybdis-ext.suse.de [195.135.221.2]) by mx2.suse.de (Postfix) with ESMTP id 357938726A; Mon, 1 Nov 2010 16:01:54 +0100 (CET) From: Alexander Graf To: qemu-devel Developers Date: Mon, 1 Nov 2010 16:01:28 +0100 Message-Id: <1288623713-28062-16-git-send-email-agraf@suse.de> X-Mailer: git-send-email 1.6.0.2 In-Reply-To: <1288623713-28062-1-git-send-email-agraf@suse.de> References: <1288623713-28062-1-git-send-email-agraf@suse.de> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4-2.6 Cc: Gerd Hoffmann Subject: [Qemu-devel] [PATCH 15/40] xenner: kernel: lapic code X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Xenner uses the lapic for interrupt handling and time keeping. This patch adds support for this. Signed-off-by: Alexander Graf --- pc-bios/xenner/xenner-lapic.c | 622 +++++++++++++++++++++++++++++++++++++++++ 1 files changed, 622 insertions(+), 0 deletions(-) create mode 100644 pc-bios/xenner/xenner-lapic.c diff --git a/pc-bios/xenner/xenner-lapic.c b/pc-bios/xenner/xenner-lapic.c new file mode 100644 index 0000000..af08c20 --- /dev/null +++ b/pc-bios/xenner/xenner-lapic.c @@ -0,0 +1,622 @@ +/* + * Copyright (C) Red Hat 2007 + * Copyright (C) Novell Inc. 2010 + * + * Author(s): Gerd Hoffmann + * Alexander Graf + * + * Xenner lapic handling + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +/* + * interrupt handling is here: + * - ioapic control + * - lapic control + * - event channel management + * - irq handler + */ + +#include "xenner.h" + +#include "cpufeature.h" +#include "msr-index.h" +#include "apicdef.h" + +/* --------------------------------------------------------------------- */ + +static void *ioapic_mmio; +static uint32_t ioapic_pins; + +static uint32_t ioapic_read(int reg) +{ + volatile uint32_t *sel = (ioapic_mmio + IOAPIC_REG_SELECT); + volatile uint32_t *win = (ioapic_mmio + IOAPIC_REG_WINDOW); + *sel = reg; + return *win; +} + +static void ioapic_write(int reg, uint32_t val) +{ + volatile uint32_t *sel = (ioapic_mmio + IOAPIC_REG_SELECT); + volatile uint32_t *win = (ioapic_mmio + IOAPIC_REG_WINDOW); + *sel = reg; + *win = val; +} + +static void ioapic_write_irq_entry(int pin, struct IO_APIC_route_entry e) +{ + union route_entry_union { + struct { uint32_t w1, w2; }; + struct IO_APIC_route_entry entry; + } eu; + + eu.entry = e; + ioapic_write(0x11 + 2*pin, eu.w2); + ioapic_write(0x10 + 2*pin, eu.w1); +} + +static void ioapic_route_irq(int pin, int vector, int cpu_id) +{ + struct IO_APIC_route_entry entry; + + memset(&entry,0,sizeof(entry)); + entry.vector = vector; + entry.dest = cpu_id; + + ioapic_write_irq_entry(pin, entry); +} + +static void ioapic_unroute_irq(int pin) +{ + struct IO_APIC_route_entry entry; + + memset(&entry,0,sizeof(entry)); + ioapic_write_irq_entry(pin, entry); +} + +static void ioapic_init(struct xen_cpu *cpu) +{ + ureg_t base = IOAPIC_DEFAULT_BASE_ADDRESS; + uint32_t ver, id; + + ioapic_mmio = fixmap_page(cpu, base); + id = ioapic_read(IOAPIC_REG_APIC_ID); + ver = ioapic_read(IOAPIC_REG_VERSION); + ioapic_pins = ((ver >> 16) & 0xff) + 1; + printk(1, "%s: base %" PRIxREG ", mapped to %p, id %d, version %d, pins %d\n", + __FUNCTION__, base, ioapic_mmio, + (id >> 24) & 0x0f, ver & 0xff, ioapic_pins); + if (!ver) { + panic("oops: ioapic version register is zero", NULL); + } + + /* PICs: mask all irqs */ + outb(0x10, 0x20); +} + +/* --------------------------------------------------------------------- */ + +static uint32_t lapic_read(struct xen_cpu *cpu, int reg) +{ + volatile uint32_t *ptr = (cpu->lapic + reg); + return *ptr; +} + +static void lapic_write(struct xen_cpu *cpu, int reg, uint32_t val) +{ + volatile uint32_t *ptr = (cpu->lapic + reg); + *ptr = val; +} + +void lapic_eoi(struct xen_cpu *cpu) +{ + lapic_write(cpu, APIC_EOI, 0); +} + +void lapic_timer(struct xen_cpu *cpu) +{ + uint64_t systime; + uint32_t lvt; + uint32_t count; + uint32_t div; + int64_t nsecs; + + systime = cpu->v.vcpu_info->time.system_time; + if (cpu->oneshot) { + nsecs = cpu->oneshot - systime; + if (nsecs < 10000) { + nsecs = 10000; + } + } else { + nsecs = cpu->periodic; + } + + /* cap the max timer time - if we return too fast the guest will be nice + with us and just retrigger again. */ + if (nsecs > 0x80000000) { + nsecs = 0x80000000; + } + + printk(3, "%s/%d: periodic %" PRId64 ", oneshot %" PRId64 + ", systime %" PRId64 ", nsecs %" PRId64 "\n", __FUNCTION__, + cpu->id, cpu->periodic, cpu->oneshot, systime, nsecs); + + printk(3, "%s/%d: periodic %" PRIx64 ", oneshot %" PRIx64 + ", systime %" PRIx64 ", nsecs %" PRIx64 "\n", __FUNCTION__, + cpu->id, cpu->periodic, cpu->oneshot, systime, nsecs); + + lvt = cpu->virq_to_vector[VIRQ_TIMER]; + if (!cpu->oneshot) { + lvt |= (1 << 17); + } + + div = APIC_TDR_DIV_1; + count = nsecs; /* kvm virtual apic has 1 ns ticks */ + if (count != nsecs) { + /* count overflow, get some more bits */ + div = APIC_TDR_DIV_128; + count = nsecs / 128; + if (count != nsecs / 128) { + /* Hmm, still overflows ... */ + printk(0, "%s: nsecs 0x%" PRIx64 ", nsecs/128 0x%" PRIx64 ", count 0x%x\n", + __FUNCTION__, nsecs, nsecs / 128, count); + panic("lapic timer count overflow", NULL); + } + } + + lapic_write(cpu, APIC_LVTT, lvt); + lapic_write(cpu, APIC_TDCR, div); + lapic_write(cpu, APIC_TMICT, count); +} + +static void lapic_ipi_send(struct xen_cpu *cpu, int dest, + int vector, uint32_t flags) +{ + uint32_t icr2 = SET_APIC_DEST_FIELD(dest); + uint32_t icr = vector | flags; + + if (lapic_read(cpu, APIC_ICR) & APIC_ICR_BUSY) { + printk(0, "%s: busy ...\n", __FUNCTION__); + while (lapic_read(cpu, APIC_ICR) & APIC_ICR_BUSY) + /* busy wait */; + printk(0, "%s: ... ok\n", __FUNCTION__); + } + + lapic_write(cpu, APIC_ICR2, icr2); + lapic_write(cpu, APIC_ICR, icr); +} + +void lapic_ipi_boot(struct xen_cpu *cpu, struct xen_cpu *ap) +{ + int addr = EMU_PA(sipi); + + emudev_set(EMUDEV_CONF_NEXT_SECONDARY_VCPU, 0, ap->id); + printk(0, "%s/%d: send init ...\n", __FUNCTION__, ap->id); + lapic_ipi_send(cpu, ap->id, 0, APIC_DM_INIT | APIC_INT_ASSERT); + printk(0, "%s/%d: send sipi @ %x ...\n", __FUNCTION__, ap->id, addr); + lapic_ipi_send(cpu, ap->id, addr >> PAGE_SHIFT, APIC_DM_STARTUP | APIC_INT_ASSERT); +} + +void lapic_ipi_flush_tlb(struct xen_cpu *cpu) +{ + lapic_ipi_send(cpu, 0, VECTOR_FLUSH_TLB, + APIC_DEST_ALLBUT | APIC_DM_FIXED | APIC_INT_ASSERT); +} + +static int lapic_init(struct xen_cpu *cpu) +{ + struct kvm_cpuid_entry entry; + uint32_t ax, dx, ver, id, spiv; + ureg_t base; + + entry.function = 0x00000001; + real_cpuid(&entry); + if (!(entry.edx & (1 << (X86_FEATURE_APIC % 32)))) { + printk(1, "%s: no lapic present\n", __FUNCTION__); + return 0; + } + + rdmsr(MSR_IA32_APICBASE, &ax, &dx); + base = (uint64_t)dx << 32 | (ax & PAGE_MASK); + cpu->lapic = fixmap_page(cpu, base); + id = lapic_read(cpu, APIC_ID); + ver = lapic_read(cpu, APIC_LVR); + spiv = lapic_read(cpu, APIC_SPIV); + printk(1, "%s: base %" PRIxREG ", mapped to %p, id %d, version %d, maxlvt %d%s%s%s\n", + __FUNCTION__, base, cpu->lapic, + GET_APIC_ID(id), + GET_APIC_VERSION(ver), + GET_APIC_MAXLVT(ver), + ax & 0x00000100 ? ", bootcpu" : "", + ax & 0x00000800 ? ", hw-enabled" : "", + spiv & APIC_SPIV_APIC_ENABLED ? ", sw-enabled" : ""); + if (!ver) { + panic("oops: lapic version register is zero", NULL); + } + + lapic_write(cpu, APIC_SPIV, spiv | APIC_SPIV_APIC_ENABLED); + + if (ax & 0x00000100) { + /* boot cpu */ + return 1; + } + + return 2; +} + +/* --------------------------------------------------------------------- */ + +#define NUM_VEC 256 + +static struct vector { + enum { + VECTYPE_UNDEFINED = 0, + VECTYPE_INTERDOMAIN, + VECTYPE_VIRQ, + VECTYPE_IPI, + } type; + int vec; + int pin; + int evtchn; + int virq; + struct xen_cpu *cpu; + char *desc; +} vectors[NUM_VEC]; + +static struct vector *evtchn_to_vec[NUM_VEC]; +static struct vector *pin_to_vec[NUM_VEC]; + +static void evtchn_route_print(int level, struct vector *vector) +{ + static const char *tname[] = { + [ VECTYPE_UNDEFINED ] = "???", + [ VECTYPE_INTERDOMAIN ] = "ext", + [ VECTYPE_VIRQ ] = "virq", + [ VECTYPE_IPI ] = "ipi", + }; + char *name = vminfo.enames + vector->evtchn * XEN_ENAME_LEN; + char linfo[64], sinfo[20]; + + switch (vector->type) { + case VECTYPE_INTERDOMAIN: + snprintf(linfo, sizeof(linfo), "vcpu %d, io-apic pin %d, %s", + vector->cpu->id, vector->pin, vector->desc); + snprintf(sinfo, sizeof(sinfo), "%s", vector->desc); + break; + case VECTYPE_VIRQ: + snprintf(linfo, sizeof(linfo), "vcpu %d, virq %d, %s", + vector->cpu->id, vector->virq, vector->desc); + snprintf(sinfo, sizeof(sinfo), "virq%d (%s)", + vector->virq, vector->desc); + break; + case VECTYPE_IPI: + snprintf(linfo, sizeof(linfo), "vcpu %d", vector->cpu->id); + snprintf(sinfo, sizeof(sinfo), "ipi"); + break; + default: + snprintf(linfo, sizeof(linfo), "FIXME"); + snprintf(sinfo, sizeof(sinfo), "FIXME"); + break; + } + printk(1, "irq route: vec %d = evtchn %d, type %s, %s\n", + vector->vec, vector->evtchn, tname[vector->type], linfo); + snprintf(name, XEN_ENAME_LEN, "#%d/%d %s", + vector->evtchn, vector->cpu->id, sinfo); +} + +static struct vector *evtchn_route_add(int type, int port) +{ + struct vector *vector; + int vec = VECTOR_EVTCHN_START; + + while (vectors[vec].type != VECTYPE_UNDEFINED) { + vec++; + } + vector = vectors + vec; + + evtchn_to_vec[port] = vector; + vector->type = type; + vector->vec = vec; + vector->evtchn = port; + return vector; +} + +int evtchn_route_interdomain(struct xen_cpu *cpu, int port, char *desc) +{ + struct vector *vector; + int pin; + + vector = evtchn_to_vec[port]; + if (vector) { + /* re-route to other vcpu */ + if (vector->type != VECTYPE_INTERDOMAIN) { + return -1; + } + } else { + /* new evtchn */ + vector = evtchn_route_add(VECTYPE_INTERDOMAIN, port); + vector->desc = desc ? desc : "other"; + for (pin = 1; pin_to_vec[pin]; pin++) { + } + vector->pin = pin; + pin_to_vec[pin] = vector; + emudev_set(EMUDEV_CONF_EVTCHN_TO_PIN, vector->evtchn, vector->pin); + } + vector->cpu = cpu; + ioapic_route_irq(vector->pin, vector->vec, vector->cpu->id); + evtchn_route_print(1, vector); + return 0; +} + +int evtchn_route_virq(struct xen_cpu *cpu, int virq, int port, char *desc) +{ + struct vector *vector; + + vector = evtchn_route_add(VECTYPE_VIRQ, port); + vector->virq = virq; + vector->cpu = cpu; + vector->desc = desc ? desc : "other"; + cpu->virq_to_vector[virq] = vector->vec; + evtchn_route_print(1, vector); + return 0; +} + +int evtchn_route_ipi(struct xen_cpu *cpu, int port) +{ + struct vector *vector; + + vector = evtchn_route_add(VECTYPE_IPI, port); + vector->cpu = cpu; + evtchn_route_print(1, vector); + return 0; +} + +int evtchn_send(struct xen_cpu *cpu, int port) +{ + struct vector *vector; + + if (port >= NUM_VEC) { + printk(0, "%s: oops: port %d is out of range\n", __FUNCTION__, port); + return 0; + } + + vector = evtchn_to_vec[port]; + if (!vector) { + printk(0, "%s: oops: vector for port %d is NULL\n", __FUNCTION__, port); + return 0; + } + switch (vector->type) { + case VECTYPE_VIRQ: + /* should not happen */ + printk(0, "%s: port %d, virq (Huh? -- FIXME)\n", __FUNCTION__, port); + return 1; + case VECTYPE_IPI: + /* handled internally */ + printk(2, "%s: port %d, ipi\n", __FUNCTION__, port); + lapic_ipi_send(cpu, vector->cpu->id, vector->vec, + APIC_DM_FIXED | APIC_INT_ASSERT); + return 1; + default: + /* handled by xenner */ + printk(3, "%s: port %d, external\n", __FUNCTION__, port); + return 0; + } +} + +void evtchn_unmask(struct xen_cpu *cpu, int port) +{ + struct vector *vector = evtchn_to_vec[port]; + int resent = 0; + + if (!vector) { + printk(0, "%s: oops: vector for port %d is NULL\n", __FUNCTION__, port); + return; + } + + clear_bit(port, shared_info.evtchn_mask); + if (test_and_clear_bit(port, shared_info.evtchn_pending)) { + lapic_ipi_send(cpu, vector->cpu->id, vector->vec, + APIC_DM_FIXED | APIC_INT_ASSERT); + resent = 1; + } + printk(2, "%s: port %d%s\n", __FUNCTION__, port, + resent ? ", resent" : ""); +} + +void evtchn_close(struct xen_cpu *cpu, int port) +{ + struct vector *vector = evtchn_to_vec[port]; + char *name; + + if (!vector) { + printk(0, "%s: oops: vector for port %d is NULL\n", __FUNCTION__, port); + return; + } + + switch (vector->type) { + case VECTYPE_INTERDOMAIN: + ioapic_unroute_irq(vector->pin); + pin_to_vec[vector->pin] = NULL; + break; + case VECTYPE_VIRQ: + if (vector->virq == VIRQ_TIMER) { + cpu->oneshot = 0; + cpu->periodic = XEN_DEFAULT_PERIOD; + lapic_timer(cpu); + return; + } + break; + default: + /* nothing -- make gcc happy */ + break; + } + printk(1, "irq route: vec %d = evtchn %d, closing\n", + vector->vec, vector->evtchn); + name = vminfo.enames + vector->evtchn * XEN_ENAME_LEN; + snprintf(name, XEN_ENAME_LEN, "#%d (closed)", vector->evtchn); + + memset(vector, 0, sizeof(*vector)); + evtchn_to_vec[port] = NULL; + emudev_cmd(EMUDEV_CMD_EVTCHN_CLOSE, port); +} + +int evtchn_alloc(int vcpu_id) +{ + ureg_t port; + + emudev_cmd(EMUDEV_CMD_EVTCHN_ALLOC, vcpu_id); + port = emudev_get(EMUDEV_CONF_COMMAND_RESULT, vcpu_id); + return port; +} + +static int evtchn_route_init(struct xen_cpu *cpu) +{ + uint64_t evtchn_store; + uint64_t evtchn_console; + + evtchn_store = emudev_get(EMUDEV_CONF_EVTCH_XENSTORE, 0); + evtchn_console = emudev_get(EMUDEV_CONF_EVTCH_CONSOLE, 0); + + evtchn_route_interdomain(cpu, evtchn_store, "xenstore"); + evtchn_route_interdomain(cpu, evtchn_console, "console"); + + cpu->timerport = evtchn_alloc(cpu->id); + evtchn_route_virq(cpu, VIRQ_TIMER, cpu->timerport, "timer"); + lapic_timer(cpu); + + return 0; +} + +/* --------------------------------------------------------------------- */ + +static void evtchn_raise_event(struct xen_cpu *cpu, int port) +{ + int word = port / (sizeof(intptr_t)*8); + + if (test_and_set_bit(port, shared_info.evtchn_pending) || + test_bit(port, shared_info.evtchn_mask) || + test_and_set_bit(word, &cpu->v.vcpu_info->evtchn_pending_sel)) { + return; + } + cpu->v.vcpu_info->evtchn_upcall_pending = 1; +} + +int evtchn_pending(struct xen_cpu *cpu) +{ + if (!cpu->v.vcpu_info->evtchn_upcall_pending || + !guest_irq_flag(cpu)) { + return 0; + } + return 1; +} + +static void evtchn_forward(struct xen_cpu *cpu, struct regs *regs) +{ + vminfo.faults[XEN_FAULT_EVENT_CALLBACK]++; + bounce_trap(cpu, regs, -1, CALLBACKTYPE_event); +#ifdef CONFIG_64BIT + /* return via iretq please */ + regs->error = HCALL_IRET; +#endif +} + +void evtchn_try_forward(struct xen_cpu *cpu, struct regs *regs) +{ + if (context_is_emu(regs)) { + return; + } + + if (!evtchn_pending(cpu)) { + +#if 0 /* deadlock detector */ + static int masked; + uint8_t *instr = (void*)regs->rip; + + if (cpu->v.vcpu_info->evtchn_upcall_pending && + !guest_irq_flag(cpu)) { + masked++; + if (masked > 10000) { + printk(0, "%s: deadlocked? injecting BUG() for trace\n", __FUNCTION__); + instr[0] = 0x0f; /* ud2a -- BUG() */ + instr[1] = 0x0b; + instr[2] = 0xcd; /* int 255 */ + instr[3] = 0xff; + masked = 0; + } + } else { + masked = 0; + } +#endif + + return; + } + + evtchn_forward(cpu, regs); +} + +/* --------------------------------------------------------------------- */ + +asmlinkage void do_irq(struct regs *regs) +{ + struct xen_cpu *cpu = get_cpu(); + struct vector *vector = vectors + regs->trapno; + + printk(3, "%s: irq vector %d\n", __FUNCTION__, vector->vec); + + lapic_eoi(cpu); + switch (vector->type) { + case VECTYPE_UNDEFINED: + printk(0, "%s: unhandled irq (vector %d)\n", __FUNCTION__, (int)regs->trapno); + panic("unknown irq", regs); + break; + case VECTYPE_VIRQ: + if (vector->virq == VIRQ_TIMER) { + if (cpu->oneshot) { + cpu->oneshot = 0; + lapic_timer(cpu); + } + pv_clock_update(0); + } + /* fall through */ + default: + vminfo.events[vector->evtchn]++; + evtchn_raise_event(cpu, vector->evtchn); + evtchn_try_forward(cpu, regs); + break; + } + + if (context_is_emu(regs)) { + uint8_t *ins = (void*)regs->rip; + if (ins[0] == 0xf4) { + printk(0, "%s: WARN: rip %" PRIxREG " points to hlt\n", + __FUNCTION__, regs->rip); + } + } +} + +int irq_init(struct xen_cpu *cpu) +{ + int rc; + + rc = lapic_init(cpu); + if (rc == 0) { + return 0; + } else if (rc == 1) { + /* boot cpu */ + ioapic_init(cpu); + evtchn_route_init(cpu); + } + return rc; +}