From patchwork Tue Jan 31 06:34:40 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matt Evans X-Patchwork-Id: 138698 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 8520C1007D1 for ; Tue, 31 Jan 2012 17:35:40 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752448Ab2AaGf3 (ORCPT ); Tue, 31 Jan 2012 01:35:29 -0500 Received: from ozlabs.org ([203.10.76.45]:45617 "EHLO ozlabs.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752087Ab2AaGfP (ORCPT ); Tue, 31 Jan 2012 01:35:15 -0500 Received: from localhost.localdomain (unknown [165.228.126.9]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPSA id ADF8C1007D7; Tue, 31 Jan 2012 17:35:12 +1100 (EST) From: Matt Evans To: kvm@vger.kernel.org, kvm-ppc@vger.kernel.org Cc: penberg@kernel.org, asias.hejun@gmail.com, levinsasha928@gmail.com, gorcunov@gmail.com, david@gibson.dropbear.id.au, aik@ozlabs.ru Subject: [PATCH V4 5/7] kvm tools: Add PPC64 XICS interrupt controller support Date: Tue, 31 Jan 2012 17:34:40 +1100 Message-Id: <1327991682-18503-6-git-send-email-matt@ozlabs.org> X-Mailer: git-send-email 1.7.0.4 In-Reply-To: <1327991682-18503-1-git-send-email-matt@ozlabs.org> References: <1327991682-18503-1-git-send-email-matt@ozlabs.org> Sender: kvm-ppc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm-ppc@vger.kernel.org This patch adds XICS emulation code (heavily borrowed from QEMU), and wires this into kvm_cpu__irq() to fire a CPU IRQ via KVM. A device tree entry is also added. IPIs work, xics_alloc_irqnum() is added to allocate an external IRQ (which will later be used by the PHB PCI code) and finally, kvm__irq_line() can be called to raise an IRQ on XICS. Signed-off-by: Matt Evans --- tools/kvm/Makefile | 1 + tools/kvm/powerpc/include/kvm/kvm-arch.h | 1 + tools/kvm/powerpc/include/kvm/kvm-cpu-arch.h | 2 + tools/kvm/powerpc/irq.c | 25 ++- tools/kvm/powerpc/kvm-cpu.c | 11 + tools/kvm/powerpc/kvm.c | 26 +- tools/kvm/powerpc/xics.c | 514 ++++++++++++++++++++++++++ tools/kvm/powerpc/xics.h | 23 ++ 8 files changed, 596 insertions(+), 7 deletions(-) create mode 100644 tools/kvm/powerpc/xics.c create mode 100644 tools/kvm/powerpc/xics.h diff --git a/tools/kvm/Makefile b/tools/kvm/Makefile index 648550a..2d97b66 100644 --- a/tools/kvm/Makefile +++ b/tools/kvm/Makefile @@ -137,6 +137,7 @@ ifeq ($(uname_M), ppc64) OBJS += powerpc/spapr_hcall.o OBJS += powerpc/spapr_rtas.o OBJS += powerpc/spapr_hvcons.o + OBJS += powerpc/xics.o # We use libfdt, but it's sometimes not packaged 64bit. It's small too, # so just build it in: CFLAGS += -I../../scripts/dtc/libfdt diff --git a/tools/kvm/powerpc/include/kvm/kvm-arch.h b/tools/kvm/powerpc/include/kvm/kvm-arch.h index ca15500..7235067 100644 --- a/tools/kvm/powerpc/include/kvm/kvm-arch.h +++ b/tools/kvm/powerpc/include/kvm/kvm-arch.h @@ -69,6 +69,7 @@ struct kvm { unsigned long initrd_size; const char *name; int vm_state; + struct icp_state *icp; }; /* Helper for the various bits of code that generate FDT nodes */ diff --git a/tools/kvm/powerpc/include/kvm/kvm-cpu-arch.h b/tools/kvm/powerpc/include/kvm/kvm-cpu-arch.h index 64e4510..c1c6539 100644 --- a/tools/kvm/powerpc/include/kvm/kvm-cpu-arch.h +++ b/tools/kvm/powerpc/include/kvm/kvm-cpu-arch.h @@ -36,6 +36,8 @@ #define MSR_RI (1UL<<1) #define MSR_LE (1UL<<0) +#define POWER7_EXT_IRQ 0 + struct kvm; struct kvm_cpu { diff --git a/tools/kvm/powerpc/irq.c b/tools/kvm/powerpc/irq.c index 46aa64f..f8f12e5 100644 --- a/tools/kvm/powerpc/irq.c +++ b/tools/kvm/powerpc/irq.c @@ -21,6 +21,15 @@ #include #include +#include "xics.h" + +#define XICS_IRQS 1024 + +/* + * FIXME: The code in this file assumes an SPAPR guest, using XICS. Make + * generic & cope with multiple PPC platform types. + */ + int irq__register_device(u32 dev, u8 *num, u8 *pin, u8 *line) { fprintf(stderr, "irq__register_device(%d, [%d], [%d], [%d]\n", @@ -28,9 +37,21 @@ int irq__register_device(u32 dev, u8 *num, u8 *pin, u8 *line) return 0; } -void irq__init(struct kvm *kvm) +int irq__init(struct kvm *kvm) { - fprintf(stderr, __func__); + /* + * kvm->nr_cpus is now valid; for /now/, pass + * this to xics_system_init(), which assumes servers + * are numbered 0..nrcpus. This may not really be true, + * but it is OK currently. + */ + kvm->icp = xics_system_init(XICS_IRQS, kvm->nrcpus); + return 0; +} + +int irq__exit(struct kvm *kvm) +{ + return 0; } int irq__add_msix_route(struct kvm *kvm, struct msi_msg *msg) diff --git a/tools/kvm/powerpc/kvm-cpu.c b/tools/kvm/powerpc/kvm-cpu.c index 51bb718..7dd1679 100644 --- a/tools/kvm/powerpc/kvm-cpu.c +++ b/tools/kvm/powerpc/kvm-cpu.c @@ -15,6 +15,7 @@ #include "kvm/kvm.h" #include "spapr.h" +#include "xics.h" #include #include @@ -90,6 +91,9 @@ struct kvm_cpu *kvm_cpu__init(struct kvm *kvm, unsigned long cpu_id) */ vcpu->is_running = true; + /* Register with IRQ controller (FIXME, assumes XICS) */ + xics_cpu_register(vcpu); + return vcpu; } @@ -160,6 +164,13 @@ void kvm_cpu__reset_vcpu(struct kvm_cpu *vcpu) /* kvm_cpu__irq - set KVM's IRQ flag on this vcpu */ void kvm_cpu__irq(struct kvm_cpu *vcpu, int pin, int level) { + unsigned int virq = level ? KVM_INTERRUPT_SET_LEVEL : KVM_INTERRUPT_UNSET; + + /* FIXME: POWER-specific */ + if (pin != POWER7_EXT_IRQ) + return; + if (ioctl(vcpu->vcpu_fd, KVM_INTERRUPT, &virq) < 0) + pr_warning("Could not KVM_INTERRUPT."); } void kvm_cpu__arch_nmi(struct kvm_cpu *cpu) diff --git a/tools/kvm/powerpc/kvm.c b/tools/kvm/powerpc/kvm.c index bb98c10..815108c 100644 --- a/tools/kvm/powerpc/kvm.c +++ b/tools/kvm/powerpc/kvm.c @@ -39,9 +39,13 @@ #define HUGETLBFS_PATH "/var/lib/hugetlbfs/global/pagesize-16MB/" +#define PHANDLE_XICP 0x00001111 + static char kern_cmdline[2048]; struct kvm_ext kvm_req_ext[] = { + { DEFINE_KVM_EXT(KVM_CAP_PPC_UNSET_IRQ) }, + { DEFINE_KVM_EXT(KVM_CAP_PPC_IRQ_LEVEL) }, { 0, 0 } }; @@ -143,11 +147,6 @@ void kvm__arch_delete_ram(struct kvm *kvm) munmap(kvm->ram_start, kvm->ram_size); } -void kvm__irq_line(struct kvm *kvm, int irq, int level) -{ - fprintf(stderr, "irq_line(%d, %d)\n", irq, level); -} - void kvm__irq_trigger(struct kvm *kvm, int irq) { kvm__irq_line(kvm, irq, 1); @@ -225,6 +224,7 @@ static void setup_fdt(struct kvm *kvm) { uint64_t mem_reg_property[] = { 0, cpu_to_be64(kvm->ram_size) }; int smp_cpus = kvm->nrcpus; + uint32_t int_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)}; char hypertas_prop_kvm[] = "hcall-pft\0hcall-term\0" "hcall-dabr\0hcall-interrupt\0hcall-tce\0hcall-vio\0" "hcall-splpar\0hcall-bulk"; @@ -368,6 +368,22 @@ static void setup_fdt(struct kvm *kvm) } _FDT(fdt_end_node(fdt)); + /* IRQ controller */ + _FDT(fdt_begin_node(fdt, "interrupt-controller@0")); + + _FDT(fdt_property_string(fdt, "device_type", + "PowerPC-External-Interrupt-Presentation")); + _FDT(fdt_property_string(fdt, "compatible", "IBM,ppc-xicp")); + _FDT(fdt_property_cell(fdt, "reg", 0)); + _FDT(fdt_property(fdt, "interrupt-controller", NULL, 0)); + _FDT(fdt_property(fdt, "ibm,interrupt-server-ranges", + int_server_ranges_prop, + sizeof(int_server_ranges_prop))); + _FDT(fdt_property_cell(fdt, "#interrupt-cells", 2)); + _FDT(fdt_property_cell(fdt, "linux,phandle", PHANDLE_XICP)); + _FDT(fdt_property_cell(fdt, "phandle", PHANDLE_XICP)); + _FDT(fdt_end_node(fdt)); + /* * VIO: See comment in linux,stdout-path; we don't yet represent a VIO * bus/address allocation so addresses are hardwired here. diff --git a/tools/kvm/powerpc/xics.c b/tools/kvm/powerpc/xics.c new file mode 100644 index 0000000..2d70d3c --- /dev/null +++ b/tools/kvm/powerpc/xics.c @@ -0,0 +1,514 @@ +/* + * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics + * + * Borrowed heavily from QEMU's xics.c, + * Copyright (c) 2010,2011 David Gibson, IBM Corporation. + * + * Modifications copyright 2011 Matt Evans , IBM Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include "spapr.h" +#include "xics.h" +#include "kvm/util.h" + +#include +#include + + +/* #define DEBUG_XICS yes */ +#ifdef DEBUG_XICS +#define xics_dprintf(fmt, ...) \ + do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) +#else +#define xics_dprintf(fmt, ...) \ + do { } while (0) +#endif + +/* + * ICP: Presentation layer + */ + +struct icp_server_state { + uint32_t xirr; + uint8_t pending_priority; + uint8_t mfrr; + struct kvm_cpu *cpu; +}; + +#define XICS_IRQ_OFFSET 16 +#define XISR_MASK 0x00ffffff +#define CPPR_MASK 0xff000000 + +#define XISR(ss) (((ss)->xirr) & XISR_MASK) +#define CPPR(ss) (((ss)->xirr) >> 24) + +struct ics_state; + +struct icp_state { + unsigned long nr_servers; + struct icp_server_state *ss; + struct ics_state *ics; +}; + +static void ics_reject(struct ics_state *ics, int nr); +static void ics_resend(struct ics_state *ics); +static void ics_eoi(struct ics_state *ics, int nr); + +static inline void cpu_irq_raise(struct kvm_cpu *vcpu) +{ + xics_dprintf("INT1[%p]\n", vcpu); + kvm_cpu__irq(vcpu, POWER7_EXT_IRQ, 1); +} + +static inline void cpu_irq_lower(struct kvm_cpu *vcpu) +{ + xics_dprintf("INT0[%p]\n", vcpu); + kvm_cpu__irq(vcpu, POWER7_EXT_IRQ, 0); +} + +static void icp_check_ipi(struct icp_state *icp, int server) +{ + struct icp_server_state *ss = icp->ss + server; + + if (XISR(ss) && (ss->pending_priority <= ss->mfrr)) { + return; + } + + if (XISR(ss)) { + ics_reject(icp->ics, XISR(ss)); + } + + ss->xirr = (ss->xirr & ~XISR_MASK) | XICS_IPI; + ss->pending_priority = ss->mfrr; + cpu_irq_raise(ss->cpu); +} + +static void icp_resend(struct icp_state *icp, int server) +{ + struct icp_server_state *ss = icp->ss + server; + + if (ss->mfrr < CPPR(ss)) { + icp_check_ipi(icp, server); + } + ics_resend(icp->ics); +} + +static void icp_set_cppr(struct icp_state *icp, int server, uint8_t cppr) +{ + struct icp_server_state *ss = icp->ss + server; + uint8_t old_cppr; + uint32_t old_xisr; + + old_cppr = CPPR(ss); + ss->xirr = (ss->xirr & ~CPPR_MASK) | (cppr << 24); + + if (cppr < old_cppr) { + if (XISR(ss) && (cppr <= ss->pending_priority)) { + old_xisr = XISR(ss); + ss->xirr &= ~XISR_MASK; /* Clear XISR */ + cpu_irq_lower(ss->cpu); + ics_reject(icp->ics, old_xisr); + } + } else { + if (!XISR(ss)) { + icp_resend(icp, server); + } + } +} + +static void icp_set_mfrr(struct icp_state *icp, int nr, uint8_t mfrr) +{ + struct icp_server_state *ss = icp->ss + nr; + + ss->mfrr = mfrr; + if (mfrr < CPPR(ss)) { + icp_check_ipi(icp, nr); + } +} + +static uint32_t icp_accept(struct icp_server_state *ss) +{ + uint32_t xirr; + + cpu_irq_lower(ss->cpu); + xirr = ss->xirr; + ss->xirr = ss->pending_priority << 24; + return xirr; +} + +static void icp_eoi(struct icp_state *icp, int server, uint32_t xirr) +{ + struct icp_server_state *ss = icp->ss + server; + + ics_eoi(icp->ics, xirr & XISR_MASK); + /* Send EOI -> ICS */ + ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK); + if (!XISR(ss)) { + icp_resend(icp, server); + } +} + +static void icp_irq(struct icp_state *icp, int server, int nr, uint8_t priority) +{ + struct icp_server_state *ss = icp->ss + server; + xics_dprintf("icp_irq(nr %d, server %d, prio 0x%x)\n", nr, server, priority); + if ((priority >= CPPR(ss)) + || (XISR(ss) && (ss->pending_priority <= priority))) { + xics_dprintf("reject %d, CPPR 0x%x, XISR 0x%x, pprio 0x%x, prio 0x%x\n", + nr, CPPR(ss), XISR(ss), ss->pending_priority, priority); + ics_reject(icp->ics, nr); + } else { + if (XISR(ss)) { + xics_dprintf("reject %d, CPPR 0x%x, XISR 0x%x, pprio 0x%x, prio 0x%x\n", + nr, CPPR(ss), XISR(ss), ss->pending_priority, priority); + ics_reject(icp->ics, XISR(ss)); + } + ss->xirr = (ss->xirr & ~XISR_MASK) | (nr & XISR_MASK); + ss->pending_priority = priority; + cpu_irq_raise(ss->cpu); + } +} + +/* + * ICS: Source layer + */ + +struct ics_irq_state { + int server; + uint8_t priority; + uint8_t saved_priority; + int rejected:1; + int masked_pending:1; +}; + +struct ics_state { + unsigned int nr_irqs; + unsigned int offset; + struct ics_irq_state *irqs; + struct icp_state *icp; +}; + +static int ics_valid_irq(struct ics_state *ics, uint32_t nr) +{ + return (nr >= ics->offset) + && (nr < (ics->offset + ics->nr_irqs)); +} + +static void ics_set_irq_msi(struct ics_state *ics, int srcno, int val) +{ + struct ics_irq_state *irq = ics->irqs + srcno; + + if (val) { + if (irq->priority == 0xff) { + xics_dprintf(" irq pri ff, masked pending\n"); + irq->masked_pending = 1; + } else { + icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); + } + } +} + +static void ics_reject_msi(struct ics_state *ics, int nr) +{ + struct ics_irq_state *irq = ics->irqs + nr - ics->offset; + + irq->rejected = 1; +} + +static void ics_resend_msi(struct ics_state *ics) +{ + unsigned int i; + + for (i = 0; i < ics->nr_irqs; i++) { + struct ics_irq_state *irq = ics->irqs + i; + + /* FIXME: filter by server#? */ + if (irq->rejected) { + irq->rejected = 0; + if (irq->priority != 0xff) { + icp_irq(ics->icp, irq->server, i + ics->offset, irq->priority); + } + } + } +} + +static void ics_write_xive_msi(struct ics_state *ics, int nr, int server, + uint8_t priority) +{ + struct ics_irq_state *irq = ics->irqs + nr - ics->offset; + + irq->server = server; + irq->priority = priority; + xics_dprintf("ics_write_xive_msi(nr %d, server %d, pri 0x%x)\n", nr, server, priority); + + if (!irq->masked_pending || (priority == 0xff)) { + return; + } + + irq->masked_pending = 0; + icp_irq(ics->icp, server, nr, priority); +} + +static void ics_reject(struct ics_state *ics, int nr) +{ + ics_reject_msi(ics, nr); +} + +static void ics_resend(struct ics_state *ics) +{ + ics_resend_msi(ics); +} + +static void ics_eoi(struct ics_state *ics, int nr) +{ +} + +/* + * Exported functions + */ + +static int allocated_irqnum = XICS_IRQ_OFFSET; + +/* + * xics_alloc_irqnum(): This is hacky. The problem boils down to the PCI device + * code which just calls kvm__irq_line( .. pcidev->pci_hdr.irq_line ..) at will. + * Each PCI device's IRQ line is allocated by irq__register_device() (which + * allocates an IRQ AND allocates a.. PCI device num..). + * + * In future I'd like to at least mimic some kind of 'upstream IRQ controller' + * whereby PCI devices let their PHB know when they want to IRQ, and that + * percolates up. + * + * For now, allocate a REAL xics irq number and (via irq__register_device) push + * that into the config space. 8 bits only though! + */ +int xics_alloc_irqnum(void) +{ + int irq = allocated_irqnum++; + + if (irq > 255) + die("Huge numbers of IRQs aren't supported with the daft kvmtool IRQ system."); + + return irq; +} + +static target_ulong h_cppr(struct kvm_cpu *vcpu, + target_ulong opcode, target_ulong *args) +{ + target_ulong cppr = args[0]; + + xics_dprintf("h_cppr(%lx)\n", cppr); + icp_set_cppr(vcpu->kvm->icp, vcpu->cpu_id, cppr); + return H_SUCCESS; +} + +static target_ulong h_ipi(struct kvm_cpu *vcpu, + target_ulong opcode, target_ulong *args) +{ + target_ulong server = args[0]; + target_ulong mfrr = args[1]; + + xics_dprintf("h_ipi(%lx, %lx)\n", server, mfrr); + if (server >= vcpu->kvm->icp->nr_servers) { + return H_PARAMETER; + } + + icp_set_mfrr(vcpu->kvm->icp, server, mfrr); + return H_SUCCESS; +} + +static target_ulong h_xirr(struct kvm_cpu *vcpu, + target_ulong opcode, target_ulong *args) +{ + uint32_t xirr = icp_accept(vcpu->kvm->icp->ss + vcpu->cpu_id); + + xics_dprintf("h_xirr() = %x\n", xirr); + args[0] = xirr; + return H_SUCCESS; +} + +static target_ulong h_eoi(struct kvm_cpu *vcpu, + target_ulong opcode, target_ulong *args) +{ + target_ulong xirr = args[0]; + + xics_dprintf("h_eoi(%lx)\n", xirr); + icp_eoi(vcpu->kvm->icp, vcpu->cpu_id, xirr); + return H_SUCCESS; +} + +static void rtas_set_xive(struct kvm_cpu *vcpu, uint32_t token, + uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets) +{ + struct ics_state *ics = vcpu->kvm->icp->ics; + uint32_t nr, server, priority; + + if ((nargs != 3) || (nret != 1)) { + rtas_st(vcpu->kvm, rets, 0, -3); + return; + } + + nr = rtas_ld(vcpu->kvm, args, 0); + server = rtas_ld(vcpu->kvm, args, 1); + priority = rtas_ld(vcpu->kvm, args, 2); + + xics_dprintf("rtas_set_xive(%x,%x,%x)\n", nr, server, priority); + if (!ics_valid_irq(ics, nr) || (server >= ics->icp->nr_servers) + || (priority > 0xff)) { + rtas_st(vcpu->kvm, rets, 0, -3); + return; + } + + ics_write_xive_msi(ics, nr, server, priority); + + rtas_st(vcpu->kvm, rets, 0, 0); /* Success */ +} + +static void rtas_get_xive(struct kvm_cpu *vcpu, uint32_t token, + uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets) +{ + struct ics_state *ics = vcpu->kvm->icp->ics; + uint32_t nr; + + if ((nargs != 1) || (nret != 3)) { + rtas_st(vcpu->kvm, rets, 0, -3); + return; + } + + nr = rtas_ld(vcpu->kvm, args, 0); + + if (!ics_valid_irq(ics, nr)) { + rtas_st(vcpu->kvm, rets, 0, -3); + return; + } + + rtas_st(vcpu->kvm, rets, 0, 0); /* Success */ + rtas_st(vcpu->kvm, rets, 1, ics->irqs[nr - ics->offset].server); + rtas_st(vcpu->kvm, rets, 2, ics->irqs[nr - ics->offset].priority); +} + +static void rtas_int_off(struct kvm_cpu *vcpu, uint32_t token, + uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets) +{ + struct ics_state *ics = vcpu->kvm->icp->ics; + uint32_t nr; + + if ((nargs != 1) || (nret != 1)) { + rtas_st(vcpu->kvm, rets, 0, -3); + return; + } + + nr = rtas_ld(vcpu->kvm, args, 0); + + if (!ics_valid_irq(ics, nr)) { + rtas_st(vcpu->kvm, rets, 0, -3); + return; + } + + /* ME: QEMU wrote xive_msi here, in #if 0. Deleted. */ + + rtas_st(vcpu->kvm, rets, 0, 0); /* Success */ +} + +static void rtas_int_on(struct kvm_cpu *vcpu, uint32_t token, + uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets) +{ + struct ics_state *ics = vcpu->kvm->icp->ics; + uint32_t nr; + + if ((nargs != 1) || (nret != 1)) { + rtas_st(vcpu->kvm, rets, 0, -3); + return; + } + + nr = rtas_ld(vcpu->kvm, args, 0); + + if (!ics_valid_irq(ics, nr)) { + rtas_st(vcpu->kvm, rets, 0, -3); + return; + } + + /* ME: QEMU wrote xive_msi here, in #if 0. Deleted. */ + + rtas_st(vcpu->kvm, rets, 0, 0); /* Success */ +} + +void xics_cpu_register(struct kvm_cpu *vcpu) +{ + if (vcpu->cpu_id < vcpu->kvm->icp->nr_servers) + vcpu->kvm->icp->ss[vcpu->cpu_id].cpu = vcpu; + else + die("Setting invalid server for cpuid %ld\n", vcpu->cpu_id); +} + +struct icp_state *xics_system_init(unsigned int nr_irqs, unsigned int nr_cpus) +{ + int max_server_num; + unsigned int i; + struct icp_state *icp; + struct ics_state *ics; + + max_server_num = nr_cpus; + + icp = malloc(sizeof(*icp)); + icp->nr_servers = max_server_num + 1; + icp->ss = malloc(icp->nr_servers*sizeof(struct icp_server_state)); + + for (i = 0; i < icp->nr_servers; i++) { + icp->ss[i].xirr = 0; + icp->ss[i].pending_priority = 0; + icp->ss[i].cpu = 0; + icp->ss[i].mfrr = 0xff; + } + + /* + * icp->ss[env->cpu_index].cpu is set by CPUs calling in to + * xics_cpu_register(). + */ + + ics = malloc(sizeof(*ics)); + ics->nr_irqs = nr_irqs; + ics->offset = XICS_IRQ_OFFSET; + ics->irqs = malloc(nr_irqs * sizeof(struct ics_irq_state)); + + icp->ics = ics; + ics->icp = icp; + + for (i = 0; i < nr_irqs; i++) { + ics->irqs[i].server = 0; + ics->irqs[i].priority = 0xff; + ics->irqs[i].saved_priority = 0xff; + ics->irqs[i].rejected = 0; + ics->irqs[i].masked_pending = 0; + } + + spapr_register_hypercall(H_CPPR, h_cppr); + spapr_register_hypercall(H_IPI, h_ipi); + spapr_register_hypercall(H_XIRR, h_xirr); + spapr_register_hypercall(H_EOI, h_eoi); + + spapr_rtas_register("ibm,set-xive", rtas_set_xive); + spapr_rtas_register("ibm,get-xive", rtas_get_xive); + spapr_rtas_register("ibm,int-off", rtas_int_off); + spapr_rtas_register("ibm,int-on", rtas_int_on); + + return icp; +} + +void kvm__irq_line(struct kvm *kvm, int irq, int level) +{ + /* + * Route event to ICS, which routes to ICP, which eventually does a + * kvm_cpu__irq(vcpu, POWER7_EXT_IRQ, 1) + */ + xics_dprintf("Raising IRQ %d -> %d\n", irq, level); + ics_set_irq_msi(kvm->icp->ics, irq - kvm->icp->ics->offset, level); +} diff --git a/tools/kvm/powerpc/xics.h b/tools/kvm/powerpc/xics.h new file mode 100644 index 0000000..144915b --- /dev/null +++ b/tools/kvm/powerpc/xics.h @@ -0,0 +1,23 @@ +/* + * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics + * + * Copyright 2011 Matt Evans , IBM Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#ifndef XICS_H +#define XICS_H + +#define XICS_IPI 0x2 + +struct kvm_cpu; +struct icp_state; + +struct icp_state *xics_system_init(unsigned int nr_irqs, unsigned int nr_cpus); +void xics_cpu_register(struct kvm_cpu *vcpu); +int xics_alloc_irqnum(void); + +#endif