]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[efi] Install a device tree for the booted OS, if available
authorMichael Brown <mcb30@ipxe.org>
Fri, 28 Mar 2025 14:20:44 +0000 (14:20 +0000)
committerMichael Brown <mcb30@ipxe.org>
Fri, 28 Mar 2025 15:29:53 +0000 (15:29 +0000)
If we have a device tree available (e.g. because the user has
explicitly downloaded a device tree using the "fdt" command), then
provide it to the booted operating system as an EFI configuration
table.

Since x86 does not typically use device trees, we create weak symbols
for efi_fdt_install() and efi_fdt_uninstall() to avoid dragging FDT
support into all x86 UEFI binaries.

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

index 82101d4842d3d48ccb50d4a5a6e57d7e15bd3a06..9261036dac7e8678a375494f9f43606f4d62e3e5 100644 (file)
@@ -33,6 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #include <ipxe/efi/efi_driver.h>
 #include <ipxe/efi/efi_image.h>
 #include <ipxe/efi/efi_shim.h>
+#include <ipxe/efi/efi_fdt.h>
 #include <ipxe/image.h>
 #include <ipxe/init.h>
 #include <ipxe/features.h>
@@ -122,6 +123,24 @@ static wchar_t * efi_image_cmdline ( struct image *image ) {
        return cmdline;
 }
 
+/**
+ * Install EFI Flattened Device Tree table (when no FDT support is present)
+ *
+ * @ret rc             Return status code
+ */
+__weak int efi_fdt_install ( void ) {
+       return 0;
+}
+
+/**
+ * Uninstall EFI Flattened Device Tree table (when no FDT support is present)
+ *
+ * @ret rc             Return status code
+ */
+__weak int efi_fdt_uninstall ( void ) {
+       return 0;
+}
+
 /**
  * Execute EFI image
  *
@@ -187,6 +206,13 @@ static int efi_image_exec ( struct image *image ) {
                goto err_download_install;
        }
 
+       /* Install Flattened Device Tree table */
+       if ( ( rc = efi_fdt_install() ) != 0 ) {
+               DBGC ( image, "EFIIMAGE %s could not install FDT: %s\n",
+                      image->name, strerror ( rc ) );
+               goto err_fdt_install;
+       }
+
        /* Create device path for image */
        path = efi_image_path ( exec, snpdev->path );
        if ( ! path ) {
@@ -313,6 +339,8 @@ static int efi_image_exec ( struct image *image ) {
  err_cmdline:
        free ( path );
  err_image_path:
+       efi_fdt_uninstall();
+ err_fdt_install:
        efi_download_uninstall ( snpdev->handle );
  err_download_install:
        efi_pxe_uninstall ( snpdev->handle );
diff --git a/src/include/ipxe/efi/efi_fdt.h b/src/include/ipxe/efi/efi_fdt.h
new file mode 100644 (file)
index 0000000..a9b7eac
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _IPXE_EFI_FDT_H
+#define _IPXE_EFI_FDT_H
+
+/** @file
+ *
+ * EFI Flattened Device Tree
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/efi/efi.h>
+
+extern int efi_fdt_install ( void );
+extern int efi_fdt_uninstall ( void );
+
+#endif /* _IPXE_EFI_FDT_H */
index 71b788ae71ac24eb8e52e9f0da77e08499dfbd51..4a10236a053589dd01faae7a8781967280c0c6c1 100644 (file)
 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 
 #include <string.h>
+#include <byteswap.h>
 #include <ipxe/fdt.h>
 #include <ipxe/init.h>
 #include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_table.h>
+#include <ipxe/efi/efi_fdt.h>
 #include <ipxe/efi/Guid/Fdt.h>
 
 /** @file
@@ -76,3 +79,82 @@ static void efi_fdt_init ( void ) {
 struct init_fn efi_fdt_init_fn __init_fn ( INIT_EARLY ) = {
        .initialise = efi_fdt_init,
 };
+
+/**
+ * Determine length of EFI Flattened Device Tree
+ *
+ * @v data             Configuration table data (presumed valid)
+ * @ret len            Length of table
+ */
+static size_t efi_fdt_len ( const void *data ) {
+       const struct fdt_header *hdr = data;
+
+       return be32_to_cpu ( hdr->totalsize );
+}
+
+/** EFI Flattened Device Tree table type */
+static struct efi_table efi_fdt_table = {
+       .guid = &efi_fdt_table_guid,
+       .len = efi_fdt_len,
+};
+
+/** EFI Flattened Device Tree table backup */
+static void *efi_fdt_backup;
+
+/** EFI Flattened Device Tree installed table */
+static struct fdt_header *efi_fdt_installed;
+
+/**
+ * Install EFI Flattened Device Tree table
+ *
+ * @ret rc             Return status code
+ */
+int efi_fdt_install ( void ) {
+       int rc;
+
+       /* Create device tree */
+       if ( ( rc = fdt_create ( &efi_fdt_installed ) ) != 0 ) {
+               DBGC ( &efi_fdt, "EFI_FDT could not install: %s\n",
+                      strerror ( rc ) );
+               goto err_create;
+       }
+
+       /* Install table */
+       if ( ( rc = efi_install_table ( &efi_fdt_table, efi_fdt_installed,
+                                       &efi_fdt_backup ) ) != 0 ) {
+               DBGC ( &efi_fdt, "EFIFDT could not install: %s\n",
+                      strerror ( rc ) );
+               goto err_install;
+       }
+
+       return 0;
+
+       efi_uninstall_table ( &efi_fdt_table, &efi_fdt_backup );
+ err_install:
+       fdt_remove ( efi_fdt_installed );
+ err_create:
+       return rc;
+}
+
+/**
+ * Uninstall EFI Flattened Device Tree table
+ *
+ * @ret rc             Return status code
+ */
+int efi_fdt_uninstall ( void ) {
+       int rc;
+
+       /* Uninstall table */
+       if ( ( rc = efi_uninstall_table ( &efi_fdt_table,
+                                         &efi_fdt_backup ) ) != 0 ) {
+               DBGC ( &efi_fdt, "EFIFDT could not %sinstall: %s\n",
+                      ( efi_fdt_backup ? "re" : "un" ), strerror ( rc ) );
+               /* Leak memory: there is nothing else we can do */
+               return rc;
+       }
+
+       /* Remove table */
+       fdt_remove ( efi_fdt_installed );
+
+       return 0;
+}