[dev][sdmmc] use BTI

This is a re-land of I1cb6db88c8a08c88c0de811acdb8123936fc9ffa (git
5a04ac31d589785303c0f41913fd59dbd5ad46ef), which was reverted in
335c3a414e169804eb9050d784af6cebd0f5c77f.  Testing on Acer and Paradise
showed no issue.  The cause of the revert was this change making
an existing buffer overrrun in the SDMMC driver become more visible (ZX-1851).
The only changes are additional logging in error cases.

Change-Id: Ia078bf659793983fc9240b0de1355e1d4d4d5c7c
diff --git a/system/dev/block/sdhci/sdhci.c b/system/dev/block/sdhci/sdhci.c
index f5ad649..88cfaac 100644
--- a/system/dev/block/sdhci/sdhci.c
+++ b/system/dev/block/sdhci/sdhci.c
@@ -39,7 +39,7 @@
 
 #define MAX_TUNING_COUNT 40
 
-#define PAGE_MASK   (PAGE_SIZE - 1)
+#define PAGE_MASK   (PAGE_SIZE - 1ull)
 
 #define HI32(val)   (((val) >> 32) & 0xffffffff)
 #define LO32(val)   ((val) & 0xffffffff)
@@ -358,8 +358,27 @@
     block_op_t* bop = &req->txn->bop;
     uint64_t pagecount = ((bop->rw.offset_vmo & PAGE_MASK) + bop->rw.length + PAGE_MASK) /
                          PAGE_SIZE;
+    if (pagecount > SDMMC_PAGES_COUNT) {
+        zxlogf(ERROR, "sdhci: too many pages %lu vs %lu\n", pagecount, SDMMC_PAGES_COUNT);
+        return ZX_ERR_INVALID_ARGS;
+    }
+
+    // pin the vmo
+    zx_paddr_t phys[SDMMC_PAGES_COUNT];
+    // offset_vmo is converted to bytes by the sdmmc layer
+    uint32_t options = bop->command == BLOCK_OP_READ ? ZX_BTI_PERM_WRITE : ZX_BTI_PERM_READ;
+    zx_status_t st = zx_bti_pin(dev->bti_handle, options, bop->rw.vmo,
+                                bop->rw.offset_vmo & ~PAGE_MASK,
+                                pagecount * PAGE_SIZE, phys, pagecount);
+    if (st != ZX_OK) {
+        zxlogf(ERROR, "sdhci: error %d bti_pin\n", st);
+        return st;
+    }
+    // cache this for zx_bti_unpin() later
+    req->phys = phys[0];
+
     phys_iter_buffer_t buf = {
-        .phys = req->phys,
+        .phys = phys,
         .phys_count = pagecount,
         .length = bop->rw.length,
         .vmo_offset = bop->rw.offset_vmo,
@@ -490,6 +509,18 @@
     return st;
 }
 
+static zx_status_t sdhci_finish_req(sdhci_device_t* dev, sdmmc_req_t* req) {
+    zx_status_t st = ZX_OK;
+    if (req->use_dma && req->phys) {
+        st = zx_bti_unpin(dev->bti_handle, req->phys);
+        if (st != ZX_OK) {
+            zxlogf(ERROR, "sdhci: error %d in bti_unpin\n", st);
+        }
+        req->phys = 0;
+    }
+    return st;
+}
+
 static zx_status_t sdhci_host_info(void* ctx, sdmmc_host_info_t* info) {
     sdhci_device_t* dev = ctx;
     memcpy(info, &dev->info, sizeof(dev->info));
@@ -724,16 +755,23 @@
     }
 
     st = sdhci_start_req_locked(dev, req);
+    if (st != ZX_OK) {
+        goto unlock_out;
+    }
 
     mtx_unlock(&dev->mtx);
 
     completion_wait(&dev->req_completion, ZX_TIME_INFINITE);
+
+    sdhci_finish_req(dev, req);
+
     completion_reset(&dev->req_completion);
 
     return req->status;
 
 unlock_out:
     mtx_unlock(&dev->mtx);
+    sdhci_finish_req(dev, req);
     return st;
 }
 
diff --git a/system/dev/block/sdmmc/sdmmc.c b/system/dev/block/sdmmc/sdmmc.c
index 5013f65..c2bc7a5 100644
--- a/system/dev/block/sdmmc/sdmmc.c
+++ b/system/dev/block/sdmmc/sdmmc.c
@@ -311,29 +311,13 @@
         if (st != ZX_OK) {
             zxlogf(TRACE, "sdmmc: do_txn cacheop error %d\n", st);
             block_complete(&txn->bop, st);
+            return;
         }
 
         req->use_dma = true;
         req->virt = NULL;
+        req->phys = 0;
 
-        // TODO: use pages in txn->bop.rw.pages
-        st = zx_vmo_op_range(txn->bop.rw.vmo, ZX_VMO_OP_COMMIT,
-                             txn->bop.rw.offset_vmo, txn->bop.rw.length, NULL, 0);
-        if (st != ZX_OK) {
-            zxlogf(TRACE, "sdmmc: do_txn vmo commit error %d\n", st);
-            block_complete(&txn->bop, st);
-            return;
-        }
-        st = zx_vmo_op_range(txn->bop.rw.vmo, ZX_VMO_OP_LOOKUP,
-                             txn->bop.rw.offset_vmo, txn->bop.rw.length,
-                             req->phys, sizeof(req->phys));
-        if (st != ZX_OK) {
-            zxlogf(TRACE, "sdmmc: do_txn vmo lookup error %d\n", st);
-            zxlogf(TRACE, "sdmmc: offset_vmo 0x%" PRIx64 " length 0x%x buflen 0x%zx\n",
-                   txn->bop.rw.offset_vmo, txn->bop.rw.length, sizeof(req->phys));
-            block_complete(&txn->bop, st);
-            return;
-        }
     } else {
         req->use_dma = false;
         st = zx_vmar_map(zx_vmar_root_self(), 0, txn->bop.rw.vmo,
diff --git a/system/ulib/ddk/include/ddk/protocol/sdmmc.h b/system/ulib/ddk/include/ddk/protocol/sdmmc.h
index 9171d1c..25dd58a 100644
--- a/system/ulib/ddk/include/ddk/protocol/sdmmc.h
+++ b/system/ulib/ddk/include/ddk/protocol/sdmmc.h
@@ -60,7 +60,7 @@
 
     bool use_dma;
     void* virt;
-    zx_paddr_t phys[SDMMC_PAGES_COUNT];
+    zx_paddr_t phys;
 
     // response data
     uint32_t response[4];