18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#define DEBUG 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/wait.h> 58c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <asm/spu.h> 88c2ecf20Sopenharmony_ci#include <asm/spu_priv1.h> 98c2ecf20Sopenharmony_ci#include <asm/io.h> 108c2ecf20Sopenharmony_ci#include <asm/unistd.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "spufs.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* interrupt-level stop callback function. */ 158c2ecf20Sopenharmony_civoid spufs_stop_callback(struct spu *spu, int irq) 168c2ecf20Sopenharmony_ci{ 178c2ecf20Sopenharmony_ci struct spu_context *ctx = spu->ctx; 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci /* 208c2ecf20Sopenharmony_ci * It should be impossible to preempt a context while an exception 218c2ecf20Sopenharmony_ci * is being processed, since the context switch code is specially 228c2ecf20Sopenharmony_ci * coded to deal with interrupts ... But, just in case, sanity check 238c2ecf20Sopenharmony_ci * the context pointer. It is OK to return doing nothing since 248c2ecf20Sopenharmony_ci * the exception will be regenerated when the context is resumed. 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_ci if (ctx) { 278c2ecf20Sopenharmony_ci /* Copy exception arguments into module specific structure */ 288c2ecf20Sopenharmony_ci switch(irq) { 298c2ecf20Sopenharmony_ci case 0 : 308c2ecf20Sopenharmony_ci ctx->csa.class_0_pending = spu->class_0_pending; 318c2ecf20Sopenharmony_ci ctx->csa.class_0_dar = spu->class_0_dar; 328c2ecf20Sopenharmony_ci break; 338c2ecf20Sopenharmony_ci case 1 : 348c2ecf20Sopenharmony_ci ctx->csa.class_1_dsisr = spu->class_1_dsisr; 358c2ecf20Sopenharmony_ci ctx->csa.class_1_dar = spu->class_1_dar; 368c2ecf20Sopenharmony_ci break; 378c2ecf20Sopenharmony_ci case 2 : 388c2ecf20Sopenharmony_ci break; 398c2ecf20Sopenharmony_ci } 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci /* ensure that the exception status has hit memory before a 428c2ecf20Sopenharmony_ci * thread waiting on the context's stop queue is woken */ 438c2ecf20Sopenharmony_ci smp_wmb(); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci wake_up_all(&ctx->stop_wq); 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ciint spu_stopped(struct spu_context *ctx, u32 *stat) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci u64 dsisr; 528c2ecf20Sopenharmony_ci u32 stopped; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci stopped = SPU_STATUS_INVALID_INSTR | SPU_STATUS_SINGLE_STEP | 558c2ecf20Sopenharmony_ci SPU_STATUS_STOPPED_BY_HALT | SPU_STATUS_STOPPED_BY_STOP; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_citop: 588c2ecf20Sopenharmony_ci *stat = ctx->ops->status_read(ctx); 598c2ecf20Sopenharmony_ci if (*stat & stopped) { 608c2ecf20Sopenharmony_ci /* 618c2ecf20Sopenharmony_ci * If the spu hasn't finished stopping, we need to 628c2ecf20Sopenharmony_ci * re-read the register to get the stopped value. 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_ci if (*stat & SPU_STATUS_RUNNING) 658c2ecf20Sopenharmony_ci goto top; 668c2ecf20Sopenharmony_ci return 1; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (test_bit(SPU_SCHED_NOTIFY_ACTIVE, &ctx->sched_flags)) 708c2ecf20Sopenharmony_ci return 1; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci dsisr = ctx->csa.class_1_dsisr; 738c2ecf20Sopenharmony_ci if (dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED)) 748c2ecf20Sopenharmony_ci return 1; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (ctx->csa.class_0_pending) 778c2ecf20Sopenharmony_ci return 1; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci return 0; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int spu_setup_isolated(struct spu_context *ctx) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci int ret; 858c2ecf20Sopenharmony_ci u64 __iomem *mfc_cntl; 868c2ecf20Sopenharmony_ci u64 sr1; 878c2ecf20Sopenharmony_ci u32 status; 888c2ecf20Sopenharmony_ci unsigned long timeout; 898c2ecf20Sopenharmony_ci const u32 status_loading = SPU_STATUS_RUNNING 908c2ecf20Sopenharmony_ci | SPU_STATUS_ISOLATED_STATE | SPU_STATUS_ISOLATED_LOAD_STATUS; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci ret = -ENODEV; 938c2ecf20Sopenharmony_ci if (!isolated_loader) 948c2ecf20Sopenharmony_ci goto out; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* 978c2ecf20Sopenharmony_ci * We need to exclude userspace access to the context. 988c2ecf20Sopenharmony_ci * 998c2ecf20Sopenharmony_ci * To protect against memory access we invalidate all ptes 1008c2ecf20Sopenharmony_ci * and make sure the pagefault handlers block on the mutex. 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci spu_unmap_mappings(ctx); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci mfc_cntl = &ctx->spu->priv2->mfc_control_RW; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* purge the MFC DMA queue to ensure no spurious accesses before we 1078c2ecf20Sopenharmony_ci * enter kernel mode */ 1088c2ecf20Sopenharmony_ci timeout = jiffies + HZ; 1098c2ecf20Sopenharmony_ci out_be64(mfc_cntl, MFC_CNTL_PURGE_DMA_REQUEST); 1108c2ecf20Sopenharmony_ci while ((in_be64(mfc_cntl) & MFC_CNTL_PURGE_DMA_STATUS_MASK) 1118c2ecf20Sopenharmony_ci != MFC_CNTL_PURGE_DMA_COMPLETE) { 1128c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 1138c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: timeout flushing MFC DMA queue\n", 1148c2ecf20Sopenharmony_ci __func__); 1158c2ecf20Sopenharmony_ci ret = -EIO; 1168c2ecf20Sopenharmony_ci goto out; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci cond_resched(); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* clear purge status */ 1228c2ecf20Sopenharmony_ci out_be64(mfc_cntl, 0); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* put the SPE in kernel mode to allow access to the loader */ 1258c2ecf20Sopenharmony_ci sr1 = spu_mfc_sr1_get(ctx->spu); 1268c2ecf20Sopenharmony_ci sr1 &= ~MFC_STATE1_PROBLEM_STATE_MASK; 1278c2ecf20Sopenharmony_ci spu_mfc_sr1_set(ctx->spu, sr1); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* start the loader */ 1308c2ecf20Sopenharmony_ci ctx->ops->signal1_write(ctx, (unsigned long)isolated_loader >> 32); 1318c2ecf20Sopenharmony_ci ctx->ops->signal2_write(ctx, 1328c2ecf20Sopenharmony_ci (unsigned long)isolated_loader & 0xffffffff); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci ctx->ops->runcntl_write(ctx, 1358c2ecf20Sopenharmony_ci SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci ret = 0; 1388c2ecf20Sopenharmony_ci timeout = jiffies + HZ; 1398c2ecf20Sopenharmony_ci while (((status = ctx->ops->status_read(ctx)) & status_loading) == 1408c2ecf20Sopenharmony_ci status_loading) { 1418c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 1428c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: timeout waiting for loader\n", 1438c2ecf20Sopenharmony_ci __func__); 1448c2ecf20Sopenharmony_ci ret = -EIO; 1458c2ecf20Sopenharmony_ci goto out_drop_priv; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci cond_resched(); 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (!(status & SPU_STATUS_RUNNING)) { 1518c2ecf20Sopenharmony_ci /* If isolated LOAD has failed: run SPU, we will get a stop-and 1528c2ecf20Sopenharmony_ci * signal later. */ 1538c2ecf20Sopenharmony_ci pr_debug("%s: isolated LOAD failed\n", __func__); 1548c2ecf20Sopenharmony_ci ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE); 1558c2ecf20Sopenharmony_ci ret = -EACCES; 1568c2ecf20Sopenharmony_ci goto out_drop_priv; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (!(status & SPU_STATUS_ISOLATED_STATE)) { 1608c2ecf20Sopenharmony_ci /* This isn't allowed by the CBEA, but check anyway */ 1618c2ecf20Sopenharmony_ci pr_debug("%s: SPU fell out of isolated mode?\n", __func__); 1628c2ecf20Sopenharmony_ci ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_STOP); 1638c2ecf20Sopenharmony_ci ret = -EINVAL; 1648c2ecf20Sopenharmony_ci goto out_drop_priv; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ciout_drop_priv: 1688c2ecf20Sopenharmony_ci /* Finished accessing the loader. Drop kernel mode */ 1698c2ecf20Sopenharmony_ci sr1 |= MFC_STATE1_PROBLEM_STATE_MASK; 1708c2ecf20Sopenharmony_ci spu_mfc_sr1_set(ctx->spu, sr1); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ciout: 1738c2ecf20Sopenharmony_ci return ret; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int spu_run_init(struct spu_context *ctx, u32 *npc) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci unsigned long runcntl = SPU_RUNCNTL_RUNNABLE; 1798c2ecf20Sopenharmony_ci int ret; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci spuctx_switch_state(ctx, SPU_UTIL_SYSTEM); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* 1848c2ecf20Sopenharmony_ci * NOSCHED is synchronous scheduling with respect to the caller. 1858c2ecf20Sopenharmony_ci * The caller waits for the context to be loaded. 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_ci if (ctx->flags & SPU_CREATE_NOSCHED) { 1888c2ecf20Sopenharmony_ci if (ctx->state == SPU_STATE_SAVED) { 1898c2ecf20Sopenharmony_ci ret = spu_activate(ctx, 0); 1908c2ecf20Sopenharmony_ci if (ret) 1918c2ecf20Sopenharmony_ci return ret; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* 1968c2ecf20Sopenharmony_ci * Apply special setup as required. 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_ci if (ctx->flags & SPU_CREATE_ISOLATE) { 1998c2ecf20Sopenharmony_ci if (!(ctx->ops->status_read(ctx) & SPU_STATUS_ISOLATED_STATE)) { 2008c2ecf20Sopenharmony_ci ret = spu_setup_isolated(ctx); 2018c2ecf20Sopenharmony_ci if (ret) 2028c2ecf20Sopenharmony_ci return ret; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* 2068c2ecf20Sopenharmony_ci * If userspace has set the runcntrl register (eg, to 2078c2ecf20Sopenharmony_ci * issue an isolated exit), we need to re-set it here 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_ci runcntl = ctx->ops->runcntl_read(ctx) & 2108c2ecf20Sopenharmony_ci (SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE); 2118c2ecf20Sopenharmony_ci if (runcntl == 0) 2128c2ecf20Sopenharmony_ci runcntl = SPU_RUNCNTL_RUNNABLE; 2138c2ecf20Sopenharmony_ci } else { 2148c2ecf20Sopenharmony_ci unsigned long privcntl; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (test_thread_flag(TIF_SINGLESTEP)) 2178c2ecf20Sopenharmony_ci privcntl = SPU_PRIVCNTL_MODE_SINGLE_STEP; 2188c2ecf20Sopenharmony_ci else 2198c2ecf20Sopenharmony_ci privcntl = SPU_PRIVCNTL_MODE_NORMAL; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci ctx->ops->privcntl_write(ctx, privcntl); 2228c2ecf20Sopenharmony_ci ctx->ops->npc_write(ctx, *npc); 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ctx->ops->runcntl_write(ctx, runcntl); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (ctx->flags & SPU_CREATE_NOSCHED) { 2288c2ecf20Sopenharmony_ci spuctx_switch_state(ctx, SPU_UTIL_USER); 2298c2ecf20Sopenharmony_ci } else { 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (ctx->state == SPU_STATE_SAVED) { 2328c2ecf20Sopenharmony_ci ret = spu_activate(ctx, 0); 2338c2ecf20Sopenharmony_ci if (ret) 2348c2ecf20Sopenharmony_ci return ret; 2358c2ecf20Sopenharmony_ci } else { 2368c2ecf20Sopenharmony_ci spuctx_switch_state(ctx, SPU_UTIL_USER); 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci set_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags); 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int spu_run_fini(struct spu_context *ctx, u32 *npc, 2458c2ecf20Sopenharmony_ci u32 *status) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci int ret = 0; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci spu_del_from_rq(ctx); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci *status = ctx->ops->status_read(ctx); 2528c2ecf20Sopenharmony_ci *npc = ctx->ops->npc_read(ctx); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci spuctx_switch_state(ctx, SPU_UTIL_IDLE_LOADED); 2558c2ecf20Sopenharmony_ci clear_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags); 2568c2ecf20Sopenharmony_ci spu_switch_log_notify(NULL, ctx, SWITCH_LOG_EXIT, *status); 2578c2ecf20Sopenharmony_ci spu_release(ctx); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (signal_pending(current)) 2608c2ecf20Sopenharmony_ci ret = -ERESTARTSYS; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return ret; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci/* 2668c2ecf20Sopenharmony_ci * SPU syscall restarting is tricky because we violate the basic 2678c2ecf20Sopenharmony_ci * assumption that the signal handler is running on the interrupted 2688c2ecf20Sopenharmony_ci * thread. Here instead, the handler runs on PowerPC user space code, 2698c2ecf20Sopenharmony_ci * while the syscall was called from the SPU. 2708c2ecf20Sopenharmony_ci * This means we can only do a very rough approximation of POSIX 2718c2ecf20Sopenharmony_ci * signal semantics. 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_cistatic int spu_handle_restartsys(struct spu_context *ctx, long *spu_ret, 2748c2ecf20Sopenharmony_ci unsigned int *npc) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci int ret; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci switch (*spu_ret) { 2798c2ecf20Sopenharmony_ci case -ERESTARTSYS: 2808c2ecf20Sopenharmony_ci case -ERESTARTNOINTR: 2818c2ecf20Sopenharmony_ci /* 2828c2ecf20Sopenharmony_ci * Enter the regular syscall restarting for 2838c2ecf20Sopenharmony_ci * sys_spu_run, then restart the SPU syscall 2848c2ecf20Sopenharmony_ci * callback. 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_ci *npc -= 8; 2878c2ecf20Sopenharmony_ci ret = -ERESTARTSYS; 2888c2ecf20Sopenharmony_ci break; 2898c2ecf20Sopenharmony_ci case -ERESTARTNOHAND: 2908c2ecf20Sopenharmony_ci case -ERESTART_RESTARTBLOCK: 2918c2ecf20Sopenharmony_ci /* 2928c2ecf20Sopenharmony_ci * Restart block is too hard for now, just return -EINTR 2938c2ecf20Sopenharmony_ci * to the SPU. 2948c2ecf20Sopenharmony_ci * ERESTARTNOHAND comes from sys_pause, we also return 2958c2ecf20Sopenharmony_ci * -EINTR from there. 2968c2ecf20Sopenharmony_ci * Assume that we need to be restarted ourselves though. 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_ci *spu_ret = -EINTR; 2998c2ecf20Sopenharmony_ci ret = -ERESTARTSYS; 3008c2ecf20Sopenharmony_ci break; 3018c2ecf20Sopenharmony_ci default: 3028c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: unexpected return code %ld\n", 3038c2ecf20Sopenharmony_ci __func__, *spu_ret); 3048c2ecf20Sopenharmony_ci ret = 0; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci return ret; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic int spu_process_callback(struct spu_context *ctx) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci struct spu_syscall_block s; 3128c2ecf20Sopenharmony_ci u32 ls_pointer, npc; 3138c2ecf20Sopenharmony_ci void __iomem *ls; 3148c2ecf20Sopenharmony_ci long spu_ret; 3158c2ecf20Sopenharmony_ci int ret; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* get syscall block from local store */ 3188c2ecf20Sopenharmony_ci npc = ctx->ops->npc_read(ctx) & ~3; 3198c2ecf20Sopenharmony_ci ls = (void __iomem *)ctx->ops->get_ls(ctx); 3208c2ecf20Sopenharmony_ci ls_pointer = in_be32(ls + npc); 3218c2ecf20Sopenharmony_ci if (ls_pointer > (LS_SIZE - sizeof(s))) 3228c2ecf20Sopenharmony_ci return -EFAULT; 3238c2ecf20Sopenharmony_ci memcpy_fromio(&s, ls + ls_pointer, sizeof(s)); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* do actual syscall without pinning the spu */ 3268c2ecf20Sopenharmony_ci ret = 0; 3278c2ecf20Sopenharmony_ci spu_ret = -ENOSYS; 3288c2ecf20Sopenharmony_ci npc += 4; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (s.nr_ret < NR_syscalls) { 3318c2ecf20Sopenharmony_ci spu_release(ctx); 3328c2ecf20Sopenharmony_ci /* do actual system call from here */ 3338c2ecf20Sopenharmony_ci spu_ret = spu_sys_callback(&s); 3348c2ecf20Sopenharmony_ci if (spu_ret <= -ERESTARTSYS) { 3358c2ecf20Sopenharmony_ci ret = spu_handle_restartsys(ctx, &spu_ret, &npc); 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci mutex_lock(&ctx->state_mutex); 3388c2ecf20Sopenharmony_ci if (ret == -ERESTARTSYS) 3398c2ecf20Sopenharmony_ci return ret; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* need to re-get the ls, as it may have changed when we released the 3438c2ecf20Sopenharmony_ci * spu */ 3448c2ecf20Sopenharmony_ci ls = (void __iomem *)ctx->ops->get_ls(ctx); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* write result, jump over indirect pointer */ 3478c2ecf20Sopenharmony_ci memcpy_toio(ls + ls_pointer, &spu_ret, sizeof(spu_ret)); 3488c2ecf20Sopenharmony_ci ctx->ops->npc_write(ctx, npc); 3498c2ecf20Sopenharmony_ci ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE); 3508c2ecf20Sopenharmony_ci return ret; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cilong spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci int ret; 3568c2ecf20Sopenharmony_ci struct spu *spu; 3578c2ecf20Sopenharmony_ci u32 status; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&ctx->run_mutex)) 3608c2ecf20Sopenharmony_ci return -ERESTARTSYS; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci ctx->event_return = 0; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci ret = spu_acquire(ctx); 3658c2ecf20Sopenharmony_ci if (ret) 3668c2ecf20Sopenharmony_ci goto out_unlock; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci spu_enable_spu(ctx); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci spu_update_sched_info(ctx); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci ret = spu_run_init(ctx, npc); 3738c2ecf20Sopenharmony_ci if (ret) { 3748c2ecf20Sopenharmony_ci spu_release(ctx); 3758c2ecf20Sopenharmony_ci goto out; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci do { 3798c2ecf20Sopenharmony_ci ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status)); 3808c2ecf20Sopenharmony_ci if (unlikely(ret)) { 3818c2ecf20Sopenharmony_ci /* 3828c2ecf20Sopenharmony_ci * This is nasty: we need the state_mutex for all the 3838c2ecf20Sopenharmony_ci * bookkeeping even if the syscall was interrupted by 3848c2ecf20Sopenharmony_ci * a signal. ewww. 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_ci mutex_lock(&ctx->state_mutex); 3878c2ecf20Sopenharmony_ci break; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci spu = ctx->spu; 3908c2ecf20Sopenharmony_ci if (unlikely(test_and_clear_bit(SPU_SCHED_NOTIFY_ACTIVE, 3918c2ecf20Sopenharmony_ci &ctx->sched_flags))) { 3928c2ecf20Sopenharmony_ci if (!(status & SPU_STATUS_STOPPED_BY_STOP)) { 3938c2ecf20Sopenharmony_ci spu_switch_notify(spu, ctx); 3948c2ecf20Sopenharmony_ci continue; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci spuctx_switch_state(ctx, SPU_UTIL_SYSTEM); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if ((status & SPU_STATUS_STOPPED_BY_STOP) && 4018c2ecf20Sopenharmony_ci (status >> SPU_STOP_STATUS_SHIFT == 0x2104)) { 4028c2ecf20Sopenharmony_ci ret = spu_process_callback(ctx); 4038c2ecf20Sopenharmony_ci if (ret) 4048c2ecf20Sopenharmony_ci break; 4058c2ecf20Sopenharmony_ci status &= ~SPU_STATUS_STOPPED_BY_STOP; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci ret = spufs_handle_class1(ctx); 4088c2ecf20Sopenharmony_ci if (ret) 4098c2ecf20Sopenharmony_ci break; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci ret = spufs_handle_class0(ctx); 4128c2ecf20Sopenharmony_ci if (ret) 4138c2ecf20Sopenharmony_ci break; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (signal_pending(current)) 4168c2ecf20Sopenharmony_ci ret = -ERESTARTSYS; 4178c2ecf20Sopenharmony_ci } while (!ret && !(status & (SPU_STATUS_STOPPED_BY_STOP | 4188c2ecf20Sopenharmony_ci SPU_STATUS_STOPPED_BY_HALT | 4198c2ecf20Sopenharmony_ci SPU_STATUS_SINGLE_STEP))); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci spu_disable_spu(ctx); 4228c2ecf20Sopenharmony_ci ret = spu_run_fini(ctx, npc, &status); 4238c2ecf20Sopenharmony_ci spu_yield(ctx); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if ((status & SPU_STATUS_STOPPED_BY_STOP) && 4268c2ecf20Sopenharmony_ci (((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100)) 4278c2ecf20Sopenharmony_ci ctx->stats.libassist++; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if ((ret == 0) || 4308c2ecf20Sopenharmony_ci ((ret == -ERESTARTSYS) && 4318c2ecf20Sopenharmony_ci ((status & SPU_STATUS_STOPPED_BY_HALT) || 4328c2ecf20Sopenharmony_ci (status & SPU_STATUS_SINGLE_STEP) || 4338c2ecf20Sopenharmony_ci ((status & SPU_STATUS_STOPPED_BY_STOP) && 4348c2ecf20Sopenharmony_ci (status >> SPU_STOP_STATUS_SHIFT != 0x2104))))) 4358c2ecf20Sopenharmony_ci ret = status; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* Note: we don't need to force_sig SIGTRAP on single-step 4388c2ecf20Sopenharmony_ci * since we have TIF_SINGLESTEP set, thus the kernel will do 4398c2ecf20Sopenharmony_ci * it upon return from the syscall anyway. 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci if (unlikely(status & SPU_STATUS_SINGLE_STEP)) 4428c2ecf20Sopenharmony_ci ret = -ERESTARTSYS; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci else if (unlikely((status & SPU_STATUS_STOPPED_BY_STOP) 4458c2ecf20Sopenharmony_ci && (status >> SPU_STOP_STATUS_SHIFT) == 0x3fff)) { 4468c2ecf20Sopenharmony_ci force_sig(SIGTRAP); 4478c2ecf20Sopenharmony_ci ret = -ERESTARTSYS; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ciout: 4518c2ecf20Sopenharmony_ci *event = ctx->event_return; 4528c2ecf20Sopenharmony_ciout_unlock: 4538c2ecf20Sopenharmony_ci mutex_unlock(&ctx->run_mutex); 4548c2ecf20Sopenharmony_ci return ret; 4558c2ecf20Sopenharmony_ci} 456