Index: qemu/hw/esp.c
===================================================================
--- qemu.orig/hw/esp.c	2008-12-15 17:50:06.000000000 +0000
+++ qemu/hw/esp.c	2008-12-15 18:11:26.000000000 +0000
@@ -76,8 +76,7 @@
     uint8_t *async_buf;
     uint32_t async_len;
 
-    espdma_memory_read_write dma_memory_read;
-    espdma_memory_read_write dma_memory_write;
+    espdma_memory_translate dma_memory_translate;
     void *dma_opaque;
 };
 
@@ -162,6 +161,24 @@
     }
 }
 
+static void esp_mem_rw(ESPState *s, void *buf, size_t len, int is_write)
+{
+    target_phys_addr_t phys;
+    size_t left, newlen;
+
+    left = len;
+    newlen = 0;
+    while (left > 0) {
+        phys = s->dma_memory_translate(s->dma_opaque, 0, len, &newlen,
+                                       is_write);
+        if (phys == (target_phys_addr_t) -1)
+            break;
+        DPRINTF("esp_mem_rw (dma addr 0x" TARGET_FMT_plx ")\n", phys);
+        cpu_physical_memory_rw(phys, buf, newlen, is_write);
+        left -= newlen;
+    }
+}
+
 static uint32_t get_cmd(ESPState *s, uint8_t *buf)
 {
     uint32_t dmalen;
@@ -170,7 +187,7 @@
     target = s->wregs[ESP_WBUSID] & BUSID_DID;
     if (s->dma) {
         dmalen = s->rregs[ESP_TCLO] | (s->rregs[ESP_TCMID] << 8);
-        s->dma_memory_read(s->dma_opaque, buf, dmalen);
+        esp_mem_rw(s, buf, dmalen, 0);
     } else {
         dmalen = s->ti_size;
         memcpy(buf, s->ti_buf, dmalen);
@@ -255,7 +272,7 @@
     s->ti_buf[0] = s->sense;
     s->ti_buf[1] = 0;
     if (s->dma) {
-        s->dma_memory_write(s->dma_opaque, s->ti_buf, 2);
+        esp_mem_rw(s, s->ti_buf, 2, 1);
         s->rregs[ESP_RSTAT] = STAT_TC | STAT_ST;
         s->rregs[ESP_RINTR] = INTR_BS | INTR_FC;
         s->rregs[ESP_RSEQ] = SEQ_CD;
@@ -288,7 +305,7 @@
     len = s->dma_left;
     if (s->do_cmd) {
         DPRINTF("command len %d + %d\n", s->cmdlen, len);
-        s->dma_memory_read(s->dma_opaque, &s->cmdbuf[s->cmdlen], len);
+        esp_mem_rw(s, &s->cmdbuf[s->cmdlen], len, !to_device);
         s->ti_size = 0;
         s->cmdlen = 0;
         s->do_cmd = 0;
@@ -302,11 +319,7 @@
     if (len > s->async_len) {
         len = s->async_len;
     }
-    if (to_device) {
-        s->dma_memory_read(s->dma_opaque, s->async_buf, len);
-    } else {
-        s->dma_memory_write(s->dma_opaque, s->async_buf, len);
-    }
+    esp_mem_rw(s, s->async_buf, len, !to_device);
     s->dma_left -= len;
     s->async_buf += len;
     s->async_len -= len;
@@ -645,8 +658,7 @@
 }
 
 void *esp_init(target_phys_addr_t espaddr, int it_shift,
-               espdma_memory_read_write dma_memory_read,
-               espdma_memory_read_write dma_memory_write,
+               espdma_memory_translate dma_memory_translate,
                void *dma_opaque, qemu_irq irq, qemu_irq *reset)
 {
     ESPState *s;
@@ -658,8 +670,7 @@
 
     s->irq = irq;
     s->it_shift = it_shift;
-    s->dma_memory_read = dma_memory_read;
-    s->dma_memory_write = dma_memory_write;
+    s->dma_memory_translate = dma_memory_translate;
     s->dma_opaque = dma_opaque;
 
     esp_io_memory = cpu_register_io_memory(0, esp_mem_read, esp_mem_write, s);
Index: qemu/hw/iommu.c
===================================================================
--- qemu.orig/hw/iommu.c	2008-12-15 17:50:06.000000000 +0000
+++ qemu/hw/iommu.c	2008-12-15 18:11:26.000000000 +0000
@@ -265,24 +265,12 @@
     return pa;
 }
 
-static void iommu_bad_addr(IOMMUState *s, target_phys_addr_t addr,
-                           int is_write)
+target_phys_addr_t iommu_translate(void *opaque, target_phys_addr_t addr,
+                                   size_t len, size_t *newlen, int is_write)
 {
-    DPRINTF("bad addr " TARGET_FMT_plx "\n", addr);
-    s->regs[IOMMU_AFSR] = IOMMU_AFSR_ERR | IOMMU_AFSR_LE | IOMMU_AFSR_RESV |
-        IOMMU_AFSR_FAV;
-    if (!is_write)
-        s->regs[IOMMU_AFSR] |= IOMMU_AFSR_RD;
-    s->regs[IOMMU_AFAR] = addr;
-    qemu_irq_raise(s->irq);
-}
-
-void sparc_iommu_memory_rw(void *opaque, target_phys_addr_t addr,
-                           uint8_t *buf, int len, int is_write)
-{
-    int l;
+    int l, first = 1;
     uint32_t flags;
-    target_phys_addr_t page, phys_addr;
+    target_phys_addr_t page, phys_addr, prev_phys = 0, first_phys = 0;
 
     while (len > 0) {
         page = addr & IOMMU_PAGE_MASK;
@@ -291,23 +279,28 @@
             l = len;
         flags = iommu_page_get_flags(opaque, page);
         if (!(flags & IOPTE_VALID)) {
-            iommu_bad_addr(opaque, page, is_write);
-            return;
+            return -1;
         }
-        phys_addr = iommu_translate_pa(addr, flags);
         if (is_write) {
             if (!(flags & IOPTE_WRITE)) {
-                iommu_bad_addr(opaque, page, is_write);
-                return;
+                return -1;
             }
-            cpu_physical_memory_write(phys_addr, buf, l);
+        }
+        phys_addr = iommu_translate_pa(addr, flags);
+        if (first) {
+            first = 0;
+            first_phys = prev_phys = phys_addr;
+        } else if ((phys_addr & IOMMU_PAGE_MASK) !=
+                   (prev_phys & IOMMU_PAGE_MASK) + IOMMU_PAGE_SIZE) {
+            return first_phys;
         } else {
-            cpu_physical_memory_read(phys_addr, buf, l);
+            prev_phys = phys_addr;
         }
         len -= l;
-        buf += l;
         addr += l;
+        *newlen += l;
     }
+    return first_phys;
 }
 
 static void iommu_save(QEMUFile *f, void *opaque)
Index: qemu/hw/sparc32_dma.c
===================================================================
--- qemu.orig/hw/sparc32_dma.c	2008-12-15 17:50:06.000000000 +0000
+++ qemu/hw/sparc32_dma.c	2008-12-15 18:11:26.000000000 +0000
@@ -63,6 +63,37 @@
     qemu_irq dev_reset;
 };
 
+static target_phys_addr_t ledma_translate(void *opaque,
+                                          target_phys_addr_t addr,
+                                          size_t len, size_t *newlen,
+                                          int is_write)
+{
+    DMAState *s = opaque;
+
+    DPRINTF("LEDMA translate, direction: %c, addr 0x%8.8x\n",
+            is_write ? 'w': 'r', s->dmaregs[3]);
+    return iommu_translate(s->iommu, s->dmaregs[3], len, newlen, is_write);
+}
+
+static void ledma_mem_rw(DMAState *s, target_phys_addr_t addr, void *buf,
+                         size_t len, int is_write)
+{
+    target_phys_addr_t phys;
+    size_t left, newlen;
+
+    left = len;
+    newlen = 0;
+    while (left > 0) {
+        phys = ledma_translate(s, addr, len, &newlen, is_write);
+        if (phys == (target_phys_addr_t) -1)
+            break;
+        DPRINTF("lance_mem_rw (dma addr 0x" TARGET_FMT_plx ")\n", phys);
+        cpu_physical_memory_rw(phys, buf, newlen, is_write);
+        addr += newlen;
+        left -= newlen;
+    }
+}
+
 /* Note: on sparc, the lance 16 bit bus is swapped */
 void ledma_memory_read(void *opaque, target_phys_addr_t addr,
                        uint8_t *buf, int len, int do_bswap)
@@ -74,11 +105,11 @@
             s->dmaregs[0] & DMA_WRITE_MEM ? 'w': 'r', s->dmaregs[1]);
     addr |= s->dmaregs[3];
     if (do_bswap) {
-        sparc_iommu_memory_read(s->iommu, addr, buf, len);
+        ledma_mem_rw(s, addr, buf, len, 0);
     } else {
         addr &= ~1;
         len &= ~1;
-        sparc_iommu_memory_read(s->iommu, addr, buf, len);
+        ledma_mem_rw(s, addr, buf, len, 0);
         for(i = 0; i < len; i += 2) {
             bswap16s((uint16_t *)(buf + i));
         }
@@ -96,7 +127,7 @@
             s->dmaregs[0] & DMA_WRITE_MEM ? 'w': 'r', s->dmaregs[1]);
     addr |= s->dmaregs[3];
     if (do_bswap) {
-        sparc_iommu_memory_write(s->iommu, addr, buf, len);
+        ledma_mem_rw(s, addr, buf, len, 1);
     } else {
         addr &= ~1;
         len &= ~1;
@@ -107,7 +138,7 @@
             for(i = 0; i < l; i += 2) {
                 tmp_buf[i >> 1] = bswap16(*(uint16_t *)(buf + i));
             }
-            sparc_iommu_memory_write(s->iommu, addr, (uint8_t *)tmp_buf, l);
+            ledma_mem_rw(s, addr, (uint8_t *)tmp_buf, l, 1);
             len -= l;
             buf += l;
             addr += l;
@@ -129,26 +160,14 @@
     }
 }
 
-void espdma_memory_read(void *opaque, uint8_t *buf, int len)
+target_phys_addr_t espdma_translate(void *opaque, target_phys_addr_t addr,
+                                    size_t len, size_t *newlen, int is_write)
 {
     DMAState *s = opaque;
 
-    DPRINTF("DMA read, direction: %c, addr 0x%8.8x\n",
-            s->dmaregs[0] & DMA_WRITE_MEM ? 'w': 'r', s->dmaregs[1]);
-    sparc_iommu_memory_read(s->iommu, s->dmaregs[1], buf, len);
-    s->dmaregs[0] |= DMA_INTR;
-    s->dmaregs[1] += len;
-}
-
-void espdma_memory_write(void *opaque, uint8_t *buf, int len)
-{
-    DMAState *s = opaque;
-
-    DPRINTF("DMA write, direction: %c, addr 0x%8.8x\n",
-            s->dmaregs[0] & DMA_WRITE_MEM ? 'w': 'r', s->dmaregs[1]);
-    sparc_iommu_memory_write(s->iommu, s->dmaregs[1], buf, len);
-    s->dmaregs[0] |= DMA_INTR;
-    s->dmaregs[1] += len;
+    DPRINTF("ESPDMA translate, direction: %c, addr 0x%8.8x\n",
+            is_write ? 'w': 'r', s->dmaregs[1]);
+    return iommu_translate(s->iommu, s->dmaregs[1], len, newlen, is_write);
 }
 
 static uint32_t dma_mem_readl(void *opaque, target_phys_addr_t addr)
Index: qemu/hw/scsi.h
===================================================================
--- qemu.orig/hw/scsi.h	2008-12-15 17:50:06.000000000 +0000
+++ qemu/hw/scsi.h	2008-12-15 18:11:26.000000000 +0000
@@ -1,8 +1,11 @@
 /* esp.c */
 #define ESP_MAX_DEVS 7
-typedef void (*espdma_memory_read_write)(void *opaque, uint8_t *buf, int len);
+typedef target_phys_addr_t (*espdma_memory_translate)(void *opaque,
+                                                      target_phys_addr_t addr,
+                                                      size_t len,
+                                                      size_t *newlen,
+                                                      int is_write);
 void esp_scsi_attach(void *opaque, BlockDriverState *bd, int id);
 void *esp_init(target_phys_addr_t espaddr, int it_shift,
-               espdma_memory_read_write dma_memory_read,
-               espdma_memory_read_write dma_memory_write,
+               espdma_memory_translate dma_memory_translate,
                void *dma_opaque, qemu_irq irq, qemu_irq *reset);
Index: qemu/hw/sparc32_dma.h
===================================================================
--- qemu.orig/hw/sparc32_dma.h	2008-12-15 17:50:06.000000000 +0000
+++ qemu/hw/sparc32_dma.h	2008-12-15 18:11:26.000000000 +0000
@@ -8,7 +8,6 @@
                        uint8_t *buf, int len, int do_bswap);
 void ledma_memory_write(void *opaque, target_phys_addr_t addr,
                         uint8_t *buf, int len, int do_bswap);
-void espdma_memory_read(void *opaque, uint8_t *buf, int len);
-void espdma_memory_write(void *opaque, uint8_t *buf, int len);
-
+target_phys_addr_t espdma_translate(void *opaque, target_phys_addr_t addr,
+                                    size_t len, size_t *newlen, int is_write);
 #endif
Index: qemu/hw/sun4m.c
===================================================================
--- qemu.orig/hw/sun4m.c	2008-12-15 17:50:06.000000000 +0000
+++ qemu/hw/sun4m.c	2008-12-15 18:11:26.000000000 +0000
@@ -580,7 +580,7 @@
     }
 
     main_esp = esp_init(hwdef->esp_base, 2,
-                        espdma_memory_read, espdma_memory_write,
+                        espdma_translate,
                         espdma, *espdma_irq, esp_reset);
 
     for (i = 0; i < ESP_MAX_DEVS; i++) {
@@ -1378,7 +1378,7 @@
     }
 
     main_esp = esp_init(hwdef->esp_base, 2,
-                        espdma_memory_read, espdma_memory_write,
+                        espdma_translate,
                         espdma, *espdma_irq, esp_reset);
 
     for (i = 0; i < ESP_MAX_DEVS; i++) {
@@ -1604,7 +1604,7 @@
     }
 
     main_esp = esp_init(hwdef->esp_base, 2,
-                        espdma_memory_read, espdma_memory_write,
+                        espdma_translate,
                         espdma, *espdma_irq, esp_reset);
 
     for (i = 0; i < ESP_MAX_DEVS; i++) {
Index: qemu/hw/sun4m.h
===================================================================
--- qemu.orig/hw/sun4m.h	2008-12-15 17:50:06.000000000 +0000
+++ qemu/hw/sun4m.h	2008-12-15 18:11:26.000000000 +0000
@@ -5,21 +5,8 @@
 
 /* iommu.c */
 void *iommu_init(target_phys_addr_t addr, uint32_t version, qemu_irq irq);
-void sparc_iommu_memory_rw(void *opaque, target_phys_addr_t addr,
-                                 uint8_t *buf, int len, int is_write);
-static inline void sparc_iommu_memory_read(void *opaque,
-                                           target_phys_addr_t addr,
-                                           uint8_t *buf, int len)
-{
-    sparc_iommu_memory_rw(opaque, addr, buf, len, 0);
-}
-
-static inline void sparc_iommu_memory_write(void *opaque,
-                                            target_phys_addr_t addr,
-                                            uint8_t *buf, int len)
-{
-    sparc_iommu_memory_rw(opaque, addr, buf, len, 1);
-}
+target_phys_addr_t iommu_translate(void *opaque, target_phys_addr_t addr,
+                                   size_t len, size_t *newlen, int is_write);
 
 /* tcx.c */
 void tcx_init(DisplayState *ds, target_phys_addr_t addr, uint8_t *vram_base,
