LCOV - code coverage report
Current view: top level - drivers/pci - pci-label.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 13 44 29.5 %
Date: 2022-03-28 16:04:14 Functions: 2 8 25.0 %
Branches: 2 8 25.0 %

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

Generated by: LCOV version 1.14