libspe2  0.9a
run.c
Go to the documentation of this file.
1 /*
2  * libspe2 - A wrapper library to adapt the JSRE SPU usage model to SPUFS
3  * Copyright (C) 2005 IBM Corp.
4  *
5  * This library is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation; either version 2.1 of the License,
8  * or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
13  * License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this library; if not, write to the Free Software Foundation,
17  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */
19 #include <errno.h>
20 #define GNU_SOURCE 1
21 
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <stdint.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <syscall.h>
28 #include <unistd.h>
29 
30 #include <sys/types.h>
31 #include <sys/mman.h>
32 
33 #include <sys/spu.h>
34 
35 #include "elf_loader.h"
36 #include "lib_builtin.h"
37 #include "spebase.h"
38 
39 /*Thread-local variable for use by the debugger*/
40 __thread struct spe_context_info {
41  int spe_id;
42  unsigned int npc;
43  unsigned int status;
46 
47 
48 static void cleanupspeinfo(struct spe_context_info *ctxinfo)
49 {
50  struct spe_context_info *tmp = ctxinfo->prev;
52 }
53 
54 static int set_regs(struct spe_context *spe, void *regs)
55 {
56  int fd_regs, rc;
57 
58  fd_regs = openat(spe->base_private->fd_spe_dir, "regs", O_RDWR);
59  if (fd_regs < 0) {
60  DEBUG_PRINTF("Could not open SPE regs file.\n");
61  errno = EFAULT;
62  return -1;
63  }
64 
65  rc = write(fd_regs, regs, 2048);
66 
67  close(fd_regs);
68 
69  if (rc < 0)
70  return -1;
71 
72  return 0;
73 }
74 
75 static int issue_isolated_exit(struct spe_context *spe)
76 {
77  struct spe_spu_control_area *cntl_area =
79 
80  if (cntl_area == MAP_FAILED) {
81  DEBUG_PRINTF("%s: could not access SPE control area\n",
82  __FUNCTION__);
83  return -1;
84  }
85 
86  /* 0x2 is an isolated exit request */
87  cntl_area->SPU_RunCntl = 0x2;
88 
89  return 0;
90 }
91 
92 static inline void freespeinfo()
93 {
94  /*Clean up the debug variable*/
97 }
98 
99 int _base_spe_context_run(spe_context_ptr_t spe, unsigned int *entry,
100  unsigned int runflags, void *argp, void *envp,
101  spe_stop_info_t *stopinfo)
102 {
103  int retval = 0, run_rc;
104  unsigned int run_status, tmp_entry;
105  spe_stop_info_t stopinfo_buf;
106  struct spe_context_info this_context_info __attribute__((cleanup(cleanupspeinfo)));
107 
108  /* If the caller hasn't set a stopinfo buffer, provide a buffer on the
109  * stack instead. */
110  if (!stopinfo)
111  stopinfo = &stopinfo_buf;
112 
113 
114  /* In emulated isolated mode, the npc will always return as zero.
115  * use our private entry point instead */
117  tmp_entry = spe->base_private->emulated_entry;
118 
119  else if (*entry == SPE_DEFAULT_ENTRY)
120  tmp_entry = spe->base_private->entry;
121  else
122  tmp_entry = *entry;
123 
124  /* If we're starting the SPE binary from its original entry point,
125  * setup the arguments to main() */
126  if (tmp_entry == spe->base_private->entry &&
127  !(spe->base_private->flags &
129 
130  addr64 argp64, envp64, tid64, ls64;
131  unsigned int regs[128][4];
132 
133  /* setup parameters */
134  argp64.ull = (uint64_t)(unsigned long)argp;
135  envp64.ull = (uint64_t)(unsigned long)envp;
136  tid64.ull = (uint64_t)(unsigned long)spe;
137 
138  /* make sure the register values are 0 */
139  memset(regs, 0, sizeof(regs));
140 
141  /* set sensible values for stack_ptr and stack_size */
142  regs[1][0] = (unsigned int) LS_SIZE - 16; /* stack_ptr */
143  regs[2][0] = 0; /* stack_size ( 0 = default ) */
144 
145  if (runflags & SPE_RUN_USER_REGS) {
146  /* When SPE_USER_REGS is set, argp points to an array
147  * of 3x128b registers to be passed directly to the SPE
148  * program.
149  */
150  memcpy(regs[3], argp, sizeof(unsigned int) * 12);
151  } else {
152  regs[3][0] = tid64.ui[0];
153  regs[3][1] = tid64.ui[1];
154 
155  regs[4][0] = argp64.ui[0];
156  regs[4][1] = argp64.ui[1];
157 
158  regs[5][0] = envp64.ui[0];
159  regs[5][1] = envp64.ui[1];
160  }
161 
162  /* Store the LS base address in R6 */
163  ls64.ull = (uint64_t)(unsigned long)spe->base_private->mem_mmap_base;
164  regs[6][0] = ls64.ui[0];
165  regs[6][1] = ls64.ui[1];
166 
167  if (set_regs(spe, regs))
168  return -1;
169  }
170 
171  /*Leave a trail of breadcrumbs for the debugger to follow */
173  __spe_current_active_context = &this_context_info;
175  return -1;
177  } else {
178  struct spe_context_info *newinfo;
179  newinfo = &this_context_info;
180  if (!newinfo)
181  return -1;
184  }
185  /*remember the ls-addr*/
187 
188 do_run:
189  /*Remember the npc value*/
190  __spe_current_active_context->npc = tmp_entry;
191 
192  /* run SPE context */
193  run_rc = spu_run(spe->base_private->fd_spe_dir,
194  &tmp_entry, &run_status);
195 
196  /*Remember the npc value*/
197  __spe_current_active_context->npc = tmp_entry;
198  __spe_current_active_context->status = run_status;
199 
200  DEBUG_PRINTF("spu_run returned run_rc=0x%08x, entry=0x%04x, "
201  "ext_status=0x%04x.\n", run_rc, tmp_entry, run_status);
202 
203  /* set up return values and stopinfo according to spu_run exit
204  * conditions. This is overwritten on error.
205  */
206  stopinfo->spu_status = run_rc;
207 
208  if (spe->base_private->flags & SPE_ISOLATE_EMULATE) {
209  /* save the entry point, and pretend that the npc is zero */
210  spe->base_private->emulated_entry = tmp_entry;
211  *entry = 0;
212  } else {
213  *entry = tmp_entry;
214  }
215 
216  /* Return with stopinfo set on syscall error paths */
217  if (run_rc == -1) {
218  DEBUG_PRINTF("spu_run returned error %d, errno=%d\n",
219  run_rc, errno);
220  stopinfo->stop_reason = SPE_RUNTIME_FATAL;
221  stopinfo->result.spe_runtime_fatal = errno;
222  retval = -1;
223 
224  /* For isolated contexts, pass EPERM up to the
225  * caller.
226  */
227  if (!(spe->base_private->flags & SPE_ISOLATE
228  && errno == EPERM))
229  errno = EFAULT;
230 
231  } else if (run_rc & SPE_SPU_INVALID_INSTR) {
232  DEBUG_PRINTF("SPU has tried to execute an invalid "
233  "instruction. %d\n", run_rc);
234  stopinfo->stop_reason = SPE_RUNTIME_ERROR;
236  errno = EFAULT;
237  retval = -1;
238 
239  } else if ((spe->base_private->flags & SPE_EVENTS_ENABLE) && run_status) {
240  /* Report asynchronous error if return val are set and
241  * SPU events are enabled.
242  */
244  stopinfo->result.spe_runtime_exception = run_status;
245  stopinfo->spu_status = -1;
246  errno = EIO;
247  retval = -1;
248 
249  } else if (run_rc & SPE_SPU_STOPPED_BY_STOP) {
250  /* Stop & signals are broken down into three groups
251  * 1. SPE library call
252  * 2. SPE user defined stop & signal
253  * 3. SPE program end.
254  *
255  * These groups are signified by the 14-bit stop code:
256  */
257  int stopcode = (run_rc >> 16) & 0x3fff;
258 
259  /* Check if this is a library callback, and callbacks are
260  * allowed (ie, running without SPE_NO_CALLBACKS)
261  */
262  if ((stopcode & 0xff00) == SPE_PROGRAM_LIBRARY_CALL
263  && !(runflags & SPE_NO_CALLBACKS)) {
264 
265  int callback_rc, callback_number = stopcode & 0xff;
266 
267  /* execute library callback */
268  DEBUG_PRINTF("SPE library call: %d\n", callback_number);
269  callback_rc = _base_spe_handle_library_callback(spe,
270  callback_number, *entry);
271 
272  if (callback_rc) {
273  /* library callback failed; set errno and
274  * return immediately */
275  DEBUG_PRINTF("SPE library call failed: %d\n",
276  callback_rc);
277  stopinfo->stop_reason = SPE_CALLBACK_ERROR;
278  stopinfo->result.spe_callback_error =
279  callback_rc;
280  errno = EFAULT;
281  retval = -1;
282  } else {
283  /* successful library callback - restart the SPE
284  * program at the next instruction */
285  tmp_entry += 4;
286  goto do_run;
287  }
288 
289  } else if ((stopcode & 0xff00) == SPE_PROGRAM_NORMAL_END) {
290  /* The SPE program has exited by exit(X) */
291  stopinfo->stop_reason = SPE_EXIT;
292  stopinfo->result.spe_exit_code = stopcode & 0xff;
293 
294  if (spe->base_private->flags & SPE_ISOLATE) {
295  /* Issue an isolated exit, and re-run the SPE.
296  * We should see a return value without the
297  * 0x80 bit set. */
298  if (!issue_isolated_exit(spe))
299  goto do_run;
300  retval = -1;
301  }
302 
303  } else if ((stopcode & 0xfff0) == SPE_PROGRAM_ISOLATED_STOP) {
304 
305  /* 0x2206: isolated app has been loaded by loader;
306  * provide a hook for the debugger to catch this,
307  * and restart
308  */
309  if (stopcode == SPE_PROGRAM_ISO_LOAD_COMPLETE) {
311  goto do_run;
312  } else {
313  stopinfo->stop_reason = SPE_ISOLATION_ERROR;
314  stopinfo->result.spe_isolation_error =
315  stopcode & 0xf;
316  }
317 
318  } else if (spe->base_private->flags & SPE_ISOLATE &&
319  !(run_rc & 0x80)) {
320  /* We've successfully exited isolated mode */
321  retval = 0;
322 
323  } else {
324  /* User defined stop & signal, including
325  * callbacks when disabled */
326  stopinfo->stop_reason = SPE_STOP_AND_SIGNAL;
327  stopinfo->result.spe_signal_code = stopcode;
328  retval = stopcode;
329  }
330 
331  } else if (run_rc & SPE_SPU_HALT) {
332  DEBUG_PRINTF("SPU was stopped by halt. %d\n", run_rc);
333  stopinfo->stop_reason = SPE_RUNTIME_ERROR;
335  errno = EFAULT;
336  retval = -1;
337 
338  } else if (run_rc & SPE_SPU_WAITING_ON_CHANNEL) {
339  DEBUG_PRINTF("SPU is waiting on channel. %d\n", run_rc);
341  stopinfo->result.spe_runtime_exception = run_status;
342  stopinfo->spu_status = -1;
343  errno = EIO;
344  retval = -1;
345 
346  } else if (run_rc & SPE_SPU_INVALID_CHANNEL) {
347  DEBUG_PRINTF("SPU has tried to access an invalid "
348  "channel. %d\n", run_rc);
349  stopinfo->stop_reason = SPE_RUNTIME_ERROR;
351  errno = EFAULT;
352  retval = -1;
353 
354  } else {
355  DEBUG_PRINTF("spu_run returned invalid data: 0x%04x\n", run_rc);
356  stopinfo->stop_reason = SPE_RUNTIME_FATAL;
357  stopinfo->result.spe_runtime_fatal = -1;
358  stopinfo->spu_status = -1;
359  errno = EFAULT;
360  retval = -1;
361 
362  }
363 
364  freespeinfo();
365  return retval;
366 }