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