xref: /linux/arch/powerpc/platforms/cell/spufs/run.c (revision de2fe5e07d58424bc286fff3fd3c1b0bf933cd58)
1 #include <linux/wait.h>
2 #include <linux/ptrace.h>
3 
4 #include <asm/spu.h>
5 
6 #include "spufs.h"
7 
8 /* interrupt-level stop callback function. */
9 void spufs_stop_callback(struct spu *spu)
10 {
11 	struct spu_context *ctx = spu->ctx;
12 
13 	wake_up_all(&ctx->stop_wq);
14 }
15 
16 static inline int spu_stopped(struct spu_context *ctx, u32 * stat)
17 {
18 	struct spu *spu;
19 	u64 pte_fault;
20 
21 	*stat = ctx->ops->status_read(ctx);
22 	if (ctx->state != SPU_STATE_RUNNABLE)
23 		return 1;
24 	spu = ctx->spu;
25 	pte_fault = spu->dsisr &
26 	    (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED);
27 	return (!(*stat & 0x1) || pte_fault || spu->class_0_pending) ? 1 : 0;
28 }
29 
30 static inline int spu_run_init(struct spu_context *ctx, u32 * npc,
31 			       u32 * status)
32 {
33 	int ret;
34 
35 	if ((ret = spu_acquire_runnable(ctx)) != 0)
36 		return ret;
37 	ctx->ops->npc_write(ctx, *npc);
38 	ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
39 	return 0;
40 }
41 
42 static inline int spu_run_fini(struct spu_context *ctx, u32 * npc,
43 			       u32 * status)
44 {
45 	int ret = 0;
46 
47 	*status = ctx->ops->status_read(ctx);
48 	*npc = ctx->ops->npc_read(ctx);
49 	spu_release(ctx);
50 
51 	if (signal_pending(current))
52 		ret = -ERESTARTSYS;
53 	if (unlikely(current->ptrace & PT_PTRACED)) {
54 		if ((*status & SPU_STATUS_STOPPED_BY_STOP)
55 		    && (*status >> SPU_STOP_STATUS_SHIFT) == 0x3fff) {
56 			force_sig(SIGTRAP, current);
57 			ret = -ERESTARTSYS;
58 		}
59 	}
60 	return ret;
61 }
62 
63 static inline int spu_reacquire_runnable(struct spu_context *ctx, u32 *npc,
64 				         u32 *status)
65 {
66 	int ret;
67 
68 	if ((ret = spu_run_fini(ctx, npc, status)) != 0)
69 		return ret;
70 	if (*status & (SPU_STATUS_STOPPED_BY_STOP |
71 		       SPU_STATUS_STOPPED_BY_HALT)) {
72 		return *status;
73 	}
74 	if ((ret = spu_run_init(ctx, npc, status)) != 0)
75 		return ret;
76 	return 0;
77 }
78 
79 /*
80  * SPU syscall restarting is tricky because we violate the basic
81  * assumption that the signal handler is running on the interrupted
82  * thread. Here instead, the handler runs on PowerPC user space code,
83  * while the syscall was called from the SPU.
84  * This means we can only do a very rough approximation of POSIX
85  * signal semantics.
86  */
87 int spu_handle_restartsys(struct spu_context *ctx, long *spu_ret,
88 			  unsigned int *npc)
89 {
90 	int ret;
91 
92 	switch (*spu_ret) {
93 	case -ERESTARTSYS:
94 	case -ERESTARTNOINTR:
95 		/*
96 		 * Enter the regular syscall restarting for
97 		 * sys_spu_run, then restart the SPU syscall
98 		 * callback.
99 		 */
100 		*npc -= 8;
101 		ret = -ERESTARTSYS;
102 		break;
103 	case -ERESTARTNOHAND:
104 	case -ERESTART_RESTARTBLOCK:
105 		/*
106 		 * Restart block is too hard for now, just return -EINTR
107 		 * to the SPU.
108 		 * ERESTARTNOHAND comes from sys_pause, we also return
109 		 * -EINTR from there.
110 		 * Assume that we need to be restarted ourselves though.
111 		 */
112 		*spu_ret = -EINTR;
113 		ret = -ERESTARTSYS;
114 		break;
115 	default:
116 		printk(KERN_WARNING "%s: unexpected return code %ld\n",
117 			__FUNCTION__, *spu_ret);
118 		ret = 0;
119 	}
120 	return ret;
121 }
122 
123 int spu_process_callback(struct spu_context *ctx)
124 {
125 	struct spu_syscall_block s;
126 	u32 ls_pointer, npc;
127 	char *ls;
128 	long spu_ret;
129 	int ret;
130 
131 	/* get syscall block from local store */
132 	npc = ctx->ops->npc_read(ctx);
133 	ls = ctx->ops->get_ls(ctx);
134 	ls_pointer = *(u32*)(ls + npc);
135 	if (ls_pointer > (LS_SIZE - sizeof(s)))
136 		return -EFAULT;
137 	memcpy(&s, ls + ls_pointer, sizeof (s));
138 
139 	/* do actual syscall without pinning the spu */
140 	ret = 0;
141 	spu_ret = -ENOSYS;
142 	npc += 4;
143 
144 	if (s.nr_ret < __NR_syscalls) {
145 		spu_release(ctx);
146 		/* do actual system call from here */
147 		spu_ret = spu_sys_callback(&s);
148 		if (spu_ret <= -ERESTARTSYS) {
149 			ret = spu_handle_restartsys(ctx, &spu_ret, &npc);
150 		}
151 		spu_acquire(ctx);
152 		if (ret == -ERESTARTSYS)
153 			return ret;
154 	}
155 
156 	/* write result, jump over indirect pointer */
157 	memcpy(ls + ls_pointer, &spu_ret, sizeof (spu_ret));
158 	ctx->ops->npc_write(ctx, npc);
159 	ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
160 	return ret;
161 }
162 
163 static inline int spu_process_events(struct spu_context *ctx)
164 {
165 	struct spu *spu = ctx->spu;
166 	u64 pte_fault = MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED;
167 	int ret = 0;
168 
169 	if (spu->dsisr & pte_fault)
170 		ret = spu_irq_class_1_bottom(spu);
171 	if (spu->class_0_pending)
172 		ret = spu_irq_class_0_bottom(spu);
173 	if (!ret && signal_pending(current))
174 		ret = -ERESTARTSYS;
175 	return ret;
176 }
177 
178 long spufs_run_spu(struct file *file, struct spu_context *ctx,
179 		   u32 * npc, u32 * status)
180 {
181 	int ret;
182 
183 	if (down_interruptible(&ctx->run_sema))
184 		return -ERESTARTSYS;
185 
186 	ret = spu_run_init(ctx, npc, status);
187 	if (ret)
188 		goto out;
189 
190 	do {
191 		ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, status));
192 		if (unlikely(ret))
193 			break;
194 		if ((*status & SPU_STATUS_STOPPED_BY_STOP) &&
195 		    (*status >> SPU_STOP_STATUS_SHIFT == 0x2104)) {
196 			ret = spu_process_callback(ctx);
197 			if (ret)
198 				break;
199 			*status &= ~SPU_STATUS_STOPPED_BY_STOP;
200 		}
201 		if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) {
202 			ret = spu_reacquire_runnable(ctx, npc, status);
203 			if (ret)
204 				goto out;
205 			continue;
206 		}
207 		ret = spu_process_events(ctx);
208 
209 	} while (!ret && !(*status & (SPU_STATUS_STOPPED_BY_STOP |
210 				      SPU_STATUS_STOPPED_BY_HALT)));
211 
212 	ctx->ops->runcntl_stop(ctx);
213 	ret = spu_run_fini(ctx, npc, status);
214 	if (!ret)
215 		ret = *status;
216 	spu_yield(ctx);
217 
218 out:
219 	up(&ctx->run_sema);
220 	return ret;
221 }
222 
223