1// Copyright 2018 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 <bits.h>
10#include <dev/interrupt.h>
11#include <dev/iommu.h>
12#include <fbl/intrusive_double_list.h>
13#include <fbl/macros.h>
14#include <fbl/mutex.h>
15#include <hwreg/mmio.h>
16#include <zircon/syscalls/iommu.h>
17
18#include "domain_allocator.h"
19#include "hw.h"
20#include "iommu_page.h"
21
22class VmMapping;
23
24namespace intel_iommu {
25
26class ContextTableState;
27class DeviceContext;
28
29class IommuImpl final : public Iommu {
30public:
31    static zx_status_t Create(fbl::unique_ptr<const uint8_t[]> desc, size_t desc_len,
32                              fbl::RefPtr<Iommu>* out);
33
34    bool IsValidBusTxnId(uint64_t bus_txn_id) const final;
35
36    zx_status_t Map(uint64_t bus_txn_id, const fbl::RefPtr<VmObject>& vmo,
37                    uint64_t offset, size_t size, uint32_t perms,
38                    dev_vaddr_t* vaddr, size_t* mapped_len) final;
39    zx_status_t MapContiguous(uint64_t bus_txn_id, const fbl::RefPtr<VmObject>& vmo,
40                              uint64_t offset, size_t size, uint32_t perms,
41                              dev_vaddr_t* vaddr, size_t* mapped_len) final;
42    zx_status_t Unmap(uint64_t bus_txn_id, dev_vaddr_t vaddr, size_t size) final;
43
44    zx_status_t ClearMappingsForBusTxnId(uint64_t bus_txn_id) final;
45
46    uint64_t minimum_contiguity(uint64_t bus_txn_id) final;
47    uint64_t aspace_size(uint64_t bus_txn_id) final;
48
49    ~IommuImpl() final;
50
51    // TODO(teisenbe): These should be const, but need to teach the register
52    // library about constness
53    reg::Capability* caps() { return &caps_; }
54    reg::ExtendedCapability* extended_caps() { return &extended_caps_; }
55
56    // Invalidate all context cache entries
57    void InvalidateContextCacheGlobal();
58    // Invalidate all context cache entries that are in the specified domain
59    void InvalidateContextCacheDomain(uint32_t domain_id);
60
61    // Invalidate all IOTLB entries for all domains
62    void InvalidateIotlbGlobal();
63    // Invalidate all IOTLB entries for the specified domain
64    void InvalidateIotlbDomainAll(uint32_t domain_id);
65    void InvalidateIotlbDomainAllLocked(uint32_t domain_id) TA_REQ(lock_);
66
67    // Invalidate the IOTLB entries for the specified translations.
68    // |pages_pow2| indicates how many pages should be invalidated (calculated
69    // as 2^|pages_pow2|).
70    void InvalidateIotlbPageLocked(uint32_t domain_id, dev_vaddr_t vaddr,
71                                   uint pages_pow2) TA_REQ(lock_);
72
73private:
74    DISALLOW_COPY_ASSIGN_AND_MOVE(IommuImpl);
75    IommuImpl(volatile void* register_base, fbl::unique_ptr<const uint8_t[]> desc,
76              size_t desc_len);
77
78    static ds::Bdf decode_bus_txn_id(uint64_t bus_txn_id) {
79        ds::Bdf bdf;
80        bdf.set_bus(static_cast<uint16_t>(BITS_SHIFT(bus_txn_id, 15, 8)));
81        bdf.set_dev(static_cast<uint16_t>(BITS_SHIFT(bus_txn_id, 7, 3)));
82        bdf.set_func(static_cast<uint16_t>(BITS_SHIFT(bus_txn_id, 2, 0)));
83        return bdf;
84    }
85
86    static zx_status_t ValidateIommuDesc(const fbl::unique_ptr<const uint8_t[]>& desc,
87                                         size_t desc_len);
88
89    // Set up initial root structures and enable translation
90    zx_status_t Initialize();
91
92    // Context cache invalidation
93    void InvalidateContextCacheGlobalLocked() TA_REQ(lock_);
94    void InvalidateContextCacheDomainLocked(uint32_t domain_id) TA_REQ(lock_);
95
96    // IOTLB invalidation
97    void InvalidateIotlbGlobalLocked() TA_REQ(lock_);
98
99    zx_status_t SetRootTablePointerLocked(paddr_t pa) TA_REQ(lock_);
100    zx_status_t SetTranslationEnableLocked(bool enabled, zx_time_t deadline) TA_REQ(lock_);
101    zx_status_t ConfigureFaultEventInterruptLocked() TA_REQ(lock_);
102
103    // Process Reserved Memory Mapping Regions and set them up as pass-through.
104    zx_status_t EnableBiosReservedMappingsLocked() TA_REQ(lock_);
105
106    void DisableFaultsLocked() TA_REQ(lock_);
107    static void FaultHandler(void* ctx);
108    zx_status_t GetOrCreateContextTableLocked(ds::Bdf bdf, ContextTableState** tbl) TA_REQ(lock_);
109    zx_status_t GetOrCreateDeviceContextLocked(ds::Bdf bdf, DeviceContext** context) TA_REQ(lock_);
110
111    // Utility for waiting until a register field changes to a value, timing out
112    // if the deadline elapses.  If deadline is ZX_TIME_INFINITE, then will never time
113    // out.  Can only return NO_ERROR and ERR_TIMED_OUT.
114    template <class RegType>
115    zx_status_t WaitForValueLocked(RegType* reg,
116                                   typename RegType::ValueType (RegType::*getter)() const,
117                                   typename RegType::ValueType value,
118                                   zx_time_t deadline) TA_REQ(lock_);
119
120    volatile ds::RootTable* root_table() const TA_REQ(lock_) {
121        return reinterpret_cast<volatile ds::RootTable*>(root_table_page_.vaddr());
122    }
123
124    fbl::Mutex lock_;
125
126    // Descriptor of this hardware unit
127    fbl::unique_ptr<const uint8_t[]> desc_;
128    size_t desc_len_;
129
130    // Location of the memory-mapped hardware register bank.
131    hwreg::RegisterIo mmio_ TA_GUARDED(lock_);
132
133    // Interrupt allocation
134    msi_block_t irq_block_ TA_GUARDED(lock_);
135
136    // In-memory root table
137    IommuPage root_table_page_ TA_GUARDED(lock_);
138    // List of allocated context tables
139    fbl::DoublyLinkedList<fbl::unique_ptr<ContextTableState>> context_tables_ TA_GUARDED(lock_);
140
141    DomainAllocator domain_allocator_ TA_GUARDED(lock_);
142
143    // A mask with bits set for each usable bit in an address with the largest allowed
144    // address width.  E.g., if the largest allowed width is 48-bit,
145    // max_guest_addr_mask will be 0xffff_ffff_ffff.
146    uint64_t max_guest_addr_mask_ TA_GUARDED(lock_) = 0;
147    uint32_t valid_pasid_mask_ TA_GUARDED(lock_) = 0;
148    uint32_t iotlb_reg_offset_ TA_GUARDED(lock_) = 0;
149    uint32_t fault_recording_reg_offset_ TA_GUARDED(lock_) = 0;
150    uint32_t num_fault_recording_reg_ TA_GUARDED(lock_) = 0;
151    bool supports_extended_context_ TA_GUARDED(lock_) = 0;
152
153    reg::Capability caps_;
154    reg::ExtendedCapability extended_caps_;
155};
156
157} // namespace intel_iommu
158