/* * Copyright (C) 2025 Michael Brown . * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * You can also choose to distribute this program under the terms of * the Unmodified Binary Distribution Licence (as given in the file * COPYING.UBDL), provided that you have satisfied its requirements. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); FILE_SECBOOT ( PERMITTED ); #include #include #include #include #include /** @file * * EFI configuration tables * */ /** * Look up EFI configuration table * * @v guid Configuration table GUID * @ret table Configuration table, or NULL */ void * efi_find_table ( EFI_GUID *guid ) { void *table; unsigned int i; /* Scan for installed table */ for ( i = 0 ; i < efi_systab->NumberOfTableEntries ; i++ ) { if ( memcmp ( &efi_systab->ConfigurationTable[i].VendorGuid, guid, sizeof ( *guid ) ) == 0 ) { table = efi_systab->ConfigurationTable[i].VendorTable; DBGC ( guid, "EFITAB %s is at %p\n", efi_guid_ntoa ( guid ), table ); return table; } } return NULL; } /** * Install EFI configuration table * * @v table Configuration table type * @v data Configuration table data, or NULL to uninstall * @v backup Table backup, or NULL to not back up old table * @ret rc Return status code */ int efi_install_table ( struct efi_table *table, const void *data, void **backup ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_GUID *guid = table->guid; void *copy; void *new; void *old; size_t old_len; size_t new_len; EFI_STATUS efirc; int rc; /* Get currently installed table, if any */ old = efi_find_table ( guid ); old_len = ( old ? table->len ( old ) : 0 ); /* Create backup copy, if applicable */ if ( old_len && backup ) { if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, old_len, © ) ) != 0 ) { rc = -EEFI ( efirc ); goto err_backup; } memcpy ( copy, old, old_len ); DBGC ( table, "EFITAB %s %p+%#zx backed up\n", efi_guid_ntoa ( guid ), old, old_len ); } else { copy = NULL; } /* Create installable runtime services data copy, if applicable */ new_len = ( data ? table->len ( data ) : 0 ); if ( new_len ) { if ( ( efirc = bs->AllocatePool ( EfiRuntimeServicesData, new_len, &new ) ) != 0 ) { rc = -EEFI ( efirc ); goto err_allocate; } memcpy ( new, data, new_len ); } else { new = NULL; } /* (Un)install configuration table, if applicable */ if ( new || old ) { if ( ( efirc = bs->InstallConfigurationTable ( guid, new ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( table, "EFITAB %s could not install: %s\n", efi_guid_ntoa ( guid ), strerror ( rc ) ); goto err_install; } if ( old ) { DBGC ( table, "EFITAB %s %p+%#zx uninstalled\n", efi_guid_ntoa ( guid ), old, old_len ); } if ( new ) { DBGC ( table, "EFITAB %s %p+%#zx installed\n", efi_guid_ntoa ( guid ), new, new_len ); } } /* Record backup copy, if applicable */ if ( backup ) { if ( *backup ) bs->FreePool ( *backup ); *backup = copy; } /* Sanity check */ assert ( efi_find_table ( guid ) == new ); return 0; err_install: if ( new ) bs->FreePool ( new ); err_allocate: if ( copy ) bs->FreePool ( copy ); err_backup: return rc; } /** * Uninstall EFI configuration table * * @v table Configuration table type * @v backup Table backup (or NULL to not restore old table) * @ret rc Return status code */ int efi_uninstall_table ( struct efi_table *table, void **backup ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; void *old; int rc; /* Uninstall or reinstall as applicable */ old = ( backup ? *backup : NULL ); if ( ( rc = efi_install_table ( table, old, NULL ) ) != 0 ) return rc; /* Free backup copy, if applicable */ if ( backup && *backup ) { bs->FreePool ( *backup ); *backup = NULL; } return 0; }