| /* | |
| * Copyright (C) 2010 VMware, Inc. All Rights Reserved. | |
| * | |
| * 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 St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| */ | |
| FILE_LICENCE ( GPL2_OR_LATER ); | |
| FILE_SECBOOT ( PERMITTED ); | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <errno.h> | |
| #include <ipxe/open.h> | |
| #include <ipxe/process.h> | |
| #include <ipxe/iobuf.h> | |
| #include <ipxe/xfer.h> | |
| #include <ipxe/efi/efi.h> | |
| #include <ipxe/efi/efi_snp.h> | |
| #include <ipxe/efi/efi_download.h> | |
| /** iPXE download protocol GUID */ | |
| static EFI_GUID ipxe_download_protocol_guid | |
| = IPXE_DOWNLOAD_PROTOCOL_GUID; | |
| /** A single in-progress file */ | |
| struct efi_download_file { | |
| /** Data transfer interface that provides downloaded data */ | |
| struct interface xfer; | |
| /** Current file position */ | |
| size_t pos; | |
| /** Data callback */ | |
| IPXE_DOWNLOAD_DATA_CALLBACK data_callback; | |
| /** Finish callback */ | |
| IPXE_DOWNLOAD_FINISH_CALLBACK finish_callback; | |
| /** Callback context */ | |
| void *context; | |
| }; | |
| /* xfer interface */ | |
| /** | |
| * Transfer finished or was aborted | |
| * | |
| * @v file Data transfer file | |
| * @v rc Reason for close | |
| */ | |
| static void efi_download_close ( struct efi_download_file *file, int rc ) { | |
| file->finish_callback ( file->context, EFIRC ( rc ) ); | |
| intf_shutdown ( &file->xfer, rc ); | |
| efi_snp_release(); | |
| } | |
| /** | |
| * Process received data | |
| * | |
| * @v file Data transfer file | |
| * @v iobuf I/O buffer | |
| * @v meta Data transfer metadata | |
| * @ret rc Return status code | |
| */ | |
| static int efi_download_deliver_iob ( struct efi_download_file *file, | |
| struct io_buffer *iobuf, | |
| struct xfer_metadata *meta ) { | |
| EFI_STATUS efirc; | |
| size_t len = iob_len ( iobuf ); | |
| int rc; | |
| /* Calculate new buffer position */ | |
| if ( meta->flags & XFER_FL_ABS_OFFSET ) | |
| file->pos = 0; | |
| file->pos += meta->offset; | |
| /* Call out to the data handler */ | |
| if ( ( efirc = file->data_callback ( file->context, iobuf->data, | |
| len, file->pos ) ) != 0 ) { | |
| rc = -EEFI ( efirc ); | |
| goto err_callback; | |
| } | |
| /* Update current buffer position */ | |
| file->pos += len; | |
| /* Success */ | |
| rc = 0; | |
| err_callback: | |
| free_iob ( iobuf ); | |
| return rc; | |
| } | |
| /** Data transfer interface operations */ | |
| static struct interface_operation efi_xfer_operations[] = { | |
| INTF_OP ( xfer_deliver, struct efi_download_file *, efi_download_deliver_iob ), | |
| INTF_OP ( intf_close, struct efi_download_file *, efi_download_close ), | |
| }; | |
| /** EFI download data transfer interface descriptor */ | |
| static struct interface_descriptor efi_download_file_xfer_desc = | |
| INTF_DESC ( struct efi_download_file, xfer, efi_xfer_operations ); | |
| /** | |
| * Start downloading a file, and register callback functions to handle the | |
| * download. | |
| * | |
| * @v This iPXE Download Protocol instance | |
| * @v Url URL to download from | |
| * @v DataCallback Callback that will be invoked when data arrives | |
| * @v FinishCallback Callback that will be invoked when the download ends | |
| * @v Context Context passed to the Data and Finish callbacks | |
| * @v File Token that can be used to abort the download | |
| * @ret Status EFI status code | |
| */ | |
| static EFI_STATUS EFIAPI | |
| efi_download_start ( IPXE_DOWNLOAD_PROTOCOL *This __unused, | |
| CHAR8 *Url, | |
| IPXE_DOWNLOAD_DATA_CALLBACK DataCallback, | |
| IPXE_DOWNLOAD_FINISH_CALLBACK FinishCallback, | |
| VOID *Context, | |
| IPXE_DOWNLOAD_FILE *File ) { | |
| struct efi_download_file *file; | |
| int rc; | |
| efi_snp_claim(); | |
| file = malloc ( sizeof ( struct efi_download_file ) ); | |
| if ( file == NULL ) { | |
| efi_snp_release(); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| intf_init ( &file->xfer, &efi_download_file_xfer_desc, NULL ); | |
| rc = xfer_open ( &file->xfer, LOCATION_URI_STRING, Url ); | |
| if ( rc ) { | |
| free ( file ); | |
| efi_snp_release(); | |
| return EFIRC ( rc ); | |
| } | |
| file->pos = 0; | |
| file->data_callback = DataCallback; | |
| file->finish_callback = FinishCallback; | |
| file->context = Context; | |
| *File = file; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| * Forcibly abort downloading a file that is currently in progress. | |
| * | |
| * It is not safe to call this function after the Finish callback has executed. | |
| * | |
| * @v This iPXE Download Protocol instance | |
| * @v File Token obtained from Start | |
| * @v Status Reason for aborting the download | |
| * @ret Status EFI status code | |
| */ | |
| static EFI_STATUS EFIAPI | |
| efi_download_abort ( IPXE_DOWNLOAD_PROTOCOL *This __unused, | |
| IPXE_DOWNLOAD_FILE File, | |
| EFI_STATUS Status ) { | |
| struct efi_download_file *file = File; | |
| efi_download_close ( file, -EEFI ( Status ) ); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| * Poll for more data from iPXE. This function will invoke the registered | |
| * callbacks if data is available or if downloads complete. | |
| * | |
| * @v This iPXE Download Protocol instance | |
| * @ret Status EFI status code | |
| */ | |
| static EFI_STATUS EFIAPI | |
| efi_download_poll ( IPXE_DOWNLOAD_PROTOCOL *This __unused ) { | |
| step(); | |
| return EFI_SUCCESS; | |
| } | |
| /** Publicly exposed iPXE download protocol */ | |
| static IPXE_DOWNLOAD_PROTOCOL ipxe_download_protocol_interface = { | |
| .Start = efi_download_start, | |
| .Abort = efi_download_abort, | |
| .Poll = efi_download_poll | |
| }; | |
| /** | |
| * Install iPXE download protocol | |
| * | |
| * @v handle EFI handle | |
| * @ret rc Return status code | |
| */ | |
| int efi_download_install ( EFI_HANDLE handle ) { | |
| EFI_BOOT_SERVICES *bs = efi_systab->BootServices; | |
| EFI_STATUS efirc; | |
| int rc; | |
| efirc = bs->InstallMultipleProtocolInterfaces ( | |
| &handle, | |
| &ipxe_download_protocol_guid, | |
| &ipxe_download_protocol_interface, | |
| NULL ); | |
| if ( efirc ) { | |
| rc = -EEFI ( efirc ); | |
| DBG ( "Could not install download protocol: %s\n", | |
| strerror ( rc ) ); | |
| return rc; | |
| } | |
| return 0; | |
| } | |
| /** | |
| * Uninstall iPXE download protocol | |
| * | |
| * @v handle EFI handle | |
| */ | |
| void efi_download_uninstall ( EFI_HANDLE handle ) { | |
| EFI_BOOT_SERVICES *bs = efi_systab->BootServices; | |
| bs->UninstallMultipleProtocolInterfaces ( | |
| handle, | |
| &ipxe_download_protocol_guid, | |
| &ipxe_download_protocol_interface, NULL ); | |
| } |