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