xref: /illumos-gate/usr/src/lib/libxpio/common/libxpio_dpio.c (revision fd71220ba0fafcc9cf5ea0785db206f3f31336e7)
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 2022 Oxide Computer Company
14  */
15 
16 /*
17  * Dedicated Purpose I/O related routines
18  */
19 
20 #include <unistd.h>
21 #include <strings.h>
22 #include <errno.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <sys/debug.h>
27 
28 #include "libxpio_impl.h"
29 
30 /*
31  * To avoid translation of flags we assume the kernel flags and our library
32  * flags are the same. The casts are required as that gets us the underlying
33  * enum's value in a way that a static assertion can handle.
34  */
35 CTASSERT((uint32_t)XPIO_DPIO_F_READ == (uint32_t)KGPIO_DPIO_F_READ);
36 CTASSERT((uint32_t)XPIO_DPIO_F_WRITE == (uint32_t)KGPIO_DPIO_F_WRITE);
37 CTASSERT((uint32_t)XPIO_DPIO_F_KERNEL == (uint32_t)KGPIO_DPIO_F_KERNEL);
38 
39 /*
40  * We do not have a /dev entry point for the dpinfo minor that we need to use to
41  * get information about DPIOs as this is private to the implementation. We
42  * therefore record it here.
43  */
44 static const char *xpio_dpinfo_path = "/devices/pseudo/kgpio@0:dpinfo";
45 
46 typedef struct {
47 	xpio_t *xdc_xpio;
48 	xpio_dpio_disc_f xdc_func;
49 	void *xdc_arg;
50 } xpio_dpio_cb_t;
51 
52 static int
xpio_dpio_discover_cb(di_node_t di,di_minor_t minor,void * arg)53 xpio_dpio_discover_cb(di_node_t di, di_minor_t minor, void *arg)
54 {
55 	bool ret;
56 	xpio_dpio_cb_t *cb = arg;
57 	xpio_dpio_disc_t disc;
58 
59 	disc.xdd_minor = minor;
60 
61 	ret = cb->xdc_func(cb->xdc_xpio, &disc, cb->xdc_arg);
62 	if (ret) {
63 		return (DI_WALK_CONTINUE);
64 	} else {
65 		return (DI_WALK_TERMINATE);
66 	}
67 }
68 
69 void
xpio_dpio_discover(xpio_t * xpio,xpio_dpio_disc_f func,void * arg)70 xpio_dpio_discover(xpio_t *xpio, xpio_dpio_disc_f func, void *arg)
71 {
72 	xpio_dpio_cb_t cb;
73 
74 	cb.xdc_xpio = xpio;
75 	cb.xdc_func = func;
76 	cb.xdc_arg = arg;
77 	(void) di_walk_minor(xpio->xp_devinfo, DDI_NT_GPIO_DPIO, 0, &cb,
78 	    xpio_dpio_discover_cb);
79 }
80 
81 void
xpio_dpio_info_free(xpio_dpio_info_t * info)82 xpio_dpio_info_free(xpio_dpio_info_t *info)
83 {
84 	free(info);
85 }
86 
87 const char *
xpio_dpio_info_ctrl(xpio_dpio_info_t * info)88 xpio_dpio_info_ctrl(xpio_dpio_info_t *info)
89 {
90 	return (info->xdi_ctrl);
91 }
92 
93 const char *
xpio_dpio_info_name(xpio_dpio_info_t * info)94 xpio_dpio_info_name(xpio_dpio_info_t *info)
95 {
96 	/*
97 	 * The raw minor name which is what we use to go to the kernel with
98 	 * includes a 'dpio:' prefix. However, that is not what users actually
99 	 * create and use, so strip that out of the returned name.
100 	 */
101 	return (info->xdi_dpio + 5);
102 }
103 
104 uint32_t
xpio_dpio_info_gpionum(xpio_dpio_info_t * info)105 xpio_dpio_info_gpionum(xpio_dpio_info_t *info)
106 {
107 	return (info->xdi_gpio);
108 }
109 
110 dpio_caps_t
xpio_dpio_info_caps(xpio_dpio_info_t * info)111 xpio_dpio_info_caps(xpio_dpio_info_t *info)
112 {
113 	return (info->xdi_caps);
114 }
115 
116 dpio_flags_t
xpio_dpio_info_flags(xpio_dpio_info_t * info)117 xpio_dpio_info_flags(xpio_dpio_info_t *info)
118 {
119 	return (info->xdi_flags);
120 }
121 
122 bool
xpio_dpio_info(xpio_t * xpio,di_minor_t minor,xpio_dpio_info_t ** outp)123 xpio_dpio_info(xpio_t *xpio, di_minor_t minor, xpio_dpio_info_t **outp)
124 {
125 	int fd = -1;
126 	xpio_dpio_info_t *info = NULL;
127 	dpio_info_t dpi;
128 
129 	if (minor == DI_NODE_NIL) {
130 		return (xpio_error(xpio, XPIO_ERR_BAD_PTR, 0, "encountered "
131 		    "invalid di_minor_t: %p", minor));
132 	}
133 
134 	if (outp == NULL) {
135 		return (xpio_error(xpio, XPIO_ERR_BAD_PTR, 0, "encountered "
136 		    "invalid xpio_ctrl_t output pointer: %p", outp));
137 	}
138 	*outp = NULL;
139 
140 	if (strcmp(di_minor_nodetype(minor), DDI_NT_GPIO_DPIO) != 0) {
141 		return (xpio_error(xpio, XPIO_ERR_WRONG_MINOR_TYPE, 0,
142 		    "minor %s has incorrect node type: %s, expected %s",
143 		    di_minor_name(minor), di_minor_nodetype(minor),
144 		    DDI_NT_GPIO_DPIO));
145 	}
146 
147 	if ((fd = open(xpio_dpinfo_path, O_RDONLY)) < 0) {
148 		int e = errno;
149 		return (xpio_error(xpio, XPIO_ERR_OPEN_DEV, e, "failed to open "
150 		    "DPIO information minor: %s", strerror(e)));
151 	}
152 
153 	info = calloc(1, sizeof (xpio_dpio_info_t));
154 	if (info == NULL) {
155 		int e = errno;
156 		(void) xpio_error(xpio, XPIO_ERR_NO_MEM, e, "failed to "
157 		    "allocate memory for a xpio_dpio_info_t: %s", strerror(e));
158 		goto err;
159 	}
160 
161 	(void) memset(&dpi, 0, sizeof (dpi));
162 	if (strlcpy(dpi.dpi_dpio, di_minor_name(minor),
163 	    sizeof (dpi.dpi_dpio)) >= sizeof (dpi.dpi_dpio)) {
164 		(void) xpio_error(xpio, XPIO_ERR_INTERNAL, 0, "DPIO name "
165 		    "somehow exceeded expected system length");
166 		goto err;
167 	}
168 
169 	if (ioctl(fd, DPIO_IOC_INFO, &dpi) != 0) {
170 		int e = errno;
171 		switch (e) {
172 		case ENOENT:
173 			(void) xpio_error(xpio, XPIO_ERR_BAD_DPIO_NAME, 0,
174 			    "DPIO %s does not exist", di_minor_name(minor));
175 			goto err;
176 		case EFAULT:
177 			abort();
178 		default:
179 			(void) xpio_error(xpio, XPIO_ERR_KGPIO, e, "failed to "
180 			    "issue dpio information ioctl for dpio %s: %s",
181 			    di_minor_name(minor), strerror(e));
182 
183 			goto err;
184 		}
185 	}
186 
187 	(void) memcpy(info->xdi_dpio, dpi.dpi_dpio, sizeof (dpi.dpi_dpio));
188 	(void) memcpy(info->xdi_ctrl, dpi.dpi_ctrl, sizeof (dpi.dpi_ctrl));
189 	info->xdi_gpio = dpi.dpi_gpio;
190 	info->xdi_caps = dpi.dpi_caps;
191 	info->xdi_flags = dpi.dpi_flags;
192 
193 	(void) close(fd);
194 	*outp = info;
195 	return (xpio_success(xpio));
196 
197 err:
198 	if (fd >= 0) {
199 		(void) close(fd);
200 	}
201 	free(info);
202 	return (false);
203 }
204 
205 bool
xpio_dpio_create(xpio_ctrl_t * ctrl,xpio_gpio_info_t * gi,const char * name,xpio_dpio_features_t feat)206 xpio_dpio_create(xpio_ctrl_t *ctrl, xpio_gpio_info_t *gi, const char *name,
207     xpio_dpio_features_t feat)
208 {
209 	kgpio_dpio_create_t create;
210 	xpio_t *xpio = ctrl->xc_xpio;
211 	const uint32_t all_feats = XPIO_DPIO_F_READ | XPIO_DPIO_F_WRITE |
212 	    XPIO_DPIO_F_KERNEL;
213 
214 	if (gi == NULL) {
215 		return (xpio_error(xpio, XPIO_ERR_BAD_PTR, 0, "encountered "
216 		    "invalid xpio_gpio_info_t pointer: %p", gi));
217 	}
218 
219 	if (name == NULL) {
220 		return (xpio_error(xpio, XPIO_ERR_BAD_PTR, 0, "encountered "
221 		    "invalid pointer for DPIO name: %p", name));
222 	}
223 
224 	if ((feat & ~all_feats) != 0) {
225 		return (xpio_error(xpio, XPIO_ERR_BAD_DPIO_FEAT, 0, "found "
226 		    "unknown dpio features specified: 0x%x",
227 		    feat & ~all_feats));
228 	}
229 
230 	(void) memset(&create, 0, sizeof (create));
231 	if (strlcpy(create.kdc_name, name, sizeof (create.kdc_name)) >=
232 	    sizeof (create.kdc_name)) {
233 		return (xpio_error(xpio, XPIO_ERR_BAD_DPIO_NAME, 0,
234 		    "requested DPIO name is longer than the maximum supported "
235 		    "(%zu characters including '\\0')",
236 		    sizeof (create.kdc_name)));
237 	}
238 
239 	/*
240 	 * Right now there is a 1:1 mapping between library and kgpio features.
241 	 */
242 	create.kdc_flags = (kgpio_dpio_flags_t)feat;
243 	create.kdc_id = gi->xgi_id;
244 
245 	/*
246 	 * At some point it'd be good for us to take this apart and create much
247 	 * more useful semantic errors rather than this generic error as it
248 	 * basically requires someone to go into the code to figure out what
249 	 * happened. Basically, we want something like with update.
250 	 */
251 	if (ioctl(ctrl->xc_fd, KGPIO_IOC_DPIO_CREATE, &create) != 0) {
252 		int e = errno;
253 		return (xpio_error(xpio, XPIO_ERR_KGPIO, e, "failed to create "
254 		    "dpio %s: %s", name, strerror(e)));
255 	}
256 
257 	return (xpio_success(xpio));
258 }
259 
260 bool
xpio_dpio_destroy(xpio_ctrl_t * ctrl,xpio_gpio_info_t * gi)261 xpio_dpio_destroy(xpio_ctrl_t *ctrl, xpio_gpio_info_t *gi)
262 {
263 	kgpio_dpio_destroy_t destroy;
264 	xpio_t *xpio = ctrl->xc_xpio;
265 
266 	if (gi == NULL) {
267 		return (xpio_error(xpio, XPIO_ERR_BAD_PTR, 0, "encountered "
268 		    "invalid xpio_gpio_info_t pointer: %p", gi));
269 	}
270 
271 	destroy.kdd_id = gi->xgi_id;
272 
273 
274 	/*
275 	 * At some point it'd be good for us to take this apart and create much
276 	 * more useful semantic errors rather than this generic error as it
277 	 * basically requires someone to go into the code to figure out what
278 	 * happened. Basically, we want something like with update.
279 	 */
280 	if (ioctl(ctrl->xc_fd, KGPIO_IOC_DPIO_DESTROY, &destroy) != 0) {
281 		int e = errno;
282 		return (xpio_error(xpio, XPIO_ERR_KGPIO, e, "failed to destroy "
283 		    "dpio %s/%u: %s", ctrl->xc_name, gi->xgi_id, strerror(e)));
284 	}
285 	return (xpio_success(xpio));
286 }
287