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