162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Low-level SPU handling 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Arnd Bergmann <arndb@de.ibm.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/sched/signal.h> 1062306a36Sopenharmony_ci#include <linux/mm.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <asm/spu.h> 1362306a36Sopenharmony_ci#include <asm/spu_csa.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "spufs.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/** 1862306a36Sopenharmony_ci * Handle an SPE event, depending on context SPU_CREATE_EVENTS_ENABLED flag. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * If the context was created with events, we just set the return event. 2162306a36Sopenharmony_ci * Otherwise, send an appropriate signal to the process. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_cistatic void spufs_handle_event(struct spu_context *ctx, 2462306a36Sopenharmony_ci unsigned long ea, int type) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci if (ctx->flags & SPU_CREATE_EVENTS_ENABLED) { 2762306a36Sopenharmony_ci ctx->event_return |= type; 2862306a36Sopenharmony_ci wake_up_all(&ctx->stop_wq); 2962306a36Sopenharmony_ci return; 3062306a36Sopenharmony_ci } 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci switch (type) { 3362306a36Sopenharmony_ci case SPE_EVENT_INVALID_DMA: 3462306a36Sopenharmony_ci force_sig_fault(SIGBUS, BUS_OBJERR, NULL); 3562306a36Sopenharmony_ci break; 3662306a36Sopenharmony_ci case SPE_EVENT_SPE_DATA_STORAGE: 3762306a36Sopenharmony_ci ctx->ops->restart_dma(ctx); 3862306a36Sopenharmony_ci force_sig_fault(SIGSEGV, SEGV_ACCERR, (void __user *)ea); 3962306a36Sopenharmony_ci break; 4062306a36Sopenharmony_ci case SPE_EVENT_DMA_ALIGNMENT: 4162306a36Sopenharmony_ci /* DAR isn't set for an alignment fault :( */ 4262306a36Sopenharmony_ci force_sig_fault(SIGBUS, BUS_ADRALN, NULL); 4362306a36Sopenharmony_ci break; 4462306a36Sopenharmony_ci case SPE_EVENT_SPE_ERROR: 4562306a36Sopenharmony_ci force_sig_fault( 4662306a36Sopenharmony_ci SIGILL, ILL_ILLOPC, 4762306a36Sopenharmony_ci (void __user *)(unsigned long) 4862306a36Sopenharmony_ci ctx->ops->npc_read(ctx) - 4); 4962306a36Sopenharmony_ci break; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ciint spufs_handle_class0(struct spu_context *ctx) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci unsigned long stat = ctx->csa.class_0_pending & CLASS0_INTR_MASK; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (likely(!stat)) 5862306a36Sopenharmony_ci return 0; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if (stat & CLASS0_DMA_ALIGNMENT_INTR) 6162306a36Sopenharmony_ci spufs_handle_event(ctx, ctx->csa.class_0_dar, 6262306a36Sopenharmony_ci SPE_EVENT_DMA_ALIGNMENT); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (stat & CLASS0_INVALID_DMA_COMMAND_INTR) 6562306a36Sopenharmony_ci spufs_handle_event(ctx, ctx->csa.class_0_dar, 6662306a36Sopenharmony_ci SPE_EVENT_INVALID_DMA); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (stat & CLASS0_SPU_ERROR_INTR) 6962306a36Sopenharmony_ci spufs_handle_event(ctx, ctx->csa.class_0_dar, 7062306a36Sopenharmony_ci SPE_EVENT_SPE_ERROR); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci ctx->csa.class_0_pending = 0; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return -EIO; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* 7862306a36Sopenharmony_ci * bottom half handler for page faults, we can't do this from 7962306a36Sopenharmony_ci * interrupt context, since we might need to sleep. 8062306a36Sopenharmony_ci * we also need to give up the mutex so we can get scheduled 8162306a36Sopenharmony_ci * out while waiting for the backing store. 8262306a36Sopenharmony_ci * 8362306a36Sopenharmony_ci * TODO: try calling hash_page from the interrupt handler first 8462306a36Sopenharmony_ci * in order to speed up the easy case. 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_ciint spufs_handle_class1(struct spu_context *ctx) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci u64 ea, dsisr, access; 8962306a36Sopenharmony_ci unsigned long flags; 9062306a36Sopenharmony_ci vm_fault_t flt = 0; 9162306a36Sopenharmony_ci int ret; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* 9462306a36Sopenharmony_ci * dar and dsisr get passed from the registers 9562306a36Sopenharmony_ci * to the spu_context, to this function, but not 9662306a36Sopenharmony_ci * back to the spu if it gets scheduled again. 9762306a36Sopenharmony_ci * 9862306a36Sopenharmony_ci * if we don't handle the fault for a saved context 9962306a36Sopenharmony_ci * in time, we can still expect to get the same fault 10062306a36Sopenharmony_ci * the immediately after the context restore. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ci ea = ctx->csa.class_1_dar; 10362306a36Sopenharmony_ci dsisr = ctx->csa.class_1_dsisr; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (!(dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED))) 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci spuctx_switch_state(ctx, SPU_UTIL_IOWAIT); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci pr_debug("ctx %p: ea %016llx, dsisr %016llx state %d\n", ctx, ea, 11162306a36Sopenharmony_ci dsisr, ctx->state); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci ctx->stats.hash_flt++; 11462306a36Sopenharmony_ci if (ctx->state == SPU_STATE_RUNNABLE) 11562306a36Sopenharmony_ci ctx->spu->stats.hash_flt++; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* we must not hold the lock when entering copro_handle_mm_fault */ 11862306a36Sopenharmony_ci spu_release(ctx); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci access = (_PAGE_PRESENT | _PAGE_READ); 12162306a36Sopenharmony_ci access |= (dsisr & MFC_DSISR_ACCESS_PUT) ? _PAGE_WRITE : 0UL; 12262306a36Sopenharmony_ci local_irq_save(flags); 12362306a36Sopenharmony_ci ret = hash_page(ea, access, 0x300, dsisr); 12462306a36Sopenharmony_ci local_irq_restore(flags); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* hashing failed, so try the actual fault handler */ 12762306a36Sopenharmony_ci if (ret) 12862306a36Sopenharmony_ci ret = copro_handle_mm_fault(current->mm, ea, dsisr, &flt); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* 13162306a36Sopenharmony_ci * This is nasty: we need the state_mutex for all the bookkeeping even 13262306a36Sopenharmony_ci * if the syscall was interrupted by a signal. ewww. 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_ci mutex_lock(&ctx->state_mutex); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* 13762306a36Sopenharmony_ci * Clear dsisr under ctxt lock after handling the fault, so that 13862306a36Sopenharmony_ci * time slicing will not preempt the context while the page fault 13962306a36Sopenharmony_ci * handler is running. Context switch code removes mappings. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_ci ctx->csa.class_1_dar = ctx->csa.class_1_dsisr = 0; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* 14462306a36Sopenharmony_ci * If we handled the fault successfully and are in runnable 14562306a36Sopenharmony_ci * state, restart the DMA. 14662306a36Sopenharmony_ci * In case of unhandled error report the problem to user space. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci if (!ret) { 14962306a36Sopenharmony_ci if (flt & VM_FAULT_MAJOR) 15062306a36Sopenharmony_ci ctx->stats.maj_flt++; 15162306a36Sopenharmony_ci else 15262306a36Sopenharmony_ci ctx->stats.min_flt++; 15362306a36Sopenharmony_ci if (ctx->state == SPU_STATE_RUNNABLE) { 15462306a36Sopenharmony_ci if (flt & VM_FAULT_MAJOR) 15562306a36Sopenharmony_ci ctx->spu->stats.maj_flt++; 15662306a36Sopenharmony_ci else 15762306a36Sopenharmony_ci ctx->spu->stats.min_flt++; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (ctx->spu) 16162306a36Sopenharmony_ci ctx->ops->restart_dma(ctx); 16262306a36Sopenharmony_ci } else 16362306a36Sopenharmony_ci spufs_handle_event(ctx, ea, SPE_EVENT_SPE_DATA_STORAGE); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci spuctx_switch_state(ctx, SPU_UTIL_SYSTEM); 16662306a36Sopenharmony_ci return ret; 16762306a36Sopenharmony_ci} 168