Beispiel #1
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;
}
Beispiel #2
0
uint32_t get_buffer(struct target *target, const uint8_t *buffer)
{
    uint32_t value = 0;
    const uint8_t *value_ptr = buffer;
    value = target_buffer_get_u32(target, value_ptr);
    return value;
}
Beispiel #3
0
int get_name(struct target *target, struct threads *t)
{
    int retval;
    uint32_t full_name[4];
    uint32_t comm = t->base_addr + COMM;
    int i;

    for (i = 0; i < 17; i++)
        t->name[i] = 0;

    retval = linux_read_memory(target, comm, 4, 4, (uint8_t *) full_name);

    if (retval != ERROR_OK) {
        LOG_ERROR("get_name: unable to read memory\n");
        return ERROR_FAIL;
    }

    uint32_t raw_name = target_buffer_get_u32(target,
                        (const uint8_t *)
                        &full_name[0]);
    t->name[3] = raw_name >> 24;
    t->name[2] = raw_name >> 16;
    t->name[1] = raw_name >> 8;
    t->name[0] = raw_name;
    raw_name =
        target_buffer_get_u32(target, (const uint8_t *)&full_name[1]);
    t->name[7] = raw_name >> 24;
    t->name[6] = raw_name >> 16;
    t->name[5] = raw_name >> 8;
    t->name[4] = raw_name;
    raw_name =
        target_buffer_get_u32(target, (const uint8_t *)&full_name[2]);
    t->name[11] = raw_name >> 24;
    t->name[10] = raw_name >> 16;
    t->name[9] = raw_name >> 8;
    t->name[8] = raw_name;
    raw_name =
        target_buffer_get_u32(target, (const uint8_t *)&full_name[3]);
    t->name[15] = raw_name >> 24;
    t->name[14] = raw_name >> 16;
    t->name[13] = raw_name >> 8;
    t->name[12] = raw_name;
    return ERROR_OK;

}
Beispiel #4
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;
}
Beispiel #5
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;
}
Beispiel #6
0
static int xmc4xxx_write_page(struct flash_bank *bank, const uint8_t *pg_buf,
			      uint32_t offset, bool user_config)
{
	int res;
	uint32_t status;

	/* Base of the flash write command */
	struct xmc4xxx_command_seq write_cmd_seq[4] = {
		{FLASH_CMD_WRITE_PAGE_1, 0xAA},
		{FLASH_CMD_WRITE_PAGE_2, 0x55},
		{FLASH_CMD_WRITE_PAGE_3, 0xFF}, /* Needs filled in */
		{0xFF,                   0xFF}	/* Needs filled in */
	};

	/* The command sequence differs depending on whether this is
	 * being written to standard flash or the user configuration
	 * area */
	if (user_config)
		write_cmd_seq[2].magic = 0xC0;
	else
		write_cmd_seq[2].magic = 0xA0;

	/* Finally, we need to add the address that this page will be
	 * written to */
	write_cmd_seq[3].address = bank->base + offset;
	write_cmd_seq[3].magic = 0xAA;


	/* Flash pages are written 256 bytes at a time.  For each 256
	 * byte chunk, we need to:
	 * 1. Enter page mode. This activates the flash write buffer
	 * 2. Load the page buffer with data (2x 32 bit words at a time)
	 * 3. Burn the page buffer into its intended location
	 * If the starting offset is not on a 256 byte boundary, we
	 * will need to pad the beginning of the write buffer
	 * accordingly. Likewise, if the last page does not fill the
	 * buffer, we should pad it to avoid leftover data from being
	 * written to flash
	 */
	res = xmc4xxx_enter_page_mode(bank);
	if (res != ERROR_OK)
		return res;

	/* Copy the data into the page buffer*/
	for (int i = 0; i < 256; i += 8) {
		uint32_t w_lo = target_buffer_get_u32(bank->target, &pg_buf[i]);
		uint32_t w_hi = target_buffer_get_u32(bank->target, &pg_buf[i + 4]);
		LOG_DEBUG("WLO: %08"PRIx32, w_lo);
		LOG_DEBUG("WHI: %08"PRIx32, w_hi);

		/* Data is loaded 2x 32 bit words at a time */
		res = target_write_u32(bank->target, FLASH_CMD_LOAD_PAGE_1, w_lo);
		if (res != ERROR_OK)
			return res;

		res = target_write_u32(bank->target, FLASH_CMD_LOAD_PAGE_2, w_hi);
		if (res != ERROR_OK)
			return res;

		/* Check for an error */
		res = xmc4xxx_get_flash_status(bank, &status);
		if (res != ERROR_OK)
			return res;

		if (status & FSR_SQER_MASK) {
			LOG_ERROR("Error loading page buffer");
			return ERROR_FAIL;
		}
	}

	/* The page buffer is now full, time to commit it to flash */

	res = xmc4xxx_write_command_sequence(bank, write_cmd_seq, ARRAY_SIZE(write_cmd_seq));
	if (res != ERROR_OK) {
		LOG_ERROR("Unable to enter write command sequence");
		return res;
	}

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

	/* Check for a sequence error */
	if (status & FSR_SQER_MASK) {
		LOG_ERROR("Error with flash write sequence");
		return ERROR_FAIL;
	}

	/* Make sure a flash write was triggered */
	if (!(status & FSR_PROG_MASK)) {
		LOG_ERROR("Failed to write flash page");
		return ERROR_FAIL;
	}

	/* Wait for the write operation to end */
	res = xmc4xxx_wait_status_busy(bank, FLASH_OP_TIMEOUT);
	if (res != ERROR_OK)
		return res;

	/* TODO: Verify that page was written without error */
	return res;
}
Beispiel #7
0
struct cpu_context *cpu_context_read(struct target *target, uint32_t base_addr,
                                     uint32_t *thread_info_addr_old)
{
    struct cpu_context *context = calloc(1, sizeof(struct cpu_context));
    uint32_t preempt_count_addr = 0;
    uint32_t registers[10];
    uint8_t *buffer = calloc(1, 4);
    uint32_t stack = base_addr + QAT;
    uint32_t thread_info_addr = 0;
    uint32_t thread_info_addr_update = 0;
    int retval = ERROR_FAIL;
    context->R4 = 0xdeadbeef;
    context->R5 = 0xdeadbeef;
    context->R6 = 0xdeadbeef;
    context->R7 = 0xdeadbeef;
    context->R8 = 0xdeadbeef;
    context->R9 = 0xdeadbeef;
    context->IP = 0xdeadbeef;
    context->FP = 0xdeadbeef;
    context->SP = 0xdeadbeef;
    context->PC = 0xdeadbeef;
retry:

    if (*thread_info_addr_old == 0xdeadbeef) {
        retval = fill_buffer(target, stack, buffer);

        if (retval == ERROR_OK)
            thread_info_addr = get_buffer(target, buffer);
        else
            LOG_ERROR("cpu_context: unable to read memory");

        thread_info_addr_update = thread_info_addr;
    } else
        thread_info_addr = *thread_info_addr_old;

    preempt_count_addr = thread_info_addr + PREEMPT;
    retval = fill_buffer(target, preempt_count_addr, buffer);

    if (retval == ERROR_OK)
        context->preempt_count = get_buffer(target, buffer);
    else {
        if (*thread_info_addr_old != 0xdeadbeef) {
            LOG_ERROR
            ("cpu_context: cannot read at thread_info_addr");

            if (*thread_info_addr_old < LINUX_USER_KERNEL_BORDER)
                LOG_INFO
                ("cpu_context : thread_info_addr in userspace!!!");

            *thread_info_addr_old = 0xdeadbeef;
            goto retry;
        }

        LOG_ERROR("cpu_context: unable to read memory");
    }

    thread_info_addr += CPU_CONT;

    retval = linux_read_memory(target, thread_info_addr, 4, 10,
                               (uint8_t *) registers);

    if (retval != ERROR_OK) {
        free(buffer);
        LOG_ERROR("cpu_context: unable to read memory\n");
        return context;
    }

    context->R4 =
        target_buffer_get_u32(target, (const uint8_t *)&registers[0]);
    context->R5 =
        target_buffer_get_u32(target, (const uint8_t *)&registers[1]);
    context->R6 =
        target_buffer_get_u32(target, (const uint8_t *)&registers[2]);
    context->R7 =
        target_buffer_get_u32(target, (const uint8_t *)&registers[3]);
    context->R8 =
        target_buffer_get_u32(target, (const uint8_t *)&registers[4]);
    context->R9 =
        target_buffer_get_u32(target, (const uint8_t *)&registers[5]);
    context->IP =
        target_buffer_get_u32(target, (const uint8_t *)&registers[6]);
    context->FP =
        target_buffer_get_u32(target, (const uint8_t *)&registers[7]);
    context->SP =
        target_buffer_get_u32(target, (const uint8_t *)&registers[8]);
    context->PC =
        target_buffer_get_u32(target, (const uint8_t *)&registers[9]);

    if (*thread_info_addr_old == 0xdeadbeef)
        *thread_info_addr_old = thread_info_addr_update;

    free(buffer);

    return context;
}
Beispiel #8
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;
}
Beispiel #9
0
/**
 * Check for and process a semihosting request using the ARM protocol). This
 * is meant to be called when the target is stopped due to a debug mode entry.
 * If the value 0 is returned then there was nothing to process. A non-zero
 * return value signifies that a request was processed and the target resumed,
 * or an error was encountered, in which case the caller must return
 * immediately.
 *
 * @param target Pointer to the target to process.
 * @param retval Pointer to a location where the return code will be stored
 * @return non-zero value if a request was processed or an error encountered
 */
int riscv_semihosting(struct target *target, int *retval)
{
	struct semihosting *semihosting = target->semihosting;
	if (!semihosting)
		return 0;

	if (!semihosting->is_active)
		return 0;

	riscv_reg_t dpc;
	int result = riscv_get_register(target, &dpc, GDB_REGNO_DPC);
	if (result != ERROR_OK)
		return 0;

	uint8_t tmp[12];

	/* Read the current instruction, including the bracketing */
	*retval = target_read_memory(target, dpc - 4, 2, 6, tmp);
	if (*retval != ERROR_OK)
		return 0;

	/*
	 * The instructions that trigger a semihosting call,
	 * always uncompressed, should look like:
	 *
	 * 01f01013              slli    zero,zero,0x1f
	 * 00100073              ebreak
	 * 40705013              srai    zero,zero,0x7
	 */
	uint32_t pre = target_buffer_get_u32(target, tmp);
	uint32_t ebreak = target_buffer_get_u32(target, tmp + 4);
	uint32_t post = target_buffer_get_u32(target, tmp + 8);
	LOG_DEBUG("check %08x %08x %08x from 0x%" PRIx64 "-4", pre, ebreak, post, dpc);

	if (pre != 0x01f01013 || ebreak != 0x00100073 || post != 0x40705013) {

		/* Not the magic sequence defining semihosting. */
		return 0;
	}

	/*
	 * Perform semihosting call if we are not waiting on a fileio
	 * operation to complete.
	 */
	if (!semihosting->hit_fileio) {

		/* RISC-V uses A0 and A1 to pass function arguments */
		riscv_reg_t r0;
		riscv_reg_t r1;

		result = riscv_get_register(target, &r0, GDB_REGNO_A0);
		if (result != ERROR_OK)
			return 0;

		result = riscv_get_register(target, &r1, GDB_REGNO_A1);
		if (result != ERROR_OK)
			return 0;

		semihosting->op = r0;
		semihosting->param = r1;
		semihosting->word_size_bytes = riscv_xlen(target) / 8;

		/* Check for ARM operation numbers. */
		if (0 <= semihosting->op && semihosting->op <= 0x31) {
			*retval = semihosting_common(target);
			if (*retval != ERROR_OK) {
				LOG_ERROR("Failed semihosting operation");
				return 0;
			}
		} else {
			/* Unknown operation number, not a semihosting call. */
			return 0;
		}
	}

	/*
	 * Resume target if we are not waiting on a fileio
	 * operation to complete.
	 */
	if (semihosting->is_resumable && !semihosting->hit_fileio) {
		/* Resume right after the EBREAK 4 bytes instruction. */
		*retval = target_resume(target, 0, dpc+4, 0, 0);
		if (*retval != ERROR_OK) {
			LOG_ERROR("Failed to resume target");
			return 0;
		}

		return 1;
	}

	return 0;
}
Beispiel #10
0
static int do_semihosting(struct target *target)
{
	struct arm *arm = target_to_arm(target);
	uint32_t r0 = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32);
	uint32_t r1 = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32);
	uint8_t params[16];
	int retval, result;

	/*
	 * TODO: lots of security issues are not considered yet, such as:
	 * - no validation on target provided file descriptors
	 * - no safety checks on opened/deleted/renamed file paths
	 * Beware the target app you use this support with.
	 *
	 * TODO: explore mapping requests to GDB's "File-I/O Remote
	 * Protocol Extension" ... when GDB is active.
	 */
	switch (r0) {
	case 0x01:	/* SYS_OPEN */
		retval = target_read_memory(target, r1, 4, 3, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			uint32_t a = target_buffer_get_u32(target, params+0);
			uint32_t m = target_buffer_get_u32(target, params+4);
			uint32_t l = target_buffer_get_u32(target, params+8);
			if (l <= 255 && m <= 11) {
				uint8_t fn[256];
				retval = target_read_memory(target, a, 1, l, fn);
				if (retval != ERROR_OK)
					return retval;
				fn[l] = 0;
				if (strcmp((char *)fn, ":tt") == 0) {
					if (m < 4)
						result = dup(STDIN_FILENO);
					else
						result = dup(STDOUT_FILENO);
				} else {
					/* cygwin requires the permission setting
					 * otherwise it will fail to reopen a previously
					 * written file */
					result = open((char *)fn, open_modeflags[m], 0644);
				}
				arm->semihosting_errno =  errno;
			} else {
				result = -1;
				arm->semihosting_errno = EINVAL;
			}
		}
		break;

	case 0x02:	/* SYS_CLOSE */
		retval = target_read_memory(target, r1, 4, 1, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			int fd = target_buffer_get_u32(target, params+0);
			result = close(fd);
			arm->semihosting_errno = errno;
		}
		break;

	case 0x03:	/* SYS_WRITEC */
		{
			unsigned char c;
			retval = target_read_memory(target, r1, 1, 1, &c);
			if (retval != ERROR_OK)
				return retval;
			putchar(c);
			result = 0;
		}
		break;

	case 0x04:	/* SYS_WRITE0 */
		do {
			unsigned char c;
			retval = target_read_memory(target, r1++, 1, 1, &c);
			if (retval != ERROR_OK)
				return retval;
			if (!c)
				break;
			putchar(c);
		} while (1);
		result = 0;
		break;

	case 0x05:	/* SYS_WRITE */
		retval = target_read_memory(target, r1, 4, 3, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			int fd = target_buffer_get_u32(target, params+0);
			uint32_t a = target_buffer_get_u32(target, params+4);
			size_t l = target_buffer_get_u32(target, params+8);
			uint8_t *buf = malloc(l);
			if (!buf) {
				result = -1;
				arm->semihosting_errno = ENOMEM;
			} else {
				retval = target_read_buffer(target, a, l, buf);
				if (retval != ERROR_OK) {
					free(buf);
					return retval;
				}
				result = write(fd, buf, l);
				arm->semihosting_errno = errno;
				if (result >= 0)
					result = l - result;
				free(buf);
			}
		}
		break;

	case 0x06:	/* SYS_READ */
		retval = target_read_memory(target, r1, 4, 3, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			int fd = target_buffer_get_u32(target, params+0);
			uint32_t a = target_buffer_get_u32(target, params+4);
			ssize_t l = target_buffer_get_u32(target, params+8);
			uint8_t *buf = malloc(l);
			if (!buf) {
				result = -1;
				arm->semihosting_errno = ENOMEM;
			} else {
				result = read(fd, buf, l);
				arm->semihosting_errno = errno;
				if (result >= 0) {
					retval = target_write_buffer(target, a, result, buf);
					if (retval != ERROR_OK) {
						free(buf);
						return retval;
					}
					result = l - result;
				}
				free(buf);
			}
		}
		break;

	case 0x07:	/* SYS_READC */
		result = getchar();
		break;

	case 0x08:	/* SYS_ISERROR */
		retval = target_read_memory(target, r1, 4, 1, params);
		if (retval != ERROR_OK)
			return retval;
		result = (target_buffer_get_u32(target, params+0) != 0);
		break;

	case 0x09:	/* SYS_ISTTY */
		retval = target_read_memory(target, r1, 4, 1, params);
		if (retval != ERROR_OK)
			return retval;
		result = isatty(target_buffer_get_u32(target, params+0));
		break;

	case 0x0a:	/* SYS_SEEK */
		retval = target_read_memory(target, r1, 4, 2, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			int fd = target_buffer_get_u32(target, params+0);
			off_t pos = target_buffer_get_u32(target, params+4);
			result = lseek(fd, pos, SEEK_SET);
			arm->semihosting_errno = errno;
			if (result == pos)
				result = 0;
		}
		break;

	case 0x0c:	/* SYS_FLEN */
		retval = target_read_memory(target, r1, 4, 1, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			int fd = target_buffer_get_u32(target, params+0);
			struct stat buf;
			result = fstat(fd, &buf);
			if (result == -1) {
				arm->semihosting_errno = errno;
				result = -1;
				break;
			}
			result = buf.st_size;
		}
		break;

	case 0x0e:	/* SYS_REMOVE */
		retval = target_read_memory(target, r1, 4, 2, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			uint32_t a = target_buffer_get_u32(target, params+0);
			uint32_t l = target_buffer_get_u32(target, params+4);
			if (l <= 255) {
				uint8_t fn[256];
				retval = target_read_memory(target, a, 1, l, fn);
				if (retval != ERROR_OK)
					return retval;
				fn[l] = 0;
				result = remove((char *)fn);
				arm->semihosting_errno =  errno;
			} else {
				result = -1;
				arm->semihosting_errno = EINVAL;
			}
		}
		break;

	case 0x0f:	/* SYS_RENAME */
		retval = target_read_memory(target, r1, 4, 4, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			uint32_t a1 = target_buffer_get_u32(target, params+0);
			uint32_t l1 = target_buffer_get_u32(target, params+4);
			uint32_t a2 = target_buffer_get_u32(target, params+8);
			uint32_t l2 = target_buffer_get_u32(target, params+12);
			if (l1 <= 255 && l2 <= 255) {
				uint8_t fn1[256], fn2[256];
				retval = target_read_memory(target, a1, 1, l1, fn1);
				if (retval != ERROR_OK)
					return retval;
				retval = target_read_memory(target, a2, 1, l2, fn2);
				if (retval != ERROR_OK)
					return retval;
				fn1[l1] = 0;
				fn2[l2] = 0;
				result = rename((char *)fn1, (char *)fn2);
				arm->semihosting_errno =  errno;
			} else {
				result = -1;
				arm->semihosting_errno = EINVAL;
			}
		}
		break;

	case 0x11:	/* SYS_TIME */
		result = time(NULL);
		break;

	case 0x13:	/* SYS_ERRNO */
		result = arm->semihosting_errno;
		break;

	case 0x15:	/* SYS_GET_CMDLINE */
		retval = target_read_memory(target, r1, 4, 2, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			uint32_t a = target_buffer_get_u32(target, params+0);
			uint32_t l = target_buffer_get_u32(target, params+4);
			char *arg = "foobar";
			uint32_t s = strlen(arg) + 1;
			if (l < s)
				result = -1;
			else {
				retval = target_write_buffer(target, a, s, (uint8_t *)arg);
				if (retval != ERROR_OK)
					return retval;
				result = 0;
			}
		}
		break;

	case 0x16:	/* SYS_HEAPINFO */
		retval = target_read_memory(target, r1, 4, 1, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			uint32_t a = target_buffer_get_u32(target, params+0);
			/* tell the remote we have no idea */
			memset(params, 0, 4*4);
			retval = target_write_memory(target, a, 4, 4, params);
			if (retval != ERROR_OK)
				return retval;
			result = 0;
		}
		break;

	case 0x18:	/* angel_SWIreason_ReportException */
		switch (r1) {
		case 0x20026:	/* ADP_Stopped_ApplicationExit */
			fprintf(stderr, "semihosting: *** application exited ***\n");
			break;
		case 0x20000:	/* ADP_Stopped_BranchThroughZero */
		case 0x20001:	/* ADP_Stopped_UndefinedInstr */
		case 0x20002:	/* ADP_Stopped_SoftwareInterrupt */
		case 0x20003:	/* ADP_Stopped_PrefetchAbort */
		case 0x20004:	/* ADP_Stopped_DataAbort */
		case 0x20005:	/* ADP_Stopped_AddressException */
		case 0x20006:	/* ADP_Stopped_IRQ */
		case 0x20007:	/* ADP_Stopped_FIQ */
		case 0x20020:	/* ADP_Stopped_BreakPoint */
		case 0x20021:	/* ADP_Stopped_WatchPoint */
		case 0x20022:	/* ADP_Stopped_StepComplete */
		case 0x20023:	/* ADP_Stopped_RunTimeErrorUnknown */
		case 0x20024:	/* ADP_Stopped_InternalError */
		case 0x20025:	/* ADP_Stopped_UserInterruption */
		case 0x20027:	/* ADP_Stopped_StackOverflow */
		case 0x20028:	/* ADP_Stopped_DivisionByZero */
		case 0x20029:	/* ADP_Stopped_OSSpecific */
		default:
			fprintf(stderr, "semihosting: exception %#x\n",
					(unsigned) r1);
		}
		return target_call_event_callbacks(target, TARGET_EVENT_HALTED);

	case 0x12:	/* SYS_SYSTEM */
		/* Provide SYS_SYSTEM functionality.  Uses the
		 * libc system command, there may be a reason *NOT*
		 * to use this, but as I can't think of one, I
		 * implemented it this way.
		 */
		retval = target_read_memory(target, r1, 4, 2, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			uint32_t len = target_buffer_get_u32(target, params+4);
			uint32_t c_ptr = target_buffer_get_u32(target, params);
			uint8_t cmd[256];
			if (len > 255) {
				result = -1;
				arm->semihosting_errno = EINVAL;
			} else {
				memset(cmd, 0x0, 256);
				retval = target_read_memory(target, c_ptr, 1, len, cmd);
				if (retval != ERROR_OK)
					return retval;
				else
					result = system((const char *)cmd);
			}
		}
		break;
	case 0x0d:	/* SYS_TMPNAM */
	case 0x10:	/* SYS_CLOCK */
	case 0x17:	/* angel_SWIreason_EnterSVC */
	case 0x30:	/* SYS_ELAPSED */
	case 0x31:	/* SYS_TICKFREQ */
	default:
		fprintf(stderr, "semihosting: unsupported call %#x\n",
				(unsigned) r0);
		result = -1;
		arm->semihosting_errno = ENOTSUP;
	}

	/* resume execution to the original mode */

	/* REVISIT this looks wrong ... ARM11 and Cortex-A8
	 * should work this way at least sometimes.
	 */
	if (is_arm7_9(target_to_arm7_9(target))) {
		uint32_t spsr;

		/* return value in R0 */
		buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, result);
		arm->core_cache->reg_list[0].dirty = 1;

		/* LR --> PC */
		buf_set_u32(arm->core_cache->reg_list[15].value, 0, 32,
			buf_get_u32(arm_reg_current(arm, 14)->value, 0, 32));
		arm->core_cache->reg_list[15].dirty = 1;

		/* saved PSR --> current PSR */
		spsr = buf_get_u32(arm->spsr->value, 0, 32);

		/* REVISIT should this be arm_set_cpsr(arm, spsr)
		 * instead of a partially unrolled version?
		 */

		buf_set_u32(arm->cpsr->value, 0, 32, spsr);
		arm->cpsr->dirty = 1;
		arm->core_mode = spsr & 0x1f;
		if (spsr & 0x20)
			arm->core_state = ARM_STATE_THUMB;

	} else {
		/* resume execution, this will be pc+2 to skip over the
		 * bkpt instruction */

		/* return result in R0 */
		buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, result);
		arm->core_cache->reg_list[0].dirty = 1;
	}

	return target_resume(target, 1, 0, 0, 0);
}
Beispiel #11
0
/**
 * Checks for and processes an ARM semihosting request.  This is meant
 * to be called when the target is stopped due to a debug mode entry.
 * If the value 0 is returned then there was nothing to process. A non-zero
 * return value signifies that a request was processed and the target resumed,
 * or an error was encountered, in which case the caller must return
 * immediately.
 *
 * @param target Pointer to the ARM target to process.  This target must
 *	not represent an ARMv6-M or ARMv7-M processor.
 * @param retval Pointer to a location where the return code will be stored
 * @return non-zero value if a request was processed or an error encountered
 */
int arm_semihosting(struct target *target, int *retval)
{
	struct arm *arm = target_to_arm(target);
	uint32_t pc, lr, spsr;
	struct reg *r;

	if (!arm->is_semihosting)
		return 0;

	if (is_arm7_9(target_to_arm7_9(target))) {
		if (arm->core_mode != ARM_MODE_SVC)
			return 0;

		/* Check for PC == 0x00000008 or 0xffff0008: Supervisor Call vector. */
		r = arm->pc;
		pc = buf_get_u32(r->value, 0, 32);
		if (pc != 0x00000008 && pc != 0xffff0008)
			return 0;

		r = arm_reg_current(arm, 14);
		lr = buf_get_u32(r->value, 0, 32);

		/* Core-specific code should make sure SPSR is retrieved
		 * when the above checks pass...
		 */
		if (!arm->spsr->valid) {
			LOG_ERROR("SPSR not valid!");
			*retval = ERROR_FAIL;
			return 1;
		}

		spsr = buf_get_u32(arm->spsr->value, 0, 32);

		/* check instruction that triggered this trap */
		if (spsr & (1 << 5)) {
			/* was in Thumb (or ThumbEE) mode */
			uint8_t insn_buf[2];
			uint16_t insn;

			*retval = target_read_memory(target, lr-2, 2, 1, insn_buf);
			if (*retval != ERROR_OK)
				return 1;
			insn = target_buffer_get_u16(target, insn_buf);

			/* SVC 0xab */
			if (insn != 0xDFAB)
				return 0;
		} else if (spsr & (1 << 24)) {
			/* was in Jazelle mode */
			return 0;
		} else {
			/* was in ARM mode */
			uint8_t insn_buf[4];
			uint32_t insn;

			*retval = target_read_memory(target, lr-4, 4, 1, insn_buf);
			if (*retval != ERROR_OK)
				return 1;
			insn = target_buffer_get_u32(target, insn_buf);

			/* SVC 0x123456 */
			if (insn != 0xEF123456)
				return 0;
		}
	} else if (is_armv7m(target_to_armv7m(target))) {
		uint16_t insn;

		if (target->debug_reason != DBG_REASON_BREAKPOINT)
			return 0;

		r = arm->pc;
		pc = buf_get_u32(r->value, 0, 32);

		pc &= ~1;
		*retval = target_read_u16(target, pc, &insn);
		if (*retval != ERROR_OK)
			return 1;

		/* bkpt 0xAB */
		if (insn != 0xBEAB)
			return 0;
	} else {
		LOG_ERROR("Unsupported semi-hosting Target");
		return 0;
	}

	*retval = do_semihosting(target);
	return 1;
}
Beispiel #12
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;
}
/**
 * Checks for and processes an ARM semihosting request.  This is meant
 * to be called when the target is stopped due to a debug mode entry.
 * If the value 0 is returned then there was nothing to process. A non-zero
 * return value signifies that a request was processed and the target resumed,
 * or an error was encountered, in which case the caller must return
 * immediately.
 *
 * @param target Pointer to the ARM target to process.  This target must
 *	not represent an ARMv6-M or ARMv7-M processor.
 * @param retval Pointer to a location where the return code will be stored
 * @return non-zero value if a request was processed or an error encountered
 */
int arm_semihosting(struct target *target, int *retval)
{
	struct arm *arm = target_to_arm(target);
	struct armv7a_common *armv7a = target_to_armv7a(target);
	uint32_t pc, lr, spsr;
	struct reg *r;

	if (!arm->is_semihosting)
		return 0;

	if (is_arm7_9(target_to_arm7_9(target)) ||
	    is_armv7a(armv7a)) {
		uint32_t vbar = 0x00000000;

		if (arm->core_mode != ARM_MODE_SVC)
			return 0;

		if (is_armv7a(armv7a)) {
			struct arm_dpm *dpm = armv7a->arm.dpm;

			*retval = dpm->prepare(dpm);
			if (*retval == ERROR_OK) {
				*retval = dpm->instr_read_data_r0(dpm,
								 ARMV4_5_MRC(15, 0, 0, 12, 0, 0),
								 &vbar);

				dpm->finish(dpm);

				if (*retval != ERROR_OK)
					return 1;
			} else {
				return 1;
			}
		}

		/* Check for PC == 0x00000008 or 0xffff0008: Supervisor Call vector. */
		r = arm->pc;
		pc = buf_get_u32(r->value, 0, 32);
		if (pc != (vbar + 0x00000008) && pc != 0xffff0008)
			return 0;

		r = arm_reg_current(arm, 14);
		lr = buf_get_u32(r->value, 0, 32);

		/* Core-specific code should make sure SPSR is retrieved
		 * when the above checks pass...
		 */
		if (!arm->spsr->valid) {
			LOG_ERROR("SPSR not valid!");
			*retval = ERROR_FAIL;
			return 1;
		}

		spsr = buf_get_u32(arm->spsr->value, 0, 32);

		/* check instruction that triggered this trap */
		if (spsr & (1 << 5)) {
			/* was in Thumb (or ThumbEE) mode */
			uint8_t insn_buf[2];
			uint16_t insn;

			*retval = target_read_memory(target, lr-2, 2, 1, insn_buf);
			if (*retval != ERROR_OK)
				return 1;
			insn = target_buffer_get_u16(target, insn_buf);

			/* SVC 0xab */
			if (insn != 0xDFAB)
				return 0;
		} else if (spsr & (1 << 24)) {
			/* was in Jazelle mode */
			return 0;
		} else {
			/* was in ARM mode */
			uint8_t insn_buf[4];
			uint32_t insn;

			*retval = target_read_memory(target, lr-4, 4, 1, insn_buf);
			if (*retval != ERROR_OK)
				return 1;
			insn = target_buffer_get_u32(target, insn_buf);

			/* SVC 0x123456 */
			if (insn != 0xEF123456)
				return 0;
		}
	} else if (is_armv7m(target_to_armv7m(target))) {
		uint16_t insn;

		if (target->debug_reason != DBG_REASON_BREAKPOINT)
			return 0;

		r = arm->pc;
		pc = buf_get_u32(r->value, 0, 32);

		pc &= ~1;
		*retval = target_read_u16(target, pc, &insn);
		if (*retval != ERROR_OK)
			return 1;

		/* bkpt 0xAB */
		if (insn != 0xBEAB)
			return 0;
	} else {
		LOG_ERROR("Unsupported semi-hosting Target");
		return 0;
	}

	/* Perform semihosting if we are not waiting on a fileio
	 * operation to complete.
	 */
	if (!arm->semihosting_hit_fileio) {
		*retval = do_semihosting(target);
		if (*retval != ERROR_OK) {
			LOG_ERROR("Failed semihosting operation");
			return 0;
		}
	}

	/* Post result to target if we are not waiting on a fileio
	 * operation to complete:
	 */
	if (!arm->semihosting_hit_fileio) {
		*retval = post_result(target);
		if (*retval != ERROR_OK) {
			LOG_ERROR("Failed to post semihosting result");
			return 0;
		}

		*retval = target_resume(target, 1, 0, 0, 0);
		if (*retval != ERROR_OK) {
			LOG_ERROR("Failed to resume target");
			return 0;
		}

		return 1;
	}

	return 0;
}
static int do_semihosting(struct target *target)
{
	struct arm *arm = target_to_arm(target);
	struct gdb_fileio_info *fileio_info = target->fileio_info;
	uint32_t r0 = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32);
	uint32_t r1 = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32);
	uint8_t params[16];
	int retval;

	/*
	 * TODO: lots of security issues are not considered yet, such as:
	 * - no validation on target provided file descriptors
	 * - no safety checks on opened/deleted/renamed file paths
	 * Beware the target app you use this support with.
	 *
	 * TODO: unsupported semihosting fileio operations could be
	 * implemented if we had a small working area at our disposal.
	 */
	switch ((arm->semihosting_op = r0)) {
	case 0x01:	/* SYS_OPEN */
		retval = target_read_memory(target, r1, 4, 3, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			uint32_t a = target_buffer_get_u32(target, params+0);
			uint32_t m = target_buffer_get_u32(target, params+4);
			uint32_t l = target_buffer_get_u32(target, params+8);
			uint8_t fn[256];
			retval = target_read_memory(target, a, 1, l, fn);
			if (retval != ERROR_OK)
				return retval;
			fn[l] = 0;
			if (arm->is_semihosting_fileio) {
				if (strcmp((char *)fn, ":tt") == 0)
					arm->semihosting_result = 0;
				else {
					arm->semihosting_hit_fileio = true;
					fileio_info->identifier = "open";
					fileio_info->param_1 = a;
					fileio_info->param_2 = l;
					fileio_info->param_3 = open_modeflags[m];
					fileio_info->param_4 = 0644;
				}
			} else {
				if (l <= 255 && m <= 11) {
					if (strcmp((char *)fn, ":tt") == 0) {
						if (m < 4)
							arm->semihosting_result = dup(STDIN_FILENO);
						else
							arm->semihosting_result = dup(STDOUT_FILENO);
					} else {
						/* cygwin requires the permission setting
						 * otherwise it will fail to reopen a previously
						 * written file */
						arm->semihosting_result = open((char *)fn, open_modeflags[m], 0644);
					}
					arm->semihosting_errno =  errno;
				} else {
					arm->semihosting_result = -1;
					arm->semihosting_errno = EINVAL;
				}
			}
		}
		break;

	case 0x02:	/* SYS_CLOSE */
		retval = target_read_memory(target, r1, 4, 1, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			int fd = target_buffer_get_u32(target, params+0);
			if (arm->is_semihosting_fileio) {
				arm->semihosting_hit_fileio = true;
				fileio_info->identifier = "close";
				fileio_info->param_1 = fd;
			} else {
				arm->semihosting_result = close(fd);
				arm->semihosting_errno = errno;
			}
		}
		break;

	case 0x03:	/* SYS_WRITEC */
		if (arm->is_semihosting_fileio) {
			arm->semihosting_hit_fileio = true;
			fileio_info->identifier = "write";
			fileio_info->param_1 = 1;
			fileio_info->param_2 = r1;
			fileio_info->param_3 = 1;
		} else {
			unsigned char c;
			retval = target_read_memory(target, r1, 1, 1, &c);
			if (retval != ERROR_OK)
				return retval;
			putchar(c);
			arm->semihosting_result = 0;
		}
		break;

	case 0x04:	/* SYS_WRITE0 */
		if (arm->is_semihosting_fileio) {
			size_t count = 0;
			for (uint32_t a = r1;; a++) {
				unsigned char c;
				retval = target_read_memory(target, a, 1, 1, &c);
				if (retval != ERROR_OK)
					return retval;
				if (c == '\0')
					break;
				count++;
			}
			arm->semihosting_hit_fileio = true;
			fileio_info->identifier = "write";
			fileio_info->param_1 = 1;
			fileio_info->param_2 = r1;
			fileio_info->param_3 = count;
		} else {
			do {
				unsigned char c;
				retval = target_read_memory(target, r1++, 1, 1, &c);
				if (retval != ERROR_OK)
					return retval;
				if (!c)
					break;
				putchar(c);
			} while (1);
			arm->semihosting_result = 0;
		}
		break;

	case 0x05:	/* SYS_WRITE */
		retval = target_read_memory(target, r1, 4, 3, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			int fd = target_buffer_get_u32(target, params+0);
			uint32_t a = target_buffer_get_u32(target, params+4);
			size_t l = target_buffer_get_u32(target, params+8);
			if (arm->is_semihosting_fileio) {
				arm->semihosting_hit_fileio = true;
				fileio_info->identifier = "write";
				fileio_info->param_1 = fd;
				fileio_info->param_2 = a;
				fileio_info->param_3 = l;
			} else {
				uint8_t *buf = malloc(l);
				if (!buf) {
					arm->semihosting_result = -1;
					arm->semihosting_errno = ENOMEM;
				} else {
					retval = target_read_buffer(target, a, l, buf);
					if (retval != ERROR_OK) {
						free(buf);
						return retval;
					}
					arm->semihosting_result = write(fd, buf, l);
					arm->semihosting_errno = errno;
					if (arm->semihosting_result >= 0)
						arm->semihosting_result = l - arm->semihosting_result;
					free(buf);
				}
			}
		}
		break;

	case 0x06:	/* SYS_READ */
		retval = target_read_memory(target, r1, 4, 3, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			int fd = target_buffer_get_u32(target, params+0);
			uint32_t a = target_buffer_get_u32(target, params+4);
			ssize_t l = target_buffer_get_u32(target, params+8);
			if (arm->is_semihosting_fileio) {
				arm->semihosting_hit_fileio = true;
				fileio_info->identifier = "read";
				fileio_info->param_1 = fd;
				fileio_info->param_2 = a;
				fileio_info->param_3 = l;
			} else {
				uint8_t *buf = malloc(l);
				if (!buf) {
					arm->semihosting_result = -1;
					arm->semihosting_errno = ENOMEM;
				} else {
					arm->semihosting_result = read(fd, buf, l);
					arm->semihosting_errno = errno;
					if (arm->semihosting_result >= 0) {
						retval = target_write_buffer(target, a, arm->semihosting_result, buf);
						if (retval != ERROR_OK) {
							free(buf);
							return retval;
						}
						arm->semihosting_result = l - arm->semihosting_result;
					}
					free(buf);
				}
			}
		}
		break;

	case 0x07:	/* SYS_READC */
		if (arm->is_semihosting_fileio) {
			LOG_ERROR("SYS_READC not supported by semihosting fileio");
			return ERROR_FAIL;
		}
		arm->semihosting_result = getchar();
		break;

	case 0x08:	/* SYS_ISERROR */
		retval = target_read_memory(target, r1, 4, 1, params);
		if (retval != ERROR_OK)
			return retval;
		arm->semihosting_result = (target_buffer_get_u32(target, params+0) != 0);
		break;

	case 0x09:	/* SYS_ISTTY */
		if (arm->is_semihosting_fileio) {
			arm->semihosting_hit_fileio = true;
			fileio_info->identifier = "isatty";
			fileio_info->param_1 = r1;
		} else {
			retval = target_read_memory(target, r1, 4, 1, params);
			if (retval != ERROR_OK)
				return retval;
			arm->semihosting_result = isatty(target_buffer_get_u32(target, params+0));
		}
		break;

	case 0x0a:	/* SYS_SEEK */
		retval = target_read_memory(target, r1, 4, 2, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			int fd = target_buffer_get_u32(target, params+0);
			off_t pos = target_buffer_get_u32(target, params+4);
			if (arm->is_semihosting_fileio) {
				arm->semihosting_hit_fileio = true;
				fileio_info->identifier = "lseek";
				fileio_info->param_1 = fd;
				fileio_info->param_2 = pos;
				fileio_info->param_3 = SEEK_SET;
			} else {
				arm->semihosting_result = lseek(fd, pos, SEEK_SET);
				arm->semihosting_errno = errno;
				if (arm->semihosting_result == pos)
					arm->semihosting_result = 0;
			}
		}
		break;

	case 0x0c:	/* SYS_FLEN */
		if (arm->is_semihosting_fileio) {
			LOG_ERROR("SYS_FLEN not supported by semihosting fileio");
			return ERROR_FAIL;
		}
		retval = target_read_memory(target, r1, 4, 1, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			int fd = target_buffer_get_u32(target, params+0);
			struct stat buf;
			arm->semihosting_result = fstat(fd, &buf);
			if (arm->semihosting_result == -1) {
				arm->semihosting_errno = errno;
				arm->semihosting_result = -1;
				break;
			}
			arm->semihosting_result = buf.st_size;
		}
		break;

	case 0x0e:	/* SYS_REMOVE */
		retval = target_read_memory(target, r1, 4, 2, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			uint32_t a = target_buffer_get_u32(target, params+0);
			uint32_t l = target_buffer_get_u32(target, params+4);
			if (arm->is_semihosting_fileio) {
				arm->semihosting_hit_fileio = true;
				fileio_info->identifier = "unlink";
				fileio_info->param_1 = a;
				fileio_info->param_2 = l;
			} else {
				if (l <= 255) {
					uint8_t fn[256];
					retval = target_read_memory(target, a, 1, l, fn);
					if (retval != ERROR_OK)
						return retval;
					fn[l] = 0;
					arm->semihosting_result = remove((char *)fn);
					arm->semihosting_errno =  errno;
				} else {
					arm->semihosting_result = -1;
					arm->semihosting_errno = EINVAL;
				}
			}
		}
		break;

	case 0x0f:	/* SYS_RENAME */
		retval = target_read_memory(target, r1, 4, 4, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			uint32_t a1 = target_buffer_get_u32(target, params+0);
			uint32_t l1 = target_buffer_get_u32(target, params+4);
			uint32_t a2 = target_buffer_get_u32(target, params+8);
			uint32_t l2 = target_buffer_get_u32(target, params+12);
			if (arm->is_semihosting_fileio) {
				arm->semihosting_hit_fileio = true;
				fileio_info->identifier = "rename";
				fileio_info->param_1 = a1;
				fileio_info->param_2 = l1;
				fileio_info->param_3 = a2;
				fileio_info->param_4 = l2;
			} else {
				if (l1 <= 255 && l2 <= 255) {
					uint8_t fn1[256], fn2[256];
					retval = target_read_memory(target, a1, 1, l1, fn1);
					if (retval != ERROR_OK)
						return retval;
					retval = target_read_memory(target, a2, 1, l2, fn2);
					if (retval != ERROR_OK)
						return retval;
					fn1[l1] = 0;
					fn2[l2] = 0;
					arm->semihosting_result = rename((char *)fn1, (char *)fn2);
					arm->semihosting_errno =  errno;
				} else {
					arm->semihosting_result = -1;
					arm->semihosting_errno = EINVAL;
				}
			}
		}
		break;

	case 0x11:	/* SYS_TIME */
		arm->semihosting_result = time(NULL);
		break;

	case 0x13:	/* SYS_ERRNO */
		arm->semihosting_result = arm->semihosting_errno;
		break;

	case 0x15:	/* SYS_GET_CMDLINE */
		retval = target_read_memory(target, r1, 4, 2, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			uint32_t a = target_buffer_get_u32(target, params+0);
			uint32_t l = target_buffer_get_u32(target, params+4);
			char *arg = arm->semihosting_cmdline != NULL ? arm->semihosting_cmdline : "";
			uint32_t s = strlen(arg) + 1;
			if (l < s)
				arm->semihosting_result = -1;
			else {
				retval = target_write_buffer(target, a, s, (uint8_t *)arg);
				if (retval != ERROR_OK)
					return retval;
				arm->semihosting_result = 0;
			}
		}
		break;

	case 0x16:	/* SYS_HEAPINFO */
		retval = target_read_memory(target, r1, 4, 1, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			uint32_t a = target_buffer_get_u32(target, params+0);
			/* tell the remote we have no idea */
			memset(params, 0, 4*4);
			retval = target_write_memory(target, a, 4, 4, params);
			if (retval != ERROR_OK)
				return retval;
			arm->semihosting_result = 0;
		}
		break;

	case 0x18:	/* angel_SWIreason_ReportException */
		switch (r1) {
		case 0x20026:	/* ADP_Stopped_ApplicationExit */
			fprintf(stderr, "semihosting: *** application exited ***\n");
			break;
		case 0x20000:	/* ADP_Stopped_BranchThroughZero */
		case 0x20001:	/* ADP_Stopped_UndefinedInstr */
		case 0x20002:	/* ADP_Stopped_SoftwareInterrupt */
		case 0x20003:	/* ADP_Stopped_PrefetchAbort */
		case 0x20004:	/* ADP_Stopped_DataAbort */
		case 0x20005:	/* ADP_Stopped_AddressException */
		case 0x20006:	/* ADP_Stopped_IRQ */
		case 0x20007:	/* ADP_Stopped_FIQ */
		case 0x20020:	/* ADP_Stopped_BreakPoint */
		case 0x20021:	/* ADP_Stopped_WatchPoint */
		case 0x20022:	/* ADP_Stopped_StepComplete */
		case 0x20023:	/* ADP_Stopped_RunTimeErrorUnknown */
		case 0x20024:	/* ADP_Stopped_InternalError */
		case 0x20025:	/* ADP_Stopped_UserInterruption */
		case 0x20027:	/* ADP_Stopped_StackOverflow */
		case 0x20028:	/* ADP_Stopped_DivisionByZero */
		case 0x20029:	/* ADP_Stopped_OSSpecific */
		default:
			fprintf(stderr, "semihosting: exception %#x\n",
					(unsigned) r1);
		}
		return target_call_event_callbacks(target, TARGET_EVENT_HALTED);

	case 0x12:	/* SYS_SYSTEM */
		/* Provide SYS_SYSTEM functionality.  Uses the
		 * libc system command, there may be a reason *NOT*
		 * to use this, but as I can't think of one, I
		 * implemented it this way.
		 */
		retval = target_read_memory(target, r1, 4, 2, params);
		if (retval != ERROR_OK)
			return retval;
		else {
			uint32_t len = target_buffer_get_u32(target, params+4);
			uint32_t c_ptr = target_buffer_get_u32(target, params);
			if (arm->is_semihosting_fileio) {
				arm->semihosting_hit_fileio = true;
				fileio_info->identifier = "system";
				fileio_info->param_1 = c_ptr;
				fileio_info->param_2 = len;
			} else {
				uint8_t cmd[256];
				if (len > 255) {
					arm->semihosting_result = -1;
					arm->semihosting_errno = EINVAL;
				} else {
					memset(cmd, 0x0, 256);
					retval = target_read_memory(target, c_ptr, 1, len, cmd);
					if (retval != ERROR_OK)
						return retval;
					else
						arm->semihosting_result = system((const char *)cmd);
				}
			}
		}
		break;
	case 0x0d:	/* SYS_TMPNAM */
	case 0x10:	/* SYS_CLOCK */
	case 0x17:	/* angel_SWIreason_EnterSVC */
	case 0x30:	/* SYS_ELAPSED */
	case 0x31:	/* SYS_TICKFREQ */
	default:
		fprintf(stderr, "semihosting: unsupported call %#x\n",
				(unsigned) r0);
		arm->semihosting_result = -1;
		arm->semihosting_errno = ENOTSUP;
	}

	return ERROR_OK;
}