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