Browse Source

[efi] Expose an UNDI interface alongside the existing SNP interface

UEFI UNDI is a hideously ugly lump of poorly specified garbage bolted
on as an appendix of the UEFI specification.  My personal favourite
line from the UNDI 'specification' is section E.2.2, which states
"Basically, the rule is: Do it right, or don't do it at all".  The
author appears to believe that such exhortations are a viable
substitute for documenting what it is that the wretched reader is
supposed to, in fact, do.

(Second favourite is the section listing the pros and cons of various
driver types.  This fails to identify a single con for the mythical
"Hardware UNDI", a design so insanely intrinsically slow that it
appears to have been the inspiration for the EFI_USB_IO_PROTOCOL.)

UNDI is functionally isomorphic to the substantially less preposterous
EFI_SIMPLE_NETWORK_PROTOCOL.  Provide an UNDI interface (as a thin
wrapper around the existing SNP interface) to allow for use by
third-party software that has made poor life choices.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 years ago
parent
commit
300a371bfb
1 changed files with 676 additions and 23 deletions
  1. 676
    23
      src/interface/efi/efi_snp.c

+ 676
- 23
src/interface/efi/efi_snp.c View File

@@ -183,7 +183,7 @@ efi_snp_start ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
183 183
 	struct efi_snp_device *snpdev =
184 184
 		container_of ( snp, struct efi_snp_device, snp );
185 185
 
186
-	DBGC2 ( snpdev, "SNPDEV %p START\n", snpdev );
186
+	DBGC ( snpdev, "SNPDEV %p START\n", snpdev );
187 187
 
188 188
 	/* Fail if net device is currently claimed for use by iPXE */
189 189
 	if ( efi_snp_claimed )
@@ -205,7 +205,7 @@ efi_snp_stop ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
205 205
 	struct efi_snp_device *snpdev =
206 206
 		container_of ( snp, struct efi_snp_device, snp );
207 207
 
208
-	DBGC2 ( snpdev, "SNPDEV %p STOP\n", snpdev );
208
+	DBGC ( snpdev, "SNPDEV %p STOP\n", snpdev );
209 209
 
210 210
 	/* Fail if net device is currently claimed for use by iPXE */
211 211
 	if ( efi_snp_claimed )
@@ -213,6 +213,7 @@ efi_snp_stop ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
213 213
 
214 214
 	snpdev->started = 0;
215 215
 	efi_snp_set_state ( snpdev );
216
+
216 217
 	return 0;
217 218
 }
218 219
 
@@ -231,9 +232,9 @@ efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
231 232
 		container_of ( snp, struct efi_snp_device, snp );
232 233
 	int rc;
233 234
 
234
-	DBGC2 ( snpdev, "SNPDEV %p INITIALIZE (%ld extra RX, %ld extra TX)\n",
235
-		snpdev, ( ( unsigned long ) extra_rx_bufsize ),
236
-		( ( unsigned long ) extra_tx_bufsize ) );
235
+	DBGC ( snpdev, "SNPDEV %p INITIALIZE (%ld extra RX, %ld extra TX)\n",
236
+	       snpdev, ( ( unsigned long ) extra_rx_bufsize ),
237
+	       ( ( unsigned long ) extra_tx_bufsize ) );
237 238
 
238 239
 	/* Fail if net device is currently claimed for use by iPXE */
239 240
 	if ( efi_snp_claimed )
@@ -262,8 +263,8 @@ efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) {
262 263
 		container_of ( snp, struct efi_snp_device, snp );
263 264
 	int rc;
264 265
 
265
-	DBGC2 ( snpdev, "SNPDEV %p RESET (%s extended verification)\n",
266
-		snpdev, ( ext_verify ? "with" : "without" ) );
266
+	DBGC ( snpdev, "SNPDEV %p RESET (%s extended verification)\n",
267
+	       snpdev, ( ext_verify ? "with" : "without" ) );
267 268
 
268 269
 	/* Fail if net device is currently claimed for use by iPXE */
269 270
 	if ( efi_snp_claimed )
@@ -294,7 +295,7 @@ efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
294 295
 	struct efi_snp_device *snpdev =
295 296
 		container_of ( snp, struct efi_snp_device, snp );
296 297
 
297
-	DBGC2 ( snpdev, "SNPDEV %p SHUTDOWN\n", snpdev );
298
+	DBGC ( snpdev, "SNPDEV %p SHUTDOWN\n", snpdev );
298 299
 
299 300
 	/* Fail if net device is currently claimed for use by iPXE */
300 301
 	if ( efi_snp_claimed )
@@ -326,9 +327,9 @@ efi_snp_receive_filters ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINT32 enable,
326 327
 		container_of ( snp, struct efi_snp_device, snp );
327 328
 	unsigned int i;
328 329
 
329
-	DBGC2 ( snpdev, "SNPDEV %p RECEIVE_FILTERS %08x&~%08x%s %ld mcast\n",
330
-		snpdev, enable, disable, ( mcast_reset ? " reset" : "" ),
331
-		( ( unsigned long ) mcast_count ) );
330
+	DBGC ( snpdev, "SNPDEV %p RECEIVE_FILTERS %08x&~%08x%s %ld mcast\n",
331
+	       snpdev, enable, disable, ( mcast_reset ? " reset" : "" ),
332
+	       ( ( unsigned long ) mcast_count ) );
332 333
 	for ( i = 0 ; i < mcast_count ; i++ ) {
333 334
 		DBGC2_HDA ( snpdev, i, &mcast[i],
334 335
 			    snpdev->netdev->ll_protocol->ll_addr_len );
@@ -359,8 +360,8 @@ efi_snp_station_address ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset,
359 360
 		container_of ( snp, struct efi_snp_device, snp );
360 361
 	struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol;
361 362
 
362
-	DBGC2 ( snpdev, "SNPDEV %p STATION_ADDRESS %s\n", snpdev,
363
-		( reset ? "reset" : ll_protocol->ntoa ( new ) ) );
363
+	DBGC ( snpdev, "SNPDEV %p STATION_ADDRESS %s\n", snpdev,
364
+	       ( reset ? "reset" : ll_protocol->ntoa ( new ) ) );
364 365
 
365 366
 	/* Fail if net device is currently claimed for use by iPXE */
366 367
 	if ( efi_snp_claimed )
@@ -396,8 +397,8 @@ efi_snp_statistics ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset,
396 397
 		container_of ( snp, struct efi_snp_device, snp );
397 398
 	EFI_NETWORK_STATISTICS stats_buf;
398 399
 
399
-	DBGC2 ( snpdev, "SNPDEV %p STATISTICS%s", snpdev,
400
-		( reset ? " reset" : "" ) );
400
+	DBGC ( snpdev, "SNPDEV %p STATISTICS%s", snpdev,
401
+	       ( reset ? " reset" : "" ) );
401 402
 
402 403
 	/* Fail if net device is currently claimed for use by iPXE */
403 404
 	if ( efi_snp_claimed )
@@ -449,7 +450,7 @@ efi_snp_mcast_ip_to_mac ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ipv6,
449 450
 
450 451
 	ip_str = ( ipv6 ? "(IPv6)" /* FIXME when we have inet6_ntoa() */ :
451 452
 		   inet_ntoa ( *( ( struct in_addr * ) ip ) ) );
452
-	DBGC2 ( snpdev, "SNPDEV %p MCAST_IP_TO_MAC %s\n", snpdev, ip_str );
453
+	DBGC ( snpdev, "SNPDEV %p MCAST_IP_TO_MAC %s\n", snpdev, ip_str );
453 454
 
454 455
 	/* Fail if net device is currently claimed for use by iPXE */
455 456
 	if ( efi_snp_claimed )
@@ -482,9 +483,9 @@ efi_snp_nvdata ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN read,
482 483
 	struct efi_snp_device *snpdev =
483 484
 		container_of ( snp, struct efi_snp_device, snp );
484 485
 
485
-	DBGC2 ( snpdev, "SNPDEV %p NVDATA %s %lx+%lx\n", snpdev,
486
-		( read ? "read" : "write" ), ( ( unsigned long ) offset ),
487
-		( ( unsigned long ) len ) );
486
+	DBGC ( snpdev, "SNPDEV %p NVDATA %s %lx+%lx\n", snpdev,
487
+	       ( read ? "read" : "write" ), ( ( unsigned long ) offset ),
488
+	       ( ( unsigned long ) len ) );
488 489
 	if ( ! read )
489 490
 		DBGC2_HDA ( snpdev, offset, data, len );
490 491
 
@@ -802,6 +803,608 @@ static EFI_SIMPLE_NETWORK_PROTOCOL efi_snp_device_snp = {
802 803
 	.Receive	= efi_snp_receive,
803 804
 };
804 805
 
806
+/******************************************************************************
807
+ *
808
+ * UNDI protocol
809
+ *
810
+ ******************************************************************************
811
+ */
812
+
813
+/** Union type for command parameter blocks */
814
+typedef union {
815
+	PXE_CPB_STATION_ADDRESS station_address;
816
+	PXE_CPB_FILL_HEADER fill_header;
817
+	PXE_CPB_FILL_HEADER_FRAGMENTED fill_header_fragmented;
818
+	PXE_CPB_TRANSMIT transmit;
819
+	PXE_CPB_RECEIVE receive;
820
+} PXE_CPB_ANY;
821
+
822
+/** Union type for data blocks */
823
+typedef union {
824
+	PXE_DB_GET_INIT_INFO get_init_info;
825
+	PXE_DB_STATION_ADDRESS station_address;
826
+	PXE_DB_GET_STATUS get_status;
827
+	PXE_DB_RECEIVE receive;
828
+} PXE_DB_ANY;
829
+
830
+/**
831
+ * Calculate UNDI byte checksum
832
+ *
833
+ * @v data		Data
834
+ * @v len		Length of data
835
+ * @ret sum		Checksum
836
+ */
837
+static uint8_t efi_undi_checksum ( void *data, size_t len ) {
838
+	uint8_t *bytes = data;
839
+	uint8_t sum = 0;
840
+
841
+	while ( len-- )
842
+		sum += *bytes++;
843
+	return sum;
844
+}
845
+
846
+/**
847
+ * Get UNDI SNP device interface number
848
+ *
849
+ * @v snpdev		SNP device
850
+ * @ret ifnum		UNDI interface number
851
+ */
852
+static unsigned int efi_undi_ifnum ( struct efi_snp_device *snpdev ) {
853
+
854
+	/* iPXE network device indexes are one-based (leaving zero
855
+	 * meaning "unspecified").  UNDI interface numbers are
856
+	 * zero-based.
857
+	 */
858
+	return ( snpdev->netdev->index - 1 );
859
+}
860
+
861
+/**
862
+ * Identify UNDI SNP device
863
+ *
864
+ * @v ifnum		Interface number
865
+ * @ret snpdev		SNP device, or NULL if not found
866
+ */
867
+static struct efi_snp_device * efi_undi_snpdev ( unsigned int ifnum ) {
868
+	struct efi_snp_device *snpdev;
869
+
870
+	list_for_each_entry ( snpdev, &efi_snp_devices, list ) {
871
+		if ( efi_undi_ifnum ( snpdev ) == ifnum )
872
+			return snpdev;
873
+	}
874
+	return NULL;
875
+}
876
+
877
+/**
878
+ * Convert EFI status code to UNDI status code
879
+ *
880
+ * @v efirc		EFI status code
881
+ * @ret statcode	UNDI status code
882
+ */
883
+static PXE_STATCODE efi_undi_statcode ( EFI_STATUS efirc ) {
884
+
885
+	switch ( efirc ) {
886
+	case EFI_INVALID_PARAMETER:	return PXE_STATCODE_INVALID_PARAMETER;
887
+	case EFI_UNSUPPORTED:		return PXE_STATCODE_UNSUPPORTED;
888
+	case EFI_OUT_OF_RESOURCES:	return PXE_STATCODE_BUFFER_FULL;
889
+	case EFI_PROTOCOL_ERROR:	return PXE_STATCODE_DEVICE_FAILURE;
890
+	case EFI_NOT_READY:		return PXE_STATCODE_NO_DATA;
891
+	default:
892
+		return PXE_STATCODE_INVALID_CDB;
893
+	}
894
+}
895
+
896
+/**
897
+ * Get state
898
+ *
899
+ * @v snpdev		SNP device
900
+ * @v cdb		Command description block
901
+ * @ret efirc		EFI status code
902
+ */
903
+static EFI_STATUS efi_undi_get_state ( struct efi_snp_device *snpdev,
904
+				       PXE_CDB *cdb ) {
905
+	EFI_SIMPLE_NETWORK_MODE *mode = &snpdev->mode;
906
+
907
+	DBGC ( snpdev, "UNDI %p GET STATE\n", snpdev );
908
+
909
+	/* Return current state */
910
+	if ( mode->State == EfiSimpleNetworkInitialized ) {
911
+		cdb->StatFlags |= PXE_STATFLAGS_GET_STATE_INITIALIZED;
912
+	} else if ( mode->State == EfiSimpleNetworkStarted ) {
913
+		cdb->StatFlags |= PXE_STATFLAGS_GET_STATE_STARTED;
914
+	} else {
915
+		cdb->StatFlags |= PXE_STATFLAGS_GET_STATE_STOPPED;
916
+	}
917
+
918
+	return 0;
919
+}
920
+
921
+/**
922
+ * Start
923
+ *
924
+ * @v snpdev		SNP device
925
+ * @ret efirc		EFI status code
926
+ */
927
+static EFI_STATUS efi_undi_start ( struct efi_snp_device *snpdev ) {
928
+	EFI_STATUS efirc;
929
+
930
+	DBGC ( snpdev, "UNDI %p START\n", snpdev );
931
+
932
+	/* Start SNP device */
933
+	if ( ( efirc = efi_snp_start ( &snpdev->snp ) ) != 0 )
934
+		return efirc;
935
+
936
+	return 0;
937
+}
938
+
939
+/**
940
+ * Stop
941
+ *
942
+ * @v snpdev		SNP device
943
+ * @ret efirc		EFI status code
944
+ */
945
+static EFI_STATUS efi_undi_stop ( struct efi_snp_device *snpdev ) {
946
+	EFI_STATUS efirc;
947
+
948
+	DBGC ( snpdev, "UNDI %p STOP\n", snpdev );
949
+
950
+	/* Stop SNP device */
951
+	if ( ( efirc = efi_snp_stop ( &snpdev->snp ) ) != 0 )
952
+		return efirc;
953
+
954
+	return 0;
955
+}
956
+
957
+/**
958
+ * Get initialisation information
959
+ *
960
+ * @v snpdev		SNP device
961
+ * @v cdb		Command description block
962
+ * @v db		Data block
963
+ * @ret efirc		EFI status code
964
+ */
965
+static EFI_STATUS efi_undi_get_init_info ( struct efi_snp_device *snpdev,
966
+					   PXE_CDB *cdb,
967
+					   PXE_DB_GET_INIT_INFO *db ) {
968
+	struct net_device *netdev = snpdev->netdev;
969
+	struct ll_protocol *ll_protocol = netdev->ll_protocol;
970
+
971
+	DBGC ( snpdev, "UNDI %p GET INIT INFO\n", snpdev );
972
+
973
+	/* Populate structure */
974
+	memset ( db, 0, sizeof ( *db ) );
975
+	db->FrameDataLen = ( netdev->max_pkt_len - ll_protocol->ll_header_len );
976
+	db->MediaHeaderLen = ll_protocol->ll_header_len;
977
+	db->HWaddrLen = ll_protocol->ll_addr_len;
978
+	db->IFtype = ntohs ( ll_protocol->ll_proto );
979
+	cdb->StatFlags |= ( PXE_STATFLAGS_CABLE_DETECT_SUPPORTED |
980
+			    PXE_STATFLAGS_GET_STATUS_NO_MEDIA_SUPPORTED );
981
+
982
+	return 0;
983
+}
984
+
985
+/**
986
+ * Initialise
987
+ *
988
+ * @v snpdev		SNP device
989
+ * @v cdb		Command description block
990
+ * @v efirc		EFI status code
991
+ */
992
+static EFI_STATUS efi_undi_initialize ( struct efi_snp_device *snpdev,
993
+					PXE_CDB *cdb ) {
994
+	struct net_device *netdev = snpdev->netdev;
995
+	EFI_STATUS efirc;
996
+
997
+	DBGC ( snpdev, "UNDI %p INITIALIZE\n", snpdev );
998
+
999
+	/* Reset SNP device */
1000
+	if ( ( efirc = efi_snp_initialize ( &snpdev->snp, 0, 0 ) ) != 0 )
1001
+		return efirc;
1002
+
1003
+	/* Report link state */
1004
+	if ( ! netdev_link_ok ( netdev ) )
1005
+		cdb->StatFlags |= PXE_STATFLAGS_INITIALIZED_NO_MEDIA;
1006
+
1007
+	return 0;
1008
+}
1009
+
1010
+/**
1011
+ * Reset
1012
+ *
1013
+ * @v snpdev		SNP device
1014
+ * @v efirc		EFI status code
1015
+ */
1016
+static EFI_STATUS efi_undi_reset ( struct efi_snp_device *snpdev ) {
1017
+	EFI_STATUS efirc;
1018
+
1019
+	DBGC ( snpdev, "UNDI %p RESET\n", snpdev );
1020
+
1021
+	/* Reset SNP device */
1022
+	if ( ( efirc = efi_snp_reset ( &snpdev->snp, 0 ) ) != 0 )
1023
+		return efirc;
1024
+
1025
+	return 0;
1026
+}
1027
+
1028
+/**
1029
+ * Shutdown
1030
+ *
1031
+ * @v snpdev		SNP device
1032
+ * @v efirc		EFI status code
1033
+ */
1034
+static EFI_STATUS efi_undi_shutdown ( struct efi_snp_device *snpdev ) {
1035
+	EFI_STATUS efirc;
1036
+
1037
+	DBGC ( snpdev, "UNDI %p SHUTDOWN\n", snpdev );
1038
+
1039
+	/* Reset SNP device */
1040
+	if ( ( efirc = efi_snp_shutdown ( &snpdev->snp ) ) != 0 )
1041
+		return efirc;
1042
+
1043
+	return 0;
1044
+}
1045
+
1046
+/**
1047
+ * Get/set receive filters
1048
+ *
1049
+ * @v snpdev		SNP device
1050
+ * @v cdb		Command description block
1051
+ * @v efirc		EFI status code
1052
+ */
1053
+static EFI_STATUS efi_undi_receive_filters ( struct efi_snp_device *snpdev,
1054
+					     PXE_CDB *cdb ) {
1055
+
1056
+	DBGC ( snpdev, "UNDI %p RECEIVE FILTERS\n", snpdev );
1057
+
1058
+	/* Mark everything as supported */
1059
+	cdb->StatFlags |= ( PXE_STATFLAGS_RECEIVE_FILTER_UNICAST |
1060
+			    PXE_STATFLAGS_RECEIVE_FILTER_BROADCAST |
1061
+			    PXE_STATFLAGS_RECEIVE_FILTER_PROMISCUOUS |
1062
+			    PXE_STATFLAGS_RECEIVE_FILTER_ALL_MULTICAST );
1063
+
1064
+	return 0;
1065
+}
1066
+
1067
+/**
1068
+ * Get/set station address
1069
+ *
1070
+ * @v snpdev		SNP device
1071
+ * @v cdb		Command description block
1072
+ * @v cpb		Command parameter block
1073
+ * @v efirc		EFI status code
1074
+ */
1075
+static EFI_STATUS efi_undi_station_address ( struct efi_snp_device *snpdev,
1076
+					     PXE_CDB *cdb,
1077
+					     PXE_CPB_STATION_ADDRESS *cpb,
1078
+					     PXE_DB_STATION_ADDRESS *db ) {
1079
+	struct net_device *netdev = snpdev->netdev;
1080
+	struct ll_protocol *ll_protocol = netdev->ll_protocol;
1081
+	void *mac;
1082
+	int reset;
1083
+	EFI_STATUS efirc;
1084
+
1085
+	DBGC ( snpdev, "UNDI %p STATION ADDRESS\n", snpdev );
1086
+
1087
+	/* Update address if applicable */
1088
+	reset = ( cdb->OpFlags & PXE_OPFLAGS_STATION_ADDRESS_RESET );
1089
+	mac = ( cpb ? &cpb->StationAddr : NULL );
1090
+	if ( ( reset || mac ) &&
1091
+	     ( ( efirc = efi_snp_station_address ( &snpdev->snp, reset,
1092
+						   mac ) ) != 0 ) )
1093
+		return efirc;
1094
+
1095
+	/* Fill in current addresses, if applicable */
1096
+	if ( db ) {
1097
+		memset ( db, 0, sizeof ( *db ) );
1098
+		memcpy ( &db->StationAddr, netdev->ll_addr,
1099
+			 ll_protocol->ll_addr_len );
1100
+		memcpy ( &db->BroadcastAddr, netdev->ll_broadcast,
1101
+			 ll_protocol->ll_addr_len );
1102
+		memcpy ( &db->PermanentAddr, netdev->hw_addr,
1103
+			 ll_protocol->hw_addr_len );
1104
+	}
1105
+
1106
+	return 0;
1107
+}
1108
+
1109
+/**
1110
+ * Get interrupt status
1111
+ *
1112
+ * @v snpdev		SNP device
1113
+ * @v cdb		Command description block
1114
+ * @v db		Data block
1115
+ * @v efirc		EFI status code
1116
+ */
1117
+static EFI_STATUS efi_undi_get_status ( struct efi_snp_device *snpdev,
1118
+					PXE_CDB *cdb, PXE_DB_GET_STATUS *db ) {
1119
+	UINT32 interrupts;
1120
+	VOID *txbuf;
1121
+	struct io_buffer *rxbuf;
1122
+	EFI_STATUS efirc;
1123
+
1124
+	DBGC2 ( snpdev, "UNDI %p GET STATUS\n", snpdev );
1125
+
1126
+	/* Get status */
1127
+	if ( ( efirc = efi_snp_get_status ( &snpdev->snp, &interrupts,
1128
+					    &txbuf ) ) != 0 )
1129
+		return efirc;
1130
+
1131
+	/* Report status */
1132
+	memset ( db, 0, sizeof ( *db ) );
1133
+	if ( interrupts & EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT )
1134
+		cdb->StatFlags |= PXE_STATFLAGS_GET_STATUS_RECEIVE;
1135
+	if ( interrupts & EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT )
1136
+		cdb->StatFlags |= PXE_STATFLAGS_GET_STATUS_TRANSMIT;
1137
+	if ( txbuf ) {
1138
+		db->TxBuffer[0] = ( ( intptr_t ) txbuf );
1139
+	} else {
1140
+		cdb->StatFlags |= PXE_STATFLAGS_GET_STATUS_NO_TXBUFS_WRITTEN;
1141
+		/* The specification states clearly that UNDI drivers
1142
+		 * should set TXBUF_QUEUE_EMPTY if all completed
1143
+		 * buffer addresses are written into the returned data
1144
+		 * block.  However, SnpDxe chooses to interpret
1145
+		 * TXBUF_QUEUE_EMPTY as a synonym for
1146
+		 * NO_TXBUFS_WRITTEN, thereby rendering it entirely
1147
+		 * pointless.  Work around this UEFI stupidity, as per
1148
+		 * usual.
1149
+		 */
1150
+		if ( snpdev->tx_prod == snpdev->tx_cons )
1151
+			cdb->StatFlags |=
1152
+				PXE_STATFLAGS_GET_STATUS_TXBUF_QUEUE_EMPTY;
1153
+	}
1154
+	rxbuf = list_first_entry ( &snpdev->rx, struct io_buffer, list );
1155
+	if ( rxbuf )
1156
+		db->RxFrameLen = iob_len ( rxbuf );
1157
+	if ( ! netdev_link_ok ( snpdev->netdev ) )
1158
+		cdb->StatFlags |= PXE_STATFLAGS_GET_STATUS_NO_MEDIA;
1159
+
1160
+	return 0;
1161
+}
1162
+
1163
+/**
1164
+ * Fill header
1165
+ *
1166
+ * @v snpdev		SNP device
1167
+ * @v cdb		Command description block
1168
+ * @v cpb		Command parameter block
1169
+ * @v efirc		EFI status code
1170
+ */
1171
+static EFI_STATUS efi_undi_fill_header ( struct efi_snp_device *snpdev,
1172
+					 PXE_CDB *cdb, PXE_CPB_ANY *cpb ) {
1173
+	struct net_device *netdev = snpdev->netdev;
1174
+	struct ll_protocol *ll_protocol = netdev->ll_protocol;
1175
+	PXE_CPB_FILL_HEADER *whole = &cpb->fill_header;
1176
+	PXE_CPB_FILL_HEADER_FRAGMENTED *fragged = &cpb->fill_header_fragmented;
1177
+	VOID *data;
1178
+	void *dest;
1179
+	void *src;
1180
+	uint16_t proto;
1181
+	struct io_buffer iobuf;
1182
+	int rc;
1183
+
1184
+	/* SnpDxe will (pointlessly) use PXE_CPB_FILL_HEADER_FRAGMENTED
1185
+	 * even though we choose to explicitly not claim support for
1186
+	 * fragments via PXE_ROMID_IMP_FRAG_SUPPORTED.
1187
+	 */
1188
+	if ( cdb->OpFlags & PXE_OPFLAGS_FILL_HEADER_FRAGMENTED ) {
1189
+		data = ( ( void * ) ( intptr_t ) fragged->FragDesc[0].FragAddr);
1190
+		dest = &fragged->DestAddr;
1191
+		src = &fragged->SrcAddr;
1192
+		proto = fragged->Protocol;
1193
+	} else {
1194
+		data = ( ( void * ) ( intptr_t ) whole->MediaHeader );
1195
+		dest = &whole->DestAddr;
1196
+		src = &whole->SrcAddr;
1197
+		proto = whole->Protocol;
1198
+	}
1199
+
1200
+	/* Construct link-layer header */
1201
+	iob_populate ( &iobuf, data, 0, ll_protocol->ll_header_len );
1202
+	iob_reserve ( &iobuf, ll_protocol->ll_header_len );
1203
+	if ( ( rc = ll_protocol->push ( netdev, &iobuf, dest, src,
1204
+					proto ) ) != 0 )
1205
+		return EFIRC ( rc );
1206
+
1207
+	return 0;
1208
+}
1209
+
1210
+/**
1211
+ * Transmit
1212
+ *
1213
+ * @v snpdev		SNP device
1214
+ * @v cpb		Command parameter block
1215
+ * @v efirc		EFI status code
1216
+ */
1217
+static EFI_STATUS efi_undi_transmit ( struct efi_snp_device *snpdev,
1218
+				      PXE_CPB_TRANSMIT *cpb ) {
1219
+	VOID *data = ( ( void * ) ( intptr_t ) cpb->FrameAddr );
1220
+	EFI_STATUS efirc;
1221
+
1222
+	DBGC2 ( snpdev, "UNDI %p TRANSMIT\n", snpdev );
1223
+
1224
+	/* Transmit packet */
1225
+	if ( ( efirc = efi_snp_transmit ( &snpdev->snp, 0, cpb->DataLen,
1226
+					  data, NULL, NULL, NULL ) ) != 0 )
1227
+		return efirc;
1228
+
1229
+	return 0;
1230
+}
1231
+
1232
+/**
1233
+ * Receive
1234
+ *
1235
+ * @v snpdev		SNP device
1236
+ * @v cpb		Command parameter block
1237
+ * @v efirc		EFI status code
1238
+ */
1239
+static EFI_STATUS efi_undi_receive ( struct efi_snp_device *snpdev,
1240
+				     PXE_CPB_RECEIVE *cpb,
1241
+				     PXE_DB_RECEIVE *db ) {
1242
+	struct net_device *netdev = snpdev->netdev;
1243
+	struct ll_protocol *ll_protocol = netdev->ll_protocol;
1244
+	VOID *data = ( ( void * ) ( intptr_t ) cpb->BufferAddr );
1245
+	UINTN hdr_len;
1246
+	UINTN len = cpb->BufferLen;
1247
+	EFI_MAC_ADDRESS src;
1248
+	EFI_MAC_ADDRESS dest;
1249
+	UINT16 proto;
1250
+	EFI_STATUS efirc;
1251
+
1252
+	DBGC2 ( snpdev, "UNDI %p RECEIVE\n", snpdev );
1253
+
1254
+	/* Receive packet */
1255
+	if ( ( efirc = efi_snp_receive ( &snpdev->snp, &hdr_len, &len, data,
1256
+					 &src, &dest, &proto ) ) != 0 )
1257
+		return efirc;
1258
+
1259
+	/* Describe frame */
1260
+	memset ( db, 0, sizeof ( *db ) );
1261
+	memcpy ( &db->SrcAddr, &src, ll_protocol->ll_addr_len );
1262
+	memcpy ( &db->DestAddr, &dest, ll_protocol->ll_addr_len );
1263
+	db->FrameLen = len;
1264
+	db->Protocol = proto;
1265
+	db->MediaHeaderLen = ll_protocol->ll_header_len;
1266
+	db->Type = PXE_FRAME_TYPE_PROMISCUOUS;
1267
+
1268
+	return 0;
1269
+}
1270
+
1271
+/** UNDI entry point */
1272
+static EFIAPI VOID efi_undi_issue ( UINT64 cdb_phys ) {
1273
+	PXE_CDB *cdb = ( ( void * ) ( intptr_t ) cdb_phys );
1274
+	PXE_CPB_ANY *cpb = ( ( void * ) ( intptr_t ) cdb->CPBaddr );
1275
+	PXE_DB_ANY *db = ( ( void * ) ( intptr_t ) cdb->DBaddr );
1276
+	struct efi_snp_device *snpdev;
1277
+	EFI_STATUS efirc;
1278
+
1279
+	/* Identify device */
1280
+	snpdev = efi_undi_snpdev ( cdb->IFnum );
1281
+	if ( ! snpdev ) {
1282
+		DBGC ( cdb, "UNDI invalid interface number %d\n", cdb->IFnum );
1283
+		cdb->StatCode = PXE_STATCODE_INVALID_CDB;
1284
+		cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
1285
+		return;
1286
+	}
1287
+
1288
+	/* Fail if net device is currently claimed for use by iPXE */
1289
+	if ( efi_snp_claimed ) {
1290
+		cdb->StatCode = PXE_STATCODE_BUSY;
1291
+		cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
1292
+		return;
1293
+	}
1294
+
1295
+	/* Handle opcode */
1296
+	cdb->StatCode = PXE_STATCODE_SUCCESS;
1297
+	cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;
1298
+	switch ( cdb->OpCode ) {
1299
+
1300
+	case PXE_OPCODE_GET_STATE:
1301
+		efirc = efi_undi_get_state ( snpdev, cdb );
1302
+		break;
1303
+
1304
+	case PXE_OPCODE_START:
1305
+		efirc = efi_undi_start ( snpdev );
1306
+		break;
1307
+
1308
+	case PXE_OPCODE_STOP:
1309
+		efirc = efi_undi_stop ( snpdev );
1310
+		break;
1311
+
1312
+	case PXE_OPCODE_GET_INIT_INFO:
1313
+		efirc = efi_undi_get_init_info ( snpdev, cdb,
1314
+						 &db->get_init_info );
1315
+		break;
1316
+
1317
+	case PXE_OPCODE_INITIALIZE:
1318
+		efirc = efi_undi_initialize ( snpdev, cdb );
1319
+		break;
1320
+
1321
+	case PXE_OPCODE_RESET:
1322
+		efirc = efi_undi_reset ( snpdev );
1323
+		break;
1324
+
1325
+	case PXE_OPCODE_SHUTDOWN:
1326
+		efirc = efi_undi_shutdown ( snpdev );
1327
+		break;
1328
+
1329
+	case PXE_OPCODE_RECEIVE_FILTERS:
1330
+		efirc = efi_undi_receive_filters ( snpdev, cdb );
1331
+		break;
1332
+
1333
+	case PXE_OPCODE_STATION_ADDRESS:
1334
+		efirc = efi_undi_station_address ( snpdev, cdb,
1335
+						   &cpb->station_address,
1336
+						   &db->station_address );
1337
+		break;
1338
+
1339
+	case PXE_OPCODE_GET_STATUS:
1340
+		efirc = efi_undi_get_status ( snpdev, cdb, &db->get_status );
1341
+		break;
1342
+
1343
+	case PXE_OPCODE_FILL_HEADER:
1344
+		efirc = efi_undi_fill_header ( snpdev, cdb, cpb );
1345
+		break;
1346
+
1347
+	case PXE_OPCODE_TRANSMIT:
1348
+		efirc = efi_undi_transmit ( snpdev, &cpb->transmit );
1349
+		break;
1350
+
1351
+	case PXE_OPCODE_RECEIVE:
1352
+		efirc = efi_undi_receive ( snpdev, &cpb->receive,
1353
+					   &db->receive );
1354
+		break;
1355
+
1356
+	default:
1357
+		DBGC ( snpdev, "UNDI %p unsupported opcode %#04x\n",
1358
+		       snpdev, cdb->OpCode );
1359
+		efirc = EFI_UNSUPPORTED;
1360
+		break;
1361
+	}
1362
+
1363
+	/* Convert EFI status code to UNDI status code */
1364
+	if ( efirc != 0 ) {
1365
+		cdb->StatFlags &= ~PXE_STATFLAGS_STATUS_MASK;
1366
+		cdb->StatFlags |= PXE_STATFLAGS_COMMAND_FAILED;
1367
+		cdb->StatCode = efi_undi_statcode ( efirc );
1368
+	}
1369
+}
1370
+
1371
+/** UNDI interface
1372
+ *
1373
+ * Must be aligned on a 16-byte boundary, for no particularly good
1374
+ * reason.
1375
+ */
1376
+static PXE_SW_UNDI efi_snp_undi __attribute__ (( aligned ( 16 ) )) = {
1377
+	.Signature	= PXE_ROMID_SIGNATURE,
1378
+	.Len		= sizeof ( efi_snp_undi ),
1379
+	.Rev		= PXE_ROMID_REV,
1380
+	.MajorVer	= PXE_ROMID_MAJORVER,
1381
+	.MinorVer	= PXE_ROMID_MINORVER,
1382
+	.Implementation	= ( PXE_ROMID_IMP_SW_VIRT_ADDR |
1383
+			    PXE_ROMID_IMP_STATION_ADDR_SETTABLE |
1384
+			    PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED |
1385
+			    PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED |
1386
+			    PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED |
1387
+			    PXE_ROMID_IMP_TX_COMPLETE_INT_SUPPORTED |
1388
+			    PXE_ROMID_IMP_PACKET_RX_INT_SUPPORTED ),
1389
+	/* SnpDxe checks that BusCnt is non-zero.  It makes no further
1390
+	 * use of BusCnt, and never looks as BusType[].  As with much
1391
+	 * of the EDK2 code, this check seems to serve no purpose
1392
+	 * whatsoever but must nonetheless be humoured.
1393
+	 */
1394
+	.BusCnt		= 1,
1395
+	.BusType[0]	= PXE_BUSTYPE ( 'i', 'P', 'X', 'E' ),
1396
+};
1397
+
1398
+/** Network Identification Interface (NII) */
1399
+static EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL efi_snp_device_nii = {
1400
+	.Revision	= EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION,
1401
+	.StringId	= "UNDI",
1402
+	.Type		= EfiNetworkInterfaceUndi,
1403
+	.MajorVer	= 3,
1404
+	.MinorVer	= 1,
1405
+	.Ipv6Supported	= TRUE, /* This is a raw packet interface, FFS! */
1406
+};
1407
+
805 1408
 /******************************************************************************
806 1409
  *
807 1410
  * Component name protocol
@@ -939,6 +1542,8 @@ static int efi_snp_probe ( struct net_device *netdev ) {
939 1542
 	EFI_DEVICE_PATH_PROTOCOL *path_end;
940 1543
 	MAC_ADDR_DEVICE_PATH *macpath;
941 1544
 	size_t path_prefix_len = 0;
1545
+	unsigned int ifcnt;
1546
+	void *interface;
942 1547
 	EFI_STATUS efirc;
943 1548
 	int rc;
944 1549
 
@@ -986,10 +1591,17 @@ static int efi_snp_probe ( struct net_device *netdev ) {
986 1591
 	efi_snp_set_mode ( snpdev );
987 1592
 
988 1593
 	/* Populate the NII structure */
989
-	snpdev->nii.Revision =
990
-		EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION;
991
-	strncpy ( snpdev->nii.StringId, "iPXE",
992
-		  sizeof ( snpdev->nii.StringId ) );
1594
+	memcpy ( &snpdev->nii, &efi_snp_device_nii, sizeof ( snpdev->nii ) );
1595
+	snpdev->nii.Id = ( ( intptr_t ) &efi_snp_undi );
1596
+	snpdev->nii.IfNum = efi_undi_ifnum ( snpdev );
1597
+	efi_snp_undi.EntryPoint = ( ( intptr_t ) efi_undi_issue );
1598
+	ifcnt = ( ( efi_snp_undi.IFcntExt << 8 ) | efi_snp_undi.IFcnt );
1599
+	if ( ifcnt < snpdev->nii.IfNum )
1600
+		ifcnt = snpdev->nii.IfNum;
1601
+	efi_snp_undi.IFcnt = ( ifcnt & 0xff );
1602
+	efi_snp_undi.IFcntExt = ( ifcnt >> 8 );
1603
+	efi_snp_undi.Fudge -= efi_undi_checksum ( &efi_snp_undi,
1604
+						  sizeof ( efi_snp_undi ) );
993 1605
 
994 1606
 	/* Populate the component name structure */
995 1607
 	efi_snprintf ( snpdev->driver_name,
@@ -1056,6 +1668,37 @@ static int efi_snp_probe ( struct net_device *netdev ) {
1056 1668
 		goto err_install_protocol_interface;
1057 1669
 	}
1058 1670
 
1671
+	/* SnpDxe will repeatedly start up and shut down our NII/UNDI
1672
+	 * interface (in order to obtain the MAC address) before
1673
+	 * discovering that it cannot install another SNP on the same
1674
+	 * handle.  This causes the underlying network device to be
1675
+	 * unexpectedly closed.
1676
+	 *
1677
+	 * Prevent this by opening our own NII (and NII31) protocol
1678
+	 * instances to prevent SnpDxe from attempting to bind to
1679
+	 * them.
1680
+	 */
1681
+	if ( ( efirc = bs->OpenProtocol ( snpdev->handle,
1682
+					  &efi_nii_protocol_guid, &interface,
1683
+					  efi_image_handle, snpdev->handle,
1684
+					  ( EFI_OPEN_PROTOCOL_BY_DRIVER |
1685
+					    EFI_OPEN_PROTOCOL_EXCLUSIVE )))!=0){
1686
+		rc = -EEFI ( efirc );
1687
+		DBGC ( snpdev, "SNPDEV %p could not open NII protocol: %s\n",
1688
+		       snpdev, strerror ( rc ) );
1689
+		goto err_open_nii;
1690
+	}
1691
+	if ( ( efirc = bs->OpenProtocol ( snpdev->handle,
1692
+					  &efi_nii31_protocol_guid, &interface,
1693
+					  efi_image_handle, snpdev->handle,
1694
+					  ( EFI_OPEN_PROTOCOL_BY_DRIVER |
1695
+					    EFI_OPEN_PROTOCOL_EXCLUSIVE )))!=0){
1696
+		rc = -EEFI ( efirc );
1697
+		DBGC ( snpdev, "SNPDEV %p could not open NII31 protocol: %s\n",
1698
+		       snpdev, strerror ( rc ) );
1699
+		goto err_open_nii31;
1700
+	}
1701
+
1059 1702
 	/* Add as child of EFI parent device */
1060 1703
 	if ( ( rc = efi_child_add ( efidev->device, snpdev->handle ) ) != 0 ) {
1061 1704
 		DBGC ( snpdev, "SNPDEV %p could not become child of %s: %s\n",
@@ -1090,6 +1733,12 @@ static int efi_snp_probe ( struct net_device *netdev ) {
1090 1733
 		efi_snp_hii_uninstall ( snpdev );
1091 1734
 	efi_child_del ( efidev->device, snpdev->handle );
1092 1735
  err_efi_child_add:
1736
+	bs->CloseProtocol ( snpdev->handle, &efi_nii_protocol_guid,
1737
+			    efi_image_handle, snpdev->handle );
1738
+ err_open_nii:
1739
+	bs->CloseProtocol ( snpdev->handle, &efi_nii31_protocol_guid,
1740
+			    efi_image_handle, snpdev->handle );
1741
+ err_open_nii31:
1093 1742
 	bs->UninstallMultipleProtocolInterfaces (
1094 1743
 			snpdev->handle,
1095 1744
 			&efi_simple_network_protocol_guid, &snpdev->snp,
@@ -1158,6 +1807,10 @@ static void efi_snp_remove ( struct net_device *netdev ) {
1158 1807
 	if ( snpdev->package_list )
1159 1808
 		efi_snp_hii_uninstall ( snpdev );
1160 1809
 	efi_child_del ( snpdev->efidev->device, snpdev->handle );
1810
+	bs->CloseProtocol ( snpdev->handle, &efi_nii_protocol_guid,
1811
+			    efi_image_handle, snpdev->handle );
1812
+	bs->CloseProtocol ( snpdev->handle, &efi_nii31_protocol_guid,
1813
+			    efi_image_handle, snpdev->handle );
1161 1814
 	bs->UninstallMultipleProtocolInterfaces (
1162 1815
 			snpdev->handle,
1163 1816
 			&efi_simple_network_protocol_guid, &snpdev->snp,

Loading…
Cancel
Save