1047043c2SRobert Mustacchi /*
2047043c2SRobert Mustacchi * This file and its contents are supplied under the terms of the
3047043c2SRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4047043c2SRobert Mustacchi * You may only use this file in accordance with the terms of version
5047043c2SRobert Mustacchi * 1.0 of the CDDL.
6047043c2SRobert Mustacchi *
7047043c2SRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8047043c2SRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at
9047043c2SRobert Mustacchi * http://www.illumos.org/license/CDDL.
10047043c2SRobert Mustacchi */
11047043c2SRobert Mustacchi
12047043c2SRobert Mustacchi /*
13ba215efeSKeith M Wesolowski * Copyright 2022 Oxide Computer Company
14047043c2SRobert Mustacchi */
15047043c2SRobert Mustacchi
16047043c2SRobert Mustacchi /*
17047043c2SRobert Mustacchi * A device driver that provides user access to the AMD System Management
18047043c2SRobert Mustacchi * Network for debugging purposes.
19047043c2SRobert Mustacchi */
20047043c2SRobert Mustacchi
21047043c2SRobert Mustacchi #include <sys/types.h>
22047043c2SRobert Mustacchi #include <sys/file.h>
23047043c2SRobert Mustacchi #include <sys/errno.h>
24047043c2SRobert Mustacchi #include <sys/open.h>
25047043c2SRobert Mustacchi #include <sys/cred.h>
26047043c2SRobert Mustacchi #include <sys/ddi.h>
27047043c2SRobert Mustacchi #include <sys/sunddi.h>
28047043c2SRobert Mustacchi #include <sys/stat.h>
29047043c2SRobert Mustacchi #include <sys/conf.h>
30047043c2SRobert Mustacchi #include <sys/devops.h>
31047043c2SRobert Mustacchi #include <sys/cmn_err.h>
32047043c2SRobert Mustacchi #include <sys/policy.h>
33047043c2SRobert Mustacchi #include <amdzen_client.h>
34047043c2SRobert Mustacchi
35047043c2SRobert Mustacchi #include "usmn.h"
36047043c2SRobert Mustacchi
37047043c2SRobert Mustacchi typedef struct usmn {
38047043c2SRobert Mustacchi dev_info_t *usmn_dip;
39047043c2SRobert Mustacchi uint_t usmn_ndfs;
40047043c2SRobert Mustacchi } usmn_t;
41047043c2SRobert Mustacchi
42047043c2SRobert Mustacchi static usmn_t usmn_data;
43047043c2SRobert Mustacchi
44047043c2SRobert Mustacchi static int
usmn_open(dev_t * devp,int flags,int otype,cred_t * credp)45047043c2SRobert Mustacchi usmn_open(dev_t *devp, int flags, int otype, cred_t *credp)
46047043c2SRobert Mustacchi {
47047043c2SRobert Mustacchi minor_t m;
48047043c2SRobert Mustacchi usmn_t *usmn = &usmn_data;
49047043c2SRobert Mustacchi
50047043c2SRobert Mustacchi if (crgetzoneid(credp) != GLOBAL_ZONEID ||
51047043c2SRobert Mustacchi secpolicy_hwmanip(credp) != 0) {
52047043c2SRobert Mustacchi return (EPERM);
53047043c2SRobert Mustacchi }
54047043c2SRobert Mustacchi
55047043c2SRobert Mustacchi if ((flags & (FEXCL | FNDELAY | FNONBLOCK)) != 0) {
56047043c2SRobert Mustacchi return (EINVAL);
57047043c2SRobert Mustacchi }
58047043c2SRobert Mustacchi
59047043c2SRobert Mustacchi if (otype != OTYP_CHR) {
60047043c2SRobert Mustacchi return (EINVAL);
61047043c2SRobert Mustacchi }
62047043c2SRobert Mustacchi
63047043c2SRobert Mustacchi m = getminor(*devp);
64047043c2SRobert Mustacchi if (m >= usmn->usmn_ndfs) {
65047043c2SRobert Mustacchi return (ENXIO);
66047043c2SRobert Mustacchi }
67047043c2SRobert Mustacchi
68047043c2SRobert Mustacchi return (0);
69047043c2SRobert Mustacchi }
70047043c2SRobert Mustacchi
71047043c2SRobert Mustacchi static int
usmn_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)72047043c2SRobert Mustacchi usmn_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
73047043c2SRobert Mustacchi int *rvalp)
74047043c2SRobert Mustacchi {
75047043c2SRobert Mustacchi uint_t dfno;
76047043c2SRobert Mustacchi usmn_t *usmn = &usmn_data;
77047043c2SRobert Mustacchi usmn_reg_t usr;
78047043c2SRobert Mustacchi
79047043c2SRobert Mustacchi if (cmd != USMN_READ && cmd != USMN_WRITE) {
80047043c2SRobert Mustacchi return (ENOTTY);
81047043c2SRobert Mustacchi }
82047043c2SRobert Mustacchi
83047043c2SRobert Mustacchi dfno = getminor(dev);
84047043c2SRobert Mustacchi if (dfno >= usmn->usmn_ndfs) {
85047043c2SRobert Mustacchi return (ENXIO);
86047043c2SRobert Mustacchi }
87047043c2SRobert Mustacchi
88047043c2SRobert Mustacchi if (crgetzoneid(credp) != GLOBAL_ZONEID ||
89047043c2SRobert Mustacchi secpolicy_hwmanip(credp) != 0) {
90047043c2SRobert Mustacchi return (EPERM);
91047043c2SRobert Mustacchi }
92047043c2SRobert Mustacchi
93047043c2SRobert Mustacchi if (ddi_copyin((void *)arg, &usr, sizeof (usr), mode & FKIOCTL) != 0) {
94047043c2SRobert Mustacchi return (EFAULT);
95047043c2SRobert Mustacchi }
96047043c2SRobert Mustacchi
97*4adf43b0SKeith M Wesolowski /*
98*4adf43b0SKeith M Wesolowski * We don't need to check size and alignment here; the client access
99*4adf43b0SKeith M Wesolowski * routines do so for us and return EINVAL if violated. The same goes
100*4adf43b0SKeith M Wesolowski * for the value to be written in the USMN_WRITE case below.
101*4adf43b0SKeith M Wesolowski */
102*4adf43b0SKeith M Wesolowski const smn_reg_t reg = SMN_MAKE_REG_SIZED(usr.usr_addr, usr.usr_size);
103*4adf43b0SKeith M Wesolowski
104047043c2SRobert Mustacchi if (cmd == USMN_READ) {
105047043c2SRobert Mustacchi int ret;
106047043c2SRobert Mustacchi
107f198607dSRobert Mustacchi if ((mode & FREAD) == 0) {
108f198607dSRobert Mustacchi return (EINVAL);
109f198607dSRobert Mustacchi }
110f198607dSRobert Mustacchi
111*4adf43b0SKeith M Wesolowski ret = amdzen_c_smn_read(dfno, reg, &usr.usr_data);
112047043c2SRobert Mustacchi if (ret != 0) {
113047043c2SRobert Mustacchi return (ret);
114047043c2SRobert Mustacchi }
115f198607dSRobert Mustacchi } else if (cmd == USMN_WRITE) {
116f198607dSRobert Mustacchi int ret;
117f198607dSRobert Mustacchi
118f198607dSRobert Mustacchi if ((mode & FWRITE) == 0) {
119f198607dSRobert Mustacchi return (EINVAL);
120f198607dSRobert Mustacchi }
121f198607dSRobert Mustacchi
122*4adf43b0SKeith M Wesolowski ret = amdzen_c_smn_write(dfno, reg, usr.usr_data);
123f198607dSRobert Mustacchi if (ret != 0) {
124f198607dSRobert Mustacchi return (ret);
125f198607dSRobert Mustacchi }
126047043c2SRobert Mustacchi } else {
127047043c2SRobert Mustacchi return (ENOTSUP);
128047043c2SRobert Mustacchi }
129047043c2SRobert Mustacchi
130f198607dSRobert Mustacchi if (cmd == USMN_READ &&
131f198607dSRobert Mustacchi ddi_copyout(&usr, (void *)arg, sizeof (usr), mode & FKIOCTL) != 0) {
132047043c2SRobert Mustacchi return (EFAULT);
133047043c2SRobert Mustacchi }
134047043c2SRobert Mustacchi
135047043c2SRobert Mustacchi return (0);
136047043c2SRobert Mustacchi }
137047043c2SRobert Mustacchi
138047043c2SRobert Mustacchi static int
usmn_close(dev_t dev,int flag,int otyp,cred_t * credp)139047043c2SRobert Mustacchi usmn_close(dev_t dev, int flag, int otyp, cred_t *credp)
140047043c2SRobert Mustacchi {
141047043c2SRobert Mustacchi return (0);
142047043c2SRobert Mustacchi }
143047043c2SRobert Mustacchi
144047043c2SRobert Mustacchi static void
usmn_cleanup(usmn_t * usmn)145047043c2SRobert Mustacchi usmn_cleanup(usmn_t *usmn)
146047043c2SRobert Mustacchi {
147047043c2SRobert Mustacchi ddi_remove_minor_node(usmn->usmn_dip, NULL);
148047043c2SRobert Mustacchi usmn->usmn_ndfs = 0;
149047043c2SRobert Mustacchi usmn->usmn_dip = NULL;
150047043c2SRobert Mustacchi }
151047043c2SRobert Mustacchi
152047043c2SRobert Mustacchi static int
usmn_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)153047043c2SRobert Mustacchi usmn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
154047043c2SRobert Mustacchi {
155047043c2SRobert Mustacchi usmn_t *usmn = &usmn_data;
156047043c2SRobert Mustacchi
157047043c2SRobert Mustacchi if (cmd == DDI_RESUME) {
158047043c2SRobert Mustacchi return (DDI_SUCCESS);
159047043c2SRobert Mustacchi } else if (cmd != DDI_ATTACH) {
160047043c2SRobert Mustacchi return (DDI_FAILURE);
161047043c2SRobert Mustacchi }
162047043c2SRobert Mustacchi
163047043c2SRobert Mustacchi if (usmn->usmn_dip != NULL) {
164047043c2SRobert Mustacchi dev_err(dip, CE_WARN, "!usmn is already attached to a "
165047043c2SRobert Mustacchi "dev_info_t: %p", usmn->usmn_dip);
166047043c2SRobert Mustacchi return (DDI_FAILURE);
167047043c2SRobert Mustacchi }
168047043c2SRobert Mustacchi
169047043c2SRobert Mustacchi usmn->usmn_dip = dip;
170047043c2SRobert Mustacchi usmn->usmn_ndfs = amdzen_c_df_count();
171047043c2SRobert Mustacchi for (uint_t i = 0; i < usmn->usmn_ndfs; i++) {
172047043c2SRobert Mustacchi char buf[32];
173047043c2SRobert Mustacchi
174047043c2SRobert Mustacchi (void) snprintf(buf, sizeof (buf), "usmn.%u", i);
175047043c2SRobert Mustacchi if (ddi_create_minor_node(dip, buf, S_IFCHR, i, DDI_PSEUDO,
176047043c2SRobert Mustacchi 0) != DDI_SUCCESS) {
177047043c2SRobert Mustacchi dev_err(dip, CE_WARN, "!failed to create minor %s",
178047043c2SRobert Mustacchi buf);
179047043c2SRobert Mustacchi goto err;
180047043c2SRobert Mustacchi }
181047043c2SRobert Mustacchi }
182047043c2SRobert Mustacchi
183047043c2SRobert Mustacchi return (DDI_SUCCESS);
184047043c2SRobert Mustacchi
185047043c2SRobert Mustacchi err:
186047043c2SRobert Mustacchi usmn_cleanup(usmn);
187047043c2SRobert Mustacchi return (DDI_FAILURE);
188047043c2SRobert Mustacchi }
189047043c2SRobert Mustacchi
190047043c2SRobert Mustacchi static int
usmn_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)191047043c2SRobert Mustacchi usmn_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
192047043c2SRobert Mustacchi {
193047043c2SRobert Mustacchi usmn_t *usmn = &usmn_data;
194047043c2SRobert Mustacchi minor_t m;
195047043c2SRobert Mustacchi
196047043c2SRobert Mustacchi switch (cmd) {
197047043c2SRobert Mustacchi case DDI_INFO_DEVT2DEVINFO:
198047043c2SRobert Mustacchi m = getminor((dev_t)arg);
199047043c2SRobert Mustacchi if (m >= usmn->usmn_ndfs) {
200047043c2SRobert Mustacchi return (DDI_FAILURE);
201047043c2SRobert Mustacchi }
202047043c2SRobert Mustacchi *resultp = (void *)usmn->usmn_dip;
203047043c2SRobert Mustacchi break;
204047043c2SRobert Mustacchi case DDI_INFO_DEVT2INSTANCE:
205047043c2SRobert Mustacchi m = getminor((dev_t)arg);
206047043c2SRobert Mustacchi if (m >= usmn->usmn_ndfs) {
207047043c2SRobert Mustacchi return (DDI_FAILURE);
208047043c2SRobert Mustacchi }
209047043c2SRobert Mustacchi *resultp = (void *)(uintptr_t)ddi_get_instance(usmn->usmn_dip);
210047043c2SRobert Mustacchi break;
211047043c2SRobert Mustacchi default:
212047043c2SRobert Mustacchi return (DDI_FAILURE);
213047043c2SRobert Mustacchi }
214047043c2SRobert Mustacchi return (DDI_SUCCESS);
215047043c2SRobert Mustacchi }
216047043c2SRobert Mustacchi
217047043c2SRobert Mustacchi static int
usmn_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)218047043c2SRobert Mustacchi usmn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
219047043c2SRobert Mustacchi {
220047043c2SRobert Mustacchi usmn_t *usmn = &usmn_data;
221047043c2SRobert Mustacchi
222047043c2SRobert Mustacchi if (cmd == DDI_SUSPEND) {
223047043c2SRobert Mustacchi return (DDI_SUCCESS);
224047043c2SRobert Mustacchi } else if (cmd != DDI_DETACH) {
225047043c2SRobert Mustacchi return (DDI_FAILURE);
226047043c2SRobert Mustacchi }
227047043c2SRobert Mustacchi
228047043c2SRobert Mustacchi if (usmn->usmn_dip != dip) {
229047043c2SRobert Mustacchi dev_err(dip, CE_WARN, "!asked to detach usmn, but dip doesn't "
230047043c2SRobert Mustacchi "match");
231047043c2SRobert Mustacchi return (DDI_FAILURE);
232047043c2SRobert Mustacchi }
233047043c2SRobert Mustacchi
234047043c2SRobert Mustacchi usmn_cleanup(usmn);
235047043c2SRobert Mustacchi return (DDI_SUCCESS);
236047043c2SRobert Mustacchi }
237047043c2SRobert Mustacchi
238047043c2SRobert Mustacchi static struct cb_ops usmn_cb_ops = {
239047043c2SRobert Mustacchi .cb_open = usmn_open,
240047043c2SRobert Mustacchi .cb_close = usmn_close,
241047043c2SRobert Mustacchi .cb_strategy = nodev,
242047043c2SRobert Mustacchi .cb_print = nodev,
243047043c2SRobert Mustacchi .cb_dump = nodev,
244047043c2SRobert Mustacchi .cb_read = nodev,
245047043c2SRobert Mustacchi .cb_write = nodev,
246047043c2SRobert Mustacchi .cb_ioctl = usmn_ioctl,
247047043c2SRobert Mustacchi .cb_devmap = nodev,
248047043c2SRobert Mustacchi .cb_mmap = nodev,
249047043c2SRobert Mustacchi .cb_segmap = nodev,
250047043c2SRobert Mustacchi .cb_chpoll = nochpoll,
251047043c2SRobert Mustacchi .cb_prop_op = ddi_prop_op,
252047043c2SRobert Mustacchi .cb_flag = D_MP,
253047043c2SRobert Mustacchi .cb_rev = CB_REV,
254047043c2SRobert Mustacchi .cb_aread = nodev,
255047043c2SRobert Mustacchi .cb_awrite = nodev
256047043c2SRobert Mustacchi };
257047043c2SRobert Mustacchi
258047043c2SRobert Mustacchi static struct dev_ops usmn_dev_ops = {
259047043c2SRobert Mustacchi .devo_rev = DEVO_REV,
260047043c2SRobert Mustacchi .devo_refcnt = 0,
261047043c2SRobert Mustacchi .devo_getinfo = usmn_getinfo,
262047043c2SRobert Mustacchi .devo_identify = nulldev,
263047043c2SRobert Mustacchi .devo_probe = nulldev,
264047043c2SRobert Mustacchi .devo_attach = usmn_attach,
265047043c2SRobert Mustacchi .devo_detach = usmn_detach,
266047043c2SRobert Mustacchi .devo_reset = nodev,
267047043c2SRobert Mustacchi .devo_quiesce = ddi_quiesce_not_needed,
268047043c2SRobert Mustacchi .devo_cb_ops = &usmn_cb_ops
269047043c2SRobert Mustacchi };
270047043c2SRobert Mustacchi
271047043c2SRobert Mustacchi static struct modldrv usmn_modldrv = {
272047043c2SRobert Mustacchi .drv_modops = &mod_driverops,
273047043c2SRobert Mustacchi .drv_linkinfo = "AMD User SMN Access",
274047043c2SRobert Mustacchi .drv_dev_ops = &usmn_dev_ops
275047043c2SRobert Mustacchi };
276047043c2SRobert Mustacchi
277047043c2SRobert Mustacchi static struct modlinkage usmn_modlinkage = {
278047043c2SRobert Mustacchi .ml_rev = MODREV_1,
279047043c2SRobert Mustacchi .ml_linkage = { &usmn_modldrv, NULL }
280047043c2SRobert Mustacchi };
281047043c2SRobert Mustacchi
282047043c2SRobert Mustacchi int
_init(void)283047043c2SRobert Mustacchi _init(void)
284047043c2SRobert Mustacchi {
285047043c2SRobert Mustacchi return (mod_install(&usmn_modlinkage));
286047043c2SRobert Mustacchi }
287047043c2SRobert Mustacchi
288047043c2SRobert Mustacchi int
_info(struct modinfo * modinfop)289047043c2SRobert Mustacchi _info(struct modinfo *modinfop)
290047043c2SRobert Mustacchi {
291047043c2SRobert Mustacchi return (mod_info(&usmn_modlinkage, modinfop));
292047043c2SRobert Mustacchi }
293047043c2SRobert Mustacchi
294047043c2SRobert Mustacchi int
_fini(void)295047043c2SRobert Mustacchi _fini(void)
296047043c2SRobert Mustacchi {
297047043c2SRobert Mustacchi return (mod_remove(&usmn_modlinkage));
298047043c2SRobert Mustacchi }
299