libspe2 0.9a
run.c
Go to the documentation of this file.
00001 /*
00002  * libspe2 - A wrapper library to adapt the JSRE SPU usage model to SPUFS
00003  * Copyright (C) 2005 IBM Corp.
00004  *
00005  * This library is free software; you can redistribute it and/or modify it
00006  * under the terms of the GNU Lesser General Public License as published by
00007  * the Free Software Foundation; either version 2.1 of the License,
00008  * or (at your option) any later version.
00009  *
00010  * This library is distributed in the hope that it will be useful, but
00011  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
00012  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
00013  * License for more details.
00014  *
00015  * You should have received a copy of the GNU Lesser General Public License
00016  * along with this library; if not, write to the Free Software Foundation,
00017  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00018  */
00019 #include <errno.h>
00020 #define GNU_SOURCE 1
00021 
00022 #include <fcntl.h>
00023 #include <stdio.h>
00024 #include <stdint.h>
00025 #include <stdlib.h>
00026 #include <string.h>
00027 #include <syscall.h>
00028 #include <unistd.h>
00029 
00030 #include <sys/types.h>
00031 #include <sys/mman.h>
00032 
00033 #include <sys/spu.h>
00034 
00035 #include "elf_loader.h"
00036 #include "lib_builtin.h"
00037 #include "spebase.h"
00038 
00039 /*Thread-local variable for use by the debugger*/
00040 __thread struct spe_context_info {
00041         int spe_id;
00042         unsigned int npc;
00043         unsigned int status;
00044         struct spe_context_info *prev;
00045 }*__spe_current_active_context;
00046 
00047 
00048 static void cleanupspeinfo(struct spe_context_info *ctxinfo)
00049 {
00050         struct spe_context_info *tmp = ctxinfo->prev;
00051         __spe_current_active_context = tmp;
00052 }
00053 
00054 static int set_regs(struct spe_context *spe, void *regs)
00055 {
00056         int fd_regs, rc;
00057 
00058         fd_regs = openat(spe->base_private->fd_spe_dir, "regs", O_RDWR);
00059         if (fd_regs < 0) {
00060                 DEBUG_PRINTF("Could not open SPE regs file.\n");
00061                 errno = EFAULT;
00062                 return -1;
00063         }
00064 
00065         rc = write(fd_regs, regs, 2048);
00066 
00067         close(fd_regs);
00068 
00069         if (rc < 0)
00070                 return -1;
00071 
00072         return 0;
00073 }
00074 
00075 static int issue_isolated_exit(struct spe_context *spe)
00076 {
00077         struct spe_spu_control_area *cntl_area =
00078                 spe->base_private->cntl_mmap_base;
00079 
00080         if (cntl_area == MAP_FAILED) {
00081                 DEBUG_PRINTF("%s: could not access SPE control area\n",
00082                                 __FUNCTION__);
00083                 return -1;
00084         }
00085 
00086         /* 0x2 is an isolated exit request */
00087         cntl_area->SPU_RunCntl = 0x2;
00088 
00089         return 0;
00090 }
00091 
00092 static inline void freespeinfo()
00093 {
00094         /*Clean up the debug variable*/
00095         struct spe_context_info *tmp = __spe_current_active_context->prev;
00096         __spe_current_active_context = tmp;
00097 }
00098 
00099 int _base_spe_context_run(spe_context_ptr_t spe, unsigned int *entry,
00100                 unsigned int runflags, void *argp, void *envp,
00101                 spe_stop_info_t *stopinfo)
00102 {
00103         int retval = 0, run_rc;
00104         unsigned int run_status, tmp_entry;
00105         spe_stop_info_t stopinfo_buf;
00106         struct spe_context_info this_context_info __attribute__((cleanup(cleanupspeinfo)));
00107 
00108         /* If the caller hasn't set a stopinfo buffer, provide a buffer on the
00109          * stack instead. */
00110         if (!stopinfo)
00111                 stopinfo = &stopinfo_buf;
00112 
00113 
00114         /* In emulated isolated mode, the npc will always return as zero.
00115          * use our private entry point instead */
00116         if (spe->base_private->flags & SPE_ISOLATE_EMULATE)
00117                 tmp_entry = spe->base_private->emulated_entry;
00118 
00119         else if (*entry == SPE_DEFAULT_ENTRY)
00120                 tmp_entry = spe->base_private->entry;
00121         else 
00122                 tmp_entry = *entry;
00123 
00124         /* If we're starting the SPE binary from its original entry point,
00125          * setup the arguments to main() */
00126         if (tmp_entry == spe->base_private->entry &&
00127                         !(spe->base_private->flags &
00128                                 (SPE_ISOLATE | SPE_ISOLATE_EMULATE))) {
00129 
00130                 addr64 argp64, envp64, tid64, ls64;
00131                 unsigned int regs[128][4];
00132 
00133                 /* setup parameters */
00134                 argp64.ull = (uint64_t)(unsigned long)argp;
00135                 envp64.ull = (uint64_t)(unsigned long)envp;
00136                 tid64.ull = (uint64_t)(unsigned long)spe;
00137 
00138                 /* make sure the register values are 0 */
00139                 memset(regs, 0, sizeof(regs));
00140 
00141                 /* set sensible values for stack_ptr and stack_size */
00142                 regs[1][0] = (unsigned int) LS_SIZE - 16;       /* stack_ptr */
00143                 regs[2][0] = 0;                                                         /* stack_size ( 0 = default ) */
00144 
00145                 if (runflags & SPE_RUN_USER_REGS) {
00146                         /* When SPE_USER_REGS is set, argp points to an array
00147                          * of 3x128b registers to be passed directly to the SPE
00148                          * program.
00149                          */
00150                         memcpy(regs[3], argp, sizeof(unsigned int) * 12);
00151                 } else {
00152                         regs[3][0] = tid64.ui[0];
00153                         regs[3][1] = tid64.ui[1];
00154 
00155                         regs[4][0] = argp64.ui[0];
00156                         regs[4][1] = argp64.ui[1];
00157 
00158                         regs[5][0] = envp64.ui[0];
00159                         regs[5][1] = envp64.ui[1];
00160                 }
00161                 
00162                 /* Store the LS base address in R6 */
00163                 ls64.ull = (uint64_t)(unsigned long)spe->base_private->mem_mmap_base;
00164                 regs[6][0] = ls64.ui[0];
00165                 regs[6][1] = ls64.ui[1];
00166 
00167                 if (set_regs(spe, regs))
00168                         return -1;
00169         }
00170 
00171         /*Leave a trail of breadcrumbs for the debugger to follow */
00172         if (!__spe_current_active_context) {
00173                 __spe_current_active_context = &this_context_info;
00174                 if (!__spe_current_active_context)
00175                         return -1;
00176                 __spe_current_active_context->prev = NULL;
00177         } else {
00178                 struct spe_context_info *newinfo;
00179                 newinfo = &this_context_info;
00180                 if (!newinfo)
00181                         return -1;
00182                 newinfo->prev = __spe_current_active_context;
00183                 __spe_current_active_context = newinfo;
00184         }
00185         /*remember the ls-addr*/
00186         __spe_current_active_context->spe_id = spe->base_private->fd_spe_dir;
00187 
00188 do_run:
00189         /*Remember the npc value*/
00190         __spe_current_active_context->npc = tmp_entry;
00191 
00192         /* run SPE context */
00193         run_rc = spu_run(spe->base_private->fd_spe_dir,
00194                         &tmp_entry, &run_status);
00195 
00196         /*Remember the npc value*/
00197         __spe_current_active_context->npc = tmp_entry;
00198         __spe_current_active_context->status = run_status;
00199 
00200         DEBUG_PRINTF("spu_run returned run_rc=0x%08x, entry=0x%04x, "
00201                         "ext_status=0x%04x.\n", run_rc, tmp_entry, run_status);
00202 
00203         /* set up return values and stopinfo according to spu_run exit
00204          * conditions. This is overwritten on error.
00205          */
00206         stopinfo->spu_status = run_rc;
00207 
00208         if (spe->base_private->flags & SPE_ISOLATE_EMULATE) {
00209                 /* save the entry point, and pretend that the npc is zero */
00210                 spe->base_private->emulated_entry = tmp_entry;
00211                 *entry = 0;
00212         } else {
00213                 *entry = tmp_entry;
00214         }
00215 
00216         /* Return with stopinfo set on syscall error paths */
00217         if (run_rc == -1) {
00218                 DEBUG_PRINTF("spu_run returned error %d, errno=%d\n",
00219                                 run_rc, errno);
00220                 stopinfo->stop_reason = SPE_RUNTIME_FATAL;
00221                 stopinfo->result.spe_runtime_fatal = errno;
00222                 retval = -1;
00223 
00224                 /* For isolated contexts, pass EPERM up to the
00225                  * caller.
00226                  */
00227                 if (!(spe->base_private->flags & SPE_ISOLATE
00228                                 && errno == EPERM))
00229                         errno = EFAULT;
00230 
00231         } else if (run_rc & SPE_SPU_INVALID_INSTR) {
00232                 DEBUG_PRINTF("SPU has tried to execute an invalid "
00233                                 "instruction. %d\n", run_rc);
00234                 stopinfo->stop_reason = SPE_RUNTIME_ERROR;
00235                 stopinfo->result.spe_runtime_error = SPE_SPU_INVALID_INSTR;
00236                 errno = EFAULT;
00237                 retval = -1;
00238 
00239         } else if ((spe->base_private->flags & SPE_EVENTS_ENABLE) && run_status) {
00240                 /* Report asynchronous error if return val are set and
00241                  * SPU events are enabled.
00242                  */
00243                 stopinfo->stop_reason = SPE_RUNTIME_EXCEPTION;
00244                 stopinfo->result.spe_runtime_exception = run_status;
00245                 stopinfo->spu_status = -1;
00246                 errno = EIO;
00247                 retval = -1;
00248 
00249         } else if (run_rc & SPE_SPU_STOPPED_BY_STOP) {
00250                 /* Stop & signals are broken down into three groups
00251                  *  1. SPE library call
00252                  *  2. SPE user defined stop & signal
00253                  *  3. SPE program end.
00254                  *
00255                  * These groups are signified by the 14-bit stop code:
00256                  */
00257                 int stopcode = (run_rc >> 16) & 0x3fff;
00258 
00259                 /* Check if this is a library callback, and callbacks are
00260                  * allowed (ie, running without SPE_NO_CALLBACKS)
00261                  */
00262                 if ((stopcode & 0xff00) == SPE_PROGRAM_LIBRARY_CALL
00263                                 && !(runflags & SPE_NO_CALLBACKS)) {
00264 
00265                         int callback_rc, callback_number = stopcode & 0xff;
00266 
00267                         /* execute library callback */
00268                         DEBUG_PRINTF("SPE library call: %d\n", callback_number);
00269                         callback_rc = _base_spe_handle_library_callback(spe,
00270                                                                         callback_number, *entry);
00271 
00272                         if (callback_rc) {
00273                                 /* library callback failed; set errno and
00274                                  * return immediately */
00275                                 DEBUG_PRINTF("SPE library call failed: %d\n",
00276                                                 callback_rc);
00277                                 stopinfo->stop_reason = SPE_CALLBACK_ERROR;
00278                                 stopinfo->result.spe_callback_error =
00279                                         callback_rc;
00280                                 errno = EFAULT;
00281                                 retval = -1;
00282                         } else {
00283                                 /* successful library callback - restart the SPE
00284                                  * program at the next instruction */
00285                                 tmp_entry += 4;
00286                                 goto do_run;
00287                         }
00288 
00289                 } else if ((stopcode & 0xff00) == SPE_PROGRAM_NORMAL_END) {
00290                         /* The SPE program has exited by exit(X) */
00291                         stopinfo->stop_reason = SPE_EXIT;
00292                         stopinfo->result.spe_exit_code = stopcode & 0xff;
00293 
00294                         if (spe->base_private->flags & SPE_ISOLATE) {
00295                                 /* Issue an isolated exit, and re-run the SPE.
00296                                  * We should see a return value without the
00297                                  * 0x80 bit set. */
00298                                 if (!issue_isolated_exit(spe))
00299                                         goto do_run;
00300                                 retval = -1;
00301                         }
00302 
00303                 } else if ((stopcode & 0xfff0) == SPE_PROGRAM_ISOLATED_STOP) {
00304 
00305                         /* 0x2206: isolated app has been loaded by loader;
00306                          * provide a hook for the debugger to catch this,
00307                          * and restart
00308                          */
00309                         if (stopcode == SPE_PROGRAM_ISO_LOAD_COMPLETE) {
00310                                 _base_spe_program_load_complete(spe);
00311                                 goto do_run;
00312                         } else {
00313                                 stopinfo->stop_reason = SPE_ISOLATION_ERROR;
00314                                 stopinfo->result.spe_isolation_error =
00315                                         stopcode & 0xf;
00316                         }
00317 
00318                 } else if (spe->base_private->flags & SPE_ISOLATE &&
00319                                 !(run_rc & 0x80)) {
00320                         /* We've successfully exited isolated mode */
00321                         retval = 0;
00322 
00323                 } else {
00324                         /* User defined stop & signal, including
00325                          * callbacks when disabled */
00326                         stopinfo->stop_reason = SPE_STOP_AND_SIGNAL;
00327                         stopinfo->result.spe_signal_code = stopcode;
00328                         retval = stopcode;
00329                 }
00330 
00331         } else if (run_rc & SPE_SPU_HALT) {
00332                 DEBUG_PRINTF("SPU was stopped by halt. %d\n", run_rc);
00333                 stopinfo->stop_reason = SPE_RUNTIME_ERROR;
00334                 stopinfo->result.spe_runtime_error = SPE_SPU_HALT;
00335                 errno = EFAULT;
00336                 retval = -1;
00337 
00338         } else if (run_rc & SPE_SPU_WAITING_ON_CHANNEL) {
00339                 DEBUG_PRINTF("SPU is waiting on channel. %d\n", run_rc);
00340                 stopinfo->stop_reason = SPE_RUNTIME_EXCEPTION;
00341                 stopinfo->result.spe_runtime_exception = run_status;
00342                 stopinfo->spu_status = -1;
00343                 errno = EIO;
00344                 retval = -1;
00345 
00346         } else if (run_rc & SPE_SPU_INVALID_CHANNEL) {
00347                 DEBUG_PRINTF("SPU has tried to access an invalid "
00348                                 "channel. %d\n", run_rc);
00349                 stopinfo->stop_reason = SPE_RUNTIME_ERROR;
00350                 stopinfo->result.spe_runtime_error = SPE_SPU_INVALID_CHANNEL;
00351                 errno = EFAULT;
00352                 retval = -1;
00353 
00354         } else {
00355                 DEBUG_PRINTF("spu_run returned invalid data: 0x%04x\n", run_rc);
00356                 stopinfo->stop_reason = SPE_RUNTIME_FATAL;
00357                 stopinfo->result.spe_runtime_fatal = -1;
00358                 stopinfo->spu_status = -1;
00359                 errno = EFAULT;
00360                 retval = -1;
00361 
00362         }
00363 
00364         freespeinfo();
00365         return retval;
00366 }