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