123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * mokvar-table.c
- *
- * Copyright (c) 2020 Red Hat
- * Author: Lenny Szubowicz <[email protected]>
- *
- * This module contains the kernel support for the Linux EFI Machine
- * Owner Key (MOK) variable configuration table, which is identified by
- * the LINUX_EFI_MOK_VARIABLE_TABLE_GUID.
- *
- * This EFI configuration table provides a more robust alternative to
- * EFI volatile variables by which an EFI boot loader can pass the
- * contents of the Machine Owner Key (MOK) certificate stores to the
- * kernel during boot. If both the EFI MOK config table and corresponding
- * EFI MOK variables are present, the table should be considered as
- * more authoritative.
- *
- * This module includes code that validates and maps the EFI MOK table,
- * if it's presence was detected very early in boot.
- *
- * Kernel interface routines are provided to walk through all the
- * entries in the MOK config table or to search for a specific named
- * entry.
- *
- * The contents of the individual named MOK config table entries are
- * made available to user space via read-only sysfs binary files under:
- *
- * /sys/firmware/efi/mok-variables/
- *
- */
- #define pr_fmt(fmt) "mokvar: " fmt
- #include <linux/capability.h>
- #include <linux/efi.h>
- #include <linux/init.h>
- #include <linux/io.h>
- #include <linux/kernel.h>
- #include <linux/kobject.h>
- #include <linux/list.h>
- #include <linux/slab.h>
- #include <asm/early_ioremap.h>
- /*
- * The LINUX_EFI_MOK_VARIABLE_TABLE_GUID config table is a packed
- * sequence of struct efi_mokvar_table_entry, one for each named
- * MOK variable. The sequence is terminated by an entry with a
- * completely NULL name and 0 data size.
- *
- * efi_mokvar_table_size is set to the computed size of the
- * MOK config table by efi_mokvar_table_init(). This will be
- * non-zero if and only if the table if present and has been
- * validated by efi_mokvar_table_init().
- */
- static size_t efi_mokvar_table_size;
- /*
- * efi_mokvar_table_va is the kernel virtual address at which the
- * EFI MOK config table has been mapped by efi_mokvar_sysfs_init().
- */
- static struct efi_mokvar_table_entry *efi_mokvar_table_va;
- /*
- * Each /sys/firmware/efi/mok-variables/ sysfs file is represented by
- * an instance of struct efi_mokvar_sysfs_attr on efi_mokvar_sysfs_list.
- * bin_attr.private points to the associated EFI MOK config table entry.
- *
- * This list is created during boot and then remains unchanged.
- * So no synchronization is currently required to walk the list.
- */
- struct efi_mokvar_sysfs_attr {
- struct bin_attribute bin_attr;
- struct list_head node;
- };
- static LIST_HEAD(efi_mokvar_sysfs_list);
- static struct kobject *mokvar_kobj;
- /*
- * efi_mokvar_table_init() - Early boot validation of EFI MOK config table
- *
- * If present, validate and compute the size of the EFI MOK variable
- * configuration table. This table may be provided by an EFI boot loader
- * as an alternative to ordinary EFI variables, due to platform-dependent
- * limitations. The memory occupied by this table is marked as reserved.
- *
- * This routine must be called before efi_free_boot_services() in order
- * to guarantee that it can mark the table as reserved.
- *
- * Implicit inputs:
- * efi.mokvar_table: Physical address of EFI MOK variable config table
- * or special value that indicates no such table.
- *
- * Implicit outputs:
- * efi_mokvar_table_size: Computed size of EFI MOK variable config table.
- * The table is considered present and valid if this
- * is non-zero.
- */
- void __init efi_mokvar_table_init(void)
- {
- efi_memory_desc_t md;
- void *va = NULL;
- unsigned long cur_offset = 0;
- unsigned long offset_limit;
- unsigned long map_size = 0;
- unsigned long map_size_needed = 0;
- unsigned long size;
- struct efi_mokvar_table_entry *mokvar_entry;
- int err;
- if (!efi_enabled(EFI_MEMMAP))
- return;
- if (efi.mokvar_table == EFI_INVALID_TABLE_ADDR)
- return;
- /*
- * The EFI MOK config table must fit within a single EFI memory
- * descriptor range.
- */
- err = efi_mem_desc_lookup(efi.mokvar_table, &md);
- if (err) {
- pr_warn("EFI MOKvar config table is not within the EFI memory map\n");
- return;
- }
- offset_limit = efi_mem_desc_end(&md) - efi.mokvar_table;
- /*
- * Validate the MOK config table. Since there is no table header
- * from which we could get the total size of the MOK config table,
- * we compute the total size as we validate each variably sized
- * entry, remapping as necessary.
- */
- err = -EINVAL;
- while (cur_offset + sizeof(*mokvar_entry) <= offset_limit) {
- mokvar_entry = va + cur_offset;
- map_size_needed = cur_offset + sizeof(*mokvar_entry);
- if (map_size_needed > map_size) {
- if (va)
- early_memunmap(va, map_size);
- /*
- * Map a little more than the fixed size entry
- * header, anticipating some data. It's safe to
- * do so as long as we stay within current memory
- * descriptor.
- */
- map_size = min(map_size_needed + 2*EFI_PAGE_SIZE,
- offset_limit);
- va = early_memremap(efi.mokvar_table, map_size);
- if (!va) {
- pr_err("Failed to map EFI MOKvar config table pa=0x%lx, size=%lu.\n",
- efi.mokvar_table, map_size);
- return;
- }
- mokvar_entry = va + cur_offset;
- }
- /* Check for last sentinel entry */
- if (mokvar_entry->name[0] == '\0') {
- if (mokvar_entry->data_size != 0)
- break;
- err = 0;
- break;
- }
- /* Sanity check that the name is null terminated */
- size = strnlen(mokvar_entry->name,
- sizeof(mokvar_entry->name));
- if (size >= sizeof(mokvar_entry->name))
- break;
- /* Advance to the next entry */
- cur_offset = map_size_needed + mokvar_entry->data_size;
- }
- if (va)
- early_memunmap(va, map_size);
- if (err) {
- pr_err("EFI MOKvar config table is not valid\n");
- return;
- }
- if (md.type == EFI_BOOT_SERVICES_DATA)
- efi_mem_reserve(efi.mokvar_table, map_size_needed);
- efi_mokvar_table_size = map_size_needed;
- }
- /*
- * efi_mokvar_entry_next() - Get next entry in the EFI MOK config table
- *
- * mokvar_entry: Pointer to current EFI MOK config table entry
- * or null. Null indicates get first entry.
- * Passed by reference. This is updated to the
- * same value as the return value.
- *
- * Returns: Pointer to next EFI MOK config table entry
- * or null, if there are no more entries.
- * Same value is returned in the mokvar_entry
- * parameter.
- *
- * This routine depends on the EFI MOK config table being entirely
- * mapped with it's starting virtual address in efi_mokvar_table_va.
- */
- struct efi_mokvar_table_entry *efi_mokvar_entry_next(
- struct efi_mokvar_table_entry **mokvar_entry)
- {
- struct efi_mokvar_table_entry *mokvar_cur;
- struct efi_mokvar_table_entry *mokvar_next;
- size_t size_cur;
- mokvar_cur = *mokvar_entry;
- *mokvar_entry = NULL;
- if (efi_mokvar_table_va == NULL)
- return NULL;
- if (mokvar_cur == NULL) {
- mokvar_next = efi_mokvar_table_va;
- } else {
- if (mokvar_cur->name[0] == '\0')
- return NULL;
- size_cur = sizeof(*mokvar_cur) + mokvar_cur->data_size;
- mokvar_next = (void *)mokvar_cur + size_cur;
- }
- if (mokvar_next->name[0] == '\0')
- return NULL;
- *mokvar_entry = mokvar_next;
- return mokvar_next;
- }
- /*
- * efi_mokvar_entry_find() - Find EFI MOK config entry by name
- *
- * name: Name of the entry to look for.
- *
- * Returns: Pointer to EFI MOK config table entry if found;
- * null otherwise.
- *
- * This routine depends on the EFI MOK config table being entirely
- * mapped with it's starting virtual address in efi_mokvar_table_va.
- */
- struct efi_mokvar_table_entry *efi_mokvar_entry_find(const char *name)
- {
- struct efi_mokvar_table_entry *mokvar_entry = NULL;
- while (efi_mokvar_entry_next(&mokvar_entry)) {
- if (!strncmp(name, mokvar_entry->name,
- sizeof(mokvar_entry->name)))
- return mokvar_entry;
- }
- return NULL;
- }
- /*
- * efi_mokvar_sysfs_read() - sysfs binary file read routine
- *
- * Returns: Count of bytes read.
- *
- * Copy EFI MOK config table entry data for this mokvar sysfs binary file
- * to the supplied buffer, starting at the specified offset into mokvar table
- * entry data, for the specified count bytes. The copy is limited by the
- * amount of data in this mokvar config table entry.
- */
- static ssize_t efi_mokvar_sysfs_read(struct file *file, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
- loff_t off, size_t count)
- {
- struct efi_mokvar_table_entry *mokvar_entry = bin_attr->private;
- if (!capable(CAP_SYS_ADMIN))
- return 0;
- if (off >= mokvar_entry->data_size)
- return 0;
- if (count > mokvar_entry->data_size - off)
- count = mokvar_entry->data_size - off;
- memcpy(buf, mokvar_entry->data + off, count);
- return count;
- }
- /*
- * efi_mokvar_sysfs_init() - Map EFI MOK config table and create sysfs
- *
- * Map the EFI MOK variable config table for run-time use by the kernel
- * and create the sysfs entries in /sys/firmware/efi/mok-variables/
- *
- * This routine just returns if a valid EFI MOK variable config table
- * was not found earlier during boot.
- *
- * This routine must be called during a "middle" initcall phase, i.e.
- * after efi_mokvar_table_init() but before UEFI certs are loaded
- * during late init.
- *
- * Implicit inputs:
- * efi.mokvar_table: Physical address of EFI MOK variable config table
- * or special value that indicates no such table.
- *
- * efi_mokvar_table_size: Computed size of EFI MOK variable config table.
- * The table is considered present and valid if this
- * is non-zero.
- *
- * Implicit outputs:
- * efi_mokvar_table_va: Start virtual address of the EFI MOK config table.
- */
- static int __init efi_mokvar_sysfs_init(void)
- {
- void *config_va;
- struct efi_mokvar_table_entry *mokvar_entry = NULL;
- struct efi_mokvar_sysfs_attr *mokvar_sysfs = NULL;
- int err = 0;
- if (efi_mokvar_table_size == 0)
- return -ENOENT;
- config_va = memremap(efi.mokvar_table, efi_mokvar_table_size,
- MEMREMAP_WB);
- if (!config_va) {
- pr_err("Failed to map EFI MOKvar config table\n");
- return -ENOMEM;
- }
- efi_mokvar_table_va = config_va;
- mokvar_kobj = kobject_create_and_add("mok-variables", efi_kobj);
- if (!mokvar_kobj) {
- pr_err("Failed to create EFI mok-variables sysfs entry\n");
- return -ENOMEM;
- }
- while (efi_mokvar_entry_next(&mokvar_entry)) {
- mokvar_sysfs = kzalloc(sizeof(*mokvar_sysfs), GFP_KERNEL);
- if (!mokvar_sysfs) {
- err = -ENOMEM;
- break;
- }
- sysfs_bin_attr_init(&mokvar_sysfs->bin_attr);
- mokvar_sysfs->bin_attr.private = mokvar_entry;
- mokvar_sysfs->bin_attr.attr.name = mokvar_entry->name;
- mokvar_sysfs->bin_attr.attr.mode = 0400;
- mokvar_sysfs->bin_attr.size = mokvar_entry->data_size;
- mokvar_sysfs->bin_attr.read = efi_mokvar_sysfs_read;
- err = sysfs_create_bin_file(mokvar_kobj,
- &mokvar_sysfs->bin_attr);
- if (err)
- break;
- list_add_tail(&mokvar_sysfs->node, &efi_mokvar_sysfs_list);
- }
- if (err) {
- pr_err("Failed to create some EFI mok-variables sysfs entries\n");
- kfree(mokvar_sysfs);
- }
- return err;
- }
- fs_initcall(efi_mokvar_sysfs_init);
|