diff --git a/src/image/efi_image.c b/src/image/efi_image.c index 82101d484..9261036da 100644 --- a/src/image/efi_image.c +++ b/src/image/efi_image.c @@ -33,6 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include #include @@ -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 index 000000000..a9b7eac8b --- /dev/null +++ b/src/include/ipxe/efi/efi_fdt.h @@ -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 + +extern int efi_fdt_install ( void ); +extern int efi_fdt_uninstall ( void ); + +#endif /* _IPXE_EFI_FDT_H */ diff --git a/src/interface/efi/efi_fdt.c b/src/interface/efi/efi_fdt.c index 71b788ae7..4a10236a0 100644 --- a/src/interface/efi/efi_fdt.c +++ b/src/interface/efi/efi_fdt.c @@ -24,9 +24,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include #include #include #include +#include +#include #include /** @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; +}