|  | /* | 
|  | * Copyright (c) 2017 Intel Corporation | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #include <drivers/spi.h> | 
|  | #include <syscall_handler.h> | 
|  | #include <string.h> | 
|  |  | 
|  | /* This assumes that bufs and buf_copy are copies from the values passed | 
|  | * as syscall arguments. | 
|  | */ | 
|  | static struct spi_buf_set *copy_and_check(struct spi_buf_set *bufs, | 
|  | struct spi_buf *buf_copy, | 
|  | int writable) | 
|  | { | 
|  | size_t i; | 
|  |  | 
|  | if (bufs->count == 0) { | 
|  | bufs->buffers = NULL; | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Validate the array of struct spi_buf instances */ | 
|  | Z_OOPS(Z_SYSCALL_MEMORY_ARRAY_READ(bufs->buffers, | 
|  | bufs->count, | 
|  | sizeof(struct spi_buf))); | 
|  |  | 
|  | /* Not worried about overflow here: _SYSCALL_MEMORY_ARRAY_READ() | 
|  | * takes care of it. | 
|  | */ | 
|  | bufs->buffers = memcpy(buf_copy, | 
|  | bufs->buffers, | 
|  | bufs->count * sizeof(struct spi_buf)); | 
|  |  | 
|  | for (i = 0; i < bufs->count; i++) { | 
|  | /* Now for each array element, validate the memory buffers | 
|  | * that they point to | 
|  | */ | 
|  | const struct spi_buf *buf = &bufs->buffers[i]; | 
|  |  | 
|  | Z_OOPS(Z_SYSCALL_MEMORY(buf->buf, buf->len, writable)); | 
|  | } | 
|  |  | 
|  | return bufs; | 
|  | } | 
|  |  | 
|  | /* This function is only here so tx_buf_copy and rx_buf_copy can be allocated | 
|  | * using VLA.  It assumes that both tx_bufs and rx_bufs will receive a copy of | 
|  | * the values passed to the syscall as arguments.  It also assumes that the | 
|  | * count member has been verified and is a value that won't lead to stack | 
|  | * overflow. | 
|  | */ | 
|  | static uint32_t copy_bufs_and_transceive(const struct device *dev, | 
|  | const struct spi_config *config, | 
|  | struct spi_buf_set *tx_bufs, | 
|  | struct spi_buf_set *rx_bufs) | 
|  | { | 
|  | struct spi_buf tx_buf_copy[tx_bufs->count ? tx_bufs->count : 1]; | 
|  | struct spi_buf rx_buf_copy[rx_bufs->count ? rx_bufs->count : 1]; | 
|  |  | 
|  | tx_bufs = copy_and_check(tx_bufs, tx_buf_copy, 0); | 
|  | rx_bufs = copy_and_check(rx_bufs, rx_buf_copy, 1); | 
|  |  | 
|  | return z_impl_spi_transceive((const struct device *)dev, config, | 
|  | tx_bufs, rx_bufs); | 
|  | } | 
|  |  | 
|  | static inline int z_vrfy_spi_transceive(const struct device *dev, | 
|  | const struct spi_config *config, | 
|  | const struct spi_buf_set *tx_bufs, | 
|  | const struct spi_buf_set *rx_bufs) | 
|  | { | 
|  | struct spi_buf_set tx_bufs_copy; | 
|  | struct spi_buf_set rx_bufs_copy; | 
|  | struct spi_config config_copy; | 
|  |  | 
|  | Z_OOPS(Z_SYSCALL_MEMORY_READ(config, sizeof(*config))); | 
|  | Z_OOPS(Z_SYSCALL_DRIVER_SPI(dev, transceive)); | 
|  |  | 
|  | if (tx_bufs) { | 
|  | const struct spi_buf_set *tx = | 
|  | (const struct spi_buf_set *)tx_bufs; | 
|  |  | 
|  | Z_OOPS(Z_SYSCALL_MEMORY_READ(tx_bufs, | 
|  | sizeof(struct spi_buf_set))); | 
|  | memcpy(&tx_bufs_copy, tx, sizeof(tx_bufs_copy)); | 
|  | Z_OOPS(Z_SYSCALL_VERIFY(tx_bufs_copy.count < 32)); | 
|  | } else { | 
|  | memset(&tx_bufs_copy, 0, sizeof(tx_bufs_copy)); | 
|  | } | 
|  |  | 
|  | if (rx_bufs) { | 
|  | const struct spi_buf_set *rx = | 
|  | (const struct spi_buf_set *)rx_bufs; | 
|  |  | 
|  | Z_OOPS(Z_SYSCALL_MEMORY_READ(rx_bufs, | 
|  | sizeof(struct spi_buf_set))); | 
|  | memcpy(&rx_bufs_copy, rx, sizeof(rx_bufs_copy)); | 
|  | Z_OOPS(Z_SYSCALL_VERIFY(rx_bufs_copy.count < 32)); | 
|  | } else { | 
|  | memset(&rx_bufs_copy, 0, sizeof(rx_bufs_copy)); | 
|  | } | 
|  |  | 
|  | memcpy(&config_copy, config, sizeof(*config)); | 
|  | if (config_copy.cs) { | 
|  | const struct spi_cs_control *cs = config_copy.cs; | 
|  |  | 
|  | Z_OOPS(Z_SYSCALL_MEMORY_READ(cs, sizeof(*cs))); | 
|  | if (cs->gpio_dev) { | 
|  | Z_OOPS(Z_SYSCALL_OBJ(cs->gpio_dev, K_OBJ_DRIVER_GPIO)); | 
|  | } | 
|  | } | 
|  |  | 
|  | return copy_bufs_and_transceive((const struct device *)dev, | 
|  | &config_copy, | 
|  | &tx_bufs_copy, | 
|  | &rx_bufs_copy); | 
|  | } | 
|  | #include <syscalls/spi_transceive_mrsh.c> | 
|  |  | 
|  | static inline int z_vrfy_spi_release(const struct device *dev, | 
|  | const struct spi_config *config) | 
|  | { | 
|  | Z_OOPS(Z_SYSCALL_MEMORY_READ(config, sizeof(*config))); | 
|  | Z_OOPS(Z_SYSCALL_DRIVER_SPI(dev, release)); | 
|  | return z_impl_spi_release((const struct device *)dev, config); | 
|  | } | 
|  | #include <syscalls/spi_release_mrsh.c> |