Merge branch 'master'
[linux-2.6] / arch / powerpc / platforms / cell / spufs / run.c
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