pseries: Convert sPAPR TCEs to use generic IOMMU infrastructure

The pseries platform already contains an IOMMU implementation, since it is
essential for the platform's paravirtualized VIO devices.  This IOMMU
support is currently built into the implementation of the VIO "bus" and
the various VIO devices.

This patch converts this code to make use of the new common IOMMU
infrastructure.

We don't yet handle synchronization of map/unmap callbacks vs. invalidations,
this will require some complex interaction with the kernel and is not a
major concern at this stage.

Cc: Alex Graf <agraf@suse.de>

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
David Gibson 2012-06-27 14:50:44 +10:00 committed by Anthony Liguori
parent e5332e6334
commit ad0ebb91cd
10 changed files with 369 additions and 342 deletions

View File

@ -10,7 +10,7 @@ obj-y += ppc_newworld.o
# IBM pSeries (sPAPR)
obj-$(CONFIG_PSERIES) += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o
obj-$(CONFIG_PSERIES) += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o
obj-$(CONFIG_PSERIES) += spapr_pci.o pci-hotplug.o
obj-$(CONFIG_PSERIES) += spapr_pci.o pci-hotplug.o spapr_iommu.o
# PowerPC 4xx boards
obj-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
obj-y += ppc440_bamboo.o

View File

@ -674,6 +674,9 @@ static void ppc_spapr_init(ram_addr_t ram_size,
spapr->icp = xics_system_init(XICS_IRQS);
spapr->next_irq = 16;
/* Set up IOMMU */
spapr_iommu_init();
/* Set up VIO bus */
spapr->vio_bus = spapr_vio_bus_init();

View File

@ -1,6 +1,7 @@
#if !defined(__HW_SPAPR_H__)
#define __HW_SPAPR_H__
#include "dma.h"
#include "hw/xics.h"
struct VIOsPAPRBus;
@ -320,4 +321,20 @@ 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)
typedef struct sPAPRTCE {
uint64_t tce;
} sPAPRTCE;
#define SPAPR_VIO_BASE_LIOBN 0x00000000
void spapr_iommu_init(void);
DMAContext *spapr_tce_new_dma_context(uint32_t liobn, size_t window_size);
void spapr_tce_free(DMAContext *dma);
int spapr_dma_dt(void *fdt, int node_off, const char *propname,
DMAContext *dma);
#endif /* !defined (__HW_SPAPR_H__) */

242
hw/spapr_iommu.c Normal file
View File

@ -0,0 +1,242 @@
/*
* QEMU sPAPR IOMMU (TCE) code
*
* Copyright (c) 2010 David Gibson, IBM Corporation <dwg@au1.ibm.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "hw.h"
#include "kvm.h"
#include "qdev.h"
#include "kvm_ppc.h"
#include "dma.h"
#include "hw/spapr.h"
#include <libfdt.h>
/* #define DEBUG_TCE */
enum sPAPRTCEAccess {
SPAPR_TCE_FAULT = 0,
SPAPR_TCE_RO = 1,
SPAPR_TCE_WO = 2,
SPAPR_TCE_RW = 3,
};
typedef struct sPAPRTCETable sPAPRTCETable;
struct sPAPRTCETable {
DMAContext dma;
uint32_t 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(uint32_t liobn)
{
sPAPRTCETable *tcet;
QLIST_FOREACH(tcet, &spapr_tce_tables, list) {
if (tcet->liobn == liobn) {
return tcet;
}
}
return NULL;
}
static int spapr_tce_translate(DMAContext *dma,
dma_addr_t addr,
target_phys_addr_t *paddr,
target_phys_addr_t *len,
DMADirection dir)
{
sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dma);
enum sPAPRTCEAccess access = (dir == DMA_DIRECTION_FROM_DEVICE)
? SPAPR_TCE_WO : SPAPR_TCE_RO;
uint64_t tce;
#ifdef DEBUG_TCE
fprintf(stderr, "spapr_tce_translate liobn=0x%" PRIx32 " addr=0x"
DMA_ADDR_FMT "\n", tcet->liobn, 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" TARGET_FMT_plx ", *len=0x"
TARGET_FMT_plx "\n", *paddr, *len);
#endif
return 0;
}
DMAContext *spapr_tce_new_dma_context(uint32_t liobn, size_t window_size)
{
sPAPRTCETable *tcet;
if (!window_size) {
return NULL;
}
tcet = g_malloc0(sizeof(*tcet));
dma_context_init(&tcet->dma, spapr_tce_translate, NULL, NULL);
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 = g_malloc0(table_size);
}
#ifdef DEBUG_TCE
fprintf(stderr, "spapr_iommu: New TCE table, liobn=0x%x, context @ %p, "
"table @ %p, fd=%d\n", liobn, &tcet->dma, tcet->table, tcet->fd);
#endif
QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list);
return &tcet->dma;
}
void spapr_tce_free(DMAContext *dma)
{
if (dma) {
sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dma);
QLIST_REMOVE(tcet, list);
if (!kvm_enabled() ||
(kvmppc_remove_spapr_tce(tcet->table, tcet->fd,
tcet->window_size) != 0)) {
g_free(tcet->table);
}
g_free(tcet);
}
}
static target_ulong h_put_tce(CPUPPCState *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 (liobn & 0xFFFFFFFF00000000ULL) {
hcall_dprintf("spapr_vio_put_tce on out-of-boundsw LIOBN "
TARGET_FMT_lx "\n", liobn);
return H_PARAMETER;
}
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 liobn=" TARGET_FMT_lx /*%s*/
" ioba 0x" TARGET_FMT_lx " TCE 0x" TARGET_FMT_lx "\n",
liobn, /*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, const char *propname,
DMAContext *dma)
{
if (dma) {
sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dma);
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, propname, dma_prop,
sizeof(dma_prop));
if (ret < 0) {
return ret;
}
}
return 0;
}

View File

@ -71,7 +71,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 +95,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 +114,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 +132,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 +149,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) {
@ -254,8 +254,10 @@ static int check_bd(VIOsPAPRVLANDevice *dev, vlan_bd_t bd,
return -1;
}
if (spapr_vio_check_tces(&dev->sdev, VLAN_BD_ADDR(bd),
VLAN_BD_LEN(bd), SPAPR_TCE_RW) != 0) {
if (!spapr_vio_dma_valid(&dev->sdev, VLAN_BD_ADDR(bd),
VLAN_BD_LEN(bd), DMA_DIRECTION_FROM_DEVICE)
|| !spapr_vio_dma_valid(&dev->sdev, VLAN_BD_ADDR(bd),
VLAN_BD_LEN(bd), DMA_DIRECTION_TO_DEVICE)) {
return -1;
}
@ -285,14 +287,14 @@ static target_ulong h_register_logical_lan(CPUPPCState *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 "\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 "\n", filter_list);
return H_PARAMETER;
}
@ -309,17 +311,17 @@ static target_ulong h_register_logical_lan(CPUPPCState *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);
spapr_vio_dma_set(sdev, buf_list + VLAN_RX_BDS_OFF, 0,
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));
spapr_vio_dma_set(sdev, VLAN_BD_ADDR(rec_queue), 0, VLAN_BD_LEN(rec_queue));
dev->isopen = 1;
return H_SUCCESS;
@ -378,14 +380,14 @@ static target_ulong h_add_logical_lan_buffer(CPUPPCState *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++;
@ -451,7 +453,7 @@ static target_ulong h_send_logical_lan(CPUPPCState *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;
@ -479,7 +481,7 @@ static target_ulong h_multicast_ctrl(CPUPPCState *env, sPAPREnvironment *spapr,
}
static Property spapr_vlan_properties[] = {
DEFINE_SPAPR_PROPERTIES(VIOsPAPRVLANDevice, sdev, 0x10000000),
DEFINE_SPAPR_PROPERTIES(VIOsPAPRVLANDevice, sdev),
DEFINE_NIC_PROPERTIES(VIOsPAPRVLANDevice, nicconf),
DEFINE_PROP_END_OF_LIST(),
};
@ -497,6 +499,7 @@ static void spapr_vlan_class_init(ObjectClass *klass, void *data)
k->dt_compatible = "IBM,l-lan";
k->signal_mask = 0x1;
dc->props = spapr_vlan_properties;
k->rtce_window_size = 0x10000000;
}
static TypeInfo spapr_vlan_info = {

View File

@ -39,7 +39,6 @@
#endif /* CONFIG_FDT */
/* #define DEBUG_SPAPR */
/* #define DEBUG_TCE */
#ifdef DEBUG_SPAPR
#define dprintf(fmt, ...) \
@ -143,26 +142,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, "ibm,my-dma-window", dev->dma);
if (ret < 0) {
return ret;
}
if (pc->devnode) {
@ -176,232 +158,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) {
dev->rtce_table = kvmppc_create_spapr_tce(dev->reg,
dev->rtce_window_size,
&dev->kvmtce_fd);
if (!dev->rtce_table) {
dev->rtce_table = g_malloc0(size);
}
}
}
static target_ulong h_put_tce(CPUPPCState *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("LIOBN 0x" TARGET_FMT_lx " does not exist\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("Out-of-bounds 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
*/
@ -526,7 +282,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;
}
@ -534,7 +290,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;
@ -542,7 +298,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;
}
@ -560,13 +316,13 @@ 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;
VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
uint32_t liobn = SPAPR_VIO_BASE_LIOBN | dev->reg;
if (dev->rtce_table) {
size_t size = (dev->rtce_window_size >> SPAPR_VIO_TCE_PAGE_SHIFT)
* sizeof(VIOsPAPR_RTCE);
memset(dev->rtce_table, 0, size);
if (dev->dma) {
spapr_tce_free(dev->dma);
}
dev->dma = spapr_tce_new_dma_context(liobn, pc->rtce_window_size);
dev->crq.qladdr = 0;
dev->crq.qsize = 0;
@ -593,9 +349,13 @@ static void rtas_set_tce_bypass(sPAPREnvironment *spapr, uint32_t token,
return;
}
if (enable) {
dev->flags |= VIO_PAPR_FLAG_DMA_BYPASS;
spapr_tce_free(dev->dma);
dev->dma = NULL;
} else {
dev->flags &= ~VIO_PAPR_FLAG_DMA_BYPASS;
VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
uint32_t liobn = SPAPR_VIO_BASE_LIOBN | dev->reg;
dev->dma = spapr_tce_new_dma_context(liobn, pc->rtce_window_size);
}
rtas_st(rets, 0, 0);
@ -662,6 +422,7 @@ static int spapr_vio_busdev_init(DeviceState *qdev)
{
VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev;
VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
uint32_t liobn;
char *id;
if (dev->reg != -1) {
@ -703,7 +464,8 @@ static int spapr_vio_busdev_init(DeviceState *qdev)
return -1;
}
rtce_init(dev);
liobn = SPAPR_VIO_BASE_LIOBN | dev->reg;
dev->dma = spapr_tce_new_dma_context(liobn, pc->rtce_window_size);
return pc->init(dev);
}
@ -751,9 +513,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);

View File

@ -21,16 +21,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#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)
enum VIOsPAPR_TCEAccess {
SPAPR_TCE_FAULT = 0,
SPAPR_TCE_RO = 1,
SPAPR_TCE_WO = 2,
SPAPR_TCE_RW = 3,
};
#include "dma.h"
#define TYPE_VIO_SPAPR_DEVICE "vio-spapr-device"
#define VIO_SPAPR_DEVICE(obj) \
@ -45,10 +36,6 @@ enum VIOsPAPR_TCEAccess {
struct VIOsPAPRDevice;
typedef struct VIOsPAPR_RTCE {
uint64_t tce;
} VIOsPAPR_RTCE;
typedef struct VIOsPAPR_CRQ {
uint64_t qladdr;
uint32_t qsize;
@ -64,6 +51,7 @@ typedef struct VIOsPAPRDeviceClass {
const char *dt_name, *dt_type, *dt_compatible;
target_ulong signal_mask;
uint32_t rtce_window_size;
int (*init)(VIOsPAPRDevice *dev);
void (*reset)(VIOsPAPRDevice *dev);
int (*devnode)(VIOsPAPRDevice *dev, void *fdt, int node_off);
@ -73,20 +61,15 @@ 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;
DMAContext *dma;
};
#define DEFINE_SPAPR_PROPERTIES(type, field, default_dma_window) \
DEFINE_PROP_UINT32("reg", type, field.reg, -1), \
DEFINE_PROP_UINT32("dma-window", type, field.rtce_window_size, \
default_dma_window)
#define DEFINE_SPAPR_PROPERTIES(type, field) \
DEFINE_PROP_UINT32("reg", type, field.reg, -1)
struct VIOsPAPRBus {
BusState bus;
@ -102,20 +85,38 @@ extern int spapr_populate_chosen_stdout(void *fdt, VIOsPAPRBus *bus);
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);
static inline bool spapr_vio_dma_valid(VIOsPAPRDevice *dev, uint64_t taddr,
uint32_t size, DMADirection dir)
{
return dma_memory_valid(dev->dma, taddr, size, dir);
}
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_dma_read(VIOsPAPRDevice *dev, uint64_t taddr,
void *buf, uint32_t size)
{
return (dma_memory_read(dev->dma, 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->dma, taddr, buf, size) != 0) ?
H_DEST_PARM : H_SUCCESS;
}
static inline int spapr_vio_dma_set(VIOsPAPRDevice *dev, uint64_t taddr,
uint8_t c, uint32_t size)
{
return (dma_memory_set(dev->dma, taddr, c, size) != 0) ?
H_DEST_PARM : H_SUCCESS;
}
#define vio_stb(_dev, _addr, _val) (stb_dma((_dev)->dma, (_addr), (_val)))
#define vio_sth(_dev, _addr, _val) (stw_be_dma((_dev)->dma, (_addr), (_val)))
#define vio_stl(_dev, _addr, _val) (stl_be_dma((_dev)->dma, (_addr), (_val)))
#define vio_stq(_dev, _addr, _val) (stq_be_dma((_dev)->dma, (_addr), (_val)))
#define vio_ldq(_dev, _addr) (ldq_be_dma((_dev)->dma, (_addr)))
int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq);

View File

@ -165,7 +165,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");
@ -281,9 +281,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;
@ -329,10 +329,11 @@ 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);
@ -345,12 +346,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",
@ -728,7 +729,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");
@ -742,7 +743,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");
@ -805,7 +806,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");
vscsi_put_req(req);
@ -947,7 +948,7 @@ static int spapr_vscsi_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off)
}
static Property spapr_vscsi_properties[] = {
DEFINE_SPAPR_PROPERTIES(VSCSIState, vdev, 0x10000000),
DEFINE_SPAPR_PROPERTIES(VSCSIState, vdev),
DEFINE_PROP_END_OF_LIST(),
};
@ -964,6 +965,7 @@ static void spapr_vscsi_class_init(ObjectClass *klass, void *data)
k->dt_compatible = "IBM,v-scsi";
k->signal_mask = 0x00000001;
dc->props = spapr_vscsi_properties;
k->rtce_window_size = 0x10000000;
}
static TypeInfo spapr_vscsi_info = {

View File

@ -133,7 +133,7 @@ void spapr_vty_create(VIOsPAPRBus *bus, CharDriverState *chardev)
}
static Property spapr_vty_properties[] = {
DEFINE_SPAPR_PROPERTIES(VIOsPAPRVTYDevice, sdev, 0),
DEFINE_SPAPR_PROPERTIES(VIOsPAPRVTYDevice, sdev),
DEFINE_PROP_CHR("chardev", VIOsPAPRVTYDevice, chardev),
DEFINE_PROP_END_OF_LIST(),
};

View File

@ -1067,7 +1067,7 @@ void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd)
return NULL;
}
len = (window_size / SPAPR_VIO_TCE_PAGE_SIZE) * sizeof(VIOsPAPR_RTCE);
len = (window_size / SPAPR_TCE_PAGE_SIZE) * sizeof(sPAPRTCE);
/* FIXME: round this up to page size */
table = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
@ -1090,7 +1090,7 @@ int kvmppc_remove_spapr_tce(void *table, int fd, uint32_t window_size)
return -1;
}
len = (window_size / SPAPR_VIO_TCE_PAGE_SIZE)*sizeof(VIOsPAPR_RTCE);
len = (window_size / SPAPR_TCE_PAGE_SIZE)*sizeof(sPAPRTCE);
if ((munmap(table, len) < 0) ||
(close(fd) < 0)) {
fprintf(stderr, "KVM: Unexpected error removing TCE table: %s",