target.c 127 KB
Newer Older
oharboe's avatar
oharboe committed
3001

3002
}
3003

3004
3005
static void writeString(FILE *f, char *s)
{
3006
	writeData(f, s, strlen(s));
3007
3008
}

3009
/* Dump a gmon.out histogram file. */
3010
static void writeGmon(uint32_t *samples, uint32_t sampleNum, const char *filename)
3011
{
3012
	uint32_t i;
zwelch's avatar
zwelch committed
3013
	FILE *f = fopen(filename, "w");
zwelch's avatar
zwelch committed
3014
	if (f == NULL)
3015
		return;
3016
	writeString(f, "gmon");
3017
3018
3019
3020
	writeLong(f, 0x00000001); /* Version */
	writeLong(f, 0); /* padding */
	writeLong(f, 0); /* padding */
	writeLong(f, 0); /* padding */
oharboe's avatar
oharboe committed
3021

3022
	uint8_t zero = 0;  /* GMON_TAG_TIME_HIST */
3023
	writeData(f, &zero, 1);
3024

3025
	/* figure out bucket size */
zwelch's avatar
zwelch committed
3026
3027
	uint32_t min = samples[0];
	uint32_t max = samples[0];
zwelch's avatar
zwelch committed
3028
	for (i = 0; i < sampleNum; i++)
3029
	{
zwelch's avatar
zwelch committed
3030
		if (min > samples[i])
3031
		{
zwelch's avatar
zwelch committed
3032
			min = samples[i];
3033
		}
zwelch's avatar
zwelch committed
3034
		if (max < samples[i])
3035
		{
zwelch's avatar
zwelch committed
3036
			max = samples[i];
3037
3038
3039
		}
	}

zwelch's avatar
zwelch committed
3040
	int addressSpace = (max-min + 1);
oharboe's avatar
oharboe committed
3041

3042
3043
	static const uint32_t maxBuckets = 256 * 1024; /* maximum buckets. */
	uint32_t length = addressSpace;
3044
3045
	if (length > maxBuckets)
	{
zwelch's avatar
zwelch committed
3046
		length = maxBuckets;
3047
	}
zwelch's avatar
zwelch committed
3048
	int *buckets = malloc(sizeof(int)*length);
zwelch's avatar
zwelch committed
3049
	if (buckets == NULL)
3050
3051
3052
3053
3054
	{
		fclose(f);
		return;
	}
	memset(buckets, 0, sizeof(int)*length);
zwelch's avatar
zwelch committed
3055
	for (i = 0; i < sampleNum;i++)
3056
	{
zwelch's avatar
zwelch committed
3057
3058
3059
3060
3061
		uint32_t 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 */
3062
3063
		buckets[index]++;
	}
oharboe's avatar
oharboe committed
3064

3065
3066
3067
3068
3069
	/* 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 */
3070
	writeString(f, "seconds");
zwelch's avatar
zwelch committed
3071
	for (i = 0; i < (15-strlen("seconds")); i++)
3072
		writeData(f, &zero, 1);
3073
	writeString(f, "s");
oharboe's avatar
oharboe committed
3074

3075
	/*append binary memory gmon.out profile_hist_data (profile_hist_data + profile_hist_hdr.hist_size) */
oharboe's avatar
oharboe committed
3076

zwelch's avatar
zwelch committed
3077
	char *data = malloc(2*length);
zwelch's avatar
zwelch committed
3078
	if (data != NULL)
3079
	{
zwelch's avatar
zwelch committed
3080
		for (i = 0; i < length;i++)
3081
3082
		{
			int val;
zwelch's avatar
zwelch committed
3083
			val = buckets[i];
zwelch's avatar
zwelch committed
3084
			if (val > 65535)
3085
			{
zwelch's avatar
zwelch committed
3086
				val = 65535;
3087
3088
			}
			data[i*2]=val&0xff;
zwelch's avatar
zwelch committed
3089
			data[i*2 + 1]=(val >> 8)&0xff;
3090
3091
		}
		free(buckets);
3092
		writeData(f, data, length * 2);
3093
3094
3095
3096
3097
3098
3099
3100
3101
		free(data);
	} else
	{
		free(buckets);
	}

	fclose(f);
}

3102
3103
/* profiling samples the CPU PC as quickly as OpenOCD is able,
 * which will be used as a random sampling of PC */
3104
COMMAND_HANDLER(handle_profile_command)
3105
{
3106
	struct target *target = get_current_target(CMD_CTX);
3107
	struct timeval timeout, now;
oharboe's avatar
oharboe committed
3108

3109
	gettimeofday(&timeout, NULL);
3110
	if (CMD_ARGC != 2)
3111
3112
3113
	{
		return ERROR_COMMAND_SYNTAX_ERROR;
	}
3114
	unsigned offset;
3115
	COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], offset);
3116
3117

	timeval_add_time(&timeout, offset, 0);
oharboe's avatar
oharboe committed
3118

3119
3120
3121
3122
3123
3124
	/**
	 * @todo: Some cores let us sample the PC without the
	 * annoying halt/resume step; for example, ARMv7 PCSR.
	 * Provide a way to use that more efficient mechanism.
	 */

3125
	command_print(CMD_CTX, "Starting profiling. Halting and resuming the target as often as we can...");
3126

zwelch's avatar
zwelch committed
3127
3128
	static const int maxSample = 10000;
	uint32_t *samples = malloc(sizeof(uint32_t)*maxSample);
zwelch's avatar
zwelch committed
3129
	if (samples == NULL)
3130
		return ERROR_OK;
oharboe's avatar
oharboe committed
3131

zwelch's avatar
zwelch committed
3132
	int numSamples = 0;
3133
	/* hopefully it is safe to cache! We want to stop/restart as quickly as possible. */
Zachary T Welch's avatar
Zachary T Welch committed
3134
	struct reg *reg = register_get_by_name(target->reg_cache, "pc", 1);
oharboe's avatar
oharboe committed
3135

3136
3137
	for (;;)
	{
3138
		int retval;
3139
		target_poll(target);
3140
3141
		if (target->state == TARGET_HALTED)
		{
3142
			uint32_t t=*((uint32_t *)reg->value);
3143
			samples[numSamples++]=t;
3144
3145
			retval = target_resume(target, 1, 0, 0, 0); /* current pc, addr = 0, do not handle breakpoints, not debugging */
			target_poll(target);
3146
			alive_sleep(10); /* sleep 10ms, i.e. <100 samples/second. */
3147
3148
		} else if (target->state == TARGET_RUNNING)
		{
3149
			/* We want to quickly sample the PC. */
zwelch's avatar
zwelch committed
3150
			if ((retval = target_halt(target)) != ERROR_OK)
3151
3152
3153
3154
			{
				free(samples);
				return retval;
			}
3155
3156
		} else
		{
3157
			command_print(CMD_CTX, "Target not halted or running");
zwelch's avatar
zwelch committed
3158
			retval = ERROR_OK;
3159
3160
			break;
		}
zwelch's avatar
zwelch committed
3161
		if (retval != ERROR_OK)
3162
3163
3164
		{
			break;
		}
oharboe's avatar
oharboe committed
3165

3166
		gettimeofday(&now, NULL);
zwelch's avatar
zwelch committed
3167
		if ((numSamples >= maxSample) || ((now.tv_sec >= timeout.tv_sec) && (now.tv_usec >= timeout.tv_usec)))
3168
		{
3169
			command_print(CMD_CTX, "Profiling completed. %d samples.", numSamples);
zwelch's avatar
zwelch committed
3170
			if ((retval = target_poll(target)) != ERROR_OK)
3171
3172
3173
3174
			{
				free(samples);
				return retval;
			}
3175
3176
			if (target->state == TARGET_HALTED)
			{
3177
				target_resume(target, 1, 0, 0, 0); /* current pc, addr = 0, do not handle breakpoints, not debugging */
3178
			}
zwelch's avatar
zwelch committed
3179
			if ((retval = target_poll(target)) != ERROR_OK)
3180
3181
3182
3183
			{
				free(samples);
				return retval;
			}
3184
			writeGmon(samples, numSamples, CMD_ARGV[1]);
3185
			command_print(CMD_CTX, "Wrote %s", CMD_ARGV[1]);
3186
3187
3188
3189
			break;
		}
	}
	free(samples);
oharboe's avatar
oharboe committed
3190

3191
3192
	return ERROR_OK;
}
3193

3194
static int new_int_array_element(Jim_Interp * interp, const char *varname, int idx, uint32_t val)
3195
3196
3197
3198
3199
3200
3201
3202
{
	char *namebuf;
	Jim_Obj *nameObjPtr, *valObjPtr;
	int result;

	namebuf = alloc_printf("%s(%d)", varname, idx);
	if (!namebuf)
		return JIM_ERR;
oharboe's avatar
oharboe committed
3203

3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
	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)
{
3224
	struct command_context *context;
Zachary T Welch's avatar
Zachary T Welch committed
3225
	struct target *target;
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239

	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;
	}

zwelch's avatar
zwelch committed
3240
	return 	target_mem2array(interp, target, argc-1, argv + 1);
3241
3242
}

Zachary T Welch's avatar
Zachary T Welch committed
3243
static int target_mem2array(Jim_Interp *interp, struct target *target, int argc, Jim_Obj *const *argv)
3244
{
3245
	long l;
3246
	uint32_t width;
3247
	int len;
3248
3249
3250
	uint32_t addr;
	uint32_t count;
	uint32_t v;
3251
	const char *varname;
3252
	int  n, e, retval;
3253
	uint32_t i;
3254
3255
3256

	/* argv[1] = name of array to receive the data
	 * argv[2] = desired width
oharboe's avatar
oharboe committed
3257
	 * argv[3] = memory address
3258
3259
	 * argv[4] = count of times to read
	 */
3260
	if (argc != 4) {
3261
3262
3263
		Jim_WrongNumArgs(interp, 1, argv, "varname width addr nelems");
		return JIM_ERR;
	}
3264
	varname = Jim_GetString(argv[0], &len);
3265
3266
	/* given "foo" get space for worse case "foo(%d)" .. add 20 */

3267
	e = Jim_GetLong(interp, argv[1], &l);
3268
3269
3270
3271
	width = l;
	if (e != JIM_OK) {
		return e;
	}
oharboe's avatar
oharboe committed
3272

3273
	e = Jim_GetLong(interp, argv[2], &l);
3274
3275
3276
3277
	addr = l;
	if (e != JIM_OK) {
		return e;
	}
3278
	e = Jim_GetLong(interp, argv[3], &l);
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
	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));
3295
			Jim_AppendStrings(interp, Jim_GetResult(interp), "Invalid width param, must be 8/16/32", NULL);
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
			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;
oharboe's avatar
oharboe committed
3313
3314
	}

3315
3316
3317
3318
3319
3320
3321
	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));
3322
3323
		sprintf(buf, "mem2array address: 0x%08" PRIx32 " is not aligned for %" PRId32 " byte reads",
				addr,
duane's avatar
duane committed
3324
				width);
3325
3326
3327
3328
3329
3330
3331
3332
		Jim_AppendStrings(interp, Jim_GetResult(interp), buf , NULL);
		return JIM_ERR;
	}

	/* Transfer loop */

	/* index counter */
	n = 0;
Øyvind Harboe's avatar
Øyvind Harboe committed
3333
3334
3335
3336
3337
3338

	size_t buffersize = 4096;
	uint8_t *buffer = malloc(buffersize);
	if (buffer == NULL)
		return JIM_ERR;

3339
3340
3341
3342
	/* assume ok */
	e = JIM_OK;
	while (len) {
		/* Slurp... in buffer size chunks */
oharboe's avatar
oharboe committed
3343

3344
		count = len; /* in objects.. */
Øyvind Harboe's avatar
Øyvind Harboe committed
3345
3346
		if (count > (buffersize/width)) {
			count = (buffersize/width);
3347
		}
oharboe's avatar
oharboe committed
3348

3349
		retval = target_read_memory(target, addr, width, count, buffer);
3350
3351
		if (retval != ERROR_OK) {
			/* BOO !*/
3352
3353
3354
			LOG_ERROR("mem2array: Read @ 0x%08x, w=%d, cnt=%d, failed",
					  (unsigned int)addr,
					  (int)width,
duane's avatar
duane committed
3355
					  (int)count);
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
			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;
		}
	}
oharboe's avatar
oharboe committed
3379

Øyvind Harboe's avatar
Øyvind Harboe committed
3380
3381
	free(buffer);

3382
3383
3384
3385
3386
	Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));

	return JIM_OK;
}

3387
static int get_int_array_element(Jim_Interp * interp, const char *varname, int idx, uint32_t *val)
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
{
	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)
{
3420
	struct command_context *context;
Zachary T Welch's avatar
Zachary T Welch committed
3421
	struct target *target;
3422

3423
	context = Jim_GetAssocData(interp, "context");
zwelch's avatar
zwelch committed
3424
	if (context == NULL) {
3425
3426
3427
3428
		LOG_ERROR("array2mem: no command context");
		return JIM_ERR;
	}
	target = get_current_target(context);
zwelch's avatar
zwelch committed
3429
	if (target == NULL) {
3430
3431
3432
		LOG_ERROR("array2mem: no current target");
		return JIM_ERR;
	}
3433

3434
	return target_array2mem(interp,target, argc-1, argv + 1);
3435
}
3436
3437
3438

static int target_array2mem(Jim_Interp *interp, struct target *target,
		int argc, Jim_Obj *const *argv)
3439
{
3440
	long l;
3441
	uint32_t width;
3442
	int len;
3443
3444
3445
	uint32_t addr;
	uint32_t count;
	uint32_t v;
3446
	const char *varname;
3447
	int  n, e, retval;
3448
	uint32_t i;
3449
3450
3451

	/* argv[1] = name of array to get the data
	 * argv[2] = desired width
oharboe's avatar
oharboe committed
3452
	 * argv[3] = memory address
3453
3454
	 * argv[4] = count to write
	 */
3455
	if (argc != 4) {
3456
		Jim_WrongNumArgs(interp, 0, argv, "varname width addr nelems");
3457
3458
		return JIM_ERR;
	}
3459
	varname = Jim_GetString(argv[0], &len);
3460
3461
	/* given "foo" get space for worse case "foo(%d)" .. add 20 */

3462
	e = Jim_GetLong(interp, argv[1], &l);
3463
3464
3465
3466
	width = l;
	if (e != JIM_OK) {
		return e;
	}
oharboe's avatar
oharboe committed
3467

3468
	e = Jim_GetLong(interp, argv[2], &l);
3469
3470
3471
3472
	addr = l;
	if (e != JIM_OK) {
		return e;
	}
3473
	e = Jim_GetLong(interp, argv[3], &l);
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
	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));
3490
			Jim_AppendStrings(interp, Jim_GetResult(interp), "Invalid width param, must be 8/16/32", NULL);
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
			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;
oharboe's avatar
oharboe committed
3508
3509
	}

3510
3511
3512
3513
3514
3515
3516
	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));
3517
3518
		sprintf(buf, "array2mem address: 0x%08x is not aligned for %d byte reads",
				(unsigned int)addr,
duane's avatar
duane committed
3519
				(int)width);
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
		Jim_AppendStrings(interp, Jim_GetResult(interp), buf , NULL);
		return JIM_ERR;
	}

	/* Transfer loop */

	/* index counter */
	n = 0;
	/* assume ok */
	e = JIM_OK;
Øyvind Harboe's avatar
Øyvind Harboe committed
3530
3531
3532
3533
3534
3535

	size_t buffersize = 4096;
	uint8_t *buffer = malloc(buffersize);
	if (buffer == NULL)
		return JIM_ERR;

3536
3537
	while (len) {
		/* Slurp... in buffer size chunks */
oharboe's avatar
oharboe committed
3538

3539
		count = len; /* in objects.. */
Øyvind Harboe's avatar
Øyvind Harboe committed
3540
3541
		if (count > (buffersize/width)) {
			count = (buffersize/width);
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
		}

		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;

zwelch's avatar
zwelch committed
3561
		retval = target_write_memory(target, addr, width, count, buffer);
3562
3563
		if (retval != ERROR_OK) {
			/* BOO !*/
3564
3565
3566
			LOG_ERROR("array2mem: Write @ 0x%08x, w=%d, cnt=%d, failed",
					  (unsigned int)addr,
					  (int)width,
duane's avatar
duane committed
3567
					  (int)count);
3568
			Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
3569
			Jim_AppendStrings(interp, Jim_GetResult(interp), "array2mem: cannot read memory", NULL);
3570
3571
3572
3573
			e = JIM_ERR;
			len = 0;
		}
	}
oharboe's avatar
oharboe committed
3574

Øyvind Harboe's avatar
Øyvind Harboe committed
3575
3576
	free(buffer);

3577
3578
3579
3580
	Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));

	return JIM_OK;
}
3581

3582
void target_all_handle_event(enum target_event e)
3583
{
Zachary T Welch's avatar
Zachary T Welch committed
3584
	struct target *target;
3585

3586
	LOG_DEBUG("**all*targets: event: %d, %s",
duane's avatar
duane committed
3587
			   (int)e,
3588
			   Jim_Nvp_value2name_simple(nvp_target_event, e)->name);
3589
3590

	target = all_targets;
zwelch's avatar
zwelch committed
3591
	while (target) {
3592
		target_handle_event(target, e);
3593
3594
3595
3596
		target = target->next;
	}
}

3597
3598
3599
3600

/* FIX? should we propagate errors here rather than printing them
 * and continuing?
 */
Zachary T Welch's avatar
Zachary T Welch committed
3601
void target_handle_event(struct target *target, enum target_event e)
3602
{
3603
	struct target_event_action *teap;
3604

dbrownell's avatar
dbrownell committed
3605
	for (teap = target->event_action; teap != NULL; teap = teap->next) {
zwelch's avatar
zwelch committed
3606
		if (teap->event == e) {
3607
			LOG_DEBUG("target: (%d) %s (%s) event: %d (%s) action: %s",
3608
					   target->target_number,
3609
					   target_name(target),
3610
					   target_type_name(target),
3611
					   e,
3612
3613
					   Jim_Nvp_value2name_simple(nvp_target_event, e)->name,
					   Jim_GetString(teap->body, NULL));
3614
			if (Jim_EvalObj(teap->interp, teap->body) != JIM_OK)
3615
			{
3616
				Jim_PrintErrorMessage(teap->interp);
3617
			}
3618
3619
3620
3621
		}
	}
}

3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
/**
 * Returns true only if the target has a handler for the specified event.
 */
bool target_has_event_action(struct target *target, enum target_event event)
{
	struct target_event_action *teap;

	for (teap = target->event_action; teap != NULL; teap = teap->next) {
		if (teap->event == event)
			return true;
	}
	return false;
}

3636
3637
enum target_cfg_param {
	TCFG_TYPE,
3638
	TCFG_EVENT,
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
	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 },
3658

3659
3660
	{ .name = NULL, .value = -1 }
};
3661

Zachary T Welch's avatar
Zachary T Welch committed
3662
static int target_configure(Jim_GetOptInfo *goi, struct target *target)
3663
3664
3665
3666
3667
3668
3669
3670
{
	Jim_Nvp *n;
	Jim_Obj *o;
	jim_wide w;
	char *cp;
	int e;

	/* parse config or cget options ... */
zwelch's avatar
zwelch committed
3671
	while (goi->argc > 0) {
3672
3673
		Jim_SetEmptyResult(goi->interp);
		/* Jim_GetOpt_Debug(goi); */
3674

zwelch's avatar
zwelch committed
3675
		if (target->type->target_jim_configure) {
3676
3677
			/* target defines a configure function */
			/* target gets first dibs on parameters */
3678
			e = (*(target->type->target_jim_configure))(target, goi);
zwelch's avatar
zwelch committed
3679
			if (e == JIM_OK) {
3680
3681
3682
				/* more? */
				continue;
			}
zwelch's avatar
zwelch committed
3683
			if (e == JIM_ERR) {
3684
3685
3686
3687
3688
				/* An error */
				return e;
			}
			/* otherwise we 'continue' below */
		}
3689
		e = Jim_GetOpt_Nvp(goi, nvp_config_opts, &n);
zwelch's avatar
zwelch committed
3690
		if (e != JIM_OK) {
3691
			Jim_GetOpt_NvpUnknown(goi, nvp_config_opts, 0);
3692
3693
			return e;
		}
zwelch's avatar
zwelch committed
3694
		switch (n->value) {
3695
3696
		case TCFG_TYPE:
			/* not setable */
zwelch's avatar
zwelch committed
3697
			if (goi->isconfigure) {
3698
3699
				Jim_SetResult_sprintf(goi->interp,
						"not settable: %s", n->name);
3700
3701
3702
				return JIM_ERR;
			} else {
			no_params:
zwelch's avatar
zwelch committed
3703
				if (goi->argc != 0) {
3704
3705
3706
					Jim_WrongNumArgs(goi->interp,
							goi->argc, goi->argv,
							"NO PARAMS");
3707
3708
3709
					return JIM_ERR;
				}
			}
3710
3711
			Jim_SetResultString(goi->interp,
					target_type_name(target), -1);
3712
3713
3714
			/* loop for more */
			break;
		case TCFG_EVENT:
zwelch's avatar
zwelch committed
3715
			if (goi->argc == 0) {
3716
				Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "-event ?event-name? ...");
3717
3718
3719
				return JIM_ERR;
			}

3720
			e = Jim_GetOpt_Nvp(goi, nvp_target_event, &n);
zwelch's avatar
zwelch committed
3721
			if (e != JIM_OK) {
3722
				Jim_GetOpt_NvpUnknown(goi, nvp_target_event, 1);
3723
3724
3725
				return e;
			}

zwelch's avatar
zwelch committed
3726
3727
			if (goi->isconfigure) {
				if (goi->argc != 1) {
3728
					Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "-event ?event-name? ?EVENT-BODY?");
3729
					return JIM_ERR;
3730
				}
3731
			} else {
zwelch's avatar
zwelch committed
3732
				if (goi->argc != 0) {
3733
3734
3735
3736
3737
					Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "-event ?event-name?");
					return JIM_ERR;
				}
			}

3738
			{
3739
				struct target_event_action *teap;
3740

3741
3742
				teap = target->event_action;
				/* replace existing? */
zwelch's avatar
zwelch committed
3743
3744
				while (teap) {
					if (teap->event == (enum target_event)n->value) {
3745
3746
3747
3748
						break;
					}
					teap = teap->next;
				}
3749

zwelch's avatar
zwelch committed
3750
				if (goi->isconfigure) {
3751
					bool replace = true;
zwelch's avatar
zwelch committed
3752
					if (teap == NULL) {
3753
						/* create new */
3754
						teap = calloc(1, sizeof(*teap));
3755
						replace = false;
3756
3757
					}
					teap->event = n->value;
3758
					teap->interp = goi->interp;
3759
					Jim_GetOpt_Obj(goi, &o);
zwelch's avatar
zwelch committed
3760
					if (teap->body) {
3761
						Jim_DecrRefCount(teap->interp, teap->body);
3762
					}
3763
					teap->body  = Jim_DuplicateObj(goi->interp, o);
3764
					/*
3765
					 * FIXME:
3766
3767
3768
3769
3770
3771
3772
3773
					 *     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.
					 */
3774
					Jim_IncrRefCount(teap->body);
3775

3776
3777
3778
3779
3780
3781
					if (!replace)
					{
						/* add to head of event list */
						teap->next = target->event_action;
						target->event_action = teap;
					}
3782
3783
3784
					Jim_SetEmptyResult(goi->interp);
				} else {
					/* get */
zwelch's avatar
zwelch committed
3785
					if (teap == NULL) {
3786
						Jim_SetEmptyResult(goi->interp);
3787
					} else {
3788
						Jim_SetResult(goi->interp, Jim_DuplicateObj(goi->interp, teap->body));
3789
3790
3791
3792
3793
3794
3795
					}
				}
			}
			/* loop for more */
			break;

		case TCFG_WORK_AREA_VIRT:
zwelch's avatar
zwelch committed
3796
			if (goi->isconfigure) {
3797
				target_free_all_working_areas(target);
3798
				e = Jim_GetOpt_Wide(goi, &w);
zwelch's avatar
zwelch committed
3799
				if (e != JIM_OK) {
3800
3801
3802
					return e;
				}
				target->working_area_virt = w;
3803
				target->working_area_virt_spec = true;
3804
			} else {
zwelch's avatar
zwelch committed
3805
				if (goi->argc != 0) {
3806
3807
					goto no_params;
				}
3808
			}
3809
			Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->working_area_virt));
3810
3811
			/* loop for more */
			break;
3812

3813
		case TCFG_WORK_AREA_PHYS:
zwelch's avatar
zwelch committed
3814
			if (goi->isconfigure) {
3815
				target_free_all_working_areas(target);
3816
				e = Jim_GetOpt_Wide(goi, &w);
zwelch's avatar
zwelch committed
3817
				if (e != JIM_OK) {
3818
3819
3820
					return e;
				}
				target->working_area_phys = w;
3821
				target->working_area_phys_spec = true;
3822
			} else {
zwelch's avatar
zwelch committed
3823
				if (goi->argc != 0) {
3824
3825
					goto no_params;
				}
3826
			}
3827
			Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->working_area_phys));
3828
3829
3830
3831
			/* loop for more */
			break;

		case TCFG_WORK_AREA_SIZE:
zwelch's avatar
zwelch committed
3832
			if (goi->isconfigure) {
3833
				target_free_all_working_areas(target);
3834
				e = Jim_GetOpt_Wide(goi, &w);
zwelch's avatar
zwelch committed
3835
				if (e != JIM_OK) {
3836
3837
3838
3839
					return e;
				}
				target->working_area_size = w;
			} else {
zwelch's avatar
zwelch committed
3840
				if (goi->argc != 0) {
3841
3842
					goto no_params;
				}
3843
			}
3844
			Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->working_area_size));
3845
3846
3847
3848
			/* loop for more */
			break;

		case TCFG_WORK_AREA_BACKUP:
zwelch's avatar
zwelch committed
3849
			if (goi->isconfigure) {
3850
				target_free_all_working_areas(target);
3851
				e = Jim_GetOpt_Wide(goi, &w);
zwelch's avatar
zwelch committed
3852
				if (e != JIM_OK) {
3853
3854
3855
3856
3857
					return e;
				}
				/* make this exactly 1 or 0 */
				target->backup_working_area = (!!w);
			} else {
zwelch's avatar
zwelch committed
3858
				if (goi->argc != 0) {
3859
3860
					goto no_params;
				}
3861
			}
3862
			Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->backup_working_area));
3863
3864
3865
3866
			/* loop for more e*/
			break;

		case TCFG_ENDIAN:
zwelch's avatar
zwelch committed
3867
			if (goi->isconfigure) {
3868
				e = Jim_GetOpt_Nvp(goi, nvp_target_endian, &n);
zwelch's avatar
zwelch committed
3869
				if (e != JIM_OK) {
3870
					Jim_GetOpt_NvpUnknown(goi, nvp_target_endian, 1);
3871
3872
3873
3874
					return e;
				}
				target->endianness = n->value;
			} else {
zwelch's avatar
zwelch committed
3875
				if (goi->argc != 0) {
3876
3877
3878
					goto no_params;
				}
			}
3879
			n = Jim_Nvp_value2name_simple(nvp_target_endian, target->endianness);
zwelch's avatar
zwelch committed
3880
			if (n->name == NULL) {
3881
				target->endianness = TARGET_LITTLE_ENDIAN;
3882
				n = Jim_Nvp_value2name_simple(nvp_target_endian, target->endianness);
3883
			}
3884
			Jim_SetResultString(goi->interp, n->name, -1);
3885
3886
3887
3888
			/* loop for more */
			break;

		case TCFG_VARIANT:
zwelch's avatar
zwelch committed
3889
3890
			if (goi->isconfigure) {
				if (goi->argc < 1) {
3891
					Jim_SetResult_sprintf(goi->interp,
3892
										   "%s ?STRING?",
3893
										   n->name);
3894
3895
					return JIM_ERR;
				}
zwelch's avatar
zwelch committed
3896
				if (target->variant) {
3897
3898
					free((void *)(target->variant));
				}
3899
				e = Jim_GetOpt_String(goi, &cp, NULL);
3900
3901
				target->variant = strdup(cp);
			} else {
zwelch's avatar
zwelch committed
3902
				if (goi->argc != 0) {
3903
3904
					goto no_params;
				}
3905
			}
3906
			Jim_SetResultString(goi->interp, target->variant,-1);
3907
3908
3909
			/* loop for more */
			break;
		case TCFG_CHAIN_POSITION:
zwelch's avatar
zwelch committed
3910
			if (goi->isconfigure) {
3911
				Jim_Obj *o;
3912
				struct jtag_tap *tap;
3913
				target_free_all_working_areas(target);
3914
				e = Jim_GetOpt_Obj(goi, &o);
zwelch's avatar
zwelch committed
3915
				if (e != JIM_OK) {
3916
3917
					return e;
				}