xref: /illumos-gate/usr/src/test/i2c-tests/tests/ioctl/invalid-io.c (revision 0cbe48189888d02563dba9c90132ac391ba233b6)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2025 Oxide Computer Company
14  */
15 
16 /*
17  * This tests the kernel's ability to detect invalid I/O requests. For I2C
18  * requests this includes:
19  *
20  *  - Invalid addresses
21  *  - Invalid flags
22  *  - Bad read and write combinations
23  *
24  * For SMBus operations this incldues:
25  *
26  *  - Invalid addresses
27  *  - Invalid flags, both general and operation-specific
28  *  - Invalid operations
29  *  - Operations which are unsupported
30  *  - Specifying read and write length on operations that where it is fixed by
31  *    the specification.
32  *  - Bad read and write lengths for block requests
33  *
34  * All of the I/O tests operate against a non-existent on smbussim1. They should
35  * never succeed as a result; however, they could fail for the wrong reason.
36  */
37 
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <err.h>
41 #include <stdio.h>
42 #include <sys/debug.h>
43 #include <sys/stat.h>
44 #include <fcntl.h>
45 #include <string.h>
46 #include <errno.h>
47 #include <sys/sysmacros.h>
48 
49 #include <sys/i2c/ioctl.h>
50 #include "i2c_ioctl_util.h"
51 
52 static bool
test_one_i2c(int fd,i2c_req_t * req,i2c_errno_t err,const char * desc)53 test_one_i2c(int fd, i2c_req_t *req, i2c_errno_t err, const char *desc)
54 {
55 	bool ret = true;
56 
57 	req->ir_error.i2c_error = INT32_MAX;
58 	req->ir_error.i2c_ctrl = INT32_MAX;
59 	if (ioctl(fd, UI2C_IOCTL_I2C_REQ, req) != 0) {
60 		warnx("TEST FAILED: %s: unexpected ioctl failure: %s",
61 		    desc, strerrordesc_np(errno));
62 		return (false);
63 	}
64 
65 	if (req->ir_error.i2c_error != err) {
66 		warnx("TEST FAILED: %s: I2C ioctl failed with I2C error 0x%x, "
67 		    "expected 0x%x", desc, req->ir_error.i2c_error, err);
68 		ret = false;
69 	}
70 
71 	if (req->ir_error.i2c_ctrl != I2C_CTRL_E_OK) {
72 		warnx("TEST FAILED: %s: I2C ioctl has unexpected controller "
73 		    "error 0x%x", desc, req->ir_error.i2c_ctrl);
74 		ret = false;
75 	}
76 
77 	if (ret) {
78 		(void) printf("TEST PASSED: %s correctly failed with I2C "
79 		    "error 0x%x\n", desc, err);
80 	}
81 
82 	return (ret);
83 }
84 
85 static bool
test_i2c_reqs(int fd)86 test_i2c_reqs(int fd)
87 {
88 	bool ret = true;
89 	i2c_req_t req;
90 
91 	/*
92 	 * Set up the request as a one byte read request.
93 	 */
94 	(void) memset(&req, 0, sizeof (i2c_req_t));
95 	req.ir_rlen = 1;
96 
97 	for (size_t i = 0; i < nbad_addrs; i++) {
98 		char desc[128];
99 
100 		(void) snprintf(desc, sizeof (desc), "i2c: bad address %zu "
101 		    "(0x%x,0x%x)", i, bad_addrs[i].ba_type,
102 		    bad_addrs[i].ba_addr);
103 		req.ir_addr.ia_type = bad_addrs[i].ba_type;
104 		req.ir_addr.ia_addr = bad_addrs[i].ba_addr;
105 
106 		if (!test_one_i2c(fd, &req, bad_addrs[i].ba_error, desc)) {
107 			ret = false;
108 		}
109 	}
110 
111 	req.ir_addr.ia_type = I2C_ADDR_7BIT;
112 	req.ir_addr.ia_addr = 0x23;
113 
114 	uint32_t bad_flags[] = { I2C_IO_REQ_F_QUICK_WRITE, 0x23, 0x777,
115 	    INT32_MAX, UINT32_MAX, 0x42 };
116 	for (size_t i = 0; i < ARRAY_SIZE(bad_flags); i++) {
117 		char desc[128];
118 
119 		(void) snprintf(desc, sizeof (desc), "i2c: bad flags 0x%x",
120 		    bad_flags[i]);
121 		req.ir_flags = bad_flags[i];
122 		if (!test_one_i2c(fd, &req, I2C_CORE_E_BAD_I2C_REQ_FLAGS,
123 		    desc)) {
124 			ret = false;
125 		}
126 	}
127 
128 	req.ir_flags = 0;
129 	req.ir_rlen = 0;
130 	req.ir_wlen = 0;
131 	if (!test_one_i2c(fd, &req, I2C_CORE_E_NEED_READ_OR_WRITE,
132 	    "i2c r=0, w=0")) {
133 		ret = false;
134 	}
135 
136 	req.ir_rlen = 1;
137 	req.ir_wlen = I2C_REQ_MAX + 1;
138 	if (!test_one_i2c(fd, &req, I2C_CORE_E_BAD_I2C_REQ_WRITE_LEN,
139 	    "i2c r=1, w=257")) {
140 		ret = false;
141 	}
142 
143 	req.ir_rlen = 1;
144 	req.ir_wlen = UINT16_MAX;
145 	if (!test_one_i2c(fd, &req, I2C_CORE_E_BAD_I2C_REQ_WRITE_LEN,
146 	    "i2c r=1, w=ffff")) {
147 		ret = false;
148 	}
149 
150 	req.ir_rlen = I2C_REQ_MAX + 1;
151 	req.ir_wlen = 0;
152 	if (!test_one_i2c(fd, &req, I2C_CORE_E_BAD_I2C_REQ_READ_LEN,
153 	    "i2c r=257, w=0")) {
154 		ret = false;
155 	}
156 
157 	req.ir_rlen = UINT16_MAX;
158 	req.ir_wlen = 0;
159 	if (!test_one_i2c(fd, &req, I2C_CORE_E_BAD_I2C_REQ_READ_LEN,
160 	    "i2c r=UINT16_MAX, w=0")) {
161 		ret = false;
162 	}
163 
164 	return (ret);
165 }
166 
167 static bool
test_one_smbus(int fd,smbus_req_t * req,i2c_errno_t err,const char * desc)168 test_one_smbus(int fd, smbus_req_t *req, i2c_errno_t err, const char *desc)
169 {
170 	bool ret = true;
171 
172 	req->smbr_error.i2c_error = INT32_MAX;
173 	req->smbr_error.i2c_ctrl = INT32_MAX;
174 	if (ioctl(fd, UI2C_IOCTL_SMBUS_REQ, req) != 0) {
175 		warnx("TEST FAILED: %s: unexpected smbus ioctl failure: %s",
176 		    desc, strerrordesc_np(errno));
177 		return (false);
178 	}
179 
180 	if (req->smbr_error.i2c_error != err) {
181 		warnx("TEST FAILED: %s: SMBus ioctl failed with I2C error "
182 		    "0x%x, expected 0x%x", desc, req->smbr_error.i2c_error,
183 		    err);
184 		ret = false;
185 	}
186 
187 	if (req->smbr_error.i2c_ctrl != I2C_CTRL_E_OK) {
188 		warnx("TEST FAILED: %s: SMBus ioctl has unexpected controller "
189 		    "error 0x%x", desc, req->smbr_error.i2c_ctrl);
190 		ret = false;
191 	}
192 
193 	if (ret) {
194 		(void) printf("TEST PASSED: %s correctly failed with I2C "
195 		    "error 0x%x\n", desc, err);
196 	}
197 
198 	return (ret);
199 }
200 
201 
202 static bool
test_smbus_reqs(int fd)203 test_smbus_reqs(int fd)
204 {
205 	bool ret = true;
206 	smbus_req_t req;
207 
208 	/*
209 	 * Set up the request as a read byte.
210 	 */
211 	(void) memset(&req, 0, sizeof (i2c_req_t));
212 	req.smbr_cmd = 0x23;
213 	req.smbr_op = SMBUS_OP_READ_BYTE;
214 
215 	for (size_t i = 0; i < nbad_addrs; i++) {
216 		char desc[128];
217 
218 		(void) snprintf(desc, sizeof (desc), "smbus: bad address %zu "
219 		    "(0x%x,0x%x)", i, bad_addrs[i].ba_type,
220 		    bad_addrs[i].ba_addr);
221 		req.smbr_addr.ia_type = bad_addrs[i].ba_type;
222 		req.smbr_addr.ia_addr = bad_addrs[i].ba_addr;
223 
224 		if (!test_one_smbus(fd, &req, bad_addrs[i].ba_error, desc)) {
225 			ret = false;
226 		}
227 	}
228 
229 	/*
230 	 * The quick flag should work only with the quick operation, hence why
231 	 * it's included in the group below.
232 	 */
233 	req.smbr_addr.ia_type = I2C_ADDR_7BIT;
234 	req.smbr_addr.ia_addr = 0x23;
235 
236 	uint32_t bad_flags[] = { I2C_IO_REQ_F_QUICK_WRITE, 0x23, 0x777,
237 	    INT32_MAX, UINT32_MAX, 0x42 };
238 	for (size_t i = 0; i < ARRAY_SIZE(bad_flags); i++) {
239 		char desc[128];
240 
241 		(void) snprintf(desc, sizeof (desc), "smbus: bad flags 0x%x",
242 		    bad_flags[i]);
243 		req.smbr_flags = bad_flags[i];
244 		if (!test_one_smbus(fd, &req, I2C_CORE_E_BAD_SMBUS_REQ_FLAGS,
245 		    desc)) {
246 			ret = false;
247 		}
248 	}
249 
250 	req.smbr_flags = 0;
251 	uint32_t bad_ops[] = { SMBUS_OP_I2C_READ_BLOCK + 1,
252 		SMBUS_OP_I2C_READ_BLOCK << 1, 0x42, 0x7777, INT32_MAX };
253 	for (size_t i = 0; i < ARRAY_SIZE(bad_ops); i++) {
254 		char desc[128];
255 
256 		(void) snprintf(desc, sizeof (desc), "smbus: bad operation "
257 		    "0x%x", bad_ops[i]);
258 		req.smbr_op = bad_ops[i];
259 		if (!test_one_smbus(fd, &req, I2C_CORE_E_BAD_SMBUS_OP, desc)) {
260 			ret = false;
261 		}
262 	}
263 
264 	smbus_op_t unsup_ops[] = { SMBUS_OP_READ_BLOCK, SMBUS_OP_HOST_NOTIFY,
265 	    SMBUS_OP_BLOCK_PROCESS_CALL };
266 
267 	for (size_t i = 0; i < ARRAY_SIZE(unsup_ops); i++) {
268 		char desc[128];
269 
270 		(void) snprintf(desc, sizeof (desc), "smbus: unsupported "
271 		    "operation 0x%x", unsup_ops[i]);
272 		req.smbr_op = unsup_ops[i];
273 		if (!test_one_smbus(fd, &req, I2C_CORE_E_UNSUP_SMBUS_OP,
274 		    desc)) {
275 			ret = false;
276 		}
277 	}
278 
279 	smbus_op_t norw_ops[] = { SMBUS_OP_QUICK_COMMAND, SMBUS_OP_SEND_BYTE,
280 	    SMBUS_OP_RECV_BYTE, SMBUS_OP_WRITE_BYTE, SMBUS_OP_READ_BYTE,
281 	    SMBUS_OP_WRITE_WORD, SMBUS_OP_READ_WORD, SMBUS_OP_PROCESS_CALL,
282 	    SMBUS_OP_WRITE_U32, SMBUS_OP_READ_U32, SMBUS_OP_WRITE_U64,
283 	    SMBUS_OP_READ_U64 };
284 
285 	for (size_t i = 0; i < ARRAY_SIZE(norw_ops); i++) {
286 		char desc[128];
287 
288 		(void) snprintf(desc, sizeof (desc), "smbus: op 0x%x fails "
289 		    "with read length", norw_ops[i]);
290 		req.smbr_op = norw_ops[i];
291 		req.smbr_rlen = 0x4;
292 		req.smbr_wlen = 0x0;
293 		if (!test_one_smbus(fd, &req, I2C_CORE_E_BAD_SMBUS_READ_LEN,
294 		    desc)) {
295 			ret = false;
296 		}
297 
298 		(void) snprintf(desc, sizeof (desc), "smbus: op 0x%x fails "
299 		    "with write length", norw_ops[i]);
300 		req.smbr_op = norw_ops[i];
301 		req.smbr_rlen = 0x0;
302 		req.smbr_wlen = 0x4;
303 		if (!test_one_smbus(fd, &req, I2C_CORE_E_BAD_SMBUS_WRITE_LEN,
304 		    desc)) {
305 			ret = false;
306 		}
307 	}
308 
309 	smbus_op_t wrblk_ops[] = { SMBUS_OP_WRITE_BLOCK,
310 	    SMBUS_OP_I2C_WRITE_BLOCK };
311 	for (size_t i = 0; i < ARRAY_SIZE(wrblk_ops); i++) {
312 		char desc[128];
313 
314 		req.smbr_op = wrblk_ops[i];
315 		(void) snprintf(desc, sizeof (desc), "smbus op 0x%x fails with "
316 		    "read and write", wrblk_ops[i]);
317 		req.smbr_rlen = 0x1;
318 		req.smbr_wlen = 0x8;
319 		if (!test_one_smbus(fd, &req, I2C_CORE_E_BAD_SMBUS_READ_LEN,
320 		    desc)) {
321 			ret = false;
322 		}
323 
324 		(void) snprintf(desc, sizeof (desc), "smbus op 0x%x fails with "
325 		    "read and no write", wrblk_ops[i]);
326 		req.smbr_rlen = 0x2;
327 		req.smbr_wlen = 0x0;
328 		if (!test_one_smbus(fd, &req, I2C_CORE_E_BAD_SMBUS_READ_LEN,
329 		    desc)) {
330 			ret = false;
331 		}
332 
333 		(void) snprintf(desc, sizeof (desc), "smbus op 0x%x fails with "
334 		    "no read and no write", wrblk_ops[i]);
335 		req.smbr_rlen = 0x0;
336 		req.smbr_wlen = 0x0;
337 		if (!test_one_smbus(fd, &req, I2C_CORE_E_BAD_SMBUS_WRITE_LEN,
338 		    desc)) {
339 			ret = false;
340 		}
341 
342 		(void) snprintf(desc, sizeof (desc), "smbus op 0x%x fails with "
343 		    "oversize write 1", wrblk_ops[i]);
344 		req.smbr_rlen = 0x0;
345 		req.smbr_wlen = I2C_REQ_MAX + 1;
346 		if (!test_one_smbus(fd, &req, I2C_CORE_E_BAD_SMBUS_WRITE_LEN,
347 		    desc)) {
348 			ret = false;
349 		}
350 
351 		(void) snprintf(desc, sizeof (desc), "smbus op 0x%x fails with "
352 		    "oversize write 2", wrblk_ops[i]);
353 		req.smbr_rlen = 0x0;
354 		req.smbr_wlen = UINT16_MAX;
355 		if (!test_one_smbus(fd, &req, I2C_CORE_E_BAD_SMBUS_WRITE_LEN,
356 		    desc)) {
357 			ret = false;
358 		}
359 	}
360 
361 	req.smbr_op = SMBUS_OP_I2C_READ_BLOCK;
362 	req.smbr_rlen = 0x2;
363 	req.smbr_wlen = 0x2;
364 	if (!test_one_smbus(fd, &req, I2C_CORE_E_BAD_SMBUS_WRITE_LEN,
365 	    "block read fails with read and write")) {
366 		ret = false;
367 	}
368 
369 	req.smbr_rlen = 0x0;
370 	req.smbr_wlen = 0x2;
371 	if (!test_one_smbus(fd, &req, I2C_CORE_E_BAD_SMBUS_READ_LEN,
372 	    "block read fails with no read and write")) {
373 		ret = false;
374 	}
375 
376 	req.smbr_rlen = 0x0;
377 	req.smbr_wlen = 0x0;
378 	if (!test_one_smbus(fd, &req, I2C_CORE_E_BAD_SMBUS_READ_LEN,
379 	    "block read fails with no read and no write")) {
380 		ret = false;
381 	}
382 
383 	req.smbr_rlen = I2C_REQ_MAX + 1;
384 	req.smbr_wlen = 0x0;
385 	if (!test_one_smbus(fd, &req, I2C_CORE_E_BAD_SMBUS_READ_LEN,
386 	    "block read fails with oversize read 1")) {
387 		ret = false;
388 	}
389 
390 	req.smbr_rlen = UINT16_MAX;
391 	req.smbr_wlen = 0x0;
392 	if (!test_one_smbus(fd, &req, I2C_CORE_E_BAD_SMBUS_READ_LEN,
393 	    "block read fails with oversize read 2")) {
394 		ret = false;
395 	}
396 
397 	return (ret);
398 }
399 
400 int
main(void)401 main(void)
402 {
403 	int ret = EXIT_SUCCESS;
404 	int fd = i2c_ioctl_test_get_fd(I2C_D_PORT, "smbussim1/0", O_RDWR);
405 
406 	if (!test_i2c_reqs(fd)) {
407 		ret = EXIT_FAILURE;
408 	}
409 
410 	if (!test_smbus_reqs(fd)) {
411 		ret = EXIT_FAILURE;
412 	}
413 
414 	VERIFY0(close(fd));
415 	if (ret == EXIT_SUCCESS) {
416 		(void) printf("All tests passed successfully\n");
417 	}
418 	return (ret);
419 }
420