blob: 0c03a04b6a3045df9a5322c252b5ae68d6f37029 [file] [log] [blame]
Arnd Bergmannce8ab852006-01-04 20:31:29 +01001#include <linux/wait.h>
2#include <linux/ptrace.h>
3
4#include <asm/spu.h>
Dave Jonescfff5b22006-03-31 23:53:09 -05005#include <asm/unistd.h>
Arnd Bergmannce8ab852006-01-04 20:31:29 +01006
7#include "spufs.h"
8
9/* interrupt-level stop callback function. */
10void spufs_stop_callback(struct spu *spu)
11{
12 struct spu_context *ctx = spu->ctx;
13
14 wake_up_all(&ctx->stop_wq);
15}
16
Arnd Bergmann9add11d2006-10-04 17:26:14 +020017void spufs_dma_callback(struct spu *spu, int type)
18{
19 struct spu_context *ctx = spu->ctx;
20
21 if (ctx->flags & SPU_CREATE_EVENTS_ENABLED) {
22 ctx->event_return |= type;
23 wake_up_all(&ctx->stop_wq);
24 } else {
25 switch (type) {
26 case SPE_EVENT_DMA_ALIGNMENT:
27 case SPE_EVENT_INVALID_DMA:
28 force_sig(SIGBUS, /* info, */ current);
29 break;
30 case SPE_EVENT_SPE_ERROR:
31 force_sig(SIGILL, /* info */ current);
32 break;
33 }
34 }
35}
36
Arnd Bergmannce8ab852006-01-04 20:31:29 +010037static inline int spu_stopped(struct spu_context *ctx, u32 * stat)
38{
39 struct spu *spu;
40 u64 pte_fault;
41
42 *stat = ctx->ops->status_read(ctx);
43 if (ctx->state != SPU_STATE_RUNNABLE)
44 return 1;
45 spu = ctx->spu;
46 pte_fault = spu->dsisr &
47 (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED);
48 return (!(*stat & 0x1) || pte_fault || spu->class_0_pending) ? 1 : 0;
49}
50
Arnd Bergmann9add11d2006-10-04 17:26:14 +020051static inline int spu_run_init(struct spu_context *ctx, u32 * npc)
Arnd Bergmannce8ab852006-01-04 20:31:29 +010052{
53 int ret;
Mark Nutter5737edd2006-10-24 18:31:16 +020054 unsigned long runcntl = SPU_RUNCNTL_RUNNABLE;
Arnd Bergmannce8ab852006-01-04 20:31:29 +010055
56 if ((ret = spu_acquire_runnable(ctx)) != 0)
57 return ret;
Mark Nutter5737edd2006-10-24 18:31:16 +020058
59 if (ctx->flags & SPU_CREATE_ISOLATE)
60 runcntl |= SPU_RUNCNTL_ISOLATE;
61 else
62 ctx->ops->npc_write(ctx, *npc);
63
64 ctx->ops->runcntl_write(ctx, runcntl);
Arnd Bergmannce8ab852006-01-04 20:31:29 +010065 return 0;
66}
67
68static inline int spu_run_fini(struct spu_context *ctx, u32 * npc,
69 u32 * status)
70{
71 int ret = 0;
72
73 *status = ctx->ops->status_read(ctx);
74 *npc = ctx->ops->npc_read(ctx);
75 spu_release(ctx);
76
77 if (signal_pending(current))
78 ret = -ERESTARTSYS;
79 if (unlikely(current->ptrace & PT_PTRACED)) {
80 if ((*status & SPU_STATUS_STOPPED_BY_STOP)
81 && (*status >> SPU_STOP_STATUS_SHIFT) == 0x3fff) {
82 force_sig(SIGTRAP, current);
83 ret = -ERESTARTSYS;
84 }
85 }
86 return ret;
87}
88
89static inline int spu_reacquire_runnable(struct spu_context *ctx, u32 *npc,
90 u32 *status)
91{
92 int ret;
93
94 if ((ret = spu_run_fini(ctx, npc, status)) != 0)
95 return ret;
96 if (*status & (SPU_STATUS_STOPPED_BY_STOP |
97 SPU_STATUS_STOPPED_BY_HALT)) {
98 return *status;
99 }
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200100 if ((ret = spu_run_init(ctx, npc)) != 0)
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100101 return ret;
102 return 0;
103}
104
Arnd Bergmann2dd14932006-03-23 00:00:09 +0100105/*
106 * SPU syscall restarting is tricky because we violate the basic
107 * assumption that the signal handler is running on the interrupted
108 * thread. Here instead, the handler runs on PowerPC user space code,
109 * while the syscall was called from the SPU.
110 * This means we can only do a very rough approximation of POSIX
111 * signal semantics.
112 */
113int spu_handle_restartsys(struct spu_context *ctx, long *spu_ret,
114 unsigned int *npc)
115{
116 int ret;
117
118 switch (*spu_ret) {
119 case -ERESTARTSYS:
120 case -ERESTARTNOINTR:
121 /*
122 * Enter the regular syscall restarting for
123 * sys_spu_run, then restart the SPU syscall
124 * callback.
125 */
126 *npc -= 8;
127 ret = -ERESTARTSYS;
128 break;
129 case -ERESTARTNOHAND:
130 case -ERESTART_RESTARTBLOCK:
131 /*
132 * Restart block is too hard for now, just return -EINTR
133 * to the SPU.
134 * ERESTARTNOHAND comes from sys_pause, we also return
135 * -EINTR from there.
136 * Assume that we need to be restarted ourselves though.
137 */
138 *spu_ret = -EINTR;
139 ret = -ERESTARTSYS;
140 break;
141 default:
142 printk(KERN_WARNING "%s: unexpected return code %ld\n",
143 __FUNCTION__, *spu_ret);
144 ret = 0;
145 }
146 return ret;
147}
148
149int spu_process_callback(struct spu_context *ctx)
150{
151 struct spu_syscall_block s;
152 u32 ls_pointer, npc;
153 char *ls;
154 long spu_ret;
155 int ret;
156
157 /* get syscall block from local store */
158 npc = ctx->ops->npc_read(ctx);
159 ls = ctx->ops->get_ls(ctx);
160 ls_pointer = *(u32*)(ls + npc);
161 if (ls_pointer > (LS_SIZE - sizeof(s)))
162 return -EFAULT;
163 memcpy(&s, ls + ls_pointer, sizeof (s));
164
165 /* do actual syscall without pinning the spu */
166 ret = 0;
167 spu_ret = -ENOSYS;
168 npc += 4;
169
170 if (s.nr_ret < __NR_syscalls) {
171 spu_release(ctx);
172 /* do actual system call from here */
173 spu_ret = spu_sys_callback(&s);
174 if (spu_ret <= -ERESTARTSYS) {
175 ret = spu_handle_restartsys(ctx, &spu_ret, &npc);
176 }
177 spu_acquire(ctx);
178 if (ret == -ERESTARTSYS)
179 return ret;
180 }
181
182 /* write result, jump over indirect pointer */
183 memcpy(ls + ls_pointer, &spu_ret, sizeof (spu_ret));
184 ctx->ops->npc_write(ctx, npc);
185 ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
186 return ret;
187}
188
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100189static inline int spu_process_events(struct spu_context *ctx)
190{
191 struct spu *spu = ctx->spu;
192 u64 pte_fault = MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED;
193 int ret = 0;
194
195 if (spu->dsisr & pte_fault)
196 ret = spu_irq_class_1_bottom(spu);
197 if (spu->class_0_pending)
198 ret = spu_irq_class_0_bottom(spu);
199 if (!ret && signal_pending(current))
200 ret = -ERESTARTSYS;
201 return ret;
202}
203
204long spufs_run_spu(struct file *file, struct spu_context *ctx,
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200205 u32 *npc, u32 *event)
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100206{
207 int ret;
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200208 u32 status;
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100209
210 if (down_interruptible(&ctx->run_sema))
211 return -ERESTARTSYS;
212
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200213 ctx->event_return = 0;
214 ret = spu_run_init(ctx, npc);
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100215 if (ret)
216 goto out;
217
218 do {
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200219 ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status));
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100220 if (unlikely(ret))
221 break;
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200222 if ((status & SPU_STATUS_STOPPED_BY_STOP) &&
223 (status >> SPU_STOP_STATUS_SHIFT == 0x2104)) {
Arnd Bergmann2dd14932006-03-23 00:00:09 +0100224 ret = spu_process_callback(ctx);
225 if (ret)
226 break;
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200227 status &= ~SPU_STATUS_STOPPED_BY_STOP;
Arnd Bergmann2dd14932006-03-23 00:00:09 +0100228 }
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100229 if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) {
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200230 ret = spu_reacquire_runnable(ctx, npc, &status);
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100231 if (ret)
232 goto out;
233 continue;
234 }
235 ret = spu_process_events(ctx);
236
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200237 } while (!ret && !(status & (SPU_STATUS_STOPPED_BY_STOP |
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100238 SPU_STATUS_STOPPED_BY_HALT)));
239
240 ctx->ops->runcntl_stop(ctx);
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200241 ret = spu_run_fini(ctx, npc, &status);
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100242 if (!ret)
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200243 ret = status;
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100244 spu_yield(ctx);
245
246out:
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200247 *event = ctx->event_return;
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100248 up(&ctx->run_sema);
249 return ret;
250}
251