blob: 63df8cf4ba1607e13ee8c01cdab8a392da12835b [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;
54
55 if ((ret = spu_acquire_runnable(ctx)) != 0)
56 return ret;
57 ctx->ops->npc_write(ctx, *npc);
58 ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
59 return 0;
60}
61
62static inline int spu_run_fini(struct spu_context *ctx, u32 * npc,
63 u32 * status)
64{
65 int ret = 0;
66
67 *status = ctx->ops->status_read(ctx);
68 *npc = ctx->ops->npc_read(ctx);
69 spu_release(ctx);
70
71 if (signal_pending(current))
72 ret = -ERESTARTSYS;
73 if (unlikely(current->ptrace & PT_PTRACED)) {
74 if ((*status & SPU_STATUS_STOPPED_BY_STOP)
75 && (*status >> SPU_STOP_STATUS_SHIFT) == 0x3fff) {
76 force_sig(SIGTRAP, current);
77 ret = -ERESTARTSYS;
78 }
79 }
80 return ret;
81}
82
83static inline int spu_reacquire_runnable(struct spu_context *ctx, u32 *npc,
84 u32 *status)
85{
86 int ret;
87
88 if ((ret = spu_run_fini(ctx, npc, status)) != 0)
89 return ret;
90 if (*status & (SPU_STATUS_STOPPED_BY_STOP |
91 SPU_STATUS_STOPPED_BY_HALT)) {
92 return *status;
93 }
Arnd Bergmann9add11d2006-10-04 17:26:14 +020094 if ((ret = spu_run_init(ctx, npc)) != 0)
Arnd Bergmannce8ab852006-01-04 20:31:29 +010095 return ret;
96 return 0;
97}
98
Arnd Bergmann2dd14932006-03-23 00:00:09 +010099/*
100 * SPU syscall restarting is tricky because we violate the basic
101 * assumption that the signal handler is running on the interrupted
102 * thread. Here instead, the handler runs on PowerPC user space code,
103 * while the syscall was called from the SPU.
104 * This means we can only do a very rough approximation of POSIX
105 * signal semantics.
106 */
107int spu_handle_restartsys(struct spu_context *ctx, long *spu_ret,
108 unsigned int *npc)
109{
110 int ret;
111
112 switch (*spu_ret) {
113 case -ERESTARTSYS:
114 case -ERESTARTNOINTR:
115 /*
116 * Enter the regular syscall restarting for
117 * sys_spu_run, then restart the SPU syscall
118 * callback.
119 */
120 *npc -= 8;
121 ret = -ERESTARTSYS;
122 break;
123 case -ERESTARTNOHAND:
124 case -ERESTART_RESTARTBLOCK:
125 /*
126 * Restart block is too hard for now, just return -EINTR
127 * to the SPU.
128 * ERESTARTNOHAND comes from sys_pause, we also return
129 * -EINTR from there.
130 * Assume that we need to be restarted ourselves though.
131 */
132 *spu_ret = -EINTR;
133 ret = -ERESTARTSYS;
134 break;
135 default:
136 printk(KERN_WARNING "%s: unexpected return code %ld\n",
137 __FUNCTION__, *spu_ret);
138 ret = 0;
139 }
140 return ret;
141}
142
143int spu_process_callback(struct spu_context *ctx)
144{
145 struct spu_syscall_block s;
146 u32 ls_pointer, npc;
147 char *ls;
148 long spu_ret;
149 int ret;
150
151 /* get syscall block from local store */
152 npc = ctx->ops->npc_read(ctx);
153 ls = ctx->ops->get_ls(ctx);
154 ls_pointer = *(u32*)(ls + npc);
155 if (ls_pointer > (LS_SIZE - sizeof(s)))
156 return -EFAULT;
157 memcpy(&s, ls + ls_pointer, sizeof (s));
158
159 /* do actual syscall without pinning the spu */
160 ret = 0;
161 spu_ret = -ENOSYS;
162 npc += 4;
163
164 if (s.nr_ret < __NR_syscalls) {
165 spu_release(ctx);
166 /* do actual system call from here */
167 spu_ret = spu_sys_callback(&s);
168 if (spu_ret <= -ERESTARTSYS) {
169 ret = spu_handle_restartsys(ctx, &spu_ret, &npc);
170 }
171 spu_acquire(ctx);
172 if (ret == -ERESTARTSYS)
173 return ret;
174 }
175
176 /* write result, jump over indirect pointer */
177 memcpy(ls + ls_pointer, &spu_ret, sizeof (spu_ret));
178 ctx->ops->npc_write(ctx, npc);
179 ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
180 return ret;
181}
182
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100183static inline int spu_process_events(struct spu_context *ctx)
184{
185 struct spu *spu = ctx->spu;
186 u64 pte_fault = MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED;
187 int ret = 0;
188
189 if (spu->dsisr & pte_fault)
190 ret = spu_irq_class_1_bottom(spu);
191 if (spu->class_0_pending)
192 ret = spu_irq_class_0_bottom(spu);
193 if (!ret && signal_pending(current))
194 ret = -ERESTARTSYS;
195 return ret;
196}
197
198long spufs_run_spu(struct file *file, struct spu_context *ctx,
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200199 u32 *npc, u32 *event)
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100200{
201 int ret;
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200202 u32 status;
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100203
204 if (down_interruptible(&ctx->run_sema))
205 return -ERESTARTSYS;
206
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200207 ctx->event_return = 0;
208 ret = spu_run_init(ctx, npc);
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100209 if (ret)
210 goto out;
211
212 do {
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200213 ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status));
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100214 if (unlikely(ret))
215 break;
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200216 if ((status & SPU_STATUS_STOPPED_BY_STOP) &&
217 (status >> SPU_STOP_STATUS_SHIFT == 0x2104)) {
Arnd Bergmann2dd14932006-03-23 00:00:09 +0100218 ret = spu_process_callback(ctx);
219 if (ret)
220 break;
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200221 status &= ~SPU_STATUS_STOPPED_BY_STOP;
Arnd Bergmann2dd14932006-03-23 00:00:09 +0100222 }
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100223 if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) {
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200224 ret = spu_reacquire_runnable(ctx, npc, &status);
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100225 if (ret)
226 goto out;
227 continue;
228 }
229 ret = spu_process_events(ctx);
230
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200231 } while (!ret && !(status & (SPU_STATUS_STOPPED_BY_STOP |
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100232 SPU_STATUS_STOPPED_BY_HALT)));
233
234 ctx->ops->runcntl_stop(ctx);
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200235 ret = spu_run_fini(ctx, npc, &status);
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100236 if (!ret)
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200237 ret = status;
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100238 spu_yield(ctx);
239
240out:
Arnd Bergmann9add11d2006-10-04 17:26:14 +0200241 *event = ctx->event_return;
Arnd Bergmannce8ab852006-01-04 20:31:29 +0100242 up(&ctx->run_sema);
243 return ret;
244}
245