1 // SPDX-License-Identifier: GPL-2.0
3 * Purpose: Export the firmware instance and label associated with
4 * a pci device to sysfs
5 * Copyright (C) 2010 Dell Inc.
6 * by Narendra K <Narendra_K@dell.com>,
7 * Jordan Hargrave <Jordan_Hargrave@dell.com>
9 * PCI Firmware Specification Revision 3.1 section 4.6.7 (DSM for Naming a
10 * PCI or PCI Express Device Under Operating Systems) defines an instance
11 * number and string name. This code retrieves them and exports them to sysfs.
12 * If the system firmware does not provide the ACPI _DSM (Device Specific
13 * Method), then the SMBIOS type 41 instance number and string is exported to
16 * SMBIOS defines type 41 for onboard pci devices. This code retrieves
17 * the instance number and string from the type 41 record and exports
20 * Please see http://linux.dell.com/files/biosdevname/ for more
24 #include <linux/dmi.h>
25 #include <linux/sysfs.h>
26 #include <linux/pci.h>
27 #include <linux/pci_ids.h>
28 #include <linux/module.h>
29 #include <linux/device.h>
30 #include <linux/nls.h>
31 #include <linux/acpi.h>
32 #include <linux/pci-acpi.h>
36 enum smbios_attr_enum
{
38 SMBIOS_ATTR_LABEL_SHOW
,
39 SMBIOS_ATTR_INSTANCE_SHOW
,
42 static size_t find_smbios_instance_string(struct pci_dev
*pdev
, char *buf
,
43 enum smbios_attr_enum attribute
)
45 const struct dmi_device
*dmi
;
46 struct dmi_dev_onboard
*donboard
;
51 domain_nr
= pci_domain_nr(pdev
->bus
);
52 bus
= pdev
->bus
->number
;
56 while ((dmi
= dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD
,
57 NULL
, dmi
)) != NULL
) {
58 donboard
= dmi
->device_data
;
59 if (donboard
&& donboard
->segment
== domain_nr
&&
60 donboard
->bus
== bus
&&
61 donboard
->devfn
== devfn
) {
63 if (attribute
== SMBIOS_ATTR_INSTANCE_SHOW
)
64 return scnprintf(buf
, PAGE_SIZE
,
67 else if (attribute
== SMBIOS_ATTR_LABEL_SHOW
)
68 return scnprintf(buf
, PAGE_SIZE
,
72 return strlen(dmi
->name
);
78 static umode_t
smbios_instance_string_exist(struct kobject
*kobj
,
79 struct attribute
*attr
, int n
)
84 dev
= kobj_to_dev(kobj
);
85 pdev
= to_pci_dev(dev
);
87 return find_smbios_instance_string(pdev
, NULL
, SMBIOS_ATTR_NONE
) ?
91 static ssize_t
smbioslabel_show(struct device
*dev
,
92 struct device_attribute
*attr
, char *buf
)
95 pdev
= to_pci_dev(dev
);
97 return find_smbios_instance_string(pdev
, buf
,
98 SMBIOS_ATTR_LABEL_SHOW
);
101 static ssize_t
smbiosinstance_show(struct device
*dev
,
102 struct device_attribute
*attr
, char *buf
)
104 struct pci_dev
*pdev
;
105 pdev
= to_pci_dev(dev
);
107 return find_smbios_instance_string(pdev
, buf
,
108 SMBIOS_ATTR_INSTANCE_SHOW
);
111 static struct device_attribute smbios_attr_label
= {
112 .attr
= {.name
= "label", .mode
= 0444},
113 .show
= smbioslabel_show
,
116 static struct device_attribute smbios_attr_instance
= {
117 .attr
= {.name
= "index", .mode
= 0444},
118 .show
= smbiosinstance_show
,
121 static struct attribute
*smbios_attributes
[] = {
122 &smbios_attr_label
.attr
,
123 &smbios_attr_instance
.attr
,
127 static const struct attribute_group smbios_attr_group
= {
128 .attrs
= smbios_attributes
,
129 .is_visible
= smbios_instance_string_exist
,
132 static int pci_create_smbiosname_file(struct pci_dev
*pdev
)
134 return sysfs_create_group(&pdev
->dev
.kobj
, &smbios_attr_group
);
137 static void pci_remove_smbiosname_file(struct pci_dev
*pdev
)
139 sysfs_remove_group(&pdev
->dev
.kobj
, &smbios_attr_group
);
142 static inline int pci_create_smbiosname_file(struct pci_dev
*pdev
)
147 static inline void pci_remove_smbiosname_file(struct pci_dev
*pdev
)
153 enum acpi_attr_enum
{
154 ACPI_ATTR_LABEL_SHOW
,
155 ACPI_ATTR_INDEX_SHOW
,
158 static void dsm_label_utf16s_to_utf8s(union acpi_object
*obj
, char *buf
)
161 len
= utf16s_to_utf8s((const wchar_t *)obj
->buffer
.pointer
,
168 static int dsm_get_label(struct device
*dev
, char *buf
,
169 enum acpi_attr_enum attr
)
172 union acpi_object
*obj
, *tmp
;
175 handle
= ACPI_HANDLE(dev
);
179 obj
= acpi_evaluate_dsm(handle
, &pci_acpi_dsm_guid
, 0x2,
180 DEVICE_LABEL_DSM
, NULL
);
184 tmp
= obj
->package
.elements
;
185 if (obj
->type
== ACPI_TYPE_PACKAGE
&& obj
->package
.count
== 2 &&
186 tmp
[0].type
== ACPI_TYPE_INTEGER
&&
187 (tmp
[1].type
== ACPI_TYPE_STRING
||
188 tmp
[1].type
== ACPI_TYPE_BUFFER
)) {
190 * The second string element is optional even when
191 * this _DSM is implemented; when not implemented,
192 * this entry must return a null string.
194 if (attr
== ACPI_ATTR_INDEX_SHOW
) {
195 scnprintf(buf
, PAGE_SIZE
, "%llu\n", tmp
->integer
.value
);
196 } else if (attr
== ACPI_ATTR_LABEL_SHOW
) {
197 if (tmp
[1].type
== ACPI_TYPE_STRING
)
198 scnprintf(buf
, PAGE_SIZE
, "%s\n",
199 tmp
[1].string
.pointer
);
200 else if (tmp
[1].type
== ACPI_TYPE_BUFFER
)
201 dsm_label_utf16s_to_utf8s(tmp
+ 1, buf
);
203 len
= strlen(buf
) > 0 ? strlen(buf
) : -1;
211 static bool device_has_dsm(struct device
*dev
)
215 handle
= ACPI_HANDLE(dev
);
219 return !!acpi_check_dsm(handle
, &pci_acpi_dsm_guid
, 0x2,
220 1 << DEVICE_LABEL_DSM
);
223 static umode_t
acpi_index_string_exist(struct kobject
*kobj
,
224 struct attribute
*attr
, int n
)
228 dev
= kobj_to_dev(kobj
);
230 if (device_has_dsm(dev
))
236 static ssize_t
acpilabel_show(struct device
*dev
,
237 struct device_attribute
*attr
, char *buf
)
239 return dsm_get_label(dev
, buf
, ACPI_ATTR_LABEL_SHOW
);
242 static ssize_t
acpiindex_show(struct device
*dev
,
243 struct device_attribute
*attr
, char *buf
)
245 return dsm_get_label(dev
, buf
, ACPI_ATTR_INDEX_SHOW
);
248 static struct device_attribute acpi_attr_label
= {
249 .attr
= {.name
= "label", .mode
= 0444},
250 .show
= acpilabel_show
,
253 static struct device_attribute acpi_attr_index
= {
254 .attr
= {.name
= "acpi_index", .mode
= 0444},
255 .show
= acpiindex_show
,
258 static struct attribute
*acpi_attributes
[] = {
259 &acpi_attr_label
.attr
,
260 &acpi_attr_index
.attr
,
264 static const struct attribute_group acpi_attr_group
= {
265 .attrs
= acpi_attributes
,
266 .is_visible
= acpi_index_string_exist
,
269 static int pci_create_acpi_index_label_files(struct pci_dev
*pdev
)
271 return sysfs_create_group(&pdev
->dev
.kobj
, &acpi_attr_group
);
274 static int pci_remove_acpi_index_label_files(struct pci_dev
*pdev
)
276 sysfs_remove_group(&pdev
->dev
.kobj
, &acpi_attr_group
);
280 static inline int pci_create_acpi_index_label_files(struct pci_dev
*pdev
)
285 static inline int pci_remove_acpi_index_label_files(struct pci_dev
*pdev
)
290 static inline bool device_has_dsm(struct device
*dev
)
296 void pci_create_firmware_label_files(struct pci_dev
*pdev
)
298 if (device_has_dsm(&pdev
->dev
))
299 pci_create_acpi_index_label_files(pdev
);
301 pci_create_smbiosname_file(pdev
);
304 void pci_remove_firmware_label_files(struct pci_dev
*pdev
)
306 if (device_has_dsm(&pdev
->dev
))
307 pci_remove_acpi_index_label_files(pdev
);
309 pci_remove_smbiosname_file(pdev
);