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 #include "context_table_state.h"
8 
9 #include <ktl/unique_ptr.h>
10 #include <ktl/move.h>
11 #include <new>
12 
13 #include "device_context.h"
14 #include "hw.h"
15 #include "iommu_impl.h"
16 
17 namespace intel_iommu {
18 
ContextTableState(uint8_t bus,bool extended,bool upper,IommuImpl * parent,volatile ds::RootEntrySubentry * root_entry,IommuPage page)19 ContextTableState::ContextTableState(uint8_t bus, bool extended, bool upper,
20                                      IommuImpl* parent, volatile ds::RootEntrySubentry* root_entry,
21                                      IommuPage page)
22         : parent_(parent), root_entry_(root_entry), page_(ktl::move(page)),
23           bus_(bus), extended_(extended), upper_(upper) {
24 }
25 
~ContextTableState()26 ContextTableState::~ContextTableState() {
27     ds::RootEntrySubentry entry;
28     entry.ReadFrom(root_entry_);
29     entry.set_present(0);
30     entry.WriteTo(root_entry_);
31 
32     // When modifying a present (extended) root entry, we must serially
33     // invalidate the context-cache, the PASID-cache, then the IOTLB (see
34     // 6.2.2.1 "Context-Entry Programming Considerations" in the VT-d spec,
35     // Oct 2014 rev).
36     parent_->InvalidateContextCacheGlobal();
37     // TODO(teisenbe): Invalidate the PASID cache once we support those
38     parent_->InvalidateIotlbGlobal();
39 }
40 
Create(uint8_t bus,bool extended,bool upper,IommuImpl * parent,volatile ds::RootEntrySubentry * root_entry,ktl::unique_ptr<ContextTableState> * table)41 zx_status_t ContextTableState::Create(uint8_t bus, bool extended, bool upper,
42                                       IommuImpl* parent, volatile ds::RootEntrySubentry* root_entry,
43                                       ktl::unique_ptr<ContextTableState>* table) {
44     ds::RootEntrySubentry entry;
45     entry.ReadFrom(root_entry);
46     DEBUG_ASSERT(!entry.present());
47 
48     IommuPage page;
49     zx_status_t status = IommuPage::AllocatePage(&page);
50     if (status != ZX_OK) {
51         return status;
52     }
53 
54     fbl::AllocChecker ac;
55     ktl::unique_ptr<ContextTableState> tbl(new (&ac) ContextTableState(bus, extended, upper,
56                                                                        parent, root_entry,
57                                                                        ktl::move(page)));
58     if (!ac.check()) {
59         return ZX_ERR_NO_MEMORY;
60     }
61 
62     entry.set_present(1);
63     entry.set_context_table(tbl->page_.paddr() >> 12);
64     entry.WriteTo(root_entry);
65 
66     *table = ktl::move(tbl);
67     return ZX_OK;
68 }
69 
CreateDeviceContext(ds::Bdf bdf,uint32_t domain_id,DeviceContext ** context)70 zx_status_t ContextTableState::CreateDeviceContext(ds::Bdf bdf, uint32_t domain_id,
71                                                    DeviceContext** context) {
72     DEBUG_ASSERT(bus_ == bdf.bus());
73 
74     ktl::unique_ptr<DeviceContext> dev;
75     zx_status_t status;
76     if (extended_) {
77         DEBUG_ASSERT(upper_ == (bdf.dev() >= 16));
78         volatile ds::ExtendedContextTable* tbl = extended_table();
79         volatile ds::ExtendedContextEntry* entry = &tbl->entry[bdf.packed_dev_and_func() & 0x7f];
80         status = DeviceContext::Create(bdf, domain_id, parent_, entry, &dev);
81     } else {
82         volatile ds::ContextTable* tbl = table();
83         volatile ds::ContextEntry* entry = &tbl->entry[bdf.packed_dev_and_func()];
84         status = DeviceContext::Create(bdf, domain_id, parent_, entry, &dev);
85     }
86     if (status != ZX_OK) {
87         return status;
88     }
89 
90     *context = dev.get();
91     devices_.push_back(ktl::move(dev));
92     return ZX_OK;
93 }
94 
GetDeviceContext(ds::Bdf bdf,DeviceContext ** context)95 zx_status_t ContextTableState::GetDeviceContext(ds::Bdf bdf, DeviceContext** context) {
96     for (auto& dev : devices_) {
97         if (dev.is_bdf(bdf)) {
98             *context = &dev;
99             return ZX_OK;
100         }
101     }
102     return ZX_ERR_NOT_FOUND;
103 }
104 
105 } // namespace intel_iommu
106