1 /*
2  * Copyright (c) 2023 Google LLC
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/drivers/uart.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/mgmt/ec_host_cmd/ec_host_cmd.h>
10 #include <zephyr/shell/shell_dummy.h>
11 #include <zephyr/types.h>
12 #include <zephyr/ztest.h>
13 #include <zephyr/ztest_assert.h>
14 #include <zephyr/ztest_mock.h>
15 
16 #include "uart_mock.h"
17 
18 #define CMD_HEADER_SIZE		   (sizeof(struct ec_host_cmd_request_header))
19 #define RSP_HEADER_SIZE		   (sizeof(struct ec_host_cmd_response_header))
20 /* Recovery time for the backend from invalid command. It has to be bigger than the RX timeout. */
21 #define UART_BACKEND_RECOVERY_TIME K_MSEC(160)
22 #define MAX_RESP_WAIT_TIME	   K_MSEC(1)
23 
cal_checksum(const uint8_t * const buffer,const uint16_t size)24 static uint8_t cal_checksum(const uint8_t *const buffer, const uint16_t size)
25 {
26 	uint8_t checksum = 0;
27 
28 	for (size_t i = 0; i < size; ++i) {
29 		checksum += buffer[i];
30 	}
31 	return (uint8_t)(-checksum);
32 }
33 
tx_done(void)34 static void tx_done(void)
35 {
36 	struct uart_event evt;
37 	struct uart_mock_data *data = uart_mock.data;
38 
39 	/* Prepare UART event passed to the UART callback */
40 	evt.type = UART_TX_DONE;
41 	evt.data.tx.buf = data->tx_buf;
42 	evt.data.tx.len = data->tx_len;
43 
44 	data->cb(&uart_mock, &evt, data->user_data);
45 }
46 
47 #define EC_CMD_HELLO 0x0001
48 #define EC_HELLO_STR "hello_ec"
49 
50 const static uint8_t hello_magic[4] = {0xAB, 0xBC, 0xDE, 0xF1};
51 struct hello_cmd_data {
52 	uint8_t magic[sizeof(hello_magic)];
53 } __packed;
54 
ec_host_cmd_hello(struct ec_host_cmd_handler_args * args)55 static enum ec_host_cmd_status ec_host_cmd_hello(struct ec_host_cmd_handler_args *args)
56 {
57 	const struct hello_cmd_data *cmd_data = args->input_buf;
58 
59 	args->output_buf_size = 0;
60 
61 	if (args->version != 0) {
62 		zassert_unreachable("Should not get version %d", args->version);
63 		return EC_HOST_CMD_INVALID_VERSION;
64 	}
65 
66 	if (args->input_buf_size != sizeof(struct hello_cmd_data)) {
67 		return EC_HOST_CMD_INVALID_PARAM;
68 	}
69 
70 	if (memcmp(hello_magic, cmd_data->magic, sizeof(hello_magic))) {
71 		return EC_HOST_CMD_INVALID_PARAM;
72 	}
73 
74 	memcpy(args->output_buf, EC_HELLO_STR, sizeof(EC_HELLO_STR));
75 	args->output_buf_size = sizeof(EC_HELLO_STR);
76 
77 	return EC_HOST_CMD_SUCCESS;
78 }
79 EC_HOST_CMD_HANDLER_UNBOUND(EC_CMD_HELLO, ec_host_cmd_hello, BIT(0));
80 
prepare_hello_cmd(uint8_t * buf)81 static void prepare_hello_cmd(uint8_t *buf)
82 {
83 	struct ec_host_cmd_request_header *cmd = (struct ec_host_cmd_request_header *)buf;
84 	struct hello_cmd_data *cmd_data = (struct hello_cmd_data *)(buf + CMD_HEADER_SIZE);
85 
86 	memset(cmd, 0, CMD_HEADER_SIZE);
87 	cmd->cmd_id = EC_CMD_HELLO;
88 	cmd->cmd_ver = 0;
89 	cmd->prtcl_ver = 3;
90 	cmd->data_len = sizeof(*cmd_data);
91 	memcpy(cmd_data->magic, hello_magic, sizeof(hello_magic));
92 	cmd->checksum = cal_checksum((uint8_t *)cmd, CMD_HEADER_SIZE + sizeof(*cmd_data));
93 }
94 
test_hello(void)95 static void test_hello(void)
96 {
97 	struct uart_event evt;
98 	struct uart_mock_data *data = uart_mock.data;
99 	struct ec_host_cmd_request_header *cmd = (struct ec_host_cmd_request_header *)data->rx_buf;
100 	uint8_t tx_buf[RSP_HEADER_SIZE + sizeof(EC_HELLO_STR)];
101 	struct ec_host_cmd_response_header *rsp = (struct ec_host_cmd_response_header *)tx_buf;
102 	int ret;
103 
104 	/* Prepare command request */
105 	prepare_hello_cmd((uint8_t *)cmd);
106 
107 	/* Prepare UART event passed to the UART callback */
108 	evt.type = UART_RX_RDY;
109 	evt.data.rx.len = CMD_HEADER_SIZE + sizeof(struct hello_cmd_data);
110 	evt.data.rx.offset = 0;
111 	evt.data.rx.buf = data->rx_buf;
112 
113 	/* Prepare expected response to the Hello command */
114 	memset(rsp, 0, RSP_HEADER_SIZE);
115 	rsp->data_len = sizeof(EC_HELLO_STR);
116 	rsp->prtcl_ver = 3;
117 	rsp->result = 0;
118 	memcpy(&tx_buf[RSP_HEADER_SIZE], EC_HELLO_STR, sizeof(EC_HELLO_STR));
119 	rsp->checksum = cal_checksum(tx_buf, sizeof(tx_buf));
120 
121 	/* Set expected data set from the EC */
122 	ztest_expect_value(uart_mock_tx, len, RSP_HEADER_SIZE + sizeof(EC_HELLO_STR));
123 	ztest_expect_data(uart_mock_tx, buf, tx_buf);
124 
125 	/* Call the UART callback to inform about a new data */
126 	data->cb(&uart_mock, &evt, data->user_data);
127 
128 	/* Let the handler handle command */
129 	ret = k_sem_take(&data->resp_sent, MAX_RESP_WAIT_TIME);
130 
131 	zassert_equal(ret, 0, "Response not sent");
132 
133 	tx_done();
134 }
135 
136 /* Test recovering from overrun(receiving more data than the header indicates)*/
ZTEST(ec_host_cmd,test_recovery_from_overrun)137 ZTEST(ec_host_cmd, test_recovery_from_overrun)
138 {
139 	struct uart_mock_data *data = uart_mock.data;
140 	struct ec_host_cmd_request_header *cmd = (struct ec_host_cmd_request_header *)data->rx_buf;
141 	struct uart_event evt;
142 	int ret;
143 
144 	/* Header that indicates 0 data bytes */
145 	memset(cmd, 0, CMD_HEADER_SIZE);
146 	cmd->prtcl_ver = 3;
147 	cmd->data_len = 0;
148 
149 	evt.type = UART_RX_RDY;
150 	evt.data.rx.len = CMD_HEADER_SIZE + 1;
151 	evt.data.rx.offset = 1;
152 	evt.data.rx.buf = data->rx_buf;
153 
154 	/* Call the UART callback to inform about a new data */
155 	data->cb(&uart_mock, &evt, data->user_data);
156 
157 	/* Make sure we don't get response */
158 	ret = k_sem_take(&data->resp_sent, UART_BACKEND_RECOVERY_TIME);
159 	zassert_equal(ret, -EAGAIN, "Got unexpected response");
160 
161 	/* Make sure the backend is ready to receive a new command again */
162 	test_hello();
163 }
164 
165 /* Test recovering from receiving invalid header*/
ZTEST(ec_host_cmd,test_recovery_from_invalid_header)166 ZTEST(ec_host_cmd, test_recovery_from_invalid_header)
167 {
168 	int ret;
169 	struct uart_event evt;
170 	struct uart_mock_data *data = uart_mock.data;
171 	struct ec_host_cmd_request_header *cmd = (struct ec_host_cmd_request_header *)data->rx_buf;
172 	/* Different types of invalid header */
173 	struct ec_host_cmd_request_header cmds[] = {
174 		{
175 			.prtcl_ver = 3,
176 			.data_len = data->rx_buf_size + 1 - CMD_HEADER_SIZE,
177 		},
178 		{
179 			.prtcl_ver = 2,
180 			.data_len = 0,
181 		}};
182 
183 	for (int i = 0; i < ARRAY_SIZE(cmds); i++) {
184 		memset(cmd, 0, CMD_HEADER_SIZE);
185 		cmd->prtcl_ver = cmds[i].prtcl_ver;
186 		cmd->data_len = cmds[i].data_len;
187 
188 		evt.type = UART_RX_RDY;
189 		evt.data.rx.len = CMD_HEADER_SIZE;
190 		evt.data.rx.offset = 0;
191 		evt.data.rx.buf = data->rx_buf;
192 
193 		/* Call the UART callback to inform about a new data */
194 		data->cb(&uart_mock, &evt, data->user_data);
195 
196 		/* Make sure we don't get response */
197 		ret = k_sem_take(&data->resp_sent, UART_BACKEND_RECOVERY_TIME);
198 		zassert_equal(ret, -EAGAIN, "Got unexpected response");
199 
200 		/* Make sure the backend is ready to receive a new command again */
201 		test_hello();
202 	}
203 }
204 
205 /* Test recovering from receiving data that exceed buf size*/
ZTEST(ec_host_cmd,test_recovery_from_too_much_data)206 ZTEST(ec_host_cmd, test_recovery_from_too_much_data)
207 {
208 	struct uart_event evt;
209 	int ret;
210 	struct uart_mock_data *data = uart_mock.data;
211 
212 	/* One big chunk larger that the buff size */
213 	evt.type = UART_RX_RDY;
214 	evt.data.rx.len = data->rx_buf_size + 1;
215 	evt.data.rx.offset = 0;
216 	evt.data.rx.buf = data->rx_buf;
217 
218 	/* Call the UART callback to inform about a new data */
219 	data->cb(&uart_mock, &evt, data->user_data);
220 
221 	/* Make sure we don't get response */
222 	ret = k_sem_take(&data->resp_sent, UART_BACKEND_RECOVERY_TIME);
223 	zassert_equal(ret, -EAGAIN, "Got unexpected response");
224 
225 	/* Make sure the backend is ready to receive a new command again */
226 	test_hello();
227 
228 	/* Two chunks larger than the buf size */
229 	evt.type = UART_RX_RDY;
230 	evt.data.rx.len = CMD_HEADER_SIZE - 1;
231 	evt.data.rx.offset = 0;
232 	evt.data.rx.buf = data->rx_buf;
233 
234 	/* Call the UART callback to inform about a new data */
235 	data->cb(&uart_mock, &evt, data->user_data);
236 
237 	evt.type = UART_RX_RDY;
238 	evt.data.rx.len = data->rx_buf_size;
239 	evt.data.rx.offset = CMD_HEADER_SIZE - 1;
240 	evt.data.rx.buf = data->rx_buf;
241 
242 	/* Call the UART callback to inform about a new data */
243 	data->cb(&uart_mock, &evt, data->user_data);
244 
245 	/* Make sure we don't get response */
246 	ret = k_sem_take(&data->resp_sent, UART_BACKEND_RECOVERY_TIME);
247 	zassert_equal(ret, -EAGAIN, "Got response to incomplete command");
248 
249 	/* Make sure the backend is ready to receive a new command again */
250 	test_hello();
251 }
252 
253 /* Test recovering from incomplete command */
ZTEST(ec_host_cmd,test_recovery_from_underrun)254 ZTEST(ec_host_cmd, test_recovery_from_underrun)
255 {
256 	struct uart_event evt;
257 	struct uart_mock_data *data = uart_mock.data;
258 	uint8_t *cmd = data->rx_buf;
259 	const size_t cmd_size = CMD_HEADER_SIZE + sizeof(struct hello_cmd_data);
260 	/* Test different types of underrun */
261 	size_t size_to_send[] = {CMD_HEADER_SIZE - 1, CMD_HEADER_SIZE, cmd_size - 1};
262 	int ret;
263 
264 	for (int i = 0; i < ARRAY_SIZE(size_to_send); i++) {
265 		/* Prepare command request */
266 		prepare_hello_cmd((uint8_t *)cmd);
267 		memset(cmd + size_to_send[i], 0, cmd_size - size_to_send[i]);
268 
269 		/* Prepare UART event passed to the UART callback */
270 		evt.type = UART_RX_RDY;
271 		evt.data.rx.len = size_to_send[i];
272 		evt.data.rx.offset = 0;
273 		evt.data.rx.buf = cmd;
274 
275 		/* Call the UART callback to inform about a new data */
276 		data->cb(&uart_mock, &evt, data->user_data);
277 
278 		/* Make sure we don't get response */
279 		ret = k_sem_take(&data->resp_sent, UART_BACKEND_RECOVERY_TIME);
280 		zassert_equal(ret, -EAGAIN, "Got unexpected response");
281 
282 		/* Make sure the backend is ready to receive a new command again */
283 		test_hello();
284 	}
285 }
286 
287 /* Test basic hello command */
ZTEST(ec_host_cmd,test_hello)288 ZTEST(ec_host_cmd, test_hello)
289 {
290 	test_hello();
291 }
292 
ec_host_cmd_tests_setup(void)293 static void *ec_host_cmd_tests_setup(void)
294 {
295 	struct uart_mock_data *data = uart_mock.data;
296 
297 	k_sem_init(&data->resp_sent, 0, 1);
298 	ec_host_cmd_init(ec_host_cmd_backend_get_uart(&uart_mock));
299 	return NULL;
300 }
301 
302 ZTEST_SUITE(ec_host_cmd, NULL, ec_host_cmd_tests_setup, NULL, NULL, NULL);
303