From 09fd6b6ea5a7f8bf39902963feff8e167bc869dd Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Sat, 23 Sep 2023 17:34:25 +0200 Subject: [PATCH] mem_mgmt: Add a memory attributes memory allocator Using this new library it is now possible to leverage the memory attribute property 'zephyr,memory-attr' to define and create a set of memory heaps from which the user can allocate memory from with certain attributes / capabilities. When the CONFIG_MEM_ATTR_HEAP option is set, every region marked with one of the memory attributes listed in include/zephyr/dt-bindings/memory-attr/memory-attr-sw.h is added to a pool of memory heaps used for dynamic allocation of memory buffers with certain attributes. Signed-off-by: Carlo Caione --- .../dt-bindings/memory-attr/memory-attr-sw.h | 27 ++++ include/zephyr/mem_mgmt/mem_attr_heap.h | 98 +++++++++++++ subsys/mem_mgmt/CMakeLists.txt | 1 + subsys/mem_mgmt/Kconfig | 7 + subsys/mem_mgmt/mem_attr_heap.c | 136 ++++++++++++++++++ 5 files changed, 269 insertions(+) create mode 100644 include/zephyr/dt-bindings/memory-attr/memory-attr-sw.h create mode 100644 include/zephyr/mem_mgmt/mem_attr_heap.h create mode 100644 subsys/mem_mgmt/mem_attr_heap.c diff --git a/include/zephyr/dt-bindings/memory-attr/memory-attr-sw.h b/include/zephyr/dt-bindings/memory-attr/memory-attr-sw.h new file mode 100644 index 0000000000..ccd47addf1 --- /dev/null +++ b/include/zephyr/dt-bindings/memory-attr/memory-attr-sw.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 Carlo Caione + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_MEM_ATTR_SW_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_MEM_ATTR_SW_H_ + +#include +#include + +/* + * Software specific memory attributes. + */ +#define DT_MEM_SW_MASK DT_MEM_SW_ATTR_MASK +#define DT_MEM_SW_GET(x) ((x) & DT_MEM_SW_ATTR_MASK) +#define DT_MEM_SW(x) ((x) << DT_MEM_SW_ATTR_SHIFT) + +#define ATTR_SW_ALLOC_CACHE BIT(0) +#define ATTR_SW_ALLOC_NON_CACHE BIT(1) +#define ATTR_SW_ALLOC_DMA BIT(2) + +#define DT_MEM_SW_ALLOC_CACHE DT_MEM_SW(ATTR_SW_ALLOC_CACHE) +#define DT_MEM_SW_ALLOC_NON_CACHE DT_MEM_SW(ATTR_SW_ALLOC_NON_CACHE) +#define DT_MEM_SW_ALLOC_DMA DT_MEM_SW(ATTR_SW_ALLOC_DMA) + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_MEM_ATTR_SW_H_ */ diff --git a/include/zephyr/mem_mgmt/mem_attr_heap.h b/include/zephyr/mem_mgmt/mem_attr_heap.h new file mode 100644 index 0000000000..60cf183dd4 --- /dev/null +++ b/include/zephyr/mem_mgmt/mem_attr_heap.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2023 Carlo Caione, + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_MEM_ATTR_HEAP_H_ +#define ZEPHYR_INCLUDE_MEM_ATTR_HEAP_H_ + +/** + * @brief Memory heaps based on memory attributes + * @defgroup memory_attr_heap Memory heaps based on memory attributes + * @ingroup mem_mgmt + * @{ + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Init the memory pool + * + * This must be the first function to be called to initialize the memory pools + * from all the memory regions with the a software attribute. + * + * @retval 0 on success. + * @retval -EALREADY if the pool was already initialized. + * @retval -ENOMEM too many regions already allocated. + */ +int mem_attr_heap_pool_init(void); + +/** + * @brief Allocate memory with a specified attribute and size. + * + * Allocates a block of memory of the specified size in bytes and with a + * specified capability / attribute. The attribute is used to select the + * correct memory heap to allocate memory from. + * + * @param attr capability / attribute requested for the memory block. + * @param bytes requested size of the allocation in bytes. + * + * @retval ptr a valid pointer to the allocated memory. + * @retval NULL if no memory is available with that attribute and size. + */ +void *mem_attr_heap_alloc(uint32_t attr, size_t bytes); + +/** + * @brief Allocate aligned memory with a specified attribute, size and alignment. + * + * 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 the allocated memory. + * @retval NULL if no memory is available with that attribute and size. + */ +void *mem_attr_heap_aligned_alloc(uint32_t attr, size_t align, size_t bytes); + +/** + * @brief Free the allocated memory + * + * Used to free the passed block of memory that must be the return value of a + * previously call to @ref mem_attr_heap_alloc or @ref + * mem_attr_heap_aligned_alloc. + * + * @param block block to free, must be a pointer to a block allocated by + * @ref mem_attr_heap_alloc or @ref mem_attr_heap_aligned_alloc. + */ +void mem_attr_heap_free(void *block); + +/** + * @brief Get a specific memory region descriptor for a provided address + * + * Finds the memory region descriptor struct controlling the provided pointer. + * + * @param addr address to be found, must be a pointer to a block allocated by + * @ref mem_attr_heap_alloc or @ref mem_attr_heap_aligned_alloc. + * + * @retval str pointer to a memory region structure the address belongs to. + */ +const struct mem_attr_region_t *mem_attr_heap_get_region(void *addr); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_MEM_ATTR_HEAP_H_ */ diff --git a/subsys/mem_mgmt/CMakeLists.txt b/subsys/mem_mgmt/CMakeLists.txt index 62e61fb4d1..d2fcd1a72f 100644 --- a/subsys/mem_mgmt/CMakeLists.txt +++ b/subsys/mem_mgmt/CMakeLists.txt @@ -1,3 +1,4 @@ # SPDX-License-Identifier: Apache-2.0 zephyr_sources_ifdef(CONFIG_MEM_ATTR mem_attr.c) +zephyr_sources_ifdef(CONFIG_MEM_ATTR_HEAP mem_attr_heap.c) diff --git a/subsys/mem_mgmt/Kconfig b/subsys/mem_mgmt/Kconfig index dda1a40416..b381b3892e 100644 --- a/subsys/mem_mgmt/Kconfig +++ b/subsys/mem_mgmt/Kconfig @@ -10,3 +10,10 @@ config MEM_ATTR time an array of the memory regions defined in the DT that can be probed at run-time using several helper functions. Set to `N` if unsure to save RODATA space. + +config MEM_ATTR_HEAP + bool "Memory Attributes heap allocator" + depends on MEM_ATTR + help + Enable an heap allocator based on memory attributes to dynamically + allocate memory from DeviceTree defined memory regions. diff --git a/subsys/mem_mgmt/mem_attr_heap.c b/subsys/mem_mgmt/mem_attr_heap.c new file mode 100644 index 0000000000..e1b3578a4e --- /dev/null +++ b/subsys/mem_mgmt/mem_attr_heap.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2021 Carlo Caione, + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +struct ma_heap { + struct sys_heap heap; + uint32_t attr; +}; + +struct { + struct ma_heap ma_heaps[MAX_MULTI_HEAPS]; + struct sys_multi_heap multi_heap; + int nheaps; +} mah_data; + +static void *mah_choice(struct sys_multi_heap *m_heap, void *cfg, size_t align, size_t size) +{ + uint32_t attr; + void *block; + + if (size == 0) { + return NULL; + } + + attr = (uint32_t)(long) cfg; + + /* Set in case the user requested a non-existing attr */ + block = NULL; + + for (size_t hdx = 0; hdx < mah_data.nheaps; hdx++) { + struct ma_heap *h; + + h = &mah_data.ma_heaps[hdx]; + + if (h->attr != attr) { + continue; + } + + block = sys_heap_aligned_alloc(&h->heap, align, size); + if (block != NULL) { + break; + } + } + + return block; +} + +void mem_attr_heap_free(void *block) +{ + sys_multi_heap_free(&mah_data.multi_heap, block); +} + +void *mem_attr_heap_alloc(uint32_t attr, size_t bytes) +{ + return sys_multi_heap_alloc(&mah_data.multi_heap, + (void *)(long) attr, bytes); +} + +void *mem_attr_heap_aligned_alloc(uint32_t attr, size_t align, size_t bytes) +{ + return sys_multi_heap_aligned_alloc(&mah_data.multi_heap, + (void *)(long) attr, align, bytes); +} + +const struct mem_attr_region_t *mem_attr_heap_get_region(void *addr) +{ + const struct sys_multi_heap_rec *heap_rec; + + heap_rec = sys_multi_heap_get_heap(&mah_data.multi_heap, addr); + + return (const struct mem_attr_region_t *) heap_rec->user_data; +} + +static int ma_heap_add(const struct mem_attr_region_t *region, uint32_t attr) +{ + struct ma_heap *mh; + struct sys_heap *h; + + /* No more heaps available */ + if (mah_data.nheaps >= MAX_MULTI_HEAPS) { + return -ENOMEM; + } + + mh = &mah_data.ma_heaps[mah_data.nheaps++]; + h = &mh->heap; + + mh->attr = attr; + + sys_heap_init(h, (void *) region->dt_addr, region->dt_size); + sys_multi_heap_add_heap(&mah_data.multi_heap, h, (void *) region); + + return 0; +} + + +int mem_attr_heap_pool_init(void) +{ + const struct mem_attr_region_t *regions; + static atomic_t state; + size_t num_regions; + + if (!atomic_cas(&state, 0, 1)) { + return -EALREADY; + } + + sys_multi_heap_init(&mah_data.multi_heap, mah_choice); + + num_regions = mem_attr_get_regions(®ions); + + for (size_t idx = 0; idx < num_regions; idx++) { + uint32_t sw_attr; + + sw_attr = DT_MEM_SW_ATTR_GET(regions[idx].dt_attr); + + /* No SW attribute is present */ + if (!sw_attr) { + continue; + } + + if (ma_heap_add(®ions[idx], sw_attr)) { + return -ENOMEM; + } + } + + return 0; +}