1 /*
2 * Copyright (c) 2016 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/mgmt/ec_host_cmd/ec_host_cmd.h>
8 #include <zephyr/mgmt/ec_host_cmd/simulator.h>
9 #include <zephyr/ztest.h>
10
11 /* Variables used to record what is "sent" to host for verification. */
12 K_SEM_DEFINE(send_called, 0, 1);
13 struct ec_host_cmd_tx_buf *sent;
14
host_send(const struct ec_host_cmd_backend * backend)15 static int host_send(const struct ec_host_cmd_backend *backend)
16 {
17 k_sem_give(&send_called);
18 return 0;
19 }
20
21 /**
22 * struct ec_params_add - Parameters to the add command.
23 * @in_data: Pass anything here.
24 */
25 struct ec_params_add {
26 uint32_t in_data;
27 } __packed;
28
29 struct ec_params_unbounded {
30 uint32_t bytes_to_write;
31 } __packed;
32
33 /**
34 * struct ec_response_add - Response to the add command.
35 * @out_data: Output will be in_data + 0x01020304.
36 */
37 struct ec_response_add {
38 uint32_t out_data;
39 } __packed;
40
41 struct ec_response_too_big {
42 uint8_t out_data[512];
43 } __packed;
44
45 /* Buffer used to simulate incoming data from host to EC. */
46 static uint8_t host_to_dut_buffer[256];
47 struct rx_structure {
48 struct ec_host_cmd_request_header header;
49 union {
50 struct ec_params_add add;
51 struct ec_params_unbounded unbounded;
52 uint8_t raw[0];
53 };
54 } __packed * const host_to_dut = (void *)&host_to_dut_buffer;
55
56 /* Buffer used to verify expected outgoing data from EC to host. */
57 static uint8_t expected_dut_to_host_buffer[256];
58 struct tx_structure {
59 struct ec_host_cmd_response_header header;
60 union {
61 struct ec_response_add add;
62 uint8_t raw[0];
63 };
64 } __packed * const expected_dut_to_host = (void *)&expected_dut_to_host_buffer;
65
update_host_to_dut_checksum(void)66 static void update_host_to_dut_checksum(void)
67 {
68 host_to_dut->header.checksum = 0;
69
70 uint8_t checksum = 0;
71
72 for (size_t i = 0;
73 i < sizeof(host_to_dut->header) + host_to_dut->header.data_len;
74 ++i) {
75 checksum += host_to_dut_buffer[i];
76 }
77 host_to_dut->header.checksum = (uint8_t)(-checksum);
78 }
79
update_dut_to_host_checksum(void)80 static void update_dut_to_host_checksum(void)
81 {
82 const uint16_t buf_size = sizeof(expected_dut_to_host->header) +
83 expected_dut_to_host->header.data_len;
84
85 expected_dut_to_host->header.checksum = 0;
86
87 uint8_t checksum = 0;
88
89 for (size_t i = 0; i < buf_size; ++i) {
90 checksum += expected_dut_to_host_buffer[i];
91 }
92
93 expected_dut_to_host->header.checksum = (uint8_t)(-checksum);
94 }
95
simulate_rx_data(void)96 static void simulate_rx_data(void)
97 {
98 int rv;
99
100 update_host_to_dut_checksum();
101 /*
102 * Always send entire buffer and let host command framework read what it
103 * needs.
104 */
105 rv = ec_host_cmd_backend_sim_data_received(host_to_dut_buffer,
106 sizeof(host_to_dut_buffer));
107 zassert_equal(rv, 0, "Could not send data %d", rv);
108
109 /* Ensure send was called so we can verify outputs */
110 rv = k_sem_take(&send_called, K_SECONDS(1));
111 zassert_equal(rv, 0, "Send was not called");
112 }
113
expected_tx_size(void)114 static uint16_t expected_tx_size(void)
115 {
116 return sizeof(expected_dut_to_host->header) +
117 expected_dut_to_host->header.data_len;
118 }
119
verify_tx_data(void)120 static void verify_tx_data(void)
121 {
122 update_dut_to_host_checksum();
123
124 zassert_equal(sent->len, expected_tx_size(), "Sent bytes did not match");
125 zassert_mem_equal(sent->buf, expected_dut_to_host, expected_tx_size(),
126 "Sent buffer did not match");
127 }
128
verify_tx_error(enum ec_host_cmd_status error)129 static void verify_tx_error(enum ec_host_cmd_status error)
130 {
131 expected_dut_to_host->header.prtcl_ver = 3;
132 expected_dut_to_host->header.result = error;
133 expected_dut_to_host->header.data_len = 0;
134 expected_dut_to_host->header.reserved = 0;
135 update_dut_to_host_checksum();
136
137 zassert_equal(sent->len, expected_tx_size(), "Sent bytes did not match");
138 zassert_mem_equal(sent->buf, expected_dut_to_host, expected_tx_size(),
139 "Sent buffer did not match");
140 }
141
142 #define EC_CMD_HELLO 0x0001
143 static enum ec_host_cmd_status
ec_host_cmd_add(struct ec_host_cmd_handler_args * args)144 ec_host_cmd_add(struct ec_host_cmd_handler_args *args)
145 {
146 const struct ec_params_add *const request = args->input_buf;
147 struct ec_response_add *const response = args->output_buf;
148
149 if (args->version == 0) {
150 response->out_data = request->in_data + 0x01020304;
151 } else if (args->version == 1) {
152 response->out_data = request->in_data + 0x02040608;
153 } else if (args->version == 2) {
154 return EC_HOST_CMD_OVERFLOW;
155 } else {
156 zassert_unreachable("Should not get version %d", args->version);
157 }
158
159 args->output_buf_size = sizeof(*response);
160 return EC_HOST_CMD_SUCCESS;
161 }
162 EC_HOST_CMD_HANDLER(EC_CMD_HELLO, ec_host_cmd_add, BIT(0) | BIT(1) | BIT(2),
163 struct ec_params_add, struct ec_response_add);
164
ZTEST(ec_host_cmd,test_add)165 ZTEST(ec_host_cmd, test_add)
166 {
167 host_to_dut->header.prtcl_ver = 3;
168 host_to_dut->header.cmd_id = EC_CMD_HELLO;
169 host_to_dut->header.cmd_ver = 0;
170 host_to_dut->header.reserved = 0;
171 host_to_dut->header.data_len = sizeof(host_to_dut->add);
172 host_to_dut->add.in_data = 0x10203040;
173
174 simulate_rx_data();
175
176 expected_dut_to_host->header.prtcl_ver = 3;
177 expected_dut_to_host->header.result = 0;
178 expected_dut_to_host->header.reserved = 0;
179 expected_dut_to_host->header.data_len =
180 sizeof(expected_dut_to_host->add);
181 expected_dut_to_host->add.out_data = 0x11223344;
182
183 verify_tx_data();
184 }
185
ZTEST(ec_host_cmd,test_add_version_2)186 ZTEST(ec_host_cmd, test_add_version_2)
187 {
188 host_to_dut->header.prtcl_ver = 3;
189 host_to_dut->header.cmd_id = EC_CMD_HELLO;
190 host_to_dut->header.cmd_ver = 1;
191 host_to_dut->header.reserved = 0;
192 host_to_dut->header.data_len = sizeof(host_to_dut->add);
193 host_to_dut->add.in_data = 0x10203040;
194
195 simulate_rx_data();
196
197 expected_dut_to_host->header.prtcl_ver = 3;
198 expected_dut_to_host->header.result = 0;
199 expected_dut_to_host->header.reserved = 0;
200 expected_dut_to_host->header.data_len =
201 sizeof(expected_dut_to_host->add);
202 expected_dut_to_host->add.out_data = 0x12243648;
203
204 verify_tx_data();
205 }
206
ZTEST(ec_host_cmd,test_add_invalid_version)207 ZTEST(ec_host_cmd, test_add_invalid_version)
208 {
209 host_to_dut->header.prtcl_ver = 3;
210 host_to_dut->header.cmd_id = EC_CMD_HELLO;
211 host_to_dut->header.cmd_ver = 3;
212 host_to_dut->header.reserved = 0;
213 host_to_dut->header.data_len = sizeof(host_to_dut->add);
214 host_to_dut->add.in_data = 0x10203040;
215
216 simulate_rx_data();
217
218 verify_tx_error(EC_HOST_CMD_INVALID_VERSION);
219 }
220
ZTEST(ec_host_cmd,test_add_invalid_version_big)221 ZTEST(ec_host_cmd, test_add_invalid_version_big)
222 {
223 host_to_dut->header.prtcl_ver = 3;
224 host_to_dut->header.cmd_id = EC_CMD_HELLO;
225 host_to_dut->header.cmd_ver = 128;
226 host_to_dut->header.reserved = 0;
227 host_to_dut->header.data_len = sizeof(host_to_dut->add);
228 host_to_dut->add.in_data = 0x10203040;
229
230 simulate_rx_data();
231
232 verify_tx_error(EC_HOST_CMD_INVALID_VERSION);
233 }
234
ZTEST(ec_host_cmd,test_add_invalid_prtcl_ver_2)235 ZTEST(ec_host_cmd, test_add_invalid_prtcl_ver_2)
236 {
237 host_to_dut->header.prtcl_ver = 2;
238 host_to_dut->header.cmd_id = EC_CMD_HELLO;
239 host_to_dut->header.cmd_ver = 2;
240 host_to_dut->header.reserved = 0;
241 host_to_dut->header.data_len = sizeof(host_to_dut->add);
242 host_to_dut->add.in_data = 0x10203040;
243
244 simulate_rx_data();
245
246 verify_tx_error(EC_HOST_CMD_INVALID_HEADER);
247 }
248
ZTEST(ec_host_cmd,test_add_invalid_prtcl_ver_4)249 ZTEST(ec_host_cmd, test_add_invalid_prtcl_ver_4)
250 {
251 host_to_dut->header.prtcl_ver = 4;
252 host_to_dut->header.cmd_id = EC_CMD_HELLO;
253 host_to_dut->header.cmd_ver = 2;
254 host_to_dut->header.reserved = 0;
255 host_to_dut->header.data_len = sizeof(host_to_dut->add);
256 host_to_dut->add.in_data = 0x10203040;
257
258 simulate_rx_data();
259
260 verify_tx_error(EC_HOST_CMD_INVALID_HEADER);
261 }
262
ZTEST(ec_host_cmd,test_add_invalid_rx_checksum)263 ZTEST(ec_host_cmd, test_add_invalid_rx_checksum)
264 {
265 int rv;
266
267 host_to_dut->header.prtcl_ver = 3;
268 host_to_dut->header.cmd_id = EC_CMD_HELLO;
269 host_to_dut->header.cmd_ver = 2;
270 host_to_dut->header.reserved = 0;
271 host_to_dut->header.data_len = sizeof(host_to_dut->add);
272 host_to_dut->add.in_data = 0x10203040;
273
274 /* Set an invalid checksum */
275 host_to_dut->header.checksum = 42;
276
277 /* Always send entire buffer and let host command framework read what it
278 * needs.
279 */
280 rv = ec_host_cmd_backend_sim_data_received(host_to_dut_buffer,
281 sizeof(host_to_dut_buffer));
282 zassert_equal(rv, 0, "Could not send data %d", rv);
283
284 /* Ensure Send was called so we can verify outputs */
285 rv = k_sem_take(&send_called, K_SECONDS(1));
286 zassert_equal(rv, 0, "Send was not called");
287
288 verify_tx_error(EC_HOST_CMD_INVALID_CHECKSUM);
289 }
290
ZTEST(ec_host_cmd,test_add_rx_size_too_small_for_header)291 ZTEST(ec_host_cmd, test_add_rx_size_too_small_for_header)
292 {
293 int rv;
294
295 host_to_dut->header.prtcl_ver = 3;
296 host_to_dut->header.cmd_id = EC_CMD_HELLO;
297 host_to_dut->header.cmd_ver = 2;
298 host_to_dut->header.reserved = 0;
299 host_to_dut->header.data_len = sizeof(host_to_dut->add);
300 host_to_dut->add.in_data = 0x10203040;
301
302 rv = ec_host_cmd_backend_sim_data_received(host_to_dut_buffer, 4);
303 zassert_equal(rv, 0, "Could not send data %d", rv);
304
305 /* Ensure Send was called so we can verify outputs */
306 rv = k_sem_take(&send_called, K_SECONDS(1));
307 zassert_equal(rv, 0, "Send was not called");
308
309 verify_tx_error(EC_HOST_CMD_REQUEST_TRUNCATED);
310 }
311
ZTEST(ec_host_cmd,test_add_rx_size_too_small)312 ZTEST(ec_host_cmd, test_add_rx_size_too_small)
313 {
314 int rv;
315
316 host_to_dut->header.prtcl_ver = 3;
317 host_to_dut->header.cmd_id = EC_CMD_HELLO;
318 host_to_dut->header.cmd_ver = 2;
319 host_to_dut->header.reserved = 0;
320 host_to_dut->header.data_len = sizeof(host_to_dut->add);
321 host_to_dut->add.in_data = 0x10203040;
322
323 rv = ec_host_cmd_backend_sim_data_received(
324 host_to_dut_buffer,
325 sizeof(host_to_dut->header) + host_to_dut->header.data_len - 1);
326 zassert_equal(rv, 0, "Could not send data %d", rv);
327
328 /* Ensure Send was called so we can verify outputs */
329 rv = k_sem_take(&send_called, K_SECONDS(1));
330 zassert_equal(rv, 0, "Send was not called");
331
332 verify_tx_error(EC_HOST_CMD_REQUEST_TRUNCATED);
333 }
334
ZTEST(ec_host_cmd,test_unknown_command)335 ZTEST(ec_host_cmd, test_unknown_command)
336 {
337 host_to_dut->header.prtcl_ver = 3;
338 host_to_dut->header.cmd_id = 1234;
339 host_to_dut->header.cmd_ver = 2;
340 host_to_dut->header.reserved = 0;
341 host_to_dut->header.data_len = 0;
342
343 simulate_rx_data();
344
345 verify_tx_error(EC_HOST_CMD_INVALID_COMMAND);
346 }
347
348 #define EC_CMD_UNBOUNDED 0x0002
349 static enum ec_host_cmd_status
ec_host_cmd_unbounded(struct ec_host_cmd_handler_args * args)350 ec_host_cmd_unbounded(struct ec_host_cmd_handler_args *args)
351 {
352 const struct ec_params_unbounded *const request = args->input_buf;
353 const uint8_t *in_buffer = args->input_buf;
354 uint8_t *out_buffer = args->output_buf;
355
356 /* Version 1 just says we used the space without writing */
357 if (args->version == 1) {
358 args->output_buf_size = request->bytes_to_write;
359 return EC_HOST_CMD_SUCCESS;
360 }
361
362 /* Version 2 adds extra asserts */
363 if (args->version == 2) {
364 zassert_equal(in_buffer[4], 0, "Ensure input data is clear");
365 zassert_equal(out_buffer[0], 0, "Ensure output is clear");
366 zassert_equal(out_buffer[1], 0, "Ensure output is clear");
367 zassert_equal(out_buffer[2], 0, "Ensure output is clear");
368 zassert_equal(out_buffer[3], 0, "Ensure output is clear");
369 }
370
371 /* Version 0 (and 2) write request bytes if it can fit */
372 if (request->bytes_to_write > args->output_buf_max) {
373 return EC_HOST_CMD_OVERFLOW;
374 }
375
376 for (int i = 0; i < request->bytes_to_write; ++i) {
377 zassert_equal(out_buffer[i], 0, "Ensure every TX byte is 0");
378 out_buffer[i] = i;
379 }
380
381 args->output_buf_size = request->bytes_to_write;
382 return EC_HOST_CMD_SUCCESS;
383 }
384 EC_HOST_CMD_HANDLER_UNBOUND(EC_CMD_UNBOUNDED, ec_host_cmd_unbounded,
385 BIT(0) | BIT(1) | BIT(2));
386
ZTEST(ec_host_cmd,test_unbounded_handler_error_return)387 ZTEST(ec_host_cmd, test_unbounded_handler_error_return)
388 {
389 host_to_dut->header.prtcl_ver = 3;
390 host_to_dut->header.cmd_id = EC_CMD_UNBOUNDED;
391 host_to_dut->header.cmd_ver = 0;
392 host_to_dut->header.reserved = 0;
393 host_to_dut->header.data_len = sizeof(host_to_dut->unbounded);
394 host_to_dut->unbounded.bytes_to_write = INT16_MAX;
395
396 simulate_rx_data();
397
398 verify_tx_error(EC_HOST_CMD_OVERFLOW);
399 }
400
ZTEST(ec_host_cmd,test_unbounded_handler_response_too_big)401 ZTEST(ec_host_cmd, test_unbounded_handler_response_too_big)
402 {
403 host_to_dut->header.prtcl_ver = 3;
404 host_to_dut->header.cmd_id = EC_CMD_UNBOUNDED;
405 host_to_dut->header.cmd_ver = 1;
406 host_to_dut->header.reserved = 0;
407 host_to_dut->header.data_len = sizeof(host_to_dut->unbounded);
408 host_to_dut->unbounded.bytes_to_write = INT16_MAX;
409
410 simulate_rx_data();
411
412 verify_tx_error(EC_HOST_CMD_INVALID_RESPONSE);
413 }
414
415 #define EC_CMD_TOO_BIG 0x0003
416 static enum ec_host_cmd_status
ec_host_cmd_too_big(struct ec_host_cmd_handler_args * args)417 ec_host_cmd_too_big(struct ec_host_cmd_handler_args *args)
418 {
419 return EC_HOST_CMD_SUCCESS;
420 }
421 EC_HOST_CMD_HANDLER(EC_CMD_TOO_BIG, ec_host_cmd_too_big, BIT(0), uint32_t,
422 struct ec_response_too_big);
423
ZTEST(ec_host_cmd,test_response_always_too_big)424 ZTEST(ec_host_cmd, test_response_always_too_big)
425 {
426 host_to_dut->header.prtcl_ver = 3;
427 host_to_dut->header.cmd_id = EC_CMD_TOO_BIG;
428 host_to_dut->header.cmd_ver = 0;
429 host_to_dut->header.reserved = 0;
430 host_to_dut->header.data_len = sizeof(uint32_t);
431
432 simulate_rx_data();
433
434 verify_tx_error(EC_HOST_CMD_INVALID_RESPONSE);
435 }
436
ec_host_cmd_tests_setup(void)437 static void *ec_host_cmd_tests_setup(void)
438 {
439 ec_host_cmd_backend_sim_install_send_cb(host_send, &sent);
440 return NULL;
441 }
442
443 ZTEST_SUITE(ec_host_cmd, NULL, ec_host_cmd_tests_setup, NULL, NULL, NULL);
444