162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2014 IBM Corp. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/workqueue.h> 762306a36Sopenharmony_ci#include <linux/sched/signal.h> 862306a36Sopenharmony_ci#include <linux/sched/mm.h> 962306a36Sopenharmony_ci#include <linux/pid.h> 1062306a36Sopenharmony_ci#include <linux/mm.h> 1162306a36Sopenharmony_ci#include <linux/moduleparam.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#undef MODULE_PARAM_PREFIX 1462306a36Sopenharmony_ci#define MODULE_PARAM_PREFIX "cxl" "." 1562306a36Sopenharmony_ci#include <asm/current.h> 1662306a36Sopenharmony_ci#include <asm/copro.h> 1762306a36Sopenharmony_ci#include <asm/mmu.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "cxl.h" 2062306a36Sopenharmony_ci#include "trace.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic bool sste_matches(struct cxl_sste *sste, struct copro_slb *slb) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci return ((sste->vsid_data == cpu_to_be64(slb->vsid)) && 2562306a36Sopenharmony_ci (sste->esid_data == cpu_to_be64(slb->esid))); 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * This finds a free SSTE for the given SLB, or returns NULL if it's already in 3062306a36Sopenharmony_ci * the segment table. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_cistatic struct cxl_sste *find_free_sste(struct cxl_context *ctx, 3362306a36Sopenharmony_ci struct copro_slb *slb) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct cxl_sste *primary, *sste, *ret = NULL; 3662306a36Sopenharmony_ci unsigned int mask = (ctx->sst_size >> 7) - 1; /* SSTP0[SegTableSize] */ 3762306a36Sopenharmony_ci unsigned int entry; 3862306a36Sopenharmony_ci unsigned int hash; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (slb->vsid & SLB_VSID_B_1T) 4162306a36Sopenharmony_ci hash = (slb->esid >> SID_SHIFT_1T) & mask; 4262306a36Sopenharmony_ci else /* 256M */ 4362306a36Sopenharmony_ci hash = (slb->esid >> SID_SHIFT) & mask; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci primary = ctx->sstp + (hash << 3); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci for (entry = 0, sste = primary; entry < 8; entry++, sste++) { 4862306a36Sopenharmony_ci if (!ret && !(be64_to_cpu(sste->esid_data) & SLB_ESID_V)) 4962306a36Sopenharmony_ci ret = sste; 5062306a36Sopenharmony_ci if (sste_matches(sste, slb)) 5162306a36Sopenharmony_ci return NULL; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci if (ret) 5462306a36Sopenharmony_ci return ret; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* Nothing free, select an entry to cast out */ 5762306a36Sopenharmony_ci ret = primary + ctx->sst_lru; 5862306a36Sopenharmony_ci ctx->sst_lru = (ctx->sst_lru + 1) & 0x7; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return ret; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void cxl_load_segment(struct cxl_context *ctx, struct copro_slb *slb) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci /* mask is the group index, we search primary and secondary here. */ 6662306a36Sopenharmony_ci struct cxl_sste *sste; 6762306a36Sopenharmony_ci unsigned long flags; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci spin_lock_irqsave(&ctx->sste_lock, flags); 7062306a36Sopenharmony_ci sste = find_free_sste(ctx, slb); 7162306a36Sopenharmony_ci if (!sste) 7262306a36Sopenharmony_ci goto out_unlock; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci pr_devel("CXL Populating SST[%li]: %#llx %#llx\n", 7562306a36Sopenharmony_ci sste - ctx->sstp, slb->vsid, slb->esid); 7662306a36Sopenharmony_ci trace_cxl_ste_write(ctx, sste - ctx->sstp, slb->esid, slb->vsid); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci sste->vsid_data = cpu_to_be64(slb->vsid); 7962306a36Sopenharmony_ci sste->esid_data = cpu_to_be64(slb->esid); 8062306a36Sopenharmony_ciout_unlock: 8162306a36Sopenharmony_ci spin_unlock_irqrestore(&ctx->sste_lock, flags); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int cxl_fault_segment(struct cxl_context *ctx, struct mm_struct *mm, 8562306a36Sopenharmony_ci u64 ea) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct copro_slb slb = {0,0}; 8862306a36Sopenharmony_ci int rc; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (!(rc = copro_calculate_slb(mm, ea, &slb))) { 9162306a36Sopenharmony_ci cxl_load_segment(ctx, &slb); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return rc; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void cxl_ack_ae(struct cxl_context *ctx) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci unsigned long flags; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci cxl_ops->ack_irq(ctx, CXL_PSL_TFC_An_AE, 0); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci spin_lock_irqsave(&ctx->lock, flags); 10462306a36Sopenharmony_ci ctx->pending_fault = true; 10562306a36Sopenharmony_ci ctx->fault_addr = ctx->dar; 10662306a36Sopenharmony_ci ctx->fault_dsisr = ctx->dsisr; 10762306a36Sopenharmony_ci spin_unlock_irqrestore(&ctx->lock, flags); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci wake_up_all(&ctx->wq); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic int cxl_handle_segment_miss(struct cxl_context *ctx, 11362306a36Sopenharmony_ci struct mm_struct *mm, u64 ea) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci int rc; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci pr_devel("CXL interrupt: Segment fault pe: %i ea: %#llx\n", ctx->pe, ea); 11862306a36Sopenharmony_ci trace_cxl_ste_miss(ctx, ea); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if ((rc = cxl_fault_segment(ctx, mm, ea))) 12162306a36Sopenharmony_ci cxl_ack_ae(ctx); 12262306a36Sopenharmony_ci else { 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci mb(); /* Order seg table write to TFC MMIO write */ 12562306a36Sopenharmony_ci cxl_ops->ack_irq(ctx, CXL_PSL_TFC_An_R, 0); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return IRQ_HANDLED; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ciint cxl_handle_mm_fault(struct mm_struct *mm, u64 dsisr, u64 dar) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci vm_fault_t flt = 0; 13462306a36Sopenharmony_ci int result; 13562306a36Sopenharmony_ci unsigned long access, flags, inv_flags = 0; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* 13862306a36Sopenharmony_ci * Add the fault handling cpu to task mm cpumask so that we 13962306a36Sopenharmony_ci * can do a safe lockless page table walk when inserting the 14062306a36Sopenharmony_ci * hash page table entry. This function get called with a 14162306a36Sopenharmony_ci * valid mm for user space addresses. Hence using the if (mm) 14262306a36Sopenharmony_ci * check is sufficient here. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci if (mm && !cpumask_test_cpu(smp_processor_id(), mm_cpumask(mm))) { 14562306a36Sopenharmony_ci cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm)); 14662306a36Sopenharmony_ci /* 14762306a36Sopenharmony_ci * We need to make sure we walk the table only after 14862306a36Sopenharmony_ci * we update the cpumask. The other side of the barrier 14962306a36Sopenharmony_ci * is explained in serialize_against_pte_lookup() 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_ci smp_mb(); 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci if ((result = copro_handle_mm_fault(mm, dar, dsisr, &flt))) { 15462306a36Sopenharmony_ci pr_devel("copro_handle_mm_fault failed: %#x\n", result); 15562306a36Sopenharmony_ci return result; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (!radix_enabled()) { 15962306a36Sopenharmony_ci /* 16062306a36Sopenharmony_ci * update_mmu_cache() will not have loaded the hash since current->trap 16162306a36Sopenharmony_ci * is not a 0x400 or 0x300, so just call hash_page_mm() here. 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_ci access = _PAGE_PRESENT | _PAGE_READ; 16462306a36Sopenharmony_ci if (dsisr & CXL_PSL_DSISR_An_S) 16562306a36Sopenharmony_ci access |= _PAGE_WRITE; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (!mm && (get_region_id(dar) != USER_REGION_ID)) 16862306a36Sopenharmony_ci access |= _PAGE_PRIVILEGED; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (dsisr & DSISR_NOHPTE) 17162306a36Sopenharmony_ci inv_flags |= HPTE_NOHPTE_UPDATE; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci local_irq_save(flags); 17462306a36Sopenharmony_ci hash_page_mm(mm, dar, access, 0x300, inv_flags); 17562306a36Sopenharmony_ci local_irq_restore(flags); 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci return 0; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void cxl_handle_page_fault(struct cxl_context *ctx, 18162306a36Sopenharmony_ci struct mm_struct *mm, 18262306a36Sopenharmony_ci u64 dsisr, u64 dar) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci trace_cxl_pte_miss(ctx, dsisr, dar); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (cxl_handle_mm_fault(mm, dsisr, dar)) { 18762306a36Sopenharmony_ci cxl_ack_ae(ctx); 18862306a36Sopenharmony_ci } else { 18962306a36Sopenharmony_ci pr_devel("Page fault successfully handled for pe: %i!\n", ctx->pe); 19062306a36Sopenharmony_ci cxl_ops->ack_irq(ctx, CXL_PSL_TFC_An_R, 0); 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* 19562306a36Sopenharmony_ci * Returns the mm_struct corresponding to the context ctx. 19662306a36Sopenharmony_ci * mm_users == 0, the context may be in the process of being closed. 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_cistatic struct mm_struct *get_mem_context(struct cxl_context *ctx) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci if (ctx->mm == NULL) 20162306a36Sopenharmony_ci return NULL; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (!mmget_not_zero(ctx->mm)) 20462306a36Sopenharmony_ci return NULL; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return ctx->mm; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic bool cxl_is_segment_miss(struct cxl_context *ctx, u64 dsisr) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci if ((cxl_is_power8() && (dsisr & CXL_PSL_DSISR_An_DS))) 21262306a36Sopenharmony_ci return true; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return false; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic bool cxl_is_page_fault(struct cxl_context *ctx, u64 dsisr) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci if ((cxl_is_power8()) && (dsisr & CXL_PSL_DSISR_An_DM)) 22062306a36Sopenharmony_ci return true; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (cxl_is_power9()) 22362306a36Sopenharmony_ci return true; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return false; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_civoid cxl_handle_fault(struct work_struct *fault_work) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct cxl_context *ctx = 23162306a36Sopenharmony_ci container_of(fault_work, struct cxl_context, fault_work); 23262306a36Sopenharmony_ci u64 dsisr = ctx->dsisr; 23362306a36Sopenharmony_ci u64 dar = ctx->dar; 23462306a36Sopenharmony_ci struct mm_struct *mm = NULL; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_HVMODE)) { 23762306a36Sopenharmony_ci if (cxl_p2n_read(ctx->afu, CXL_PSL_DSISR_An) != dsisr || 23862306a36Sopenharmony_ci cxl_p2n_read(ctx->afu, CXL_PSL_DAR_An) != dar || 23962306a36Sopenharmony_ci cxl_p2n_read(ctx->afu, CXL_PSL_PEHandle_An) != ctx->pe) { 24062306a36Sopenharmony_ci /* Most likely explanation is harmless - a dedicated 24162306a36Sopenharmony_ci * process has detached and these were cleared by the 24262306a36Sopenharmony_ci * PSL purge, but warn about it just in case 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci dev_notice(&ctx->afu->dev, "cxl_handle_fault: Translation fault regs changed\n"); 24562306a36Sopenharmony_ci return; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* Early return if the context is being / has been detached */ 25062306a36Sopenharmony_ci if (ctx->status == CLOSED) { 25162306a36Sopenharmony_ci cxl_ack_ae(ctx); 25262306a36Sopenharmony_ci return; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci pr_devel("CXL BOTTOM HALF handling fault for afu pe: %i. " 25662306a36Sopenharmony_ci "DSISR: %#llx DAR: %#llx\n", ctx->pe, dsisr, dar); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (!ctx->kernel) { 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci mm = get_mem_context(ctx); 26162306a36Sopenharmony_ci if (mm == NULL) { 26262306a36Sopenharmony_ci pr_devel("%s: unable to get mm for pe=%d pid=%i\n", 26362306a36Sopenharmony_ci __func__, ctx->pe, pid_nr(ctx->pid)); 26462306a36Sopenharmony_ci cxl_ack_ae(ctx); 26562306a36Sopenharmony_ci return; 26662306a36Sopenharmony_ci } else { 26762306a36Sopenharmony_ci pr_devel("Handling page fault for pe=%d pid=%i\n", 26862306a36Sopenharmony_ci ctx->pe, pid_nr(ctx->pid)); 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (cxl_is_segment_miss(ctx, dsisr)) 27362306a36Sopenharmony_ci cxl_handle_segment_miss(ctx, mm, dar); 27462306a36Sopenharmony_ci else if (cxl_is_page_fault(ctx, dsisr)) 27562306a36Sopenharmony_ci cxl_handle_page_fault(ctx, mm, dsisr, dar); 27662306a36Sopenharmony_ci else 27762306a36Sopenharmony_ci WARN(1, "cxl_handle_fault has nothing to handle\n"); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (mm) 28062306a36Sopenharmony_ci mmput(mm); 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic u64 next_segment(u64 ea, u64 vsid) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci if (vsid & SLB_VSID_B_1T) 28662306a36Sopenharmony_ci ea |= (1ULL << 40) - 1; 28762306a36Sopenharmony_ci else 28862306a36Sopenharmony_ci ea |= (1ULL << 28) - 1; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return ea + 1; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic void cxl_prefault_vma(struct cxl_context *ctx, struct mm_struct *mm) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci u64 ea, last_esid = 0; 29662306a36Sopenharmony_ci struct copro_slb slb; 29762306a36Sopenharmony_ci VMA_ITERATOR(vmi, mm, 0); 29862306a36Sopenharmony_ci struct vm_area_struct *vma; 29962306a36Sopenharmony_ci int rc; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci mmap_read_lock(mm); 30262306a36Sopenharmony_ci for_each_vma(vmi, vma) { 30362306a36Sopenharmony_ci for (ea = vma->vm_start; ea < vma->vm_end; 30462306a36Sopenharmony_ci ea = next_segment(ea, slb.vsid)) { 30562306a36Sopenharmony_ci rc = copro_calculate_slb(mm, ea, &slb); 30662306a36Sopenharmony_ci if (rc) 30762306a36Sopenharmony_ci continue; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (last_esid == slb.esid) 31062306a36Sopenharmony_ci continue; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci cxl_load_segment(ctx, &slb); 31362306a36Sopenharmony_ci last_esid = slb.esid; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci mmap_read_unlock(mm); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_civoid cxl_prefault(struct cxl_context *ctx, u64 wed) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct mm_struct *mm = get_mem_context(ctx); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (mm == NULL) { 32462306a36Sopenharmony_ci pr_devel("cxl_prefault unable to get mm %i\n", 32562306a36Sopenharmony_ci pid_nr(ctx->pid)); 32662306a36Sopenharmony_ci return; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci switch (ctx->afu->prefault_mode) { 33062306a36Sopenharmony_ci case CXL_PREFAULT_WED: 33162306a36Sopenharmony_ci cxl_fault_segment(ctx, mm, wed); 33262306a36Sopenharmony_ci break; 33362306a36Sopenharmony_ci case CXL_PREFAULT_ALL: 33462306a36Sopenharmony_ci cxl_prefault_vma(ctx, mm); 33562306a36Sopenharmony_ci break; 33662306a36Sopenharmony_ci default: 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci mmput(mm); 34162306a36Sopenharmony_ci} 342