[syscalls][bti] Introduce zx_bti_clear_quarantine

This allows BTI holders to clear out quarantined PMTs.  This will
generally be done by a driver after the device is initialized and its
DMA systems quiesced.

ZX-1936 #comment Introduce quarantine syscall

Change-Id: Ic6bf132381f994607d02d987e0302f3c9cf3265b
diff --git a/docs/objects/bus_transaction_initiator.md b/docs/objects/bus_transaction_initiator.md
index a6f73e6..ce7573d 100644
--- a/docs/objects/bus_transaction_initiator.md
+++ b/docs/objects/bus_transaction_initiator.md
@@ -23,6 +23,13 @@
 these addresses are issued with a different transaction ID, the transaction
 may fail and the issuing device may need a reset in order to continue functioning.
 
+A BTI manages a quarantine of PMTs.  If a PMT was created from a BTI using
+**bti_pin**(), and the PMT's handle is released without **pmt_unpin**() being
+called, the PMT will be quarantined.  Quarantined PMTs will prevent their
+underlying physical memory from being released to the system for reuse, in order
+to prevent DMA to memory that has since been reallocated.  The quarantine may be
+cleared by invoking **bti_clear_quarantine**().
+
 TODO(teisenbe): Add details about failed transaction notification.
 
 ## SEE ALSO
@@ -34,4 +41,5 @@
 
 + [bti_create](../syscalls/bti_create.md) - create a new bus transaction initiator
 + [bti_pin](../syscalls/bti_pin.md) - pin memory and grant access to it to the BTI
++ [bti_clear_quarantine](../syscalls/bti_clear_quarantine.md) - release quarantined PMTs
 + [pmt_unpin](../syscalls/pmt_unpin.md) - revoke access and unpin memory
diff --git a/docs/syscalls/bti_clear_quarantine.md b/docs/syscalls/bti_clear_quarantine.md
new file mode 100644
index 0000000..dfb053c
--- /dev/null
+++ b/docs/syscalls/bti_clear_quarantine.md
@@ -0,0 +1,39 @@
+# zx_bti_clear_quarantine
+
+## NAME
+
+bti_clear_quarantine - releases all quarantined PMTs
+
+## SYNOPSIS
+
+```
+#include <zircon/syscalls.h>
+
+zx_status_t zx_bti_clear_quarantine(zx_handle_t bti);
+
+```
+
+## DESCRIPTION
+
+**bti_clear_quarantine**() releases all quarantined PMTs for the given BTI.
+This will release the PMTs' underlying references to VMOs and physical page
+pins.  The underlying physical pages may be eligible to be reallocated
+afterwards.
+
+## RETURN VALUE
+
+**bti_clear_quarantine**() returns **ZX_OK** on success.
+In the event of failure, a negative error value is returned.
+
+## ERRORS
+
+**ZX_ERR_BAD_HANDLE**  *bti* is not a valid handle.
+
+**ZX_ERR_WRONG_TYPE**  *bti* is not a BTI handle.
+
+**ZX_ERR_ACCESS_DENIED** *bti* does not have the *ZX_RIGHT_WRITE* right.
+
+## SEE ALSO
+
+[bti_pin](bti_pin.md),
+[pmt_unpin](pmt_unpin.md).
diff --git a/docs/syscalls/bti_create.md b/docs/syscalls/bti_create.md
index 2e92431..2c99a54 100644
--- a/docs/syscalls/bti_create.md
+++ b/docs/syscalls/bti_create.md
@@ -22,11 +22,12 @@
 *options* must be 0 (reserved for future definition of creation flags).
 
 Upon success a handle for the new BTI is returned.  This handle will have rights
-**ZX_RIGHT_READ**, **ZX_RIGHT_MAP**, **ZX_RIGHT_DUPLICATE**, and **ZX_RIGHT_TRANSFER**.
+**ZX_RIGHT_READ**, **ZX_RIGHT_WRITE**, **ZX_RIGHT_MAP**, **ZX_RIGHT_INSPECT**,
+**ZX_RIGHT_DUPLICATE**, and **ZX_RIGHT_TRANSFER**.
 
 ## RETURN VALUE
 
-**bti_create**() returns ZX_OK and a handle to the new BTI
+**bti_create**() returns **ZX_OK** and a handle to the new BTI
 (via *out*) on success.  In the event of failure, a negative error value
 is returned.
 
@@ -46,4 +47,5 @@
 ## SEE ALSO
 
 [bti_pin](bti_pin.md),
+[bti_clear_quarantine](bti_clear_quarantine.md)
 [pmt_unpin](pmt_unpin.md).
diff --git a/docs/syscalls/bti_pin.md b/docs/syscalls/bti_pin.md
index 84758c7..c835852 100644
--- a/docs/syscalls/bti_pin.md
+++ b/docs/syscalls/bti_pin.md
@@ -61,7 +61,8 @@
 
 On success, **bti_pin**() returns *ZX_OK*.  The device-physical addresses of the
 requested VMO pages will be written in *addrs*.  A handle to the created Pinned
-Memory Token is returned via *pmt*.
+Memory Token is returned via *pmt*.  When the PMT is no longer needed,
+*pmt_unpin*() should be invoked.
 
 In the event of failure, a negative error value is returned.
 
diff --git a/docs/syscalls/pmt_unpin.md b/docs/syscalls/pmt_unpin.md
index 161e8ec..697211e 100644
--- a/docs/syscalls/pmt_unpin.md
+++ b/docs/syscalls/pmt_unpin.md
@@ -34,4 +34,5 @@
 ## SEE ALSO
 
 [bti_create](bti_create.md),
+[bti_clear_quarantine](bti_clear_quarantine.md),
 [bti_pin](bti_pin.md).
diff --git a/kernel/object/bus_transaction_initiator_dispatcher.cpp b/kernel/object/bus_transaction_initiator_dispatcher.cpp
index 0821ded..d453539 100644
--- a/kernel/object/bus_transaction_initiator_dispatcher.cpp
+++ b/kernel/object/bus_transaction_initiator_dispatcher.cpp
@@ -62,6 +62,17 @@
                                                offset, size, perms, pmt, pmt_rights);
 }
 
+void BusTransactionInitiatorDispatcher::ClearQuarantine() {
+    QuarantineList tmp;
+
+    // The PMT dtor will call RemovePmo, which will reacquire this BTI's lock.
+    // To avoid deadlock, drop the lock before letting the quarantined PMTs go.
+    {
+        fbl::AutoLock guard(&lock_);
+        quarantine_.swap(tmp);
+    }
+}
+
 void BusTransactionInitiatorDispatcher::on_zero_handles() {
     fbl::AutoLock guard(&lock_);
     // Prevent new pinning from happening.  The Dispatcher will stick around
diff --git a/kernel/object/include/object/bus_transaction_initiator_dispatcher.h b/kernel/object/include/object/bus_transaction_initiator_dispatcher.h
index fc6e25f..0b1123b 100644
--- a/kernel/object/include/object/bus_transaction_initiator_dispatcher.h
+++ b/kernel/object/include/object/bus_transaction_initiator_dispatcher.h
@@ -42,6 +42,11 @@
     zx_status_t Pin(fbl::RefPtr<VmObject> vmo, uint64_t offset, uint64_t size, uint32_t perms,
                     fbl::RefPtr<Dispatcher>* pmt, zx_rights_t* rights);
 
+    // Releases all quarantined PMTs.  The memory pins are released and the VMO
+    // references are dropped, so the underlying VMOs may be immediately destroyed, and the
+    // underlying physical memory may be reallocated.
+    void ClearQuarantine();
+
     void on_zero_handles() final;
 
     fbl::RefPtr<Iommu> iommu() const { return iommu_; }
diff --git a/kernel/syscalls/ddk.cpp b/kernel/syscalls/ddk.cpp
index 2ed74d1..6881f7a 100644
--- a/kernel/syscalls/ddk.cpp
+++ b/kernel/syscalls/ddk.cpp
@@ -504,6 +504,19 @@
     return pmt->make(fbl::move(new_pmt), new_pmt_rights);
 }
 
+zx_status_t sys_bti_clear_quarantine(zx_handle_t bti) {
+    auto up = ProcessDispatcher::GetCurrent();
+    fbl::RefPtr<BusTransactionInitiatorDispatcher> bti_dispatcher;
+
+    zx_status_t status = up->GetDispatcherWithRights(bti, ZX_RIGHT_WRITE, &bti_dispatcher);
+    if (status != ZX_OK) {
+        return status;
+    }
+
+    bti_dispatcher->ClearQuarantine();
+    return ZX_OK;
+}
+
 // Having a single-purpose syscall like this is a bit of an anti-pattern in our
 // syscall API, but we feel there is benefit in this over trying to extend the
 // semantics of handle closing in sys_handle_close and process death.  In
diff --git a/system/public/zircon/rights.h b/system/public/zircon/rights.h
index 9717085..8059bd1 100644
--- a/system/public/zircon/rights.h
+++ b/system/public/zircon/rights.h
@@ -81,7 +81,7 @@
     (ZX_RIGHTS_BASIC & (~ZX_RIGHT_WAIT))
 
 #define ZX_DEFAULT_BTI_RIGHTS \
-    ((ZX_RIGHTS_BASIC & (~ZX_RIGHT_WAIT)) | ZX_RIGHT_READ | ZX_RIGHT_MAP)
+    ((ZX_RIGHTS_BASIC & (~ZX_RIGHT_WAIT)) | ZX_RIGHTS_IO | ZX_RIGHT_MAP)
 
 #define ZX_DEFAULT_PROFILE_RIGHTS \
     ((ZX_RIGHTS_BASIC & (~ZX_RIGHT_WAIT)) | ZX_RIGHT_APPLY_PROFILE)
diff --git a/system/public/zircon/syscalls.abigen b/system/public/zircon/syscalls.abigen
index fdae04d..764377a 100644
--- a/system/public/zircon/syscalls.abigen
+++ b/system/public/zircon/syscalls.abigen
@@ -622,6 +622,10 @@
         addrs: zx_paddr_t[addrs_count] OUT, addrs_count: size_t)
     returns (zx_status_t, out: zx_handle_t handle_acquire);
 
+syscall bti_clear_quarantine
+    (bti: zx_handle_t)
+    returns (zx_status_t);
+
 syscall pmt_unpin
     (pmt: zx_handle_t handle_release)
     returns (zx_status_t);