From patchwork Thu Jun 2 15:12:42 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Gibson X-Patchwork-Id: 98415 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 1887DB6FBF for ; Fri, 3 Jun 2011 01:20:34 +1000 (EST) Received: from localhost ([::1]:56432 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QS9hF-0005Uk-Mn for incoming@patchwork.ozlabs.org; Thu, 02 Jun 2011 11:20:29 -0400 Received: from eggs.gnu.org ([140.186.70.92]:55157) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QS9aC-0004X9-AZ for qemu-devel@nongnu.org; Thu, 02 Jun 2011 11:13:15 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1QS9a5-0005LH-Av for qemu-devel@nongnu.org; Thu, 02 Jun 2011 11:13:12 -0400 Received: from ozlabs.org ([203.10.76.45]:35351) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QS9a2-0005Ix-5L for qemu-devel@nongnu.org; Thu, 02 Jun 2011 11:13:04 -0400 Received: by ozlabs.org (Postfix, from userid 1007) id C361AB6FAF; Fri, 3 Jun 2011 01:12:55 +1000 (EST) From: David Gibson To: qemu-devel@nongnu.org, eduard.munteanu@linux360.ro, rth@twiddle.net Date: Fri, 3 Jun 2011 01:12:42 +1000 Message-Id: <1307027562-3460-15-git-send-email-david@gibson.dropbear.id.au> X-Mailer: git-send-email 1.7.4.4 In-Reply-To: <1307027562-3460-1-git-send-email-david@gibson.dropbear.id.au> References: <1307027562-3460-1-git-send-email-david@gibson.dropbear.id.au> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) X-Received-From: 203.10.76.45 Subject: [Qemu-devel] [PATCH 14/14] Make spapr tces use generic dma layer X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org --- Makefile.target | 4 +- hw/spapr.c | 3 + hw/spapr.h | 14 +++- hw/spapr_iommu.c | 236 ++++++++++++++++++++++++++++++++++++++++++++ hw/spapr_llan.c | 70 ++++++++------ hw/spapr_vio.c | 289 +++-------------------------------------------------- hw/spapr_vio.h | 60 ++++++------ hw/spapr_vscsi.c | 26 +++--- hw/spapr_vty.c | 1 + 9 files changed, 354 insertions(+), 349 deletions(-) create mode 100644 hw/spapr_iommu.c diff --git a/Makefile.target b/Makefile.target index 042ba1b..365d43e 100644 --- a/Makefile.target +++ b/Makefile.target @@ -252,8 +252,8 @@ obj-ppc-y += ppc_oldworld.o # NewWorld PowerMac obj-ppc-y += ppc_newworld.o # IBM pSeries (sPAPR) -ifeq ($(CONFIG_FDT)$(TARGET_PPC64),yy) -obj-ppc-y += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o +ifeq ($(CONFIG_FDT)$(CONFIG_IOMMU)$(TARGET_PPC64),yyy) +obj-ppc-y += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o spapr_iommu.o obj-ppc-y += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o obj-ppc-$(CONFIG_VIRTIO) += spapr_virtio.o endif diff --git a/hw/spapr.c b/hw/spapr.c index febcad4..45b87bb 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -415,6 +415,9 @@ static void ppc_spapr_init(ram_addr_t ram_size, /* Set up Interrupt Controller */ spapr->icp = xics_system_init(XICS_IRQS); + /* Set up IOMMU */ + spapr_iommu_init(); + /* Set up VIO bus */ spapr->vio_bus = spapr_vio_bus_init(); diff --git a/hw/spapr.h b/hw/spapr.h index 382b0f1..525c9ba 100644 --- a/hw/spapr.h +++ b/hw/spapr.h @@ -1,6 +1,8 @@ #if !defined(__HW_SPAPR_H__) #define __HW_SPAPR_H__ +#include "dma_rw.h" + struct VIOsPAPRBus; struct VirtIOsPAPRBus; struct icp_state; @@ -269,7 +271,7 @@ typedef struct sPAPREnvironment { extern sPAPREnvironment *spapr; -/*#define DEBUG_SPAPR_HCALLS*/ +#define DEBUG_SPAPR_HCALLS #ifdef DEBUG_SPAPR_HCALLS #define hcall_dprintf(fmt, ...) \ @@ -307,4 +309,14 @@ target_ulong spapr_rtas_call(sPAPREnvironment *spapr, int spapr_rtas_device_tree_setup(void *fdt, target_phys_addr_t rtas_addr, target_phys_addr_t rtas_size); +#define SPAPR_TCE_PAGE_SHIFT 12 +#define SPAPR_TCE_PAGE_SIZE (1ULL << SPAPR_TCE_PAGE_SHIFT) +#define SPAPR_TCE_PAGE_MASK (SPAPR_TCE_PAGE_SIZE - 1) + +void spapr_iommu_init(void); +void spapr_tce_init_dev(DeviceState *dev, uint64_t liobn, + size_t window_size); +void spapr_tce_clear_dev(DeviceState *dev); +int spapr_dma_dt(void *fdt, int node_off, DMAMmu *iommu); + #endif /* !defined (__HW_SPAPR_H__) */ diff --git a/hw/spapr_iommu.c b/hw/spapr_iommu.c new file mode 100644 index 0000000..49b930d --- /dev/null +++ b/hw/spapr_iommu.c @@ -0,0 +1,236 @@ +/* + * QEMU sPAPR IOMMU (TCE) code + * + * Copyright (c) 2010 David Gibson, IBM Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#include "hw.h" +#include "kvm.h" +#include "qdev.h" +#include "kvm_ppc.h" +#include "hw/dma_rw.h" + +#include "hw/spapr.h" + +#include + +/* #define DEBUG_TCE */ + +enum sPAPRTCEAccess { + SPAPR_TCE_FAULT = 0, + SPAPR_TCE_RO = 1, + SPAPR_TCE_WO = 2, + SPAPR_TCE_RW = 3, +}; + +typedef struct sPAPRTCE { + uint64_t tce; +} sPAPRTCE; + + +typedef struct sPAPRTCETable sPAPRTCETable; + +struct sPAPRTCETable { + struct DMAMmu dma; + target_ulong liobn; + uint32_t window_size; + sPAPRTCE *table; + int fd; + QLIST_ENTRY(sPAPRTCETable) list; +}; + + +QLIST_HEAD(spapr_tce_tables, sPAPRTCETable) spapr_tce_tables; + +static sPAPRTCETable *spapr_tce_find_by_liobn(target_ulong liobn) +{ + sPAPRTCETable *tcet; + + QLIST_FOREACH(tcet, &spapr_tce_tables, list) { + if (tcet->liobn == liobn) { + return tcet; + } + } + + return NULL; +} + +static int spapr_tce_translate(DeviceState *qdev, + dma_addr_t addr, + dma_addr_t *paddr, + dma_addr_t *len, + int is_write) +{ + sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, qdev->iommu); + enum sPAPRTCEAccess access = is_write ? SPAPR_TCE_WO : SPAPR_TCE_RO; + uint64_t tce; + +#ifdef DEBUG_TCE + fprintf(stderr, "spapr_tce_translate addr=0x%llx\n", + (unsigned long long)addr); +#endif + + /* Check if we are in bound */ + if (addr >= tcet->window_size) { +#ifdef DEBUG_TCE + fprintf(stderr, "spapr_tce_translate out of bounds\n"); +#endif + return -EFAULT; + } + + tce = tcet->table[addr >> SPAPR_TCE_PAGE_SHIFT].tce; + + /* Check TCE */ + if (!(tce & access)) { + return -EPERM; + } + + /* How much til end of page ? */ + *len = ((~addr) & SPAPR_TCE_PAGE_MASK) + 1; + + /* Translate */ + *paddr = (tce & ~SPAPR_TCE_PAGE_MASK) | + (addr & SPAPR_TCE_PAGE_MASK); + +#ifdef DEBUG_TCE + fprintf(stderr, " -> *paddr=0x%llx, *len=0x%llxx\n", + (unsigned long long)*paddr, (unsigned long long )*len); +#endif + + return 0; +} + +void spapr_tce_init_dev(DeviceState *dev, target_ulong liobn, + size_t window_size) +{ + sPAPRTCETable *tcet; + + if (dev->iommu || !window_size) { + return; + } + + tcet = qemu_mallocz(sizeof(*tcet)); + dma_mmu_init(&tcet->dma, spapr_tce_translate); + dev->iommu = &tcet->dma; + + tcet->liobn = liobn; + tcet->window_size = window_size; + + if (kvm_enabled()) { + tcet->table = kvmppc_create_spapr_tce(liobn, + window_size, + &tcet->fd); + } + + if (!tcet->table) { + size_t table_size = (window_size >> SPAPR_TCE_PAGE_SHIFT) + * sizeof(sPAPRTCE); + tcet->table = qemu_mallocz(table_size); + } + + + QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list); +} + +void spapr_tce_clear_dev(DeviceState *dev) +{ + + if (dev->iommu) { + sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dev->iommu); + + QLIST_REMOVE(tcet, list); + + if (!kvm_enabled() || + (kvmppc_remove_spapr_tce(tcet->table, tcet->fd, + tcet->window_size) != 0)) { + qemu_free(tcet->table); + } + + qemu_free(tcet); + dev->iommu = NULL; + } +} + + +static target_ulong h_put_tce(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong liobn = args[0]; + target_ulong ioba = args[1]; + target_ulong tce = args[2]; + sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn); + sPAPRTCE *tcep; + + if (!tcet) { + hcall_dprintf("spapr_vio_put_tce on non-existent LIOBN " + TARGET_FMT_lx "\n", liobn); + return H_PARAMETER; + } + + ioba &= ~(SPAPR_TCE_PAGE_SIZE - 1); + +#ifdef DEBUG_TCE + fprintf(stderr, "spapr_vio_put_tce on %s ioba 0x" TARGET_FMT_lx + " TCE 0x" TARGET_FMT_lx "\n", dev->qdev.id, ioba, tce); +#endif + + if (ioba >= tcet->window_size) { + hcall_dprintf("spapr_vio_put_tce on out-of-boards IOBA 0x" + TARGET_FMT_lx "\n", ioba); + return H_PARAMETER; + } + + tcep = tcet->table + (ioba >> SPAPR_TCE_PAGE_SHIFT); + tcep->tce = tce; + + return H_SUCCESS; +} + +void spapr_iommu_init(void) +{ + QLIST_INIT(&spapr_tce_tables); + + /* hcall-tce */ + spapr_register_hypercall(H_PUT_TCE, h_put_tce); +} + +int spapr_dma_dt(void *fdt, int node_off, DMAMmu *iommu) +{ + if (iommu) { + sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, iommu); + uint32_t dma_prop[] = {cpu_to_be32(tcet->liobn), + 0, 0, + 0, cpu_to_be32(tcet->window_size)}; + int ret; + + ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-address-cells", 2); + if (ret < 0) { + return ret; + } + + ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-size-cells", 2); + if (ret < 0) { + return ret; + } + + ret = fdt_setprop(fdt, node_off, "ibm,my-dma-window", dma_prop, + sizeof(dma_prop)); + if (ret < 0) { + return ret; + } + } + + return 0; +} diff --git a/hw/spapr_llan.c b/hw/spapr_llan.c index c18efc7..d206f0c 100644 --- a/hw/spapr_llan.c +++ b/hw/spapr_llan.c @@ -27,6 +27,7 @@ #include "hw.h" #include "net.h" #include "hw/qdev.h" +#include "hw/dma_rw.h" #include "hw/spapr.h" #include "hw/spapr_vio.h" @@ -71,7 +72,7 @@ typedef uint64_t vlan_bd_t; #define VLAN_RXQ_BD_OFF 0 #define VLAN_FILTER_BD_OFF 8 #define VLAN_RX_BDS_OFF 16 -#define VLAN_MAX_BUFS ((SPAPR_VIO_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF) / 8) +#define VLAN_MAX_BUFS ((SPAPR_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF) / 8) typedef struct VIOsPAPRVLANDevice { VIOsPAPRDevice sdev; @@ -95,7 +96,7 @@ static ssize_t spapr_vlan_receive(VLANClientState *nc, const uint8_t *buf, { VIOsPAPRDevice *sdev = DO_UPCAST(NICState, nc, nc)->opaque; VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev; - vlan_bd_t rxq_bd = ldq_tce(sdev, dev->buf_list + VLAN_RXQ_BD_OFF); + vlan_bd_t rxq_bd = vio_ldq(sdev, dev->buf_list + VLAN_RXQ_BD_OFF); vlan_bd_t bd; int buf_ptr = dev->use_buf_ptr; uint64_t handle; @@ -114,11 +115,11 @@ static ssize_t spapr_vlan_receive(VLANClientState *nc, const uint8_t *buf, do { buf_ptr += 8; - if (buf_ptr >= SPAPR_VIO_TCE_PAGE_SIZE) { + if (buf_ptr >= SPAPR_TCE_PAGE_SIZE) { buf_ptr = VLAN_RX_BDS_OFF; } - bd = ldq_tce(sdev, dev->buf_list + buf_ptr); + bd = vio_ldq(sdev, dev->buf_list + buf_ptr); dprintf("use_buf_ptr=%d bd=0x%016llx\n", buf_ptr, (unsigned long long)bd); } while ((!(bd & VLAN_BD_VALID) || (VLAN_BD_LEN(bd) < (size + 8))) @@ -132,12 +133,12 @@ static ssize_t spapr_vlan_receive(VLANClientState *nc, const uint8_t *buf, /* Remove the buffer from the pool */ dev->rx_bufs--; dev->use_buf_ptr = buf_ptr; - stq_tce(sdev, dev->buf_list + dev->use_buf_ptr, 0); + vio_stq(sdev, dev->buf_list + dev->use_buf_ptr, 0); dprintf("Found buffer: ptr=%d num=%d\n", dev->use_buf_ptr, dev->rx_bufs); /* Transfer the packet data */ - if (spapr_tce_dma_write(sdev, VLAN_BD_ADDR(bd) + 8, buf, size) < 0) { + if (spapr_vio_dma_write(sdev, VLAN_BD_ADDR(bd) + 8, buf, size) < 0) { return -1; } @@ -149,23 +150,23 @@ static ssize_t spapr_vlan_receive(VLANClientState *nc, const uint8_t *buf, control ^= VLAN_RXQC_TOGGLE; } - handle = ldq_tce(sdev, VLAN_BD_ADDR(bd)); - stq_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 8, handle); - stw_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 4, size); - sth_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 2, 8); - stb_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr, control); + handle = vio_ldq(sdev, VLAN_BD_ADDR(bd)); + vio_stq(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 8, handle); + vio_stl(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 4, size); + vio_sth(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 2, 8); + vio_stb(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr, control); dprintf("wrote rxq entry (ptr=0x%llx): 0x%016llx 0x%016llx\n", (unsigned long long)dev->rxq_ptr, - (unsigned long long)ldq_tce(sdev, VLAN_BD_ADDR(rxq_bd) + + (unsigned long long)vio_ldq(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr), - (unsigned long long)ldq_tce(sdev, VLAN_BD_ADDR(rxq_bd) + + (unsigned long long)vio_ldq(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 8)); dev->rxq_ptr += 16; if (dev->rxq_ptr >= VLAN_BD_LEN(rxq_bd)) { dev->rxq_ptr = 0; - stq_tce(sdev, dev->buf_list + VLAN_RXQ_BD_OFF, rxq_bd ^ VLAN_BD_TOGGLE); + vio_stq(sdev, dev->buf_list + VLAN_RXQ_BD_OFF, rxq_bd ^ VLAN_BD_TOGGLE); } if (sdev->signal_state & 1) { @@ -252,13 +253,23 @@ static int check_bd(VIOsPAPRVLANDevice *dev, vlan_bd_t bd, } if (spapr_vio_check_tces(&dev->sdev, VLAN_BD_ADDR(bd), - VLAN_BD_LEN(bd), SPAPR_TCE_RW) != 0) { + VLAN_BD_LEN(bd), 1, 1) != 0) { return -1; } return 0; } +static int vio_dma_zero(VIOsPAPRDevice *dev, uint64_t taddr, uint32_t size) +{ + /* FIXME: allocating a temp buffer is nasty, but just stepping + * through writing zeroes is awkward. This will do for now. */ + uint8_t zeroes[size]; + + memset(zeroes, 0, size); + return spapr_vio_dma_write(dev, taddr, zeroes, size); +} + static target_ulong h_register_logical_lan(CPUState *env, sPAPREnvironment *spapr, target_ulong opcode, @@ -282,15 +293,15 @@ static target_ulong h_register_logical_lan(CPUState *env, return H_RESOURCE; } - if (check_bd(dev, VLAN_VALID_BD(buf_list, SPAPR_VIO_TCE_PAGE_SIZE), - SPAPR_VIO_TCE_PAGE_SIZE) < 0) { + if (check_bd(dev, VLAN_VALID_BD(buf_list, SPAPR_TCE_PAGE_SIZE), + SPAPR_TCE_PAGE_SIZE) < 0) { hcall_dprintf("Bad buf_list 0x" TARGET_FMT_lx " for " "H_REGISTER_LOGICAL_LAN\n", buf_list); return H_PARAMETER; } - filter_list_bd = VLAN_VALID_BD(filter_list, SPAPR_VIO_TCE_PAGE_SIZE); - if (check_bd(dev, filter_list_bd, SPAPR_VIO_TCE_PAGE_SIZE) < 0) { + filter_list_bd = VLAN_VALID_BD(filter_list, SPAPR_TCE_PAGE_SIZE); + if (check_bd(dev, filter_list_bd, SPAPR_TCE_PAGE_SIZE) < 0) { hcall_dprintf("Bad filter_list 0x" TARGET_FMT_lx " for " "H_REGISTER_LOGICAL_LAN\n", filter_list); return H_PARAMETER; @@ -308,17 +319,17 @@ static target_ulong h_register_logical_lan(CPUState *env, rec_queue &= ~VLAN_BD_TOGGLE; /* Initialize the buffer list */ - stq_tce(sdev, buf_list, rec_queue); - stq_tce(sdev, buf_list + 8, filter_list_bd); - spapr_tce_dma_zero(sdev, buf_list + VLAN_RX_BDS_OFF, - SPAPR_VIO_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF); + vio_stq(sdev, buf_list, rec_queue); + vio_stq(sdev, buf_list + 8, filter_list_bd); + vio_dma_zero(sdev, buf_list + VLAN_RX_BDS_OFF, + SPAPR_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF); dev->add_buf_ptr = VLAN_RX_BDS_OFF - 8; dev->use_buf_ptr = VLAN_RX_BDS_OFF - 8; dev->rx_bufs = 0; dev->rxq_ptr = 0; /* Initialize the receive queue */ - spapr_tce_dma_zero(sdev, VLAN_BD_ADDR(rec_queue), VLAN_BD_LEN(rec_queue)); + vio_dma_zero(sdev, VLAN_BD_ADDR(rec_queue), VLAN_BD_LEN(rec_queue)); dev->isopen = 1; return H_SUCCESS; @@ -379,14 +390,14 @@ static target_ulong h_add_logical_lan_buffer(CPUState *env, do { dev->add_buf_ptr += 8; - if (dev->add_buf_ptr >= SPAPR_VIO_TCE_PAGE_SIZE) { + if (dev->add_buf_ptr >= SPAPR_TCE_PAGE_SIZE) { dev->add_buf_ptr = VLAN_RX_BDS_OFF; } - bd = ldq_tce(sdev, dev->buf_list + dev->add_buf_ptr); + bd = vio_ldq(sdev, dev->buf_list + dev->add_buf_ptr); } while (bd & VLAN_BD_VALID); - stq_tce(sdev, dev->buf_list + dev->add_buf_ptr, buf); + vio_stq(sdev, dev->buf_list + dev->add_buf_ptr, buf); dev->rx_bufs++; @@ -452,7 +463,7 @@ static target_ulong h_send_logical_lan(CPUState *env, sPAPREnvironment *spapr, lbuf = alloca(total_len); p = lbuf; for (i = 0; i < nbufs; i++) { - ret = spapr_tce_dma_read(sdev, VLAN_BD_ADDR(bufs[i]), + ret = spapr_vio_dma_read(sdev, VLAN_BD_ADDR(bufs[i]), p, VLAN_BD_LEN(bufs[i])); if (ret < 0) { return ret; @@ -496,13 +507,12 @@ static VIOsPAPRDeviceInfo spapr_vlan = { .dt_type = "network", .dt_compatible = "IBM,l-lan", .signal_mask = 0x1, + .rtce_window_size = 0x10000000, .hcalls = vlan_hcalls, .qdev.name = "spapr-vlan", .qdev.size = sizeof(VIOsPAPRVLANDevice), .qdev.props = (Property[]) { DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0x1000), - DEFINE_PROP_UINT32("dma-window", VIOsPAPRDevice, rtce_window_size, - 0x10000000), DEFINE_NIC_PROPERTIES(VIOsPAPRVLANDevice, nicconf), DEFINE_PROP_END_OF_LIST(), }, diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c index a75f1d6..21a9182 100644 --- a/hw/spapr_vio.c +++ b/hw/spapr_vio.c @@ -29,6 +29,7 @@ #include "kvm.h" #include "device_tree.h" #include "kvm_ppc.h" +#include "hw/dma_rw.h" #include "hw/spapr.h" #include "hw/spapr_vio.h" @@ -38,7 +39,6 @@ #endif /* CONFIG_FDT */ /* #define DEBUG_SPAPR */ -/* #define DEBUG_TCE */ #ifdef DEBUG_SPAPR #define dprintf(fmt, ...) \ @@ -117,26 +117,9 @@ static int vio_make_devnode(VIOsPAPRDevice *dev, } } - if (dev->rtce_window_size) { - uint32_t dma_prop[] = {cpu_to_be32(dev->reg), - 0, 0, - 0, cpu_to_be32(dev->rtce_window_size)}; - - ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-address-cells", 2); - if (ret < 0) { - return ret; - } - - ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-size-cells", 2); - if (ret < 0) { - return ret; - } - - ret = fdt_setprop(fdt, node_off, "ibm,my-dma-window", dma_prop, - sizeof(dma_prop)); - if (ret < 0) { - return ret; - } + ret = spapr_dma_dt(fdt, node_off, dev->qdev.iommu); + if (ret < 0) { + return ret; } if (info->devnode) { @@ -151,236 +134,6 @@ static int vio_make_devnode(VIOsPAPRDevice *dev, #endif /* CONFIG_FDT */ /* - * RTCE handling - */ - -static void rtce_init(VIOsPAPRDevice *dev) -{ - size_t size = (dev->rtce_window_size >> SPAPR_VIO_TCE_PAGE_SHIFT) - * sizeof(VIOsPAPR_RTCE); - - if (size) { - if (kvm_enabled()) { - dev->rtce_table = kvmppc_create_spapr_tce(dev->reg, - dev->rtce_window_size, - &dev->kvmtce_fd); - } - - if (!dev->rtce_table) { - dev->rtce_table = qemu_mallocz(size); - } - } -} - -static target_ulong h_put_tce(CPUState *env, sPAPREnvironment *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong liobn = args[0]; - target_ulong ioba = args[1]; - target_ulong tce = args[2]; - VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, liobn); - VIOsPAPR_RTCE *rtce; - - if (!dev) { - hcall_dprintf("spapr_vio_put_tce on non-existent LIOBN " - TARGET_FMT_lx "\n", liobn); - return H_PARAMETER; - } - - ioba &= ~(SPAPR_VIO_TCE_PAGE_SIZE - 1); - -#ifdef DEBUG_TCE - fprintf(stderr, "spapr_vio_put_tce on %s ioba 0x" TARGET_FMT_lx - " TCE 0x" TARGET_FMT_lx "\n", dev->qdev.id, ioba, tce); -#endif - - if (ioba >= dev->rtce_window_size) { - hcall_dprintf("spapr_vio_put_tce on out-of-boards IOBA 0x" - TARGET_FMT_lx "\n", ioba); - return H_PARAMETER; - } - - rtce = dev->rtce_table + (ioba >> SPAPR_VIO_TCE_PAGE_SHIFT); - rtce->tce = tce; - - return H_SUCCESS; -} - -int spapr_vio_check_tces(VIOsPAPRDevice *dev, target_ulong ioba, - target_ulong len, enum VIOsPAPR_TCEAccess access) -{ - int start, end, i; - - start = ioba >> SPAPR_VIO_TCE_PAGE_SHIFT; - end = (ioba + len - 1) >> SPAPR_VIO_TCE_PAGE_SHIFT; - - for (i = start; i <= end; i++) { - if ((dev->rtce_table[i].tce & access) != access) { -#ifdef DEBUG_TCE - fprintf(stderr, "FAIL on %d\n", i); -#endif - return -1; - } - } - - return 0; -} - -int spapr_tce_dma_write(VIOsPAPRDevice *dev, uint64_t taddr, const void *buf, - uint32_t size) -{ -#ifdef DEBUG_TCE - fprintf(stderr, "spapr_tce_dma_write taddr=0x%llx size=0x%x\n", - (unsigned long long)taddr, size); -#endif - - /* Check for bypass */ - if (dev->flags & VIO_PAPR_FLAG_DMA_BYPASS) { - cpu_physical_memory_write(taddr, buf, size); - return 0; - } - - while (size) { - uint64_t tce; - uint32_t lsize; - uint64_t txaddr; - - /* Check if we are in bound */ - if (taddr >= dev->rtce_window_size) { -#ifdef DEBUG_TCE - fprintf(stderr, "spapr_tce_dma_write out of bounds\n"); -#endif - return H_DEST_PARM; - } - tce = dev->rtce_table[taddr >> SPAPR_VIO_TCE_PAGE_SHIFT].tce; - - /* How much til end of page ? */ - lsize = MIN(size, ((~taddr) & SPAPR_VIO_TCE_PAGE_MASK) + 1); - - /* Check TCE */ - if (!(tce & 2)) { - return H_DEST_PARM; - } - - /* Translate */ - txaddr = (tce & ~SPAPR_VIO_TCE_PAGE_MASK) | - (taddr & SPAPR_VIO_TCE_PAGE_MASK); - -#ifdef DEBUG_TCE - fprintf(stderr, " -> write to txaddr=0x%llx, size=0x%x\n", - (unsigned long long)txaddr, lsize); -#endif - - /* Do it */ - cpu_physical_memory_write(txaddr, buf, lsize); - buf += lsize; - taddr += lsize; - size -= lsize; - } - return 0; -} - -int spapr_tce_dma_zero(VIOsPAPRDevice *dev, uint64_t taddr, uint32_t size) -{ - /* FIXME: allocating a temp buffer is nasty, but just stepping - * through writing zeroes is awkward. This will do for now. */ - uint8_t zeroes[size]; - -#ifdef DEBUG_TCE - fprintf(stderr, "spapr_tce_dma_zero taddr=0x%llx size=0x%x\n", - (unsigned long long)taddr, size); -#endif - - memset(zeroes, 0, size); - return spapr_tce_dma_write(dev, taddr, zeroes, size); -} - -void stb_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint8_t val) -{ - spapr_tce_dma_write(dev, taddr, &val, sizeof(val)); -} - -void sth_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint16_t val) -{ - val = tswap16(val); - spapr_tce_dma_write(dev, taddr, &val, sizeof(val)); -} - - -void stw_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint32_t val) -{ - val = tswap32(val); - spapr_tce_dma_write(dev, taddr, &val, sizeof(val)); -} - -void stq_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint64_t val) -{ - val = tswap64(val); - spapr_tce_dma_write(dev, taddr, &val, sizeof(val)); -} - -int spapr_tce_dma_read(VIOsPAPRDevice *dev, uint64_t taddr, void *buf, - uint32_t size) -{ -#ifdef DEBUG_TCE - fprintf(stderr, "spapr_tce_dma_write taddr=0x%llx size=0x%x\n", - (unsigned long long)taddr, size); -#endif - - /* Check for bypass */ - if (dev->flags & VIO_PAPR_FLAG_DMA_BYPASS) { - cpu_physical_memory_read(taddr, buf, size); - return 0; - } - - while (size) { - uint64_t tce; - uint32_t lsize; - uint64_t txaddr; - - /* Check if we are in bound */ - if (taddr >= dev->rtce_window_size) { -#ifdef DEBUG_TCE - fprintf(stderr, "spapr_tce_dma_read out of bounds\n"); -#endif - return H_DEST_PARM; - } - tce = dev->rtce_table[taddr >> SPAPR_VIO_TCE_PAGE_SHIFT].tce; - - /* How much til end of page ? */ - lsize = MIN(size, ((~taddr) & SPAPR_VIO_TCE_PAGE_MASK) + 1); - - /* Check TCE */ - if (!(tce & 1)) { - return H_DEST_PARM; - } - - /* Translate */ - txaddr = (tce & ~SPAPR_VIO_TCE_PAGE_MASK) | - (taddr & SPAPR_VIO_TCE_PAGE_MASK); - -#ifdef DEBUG_TCE - fprintf(stderr, " -> write to txaddr=0x%llx, size=0x%x\n", - (unsigned long long)txaddr, lsize); -#endif - /* Do it */ - cpu_physical_memory_read(txaddr, buf, lsize); - buf += lsize; - taddr += lsize; - size -= lsize; - } - return H_SUCCESS; -} - -uint64_t ldq_tce(VIOsPAPRDevice *dev, uint64_t taddr) -{ - uint64_t val; - - spapr_tce_dma_read(dev, taddr, &val, sizeof(val)); - return tswap64(val); -} - -/* * CRQ handling */ static target_ulong h_reg_crq(CPUState *env, sPAPREnvironment *spapr, @@ -503,7 +256,7 @@ int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq) } /* Maybe do a fast path for KVM just writing to the pages */ - rc = spapr_tce_dma_read(dev, dev->crq.qladdr + dev->crq.qnext, &byte, 1); + rc = spapr_vio_dma_read(dev, dev->crq.qladdr + dev->crq.qnext, &byte, 1); if (rc) { return rc; } @@ -511,7 +264,7 @@ int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq) return 1; } - rc = spapr_tce_dma_write(dev, dev->crq.qladdr + dev->crq.qnext + 8, + rc = spapr_vio_dma_write(dev, dev->crq.qladdr + dev->crq.qnext + 8, &crq[8], 8); if (rc) { return rc; @@ -519,7 +272,7 @@ int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq) kvmppc_eieio(); - rc = spapr_tce_dma_write(dev, dev->crq.qladdr + dev->crq.qnext, crq, 8); + rc = spapr_vio_dma_write(dev, dev->crq.qladdr + dev->crq.qnext, crq, 8); if (rc) { return rc; } @@ -537,19 +290,12 @@ int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq) static void spapr_vio_quiesce_one(VIOsPAPRDevice *dev) { - dev->flags &= ~VIO_PAPR_FLAG_DMA_BYPASS; - - if (dev->rtce_table) { - if (kvm_enabled() && - (kvmppc_remove_spapr_tce(dev->rtce_table, dev->kvmtce_fd, - dev->rtce_window_size) == 0)) { - rtce_init(dev); - } else { - size_t size = (dev->rtce_window_size >> SPAPR_VIO_TCE_PAGE_SHIFT) - * sizeof(VIOsPAPR_RTCE); - memset(dev->rtce_table, 0, size); - } + VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)dev->qdev.info; + + if (dev->qdev.iommu) { + spapr_tce_clear_dev(&dev->qdev); } + spapr_tce_init_dev(&dev->qdev, dev->reg, info->rtce_window_size); dev->crq.qladdr = 0; dev->crq.qsize = 0; @@ -576,9 +322,11 @@ static void rtas_set_tce_bypass(sPAPREnvironment *spapr, uint32_t token, return; } if (enable) { - dev->flags |= VIO_PAPR_FLAG_DMA_BYPASS; + spapr_tce_clear_dev(&dev->qdev); } else { - dev->flags &= ~VIO_PAPR_FLAG_DMA_BYPASS; + VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)dev->qdev.info; + + spapr_tce_init_dev(&dev->qdev, dev->reg, info->rtce_window_size); } rtas_st(rets, 0, 0); @@ -617,7 +365,7 @@ static int spapr_vio_busdev_init(DeviceState *qdev, DeviceInfo *qinfo) dev->qdev.id = id; - rtce_init(dev); + spapr_tce_init_dev(&dev->qdev, dev->reg, info->rtce_window_size); return info->init(dev); } @@ -674,9 +422,6 @@ VIOsPAPRBus *spapr_vio_bus_init(void) /* hcall-vio */ spapr_register_hypercall(H_VIO_SIGNAL, h_vio_signal); - /* hcall-tce */ - spapr_register_hypercall(H_PUT_TCE, h_put_tce); - /* hcall-crq */ spapr_register_hypercall(H_REG_CRQ, h_reg_crq); spapr_register_hypercall(H_FREE_CRQ, h_free_crq); diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h index d6073b5..7578be3 100644 --- a/hw/spapr_vio.h +++ b/hw/spapr_vio.h @@ -21,25 +21,13 @@ * License along with this library; if not, see . */ -#define SPAPR_VIO_TCE_PAGE_SHIFT 12 -#define SPAPR_VIO_TCE_PAGE_SIZE (1ULL << SPAPR_VIO_TCE_PAGE_SHIFT) -#define SPAPR_VIO_TCE_PAGE_MASK (SPAPR_VIO_TCE_PAGE_SIZE - 1) +#include "hw/dma_rw.h" -enum VIOsPAPR_TCEAccess { - SPAPR_TCE_FAULT = 0, - SPAPR_TCE_RO = 1, - SPAPR_TCE_WO = 2, - SPAPR_TCE_RW = 3, -}; #define SPAPR_VTY_BASE_ADDRESS 0x30000000 struct VIOsPAPRDevice; -typedef struct VIOsPAPR_RTCE { - uint64_t tce; -} VIOsPAPR_RTCE; - typedef struct VIOsPAPR_CRQ { uint64_t qladdr; uint32_t qsize; @@ -51,13 +39,9 @@ typedef struct VIOsPAPRDevice { DeviceState qdev; uint32_t reg; uint32_t flags; -#define VIO_PAPR_FLAG_DMA_BYPASS 0x1 qemu_irq qirq; uint32_t vio_irq_num; target_ulong signal_state; - uint32_t rtce_window_size; - VIOsPAPR_RTCE *rtce_table; - int kvmtce_fd; VIOsPAPR_CRQ crq; } VIOsPAPRDevice; @@ -69,6 +53,7 @@ typedef struct { DeviceInfo qdev; const char *dt_name, *dt_type, *dt_compatible; target_ulong signal_mask; + uint32_t rtce_window_size; int (*init)(VIOsPAPRDevice *dev); void (*hcalls)(VIOsPAPRBus *bus); int (*devnode)(VIOsPAPRDevice *dev, void *fdt, int node_off); @@ -81,20 +66,33 @@ extern int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt); extern int spapr_vio_signal(VIOsPAPRDevice *dev, target_ulong mode); -int spapr_vio_check_tces(VIOsPAPRDevice *dev, target_ulong ioba, - target_ulong len, - enum VIOsPAPR_TCEAccess access); - -int spapr_tce_dma_read(VIOsPAPRDevice *dev, uint64_t taddr, - void *buf, uint32_t size); -int spapr_tce_dma_write(VIOsPAPRDevice *dev, uint64_t taddr, - const void *buf, uint32_t size); -int spapr_tce_dma_zero(VIOsPAPRDevice *dev, uint64_t taddr, uint32_t size); -void stb_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint8_t val); -void sth_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint16_t val); -void stw_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint32_t val); -void stq_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint64_t val); -uint64_t ldq_tce(VIOsPAPRDevice *dev, uint64_t taddr); +static inline int spapr_vio_check_tces(VIOsPAPRDevice *dev, target_ulong ioba, + target_ulong len, + int check_r, int check_w) +{ + return (dma_memory_check(&dev->qdev, ioba, len, check_r, check_w) != 0) ? + H_DEST_PARM : H_SUCCESS; +} + +static inline int spapr_vio_dma_read(VIOsPAPRDevice *dev, uint64_t taddr, + void *buf, uint32_t size) +{ + return (dma_memory_read(&dev->qdev, taddr, buf, size) != 0) ? + H_DEST_PARM : H_SUCCESS; +} + +static inline int spapr_vio_dma_write(VIOsPAPRDevice *dev, uint64_t taddr, + const void *buf, uint32_t size) +{ + return (dma_memory_write(&dev->qdev, taddr, buf, size) != 0) ? + H_DEST_PARM : H_SUCCESS; +} + +#define vio_stb(_dev, _addr, _val) (dma_stb(&(_dev)->qdev, (_addr), (_val))) +#define vio_sth(_dev, _addr, _val) (dma_stw(&(_dev)->qdev, (_addr), (_val))) +#define vio_stl(_dev, _addr, _val) (dma_stl(&(_dev)->qdev, (_addr), (_val))) +#define vio_stq(_dev, _addr, _val) (dma_stq(&(_dev)->qdev, (_addr), (_val))) +#define vio_ldq(_dev, _addr) (dma_ldq(&(_dev)->qdev, (_addr))) int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq); diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index 1c901ef..d8431b3 100644 --- a/hw/spapr_vscsi.c +++ b/hw/spapr_vscsi.c @@ -37,6 +37,7 @@ #include "net.h" /* Remove that when we can */ #include "srp.h" #include "hw/qdev.h" +#include "hw/dma_rw.h" #include "hw/spapr.h" #include "hw/spapr_vio.h" #include "hw/ppc-viosrp.h" @@ -152,7 +153,7 @@ static int vscsi_send_iu(VSCSIState *s, vscsi_req *req, long rc, rc1; /* First copy the SRP */ - rc = spapr_tce_dma_write(&s->vdev, req->crq.s.IU_data_ptr, + rc = spapr_vio_dma_write(&s->vdev, req->crq.s.IU_data_ptr, &req->iu, length); if (rc) { fprintf(stderr, "vscsi_send_iu: DMA write failure !\n"); @@ -268,9 +269,9 @@ static int vscsi_srp_direct_data(VSCSIState *s, vscsi_req *req, llen = MIN(len, md->len); if (llen) { if (req->writing) { /* writing = to device = reading from memory */ - rc = spapr_tce_dma_read(&s->vdev, md->va, buf, llen); + rc = spapr_vio_dma_read(&s->vdev, md->va, buf, llen); } else { - rc = spapr_tce_dma_write(&s->vdev, md->va, buf, llen); + rc = spapr_vio_dma_write(&s->vdev, md->va, buf, llen); } } md->len -= llen; @@ -316,10 +317,10 @@ static int vscsi_srp_indirect_data(VSCSIState *s, vscsi_req *req, md = req->cur_desc = &req->ext_desc; dprintf("VSCSI: Reading desc from 0x%llx\n", (unsigned long long)td->va); - rc = spapr_tce_dma_read(&s->vdev, td->va, md, + rc = spapr_vio_dma_read(&s->vdev, td->va, md, sizeof(struct srp_direct_buf)); if (rc) { - dprintf("VSCSI: tce_dma_read -> %d reading ext_desc\n", rc); + dprintf("VSCSI: spapr_vio_dma_read -> %d reading ext_desc\n", rc); break; } vscsi_swap_desc(md); @@ -332,12 +333,12 @@ static int vscsi_srp_indirect_data(VSCSIState *s, vscsi_req *req, /* Perform transfer */ llen = MIN(len, md->len); if (req->writing) { /* writing = to device = reading from memory */ - rc = spapr_tce_dma_read(&s->vdev, md->va, buf, llen); + rc = spapr_vio_dma_read(&s->vdev, md->va, buf, llen); } else { - rc = spapr_tce_dma_write(&s->vdev, md->va, buf, llen); + rc = spapr_vio_dma_write(&s->vdev, md->va, buf, llen); } if (rc) { - dprintf("VSCSI: tce_dma_r/w(%d) -> %d\n", req->writing, rc); + dprintf("VSCSI: spapr_vio_dma_r/w(%d) -> %d\n", req->writing, rc); break; } dprintf("VSCSI: data: %02x %02x %02x %02x...\n", @@ -768,7 +769,7 @@ static int vscsi_send_adapter_info(VSCSIState *s, vscsi_req *req) sinfo = &req->iu.mad.adapter_info; #if 0 /* What for ? */ - rc = spapr_tce_dma_read(&s->vdev, be64_to_cpu(sinfo->buffer), + rc = spapr_vio_dma_read(&s->vdev, be64_to_cpu(sinfo->buffer), &info, be16_to_cpu(sinfo->common.length)); if (rc) { fprintf(stderr, "vscsi_send_adapter_info: DMA read failure !\n"); @@ -782,7 +783,7 @@ static int vscsi_send_adapter_info(VSCSIState *s, vscsi_req *req) info.os_type = cpu_to_be32(2); info.port_max_txu[0] = cpu_to_be32(VSCSI_MAX_SECTORS << 9); - rc = spapr_tce_dma_write(&s->vdev, be64_to_cpu(sinfo->buffer), + rc = spapr_vio_dma_write(&s->vdev, be64_to_cpu(sinfo->buffer), &info, be16_to_cpu(sinfo->common.length)); if (rc) { fprintf(stderr, "vscsi_send_adapter_info: DMA write failure !\n"); @@ -844,7 +845,7 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq) } /* XXX Handle failure differently ? */ - if (spapr_tce_dma_read(&s->vdev, crq->s.IU_data_ptr, &req->iu, + if (spapr_vio_dma_read(&s->vdev, crq->s.IU_data_ptr, &req->iu, crq->s.IU_length)) { fprintf(stderr, "vscsi_got_payload: DMA read failure !\n"); qemu_free(req); @@ -992,12 +993,11 @@ static VIOsPAPRDeviceInfo spapr_vscsi = { .dt_type = "vscsi", .dt_compatible = "IBM,v-scsi", .signal_mask = 0x00000001, + .rtce_window_size = 0x10000000, .qdev.name = "spapr-vscsi", .qdev.size = sizeof(VSCSIState), .qdev.props = (Property[]) { DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0x2000), - DEFINE_PROP_UINT32("dma-window", VIOsPAPRDevice, - rtce_window_size, 0x10000000), DEFINE_PROP_END_OF_LIST(), }, }; diff --git a/hw/spapr_vty.c b/hw/spapr_vty.c index 6fc0105..284d789 100644 --- a/hw/spapr_vty.c +++ b/hw/spapr_vty.c @@ -1,5 +1,6 @@ #include "qdev.h" #include "qemu-char.h" +#include "hw/dma_rw.h" #include "hw/spapr.h" #include "hw/spapr_vio.h"