brintos

brintos / linux-shallow public Read only

0
0
Text · 10.6 KiB · 5ed0602 Raw
363 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * mokvar-table.c4 *5 * Copyright (c) 2020 Red Hat6 * Author: Lenny Szubowicz <lszubowi@redhat.com>7 *8 * This module contains the kernel support for the Linux EFI Machine9 * Owner Key (MOK) variable configuration table, which is identified by10 * the LINUX_EFI_MOK_VARIABLE_TABLE_GUID.11 *12 * This EFI configuration table provides a more robust alternative to13 * EFI volatile variables by which an EFI boot loader can pass the14 * contents of the Machine Owner Key (MOK) certificate stores to the15 * kernel during boot. If both the EFI MOK config table and corresponding16 * EFI MOK variables are present, the table should be considered as17 * 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 the23 * entries in the MOK config table or to search for a specific named24 * entry.25 *26 * The contents of the individual named MOK config table entries are27 * 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: " fmt33 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 packed47 * sequence of struct efi_mokvar_table_entry, one for each named48 * MOK variable. The sequence is terminated by an entry with a49 * completely NULL name and 0 data size.50 *51 * efi_mokvar_table_size is set to the computed size of the52 * MOK config table by efi_mokvar_table_init(). This will be53 * non-zero if and only if the table if present and has been54 * 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 the60 * 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 by66 * 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 table82 *83 * If present, validate and compute the size of the EFI MOK variable84 * configuration table. This table may be provided by an EFI boot loader85 * as an alternative to ordinary EFI variables, due to platform-dependent86 * limitations. The memory occupied by this table is marked as reserved.87 *88 * This routine must be called before efi_free_boot_services() in order89 * 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 table93 *			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 this98 *			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 memory119	 * 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 header131	 * from which we could get the total size of the MOK config table,132	 * we compute the total size as we validate each variably sized133	 * 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 entry144			 * header, anticipating some data. It's safe to145			 * do so as long as we stay within current memory146			 * 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 table192 *193 * mokvar_entry:	Pointer to current EFI MOK config table entry194 *			or null. Null indicates get first entry.195 *			Passed by reference. This is updated to the196 *			same value as the return value.197 *198 * Returns:		Pointer to next EFI MOK config table entry199 *			or null, if there are no more entries.200 *			Same value is returned in the mokvar_entry201 *			parameter.202 *203 * This routine depends on the EFI MOK config table being entirely204 * 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 name237 *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 entirely244 * 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 routine260 *261 * Returns:	Count of bytes read.262 *263 * Copy EFI MOK config table entry data for this mokvar sysfs binary file264 * to the supplied buffer, starting at the specified offset into mokvar table265 * entry data, for the specified count bytes. The copy is limited by the266 * 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 sysfs288 *289 * Map the EFI MOK variable config table for run-time use by the kernel290 * and create the sysfs entries in /sys/firmware/efi/mok-variables/291 *292 * This routine just returns if a valid EFI MOK variable config table293 * 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 loaded297 * during late init.298 *299 * Implicit inputs:300 * efi.mokvar_table:	Physical address of EFI MOK variable config table301 *			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 this305 *			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);363