1549e0fd3SRobert Mustacchi /*
2549e0fd3SRobert Mustacchi * This file and its contents are supplied under the terms of the
3549e0fd3SRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4549e0fd3SRobert Mustacchi * You may only use this file in accordance with the terms of version
5549e0fd3SRobert Mustacchi * 1.0 of the CDDL.
6549e0fd3SRobert Mustacchi *
7549e0fd3SRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8549e0fd3SRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at
9549e0fd3SRobert Mustacchi * http://www.illumos.org/license/CDDL.
10549e0fd3SRobert Mustacchi */
11549e0fd3SRobert Mustacchi
12549e0fd3SRobert Mustacchi /*
13*71815ce7SRobert Mustacchi * Copyright 2022 Oxide Computer Company
14549e0fd3SRobert Mustacchi */
15549e0fd3SRobert Mustacchi
16549e0fd3SRobert Mustacchi /*
17bbf21555SRichard Lowe * A companion to zen_udf(4D) that allows user access to read the data fabric
18549e0fd3SRobert Mustacchi * for development purposes.
19549e0fd3SRobert Mustacchi */
20549e0fd3SRobert Mustacchi
21549e0fd3SRobert Mustacchi #include <sys/types.h>
22549e0fd3SRobert Mustacchi #include <sys/file.h>
23549e0fd3SRobert Mustacchi #include <sys/errno.h>
24549e0fd3SRobert Mustacchi #include <sys/open.h>
25549e0fd3SRobert Mustacchi #include <sys/cred.h>
26549e0fd3SRobert Mustacchi #include <sys/ddi.h>
27549e0fd3SRobert Mustacchi #include <sys/sunddi.h>
28549e0fd3SRobert Mustacchi #include <sys/stat.h>
29549e0fd3SRobert Mustacchi #include <sys/conf.h>
30549e0fd3SRobert Mustacchi #include <sys/devops.h>
31549e0fd3SRobert Mustacchi #include <sys/cmn_err.h>
32549e0fd3SRobert Mustacchi #include <sys/policy.h>
33549e0fd3SRobert Mustacchi #include <amdzen_client.h>
34549e0fd3SRobert Mustacchi
35549e0fd3SRobert Mustacchi #include <zen_udf.h>
36549e0fd3SRobert Mustacchi
37549e0fd3SRobert Mustacchi typedef struct zen_udf {
38549e0fd3SRobert Mustacchi dev_info_t *zudf_dip;
39549e0fd3SRobert Mustacchi uint_t zudf_ndfs;
40549e0fd3SRobert Mustacchi } zen_udf_t;
41549e0fd3SRobert Mustacchi
42549e0fd3SRobert Mustacchi static zen_udf_t zen_udf_data;
43549e0fd3SRobert Mustacchi
44549e0fd3SRobert Mustacchi static int
zen_udf_open(dev_t * devp,int flags,int otype,cred_t * credp)45549e0fd3SRobert Mustacchi zen_udf_open(dev_t *devp, int flags, int otype, cred_t *credp)
46549e0fd3SRobert Mustacchi {
47549e0fd3SRobert Mustacchi minor_t m;
48549e0fd3SRobert Mustacchi zen_udf_t *zen_udf = &zen_udf_data;
49549e0fd3SRobert Mustacchi
50549e0fd3SRobert Mustacchi if (crgetzoneid(credp) != GLOBAL_ZONEID ||
51549e0fd3SRobert Mustacchi secpolicy_hwmanip(credp) != 0) {
52549e0fd3SRobert Mustacchi return (EPERM);
53549e0fd3SRobert Mustacchi }
54549e0fd3SRobert Mustacchi
55549e0fd3SRobert Mustacchi if ((flags & (FEXCL | FNDELAY | FNONBLOCK)) != 0) {
56549e0fd3SRobert Mustacchi return (EINVAL);
57549e0fd3SRobert Mustacchi }
58549e0fd3SRobert Mustacchi
59549e0fd3SRobert Mustacchi if (otype != OTYP_CHR) {
60549e0fd3SRobert Mustacchi return (EINVAL);
61549e0fd3SRobert Mustacchi }
62549e0fd3SRobert Mustacchi
63549e0fd3SRobert Mustacchi m = getminor(*devp);
64549e0fd3SRobert Mustacchi if (m >= zen_udf->zudf_ndfs) {
65549e0fd3SRobert Mustacchi return (ENXIO);
66549e0fd3SRobert Mustacchi }
67549e0fd3SRobert Mustacchi
68549e0fd3SRobert Mustacchi return (0);
69549e0fd3SRobert Mustacchi }
70549e0fd3SRobert Mustacchi
71549e0fd3SRobert Mustacchi static int
zen_udf_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)72549e0fd3SRobert Mustacchi zen_udf_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
73549e0fd3SRobert Mustacchi int *rvalp)
74549e0fd3SRobert Mustacchi {
75549e0fd3SRobert Mustacchi uint_t dfno;
76549e0fd3SRobert Mustacchi zen_udf_t *zen_udf = &zen_udf_data;
77549e0fd3SRobert Mustacchi zen_udf_io_t zui;
78*71815ce7SRobert Mustacchi df_reg_def_t def;
79549e0fd3SRobert Mustacchi
80549e0fd3SRobert Mustacchi if (cmd != ZEN_UDF_READ32 && cmd != ZEN_UDF_READ64) {
81549e0fd3SRobert Mustacchi return (ENOTTY);
82549e0fd3SRobert Mustacchi }
83549e0fd3SRobert Mustacchi
84549e0fd3SRobert Mustacchi dfno = getminor(dev);
85549e0fd3SRobert Mustacchi if (dfno >= zen_udf->zudf_ndfs) {
86549e0fd3SRobert Mustacchi return (ENXIO);
87549e0fd3SRobert Mustacchi }
88549e0fd3SRobert Mustacchi
89549e0fd3SRobert Mustacchi if (crgetzoneid(credp) != GLOBAL_ZONEID ||
90549e0fd3SRobert Mustacchi secpolicy_hwmanip(credp) != 0) {
91549e0fd3SRobert Mustacchi return (EPERM);
92549e0fd3SRobert Mustacchi }
93549e0fd3SRobert Mustacchi
94549e0fd3SRobert Mustacchi if (ddi_copyin((void *)arg, &zui, sizeof (zui), mode & FKIOCTL) != 0) {
95549e0fd3SRobert Mustacchi return (EFAULT);
96549e0fd3SRobert Mustacchi }
97549e0fd3SRobert Mustacchi
98*71815ce7SRobert Mustacchi /*
99*71815ce7SRobert Mustacchi * Cons up a register definition based on the user request. We set the
100*71815ce7SRobert Mustacchi * gen to our current one.
101*71815ce7SRobert Mustacchi */
102*71815ce7SRobert Mustacchi def.drd_gens = amdzen_c_df_rev();
103*71815ce7SRobert Mustacchi def.drd_func = zui.zui_func;
104*71815ce7SRobert Mustacchi def.drd_reg = zui.zui_reg;
105*71815ce7SRobert Mustacchi
106549e0fd3SRobert Mustacchi if (cmd == ZEN_UDF_READ32) {
107549e0fd3SRobert Mustacchi int ret;
108549e0fd3SRobert Mustacchi uint32_t data;
109549e0fd3SRobert Mustacchi
110*71815ce7SRobert Mustacchi ret = amdzen_c_df_read32(dfno, zui.zui_inst, def, &data);
111549e0fd3SRobert Mustacchi if (ret != 0) {
112549e0fd3SRobert Mustacchi return (ret);
113549e0fd3SRobert Mustacchi }
114549e0fd3SRobert Mustacchi
115549e0fd3SRobert Mustacchi zui.zui_data = data;
116549e0fd3SRobert Mustacchi } else {
117549e0fd3SRobert Mustacchi int ret;
118549e0fd3SRobert Mustacchi
119*71815ce7SRobert Mustacchi ret = amdzen_c_df_read64(dfno, zui.zui_inst, def,
120*71815ce7SRobert Mustacchi &zui.zui_data);
121549e0fd3SRobert Mustacchi if (ret != 0) {
122549e0fd3SRobert Mustacchi return (ret);
123549e0fd3SRobert Mustacchi }
124549e0fd3SRobert Mustacchi }
125549e0fd3SRobert Mustacchi
126549e0fd3SRobert Mustacchi if (ddi_copyout(&zui, (void *)arg, sizeof (zui), mode & FKIOCTL) != 0) {
127549e0fd3SRobert Mustacchi return (EFAULT);
128549e0fd3SRobert Mustacchi }
129549e0fd3SRobert Mustacchi
130549e0fd3SRobert Mustacchi return (0);
131549e0fd3SRobert Mustacchi }
132549e0fd3SRobert Mustacchi
133549e0fd3SRobert Mustacchi static int
zen_udf_close(dev_t dev,int flag,int otyp,cred_t * credp)134549e0fd3SRobert Mustacchi zen_udf_close(dev_t dev, int flag, int otyp, cred_t *credp)
135549e0fd3SRobert Mustacchi {
136549e0fd3SRobert Mustacchi return (0);
137549e0fd3SRobert Mustacchi }
138549e0fd3SRobert Mustacchi
139549e0fd3SRobert Mustacchi static void
zen_udf_cleanup(zen_udf_t * zen_udf)140549e0fd3SRobert Mustacchi zen_udf_cleanup(zen_udf_t *zen_udf)
141549e0fd3SRobert Mustacchi {
142549e0fd3SRobert Mustacchi ddi_remove_minor_node(zen_udf->zudf_dip, NULL);
143549e0fd3SRobert Mustacchi zen_udf->zudf_ndfs = 0;
144549e0fd3SRobert Mustacchi zen_udf->zudf_dip = NULL;
145549e0fd3SRobert Mustacchi }
146549e0fd3SRobert Mustacchi
147549e0fd3SRobert Mustacchi static int
zen_udf_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)148549e0fd3SRobert Mustacchi zen_udf_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
149549e0fd3SRobert Mustacchi {
150549e0fd3SRobert Mustacchi zen_udf_t *zen_udf = &zen_udf_data;
151549e0fd3SRobert Mustacchi
152549e0fd3SRobert Mustacchi if (cmd == DDI_RESUME) {
153549e0fd3SRobert Mustacchi return (DDI_SUCCESS);
154549e0fd3SRobert Mustacchi } else if (cmd != DDI_ATTACH) {
155549e0fd3SRobert Mustacchi return (DDI_FAILURE);
156549e0fd3SRobert Mustacchi }
157549e0fd3SRobert Mustacchi
158549e0fd3SRobert Mustacchi if (zen_udf->zudf_dip != NULL) {
159549e0fd3SRobert Mustacchi dev_err(dip, CE_WARN, "!zen_udf is already attached to a "
160549e0fd3SRobert Mustacchi "dev_info_t: %p", zen_udf->zudf_dip);
161549e0fd3SRobert Mustacchi return (DDI_FAILURE);
162549e0fd3SRobert Mustacchi }
163549e0fd3SRobert Mustacchi
164549e0fd3SRobert Mustacchi zen_udf->zudf_dip = dip;
165549e0fd3SRobert Mustacchi zen_udf->zudf_ndfs = amdzen_c_df_count();
166549e0fd3SRobert Mustacchi for (uint_t i = 0; i < zen_udf->zudf_ndfs; i++) {
167549e0fd3SRobert Mustacchi char buf[32];
168549e0fd3SRobert Mustacchi
169549e0fd3SRobert Mustacchi (void) snprintf(buf, sizeof (buf), "zen_udf.%u", i);
170549e0fd3SRobert Mustacchi if (ddi_create_minor_node(dip, buf, S_IFCHR, i, DDI_PSEUDO,
171549e0fd3SRobert Mustacchi 0) != DDI_SUCCESS) {
172549e0fd3SRobert Mustacchi dev_err(dip, CE_WARN, "!failed to create minor %s",
173549e0fd3SRobert Mustacchi buf);
174549e0fd3SRobert Mustacchi goto err;
175549e0fd3SRobert Mustacchi }
176549e0fd3SRobert Mustacchi }
177549e0fd3SRobert Mustacchi
178549e0fd3SRobert Mustacchi return (DDI_SUCCESS);
179549e0fd3SRobert Mustacchi
180549e0fd3SRobert Mustacchi err:
181549e0fd3SRobert Mustacchi zen_udf_cleanup(zen_udf);
182549e0fd3SRobert Mustacchi return (DDI_FAILURE);
183549e0fd3SRobert Mustacchi }
184549e0fd3SRobert Mustacchi
185549e0fd3SRobert Mustacchi static int
zen_udf_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)186549e0fd3SRobert Mustacchi zen_udf_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
187549e0fd3SRobert Mustacchi {
188549e0fd3SRobert Mustacchi zen_udf_t *zen_udf = &zen_udf_data;
189549e0fd3SRobert Mustacchi minor_t m;
190549e0fd3SRobert Mustacchi
191549e0fd3SRobert Mustacchi switch (cmd) {
192549e0fd3SRobert Mustacchi case DDI_INFO_DEVT2DEVINFO:
193549e0fd3SRobert Mustacchi m = getminor((dev_t)arg);
194549e0fd3SRobert Mustacchi if (m >= zen_udf->zudf_ndfs) {
195549e0fd3SRobert Mustacchi return (DDI_FAILURE);
196549e0fd3SRobert Mustacchi }
197549e0fd3SRobert Mustacchi *resultp = (void *)zen_udf->zudf_dip;
198549e0fd3SRobert Mustacchi break;
199549e0fd3SRobert Mustacchi case DDI_INFO_DEVT2INSTANCE:
200549e0fd3SRobert Mustacchi m = getminor((dev_t)arg);
201549e0fd3SRobert Mustacchi if (m >= zen_udf->zudf_ndfs) {
202549e0fd3SRobert Mustacchi return (DDI_FAILURE);
203549e0fd3SRobert Mustacchi }
204549e0fd3SRobert Mustacchi *resultp = (void *)(uintptr_t)ddi_get_instance(
205549e0fd3SRobert Mustacchi zen_udf->zudf_dip);
206549e0fd3SRobert Mustacchi break;
207549e0fd3SRobert Mustacchi default:
208549e0fd3SRobert Mustacchi return (DDI_FAILURE);
209549e0fd3SRobert Mustacchi }
210549e0fd3SRobert Mustacchi return (DDI_SUCCESS);
211549e0fd3SRobert Mustacchi }
212549e0fd3SRobert Mustacchi
213549e0fd3SRobert Mustacchi static int
zen_udf_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)214549e0fd3SRobert Mustacchi zen_udf_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
215549e0fd3SRobert Mustacchi {
216549e0fd3SRobert Mustacchi zen_udf_t *zen_udf = &zen_udf_data;
217549e0fd3SRobert Mustacchi
218549e0fd3SRobert Mustacchi if (cmd == DDI_SUSPEND) {
219549e0fd3SRobert Mustacchi return (DDI_SUCCESS);
220549e0fd3SRobert Mustacchi } else if (cmd != DDI_DETACH) {
221549e0fd3SRobert Mustacchi return (DDI_FAILURE);
222549e0fd3SRobert Mustacchi }
223549e0fd3SRobert Mustacchi
224549e0fd3SRobert Mustacchi if (zen_udf->zudf_dip != dip) {
225549e0fd3SRobert Mustacchi dev_err(dip, CE_WARN, "!asked to detach zen_udf, but dip "
226549e0fd3SRobert Mustacchi "doesn't match");
227549e0fd3SRobert Mustacchi return (DDI_FAILURE);
228549e0fd3SRobert Mustacchi }
229549e0fd3SRobert Mustacchi
230549e0fd3SRobert Mustacchi zen_udf_cleanup(zen_udf);
231549e0fd3SRobert Mustacchi return (DDI_SUCCESS);
232549e0fd3SRobert Mustacchi }
233549e0fd3SRobert Mustacchi
234549e0fd3SRobert Mustacchi static struct cb_ops zen_udf_cb_ops = {
235549e0fd3SRobert Mustacchi .cb_open = zen_udf_open,
236549e0fd3SRobert Mustacchi .cb_close = zen_udf_close,
237549e0fd3SRobert Mustacchi .cb_strategy = nodev,
238549e0fd3SRobert Mustacchi .cb_print = nodev,
239549e0fd3SRobert Mustacchi .cb_dump = nodev,
240549e0fd3SRobert Mustacchi .cb_read = nodev,
241549e0fd3SRobert Mustacchi .cb_write = nodev,
242549e0fd3SRobert Mustacchi .cb_ioctl = zen_udf_ioctl,
243549e0fd3SRobert Mustacchi .cb_devmap = nodev,
244549e0fd3SRobert Mustacchi .cb_mmap = nodev,
245549e0fd3SRobert Mustacchi .cb_segmap = nodev,
246549e0fd3SRobert Mustacchi .cb_chpoll = nochpoll,
247549e0fd3SRobert Mustacchi .cb_prop_op = ddi_prop_op,
248549e0fd3SRobert Mustacchi .cb_flag = D_MP,
249549e0fd3SRobert Mustacchi .cb_rev = CB_REV,
250549e0fd3SRobert Mustacchi .cb_aread = nodev,
251549e0fd3SRobert Mustacchi .cb_awrite = nodev
252549e0fd3SRobert Mustacchi };
253549e0fd3SRobert Mustacchi
254549e0fd3SRobert Mustacchi static struct dev_ops zen_udf_dev_ops = {
255549e0fd3SRobert Mustacchi .devo_rev = DEVO_REV,
256549e0fd3SRobert Mustacchi .devo_refcnt = 0,
257549e0fd3SRobert Mustacchi .devo_getinfo = zen_udf_getinfo,
258549e0fd3SRobert Mustacchi .devo_identify = nulldev,
259549e0fd3SRobert Mustacchi .devo_probe = nulldev,
260549e0fd3SRobert Mustacchi .devo_attach = zen_udf_attach,
261549e0fd3SRobert Mustacchi .devo_detach = zen_udf_detach,
262549e0fd3SRobert Mustacchi .devo_reset = nodev,
263549e0fd3SRobert Mustacchi .devo_quiesce = ddi_quiesce_not_needed,
264549e0fd3SRobert Mustacchi .devo_cb_ops = &zen_udf_cb_ops
265549e0fd3SRobert Mustacchi };
266549e0fd3SRobert Mustacchi
267549e0fd3SRobert Mustacchi static struct modldrv zen_udf_modldrv = {
268549e0fd3SRobert Mustacchi .drv_modops = &mod_driverops,
269549e0fd3SRobert Mustacchi .drv_linkinfo = "AMD User DF Access",
270549e0fd3SRobert Mustacchi .drv_dev_ops = &zen_udf_dev_ops
271549e0fd3SRobert Mustacchi };
272549e0fd3SRobert Mustacchi
273549e0fd3SRobert Mustacchi static struct modlinkage zen_udf_modlinkage = {
274549e0fd3SRobert Mustacchi .ml_rev = MODREV_1,
275549e0fd3SRobert Mustacchi .ml_linkage = { &zen_udf_modldrv, NULL }
276549e0fd3SRobert Mustacchi };
277549e0fd3SRobert Mustacchi
278549e0fd3SRobert Mustacchi int
_init(void)279549e0fd3SRobert Mustacchi _init(void)
280549e0fd3SRobert Mustacchi {
281549e0fd3SRobert Mustacchi return (mod_install(&zen_udf_modlinkage));
282549e0fd3SRobert Mustacchi }
283549e0fd3SRobert Mustacchi
284549e0fd3SRobert Mustacchi int
_info(struct modinfo * modinfop)285549e0fd3SRobert Mustacchi _info(struct modinfo *modinfop)
286549e0fd3SRobert Mustacchi {
287549e0fd3SRobert Mustacchi return (mod_info(&zen_udf_modlinkage, modinfop));
288549e0fd3SRobert Mustacchi }
289549e0fd3SRobert Mustacchi
290549e0fd3SRobert Mustacchi int
_fini(void)291549e0fd3SRobert Mustacchi _fini(void)
292549e0fd3SRobert Mustacchi {
293549e0fd3SRobert Mustacchi return (mod_remove(&zen_udf_modlinkage));
294549e0fd3SRobert Mustacchi }
295