1 /*
2  * Copyright (c) 2017 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/drivers/spi.h>
8 #include <zephyr/syscall_handler.h>
9 #include <string.h>
10 
11 /* This assumes that bufs and buf_copy are copies from the values passed
12  * as syscall arguments.
13  */
copy_and_check(struct spi_buf_set * bufs,struct spi_buf * buf_copy,int writable)14 static struct spi_buf_set *copy_and_check(struct spi_buf_set *bufs,
15 					  struct spi_buf *buf_copy,
16 					  int writable)
17 {
18 	size_t i;
19 
20 	if (bufs->count == 0) {
21 		bufs->buffers = NULL;
22 		return NULL;
23 	}
24 
25 	/* Validate the array of struct spi_buf instances */
26 	Z_OOPS(Z_SYSCALL_MEMORY_ARRAY_READ(bufs->buffers,
27 					   bufs->count,
28 					   sizeof(struct spi_buf)));
29 
30 	/* Not worried about overflow here: _SYSCALL_MEMORY_ARRAY_READ()
31 	 * takes care of it.
32 	 */
33 	bufs->buffers = memcpy(buf_copy,
34 			       bufs->buffers,
35 			       bufs->count * sizeof(struct spi_buf));
36 
37 	for (i = 0; i < bufs->count; i++) {
38 		/* Now for each array element, validate the memory buffers
39 		 * that they point to
40 		 */
41 		const struct spi_buf *buf = &bufs->buffers[i];
42 
43 		Z_OOPS(Z_SYSCALL_MEMORY(buf->buf, buf->len, writable));
44 	}
45 
46 	return bufs;
47 }
48 
49 /* This function is only here so tx_buf_copy and rx_buf_copy can be allocated
50  * using VLA.  It assumes that both tx_bufs and rx_bufs will receive a copy of
51  * the values passed to the syscall as arguments.  It also assumes that the
52  * count member has been verified and is a value that won't lead to stack
53  * overflow.
54  */
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)55 static uint32_t copy_bufs_and_transceive(const struct device *dev,
56 					 const struct spi_config *config,
57 					 struct spi_buf_set *tx_bufs,
58 					 struct spi_buf_set *rx_bufs)
59 {
60 	struct spi_buf tx_buf_copy[tx_bufs->count ? tx_bufs->count : 1];
61 	struct spi_buf rx_buf_copy[rx_bufs->count ? rx_bufs->count : 1];
62 
63 	tx_bufs = copy_and_check(tx_bufs, tx_buf_copy, 0);
64 	rx_bufs = copy_and_check(rx_bufs, rx_buf_copy, 1);
65 
66 	return z_impl_spi_transceive((const struct device *)dev, config,
67 				     tx_bufs, rx_bufs);
68 }
69 
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)70 static inline int z_vrfy_spi_transceive(const struct device *dev,
71 					const struct spi_config *config,
72 					const struct spi_buf_set *tx_bufs,
73 					const struct spi_buf_set *rx_bufs)
74 {
75 	struct spi_buf_set tx_bufs_copy;
76 	struct spi_buf_set rx_bufs_copy;
77 	struct spi_config config_copy;
78 
79 	Z_OOPS(Z_SYSCALL_MEMORY_READ(config, sizeof(*config)));
80 	Z_OOPS(Z_SYSCALL_DRIVER_SPI(dev, transceive));
81 
82 	if (tx_bufs) {
83 		const struct spi_buf_set *tx =
84 			(const struct spi_buf_set *)tx_bufs;
85 
86 		Z_OOPS(Z_SYSCALL_MEMORY_READ(tx_bufs,
87 					     sizeof(struct spi_buf_set)));
88 		memcpy(&tx_bufs_copy, tx, sizeof(tx_bufs_copy));
89 		Z_OOPS(Z_SYSCALL_VERIFY(tx_bufs_copy.count < 32));
90 	} else {
91 		memset(&tx_bufs_copy, 0, sizeof(tx_bufs_copy));
92 	}
93 
94 	if (rx_bufs) {
95 		const struct spi_buf_set *rx =
96 			(const struct spi_buf_set *)rx_bufs;
97 
98 		Z_OOPS(Z_SYSCALL_MEMORY_READ(rx_bufs,
99 					     sizeof(struct spi_buf_set)));
100 		memcpy(&rx_bufs_copy, rx, sizeof(rx_bufs_copy));
101 		Z_OOPS(Z_SYSCALL_VERIFY(rx_bufs_copy.count < 32));
102 	} else {
103 		memset(&rx_bufs_copy, 0, sizeof(rx_bufs_copy));
104 	}
105 
106 	memcpy(&config_copy, config, sizeof(*config));
107 	if (spi_cs_is_gpio(&config_copy)) {
108 		Z_OOPS(Z_SYSCALL_OBJ(config_copy.cs.gpio.port,
109 				     K_OBJ_DRIVER_GPIO));
110 	}
111 
112 	return copy_bufs_and_transceive((const struct device *)dev,
113 					&config_copy,
114 					&tx_bufs_copy,
115 					&rx_bufs_copy);
116 }
117 #include <syscalls/spi_transceive_mrsh.c>
118 
z_vrfy_spi_release(const struct device * dev,const struct spi_config * config)119 static inline int z_vrfy_spi_release(const struct device *dev,
120 				     const struct spi_config *config)
121 {
122 	Z_OOPS(Z_SYSCALL_MEMORY_READ(config, sizeof(*config)));
123 	Z_OOPS(Z_SYSCALL_DRIVER_SPI(dev, release));
124 	return z_impl_spi_release((const struct device *)dev, config);
125 }
126 #include <syscalls/spi_release_mrsh.c>
127