blob: d453539ed3197952e3a20bdce6ab274b70c64c04 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <object/bus_transaction_initiator_dispatcher.h>
#include <dev/iommu.h>
#include <err.h>
#include <vm/vm_object.h>
#include <zircon/rights.h>
#include <zxcpp/new.h>
#include <fbl/auto_lock.h>
zx_status_t BusTransactionInitiatorDispatcher::Create(fbl::RefPtr<Iommu> iommu, uint64_t bti_id,
fbl::RefPtr<Dispatcher>* dispatcher,
zx_rights_t* rights) {
if (!iommu->IsValidBusTxnId(bti_id)) {
return ZX_ERR_INVALID_ARGS;
}
fbl::AllocChecker ac;
auto disp = new (&ac) BusTransactionInitiatorDispatcher(fbl::move(iommu), bti_id);
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
*rights = ZX_DEFAULT_BTI_RIGHTS;
*dispatcher = fbl::AdoptRef<Dispatcher>(disp);
return ZX_OK;
}
BusTransactionInitiatorDispatcher::BusTransactionInitiatorDispatcher(fbl::RefPtr<Iommu> iommu,
uint64_t bti_id)
: iommu_(fbl::move(iommu)), bti_id_(bti_id), zero_handles_(false) {}
BusTransactionInitiatorDispatcher::~BusTransactionInitiatorDispatcher() {
DEBUG_ASSERT(pinned_memory_.is_empty());
}
zx_status_t BusTransactionInitiatorDispatcher::Pin(fbl::RefPtr<VmObject> vmo, uint64_t offset,
uint64_t size, uint32_t perms,
fbl::RefPtr<Dispatcher>* pmt,
zx_rights_t* pmt_rights) {
DEBUG_ASSERT(IS_PAGE_ALIGNED(offset));
DEBUG_ASSERT(IS_PAGE_ALIGNED(size));
if (size == 0) {
return ZX_ERR_INVALID_ARGS;
}
fbl::AutoLock guard(&lock_);
if (zero_handles_) {
return ZX_ERR_BAD_STATE;
}
return PinnedMemoryTokenDispatcher::Create(fbl::WrapRefPtr(this), fbl::move(vmo),
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
// until all of the PMTs are closed.
zero_handles_ = true;
// Do not clear out the quarantine list. PMTs hold a reference to the BTI
// and the BTI holds a reference to each quarantined PMT. We intentionally
// leak the BTI, all quarantined PMTs, and their underlying VMOs. We could
// get away with freeing the BTI and the PMTs, but for safety we must leak
// at least the pinned parts of the VMOs, since we have no assurance that
// hardware is not still reading/writing to it.
if (!quarantine_.is_empty()) {
PrintQuarantineWarningLocked();
}
}
void BusTransactionInitiatorDispatcher::AddPmoLocked(PinnedMemoryTokenDispatcher* pmt) {
DEBUG_ASSERT(!pmt->dll_pmt_.InContainer());
pinned_memory_.push_back(pmt);
}
void BusTransactionInitiatorDispatcher::RemovePmo(PinnedMemoryTokenDispatcher* pmt) {
fbl::AutoLock guard(&lock_);
DEBUG_ASSERT(pmt->dll_pmt_.InContainer());
pinned_memory_.erase(*pmt);
}
void BusTransactionInitiatorDispatcher::Quarantine(fbl::RefPtr<PinnedMemoryTokenDispatcher> pmt) {
fbl::AutoLock guard(&lock_);
DEBUG_ASSERT(pmt->dll_pmt_.InContainer());
quarantine_.push_back(fbl::move(pmt));
if (zero_handles_) {
// If we quarantine when at zero handles, this PMT will be leaked. See
// the comment in on_zero_handles().
PrintQuarantineWarningLocked();
}
}
void BusTransactionInitiatorDispatcher::PrintQuarantineWarningLocked() {
uint64_t leaked_pages = 0;
size_t num_entries = 0;
for (const auto& pmt : quarantine_) {
leaked_pages += pmt.size() / PAGE_SIZE;
num_entries++;
}
printf("Bus Transaction Initiator 0x%lx has leaked %" PRIu64 " pages in %zu VMOs\n",
bti_id_, leaked_pages, num_entries);
}