162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright © 2020 Intel Corporation 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/log2.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "gem/i915_gem_lmem.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "gen8_ppgtt.h" 1162306a36Sopenharmony_ci#include "i915_scatterlist.h" 1262306a36Sopenharmony_ci#include "i915_trace.h" 1362306a36Sopenharmony_ci#include "i915_pvinfo.h" 1462306a36Sopenharmony_ci#include "i915_vgpu.h" 1562306a36Sopenharmony_ci#include "intel_gt.h" 1662306a36Sopenharmony_ci#include "intel_gtt.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic u64 gen8_pde_encode(const dma_addr_t addr, 1962306a36Sopenharmony_ci const enum i915_cache_level level) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci u64 pde = addr | GEN8_PAGE_PRESENT | GEN8_PAGE_RW; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci if (level != I915_CACHE_NONE) 2462306a36Sopenharmony_ci pde |= PPAT_CACHED_PDE; 2562306a36Sopenharmony_ci else 2662306a36Sopenharmony_ci pde |= PPAT_UNCACHED; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci return pde; 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic u64 gen8_pte_encode(dma_addr_t addr, 3262306a36Sopenharmony_ci unsigned int pat_index, 3362306a36Sopenharmony_ci u32 flags) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci gen8_pte_t pte = addr | GEN8_PAGE_PRESENT | GEN8_PAGE_RW; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (unlikely(flags & PTE_READ_ONLY)) 3862306a36Sopenharmony_ci pte &= ~GEN8_PAGE_RW; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci /* 4162306a36Sopenharmony_ci * For pre-gen12 platforms pat_index is the same as enum 4262306a36Sopenharmony_ci * i915_cache_level, so the switch-case here is still valid. 4362306a36Sopenharmony_ci * See translation table defined by LEGACY_CACHELEVEL. 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci switch (pat_index) { 4662306a36Sopenharmony_ci case I915_CACHE_NONE: 4762306a36Sopenharmony_ci pte |= PPAT_UNCACHED; 4862306a36Sopenharmony_ci break; 4962306a36Sopenharmony_ci case I915_CACHE_WT: 5062306a36Sopenharmony_ci pte |= PPAT_DISPLAY_ELLC; 5162306a36Sopenharmony_ci break; 5262306a36Sopenharmony_ci default: 5362306a36Sopenharmony_ci pte |= PPAT_CACHED; 5462306a36Sopenharmony_ci break; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return pte; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic u64 gen12_pte_encode(dma_addr_t addr, 6162306a36Sopenharmony_ci unsigned int pat_index, 6262306a36Sopenharmony_ci u32 flags) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci gen8_pte_t pte = addr | GEN8_PAGE_PRESENT | GEN8_PAGE_RW; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (unlikely(flags & PTE_READ_ONLY)) 6762306a36Sopenharmony_ci pte &= ~GEN8_PAGE_RW; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (flags & PTE_LM) 7062306a36Sopenharmony_ci pte |= GEN12_PPGTT_PTE_LM; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (pat_index & BIT(0)) 7362306a36Sopenharmony_ci pte |= GEN12_PPGTT_PTE_PAT0; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (pat_index & BIT(1)) 7662306a36Sopenharmony_ci pte |= GEN12_PPGTT_PTE_PAT1; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (pat_index & BIT(2)) 7962306a36Sopenharmony_ci pte |= GEN12_PPGTT_PTE_PAT2; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (pat_index & BIT(3)) 8262306a36Sopenharmony_ci pte |= MTL_PPGTT_PTE_PAT3; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return pte; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void gen8_ppgtt_notify_vgt(struct i915_ppgtt *ppgtt, bool create) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct drm_i915_private *i915 = ppgtt->vm.i915; 9062306a36Sopenharmony_ci struct intel_uncore *uncore = ppgtt->vm.gt->uncore; 9162306a36Sopenharmony_ci enum vgt_g2v_type msg; 9262306a36Sopenharmony_ci int i; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (create) 9562306a36Sopenharmony_ci atomic_inc(px_used(ppgtt->pd)); /* never remove */ 9662306a36Sopenharmony_ci else 9762306a36Sopenharmony_ci atomic_dec(px_used(ppgtt->pd)); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci mutex_lock(&i915->vgpu.lock); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (i915_vm_is_4lvl(&ppgtt->vm)) { 10262306a36Sopenharmony_ci const u64 daddr = px_dma(ppgtt->pd); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci intel_uncore_write(uncore, 10562306a36Sopenharmony_ci vgtif_reg(pdp[0].lo), lower_32_bits(daddr)); 10662306a36Sopenharmony_ci intel_uncore_write(uncore, 10762306a36Sopenharmony_ci vgtif_reg(pdp[0].hi), upper_32_bits(daddr)); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci msg = create ? 11062306a36Sopenharmony_ci VGT_G2V_PPGTT_L4_PAGE_TABLE_CREATE : 11162306a36Sopenharmony_ci VGT_G2V_PPGTT_L4_PAGE_TABLE_DESTROY; 11262306a36Sopenharmony_ci } else { 11362306a36Sopenharmony_ci for (i = 0; i < GEN8_3LVL_PDPES; i++) { 11462306a36Sopenharmony_ci const u64 daddr = i915_page_dir_dma_addr(ppgtt, i); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci intel_uncore_write(uncore, 11762306a36Sopenharmony_ci vgtif_reg(pdp[i].lo), 11862306a36Sopenharmony_ci lower_32_bits(daddr)); 11962306a36Sopenharmony_ci intel_uncore_write(uncore, 12062306a36Sopenharmony_ci vgtif_reg(pdp[i].hi), 12162306a36Sopenharmony_ci upper_32_bits(daddr)); 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci msg = create ? 12562306a36Sopenharmony_ci VGT_G2V_PPGTT_L3_PAGE_TABLE_CREATE : 12662306a36Sopenharmony_ci VGT_G2V_PPGTT_L3_PAGE_TABLE_DESTROY; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* g2v_notify atomically (via hv trap) consumes the message packet. */ 13062306a36Sopenharmony_ci intel_uncore_write(uncore, vgtif_reg(g2v_notify), msg); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci mutex_unlock(&i915->vgpu.lock); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* Index shifts into the pagetable are offset by GEN8_PTE_SHIFT [12] */ 13662306a36Sopenharmony_ci#define GEN8_PAGE_SIZE (SZ_4K) /* page and page-directory sizes are the same */ 13762306a36Sopenharmony_ci#define GEN8_PTE_SHIFT (ilog2(GEN8_PAGE_SIZE)) 13862306a36Sopenharmony_ci#define GEN8_PDES (GEN8_PAGE_SIZE / sizeof(u64)) 13962306a36Sopenharmony_ci#define gen8_pd_shift(lvl) ((lvl) * ilog2(GEN8_PDES)) 14062306a36Sopenharmony_ci#define gen8_pd_index(i, lvl) i915_pde_index((i), gen8_pd_shift(lvl)) 14162306a36Sopenharmony_ci#define __gen8_pte_shift(lvl) (GEN8_PTE_SHIFT + gen8_pd_shift(lvl)) 14262306a36Sopenharmony_ci#define __gen8_pte_index(a, lvl) i915_pde_index((a), __gen8_pte_shift(lvl)) 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci#define as_pd(x) container_of((x), typeof(struct i915_page_directory), pt) 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic unsigned int 14762306a36Sopenharmony_cigen8_pd_range(u64 start, u64 end, int lvl, unsigned int *idx) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci const int shift = gen8_pd_shift(lvl); 15062306a36Sopenharmony_ci const u64 mask = ~0ull << gen8_pd_shift(lvl + 1); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci GEM_BUG_ON(start >= end); 15362306a36Sopenharmony_ci end += ~mask >> gen8_pd_shift(1); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci *idx = i915_pde_index(start, shift); 15662306a36Sopenharmony_ci if ((start ^ end) & mask) 15762306a36Sopenharmony_ci return GEN8_PDES - *idx; 15862306a36Sopenharmony_ci else 15962306a36Sopenharmony_ci return i915_pde_index(end, shift) - *idx; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic bool gen8_pd_contains(u64 start, u64 end, int lvl) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci const u64 mask = ~0ull << gen8_pd_shift(lvl + 1); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci GEM_BUG_ON(start >= end); 16762306a36Sopenharmony_ci return (start ^ end) & mask && (start & ~mask) == 0; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic unsigned int gen8_pt_count(u64 start, u64 end) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci GEM_BUG_ON(start >= end); 17362306a36Sopenharmony_ci if ((start ^ end) >> gen8_pd_shift(1)) 17462306a36Sopenharmony_ci return GEN8_PDES - (start & (GEN8_PDES - 1)); 17562306a36Sopenharmony_ci else 17662306a36Sopenharmony_ci return end - start; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic unsigned int gen8_pd_top_count(const struct i915_address_space *vm) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci unsigned int shift = __gen8_pte_shift(vm->top); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci return (vm->total + (1ull << shift) - 1) >> shift; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic struct i915_page_directory * 18762306a36Sopenharmony_cigen8_pdp_for_page_index(struct i915_address_space * const vm, const u64 idx) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci struct i915_ppgtt * const ppgtt = i915_vm_to_ppgtt(vm); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (vm->top == 2) 19262306a36Sopenharmony_ci return ppgtt->pd; 19362306a36Sopenharmony_ci else 19462306a36Sopenharmony_ci return i915_pd_entry(ppgtt->pd, gen8_pd_index(idx, vm->top)); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic struct i915_page_directory * 19862306a36Sopenharmony_cigen8_pdp_for_page_address(struct i915_address_space * const vm, const u64 addr) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci return gen8_pdp_for_page_index(vm, addr >> GEN8_PTE_SHIFT); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic void __gen8_ppgtt_cleanup(struct i915_address_space *vm, 20462306a36Sopenharmony_ci struct i915_page_directory *pd, 20562306a36Sopenharmony_ci int count, int lvl) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci if (lvl) { 20862306a36Sopenharmony_ci void **pde = pd->entry; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci do { 21162306a36Sopenharmony_ci if (!*pde) 21262306a36Sopenharmony_ci continue; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci __gen8_ppgtt_cleanup(vm, *pde, GEN8_PDES, lvl - 1); 21562306a36Sopenharmony_ci } while (pde++, --count); 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci free_px(vm, &pd->pt, lvl); 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic void gen8_ppgtt_cleanup(struct i915_address_space *vm) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct i915_ppgtt *ppgtt = i915_vm_to_ppgtt(vm); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (intel_vgpu_active(vm->i915)) 22662306a36Sopenharmony_ci gen8_ppgtt_notify_vgt(ppgtt, false); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (ppgtt->pd) 22962306a36Sopenharmony_ci __gen8_ppgtt_cleanup(vm, ppgtt->pd, 23062306a36Sopenharmony_ci gen8_pd_top_count(vm), vm->top); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci free_scratch(vm); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic u64 __gen8_ppgtt_clear(struct i915_address_space * const vm, 23662306a36Sopenharmony_ci struct i915_page_directory * const pd, 23762306a36Sopenharmony_ci u64 start, const u64 end, int lvl) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci const struct drm_i915_gem_object * const scratch = vm->scratch[lvl]; 24062306a36Sopenharmony_ci unsigned int idx, len; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci GEM_BUG_ON(end > vm->total >> GEN8_PTE_SHIFT); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci len = gen8_pd_range(start, end, lvl--, &idx); 24562306a36Sopenharmony_ci DBG("%s(%p):{ lvl:%d, start:%llx, end:%llx, idx:%d, len:%d, used:%d }\n", 24662306a36Sopenharmony_ci __func__, vm, lvl + 1, start, end, 24762306a36Sopenharmony_ci idx, len, atomic_read(px_used(pd))); 24862306a36Sopenharmony_ci GEM_BUG_ON(!len || len >= atomic_read(px_used(pd))); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci do { 25162306a36Sopenharmony_ci struct i915_page_table *pt = pd->entry[idx]; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (atomic_fetch_inc(&pt->used) >> gen8_pd_shift(1) && 25462306a36Sopenharmony_ci gen8_pd_contains(start, end, lvl)) { 25562306a36Sopenharmony_ci DBG("%s(%p):{ lvl:%d, idx:%d, start:%llx, end:%llx } removing pd\n", 25662306a36Sopenharmony_ci __func__, vm, lvl + 1, idx, start, end); 25762306a36Sopenharmony_ci clear_pd_entry(pd, idx, scratch); 25862306a36Sopenharmony_ci __gen8_ppgtt_cleanup(vm, as_pd(pt), I915_PDES, lvl); 25962306a36Sopenharmony_ci start += (u64)I915_PDES << gen8_pd_shift(lvl); 26062306a36Sopenharmony_ci continue; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (lvl) { 26462306a36Sopenharmony_ci start = __gen8_ppgtt_clear(vm, as_pd(pt), 26562306a36Sopenharmony_ci start, end, lvl); 26662306a36Sopenharmony_ci } else { 26762306a36Sopenharmony_ci unsigned int count; 26862306a36Sopenharmony_ci unsigned int pte = gen8_pd_index(start, 0); 26962306a36Sopenharmony_ci unsigned int num_ptes; 27062306a36Sopenharmony_ci u64 *vaddr; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci count = gen8_pt_count(start, end); 27362306a36Sopenharmony_ci DBG("%s(%p):{ lvl:%d, start:%llx, end:%llx, idx:%d, len:%d, used:%d } removing pte\n", 27462306a36Sopenharmony_ci __func__, vm, lvl, start, end, 27562306a36Sopenharmony_ci gen8_pd_index(start, 0), count, 27662306a36Sopenharmony_ci atomic_read(&pt->used)); 27762306a36Sopenharmony_ci GEM_BUG_ON(!count || count >= atomic_read(&pt->used)); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci num_ptes = count; 28062306a36Sopenharmony_ci if (pt->is_compact) { 28162306a36Sopenharmony_ci GEM_BUG_ON(num_ptes % 16); 28262306a36Sopenharmony_ci GEM_BUG_ON(pte % 16); 28362306a36Sopenharmony_ci num_ptes /= 16; 28462306a36Sopenharmony_ci pte /= 16; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci vaddr = px_vaddr(pt); 28862306a36Sopenharmony_ci memset64(vaddr + pte, 28962306a36Sopenharmony_ci vm->scratch[0]->encode, 29062306a36Sopenharmony_ci num_ptes); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci atomic_sub(count, &pt->used); 29362306a36Sopenharmony_ci start += count; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (release_pd_entry(pd, idx, pt, scratch)) 29762306a36Sopenharmony_ci free_px(vm, pt, lvl); 29862306a36Sopenharmony_ci } while (idx++, --len); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci return start; 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic void gen8_ppgtt_clear(struct i915_address_space *vm, 30462306a36Sopenharmony_ci u64 start, u64 length) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci GEM_BUG_ON(!IS_ALIGNED(start, BIT_ULL(GEN8_PTE_SHIFT))); 30762306a36Sopenharmony_ci GEM_BUG_ON(!IS_ALIGNED(length, BIT_ULL(GEN8_PTE_SHIFT))); 30862306a36Sopenharmony_ci GEM_BUG_ON(range_overflows(start, length, vm->total)); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci start >>= GEN8_PTE_SHIFT; 31162306a36Sopenharmony_ci length >>= GEN8_PTE_SHIFT; 31262306a36Sopenharmony_ci GEM_BUG_ON(length == 0); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci __gen8_ppgtt_clear(vm, i915_vm_to_ppgtt(vm)->pd, 31562306a36Sopenharmony_ci start, start + length, vm->top); 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic void __gen8_ppgtt_alloc(struct i915_address_space * const vm, 31962306a36Sopenharmony_ci struct i915_vm_pt_stash *stash, 32062306a36Sopenharmony_ci struct i915_page_directory * const pd, 32162306a36Sopenharmony_ci u64 * const start, const u64 end, int lvl) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci unsigned int idx, len; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci GEM_BUG_ON(end > vm->total >> GEN8_PTE_SHIFT); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci len = gen8_pd_range(*start, end, lvl--, &idx); 32862306a36Sopenharmony_ci DBG("%s(%p):{ lvl:%d, start:%llx, end:%llx, idx:%d, len:%d, used:%d }\n", 32962306a36Sopenharmony_ci __func__, vm, lvl + 1, *start, end, 33062306a36Sopenharmony_ci idx, len, atomic_read(px_used(pd))); 33162306a36Sopenharmony_ci GEM_BUG_ON(!len || (idx + len - 1) >> gen8_pd_shift(1)); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci spin_lock(&pd->lock); 33462306a36Sopenharmony_ci GEM_BUG_ON(!atomic_read(px_used(pd))); /* Must be pinned! */ 33562306a36Sopenharmony_ci do { 33662306a36Sopenharmony_ci struct i915_page_table *pt = pd->entry[idx]; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (!pt) { 33962306a36Sopenharmony_ci spin_unlock(&pd->lock); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci DBG("%s(%p):{ lvl:%d, idx:%d } allocating new tree\n", 34262306a36Sopenharmony_ci __func__, vm, lvl + 1, idx); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci pt = stash->pt[!!lvl]; 34562306a36Sopenharmony_ci __i915_gem_object_pin_pages(pt->base); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci fill_px(pt, vm->scratch[lvl]->encode); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci spin_lock(&pd->lock); 35062306a36Sopenharmony_ci if (likely(!pd->entry[idx])) { 35162306a36Sopenharmony_ci stash->pt[!!lvl] = pt->stash; 35262306a36Sopenharmony_ci atomic_set(&pt->used, 0); 35362306a36Sopenharmony_ci set_pd_entry(pd, idx, pt); 35462306a36Sopenharmony_ci } else { 35562306a36Sopenharmony_ci pt = pd->entry[idx]; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (lvl) { 36062306a36Sopenharmony_ci atomic_inc(&pt->used); 36162306a36Sopenharmony_ci spin_unlock(&pd->lock); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci __gen8_ppgtt_alloc(vm, stash, 36462306a36Sopenharmony_ci as_pd(pt), start, end, lvl); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci spin_lock(&pd->lock); 36762306a36Sopenharmony_ci atomic_dec(&pt->used); 36862306a36Sopenharmony_ci GEM_BUG_ON(!atomic_read(&pt->used)); 36962306a36Sopenharmony_ci } else { 37062306a36Sopenharmony_ci unsigned int count = gen8_pt_count(*start, end); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci DBG("%s(%p):{ lvl:%d, start:%llx, end:%llx, idx:%d, len:%d, used:%d } inserting pte\n", 37362306a36Sopenharmony_ci __func__, vm, lvl, *start, end, 37462306a36Sopenharmony_ci gen8_pd_index(*start, 0), count, 37562306a36Sopenharmony_ci atomic_read(&pt->used)); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci atomic_add(count, &pt->used); 37862306a36Sopenharmony_ci /* All other pdes may be simultaneously removed */ 37962306a36Sopenharmony_ci GEM_BUG_ON(atomic_read(&pt->used) > NALLOC * I915_PDES); 38062306a36Sopenharmony_ci *start += count; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci } while (idx++, --len); 38362306a36Sopenharmony_ci spin_unlock(&pd->lock); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic void gen8_ppgtt_alloc(struct i915_address_space *vm, 38762306a36Sopenharmony_ci struct i915_vm_pt_stash *stash, 38862306a36Sopenharmony_ci u64 start, u64 length) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci GEM_BUG_ON(!IS_ALIGNED(start, BIT_ULL(GEN8_PTE_SHIFT))); 39162306a36Sopenharmony_ci GEM_BUG_ON(!IS_ALIGNED(length, BIT_ULL(GEN8_PTE_SHIFT))); 39262306a36Sopenharmony_ci GEM_BUG_ON(range_overflows(start, length, vm->total)); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci start >>= GEN8_PTE_SHIFT; 39562306a36Sopenharmony_ci length >>= GEN8_PTE_SHIFT; 39662306a36Sopenharmony_ci GEM_BUG_ON(length == 0); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci __gen8_ppgtt_alloc(vm, stash, i915_vm_to_ppgtt(vm)->pd, 39962306a36Sopenharmony_ci &start, start + length, vm->top); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic void __gen8_ppgtt_foreach(struct i915_address_space *vm, 40362306a36Sopenharmony_ci struct i915_page_directory *pd, 40462306a36Sopenharmony_ci u64 *start, u64 end, int lvl, 40562306a36Sopenharmony_ci void (*fn)(struct i915_address_space *vm, 40662306a36Sopenharmony_ci struct i915_page_table *pt, 40762306a36Sopenharmony_ci void *data), 40862306a36Sopenharmony_ci void *data) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci unsigned int idx, len; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci len = gen8_pd_range(*start, end, lvl--, &idx); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci spin_lock(&pd->lock); 41562306a36Sopenharmony_ci do { 41662306a36Sopenharmony_ci struct i915_page_table *pt = pd->entry[idx]; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci atomic_inc(&pt->used); 41962306a36Sopenharmony_ci spin_unlock(&pd->lock); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (lvl) { 42262306a36Sopenharmony_ci __gen8_ppgtt_foreach(vm, as_pd(pt), start, end, lvl, 42362306a36Sopenharmony_ci fn, data); 42462306a36Sopenharmony_ci } else { 42562306a36Sopenharmony_ci fn(vm, pt, data); 42662306a36Sopenharmony_ci *start += gen8_pt_count(*start, end); 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci spin_lock(&pd->lock); 43062306a36Sopenharmony_ci atomic_dec(&pt->used); 43162306a36Sopenharmony_ci } while (idx++, --len); 43262306a36Sopenharmony_ci spin_unlock(&pd->lock); 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic void gen8_ppgtt_foreach(struct i915_address_space *vm, 43662306a36Sopenharmony_ci u64 start, u64 length, 43762306a36Sopenharmony_ci void (*fn)(struct i915_address_space *vm, 43862306a36Sopenharmony_ci struct i915_page_table *pt, 43962306a36Sopenharmony_ci void *data), 44062306a36Sopenharmony_ci void *data) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci start >>= GEN8_PTE_SHIFT; 44362306a36Sopenharmony_ci length >>= GEN8_PTE_SHIFT; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci __gen8_ppgtt_foreach(vm, i915_vm_to_ppgtt(vm)->pd, 44662306a36Sopenharmony_ci &start, start + length, vm->top, 44762306a36Sopenharmony_ci fn, data); 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic __always_inline u64 45162306a36Sopenharmony_cigen8_ppgtt_insert_pte(struct i915_ppgtt *ppgtt, 45262306a36Sopenharmony_ci struct i915_page_directory *pdp, 45362306a36Sopenharmony_ci struct sgt_dma *iter, 45462306a36Sopenharmony_ci u64 idx, 45562306a36Sopenharmony_ci unsigned int pat_index, 45662306a36Sopenharmony_ci u32 flags) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct i915_page_directory *pd; 45962306a36Sopenharmony_ci const gen8_pte_t pte_encode = ppgtt->vm.pte_encode(0, pat_index, flags); 46062306a36Sopenharmony_ci gen8_pte_t *vaddr; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci pd = i915_pd_entry(pdp, gen8_pd_index(idx, 2)); 46362306a36Sopenharmony_ci vaddr = px_vaddr(i915_pt_entry(pd, gen8_pd_index(idx, 1))); 46462306a36Sopenharmony_ci do { 46562306a36Sopenharmony_ci GEM_BUG_ON(sg_dma_len(iter->sg) < I915_GTT_PAGE_SIZE); 46662306a36Sopenharmony_ci vaddr[gen8_pd_index(idx, 0)] = pte_encode | iter->dma; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci iter->dma += I915_GTT_PAGE_SIZE; 46962306a36Sopenharmony_ci if (iter->dma >= iter->max) { 47062306a36Sopenharmony_ci iter->sg = __sg_next(iter->sg); 47162306a36Sopenharmony_ci if (!iter->sg || sg_dma_len(iter->sg) == 0) { 47262306a36Sopenharmony_ci idx = 0; 47362306a36Sopenharmony_ci break; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci iter->dma = sg_dma_address(iter->sg); 47762306a36Sopenharmony_ci iter->max = iter->dma + sg_dma_len(iter->sg); 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (gen8_pd_index(++idx, 0) == 0) { 48162306a36Sopenharmony_ci if (gen8_pd_index(idx, 1) == 0) { 48262306a36Sopenharmony_ci /* Limited by sg length for 3lvl */ 48362306a36Sopenharmony_ci if (gen8_pd_index(idx, 2) == 0) 48462306a36Sopenharmony_ci break; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci pd = pdp->entry[gen8_pd_index(idx, 2)]; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci drm_clflush_virt_range(vaddr, PAGE_SIZE); 49062306a36Sopenharmony_ci vaddr = px_vaddr(i915_pt_entry(pd, gen8_pd_index(idx, 1))); 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci } while (1); 49362306a36Sopenharmony_ci drm_clflush_virt_range(vaddr, PAGE_SIZE); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci return idx; 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic void 49962306a36Sopenharmony_cixehpsdv_ppgtt_insert_huge(struct i915_address_space *vm, 50062306a36Sopenharmony_ci struct i915_vma_resource *vma_res, 50162306a36Sopenharmony_ci struct sgt_dma *iter, 50262306a36Sopenharmony_ci unsigned int pat_index, 50362306a36Sopenharmony_ci u32 flags) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci const gen8_pte_t pte_encode = vm->pte_encode(0, pat_index, flags); 50662306a36Sopenharmony_ci unsigned int rem = sg_dma_len(iter->sg); 50762306a36Sopenharmony_ci u64 start = vma_res->start; 50862306a36Sopenharmony_ci u64 end = start + vma_res->vma_size; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci GEM_BUG_ON(!i915_vm_is_4lvl(vm)); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci do { 51362306a36Sopenharmony_ci struct i915_page_directory * const pdp = 51462306a36Sopenharmony_ci gen8_pdp_for_page_address(vm, start); 51562306a36Sopenharmony_ci struct i915_page_directory * const pd = 51662306a36Sopenharmony_ci i915_pd_entry(pdp, __gen8_pte_index(start, 2)); 51762306a36Sopenharmony_ci struct i915_page_table *pt = 51862306a36Sopenharmony_ci i915_pt_entry(pd, __gen8_pte_index(start, 1)); 51962306a36Sopenharmony_ci gen8_pte_t encode = pte_encode; 52062306a36Sopenharmony_ci unsigned int page_size; 52162306a36Sopenharmony_ci gen8_pte_t *vaddr; 52262306a36Sopenharmony_ci u16 index, max, nent, i; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci max = I915_PDES; 52562306a36Sopenharmony_ci nent = 1; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (vma_res->bi.page_sizes.sg & I915_GTT_PAGE_SIZE_2M && 52862306a36Sopenharmony_ci IS_ALIGNED(iter->dma, I915_GTT_PAGE_SIZE_2M) && 52962306a36Sopenharmony_ci rem >= I915_GTT_PAGE_SIZE_2M && 53062306a36Sopenharmony_ci !__gen8_pte_index(start, 0)) { 53162306a36Sopenharmony_ci index = __gen8_pte_index(start, 1); 53262306a36Sopenharmony_ci encode |= GEN8_PDE_PS_2M; 53362306a36Sopenharmony_ci page_size = I915_GTT_PAGE_SIZE_2M; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci vaddr = px_vaddr(pd); 53662306a36Sopenharmony_ci } else { 53762306a36Sopenharmony_ci index = __gen8_pte_index(start, 0); 53862306a36Sopenharmony_ci page_size = I915_GTT_PAGE_SIZE; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (vma_res->bi.page_sizes.sg & I915_GTT_PAGE_SIZE_64K) { 54162306a36Sopenharmony_ci /* 54262306a36Sopenharmony_ci * Device local-memory on these platforms should 54362306a36Sopenharmony_ci * always use 64K pages or larger (including GTT 54462306a36Sopenharmony_ci * alignment), therefore if we know the whole 54562306a36Sopenharmony_ci * page-table needs to be filled we can always 54662306a36Sopenharmony_ci * safely use the compact-layout. Otherwise fall 54762306a36Sopenharmony_ci * back to the TLB hint with PS64. If this is 54862306a36Sopenharmony_ci * system memory we only bother with PS64. 54962306a36Sopenharmony_ci */ 55062306a36Sopenharmony_ci if ((encode & GEN12_PPGTT_PTE_LM) && 55162306a36Sopenharmony_ci end - start >= SZ_2M && !index) { 55262306a36Sopenharmony_ci index = __gen8_pte_index(start, 0) / 16; 55362306a36Sopenharmony_ci page_size = I915_GTT_PAGE_SIZE_64K; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci max /= 16; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci vaddr = px_vaddr(pd); 55862306a36Sopenharmony_ci vaddr[__gen8_pte_index(start, 1)] |= GEN12_PDE_64K; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci pt->is_compact = true; 56162306a36Sopenharmony_ci } else if (IS_ALIGNED(iter->dma, I915_GTT_PAGE_SIZE_64K) && 56262306a36Sopenharmony_ci rem >= I915_GTT_PAGE_SIZE_64K && 56362306a36Sopenharmony_ci !(index % 16)) { 56462306a36Sopenharmony_ci encode |= GEN12_PTE_PS64; 56562306a36Sopenharmony_ci page_size = I915_GTT_PAGE_SIZE_64K; 56662306a36Sopenharmony_ci nent = 16; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci vaddr = px_vaddr(pt); 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci do { 57462306a36Sopenharmony_ci GEM_BUG_ON(rem < page_size); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci for (i = 0; i < nent; i++) { 57762306a36Sopenharmony_ci vaddr[index++] = 57862306a36Sopenharmony_ci encode | (iter->dma + i * 57962306a36Sopenharmony_ci I915_GTT_PAGE_SIZE); 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci start += page_size; 58362306a36Sopenharmony_ci iter->dma += page_size; 58462306a36Sopenharmony_ci rem -= page_size; 58562306a36Sopenharmony_ci if (iter->dma >= iter->max) { 58662306a36Sopenharmony_ci iter->sg = __sg_next(iter->sg); 58762306a36Sopenharmony_ci if (!iter->sg) 58862306a36Sopenharmony_ci break; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci rem = sg_dma_len(iter->sg); 59162306a36Sopenharmony_ci if (!rem) 59262306a36Sopenharmony_ci break; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci iter->dma = sg_dma_address(iter->sg); 59562306a36Sopenharmony_ci iter->max = iter->dma + rem; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (unlikely(!IS_ALIGNED(iter->dma, page_size))) 59862306a36Sopenharmony_ci break; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci } while (rem >= page_size && index < max); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci drm_clflush_virt_range(vaddr, PAGE_SIZE); 60362306a36Sopenharmony_ci vma_res->page_sizes_gtt |= page_size; 60462306a36Sopenharmony_ci } while (iter->sg && sg_dma_len(iter->sg)); 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic void gen8_ppgtt_insert_huge(struct i915_address_space *vm, 60862306a36Sopenharmony_ci struct i915_vma_resource *vma_res, 60962306a36Sopenharmony_ci struct sgt_dma *iter, 61062306a36Sopenharmony_ci unsigned int pat_index, 61162306a36Sopenharmony_ci u32 flags) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci const gen8_pte_t pte_encode = vm->pte_encode(0, pat_index, flags); 61462306a36Sopenharmony_ci unsigned int rem = sg_dma_len(iter->sg); 61562306a36Sopenharmony_ci u64 start = vma_res->start; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci GEM_BUG_ON(!i915_vm_is_4lvl(vm)); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci do { 62062306a36Sopenharmony_ci struct i915_page_directory * const pdp = 62162306a36Sopenharmony_ci gen8_pdp_for_page_address(vm, start); 62262306a36Sopenharmony_ci struct i915_page_directory * const pd = 62362306a36Sopenharmony_ci i915_pd_entry(pdp, __gen8_pte_index(start, 2)); 62462306a36Sopenharmony_ci gen8_pte_t encode = pte_encode; 62562306a36Sopenharmony_ci unsigned int maybe_64K = -1; 62662306a36Sopenharmony_ci unsigned int page_size; 62762306a36Sopenharmony_ci gen8_pte_t *vaddr; 62862306a36Sopenharmony_ci u16 index; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (vma_res->bi.page_sizes.sg & I915_GTT_PAGE_SIZE_2M && 63162306a36Sopenharmony_ci IS_ALIGNED(iter->dma, I915_GTT_PAGE_SIZE_2M) && 63262306a36Sopenharmony_ci rem >= I915_GTT_PAGE_SIZE_2M && 63362306a36Sopenharmony_ci !__gen8_pte_index(start, 0)) { 63462306a36Sopenharmony_ci index = __gen8_pte_index(start, 1); 63562306a36Sopenharmony_ci encode |= GEN8_PDE_PS_2M; 63662306a36Sopenharmony_ci page_size = I915_GTT_PAGE_SIZE_2M; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci vaddr = px_vaddr(pd); 63962306a36Sopenharmony_ci } else { 64062306a36Sopenharmony_ci struct i915_page_table *pt = 64162306a36Sopenharmony_ci i915_pt_entry(pd, __gen8_pte_index(start, 1)); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci index = __gen8_pte_index(start, 0); 64462306a36Sopenharmony_ci page_size = I915_GTT_PAGE_SIZE; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if (!index && 64762306a36Sopenharmony_ci vma_res->bi.page_sizes.sg & I915_GTT_PAGE_SIZE_64K && 64862306a36Sopenharmony_ci IS_ALIGNED(iter->dma, I915_GTT_PAGE_SIZE_64K) && 64962306a36Sopenharmony_ci (IS_ALIGNED(rem, I915_GTT_PAGE_SIZE_64K) || 65062306a36Sopenharmony_ci rem >= (I915_PDES - index) * I915_GTT_PAGE_SIZE)) 65162306a36Sopenharmony_ci maybe_64K = __gen8_pte_index(start, 1); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci vaddr = px_vaddr(pt); 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci do { 65762306a36Sopenharmony_ci GEM_BUG_ON(sg_dma_len(iter->sg) < page_size); 65862306a36Sopenharmony_ci vaddr[index++] = encode | iter->dma; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci start += page_size; 66162306a36Sopenharmony_ci iter->dma += page_size; 66262306a36Sopenharmony_ci rem -= page_size; 66362306a36Sopenharmony_ci if (iter->dma >= iter->max) { 66462306a36Sopenharmony_ci iter->sg = __sg_next(iter->sg); 66562306a36Sopenharmony_ci if (!iter->sg) 66662306a36Sopenharmony_ci break; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci rem = sg_dma_len(iter->sg); 66962306a36Sopenharmony_ci if (!rem) 67062306a36Sopenharmony_ci break; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci iter->dma = sg_dma_address(iter->sg); 67362306a36Sopenharmony_ci iter->max = iter->dma + rem; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci if (maybe_64K != -1 && index < I915_PDES && 67662306a36Sopenharmony_ci !(IS_ALIGNED(iter->dma, I915_GTT_PAGE_SIZE_64K) && 67762306a36Sopenharmony_ci (IS_ALIGNED(rem, I915_GTT_PAGE_SIZE_64K) || 67862306a36Sopenharmony_ci rem >= (I915_PDES - index) * I915_GTT_PAGE_SIZE))) 67962306a36Sopenharmony_ci maybe_64K = -1; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (unlikely(!IS_ALIGNED(iter->dma, page_size))) 68262306a36Sopenharmony_ci break; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci } while (rem >= page_size && index < I915_PDES); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci drm_clflush_virt_range(vaddr, PAGE_SIZE); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci /* 68962306a36Sopenharmony_ci * Is it safe to mark the 2M block as 64K? -- Either we have 69062306a36Sopenharmony_ci * filled whole page-table with 64K entries, or filled part of 69162306a36Sopenharmony_ci * it and have reached the end of the sg table and we have 69262306a36Sopenharmony_ci * enough padding. 69362306a36Sopenharmony_ci */ 69462306a36Sopenharmony_ci if (maybe_64K != -1 && 69562306a36Sopenharmony_ci (index == I915_PDES || 69662306a36Sopenharmony_ci (i915_vm_has_scratch_64K(vm) && 69762306a36Sopenharmony_ci !iter->sg && IS_ALIGNED(vma_res->start + 69862306a36Sopenharmony_ci vma_res->node_size, 69962306a36Sopenharmony_ci I915_GTT_PAGE_SIZE_2M)))) { 70062306a36Sopenharmony_ci vaddr = px_vaddr(pd); 70162306a36Sopenharmony_ci vaddr[maybe_64K] |= GEN8_PDE_IPS_64K; 70262306a36Sopenharmony_ci drm_clflush_virt_range(vaddr, PAGE_SIZE); 70362306a36Sopenharmony_ci page_size = I915_GTT_PAGE_SIZE_64K; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci /* 70662306a36Sopenharmony_ci * We write all 4K page entries, even when using 64K 70762306a36Sopenharmony_ci * pages. In order to verify that the HW isn't cheating 70862306a36Sopenharmony_ci * by using the 4K PTE instead of the 64K PTE, we want 70962306a36Sopenharmony_ci * to remove all the surplus entries. If the HW skipped 71062306a36Sopenharmony_ci * the 64K PTE, it will read/write into the scratch page 71162306a36Sopenharmony_ci * instead - which we detect as missing results during 71262306a36Sopenharmony_ci * selftests. 71362306a36Sopenharmony_ci */ 71462306a36Sopenharmony_ci if (I915_SELFTEST_ONLY(vm->scrub_64K)) { 71562306a36Sopenharmony_ci u16 i; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci encode = vm->scratch[0]->encode; 71862306a36Sopenharmony_ci vaddr = px_vaddr(i915_pt_entry(pd, maybe_64K)); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci for (i = 1; i < index; i += 16) 72162306a36Sopenharmony_ci memset64(vaddr + i, encode, 15); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci drm_clflush_virt_range(vaddr, PAGE_SIZE); 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci vma_res->page_sizes_gtt |= page_size; 72862306a36Sopenharmony_ci } while (iter->sg && sg_dma_len(iter->sg)); 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic void gen8_ppgtt_insert(struct i915_address_space *vm, 73262306a36Sopenharmony_ci struct i915_vma_resource *vma_res, 73362306a36Sopenharmony_ci unsigned int pat_index, 73462306a36Sopenharmony_ci u32 flags) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci struct i915_ppgtt * const ppgtt = i915_vm_to_ppgtt(vm); 73762306a36Sopenharmony_ci struct sgt_dma iter = sgt_dma(vma_res); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (vma_res->bi.page_sizes.sg > I915_GTT_PAGE_SIZE) { 74062306a36Sopenharmony_ci if (GRAPHICS_VER_FULL(vm->i915) >= IP_VER(12, 50)) 74162306a36Sopenharmony_ci xehpsdv_ppgtt_insert_huge(vm, vma_res, &iter, pat_index, flags); 74262306a36Sopenharmony_ci else 74362306a36Sopenharmony_ci gen8_ppgtt_insert_huge(vm, vma_res, &iter, pat_index, flags); 74462306a36Sopenharmony_ci } else { 74562306a36Sopenharmony_ci u64 idx = vma_res->start >> GEN8_PTE_SHIFT; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci do { 74862306a36Sopenharmony_ci struct i915_page_directory * const pdp = 74962306a36Sopenharmony_ci gen8_pdp_for_page_index(vm, idx); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci idx = gen8_ppgtt_insert_pte(ppgtt, pdp, &iter, idx, 75262306a36Sopenharmony_ci pat_index, flags); 75362306a36Sopenharmony_ci } while (idx); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci vma_res->page_sizes_gtt = I915_GTT_PAGE_SIZE; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic void gen8_ppgtt_insert_entry(struct i915_address_space *vm, 76062306a36Sopenharmony_ci dma_addr_t addr, 76162306a36Sopenharmony_ci u64 offset, 76262306a36Sopenharmony_ci unsigned int pat_index, 76362306a36Sopenharmony_ci u32 flags) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci u64 idx = offset >> GEN8_PTE_SHIFT; 76662306a36Sopenharmony_ci struct i915_page_directory * const pdp = 76762306a36Sopenharmony_ci gen8_pdp_for_page_index(vm, idx); 76862306a36Sopenharmony_ci struct i915_page_directory *pd = 76962306a36Sopenharmony_ci i915_pd_entry(pdp, gen8_pd_index(idx, 2)); 77062306a36Sopenharmony_ci struct i915_page_table *pt = i915_pt_entry(pd, gen8_pd_index(idx, 1)); 77162306a36Sopenharmony_ci gen8_pte_t *vaddr; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci GEM_BUG_ON(pt->is_compact); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci vaddr = px_vaddr(pt); 77662306a36Sopenharmony_ci vaddr[gen8_pd_index(idx, 0)] = vm->pte_encode(addr, pat_index, flags); 77762306a36Sopenharmony_ci drm_clflush_virt_range(&vaddr[gen8_pd_index(idx, 0)], sizeof(*vaddr)); 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic void __xehpsdv_ppgtt_insert_entry_lm(struct i915_address_space *vm, 78162306a36Sopenharmony_ci dma_addr_t addr, 78262306a36Sopenharmony_ci u64 offset, 78362306a36Sopenharmony_ci unsigned int pat_index, 78462306a36Sopenharmony_ci u32 flags) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci u64 idx = offset >> GEN8_PTE_SHIFT; 78762306a36Sopenharmony_ci struct i915_page_directory * const pdp = 78862306a36Sopenharmony_ci gen8_pdp_for_page_index(vm, idx); 78962306a36Sopenharmony_ci struct i915_page_directory *pd = 79062306a36Sopenharmony_ci i915_pd_entry(pdp, gen8_pd_index(idx, 2)); 79162306a36Sopenharmony_ci struct i915_page_table *pt = i915_pt_entry(pd, gen8_pd_index(idx, 1)); 79262306a36Sopenharmony_ci gen8_pte_t *vaddr; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci GEM_BUG_ON(!IS_ALIGNED(addr, SZ_64K)); 79562306a36Sopenharmony_ci GEM_BUG_ON(!IS_ALIGNED(offset, SZ_64K)); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci /* XXX: we don't strictly need to use this layout */ 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (!pt->is_compact) { 80062306a36Sopenharmony_ci vaddr = px_vaddr(pd); 80162306a36Sopenharmony_ci vaddr[gen8_pd_index(idx, 1)] |= GEN12_PDE_64K; 80262306a36Sopenharmony_ci pt->is_compact = true; 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci vaddr = px_vaddr(pt); 80662306a36Sopenharmony_ci vaddr[gen8_pd_index(idx, 0) / 16] = vm->pte_encode(addr, pat_index, flags); 80762306a36Sopenharmony_ci} 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_cistatic void xehpsdv_ppgtt_insert_entry(struct i915_address_space *vm, 81062306a36Sopenharmony_ci dma_addr_t addr, 81162306a36Sopenharmony_ci u64 offset, 81262306a36Sopenharmony_ci unsigned int pat_index, 81362306a36Sopenharmony_ci u32 flags) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci if (flags & PTE_LM) 81662306a36Sopenharmony_ci return __xehpsdv_ppgtt_insert_entry_lm(vm, addr, offset, 81762306a36Sopenharmony_ci pat_index, flags); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci return gen8_ppgtt_insert_entry(vm, addr, offset, pat_index, flags); 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cistatic int gen8_init_scratch(struct i915_address_space *vm) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci u32 pte_flags; 82562306a36Sopenharmony_ci int ret; 82662306a36Sopenharmony_ci int i; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci /* 82962306a36Sopenharmony_ci * If everybody agrees to not to write into the scratch page, 83062306a36Sopenharmony_ci * we can reuse it for all vm, keeping contexts and processes separate. 83162306a36Sopenharmony_ci */ 83262306a36Sopenharmony_ci if (vm->has_read_only && vm->gt->vm && !i915_is_ggtt(vm->gt->vm)) { 83362306a36Sopenharmony_ci struct i915_address_space *clone = vm->gt->vm; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci GEM_BUG_ON(!clone->has_read_only); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci vm->scratch_order = clone->scratch_order; 83862306a36Sopenharmony_ci for (i = 0; i <= vm->top; i++) 83962306a36Sopenharmony_ci vm->scratch[i] = i915_gem_object_get(clone->scratch[i]); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci return 0; 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci ret = setup_scratch_page(vm); 84562306a36Sopenharmony_ci if (ret) 84662306a36Sopenharmony_ci return ret; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci pte_flags = vm->has_read_only; 84962306a36Sopenharmony_ci if (i915_gem_object_is_lmem(vm->scratch[0])) 85062306a36Sopenharmony_ci pte_flags |= PTE_LM; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci vm->scratch[0]->encode = 85362306a36Sopenharmony_ci vm->pte_encode(px_dma(vm->scratch[0]), 85462306a36Sopenharmony_ci i915_gem_get_pat_index(vm->i915, 85562306a36Sopenharmony_ci I915_CACHE_NONE), 85662306a36Sopenharmony_ci pte_flags); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci for (i = 1; i <= vm->top; i++) { 85962306a36Sopenharmony_ci struct drm_i915_gem_object *obj; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci obj = vm->alloc_pt_dma(vm, I915_GTT_PAGE_SIZE_4K); 86262306a36Sopenharmony_ci if (IS_ERR(obj)) { 86362306a36Sopenharmony_ci ret = PTR_ERR(obj); 86462306a36Sopenharmony_ci goto free_scratch; 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci ret = map_pt_dma(vm, obj); 86862306a36Sopenharmony_ci if (ret) { 86962306a36Sopenharmony_ci i915_gem_object_put(obj); 87062306a36Sopenharmony_ci goto free_scratch; 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci fill_px(obj, vm->scratch[i - 1]->encode); 87462306a36Sopenharmony_ci obj->encode = gen8_pde_encode(px_dma(obj), I915_CACHE_NONE); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci vm->scratch[i] = obj; 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci return 0; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cifree_scratch: 88262306a36Sopenharmony_ci while (i--) 88362306a36Sopenharmony_ci i915_gem_object_put(vm->scratch[i]); 88462306a36Sopenharmony_ci vm->scratch[0] = NULL; 88562306a36Sopenharmony_ci return ret; 88662306a36Sopenharmony_ci} 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_cistatic int gen8_preallocate_top_level_pdp(struct i915_ppgtt *ppgtt) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci struct i915_address_space *vm = &ppgtt->vm; 89162306a36Sopenharmony_ci struct i915_page_directory *pd = ppgtt->pd; 89262306a36Sopenharmony_ci unsigned int idx; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci GEM_BUG_ON(vm->top != 2); 89562306a36Sopenharmony_ci GEM_BUG_ON(gen8_pd_top_count(vm) != GEN8_3LVL_PDPES); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci for (idx = 0; idx < GEN8_3LVL_PDPES; idx++) { 89862306a36Sopenharmony_ci struct i915_page_directory *pde; 89962306a36Sopenharmony_ci int err; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci pde = alloc_pd(vm); 90262306a36Sopenharmony_ci if (IS_ERR(pde)) 90362306a36Sopenharmony_ci return PTR_ERR(pde); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci err = map_pt_dma(vm, pde->pt.base); 90662306a36Sopenharmony_ci if (err) { 90762306a36Sopenharmony_ci free_pd(vm, pde); 90862306a36Sopenharmony_ci return err; 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci fill_px(pde, vm->scratch[1]->encode); 91262306a36Sopenharmony_ci set_pd_entry(pd, idx, pde); 91362306a36Sopenharmony_ci atomic_inc(px_used(pde)); /* keep pinned */ 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci wmb(); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci return 0; 91862306a36Sopenharmony_ci} 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_cistatic struct i915_page_directory * 92162306a36Sopenharmony_cigen8_alloc_top_pd(struct i915_address_space *vm) 92262306a36Sopenharmony_ci{ 92362306a36Sopenharmony_ci const unsigned int count = gen8_pd_top_count(vm); 92462306a36Sopenharmony_ci struct i915_page_directory *pd; 92562306a36Sopenharmony_ci int err; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci GEM_BUG_ON(count > I915_PDES); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci pd = __alloc_pd(count); 93062306a36Sopenharmony_ci if (unlikely(!pd)) 93162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci pd->pt.base = vm->alloc_pt_dma(vm, I915_GTT_PAGE_SIZE_4K); 93462306a36Sopenharmony_ci if (IS_ERR(pd->pt.base)) { 93562306a36Sopenharmony_ci err = PTR_ERR(pd->pt.base); 93662306a36Sopenharmony_ci pd->pt.base = NULL; 93762306a36Sopenharmony_ci goto err_pd; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci err = map_pt_dma(vm, pd->pt.base); 94162306a36Sopenharmony_ci if (err) 94262306a36Sopenharmony_ci goto err_pd; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci fill_page_dma(px_base(pd), vm->scratch[vm->top]->encode, count); 94562306a36Sopenharmony_ci atomic_inc(px_used(pd)); /* mark as pinned */ 94662306a36Sopenharmony_ci return pd; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_cierr_pd: 94962306a36Sopenharmony_ci free_pd(vm, pd); 95062306a36Sopenharmony_ci return ERR_PTR(err); 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci/* 95462306a36Sopenharmony_ci * GEN8 legacy ppgtt programming is accomplished through a max 4 PDP registers 95562306a36Sopenharmony_ci * with a net effect resembling a 2-level page table in normal x86 terms. Each 95662306a36Sopenharmony_ci * PDP represents 1GB of memory 4 * 512 * 512 * 4096 = 4GB legacy 32b address 95762306a36Sopenharmony_ci * space. 95862306a36Sopenharmony_ci * 95962306a36Sopenharmony_ci */ 96062306a36Sopenharmony_cistruct i915_ppgtt *gen8_ppgtt_create(struct intel_gt *gt, 96162306a36Sopenharmony_ci unsigned long lmem_pt_obj_flags) 96262306a36Sopenharmony_ci{ 96362306a36Sopenharmony_ci struct i915_page_directory *pd; 96462306a36Sopenharmony_ci struct i915_ppgtt *ppgtt; 96562306a36Sopenharmony_ci int err; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci ppgtt = kzalloc(sizeof(*ppgtt), GFP_KERNEL); 96862306a36Sopenharmony_ci if (!ppgtt) 96962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci ppgtt_init(ppgtt, gt, lmem_pt_obj_flags); 97262306a36Sopenharmony_ci ppgtt->vm.top = i915_vm_is_4lvl(&ppgtt->vm) ? 3 : 2; 97362306a36Sopenharmony_ci ppgtt->vm.pd_shift = ilog2(SZ_4K * SZ_4K / sizeof(gen8_pte_t)); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci /* 97662306a36Sopenharmony_ci * From bdw, there is hw support for read-only pages in the PPGTT. 97762306a36Sopenharmony_ci * 97862306a36Sopenharmony_ci * Gen11 has HSDES#:1807136187 unresolved. Disable ro support 97962306a36Sopenharmony_ci * for now. 98062306a36Sopenharmony_ci * 98162306a36Sopenharmony_ci * Gen12 has inherited the same read-only fault issue from gen11. 98262306a36Sopenharmony_ci */ 98362306a36Sopenharmony_ci ppgtt->vm.has_read_only = !IS_GRAPHICS_VER(gt->i915, 11, 12); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci if (HAS_LMEM(gt->i915)) 98662306a36Sopenharmony_ci ppgtt->vm.alloc_pt_dma = alloc_pt_lmem; 98762306a36Sopenharmony_ci else 98862306a36Sopenharmony_ci ppgtt->vm.alloc_pt_dma = alloc_pt_dma; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci /* 99162306a36Sopenharmony_ci * Using SMEM here instead of LMEM has the advantage of not reserving 99262306a36Sopenharmony_ci * high performance memory for a "never" used filler page. It also 99362306a36Sopenharmony_ci * removes the device access that would be required to initialise the 99462306a36Sopenharmony_ci * scratch page, reducing pressure on an even scarcer resource. 99562306a36Sopenharmony_ci */ 99662306a36Sopenharmony_ci ppgtt->vm.alloc_scratch_dma = alloc_pt_dma; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci if (GRAPHICS_VER(gt->i915) >= 12) 99962306a36Sopenharmony_ci ppgtt->vm.pte_encode = gen12_pte_encode; 100062306a36Sopenharmony_ci else 100162306a36Sopenharmony_ci ppgtt->vm.pte_encode = gen8_pte_encode; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci ppgtt->vm.bind_async_flags = I915_VMA_LOCAL_BIND; 100462306a36Sopenharmony_ci ppgtt->vm.insert_entries = gen8_ppgtt_insert; 100562306a36Sopenharmony_ci if (HAS_64K_PAGES(gt->i915)) 100662306a36Sopenharmony_ci ppgtt->vm.insert_page = xehpsdv_ppgtt_insert_entry; 100762306a36Sopenharmony_ci else 100862306a36Sopenharmony_ci ppgtt->vm.insert_page = gen8_ppgtt_insert_entry; 100962306a36Sopenharmony_ci ppgtt->vm.allocate_va_range = gen8_ppgtt_alloc; 101062306a36Sopenharmony_ci ppgtt->vm.clear_range = gen8_ppgtt_clear; 101162306a36Sopenharmony_ci ppgtt->vm.foreach = gen8_ppgtt_foreach; 101262306a36Sopenharmony_ci ppgtt->vm.cleanup = gen8_ppgtt_cleanup; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci err = gen8_init_scratch(&ppgtt->vm); 101562306a36Sopenharmony_ci if (err) 101662306a36Sopenharmony_ci goto err_put; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci pd = gen8_alloc_top_pd(&ppgtt->vm); 101962306a36Sopenharmony_ci if (IS_ERR(pd)) { 102062306a36Sopenharmony_ci err = PTR_ERR(pd); 102162306a36Sopenharmony_ci goto err_put; 102262306a36Sopenharmony_ci } 102362306a36Sopenharmony_ci ppgtt->pd = pd; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci if (!i915_vm_is_4lvl(&ppgtt->vm)) { 102662306a36Sopenharmony_ci err = gen8_preallocate_top_level_pdp(ppgtt); 102762306a36Sopenharmony_ci if (err) 102862306a36Sopenharmony_ci goto err_put; 102962306a36Sopenharmony_ci } 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci if (intel_vgpu_active(gt->i915)) 103262306a36Sopenharmony_ci gen8_ppgtt_notify_vgt(ppgtt, true); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci return ppgtt; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_cierr_put: 103762306a36Sopenharmony_ci i915_vm_put(&ppgtt->vm); 103862306a36Sopenharmony_ci return ERR_PTR(err); 103962306a36Sopenharmony_ci} 1040