~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/powerpc/platforms/cell/spufs/run.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 #define DEBUG
  3 
  4 #include <linux/wait.h>
  5 #include <linux/ptrace.h>
  6 
  7 #include <asm/spu.h>
  8 #include <asm/spu_priv1.h>
  9 #include <asm/io.h>
 10 #include <asm/unistd.h>
 11 
 12 #include "spufs.h"
 13 
 14 /* interrupt-level stop callback function. */
 15 void spufs_stop_callback(struct spu *spu, int irq)
 16 {
 17         struct spu_context *ctx = spu->ctx;
 18 
 19         /*
 20          * It should be impossible to preempt a context while an exception
 21          * is being processed, since the context switch code is specially
 22          * coded to deal with interrupts ... But, just in case, sanity check
 23          * the context pointer.  It is OK to return doing nothing since
 24          * the exception will be regenerated when the context is resumed.
 25          */
 26         if (ctx) {
 27                 /* Copy exception arguments into module specific structure */
 28                 switch(irq) {
 29                 case 0 :
 30                         ctx->csa.class_0_pending = spu->class_0_pending;
 31                         ctx->csa.class_0_dar = spu->class_0_dar;
 32                         break;
 33                 case 1 :
 34                         ctx->csa.class_1_dsisr = spu->class_1_dsisr;
 35                         ctx->csa.class_1_dar = spu->class_1_dar;
 36                         break;
 37                 case 2 :
 38                         break;
 39                 }
 40 
 41                 /* ensure that the exception status has hit memory before a
 42                  * thread waiting on the context's stop queue is woken */
 43                 smp_wmb();
 44 
 45                 wake_up_all(&ctx->stop_wq);
 46         }
 47 }
 48 
 49 int spu_stopped(struct spu_context *ctx, u32 *stat)
 50 {
 51         u64 dsisr;
 52         u32 stopped;
 53 
 54         stopped = SPU_STATUS_INVALID_INSTR | SPU_STATUS_SINGLE_STEP |
 55                 SPU_STATUS_STOPPED_BY_HALT | SPU_STATUS_STOPPED_BY_STOP;
 56 
 57 top:
 58         *stat = ctx->ops->status_read(ctx);
 59         if (*stat & stopped) {
 60                 /*
 61                  * If the spu hasn't finished stopping, we need to
 62                  * re-read the register to get the stopped value.
 63                  */
 64                 if (*stat & SPU_STATUS_RUNNING)
 65                         goto top;
 66                 return 1;
 67         }
 68 
 69         if (test_bit(SPU_SCHED_NOTIFY_ACTIVE, &ctx->sched_flags))
 70                 return 1;
 71 
 72         dsisr = ctx->csa.class_1_dsisr;
 73         if (dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED))
 74                 return 1;
 75 
 76         if (ctx->csa.class_0_pending)
 77                 return 1;
 78 
 79         return 0;
 80 }
 81 
 82 static int spu_setup_isolated(struct spu_context *ctx)
 83 {
 84         int ret;
 85         u64 __iomem *mfc_cntl;
 86         u64 sr1;
 87         u32 status;
 88         unsigned long timeout;
 89         const u32 status_loading = SPU_STATUS_RUNNING
 90                 | SPU_STATUS_ISOLATED_STATE | SPU_STATUS_ISOLATED_LOAD_STATUS;
 91 
 92         ret = -ENODEV;
 93         if (!isolated_loader)
 94                 goto out;
 95 
 96         /*
 97          * We need to exclude userspace access to the context.
 98          *
 99          * To protect against memory access we invalidate all ptes
100          * and make sure the pagefault handlers block on the mutex.
101          */
102         spu_unmap_mappings(ctx);
103 
104         mfc_cntl = &ctx->spu->priv2->mfc_control_RW;
105 
106         /* purge the MFC DMA queue to ensure no spurious accesses before we
107          * enter kernel mode */
108         timeout = jiffies + HZ;
109         out_be64(mfc_cntl, MFC_CNTL_PURGE_DMA_REQUEST);
110         while ((in_be64(mfc_cntl) & MFC_CNTL_PURGE_DMA_STATUS_MASK)
111                         != MFC_CNTL_PURGE_DMA_COMPLETE) {
112                 if (time_after(jiffies, timeout)) {
113                         printk(KERN_ERR "%s: timeout flushing MFC DMA queue\n",
114                                         __func__);
115                         ret = -EIO;
116                         goto out;
117                 }
118                 cond_resched();
119         }
120 
121         /* clear purge status */
122         out_be64(mfc_cntl, 0);
123 
124         /* put the SPE in kernel mode to allow access to the loader */
125         sr1 = spu_mfc_sr1_get(ctx->spu);
126         sr1 &= ~MFC_STATE1_PROBLEM_STATE_MASK;
127         spu_mfc_sr1_set(ctx->spu, sr1);
128 
129         /* start the loader */
130         ctx->ops->signal1_write(ctx, (unsigned long)isolated_loader >> 32);
131         ctx->ops->signal2_write(ctx,
132                         (unsigned long)isolated_loader & 0xffffffff);
133 
134         ctx->ops->runcntl_write(ctx,
135                         SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE);
136 
137         ret = 0;
138         timeout = jiffies + HZ;
139         while (((status = ctx->ops->status_read(ctx)) & status_loading) ==
140                                 status_loading) {
141                 if (time_after(jiffies, timeout)) {
142                         printk(KERN_ERR "%s: timeout waiting for loader\n",
143                                         __func__);
144                         ret = -EIO;
145                         goto out_drop_priv;
146                 }
147                 cond_resched();
148         }
149 
150         if (!(status & SPU_STATUS_RUNNING)) {
151                 /* If isolated LOAD has failed: run SPU, we will get a stop-and
152                  * signal later. */
153                 pr_debug("%s: isolated LOAD failed\n", __func__);
154                 ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
155                 ret = -EACCES;
156                 goto out_drop_priv;
157         }
158 
159         if (!(status & SPU_STATUS_ISOLATED_STATE)) {
160                 /* This isn't allowed by the CBEA, but check anyway */
161                 pr_debug("%s: SPU fell out of isolated mode?\n", __func__);
162                 ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_STOP);
163                 ret = -EINVAL;
164                 goto out_drop_priv;
165         }
166 
167 out_drop_priv:
168         /* Finished accessing the loader. Drop kernel mode */
169         sr1 |= MFC_STATE1_PROBLEM_STATE_MASK;
170         spu_mfc_sr1_set(ctx->spu, sr1);
171 
172 out:
173         return ret;
174 }
175 
176 static int spu_run_init(struct spu_context *ctx, u32 *npc)
177 {
178         unsigned long runcntl = SPU_RUNCNTL_RUNNABLE;
179         int ret;
180 
181         spuctx_switch_state(ctx, SPU_UTIL_SYSTEM);
182 
183         /*
184          * NOSCHED is synchronous scheduling with respect to the caller.
185          * The caller waits for the context to be loaded.
186          */
187         if (ctx->flags & SPU_CREATE_NOSCHED) {
188                 if (ctx->state == SPU_STATE_SAVED) {
189                         ret = spu_activate(ctx, 0);
190                         if (ret)
191                                 return ret;
192                 }
193         }
194 
195         /*
196          * Apply special setup as required.
197          */
198         if (ctx->flags & SPU_CREATE_ISOLATE) {
199                 if (!(ctx->ops->status_read(ctx) & SPU_STATUS_ISOLATED_STATE)) {
200                         ret = spu_setup_isolated(ctx);
201                         if (ret)
202                                 return ret;
203                 }
204 
205                 /*
206                  * If userspace has set the runcntrl register (eg, to
207                  * issue an isolated exit), we need to re-set it here
208                  */
209                 runcntl = ctx->ops->runcntl_read(ctx) &
210                         (SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE);
211                 if (runcntl == 0)
212                         runcntl = SPU_RUNCNTL_RUNNABLE;
213         } else {
214                 unsigned long privcntl;
215 
216                 if (test_thread_flag(TIF_SINGLESTEP))
217                         privcntl = SPU_PRIVCNTL_MODE_SINGLE_STEP;
218                 else
219                         privcntl = SPU_PRIVCNTL_MODE_NORMAL;
220 
221                 ctx->ops->privcntl_write(ctx, privcntl);
222                 ctx->ops->npc_write(ctx, *npc);
223         }
224 
225         ctx->ops->runcntl_write(ctx, runcntl);
226 
227         if (ctx->flags & SPU_CREATE_NOSCHED) {
228                 spuctx_switch_state(ctx, SPU_UTIL_USER);
229         } else {
230 
231                 if (ctx->state == SPU_STATE_SAVED) {
232                         ret = spu_activate(ctx, 0);
233                         if (ret)
234                                 return ret;
235                 } else {
236                         spuctx_switch_state(ctx, SPU_UTIL_USER);
237                 }
238         }
239 
240         set_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags);
241         return 0;
242 }
243 
244 static int spu_run_fini(struct spu_context *ctx, u32 *npc,
245                                u32 *status)
246 {
247         int ret = 0;
248 
249         spu_del_from_rq(ctx);
250 
251         *status = ctx->ops->status_read(ctx);
252         *npc = ctx->ops->npc_read(ctx);
253 
254         spuctx_switch_state(ctx, SPU_UTIL_IDLE_LOADED);
255         clear_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags);
256         spu_switch_log_notify(NULL, ctx, SWITCH_LOG_EXIT, *status);
257         spu_release(ctx);
258 
259         if (signal_pending(current))
260                 ret = -ERESTARTSYS;
261 
262         return ret;
263 }
264 
265 /*
266  * SPU syscall restarting is tricky because we violate the basic
267  * assumption that the signal handler is running on the interrupted
268  * thread. Here instead, the handler runs on PowerPC user space code,
269  * while the syscall was called from the SPU.
270  * This means we can only do a very rough approximation of POSIX
271  * signal semantics.
272  */
273 static int spu_handle_restartsys(struct spu_context *ctx, long *spu_ret,
274                           unsigned int *npc)
275 {
276         int ret;
277 
278         switch (*spu_ret) {
279         case -ERESTARTSYS:
280         case -ERESTARTNOINTR:
281                 /*
282                  * Enter the regular syscall restarting for
283                  * sys_spu_run, then restart the SPU syscall
284                  * callback.
285                  */
286                 *npc -= 8;
287                 ret = -ERESTARTSYS;
288                 break;
289         case -ERESTARTNOHAND:
290         case -ERESTART_RESTARTBLOCK:
291                 /*
292                  * Restart block is too hard for now, just return -EINTR
293                  * to the SPU.
294                  * ERESTARTNOHAND comes from sys_pause, we also return
295                  * -EINTR from there.
296                  * Assume that we need to be restarted ourselves though.
297                  */
298                 *spu_ret = -EINTR;
299                 ret = -ERESTARTSYS;
300                 break;
301         default:
302                 printk(KERN_WARNING "%s: unexpected return code %ld\n",
303                         __func__, *spu_ret);
304                 ret = 0;
305         }
306         return ret;
307 }
308 
309 static int spu_process_callback(struct spu_context *ctx)
310 {
311         struct spu_syscall_block s;
312         u32 ls_pointer, npc;
313         void __iomem *ls;
314         long spu_ret;
315         int ret;
316 
317         /* get syscall block from local store */
318         npc = ctx->ops->npc_read(ctx) & ~3;
319         ls = (void __iomem *)ctx->ops->get_ls(ctx);
320         ls_pointer = in_be32(ls + npc);
321         if (ls_pointer > (LS_SIZE - sizeof(s)))
322                 return -EFAULT;
323         memcpy_fromio(&s, ls + ls_pointer, sizeof(s));
324 
325         /* do actual syscall without pinning the spu */
326         ret = 0;
327         spu_ret = -ENOSYS;
328         npc += 4;
329 
330         if (s.nr_ret < NR_syscalls) {
331                 spu_release(ctx);
332                 /* do actual system call from here */
333                 spu_ret = spu_sys_callback(&s);
334                 if (spu_ret <= -ERESTARTSYS) {
335                         ret = spu_handle_restartsys(ctx, &spu_ret, &npc);
336                 }
337                 mutex_lock(&ctx->state_mutex);
338                 if (ret == -ERESTARTSYS)
339                         return ret;
340         }
341 
342         /* need to re-get the ls, as it may have changed when we released the
343          * spu */
344         ls = (void __iomem *)ctx->ops->get_ls(ctx);
345 
346         /* write result, jump over indirect pointer */
347         memcpy_toio(ls + ls_pointer, &spu_ret, sizeof(spu_ret));
348         ctx->ops->npc_write(ctx, npc);
349         ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
350         return ret;
351 }
352 
353 long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event)
354 {
355         int ret;
356         u32 status;
357 
358         if (mutex_lock_interruptible(&ctx->run_mutex))
359                 return -ERESTARTSYS;
360 
361         ctx->event_return = 0;
362 
363         ret = spu_acquire(ctx);
364         if (ret)
365                 goto out_unlock;
366 
367         spu_enable_spu(ctx);
368 
369         spu_update_sched_info(ctx);
370 
371         ret = spu_run_init(ctx, npc);
372         if (ret) {
373                 spu_release(ctx);
374                 goto out;
375         }
376 
377         do {
378                 ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status));
379                 if (unlikely(ret)) {
380                         /*
381                          * This is nasty: we need the state_mutex for all the
382                          * bookkeeping even if the syscall was interrupted by
383                          * a signal. ewww.
384                          */
385                         mutex_lock(&ctx->state_mutex);
386                         break;
387                 }
388                 if (unlikely(test_and_clear_bit(SPU_SCHED_NOTIFY_ACTIVE,
389                                                 &ctx->sched_flags))) {
390                         if (!(status & SPU_STATUS_STOPPED_BY_STOP))
391                                 continue;
392                 }
393 
394                 spuctx_switch_state(ctx, SPU_UTIL_SYSTEM);
395 
396                 if ((status & SPU_STATUS_STOPPED_BY_STOP) &&
397                     (status >> SPU_STOP_STATUS_SHIFT == 0x2104)) {
398                         ret = spu_process_callback(ctx);
399                         if (ret)
400                                 break;
401                         status &= ~SPU_STATUS_STOPPED_BY_STOP;
402                 }
403                 ret = spufs_handle_class1(ctx);
404                 if (ret)
405                         break;
406 
407                 ret = spufs_handle_class0(ctx);
408                 if (ret)
409                         break;
410 
411                 if (signal_pending(current))
412                         ret = -ERESTARTSYS;
413         } while (!ret && !(status & (SPU_STATUS_STOPPED_BY_STOP |
414                                       SPU_STATUS_STOPPED_BY_HALT |
415                                        SPU_STATUS_SINGLE_STEP)));
416 
417         spu_disable_spu(ctx);
418         ret = spu_run_fini(ctx, npc, &status);
419         spu_yield(ctx);
420 
421         if ((status & SPU_STATUS_STOPPED_BY_STOP) &&
422             (((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100))
423                 ctx->stats.libassist++;
424 
425         if ((ret == 0) ||
426             ((ret == -ERESTARTSYS) &&
427              ((status & SPU_STATUS_STOPPED_BY_HALT) ||
428               (status & SPU_STATUS_SINGLE_STEP) ||
429               ((status & SPU_STATUS_STOPPED_BY_STOP) &&
430                (status >> SPU_STOP_STATUS_SHIFT != 0x2104)))))
431                 ret = status;
432 
433         /* Note: we don't need to force_sig SIGTRAP on single-step
434          * since we have TIF_SINGLESTEP set, thus the kernel will do
435          * it upon return from the syscall anyway.
436          */
437         if (unlikely(status & SPU_STATUS_SINGLE_STEP))
438                 ret = -ERESTARTSYS;
439 
440         else if (unlikely((status & SPU_STATUS_STOPPED_BY_STOP)
441             && (status >> SPU_STOP_STATUS_SHIFT) == 0x3fff)) {
442                 force_sig(SIGTRAP);
443                 ret = -ERESTARTSYS;
444         }
445 
446 out:
447         *event = ctx->event_return;
448 out_unlock:
449         mutex_unlock(&ctx->run_mutex);
450         return ret;
451 }
452 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php