/* * Copyright (C) 2021 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 ); #include #include #include #include #include #include #include #include #include #include #include /** @file * * EFI autoexec script * */ /** Timeout for autoexec script downloads */ #define EFI_AUTOEXEC_TIMEOUT ( 2 * TICKS_PER_SEC ) /** Autoexec script image name */ #define EFI_AUTOEXEC_NAME "autoexec.ipxe" /** An EFI autoexec script loader */ struct efi_autoexec_loader { /** Required protocol GUID */ EFI_GUID *protocol; /** * Load autoexec script * * @v handle Handle on which protocol was found * @v image Image to fill in * @ret rc Return status code */ int ( * load ) ( EFI_HANDLE handle, struct image **image ); }; /** * Load autoexec script from filesystem * * @v handle Simple filesystem protocol handle * @v image Image to fill in * @ret rc Return status code */ static int efi_autoexec_filesystem ( EFI_HANDLE handle, struct image **image ) { EFI_HANDLE device = efi_loaded_image->DeviceHandle; int rc; /* Check that we were loaded from a filesystem */ if ( handle != device ) { DBGC ( device, "EFI %s is not the file system handle\n", efi_handle_name ( device ) ); return -ENOTTY; } /* Try loading from loaded image directory, if supported */ if ( ( rc = imgacquire ( "file:" EFI_AUTOEXEC_NAME, EFI_AUTOEXEC_TIMEOUT, image ) ) == 0 ) return 0; /* Try loading from root directory, if supported */ if ( ( rc = imgacquire ( "file:/" EFI_AUTOEXEC_NAME, EFI_AUTOEXEC_TIMEOUT, image ) ) == 0 ) return 0; return rc; } /** * Load autoexec script via temporary network device * * @v handle Managed network protocol service binding handle * @v image Image to fill in * @ret rc Return status code */ static int efi_autoexec_network ( EFI_HANDLE handle, struct image **image ) { EFI_HANDLE device = efi_loaded_image->DeviceHandle; struct net_device *netdev; int rc; /* Create temporary network device */ if ( ( rc = mnptemp_create ( handle, &netdev ) ) != 0 ) { DBGC ( device, "EFI %s could not create net device: %s\n", efi_handle_name ( device ), strerror ( rc ) ); goto err_create; } /* Open network device */ if ( ( rc = netdev_open ( netdev ) ) != 0 ) { DBGC ( device, "EFI %s could not open net device: %s\n", efi_handle_name ( device ), strerror ( rc ) ); goto err_open; } /* Attempt download */ rc = imgacquire ( EFI_AUTOEXEC_NAME, EFI_AUTOEXEC_TIMEOUT, image ); if ( rc != 0 ) { DBGC ( device, "EFI %s could not download %s: %s\n", efi_handle_name ( device ), EFI_AUTOEXEC_NAME, strerror ( rc ) ); } /* Ensure network exchanges have completed */ sync ( EFI_AUTOEXEC_TIMEOUT ); err_open: mnptemp_destroy ( netdev ); err_create: return rc; } /** Autoexec script loaders */ static struct efi_autoexec_loader efi_autoexec_loaders[] = { { .protocol = &efi_simple_file_system_protocol_guid, .load = efi_autoexec_filesystem, }, { .protocol = &efi_managed_network_service_binding_protocol_guid, .load = efi_autoexec_network, }, }; /** * Load autoexec script * * @ret rc Return status code */ int efi_autoexec_load ( void ) { EFI_HANDLE device = efi_loaded_image->DeviceHandle; EFI_HANDLE handle; struct efi_autoexec_loader *loader; struct image *image; unsigned int i; int rc; /* Use first applicable loader */ for ( i = 0 ; i < ( sizeof ( efi_autoexec_loaders ) / sizeof ( efi_autoexec_loaders[0] ) ) ; i ++ ) { /* Locate required protocol for this loader */ loader = &efi_autoexec_loaders[i]; if ( ( rc = efi_locate_device ( device, loader->protocol, &handle, 0 ) ) != 0 ) { DBGC ( device, "EFI %s found no %s: %s\n", efi_handle_name ( device ), efi_guid_ntoa ( loader->protocol ), strerror ( rc ) ); continue; } DBGC ( device, "EFI %s found %s on ", efi_handle_name ( device ), efi_guid_ntoa ( loader->protocol ) ); DBGC ( device, "%s\n", efi_handle_name ( handle ) ); /* Try loading */ if ( ( rc = loader->load ( handle, &image ) ) != 0 ) return rc; /* Discard zero-length images */ if ( ! image->len ) { DBGC ( device, "EFI %s discarding zero-length %s\n", efi_handle_name ( device ), image->name ); unregister_image ( image ); return -ENOENT; } DBGC ( device, "EFI %s loaded %s (%zd bytes)\n", efi_handle_name ( device ), image->name, image->len ); return 0; } return -ENOENT; }