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