// 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 #pragma once #include #include #include #include #include #include #include "hw.h" #include "second_level_pt.h" namespace intel_iommu { class IommuImpl; class DeviceContext : public fbl::DoublyLinkedListable> { public: ~DeviceContext(); // Create a new DeviceContext representing the given BDF. It is a fatal error // to try to create a context for a BDF that already has one. static zx_status_t Create(ds::Bdf bdf, uint32_t domain_id, IommuImpl* parent, volatile ds::ExtendedContextEntry* context_entry, fbl::unique_ptr* device); static zx_status_t Create(ds::Bdf bdf, uint32_t domain_id, IommuImpl* parent, volatile ds::ContextEntry* context_entry, fbl::unique_ptr* device); // Check if this DeviceContext is for the given BDF bool is_bdf(ds::Bdf bdf) const { return bdf_ == bdf; } uint32_t domain_id() const { return domain_id_; } uint64_t minimum_contiguity() const; uint64_t aspace_size() const; // Use the second-level translation table to map the host pages in the given // range on |vmo| to the guest's address |*virt_paddr|. |size| is in bytes. // |mapped_len| may be larger than |size|, if |size| was not page-aligned. // // If |map_contiguous| is false, this function may return a partial mapping, // in which case |mapped_len| will indicate how many bytes were actually mapped. // // If |map_contiguous| is true, this function will never return a partial // mapping, and |mapped_len| should be equal to |size|. zx_status_t SecondLevelMap(const fbl::RefPtr& vmo, uint64_t offset, size_t size, uint32_t perms, bool map_contiguous, paddr_t* virt_paddr, size_t* mapped_len); zx_status_t SecondLevelUnmap(paddr_t virt_paddr, size_t size); // Use the second-level translation table to identity-map the given range of // host pages. zx_status_t SecondLevelMapIdentity(paddr_t base, size_t size, uint32_t perms); private: DeviceContext(ds::Bdf bdf, uint32_t domain_id, IommuImpl* parent, volatile ds::ExtendedContextEntry* context_entry); DeviceContext(ds::Bdf bdf, uint32_t domain_id, IommuImpl* parent, volatile ds::ContextEntry* context_entry); DISALLOW_COPY_ASSIGN_AND_MOVE(DeviceContext); // Shared initialization code for the two public Create() methods zx_status_t InitCommon(); // Map a VMO which may consist of discontiguous physical pages. If // |map_contiguous| is true, this must either map the whole requested range // contiguously, or fail. If |map_contiguous| is false, it may return // success with a partial mapping. zx_status_t SecondLevelMapDiscontiguous(const fbl::RefPtr& vmo, uint64_t offset, size_t size, uint flags, bool map_contiguous, paddr_t* virt_paddr, size_t* mapped_len); // Map a VMO which consists of contiguous physical pages. Currently we assume // that all contiguous VMOs should be mapped as a contiguous range, so this // function will not return a partial mapping. zx_status_t SecondLevelMapContiguous(const fbl::RefPtr& vmo, uint64_t offset, size_t size, uint flags, paddr_t* virt_paddr, size_t* mapped_len); IommuImpl* const parent_; union { volatile ds::ExtendedContextEntry* const extended_context_entry_; volatile ds::ContextEntry* const context_entry_; }; // Page tables used for translating requests-without-PASID and for nested // translation of requests-with-PASID. SecondLevelPageTable second_level_pt_; RegionAllocator region_alloc_; // TODO(teisenbe): Use a better data structure for these. If the region // nodes were intrusive, we wouldn't need to have a resizable array for this // and we could have cheaper removal. We can fix this up when it's a // problem though. fbl::Vector> allocated_regions_; const ds::Bdf bdf_; const bool extended_; const uint32_t domain_id_; }; } // namespace intel_iommu