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