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