diff --git a/drivers/flash/flash_handlers.c b/drivers/flash/flash_handlers.c index c304e8c6ef4ff..deb7995c96d9b 100644 --- a/drivers/flash/flash_handlers.c +++ b/drivers/flash/flash_handlers.c @@ -45,6 +45,18 @@ static inline int z_vrfy_flash_get_size(const struct device *dev, uint64_t *size } #include +static inline int z_vrfy_flash_mmap(const struct device *dev, void **base, + uint64_t *size, uint32_t flags) +{ + K_OOPS(K_SYSCALL_OBJ(dev, K_OBJ_DRIVER_FLASH)); + K_OOPS(K_SYSCALL_MEMORY_READ(*base, sizeof(*base))); + K_OOPS(K_SYSCALL_MEMORY_WRITE(*base, sizeof(*base))); + K_OOPS(K_SYSCALL_MEMORY_READ(size, sizeof(*size))); + K_OOPS(K_SYSCALL_MEMORY_WRITE(size, sizeof(*size))); + return z_impl_flash_mmap((const struct device *)dev, base, size, flags); +} +#include + static inline size_t z_vrfy_flash_get_write_block_size(const struct device *dev) { K_OOPS(K_SYSCALL_OBJ(dev, K_OBJ_DRIVER_FLASH)); diff --git a/drivers/flash/flash_simulator.c b/drivers/flash/flash_simulator.c index e9795d68440e1..254c82d666dd4 100644 --- a/drivers/flash/flash_simulator.c +++ b/drivers/flash/flash_simulator.c @@ -372,6 +372,22 @@ static int flash_sim_get_size(const struct device *dev, uint64_t *size) return 0; } + +static int flash_sim_mmap(const struct device *dev, void **base, uint64_t *size, + uint32_t flags) +{ + ARG_UNUSED(dev); + + if (flags & ~(FLASH_MMAP_F_READ | FLASH_MMAP_F_WRITE)) { + return -EINVAL; + } + + *base = (void *)&mock_flash[0]; + *size = (uint64_t)FLASH_SIMULATOR_FLASH_SIZE; + + return 0; +} + static const struct flash_parameters * flash_sim_get_parameters(const struct device *dev) { @@ -384,6 +400,7 @@ static DEVICE_API(flash, flash_sim_api) = { .read = flash_sim_read, .write = flash_sim_write, .erase = flash_sim_erase, + .mmap = flash_sim_mmap, .get_parameters = flash_sim_get_parameters, .get_size = flash_sim_get_size, #ifdef CONFIG_FLASH_PAGE_LAYOUT diff --git a/drivers/flash/soc_flash_nrf.c b/drivers/flash/soc_flash_nrf.c index 9e1ba68319ffe..4ffd410dd2a83 100644 --- a/drivers/flash/soc_flash_nrf.c +++ b/drivers/flash/soc_flash_nrf.c @@ -270,6 +270,20 @@ static int flash_nrf_get_size(const struct device *dev, uint64_t *size) return 0; } +static int flash_nrf_mmap(const struct device *dev, void **base, uint64_t *size, + uint32_t flags) +{ + /* Currently supporting read operation only */ + if (flags & ~(FLASH_MMAP_F_READ)) { + return -EINVAL; + } + + *base = (void *)DT_REG_ADDR(SOC_NV_FLASH_NODE); + *size = nrfx_nvmc_flash_size_get(); + + return 0; +} + #if defined(CONFIG_FLASH_PAGE_LAYOUT) static struct flash_pages_layout dev_layout; @@ -294,6 +308,7 @@ static DEVICE_API(flash, flash_nrf_api) = { .read = flash_nrf_read, .write = flash_nrf_write, .erase = flash_nrf_erase, + .mmap = flash_nrf_mmap, .get_parameters = flash_nrf_get_parameters, .get_size = flash_nrf_get_size, #if defined(CONFIG_FLASH_PAGE_LAYOUT) diff --git a/include/zephyr/drivers/flash.h b/include/zephyr/drivers/flash.h index b72e551eafa1f..eae60ca6028d9 100644 --- a/include/zephyr/drivers/flash.h +++ b/include/zephyr/drivers/flash.h @@ -169,6 +169,15 @@ typedef int (*flash_api_get_size)(const struct device *dev, uint64_t *size); typedef const struct flash_parameters* (*flash_api_get_parameters)(const struct device *dev); +/** + * @brief Get device mapping to processor address space. + * + * When supported by driver this function will try to map device to MPU address + * space, either to default memory address or to requeste one. + */ +typedef int (*flash_api_mmap)(const struct device *dev, void **base, uint64_t *size, + uint32_t flags); + #if defined(CONFIG_FLASH_PAGE_LAYOUT) /** * @brief Retrieve a flash device's layout. @@ -208,6 +217,7 @@ __subsystem struct flash_driver_api { flash_api_erase erase; flash_api_get_parameters get_parameters; flash_api_get_size get_size; + flash_api_mmap mmap; #if defined(CONFIG_FLASH_PAGE_LAYOUT) flash_api_pages_layout page_layout; #endif /* CONFIG_FLASH_PAGE_LAYOUT */ @@ -334,6 +344,56 @@ static inline int z_impl_flash_erase(const struct device *dev, off_t offset, return rc; } +/** Map at custom address */ +#define FLASH_MMAP_F_CUSTOM_ADDR BIT(0) +/** Map for read */ +#define FLASH_MMAP_F_READ BIT(1) +/** Map for write */ +#define FLASH_MMAP_F_WRITE BIT(2) + +/** + * @brief Get device mapping to processor address space + * + * Function returns base address for memory mapping of device to address space + * and size of the device mapped. Internal SoC memory and some XIP memories + * may be addressable through the same address space as device RAM, which allows + * data transfers via memcpy or direct access to storage by casting address + * range to data structures. + * Not all processors may support the feature and its usage should be well + * tested in user code, because while, for example, flash_read would return + * -EINVAL error in case when attempting read out of device range, directly + * accessing memory beyond given range may invoke CPU fault. + * + * @param dev device to get mapping for + * @param base pointer to void pointer for memory address, in case when + * FLASH_MMAP_CUSTOM_ADDR flag is set this is also read as desired + * address to map device to. + * @param size pointer to variable where device size will be reported. + * @param flags flags describing desired access and mapping. + * + * @return 0 on success, -ENOTSUP when driver does not support mapping, + * -EINVAL in case, when selected flags are not supported. + */ + +__syscall int flash_mmap(const struct device *dev, void **base, uint64_t *size, + uint32_t flags); + +static inline int z_impl_flash_mmap(const struct device *dev, void **base, + uint64_t *size, uint32_t flags) +{ + int rc = -ENOSYS; + + const struct flash_driver_api *api = + (const struct flash_driver_api *)dev->api; + + if (api->mmap != NULL) { + rc = api->mmap(dev, base, size, flags); + } + + return rc; +} + + /** * @brief Get device size in bytes. * diff --git a/tests/drivers/flash/common/src/main.c b/tests/drivers/flash/common/src/main.c index e7552aaf177b2..49022060949a8 100644 --- a/tests/drivers/flash/common/src/main.c +++ b/tests/drivers/flash/common/src/main.c @@ -307,6 +307,25 @@ ZTEST(flash_driver, test_flash_erase) zassert_not_equal(expected[0], erase_value, "These values shall be different"); } +ZTEST(flash_driver, test_mmap) +{ + int rc; + uint64_t size; + uint8_t *p; + + rc = flash_mmap(flash_dev, (void **)&p, &size, FLASH_MMAP_F_READ); + if (rc == -ENOTSUP || rc == -ENOSYS) { + ztest_test_skip(); + } else { + const static uint8_t out[] = "Hello World!!!\n"; + struct flash_pages_info fp; + + zassert_ok(flash_get_page_info_by_offs(flash_dev, size - 1, &fp)); + zassert_ok(flash_erase(flash_dev, fp.start_offset, fp.size)); + zassert_ok(flash_write(flash_dev, fp.start_offset, out, sizeof(out))); + zassert_ok(memcmp(p + fp.start_offset, out, sizeof(out))); + } +} struct test_cb_data_type { uint32_t page_counter; /* used to count how many pages was iterated */ uint32_t exit_page; /* terminate iteration when this page is reached */ diff --git a/tests/drivers/flash_api/src/main.c b/tests/drivers/flash_api/src/main.c index d315895037da3..05a66fbda44c5 100644 --- a/tests/drivers/flash_api/src/main.c +++ b/tests/drivers/flash_api/src/main.c @@ -20,12 +20,16 @@ static struct { int ret; /* Some size */ uint64_t size; + /* Device mmap test values */ + void *mmap_base; + ssize_t mmap_size; + uint32_t mmap_flags; } simulated_values = { .ret = 0, .size = 0, }; -/*** Device definition atd == API Test Dev ***/ +/*** Device definition of API pseudo functions **/ static int some_get_size(const struct device *dev, uint64_t *size) { __ASSERT_NO_MSG(dev != NULL); @@ -42,6 +46,36 @@ static int enotsup_get_size(const struct device *dev, uint64_t *size) return -ENOTSUP; } +static int some_mmap(const struct device *dev, void **base, uint64_t *size, + uint32_t flags) +{ + __ASSERT_NO_MSG(dev != NULL); + + if (flags != simulated_values.mmap_flags) { + return -EINVAL; + } + + if (base == NULL || size == NULL) { + return -EINVAL; + } + + *base = simulated_values.mmap_base; + *size = simulated_values.mmap_size; + + return 0; +} + +static int enotsup_mmap(const struct device *dev, void **base, uint64_t *size, + uint32_t flags) +{ + ARG_UNUSED(dev); + ARG_UNUSED(base); + ARG_UNUSED(size); + ARG_UNUSED(flags); + + return 0; +} + /** Device objects **/ /* The device state, just to make it "ready" device */ static struct device_state some_dev_state = { @@ -49,14 +83,16 @@ static struct device_state some_dev_state = { .initialized = 1, }; -/* Device with get_size */ -static DEVICE_API(flash, size_fun_api) = { +/* Device with implemented api calls */ +static DEVICE_API(flash, some_fun_api) = { .get_size = some_get_size, + .mmap = some_mmap, }; -const static struct device size_fun_dev = { - "get_size", + +const static struct device some_fun_dev = { + "some_fun", NULL, - &size_fun_api, + &some_fun_api, &some_dev_state, }; @@ -72,6 +108,7 @@ const static struct device no_fun_dev = { /* Device with get_size implemented but returning -ENOTSUP */ static DEVICE_API(flash, enotsup_fun_api) = { .get_size = enotsup_get_size, + .mmap = enotsup_mmap, }; static struct device enotsup_fun_dev = { "enotsup", @@ -85,14 +122,53 @@ ZTEST(flash_api, test_get_size) uint64_t size = 0; simulated_values.size = 45; - zassert_ok(flash_get_size(&size_fun_dev, &size), "Expected success"); + zassert_ok(flash_get_size(&some_fun_dev, &size), "Expected success"); zassert_equal(size, simulated_values.size, "Size mismatch"); simulated_values.size = 46; - zassert_ok(flash_get_size(&size_fun_dev, &size), "Expected success"); + zassert_ok(flash_get_size(&some_fun_dev, &size), "Expected success"); zassert_equal(size, simulated_values.size, "Size mismatch"); zassert_equal(flash_get_size(&no_fun_dev, &size), -ENOSYS); zassert_equal(flash_get_size(&enotsup_fun_dev, &size), -ENOTSUP); } + +ZTEST(flash_api, test_flash_mmap) +{ + void *base = NULL; + uint64_t size = 0; + + simulated_values.mmap_size = 40; + /* Just need some pointer */ + simulated_values.mmap_base = (void *)&simulated_values; + /* 0 for error tests */ + simulated_values.mmap_flags = 0; + + zassert_equal(flash_mmap(&some_fun_dev, NULL, NULL, 0), -EINVAL); + zassert_equal(flash_mmap(&some_fun_dev, &base, NULL, 0), -EINVAL); + zassert_equal(flash_mmap(&some_fun_dev, NULL, &size, 0), -EINVAL); + zassert_equal(flash_mmap(&some_fun_dev, &base, &size, FLASH_MMAP_F_READ), -EINVAL); + zassert_equal(flash_mmap(&some_fun_dev, &base, &size, FLASH_MMAP_F_WRITE), -EINVAL); + zassert_equal(flash_mmap(&some_fun_dev, &base, &size, + FLASH_MMAP_F_READ | FLASH_MMAP_F_WRITE), -EINVAL); + + simulated_values.mmap_flags = FLASH_MMAP_F_READ; + zassert_equal(flash_mmap(&some_fun_dev, &base, &size, FLASH_MMAP_F_WRITE), -EINVAL); + + simulated_values.mmap_flags = FLASH_MMAP_F_WRITE; + zassert_equal(flash_mmap(&some_fun_dev, &base, &size, FLASH_MMAP_F_READ), -EINVAL); + + zassert_equal(flash_get_size(&enotsup_fun_dev, &size), -ENOTSUP); + + /* After all failures the base and size are expected to be not modified */ + zassert_equal(base, NULL); + zassert_equal(size, 0); + + simulated_values.mmap_flags = FLASH_MMAP_F_READ; + zassert_ok(flash_mmap(&some_fun_dev, &base, &size, FLASH_MMAP_F_READ)); + /* Expected values to be read by API and updated */ + zassert_equal(base, simulated_values.mmap_base); + zassert_equal(size, simulated_values.mmap_size); +} + ZTEST_SUITE(flash_api, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/drivers/flash_api/testcase.yaml b/tests/drivers/flash_api/testcase.yaml index 97f03eb4915d1..e66bbf239253f 100644 --- a/tests/drivers/flash_api/testcase.yaml +++ b/tests/drivers/flash_api/testcase.yaml @@ -17,5 +17,9 @@ tests: - driver - flash - userspace + platform_allow: + - nrf52840dk/nrf52840 + integration_platforms: + - nrf52840dk/nrf52840 extra_configs: - CONFIG_USERSPACE=y diff --git a/tests/drivers/flash_simulator/flash_sim_impl/src/main.c b/tests/drivers/flash_simulator/flash_sim_impl/src/main.c index 87eff3986c4ba..0366c18a940eb 100644 --- a/tests/drivers/flash_simulator/flash_sim_impl/src/main.c +++ b/tests/drivers/flash_simulator/flash_sim_impl/src/main.c @@ -498,6 +498,53 @@ ZTEST(flash_sim_api, test_flash_fill) } } +ZTEST(flash_sim_api, test_flash_mmap) +{ + const uint8_t out[] = "Hello world!!!\n"; + uint8_t in[64]; + uint8_t *p; + uint64_t size; + int rc; +#if defined(CONFIG_FLASH_SIMULATOR_EXPLICIT_ERASE) + rc = flash_erase(flash_dev, FLASH_SIMULATOR_BASE_OFFSET, + FLASH_SIMULATOR_FLASH_SIZE); + zassert_equal(0, rc, "flash_erase should succeed"); +#else + rc = flash_fill(flash_dev, FLASH_SIMULATOR_ERASE_VALUE, + FLASH_SIMULATOR_BASE_OFFSET, + FLASH_SIMULATOR_FLASH_SIZE); + zassert_equal(0, rc, "flash_fill should succeed"); +#endif + zassert_ok(flash_mmap(flash_dev, (void **)&p, &size, FLASH_MMAP_F_READ)); + zassert_ok(flash_write(flash_dev, FLASH_SIMULATOR_BASE_OFFSET, out, + sizeof(out))); + + zassert_ok(memcmp(p, out, sizeof(out))); + + zassert_ok(flash_read(flash_dev, FLASH_SIMULATOR_BASE_OFFSET, in, + sizeof(out))); + zassert_ok(memcmp(out, in, sizeof(out))); + +#if defined(CONFIG_FLASH_SIMULATOR_EXPLICIT_ERASE) + rc = flash_erase(flash_dev, FLASH_SIMULATOR_BASE_OFFSET, + FLASH_SIMULATOR_FLASH_SIZE); + zassert_equal(0, rc, "flash_erase should succeed"); +#else + rc = flash_fill(flash_dev, FLASH_SIMULATOR_ERASE_VALUE, + FLASH_SIMULATOR_BASE_OFFSET, + FLASH_SIMULATOR_FLASH_SIZE); + zassert_equal(0, rc, "flash_fill should succeed"); +#endif + zassert_ok(flash_mmap(flash_dev, (void **)&p, &size, FLASH_MMAP_F_WRITE)); + memset(in, 0, sizeof(in)); + /* Write via memcpy */ + memcpy(p, out, sizeof(out)); + zassert_ok(memcmp(p, out, sizeof(out))); + zassert_ok(flash_read(flash_dev, FLASH_SIMULATOR_BASE_OFFSET, in, + sizeof(out))); + zassert_ok(memcmp(out, in, sizeof(out))); +} + ZTEST(flash_sim_api, test_flash_flatten) { int rc;