10afacde3Sarnd@arndb.de #define DEBUG 20afacde3Sarnd@arndb.de 3ce8ab854SArnd Bergmann #include <linux/wait.h> 4ce8ab854SArnd Bergmann #include <linux/ptrace.h> 5ce8ab854SArnd Bergmann 6ce8ab854SArnd Bergmann #include <asm/spu.h> 7cfff5b23SDave Jones #include <asm/unistd.h> 8ce8ab854SArnd Bergmann 9ce8ab854SArnd Bergmann #include "spufs.h" 10ce8ab854SArnd Bergmann 11ce8ab854SArnd Bergmann /* interrupt-level stop callback function. */ 12ce8ab854SArnd Bergmann void spufs_stop_callback(struct spu *spu) 13ce8ab854SArnd Bergmann { 14ce8ab854SArnd Bergmann struct spu_context *ctx = spu->ctx; 15ce8ab854SArnd Bergmann 16ce8ab854SArnd Bergmann wake_up_all(&ctx->stop_wq); 17ce8ab854SArnd Bergmann } 18ce8ab854SArnd Bergmann 199add11daSArnd Bergmann void spufs_dma_callback(struct spu *spu, int type) 209add11daSArnd Bergmann { 219add11daSArnd Bergmann struct spu_context *ctx = spu->ctx; 229add11daSArnd Bergmann 239add11daSArnd Bergmann if (ctx->flags & SPU_CREATE_EVENTS_ENABLED) { 249add11daSArnd Bergmann ctx->event_return |= type; 259add11daSArnd Bergmann wake_up_all(&ctx->stop_wq); 269add11daSArnd Bergmann } else { 279add11daSArnd Bergmann switch (type) { 289add11daSArnd Bergmann case SPE_EVENT_DMA_ALIGNMENT: 29453d9f72SArnd Bergmann case SPE_EVENT_SPE_DATA_STORAGE: 309add11daSArnd Bergmann case SPE_EVENT_INVALID_DMA: 319add11daSArnd Bergmann force_sig(SIGBUS, /* info, */ current); 329add11daSArnd Bergmann break; 339add11daSArnd Bergmann case SPE_EVENT_SPE_ERROR: 349add11daSArnd Bergmann force_sig(SIGILL, /* info */ current); 359add11daSArnd Bergmann break; 369add11daSArnd Bergmann } 379add11daSArnd Bergmann } 389add11daSArnd Bergmann } 399add11daSArnd Bergmann 40ce8ab854SArnd Bergmann static inline int spu_stopped(struct spu_context *ctx, u32 * stat) 41ce8ab854SArnd Bergmann { 42ce8ab854SArnd Bergmann struct spu *spu; 43ce8ab854SArnd Bergmann u64 pte_fault; 44ce8ab854SArnd Bergmann 45ce8ab854SArnd Bergmann *stat = ctx->ops->status_read(ctx); 46ce8ab854SArnd Bergmann if (ctx->state != SPU_STATE_RUNNABLE) 47ce8ab854SArnd Bergmann return 1; 48ce8ab854SArnd Bergmann spu = ctx->spu; 49ce8ab854SArnd Bergmann pte_fault = spu->dsisr & 50ce8ab854SArnd Bergmann (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED); 51ce8ab854SArnd Bergmann return (!(*stat & 0x1) || pte_fault || spu->class_0_pending) ? 1 : 0; 52ce8ab854SArnd Bergmann } 53ce8ab854SArnd Bergmann 549add11daSArnd Bergmann static inline int spu_run_init(struct spu_context *ctx, u32 * npc) 55ce8ab854SArnd Bergmann { 56ce8ab854SArnd Bergmann int ret; 575737edd1SMark Nutter unsigned long runcntl = SPU_RUNCNTL_RUNNABLE; 58ce8ab854SArnd Bergmann 59ce8ab854SArnd Bergmann if ((ret = spu_acquire_runnable(ctx)) != 0) 60ce8ab854SArnd Bergmann return ret; 615737edd1SMark Nutter 620afacde3Sarnd@arndb.de /* if we're in isolated mode, we would have started the SPU 630afacde3Sarnd@arndb.de * earlier, so don't do it again now. */ 640afacde3Sarnd@arndb.de if (!(ctx->flags & SPU_CREATE_ISOLATE)) { 65ce8ab854SArnd Bergmann ctx->ops->npc_write(ctx, *npc); 665737edd1SMark Nutter ctx->ops->runcntl_write(ctx, runcntl); 670afacde3Sarnd@arndb.de } 68ce8ab854SArnd Bergmann return 0; 69ce8ab854SArnd Bergmann } 70ce8ab854SArnd Bergmann 71ce8ab854SArnd Bergmann static inline int spu_run_fini(struct spu_context *ctx, u32 * npc, 72ce8ab854SArnd Bergmann u32 * status) 73ce8ab854SArnd Bergmann { 74ce8ab854SArnd Bergmann int ret = 0; 75ce8ab854SArnd Bergmann 76ce8ab854SArnd Bergmann *status = ctx->ops->status_read(ctx); 77ce8ab854SArnd Bergmann *npc = ctx->ops->npc_read(ctx); 78ce8ab854SArnd Bergmann spu_release(ctx); 79ce8ab854SArnd Bergmann 80ce8ab854SArnd Bergmann if (signal_pending(current)) 81ce8ab854SArnd Bergmann ret = -ERESTARTSYS; 822ebb2477SMasato Noguchi 83ce8ab854SArnd Bergmann return ret; 84ce8ab854SArnd Bergmann } 85ce8ab854SArnd Bergmann 86ce8ab854SArnd Bergmann static inline int spu_reacquire_runnable(struct spu_context *ctx, u32 *npc, 87ce8ab854SArnd Bergmann u32 *status) 88ce8ab854SArnd Bergmann { 89ce8ab854SArnd Bergmann int ret; 90ce8ab854SArnd Bergmann 91ce8ab854SArnd Bergmann if ((ret = spu_run_fini(ctx, npc, status)) != 0) 92ce8ab854SArnd Bergmann return ret; 93ce8ab854SArnd Bergmann if (*status & (SPU_STATUS_STOPPED_BY_STOP | 94ce8ab854SArnd Bergmann SPU_STATUS_STOPPED_BY_HALT)) { 95ce8ab854SArnd Bergmann return *status; 96ce8ab854SArnd Bergmann } 979add11daSArnd Bergmann if ((ret = spu_run_init(ctx, npc)) != 0) 98ce8ab854SArnd Bergmann return ret; 99ce8ab854SArnd Bergmann return 0; 100ce8ab854SArnd Bergmann } 101ce8ab854SArnd Bergmann 1022dd14934SArnd Bergmann /* 1032dd14934SArnd Bergmann * SPU syscall restarting is tricky because we violate the basic 1042dd14934SArnd Bergmann * assumption that the signal handler is running on the interrupted 1052dd14934SArnd Bergmann * thread. Here instead, the handler runs on PowerPC user space code, 1062dd14934SArnd Bergmann * while the syscall was called from the SPU. 1072dd14934SArnd Bergmann * This means we can only do a very rough approximation of POSIX 1082dd14934SArnd Bergmann * signal semantics. 1092dd14934SArnd Bergmann */ 1102dd14934SArnd Bergmann int spu_handle_restartsys(struct spu_context *ctx, long *spu_ret, 1112dd14934SArnd Bergmann unsigned int *npc) 1122dd14934SArnd Bergmann { 1132dd14934SArnd Bergmann int ret; 1142dd14934SArnd Bergmann 1152dd14934SArnd Bergmann switch (*spu_ret) { 1162dd14934SArnd Bergmann case -ERESTARTSYS: 1172dd14934SArnd Bergmann case -ERESTARTNOINTR: 1182dd14934SArnd Bergmann /* 1192dd14934SArnd Bergmann * Enter the regular syscall restarting for 1202dd14934SArnd Bergmann * sys_spu_run, then restart the SPU syscall 1212dd14934SArnd Bergmann * callback. 1222dd14934SArnd Bergmann */ 1232dd14934SArnd Bergmann *npc -= 8; 1242dd14934SArnd Bergmann ret = -ERESTARTSYS; 1252dd14934SArnd Bergmann break; 1262dd14934SArnd Bergmann case -ERESTARTNOHAND: 1272dd14934SArnd Bergmann case -ERESTART_RESTARTBLOCK: 1282dd14934SArnd Bergmann /* 1292dd14934SArnd Bergmann * Restart block is too hard for now, just return -EINTR 1302dd14934SArnd Bergmann * to the SPU. 1312dd14934SArnd Bergmann * ERESTARTNOHAND comes from sys_pause, we also return 1322dd14934SArnd Bergmann * -EINTR from there. 1332dd14934SArnd Bergmann * Assume that we need to be restarted ourselves though. 1342dd14934SArnd Bergmann */ 1352dd14934SArnd Bergmann *spu_ret = -EINTR; 1362dd14934SArnd Bergmann ret = -ERESTARTSYS; 1372dd14934SArnd Bergmann break; 1382dd14934SArnd Bergmann default: 1392dd14934SArnd Bergmann printk(KERN_WARNING "%s: unexpected return code %ld\n", 1402dd14934SArnd Bergmann __FUNCTION__, *spu_ret); 1412dd14934SArnd Bergmann ret = 0; 1422dd14934SArnd Bergmann } 1432dd14934SArnd Bergmann return ret; 1442dd14934SArnd Bergmann } 1452dd14934SArnd Bergmann 1462dd14934SArnd Bergmann int spu_process_callback(struct spu_context *ctx) 1472dd14934SArnd Bergmann { 1482dd14934SArnd Bergmann struct spu_syscall_block s; 1492dd14934SArnd Bergmann u32 ls_pointer, npc; 1502dd14934SArnd Bergmann char *ls; 1512dd14934SArnd Bergmann long spu_ret; 1522dd14934SArnd Bergmann int ret; 1532dd14934SArnd Bergmann 1542dd14934SArnd Bergmann /* get syscall block from local store */ 1552dd14934SArnd Bergmann npc = ctx->ops->npc_read(ctx); 1562dd14934SArnd Bergmann ls = ctx->ops->get_ls(ctx); 1572dd14934SArnd Bergmann ls_pointer = *(u32*)(ls + npc); 1582dd14934SArnd Bergmann if (ls_pointer > (LS_SIZE - sizeof(s))) 1592dd14934SArnd Bergmann return -EFAULT; 1602dd14934SArnd Bergmann memcpy(&s, ls + ls_pointer, sizeof (s)); 1612dd14934SArnd Bergmann 1622dd14934SArnd Bergmann /* do actual syscall without pinning the spu */ 1632dd14934SArnd Bergmann ret = 0; 1642dd14934SArnd Bergmann spu_ret = -ENOSYS; 1652dd14934SArnd Bergmann npc += 4; 1662dd14934SArnd Bergmann 1672dd14934SArnd Bergmann if (s.nr_ret < __NR_syscalls) { 1682dd14934SArnd Bergmann spu_release(ctx); 1692dd14934SArnd Bergmann /* do actual system call from here */ 1702dd14934SArnd Bergmann spu_ret = spu_sys_callback(&s); 1712dd14934SArnd Bergmann if (spu_ret <= -ERESTARTSYS) { 1722dd14934SArnd Bergmann ret = spu_handle_restartsys(ctx, &spu_ret, &npc); 1732dd14934SArnd Bergmann } 1742dd14934SArnd Bergmann spu_acquire(ctx); 1752dd14934SArnd Bergmann if (ret == -ERESTARTSYS) 1762dd14934SArnd Bergmann return ret; 1772dd14934SArnd Bergmann } 1782dd14934SArnd Bergmann 1792dd14934SArnd Bergmann /* write result, jump over indirect pointer */ 1802dd14934SArnd Bergmann memcpy(ls + ls_pointer, &spu_ret, sizeof (spu_ret)); 1812dd14934SArnd Bergmann ctx->ops->npc_write(ctx, npc); 1822dd14934SArnd Bergmann ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE); 1832dd14934SArnd Bergmann return ret; 1842dd14934SArnd Bergmann } 1852dd14934SArnd Bergmann 186ce8ab854SArnd Bergmann static inline int spu_process_events(struct spu_context *ctx) 187ce8ab854SArnd Bergmann { 188ce8ab854SArnd Bergmann struct spu *spu = ctx->spu; 189ce8ab854SArnd Bergmann u64 pte_fault = MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED; 190ce8ab854SArnd Bergmann int ret = 0; 191ce8ab854SArnd Bergmann 192ce8ab854SArnd Bergmann if (spu->dsisr & pte_fault) 193ce8ab854SArnd Bergmann ret = spu_irq_class_1_bottom(spu); 194ce8ab854SArnd Bergmann if (spu->class_0_pending) 195ce8ab854SArnd Bergmann ret = spu_irq_class_0_bottom(spu); 196ce8ab854SArnd Bergmann if (!ret && signal_pending(current)) 197ce8ab854SArnd Bergmann ret = -ERESTARTSYS; 198ce8ab854SArnd Bergmann return ret; 199ce8ab854SArnd Bergmann } 200ce8ab854SArnd Bergmann 201ce8ab854SArnd Bergmann long spufs_run_spu(struct file *file, struct spu_context *ctx, 2029add11daSArnd Bergmann u32 *npc, u32 *event) 203ce8ab854SArnd Bergmann { 204ce8ab854SArnd Bergmann int ret; 2059add11daSArnd Bergmann u32 status; 206ce8ab854SArnd Bergmann 207ce8ab854SArnd Bergmann if (down_interruptible(&ctx->run_sema)) 208ce8ab854SArnd Bergmann return -ERESTARTSYS; 209ce8ab854SArnd Bergmann 2109add11daSArnd Bergmann ctx->event_return = 0; 2119add11daSArnd Bergmann ret = spu_run_init(ctx, npc); 212ce8ab854SArnd Bergmann if (ret) 213ce8ab854SArnd Bergmann goto out; 214ce8ab854SArnd Bergmann 215ce8ab854SArnd Bergmann do { 2169add11daSArnd Bergmann ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status)); 217ce8ab854SArnd Bergmann if (unlikely(ret)) 218ce8ab854SArnd Bergmann break; 2199add11daSArnd Bergmann if ((status & SPU_STATUS_STOPPED_BY_STOP) && 2209add11daSArnd Bergmann (status >> SPU_STOP_STATUS_SHIFT == 0x2104)) { 2212dd14934SArnd Bergmann ret = spu_process_callback(ctx); 2222dd14934SArnd Bergmann if (ret) 2232dd14934SArnd Bergmann break; 2249add11daSArnd Bergmann status &= ~SPU_STATUS_STOPPED_BY_STOP; 2252dd14934SArnd Bergmann } 226ce8ab854SArnd Bergmann if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) { 2279add11daSArnd Bergmann ret = spu_reacquire_runnable(ctx, npc, &status); 228ce8ab854SArnd Bergmann if (ret) 2292ebb2477SMasato Noguchi goto out2; 230ce8ab854SArnd Bergmann continue; 231ce8ab854SArnd Bergmann } 232ce8ab854SArnd Bergmann ret = spu_process_events(ctx); 233ce8ab854SArnd Bergmann 2349add11daSArnd Bergmann } while (!ret && !(status & (SPU_STATUS_STOPPED_BY_STOP | 235ce8ab854SArnd Bergmann SPU_STATUS_STOPPED_BY_HALT))); 236ce8ab854SArnd Bergmann 237ce8ab854SArnd Bergmann ctx->ops->runcntl_stop(ctx); 2389add11daSArnd Bergmann ret = spu_run_fini(ctx, npc, &status); 239ce8ab854SArnd Bergmann spu_yield(ctx); 240ce8ab854SArnd Bergmann 2412ebb2477SMasato Noguchi out2: 2422ebb2477SMasato Noguchi if ((ret == 0) || 2432ebb2477SMasato Noguchi ((ret == -ERESTARTSYS) && 2442ebb2477SMasato Noguchi ((status & SPU_STATUS_STOPPED_BY_HALT) || 2452ebb2477SMasato Noguchi ((status & SPU_STATUS_STOPPED_BY_STOP) && 2462ebb2477SMasato Noguchi (status >> SPU_STOP_STATUS_SHIFT != 0x2104))))) 2472ebb2477SMasato Noguchi ret = status; 2482ebb2477SMasato Noguchi 2492ebb2477SMasato Noguchi if (unlikely(current->ptrace & PT_PTRACED)) { 2502ebb2477SMasato Noguchi if ((status & SPU_STATUS_STOPPED_BY_STOP) 2512ebb2477SMasato Noguchi && (status >> SPU_STOP_STATUS_SHIFT) == 0x3fff) { 2522ebb2477SMasato Noguchi force_sig(SIGTRAP, current); 2532ebb2477SMasato Noguchi ret = -ERESTARTSYS; 2542ebb2477SMasato Noguchi } 2552ebb2477SMasato Noguchi } 2562ebb2477SMasato Noguchi 257ce8ab854SArnd Bergmann out: 2589add11daSArnd Bergmann *event = ctx->event_return; 259ce8ab854SArnd Bergmann up(&ctx->run_sema); 260ce8ab854SArnd Bergmann return ret; 261ce8ab854SArnd Bergmann } 262ce8ab854SArnd Bergmann 263