162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#define DEBUG 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/wait.h> 562306a36Sopenharmony_ci#include <linux/ptrace.h> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <asm/spu.h> 862306a36Sopenharmony_ci#include <asm/spu_priv1.h> 962306a36Sopenharmony_ci#include <asm/io.h> 1062306a36Sopenharmony_ci#include <asm/unistd.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "spufs.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* interrupt-level stop callback function. */ 1562306a36Sopenharmony_civoid spufs_stop_callback(struct spu *spu, int irq) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci struct spu_context *ctx = spu->ctx; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci /* 2062306a36Sopenharmony_ci * It should be impossible to preempt a context while an exception 2162306a36Sopenharmony_ci * is being processed, since the context switch code is specially 2262306a36Sopenharmony_ci * coded to deal with interrupts ... But, just in case, sanity check 2362306a36Sopenharmony_ci * the context pointer. It is OK to return doing nothing since 2462306a36Sopenharmony_ci * the exception will be regenerated when the context is resumed. 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci if (ctx) { 2762306a36Sopenharmony_ci /* Copy exception arguments into module specific structure */ 2862306a36Sopenharmony_ci switch(irq) { 2962306a36Sopenharmony_ci case 0 : 3062306a36Sopenharmony_ci ctx->csa.class_0_pending = spu->class_0_pending; 3162306a36Sopenharmony_ci ctx->csa.class_0_dar = spu->class_0_dar; 3262306a36Sopenharmony_ci break; 3362306a36Sopenharmony_ci case 1 : 3462306a36Sopenharmony_ci ctx->csa.class_1_dsisr = spu->class_1_dsisr; 3562306a36Sopenharmony_ci ctx->csa.class_1_dar = spu->class_1_dar; 3662306a36Sopenharmony_ci break; 3762306a36Sopenharmony_ci case 2 : 3862306a36Sopenharmony_ci break; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci /* ensure that the exception status has hit memory before a 4262306a36Sopenharmony_ci * thread waiting on the context's stop queue is woken */ 4362306a36Sopenharmony_ci smp_wmb(); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci wake_up_all(&ctx->stop_wq); 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ciint spu_stopped(struct spu_context *ctx, u32 *stat) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci u64 dsisr; 5262306a36Sopenharmony_ci u32 stopped; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci stopped = SPU_STATUS_INVALID_INSTR | SPU_STATUS_SINGLE_STEP | 5562306a36Sopenharmony_ci SPU_STATUS_STOPPED_BY_HALT | SPU_STATUS_STOPPED_BY_STOP; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_citop: 5862306a36Sopenharmony_ci *stat = ctx->ops->status_read(ctx); 5962306a36Sopenharmony_ci if (*stat & stopped) { 6062306a36Sopenharmony_ci /* 6162306a36Sopenharmony_ci * If the spu hasn't finished stopping, we need to 6262306a36Sopenharmony_ci * re-read the register to get the stopped value. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci if (*stat & SPU_STATUS_RUNNING) 6562306a36Sopenharmony_ci goto top; 6662306a36Sopenharmony_ci return 1; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (test_bit(SPU_SCHED_NOTIFY_ACTIVE, &ctx->sched_flags)) 7062306a36Sopenharmony_ci return 1; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci dsisr = ctx->csa.class_1_dsisr; 7362306a36Sopenharmony_ci if (dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED)) 7462306a36Sopenharmony_ci return 1; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (ctx->csa.class_0_pending) 7762306a36Sopenharmony_ci return 1; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic int spu_setup_isolated(struct spu_context *ctx) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci int ret; 8562306a36Sopenharmony_ci u64 __iomem *mfc_cntl; 8662306a36Sopenharmony_ci u64 sr1; 8762306a36Sopenharmony_ci u32 status; 8862306a36Sopenharmony_ci unsigned long timeout; 8962306a36Sopenharmony_ci const u32 status_loading = SPU_STATUS_RUNNING 9062306a36Sopenharmony_ci | SPU_STATUS_ISOLATED_STATE | SPU_STATUS_ISOLATED_LOAD_STATUS; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci ret = -ENODEV; 9362306a36Sopenharmony_ci if (!isolated_loader) 9462306a36Sopenharmony_ci goto out; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* 9762306a36Sopenharmony_ci * We need to exclude userspace access to the context. 9862306a36Sopenharmony_ci * 9962306a36Sopenharmony_ci * To protect against memory access we invalidate all ptes 10062306a36Sopenharmony_ci * and make sure the pagefault handlers block on the mutex. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ci spu_unmap_mappings(ctx); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci mfc_cntl = &ctx->spu->priv2->mfc_control_RW; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* purge the MFC DMA queue to ensure no spurious accesses before we 10762306a36Sopenharmony_ci * enter kernel mode */ 10862306a36Sopenharmony_ci timeout = jiffies + HZ; 10962306a36Sopenharmony_ci out_be64(mfc_cntl, MFC_CNTL_PURGE_DMA_REQUEST); 11062306a36Sopenharmony_ci while ((in_be64(mfc_cntl) & MFC_CNTL_PURGE_DMA_STATUS_MASK) 11162306a36Sopenharmony_ci != MFC_CNTL_PURGE_DMA_COMPLETE) { 11262306a36Sopenharmony_ci if (time_after(jiffies, timeout)) { 11362306a36Sopenharmony_ci printk(KERN_ERR "%s: timeout flushing MFC DMA queue\n", 11462306a36Sopenharmony_ci __func__); 11562306a36Sopenharmony_ci ret = -EIO; 11662306a36Sopenharmony_ci goto out; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci cond_resched(); 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* clear purge status */ 12262306a36Sopenharmony_ci out_be64(mfc_cntl, 0); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* put the SPE in kernel mode to allow access to the loader */ 12562306a36Sopenharmony_ci sr1 = spu_mfc_sr1_get(ctx->spu); 12662306a36Sopenharmony_ci sr1 &= ~MFC_STATE1_PROBLEM_STATE_MASK; 12762306a36Sopenharmony_ci spu_mfc_sr1_set(ctx->spu, sr1); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* start the loader */ 13062306a36Sopenharmony_ci ctx->ops->signal1_write(ctx, (unsigned long)isolated_loader >> 32); 13162306a36Sopenharmony_ci ctx->ops->signal2_write(ctx, 13262306a36Sopenharmony_ci (unsigned long)isolated_loader & 0xffffffff); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci ctx->ops->runcntl_write(ctx, 13562306a36Sopenharmony_ci SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci ret = 0; 13862306a36Sopenharmony_ci timeout = jiffies + HZ; 13962306a36Sopenharmony_ci while (((status = ctx->ops->status_read(ctx)) & status_loading) == 14062306a36Sopenharmony_ci status_loading) { 14162306a36Sopenharmony_ci if (time_after(jiffies, timeout)) { 14262306a36Sopenharmony_ci printk(KERN_ERR "%s: timeout waiting for loader\n", 14362306a36Sopenharmony_ci __func__); 14462306a36Sopenharmony_ci ret = -EIO; 14562306a36Sopenharmony_ci goto out_drop_priv; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci cond_resched(); 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (!(status & SPU_STATUS_RUNNING)) { 15162306a36Sopenharmony_ci /* If isolated LOAD has failed: run SPU, we will get a stop-and 15262306a36Sopenharmony_ci * signal later. */ 15362306a36Sopenharmony_ci pr_debug("%s: isolated LOAD failed\n", __func__); 15462306a36Sopenharmony_ci ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE); 15562306a36Sopenharmony_ci ret = -EACCES; 15662306a36Sopenharmony_ci goto out_drop_priv; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (!(status & SPU_STATUS_ISOLATED_STATE)) { 16062306a36Sopenharmony_ci /* This isn't allowed by the CBEA, but check anyway */ 16162306a36Sopenharmony_ci pr_debug("%s: SPU fell out of isolated mode?\n", __func__); 16262306a36Sopenharmony_ci ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_STOP); 16362306a36Sopenharmony_ci ret = -EINVAL; 16462306a36Sopenharmony_ci goto out_drop_priv; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ciout_drop_priv: 16862306a36Sopenharmony_ci /* Finished accessing the loader. Drop kernel mode */ 16962306a36Sopenharmony_ci sr1 |= MFC_STATE1_PROBLEM_STATE_MASK; 17062306a36Sopenharmony_ci spu_mfc_sr1_set(ctx->spu, sr1); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ciout: 17362306a36Sopenharmony_ci return ret; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic int spu_run_init(struct spu_context *ctx, u32 *npc) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci unsigned long runcntl = SPU_RUNCNTL_RUNNABLE; 17962306a36Sopenharmony_ci int ret; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci spuctx_switch_state(ctx, SPU_UTIL_SYSTEM); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* 18462306a36Sopenharmony_ci * NOSCHED is synchronous scheduling with respect to the caller. 18562306a36Sopenharmony_ci * The caller waits for the context to be loaded. 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_ci if (ctx->flags & SPU_CREATE_NOSCHED) { 18862306a36Sopenharmony_ci if (ctx->state == SPU_STATE_SAVED) { 18962306a36Sopenharmony_ci ret = spu_activate(ctx, 0); 19062306a36Sopenharmony_ci if (ret) 19162306a36Sopenharmony_ci return ret; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* 19662306a36Sopenharmony_ci * Apply special setup as required. 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_ci if (ctx->flags & SPU_CREATE_ISOLATE) { 19962306a36Sopenharmony_ci if (!(ctx->ops->status_read(ctx) & SPU_STATUS_ISOLATED_STATE)) { 20062306a36Sopenharmony_ci ret = spu_setup_isolated(ctx); 20162306a36Sopenharmony_ci if (ret) 20262306a36Sopenharmony_ci return ret; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* 20662306a36Sopenharmony_ci * If userspace has set the runcntrl register (eg, to 20762306a36Sopenharmony_ci * issue an isolated exit), we need to re-set it here 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci runcntl = ctx->ops->runcntl_read(ctx) & 21062306a36Sopenharmony_ci (SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE); 21162306a36Sopenharmony_ci if (runcntl == 0) 21262306a36Sopenharmony_ci runcntl = SPU_RUNCNTL_RUNNABLE; 21362306a36Sopenharmony_ci } else { 21462306a36Sopenharmony_ci unsigned long privcntl; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (test_thread_flag(TIF_SINGLESTEP)) 21762306a36Sopenharmony_ci privcntl = SPU_PRIVCNTL_MODE_SINGLE_STEP; 21862306a36Sopenharmony_ci else 21962306a36Sopenharmony_ci privcntl = SPU_PRIVCNTL_MODE_NORMAL; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci ctx->ops->privcntl_write(ctx, privcntl); 22262306a36Sopenharmony_ci ctx->ops->npc_write(ctx, *npc); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci ctx->ops->runcntl_write(ctx, runcntl); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (ctx->flags & SPU_CREATE_NOSCHED) { 22862306a36Sopenharmony_ci spuctx_switch_state(ctx, SPU_UTIL_USER); 22962306a36Sopenharmony_ci } else { 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (ctx->state == SPU_STATE_SAVED) { 23262306a36Sopenharmony_ci ret = spu_activate(ctx, 0); 23362306a36Sopenharmony_ci if (ret) 23462306a36Sopenharmony_ci return ret; 23562306a36Sopenharmony_ci } else { 23662306a36Sopenharmony_ci spuctx_switch_state(ctx, SPU_UTIL_USER); 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci set_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags); 24162306a36Sopenharmony_ci return 0; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic int spu_run_fini(struct spu_context *ctx, u32 *npc, 24562306a36Sopenharmony_ci u32 *status) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci int ret = 0; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci spu_del_from_rq(ctx); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci *status = ctx->ops->status_read(ctx); 25262306a36Sopenharmony_ci *npc = ctx->ops->npc_read(ctx); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci spuctx_switch_state(ctx, SPU_UTIL_IDLE_LOADED); 25562306a36Sopenharmony_ci clear_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags); 25662306a36Sopenharmony_ci spu_switch_log_notify(NULL, ctx, SWITCH_LOG_EXIT, *status); 25762306a36Sopenharmony_ci spu_release(ctx); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (signal_pending(current)) 26062306a36Sopenharmony_ci ret = -ERESTARTSYS; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return ret; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci/* 26662306a36Sopenharmony_ci * SPU syscall restarting is tricky because we violate the basic 26762306a36Sopenharmony_ci * assumption that the signal handler is running on the interrupted 26862306a36Sopenharmony_ci * thread. Here instead, the handler runs on PowerPC user space code, 26962306a36Sopenharmony_ci * while the syscall was called from the SPU. 27062306a36Sopenharmony_ci * This means we can only do a very rough approximation of POSIX 27162306a36Sopenharmony_ci * signal semantics. 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_cistatic int spu_handle_restartsys(struct spu_context *ctx, long *spu_ret, 27462306a36Sopenharmony_ci unsigned int *npc) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci int ret; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci switch (*spu_ret) { 27962306a36Sopenharmony_ci case -ERESTARTSYS: 28062306a36Sopenharmony_ci case -ERESTARTNOINTR: 28162306a36Sopenharmony_ci /* 28262306a36Sopenharmony_ci * Enter the regular syscall restarting for 28362306a36Sopenharmony_ci * sys_spu_run, then restart the SPU syscall 28462306a36Sopenharmony_ci * callback. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_ci *npc -= 8; 28762306a36Sopenharmony_ci ret = -ERESTARTSYS; 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci case -ERESTARTNOHAND: 29062306a36Sopenharmony_ci case -ERESTART_RESTARTBLOCK: 29162306a36Sopenharmony_ci /* 29262306a36Sopenharmony_ci * Restart block is too hard for now, just return -EINTR 29362306a36Sopenharmony_ci * to the SPU. 29462306a36Sopenharmony_ci * ERESTARTNOHAND comes from sys_pause, we also return 29562306a36Sopenharmony_ci * -EINTR from there. 29662306a36Sopenharmony_ci * Assume that we need to be restarted ourselves though. 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_ci *spu_ret = -EINTR; 29962306a36Sopenharmony_ci ret = -ERESTARTSYS; 30062306a36Sopenharmony_ci break; 30162306a36Sopenharmony_ci default: 30262306a36Sopenharmony_ci printk(KERN_WARNING "%s: unexpected return code %ld\n", 30362306a36Sopenharmony_ci __func__, *spu_ret); 30462306a36Sopenharmony_ci ret = 0; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci return ret; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic int spu_process_callback(struct spu_context *ctx) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct spu_syscall_block s; 31262306a36Sopenharmony_ci u32 ls_pointer, npc; 31362306a36Sopenharmony_ci void __iomem *ls; 31462306a36Sopenharmony_ci long spu_ret; 31562306a36Sopenharmony_ci int ret; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* get syscall block from local store */ 31862306a36Sopenharmony_ci npc = ctx->ops->npc_read(ctx) & ~3; 31962306a36Sopenharmony_ci ls = (void __iomem *)ctx->ops->get_ls(ctx); 32062306a36Sopenharmony_ci ls_pointer = in_be32(ls + npc); 32162306a36Sopenharmony_ci if (ls_pointer > (LS_SIZE - sizeof(s))) 32262306a36Sopenharmony_ci return -EFAULT; 32362306a36Sopenharmony_ci memcpy_fromio(&s, ls + ls_pointer, sizeof(s)); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* do actual syscall without pinning the spu */ 32662306a36Sopenharmony_ci ret = 0; 32762306a36Sopenharmony_ci spu_ret = -ENOSYS; 32862306a36Sopenharmony_ci npc += 4; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (s.nr_ret < NR_syscalls) { 33162306a36Sopenharmony_ci spu_release(ctx); 33262306a36Sopenharmony_ci /* do actual system call from here */ 33362306a36Sopenharmony_ci spu_ret = spu_sys_callback(&s); 33462306a36Sopenharmony_ci if (spu_ret <= -ERESTARTSYS) { 33562306a36Sopenharmony_ci ret = spu_handle_restartsys(ctx, &spu_ret, &npc); 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci mutex_lock(&ctx->state_mutex); 33862306a36Sopenharmony_ci if (ret == -ERESTARTSYS) 33962306a36Sopenharmony_ci return ret; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* need to re-get the ls, as it may have changed when we released the 34362306a36Sopenharmony_ci * spu */ 34462306a36Sopenharmony_ci ls = (void __iomem *)ctx->ops->get_ls(ctx); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* write result, jump over indirect pointer */ 34762306a36Sopenharmony_ci memcpy_toio(ls + ls_pointer, &spu_ret, sizeof(spu_ret)); 34862306a36Sopenharmony_ci ctx->ops->npc_write(ctx, npc); 34962306a36Sopenharmony_ci ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE); 35062306a36Sopenharmony_ci return ret; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cilong spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci int ret; 35662306a36Sopenharmony_ci u32 status; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (mutex_lock_interruptible(&ctx->run_mutex)) 35962306a36Sopenharmony_ci return -ERESTARTSYS; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci ctx->event_return = 0; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci ret = spu_acquire(ctx); 36462306a36Sopenharmony_ci if (ret) 36562306a36Sopenharmony_ci goto out_unlock; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci spu_enable_spu(ctx); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci spu_update_sched_info(ctx); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci ret = spu_run_init(ctx, npc); 37262306a36Sopenharmony_ci if (ret) { 37362306a36Sopenharmony_ci spu_release(ctx); 37462306a36Sopenharmony_ci goto out; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci do { 37862306a36Sopenharmony_ci ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status)); 37962306a36Sopenharmony_ci if (unlikely(ret)) { 38062306a36Sopenharmony_ci /* 38162306a36Sopenharmony_ci * This is nasty: we need the state_mutex for all the 38262306a36Sopenharmony_ci * bookkeeping even if the syscall was interrupted by 38362306a36Sopenharmony_ci * a signal. ewww. 38462306a36Sopenharmony_ci */ 38562306a36Sopenharmony_ci mutex_lock(&ctx->state_mutex); 38662306a36Sopenharmony_ci break; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci if (unlikely(test_and_clear_bit(SPU_SCHED_NOTIFY_ACTIVE, 38962306a36Sopenharmony_ci &ctx->sched_flags))) { 39062306a36Sopenharmony_ci if (!(status & SPU_STATUS_STOPPED_BY_STOP)) 39162306a36Sopenharmony_ci continue; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci spuctx_switch_state(ctx, SPU_UTIL_SYSTEM); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if ((status & SPU_STATUS_STOPPED_BY_STOP) && 39762306a36Sopenharmony_ci (status >> SPU_STOP_STATUS_SHIFT == 0x2104)) { 39862306a36Sopenharmony_ci ret = spu_process_callback(ctx); 39962306a36Sopenharmony_ci if (ret) 40062306a36Sopenharmony_ci break; 40162306a36Sopenharmony_ci status &= ~SPU_STATUS_STOPPED_BY_STOP; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci ret = spufs_handle_class1(ctx); 40462306a36Sopenharmony_ci if (ret) 40562306a36Sopenharmony_ci break; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci ret = spufs_handle_class0(ctx); 40862306a36Sopenharmony_ci if (ret) 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (signal_pending(current)) 41262306a36Sopenharmony_ci ret = -ERESTARTSYS; 41362306a36Sopenharmony_ci } while (!ret && !(status & (SPU_STATUS_STOPPED_BY_STOP | 41462306a36Sopenharmony_ci SPU_STATUS_STOPPED_BY_HALT | 41562306a36Sopenharmony_ci SPU_STATUS_SINGLE_STEP))); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci spu_disable_spu(ctx); 41862306a36Sopenharmony_ci ret = spu_run_fini(ctx, npc, &status); 41962306a36Sopenharmony_ci spu_yield(ctx); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if ((status & SPU_STATUS_STOPPED_BY_STOP) && 42262306a36Sopenharmony_ci (((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100)) 42362306a36Sopenharmony_ci ctx->stats.libassist++; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if ((ret == 0) || 42662306a36Sopenharmony_ci ((ret == -ERESTARTSYS) && 42762306a36Sopenharmony_ci ((status & SPU_STATUS_STOPPED_BY_HALT) || 42862306a36Sopenharmony_ci (status & SPU_STATUS_SINGLE_STEP) || 42962306a36Sopenharmony_ci ((status & SPU_STATUS_STOPPED_BY_STOP) && 43062306a36Sopenharmony_ci (status >> SPU_STOP_STATUS_SHIFT != 0x2104))))) 43162306a36Sopenharmony_ci ret = status; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* Note: we don't need to force_sig SIGTRAP on single-step 43462306a36Sopenharmony_ci * since we have TIF_SINGLESTEP set, thus the kernel will do 43562306a36Sopenharmony_ci * it upon return from the syscall anyway. 43662306a36Sopenharmony_ci */ 43762306a36Sopenharmony_ci if (unlikely(status & SPU_STATUS_SINGLE_STEP)) 43862306a36Sopenharmony_ci ret = -ERESTARTSYS; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci else if (unlikely((status & SPU_STATUS_STOPPED_BY_STOP) 44162306a36Sopenharmony_ci && (status >> SPU_STOP_STATUS_SHIFT) == 0x3fff)) { 44262306a36Sopenharmony_ci force_sig(SIGTRAP); 44362306a36Sopenharmony_ci ret = -ERESTARTSYS; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ciout: 44762306a36Sopenharmony_ci *event = ctx->event_return; 44862306a36Sopenharmony_ciout_unlock: 44962306a36Sopenharmony_ci mutex_unlock(&ctx->run_mutex); 45062306a36Sopenharmony_ci return ret; 45162306a36Sopenharmony_ci} 452