1 /* 2 * Purpose: Export the firmware instance and label associated with 3 * a pci device to sysfs 4 * Copyright (C) 2010 Dell Inc. 5 * by Narendra K <Narendra_K@dell.com>, 6 * Jordan Hargrave <Jordan_Hargrave@dell.com> 7 * 8 * PCI Firmware Specification Revision 3.1 section 4.6.7 (DSM for Naming a 9 * PCI or PCI Express Device Under Operating Systems) defines an instance 10 * number and string name. This code retrieves them and exports them to sysfs. 11 * If the system firmware does not provide the ACPI _DSM (Device Specific 12 * Method), then the SMBIOS type 41 instance number and string is exported to 13 * sysfs. 14 * 15 * SMBIOS defines type 41 for onboard pci devices. This code retrieves 16 * the instance number and string from the type 41 record and exports 17 * it to sysfs. 18 * 19 * Please see http://linux.dell.com/wiki/index.php/Oss/libnetdevname for more 20 * information. 21 */ 22 23 #include <linux/dmi.h> 24 #include <linux/sysfs.h> 25 #include <linux/pci.h> 26 #include <linux/pci_ids.h> 27 #include <linux/module.h> 28 #include <linux/device.h> 29 #include <linux/nls.h> 30 #include <linux/acpi.h> 31 #include <linux/pci-acpi.h> 32 #include <acpi/acpi_bus.h> 33 #include "pci.h" 34 35 #define DEVICE_LABEL_DSM 0x07 36 37 #ifndef CONFIG_DMI 38 39 static inline int 40 pci_create_smbiosname_file(struct pci_dev *pdev) 41 { 42 return -1; 43 } 44 45 static inline void 46 pci_remove_smbiosname_file(struct pci_dev *pdev) 47 { 48 } 49 50 #else 51 52 enum smbios_attr_enum { 53 SMBIOS_ATTR_NONE = 0, 54 SMBIOS_ATTR_LABEL_SHOW, 55 SMBIOS_ATTR_INSTANCE_SHOW, 56 }; 57 58 static size_t 59 find_smbios_instance_string(struct pci_dev *pdev, char *buf, 60 enum smbios_attr_enum attribute) 61 { 62 const struct dmi_device *dmi; 63 struct dmi_dev_onboard *donboard; 64 int bus; 65 int devfn; 66 67 bus = pdev->bus->number; 68 devfn = pdev->devfn; 69 70 dmi = NULL; 71 while ((dmi = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, 72 NULL, dmi)) != NULL) { 73 donboard = dmi->device_data; 74 if (donboard && donboard->bus == bus && 75 donboard->devfn == devfn) { 76 if (buf) { 77 if (attribute == SMBIOS_ATTR_INSTANCE_SHOW) 78 return scnprintf(buf, PAGE_SIZE, 79 "%d\n", 80 donboard->instance); 81 else if (attribute == SMBIOS_ATTR_LABEL_SHOW) 82 return scnprintf(buf, PAGE_SIZE, 83 "%s\n", 84 dmi->name); 85 } 86 return strlen(dmi->name); 87 } 88 } 89 return 0; 90 } 91 92 static umode_t 93 smbios_instance_string_exist(struct kobject *kobj, struct attribute *attr, 94 int n) 95 { 96 struct device *dev; 97 struct pci_dev *pdev; 98 99 dev = container_of(kobj, struct device, kobj); 100 pdev = to_pci_dev(dev); 101 102 return find_smbios_instance_string(pdev, NULL, SMBIOS_ATTR_NONE) ? 103 S_IRUGO : 0; 104 } 105 106 static ssize_t 107 smbioslabel_show(struct device *dev, struct device_attribute *attr, char *buf) 108 { 109 struct pci_dev *pdev; 110 pdev = to_pci_dev(dev); 111 112 return find_smbios_instance_string(pdev, buf, 113 SMBIOS_ATTR_LABEL_SHOW); 114 } 115 116 static ssize_t 117 smbiosinstance_show(struct device *dev, 118 struct device_attribute *attr, char *buf) 119 { 120 struct pci_dev *pdev; 121 pdev = to_pci_dev(dev); 122 123 return find_smbios_instance_string(pdev, buf, 124 SMBIOS_ATTR_INSTANCE_SHOW); 125 } 126 127 static struct device_attribute smbios_attr_label = { 128 .attr = {.name = "label", .mode = 0444}, 129 .show = smbioslabel_show, 130 }; 131 132 static struct device_attribute smbios_attr_instance = { 133 .attr = {.name = "index", .mode = 0444}, 134 .show = smbiosinstance_show, 135 }; 136 137 static struct attribute *smbios_attributes[] = { 138 &smbios_attr_label.attr, 139 &smbios_attr_instance.attr, 140 NULL, 141 }; 142 143 static struct attribute_group smbios_attr_group = { 144 .attrs = smbios_attributes, 145 .is_visible = smbios_instance_string_exist, 146 }; 147 148 static int 149 pci_create_smbiosname_file(struct pci_dev *pdev) 150 { 151 return sysfs_create_group(&pdev->dev.kobj, &smbios_attr_group); 152 } 153 154 static void 155 pci_remove_smbiosname_file(struct pci_dev *pdev) 156 { 157 sysfs_remove_group(&pdev->dev.kobj, &smbios_attr_group); 158 } 159 160 #endif 161 162 #ifndef CONFIG_ACPI 163 164 static inline int 165 pci_create_acpi_index_label_files(struct pci_dev *pdev) 166 { 167 return -1; 168 } 169 170 static inline int 171 pci_remove_acpi_index_label_files(struct pci_dev *pdev) 172 { 173 return -1; 174 } 175 176 static inline bool 177 device_has_dsm(struct device *dev) 178 { 179 return false; 180 } 181 182 #else 183 184 static const char device_label_dsm_uuid[] = { 185 0xD0, 0x37, 0xC9, 0xE5, 0x53, 0x35, 0x7A, 0x4D, 186 0x91, 0x17, 0xEA, 0x4D, 0x19, 0xC3, 0x43, 0x4D 187 }; 188 189 enum acpi_attr_enum { 190 ACPI_ATTR_NONE = 0, 191 ACPI_ATTR_LABEL_SHOW, 192 ACPI_ATTR_INDEX_SHOW, 193 }; 194 195 static void dsm_label_utf16s_to_utf8s(union acpi_object *obj, char *buf) 196 { 197 int len; 198 len = utf16s_to_utf8s((const wchar_t *)obj-> 199 package.elements[1].string.pointer, 200 obj->package.elements[1].string.length, 201 UTF16_LITTLE_ENDIAN, 202 buf, PAGE_SIZE); 203 buf[len] = '\n'; 204 } 205 206 static int 207 dsm_get_label(acpi_handle handle, int func, 208 struct acpi_buffer *output, 209 char *buf, enum acpi_attr_enum attribute) 210 { 211 struct acpi_object_list input; 212 union acpi_object params[4]; 213 union acpi_object *obj; 214 int len = 0; 215 216 int err; 217 218 input.count = 4; 219 input.pointer = params; 220 params[0].type = ACPI_TYPE_BUFFER; 221 params[0].buffer.length = sizeof(device_label_dsm_uuid); 222 params[0].buffer.pointer = (char *)device_label_dsm_uuid; 223 params[1].type = ACPI_TYPE_INTEGER; 224 params[1].integer.value = 0x02; 225 params[2].type = ACPI_TYPE_INTEGER; 226 params[2].integer.value = func; 227 params[3].type = ACPI_TYPE_PACKAGE; 228 params[3].package.count = 0; 229 params[3].package.elements = NULL; 230 231 err = acpi_evaluate_object(handle, "_DSM", &input, output); 232 if (err) 233 return -1; 234 235 obj = (union acpi_object *)output->pointer; 236 237 switch (obj->type) { 238 case ACPI_TYPE_PACKAGE: 239 if (obj->package.count != 2) 240 break; 241 len = obj->package.elements[0].integer.value; 242 if (buf) { 243 if (attribute == ACPI_ATTR_INDEX_SHOW) 244 scnprintf(buf, PAGE_SIZE, "%llu\n", 245 obj->package.elements[0].integer.value); 246 else if (attribute == ACPI_ATTR_LABEL_SHOW) 247 dsm_label_utf16s_to_utf8s(obj, buf); 248 kfree(output->pointer); 249 return strlen(buf); 250 } 251 kfree(output->pointer); 252 return len; 253 break; 254 default: 255 kfree(output->pointer); 256 } 257 return -1; 258 } 259 260 static bool 261 device_has_dsm(struct device *dev) 262 { 263 acpi_handle handle; 264 struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; 265 266 handle = ACPI_HANDLE(dev); 267 268 if (!handle) 269 return FALSE; 270 271 if (dsm_get_label(handle, DEVICE_LABEL_DSM, &output, NULL, 272 ACPI_ATTR_NONE) > 0) 273 return TRUE; 274 275 return FALSE; 276 } 277 278 static umode_t 279 acpi_index_string_exist(struct kobject *kobj, struct attribute *attr, int n) 280 { 281 struct device *dev; 282 283 dev = container_of(kobj, struct device, kobj); 284 285 if (device_has_dsm(dev)) 286 return S_IRUGO; 287 288 return 0; 289 } 290 291 static ssize_t 292 acpilabel_show(struct device *dev, struct device_attribute *attr, char *buf) 293 { 294 struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; 295 acpi_handle handle; 296 int length; 297 298 handle = ACPI_HANDLE(dev); 299 300 if (!handle) 301 return -1; 302 303 length = dsm_get_label(handle, DEVICE_LABEL_DSM, 304 &output, buf, ACPI_ATTR_LABEL_SHOW); 305 306 if (length < 1) 307 return -1; 308 309 return length; 310 } 311 312 static ssize_t 313 acpiindex_show(struct device *dev, struct device_attribute *attr, char *buf) 314 { 315 struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; 316 acpi_handle handle; 317 int length; 318 319 handle = ACPI_HANDLE(dev); 320 321 if (!handle) 322 return -1; 323 324 length = dsm_get_label(handle, DEVICE_LABEL_DSM, 325 &output, buf, ACPI_ATTR_INDEX_SHOW); 326 327 if (length < 0) 328 return -1; 329 330 return length; 331 332 } 333 334 static struct device_attribute acpi_attr_label = { 335 .attr = {.name = "label", .mode = 0444}, 336 .show = acpilabel_show, 337 }; 338 339 static struct device_attribute acpi_attr_index = { 340 .attr = {.name = "acpi_index", .mode = 0444}, 341 .show = acpiindex_show, 342 }; 343 344 static struct attribute *acpi_attributes[] = { 345 &acpi_attr_label.attr, 346 &acpi_attr_index.attr, 347 NULL, 348 }; 349 350 static struct attribute_group acpi_attr_group = { 351 .attrs = acpi_attributes, 352 .is_visible = acpi_index_string_exist, 353 }; 354 355 static int 356 pci_create_acpi_index_label_files(struct pci_dev *pdev) 357 { 358 return sysfs_create_group(&pdev->dev.kobj, &acpi_attr_group); 359 } 360 361 static int 362 pci_remove_acpi_index_label_files(struct pci_dev *pdev) 363 { 364 sysfs_remove_group(&pdev->dev.kobj, &acpi_attr_group); 365 return 0; 366 } 367 #endif 368 369 void pci_create_firmware_label_files(struct pci_dev *pdev) 370 { 371 if (device_has_dsm(&pdev->dev)) 372 pci_create_acpi_index_label_files(pdev); 373 else 374 pci_create_smbiosname_file(pdev); 375 } 376 377 void pci_remove_firmware_label_files(struct pci_dev *pdev) 378 { 379 if (device_has_dsm(&pdev->dev)) 380 pci_remove_acpi_index_label_files(pdev); 381 else 382 pci_remove_smbiosname_file(pdev); 383 } 384