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