blob: 8d12bd57c038aaf10e9e5c74ac902a0e3527d91e [file] [log] [blame]
/*
* 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 );
#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 );
}