[block][nvme] migrate to new bti based pin/unpin operations

Change-Id: I8bd73703e47b1604f6ab1305502a7bb23bec9952
diff --git a/system/dev/block/nvme/nvme.c b/system/dev/block/nvme/nvme.c
index ea7e285..38bdaa1 100644
--- a/system/dev/block/nvme/nvme.c
+++ b/system/dev/block/nvme/nvme.c
@@ -56,10 +56,10 @@
 #define UTXN_COUNT 63
 
 // There's no system constant for this.  Ensure it matches reality.
-#define PAGE_SHIFT 12
-static_assert(PAGE_SIZE == (1 << PAGE_SHIFT), "");
+#define PAGE_SHIFT (12ULL)
+static_assert(PAGE_SIZE == (1ULL << PAGE_SHIFT), "");
 
-#define PAGE_MASK (PAGE_SIZE - 1)
+#define PAGE_MASK (PAGE_SIZE - 1ULL)
 
 // Limit maximum transfer size to 1MB which fits comfortably
 // within our single scatter gather page per utxn setup
@@ -79,7 +79,9 @@
 
 typedef struct {
     void* io;
+    zx_handle_t ioh;
     zx_handle_t irqh;
+    zx_handle_t bti;
     uint32_t flags;
     mtx_t lock;
 
@@ -138,7 +140,6 @@
     zx_device_t* zxdev;
 
     size_t iosz;
-    zx_handle_t ioh;
 
     // source of physical pages for queues and admin commands
     io_buffer_t iob;
@@ -337,6 +338,7 @@
 static bool io_process_txn(nvme_device_t* nvme, nvme_txn_t* txn) {
     zx_handle_t vmo = txn->op.rw.vmo;
     nvme_utxn_t* utxn;
+    zx_paddr_t* pages;
     zx_status_t r;
 
     for (;;) {
@@ -351,25 +353,30 @@
             blocks = nvme->max_xfer;
         }
 
+        // Total transfer size in bytes
         size_t bytes = ((size_t) blocks) * ((size_t) nvme->info.block_size);
 
-        if ((r = zx_vmo_op_range(vmo, ZX_VMO_OP_COMMIT,
-                                 txn->op.rw.offset_vmo, bytes, NULL, 0)) != ZX_OK) {
-            zxlogf(ERROR, "nvme: could not commit pages\n");
+        // Page offset of first page of transfer
+        size_t pageoffset = txn->op.rw.offset_vmo & (~PAGE_MASK);
+
+        // Byte offset into first page of transfer
+        size_t byteoffset = txn->op.rw.offset_vmo & PAGE_MASK;
+
+        // Total pages mapped / touched
+        size_t pagecount = (byteoffset + bytes + PAGE_MASK) >> PAGE_SHIFT;
+
+        // read disk (OP_READ) -> memory (PERM_WRITE) or
+        // write memory (PERM_READ) -> disk (OP_WRITE)
+        uint32_t opt = (txn->opcode == NVME_OP_READ) ? ZX_BTI_PERM_WRITE : ZX_BTI_PERM_READ;
+
+        pages = utxn->virt;
+
+        if ((r = zx_bti_pin(nvme->bti, opt, vmo, pageoffset, pagecount << PAGE_SHIFT,
+                            pages, pagecount)) != ZX_OK) {
+            zxlogf(ERROR, "nvme: could not pin pages: %d\n", r);
             break;
         }
 
-        zx_paddr_t* pages = utxn->virt;
-        if ((r = zx_vmo_op_range(vmo, ZX_VMO_OP_LOOKUP,
-                                 txn->op.rw.offset_vmo, bytes, pages, PAGE_SIZE)) != ZX_OK) {
-            zxlogf(ERROR, "nvme: could not lookup pages\n");
-            break;
-        }
-
-        // Take the starting byte into the initial page plus total bytes
-        // transferred, convert to page count (rounded up)
-        size_t pagecount = ((txn->op.rw.offset_vmo & PAGE_MASK) + bytes + PAGE_MASK) / PAGE_SIZE;
-
         nvme_cmd_t cmd;
         memset(&cmd, 0, sizeof(cmd));
         cmd.cmd = NVME_CMD_CID(utxn->id) | NVME_CMD_PRP | NVME_CMD_NORMAL | NVME_CMD_OPC(txn->opcode);
@@ -380,7 +387,7 @@
         // The first is always the pointer to the first page where data is.
         // The second is the second page if pagecount is 2.
         // The second is the address of an array of page 2..n if pagecount > 2
-        cmd.dptr.prp[0] = pages[0] | (txn->op.rw.offset_vmo & PAGE_MASK);
+        cmd.dptr.prp[0] = pages[0] | byteoffset;
         if (pagecount == 2) {
             cmd.dptr.prp[1] = pages[1];
         } else if (pagecount > 2) {
@@ -418,6 +425,9 @@
     }
 
     // failure
+    if ((r = zx_bti_unpin(nvme->bti, pages[0])) != ZX_OK) {
+        zxlogf(ERROR, "nvme: cannot unpin io buffer: %d\n", r);
+    }
     utxn_put(nvme, utxn);
 
     mtx_lock(&nvme->lock);
@@ -493,6 +503,11 @@
             zxlogf(SPEW, "nvme: utxn #%u txn %p OKAY\n", cpl.cmd_id, txn);
         }
 
+        zx_status_t r;
+        if ((r = zx_bti_unpin(nvme->bti, ((zx_paddr_t*)utxn->virt)[0])) != ZX_OK) {
+            zxlogf(ERROR, "nvme: cannot unpin io buffer: %d\n", r);
+        }
+
         // release the microtransaction
         utxn->txn = NULL;
         utxn_put(nvme, utxn);
@@ -665,6 +680,7 @@
     nvme->flags |= FLAG_SHUTDOWN;
     if (nvme->ioh != ZX_HANDLE_INVALID) {
         pci_enable_bus_master(&nvme->pci, false);
+        zx_handle_close(nvme->bti);
         zx_handle_close(nvme->ioh);
         // TODO: risks a handle use-after-close, will be resolved by IRQ api
         // changes coming soon
@@ -780,7 +796,8 @@
         return ZX_ERR_NOT_SUPPORTED;
     }
     // allocate pages for various queues and the utxn scatter lists
-    if (io_buffer_init(&nvme->iob, PAGE_SIZE * IO_PAGE_COUNT, IO_BUFFER_RW) ||
+    // TODO: these should all be RO to hardware apart from the scratch io page(s)
+    if (io_buffer_init_with_bti(&nvme->iob, nvme->bti, PAGE_SIZE * IO_PAGE_COUNT, IO_BUFFER_RW) ||
         io_buffer_physmap(&nvme->iob)) {
         zxlogf(ERROR, "nvme: could not allocate io buffers\n");
         return ZX_ERR_NO_MEMORY;
@@ -1102,6 +1119,10 @@
         zxlogf(ERROR, "nvme: cannot enable bus mastering\n");
         goto fail;
     }
+    if (pci_get_bti(&nvme->pci, 0, &nvme->bti) != ZX_OK) {
+        zxlogf(ERROR, "nvme: cannot obtain bti handle\n");
+        goto fail;
+    }
 
     device_add_args_t args = {
         .version = DEVICE_ADD_ARGS_VERSION,