forked from free1139/ziron
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbus_transaction_initiator_dispatcher.cpp
More file actions
131 lines (107 loc) · 4.54 KB
/
bus_transaction_initiator_dispatcher.cpp
File metadata and controls
131 lines (107 loc) · 4.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// 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/pinned_vm_object.h>
#include <vm/vm_object.h>
#include <zircon/rights.h>
#include <new>
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(ktl::move(iommu), bti_id);
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
*rights = default_rights();
*dispatcher = fbl::AdoptRef<Dispatcher>(disp);
return ZX_OK;
}
BusTransactionInitiatorDispatcher::BusTransactionInitiatorDispatcher(fbl::RefPtr<Iommu> iommu,
uint64_t bti_id)
: iommu_(ktl::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;
}
PinnedVmObject pinned_vmo;
zx_status_t status = PinnedVmObject::Create(vmo, offset, size, &pinned_vmo);
if (status != ZX_OK) {
return status;
}
Guard<fbl::Mutex> guard{get_lock()};
if (zero_handles_) {
return ZX_ERR_BAD_STATE;
}
return PinnedMemoryTokenDispatcher::Create(fbl::WrapRefPtr(this), ktl::move(pinned_vmo),
perms, pmt, pmt_rights);
}
void BusTransactionInitiatorDispatcher::ReleaseQuarantine() {
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.
{
Guard<fbl::Mutex> guard{get_lock()};
quarantine_.swap(tmp);
}
}
void BusTransactionInitiatorDispatcher::on_zero_handles() {
Guard<fbl::Mutex> guard{get_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) {
Guard<fbl::Mutex> guard{get_lock()};
DEBUG_ASSERT(pmt->dll_pmt_.InContainer());
pinned_memory_.erase(*pmt);
}
void BusTransactionInitiatorDispatcher::Quarantine(fbl::RefPtr<PinnedMemoryTokenDispatcher> pmt) {
Guard<fbl::Mutex> guard{get_lock()};
DEBUG_ASSERT(pmt->dll_pmt_.InContainer());
quarantine_.push_back(ktl::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);
}