|  | #define DEBUG | 
|  |  | 
|  | #include <linux/wait.h> | 
|  | #include <linux/ptrace.h> | 
|  |  | 
|  | #include <asm/spu.h> | 
|  | #include <asm/spu_priv1.h> | 
|  | #include <asm/io.h> | 
|  | #include <asm/unistd.h> | 
|  |  | 
|  | #include "spufs.h" | 
|  |  | 
|  | /* interrupt-level stop callback function. */ | 
|  | void spufs_stop_callback(struct spu *spu, int irq) | 
|  | { | 
|  | struct spu_context *ctx = spu->ctx; | 
|  |  | 
|  | /* | 
|  | * It should be impossible to preempt a context while an exception | 
|  | * is being processed, since the context switch code is specially | 
|  | * coded to deal with interrupts ... But, just in case, sanity check | 
|  | * the context pointer.  It is OK to return doing nothing since | 
|  | * the exception will be regenerated when the context is resumed. | 
|  | */ | 
|  | if (ctx) { | 
|  | /* Copy exception arguments into module specific structure */ | 
|  | switch(irq) { | 
|  | case 0 : | 
|  | ctx->csa.class_0_pending = spu->class_0_pending; | 
|  | ctx->csa.class_0_dar = spu->class_0_dar; | 
|  | break; | 
|  | case 1 : | 
|  | ctx->csa.class_1_dsisr = spu->class_1_dsisr; | 
|  | ctx->csa.class_1_dar = spu->class_1_dar; | 
|  | break; | 
|  | case 2 : | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* ensure that the exception status has hit memory before a | 
|  | * thread waiting on the context's stop queue is woken */ | 
|  | smp_wmb(); | 
|  |  | 
|  | wake_up_all(&ctx->stop_wq); | 
|  | } | 
|  | } | 
|  |  | 
|  | int spu_stopped(struct spu_context *ctx, u32 *stat) | 
|  | { | 
|  | u64 dsisr; | 
|  | u32 stopped; | 
|  |  | 
|  | stopped = SPU_STATUS_INVALID_INSTR | SPU_STATUS_SINGLE_STEP | | 
|  | SPU_STATUS_STOPPED_BY_HALT | SPU_STATUS_STOPPED_BY_STOP; | 
|  |  | 
|  | top: | 
|  | *stat = ctx->ops->status_read(ctx); | 
|  | if (*stat & stopped) { | 
|  | /* | 
|  | * If the spu hasn't finished stopping, we need to | 
|  | * re-read the register to get the stopped value. | 
|  | */ | 
|  | if (*stat & SPU_STATUS_RUNNING) | 
|  | goto top; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | if (test_bit(SPU_SCHED_NOTIFY_ACTIVE, &ctx->sched_flags)) | 
|  | return 1; | 
|  |  | 
|  | dsisr = ctx->csa.class_1_dsisr; | 
|  | if (dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED)) | 
|  | return 1; | 
|  |  | 
|  | if (ctx->csa.class_0_pending) | 
|  | return 1; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int spu_setup_isolated(struct spu_context *ctx) | 
|  | { | 
|  | int ret; | 
|  | u64 __iomem *mfc_cntl; | 
|  | u64 sr1; | 
|  | u32 status; | 
|  | unsigned long timeout; | 
|  | const u32 status_loading = SPU_STATUS_RUNNING | 
|  | | SPU_STATUS_ISOLATED_STATE | SPU_STATUS_ISOLATED_LOAD_STATUS; | 
|  |  | 
|  | ret = -ENODEV; | 
|  | if (!isolated_loader) | 
|  | goto out; | 
|  |  | 
|  | /* | 
|  | * We need to exclude userspace access to the context. | 
|  | * | 
|  | * To protect against memory access we invalidate all ptes | 
|  | * and make sure the pagefault handlers block on the mutex. | 
|  | */ | 
|  | spu_unmap_mappings(ctx); | 
|  |  | 
|  | mfc_cntl = &ctx->spu->priv2->mfc_control_RW; | 
|  |  | 
|  | /* purge the MFC DMA queue to ensure no spurious accesses before we | 
|  | * enter kernel mode */ | 
|  | timeout = jiffies + HZ; | 
|  | out_be64(mfc_cntl, MFC_CNTL_PURGE_DMA_REQUEST); | 
|  | while ((in_be64(mfc_cntl) & MFC_CNTL_PURGE_DMA_STATUS_MASK) | 
|  | != MFC_CNTL_PURGE_DMA_COMPLETE) { | 
|  | if (time_after(jiffies, timeout)) { | 
|  | printk(KERN_ERR "%s: timeout flushing MFC DMA queue\n", | 
|  | __func__); | 
|  | ret = -EIO; | 
|  | goto out; | 
|  | } | 
|  | cond_resched(); | 
|  | } | 
|  |  | 
|  | /* clear purge status */ | 
|  | out_be64(mfc_cntl, 0); | 
|  |  | 
|  | /* put the SPE in kernel mode to allow access to the loader */ | 
|  | sr1 = spu_mfc_sr1_get(ctx->spu); | 
|  | sr1 &= ~MFC_STATE1_PROBLEM_STATE_MASK; | 
|  | spu_mfc_sr1_set(ctx->spu, sr1); | 
|  |  | 
|  | /* start the loader */ | 
|  | ctx->ops->signal1_write(ctx, (unsigned long)isolated_loader >> 32); | 
|  | ctx->ops->signal2_write(ctx, | 
|  | (unsigned long)isolated_loader & 0xffffffff); | 
|  |  | 
|  | ctx->ops->runcntl_write(ctx, | 
|  | SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE); | 
|  |  | 
|  | ret = 0; | 
|  | timeout = jiffies + HZ; | 
|  | while (((status = ctx->ops->status_read(ctx)) & status_loading) == | 
|  | status_loading) { | 
|  | if (time_after(jiffies, timeout)) { | 
|  | printk(KERN_ERR "%s: timeout waiting for loader\n", | 
|  | __func__); | 
|  | ret = -EIO; | 
|  | goto out_drop_priv; | 
|  | } | 
|  | cond_resched(); | 
|  | } | 
|  |  | 
|  | if (!(status & SPU_STATUS_RUNNING)) { | 
|  | /* If isolated LOAD has failed: run SPU, we will get a stop-and | 
|  | * signal later. */ | 
|  | pr_debug("%s: isolated LOAD failed\n", __func__); | 
|  | ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE); | 
|  | ret = -EACCES; | 
|  | goto out_drop_priv; | 
|  | } | 
|  |  | 
|  | if (!(status & SPU_STATUS_ISOLATED_STATE)) { | 
|  | /* This isn't allowed by the CBEA, but check anyway */ | 
|  | pr_debug("%s: SPU fell out of isolated mode?\n", __func__); | 
|  | ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_STOP); | 
|  | ret = -EINVAL; | 
|  | goto out_drop_priv; | 
|  | } | 
|  |  | 
|  | out_drop_priv: | 
|  | /* Finished accessing the loader. Drop kernel mode */ | 
|  | sr1 |= MFC_STATE1_PROBLEM_STATE_MASK; | 
|  | spu_mfc_sr1_set(ctx->spu, sr1); | 
|  |  | 
|  | out: | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int spu_run_init(struct spu_context *ctx, u32 *npc) | 
|  | { | 
|  | unsigned long runcntl = SPU_RUNCNTL_RUNNABLE; | 
|  | int ret; | 
|  |  | 
|  | spuctx_switch_state(ctx, SPU_UTIL_SYSTEM); | 
|  |  | 
|  | /* | 
|  | * NOSCHED is synchronous scheduling with respect to the caller. | 
|  | * The caller waits for the context to be loaded. | 
|  | */ | 
|  | if (ctx->flags & SPU_CREATE_NOSCHED) { | 
|  | if (ctx->state == SPU_STATE_SAVED) { | 
|  | ret = spu_activate(ctx, 0); | 
|  | if (ret) | 
|  | return ret; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Apply special setup as required. | 
|  | */ | 
|  | if (ctx->flags & SPU_CREATE_ISOLATE) { | 
|  | if (!(ctx->ops->status_read(ctx) & SPU_STATUS_ISOLATED_STATE)) { | 
|  | ret = spu_setup_isolated(ctx); | 
|  | if (ret) | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * If userspace has set the runcntrl register (eg, to | 
|  | * issue an isolated exit), we need to re-set it here | 
|  | */ | 
|  | runcntl = ctx->ops->runcntl_read(ctx) & | 
|  | (SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE); | 
|  | if (runcntl == 0) | 
|  | runcntl = SPU_RUNCNTL_RUNNABLE; | 
|  | } else { | 
|  | unsigned long privcntl; | 
|  |  | 
|  | if (test_thread_flag(TIF_SINGLESTEP)) | 
|  | privcntl = SPU_PRIVCNTL_MODE_SINGLE_STEP; | 
|  | else | 
|  | privcntl = SPU_PRIVCNTL_MODE_NORMAL; | 
|  |  | 
|  | ctx->ops->privcntl_write(ctx, privcntl); | 
|  | ctx->ops->npc_write(ctx, *npc); | 
|  | } | 
|  |  | 
|  | ctx->ops->runcntl_write(ctx, runcntl); | 
|  |  | 
|  | if (ctx->flags & SPU_CREATE_NOSCHED) { | 
|  | spuctx_switch_state(ctx, SPU_UTIL_USER); | 
|  | } else { | 
|  |  | 
|  | if (ctx->state == SPU_STATE_SAVED) { | 
|  | ret = spu_activate(ctx, 0); | 
|  | if (ret) | 
|  | return ret; | 
|  | } else { | 
|  | spuctx_switch_state(ctx, SPU_UTIL_USER); | 
|  | } | 
|  | } | 
|  |  | 
|  | set_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int spu_run_fini(struct spu_context *ctx, u32 *npc, | 
|  | u32 *status) | 
|  | { | 
|  | int ret = 0; | 
|  |  | 
|  | spu_del_from_rq(ctx); | 
|  |  | 
|  | *status = ctx->ops->status_read(ctx); | 
|  | *npc = ctx->ops->npc_read(ctx); | 
|  |  | 
|  | spuctx_switch_state(ctx, SPU_UTIL_IDLE_LOADED); | 
|  | clear_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags); | 
|  | spu_switch_log_notify(NULL, ctx, SWITCH_LOG_EXIT, *status); | 
|  | spu_release(ctx); | 
|  |  | 
|  | if (signal_pending(current)) | 
|  | ret = -ERESTARTSYS; | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * SPU syscall restarting is tricky because we violate the basic | 
|  | * assumption that the signal handler is running on the interrupted | 
|  | * thread. Here instead, the handler runs on PowerPC user space code, | 
|  | * while the syscall was called from the SPU. | 
|  | * This means we can only do a very rough approximation of POSIX | 
|  | * signal semantics. | 
|  | */ | 
|  | static int spu_handle_restartsys(struct spu_context *ctx, long *spu_ret, | 
|  | unsigned int *npc) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | switch (*spu_ret) { | 
|  | case -ERESTARTSYS: | 
|  | case -ERESTARTNOINTR: | 
|  | /* | 
|  | * Enter the regular syscall restarting for | 
|  | * sys_spu_run, then restart the SPU syscall | 
|  | * callback. | 
|  | */ | 
|  | *npc -= 8; | 
|  | ret = -ERESTARTSYS; | 
|  | break; | 
|  | case -ERESTARTNOHAND: | 
|  | case -ERESTART_RESTARTBLOCK: | 
|  | /* | 
|  | * Restart block is too hard for now, just return -EINTR | 
|  | * to the SPU. | 
|  | * ERESTARTNOHAND comes from sys_pause, we also return | 
|  | * -EINTR from there. | 
|  | * Assume that we need to be restarted ourselves though. | 
|  | */ | 
|  | *spu_ret = -EINTR; | 
|  | ret = -ERESTARTSYS; | 
|  | break; | 
|  | default: | 
|  | printk(KERN_WARNING "%s: unexpected return code %ld\n", | 
|  | __func__, *spu_ret); | 
|  | ret = 0; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int spu_process_callback(struct spu_context *ctx) | 
|  | { | 
|  | struct spu_syscall_block s; | 
|  | u32 ls_pointer, npc; | 
|  | void __iomem *ls; | 
|  | long spu_ret; | 
|  | int ret; | 
|  |  | 
|  | /* get syscall block from local store */ | 
|  | npc = ctx->ops->npc_read(ctx) & ~3; | 
|  | ls = (void __iomem *)ctx->ops->get_ls(ctx); | 
|  | ls_pointer = in_be32(ls + npc); | 
|  | if (ls_pointer > (LS_SIZE - sizeof(s))) | 
|  | return -EFAULT; | 
|  | memcpy_fromio(&s, ls + ls_pointer, sizeof(s)); | 
|  |  | 
|  | /* do actual syscall without pinning the spu */ | 
|  | ret = 0; | 
|  | spu_ret = -ENOSYS; | 
|  | npc += 4; | 
|  |  | 
|  | if (s.nr_ret < __NR_syscalls) { | 
|  | spu_release(ctx); | 
|  | /* do actual system call from here */ | 
|  | spu_ret = spu_sys_callback(&s); | 
|  | if (spu_ret <= -ERESTARTSYS) { | 
|  | ret = spu_handle_restartsys(ctx, &spu_ret, &npc); | 
|  | } | 
|  | mutex_lock(&ctx->state_mutex); | 
|  | if (ret == -ERESTARTSYS) | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* need to re-get the ls, as it may have changed when we released the | 
|  | * spu */ | 
|  | ls = (void __iomem *)ctx->ops->get_ls(ctx); | 
|  |  | 
|  | /* write result, jump over indirect pointer */ | 
|  | memcpy_toio(ls + ls_pointer, &spu_ret, sizeof(spu_ret)); | 
|  | ctx->ops->npc_write(ctx, npc); | 
|  | ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event) | 
|  | { | 
|  | int ret; | 
|  | struct spu *spu; | 
|  | u32 status; | 
|  |  | 
|  | if (mutex_lock_interruptible(&ctx->run_mutex)) | 
|  | return -ERESTARTSYS; | 
|  |  | 
|  | ctx->event_return = 0; | 
|  |  | 
|  | ret = spu_acquire(ctx); | 
|  | if (ret) | 
|  | goto out_unlock; | 
|  |  | 
|  | spu_enable_spu(ctx); | 
|  |  | 
|  | spu_update_sched_info(ctx); | 
|  |  | 
|  | ret = spu_run_init(ctx, npc); | 
|  | if (ret) { | 
|  | spu_release(ctx); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | do { | 
|  | ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status)); | 
|  | if (unlikely(ret)) { | 
|  | /* | 
|  | * This is nasty: we need the state_mutex for all the | 
|  | * bookkeeping even if the syscall was interrupted by | 
|  | * a signal. ewww. | 
|  | */ | 
|  | mutex_lock(&ctx->state_mutex); | 
|  | break; | 
|  | } | 
|  | spu = ctx->spu; | 
|  | if (unlikely(test_and_clear_bit(SPU_SCHED_NOTIFY_ACTIVE, | 
|  | &ctx->sched_flags))) { | 
|  | if (!(status & SPU_STATUS_STOPPED_BY_STOP)) { | 
|  | spu_switch_notify(spu, ctx); | 
|  | continue; | 
|  | } | 
|  | } | 
|  |  | 
|  | spuctx_switch_state(ctx, SPU_UTIL_SYSTEM); | 
|  |  | 
|  | if ((status & SPU_STATUS_STOPPED_BY_STOP) && | 
|  | (status >> SPU_STOP_STATUS_SHIFT == 0x2104)) { | 
|  | ret = spu_process_callback(ctx); | 
|  | if (ret) | 
|  | break; | 
|  | status &= ~SPU_STATUS_STOPPED_BY_STOP; | 
|  | } | 
|  | ret = spufs_handle_class1(ctx); | 
|  | if (ret) | 
|  | break; | 
|  |  | 
|  | ret = spufs_handle_class0(ctx); | 
|  | if (ret) | 
|  | break; | 
|  |  | 
|  | if (signal_pending(current)) | 
|  | ret = -ERESTARTSYS; | 
|  | } while (!ret && !(status & (SPU_STATUS_STOPPED_BY_STOP | | 
|  | SPU_STATUS_STOPPED_BY_HALT | | 
|  | SPU_STATUS_SINGLE_STEP))); | 
|  |  | 
|  | spu_disable_spu(ctx); | 
|  | ret = spu_run_fini(ctx, npc, &status); | 
|  | spu_yield(ctx); | 
|  |  | 
|  | if ((status & SPU_STATUS_STOPPED_BY_STOP) && | 
|  | (((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100)) | 
|  | ctx->stats.libassist++; | 
|  |  | 
|  | if ((ret == 0) || | 
|  | ((ret == -ERESTARTSYS) && | 
|  | ((status & SPU_STATUS_STOPPED_BY_HALT) || | 
|  | (status & SPU_STATUS_SINGLE_STEP) || | 
|  | ((status & SPU_STATUS_STOPPED_BY_STOP) && | 
|  | (status >> SPU_STOP_STATUS_SHIFT != 0x2104))))) | 
|  | ret = status; | 
|  |  | 
|  | /* Note: we don't need to force_sig SIGTRAP on single-step | 
|  | * since we have TIF_SINGLESTEP set, thus the kernel will do | 
|  | * it upon return from the syscall anyawy | 
|  | */ | 
|  | if (unlikely(status & SPU_STATUS_SINGLE_STEP)) | 
|  | ret = -ERESTARTSYS; | 
|  |  | 
|  | else if (unlikely((status & SPU_STATUS_STOPPED_BY_STOP) | 
|  | && (status >> SPU_STOP_STATUS_SHIFT) == 0x3fff)) { | 
|  | force_sig(SIGTRAP, current); | 
|  | ret = -ERESTARTSYS; | 
|  | } | 
|  |  | 
|  | out: | 
|  | *event = ctx->event_return; | 
|  | out_unlock: | 
|  | mutex_unlock(&ctx->run_mutex); | 
|  | return ret; | 
|  | } |