blob: 88a41d83a79befd17445b3d2cdc89f5511b26c8c [file] [log] [blame]
arnd@arndb.de0afacde2006-10-24 18:31:18 +02001#define DEBUG
2
Arnd Bergmannce8ab852006-01-04 20:31:29 +01003#include <linux/wait.h>
4#include <linux/ptrace.h>
5
6#include <asm/spu.h>
Dave Jonescfff5b22006-03-31 23:53:09 -05007#include <asm/unistd.h>
Arnd Bergmannce8ab852006-01-04 20:31:29 +01008
9#include "spufs.h"
10
11/* interrupt-level stop callback function. */
12void spufs_stop_callback(struct spu *spu)
13{
14 struct spu_context *ctx = spu->ctx;
15
16 wake_up_all(&ctx->stop_wq);
17}
18
Arnd Bergmann9add11d2006-10-04 17:26:14 +020019void 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:
Arnd Bergmann453d9f72006-11-20 18:45:03 +010029 case SPE_EVENT_SPE_DATA_STORAGE:
Arnd Bergmann9add11d2006-10-04 17:26:14 +020030 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
Arnd Bergmannce8ab852006-01-04 20:31:29 +010040static 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
Arnd Bergmann9add11d2006-10-04 17:26:14 +020054static inline int spu_run_init(struct spu_context *ctx, u32 * npc)
Arnd Bergmannce8ab852006-01-04 20:31:29 +010055{
56 int ret;
Mark Nutter5737edd2006-10-24 18:31:16 +020057 unsigned long runcntl = SPU_RUNCNTL_RUNNABLE;
Arnd Bergmannce8ab852006-01-04 20:31:29 +010058
59 if ((ret = spu_acquire_runnable(ctx)) != 0)
60 return ret;
Mark Nutter5737edd2006-10-24 18:31:16 +020061
arnd@arndb.de0afacde2006-10-24 18:31:18 +020062 /* 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)) {
Mark Nutter5737edd2006-10-24 18:31:16 +020065 ctx->ops->npc_write(ctx, *npc);
arnd@arndb.de0afacde2006-10-24 18:31:18 +020066 ctx->ops->runcntl_write(ctx, runcntl);
67 }
Arnd Bergmannce8ab852006-01-04 20:31:29 +010068 return 0;
69}
70
71static 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 if (unlikely(current->ptrace & PT_PTRACED)) {
83 if ((*status & SPU_STATUS_STOPPED_BY_STOP)
84 && (*status >> SPU_STOP_STATUS_SHIFT) == 0x3fff) {
85 force_sig(SIGTRAP, current);
86 ret = -ERESTARTSYS;
87 }
88 }
89 return ret;
90}
91
92static inline int spu_reacquire_runnable(struct spu_context *ctx, u32 *npc,
93 u32 *status)
94{
95 int ret;
96
97 if ((ret = spu_run_fini(ctx, npc, status)) != 0)
98 return ret;
99 if (*status & (SPU_STATUS_STOPPED_BY_STOP |
100 SPU_STATUS_STOPPED_BY_HALT)) {
101 return *status;
102 }
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200103 if ((ret = spu_run_init(ctx, npc)) != 0)
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100104 return ret;
105 return 0;
106}
107
Arnd Bergmann2dd14932006-03-23 00:00:09 +0100108/*
109 * SPU syscall restarting is tricky because we violate the basic
110 * assumption that the signal handler is running on the interrupted
111 * thread. Here instead, the handler runs on PowerPC user space code,
112 * while the syscall was called from the SPU.
113 * This means we can only do a very rough approximation of POSIX
114 * signal semantics.
115 */
116int spu_handle_restartsys(struct spu_context *ctx, long *spu_ret,
117 unsigned int *npc)
118{
119 int ret;
120
121 switch (*spu_ret) {
122 case -ERESTARTSYS:
123 case -ERESTARTNOINTR:
124 /*
125 * Enter the regular syscall restarting for
126 * sys_spu_run, then restart the SPU syscall
127 * callback.
128 */
129 *npc -= 8;
130 ret = -ERESTARTSYS;
131 break;
132 case -ERESTARTNOHAND:
133 case -ERESTART_RESTARTBLOCK:
134 /*
135 * Restart block is too hard for now, just return -EINTR
136 * to the SPU.
137 * ERESTARTNOHAND comes from sys_pause, we also return
138 * -EINTR from there.
139 * Assume that we need to be restarted ourselves though.
140 */
141 *spu_ret = -EINTR;
142 ret = -ERESTARTSYS;
143 break;
144 default:
145 printk(KERN_WARNING "%s: unexpected return code %ld\n",
146 __FUNCTION__, *spu_ret);
147 ret = 0;
148 }
149 return ret;
150}
151
152int spu_process_callback(struct spu_context *ctx)
153{
154 struct spu_syscall_block s;
155 u32 ls_pointer, npc;
156 char *ls;
157 long spu_ret;
158 int ret;
159
160 /* get syscall block from local store */
161 npc = ctx->ops->npc_read(ctx);
162 ls = ctx->ops->get_ls(ctx);
163 ls_pointer = *(u32*)(ls + npc);
164 if (ls_pointer > (LS_SIZE - sizeof(s)))
165 return -EFAULT;
166 memcpy(&s, ls + ls_pointer, sizeof (s));
167
168 /* do actual syscall without pinning the spu */
169 ret = 0;
170 spu_ret = -ENOSYS;
171 npc += 4;
172
173 if (s.nr_ret < __NR_syscalls) {
174 spu_release(ctx);
175 /* do actual system call from here */
176 spu_ret = spu_sys_callback(&s);
177 if (spu_ret <= -ERESTARTSYS) {
178 ret = spu_handle_restartsys(ctx, &spu_ret, &npc);
179 }
180 spu_acquire(ctx);
181 if (ret == -ERESTARTSYS)
182 return ret;
183 }
184
185 /* write result, jump over indirect pointer */
186 memcpy(ls + ls_pointer, &spu_ret, sizeof (spu_ret));
187 ctx->ops->npc_write(ctx, npc);
188 ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
189 return ret;
190}
191
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100192static inline int spu_process_events(struct spu_context *ctx)
193{
194 struct spu *spu = ctx->spu;
195 u64 pte_fault = MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED;
196 int ret = 0;
197
198 if (spu->dsisr & pte_fault)
199 ret = spu_irq_class_1_bottom(spu);
200 if (spu->class_0_pending)
201 ret = spu_irq_class_0_bottom(spu);
202 if (!ret && signal_pending(current))
203 ret = -ERESTARTSYS;
204 return ret;
205}
206
207long spufs_run_spu(struct file *file, struct spu_context *ctx,
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200208 u32 *npc, u32 *event)
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100209{
210 int ret;
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200211 u32 status;
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100212
213 if (down_interruptible(&ctx->run_sema))
214 return -ERESTARTSYS;
215
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200216 ctx->event_return = 0;
217 ret = spu_run_init(ctx, npc);
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100218 if (ret)
219 goto out;
220
221 do {
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200222 ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status));
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100223 if (unlikely(ret))
224 break;
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200225 if ((status & SPU_STATUS_STOPPED_BY_STOP) &&
226 (status >> SPU_STOP_STATUS_SHIFT == 0x2104)) {
Arnd Bergmann2dd14932006-03-23 00:00:09 +0100227 ret = spu_process_callback(ctx);
228 if (ret)
229 break;
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200230 status &= ~SPU_STATUS_STOPPED_BY_STOP;
Arnd Bergmann2dd14932006-03-23 00:00:09 +0100231 }
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100232 if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) {
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200233 ret = spu_reacquire_runnable(ctx, npc, &status);
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100234 if (ret)
235 goto out;
236 continue;
237 }
238 ret = spu_process_events(ctx);
239
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200240 } while (!ret && !(status & (SPU_STATUS_STOPPED_BY_STOP |
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100241 SPU_STATUS_STOPPED_BY_HALT)));
242
243 ctx->ops->runcntl_stop(ctx);
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200244 ret = spu_run_fini(ctx, npc, &status);
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100245 if (!ret)
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200246 ret = status;
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100247 spu_yield(ctx);
248
249out:
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200250 *event = ctx->event_return;
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100251 up(&ctx->run_sema);
252 return ret;
253}
254