/* 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; }
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; }
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; }
/* 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; }
/* 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; }
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; }
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 *)®isters[0]); context->R5 = target_buffer_get_u32(target, (const uint8_t *)®isters[1]); context->R6 = target_buffer_get_u32(target, (const uint8_t *)®isters[2]); context->R7 = target_buffer_get_u32(target, (const uint8_t *)®isters[3]); context->R8 = target_buffer_get_u32(target, (const uint8_t *)®isters[4]); context->R9 = target_buffer_get_u32(target, (const uint8_t *)®isters[5]); context->IP = target_buffer_get_u32(target, (const uint8_t *)®isters[6]); context->FP = target_buffer_get_u32(target, (const uint8_t *)®isters[7]); context->SP = target_buffer_get_u32(target, (const uint8_t *)®isters[8]); context->PC = target_buffer_get_u32(target, (const uint8_t *)®isters[9]); if (*thread_info_addr_old == 0xdeadbeef) *thread_info_addr_old = thread_info_addr_update; free(buffer); return context; }
/* 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(®_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(®_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(®_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(®_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(®_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(®_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(®_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(®_params[0]); destroy_reg_param(®_params[1]); destroy_reg_param(®_params[2]); destroy_reg_param(®_params[3]); destroy_reg_param(®_params[4]); return status_code; }
/** * 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; }
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); }
/** * 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; }
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(®_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(®_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(®_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(®_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(®_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(®_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(®_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(®_params[0]); destroy_reg_param(®_params[1]); destroy_reg_param(®_params[2]); destroy_reg_param(®_params[3]); destroy_reg_param(®_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; }