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