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