Parcourir la source

[efi] Disable SNP devices when running iPXE as the application

Some UEFI builds will set up a timer to continuously poll any SNP
devices.  This can drain packets from the network device's receive
queue before iPXE gets a chance to process them.

Use netdev_rx_[un]freeze() to explicitly indicate when we expect our
network devices to be driven via the external SNP API (as we do with
the UNDI API on the standard BIOS build), and disable the SNP API
except when receive queue processing is frozen.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown il y a 10 ans
Parent
révision
f473b9c3f6

+ 4
- 2
src/arch/x86/prefix/efidrvprefix.c Voir le fichier

@@ -22,6 +22,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
22 22
 #include <stdlib.h>
23 23
 #include <ipxe/init.h>
24 24
 #include <ipxe/efi/efi.h>
25
+#include <ipxe/efi/efi_snp.h>
25 26
 
26 27
 /**
27 28
  * EFI entry point
@@ -42,7 +43,8 @@ EFI_STATUS EFIAPI _efidrv_start ( EFI_HANDLE image_handle,
42 43
 	initialise();
43 44
 	startup();
44 45
 
46
+	/* Release network devices for use via SNP */
47
+	efi_snp_release();
48
+
45 49
 	return 0;
46 50
 }
47
-
48
-REQUIRE_OBJECT ( efi_snp );

+ 4
- 0
src/image/efi_image.c Voir le fichier

@@ -219,6 +219,9 @@ static int efi_image_exec ( struct image *image ) {
219 219
 	loaded.image->LoadOptionsSize =
220 220
 		( ( wcslen ( cmdline ) + 1 /* NUL */ ) * sizeof ( wchar_t ) );
221 221
 
222
+	/* Release network devices for use via SNP */
223
+	efi_snp_release();
224
+
222 225
 	/* Start the image */
223 226
 	if ( ( efirc = bs->StartImage ( handle, NULL, NULL ) ) != 0 ) {
224 227
 		rc = -EEFI_START ( efirc );
@@ -231,6 +234,7 @@ static int efi_image_exec ( struct image *image ) {
231 234
 	rc = 0;
232 235
 
233 236
  err_start_image:
237
+	efi_snp_claim();
234 238
  err_open_protocol:
235 239
 	/* Unload the image.  We can't leave it loaded, because we
236 240
 	 * have no "unload" operation.

+ 4
- 0
src/include/ipxe/efi/efi_snp.h Voir le fichier

@@ -32,6 +32,8 @@ struct efi_snp_device {
32 32
 	EFI_SIMPLE_NETWORK_PROTOCOL snp;
33 33
 	/** The SNP "mode" (parameters) */
34 34
 	EFI_SIMPLE_NETWORK_MODE mode;
35
+	/** Started flag */
36
+	int started;
35 37
 	/** Outstanding TX packet count (via "interrupt status")
36 38
 	 *
37 39
 	 * Used in order to generate TX completions.
@@ -75,5 +77,7 @@ struct efi_snp_device {
75 77
 extern int efi_snp_hii_install ( struct efi_snp_device *snpdev );
76 78
 extern void efi_snp_hii_uninstall ( struct efi_snp_device *snpdev );
77 79
 extern struct efi_snp_device * last_opened_snpdev ( void );
80
+extern void efi_snp_claim ( void );
81
+extern void efi_snp_release ( void );
78 82
 
79 83
 #endif /* _IPXE_EFI_SNP_H */

+ 126
- 7
src/interface/efi/efi_snp.c Voir le fichier

@@ -69,6 +69,35 @@ static EFI_GUID efi_load_file_protocol_guid
69 69
 /** List of SNP devices */
70 70
 static LIST_HEAD ( efi_snp_devices );
71 71
 
72
+/**
73
+ * Set EFI SNP mode state
74
+ *
75
+ * @v snp		SNP interface
76
+ */
77
+static void efi_snp_set_state ( struct efi_snp_device *snpdev ) {
78
+	struct net_device *netdev = snpdev->netdev;
79
+	EFI_SIMPLE_NETWORK_MODE *mode = &snpdev->mode;
80
+
81
+	/* Calculate state */
82
+	if ( ! snpdev->started ) {
83
+		/* Start() method not called; report as Stopped */
84
+		mode->State = EfiSimpleNetworkStopped;
85
+	} else if ( ! netdev_is_open ( netdev ) ) {
86
+		/* Network device not opened; report as Started */
87
+		mode->State = EfiSimpleNetworkStarted;
88
+	} else if ( ! netdev_rx_frozen ( netdev ) ) {
89
+		/* Network device opened but claimed for use by iPXE; report
90
+		 * as Started to inhibit receive polling.
91
+		 */
92
+		mode->State = EfiSimpleNetworkStarted;
93
+	} else {
94
+		/* Network device opened and available for use via SNP; report
95
+		 * as Initialized.
96
+		 */
97
+		mode->State = EfiSimpleNetworkInitialized;
98
+	}
99
+}
100
+
72 101
 /**
73 102
  * Set EFI SNP mode based on iPXE net device parameters
74 103
  *
@@ -134,7 +163,12 @@ efi_snp_start ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
134 163
 
135 164
 	DBGC2 ( snpdev, "SNPDEV %p START\n", snpdev );
136 165
 
137
-	snpdev->mode.State = EfiSimpleNetworkStarted;
166
+	/* Fail if net device is currently claimed for use by iPXE */
167
+	if ( ! netdev_rx_frozen ( snpdev->netdev ) )
168
+		return EFI_NOT_READY;
169
+
170
+	snpdev->started = 1;
171
+	efi_snp_set_state ( snpdev );
138 172
 	return 0;
139 173
 }
140 174
 
@@ -151,7 +185,12 @@ efi_snp_stop ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
151 185
 
152 186
 	DBGC2 ( snpdev, "SNPDEV %p STOP\n", snpdev );
153 187
 
154
-	snpdev->mode.State = EfiSimpleNetworkStopped;
188
+	/* Fail if net device is currently claimed for use by iPXE */
189
+	if ( ! netdev_rx_frozen ( snpdev->netdev ) )
190
+		return EFI_NOT_READY;
191
+
192
+	snpdev->started = 0;
193
+	efi_snp_set_state ( snpdev );
155 194
 	return 0;
156 195
 }
157 196
 
@@ -174,13 +213,17 @@ efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
174 213
 		snpdev, ( ( unsigned long ) extra_rx_bufsize ),
175 214
 		( ( unsigned long ) extra_tx_bufsize ) );
176 215
 
216
+	/* Fail if net device is currently claimed for use by iPXE */
217
+	if ( ! netdev_rx_frozen ( snpdev->netdev ) )
218
+		return EFI_NOT_READY;
219
+
177 220
 	if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) {
178 221
 		DBGC ( snpdev, "SNPDEV %p could not open %s: %s\n",
179 222
 		       snpdev, snpdev->netdev->name, strerror ( rc ) );
180 223
 		return EFIRC ( rc );
181 224
 	}
225
+	efi_snp_set_state ( snpdev );
182 226
 
183
-	snpdev->mode.State = EfiSimpleNetworkInitialized;
184 227
 	return 0;
185 228
 }
186 229
 
@@ -200,16 +243,20 @@ efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) {
200 243
 	DBGC2 ( snpdev, "SNPDEV %p RESET (%s extended verification)\n",
201 244
 		snpdev, ( ext_verify ? "with" : "without" ) );
202 245
 
246
+	/* Fail if net device is currently claimed for use by iPXE */
247
+	if ( ! netdev_rx_frozen ( snpdev->netdev ) )
248
+		return EFI_NOT_READY;
249
+
203 250
 	netdev_close ( snpdev->netdev );
204
-	snpdev->mode.State = EfiSimpleNetworkStarted;
251
+	efi_snp_set_state ( snpdev );
205 252
 
206 253
 	if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) {
207 254
 		DBGC ( snpdev, "SNPDEV %p could not reopen %s: %s\n",
208 255
 		       snpdev, snpdev->netdev->name, strerror ( rc ) );
209 256
 		return EFIRC ( rc );
210 257
 	}
258
+	efi_snp_set_state ( snpdev );
211 259
 
212
-	snpdev->mode.State = EfiSimpleNetworkInitialized;
213 260
 	return 0;
214 261
 }
215 262
 
@@ -226,8 +273,13 @@ efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
226 273
 
227 274
 	DBGC2 ( snpdev, "SNPDEV %p SHUTDOWN\n", snpdev );
228 275
 
276
+	/* Fail if net device is currently claimed for use by iPXE */
277
+	if ( ! netdev_rx_frozen ( snpdev->netdev ) )
278
+		return EFI_NOT_READY;
279
+
229 280
 	netdev_close ( snpdev->netdev );
230
-	snpdev->mode.State = EfiSimpleNetworkStarted;
281
+	efi_snp_set_state ( snpdev );
282
+
231 283
 	return 0;
232 284
 }
233 285
 
@@ -258,6 +310,10 @@ efi_snp_receive_filters ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINT32 enable,
258 310
 			    snpdev->netdev->ll_protocol->ll_addr_len );
259 311
 	}
260 312
 
313
+	/* Fail if net device is currently claimed for use by iPXE */
314
+	if ( ! netdev_rx_frozen ( snpdev->netdev ) )
315
+		return EFI_NOT_READY;
316
+
261 317
 	/* Lie through our teeth, otherwise MNP refuses to accept us */
262 318
 	return 0;
263 319
 }
@@ -280,6 +336,10 @@ efi_snp_station_address ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset,
280 336
 	DBGC2 ( snpdev, "SNPDEV %p STATION_ADDRESS %s\n", snpdev,
281 337
 		( reset ? "reset" : ll_protocol->ntoa ( new ) ) );
282 338
 
339
+	/* Fail if net device is currently claimed for use by iPXE */
340
+	if ( ! netdev_rx_frozen ( snpdev->netdev ) )
341
+		return EFI_NOT_READY;
342
+
283 343
 	/* Set the MAC address */
284 344
 	if ( reset )
285 345
 		new = &snpdev->mode.PermanentAddress;
@@ -313,6 +373,10 @@ efi_snp_statistics ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset,
313 373
 	DBGC2 ( snpdev, "SNPDEV %p STATISTICS%s", snpdev,
314 374
 		( reset ? " reset" : "" ) );
315 375
 
376
+	/* Fail if net device is currently claimed for use by iPXE */
377
+	if ( ! netdev_rx_frozen ( snpdev->netdev ) )
378
+		return EFI_NOT_READY;
379
+
316 380
 	/* Gather statistics */
317 381
 	memset ( &stats_buf, 0, sizeof ( stats_buf ) );
318 382
 	stats_buf.TxGoodFrames = snpdev->netdev->tx_stats.good;
@@ -361,6 +425,10 @@ efi_snp_mcast_ip_to_mac ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ipv6,
361 425
 		   inet_ntoa ( *( ( struct in_addr * ) ip ) ) );
362 426
 	DBGC2 ( snpdev, "SNPDEV %p MCAST_IP_TO_MAC %s\n", snpdev, ip_str );
363 427
 
428
+	/* Fail if net device is currently claimed for use by iPXE */
429
+	if ( ! netdev_rx_frozen ( snpdev->netdev ) )
430
+		return EFI_NOT_READY;
431
+
364 432
 	/* Try to hash the address */
365 433
 	if ( ( rc = ll_protocol->mc_hash ( ( ipv6 ? AF_INET6 : AF_INET ),
366 434
 					   ip, mac ) ) != 0 ) {
@@ -394,6 +462,10 @@ efi_snp_nvdata ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN read,
394 462
 	if ( ! read )
395 463
 		DBGC2_HDA ( snpdev, offset, data, len );
396 464
 
465
+	/* Fail if net device is currently claimed for use by iPXE */
466
+	if ( ! netdev_rx_frozen ( snpdev->netdev ) )
467
+		return EFI_NOT_READY;
468
+
397 469
 	return EFI_UNSUPPORTED;
398 470
 }
399 471
 
@@ -413,6 +485,10 @@ efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
413 485
 
414 486
 	DBGC2 ( snpdev, "SNPDEV %p GET_STATUS", snpdev );
415 487
 
488
+	/* Fail if net device is currently claimed for use by iPXE */
489
+	if ( ! netdev_rx_frozen ( snpdev->netdev ) )
490
+		return EFI_NOT_READY;
491
+
416 492
 	/* Poll the network device */
417 493
 	efi_snp_poll ( snpdev );
418 494
 
@@ -508,6 +584,10 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
508 584
 	}
509 585
 	DBGC2 ( snpdev, "\n" );
510 586
 
587
+	/* Fail if net device is currently claimed for use by iPXE */
588
+	if ( ! netdev_rx_frozen ( snpdev->netdev ) )
589
+		return EFI_NOT_READY;
590
+
511 591
 	/* Sanity checks */
512 592
 	if ( ll_header_len ) {
513 593
 		if ( ll_header_len != ll_protocol->ll_header_len ) {
@@ -616,6 +696,10 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
616 696
 	DBGC2 ( snpdev, "SNPDEV %p RECEIVE %p(+%lx)", snpdev, data,
617 697
 		( ( unsigned long ) *len ) );
618 698
 
699
+	/* Fail if net device is currently claimed for use by iPXE */
700
+	if ( ! netdev_rx_frozen ( snpdev->netdev ) )
701
+		return EFI_NOT_READY;
702
+
619 703
 	/* Poll the network device */
620 704
 	efi_snp_poll ( snpdev );
621 705
 
@@ -655,7 +739,7 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
655 739
 
656 740
  out_bad_ll_header:
657 741
 	free_iob ( iobuf );
658
-out_no_packet:
742
+ out_no_packet:
659 743
 	return EFIRC ( rc );
660 744
 }
661 745
 
@@ -676,6 +760,10 @@ static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event,
676 760
 	if ( ! netdev_is_open ( snpdev->netdev ) )
677 761
 		return;
678 762
 
763
+	/* Do nothing if net device is currently claimed for use by iPXE */
764
+	if ( ! netdev_rx_frozen ( snpdev->netdev ) )
765
+		return;
766
+
679 767
 	/* Poll the network device */
680 768
 	efi_snp_poll ( snpdev );
681 769
 
@@ -785,9 +873,15 @@ efi_snp_load_file ( EFI_LOAD_FILE_PROTOCOL *load_file,
785 873
 		return EFI_UNSUPPORTED;
786 874
 	}
787 875
 
876
+	/* Claim network devices for use by iPXE */
877
+	efi_snp_claim();
878
+
788 879
 	/* Boot from network device */
789 880
 	ipxe ( netdev );
790 881
 
882
+	/* Release network devices for use via SNP */
883
+	efi_snp_release();
884
+
791 885
 	/* Assume boot process was aborted */
792 886
 	return EFI_ABORTED;
793 887
 }
@@ -1011,6 +1105,9 @@ static void efi_snp_notify ( struct net_device *netdev ) {
1011 1105
 		( netdev_link_ok ( netdev ) ? TRUE : FALSE );
1012 1106
 	DBGC ( snpdev, "SNPDEV %p link is %s\n", snpdev,
1013 1107
 	       ( snpdev->mode.MediaPresent ? "up" : "down" ) );
1108
+
1109
+	/* Update mode state */
1110
+	efi_snp_set_state ( snpdev );
1014 1111
 }
1015 1112
 
1016 1113
 /**
@@ -1069,3 +1166,25 @@ struct efi_snp_device * last_opened_snpdev ( void ) {
1069 1166
 
1070 1167
 	return efi_snp_demux ( netdev );
1071 1168
 }
1169
+
1170
+/**
1171
+ * Claim network devices for use by iPXE
1172
+ *
1173
+ */
1174
+void efi_snp_claim ( void ) {
1175
+	struct net_device *netdev;
1176
+
1177
+	for_each_netdev ( netdev )
1178
+		netdev_rx_unfreeze ( netdev );
1179
+}
1180
+
1181
+/**
1182
+ * Release network devices for use via SNP
1183
+ *
1184
+ */
1185
+void efi_snp_release ( void ) {
1186
+	struct net_device *netdev;
1187
+
1188
+	for_each_netdev ( netdev )
1189
+		netdev_rx_freeze ( netdev );
1190
+}

Chargement…
Annuler
Enregistrer