+
+static void writeData(FILE *f, const void *data, size_t len)
+{
+ size_t written = fwrite(data, len, 1, f);
+ if (written != len)
+ LOG_ERROR("failed to write %zu bytes: %s", len, strerror(errno));
+}
+
+static void writeLong(FILE *f, int l)
+{
+ int i;
+ for (i=0; i<4; i++)
+ {
+ char c=(l>>(i*8))&0xff;
+ writeData(f, &c, 1);
+ }
+
+}
+
+static void writeString(FILE *f, char *s)
+{
+ writeData(f, s, strlen(s));
+}
+
+/* Dump a gmon.out histogram file. */
+static void writeGmon(u32 *samples, u32 sampleNum, char *filename)
+{
+ u32 i;
+ FILE *f=fopen(filename, "w");
+ if (f==NULL)
+ return;
+ writeString(f, "gmon");
+ writeLong(f, 0x00000001); /* Version */
+ writeLong(f, 0); /* padding */
+ writeLong(f, 0); /* padding */
+ writeLong(f, 0); /* padding */
+
+ u8 zero = 0; /* GMON_TAG_TIME_HIST */
+ writeData(f, &zero, 1);
+
+ /* figure out bucket size */
+ u32 min=samples[0];
+ u32 max=samples[0];
+ for (i=0; i<sampleNum; i++)
+ {
+ if (min>samples[i])
+ {
+ min=samples[i];
+ }
+ if (max<samples[i])
+ {
+ max=samples[i];
+ }
+ }
+
+ int addressSpace=(max-min+1);
+
+ static const u32 maxBuckets = 256 * 1024; /* maximum buckets. */
+ u32 length = addressSpace;
+ if (length > maxBuckets)
+ {
+ length=maxBuckets;
+ }
+ int *buckets=malloc(sizeof(int)*length);
+ if (buckets==NULL)
+ {
+ fclose(f);
+ return;
+ }
+ memset(buckets, 0, sizeof(int)*length);
+ for (i=0; i<sampleNum;i++)
+ {
+ u32 address=samples[i];
+ long long a=address-min;
+ long long b=length-1;
+ long long c=addressSpace-1;
+ int index=(a*b)/c; /* danger!!!! int32 overflows */
+ buckets[index]++;
+ }
+
+ /* append binary memory gmon.out &profile_hist_hdr ((char*)&profile_hist_hdr + sizeof(struct gmon_hist_hdr)) */
+ writeLong(f, min); /* low_pc */
+ writeLong(f, max); /* high_pc */
+ writeLong(f, length); /* # of samples */
+ writeLong(f, 64000000); /* 64MHz */
+ writeString(f, "seconds");
+ for (i=0; i<(15-strlen("seconds")); i++)
+ writeData(f, &zero, 1);
+ writeString(f, "s");
+
+ /*append binary memory gmon.out profile_hist_data (profile_hist_data + profile_hist_hdr.hist_size) */
+
+ char *data=malloc(2*length);
+ if (data!=NULL)
+ {
+ for (i=0; i<length;i++)
+ {
+ int val;
+ val=buckets[i];
+ if (val>65535)
+ {
+ val=65535;
+ }
+ data[i*2]=val&0xff;
+ data[i*2+1]=(val>>8)&0xff;
+ }
+ free(buckets);
+ writeData(f, data, length * 2);
+ free(data);
+ } else
+ {
+ free(buckets);
+ }
+
+ fclose(f);
+}
+
+/* profiling samples the CPU PC as quickly as OpenOCD is able, which will be used as a random sampling of PC */
+static int handle_profile_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ target_t *target = get_current_target(cmd_ctx);
+ struct timeval timeout, now;
+
+ gettimeofday(&timeout, NULL);
+ if (argc!=2)
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ char *end;
+ timeval_add_time(&timeout, strtoul(args[0], &end, 0), 0);
+ if (*end)
+ {
+ return ERROR_OK;
+ }
+
+ command_print(cmd_ctx, "Starting profiling. Halting and resuming the target as often as we can...");
+
+ static const int maxSample=10000;
+ u32 *samples=malloc(sizeof(u32)*maxSample);
+ if (samples==NULL)
+ return ERROR_OK;
+
+ int numSamples=0;
+ int retval=ERROR_OK;
+ /* hopefully it is safe to cache! We want to stop/restart as quickly as possible. */
+ reg_t *reg = register_get_by_name(target->reg_cache, "pc", 1);
+
+ for (;;)
+ {
+ target_poll(target);
+ if (target->state == TARGET_HALTED)
+ {
+ u32 t=*((u32 *)reg->value);
+ samples[numSamples++]=t;
+ retval = target_resume(target, 1, 0, 0, 0); /* current pc, addr = 0, do not handle breakpoints, not debugging */
+ target_poll(target);
+ alive_sleep(10); /* sleep 10ms, i.e. <100 samples/second. */
+ } else if (target->state == TARGET_RUNNING)
+ {
+ /* We want to quickly sample the PC. */
+ if((retval = target_halt(target)) != ERROR_OK)
+ {
+ free(samples);
+ return retval;
+ }
+ } else
+ {
+ command_print(cmd_ctx, "Target not halted or running");
+ retval=ERROR_OK;
+ break;
+ }
+ if (retval!=ERROR_OK)
+ {
+ break;
+ }
+
+ gettimeofday(&now, NULL);
+ if ((numSamples>=maxSample) || ((now.tv_sec >= timeout.tv_sec) && (now.tv_usec >= timeout.tv_usec)))
+ {
+ command_print(cmd_ctx, "Profiling completed. %d samples.", numSamples);
+ if((retval = target_poll(target)) != ERROR_OK)
+ {
+ free(samples);
+ return retval;
+ }
+ if (target->state == TARGET_HALTED)
+ {
+ target_resume(target, 1, 0, 0, 0); /* current pc, addr = 0, do not handle breakpoints, not debugging */
+ }
+ if((retval = target_poll(target)) != ERROR_OK)
+ {
+ free(samples);
+ return retval;
+ }
+ writeGmon(samples, numSamples, args[1]);
+ command_print(cmd_ctx, "Wrote %s", args[1]);
+ break;
+ }
+ }
+ free(samples);
+
+ return ERROR_OK;
+}
+
+static int new_int_array_element(Jim_Interp * interp, const char *varname, int idx, u32 val)
+{
+ char *namebuf;
+ Jim_Obj *nameObjPtr, *valObjPtr;
+ int result;
+
+ namebuf = alloc_printf("%s(%d)", varname, idx);
+ if (!namebuf)
+ return JIM_ERR;
+
+ nameObjPtr = Jim_NewStringObj(interp, namebuf, -1);
+ valObjPtr = Jim_NewIntObj(interp, val);
+ if (!nameObjPtr || !valObjPtr)
+ {
+ free(namebuf);
+ return JIM_ERR;
+ }
+
+ Jim_IncrRefCount(nameObjPtr);
+ Jim_IncrRefCount(valObjPtr);
+ result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
+ Jim_DecrRefCount(interp, nameObjPtr);
+ Jim_DecrRefCount(interp, valObjPtr);
+ free(namebuf);
+ /* printf("%s(%d) <= 0%08x\n", varname, idx, val); */
+ return result;
+}
+
+static int jim_mem2array(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ command_context_t *context;
+ target_t *target;
+
+ context = Jim_GetAssocData(interp, "context");
+ if (context == NULL)
+ {
+ LOG_ERROR("mem2array: no command context");
+ return JIM_ERR;
+ }
+ target = get_current_target(context);
+ if (target == NULL)
+ {
+ LOG_ERROR("mem2array: no current target");
+ return JIM_ERR;
+ }
+
+ return target_mem2array(interp, target, argc,argv);
+}
+
+static int target_mem2array(Jim_Interp *interp, target_t *target, int argc, Jim_Obj *const *argv)
+{
+ long l;
+ u32 width;
+ int len;
+ u32 addr;
+ u32 count;
+ u32 v;
+ const char *varname;
+ u8 buffer[4096];
+ int n, e, retval;
+ u32 i;
+
+ /* argv[1] = name of array to receive the data
+ * argv[2] = desired width
+ * argv[3] = memory address
+ * argv[4] = count of times to read
+ */
+ if (argc != 5) {
+ Jim_WrongNumArgs(interp, 1, argv, "varname width addr nelems");
+ return JIM_ERR;
+ }
+ varname = Jim_GetString(argv[1], &len);
+ /* given "foo" get space for worse case "foo(%d)" .. add 20 */
+
+ e = Jim_GetLong(interp, argv[2], &l);
+ width = l;
+ if (e != JIM_OK) {
+ return e;
+ }
+
+ e = Jim_GetLong(interp, argv[3], &l);
+ addr = l;
+ if (e != JIM_OK) {
+ return e;
+ }
+ e = Jim_GetLong(interp, argv[4], &l);
+ len = l;
+ if (e != JIM_OK) {
+ return e;
+ }
+ switch (width) {
+ case 8:
+ width = 1;
+ break;
+ case 16:
+ width = 2;
+ break;
+ case 32:
+ width = 4;
+ break;
+ default:
+ Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+ Jim_AppendStrings( interp, Jim_GetResult(interp), "Invalid width param, must be 8/16/32", NULL );
+ return JIM_ERR;
+ }
+ if (len == 0) {
+ Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+ Jim_AppendStrings(interp, Jim_GetResult(interp), "mem2array: zero width read?", NULL);
+ return JIM_ERR;
+ }
+ if ((addr + (len * width)) < addr) {
+ Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+ Jim_AppendStrings(interp, Jim_GetResult(interp), "mem2array: addr + len - wraps to zero?", NULL);
+ return JIM_ERR;
+ }
+ /* absurd transfer size? */
+ if (len > 65536) {
+ Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+ Jim_AppendStrings(interp, Jim_GetResult(interp), "mem2array: absurd > 64K item request", NULL);
+ return JIM_ERR;
+ }
+
+ if ((width == 1) ||
+ ((width == 2) && ((addr & 1) == 0)) ||
+ ((width == 4) && ((addr & 3) == 0))) {
+ /* all is well */
+ } else {
+ char buf[100];
+ Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+ sprintf(buf, "mem2array address: 0x%08x is not aligned for %d byte reads", addr, width);
+ Jim_AppendStrings(interp, Jim_GetResult(interp), buf , NULL);
+ return JIM_ERR;
+ }
+
+ /* Transfer loop */
+
+ /* index counter */
+ n = 0;
+ /* assume ok */
+ e = JIM_OK;
+ while (len) {
+ /* Slurp... in buffer size chunks */
+
+ count = len; /* in objects.. */
+ if (count > (sizeof(buffer)/width)) {
+ count = (sizeof(buffer)/width);
+ }
+
+ retval = target->type->read_memory( target, addr, width, count, buffer );
+ if (retval != ERROR_OK) {
+ /* BOO !*/
+ LOG_ERROR("mem2array: Read @ 0x%08x, w=%d, cnt=%d, failed", addr, width, count);
+ Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+ Jim_AppendStrings(interp, Jim_GetResult(interp), "mem2array: cannot read memory", NULL);
+ e = JIM_ERR;
+ len = 0;
+ } else {
+ v = 0; /* shut up gcc */
+ for (i = 0 ;i < count ;i++, n++) {
+ switch (width) {
+ case 4:
+ v = target_buffer_get_u32(target, &buffer[i*width]);
+ break;
+ case 2:
+ v = target_buffer_get_u16(target, &buffer[i*width]);
+ break;
+ case 1:
+ v = buffer[i] & 0x0ff;
+ break;
+ }
+ new_int_array_element(interp, varname, n, v);
+ }
+ len -= count;
+ }
+ }
+
+ Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+
+ return JIM_OK;
+}
+
+static int get_int_array_element(Jim_Interp * interp, const char *varname, int idx, u32 *val)
+{
+ char *namebuf;
+ Jim_Obj *nameObjPtr, *valObjPtr;
+ int result;
+ long l;
+
+ namebuf = alloc_printf("%s(%d)", varname, idx);
+ if (!namebuf)
+ return JIM_ERR;
+
+ nameObjPtr = Jim_NewStringObj(interp, namebuf, -1);
+ if (!nameObjPtr)
+ {
+ free(namebuf);
+ return JIM_ERR;
+ }
+
+ Jim_IncrRefCount(nameObjPtr);
+ valObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_ERRMSG);
+ Jim_DecrRefCount(interp, nameObjPtr);
+ free(namebuf);
+ if (valObjPtr == NULL)
+ return JIM_ERR;
+
+ result = Jim_GetLong(interp, valObjPtr, &l);
+ /* printf("%s(%d) => 0%08x\n", varname, idx, val); */
+ *val = l;
+ return result;
+}
+
+static int jim_array2mem(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ command_context_t *context;
+ target_t *target;
+
+ context = Jim_GetAssocData(interp, "context");
+ if (context == NULL){
+ LOG_ERROR("array2mem: no command context");
+ return JIM_ERR;
+ }
+ target = get_current_target(context);
+ if (target == NULL){
+ LOG_ERROR("array2mem: no current target");
+ return JIM_ERR;
+ }
+
+ return target_array2mem( interp,target, argc, argv );
+}
+
+static int target_array2mem(Jim_Interp *interp, target_t *target, int argc, Jim_Obj *const *argv)
+{
+ long l;
+ u32 width;
+ int len;
+ u32 addr;
+ u32 count;
+ u32 v;
+ const char *varname;
+ u8 buffer[4096];
+ int n, e, retval;
+ u32 i;
+
+ /* argv[1] = name of array to get the data
+ * argv[2] = desired width
+ * argv[3] = memory address
+ * argv[4] = count to write
+ */
+ if (argc != 5) {
+ Jim_WrongNumArgs(interp, 1, argv, "varname width addr nelems");
+ return JIM_ERR;
+ }
+ varname = Jim_GetString(argv[1], &len);
+ /* given "foo" get space for worse case "foo(%d)" .. add 20 */
+
+ e = Jim_GetLong(interp, argv[2], &l);
+ width = l;
+ if (e != JIM_OK) {
+ return e;
+ }
+
+ e = Jim_GetLong(interp, argv[3], &l);
+ addr = l;
+ if (e != JIM_OK) {
+ return e;
+ }
+ e = Jim_GetLong(interp, argv[4], &l);
+ len = l;
+ if (e != JIM_OK) {
+ return e;
+ }
+ switch (width) {
+ case 8:
+ width = 1;
+ break;
+ case 16:
+ width = 2;
+ break;
+ case 32:
+ width = 4;
+ break;
+ default:
+ Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+ Jim_AppendStrings( interp, Jim_GetResult(interp), "Invalid width param, must be 8/16/32", NULL );
+ return JIM_ERR;
+ }
+ if (len == 0) {
+ Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+ Jim_AppendStrings(interp, Jim_GetResult(interp), "array2mem: zero width read?", NULL);
+ return JIM_ERR;
+ }
+ if ((addr + (len * width)) < addr) {
+ Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+ Jim_AppendStrings(interp, Jim_GetResult(interp), "array2mem: addr + len - wraps to zero?", NULL);
+ return JIM_ERR;
+ }
+ /* absurd transfer size? */
+ if (len > 65536) {
+ Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+ Jim_AppendStrings(interp, Jim_GetResult(interp), "array2mem: absurd > 64K item request", NULL);
+ return JIM_ERR;
+ }
+
+ if ((width == 1) ||
+ ((width == 2) && ((addr & 1) == 0)) ||
+ ((width == 4) && ((addr & 3) == 0))) {
+ /* all is well */
+ } else {
+ char buf[100];
+ Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+ sprintf(buf, "array2mem address: 0x%08x is not aligned for %d byte reads", addr, width);
+ Jim_AppendStrings(interp, Jim_GetResult(interp), buf , NULL);
+ return JIM_ERR;
+ }
+
+ /* Transfer loop */
+
+ /* index counter */
+ n = 0;
+ /* assume ok */
+ e = JIM_OK;
+ while (len) {
+ /* Slurp... in buffer size chunks */
+
+ count = len; /* in objects.. */
+ if (count > (sizeof(buffer)/width)) {
+ count = (sizeof(buffer)/width);
+ }
+
+ v = 0; /* shut up gcc */
+ for (i = 0 ;i < count ;i++, n++) {
+ get_int_array_element(interp, varname, n, &v);
+ switch (width) {
+ case 4:
+ target_buffer_set_u32(target, &buffer[i*width], v);
+ break;
+ case 2:
+ target_buffer_set_u16(target, &buffer[i*width], v);
+ break;
+ case 1:
+ buffer[i] = v & 0x0ff;
+ break;
+ }
+ }
+ len -= count;
+
+ retval = target->type->write_memory(target, addr, width, count, buffer);
+ if (retval != ERROR_OK) {
+ /* BOO !*/
+ LOG_ERROR("array2mem: Write @ 0x%08x, w=%d, cnt=%d, failed", addr, width, count);
+ Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+ Jim_AppendStrings(interp, Jim_GetResult(interp), "array2mem: cannot read memory", NULL);
+ e = JIM_ERR;
+ len = 0;
+ }
+ }
+
+ Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+
+ return JIM_OK;
+}
+
+void target_all_handle_event( enum target_event e )
+{
+ target_t *target;
+
+ LOG_DEBUG( "**all*targets: event: %d, %s",
+ e,
+ Jim_Nvp_value2name_simple( nvp_target_event, e )->name );
+
+ target = all_targets;
+ while (target){
+ target_handle_event( target, e );
+ target = target->next;
+ }
+}
+
+void target_handle_event( target_t *target, enum target_event e )
+{
+ target_event_action_t *teap;
+ int done;
+
+ teap = target->event_action;
+
+ done = 0;
+ while( teap ){
+ if( teap->event == e ){
+ done = 1;
+ LOG_DEBUG( "target: (%d) %s (%s) event: %d (%s) action: %s\n",
+ target->target_number,
+ target->cmd_name,
+ target->type->name,
+ e,
+ Jim_Nvp_value2name_simple( nvp_target_event, e )->name,
+ Jim_GetString( teap->body, NULL ) );
+ if (Jim_EvalObj( interp, teap->body )!=JIM_OK)
+ {
+ Jim_PrintErrorMessage(interp);
+ }
+ }
+ teap = teap->next;
+ }
+ if( !done ){
+ LOG_DEBUG( "event: %d %s - no action",
+ e,
+ Jim_Nvp_value2name_simple( nvp_target_event, e )->name );
+ }
+}
+
+enum target_cfg_param {
+ TCFG_TYPE,
+ TCFG_EVENT,
+ TCFG_WORK_AREA_VIRT,
+ TCFG_WORK_AREA_PHYS,
+ TCFG_WORK_AREA_SIZE,
+ TCFG_WORK_AREA_BACKUP,
+ TCFG_ENDIAN,
+ TCFG_VARIANT,
+ TCFG_CHAIN_POSITION,
+};
+
+static Jim_Nvp nvp_config_opts[] = {
+ { .name = "-type", .value = TCFG_TYPE },
+ { .name = "-event", .value = TCFG_EVENT },
+ { .name = "-work-area-virt", .value = TCFG_WORK_AREA_VIRT },
+ { .name = "-work-area-phys", .value = TCFG_WORK_AREA_PHYS },
+ { .name = "-work-area-size", .value = TCFG_WORK_AREA_SIZE },
+ { .name = "-work-area-backup", .value = TCFG_WORK_AREA_BACKUP },
+ { .name = "-endian" , .value = TCFG_ENDIAN },
+ { .name = "-variant", .value = TCFG_VARIANT },
+ { .name = "-chain-position", .value = TCFG_CHAIN_POSITION },
+
+ { .name = NULL, .value = -1 }
+};
+
+static int target_configure( Jim_GetOptInfo *goi, target_t *target )
+{
+ Jim_Nvp *n;
+ Jim_Obj *o;
+ jim_wide w;
+ char *cp;
+ int e;
+
+ /* parse config or cget options ... */
+ while( goi->argc > 0 ){
+ Jim_SetEmptyResult( goi->interp );
+ /* Jim_GetOpt_Debug( goi ); */
+
+ if( target->type->target_jim_configure ){
+ /* target defines a configure function */
+ /* target gets first dibs on parameters */
+ e = (*(target->type->target_jim_configure))( target, goi );
+ if( e == JIM_OK ){
+ /* more? */
+ continue;
+ }
+ if( e == JIM_ERR ){
+ /* An error */
+ return e;
+ }
+ /* otherwise we 'continue' below */
+ }
+ e = Jim_GetOpt_Nvp( goi, nvp_config_opts, &n );
+ if( e != JIM_OK ){
+ Jim_GetOpt_NvpUnknown( goi, nvp_config_opts, 0 );
+ return e;
+ }
+ switch( n->value ){
+ case TCFG_TYPE:
+ /* not setable */
+ if( goi->isconfigure ){
+ Jim_SetResult_sprintf( goi->interp, "not setable: %s", n->name );
+ return JIM_ERR;
+ } else {
+ no_params:
+ if( goi->argc != 0 ){
+ Jim_WrongNumArgs( goi->interp, goi->argc, goi->argv, "NO PARAMS");
+ return JIM_ERR;
+ }
+ }
+ Jim_SetResultString( goi->interp, target->type->name, -1 );
+ /* loop for more */
+ break;
+ case TCFG_EVENT:
+ if( goi->argc == 0 ){
+ Jim_WrongNumArgs( goi->interp, goi->argc, goi->argv, "-event ?event-name? ...");
+ return JIM_ERR;
+ }
+
+ e = Jim_GetOpt_Nvp( goi, nvp_target_event, &n );
+ if( e != JIM_OK ){
+ Jim_GetOpt_NvpUnknown( goi, nvp_target_event, 1 );
+ return e;
+ }
+
+ if( goi->isconfigure ){
+ if( goi->argc != 1 ){
+ Jim_WrongNumArgs( goi->interp, goi->argc, goi->argv, "-event ?event-name? ?EVENT-BODY?");
+ return JIM_ERR;
+ }
+ } else {
+ if( goi->argc != 0 ){
+ Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "-event ?event-name?");
+ return JIM_ERR;
+ }
+ }
+
+ {
+ target_event_action_t *teap;
+
+ teap = target->event_action;
+ /* replace existing? */
+ while( teap ){
+ if( teap->event == (enum target_event)n->value ){
+ break;
+ }
+ teap = teap->next;
+ }
+
+ if( goi->isconfigure ){
+ if( teap == NULL ){
+ /* create new */
+ teap = calloc( 1, sizeof(*teap) );
+ }
+ teap->event = n->value;
+ Jim_GetOpt_Obj( goi, &o );
+ if( teap->body ){
+ Jim_DecrRefCount( interp, teap->body );
+ }
+ teap->body = Jim_DuplicateObj( goi->interp, o );
+ /*
+ * FIXME:
+ * Tcl/TK - "tk events" have a nice feature.
+ * See the "BIND" command.
+ * We should support that here.
+ * You can specify %X and %Y in the event code.
+ * The idea is: %T - target name.
+ * The idea is: %N - target number
+ * The idea is: %E - event name.
+ */
+ Jim_IncrRefCount( teap->body );
+
+ /* add to head of event list */
+ teap->next = target->event_action;
+ target->event_action = teap;
+ Jim_SetEmptyResult(goi->interp);
+ } else {
+ /* get */
+ if( teap == NULL ){
+ Jim_SetEmptyResult( goi->interp );
+ } else {
+ Jim_SetResult( goi->interp, Jim_DuplicateObj( goi->interp, teap->body ) );
+ }
+ }
+ }
+ /* loop for more */
+ break;
+
+ case TCFG_WORK_AREA_VIRT:
+ if( goi->isconfigure ){
+ target_free_all_working_areas(target);
+ e = Jim_GetOpt_Wide( goi, &w );
+ if( e != JIM_OK ){
+ return e;
+ }
+ target->working_area_virt = w;
+ } else {
+ if( goi->argc != 0 ){
+ goto no_params;
+ }
+ }
+ Jim_SetResult( interp, Jim_NewIntObj( goi->interp, target->working_area_virt ) );
+ /* loop for more */
+ break;
+
+ case TCFG_WORK_AREA_PHYS:
+ if( goi->isconfigure ){
+ target_free_all_working_areas(target);
+ e = Jim_GetOpt_Wide( goi, &w );
+ if( e != JIM_OK ){
+ return e;
+ }
+ target->working_area_phys = w;
+ } else {
+ if( goi->argc != 0 ){
+ goto no_params;
+ }
+ }
+ Jim_SetResult( interp, Jim_NewIntObj( goi->interp, target->working_area_phys ) );
+ /* loop for more */
+ break;
+
+ case TCFG_WORK_AREA_SIZE:
+ if( goi->isconfigure ){
+ target_free_all_working_areas(target);
+ e = Jim_GetOpt_Wide( goi, &w );
+ if( e != JIM_OK ){
+ return e;
+ }
+ target->working_area_size = w;
+ } else {
+ if( goi->argc != 0 ){
+ goto no_params;
+ }
+ }
+ Jim_SetResult( interp, Jim_NewIntObj( goi->interp, target->working_area_size ) );
+ /* loop for more */
+ break;
+
+ case TCFG_WORK_AREA_BACKUP:
+ if( goi->isconfigure ){
+ target_free_all_working_areas(target);
+ e = Jim_GetOpt_Wide( goi, &w );
+ if( e != JIM_OK ){
+ return e;
+ }
+ /* make this exactly 1 or 0 */
+ target->backup_working_area = (!!w);
+ } else {
+ if( goi->argc != 0 ){
+ goto no_params;
+ }
+ }
+ Jim_SetResult( interp, Jim_NewIntObj( goi->interp, target->working_area_size ) );
+ /* loop for more e*/
+ break;
+
+ case TCFG_ENDIAN:
+ if( goi->isconfigure ){
+ e = Jim_GetOpt_Nvp( goi, nvp_target_endian, &n );
+ if( e != JIM_OK ){
+ Jim_GetOpt_NvpUnknown( goi, nvp_target_endian, 1 );
+ return e;
+ }
+ target->endianness = n->value;
+ } else {
+ if( goi->argc != 0 ){
+ goto no_params;
+ }
+ }
+ n = Jim_Nvp_value2name_simple( nvp_target_endian, target->endianness );
+ if( n->name == NULL ){
+ target->endianness = TARGET_LITTLE_ENDIAN;
+ n = Jim_Nvp_value2name_simple( nvp_target_endian, target->endianness );
+ }
+ Jim_SetResultString( goi->interp, n->name, -1 );
+ /* loop for more */
+ break;
+
+ case TCFG_VARIANT:
+ if( goi->isconfigure ){
+ if( goi->argc < 1 ){
+ Jim_SetResult_sprintf( goi->interp,
+ "%s ?STRING?",
+ n->name );
+ return JIM_ERR;
+ }
+ if( target->variant ){
+ free((void *)(target->variant));
+ }
+ e = Jim_GetOpt_String( goi, &cp, NULL );
+ target->variant = strdup(cp);
+ } else {
+ if( goi->argc != 0 ){
+ goto no_params;
+ }
+ }
+ Jim_SetResultString( goi->interp, target->variant,-1 );
+ /* loop for more */
+ break;
+ case TCFG_CHAIN_POSITION:
+ if( goi->isconfigure ){
+ Jim_Obj *o;
+ jtag_tap_t *tap;
+ target_free_all_working_areas(target);
+ e = Jim_GetOpt_Obj( goi, &o );
+ if( e != JIM_OK ){
+ return e;
+ }
+ tap = jtag_TapByJimObj( goi->interp, o );
+ if( tap == NULL ){
+ return JIM_ERR;
+ }
+ /* make this exactly 1 or 0 */
+ target->tap = tap;
+ } else {
+ if( goi->argc != 0 ){
+ goto no_params;
+ }
+ }
+ Jim_SetResultString( interp, target->tap->dotted_name, -1 );
+ /* loop for more e*/
+ break;
+ }
+ } /* while( goi->argc ) */
+
+
+ /* done - we return */
+ return JIM_OK;
+}
+
+/** this is the 'tcl' handler for the target specific command */
+static int tcl_target_func( Jim_Interp *interp, int argc, Jim_Obj *const *argv )
+{
+ Jim_GetOptInfo goi;
+ jim_wide a,b,c;
+ int x,y,z;
+ u8 target_buf[32];
+ Jim_Nvp *n;
+ target_t *target;
+ struct command_context_s *cmd_ctx;
+ int e;
+
+ enum {
+ TS_CMD_CONFIGURE,
+ TS_CMD_CGET,
+
+ TS_CMD_MWW, TS_CMD_MWH, TS_CMD_MWB,
+ TS_CMD_MDW, TS_CMD_MDH, TS_CMD_MDB,
+ TS_CMD_MRW, TS_CMD_MRH, TS_CMD_MRB,
+ TS_CMD_MEM2ARRAY, TS_CMD_ARRAY2MEM,
+ TS_CMD_EXAMINE,
+ TS_CMD_POLL,
+ TS_CMD_RESET,
+ TS_CMD_HALT,
+ TS_CMD_WAITSTATE,
+ TS_CMD_EVENTLIST,
+ TS_CMD_CURSTATE,
+ TS_CMD_INVOKE_EVENT,
+ };
+
+ static const Jim_Nvp target_options[] = {
+ { .name = "configure", .value = TS_CMD_CONFIGURE },
+ { .name = "cget", .value = TS_CMD_CGET },
+ { .name = "mww", .value = TS_CMD_MWW },
+ { .name = "mwh", .value = TS_CMD_MWH },
+ { .name = "mwb", .value = TS_CMD_MWB },
+ { .name = "mdw", .value = TS_CMD_MDW },
+ { .name = "mdh", .value = TS_CMD_MDH },
+ { .name = "mdb", .value = TS_CMD_MDB },
+ { .name = "mem2array", .value = TS_CMD_MEM2ARRAY },
+ { .name = "array2mem", .value = TS_CMD_ARRAY2MEM },
+ { .name = "eventlist", .value = TS_CMD_EVENTLIST },
+ { .name = "curstate", .value = TS_CMD_CURSTATE },
+
+ { .name = "arp_examine", .value = TS_CMD_EXAMINE },
+ { .name = "arp_poll", .value = TS_CMD_POLL },
+ { .name = "arp_reset", .value = TS_CMD_RESET },
+ { .name = "arp_halt", .value = TS_CMD_HALT },
+ { .name = "arp_waitstate", .value = TS_CMD_WAITSTATE },
+ { .name = "invoke-event", .value = TS_CMD_INVOKE_EVENT },
+
+ { .name = NULL, .value = -1 },
+ };
+
+ /* go past the "command" */
+ Jim_GetOpt_Setup( &goi, interp, argc-1, argv+1 );
+
+ target = Jim_CmdPrivData( goi.interp );
+ cmd_ctx = Jim_GetAssocData(goi.interp, "context");
+
+ /* commands here are in an NVP table */
+ e = Jim_GetOpt_Nvp( &goi, target_options, &n );
+ if( e != JIM_OK ){
+ Jim_GetOpt_NvpUnknown( &goi, target_options, 0 );
+ return e;
+ }
+ /* Assume blank result */
+ Jim_SetEmptyResult( goi.interp );
+
+ switch( n->value ){
+ case TS_CMD_CONFIGURE:
+ if( goi.argc < 2 ){
+ Jim_WrongNumArgs( goi.interp, goi.argc, goi.argv, "missing: -option VALUE ...");
+ return JIM_ERR;
+ }
+ goi.isconfigure = 1;
+ return target_configure( &goi, target );
+ case TS_CMD_CGET:
+ // some things take params
+ if( goi.argc < 1 ){
+ Jim_WrongNumArgs( goi.interp, 0, goi.argv, "missing: ?-option?");
+ return JIM_ERR;
+ }
+ goi.isconfigure = 0;
+ return target_configure( &goi, target );
+ break;
+ case TS_CMD_MWW:
+ case TS_CMD_MWH:
+ case TS_CMD_MWB:
+ /* argv[0] = cmd
+ * argv[1] = address
+ * argv[2] = data
+ * argv[3] = optional count.
+ */
+
+ if( (goi.argc == 3) || (goi.argc == 4) ){
+ /* all is well */
+ } else {
+ mwx_error:
+ Jim_SetResult_sprintf( goi.interp, "expected: %s ADDR DATA [COUNT]", n->name );
+ return JIM_ERR;
+ }
+
+ e = Jim_GetOpt_Wide( &goi, &a );
+ if( e != JIM_OK ){
+ goto mwx_error;
+ }
+
+ e = Jim_GetOpt_Wide( &goi, &b );
+ if( e != JIM_OK ){
+ goto mwx_error;
+ }
+ if( goi.argc ){
+ e = Jim_GetOpt_Wide( &goi, &c );
+ if( e != JIM_OK ){
+ goto mwx_error;
+ }
+ } else {
+ c = 1;
+ }
+
+ switch( n->value ){
+ case TS_CMD_MWW:
+ target_buffer_set_u32( target, target_buf, b );
+ b = 4;
+ break;
+ case TS_CMD_MWH:
+ target_buffer_set_u16( target, target_buf, b );
+ b = 2;
+ break;
+ case TS_CMD_MWB:
+ target_buffer_set_u8( target, target_buf, b );
+ b = 1;
+ break;
+ }
+ for( x = 0 ; x < c ; x++ ){
+ e = target->type->write_memory( target, a, b, 1, target_buf );
+ if( e != ERROR_OK ){
+ Jim_SetResult_sprintf( interp, "Error writing @ 0x%08x: %d\n", (int)(a), e );
+ return JIM_ERR;
+ }
+ /* b = width */
+ a = a + b;
+ }
+ return JIM_OK;
+ break;
+
+ /* display */
+ case TS_CMD_MDW:
+ case TS_CMD_MDH:
+ case TS_CMD_MDB:
+ /* argv[0] = command
+ * argv[1] = address
+ * argv[2] = optional count
+ */
+ if( (goi.argc == 2) || (goi.argc == 3) ){
+ Jim_SetResult_sprintf( goi.interp, "expected: %s ADDR [COUNT]", n->name );
+ return JIM_ERR;
+ }
+ e = Jim_GetOpt_Wide( &goi, &a );
+ if( e != JIM_OK ){
+ return JIM_ERR;
+ }
+ if( goi.argc ){
+ e = Jim_GetOpt_Wide( &goi, &c );
+ if( e != JIM_OK ){
+ return JIM_ERR;
+ }
+ } else {
+ c = 1;
+ }
+ b = 1; /* shut up gcc */
+ switch( n->value ){
+ case TS_CMD_MDW:
+ b = 4;
+ break;
+ case TS_CMD_MDH:
+ b = 2;
+ break;
+ case TS_CMD_MDB:
+ b = 1;
+ break;
+ }
+
+ /* convert to "bytes" */
+ c = c * b;
+ /* count is now in 'BYTES' */
+ while( c > 0 ){
+ y = c;
+ if( y > 16 ){
+ y = 16;
+ }
+ e = target->type->read_memory( target, a, b, y / b, target_buf );
+ if( e != ERROR_OK ){
+ Jim_SetResult_sprintf( interp, "error reading target @ 0x%08lx", (int)(a) );
+ return JIM_ERR;
+ }
+
+ Jim_fprintf( interp, interp->cookie_stdout, "0x%08x ", (int)(a) );
+ switch( b ){
+ case 4:
+ for( x = 0 ; (x < 16) && (x < y) ; x += 4 ){
+ z = target_buffer_get_u32( target, &(target_buf[ x * 4 ]) );
+ Jim_fprintf( interp, interp->cookie_stdout, "%08x ", (int)(z) );
+ }
+ for( ; (x < 16) ; x += 4 ){
+ Jim_fprintf( interp, interp->cookie_stdout, " " );
+ }
+ break;
+ case 2:
+ for( x = 0 ; (x < 16) && (x < y) ; x += 2 ){
+ z = target_buffer_get_u16( target, &(target_buf[ x * 2 ]) );
+ Jim_fprintf( interp, interp->cookie_stdout, "%04x ", (int)(z) );
+ }
+ for( ; (x < 16) ; x += 2 ){
+ Jim_fprintf( interp, interp->cookie_stdout, " " );
+ }
+ break;
+ case 1:
+ default:
+ for( x = 0 ; (x < 16) && (x < y) ; x += 1 ){
+ z = target_buffer_get_u8( target, &(target_buf[ x * 4 ]) );
+ Jim_fprintf( interp, interp->cookie_stdout, "%02x ", (int)(z) );
+ }
+ for( ; (x < 16) ; x += 1 ){
+ Jim_fprintf( interp, interp->cookie_stdout, " " );
+ }
+ break;
+ }
+ /* ascii-ify the bytes */
+ for( x = 0 ; x < y ; x++ ){
+ if( (target_buf[x] >= 0x20) &&
+ (target_buf[x] <= 0x7e) ){
+ /* good */
+ } else {
+ /* smack it */
+ target_buf[x] = '.';
+ }
+ }
+ /* space pad */
+ while( x < 16 ){
+ target_buf[x] = ' ';
+ x++;
+ }
+ /* terminate */
+ target_buf[16] = 0;
+ /* print - with a newline */
+ Jim_fprintf( interp, interp->cookie_stdout, "%s\n", target_buf );
+ /* NEXT... */
+ c -= 16;
+ a += 16;
+ }
+ return JIM_OK;
+ case TS_CMD_MEM2ARRAY:
+ return target_mem2array( goi.interp, target, goi.argc, goi.argv );
+ break;
+ case TS_CMD_ARRAY2MEM:
+ return target_array2mem( goi.interp, target, goi.argc, goi.argv );
+ break;
+ case TS_CMD_EXAMINE:
+ if( goi.argc ){
+ Jim_WrongNumArgs( goi.interp, 2, argv, "[no parameters]");
+ return JIM_ERR;
+ }
+ e = target->type->examine( target );
+ if( e != ERROR_OK ){
+ Jim_SetResult_sprintf( interp, "examine-fails: %d", e );
+ return JIM_ERR;
+ }
+ return JIM_OK;
+ case TS_CMD_POLL:
+ if( goi.argc ){
+ Jim_WrongNumArgs( goi.interp, 2, argv, "[no parameters]");
+ return JIM_ERR;
+ }
+ if( !(target->type->examined) ){
+ e = ERROR_TARGET_NOT_EXAMINED;
+ } else {
+ e = target->type->poll( target );
+ }
+ if( e != ERROR_OK ){
+ Jim_SetResult_sprintf( interp, "poll-fails: %d", e );
+ return JIM_ERR;
+ } else {
+ return JIM_OK;
+ }
+ break;
+ case TS_CMD_RESET:
+ if( goi.argc != 2 ){
+ Jim_WrongNumArgs( interp, 2, argv, "t|f|assert|deassert BOOL");
+ return JIM_ERR;
+ }
+ e = Jim_GetOpt_Nvp( &goi, nvp_assert, &n );
+ if( e != JIM_OK ){
+ Jim_GetOpt_NvpUnknown( &goi, nvp_assert, 1 );
+ return e;
+ }
+ /* the halt or not param */
+ e = Jim_GetOpt_Wide( &goi, &a);
+ if( e != JIM_OK ){
+ return e;
+ }
+ /* determine if we should halt or not. */
+ target->reset_halt = !!a;
+ /* When this happens - all workareas are invalid. */
+ target_free_all_working_areas_restore(target, 0);
+
+ /* do the assert */
+ if( n->value == NVP_ASSERT ){
+ target->type->assert_reset( target );
+ } else {
+ target->type->deassert_reset( target );
+ }
+ return JIM_OK;
+ case TS_CMD_HALT:
+ if( goi.argc ){
+ Jim_WrongNumArgs( goi.interp, 0, argv, "halt [no parameters]");
+ return JIM_ERR;
+ }
+ target->type->halt( target );
+ return JIM_OK;
+ case TS_CMD_WAITSTATE:
+ /* params: <name> statename timeoutmsecs */
+ if( goi.argc != 2 ){
+ Jim_SetResult_sprintf( goi.interp, "%s STATENAME TIMEOUTMSECS", n->name );
+ return JIM_ERR;
+ }
+ e = Jim_GetOpt_Nvp( &goi, nvp_target_state, &n );
+ if( e != JIM_OK ){
+ Jim_GetOpt_NvpUnknown( &goi, nvp_target_state,1 );
+ return e;
+ }
+ e = Jim_GetOpt_Wide( &goi, &a );
+ if( e != JIM_OK ){
+ return e;
+ }
+ e = target_wait_state( target, n->value, a );
+ if( e != ERROR_OK ){
+ Jim_SetResult_sprintf( goi.interp,
+ "target: %s wait %s fails (%d) %s",
+ target->cmd_name,
+ n->name,
+ e, target_strerror_safe(e) );
+ return JIM_ERR;
+ } else {
+ return JIM_OK;
+ }
+ case TS_CMD_EVENTLIST:
+ /* List for human, Events defined for this target.
+ * scripts/programs should use 'name cget -event NAME'
+ */
+ {
+ target_event_action_t *teap;
+ teap = target->event_action;
+ command_print( cmd_ctx, "Event actions for target (%d) %s\n",
+ target->target_number,
+ target->cmd_name );
+ command_print( cmd_ctx, "%-25s | Body", "Event");
+ command_print( cmd_ctx, "------------------------- | ----------------------------------------");
+ while( teap ){
+ command_print( cmd_ctx,
+ "%-25s | %s",
+ Jim_Nvp_value2name_simple( nvp_target_event, teap->event )->name,
+ Jim_GetString( teap->body, NULL ) );
+ teap = teap->next;
+ }
+ command_print( cmd_ctx, "***END***");
+ return JIM_OK;
+ }
+ case TS_CMD_CURSTATE:
+ if( goi.argc != 0 ){
+ Jim_WrongNumArgs( goi.interp, 0, argv, "[no parameters]");
+ return JIM_ERR;
+ }
+ Jim_SetResultString( goi.interp,
+ Jim_Nvp_value2name_simple(nvp_target_state,target->state)->name,-1);
+ return JIM_OK;
+ case TS_CMD_INVOKE_EVENT:
+ if( goi.argc != 1 ){
+ Jim_SetResult_sprintf( goi.interp, "%s ?EVENTNAME?",n->name);
+ return JIM_ERR;
+ }
+ e = Jim_GetOpt_Nvp( &goi, nvp_target_event, &n );
+ if( e != JIM_OK ){
+ Jim_GetOpt_NvpUnknown( &goi, nvp_target_event, 1 );
+ return e;
+ }
+ target_handle_event( target, n->value );
+ return JIM_OK;
+ }
+ return JIM_ERR;
+}
+
+static int target_create( Jim_GetOptInfo *goi )
+{
+ Jim_Obj *new_cmd;
+ Jim_Cmd *cmd;
+ const char *cp;
+ char *cp2;
+ int e;
+ int x;
+ target_t *target;
+ struct command_context_s *cmd_ctx;
+
+ cmd_ctx = Jim_GetAssocData(goi->interp, "context");
+ if( goi->argc < 3 ){
+ Jim_WrongNumArgs( goi->interp, 1, goi->argv, "?name? ?type? ..options...");
+ return JIM_ERR;
+ }
+
+ /* COMMAND */
+ Jim_GetOpt_Obj( goi, &new_cmd );
+ /* does this command exist? */
+ cmd = Jim_GetCommand( goi->interp, new_cmd, JIM_ERRMSG );
+ if( cmd ){
+ cp = Jim_GetString( new_cmd, NULL );
+ Jim_SetResult_sprintf(goi->interp, "Command/target: %s Exists", cp);
+ return JIM_ERR;
+ }
+
+ /* TYPE */
+ e = Jim_GetOpt_String( goi, &cp2, NULL );
+ cp = cp2;
+ /* now does target type exist */
+ for( x = 0 ; target_types[x] ; x++ ){
+ if( 0 == strcmp( cp, target_types[x]->name ) ){
+ /* found */
+ break;
+ }
+ }
+ if( target_types[x] == NULL ){
+ Jim_SetResult_sprintf( goi->interp, "Unknown target type %s, try one of ", cp );
+ for( x = 0 ; target_types[x] ; x++ ){
+ if( target_types[x+1] ){
+ Jim_AppendStrings( goi->interp,
+ Jim_GetResult(goi->interp),
+ target_types[x]->name,
+ ", ", NULL);
+ } else {
+ Jim_AppendStrings( goi->interp,
+ Jim_GetResult(goi->interp),
+ " or ",
+ target_types[x]->name,NULL );
+ }
+ }
+ return JIM_ERR;
+ }
+
+ /* Create it */
+ target = calloc(1,sizeof(target_t));
+ /* set target number */
+ target->target_number = new_target_number();
+
+ /* allocate memory for each unique target type */
+ target->type = (target_type_t*)calloc(1,sizeof(target_type_t));
+
+ memcpy( target->type, target_types[x], sizeof(target_type_t));
+
+ /* will be set by "-endian" */
+ target->endianness = TARGET_ENDIAN_UNKNOWN;
+
+ target->working_area = 0x0;
+ target->working_area_size = 0x0;
+ target->working_areas = NULL;
+ target->backup_working_area = 0;
+
+ target->state = TARGET_UNKNOWN;
+ target->debug_reason = DBG_REASON_UNDEFINED;
+ target->reg_cache = NULL;
+ target->breakpoints = NULL;
+ target->watchpoints = NULL;
+ target->next = NULL;
+ target->arch_info = NULL;
+
+ target->display = 1;
+
+ /* initialize trace information */
+ target->trace_info = malloc(sizeof(trace_t));
+ target->trace_info->num_trace_points = 0;
+ target->trace_info->trace_points_size = 0;
+ target->trace_info->trace_points = NULL;
+ target->trace_info->trace_history_size = 0;
+ target->trace_info->trace_history = NULL;
+ target->trace_info->trace_history_pos = 0;
+ target->trace_info->trace_history_overflowed = 0;
+
+ target->dbgmsg = NULL;
+ target->dbg_msg_enabled = 0;
+
+ target->endianness = TARGET_ENDIAN_UNKNOWN;
+
+ /* Do the rest as "configure" options */
+ goi->isconfigure = 1;
+ e = target_configure( goi, target);
+
+ if (target->tap == NULL)
+ {
+ Jim_SetResultString( interp, "-chain-position required when creating target", -1);
+ e=JIM_ERR;
+ }
+
+ if( e != JIM_OK ){
+ free( target->type );
+ free( target );
+ return e;
+ }
+
+ if( target->endianness == TARGET_ENDIAN_UNKNOWN ){
+ /* default endian to little if not specified */
+ target->endianness = TARGET_LITTLE_ENDIAN;
+ }
+
+ /* incase variant is not set */
+ if (!target->variant)
+ target->variant = strdup("");
+
+ /* create the target specific commands */
+ if( target->type->register_commands ){
+ (*(target->type->register_commands))( cmd_ctx );
+ }
+ if( target->type->target_create ){
+ (*(target->type->target_create))( target, goi->interp );
+ }
+
+ /* append to end of list */
+ {
+ target_t **tpp;
+ tpp = &(all_targets);
+ while( *tpp ){
+ tpp = &( (*tpp)->next );
+ }
+ *tpp = target;
+ }
+
+ cp = Jim_GetString( new_cmd, NULL );
+ target->cmd_name = strdup(cp);
+
+ /* now - create the new target name command */
+ e = Jim_CreateCommand( goi->interp,
+ /* name */
+ cp,
+ tcl_target_func, /* C function */
+ target, /* private data */
+ NULL ); /* no del proc */
+
+ return e;
+}
+
+static int jim_target( Jim_Interp *interp, int argc, Jim_Obj *const *argv )
+{
+ int x,r,e;
+ jim_wide w;
+ struct command_context_s *cmd_ctx;
+ target_t *target;
+ Jim_GetOptInfo goi;
+ enum tcmd {
+ /* TG = target generic */
+ TG_CMD_CREATE,
+ TG_CMD_TYPES,
+ TG_CMD_NAMES,
+ TG_CMD_CURRENT,
+ TG_CMD_NUMBER,
+ TG_CMD_COUNT,
+ };
+ const char *target_cmds[] = {
+ "create", "types", "names", "current", "number",
+ "count",
+ NULL /* terminate */
+ };
+
+ LOG_DEBUG("Target command params:");
+ LOG_DEBUG("%s", Jim_Debug_ArgvString(interp, argc, argv));
+
+ cmd_ctx = Jim_GetAssocData( interp, "context" );
+
+ Jim_GetOpt_Setup( &goi, interp, argc-1, argv+1 );
+
+ if( goi.argc == 0 ){
+ Jim_WrongNumArgs(interp, 1, argv, "missing: command ...");
+ return JIM_ERR;
+ }
+
+ /* Jim_GetOpt_Debug( &goi ); */
+ r = Jim_GetOpt_Enum( &goi, target_cmds, &x );
+ if( r != JIM_OK ){
+ return r;
+ }
+
+ switch(x){
+ default:
+ Jim_Panic(goi.interp,"Why am I here?");
+ return JIM_ERR;
+ case TG_CMD_CURRENT:
+ if( goi.argc != 0 ){
+ Jim_WrongNumArgs( goi.interp, 1, goi.argv, "Too many parameters");
+ return JIM_ERR;
+ }
+ Jim_SetResultString( goi.interp, get_current_target( cmd_ctx )->cmd_name, -1 );
+ return JIM_OK;
+ case TG_CMD_TYPES:
+ if( goi.argc != 0 ){
+ Jim_WrongNumArgs( goi.interp, 1, goi.argv, "Too many parameters" );
+ return JIM_ERR;
+ }
+ Jim_SetResult( goi.interp, Jim_NewListObj( goi.interp, NULL, 0 ) );
+ for( x = 0 ; target_types[x] ; x++ ){
+ Jim_ListAppendElement( goi.interp,
+ Jim_GetResult(goi.interp),
+ Jim_NewStringObj( goi.interp, target_types[x]->name, -1 ) );
+ }
+ return JIM_OK;
+ case TG_CMD_NAMES:
+ if( goi.argc != 0 ){
+ Jim_WrongNumArgs( goi.interp, 1, goi.argv, "Too many parameters" );
+ return JIM_ERR;
+ }
+ Jim_SetResult( goi.interp, Jim_NewListObj( goi.interp, NULL, 0 ) );
+ target = all_targets;
+ while( target ){
+ Jim_ListAppendElement( goi.interp,
+ Jim_GetResult(goi.interp),
+ Jim_NewStringObj( goi.interp, target->cmd_name, -1 ) );
+ target = target->next;
+ }
+ return JIM_OK;
+ case TG_CMD_CREATE:
+ if( goi.argc < 3 ){
+ Jim_WrongNumArgs( goi.interp, goi.argc, goi.argv, "?name ... config options ...");
+ return JIM_ERR;
+ }
+ return target_create( &goi );
+ break;
+ case TG_CMD_NUMBER:
+ if( goi.argc != 1 ){
+ Jim_SetResult_sprintf( goi.interp, "expected: target number ?NUMBER?");
+ return JIM_ERR;
+ }
+ e = Jim_GetOpt_Wide( &goi, &w );
+ if( e != JIM_OK ){
+ return JIM_ERR;
+ }
+ {
+ target_t *t;
+ t = get_target_by_num(w);
+ if( t == NULL ){
+ Jim_SetResult_sprintf( goi.interp,"Target: number %d does not exist", (int)(w));
+ return JIM_ERR;
+ }
+ Jim_SetResultString( goi.interp, t->cmd_name, -1 );
+ return JIM_OK;
+ }
+ case TG_CMD_COUNT:
+ if( goi.argc != 0 ){
+ Jim_WrongNumArgs( goi.interp, 0, goi.argv, "<no parameters>");
+ return JIM_ERR;
+ }
+ Jim_SetResult( goi.interp,
+ Jim_NewIntObj( goi.interp, max_target_number()));
+ return JIM_OK;
+ }
+
+ return JIM_ERR;
+}
+
+
+struct FastLoad
+{
+ u32 address;
+ u8 *data;
+ int length;
+
+};
+
+static int fastload_num;
+static struct FastLoad *fastload;
+
+static void free_fastload(void)
+{
+ if (fastload!=NULL)
+ {
+ int i;
+ for (i=0; i<fastload_num; i++)
+ {
+ if (fastload[i].data)
+ free(fastload[i].data);
+ }
+ free(fastload);
+ fastload=NULL;
+ }
+}
+
+
+
+
+static int handle_fast_load_image_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ u8 *buffer;
+ u32 buf_cnt;
+ u32 image_size;
+ u32 min_address=0;
+ u32 max_address=0xffffffff;
+ int i;
+ int retval;
+
+ image_t image;
+
+ duration_t duration;
+ char *duration_text;
+
+ if ((argc < 1)||(argc > 5))
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ /* a base address isn't always necessary, default to 0x0 (i.e. don't relocate) */
+ if (argc >= 2)
+ {
+ image.base_address_set = 1;
+ image.base_address = strtoul(args[1], NULL, 0);
+ }
+ else
+ {
+ image.base_address_set = 0;
+ }
+
+
+ image.start_address_set = 0;
+
+ if (argc>=4)
+ {
+ min_address=strtoul(args[3], NULL, 0);
+ }
+ if (argc>=5)
+ {
+ max_address=strtoul(args[4], NULL, 0)+min_address;
+ }
+
+ if (min_address>max_address)
+ {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ duration_start_measure(&duration);
+
+ if (image_open(&image, args[0], (argc >= 3) ? args[2] : NULL) != ERROR_OK)
+ {
+ return ERROR_OK;
+ }
+
+ image_size = 0x0;
+ retval = ERROR_OK;
+ fastload_num=image.num_sections;
+ fastload=(struct FastLoad *)malloc(sizeof(struct FastLoad)*image.num_sections);
+ if (fastload==NULL)
+ {
+ image_close(&image);
+ return ERROR_FAIL;
+ }
+ memset(fastload, 0, sizeof(struct FastLoad)*image.num_sections);
+ for (i = 0; i < image.num_sections; i++)
+ {
+ buffer = malloc(image.sections[i].size);
+ if (buffer == NULL)
+ {
+ command_print(cmd_ctx, "error allocating buffer for section (%d bytes)", image.sections[i].size);
+ break;
+ }
+
+ if ((retval = image_read_section(&image, i, 0x0, image.sections[i].size, buffer, &buf_cnt)) != ERROR_OK)
+ {
+ free(buffer);
+ break;
+ }
+
+ u32 offset=0;
+ u32 length=buf_cnt;
+
+
+ /* DANGER!!! beware of unsigned comparision here!!! */
+
+ if ((image.sections[i].base_address+buf_cnt>=min_address)&&
+ (image.sections[i].base_address<max_address))
+ {
+ if (image.sections[i].base_address<min_address)
+ {
+ /* clip addresses below */
+ offset+=min_address-image.sections[i].base_address;
+ length-=offset;
+ }
+
+ if (image.sections[i].base_address+buf_cnt>max_address)
+ {
+ length-=(image.sections[i].base_address+buf_cnt)-max_address;
+ }
+
+ fastload[i].address=image.sections[i].base_address+offset;
+ fastload[i].data=malloc(length);
+ if (fastload[i].data==NULL)
+ {
+ free(buffer);
+ break;
+ }
+ memcpy(fastload[i].data, buffer+offset, length);
+ fastload[i].length=length;
+
+ image_size += length;
+ command_print(cmd_ctx, "%u byte written at address 0x%8.8x", length, image.sections[i].base_address+offset);
+ }
+
+ free(buffer);
+ }
+
+ duration_stop_measure(&duration, &duration_text);
+ if (retval==ERROR_OK)
+ {
+ command_print(cmd_ctx, "Loaded %u bytes in %s", image_size, duration_text);
+ command_print(cmd_ctx, "NB!!! image has not been loaded to target, issue a subsequent 'fast_load' to do so.");
+ }
+ free(duration_text);
+
+ image_close(&image);
+
+ if (retval!=ERROR_OK)
+ {
+ free_fastload();
+ }
+
+ return retval;
+}
+
+static int handle_fast_load_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+ if (argc>0)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ if (fastload==NULL)
+ {
+ LOG_ERROR("No image in memory");
+ return ERROR_FAIL;
+ }
+ int i;
+ int ms=timeval_ms();
+ int size=0;
+ int retval=ERROR_OK;
+ for (i=0; i<fastload_num;i++)
+ {
+ target_t *target = get_current_target(cmd_ctx);
+ command_print(cmd_ctx, "Write to 0x%08x, length 0x%08x", fastload[i].address, fastload[i].length);
+ if (retval==ERROR_OK)
+ {
+ retval = target_write_buffer(target, fastload[i].address, fastload[i].length, fastload[i].data);
+ }
+ size+=fastload[i].length;
+ }
+ int after=timeval_ms();
+ command_print(cmd_ctx, "Loaded image %f kBytes/s", (float)(size/1024.0)/((float)(after-ms)/1000.0));
+ return retval;
+}