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
33static HalDevice *devinfo_cpu_add(HalDevice *, di_node_t, char *, char *);
34
35DevinfoDevHandler devinfo_cpu_handler = {
36	devinfo_cpu_add,
37	NULL,
38	NULL,
39	NULL,
40	NULL,
41	NULL
42};
43
44static HalDevice *
45devinfo_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
240next:
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