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 #pragma once
8 
9 #include <dev/iommu.h>
10 #include <fbl/canary.h>
11 #include <fbl/mutex.h>
12 #include <kernel/lockdep.h>
13 #include <object/dispatcher.h>
14 #include <object/pinned_memory_token_dispatcher.h>
15 
16 #include <sys/types.h>
17 
18 class Iommu;
19 
20 class BusTransactionInitiatorDispatcher final :
21     public SoloDispatcher<BusTransactionInitiatorDispatcher, ZX_DEFAULT_BTI_RIGHTS> {
22 public:
23     static zx_status_t Create(fbl::RefPtr<Iommu> iommu, uint64_t bti_id,
24                               fbl::RefPtr<Dispatcher>* dispatcher, zx_rights_t* rights);
25 
26     ~BusTransactionInitiatorDispatcher() final;
get_type()27     zx_obj_type_t get_type() const final { return ZX_OBJ_TYPE_BTI; }
28 
29     // Pins the given VMO range and returns an PinnedMemoryTokenDispatcher
30     // representing the pinned range.
31     //
32     // |mapped_addrs_count| must be either
33     // 1) If |compress_results|, |size|/|minimum_contiguity()|, rounded up, in which
34     // case each returned address represents a run of |minimum_contiguity()| bytes (with
35     // the exception of the last which may be short)
36     // 2) Otherwise, |size|/|PAGE_SIZE|, in which case each returned address represents a
37     // single page.
38     //
39     // Returns ZX_ERR_INVALID_ARGS if |offset| or |size| are not PAGE_SIZE aligned.
40     // Returns ZX_ERR_INVALID_ARGS if |perms| is not suitable to pass to the Iommu::Map() interface.
41     // Returns ZX_ERR_INVALID_ARGS if |mapped_addrs_count| is not exactly the
42     //   value described above.
43     zx_status_t Pin(fbl::RefPtr<VmObject> vmo, uint64_t offset, uint64_t size, uint32_t perms,
44                     fbl::RefPtr<Dispatcher>* pmt, zx_rights_t* rights);
45 
46     // Releases all quarantined PMTs.  The memory pins are released and the VMO
47     // references are dropped, so the underlying VMOs may be immediately destroyed, and the
48     // underlying physical memory may be reallocated.
49     void ReleaseQuarantine();
50 
51     void on_zero_handles() final;
52 
iommu()53     fbl::RefPtr<Iommu> iommu() const { return iommu_; }
bti_id()54     uint64_t bti_id() const { return bti_id_; }
55 
56     // Pin will always be able to return addresses that are contiguous for at
57     // least this many bytes.  E.g. if this returns 1MB, then a call to Pin()
58     // with a size of 2MB will return at most two physically-contiguous runs.  If the size
59     // were 2.5MB, it will return at most three physically-contiguous runs.
minimum_contiguity()60     uint64_t minimum_contiguity() const { return iommu_->minimum_contiguity(bti_id_); }
61 
62     // The number of bytes in the address space (UINT64_MAX if 2^64).
aspace_size()63     uint64_t aspace_size() const { return iommu_->aspace_size(bti_id_); }
64 
65 protected:
66     friend PinnedMemoryTokenDispatcher;
67 
68     // Used to register a PMT pointer during PMT construction
69     void AddPmoLocked(PinnedMemoryTokenDispatcher* pmt) TA_REQ(get_lock());
70     // Used to unregister a PMT pointer during PMT destruction
71     void RemovePmo(PinnedMemoryTokenDispatcher* pmt);
72 
73     // Remove |pmt| from pinned_memory_ and append it to the quarantine_ list.
74     // This will prevent its underlying VMO from being unpinned until the
75     // quarantine is cleared.
76     void Quarantine(fbl::RefPtr<PinnedMemoryTokenDispatcher> pmt) TA_EXCL(get_lock());
77 
78 private:
79     BusTransactionInitiatorDispatcher(fbl::RefPtr<Iommu> iommu, uint64_t bti_id);
80     void PrintQuarantineWarningLocked() TA_REQ(get_lock());
81 
82     fbl::Canary<fbl::magic("BTID")> canary_;
83 
84     const fbl::RefPtr<Iommu> iommu_;
85     const uint64_t bti_id_;
86 
87     using PmoList = fbl::DoublyLinkedList<PinnedMemoryTokenDispatcher*,
88           PinnedMemoryTokenDispatcher::PinnedMemoryTokenListTraits>;
89     PmoList pinned_memory_ TA_GUARDED(get_lock());
90 
91     using QuarantineList = fbl::DoublyLinkedList<fbl::RefPtr<PinnedMemoryTokenDispatcher>,
92           PinnedMemoryTokenDispatcher::QuarantineListTraits>;
93     QuarantineList quarantine_ TA_GUARDED(get_lock());
94 
95     bool zero_handles_ TA_GUARDED(get_lock());
96 };
97