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