Skip to content

Commit

Permalink
mem_attr: Introduce library to query memory attributes
Browse files Browse the repository at this point in the history
The introduction on the `zephyr,memory-attr` property for memory nodes
has been very useful to mark attributes and capabilities of the memory
regions defined into the DT. What's still missing is an abstracted and
unified way to deal and manage this kind of information.

In this PR we introduce a small and extensible library / subsystem that
can be used by application, drivers or other subsystems to query this
kind of information.

One very common use-case of this library is for example to check whether
a buffer has certain kind of capabilities (i.e. it is cacheable /
non-cacheable).

Signed-off-by: Carlo Caione <[email protected]>
  • Loading branch information
carlocaione committed Jul 28, 2023
1 parent ab59087 commit 7f4e287
Show file tree
Hide file tree
Showing 13 changed files with 284 additions and 0 deletions.
1 change: 1 addition & 0 deletions doc/services/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ OS Services
logging/index.rst
tracing/index.rst
resource_management/index.rst
mem_mgmt/index.rst
modbus/index.rst
notify.rst
pm/index.rst
Expand Down
9 changes: 9 additions & 0 deletions doc/services/mem_mgmt/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.. _mem_mgmt_api:

Memory Attributes management API
================================

API Reference
*************

.. doxygengroup:: memory_attr_interface
79 changes: 79 additions & 0 deletions include/zephyr/mem_mgmt/mem_attr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2023 Carlo Caione <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_INCLUDE_MEM_ATTR_H_
#define ZEPHYR_INCLUDE_MEM_ATTR_H_

/**
* @brief Memory-Attr Interface
* @defgroup memory_attr_interface Memory-Attr Interface
* @ingroup mem_mgmt
* @{
*/

#include <stddef.h>
#include <zephyr/types.h>
#include <zephyr/dt-bindings/memory-attr/memory-attr.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief memory-attr region structure.
*
* This structure represents the data gathered from DT about a memory-region
* marked with memory attribute.
*/
struct mem_attr_region_t {
/** Memory region physical address */
uintptr_t dt_addr;
/** Memory region size */
size_t dt_size;
/** Memory region attribute */
enum dt_memory_attr dt_attr;
};

/**
* @brief Get the list of memory regions.
*
* Get the list of enabled memory regions with their memory-attribute as
* gathered by DT.
*
* @retval a const struct pointer to the memory regions array.
* @retval NULL if there are no regions.
*/
const struct mem_attr_region_t *mem_attr_get_regions(void);

/**
* @brief Check if a buffer has correct size and attributes.
*
* This function is used to check if a given buffer with a given attribute
* fully match a memory regions in terms of size and attributes. This is
* usually used to verify that a buffer has the expected attributes (for
* example the buffer is cacheable / non-cacheable or belongs to RAM / FLASH,
* etc...).
*
* @param addr Virtual address of the user buffer.
* @param size Size of the user buffer.
* @param attr Expected / desired attribute for the buffer.
*
* @retval 0 if the buffer has the correct size and attribute.
* @retval -ENOSYS if the operation is not supported (for example if the MMU is enabled).
* @retval -ENOTSUP if the wrong parameters were passed.
* @retval -EINVAL if the buffer has the wrong attribute.
* @retval -ENOSPC if the buffer is too big for the region it belongs to.
* @retval -ENOBUFS if the buffer is allocated outside a memory region.
*/
int mem_attr_check_buf(void *addr, size_t size, enum dt_memory_attr attr);

#ifdef __cplusplus
}
#endif

/** @} */

#endif /* ZEPHYR_INCLUDE_MEM_ATTR_H_ */
1 change: 1 addition & 0 deletions subsys/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ add_subdirectory(fb)
add_subdirectory(fs)
add_subdirectory(ipc)
add_subdirectory(logging)
add_subdirectory(mem_mgmt)
add_subdirectory(mgmt)
add_subdirectory(modbus)
add_subdirectory(pm)
Expand Down
1 change: 1 addition & 0 deletions subsys/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ source "subsys/ipc/Kconfig"
source "subsys/jwt/Kconfig"
source "subsys/logging/Kconfig"
source "subsys/lorawan/Kconfig"
source "subsys/mem_mgmt/Kconfig"
source "subsys/mgmt/Kconfig"
source "subsys/modbus/Kconfig"
source "subsys/net/Kconfig"
Expand Down
3 changes: 3 additions & 0 deletions subsys/mem_mgmt/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: Apache-2.0

zephyr_sources_ifdef(CONFIG_MEM_ATTR mem_attr.c)
11 changes: 11 additions & 0 deletions subsys/mem_mgmt/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Copyright (c) 2023 Carlo Caione <[email protected]>
# SPDX-License-Identifier: Apache-2.0

config MEM_ATTR
bool "Memory Attributes management library"
help
Enable a small library to manage the memory regions defined in the DT
with a `zephyr,memory-attr` property. This library builds at build
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.
62 changes: 62 additions & 0 deletions subsys/mem_mgmt/mem_attr.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright (c) 2023 Carlo Caione, <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/kernel.h>
#include <zephyr/mem_mgmt/mem_attr.h>

#define _BUILD_MEM_ATTR_REGION(node_id) \
{ \
.dt_addr = DT_REG_ADDR(node_id), \
.dt_size = DT_REG_SIZE(node_id), \
.dt_attr = DT_ENUM_IDX(node_id, zephyr_memory_attr), \
},

static const struct mem_attr_region_t mem_attr_region[] = {
DT_MEMORY_ATTR_FOREACH_STATUS_OKAY_NODE(_BUILD_MEM_ATTR_REGION)
};

const struct mem_attr_region_t *mem_attr_get_regions(void)
{
if (ARRAY_SIZE(mem_attr_region) == 0) {
return NULL;
}

return mem_attr_region;
}

int mem_attr_check_buf(void *v_addr, size_t size, enum dt_memory_attr attr)
{
uintptr_t addr = (uintptr_t) v_addr;

/*
* If MMU is enabled the address of the buffer is a virtual address
* while the addresses in the DT are physical addresses. Given that we
* have no way of knowing whether a mapping exists, we simply bail out.
*/
if (IS_ENABLED(CONFIG_MMU)) {
return -ENOSYS;
}

if (v_addr == NULL || size == 0 || attr >= DT_MEMORY_ATTR_UNKNOWN) {
return -ENOTSUP;
}

for (size_t idx = 0; idx < ARRAY_SIZE(mem_attr_region); idx++) {
const struct mem_attr_region_t *region = &mem_attr_region[idx];
size_t region_end = region->dt_addr + region->dt_size;

/* Check if the buffer is in the region */
if ((addr >= region->dt_addr) && (addr < region_end)) {
/* Check if the buffer is entirely contained in the region */
if ((addr + size) <= region_end) {
/* check if the attribute is correct */
return (region->dt_attr == attr) ? 0 : -EINVAL;
}
return -ENOSPC;
}
}
return -ENOBUFS;
}
8 changes: 8 additions & 0 deletions tests/subsys/mem_mgmt/mem_attr/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(test_mem_attr)

FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})
27 changes: 27 additions & 0 deletions tests/subsys/mem_mgmt/mem_attr/app.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2023 Carlo Caione <[email protected]>
* SPDX-License-Identifier: Apache-2.0
*/

/ {
mem_ram: memory@10000000 {
compatible = "vnd,memory-attr";
reg = <0x10000000 0x1000>;
zephyr,memory-attr = "RAM";
};

mem_ram_nocache: memory@20000000 {
compatible = "vnd,memory-attr";
reg = <0x20000000 0x2000>;
zephyr,memory-attr = "RAM_NOCACHE";
};

mem_ram_disabled: memory@30000000 {
compatible = "vnd,memory-attr";
reg = <0x30000000 0x3000>;
zephyr,memory-attr = "FLASH";
status = "disabled";
};

};

3 changes: 3 additions & 0 deletions tests/subsys/mem_mgmt/mem_attr/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CONFIG_ZTEST=y
CONFIG_ZTEST_NEW_API=y
CONFIG_MEM_ATTR=y
71 changes: 71 additions & 0 deletions tests/subsys/mem_mgmt/mem_attr/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2023 Carlo Caione <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/ztest.h>
#include <zephyr/mem_mgmt/mem_attr.h>

ZTEST(mem_attr, test_mem_attr)
{
const struct mem_attr_region_t *region;

region = mem_attr_get_regions();
zassert_true(region != NULL, "No regions returned");

/*
* Check the data in the regions
*/
zassert_equal(region[0].dt_addr, 0x10000000, "Wrong region address");
zassert_equal(region[0].dt_size, 0x1000, "Wrong region size");
zassert_equal(region[0].dt_attr, DT_MEMORY_ATTR_RAM, "Wrong region address");

zassert_equal(region[1].dt_addr, 0x20000000, "Wrong region address");
zassert_equal(region[1].dt_size, 0x2000, "Wrong region size");
zassert_equal(region[1].dt_attr, DT_MEMORY_ATTR_RAM_NOCACHE, "Wrong region address");

/*
* Check the input sanitization
*/
zassert_equal(mem_attr_check_buf((void *) 0x0, 0x1000, DT_MEMORY_ATTR_RAM),
-ENOTSUP, "Unexpected return value");
zassert_equal(mem_attr_check_buf((void *) 0x10000000, 0x0, DT_MEMORY_ATTR_RAM),
-ENOTSUP, "Unexpected return value");
zassert_equal(mem_attr_check_buf((void *) 0x10000000, 0x100, DT_MEMORY_ATTR_UNKNOWN + 1),
-ENOTSUP, "Unexpected return value");

/*
* Check a buffer with the correct properties
*/
zassert_equal(mem_attr_check_buf((void *) 0x10000100, 0x100, DT_MEMORY_ATTR_RAM),
0, "Unexpected return value");
zassert_equal(mem_attr_check_buf((void *) 0x20000000, 0x2000, DT_MEMORY_ATTR_RAM_NOCACHE),
0, "Unexpected return value");

/*
* Check a buffer with the wrong attribute
*/
zassert_equal(mem_attr_check_buf((void *) 0x20000000, 0x2000, DT_MEMORY_ATTR_RAM),
-EINVAL, "Unexpected return value");

/*
* Check a buffer outsize the regions
*/
zassert_equal(mem_attr_check_buf((void *) 0x40000000, 0x1000, DT_MEMORY_ATTR_RAM),
-ENOBUFS, "Unexpected return value");

/*
* Check a buffer too big for the region
*/
zassert_equal(mem_attr_check_buf((void *) 0x10000000, 0x2000, DT_MEMORY_ATTR_RAM),
-ENOSPC, "Unexpected return value");

/*
* Check a buffer in a disabled region
*/
zassert_equal(mem_attr_check_buf((void *) 0x30000000, 0x1000, DT_MEMORY_ATTR_FLASH),
-ENOBUFS, "Unexpected return value");
}

ZTEST_SUITE(mem_attr, NULL, NULL, NULL, NULL, NULL);
8 changes: 8 additions & 0 deletions tests/subsys/mem_mgmt/mem_attr/testcase.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
common:
platform_allow:
- native_posix
- native_posix_64
integration_platforms:
- native_posix
tests:
mem_mgmt.mem_attr.default: {}

0 comments on commit 7f4e287

Please sign in to comment.