1// Copyright 2017 The Fuchsia Authors 2// 3// Use of this source code is governed by a MIT-style 4// license that can be found in the LICENSE file or at 5// https://opensource.org/licenses/MIT 6 7#include <object/bus_transaction_initiator_dispatcher.h> 8 9#include <dev/iommu.h> 10#include <err.h> 11#include <vm/vm_object.h> 12#include <zircon/rights.h> 13#include <zxcpp/new.h> 14 15zx_status_t BusTransactionInitiatorDispatcher::Create(fbl::RefPtr<Iommu> iommu, uint64_t bti_id, 16 fbl::RefPtr<Dispatcher>* dispatcher, 17 zx_rights_t* rights) { 18 19 if (!iommu->IsValidBusTxnId(bti_id)) { 20 return ZX_ERR_INVALID_ARGS; 21 } 22 23 fbl::AllocChecker ac; 24 auto disp = new (&ac) BusTransactionInitiatorDispatcher(fbl::move(iommu), bti_id); 25 if (!ac.check()) { 26 return ZX_ERR_NO_MEMORY; 27 } 28 29 *rights = ZX_DEFAULT_BTI_RIGHTS; 30 *dispatcher = fbl::AdoptRef<Dispatcher>(disp); 31 return ZX_OK; 32} 33 34BusTransactionInitiatorDispatcher::BusTransactionInitiatorDispatcher(fbl::RefPtr<Iommu> iommu, 35 uint64_t bti_id) 36 : iommu_(fbl::move(iommu)), bti_id_(bti_id), zero_handles_(false) {} 37 38BusTransactionInitiatorDispatcher::~BusTransactionInitiatorDispatcher() { 39 DEBUG_ASSERT(pinned_memory_.is_empty()); 40} 41 42zx_status_t BusTransactionInitiatorDispatcher::Pin(fbl::RefPtr<VmObject> vmo, uint64_t offset, 43 uint64_t size, uint32_t perms, 44 fbl::RefPtr<Dispatcher>* pmt, 45 zx_rights_t* pmt_rights) { 46 47 DEBUG_ASSERT(IS_PAGE_ALIGNED(offset)); 48 DEBUG_ASSERT(IS_PAGE_ALIGNED(size)); 49 50 if (size == 0) { 51 return ZX_ERR_INVALID_ARGS; 52 } 53 54 Guard<fbl::Mutex> guard{get_lock()}; 55 56 if (zero_handles_) { 57 return ZX_ERR_BAD_STATE; 58 } 59 60 return PinnedMemoryTokenDispatcher::Create(fbl::WrapRefPtr(this), fbl::move(vmo), 61 offset, size, perms, pmt, pmt_rights); 62} 63 64void BusTransactionInitiatorDispatcher::ReleaseQuarantine() { 65 QuarantineList tmp; 66 67 // The PMT dtor will call RemovePmo, which will reacquire this BTI's lock. 68 // To avoid deadlock, drop the lock before letting the quarantined PMTs go. 69 { 70 Guard<fbl::Mutex> guard{get_lock()}; 71 quarantine_.swap(tmp); 72 } 73} 74 75void BusTransactionInitiatorDispatcher::on_zero_handles() { 76 Guard<fbl::Mutex> guard{get_lock()}; 77 // Prevent new pinning from happening. The Dispatcher will stick around 78 // until all of the PMTs are closed. 79 zero_handles_ = true; 80 81 // Do not clear out the quarantine list. PMTs hold a reference to the BTI 82 // and the BTI holds a reference to each quarantined PMT. We intentionally 83 // leak the BTI, all quarantined PMTs, and their underlying VMOs. We could 84 // get away with freeing the BTI and the PMTs, but for safety we must leak 85 // at least the pinned parts of the VMOs, since we have no assurance that 86 // hardware is not still reading/writing to it. 87 if (!quarantine_.is_empty()) { 88 PrintQuarantineWarningLocked(); 89 } 90} 91 92void BusTransactionInitiatorDispatcher::AddPmoLocked(PinnedMemoryTokenDispatcher* pmt) { 93 DEBUG_ASSERT(!pmt->dll_pmt_.InContainer()); 94 pinned_memory_.push_back(pmt); 95} 96 97void BusTransactionInitiatorDispatcher::RemovePmo(PinnedMemoryTokenDispatcher* pmt) { 98 Guard<fbl::Mutex> guard{get_lock()}; 99 DEBUG_ASSERT(pmt->dll_pmt_.InContainer()); 100 pinned_memory_.erase(*pmt); 101} 102 103void BusTransactionInitiatorDispatcher::Quarantine(fbl::RefPtr<PinnedMemoryTokenDispatcher> pmt) { 104 Guard<fbl::Mutex> guard{get_lock()}; 105 106 DEBUG_ASSERT(pmt->dll_pmt_.InContainer()); 107 quarantine_.push_back(fbl::move(pmt)); 108 109 if (zero_handles_) { 110 // If we quarantine when at zero handles, this PMT will be leaked. See 111 // the comment in on_zero_handles(). 112 PrintQuarantineWarningLocked(); 113 } 114} 115 116void BusTransactionInitiatorDispatcher::PrintQuarantineWarningLocked() { 117 uint64_t leaked_pages = 0; 118 size_t num_entries = 0; 119 for (const auto& pmt : quarantine_) { 120 leaked_pages += pmt.size() / PAGE_SIZE; 121 num_entries++; 122 } 123 printf("Bus Transaction Initiator 0x%lx has leaked %" PRIu64 " pages in %zu VMOs\n", 124 bti_id_, leaked_pages, num_entries); 125} 126