From 1dcea253d2e2b2da1418bd037b15320b89a0c8b6 Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Tue, 29 Mar 2022 10:18:06 +0200 Subject: [PATCH] shared_multi_heap: Rework framework Entirely rework the shared_multi_heap framework. Refer to the documentation for more information. Signed-off-by: Carlo Caione --- .../memory_management/shared_multi_heap.rst | 106 +++++----- .../multi_heap_manager/shared-multi-heap.yaml | 20 -- include/zephyr/multi_heap/shared_multi_heap.h | 145 +++++++++----- lib/os/shared_multi_heap.c | 113 +++++------ .../boards/mps2_an521.overlay | 36 ++++ .../boards/qemu_cortex_a53.conf | 2 + .../boards/qemu_cortex_a53.overlay | 42 ++-- .../linker_arm64_shared_pool.ld | 5 +- .../mem_heap/shared_multi_heap/prj.conf | 2 - .../mem_heap/shared_multi_heap/src/main.c | 188 +++++++++++++----- .../mem_heap/shared_multi_heap/testcase.yaml | 2 +- 11 files changed, 409 insertions(+), 252 deletions(-) delete mode 100644 dts/bindings/multi_heap_manager/shared-multi-heap.yaml create mode 100644 tests/kernel/mem_heap/shared_multi_heap/boards/mps2_an521.overlay create mode 100644 tests/kernel/mem_heap/shared_multi_heap/boards/qemu_cortex_a53.conf diff --git a/doc/kernel/memory_management/shared_multi_heap.rst b/doc/kernel/memory_management/shared_multi_heap.rst index 8adc2e7836d..89458ecb927 100644 --- a/doc/kernel/memory_management/shared_multi_heap.rst +++ b/doc/kernel/memory_management/shared_multi_heap.rst @@ -5,80 +5,78 @@ Shared Multi Heap The shared multi-heap memory pool manager uses the multi-heap allocator to manage a set of reserved memory regions with different capabilities / -attributes (cacheable, non-cacheable, etc...) defined in the DT. +attributes (cacheable, non-cacheable, etc...). -The user can request allocation from the shared pool specifying the capability -/ attribute of interest for the memory (cacheable / non-cacheable memory, -etc...). +All the different regions can be added at run-time to the shared multi-heap +pool providing an opaque "attribute" value (an integer or enum value) that can +be used by drivers or applications to request memory with certain capabilities. -The different heaps with their attributes available in the shared pool are -defined into the DT file leveraging the ``reserved-memory`` nodes. +This framework is commonly used as follow: -This is a DT example declaring three different memory regions with different -cacheability attributes: ``cacheable`` and ``non-cacheable`` +1. At boot time some platform code initialize the shared multi-heap framework + using :c:func:`shared_multi_heap_pool_init()` and add the memory regions to + the pool with :c:func:`shared_multi_heap_add()`, possibly gathering the + needed information for the regions from the DT. -.. code-block:: devicetree +2. Each memory region encoded in a :c:type:`shared_multi_heap_region` + structure. This structure is also carrying an opaque and user-defined + integer value that is used to define the region capabilities (for example: + cacheability, cpu affinity, etc...) - / { - reserved-memory { - compatible = "reserved-memory"; - #address-cells = <1>; - #size-cells = <1>; +.. code-block:: c - res0: reserved@42000000 { - compatible = "shared-multi-heap"; - reg = <0x42000000 0x1000>; - capability = "cacheable"; - label = "res0"; - }; + // Init the shared multi-heap pool + shared_multi_heap_pool_init() - res1: reserved@43000000 { - compatible = "shared-multi-heap"; - reg = <0x43000000 0x2000>; - capability = "non-cacheable"; - label = "res1"; - }; + // Fill the struct with the data for cacheable memory + struct shared_multi_heap_region cacheable_r0 = { + .addr = addr_r0, + .size = size_r0, + .attr = SMH_REG_ATTR_CACHEABLE, + }; - res2: reserved2@44000000 { - compatible = "shared-multi-heap"; - reg = <0x44000000 0x3000>; - capability = "cacheable"; - label = "res2"; - }; - }; + // Add the region to the pool + shared_multi_heap_add(&cacheable_r0, NULL); -The user can then request 4K from heap memory ``cacheable`` or -``non-cacheable`` using the provided APIs: + // Add another cacheable region + struct shared_multi_heap_region cacheable_r1 = { + .addr = addr_r1, + .size = size_r1, + .attr = SMH_REG_ATTR_CACHEABLE, + }; + + shared_multi_heap_add(&cacheable_r0, NULL); + + // Add a non-cacheable region + struct shared_multi_heap_region non_cacheable_r2 = { + .addr = addr_r2, + .size = size_r2, + .attr = SMH_REG_ATTR_NON_CACHEABLE, + }; + + shared_multi_heap_add(&non_cacheable_r2, NULL); + +3. When a driver or application needs some dynamic memory with a certain + capability, it can use :c:func:`shared_multi_heap_alloc()` (or the aligned + version) to request the memory by using the opaque parameter to select the + correct set of attributes for the needed memory. The framework will take + care of selecting the correct heap (thus memory region) to carve memory + from, based on the opaque parameter and the runtime state of the heaps + (available memory, heap state, etc...) .. code-block:: c // Allocate 4K from cacheable memory shared_multi_heap_alloc(SMH_REG_ATTR_CACHEABLE, 0x1000); - // Allocate 4K from non-cacheable + // Allocate 4K from non-cacheable memory shared_multi_heap_alloc(SMH_REG_ATTR_NON_CACHEABLE, 0x1000); -The backend implementation will allocate the memory region from the heap with -the correct attribute and using the region able to accommodate the required size. - -Special handling for MMU/MPU -**************************** - -For MMU/MPU enabled platform sometimes it is required to setup and configure -the memory regions before these are added to the managed pool. This is done at -init time using the :c:func:`shared_multi_heap_pool_init()` function that is -accepting a :c:type:`smh_init_reg_fn_t` callback function. This callback will -be called for each memory region at init time and it can be used to correctly -map the region before this is considered valid and accessible. - Adding new attributes ********************* -Currently only two memory attributes are supported: ``cacheable`` and -``non-cacheable``. To add a new attribute: - -1. Add the new ``enum`` for the attribute in the :c:enum:`smh_reg_attr` -2. Add the corresponding attribute name in :file:`shared-multi-heap.yaml` +The API does not enforce any attributes, but at least it defines the two most +common ones: :c:enum:`SMH_REG_ATTR_CACHEABLE` and :c:enum:`SMH_REG_ATTR_NON_CACHEABLE` .. doxygengroup:: shared_multi_heap :project: Zephyr diff --git a/dts/bindings/multi_heap_manager/shared-multi-heap.yaml b/dts/bindings/multi_heap_manager/shared-multi-heap.yaml deleted file mode 100644 index fdf735c97e8..00000000000 --- a/dts/bindings/multi_heap_manager/shared-multi-heap.yaml +++ /dev/null @@ -1,20 +0,0 @@ -description: Shared multi-heap memory pool manager - -compatible: "shared-multi-heap" - -include: - - name: base.yaml - property-allowlist: ['reg', 'label'] - -properties: - # Keep this is sync with shared_multi_heap.h - capability: - type: string - required: false - description: memory region capability - enum: - - "cacheable" - - "non-cacheable" - - label: - required: true diff --git a/include/zephyr/multi_heap/shared_multi_heap.h b/include/zephyr/multi_heap/shared_multi_heap.h index ea5367fa61a..77ff413b414 100644 --- a/include/zephyr/multi_heap/shared_multi_heap.h +++ b/include/zephyr/multi_heap/shared_multi_heap.h @@ -4,34 +4,59 @@ * SPDX-License-Identifier: Apache-2.0 */ +/** + * @file + * @brief Public API for Shared Multi-Heap framework + */ + #ifndef ZEPHYR_INCLUDE_MULTI_HEAP_MANAGER_SMH_H_ #define ZEPHYR_INCLUDE_MULTI_HEAP_MANAGER_SMH_H_ - #ifdef __cplusplus extern "C" { #endif /** - * @brief Shared multi-heap interface + * @brief Shared Multi-Heap (SMH) interface * @defgroup shared_multi_heap Shared multi-heap interface * @ingroup multi_heap * @{ * * The shared multi-heap manager uses the multi-heap allocator to manage a set - * of reserved memory regions with different capabilities / attributes - * (cacheable, non-cacheable, etc...) defined in the DT. + * of memory regions with different capabilities / attributes (cacheable, + * non-cacheable, etc...). * - * The user can request allocation from the shared pool specifying the - * capability / attribute of interest for the memory (cacheable / non-cacheable - * memory, etc...) + * All the different regions can be added at run-time to the shared multi-heap + * pool providing an opaque "attribute" value (an integer or enum value) that + * can be used by drivers or applications to request memory with certain + * capabilities. * + * This framework is commonly used as follow: + * + * - At boot time some platform code initialize the shared multi-heap + * framework using @ref shared_multi_heap_pool_init and add the memory + * regions to the pool with @ref shared_multi_heap_add, possibly gathering + * the needed information for the regions from the DT. + * + * - Each memory region encoded in a @ref shared_multi_heap_region structure. + * This structure is also carrying an opaque and user-defined integer value + * that is used to define the region capabilities (for example: + * cacheability, cpu affinity, etc...) + * + * - When a driver or application needs some dynamic memory with a certain + * capability, it can use @ref shared_multi_heap_alloc (or the aligned + * version) to request the memory by using the opaque parameter to select + * the correct set of attributes for the needed memory. The framework will + * take care of selecting the correct heap (thus memory region) to carve + * memory from, based on the opaque parameter and the runtime state of the + * heaps (available memory, heap state, etc...) */ /** - * @brief Memory region attributes / capabilities + * @brief SMH region attributes enumeration type. + * + * Enumeration type for some common memory region attributes. * - * ** This list needs to be kept in sync with shared-multi-heap.yaml ** */ enum smh_reg_attr { /** cacheable */ @@ -44,73 +69,101 @@ enum smh_reg_attr { SMH_REG_ATTR_NUM, }; +/** Maximum number of standard attributes. */ +#define MAX_SHARED_MULTI_HEAP_ATTR SMH_REG_ATTR_NUM + /** * @brief SMH region struct * * This struct is carrying information about the memory region to be added in - * the multi-heap pool. This is filled by the manager with the information - * coming from the reserved memory children nodes in the DT. + * the multi-heap pool. */ struct shared_multi_heap_region { - enum smh_reg_attr attr; + /** Memory heap attribute */ + unsigned int attr; + + /** Memory heap starting virtual address */ uintptr_t addr; + + /** Memory heap size in bytes */ size_t size; }; -/** - * @brief Region init function - * - * This is a user-provided function whose responsibility is to setup or - * initialize the memory region passed in input before this is added to the - * heap pool by the shared multi-heap manager. This function can be used by - * architectures using MMU / MPU that must correctly map the region before this - * is considered valid and accessible. - * - * @param reg Pointer to the SMH region structure. - * @param v_addr Virtual address obtained after mapping. For non-MMU - * architectures this value is the physical address of the - * region. - * @param size Size of the region after mapping. - * - * @return True if the region is ready to be added to the heap pool. - * False if the region must be skipped. - */ -typedef bool (*smh_init_reg_fn_t)(struct shared_multi_heap_region *reg, - uint8_t **v_addr, size_t *size); - - /** * @brief Init the pool * - * Initialize the shared multi-heap pool and hook-up the region init function. + * This must be the first function to be called to initialize the shared + * multi-heap pool. All the individual heaps must be added later with @ref + * shared_multi_heap_add. * - * @param smh_init_reg_fn The function pointer to the region init function. Can - * be NULL for non-MPU / non-MMU architectures. + * @note As for the generic multi-heap allocator the expectation is that this + * function will be called at soc- or board-level. + * + * @retval 0 on success. + * @retval -EALREADY when the pool was already inited. + * @retval other errno codes */ -int shared_multi_heap_pool_init(smh_init_reg_fn_t smh_init_reg_fn); +int shared_multi_heap_pool_init(void); /** * @brief Allocate memory from the memory shared multi-heap pool * - * Allocate a block of memory of the specified size in bytes and with a - * specified capability / attribute. + * Allocates a block of memory of the specified size in bytes and with a + * specified capability / attribute. The opaque attribute parameter is used + * by the backend to select the correct heap to allocate memory from. * - * @param attr Capability / attribute requested for the memory block. - * @param bytes Requested size of the allocation in bytes. + * @param attr capability / attribute requested for the memory block. + * @param bytes requested size of the allocation in bytes. * - * @return A valid pointer to heap memory or NULL if no memory is available. + * @retval ptr a valid pointer to heap memory. + * @retval err NULL if no memory is available. */ -void *shared_multi_heap_alloc(enum smh_reg_attr attr, size_t bytes); +void *shared_multi_heap_alloc(unsigned int attr, size_t bytes); + +/** + * @brief Allocate aligned memory from the memory shared multi-heap pool + * + * Allocates a block of memory of the specified size in bytes and with a + * specified capability / attribute. Takes an additional parameter specifying a + * power of two alignment in bytes. + * + * @param attr capability / attribute requested for the memory block. + * @param align power of two alignment for the returned pointer, in bytes. + * @param bytes requested size of the allocation in bytes. + * + * @retval ptr a valid pointer to heap memory. + * @retval err NULL if no memory is available. + */ +void *shared_multi_heap_aligned_alloc(unsigned int attr, size_t align, size_t bytes); /** * @brief Free memory from the shared multi-heap pool * - * Free the passed block of memory. + * Used to free the passed block of memory that must be the return value of a + * previously call to @ref shared_multi_heap_alloc or @ref + * shared_multi_heap_aligned_alloc. * - * @param block Block to free. + * @param block block to free, must be a pointer to a block allocated + * by shared_multi_heap_alloc or + * shared_multi_heap_aligned_alloc. */ void shared_multi_heap_free(void *block); +/** + * @brief Add an heap region to the shared multi-heap pool + * + * This adds a shared multi-heap region to the multi-heap pool. + * + * @param user_data pointer to any data for the heap. + * @param region pointer to the memory region to be added. + * + * @retval 0 on success. + * @retval -EINVAL when the region attribute is out-of-bound. + * @retval -ENOMEM when there are no more heaps available. + * @retval other errno codes + */ +int shared_multi_heap_add(struct shared_multi_heap_region *region, void *user_data); + /** * @} */ diff --git a/lib/os/shared_multi_heap.c b/lib/os/shared_multi_heap.c index b86c3f979cc..373ea85d0e3 100644 --- a/lib/os/shared_multi_heap.c +++ b/lib/os/shared_multi_heap.c @@ -8,43 +8,31 @@ #include #include #include -#include #include -#define DT_DRV_COMPAT shared_multi_heap - -#define NUM_REGIONS DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) - static struct sys_multi_heap shared_multi_heap; -static struct sys_heap heap_pool[SMH_REG_ATTR_NUM][NUM_REGIONS]; +static struct sys_heap heap_pool[MAX_SHARED_MULTI_HEAP_ATTR][MAX_MULTI_HEAPS]; -static smh_init_reg_fn_t smh_init_reg; - -#define FOREACH_REG(n) \ - { .addr = (uintptr_t) LINKER_DT_RESERVED_MEM_GET_PTR(DT_DRV_INST(n)), \ - .size = LINKER_DT_RESERVED_MEM_GET_SIZE(DT_DRV_INST(n)), \ - .attr = DT_ENUM_IDX(DT_DRV_INST(n), capability), \ - }, - -static struct shared_multi_heap_region dt_region[NUM_REGIONS] = { - DT_INST_FOREACH_STATUS_OKAY(FOREACH_REG) -}; +static unsigned int attr_cnt[MAX_SHARED_MULTI_HEAP_ATTR]; static void *smh_choice(struct sys_multi_heap *mheap, void *cfg, size_t align, size_t size) { - enum smh_reg_attr attr; struct sys_heap *h; + unsigned int attr; void *block; - attr = (enum smh_reg_attr) cfg; + attr = (unsigned int)(long) cfg; - if (attr >= SMH_REG_ATTR_NUM || size == 0) { + if (attr >= MAX_SHARED_MULTI_HEAP_ATTR || size == 0) { return NULL; } - for (size_t reg = 0; reg < NUM_REGIONS; reg++) { - h = &heap_pool[attr][reg]; + /* Set in case the user requested a non-existing attr */ + block = NULL; + + for (size_t hdx = 0; hdx < attr_cnt[attr]; hdx++) { + h = &heap_pool[attr][hdx]; if (h->heap == NULL) { return NULL; @@ -59,29 +47,30 @@ static void *smh_choice(struct sys_multi_heap *mheap, void *cfg, size_t align, s return block; } -static void smh_init_with_attr(enum smh_reg_attr attr) +int shared_multi_heap_add(struct shared_multi_heap_region *region, void *user_data) { - unsigned int slot = 0; - uint8_t *mapped; - size_t size; + static int n_heaps; + struct sys_heap *h; + unsigned int slot; - for (size_t reg = 0; reg < NUM_REGIONS; reg++) { - if (dt_region[reg].attr == attr) { - - if (smh_init_reg != NULL) { - smh_init_reg(&dt_region[reg], &mapped, &size); - } else { - mapped = (uint8_t *) dt_region[reg].addr; - size = dt_region[reg].size; - } - - sys_heap_init(&heap_pool[attr][slot], mapped, size); - sys_multi_heap_add_heap(&shared_multi_heap, - &heap_pool[attr][slot], &dt_region[reg]); - - slot++; - } + if (region->attr >= MAX_SHARED_MULTI_HEAP_ATTR) { + return -EINVAL; } + + /* No more heaps available */ + if (n_heaps++ >= MAX_MULTI_HEAPS) { + return -ENOMEM; + } + + slot = attr_cnt[region->attr]; + h = &heap_pool[region->attr][slot]; + + sys_heap_init(h, (void *) region->addr, region->size); + sys_multi_heap_add_heap(&shared_multi_heap, h, user_data); + + attr_cnt[region->attr]++; + + return 0; } void shared_multi_heap_free(void *block) @@ -89,30 +78,36 @@ void shared_multi_heap_free(void *block) sys_multi_heap_free(&shared_multi_heap, block); } -void *shared_multi_heap_alloc(enum smh_reg_attr attr, size_t bytes) +void *shared_multi_heap_alloc(unsigned int attr, size_t bytes) { - return sys_multi_heap_alloc(&shared_multi_heap, (void *) attr, bytes); + if (attr >= MAX_SHARED_MULTI_HEAP_ATTR) { + return NULL; + } + + return sys_multi_heap_alloc(&shared_multi_heap, (void *)(long) attr, bytes); } -int shared_multi_heap_pool_init(smh_init_reg_fn_t smh_init_reg_fn) +void *shared_multi_heap_aligned_alloc(unsigned int attr, size_t align, size_t bytes) { - smh_init_reg = smh_init_reg_fn; + if (attr >= MAX_SHARED_MULTI_HEAP_ATTR) { + return NULL; + } + + return sys_multi_heap_aligned_alloc(&shared_multi_heap, (void *)(long) attr, + align, bytes); +} + +int shared_multi_heap_pool_init(void) +{ + static atomic_t state; + + if (!atomic_cas(&state, 0, 1)) { + return -EALREADY; + } sys_multi_heap_init(&shared_multi_heap, smh_choice); - for (size_t attr = 0; attr < SMH_REG_ATTR_NUM; attr++) { - smh_init_with_attr(attr); - } + atomic_set(&state, 1); return 0; } - -static int shared_multi_heap_init(const struct device *dev) -{ - __ASSERT_NO_MSG(NUM_REGIONS <= MAX_MULTI_HEAPS); - - /* Nothing to do here. */ - - return 0; -} -SYS_INIT(shared_multi_heap_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); diff --git a/tests/kernel/mem_heap/shared_multi_heap/boards/mps2_an521.overlay b/tests/kernel/mem_heap/shared_multi_heap/boards/mps2_an521.overlay new file mode 100644 index 00000000000..76fb7ac693f --- /dev/null +++ b/tests/kernel/mem_heap/shared_multi_heap/boards/mps2_an521.overlay @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Carlo Caione + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + /delete-node/ memory@38000000; + + sram2_3: memory@38000000 { + compatible = "zephyr,memory-region", "mmio-sram"; + reg = <0x38000000 0x100000>; + zephyr,memory-region = "SRAM2_3"; + }; + + res0: memory@38100000 { + compatible = "zephyr,memory-region", "mmio-sram"; + reg = <0x38100000 0x1000>; + zephyr,memory-region = "RES0"; + zephyr,memory-region-mpu = "RAM"; + }; + + res1: memory@38200000 { + compatible = "zephyr,memory-region", "mmio-sram"; + reg = <0x38200000 0x2000>; + zephyr,memory-region = "RES1"; + zephyr,memory-region-mpu = "RAM_NOCACHE"; + }; + + res2: memory@38300000 { + compatible = "zephyr,memory-region", "mmio-sram"; + reg = <0x38300000 0x3000>; + zephyr,memory-region = "RES2"; + zephyr,memory-region-mpu = "RAM"; + }; +}; diff --git a/tests/kernel/mem_heap/shared_multi_heap/boards/qemu_cortex_a53.conf b/tests/kernel/mem_heap/shared_multi_heap/boards/qemu_cortex_a53.conf new file mode 100644 index 00000000000..4f652d8a451 --- /dev/null +++ b/tests/kernel/mem_heap/shared_multi_heap/boards/qemu_cortex_a53.conf @@ -0,0 +1,2 @@ +CONFIG_HAVE_CUSTOM_LINKER_SCRIPT=y +CONFIG_CUSTOM_LINKER_SCRIPT="linker_arm64_shared_pool.ld" diff --git a/tests/kernel/mem_heap/shared_multi_heap/boards/qemu_cortex_a53.overlay b/tests/kernel/mem_heap/shared_multi_heap/boards/qemu_cortex_a53.overlay index e516dc7ca7d..90b0aeca8ac 100644 --- a/tests/kernel/mem_heap/shared_multi_heap/boards/qemu_cortex_a53.overlay +++ b/tests/kernel/mem_heap/shared_multi_heap/boards/qemu_cortex_a53.overlay @@ -5,30 +5,32 @@ */ / { - reserved-memory { - compatible = "reserved-memory"; - #address-cells = <1>; - #size-cells = <1>; - - res0: reserved@42000000 { - compatible = "shared-multi-heap"; - reg = <0x42000000 0x1000>; - capability = "cacheable"; - label = "res0"; + soc { + res0: memory@42000000 { + compatible = "zephyr,memory-region", "mmio-sram"; + reg = <0x0 0x42000000 0x0 0x1000>; + zephyr,memory-region = "RES0"; + zephyr,memory-region-mpu = "RAM"; }; - res1: reserved@43000000 { - compatible = "shared-multi-heap"; - reg = <0x43000000 0x2000>; - capability = "non-cacheable"; - label = "res1"; + res1: memory@43000000 { + compatible = "zephyr,memory-region", "mmio-sram"; + reg = <0x0 0x43000000 0x0 0x2000>; + zephyr,memory-region = "RES1"; + zephyr,memory-region-mpu = "RAM_NOCACHE"; }; - res2: reserved2@44000000 { - compatible = "shared-multi-heap"; - reg = <0x44000000 0x3000>; - capability = "cacheable"; - label = "res2"; + res_no_mpu: memory@45000000 { + compatible = "zephyr,memory-region", "mmio-sram"; + reg = <0x0 0x45000000 0x0 0x1000>; + zephyr,memory-region = "RES_NO_MPU"; + }; + + res2: memory@44000000 { + compatible = "zephyr,memory-region", "mmio-sram"; + reg = <0x0 0x44000000 0x0 0x3000>; + zephyr,memory-region = "RES2"; + zephyr,memory-region-mpu = "RAM"; }; }; }; diff --git a/tests/kernel/mem_heap/shared_multi_heap/linker_arm64_shared_pool.ld b/tests/kernel/mem_heap/shared_multi_heap/linker_arm64_shared_pool.ld index b588eaaa5a9..8ca83de1fa7 100644 --- a/tests/kernel/mem_heap/shared_multi_heap/linker_arm64_shared_pool.ld +++ b/tests/kernel/mem_heap/shared_multi_heap/linker_arm64_shared_pool.ld @@ -6,18 +6,19 @@ #include #include +#include #include #include MEMORY { - LINKER_DT_RESERVED_MEM_REGIONS() + LINKER_DT_REGIONS() } SECTIONS { - LINKER_DT_RESERVED_MEM_SECTIONS() + LINKER_DT_SECTIONS() } #include diff --git a/tests/kernel/mem_heap/shared_multi_heap/prj.conf b/tests/kernel/mem_heap/shared_multi_heap/prj.conf index 7efb4f4027f..c596cfecac4 100644 --- a/tests/kernel/mem_heap/shared_multi_heap/prj.conf +++ b/tests/kernel/mem_heap/shared_multi_heap/prj.conf @@ -2,6 +2,4 @@ # SPDX-License-Identifier: Apache-2.0 CONFIG_ZTEST=y -CONFIG_HAVE_CUSTOM_LINKER_SCRIPT=y -CONFIG_CUSTOM_LINKER_SCRIPT="linker_arm64_shared_pool.ld" CONFIG_SHARED_MULTI_HEAP=y diff --git a/tests/kernel/mem_heap/shared_multi_heap/src/main.c b/tests/kernel/mem_heap/shared_multi_heap/src/main.c index 0aeecafe1ed..8c8b1c0cbf5 100644 --- a/tests/kernel/mem_heap/shared_multi_heap/src/main.c +++ b/tests/kernel/mem_heap/shared_multi_heap/src/main.c @@ -11,101 +11,194 @@ #include -#define MAX_REGIONS (3) +#define DT_DRV_COMPAT zephyr_memory_region -static struct { - struct shared_multi_heap_region *reg; - uint8_t *v_addr; -} map[MAX_REGIONS]; +#define RES0_CACHE_ADDR DT_REG_ADDR(DT_NODELABEL(res0)) +#define RES1_NOCACHE_ADDR DT_REG_ADDR(DT_NODELABEL(res1)) +#define RES2_CACHE_ADDR DT_REG_ADDR(DT_NODELABEL(res2)) -static bool smh_reg_init(struct shared_multi_heap_region *reg, uint8_t **v_addr, size_t *size) +struct region_map { + struct shared_multi_heap_region region; + uintptr_t p_addr; +}; + +#define FOREACH_REG(n) \ + { \ + .region = { \ + .addr = (uintptr_t) DT_INST_REG_ADDR(n), \ + .size = DT_INST_REG_SIZE(n), \ + .attr = DT_INST_ENUM_IDX_OR(n, zephyr_memory_region_mpu, \ + SMH_REG_ATTR_NUM), \ + }, \ + }, + +struct region_map map[] = { + DT_INST_FOREACH_STATUS_OKAY(FOREACH_REG) +}; + +#if defined(CONFIG_MMU) +static void smh_reg_map(struct shared_multi_heap_region *region) { - static int reg_idx; uint32_t mem_attr; + uint8_t *v_addr; - mem_attr = (reg->attr == SMH_REG_ATTR_CACHEABLE) ? K_MEM_CACHE_WB : K_MEM_CACHE_NONE; + mem_attr = (region->attr == SMH_REG_ATTR_CACHEABLE) ? K_MEM_CACHE_WB : K_MEM_CACHE_NONE; mem_attr |= K_MEM_PERM_RW; - z_phys_map(v_addr, reg->addr, reg->size, mem_attr); + z_phys_map(&v_addr, region->addr, region->size, mem_attr); - *size = reg->size; - - /* Save the mapping to retrieve the region from the vaddr */ - map[reg_idx].reg = reg; - map[reg_idx].v_addr = *v_addr; - - reg_idx++; - - return true; + region->addr = (uintptr_t) v_addr; } +#endif /* CONFIG_MMU */ -static struct shared_multi_heap_region *get_reg_addr(uint8_t *v_addr) +/* + * Given a virtual address retrieve the original memory region that the mapping + * is belonging to. + */ +static struct region_map *get_region_map(void *v_addr) { - for (size_t reg = 0; reg < MAX_REGIONS; reg++) { - if (v_addr >= map[reg].v_addr && - v_addr < map[reg].v_addr + map[reg].reg->size) { - return map[reg].reg; + for (size_t reg = 0; reg < ARRAY_SIZE(map); reg++) { + if ((uintptr_t) v_addr >= map[reg].region.addr && + (uintptr_t) v_addr < map[reg].region.addr + map[reg].region.size) { + return &map[reg]; } } return NULL; } +static inline enum smh_reg_attr mpu_to_reg_attr(int mpu_attr) +{ + /* + * All the memory regions defined in the DT with the MPU property `RAM` + * can be accessed and memory can be retrieved from using the attribute + * `SMH_REG_ATTR_CACHEABLE`. + * + * All the memory regions defined in the DT with the MPU property + * `RAM_NOCACHE` can be accessed and memory can be retrieved from using + * the attribute `SMH_REG_ATTR_NON_CACHEABLE`. + * + * [MPU attr] -> [SMH attr] + * + * RAM -> SMH_REG_ATTR_CACHEABLE + * RAM_NOCACHE -> SMH_REG_ATTR_NON_CACHEABLE + */ + switch (mpu_attr) { + case 0: /* RAM */ + return SMH_REG_ATTR_CACHEABLE; + case 1: /* RAM_NOCACHE */ + return SMH_REG_ATTR_NON_CACHEABLE; + default: + /* How ? */ + ztest_test_fail(); + } + + /* whatever */ + return 0; +} + +static void fill_multi_heap(void) +{ + struct region_map *reg_map; + + for (size_t idx = 0; idx < DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT); idx++) { + reg_map = &map[idx]; + + /* zephyr,memory-region-mpu property not found. Skip it. */ + if (reg_map->region.attr == SMH_REG_ATTR_NUM) { + continue; + } + + /* Convert MPU attributes to shared-multi-heap capabilities */ + reg_map->region.attr = mpu_to_reg_attr(reg_map->region.attr); + + /* Assume for now that phys == virt */ + reg_map->p_addr = reg_map->region.addr; + +#if defined(CONFIG_MMU) + /* + * For MMU-enabled platform we have to MMU-map the physical + * address retrieved by DT at run-time because the SMH + * framework expects virtual addresses. + * + * For MPU-enabled plaform the code is assuming that the region + * are configured at build-time, so no map is needed. + */ + smh_reg_map(®_map->region); +#endif /* CONFIG_MMU */ + + shared_multi_heap_add(®_map->region, NULL); + } +} + void test_shared_multi_heap(void) { - struct shared_multi_heap_region *reg; - uint8_t *block; + struct region_map *reg_map; + void *block; + int ret; - shared_multi_heap_pool_init(smh_reg_init); + ret = shared_multi_heap_pool_init(); + zassert_equal(0, ret, "failed initialization"); + + /* + * Return -EALREADY if already inited + */ + ret = shared_multi_heap_pool_init(); + zassert_equal(-EALREADY, ret, "second init should fail"); + + /* + * Fill the buffer pool with the memory heaps coming from DT + */ + fill_multi_heap(); /* * Request a small cacheable chunk. It should be allocated in the - * smaller region (@ 0x42000000) + * smaller region RES0 */ block = shared_multi_heap_alloc(SMH_REG_ATTR_CACHEABLE, 0x40); - reg = get_reg_addr(block); + reg_map = get_region_map(block); - zassert_equal(reg->addr, 0x42000000, "block in the wrong memory region"); - zassert_equal(reg->attr, SMH_REG_ATTR_CACHEABLE, "wrong memory attribute"); + zassert_equal(reg_map->p_addr, RES0_CACHE_ADDR, "block in the wrong memory region"); + zassert_equal(reg_map->region.attr, SMH_REG_ATTR_CACHEABLE, "wrong memory attribute"); /* * Request another small cacheable chunk. It should be allocated in the - * smaller cacheable region (@ 0x42000000) + * smaller cacheable region RES0 */ block = shared_multi_heap_alloc(SMH_REG_ATTR_CACHEABLE, 0x80); - reg = get_reg_addr(block); + reg_map = get_region_map(block); - zassert_equal(reg->addr, 0x42000000, "block in the wrong memory region"); - zassert_equal(reg->attr, SMH_REG_ATTR_CACHEABLE, "wrong memory attribute"); + zassert_equal(reg_map->p_addr, RES0_CACHE_ADDR, "block in the wrong memory region"); + zassert_equal(reg_map->region.attr, SMH_REG_ATTR_CACHEABLE, "wrong memory attribute"); /* * Request a big cacheable chunk. It should be allocated in the - * bigger cacheable region (@ 0x44000000) + * bigger cacheable region RES2 */ block = shared_multi_heap_alloc(SMH_REG_ATTR_CACHEABLE, 0x1200); - reg = get_reg_addr(block); + reg_map = get_region_map(block); - zassert_equal(reg->addr, 0x44000000, "block in the wrong memory region"); - zassert_equal(reg->attr, SMH_REG_ATTR_CACHEABLE, "wrong memory attribute"); + zassert_equal(reg_map->p_addr, RES2_CACHE_ADDR, "block in the wrong memory region"); + zassert_equal(reg_map->region.attr, SMH_REG_ATTR_CACHEABLE, "wrong memory attribute"); /* * Request a non-cacheable chunk. It should be allocated in the - * non-cacheable region (@ 0x43000000) + * non-cacheable region RES1 */ block = shared_multi_heap_alloc(SMH_REG_ATTR_NON_CACHEABLE, 0x100); - reg = get_reg_addr(block); + reg_map = get_region_map(block); - zassert_equal(reg->addr, 0x43000000, "block in the wrong memory region"); - zassert_equal(reg->attr, SMH_REG_ATTR_NON_CACHEABLE, "wrong memory attribute"); + zassert_equal(reg_map->p_addr, RES1_NOCACHE_ADDR, "block in the wrong memory region"); + zassert_equal(reg_map->region.attr, SMH_REG_ATTR_NON_CACHEABLE, "wrong memory attribute"); /* * Request again a non-cacheable chunk. It should be allocated in the - * non-cacheable region (@ 0x43000000) + * non-cacheable region RES1 */ block = shared_multi_heap_alloc(SMH_REG_ATTR_NON_CACHEABLE, 0x100); - reg = get_reg_addr(block); + reg_map = get_region_map(block); - zassert_equal(reg->addr, 0x43000000, "block in the wrong memory region"); - zassert_equal(reg->attr, SMH_REG_ATTR_NON_CACHEABLE, "wrong memory attribute"); + zassert_equal(reg_map->p_addr, RES1_NOCACHE_ADDR, "block in the wrong memory region"); + zassert_equal(reg_map->region.attr, SMH_REG_ATTR_NON_CACHEABLE, "wrong memory attribute"); /* Request a block too big */ block = shared_multi_heap_alloc(SMH_REG_ATTR_NON_CACHEABLE, 0x10000); @@ -116,9 +209,8 @@ void test_shared_multi_heap(void) zassert_is_null(block, "0 size accepted as valid"); /* Request a non-existent attribute */ - block = shared_multi_heap_alloc(SMH_REG_ATTR_NUM + 1, 0x100); + block = shared_multi_heap_alloc(MAX_SHARED_MULTI_HEAP_ATTR, 0x100); zassert_is_null(block, "wrong attribute accepted as valid"); - } void test_main(void) diff --git a/tests/kernel/mem_heap/shared_multi_heap/testcase.yaml b/tests/kernel/mem_heap/shared_multi_heap/testcase.yaml index 4bf3559d4ad..ed5f6b6f35e 100644 --- a/tests/kernel/mem_heap/shared_multi_heap/testcase.yaml +++ b/tests/kernel/mem_heap/shared_multi_heap/testcase.yaml @@ -3,6 +3,6 @@ tests: kernel.shared_multi_heap: - platform_allow: qemu_cortex_a53 + platform_allow: qemu_cortex_a53 mps2_an521 tags: board multi_heap harness: ztest