xref: /illumos-gate/usr/src/uts/common/io/tphci.c (revision d5ebc4938a50bb2fb1914062e396761dc9161a51)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Copyright 2023 Oxide Computer Company
28  */
29 
30 /*
31  * The tphci driver can be used to exercise the mpxio framework together
32  * with tvhci/tclient.
33  */
34 
35 #include <sys/conf.h>
36 #include <sys/file.h>
37 #include <sys/open.h>
38 #include <sys/stat.h>
39 #include <sys/modctl.h>
40 #include <sys/ddi.h>
41 #include <sys/sunddi.h>
42 #include <sys/sunndi.h>
43 #include <sys/sunmdi.h>
44 #include <sys/disp.h>
45 
46 /* cb_ops entry points */
47 static int tphci_open(dev_t *, int, int, cred_t *);
48 static int tphci_close(dev_t, int, int, cred_t *);
49 static int tphci_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
50 static int tphci_attach(dev_info_t *, ddi_attach_cmd_t);
51 static int tphci_detach(dev_info_t *, ddi_detach_cmd_t);
52 static int tphci_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
53 
54 /* bus_ops entry points */
55 static int tphci_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
56     void *);
57 static int tphci_initchild(dev_info_t *, dev_info_t *);
58 static int tphci_uninitchild(dev_info_t *, dev_info_t *);
59 static int tphci_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t, void *,
60     dev_info_t **);
61 static int tphci_bus_unconfig(dev_info_t *, uint_t, ddi_bus_config_op_t,
62     void *);
63 static int tphci_intr_op(dev_info_t *dip, dev_info_t *rdip,
64     ddi_intr_op_t op, ddi_intr_handle_impl_t *hdlp, void *result);
65 
66 
67 static void *tphci_state;
68 struct tphci_state {
69 	dev_info_t *dip;
70 };
71 
72 static struct cb_ops tphci_cb_ops = {
73 	tphci_open,			/* open */
74 	tphci_close,			/* close */
75 	nodev,				/* strategy */
76 	nodev,				/* print */
77 	nodev,				/* dump */
78 	nodev,				/* read */
79 	nodev,				/* write */
80 	tphci_ioctl,			/* ioctl */
81 	nodev,				/* devmap */
82 	nodev,				/* mmap */
83 	nodev,				/* segmap */
84 	nochpoll,			/* chpoll */
85 	ddi_prop_op,			/* cb_prop_op */
86 	0,				/* streamtab */
87 	D_NEW | D_MP,			/* cb_flag */
88 	CB_REV,				/* rev */
89 	nodev,				/* aread */
90 	nodev				/* awrite */
91 };
92 
93 static struct bus_ops tphci_bus_ops = {
94 	BUSO_REV,			/* busops_rev */
95 	nullbusmap,			/* bus_map */
96 	NULL,				/* bus_get_intrspec */
97 	NULL,				/* bus_add_interspec */
98 	NULL,				/* bus_remove_interspec */
99 	i_ddi_map_fault,		/* bus_map_fault */
100 	ddi_no_dma_map,			/* bus_dma_map */
101 	ddi_no_dma_allochdl,		/* bus_dma_allochdl */
102 	NULL,				/* bus_dma_freehdl */
103 	NULL,				/* bus_dma_bindhdl */
104 	NULL,				/* bus_dma_unbindhdl */
105 	NULL,				/* bus_dma_flush */
106 	NULL,				/* bus_dma_win */
107 	NULL,				/* bus_dma_ctl */
108 	tphci_ctl,			/* bus_ctl */
109 	ddi_bus_prop_op,		/* bus_prop_op */
110 	NULL,				/* bus_get_eventcookie */
111 	NULL,				/* bus_add_eventcall */
112 	NULL,				/* bus_remove_event */
113 	NULL,				/* bus_post_event */
114 	NULL,				/* bus_intr_ctl */
115 	tphci_bus_config,		/* bus_config */
116 	tphci_bus_unconfig,		/* bus_unconfig */
117 	NULL,				/* bus_fm_init */
118 	NULL,				/* bus_fm_fini */
119 	NULL,				/* bus_fm_access_enter */
120 	NULL,				/* bus_fm_access_exit */
121 	NULL,				/* bus_power */
122 	tphci_intr_op			/* bus_intr_op */
123 };
124 
125 static struct dev_ops tphci_ops = {
126 	DEVO_REV,
127 	0,
128 	tphci_getinfo,
129 	nulldev,		/* identify */
130 	nulldev,		/* probe */
131 	tphci_attach,		/* attach and detach are mandatory */
132 	tphci_detach,
133 	nodev,			/* reset */
134 	&tphci_cb_ops,		/* cb_ops */
135 	&tphci_bus_ops,		/* bus_ops */
136 	NULL,			/* power */
137 	ddi_quiesce_not_needed,		/* quiesce */
138 };
139 
140 extern struct mod_ops mod_driverops;
141 
142 static struct modldrv modldrv = {
143 	&mod_driverops,
144 	"test phci driver",
145 	&tphci_ops
146 };
147 
148 static struct modlinkage modlinkage = {
149 	MODREV_1,
150 	&modldrv,
151 	NULL
152 };
153 
154 int
_init(void)155 _init(void)
156 {
157 	int rval;
158 
159 	if ((rval = ddi_soft_state_init(&tphci_state,
160 	    sizeof (struct tphci_state), 2)) != 0) {
161 		return (rval);
162 	}
163 
164 	if ((rval = mod_install(&modlinkage)) != 0) {
165 		ddi_soft_state_fini(&tphci_state);
166 	}
167 	return (rval);
168 }
169 
170 
171 int
_fini(void)172 _fini(void)
173 {
174 	int rval;
175 
176 	/*
177 	 * don't start cleaning up until we know that the module remove
178 	 * has worked  -- if this works, then we know that each instance
179 	 * has successfully been detached
180 	 */
181 	if ((rval = mod_remove(&modlinkage)) != 0) {
182 		return (rval);
183 	}
184 
185 	ddi_soft_state_fini(&tphci_state);
186 
187 	return (rval);
188 }
189 
190 int
_info(struct modinfo * modinfop)191 _info(struct modinfo *modinfop)
192 {
193 	return (mod_info(&modlinkage, modinfop));
194 }
195 
196 /* ARGSUSED */
197 static int
tphci_open(dev_t * devp,int flag,int otype,cred_t * credp)198 tphci_open(dev_t *devp, int flag, int otype, cred_t *credp)
199 {
200 	struct tphci_state *phci;
201 
202 	if (otype != OTYP_CHR) {
203 		return (EINVAL);
204 	}
205 
206 	phci = ddi_get_soft_state(tphci_state, getminor(*devp));
207 	if (phci == NULL) {
208 		return (ENXIO);
209 	}
210 
211 	return (0);
212 }
213 
214 
215 /* ARGSUSED */
216 static int
tphci_close(dev_t dev,int flag,int otype,cred_t * credp)217 tphci_close(dev_t dev, int flag, int otype, cred_t *credp)
218 {
219 	struct tphci_state *phci;
220 	if (otype != OTYP_CHR) {
221 		return (EINVAL);
222 	}
223 
224 	phci = ddi_get_soft_state(tphci_state, getminor(dev));
225 	if (phci == NULL) {
226 		return (ENXIO);
227 	}
228 
229 	return (0);
230 }
231 
232 /* ARGSUSED */
233 static int
tphci_ioctl(dev_t dev,int cmd,intptr_t data,int mode,cred_t * credp,int * rval)234 tphci_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
235     cred_t *credp, int *rval)
236 {
237 	return (0);
238 }
239 
240 /*
241  * attach the module
242  */
243 static int
tphci_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)244 tphci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
245 {
246 	char *vclass;
247 	int instance, phci_regis = 0;
248 	struct tphci_state *phci = NULL;
249 
250 	instance = ddi_get_instance(dip);
251 
252 	switch (cmd) {
253 	case DDI_ATTACH:
254 		break;
255 
256 	case DDI_RESUME:
257 	case DDI_PM_RESUME:
258 		return (0);	/* nothing to do */
259 
260 	default:
261 		return (DDI_FAILURE);
262 	}
263 
264 	/*
265 	 * Allocate phci data structure.
266 	 */
267 	if (ddi_soft_state_zalloc(tphci_state, instance) != DDI_SUCCESS) {
268 		return (DDI_FAILURE);
269 	}
270 
271 	phci = ddi_get_soft_state(tphci_state, instance);
272 	ASSERT(phci != NULL);
273 	phci->dip = dip;
274 
275 	/* bus_addr has the form #,<vhci_class> */
276 	vclass = strchr(ddi_get_name_addr(dip), ',');
277 	if (vclass == NULL || vclass[1] == '\0') {
278 		cmn_err(CE_NOTE, "tphci invalid bus_addr %s",
279 		    ddi_get_name_addr(dip));
280 		goto attach_fail;
281 	}
282 
283 	/*
284 	 * Attach this instance with the mpxio framework
285 	 */
286 	if (mdi_phci_register(vclass + 1, dip, 0) != MDI_SUCCESS) {
287 		cmn_err(CE_WARN, "%s mdi_phci_register failed",
288 		    ddi_node_name(dip));
289 		goto attach_fail;
290 	}
291 	phci_regis++;
292 
293 	if (ddi_create_minor_node(dip, "devctl", S_IFCHR,
294 	    instance, DDI_NT_SCSI_NEXUS, 0) != DDI_SUCCESS) {
295 		cmn_err(CE_NOTE, "%s ddi_create_minor_node failed",
296 		    ddi_node_name(dip));
297 		goto attach_fail;
298 	}
299 
300 	(void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, DDI_NO_AUTODETACH, 1);
301 	ddi_report_dev(dip);
302 	return (DDI_SUCCESS);
303 
304 attach_fail:
305 	if (phci_regis)
306 		(void) mdi_phci_unregister(dip, 0);
307 
308 	ddi_soft_state_free(tphci_state, instance);
309 	return (DDI_FAILURE);
310 }
311 
312 
313 /*ARGSUSED*/
314 static int
tphci_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)315 tphci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
316 {
317 	int instance = ddi_get_instance(dip);
318 
319 	switch (cmd) {
320 	case DDI_DETACH:
321 		break;
322 
323 	case DDI_SUSPEND:
324 	case DDI_PM_SUSPEND:
325 		return (0);	/* nothing to do */
326 
327 	default:
328 		return (DDI_FAILURE);
329 	}
330 
331 	if (mdi_phci_unregister(dip, 0) != MDI_SUCCESS)
332 		return (DDI_FAILURE);
333 
334 	ddi_remove_minor_node(dip, NULL);
335 	ddi_soft_state_free(tphci_state, instance);
336 
337 	return (DDI_SUCCESS);
338 }
339 
340 /*
341  * tphci_getinfo()
342  * Given the device number, return the devinfo pointer or the
343  * instance number.
344  * Note: always succeed DDI_INFO_DEVT2INSTANCE, even before attach.
345  */
346 
347 /*ARGSUSED*/
348 static int
tphci_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)349 tphci_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
350 {
351 	struct tphci_state *phci;
352 	int instance = getminor((dev_t)arg);
353 
354 	switch (cmd) {
355 	case DDI_INFO_DEVT2DEVINFO:
356 		phci = ddi_get_soft_state(tphci_state, instance);
357 		if (phci != NULL)
358 			*result = phci->dip;
359 		else {
360 			*result = NULL;
361 			return (DDI_FAILURE);
362 		}
363 		break;
364 
365 	case DDI_INFO_DEVT2INSTANCE:
366 		*result = (void *)(uintptr_t)instance;
367 		break;
368 
369 	default:
370 		return (DDI_FAILURE);
371 	}
372 
373 	return (DDI_SUCCESS);
374 }
375 
376 /*
377  * Interrupt stuff. NO OP for pseudo drivers.
378  */
379 /*ARGSUSED*/
380 static int
tphci_intr_op(dev_info_t * dip,dev_info_t * rdip,ddi_intr_op_t op,ddi_intr_handle_impl_t * hdlp,void * result)381 tphci_intr_op(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t op,
382     ddi_intr_handle_impl_t *hdlp, void *result)
383 {
384 	return (DDI_FAILURE);
385 }
386 
387 static int
tphci_ctl(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)388 tphci_ctl(dev_info_t *dip, dev_info_t *rdip,
389     ddi_ctl_enum_t ctlop, void *arg, void *result)
390 {
391 	switch (ctlop) {
392 	case DDI_CTLOPS_REPORTDEV:
393 		if (rdip == (dev_info_t *)0)
394 			return (DDI_FAILURE);
395 		cmn_err(CE_CONT, "?tphci-device: %s%d\n",
396 		    ddi_get_name(rdip), ddi_get_instance(rdip));
397 		return (DDI_SUCCESS);
398 
399 	case DDI_CTLOPS_INITCHILD:
400 	{
401 		dev_info_t *child = (dev_info_t *)arg;
402 		return (tphci_initchild(dip, child));
403 	}
404 
405 	case DDI_CTLOPS_UNINITCHILD:
406 	{
407 		dev_info_t *child = (dev_info_t *)arg;
408 		return (tphci_uninitchild(dip, child));
409 	}
410 
411 	case DDI_CTLOPS_DMAPMAPC:
412 	case DDI_CTLOPS_REPORTINT:
413 	case DDI_CTLOPS_REGSIZE:
414 	case DDI_CTLOPS_NREGS:
415 	case DDI_CTLOPS_SIDDEV:
416 	case DDI_CTLOPS_SLAVEONLY:
417 	case DDI_CTLOPS_AFFINITY:
418 	case DDI_CTLOPS_POKE:
419 	case DDI_CTLOPS_PEEK:
420 		/*
421 		 * These ops correspond to functions that "shouldn't" be called
422 		 * by a pseudo driver.  So we whine when we're called.
423 		 */
424 		cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n",
425 		    ddi_get_name(dip), ddi_get_instance(dip),
426 		    ctlop, ddi_get_name(rdip), ddi_get_instance(rdip));
427 		return (DDI_FAILURE);
428 
429 	case DDI_CTLOPS_ATTACH:
430 	case DDI_CTLOPS_BTOP:
431 	case DDI_CTLOPS_BTOPR:
432 	case DDI_CTLOPS_DETACH:
433 	case DDI_CTLOPS_DVMAPAGESIZE:
434 	case DDI_CTLOPS_IOMIN:
435 	case DDI_CTLOPS_POWER:
436 	case DDI_CTLOPS_PTOB:
437 	default:
438 		/*
439 		 * The ops that we pass up (default).  We pass up memory
440 		 * allocation oriented ops that we receive - these may be
441 		 * associated with pseudo HBA drivers below us with target
442 		 * drivers below them that use ddi memory allocation
443 		 * interfaces like scsi_alloc_consistent_buf.
444 		 */
445 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
446 	}
447 }
448 
449 static int
tphci_initchild(dev_info_t * dip,dev_info_t * child)450 tphci_initchild(dev_info_t *dip, dev_info_t *child)
451 {
452 	_NOTE(ARGUNUSED(dip))
453 	ddi_set_name_addr(child, "0");
454 	return (DDI_SUCCESS);
455 }
456 
457 /*ARGSUSED*/
458 static int
tphci_uninitchild(dev_info_t * dip,dev_info_t * child)459 tphci_uninitchild(dev_info_t *dip, dev_info_t *child)
460 {
461 	ddi_set_name_addr(child, NULL);
462 	return (DDI_SUCCESS);
463 }
464 
465 static int
tp_decode_name(char * devnm,char ** cname,char ** paddr,char ** guid)466 tp_decode_name(char *devnm, char **cname, char **paddr, char **guid)
467 {
468 	char *tmp;
469 
470 	i_ddi_parse_name(devnm, cname, paddr, NULL);
471 	if ((strcmp(*cname, "tclient") != 0) &&
472 	    (strcmp(*cname, "tphci") != 0) || *paddr == NULL)
473 		return (-1);
474 
475 	tmp = strchr(*paddr, ',');
476 	if (tmp == NULL || tmp[1] == '\0')
477 		return (-1);
478 
479 	*guid = tmp + 1;
480 	return (0);
481 }
482 
483 static int
tphci_bus_config(dev_info_t * parent,uint_t flags,ddi_bus_config_op_t op,void * arg,dev_info_t ** childp)484 tphci_bus_config(dev_info_t *parent, uint_t flags,
485     ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
486 {
487 	_NOTE(ARGUNUSED(flags))
488 	char		*cname, *paddr, *guid, *devnm;
489 	mdi_pathinfo_t	*pip;
490 	int		len, rval;
491 	boolean_t	enteredv;
492 
493 	switch (op) {
494 	case BUS_CONFIG_ONE:
495 		break;
496 	case BUS_CONFIG_DRIVER:	/* no direct children to configure */
497 	case BUS_CONFIG_ALL:
498 		return (NDI_SUCCESS);
499 	default:
500 		return (NDI_FAILURE);
501 	}
502 
503 	/* only implement BUS_CONFIG_ONE */
504 	devnm = i_ddi_strdup((char *)arg, KM_SLEEP);
505 	len = strlen(devnm) + 1;
506 
507 	/* caddr is hardcoded in the form *,<guid> */
508 	if (tp_decode_name(devnm, &cname, &paddr, &guid) != 0) {
509 		cmn_err(CE_NOTE, "tphci_bus_config -- invalid device %s",
510 		    (char *)arg);
511 		kmem_free(devnm, len);
512 		return (NDI_FAILURE);
513 	}
514 
515 	mdi_devi_enter(parent, &enteredv);
516 	rval = mdi_pi_alloc(parent, cname, guid, paddr, 0, &pip);
517 	kmem_free(devnm, len);
518 	if (rval != MDI_SUCCESS) {
519 		cmn_err(CE_NOTE, "tphci_bus_config -- mdi_pi_alloc failed");
520 		mdi_devi_exit(parent, enteredv);
521 		return (NDI_FAILURE);
522 	}
523 
524 	/*
525 	 * Hold the path and exit the pHCI while calling mdi_pi_online
526 	 * to avoid deadlock with power management of pHCI.
527 	 */
528 	mdi_hold_path(pip);
529 	mdi_devi_exit_phci(parent);
530 	rval = mdi_pi_online(pip, 0);
531 	mdi_devi_enter_phci(parent);
532 	mdi_rele_path(pip);
533 
534 	if (rval != MDI_SUCCESS) {
535 		cmn_err(CE_NOTE, "tphci_bus_config -- mdi_pi_online failed");
536 		(void) mdi_pi_free(pip, 0);
537 		mdi_devi_exit(parent, enteredv);
538 		return (NDI_FAILURE);
539 	}
540 
541 	if (childp) {
542 		*childp = mdi_pi_get_client(pip);
543 		ndi_hold_devi(*childp);
544 	}
545 	mdi_devi_exit(parent, enteredv);
546 
547 	return (NDI_SUCCESS);
548 }
549 
550 static int
tphci_bus_unconfig(dev_info_t * parent,uint_t flags,ddi_bus_config_op_t op,void * arg)551 tphci_bus_unconfig(dev_info_t *parent, uint_t flags,
552     ddi_bus_config_op_t op, void *arg)
553 {
554 	int		rval = MDI_SUCCESS;
555 	boolean_t	enteredv;
556 	mdi_pathinfo_t	*pip, *next;
557 	char		*devnm, *cname, *caddr;
558 
559 	switch (op) {
560 	case BUS_UNCONFIG_ONE:
561 		devnm = (char *)arg;
562 		i_ddi_parse_name(devnm, &cname, &caddr, NULL);
563 		if (strcmp(cname, "tclient") != 0)
564 			return (NDI_SUCCESS);	/* no such device */
565 
566 		mdi_devi_enter(parent, &enteredv);
567 		pip = mdi_pi_find(parent, NULL, caddr);
568 		if (pip) {
569 			mdi_hold_path(pip);
570 			mdi_devi_exit_phci(parent);
571 			rval = mdi_pi_offline(pip, NDI_DEVI_REMOVE);
572 			mdi_devi_enter_phci(parent);
573 			mdi_rele_path(pip);
574 
575 			if (rval == MDI_SUCCESS)
576 				(void) mdi_pi_free(pip, 0);
577 		}
578 		mdi_devi_exit(parent, enteredv);
579 		return (rval == MDI_SUCCESS ? NDI_SUCCESS : NDI_FAILURE);
580 
581 	case BUS_UNCONFIG_ALL:
582 		if (flags & NDI_AUTODETACH)
583 			return (NDI_FAILURE);
584 
585 		mdi_devi_enter(parent, &enteredv);
586 		next = mdi_get_next_client_path(parent, NULL);
587 		while ((pip = next) != NULL) {
588 			next = mdi_get_next_client_path(parent, pip);
589 
590 			mdi_hold_path(pip);
591 			mdi_devi_exit_phci(parent);
592 			rval = mdi_pi_offline(pip, NDI_DEVI_REMOVE);
593 			mdi_devi_enter_phci(parent);
594 			mdi_rele_path(pip);
595 
596 			if (rval != MDI_SUCCESS)
597 				break;
598 			(void) mdi_pi_free(pip, 0);
599 		}
600 		mdi_devi_exit(parent, enteredv);
601 		return (rval == MDI_SUCCESS ? NDI_SUCCESS : NDI_FAILURE);
602 
603 	case BUS_UNCONFIG_DRIVER:	/* nothing to do */
604 		return (NDI_SUCCESS);
605 
606 	default:
607 		return (NDI_FAILURE);
608 	}
609 	/*NOTREACHED*/
610 }
611