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 "second_level_pt.h" 8 9#include <arch/x86/mmu.h> 10 11#include "device_context.h" 12#include "iommu_impl.h" 13 14#define LOCAL_TRACE 0 15 16namespace { 17 18constexpr X86PageTableBase::PtFlags kSlptRead = 1u << 0; 19constexpr X86PageTableBase::PtFlags kSlptWrite = 1u << 1; 20constexpr X86PageTableBase::PtFlags kSlptExecute = 1u << 2; 21 22vaddr_t compute_vaddr_mask(PageTableLevel top_level) { 23 uint width; 24 switch (top_level) { 25 case PD_L: width = 30; break; 26 case PDP_L: width = 39; break; 27 case PML4_L: width = 48; break; 28 default: panic("Unsupported iommu width\n"); 29 } 30 31 // Valid vaddrs for mapping should be page-aligned and not larger than the 32 // width of the top level. 33 return ((1ull << width) - 1) & ~(PAGE_SIZE - 1); 34} 35 36} // namespace 37 38namespace intel_iommu { 39 40SecondLevelPageTable::SecondLevelPageTable(IommuImpl* iommu, DeviceContext* parent) 41 : iommu_(iommu), 42 parent_(parent), 43 needs_flushes_(!iommu->extended_caps()->page_walk_coherency()), 44 supports_2mb_(iommu->caps()->supports_second_level_2mb_page()), 45 supports_1gb_(iommu->caps()->supports_second_level_1gb_page()), 46 initialized_(false) { 47} 48 49SecondLevelPageTable::~SecondLevelPageTable() { 50 DEBUG_ASSERT(!initialized_); 51} 52 53zx_status_t SecondLevelPageTable::Init(PageTableLevel top_level) { 54 DEBUG_ASSERT(!initialized_); 55 56 top_level_ = top_level; 57 valid_vaddr_mask_ = compute_vaddr_mask(top_level); 58 zx_status_t status = X86PageTableBase::Init(nullptr); 59 if (status != ZX_OK) { 60 return status; 61 } 62 initialized_ = true; 63 return ZX_OK; 64} 65 66void SecondLevelPageTable::Destroy() { 67 if (!initialized_) { 68 return; 69 } 70 71 size_t size = valid_vaddr_mask_ + PAGE_SIZE; 72 initialized_ = false; 73 X86PageTableBase::Destroy(0, size); 74} 75 76bool SecondLevelPageTable::allowed_flags(uint flags) { 77 constexpr uint kSupportedFlags = 78 ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE | ARCH_MMU_FLAG_PERM_EXECUTE; 79 if (flags & ~kSupportedFlags) { 80 return false; 81 } 82 return true; 83} 84 85// Validation for host physical addresses 86bool SecondLevelPageTable::check_paddr(paddr_t paddr) { 87 return x86_mmu_check_paddr(paddr); 88} 89 90// Validation for virtual physical addresses 91bool SecondLevelPageTable::check_vaddr(vaddr_t vaddr) { 92 return !(vaddr & ~valid_vaddr_mask_); 93} 94 95bool SecondLevelPageTable::supports_page_size(PageTableLevel level) { 96 switch (level) { 97 case PT_L: return true; 98 case PD_L: return supports_2mb_; 99 case PDP_L: return supports_1gb_; 100 default: return false; 101 } 102} 103 104X86PageTableBase::IntermediatePtFlags SecondLevelPageTable::intermediate_flags() { 105 return kSlptRead | kSlptWrite | kSlptExecute; 106} 107 108X86PageTableBase::PtFlags SecondLevelPageTable::terminal_flags(PageTableLevel level, 109 uint flags) { 110 X86PageTableBase::PtFlags terminal_flags = 0; 111 112 if (flags & ARCH_MMU_FLAG_PERM_READ) { 113 terminal_flags |= kSlptRead; 114 } 115 if (flags & ARCH_MMU_FLAG_PERM_WRITE) { 116 terminal_flags |= kSlptWrite; 117 } 118 if (flags & ARCH_MMU_FLAG_PERM_EXECUTE) { 119 terminal_flags |= kSlptExecute; 120 } 121 122 return terminal_flags; 123} 124 125X86PageTableBase::PtFlags SecondLevelPageTable::split_flags(PageTableLevel level, 126 PtFlags flags) { 127 // We don't need to relocate any flags on split 128 return flags; 129} 130 131// We disable thread safety analysis here, since the lock being held is being 132// held across the MMU operations, but goes through code that is not aware of 133// the lock. 134void SecondLevelPageTable::TlbInvalidate(PendingTlbInvalidation* pending) 135 TA_NO_THREAD_SAFETY_ANALYSIS { 136 137 DEBUG_ASSERT(!pending->contains_global); 138 139 if (pending->full_shootdown) { 140 iommu_->InvalidateIotlbDomainAllLocked(parent_->domain_id()); 141 pending->clear(); 142 return; 143 } 144 145 constexpr uint kBitsPerLevel = 9; 146 for (uint i = 0; i < pending->count; ++i) { 147 const auto& item = pending->item[i]; 148 uint address_mask = kBitsPerLevel * static_cast<uint>(item.page_level()); 149 150 if (!item.is_terminal()) { 151 // If this is non-terminal, force the paging-structure cache to be 152 // cleared for this address still, even though a terminal mapping hasn't 153 // been changed. 154 // TODO(teisenbe): Not completely sure this is necessary. Including for 155 // now out of caution. 156 address_mask = 0; 157 } 158 iommu_->InvalidateIotlbPageLocked(parent_->domain_id(), item.addr(), address_mask); 159 } 160 pending->clear(); 161} 162 163uint SecondLevelPageTable::pt_flags_to_mmu_flags(PtFlags flags, PageTableLevel level) { 164 uint mmu_flags = 0; 165 166 if (flags & kSlptRead) { 167 mmu_flags |= ARCH_MMU_FLAG_PERM_READ; 168 } 169 if (flags & kSlptWrite) { 170 mmu_flags |= ARCH_MMU_FLAG_PERM_WRITE; 171 } 172 if (flags & kSlptExecute) { 173 mmu_flags |= ARCH_MMU_FLAG_PERM_EXECUTE; 174 } 175 176 return mmu_flags; 177} 178 179} // namespace intel_iommu 180