| /* |
| * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. |
| * |
| * 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 ); |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <ipxe/efi/efi.h> |
| #include <ipxe/efi/efi_table.h> |
| |
| /** @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; |
| } |