xref: /illumos-gate/usr/src/uts/intel/io/amdzen/usmn.c (revision 4adf43b0)
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