tests: arch: arm: verify Armv8.1-M MPU PXN

Adds a test to verify Armv8.1-M MPU architecture's PXN attribute.

Verify PXN via static mpu config by utilizing existing __ramfunc MPU region
Since previous commit changes the behaviour of __ramfunc region,
executing userspace code in this region in privileged mode will
result in an MPU fault while execution in unprivileged mode should
work fine.

Verify PXN via DT by creating a custom section in the unused SRAM region.
Since the overlay sets the PXN attribute for the SRAM, executing code
in this region in privileged mode will result in an MPU fault while,
execution in unprivileged mode should work fine.
Also, instead of testing DT config for each board, do it only for MPS3
boards that which can be tested with simulation.

Signed-off-by: Sudan Landge <sudan.landge@arm.com>
This commit is contained in:
Sudan Landge 2025-02-28 18:02:05 +00:00 committed by Carles Cufí
commit 0161118913
9 changed files with 226 additions and 0 deletions

View file

@ -0,0 +1,10 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(arm_mpu_pxn)
target_sources(app PRIVATE src/main.c)
zephyr_linker_sources_ifdef(CONFIG_SOC_MPS3_CORSTONE300 RAM_SECTIONS mps3_corstone300.ld)
zephyr_linker_sources_ifdef(CONFIG_SOC_MPS3_CORSTONE310 RAM_SECTIONS mps3_corstone310.ld)

View file

@ -0,0 +1,11 @@
/*
* Copyright 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
SECTION_DATA_PROLOGUE(.customramfunc, , )
{
*(.customramfunc) * (".customramfunc.*")
}
GROUP_DATA_LINK_IN(SRAM, SRAM)

View file

@ -0,0 +1,18 @@
/*
* Copyright 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/dt-bindings/memory-attr/memory-attr.h>
#include <zephyr/dt-bindings/memory-attr/memory-attr-arm.h>
/ {
sram: sram@11000000 {
compatible = "zephyr,memory-region", "mmio-sram";
reg = <0x11000000 DT_SIZE_M(1)>;
zephyr,memory-region = "SRAM";
zephyr,memory-attr = <( DT_MEM_ARM(ATTR_MPU_RAM_PXN) )>;
};
};

View file

@ -0,0 +1,18 @@
/*
* Copyright 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/dt-bindings/memory-attr/memory-attr.h>
#include <zephyr/dt-bindings/memory-attr/memory-attr-arm.h>
/ {
sram: sram@11000000 {
compatible = "zephyr,memory-region", "mmio-sram";
reg = <0x11000000 DT_SIZE_M(1)>;
zephyr,memory-region = "SRAM";
zephyr,memory-attr = <( DT_MEM_ARM(ATTR_MPU_RAM_PXN) )>;
};
};

View file

@ -0,0 +1,11 @@
/*
* Copyright 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
SECTION_DATA_PROLOGUE(.customramfunc, , )
{
*(.customramfunc) * (".customramfunc.*")
}
GROUP_DATA_LINK_IN(DTCM, DTCM)

View file

@ -0,0 +1,18 @@
/*
* Copyright 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/dt-bindings/memory-attr/memory-attr.h>
#include <zephyr/dt-bindings/memory-attr/memory-attr-arm.h>
/ {
dtcm: dtcm@30000000 {
compatible = "zephyr,memory-region";
reg = <0x30000000 DT_SIZE_K(32)>;
zephyr,memory-region = "DTCM";
zephyr,memory-attr = <( DT_MEM_ARM(ATTR_MPU_RAM_PXN) )>;
};
};

View file

@ -0,0 +1,2 @@
CONFIG_ZTEST=y
CONFIG_ZTEST_FATAL_HOOK=y

View file

@ -0,0 +1,118 @@
/*
* Copyright 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
*
* SPDX-License-Identifier: Apache-2.0
*
*/
#include <zephyr/ztest.h>
#include <zephyr/ztest_error_hook.h>
#define __customramfunc \
__attribute__((noinline)) __attribute__((long_call, section(".customramfunc")))
/**
* @brief Simple func to verify PXN via DT config since code with
* __customramfunc falls in an MPU region with PXN set
*
*/
__customramfunc bool custom_ram_func(void)
{
return true;
}
/**
* @brief This is a simple solution to execute a code with and without PXN set
* because code that falls in `__ramfunc` section has PXN attr set if
* built with CONFIG_USERSPACE and unset otherwise.
*/
__ramfunc bool ram_function(void)
{
return true;
}
#ifdef CONFIG_USERSPACE
/**
* @brief Verify that MPU region with PXN attribute set can be executed from
* unprivileged mode.
*/
ZTEST_USER(arm_mpu_pxn, test_arm_mpu_pxn_static_config_user)
{
volatile bool ramfunc_called = false;
/*
* With CONFIG_USERSPACE
* - this func is called in unprivileged mode
* - and __ramfunc falls in an MPU region which has PXN attribute set
*
* Calling ram_function shouldn't result in an exception.
* This confirms that ram_function though part of a region with PXN attribute set,
* can be called from an unprivileged code.
*/
ramfunc_called = ram_function();
zassert_true(true == ramfunc_called,
"Executing code in __ramfunc failed in unprivileged mode.");
}
#endif
/**
* @brief Verify that region marked with PXN attribute via DT can be executed
* from unprivileged mode but cannot be executed from privileged mode.
*/
ZTEST_USER(arm_mpu_pxn, test_arm_mpu_pxn_dt)
{
volatile bool ramfunc_called = false;
#ifndef CONFIG_USERSPACE
/*
* It is expected that calling ram_function() should result in an exception
* because with CONFIG_USERSPACE, ram_function falls in region with
* PXN attribute set.
*/
ztest_set_fault_valid(true);
#endif
ramfunc_called = custom_ram_func();
#ifdef CONFIG_USERSPACE
zassert_true(1 == ramfunc_called,
"Executing code in __customramfunc failed in unprivileged mode.");
#else
/*
* If calling ram_function didn't result in an MPU fault then,
* PXN isn't working as expected so, fail the test.
*/
ztest_test_fail();
#endif
}
/**
* @brief This func is always called in privileged mode so, verify that:
* - region marked with PXN attribute
* cannot be executed from privileged mode
* - and same region when marked without PXN attribute
* can be executed from privileged mode
*/
ZTEST(arm_mpu_pxn, test_arm_mpu_pxn_static_config)
{
volatile bool ramfunc_called = false;
#ifdef CONFIG_USERSPACE
/*
* It is expected that calling ram_function() should result in an exception
* because with CONFIG_USERSPACE, ram_function falls in region with
* PXN attribute set.
*/
ztest_set_fault_valid(true);
#endif
ramfunc_called = ram_function();
#ifdef CONFIG_USERSPACE
/*
* If calling ram_function didn't result in an MPU fault then,
* PXN isn't working as expected so, fail the test.
*/
ztest_test_fail();
#else
zassert_true(1 == ramfunc_called, "Executing code in __ramfunc failed in privileged mode.");
#endif
}
ZTEST_SUITE(arm_mpu_pxn, NULL, NULL, NULL, NULL, NULL);

View file

@ -0,0 +1,20 @@
common:
filter: CONFIG_ARM_MPU_PXN
tags:
- arm
- mpu
platform_allow:
- mps3/corstone300/an547
- mps3/corstone300/fvp
# TODO: enable this after CONFIG_USERSPACE is supported for mps3/corstone310
# - mps3/corstone310/fvp
tests:
# To verify that region marked with PXN attribute can be executed from unprivileged code
# and cannot be executed from privileged code
arch.arm.pxn_with_userspace:
extra_configs:
- CONFIG_USERSPACE=y
# To verify that region marked *without* PXN attribute can be executed from privileged code
arch.arm.pxn_without_userspace:
extra_configs:
- CONFIG_USERSPACE=n