summaryrefslogtreecommitdiff
path: root/hw/spapr_vio.c
diff options
context:
space:
mode:
authorBen Herrenschmidt <benh@kernel.crashing.org>2011-04-01 15:15:32 +1100
committerAlexander Graf <agraf@suse.de>2011-04-01 18:34:57 +0200
commit08942ac17922d923a7cc5cf9854e9cc4b150b942 (patch)
treeb080513d06e331b97f9a8d38101e4e5bc0e03c3c /hw/spapr_vio.c
parent6e270446d0e107b5227d8c51d2f85546f8811e99 (diff)
downloadqemu-08942ac17922d923a7cc5cf9854e9cc4b150b942.tar.gz
Add a PAPR TCE-bypass mechanism for the pSeries machine
Usually, PAPR virtual IO devices use a virtual IOMMU mechanism, TCEs, to mediate all DMA transfers. While this is necessary for some sorts of operation, it can be complex to program and slow for others. This patch implements a mechanism for bypassing TCE translation, treating "IO" addresses as plain (guest) physical memory addresses. This has two main uses: * Simple, but 64-bit aware programs like firmwares can use the VIO devices without the complexity of TCE setup. * The guest OS can optionally use the TCE bypass to improve performance in suitable situations. The mechanism used is a per-device flag which disables TCE translation. The flag is toggled with some (hypervisor-implemented) RTAS methods. Signed-off-by: Ben Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: David Gibson <dwg@au1.ibm.com> Signed-off-by: Alexander Graf <agraf@suse.de>
Diffstat (limited to 'hw/spapr_vio.c')
-rw-r--r--hw/spapr_vio.c82
1 files changed, 82 insertions, 0 deletions
diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c
index 8f14fcc794..481a804e73 100644
--- a/hw/spapr_vio.c
+++ b/hw/spapr_vio.c
@@ -226,6 +226,12 @@ int spapr_tce_dma_write(VIOsPAPRDevice *dev, uint64_t taddr, const void *buf,
(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;
@@ -313,6 +319,12 @@ int spapr_tce_dma_read(VIOsPAPRDevice *dev, uint64_t taddr, void *buf,
(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;
@@ -513,6 +525,72 @@ int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq)
return 0;
}
+/* "quiesce" handling */
+
+static void spapr_vio_quiesce_one(VIOsPAPRDevice *dev)
+{
+ dev->flags &= ~VIO_PAPR_FLAG_DMA_BYPASS;
+
+ 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);
+ }
+
+ dev->crq.qladdr = 0;
+ dev->crq.qsize = 0;
+ dev->crq.qnext = 0;
+}
+
+static void rtas_set_tce_bypass(sPAPREnvironment *spapr, uint32_t token,
+ uint32_t nargs, target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ VIOsPAPRBus *bus = spapr->vio_bus;
+ VIOsPAPRDevice *dev;
+ uint32_t unit, enable;
+
+ if (nargs != 2) {
+ rtas_st(rets, 0, -3);
+ return;
+ }
+ unit = rtas_ld(args, 0);
+ enable = rtas_ld(args, 1);
+ dev = spapr_vio_find_by_reg(bus, unit);
+ if (!dev) {
+ rtas_st(rets, 0, -3);
+ return;
+ }
+ if (enable) {
+ dev->flags |= VIO_PAPR_FLAG_DMA_BYPASS;
+ } else {
+ dev->flags &= ~VIO_PAPR_FLAG_DMA_BYPASS;
+ }
+
+ rtas_st(rets, 0, 0);
+}
+
+static void rtas_quiesce(sPAPREnvironment *spapr, uint32_t token,
+ uint32_t nargs, target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ VIOsPAPRBus *bus = spapr->vio_bus;
+ DeviceState *qdev;
+ VIOsPAPRDevice *dev = NULL;
+
+ if (nargs != 0) {
+ rtas_st(rets, 0, -3);
+ return;
+ }
+
+ QLIST_FOREACH(qdev, &bus->bus.children, sibling) {
+ dev = (VIOsPAPRDevice *)qdev;
+ spapr_vio_quiesce_one(dev);
+ }
+
+ rtas_st(rets, 0, 0);
+}
+
static int spapr_vio_busdev_init(DeviceState *qdev, DeviceInfo *qinfo)
{
VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo;
@@ -591,6 +669,10 @@ VIOsPAPRBus *spapr_vio_bus_init(void)
spapr_register_hypercall(H_SEND_CRQ, h_send_crq);
spapr_register_hypercall(H_ENABLE_CRQ, h_enable_crq);
+ /* RTAS calls */
+ spapr_rtas_register("ibm,set-tce-bypass", rtas_set_tce_bypass);
+ spapr_rtas_register("quiesce", rtas_quiesce);
+
for (qinfo = device_info_list; qinfo; qinfo = qinfo->next) {
VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo;