[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);