libspe2 0.9a
|
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 }