iPXE
efi_download.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2010 VMware, Inc.  All Rights Reserved.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
00017  */
00018 
00019 FILE_LICENCE ( GPL2_OR_LATER );
00020 
00021 #include <stdlib.h>
00022 #include <string.h>
00023 #include <errno.h>
00024 #include <ipxe/open.h>
00025 #include <ipxe/process.h>
00026 #include <ipxe/iobuf.h>
00027 #include <ipxe/xfer.h>
00028 #include <ipxe/efi/efi.h>
00029 #include <ipxe/efi/efi_snp.h>
00030 #include <ipxe/efi/efi_download.h>
00031 
00032 /** iPXE download protocol GUID */
00033 static EFI_GUID ipxe_download_protocol_guid
00034         = IPXE_DOWNLOAD_PROTOCOL_GUID;
00035 
00036 /** A single in-progress file */
00037 struct efi_download_file {
00038         /** Data transfer interface that provides downloaded data */
00039         struct interface xfer;
00040 
00041         /** Current file position */
00042         size_t pos;
00043 
00044         /** Data callback */
00045         IPXE_DOWNLOAD_DATA_CALLBACK data_callback;
00046 
00047         /** Finish callback */
00048         IPXE_DOWNLOAD_FINISH_CALLBACK finish_callback;
00049 
00050         /** Callback context */
00051         void *context;
00052 };
00053 
00054 /* xfer interface */
00055 
00056 /**
00057  * Transfer finished or was aborted
00058  *
00059  * @v file              Data transfer file
00060  * @v rc                Reason for close
00061  */
00062 static void efi_download_close ( struct efi_download_file *file, int rc ) {
00063 
00064         file->finish_callback ( file->context, EFIRC ( rc ) );
00065 
00066         intf_shutdown ( &file->xfer, rc );
00067 
00068         efi_snp_release();
00069 }
00070 
00071 /**
00072  * Process received data
00073  *
00074  * @v file              Data transfer file
00075  * @v iobuf             I/O buffer
00076  * @v meta              Data transfer metadata
00077  * @ret rc              Return status code
00078  */
00079 static int efi_download_deliver_iob ( struct efi_download_file *file,
00080                                       struct io_buffer *iobuf,
00081                                       struct xfer_metadata *meta ) {
00082         EFI_STATUS efirc;
00083         size_t len = iob_len ( iobuf );
00084         int rc;
00085 
00086         /* Calculate new buffer position */
00087         if ( meta->flags & XFER_FL_ABS_OFFSET )
00088                 file->pos = 0;
00089         file->pos += meta->offset;
00090 
00091         /* Call out to the data handler */
00092         if ( ( efirc = file->data_callback ( file->context, iobuf->data,
00093                                              len, file->pos ) ) != 0 ) {
00094                 rc = -EEFI ( efirc );
00095                 goto err_callback;
00096         }
00097 
00098         /* Update current buffer position */
00099         file->pos += len;
00100 
00101         /* Success */
00102         rc = 0;
00103 
00104  err_callback:
00105         free_iob ( iobuf );
00106         return rc;
00107 }
00108 
00109 /** Data transfer interface operations */
00110 static struct interface_operation efi_xfer_operations[] = {
00111         INTF_OP ( xfer_deliver, struct efi_download_file *, efi_download_deliver_iob ),
00112         INTF_OP ( intf_close, struct efi_download_file *, efi_download_close ),
00113 };
00114 
00115 /** EFI download data transfer interface descriptor */
00116 static struct interface_descriptor efi_download_file_xfer_desc =
00117         INTF_DESC ( struct efi_download_file, xfer, efi_xfer_operations );
00118 
00119 /**
00120  * Start downloading a file, and register callback functions to handle the
00121  * download.
00122  *
00123  * @v This              iPXE Download Protocol instance
00124  * @v Url               URL to download from
00125  * @v DataCallback      Callback that will be invoked when data arrives
00126  * @v FinishCallback    Callback that will be invoked when the download ends
00127  * @v Context           Context passed to the Data and Finish callbacks
00128  * @v File              Token that can be used to abort the download
00129  * @ret Status          EFI status code
00130  */
00131 static EFI_STATUS EFIAPI
00132 efi_download_start ( IPXE_DOWNLOAD_PROTOCOL *This __unused,
00133                      CHAR8 *Url,
00134                      IPXE_DOWNLOAD_DATA_CALLBACK DataCallback,
00135                      IPXE_DOWNLOAD_FINISH_CALLBACK FinishCallback,
00136                      VOID *Context,
00137                      IPXE_DOWNLOAD_FILE *File ) {
00138         struct efi_download_file *file;
00139         int rc;
00140 
00141         file = malloc ( sizeof ( struct efi_download_file ) );
00142         if ( file == NULL ) {
00143                 return EFI_OUT_OF_RESOURCES;
00144         }
00145 
00146         intf_init ( &file->xfer, &efi_download_file_xfer_desc, NULL );
00147         rc = xfer_open ( &file->xfer, LOCATION_URI_STRING, Url );
00148         if ( rc ) {
00149                 free ( file );
00150                 return EFIRC ( rc );
00151         }
00152 
00153         efi_snp_claim();
00154         file->pos = 0;
00155         file->data_callback = DataCallback;
00156         file->finish_callback = FinishCallback;
00157         file->context = Context;
00158         *File = file;
00159         return EFI_SUCCESS;
00160 }
00161 
00162 /**
00163  * Forcibly abort downloading a file that is currently in progress.
00164  *
00165  * It is not safe to call this function after the Finish callback has executed.
00166  *
00167  * @v This              iPXE Download Protocol instance
00168  * @v File              Token obtained from Start
00169  * @v Status            Reason for aborting the download
00170  * @ret Status          EFI status code
00171  */
00172 static EFI_STATUS EFIAPI
00173 efi_download_abort ( IPXE_DOWNLOAD_PROTOCOL *This __unused,
00174                      IPXE_DOWNLOAD_FILE File,
00175                      EFI_STATUS Status ) {
00176         struct efi_download_file *file = File;
00177 
00178         efi_download_close ( file, -EEFI ( Status ) );
00179         return EFI_SUCCESS;
00180 }
00181 
00182 /**
00183  * Poll for more data from iPXE. This function will invoke the registered
00184  * callbacks if data is available or if downloads complete.
00185  *
00186  * @v This              iPXE Download Protocol instance
00187  * @ret Status          EFI status code
00188  */
00189 static EFI_STATUS EFIAPI
00190 efi_download_poll ( IPXE_DOWNLOAD_PROTOCOL *This __unused ) {
00191         step();
00192         return EFI_SUCCESS;
00193 }
00194 
00195 /** Publicly exposed iPXE download protocol */
00196 static IPXE_DOWNLOAD_PROTOCOL ipxe_download_protocol_interface = {
00197         .Start = efi_download_start,
00198         .Abort = efi_download_abort,
00199         .Poll = efi_download_poll
00200 };
00201 
00202 /**
00203  * Install iPXE download protocol
00204  *
00205  * @v handle            EFI handle
00206  * @ret rc              Return status code
00207  */
00208 int efi_download_install ( EFI_HANDLE handle ) {
00209         EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
00210         EFI_STATUS efirc;
00211         int rc;
00212 
00213         efirc = bs->InstallMultipleProtocolInterfaces (
00214                         &handle,
00215                         &ipxe_download_protocol_guid,
00216                         &ipxe_download_protocol_interface,
00217                         NULL );
00218         if ( efirc ) {
00219                 rc = -EEFI ( efirc );
00220                 DBG ( "Could not install download protocol: %s\n",
00221                       strerror ( rc ) );
00222                 return rc;
00223         }
00224 
00225         return 0;
00226 }
00227 
00228 /**
00229  * Uninstall iPXE download protocol
00230  *
00231  * @v handle            EFI handle
00232  */
00233 void efi_download_uninstall ( EFI_HANDLE handle ) {
00234         EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
00235 
00236         bs->UninstallMultipleProtocolInterfaces (
00237                         handle,
00238                         &ipxe_download_protocol_guid,
00239                         &ipxe_download_protocol_interface, NULL );
00240 }