1 /***************************************************************************
2  *
3  * sysevent.c : Solaris sysevents
4  *
5  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
6  * Use is subject to license terms.
7  *
8  * Licensed under the Academic Free License version 2.1
9  *
10  **************************************************************************/
11 
12 #pragma ident	"%Z%%M%	%I%	%E% SMI"
13 
14 #ifdef HAVE_CONFIG_H
15 #include <config.h>
16 #endif
17 
18 #include <stdio.h>
19 #include <unistd.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <sys/dkio.h>
25 #include <sys/stat.h>
26 #include <libdevinfo.h>
27 #include <libsysevent.h>
28 #include <sys/sysevent/dev.h>
29 #include <sys/sysevent/pwrctl.h>
30 #include <sys/sysevent/dr.h>
31 #include <glib.h>
32 #include <config_admin.h>
33 #include <kstat.h>
34 
35 #include "../osspec.h"
36 #include "../logger.h"
37 #include "../hald.h"
38 #include "../hald_dbus.h"
39 #include "../device_info.h"
40 #include "../util.h"
41 #include "osspec_solaris.h"
42 #include "hotplug.h"
43 #include "devinfo.h"
44 #include "devinfo_storage.h"
45 #include "devinfo_acpi.h"
46 #include "devinfo_usb.h"
47 #include "sysevent.h"
48 #include "devinfo_misc.h"
49 #include "devinfo_cpu.h"
50 
51 #ifndef ESC_LOFI
52 #define	ESC_LOFI "lofi"
53 #endif
54 
55 static void	sysevent_dev_handler(sysevent_t *);
56 static gboolean sysevent_iochannel_data(GIOChannel *, GIOCondition, gpointer);
57 static void	sysevent_dev_add(gchar *, gchar *);
58 static void	sysevent_dev_remove(gchar *, gchar *);
59 static void	sysevent_dev_branch(gchar *);
60 static void	sysevent_lofi_add(gchar *, gchar *);
61 static void	sysevent_lofi_remove(gchar *, gchar *);
62 static void	sysevent_devfs_add(gchar *);
63 static void	sysevent_pwrctl(gchar *, gchar *, gchar *, gchar *, gchar *,
64 		    gchar *, uint_t);
65 static void	sysevent_process_dr(gchar *, gchar *);
66 
67 static sysevent_handle_t	*shp;
68 
69 static int sysevent_pipe_fds[2];
70 static GIOChannel *sysevent_iochannel;
71 static guint sysevent_source_id;
72 
73 gboolean
74 sysevent_init(void)
75 {
76 	GError *err = NULL;
77 	const char	*subcl[6];
78 
79 	/*
80 	 * pipe used to serialize sysevents through the main loop
81 	 */
82 	if (pipe (sysevent_pipe_fds) != 0) {
83 		HAL_INFO (("pipe() failed errno=%d", errno));
84 		return (FALSE);
85 	}
86 	sysevent_iochannel = g_io_channel_unix_new (sysevent_pipe_fds[0]);
87 	if (sysevent_iochannel == NULL) {
88 		HAL_INFO (("g_io_channel_unix_new failed"));
89 		return (FALSE);
90 	}
91 	g_io_channel_set_flags (sysevent_iochannel, G_IO_FLAG_NONBLOCK, &err);
92 	sysevent_source_id = g_io_add_watch (
93 	    sysevent_iochannel, G_IO_IN, sysevent_iochannel_data, NULL);
94 
95 	shp = sysevent_bind_handle(sysevent_dev_handler);
96 	if (shp == NULL) {
97 		HAL_INFO (("sysevent_bind_handle failed %d", errno));
98 		return (FALSE);
99 	}
100 
101 	subcl[0] = ESC_DISK;
102 	subcl[1] = ESC_LOFI;
103 	subcl[2] = ESC_PRINTER;
104 	if (sysevent_subscribe_event(shp, EC_DEV_ADD, subcl, 3) != 0) {
105 		HAL_INFO (("subscribe(dev_add) failed %d", errno));
106 		sysevent_unbind_handle(shp);
107 		return (FALSE);
108 	}
109 	if (sysevent_subscribe_event(shp, EC_DEV_REMOVE, subcl, 3) != 0) {
110 		HAL_INFO (("subscribe(dev_remove) failed %d", errno));
111 		sysevent_unbind_handle(shp);
112 		return (FALSE);
113 	}
114 
115 	subcl[0] = ESC_DEV_BRANCH_REMOVE;
116 	if (sysevent_subscribe_event(shp, EC_DEV_BRANCH, subcl, 1) != 0) {
117 		HAL_INFO (("subscribe(dev_branch) failed %d", errno));
118 		sysevent_unbind_handle(shp);
119 		return (FALSE);
120 	}
121 
122 	subcl[0] = ESC_PWRCTL_ADD;
123 	subcl[1] = ESC_PWRCTL_REMOVE;
124 	subcl[2] = ESC_PWRCTL_STATE_CHANGE;
125 	subcl[3] = ESC_PWRCTL_BRIGHTNESS_UP;
126 	subcl[4] = ESC_PWRCTL_BRIGHTNESS_DOWN;
127 	subcl[5] = ESC_PWRCTL_POWER_BUTTON;
128 	if (sysevent_subscribe_event(shp, EC_PWRCTL, subcl, 6) != 0) {
129 		HAL_INFO(("subscribe(dev_add) failed %d", errno));
130 		sysevent_unbind_handle(shp);
131 		return (FALSE);
132 	}
133 
134 	subcl[0] = ESC_DEVFS_DEVI_ADD;
135 	if (sysevent_subscribe_event(shp, EC_DEVFS, subcl, 1) != 0) {
136 		HAL_INFO (("subscribe(EC_DEVFS) failed %d", errno));
137 		sysevent_unbind_handle(shp);
138 		return (FALSE);
139 	}
140 
141 	subcl[0] = ESC_DR_AP_STATE_CHANGE;
142 	if (sysevent_subscribe_event(shp, EC_DR, subcl, 1) != 0) {
143 		HAL_INFO (("subscribe(dynamic reconfiguration) failed %d",
144 		    errno));
145 		sysevent_unbind_handle(shp);
146 		return (FALSE);
147 	}
148 
149 	return (B_TRUE);
150 }
151 
152 void
153 sysevent_fini(void)
154 {
155 	sysevent_unbind_handle(shp);
156 	shp = NULL;
157 }
158 
159 static void
160 sysevent_dev_handler(sysevent_t *ev)
161 {
162 	char		*class;
163 	char		*subclass;
164 	nvlist_t	*attr_list;
165 	char		*phys_path;
166 	char		*dev_name;
167 	char		*dev_hid;
168 	char		*dev_uid;
169 	uint_t		dev_index;
170 	char		s[1024];
171 	ssize_t		nwritten;
172 
173 	if ((class = sysevent_get_class_name(ev)) == NULL)
174 		return;
175 
176 	if ((subclass = sysevent_get_subclass_name(ev)) == NULL)
177 		return;
178 
179 	if (sysevent_get_attr_list(ev, &attr_list) != 0)
180 		return;
181 
182 	if (strcmp(class, EC_DEVFS) == 0) {
183 		if (nvlist_lookup_string(attr_list, DEVFS_PATHNAME, &phys_path) != 0) {
184 			goto out;
185 		}
186 
187 		snprintf(s, sizeof (s), "%s %s %s\n",
188 		    class, subclass, phys_path);
189 		nwritten = write(sysevent_pipe_fds[1], s, strlen(s) + 1);
190 
191 		HAL_INFO (("sysevent_dev_handler: wrote %d bytes", nwritten));
192 		goto out;
193 	}
194 
195 	if (strcmp(class, EC_PWRCTL) == 0) {
196 		if (nvlist_lookup_string(attr_list, PWRCTL_DEV_PHYS_PATH,
197 		    &phys_path) != 0) {
198 			goto out;
199 		}
200 	} else if (strcmp(class, EC_DR) == 0) {
201 		if (nvlist_lookup_string(attr_list, DR_AP_ID,
202 		    &phys_path) != 0) {
203 			goto out;
204 		}
205 	} else if (nvlist_lookup_string(attr_list, DEV_PHYS_PATH, &phys_path)
206 	    != 0) {
207 		goto out;
208 	}
209 
210 	/*
211 	 * In case of EC_DR, use dev_name to store DR_HINT val
212 	 */
213 	if (strcmp(class, EC_DR) == 0) {
214 		if (nvlist_lookup_string(attr_list, DR_HINT, &dev_name) != 0) {
215 			goto out;
216 		}
217 	} else if (nvlist_lookup_string(attr_list, DEV_NAME, &dev_name) != 0) {
218 		if (strcmp(class, EC_PWRCTL) == 0) {
219 			dev_name = "noname";
220 		} else {
221 			dev_name = "";
222 		}
223 	}
224 
225 	if (nvlist_lookup_string(attr_list, PWRCTL_DEV_HID, &dev_hid) != 0) {
226 		dev_hid = "";
227 	}
228 	if (nvlist_lookup_string(attr_list, PWRCTL_DEV_UID, &dev_uid) != 0) {
229 		dev_uid = "";
230 	}
231 	if (nvlist_lookup_uint32(attr_list, PWRCTL_DEV_INDEX, &dev_index)
232 	    != 0) {
233 		dev_index = 0;
234 	}
235 
236 	snprintf(s, sizeof (s), "%s %s %s %s %s %s %d\n",
237 	    class, subclass, phys_path, dev_name, dev_hid, dev_uid, dev_index);
238 	nwritten = write(sysevent_pipe_fds[1], s, strlen(s) + 1);
239 
240 	HAL_INFO (("sysevent_dev_handler: wrote %d bytes", nwritten));
241 
242 out:
243 	nvlist_free(attr_list);
244 }
245 
246 static gboolean
247 sysevent_iochannel_data (GIOChannel *source,
248 		    GIOCondition condition,
249 		    gpointer user_data)
250 {
251 	GError *err = NULL;
252 	gchar *s = NULL;
253 	gsize len;
254 	int matches;
255 	gchar class[1024];
256 	gchar subclass[1024];
257 	gchar phys_path[1024];
258 	gchar dev_name[1024];
259 	gchar dev_uid[1024];
260 	gchar dev_hid[1024];
261 	uint_t dev_index;
262 
263 	HAL_INFO (("sysevent_iochannel_data"));
264 
265 	while (g_io_channel_read_line (sysevent_iochannel, &s, &len, NULL,
266 	    &err) == G_IO_STATUS_NORMAL) {
267 		if (len == 0) {
268 			break;
269 		}
270 		HAL_INFO (("IOChannel val => %s", s));
271 		class[0] = subclass[0] = phys_path[0] = dev_name[0] =
272 		    dev_hid[0] = dev_uid[0] = '\0';
273 		matches = sscanf(s, "%s %s %s %s %s %s %d", class, subclass,
274 		    phys_path, dev_name, dev_hid, dev_uid, &dev_index);
275 		g_free (s);
276 		s = NULL;
277 		if (matches < 3) {
278 			continue;
279 		}
280 		HAL_INFO (("sysevent: class=%s, sub=%s", class, subclass));
281 
282 		if (strcmp(class, EC_DEV_ADD) == 0) {
283 			if ((strcmp(subclass, ESC_DISK) == 0) ||
284 			    (strcmp(subclass, ESC_PRINTER) == 0)) {
285 				sysevent_dev_add(phys_path, dev_name);
286 			} else if (strcmp(subclass, ESC_LOFI) == 0) {
287 				sysevent_lofi_add(phys_path, dev_name);
288 			}
289 		} else if (strcmp(class, EC_DEV_REMOVE) == 0) {
290 			if ((strcmp(subclass, ESC_DISK) == 0) ||
291 			    (strcmp(subclass, ESC_PRINTER) == 0)) {
292 				sysevent_dev_remove(phys_path, dev_name);
293 			} else if (strcmp(subclass, ESC_LOFI) == 0) {
294 				sysevent_lofi_remove(phys_path, dev_name);
295 			}
296 		} else if (strcmp(class, EC_DEV_BRANCH) == 0) {
297 			sysevent_dev_branch(phys_path);
298 		} else if (strcmp(class, EC_PWRCTL) == 0) {
299 			sysevent_pwrctl(class, subclass, phys_path,
300 			    dev_name, dev_hid, dev_uid, dev_index);
301 		} else if (strcmp(class, EC_DEVFS) == 0) {
302 			if (strcmp(subclass, ESC_DEVFS_DEVI_ADD) == 0) {
303 				sysevent_devfs_add(phys_path);
304 			}
305 		} else if (strcmp(class, EC_DR) == 0) {
306 			/*
307 			 * Note: AP_ID is stored in phys_path and HINT is
308 			 * stored in dev_name, to avoid creating seperate
309 			 * variables and multiple conditions checking
310 			 */
311 			HAL_DEBUG (("In %s, AP_ID-> %s, Hint-> %s", class,
312 			    phys_path, dev_name));
313 			if (strcmp(subclass, ESC_DR_AP_STATE_CHANGE) == 0) {
314 				sysevent_process_dr(phys_path, dev_name);
315 			}
316 		}
317 	}
318 
319 	if (err) {
320 		g_error_free (err);
321 	}
322 
323 	return (TRUE);
324 }
325 
326 static void
327 sysevent_dev_add(gchar *devfs_path, gchar *name)
328 {
329 	gchar	*parent_devfs_path, *hotplug_devfs_path;
330 	HalDevice *parent;
331 
332 	HAL_INFO (("dev_add: %s %s", name, devfs_path));
333 
334 	parent = hal_util_find_closest_ancestor (devfs_path, &parent_devfs_path, &hotplug_devfs_path);
335 	if (parent == NULL) {
336 		return;
337 	}
338 
339 	HAL_INFO (("dev_add: parent=%s", parent_devfs_path));
340 	HAL_INFO (("dev_add: real=%s", hotplug_devfs_path));
341 
342 	devinfo_add (parent, hotplug_devfs_path);
343 
344 	g_free (parent_devfs_path);
345 	g_free (hotplug_devfs_path);
346 
347 	hotplug_event_process_queue ();
348 }
349 
350 static void
351 sysevent_dev_remove(gchar *devfs_path, gchar *name)
352 {
353 	HAL_INFO (("dev_remove: %s %s", name, devfs_path));
354 
355 	devinfo_remove_branch (devfs_path, NULL);
356 	hotplug_event_process_queue ();
357 }
358 
359 static void
360 sysevent_dev_branch(gchar *devfs_path)
361 {
362 	HAL_INFO (("branch_remove: %s", devfs_path));
363 
364 	devinfo_remove_branch (devfs_path, NULL);
365 	hotplug_event_process_queue ();
366 }
367 
368 static void
369 sysevent_lofi_add(gchar *devfs_path, gchar *name)
370 {
371 	di_node_t node;
372 	const char *parent_udi;
373 	HalDevice *d, *parent;
374 
375 	HAL_INFO (("lofi_add: %s %s", name, devfs_path));
376 
377 	if ((d = hal_device_store_match_key_value_string (hald_get_gdl (),
378 	    "solaris.devfs_path", devfs_path)) == NULL) {
379 		HAL_INFO (("device not found in GDL %s", devfs_path));
380 		return;
381 	}
382 	parent_udi = hal_device_property_get_string (d, "info.parent");
383 	if ((parent_udi == NULL) || (strlen(parent_udi) == 0)) {
384 		HAL_INFO (("parent not found in GDL %s", parent_udi));
385 		return;
386 	}
387 	if ((parent = hal_device_store_match_key_value_string (hald_get_gdl (),
388 	    "info.udi", parent_udi)) == NULL) {
389 		HAL_INFO (("parent not found in GDL %s", parent_udi));
390 		return;
391 	}
392 
393 	if ((node = di_init (devfs_path, DINFOCPYALL)) == DI_NODE_NIL) {
394 		HAL_INFO (("device not found in devinfo %s", devfs_path));
395 		return;
396 	}
397 
398 	HAL_INFO (("device %s parent %s", hal_device_get_udi (d), parent_udi));
399 	devinfo_lofi_add_major (parent, node, devfs_path, NULL, TRUE, d);
400 
401 	di_fini (node);
402 
403 	hotplug_event_process_queue ();
404 }
405 
406 static void
407 sysevent_lofi_remove(gchar *parent_devfs_path, gchar *name)
408 {
409 	devinfo_lofi_remove_minor(parent_devfs_path, name);
410 	hotplug_event_process_queue ();
411 }
412 
413 static HalDevice *
414 lookup_parent(char *devfs_path)
415 {
416 	gchar		*path = NULL;
417 	HalDevice	*parent = NULL;
418 	char *p;
419 
420 	path = strdup (devfs_path);
421 	p = strrchr (path, '/');
422 	if (p == NULL) {
423 		free (path);
424 		return (NULL);
425 	}
426 	*p = '\0';
427 
428 	/* Look up the parent node in the gdl. */
429 	parent = hal_device_store_match_key_value_string (hald_get_gdl (),
430 	    "solaris.devfs_path", path);
431 
432 	if (parent == NULL) {
433 		/* Look up the parent node in the tdl. */
434 		parent = hal_device_store_match_key_value_string (hald_get_tdl (),
435 		    "solaris.devfs_path", path);
436 	}
437 
438 	free (path);
439 	return (parent);
440 }
441 
442 /*
443  * Handle the USB bus devices hot plugging events.
444  */
445 static void
446 sysevent_devfs_add(gchar *devfs_path)
447 {
448 	di_node_t node;
449 	HalDevice *parent;
450 	char *driver_name;
451 
452 	HAL_INFO (("devfs_handle: %s", devfs_path));
453 
454 	if ((node = di_init (devfs_path, DINFOCPYALL)) == DI_NODE_NIL) {
455 		HAL_INFO (("device not found in devinfo %s", devfs_path));
456 		return;
457 	}
458 
459 	if ((driver_name = di_driver_name (node)) == NULL)
460 		goto out;
461 
462 	/* The disk and printer devices are handled by EC_DEV_ADD class. */
463 	if ((strcmp (driver_name, "scsa2usb") == 0) ||
464 	    (strcmp (driver_name, "usbprn") == 0))
465 		goto out;
466 
467 	if ((parent = lookup_parent (devfs_path)) == NULL)
468 		goto out;
469 
470 	devinfo_usb_add (parent, node, devfs_path, NULL);
471 
472 	di_fini (node);
473 
474 	hotplug_event_process_queue ();
475 
476 	return;
477 
478  out:
479 	di_fini (node);
480 }
481 
482 static void
483 sysevent_pwrctl(gchar *class, gchar *subclass, gchar *phys_path,
484     gchar *dev_name, gchar *dev_hid, gchar *dev_uid, uint_t dev_index)
485 {
486 	const gchar prefix[] = "/org/freedesktop/Hal/devices/pseudo/acpi_drv_0";
487 	gchar udi[HAL_PATH_MAX];
488 
489 	if (strcmp(dev_hid, "PNP0C0A") == 0) {
490 		snprintf(udi, sizeof(udi), "%s_battery%d_0", prefix, dev_index);
491 		devinfo_battery_device_rescan(phys_path, udi);
492 	} else if (strcmp(dev_hid, "ACPI0003") == 0) {
493 		snprintf(udi, sizeof (udi), "%s_ac%d_0", prefix, dev_index);
494 		devinfo_battery_device_rescan(phys_path, udi);
495 	} else if (strcmp(dev_hid, "PNP0C0D") == 0) {
496 		snprintf(udi, sizeof (udi), "%s_lid_0", prefix);
497 		devinfo_lid_device_rescan(subclass, udi);
498 	} else if (strcmp(subclass, ESC_PWRCTL_POWER_BUTTON) == 0) {
499 		devinfo_power_button_rescan();
500 	} else if ((strcmp(subclass, ESC_PWRCTL_BRIGHTNESS_UP) == 0) ||
501 	    (strcmp(subclass, ESC_PWRCTL_BRIGHTNESS_DOWN) == 0)) {
502 		devinfo_brightness_hotkeys_rescan(subclass);
503 	} else {
504 		HAL_INFO(("Unmatched EC_PWRCTL"));
505 	}
506 }
507 
508 static void
509 sysevent_dr_remove_cpu()
510 {
511 
512 	HalDeviceStore	*gdl;
513 	GSList		*iter;
514 	HalDevice	*d, *del_dev;
515 	int		cpu_id, del_cpuid;
516 	kstat_ctl_t	*kc;
517 	kstat_t		*ksp;
518 	kstat_named_t	*ksdata;
519 	const char	*cpu_devfs_path;
520 	/*
521 	 * Find the CPU's that are DR removed. For each "processor" device in
522 	 * HAL device tree, check if it has its corresponding kstat_info. If
523 	 * not, then, that cpu has been removed and can remove the entry from
524 	 * HAL entry
525 	 */
526 
527 	HAL_DEBUG (("sysevent_dr_remove_cpu()"));
528 	kc = kstat_open ();
529 	if (kc == NULL) {
530 		HAL_INFO (("Error in removing HAL cpu entry during DR. Could"
531 		    " not open kstat to get cpu info: %s", strerror (errno)));
532 		return;
533 	}
534 
535 	/*
536 	 * Iterate through the HAL device list to get the processor devices
537 	 */
538 	gdl = hald_get_gdl ();
539 	iter = gdl->devices;
540 
541 	while (iter != NULL) {
542 		d = HAL_DEVICE (iter->data);
543 
544 		if (!hal_device_has_property (d, "processor.number")) {
545 			iter = iter->next;
546 			continue;
547 		}
548 
549 		cpu_id = hal_device_property_get_int (d, "processor.number");
550 
551 		/*
552 		 * Check if the above cpu_id has its info in kstat
553 		 */
554 
555 		ksp = kstat_lookup (kc, "cpu_info", cpu_id, NULL);
556 		if (ksp != NULL) {
557 			iter = iter->next;
558 			continue;
559 		}
560 		/*
561 		 *  kstat info not found. Delete the device entry
562 		 */
563 		HAL_INFO ((" Remove CPU entry: %d", cpu_id));
564 		iter = iter->next;
565 		cpu_devfs_path = hal_device_property_get_string (d,
566 		    "solaris.devfs_path");
567 		if (cpu_devfs_path == NULL) {
568 			HAL_INFO (("Could not get cpu_devfs_path to "
569 			    "remove for cpu_id %d", cpu_id));
570 		} else {
571 			/*
572 			 * Remove the cpu device
573 			 */
574 			HAL_DEBUG (("Queue %s for removal", cpu_devfs_path));
575 			devinfo_remove_enqueue ((char *)cpu_devfs_path, NULL);
576 			hotplug_event_process_queue ();
577 		}
578 	}
579 
580 	if (kc) {
581 		kstat_close (kc);
582 	}
583 }
584 
585 int
586 sysevent_dr_insert_cpu(di_node_t node, void *arg)
587 {
588 	char	*devfs_path;
589 	char	*device_type = NULL;
590 	DevinfoDevHandler *dh;
591 
592 	dh = &devinfo_cpu_handler;
593 	devfs_path = di_devfs_path (node);
594 
595 	(void) di_prop_lookup_strings (DDI_DEV_T_ANY, node, "device_type",
596 	    &device_type);
597 
598 	dh->add (NULL, node, devfs_path, device_type);
599 
600 	di_devfs_path_free (devfs_path);
601 	return (DI_WALK_CONTINUE);
602 }
603 
604 /*
605  * Remove/Add the DR event device
606  * Note: Currently it supports only CPU DR events
607  */
608 static void
609 sysevent_process_dr(gchar *ap_id, gchar *hint_val)
610 {
611 	cfga_err_t 		cfgerr;
612 	cfga_list_data_t 	*cfg_stat;
613 	int			nlist;
614 	char			*errstr;
615 	di_node_t		root_node;
616 
617 	if ((ap_id == NULL) || (hint_val == NULL))
618 		return;
619 	HAL_DEBUG (("sysevent_process_dr: %s", ap_id));
620 
621 	cfgerr = config_list_ext (1, (char *const *)&ap_id, &cfg_stat, &nlist,
622 	    NULL, NULL, &errstr, 0);
623 
624 	if (cfgerr != CFGA_OK) {
625 		HAL_INFO (("DR sysevent process %d config_list_ext error: %s",
626 		    ap_id, errstr));
627 		goto out;
628 	}
629 	/*
630 	 * Check if the device type is CPU
631 	 */
632 	HAL_DEBUG ((" Ap-Type: %s, State: %d", cfg_stat->ap_type,
633 	    cfg_stat->ap_r_state));
634 	if (strcmp (cfg_stat->ap_type, "CPU") == 0) {
635 		if (strcmp (hint_val, DR_HINT_REMOVE) == 0) {
636 			sysevent_dr_remove_cpu();
637 		} else if (strcmp (hint_val, DR_HINT_INSERT) == 0) {
638 			/*
639 			 * Go through the device list and add the new cpu
640 			 * entries into HAL
641 			 */
642 			if ((root_node =
643 			    di_init ("/", DINFOCPYALL)) == DI_NODE_NIL) {
644 				HAL_INFO (("di_init failed. "\
645 				    "Cannot insert CPU"));
646 				goto out;
647 			}
648 			di_walk_node (root_node, DI_WALK_CLDFIRST, NULL,
649 			    sysevent_dr_insert_cpu);
650 			di_fini (root_node);
651 			hotplug_event_process_queue ();
652 		}
653 	} else {
654 		HAL_INFO (("Not a CPU, so cannot DR"));
655 	}
656 
657 out:
658 	if (cfg_stat)
659 		free (cfg_stat);
660 	if (errstr)
661 		free (errstr);
662 }
663