X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Ftarget%2Fcortex_a.c;h=45a877d36783b0b785bc871fc3edd2502bf34dbd;hp=3da58701d869089b9480a5bc7392312db43c3247;hb=bf4cf766310768198cfa766467d47bdb180f9b27;hpb=b55361d07fc02f626103355115274117aa05d330 diff --git a/src/target/cortex_a.c b/src/target/cortex_a.c index 3da58701d8..45a877d367 100644 --- a/src/target/cortex_a.c +++ b/src/target/cortex_a.c @@ -73,6 +73,7 @@ static int cortex_a_dap_read_coreregister_u32(struct target *target, static int cortex_a_dap_write_coreregister_u32(struct target *target, uint32_t value, int regnum); static int cortex_a_mmu(struct target *target, int *enabled); +static int cortex_a_mmu_modify(struct target *target, int enable); static int cortex_a_virt2phys(struct target *target, uint32_t virt, uint32_t *phys); static int cortex_a_read_apb_ab_memory(struct target *target, @@ -97,33 +98,50 @@ static int cortex_a_restore_cp15_control_reg(struct target *target) return retval; } -/* check address before cortex_a_apb read write access with mmu on - * remove apb predictible data abort */ -static int cortex_a_check_address(struct target *target, uint32_t address) +/* + * Set up ARM core for memory access. + * If !phys_access, switch to SVC mode and make sure MMU is on + * If phys_access, switch off mmu + */ +static int cortex_a_prep_memaccess(struct target *target, int phys_access) { struct armv7a_common *armv7a = target_to_armv7a(target); - struct cortex_a_common *cortex_a = target_to_cortex_a(target); - uint32_t os_border = armv7a->armv7a_mmu.os_border; - if ((address < os_border) && - (armv7a->arm.core_mode == ARM_MODE_SVC)) { - LOG_ERROR("%" PRIx32 " access in userspace and target in supervisor", address); - return ERROR_FAIL; - } - if ((address >= os_border) && - (cortex_a->curr_mode != ARM_MODE_SVC)) { + int mmu_enabled = 0; + + if (phys_access == 0) { dpm_modeswitch(&armv7a->dpm, ARM_MODE_SVC); - cortex_a->curr_mode = ARM_MODE_SVC; - LOG_INFO("%" PRIx32 " access in kernel space and target not in supervisor", - address); - return ERROR_OK; + cortex_a_mmu(target, &mmu_enabled); + if (mmu_enabled) + cortex_a_mmu_modify(target, 1); + } else { + cortex_a_mmu(target, &mmu_enabled); + if (mmu_enabled) + cortex_a_mmu_modify(target, 0); } - if ((address < os_border) && - (cortex_a->curr_mode == ARM_MODE_SVC)) { + return ERROR_OK; +} + +/* + * Restore ARM core after memory access. + * If !phys_access, switch to previous mode + * If phys_access, restore MMU setting + */ +static int cortex_a_post_memaccess(struct target *target, int phys_access) +{ + struct armv7a_common *armv7a = target_to_armv7a(target); + + if (phys_access == 0) { dpm_modeswitch(&armv7a->dpm, ARM_MODE_ANY); - cortex_a->curr_mode = ARM_MODE_ANY; + } else { + int mmu_enabled = 0; + cortex_a_mmu(target, &mmu_enabled); + if (mmu_enabled) + cortex_a_mmu_modify(target, 1); } return ERROR_OK; } + + /* modify cp15_control_reg in order to enable or disable mmu for : * - virt2phys address conversion * - read or write memory in phys or virt address */ @@ -132,35 +150,35 @@ static int cortex_a_mmu_modify(struct target *target, int enable) struct cortex_a_common *cortex_a = target_to_cortex_a(target); struct armv7a_common *armv7a = target_to_armv7a(target); int retval = ERROR_OK; + int need_write = 0; + if (enable) { /* if mmu enabled at target stop and mmu not enable */ if (!(cortex_a->cp15_control_reg & 0x1U)) { LOG_ERROR("trying to enable mmu on target stopped with mmu disable"); return ERROR_FAIL; } - if (!(cortex_a->cp15_control_reg_curr & 0x1U)) { + if ((cortex_a->cp15_control_reg_curr & 0x1U) == 0) { cortex_a->cp15_control_reg_curr |= 0x1U; - retval = armv7a->arm.mcr(target, 15, - 0, 0, /* op1, op2 */ - 1, 0, /* CRn, CRm */ - cortex_a->cp15_control_reg_curr); + need_write = 1; } } else { - if ((cortex_a->cp15_control_reg_curr & 0x1U)) { - if (cortex_a->cp15_control_reg_curr & 0x4U) { - /* data cache is active */ - cortex_a->cp15_control_reg_curr &= ~0x4U; - /* flush data cache armv7 function to be called */ - if (armv7a->armv7a_mmu.armv7a_cache.flush_all_data_cache) - armv7a->armv7a_mmu.armv7a_cache.flush_all_data_cache(target); - } + if ((cortex_a->cp15_control_reg_curr & 0x1U) == 0x1U) { cortex_a->cp15_control_reg_curr &= ~0x1U; - retval = armv7a->arm.mcr(target, 15, - 0, 0, /* op1, op2 */ - 1, 0, /* CRn, CRm */ - cortex_a->cp15_control_reg_curr); + need_write = 1; } } + + if (need_write) { + LOG_DEBUG("%s, writing cp15 ctrl: %" PRIx32, + enable ? "enable mmu" : "disable mmu", + cortex_a->cp15_control_reg_curr); + + retval = armv7a->arm.mcr(target, 15, + 0, 0, /* op1, op2 */ + 1, 0, /* CRn, CRm */ + cortex_a->cp15_control_reg_curr); + } return retval; } @@ -243,6 +261,18 @@ static int cortex_a_init_debug_access(struct target *target) if (retval != ERROR_OK) return retval; + /* Disable cacheline fills and force cache write-through in debug state */ + retval = mem_ap_sel_write_atomic_u32(swjdp, armv7a->debug_ap, + armv7a->debug_base + CPUDBG_DSCCR, 0); + if (retval != ERROR_OK) + return retval; + + /* Disable TLB lookup and refill/eviction in debug state */ + retval = mem_ap_sel_write_atomic_u32(swjdp, armv7a->debug_ap, + armv7a->debug_base + CPUDBG_DSMCR, 0); + if (retval != ERROR_OK) + return retval; + /* Enabling of instruction execution in debug mode is done in debug_entry code */ /* Resync breakpoint registers */ @@ -1305,7 +1335,7 @@ static int cortex_a_post_debug_entry(struct target *target) LOG_DEBUG("cp15_control_reg: %8.8" PRIx32, cortex_a->cp15_control_reg); cortex_a->cp15_control_reg_curr = cortex_a->cp15_control_reg; - if (armv7a->armv7a_mmu.armv7a_cache.ctype == -1) + if (armv7a->armv7a_mmu.armv7a_cache.info == -1) armv7a_identify_cache(target); if (armv7a->is_armv7r) { @@ -1509,11 +1539,25 @@ static int cortex_a_set_breakpoint(struct target *target, breakpoint->orig_instr); if (retval != ERROR_OK) return retval; + + /* make sure data cache is cleaned & invalidated down to PoC */ + if (!armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled) { + armv7a_cache_flush_virt(target, breakpoint->address, + breakpoint->length); + } + retval = target_write_memory(target, breakpoint->address & 0xFFFFFFFE, breakpoint->length, 1, code); if (retval != ERROR_OK) return retval; + + /* update i-cache at breakpoint location */ + armv7a_l1_d_cache_inval_virt(target, breakpoint->address, + breakpoint->length); + armv7a_l1_i_cache_inval_virt(target, breakpoint->address, + breakpoint->length); + breakpoint->set = 0x11; /* Any nice value but 0 */ } @@ -1733,6 +1777,13 @@ static int cortex_a_unset_breakpoint(struct target *target, struct breakpoint *b return ERROR_OK; } } else { + + /* make sure data cache is cleaned & invalidated down to PoC */ + if (!armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled) { + armv7a_cache_flush_virt(target, breakpoint->address, + breakpoint->length); + } + /* restore original instruction (kept in target endianness) */ if (breakpoint->length == 4) { retval = target_write_memory(target, @@ -1747,6 +1798,12 @@ static int cortex_a_unset_breakpoint(struct target *target, struct breakpoint *b if (retval != ERROR_OK) return retval; } + + /* update i-cache at breakpoint location */ + armv7a_l1_d_cache_inval_virt(target, breakpoint->address, + breakpoint->length); + armv7a_l1_i_cache_inval_virt(target, breakpoint->address, + breakpoint->length); } breakpoint->set = 0; @@ -2394,7 +2451,7 @@ static int cortex_a_read_apb_ab_memory_fast(struct target *target, */ struct armv7a_common *armv7a = target_to_armv7a(target); struct adiv5_dap *swjdp = armv7a->arm.dap; - uint32_t new_dscr, u32; + uint32_t u32; int retval; /* Switch to non-blocking mode if not already in that mode. */ @@ -2402,19 +2459,24 @@ static int cortex_a_read_apb_ab_memory_fast(struct target *target, if (retval != ERROR_OK) return retval; - if (count > 1) { - /* Consecutively issue the LDC instruction via a write to ITR and - * change to fast mode, in a single bulk copy since DSCR == ITR + 4. - * The instruction is issued into the core before the mode switch. */ - uint8_t command[8]; - target_buffer_set_u32(target, command, ARMV4_5_LDC(0, 1, 0, 1, 14, 5, 0, 4)); - new_dscr = (*dscr & ~DSCR_EXT_DCC_MASK) | DSCR_EXT_DCC_FAST_MODE; - target_buffer_set_u32(target, command + 4, new_dscr); - retval = mem_ap_sel_write_buf(swjdp, armv7a->debug_ap, command, 4, 2, - armv7a->debug_base + CPUDBG_ITR); + /* Issue the LDC instruction via a write to ITR. */ + retval = cortex_a_exec_opcode(target, ARMV4_5_LDC(0, 1, 0, 1, 14, 5, 0, 4), dscr); + if (retval != ERROR_OK) + return retval; + + count--; + + if (count > 0) { + /* Switch to fast mode if not already in that mode. */ + retval = cortex_a_set_dcc_mode(target, DSCR_EXT_DCC_FAST_MODE, dscr); + if (retval != ERROR_OK) + return retval; + + /* Latch LDC instruction. */ + retval = mem_ap_sel_write_atomic_u32(swjdp, armv7a->debug_ap, + armv7a->debug_base + CPUDBG_ITR, ARMV4_5_LDC(0, 1, 0, 1, 14, 5, 0, 4)); if (retval != ERROR_OK) return retval; - *dscr = new_dscr; /* Read the value transferred to DTRTX into the buffer. Due to fast * mode rules, this blocks until the instruction finishes executing and @@ -2423,26 +2485,21 @@ static int cortex_a_read_apb_ab_memory_fast(struct target *target, * word from memory and issues the read instruction for the last word. */ retval = mem_ap_sel_read_buf_noincr(swjdp, armv7a->debug_ap, buffer, - 4, count - 1, armv7a->debug_base + CPUDBG_DTRTX); + 4, count, armv7a->debug_base + CPUDBG_DTRTX); if (retval != ERROR_OK) return retval; /* Advance. */ - buffer += (count - 1) * 4; - } else { - /* Issue the LDC instruction via a write to ITR. */ - retval = cortex_a_exec_opcode(target, ARMV4_5_LDC(0, 1, 0, 1, 14, 5, 0, 4), dscr); - if (retval != ERROR_OK) - return retval; + buffer += count * 4; } - /* Switch to non-blocking mode if not already in that mode. */ - retval = cortex_a_set_dcc_mode(target, DSCR_EXT_DCC_NON_BLOCKING, dscr); + /* Wait for last issued instruction to complete. */ + retval = cortex_a_wait_instrcmpl(target, dscr, false); if (retval != ERROR_OK) return retval; - /* Wait for last issued instruction to complete. */ - retval = cortex_a_wait_instrcmpl(target, dscr, false); + /* Switch to non-blocking mode if not already in that mode. */ + retval = cortex_a_set_dcc_mode(target, DSCR_EXT_DCC_NON_BLOCKING, dscr); if (retval != ERROR_OK) return retval; @@ -2610,7 +2667,6 @@ static int cortex_a_read_phys_memory(struct target *target, uint32_t address, uint32_t size, uint32_t count, uint8_t *buffer) { - struct armv7a_common *armv7a = target_to_armv7a(target); int retval = ERROR_COMMAND_SYNTAX_ERROR; LOG_DEBUG("Reading memory at real address 0x%" PRIx32 "; size %" PRId32 "; count %" PRId32, @@ -2618,19 +2674,31 @@ static int cortex_a_read_phys_memory(struct target *target, if (count && buffer) { /* read memory through APB-AP */ - if (!armv7a->is_armv7r) { - /* disable mmu */ - retval = cortex_a_mmu_modify(target, 0); - if (retval != ERROR_OK) - return retval; - } + cortex_a_prep_memaccess(target, 1); retval = cortex_a_read_apb_ab_memory(target, address, size, count, buffer); + cortex_a_post_memaccess(target, 1); } return retval; } static int cortex_a_read_memory(struct target *target, uint32_t address, uint32_t size, uint32_t count, uint8_t *buffer) +{ + int retval; + + /* cortex_a handles unaligned memory access */ + LOG_DEBUG("Reading memory at address 0x%" PRIx32 "; size %" PRId32 "; count %" PRId32, address, + size, count); + + cortex_a_prep_memaccess(target, 0); + retval = cortex_a_read_apb_ab_memory(target, address, size, count, buffer); + cortex_a_post_memaccess(target, 0); + + return retval; +} + +static int cortex_a_read_memory_ahb(struct target *target, uint32_t address, + uint32_t size, uint32_t count, uint8_t *buffer) { int mmu_enabled = 0; uint32_t virt, phys; @@ -2639,6 +2707,9 @@ static int cortex_a_read_memory(struct target *target, uint32_t address, struct adiv5_dap *swjdp = armv7a->arm.dap; uint8_t apsel = swjdp->apsel; + if (!armv7a->memory_ap_available || (apsel != armv7a->memory_ap)) + return target_read_memory(target, address, size, count, buffer); + /* cortex_a handles unaligned memory access */ LOG_DEBUG("Reading memory at address 0x%" PRIx32 "; size %" PRId32 "; count %" PRId32, address, size, count); @@ -2650,31 +2721,22 @@ static int cortex_a_read_memory(struct target *target, uint32_t address, return retval; } - if (armv7a->memory_ap_available && (apsel == armv7a->memory_ap)) { - if (mmu_enabled) { - virt = address; - retval = cortex_a_virt2phys(target, virt, &phys); - if (retval != ERROR_OK) - return retval; + if (mmu_enabled) { + virt = address; + retval = cortex_a_virt2phys(target, virt, &phys); + if (retval != ERROR_OK) + return retval; - LOG_DEBUG("Reading at virtual address. Translating v:0x%" PRIx32 " to r:0x%" PRIx32, - virt, phys); - address = phys; - } - retval = cortex_a_read_phys_memory(target, address, size, - count, buffer); - } else { - if (mmu_enabled) { - retval = cortex_a_check_address(target, address); - if (retval != ERROR_OK) - return retval; - /* enable MMU as we could have disabled it for phys access */ - retval = cortex_a_mmu_modify(target, 1); - if (retval != ERROR_OK) - return retval; - } - retval = cortex_a_read_apb_ab_memory(target, address, size, count, buffer); + LOG_DEBUG("Reading at virtual address. Translating v:0x%" PRIx32 " to r:0x%" PRIx32, + virt, phys); + address = phys; } + + if (!count || !buffer) + return ERROR_COMMAND_SYNTAX_ERROR; + + retval = mem_ap_sel_read_buf(swjdp, armv7a->memory_ap, buffer, size, count, address); + return retval; } @@ -2682,7 +2744,6 @@ static int cortex_a_write_phys_memory(struct target *target, uint32_t address, uint32_t size, uint32_t count, const uint8_t *buffer) { - struct armv7a_common *armv7a = target_to_armv7a(target); int retval = ERROR_COMMAND_SYNTAX_ERROR; LOG_DEBUG("Writing memory to real address 0x%" PRIx32 "; size %" PRId32 "; count %" PRId32, address, @@ -2690,12 +2751,9 @@ static int cortex_a_write_phys_memory(struct target *target, if (count && buffer) { /* write memory through APB-AP */ - if (!armv7a->is_armv7r) { - retval = cortex_a_mmu_modify(target, 0); - if (retval != ERROR_OK) - return retval; - } - return cortex_a_write_apb_ab_memory(target, address, size, count, buffer); + cortex_a_prep_memaccess(target, 1); + retval = cortex_a_write_apb_ab_memory(target, address, size, count, buffer); + cortex_a_post_memaccess(target, 1); } return retval; @@ -2703,6 +2761,24 @@ static int cortex_a_write_phys_memory(struct target *target, static int cortex_a_write_memory(struct target *target, uint32_t address, uint32_t size, uint32_t count, const uint8_t *buffer) +{ + int retval; + + /* cortex_a handles unaligned memory access */ + LOG_DEBUG("Writing memory at address 0x%" PRIx32 "; size %" PRId32 "; count %" PRId32, address, + size, count); + + /* memory writes bypass the caches, must flush before writing */ + armv7a_cache_auto_flush_on_write(target, address, size * count); + + cortex_a_prep_memaccess(target, 0); + retval = cortex_a_write_apb_ab_memory(target, address, size, count, buffer); + cortex_a_post_memaccess(target, 0); + return retval; +} + +static int cortex_a_write_memory_ahb(struct target *target, uint32_t address, + uint32_t size, uint32_t count, const uint8_t *buffer) { int mmu_enabled = 0; uint32_t virt, phys; @@ -2711,6 +2787,9 @@ static int cortex_a_write_memory(struct target *target, uint32_t address, struct adiv5_dap *swjdp = armv7a->arm.dap; uint8_t apsel = swjdp->apsel; + if (!armv7a->memory_ap_available || (apsel != armv7a->memory_ap)) + return target_write_memory(target, address, size, count, buffer); + /* cortex_a handles unaligned memory access */ LOG_DEBUG("Writing memory at address 0x%" PRIx32 "; size %" PRId32 "; count %" PRId32, address, size, count); @@ -2722,35 +2801,92 @@ static int cortex_a_write_memory(struct target *target, uint32_t address, return retval; } - if (armv7a->memory_ap_available && (apsel == armv7a->memory_ap)) { - LOG_DEBUG("Writing memory to address 0x%" PRIx32 "; size %" PRId32 "; count %" PRId32, address, size, - count); - if (mmu_enabled) { - virt = address; - retval = cortex_a_virt2phys(target, virt, &phys); + if (mmu_enabled) { + virt = address; + retval = cortex_a_virt2phys(target, virt, &phys); + if (retval != ERROR_OK) + return retval; + + LOG_DEBUG("Writing to virtual address. Translating v:0x%" PRIx32 " to r:0x%" PRIx32, + virt, + phys); + address = phys; + } + + if (!count || !buffer) + return ERROR_COMMAND_SYNTAX_ERROR; + + retval = mem_ap_sel_write_buf(swjdp, armv7a->memory_ap, buffer, size, count, address); + + return retval; +} + +static int cortex_a_read_buffer(struct target *target, uint32_t address, + uint32_t count, uint8_t *buffer) +{ + uint32_t size; + + /* Align up to maximum 4 bytes. The loop condition makes sure the next pass + * will have something to do with the size we leave to it. */ + for (size = 1; size < 4 && count >= size * 2 + (address & size); size *= 2) { + if (address & size) { + int retval = cortex_a_read_memory_ahb(target, address, size, 1, buffer); if (retval != ERROR_OK) return retval; + address += size; + count -= size; + buffer += size; + } + } - LOG_DEBUG("Writing to virtual address. Translating v:0x%" PRIx32 " to r:0x%" PRIx32, - virt, - phys); - address = phys; + /* Read the data with as large access size as possible. */ + for (; size > 0; size /= 2) { + uint32_t aligned = count - count % size; + if (aligned > 0) { + int retval = cortex_a_read_memory_ahb(target, address, size, aligned / size, buffer); + if (retval != ERROR_OK) + return retval; + address += aligned; + count -= aligned; + buffer += aligned; } - retval = cortex_a_write_phys_memory(target, address, size, - count, buffer); - } else { - if (mmu_enabled) { - retval = cortex_a_check_address(target, address); + } + + return ERROR_OK; +} + +static int cortex_a_write_buffer(struct target *target, uint32_t address, + uint32_t count, const uint8_t *buffer) +{ + uint32_t size; + + /* Align up to maximum 4 bytes. The loop condition makes sure the next pass + * will have something to do with the size we leave to it. */ + for (size = 1; size < 4 && count >= size * 2 + (address & size); size *= 2) { + if (address & size) { + int retval = cortex_a_write_memory_ahb(target, address, size, 1, buffer); if (retval != ERROR_OK) return retval; - /* enable MMU as we could have disabled it for phys access */ - retval = cortex_a_mmu_modify(target, 1); + address += size; + count -= size; + buffer += size; + } + } + + /* Write the data with as large access size as possible. */ + for (; size > 0; size /= 2) { + uint32_t aligned = count - count % size; + if (aligned > 0) { + int retval = cortex_a_write_memory_ahb(target, address, size, aligned / size, buffer); if (retval != ERROR_OK) return retval; + address += aligned; + count -= aligned; + buffer += aligned; } - retval = cortex_a_write_apb_ab_memory(target, address, size, count, buffer); } - return retval; + + return ERROR_OK; } static int cortex_a_handle_target_request(void *priv) @@ -2802,7 +2938,7 @@ static int cortex_a_examine_first(struct target *target) /* We do one extra read to ensure DAP is configured, * we call ahbap_debugport_init(swjdp) instead */ - retval = ahbap_debugport_init(swjdp); + retval = ahbap_debugport_init(swjdp, 0); if (retval != ERROR_OK) return retval; @@ -2842,7 +2978,7 @@ static int cortex_a_examine_first(struct target *target) return retval; } LOG_DEBUG("Detected core %" PRId32 " dbgbase: %08" PRIx32, - coreidx, armv7a->debug_base); + target->coreid, armv7a->debug_base); } else armv7a->debug_base = target->dbgbase; @@ -2983,30 +3119,24 @@ static int cortex_a_init_arch_info(struct target *target, struct cortex_a_common *cortex_a, struct jtag_tap *tap) { struct armv7a_common *armv7a = &cortex_a->armv7a_common; - struct adiv5_dap *dap = &armv7a->dap; - - armv7a->arm.dap = dap; /* Setup struct cortex_a_common */ cortex_a->common_magic = CORTEX_A_COMMON_MAGIC; + /* tap has no dap initialized */ if (!tap->dap) { - armv7a->arm.dap = dap; - /* Setup struct cortex_a_common */ + tap->dap = dap_init(); /* prepare JTAG information for the new target */ cortex_a->jtag_info.tap = tap; cortex_a->jtag_info.scann_size = 4; /* Leave (only) generic DAP stuff for debugport_init() */ - dap->jtag_info = &cortex_a->jtag_info; + tap->dap->jtag_info = &cortex_a->jtag_info; + } - /* Number of bits for tar autoincrement, impl. dep. at least 10 */ - dap->tar_autoincr_block = (1 << 10); - dap->memaccess_tck = 80; - tap->dap = dap; - } else - armv7a->arm.dap = tap->dap; + tap->dap->ap[dap_ap_get_select(tap->dap)].memaccess_tck = 80; + armv7a->arm.dap = tap->dap; cortex_a->fast_reg_read = 0; @@ -3060,12 +3190,18 @@ static void cortex_a_deinit_target(struct target *target) static int cortex_a_mmu(struct target *target, int *enabled) { + struct armv7a_common *armv7a = target_to_armv7a(target); + if (target->state != TARGET_HALTED) { LOG_ERROR("%s: target not halted", __func__); return ERROR_TARGET_INVALID; } - *enabled = target_to_cortex_a(target)->armv7a_common.armv7a_mmu.mmu_enabled; + if (armv7a->is_armv7r) + *enabled = 0; + else + *enabled = target_to_cortex_a(target)->armv7a_common.armv7a_mmu.mmu_enabled; + return ERROR_OK; } @@ -3285,6 +3421,9 @@ struct target_type cortexa_target = { .read_memory = cortex_a_read_memory, .write_memory = cortex_a_write_memory, + .read_buffer = cortex_a_read_buffer, + .write_buffer = cortex_a_write_buffer, + .checksum_memory = arm_checksum_memory, .blank_check_memory = arm_blank_check_memory,