| // 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 |
| |
| #ifndef ZIRCON_KERNEL_DEV_IOMMU_INTEL_IOMMU_IMPL_H_ |
| #define ZIRCON_KERNEL_DEV_IOMMU_INTEL_IOMMU_IMPL_H_ |
| |
| #include <bits.h> |
| #include <zircon/syscalls/iommu.h> |
| |
| #include <dev/interrupt.h> |
| #include <dev/iommu.h> |
| #include <fbl/intrusive_double_list.h> |
| #include <fbl/macros.h> |
| #include <hwreg/mmio.h> |
| #include <kernel/lockdep.h> |
| #include <kernel/mutex.h> |
| |
| #include "domain_allocator.h" |
| #include "hw.h" |
| #include "iommu_page.h" |
| |
| class VmMapping; |
| |
| namespace intel_iommu { |
| |
| class ContextTableState; |
| class DeviceContext; |
| |
| class IommuImpl final : public Iommu { |
| public: |
| static zx_status_t Create(ktl::unique_ptr<const uint8_t[]> desc, size_t desc_len, |
| fbl::RefPtr<Iommu>* out); |
| |
| bool IsValidBusTxnId(uint64_t bus_txn_id) const final; |
| |
| zx_status_t Map(uint64_t bus_txn_id, const fbl::RefPtr<VmObject>& vmo, uint64_t offset, |
| size_t size, uint32_t perms, dev_vaddr_t* vaddr, size_t* mapped_len) final; |
| zx_status_t MapContiguous(uint64_t bus_txn_id, const fbl::RefPtr<VmObject>& vmo, uint64_t offset, |
| size_t size, uint32_t perms, dev_vaddr_t* vaddr, |
| size_t* mapped_len) final; |
| zx_status_t Unmap(uint64_t bus_txn_id, dev_vaddr_t vaddr, size_t size) final; |
| |
| zx_status_t ClearMappingsForBusTxnId(uint64_t bus_txn_id) final; |
| |
| uint64_t minimum_contiguity(uint64_t bus_txn_id) final; |
| uint64_t aspace_size(uint64_t bus_txn_id) final; |
| |
| ~IommuImpl() final; |
| |
| // TODO(teisenbe): These should be const, but need to teach the register |
| // library about constness |
| reg::Capability* caps() { return &caps_; } |
| reg::ExtendedCapability* extended_caps() { return &extended_caps_; } |
| |
| // Invalidate all context cache entries |
| void InvalidateContextCacheGlobal(); |
| // Invalidate all context cache entries that are in the specified domain |
| void InvalidateContextCacheDomain(uint32_t domain_id); |
| |
| // Invalidate all IOTLB entries for all domains |
| void InvalidateIotlbGlobal(); |
| // Invalidate all IOTLB entries for the specified domain |
| void InvalidateIotlbDomainAll(uint32_t domain_id); |
| void InvalidateIotlbDomainAllLocked(uint32_t domain_id) TA_REQ(lock_); |
| |
| // Invalidate the IOTLB entries for the specified translations. |
| // |pages_pow2| indicates how many pages should be invalidated (calculated |
| // as 2^|pages_pow2|). |
| void InvalidateIotlbPageLocked(uint32_t domain_id, dev_vaddr_t vaddr, uint pages_pow2) |
| TA_REQ(lock_); |
| |
| private: |
| DISALLOW_COPY_ASSIGN_AND_MOVE(IommuImpl); |
| IommuImpl(volatile void* register_base, ktl::unique_ptr<const uint8_t[]> desc, size_t desc_len); |
| |
| static ds::Bdf decode_bus_txn_id(uint64_t bus_txn_id) { |
| ds::Bdf bdf; |
| bdf.set_bus(static_cast<uint16_t>(BITS_SHIFT(bus_txn_id, 15, 8))); |
| bdf.set_dev(static_cast<uint16_t>(BITS_SHIFT(bus_txn_id, 7, 3))); |
| bdf.set_func(static_cast<uint16_t>(BITS_SHIFT(bus_txn_id, 2, 0))); |
| return bdf; |
| } |
| |
| static zx_status_t ValidateIommuDesc(const ktl::unique_ptr<const uint8_t[]>& desc, |
| size_t desc_len); |
| |
| // Set up initial root structures and enable translation |
| zx_status_t Initialize(); |
| |
| // Context cache invalidation |
| void InvalidateContextCacheGlobalLocked() TA_REQ(lock_); |
| void InvalidateContextCacheDomainLocked(uint32_t domain_id) TA_REQ(lock_); |
| |
| // IOTLB invalidation |
| void InvalidateIotlbGlobalLocked() TA_REQ(lock_); |
| |
| zx_status_t SetRootTablePointerLocked(paddr_t pa) TA_REQ(lock_); |
| zx_status_t SetTranslationEnableLocked(bool enabled, zx_time_t deadline) TA_REQ(lock_); |
| zx_status_t ConfigureFaultEventInterruptLocked() TA_REQ(lock_); |
| |
| // Process Reserved Memory Mapping Regions and set them up as pass-through. |
| zx_status_t EnableBiosReservedMappingsLocked() TA_REQ(lock_); |
| |
| void DisableFaultsLocked() TA_REQ(lock_); |
| static interrupt_eoi FaultHandler(void* ctx); |
| zx_status_t GetOrCreateContextTableLocked(ds::Bdf bdf, ContextTableState** tbl) TA_REQ(lock_); |
| zx_status_t GetOrCreateDeviceContextLocked(ds::Bdf bdf, DeviceContext** context) TA_REQ(lock_); |
| |
| // Utility for waiting until a register field changes to a value, timing out |
| // if the deadline elapses. If deadline is ZX_TIME_INFINITE, then will never time |
| // out. Can only return NO_ERROR and ERR_TIMED_OUT. |
| template <class RegType> |
| zx_status_t WaitForValueLocked(RegType* reg, |
| typename RegType::ValueType (RegType::*getter)() const, |
| typename RegType::ValueType value, zx_time_t deadline) |
| TA_REQ(lock_); |
| |
| volatile ds::RootTable* root_table() const TA_REQ(lock_) { |
| return reinterpret_cast<volatile ds::RootTable*>(root_table_page_.vaddr()); |
| } |
| |
| DECLARE_MUTEX(IommuImpl) lock_; |
| |
| // Descriptor of this hardware unit |
| ktl::unique_ptr<const uint8_t[]> desc_; |
| size_t desc_len_; |
| |
| // Location of the memory-mapped hardware register bank. |
| hwreg::RegisterMmio mmio_ TA_GUARDED(lock_); |
| |
| // Interrupt allocation |
| msi_block_t irq_block_ TA_GUARDED(lock_); |
| |
| // In-memory root table |
| IommuPage root_table_page_ TA_GUARDED(lock_); |
| // List of allocated context tables |
| fbl::DoublyLinkedList<ktl::unique_ptr<ContextTableState>> context_tables_ TA_GUARDED(lock_); |
| |
| DomainAllocator domain_allocator_ TA_GUARDED(lock_); |
| |
| // A mask with bits set for each usable bit in an address with the largest allowed |
| // address width. E.g., if the largest allowed width is 48-bit, |
| // max_guest_addr_mask will be 0xffff_ffff_ffff. |
| uint64_t max_guest_addr_mask_ TA_GUARDED(lock_) = 0; |
| uint32_t valid_pasid_mask_ TA_GUARDED(lock_) = 0; |
| uint32_t iotlb_reg_offset_ TA_GUARDED(lock_) = 0; |
| uint32_t fault_recording_reg_offset_ TA_GUARDED(lock_) = 0; |
| uint32_t num_fault_recording_reg_ TA_GUARDED(lock_) = 0; |
| bool supports_extended_context_ TA_GUARDED(lock_) = 0; |
| |
| reg::Capability caps_; |
| reg::ExtendedCapability extended_caps_; |
| }; |
| |
| } // namespace intel_iommu |
| |
| #endif // ZIRCON_KERNEL_DEV_IOMMU_INTEL_IOMMU_IMPL_H_ |