Bläddra i källkod

[efi] Add iPXE download protocol

iPXE exposes some extended capabilities via the PXE FILE API to allow
NBPs such as pxelinux to use protocols other than TFTP.  Provide an
equivalent interface as a UEFI protocol so that EFI binaries may also
take advantage of iPXE's extended capabilities.

This can be used with a patched version of elilo, for example:

  http://comments.gmane.org/gmane.comp.boot-loaders.elilo.general/147

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Jarrod Johnson 13 år sedan
förälder
incheckning
32c4a3a255
4 ändrade filer med 513 tillägg och 2 borttagningar
  1. 124
    2
      src/image/efi_image.c
  2. 2
    0
      src/include/ipxe/efi/efi.h
  3. 154
    0
      src/include/ipxe/efi/ipxe_download.h
  4. 233
    0
      src/interface/efi/efi_download.c

+ 124
- 2
src/image/efi_image.c Visa fil

@@ -19,13 +19,95 @@
19 19
 FILE_LICENCE ( GPL2_OR_LATER );
20 20
 
21 21
 #include <errno.h>
22
+#include <stdlib.h>
22 23
 #include <ipxe/efi/efi.h>
23 24
 #include <ipxe/image.h>
24 25
 #include <ipxe/init.h>
25 26
 #include <ipxe/features.h>
27
+#include <ipxe/uri.h>
26 28
 
27 29
 FEATURE ( FEATURE_IMAGE, "EFI", DHCP_EB_FEATURE_EFI, 1 );
28 30
 
31
+/** EFI loaded image protocol GUID */
32
+static EFI_GUID efi_loaded_image_protocol_guid =
33
+	EFI_LOADED_IMAGE_PROTOCOL_GUID;
34
+
35
+/**
36
+ * Create a Unicode command line for the image
37
+ *
38
+ * @v image             EFI image
39
+ * @v devpath_out       Device path to pass to image (output)
40
+ * @v cmdline_out       Unicode command line (output)
41
+ * @v cmdline_len_out   Length of command line in bytes (output)
42
+ * @ret rc              Return status code
43
+ */
44
+static int efi_image_make_cmdline ( struct image *image,
45
+				    EFI_DEVICE_PATH **devpath_out,
46
+				    VOID **cmdline_out,
47
+				    UINT32 *cmdline_len_out ) {
48
+	char *uri;
49
+	size_t uri_len;
50
+	FILEPATH_DEVICE_PATH *devpath;
51
+	EFI_DEVICE_PATH *endpath;
52
+	size_t devpath_len;
53
+	CHAR16 *cmdline;
54
+	UINT32 cmdline_len;
55
+	size_t args_len = 0;
56
+	UINT32 i;
57
+
58
+	/* Get the URI string of the image */
59
+	uri_len = unparse_uri ( NULL, 0, image->uri, URI_ALL ) + 1;
60
+
61
+	/* Compute final command line length */
62
+	if ( image->cmdline ) {
63
+		args_len = strlen ( image->cmdline ) + 1;
64
+	}
65
+	cmdline_len = args_len + uri_len;
66
+
67
+	/* Allocate space for the uri, final command line and device path */
68
+	cmdline = malloc ( cmdline_len * sizeof ( CHAR16 ) + uri_len
69
+			   + SIZE_OF_FILEPATH_DEVICE_PATH
70
+			   + uri_len * sizeof ( CHAR16 )
71
+			   + sizeof ( EFI_DEVICE_PATH ) );
72
+	if ( ! cmdline )
73
+		return -ENOMEM;
74
+	uri = (char *) ( cmdline + cmdline_len );
75
+	devpath = (FILEPATH_DEVICE_PATH *) ( uri + uri_len );
76
+	endpath = (EFI_DEVICE_PATH *) ( (char *) devpath
77
+					+ SIZE_OF_FILEPATH_DEVICE_PATH
78
+					+ uri_len * sizeof ( CHAR16 ) );
79
+
80
+	/* Build the iPXE device path */
81
+	devpath->Header.Type = MEDIA_DEVICE_PATH;
82
+	devpath->Header.SubType = MEDIA_FILEPATH_DP;
83
+	devpath_len = SIZE_OF_FILEPATH_DEVICE_PATH
84
+			+ uri_len * sizeof ( CHAR16 );
85
+	devpath->Header.Length[0] = devpath_len & 0xFF;
86
+	devpath->Header.Length[1] = devpath_len >> 8;
87
+	endpath->Type = END_DEVICE_PATH_TYPE;
88
+	endpath->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE;
89
+	endpath->Length[0] = 4;
90
+	endpath->Length[1] = 0;
91
+	unparse_uri ( uri, uri_len, image->uri, URI_ALL );
92
+
93
+	/* Convert to Unicode */
94
+	for ( i = 0 ; i < uri_len ; i++ ) {
95
+		cmdline[i] = uri[i];
96
+		devpath->PathName[i] = uri[i];
97
+	}
98
+	if ( image->cmdline ) {
99
+		cmdline[uri_len - 1] = ' ';
100
+	}
101
+	for ( i = 0 ; i < args_len ; i++ ) {
102
+		cmdline[i + uri_len] = image->cmdline[i];
103
+	}
104
+
105
+	*devpath_out = &devpath->Header;
106
+	*cmdline_out = cmdline;
107
+	*cmdline_len_out = cmdline_len * sizeof ( CHAR16 );
108
+	return 0;
109
+}
110
+
29 111
 /**
30 112
  * Execute EFI image
31 113
  *
@@ -34,7 +116,12 @@ FEATURE ( FEATURE_IMAGE, "EFI", DHCP_EB_FEATURE_EFI, 1 );
34 116
  */
35 117
 static int efi_image_exec ( struct image *image ) {
36 118
 	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
119
+	union {
120
+		EFI_LOADED_IMAGE_PROTOCOL *image;
121
+		void *interface;
122
+	} loaded;
37 123
 	EFI_HANDLE handle;
124
+	EFI_HANDLE device_handle = NULL;
38 125
 	UINTN exit_data_size;
39 126
 	CHAR16 *exit_data;
40 127
 	EFI_STATUS efirc;
@@ -47,21 +134,56 @@ static int efi_image_exec ( struct image *image ) {
47 134
 		/* Not an EFI image */
48 135
 		DBGC ( image, "EFIIMAGE %p could not load: %s\n",
49 136
 		       image, efi_strerror ( efirc ) );
50
-		return -ENOEXEC;
137
+		rc = -ENOEXEC;
138
+		goto err_load_image;
51 139
 	}
52 140
 
141
+	/* Get the loaded image protocol for the newly loaded image */
142
+	efirc = bs->OpenProtocol ( handle, &efi_loaded_image_protocol_guid,
143
+				   &loaded.interface, efi_image_handle,
144
+				   NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL );
145
+	if ( efirc ) {
146
+		/* Should never happen */
147
+		rc = EFIRC_TO_RC ( efirc );
148
+		goto err_open_protocol;
149
+	}
150
+
151
+	/* Pass an iPXE download protocol to the image */
152
+	if ( ( rc = efi_download_install ( &device_handle ) ) != 0 ) {
153
+		DBGC ( image, "EFIIMAGE %p could not install iPXE download "
154
+		       "protocol: %s\n", image, strerror ( rc ) );
155
+		goto err_download_install;
156
+	}
157
+	loaded.image->DeviceHandle = device_handle;
158
+	loaded.image->ParentHandle = efi_loaded_image;
159
+	if ( ( rc = efi_image_make_cmdline ( image, &loaded.image->FilePath,
160
+				       &loaded.image->LoadOptions,
161
+				       &loaded.image->LoadOptionsSize ) ) != 0 )
162
+		goto err_make_cmdline;
163
+
53 164
 	/* Start the image */
54 165
 	if ( ( efirc = bs->StartImage ( handle, &exit_data_size,
55 166
 					&exit_data ) ) != 0 ) {
56 167
 		DBGC ( image, "EFIIMAGE %p returned with status %s\n",
57 168
 		       image, efi_strerror ( efirc ) );
169
+		rc = EFIRC_TO_RC ( efirc );
170
+		goto err_start_image;
58 171
 	}
59
-	rc = EFIRC_TO_RC ( efirc );
60 172
 
173
+	/* Success */
174
+	rc = 0;
175
+
176
+ err_start_image:
177
+	free ( loaded.image->LoadOptions );
178
+ err_make_cmdline:
179
+	efi_download_uninstall ( device_handle );
180
+ err_download_install:
181
+ err_open_protocol:
61 182
 	/* Unload the image.  We can't leave it loaded, because we
62 183
 	 * have no "unload" operation.
63 184
 	 */
64 185
 	bs->UnloadImage ( handle );
186
+ err_load_image:
65 187
 
66 188
 	return rc;
67 189
 }

+ 2
- 0
src/include/ipxe/efi/efi.h Visa fil

@@ -142,5 +142,7 @@ extern EFI_SYSTEM_TABLE *efi_systab;
142 142
 extern const char * efi_strerror ( EFI_STATUS efirc );
143 143
 extern EFI_STATUS efi_init ( EFI_HANDLE image_handle,
144 144
 			     EFI_SYSTEM_TABLE *systab );
145
+extern int efi_download_install ( EFI_HANDLE *device_handle );
146
+extern void efi_download_uninstall ( EFI_HANDLE device_handle );
145 147
 
146 148
 #endif /* _IPXE_EFI_H */

+ 154
- 0
src/include/ipxe/efi/ipxe_download.h Visa fil

@@ -0,0 +1,154 @@
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
+#ifndef IPXE_DOWNLOAD_H
20
+#define IPXE_DOWNLOAD_H
21
+
22
+FILE_LICENCE ( GPL2_OR_LATER );
23
+
24
+/** @file
25
+ *
26
+ * iPXE Download Protocol
27
+ *
28
+ * EFI applications started by iPXE may use this interface to download files.
29
+ */
30
+
31
+typedef struct _IPXE_DOWNLOAD_PROTOCOL IPXE_DOWNLOAD_PROTOCOL;
32
+
33
+/** Token to represent a currently downloading file */
34
+typedef VOID *IPXE_DOWNLOAD_FILE;
35
+
36
+/**
37
+ * Callback function that is invoked when data arrives for a particular file.
38
+ *
39
+ * Not all protocols will deliver data in order. Clients should not rely on the
40
+ * order of data delivery matching the order in the file.
41
+ *
42
+ * Some protocols are capable of determining the file size near the beginning
43
+ * of data transfer. To allow the client to allocate memory more efficiently,
44
+ * iPXE may give a hint about the file size by calling the Data callback with
45
+ * a zero BufferLength and the file size in FileOffset. Clients should be
46
+ * prepared to deal with more or less data than the hint actually arriving.
47
+ *
48
+ * @v Context		Context provided to the Start function
49
+ * @v Buffer		New data
50
+ * @v BufferLength	Length of new data in bytes
51
+ * @v FileOffset	Offset of new data in the file
52
+ * @ret Status		EFI_SUCCESS to continue the download,
53
+ *			or any error code to abort.
54
+ */
55
+typedef
56
+EFI_STATUS
57
+(EFIAPI *IPXE_DOWNLOAD_DATA_CALLBACK)(
58
+  IN VOID *Context,
59
+  IN VOID *Buffer,
60
+  IN UINTN BufferLength,
61
+  IN UINTN FileOffset
62
+  );
63
+
64
+/**
65
+ * Callback function that is invoked when the file is finished downloading, or
66
+ * when a connection unexpectedly closes or times out.
67
+ *
68
+ * The finish callback is also called when a download is aborted by the Abort
69
+ * function (below).
70
+ *
71
+ * @v Context		Context provided to the Start function
72
+ * @v Status		Reason for termination: EFI_SUCCESS when the entire
73
+ * 			file was transferred successfully, or an error
74
+ * 			otherwise
75
+ */
76
+typedef
77
+void
78
+(EFIAPI *IPXE_DOWNLOAD_FINISH_CALLBACK)(
79
+  IN VOID *Context,
80
+  IN EFI_STATUS Status
81
+  );
82
+
83
+/**
84
+ * Start downloading a file, and register callback functions to handle the
85
+ * download.
86
+ *
87
+ * @v This		iPXE Download Protocol instance
88
+ * @v Url		URL to download from
89
+ * @v DataCallback	Callback that will be invoked when data arrives
90
+ * @v FinishCallback	Callback that will be invoked when the download ends
91
+ * @v Context		Context passed to the Data and Finish callbacks
92
+ * @v File		Token that can be used to abort the download
93
+ * @ret Status		EFI status code
94
+ */
95
+typedef
96
+EFI_STATUS
97
+(EFIAPI *IPXE_DOWNLOAD_START)(
98
+  IN IPXE_DOWNLOAD_PROTOCOL *This,
99
+  IN CHAR8 *Url,
100
+  IN IPXE_DOWNLOAD_DATA_CALLBACK DataCallback,
101
+  IN IPXE_DOWNLOAD_FINISH_CALLBACK FinishCallback,
102
+  IN VOID *Context,
103
+  OUT IPXE_DOWNLOAD_FILE *File
104
+  );
105
+
106
+/**
107
+ * Forcibly abort downloading a file that is currently in progress.
108
+ *
109
+ * It is not safe to call this function after the Finish callback has executed.
110
+ *
111
+ * @v This		iPXE Download Protocol instance
112
+ * @v File		Token obtained from Start
113
+ * @v Status		Reason for aborting the download
114
+ * @ret Status		EFI status code
115
+ */
116
+typedef
117
+EFI_STATUS
118
+(EFIAPI *IPXE_DOWNLOAD_ABORT)(
119
+  IN IPXE_DOWNLOAD_PROTOCOL *This,
120
+  IN IPXE_DOWNLOAD_FILE File,
121
+  IN EFI_STATUS Status
122
+  );
123
+
124
+/**
125
+ * Poll for more data from iPXE. This function will invoke the registered
126
+ * callbacks if data is available or if downloads complete.
127
+ *
128
+ * @v This		iPXE Download Protocol instance
129
+ * @ret Status		EFI status code
130
+ */
131
+typedef
132
+EFI_STATUS
133
+(EFIAPI *IPXE_DOWNLOAD_POLL)(
134
+  IN IPXE_DOWNLOAD_PROTOCOL *This
135
+  );
136
+
137
+/**
138
+ * The iPXE Download Protocol.
139
+ *
140
+ * iPXE will attach a iPXE Download Protocol to the DeviceHandle in the Loaded
141
+ * Image Protocol of all child EFI applications.
142
+ */
143
+struct _IPXE_DOWNLOAD_PROTOCOL {
144
+   IPXE_DOWNLOAD_START Start;
145
+   IPXE_DOWNLOAD_ABORT Abort;
146
+   IPXE_DOWNLOAD_POLL Poll;
147
+};
148
+
149
+#define IPXE_DOWNLOAD_PROTOCOL_GUID \
150
+  { \
151
+    0x3eaeaebd, 0xdecf, 0x493b, { 0x9b, 0xd1, 0xcd, 0xb2, 0xde, 0xca, 0xe7, 0x19 } \
152
+  }
153
+
154
+#endif

+ 233
- 0
src/interface/efi/efi_download.c Visa fil

@@ -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
+}

Laddar…
Avbryt
Spara