drivers: flash: provide a generic flash_copy() algorithm
Provide a generic flash_copy() algorithm that is capable of copying from one flash device to another or within different regions of the same flash device. Signed-off-by: Chris Friedt <cfriedt@tenstorrent.com>
This commit is contained in:
parent
6bc0970c40
commit
ced4e16235
2 changed files with 152 additions and 1 deletions
|
|
@ -6,10 +6,17 @@
|
|||
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
/* FIXME: use k_off_t instead of off_t */
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <zephyr/drivers/flash.h>
|
||||
#include <zephyr/internal/syscall_handler.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <string.h>
|
||||
#include <zephyr/sys/math_extras.h>
|
||||
|
||||
LOG_MODULE_REGISTER(flash, CONFIG_FLASH_LOG_LEVEL);
|
||||
|
||||
|
|
@ -80,3 +87,116 @@ int z_impl_flash_flatten(const struct device *dev, off_t offset, size_t size)
|
|||
return -ENOSYS;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* note: caller must first check for positivity (>=0) */
|
||||
static inline bool off_add_overflow(off_t offset, off_t size, off_t *result)
|
||||
{
|
||||
BUILD_ASSERT((sizeof(off_t) == sizeof(uint32_t)) || (sizeof(off_t) == sizeof(uint64_t)));
|
||||
|
||||
if (sizeof(off_t) == sizeof(uint32_t)) {
|
||||
uint32_t end;
|
||||
|
||||
/* account for signedness of off_t due to lack of s32_add_overflow() */
|
||||
if (u32_add_overflow((uint32_t)offset, (uint32_t)size, &end) || (end > INT32_MAX)) {
|
||||
return true;
|
||||
}
|
||||
} else if (sizeof(off_t) == sizeof(uint64_t)) {
|
||||
uint64_t end;
|
||||
|
||||
/* account for signedness of off_t due to lack of s64_add_overflow() */
|
||||
if (u64_add_overflow((uint64_t)offset, (uint64_t)size, &end) || (end > INT64_MAX)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* note: caller must first check for overflow */
|
||||
static inline bool flash_ranges_overlap(off_t a_start, off_t a_size, off_t b_start, off_t b_size)
|
||||
{
|
||||
off_t a_end = a_start + a_size;
|
||||
off_t b_end = b_start + b_size;
|
||||
|
||||
return (a_start < b_end) && (a_end > b_start);
|
||||
}
|
||||
|
||||
int z_impl_flash_copy(const struct device *src_dev, off_t src_offset, const struct device *dst_dev,
|
||||
off_t dst_offset, off_t size, uint8_t *buf, size_t buf_size)
|
||||
{
|
||||
int ret;
|
||||
off_t end;
|
||||
size_t write_size;
|
||||
|
||||
if ((src_offset < 0) || (dst_offset < 0) || (size < 0) || (buf == NULL) ||
|
||||
(buf_size == 0) || off_add_overflow(src_offset, size, &end) ||
|
||||
off_add_overflow(dst_offset, size, &end)) {
|
||||
LOG_DBG("invalid argument");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (src_dev == dst_dev) {
|
||||
if (src_offset == dst_offset) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (flash_ranges_overlap(src_offset, size, dst_offset, size) != 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!device_is_ready(src_dev)) {
|
||||
LOG_DBG("%s device not ready", "src");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!device_is_ready(dst_dev)) {
|
||||
LOG_DBG("%s device not ready", "dst");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
write_size = flash_get_write_block_size(dst_dev);
|
||||
if ((buf_size < write_size) || ((buf_size % write_size) != 0)) {
|
||||
LOG_DBG("buf size %zu is incompatible with write_size of %zu", buf_size,
|
||||
write_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (uint32_t offs = 0, N = size, bytes_read = 0, bytes_left = N; offs < N;
|
||||
offs += bytes_read, bytes_left -= bytes_read) {
|
||||
|
||||
if (bytes_left < write_size) {
|
||||
const struct flash_driver_api *api =
|
||||
(const struct flash_driver_api *)dst_dev->api;
|
||||
const struct flash_parameters *params = api->get_parameters(dst_dev);
|
||||
|
||||
memset(buf, params->erase_value, write_size);
|
||||
}
|
||||
bytes_read = MIN(MAX(bytes_left, write_size), buf_size);
|
||||
ret = flash_read(src_dev, src_offset + offs, buf, bytes_read);
|
||||
if (ret < 0) {
|
||||
LOG_DBG("%s() failed at offset %lx: %d", "flash_read",
|
||||
(long)(src_offset + offs), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = flash_write(dst_dev, dst_offset + offs, buf, bytes_read);
|
||||
if (ret < 0) {
|
||||
LOG_DBG("%s() failed at offset %lx: %d", "flash_write",
|
||||
(long)(src_offset + offs), ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USERSPACE
|
||||
int z_vrfy_flash_copy(const struct device *src_dev, off_t src_offset, const struct device *dst_dev,
|
||||
off_t dst_offset, off_t size, uint8_t *buf, size_t buf_size)
|
||||
{
|
||||
K_OOPS(K_SYSCALL_MEMORY_WRITE(buf, buf_size));
|
||||
return z_impl_flash_copy(src_dev, src_offset, dst_dev, dst_offset, size, buf, buf_size);
|
||||
}
|
||||
#include <zephyr/syscalls/flash_copy_mrsh.c>
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -583,6 +583,37 @@ static inline const struct flash_parameters *z_impl_flash_get_parameters(const s
|
|||
__syscall int flash_ex_op(const struct device *dev, uint16_t code,
|
||||
const uintptr_t in, void *out);
|
||||
|
||||
/**
|
||||
* @brief Copy flash memory from one device to another.
|
||||
*
|
||||
* Copy a region of flash memory from one place to another. The source and
|
||||
* destination flash devices may be the same or different devices. However,
|
||||
* this function will fail if the source and destination devices are the same
|
||||
* if memory regions overlap and are not identical.
|
||||
*
|
||||
* The caller must supply a buffer of suitable size and ensure that the
|
||||
* destination is erased beforehand, if necessary.
|
||||
*
|
||||
* @note If the source and destination devices are the same, and the source
|
||||
* and destination offsets are also the same, this function succeeds without
|
||||
* performing any copy operation.
|
||||
*
|
||||
* @param src_dev Source flash device.
|
||||
* @param dst_dev Destination flash device.
|
||||
* @param src_offset Offset within the source flash device.
|
||||
* @param dst_offset Offset within the destination flash device.
|
||||
* @param size Size of the region to copy, in bytes.
|
||||
* @param[out] buf Pointer to a buffer of size @a buf_size.
|
||||
* @param buf_size Size of the buffer pointed to by @a buf.
|
||||
*
|
||||
* @retval 0 on success
|
||||
* @retval -EINVAL if an argument is invalid.
|
||||
* @retval -EIO if an I/O error occurs.
|
||||
* @retval -ENODEV if either @a src_dev or @a dst_dev are not ready.
|
||||
*/
|
||||
__syscall int flash_copy(const struct device *src_dev, off_t src_offset,
|
||||
const struct device *dst_dev, off_t dst_offset, off_t size, uint8_t *buf,
|
||||
size_t buf_size);
|
||||
/*
|
||||
* Extended operation interface provides flexible way for supporting flash
|
||||
* controller features. Code space is divided equally into Zephyr codes
|
||||
|
|
|
|||
Loading…
Reference in a new issue