xref: /OK3568_Linux_fs/kernel/drivers/pci/pci-label.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Export the firmware instance and label associated with a PCI device to
4*4882a593Smuzhiyun  * sysfs
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * Copyright (C) 2010 Dell Inc.
7*4882a593Smuzhiyun  * by Narendra K <Narendra_K@dell.com>,
8*4882a593Smuzhiyun  * Jordan Hargrave <Jordan_Hargrave@dell.com>
9*4882a593Smuzhiyun  *
10*4882a593Smuzhiyun  * PCI Firmware Specification Revision 3.1 section 4.6.7 (DSM for Naming a
11*4882a593Smuzhiyun  * PCI or PCI Express Device Under Operating Systems) defines an instance
12*4882a593Smuzhiyun  * number and string name. This code retrieves them and exports them to sysfs.
13*4882a593Smuzhiyun  * If the system firmware does not provide the ACPI _DSM (Device Specific
14*4882a593Smuzhiyun  * Method), then the SMBIOS type 41 instance number and string is exported to
15*4882a593Smuzhiyun  * sysfs.
16*4882a593Smuzhiyun  *
17*4882a593Smuzhiyun  * SMBIOS defines type 41 for onboard pci devices. This code retrieves
18*4882a593Smuzhiyun  * the instance number and string from the type 41 record and exports
19*4882a593Smuzhiyun  * it to sysfs.
20*4882a593Smuzhiyun  *
21*4882a593Smuzhiyun  * Please see https://linux.dell.com/files/biosdevname/ for more
22*4882a593Smuzhiyun  * information.
23*4882a593Smuzhiyun  */
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun #include <linux/dmi.h>
26*4882a593Smuzhiyun #include <linux/sysfs.h>
27*4882a593Smuzhiyun #include <linux/pci.h>
28*4882a593Smuzhiyun #include <linux/pci_ids.h>
29*4882a593Smuzhiyun #include <linux/module.h>
30*4882a593Smuzhiyun #include <linux/device.h>
31*4882a593Smuzhiyun #include <linux/nls.h>
32*4882a593Smuzhiyun #include <linux/acpi.h>
33*4882a593Smuzhiyun #include <linux/pci-acpi.h>
34*4882a593Smuzhiyun #include "pci.h"
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun #ifdef CONFIG_DMI
37*4882a593Smuzhiyun enum smbios_attr_enum {
38*4882a593Smuzhiyun 	SMBIOS_ATTR_NONE = 0,
39*4882a593Smuzhiyun 	SMBIOS_ATTR_LABEL_SHOW,
40*4882a593Smuzhiyun 	SMBIOS_ATTR_INSTANCE_SHOW,
41*4882a593Smuzhiyun };
42*4882a593Smuzhiyun 
find_smbios_instance_string(struct pci_dev * pdev,char * buf,enum smbios_attr_enum attribute)43*4882a593Smuzhiyun static size_t find_smbios_instance_string(struct pci_dev *pdev, char *buf,
44*4882a593Smuzhiyun 					  enum smbios_attr_enum attribute)
45*4882a593Smuzhiyun {
46*4882a593Smuzhiyun 	const struct dmi_device *dmi;
47*4882a593Smuzhiyun 	struct dmi_dev_onboard *donboard;
48*4882a593Smuzhiyun 	int domain_nr;
49*4882a593Smuzhiyun 	int bus;
50*4882a593Smuzhiyun 	int devfn;
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 	domain_nr = pci_domain_nr(pdev->bus);
53*4882a593Smuzhiyun 	bus = pdev->bus->number;
54*4882a593Smuzhiyun 	devfn = pdev->devfn;
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun 	dmi = NULL;
57*4882a593Smuzhiyun 	while ((dmi = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD,
58*4882a593Smuzhiyun 				      NULL, dmi)) != NULL) {
59*4882a593Smuzhiyun 		donboard = dmi->device_data;
60*4882a593Smuzhiyun 		if (donboard && donboard->segment == domain_nr &&
61*4882a593Smuzhiyun 				donboard->bus == bus &&
62*4882a593Smuzhiyun 				donboard->devfn == devfn) {
63*4882a593Smuzhiyun 			if (buf) {
64*4882a593Smuzhiyun 				if (attribute == SMBIOS_ATTR_INSTANCE_SHOW)
65*4882a593Smuzhiyun 					return scnprintf(buf, PAGE_SIZE,
66*4882a593Smuzhiyun 							 "%d\n",
67*4882a593Smuzhiyun 							 donboard->instance);
68*4882a593Smuzhiyun 				else if (attribute == SMBIOS_ATTR_LABEL_SHOW)
69*4882a593Smuzhiyun 					return scnprintf(buf, PAGE_SIZE,
70*4882a593Smuzhiyun 							 "%s\n",
71*4882a593Smuzhiyun 							 dmi->name);
72*4882a593Smuzhiyun 			}
73*4882a593Smuzhiyun 			return strlen(dmi->name);
74*4882a593Smuzhiyun 		}
75*4882a593Smuzhiyun 	}
76*4882a593Smuzhiyun 	return 0;
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun 
smbios_instance_string_exist(struct kobject * kobj,struct attribute * attr,int n)79*4882a593Smuzhiyun static umode_t smbios_instance_string_exist(struct kobject *kobj,
80*4882a593Smuzhiyun 					    struct attribute *attr, int n)
81*4882a593Smuzhiyun {
82*4882a593Smuzhiyun 	struct device *dev;
83*4882a593Smuzhiyun 	struct pci_dev *pdev;
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	dev = kobj_to_dev(kobj);
86*4882a593Smuzhiyun 	pdev = to_pci_dev(dev);
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	return find_smbios_instance_string(pdev, NULL, SMBIOS_ATTR_NONE) ?
89*4882a593Smuzhiyun 					   S_IRUGO : 0;
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun 
smbioslabel_show(struct device * dev,struct device_attribute * attr,char * buf)92*4882a593Smuzhiyun static ssize_t smbioslabel_show(struct device *dev,
93*4882a593Smuzhiyun 				struct device_attribute *attr, char *buf)
94*4882a593Smuzhiyun {
95*4882a593Smuzhiyun 	struct pci_dev *pdev;
96*4882a593Smuzhiyun 	pdev = to_pci_dev(dev);
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 	return find_smbios_instance_string(pdev, buf,
99*4882a593Smuzhiyun 					   SMBIOS_ATTR_LABEL_SHOW);
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun 
smbiosinstance_show(struct device * dev,struct device_attribute * attr,char * buf)102*4882a593Smuzhiyun static ssize_t smbiosinstance_show(struct device *dev,
103*4882a593Smuzhiyun 				   struct device_attribute *attr, char *buf)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun 	struct pci_dev *pdev;
106*4882a593Smuzhiyun 	pdev = to_pci_dev(dev);
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	return find_smbios_instance_string(pdev, buf,
109*4882a593Smuzhiyun 					   SMBIOS_ATTR_INSTANCE_SHOW);
110*4882a593Smuzhiyun }
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun static struct device_attribute smbios_attr_label = {
113*4882a593Smuzhiyun 	.attr = {.name = "label", .mode = 0444},
114*4882a593Smuzhiyun 	.show = smbioslabel_show,
115*4882a593Smuzhiyun };
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun static struct device_attribute smbios_attr_instance = {
118*4882a593Smuzhiyun 	.attr = {.name = "index", .mode = 0444},
119*4882a593Smuzhiyun 	.show = smbiosinstance_show,
120*4882a593Smuzhiyun };
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun static struct attribute *smbios_attributes[] = {
123*4882a593Smuzhiyun 	&smbios_attr_label.attr,
124*4882a593Smuzhiyun 	&smbios_attr_instance.attr,
125*4882a593Smuzhiyun 	NULL,
126*4882a593Smuzhiyun };
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun static const struct attribute_group smbios_attr_group = {
129*4882a593Smuzhiyun 	.attrs = smbios_attributes,
130*4882a593Smuzhiyun 	.is_visible = smbios_instance_string_exist,
131*4882a593Smuzhiyun };
132*4882a593Smuzhiyun 
pci_create_smbiosname_file(struct pci_dev * pdev)133*4882a593Smuzhiyun static int pci_create_smbiosname_file(struct pci_dev *pdev)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun 	return sysfs_create_group(&pdev->dev.kobj, &smbios_attr_group);
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun 
pci_remove_smbiosname_file(struct pci_dev * pdev)138*4882a593Smuzhiyun static void pci_remove_smbiosname_file(struct pci_dev *pdev)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun 	sysfs_remove_group(&pdev->dev.kobj, &smbios_attr_group);
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun #else
pci_create_smbiosname_file(struct pci_dev * pdev)143*4882a593Smuzhiyun static inline int pci_create_smbiosname_file(struct pci_dev *pdev)
144*4882a593Smuzhiyun {
145*4882a593Smuzhiyun 	return -1;
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun 
pci_remove_smbiosname_file(struct pci_dev * pdev)148*4882a593Smuzhiyun static inline void pci_remove_smbiosname_file(struct pci_dev *pdev)
149*4882a593Smuzhiyun {
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun #endif
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun #ifdef CONFIG_ACPI
154*4882a593Smuzhiyun enum acpi_attr_enum {
155*4882a593Smuzhiyun 	ACPI_ATTR_LABEL_SHOW,
156*4882a593Smuzhiyun 	ACPI_ATTR_INDEX_SHOW,
157*4882a593Smuzhiyun };
158*4882a593Smuzhiyun 
dsm_label_utf16s_to_utf8s(union acpi_object * obj,char * buf)159*4882a593Smuzhiyun static void dsm_label_utf16s_to_utf8s(union acpi_object *obj, char *buf)
160*4882a593Smuzhiyun {
161*4882a593Smuzhiyun 	int len;
162*4882a593Smuzhiyun 	len = utf16s_to_utf8s((const wchar_t *)obj->buffer.pointer,
163*4882a593Smuzhiyun 			      obj->buffer.length,
164*4882a593Smuzhiyun 			      UTF16_LITTLE_ENDIAN,
165*4882a593Smuzhiyun 			      buf, PAGE_SIZE - 1);
166*4882a593Smuzhiyun 	buf[len] = '\n';
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun 
dsm_get_label(struct device * dev,char * buf,enum acpi_attr_enum attr)169*4882a593Smuzhiyun static int dsm_get_label(struct device *dev, char *buf,
170*4882a593Smuzhiyun 			 enum acpi_attr_enum attr)
171*4882a593Smuzhiyun {
172*4882a593Smuzhiyun 	acpi_handle handle;
173*4882a593Smuzhiyun 	union acpi_object *obj, *tmp;
174*4882a593Smuzhiyun 	int len = -1;
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 	handle = ACPI_HANDLE(dev);
177*4882a593Smuzhiyun 	if (!handle)
178*4882a593Smuzhiyun 		return -1;
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, 0x2,
181*4882a593Smuzhiyun 				DSM_PCI_DEVICE_NAME, NULL);
182*4882a593Smuzhiyun 	if (!obj)
183*4882a593Smuzhiyun 		return -1;
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 	tmp = obj->package.elements;
186*4882a593Smuzhiyun 	if (obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 2 &&
187*4882a593Smuzhiyun 	    tmp[0].type == ACPI_TYPE_INTEGER &&
188*4882a593Smuzhiyun 	    (tmp[1].type == ACPI_TYPE_STRING ||
189*4882a593Smuzhiyun 	     tmp[1].type == ACPI_TYPE_BUFFER)) {
190*4882a593Smuzhiyun 		/*
191*4882a593Smuzhiyun 		 * The second string element is optional even when
192*4882a593Smuzhiyun 		 * this _DSM is implemented; when not implemented,
193*4882a593Smuzhiyun 		 * this entry must return a null string.
194*4882a593Smuzhiyun 		 */
195*4882a593Smuzhiyun 		if (attr == ACPI_ATTR_INDEX_SHOW) {
196*4882a593Smuzhiyun 			scnprintf(buf, PAGE_SIZE, "%llu\n", tmp->integer.value);
197*4882a593Smuzhiyun 		} else if (attr == ACPI_ATTR_LABEL_SHOW) {
198*4882a593Smuzhiyun 			if (tmp[1].type == ACPI_TYPE_STRING)
199*4882a593Smuzhiyun 				scnprintf(buf, PAGE_SIZE, "%s\n",
200*4882a593Smuzhiyun 					  tmp[1].string.pointer);
201*4882a593Smuzhiyun 			else if (tmp[1].type == ACPI_TYPE_BUFFER)
202*4882a593Smuzhiyun 				dsm_label_utf16s_to_utf8s(tmp + 1, buf);
203*4882a593Smuzhiyun 		}
204*4882a593Smuzhiyun 		len = strlen(buf) > 0 ? strlen(buf) : -1;
205*4882a593Smuzhiyun 	}
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 	ACPI_FREE(obj);
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	return len;
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun 
device_has_dsm(struct device * dev)212*4882a593Smuzhiyun static bool device_has_dsm(struct device *dev)
213*4882a593Smuzhiyun {
214*4882a593Smuzhiyun 	acpi_handle handle;
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	handle = ACPI_HANDLE(dev);
217*4882a593Smuzhiyun 	if (!handle)
218*4882a593Smuzhiyun 		return false;
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	return !!acpi_check_dsm(handle, &pci_acpi_dsm_guid, 0x2,
221*4882a593Smuzhiyun 				1 << DSM_PCI_DEVICE_NAME);
222*4882a593Smuzhiyun }
223*4882a593Smuzhiyun 
acpi_index_string_exist(struct kobject * kobj,struct attribute * attr,int n)224*4882a593Smuzhiyun static umode_t acpi_index_string_exist(struct kobject *kobj,
225*4882a593Smuzhiyun 				       struct attribute *attr, int n)
226*4882a593Smuzhiyun {
227*4882a593Smuzhiyun 	struct device *dev;
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 	dev = kobj_to_dev(kobj);
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 	if (device_has_dsm(dev))
232*4882a593Smuzhiyun 		return S_IRUGO;
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun 	return 0;
235*4882a593Smuzhiyun }
236*4882a593Smuzhiyun 
acpilabel_show(struct device * dev,struct device_attribute * attr,char * buf)237*4882a593Smuzhiyun static ssize_t acpilabel_show(struct device *dev,
238*4882a593Smuzhiyun 			      struct device_attribute *attr, char *buf)
239*4882a593Smuzhiyun {
240*4882a593Smuzhiyun 	return dsm_get_label(dev, buf, ACPI_ATTR_LABEL_SHOW);
241*4882a593Smuzhiyun }
242*4882a593Smuzhiyun 
acpiindex_show(struct device * dev,struct device_attribute * attr,char * buf)243*4882a593Smuzhiyun static ssize_t acpiindex_show(struct device *dev,
244*4882a593Smuzhiyun 			      struct device_attribute *attr, char *buf)
245*4882a593Smuzhiyun {
246*4882a593Smuzhiyun 	return dsm_get_label(dev, buf, ACPI_ATTR_INDEX_SHOW);
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun static struct device_attribute acpi_attr_label = {
250*4882a593Smuzhiyun 	.attr = {.name = "label", .mode = 0444},
251*4882a593Smuzhiyun 	.show = acpilabel_show,
252*4882a593Smuzhiyun };
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun static struct device_attribute acpi_attr_index = {
255*4882a593Smuzhiyun 	.attr = {.name = "acpi_index", .mode = 0444},
256*4882a593Smuzhiyun 	.show = acpiindex_show,
257*4882a593Smuzhiyun };
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun static struct attribute *acpi_attributes[] = {
260*4882a593Smuzhiyun 	&acpi_attr_label.attr,
261*4882a593Smuzhiyun 	&acpi_attr_index.attr,
262*4882a593Smuzhiyun 	NULL,
263*4882a593Smuzhiyun };
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun static const struct attribute_group acpi_attr_group = {
266*4882a593Smuzhiyun 	.attrs = acpi_attributes,
267*4882a593Smuzhiyun 	.is_visible = acpi_index_string_exist,
268*4882a593Smuzhiyun };
269*4882a593Smuzhiyun 
pci_create_acpi_index_label_files(struct pci_dev * pdev)270*4882a593Smuzhiyun static int pci_create_acpi_index_label_files(struct pci_dev *pdev)
271*4882a593Smuzhiyun {
272*4882a593Smuzhiyun 	return sysfs_create_group(&pdev->dev.kobj, &acpi_attr_group);
273*4882a593Smuzhiyun }
274*4882a593Smuzhiyun 
pci_remove_acpi_index_label_files(struct pci_dev * pdev)275*4882a593Smuzhiyun static int pci_remove_acpi_index_label_files(struct pci_dev *pdev)
276*4882a593Smuzhiyun {
277*4882a593Smuzhiyun 	sysfs_remove_group(&pdev->dev.kobj, &acpi_attr_group);
278*4882a593Smuzhiyun 	return 0;
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun #else
pci_create_acpi_index_label_files(struct pci_dev * pdev)281*4882a593Smuzhiyun static inline int pci_create_acpi_index_label_files(struct pci_dev *pdev)
282*4882a593Smuzhiyun {
283*4882a593Smuzhiyun 	return -1;
284*4882a593Smuzhiyun }
285*4882a593Smuzhiyun 
pci_remove_acpi_index_label_files(struct pci_dev * pdev)286*4882a593Smuzhiyun static inline int pci_remove_acpi_index_label_files(struct pci_dev *pdev)
287*4882a593Smuzhiyun {
288*4882a593Smuzhiyun 	return -1;
289*4882a593Smuzhiyun }
290*4882a593Smuzhiyun 
device_has_dsm(struct device * dev)291*4882a593Smuzhiyun static inline bool device_has_dsm(struct device *dev)
292*4882a593Smuzhiyun {
293*4882a593Smuzhiyun 	return false;
294*4882a593Smuzhiyun }
295*4882a593Smuzhiyun #endif
296*4882a593Smuzhiyun 
pci_create_firmware_label_files(struct pci_dev * pdev)297*4882a593Smuzhiyun void pci_create_firmware_label_files(struct pci_dev *pdev)
298*4882a593Smuzhiyun {
299*4882a593Smuzhiyun 	if (device_has_dsm(&pdev->dev))
300*4882a593Smuzhiyun 		pci_create_acpi_index_label_files(pdev);
301*4882a593Smuzhiyun 	else
302*4882a593Smuzhiyun 		pci_create_smbiosname_file(pdev);
303*4882a593Smuzhiyun }
304*4882a593Smuzhiyun 
pci_remove_firmware_label_files(struct pci_dev * pdev)305*4882a593Smuzhiyun void pci_remove_firmware_label_files(struct pci_dev *pdev)
306*4882a593Smuzhiyun {
307*4882a593Smuzhiyun 	if (device_has_dsm(&pdev->dev))
308*4882a593Smuzhiyun 		pci_remove_acpi_index_label_files(pdev);
309*4882a593Smuzhiyun 	else
310*4882a593Smuzhiyun 		pci_remove_smbiosname_file(pdev);
311*4882a593Smuzhiyun }
312