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