blob: 8fe52ead16e899ec92ca1face51d46ce1cedab87 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include "context_table_state.h"
#include <ktl/unique_ptr.h>
#include <ktl/move.h>
#include <new>
#include "device_context.h"
#include "hw.h"
#include "iommu_impl.h"
namespace intel_iommu {
ContextTableState::ContextTableState(uint8_t bus, bool extended, bool upper,
IommuImpl* parent, volatile ds::RootEntrySubentry* root_entry,
IommuPage page)
: parent_(parent), root_entry_(root_entry), page_(ktl::move(page)),
bus_(bus), extended_(extended), upper_(upper) {
}
ContextTableState::~ContextTableState() {
ds::RootEntrySubentry entry;
entry.ReadFrom(root_entry_);
entry.set_present(0);
entry.WriteTo(root_entry_);
// When modifying a present (extended) root entry, we must serially
// invalidate the context-cache, the PASID-cache, then the IOTLB (see
// 6.2.2.1 "Context-Entry Programming Considerations" in the VT-d spec,
// Oct 2014 rev).
parent_->InvalidateContextCacheGlobal();
// TODO(teisenbe): Invalidate the PASID cache once we support those
parent_->InvalidateIotlbGlobal();
}
zx_status_t ContextTableState::Create(uint8_t bus, bool extended, bool upper,
IommuImpl* parent, volatile ds::RootEntrySubentry* root_entry,
ktl::unique_ptr<ContextTableState>* table) {
ds::RootEntrySubentry entry;
entry.ReadFrom(root_entry);
DEBUG_ASSERT(!entry.present());
IommuPage page;
zx_status_t status = IommuPage::AllocatePage(&page);
if (status != ZX_OK) {
return status;
}
fbl::AllocChecker ac;
ktl::unique_ptr<ContextTableState> tbl(new (&ac) ContextTableState(bus, extended, upper,
parent, root_entry,
ktl::move(page)));
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
entry.set_present(1);
entry.set_context_table(tbl->page_.paddr() >> 12);
entry.WriteTo(root_entry);
*table = ktl::move(tbl);
return ZX_OK;
}
zx_status_t ContextTableState::CreateDeviceContext(ds::Bdf bdf, uint32_t domain_id,
DeviceContext** context) {
DEBUG_ASSERT(bus_ == bdf.bus());
ktl::unique_ptr<DeviceContext> dev;
zx_status_t status;
if (extended_) {
DEBUG_ASSERT(upper_ == (bdf.dev() >= 16));
volatile ds::ExtendedContextTable* tbl = extended_table();
volatile ds::ExtendedContextEntry* entry = &tbl->entry[bdf.packed_dev_and_func() & 0x7f];
status = DeviceContext::Create(bdf, domain_id, parent_, entry, &dev);
} else {
volatile ds::ContextTable* tbl = table();
volatile ds::ContextEntry* entry = &tbl->entry[bdf.packed_dev_and_func()];
status = DeviceContext::Create(bdf, domain_id, parent_, entry, &dev);
}
if (status != ZX_OK) {
return status;
}
*context = dev.get();
devices_.push_back(ktl::move(dev));
return ZX_OK;
}
zx_status_t ContextTableState::GetDeviceContext(ds::Bdf bdf, DeviceContext** context) {
for (auto& dev : devices_) {
if (dev.is_bdf(bdf)) {
*context = &dev;
return ZX_OK;
}
}
return ZX_ERR_NOT_FOUND;
}
} // namespace intel_iommu