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 <errno.h>
24 #include <ipxe/open.h>
25 #include <ipxe/process.h>
26 #include <ipxe/iobuf.h>
27 #include <ipxe/xfer.h>
28 #include <ipxe/efi/efi.h>
29 #include <ipxe/efi/efi_snp.h>
30 #include <ipxe/efi/efi_download.h>
31 
32 /** iPXE download protocol GUID */
33 static EFI_GUID ipxe_download_protocol_guid
34 	= IPXE_DOWNLOAD_PROTOCOL_GUID;
35 
36 /** A single in-progress file */
37 struct efi_download_file {
38 	/** Data transfer interface that provides downloaded data */
39 	struct interface xfer;
40 
41 	/** Current file position */
42 	size_t pos;
43 
44 	/** Data callback */
45 	IPXE_DOWNLOAD_DATA_CALLBACK data_callback;
46 
47 	/** Finish callback */
48 	IPXE_DOWNLOAD_FINISH_CALLBACK finish_callback;
49 
50 	/** Callback context */
51 	void *context;
52 };
53 
54 /* xfer interface */
55 
56 /**
57  * Transfer finished or was aborted
58  *
59  * @v file		Data transfer file
60  * @v rc		Reason for close
61  */
efi_download_close(struct efi_download_file * file,int rc)62 static void efi_download_close ( struct efi_download_file *file, int rc ) {
63 
64 	file->finish_callback ( file->context, EFIRC ( rc ) );
65 
66 	intf_shutdown ( &file->xfer, rc );
67 
68 	efi_snp_release();
69 }
70 
71 /**
72  * Process received data
73  *
74  * @v file		Data transfer file
75  * @v iobuf		I/O buffer
76  * @v meta		Data transfer metadata
77  * @ret rc		Return status code
78  */
efi_download_deliver_iob(struct efi_download_file * file,struct io_buffer * iobuf,struct xfer_metadata * meta)79 static int efi_download_deliver_iob ( struct efi_download_file *file,
80 				      struct io_buffer *iobuf,
81 				      struct xfer_metadata *meta ) {
82 	EFI_STATUS efirc;
83 	size_t len = iob_len ( iobuf );
84 	int rc;
85 
86 	/* Calculate new buffer position */
87 	if ( meta->flags & XFER_FL_ABS_OFFSET )
88 		file->pos = 0;
89 	file->pos += meta->offset;
90 
91 	/* Call out to the data handler */
92 	if ( ( efirc = file->data_callback ( file->context, iobuf->data,
93 					     len, file->pos ) ) != 0 ) {
94 		rc = -EEFI ( efirc );
95 		goto err_callback;
96 	}
97 
98 	/* Update current buffer position */
99 	file->pos += len;
100 
101 	/* Success */
102 	rc = 0;
103 
104  err_callback:
105 	free_iob ( iobuf );
106 	return rc;
107 }
108 
109 /** Data transfer interface operations */
110 static struct interface_operation efi_xfer_operations[] = {
111 	INTF_OP ( xfer_deliver, struct efi_download_file *, efi_download_deliver_iob ),
112 	INTF_OP ( intf_close, struct efi_download_file *, efi_download_close ),
113 };
114 
115 /** EFI download data transfer interface descriptor */
116 static struct interface_descriptor efi_download_file_xfer_desc =
117 	INTF_DESC ( struct efi_download_file, xfer, efi_xfer_operations );
118 
119 /**
120  * Start downloading a file, and register callback functions to handle the
121  * download.
122  *
123  * @v This		iPXE Download Protocol instance
124  * @v Url		URL to download from
125  * @v DataCallback	Callback that will be invoked when data arrives
126  * @v FinishCallback	Callback that will be invoked when the download ends
127  * @v Context		Context passed to the Data and Finish callbacks
128  * @v File		Token that can be used to abort the download
129  * @ret Status		EFI status code
130  */
131 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)132 efi_download_start ( IPXE_DOWNLOAD_PROTOCOL *This __unused,
133 		     CHAR8 *Url,
134 		     IPXE_DOWNLOAD_DATA_CALLBACK DataCallback,
135 		     IPXE_DOWNLOAD_FINISH_CALLBACK FinishCallback,
136 		     VOID *Context,
137 		     IPXE_DOWNLOAD_FILE *File ) {
138 	struct efi_download_file *file;
139 	int rc;
140 
141 	efi_snp_claim();
142 
143 	file = malloc ( sizeof ( struct efi_download_file ) );
144 	if ( file == NULL ) {
145 		efi_snp_release();
146 		return EFI_OUT_OF_RESOURCES;
147 	}
148 
149 	intf_init ( &file->xfer, &efi_download_file_xfer_desc, NULL );
150 	rc = xfer_open ( &file->xfer, LOCATION_URI_STRING, Url );
151 	if ( rc ) {
152 		free ( file );
153 		efi_snp_release();
154 		return EFIRC ( rc );
155 	}
156 
157 	file->pos = 0;
158 	file->data_callback = DataCallback;
159 	file->finish_callback = FinishCallback;
160 	file->context = Context;
161 	*File = file;
162 	return EFI_SUCCESS;
163 }
164 
165 /**
166  * Forcibly abort downloading a file that is currently in progress.
167  *
168  * It is not safe to call this function after the Finish callback has executed.
169  *
170  * @v This		iPXE Download Protocol instance
171  * @v File		Token obtained from Start
172  * @v Status		Reason for aborting the download
173  * @ret Status		EFI status code
174  */
175 static EFI_STATUS EFIAPI
efi_download_abort(IPXE_DOWNLOAD_PROTOCOL * This __unused,IPXE_DOWNLOAD_FILE File,EFI_STATUS Status)176 efi_download_abort ( IPXE_DOWNLOAD_PROTOCOL *This __unused,
177 		     IPXE_DOWNLOAD_FILE File,
178 		     EFI_STATUS Status ) {
179 	struct efi_download_file *file = File;
180 
181 	efi_download_close ( file, -EEFI ( Status ) );
182 	return EFI_SUCCESS;
183 }
184 
185 /**
186  * Poll for more data from iPXE. This function will invoke the registered
187  * callbacks if data is available or if downloads complete.
188  *
189  * @v This		iPXE Download Protocol instance
190  * @ret Status		EFI status code
191  */
192 static EFI_STATUS EFIAPI
efi_download_poll(IPXE_DOWNLOAD_PROTOCOL * This __unused)193 efi_download_poll ( IPXE_DOWNLOAD_PROTOCOL *This __unused ) {
194 	step();
195 	return EFI_SUCCESS;
196 }
197 
198 /** Publicly exposed iPXE download protocol */
199 static IPXE_DOWNLOAD_PROTOCOL ipxe_download_protocol_interface = {
200 	.Start = efi_download_start,
201 	.Abort = efi_download_abort,
202 	.Poll = efi_download_poll
203 };
204 
205 /**
206  * Install iPXE download protocol
207  *
208  * @v handle		EFI handle
209  * @ret rc		Return status code
210  */
efi_download_install(EFI_HANDLE handle)211 int efi_download_install ( EFI_HANDLE handle ) {
212 	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
213 	EFI_STATUS efirc;
214 	int rc;
215 
216 	efirc = bs->InstallMultipleProtocolInterfaces (
217 			&handle,
218 			&ipxe_download_protocol_guid,
219 			&ipxe_download_protocol_interface,
220 			NULL );
221 	if ( efirc ) {
222 		rc = -EEFI ( efirc );
223 		DBG ( "Could not install download protocol: %s\n",
224 		      strerror ( rc ) );
225 		return rc;
226 	}
227 
228 	return 0;
229 }
230 
231 /**
232  * Uninstall iPXE download protocol
233  *
234  * @v handle		EFI handle
235  */
efi_download_uninstall(EFI_HANDLE handle)236 void efi_download_uninstall ( EFI_HANDLE handle ) {
237 	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
238 
239 	bs->UninstallMultipleProtocolInterfaces (
240 			handle,
241 			&ipxe_download_protocol_guid,
242 			&ipxe_download_protocol_interface, NULL );
243 }
244