|  | @@ -0,0 +1,233 @@
 | 
		
	
		
			
			|  | 1 | +/*
 | 
		
	
		
			
			|  | 2 | + * Copyright (C) 2010 VMware, Inc.  All Rights Reserved.
 | 
		
	
		
			
			|  | 3 | + *
 | 
		
	
		
			
			|  | 4 | + * This program is free software; you can redistribute it and/or
 | 
		
	
		
			
			|  | 5 | + * modify it under the terms of the GNU General Public License as
 | 
		
	
		
			
			|  | 6 | + * published by the Free Software Foundation; either version 2 of the
 | 
		
	
		
			
			|  | 7 | + * License, or any later version.
 | 
		
	
		
			
			|  | 8 | + *
 | 
		
	
		
			
			|  | 9 | + * This program is distributed in the hope that it will be useful, but
 | 
		
	
		
			
			|  | 10 | + * WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
		
	
		
			
			|  | 11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
		
	
		
			
			|  | 12 | + * General Public License for more details.
 | 
		
	
		
			
			|  | 13 | + *
 | 
		
	
		
			
			|  | 14 | + * You should have received a copy of the GNU General Public License
 | 
		
	
		
			
			|  | 15 | + * along with this program; if not, write to the Free Software
 | 
		
	
		
			
			|  | 16 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 | 
		
	
		
			
			|  | 17 | + */
 | 
		
	
		
			
			|  | 18 | +
 | 
		
	
		
			
			|  | 19 | +FILE_LICENCE ( GPL2_OR_LATER );
 | 
		
	
		
			
			|  | 20 | +
 | 
		
	
		
			
			|  | 21 | +#include <stdlib.h>
 | 
		
	
		
			
			|  | 22 | +#include <string.h>
 | 
		
	
		
			
			|  | 23 | +#include <ipxe/open.h>
 | 
		
	
		
			
			|  | 24 | +#include <ipxe/process.h>
 | 
		
	
		
			
			|  | 25 | +#include <ipxe/iobuf.h>
 | 
		
	
		
			
			|  | 26 | +#include <ipxe/xfer.h>
 | 
		
	
		
			
			|  | 27 | +#include <ipxe/efi/efi.h>
 | 
		
	
		
			
			|  | 28 | +#include <ipxe/efi/ipxe_download.h>
 | 
		
	
		
			
			|  | 29 | +
 | 
		
	
		
			
			|  | 30 | +/** iPXE download protocol GUID */
 | 
		
	
		
			
			|  | 31 | +static EFI_GUID ipxe_download_protocol_guid
 | 
		
	
		
			
			|  | 32 | +	= IPXE_DOWNLOAD_PROTOCOL_GUID;
 | 
		
	
		
			
			|  | 33 | +
 | 
		
	
		
			
			|  | 34 | +/** A single in-progress file */
 | 
		
	
		
			
			|  | 35 | +struct efi_download_file {
 | 
		
	
		
			
			|  | 36 | +	/** Data transfer interface that provides downloaded data */
 | 
		
	
		
			
			|  | 37 | +	struct interface xfer;
 | 
		
	
		
			
			|  | 38 | +
 | 
		
	
		
			
			|  | 39 | +	/** Current file position */
 | 
		
	
		
			
			|  | 40 | +	size_t pos;
 | 
		
	
		
			
			|  | 41 | +
 | 
		
	
		
			
			|  | 42 | +	/** Data callback */
 | 
		
	
		
			
			|  | 43 | +	IPXE_DOWNLOAD_DATA_CALLBACK data_callback;
 | 
		
	
		
			
			|  | 44 | +
 | 
		
	
		
			
			|  | 45 | +	/** Finish callback */
 | 
		
	
		
			
			|  | 46 | +	IPXE_DOWNLOAD_FINISH_CALLBACK finish_callback;
 | 
		
	
		
			
			|  | 47 | +
 | 
		
	
		
			
			|  | 48 | +	/** Callback context */
 | 
		
	
		
			
			|  | 49 | +	void *context;
 | 
		
	
		
			
			|  | 50 | +};
 | 
		
	
		
			
			|  | 51 | +
 | 
		
	
		
			
			|  | 52 | +/* xfer interface */
 | 
		
	
		
			
			|  | 53 | +
 | 
		
	
		
			
			|  | 54 | +/**
 | 
		
	
		
			
			|  | 55 | + * Transfer finished or was aborted
 | 
		
	
		
			
			|  | 56 | + *
 | 
		
	
		
			
			|  | 57 | + * @v file		Data transfer file
 | 
		
	
		
			
			|  | 58 | + * @v rc		Reason for close
 | 
		
	
		
			
			|  | 59 | + */
 | 
		
	
		
			
			|  | 60 | +static void efi_download_close ( struct efi_download_file *file, int rc ) {
 | 
		
	
		
			
			|  | 61 | +
 | 
		
	
		
			
			|  | 62 | +	file->finish_callback ( file->context, RC_TO_EFIRC ( rc ) );
 | 
		
	
		
			
			|  | 63 | +
 | 
		
	
		
			
			|  | 64 | +	intf_shutdown ( &file->xfer, rc );
 | 
		
	
		
			
			|  | 65 | +}
 | 
		
	
		
			
			|  | 66 | +
 | 
		
	
		
			
			|  | 67 | +/**
 | 
		
	
		
			
			|  | 68 | + * Process received data
 | 
		
	
		
			
			|  | 69 | + *
 | 
		
	
		
			
			|  | 70 | + * @v file		Data transfer file
 | 
		
	
		
			
			|  | 71 | + * @v iobuf		I/O buffer
 | 
		
	
		
			
			|  | 72 | + * @v meta		Data transfer metadata
 | 
		
	
		
			
			|  | 73 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 74 | + */
 | 
		
	
		
			
			|  | 75 | +static int efi_download_deliver_iob ( struct efi_download_file *file,
 | 
		
	
		
			
			|  | 76 | +				      struct io_buffer *iobuf,
 | 
		
	
		
			
			|  | 77 | +				      struct xfer_metadata *meta ) {
 | 
		
	
		
			
			|  | 78 | +	EFI_STATUS efirc;
 | 
		
	
		
			
			|  | 79 | +	size_t len = iob_len ( iobuf );
 | 
		
	
		
			
			|  | 80 | +
 | 
		
	
		
			
			|  | 81 | +	/* Calculate new buffer position */
 | 
		
	
		
			
			|  | 82 | +	if ( meta->flags & XFER_FL_ABS_OFFSET )
 | 
		
	
		
			
			|  | 83 | +		file->pos = 0;
 | 
		
	
		
			
			|  | 84 | +	file->pos += meta->offset;
 | 
		
	
		
			
			|  | 85 | +
 | 
		
	
		
			
			|  | 86 | +	/* Call out to the data handler */
 | 
		
	
		
			
			|  | 87 | +	efirc = file->data_callback ( file->context, iobuf->data,
 | 
		
	
		
			
			|  | 88 | +				      len, file->pos );
 | 
		
	
		
			
			|  | 89 | +
 | 
		
	
		
			
			|  | 90 | +	/* Update current buffer position */
 | 
		
	
		
			
			|  | 91 | +	file->pos += len;
 | 
		
	
		
			
			|  | 92 | +
 | 
		
	
		
			
			|  | 93 | +	free_iob ( iobuf );
 | 
		
	
		
			
			|  | 94 | +	return EFIRC_TO_RC ( efirc );
 | 
		
	
		
			
			|  | 95 | +}
 | 
		
	
		
			
			|  | 96 | +
 | 
		
	
		
			
			|  | 97 | +/** Data transfer interface operations */
 | 
		
	
		
			
			|  | 98 | +static struct interface_operation efi_xfer_operations[] = {
 | 
		
	
		
			
			|  | 99 | +	INTF_OP ( xfer_deliver, struct efi_download_file *, efi_download_deliver_iob ),
 | 
		
	
		
			
			|  | 100 | +	INTF_OP ( intf_close, struct efi_download_file *, efi_download_close ),
 | 
		
	
		
			
			|  | 101 | +};
 | 
		
	
		
			
			|  | 102 | +
 | 
		
	
		
			
			|  | 103 | +/** EFI download data transfer interface descriptor */
 | 
		
	
		
			
			|  | 104 | +static struct interface_descriptor efi_download_file_xfer_desc =
 | 
		
	
		
			
			|  | 105 | +	INTF_DESC ( struct efi_download_file, xfer, efi_xfer_operations );
 | 
		
	
		
			
			|  | 106 | +
 | 
		
	
		
			
			|  | 107 | +/**
 | 
		
	
		
			
			|  | 108 | + * Start downloading a file, and register callback functions to handle the
 | 
		
	
		
			
			|  | 109 | + * download.
 | 
		
	
		
			
			|  | 110 | + *
 | 
		
	
		
			
			|  | 111 | + * @v This		iPXE Download Protocol instance
 | 
		
	
		
			
			|  | 112 | + * @v Url		URL to download from
 | 
		
	
		
			
			|  | 113 | + * @v DataCallback	Callback that will be invoked when data arrives
 | 
		
	
		
			
			|  | 114 | + * @v FinishCallback	Callback that will be invoked when the download ends
 | 
		
	
		
			
			|  | 115 | + * @v Context		Context passed to the Data and Finish callbacks
 | 
		
	
		
			
			|  | 116 | + * @v File		Token that can be used to abort the download
 | 
		
	
		
			
			|  | 117 | + * @ret Status		EFI status code
 | 
		
	
		
			
			|  | 118 | + */
 | 
		
	
		
			
			|  | 119 | +static EFI_STATUS EFIAPI
 | 
		
	
		
			
			|  | 120 | +efi_download_start ( IPXE_DOWNLOAD_PROTOCOL *This __unused,
 | 
		
	
		
			
			|  | 121 | +		     CHAR8 *Url,
 | 
		
	
		
			
			|  | 122 | +		     IPXE_DOWNLOAD_DATA_CALLBACK DataCallback,
 | 
		
	
		
			
			|  | 123 | +		     IPXE_DOWNLOAD_FINISH_CALLBACK FinishCallback,
 | 
		
	
		
			
			|  | 124 | +		     VOID *Context,
 | 
		
	
		
			
			|  | 125 | +		     IPXE_DOWNLOAD_FILE *File ) {
 | 
		
	
		
			
			|  | 126 | +	struct efi_download_file *file;
 | 
		
	
		
			
			|  | 127 | +	int rc;
 | 
		
	
		
			
			|  | 128 | +
 | 
		
	
		
			
			|  | 129 | +	file = malloc ( sizeof ( struct efi_download_file ) );
 | 
		
	
		
			
			|  | 130 | +	if ( file == NULL ) {
 | 
		
	
		
			
			|  | 131 | +		return EFI_OUT_OF_RESOURCES;
 | 
		
	
		
			
			|  | 132 | +	}
 | 
		
	
		
			
			|  | 133 | +
 | 
		
	
		
			
			|  | 134 | +	intf_init ( &file->xfer, &efi_download_file_xfer_desc, NULL );
 | 
		
	
		
			
			|  | 135 | +	rc = xfer_open ( &file->xfer, LOCATION_URI_STRING, Url );
 | 
		
	
		
			
			|  | 136 | +	if ( rc ) {
 | 
		
	
		
			
			|  | 137 | +		free ( file );
 | 
		
	
		
			
			|  | 138 | +		return RC_TO_EFIRC ( rc );
 | 
		
	
		
			
			|  | 139 | +	}
 | 
		
	
		
			
			|  | 140 | +
 | 
		
	
		
			
			|  | 141 | +	file->pos = 0;
 | 
		
	
		
			
			|  | 142 | +	file->data_callback = DataCallback;
 | 
		
	
		
			
			|  | 143 | +	file->finish_callback = FinishCallback;
 | 
		
	
		
			
			|  | 144 | +	file->context = Context;
 | 
		
	
		
			
			|  | 145 | +	*File = file;
 | 
		
	
		
			
			|  | 146 | +	return EFI_SUCCESS;
 | 
		
	
		
			
			|  | 147 | +}
 | 
		
	
		
			
			|  | 148 | +
 | 
		
	
		
			
			|  | 149 | +/**
 | 
		
	
		
			
			|  | 150 | + * Forcibly abort downloading a file that is currently in progress.
 | 
		
	
		
			
			|  | 151 | + *
 | 
		
	
		
			
			|  | 152 | + * It is not safe to call this function after the Finish callback has executed.
 | 
		
	
		
			
			|  | 153 | + *
 | 
		
	
		
			
			|  | 154 | + * @v This		iPXE Download Protocol instance
 | 
		
	
		
			
			|  | 155 | + * @v File		Token obtained from Start
 | 
		
	
		
			
			|  | 156 | + * @v Status		Reason for aborting the download
 | 
		
	
		
			
			|  | 157 | + * @ret Status		EFI status code
 | 
		
	
		
			
			|  | 158 | + */
 | 
		
	
		
			
			|  | 159 | +static EFI_STATUS EFIAPI
 | 
		
	
		
			
			|  | 160 | +efi_download_abort ( IPXE_DOWNLOAD_PROTOCOL *This __unused,
 | 
		
	
		
			
			|  | 161 | +		     IPXE_DOWNLOAD_FILE File,
 | 
		
	
		
			
			|  | 162 | +		     EFI_STATUS Status ) {
 | 
		
	
		
			
			|  | 163 | +	struct efi_download_file *file = File;
 | 
		
	
		
			
			|  | 164 | +
 | 
		
	
		
			
			|  | 165 | +	efi_download_close ( file, EFIRC_TO_RC ( Status ) );
 | 
		
	
		
			
			|  | 166 | +	return EFI_SUCCESS;
 | 
		
	
		
			
			|  | 167 | +}
 | 
		
	
		
			
			|  | 168 | +
 | 
		
	
		
			
			|  | 169 | +/**
 | 
		
	
		
			
			|  | 170 | + * Poll for more data from iPXE. This function will invoke the registered
 | 
		
	
		
			
			|  | 171 | + * callbacks if data is available or if downloads complete.
 | 
		
	
		
			
			|  | 172 | + *
 | 
		
	
		
			
			|  | 173 | + * @v This		iPXE Download Protocol instance
 | 
		
	
		
			
			|  | 174 | + * @ret Status		EFI status code
 | 
		
	
		
			
			|  | 175 | + */
 | 
		
	
		
			
			|  | 176 | +static EFI_STATUS EFIAPI
 | 
		
	
		
			
			|  | 177 | +efi_download_poll ( IPXE_DOWNLOAD_PROTOCOL *This __unused ) {
 | 
		
	
		
			
			|  | 178 | +	step();
 | 
		
	
		
			
			|  | 179 | +	return EFI_SUCCESS;
 | 
		
	
		
			
			|  | 180 | +}
 | 
		
	
		
			
			|  | 181 | +
 | 
		
	
		
			
			|  | 182 | +/** Publicly exposed iPXE download protocol */
 | 
		
	
		
			
			|  | 183 | +static IPXE_DOWNLOAD_PROTOCOL ipxe_download_protocol_interface = {
 | 
		
	
		
			
			|  | 184 | +	.Start = efi_download_start,
 | 
		
	
		
			
			|  | 185 | +	.Abort = efi_download_abort,
 | 
		
	
		
			
			|  | 186 | +	.Poll = efi_download_poll
 | 
		
	
		
			
			|  | 187 | +};
 | 
		
	
		
			
			|  | 188 | +
 | 
		
	
		
			
			|  | 189 | +/**
 | 
		
	
		
			
			|  | 190 | + * Create a new device handle with a iPXE download protocol attached to it.
 | 
		
	
		
			
			|  | 191 | + *
 | 
		
	
		
			
			|  | 192 | + * @v device_handle	Newly created device handle (output)
 | 
		
	
		
			
			|  | 193 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 194 | + */
 | 
		
	
		
			
			|  | 195 | +int efi_download_install ( EFI_HANDLE *device_handle ) {
 | 
		
	
		
			
			|  | 196 | +	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
 | 
		
	
		
			
			|  | 197 | +	EFI_STATUS efirc;
 | 
		
	
		
			
			|  | 198 | +	EFI_HANDLE handle = NULL;
 | 
		
	
		
			
			|  | 199 | +	if (efi_loaded_image->DeviceHandle) { /* TODO: ensure handle is the NIC (maybe efi_image has a better way to indicate the handle doing SNP?) */
 | 
		
	
		
			
			|  | 200 | +		handle = efi_loaded_image->DeviceHandle;
 | 
		
	
		
			
			|  | 201 | +	}
 | 
		
	
		
			
			|  | 202 | +
 | 
		
	
		
			
			|  | 203 | +	DBG ( "Installing ipxe protocol interface (%p)... ",
 | 
		
	
		
			
			|  | 204 | +	      &ipxe_download_protocol_interface );
 | 
		
	
		
			
			|  | 205 | +	efirc = bs->InstallMultipleProtocolInterfaces (
 | 
		
	
		
			
			|  | 206 | +			&handle,
 | 
		
	
		
			
			|  | 207 | +			&ipxe_download_protocol_guid,
 | 
		
	
		
			
			|  | 208 | +			&ipxe_download_protocol_interface,
 | 
		
	
		
			
			|  | 209 | +			NULL );
 | 
		
	
		
			
			|  | 210 | +	if ( efirc ) {
 | 
		
	
		
			
			|  | 211 | +		DBG ( "failed (%s)\n", efi_strerror ( efirc ) );
 | 
		
	
		
			
			|  | 212 | +		return EFIRC_TO_RC ( efirc );
 | 
		
	
		
			
			|  | 213 | +	}
 | 
		
	
		
			
			|  | 214 | +
 | 
		
	
		
			
			|  | 215 | +	DBG ( "success (%p)\n", handle );
 | 
		
	
		
			
			|  | 216 | +	*device_handle = handle;
 | 
		
	
		
			
			|  | 217 | +	return 0;
 | 
		
	
		
			
			|  | 218 | +}
 | 
		
	
		
			
			|  | 219 | +
 | 
		
	
		
			
			|  | 220 | +/**
 | 
		
	
		
			
			|  | 221 | + * Remove the iPXE download protocol from the given handle, and if nothing
 | 
		
	
		
			
			|  | 222 | + * else is attached, destroy the handle.
 | 
		
	
		
			
			|  | 223 | + *
 | 
		
	
		
			
			|  | 224 | + * @v device_handle	EFI device handle to remove from
 | 
		
	
		
			
			|  | 225 | + */
 | 
		
	
		
			
			|  | 226 | +void efi_download_uninstall ( EFI_HANDLE device_handle ) {
 | 
		
	
		
			
			|  | 227 | +	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
 | 
		
	
		
			
			|  | 228 | +
 | 
		
	
		
			
			|  | 229 | +	bs->UninstallMultipleProtocolInterfaces (
 | 
		
	
		
			
			|  | 230 | +			device_handle,
 | 
		
	
		
			
			|  | 231 | +			ipxe_download_protocol_guid,
 | 
		
	
		
			
			|  | 232 | +			ipxe_download_protocol_interface );
 | 
		
	
		
			
			|  | 233 | +}
 |