xref: /illumos-gate/usr/src/test/i2c-tests/tests/ioctl/ioc-errnos.c (revision 0cbe48189888d02563dba9c90132ac391ba233b6)
1*0cbe4818SRobert Mustacchi /*
2*0cbe4818SRobert Mustacchi  * This file and its contents are supplied under the terms of the
3*0cbe4818SRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4*0cbe4818SRobert Mustacchi  * You may only use this file in accordance with the terms of version
5*0cbe4818SRobert Mustacchi  * 1.0 of the CDDL.
6*0cbe4818SRobert Mustacchi  *
7*0cbe4818SRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8*0cbe4818SRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9*0cbe4818SRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10*0cbe4818SRobert Mustacchi  */
11*0cbe4818SRobert Mustacchi 
12*0cbe4818SRobert Mustacchi /*
13*0cbe4818SRobert Mustacchi  * Copyright 2025 Oxide Computer Company
14*0cbe4818SRobert Mustacchi  */
15*0cbe4818SRobert Mustacchi 
16*0cbe4818SRobert Mustacchi /*
17*0cbe4818SRobert Mustacchi  * While most ioctls all embed information that is i2c specific when they fail,
18*0cbe4818SRobert Mustacchi  * there are a few general classes of errors that can be generated with errnos:
19*0cbe4818SRobert Mustacchi  *
20*0cbe4818SRobert Mustacchi  *  - ENOTTY: When we ask for an ioctl on the wrong device minor class
21*0cbe4818SRobert Mustacchi  *  - EFAULT: When we pass a bad address for the ioctl
22*0cbe4818SRobert Mustacchi  *  - EBADF:  When we have an fd that's not open for read/write
23*0cbe4818SRobert Mustacchi  *  - EPERM:  When we don't have the requisite privileges to perfom the
24*0cbe4818SRobert Mustacchi  *            operation
25*0cbe4818SRobert Mustacchi  *
26*0cbe4818SRobert Mustacchi  * This test assumes we use the setup-full target.
27*0cbe4818SRobert Mustacchi  */
28*0cbe4818SRobert Mustacchi 
29*0cbe4818SRobert Mustacchi #include <stdlib.h>
30*0cbe4818SRobert Mustacchi #include <stdbool.h>
31*0cbe4818SRobert Mustacchi #include <err.h>
32*0cbe4818SRobert Mustacchi #include <stdio.h>
33*0cbe4818SRobert Mustacchi #include <sys/types.h>
34*0cbe4818SRobert Mustacchi #include <sys/stat.h>
35*0cbe4818SRobert Mustacchi #include <fcntl.h>
36*0cbe4818SRobert Mustacchi #include <sys/debug.h>
37*0cbe4818SRobert Mustacchi #include <unistd.h>
38*0cbe4818SRobert Mustacchi #include <sys/sysmacros.h>
39*0cbe4818SRobert Mustacchi #include <string.h>
40*0cbe4818SRobert Mustacchi #include <errno.h>
41*0cbe4818SRobert Mustacchi #include <sys/mman.h>
42*0cbe4818SRobert Mustacchi #include <priv.h>
43*0cbe4818SRobert Mustacchi #include <sys/i2c/ioctl.h>
44*0cbe4818SRobert Mustacchi 
45*0cbe4818SRobert Mustacchi #include "i2c_ioctl_util.h"
46*0cbe4818SRobert Mustacchi 
47*0cbe4818SRobert Mustacchi static void *ioctl_fault_addr;
48*0cbe4818SRobert Mustacchi static priv_set_t *ioctl_basic_set;
49*0cbe4818SRobert Mustacchi static priv_set_t *ioctl_orig_set;
50*0cbe4818SRobert Mustacchi 
51*0cbe4818SRobert Mustacchi typedef struct {
52*0cbe4818SRobert Mustacchi 	char *info_name;
53*0cbe4818SRobert Mustacchi 	int info_ioctl;
54*0cbe4818SRobert Mustacchi 	i2c_dev_t info_type;
55*0cbe4818SRobert Mustacchi 	bool info_write;
56*0cbe4818SRobert Mustacchi 	bool info_perm;
57*0cbe4818SRobert Mustacchi } i2c_ioctl_info_t;
58*0cbe4818SRobert Mustacchi 
59*0cbe4818SRobert Mustacchi const i2c_ioctl_info_t i2c_ioctls[] = { {
60*0cbe4818SRobert Mustacchi 	.info_name = "UI2C_IOCTL_CTRL_NPROPS",
61*0cbe4818SRobert Mustacchi 	.info_ioctl = UI2C_IOCTL_CTRL_NPROPS,
62*0cbe4818SRobert Mustacchi 	.info_type = I2C_D_CTRL,
63*0cbe4818SRobert Mustacchi 	.info_write = false,
64*0cbe4818SRobert Mustacchi 	.info_perm = false
65*0cbe4818SRobert Mustacchi }, {
66*0cbe4818SRobert Mustacchi 	.info_name = "UI2C_IOCTL_CTRL_PROP_INFO",
67*0cbe4818SRobert Mustacchi 	.info_ioctl = UI2C_IOCTL_CTRL_PROP_INFO,
68*0cbe4818SRobert Mustacchi 	.info_type = I2C_D_CTRL,
69*0cbe4818SRobert Mustacchi 	.info_write = false,
70*0cbe4818SRobert Mustacchi 	.info_perm = false
71*0cbe4818SRobert Mustacchi }, {
72*0cbe4818SRobert Mustacchi 	.info_name = "UI2C_IOCTL_CTRL_PROP_GET",
73*0cbe4818SRobert Mustacchi 	.info_ioctl = UI2C_IOCTL_CTRL_PROP_GET,
74*0cbe4818SRobert Mustacchi 	.info_type = I2C_D_CTRL,
75*0cbe4818SRobert Mustacchi 	.info_write = false,
76*0cbe4818SRobert Mustacchi 	.info_perm = false
77*0cbe4818SRobert Mustacchi }, {
78*0cbe4818SRobert Mustacchi 	.info_name = "UI2C_IOCTL_CTRL_PROP_SET",
79*0cbe4818SRobert Mustacchi 	.info_ioctl = UI2C_IOCTL_CTRL_PROP_SET,
80*0cbe4818SRobert Mustacchi 	.info_type = I2C_D_CTRL,
81*0cbe4818SRobert Mustacchi 	.info_write = true,
82*0cbe4818SRobert Mustacchi 	.info_perm = true
83*0cbe4818SRobert Mustacchi }, {
84*0cbe4818SRobert Mustacchi 	.info_name = "UI2C_IOCTL_DEVICE_ADD",
85*0cbe4818SRobert Mustacchi 	.info_ioctl = UI2C_IOCTL_DEVICE_ADD,
86*0cbe4818SRobert Mustacchi 	.info_type = I2C_D_PORT,
87*0cbe4818SRobert Mustacchi 	.info_write = true,
88*0cbe4818SRobert Mustacchi 	.info_perm = true
89*0cbe4818SRobert Mustacchi }, {
90*0cbe4818SRobert Mustacchi 	.info_name = "UI2C_IOCTL_DEVICE_REMOVE",
91*0cbe4818SRobert Mustacchi 	.info_ioctl = UI2C_IOCTL_DEVICE_REMOVE,
92*0cbe4818SRobert Mustacchi 	.info_type = I2C_D_PORT,
93*0cbe4818SRobert Mustacchi 	.info_write = true,
94*0cbe4818SRobert Mustacchi 	.info_perm = true
95*0cbe4818SRobert Mustacchi }, {
96*0cbe4818SRobert Mustacchi 	.info_name = "UI2C_IOCTL_I2C_REQ",
97*0cbe4818SRobert Mustacchi 	.info_ioctl = UI2C_IOCTL_I2C_REQ,
98*0cbe4818SRobert Mustacchi 	.info_type = I2C_D_PORT,
99*0cbe4818SRobert Mustacchi 	.info_write = true,
100*0cbe4818SRobert Mustacchi 	.info_perm = true
101*0cbe4818SRobert Mustacchi }, {
102*0cbe4818SRobert Mustacchi 	.info_name = "UI2C_IOCTL_SMBUS_REQ",
103*0cbe4818SRobert Mustacchi 	.info_ioctl = UI2C_IOCTL_SMBUS_REQ,
104*0cbe4818SRobert Mustacchi 	.info_type = I2C_D_PORT,
105*0cbe4818SRobert Mustacchi 	.info_write = true,
106*0cbe4818SRobert Mustacchi 	.info_perm = true
107*0cbe4818SRobert Mustacchi }, {
108*0cbe4818SRobert Mustacchi 	.info_name = "UI2C_IOCTL_PORT_INFO",
109*0cbe4818SRobert Mustacchi 	.info_ioctl = UI2C_IOCTL_PORT_INFO,
110*0cbe4818SRobert Mustacchi 	.info_type = I2C_D_PORT,
111*0cbe4818SRobert Mustacchi 	.info_write = false,
112*0cbe4818SRobert Mustacchi 	.info_perm = false
113*0cbe4818SRobert Mustacchi }, {
114*0cbe4818SRobert Mustacchi 	.info_name = "UI2C_IOCTL_DEV_INFO",
115*0cbe4818SRobert Mustacchi 	.info_ioctl = UI2C_IOCTL_DEV_INFO,
116*0cbe4818SRobert Mustacchi 	.info_type = I2C_D_DEVICE,
117*0cbe4818SRobert Mustacchi 	.info_write = false,
118*0cbe4818SRobert Mustacchi 	.info_perm = false
119*0cbe4818SRobert Mustacchi }, {
120*0cbe4818SRobert Mustacchi 	.info_name = "UI2C_IOCTL_MUX_INFO",
121*0cbe4818SRobert Mustacchi 	.info_ioctl = UI2C_IOCTL_MUX_INFO,
122*0cbe4818SRobert Mustacchi 	.info_type = I2C_D_MUX,
123*0cbe4818SRobert Mustacchi 	.info_write = false,
124*0cbe4818SRobert Mustacchi 	.info_perm = false
125*0cbe4818SRobert Mustacchi } };
126*0cbe4818SRobert Mustacchi 
127*0cbe4818SRobert Mustacchi static const char *ioctl_paths[4] = {
128*0cbe4818SRobert Mustacchi 	[I2C_D_CTRL] = "i2csim0",
129*0cbe4818SRobert Mustacchi 	[I2C_D_PORT] = "i2csim0/0",
130*0cbe4818SRobert Mustacchi 	[I2C_D_MUX] = "i2csim0/0/0x70/mux",
131*0cbe4818SRobert Mustacchi 	[I2C_D_DEVICE] = "i2csim0/0/0x10"
132*0cbe4818SRobert Mustacchi };
133*0cbe4818SRobert Mustacchi 
134*0cbe4818SRobert Mustacchi static bool
i2c_ioctl_fail(const i2c_ioctl_info_t * info,int fd,int exp,void * arg,const char * desc)135*0cbe4818SRobert Mustacchi i2c_ioctl_fail(const i2c_ioctl_info_t *info, int fd, int exp,
136*0cbe4818SRobert Mustacchi     void *arg, const char *desc)
137*0cbe4818SRobert Mustacchi {
138*0cbe4818SRobert Mustacchi 	if (ioctl(fd, info->info_ioctl, arg) == 0) {
139*0cbe4818SRobert Mustacchi 		warnx("TEST FAILED: %s: %s: ioctl returned zero, but "
140*0cbe4818SRobert Mustacchi 		    "expected %s", info->info_name, desc, strerrorname_np(exp));
141*0cbe4818SRobert Mustacchi 		return (false);
142*0cbe4818SRobert Mustacchi 	}
143*0cbe4818SRobert Mustacchi 
144*0cbe4818SRobert Mustacchi 	if (errno != exp) {
145*0cbe4818SRobert Mustacchi 		int e = errno;
146*0cbe4818SRobert Mustacchi 		warnx("TEST FAILED: %s: %s: ioctl failed with %s, but "
147*0cbe4818SRobert Mustacchi 		    "expected %s", info->info_name, desc, strerrorname_np(e),
148*0cbe4818SRobert Mustacchi 		    strerrorname_np(exp));
149*0cbe4818SRobert Mustacchi 		return (false);
150*0cbe4818SRobert Mustacchi 	}
151*0cbe4818SRobert Mustacchi 
152*0cbe4818SRobert Mustacchi 	(void) printf("TEST PASSED: %s: %s\n", info->info_name, desc);
153*0cbe4818SRobert Mustacchi 	return (true);
154*0cbe4818SRobert Mustacchi }
155*0cbe4818SRobert Mustacchi 
156*0cbe4818SRobert Mustacchi static bool
i2c_ioctl_test_one(const i2c_ioctl_info_t * info)157*0cbe4818SRobert Mustacchi i2c_ioctl_test_one(const i2c_ioctl_info_t *info)
158*0cbe4818SRobert Mustacchi {
159*0cbe4818SRobert Mustacchi 	bool ret = true;
160*0cbe4818SRobert Mustacchi 
161*0cbe4818SRobert Mustacchi 	/*
162*0cbe4818SRobert Mustacchi 	 * First, verify it generates ENOTTY on the wrong device type.
163*0cbe4818SRobert Mustacchi 	 */
164*0cbe4818SRobert Mustacchi 	for (i2c_dev_t d = I2C_D_CTRL; d < I2C_D_OTHER; d++) {
165*0cbe4818SRobert Mustacchi 		char buf[128];
166*0cbe4818SRobert Mustacchi 		if (info->info_type == d)
167*0cbe4818SRobert Mustacchi 			continue;
168*0cbe4818SRobert Mustacchi 		(void) snprintf(buf, sizeof (buf), "wrong device type 0x%x "
169*0cbe4818SRobert Mustacchi 		    "returns ENOTTY", d);
170*0cbe4818SRobert Mustacchi 		int fd = i2c_ioctl_test_get_fd(d, ioctl_paths[d], O_RDWR);
171*0cbe4818SRobert Mustacchi 		if (!i2c_ioctl_fail(info, fd, ENOTTY, NULL, buf)) {
172*0cbe4818SRobert Mustacchi 			ret = false;
173*0cbe4818SRobert Mustacchi 		}
174*0cbe4818SRobert Mustacchi 		VERIFY0(close(fd));
175*0cbe4818SRobert Mustacchi 	}
176*0cbe4818SRobert Mustacchi 
177*0cbe4818SRobert Mustacchi 	/* EBADF */
178*0cbe4818SRobert Mustacchi 	if (info->info_write) {
179*0cbe4818SRobert Mustacchi 		/*
180*0cbe4818SRobert Mustacchi 		 * Currently the nexus doesn't allow opening the device without
181*0cbe4818SRobert Mustacchi 		 * read permissions, so we only test cases where writing is
182*0cbe4818SRobert Mustacchi 		 * required. If this changes, then we should add an analogous
183*0cbe4818SRobert Mustacchi 		 * test for read.
184*0cbe4818SRobert Mustacchi 		 */
185*0cbe4818SRobert Mustacchi 		int fd = i2c_ioctl_test_get_fd(info->info_type,
186*0cbe4818SRobert Mustacchi 		    ioctl_paths[info->info_type], O_RDONLY);
187*0cbe4818SRobert Mustacchi 		if (!i2c_ioctl_fail(info, fd, EBADF, 0, "EBADF (O_RDONLY)")) {
188*0cbe4818SRobert Mustacchi 			ret = false;
189*0cbe4818SRobert Mustacchi 		}
190*0cbe4818SRobert Mustacchi 	}
191*0cbe4818SRobert Mustacchi 
192*0cbe4818SRobert Mustacchi 	/* EPERM */
193*0cbe4818SRobert Mustacchi 	if (info->info_perm) {
194*0cbe4818SRobert Mustacchi 		int fd = i2c_ioctl_test_get_fd(info->info_type,
195*0cbe4818SRobert Mustacchi 		    ioctl_paths[info->info_type], O_RDWR);
196*0cbe4818SRobert Mustacchi 		VERIFY0(setppriv(PRIV_SET, PRIV_EFFECTIVE, ioctl_basic_set));
197*0cbe4818SRobert Mustacchi 		if (!i2c_ioctl_fail(info, fd, EPERM, 0, "missing privs")) {
198*0cbe4818SRobert Mustacchi 			ret = false;
199*0cbe4818SRobert Mustacchi 		}
200*0cbe4818SRobert Mustacchi 		VERIFY0(setppriv(PRIV_SET, PRIV_EFFECTIVE, ioctl_orig_set));
201*0cbe4818SRobert Mustacchi 	}
202*0cbe4818SRobert Mustacchi 
203*0cbe4818SRobert Mustacchi 	/* EFAULT */
204*0cbe4818SRobert Mustacchi 	int fd = i2c_ioctl_test_get_fd(info->info_type,
205*0cbe4818SRobert Mustacchi 	    ioctl_paths[info->info_type], O_RDWR);
206*0cbe4818SRobert Mustacchi 	if (!i2c_ioctl_fail(info, fd, EFAULT, ioctl_fault_addr,
207*0cbe4818SRobert Mustacchi 	    "bad address")) {
208*0cbe4818SRobert Mustacchi 		ret = false;
209*0cbe4818SRobert Mustacchi 	}
210*0cbe4818SRobert Mustacchi 
211*0cbe4818SRobert Mustacchi 	return (ret);
212*0cbe4818SRobert Mustacchi }
213*0cbe4818SRobert Mustacchi 
214*0cbe4818SRobert Mustacchi int
main(void)215*0cbe4818SRobert Mustacchi main(void)
216*0cbe4818SRobert Mustacchi {
217*0cbe4818SRobert Mustacchi 	int ret = EXIT_SUCCESS;
218*0cbe4818SRobert Mustacchi 
219*0cbe4818SRobert Mustacchi 	ioctl_fault_addr = mmap(NULL, sysconf(_SC_PAGESIZE) * 4, PROT_NONE,
220*0cbe4818SRobert Mustacchi 	    MAP_PRIVATE | MAP_ANON, -1, 0);
221*0cbe4818SRobert Mustacchi 	if (ioctl_fault_addr == NULL) {
222*0cbe4818SRobert Mustacchi 		err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to create "
223*0cbe4818SRobert Mustacchi 		    "bad page");
224*0cbe4818SRobert Mustacchi 	}
225*0cbe4818SRobert Mustacchi 
226*0cbe4818SRobert Mustacchi 	ioctl_basic_set = priv_allocset();
227*0cbe4818SRobert Mustacchi 	ioctl_orig_set = priv_allocset();
228*0cbe4818SRobert Mustacchi 	if (ioctl_basic_set == NULL || ioctl_orig_set == NULL) {
229*0cbe4818SRobert Mustacchi 		err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to allocate "
230*0cbe4818SRobert Mustacchi 		    "privilege sets");
231*0cbe4818SRobert Mustacchi 	}
232*0cbe4818SRobert Mustacchi 	priv_basicset(ioctl_basic_set);
233*0cbe4818SRobert Mustacchi 
234*0cbe4818SRobert Mustacchi 	if (getppriv(PRIV_EFFECTIVE, ioctl_orig_set) != 0) {
235*0cbe4818SRobert Mustacchi 		err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to get "
236*0cbe4818SRobert Mustacchi 		    "current privilege set");
237*0cbe4818SRobert Mustacchi 	}
238*0cbe4818SRobert Mustacchi 
239*0cbe4818SRobert Mustacchi 	for (size_t i = 0; i < ARRAY_SIZE(i2c_ioctls); i++) {
240*0cbe4818SRobert Mustacchi 		if (!i2c_ioctl_test_one(&i2c_ioctls[i]))
241*0cbe4818SRobert Mustacchi 			ret = EXIT_FAILURE;
242*0cbe4818SRobert Mustacchi 	}
243*0cbe4818SRobert Mustacchi 
244*0cbe4818SRobert Mustacchi 	if (ret == EXIT_SUCCESS) {
245*0cbe4818SRobert Mustacchi 		(void) printf("All tests completed successfully\n");
246*0cbe4818SRobert Mustacchi 	}
247*0cbe4818SRobert Mustacchi 	return (ret);
248*0cbe4818SRobert Mustacchi }
249