1 /***************************************************************************
2  *
3  * devinfo_cpu : cpu devices
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 <string.h>
20 #include <kstat.h>
21 #include <sys/utsname.h>
22 #include <libdevinfo.h>
23 #include <sys/systeminfo.h>
24 
25 #include "../osspec.h"
26 #include "../logger.h"
27 #include "../hald.h"
28 #include "../hald_dbus.h"
29 #include "../device_info.h"
30 #include "../util.h"
31 #include "devinfo_cpu.h"
32 
33 static HalDevice *devinfo_cpu_add(HalDevice *, di_node_t, char *, char *);
34 
35 DevinfoDevHandler devinfo_cpu_handler = {
36 	devinfo_cpu_add,
37 	NULL,
38 	NULL,
39 	NULL,
40 	NULL,
41 	NULL
42 };
43 
44 static HalDevice *
devinfo_cpu_add(HalDevice * parent,di_node_t node,char * devfs_path,char * device_type)45 devinfo_cpu_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
46 {
47 
48 	HalDevice	*d;
49 	char		*prom_device_type = NULL;
50 	int		*int_cpu_id;
51 	static int	cpu_id = -1;
52 	uint64_t	clock_mhz;
53 	di_prom_handle_t phdl;
54 	kstat_ctl_t	*kc;
55 	kstat_t		*ksp;
56 	kstat_named_t	*ksdata;
57 	dbus_bool_t	is_supp_freqs;
58 	char		udi[HAL_PATH_MAX];
59 	char		*driver_name, *s;
60 	char		cpu_devfs_path[HAL_PATH_MAX];
61 
62 	/*
63 	 * If it is x86, the software device tree node will have the
64 	 * device_type information which is the one passed above. If it is
65 	 * NULL, check if the node has a PROM entry, and check the device_type
66 	 * in case of sparc. Else return NULL
67 	 */
68 	if (device_type == NULL) {
69 		/*
70 		 * Check the device type if it has a PROM entry. Because
71 		 * in sparc, the device_type entry will in the PROM node
72 		 */
73 		if (di_nodeid (node) == DI_PROM_NODEID) {
74 			phdl = di_prom_init ();
75 			if (phdl == DI_PROM_HANDLE_NIL) {
76 				HAL_ERROR (("Error in Initializing the PROM "
77 				    "handle to find cpu device: %s",
78 				    strerror (errno)));
79 				return (NULL);
80 			}
81 			if (di_prom_prop_lookup_strings (phdl, node,
82 			    "device_type", &prom_device_type) == -1) {
83 				di_prom_fini (phdl);
84 				return (NULL);
85 			}
86 			if (strcmp (prom_device_type, "cpu") != 0) {
87 				di_prom_fini (phdl);
88 				return (NULL);
89 			}
90 			/*
91 			 * Get cpuid if available
92 			 */
93 			if (di_prom_prop_lookup_ints (phdl, node,
94 			    "cpuid", &int_cpu_id) > 0) {
95 				cpu_id = *int_cpu_id;
96 			} else {
97 				/*
98 				 * There is no cpuid entry in this arch.Just
99 				 * increment the cpuid which will be the
100 				 * current instance
101 				 */
102 				++cpu_id;
103 			}
104 			di_prom_fini (phdl);
105 		} else {
106 			return (NULL);
107 		}
108 
109 	} else if (strcmp (device_type, "cpu") == 0) {
110 		/*
111 		 * This is a x86 arch, because software device tree node
112 		 * has the device_type entry for cpu. The "reg" property
113 		 * will have the cpuid. If not just increment the cpuid
114 		 * which will be the current cpu instance in the kstat
115 		 */
116 		if (di_prop_lookup_ints (DDI_DEV_T_ANY, node,
117 		    "reg", &int_cpu_id) > 0) {
118 			cpu_id = *int_cpu_id;
119 		} else {
120 			/*
121 			 * There is no cpuid entry in this arch. Just
122 			 * increment the cpuid which will be the
123 			 * current instance
124 			 */
125 			++cpu_id;
126 		}
127 
128 	} else {
129 		return (NULL);
130 	}
131 
132 	HAL_DEBUG (("CPUID=> %x", cpu_id));
133 
134 	d = hal_device_new ();
135 
136 	/*
137 	 * devinfo_set_default_properties () uses di_instance() as part of
138 	 * the udi. For some solaris devices like cpu di_instance() is not
139 	 * present and it returns -1. For the udi to be unique can use the
140 	 * cpu_id.
141 	 */
142 	hal_device_property_set_string (d, "info.parent",
143 	    "/org/freedesktop/Hal/devices/local");
144 
145 	/*
146 	 * If cpu driver is not installed, then devfs_path returned by
147 	 * libdevinfo will be same for all cpu's.
148 	 * Since HAL stores the devices in its tree based on the devfs_path,
149 	 * To make it unique, will be concatenating devfs_path with cpu_id
150 	 */
151 	if (di_driver_name (node) == NULL) {
152 		snprintf (cpu_devfs_path, HAL_PATH_MAX, "%s_%d",
153 		    devfs_path, cpu_id);
154 	} else {
155 		snprintf (cpu_devfs_path, HAL_PATH_MAX, "%s", devfs_path);
156 	}
157 
158 	HAL_DEBUG(("DevfsPath=> %s, CPUID=> %d", cpu_devfs_path, cpu_id));
159 
160 	hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
161 	    "/org/freedesktop/Hal/devices%s_%d", cpu_devfs_path, cpu_id);
162 	hal_device_set_udi (d, udi);
163 	hal_device_property_set_string (d, "info.udi", udi);
164 	if (di_prop_lookup_strings (DDI_DEV_T_ANY, node, "model", &s) > 0) {
165 		hal_device_property_set_string (d, "info.product", s);
166 	} else {
167 		hal_device_property_set_string (d, "info.product",
168 		    di_node_name (node));
169 	}
170 	hal_device_property_set_string (d, "solaris.devfs_path",
171 	    cpu_devfs_path);
172 	if ((driver_name = di_driver_name (node)) != NULL) {
173 		hal_device_property_set_string (d, "info.solaris.driver",
174 		    driver_name);
175 	}
176 
177 	hal_device_add_capability (d, "processor");
178 
179 	hal_device_property_set_int (d, "processor.number", cpu_id);
180 
181 	/*
182 	 * Get the cpu related info from the kstat
183 	 */
184 	kc = kstat_open ();
185 	if (kc == NULL) {
186 		HAL_ERROR (("Could not open kstat to get cpu info: %s",
187 		    strerror (errno)));
188 		goto next;
189 	}
190 
191 	ksp = kstat_lookup (kc, "cpu_info", cpu_id, NULL);
192 	if (ksp == NULL) {
193 		HAL_ERROR (("Could not lookup kstat to get cpu info: %s",
194 		    strerror (errno)));
195 		if (kc) {
196 			kstat_close (kc);
197 		}
198 		return (NULL);
199 	}
200 
201 	kstat_read (kc, ksp, NULL);
202 	ksdata = (kstat_named_t *)kstat_data_lookup (ksp, "clock_MHz");
203 	if (ksdata == NULL) {
204 		HAL_ERROR (("Could not get kstat clock_MHz data for cpu: %s",
205 		    strerror (errno)));
206 		goto next;
207 	}
208 	clock_mhz = (uint64_t)ksdata->value.l;
209 
210 	if (hal_device_property_set_uint64 (d, "processor.maximum_speed",
211 	    clock_mhz) == FALSE) {
212 		HAL_INFO (("Could not set the processor speed device prop"));
213 	}
214 
215 
216 	ksdata = (kstat_named_t *)kstat_data_lookup (ksp,
217 	    "supported_frequencies_Hz");
218 	if (ksdata == NULL) {
219 		HAL_INFO (("Could not get kstat supported_frequencies_Hz data"
220 		    " for cpu: %s", strerror (errno)));
221 		is_supp_freqs = FALSE;
222 	} else {
223 		/*
224 		 * If more than one freq is supported, then they are seperated
225 		 * by a ":"
226 		 */
227 		if (strstr (ksdata->value.str.addr.ptr, ":") == NULL) {
228 			is_supp_freqs = FALSE;
229 		} else {
230 			is_supp_freqs = TRUE;
231 		}
232 	}
233 
234 	if (hal_device_property_set_bool (d, "processor.can_throttle",
235 	    is_supp_freqs) == FALSE) {
236 		HAL_INFO (("Could not set the processor.can_throttle"
237 		    " device prop"));
238 	}
239 
240 next:
241 	if (kc) {
242 		kstat_close (kc);
243 	}
244 
245 	devinfo_add_enqueue (d, cpu_devfs_path, &devinfo_cpu_handler);
246 	return (d);
247 }
248