From patchwork Sat Dec 9 08:43:24 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?C=C3=A9dric_Le_Goater?= X-Patchwork-Id: 846529 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=nongnu.org (client-ip=2001:4830:134:3::11; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3yv2qF6GXFz9s7v for ; Sat, 9 Dec 2017 19:45:29 +1100 (AEDT) Received: from localhost ([::1]:40415 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eNalD-00015W-TY for incoming@patchwork.ozlabs.org; Sat, 09 Dec 2017 03:45:27 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:45337) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eNakK-00012c-QX for qemu-devel@nongnu.org; Sat, 09 Dec 2017 03:44:35 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1eNakG-0006yM-Qk for qemu-devel@nongnu.org; Sat, 09 Dec 2017 03:44:32 -0500 Received: from 20.mo6.mail-out.ovh.net ([178.32.124.17]:58784) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1eNakG-0006wm-HW for qemu-devel@nongnu.org; Sat, 09 Dec 2017 03:44:28 -0500 Received: from player735.ha.ovh.net (b9.ovh.net [213.186.33.59]) by mo6.mail-out.ovh.net (Postfix) with ESMTP id 17E9D1277C8 for ; Sat, 9 Dec 2017 09:44:27 +0100 (CET) Received: from zorba.kaod.org.com (LFbn-1-2231-173.w90-76.abo.wanadoo.fr [90.76.52.173]) (Authenticated sender: clg@kaod.org) by player735.ha.ovh.net (Postfix) with ESMTPSA id DEEC0160090; Sat, 9 Dec 2017 09:44:20 +0100 (CET) From: =?utf-8?q?C=C3=A9dric_Le_Goater?= To: qemu-ppc@nongnu.org, qemu-devel@nongnu.org, David Gibson , Benjamin Herrenschmidt , Greg Kurz Date: Sat, 9 Dec 2017 09:43:24 +0100 Message-Id: <20171209084338.29395-6-clg@kaod.org> X-Mailer: git-send-email 2.13.6 In-Reply-To: <20171209084338.29395-1-clg@kaod.org> References: <20171209084338.29395-1-clg@kaod.org> MIME-Version: 1.0 X-Ovh-Tracer-Id: 11703448058253183827 X-VR-SPAMSTATE: OK X-VR-SPAMSCORE: -100 X-VR-SPAMCAUSE: gggruggvucftvghtrhhoucdtuddrgedtuddrvddugdduvdegucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuqfggjfdpvefjgfevmfevgfenuceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddm X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 178.32.124.17 Subject: [Qemu-devel] [PATCH v2 05/19] spapr: introduce a XIVE interrupt presenter model X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?utf-8?q?C=C3=A9dric_Le_Goater?= Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" Once an event has been routed by the IVRE, it reaches the XIVE virtualization presentation engine which, simply speaking, raises one bit in the Interrupt Pending Buffer (IBP) register corresponding to the priority of the pending interrupt. This indicates there is an event pending in one of the 8 priority queues and the interrupt can then be delivered to the Virtual Processor. The XIVE presenter engine uses a set of registers to handle priority management and interrupt acknowledgment among other things. The most important being : - Interrupt Priority Register (PIPR) - Interrupt Pending Buffer (IPB) - Current Processor Priority (CPPR) - Notification Source Register (NSR) There is one set of registers per level of privilege, four in all : HW, HV pool, OS and User. These are called rings. All registers are accessible through a specific MMIO region called the Thread Interrupt Management Areas (TIMA) but, depending on the privilege level of the CPU, the view of the TIMA is filtered. The sPAPR machine runs at the OS privilege and therefore can only accesses the OS and the User rings. The others are for hypervisor levels. The CPU interrupt state is modeled with a sPAPRXiveNVT object which stores the values of the different registers. The different TIMA views are mapped at the same address for each CPU and 'current_cpu' is used to retrieve the sPAPRXiveNVT holding the ring registers. Signed-off-by: Cédric Le Goater --- Changes since v1: - renamed 'sPAPRXiveICP' to 'sPAPRXiveNVT' - renamed 'tima' to 'regs' - renamed 'tima_os' to 'ring_os' - introduced TM_RING_SIZE - removed 'tm_shift' field - introduced a memory region to model the User TIMA and another one for the OS TIMA. One page size for each. - removed useless checks in the memory region handlers - removed support for 970 ... hw/intc/spapr_xive.c | 281 ++++++++++++++++++++++++++++++++++++++++++++ hw/intc/xive-internal.h | 92 +++++++++++++++ include/hw/ppc/spapr_xive.h | 11 ++ 3 files changed, 384 insertions(+) diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index c772c726667f..53f0e698e135 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -15,9 +15,177 @@ #include "sysemu/dma.h" #include "monitor/monitor.h" #include "hw/ppc/spapr_xive.h" +#include "hw/ppc/xics.h" #include "xive-internal.h" +struct sPAPRXiveNVT { + DeviceState parent_obj; + + CPUState *cs; + qemu_irq output; + + /* Registers for all rings but sPAPR can only access the OS ring */ + uint8_t regs[TM_RING_COUNT * TM_RING_SIZE]; + + /* Shortcut to the OS ring */ + uint8_t *ring_os; +}; + +static uint64_t spapr_xive_nvt_accept(sPAPRXiveNVT *nvt) +{ + return 0; +} + +static void spapr_xive_nvt_set_cppr(sPAPRXiveNVT *nvt, uint8_t cppr) +{ + if (cppr > XIVE_PRIORITY_MAX) { + cppr = 0xff; + } + + nvt->ring_os[TM_CPPR] = cppr; +} + +/* + * Thread Interrupt Management Area MMIO + */ +static uint64_t spapr_xive_tm_read_special(sPAPRXiveNVT *nvt, hwaddr offset, + unsigned size) +{ + uint64_t ret = -1; + + if (offset == TM_SPC_ACK_OS_REG && size == 2) { + ret = spapr_xive_nvt_accept(nvt); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid TIMA read @%" + HWADDR_PRIx" size %d\n", offset, size); + } + + return ret; +} + +#define TM_RING(offset) ((offset) & 0xf0) + +static uint64_t spapr_xive_tm_os_read(void *opaque, hwaddr offset, + unsigned size) +{ + PowerPCCPU *cpu = POWERPC_CPU(current_cpu); + sPAPRXiveNVT *nvt = SPAPR_XIVE_NVT(cpu->intc); + uint64_t ret = -1; + int i; + + if (offset >= TM_SPC_ACK_EBB) { + return spapr_xive_tm_read_special(nvt, offset, size); + } + + if (TM_RING(offset) != TM_QW1_OS) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid access to non-OS ring @%" + HWADDR_PRIx"\n", offset); + return ret; + } + + ret = 0; + for (i = 0; i < size; i++) { + ret |= nvt->regs[offset + i] << (8 * i); + } + + return ret; +} + +static bool spapr_xive_tm_is_readonly(uint8_t offset) +{ + return offset != TM_QW1_OS + TM_CPPR; +} + +static void spapr_xive_tm_write_special(sPAPRXiveNVT *nvt, hwaddr offset, + uint64_t value, unsigned size) +{ + /* TODO: support TM_SPC_SET_OS_PENDING */ + + /* TODO: support TM_SPC_ACK_OS_EL */ +} + +static void spapr_xive_tm_os_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + PowerPCCPU *cpu = POWERPC_CPU(current_cpu); + sPAPRXiveNVT *nvt = SPAPR_XIVE_NVT(cpu->intc); + int i; + + if (offset >= TM_SPC_ACK_EBB) { + spapr_xive_tm_write_special(nvt, offset, value, size); + return; + } + + if (TM_RING(offset) != TM_QW1_OS) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid access to non-OS ring @%" + HWADDR_PRIx"\n", offset); + return; + } + + switch (size) { + case 1: + if (offset == TM_QW1_OS + TM_CPPR) { + spapr_xive_nvt_set_cppr(nvt, value & 0xff); + } + break; + case 4: + case 8: + for (i = 0; i < size; i++) { + if (!spapr_xive_tm_is_readonly(offset + i)) { + nvt->regs[offset + i] = (value >> (8 * i)) & 0xff; + } + } + break; + default: + g_assert_not_reached(); + } +} + +static const MemoryRegionOps spapr_xive_tm_os_ops = { + .read = spapr_xive_tm_os_read, + .write = spapr_xive_tm_os_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + +static uint64_t spapr_xive_tm_user_read(void *opaque, hwaddr offset, + unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "XIVE: invalid access to User TIMA @%" + HWADDR_PRIx"\n", offset); + return -1; +} + +static void spapr_xive_tm_user_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "XIVE: invalid access to User TIMA @%" + HWADDR_PRIx"\n", offset); +} + + +static const MemoryRegionOps spapr_xive_tm_user_ops = { + .read = spapr_xive_tm_user_read, + .write = spapr_xive_tm_user_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + static void spapr_xive_irq(sPAPRXive *xive, int lisn) { @@ -358,6 +526,22 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp) &spapr_xive_esb_ops, xive, "xive.esb", (1ull << ESB_SHIFT) * xive->nr_irqs); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->esb_iomem); + + /* The Thread Interrupt Management Area has the same address for + * each chip. On sPAPR, we only need to expose the User and OS + * level views of the TIMA. + */ + xive->tm_base = (P9_MMIO_BASE | TM_BAR_DEFAULT); + + memory_region_init_io(&xive->tm_iomem_user, OBJECT(xive), + &spapr_xive_tm_user_ops, xive, "xive.tima.user", + 1ull << TM_SHIFT); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_iomem_user); + + memory_region_init_io(&xive->tm_iomem_os, OBJECT(xive), + &spapr_xive_tm_os_ops, xive, "xive.tima.os", + 1ull << TM_SHIFT); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_iomem_os); } static const VMStateDescription vmstate_spapr_xive_ive = { @@ -413,9 +597,106 @@ static const TypeInfo spapr_xive_info = { .class_init = spapr_xive_class_init, }; +void spapr_xive_nvt_pic_print_info(sPAPRXiveNVT *nvt, Monitor *mon) +{ + int cpu_index = nvt->cs ? nvt->cs->cpu_index : -1; + + monitor_printf(mon, "CPU %d CPPR=%02x IPB=%02x PIPR=%02x NSR=%02x\n", + cpu_index, nvt->ring_os[TM_CPPR], nvt->ring_os[TM_IPB], + nvt->ring_os[TM_PIPR], nvt->ring_os[TM_NSR]); +} + +static void spapr_xive_nvt_reset(void *dev) +{ + sPAPRXiveNVT *nvt = SPAPR_XIVE_NVT(dev); + + memset(nvt->regs, 0, sizeof(nvt->regs)); +} + +static void spapr_xive_nvt_realize(DeviceState *dev, Error **errp) +{ + sPAPRXiveNVT *nvt = SPAPR_XIVE_NVT(dev); + PowerPCCPU *cpu; + CPUPPCState *env; + Object *obj; + Error *err = NULL; + + obj = object_property_get_link(OBJECT(dev), ICP_PROP_CPU, &err); + if (!obj) { + error_propagate(errp, err); + error_prepend(errp, "required link '" ICP_PROP_CPU "' not found: "); + return; + } + + cpu = POWERPC_CPU(obj); + nvt->cs = CPU(obj); + + env = &cpu->env; + switch (PPC_INPUT(env)) { + case PPC_FLAGS_INPUT_POWER7: + nvt->output = env->irq_inputs[POWER7_INPUT_INT]; + break; + + default: + error_setg(errp, "XIVE interrupt controller does not support " + "this CPU bus model"); + return; + } + + qemu_register_reset(spapr_xive_nvt_reset, dev); +} + +static void spapr_xive_nvt_unrealize(DeviceState *dev, Error **errp) +{ + qemu_unregister_reset(spapr_xive_nvt_reset, dev); +} + +static void spapr_xive_nvt_init(Object *obj) +{ + sPAPRXiveNVT *nvt = SPAPR_XIVE_NVT(obj); + + nvt->ring_os = &nvt->regs[TM_QW1_OS]; +} + +static bool vmstate_spapr_xive_nvt_needed(void *opaque) +{ + /* TODO check machine XIVE support */ + return true; +} + +static const VMStateDescription vmstate_spapr_xive_nvt = { + .name = TYPE_SPAPR_XIVE_NVT, + .version_id = 1, + .minimum_version_id = 1, + .needed = vmstate_spapr_xive_nvt_needed, + .fields = (VMStateField[]) { + VMSTATE_BUFFER(regs, sPAPRXiveNVT), + VMSTATE_END_OF_LIST() + }, +}; + +static void spapr_xive_nvt_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = spapr_xive_nvt_realize; + dc->unrealize = spapr_xive_nvt_unrealize; + dc->desc = "sPAPR XIVE Interrupt Presenter"; + dc->vmsd = &vmstate_spapr_xive_nvt; +} + +static const TypeInfo xive_nvt_info = { + .name = TYPE_SPAPR_XIVE_NVT, + .parent = TYPE_DEVICE, + .instance_size = sizeof(sPAPRXiveNVT), + .instance_init = spapr_xive_nvt_init, + .class_init = spapr_xive_nvt_class_init, +}; + static void spapr_xive_register_types(void) { type_register_static(&spapr_xive_info); + type_register_static(&xive_nvt_info); } type_init(spapr_xive_register_types) diff --git a/hw/intc/xive-internal.h b/hw/intc/xive-internal.h index 872648dd96a2..49f4b7c5f393 100644 --- a/hw/intc/xive-internal.h +++ b/hw/intc/xive-internal.h @@ -26,6 +26,96 @@ #define VC_BAR_SIZE 0x08000000000ull #define ESB_SHIFT 16 /* One 64k page. OPAL has two */ +/* Thread Interrupt Management Area */ +#define TM_BAR_DEFAULT 0x30203180000ull +#define TM_SHIFT 16 + +/* + * Thread Management (aka "TM") registers + */ +#define TM_RING_COUNT 4 +#define TM_RING_SIZE 0x10 + +/* TM register offsets */ +#define TM_QW0_USER 0x000 /* All rings */ +#define TM_QW1_OS 0x010 /* Ring 0..2 */ +#define TM_QW2_HV_POOL 0x020 /* Ring 0..1 */ +#define TM_QW3_HV_PHYS 0x030 /* Ring 0..1 */ + +/* Byte offsets inside a QW QW0 QW1 QW2 QW3 */ +#define TM_NSR 0x0 /* + + - + */ +#define TM_CPPR 0x1 /* - + - + */ +#define TM_IPB 0x2 /* - + + + */ +#define TM_LSMFB 0x3 /* - + + + */ +#define TM_ACK_CNT 0x4 /* - + - - */ +#define TM_INC 0x5 /* - + - + */ +#define TM_AGE 0x6 /* - + - + */ +#define TM_PIPR 0x7 /* - + - + */ + +#define TM_WORD0 0x0 +#define TM_WORD1 0x4 + +/* + * QW word 2 contains the valid bit at the top and other fields + * depending on the QW. + */ +#define TM_WORD2 0x8 +#define TM_QW0W2_VU PPC_BIT32(0) +#define TM_QW0W2_LOGIC_SERV PPC_BITMASK32(1, 31) /* XX 2,31 ? */ +#define TM_QW1W2_VO PPC_BIT32(0) +#define TM_QW1W2_OS_CAM PPC_BITMASK32(8, 31) +#define TM_QW2W2_VP PPC_BIT32(0) +#define TM_QW2W2_POOL_CAM PPC_BITMASK32(8, 31) +#define TM_QW3W2_VT PPC_BIT32(0) +#define TM_QW3W2_LP PPC_BIT32(6) +#define TM_QW3W2_LE PPC_BIT32(7) +#define TM_QW3W2_T PPC_BIT32(31) + +/* + * In addition to normal loads to "peek" and writes (only when invalid) + * using 4 and 8 bytes accesses, the above registers support these + * "special" byte operations: + * + * - Byte load from QW0[NSR] - User level NSR (EBB) + * - Byte store to QW0[NSR] - User level NSR (EBB) + * - Byte load/store to QW1[CPPR] and QW3[CPPR] - CPPR access + * - Byte load from QW3[TM_WORD2] - Read VT||00000||LP||LE on thrd 0 + * otherwise VT||0000000 + * - Byte store to QW3[TM_WORD2] - Set VT bit (and LP/LE if present) + * + * Then we have all these "special" CI ops at these offset that trigger + * all sorts of side effects: + */ +#define TM_SPC_ACK_EBB 0x800 /* Load8 ack EBB to reg*/ +#define TM_SPC_ACK_OS_REG 0x810 /* Load16 ack OS irq to reg */ +#define TM_SPC_PUSH_USR_CTX 0x808 /* Store32 Push/Validate user context */ +#define TM_SPC_PULL_USR_CTX 0x808 /* Load32 Pull/Invalidate user + * context */ +#define TM_SPC_SET_OS_PENDING 0x812 /* Store8 Set OS irq pending bit */ +#define TM_SPC_PULL_OS_CTX 0x818 /* Load32/Load64 Pull/Invalidate OS + * context to reg */ +#define TM_SPC_PULL_POOL_CTX 0x828 /* Load32/Load64 Pull/Invalidate Pool + * context to reg*/ +#define TM_SPC_ACK_HV_REG 0x830 /* Load16 ack HV irq to reg */ +#define TM_SPC_PULL_USR_CTX_OL 0xc08 /* Store8 Pull/Inval usr ctx to odd + * line */ +#define TM_SPC_ACK_OS_EL 0xc10 /* Store8 ack OS irq to even line */ +#define TM_SPC_ACK_HV_POOL_EL 0xc20 /* Store8 ack HV evt pool to even + * line */ +#define TM_SPC_ACK_HV_EL 0xc30 /* Store8 ack HV irq to even line */ +/* XXX more... */ + +/* NSR fields for the various QW ack types */ +#define TM_QW0_NSR_EB PPC_BIT8(0) +#define TM_QW1_NSR_EO PPC_BIT8(0) +#define TM_QW3_NSR_HE PPC_BITMASK8(0, 1) +#define TM_QW3_NSR_HE_NONE 0 +#define TM_QW3_NSR_HE_POOL 1 +#define TM_QW3_NSR_HE_PHYS 2 +#define TM_QW3_NSR_HE_LSI 3 +#define TM_QW3_NSR_I PPC_BIT8(2) +#define TM_QW3_NSR_GRP_LVL PPC_BIT8(3, 7) + /* IVE/EAS * * One per interrupt source. Targets that interrupt to a given EQ @@ -46,6 +136,8 @@ typedef struct XiveIVE { #define IVE_EQ_DATA PPC_BITMASK(33, 63) /* Data written to the EQ */ } XiveIVE; +#define XIVE_PRIORITY_MAX 7 + XiveIVE *spapr_xive_get_ive(sPAPRXive *xive, uint32_t lisn); #endif /* _INTC_XIVE_INTERNAL_H */ diff --git a/include/hw/ppc/spapr_xive.h b/include/hw/ppc/spapr_xive.h index a7e59fd601d7..dcaa69025878 100644 --- a/include/hw/ppc/spapr_xive.h +++ b/include/hw/ppc/spapr_xive.h @@ -14,10 +14,15 @@ typedef struct sPAPRXive sPAPRXive; typedef struct XiveIVE XiveIVE; +typedef struct sPAPRXiveNVT sPAPRXiveNVT; #define TYPE_SPAPR_XIVE "spapr-xive" #define SPAPR_XIVE(obj) OBJECT_CHECK(sPAPRXive, (obj), TYPE_SPAPR_XIVE) +#define TYPE_SPAPR_XIVE_NVT "spapr-xive-nvt" +#define SPAPR_XIVE_NVT(obj) \ + OBJECT_CHECK(sPAPRXiveNVT, (obj), TYPE_SPAPR_XIVE_NVT) + struct sPAPRXive { SysBusDevice parent; @@ -38,6 +43,11 @@ struct sPAPRXive { /* ESB memory region */ hwaddr esb_base; MemoryRegion esb_iomem; + + /* TIMA memory regions */ + hwaddr tm_base; + MemoryRegion tm_iomem_user; + MemoryRegion tm_iomem_os; }; static inline bool spapr_xive_irq_is_lsi(sPAPRXive *xive, int lisn) @@ -48,5 +58,6 @@ static inline bool spapr_xive_irq_is_lsi(sPAPRXive *xive, int lisn) bool spapr_xive_irq_enable(sPAPRXive *xive, uint32_t lisn, bool lsi); bool spapr_xive_irq_disable(sPAPRXive *xive, uint32_t lisn); void spapr_xive_pic_print_info(sPAPRXive *xive, Monitor *mon); +void spapr_xive_nvt_pic_print_info(sPAPRXiveNVT *nvt, Monitor *mon); #endif /* PPC_SPAPR_XIVE_H */