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

efi.c (6119B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Helpers for early access to EFI configuration table.
      4 *
      5 * Originally derived from arch/x86/boot/compressed/acpi.c
      6 */
      7
      8#include "misc.h"
      9
     10/**
     11 * efi_get_type - Given a pointer to boot_params, determine the type of EFI environment.
     12 *
     13 * @bp:         pointer to boot_params
     14 *
     15 * Return: EFI_TYPE_{32,64} for valid EFI environments, EFI_TYPE_NONE otherwise.
     16 */
     17enum efi_type efi_get_type(struct boot_params *bp)
     18{
     19	struct efi_info *ei;
     20	enum efi_type et;
     21	const char *sig;
     22
     23	ei = &bp->efi_info;
     24	sig = (char *)&ei->efi_loader_signature;
     25
     26	if (!strncmp(sig, EFI64_LOADER_SIGNATURE, 4)) {
     27		et = EFI_TYPE_64;
     28	} else if (!strncmp(sig, EFI32_LOADER_SIGNATURE, 4)) {
     29		et = EFI_TYPE_32;
     30	} else {
     31		debug_putstr("No EFI environment detected.\n");
     32		et = EFI_TYPE_NONE;
     33	}
     34
     35#ifndef CONFIG_X86_64
     36	/*
     37	 * Existing callers like acpi.c treat this case as an indicator to
     38	 * fall-through to non-EFI, rather than an error, so maintain that
     39	 * functionality here as well.
     40	 */
     41	if (ei->efi_systab_hi || ei->efi_memmap_hi) {
     42		debug_putstr("EFI system table is located above 4GB and cannot be accessed.\n");
     43		et = EFI_TYPE_NONE;
     44	}
     45#endif
     46
     47	return et;
     48}
     49
     50/**
     51 * efi_get_system_table - Given a pointer to boot_params, retrieve the physical address
     52 *                        of the EFI system table.
     53 *
     54 * @bp:         pointer to boot_params
     55 *
     56 * Return: EFI system table address on success. On error, return 0.
     57 */
     58unsigned long efi_get_system_table(struct boot_params *bp)
     59{
     60	unsigned long sys_tbl_pa;
     61	struct efi_info *ei;
     62	enum efi_type et;
     63
     64	/* Get systab from boot params. */
     65	ei = &bp->efi_info;
     66#ifdef CONFIG_X86_64
     67	sys_tbl_pa = ei->efi_systab | ((__u64)ei->efi_systab_hi << 32);
     68#else
     69	sys_tbl_pa = ei->efi_systab;
     70#endif
     71	if (!sys_tbl_pa) {
     72		debug_putstr("EFI system table not found.");
     73		return 0;
     74	}
     75
     76	return sys_tbl_pa;
     77}
     78
     79/*
     80 * EFI config table address changes to virtual address after boot, which may
     81 * not be accessible for the kexec'd kernel. To address this, kexec provides
     82 * the initial physical address via a struct setup_data entry, which is
     83 * checked for here, along with some sanity checks.
     84 */
     85static struct efi_setup_data *get_kexec_setup_data(struct boot_params *bp,
     86						   enum efi_type et)
     87{
     88#ifdef CONFIG_X86_64
     89	struct efi_setup_data *esd = NULL;
     90	struct setup_data *data;
     91	u64 pa_data;
     92
     93	pa_data = bp->hdr.setup_data;
     94	while (pa_data) {
     95		data = (struct setup_data *)pa_data;
     96		if (data->type == SETUP_EFI) {
     97			esd = (struct efi_setup_data *)(pa_data + sizeof(struct setup_data));
     98			break;
     99		}
    100
    101		pa_data = data->next;
    102	}
    103
    104	/*
    105	 * Original ACPI code falls back to attempting normal EFI boot in these
    106	 * cases, so maintain existing behavior by indicating non-kexec
    107	 * environment to the caller, but print them for debugging.
    108	 */
    109	if (esd && !esd->tables) {
    110		debug_putstr("kexec EFI environment missing valid configuration table.\n");
    111		return NULL;
    112	}
    113
    114	return esd;
    115#endif
    116	return NULL;
    117}
    118
    119/**
    120 * efi_get_conf_table - Given a pointer to boot_params, locate and return the physical
    121 *                      address of EFI configuration table.
    122 *
    123 * @bp:                 pointer to boot_params
    124 * @cfg_tbl_pa:         location to store physical address of config table
    125 * @cfg_tbl_len:        location to store number of config table entries
    126 *
    127 * Return: 0 on success. On error, return params are left unchanged.
    128 */
    129int efi_get_conf_table(struct boot_params *bp, unsigned long *cfg_tbl_pa,
    130		       unsigned int *cfg_tbl_len)
    131{
    132	unsigned long sys_tbl_pa;
    133	enum efi_type et;
    134	int ret;
    135
    136	if (!cfg_tbl_pa || !cfg_tbl_len)
    137		return -EINVAL;
    138
    139	sys_tbl_pa = efi_get_system_table(bp);
    140	if (!sys_tbl_pa)
    141		return -EINVAL;
    142
    143	/* Handle EFI bitness properly */
    144	et = efi_get_type(bp);
    145	if (et == EFI_TYPE_64) {
    146		efi_system_table_64_t *stbl = (efi_system_table_64_t *)sys_tbl_pa;
    147		struct efi_setup_data *esd;
    148
    149		/* kexec provides an alternative EFI conf table, check for it. */
    150		esd = get_kexec_setup_data(bp, et);
    151
    152		*cfg_tbl_pa = esd ? esd->tables : stbl->tables;
    153		*cfg_tbl_len = stbl->nr_tables;
    154	} else if (et == EFI_TYPE_32) {
    155		efi_system_table_32_t *stbl = (efi_system_table_32_t *)sys_tbl_pa;
    156
    157		*cfg_tbl_pa = stbl->tables;
    158		*cfg_tbl_len = stbl->nr_tables;
    159	} else {
    160		return -EINVAL;
    161	}
    162
    163	return 0;
    164}
    165
    166/* Get vendor table address/guid from EFI config table at the given index */
    167static int get_vendor_table(void *cfg_tbl, unsigned int idx,
    168			    unsigned long *vendor_tbl_pa,
    169			    efi_guid_t *vendor_tbl_guid,
    170			    enum efi_type et)
    171{
    172	if (et == EFI_TYPE_64) {
    173		efi_config_table_64_t *tbl_entry = (efi_config_table_64_t *)cfg_tbl + idx;
    174
    175		if (!IS_ENABLED(CONFIG_X86_64) && tbl_entry->table >> 32) {
    176			debug_putstr("Error: EFI config table entry located above 4GB.\n");
    177			return -EINVAL;
    178		}
    179
    180		*vendor_tbl_pa = tbl_entry->table;
    181		*vendor_tbl_guid = tbl_entry->guid;
    182
    183	} else if (et == EFI_TYPE_32) {
    184		efi_config_table_32_t *tbl_entry = (efi_config_table_32_t *)cfg_tbl + idx;
    185
    186		*vendor_tbl_pa = tbl_entry->table;
    187		*vendor_tbl_guid = tbl_entry->guid;
    188	} else {
    189		return -EINVAL;
    190	}
    191
    192	return 0;
    193}
    194
    195/**
    196 * efi_find_vendor_table - Given EFI config table, search it for the physical
    197 *                         address of the vendor table associated with GUID.
    198 *
    199 * @bp:                pointer to boot_params
    200 * @cfg_tbl_pa:        pointer to EFI configuration table
    201 * @cfg_tbl_len:       number of entries in EFI configuration table
    202 * @guid:              GUID of vendor table
    203 *
    204 * Return: vendor table address on success. On error, return 0.
    205 */
    206unsigned long efi_find_vendor_table(struct boot_params *bp,
    207				    unsigned long cfg_tbl_pa,
    208				    unsigned int cfg_tbl_len,
    209				    efi_guid_t guid)
    210{
    211	enum efi_type et;
    212	unsigned int i;
    213
    214	et = efi_get_type(bp);
    215	if (et == EFI_TYPE_NONE)
    216		return 0;
    217
    218	for (i = 0; i < cfg_tbl_len; i++) {
    219		unsigned long vendor_tbl_pa;
    220		efi_guid_t vendor_tbl_guid;
    221		int ret;
    222
    223		ret = get_vendor_table((void *)cfg_tbl_pa, i,
    224				       &vendor_tbl_pa,
    225				       &vendor_tbl_guid, et);
    226		if (ret)
    227			return 0;
    228
    229		if (!efi_guidcmp(guid, vendor_tbl_guid))
    230			return vendor_tbl_pa;
    231	}
    232
    233	return 0;
    234}