xref: /illumos-gate/usr/src/uts/intel/io/amdzen/zen_udf.c (revision 6446bd46ed1b4e9f69da153665f82181ccaedad5)
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 2020 Oxide Computer Company
14  */
15 
16 /*
17  * A companion to zen_udf(4D) that allows user access to read the data fabric
18  * for development purposes.
19  */
20 
21 #include <sys/types.h>
22 #include <sys/file.h>
23 #include <sys/errno.h>
24 #include <sys/open.h>
25 #include <sys/cred.h>
26 #include <sys/ddi.h>
27 #include <sys/sunddi.h>
28 #include <sys/stat.h>
29 #include <sys/conf.h>
30 #include <sys/devops.h>
31 #include <sys/cmn_err.h>
32 #include <sys/policy.h>
33 #include <amdzen_client.h>
34 
35 #include <zen_udf.h>
36 
37 typedef struct zen_udf {
38 	dev_info_t *zudf_dip;
39 	uint_t zudf_ndfs;
40 } zen_udf_t;
41 
42 static zen_udf_t zen_udf_data;
43 
44 static int
45 zen_udf_open(dev_t *devp, int flags, int otype, cred_t *credp)
46 {
47 	minor_t m;
48 	zen_udf_t *zen_udf = &zen_udf_data;
49 
50 	if (crgetzoneid(credp) != GLOBAL_ZONEID ||
51 	    secpolicy_hwmanip(credp) != 0) {
52 		return (EPERM);
53 	}
54 
55 	if ((flags & (FEXCL | FNDELAY | FNONBLOCK)) != 0) {
56 		return (EINVAL);
57 	}
58 
59 	if (otype != OTYP_CHR) {
60 		return (EINVAL);
61 	}
62 
63 	m = getminor(*devp);
64 	if (m >= zen_udf->zudf_ndfs) {
65 		return (ENXIO);
66 	}
67 
68 	return (0);
69 }
70 
71 static int
72 zen_udf_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
73     int *rvalp)
74 {
75 	uint_t dfno;
76 	zen_udf_t *zen_udf = &zen_udf_data;
77 	zen_udf_io_t zui;
78 
79 	if (cmd != ZEN_UDF_READ32 && cmd != ZEN_UDF_READ64) {
80 		return (ENOTTY);
81 	}
82 
83 	dfno = getminor(dev);
84 	if (dfno >= zen_udf->zudf_ndfs) {
85 		return (ENXIO);
86 	}
87 
88 	if (crgetzoneid(credp) != GLOBAL_ZONEID ||
89 	    secpolicy_hwmanip(credp) != 0) {
90 		return (EPERM);
91 	}
92 
93 	if (ddi_copyin((void *)arg, &zui, sizeof (zui), mode & FKIOCTL) != 0) {
94 		return (EFAULT);
95 	}
96 
97 	if (cmd == ZEN_UDF_READ32) {
98 		int ret;
99 		uint32_t data;
100 
101 		ret = amdzen_c_df_read32(dfno, zui.zui_inst, zui.zui_func,
102 		    zui.zui_reg, &data);
103 		if (ret != 0) {
104 			return (ret);
105 		}
106 
107 		zui.zui_data = data;
108 	} else {
109 		int ret;
110 
111 		ret = amdzen_c_df_read64(dfno, zui.zui_inst, zui.zui_func,
112 		    zui.zui_reg, &zui.zui_data);
113 		if (ret != 0) {
114 			return (ret);
115 		}
116 	}
117 
118 	if (ddi_copyout(&zui, (void *)arg, sizeof (zui), mode & FKIOCTL) != 0) {
119 		return (EFAULT);
120 	}
121 
122 	return (0);
123 }
124 
125 static int
126 zen_udf_close(dev_t dev, int flag, int otyp, cred_t *credp)
127 {
128 	return (0);
129 }
130 
131 static void
132 zen_udf_cleanup(zen_udf_t *zen_udf)
133 {
134 	ddi_remove_minor_node(zen_udf->zudf_dip, NULL);
135 	zen_udf->zudf_ndfs = 0;
136 	zen_udf->zudf_dip = NULL;
137 }
138 
139 static int
140 zen_udf_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
141 {
142 	zen_udf_t *zen_udf = &zen_udf_data;
143 
144 	if (cmd == DDI_RESUME) {
145 		return (DDI_SUCCESS);
146 	} else if (cmd != DDI_ATTACH) {
147 		return (DDI_FAILURE);
148 	}
149 
150 	if (zen_udf->zudf_dip != NULL) {
151 		dev_err(dip, CE_WARN, "!zen_udf is already attached to a "
152 		    "dev_info_t: %p", zen_udf->zudf_dip);
153 		return (DDI_FAILURE);
154 	}
155 
156 	zen_udf->zudf_dip = dip;
157 	zen_udf->zudf_ndfs = amdzen_c_df_count();
158 	for (uint_t i = 0; i < zen_udf->zudf_ndfs; i++) {
159 		char buf[32];
160 
161 		(void) snprintf(buf, sizeof (buf), "zen_udf.%u", i);
162 		if (ddi_create_minor_node(dip, buf, S_IFCHR, i, DDI_PSEUDO,
163 		    0) != DDI_SUCCESS) {
164 			dev_err(dip, CE_WARN, "!failed to create minor %s",
165 			    buf);
166 			goto err;
167 		}
168 	}
169 
170 	return (DDI_SUCCESS);
171 
172 err:
173 	zen_udf_cleanup(zen_udf);
174 	return (DDI_FAILURE);
175 }
176 
177 static int
178 zen_udf_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
179 {
180 	zen_udf_t *zen_udf = &zen_udf_data;
181 	minor_t m;
182 
183 	switch (cmd) {
184 	case DDI_INFO_DEVT2DEVINFO:
185 		m = getminor((dev_t)arg);
186 		if (m >= zen_udf->zudf_ndfs) {
187 			return (DDI_FAILURE);
188 		}
189 		*resultp = (void *)zen_udf->zudf_dip;
190 		break;
191 	case DDI_INFO_DEVT2INSTANCE:
192 		m = getminor((dev_t)arg);
193 		if (m >= zen_udf->zudf_ndfs) {
194 			return (DDI_FAILURE);
195 		}
196 		*resultp = (void *)(uintptr_t)ddi_get_instance(
197 		    zen_udf->zudf_dip);
198 		break;
199 	default:
200 		return (DDI_FAILURE);
201 	}
202 	return (DDI_SUCCESS);
203 }
204 
205 static int
206 zen_udf_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
207 {
208 	zen_udf_t *zen_udf = &zen_udf_data;
209 
210 	if (cmd == DDI_SUSPEND) {
211 		return (DDI_SUCCESS);
212 	} else if (cmd != DDI_DETACH) {
213 		return (DDI_FAILURE);
214 	}
215 
216 	if (zen_udf->zudf_dip != dip) {
217 		dev_err(dip, CE_WARN, "!asked to detach zen_udf, but dip "
218 		    "doesn't match");
219 		return (DDI_FAILURE);
220 	}
221 
222 	zen_udf_cleanup(zen_udf);
223 	return (DDI_SUCCESS);
224 }
225 
226 static struct cb_ops zen_udf_cb_ops = {
227 	.cb_open = zen_udf_open,
228 	.cb_close = zen_udf_close,
229 	.cb_strategy = nodev,
230 	.cb_print = nodev,
231 	.cb_dump = nodev,
232 	.cb_read = nodev,
233 	.cb_write = nodev,
234 	.cb_ioctl = zen_udf_ioctl,
235 	.cb_devmap = nodev,
236 	.cb_mmap = nodev,
237 	.cb_segmap = nodev,
238 	.cb_chpoll = nochpoll,
239 	.cb_prop_op = ddi_prop_op,
240 	.cb_flag = D_MP,
241 	.cb_rev = CB_REV,
242 	.cb_aread = nodev,
243 	.cb_awrite = nodev
244 };
245 
246 static struct dev_ops zen_udf_dev_ops = {
247 	.devo_rev = DEVO_REV,
248 	.devo_refcnt = 0,
249 	.devo_getinfo = zen_udf_getinfo,
250 	.devo_identify = nulldev,
251 	.devo_probe = nulldev,
252 	.devo_attach = zen_udf_attach,
253 	.devo_detach = zen_udf_detach,
254 	.devo_reset = nodev,
255 	.devo_quiesce = ddi_quiesce_not_needed,
256 	.devo_cb_ops = &zen_udf_cb_ops
257 };
258 
259 static struct modldrv zen_udf_modldrv = {
260 	.drv_modops = &mod_driverops,
261 	.drv_linkinfo = "AMD User DF Access",
262 	.drv_dev_ops = &zen_udf_dev_ops
263 };
264 
265 static struct modlinkage zen_udf_modlinkage = {
266 	.ml_rev = MODREV_1,
267 	.ml_linkage = { &zen_udf_modldrv, NULL }
268 };
269 
270 int
271 _init(void)
272 {
273 	return (mod_install(&zen_udf_modlinkage));
274 }
275 
276 int
277 _info(struct modinfo *modinfop)
278 {
279 	return (mod_info(&zen_udf_modlinkage, modinfop));
280 }
281 
282 int
283 _fini(void)
284 {
285 	return (mod_remove(&zen_udf_modlinkage));
286 }
287