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 * 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