/**
 * Copies code to a working area.  This will allocate room for the code plus the
 * additional amount requested if the working area pointer is null.
 *
 * @param target Pointer to the target to copy code to
 * @param code Pointer to the code area to be copied
 * @param code_size Size of the code being copied
 * @param additional Size of the additional area to be allocated in addition to
 *                   code
 * @param area Pointer to a pointer to a working area to copy code to
 * @return Success or failure of the operation
 */
static int arm_code_to_working_area(struct target *target,
	const uint32_t *code, unsigned code_size,
	unsigned additional, struct working_area **area)
{
	uint8_t code_buf[code_size];
	unsigned i;
	int retval;
	unsigned size = code_size + additional;

	/* REVISIT this assumes size doesn't ever change.
	 * That's usually correct; but there are boards with
	 * both large and small page chips, where it won't be...
	 */

	/* make sure we have a working area */
	if (NULL == *area) {
		retval = target_alloc_working_area(target, size, area);
		if (retval != ERROR_OK) {
			LOG_DEBUG("%s: no %d byte buffer", __func__, (int) size);
			return ERROR_NAND_NO_BUFFER;
		}
	}

	/* buffer code in target endianness */
	for (i = 0; i < code_size / 4; i++)
		target_buffer_set_u32(target, code_buf + i * 4, code[i]);

	/* copy code to work area */
	retval = target_write_memory(target, (*area)->address,
			4, code_size / 4, code_buf);

	return retval;
}
Exemple #2
0
/* Write byte at address */
static int arc_mem_write_block8(struct target *target, uint32_t addr,
	uint32_t count, void *buf)
{
	struct arc32_common *arc32 = target_to_arc32(target);
	uint32_t i;

	LOG_DEBUG("Write 1-byte memory block: addr=0x%08" PRIx32 ", count=%" PRIu32,
			addr, count);

	/* We will read data from memory, so we need to flush D$. */
	CHECK_RETVAL(arc32_dcache_flush(target));

	uint32_t buffer_he;
	uint8_t buffer_te[sizeof(uint32_t)];
	/* non-word writes are less common, than 4-byte writes, so I suppose we can
	 * allowe ourselves to write this in a cycle, instead of calling arc_jtag
	 * with count > 1. */
	for(i = 0; i < count; i++) {
		/* See comment in arc_mem_write_block16 for details. Since it is a byte
		 * there is not need to convert write buffer to target endianness, but
		 * we still have to convert read buffer. */
		CHECK_RETVAL(arc_jtag_read_memory(&arc32->jtag_info, (addr + i) & ~3, 1, &buffer_he,
			    arc_mem_is_slow_memory(arc32, (addr + i) & ~3, 4, 1)));
		target_buffer_set_u32(target, buffer_te, buffer_he);
		memcpy(buffer_te  + ((addr + i) & 3), (uint8_t*)buf + i, 1);
		buffer_he = target_buffer_get_u32(target, buffer_te);
		CHECK_RETVAL(arc_jtag_write_memory(&arc32->jtag_info, (addr + i) & ~3, 1, &buffer_he));
	}

	/* Invalidate caches. */
	CHECK_RETVAL(arc32_cache_invalidate(target));

	return ERROR_OK;
}
Exemple #3
0
static int or1k_add_breakpoint(struct target *target, 
			       struct breakpoint *breakpoint)
{
	LOG_DEBUG("Adding breakpoint: addr %08x, len %d, type %d, set: %d, id: %d",
		  breakpoint->address, breakpoint->length, breakpoint->type,
		  breakpoint->set, breakpoint->unique_id);

	struct or1k_common *or1k = target_to_or1k(target);

	/* Only support SW breakpoints for now. */
	if (breakpoint->type == BKPT_HARD)
		LOG_ERROR("HW breakpoints not supported for now. Doing SW breakpoint.");
	
	/* Read and save the instruction */
	or1k_jtag_read_memory32(&or1k->jtag, 
				breakpoint->address , 
				1,
				(uint32_t*)breakpoint->orig_instr);

	/* Sub in the OR1K trap instruction */
	uint32_t or1k_trap_insn;
	/* set correct endianess */
	target_buffer_set_u32(target, (uint8_t*)&or1k_trap_insn, OR1K_TRAP_INSTR);
	or1k_jtag_write_memory32(&or1k->jtag, 
				breakpoint->address , 
				 1,
				 (uint32_t*)&or1k_trap_insn);

	/* invalidate instruction cache */
	or1k_jtag_write_cpu(&or1k->jtag,
			OR1K_ICBIR_CPU_REG_ADD, 1, &breakpoint->address);

	return ERROR_OK;
}
Exemple #4
0
static int or1k_add_breakpoint(struct target *target,
			       struct breakpoint *breakpoint)
{
	struct or1k_common *or1k = target_to_or1k(target);
	struct or1k_du *du_core = or1k_to_du(or1k);
	uint8_t data;

	LOG_DEBUG("Adding breakpoint: addr 0x%08" TARGET_PRIxADDR ", len %d, type %d, set: %d, id: %" PRId32,
		  breakpoint->address, breakpoint->length, breakpoint->type,
		  breakpoint->set, breakpoint->unique_id);

	/* Only support SW breakpoints for now. */
	if (breakpoint->type == BKPT_HARD)
		LOG_ERROR("HW breakpoints not supported for now. Doing SW breakpoint.");

	/* Read and save the instruction */
	int retval = du_core->or1k_jtag_read_memory(&or1k->jtag,
					 breakpoint->address,
					 4,
					 1,
					 &data);
	if (retval != ERROR_OK) {
		LOG_ERROR("Error while reading the instruction at 0x%08" TARGET_PRIxADDR,
			   breakpoint->address);
		return retval;
	}

	if (breakpoint->orig_instr != NULL)
		free(breakpoint->orig_instr);

	breakpoint->orig_instr = malloc(breakpoint->length);
	memcpy(breakpoint->orig_instr, &data, breakpoint->length);

	/* Sub in the OR1K trap instruction */
	uint8_t or1k_trap_insn[4];
	target_buffer_set_u32(target, or1k_trap_insn, OR1K_TRAP_INSTR);
	retval = du_core->or1k_jtag_write_memory(&or1k->jtag,
					  breakpoint->address,
					  4,
					  1,
					  or1k_trap_insn);

	if (retval != ERROR_OK) {
		LOG_ERROR("Error while writing OR1K_TRAP_INSTR at 0x%08" TARGET_PRIxADDR,
			   breakpoint->address);
		return retval;
	}

	/* invalidate instruction cache */
	uint32_t addr = breakpoint->address;
	retval = du_core->or1k_jtag_write_cpu(&or1k->jtag,
			OR1K_ICBIR_CPU_REG_ADD, 1, &addr);
	if (retval != ERROR_OK) {
		LOG_ERROR("Error while invalidating the ICACHE");
		return retval;
	}

	return ERROR_OK;
}
Exemple #5
0
static int lpc2000_iap_working_area_init(struct flash_bank *bank, struct working_area **iap_working_area)
{
	struct target *target = bank->target;
	struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv;

	if (target_alloc_working_area(target, IAP_CODE_LEN + lpc2000_info->iap_max_stack, iap_working_area) != ERROR_OK) {
		LOG_ERROR("no working area specified, can't write LPC2000 internal flash");
		return ERROR_FLASH_OPERATION_FAILED;
	}

	uint8_t jump_gate[8];

	/* write IAP code to working area */
	switch (lpc2000_info->variant) {
		case lpc800:
		case lpc1100:
		case lpc1500:
		case lpc1700:
		case lpc4300:
		case lpc54100:
		case lpc_auto:
			target_buffer_set_u32(target, jump_gate, ARMV4_5_T_BX(12));
			target_buffer_set_u32(target, jump_gate + 4, ARMV5_T_BKPT(0));
			break;
		case lpc2000_v1:
		case lpc2000_v2:
			target_buffer_set_u32(target, jump_gate, ARMV4_5_BX(12));
			target_buffer_set_u32(target, jump_gate + 4, ARMV4_5_B(0xfffffe, 0));
			break;
		default:
			LOG_ERROR("BUG: unknown lpc2000_info->variant encountered");
			exit(-1);
	}

	int retval = target_write_memory(target, (*iap_working_area)->address, 4, 2, jump_gate);
	if (retval != ERROR_OK) {
		LOG_ERROR("Write memory at address 0x%8.8" PRIx32 " failed (check work_area definition)",
				(*iap_working_area)->address);
		target_free_working_area(target, *iap_working_area);
	}

	return retval;
}
Exemple #6
0
/* Write half-word at half-word-aligned address */
static int arc_mem_write_block16(struct target *target, uint32_t addr, int count,
	void *buf)
{
	struct arc32_common *arc32 = target_to_arc32(target);
	int retval = ERROR_OK;
	int i;

	LOG_DEBUG("Write memory (16bit): addr=0x%" PRIx32 ", count=%i", addr, count);

	/* Check arguments */
	if (addr & 1u)
		return ERROR_TARGET_UNALIGNED_ACCESS;

	/* We will read data from memory, so we need to flush D$. */
	retval = arc32_dcache_flush(target);
	if (ERROR_OK != retval)
		return retval;

	uint32_t buffer_he;
	uint8_t buffer_te[sizeof(uint32_t)];
	uint8_t halfword_te[sizeof(uint16_t)];
	/* non-word writes are less common, than 4-byte writes, so I suppose we can
	 * allowe ourselves to write this in a cycle, instead of calling arc_jtag
	 * with count > 1. */
	for(i = 0; i < count; i++) {
		/* We can read only word at word-aligned address. Also *jtag_read_memory
		 * functions return data in host endianness, so host endianness !=
		 * target endianness we have to convert data back to target endianness,
		 * or bytes will be at the wrong places.So:
		 *   1) read word
		 *   2) convert to target endianness
		 *   3) make changes
		 *   4) convert back to host endianness
		 *   5) write word back to target.
		 */
		retval = arc_jtag_read_memory(&arc32->jtag_info,
				(addr + i * sizeof(uint16_t)) & ~3u, 1, &buffer_he);
		target_buffer_set_u32(target, buffer_te, buffer_he);
		/* buf is in host endianness, convert to target */
		target_buffer_set_u16(target, halfword_te, ((uint16_t *)buf)[i]);
		memcpy(buffer_te  + ((addr + i * sizeof(uint16_t)) & 3u),
                        halfword_te, sizeof(uint16_t));
		buffer_he = target_buffer_get_u32(target, buffer_te);
		retval = arc_jtag_write_memory(&arc32->jtag_info,
                        (addr + i * sizeof(uint16_t)) & ~3u, 1, &buffer_he);

		if (ERROR_OK != retval)
			return retval;
	}

	/* Invalidate caches. */
	retval = arc32_cache_invalidate(target);

	return retval;
}
Exemple #7
0
/* Write byte at address */
static int arc_mem_write_block8(struct target *target, uint32_t addr, int count,
	void *buf)
{
	struct arc32_common *arc32 = target_to_arc32(target);
	int retval = ERROR_OK;
	int i;

	/* We will read data from memory, so we need to flush D$. */
	retval = arc32_dcache_flush(target);
	if (ERROR_OK != retval)
		return retval;

	uint32_t buffer_he;
	uint8_t buffer_te[sizeof(uint32_t)];
	/* non-word writes are less common, than 4-byte writes, so I suppose we can
	 * allowe ourselves to write this in a cycle, instead of calling arc_jtag
	 * with count > 1. */
	for(i = 0; i < count; i++) {
		/* See comment in arc_mem_write_block16 for details. Since it is a byte
		 * there is not need to convert write buffer to target endianness, but
		 * we still have to convert read buffer. */
		retval = arc_jtag_read_memory(&arc32->jtag_info, (addr + i) & ~3, 1, &buffer_he);
		target_buffer_set_u32(target, buffer_te, buffer_he);
		memcpy(buffer_te  + ((addr + i) & 3), (uint8_t*)buf + i, 1);
		buffer_he = target_buffer_get_u32(target, buffer_te);
		retval = arc_jtag_write_memory(&arc32->jtag_info, (addr + i) & ~3, 1, &buffer_he);

		if (ERROR_OK != retval)
			return retval;
	}

	/* Invalidate caches. */
	retval = arc32_cache_invalidate(target);

	return retval;
}
Exemple #8
0
/* Reference: "XMC4500 Flash Protection.pptx" app note */
static int xmc4xxx_flash_protect(struct flash_bank *bank, int level, bool read_protect,
				 int first, int last)
{
	/* User configuration block buffers */
	uint8_t ucp0_buf[8 * sizeof(uint32_t)] = {0};
	uint32_t ucb_base = 0;
	uint32_t procon = 0;
	int res = ERROR_OK;
	uint32_t status = 0;
	bool proin = false;

	struct xmc4xxx_flash_bank *fb = bank->driver_priv;

	/* Read protect only works for user 0, make sure we don't try
	 * to do something silly */
	if (level != 0 && read_protect) {
		LOG_ERROR("Read protection is for user level 0 only!");
		return ERROR_FAIL;
	}

	/* Check to see if protection is already installed for the
	 * specified user level.  If it is, the user configuration
	 * block will need to be erased before we can continue */

	/* Grab the flash status register*/
	res = xmc4xxx_get_flash_status(bank, &status);
	if (res != ERROR_OK)
		return res;

	switch (level) {
	case 0:
		if ((status & FSR_RPROIN_MASK) || (status & FSR_WPROIN0_MASK))
			proin = true;
		break;
	case 1:
		if (status & FSR_WPROIN1_MASK)
			proin = true;
		break;
	case 2:
		if (status & FSR_WPROIN2_MASK)
			proin = true;
		break;
	}

	if (proin) {
		LOG_ERROR("Flash protection is installed for user %d"
			  " and must be removed before continuing", level);
		return ERROR_FAIL;
	}

	/* If this device has 12 flash sectors, protection for
	 * sectors 10 & 11 are handled jointly. If we are trying to
	 * write all sectors, we should decrement
	 * last to ensure we don't write to a register bit that
	 * doesn't exist*/
	if ((bank->num_sectors == 12) && (last == 12))
		last--;

	/*  We need to fill out the procon register representation
	 *   that we will be writing to the device */
	for (int i = first; i <= last; i++)
		procon |= 1 << i;

	/* If read protection is requested, set the appropriate bit
	 * (we checked that this is allowed above) */
	if (read_protect)
		procon |= PROCON_RPRO_MASK;

	LOG_DEBUG("Setting flash protection with procon:");
	LOG_DEBUG("PROCON: %"PRIx32, procon);

	/* First we need to copy in the procon register to the buffer
	 * we're going to attempt to write.  This is written twice */
	target_buffer_set_u32(bank->target, &ucp0_buf[0 * 4], procon);
	target_buffer_set_u32(bank->target, &ucp0_buf[2 * 4], procon);

	/* Now we must copy in both flash passwords.  As with the
	 * procon data, this must be written twice (4 total words
	 * worth of data) */
	target_buffer_set_u32(bank->target, &ucp0_buf[4 * 4], fb->pw1);
	target_buffer_set_u32(bank->target, &ucp0_buf[5 * 4], fb->pw2);
	target_buffer_set_u32(bank->target, &ucp0_buf[6 * 4], fb->pw1);
	target_buffer_set_u32(bank->target, &ucp0_buf[7 * 4], fb->pw2);

	/* Finally, (if requested) we copy in the confirmation
	 * code so that the protection is permanent and will
	 * require a password to undo. */
	target_buffer_set_u32(bank->target, &ucp0_buf[0 * 4], FLASH_PROTECT_CONFIRMATION_CODE);
	target_buffer_set_u32(bank->target, &ucp0_buf[2 * 4], FLASH_PROTECT_CONFIRMATION_CODE);

	/* Now that the data is copied into place, we must write
	 * these pages into flash */

	/* The user configuration block base depends on what level of
	 * protection we're trying to install, select the proper one */
	switch (level) {
	case 0:
		ucb_base = UCB0_BASE;
		break;
	case 1:
		ucb_base = UCB1_BASE;
		break;
	case 2:
		ucb_base = UCB2_BASE;
		break;
	}

	/* Write the user config pages */
	res = xmc4xxx_write_page(bank, ucp0_buf, ucb_base, true);
	if (res != ERROR_OK) {
		LOG_ERROR("Error writing user configuration block 0");
		return res;
	}

	return ERROR_OK;
}
Exemple #9
0
/* call LPC1700/LPC2000 IAP function
 * uses 180 bytes working area
 * 0x0 to 0x7: jump gate (BX to thumb state, b -2 to wait)
 * 0x8 to 0x1f: command parameter table (1+5 words)
 * 0x20 to 0x33: command result table (1+4 words)
 * 0x34 to 0xb3: stack (only 128b needed)
 */
static int lpc2000_iap_call(struct flash_bank *bank,
	int code,
	uint32_t param_table[5],
	uint32_t result_table[4])
{
	int retval;
	struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv;
	struct target *target = bank->target;
	struct mem_param mem_params[2];
	struct reg_param reg_params[5];
	struct arm_algorithm arm_algo;	/* for LPC2000 */
	struct armv7m_algorithm armv7m_info;	/* for LPC1700 */
	uint32_t status_code;
	uint32_t iap_entry_point = 0;	/* to make compiler happier */

	/* regrab previously allocated working_area, or allocate a new one */
	if (!lpc2000_info->iap_working_area) {
		uint8_t jump_gate[8];

		/* make sure we have a working area */
		if (target_alloc_working_area(target, 180,
				&lpc2000_info->iap_working_area) != ERROR_OK) {
			LOG_ERROR("no working area specified, can't write LPC2000 internal flash");
			return ERROR_FLASH_OPERATION_FAILED;
		}

		/* write IAP code to working area */
		switch (lpc2000_info->variant) {
			case lpc1700:
				target_buffer_set_u32(target, jump_gate, ARMV4_5_T_BX(12));
				target_buffer_set_u32(target, jump_gate + 4, ARMV5_T_BKPT(0));
				break;
			case lpc2000_v1:
			case lpc2000_v2:
				target_buffer_set_u32(target, jump_gate, ARMV4_5_BX(12));
				target_buffer_set_u32(target, jump_gate + 4, ARMV4_5_B(0xfffffe, 0));
				break;
			default:
				LOG_ERROR("BUG: unknown lpc2000_info->variant encountered");
				exit(-1);
		}

		retval = target_write_memory(target,
				lpc2000_info->iap_working_area->address, 4, 2, jump_gate);
		if (retval != ERROR_OK) {
			LOG_ERROR(
				"Write memory at address 0x%8.8" PRIx32 " failed (check work_area definition)",
				lpc2000_info->iap_working_area->address);
			return retval;
		}
	}

	switch (lpc2000_info->variant) {
		case lpc1700:
			armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
			armv7m_info.core_mode = ARMV7M_MODE_ANY;
			iap_entry_point = 0x1fff1ff1;
			break;
		case lpc2000_v1:
		case lpc2000_v2:
			arm_algo.common_magic = ARM_COMMON_MAGIC;
			arm_algo.core_mode = ARM_MODE_SVC;
			arm_algo.core_state = ARM_STATE_ARM;
			iap_entry_point = 0x7ffffff1;
			break;
		default:
			LOG_ERROR("BUG: unknown lpc2000->variant encountered");
			exit(-1);
	}

	/* command parameter table */
	init_mem_param(&mem_params[0], lpc2000_info->iap_working_area->address + 8, 6 * 4,
		PARAM_OUT);
	target_buffer_set_u32(target, mem_params[0].value, code);
	target_buffer_set_u32(target, mem_params[0].value + 0x04, param_table[0]);
	target_buffer_set_u32(target, mem_params[0].value + 0x08, param_table[1]);
	target_buffer_set_u32(target, mem_params[0].value + 0x0c, param_table[2]);
	target_buffer_set_u32(target, mem_params[0].value + 0x10, param_table[3]);
	target_buffer_set_u32(target, mem_params[0].value + 0x14, param_table[4]);

	init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
	buf_set_u32(reg_params[0].value, 0, 32, lpc2000_info->iap_working_area->address + 0x08);

	/* command result table */
	init_mem_param(&mem_params[1],
		lpc2000_info->iap_working_area->address + 0x20,
		5 * 4,
		PARAM_IN);

	init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
	buf_set_u32(reg_params[1].value, 0, 32, lpc2000_info->iap_working_area->address + 0x20);

	/* IAP entry point */
	init_reg_param(&reg_params[2], "r12", 32, PARAM_OUT);
	buf_set_u32(reg_params[2].value, 0, 32, iap_entry_point);

	switch (lpc2000_info->variant) {
		case lpc1700:
			/* IAP stack */
			init_reg_param(&reg_params[3], "sp", 32, PARAM_OUT);
			buf_set_u32(reg_params[3].value, 0, 32,
					lpc2000_info->iap_working_area->address + 0xb4);

			/* return address */
			init_reg_param(&reg_params[4], "lr", 32, PARAM_OUT);
			buf_set_u32(reg_params[4].value, 0, 32,
					(lpc2000_info->iap_working_area->address + 0x04) | 1);
			/* bit0 of LR = 1 to return in Thumb mode */

			target_run_algorithm(target, 2, mem_params, 5, reg_params,
					lpc2000_info->iap_working_area->address, 0, 10000, &armv7m_info);
			break;
		case lpc2000_v1:
		case lpc2000_v2:
			/* IAP stack */
			init_reg_param(&reg_params[3], "sp_svc", 32, PARAM_OUT);
			buf_set_u32(reg_params[3].value, 0, 32,
					lpc2000_info->iap_working_area->address + 0xb4);

			/* return address */
			init_reg_param(&reg_params[4], "lr_svc", 32, PARAM_OUT);
			buf_set_u32(reg_params[4].value, 0, 32,
					lpc2000_info->iap_working_area->address + 0x04);

			target_run_algorithm(target, 2, mem_params, 5, reg_params,
					lpc2000_info->iap_working_area->address,
					lpc2000_info->iap_working_area->address + 0x4,
					10000, &arm_algo);
			break;
		default:
			LOG_ERROR("BUG: unknown lpc2000->variant encountered");
			exit(-1);
	}

	status_code = target_buffer_get_u32(target, mem_params[1].value);
	result_table[0] = target_buffer_get_u32(target, mem_params[1].value + 0x04);
	result_table[1] = target_buffer_get_u32(target, mem_params[1].value + 0x08);
	result_table[2] = target_buffer_get_u32(target, mem_params[1].value + 0x0c);
	result_table[3] = target_buffer_get_u32(target, mem_params[1].value + 0x10);

	LOG_DEBUG("IAP command = %i (0x%8.8" PRIx32 ", 0x%8.8" PRIx32
			", 0x%8.8" PRIx32 ", 0x%8.8" PRIx32 ", 0x%8.8"
			PRIx32 ") completed with result = %8.8" PRIx32,
			code, param_table[0], param_table[1], param_table[2],
			param_table[3], param_table[4], status_code);

	destroy_mem_param(&mem_params[0]);
	destroy_mem_param(&mem_params[1]);

	destroy_reg_param(&reg_params[0]);
	destroy_reg_param(&reg_params[1]);
	destroy_reg_param(&reg_params[2]);
	destroy_reg_param(&reg_params[3]);
	destroy_reg_param(&reg_params[4]);

	return status_code;
}
Exemple #10
0
static int lpc2000_iap_call(struct flash_bank *bank, struct working_area *iap_working_area, int code,
		uint32_t param_table[5], uint32_t result_table[4])
{
	struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv;
	struct target *target = bank->target;

	struct arm_algorithm arm_algo;	/* for LPC2000 */
	struct armv7m_algorithm armv7m_info;	/* for LPC1700 */
	uint32_t iap_entry_point = 0;	/* to make compiler happier */

	switch (lpc2000_info->variant) {
		case lpc800:
		case lpc1100:
		case lpc1700:
		case lpc_auto:
			armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
			armv7m_info.core_mode = ARM_MODE_THREAD;
			iap_entry_point = 0x1fff1ff1;
			break;
		case lpc2000_v1:
		case lpc2000_v2:
			arm_algo.common_magic = ARM_COMMON_MAGIC;
			arm_algo.core_mode = ARM_MODE_SVC;
			arm_algo.core_state = ARM_STATE_ARM;
			iap_entry_point = 0x7ffffff1;
			break;
		case lpc4300:
			armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
			armv7m_info.core_mode = ARM_MODE_THREAD;
			/* read out IAP entry point from ROM driver table at 0x10400100 */
			target_read_u32(target, 0x10400100, &iap_entry_point);
			break;
		default:
			LOG_ERROR("BUG: unknown lpc2000->variant encountered");
			exit(-1);
	}

	struct mem_param mem_params[2];

	/* command parameter table */
	init_mem_param(&mem_params[0], iap_working_area->address + 8, 6 * 4, PARAM_OUT);
	target_buffer_set_u32(target, mem_params[0].value, code);
	target_buffer_set_u32(target, mem_params[0].value + 0x04, param_table[0]);
	target_buffer_set_u32(target, mem_params[0].value + 0x08, param_table[1]);
	target_buffer_set_u32(target, mem_params[0].value + 0x0c, param_table[2]);
	target_buffer_set_u32(target, mem_params[0].value + 0x10, param_table[3]);
	target_buffer_set_u32(target, mem_params[0].value + 0x14, param_table[4]);

	struct reg_param reg_params[5];

	init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
	buf_set_u32(reg_params[0].value, 0, 32, iap_working_area->address + 0x08);

	/* command result table */
	init_mem_param(&mem_params[1], iap_working_area->address + 0x20, 5 * 4, PARAM_IN);

	init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
	buf_set_u32(reg_params[1].value, 0, 32, iap_working_area->address + 0x20);

	/* IAP entry point */
	init_reg_param(&reg_params[2], "r12", 32, PARAM_OUT);
	buf_set_u32(reg_params[2].value, 0, 32, iap_entry_point);

	switch (lpc2000_info->variant) {
		case lpc800:
		case lpc1100:
		case lpc1700:
		case lpc4300:
		case lpc_auto:
			/* IAP stack */
			init_reg_param(&reg_params[3], "sp", 32, PARAM_OUT);
			buf_set_u32(reg_params[3].value, 0, 32,
				iap_working_area->address + IAP_CODE_LEN + lpc2000_info->iap_max_stack);

			/* return address */
			init_reg_param(&reg_params[4], "lr", 32, PARAM_OUT);
			buf_set_u32(reg_params[4].value, 0, 32, (iap_working_area->address + 0x04) | 1);
			/* bit0 of LR = 1 to return in Thumb mode */

			target_run_algorithm(target, 2, mem_params, 5, reg_params, iap_working_area->address, 0, 10000,
					&armv7m_info);
			break;
		case lpc2000_v1:
		case lpc2000_v2:
			/* IAP stack */
			init_reg_param(&reg_params[3], "sp_svc", 32, PARAM_OUT);
			buf_set_u32(reg_params[3].value, 0, 32,
				iap_working_area->address + IAP_CODE_LEN + lpc2000_info->iap_max_stack);

			/* return address */
			init_reg_param(&reg_params[4], "lr_svc", 32, PARAM_OUT);
			buf_set_u32(reg_params[4].value, 0, 32, iap_working_area->address + 0x04);

			target_run_algorithm(target, 2, mem_params, 5, reg_params, iap_working_area->address,
					iap_working_area->address + 0x4, 10000, &arm_algo);
			break;
		default:
			LOG_ERROR("BUG: unknown lpc2000->variant encountered");
			exit(-1);
	}

	int status_code = target_buffer_get_u32(target, mem_params[1].value);
	result_table[0] = target_buffer_get_u32(target, mem_params[1].value + 0x04);
	result_table[1] = target_buffer_get_u32(target, mem_params[1].value + 0x08);
	result_table[2] = target_buffer_get_u32(target, mem_params[1].value + 0x0c);
	result_table[3] = target_buffer_get_u32(target, mem_params[1].value + 0x10);

	LOG_DEBUG("IAP command = %i (0x%8.8" PRIx32 ", 0x%8.8" PRIx32 ", 0x%8.8" PRIx32 ", 0x%8.8" PRIx32 ", 0x%8.8" PRIx32
			") completed with result = %8.8x",
			code, param_table[0], param_table[1], param_table[2], param_table[3], param_table[4], status_code);

	destroy_mem_param(&mem_params[0]);
	destroy_mem_param(&mem_params[1]);

	destroy_reg_param(&reg_params[0]);
	destroy_reg_param(&reg_params[1]);
	destroy_reg_param(&reg_params[2]);
	destroy_reg_param(&reg_params[3]);
	destroy_reg_param(&reg_params[4]);

	return status_code;
}