cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

mokvar-table.c (10893B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * mokvar-table.c
      4 *
      5 * Copyright (c) 2020 Red Hat
      6 * Author: Lenny Szubowicz <lszubowi@redhat.com>
      7 *
      8 * This module contains the kernel support for the Linux EFI Machine
      9 * Owner Key (MOK) variable configuration table, which is identified by
     10 * the LINUX_EFI_MOK_VARIABLE_TABLE_GUID.
     11 *
     12 * This EFI configuration table provides a more robust alternative to
     13 * EFI volatile variables by which an EFI boot loader can pass the
     14 * contents of the Machine Owner Key (MOK) certificate stores to the
     15 * kernel during boot. If both the EFI MOK config table and corresponding
     16 * EFI MOK variables are present, the table should be considered as
     17 * more authoritative.
     18 *
     19 * This module includes code that validates and maps the EFI MOK table,
     20 * if it's presence was detected very early in boot.
     21 *
     22 * Kernel interface routines are provided to walk through all the
     23 * entries in the MOK config table or to search for a specific named
     24 * entry.
     25 *
     26 * The contents of the individual named MOK config table entries are
     27 * made available to user space via read-only sysfs binary files under:
     28 *
     29 * /sys/firmware/efi/mok-variables/
     30 *
     31 */
     32#define pr_fmt(fmt) "mokvar: " fmt
     33
     34#include <linux/capability.h>
     35#include <linux/efi.h>
     36#include <linux/init.h>
     37#include <linux/io.h>
     38#include <linux/kernel.h>
     39#include <linux/kobject.h>
     40#include <linux/list.h>
     41#include <linux/slab.h>
     42
     43#include <asm/early_ioremap.h>
     44
     45/*
     46 * The LINUX_EFI_MOK_VARIABLE_TABLE_GUID config table is a packed
     47 * sequence of struct efi_mokvar_table_entry, one for each named
     48 * MOK variable. The sequence is terminated by an entry with a
     49 * completely NULL name and 0 data size.
     50 *
     51 * efi_mokvar_table_size is set to the computed size of the
     52 * MOK config table by efi_mokvar_table_init(). This will be
     53 * non-zero if and only if the table if present and has been
     54 * validated by efi_mokvar_table_init().
     55 */
     56static size_t efi_mokvar_table_size;
     57
     58/*
     59 * efi_mokvar_table_va is the kernel virtual address at which the
     60 * EFI MOK config table has been mapped by efi_mokvar_sysfs_init().
     61 */
     62static struct efi_mokvar_table_entry *efi_mokvar_table_va;
     63
     64/*
     65 * Each /sys/firmware/efi/mok-variables/ sysfs file is represented by
     66 * an instance of struct efi_mokvar_sysfs_attr on efi_mokvar_sysfs_list.
     67 * bin_attr.private points to the associated EFI MOK config table entry.
     68 *
     69 * This list is created during boot and then remains unchanged.
     70 * So no synchronization is currently required to walk the list.
     71 */
     72struct efi_mokvar_sysfs_attr {
     73	struct bin_attribute bin_attr;
     74	struct list_head node;
     75};
     76
     77static LIST_HEAD(efi_mokvar_sysfs_list);
     78static struct kobject *mokvar_kobj;
     79
     80/*
     81 * efi_mokvar_table_init() - Early boot validation of EFI MOK config table
     82 *
     83 * If present, validate and compute the size of the EFI MOK variable
     84 * configuration table. This table may be provided by an EFI boot loader
     85 * as an alternative to ordinary EFI variables, due to platform-dependent
     86 * limitations. The memory occupied by this table is marked as reserved.
     87 *
     88 * This routine must be called before efi_free_boot_services() in order
     89 * to guarantee that it can mark the table as reserved.
     90 *
     91 * Implicit inputs:
     92 * efi.mokvar_table:	Physical address of EFI MOK variable config table
     93 *			or special value that indicates no such table.
     94 *
     95 * Implicit outputs:
     96 * efi_mokvar_table_size: Computed size of EFI MOK variable config table.
     97 *			The table is considered present and valid if this
     98 *			is non-zero.
     99 */
    100void __init efi_mokvar_table_init(void)
    101{
    102	efi_memory_desc_t md;
    103	void *va = NULL;
    104	unsigned long cur_offset = 0;
    105	unsigned long offset_limit;
    106	unsigned long map_size = 0;
    107	unsigned long map_size_needed = 0;
    108	unsigned long size;
    109	struct efi_mokvar_table_entry *mokvar_entry;
    110	int err;
    111
    112	if (!efi_enabled(EFI_MEMMAP))
    113		return;
    114
    115	if (efi.mokvar_table == EFI_INVALID_TABLE_ADDR)
    116		return;
    117	/*
    118	 * The EFI MOK config table must fit within a single EFI memory
    119	 * descriptor range.
    120	 */
    121	err = efi_mem_desc_lookup(efi.mokvar_table, &md);
    122	if (err) {
    123		pr_warn("EFI MOKvar config table is not within the EFI memory map\n");
    124		return;
    125	}
    126
    127	offset_limit = efi_mem_desc_end(&md) - efi.mokvar_table;
    128
    129	/*
    130	 * Validate the MOK config table. Since there is no table header
    131	 * from which we could get the total size of the MOK config table,
    132	 * we compute the total size as we validate each variably sized
    133	 * entry, remapping as necessary.
    134	 */
    135	err = -EINVAL;
    136	while (cur_offset + sizeof(*mokvar_entry) <= offset_limit) {
    137		mokvar_entry = va + cur_offset;
    138		map_size_needed = cur_offset + sizeof(*mokvar_entry);
    139		if (map_size_needed > map_size) {
    140			if (va)
    141				early_memunmap(va, map_size);
    142			/*
    143			 * Map a little more than the fixed size entry
    144			 * header, anticipating some data. It's safe to
    145			 * do so as long as we stay within current memory
    146			 * descriptor.
    147			 */
    148			map_size = min(map_size_needed + 2*EFI_PAGE_SIZE,
    149				       offset_limit);
    150			va = early_memremap(efi.mokvar_table, map_size);
    151			if (!va) {
    152				pr_err("Failed to map EFI MOKvar config table pa=0x%lx, size=%lu.\n",
    153				       efi.mokvar_table, map_size);
    154				return;
    155			}
    156			mokvar_entry = va + cur_offset;
    157		}
    158
    159		/* Check for last sentinel entry */
    160		if (mokvar_entry->name[0] == '\0') {
    161			if (mokvar_entry->data_size != 0)
    162				break;
    163			err = 0;
    164			break;
    165		}
    166
    167		/* Sanity check that the name is null terminated */
    168		size = strnlen(mokvar_entry->name,
    169			       sizeof(mokvar_entry->name));
    170		if (size >= sizeof(mokvar_entry->name))
    171			break;
    172
    173		/* Advance to the next entry */
    174		cur_offset = map_size_needed + mokvar_entry->data_size;
    175	}
    176
    177	if (va)
    178		early_memunmap(va, map_size);
    179	if (err) {
    180		pr_err("EFI MOKvar config table is not valid\n");
    181		return;
    182	}
    183
    184	if (md.type == EFI_BOOT_SERVICES_DATA)
    185		efi_mem_reserve(efi.mokvar_table, map_size_needed);
    186
    187	efi_mokvar_table_size = map_size_needed;
    188}
    189
    190/*
    191 * efi_mokvar_entry_next() - Get next entry in the EFI MOK config table
    192 *
    193 * mokvar_entry:	Pointer to current EFI MOK config table entry
    194 *			or null. Null indicates get first entry.
    195 *			Passed by reference. This is updated to the
    196 *			same value as the return value.
    197 *
    198 * Returns:		Pointer to next EFI MOK config table entry
    199 *			or null, if there are no more entries.
    200 *			Same value is returned in the mokvar_entry
    201 *			parameter.
    202 *
    203 * This routine depends on the EFI MOK config table being entirely
    204 * mapped with it's starting virtual address in efi_mokvar_table_va.
    205 */
    206struct efi_mokvar_table_entry *efi_mokvar_entry_next(
    207			struct efi_mokvar_table_entry **mokvar_entry)
    208{
    209	struct efi_mokvar_table_entry *mokvar_cur;
    210	struct efi_mokvar_table_entry *mokvar_next;
    211	size_t size_cur;
    212
    213	mokvar_cur = *mokvar_entry;
    214	*mokvar_entry = NULL;
    215
    216	if (efi_mokvar_table_va == NULL)
    217		return NULL;
    218
    219	if (mokvar_cur == NULL) {
    220		mokvar_next = efi_mokvar_table_va;
    221	} else {
    222		if (mokvar_cur->name[0] == '\0')
    223			return NULL;
    224		size_cur = sizeof(*mokvar_cur) + mokvar_cur->data_size;
    225		mokvar_next = (void *)mokvar_cur + size_cur;
    226	}
    227
    228	if (mokvar_next->name[0] == '\0')
    229		return NULL;
    230
    231	*mokvar_entry = mokvar_next;
    232	return mokvar_next;
    233}
    234
    235/*
    236 * efi_mokvar_entry_find() - Find EFI MOK config entry by name
    237 *
    238 * name:	Name of the entry to look for.
    239 *
    240 * Returns:	Pointer to EFI MOK config table entry if found;
    241 *		null otherwise.
    242 *
    243 * This routine depends on the EFI MOK config table being entirely
    244 * mapped with it's starting virtual address in efi_mokvar_table_va.
    245 */
    246struct efi_mokvar_table_entry *efi_mokvar_entry_find(const char *name)
    247{
    248	struct efi_mokvar_table_entry *mokvar_entry = NULL;
    249
    250	while (efi_mokvar_entry_next(&mokvar_entry)) {
    251		if (!strncmp(name, mokvar_entry->name,
    252			     sizeof(mokvar_entry->name)))
    253			return mokvar_entry;
    254	}
    255	return NULL;
    256}
    257
    258/*
    259 * efi_mokvar_sysfs_read() - sysfs binary file read routine
    260 *
    261 * Returns:	Count of bytes read.
    262 *
    263 * Copy EFI MOK config table entry data for this mokvar sysfs binary file
    264 * to the supplied buffer, starting at the specified offset into mokvar table
    265 * entry data, for the specified count bytes. The copy is limited by the
    266 * amount of data in this mokvar config table entry.
    267 */
    268static ssize_t efi_mokvar_sysfs_read(struct file *file, struct kobject *kobj,
    269				 struct bin_attribute *bin_attr, char *buf,
    270				 loff_t off, size_t count)
    271{
    272	struct efi_mokvar_table_entry *mokvar_entry = bin_attr->private;
    273
    274	if (!capable(CAP_SYS_ADMIN))
    275		return 0;
    276
    277	if (off >= mokvar_entry->data_size)
    278		return 0;
    279	if (count >  mokvar_entry->data_size - off)
    280		count = mokvar_entry->data_size - off;
    281
    282	memcpy(buf, mokvar_entry->data + off, count);
    283	return count;
    284}
    285
    286/*
    287 * efi_mokvar_sysfs_init() - Map EFI MOK config table and create sysfs
    288 *
    289 * Map the EFI MOK variable config table for run-time use by the kernel
    290 * and create the sysfs entries in /sys/firmware/efi/mok-variables/
    291 *
    292 * This routine just returns if a valid EFI MOK variable config table
    293 * was not found earlier during boot.
    294 *
    295 * This routine must be called during a "middle" initcall phase, i.e.
    296 * after efi_mokvar_table_init() but before UEFI certs are loaded
    297 * during late init.
    298 *
    299 * Implicit inputs:
    300 * efi.mokvar_table:	Physical address of EFI MOK variable config table
    301 *			or special value that indicates no such table.
    302 *
    303 * efi_mokvar_table_size: Computed size of EFI MOK variable config table.
    304 *			The table is considered present and valid if this
    305 *			is non-zero.
    306 *
    307 * Implicit outputs:
    308 * efi_mokvar_table_va:	Start virtual address of the EFI MOK config table.
    309 */
    310static int __init efi_mokvar_sysfs_init(void)
    311{
    312	void *config_va;
    313	struct efi_mokvar_table_entry *mokvar_entry = NULL;
    314	struct efi_mokvar_sysfs_attr *mokvar_sysfs = NULL;
    315	int err = 0;
    316
    317	if (efi_mokvar_table_size == 0)
    318		return -ENOENT;
    319
    320	config_va = memremap(efi.mokvar_table, efi_mokvar_table_size,
    321			     MEMREMAP_WB);
    322	if (!config_va) {
    323		pr_err("Failed to map EFI MOKvar config table\n");
    324		return -ENOMEM;
    325	}
    326	efi_mokvar_table_va = config_va;
    327
    328	mokvar_kobj = kobject_create_and_add("mok-variables", efi_kobj);
    329	if (!mokvar_kobj) {
    330		pr_err("Failed to create EFI mok-variables sysfs entry\n");
    331		return -ENOMEM;
    332	}
    333
    334	while (efi_mokvar_entry_next(&mokvar_entry)) {
    335		mokvar_sysfs = kzalloc(sizeof(*mokvar_sysfs), GFP_KERNEL);
    336		if (!mokvar_sysfs) {
    337			err = -ENOMEM;
    338			break;
    339		}
    340
    341		sysfs_bin_attr_init(&mokvar_sysfs->bin_attr);
    342		mokvar_sysfs->bin_attr.private = mokvar_entry;
    343		mokvar_sysfs->bin_attr.attr.name = mokvar_entry->name;
    344		mokvar_sysfs->bin_attr.attr.mode = 0400;
    345		mokvar_sysfs->bin_attr.size = mokvar_entry->data_size;
    346		mokvar_sysfs->bin_attr.read = efi_mokvar_sysfs_read;
    347
    348		err = sysfs_create_bin_file(mokvar_kobj,
    349					   &mokvar_sysfs->bin_attr);
    350		if (err)
    351			break;
    352
    353		list_add_tail(&mokvar_sysfs->node, &efi_mokvar_sysfs_list);
    354	}
    355
    356	if (err) {
    357		pr_err("Failed to create some EFI mok-variables sysfs entries\n");
    358		kfree(mokvar_sysfs);
    359	}
    360	return err;
    361}
    362fs_initcall(efi_mokvar_sysfs_init);