Commit 6058989bad05b82e78baacce69ec14f27a11b5fd

Authored by Narendra_K@Dell.com
Committed by Jesse Barnes
1 parent cdb9755849

PCI: Export ACPI _DSM provided firmware instance number and string name to sysfs

This patch exports ACPI _DSM (Device Specific Method) provided firmware
instance number and string name of PCI devices as defined by 'PCI
Firmware Specification Revision 3.1' section 4.6.7.( DSM for Naming a
PCI or PCI Express Device Under Operating Systems) to sysfs.

New files created are:
  /sys/bus/pci/devices/.../label which contains the firmware name for
the device in question, and
  /sys/bus/pci/devices/.../acpi_index which contains the firmware device type
instance for the given device.

cat /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/acpi_index
1
cat /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/label
Embedded Broadcom 5709C NIC 1

cat /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.1/acpi_index
2
cat /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.1/label
Embedded Broadcom 5709C NIC 2

The ACPI _DSM provided firmware 'instance number' and 'string name' will
be given priority if the firmware also provides 'SMBIOS type 41 device
type instance and string'.

Signed-off-by: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Jordan Hargrave <jordan_hargrave@dell.com>
Signed-off-by: Narendra K <narendra_k@dell.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>

Showing 5 changed files with 270 additions and 15 deletions Side-by-side Diff

Documentation/ABI/testing/sysfs-bus-pci
... ... @@ -145,9 +145,11 @@
145 145 Contact: Narendra K <narendra_k@dell.com>, linux-bugs@dell.com
146 146 Description:
147 147 Reading this attribute will provide the firmware
148   - given name(SMBIOS type 41 string) of the PCI device.
149   - The attribute will be created only if the firmware
150   - has given a name to the PCI device.
  148 + given name (SMBIOS type 41 string or ACPI _DSM string) of
  149 + the PCI device. The attribute will be created only
  150 + if the firmware has given a name to the PCI device.
  151 + ACPI _DSM string name will be given priority if the
  152 + system firmware provides SMBIOS type 41 string also.
151 153 Users:
152 154 Userspace applications interested in knowing the
153 155 firmware assigned name of the PCI device.
154 156  
... ... @@ -157,13 +159,28 @@
157 159 Contact: Narendra K <narendra_k@dell.com>, linux-bugs@dell.com
158 160 Description:
159 161 Reading this attribute will provide the firmware
160   - given instance(SMBIOS type 41 device type instance)
161   - of the PCI device. The attribute will be created
162   - only if the firmware has given a device type instance
163   - to the PCI device.
  162 + given instance (SMBIOS type 41 device type instance) of the
  163 + PCI device. The attribute will be created only if the firmware
  164 + has given an instance number to the PCI device.
164 165 Users:
165 166 Userspace applications interested in knowing the
166 167 firmware assigned device type instance of the PCI
  168 + device that can help in understanding the firmware
  169 + intended order of the PCI device.
  170 +
  171 +What: /sys/bus/pci/devices/.../acpi_index
  172 +Date: July 2010
  173 +Contact: Narendra K <narendra_k@dell.com>, linux-bugs@dell.com
  174 +Description:
  175 + Reading this attribute will provide the firmware
  176 + given instance (ACPI _DSM instance number) of the PCI device.
  177 + The attribute will be created only if the firmware has given
  178 + an instance number to the PCI device. ACPI _DSM instance number
  179 + will be given priority if the system firmware provides SMBIOS
  180 + type 41 device type instance also.
  181 +Users:
  182 + Userspace applications interested in knowing the
  183 + firmware assigned instance number of the PCI
167 184 device that can help in understanding the firmware
168 185 intended order of the PCI device.
... ... @@ -87,4 +87,6 @@
87 87 depends on ACPI
88 88 depends on HOTPLUG
89 89 default y
  90 +
  91 +select NLS if (DMI || ACPI)
drivers/pci/Makefile
... ... @@ -53,8 +53,9 @@
53 53  
54 54 #
55 55 # ACPI Related PCI FW Functions
  56 +# ACPI _DSM provided firmware instance and string name
56 57 #
57   -obj-$(CONFIG_ACPI) += pci-acpi.o
  58 +obj-$(CONFIG_ACPI) += pci-acpi.o pci-label.o
58 59  
59 60 # SMBIOS provided firmware instance and labels
60 61 obj-$(CONFIG_DMI) += pci-label.o
drivers/pci/pci-label.c
... ... @@ -5,6 +5,13 @@
5 5 * by Narendra K <Narendra_K@dell.com>,
6 6 * Jordan Hargrave <Jordan_Hargrave@dell.com>
7 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 + *
8 15 * SMBIOS defines type 41 for onboard pci devices. This code retrieves
9 16 * the instance number and string from the type 41 record and exports
10 17 * it to sysfs.
11 18  
... ... @@ -19,8 +26,30 @@
19 26 #include <linux/pci_ids.h>
20 27 #include <linux/module.h>
21 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_drivers.h>
  33 +#include <acpi/acpi_bus.h>
22 34 #include "pci.h"
23 35  
  36 +#define DEVICE_LABEL_DSM 0x07
  37 +
  38 +#ifndef CONFIG_DMI
  39 +
  40 +static inline int
  41 +pci_create_smbiosname_file(struct pci_dev *pdev)
  42 +{
  43 + return -1;
  44 +}
  45 +
  46 +static inline void
  47 +pci_remove_smbiosname_file(struct pci_dev *pdev)
  48 +{
  49 +}
  50 +
  51 +#else
  52 +
24 53 enum smbios_attr_enum {
25 54 SMBIOS_ATTR_NONE = 0,
26 55 SMBIOS_ATTR_LABEL_SHOW,
... ... @@ -120,9 +149,7 @@
120 149 static int
121 150 pci_create_smbiosname_file(struct pci_dev *pdev)
122 151 {
123   - if (!sysfs_create_group(&pdev->dev.kobj, &smbios_attr_group))
124   - return 0;
125   - return -ENODEV;
  152 + return sysfs_create_group(&pdev->dev.kobj, &smbios_attr_group);
126 153 }
127 154  
128 155 static void
129 156  
130 157  
... ... @@ -131,14 +158,222 @@
131 158 sysfs_remove_group(&pdev->dev.kobj, &smbios_attr_group);
132 159 }
133 160  
  161 +#endif
  162 +
  163 +#ifndef CONFIG_ACPI
  164 +
  165 +static inline int
  166 +pci_create_acpi_index_label_files(struct pci_dev *pdev)
  167 +{
  168 + return -1;
  169 +}
  170 +
  171 +static inline int
  172 +pci_remove_acpi_index_label_files(struct pci_dev *pdev)
  173 +{
  174 + return -1;
  175 +}
  176 +
  177 +#else
  178 +
  179 +static const char device_label_dsm_uuid[] = {
  180 + 0xD0, 0x37, 0xC9, 0xE5, 0x53, 0x35, 0x7A, 0x4D,
  181 + 0x91, 0x17, 0xEA, 0x4D, 0x19, 0xC3, 0x43, 0x4D
  182 +};
  183 +
  184 +enum acpi_attr_enum {
  185 + ACPI_ATTR_NONE = 0,
  186 + ACPI_ATTR_LABEL_SHOW,
  187 + ACPI_ATTR_INDEX_SHOW,
  188 +};
  189 +
  190 +static void dsm_label_utf16s_to_utf8s(union acpi_object *obj, char *buf)
  191 +{
  192 + int len;
  193 + len = utf16s_to_utf8s((const wchar_t *)obj->
  194 + package.elements[1].string.pointer,
  195 + obj->package.elements[1].string.length,
  196 + UTF16_LITTLE_ENDIAN,
  197 + buf, PAGE_SIZE);
  198 + buf[len] = '\n';
  199 +}
  200 +
  201 +static int
  202 +dsm_get_label(acpi_handle handle, int func,
  203 + struct acpi_buffer *output,
  204 + char *buf, enum acpi_attr_enum attribute)
  205 +{
  206 + struct acpi_object_list input;
  207 + union acpi_object params[4];
  208 + union acpi_object *obj;
  209 + int len = 0;
  210 +
  211 + int err;
  212 +
  213 + input.count = 4;
  214 + input.pointer = params;
  215 + params[0].type = ACPI_TYPE_BUFFER;
  216 + params[0].buffer.length = sizeof(device_label_dsm_uuid);
  217 + params[0].buffer.pointer = (char *)device_label_dsm_uuid;
  218 + params[1].type = ACPI_TYPE_INTEGER;
  219 + params[1].integer.value = 0x02;
  220 + params[2].type = ACPI_TYPE_INTEGER;
  221 + params[2].integer.value = func;
  222 + params[3].type = ACPI_TYPE_PACKAGE;
  223 + params[3].package.count = 0;
  224 + params[3].package.elements = NULL;
  225 +
  226 + err = acpi_evaluate_object(handle, "_DSM", &input, output);
  227 + if (err)
  228 + return -1;
  229 +
  230 + obj = (union acpi_object *)output->pointer;
  231 +
  232 + switch (obj->type) {
  233 + case ACPI_TYPE_PACKAGE:
  234 + if (obj->package.count != 2)
  235 + break;
  236 + len = obj->package.elements[0].integer.value;
  237 + if (buf) {
  238 + if (attribute == ACPI_ATTR_INDEX_SHOW)
  239 + scnprintf(buf, PAGE_SIZE, "%llu\n",
  240 + obj->package.elements[0].integer.value);
  241 + else if (attribute == ACPI_ATTR_LABEL_SHOW)
  242 + dsm_label_utf16s_to_utf8s(obj, buf);
  243 + kfree(output->pointer);
  244 + return strlen(buf);
  245 + }
  246 + kfree(output->pointer);
  247 + return len;
  248 + break;
  249 + default:
  250 + kfree(output->pointer);
  251 + }
  252 + return -1;
  253 +}
  254 +
  255 +static bool
  256 +device_has_dsm(struct device *dev)
  257 +{
  258 + acpi_handle handle;
  259 + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
  260 +
  261 + handle = DEVICE_ACPI_HANDLE(dev);
  262 +
  263 + if (!handle)
  264 + return FALSE;
  265 +
  266 + if (dsm_get_label(handle, DEVICE_LABEL_DSM, &output, NULL,
  267 + ACPI_ATTR_NONE) > 0)
  268 + return TRUE;
  269 +
  270 + return FALSE;
  271 +}
  272 +
  273 +static mode_t
  274 +acpi_index_string_exist(struct kobject *kobj, struct attribute *attr, int n)
  275 +{
  276 + struct device *dev;
  277 +
  278 + dev = container_of(kobj, struct device, kobj);
  279 +
  280 + if (device_has_dsm(dev))
  281 + return S_IRUGO;
  282 +
  283 + return 0;
  284 +}
  285 +
  286 +static ssize_t
  287 +acpilabel_show(struct device *dev, struct device_attribute *attr, char *buf)
  288 +{
  289 + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
  290 + acpi_handle handle;
  291 + int length;
  292 +
  293 + handle = DEVICE_ACPI_HANDLE(dev);
  294 +
  295 + if (!handle)
  296 + return -1;
  297 +
  298 + length = dsm_get_label(handle, DEVICE_LABEL_DSM,
  299 + &output, buf, ACPI_ATTR_LABEL_SHOW);
  300 +
  301 + if (length < 1)
  302 + return -1;
  303 +
  304 + return length;
  305 +}
  306 +
  307 +static ssize_t
  308 +acpiindex_show(struct device *dev, struct device_attribute *attr, char *buf)
  309 +{
  310 + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
  311 + acpi_handle handle;
  312 + int length;
  313 +
  314 + handle = DEVICE_ACPI_HANDLE(dev);
  315 +
  316 + if (!handle)
  317 + return -1;
  318 +
  319 + length = dsm_get_label(handle, DEVICE_LABEL_DSM,
  320 + &output, buf, ACPI_ATTR_INDEX_SHOW);
  321 +
  322 + if (length < 0)
  323 + return -1;
  324 +
  325 + return length;
  326 +
  327 +}
  328 +
  329 +static struct device_attribute acpi_attr_label = {
  330 + .attr = {.name = "label", .mode = 0444},
  331 + .show = acpilabel_show,
  332 +};
  333 +
  334 +static struct device_attribute acpi_attr_index = {
  335 + .attr = {.name = "acpi_index", .mode = 0444},
  336 + .show = acpiindex_show,
  337 +};
  338 +
  339 +static struct attribute *acpi_attributes[] = {
  340 + &acpi_attr_label.attr,
  341 + &acpi_attr_index.attr,
  342 + NULL,
  343 +};
  344 +
  345 +static struct attribute_group acpi_attr_group = {
  346 + .attrs = acpi_attributes,
  347 + .is_visible = acpi_index_string_exist,
  348 +};
  349 +
  350 +static int
  351 +pci_create_acpi_index_label_files(struct pci_dev *pdev)
  352 +{
  353 + return sysfs_create_group(&pdev->dev.kobj, &acpi_attr_group);
  354 +}
  355 +
  356 +static int
  357 +pci_remove_acpi_index_label_files(struct pci_dev *pdev)
  358 +{
  359 + sysfs_remove_group(&pdev->dev.kobj, &acpi_attr_group);
  360 + return 0;
  361 +}
  362 +#endif
  363 +
134 364 void pci_create_firmware_label_files(struct pci_dev *pdev)
135 365 {
136   - if (!pci_create_smbiosname_file(pdev))
137   - ;
  366 + if (device_has_dsm(&pdev->dev))
  367 + pci_create_acpi_index_label_files(pdev);
  368 + else
  369 + pci_create_smbiosname_file(pdev);
138 370 }
139 371  
140 372 void pci_remove_firmware_label_files(struct pci_dev *pdev)
141 373 {
142   - pci_remove_smbiosname_file(pdev);
  374 + if (device_has_dsm(&pdev->dev))
  375 + pci_remove_acpi_index_label_files(pdev);
  376 + else
  377 + pci_remove_smbiosname_file(pdev);
143 378 }
... ... @@ -11,7 +11,7 @@
11 11 extern int pci_uevent(struct device *dev, struct kobj_uevent_env *env);
12 12 extern int pci_create_sysfs_dev_files(struct pci_dev *pdev);
13 13 extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
14   -#ifndef CONFIG_DMI
  14 +#if !defined(CONFIG_DMI) && !defined(CONFIG_ACPI)
15 15 static inline void pci_create_firmware_label_files(struct pci_dev *pdev)
16 16 { return; }
17 17 static inline void pci_remove_firmware_label_files(struct pci_dev *pdev)