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 /*
23  *  Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  *  Use is subject to license terms.
25  * Copyright 2019 Joyent, Inc.
26  */
27 
28 /*
29  * This file contains the common hotplug code that is used by Standard
30  * PCIe and PCI HotPlug Controller code.
31  *
32  * NOTE: This file is compiled and delivered through misc/pcie module.
33  */
34 
35 #include <sys/types.h>
36 #include <sys/conf.h>
37 #include <sys/kmem.h>
38 #include <sys/debug.h>
39 #include <sys/vtrace.h>
40 #include <sys/autoconf.h>
41 #include <sys/varargs.h>
42 #include <sys/ddi_impldefs.h>
43 #include <sys/time.h>
44 #include <sys/note.h>
45 #include <sys/callb.h>
46 #include <sys/ddi.h>
47 #include <sys/sunddi.h>
48 #include <sys/sunndi.h>
49 #include <sys/sysevent.h>
50 #include <sys/sysevent/eventdefs.h>
51 #include <sys/sysevent/dr.h>
52 #include <sys/pci_impl.h>
53 #include <sys/pci_cap.h>
54 #include <sys/hotplug/pci/pcicfg.h>
55 #include <sys/hotplug/pci/pcie_hp.h>
56 #include <sys/hotplug/pci/pciehpc.h>
57 #include <sys/hotplug/pci/pcishpc.h>
58 #include <io/pciex/pcieb.h>
59 
60 /* Local functions prototype */
61 static int pcie_hp_list_occupants(dev_info_t *dip, void *arg);
62 static int pcie_hp_register_port(dev_info_t *dip, dev_info_t *pdip,
63     char *cn_name);
64 static int pcie_hp_register_ports_for_dev(dev_info_t *dip, int device_num);
65 static int pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t *info, void *arg);
66 static int pcie_hp_get_port_state(ddi_hp_cn_info_t *info, void *arg);
67 static int pcie_hp_match_dev_func(dev_info_t *dip, void *hdl);
68 static boolean_t pcie_hp_match_dev(dev_info_t *dip, int dev_num);
69 static int pcie_hp_get_df_from_port_name(char *cn_name, int *dev_num,
70     int *func_num);
71 static int pcie_hp_create_port_name_num(dev_info_t *dip,
72     ddi_hp_cn_info_t *cn_info);
73 static int pcie_hp_check_hardware_existence(dev_info_t *dip, int dev_num,
74     int func_num);
75 
76 /*
77  * Global functions (called by other drivers/modules)
78  */
79 
80 /*
81  * return description text for led state
82  */
83 char *
pcie_led_state_text(pcie_hp_led_state_t state)84 pcie_led_state_text(pcie_hp_led_state_t state)
85 {
86 	switch (state) {
87 	case PCIE_HP_LED_ON:
88 		return (PCIEHPC_PROP_VALUE_ON);
89 	case PCIE_HP_LED_OFF:
90 		return (PCIEHPC_PROP_VALUE_OFF);
91 	case PCIE_HP_LED_BLINK:
92 	default:
93 		return (PCIEHPC_PROP_VALUE_BLINK);
94 	}
95 }
96 
97 /*
98  * return description text for slot condition
99  */
100 char *
pcie_slot_condition_text(ap_condition_t condition)101 pcie_slot_condition_text(ap_condition_t condition)
102 {
103 	switch (condition) {
104 	case AP_COND_UNKNOWN:
105 		return (PCIEHPC_PROP_VALUE_UNKNOWN);
106 	case AP_COND_OK:
107 		return (PCIEHPC_PROP_VALUE_OK);
108 	case AP_COND_FAILING:
109 		return (PCIEHPC_PROP_VALUE_FAILING);
110 	case AP_COND_FAILED:
111 		return (PCIEHPC_PROP_VALUE_FAILED);
112 	case AP_COND_UNUSABLE:
113 		return (PCIEHPC_PROP_VALUE_UNUSABLE);
114 	default:
115 		return (PCIEHPC_PROP_VALUE_UNKNOWN);
116 	}
117 }
118 
119 /*
120  * routine to copy in a nvlist from userland
121  */
122 int
pcie_copyin_nvlist(char * packed_buf,size_t packed_sz,nvlist_t ** nvlp)123 pcie_copyin_nvlist(char *packed_buf, size_t packed_sz, nvlist_t **nvlp)
124 {
125 	int		ret = DDI_SUCCESS;
126 	char		*packed;
127 	nvlist_t	*dest = NULL;
128 
129 	if (packed_buf == NULL || packed_sz == 0)
130 		return (DDI_EINVAL);
131 
132 	/* copyin packed nvlist */
133 	if ((packed = kmem_alloc(packed_sz, KM_SLEEP)) == NULL)
134 		return (DDI_ENOMEM);
135 
136 	if (copyin(packed_buf, packed, packed_sz) != 0) {
137 		cmn_err(CE_WARN, "pcie_copyin_nvlist: copyin failed.\n");
138 		ret = DDI_FAILURE;
139 		goto copyin_cleanup;
140 	}
141 
142 	/* unpack packed nvlist */
143 	if ((ret = nvlist_unpack(packed, packed_sz, &dest, KM_SLEEP)) != 0) {
144 		cmn_err(CE_WARN, "pcie_copyin_nvlist: nvlist_unpack "
145 		    "failed with err %d\n", ret);
146 		switch (ret) {
147 		case EINVAL:
148 		case ENOTSUP:
149 			ret = DDI_EINVAL;
150 			goto copyin_cleanup;
151 		case ENOMEM:
152 			ret = DDI_ENOMEM;
153 			goto copyin_cleanup;
154 		default:
155 			ret = DDI_FAILURE;
156 			goto copyin_cleanup;
157 		}
158 	}
159 	*nvlp = dest;
160 copyin_cleanup:
161 	kmem_free(packed, packed_sz);
162 	return (ret);
163 }
164 
165 /*
166  * routine to copy out a nvlist to userland
167  */
168 int
pcie_copyout_nvlist(nvlist_t * nvl,char * packed_buf,size_t * buf_sz)169 pcie_copyout_nvlist(nvlist_t *nvl, char *packed_buf, size_t *buf_sz)
170 {
171 	int	err = 0;
172 	char	*buf = NULL;
173 	size_t	packed_sz;
174 
175 	if (nvl == NULL || packed_buf == NULL || buf_sz == NULL)
176 		return (DDI_EINVAL);
177 
178 	/* pack nvlist, the library will allocate memory */
179 	if ((err = nvlist_pack(nvl, &buf, &packed_sz, NV_ENCODE_NATIVE, 0))
180 	    != 0) {
181 		cmn_err(CE_WARN, "pcie_copyout_nvlist: nvlist_pack "
182 		    "failed with err %d\n", err);
183 		switch (err) {
184 		case EINVAL:
185 		case ENOTSUP:
186 			return (DDI_EINVAL);
187 		case ENOMEM:
188 			return (DDI_ENOMEM);
189 		default:
190 			return (DDI_FAILURE);
191 		}
192 	}
193 	if (packed_sz > *buf_sz) {
194 		return (DDI_EINVAL);
195 	}
196 
197 	/* copyout packed nvlist */
198 	if (copyout(buf, packed_buf, packed_sz) != 0) {
199 		cmn_err(CE_WARN, "pcie_copyout_nvlist: copyout " "failed.\n");
200 		kmem_free(buf, packed_sz);
201 		return (DDI_FAILURE);
202 	}
203 
204 	*buf_sz = packed_sz;
205 	kmem_free(buf, packed_sz);
206 	return (DDI_SUCCESS);
207 }
208 
209 /*
210  * init bus_hp_op entry and init hotpluggable slots & virtual ports
211  */
212 int
pcie_hp_init(dev_info_t * dip,caddr_t arg)213 pcie_hp_init(dev_info_t *dip, caddr_t arg)
214 {
215 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
216 	int		ret = DDI_SUCCESS, count;
217 	dev_info_t	*cdip;
218 
219 	if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) {
220 		/* Init hotplug controller */
221 		ret = pciehpc_init(dip, arg);
222 	} else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) {
223 		ret = pcishpc_init(dip);
224 	}
225 
226 	if (ret != DDI_SUCCESS) {
227 		PCIE_DBG("pcie_hp_init: initialize hotplug "
228 		    "controller failed with %d\n", ret);
229 		return (ret);
230 	}
231 
232 	ndi_devi_enter(dip, &count);
233 
234 	/* Create port for the first level children */
235 	cdip = ddi_get_child(dip);
236 	while (cdip != NULL) {
237 		if ((ret = pcie_hp_register_port(cdip, dip, NULL))
238 		    != DDI_SUCCESS) {
239 			/* stop and cleanup */
240 			break;
241 		}
242 		cdip = ddi_get_next_sibling(cdip);
243 	}
244 	ndi_devi_exit(dip, count);
245 	if (ret != DDI_SUCCESS) {
246 		cmn_err(CE_WARN, "pcie_hp_init: initialize virtual "
247 		    "hotplug port failed with %d\n", ret);
248 		(void) pcie_hp_uninit(dip);
249 
250 		return (ret);
251 	}
252 
253 	return (DDI_SUCCESS);
254 }
255 
256 /*
257  * uninit the hotpluggable slots and virtual ports
258  */
259 int
pcie_hp_uninit(dev_info_t * dip)260 pcie_hp_uninit(dev_info_t *dip)
261 {
262 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
263 	pcie_hp_unreg_port_t arg;
264 
265 	/*
266 	 * Must set arg.rv to NDI_SUCCESS so that if there's no port
267 	 * under this dip, we still return success thus the bridge
268 	 * driver can be successfully detached.
269 	 *
270 	 * Note that during the probe PCI configurator calls
271 	 * ndi_devi_offline() to detach driver for a new probed bridge,
272 	 * so that it can reprogram the resources for the bridge,
273 	 * ndi_devi_offline() calls into pcieb_detach() which in turn
274 	 * calls into this function. In this case there are no ports
275 	 * created under a new probe bridge dip, as ports are only
276 	 * created after the configurator finishing probing, thus the
277 	 * ndi_hp_walk_cn() will see no ports when this is called
278 	 * from the PCI configurtor.
279 	 */
280 	arg.nexus_dip = dip;
281 	arg.connector_num = DDI_HP_CN_NUM_NONE;
282 	arg.rv = NDI_SUCCESS;
283 
284 	/* tear down all virtual hotplug handles */
285 	ndi_hp_walk_cn(dip, pcie_hp_unregister_ports_cb, &arg);
286 
287 	if (arg.rv != NDI_SUCCESS)
288 		return (DDI_FAILURE);
289 
290 	if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p))
291 		(void) pciehpc_uninit(dip);
292 	else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p))
293 		(void) pcishpc_uninit(dip);
294 
295 	return (DDI_SUCCESS);
296 }
297 
298 /*
299  * interrupt handler
300  */
301 int
pcie_hp_intr(dev_info_t * dip)302 pcie_hp_intr(dev_info_t *dip)
303 {
304 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
305 	int		ret = DDI_INTR_UNCLAIMED;
306 
307 	if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p))
308 		ret = pciehpc_intr(dip);
309 	else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p))
310 		ret = pcishpc_intr(dip);
311 
312 	return (ret);
313 }
314 
315 /*
316  * Probe the given PCIe/PCI Hotplug Connection (CN).
317  */
318 /*ARGSUSED*/
319 int
pcie_hp_probe(pcie_hp_slot_t * slot_p)320 pcie_hp_probe(pcie_hp_slot_t *slot_p)
321 {
322 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
323 	dev_info_t	*dip = ctrl_p->hc_dip;
324 
325 	/*
326 	 * Call the configurator to probe a given PCI hotplug
327 	 * Hotplug Connection (CN).
328 	 */
329 	if (pcicfg_configure(dip, slot_p->hs_device_num, PCICFG_ALL_FUNC, 0)
330 	    != PCICFG_SUCCESS) {
331 		PCIE_DBG("pcie_hp_probe() failed\n");
332 		return (DDI_FAILURE);
333 	}
334 	slot_p->hs_condition = AP_COND_OK;
335 	pcie_hp_create_occupant_props(dip, makedevice(ddi_driver_major(dip),
336 	    slot_p->hs_minor), slot_p->hs_device_num);
337 
338 	/*
339 	 * Create ports for the newly probed devices.
340 	 * Note, this is only for the first level children because the
341 	 * descendants' ports will be created during bridge driver attach.
342 	 */
343 	return (pcie_hp_register_ports_for_dev(dip, slot_p->hs_device_num));
344 }
345 
346 /*
347  * Unprobe the given PCIe/PCI Hotplug Connection (CN):
348  *	1. remove all child device nodes
349  *	2. unregister all dependent ports
350  */
351 /*ARGSUSED*/
352 int
pcie_hp_unprobe(pcie_hp_slot_t * slot_p)353 pcie_hp_unprobe(pcie_hp_slot_t *slot_p)
354 {
355 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
356 	dev_info_t	*dip = ctrl_p->hc_dip;
357 	pcie_hp_unreg_port_t arg;
358 
359 	/*
360 	 * Call the configurator to unprobe a given PCI hotplug
361 	 * Hotplug Connection (CN).
362 	 */
363 	if (pcicfg_unconfigure(dip, slot_p->hs_device_num, PCICFG_ALL_FUNC, 0)
364 	    != PCICFG_SUCCESS) {
365 		PCIE_DBG("pcie_hp_unprobe() failed\n");
366 		return (DDI_FAILURE);
367 	}
368 	slot_p->hs_condition = AP_COND_UNKNOWN;
369 	pcie_hp_delete_occupant_props(dip, makedevice(ddi_driver_major(dip),
370 	    slot_p->hs_minor));
371 
372 	/*
373 	 * Remove ports for the unprobed devices.
374 	 * Note, this is only for the first level children because the
375 	 * descendants' ports were already removed during bridge driver dettach.
376 	 */
377 	arg.nexus_dip = dip;
378 	arg.connector_num = slot_p->hs_info.cn_num;
379 	arg.rv = NDI_SUCCESS;
380 	ndi_hp_walk_cn(dip, pcie_hp_unregister_ports_cb, &arg);
381 
382 	return (arg.rv == NDI_SUCCESS) ? (DDI_SUCCESS) : (DDI_FAILURE);
383 }
384 
385 /* Read-only probe: no hardware register programming. */
386 int
pcie_read_only_probe(dev_info_t * dip,char * cn_name,dev_info_t ** pcdip)387 pcie_read_only_probe(dev_info_t *dip, char *cn_name, dev_info_t **pcdip)
388 {
389 	long dev, func;
390 	int ret;
391 	char *sp;
392 	dev_info_t *cdip;
393 
394 	*pcdip = NULL;
395 	/*
396 	 * Parse the string of a pci Port name and get the device number
397 	 * and function number.
398 	 */
399 	if (ddi_strtol(cn_name + 4, &sp, 10, &dev) != 0)
400 		return (DDI_EINVAL);
401 	if (ddi_strtol(sp + 1, NULL, 10, &func) != 0)
402 		return (DDI_EINVAL);
403 
404 	ret = pcicfg_configure(dip, (int)dev, (int)func,
405 	    PCICFG_FLAG_READ_ONLY);
406 	if (ret == PCICFG_SUCCESS) {
407 		cdip = pcie_hp_devi_find(dip, (int)dev, (int)func);
408 		*pcdip = cdip;
409 	}
410 	return (ret);
411 }
412 
413 /* Read-only unprobe: no hardware register programming. */
414 int
pcie_read_only_unprobe(dev_info_t * dip,char * cn_name)415 pcie_read_only_unprobe(dev_info_t *dip, char *cn_name)
416 {
417 	long dev, func;
418 	int ret;
419 	char *sp;
420 
421 	/*
422 	 * Parse the string of a pci Port name and get the device number
423 	 * and function number.
424 	 */
425 	if (ddi_strtol(cn_name + 4, &sp, 10, &dev) != 0)
426 		return (DDI_EINVAL);
427 	if (ddi_strtol(sp + 1, NULL, 10, &func) != 0)
428 		return (DDI_EINVAL);
429 
430 	ret = pcicfg_unconfigure(dip, (int)dev, (int)func,
431 	    PCICFG_FLAG_READ_ONLY);
432 
433 	return (ret);
434 }
435 
436 /* Control structure used to find a device in the devinfo tree */
437 struct pcie_hp_find_ctrl {
438 	uint_t		device;
439 	uint_t		function;
440 	dev_info_t	*dip;
441 };
442 
443 /*
444  * find a devinfo node with specified device and function number
445  * in the device tree under 'dip'
446  */
447 dev_info_t *
pcie_hp_devi_find(dev_info_t * dip,uint_t device,uint_t function)448 pcie_hp_devi_find(dev_info_t *dip, uint_t device, uint_t function)
449 {
450 	struct pcie_hp_find_ctrl	ctrl;
451 	int				count;
452 
453 	ctrl.device = device;
454 	ctrl.function = function;
455 	ctrl.dip = NULL;
456 
457 	ndi_devi_enter(dip, &count);
458 	ddi_walk_devs(ddi_get_child(dip), pcie_hp_match_dev_func,
459 	    (void *)&ctrl);
460 	ndi_devi_exit(dip, count);
461 
462 	return (ctrl.dip);
463 }
464 
465 /*
466  * routine to create 'pci-occupant' property for a hotplug slot
467  */
468 void
pcie_hp_create_occupant_props(dev_info_t * dip,dev_t dev,int pci_dev)469 pcie_hp_create_occupant_props(dev_info_t *dip, dev_t dev, int pci_dev)
470 {
471 	pcie_bus_t		*bus_p = PCIE_DIP2BUS(dip);
472 	pcie_hp_ctrl_t		*ctrl_p = (pcie_hp_ctrl_t *)bus_p->bus_hp_ctrl;
473 	pcie_hp_slot_t		*slotp = NULL;
474 	pcie_hp_cn_cfg_t	cn_cfg;
475 	pcie_hp_occupant_info_t	*occupant;
476 	int			circular, i;
477 
478 	ndi_devi_enter(dip, &circular);
479 
480 	if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p)) {
481 		slotp = (ctrl_p && (pci_dev == 0)) ?
482 		    ctrl_p->hc_slots[pci_dev] : NULL;
483 	} else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p)) {
484 		if (ctrl_p) {
485 			int	slot_num;
486 
487 			slot_num = (ctrl_p->hc_device_increases) ?
488 			    (pci_dev - ctrl_p->hc_device_start) :
489 			    (pci_dev + ctrl_p->hc_device_start);
490 
491 			slotp = ctrl_p->hc_slots[slot_num];
492 		} else {
493 			slotp = NULL;
494 		}
495 	}
496 
497 	if (slotp == NULL)
498 		return;
499 
500 	occupant = kmem_alloc(sizeof (pcie_hp_occupant_info_t), KM_SLEEP);
501 	occupant->i = 0;
502 
503 	cn_cfg.flag = B_FALSE;
504 	cn_cfg.rv = NDI_SUCCESS;
505 	cn_cfg.dip = NULL;
506 	cn_cfg.slotp = (void *)slotp;
507 	cn_cfg.cn_private = (void *)occupant;
508 
509 	ddi_walk_devs(ddi_get_child(dip), pcie_hp_list_occupants,
510 	    (void *)&cn_cfg);
511 
512 	if (occupant->i == 0) {
513 		/* no occupants right now, need to create stub property */
514 		char *c[] = { "" };
515 		(void) ddi_prop_update_string_array(dev, dip, "pci-occupant",
516 		    c, 1);
517 	} else {
518 		(void) ddi_prop_update_string_array(dev, dip, "pci-occupant",
519 		    occupant->id, occupant->i);
520 	}
521 
522 	for (i = 0; i < occupant->i; i++)
523 		kmem_free(occupant->id[i], sizeof (char[MAXPATHLEN]));
524 
525 	kmem_free(occupant, sizeof (pcie_hp_occupant_info_t));
526 
527 	ndi_devi_exit(dip, circular);
528 }
529 
530 /*
531  * routine to remove 'pci-occupant' property for a hotplug slot
532  */
533 void
pcie_hp_delete_occupant_props(dev_info_t * dip,dev_t dev)534 pcie_hp_delete_occupant_props(dev_info_t *dip, dev_t dev)
535 {
536 	(void) ddi_prop_remove(dev, dip, "pci-occupant");
537 }
538 
539 /*
540  * general code to create a minor node, called from hotplug controller
541  * drivers.
542  */
543 int
pcie_create_minor_node(pcie_hp_ctrl_t * ctrl_p,int slot)544 pcie_create_minor_node(pcie_hp_ctrl_t *ctrl_p, int slot)
545 {
546 	dev_info_t		*dip = ctrl_p->hc_dip;
547 	pcie_hp_slot_t		*slot_p = ctrl_p->hc_slots[slot];
548 	ddi_hp_cn_info_t	*info_p = &slot_p->hs_info;
549 
550 	if (ddi_create_minor_node(dip, info_p->cn_name,
551 	    S_IFCHR, slot_p->hs_minor,
552 	    DDI_NT_PCI_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
553 		return (DDI_FAILURE);
554 	}
555 
556 	(void) ddi_prop_update_int(DDI_DEV_T_NONE,
557 	    dip, "ap-names", 1 << slot_p->hs_device_num);
558 
559 	return (DDI_SUCCESS);
560 }
561 
562 /*
563  * general code to remove a minor node, called from hotplug controller
564  * drivers.
565  */
566 void
pcie_remove_minor_node(pcie_hp_ctrl_t * ctrl_p,int slot)567 pcie_remove_minor_node(pcie_hp_ctrl_t *ctrl_p, int slot)
568 {
569 	ddi_remove_minor_node(ctrl_p->hc_dip,
570 	    ctrl_p->hc_slots[slot]->hs_info.cn_name);
571 }
572 
573 /*
574  * Local functions (called within this file)
575  */
576 
577 /*
578  * Register ports for all the children with device number device_num
579  */
580 static int
pcie_hp_register_ports_for_dev(dev_info_t * dip,int device_num)581 pcie_hp_register_ports_for_dev(dev_info_t *dip, int device_num)
582 {
583 	dev_info_t	*cdip;
584 	int		rv;
585 
586 	for (cdip = ddi_get_child(dip); cdip;
587 	    cdip = ddi_get_next_sibling(cdip)) {
588 		if (pcie_hp_match_dev(cdip, device_num)) {
589 			/*
590 			 * Found the newly probed device under the
591 			 * current slot. Register a port for it.
592 			 */
593 			if ((rv = pcie_hp_register_port(cdip, dip, NULL))
594 			    != DDI_SUCCESS)
595 				return (rv);
596 		} else {
597 			continue;
598 		}
599 	}
600 
601 	return (DDI_SUCCESS);
602 }
603 
604 /*
605  * Unregister ports of a pci bridge dip, get called from ndi_hp_walk_cn()
606  *
607  * If connector_num is specified, then unregister the slot's dependent ports
608  * only; Otherwise, unregister all ports of a pci bridge dip.
609  */
610 static int
pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t * info,void * arg)611 pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t *info, void *arg)
612 {
613 	pcie_hp_unreg_port_t *unreg_arg = (pcie_hp_unreg_port_t *)arg;
614 	dev_info_t *dip = unreg_arg->nexus_dip;
615 	int rv = NDI_SUCCESS;
616 
617 	if (info->cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT) {
618 		unreg_arg->rv = rv;
619 		return (DDI_WALK_CONTINUE);
620 	}
621 
622 	if (unreg_arg->connector_num != DDI_HP_CN_NUM_NONE) {
623 		/* Unregister ports for all unprobed devices under a slot. */
624 		if (unreg_arg->connector_num == info->cn_num_dpd_on) {
625 
626 			rv = ndi_hp_unregister(dip, info->cn_name);
627 		}
628 	} else {
629 
630 		/* Unregister all ports of a pci bridge dip. */
631 		rv = ndi_hp_unregister(dip, info->cn_name);
632 	}
633 
634 	unreg_arg->rv = rv;
635 	if (rv == NDI_SUCCESS)
636 		return (DDI_WALK_CONTINUE);
637 	else
638 		return (DDI_WALK_TERMINATE);
639 }
640 
641 /*
642  * Find a port according to cn_name and get the port's state.
643  */
644 static int
pcie_hp_get_port_state(ddi_hp_cn_info_t * info,void * arg)645 pcie_hp_get_port_state(ddi_hp_cn_info_t *info, void *arg)
646 {
647 	pcie_hp_port_state_t *port = (pcie_hp_port_state_t *)arg;
648 
649 	if (info->cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT)
650 		return (DDI_WALK_CONTINUE);
651 
652 	if (strcmp(info->cn_name, port->cn_name) == 0) {
653 		/* Matched. */
654 		port->cn_state = info->cn_state;
655 		port->rv = DDI_SUCCESS;
656 
657 		return (DDI_WALK_TERMINATE);
658 	}
659 
660 	return (DDI_WALK_CONTINUE);
661 }
662 
663 /*
664  * Find the physical slot with the given device number;
665  * return the slot if found.
666  */
667 static pcie_hp_slot_t *
pcie_find_physical_slot(dev_info_t * dip,int dev_num)668 pcie_find_physical_slot(dev_info_t *dip, int dev_num)
669 {
670 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
671 	pcie_hp_ctrl_t	*ctrl = PCIE_GET_HP_CTRL(dip);
672 
673 	if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) {
674 		/* PCIe has only one slot */
675 		return (dev_num == 0) ? (ctrl->hc_slots[0]) : (NULL);
676 	} else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) {
677 		for (int slot = 0; slot < ctrl->hc_num_slots_impl; slot++) {
678 			if (ctrl->hc_slots[slot]->hs_device_num == dev_num) {
679 				/* found */
680 				return (ctrl->hc_slots[slot]);
681 			}
682 		}
683 	}
684 
685 	return (NULL);
686 }
687 
688 /*
689  * setup slot name/slot-number info for the port which is being registered.
690  */
691 static int
pcie_hp_create_port_name_num(dev_info_t * dip,ddi_hp_cn_info_t * cn_info)692 pcie_hp_create_port_name_num(dev_info_t *dip, ddi_hp_cn_info_t *cn_info)
693 {
694 	int		ret, dev_num, func_num, name_len;
695 	dev_info_t	*pdip = ddi_get_parent(dip);
696 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(pdip);
697 	pcie_hp_slot_t	*slot;
698 	pcie_req_id_t	bdf;
699 	char		tmp[PCIE_HP_DEV_FUNC_NUM_STRING_LEN];
700 
701 	ret = pcie_get_bdf_from_dip(dip, &bdf);
702 	if (ret != DDI_SUCCESS) {
703 		return (ret);
704 	}
705 	if (PCIE_IS_RP(bus_p) || PCIE_IS_SWD(bus_p) ||
706 	    PCIE_IS_PCI2PCIE(bus_p)) {
707 		/*
708 		 * It is under a PCIe device, devcie number is always 0;
709 		 * function number might > 8 in ARI supported case.
710 		 */
711 		dev_num = 0;
712 		func_num = (bdf & ((~PCI_REG_BUS_M) >> 8));
713 	} else {
714 		dev_num = (bdf & (PCI_REG_DEV_M >> 8)) >> 3;
715 		func_num = bdf & (PCI_REG_FUNC_M >> 8);
716 	}
717 	/*
718 	 * The string length of dev_num and func_num must be no longer than 4
719 	 * including the string end mark. (With ARI case considered, e.g.,
720 	 * dev_num=0x0, func_num=0xff.)
721 	 */
722 	(void) snprintf(tmp, PCIE_HP_DEV_FUNC_NUM_STRING_LEN, "%x%x",
723 	    dev_num, func_num);
724 	/*
725 	 * Calculate the length of cn_name.
726 	 * The format of pci port name is: pci.d,f
727 	 * d stands for dev_num, f stands for func_num. So the length of the
728 	 * name string can be calculated as following.
729 	 */
730 	name_len = strlen(tmp) + PCIE_HP_PORT_NAME_STRING_LEN + 1;
731 
732 	cn_info->cn_name = (char *)kmem_zalloc(name_len, KM_SLEEP);
733 	(void) snprintf(cn_info->cn_name, name_len, "pci.%x,%x",
734 	    dev_num, func_num);
735 	cn_info->cn_num = (dev_num << 8) | func_num;
736 	slot = pcie_find_physical_slot(pdip, dev_num);
737 
738 	cn_info->cn_num_dpd_on = slot ?
739 	    slot->hs_info.cn_num : DDI_HP_CN_NUM_NONE;
740 
741 	return (DDI_SUCCESS);
742 }
743 
744 /*
745  * Extract device and function number from port name, whose format is
746  * something like 'pci.1,0'
747  */
748 static int
pcie_hp_get_df_from_port_name(char * cn_name,int * dev_num,int * func_num)749 pcie_hp_get_df_from_port_name(char *cn_name, int *dev_num, int *func_num)
750 {
751 	int name_len, ret;
752 	long d, f;
753 	char *sp;
754 
755 	/* some checks for the input name */
756 	name_len = strlen(cn_name);
757 	if ((name_len <= PCIE_HP_PORT_NAME_STRING_LEN) ||
758 	    (name_len > (PCIE_HP_PORT_NAME_STRING_LEN +
759 	    PCIE_HP_DEV_FUNC_NUM_STRING_LEN - 1)) ||
760 	    (strncmp("pci.", cn_name, 4) != 0)) {
761 		return (DDI_EINVAL);
762 	}
763 	ret = ddi_strtol(cn_name + 4, &sp, 10, &d);
764 	if (ret != DDI_SUCCESS)
765 		return (ret);
766 
767 	if (strncmp(",", sp, 1) != 0)
768 		return (DDI_EINVAL);
769 
770 	ret = ddi_strtol(sp + 1, NULL, 10, &f);
771 	if (ret != DDI_SUCCESS)
772 		return (ret);
773 	*dev_num = (int)d;
774 	*func_num = (int)f;
775 
776 	return (ret);
777 }
778 
779 /*
780  * Check/copy cn_name and set connection numbers.
781  * If it is a valid name, then setup cn_info for the newly created port.
782  */
783 static int
pcie_hp_setup_port_name_num(dev_info_t * pdip,char * cn_name,ddi_hp_cn_info_t * cn_info)784 pcie_hp_setup_port_name_num(dev_info_t *pdip, char *cn_name,
785     ddi_hp_cn_info_t *cn_info)
786 {
787 	int dev_num, func_num, ret;
788 	pcie_hp_slot_t *slot;
789 
790 	if ((ret = pcie_hp_get_df_from_port_name(cn_name, &dev_num, &func_num))
791 	    != DDI_SUCCESS)
792 		return (ret);
793 
794 	if (pcie_hp_check_hardware_existence(pdip, dev_num, func_num) ==
795 	    DDI_SUCCESS) {
796 		cn_info->cn_state = DDI_HP_CN_STATE_PRESENT;
797 	} else {
798 		cn_info->cn_state = DDI_HP_CN_STATE_EMPTY;
799 	}
800 
801 	cn_info->cn_name = ddi_strdup(cn_name, KM_SLEEP);
802 	cn_info->cn_num = (dev_num << 8) | func_num;
803 
804 	slot = pcie_find_physical_slot(pdip, dev_num);
805 	if (slot) {
806 		cn_info->cn_num_dpd_on = slot->hs_info.cn_num;
807 	} else {
808 		cn_info->cn_num_dpd_on = DDI_HP_CN_NUM_NONE;
809 	}
810 	return (DDI_SUCCESS);
811 }
812 
813 static int
ndi2ddi(int n)814 ndi2ddi(int n)
815 {
816 	int ret;
817 
818 	switch (n) {
819 	case NDI_SUCCESS:
820 		ret = DDI_SUCCESS;
821 		break;
822 	case NDI_NOMEM:
823 		ret = DDI_ENOMEM;
824 		break;
825 	case NDI_BUSY:
826 		ret = DDI_EBUSY;
827 		break;
828 	case NDI_EINVAL:
829 		ret = DDI_EINVAL;
830 		break;
831 	case NDI_ENOTSUP:
832 		ret = DDI_ENOTSUP;
833 		break;
834 	case NDI_FAILURE:
835 	default:
836 		ret = DDI_FAILURE;
837 		break;
838 	}
839 	return (ret);
840 }
841 
842 /*
843  * Common routine to create and register a new port
844  *
845  * Create an empty port if dip is NULL, and cn_name needs to be specified in
846  * this case. Otherwise, create a port mapping to the specified dip, and cn_name
847  * is not needed in this case.
848  */
849 static int
pcie_hp_register_port(dev_info_t * dip,dev_info_t * pdip,char * cn_name)850 pcie_hp_register_port(dev_info_t *dip, dev_info_t *pdip, char *cn_name)
851 {
852 	ddi_hp_cn_info_t	*cn_info;
853 	int			ret;
854 
855 	ASSERT((dip == NULL) != (cn_name == NULL));
856 	cn_info = kmem_zalloc(sizeof (ddi_hp_cn_info_t), KM_SLEEP);
857 	if (dip != NULL)
858 		ret = pcie_hp_create_port_name_num(dip, cn_info);
859 	else
860 		ret = pcie_hp_setup_port_name_num(pdip, cn_name, cn_info);
861 
862 	if (ret != DDI_SUCCESS) {
863 		kmem_free(cn_info, sizeof (ddi_hp_cn_info_t));
864 		return (ret);
865 	}
866 
867 	cn_info->cn_child = dip;
868 	cn_info->cn_type = DDI_HP_CN_TYPE_VIRTUAL_PORT;
869 	cn_info->cn_type_str = DDI_HP_CN_TYPE_STR_PORT;
870 
871 	ret = ndi_hp_register(pdip, cn_info);
872 
873 	kmem_free(cn_info->cn_name, strlen(cn_info->cn_name) + 1);
874 	kmem_free(cn_info, sizeof (ddi_hp_cn_info_t));
875 
876 	return (ndi2ddi(ret));
877 }
878 
879 /* Check if there is a piece of hardware exist corresponding to the cn_name */
880 static int
pcie_hp_check_hardware_existence(dev_info_t * dip,int dev_num,int func_num)881 pcie_hp_check_hardware_existence(dev_info_t *dip, int dev_num, int func_num)
882 {
883 
884 	/*
885 	 * VHPTODO:
886 	 * According to device and function number, check if there is a hardware
887 	 * device exists. Currently, this function can not be reached before
888 	 * we enable state transition to or from "Port-Empty" or "Port-Present"
889 	 * states. When the pci device type project is integrated, we are going
890 	 * to call the pci config space access interfaces introduced by it.
891 	 */
892 	_NOTE(ARGUNUSED(dip, dev_num, func_num));
893 
894 	return (DDI_SUCCESS);
895 }
896 
897 /*
898  * Dispatch hotplug commands to different hotplug controller drivers, including
899  * physical and virtual hotplug operations.
900  */
901 /* ARGSUSED */
902 int
pcie_hp_common_ops(dev_info_t * dip,char * cn_name,ddi_hp_op_t op,void * arg,void * result)903 pcie_hp_common_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op,
904     void *arg, void *result)
905 {
906 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
907 	int		ret = DDI_SUCCESS;
908 
909 	PCIE_DBG("pcie_hp_common_ops: dip=%p cn_name=%s op=%x arg=%p\n",
910 	    dip, cn_name, op, arg);
911 
912 	switch (op) {
913 	case DDI_HPOP_CN_CREATE_PORT:
914 	{
915 		/* create an empty port */
916 		return (pcie_hp_register_port(NULL, dip, cn_name));
917 	}
918 	case DDI_HPOP_CN_CHANGE_STATE:
919 	{
920 		ddi_hp_cn_state_t curr_state;
921 		ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
922 		pcie_hp_port_state_t state_arg;
923 
924 		if (target_state < DDI_HP_CN_STATE_PORT_EMPTY) {
925 			/* this is for physical slot state change */
926 			break;
927 		}
928 		PCIE_DBG("pcie_hp_common_ops: change port state"
929 		    " dip=%p cn_name=%s"
930 		    " op=%x arg=%p\n", (void *)dip, cn_name, op, arg);
931 
932 		state_arg.rv = DDI_FAILURE;
933 		state_arg.cn_name = cn_name;
934 		ndi_hp_walk_cn(dip, pcie_hp_get_port_state, &state_arg);
935 		if (state_arg.rv != DDI_SUCCESS) {
936 			/* can not find the port */
937 			return (DDI_EINVAL);
938 		}
939 		curr_state = state_arg.cn_state;
940 		/*
941 		 * Check if this is for changing port's state: change to/from
942 		 * PORT_EMPTY/PRESENT states.
943 		 */
944 		if (curr_state < target_state) {
945 			/* Upgrade state */
946 			switch (curr_state) {
947 			case DDI_HP_CN_STATE_PORT_EMPTY:
948 				if (target_state ==
949 				    DDI_HP_CN_STATE_PORT_PRESENT) {
950 					int dev_num, func_num;
951 
952 					ret = pcie_hp_get_df_from_port_name(
953 					    cn_name, &dev_num, &func_num);
954 					if (ret != DDI_SUCCESS)
955 						goto port_state_done;
956 
957 					ret = pcie_hp_check_hardware_existence(
958 					    dip, dev_num, func_num);
959 				} else if (target_state ==
960 				    DDI_HP_CN_STATE_OFFLINE) {
961 					ret = pcie_read_only_probe(dip,
962 					    cn_name, (dev_info_t **)result);
963 				} else
964 					ret = DDI_EINVAL;
965 
966 				goto port_state_done;
967 			case DDI_HP_CN_STATE_PORT_PRESENT:
968 				if (target_state ==
969 				    DDI_HP_CN_STATE_OFFLINE)
970 					ret = pcie_read_only_probe(dip,
971 					    cn_name, (dev_info_t **)result);
972 				else
973 					ret = DDI_EINVAL;
974 
975 				goto port_state_done;
976 			default:
977 				ASSERT("unexpected state");
978 			}
979 		} else {
980 			/* Downgrade state */
981 			switch (curr_state) {
982 			case DDI_HP_CN_STATE_PORT_PRESENT:
983 			{
984 				int dev_num, func_num;
985 
986 				ret = pcie_hp_get_df_from_port_name(cn_name,
987 				    &dev_num, &func_num);
988 				if (ret != DDI_SUCCESS)
989 					goto port_state_done;
990 
991 				ret = pcie_hp_check_hardware_existence(dip,
992 				    dev_num, func_num);
993 
994 				goto port_state_done;
995 			}
996 			case DDI_HP_CN_STATE_OFFLINE:
997 				ret = pcie_read_only_unprobe(dip, cn_name);
998 
999 				goto port_state_done;
1000 			default:
1001 				ASSERT("unexpected state");
1002 			}
1003 		}
1004 port_state_done:
1005 		*(ddi_hp_cn_state_t *)result = curr_state;
1006 		return (ret);
1007 	}
1008 	default:
1009 		break;
1010 	}
1011 
1012 	if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) {
1013 		/* PCIe hotplug */
1014 		ret = pciehpc_hp_ops(dip, cn_name, op, arg, result);
1015 	} else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) {
1016 		/* PCI SHPC hotplug */
1017 		ret = pcishpc_hp_ops(dip, cn_name, op, arg, result);
1018 	} else {
1019 		cmn_err(CE_WARN, "pcie_hp_common_ops: op is not supported."
1020 		    " dip=%p cn_name=%s"
1021 		    " op=%x arg=%p\n", (void *)dip, cn_name, op, arg);
1022 		ret = DDI_ENOTSUP;
1023 	}
1024 
1025 #if defined(__x86)
1026 	/*
1027 	 * like in attach, since hotplugging can change error registers,
1028 	 * we need to ensure that the proper bits are set on this port
1029 	 * after a configure operation
1030 	 */
1031 	if ((ret == DDI_SUCCESS) && (op == DDI_HPOP_CN_CHANGE_STATE) &&
1032 	    (*(ddi_hp_cn_state_t *)arg == DDI_HP_CN_STATE_ENABLED))
1033 		pcieb_intel_error_workaround(dip);
1034 #endif
1035 
1036 	return (ret);
1037 }
1038 
1039 /*
1040  * pcie_hp_match_dev_func:
1041  * Match dip's PCI device number and function number with input ones.
1042  */
1043 static int
pcie_hp_match_dev_func(dev_info_t * dip,void * hdl)1044 pcie_hp_match_dev_func(dev_info_t *dip, void *hdl)
1045 {
1046 	struct pcie_hp_find_ctrl	*ctrl = (struct pcie_hp_find_ctrl *)hdl;
1047 	pci_regspec_t			*pci_rp;
1048 	int				length;
1049 	int				pci_dev, pci_func;
1050 
1051 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1052 	    "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) {
1053 		ctrl->dip = NULL;
1054 		return (DDI_WALK_TERMINATE);
1055 	}
1056 
1057 	/* get the PCI device address info */
1058 	pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
1059 	pci_func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
1060 
1061 	/*
1062 	 * free the memory allocated by ddi_prop_lookup_int_array
1063 	 */
1064 	ddi_prop_free(pci_rp);
1065 
1066 	if ((pci_dev == ctrl->device) && (pci_func == ctrl->function)) {
1067 		/* found the match for the specified device address */
1068 		ctrl->dip = dip;
1069 		return (DDI_WALK_TERMINATE);
1070 	}
1071 
1072 	/*
1073 	 * continue the walk to the next sibling to look for a match.
1074 	 */
1075 	return (DDI_WALK_PRUNECHILD);
1076 }
1077 
1078 /*
1079  * pcie_hp_match_dev:
1080  * Match the dip's pci device number with the input dev_num
1081  */
1082 static boolean_t
pcie_hp_match_dev(dev_info_t * dip,int dev_num)1083 pcie_hp_match_dev(dev_info_t *dip, int dev_num)
1084 {
1085 	pci_regspec_t			*pci_rp;
1086 	int				length;
1087 	int				pci_dev;
1088 
1089 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1090 	    "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) {
1091 		return (B_FALSE);
1092 	}
1093 
1094 	/* get the PCI device address info */
1095 	pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
1096 
1097 	/*
1098 	 * free the memory allocated by ddi_prop_lookup_int_array
1099 	 */
1100 	ddi_prop_free(pci_rp);
1101 
1102 	if (pci_dev == dev_num) {
1103 		/* found the match for the specified device address */
1104 		return (B_TRUE);
1105 	}
1106 
1107 	return (B_FALSE);
1108 }
1109 
1110 /*
1111  * Callback function to match with device number in order to list
1112  * occupants under a specific slot
1113  */
1114 static int
pcie_hp_list_occupants(dev_info_t * dip,void * arg)1115 pcie_hp_list_occupants(dev_info_t *dip, void *arg)
1116 {
1117 	pcie_hp_cn_cfg_t	*cn_cfg_p = (pcie_hp_cn_cfg_t *)arg;
1118 	pcie_hp_occupant_info_t	*occupant =
1119 	    (pcie_hp_occupant_info_t *)cn_cfg_p->cn_private;
1120 	pcie_hp_slot_t		*slot_p =
1121 	    (pcie_hp_slot_t *)cn_cfg_p->slotp;
1122 	int			pci_dev;
1123 	pci_regspec_t		*pci_rp;
1124 	int			length;
1125 	major_t			major;
1126 
1127 	/*
1128 	 * Get the PCI device number information from the devinfo
1129 	 * node. Since the node may not have the address field
1130 	 * setup (this is done in the DDI_INITCHILD of the parent)
1131 	 * we look up the 'reg' property to decode that information.
1132 	 */
1133 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
1134 	    DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
1135 	    (uint_t *)&length) != DDI_PROP_SUCCESS) {
1136 		cn_cfg_p->rv = DDI_FAILURE;
1137 		cn_cfg_p->dip = dip;
1138 		return (DDI_WALK_TERMINATE);
1139 	}
1140 
1141 	/* get the pci device id information */
1142 	pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
1143 
1144 	/*
1145 	 * free the memory allocated by ddi_prop_lookup_int_array
1146 	 */
1147 	ddi_prop_free(pci_rp);
1148 
1149 	/*
1150 	 * Match the node for the device number of the slot.
1151 	 */
1152 	if (pci_dev == slot_p->hs_device_num) {
1153 
1154 		major = ddi_driver_major(dip);
1155 
1156 		/*
1157 		 * If the node is not yet attached, then don't list it
1158 		 * as an occupant. This is valid, since nothing can be
1159 		 * consuming it until it is attached, and cfgadm will
1160 		 * ask for the property explicitly which will cause it
1161 		 * to be re-freshed right before checking with rcm.
1162 		 */
1163 		if ((major == DDI_MAJOR_T_NONE) || !i_ddi_devi_attached(dip))
1164 			return (DDI_WALK_PRUNECHILD);
1165 
1166 		/*
1167 		 * If we have used all our occupants then print mesage
1168 		 * and terminate walk.
1169 		 */
1170 		if (occupant->i >= PCIE_HP_MAX_OCCUPANTS) {
1171 			cmn_err(CE_WARN,
1172 			    "pcie (%s%d): unable to list all occupants",
1173 			    ddi_driver_name(ddi_get_parent(dip)),
1174 			    ddi_get_instance(ddi_get_parent(dip)));
1175 			return (DDI_WALK_TERMINATE);
1176 		}
1177 
1178 		/*
1179 		 * No need to hold the dip as ddi_walk_devs
1180 		 * has already arranged that for us.
1181 		 */
1182 		occupant->id[occupant->i] =
1183 		    kmem_alloc(sizeof (char[MAXPATHLEN]), KM_SLEEP);
1184 		(void) ddi_pathname(dip, (char *)occupant->id[occupant->i]);
1185 		occupant->i++;
1186 	}
1187 
1188 	/*
1189 	 * continue the walk to the next sibling to look for a match
1190 	 * or to find other nodes if this card is a multi-function card.
1191 	 */
1192 	return (DDI_WALK_PRUNECHILD);
1193 }
1194 
1195 /*
1196  * Generate the System Event for ESC_DR_REQ.
1197  * One of the consumers is pcidr, it calls to libcfgadm to perform a
1198  * configure or unconfigure operation to the AP.
1199  */
1200 void
pcie_hp_gen_sysevent_req(char * slot_name,int hint,dev_info_t * self,int kmflag)1201 pcie_hp_gen_sysevent_req(char *slot_name, int hint,
1202     dev_info_t *self, int kmflag)
1203 {
1204 	sysevent_id_t	eid;
1205 	nvlist_t	*ev_attr_list = NULL;
1206 	char		cn_path[MAXPATHLEN];
1207 	char		*ap_id;
1208 	int		err, ap_id_len;
1209 
1210 	/*
1211 	 * Minor device name (AP) will be bus path
1212 	 * concatenated with slot name
1213 	 */
1214 	(void) strcpy(cn_path, "/devices");
1215 	(void) ddi_pathname(self, cn_path + strlen("/devices"));
1216 
1217 	ap_id_len = strlen(cn_path) + strlen(":") +
1218 	    strlen(slot_name) + 1;
1219 	ap_id = kmem_zalloc(ap_id_len, kmflag);
1220 	if (ap_id == NULL) {
1221 		cmn_err(CE_WARN,
1222 		    "%s%d: Failed to allocate memory for AP ID: %s:%s",
1223 		    ddi_driver_name(self), ddi_get_instance(self),
1224 		    cn_path, slot_name);
1225 
1226 		return;
1227 	}
1228 
1229 	(void) strcpy(ap_id, cn_path);
1230 	(void) strcat(ap_id, ":");
1231 	(void) strcat(ap_id, slot_name);
1232 
1233 	err = nvlist_alloc(&ev_attr_list, NV_UNIQUE_NAME_TYPE, kmflag);
1234 	if (err != 0) {
1235 		cmn_err(CE_WARN,
1236 		    "%s%d: Failed to allocate memory "
1237 		    "for event attributes%s", ddi_driver_name(self),
1238 		    ddi_get_instance(self), ESC_DR_REQ);
1239 
1240 		kmem_free(ap_id, ap_id_len);
1241 		return;
1242 	}
1243 
1244 	switch (hint) {
1245 
1246 	case SE_INVESTIGATE_RES:	/* fall through */
1247 	case SE_INCOMING_RES:		/* fall through */
1248 	case SE_OUTGOING_RES:		/* fall through */
1249 
1250 		err = nvlist_add_string(ev_attr_list, DR_REQ_TYPE,
1251 		    SE_REQ2STR(hint));
1252 
1253 		if (err != 0) {
1254 			cmn_err(CE_WARN,
1255 			    "%s%d: Failed to add attr [%s] "
1256 			    "for %s event", ddi_driver_name(self),
1257 			    ddi_get_instance(self),
1258 			    DR_REQ_TYPE, ESC_DR_REQ);
1259 
1260 			goto done;
1261 		}
1262 		break;
1263 
1264 	default:
1265 		cmn_err(CE_WARN, "%s%d:  Unknown hint on sysevent",
1266 		    ddi_driver_name(self), ddi_get_instance(self));
1267 
1268 		goto done;
1269 	}
1270 
1271 	/*
1272 	 * Add attachment point as attribute (common attribute)
1273 	 */
1274 
1275 	err = nvlist_add_string(ev_attr_list, DR_AP_ID, ap_id);
1276 
1277 	if (err != 0) {
1278 		cmn_err(CE_WARN, "%s%d: Failed to add attr [%s] for %s event",
1279 		    ddi_driver_name(self), ddi_get_instance(self),
1280 		    DR_AP_ID, EC_DR);
1281 
1282 		goto done;
1283 	}
1284 
1285 
1286 	/*
1287 	 * Log this event with sysevent framework.
1288 	 */
1289 
1290 	err = ddi_log_sysevent(self, DDI_VENDOR_SUNW, EC_DR,
1291 	    ESC_DR_REQ, ev_attr_list, &eid,
1292 	    ((kmflag == KM_SLEEP) ? DDI_SLEEP : DDI_NOSLEEP));
1293 	if (err != 0) {
1294 		cmn_err(CE_WARN, "%s%d: Failed to log %s event",
1295 		    ddi_driver_name(self), ddi_get_instance(self), EC_DR);
1296 	}
1297 
1298 done:
1299 	nvlist_free(ev_attr_list);
1300 	kmem_free(ap_id, ap_id_len);
1301 }
1302