1 // SPDX-License-Identifier: GPL-2.0
3 * Export the firmware instance and label associated with a PCI device to
6 * Copyright (C) 2010 Dell Inc.
7 * by Narendra K <Narendra_K@dell.com>,
8 * Jordan Hargrave <Jordan_Hargrave@dell.com>
10 * PCI Firmware Specification Revision 3.1 section 4.6.7 (DSM for Naming a
11 * PCI or PCI Express Device Under Operating Systems) defines an instance
12 * number and string name. This code retrieves them and exports them to sysfs.
13 * If the system firmware does not provide the ACPI _DSM (Device Specific
14 * Method), then the SMBIOS type 41 instance number and string is exported to
17 * SMBIOS defines type 41 for onboard pci devices. This code retrieves
18 * the instance number and string from the type 41 record and exports
21 * Please see https://linux.dell.com/files/biosdevname/ for more
25 #include <linux/dmi.h>
26 #include <linux/sysfs.h>
27 #include <linux/pci.h>
28 #include <linux/pci_ids.h>
29 #include <linux/module.h>
30 #include <linux/device.h>
31 #include <linux/nls.h>
32 #include <linux/acpi.h>
33 #include <linux/pci-acpi.h>
36 static bool device_has_acpi_name(struct device
*dev
)
39 acpi_handle handle
= ACPI_HANDLE(dev
);
44 return acpi_check_dsm(handle
, &pci_acpi_dsm_guid
, 0x2,
45 1 << DSM_PCI_DEVICE_NAME
);
52 enum smbios_attr_enum
{
54 SMBIOS_ATTR_LABEL_SHOW
,
55 SMBIOS_ATTR_INSTANCE_SHOW
,
58 static size_t find_smbios_instance_string(struct pci_dev
*pdev
, char *buf
,
59 enum smbios_attr_enum attribute
)
61 const struct dmi_device
*dmi
;
62 struct dmi_dev_onboard
*donboard
;
63 int domain_nr
= pci_domain_nr(pdev
->bus
);
64 int bus
= pdev
->bus
->number
;
65 int devfn
= pdev
->devfn
;
68 while ((dmi
= dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD
,
69 NULL
, dmi
)) != NULL
) {
70 donboard
= dmi
->device_data
;
71 if (donboard
&& donboard
->segment
== domain_nr
&&
72 donboard
->bus
== bus
&&
73 donboard
->devfn
== devfn
) {
75 if (attribute
== SMBIOS_ATTR_INSTANCE_SHOW
)
76 return sysfs_emit(buf
, "%d\n",
78 else if (attribute
== SMBIOS_ATTR_LABEL_SHOW
)
79 return sysfs_emit(buf
, "%s\n",
82 return strlen(dmi
->name
);
88 static ssize_t
smbios_label_show(struct device
*dev
,
89 struct device_attribute
*attr
, char *buf
)
91 struct pci_dev
*pdev
= to_pci_dev(dev
);
93 return find_smbios_instance_string(pdev
, buf
,
94 SMBIOS_ATTR_LABEL_SHOW
);
96 static struct device_attribute dev_attr_smbios_label
= __ATTR(label
, 0444,
97 smbios_label_show
, NULL
);
99 static ssize_t
index_show(struct device
*dev
, struct device_attribute
*attr
,
102 struct pci_dev
*pdev
= to_pci_dev(dev
);
104 return find_smbios_instance_string(pdev
, buf
,
105 SMBIOS_ATTR_INSTANCE_SHOW
);
107 static DEVICE_ATTR_RO(index
);
109 static struct attribute
*smbios_attrs
[] = {
110 &dev_attr_smbios_label
.attr
,
111 &dev_attr_index
.attr
,
115 static umode_t
smbios_attr_is_visible(struct kobject
*kobj
, struct attribute
*a
,
118 struct device
*dev
= kobj_to_dev(kobj
);
119 struct pci_dev
*pdev
= to_pci_dev(dev
);
121 if (device_has_acpi_name(dev
))
124 if (!find_smbios_instance_string(pdev
, NULL
, SMBIOS_ATTR_NONE
))
130 const struct attribute_group pci_dev_smbios_attr_group
= {
131 .attrs
= smbios_attrs
,
132 .is_visible
= smbios_attr_is_visible
,
137 enum acpi_attr_enum
{
138 ACPI_ATTR_LABEL_SHOW
,
139 ACPI_ATTR_INDEX_SHOW
,
142 static int dsm_label_utf16s_to_utf8s(union acpi_object
*obj
, char *buf
)
146 len
= utf16s_to_utf8s((const wchar_t *)obj
->buffer
.pointer
,
155 static int dsm_get_label(struct device
*dev
, char *buf
,
156 enum acpi_attr_enum attr
)
158 acpi_handle handle
= ACPI_HANDLE(dev
);
159 union acpi_object
*obj
, *tmp
;
165 obj
= acpi_evaluate_dsm(handle
, &pci_acpi_dsm_guid
, 0x2,
166 DSM_PCI_DEVICE_NAME
, NULL
);
170 tmp
= obj
->package
.elements
;
171 if (obj
->type
== ACPI_TYPE_PACKAGE
&& obj
->package
.count
== 2 &&
172 tmp
[0].type
== ACPI_TYPE_INTEGER
&&
173 (tmp
[1].type
== ACPI_TYPE_STRING
||
174 tmp
[1].type
== ACPI_TYPE_BUFFER
)) {
176 * The second string element is optional even when
177 * this _DSM is implemented; when not implemented,
178 * this entry must return a null string.
180 if (attr
== ACPI_ATTR_INDEX_SHOW
) {
181 len
= sysfs_emit(buf
, "%llu\n", tmp
->integer
.value
);
182 } else if (attr
== ACPI_ATTR_LABEL_SHOW
) {
183 if (tmp
[1].type
== ACPI_TYPE_STRING
)
184 len
= sysfs_emit(buf
, "%s\n",
185 tmp
[1].string
.pointer
);
186 else if (tmp
[1].type
== ACPI_TYPE_BUFFER
)
187 len
= dsm_label_utf16s_to_utf8s(tmp
+ 1, buf
);
193 return len
> 0 ? len
: -1;
196 static ssize_t
label_show(struct device
*dev
, struct device_attribute
*attr
,
199 return dsm_get_label(dev
, buf
, ACPI_ATTR_LABEL_SHOW
);
201 static DEVICE_ATTR_RO(label
);
203 static ssize_t
acpi_index_show(struct device
*dev
,
204 struct device_attribute
*attr
, char *buf
)
206 return dsm_get_label(dev
, buf
, ACPI_ATTR_INDEX_SHOW
);
208 static DEVICE_ATTR_RO(acpi_index
);
210 static struct attribute
*acpi_attrs
[] = {
211 &dev_attr_label
.attr
,
212 &dev_attr_acpi_index
.attr
,
216 static umode_t
acpi_attr_is_visible(struct kobject
*kobj
, struct attribute
*a
,
219 struct device
*dev
= kobj_to_dev(kobj
);
221 if (!device_has_acpi_name(dev
))
227 const struct attribute_group pci_dev_acpi_attr_group
= {
229 .is_visible
= acpi_attr_is_visible
,