blob: 1fc857bcd94f195a451cb2102f0ecb653f2554d5 [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
#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_