Merge pull request #11358 from fjmolinas/riot-cortexm-address-check
cpu/cortexm_common: function to check address validity
This commit is contained in:
commit
cbc08edcd1
@ -874,6 +874,9 @@ FEATURES_USED := $(sort $(FEATURES_REQUIRED) $(filter $(FEATURES_OPTIONAL),$(FEA
|
|||||||
# all periph features correspond to a periph submodule
|
# all periph features correspond to a periph submodule
|
||||||
USEMODULE += $(filter periph_%,$(FEATURES_USED))
|
USEMODULE += $(filter periph_%,$(FEATURES_USED))
|
||||||
|
|
||||||
|
# select cpu_check_address pseudomodule if the corresponding feature is used
|
||||||
|
USEMODULE += $(filter cpu_check_address, $(FEATURES_USED))
|
||||||
|
|
||||||
# recursively catch transitive dependencies
|
# recursively catch transitive dependencies
|
||||||
USEMODULE := $(sort $(USEMODULE))
|
USEMODULE := $(sort $(USEMODULE))
|
||||||
USEPKG := $(sort $(USEPKG))
|
USEPKG := $(sort $(USEPKG))
|
||||||
|
|||||||
@ -1,2 +1,3 @@
|
|||||||
FEATURES_PROVIDED += periph_pm
|
FEATURES_PROVIDED += periph_pm
|
||||||
FEATURES_PROVIDED += cpp
|
FEATURES_PROVIDED += cpp
|
||||||
|
FEATURES_PROVIDED += cpu_check_address
|
||||||
|
|||||||
@ -73,3 +73,53 @@ void cortexm_init(void)
|
|||||||
cortexm_init_isr_priorities();
|
cortexm_init_isr_priorities();
|
||||||
cortexm_init_misc();
|
cortexm_init_misc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool cpu_check_address(volatile const char *address)
|
||||||
|
{
|
||||||
|
#if defined(CPU_ARCH_CORTEX_M3) || defined(CPU_ARCH_CORTEX_M4) || \
|
||||||
|
defined(CPU_ARCH_CORTEX_M4F) || defined(CPU_ARCH_CORTEX_M7)
|
||||||
|
static const uint32_t BFARVALID_MASK = (0x80 << SCB_CFSR_BUSFAULTSR_Pos);
|
||||||
|
|
||||||
|
bool is_valid = true;
|
||||||
|
|
||||||
|
/* Clear BFARVALID flag */
|
||||||
|
SCB->CFSR |= BFARVALID_MASK;
|
||||||
|
|
||||||
|
/* Ignore BusFault by enabling BFHFNMIGN and disabling interrupts */
|
||||||
|
uint32_t mask = __get_FAULTMASK();
|
||||||
|
__disable_fault_irq();
|
||||||
|
SCB->CCR |= SCB_CCR_BFHFNMIGN_Msk;
|
||||||
|
|
||||||
|
*address;
|
||||||
|
/* Check BFARVALID flag */
|
||||||
|
if ((SCB->CFSR & BFARVALID_MASK) != 0) {
|
||||||
|
/* Bus Fault occured reading the address */
|
||||||
|
is_valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reenable BusFault by clearing BFHFNMIGN */
|
||||||
|
SCB->CCR &= ~SCB_CCR_BFHFNMIGN_Msk;
|
||||||
|
__set_FAULTMASK(mask);
|
||||||
|
|
||||||
|
return is_valid;
|
||||||
|
#else
|
||||||
|
/* Cortex-M0 doesn't have BusFault so we need to catch HardFault */
|
||||||
|
(void)address;
|
||||||
|
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
__asm__ volatile (
|
||||||
|
"movs r5, #1 \n" /* R5 will be set to 0 by HardFault handler */
|
||||||
|
/* to indicate HardFault has occured */
|
||||||
|
"ldr r1, =0xDEADF00D \n" /* set magic number */
|
||||||
|
"ldr r2, =0xCAFEBABE \n" /* 2nd magic to be sure */
|
||||||
|
"ldrb r3, %1 \n" /* probe address */
|
||||||
|
"mov %0, r5 \n" /* store result */
|
||||||
|
: "=r"(result)
|
||||||
|
: "m"(*address)
|
||||||
|
: "r1", "r2", "r3", "r5", "cc"
|
||||||
|
);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|||||||
@ -228,6 +228,17 @@ static inline uint32_t cpu_get_image_baseaddr(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks is memory address valid or not
|
||||||
|
*
|
||||||
|
* This function can be used to check for memory size,
|
||||||
|
* peripherals availability, etc.
|
||||||
|
*
|
||||||
|
* @param[in] address Address to check
|
||||||
|
* @return true if address is valid
|
||||||
|
*/
|
||||||
|
bool cpu_check_address(volatile const char *address);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -165,6 +165,7 @@ __attribute__((naked)) void hard_fault_default(void)
|
|||||||
/* Get stack pointer where exception stack frame lies */
|
/* Get stack pointer where exception stack frame lies */
|
||||||
__asm__ volatile
|
__asm__ volatile
|
||||||
(
|
(
|
||||||
|
".syntax unified \n"
|
||||||
/* Check that msp is valid first because we want to stack all the
|
/* Check that msp is valid first because we want to stack all the
|
||||||
* r4-r11 registers so that we can use r0, r1, r2, r3 for other things. */
|
* r4-r11 registers so that we can use r0, r1, r2, r3 for other things. */
|
||||||
"mov r0, sp \n" /* r0 = msp */
|
"mov r0, sp \n" /* r0 = msp */
|
||||||
@ -188,6 +189,24 @@ __attribute__((naked)) void hard_fault_default(void)
|
|||||||
" use_psp: \n" /* else { */
|
" use_psp: \n" /* else { */
|
||||||
"mrs r0, psp \n" /* r0 = psp */
|
"mrs r0, psp \n" /* r0 = psp */
|
||||||
" out: \n" /* } */
|
" out: \n" /* } */
|
||||||
|
#if (defined(CPU_ARCH_CORTEX_M0) || defined(CPU_ARCH_CORTEX_M0PLUS)) \
|
||||||
|
&& defined(MODULE_CPU_CHECK_ADDRESS)
|
||||||
|
/* catch intended HardFaults on Cortex-M0 to probe memory addresses */
|
||||||
|
"ldr r1, [r0, #0x04] \n" /* read R1 from the stack */
|
||||||
|
"ldr r2, =0xDEADF00D \n" /* magic number to be found */
|
||||||
|
"cmp r1, r2 \n" /* compare with the magic number */
|
||||||
|
"bne regular_handler \n" /* no magic -> handle as usual */
|
||||||
|
"ldr r1, [r0, #0x08] \n" /* read R2 from the stack */
|
||||||
|
"ldr r2, =0xCAFEBABE \n" /* 2nd magic number to be found */
|
||||||
|
"cmp r1, r2 \n" /* compare with 2nd magic number */
|
||||||
|
"bne regular_handler \n" /* no magic -> handle as usual */
|
||||||
|
"ldr r1, [r0, #0x18] \n" /* read PC from the stack */
|
||||||
|
"adds r1, r1, #2 \n" /* move to the next instruction */
|
||||||
|
"str r1, [r0, #0x18] \n" /* modify PC in the stack */
|
||||||
|
"ldr r5, =0 \n" /* set R5 to indicate HardFault */
|
||||||
|
"bx lr \n" /* exit the exception handler */
|
||||||
|
" regular_handler: \n"
|
||||||
|
#endif
|
||||||
#if defined(CPU_ARCH_CORTEX_M0) || defined(CPU_ARCH_CORTEX_M0PLUS) \
|
#if defined(CPU_ARCH_CORTEX_M0) || defined(CPU_ARCH_CORTEX_M0PLUS) \
|
||||||
|| defined(CPU_ARCH_CORTEX_M23)
|
|| defined(CPU_ARCH_CORTEX_M23)
|
||||||
"push {r4-r7} \n" /* save r4..r7 to the stack */
|
"push {r4-r7} \n" /* save r4..r7 to the stack */
|
||||||
|
|||||||
@ -8,6 +8,7 @@ PSEUDOMODULES += conn_can_isotp_multi
|
|||||||
PSEUDOMODULES += cord_ep_standalone
|
PSEUDOMODULES += cord_ep_standalone
|
||||||
PSEUDOMODULES += core_%
|
PSEUDOMODULES += core_%
|
||||||
PSEUDOMODULES += cortexm_fpu
|
PSEUDOMODULES += cortexm_fpu
|
||||||
|
PSEUDOMODULES += cpu_check_address
|
||||||
PSEUDOMODULES += ecc_%
|
PSEUDOMODULES += ecc_%
|
||||||
PSEUDOMODULES += emb6_router
|
PSEUDOMODULES += emb6_router
|
||||||
PSEUDOMODULES += event_%
|
PSEUDOMODULES += event_%
|
||||||
|
|||||||
10
tests/cpu_cortexm_address_check/Makefile
Normal file
10
tests/cpu_cortexm_address_check/Makefile
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
BOARD ?= samr21-xpro
|
||||||
|
|
||||||
|
include ../Makefile.tests_common
|
||||||
|
|
||||||
|
FEATURES_REQUIRED += cpu_check_address
|
||||||
|
|
||||||
|
USEMODULE += shell
|
||||||
|
USEMODULE += shell_commands
|
||||||
|
|
||||||
|
include $(RIOTBASE)/Makefile.include
|
||||||
12
tests/cpu_cortexm_address_check/README.md
Normal file
12
tests/cpu_cortexm_address_check/README.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Cortex-M check for memory address validity
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
Cortex-M3/M4/M7-based MCUs allow to check memory address validity
|
||||||
|
by temporarily blocking BusFault handler.
|
||||||
|
|
||||||
|
Validity check can be used to determine RAM/flash/EEPROM sizes,
|
||||||
|
peripherals availability, etc., to create firmware that runs
|
||||||
|
effectively on different MCUs without recompiling.
|
||||||
|
|
||||||
|
NB: Cortex-M0 and Cortex-M0+ don't have BusFault events, all
|
||||||
|
bus errors escalate to HardFault immediately.
|
||||||
65
tests/cpu_cortexm_address_check/main.c
Normal file
65
tests/cpu_cortexm_address_check/main.c
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Unwired Devices LLC
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser
|
||||||
|
* General Public License v2.1. See the file LICENSE in the top level
|
||||||
|
* directory for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup tests
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief Test application for Cortex-M address checks
|
||||||
|
*
|
||||||
|
* @author Oleg Artamonov <oleg@unwds.com>
|
||||||
|
*
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "cpu.h"
|
||||||
|
#include "shell.h"
|
||||||
|
|
||||||
|
static int cmd_check(int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc < 2) {
|
||||||
|
printf("usage: %s <hex address>\n", argv[0]);
|
||||||
|
printf("\t example: %s 0x08080000\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *address;
|
||||||
|
uint32_t address_value = strtoul(argv[1], NULL, 0);
|
||||||
|
|
||||||
|
address = (char *)address_value;
|
||||||
|
|
||||||
|
if (cpu_check_address(address))
|
||||||
|
printf("Address 0x%08" PRIx32 " is valid. Feel free to access it\n", address_value);
|
||||||
|
else
|
||||||
|
printf("Address 0x%08" PRIx32 " is NOT valid. Accessing it will result in BusFault\n", address_value);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const shell_command_t shell_commands[] = {
|
||||||
|
{ "check", "Check address", cmd_check},
|
||||||
|
{ NULL, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
puts("Address check test\n");
|
||||||
|
puts("Please refer to the README.md for more details\n");
|
||||||
|
puts("usage: check <hex address>");
|
||||||
|
puts("\texample: check 0x08080000");
|
||||||
|
|
||||||
|
/* run the shell */
|
||||||
|
char line_buf[SHELL_DEFAULT_BUFSIZE];
|
||||||
|
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user