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 <fbl/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
19namespace intel_iommu {
20
21class IommuImpl;
22
23class DeviceContext : public fbl::DoublyLinkedListable<fbl::unique_ptr<DeviceContext>> {
24public:
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                              fbl::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                              fbl::unique_ptr<DeviceContext>* device);
35
36    // Check if this DeviceContext is for the given BDF
37    bool is_bdf(ds::Bdf bdf) const {
38        return bdf_ == bdf;
39    }
40
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
64private:
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(teisenbe): Use a better data structure for these.  If the region
102    // nodes were intrusive, we wouldn't need to have a resizable array for this
103    // and we could have cheaper removal.  We can fix this up when it's a
104    // problem though.
105    fbl::Vector<fbl::unique_ptr<const RegionAllocator::Region>> allocated_regions_;
106
107    const ds::Bdf bdf_;
108    const bool extended_;
109    const uint32_t domain_id_;
110};
111
112} // namespace intel_iommu
113