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