Skip to content

MPU API #8335

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 22 commits into from
Closed

MPU API #8335

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion TESTS/mbed_hal/flash/functional_tests/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "utest/utest.h"
#include "unity/unity.h"
#include "greentea-client/test_env.h"
#include "platform/mbed_mpu_mgmt.h"

#include "mbed.h"
#include "flash_api.h"
Expand Down Expand Up @@ -251,11 +252,22 @@ Case cases[] = {

utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
{
mbed_mpu_manager_lock_ram_execution();
mbed_mpu_manager_lock_rom_write();

GREENTEA_SETUP(20, "default_auto");
return greentea_test_setup_handler(number_of_cases);
}

Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
void greentea_test_teardown(const size_t passed, const size_t failed, const failure_t failure)
{
mbed_mpu_manager_unlock_ram_execution();
mbed_mpu_manager_unlock_rom_write();

greentea_test_teardown_handler(passed, failed, failure);
}

Specification specification(greentea_test_setup, cases, greentea_test_teardown);

int main()
{
Expand Down
201 changes: 201 additions & 0 deletions TESTS/mbed_hal/mpu/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "utest/utest.h"
#include "unity/unity.h"
#include "greentea-client/test_env.h"

#include "cmsis.h"
#include <stdlib.h>

#include "mpu_api.h"
#include "mpu_test.h"

#if !DEVICE_MPU
#error [NOT_SUPPORTED] MPU API not supported for this target
#endif

using namespace utest::v1;

#define HARDFAULT_IRQn ((IRQn_Type)-13)
#define MEMFAULT_IRQn ((IRQn_Type)-12)

// Assembly return instruction: bx lr
#define ASM_BX_LR 0x4770

volatile uint32_t fault_count;
uint32_t real_hard_fault_handler;
uint32_t real_mem_fault_handler;

static volatile uint16_t data_function = ASM_BX_LR;
static volatile uint16_t bss_function;

static void clear_caches()
{
#if defined(__CORTEX_M7)
/* Data cache clean and invalid */
SCB_CleanInvalidateDCache();

/* Instruction cache invalid */
SCB_InvalidateICache();
#endif

__ISB();
__DSB();

}

static void call_mem(const volatile uint16_t *mem_function)
{
// or the address with 1 to ensure the thumb bit is set
((void (*)())((uint32_t)mem_function | 1))();
}

static void hard_fault_handler_test()
{
fault_count++;
mbed_mpu_enable_ram_xn(false);
}

static void mpu_fault_test(const volatile uint16_t *mem_function)
{
mbed_mpu_init();

// Verify that the mpu causes faults when executing ram
fault_count = 0;
mbed_mpu_enable_ram_xn(true);
call_mem(mem_function);
TEST_ASSERT_EQUAL(1, fault_count);

// Verify that the mpu can be turned off
fault_count = 0;
mbed_mpu_enable_ram_xn(false);
call_mem(mem_function);
TEST_ASSERT_EQUAL(0, fault_count);

// Verify that the mpu causes faults when executing ram
fault_count = 0;
mbed_mpu_enable_ram_xn(true);
call_mem(mem_function);
TEST_ASSERT_EQUAL(1, fault_count);

// Verify that free turns off the mpu
fault_count = 0;
mbed_mpu_free();
call_mem(mem_function);
TEST_ASSERT_EQUAL(0, fault_count);
}

void mpu_init_test()
{
for (int i = 0; i < 10; i++) {
mbed_mpu_init();
}

mbed_mpu_free();
}

void mpu_free_test()
{
mbed_mpu_init();

// Enable the MPU
mbed_mpu_enable_ram_xn(true);

// Free and ensure execution from RAM is allowed
mbed_mpu_free();

call_mem(&data_function);
}

void mpu_fault_test_data()
{
mpu_fault_test(&data_function);
}

void mpu_fault_test_bss()
{
bss_function = ASM_BX_LR;
clear_caches();
mpu_fault_test(&bss_function);
}

void mpu_fault_test_stack()
{
uint16_t stack_function;

stack_function = ASM_BX_LR;
clear_caches();
mpu_fault_test(&stack_function);
}

void mpu_fault_test_heap()
{
uint16_t *heap_function = (uint16_t *)malloc(2);

TEST_ASSERT_NOT_EQUAL(NULL, heap_function);
*heap_function = ASM_BX_LR;
clear_caches();
mpu_fault_test(heap_function);

free(heap_function);
}

utest::v1::status_t fault_override_setup(const Case *const source, const size_t index_of_case)
{
// Save old fault handlers and replace it with a new one
real_hard_fault_handler = NVIC_GetVector(HARDFAULT_IRQn);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it not be more direct to intercept MemManage exception? Would side-step the security issue.

(Any prospect of ever needing the combo of MPU + no ARMv8-M Main Extension? Not sure how easy it is to detect to conditionally support both)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The M2351 doesn't have Main Extension so the combo you mention is already a problem. I tried enabling MemManage initially but it didn't help as this device doesn't support it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunate. Probably not worth doing it here, but if I wanted conditional code, what would the canonical way of testing for Main Extension be? I can see lots of ways of inferring it from what's defined in core_cm23.h versus core_cm33.h, and I can obviously make more specific feature tests, but is there a "master" architecture flag for the concept?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't see a "master" architecture flag for this. The CMSIS MPU functions are checking for
SCB_SHCSR_MEMFAULTENA_Msk being defined so I would probably use that if need be.

real_mem_fault_handler = NVIC_GetVector(MEMFAULT_IRQn);
NVIC_SetVector(HARDFAULT_IRQn, (uint32_t)&hard_fault_handler_test);
NVIC_SetVector(MEMFAULT_IRQn, (uint32_t)&hard_fault_handler_test);

return greentea_case_setup_handler(source, index_of_case);
}

utest::v1::status_t fault_override_teardown(const Case *const source, const size_t passed, const size_t failed,
const failure_t reason)
{
// Restore real fault handlers
NVIC_SetVector(HARDFAULT_IRQn, real_hard_fault_handler);
NVIC_SetVector(MEMFAULT_IRQn, real_mem_fault_handler);

return greentea_case_teardown_handler(source, passed, failed, reason);
}

Case cases[] = {
Case("MPU - init", fault_override_setup, mpu_init_test, fault_override_teardown),
Case("MPU - free", fault_override_setup, mpu_free_test, fault_override_teardown),
#if !((__ARM_ARCH_8M_BASE__ == 1U) || (__ARM_ARCH_8M_MAIN__ == 1U))
// Skip fault tests for ARMv8-M until a fault handler hook is provided
Case("MPU - data fault", fault_override_setup, mpu_fault_test_data, fault_override_teardown),
Case("MPU - bss fault", fault_override_setup, mpu_fault_test_bss, fault_override_teardown),
Case("MPU - stack fault", fault_override_setup, mpu_fault_test_stack, fault_override_teardown),
Case("MPU - heap fault", fault_override_setup, mpu_fault_test_heap, fault_override_teardown)
#endif
};

utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(20, "default_auto");
return greentea_test_setup_handler(number_of_cases);
}

Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);

int main()
{
Harness::run(specification);
}
94 changes: 94 additions & 0 deletions TESTS/mbed_hal/mpu/mpu_test.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/* mbed Microcontroller Library
* Copyright (c) 2018-2018 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/** \addtogroup hal_mpu_tests
* @{
*/

#ifndef MBED_MPU_TEST_H
#define MBED_MPU_TEST_H

#if DEVICE_MPU

#ifdef __cplusplus
extern "C" {
#endif

/** Test that ::mbed_mpu_init can be called multiple times.
*
* Given board provides MPU.
* When ::mbed_mpu_init is called multiple times.
* Then ::mbed_mpu_init are successfully performed (no exception is generated).
*
*/
void mpu_init_test(void);

/** Test that ::mbed_mpu_free disables the MPU
*
* Given board provides MPU.
* When ::mbed_mpu_free is called.
* Then execution from RAM is allowed.
*
*/
void mpu_free_test(void);

/** Test that MPU protection works for global data
*
* Given board provides MPU.
* When RAM execution is disabled with a call to ::mbed_mpu_enable_ram_xn.
* Then execution from global initialized data results in a fault.
*
*/
void mpu_fault_test_data(void);

/** Test that MPU protection works for zero initialized data
*
* Given board provides MPU.
* When RAM execution is disabled with a call to ::mbed_mpu_enable_ram_xn.
* Then execution from global uninitialized data results in a fault.
*
*/
void mpu_fault_test_bss(void);

/** Test that MPU protection works for the stack
*
* Given board provides MPU.
* When RAM execution is disabled with a call to ::mbed_mpu_enable_ram_xn.
* Then execution from stack memory results in a fault.
*
*/
void mpu_fault_test_stack(void);

/** Test that MPU protection works for the heap
*
* Given board provides MPU.
* When RAM execution is disabled with a call to ::mbed_mpu_enable_ram_xn.
* Then execution from heap memory results in a fault.
*
*/
void mpu_fault_test_heap(void);

/**@}*/

#ifdef __cplusplus
}
#endif

#endif

#endif

/** @}*/
1 change: 1 addition & 0 deletions doxyfile_options
Original file line number Diff line number Diff line change
Expand Up @@ -2083,6 +2083,7 @@ PREDEFINED = DOXYGEN_ONLY \
DEVICE_INTERRUPTIN \
DEVICE_ITM \
DEVICE_LPTICKER \
DEVICE_MPU \
DEVICE_PORTIN \
DEVICE_PORTINOUT \
DEVICE_PORTOUT \
Expand Down
2 changes: 1 addition & 1 deletion doxygen_options.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"SEARCH_INCLUDES": "YES",
"INCLUDE_PATH": "",
"INCLUDE_FILE_PATTERNS": "",
"PREDEFINED": "DOXYGEN_ONLY DEVICE_ANALOGIN DEVICE_ANALOGOUT DEVICE_CAN DEVICE_CRC DEVICE_ETHERNET DEVICE_EMAC DEVICE_FLASH DEVICE_I2C DEVICE_I2CSLAVE DEVICE_I2C_ASYNCH DEVICE_INTERRUPTIN DEVICE_ITM DEVICE_LPTICKER DEVICE_PORTIN DEVICE_PORTINOUT DEVICE_PORTOUT DEVICE_PWMOUT DEVICE_RTC DEVICE_TRNG DEVICE_SERIAL DEVICE_SERIAL_ASYNCH DEVICE_SERIAL_FC DEVICE_SLEEP DEVICE_SPI DEVICE_SPI_ASYNCH DEVICE_SPISLAVE DEVICE_QSPI DEVICE_STORAGE \"MBED_DEPRECATED_SINCE(f, g)=\" \"MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, M)=\" \"MBED_DEPRECATED(s)=\"",
"PREDEFINED": "DOXYGEN_ONLY DEVICE_ANALOGIN DEVICE_ANALOGOUT DEVICE_CAN DEVICE_CRC DEVICE_ETHERNET DEVICE_EMAC DEVICE_FLASH DEVICE_I2C DEVICE_I2CSLAVE DEVICE_I2C_ASYNCH DEVICE_INTERRUPTIN DEVICE_ITM DEVICE_LPTICKER DEVICE_MPU DEVICE_PORTIN DEVICE_PORTINOUT DEVICE_PORTOUT DEVICE_PWMOUT DEVICE_RTC DEVICE_TRNG DEVICE_SERIAL DEVICE_SERIAL_ASYNCH DEVICE_SERIAL_FC DEVICE_SLEEP DEVICE_SPI DEVICE_SPI_ASYNCH DEVICE_SPISLAVE DEVICE_QSPI DEVICE_STORAGE \"MBED_DEPRECATED_SINCE(f, g)=\" \"MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, M)=\" \"MBED_DEPRECATED(s)=\"",
"EXPAND_AS_DEFINED": "",
"SKIP_FUNCTION_MACROS": "NO",
"STRIP_CODE_COMMENTS": "NO",
Expand Down
Loading