cortex_a: replace cortex_a_check_address function

When accessing memory through the ARM core, privilege levels and mmu
access permissions observed. Thus it depends on the current mode of the
ARM core whether an access is possible or not. the ARM in USR mode can
not access memory mapped to a higher privilege level. This means, if the
ARM core is halted while executing at PL0, the debugger would be
prevented from setting a breakpoint at an address with a higher privilege
level, e.g. in the OS kernel. This is not desirable.

cortex_a_check_address() tried to work around this by predicting if an
access would fail and switched the ARM core to SVC mode. However, the
prediction was based on hardcoded address ranges and only worked for
Linux and a 3G/1G user/kernel space split.

This patch changes the policy to always switch to SVC mode for memory
accesses. It introduces two functions cortex_a_prep_memaccess() and
cortex_a_post_memaccess() which bracket memory reads and writes. These
function encapsulate all actions necessary for preparation and cleanup.

Change-Id: I4ccdb5fd17eadeb2b66ae28caaf0ccd2d014eaa9
Signed-off-by: Matthias Welwarsky <matthias@welwarsky.de>
Reviewed-on: http://openocd.zylin.com/3119
Reviewed-by: Paul Fertser <fercerpav@gmail.com>
Tested-by: jenkins
__archive__
Matthias Welwarsky 2015-11-24 15:59:59 +01:00 committed by Paul Fertser
parent 3683f8cef0
commit 8b140fd724
3 changed files with 55 additions and 82 deletions

View File

@ -172,13 +172,6 @@ static int armv7a_read_ttbcr(struct target *target)
armv7a->armv7a_mmu.ttbr_mask[0],
armv7a->armv7a_mmu.ttbr_mask[1]);
/* FIXME: default is hard coded LINUX border */
armv7a->armv7a_mmu.os_border = 0xc0000000;
if (ttbcr_n != 0) {
LOG_INFO("SVC access above %" PRIx32,
armv7a->armv7a_mmu.ttbr_range[0] + 1);
armv7a->armv7a_mmu.os_border = armv7a->armv7a_mmu.ttbr_range[0] + 1;
}
done:
dpm->finish(dpm);
return retval;

View File

@ -92,7 +92,6 @@ struct armv7a_mmu_common {
uint32_t ttbcr; /* cache for ttbcr register */
uint32_t ttbr_mask[2];
uint32_t ttbr_range[2];
uint32_t os_border;
int (*read_physical_memory)(struct target *target, uint32_t address, uint32_t size,
uint32_t count, uint8_t *buffer);

View File

@ -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;
}
if ((address < os_border) &&
(cortex_a->curr_mode == ARM_MODE_SVC)) {
dpm_modeswitch(&armv7a->dpm, ARM_MODE_ANY);
cortex_a->curr_mode = ARM_MODE_ANY;
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);
}
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);
} 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 */
@ -2649,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,
@ -2657,13 +2674,9 @@ 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;
}
@ -2671,31 +2684,15 @@ static int cortex_a_read_phys_memory(struct target *target,
static int cortex_a_read_memory(struct target *target, uint32_t address,
uint32_t size, uint32_t count, uint8_t *buffer)
{
int mmu_enabled = 0;
int retval;
struct armv7a_common *armv7a = target_to_armv7a(target);
/* cortex_a handles unaligned memory access */
LOG_DEBUG("Reading memory at address 0x%" PRIx32 "; size %" PRId32 "; count %" PRId32, address,
size, count);
/* determine if MMU was enabled on target stop */
if (!armv7a->is_armv7r) {
retval = cortex_a_mmu(target, &mmu_enabled);
if (retval != ERROR_OK)
return retval;
}
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;
}
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;
}
@ -2747,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,
@ -2755,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;
@ -2769,36 +2762,18 @@ 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 mmu_enabled = 0;
int retval;
struct armv7a_common *armv7a = target_to_armv7a(target);
/* cortex_a handles unaligned memory access */
LOG_DEBUG("Writing memory at address 0x%" PRIx32 "; size %" PRId32 "; count %" PRId32, address,
size, count);
/* determine if MMU was enabled on target stop */
if (!armv7a->is_armv7r) {
retval = cortex_a_mmu(target, &mmu_enabled);
if (retval != ERROR_OK)
return retval;
}
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;
}
/* 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;
}
@ -3221,12 +3196,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;
}