LCOV - code coverage report
Current view: top level - drivers/firmware/efi - esrt.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 14 158 8.9 %
Date: 2022-04-01 14:35:51 Functions: 2 18 11.1 %
Branches: 2 70 2.9 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0+
       2                 :            : /*
       3                 :            :  * esrt.c
       4                 :            :  *
       5                 :            :  * This module exports EFI System Resource Table (ESRT) entries into userspace
       6                 :            :  * through the sysfs file system. The ESRT provides a read-only catalog of
       7                 :            :  * system components for which the system accepts firmware upgrades via UEFI's
       8                 :            :  * "Capsule Update" feature. This module allows userland utilities to evaluate
       9                 :            :  * what firmware updates can be applied to this system, and potentially arrange
      10                 :            :  * for those updates to occur.
      11                 :            :  *
      12                 :            :  * Data is currently found below /sys/firmware/efi/esrt/...
      13                 :            :  */
      14                 :            : #define pr_fmt(fmt) "esrt: " fmt
      15                 :            : 
      16                 :            : #include <linux/capability.h>
      17                 :            : #include <linux/device.h>
      18                 :            : #include <linux/efi.h>
      19                 :            : #include <linux/init.h>
      20                 :            : #include <linux/io.h>
      21                 :            : #include <linux/kernel.h>
      22                 :            : #include <linux/kobject.h>
      23                 :            : #include <linux/list.h>
      24                 :            : #include <linux/memblock.h>
      25                 :            : #include <linux/slab.h>
      26                 :            : #include <linux/types.h>
      27                 :            : 
      28                 :            : #include <asm/io.h>
      29                 :            : #include <asm/early_ioremap.h>
      30                 :            : 
      31                 :            : struct efi_system_resource_entry_v1 {
      32                 :            :         efi_guid_t      fw_class;
      33                 :            :         u32             fw_type;
      34                 :            :         u32             fw_version;
      35                 :            :         u32             lowest_supported_fw_version;
      36                 :            :         u32             capsule_flags;
      37                 :            :         u32             last_attempt_version;
      38                 :            :         u32             last_attempt_status;
      39                 :            : };
      40                 :            : 
      41                 :            : /*
      42                 :            :  * _count and _version are what they seem like.  _max is actually just
      43                 :            :  * accounting info for the firmware when creating the table; it should never
      44                 :            :  * have been exposed to us.  To wit, the spec says:
      45                 :            :  * The maximum number of resource array entries that can be within the
      46                 :            :  * table without reallocating the table, must not be zero.
      47                 :            :  * Since there's no guidance about what that means in terms of memory layout,
      48                 :            :  * it means nothing to us.
      49                 :            :  */
      50                 :            : struct efi_system_resource_table {
      51                 :            :         u32     fw_resource_count;
      52                 :            :         u32     fw_resource_count_max;
      53                 :            :         u64     fw_resource_version;
      54                 :            :         u8      entries[];
      55                 :            : };
      56                 :            : 
      57                 :            : static phys_addr_t esrt_data;
      58                 :            : static size_t esrt_data_size;
      59                 :            : 
      60                 :            : static struct efi_system_resource_table *esrt;
      61                 :            : 
      62                 :            : struct esre_entry {
      63                 :            :         union {
      64                 :            :                 struct efi_system_resource_entry_v1 *esre1;
      65                 :            :         } esre;
      66                 :            : 
      67                 :            :         struct kobject kobj;
      68                 :            :         struct list_head list;
      69                 :            : };
      70                 :            : 
      71                 :            : /* global list of esre_entry. */
      72                 :            : static LIST_HEAD(entry_list);
      73                 :            : 
      74                 :            : /* entry attribute */
      75                 :            : struct esre_attribute {
      76                 :            :         struct attribute attr;
      77                 :            :         ssize_t (*show)(struct esre_entry *entry, char *buf);
      78                 :            :         ssize_t (*store)(struct esre_entry *entry,
      79                 :            :                          const char *buf, size_t count);
      80                 :            : };
      81                 :            : 
      82                 :          0 : static struct esre_entry *to_entry(struct kobject *kobj)
      83                 :            : {
      84                 :          0 :         return container_of(kobj, struct esre_entry, kobj);
      85                 :            : }
      86                 :            : 
      87                 :          0 : static struct esre_attribute *to_attr(struct attribute *attr)
      88                 :            : {
      89                 :          0 :         return container_of(attr, struct esre_attribute, attr);
      90                 :            : }
      91                 :            : 
      92                 :          0 : static ssize_t esre_attr_show(struct kobject *kobj,
      93                 :            :                               struct attribute *_attr, char *buf)
      94                 :            : {
      95                 :          0 :         struct esre_entry *entry = to_entry(kobj);
      96                 :          0 :         struct esre_attribute *attr = to_attr(_attr);
      97                 :            : 
      98                 :            :         /* Don't tell normal users what firmware versions we've got... */
      99         [ #  # ]:          0 :         if (!capable(CAP_SYS_ADMIN))
     100                 :            :                 return -EACCES;
     101                 :            : 
     102                 :          0 :         return attr->show(entry, buf);
     103                 :            : }
     104                 :            : 
     105                 :            : static const struct sysfs_ops esre_attr_ops = {
     106                 :            :         .show = esre_attr_show,
     107                 :            : };
     108                 :            : 
     109                 :            : /* Generic ESRT Entry ("ESRE") support. */
     110                 :          0 : static ssize_t fw_class_show(struct esre_entry *entry, char *buf)
     111                 :            : {
     112                 :          0 :         char *str = buf;
     113                 :            : 
     114                 :          0 :         efi_guid_to_str(&entry->esre.esre1->fw_class, str);
     115                 :          0 :         str += strlen(str);
     116                 :          0 :         str += sprintf(str, "\n");
     117                 :            : 
     118                 :          0 :         return str - buf;
     119                 :            : }
     120                 :            : 
     121                 :            : static struct esre_attribute esre_fw_class = __ATTR_RO_MODE(fw_class, 0400);
     122                 :            : 
     123                 :            : #define esre_attr_decl(name, size, fmt) \
     124                 :            : static ssize_t name##_show(struct esre_entry *entry, char *buf) \
     125                 :            : { \
     126                 :            :         return sprintf(buf, fmt "\n", \
     127                 :            :                        le##size##_to_cpu(entry->esre.esre1->name)); \
     128                 :            : } \
     129                 :            : \
     130                 :            : static struct esre_attribute esre_##name = __ATTR_RO_MODE(name, 0400)
     131                 :            : 
     132                 :          0 : esre_attr_decl(fw_type, 32, "%u");
     133                 :          0 : esre_attr_decl(fw_version, 32, "%u");
     134                 :          0 : esre_attr_decl(lowest_supported_fw_version, 32, "%u");
     135                 :          0 : esre_attr_decl(capsule_flags, 32, "0x%x");
     136                 :          0 : esre_attr_decl(last_attempt_version, 32, "%u");
     137                 :          0 : esre_attr_decl(last_attempt_status, 32, "%u");
     138                 :            : 
     139                 :            : static struct attribute *esre1_attrs[] = {
     140                 :            :         &esre_fw_class.attr,
     141                 :            :         &esre_fw_type.attr,
     142                 :            :         &esre_fw_version.attr,
     143                 :            :         &esre_lowest_supported_fw_version.attr,
     144                 :            :         &esre_capsule_flags.attr,
     145                 :            :         &esre_last_attempt_version.attr,
     146                 :            :         &esre_last_attempt_status.attr,
     147                 :            :         NULL
     148                 :            : };
     149                 :          0 : static void esre_release(struct kobject *kobj)
     150                 :            : {
     151                 :          0 :         struct esre_entry *entry = to_entry(kobj);
     152                 :            : 
     153                 :          0 :         list_del(&entry->list);
     154                 :          0 :         kfree(entry);
     155                 :          0 : }
     156                 :            : 
     157                 :            : static struct kobj_type esre1_ktype = {
     158                 :            :         .release = esre_release,
     159                 :            :         .sysfs_ops = &esre_attr_ops,
     160                 :            :         .default_attrs = esre1_attrs,
     161                 :            : };
     162                 :            : 
     163                 :            : 
     164                 :            : static struct kobject *esrt_kobj;
     165                 :            : static struct kset *esrt_kset;
     166                 :            : 
     167                 :          0 : static int esre_create_sysfs_entry(void *esre, int entry_num)
     168                 :            : {
     169                 :          0 :         struct esre_entry *entry;
     170                 :            : 
     171                 :          0 :         entry = kzalloc(sizeof(*entry), GFP_KERNEL);
     172         [ #  # ]:          0 :         if (!entry)
     173                 :            :                 return -ENOMEM;
     174                 :            : 
     175                 :          0 :         entry->kobj.kset = esrt_kset;
     176                 :            : 
     177         [ #  # ]:          0 :         if (esrt->fw_resource_version == 1) {
     178                 :          0 :                 int rc = 0;
     179                 :            : 
     180                 :          0 :                 entry->esre.esre1 = esre;
     181                 :          0 :                 rc = kobject_init_and_add(&entry->kobj, &esre1_ktype, NULL,
     182                 :            :                                           "entry%d", entry_num);
     183         [ #  # ]:          0 :                 if (rc) {
     184                 :          0 :                         kfree(entry);
     185                 :          0 :                         return rc;
     186                 :            :                 }
     187                 :            :         }
     188                 :            : 
     189                 :          0 :         list_add_tail(&entry->list, &entry_list);
     190                 :          0 :         return 0;
     191                 :            : }
     192                 :            : 
     193                 :            : /* support for displaying ESRT fields at the top level */
     194                 :            : #define esrt_attr_decl(name, size, fmt) \
     195                 :            : static ssize_t name##_show(struct kobject *kobj, \
     196                 :            :                                   struct kobj_attribute *attr, char *buf)\
     197                 :            : { \
     198                 :            :         return sprintf(buf, fmt "\n", le##size##_to_cpu(esrt->name)); \
     199                 :            : } \
     200                 :            : \
     201                 :            : static struct kobj_attribute esrt_##name = __ATTR_RO_MODE(name, 0400)
     202                 :            : 
     203                 :          0 : esrt_attr_decl(fw_resource_count, 32, "%u");
     204                 :          0 : esrt_attr_decl(fw_resource_count_max, 32, "%u");
     205                 :          0 : esrt_attr_decl(fw_resource_version, 64, "%llu");
     206                 :            : 
     207                 :            : static struct attribute *esrt_attrs[] = {
     208                 :            :         &esrt_fw_resource_count.attr,
     209                 :            :         &esrt_fw_resource_count_max.attr,
     210                 :            :         &esrt_fw_resource_version.attr,
     211                 :            :         NULL,
     212                 :            : };
     213                 :            : 
     214                 :          0 : static inline int esrt_table_exists(void)
     215                 :            : {
     216   [ #  #  #  #  :          0 :         if (!efi_enabled(EFI_CONFIG_TABLES))
                   #  # ]
     217                 :            :                 return 0;
     218   [ #  #  #  #  :          0 :         if (efi.esrt == EFI_INVALID_TABLE_ADDR)
                   #  # ]
     219                 :            :                 return 0;
     220                 :            :         return 1;
     221                 :            : }
     222                 :            : 
     223                 :          0 : static umode_t esrt_attr_is_visible(struct kobject *kobj,
     224                 :            :                                     struct attribute *attr, int n)
     225                 :            : {
     226                 :          0 :         if (!esrt_table_exists())
     227                 :            :                 return 0;
     228                 :          0 :         return attr->mode;
     229                 :            : }
     230                 :            : 
     231                 :            : static const struct attribute_group esrt_attr_group = {
     232                 :            :         .attrs = esrt_attrs,
     233                 :            :         .is_visible = esrt_attr_is_visible,
     234                 :            : };
     235                 :            : 
     236                 :            : /*
     237                 :            :  * remap the table, validate it, mark it reserved and unmap it.
     238                 :            :  */
     239                 :         21 : void __init efi_esrt_init(void)
     240                 :            : {
     241                 :         21 :         void *va;
     242                 :         21 :         struct efi_system_resource_table tmpesrt;
     243                 :         21 :         struct efi_system_resource_entry_v1 *v1_entries;
     244                 :         21 :         size_t size, max, entry_size, entries_size;
     245                 :         21 :         efi_memory_desc_t md;
     246                 :         21 :         int rc;
     247                 :         21 :         phys_addr_t end;
     248                 :            : 
     249         [ -  + ]:         21 :         if (!efi_enabled(EFI_MEMMAP))
     250                 :         21 :                 return;
     251                 :            : 
     252                 :          0 :         pr_debug("esrt-init: loading.\n");
     253                 :          0 :         if (!esrt_table_exists())
     254                 :            :                 return;
     255                 :            : 
     256                 :          0 :         rc = efi_mem_desc_lookup(efi.esrt, &md);
     257         [ #  # ]:          0 :         if (rc < 0 ||
     258         [ #  # ]:          0 :             (!(md.attribute & EFI_MEMORY_RUNTIME) &&
     259   [ #  #  #  # ]:          0 :              md.type != EFI_BOOT_SERVICES_DATA &&
     260                 :            :              md.type != EFI_RUNTIME_SERVICES_DATA)) {
     261                 :          0 :                 pr_warn("ESRT header is not in the memory map.\n");
     262                 :          0 :                 return;
     263                 :            :         }
     264                 :            : 
     265                 :          0 :         max = efi_mem_desc_end(&md);
     266         [ #  # ]:          0 :         if (max < efi.esrt) {
     267                 :          0 :                 pr_err("EFI memory descriptor is invalid. (esrt: %p max: %p)\n",
     268                 :            :                        (void *)efi.esrt, (void *)max);
     269                 :          0 :                 return;
     270                 :            :         }
     271                 :            : 
     272                 :          0 :         size = sizeof(*esrt);
     273                 :          0 :         max -= efi.esrt;
     274                 :            : 
     275         [ #  # ]:          0 :         if (max < size) {
     276                 :          0 :                 pr_err("ESRT header doesn't fit on single memory map entry. (size: %zu max: %zu)\n",
     277                 :            :                        size, max);
     278                 :          0 :                 return;
     279                 :            :         }
     280                 :            : 
     281                 :          0 :         va = early_memremap(efi.esrt, size);
     282         [ #  # ]:          0 :         if (!va) {
     283                 :          0 :                 pr_err("early_memremap(%p, %zu) failed.\n", (void *)efi.esrt,
     284                 :            :                        size);
     285                 :          0 :                 return;
     286                 :            :         }
     287                 :            : 
     288                 :          0 :         memcpy(&tmpesrt, va, sizeof(tmpesrt));
     289                 :          0 :         early_memunmap(va, size);
     290                 :            : 
     291         [ #  # ]:          0 :         if (tmpesrt.fw_resource_version == 1) {
     292                 :          0 :                 entry_size = sizeof (*v1_entries);
     293                 :            :         } else {
     294                 :          0 :                 pr_err("Unsupported ESRT version %lld.\n",
     295                 :            :                        tmpesrt.fw_resource_version);
     296                 :          0 :                 return;
     297                 :            :         }
     298                 :            : 
     299   [ #  #  #  # ]:          0 :         if (tmpesrt.fw_resource_count > 0 && max - size < entry_size) {
     300                 :          0 :                 pr_err("ESRT memory map entry can only hold the header. (max: %zu size: %zu)\n",
     301                 :            :                        max - size, entry_size);
     302                 :          0 :                 return;
     303                 :            :         }
     304                 :            : 
     305                 :            :         /*
     306                 :            :          * The format doesn't really give us any boundary to test here,
     307                 :            :          * so I'm making up 128 as the max number of individually updatable
     308                 :            :          * components we support.
     309                 :            :          * 128 should be pretty excessive, but there's still some chance
     310                 :            :          * somebody will do that someday and we'll need to raise this.
     311                 :            :          */
     312         [ #  # ]:          0 :         if (tmpesrt.fw_resource_count > 128) {
     313                 :          0 :                 pr_err("ESRT says fw_resource_count has very large value %d.\n",
     314                 :            :                        tmpesrt.fw_resource_count);
     315                 :          0 :                 return;
     316                 :            :         }
     317                 :            : 
     318                 :            :         /*
     319                 :            :          * We know it can't be larger than N * sizeof() here, and N is limited
     320                 :            :          * by the previous test to a small number, so there's no overflow.
     321                 :            :          */
     322                 :          0 :         entries_size = tmpesrt.fw_resource_count * entry_size;
     323         [ #  # ]:          0 :         if (max < size + entries_size) {
     324                 :          0 :                 pr_err("ESRT does not fit on single memory map entry (size: %zu max: %zu)\n",
     325                 :            :                        size, max);
     326                 :          0 :                 return;
     327                 :            :         }
     328                 :            : 
     329                 :          0 :         size += entries_size;
     330                 :            : 
     331                 :          0 :         esrt_data = (phys_addr_t)efi.esrt;
     332                 :          0 :         esrt_data_size = size;
     333                 :            : 
     334                 :          0 :         end = esrt_data + size;
     335                 :          0 :         pr_info("Reserving ESRT space from %pa to %pa.\n", &esrt_data, &end);
     336         [ #  # ]:          0 :         if (md.type == EFI_BOOT_SERVICES_DATA)
     337                 :          0 :                 efi_mem_reserve(esrt_data, esrt_data_size);
     338                 :            : 
     339                 :          0 :         pr_debug("esrt-init: loaded.\n");
     340                 :            : }
     341                 :            : 
     342                 :          0 : static int __init register_entries(void)
     343                 :            : {
     344                 :          0 :         struct efi_system_resource_entry_v1 *v1_entries = (void *)esrt->entries;
     345                 :          0 :         int i, rc;
     346                 :            : 
     347                 :          0 :         if (!esrt_table_exists())
     348                 :            :                 return 0;
     349                 :            : 
     350         [ #  # ]:          0 :         for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
     351                 :          0 :                 void *esre = NULL;
     352         [ #  # ]:          0 :                 if (esrt->fw_resource_version == 1) {
     353                 :          0 :                         esre = &v1_entries[i];
     354                 :            :                 } else {
     355                 :          0 :                         pr_err("Unsupported ESRT version %lld.\n",
     356                 :            :                                esrt->fw_resource_version);
     357                 :          0 :                         return -EINVAL;
     358                 :            :                 }
     359                 :            : 
     360                 :          0 :                 rc = esre_create_sysfs_entry(esre, i);
     361         [ #  # ]:          0 :                 if (rc < 0) {
     362                 :          0 :                         pr_err("ESRT entry creation failed with error %d.\n",
     363                 :            :                                rc);
     364                 :          0 :                         return rc;
     365                 :            :                 }
     366                 :            :         }
     367                 :            :         return 0;
     368                 :            : }
     369                 :            : 
     370                 :          0 : static void cleanup_entry_list(void)
     371                 :            : {
     372                 :          0 :         struct esre_entry *entry, *next;
     373                 :            : 
     374         [ #  # ]:          0 :         list_for_each_entry_safe(entry, next, &entry_list, list) {
     375                 :          0 :                 kobject_put(&entry->kobj);
     376                 :            :         }
     377                 :          0 : }
     378                 :            : 
     379                 :         21 : static int __init esrt_sysfs_init(void)
     380                 :            : {
     381                 :         21 :         int error;
     382                 :            : 
     383                 :         21 :         pr_debug("esrt-sysfs: loading.\n");
     384   [ -  +  -  - ]:         21 :         if (!esrt_data || !esrt_data_size)
     385                 :            :                 return -ENOSYS;
     386                 :            : 
     387                 :          0 :         esrt = memremap(esrt_data, esrt_data_size, MEMREMAP_WB);
     388         [ #  # ]:          0 :         if (!esrt) {
     389                 :          0 :                 pr_err("memremap(%pa, %zu) failed.\n", &esrt_data,
     390                 :            :                        esrt_data_size);
     391                 :          0 :                 return -ENOMEM;
     392                 :            :         }
     393                 :            : 
     394                 :          0 :         esrt_kobj = kobject_create_and_add("esrt", efi_kobj);
     395         [ #  # ]:          0 :         if (!esrt_kobj) {
     396                 :          0 :                 pr_err("Firmware table registration failed.\n");
     397                 :          0 :                 error = -ENOMEM;
     398                 :          0 :                 goto err;
     399                 :            :         }
     400                 :            : 
     401                 :          0 :         error = sysfs_create_group(esrt_kobj, &esrt_attr_group);
     402         [ #  # ]:          0 :         if (error) {
     403                 :          0 :                 pr_err("Sysfs attribute export failed with error %d.\n",
     404                 :            :                        error);
     405                 :          0 :                 goto err_remove_esrt;
     406                 :            :         }
     407                 :            : 
     408                 :          0 :         esrt_kset = kset_create_and_add("entries", NULL, esrt_kobj);
     409         [ #  # ]:          0 :         if (!esrt_kset) {
     410                 :          0 :                 pr_err("kset creation failed.\n");
     411                 :          0 :                 error = -ENOMEM;
     412                 :          0 :                 goto err_remove_group;
     413                 :            :         }
     414                 :            : 
     415                 :          0 :         error = register_entries();
     416         [ #  # ]:          0 :         if (error)
     417                 :          0 :                 goto err_cleanup_list;
     418                 :            : 
     419                 :            :         pr_debug("esrt-sysfs: loaded.\n");
     420                 :            : 
     421                 :            :         return 0;
     422                 :            : err_cleanup_list:
     423                 :          0 :         cleanup_entry_list();
     424                 :          0 :         kset_unregister(esrt_kset);
     425                 :          0 : err_remove_group:
     426                 :          0 :         sysfs_remove_group(esrt_kobj, &esrt_attr_group);
     427                 :          0 : err_remove_esrt:
     428                 :          0 :         kobject_put(esrt_kobj);
     429                 :          0 : err:
     430                 :          0 :         memunmap(esrt);
     431                 :          0 :         esrt = NULL;
     432                 :          0 :         return error;
     433                 :            : }
     434                 :            : device_initcall(esrt_sysfs_init);
     435                 :            : 
     436                 :            : /*
     437                 :            : MODULE_AUTHOR("Peter Jones <pjones@redhat.com>");
     438                 :            : MODULE_DESCRIPTION("EFI System Resource Table support");
     439                 :            : MODULE_LICENSE("GPL");
     440                 :            : */

Generated by: LCOV version 1.14