]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[efi] Add support for installing EFI configuration tables
authorMichael Brown <mcb30@ipxe.org>
Thu, 27 Mar 2025 14:45:12 +0000 (14:45 +0000)
committerMichael Brown <mcb30@ipxe.org>
Thu, 27 Mar 2025 15:36:39 +0000 (15:36 +0000)
Add the ability to install and uninstall arbitrary EFI configuration
tables.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/include/ipxe/efi/efi_table.h [new file with mode: 0644]
src/include/ipxe/errfile.h
src/interface/efi/efi_init.c
src/interface/efi/efi_table.c [new file with mode: 0644]

diff --git a/src/include/ipxe/efi/efi_table.h b/src/include/ipxe/efi/efi_table.h
new file mode 100644 (file)
index 0000000..9a41d87
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef _IPXE_EFI_TABLE_H
+#define _IPXE_EFI_TABLE_H
+
+/** @file
+ *
+ * EFI configuration tables
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/efi/efi.h>
+
+/** An installable EFI configuration table type */
+struct efi_table {
+       /** Table GUID */
+       EFI_GUID *guid;
+       /**
+        * Determine length of table
+        *
+        * @v data              Configuration table data (presumed valid)
+        * @ret len             Length of table
+        *
+        * EFI does not record the length of installed configuration
+        * tables.  Consumers must understand the specific type of
+        * table in order to be able to determine its length from the
+        * contents.
+        */
+       size_t ( * len ) ( const void *data );
+};
+
+extern void * efi_find_table ( EFI_GUID *guid );
+extern int efi_install_table ( struct efi_table *table, const void *data,
+                              void **backup );
+extern int efi_uninstall_table ( struct efi_table *table, void **backup );
+
+#endif /* _IPXE_EFI_TABLE_H */
index 51b458d56ac5bb7325d6cf7d0efe758807dbf78d..a64104a22c8ad5db90d556d497f5bc46e6cb2db4 100644 (file)
@@ -85,6 +85,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #define ERRFILE_efi_service           ( ERRFILE_CORE | 0x002d0000 )
 #define ERRFILE_null_smbios           ( ERRFILE_CORE | 0x002e0000 )
 #define ERRFILE_efi_open              ( ERRFILE_CORE | 0x002f0000 )
+#define ERRFILE_efi_table             ( ERRFILE_CORE | 0x00300000 )
 
 #define ERRFILE_eisa                ( ERRFILE_DRIVER | 0x00000000 )
 #define ERRFILE_isa                 ( ERRFILE_DRIVER | 0x00010000 )
index 1ba144ee7459a574092662be5e98d0cebea4eac9..69aea0d5021e9559c58539d4dc2a41d8e92d42fa 100644 (file)
@@ -25,6 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #include <ipxe/init.h>
 #include <ipxe/rotate.h>
 #include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_table.h>
 #include <ipxe/efi/efi_driver.h>
 #include <ipxe/efi/efi_path.h>
 #include <ipxe/efi/efi_cmdline.h>
@@ -103,24 +104,6 @@ static EFIAPI void efi_shutdown_hook ( EFI_EVENT event __unused,
        shutdown_boot();
 }
 
-/**
- * Look up EFI configuration table
- *
- * @v guid             Configuration table GUID
- * @ret table          Configuration table, or NULL
- */
-static void * efi_find_table ( EFI_GUID *guid ) {
-       unsigned int i;
-
-       for ( i = 0 ; i < efi_systab->NumberOfTableEntries ; i++ ) {
-               if ( memcmp ( &efi_systab->ConfigurationTable[i].VendorGuid,
-                             guid, sizeof ( *guid ) ) == 0 )
-                       return efi_systab->ConfigurationTable[i].VendorTable;
-       }
-
-       return NULL;
-}
-
 /**
  * Construct a stack cookie value
  *
diff --git a/src/interface/efi/efi_table.c b/src/interface/efi/efi_table.c
new file mode 100644 (file)
index 0000000..3c3f35a
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * 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,
+                                                 &copy ) ) != 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;
+}