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 <fbl/intrusive_double_list.h>
10 #include <fbl/macros.h>
11 #include <ktl/unique_ptr.h>
12 #include <fbl/vector.h>
13 #include <region-alloc/region-alloc.h>
14 #include <vm/vm_object.h>
15 
16 #include "hw.h"
17 #include "second_level_pt.h"
18 
19 namespace intel_iommu {
20 
21 class IommuImpl;
22 
23 class DeviceContext : public fbl::DoublyLinkedListable<ktl::unique_ptr<DeviceContext>> {
24 public:
25     ~DeviceContext();
26 
27     // Create a new DeviceContext representing the given BDF.  It is a fatal error
28     // to try to create a context for a BDF that already has one.
29     static zx_status_t Create(ds::Bdf bdf, uint32_t domain_id, IommuImpl* parent,
30                               volatile ds::ExtendedContextEntry* context_entry,
31                               ktl::unique_ptr<DeviceContext>* device);
32     static zx_status_t Create(ds::Bdf bdf, uint32_t domain_id, IommuImpl* parent,
33                               volatile ds::ContextEntry* context_entry,
34                               ktl::unique_ptr<DeviceContext>* device);
35 
36     // Check if this DeviceContext is for the given BDF
is_bdf(ds::Bdf bdf)37     bool is_bdf(ds::Bdf bdf) const {
38         return bdf_ == bdf;
39     }
40 
domain_id()41     uint32_t domain_id() const { return domain_id_; }
42 
43     uint64_t minimum_contiguity() const;
44     uint64_t aspace_size() const;
45 
46     // Use the second-level translation table to map the host pages in the given
47     // range on |vmo| to the guest's address |*virt_paddr|.  |size| is in bytes.
48     // |mapped_len| may be larger than |size|, if |size| was not page-aligned.
49     //
50     // If |map_contiguous| is false, this function may return a partial mapping,
51     // in which case |mapped_len| will indicate how many bytes were actually mapped.
52     //
53     // If |map_contiguous| is true, this function will never return a partial
54     // mapping, and |mapped_len| should be equal to |size|.
55     zx_status_t SecondLevelMap(const fbl::RefPtr<VmObject>& vmo,
56                                uint64_t offset, size_t size, uint32_t perms,
57                                bool map_contiguous, paddr_t* virt_paddr, size_t* mapped_len);
58     zx_status_t SecondLevelUnmap(paddr_t virt_paddr, size_t size);
59 
60     // Use the second-level translation table to identity-map the given range of
61     // host pages.
62     zx_status_t SecondLevelMapIdentity(paddr_t base, size_t size, uint32_t perms);
63 
64 private:
65     DeviceContext(ds::Bdf bdf, uint32_t domain_id, IommuImpl* parent,
66                   volatile ds::ExtendedContextEntry* context_entry);
67     DeviceContext(ds::Bdf bdf, uint32_t domain_id, IommuImpl* parent,
68                   volatile ds::ContextEntry* context_entry);
69 
70     DISALLOW_COPY_ASSIGN_AND_MOVE(DeviceContext);
71 
72     // Shared initialization code for the two public Create() methods
73     zx_status_t InitCommon();
74 
75     // Map a VMO which may consist of discontiguous physical pages. If
76     // |map_contiguous| is true, this must either map the whole requested range
77     // contiguously, or fail. If |map_contiguous| is false, it may return
78     // success with a partial mapping.
79     zx_status_t SecondLevelMapDiscontiguous(const fbl::RefPtr<VmObject>& vmo,
80                                             uint64_t offset, size_t size, uint flags,
81                                             bool map_contiguous, paddr_t* virt_paddr,
82                                             size_t* mapped_len);
83 
84     // Map a VMO which consists of contiguous physical pages. Currently we assume
85     // that all contiguous VMOs should be mapped as a contiguous range, so this
86     // function will not return a partial mapping.
87     zx_status_t SecondLevelMapContiguous(const fbl::RefPtr<VmObject>& vmo,
88                                          uint64_t offset, size_t size, uint flags,
89                                          paddr_t* virt_paddr, size_t* mapped_len);
90 
91     IommuImpl* const parent_;
92     union {
93         volatile ds::ExtendedContextEntry* const extended_context_entry_;
94         volatile ds::ContextEntry* const context_entry_;
95     };
96 
97     // Page tables used for translating requests-without-PASID and for nested
98     // translation of requests-with-PASID.
99     SecondLevelPageTable second_level_pt_;
100     RegionAllocator region_alloc_;
101     // TODO(ZX-3210) Use a better data structure for these.  If the
102     // region nodes were intrusive, we wouldn't need to have a
103     // resizable array for this and we could have cheaper removal.  We
104     // can fix this up when it's a problem though.
105     //
106     fbl::Vector<RegionAllocator::Region::UPtr> allocated_regions_;
107 
108     const ds::Bdf bdf_;
109     const bool extended_;
110     const uint32_t domain_id_;
111 };
112 
113 } // namespace intel_iommu
114